Component library loading on demand

way

Loading on demand is currently implemented in two ways.

  • usebabel-plugin-importPlug-ins to automatically import on demand
  • providees moduleVersion, ontree shaking

babel-plugin-import

Babel-plugin-import is a Babel plug-in developed by the Ant-Design team. It is mainly used for loading modules on demand. The idea is to convert direct imports through Babel into on-demand imports. If CSS also needs to be loaded on demand, CSS reference code is also injected.

Such as:

import { Button } from 'antd';
Copy the code

Converted to:

import Button from 'antd/es/button';
import 'antd/es/button/style';
Copy the code

Babel-plugin-import The default JS path is [libraryName]/[moduleType]/[componentName] and the default style path is [libraryName]/[moduleType]/[componentName]/style, if the UI component library used does not conform to babel-plugin-import conversion rules, You can customize the transformed path through the customName field provided with babel-plugin-import. Use the style field to further customize the transformed style path.

Refer to babel-plugin-import for detailed usage documentation

tree shaking

If the component library provides an ES Module version and tree shaking is enabled, loading on demand does not require babel-plugin-import. This method is only for JS, and the loading on demand for styles still needs to be introduced manually. Babel-plugin-import and tree shaking can also be used together. But in most cases, the volume gap between coexisting use and single use is not very large.

Such as:

import { Button } from 'antd';
import 'antd/es/button/style';
Copy the code

Webpack can turn tree shaking on by setting sideEffects: false in package.json.

Which SSR application is used?

For THE SPA project of CSR, the effects achieved by using the above two methods are almost the same. However, if our project is SSR application, there may be a little difference, because SSR application is on the server side, we set the external of webpack.server.config.js, and introduce the third-party module directly without webpack processing. The purpose of this is not to make the bundles packaged by WebPack too large on the server side.

// webpack.server.config.js
module.exports = {
    ...
    externals: nodeExternals({
    whitelist: [/\.(css|scss|less)$/, / /? vue&type=style/]// Remove styles}}),Copy the code

If we use Tree shaking, there is no way to do tree shaking because webpack on the server side doesn’t handle these third-party modules, and the Server side is a Node environment. Node doesn’t support ESM very well at the moment, so CJS is still widely used. On the server side, the component library is actually introduced in its entirety.

Therefore, babel-plugin-import is recommended for on-demand loading on the server side.

I’ll use bootstrap-vue as an example, because bootstrap-vue using babel-plugin-import is not very smooth.

bootstrap-vue

Bootstrap-vue provides the ES Module version, and tree shaking is enabled. You don’t need babel-plugin-import. I can just introduce it.

/ /... The introduction of the style
import { ButtonPlugin, LayoutPlugin, TabsPlugin } from 'bootstrap-vue'
Vue.use(ButtonPlugin)
Vue.use(LayoutPlugin)
Vue.use(TabsPlugin)
Copy the code

In an SSR application, however, the server actually introduces dist/ bootstrap-vue.com.js for the reasons mentioned above. Alinode’s heap snapshot analysis tool can be used to see how much memory this file takes up.

bootstrap-vue
Retained Heap
1.18 MB
server
bootstrap-vue
babel-plugin-import

Use the Babel – plugin – import

Bootstrap-vue is divided into components and directives, that is, components and directives, and provides both bulk registration of components or directives as plugins and separate import of components or directives. Such as:

Import {LayoutPlugin} from import {LayoutPlugin} from'bootstrap-vue'Vue.use(LayoutPlugin) import {VBModalPlugin} from'bootstrap-vue'Vue.use(VBModalPlugin) // Register component import {BModal} from separately'bootstrap-vue'
Vue.compoents('b-modal', BModal) // Register plug-in import {VBModal} from separately'bootstrap-vue'
Vue.directives('b-modal', VBModal)
Copy the code

The directory of bootstrap-vue does not comply with the conversion rules of babel-plugin-import because bootstrap-Vue provides not only two main folders components,directives, but also plugin and non-plugin components. Directives In addition, a component directory contains multiple components, such as Carousel, which contains carousel and Carousel-Slide.

In this case, we need to customize the transformation path using the customName,style field of babel-plugin-import. We can use them to customize conversion functions to implement the following conversion rules. Bootstrap-vue does not provide style loading on demand, so loading on demand here applies only to JS.

import { LayoutPlugin,VBModalPlugin,Carousel,CarouselSlide } from 'bootstrap-vue'=> // Component plug-in import LayoutPlugin from'bootstrap-vue/esm/components/layout/index.js'// Import VBModalPlugin from'bootstrap-vue/esm/directives/modal/index.js'// Import Carousel from separately'bootstrap-vue/esm/components/carousel/carousel.js'CarouselSlide is included in the carousel folder import CarouselSlide from'bootstrap-vue/esm/components/carousel/carousel-slide.js'
Copy the code

From the above we can find:

  • In order toVBThey all start withdirectiveIn order toBThey all start withcomponent.
  • In order toPluginAll of them at the endplugin,PluginThe first ones are individually registered.
  • If it is a separately imported component, you need to get its component directory.

So our final babelrc.js should look something like this

// Provide the bootstrap component directory
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 // Turn off the hump conversion
        '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}`}}]]}Copy the code

VUE_ENV is an environment variable injected to distinguish between the server side and the client side. The es of bootstrap-vue is actually CJS. The ESM of bootstrap-vue is esM.

conclusion

Common load on demand mode

  • usebabel-plugin-importThe plug-in
  • providees moduleVersion, ontree shaking

The babel-plugin-import plugin enables on-demand loading of JS and CSS, essentially changing the on-demand mode to the direct mode. If the style field is configured, the style direct import code is also injected.

Tree shaking is only for JS, CSS needs to be imported manually if it needs to be loaded on demand.

In contrast, since tree shaking is only for JS, babel-plugin-import is more convenient. Babel-plugin-import and tree shaking can also be used together. Coexisting use in some cases, the volume will be relatively a bit smaller, but with alone use volume gap is not big.

In SSR applications, since webpack on the server side does not deal with third-party modules, it is not possible to tree shaking. If the server side also needs to consider loading on demand, you can use babel-plugin-import.

If the UI component library used does not conform to the transformation rules of babel-plugin-import, you can customize the transformed path using the customName field provided by babel-plugin-import. Use the style field to further customize the converted style path.