Since my own project is based on VUE-CLI3 development, I will only discuss the solution in this case. In the multi-page development phase, the project starts with a small number of pages, the compilation speed is tolerable, but once the page increases, the multiple hot updates cause memory overflow.

why

Here we need a plug-in for performance analysis webpack-bundle-Analyzer. Add the following code to vue.config.js

const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
configureWebpack: {
 plugins: [
    new BundleAnalyzerPlugin(),
 ],
}
Copy the code

Below is a screenshot of the compilation of my own project

webpack
18M
node

Here are a few ways to try to speed up compilation

increaseNodeRun a memory

As mentioned above, hot update code can take up a lot of node’s allocated memory during hot update, resulting in memory overflow. So the first way is to try to increase the running memory of node. You can only use part of the memory when using JavaScript in Node (about 1.4 GB on 64-bit systems and 0.7 GB on 32-bit systems). So no matter how much memory the computer actually has, Node doesn’t change the amount of memory it uses to compile code. This may result in memory overflow due to insufficient original memory. The following two scenarios are presented

To change thecmd

Copy the following code on node_modules/.bin/vue-cli-server. CMD

@IF EXIST "%~dp0\node.exe" (
  "%~dp0\node.exe" --max_old_space_size=4096"%~dp0\.. \@vue\cli-service\bin\vue-cli-service.js" %* )ELSE(@SETLOCAL
  @SET PATHEXT=%PATHEXT:; .JS; =; %
  node --max_old_space_size=4096"%~dp0\.. \@vue\cli-service\bin\vue-cli-service.js" %* )Copy the code

To change thepackage.json

Change the changes to start the Node service:

node --max_old_space_size=4096 node_modules/@vue/cli-service/bin/vue-cli-service.js serve
Copy the code

In essence, there is no difference, just increasing the memory allocated by Node. In this case, increasing Node’s running memory to 4 gb will keep WebPack hot compiled without running out of memory. With this approach, there really is no memory overflow. However, there was no significant improvement in the speed of first startup and hot compilation, and the first compilation was still an intolerable one minute. If the project expands further, do we need to increase node’s running memory again?

Filter compiled pages

As can be seen in the above screenshot, webpack compiled some unnecessary pages during compilation. Originally, we only need to debug page A, but Webpack compiled all the pages, some pages we may not need, so here is an idea to filter the pages that need to be debugged. Here is the multi-page configuration:

// page.config.js
module.exports = {
    index: {
        entry: 'src/page/index/main.js'.// Page entry
        template: 'public/index.html'.// Page template path
        filename: 'index.html' // Output the file name
        title: 'title page',}}// vue.config.js
const pages = require('./page.config.js')
module.expors = {
    pages,
}
Copy the code

It can be seen that the traditional solution is to introduce all the configuration of multiple pages and compile, so here is a solution to filter the multiple pages and get the compiled pages we need. The following is the filtering script:

const path = require('path');
const fs = require('fs');
const pages = require('.. /pages.config');

const params = JSON.parse(process.env.npm_config_argv).original;
const buildPath = params[params.length - 1].match(/[a-zA-Z0-9]+/) [0] | |' ';

let buildConfig = {
  pages: [],};if (!/(test|online|serve)/gi.test(buildPath)) {
  const configJsPath = path.resolve(__dirname, `${buildPath}.js`);

  // If the path exists
  if (fs.existsSync(configJsPath)) {
    // eslint-disable-next-line import/no-dynamic-require
    buildConfig = require(configJsPath);
  } else if (pages[buildPath]) {
    buildConfig.pages = buildPath.split(', ');
  } else {
    throw new Error('This path does not exist'); }}else {
  buildConfig = require('./default');
}
const buildPages = {};
buildConfig.pages.forEach((name) = > {
  buildPages[name] = pages[name];
});
module.exports = buildPages;
Copy the code

Here is what default.js looks like:

module.exports = {
    pages: ['ugcDetail']}Copy the code

In this file, Pages is the article we need to compile. Now WebPack filters the pages that do not need to compile. Here is a screenshot of the page compilation speed:

10
webpack

usewebbpack-dev-serveThe hook compiles separately

As can be seen from page.config.js above, each single page has an entry file. Webpack uses these entry files to compile each page separately, and the js compiled on each page will be very large together. If you need to compile the page, introduce the entry file you want to compile into the empty file. All entry files become an empty JS file. If you need to compile the page, pass it

The import entryCopy the code

Implement separate page file compilation. In webpack-dev-serve, there is a hook before, and when we visit the page, we can get the path of the page information. Here is the implementation:

// vue.config.js
const compiledPages = [];
before(app) {
      app.get('*.html', (req, res, next) => {
        const result = req.url.match(/ [^ /] +? (? = \.) /);
        const pageName = result && result[0];
        const pagesName = Object.keys(multiPageConfig);

        if (pageName) {
          if (pagesName.includes(pageName)) {
            if(! compiledPages.includes(pageName)) {const page = multiPageConfig[pageName];
              fs.writeFileSync(`dev-entries/${pageName}.js`.`import '.. /${page.tempEntry}'; // eslint-disable-line`); compiledPages.push(pageName); }}else {
            // There is no entrance
            res.writeHead(200, { 'content-type': 'text/html; charset=utf-8' });
            res.end('

nonexistent entry

'
); } } next(); }); }, Copy the code

In multi-page configuration, the following configuration needs to cache the entry path first and then empty it. The following is the concrete implementation

{
    pageName: {
        entry: entryPath,
        chunks: [array]
    }
}
const fs = require('fs');
const util = require('util');

const outputFile = util.promisify(fs.writeFile);
async function main() {
  const tasks = [];
  if(! fs.existsSync('dev-entries')) {
    fs.mkdirSync('dev-entries');
  }
  Object.keys(pages).forEach((key) = > {
    const entry = `dev-entries/${key}.js`;
    pages[key].tempEntry = pages[key].entry; // store the real entry file address
    pages[key].entry = entry;
    tasks.push(outputFile(entry, ' '));
  });
  await Promise.all(tasks);
}

if (process.env.NODE_ENV === 'development') {
  main();
}

module.exports = pages;
Copy the code

This method makes all page entry files empty, so all pages are compiled but all files are empty, which greatly reduces the size of the file for the first compilation.

80
8s
before

Optimization of the script

In view of the above code is optimized, which, in fact, although the so file entrance into the empty, but in practice there are the empty file package, so now the optimal point is that as long as this page without debugging, we will not he package, this way has a benefit is generally have a test environment, for the individual, Many times you will need to deploy this code to a test machine. The above solution solves the problem of slow compilation in the development environment, but it doesn’t help at all if you want to upload the code to the test machine, and you need to upload all the page files. So here’s how to optimize: this time we don’t change the page.config.js code. Create a JSON file under the Script folder to store the simplest entry to the page, preferably with nothing in the package. Such as:

{
  "miguRead": {
    "entry": "src/pages/miguRead/main.js"."template": "public/index.html"."filename": "miguRead.html"."title": "Migu Homework Club Fun Reading"."business": "base"}}Copy the code

Then modify vue.config.js

const developemntPages = require('./help')
let compiledPages;
if (process.env.NODE_ENV === 'development') {
  compiledPages = developmentPages;
  compiledPages.miguRead = multiPageConfig.miguRead;
} else {
  compiledPages = multiPageConfig;
}
before: (app) {
    / /...
    if (pageName) {
        if (multiPageConfig[pageName]) {
            if(! compiledPages[pageName]) { compiledPages[pageName] = multiPageConfig[pageName]; fs.writeFileSync('help.json'.`
                The ${JSON.stringify(compiledPages)}
              `); }}else {
            res.writeHead(200, { 'content-type': 'text/html; charset=utf-8' });
            res.end('

nonexistent entry

'
); }}}Copy the code

Since these variables are also reset each time the service is restarted, you need a file that will not be modified by restart, so a help.json is introduced here. Finally, add a listener help.json to package.json that writes information about a page to help.json when we visit it. When you restart the service, you will get the information on this page. The changed information in package.json is

nodemon --watch vue.config.js --watch help.json --exec node --max_old_space_size=4096 node_modules/@vue/cli-service/bin/vue-cli-service.js serve
Copy the code

upgradehtml-webpack-pluginversion

The problem of multiple pages running out of memory is due to the fact that multiple files are compiled in one change at compile time, which is a problem of the HTML-webpack-plugin. Because you didn’t generate a single page, you needed to call new htmlWebpackPlugin() and run out of memory for multiple pages. So change the webpack plugin to 4.0.0-beta.8. Then add the following configuration to vue.config.js without running out of memory.

const htmlPlugins = [];
Object.keys(multiPageConfig).forEach((key) = > {
    htmlPlugins.push(multiPageConfig[key])
})
configureWebpack: {
    plugins: [
      ...htmlPlugins,
    ],
}
Copy the code

The single-page hot compilation is slow

Vue-cli3 is used in the background technology stack. As the background project gathers the background services of a department, the scheme of single page and multiple routes is adopted, so there are too many routes. When we started with fewer routes, it was pretty fast to compile, but when we got to 100 pages, it was unbelievably fast, slower than the multiple pages in the foreground, and took a few minutes to start. Therefore, I found a solution on the Internet. I need to introduce all asynchronous components into the development environment in a synchronous way. I don’t know why this is done, if you are interested, you can study it.

The solution

Install Babel – plugin – dynamic – import – node. One thing it does is convert all import() to require(), so you can use this plug-in to introduce all asynchronous components synchronously, and combine the Babel environment variable BABEL_ENV to make it work only in the development environment. To do this, add in babel.config.js

env: {
    development: {
      plugins: ['dynamic-import-node'],}},Copy the code

Vue-cli3 has the corresponding env file, for example, mine is.env.development, and then add the environment variable

VUE_CLI_BABEL_TRANSPILE_MODULES=true
Copy the code

OK, done, the compilation speed is more than 15 times faster, hurry up to try it.

Each of these approaches has its own advantages and disadvantages, and the reader can decide which approach to use depending on his or her own project, and may sometimes need to use a combination of them to see what works. Github will continue to update the article. I hope you will pay more attention to it.

Log:

11-13: Optimized multi-page packaging scheme to solve multi-route slow compilation