webpack打包文件体积过大,怎么提升速度?

借助webpack visualizer可视化插件,来看构建的情况。
这个问题要具体情况具体分析,看看打包文件有哪些块头比较大,哪些不常改变,最好列一个list,分类优化。下面提供8种方法,仅供参考。

一、区分配置文件,去除不必要的插件。
前端开发环境通常分为两种:开发环境和生产环境,我们可以创建两个配置文件来区分开发环境和生产环境。
webpack.config.dev.js,用于开发环境,需要日志输出,sourcemap,热模块替换,错误报告等。
webpack.config.prod.js,用于生产环境,需要做代码压缩,对文件名进行hash处理等。

//安装webpack-merge
npm install --save-dev webpack-merge
//webpack.config.common.js

const path = require('path');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: {
    app: './src/index.js'
  },
  plugins: [
    new CleanWebpackPlugin(['dist']),
    new HtmlWebpackPlugin({
      title: 'Production'
    })
  ],
  output: {
    filename: '[name].bundle.js',
    path: path.resolve(__dirname, 'dist')
  }
};
//webpack.config.dev.js

const merge = require('webpack-merge');
const common = require('./webpack.config.common.js');

module.exports = merge(common, {
  devtool: 'inline-source-map',
  devServer: {
    contentBase: './dist'
  }
});
//webpack.config.prod.js

const merge = require('webpack-merge');
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
const common = require('./webpack.config.common.js');

module.exports = merge(common, {
  devtool: 'source-map',
  plugins: [
    new UglifyJSPlugin({
      sourceMap: true
    })
  ]
});

在webpack.config.common.js中,我们设置了entry和output配置,并且在其中引入这两个环境公用的全部插件。在webpack.config.dev.js中,我们添加了推荐的devtool(强大的 source map)和简单的devServer配置。在webpack.config.prod.js中,我们引入了UglifyJSPlugin。

修改package.json文件

"scripts": {
  "dev": "webpack-dev-server --open --config webpack.config.dev.js",
  "prod": "webpack --config webpack.config.prod.js"
}

二、合理使用CommonsChunkPlugin插件,提取公用代码。

该方法适用于开发环境和生产环境,具体用法在下面有介绍。

三、通过externals配置来引用外部文件的方式,提取第三方库。

{
  externals: {
    'react': 'React'
  }
}
<script src="//cdn.com/vue.min.js"></script>
<script src="/build/bundle.js"></script>

该方法适用于生产环境。

四、利用DllPlugin和DllReferencePlugin预编译资源模块,提取第三方库。

const vendors = [
  'vue','vue-router',
  // ...其它库
];

module.exports = {
  output: {
    path: 'build',
    filename: '[name].js',
    library: '[name]'
  },
  entry: {
    "lib": vendors
  },
  plugins: [
    new webpack.DllPlugin({
      path: 'manifest.json',
      name: '[name]',
      context: __dirname
    })
  ]
};

该方法适用于开发环境和生产环境。

五、压缩代码

使用tree shaking,即先用es6语法import,export,再引入删除引用代码的压缩工具UglifyJsPlugin。

new webpack.optimize.UglifyJsPlugin({
    // 最紧凑的输出
    beautify: false,
    // 删除所有的注释
    comments: false,
    compress: {
        //在UglifyJs删除没有用到的代码时不输出警告
        warnings: false,
        //用于压缩的时候去除log
        drop_debugger: true,
        drop_console: true,
        //内嵌定义了但是只用到一次的变量
        collapse_vars: true,
        //提取出现多次但是没有定义成变量去引用的静态值
        reduce_vars: true
    }
})

加入了这个插件之后,编译的速度会明显变慢,所以一般只在生产环境启用。如果服务器端可以开启gzip压缩,优化的效果更明显。
另外,可以通过webpack-parallel-uglify-plugin来提升压缩速度,原理是采用多核并行压缩的方式提升速度。

let ParallelUglifyPlugin = require('webpack-parallel-uglify-plugin');
let os = require('os');

new ParallelUglifyPlugin({
   workerCount: os.cpus().length,
   cacheDir: '.cache/',
   uglifyJS: {
        output: {
            comments: false
        },
        compress: {
            warnings: false
        }
    }
})

该方法适用于生产环境。

六、happyPack多核编译资源(多线程介入

var HappyPack = require('happypack');
const os = require('os');
const HappyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length });
//启动线程池

let happyPackConfig = {
    plugins: [
        new HappyPack({
            id:'jsx',
            threadPool: HappyThreadPool,
            cache: true,
            loaders: ['babel-loader']
        })
    ]
}

{
    test: /.jsx?$/,
    exclude: /(node_modules|bower_components)/,
    use: {
        loader: 'HappyPack/loader?id=jsx'
    }
}

七、代码分割
分离代码,实现缓存资源和并行加载资源。
对于大型app而言,将所有代码都放在一个文件中是不高效的,尤其是一些代码块只要某些情况下才需要加载。webpack有一个特性,就是能够对项目进行代码分割。代码分割不只是提取通用代码放入可分享的块,更重要的是可以将代码分割成按需加载的块。代码分割可选择,可定义分割点,下面介绍几种分割代码的方法。
1、entry
当项目多入口点时,必须改写默认的output.filename选项,否则每个入口点都会写入相同的文件。
A、多个入口文件,打包在一起;

'use strict';
const webpack = require("webpack");
module.exports = {
  context: __dirname + "/src",
  entry: {
    app: ["./file1.js", "./file2.js", "./file3.js"]
  },
  output: {
    path: __dirname + "/dist",
    filename: "[name].bundle.js"
  }
};

打包结果:所有文件会按数组顺序一起打包到dist/app.bundle.js文件中。

B、多个入口文件,分开打包。

const path = require('path');
const HTMLWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: {
    file1: "./file1.js",
    file2: "./file2.js",
    file3: "./file3.js"
  },
  plugins: [
    new HTMLWebpackPlugin({
      title: 'Code Splitting'
    })
  ],
  output: {
    filename: '[name].bundle.js',
    path: path.resolve(__dirname, 'dist')
  }
};

打包结果:dist/file1.bundle.js、dist/file2.bundle.js、dist/file3.bundle.js。

2、CommonsChunkPlugin插件

防止重复,移除重复的依赖模块。

const path = require('path');
const webpack = require('webpack');
const HTMLWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: {
    file1: "./file1.js",
    file2: "./file2.js",
    file3: "./file3.js"
  },
  plugins: [
    new HTMLWebpackPlugin({
      title: 'Code Splitting'
    }),
    new webpack.optimize.CommonsChunkPlugin({
      name: 'common' // 指定公共bundle的名称。
    })
  ],
  output: {
    filename: '[name].bundle.js',
    path: path.resolve(__dirname, 'dist')
  }
};

3、动态import
ES2015模块加载规范定义了import()方法来运行时动态地加载ES2015模块,webpack2将import()作为分割点并将被请求的模块放到一个单独的chunk中,import()接收模块名作为参数,并返回一个Promise。

//webpack.config.js

const path = require('path');
const HTMLWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: {
    index: './src/index.js'
  },
  plugins: [
    new HTMLWebpackPlugin({
      title: 'Code Splitting'
    })
  ],
  output: {
    filename: '[name].bundle.js',
    chunkFilename: '[name].bundle.js',
    path: path.resolve(__dirname, 'dist')
  }
};
// src/index.js

function getComponent() {
  return import(/* webpackChunkName: "lodash" */ 'lodash').then(_ => {
   var element = document.createElement('div');
   element.innerHTML = _.join(['Hello', 'webpack'], ' ');
   return element;
 }).catch(error => 'An error occurred while loading the component');
}

getComponent().then(component => {
  document.body.appendChild(component);
});

在注释中使用了webpackChunkName,这样做会导致我们的bundle被命名为lodash.bundle.js,而不是[id].bundle.js 。

4、commonjs的require.ensure
require.ensure(dependencies, callback);
require.ensure方法确保回调时,dependencies里的每个依赖都会被同步的引入,require作为参数传递到回调函数中。require.ensure只加载模块,并不执行。

require.ensure(["module-a", "module-b"], function(require) {
  var a = require("module-a");
  // ...
});

5、AMD的require
reuqire(dependices, callback);
调用时,所有依赖都被加载,callback函数中能得到依赖暴露出来的对象。AMD的require加载并执行模块,在webpack中,模块由左向右执行,允许省略回调函数。

require(["module-a", "module-b"], function (a, b) {// ...})

6、按需加载

像d3,echarts,lodash,antd,比较大,没必要全部加载。

let d3 = Object.assign({},
    require("d3-selection"),
    require("d3-transition"),
    require("d3-shape")
);

//为d3.event加上get方法,获取d3Selection.event
Object.defineProperty(d3,"event", {get: function() { return d3Selection.event; }});
// 引入echarts主模块

var echarts = require('echarts/lib/echarts');

//引入柱状图
require('echarts/lib/component/bar');

//引入提示框和标题组件
require('echarts/lib/component/tooltip');
require('echarts/lib/component/title');

//基于准备好的dom,初始化echarts

var myChart = echarts.init(document.getElementById('main'));

//绘制图表
myChart.setOption({
    title: { text:'走向社会'},
    tooltip:{},
    xAxis:{ data:['2013年','2014年','2015年','2016年','2017年','2018年']},
    yAxis: {},
    series: [{
        name: '技术积累',
        type: 'bar',
        data: [5,8,11,15,19,23]
    }]
})
## 安装lodash相关插件
cnpm i -S lodash-webpack-plugin babel-plugin-lodash
//配置webpack.config.js

var LoashModuleReplacementPlugin = require('lodash-webpack-plugin');

{
    ...,
    plugins: [
        ...,
        new LodashModuleReplacementPlugin({
            'collections': true,
            'shorthands': true
        })
    ]
}
//在.babelrc中增加lodash配置
{
    "plugins": [..., "lodash"]
}
//引用一次,即可调用任意函数,而不会完全打包

import  _  from 'lodash';
_.add(1,2); //打包时,只会引入add函数

八、缓存不常修改的库文件
不要在开发环境下使用[chunkhash],因为这会增加编译时间。将开发和生产模式的配置分开,并在开发模式中使用[name].js的文件名,在生产模式中使用[name].[chunkhash].js文件名。

1、output.filename

var path = require("path");
var webpack = require("webpack");
var ChunkManifestPlugin = require("chunk-manifest-webpack-plugin");
var WebpackChunkHash = require("webpack-chunk-hash");

module.exports = {
  entry: {
    vendor: "./src/vendor.js",
    main: "./src/index.js"
  },
  output: {
    path: path.join(__dirname, "build"),
    filename: "[name].[chunkhash:8].js",
    chunkFilename: "[name].[chunkhash:8].js"
  },
  plugins: [
    new webpack.optimize.CommonsChunkPlugin({
      name: ["vendor", "manifest"],
      minChunks: Infinity
    }),
    new webpack.HashedModuleIdsPlugin(),
    new WebpackChunkHash(),
    new ChunkManifestPlugin({
      filename: "chunk-manifest.json",
      manifestVariable: "webpackManifest"
    })
  ]
};

提升webpack打包速度的更多相关文章

  1. 优化webpack打包速度方案

    基本原理要么不进行打包:要么缓存文件,不进行打包:要么加快打包速度. 不进行打包方案: 1,能够用CDN处理的用CDN处理,比如项目引入的第三方依赖jquery.js,百度编辑器 先进行打包或者缓存然 ...

  2. webpack打包速度和性能再次优化

    一. 改单dll为双dll 因为上图原因,使用CommonsChunkPlugin时,导致其打包出来的vendors.js内的模块ID会因为其他文件引用模块数量的变化而变化. 所以现利用DllPlug ...

  3. 优化Webpack打包速度

    1. Webpack 可以配置 externals 来将依赖的库指向全局变量,从而不再打包这个库,比如对于这样一个文件:   import React from 'react'; console.lo ...

  4. 解决webpack打包速度慢的解决办法

    技巧1 webpack在打包的时候第一次总是会做很长的准备工作,包括加载插件之类的.在刚接触webpack的时候总是webpack一下-测一下-改一下-再webpack一下,这种方式最后让很多人崩溃了 ...

  5. Webpack 打包优化之速度篇

    在前文 Webpack 打包优化之体积篇中,对如何减小 Webpack 打包体积,做了些探讨:当然,那些法子对于打包速度的提升,也是大有裨益.然而,打包速度之于开发体验和及时构建,相当重要:所以有必要 ...

  6. webpack打包性能分析

    1. 如何定位webpack打包速度慢的原因 首先需要定位webpack打包速度慢的原因,才能因地制宜采取合适的方案,我们可以在终端输入: webpack --profile --json > ...

  7. Webpack 打包太慢? 试试 Dllplugin

    webpack在build包的时候,有时候会遇到打包时间很长的问题,这里提供了一个解决方案,让打包如丝般顺滑~ 1. 介绍 在用 Webpack 打包的时候,对于一些不经常更新的第三方库,比如 rea ...

  8. vuecli中配置webpack加快打包速度

    webpack4中webpack 的DllPlugin插件可以将常见的库文件作为dll文件来,每次打包的时候就不用再次打包库文件了. 但是游鱼西在vuecli中已经去除这个选项,意识到带来的打包速度提 ...

  9. 如何将 iOS 工程打包速度提升十倍以上

    如何将 iOS 工程打包速度提升十倍以上   过慢的编译速度有非常明显的副作用.一方面,程序员在等待打包的过程中可能会分心,比如刷刷朋友圈,看条新闻等等.这种认知上下文的切换会带来很多隐形的时间浪费. ...

随机推荐

  1. 【系统架构】软件核心复杂性应对之道-领域驱动DDD(Domain-Driven Design)

    前言 领域驱动设计是一个开放的设计方法体系,目的是对软件所涉及到的领域进行建模,以应对系统规模过大时引起的软件复杂性的问题,本文将介绍领域驱动的相关概念. 一.软件复杂度的根源 1.业务复杂度(软件的 ...

  2. CF618G(利用浮点数精度+矩乘优化DP)

    这题真的太神辣,%了一发题解,原来还能这么搞QWQ 设\(A_{i,j}\)表示不加任何限制时,第\(i\)个格子会出现权值为\(j\)的史莱姆的概率,则有: \[A_{i,j}=A_{i,j-1}* ...

  3. Zabbix通过JMX方式监控java中间件

    Zabbix2.0添加了支持用于监控JMX应用程序的服务进程,称为“Zabbix-Java-gateway”:它是用java写的一个程序. 工作原理: zabbix_server想知道一台主机上的特定 ...

  4. 消息框MessageBox+遍历控件

    消息对话框:主要用来显示信息,也可以警告.用户确认取消等. MessageBox.Show("展示内容","标题",MessageBoxButtons.按钮种类 ...

  5. linux basic ------ dd 和 cp 的区别

    问:看了一些关于dd和cp的命令,但是我始终无法明白dd和cp之间有什么不同?不是都可以看成是备份的作用么?还有什么区别呢?答:1.dd是对块进行操作的,cp是对文件操作的. 2.比如有两块硬盘,要将 ...

  6. 分布式监控系统开发【day38】:监控trigger表结构设计(一)

    一.需求讨论 1.zabbix触发器的模板截图 1.zabbix2.4.7 2.zabbix3.0 2.模板与触发器关联的好处 好处就是可以批量处理,比如我说我有1000机器都要监控cpu.内存.IO ...

  7. SNMP学习——v3 VACM

    目录: ☆ SNMPv3视图访问控制模型    ☆ SNMPv3报文格式    ☆ VACM参数    ☆ Context Table    ☆ Security To Group Table     ...

  8. Typora使用说明(记录总结)

    目录 区域元素 YAML FONT Matters 菜单 段落 标题 引注 序列 可选序列 代码块 数学块 表格 脚注 水平线 特征元素 链接 超链接 内链接 相关链 URLs 图片 斜体 加粗 删除 ...

  9. Python复习笔记(十一)TCP/IP协议

    1. TCP/IP协议简介 帧头: mac地址, 网卡上的序列号 2. wireshark使用 分析一个数据是否发送, 是否是网络问题 ip.dst == 192.168.0.137 and udp ...

  10. java8 按条件过滤集合

    //黄色部分为过滤条件list.stream().filter(user-> user.getId() > 5 && "1组".equals(user. ...