分析 element UI 源码( 三 )—— npm run dist

在前两篇文章中,我们分析了 element-ui 的目录结构,入口文件,组件页面的路由,md-loder。今天我们回归到 的源码中继续分析 package.json 中的 dist 命令,看看 element-ui 打包上线的流程是怎样实现的。

执行 npm run dis 命令会发生什么?

js 复制代码
//package.json
"scripts": {
    "build:file": "node build/bin/iconInit.js & node build/bin/build-entry.js & node build/bin/i18n.js & node build/bin/version.js",
    "build:theme": "node build/bin/gen-cssfile && gulp build --gulpfile packages/theme-chalk/gulpfile.js && cp-cli packages/theme-chalk/lib lib/theme-chalk",
    "build:utils": "cross-env BABEL_ENV=utils babel src --out-dir lib --ignore src/index.js",
    "build:umd": "node build/bin/build-locale.js",
    "clean": "rimraf lib && rimraf packages/*/lib && rimraf test/**/coverage"
    "dist": "npm run clean && npm run build:file && npm run lint && webpack --config build/webpack.conf.js && webpack --config build/webpack.common.js && webpack --config build/webpack.component.js && npm run build:utils && npm run build:umd && npm run build:theme",
    "i18n": "node build/bin/i18n.js",
    "lint": "eslint src/**/* test/**/* packages/**/* build/**/* --quiet",
  },

第 14 行的 dist 命令由 9 个串行命令组成,执行 npm run dist,就会依次执行这九个命令:

  • npm run clean
  • npm run build:file
  • npm run lint
  • webpack --config build/webpack.conf.js
  • webpack --config build/webpack.common.js
  • webpack --config build/webpack.component.js
  • npm run build:utils
  • npm run build:umd
  • npm run build:theme

npm run clean

js 复制代码
"clean": "rimraf lib && rimraf packages/*/lib && rimraf test/**/coverage"

clean 在脚本中对应着以上三个命令,rimraf是一个用来删除文件和文件夹的包。

  • rimraf lib :删除 lib 文件夹
  • rimraf packages :删除 packages 文件夹下的 lib 文件夹
  • rimraf test / ** / coverage :删除 test文件夹下的 coverage 文件夹

该命令用于在打包之前,删除一些文件

npm run build : file

js 复制代码
"build:file": "node build/bin/iconInit.js & node build/bin/build-entry.js & node build/bin/i18n.js & node build/bin/version.js"

node build / bin / iconInit.js

该命令指向 build/bin/iconInit.js 文件 ,它主要进行的操作是从 packages/theme-chalk/src/icon.scss 文件中提取字体图标的类名到 examples/icon.json 中;

node build/bin/build-entry.js

该命令指向 build/bin/build-entry.js 文件,它根据components.json中组件的列表,结合字符串模板生成src/index.js,用来建造组件库的入口文件;

node build/bin/i18n.js

该命令指向 build/bin/i18n.js 文件,它为 examples / pages 中的模板文件生成不同语言的vue文件到 examples / pages / [语言] /;

node build/bin/version.js

该命令指向 build/bin/version.js 文件,它生成版本字符串数组到examples/version.json中,用来表示组件库的版本。

npm run lint

npm run lint 是一个常用的命令,通常用于在 JavaScript 项目中运行代码检查工具。代码检查工具用于分析代码中潜在的错误、bug 和风格问题。当运行 npm run lint 命令时,它将执行 package.json 文件中指定的代码检查工具。

js 复制代码
"lint": "eslint src/**/* test/**/* packages/**/* build/**/* --quiet"

上面的配置将使用 ESLint 检查src、test、packages、build目录中的所有 JavaScript 文件,并根据 .eslintrc.json 文件中的配置指定检查规则和选项。

webpack --config build / webpack.conf.js

使用 webpack 打包,config 指向了 build / webpack.conf.js

  • 入口文件:src / index.js (npm run build:file 生成)
  • 输出:以 umd 形式输出到 lib/index.js,该格式支持客户端使用
  • loader:bable-loader 处理 jsx 等文件;vue-loader 处理 packages 下的 vue 组件

webpack --config build/webpack.common.js

使用 webpack 打包,config 指向了 build/webpack.common.js

  • 入口文件:src / index.js (npm run build:file 生成)
  • 输出:以 commonjs2 形式输出到 lib/element-ui.common.js,该格式支持服务端
  • loader:babel-loader 处理 jsx、babel 和 es6 等文件;vue-loader 处理 packages 下的 vue 组件;style-loadercss-loader 处理 css 文件; url-loader 处理图片等;

webpack --config build/webpack.component.js

config 指向了 build/webpack.component.js,该命令目的是为了实现组件的按需引入

  • 入口文件:components.json ,包含 packages 下的组件
  • 输出:把 packages 下的组件,以 connomjs2 形式分别输出到 lib目录
  • loader:babel-loader 处理 jsx、babel 和 es6 等文件;vue-loader 处理 packages 下面的 vue 组件;style-loadercss-loader 处理 css 文件; url-loader 处理图片等
  • externals:externals配置项用来告诉Webpack要构建的代码中使用了哪些不用被打包的模块,也就是说这些模版是外部环境提供的,Webpack在打包时可以忽略它们。

防止将某些 import 的包(package)打包到bundle中,而是在运行时(runtime)再去从外部获取这些扩展依赖------【正确使用externals,vue工程构建性能提升67%

js 复制代码
const Components = require('../components.json');
const config = require('./config');

const webpackConfig = {
  mode: 'production',
  // 此处实现了多入口打包
  entry: Components,
  output: {
    path: path.resolve(process.cwd(), './lib'),
    publicPath: '/dist/',
    filename: '[name].js',
    chunkFilename: '[id].js',
    libraryTarget: 'commonjs2'
  },
  // 防止代码冗余,此处做性能优化
  externals: config.externals,
}

按需引入

webpackConfig.output 打包出来的内容如下图,可以按组件打包,方便按需引入:

根据官网的提示,按需引入需要安装插件babel-plugin-component

在页面中引入需要的组件:

js 复制代码
import { Button } from 'element-ui'

借助babel插件可以把上面的代码转换为以下的形式:

js 复制代码
// lib/button.js即按组件打包后的el-button组件
var button = require('element-ui/lib/button') 
require('element-ui/lib/theme-chalk/button.css')

externals 解决按需加载的代码冗余问题

注意:按需引入能有效减小项目体积,遇到将要引入的组件依赖于已引入的组件时会发生代码的冗余,此时用 externals 可以防止将这些 import 的包打包到 bundle 中,当运行时再从外部获取这些扩展依赖,但是对于 CSS 部分,element-ui 并未处理冗余情况。

以上问题该如何理解呢 ? 我们举个例子说明一下!

在 element-ui 中,Table 组件依赖了 CheckBox 组件,那么当我在项目中同时引入了 Table 组件和 CheckBox 组件的时候,会不会产生代码冗余呢?

js 复制代码
import { Table, CheckBox } from 'element-ui'

答案是会,在项目中最终引入的包会有 2 份 CheckBox 的代码,此时通过在 webpack 配置文件中配置的 externals 能有效解决部分冗余,不会打包生成 2 份 CheckBox JS 部分的代码。但是对于 CSS ,element-ui 并未处理冗余情况,可以看到 lib/theme-chalk/checkbox.csslib/theme-chalk/table.css 中都会有 CheckBox 组件的 CSS 样式。

npm run build : utils

js 复制代码
"build:utils": "cross-env BABEL_ENV=utils babel src --out-dir lib --ignore src/index.js",

使用 Babel 工具链中的 babel 命令,将 src 目录下的 javaScript 代码编译为 lib 下的 ES5代码,并忽略了 src / index.js 文件。

cross-env 是一个跨平台的环境变量设置工具。用于设置 BABEL_ENV 环境变量为 utils(.babelrc 文件中 env 选项的值将从 process.env.BABEL_ENV 获取,如果没有的话,则获取 process.env.NODE_ENV 的值,它也无法获取时会设置为 "development" )。

npm run build : umd

js 复制代码
"build:umd": "node build/bin/build-locale.js",
  1. src/locale/lang 里的文件被 require('babel-core').transformFile 转换后的文件放在 lib/umd/locale中。
  2. transform 函数是一个用于将指定文件转换为 UMD 模块格式的函数。具体来说,它使用了 Babel 工具链中的 transformFile 方法,并在编译过程中使用了 add-module-exportstransform-es2015-modules-umd 两个插件。其中,add-module-exports 插件用于将 ES6 模块转换为 CommonJS 模块,并添加 module.exports 属性,以便在 Node.js 等环境中使用。
  3. transform-es2015-modules-umd 插件用于将 ES6 模块转换为 UMD 模块,并添加适当的 UMD 包装器,以便在浏览器等环境中使用。利用file-save进一步处理,如将define('zh-CN'处理成define('element/locale/zh-CN',将global.zhCN = mod.exports处理成global.ELEMENT.lang = global.ELEMENT.lang || {};global.ELEMENT.lang.zhCN = mod.exports;

编译前关键代码 build/bin/build-locale.js :

编译后关键代码 lib/umd/locale/ar.js :

npm run build : theme

js 复制代码
"build:theme": "node build/bin/gen-cssfile && gulp build --gulpfile packages/theme-chalk/gulpfile.js && cp-cli packages/theme-chalk/lib lib/theme-chalk",  

node build/bin/gen-cssfile

检查 packages/theme-chalk/src 里是否有 components.json 对应的 scss 文件,没有的话,生成。并生成 packages/theme-chalk/src/index.scss 里面是所有组件的导入。

gulp build --gulpfile packages/theme-chalk/gulpfile.js

packages/theme-chalk/src 下的 scss 转换为 css 并压缩,将 font 压缩,后放到 packages/theme-chalk/lib 下面。

cp-cli packages/theme-chalk/lib lib/theme-chalk

packages/theme-chalk/lib 复制一份到 lib/theme-chalk 下面。

babel-plugin-component 插件的原理

分析到这里,element ui 的样式部分编译就结束了,我们提出一个小问题,如果不使用babel-plugin-component插件,我们如何将打包好的组件引入到项目中呢?上文提到的babel-plugin-component的作用是什么?

先回答第一个问题,如果不是用插件,我们用 import 'xxx' from './xxx/ccc.js'的方式将组件引入到项目中,连同打包好的 css 文件一并引入,示例如下:

js 复制代码
//index.vue
import button from "element-ui/lib/button.js"
import "element-ui/lib/theme-chalk/button.css"

一个组件需要引入两行代码,试问,如果该页面需要引入 5 个组件,那岂不是需要 10 行代码? 这显然很麻烦且臃肿,后期维护变更也显得很繁琐。为了解决这个问题,我们可以通过以下形式结合babel-plugin-component来进行优化

js 复制代码
import {button, alert, aside, card, checkbox-button} fron 'element-ui'

babel 能够将ES6、ES7等高级语法转换成ES5语法。而babel-plugin-component作为babel的一个插件,专门用于按需加载UI组件的JS和CSS,并且自动支持按需加载,使用起来非常方便,可以最大限度地减少页面加载时间并提高页面性能,babel-plugin-component也是按需加载的重要一环。

相关推荐
旧林84315 分钟前
第八章 利用CSS制作导航菜单
前端·css
yngsqq27 分钟前
c#使用高版本8.0步骤
java·前端·c#
Myli_ing1 小时前
考研倒计时-配色+1
前端·javascript·考研
余道各努力,千里自同风1 小时前
前端 vue 如何区分开发环境
前端·javascript·vue.js
软件小伟1 小时前
Vue3+element-plus 实现中英文切换(Vue-i18n组件的使用)
前端·javascript·vue.js
醉の虾2 小时前
Vue3 使用v-for 渲染列表数据后更新
前端·javascript·vue.js
张小小大智慧2 小时前
TypeScript 的发展与基本语法
前端·javascript·typescript
hummhumm2 小时前
第 22 章 - Go语言 测试与基准测试
java·大数据·开发语言·前端·python·golang·log4j
asleep7012 小时前
第8章利用CSS制作导航菜单
前端·css
hummhumm2 小时前
第 28 章 - Go语言 Web 开发入门
java·开发语言·前端·python·sql·golang·前端框架