preface

  • Build your React+Typescript environment

On the basis of an article introduces the React + Typescript environment set up, and didn’t do any optimization configuration, and according to the different development environment configuration, split this article is mainly to introduce these, and all configuration is on the basis of the previous article, if you have any questions or wrong place, hope leaders can timely points out, Finally, there is the project address ~

A few dependencies to use

  • Webpack-merge: Merges webpack configuration
  • Webpack.defineplugin: Create some global variables at compile time
  • Webpack. HotModuleReplacementPlugin: thermal overload for enabling local module, the development environment
  • Html-webpack-plugin: Generate HTML based on the bundle generated by webpack
  • Add-asset-html-webpack-plugin: Used in conjunction with htMl-webpack-plugin to reference the resource file to the HTML it generates
  • Mini-css-extract-plugin: Extract CSS into different files
  • Terser-webpack-plugin: New plugin for compressed JS code
  • Optimize – CSS-assets-webpack-plugin: optimize and compress CSS code during Webpack packaging, mainly using cssnano compressor.
  • Webpack.runtimechunk: Related to persistent caching
  • Webpack.splitchunks: The biggest change in WebPack 4 is the abolition of the CommonsChunkPlugin and the introduction of Optimization.splitchunks to configure the subcontracting policy.
  • Webpack. DllPlugin: precompiles modules. It compiles modules configured to be precompiled in the cache during the first compilation and uses the cache when these modules are parsed into the cache during the second compilation
  • Webpack. DllReferencePlugin: compile the compiled module associated with the current in advance, when the webpack resolution to these modules, can use directly compiled module in advance
  • Webpack-bundle-analyzer: Webpack packaging analyzer, you can see the ratio of bundles
  • Clean-webpack-plugin: Clean up the package folder

Common configuration

We continued to add and update our configuration in webpack.common.js.

Define global variables that may be used

Sometimes you need to define different variables in different environments, like the. Env file in the project created by VUe-cli3.

Start by creating a new env.json folder in the build folder and writing in it any global variables you may need.

{
  "dev": {
    "APP_ENVO": "dev"."BASEURL": "https://xxxx.xxxx.com/api/"
  },
  "test": {
    "APP_ENVO": "test"."BASEURL": "https://xxxx.xxxx.com/api/"
  },
  "pre": {
    "APP_ENVO": "pre"."BASEURL": "https://xxxx.xxxx.com/api/"
  },
  "prod": {
    "APP_ENVO": "prod"."BASEURL": "https://xxxx.xxxx.com/api/"}}Copy the code

Yargs-parser is used to convert command line parameters in NPM scripts into key-value pairs such as –mode development will be parsed into key-value pairs mode: “Development” to facilitate obtaining parameters in the configuration file.

Then add our environment parameters –env test and so on to scripts in package.json, for example:

  "scripts": {
    "dev": "webpack-dev-server --config build/webpack.dev.js --mode development --open"."test-build": "webpack --config build/webpack.prod.js --mode production --env test"."pre-build": "webpack --config build/webpack.prod.js --mode production --env pre"."prod-build": "webpack --config build/webpack.prod.js --mode production --env prod"
  },
Copy the code

Then get the parameters in webpack.common.js and configure them using the webpack.defineplugin plugin

const argv = require('yargs-parser')(process.argv.slice(4))
const APP_ENV = argv.env || 'dev'

const env = require('./env.json')
const oriEnv = env[config.APP_ENV]
Object.assign(oriEnv, {
	APP_ENV: config.APP_ENV
})

const defineEnv = {}
for (let key in oriEnv) {
	defineEnv[`process.env.${key}`] = JSON.stringify(oriEnv[key])
}

module.exports={
  / /... Other configurations are omitted
  plugins: [
    new webpack.DefinePlugin(defineEnv)
  ]
}

Copy the code

${key} can be used to obtain the corresponding value after the project is started.

Modify output output

Modify our packaged JS output directory and name to make it look cleaner.

module.exports={
  output: {
    filename: 'js/[name].[chunkhash].js'.path: path.join(__dirname, '.. /dist')}}Copy the code

Development Environment Configuration

First create a new webpack.dev.js under Build, and then you need to install webpack-Merge to merge the configuration.

yarn add webpack-merge -D
Copy the code

Next to introduce it and common configuration file, before devServer moved to here, and introduce webpack. HotModuleReplacementPlugin convenient we developed for enabling local module thermal overload, if want to configure the agent, You need to configure proxy in devServer. For details about the meaning of each field, see the official website.

As for source-map, you can understand that it is a mapping between your source code and the packaged code. Because the packaged code is compressed, it can be difficult to find errors and debug, so you need it.

const webpack=require('webpack')
const merge = require('webpack-merge')
const baseConfig=require('./webpack.common')
const HtmlWebpackPlugin = require('html-webpack-plugin')

const devConfig={
  mode: 'development'.devtool: 'eval-source-map'.plugins: [
    new HtmlWebpackPlugin({
      filename: 'index.html'.template: 'public/index.html'.inject: true
    }),
    new webpack.HotModuleReplacementPlugin()
  ],
  devServer: {
    host: 'localhost'.port: 3000.historyApiFallback: true.overlay: {// When a compiler error or warning occurs, a black background layer and error message are displayed on the page
      errors: true
    },
    inline: true.hot: true.// proxy: {
    // '/api/v1': {
    // target: '',
    // ws: true,
    // changeOrigin: true,
    // pathRewrite: {
    // '^/api/v1': '/api/v1'
    / /}
    / /}
    // }}},module.exports=merge(baseConfig,devConfig)
Copy the code

Then add our command to start the development environment to scripts in package.json, and the project is ready to start.

"dev": "webpack-dev-server --config build/webpack.dev.js --mode development --open"
Copy the code

Production Environment Configuration

Create a new webpack.prod.js file under Build, which, like the development environment, introduces common configuration, and then gradually introduces plug-ins.

const merge = require('webpack-merge')
const baseConfig = require('./webpack.common')
const webpack = require('webpack')

const prodConfig = {
  mode: 'production'.devtool: 'source-map'
}
module.exports = merge(baseConfig, prodConfig)
Copy the code

html-webpack-plugin

Minify is used to automatically generate HTML, and by default, to introduce packed JS and CSS into HTML files. There are many minify configuration items, see html-minifier for details

const HtmlWebpackPlugin = require('html-webpack-plugin')

const prodConfig = {
  plugins: [
    new HtmlWebpackPlugin({
      filename: 'index.html'.template: 'public/index.html'.inject: true.minify: {
        removeComments: true.// Remove the comment
        collapseWhitespace: true.// Remove extra white space
        removeAttributeQuotes: true // Remove quotes from some attributes, for example id="moo" => id=moo}}})]Copy the code

mini-css-extract-plugin

Use mini-CSS-extract-plugin to separate CSS from JS and support chunk CSS.

const MiniCssExtractPlugin = require('mini-css-extract-plugin')

// ...
const prodConfig = {
  // ...
  plugins: [
    // ...
    new MiniCssExtractPlugin({
      // Options similar to the same options in webpackOptions.output
      // both options are optional
      filename: assetsPath('css/[name].[contenthash].css'),
      chunkFilename: assetsPath('css/[name].[id].[contenthash].css')]}})Copy the code

The MiniCssExtractPlugin does not support local development of hot overloading. The MiniCssExtractPlugin does not support local development of hot overloading. The development environment still uses style-loader, using CSS files as an example.

  {
    test: /\.css$/.// Rematches the file path
    exclude: /node_modules/.use: [
      // APP_ENV ! = ='dev' ? MiniCssExtractPlugin.loader : 'style-loader',
      {
        loader: 'css-loader'.// parse @import and URL () as import/require()
        options: {
          importLoaders: 1 // 0 => No loader(default). 1 => postcss-loader; 2 => postcss-loader, sass-loader}},'postcss-loader']}Copy the code

clean-webpack-plugin

It is used to clear local files. If the dist folder is not cleared during production environment packaging, different JS files or CSS files will be generated and stacked in the folder each time. Note that different versions bring different usage.

const { CleanWebpackPlugin } = require('clean-webpack-plugin')

// ...
const prodConfig = {
  // ...
  plugins: [
    // ...
    new CleanWebpackPlugin(),
  ]
}
Copy the code

optimize-css-assets-webpack-plugin

Optimize compression of CSS code during Webpack packaging, using the CSsnano compressor, which is not configured in plugins, but minimizer under Optimization.

const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin')

// ...
const prodConfig = {
  // ...
  optimization: { // Performance configuration
    // ...
    minimizer: [
      new OptimizeCssAssetsPlugin({
        cssProcessor: require('cssnano'), // Use the cssnano compressor
        cssProcessorOptions: {
          reduceIdents: false.autoprefixer: false.safe: true.discardComments: {
            removeAll: true}}})]}}Copy the code

terser-webpack-plugin

Optimize – csS-assets-webpack-plugin is used to compress CSS code, and uglifyjs-webpack-plugin is used to compress JS code, but it needs Babel support. The Terser-webpack-plugin is now officially recommended, but it works pretty well and doesn’t require installation.

const TerserPlugin = require('terser-webpack-plugin')

// ...
const prodConfig = {
  // ...
  optimization: { // Performance configuration
    // ...
    minimizer: [
      new TerserPlugin({
        cache: true.// parallel: true,
        terserOptions: {
          compress: {
            warnings: true.drop_console: true.drop_debugger: true.pure_funcs: ['console.log'] / / remove the console}},sourceMap: true}}}),]Copy the code

webpack.RuntimeChunk

It can separate the chunks mapping list from app.js, because the ID of each chunk is basically hash based on the content, so every change you make will affect it. If you don’t extract it, app.js will change every time. The cache is invalidated. In webpack4, there is no need to import the plug-in manually, just configure runtimeChunk.

const prodConfig = {
  // ...
  optimization: { // Performance configuration
    // ...
    {
      runtimeChunk: true; }}}Copy the code

The runtime.js generated by packaging is very small, usually only a few kilobytes after gzip, but the file changes frequently and we need to request it again each time. Its HTTP time is much longer than its execution time, so it is not recommended to unpack it separately. The optimization is about inlining it into our index.html.

The script-ext-html-webpack-plugin is used here.

const ScriptExtHtmlWebpackPlugin = require("script-ext-html-webpack-plugin");

HtmlWebpackPlugin must be referenced after HtmlWebpackPlugin
// Inline name is the same as your runtimeChunk name
new ScriptExtHtmlWebpackPlugin({
  //`runtime` must same as runtimeChunk name. default is `runtime`
  inline: /runtime\.. *\.js$/
});
Copy the code

webpack.splitChunks

This configuration allows us to extract the desired package with a certain rule. Webpack4 has a default code subcontracting strategy.

  • Whether the new chunk is shared or from the node_modules module
  • Whether the new chunk size is greater than 30KB before compression
  • The number of concurrent requests for loading chunk on demand is less than or equal to five
  • The number of concurrent requests during initial page loading is less than or equal to 3

About on-demand loaded with the initial page load corresponding to the webpack. SplitChunks. Chunks it says it will choose which block is optimized, async said only optimize dynamic import package, initial loads and the initial import packages, and a value can optimize all said, The default is async, which means that if you dynamically import a package that is larger than 30KB before compression and you reference it in more than five places in your code, WebPack will package it separately.

Usually we need to pull out the large base library packages under node_modules, such as vuex and Vue, or the large UI component libraries such as ANTD and element-UI, as well as self-written components that may be used multiple times across multiple pages. Here is my configuration, note: do not pursue too granular when unpacking, there is no complete solution for resource loading strategy, you need to find the most appropriate unpacking strategy based on your own project.

const prodConfig = {
  // ...
  optimization: { // Performance configuration
    // ...
    splitChunks: {
      chunks: 'async'.// The type of chunk to be extracted. All: all, async: asynchronous, and initial: initial
      // minSize: 30000, // Default, minimum integer type (in bytes) for new chunks
      // maxSize: 0, // the default value, the maximum limit for new chunks, 0 is an infinite integer type (in bytes)
      // minChunks: 1, // The default value, the minimum number of times a new chunk is referenced
      // maxAsyncRequests: 5, // Default value, chunk loaded on demand, maximum number of quests
      // maxInitialRequests: 3, // default, initial chunk loaded, maximum number
      // Name: true, // The default value controls the name of chunk
      cacheGroups: { // Configure the cache group
        vendor: {
          name: 'vendor'.chunks: 'initial'.priority: 10./ / priority
          reuseExistingChunk: false.// Allow reuse of existing code blocks
          test: /node_modules\/(.*)\.js/.// Package only the third party that you originally relied on
        },
        common: {
          name: 'common'.chunks: 'initial'.// test: resolve(" SRC /components"), // Extend your rule
          minChunks: 2.priority: 5.reuseExistingChunk: true
        }
      }
    }
  }
}
Copy the code

Webpack. DllPlugin and webpack. DllReferencePlugin

For example, if the basic runtime environment related to React is not upgraded, the basic modules are put into a package. As long as the package version of these packages is not upgraded, there is no need to compile these modules every time the package is packaged, which can improve the packaging speed. Here we can use webpack.DllPlugin. Then use webpack. DllReferencePlugin to compile this DLL package associated with the current.

Create a new webpack.dll.js file in the build folder and write the following configuration

const path = require('path')
const webpack = require('webpack')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')

module.exports = {
  mode:'production'.entry: {
    // Redux can also be added
    vendor: ['react'.'react-dom'.'react-router-dom']},output: {
    filename: '[name].dll.[hash:8].js'.path: path.join(__dirname, '.. /dll'),
    // The link library output mode is assigned to variables in the 'var' form by default
    libraryTarget: 'var'.// The global variable name export library will be assigned to the global variable in the form of var to get the module inside
    library: '_dll_[name]_[hash:8]'
  },
  plugins: [
    // Empty the previous DLL files at each run time
    new CleanWebpackPlugin({
      cleanOnceBeforeBuildPatterns: [path.join(__dirname, '.. /dll/**/*')]}),new webpack.DllPlugin({
      // path Specifies the output path of the manifest file
      path: path.join(__dirname, '.. /dll/[name].manifest.json'),
      // Same as library, output the name value in manifest.json
      name: '_dll_[name]_[hash:8]'}})]Copy the code

Webpack.prod. js uses the DllReferencePlugin to tell webpack what dynamic link libraries are used. The HTML Webpack plug-in is then injected into the generated HTML using the Add-asset-html-webpack-plugin described below into the resource list.

Vendor.manifest.json is generated by DllPlugin to describe which modules are included in the dynamic link library file.

// ...
const prodConfig = {
  // ...
  plugins: [
    // ...
    // Tell Webpack which dynamic link libraries are used
    new webpack.DllReferencePlugin({
      manifest: path.join(__dirname, `.. /dll/vendor.manifest.json`)]}})Copy the code

Then add another command to scripts in package.json

  "scripts": {
    "dll": "webpack --config build/webpack.config.dll.js",}Copy the code

Then run it and you can see that the root DLL generates two files vendor.dll.xxxxxxxx.js and vendor.manifest.json

add-asset-html-webpack-plugin

We use it to import a given static resource CSS or JS into an HTML file generated by the HTMl-webpack-plugin.

const AddAssetHtmlPlugin = require('add-asset-html-webpack-plugin')

// ...
const prodConfig = {
  // ...
  plugins: [
    // ...
    new AddAssetHtmlPlugin({
      filepath: resolve(`${DLL_PATH}/**/*.js`),
      includeSourcemap: false]}}),Copy the code

webpack-bundle-analyzer

If you want to see the size ratio of the output file after your webpack is packed, you can use this plugin. Add the following configuration to webpack.prod.js. If you want to control whether the plugin is introduced, you can use a variable:

if (config.bundleAnalyzerReport) {
  const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
  prodConfig.plugins.push(new BundleAnalyzerPlugin())
}
Copy the code

When the packaging is complete, a browser window automatically opens showing the size ratio of the output file.

Performance tip

If you want to show performance hints during packaging or development, you can add the following configuration to webpack.common.js.


module.exports={
   // ...
   performance: { // Performance prompt, can prompt too large file
    hints: "warning"./ / false performance tip switch | | "error" "warning"
    maxAssetSize: 100000.// The maximum integer type (in bytes) of the generated file
    maxEntrypointSize: 100000.// The imported file Max limit integer type (in bytes)
    assetFilter: function(assetFilename) {
        // Provides an assertion function for the resource file name
        return (/\.(png|jpe? g|gif|svg)(\? . *)? $/.test(assetFilename))
    }
  } 
}
Copy the code

The last

Here the configuration of the production development environment is basically over, if there is any missing or incorrect configuration, I hope the boss pointed out.

Finally, the address of the project is attached. If there is something wrong, I hope you can point it out. Thank you.

Reference articles:

  • Webpackage 4+Babel7 optimized for 70% speed
  • Using webpack4 with proper posture
  • The Road to Refactoring: WebPack Volume Optimization (hyperdetailed)