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. dajngo cache,throttling

    缓存 背景介绍: 动态网站的问题就在于它是动态的. 也就是说每次用户访问一个页面,服务器要执行数据库查询,启动模板,执行业务逻辑以及最终生成一个你所看到的网页,这一切都是动态即时生成的. 从处理器资源 ...

  2. 使用Spring Boot Actuator将指标导出到InfluxDB和Prometheus

    使用Spring Boot Actuator将指标导出到InfluxDB和Prometheus   Spring Boot Actuator是Spring Boot 2发布后修改最多的项目之一.它经过 ...

  3. (二分查找 结构体) leetcode33. Search in Rotated Sorted Array

    Suppose an array sorted in ascending order is rotated at some pivot unknown to you beforehand. (i.e. ...

  4. EXCEL(1)级联下拉框

    EXCEL级联下拉框 http://jingyan.baidu.com/article/3c343ff756e0cf0d377963f9.html 在输入一些多级项目时,如果输入前一级内容后,能够自动 ...

  5. windows telnet 模拟 http请求

    1. 开启windows自带的telnet客户端(控制面板 --> 程序 --> 启用或关闭windows功能 --> ) 2. 打开cmd,使用Telnet客户端 3. 按ctrl ...

  6. Centos7的目录结构

    CentOS 目录结构 : /: 根目录,一般根目录下只存放目录,不要存放文件,/etc./bin./dev./lib./sbin应该和根目录放置在一个分区中/bin:/usr/bin: 可执行二进制 ...

  7. An Apple a day keeps the doctor away

    An apple a day keeps the doctor away. 一天一苹果,不用请医生. 活学活用:apple as like as an apple to an oyster 毫无相同之 ...

  8. [物理学与PDEs]第1章第7节 媒质中的 Maxwell 方程组 7.1 媒质中的 Maxwell 方程组

    1.媒质的极化 (1) 束缚电荷: 被束缚在原来位置上的电荷. (2) 在电磁场中, 束缚电荷会有一微小的运动, 而产生电偶极矩. 此即称为媒质的极化. (3) 设电极化强度 (单位体积的电偶极矩) ...

  9. java8 list和map的forEach

    list forEach示例 public class HelloWorld { public static void main(String[] args) { List<User> l ...

  10. vue封装axios方法推荐)

    目录结构: api.js export default { myTopic: '/api/subscribe-data/post/cat' } request.js import axios from ...