preface

This article is based on Webpack4 to build vUE scaffolding common knowledge points. Not from 0 to start building, because the online tutorial is very much, only the author over the years to build scaffolding configuration of knowledge points listed, ranked in no particular order, can be used as a mining document query, welcome comments.

How to configure startup service and realize hot update in development environment

After modifying any file in the development directory, rendering the page directly without manually refreshing is the greatest respect to the developer. Principle no what to say, directly on the configuration:

const webpack = require('webpack');
module.exports = {
	// Ignore others
	plugins: [new webpack.HotModuleReplacementPlugin()
	],
	devServer: {historyApiFallback: {index:'/index.html'
		},
		progress:true./ / the progress bar
		inline:true.// Add a webSocket client to the package
	    hot:true./ / thermal load
	    contentBase: path.resolve(__dirname,'.. /src'),	// The root of the development service runtime file
	    host: 'localhost'.// Host address
	    port: 5000./ / the port number
	}
	// Ignore others
}
Copy the code

Second, the introduction ofhtmlThe template

Html-webpack-plugin is a Webpack plug-in that creates HTML entry files. The configuration method of the simple version is as follows:

const HtmlWebpackPlugin 	= require('html-webpack-plugin');
module.exports = {
	// Ignore others
	plugins: [/ /...
		new HtmlWebpackPlugin({
			title:'Home'.filename:'index.html'.template:resolve('.. /public/index.html')})/ /...
	]
	// Ignore others
}
Copy the code

The htML-webpack-plugin can also be used to configure multi-page applications, and interested students can check out more information. For more information about options parameters, see the following:

Title: The title of the HTML file to be generated.filename: The name of the HTML file to be generated. The default is index.html. You can specify subdirectories here (e.g. assets/admin.html)template: Indicates the path of the template. Support loaders, such as HTML! . / index. HTML.inject :true| | 'head' | 'body'false. Inject all output files into the given template or templateContent. When the incomingtrueOr 'body' will place all javascript resources at the bottom of the body element, and 'head' will be placed within the head element.favicon: Gives the icon path to add to the output HTML.minify: {... } |false. Pass an HTML-minifier configuration object to compress the output.hash : true | false. If it istrue, adds a unique Webpack compiled hash value to all contained scripts and CSS. This is useful for cache clearing.cache : true | false. If the incomingtrue(default), only send (emit) the file if the file changes.showErrors : true | false. If the incomingtrue(Default), error messages are written to the HTML page.chunks: Only you are allowed to add chunks. (ex: Only unit test blocks)chunksSortMode: You can control the sorting of chunks before they are inserted into HTML. Allowed values' none '|' auto '|' dependency '| {function} Defaults to 'auto'.excludeChunks: Allows you to skip somechunks(For example, don't unit testchunk).xhtml: used for generationHTMLTitle of the file.title : true | false. If it istrue,linkTags render as self-closing tags,XHTMLThat's what we're gonna do. The defaultfalse.Copy the code

Three,distfolder

Now, in order to prevent caching, single-page applications usually change the file name after each build package. In this case, multiple similar files with the same function will be generated in the dist folder after each package. Over time, more and more files will be generated. In this case, we need to clear the directory before each packing. The clean-webpack-plugin is recommended

const {CleanWebpackPlugin} = require('clean-webpack-plugin');
module.exports = {
	// Ignore others
	plugins: [new CleanWebpackPlugin()
	]
	// Ignore others
}
Copy the code

Each time you pack, you empty the Dist folder first. At this point, the question arises: why is there no configuration in the New CleanWebpackPlugin? How does it know that it needs to clear the dist folder? What if the file directory after my project build is the build folder? In fact, there is a configuration, document address, but generally not used, our purpose is to clear the directory after packaging. The clean-webpack-plugin dynamically reads the output directory every time we build and cleans up the files in it.

Iv. CDN subcontracting loading method

Generally, common class libraries will be published on THE CDN. When we package in Webpack, we actually do not need to package public class libraries into our own projects, because this will not only increase the file size, affect the loading speed of the network line from client to server, but also affect the efficiency of Webpack packaging. Add-asset-html-cdn-webpack-plugin is required to load resources in CDN mode. Let’s take VUE as an example:

const AddAssetHtmlCdnPlugin = require("add-asset-html-cdn-webpack-plugin");
module.exports = {
	// Ignore others
	plugins: [new AddAssetHtmlCdnPlugin(true, {
      		'vue': "https://lib.baomitu.com/vue/2.6.12/vue.min.js",})],externals: {
    	'vue': 'Vue'
  	},
	// Ignore others
}
Copy the code

After the above configuration, when we perform the following import in the project, webPack will ignore the import of VUE and import from CDN instead.

import Vue from 'vue';
Copy the code

Note: the imported CDN library must expose the Vue in the global scope, otherwise the Vue will not be found after the build error.

Vue -router Cannot GET/XXX when using history mode

Error cause: Resources cannot be found. When setting up local development services, we usually use webpack-dev-server to set up services. After starting the service, the home page can be accessed normally, for example, http://localhost:5000/ page can be displayed normally. Click demohttp routing to jump to page: / / localhost: 5000 / demo access is normal also, this time I’m afraid if you refresh the page will be prompted to always GET/demo. Why is that? Since the demo directory does not actually exist on the server, we need to redirect the non-existing directory to index.html. The key configuration is as follows:

module.exports = {
	// Ignore others
	devServer: {/ /...
		historyApiFallback: {index:'/index.html'
		}
		/ /...
	}
	// Ignore others
}
Copy the code

Copy the contents of the folder to the packing directory

In the era of WebPack compilation and packaging, there will inevitably be some files that need to be imported without packaging, such as Icon Font. In this case, you only need to copy the specified file contents to the dist directory during build. The copy-webpack-plugin is designed to solve this problem by not only copying content, but also converting it dynamically. The following configuration means that the contents of the public folder are copied to the dist folder, but the PNG files in the test folder are not copied, and the HTML files are not copied

const CopyWebpackPlugin     = require("copy-webpack-plugin");
module.exports = {
	// Ignore others
	plugins: [new CopyWebpackPlugin(
			[
				{
					from:'./public'.to:'/',}, {ignore: ['test/*.png'.'*.html'"})"// Ignore others
}
Copy the code

Seven, setLoaderA search scope

Webpack, mainly rely on various Loader to convert code into strings to generate AST, and then continue to transform AST and finally regenerate new code. The larger the project, the more code needs to be converted, so the efficiency is lower. So we need to specify which places to convert and which places to ignore in the project. Obviously node_modules has a lot of libraries that are compiled, so there’s no need to deal with them again, just put the focus of the business code in the SRC folder: Just deal with SRC folders and ignore node_modules, let’s take Babel as an example

module.exports = {
	// Ignore others
	module: {rules: [/ /...
			{
				test: /\.js$/,
				include: path.resolve('src'),		// Only the SRC folder is processed
				exclude: /node_modules/.// Ignore the node_modules folder
				use: ['babel-loader']}/ /...]}// Ignore others
}
Copy the code

Eight, support,SASSTo query the specific location of the original stylesourceMap

Note: Common problems with this configuration are related to loader versions. If the configuration is correct but the error cannot be resolved, check whether the version of style-loader, CSS-loader, and sas-loader is too high. Lower the version.

module.exports = {
	// Ignore others
	module: {rules: [/ /...
			{
				test:/\.scss$/,
				include:resolve('.. /src'),
				exclude:/node_modules/,
				use:[
					{ loader:'style-loader'}, {loader:'css-loader'.options: {sourceMap:true}}, {loader:'sass-loader'.options: {sourceMap:true]}}},/ /...]}// Ignore others
}
Copy the code

Note: sourceMap is mainly used for debugging code. For performance reasons, set sourceMap to false in production.

Nine, positioningjsPrint specific locationsourceMap

Sometimes we need to know which file the source code is in and where it is when the page reports an error, prints a warning, or console.log. In this case, we need to turn on sourceMap locating of JS

module.exports = {
	// Ignore others
	devtool: 'eval-source-map'
	// Ignore others
}
Copy the code

Note: sourceMap is mainly used for debugging code, and devtool is set to None in production for performance reasons.

Introduction of picture PIC and font fonts configuration

Very simple, nothing to say, directly on the configuration. Images smaller than 10KB are packaged directly into Base64, and images larger than 10KB are imported as a path.

module.exports = {
	module: {rules:[
			{
            	test: /\.(png|jpe? g|gif|svg)(\? . *)? $/,
            	use:[
            		{
            			loader: 'url-loader'.options: {esModule:false.limit:10*1024.name:'static/images/[name]-[hash:15].[ext]'}}]}, {test: /\.(eot|woff|woff2? |ttf|svg)$/,
                use: [
                	{
                    	loader: "url-loader".options: {
                      		name: "static/fonts/[name]-[hash:15].[ext]".limit: 5*1024,}}]}]}}Copy the code

11. Js compression and asynchronous loading method subcontracting strategy of Webpackage 4

The compression configuration of JS files in Webpack4 is very convenient. In the production environment, that is, when mode value is production and compression is performed in package.json, webpack -p can realize file compression.

1.require.ensureway

Like this scenario: Testa. js and testb. js are introduced in index.js, and these two JS functions have no impact on the first screen loading, that is, they are only used when some operations need to be performed. However, when these two js functions are large, they are suitable for asynchronous loading, because they can reduce the volume of the first screen loading file. Without further ado, let’s get right to the code

{
	methods: {alertA(){
			require.ensure([],() = > {
				// Load asynchronously
				let testA = require('./testA.js');
				console.log(testA)
			})
		},
		alertB(){
			require.ensure([],() = > {
				// Load asynchronously
				let testB = require('./testB.js');
				console.log(testB)
			})
		},
	}
}
Copy the code

When you package this way, you’ll find two more files. The reason for this is that Webpack packs testa.js and testb.js separately, so you can use them only when you need them, which is obviously a better experience.

2.import(xxx).then()way

Import () is a subcontract cutting method that was only enabled in Webpackage 4. The syntax is relatively simple, accepting only the reference address of a package as an argument, using a Promise callback that executes logic after a successful load.

{
	methods: {alertA(){
			// Load asynchronously
			import('./testA.js').then(res= > {
				console.log(res)
			})
		},
		alertB(){
			// Load asynchronously
			import('./testB.js').then(res= > {
				console.log(res)
			})
		},
	}
}
Copy the code

The question is: how do you control where these asynchronously loaded JS are packaged and named? In fact, both require. Ensure and import(XXX) subcontracting are eventually placed in the directory of chunk storage. Static /js/

output:{
	chunkFilename:'static/js/[name].[chunkhash:10].chunk.min.js'
}
Copy the code

3.vue-routersub-contracting

In fact, this is not a new way of subcontracting, at most the classical application of the above two ways, is also one of the common application cases. The main change is the import mode of vue-Router configuration Component. Examples of the two methods are as follows:

export default new Router({
    mode: 'history'.routes: [{path: '/'.name: 'Index'.component: () = > import('@/pages/index/index.vue')}, {path: '/list'.name: 'List'.component: resolve= > require(['@/pages/list/list.vue'],resolve)
        },
    ]
})
Copy the code

4.webpack4The sub-contract strategy

The above two asynchronous subcontracting methods are often used in business classes to facilitate subcontracting at any time, and generally, a single file is small. But in addition, the public libraries introduced by project packaging, such as Vue, VUe-Router, Lodash, Echart, etc., are often packed into a public JS, which will lead to large files, long loading time of the first screen, and long white screen time. At this time, it is very necessary to subload these dependent libraries. First of all, the subcontracting loading method of CDN is a good way, which has been mentioned above and ignored here. The second is to specify which libraries together to truly realize the personalized optimization of arbitrary subcontracting.

4.1 CommonsChunkPluginThe sub-contract strategy

This approach was used prior to webpack4.x, but since this article is based on webpack4 configuration, this approach will be briefly skipped

module.exports = {
	// Ignore others
	plugins: [
		new webpack.optimize.CommonsChunkPlugin({
	    	name: 'vendor'.minChunks: function (module, count) {
	      		return (
	        		module.resource && /\.js$/.test(module.resource) && module.resource.indexOf(path.join(__dirname, './node_modules'= = =))0)}}),new webpack.optimize.CommonsChunkPlugin({
	    	name: 'common'.chunks: 'initial'.minChunks: 2,}),]// Ignore others
}
Copy the code

4.2 splitChunksThe sub-contract strategy

Webpack4 removes CommonsChunkPlugin and uses splitChunksPlugin. This content is the core content of Webpack4 subcontract, the next will use a whole article analysis, here temporarily skipped, please look forward to.

Twelve,sassImport global registration variable files

Preprocessors like SASS and LESS greatly facilitate writing CSS variables. We can define a file, define variables in it, and import other files. In the case of Sass, define the generic style file mixins.scss and import each sass file via @import. If one day the common style name becomes base.scss, then we have dozens of pages in the project to manually modify, painful!! Can sass files be imported globally? Yes, try the sas-Resources-loader plug-in. After all, loader parsing works from back to front, and base dependencies need to run first

module.exports = {
	module: {rules:[
			{
				test:/\.scss$/,
				include:resolve('.. /src'),
				exclude:/node_modules/,
				use:[
					// Ignore others for now
					{
						loader: 'sass-resources-loader'.options: {
		                	sourceMap:process.env.NODE_ENV==='production'?false:true.resources: [resolve('.. /src/static/css/mixins.scss']}}]}}Copy the code

We note that Resources is an array (or a string, which is a generic style path introduced directly). The reason we use arrays is that we can configure multiple generic styles. This is the sASS example. The same principle applies to less configuration. Interested students can try style-resources-loader

CSS subcontract, compress, and remove comments

Note:

  • 1. This method can only be used in production environment
  • 2. Note the introduction of plug-in version numbers

The plugins mini-CSS-extract -plugin, optimize- CSS-assets-webpack-plugin, cssnano are configured as follows

const MiniCssExtractPlugin  = require('mini-css-extract-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
module.exports = {
	module: {rules:[
			{
				test:/\.scss$/,
				include:resolve('.. /src'),
				exclude:/node_modules/,
				use:[
					MiniCssExtractPlugin.loader
				]
			},
			{
				test:/\.less$/,
				include:resolve('.. /src'),
				exclude:/node_modules/,
				use:[
					MiniCssExtractPlugin.loader
				]
			},
			{
				test:/\.css$/,
				include: resolve('.. /src'),
				exclude: /node_modules/,
				use:[
					MiniCssExtractPlugin.loader
				]
			}
		]
	},
	plugins: [new OptimizeCSSAssetsPlugin({
	    	assetNameRegExp:/\.css$/g,
	    	cssProcessor:require("cssnano"),		// Introduce the CSsnano configuration compression option
	       	cssProcessorPluginOptions: {preset: ['default', {discardComments: {removeAll:true}}},canPrint:true.// Whether to print the plug-in information to the console
	    }),
	    new MiniCssExtractPlugin({
        	filename: "static/css/[name].[chunkhash:10].css",]}})Copy the code

Version number see the final source egg.

Fourteen, the solution can not be resolvedAsync... awaitAnd other advanced grammar problems

When your Chrome browser reports this error, it must mean that your business code is using some advanced version of JS, such as this error

ReferenceError: regeneratorRuntime is not defined
Copy the code

This is where you need babel-Polyfill to install and introduce the re-restart service

npm install babel-polyfill -save-dev

//webpack entry file, such as' main.js'
import "babel-polyfill"
Copy the code

Open Tree Shaking

Tree-shaking literally translates to shaking a Tree. In autumn, when we shake trees, unused leaves fall to the ground, just as there are dozens of methods written in our code util.js. There is no need to pack methods that are not used for Tree shaking. In the old days of Webpack4, tree-shaking is enabled by default when mode is set to Production. For example: the main js

import { A } from './util';
console.log(A())
Copy the code

util.js

export const A  = () = > 'aaa';
export const B  = () = > 'bbb';
Copy the code

The following code will not be included in the package

export const B  = () = > 'bbb';
Copy the code

The ES6 Module introduces static analysis, and its modules are printed at compile time. A static analysis flow can determine which modules or variables are not being used or output.

Sixteen, start the Scope collieries

Another thing that will also be enabled by default in Webpackage 4 when mode is set to Production is Scope reactive. Webpack is a way to make webpack JS smaller and faster. Initially, when webpack is packaged, a layer of functions will be wrapped around the packaged module, and import will be converted into require, thus generating a large number of scopes. The more modules there are, the more memory will be occupied. In this case, if Scope collieries is enabled, Webpack will dynamically analyze the dependency relation between modules, combine some modules into a function, so as to reduce the number of packages, and rename some variables to prevent conflicts.

Install package analysis tools

To analyze the size of a packed file and what was imported into the file before an analysis tool was available, it was common to manually look in the Dist directory to see which files were large and which JS that might be imported could be separated. Time-consuming and laborious, and accuracy is not high. The webpack-bundle-Analyzer plugin is recommended to help analyze site quality and performance. The use mode is relatively simple

const BundleAnalyzerPlugin  = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
	// Ignore others
	plugins: [new BundleAnalyzerPlugin({
            generateStatsFile: true.// Whether to generate stats.json file})]// Ignore others
}
Copy the code

Here is the simplest configuration method, more configurations can be viewedwebpack-bundle-analyzerThe document.

By default, another service will be opened after packaging. The page should look like this:The analysis is easy to see, which files are large and which libraries are packaged within the files, or which libraries are not changed. Then it is for specific project specific analysis, as to how to subcontract, can see abovewebpack4The sub-contract strategyOr,CDN subcontracting loading method. Subcontracting details are different for different projectsTeach a man to fish.

Measurement and analysis of packaging speed

For some large projects, although we have made a lot of packaging optimization, they still take a certain amount of time. As for which place takes more time, it is difficult to analyze by experience alone. At this time, we need a plug-in to analyze the packaging speed ———— speed-measure-webpack-plugin. The method of use is simple:

const SpeedMeasureWebpackPlugin = require("speed-measure-webpack-plugin");
const seepM = new SpeedMeasureWebpackPlugin();
module.exports = seepM.wrap({
	entry:' '.output:' '.module: {},plugins:[]
})
Copy the code

It’s easy to use: just import and implement the package speed plugin and pack the configuration content in seemp.wrap.

In the 19th,DllPluginPre-package specific class libraries into dynamic links

DllPlugin can pre-package a particular class library into a dynamically linked library, which is suitable for libraries or function modules that are not updated frequently. Because every time after updating the business logic code, they have to be brought along when packaging, which greatly affects the packaging speed. If you can pack these libraries away, you can use them when you need them. Manually update only when the code base is updated. Configure file generation dependencies in package.json ahead of time:

{
	"scripts": {"dll": "webpack --config ./config/webpack.dll.js --mode production"}}Copy the code

The corresponding webpack.dll. Js configuration:

const path = require("path");
const DllPlugin = require("webpack/lib/DllPlugin");
module.exports = {
    // The class library that you want to pack uniformly
    entry: ["vue-router"."bignumber.js"."axios"."vant"."js-md5"].output: {
        filename: "[name].dll.js".[name] indicates the name of the current dynamic link library
        path: path.resolve(__dirname, "dll"), // Put the output files in the DLL directory
        library: "_dll_[name]".// The name of the global variable that stores the dynamically linked library, for example, _dll_vant
    },
    plugins: [
        new DllPlugin({
            // The global variable name of the dynamically linked library needs to be the same as that in output.library
            // The value of this field is the value of the name field in the output manifest.json file
            // For example, manifest.json has "name": "_dll_vant"
            name: "_dll_[name]".// The name of the manifest.json file that describes the output of the dynamic link library
            path: path.join(__dirname, "dll"."[name].manifest.json"),})]};Copy the code

Add dependency files to your project:

const DllReferencePlugin    = require("webpack/lib/DllReferencePlugin");
module.exports = {
	// Ignore others
	plugins: [/ /...
		new DllReferencePlugin({
      		// Manifest is the json file that was packaged earlier
      		manifest: path.join(__dirname, ".. /dll"."main.manifest.json"),}),/ /...
	]
	// Ignore others
}
Copy the code

Twenty, noParse

To speed up packaging, a production environment can set noParse to tell WebPack to filter specified files without parsing them. So what conditional libraries can noParse use? A plug-in or framework that runs directly in the browser without special processing, or that has been compiled, can be used directly. For example, jquery and LoDash can be filtered directly

module.exports = {
	// Ignore others
        module: {noParse:/jquery|lodash/.// Or support function types (e.g., ignore all parsing in lib folders)
            noParse:function(fullPath){
		return /lib/.test(fullPath)
            }
        }
	// Ignore others
}
Copy the code

21, IgnorePlugin

IgnorePlugin prevents import or require calls from ignoring content that meets regular expression conditions. For example, the moment library supports multiple languages, but we only use the Chinese language package, the rest can be ignored to reduce the size of the package.

module.exports = {
	// Ignore others
	plugins: [	
		new webpack.IgnorePlugin(/^\.\/locale/./moment$/)].// Ignore others
};
Copy the code

22. Other small configuration optimization points

  • 1, you can use alias, manually specify the mapping location, reducewebpackSearch scope;
  • 2, when importing components or JS libraries, we usually do not carry suffixes. We can set the file search suffixes list, put the common ones in front.
module.exports = {
	// Ignore others
	resolve: {extensions: ['.js'.'.vue'.'.scss'.'.css'.'.json'].alias: {'vue': 'vue/dist/vue.esm.js'.The '@':path.resolve(__dirname,'.. /src')}}// Ignore others
}
Copy the code

23. CachingloaderThe processing results

Add cache-loader to the last part of the use array, which can cache the processing results. In this way, as long as the file content does not change, the last cached results can be used in the next packaging, which greatly saves time.

Of course, the results of babel-Loader compilation can also be cached, which can greatly improve the packaging time.

module.exports = {
	// Ignore others
	module: {rules:[
			{
				test: /\.js$/,
				use: ['cache-loader'.'babel-loader? cacheDirectory=true']},]}// Ignore others
}
Copy the code

Note: Using cache-loader to cache results is not recommended for content that is relatively easy to compile. Because compilation itself takes less time, there is no need to waste time saving and reading the cache.

24. Enable multi-threaded packaging

Since webpack is a single-threaded process, many Loader tasks queue up for each package. When a compilation takes a long time, others have to queue up, resulting in a longer packaging time. If multiple Loaders can work together, the packaging process will be greatly shortened.

25,webpack-dashboardThe dashboard

In a local development environment, the information printed on the console is presented as a list, which is often not intuitive or aesthetically pleasing.webpack-dashboardBasically translated as “dashboard,” it allows the console to display content in multiple areas. The upper left corner ofwebpackThe status, compilation time, and compilation progress bars are displayed on the right side of the log. The following are the imported modules, file sizes, and error messages. The screenshot information is as follows:Usage:

	// Step 1: Install
	npm install webpack-dashboard -D
	// Step 2: Import and use
	const DashboardPlugin = require('webpack-dashboard/plugin');
	{
		// Ignore it for now
		plugins: [new DashboardPlugin()
		]
		// Ignore it for now
	}
	// Step 3: Change the package.json startup mode and append "webpack-Dashboard --" to the previous startup mode.
	{
		"scripts": {"serve":"webpack-dashboard -- webpack-dev-server"}}Copy the code

Summary: Common thorny problems are summarized

  • 1. Version problems.webpackA large number of third-party plug-ins may be introduced into a configuration project. After a major upgrade, the configuration API will be modified. After confirming that the CONFIGURATION API is correct, uninstall the plug-in and retry after lowering the version.
  • 2. The import fails due to a path problem. Recommended when reading import filespath.resolve(__dirname,".. /src")Mode to avoid import failures caused by relative paths.
  • 3. Js and CSS compression only applies to production environments.
  • 4. Load in the development environmentcssBe sure to use itstyle-loaderDo not compress orlinkOtherwise, hot updates will fail and the development experience will be adversely affected.
  • 5. CSS elimination of redundant styles, a very good idea, but the author has not figured it out yetpurifycss-webpackPlugins remove redundant CSS principles. For example, the dynamic addition of CSS styles, how to do not accidentally killed, is now known to be mistakenly killed, understand the welcome comment discussion.