In the development of VUE project, we will abstract the frequently used logic or modules into components. For those components that are useful to multiple projects, we can consider encapsulating them into component libraries and publishing them to NPM. Each time to only need NPM install xx once, there is no need to copy back and forth. Let’s package a Vue component library starting at 0.

Common ways to use a VUE component library

  • 1, through thescriptTags introduced
<body> <div id="app"> <hello></hello> </div> </body> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <! <script SRC =".. /dist/my-lib.js"></script> <script> new Vue({ el: '#app' }) </script>Copy the code
  • 2, through theimportThe introduction of
import Vue from "vue"
import App from "./App.vue"
import MyLib from "my-lib"
Vue.use(MyLib)
new Vue({
    name: "root",
    el: '#root',
    render: h => h(App)
})
Copy the code

After this is introduced, use it directly in the project component that needs to use the component library, such as:

<template>
  <div id="app">
      <hello></hello>
  </div>
</template>
Copy the code

Components need to be registered before they are used. Registered components can be registered locally and globally. From the usage of component libraries above, we can see that the components of component libraries are registered globally.

In the first method of importing through the script tag, we complete the global registration inside the component library so that the imported component library can be used directly.

In the second import approach, we complete the global registration of components through vue.use (MyLib).

The project structure for the component library is as follows, with the install method defined in SRC /index.js.

// SRC /index.js import Hello from "./components/ hello.vue "function install(vue){// external vue.use (MyLib) will execute this method to complete the global registration of the component. Vue.component(hello.name, Hello)} if(window && window.vue) {// Complete the component registration within the component by introducing the 'script' tag. Vue.use(install) } export default installCopy the code

Webpack packaging

Webpack: WebPack: WebPack: WebPack: WebPack: WebPack: WebPack

Now we only have one component libraryHello componentThrough WebPack, we will package it into a component library that can be used in both ways.Webpack packages vUE component libraries in much the same way as a normal VUE project. justoutputDifferent, as shown below, increasedlibraryTargetAnd so on.

//webpack.config.js const path = require('path') const { VueLoaderPlugin } = require('vue-loader') module.exports = { mode: 'none', entry: './src/index.js', output: { path: path.join(__dirname,"/dist"), filename: LibraryExport: 'default'}, module: {rules: [ { test: /\.vue$/, use: ['vue-loader'] }, { test: /\.css$/, use: ['style-loader','css-loader','postcss-loader'] }, { test: /\.s[ac]ss$/i, use: ['style-loader','css-loader','postcss-loader','sass-loader'] }, { test: /\.js$/, loader: 'babel-loader', exclude: /node_modules/, } ] }, plugins: [ new VueLoaderPlugin() ] }Copy the code

libraryTarget

Var Assign this window Global JSONp CommonJS CommonJS2 AMD UMD var Assign this window Global JSONp CommonJS CommonJS2 AMD UMD For details, see official documents

We are using UMD here, which will run in CommonJS, AMD environment, or export modules to variables under Global. The basic structure of the exported library files under the UMD module specification is as follows, which we often see in some plug-ins or library files, such as jquery.

// The official example. 'MyLibrary' is the name of the library defined in the library, and corresponds to myLib in our own demo. (function webpackUniversalModuleDefinition(root, factory) { if(typeof exports === 'object' && typeof module === 'object') module.exports = factory(); else if(typeof define === 'function' && define.amd) define([], factory); else if(typeof exports === 'object') exports['MyLibrary'] = factory(); else root['MyLibrary'] = factory(); })(typeof self ! == 'undefined' ? self : this, function() { return _entry_return_; // This module returns the value returned by the entry chunk});Copy the code

With webpack.config.js written, we can configure the scripts in package.json:

"Scripts ": {"build": "webpack" // the default will execute webpack.config.js in the root directory},Copy the code

npm run build

A beggar version of the Vue component library is packaged.

externals

In our VUE component library, vue is passed in as a parameter. We didn’t import any other libraries either. When the functionality of this library is more complex, it is often inevitable to import other libraries, such as if you want to use Vue’s static method vue.xxx, or a tool library such as lodash.

It is not possible to package these external libraries into our own component library, because when the user introduces these external libraries and then introduces our component library, the external library will be imported and packaged twice, which is completely redundant. Therefore, when we develop a library, the external modules that the library depends on can be provided by the users who import the library.

This requires the use of externals, which prevents certain imported packages from being packaged into the bundle and instead retrieves these external dependency modules from the outside at run time. Add externals to webpack.config.js above:

const path = require('path') const { VueLoaderPlugin } = require('vue-loader') module.exports = { ... Externals: {vue: {root: "vue ", // the global variable can access vue commonjs: "Vue ", // can access CommonJs2 as a CommonJS module: "vue", // similar to above, but exports module.exports.default AMD: "Vue" is similar to commonJS, but uses AMD module system}}}Copy the code

The externals configuration can be string, array, object, function, and regex. Externals configuration

Only libraryTarget: ‘umd’ can be configured with {root, AMD, commonjs,… }, other libraryTarget values cannot be configured this way.

To test the externals configuration, add the following two lines to SRC /index.js:

import Vue from "vue"
console.log(Vue)
Copy the code

How do I debug component libraries locally

  • 1, first to debug throughscriptIn the case of tag import, create a new HTML document and import the packaged component library.
<! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, Word-wrap: break-word! Important; "> <title>Document</title> </head> <body> <div id="app"> <hello></hello> </div> </body> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script src=".. /dist/my-lib.js"></script> <script> new Vue({ el: '#app' }) </script> </html>Copy the code

  • 2,importIn the case of the introduction. Create a vue project called LibTest for testing, or, more conveniently, add a test component to the existing Vue project and add it to routes.

Modify the main field in the package.json of the myLib project, which defines the entry file for the NPM package.

"main": "./dist/my-lib.js",
Copy the code

Execute in the myLib component library project root directorynpm link

cd myLib
npm link
Copy the code

Execute in the test project root directorynpm link my-lib

CD LibTest NPM link my-lib // "name": "my-lib" : "name": "my-lib"Copy the code

You can then use the same component libraries as a normal NPM installation

In the js entry of the LibTest test project, introduce my-lib and execute vue.use () to register the component

import MyLib from 'my-lib'
Vue.use(MyLib)
Copy the code

Using components in LibTest’s app.vue:

<template>
  <div>
    <hello></hello>
  </div>
</template>
Copy the code

According to the need to load

Now that we’ve got the basic component library out of the way, let’s transform it into an on-demand component library.

Component libraries can be loaded on demand only when: Component libraries are exported in ES6 modular mode. In other words, the exported file should look like this:

import { xxx } from 'xxx'
import yyy from yyy
export default zzz;
export { a, b, c };
Copy the code

Use tree-shaking to remove unused code.

The principles of Tree-shaking, Wall cracking suggest careful reading of these two articles:

Tree-Shaking Performance Optimization Practice – Principles, your Tree-Shaking is not good

1. The modules introduced in ES6 are statically analyzed, so it is possible to determine exactly what code has been loaded at compile time. 2. Analyze the program flow, determine which variables are not used or referenced, and then delete the code.Copy the code

In the base packaging, we know that WebPack has multiple export modes (libraryTarget), but WebPack does not support an export mode for ES modules. The popular ‘UMd’ export mode, where the export file is also an instant-execute function, does not fit the ES6 modular approach at all. There is no way to load on demand with the above configuration.

There are many libraries that package each component or function into a separate file or directory for reference on demand. It is then referenced by the file path:

import 'echarts/lib/chart/pie'
import 'echarts/lib/component/title'
Copy the code

Writing this way doesn’t introduce more than one component at a time, nor is it elegant. The Elemental-UI has developed the Babel plugin, babel-plugin-Component, so we can introduce it on demand as follows:

import { Button, Select } from 'element-ui'
Vue.use(Button)
Vue.use(Select)
Copy the code

See how element-UI is built

Babel-plugin-component will:

import { Button } from 'element-ui'
Copy the code

Converted to:

var button = require('element-ui/lib/button')
require('element-ui/lib/theme-chalk/button.css')
Copy the code

In the path above, element-UI and Theme-Chalk are configurable, button is the component name, and lib is the folder where the babel-plugin-Component will find the component by default.

If we also want to implement more elegant on-demand references with the help of the Babel-plugin-Component plug-in, we pack each component separately and place it in the lib file of the component library.

Implement on-demand references

To create awebpack.component.js, as a configuration file packaged separately as a component. Write two more components, Hello and Test, which will be used for testing.Since components are packaged separately, each component exports a function that is executed at vue.use (xx) to complete the global registration of the component.

Hello/Hello.vueandHello/index.js

/ SRC /index.js import Hello from "./components/Hello" import Test from "./components/Test" function install(Vue){ Vue.use(Hello) Vue.use(Test) } if(window && window.Vue) { Vue.use(install) } export default installCopy the code

The implementation is simple. The core is multi-entry packaging. There is a multi-entry package

Let’s copy and paste the previous content from webpack.config.js into webpack.component.js.

  • Step 1: ModifyentryandoutputFields:
entry: {
  'hello': './src/components/Hello/index.js',
  'test': './src/components/Test/index.js',
  'my-lib': './src/index.js'
},
output: {
  path: path.join(__dirname,"/lib"),
  filename: '[name].js',
  libraryTarget: 'umd',
  library: '[name]',
  libraryExport: 'default'
},
Copy the code

Add package.json scripts: “component”: “webpack –config webpack.component.js”

npm run component

There is a lib folder in the myLib component directory.

  • Step two, from the topelement-uithebabel-plugin-componentUsing the plugin, we can see that the js and CSS files of the component library are separated. So we also separate the component CSS into a separate file, which we use heremini-css-extract-pluginThe plug-in.
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
Copy the code

Configure the NPM run Component again

Next, modify package.json"main": "./lib/my-lib.js",

Back to the test item. Create.babelrc in the project root directory (install the plug-in as prompted) and restart the project. Babel-plugin-component configuration reference

{
  "presets": [["es2015", { "modules": false }]],
  "plugins": [
    [
      "component",
      {
        "libraryName": "my-lib",
        "styleLibrary": {
          "name": "lib-style", // same with styleLibraryName
          "base": false  // if theme package has a base.css
        }
      }
    ]
  ]
}
Copy the code

Let’s put it to the rough test. Reference only the Hello component, using both Hello and test.

// LibTest/src/index.js import { Hello } from "my-lib" Vue.use(Hello) // LibTest/src/App.vue <template> <div class="cp">  <hello></hello> <test></test> </div> </template>Copy the code

Add a reference to the test component:

import { Hello,Test } from "my-lib"
Vue.use(Hello)
Vue.use(Test)
Copy the code

Let’s remove the reference to the Test component. Package the test project. Search the contents of the two components in the packaged file.

We have packaged the vUE component library to load on demand. In fact, developing component libraries can also be packaged using rollup.js, which supports ES Modules very well. Tree-shaking was also proposed by Rollup.js first, so it is much easier to load component libraries on demand. That’s it for today. The next article will show you how to package a library using Rollup. See you next time.