元信息头与指令
入口文件顶部的 // ==UserScript== 注释块声明应用的全部元信息。全部应用共用指令,并统一编译注入页面。
| 指令 | 语法 | 可重复 | 说明 |
|---|---|---|---|
@name | 文本 | 否(重复时以最后一条为准) | 应用名称,显示在管理页列表和 IDE 顶部。终端 app info/enable/disable <名称> 也按它寻址,改名后旧名称随即失效(脚本里建议用 bundleId 寻址)。与项目名双向同步:修改 @name 并保存即重命名项目,在列表改名会回写 @name |
@description | 文本 | 否(重复时以最后一条为准) | 应用说明,建议写成业务能力简介 |
@match | match pattern | 是 | 应用运行的页面。Chrome match pattern 语法(如 https://*.commerce.com/*)。不声明时默认 <all_urls>。保留字 background 声明后台运行(见「运行环境:background」)。非法 pattern 会导致编译失败 |
@include | glob | 是 | 在 @match 范围内进一步收窄运行范围(如 *://*.commerce.com/bg*)。声明后页面 URL 必须同时命中 @match 与任一 @include;不写 @match 时在 <all_urls> 内收窄 |
@exclude | glob | 是 | 按 glob 排除页面 |
@exclude-match | match pattern | 是 | 按 match pattern 语义排除页面(需要精确语义时用它)。非法 pattern 会导致编译失败 |
@run-at | 枚举 | 否(重复时以最后一条为准) | 注入时机:document_start / document_end / document_idle(默认)。兼容油猴连字符写法(如 document-start),大小写不敏感;无法识别的取值按 document_idle 处理并给出编译告警 |
@require | 库 id | 是 | 启用内置库:dom / store / http / model / user / network / agent(agent 仅能在 plugincmd 命令内使用)。id 区分大小写,必须小写。可写成 builtin:dom 或带引号;runtime 无需声明(写了也会忽略)。未知 id 会导致编译失败;不支持本地文件,本地模块和 npm 包请用 ESM import |
@resource | 别名 + 路径 | 是 | 给本地文件起别名,运行时用 LFRuntime.getResource(别名) 读取。解析规则见下文「@resource」 |
@intercept | URL 子串或 /regex/ | 是 | 声明需要拦截页面请求。代码中使用 LFRuntime.network.onResponse / interceptRequest / interceptResponse 而入口未声明时,点「编译」会直接报错 |
语法速查(最常踩的坑)
@match / @exclude-match 用 Chrome MATCH PATTERN;@include / @exclude 用 GLOB。往 @match 里写 glob(如 *commerce*、https://commerce.*/*)会直接编译失败并提示原因;反过来往 @include 里写 match pattern 不会报错,但会按 glob 字面匹配,可能匹配不到。
解析与校验规则
- 插件构建只从入口文件读取指令,扫描文件前 120 行;其它文件里写的指令在插件构建中一律忽略(非入口文件头部
@require的唯一用途见下文「@require 的作用范围」)。// ==UserScript==包裹块可省略,未知指令(如@version、@grant、@namespace、@noframes)静默忽略 - 只识别
// @key value形式的单行注释;/* */块注释与 JSDoc 风格的* @name不会被识别。遇到第一条指令后,第一行非注释代码即结束头部,之后再写的指令无效。头部之前允许有"use strict"等少量前置语句(编译检查与运行时使用同一个解析器,包括@intercept检查),但建议仍把元信息头放在文件最顶部 - 指令必须带值:
// @run-at、// @match这类没有值的行会被当作普通注释忽略;// @intercept ""这类空值视为未声明 - 每行只写一个值:一行里用空格或逗号写多个值会被当作一个整体(例如
// @require dom store会按未知库 id 编译失败);多个规则或库请重复写多行 @name/@description/@run-at重复声明时以最后一条为准- 指令名大小写不敏感;
@match/@include/@exclude/@exclude-match/@require等取值可以用引号包裹 - 编译期会拦下以下错误(应用不会进入「编译成功但不注入」的状态):
@match/@exclude-match不是合法 match pattern:scheme 只能是小写的*/http/https/file/ftp,host 的*只能单独出现或作为*.前缀,必须带路径(至少/)@require了不存在的内置库@require了本地文件(如./lib.js)@resource指向的文件不存在- 使用了
LFRuntime.network.onResponse/interceptRequest/interceptResponse却没有声明@intercept(liveDebug 与run --tab调试期会自动注入拦截宿主,不做这项检查,所以调试能跑、编译却报错时先看这里)
- 不写
@name时以项目名作为应用名称;不写@match时默认<all_urls>
与油猴脚本的差异
- 只提供本页列出的指令和 PluginX 内置库,没有
GM_*API 与unsafeWindow;@grant等指令写了也无效。需要读取页面自身的 JS 变量请用LFRuntime.runInMainWorld - 应用只注入顶层页面,不注入 iframe(不需要写
@noframes) @include在@match范围内收窄,而不是追加运行页面(见「匹配规则详解」)
@require 的作用范围
@require 同时决定编译期允许使用哪些全局变量:代码里用到某个库的全局变量(如 LFHttp)却没在入口声明对应 id,编译会报错。@require 填的是小写库 id(如 user),不是全局名:写成 // @require LFUser 或其它未知 id 时,编辑器会在该行直接标红并提示正确写法,编译与终端 run 也都会失败。
内置库只有在入口文件里 @require 才会被注入插件构建(编译、liveDebug、已启用 / 已安装应用)。写在非入口文件头部的 @require 只在终端直接 run <该文件> 时生效:编辑器会在这类行上给出 Warning,其它文件也不能借用它——在别的文件里使用该库仍会标红。组件、工具模块用到的库请统一在入口声明。
// tools/probe.js —— 非入口文件,只用于终端 `run tools/probe.js`
// @require http
const data = await LFHttp.json("https://open.er-api.com/v6/latest/USD");
LFRuntime.print(data.rates.CNY);@require dom 只保证 LFDOM 内部依赖的能力(如 LFDOM.waitForResponse)可用;代码里直接使用 LFNetwork 仍须显式写 // @require network。
@resource
写法:// @resource <别名> <路径>,别名与路径之间用空白分隔,路径可加引号。
- 路径先按工作区绝对路径查找,找不到再相对入口文件解析;文件不存在时编译失败
- CSS 资源会在应用启动时自动注入页面,无需再调用
LFRuntime.appendStyle - 文本资源(HTML、CSS、JSON 等)
getResource返回文件内容;图片等二进制资源返回data:URL;别名不存在时返回空串,不会抛错 - 别名
compiledStyle是保留名:源码中import的样式会以这个别名打进产物,请不要自己使用
完整示例
入口文件头部
// ==UserScript==
// @name 订单增强
// @description 在订单页注入批量操作面板
// @match https://*.commerce.com/*
// @include *://*.commerce.com/bg*
// @exclude *://*.commerce.com/bg/login*
// @exclude-match https://static.commerce.com/*
// @run-at document_idle
// @require dom
// @require store
// @resource panel ./panel.html
// @intercept /api/orders
// ==/UserScript==