关于webpack优化,你须要知道的事(上篇)

前言

webpack 是一个优秀的打包工具,其自己为咱们作了大量优化,同时也为咱们提供了大量的配置项让咱们能够自定义,从而有优化空间。javascript

在讲 webpack 优化篇以前,因为楼主主要以 vue 脚手架开始的,并且是已经升级为 webpack4 以后的优化,若是对 vue脚手架配置不太了解的同窗。能够看我上一篇文章 如何优雅的升级到webpack4,或者直接看 webpack3 vue脚手架注解

下面我先讲讲vue脚手架为咱们作的一些优化,不喜欢看的请跳过,而后会讲如何在优化的基础上升华一下,内容从浅到深,可是全部的方法都通过楼主考证,内容较长,请自带板凳瓜子。css

vue-cli 脚手架自带优化

babel

Babel 是一个 JavaScript 编译器,能将 ES6 代码转为 ES5 代码,让你使用最新的语言特性而不用担忧兼容性问题,而且能够经过插件机制根据需求灵活的扩展。这里我不讲babel ,而是讲官方用的插件 transform-runtime,对应的插件全名叫作 babel-plugin-transform-runtime,其做用是减小冗余代码,究竟是怎么减小的呢?html

例如在转换 class extent 语法时会在转换后的 ES5 代码里注入 _extent 辅助函数用于实现继承:前端

function _extent(target) {
  for (var i = 1; i < arguments.length; i++) {
    var source = arguments[i];
    for (var key in source) {
      if (Object.prototype.hasOwnProperty.call(source, key)) {
        target[key] = source[key];
      }
    }
  }
  return target;
}

这会致使每一个使用了 class extent 语法的文件都被注入重复的_extent 辅助函数代码,babel-plugin-transform-runtime 的做用在于不把辅助函数内容注入到文件里,而是注入一条导入语句:vue

var _extent = require('babel-runtime/helpers/_extent');

这样能减少 Babel 编译出来的代码的文件大小。
注意:babel-plugin-transform-runtime 必须和 babel-runtime 须要配套使用java

说来惭愧,楼主试了一下,把这个插件去掉,生成文件的hash和大小并无变化(汗,别砸,翻资料webpack 标准入门前端工程化-webpack篇之babel-polyfill与babel-runtime(三)上有写,并且脚手架上有)。后来发现,楼主的代码中并无es6。后来换了一个大项目,作了对比
babel对比
能够发现,图右边是去掉插件的。体积明显大了一点。使用此插件能够减小重复代码,缩小项目体积。node

缩小文件搜索范围

loader

使用 Loader 时能够经过 test 、 include 、 exclude 三个配置项来命中,对于咱们的项目大部分都是 js,下面看看官方脚手架 js 的 babel-loader:react

module.exports = {
    // ...
    module: {
        rules: [
            // ...
           {
                test: /\.js$/,
                loader: 'babel-loader',
                include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')]
           },
        ]
    }
}

因为经过 npm 安装的第三方的库,都是通过 webpack 打包 es5 化了,因此这里就能够只对 include 包括的文件使用 babel-loader 解析webpack

注意了。因为 css、less 的引入是要插入到js中的,因此并不适用于这个(把 node_modules 排除在外)方法。说到这里,多说一句,也是曾经很困扰个人 css 的 loader 解析顺序,use 的 loader 解析顺序跟数组的位置是反着的,以 less 为例,具体来说git

module.exports = {
    // ...
    module: {
        rules: [
            // ...
           {
                test: /\.less$/,
                // less 文件的处理顺序为先 less-loader 再 css-loader 再 vue-style-loader
                use: [
                    // style-loader 会把 CSS 代码转换成字符串后,注入到 JavaScript 代码中去,
                    'vue-style-loader',
                    // css-loader 会找出 CSS 代码中的 @import 和 url() 这样的导入语句,告诉 Webpack 依赖这些资源。同时还支持 CSS Modules、压缩 CSS 等功能。处理完后再把结果交给 vue-style-loader 去处理。
                    {
                        loader: 'css-loader',
                        options: {
                            sourceMap: config.dev.cssSourceMap
                        }
                    },
                    //经过 less-loader 把 less 源码转换为 CSS 代码,再把 CSS 代码交给 css-loader 去处理。
                    {
                        loader: 'less-loader'
                    }
                ] 
            },
        ]
    }
}

关于缩小范围增长命中这个思想,还能够作不少事情,这里只讲了vue脚手架优化作的事情,更多配置请日后看,看我如何自定义的

node 选项

webpack 的官方脚手架里面的node选项能够防止node包,还有 setImmediate 的 profill注入到代码中

node: {
    // prevent webpack from injecting useless setImmediate polyfill because Vue
    // source contains it (although only uses it if it's native).
    setImmediate: false,
    // prevent webpack from injecting mocks to Node native modules
    // that does not make sense for the client
    dgram: 'empty',
    fs: 'empty',
    net: 'empty',
    tls: 'empty',
    child_process: 'empty'
}

好很差,看疗效。那么具体的疗效怎么样呢,楼主一样的代码,作了对比,效果以下:
node选项对比

经过对比能够看到,两次打包css的hash值所有变了,js部分hash发生改变(这个打包没看出js变化,可是另外一个项目的部分js的hash变了)。整体打出来的包的体积相差不大。去掉node选项打包时间差异不明显,因此用不用,见仁见智吧。我看create-react-app中也使用了,因此仍是建议使用吧,知道更多的,能够留言区讨论。

js、css 压缩

css 压缩这个就很少说了,你们都懂,

值得一提的是因为 UglifyJsPlugin 插件升级到1.0以后有了 parallel选项,开启了多线程压缩

new UglifyJsPlugin({
  uglifyOptions: {
    compress: {
      warnings: false
    }
  },
  sourceMap: config.build.productionSourceMap,
  parallel: true  // 开启多线程压缩
})

这两个插件都有配置项,合理配置能够优化项目。后面会讲。

代码分割

代码分割就是将动态引入的代码分割成一个一个的代码块(chunk),根据需求加载到html上。注意:要使用代码分割功能,在项目中要配合使用组件、路由懒加载的方式(能够经过import实现)

webpack4 的 mode 为 production 时,默认会对代码进行分割。楼主看了 webpack3 的代码分割方式是使用 CommonsChunkPlugin 插件,目的就是分割出几类代码:

  1. vendor 也就是第三方库打包这里。
  2. manifest 当编译器开始执行、解析和映射应用程序时,它会保留全部模块的详细要点。这个数据集合称为 "Manifest"
  3. app 这个是代码中的公共部分

HashedModuleIdsPlugin

嗯 webpack 生成 js 的 hash 是如何计算我并不清楚,可是若是不用这个插件的话,全部生成 js 的 hash 是同样的,并且只要有一点点改动,全部文件的 hash 值都会变化。那形成什么样的结果呢?

好比你只改了 b 页面的 js 里的一行代码,若是不用此插件的话,全部页面的 js 的 hash 所有会变化,浏览器要从新请求所有的js。性能浪费到使人发指。而使用了 HashedModuleIdsPlugin 这个插件,只有你改动的那个 chunk 的 hash会发生变化,其余不变,因为浏览器的缓存机制,浏览器只从新请求改动的js。是否是很棒。并且上一小节对代码分割那里的分割方式,也是为了把不常常变更的文件单独打包,hash 能够保持不变。

使用方法也很简单

new webpack.HashedModuleIdsPlugin(),

什么?为何就算去掉 HashedModuleIdsPlugin 插件 用脚手架第一次打包项目生成的 js 的 hash 不所有同样,并且改动以后,也不是所有发生变化啊。这个也是楼主遇到的问题。楼主不用脚手架搭建的项目,js 的 hash 是同样的,知道为何出现初始打包的 js hash 值为何不所有同样的同窗,欢迎评论区讨论。

做用域提高(scope hoisting)

过去 webpack 打包时的一个取舍是将 bundle 中各个模块单独打包成闭包。这些打包函数使你的 JavaScript 在浏览器中处理的更慢。相比之下,一些工具像 Closure Compiler 和 RollupJS 能够提高(hoist)或者预编译全部模块到一个闭包中,提高你的代码在浏览器中的执行速度。
个插件会在 webpack 中实现以上的预编译功能。

new webpack.optimize.ModuleConcatenationPlugin()

这种连结行为被称为“做用域提高(scope hoisting)

记住,此插件仅适用于由 webpack 直接处理的 ES6 模块。在使用转译器(transpiler)时,你须要禁用对模块的处理(例如 Babel 中的 modules 选项)。

css 优化

因为css加载不会阻塞dom的解析,因此把css抽取出来。不占用js的大小是一个明智的选择 OptimizeCSSPlugin 插件作的就是这个,而且代码复用,会减少css体积

new OptimizeCSSPlugin({
  cssProcessorOptions: config.build.productionSourceMap
    ? { safe: true, map: { inline: false } }
    : { safe: true }
}),

总结

整体来说 webpack 为咱们作的优化有

  1. babel-plugin-transform-runtime 插件去除重复垫片代码
  2. module.rules 的 js 解析,使用 include 提升命中
  3. node 选项,防止 node 的自带包(dgram、fs、net、tls、child_process)注入到咱们的代码中
  4. js、css 压缩,代码分割,公共部分抽离
  5. 维持打包后不变chunk的hash值不变
  6. 做用域提高(scope hoisting)
  7. css 抽离。公共部分抽离

大体就这样了,有没有讲到的也请评论区提出,那么如何在此基础上作优化呢,这个也许是你们都很关心的问题。接下来我会在 《关于webpack优化,你须要知道的事(下篇)》讲到。

相关文章
相关标签/搜索