webpack打包优化

407 阅读4分钟

webpack是一个JavaScript应用程序的静态模块打包器。下面从为什么、干了啥来说说我对它的理解。

为什么要使用webpack打包?

将ES6、ES7语法转换成浏览器支持的语法、生成CSS3前缀、代码压缩、编译less sass、编译jsx vuex、处理图片等。

webpack基本配置

创建一个webpack.config.js文件,以下是一个react工程项目的基本配置

const path = require('path');
module.exports = {
  mode: "development"
  entry: {
    inde: './src/index.js'
  },
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [{
        test: /\.css$/,
        use: ['style-loader', 'css-loader'],
    }, {
        test: /\.less$/,
        use:["style-loader","css-loader","less-loader"]
       // 加载时顺序从右向左 
    },
    {
        test: /\.(png|svg|jpg|gif)$/,
        use: ['file-loader']
    },
    {
      test: /\.(js|jsx)$/,
      loader: 'babel-loader',
      exclude: /node_modules/
    }]
  },
  devServer: {
    contentBase: './dist',
    hot: true
  },
  plugins: [
    new CleanWebpackPlugin(),//每次编译都会把dist下的文件清除,我们可以在合适的时候打开这行代码,例如我们打包的时候,开发过程中这段代码关闭比较好
    new webpack.HotModuleReplacementPlugin(),
    new HtmlWebpackPlugin({
        template: 'src/index.html' //使用一个模板
    }),
  ]
}

然后在webpack.json的script中配置start build命令,现在就可以打包了。目前打包时间是27s

图片alt
基本配置打包时间

"start": "webpack-dev-server --open",
"build": "webpack"

随着项目代码越来越多,开启项目服务和打包会越来越慢,这里就需要对配置进行优化,下面来说说我优化一个前年的老项目,打包时间从27s到4.5s
第一步:多进程多实例打包,我采用的是对所有项目都比较友好的happypack插件,其实还有thread-loader和parallel-webpack,但是当前的项目不太合适使用后2个插件,后续我会补充这2个插件的效果。

const HappyPack = require('happypack');

// loader部分
// 在loader的babel前加上happypack/,后加上?id=jsx
{ test: /\.(js|jsx)$/, exclude: /node_modules/, loader: 'happypack/babel?id=jsx' },

// plugins部分
// 将babel-loader编译的过程放进多进程处理中,其实还可以处理css-loader和file-loader,这里就不写了
// id对应的是loader部分的?id=jsx,loader对应的是babel全称babel-loader
new HappyPack({
  id: 'jsx',
  loaders: [ 'babel-loader' ]
}),

添加了多进程打包后,打包时间缩短到10s

图片ALT
加了happypack打包时间

打包时间减少了17s,非常惊人,但是我们还可以再减少打包时间,往下看:

webpack-parallel-uglify-plugin

这个插件可以帮助有很多入口点的项目加速他们的构建。webpack提供的UglifyJS插件在每个输出文件上依次运行。这个插件运行uglify与一个线程并行为每个可用的cpu。这可以显著减少构建时间,因为小型化非常耗费CPU资源

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

new ParallelUglifyPlugin({
      uglifyJS: {
        output: {
          beautify: false,
          comments: false,
        },
        compress: {
          warnings: false,
          drop_console: true,
          collapse_vars: true,
          reduce_vars: true,
        }
      }
    }),

图片ats
加了并行压缩后的打包时间
现在打包时间只要8s了,这里还没有结束...

html-webpack-externals-plugin

像react,react-dom等等依赖包比较大,我们可以引用cdn的资源,不用本地编译,在插件中配置他们的cdn地址,最后html文件中就会以script标签引用这些依赖

const HtmlWebpackExternalsPlugin = require('html-webpack-externals-plugin')

new HtmlWebpackExternalsPlugin({
  externals: [
    {
      module: 'react',
      entry: 'https://cdnjs.cloudflare.com/ajax/libs/react/15.3.1/react.min.js',
      global: 'react',
    },
    {
      module: 'react-dom',
      entry: 'https://cdn.bootcss.com/react-dom/15.3.1/react-dom.js',
      global: 'react-dom',
    },
    {
      module: 'moment',
      entry: 'https://cdn.bootcss.com/moment.js/2.22.1/moment.js',
      global: 'moment'
    }
  ]
}),

打包后的html文件

<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.1/react.min.js"></script>
<script type="text/javascript" src="https://cdn.bootcss.com/react-dom/15.3.1/react-dom.js"></script>
<script type="text/javascript" src="https://cdn.bootcss.com/moment.js/2.22.1/moment.js"></script>
<script type="text/javascript" src="/myAccount/js/vendor.e4c0ae97.js"></script></body>

图片ats
加了cnd引用后打包时间

现在打包时间是6s了,最后再加个dll,看看
创建一个webpack.dll.config.js文件,配置如下:

const path = require("path");
const webpack = require("webpack");
const {
  BundleAnalyzerPlugin
} = require('webpack-bundle-analyzer')

const vendor = [
  "react",
  "react-dom",
  "react-router-dom",
  "jquery",
  "js-md5",
  "moment",
  "react-router",
  "react-weui",
  "weui",
  "redux",
  "immutable",
];

const dllPath = path.join(__dirname, 'dll');

module.exports = {
  entry: {
    dll: vendor
  },
  output: {
    path: dllPath,
    filename: "[name].js",
    library: "_dll_[name]"
  },
  plugins: [
		new webpack.DllPlugin({
	    name: "_dll_[name]",
			path: path.join(__dirname, 'dll','manifest.json'),
	  }),
		new BundleAnalyzerPlugin({
			analyzerMode: 'static'
		}),
	]
}

在webpack.config.js中通过webpack.DllReferencePlugin引用,配置如下:

new webpack.DllReferencePlugin({
    context: __dirname,
    manifest: require('./dll/manifest.json')
}),

图片ats
加了dll后打包时间

又减少了1s webpack里面有个modules配置,"modules": false时,就是没有引用的部分不会打包进去,比如a.js中有10个函数,b.js只引用了a.js中的2个方法,另外8个方法就不会打包了。在.babelrc中配置"modules": false

{
  "presets": ["react", "es2015"],
  "env": {
    "dev": {
        "plugins": [["react-transform", {
           "transforms": [{
             "transform": "react-transform-hmr",
             "imports": ["react"],
             "locals": ["module"]
           }]
         }]]
    }
  },
  "modules": false
}

图片alt
去shaking后的打包时间

简直是惊呆了,现在打包时间只要4s了。