This article is about 2000 words, it takes about 20 minutes to read it and an hour to try it out

Some time ago, when I did a project, the technology stack was VUE + Webpack, mainly the homepage of the official website and the background management system. According to the situation at that time, I analyzed three schemes

  1. Two SPA applications (official website and background system) are embedded in one project code.
  2. Separate two sets of project source code
  3. A set of project source code on a SPA application

Think about:

  1. Direct denial of a SPA application in a set of project source code (UI styles will cover each other, if no code specification is difficult to maintain later)
  2. Two sets of source code, the background may open two ports, and then need to use nGINx reverse proxy may be more troublesome, and front-end development is more troublesome, after all, need to maintain two Git repositories, two sets of Git online process, may take a lot of time.
  3. I’m (blind) confident in my own technology, and I want to try new things, so it’s not too complicated to figure out what needs to be done. Selected the first solution, which is multiple single page application in a set of source code

The previous multi-page structure diagram

Download the Vue SPA template

npm install vue-cli -g
vue init webpack multiple-vue-amazing
Copy the code

Retrofit multi-page applications

npm install glob --save-dev
Copy the code

Change the directory structure under the SRC folder

/ * this was part of the add -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- start * / / / glob is webpack installation relying on a third-party modules, Var glob = require(var glob = require(var glob = require(var glob = require())'glob'Var HtmlWebpackPlugin = require()'html-webpack-plugin'Var PAGE_PATH = path.resolve(__dirname, __dirname)'.. /src/pages'Var merge = require() var merge = require()'webpack-merge') // Multi-entry configuration // The glob module reads all the js suffix files in the corresponding folder of the Pages folder, if the file exists // then processes exports.entries =function () {
    var entryFiles = glob.sync(PAGE_PATH + '/*/*.js')
    var map = {}
    entryFiles.forEach((filePath) => {
        var filename = filePath.substring(filePath.lastIndexOf('/ /') + 1, filePath.lastIndexOf('. '))
        map[filename] = filePath
    })
    return// Read the corresponding HTML suffix from the pages folder and put it in the arrayexport.htmlplugin =function () {
    let entryHtml = glob.sync(PAGE_PATH + '/*/*.html')
    let arr = []
    entryHtml.forEach((filePath) => {
        let filename = filePath.substring(filePath.lastIndexOf('/ /') + 1, filePath.lastIndexOf('. '))
        letConf = {// template source template: filePath, // filename filename: filename +'.html'// The page template needs to be added with the corresponding JS script chunks. If you do not add this line, each page will be added with all the JS script chunks: ['manifest'.'vendor', filename],
            inject: true
        }
        if (process.env.NODE_ENV === 'production') {
            conf = merge(conf, {
                minify: {
                    removeComments: true,
                    collapseWhitespace: true,
                    removeAttributeQuotes: true
                },
                chunksSortMode: 'dependency'
            })
        }
        arr.push(new HtmlWebpackPlugin(conf))
    })
    returnArr} / * here is to add some -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - * / endCopy the code

Webpack. Base. Conf. Js file

/ * modify part -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - start * / entry: utils. Entries (), / * modify part -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - * / endCopy the code

Webpack. Dev. Conf. Js file

This area / * comment file -- -- -- -- -- -- -- -- -- -- -- -- -- start * / / / new HtmlWebpackPlugin ({/ / filename:'index.html',
    //   template: 'index.html',
    //   inject: true/ /}), This area / * comment file -- -- -- -- -- -- -- -- -- -- -- -- -- end * / new FriendlyErrorsPlugin () / * add concat (utils. HtmlPlugin ()) -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - * / ].concat(utils.htmlPlugin())Copy the code

Webpack. Prod. Conf. Js file

/ * comment the contents of this area -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - * / / / new HtmlWebpackPlugin ({/ / filename: config. Build. The index, / / the template:'index.html',
    //   inject: true,
    //   minify: {
    //     removeComments: true,
    //     collapseWhitespace: true,
    //     removeAttributeQuotes: true
    //     // more options:
    //     // https://github.com/kangax/html-minifier#options-quick-reference
    //   },
    //   // necessary to consistently work with multiple chunks via CommonsChunkPlugin
    //   chunksSortMode: 'dependency'/ /}), / * comment the contents of this area -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- end * / / / copy the custom static assets new CopyWebpackPlugin ([{the from: path.resolve(__dirname,'.. /static'),
        to: config.build.assetsSubDirectory,
        ignore: ['*']}]) / * this location add. Concat (utils. HtmlPlugin ()) -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - * /] concat (utils. HtmlPlugin ())Copy the code

Introduce third-party UI libraries

npm install element-ui bootstrap-vue --save
Copy the code

Introduce different UI index.js for different pages

import BootstrapVue from 'bootstrap-vue'
Vue.use(BootstrapVue)
Copy the code

admin.js

import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
Vue.use(ElementUI)
Copy the code

The above multi-page configuration is a reference to the Internet, and most of the ideas on the Internet are very similar, the core is to change multiple entries, after the configuration is completed, the development can not find the problem, and then about a month of development, after the development of the official website performance analysis found, The webpack-packed Vvendor. Js network loading time is very long, resulting in a very long white screen time of the first screen. Finally, the conclusion is obtained through -webpack-bundle-Analyzer analysis

npm run build --report
Copy the code

solution

Since the loading speed is slow because the vendor is too large, it is good to separate this vendor. I think like this: extract the third-party code used in each page into vendor.js, and then package the third-party code used in each page into their respective vendor-x.js, such as the existing pages index.html and admin.html. Js, vendor-index.js, and vendor-admin.js will be packaged.

Webpack. Prod. Conf. Js file

    new webpack.optimize.CommonsChunkPlugin({
      name: 'vendor-admin',
      chunks: ['vendor'],
      minChunks: function (module, count) {
        return (
          module.resource &&
          /\.js$/.test(module.resource) &&
          module.resource.indexOf(path.join(__dirname, '.. /node_modules')) === 0 &&
          module.resource.indexOf('element-ui') != -1
        )
      }
    }),
    new webpack.optimize.CommonsChunkPlugin({
      name: 'vendor-index',
      chunks: ['vendor'],
      minChunks: function (module, count) {
        return (
          module.resource &&
          /\.js$/.test(module.resource) &&
          module.resource.indexOf(path.join(__dirname, '.. /node_modules')) === 0 &&
          module.resource.indexOf('bootstrap-vue')! = -1)}})Copy the code

Again, everything is fine. Vendor. js is separated into vendor.js, vendor-index, and vendor-admin.js

The solution

HtmlWebpackPlugin has changed chunksSortMode: ‘dependency’ to a custom function configuration, as shown below

Util. Js file

chunksSortMode: function (chunk1, chunk2) {
          var order1 = chunks.indexOf(chunk1.names[0])
          var order2 = chunks.indexOf(chunk2.names[0])
          return order1 - order2
        },
Copy the code

Pull out the common-API, a module common between multiple pagesUpdate (2018/4/23)

In fact, the scenario of removing the common files between multiple pages is often used by medium and large projects. At first, I saw the comments below saying that the common API needs to be removed, and I will pack more common-api. CSS, and I have some questions about commonChunk

Requirements: Some common JS and even CSS in the project can be reused on every page. For example, admin.js refers to common-api.js, and index.js refers to common-api.js. Now pull out the common-API module that is common across multiple pages

Adding a Common directory

Create a new common/index.js file that directly references a local js file (I’m using jquery instead of public JS).

Referencing a public file

Reference in admin.js index.js

import $ from '.. /.. /common'
console.log($('body'))
Copy the code

Packaging clearly shows that jquery is packaged twice, wasting resources.

The solution

  • Because there are multiple pages, a common entry common-API must be added
  • CommonChunkPlugin extracts common-api
  • Change the chunks sequence of htmlWebpackPlugin

Webpack ERROR in CommonsChunkPlugin: While running in normal mode it’s not allowed to use a non-entry chunk

At the beginning, a student asked me how to solve this error. At first, I did not know. Later, AFTER looking up some materials, I found that you can solve this error as long as you specify chunks

Respecify the order of htmlplugins in util.js

 let chunks = filename === 'admin' ?
      ['manifest'.'vendor'.'vendor-admin'.'common-api', filename] :
      ['manifest'.'vendor'.'vendor-index'.'common-api', filename]
Copy the code

The jq is loaded only once, and it is not packaged into common-api. CSS, and the order of scripts in HTML is correct

  • Each page loads its own chunk
  • Each page has different parameters
  • Each page can share the public chunk
  • Browser caching for better performance
  • If that’s too slow, open gzip
  • Pull out the common function common-api per page (updated on 2018/4/23)

feeling

The configuration looks pretty simple, but I thought a lot about it when I was developing it, so if you’re not familiar with CommonsChunkPlugin and HtmlWebpackPlugin, or you only use a third party’s configuration table, you might get stuck. For example,CommonsChunkPlugin does not specify chunks. What is the default? Most people only write a single value for minChunks, but it is the custom function that is the most powerful. In my experience, the combination of chunks and minChunks with custom functions solves almost all of the CommonsChunkPlugin’s magic events.

webpack4

Although this article is based on WebPack3, the idea of webPack4 multi-page configuration and optimized packaging is actually the same.

The source code

This source code like a like

reference

Reference for configuring multiple pages

HtmlWebpackPlugin sequence problem