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
- Write and publish a usable component library
- To be able to
import { demoComponent } from 'xxxUI'
Way to introduce - Also to be able to
import demoComponent from xxxUI/component/demoComponent
Way to introduce - The individual components
Packaging is relatively independent
Do not interfere with each other - The output component can
Simple and easy to use
And with goodcompatibility
- Component libraries can be implemented according to user configuration
According to the need to load
- Component libraries can be implemented according to user configuration
Tree Shaking
- Components through
Unit testing
- Package and publish to
npm
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.
src
Storing core codedist
Store the code for the last packaged outputsass
Separate 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.npmignore
with.gitignore
Works in a similar way.babelrc
I 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
- Go to the official website to register first
npm login
The login- add
.npmignore
Files, list the files that you want to ignore - add
README.md
It is a good habit to write out the necessary explanations - in
package.json
thescript
Add command towebpack --mode production && npm publish ./dist
. This means packaging in production mode and puttingdist
Publish 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”