12

Webpack 4给我们带来了一些改变。包括更快的打包速度,引入了SplitChunksPlugin插件来取代(之前版本里的)CommonsChunksPlugin插件。在这篇文章中,你将学习如何分割你的输出代码,从而提升我们应用的性能。

代码分割的理念

首先搞明白: webpack里的代码分割是个什么鬼? 它允许你将一个文件分割成多个文件。如果使用的好,它能大幅提升你的应用的性能。其原因是基于浏览器会缓存你的代码这一事实。每当你对某一文件做点改变,访问你站点的人们就要重新下载它。然而依赖却很少变动。如果你将(这些依赖)分离成单独的文件,访问者就无需多次重复下载它们了。

使用webpack生成一个或多个包含你源代码最终版本的“打包好的文件”(bundles),(概念上我们当作)它们由(一个一个的)chunks组成。

入口(Entry)

入口定义了我们的应用代码开始执行的那个文件,webpack从这个文件开始打包。你能定义一个入口点(常见于单页应用 - Single-Page Application), 或者多个入口点(常见于多页应用 - Multiple-Page Application)。

定义一个入口点就生成一个chunk。如果你只是用字符串的方式定义了一个入口点,其就被命名为main。如果你用对象的方式定义多个入口点,其就被命名为入口对象中的键值。下面两个例子是等价的:

entry: './src/index.js'
entry: {
  main: './src/index.js'
}

输出(Output)

输出对象配置webpack如何输出我们的打包(bundles)和资源(assets),以及将它们放到哪里。因为可能多于一个入口点,而只(能)指定一个输出配置。事实上我们就用chunks来给其一一命名。你能给打包输出的文件定义一个确定的名字,但既然我们想要分割我们的代码,就不能这么干。你得使用[name]来创建输出文件名的模板:

output: {
  filename: '[name].[chunkhash].bundle.js',
  path: path.resolve(__dirname, 'dist')
}

这里要注意的重要事情是 [chunkhash]: 它基于你文件的内容给每个chunk生成了一个特有的hash。它只有在你的文件内容本身变化的时候才变化。事实上,(如果内容没有变化)浏览器会缓存它。如果文件名改变了(译注:这里是指hash变化了,而hash是文件名的一部分,即意味着文件的内容变化了),浏览器就知道要重新下载了。chunkhash看起来长得就象这样子: 0c553ebfd158e16da428

如此这般,我们的main chunk就会被打包成名为 main.[chunkhash].bundle.js的文件。

SplitChunksPlugin插件

正是有了SplitChunksPlugin插件,你能在你的应用中移出一部分到单独的文件中。如果一个模块被多个chunks使用,(分割出它之后)就能很容易的在这些chunks之间共享。这正是webpack的默认行为。

// utilities/users.js

export default [
  { firstName: "Adam", age: 28 },
  { firstName: "Jane", age: 24 },
  { firstName: "Ben",  age: 31 },
  { firstName: "Lucy", age: 40 }
]
// a.js

import _ from 'lodash';
import users from './users';

const adam = _.find(users, { firstName: 'Adam' });
// b.js

import _ from 'lodash';
import users from './users';

const lucy = _.find(users, { firstName: 'Lucy' });
// webpack.config.js

module.exports = {
  entry: {
    a: "./src/a.js",
    b: "./src/b.js"
  },
  output: {
    filename: "[name].[chunkhash].bundle.js",
    path: __dirname + "/dist"
  }
};

运行之后,你会看到webpack创建了二个文件: a.[chunkhash].bundle.js和b.[chunkhash].bundle.js,每一个文件都包含了lodash库的一份拷贝: 这不太好! 我之前说过webpack的默认行为会给共享库创建分离的文件,但这涉及到异步chunks,即我们异步导入文件。我们在讨论懒加载的时候再来更多的覆盖这一主题。为了针对所有类型的chunks,我们需要稍微改改我们的webpack配置:

// webpack.config.js

module.exports = {
  entry: {
    a: "./src/a.js",
    b: "./src/b.js"
  },
  output: {
    filename: "[name].[chunkhash].bundle.js",
    path: __dirname + "/dist"
  },
  optimization: {
    splitChunks: {
      chunks: "all"
    }
  },
};

现在我们看到创建了一个附加的名叫vendors~a~b.[chunkhash].bundle.js的文件,其包含了Lodash库。事实上这全靠了配置中本身默认固有一个cacheGroups的配置项:

splitChunks: {
    chunks: "all",
    cacheGroups: {
      vendors: {
        test: /[\\/]node_modules[\\/]/,
        priority: -10
      },
      default: {
        minChunks: 2,
        priority: -20,
        reuseExistingChunk: true
      }
    }
  }

首先,vendors这一项指明了包括来自node_modules目录中的文件。其次default这一项表示默认的缓存组,包括其它共享模块。这里有一个小小的问题:发生了重复。a.[chunkhash].bundle.js和b.[chunkhash].bundle.js都包含了users.js的内容。这是因为,SplitChunksPlugin插件默认只分割超过30kb的文件。我们能很容易的更改这点:

// webpack.config.js

module.exports = {
  entry: {
    a: "./src/a.js",
    b: "./src/b.js"
  },
  output: {
    filename: "[name].[chunkhash].bundle.js",
    path: __dirname + "/dist"
  },
  optimization: {
    splitChunks: {
      chunks: "all",
      minSize: 0
    }
  }
};

默认的cache group配置使得会生成一个名为a~b.[chunkhash].bundle.js的文件。因为我们的users.js文件大大小于30kb,如果没有修改minSize属性的话,它就不会分割成一个单独的文件。在真实情形下,这是合理的,因为(如分割)并不能带来性能确实的提升,反而使得浏览器多了一次对utilities.js的请求,而这个utilities.js又是如此之小(不划算)。

我们能更进一小步,只针对utilities目录中的文件:

// webpack.config.js

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

module.exports = {
  entry: {
    a: "./src/a.js",
    b: "./src/b.js"
  },
  output: {
    filename: "[name].[chunkhash].bundle.js",
    path: __dirname + "/dist"
  },
  optimization: {
    splitChunks: {
      chunks: "all",
      cacheGroups: {
        utilities: {
          test: /[\\/]src[\\/]utilities[\\/]/,
          minSize: 0
        }
      }
    }
  }
};

现在我们打包出4个文件:a.[chunkhash].bundle.js, b.[chunkhash].bundle.js, vendors~a~b.[chunkhash].bundle.js 和 utilities~a~b.[chunkhash].bundle.js。即使我们现在设置一个全局的minSize: 0(在splitChunks对象中),默认的cache group也不会创建。这是因为被我们创建的utilities group覆盖了。utilities group默认的优先级值是0, 高于default cache group的。你可能已经注意到,默认cache group的优先级设置成了-20。

还有其它一些默认参数的设置,你能查阅SplitChunksPlugin的文档

小结

即使你只有一个入口点(见于大多数的单页应用),分离依赖到单独的文件中也是一个好主意。使用SplitChunksPlugin来达到这一目标实际上很简单,因为这是Webpack 4的默认行为,很有可能你只需设置chunks: “all”就足够了。如果你想要我说说其它有关的东西,告诉我吧。很快我们将学习如何应用懒加载来更好的提升性能, 敬请期待!


Thanksforhappy
135 声望6 粉丝

引用和评论

0 条评论