In Vue CLI3 build component library and NPM release actual combat operation introduced a single component component library how to develop, this article will step by step to teach you how to develop multiple components integrated component library, how to realize the introduction of component library as needed. Give it a thumbs up if you think it’s useful.

This article will skip the installation of Vue CLI3, build component library project, clean component library project steps, do not understand the place can see Vue CLI3 build component library and NPM release combat operation

Create two Git repositories for this article’s sample code

  • Component library
  • Reference component library Demo

2. Some basic configuration of Webpack in component library project

In Vue CLI3, the webpack configuration of the project is to be configured by creating vue.config.js in the root directory.

1. Distinguish between the configuration of development and build environments

Because the configuration of the development and build environments in the multi-entry component library is different, it is important to distinguish between them. According to the process.env.NODE_ENV variable, process.env.NODE_ENV is development in the production environment.

Const devConfig = {//... } const buildConfig = { //... } module.exports = process.env.NODE_ENV === 'development' ? devConfig : buildConfig;Copy the code

2. Change the name of the SRC folder

In the Vue component library project, the original SRC folder is the content of the demo, so the file name is changed to examples, more vivid.

3. Reconfigure the project entry file

The configuration content in the vue.config.js file is as follows:

const devConfig={
    pages: {
        index: {
            entry: 'examples/main.js',
            template: 'public/index.html',
            filename: 'index.html',
        },
    },
}
Copy the code

4. Configure file alias

The file alias will be used in writing the demo. The configuration content in the vue.config.js file is as follows:

const path = require('path');
function resolve(dir) {
    return path.resolve(__dirname, dir)
}
const devConfig = {
    configureWebpack: {
        resolve: {
            extensions: ['.js', '.vue', '.json'],
            alias: {
                '@': resolve('packages'),
                'assets': resolve('examples/assets'),
                'views': resolve('examples/views'),
            }
        }, 
    },
}
Copy the code

5. Configure devServer

The configuration content in the vue.config.js file is as follows:

Const devConfig = {devServer:{port: 8091,// fixed port hot: true,// open: 'Google Chrome'// fixed browser}}Copy the code

6. Create the Packages folder under the root directory

The code for the component is developed in the Packages folder

7. Add the new Packages folder to Babel transcoding

The configuration content in the vue.config.js file is as follows:

const devConfig = {
    chainWebpack: config => {
        config.module
            .rule('js')
            .include
            .add('/packages')
            .end()
    },
}
Copy the code

This is the configuration for the development environment. Let’s write the configuration for the production environment

8. Add the new Packages folder to Babel transcoding as well in production

const buildConfig = {
    chainWebpack: config => {
        config.module
            .rule('js')
            .include
            .add('/packages')
            .end()
    },
}
Copy the code

9. Configure the directory for the production environment build files

We packed and compiled the component library and put it in the lib folder. The configuration content in the vue.config.js file is as follows:

const buildConfig = {
    outputDir: 'lib',
}
Copy the code

10. Close source Map

Closing the Source Map has two benefits

  1. Reduce packaging compilation time;
  2. Avoid seeing source code in Sources with the F12 developer tools in production.

The configuration content in the vue.config.js file is as follows:

const buildConfig = {
    productionSourceMap: false,
}
Copy the code

Three, multi-entry file page packaging configuration

In the project built by Vue CLI3 with the help of babel-plugin-import webpack plug-in and configure babel.config.js, to realize the component library on demand import premise is the component library is multi-entry file page package.

1. Configure Entry

In Vue CLI3, the project multiple entry is configured on the Entry property of the configureWebpack option. In this case, the configuration is as follows

const buildConfig = {
    configureWebpack: {
        entry: { 
            index: 'D:\\project\\02npm\\02Vue_Cli3_MAPLib\\packages\\index.js',
            testA: 'D:\\project\\02npm\\02Vue_Cli3_MAPLib\\packages\\testA\\index.js',
            testB: 'D:\\project\\02npm\\02Vue_Cli3_MAPLib\\packages\\testB\\index.js' 
        },
    },
}
Copy the code

But each entry is written dead, so we use NodeJS to automate the configuration.

First we introduce the path module in NodeJS to handle file paths.

const path = require('path'); const join = path.join; // Splice pathsCopy the code

Write a method that converts the destination path to an absolute path based on the current file path

function resolve(dir) {
    return path.resolve(__dirname, dir)
}
Copy the code

__dirname is the complete absolute path of the current file directory, for example

The fs module in NodeJS is also introduced to handle file information

const fs = require('fs');
Copy the code

We create a function getEntries(PATH), where PATH is the name of the folder where the component code is located, returns an object entry with key being the name of each component folder and the absolute path to the entry file index.js in each component folder.

First use fs.readdirsync (resolve(path)) to get the names of all the files in the component code’s folder, stored in the files variable.

Then use the array reduce() method to loop files, first convert each filename (item) into a path by join(path, item) and store it in the itemPath variable.

Determine if each file is a folder with fs.statsync (itemPath).isdirectory ().

If it is a folder, concatenate itemPath and the entry file index.js into an address, then convert it to an absolute path, and assign item as the key to the returned object

Entries [item] = resolve(join(itemPath, 'index.js')).Copy the code

If it is not a folder, convert itemPath directly to an absolute path, remove the item suffix as key, and assign the value to the returned object

const [name] = item.split('.')
entries[name] = resolve(`${itemPath}`)
Copy the code

Here is the complete code

function getEntries(path) {
    let files = fs.readdirSync(resolve(path));
    const entries = files.reduce((ret, item) => {
        const itemPath = join(path, item)
        const isDir = fs.statSync(itemPath).isDirectory();
        if (isDir) {
            ret[item] = resolve(join(itemPath, 'index.js'))
        } else {
            const [name] = item.split('.')
            ret[name] = resolve(`${itemPath}`)
        }
        return ret
    }, {})
    return entries
}
Copy the code

For example, in this case, executing getEntries(‘ Packages ‘) will result in an object

{ 
    index: 'D:\\project\\02npm\\02Vue_Cli3_MAPLib\\packages\\index.js',
    testA: 'D:\\project\\02npm\\02Vue_Cli3_MAPLib\\packages\\testA\\index.js',
    testB: 'D:\\project\\02npm\\02Vue_Cli3_MAPLib\\packages\\testB\\index.js' 
},
Copy the code

Configure Entry using the object expansion operator

const buildConfig = { configureWebpack: { entry: { ... getEntries('packages'), }, }, }Copy the code

2. Configure output

  • Filename: generates the corresponding filename after each component is packaged[name].index.jsWhy this name is configured will be explained later.
  • LibraryTarget: Set tocommonjs2The key is that the return value of the entry file will be assigned to the Module.exports object to make its component library available in a WebPack built environment.
const buildConfig = {
    configureWebpack: {
        output: {
            filename: '[name]/index.js',
            libraryTarget: 'commonjs2',
        }
    },
}
Copy the code

3. Style packing configuration

Configure the style packing path and filename on css.extract. Filename

Const buildConfig = {CSS: {sourceMap: true, extract: {filename: 'style/[name].css'// Create style folder in lib, generate corresponding CSS file. }}},Copy the code

4. Delete some useless functions of Vue CLI3 originally packaged and compiled

  • Remove splitChunks because each component is packaged separately and there is no need to pull out the common JS for each component.
  • Delete copy. Do not copy the contents of the public folder into the lib folder.
  • Remove HTML, package only components, do not generate HTML pages.
  • Remove preload and prefetch, which are useless because no HTML pages are generated.
  • Delete HMR, delete hot update.
  • Delete automatically added entry App.
const buildConfig = {
    chainWebpack: config => {
        config.optimization.delete('splitChunks')
        config.plugins.delete('copy')
        config.plugins.delete('html')
        config.plugins.delete('preload')
        config.plugins.delete('prefetch')
        config.plugins.delete('hmr')
        config.entryPoints.delete('app')
    },
}
Copy the code

5. Configure the loader for fonts

const buildConfig = {
    chainWebpack: config => {
        config.module
            .rule('fonts')
            .use('url-loader')
            .tap(option => {
                option.fallback.options.name = 'static/fonts/[name].[hash:8].[ext]'
                return option
            })
    },
}
Copy the code

4. Component library development

In this case, testA and testB are simply written for testing

1, Packages directory structure

Mainly about the component entry file how to write, other development as usual.

2. Single component entry

In the case of component testA, expose an object called test and provide the install method. This component can be called via vue.use ().

import test from './src/index.vue';
test.install = function(Vue) {
    Vue.component(test.name, test);
};
export default test;
Copy the code

3. General entry to the component library

import testA from './testA'
import testB from './testB'
export default {
    install(Vue) {
        Vue.use(testA);
        Vue.use(testB)
    },
}
Copy the code

Compile and package

Execute NPM run build, package and compile, and you’ll get a lib folder in your project with the contents shown in the figure below.

Vi. Preparation before NPM release

1. Configure the main import file

In package.json, write:

"main": "lib/index/index.js",
Copy the code

2. Other configurations

You can refer to Vue CLI3 to build a component library and release actual combat operation with NPM

7. Introduce configuration on demand

1. Introduce components

After the release (if not released please see Vue CLI3 build component library and NPM release actual operation), in reference to the component library demo, execute NPM install [email protected] –save install component library.

Install the babel-plugin-import plug-in after NPM install babel-plugin-import –save-dev and use it to import components on demand.

In the. Babelrc. js file in the root directory, do as follows

module.exports = { "presets": ["@vue/app"], "plugins": [ [ "import", { "libraryName": "The map - the lib - test", / / the name of the component library "camel2DashComponentName" : false, / / close the hump automatic chain "camel2UnderlineComponentName" : False // Disable serpentine autochain}],]}Copy the code

In the main. Js to write

import { testA, testB } from 'map-lib-test';
Vue.use(testA);
Vue.use(testB);
Copy the code

In the SRC/views/demo. Vue references

<template>
    <div>
        <testA></testA>
        <testB></testB>
    </div>
</template>
<script>
export default {
    data() {
        return {
        }
    },
}
</script>
Copy the code

Browser display

2. Introduce styles

In the. Babelrc. js file in the root directory, do as follows

module.exports = { "presets": ["@vue/app"], "plugins": [ [ "import", { "libraryName": "The map - the lib - test", / / the name of the component library "camel2DashComponentName" : false, / / close the hump automatic chain "camel2UnderlineComponentName" : False // Turn off serpentine autochain "style": (name) =>{ const cssName = name.split('/')[2]; return `map-lib-test/lib/style/${cssName}.css` } } ], ] }Copy the code

styleWhen the value of babel-plugin-import is a function, babel-plugin-import will automatically import files whose file path is equal to the value returned by the function. The following figure shows the path of the CSS file after the component library is packaged. This is as configured in the code above. Among themstyleIs a function, an example of the value of its parameter name ismap-lib-test/lib/testA, you can print it out when you use it.

Browser display

Eight, pay attention

1. Whyimport{testA,testB}Rather thanimport{testC,testD}

If it is' import {testC, testD} ', the following error occursCopy the code

import { testA, testB } from 'map-lib-test';
Copy the code

Quite a

import testA from  "map-lib-test/lib/testA";
import testB from  "map-lib-test/lib/testB";
Copy the code
import { testC, testD } from 'map-lib-test';
Copy the code

Quite a

import testC from  "map-lib-test/lib/testC";
import testB from  "map-lib-test/lib/testD";
Copy the code

Map-lib-test /lib does not contain testC or testD.

Import {testA, testB} : import{testA, testB} : import{testA, testB} : import{testA, testB} : import{testA, testB} : import{testA, testB} Each component is packaged independently and the generated folder name is the original folder name of each component code.

Import {testA, testB} because testA and testB are packaged into folders named testA and testB, respectively.

Js’ instead of ‘[name]/main.js’ or ‘[name]/out.js’

Import testA from “map-lib-test/lib/testA”; Import testA from “map-lib-test/lib/testA/index.js”; import testA from “map-lib-test/lib/testA/index.js”;

Import {testA, testB} as needed.

2. Component labels

Why
and not
? If you look at the entry file for the testA component, there’s this code

import test from './src/index.vue';
test.install = function(Vue) {
    Vue.component(test.name, test);
};
Copy the code

Register a component in Vue with the name test.name, which is the value of the name option in the testA component.