preface

New company, one day was suddenly informed to set up a new background project. I prepared for the selection of technology in a hurry and looked at the two projects being iterated and maintained by the project team. Both of the two projects are initialized with a scaffolding built by the company, but the scaffolding is also packaged based on VUE-CLI. The webpack version used in the latest version of VUe-CLI is only WebPack4. While webpack4 is powerful enough, webpack5 has been out for more than a year and has brought with it a number of major changes, in short smaller packages in less time. Just when a new project starts, it is bound to be used to “practice”, and without the use of scaffolding can be customized according to the actual situation of their own project WebPack configuration. This share is mainly to record and summarize the process of using Webpack 5 to build a project. Due to the large length of the article, it is divided into two parts: Basic concepts, configuration and optimization of Webpack: some optimization of packaging time, packaging volume and project. This is the basis of the article, will continue to update the optimized article.


An overview of the content of this article


Core WebPack concepts

Whether it is WebPack 4 or WebPack 5, it is the official website of Webpack, so there are no big changes in the core concepts and configuration.

Entry

Indicates which module WebPack should use as a starting point for building its internal dependency diagram. Once at the entry point, WebPack finds out which modules and libraries are (directly and indirectly) dependent on the entry point.

module.exports = {
  entry: './src/main.ts'
}
Copy the code

Output = output

Output is located at the top key of the object and includes a set of options that dictate how and where WebPack should output your “bundles, assets, and anything else you’ve packaged or loaded with WebPack.”

module.exports = {
  output: {
    filename: 'main.js'.path: path.resolve(__dirname, 'dist'),}}Copy the code

Loader

Webpack can only understand JavaScript and JSON files, which is a built-in capability of WebPack available out of the box. One of the best features of WebPack is that in addition to importing JavaScript, you can import any other type of file through the Loader or the built-in Asset Modules. Loader enables WebPack to process other types of files and convert them into valid modules for use by applications and to be added to dependency diagrams.

module.exports = {
  module: {
    rules: [{test: /\.css$/i,
        use: ['style-loader'.'css-loader'],},],},}Copy the code

Plugins

Loaders are used to transform certain types of modules, while plug-ins can be used to perform a wider range of tasks. Including: package optimization, resource management, injection of environment variables.

module.exports = {
  plugins: [
    new HtmlWebpackPlugin({
      title: 'Project Name',]}})Copy the code

Mode (mode)

By selecting one of development, Production, or None to set the mode parameter, you can enable the optimizations built into WebPack for that environment. The default value is production.

module.exports = {
  mode: 'development'
}
Copy the code
  • development: development mode, which sets the value of process.env.node_env in DefinePlugin to development. There are fewer internal processing operations, so it is faster to run and package.
  • production: Production mode, which sets the value of process.env.node_env in DefinePlugin to production. Many tuning actions are enabled internally: FlagDependencyUsagePlugin FlagIncludedChunksPlugin, ModuleConcatenationPlugin NoEmitOnErrorsPlugin and TerserPlugin, So packaging takes a long time to run.
  • none: Do not use any default optimization options

Webpack basic configuration

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

module.exports = {
  mode: 'none'.entry: './src/main.js'.output: {
    filename: 'main.js'.path: path.resolve(__dirname, 'dist'),},module: {
    rules: [{test: /\.css$/i,
        use: ['style-loader'.'css-loader'],},],},plugins: [
    new HtmlWebpackPlugin({
      title: 'Project Name'}})];Copy the code

mkdir retail-admin

Since Webpack is an NPM tool module, we first initialize a package.json file to manage NPM dependent versions, and then install the core module of Webpack and its CLI module as follows:

  1. It all started with mkdir retail-admin. CD retail-admin run the following command and press Enter to generate package.json without going back
npm init
Copy the code

  1. Specify webpack and Webpack CLI version installation dependency to prevent incompatible anomalies caused by webpack version update. When I build the project, the latest version of Webpack is 5.59.0 and the latest version of Webpack-CLI is 4.9.1.
NPM install [email protected] -d NPM install [email protected] -dCopy the code
// package.json
"devDependencies": {
  "webpack": "^ 5.59.0"."webpack-cli": "^ 4.9.1." "
}
Copy the code

warning

Webpack5 runs on node.js v10.13.0+. The development environment and packaging environment install node.js later than v10.13.0. For node.js version management in the front-end development environment, see Using NVM to Manage Node versions to solve front-end development environment configuration

  1. Create index.html and the source directory SRC in the project root directory, and create main.js in the SRC directory. After Webpack4, we can implement zero configuration packaging. We type a code in main.js and then execute the command in the terminal
// main.js
let array = [1.2.3.4]
array.map((item) = >{
  console.log(item);
})
Copy the code
npx webpack ./src/main.js
Copy the code

As shown in the figure, webpack is successfully packaged, and a warning is reported that mode is not set. Dist directory output main.js file automatically generated, output file code escaped into ES6 arrow function syntax.

  1. Create a new webpack configuration file, webpack.config.js, in the project root directory

  2. Add the WebPack basic configuration

// webpack.config.js
const { resolve } = require('path')

module.exports = {
  mode: 'none'.entry: './src/main.js'.output: {
    filename: 'main.js'.path: resolve(__dirname, 'dist')}}Copy the code
  1. Added scripts command for package.json file
"scripts": {
  "build": " webpack --config ./webpack.config.js"
}
Copy the code
  1. performnpm run buildThe script package successfully generated the dist and main.js files


Configure Babel to handle ES6+

Babel Chinese official website is the compiler of javascript, our daily development is to write JS code with ES6 grammar more. However, most older browsers are not compatible with this approach, and the code will not run. Using Babel helps us translate ES6+ code that doesn’t run in older browser environments into a backward compatible version of JS code at packaging time.

We’ll write an arrow function in SRC /main.js

// src/main.js
[1.2.3.4.5].map((o= >{console.log(o)}));
Copy the code

When you run the package command NPM run build, the output code in the dist directory still contains the arrow function

// dist/main.js
[1.2.3.4.5].map((o= >{console.log(o)}));
Copy the code
  1. Install the Babel dependency
npm install -D babel-loader @babel/core @babel/preset-env
npm install -D @babel/polyfill
npm install -D @babel/plugin-transform-runtime
npm install -D @babel/runtime
npm install -D core-js@3
npm install regenerator-runtime -D
npm install @babel/runtime-corejs3 -D
Copy the code
  • @babel/preset-env: @babel/preset-env includes all the plugins that support modern JavaScript(ES6+), mainly for code-switching and polyfilling of the syntax and APIS we use and are missing in the target browser.

  • @babel/core: the core module of Babel, which can be used programmatically

  • @babel/plugin-transform-runtim: Babel uses very small helper code for some public methods, such as _extend. By default, it is added to every file that needs it. You can introduce Babel Runtime as a separate module to avoid duplication.

  • @babel/ Runtime: install @babel/ Runtime as a dependency when using @babel/plugin-transform-runtim

  1. Configure the Babel – loader
module: {
  rules: [{test: /\.m? js$/,
      exclude: /node_modules/,
      use: {
        loader: 'babel-loader'.options: {
          presets: [['@babel/preset-env', { targets: "defaults"}]}}}]}Copy the code

On execution of the package command, the arrow function is converted to function

// dist/main.js
[1.2.3.4.5].map(function (o) {
  console.log(o);
});
Copy the code
  1. Using @babel/preset-env we can use new ES6 syntax like arrow functions, and introduce Babel shims in the project to use some advanced APIS.
  • With @babel/ Polyfill or core-js@3 you can use new built-in functions (such as Promise or WeakMap), static methods (such as array. from or object. assign), instance methods (such as Array.prototype.includes) and generator functions.
  • core-js@3: @babel/ Polyfill cannot provide the transition from core-js@2 to core-js@3, so a new solution is recommended to replace @babel/ Polyfill above Babel7.4.0. The babel-loader version must be upgraded to 8.0.0 or later, and the @babel/ Core version must be upgraded to 7.4.0 or later.
  1. Example Modify the configuration mode of Babel to create a configuration file. Babelrc
  • Preset (the default): a collection of Babel plug-ins
  • The plugins (plug-in): Transcoding Babel can be enabled by applying plug-ins (or presets) in configuration files.
// .babelrc
{
  "presets": [["@babel/preset-env"]],"plugins": [["@babel/plugin-transform-runtime",
      {
        "corejs": 3}}]]Copy the code

We use the advanced API in main.js to implement an array deduplicate, perform packaging, open index.html in IE9, open console error.

Next, introduce the shim file in the entry file of the project and then pack it, the page still reported an error, this is what ghost…

// import file main.js
// import "@babel/polyfill" // unload @babel/polyfill annotation import @babel/polyfill instead use the following import
import 'core-js/stable'
import 'regenerator-runtime/runtime'
Copy the code

In fact, the error just reported after the packaging file webpack syntax is incompatible! We need to create a new.browserslistrc file to configure the compatible browser range

// .browserslistrc
> 1%
last 2 versions
not ie <= 8
Copy the code
  • > 1%: a browser used by more than 1% of the world’s population
  • last 2 versions: All browsers are compatible with the last two versions
  • not ie <= 8: Indicates that the Version of Internet Explorer is larger than 8

After executing the pack command, the console can print out the results

These advanced apis are available in most modern browsers, and shippers are introduced mainly to be compatible with Internet Explorer and some lower-version browsers. If there is no need to be compatible with lower-version browsers, they may not be introduced because they would increase the size of the package. At this point, with only a few lines of code written, main.js packs up to 584KB in size, which of course is not optimized and compressed, otherwise who can carry it…

The difference between.babelrc and babel.config.js

  • .babelrcOnly the code in this project is affected
  • babel.config.jsAffects code in the entire project, including code in node_modules

In the previous project, the package of the third-party plug-in installed by NPM was not packaged to min.js, but was given the source code. At this time, the code in node_modules should be Babel processed by using the babel.config.js file. Otherwise the packaged code contains ES6+ syntax and will not run in older browsers!!


HTML file creation

The htmL-webpack-plugin will generate an HTML5 file for your project and import all of your WebPack-generated bundles using the Script tag in the body. Simply add the plug-in to the WebPack configuration to do the above.

  1. Install htML-webpack-plugin using documentation
npm install -D html-webpack-plugin
Copy the code
  1. Webpack configuration
const { resolve } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  mode: 'none'.entry: './src/main.js'.output: {
    filename: 'main.js'.path: resolve(__dirname, 'dist')},module: {
    rules: [{test: /\.m? js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader'}}},plugins: [
    new HtmlWebpackPlugin({
      title: 'retail-admin'.filename: 'index.html'.template: 'index.html'.inject: true.scriptLoading: 'defer'}})]Copy the code
  1. Main Options
  • title: The title of the HTML document to be generated.
  • filename: Enter the HTML content of the file.
  • template: Relative or absolute path to the template used to generate HTML.
  • inject: True adds javascript resources to the header or body, depending on the scriptLoading option.
  • scriptLoading: Modern browsers support non-blocking javascript loading (‘defer’) to improve page startup performance, and when set to defer, the defer attribute is added to the generated script tag from importing the bundle.
  • minify: Defaults to true if webpack mode is production, which compresses the output HTML.

Defer and async properties for the script tag

Common Script: During document parsing, if a script script is encountered, the page will be stopped parsing and downloaded.

Defer: If the script tag has this property set, the browser will download the file asynchronously without affecting subsequent DOM rendering; If more than one script tag exists with defer set, all scripts are executed in order; The defer script executes after the document has been rendered and before the DOMContentLoaded event is called.

Async: The async setting that causes scripts to be loaded asynchronously and executed after loading. Async is executed not according to the order of scripts in the page, but by the person who finishes loading first. : : :

  1. Create a new index.html template in the directory

We can use native syntax in templates. We can directly write JS statements to get htML-webpack-plugin parameters, and we can also iterate.

  1. After executing the packaging command, we found that an index. HTML file was generated in the dist directory. We introduced the packaged main.js in the head as defer, and the title was the retail-admin configured above.

  1. Open dist/index.html in your browser and the console prints out 1, 2, 3, 4, 5


Enable the HMR

In the previous configuration of Webpack, we had to build the package and refresh the browser to see the result, which was tedious and time-consuming. Fortunately, WebPack provides us with webpack-dev-server, which provides a basic Web server. And it has real-time reload function. The Settings are as follows:

  1. Webpack – dev – server installation
NPM install [email protected] - DCopy the code
  1. Webpack configuration
module.exports = {
  devServer: {
    hot: true.client: {
      overlay: true
    },
    static: {
      directory: pathResolve('public')},compress: true.host: 'localhost'.allowedHosts: ['oms-test.hd123.com'].port: 9000.open: true.proxy: [{context: ['/test13'.'/v1'].target: 'https://oms-test-merchant.hd123.com/ui/test13'.changeOrigin: true}].headers: { 'Access-Control-Allow-Origin': The '*'}}}Copy the code
  • hot:Enable webPack’s hot module replacement featureReplacing, adding, or removing modules while the application is running can significantly speed up development without having to reload the entire page. Starting with webpack-dev-server4, HMR is enabled by default. It will be automatically applied webpack HotModuleReplacementPlugin, it is necessary to enable the HMR.
  • client.overlay: Displays full screen overlay in the browser when a compilation error or warning occurs
  • static.directory: During recompilation, files in the static directory of the project are not processed, and files are directly read in the corresponding static directory during use, which saves the time and performance overhead of the packaging runtime.
  • allowedHosts: this option allows to allow access to the development server service included in the white list, set this parameter here because the project will be the child of the micro qiankun front-end applications access to other systems, the development, the resources of the project is in the main application domain access, so configuration allowedHosts allows the main application services visit our development environment.
  • compress: Enables gzip compression for each static file
  • headers: Add headers for all responses, configured here to address cross-domain issues with the development environment Qiankun microfront-end
  • host: Specifies the host to use. If you want your server to be externally accessible, set it to0.0.0.0, so that you can also access your own project by accessing the native IP.
  • open: Open your default browser after the server has started.
  • port: Specifies the port number to listen on requests
  • proxy: Configure a proxy server for development environment interface requests to solve the problem of cross-domain interface access during development
  1. Proxy Indicates proxy configuration
  • right/test13/userWill proxy the request tohttps://oms-test-merchant.hd123.com/ui/test13/user
module.exports = {
  devServer: {
    proxy: {
      '/test13': 'https://oms-test-merchant.hd123.com/ui/test13'}}}Copy the code
  • If you don’t want to pass/API, you need to rewrite the path
module.exports = {
  devServer: {
    proxy: {
      '/test13': {
        target: 'https://oms-test-merchant.hd123.com/ui/test13'.pathRewrite: { '^/test13': ' ' }
      }
    }
  }
}
Copy the code
  • If you want to proxy multiple specific paths to the same target, you can use an array of one or more objects with the context attribute:
module.exports = {
  devServer: {
    proxy: [{context: ['/test13'.'/v1'].target: 'https://oms-test-merchant.hd123.com/ui/test13',}]}}Copy the code

Hit the pit to remind

When using the AJAX wrapped library AXIos to send requests, baseURL should be set to./ otherwise the proxy will be invalid!!

  1. Package. json add NPM script and run it
"scripts": {
  "dev": "webpack serve --config ./webpack.config.js"
}
Copy the code

At this time, Webpack will help us start a server in the 9000 port of local localhost, deploy the static resources packaged by the development environment, open the browser display page automatically after running, and modify the content in main.js. After saving the content directly, it will be automatically updated in the browser.

Working with style files

We’ll create a new style folder under the project directory, create index.css under the folder, and write a style code to manipulate our index.html elements. }}}}}}}}}}}}

Processing.css files

  1. At this point, we need to install our CSS-loader, style-loader configuration document
npm install -D style-loader css-loader
Copy the code
  1. Configure the loader
module: {
  rules: [{test: /\.css$/,
      use: [
        'style-loader',
        {
          loader: 'css-loader'.options: {
            sourceMap: false.importLoaders: 2}}]}Copy the code
  1. Re-execute the dev script, with the style in effect and the style code introduced in the style tag.

Process. SCSS files

CSS preprocessors are commonly used in projects, so we can use SASS to write style code.

  1. Install sass-Loader and sASS configuration documents
npm install sass-loader sass node-sass -D
Copy the code

::: tip Node-sass installation is particularly difficult to download, so we will create a.npmrc file in the root directory of the project and specify the download address of the NPM package and Node sass. You can download it again, and it’s free. : : :

registry=https://registry.npm.taobao.org/
sass_binary_site=https://npm.taobao.org/mirrors/node-sass/
Copy the code
  1. Configure the loader
module: {
  rules: [{test: /\.(sa|sc)ss$/,
      use: [
        'style-loader',
        {
          loader: 'css-loader'.options: {
            sourceMap: false.importLoaders: 2}}, {loader: 'sass-loader'.options: {
            sourceMap: false}}]}Copy the code

Use the PostCSS plug-in

When working with styles, we add browser prefixes for some of the CSS3 properties to accommodate different browser vendors.

  1. Install using postCSS-loader configuration document,
npm install postcss-loader postcss -D
Copy the code
  1. Install the autocomplete style plug-in Autoprefixer
npm install autoprefixer -D
Copy the code
  1. Create the postCSS configuration file postcss.config.js
module.exports = {
  plugins: [require('autoprefixer')]}Copy the code
  1. Configure the loader
module: {
  rules: [
    // Process the.css file
    {
      test: /\.css$/,
      {
        use: [
          'style-loader',
          {
            loader: 'css-loader'.options: {
              sourceMap: false.importLoaders: 1}}, {loader: 'postcss-loader'.options: {
              sourceMap: false}}]}},// Process.scss files
    {
      test: /\.(sa|sc)ss$/,
      use: [
        'style-loader',
        {
          loader: 'css-loader'.options: {
            sourceMap: false.importLoaders: 2}}, {loader: 'postcss-loader'.options: {
            sourceMap: false}}, {loader: 'sass-loader'.options: {
            sourceMap: false}}]}Copy the code
  1. Use postcss – loader will create the browserslistrc specify a range of browser compatibility, and then implement the packaging, the transform before safari, chrome is automatically added to the private property – its – prefix


Integrate vUE framework

The project we set up here uses vue2 framework. If you want to use other frameworks, you can install packages of other frameworks and corresponding loader and plugin here.

  1. Install vUE and VUe-loader Official documents of vue Loader
npm install vue
npm install -D vue-loader vue-template-compiler
Copy the code

Vue-template-compiler needs to be installed separately because you can specify its version separately. When a new version of each VUE package is released, a corresponding version of vue-template-Compiler is also released. The version of the compiler must be synchronized with the base VUE package so that vue-Loader generates run-time compatible code. This means that every time you upgrade the VUE package in your project, you should also upgrade the VUe-template-Compiler.

const { VueLoaderPlugin } = require('vue-loader')

module: {
  rules: [{test: /\.vue$/,
      loader: 'vue-loader'}},plugins: [
  // Be sure to introduce this plugin!
  new VueLoaderPlugin()
]
Copy the code
  • VueLoader v15 now requires a webpack plugin to be used correctly: the VueLoaderPlugin is a must! Its job is to copy and apply the other rules you have defined to the corresponding language blocks in the.vue file. For example, if you have a match/\.js$/It will be applied to.vue files<script>Block.
  1. Create the app. vue file in the SRC directory
// App.vue
<template>
  <div id="app">
    retail-admin
  </div>
</template>

<script lang='ts'></script>

<style scoped lang='scss'>
  #app {
    width: 100px;
    height: 100px;
    background-color: #ccc;
  }
</style>
Copy the code
  1. Mount vue in main.js and open a page that displays retail-admin
// main.js
import 'core-js/stable'
import 'regenerator-runtime/runtime'
import Vue from 'vue'
import App from './App.vue'
// import './style/index.css'
// import './style/index.scss'


new Vue({
  render: h= > h(App)
}).$mount('#app')
Copy the code


asset module

Generally, static resources such as pictures or font files will be introduced in our front-end project. Before Webpack5, we need to process resource files such as pictures, fonts or audio and video files through Loader:

  • raw-loader: Imports the file as a string
  • url-loader: inline the file as a data URI into the bundle
  • file-loader: Sends the file to the output directory

But the asset Module type is new in Webpack5, replacing all of these loaders by adding four new module types:

  • asset/resource: Sends a separate file and exports the URL. This was previously implemented using file-loader.
  • asset/inline: Exports the data URI of a resource. This was previously implemented using urL-loader.
  • asset/source: Exports the source code of the resource. This was previously implemented using raw-loader.
  • asset: Automatically selects between exporting a data URI and sending a separate file. Previously, the urL-loader was used and the resource volume limit was configured.

Working with image files

module: {
  rules: [{test: /\.(png|jpe? g|gif|webp|svg)(\? . *)? $/,
      type: 'asset'.generator: {
        filename: 'static/images/[name].[ext]'
      },
      parser: {
        dataUrlCondition: {
          maxSize: 10 * 1024}}}]}Copy the code

Working with font files

module: {
  rules: [{test: /\.(woff2? |eot|ttf|otf)(\? . *)? $/i,
      type: 'asset'.generator: {
        filename: 'static/fonts/[name].[ext]'
      },
      parser: {
        dataUrlCondition: {
          maxSize: 10 * 1024}}}]}Copy the code

Handle audio and video resources

module: {
  rules: [{test: /.(mp4|webm|ogg|mp3|wav|flac|aac)(\? . *)? $/,
      type: 'asset/resource'.generator: {
        filename: 'static/media/[name].[ext]']}}},Copy the code

The options that

  • generator.filename: Specifies the location and file name of the output file
  • parser.dataUrlCondition.maxSize: Automatically selects module types between resource and inline based on file size and maxSize limits the size, maxSize: 10 * 1024, over 10K does not export a resource’s data URI. The advantage of data URI is that when the size of the image is too small to occupy an HTTP session, using data URI can reduce HTTP requests. However, the image in the form of Data URL is not cached by the browser, so it needs to be configured according to the actual situation of the project.
  1. Create assets/img in the SRC directory and add two images, one smaller than 10K and one larger than 10K. Then run the NPM run build command

  1. In the file generated by dist, only one cat.jpg image is found, while another small image is converted into base64 encoding and typed into main.js. Open dist’s index.html and send the requested image resource in the large image and load the Base64 encoding in the small image.

Using SVG icon

As for the use of some solid-color ICONS in the project, UI gave Ali icon-font ICONS, and decided to use the form of SVG-icon. The best way to do this is to use the SVG-Sprite-Loader configuration document. For the specific use of SVG-Icon and component packaging, you can refer to the hand touch of the Underpants to guide you to use icon gracefully. My processing in the project is also referred to this article.

npm install svg-sprite-loader -D
Copy the code
module: {
  rules: [{test: /\.svg$/,
      loader: 'svg-sprite-loader'.options: {
        symbolId: 'icon-[name]']}}},Copy the code
  • How should the symbolId: ID attribute be named

Note that the SVG folder is excluded from handling static resources with The Asset Module


Use the Typescript

  1. Install dependencies
npm install -D typescript ts-loader
Copy the code
  1. Generate tsconfig. Json
tsc --init
Copy the code

  1. Install the ts – loader
module: {
  rules: [{test: /\.tsx? $/,
      use: [
        {
          loader: 'ts-loader'.options: {
            transpileOnly: true.happyPackMode: true.appendTsSuffixTo: [/\.vue$/]}}]}Copy the code
  1. The options that
  • transpileOnly: With this option, type checking is turned off and build time is shortened when using TS-Loader. Fork-ts-checker-webpack-plugin is used to implement type checking
  • happyPackMode: Use HappyPack or thread loaders to parallelize your build
  • appendTsSuffixTo: [/\.vue$/], add.ts extension name to the vue file to facilitate ts-Loader processing
  1. Install the fork – ts – the checker – webpack – the plugin

To use the fork – ts – the checker – webpack – the plugin. It runs the type checker on a separate process, so the build is still fast because when configuring the TS-Loader we set transpileOnly: true but can still do type checking. In addition, several optimizations have been made to speed up incremental type checking.

npm install -D fork-ts-checker-webpack-plugin
Copy the code
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin')

module.exports = {
  plugins: [
     new ForkTsCheckerWebpackPlugin()
  ]
}
Copy the code
  1. We changed the entry filename main.js to main.ts and changed the webpack configuration
module.exports = {
  entry: pathResolve('src/main.ts')}Copy the code

Vue file declaration cannot be found

This is because to use typescript in a vue project we need to add a new shims-vue.d.ts file in the SRC directory. Now there is no declaration that the vue file cannot be found

// shims-vue.d.ts
declare module '*.vue' {
  import Vue from 'vue'
  export default Vue
}
Copy the code
  1. source map

If you want to debug typescript source code, you need to set up ts-Loader and webpack:

  • First modify the tsconfig.json configuration
{
  "compilerOptions": {
    "sourceMap": true}}Copy the code
  • Second, you need to set the devtool option in webpack.config.js to support the desired type of source mapping.

source map

The packed and compressed code is not visible to ordinary people. If our code fails and the console throws an error at the compressed code location, this…… ; Fortunately, the Source Map maps compiled, packaged, and compressed code back to source code, making it easier to debug errors.

  1. We wrote an error code in the 20th line of app. vue. If the source map is not configured, the error message cannot be accurately located to which line.

  1. Now configure devtool in webpack.dev.js, restart hot update, and the error message can be located exactly which line!
devtool: 'eval-cheap-module-source-map'
Copy the code

  • Source map is usually configured only in the development environment. The eval-cheap-module-source-map is officially recommended. See the official documentation for other functions and differences

Optimized the terminal command line display

When starting the project and packaging the project, the terminal does not display any progress content. We can use Webpack for our built-in ProgressPlugin plug-in, and we can see the real-time progress and the currently running Loader or plugin when starting the project.

Packaging progress prompt

  1. Using ProgressPlugin
const { ProgressPlugin } = require('webpack')

module.exports = {
  plugins: [
    new ProgressPlugin()
  ]
}
Copy the code
  1. Then execute the command line NPM run dev to see the progress information on the console.

Optimize the packaging log display

  1. After a project or package is completed, the packaged resource information is displayed in the console, but generally we don’t need to focus on these, just need to know the success and failure.

Webpack’s Stats option gives you more precise control over how bundle information is displayed

module.exports = {
  stats: 'errors-only'
}
Copy the code
  • errors-only: Outputs information only when an error occurs
  1. NPM run dev, the console output is much reduced, but too succinct to be successful and time-consuming. Come again…

  1. The installation uses the documentation using friendly-errors-webpack-plugin
npm install friendly-errors-webpack-plugin -D
Copy the code
const FriendlyErrorsWebpackPlugin = require('friendly-errors-webpack-plugin')

module.exports = {
  plugins: [
    new FriendlyErrorsWebpackPlugin({
      compilationSuccessInfo: {
        messages: ['You application is running here http://localhost:9000'].notes: ['Some additionnal notes to be displayed unpon successful compilation']}})]}Copy the code
  1. Following the command line NPM run dev, the console output is complete, with information about compile success and time, as well as adding project access addresses and Notes displays.


Copying static files

For some static resource files in the project that don’t need to be processed by Webpack, like favicon.ico on our website, we can just copy the folder and move it to the directory we have packed.

  1. The installation uses copy-webpack-plugin to configure the documentation
npm install copy-webpack-plugin -D
Copy the code
const CopyWebpackPlugin = require("copy-webpack-plugin")

module.exports = {
  plugins: [
    // Copy static files
    new CopyWebpackPlugin({
      patterns: [{from: resolve(__dirname, 'public'),
          to: 'static/public'.toType: 'dir'}]}]}Copy the code
  • The configuration above is to copy the public directory under the project directly to the static directory under the packaged output folder
  1. Create a new public directory under the project directory and place the file favicon.ico

  2. Run the NPM run build command to add the public folder in the generated dist/static directory.


Inject global environment variables

The DefinePlugin plugin built into Webpack replaces variables in your code with other values or expressions at compile time, which we can use to inject global environment variables

  1. Configuration DefinePlugin
const webpack = require('webpack')

module.exports = {
  plugins: [
    new webpack.DefinePlugin({
      NODE_ENV: JSON.stringify(process.env.NODE_ENV)
    })
  ]
}
Copy the code
  1. Print NODE_ENV and process.env.node_env in the entry file main.js
import 'core-js/stable'
import 'regenerator-runtime/runtime'
import Vue from 'vue'
import App from './App.vue'
// import './style/index.css'
// import './style/index.scss'

console.log(NODE_ENV)
console.log(process.env.NODE_ENV)

new Vue({
  render: h= > h(App)
}).$mount('#app')
Copy the code
  1. NODE_ENV is undefined, process.env.node_env is development, Doesn’t setting mode to development set the value of process.env.node_env to development in DefinePlugin? Yes, it did after the Webapck execution. So we’re going to use cross-env to set our environment variables for us. Cross-env is a script that runs cross-platform Settings and uses environment variables.

  1. Installation of cross – env
npm install cross-env -D
Copy the code
  1. Modify the NPM script
"scripts": {
  "dev": "cross-env NODE_ENV=development webpack serve --config ./webpack.config.js"."build": "cross-env NODE_ENV=production webpack --config ./webpack.config.js"
}
Copy the code
  1. Then run the script command NPM run dev, and it will print normally


File fingerprint

After packaging, WebPack will generate bundles based on chunk dependencies and export them to files. Not all files have code changes every front-end release. In this case, we can use the browser cache. The client only requests the files with changes when loading, and the files with no changes go through the cache. In terms of implementation, we can use strong cache for CSS, JS, image files and then add hash value to our packaged files, judging the update of files from the change of hash value. Webpack provides us with three hash methods: Hash, Chunkhash, and Contenthash:

  • Hsah: Relates to the build of the entire project. Whenever the project file is modified, the hash value of the entire project build will change.

  • Chunkhash: Related to chunks packed by Webpack, different entries will generate different chunkhash values.

  • ContentHash: Defines hash based on the content of the file. ContentHash does not change if the content of the file remains unchanged.

Depending on the three hash uses, we typically set chunkhash to export JS files; The CSS in the project is usually isolated from the corresponding CSS file to reference. If we use chunkhash, when we change the CSS code, we will find that the CSS file’s hash value changes as well as the JS file’s hash value, so the CSS file is set to contenthash; Image or font files use hash values. We add the hash value to the configuration:

output: {
  // filename: 'main.js',
  filename: 'static/js/[name].[chunkhash:8].js'.path: pathResolve('dist')} {test: /\.(png|jpe? g|gif|webp|svg)(\? . *)? $/,
  type: 'asset'.generator: {
    // filename: static/images/[name].[ext]
    filename: 'static/images/[name].[hash:8].[ext]'
  },
  parser: {
    dataUrlCondition: {
      maxSize: 10 * 1024}}}Copy the code

After the package command is executed, a string of hash values are found on the file name. In this case, no modification is made to the file and the hash values are not changed.

Now we make a few changes to the main.js entry file and re-execute the package command, at which point the main.js hash has changed.


Clear the dist directory

After we configured the file fingerprint, the hash value changed every time we changed the file and packaged it. Our Dist folder became quite messy and the previous files were no longer used. Therefore, it was recommended to clean up the Dist folder before each build so that only the files used were generated.

Let’s implement this requirement using the output.clean configuration item, which clears the output directory before generating the file. This is also a new configuration in WebPack 5.20.0+. In previous versions, we had to install plug-ins to implement this feature. Now WebPack 5 has this feature built in, just configure it:

module.exports = {
  output: {
    clean: true}};Copy the code

Build configuration pack design

Our actual front-end projects are generally divided into development environment and production environment. The development environment is used to write code and debug functions, while the production environment is packaged and deployed to publish our written code to online servers. Different environments require different features that WebPack provides, so we’ll make a distinction between configuration files here.

  1. webpack.common.js: Common configuration for development and production environments

We create a new Webpack folder in the project directory to hold the WebPack configuration file, create a new webpack.common.js file in the project directory, and move the common configuration from the previous webpack.config.js

const { resolve } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { VueLoaderPlugin } = require('vue-loader')
const { ProgressPlugin } = require('webpack')
const CopyWebpackPlugin = require("copy-webpack-plugin")
const webpack = require('webpack')

module.exports = {
  entry: './src/main.js'.output: {
    filename: 'static/js/[name].[chunkhash:8].js'.path: resolve(__dirname, 'dist'),
    clean: true
  },
  stats: 'errors-only'.module: {
    rules: [{test: /\.m? js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader'}}, {test: /\.tsx? $/,
        use: [
          {
            loader: 'ts-loader'.options: {
              transpileOnly: true.happyPackMode: true.appendTsSuffixTo: [/\.vue$/}}]}, {test: /\.css$/,
        use: [
          'style-loader',
          {
            loader: 'css-loader'.options: {
              sourceMap: false.importLoaders: 2}}, {loader: 'postcss-loader'.options: {
              sourceMap: false}}]}, {test: /\.(sa|sc)ss$/,
        use: [
          'style-loader',
          {
            loader: 'css-loader'.options: {
              sourceMap: false.importLoaders: 2}}, {loader: 'sass-loader'.options: {
              sourceMap: false}}, {loader: 'postcss-loader'.options: {
              sourceMap: false}}]}, {test: /\.(png|jpe? g|gif|webp|svg)(\? . *)? $/,
        type: 'asset'.generator: {
          filename: 'static/images/[name].[hash:8].[ext]'
        },
        parser: {
          dataUrlCondition: {
            maxSize: 10 * 1024}}}, {test: /\.(woff2? |eot|ttf|otf)(\? . *)? $/i,
        type: 'asset'.generator: {
          filename: 'static/fonts/[name].[hash:8].[ext]'
        },
        parser: {
          dataUrlCondition: {
            maxSize: 10 * 1024}}}, {test: /.(mp4|webm|ogg|mp3|wav|flac|aac)(\? . *)? $/,
        type: 'asset/resource'.generator: {
          filename: 'static/media/[name].[hash:8].[ext]'}}, {test: /\.vue$/,
        loader: 'vue-loader'
      },
      {
        test: /\.svg$/,
        loader: 'svg-sprite-loader'.options: {
          symbolId: 'icon-[name]'}}},],plugins: [
    new HtmlWebpackPlugin({
      title: 'retail-admin'.filename: 'index.html'.template: 'index.html'.inject: true.scriptLoading: 'defer'
    }),
    new ForkTsCheckerWebpackPlugin(),
    // Be sure to introduce this plugin!
    new VueLoaderPlugin(),
    new ProgressPlugin(),
    // Copy static files
    new CopyWebpackPlugin({
      patterns: [{from: resolve(__dirname, 'public'),
          to: 'static/public'.toType: 'dir'}}),new webpack.DefinePlugin({
      NODE_ENV: JSON.stringify(process.env.NODE_ENV)
    })
  ]
}
Copy the code
  1. webpack.dev.js: Development environment WebPack configuration

Create a new webpack.dev.js file and move the configuration of the development environment from webpack.config.js and set the mode of the development environment to development. We also need to merge the common configuration with the developed configuration using the merge function provided by WebPack.

const { merge } = require('webpack-merge')
const common = require('./webpack.common.js')
const { resolve } = require('path')
const FriendlyErrorsWebpackPlugin = require('friendly-errors-webpack-plugin')

module.exports = merge(common, {
  mode: 'development'.devtool: 'eval-cheap-module-source-map'.devServer: {
    hot: true.client: {
      overlay: true
    },
    static: {
      directory: resolve(__dirname, 'public')},compress: true.host: 'localhost'.allowedHosts: ['oms-test.hd123.com'].port: 9000.open: true.proxy: [{context: ['/test13'.'/v1'].target: 'https://oms-test-merchant.hd123.com/ui/test13'.changeOrigin: true}].headers: { 'Access-Control-Allow-Origin': The '*'}},plugins: [
    new FriendlyErrorsWebpackPlugin({
      compilationSuccessInfo: {
        messages: ['You application is running here http://localhost:9000'].notes: ['Some additionnal notes to be displayed unpon successful compilation']}})]})Copy the code
  1. webpack.prod.js: WebPack configuration in the production environment

Create a new webpack.dev.js file and move the production environment configuration from webpack.config.js and set the production mode to production

const { merge } = require('webpack-merge')
const common = require('./webpack.common.js')

module.exports = merge(common, {
  mode: 'production'
})
Copy the code
  1. Execute the development command and production packaging command after modifying NPM script
"scripts": {
  "dev": "cross-env NODE_ENV=development webpack serve --config ./webpack/webpack.dev.js"."build": "cross-env NODE_ENV=production webpack --config ./webpack/webpack.prod.js"
}
Copy the code

After hot update was enabled, the console reported an error and could not locate the folder ‘E:\learnCode\retail-admin\webpack\public’

The dist directory is placed under the webpack directory… Lost in thought…

Resolve (__dirname, ‘public’). This path points to the public folder in the same directory as the configuration file to be executed. It should point to the public folder under the project root.

This is where we change the direction of the file path and add a pathResolve function

function pathResolve(dir) {
  return resolve(process.cwd(), '. ', dir)
}
Copy the code

Replace all use of resolve(__dirname, “”) with pathResolve()

// entry: './src/main.js',
entry: pathResolve('src/main.js')

// path: resolve(__dirname, 'dist')
path: pathResolve('dist')

new CopyWebpackPlugin({
  patterns: [{// from: resolve(__dirname, 'public'),
      from: pathResolve('public'),
      to: 'static/public'.toType: 'dir'}]})Copy the code

After the modification, run the script command to find no problem

Tip path points to instructions

  • path.resolve(): Resolves a sequence of paths or path fragments into an absolute path.
  • __dirname: always points to the absolute path of the js file being executed
  • process.cwd(): The working directory of the current Node.js process

The end

At this point we have implemented a simple WebPack packaging configuration that allows for local development and production of front-end resource packs. However, this is far from meeting the requirements of our release and production line, and there is still a lot of room for optimization. The next phase is more exciting: Webpack5, from 0, fast, build the company in the background project full analysis — optimization!!

This article demonstrates code addresses