从零开始打造公共库;package.json出口字段;umd产物;

疑问

  • 是否有最佳实践?

    • 答:没有,各自配置各自的。只能说借鉴,因为天生混乱。

      摘录&心得

      从零开始打造公共库,不借助任何的脚手架工具,纯webpack+babel

      一、demo

      1、接入测试

      1、初始化
      1. $ mkdir animal-api
      2. $ cd animal-api
      3. $ npm init 初始化package.json文件
      package.json,注意这里main字段

    1. {
    2. "name": "animal-api",
    3. "version": "1.0.0",
    4. "description": "",
    5. "main": "index.js",
    6. "scripts": {
    7. "test": "echo \"Error: no test specified\" && exit 1"
    8. },
    9. "author": "",
    10. "license": "ISC"
    11. }

    2、编写index.js
    通过 ESM 的方式提供对外接口,注意这里模块化的方式

    1. const getCat = () => {...}
    2. const getDog = () => {...}
    3. const getGoat = () => {...}
    4. export default {
    5. getDog,
    6. getCat,
    7. getGoat
    8. }

    3、安装测试库jest并运行,报错
    因为jest不认识import这样的关键字
    4、安装 babel-jest 和 Babel 相关依赖到开发环境中:
    使用 babel-jest 将测试脚本编译降级为当前 Node.js 版本支持的代码

    1. npm install --save-dev babel-jest @babel/core @babel/preset-env

    5、创建babel.config.js
    将 @babel/preset-env 的 targets.node 属性设置为当前环境 current

    1. module.exports = {
    2. presets: [
    3. [
    4. '@babel/preset-env',
    5. {
    6. targets: {
    7. node: 'current',
    8. },
    9. },
    10. ],
    11. ],
    12. };

    2、使用babel打造UMD产物

  • umd格式支持在浏览器中将公共库以script标签的模式引入

  • 依旧需要babel将代码降级
  • 这里的降级需要输出代码内容到一个 output 目录中,浏览器即可直接引入该 output 目录中的编译后资源。
  • 使用@babel/plugin-transform-modules-umd来完成对代码的降级编译:

    1. $ npm install --save-dev @babel/plugin-transform-modules-umd @babel/core @babel/cli

    在 package.json 中加入相关 script 内容:

    1. "build": "babel index.js -d lib"

    运行后报错,无法找到某个对象,这个对象为从另一个文件导入的对象。
    Babel 的编译产出如果要支持全局命名空间,需要添加以下配置:

    1. plugins: [
    2. ["@babel/plugin-transform-modules-umd", {
    3. exactGlobals: true,
    4. globals: {
    5. index: 'AnimalApi'
    6. }
    7. }]
    8. ],

    配置后,编译源码产出内容改为了由一个 IIFE 形式实现的命名空间。

    3、使用webpack管理依赖

    纯babel的打包产物没有使用引入并编译 index.js 所需要的依赖,正确的方式应该是把公共库需要的依赖,一并按照依赖关系进行打包和引入,因此需要借助webpack
    1、引入 Webpack:

    1. npm install --save-dev webpack webpack-cli

    2、添加webpack.config.js:

  • 设置入口为./index.js

  • 构建产出为./lib/animal-api.js
  • 通过设置 library 和 libraryTarget 将 AnimalApi 作为公共库对外暴露的命名空间

    1. const path = require('path');
    2. module.exports = {
    3. entry: './index.js',
    4. output: {
    5. path: path.resolve(__dirname, 'lib'),
    6. filename: 'animal-api.js',
    7. library: 'AnimalApi',
    8. libraryTarget: 'var'
    9. },
    10. };

    3、修改package.json

    1. "build": "webpack"

    至此,我们终于构造出了能够在浏览器中通过 script 标签引入的公共库。

    4、支持node.js(其他)环境

    Node.js 环境的验证:

    1. const AnimalApi = require('./index.js')
    2. AnimalApi.getCat().then(animal => {
    3. console.log(animal)
    4. })

    意义在于测试公共库是否能在 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是一个受众很广的公共库
      1. "build": "npm run build:main && npm run build:fp",
      2. "build:fp": "node lib/fp/build-dist.js",
      3. "build:fp-modules": "node lib/fp/build-modules.js",
      4. "build:main": "node lib/main/build-dist.js",
      5. "build:main-modules": "node lib/main/build-modules.js",
      总而言之,前端生态天生“混乱”,不统一的运行环境使得公共库的架构,尤其是相关的构建设计更加复杂。