提到写 npm 插件,很多没搞过的可能第一感觉觉得很难,无从下手,其实不然。
我们甚至写个简单的 console.log('hello word')
,都是可以当成一个插件发布上去的。
其实无从下手的主要难点还是在于你的具体要做的功能逻辑,这个理清楚了,写插件并没有想象的那么难。
接下来,我们来看下具体插件编写的思路。
拿我想做一个 h5 前端的水印插件为例,思路就是用 canvas 去绘制,创建 dom,然后用 js 把这个 dom 添加到页面中去。
根据这个思路,我要用到的是纯纯的 js 就够了。但是我的文件夹不能乱放吧,该有的代码规范总归还得尽量保持一下(日行一善)。
那就弄个src
目录,入口文件index.js
,主逻辑文件watermark.js
,公共库文件utils.js
,全局配置文件config.js
等等。这样一套下来,目录结构也就起来了。
这个是我最终的插件目录,就大概参考一下。
再想想,怎么在入口文件index.js
引入我们的主逻辑,或者在xx.js
中引入xxx.js
?
是不是想说,用import
、export
呗。
是的!但是这种语法,有的浏览器(拿IE举例)不认识啊,或者你想用一些 es6+ 语法、ts、eslint等等,那我们就得用到打包工具,把它转义成浏览器能识别的 js 代码。
提到打包工具,日常搬砖过程中,最常用的应该是webpack
和rollup
,这里对它俩的区别不过多的介绍,大概的理解为一般涉及到web页面开发相关的插件就用webpack,纯js的功能性插件就用rollup。
这里用rollup来举例,相比webpack,它的配置对新手更容易理解一些。
npm install -s-d rollup typescript rollup-plugin-commonjs @rollup/plugin-typescript
npm install -d @babel/core @babel/preset-env rollup-plugin-babel rollup-plugin-terser
rollup的作用就是把我们最终的源代码进行打包压缩,生成最终的插件文件。这里拿最主要的 3 个配置属性来讲一下。 也可以参考这个配置 package.json、rollup.config.js
{
input: "src/index.ts", // 入口文件,必须
plugins: [
rollupTypescript(),
commonjs({
exclude: "node_modules",
}),
babel({
exclude: "node_modules",
}),
terser(),
],
output: {
// 必须 (如果要输出多个,可以是一个数组)
file: "lib/esm/index.js", // 出口文件,必须
format: "esm", // 必须
banner: "/* watermark version " + version + " */",
footer: "/* up up up */",
sourcemap: false,
},
}
{
"compilerOptions": {
"allowUnreachableCode": true, // 不报告执行不到的代码错误。
"allowUnusedLabels": false, // 不报告未使用的标签错误
"alwaysStrict": false, // 以严格模式解析并为每个源文件生成 "use strict"语句
"baseUrl": "./src", // 工作根目录
"preserveConstEnums": true, // 使用 const enum 产生内联成员
"experimentalDecorators": true, // 启用实验性的ES装饰器
"sourceMap": false,
"noImplicitAny": false, // 是否默认禁用 any
"removeComments": true, // 是否移除注释
"forceConsistentCasingInFileNames": true, //禁止对同一个文件的不一致的引用。
"paths": {
// 指定模块的路径,和baseUrl有关联
"@/*": ["./*"]
},
"types": ["node"],
"target": "es5", // 编译成什么版本
"module": "es6", // 指定生成哪个模块系统代码
"outDir": "examples/monitorHybird/", // 输出目录
"declaration": true, // 是否自动创建类型声明文件
"declarationDir": "examples/monitorHybird/types/", // 类型声明文件的输出目录
"typeRoots": [
// 指定某个文件夹的声明文件
"node_modules/@types"
],
"allowJs": true, // 允许编译javascript文件。
"lib": [
// 编译过程中需要引入的库文件的列表
"es5",
"es2015",
"es2016",
"es2017",
"es2018",
"dom"
]
},
"files": ["src/index.ts"],
"include": ["src/**/*"],
"exclude": ["node_modules", "**/*.spec.ts"]
}
开发前,你要考虑这个插件对外提供哪些属性或方法,它发布了以后别人怎么去用。
import watermark from "watermark-h5";
watermark.init({
parentDomName: 'body', // string 父节点dom选择器名字
show: true, // boolean 水印开关
color: 'rgba(0, 0, 0, 0.06)', // string 水印色值
title: '严禁外传', // string 显示的水印文本
width: 200, // number 单个水印宽度
height: 200, // number 单个水印高度
fontNum: 15, // number 水印字体大小
rotate: -25, // number 旋转角度
zIndex: 9999 // number 层级
});
拿这个水印插件为例,如上述代码的使用方式,开发的时候就考虑以下 3 点。
init
方法目的是为了让用户能够调用身上的方法,写法其实很简单,如下图所示。
这里当然也可以抛出构造函数,让用户自己去new,new的时候传参初始化一些配置,效果其实是一样的。
init
方法用户通过调用这个函数来实现在页面上添加水印的效果,所以,这个方法就是用来执行主逻辑的。
通过init支持用户传递参数过来。
这个就很简单了,根据用户传过来的参数,来执行主逻辑。比如这里的水印内容、颜色、倾斜角度等等,都支持用户自定义配置。不传就用默认值,传了就用用户传过来的参数实现最终效果。
在刚刚第四点中,我们已经提到了打包相关的rollup配置,下图中就是打包流程。执行对应命令,打出最终的插件文件。
至此,距离成功就已经很接近了,只需要把打包生成的文件发布到npm上,别人就能用你写的插件了!!!如果不太熟悉发布流程,可以参考下篇文章~
如何将自己的插件发布到npm上 / 发布到公司npm源上
文中插件源码获取:https://github.com/zttop/watermark-h5#readme