全部引入和按需加载
# 前言
组件库往往有很多组件,但是开发者可能项目中只用到了少数的几个组件,不可能因为使用到了一两个组件,就需要全部引入组件库的所有组件,开销和收益严重不平衡,对于开发者来说是不可接受的,于是就将组件库的加载方式分为 全部加载
和 按需引入
。
# 全局加载
前面文章提到,MSSUI
像下面这样全局加载:
import Vue from 'vue'
import App from './App.vue'
import MssUI from 'mssui'
import 'mssui/lib/theme-chalk/index.css'
Vue.use(MssUI)
Vue.config.productionTip = false
new Vue({
render: h => h(App),
})
2
3
4
5
6
7
8
9
10
11
所以这段代码到底做了什么事情呢?最主要看这三句代码:
import MssUI from 'mssui'
import 'mssui/lib/theme-chalk/index.css'
Vue.use(MssUI)
2
3
4
很明显,这是引入的是npm 安装的 mssui
,先看第一行代码,当你import MssUI from 'mssui'
的时候,其实是引入 mssui
根目录下面 package.json
中的 main
字段指定的文件:
"main": "lib/ml-element-ui.common.js",
可以看到,当代码执行 import MssUI from 'mssui'
的时候其实是 import MssUI from 'lib/ml-element-ui.common.js'
。该文件是一个 CommonJS (opens new window) 规范的包,因为 npm 是使用 CommomJS
规范的。
再看看 import 'mssui/lib/theme-chalk/index.css'
,这里直接引入了全部组件的样式文件,而 Vue.use(MssUI)
方法全局安装了 MssUI
。
要想全部加载所有组件,其实我们可以总结出如下的关键点:
- 所有的组件需要打包到一个满足
CommomJS
的文件中,且这个文件得提供一个install
方法,方法中需要将所有的组件全部注册。 - 所有组件的样式得打包到一个文件中,方便全局引入
这不就是现在的打包工具干的事情吗?
我们这里用的是 webpack (opens new window)。将 src/packages/index.js
文件作为完整打包的入口,该文件中提供 install
方法注册全局组件, install
文件如下:
import components from "@/components";
const MElement = {
install: function (Vue, opts = {}) {
components.forEach((component) => {
Vue.component(component.name, component);
});
},
// 每个组件单独导出
...components,
};
2
3
4
5
6
7
8
9
10
11
然后在 build
目录下,新建 webpack.common.js
,配置如下:
const path = require("path");
const ProgressBarPlugin = require("progress-bar-webpack-plugin");
const VueLoaderPlugin = require("vue-loader/lib/plugin");
module.exports = {
mode: "production",
entry: {
app: [path.resolve(process.cwd(), "./src/packages/index.js")],
},
output: {
path: path.resolve(process.cwd(), "./lib"),
filename: "ml-element-ui.common.js",
publicPath: "/dist/",
chunkFilename: "[id].js",
libraryExport: "default",
library: "MLELEMENT",
libraryTarget: "commonjs2",
},
resolve: {
extensions: [".js", ".vue", ".json"],
alias: {
"@": path.resolve(process.cwd(), "./src"), // 这样配置后 @ 可以指向 src 目录
},
},
module: {
rules: [
{
test: /\.js$/,
include: process.cwd(),
loader: "babel-loader",
},
{
test: /\.vue$/,
loader: "vue-loader",
},
{
test: /\.css$/,
use: ["style-loader", "css-loader"],
},
{
test: /\.(svg|otf|ttf|woff2?|eot|gif|png|jpe?g)(\?\S*)?$/,
loader: "url-loader",
},
],
},
plugins: [new ProgressBarPlugin(), new VueLoaderPlugin()],
};
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
大多数配置,几乎都比较常见,只有 output
比较不同,我们单独解释下:
module.exports = {
// ...
output: {
path: path.resolve(process.cwd(), "./lib"),
filename: "ml-element-ui.common.js",
publicPath: "/dist/",
chunkFilename: "[id].js",
libraryExport: "default",
library: "MLELEMENT",
libraryTarget: "commonjs2",
}
}
2
3
4
5
6
7
8
9
10
11
12
相关配置解释如下:
- path 打包后的文件输出路径
- filename 打包后输出的文件名称
- publicPath 静态资源访问时的路径前缀
- chunkFilename 每个chunk文件的名称
- libraryExport 打包后需要导出的模块内容 这里将整个 default 对象导出 也就是完整组件的代码
- library 打包的库的名称为
MLELEMENT
- libraryTarget 打包成
commomjs2
规范 的包
# 按需加载
按需加载其实有很多方式,你单独引入某个组件和组件的样式也可以实现按需加载,比如你可以这样使用 MSSUI
:
import Vue from 'vue'
import App from './App.vue'
import Button from 'mssui/lib/packages/button'
import 'mssui/lib/theme-chalk/button.css'
Vue.use(Button)
Vue.config.productionTip = false
new Vue({
render: h => h(App),
})
2
3
4
5
6
7
8
9
10
11
但是每个这样加载很麻烦,而且不优雅,我们希望是这样的用法:
import Vue from 'vue'
import App from './App.vue'
import { Button } from 'mssui'
Vue.use(Button)
Vue.config.productionTip = false
new Vue({
render: h => h(App),
})
2
3
4
5
6
7
8
9
10
只需要 import { xxx } from 'mssui'
的方式,就可以只引入这个组件的代码。于是有人写了 babel-plugin-component
这个 babel
的插件用于按需加载,比如:
import { Button } from 'mssui'
Vue.use(Button)
2
babel-plugin-component
会上上述的代码转换为:
var Button = require('mssui/lib/button')
require('mssui/lib/theme-chalk/button.css')
2
具体原理我们就不去分析了,主要讲实现 按需引入
的过程,为了实现引入单个文件的代码,我们需要将每个组件的组件代码和样式代码都单独打包。也是使用 webpack
,在build
目录下新建 webpack-component.js
文件,配置如下:
const path = require("path");
const ProgressBarPlugin = require("progress-bar-webpack-plugin");
const VueLoaderPlugin = require("vue-loader/lib/plugin");
const Components = require("../src/components.json");
const webpackConfig = {
mode: "production",
entry: Components,
output: {
path: path.resolve(process.cwd(), "./lib"),
filename: "[name].js",
chunkFilename: "[id].js",
libraryTarget: "commonjs2",
},
resolve: {
extensions: [".js", ".vue", ".json"],
alias: {
"@": path.resolve(process.cwd(), "./src"), // 这样配置后 @ 可以指向 src 目录
},
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
include: process.cwd(),
loader: "babel-loader",
},
{
test: /\.vue$/,
loader: "vue-loader",
},
{
test: /\.css$/,
use: ["style-loader", "css-loader"],
},
{
test: /\.(svg|otf|ttf|woff2?|eot|gif|png|jpe?g)(\?\S*)?$/,
loader: "url-loader",
},
],
},
plugins: [new ProgressBarPlugin(), new VueLoaderPlugin()],
};
module.exports = webpackConfig;
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
大多数的配置我们都很熟悉,注意代码块中高亮的部分,组件单独打包的秘诀就在于这里: 配置多入口打包。
components.json
是保存了所有的组件代码的路径:
{
// ...
"button": "./src/packages/alert/index.js",
}
2
3
4
最后配置 webpack
的入口文件为 entry: Components
,就可以实现每个组件单独打包成单独的文件。
前面说的都是 如何打包完整组件和单独打包每个组件,但是组件样式还没介绍如何打包,下面就介绍下如何完整打包组件样式和单独打包那个组件的样式,这里使用 glup (opens new window), 这也是和 webpack
齐名的打包构建工具,比较适合流程式的工作,所以这里用来打包样式文件。
在根目录下新建 glupfile.js
, 配置如下:
const { series, src, dest } = require("gulp");
const sass = require("gulp-sass");
const autoprefixer = require("gulp-autoprefixer");
const cssmin = require("gulp-cssmin");
function compile() {
return src("./src/theme/*.scss")
.pipe(sass.sync())
.pipe(
autoprefixer({
browsers: ["ie > 9", "last 2 versions"],
cascade: false,
})
)
.pipe(cssmin())
.pipe(dest("./lib/theme-chalk"));
}
function copyfont() {
return src("./src/theme/fonts/**")
.pipe(cssmin())
.pipe(dest("./lib/theme-chalk/fonts"));
}
exports.build = series(compile, copyfont);
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
glup
讲究管道式打包,上述的配置含义为:将 src/theme
目录先的所有 .scss
文件使用 gulp-autoprefixer
插件自动加上浏览器兼容前缀,然后使用 gulp-cssmin
转换成css并压缩,最后将打包后的文件输出到 lib/theme-chalk
目录下。
series
函数用于同步执行 glup
任务,exports.build = series(compile, copyfont)
表示注册了两个任务:compile
和 copyfont
, copycont
这里是将字体文件复制到 lib/theme-chalk/fonts
下。
至此,全部加载和按需加载文件的打包也就完成了。