目前按需加載有兩種方式實現。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
路徑。