Reasoning UI component libraries on demand principle

background

Since I also wrote an in-house component library, I wanted to implement the functionality that was introduced on demand. Reduce the file size. Improve project performance.

This article serves as a collation, shares the thinking process and the train of thought to everybody

Start by looking for implementation cases

Let’s start with the on-demand introduction of Element-UI

Check the website

  • Rely on a Babel plug-in, babel-plugin-Component
  • The plugin is not used in the beta test, and invalidates as needed (see file size after run build).

conclusion

  • Don’t see anything special about it. Maybe the core has something to do with the babel-plugin-Component plug-in

Look at lodash’s on-demand introduction

// Import on demand
import cloneDeep from 'lodash/cloneDeep'; // Check the path 'lodash/cloneDeep'

var objects = [{ 'a': 1 }, { 'b': 2 }];
var deep = cloneDeep(objects);
console.log(deep[0] === objects[0]); // false



// Not on demand (all imports)
import _ from 'lodash';

var objects = [{ 'a': 1 }, { 'b': 2 }];
var deep = _.cloneDeep(objects);
console.log(deep[0] === objects[0]); // false
Copy the code

Package in 2 times, once as needed, once all. The final webpack result :(it does work)

If you look at the loDash source code, the directory structure is as follows:

Lodash ├ ─ ─... ├ ─ ─ cloneDeep. Js ├ ─ ─...Copy the code

Reasonable guess: The principle of import on demand is related to import by file path

  • On a further guess, the loDash Module export should be ES6 Module (check the source code, it is)
    • Why must it be ES6 Module? Because only ES6 Modules can do tree shaking, because ES6 Modules are static, dependencies can be analyzed at compile time. (See my other article for details at juejin.cn/post/695936…

Verify the conjecture: Import on demand works by file path

Try element-UI, I won’t introduce his Babel plugin (babel-plugin-component)

I’m going to write it as a file path import

// Import on demand (import file path)
import Button from "element-ui/lib/button" // open node_modules to find the directory structure
// Import Button from 'elder-ui /packages/ Button '; The difference is, the ones on top are packed. This is an unpackaged file with a better source map for debugging
import 'element-ui/lib/theme-chalk/button.css' // Style files can also be imported on demand
Vue.component(Button.name, Button);


// global import
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);
Copy the code

Package in 2 times, once as needed, once all. After packing, contrast: It did work! Js and CSS have been greatly reduced in size

  • Js: Reduced from 809KB to 101KB
  • CSS: reduced from 236kb to 11kb

One last guess: the babel-plugin-component plugin for Element-UI

Element UI on demand:

// main.js
import { Button } from 'element-ui'; 
Vue.component(Button.name, Button);

// Install NPM install babel-plugin-component -d
//. Babelrc:
{
  "presets": [["es2015", { "modules": false }]], // There is a pit if you are using Babel 7 or higher. The written to the [[" @ Babel/preset - env, "{}" modules ": false]].
  "plugins": [["component",
      {
        "libraryName": "element-ui"."styleLibraryName": "theme-chalk"}}]]Copy the code

Import {Button} from ‘element-ui’; import {Button} from ‘element-ui’; Import Button from ‘element-ui/lib/ Button ‘;

Verify conjecture: Write a loader and place it before babel-loader

{
  test: /.js$/,
  loader: './my-loader'.// Create a new loader and place it in front of the Babel loader to get the result of the Babel parse.
},
{
  test: /.js$/,
  loader: 'babel-loader'.include: /src/,
  options: {
    cacheDirectory: true}},Copy the code

My-loader. js (in the same directory as webpack.config.js)

module.exports = function (source) {
  console.log(source)
  debugger
  return source
}
Copy the code

The converted babel-plugin-component js code looks like this:

/ / before the conversion
import Vue from 'vue';
import App from './App.vue';

import { Button } from 'element-ui';
Vue.component(Button.name, Button);

new Vue({
  el: '#app'.render: h= > h(App)
});


// babel-plugin-component after conversion
import Vue from 'vue';
import App from './App.vue';

import _Button2 from "element-ui/lib/theme-chalk/button.css"; // This is consistent with the conjecture
import "element-ui/lib/theme-chalk/base.css"; // Add a base configuration, check the source code, is some icon class and animation configuration. Personally, I don't think we can introduce it based on demand
import _Button from "element-ui/lib/button"; // This is consistent with the conjecture

Vue.component(_Button.name, _Button);

new Vue({
  el: '#app'.render: function render(h) {
    returnh(App); }});Copy the code

The guess is right!

Conclusion:

The principle of on-demand import is that the import is based on the path of resources, provided that the ES6 Module is used

Are the conclusions applicable to all component libraries? Does it apply to other third party resources?

A reasonable guess is that this should work as long as you use the ES6 Module and strip out all the sub-modules

  • Such as UI component libraries (EL-UI, iView), function tools libraries (LoDash), are applicable

Code word is not easy, praise encouragement