jjjjjj 2019-06-29
之前讲了很多关于项目工程化、前端架构、前端构建等方面的技术,这次说说怎么写一个完美的第三方库。
js
模块化的发展大致有这样一个过程 iife => commonjs/amd => es6
,而在这几个规范中:
iife
: js
原生支持,但一般不会直接使用这种规范写代码amd
: requirejs
定义的加载规范,但随着构建工具的出现,便一般不会用这种规范写代码commonjs
: node
的模块加载规范,一般会用这种规范写 node
程序es6
: ECMAScript2015
定义的模块加载规范,但到目前为止,几乎所有的 js
运行环境都不支持,包括浏览器、node
(包括 electron
、nw.js
)、React Native
等针对原生不支持任何规范的运行环境程序(如浏览器、React Native
),建议使用 es6
规范来写代码,然后由工具转换成原生 js
能够运行的。
而针对 node
程序,可以直接用 commonjs
规范来写,也可由 es6
规范来写,然后用工具转化成 commonjs
规范。
所以,总的来说,都可以使用 es6
规范来写代码,然后用工具转换成其他规范,而且 es6
的代码可以使用 tree-shaking
功能。
参考:
对于前端项目来说,因为有静态资源(如图片、字体等)加载与按需加载的需求,所以使用 webpack
是不二选择,但对于第三方库来说,其实还有更好的选择:rollup
。
可以查看 webpack 之外的另一种选择:rollup 了解 webpack
与 rollup
之间各自的差异与优势。
webpack
在打包成第三方库的时候只能导出 amd/commonjs/umd
,而 rollup
能够导出 amd/commonjs/umd/es6
。使用 rollup
导出 es6
模块,就可以在使用这个库的项目中构建时使用 tree-shaking
功能。
对于有样式文件(css
、less
、scss
)、静态资源文件(图片、字体)的前端组件来说,可以使用 rollup-plugin-postcss 插件配合 rollup
处理样式文件与静态资源文件。
参考:
一般库项目的目录:
|-- / # 项目根目录 |-- src/ # 源代码目录 |-- lib/(dist/) # 发布文件目录 |-- test/ # 测试文件目录 |-- ... # 更多其他目录
如果是多包项目(一个项目里有多个 npm packages,比如 babel):
|-- / # 项目根目录 |-- packages/ # packages 目录 |-- pkg1/ # package1 目录 |-- src/ # 源代码目录 |-- lib/(dist/) # 发布文件目录 |-- pkg2/ # package2 目录 |-- src/ # 源代码目录 |-- lib/(dist/) # 发布文件目录 |-- ...
后面会详细讲解多包项目。
不管是应用项目还是第三方库项目,都需要搭建一个好的脚手架,来帮助我们更好的编写代码、构建项目等。
可以查看 搭建自己的前端脚手架 了解一些基本的脚手架文件与工具。
比如:
.editorconfig
: 用这个文件来统一不同编辑器的一些配置,比如 tab
转 2 个空格、自动插入空尾行、去掉行尾的空格等,http://editorconfig.orggit
提交之前对代码进行审查,否则不予提交.travis.yml
: 一个很棒的持续集成服务,https://www.travis-ci.org/详细的文件、工具与配置,参考 搭建自己的前端脚手架。
另外,针对开源的第三方库,还可以有:
LICENSE
: 协议文件CONTRIBUTING.md
: 项目代码参与者codecov.yml
: 测试覆盖率配置文件.github
: github
上的一些自定义配置,比如 issue
模板、pr
模板等/docs
: 文档目录/examples
: 使用示例目录/scripts
: 脚本目录加上 rollup
的配置文件 rollup.config.js
:
如果是 node
程序,把 es6
规范转化成 commonjs
规范:
export default { input: 'src/index.js', output: { file: 'lib/index.js', format: 'cjs', }, };
如果是前端库,还需要转 es6+
到 es5
、导出不同规范的文件(es6/commonjs/amd/umd
):
import babel from 'rollup-plugin-babel'; import postcss from 'rollup-plugin-postcss'; export default [ { file: 'lib/cjs.js', format: 'cjs', }, { file: 'lib/m.js', format: 'esm', }, { file: 'lib/umd.js', format: 'umd', name: 'Name', }, { file: 'lib/amd.js', format: 'amd', }, ].map(output => ({ input: 'src/index.js', output, plugins: [ babel({ presets: ['@babel/preset-env'], }), postcss({ extract: !0 }), // 构建样式文件时需要这个插件 ], }));
一般来说,我们并不希望把发布文件放到 git
的版本控制之中,而只是发布到仓库而已,所以:
# .gitignore .DS_Store node_modules bower_components /coverage *.log .idea .vscode .eslintcache package-lock.json /lib # 把 lib 排除在外 /packages/*/lib # 多包项目
{ ... # node 项目 "main": "lib/index.js", # 前端项目 "main": "lib/cjs.js", # commonjs 规范文件 "module": "lib/m.js", # es6 规范文件 "umd:main": "lib/umd.js", # umd 规范文件 "amd:main": "lib/amd.js", # amd 规范文件 "files": [ # 发布时只发布 lib 目录下文件 "lib" ], "scripts": { ... "build": "rollup -c", # 构建发布文件 "prepublishOnly": "npm run build", # npm publish 之前先 npm run build "pretest": "npm run build", # npm run test 之前先 npm run build }, ... }
在实际项目中,构建工具(如 webpack
)会首先找这个包中的 module
字段对应的 es6
规范文件,并使用 tree-shaking
;如果不存在,然后找 main
字段对应的文件。
有些构建工具可能也会用 amd
规范文件与 umd
规范文件。
参考:
如果一个项目很大,需要分割成多个 npm
包进行管理,但这些包仍然在一个项目里,并且这些包可能有相互依赖关系,这个时候就比较难以管理和开发了。
为了方便的管理多包项目,lerna 便应运而生,babel、create-react-app、jest、lila 等都是用 lerna
来管理多个包的。
英文不好的童鞋,可以参考 使用lerna管理大型前端项目,了解 lerna
的一些基本用法。
my-lerna-repo/ package.json packages/ package-1/ package.json package-2/ package.json
# 安装 npm i -g lerna
# 初始化 git init lerna-repo && cd lerna-repo lerna init
# 初始化后的目录及文件 lerna-repo/ packages/ package.json lerna.json
{ "version": "0.5.2", # 当前版本号 "packages": [ "packages/*" ], "command": { "publish": { # 发布配置 "ignoreChanges": [ # 哪些文件变动不会引发发布新版本 "*.md", "*.json", "*.txt", "test/**", "example/**", "package.json" ] }, "bootstrap": { "npmClient": "cnpm" # lerna bootstrap 时使用哪个 npm 客户端 } }, "npmClientArgs": [ # npm 客户端 运行时的参数 "--no-package-lock" ] }
lerna publish
: 发布所有有更新的包在默认的固定模式(Fixed mode)下,这个命令会检查 packages
目录下哪些包的文件有更新(lerna.json
中 command.publish.ignoreChanges
除外),然后把 lerna.json
中的 version
与有更新的包中 package.json
的 version
字段更新到一个新的版本号上,最后把这些有更新的包都发布到远程仓库上。
lerna bootstrap
: 启动建立包相互之间的 node_modules
链接这个命令会根据各个包下 package.json
里面的 dependencies
和 devDependencies
配置,使用 symlink
在各个包的 node_modules
下面建立引用关系。这样就解决了相互之间有依赖而无法联调的问题。
lerna changed
: 查看哪些包有更新,可以发布一个新的版本lerna diff [package?]
: 查看包都更新了些什么lerna run [script]
: 使用 npm
运行每个包下面的 [script]
参考:
单个包的 node
项目可以参考我的项目:sclean
单个包的前端项目可以参考我的项目:see-fetch
多个包的项目可以参考我的项目:lila
更多博客,查看 https://github.com/senntyou/blogs
版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)