从零开始打造公共库;package.json出口字段;umd产物;
疑问
是否有最佳实践?
{
"name": "animal-api",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC"
}
2、编写index.js
通过 ESM 的方式提供对外接口,注意这里模块化的方式const getCat = () => {...}
const getDog = () => {...}
const getGoat = () => {...}
export default {
getDog,
getCat,
getGoat
}
3、安装测试库jest并运行,报错
因为jest不认识import这样的关键字
4、安装 babel-jest 和 Babel 相关依赖到开发环境中:
使用 babel-jest 将测试脚本编译降级为当前 Node.js 版本支持的代码npm install --save-dev babel-jest @babel/core @babel/preset-env
5、创建babel.config.js
将 @babel/preset-env 的 targets.node 属性设置为当前环境 currentmodule.exports = {
presets: [
[
'@babel/preset-env',
{
targets: {
node: 'current',
},
},
],
],
};
2、使用babel打造UMD产物
umd格式支持在浏览器中将公共库以script标签的模式引入
- 依旧需要babel将代码降级
- 这里的降级需要输出代码内容到一个 output 目录中,浏览器即可直接引入该 output 目录中的编译后资源。
使用@babel/plugin-transform-modules-umd来完成对代码的降级编译:
$ npm install --save-dev @babel/plugin-transform-modules-umd @babel/core @babel/cli
在 package.json 中加入相关 script 内容:
"build": "babel index.js -d lib"
运行后报错,无法找到某个对象,这个对象为从另一个文件导入的对象。
Babel 的编译产出如果要支持全局命名空间,需要添加以下配置:plugins: [
["@babel/plugin-transform-modules-umd", {
exactGlobals: true,
globals: {
index: 'AnimalApi'
}
}]
],
配置后,编译源码产出内容改为了由一个 IIFE 形式实现的命名空间。
3、使用webpack管理依赖
纯babel的打包产物没有使用引入并编译 index.js 所需要的依赖,正确的方式应该是把公共库需要的依赖,一并按照依赖关系进行打包和引入,因此需要借助webpack
1、引入 Webpack:npm install --save-dev webpack webpack-cli
2、添加webpack.config.js:
设置入口为./index.js
- 构建产出为./lib/animal-api.js
通过设置 library 和 libraryTarget 将 AnimalApi 作为公共库对外暴露的命名空间
const path = require('path');
module.exports = {
entry: './index.js',
output: {
path: path.resolve(__dirname, 'lib'),
filename: 'animal-api.js',
library: 'AnimalApi',
libraryTarget: 'var'
},
};
3、修改package.json
"build": "webpack"
至此,我们终于构造出了能够在浏览器中通过 script 标签引入的公共库。
4、支持node.js(其他)环境
Node.js 环境的验证:
const AnimalApi = require('./index.js')
AnimalApi.getCat().then(animal => {
console.log(animal)
})
意义在于测试公共库是否能在 Node.js 环境下使用。执行node node-test.js报错。
解决方案1
- 编译产出是UMD 格式天然支持浏览器和 Node.js 环境的。
- 我们通过 Webpack 编译出来了符合 UMD 规范的代码,尝试修改node.test.js文件为从lib下的产物中 require(‘./lib/index.js’)引入即可。
- 并不优雅
- 解决方案2
- 将公共库按环境区分,分别产出两个 bundle,分别支持 Node.js 和浏览器环境。
- 如果一个 npm 需要在不同环境下加载 npm 包不同的入口文件,涉及修改package.json中以下3个字段
- main : 定义了 npm 包的入口文件,browser 环境和 node 环境均可使用
- module : 定义 npm 包的 ESM 规范的入口文件,browser 环境和 node 环境均可使用
- browser : 定义 npm 包在 browser 环境下的入口文件
二、从开源库总结生态设计
主要看受众。
- simple-date-format 可以将 Date 类型转换为标准定义格式的字符串类型
- 是一个函数 helper 库,同时支持浏览器和 Node.js 环境。
- 它采取了比较“偷懒”的方式,使用了 UMD 规范来输出代码。
- lodash
- 看其构建脚本就明白了,设置了各种产物格式,甚至还可以自定义
- 因为lodash是一个受众很广的公共库
总而言之,前端生态天生“混乱”,不统一的运行环境使得公共库的架构,尤其是相关的构建设计更加复杂。"build": "npm run build:main && npm run build:fp",
"build:fp": "node lib/fp/build-dist.js",
"build:fp-modules": "node lib/fp/build-modules.js",
"build:main": "node lib/main/build-dist.js",
"build:main-modules": "node lib/main/build-modules.js",