Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
yygmind opened this issue Aug 8, 2019 · 24 comments
Open

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

yygmind opened this issue Aug 8, 2019 · 24 comments

Comments

@yygmind
Copy link
Contributor

yygmind commented Aug 8, 2019

No description provided.

@Loading-m
Copy link

happypack多线程插件,DllPlugin

@ZodiacSyndicate
Copy link

不用vue

@Binbiubiubiu
Copy link

不用webpack

@HHZWill
Copy link

HHZWill commented Aug 8, 2019

webpack 和 vue都不用

@suguoyao
Copy link

suguoyao commented Aug 8, 2019

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

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/element-ui@2.10.0/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>

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

@superBlithe
Copy link

superBlithe commented Aug 8, 2019

"打包慢",是一个综合的因素,和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会将模块编译后进行缓存,第一次之后速度会明显提升。

@YaYaHuaZhu
Copy link

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

@Clara07
Copy link

Clara07 commented Aug 8, 2019

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'
}
...
}

@HCLQ
Copy link

HCLQ commented Aug 8, 2019

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

@svenjia
Copy link

svenjia commented Aug 9, 2019

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官方文档

@mofiggHasSugar
Copy link

mofiggHasSugar commented Aug 9, 2019

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

@zhixinpeng
Copy link

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 开启新的子进程,子进程处理完成之后把结果汇总到主进程中。

@mengsixing
Copy link

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 清除缓存目录。

@LienJack
Copy link

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

@nordon-wang
Copy link

参考

@HuangYongXuan
Copy link

 不写js

@humorHan
Copy link

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

@dhbdn
Copy link

dhbdn commented Oct 24, 2019

我工作中用到优化:

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

@humorHan
Copy link

humorHan commented Oct 24, 2019

我工作中用到优化:

  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 这个怎么破

@HCLQ
Copy link

HCLQ commented Oct 24, 2019

我工作中用到优化:

  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 }])
        }

@nazhenhuiyi
Copy link

nazhenhuiyi commented Mar 10, 2020

  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. 升级自己的电脑。

@cutie6
Copy link

cutie6 commented May 30, 2020

通用环境:

521590808978_ pic_hd

开发环境:
531590809049_ pic_hd

生产环境:
551590809236_ pic

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

@m7yue
Copy link

m7yue commented Oct 15, 2020

缩小文件搜索范围

  • 优化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

@Yangfan2016
Copy link

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests