油猴插件
油猴插件 泛指 Tampermonkey, Violentmonkey, Greasemonkey 这类给页面注入 js 的浏览器扩展
通过在浏览器扩展在网页加载时注入特定的 js 达到更改页面布局效果等等一切 js 能做的事情
这些扩展也提供了一些网页没有的 api,使注入的 js 能突破网页默认的限制,实现其他的功能
现有油猴脚本开发模式
现有的油猴脚本一般是两种开发模式
扩展自带的编辑器
这种就是纯手写了,只能写原生 js,没有代码提示,没有模块的概念,只能通过 @require 使用 npmjs.com 的库
由于没有工程化的概念,没有模块热替换,当代码有任何改动就得重新刷新宿主页面才能查看效果
并且代码只能在一个单文件里,功能少还好,功能和项目稍微大点,就完全不适合编辑了
这种是完全落后的方式,效率和开发体验都及其低下
webpack/rollup/uglify-js/esbuild
这类开发方式相比前者是极大的进步,有了模块化和打包的概念,开发方式也从简陋编辑器改到了 vscode,代码提示,typescript 都不是问题
开发者提前在扩展编辑器里写好桥接的代码,比如把 path/dist/js 写在 userscript 的 @require,或者在 userscript 下面写 动态添加 script src=path/dist/js
但是,rollup/uglify-js/esbuild 不支持模块热替换(hmr),仅有 webpack 支持
试想一下,如果没有 模块热替换 你的代码在 vscode 里每改动一下,你都要手动刷新宿主页面,并且手动点击恢复刚刚页面的状态,这和开发 vue/react 时的体验差的不是一点半点
但是 webpack 的 模块热替换 在这种注入代码的模式下 也存在问题,由于代码的来源
和代码的运行环境
不是一个源,而 webpack dev server 设计的时候只考虑了同源的情况
而且 webpack dev server 是一个插件,不是一个核心功能,比如我想用中间件的形式拦截任意路径的请求就做不到
导致现有的基于 webpack 的油猴脚本插件开发体验都一言难尽
vite 的优势
vite 相比 webpack,最大的提升就是 速度 和 开箱即用,相信用过 vite 的都能体验它的优点
vite 比 webpack 好的其中两个点就是 它明确区分了 开发模式 和 构建模式,并暴露出了 本地开发服务器 的 api 供外部使用
插件可以注册一个中间件,用来生成中间桥接代码,无需每次生成文件或手动填写代码
另一个点是 vite 的 hmr 服务器是 在 /@vite/client
模块里,开发模式时 vite 向 index.html 注入 <script type="module" src="/@vite/client"></script>
插件可以修改这块代码,让 vite 兼容 两个 host 工作的场景
使用插件开发
接下来我们使用 vite-plugin-monkey 这个插件来开发油猴脚本
提供的功能优势
- 额外的脚手架一键初始化各种模板 vue/react/vanilla/svelte/preact
- 支持 Tampermonkey 和 Violentmonkey 和 Greasemonkey 的脚本辅助开发
- 打包自动注入脚本配置头部注释
- 当 第一次启动 或 脚本配置注释改变时 自动在默认浏览器打开脚本安装
- 友好的利用 @require 配置库的 cdn 的方案,大大减少构建脚本大小
- 完全的 Typescript 和 Vite 的开发体验,比如模块热替换,秒启动
基础配置如下,详情请看文档 https://github.com/lisonge/vi...
export interface MonkeyOption {
/**
* 脚本文件的入口路径
*/
entry: string;
userscript: MonkeyUserScript;
format?: Format;
server?: {
/**
* 当 第一次启动 或 脚本配置注释改变时 自动在默认浏览器打开脚本
* @default true
*/
open?: boolean;
/**
* 开发阶段的脚本名字前缀,用以在脚本安装列表里区分构建好的脚本
* @default 'dev:'
*/
prefix?: string | ((name: string) => string);
};
build?: {
/**
* 打包构建的脚本文件名字 应该以 '.user.js' 结尾
* @default (package.json.name||'monkey')+'.user.js'
*/
fileName?: string;
/**
* @example
* {
* vue:'Vue',
* // 你需要额外设置脚本配置 userscript.require = ['https://unpkg.com/vue@3.0.0/dist/vue.global.js']
* vuex:['Vuex', 'https://unpkg.com/vuex@4.0.0/dist/vuex.global.js'],
* // 插件将会自动注入 cdn 链接到 userscript.require
* vuex:['Vuex', (version)=>`https://unpkg.com/vuex@${version}/dist/vuex.global.js`],
* // 相比之前的,加了版本号,当依赖升级的时候,cdn 链接自动改变
* vuex:['Vuex', (version, name)=>`https://unpkg.com/${name}@${version}/dist/vuex.global.js`],
* // 还可以加依赖名字,不过各个依赖的 cdn basename 都不尽一致, 导致可能没什么用
* }
*
*/
externalGlobals?: Record<
string,
string | [string, string | ((version: string, name: string) => string)]
>;
/**
* 自动识别代码里用到的 浏览器插件api,然后自动配置 GM_* 或 GM.* 函数到脚本配置注释头
*
* 识别依据是判断代码文本里有没有特定的函数名字
* @default true
*/
autoGrant?: boolean;
};
}
脚手架初始化
vite 有官方脚手架 create-vite,插件自然也有,并且使用方式完全一致
pnpm create monkey
# npm create monkey
# yarn create monkey
然后你能从以下模板选择
选择之后,进入目录安装依赖之后,和启动正常的 vite 项目一样
pnpm run dev
就会自动打开默认浏览器安装脚本,开发者只需点击安装
即可,然后打开被注入的网页,即可看到效果
上图示例中初始化的 vite.config.ts 如下
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import monkey from 'vite-plugin-monkey';
export default defineConfig({
plugins: [
vue(),
monkey({
entry: 'src/main.ts',
userscript: {
icon: 'https://vitejs.dev/logo.svg',
namespace: 'npm/vite-plugin-monkey',
match: ['https://www.google.com/'],
},
build: {
externalGlobals: {
vue: [
'Vue',
(version) =>
`https://cdn.jsdelivr.net/npm/vue@${version}/dist/vue.global.prod.js`,
],
},
},
}),
],
});
模块热替换
模块热替换的例子,与正常 vite 项目开发体验一致
构建
构建命令 与正常 vite 项目开发体验一致
pnpm run build
构建完毕后 dist 目录会出现一个 xxx.user.js,文件头部信息如下
// ==UserScript==
// @name vite-project
// @namespace npm/vite-plugin-monkey
// @version 0.0.0
// @icon https://vitejs.dev/logo.svg
// @match https://www.google.com/
// @require https://cdn.jsdelivr.net/npm/vue@3.2.37/dist/vue.global.prod.js
// ==/UserScript==
// use vite-plugin-monkey@1.0.0 at 2022-07-18T10:14:55.580Z
之前在插件的配置自动全部注入到构建后的文件头注释中
总结
vite 提供 速度 和 开箱即用,vite-plugin-monkey 负责处理 油猴脚本 和 vite 之前的关系
保证 油猴脚本 的开发与正常 vite 项目的开发一致,欢迎 issues
和 star
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。