組件庫之按需加載

組件庫之按需加載

方式

目前按需加載有兩種方式實現。css

  • 使用babel-plugin-import插件來自動按需引入
  • 提供es module版本,開啓tree shaking

babel-plugin-import

babel-plugin-important-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-importgithub

tree shaking

若是組件庫提供了es module版本,並開啓了tree shaking,那麼不須要babel-plugin-import,也能夠達到按需加載的目的,這個方法只針對於js, 對於樣式的按需加載仍須要手動引入。 固然babel-plugin-importtree shaking 也能夠並存使用。但大部分狀況並存使用與單獨使用體積差距不是很大。web

例如:json

import { Button } from 'antd';
import 'antd/es/button/style';
複製代碼

webpack能夠經過在package.json設置sideEffects: false,開啓tree shakingbootstrap

ssr應用使用哪種?

對於csrspa項目,使用以上兩種辦法達到的效果都是差很少的。可是若是咱們的項目是ssr應用,可能會有一點差別,由於ssr應用在server端,咱們是經過設置webpack.server.config.jsexternal,將第三方模塊不交給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環境, 目前nodeesm支持的並非很好,因此仍是廣泛使用cjs。那麼在server端,組件庫實際仍是會整個引入進來。

因此若是須要在server端作按需加載,仍是建議用babel-plugin-import

下面我會以bootstrap-vue舉例,由於bootstrap-vue使用babel-plugin-import並非很平滑。

bootstrap-vue

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-vueRetained Heap達到了 1.18MB,穩居第一位。 因此若是但願 server端的 bootstrap-vue也作到按需加載,可使用 babel-plugin-import

使用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這個目錄,就包含了carouselcarousel-slide兩個組件。

針對以上狀況,咱們須要自定義轉換路徑,利用babel-plugin-importcustomName,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端,則使用cjsbootstrap-vuees實際上是cjs,若是是client,則使用esm

總結

經常使用的按需加載方式

  • 使用babel-plugin-import插件
  • 提供es module版本,開啓tree shaking

babel-plugin-import插件能夠實現js,css的按需加載,本質上就是將按需引入的方式變動爲直接引入的方式。 若是配置了style字段,同時也會注入style的直接引入代碼。

tree shaking只針對於jscss若是須要按需加載,須要手動直接引入。

相比之下,因爲tree shaking只針對於jsbabel-plugin-import會更方便。babel-plugin-importtree shaking也能夠並存使用。並存使用在有些狀況,體積會相對小一點,但與單獨使用體積差距不大。

ssr應用中,因爲server端的webpack不會處理第三方模塊,因此沒辦法tree shaking,若是server端也須要考慮按需加載,可使用babel-plugin-import

若是所用的ui組件庫不符合babel-plugin-import的轉換規則,能夠經過babel-plugin-import提供的customName字段來自定義轉換後的路徑。經過style字段,來進一步自定義轉換後的style路徑。

相關文章
相關標籤/搜索