目前按需加载有两种方式实现。css
babel-plugin-import
插件来自动按需引入es module
版本,开启tree shaking
babel-plugin-import
是ant-design
团队出的一个babel插件,主要用于模块的按需加载。其原理就是将直接引入的方式经过babel
转化成按需引入的方式。若是css也须要按需加载,也会注入css引用代码。vue
例如:node
import { Button } from 'antd';
复制代码
转换成:webpack
import Button from 'antd/es/button';
import 'antd/es/button/style';
复制代码
babel-plugin-import
默认js
路径是 [libraryName]/[moduleType]/[componentName]
,默认样式
路径是 [libraryName]/[moduleType]/[componentName]/style
,若是所用的ui组件库不符合babel-plugin-import
的转换规则,能够经过babel-plugin-import
提供的customName
字段来自定义转换后的路径。经过style
字段,来进一步自定义转换后的样式
路径。git
具体使用文档能够参考babel-plugin-import
github
若是组件库提供了es module
版本,并开启了tree shaking
,那么不须要babel-plugin-import
,也能够达到按需加载的目的,这个方法只针对于js
, 对于样式
的按需加载仍须要手动引入。 固然babel-plugin-import
和tree shaking
也能够并存使用。但大部分状况并存使用与单独使用体积差距不是很大。web
例如:json
import { Button } from 'antd';
import 'antd/es/button/style';
复制代码
webpack
能够经过在package.json
设置sideEffects: false
,开启tree shaking
。bootstrap
对于csr
的spa
项目,使用以上两种办法达到的效果都是差很少的。可是若是咱们的项目是ssr
应用,可能会有一点差别,由于ssr
应用在server
端,咱们是经过设置webpack.server.config.js
的external
,将第三方模块不交给webpack
打包处理,直接require
引入便可,这样作的目的是不但愿server
端由webpack
打包的bundle
文件体积过大。bash
// webpack.server.config.js
module.exports = {
...
externals: nodeExternals({
whitelist: [/\.(css|scss|less)$/, /\?vue&type=style/] //除去样式
}),
}
复制代码
若是咱们使用tree shaking
方式, 因为server
端的webpack
不处理这些第三方模块,那么就没办法作tree shaking
,并且server
端是node
环境, 目前node
对esm
支持的并非很好,因此仍是广泛使用cjs
。那么在server
端,组件库实际仍是会整个引入进来。
因此若是须要在server
端作按需加载,仍是建议用babel-plugin-import
。
下面我会以bootstrap-vue
举例,由于bootstrap-vue
使用babel-plugin-import
并非很平滑。
bootstrap-vue提供了es module
版本,而且开启了tree shaking
,能够不须要babel-plugin-import
。直接引入便可。
//...引入样式
import { ButtonPlugin, LayoutPlugin, TabsPlugin } from 'bootstrap-vue'
Vue.use(ButtonPlugin)
Vue.use(LayoutPlugin)
Vue.use(TabsPlugin)
复制代码
但若是是在ssr
应用里,根据上面提到的缘由,server
端其实引入的是dist/bootstrap-vue.common.js
,咱们能够经过alinode
的堆快照
分析工具来查看下这个文件占了多大的内存。
bootstrap-vue
的
Retained Heap
达到了
1.18MB
,稳居第一位。 因此若是但愿
server
端的
bootstrap-vue
也作到按需加载,可使用
babel-plugin-import
。
bootstrap-vue
分红components
,directives
,两个组成部分,也就是组件和指令部分,并提供了两种引入方式,一个是批量注册组件或者指令的plugin
,另外一个是单独引入组件或者指令。 例如:
//批量注册组件
import { LayoutPlugin } from 'bootstrap-vue'
Vue.use(LayoutPlugin)
//批量注册指令
import { VBModalPlugin } from 'bootstrap-vue'
Vue.use(VBModalPlugin)
//单独注册组件
import { BModal } from 'bootstrap-vue'
Vue.compoents('b-modal', BModal)
//单独注册插件
import { VBModal } from 'bootstrap-vue'
Vue.directives('b-modal', VBModal)
复制代码
bootstrap-vue
的目录并不符合babel-plugin-import
的转换规则,由于bootstrap-vue
不只提供了两个主文件夹components
,directives
, 并且还有plugin组件,以及非plugin组件。另外,一个组件目录还包括了多个组件 如carousel
这个目录,就包含了carousel
和carousel-slide
两个组件。
针对以上状况,咱们须要自定义转换路径,利用babel-plugin-import
的customName
,style
字段。咱们能够利用它们来自定义转换函数,来实现下面的转换规则。bootstrap-vue
没有提供style
的按需加载,因此这里的按需加载,只针对于js
。
import { LayoutPlugin,VBModalPlugin,Carousel,CarouselSlide } from 'bootstrap-vue'
=>
//组件插件
import LayoutPlugin from 'bootstrap-vue/esm/components/layout/index.js'
//指令插件
import VBModalPlugin from 'bootstrap-vue/esm/directives/modal/index.js'
//单独引入
import Carousel from 'bootstrap-vue/esm/components/carousel/carousel.js'
//CarouselSlide包含在carousel文件夹里
import CarouselSlide from 'bootstrap-vue/esm/components/carousel/carousel-slide.js'
复制代码
由上咱们能够发现:
VB
开头的都是directive
, 以B
开头的都是component
。Plugin
结尾的都是plugin
,非Plugin
开头的都是单独注册的。因此咱们最终的.babelrc.js
应该这样写
//提供bootstrap的全部组件目录
const bootstrapComponents = [
'alert',
'badge',
'breadcrumb',
'button',
'button-group',
'button-toolbar',
'card',
'carousel',
'collapse',
'dropdown',
'embed',
'form',
'form-checkbox',
'form-file',
'form-group',
'form-input',
'form-radio',
'form-select',
'form-textarea',
'image',
'input-group',
'jumbotron',
'layout',
'link',
'list-group',
'media',
'modal',
'nav',
'navbar',
'pagination',
'pagination-nav',
'popover',
'progress',
'spinner',
'table',
'tabs',
'toast',
'tooltip'
]
module.exports = {
...
'plugins': [
[
'import',
{
'libraryName': 'bootstrap-vue',
camel2DashComponentName: false // 关闭驼峰转换
'customName': (name) => {
let category, cname = name, isPlugin = false
if (/^VB/.test(cname)) { //directives like VBModalPlugin, VBModal
category = 'directives'
cname = cname.replace(/^VB/, '')
} else { // components
category = 'components'
}
if (/Plugin$/.test(cname)) { //plugin like ButtonPlugin,ModalPlugin
isPlugin = true
cname = `${cname.replace(/Plugin$/, '')}`
} else { //Individual components like BButton, BModal
cname = cname.replace(/^B/, '')
}
//FormCheckbox -> form-checkbox
cname = cname.replace(/\B([A-Z])/, (m) => {
return `-${m}`
}).toLowerCase()
// 这里须要处理下当一个组件文件夹里包含多个组件的时候,如: carousel-slide -> /carousel/carousel-slide
if (!isPlugin && category === 'components') {
let dir = bootstrapComponents.filter(c => {
return cname.startsWith(c)
})[0]
return `bootstrap-vue/${process.env.VUE_ENV === 'server' ? 'es' : 'esm'}/${category}/${dir}/${cname}`
}
return `bootstrap-vue/${process.env.VUE_ENV === 'server' ? 'es' : 'esm'}/${category}/${cname}`
}
}
]
]
}
复制代码
process.env.VUE_ENV
是注入的一个环境变量,用于区分是server
端,仍是client
端。这里若是是server
端,则使用cjs
,bootstrap-vue
的es
实际上是cjs
,若是是client
,则使用esm
。
经常使用的按需加载方式
babel-plugin-import
插件es module
版本,开启tree shaking
babel-plugin-import
插件能够实现js
,css
的按需加载,本质上就是将按需引入
的方式变动为直接引入
的方式。 若是配置了style
字段,同时也会注入style
的直接引入代码。
tree shaking
只针对于js
,css
若是须要按需加载,须要手动直接引入。
相比之下,因为tree shaking
只针对于js
,babel-plugin-import
会更方便。babel-plugin-import
和tree shaking
也能够并存使用。并存使用在有些状况,体积会相对小一点,但与单独使用体积差距不大。
ssr
应用中,因为server
端的webpack
不会处理第三方模块,因此没办法tree shaking
,若是server
端也须要考虑按需加载,可使用babel-plugin-import
。
若是所用的ui组件库不符合babel-plugin-import
的转换规则,能够经过babel-plugin-import
提供的customName
字段来自定义转换后的路径。经过style
字段,来进一步自定义转换后的style
路径。