preface

Recently, I just used vuE-CLI to create a VUE project, which is not familiar with the principle of packaging, here to record. My level is limited, in line with the spirit of sharing happiness theme, I hope to provide you with some reference, if there is a wrong place, I hope you don’t hesitate to correct.

Webpack

To understand how the VUE development environment is packaged, let’s first take a look at how WebPack works

Initialize the

Select [email protected] and [email protected]

$NPM init -y $NPM I [email protected] [email protected] -dCopy the code

test

The package.json file adds code

"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev": "webpack"
},
Copy the code

Create a./ SRC /index.js file in the project root directory

console.log("Hello Webpack !!!" );Copy the code

Execute the command

$ npm run dev
Copy the code

You’ll find./dist/index.js files in the root directory. Here’s a little bit about why no configuration works. Because starting with WebPack V4.0.0, you don’t have to import a configuration file. The default configuration for Webpack: Entry points to./ SRC and output points to./dist.

Initial configuration of WebPack

Create webpack.config.js in the project root directory

const path = require("path");
module.exports = {
    entry: "./src/index.js",
    output: {
        path: path.resolve(__dirname, "dist"),
        filename: "bundle.js"
    },
    mode: "development"
}
Copy the code

In order to better observe the internal structure of bundle.js, let’s choose webPack development mode first. Then execute the command NPM run dev to get an uncompressed bundle.js file

Bundle. Js analyses

With the bundle.js file generated above, we first empty out the code and end up with a self-executing function

(function(modules) {}) ({});
Copy the code

As you can see above, the argument passed to the self-executing function is an object

(function(modules) {}) ({ "./src/index.js": (function(module, exports) { eval("console.log('Hello Webpack !!! '); \n\n//# sourceURL=webpack:///./src/index.js?" ); })});Copy the code

Execute the first entry module

(function(modules) { function __webpack_require__(moduleId) {... } return __webpack_require__(__webpack_require__.s = "./src/index.js"); }) ({ "./src/index.js": (function(module, exports) { eval("console.log('Hello Webpack !!! '); \n\n//# sourceURL=webpack:///./src/index.js?" ); })});Copy the code

The function has an internal __webpack_require__(moduleId) module loading function, which is used to cache modules and execute module functions.

(function(modules) { var installedModules = {}; Function __webpack_require__(moduleId) {// If (installedModules[moduleId]) {return when other modules reference the cached module installedModules[moduleId].exports; Var Module = installedModules[moduleId] = {I: moduleId, l: false, exports: {}}; // Execute module functions // use call to ensure that this in each module refers to the module itself // Pass the webpack_require__ function to provide the ability to load other dependent modules modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); // export module return module.exports; }... }) ({});Copy the code

/ SRC /home.js file to get a better idea of how module functions work

console.log("It`s home.js !!!" );Copy the code

Update index.js to make home.js its dependency

import './home'; console.log('Hello Webpack !!! ');Copy the code

Execute command to obtain

(function(modules) { function __webpack_require__(moduleId) { ... Modules [moduleId]. Call (module.exports, module, module.exports, __webpack_require__); . } // Do compatibility with module loading functions... __webpack_require__.r = function(exports) {... } __webpack_require__.n =function(module) {... }... }) ({ "./src/home.js": (function(module, exports) { eval("console.log(\"It`s home.js !!! \ "); \n\n//# sourceURL=webpack:///./src/home.js?" ); }), "./src/index.js": (function(module, __webpack_exports__, __webpack_require__) { "use strict"; eval(" __webpack_require__.r(__webpack_exports__); Var _home__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("./ SRC /home.js "); var _home__WEBPACK_IMPORTED_MODULE_0___default = __webpack_require__.n(_home__WEBPACK_IMPORTED_MODULE_0__); console.log('Hello Webpack !!! '); "); })});Copy the code

The __webpack_require__ function is used when executing eval, indicating that the current module index.js is dependent on the other module home.js. After executing the dependency home.js, go back to index.js and process the rest of the code.

The general flow is similar to the Onion ring model of the Nodejs Koa framework or recursive function calls

__webpack_require__(./src/index.js) ------------------------------------------------ // index.js __webpack_require__(./src/home.js) |------------------------------------ | | // home.js | | console.log("It`s home.js !!!!!!!!!" ); | |-------------------------------------| console.log("Hello Webpack !!!" ) -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --Copy the code

The final output is

It`s home.js !!!
Hello Webpack !!!
Copy the code

configuration

This is just a glimpse of WebPack, and let’s start with some basic configuration.

First, let’s configure a basic package project. Create webPack profiles for production and development environments. To make the project look reasonable, let’s create a few configuration files and put them in the./build file:

  • Webpack.base.config.js is a common configuration for both development and production environments
const path = require('path'); module.exports = { entry: { bundle: path.resolve(__dirname, '.. /src/main.js') }, output: { path: path.resolve(__dirname, '.. /dist'), filename: '[name].js' }, module: { rules:[] }, plugins: [ new HtmlWebpackPlugin({ template: path.resolve(__dirname, '../public/index.html') }) ] };Copy the code
  • Webpack.dev.config.js development environment configuration
const merge = require('webpack-merge'); const path = require('path'); const baseConfig = require('./webpack.base.config'); module.exports = merge(baseConfig, { mode: 'development', devtool: "cheap-module-eval-source-map", devServer: { contentBase: path.resolve(__dirname, '.. /dist'), open: true } });Copy the code
  • Webpack.prod.config.js generates the environment configuration
const merge = require('webpack-merge');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const baseConfig = require('./webpack.base.config');

module.exports = merge(baseConfig, {
  mode: 'production',
  devtool: 'none',
  plugins: [
    new CleanWebpackPlugin()
  ]
});
Copy the code

Note that the above configuration contains webPack plug-in dependencies

$ npm i -D webpack-merge@4 clean-webpack-plugin webpack-dev-server html-webpack-plugin@4
Copy the code

/ SRC /index.js to create./public/index.html file./ SRC /main.js

Modify the package.json command configuration

"scripts": {
    "dev": "webpack-dev-server --inline --progress --config ./build/webpack.dev.conf.js",
    "build": "webpack --progress --color --config ./build/webpack.prod.conf.js"
},
Copy the code

Execute the command

$ npm run dev
$ npm run build
Copy the code

Tip: If something goes wrong during the runtime, it’s mostly because of the package version

Resources to deal with

We all know that Webpack is a JS packaging tool (meaning that it can only deal with javaScript code), but the actual project is not only JS, at this time need to cooperate with loader and plugin to help deal with non-JS resources.

Loader is used to convert the module source code. Loader allows you to preprocess files when importing or “loading” modules. As such, Loader is similar to “tasks” in other build tools and provides a powerful way to handle the front-end build steps. Loader can convert files from different languages (such as TypeScript) to JavaScript, or convert inline images to data urls. Loader even allows you to import CSS files directly into JavaScript modules!

Here we briefly understand several common loader:

JS processing

babel-loader

Babel is a JavaScript compiler designed to convert code written in ECMAScript 2015+ syntax into backwardly compatible JavaScript syntax so that it can run in current and older versions of browsers or other environments.

Configure the browser version package.json

"browserslist": [
    "last 2 versions",
    "> 1%",
    "not ie <= 8"
]
Copy the code

Babel-loader is just a communication bridge between Webpack and Babel, and won’t do the conversion of ES6 to ES5, it’s handed over to @babel/preset-env for installation

$ npm i babel-loader @babel/core @babel/preset-env -D
Copy the code

Create the. Babelrc file in the project root directory

{
    "presets": [
        [
            "@babel/preset-env"
        ]
    ]
}
Copy the code

New object in the module.rules array in webpack.base.config.js

{
    test: /\.js$/,
    exclude: /node_modules/,
    use: {
        loader: "babel-loader"
    }
}
Copy the code

To test this, modify the contents of the main.js file

// Syntax
let a = 10;
const fn = () => {};
let mapData = ['1','2'].map(item => item);
class Test1 {}
class Test2 {}

// Api
const c = [1, 2, 3].includes(1);
new Promise(() => {});
Copy the code

The result of packing is

function _classCallCheck(instance, Constructor) { if (! (instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } // Syntax var a = 10; var fn = function fn() {}; var mapData = ['1', '2'].map(function (item) { return item; }); var Test1 = function Test1() { _classCallCheck(this, Test1); }; var Test2 = function Test2() { _classCallCheck(this, Test2); }; // Api var c = [1, 2, 3].includes(1); new Promise(function () {});Copy the code

From the above comment, you can see that the code under Syntax is converted, but the code under Api is not changed. Why? When translating, Babel breaks the source code into Syntax and Api:

  • Syntax: similar to expand objects, Optional Chaining, let, const, etc
  • Api: functions and methods like [1,2,3]. Includes

Babel’s translated code will report an error if it runs in a browser that does not support Api methods such as includes and Promise. To convert API methods such as includes and Promise, you need to use @babel/ Polyfill to add all the new features of ES to make up for the missing features in earlier versions of the browser. The installation

$ npm install --save @babel/polyfill
Copy the code

Then add the code at the top of main.js

import "@babel/polyfill";
Copy the code

After adding new feature dependencies, package

__webpack_require__.r(__webpack_exports__); /* harmony import */ var _babel_polyfill__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @babel/polyfill */ "./node_modules/@babel/polyfill/lib/index.js"); /* harmony import */ var _babel_polyfill__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_babel_polyfill__WEBPACK_IMPORTED_MODULE_0__); function _classCallCheck(instance, Constructor) { if (! (instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } // syntax var a = 10; var fn = function fn() {}; var mapData = ['1', '2'].map(function (item) { return item; }); var Test1 = function Test1() { _classCallCheck(this, Test1); }; var Test2 = function Test2() { _classCallCheck(this, Test2); }; // api var c = [1, 2, 3].includes(1); new Promise(function () {});Copy the code

The package size is 911 KIb (in development mode). As you can see, Babel injects all polyfill features by default, which results in a very large package size, and we only need includes and Promise methods here. So we need to load the Polyfill feature on demand to reduce the volume of packaging.

Since @babel/ Polyfill was deprecated in 7.4.0, we recommend directly adding core-js and setting the version via the corejs option.

Since Babel deprecated @babel/ Polyfill after version 7.4.0, Babel suggested that we use core-js directly and set the version of Corejs to replace polyfill. Delete import “@babel/polyfill” from index.js; And install the core-js@3 dependency

$ npm install -S core-js@3
Copy the code

Modify bablerc.

{"presets": [["@babel/preset-env", {"corejs": 3, // New version needs to specify the core library version "useBuiltIns": "usage" // on demand}]]}Copy the code

After packaging

__webpack_require__.r(__webpack_exports__); /* harmony import */ var core_js_modules_es_array_map_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! core-js/modules/es.array.map.js */ "./node_modules/core-js/modules/es.array.map.js"); /* harmony import */ var core_js_modules_es_array_map_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(core_js_modules_es_array_map_js__WEBPACK_IMPORTED_MODULE_0__); /* harmony import */ var core_js_modules_es_array_includes_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! core-js/modules/es.array.includes.js */ "./node_modules/core-js/modules/es.array.includes.js"); /* harmony import */ var core_js_modules_es_array_includes_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(core_js_modules_es_array_includes_js__WEBPACK_IMPORTED_MODULE_1__); /* harmony import */ var core_js_modules_es_object_to_string_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! core-js/modules/es.object.to-string.js */ "./node_modules/core-js/modules/es.object.to-string.js"); /* harmony import */ var core_js_modules_es_object_to_string_js__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(core_js_modules_es_object_to_string_js__WEBPACK_IMPORTED_MODULE_2__); /* harmony import */ var core_js_modules_es_promise_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! core-js/modules/es.promise.js */ "./node_modules/core-js/modules/es.promise.js"); /* harmony import */ var core_js_modules_es_promise_js__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(core_js_modules_es_promise_js__WEBPACK_IMPORTED_MODULE_3__); function _classCallCheck(instance, Constructor) { if (! (instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj ! == Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } // Syntax var a = 10; _typeof(a); var fn = function fn() {}; var mapData = ['1', '2'].map(function (item) { return item; }); var Test1 = function Test1() { _classCallCheck(this, Test1); }; var Test2 = function Test2() { _classCallCheck(this, Test2); }; // Api var c = [1, 2, 3].includes(1); new Promise(function () {});Copy the code

Packaged code volume is 263 kib (in Development mode)

On-demand injection is fine. So far, there are two problems with the above Babel configuration:

  1. For such asincludesSuch instance methods, directly inglobal.Array.prototypeTo add. This direct modification of the global variable prototype can lead to unexpected problems. This issue is especially important when developing third-party libraries that modify global variables and may conflict with another third-party library that also modified global variables, or with users of our third-party library. Generally accepted programming paradigms also discourage direct modification of global variables and global variable prototypes.
  2. When Babel interprets syntax, it sometimes uses auxiliary functions to help. In class syntax, Babel is custom_classCallCheckThis function to assist; Typeof is rewritten and customized_typeofThis function to assist. These functions are called helpers. As you can see from the above code, the helper is defined directly in the translated file. If there are 100 files in a project, and each file writes a class, 100 of them will be in the final package of the project_classCallCheckFunctions, they look and function exactly the same, which obviously doesn’t make sense.

The @babel/ plugin-transform-Runtime plugin solves both of these problems.

$ npm install --save-dev @babel/plugin-transform-runtime
$ npm install -D @babel/runtime-corejs3
Copy the code

Modify the.bablerc file

{
	"presets": [
        [
            "@babel/preset-env"
        ]
    ],
    "plugins": [
        [
            "@babel/plugin-transform-runtime",
            {
                "corejs": 3
            }
        ]
    ]
}
Copy the code

After packaging

__webpack_require__.r(__webpack_exports__); /* harmony import */ var _babel_runtime_corejs3_helpers_classCallCheck__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @babel/runtime-corejs3/helpers/classCallCheck */ "./node_modules/@babel/runtime-corejs3/helpers/classCallCheck.js"); /* harmony import */ var _babel_runtime_corejs3_helpers_classCallCheck__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_babel_runtime_corejs3_helpers_classCallCheck__WEBPACK_IMPORTED_MODULE_0__); /* harmony import */ var _babel_runtime_corejs3_core_js_stable_instance_map__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @babel/runtime-corejs3/core-js-stable/instance/map */ "./node_modules/@babel/runtime-corejs3/core-js-stable/instance/map.js"); /* harmony import */ var _babel_runtime_corejs3_core_js_stable_instance_map__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_babel_runtime_corejs3_core_js_stable_instance_map__WEBPACK_IMPORTED_MODULE_1__); /* harmony import */ var _babel_runtime_corejs3_core_js_stable_instance_includes__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @babel/runtime-corejs3/core-js-stable/instance/includes */ "./node_modules/@babel/runtime-corejs3/core-js-stable/instance/includes.js"); /* harmony import */ var _babel_runtime_corejs3_core_js_stable_instance_includes__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_babel_runtime_corejs3_core_js_stable_instance_includes__WEBPACK_IMPORTED_MODULE_2__) ; /* harmony import */ var _babel_runtime_corejs3_core_js_stable_promise__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! @babel/runtime-corejs3/core-js-stable/promise */ "./node_modules/@babel/runtime-corejs3/core-js-stable/promise.js"); /* harmony import */ var _babel_runtime_corejs3_core_js_stable_promise__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(_babel_runtime_corejs3_core_js_stable_promise__WEBPACK_IMPORTED_MODULE_3__); var _context, _context2; // Syntax var a = 10; var fn = function fn() {}; var mapData = _babel_runtime_corejs3_core_js_stable_instance_map__WEBPACK_IMPORTED_MODULE_1___default()(_context = ['1',  '2']).call(_context, function (item) { return item; }); var Test1 = function Test1() { _babel_runtime_corejs3_helpers_classCallCheck__WEBPACK_IMPORTED_MODULE_0___default()(this, Test1); }; var Test2 = function Test2() { _babel_runtime_corejs3_helpers_classCallCheck__WEBPACK_IMPORTED_MODULE_0___default()(this, Test2); }; // Api var c = _babel_runtime_corejs3_core_js_stable_instance_includes__WEBPACK_IMPORTED_MODULE_2___default()(_context2 = [1, 2, 3]).call(_context2, 1); new _babel_runtime_corejs3_core_js_stable_promise__WEBPACK_IMPORTED_MODULE_3___default.a(function () {});Copy the code

After introducing transform-Runtime (in development mode) :

  • The Api was changed from a direct modification prototype to a unified module, avoiding contamination of global variables and their prototypes, solving the first problem
  • Helpers were changed from the previous definition in place to a unified module, so that only one helper exists for each packaged result, which solves the second problem

Style to deal with

Css-loader handles @import and URL (), just as JS interprets import/require(), and then analyzes dependencies between CSS modules.

$ npm install style-loader css-loader -D
Copy the code

New object in the module.rules array in webpack.base.conf.js

{
    test: /\.css$/,
    use: ["style-loader", "css-loader"]
}
Copy the code

Let’s do some optimization here

  • The use of plug-inmini-css-extract-pluginPull the style into a style file
$ npm install mini-css-extract-plugin -D
Copy the code
  • usePostCssPrefixes CSS rules with specific browser vendors.AutoprefixerAutomatically gets the browser’s popularity and supported properties, and automatically prefixes CSS rules based on that data.
$ npm install postcss-loader@4 autoprefixer@9 cssnano@4 -D
Copy the code

Create postcss.config.js in the project root directory

module.exports = {
    plugins: [
        require("autoprefixer"),
        require("cssnano"),
    ]
};
Copy the code

Modify the webpack.base.config.js file

const MiniCssExtractPlugin = require('mini-css-extract-plugin'); module.exports = { module: { rules:[ { test: /\.css$/, use: [// CSS is not mounted directly to the page, but separated into a single CSS file, . By this time there is no need for style - loader MiniCssExtractPlugin loader, 'CSS - loader', 'postcss - loader'],},]}, plugins: [new MiniCssExtractPlugin({filename: '[name].css', // output filename}),]};Copy the code

Modify the main js

import "./index.css"
Copy the code

Create. / SRC/index. The CSS

div {
    display: flex;
}
Copy the code

You get bundle.css

div{display:-webkit-box; display:-ms-flexbox; display:flex}Copy the code

File resource processing (images, audio, video, fonts, etc.)

File-loader resolves import/require() in a local resource file to a URL, sends the file to the output folder, and modifies the path referenced by the file according to the configuration so that the resource can be loaded correctly during running.

The installation

$ npm install file-loader -D
Copy the code

Module. rules for webpack.base. Config added

{ test: /\.(png|jpe? G | | GIF webp | SVG | eot | the vera.ttf | woff | woff2) $/, use: [{loader: 'file - loader' options: {outputPath: 'assets', / / the output file directory name: "[name] _ [hash: 6] [ext]" / / output filename},}], exclude/node_modules /} :Copy the code

Modify/main. Js

import photo from './photo.jpg';
const img = new Image();
img.src = photo;
document.body.appendChild(img);
Copy the code

You end up putting the processed images in the./dist/assets directory

Url-loader is similar to file-loader except that when the file size is smaller than the specified limit, it will return the file encoding DataURL and put it directly into the main file, without output of the real file. If a page is loaded with too many image resources, it will send a lot of HTTP requests, which reduces the performance of the page. In this case, you can directly put the resources in the main file, which can reduce the expensive network requests.

Note: If the size of the image is smaller than the set value, it can be converted into Base64 code instead of directly outputting the file. However, if the limit is set properly, it will cause the JS file to become larger (mainly, the volume increased after the file encoding is borne by the JS file) and the loading will be slow. Therefore, the loading speed and network request times should be taken into account. If you want to use image compression, you can use image-webpack-loader.

The installation

$ npm install url-loader -D
Copy the code

Webpack. Base. Modified config module. Rules

{test: / \. (eot | the vera.ttf | woff | woff2) $/, use: [{loader: 'file - loader' options: {outputPath: 'assets', / / the output file directory name: "[name] _ [hash: 6] [ext]" / / output filename},}], exclude: / node_modules /}, {test: / \. (PNG |, jpe." G | | GIF webp | $/ SVG), use: [{loader: 'url - loader' options: {limit: 10240, / / less than the value of the file will be converted to DataURL outputPath: 'assets', / / the output file directory name: "[name] _ [hash: 6] [ext]" / / output filename},}], exclude/node_modules /} :Copy the code

Here we do a little processing, because font resources don’t need encoding, so file-loader is used to process them. Then let’s include matching video and audio.

{ test: /\.(eot|ttf|otf|woff2?) (\? \S*)? $/, use: [ { loader: 'file-loader', options: { outputPath: 'assets', }, } ], exclude: /node_modules/ }, { test: /\.(png|jpe? g|gif|webp|svg)(\? . *)? $/, use: [ { loader: "url-loader", options: { limit: 10240, name: "[name]_[hash:6].[ext]", outputPath: "assets", }, }, ], exclude: /node_modules/ }, { test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\? . *)? $/, use: [ { loader: "url-loader", options: { limit: 4096, name: "[name]_[hash:6].[ext]", outputPath: "assets", publicPath: (url, resourcePath, context) => { return `../wp-content/plugins/vue-create-post-type/assets/${url}` } }, }, ], exclude: /node_modules/, },Copy the code

To optimize the

HMR (Hot Moudle Replacement)

The HMR-Hot Module replacement function replaces, adds, or removes modules while the application is running without reloading the entire page. Modify devserver.hot in webpack.dev.config.js

devServer: { contentBase: path.resolve(__dirname, '.. /dist'), open: true, hot: true }Copy the code

SplitChunksPlugin

Generally, project code contains its own code and third-party code. Generally, webPack will pack these two codes into a bundle file, and the browser will request the bundle file through HTTP. If the file is too large, the loading time will be too long, which affects the loading time of the first screen of the website. We all know that browsers have a concurrent request mechanism, so if you split a large bundle into multiple chunks and let the browser load it in parallel, you can just do it.

CommonsChunkPlugin is a plugin used to extract third-party libraries and common modules to avoid repeated dependencies. If the bundle file is too large, it will be subpacked and some non-own code will be pulled out of the bundle file to reduce the size of the bundle file and improve the loading speed. However, the CommonsChunkPlugin has another problem that it can’t deal with, which is that when multiple entries are made, a third party block is repeatedly referenced, and the third party block is packaged multiple times.

SplitChunksPlugin not only inherits the CommonsChunkPlugin functionality, but also supports caching of duplicate modules to avoid repeated packaging.

Since version 4 the CommonsChunkPlugin was removed in favor of optimization.splitChunks and optimization.runtimeChunk options. Here is how the new flow works.

$ npm install terser-webpack-plugin@4 --save-dev
$ npm install css-minimizer-webpack-plugin --save-dev
Copy the code

Webpack. Base. Add the config module. The optimization

const TerserPlugin = require("terser-webpack-plugin"); . // Optimization: {splitChunks: {vendors: {vendors: {name: 'chunk-vendors', test: /[\\/]node_modules[\\/]/, priority: -10, chunks: 'initial' }, common: { name: 'chunk-common', minChunks: 2, priority: -20, chunks: 'initial', reuseExistingChunk: true } } }, minimizer: [ new TerserPlugin( { parallel: true, extractComments: false, terserOptions: { compress: { arrows: false, collapse_vars: false, comparisons: false, computed_props: false, hoist_funs: false, hoist_props: false, hoist_vars: false, inline: false, loops: false, negate_iife: false, properties: false, reduce_funcs: false, reduce_vars: false, switches: false, toplevel: false, typeofs: false, booleans: true, if_return: true, sequences: true, unused: true, conditionals: true, dead_code: true, evaluate: true }, safari10: true } } ) ] },Copy the code

Webpack. Dev. Add the config module. The optimization

const CssMinimizerPlugin = require('css-minimizer-webpack-plugin'); . Optimization: {minimizer: [new CssMinimizerPlugin(), // production environment]}Copy the code

Now that you’re ready to package your project with Webpack, build Vue.

Vue

The installation

You should install vue-Loader with vue-template-compiler — unless you are an advanced user of your own fork version of the VUE template compiler:

$ npm install -D vue-loader vue-template-compiler vue-style-css
Copy the code

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.

configuration

Webpack. Base. Add the config module. Rules

Export.exports = {module: {rules: [//... export.exports = {rules: [//... export.exports = {rules: [//... /\.vue$/, loader: 'vue-loader' }, { test: /\.css$/, use: NODE_ENV!== 'production'? 'vue-style-loader' : MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader' ], }, ] }, plugins: [// Please make sure to introduce this plugin! New VueLoaderPlugin()]}Copy the code

test

Install the vue

$ npm install vue -S
Copy the code

./src/main.js

import Vue from 'vue'
import App from './view/App.vue'

new Vue({
    el: "#app",
    render: (h) => h(App)
})
Copy the code

./view/App.vue

<template>
    <div>
        Hello Vue!
    </div> 
</template>
Copy the code

conclusion

At this point, the basic Vue packaging environment is complete, some configuration is not explained to clear, because MY explanation is not well documented, people still switch to Webpack. During configuration, installation package errors are mostly caused by version problems. Some dependent packages are used together. There are many webPack configuration items, but not all of them need to be configured because WebPack already has a default configuration to help handle them. The most important thing is to read documents, read documents, read documents, especially English documents.