There was a requirement to write 618 + page activities on mobile, but the company was now switching from VUE to React, so it needed to build a new library, requiring all future activities to be in this library.

The directory structure

| | - react_cli / / project - build/configuration/webpack | -- webpack. Base. Config. Js / / webpack basic configuration | -- webpack. Dev. Config. Js / / Webpack open environment configuration | - webpack. Prod. Config. Js / / webpack production environment configuration | | - dist / / pack directory - the mock | - mockData / / / / mock data All the json data | -- initData. Json / / a json data | -- index. Js / / export | | - SRC / / code - the mock data assets / / all the activities of public image, style, | - comments // Common code for all activities, Such as utils, fatch, native etc. | | - components / / all the activities of public components - | - test_2020_618 / pages / / activities / 618 activity | - | - test22 / / test22 page Test33 / / test33 page. | | - test_2020_625 / / 625 activities - babelrc/configuration/Babel. | - env / / environment, Used to configure which activity | -- index. Ejs / / HTML template | -- package. Json / / package | -- postcss. Config. Js/CSS/packaging configurationCopy the code

1, package. Json

{" name ":" react_st_cli ", "version" : "1.0.0", "description" : ""," main ":" index. Js ", "scripts" : {" start ": "cross-env NODE_ENV=development webpack-dev-server --config build/webpack.dev.config.js", "mock": "cross-env NODE_ENV=mock webpack-dev-server --config build/webpack.dev.config.js", "build": "cross-env NODE_ENV=production webpack --config build/webpack.prod.config.js" }, "author": "", "license": < span style = "max-width: 100%; box-sizing: border-box; "^ 7.10.1 @", "Babel/preset - env" : "^ 7.10.2", "@ Babel/preset - react" : "^ 7.10.1", "autoprefixer" : "^ 9.8.0", "Babel - loader" : "^ 8.1.0 clean - webpack -", "plugins" : "^ 3.0.0", "cross - env" : "^ 7.0.2", "CSS - loader" : "^ 3.5.3", "eslint" : "^ 7.2.0 eslint -", "loader" : "^ 4.0.2", "file - loader" : "^ 6.0.0", "the glob" : "^ 7.1.6", "happypack" : "^ 5.0.1," "hard - source - webpack - plugin" : "^ 0.13.1", "HTML - webpack - plugin" : "^ 4.3.0", "less" : "^ 3.11.2", "less - loader" : "^ 6.1.0", "mini - CSS - extract - the plugin" : "^ 0.9.0", "mocker - API" : "^ 2.1.0", "optimize - CSS - assets - webpack - plugin" : "^ 5.0.3 postcss -", "loader" : "^ 3.0.0", "postcss - px - to - the viewport" : "^ 1.1.1", "purgecss - webpack - plugin" : "^ 2.2.0," "speed - measure - webpack - plugin" : "^ 1.3.3", "style - loader" : "^ 1.2.1", "terser - webpack - plugin" : "^ 3.0.3", "url - loader" : "^ 4.1.0", "webpack" : "^ 4.43.0", "webpack - bundle - analyzer" : "^ 3.8.0", "webpack - cli" : "^ 3.3.11 webpack - dev -", "server" : "^ 3.11.0", "webpack - merge" : "^ 4.2.2"}, "dependencies" : {" @ Babel/runtime ": "^ 7.10.2 dotenv", ""," ^ 8.2.0 ", "lodash" : "^ 4.17.15", "lodash - es" : "^ 4.17.15", "react" : "^ 16.13.1", "the react - dom" : ^16.13.1"}, "browserslist": ["defaults", "IE >= 9", "last 2 versions", "> 1%", "iOS 7", "last 3 iOS versions"]}Copy the code

Package. json comes in three environments: Development, Mock, and production, and not much else.

2, webpack. Base. Config. Js

Const path = require("path") Const HappyPack = require(' HappyPack ') const HappyPack = require(' HappyPack ') // Remove CSS styles from js to prevent large files and network requests from timeout. // style-loader conflicts with mini-css-extract-plugin. If the mini-CSS-extract-plugin is used, Style-loader const MiniCssExtractPlugin = require('mini-css-extract-plugin') const HtmlWebpackPlugin = Require ("html-webpack-plugin") // introduce dotenv, Env require('dotenv').config() const glob = require("glob") const isProduction = Process.env.node_env === 'production' // Read all page entries in SRC directory const entryToObj = () => {let entry = {} glob .sync(`./src/pages/${process.env.ACTIVITY_NAME}/**/index.js`) .map(item => { let name = item.split('/') entry[name[4]] = Const getHtmlConfig = function (name) {return {template: const getHtmlConfig = function (name) {return {template: `./index.ejs`, filename: isProduction ? HTML ':' ${name}.html ':' ${name}.html ', inject: true, // true: default, script tag is located at the bottom of the body of the HTML file hash: False, // Insert HTML chunks in packaged resources with hash chunks: ['vendor', 'common', name], minify: {removeComments: CollapseWhitespace: true // Collapse attributequotes: true, // Remove attribute references},}; }; Module.exports = {// multi-entry: entryToObj(), // Resolve: {extensions: ['.js', '.json', '.jsx'], alias: {'@': Path. The resolve (' SRC ')}}, / / module module: {rules: [{test: / \. (js | JSX) $/, use: [' happypack/loader? Id = Babel '], exclude: path.resolve(__dirname, 'node_modules'), }, { test: /\.(le|c)ss$/, use: [ MiniCssExtractPlugin.loader, "css-loader", "postcss-loader", "less-loader" ] }, { test: /\.(png|jpe?g|gif)$/, use: [{loader: "url-loader", options: {limit: 5 * 1024, // if the limit is smaller than this, the base64 image will be processed. 'imgs/[name].[contenthash].[ext]', esModule: False, in solving the react / / img SRC for [object Module] question outputPath: 'static' / / packaging after pictures on the dist/static/down}}}, {test: / \. (woff | woff2 | eot | the vera.ttf | otf) $/, use: [' file - loader]}}], / / extraction, generate the code optimization: {splitChunks: {chunks: 'all', cacheGroups: {// sets the cacheGroups used to extract chunk vendors that meet different rules: {// vendors are extracted from third-party plug-ins // specifying third-party packages under node_modules. If you don't need lodash on your home page, you can package loDash separately // remove Lodash and pack the rest of the third-party libraries into a package: /node_modules\/(? ! // test: /node_modules/ chunks: 'all', name: // node_modules/ chunks: 'all', name: 'vendors', // packaged file names, arbitrary names // Set precedence, prevent and customize common code extraction from being overridden, do not package priority: 10}, common: {// error: 'error ',' error ', 'error ',' error ', 'error ',' error ', 'error ',' error ', 'error ',' error ', 'error ',' error ' 0, // Generate a new package minChunks: 2}}}}, plugins: [new HappyPack({id: 'Babel ', loaders: ['babel-loader? CacheDirectory =true'] // enable caching}), new MiniCssExtractPlugin({filename: isProduction ? 'css/[name].[contenthash].css' : '[name].[contenthash].css', }) ], performance: { hints: MaxEntrypointSize: 40000000, // Controls when WebPack generates performance alerts based on the maximum size of the entry starting point maxAssetSize: // This option controls when WebPack generates a performance prompt assetFilter based on a single resource volume: function(assetFilename) { return assetFilename.endsWith('.js') } } } const entryObj = entryToObj() for (item in entryObj) { module.exports.plugins.push(new HtmlWebpackPlugin(getHtmlConfig(item))) }Copy the code

In webpack. Base. Config. Js,

require('dotenv').config()
Copy the code

It’s used to configure which activity, so for example, I’m going to do an activity in 618 right now, in.env

ACTIVITY_NAME = test_2020_618
Copy the code

Then I can use process.env.activity_name to specify the directory for the webpack entry file to traverse. The entryToObj method generates the entry object and returns the following

{ 
    test22: './src/pages/test_2020_618/test22/index.js',
    test33: './src/pages/test_2020_618/test33/index.js'
}
Copy the code

SplitChunks are used to divide code, and I extract third-party plug-ins into vendors. However, if my project is large and I need to introduce a lot of vendors, such as ANTD, LoDash, momnetJS, etc., the vendors will be very large, and then I need to consider how to reduce the vendors. There are several ideas:

  • 1. Split the vendors, so, for example, I don’t need LoDash to access the home page, so I remove LoDash, pack the remaining third-party libraries into a package, and then pack the LoDash libraries separately.
  • 2. Introduction on demand.
  • 3. Take advantage of tree-shaking features and reference ES6 modular versions
    import { fill } from "lodash-es" 
    Copy the code

In the final iteration, the HtmlWebpackPlugin is pushed as many pages as there are pages

3, webpack. Dev. Config. Js

Const webpackMerge = require("webpack-merge") // mock data const apiMocker = require('mocker-api') const baseWebpackConfig = require("./webpack.base.config") const path = require("path") module.exports = webpackMerge(baseWebpackConfig, Output: {path: path.resolve(__dirname, '.. /dist'), filename: "js/[name].[contenthash].js", // publicPath: ".. DevServer: {contentBase: path.join(__dirname, ".. / SRC /pages/index"), // tells the server where to provide content. Only use publicPath:'/', // access resource prefix host: "0.0.0.0", port: "9527", overlay: Proxy: {'/ API ': {target:'http://1.1.1.1:0000', changeOrigin: true, pathRewrite: { '^/api': '/' } } }, before (app) { if(process.env.NODE_ENV === 'mock') { apiMocker(app, path.resolve(__dirname, '.. /mock/index.js')) } } } })Copy the code

Mock. When in a mock environment, the data in the mock folder is called. Here is the code in index.js

const fs = require('fs') function fromJSONFile(filename) { return (req, res) => { const data = fs.readFileSync(`mock/mockData/${filename}.json`).toString() const json = JSON.parse(data) return  res.json(json) } } const proxy = { 'POST /api/test_618/initData': fromJSONFile('initData') }; module.exports = proxyCopy the code

It can be used in JS

const fatchMock = () => {
    fetch('/api/test_618/initData', { method: "POST" })
        .then(res => res.json().then(data => console.log(data)))
}
Copy the code

4, webpack. Prod. Config. Js

// merge const webpackMerge = require("webpack-merge") const baseWebpackConfig = require("./webpack.base. Config ") const webpackMerge = require("./webpack.base Path = require("path") const glob = require("glob") const {CleanWebpackPlugin} = Const OptimizeCssAssetsPlugin = require('optimize- csS-assets-webpack-plugin ') Const PurgecssPlugin = require('purgecss-webpack-plugin') // compress js const TerserPlugin = Require ('terser-webpack-plugin') Const HardSourceWebpackPlugin = require('hard-source-webpack-plugin') // webpack volume analysis tool const BundleAnalyzerPlugin = require(' webpack-bundle-Analyzer ').BundleAnalyzerPlugin // webpack speed analyzer const SpeedMeasurePlugin  = require("speed-measure-webpack-plugin") const smp = new SpeedMeasurePlugin() module.exports = Wrap (baseWebPackMerge (baseWebpackConfig, {// specify build environment mode: "Production ", // cheap-module-eval-source-map allows you to view the source code, facilitating breakpoint debugging, recommended for test environments. // devtool: 'cheap-module-eval-source-map', // output: {path: path.resolve(__dirname, '.. Dist '), filename: "js/[name].[contenthash].js", // contenthash publicPath: ".. New CleanWebpackPlugin({verbose: true, // enable output information on console dry: New OptimizeCssAssetsPlugin({}), new BundleAnalyzerPlugin(), new PurgecssPlugin({paths: glob.sync(`${path.join(__dirname, '../src/pages/**')}`, { nodir: }), new HardSourceWebpackPlugin()], optimization: {usedExports: If you want to use a third-party plugin, you need to set minimizer to minimizer: [new TerserPlugin({cache: Parallel: true, // Parallel package terserOptions: {compress: {drop_console: true, // remove console}},})Copy the code

The code annotations in webpack.prod.config.js are more detailed.

5、.babelrc

{"@babel/ presets": [["@babel/preset-env", {"modules": false // make tree-shaking work}], "@babel/preset-react"], "plugins": ["@babel/plugin-transform-runtime"] }Copy the code

5, the index ejs

<! DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <meta name="format-detection" content="telephone=no,email=no"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no"> <meta name="theme-color" content="#000000" /> <title>ST</title> </head> <body> <noscript>You need to enable JavaScript to run this app.</noscript> <div id="app"></div> <! -- built files will be auto injected --> <% if (process.env.NODE_ENV ! = = 'production') {% > < script type = "text/javascript" SRC = "https://cdn.bootcss.com/eruda/1.2.6/eruda.min.js" > < / script > <script>eruda.init(); </script> <%}%> </body> </html>Copy the code

It is convenient to use EJS here

6, postcss. Config. Js

Module. exports = {plugins: {autoprefixer: {}, "postcss-px-to-viewport": {viewportWidth: // The width of the window corresponds to the width of our design, which is usually 750. UnitPrecision: 3, // Specify the decimal number of Windows unit values to be converted from 'px' viewportUnit: 'vw', // Specify the Windows unit to be converted to. Vw selectorBlackList is recommended: ['.ignore'], // Specify a class that is not converted to Windows units. It can be customized and added indefinitely. It is recommended to define one or two generic class names minPixelValue: 1, // less than or equal to '1px' does not convert to window units, you can also set to the value you want mediaQuery: false // Allows conversion of 'px' in media queries}}}Copy the code

For some CSS Settings, autoprefixer needs to work with browserslist under package.json.

  • Reference: WebPack4 Multi-page, multi-environment configuration