Daily-Interview-Question icon indicating copy to clipboard operation
Daily-Interview-Question copied to clipboard

第 122 题:webpack 打包 vue 速度太慢怎么办?

Open yygmind opened this issue 4 years ago • 24 comments

yygmind avatar Aug 08 '19 01:08 yygmind

happypack多线程插件,DllPlugin

Loading-m avatar Aug 08 '19 01:08 Loading-m

不用vue

ZodiacSyndicate avatar Aug 08 '19 01:08 ZodiacSyndicate

不用webpack

Binbiubiubiu avatar Aug 08 '19 01:08 Binbiubiubiu

webpack 和 vue都不用

HHZWill avatar Aug 08 '19 01:08 HHZWill

说说我的处理方式吧,纯经验之谈

1.使用webpack-bundle-analyzer对项目进行模块分析生成report,查看report后看看哪些模块体积过大,然后针对性优化,比如我项目中引用了常用的UI库element-ui和v-charts等

2.配置webpack的externals ,官方文档的解释:防止将某些import的包(package)打包到 bundle 中,而是在运行时(runtime)再去从外部获取这些扩展依赖。 所以,可以将体积大的库分离出来:

// ...
externals: {
    'element-ui': 'Element',
    'v-charts': 'VCharts'
}

3.然后在main.js中移除相关库的import

4.在index.html模板文件中,添加相关库的cdn引用,如:

<script src="https://unpkg.com/[email protected]/lib/index.js"></script>
<script src="https://cdn.jsdelivr.net/npm/echarts/dist/echarts.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/v-charts/lib/index.min.js"></script>

经过以上的处理,再尝试编译打包,会发现速度快了一些。 有什么更好的方式或不对的地方欢迎指出

suguoyao avatar Aug 08 '19 02:08 suguoyao

"打包慢",是一个综合的因素,和vue关系不大。

1:确保下webpack,npm, node 及主要库版本要新,比如:4.x比3.x提升很多。

2:loader范围缩小到src项目文件!一些不必要的loader能关就关了吧

3:eslint代码校验其实是一个很费时间的一个步奏。 :可以把eslint的范围缩小到src,且只检查*.js 和 *.vue :生产环境不开启lint,使用pre-commit或者husky在提交前校验

4:happypack多进程进行

如果上面优化后,时间还是不满意的话,就尝试下5,6吧。

5:动态链接库(DllPlugin),楼上已说。有点类似配置的externals。 补充一下: 缺点:将不能按需加载,会将配置的第三方库全部打包进去。 推荐:可以将使用率较高的包采用dll方案。

6:HardSourceWebpackPlugin会将模块编译后进行缓存,第一次之后速度会明显提升。

superBlithe avatar Aug 08 '19 02:08 superBlithe

  1. 上面说到的Webpack-Happypack,从单进程变成多进程,加速代码构建速度
  2. 使用插件直接拷贝静态文件
  3. 使用更合理的代码压缩插件
  4. 减小文件搜索范围
  5. DllPlugin

YaYaHuaZhu avatar Aug 08 '19 03:08 YaYaHuaZhu

1.开启gzip压缩,这个需要服务端配合,以Nginx为例 1)在config/index.js 里面设置 productionGzip:true; 2)安装稳定版本的compression-webpack-plugin,注意别着急安装,因为安装最新版本的容易报错; 3)在/build/webpack.base.config.js文件,找到module.exports的module中的rules,将图片类,音视频类,字体类加上limit选项,这样打包时可缩小静态资源体积 4)在Nginx服务端的配置中设置gzip:on gzip_static:on 2.对于引用的第三方库,可以通过CDN的方式,在index.html中引入,然后在build/webpack.base.config.js中,添加配置排除掉这些第三方引用: //index.html <body> <div id="app"></div> <!-- built files will be auto injected --> <script src="https://cdn.bootcss.com/vue/2.6.6/vue.min.js"></script> <script src="https://cdn.bootcss.com/vue-router/3.0.2/vue-router.min.js"></script> <script src="https://cdn.bootcss.com/axios/0.19.0-beta.1/axios.min.js"></script> <script src="https://cdn.bootcss.com/echarts/4.2.1-rc1/echarts.min.js"></script> </body> //webpack.base.config.js let webpackConfig={ ... externals:{ 'vue':'vue', 'vue-router':'vue-router', 'axios':'axios', 'echarts':'echarts' } ... }

Clara07 avatar Aug 08 '19 03:08 Clara07

1.先设externals选项 把一些能直接走cdn的库拿出去如vue,vue-router的 2.拆分dll,把node_modules中的一部分拿出去先打包成一个静态的文件,在配置里引入dll的json配置,js文件拿去cdn,如 echarts下选用的模块 然后打包的都基本是自己的业务代码了。。 当然还可以自己再抽离组件 放到cdn去

HCLQ avatar Aug 08 '19 03:08 HCLQ

1. 使用最新版的webpack,官方会优化模块的解析速度

2.缩小loader的查询范围,例如:rules中loader添加:`include: path.resolve(__dirname, 'src')` 

3. 用DllPlugin插件单独编译一些不经常改变的代码,比如node_modules的第三方库

4.删除不需要的一些代码,利用SplitChunksPlugin 进行分块

5.cache-loader来进行缓存持久化

6.不同的devtool配置也会影响性能,最好配置为‘eval’,或者‘cheap-module-eval-source-map’

详情可以参考webpack官方文档

svenjia avatar Aug 09 '19 02:08 svenjia

1、因webpack提供的UglifyJS插件采用单线程压缩,速度很慢。所以将此插件替换为webpack-parallel-uglify-plugin插件,此插件可以并行运行UglifyJS插件,可有效减少构建时间。

//首先下载插件 npm i -D webpack-parallel-uglify-plugin
//修改 webpack.prod.conf.js
//将引入的UglifyJS 和 作用代码注释掉 并替换成下方代码
const ParallelUglifyPlugin = require('webpack-parallel-uglify-plugin')
new ParallelUglifyPlugin({
          cacheDir: '.cache/',
          uglifyJS:{
              output: {
                  comments: false
              },
              compress: {
                  warnings: false,
                  drop_debugger: true,
                  drop_console: false
              }
          }
      }),

2、由于运行在node.js之上的webpack是单线程模型,所以webpack做事只能一件一件去做。HappyPack可以让webpack在同一时间处理多个任务,把任务分解给多个子进程去并发执行,处理完之后将结果发给主进程

//首先下载插件  npm install --save-dev happypack
//修改 webpack.base.conf.js
const HappyPack = require('happypack')
const os = require('os')
let happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length });
//加入此插件
plugins:[
      new HappyPack({
          id:'babel',
          loaders:['babel-loader?cacheDirectory=true'],
          threadPool:happyThreadPool
      })
  ],
//将js loader作用代码替换
// loader: 'babel-loader'    替换成下方loader
 loader: 'happypack/loader?id=babel',

3、使用dll plugin => dynamic link library plugin,其中UglifyJS插件也可进行替换,详细请看第一条。

//首先在build文件下创建一个js文件,webpack.dll.conf.js,并写入下方代码
const path = require("path")
const webpack = require("webpack")
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
const pkg = require('../package')//引入package文件,目的就是找到依赖

module.exports = {
    // 想要打包的模块的数组
    entry: {
        // vendor: ['axios', 'vue-router', 'vue','weixin-js-sdk','element-ui','vuex']
        vendor:Object.keys(pkg.dependencies)//取出所有依赖单独打包
    },
    output: {
        path: path.join(__dirname, '../static/js'), // 打包后文件输出的位置
        filename: '[name].dll.js',//生成的文件名字 默认为vendor.dll.js
        library: '[name]_library'//生成文件的映射关系,与下面的DLLPlugin配置相对应
    },
    plugins: [
        new webpack.DllPlugin({//生成一个json文件 里面是关于dll.js的配置信息
            path: path.join(__dirname, '.', '[name]-manifest.json'),
            name: '[name]_library',//与上面output中的配置对应
            context: __dirname//上下文环境路径,必须填写,为了与DLLReferencePlugin存在于同一上下文中,否则undefined
        }),
        // 压缩打包的文件
        new UglifyJsPlugin({
            uglifyOptions: {
            compress: {
                  warnings: false
            }
          },
      }),
    ]
}

//然后在webpack.prod.conf.js和webpack.dev.conf.js中加入当前插件
new webpack.DllReferencePlugin({
          context: __dirname,//与DllPlugin中的context保持一致
          /*这个地址对应webpack.dll.conf.js中生成的那个json文件的路径,这样webpack打包的时候
          会检测当前文件中的映射,不会把已经存在映射的包再次打包进bundle.js */
          manifest: require('./vendor-manifest.json')
      }),

//在package.json中新加一条npm命令,执行webpack.dll.conf.js文件
"dll": "webpack --config ./build/webpack.dll.conf.js"
//tips:每次添加新依赖后,一定要运行npm run dll这个命令一次。

//最后在index.html中引入static/js/vendor.dll.js文件
<script src="static/js/vendor.dll.js"></script>

楼下老哥说的对,已经修改,感谢指正 @zhixinpeng

mofiggHasSugar avatar Aug 09 '19 03:08 mofiggHasSugar

1、因webpack提供的UglifyJS插件采用单线程压缩,速度很慢。所以将此插件替换为webpack-parallel-uglify-plugin插件,此插件可以并行运行UglifyJS插件,可有效减少构建时间。

//首先下载插件 npm i -D webpack-parallel-uglify-plugin
//修改 webpack.prod.conf.js
//将引入的UglifyJS 和 作用代码注释掉 并替换成下方代码
const ParallelUglifyPlugin = require('webpack-parallel-uglify-plugin')
new ParallelUglifyPlugin({
          cacheDir: '.cache/',
          uglifyJS:{
              output: {
                  comments: false
              },
              compress: {
                  warnings: false,
                  drop_debugger: true,
                  drop_console: false
              }
          }
      }),

2、happypack,因nodejs是单线程执行编译,而happypack是启动node的多线程进行构建,进而提高构建速度

//首先下载插件  npm install --save-dev happypack
//修改 webpack.base.conf.js
const HappyPack = require('happypack')
const os = require('os')
let happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length });
//加入此插件
plugins:[
      new HappyPack({
          id:'babel',
          loaders:['babel-loader?cacheDirectory=true'],
          threadPool:happyThreadPool
      })
  ],
//将js loader作用代码替换
// loader: 'babel-loader'    替换成下方loader
 loader: 'happypack/loader?id=babel',

3、使用dll plugin => dynamic link library plugin,其中UglifyJS插件也可进行替换,详细请看第一条。

//首先在build文件下创建一个js文件,webpack.dll.conf.js,并写入下方代码
const path = require("path")
const webpack = require("webpack")
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
const pkg = require('../package')//引入package文件,目的就是找到依赖

module.exports = {
    // 想要打包的模块的数组
    entry: {
        // vendor: ['axios', 'vue-router', 'vue','weixin-js-sdk','element-ui','vuex']
        vendor:Object.keys(pkg.dependencies)//取出所有依赖单独打包
    },
    output: {
        path: path.join(__dirname, '../static/js'), // 打包后文件输出的位置
        filename: '[name].dll.js',//生成的文件名字 默认为vendor.dll.js
        library: '[name]_library'//生成文件的映射关系,与下面的DLLPlugin配置相对应
    },
    plugins: [
        new webpack.DllPlugin({//生成一个json文件 里面是关于dll.js的配置信息
            path: path.join(__dirname, '.', '[name]-manifest.json'),
            name: '[name]_library',//与上面output中的配置对应
            context: __dirname//上下文环境路径,必须填写,为了与DLLReferencePlugin存在于同一上下文中,否则undefined
        }),
        // 压缩打包的文件
        new UglifyJsPlugin({
            uglifyOptions: {
            compress: {
                  warnings: false
            }
          },
      }),
    ]
}

//然后在webpack.prod.conf.js和webpack.dev.conf.js中加入当前插件
new webpack.DllReferencePlugin({
          context: __dirname,//与DllPlugin中的context保持一致
          /*这个地址对应webpack.dll.conf.js中生成的那个json文件的路径,这样webpack打包的时候
          会检测当前文件中的映射,不会把已经存在映射的包再次打包进bundle.js */
          manifest: require('./vendor-manifest.json')
      }),

//在package.json中新加一条npm命令,执行webpack.dll.conf.js文件
"dll": "webpack --config ./build/webpack.dll.conf.js"
//tips:每次添加新依赖后,一定要运行npm run dll这个命令一次。

//最后在index.html中引入static/js/vendor.dll.js文件
<script src="static/js/vendor.dll.js"></script>

由于 JavaScript 是单线程模型,要想发挥多核 CPU 的能力,只能通过多进程去实现,而无法通过多线程实现。所以 happypack 应该是让 webapck 开启新的子进程,子进程处理完成之后把结果汇总到主进程中。

zhixinpeng avatar Aug 09 '19 06:08 zhixinpeng

1、配置 externals,工具库直接使用 cdn,不需要打包,例如:vue,vue-router 等。

2、在处理 loader 时,配置 include,缩小 loader 检查范围。

3、使用 alias 可以更快地找到对应文件。

4、如果在 require 模块时不写后缀名,默认 webpack 会尝试.js,.json 等后缀名匹配,配置 extensions,可以让 webpack 少做一点后缀匹配。

5、thread-loader 可以将非常消耗资源的 loaders 转存到 worker pool 中。

6、使用 cache-loader 启用持久化缓存。使用 package.json 中的 postinstall 清除缓存目录。

mengsixing avatar Aug 10 '19 04:08 mengsixing

使用webpack5,速度有质一般提升

LienJack avatar Aug 12 '19 02:08 LienJack

参考

nordon-wang avatar Aug 21 '19 08:08 nordon-wang

 不写js

HuangYongXuan avatar Sep 09 '19 09:09 HuangYongXuan

冷水 1.externals方案的缺点是什么呢,是外链的包如果升级需要手动添加hash 2.拆分dll,本身并没有问题,但是放在服务器部署的时候,那什么时候应该构建dll的部分是一个js界通用难题

humorHan avatar Oct 17 '19 10:10 humorHan

我工作中用到优化:

  1. 排除不用的第三方包,
  2. 使用npm run build --report去观察各个文件的大小,使用externals方案及引入cdn, 或者按需加载(比如: element-ui)
  3. 处理 loader 时,配置 include,exclude,缩小 loader 检查范围
  4. webpack-parallel-uglify-plugin插件, 减少的构建时间
  5. eslint不检测或者缩小检测范围
  6. 使用gzip压缩

dhbdn avatar Oct 24 '19 02:10 dhbdn

我工作中用到优化:

  1. 排除不用的第三方包,
  2. 使用npm run build --report去观察各个文件的大小,使用externals方案及引入cdn, 或者按需加载(比如: element-ui)
  3. 处理 loader 时,配置 include,exclude,缩小 loader 检查范围
  4. webpack-parallel-uglify-plugin插件, 减少的构建时间
  5. eslint不检测或者缩小检测范围
  6. 使用gzip压缩

@dhbdn 使用externals 之后需要手动维护这些文件 升级需要添加手动添加hash 这个怎么破

humorHan avatar Oct 24 '19 07:10 humorHan

我工作中用到优化:

  1. 排除不用的第三方包,
  2. 使用npm run build --report去观察各个文件的大小,使用externals方案及引入cdn, 或者按需加载(比如: element-ui)
  3. 处理 loader 时,配置 include,exclude,缩小 loader 检查范围
  4. webpack-parallel-uglify-plugin插件, 减少的构建时间
  5. eslint不检测或者缩小检测范围
  6. 使用gzip压缩

@dhbdn 使用externals 之后需要手动维护这些文件 升级需要添加手动添加hash 这个怎么破

这个可以自动化, html-webpack-tags-plugin 插件在生产环境时自动插入script标签 可以根据版本生成hash加在标签链接上

// 自动externals配置, 打包阶段走cdn
        if (process.env.NODE_ENV === 'production') {
          const [webpackExternals, scripts] = formatExternal(externals)
          debug(`use externals ${JSON.stringify(webpackExternals)}`)
          scripts.forEach((url) => debug(`external link: ${url}`))
          chain.externals(webpackExternals)
          chain.plugin('InjectExternalsToHtml').use(HtmlWebpackTagsPlugin, [{ append: false, usePublicPath: false, scripts }])
        }

HCLQ avatar Oct 24 '19 07:10 HCLQ

  1. 升级到最新的 webpack 版本,更新项目中 loader plugin的版本,很大可能他们都解决一些性能问题。
  2. 升级 node 版本,看看你的版本是否落后于时代。
  3. 基本上的问题都是项目整体参与打包的尺寸太大,可以尽量的缩小尺寸,比如外部依赖可以通过DLL 或者 cdn 的方式引用进来。比如精简依赖,使用 date-fns 替代 moment,lodash 可以使用他的子模块,比如 lodahs.unique。依赖图片类资源的自己手动上传到cdn,避免引入到 webpack 中打包。
  4. HappyPack/thread-loader 使用多进程提高loader 处理速度。
  5. 优化模块查找路径 载入内置模块 载入文件模块 载入文件目录模块 载入node_modules里的模块 自动缓存已载入模块 如果模块名不是路径,也不是内置模块,Node将试图去当前目录的node_modules文件夹里搜索。如果当前目录的node_modules里没有找到,Node会从父目录的node_modules里搜索,这样递归下去直到根目录。
  6. 重新规划项目架构,如果过于庞大,可以反思是否合理,能否抽离出不太经常更新的代码,比如经常用到的组件库单独打包引入。项目业务能否拆成几个小块,单独构建。
  7. 升级自己的电脑。

nazhenhuiyi avatar Mar 10 '20 04:03 nazhenhuiyi

通用环境:

521590808978_ pic_hd

开发环境: 531590809049_ pic_hd

生产环境: 551590809236_ pic

参考:https://webpack.docschina.org/guides/build-performance

cutie6 avatar May 30 '20 03:05 cutie6

缩小文件搜索范围

  • 优化loader配置:include 、 exclude
  • 优化module.noParse配置: 忽略对部分没采用模块化的文件的递归解析处理
  • 优化resolve.modules配置: 去哪些目录下寻找第三方模块
  • 优化resolve.alias配置
  • 优化resolve.mainFields配置
  • 优化resolve.extensions配置:配置在尝试匹配过程中用到的后缀列表

减少打包文件

  • 提取公共代码
  • 动态链接DllPlugin
  • externals
  • Tree Shaking

缓存

  • babel 缓存 cacheDirectory: true
  • cache-loader
  • contenthash

多进程

  • happypack
  • thread-loader

m7yue avatar Oct 15 '20 16:10 m7yue

缩小loader的查找范围 提取公共库 ,减少参与不必要的打包体积 开启缓存,加快构建速度 开启多进程,并发处理,加快loader处理速度

Yangfan2016 avatar Aug 22 '22 09:08 Yangfan2016