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.

WebPack can be viewed as a module baler: What it does is analyze the structure of your project, find JavaScript modules and other extension languages that browsers don’t run directly (Scss, TypeScript, etc.), and package them into a format that browsers can use.


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 <! - each page only VUE instance, tied 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 installCopy the code

Go into development mode

$ npm run startCopy 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 buildCopy 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'); const devMode = process.env.NODE_ENV ! =='production'; / / / / need to be packaged entrance files array array element type {string | object} / / string: will with the default rules generated bundle / / object {filename | in the | template} Generated by the bundle. The HTML file name | | title tag content under the path/public template file (specified file suffix) const entryList = [' page1 ', 'page2,]; /** * @param {array} entryList * @param {object} option: Optional, 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 = { // ... Optimization :{runtimeChunk:{name:'./js/runtime'} splitChunks:{ //cacheGroups inherits this value minSize:30000, cacheGroups:{//vue main:{test: /[\\/]node_modules[\\/]vue[\\/]/, name: '. / js/main ', chunks: 'all'}, / / in addition to the Vue other framework vendors: {test: / / \ \ / node_modules / \ \ /? ! (vue)[\\/]/, name: '. / js/vendors' chunks: 'all'}, / / the reusable js extractedJS business: {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 // ... 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 //... Plugins :[new CleanWebpackPlugin('build'), 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:

/ /... 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).

Personal blog

More front-end technology articles, art design, wordpress plug-ins, optimization tutorials, learning notes in my personal blog Meorong – describe life with you, welcome to exchange and learn together, progress together: http://panmiaorong.top

Article recommendation:

Here are some of the top tips for interviewing in 2018

Super easy to use development tools, programmer utility collation

Collect quality Chinese front-end blogs (updated irregularly)

In-game Search (laravel Scout + ElasticSearch)

Vue Axios encapsulation and API interface management

Webpack Engineer packages vUE front-end multi-page projects with Webpack 4