I have been using scaffolding before, but this time I set up the webpack front end project by myself. I spent a lot of thought, so I made a summary.

Use 1.

The project structure is as follows:

project |- bulid <! - this directory are automatically generated - > | - public | | - CSS - js | - page1. HTML <! - plug-in generated HTML file - > | - page2. HTML <! -- HTML file generated by the plugin -->... |- public/ <! - store fonts, pictures, web templates and other static resources - > | - SRC <! - source folder - > | - components / / | | - CSS - js / | - page1. Js <! -- unique VUE instance per page, bound to#app-->|- page2.js <! -- unique VUE instance per page, bound to#app-->. |- package.json |- package-lock.json |- README.mdCopy the code

The public folder holds some static files, and the SRC folder holds source code. Each page passes through an entry file (page1.js, page2.js,..). Generate a vUE instance and mount it to the #app element of the HTML file generated by the plug-in.

Install dependencies

$ npm install
Copy the code

Go into development mode

$ npm run start
Copy the code

The browser opens http://localhost:3000 and the page goes blank with the words cannot get. Don’t panic, add /page1. HTML to the URL and press Enter to see our page. This is because I set the home page of the development server to index.html, whereas in this case the pages are page1.html,page2.html, so it will be blank.

Development is complete, build the production version:

$ npm run build
Copy the code

This will result in a build/ folder with optimized files from which the server responds to resources.

2. Introduction

2.1 Basic WebPack Configuration

Our development is divided into production and development environments, so we need to have two webPack configuration files (you might want to use env environment variables, and then use the 3-mesh operator to return different values depending on the value of env). However, this method does not work in webPack export module properties, I have tried ~~~). Here we split it into three files, of which webpack.common.js is the general configuration and is used in both environments, while webpack.dev.js and webpack.prod.js are the specific configurations in both environments. The webpack-merge package is used here to combine common and specific configurations.

  • webpack.common.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const VueLoaderPlugin = require('vue-loader/lib/plugin');
constdevMode = process.env.NODE_ENV ! = ='production';

// An array of entry files needs to be packaged
/ / array element type {string | object}
// string: The bundle will be generated using the default rules
/ / object {filename | in the | template} generated bundle. The HTML file name | | title tag content under the path/public template file (suffix specified file)
const entryList = [
    'page1'.'page2',];/** * @param {array} entryList * @param {object} option: Optional manual configuration */
const createEntry = (list = [], option = {}) = > {
    const obj = {};
    list.forEach((item) = > {
        const name = item.filename ? `./js/${item.filename}` : `./js/${item}`;
        obj[name] = path.resolve(__dirname, './src'.`. /${item}.js`);
    });
    return Object.assign(obj, option);
};


module.exports = {
    entry: createEntry(entryList),
    output: {
        path: path.resolve(__dirname, './build'),},module: {
        rules: [{test: /\.js$/.exclude: /(node_modules|bower_components)/.use: {
                    loader: 'babel-loader'.options: {
                        presets: ['@babel/preset-env'],},},}, {test: /\.vue$/.use: 'vue-loader'}, {test: /\.(woff|woff2|eot|ttf|otf)$/.use: {
                    loader: 'file-loader'.options: {
                        name: 'public/fonts/[name].[ext]',},},}, {test: /\.(png|svg|jpg|gif)$/.use: {
                    loader: 'file-loader'.options: {
                        name: 'public/images/[name].[ext]',},},},],},plugins: createPluginInstance(entryList).concat([
        Vue SFCs single file support
        new VueLoaderPlugin(),
    ]),
};

Copy the code

The CSS file is not configured here, because it needs to be optimized and extracted in the production environment, so it is configured in the other two files.

  • webpack.dev.js
const webpack = require('webpack');
const path = require('path');
const merge = require('webpack-merge');
const common = require('./webpack.common.js');

module.exports = merge(common, {
    mode: 'development'.devtool: 'inline-source-map'.output: {
        filename: '[name].js'.chunkFilename: '[name].js',},module: {
        rules: [{test: /\.(css|less)$/.use: [
                    'vue-style-loader'.'css-loader'.'postcss-loader'.'less-loader'],},],},resolve: { alias: { vue: 'vue/dist/vue.js'}}});Copy the code

Vue is divided into development and production versions, and the last line specifies which version to use based on the path.

  • webpack.prod.js
const webpack = require('webpack');
const merge = require('webpack-merge');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const UglifyJsPlugin = require("uglifyjs-webpack-plugin");
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
const common = require('./webpack.common.js');

module.exports = merge(common, {
    mode: 'production'.output: {
        filename: '[name].[contenthash].js'.chunkFilename: '[name].[contenthash].js',},resolve: { alias: { vue: 'vue/dist/vue.min.js'}},module: {
        rules: [{test: /\.(css|less)$/.use: [
                    MiniCssExtractPlugin.loader, 
                    'css-loader'.'postcss-loader'.'less-loader'],},],},Copy the code

In the production environment, we used hashes to facilitate caching, as will be the case when we add other resources to the production environment.

2.2 Solving the problem of file Output directory

We expect the build folder to have the following structure:

build
  |- css/
  |- js/
  |- page1.html
  |- page2.html
  ...
Copy the code

That is, files are grouped by type. HTML files are placed directly in this directory, but the output from our configuration above is mixed together. Since the name attribute can be either a file name or a file name with a path, such as /dir/a, we make some changes based on this feature.

  • Change the output path of output directly

Such as build/js, other resources use relative paths such as.. /page1.html is modified. I did this from the start, but it ended up making the development server unable to respond to file changes because it could only listen for files in the output directory, not files in that directory.

  • Modifying an entry Name

This is our final solution. Change the original file name page1 to /js/page1, and the final output JS files will be placed in the JS folder. In production we extract CSS from the JS file using the MiniCssExtractPlugin. This is the configuration of the plugin:

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

The name is the name of the first entry here, under the influence of the entry name change, it will eventually become a js/page1.131 de8553ft82, CSS, and the placeholder [name] only effective at compile time, which means that can’t use the function to deal with this value. So instead of using the [name] placeholder for the desired purpose, you simply use [id].

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

3. Code segmentation

In webpack4, segmentation is done using optimization.splitchunks.

//webpack.common.js
const path = require('path');

module.exports = {
   / /... Leave out the rest
    optimization:{
        runtimeChunk: {name:'./js/runtime'
        },
        splitChunks: {// To avoid excessive segmentation, set the size to at least 30KB
            //cacheGroups inherits this value
            minSize:30000.cacheGroups: {// VUE related framework
                main:{
                    test: /[\\/]node_modules[\\/]vue[\\/]/.name: './js/main'.chunks:'all'
                },
                // Other frameworks besides Vue
                vendors:{
                    test:/[\\/]node_modules[\\/]? ! (vue)[\\/]/.name: './js/vendors'.chunks:'all'
                },
                // Business reusable JS
                extractedJS:{
                    test:/[\\/]src[\\/].+\.js$/.name:'./js/extractedJS'.chunks:'all'}}}}};Copy the code

RuntimeChunk contains some webapck boilerplate files that allow you to package without changing the contents of the source file. The hash value still changes, so we’ll separate it out and learn more here. CacheGroups used to extract reusable module, the test will attempt to match (module absolute path | | module name), the return value is true and meet the conditions of module will be split. The conditions can be customized, such as the minimum size of the module and the minimum number of chunks to be imported (i.e. the number of reuse times). By default, modules are segmented only when they are at least 30KB before packaging.

4. The tree shaking

Add it to package.json

"sideEffects": ["*.css"."*.less"."*.sass"]
Copy the code

Files outside this array will be affected by tree jitter — unused code will be culled from the export export object. This will greatly reduce useless code. If tree jitter has a side effect on some files, put those file names in the array to skip this operation. CSS files (including.less,.sass) must be included, otherwise styles will be lost.

5. Use of plug-ins

5.1 the clean – webpack – the plugin

After each package, new files are generated, which may lead to the accumulation of useless old files. It is too troublesome to delete these useless files one by one, the plug-in will automatically clean up before each package. In practice, we didn’t want to clean up the files generated by the build command in the development environment, so we only used this plug-in in the production environment.

5.2 HTML – Webpack – the plugin

There are no HTML files in our source directory, so we use this plugin to generate multiple HTML files after packaging.

//webpack.common.js
/ /... Omit anything that has already appeared above

// One instance of the plugin is required per HTML
// Generate HTML files in batches
const createPluginInstance = (list = []) = > (
    list.map((item) = > {
        return new HtmlWebpackPlugin({
            filename: item.filename ? `${item.filename}.html` : `${item}.html`.template: item.template ? `./public/${item.template}` :             './public/template.html'.title: item.title ? item.title : item,
            chunks: [
                `./js/${item.filename ? item.filename : item}`.'./js/extractedJS'.'./js/vendors'.'./js/main'.'./js/runtime'.'./css/styles.css',
                devMode ? './css/[id].css' : './css/[id].[contenthash].css',]}); }));Copy the code

The default is to pack all the entry files, code split files into an HTML file, specifying chunks to tell the plug-in which chunks to include, or exludeChunks specifying chunks not to include. There is a slight problem here, we can’t get the file to contain exactly the blocks it needs. If you want to exclude chunks that are not used, you have to manually configure them as required. A batch file generated with this function will always contain all the common packages.

5.3 the mini – CSS – extract – the plugin (prooduction)

This plug-in is used to extract CSS from a JS file into a separate CSS file.

//webpack.prod.js
/ /... Leave out the rest
plugins:[
        new CleanWebpackPlugin('build'), 
        / / CSS
        new MiniCssExtractPlugin({
            filename:'./css/[id].[contenthash].css'
        }),
        // Optimize the cache
        new webpack.HashedModuleIdsPlugin()
    ]   
Copy the code

Optimize – CSS – Assets -webpack-plugin (Production)

To simplify packaged CSS code, set it in the Minimizer property of the Optimization configuration, which overrides the WebPack default, so also set js minimizer (here we use uglifyPlugin) :

optimization: {
        minimizer: [new UglifyJsPlugin({
            cache: true.parallel: true
          }),
          new OptimizeCSSAssetsPlugin()
        ]
    }
Copy the code

6. Development server and hot module replacement

Add the following to webpack.dev.js:

/ /... Leave out the rest
devServer:{
        index:'index.html'.hot:true.contentBase:path.resolve(__dirname,'./build'),
        port:3000.noInfo:true
    },
plugins: [new webpack.HotModuleReplacementPlugin()
    ]
Copy the code

Using the development server, we can refresh the source files automatically after we make changes, because we are keeping the data in memory, so the build folder on the hard disk is not affected. Hot module replacement also needs to be modified in the source file. We also configured the dynamic import syntax accordingly.

7. Other

Public is used to store static resources. After packaging, a folder of the same name will be created under build/ to store the resources that public will use. If you reference a public resource such as an image in a.css file, use an absolute path when adding urls:

<! -- src/css/page1.css --> .bg-img { background-image:url(/public/images/1.jpg) }Copy the code

This works when opened over HTTP/HTTPS, but if opened as a file (such as double-clicking page1.html after packaging), the browser will not be able to find the resource. You can use both absolute and relative paths by importing images as variable references (import name from path).

Are there any mistakes, welcome to correct, if you like, okgive a like.