回首相望于时光,毕业至今已三年有余。曾懵懂、莽撞,拘泥于小小空间,以此足矣。然其自强不息,不满于此,从事前端工作。而后工于技术。现追随潮流,做年度总结文章一篇,以此记录...
2018 年前,我还在公司做着 jQuery 与 PHP 的项目。而后公司领导层做了关于技术选型的讨论,但并没有做技术选型的定论,网上搜索许久,每种技术各有说辞,无意间看到一篇关于 React 的文章,适合我们的项目,感觉挺有挑战性的,于是我决心在业余之时学习先学习下 React(万一用上了呢)。 我花了两个月的时间学会了 React 技术的使用,包括其中的 API、状态管理、路由等。恰逢学习之际,公司技术选型中有一个小项目是想先用 React 试用下。于是给了我一个练手的机会,让我基本上掌握了 React 的使用,但对于 redux 状态管理并不是很理解,只知道他的作用于用法,直到项目结束,我依旧处于半梦半醒的状态。 有人说慢慢学习、多加练习,终究会恍然大悟... 可是文章到这,你以为接下来我要讲述自己是如何认真学习 React 的么?
.........
并没有,一个 Vue 项目来了... 都说 Vue 比较容易上手,于是我花了十天的时间通读了 Vue 的 API,并搭建了 Vue 项目,其中包括 Sass 管理 css,Vuex 状态管理,webpack 打包等等的配置。果然,跟 React 相比,是容易了许多。我以为自己可以沉浸在这小小的成就中,可以空余些时间多多学习其他没用到的 API,或者学习下 webpack 的相关内容。然而,我以为的以为终究不是我以为。
项目结束后,偶尔参与下这个项目的需求。由于某种原因我又转战了 React 项目。经过 Vuex 的学习使用,对于状态管理方面的东西理解了很多,在实际使用中,也简单了许多。
2018 年是我辞职转战团队的一年,我于 8 月份来到了现在的这个团队。说实话我很喜欢现在团队的氛围,90 后占据了主要的部分,青春洋溢的色彩。进入公司后,依旧做着 React 项目,对公司的一个 React15.0 + Webpack1.12.0 + Ant.design 的老项目进行优化,由于项目的代码量极大,导致打包后的10M以上,所以我们采取了按需加载的方案,用 require.ensure 来拆分项目代码,结果代码量没降反而增加很多。于是使用了 webpack 插件 webpack-bundle-analyzer 进行了分析。
webpack-bundle-analyzer
使用 webpack-bundle-analyzer 插件分析项目分析,优化代码,提升性能
-
安装依赖包:
npm install webpack-bundle-analyzer --save-dev
-
webpack 配置:
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
plugins: [
new BundleAnalyzerPlugin({
// 可以是`server`,`static`或`disabled`。
// 在`server`模式下,分析器将启动HTTP服务器来显示软件包报告。
// 在“静态”模式下,会生成带有报告的单个HTML文件。
// 在`disabled`模式下,你可以使用这个插件来将`generateStatsFile`设置为`true`来生成Webpack Stats JSON文件。
analyzerMode: 'server',
// 将在“服务器”模式下使用的主机启动HTTP服务器。
analyzerHost: '127.0.0.1',
// 将在“服务器”模式下使用的端口启动HTTP服务器。
analyzerPort: 8866,
// 路径捆绑,将在`static`模式下生成的报告文件。
// 相对于捆绑输出目录。
reportFilename: 'report.html',
// 模块大小默认显示在报告中。
// 应该是`stat`,`parsed`或者`gzip`中的一个。
// 有关更多信息,请参见“定义”一节。
defaultSizes: 'parsed',
// 在默认浏览器中自动打开报告
openAnalyzer: true,
// 如果为true,则Webpack Stats JSON文件将在bundle输出目录中生成
generateStatsFile: false,
// 如果`generateStatsFile`为`true`,将会生成Webpack Stats JSON文件的名字。
// 相对于捆绑输出目录。
statsFilename: 'stats.json',
// stats.toJson()方法的选项。
// 例如,您可以使用`source:false`选项排除统计文件中模块的来源。
// 在这里查看更多选项:https: //github.com/webpack/webpack/blob/webpack-1/lib/Stats.js#L21
statsOptions: null,
logLevel: 'info'
})
]
}
commons-chunk-plugin
经过分析,发现拆分后得每个 js 文件中都包含了重复的代码,比如 antd 之类的插件。,考虑将公共的代码提取出来,减少总体打包之后的代码,并且提取的公共代码也比较稳定,于是引入了 webpack 官方提供的 CommonsChunkPlugin 插件(但是在 webpack v4 中已经被移除,使用了 SplitChunksPlugin 插件)。 webpack 网站做了CommonsChunkPlugin 插件的
-
相关说明:
CommonsChunkPlugin 插件,是一个可选的用于建立一个独立文件(又称作chunk)的功能,这个文件包括多个入口 chunk 的公共模块。它的作用是通过将公共模块拆出来,最终合成的文件能够在最开始的时候加载一次,便存到缓存中供后续使用。这个带来页面速度上的提升,因为浏览器会迅速将公共的代码从缓存中取出来,而不是每次访问一个新页面时,再去加载一个更大的文件。。
-
安装依赖包
npm install commons-chunk-plugin --save
-
使用及配置说明:
new webpack.optimize.CommonsChunkPlugin({
name: string, // or
names: string[],
// 这是 common chunk 的名称。已经存在的 chunk 可以通过传入一个已存在的 chunk 名称而被选择。
// 如果一个字符串数组被传入,这相当于插件针对每个 chunk 名被多次调用
// 如果该选项被忽略,同时 `options.async` 或者 `options.children` 被设置,所有的 chunk 都会被使用,
// 否则 `options.filename` 会用于作为 chunk 名。
// When using `options.async` to create common chunks from other async chunks you must specify an entry-point
// chunk name here instead of omitting the `option.name`.
filename: string,
// common chunk 的文件名模板。可以包含与 `output.filename` 相同的占位符。
// 如果被忽略,原本的文件名不会被修改(通常是 `output.filename` 或者 `output.chunkFilename`)。
// This option is not permitted if you're using `options.async` as well, see below for more details.
minChunks: number|Infinity|function(module, count) => boolean,
// 在传入 公共chunk(commons chunk) 之前所需要包含的最少数量的 chunks 。
// 数量必须大于等于2,或者少于等于 chunks的数量
// 传入 `Infinity` 会马上生成 公共chunk,但里面没有模块。
// 你可以传入一个 `function` ,以添加定制的逻辑(默认是 chunk 的数量)
chunks: string[],
// 通过 chunk name 去选择 chunks 的来源。chunk 必须是 公共chunk 的子模块。
// 如果被忽略,所有的,所有的 入口chunk (entry chunk) 都会被选择。
children: boolean, // 如果设置为 `true`,所有公共 chunk 的子模块都会被选择
deepChildren: boolean,
// 如果设置为 `true`,所有公共 chunk 的后代模块都会被选择
async: boolean|string,
// 如果设置为 `true`,一个异步的 公共chunk 会作为 `options.name` 的子模块,和 `options.chunks` 的兄弟模块被创建。
// 它会与 `options.chunks` 并行被加载。
// Instead of using `option.filename`, it is possible to change the name of the output file by providing
// the desired string here instead of `true`.
minSize: number,
// 在 公共chunk 被创建立之前,所有 公共模块 (common module) 的最少大小。
});
通过 require.ensure 等方式拆分代码,需要借助 children 来提取公共模块,代码会拼接到 entry chunk 上。如果 async 为 true 的时候,会重新创建一个文件来放置。
entry: {
app: './entry'
},
plugins: [
new webpack.optimize.CommonsChunkPlugin({
children: true,
async: 'async-common', // 定义 chunk 名字
minChunks: 2,
})
]
extract-text-webpack-plugin
使用了 webpack 插件 extract-text-webpack-plugin(但是在 webpack v4中将不再使用,替换成mini-css-extract-plugin 插件),将 css 样式进行了抽离,需要将 style-loader 删掉,否则会导致模块构建失败,参考 github issues
-
安装依赖包
npm install extract-text-webpack-plugin --save
-
使用及配置:
const ExtractTextPlugin = require('extract-text-webpack-plugin');
plugins: [
new ExtractTextPlugin('[name].css')
].
module:{
loaders: [{
test: /\.css$/,
loader: ExtractTextPlugin.extract('css-loader')
}, {
test: /\.less/,
loader: ExtractTextPlugin.extract('css-loader!less-loader?')
}]
}
- children + async
使用 children + async 的方式进行打包(需要添加参数 allChunks),最后只有 8.7 M
plugins: [
new ExtractTextPlugin('[name].css',{
allChunks : true
}),
new webpack.optimize.CommonsChunkPlugin({
children: true,
async: 'async-common',
minChunks: 3,
})
]