按需加载就是需要什么,就只要什么,其他的东西不要。这样做的目的是为了缩小打包体积。
比如当前流行的web端组件库ElementUI,就有这个按需加载功能;一个系统的登录页,需要的组件是非常少的,大多数都是只用到Input输入框以及Button按钮。所以为了能够提高登录页面的加载速度,需要使用ElementUI的按需加载功能,只引入输入框以及按钮,这样用户在打开登录页的时候,就不需要等待太久,会有一个比较好的用户体验;
本文的原理参考ElementUI,只不过要做的东西比element要简单很多。element在使用按需引入的时候,需要开发者在自己的项目上使用插件babel-plugin-component,这个插件可以将代码中的引入代码进行转换
import { Button } from 'components'
这句代码会被转换成:
var button = require('components/lib/button')
require('components/lib/button/style.css')
这个components
就是依赖的名称,比如element-ui
,那它怎么知道引入的是lib目录下的button,而不是其他目录下的button,这个是需要配置.babelrc
这个配置文件,接下来以element-ui的按需引入配置进行示例说明。
配置.babelrc
文件
{
"presets": [["es2015", { "modules": false }]],
"plugins": [
[
"component",
{
"libraryName": "element-ui",
"styleLibraryName": "theme-chalk"
}
]
]
}
以下代码,会被转换:
import {Button,Icon} fron 'element-ui'
会被转换成以下代码
const Button = require('element-ui/lib/button.js`)
require('element-ui/theme-chalk/button.css')
const Icon = require('element-ui/lib/icon.js')
require('element-ui/theme-chalk/icon.css')
其中在引入css的时候,其中的theme-chalk
路径是通过.babelrc文件中的styleLibraryName
确定的,
引入js的时候,其中的lib
是通过.babelrc文件中的libDir
属性决定的,只不过这个属性的默认值是lib;
接下来看一下,在node_modules中的element-ui的结构,先看js
再看css:
看package.json中的main:
看完这个结构之后,大家应该明白了,当全引入的时候:
import ELEMENT from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
这两句代码,就相当于引入截图中的element-ui.common.js
以及theme-chalk/index.css
;当按需引入组件的时候,就是分别引入对应组件的js以及css文件,而babel-plugin-component
只是帮忙转换了一下语法而已,所以说,按需引入,是需要组件库打包出来的文件支持的,如果element-ui没有分别打包这些组件,那么调用者是无法实现按需引入功能的;
因为现在使用vue-cli3脚手架创建的vue工程是使用babel7编译的,babel-plugin-component
已经不再适用于babel7,文本将会使用babel-plugin-import实现按需引入的功能。其实babel-plugin-import
与babel-plugin-component
差异不大,就是做一下语法转换。
刚刚在分析element-ui的打包目录可以知道,当需要按需引入的时候,会分别引入对应组件的js以及css,所以不同的组件都需要打包,所以在webpack配置中,每个组件都需要有一个打包入口。同时还需要有一个总的打包入口,这个入口文件引入所有的组件并且注册
组件库中的组件是非常多的,如果每一个入口文件都是手动在webpack配置文件中维护的话,会非常麻烦,这里设定一个规则,所有的组件都放在某个目录下,这里为components
,打包的时候,自动扫描这个目录。自动生成入口配置信息。示例:
- components
- button
- pl-button.vue
- index.js
- ele
- index.js
- icon
- pl-icon.vue
- index.js
- input
- pl-input.vue
- index.js
里面的index.js就是每一个组件的入口文件,组件的入口文件暴露一个install方法,用来注册组件,比如button中的入口文件:
import Button from './pl-button'
Button.install = (Vue) => Vue.component(Button.name, Button)
export default Button
当需要按需引入button的时候,可以通过以下方式使用这个组件:
const Button = require('[button打包之后得到的js地址]')
Vue.use(Button)
扫描组件入口信息:
/*build/utils.js*/
const fs = require('fs')
const path = require('path')
const join = path.join
const resolve = (dir) => path.join(__dirname, '../', dir)
function getComponentEntries(path) {
let files = fs.readdirSync(resolve(path));
const componentEntries = files.reduce((ret, item) => {
const itemPath = join(path, item)
const isDir = fs.statSync(itemPath).isDirectory();
if (isDir) {
ret[item] = resolve(join(itemPath, 'index.js'))
} else {
const [name] = item.split('.')
ret[name] = resolve(`${itemPath}`)
}
return ret
}, {})
console.dir(componentEntries)
return componentEntries
}
getComponentEntries('components')
当不需要按需引入的时候,开发者引入的是总的打包文件,这个总的打包文件是通过总的打包入口文件打包而来的,根据上面的组件信息,总的打包入口文件:
/*src/index.js*/
import Button from 'components/button'
import Icon from 'components/icon'
import Ele from 'components/ele'
const PlainApp = {
install(Vue) {
Vue.use(Button)
Vue.use(Icon)
Vue.use(Ele)
}
}
export default PlainApp
同时,还需要在打包入口中增加总的打包入口文件地址:
entry:{
index: resolve('src/index.js'),
},
所以,最后的入口信息为:
entry: {
...getComponentEntries('components'),
index: resolve('src/index.js'),
},
为了测试我们自己创建的组件库能够支持按需引入,我们需要创建一个示例工程,用来测试按需引入是否成功,这里我们使用vuecli3脚手架创建的工程来测试,不过在测试我们自己创建的组件库之前,先测试element-ui的按需引入功能,看看有哪些特性
vue create test-load-on-demend
配置随意,我这里选手动选择特性,只要Babel
以及Css Pre-preocessor
Sass/SCSS
,剩下的随意。npm run serve
npm i element-ui -S
import Vue from 'vue'
import App from './App.vue'
import ELEMENT from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
Vue.use(ELEMENT)
Vue.config.productionTip = false
new Vue({
render: h => h(App),
}).$mount('#app')
/*src/App.Vue*/
hello world
Disabled cache
,在刷新一下页面。vue.config.js
,配置publicPath
为./
;/*vue.config.js*/
module.exports = {
publicPath: './'
}
这么做可以使得打包出来的文件,可以通过file协议打打开,查看效果。npm run build
在生成的dist目录中,直接打开index.htmlDisabled cache
,刷新:/*.babelrc*/
{
"presets": ["@vue/app", ["@babel/preset-env", { "modules": false }]],
"plugins": [
[
"component",
{
"libraryName": "element-ui",
"styleLibraryName": "theme-chalk"
}
]
]
}
babel-plugin-component
npm i babel-plugin-component -S
main.js
,调整为按需引入组件,只要Button以及Inputimport Vue from 'vue'
import App from './App.vue'
/*import ELEMENT from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
Vue.use(ELEMENT)*/
import {Button,Input} from 'element-ui'
Vue.use(Button)
Vue.use(Input)
Vue.config.productionTip = false
new Vue({
render: h => h(App),
}).$mount('#app')
npm run build
会稍微感觉到,打包要比上次快了很多,因为只打包了两个组件。打包完之后,打开index.html:好了,现在已经准备好了测试工程,准备开始实现我们自己的组件库工程,使得自己开发的组件库工程也有按需引入的功能,下面介绍两种方式创建组件库工程,实现按需引入功能
这里为什么要分为两种方式,一个是vuecli2创建的webpack工程,配置可以自主定义,较为灵活,因为webpack的配置文件都暴露出来了;另一个vuecli3创建的工程使用方便,简化了配置文件,同时很方便配置多单页面应用,以及其他打包配置。但是,两个工程实现按需引入功能的方式,却是有很大的差别。
文章地址
文章地址