一、项目简介:
这篇文件介绍的是油猴插件脚手架Wokoo的开发过程与复盘。Wokoo是我开发的脚手架,用来快速起一个基础项目用于油猴插件
的开发。
- Wokoo的npm地址:wokoo
- Wokoo的github地址:wokoo (如果觉得不错,请点小星星)
- Wokoo脚手架开发的油猴插件有:
- Wokoo使用说明文档 👉 5分钟上手开发浏览器插件——油猴脚手架wokoo(使用篇)
- 不了解什么是油猴插件?戳这里 👉 Chrome 插件大杀器:「油猴」Tampermonkey 使用详解
在这个项目中我扮演了项目的产品经理
,开发主R
,运营官
这几个角色。从头到尾负责开发一个开源项目,让我有了满满的成就感。
回想当初想做Wokoo脚手架的初衷,只是因为自己想写一个油猴插件,发现比较麻烦,自己也趟了一些坑。我就觉得写一个脚手架,能够一键生成基础的油猴项目很有意义。完成Wokoo脚手架的开发后,我在某技术群中分享了这篇文章,被该圈里的大佬邀请做一次分享。于是我又增加了使用Wokoo进行项目实战的案例。然后又指导群里成员完成从零上手,实现一个油猴插件的开发。
整个过程在当初只是一个小小的idea,在逐步升级打怪的过程中,我把当初的小想法变成了一个正式的开源项目。这次经历让我得到很多成长,包括技术、沟通、合作等方面。感谢这次经历~ 🎉
二、项目背景:
开发Wokoo脚手架初衷
一开始我想做一个给团队内部使用的浏览器插件。进行技术选型时发现开发油猴插件开发最省时省力,而且不用审核上线迅速,是最佳选择。
开发过程遇到一些坑:
油猴插件本质上是嵌入一段js代码到当前html中。所以很多油猴插件都直接使用jQuery进行开发。这对于我们习惯使用了Vue、React等框架的rd来说会不适应,尤其是插件涉及到页面组件开发,使用jQuery有点痛苦。这就是第一个痛点:要配置vue或react基础项目
所以我就考虑用vue-cli起一个vue项目来完成插件开发。项目创建完成后,我又发现要配置油猴脚本。下面这个编辑器里的代码就是要配置的油猴脚本,里面的注释都是有含义的,比如
@match
指定某些域名下开启此插件,// @match http://*/*
表示只能在http协议的网页中使用。默认配置的就是http协议,但是现在的网页都升级到https了,如果不修改此条配置,你会发现插件根本跑不起来。👇
第二个痛点:要阅读油猴脚本的文档,理解里面的注释内容
- 使用vue开发的项目能够通过
localhost:8080
访问,但是怎么把项目和油猴插件结合起来,实现一边开发项目,一边实时调试插件?总不能一边开发,一边npm run build
构建出包,再把构建结果复制到油猴插件的编辑器中吧?第三个痛点:如何实现边开发边展示插件效果?
业务场景
总结下,我想实现一个针对开发油猴插件的脚手架,满足如下功能:
- 输入一个命令行生成一个基础项目
- 项目中包含基础的油猴脚本配置
- 提供方案,让油猴插件能边开发,边调试
解决的痛点
wokoo脚手架就是为了满足上业务场景,解决我在开发油猴插件时遇到的3个痛点。
一键式创建基础的油猴插件项目,可选模板有:
[ ] vue
[ ] react
基础项目中的tampermonkey.js文件中满足油猴插件的基础配置,例如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18// ==UserScript==
// @name my-plugin
// @namespace http://tampermonkey.net/
// @version 0.0.1
// @description try to take over the world!
// @author
// @match https://*/*
// @match http://*/*
// ==/UserScript==
;(function () {
if (location.href === 'http://localhost:8080/') return
var script = document.createElement('script')
script.src = 'http://localhost:8080/app.bundle.js'
document.body.appendChild(script)
})()开发者直接复制这段代码到油猴插件编辑器就行,解决上面的第二个痛点。
当然开发者在开发过程中是要做到阅读 tampermonkey官网文档 的,wokoo脚手架提供的基础文档配置,目的是降低油猴插件的开发难度。
tampermonkey.js中写的js脚本,将油猴插件加载的js文件打到本地用webpack起的服务上,实现边开发边调试。解决上面的第三个痛点。
三、实践过程:
技术选型
- 参考了create-react-app的设计思路,将
wokoo-scripts
和wokoo-template
部分解耦,拆分成两个包单独管理,有利于未来的功能拓展。比如未来要新增一个模板是基于jquery的,只需要拓展wokoo-template部分即可。 - monorepo方案管理代码,使用lerna进行包管理。
开发过程
这张图是Wokoo的架构设计图,Wokoo使用monorepo方案管理代码,在一个git仓库中维护wokoo-scripts
和wokoo-template
两个模块。使用lerna进行包管理。
思路整理
我要实现的功能如下:
安装wokoo,执行
npm i wokoo -g
后在npm的全局目录下安装wokoo执行命令
wokoo my-app
,弹出要求用户选择模板的提示1
2
3? which template do you prefer? (Use arrow keys)
❯ vue
react选择模板后,创建一个基础项目,项目内容包括:1. 配置好的vue或react项目,2.
tampermonkey.js
文件,内部是配置好的基础油猴脚本。
模仿cra的实现思路,我将wokoo拆分成两个部分,wokoo-scripts
和wokoo-template
。
wokoo-scripts
负责命令行的交互,从npm上拉取wokoo-template
到生成的基础项目my-app中。wokoo-template
提供两套模板,分别是轻量级配置的vue和react基础项目。还提供tampermonkey.js
文件
整个项目的工作流入下👇:
wokoo-scripts
目录介绍:
1 | . |
代码流程图:
图中介绍了wokoo-scripts
各个方法的调用顺序: init -> createApp ->run->modifyTemplate。以及各方法实现的功能。
wokoo-template
目录介绍:
1 | . |
wokoo-template
提供基础项目的模板:
分为vue-template和react-template
vue-template和react-template分别对应webpack配置的一个vue或react基础项目
使用ejs模板,实现wokoo-scripts注入变量
tampermonkey.js 文件是油猴插件的配置文件,需要将此文件内的代码复制到油猴插件的编辑框中。如下是tampermonkey.js文件的内容:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19// ==UserScript==
// @name <%=projectName%>
// @namespace http://tampermonkey.net/
// @version <%=version%>
// @description try to take over the world!
// @author
// @match https://*/*
// @match http://*/*
// ==/UserScript==
;(function () {
if (location.href === 'http://localhost:8080/') return
var script = document.createElement('script')
script.src = 'http://localhost:8080/app.bundle.js'
document.body.appendChild(script)
})()
此文件中被注释掉的//@xxx
都有含义,可以对应着 tampermonkey开发文档 理解。
@name
脚本的名字,最后会被替换成项目名@namespace
可以写自己的域名,当自己把脚本分享后,用户可以直接通过这儿找到你的具体功能实现@description
插件描述@match
指定某些域名下开启此插件,默认配了两条,// @match https://*/*
和// @match https://*/*
表示在所有域名下都开启。
详细的开发过程可以阅读文章:wokoo脚手架(搭建篇)
成果展示
创建基础项目
1
npx wokoo my-app
起服务
1
npm start
复制tampermonkey.js内容到油猴插件编辑器,注意:要复制全部内容,包括注释部分。(此步骤默认你已经安装了油猴插件,没安装的话就安装下 👉油猴插件安装地址)
- 打开某网页,你能看到一只的猴子🐒,代表流程已跑通,你只需开发自己的业务代码即可。
开发中遇到的问题及解决方案
问题1
油猴插件的本质是往页面中注入一段js脚本,要注意避免和原始网页之间的冲突。拿知乎举例子,
- 使用wokoo脚手架创建的项目的根节点id = root ,而知乎根节点网页中已有一个div节点id=root,命名重复了。
- 不光根节点id的命名要注意避免和宿主网页中的元素重复,还要wokoo创建的项目A和项目B的根节点id不同。因为用户可能同时使用多个油猴插件,比如「划词搜索」和「知乎目录」一起用,这就要确保两个插件的根节点id是不一样的。
解决方式
将wokoo-template中的根节点根据wokooApp-${项目名}-${时间戳}
的规则进行命名,避免冲突。
问题2
有的网页安全策略做的比较好,比如知乎使用了csp内容安全策略,阻止加载非指定域名的js脚本。
结合实际情况来看,比如我们已经使用wokoo初始化一个项目my-app,并完成了油猴脚本的配置,打开知乎发现右上角没有出现猴子logo,并且控制台报错。
再看请求的html资源的响应头,可以发现多了一条content-security-policy
规则。也就是说知乎使用了csp内容安全策略,通过content-security-policy
中的script-src
字段可知,知乎只允许加载指定域名的js。具体情况可阅读 👉 内容安全策略( CSP )
而将tampermonkey.js文件拷贝到油猴插件编辑器时,有下面一段代码👇
1 | // ==/UserScript== |
因为我们在调试的时候为了保证实时查看插件效果,往页面的html拼了一个js文件上去,而该文件指向webpack起的服务:http://localhost:8080/app.bundle.js
。这不是知乎指定的域名,当然被拦截了。
解决方式
怎么办呢?嘿嘿,魔高一尺道高一丈,wokoo脚手架当然给出了解决方案。
开发阶段,安装插件Disable Content-Security-Policy, 在调试知乎页面时开启插件,自动把html页面的
content-security-policy
给设置为空。上线到油猴市场时,将构建后的脚本复制到油猴插件编辑器中,避免使用cdn的方式部署。
注意,编辑框内对代码有最大限制,如果app.bundle.js大小超过最大限制,要进行拆包处理。
拆包步骤:
油猴编辑器内的配置代码,增加两行。此处我用react举例,把react通过静态资源链接的方式引入
1
2// @require https://unpkg.com/react@17/umd/react.production.min.js
// @require https://unpkg.com/react-dom@17/umd/react-dom.production.min.js修改webpack.config.base.js的entry字段
1
2
3
4
5
6
7
8entry: {
app: '/src/index.js',
vendor: [
// 将react和react-dom这些单独打包出来,减小打包文件体积
'react',
'react-dom',
],
}重新构建代码,把代码复制到编辑框中。
具体的wokoo使用方式我有写专门的文档:
四、总结思考:
技术上收获
- 为了把脚手架写的专业一点,我去研究了create-react-app的源码,看大佬写的代码真的是佩服,自己以后在写代码时也要多考虑组织结构,逻辑解耦。
- 使用monorepo方案,一个git仓库管理多个库文件(包括wokoo-script和wokoo-template,发现真香👍。 解决了开发多个相关联的库时,需要切换不同库的仓库,代码等问题,保证工作流的连续。
总之,在搞技术的道路上还是一句话:「纸上得来终觉浅,绝知此事要躬行」。
思维上收获
我意识到做一件事情,小到一次分享,一篇文章,大到一个开源项目,一个产品,最根本的出发点应该是「更好的服务产品的使用方」。只有服务好使用方,你做的事情才能发挥最大价值。
比如当初在群里分享利用Wokoo脚手架开发插件「划词搜索」时,发现总是有人问我一些简单的问题,我当时就想文档里明明写了啊,后来再次读那段内容时,我发现我是站在开发者的角度去写文档,而没有站在使用者的角度写。
任何事情,一开始看可能觉得难度巨大,但是拆解成一件件小的事情后就会觉得不是那么困难。就像
Wokoo脚手架
,也是被拆分成多个步骤完成:- 开发脚手架
- 编写使用文档
- 写油猴插件实战案例
所以遇到事情先拆解,拆分成几个小步骤来完成。不要一看到大项目就畏难就觉得搞不定。
五、往期文章
除了油猴脚手架wokoo,我还造了其他轮子🎉🎉:
欢迎大家在搞技术的路上一起升级打怪造轮子~
——
感谢你的阅读😊,希望这篇文章对你有所帮助。