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 ofhtml
The 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,dist
folder
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, setLoader
A 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,SASS
To 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, positioningjs
Print 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.ensure
way
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-router
sub-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.webpack4
The 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 CommonsChunkPlugin
The 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 splitChunks
The 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,sass
Import 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... await
And 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-analyzer
The 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 abovewebpack4
The 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,DllPlugin
Pre-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, reduce
webpack
Search 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. Cachingloader
The 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-dashboard
The 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-dashboard
Basically translated as “dashboard,” it allows the console to display content in multiple areas. The upper left corner ofwebpack
The 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.
webpack
A 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 files
path.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 environment
css
Be sure to use itstyle-loader
Do not compress orlink
Otherwise, 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 yet
purifycss-webpack
Plugins 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.