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

发布于 2022-06-12 12:14:22 字数 1704 浏览 1215 评论 23

项目使用 ts+webpack+vue,发现了 npm 跑起来和打包起来都非常的慢,经过多番查阅,发现是一个配置得问题

Webpack 配置 TS 和 TSLint 支持

{
      test: /\.tsx*?$/,
      exclude: /node_modules/,
      loader: require.resolve('ts-loader'),
      options: {
        transpileOnly: true,
      },
}

需要注意的是 options 处设置了 transpileOnly 为 true,否则编译会很慢很慢,官方解释是:

As your project becomes bigger and bigger, compilation time increases linearly. It’s because typescript’s semantic checker has to inspect all files on every rebuild. The simple solution is to disable it by using the transpileOnly: true option, but doing so leaves you without type checking.

也就是每次都要跑一边类型检查

注意最后一句话,那如果我们还想要 type checking 怎么办呢?后面接着就解释怎么做了,想看原文的话去 ts-loader 的 GitHub 库看就好了。我就说下是怎么做的。

你需要配置下这个插件 fork-ts-checker-webpack-plugin:

new ForkTsCheckerWebpackPlugin({
  checkSyntacticErrors: true,
  tsconfig: path.resolve(__dirname, 'tsconfig.json'),
  tslint: path.resolve(__dirname, 'tslint.json'),
  watch: ['./src/**/*.tsx'],
  ignoreLints: [
    'no-console',
    'object-literal-sort-keys',
    'quotemark',
  ],
})

把他加入到 Webpack 的 Plugins 中去,然后在 Webpack 启动时他会自动创建一个新进程去做 type checking,和 Webpack 的编译独立开来了,这样就可以在快速编译的同时又可以享受类型检查了。

你还可以使用 Webpack 的另一个插件忽略掉 d.ts 文件,避免因为编译生成 d.ts 文件导致又重新检查。

new webpack.WatchIgnorePlugin([
  /\.d\.ts$/,
]),

在 ts-loader 的仓库中,还有其他的一些提高编译速度的讨论,例如使用 cache-loader 或者 thread-loader,或者使用 happypack,但是这三个库我都试过了,并没有提升,一点提升都没有,反而降速了。也有尝试了 awesome-typescript-loader 但编译就直接失败了,想想 ts-loader 跟 ForkTsCheckerWebpackPlugin 一起工作也不慢,就暂时这样了。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(23

风流物 2022-05-04 13:54:59

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

末蓝 2022-05-04 13:54:59

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

半窗疏影 2022-05-04 13:54:59

 不写js

萌︼了一个春 2022-05-04 13:54:59

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

年少掌心 2022-05-04 13:54:59

我工作中用到优化:

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

我工作中用到优化:

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

要走就滚别墨迹 2022-05-04 13:54:59

我工作中用到优化:

  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 }])
        }
韶华倾负 2022-05-04 13:54:59
  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. 升级自己的电脑。
东北女汉子 2022-05-04 13:54:59

通用环境:

521590808978_ pic_hd

开发环境:
531590809049_ pic_hd

生产环境:
551590809236_ pic

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

海的爱人是光 2022-05-04 13:54:59

缩小文件搜索范围

  • 优化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
枯〃寂 2022-05-04 13:54:58

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

送你一个梦 2022-05-04 13:54:55

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

放低过去 2022-05-04 13:54:53
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官方文档

玉环 2022-05-04 13:54:52

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

热血少△年 2022-05-04 13:54:50

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

江南月 2022-05-04 13:54:32
  1. 上面说到的Webpack-Happypack,从单进程变成多进程,加速代码构建速度
  2. 使用插件直接拷贝静态文件
  3. 使用更合理的代码压缩插件
  4. 减小文件搜索范围
  5. DllPlugin
没企图 2022-05-04 13:54:22

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

宁愿没拥抱 2022-05-04 13:53:37

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

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>

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

放低过去 2022-05-04 13:44:21

webpack 和 vue都不用

清眉祭 2022-05-04 13:40:36

不用webpack

红墙和绿瓦 2022-05-03 18:51:21

不用vue

乙白 2022-05-03 05:19:12

happypack多线程插件,DllPlugin

~没有更多了~

关于作者

始终不够

暂无简介

文章
评论
27 人气
更多

推荐作者

櫻之舞

文章 0 评论 0

弥枳

文章 0 评论 0

m2429

文章 0 评论 0

野却迷人

文章 0 评论 0

我怀念的。

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文