Parsing es6

Parsing ES6 in WebPack requires the help of Babel-Loader:

npm i babel-loader @babel/core --D

Module :{rules:[{test: /\.js$/, loader: 'babel-loader', exclude: /node_modules/, // does not translate the source code of node_modules.},]},Copy the code

The babel-Loader is dependent on Babel and needs to use the Babel configuration file. Babelrc.

Configure the. Babelrc file in the project root directory. Babelrc is a JSON file that configures presets and plugins. Plugins tell Babel which plug-ins to use, and those plug-ins control how the code is transformed; Presets are collections of plugins that contain all the functionality of a plugin.

{"useBuiltIns": [["@babel/preset-env", // convert ES6 syntax {"useBuiltIns": "Usage ",// Polyfill "corejs" based on configured browser compatibility, and the apis you use in your code: "2", "modules": false, // does not transform modules: {"browsers": [" @babel/plugin-transform-runtime"]} [" @babel/plugin-transform-runtime"]} [" @babel/plugin-transform-runtime"]Copy the code

More detailed reading configuration reference to www.cnblogs.com/tugenhua070…

The CSS automatically prefixes

Some Css3 attributes need to be prefixed to fit different browser kernels. There are four main types:

In WebPack, you can use the AutopreFixer plug-in, which automatically completes the CSS3 prefix based on the browser kernel and version. Its use is based on postcsS-Loader, as follows:

npm i postcss-loader autoprefixer --D

//webpack.config.js module:{ rules:[ { test: /\.css$/, use: ['style-loader','css-loader','postcss-loader'] }, { test: /\.less$/, use: ['style-loader','css-loader','postcss-loader','less-loader'] }, } //. Module. Exports = {"plugins": {"autoprefixer": {}}} //package.json(the browserslist field specifies which browser versions are compatible, and the prefix is determined by the browser version) {//... "browserslist": [ "defaults", "not ie < 8", "last 2 versions", "> 1%", "iOS 7", "last 3 iOS versions" ] }Copy the code

File listening and hot updates

File listening automatically reconstructs a new output file when it detects changes in the source code.

There are two ways to enable file listening:

  • Add –watch to the command line
  • Set Watch: true in the Webpack configuration file

You can set the listening parameters in the configuration file as required:

Module. export = {watch: true, // watchOptions are valid only when watch is true watchOptions:{ignored: [' SRC /test/**', 'node_modules/**'], // do not listen to files or folders, support regular match aggregateTimeout: Poll :1000 // ask for file changes every second}}Copy the code

File to monitor polling is principle to determine whether a file last modified time of change, a file modification time at the beginning, to store up the modification time, next time again modify the will and the last modified time, found inconsistencies will not immediately tell the listener, but the cache file modification, waiting for a period of time, If any other changes occur during the waiting period, the list of changes is built together and generated into the bundle file.

While file listening requires a browser refresh after a rebuild, hot updates allow us to see the changes without having to refresh the entire page after a code change. For example, in a form page, we have filled in a lot of form information, and then the code logic is changed. If there is no hot update, the form information that was filled in before will disappear after refreshing the browser page and need to be filled in again. Hot update can avoid this problem. In development environments, hot updates are often used to improve development efficiency.

Hot update configuration:

  • Install webpack dev – server:npm i webpack-dev-server --D
  • webpack.config.js
module.exports = { mode: 'development', entry: "./src/index.js", ... DevServer: {contentBase: '. / dist, hot: true. / / will automatically introduce webpack HotModuleReplacementPlugin}}Copy the code
  • ./src/index.js
If (module.hot){// If this code is not available, the entire page will be refreshed when the file is modified. module.hot.accept() }Copy the code
  • package.json
"scripts": {
    "build": "webpack",
    "dev": "webpack-dev-server"
  },
Copy the code

Any files in the webpack dependency tree (index.html, bundle.js, CopyWebpackPlugin) will be recompiled when updated, but only changes in the bundle.js dependency tree will be hot-updated to the browser. With the mini-CSs-extract-plugin, style changes are not hot updated to the browser.

File fingerprint

A file fingerprint is a string of hashes, usually after the file name, used to identify the file and to manage the version. For example, after the project goes online, if some things need to be modified, only the modified files need to be released. For the unmodified files, users can still use the browser cache to speed up page rendering when accessing them.

There are three types of file fingerprints in WebPack:

  • Hash: Related to the entire project build, all files share the same hash value, any file changes, the project hash value is changed, no caching effect.
  • Chunkhash: It parses dependent files based on different entry files, builds corresponding chunks, and generates corresponding chunkhash values
  • Contenthash: Indicates the hash value generated by the file content. The contenthash value varies according to the content.

Static resource files are usually used to set the file fingerprint. The following are the methods of setting the file fingerprint for several files:

Js: set filename for output, using [chunkhash]

output: {
   path: path.join(__dirname,"/dist"),
   filename: "js/bundle.[chunkhash:6].js"
}
Copy the code

CSS: You need to set the fingerprint only when you extract the CSS into separate files. Use the mini-css-extract-plugin command to extract the CSS into separate files and set the filename of the mini-css-extract-plugin command. Using [Contenthash], you do not need to re-upload the CSS file if you only change js.

new MiniCssExtractPlugin({
    filename: 'css/[name].[contenthash:7].css'
})
Copy the code

Image: Set the name of file-loader or URl-loader, and use [hash]. Unlike the hash built above for the entire project, the hash of media files such as images is generated according to the content of the file. You can also use [Contenthash].

{ test: /\.(png|jpe? g|gif|svg)(\? . *)? $/, loader: 'file-loader', options: { esModule: false, name: 'img/[name].[hash:7].[ext]' } }Copy the code

In the case of hot update, chunkhash or Contenthash cannot be used, an error will be reported, and only hash can be used. Therefore, file fingerprints are generally not set in the development environment, but in the production environment.

Multipage packaging

Imagine such a business scenario, in an app, there are multiple EMBEDDED H5 pages, these pages are not connected, and when we develop these pages, the usual practice is that one app corresponds to one project, instead of building a project for each page. In such a scenario, SPA is not suitable, decoupled between pages, more logical, also avoid resource redundancy, reduce the size of the file. When webPack builds a project like this, it requires a multi-page packaging approach.

Multi-page packaging is based on multi-entry packaging (see juejin. Im /post/686438… ), and then import the packaged JS files into the corresponding HTML. But this is a bit primitive, requiring changes to the WebPack configuration each time a page is added or removed.

A more automated solution is to dynamically retrieve the entries and configure the HTml-Webpack-plugin for each entry to complete automatic injection, as shown below.

Const path = require('path') const glob = require('glob') const glob = require('glob') Const HtmlWebpackPlugin = require('html-webpack-plugin') const resolve = dir => path.join(__dirname, Dir) // Dynamically retrieve multi-page packaged entries, Set corresponding html-webpack-plugin function setMPA(){const Entry = {} const htmlWebpackPlugins = [] const entryFiles = glob.sync(resolve('src/*/index.js')) entryFiles.forEach( entryFile => { const match = entryFile.match(/src\/(.*)\/index\.js/) const pageName = match && match[1] entry[pageName] = entryFile htmlWebpackPlugins.push( new HtmlWebpackPlugin({ template: resolve(`src/${pageName}/index.html`), filename: ${pageName}.html ', chunks: [pageName], // inject: true, }) ) }) return { entry, htmlWebpackPlugins } } let { entry, HtmlWebpackPlugins} = setMPA() module.exports = {// configuration related to multipage packaging entry: entry, output: {path: resolve('dist'), filename: 'js/[name]_[chunkhash:6].js' }, plugins: [ //... other plugins ].concat(htmlWebpackPlugins) }Copy the code

In this way, there are certain requirements for the file directory. You need to create folders according to the page, and each folder includes index.js and index.html

The code segment

Based library files in the project, the change of frequency is low, such as the vue, react, and our business code often change, this time we should reasonable division code, low change frequency of these modules will be moved to the separate files, so that the browser can cache them alone, ensure that each application code changes don’t have to download them again. On the other hand, properly split code can be loaded in parallel using the browser. For example, when multiple pages often share the base library files, it is necessary to package the library files separately to avoid duplication of library files increasing the size of the main business code file, and also to take advantage of the browser cache.

There are also some non-first screen content, but the file size is large, such as a page with multiple tabs, non-first screen Tab is a chart statistics, this part introduces echarts, but this is not the kind of statistics users are particularly concerned about, click rate is not high. It is not desirable to pack in a large library for a feature that is not commonly used by the user, resulting in slow first-screen loading. Using import() to load asynchronously and dynamically reduces the size of the code on the first screen.

In WebPack, code segmentation comes in three ways:

  • Entry configuration: This is achieved through multi-entry packaging and is not commonly used except for multi-page applications.
  • Dynamic loading (load on demand) : The automatic separation of modules loaded with import() into separate packages in your code, often used.
  • Extract common code: Use splitChunks configuration to extract common code, separate base library files, common modules, etc., commonly used.

See multipage packaging above for entry configuration, and the latter two are discussed here.

(1) Import () to realize dynamic loading

Modules referenced by import() become independent chunks. We often see this code in the vue-router configuration:

{
    path: "/poster",
    component: () => import('@/views/poster'),
    name: "poster"
 }
Copy the code

When packaging, the Poster component is packaged as a separate JS, which is loaded when the route is switched to/Poster. Import () is often used to dynamically load tab-toggles or complex popovers.

As described above, the packaged files are named with ids, such as 0.bundle.531194.js and 1.bundle.ddf6C4.js. To self-name the file name you need to write it like this.

//router.js {path: "/poster", component: () => import(/* webpackChunkName: 'poster' */'@/views/poster'), // "poster" } //webpack.config.js output: { path: path.join(__dirname,"/dist"), filename: "Js /[name].[chunkhash:6].js" // If you do not specify [name], the production environment will still use the ID, and the development environment will use the self name. }Copy the code

They are usually self-named in the development environment and named by id in the production environment.

(2) splitChunks configuration

SplitChunks are a built-in plug-in for WebPack4 and are configured by default in the absence of manual configuration.

The module. Exports = {optimization: {splitChunks: {chunks: 'async', / / "initial" | | "all" "async" (default) minSize: MaxSize: 0, // The maximum size of the file, 0 means that the chunks are not limited: MaxAsyncRequests: 5, maxInitialRequests (maxInitialRequests) maxInitialRequests (maxInitialRequests) 3, // When the page is initialized, the maximum number of requests should not be sent at the same time, the default is 3 automaticNameDelimiter: '~', // package filename separator name: True, // The name of the split file, which defaults to true, meaning that the file name is automatically generated. If set to a fixed string then all chunks will be combined into one cacheGroups: {vendors: {test: /[\\/]node_modules[\\/]/, // The regular rule, if it is, the chunk priority is set: -10 // Cache group priority, if a module may be in multiple chunkgroups, here is the priority}, default: {minChunks: 2, priority: -20, reuseExistingChunks: If this chunk contains modules that already exist in another chunk, then a direct reference to the existing chunk will not generate a new}}}}};Copy the code

Chunks of three options: “initial” | | “all” “async” distinction. We built several files like this to simulate the vUE single page application. The code for each page is as follows:

  • sayName.js
export default function sayName(){
    console.log('This is Tom. Nice to meet you')
}
Copy the code
  • home.vue
<template> <div> <p>This is home</p> </div> </template> <script> import sayName from ".. /common/sayName.js" export default { mounted(){ sayName() } } </script>Copy the code
  • poster.vue
<template> <div> This is poster </div> </template> <script> import sayName from ".. /common/sayName.js" export default { mounted(){ sayName() } } </script>Copy the code
  • app.vue
<template>
    <div>
       <router-view />
    </div>
</template>
<script>
export default {}
</script>
Copy the code
  • index.js
import Vue from "vue"
import App from "./App.vue"
import router from './router.js'
new Vue({
    el: '#app',
    router,
    render: h => h(App)
})
Copy the code
  • router/index.js
import Vue from "vue";
import Router from "vue-router";
Vue.use(Router);
const Routes = [
  {
    path: "/",
    component: () => import(/*webpackChunkName: 'home'*/"../views/home.vue"),
    name: "home"
  },
  {
    path: "/poster",
    component: () => import(/*webpackChunkName: 'poster'*/"../views/poster.vue"),
    name: "poster"
  }
];
export default new Router({
  routes: Routes
});
Copy the code
  • webpack.dev.js
module.exports = { mode: 'development', entry: "./src/index.js", output: { path: path.join(__dirname,"/dist"), filename: "[name].js" }, module:{ rules:[ ... { test: /\.vue$/, use: 'vue-loader' } ] }, plugins:[ new VueLoaderPlugin(), new HtmlWebpackPlugin({ template: 'index.html', filename: HTML ', inject: true}), new CleanWebpackPlugin()], optimization: {// default configuration above}}Copy the code

The desired packaging result is :(1) the files in node_modules are infrequently updated and are packaged as a separate package. (2) Home and Poster are packaged separately and loaded on demand according to the route. (3) The update frequency of the public code written by myself is between the library file and the business code, and it is individually packed into a package.

First change minSize: 0 (our test modules are very small, change to 0 to avoid the module size reason); MaxInitialRequests: 6

The default chunks: ‘async’ :Modules introduced synchronously are not packaged. So part (1) is not packaged and is included in main.js. The contents of Part (3) are removed from the default ~ home~poster. Js. There is no part (3) in home.js and poster.

Modify the chunks: ‘initial’,(1) is packaged separately, (3) is not, and both home.js and poster.js contain (3). In the initial mode, common code in the chunk generated by dynamic import is not extracted.

Modify the chunks: ‘all’(1) and (3) are packaged. Therefore, in daily development, chunks are usually set to all.

The chunk name is set in the cacheGroups

cacheGroups: {
     vendors: {
     	  name: "libs",
          test: /[\\/]node_modules[\\/]/, 
          priority: -10 
     },
     default: {
          name: "common",
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true 
    }
}
Copy the code