背景
在图转换中间件的开发过程中,需要准备一个开发测试打包一体化环境,该环境至少需要把开发好的中间件代码打包,并上传到我们的私有 npm 仓库中。
中间件代码如下:
准备过程
由于之前的开发测试环境有太多无关代码,现在准备自己重新着手搭建一个纯净的打包环境。优先实现 library(即中间件源码) 文件打包并上传到 npm 中;其次实现本地启动 webpack server,用来作为开发环境调试代码服务器;再次实现本地 vue 项目打包,并放在服务器中可用
创建项目
新建一个项目文件夹,起名 test,然后在当前项目目录运行
可以看到当前生成了一个 package.json 文件
然后在当前目录中新建 src 文件夹,用来存放项目代码。并把我们的中间件文件放入其中。现在文件目录是这样的:
集成 webpack
安装
我们需要把中间件代码打包成一个文件,方便压缩代码和引用等。现在引入 webpack 作为打包工具。由于现在 webpack5 比较新,我们直接安装 webpack5
1
   | npm install webpack@5 --save-dev
   | 
 
当前初始的 package.json 文件是这样的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
   | {   "name": "test",   "version": "1.0.0",   "description": "test",   "main": "index.js",   "scripts": {     "test": "echo \"Error: no test specified\" && exit 1"   },   "keywords": [     "test"   ],   "author": "guan",   "license": "ISC",   "devDependencies": {     "webpack": "^5.24.3"   } }
  | 
 
使用
我们利用 webpack 文件,把一些基本的 webpack 配置写在文件里,然后通过 package.json 中的命令行执行 webpack 命令。
为了试验,我们在 src 目录下新建 index.js 文件,输入测试代码:
1 2 3 4
   | const test = () => {   console.log("hello webpack!"); }; export { test };
  | 
 
在根目录下新建 webpack.config.js 文件,输入以下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
   | const path = require("path");
  module.exports = {   mode: "production",    entry: "./src/index.js",    output: {     filename: "main.js",      library: "ynChartMiddleware",      libraryTarget: "umd",      path: path.resolve(__dirname, "lib"),   },      optimization: {     minimize: false,   }, };
  | 
 
然后在 package.json scripts 字段下加入以下命令:
1
   | "build": "webpack --config webpack.config.js"
   | 
 
现在在控制台执行命令:
控制台报错:
1 2 3 4 5
   | CLI for webpack must be installed.   webpack-cli (https:
  We will use "npm" to install the CLI via "npm install -D webpack-cli". Do you want to install 'webpack-cli' (yes/no):
   | 
 
因为我们想要使用命令行方式调用 webpack 需要首先安装 webpack-cli,所以我们选择安装;再次执行打包命令后得到目标文件(lib/main.js)代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
   | (function webpackUniversalModuleDefinition(root, factory) {   if (typeof exports === "object" && typeof module === "object")     module.exports = factory();   else if (typeof define === "function" && define.amd) define([], factory);   else if (typeof exports === "object")     exports["ynChartMiddleware"] = factory();   else root["ynChartMiddleware"] = factory(); })(self, function () {   return  (() => {           "use strict";     ...        ...     var __webpack_exports__ = {};     __webpack_require__.r(__webpack_exports__);      __webpack_require__.d(__webpack_exports__, {        test: () =>  test,            });     const test = () => {       console.log("hello webpack!");     };
       return __webpack_exports__;        })(); });
  | 
 
当前文件目录结构:
上图可以看到,我们确实把 src/index.js 文件代码打包成了 lib/main.js
ES6 转 ES5
在上面的实践中,我们可以看到,虽然我们实现了模块化打包,但是我们发现 webpack 并没有自动进行代码版本转换。
原代码:
1 2 3 4
   | const test = () => {   console.log("hello webpack!"); }; export { test };
  | 
 
打包后:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
   | return  (() => {       "use strict";      var __webpack_require__ = {};            (() => {            __webpack_require__.d = (exports, definition) => {        for (var key in definition) {          if (           __webpack_require__.o(definition, key) &&           !__webpack_require__.o(exports, key)         ) {            Object.defineProperty(exports, key, {             enumerable: true,             get: definition[key],           });           /******/         }         /******/       }       /******/     };     /******/   })(); /* webpack/runtime/hasOwnProperty shorthand */   /******/   /******/ /******/ (() => {      __webpack_require__.o = (obj, prop) =>       Object.prototype.hasOwnProperty.call(obj, prop);        })();         (() => {            __webpack_require__.r = (exports) => {        if (typeof Symbol !== "undefined" && Symbol.toStringTag) {          Object.defineProperty(exports, Symbol.toStringTag, {           value: "Module",         });                }        Object.defineProperty(exports, "__esModule", { value: true });            };        })();         var __webpack_exports__ = {};   __webpack_require__.r(__webpack_exports__);    __webpack_require__.d(__webpack_exports__, {      test: () =>  test,        });   const test = () => {     console.log("hello webpack!");   };
     return __webpack_exports__;    })();
  | 
 
这样的代码在 ie11 中无法运行,我们得把 es6 语法编译为 es5。
首先我们把 webpack 自己生成的打包代码变为 es5,这需要再 webpack.config.js 中配置参数:
该参数意思是打包时编译为类浏览器环境,并按 es5 的风格进行打包。但是这样只会改变 webpack 默认的打包代码,而我们自己的代码任然不会被转码。
因此我们需要引入 babel,来转码我们的 js 文件。先在 webpack 中添加 babel 规则,可以正则检测文件。也可以认为,在 webpack 中使用 babel 就需要使用 babel-loader 来作为连接纽带,用来编译目标文件代码:
1 2 3 4 5 6 7 8 9 10 11
   | module: {     rules: [       {         test: /\.m?js$/,         exclude: /(node_modules)/,         use: {           loader: "babel-loader",         },       },     ],   },
  | 
 
我们需要安装 babel-loader 包和 @babel/core 包,并且需要在根目录创建一个 babel 配置文件,文件名为”.babelrc”,该文件用来告诉 babel 应该如何转码文件,该文件配置如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
   | {     "presets": [         [             "@babel/preset-env",             {                 "useBuiltIns": "usage",                 "modules": false,                 "corejs": {                     "version": 3                 },                 "targets": {                     "ie": "11"                 }             }         ]     ] }
  | 
 
该文件主要定义了输出的代码版本,以上用到了 @babel/preset-env 用来转换代码,所以我们需要安装它。
现在我们再次执行 build 命令,得到如下编译过后的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
   | return  (function () {       "use strict";      var __webpack_require__ = {};            !(function () {            __webpack_require__.d = function (exports, definition) {        for (var key in definition) {          if (           __webpack_require__.o(definition, key) &&           !__webpack_require__.o(exports, key)         ) {            Object.defineProperty(exports, key, {             enumerable: true,             get: definition[key],           });           /******/         }         /******/       }       /******/     };     /******/   })(); /* webpack/runtime/hasOwnProperty shorthand */   /******/   /******/ /******/ !(function () {      __webpack_require__.o = function (obj, prop) {       return Object.prototype.hasOwnProperty.call(obj, prop);     };        })();         !(function () {            __webpack_require__.r = function (exports) {        if (typeof Symbol !== "undefined" && Symbol.toStringTag) {          Object.defineProperty(exports, Symbol.toStringTag, {           value: "Module",         });                }        Object.defineProperty(exports, "__esModule", { value: true });            };        })();         var __webpack_exports__ = {};   __webpack_require__.r(__webpack_exports__);    __webpack_require__.d(__webpack_exports__, {      test: function () {       return  test;     },        });   var test = function test() {     console.log("hello webpack!");   };
     return __webpack_exports__;    })();
  | 
 
可以看到,我们的代码已经正确的转换了箭头函数,已经把 es6 风格的代码转换成了 es5
综上所述,babel 用来转换代码版本,需要 3 个包:@babel/core babel-loader @babel/preset-env
打包中间件
打包
现在我们已经用测试文件做好了前期准备,现在我们把 webpack.config.js 中的入口文件改为中间件文件,然后出口文件名改为中间件名。
可以看到报了如下错误:
这提示我们没有安装 core-js 模块,无法找到对应的转换方法。执行:
1
   | cnpm install core-js --save-dev
   | 
 
再重现 build,发现中间件中使用了 echarts,而我们也需要安装一下 echarts 包;当所有需要用到的包全部安装完毕后,我们可以看到,打包成功了。
优化打包大小
我们查看打包文件,代码风格是我们需要的 es5,但是打包文件有 3.5M,这明显太大了,我们需要减小大小。
首先第一步是代码压缩,压缩后大小从 3.5M 变为了 0.9M,明显还是太大了。
我们思考是不是可以不要打包某些包进中间件,比如 echarts。因为中间件宿主环境,也一定会引入 echarts,所以 echarts 可以不用打包进中间件。我们在 webpack 配置文件中加入以下代码:
1 2 3 4 5 6 7 8
   | externals: {     echarts: {       commonjs: "echarts",       commonjs2: "echarts",       amd: "echarts",       root: "_",     },   },
  | 
 
重新打包,我们发现包文件已经变为 66KB,相比于之前的 3.5M,已经小非常多了。现在这个库文件,就已经打包完成了。
上传 npm
上传到 npm 中时,包名默认 package.json 中的 name 字段,引用路径默认 main 字段。比如我们把 package.json 改为如下,代表我们包名为 yn-chart-middleware,用户下载包后默认会引用“./lib/yn-chart-middleware.js”目录下的文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
   | {   "name": "yn-chart-middleware",   "version": "1.0.0",   "description": "test",   "main": "./lib/yn-chart-middleware.js",   "scripts": {     "test": "echo \"Error: no test specified\" && exit 1",     "build": "webpack --config webpack.config.js"   },   "keywords": [     "test"   ],   "author": "guan",   "license": "ISC",   ...   ... }
  | 
 
发布包时先使用 npm login 登录 npm,然后 npm publish 即可发布自己的包
注意:内网环境下,需要先切换 npm 源,然后登录内网 npm。之后的操作和 npm 一致
在有时候,我们不希望自己的开发文件,比如 src/目录下的文件被上传到 npm,我们可以选择不上传这部分文件到 npm 上。如下,在 package.json 中增加如下代码:
表明只把根目录下 lib 文件夹内容上传到 npm
除了上面的白名单模式,还有黑名单模式(及指定不传的文件,上传所有其他文件)
配置代码以及目录结构
目录结构:
.babelrc 文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
   | {     "presets": [         [             "@babel/preset-env",             {                 "useBuiltIns": "usage",                 "modules": false,                 "corejs": {                     "version": 3                 },                 "targets": {                     "ie": "11"                 }             }         ]     ] }
  | 
 
webpack.config.js 文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
   | const path = require("path");
  module.exports = {   mode: "production",    entry: "./src/yn-chart-middleware/index.js",    output: {     filename: "ynChartMiddleware.js",      library: "ynChartMiddleware",      libraryTarget: "umd",      path: path.resolve(__dirname, "lib"),    },      optimization: {     minimize: true,   },   target: ["web", "es5"],   module: {     rules: [       {         test: /\.m?js$/,         exclude: /(node_modules|bower_components)/,         use: {           loader: "babel-loader",         },       },     ],   },      externals: {     echarts: {       commonjs: "echarts",       commonjs2: "echarts",       amd: "echarts",       root: "_",     },   }, };
  | 
 
package.json 文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
   | {   "name": "test",   "version": "1.0.0",   "description": "test",   "main": "index.js",   "files": [ 		"lib" 	],   "scripts": {     "test": "echo \"Error: no test specified\" && exit 1",     "build": "webpack --config webpack.config.js"   },   "keywords": [     "test"   ],   "author": "guan",   "license": "ISC",   "devDependencies": {     "@babel/core": "^7.13.8",     "@babel/preset-env": "^7.13.9",     "babel-loader": "^8.2.2",     "core-js": "^3.9.1",     "webpack": "^5.24.3",     "webpack-cli": "^4.5.0"   },   "dependencies": {     "echarts": "^5.0.2",     "number-precision": "^1.5.0"   } }
  | 
 
参考资料
webpack 手册