beginning

Most of the time, components are pulled out and sent to NPM for reuse or generalization. One problem you will discover is how best to distribute and manage these components. It turns out that other tutorials on the web are either too patchy or lacking in detail. Here borrow this opportunity to sum up, if read feel helpful, might as well a praise, pay attention to ha ha.

Once we understand it, we can

  1. Write and publish a usable component library
  2. To be able toimport { demoComponent } from 'xxxUI'Way to introduce
  3. Also to be able toimport demoComponent from xxxUI/component/demoComponentWay to introduce
  4. The individual componentsPackaging is relatively independentDo not interfere with each other
  5. The output component canSimple and easy to useAnd with goodcompatibility
  6. Component libraries can be implemented according to user configurationAccording to the need to load
  7. Component libraries can be implemented according to user configurationTree Shaking
  8. Components throughUnit testing
  9. Package and publish tonpm

Like this, heh heh.

The React component library is used as an example. Vue is the same, but the Babel configuration is different

The project structure

Structure analysis

Let’s start by looking at the component library project structure

Well, it looks very complicated, and the first impression should be to think about what kind of messy files are there. Here’s a brief explanation.

  • srcStoring core code
  • distStore the code for the last packaged output
  • sassSeparate styles (with components, of course; the purpose here is to use related styles without related components)
  • __mocks__Mock object,coverage(Coverage),test,jest.config.js(JEST configuration) These are all related to unit testing and will be covered in more detail in the next chapter
  • .npmignorewith.gitignoreWorks in a similar way
  • .babelrcI think the famous Babel would know
  • Other should be very familiar with, and then go on to introduce the suspicion of the word count.

Key directory

Let’s focus on the SRC core code directory. First we store components in Component and reference components in Component with index in the outer layer, since index is found by default when import is introduced without providing a specific path. After the output is packaged, the component can be referenced by importing {demoComponent} from ‘xxxUI’.

// index.jsx
import demoComponent from './component/demoComponent';

export {
 demoComponent
};
Copy the code
// demoComponent.jsx
export default class demoComponent extends Component {
    render() {
        return (
            <div>
                hello world
            </div>); }}Copy the code

Then use this form to export components, can through the import demoComponent from xxxUI/component/demoComponent introduced this form a separate components.

Component libraries are loaded on demand

Import {demoComponent} from ‘xxxUI’ imports the entire library of components into the development project, sometimes using only two or three components, which we don’t want to see. By the import demoComponent from xxxUI/component/demoComponent this form to quote, can only introduce some needed components, this could help to solve this problem. But it’s inconvenient to write this long string every time you introduce it. This is where the babel-plugin-import plug-in comes in.

import { demoComponent, demoComponent1, demoComponent2 } from 'xxxUI'// Use the babel-plugin-import plug-in to automatically rewrite the above invocation form in the AST(abstract syntax tree) to the following form. // It is easy to import related components, And don't have to worry about a full introduction of the import the issues that led to the package is too large demoComponent from xxxUI/component/demoComponent import demoComponent1 the from xxxUI/component/demoComponent import demoComponent2 from xxxUI/component/demoComponentCopy the code

Finally, configure the path to be converted in. Babelrc

// .babelrc{..."plugins": ["import", {
            "libraryName": "xxxUI"."libraryDirectory": "component",}}]Copy the code

Note that this needs to be configured by the user of the component library, not written in the component library’s.babelrc. If the component library supports on-demand loading, this configuration should be written in readme.md for the user of the component library to select. The advantages and disadvantages of loading on demand are determined by the specific project environment and need to be analyzed on a case-by-case basis.

In this way, through clever document structure, goals 2, 3, and 6 have been achieved.

The input

Once the project structure is clear, the next step is to collect component source code. Resolve: path.resolve(__dirname, ‘SRC ‘, ‘index.jsx’). But since we need each component to be packaged separately from each other, we need to introduce each component individually, while keeping the corresponding file structure.

function getFileCollection() {
    const globPath = './src/**/*.*(jsx|js)';
    const files = glob.sync(globPath);
    return files;
}

function entryConfig() {
    let entryObj = {};
    getFileCollection().forEach(item => {
        const filePath = item.replace('./src'.' ');
        entryObj[filePath] = path.resolve(__dirname, item);
    });
    return entryObj;
}
Copy the code

Glob is a handy tool to match files. What is returned is a mapping object of the file path, and we can look at the console to see which files were entered.

Ok, now is what to do with these source files.

Compile processing and component library Tree Shaking

The process here is simple. The logic is to configure Babel to process es6+ source code into ES5 compatible code, and also to embed small SVG ICONS in Base64 format. This is more about allowing users to use the component library with as little configuration as possible and as little upstart cost as possible. If we keep the ES6 code here, it will allow developers to freely configure Tree Shaking (for example, if developers only use one method in one component, there is no need to bring in the whole component). Finally, we’ll talk about how developers configure Tree Shaking.

Es6 Modules provide modularity at the syntactic level, Tree Shaking is based on Es6 modularity. When compiling and packaging nodes can be statically analyzed in the AST to weed out code that is not needed. Our compiled and packaged ES5 code is not Tree Shaking.

// loader configuration rules in webpack.config: [{test: /.jsx|.js$/,
    loader: 'babel-loader',
    exclude: /node_modules/
}, {
    test: /\.(jpg|png|gif|svg|jpeg)$/,
    loader: 'url-loader',
    exclude: /node_modules/
}]
Copy the code
// .babelrc
{
    "presets": [["@babel/preset-env", {// Browser compatibility scheme configuration"targets": {
                "browsers": [
                    "0.25%" >."not ie 11"."not op_mini all"]}}],"@babel/preset-react",]."plugins"[// Some necessary conversion plug-ins"@babel/plugin-proposal-function-bind"."@babel/plugin-proposal-class-properties"// Solve the problem of duplicate utility functions generated during compilation"@babel/plugin-transform-runtime"."transform-remove-console"]}Copy the code

Reach goal # 7.

The output

Package the compiled output to the dist directory. Keep in mind that the structure of the dist directory is the same as that of the SRC directory so that the reference paths between components are not messy, as in the dist directory, which has a similar structure to SRC.

Let’s take a look at the output configuration. Since we kept the file path information during the file input, we can simply change the suffix and output to dist. LibraryTarget is used to set the packaging format, using the UMD standard. If library is set, the single-entry reference import xxxUI from ‘xxxUI’ will be exported, which is undesirable. The values of library and libraryTarget vary depending on the project type. See here for details

output: {
    filename: (chunkData) => {
        let filePath = chunkData.chunk.name;
        const filename = filePath.replace('.jsx'.'.js');
        return filename;
    },
    path: __dirname + '/dist',
    libraryTarget: 'umd',
    // library: 'xxxUI'
}
Copy the code

The react/react-DOM package is also included in the react/react-DOM package, resulting in a large component library.

We need to configure this to filter out imported third-party packages

externals: [
    function(context, request, callback) {// Allow the following suffix files to be compiledif (/.jsx|.jpg|.png|.gif|.svg|.jpeg$/g.test(request)) {
            returncallback(); } callback(null, request); }]Copy the code

You can see the huge changes! The entire package size is now only 120KB (excluding styles)

Since the styles are isolated, you just need to copy them to the dist directory, which can be configured automatically by the plug-in.

new CopyPlugin([{
    from: './sass',
    to: './sass'
}])
Copy the code

Achieve 4 or 5 of your goals

Final release

  1. Go to the official website to register first
  2. npm loginThe login
  3. add.npmignoreFiles, list the files that you want to ignore
  4. addREADME.mdIt is a good habit to write out the necessary explanations
  5. inpackage.jsonthescriptAdd command towebpack --mode production && npm publish ./dist. This means packaging in production mode and puttingdistPublish on catalognpm.

At the end of the readme.md user manual you can write something like this

/ / installation
npm i -S xxxUI

// Webpack configuration processing styles
{
    test: /\.scss$/.use: [MiniCssExtractPlugin.loader, 'css-loader'."postcss-loader".'sass-loader'].include: [
        path.join(__dirname, 'node_modules/xxxUI/sass/')]}// Introduce styles in index.jsx
import "xxxUI/sass/index.scss";

// Optional ---------------
//.babelrc configuration is loaded on demand
"plugins": [["import",
        {
            "libraryName": "xxxUI"."libraryDirectory": "component",}],// ...
]

// Optional ---------------
// Configure Tree Shaking
// webpack.config.js
// ...
{
    test: /\.scss$/.use: [MiniCssExtractPlugin.loader, 'css-loader'."postcss-loader".'sass-loader'].include: [
        path.join(__dirname, 'node_modules/xxxUI/sass/')].// Style does not need to be Tree Shaking
    sideEffects: true
}
// ...
optimization: {
    usedExports: true.minimizer: [
       new TerserPlugin({})
    ]
}
// .babelrc
"presets": [["@babel/preset-env",
        {
            // Want to achieve the Tree Shaking effect here
            "modules": false,}]]Copy the code

Babel option modules in ‘amd’ | ‘umd’ | ‘systemjs’ |’ commonjs | false these a few, because the Tree Shaking based on ES6 modules, here cannot be converted to other standards, can only choose false, That is to use the original file module standard compilation.

Done, a useful component library has been published, come and try it.

Unit testing

And so on, it seems to also miss the unit testing, in fact, there are too many points to pay attention to (KENG), one can not finish, will be the next article “Re from scratch component unit testing” in detail.

The end of the

SluckyUI source code and project construction is built according to this pattern, there are other considerations in the details, may be different, but the idea is the same. The idea behind SluckyUI is to create a component library seed that allows other developers to quickly redevelop and reduce unnecessary wheel building. However, there are still a lot of incomplete writing in SluckyUI, so you can click Start to support it.

Online component Demo& component library source

Recently I finally sorted out the code. The early writing is a little bit ugly. Online component Demo& component library source

How to write a component in a component library?

  • Re from scratch UI library authoring life specification
  • Re from scratch UI library writing buttons for Life
  • Re writing forms for Life from scratch UI Library
  • Re writing life’s Table Components from scratch UI library
  • Re writing life from scratch UI Library – Step Management Component Steps
  • Re from scratch UI library writing life-tree components
  • Re From Scratch backend learning configuration Ubuntu+Ngnix+Nodejs+Mysql environment
  • Configuring LAMP environments for Re From Scratch Back-end Learning

Web Security Series

  • “Web Attack and Defense Warrior Directory -XSS&CSRF”