由于本身的项目是基于vue-cli3
进行开发,因此这里只讨论这种状况下的解决办法
在进行多页面开发的时候,项目刚开始阶段,页面较少,编译速度还能忍受,可是一旦页面增长,屡次热更新就形成了内存溢出。html
这里须要借助一个插件来进行性能分析webpack-bundle-analyzer
,在vue.config.js
中添加如下代码vue
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer'); configureWebpack: { plugins: [ new BundleAnalyzerPlugin(), ], }
下面是本身项目编译的截图node
能够看到的是webpack
把全部的页面都进行了编译,整体积已经达到了18M
,耗时超过1分钟,在热更新的时候这个体积会变得更大,从而占据node
的运行内存,致使内存溢出。可是通常在开发的时候,咱们一次更改的页面可能就只有几个,因此编译这些多余的页面是没有必要的。那下面就是多种解决方案webpack
下面就是几种尝试的方法,加快编译的速度git
Node
运行内存在上面提到在热更新的时候,热更新的代码会大量占据node
分配的内存,致使内存溢出。那么第一种方式,尝试增长node
的运行内存。在Node
中经过JavaScript
使用内存时只能使用部份内存(64
位系统下约为1.4 GB
,32
位系统下约为0.7 GB
)。因此无论电脑实际的运行内存是多少,Node
在运行代码编译的时候,使用内存大小不会发生变化。这样就可能致使由于原有的内存不够,致使内存溢出。下面给出两种方案github
cmd
在node_modules/.bin/vue-cli-server.cmd
把下面代码复制上去web
@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" %* )
package.json
把启动Node
服务的更改下:chrome
node --max_old_space_size=4096 node_modules/@vue/cli-service/bin/vue-cli-service.js serve
本质上没什么区别,都是增长Node
分配的内存,在这里把node
的运行内存提升到4g
就可以让webpack
热编译的时候不会内存溢出。使用这种方法,确实是没有在出现内存溢出的状况。可是在首次启动和热编译的时候,速度并无发生质的提高,首次编译仍是达到了1分钟这种难以忍受的速度。若是项目进一步扩大,难道咱们须要再次增长node
的运行内存?vue-cli
在上面截图中能够看到,在编译的时候webpack
编译了一些没必要要的页面,原本咱们只须要调试A
页面,可是webpack
把全部的页面都进行了编译,某些页面咱们可能并不须要,那这里就提出一种思路,对须要调试的页面进行过滤。
下面是多页面的配置:npm
// page.config.js module.exports = { index: { entry: 'src/page/index/main.js', // 页面入口 template: 'public/index.html', // 页面模板路径 filename: 'index.html' // 输出文件名 title: '页面title', } } // vue.config.js const pages = require('./page.config.js') module.expors = { pages, }
能够看到的是传统的方案是把多页面的配置所有引入,进行编译,因此这里就提出一种解决方案,对多页面进行过滤,获得咱们须要的编译页面,下面是过滤的脚本:
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 (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('该路径不存在'); } } else { buildConfig = require('./default'); } const buildPages = {}; buildConfig.pages.forEach((name) => { buildPages[name] = pages[name]; }); module.exports = buildPages;
这样就能够单独单独编译咱们所须要的页面下面是default.js
的内容:
module.exports = { pages: ['ugcDetail'] }
这个文件中pages
就是咱们须要编译的文章,如今webpack
就过滤了不须要编译的页面,下面是页面编译速度的截图:
页面的编译速度提升了惊人的10
倍,由于须要编译的文件少了,因此运行速度也就提升了很多。通常状况下,一我的是负责单独的业务线,别人的代码咱们也不须要干涉,因此也能实现一次配置,屡次运行的效果。可是有的时候咱们也须要更改别人的代码,不能说又加一个配置文件,下面就是借助webpack
自带的钩子实现编译指定文件。
webbpack-dev-serve
钩子进行单独编译在上面page.config.js
中能够看到每一个单页面都有一个入口文件,webpack
借助这些入口文件进行对每一个页面进行单独编译,每一个页面编译后的js
混合到一块儿也就很是大了,那咱们能不能让这些入口文件暂时变成一个空文件,若是须要编译这个页面,在空文件中引入须要编译的入口文件。也就是全部的入口文件都变成了一个空的js
文件,若是须要编译这个页面,在经过
import 入口
实现单独的页面文件编译。那webpack
是如何知道咱们须要编译的页面呢,在webpack-dev-serve
中,有一个钩子before
,在访问页面的时候咱们可以拿到页面信息的路径,下面是实现:
// 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 { // 没这个入口 res.writeHead(200, { 'content-type': 'text/html; charset=utf-8' }); res.end('<p style="font-size: 50px;">不存在的入口</p>'); } } next(); }); },
多页面配置中如下配置,须要先把入口路径,先缓存起来,而后置空,下面是具体实现
{ 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; // 暂存真正的入口文件地址 pages[key].entry = entry; tasks.push(outputFile(entry, '')); }); await Promise.all(tasks); } if (process.env.NODE_ENV === 'development') { main(); } module.exports = pages;
这种方法就是把全部页面入口文件置为空文件,虽然编译了全部的页面可是全部的文件都是空的,因此大大的减小了首次编译的文件大小。
速度也从原来的80
多秒,下降到了8s
。而后当咱们访问某个页面的页面,执行到before
钩子,进行单独编译,速度也是很是快的。
html-webpack-plugin
版本多页面出现内存溢出的问题是由于在编译的时候,实际是一次更改,编译了多个文件,这是html-webpack-plugin
的问题。由于没生成一个页面,就须要调用一下new htmlWebpackPlugin()
,多个页面的时候内存就不够用了。因此改一下这个这个webpack
插件的版本,升级到4.0.0-beta.8
这个版本。而后再vue.config.js
中添加下面的配置,这样也不会形成内存溢出。
const htmlPlugins = []; Object.keys(multiPageConfig).forEach((key) => { htmlPlugins.push(multiPageConfig[key]) }) configureWebpack: { plugins: [ ...htmlPlugins, ], }
webpack
的插件仍是很方便的,网上有啥happypack
相似的插件。因为运行在 Node.js 之上的 Webpack 是单线程模型的,因此Webpack
须要处理的事情须要一件一件的作,不能多件事一块儿作。
咱们须要Webpack
能同一时间处理多个任务,发挥多核CPU
电脑的威力,HappyPack
就能让Webpack
作到这点,它把任务分解给多个子进程去并发的执行,子进程处理完后再把结果发送给主进程。多是我电脑太烂了,装上没啥太大的提高,具体使用方法能够参照这篇文章webpack优化之HappyPack 实战。还有一些细节的地方好比说有些包须要加入编译,可是通常咱们在调试的时候只须要在chrome
上进行调试,开发环境就不用加入编译,多处使用的代码单独打包,这些也就不说了,你们多多尝试
这几种解决多页面内存溢出的方法各有优缺点,读者可根据本身的项目自行决定使用哪一种方法,可能有时还须要多种方式组合使用,就看看那个好使好用了。
推销一波本身的github最近在抓紧学习,会持续更新文章,但愿你们多多关注。