在日常的开发过程当中,若是没有手动地配置或者优化打包后尺寸,那么用户打开网站时,首屏加载会很慢,几秒后才出现内容,大大增长了用户的等待时间。css
为了解决这个问题,咱们须要从打包这个环节进行优化。常见的优化打包工具webpack
,咱们从流行的React
和Vue
库着手,尝试着优化它们。html
首先,使用create-react-app
脚手架建立一个React
应用。react
若是没有,首先须要从全局中安装脚手架,命令以下:webpack
npm install create-react-app -g
复制代码
接着新建一个文件夹,并在终端编写命令为:web
create-react-app webpack-optimiation-react
复制代码
模板建立完毕以后,它目录结构以下:npm
.
├── package.json
├── public
│ ├── favicon.ico
│ ├── index.html
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── robots.txt
├── README.md
├── src
│ ├── App.css
│ ├── App.js
│ ├── App.test.js
│ ├── index.css
│ ├── index.js
│ ├── logo.svg
│ ├── serviceWorker.js
│ └── setupTests.js
├── tree.txt
└── yarn.lock
复制代码
模板文件建好后,咱们在不一样的库中添加相同的代码,而后使用router
使他们可以正常运转。json
// src/Home.js
import React from 'react';
export default () => <h1>Home</>; 复制代码
// src/About.js
import React from 'react';
export default () => <h1>About</>; 复制代码
// src/Concat.js
import React from 'react';
export default () => <h1>Concat</h1>;
复制代码
而后使用npm
包管理器,添加react-router-dom
。后端
npm install react-router-dom -D
复制代码
以上的代码写好后,在src/index.js
中添加:跨域
import React, { lazy, Suspense } from 'react';
import { Switch, BrowserRouter as Router, Link, Route } from 'react-router-dom';
const Home = lazy(() => import('./Home'));
const Concat = lazy(() => import('./Concat'));
const About = lazy(() => import('./About'));
const NavBar = () => (
<div> <Link to='/'>Home</Link> <Link to='/about'>About</Link> <Link to='/concat'>Concat</Link> </div>
);
function App() {
return (
<Router className='App'>
<>
<NavBar />
<Suspense fallback={<div>loading...</div>}>
<Switch>
<Route path='/' exact component={Home} />
<Route path='/about' component={About} />
<Route path='/concat' component={Concat} />
</Switch>
</Suspense>
</>
</Router>
);
}
export default App;
复制代码
编写完成以后,使用npm run start
,浏览器会自动打开,并显示:浏览器
OK,运行正常。
咱们打包试试,在终端输入命令:
npm run build
复制代码
这时就会出现一个build
文件夹,这就是打包后的结果,能够给后端部署了。
build
以后的文件大小竟然有600k
之多,为了查看具体那些包体积大,就须要配置webpack
。
可是create-react-app
并不暴露webpack
配置文件,须要输入命令才能看到webapck
配置文件:
npm run eject
复制代码
它的结构目录以下:
.
├── config
│ ├── env.js
│ ├── jest
│ │ ├── cssTransform.js
│ │ └── fileTransform.js
│ ├── modules.js
│ ├── paths.js
│ ├── pnpTs.js
│ ├── webpack.config.js
│ └── webpackDevServer.config.js
├── package.json
├── public
│ ├── favicon.ico
│ ├── index.html
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── robots.txt
├── README.md
├── scripts
│ ├── build.js
│ ├── start.js
│ └── test.js
├── src
│ ├── App.css
│ ├── App.js
│ ├── App.test.js
│ ├── index.css
│ ├── index.js
│ ├── logo.svg
│ ├── serviceWorker.js
│ └── setupTests.js
├── tree.txt
└── yarn.lock
复制代码
为了解决上面打包600kb
的问题,首先须要分析,那些地方打包后的尺寸过大。咱们须要webpack-bundle-analyzer
这个插件才能可视化地看到那些包尺寸较大。
安装方法:
npm install webpack-bundle-analyzer -D
复制代码
而后在config/webpack.config.js
写入代码:
首先导入这个包,而后再添加插件:
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
plugins:[
...
isEnvProduction && new BundleAnalyzerPlugin(),
// isEnvProduction这个变量是指,在是否在生产环境
...
]
复制代码
可视化分析出来的结果以下:
从上图咱们能够发现,打包后最大的包是react-dom
。
那么咱们就着手优化它吧,从两个方面考虑:
减小服务器端的压力方法
减小尺寸的方法
减小服务器压力的方法有:
使用AggressiveSplittingPlugin
插件
使用prefetch&preload
方法
使用gzip
方法
减小尺寸的方法有:
使用ModuleConcatenationPlugin
插件
使用uglifyjs
插件
使用exterals
选项
那么如今试着用AggressiveSplittingPlugin
优化打包后的代码吧。
直接在config/webpack.config.js
的plugins
中添加代码:
plugins: [
new webpack.optimize.AggressiveSplittingPlugin({
minSize: 3000,
maxSize: 5000,
chunkOverhead: 0,
entryChunkMultiplicator: 1
})
];
复制代码
build
文件夹中出现了很是多的小文件。这个插件是的超过必定体积会分割文件。有利于减小服务器的请求压力。AggressiveSplittingPlugin
能够将bundle
拆分红更小的chunk
,直到各个chunk
的大小达到option
设置的 maxSize
。它经过目录结构将模块组织在一块儿。
它记录了在webpack Records
里的分离点,并尝试按照它开始的方式还原分离。这确保了在更改应用程序后,旧的分离点(和 chunk)是可再使用的,由于它们可能早已在客户端的缓存中。所以强烈推荐使用Records
。
第二项是预加载的能力,和预请求能力。咱们先来看看使用preload
会怎么样?
个人html-webpack-plugin
版本是4.0.0-beta.11
,为了正确安装这个 preload 插件,必需要将它的版本设置为3.0.0-beta.3
,否则它会报如下的错误:
Plugin could not be registered at 'html-webpack-plugin-before-html-processing'. Hook was not found.
复制代码
使用npm
管理器安装这个版本:
npm install preload-webpack-plugin@3.0.0-beta.3
复制代码
接着在webpack.config.js
中添加:
const PreloadWebpackPlugin = require('preload-webpack-plugin');
// ...
plugins: [
new HtmlWebpackPlugin(
Object.assign(
{},
{
inject: true,
template: paths.appHtml
},
isEnvProduction
? {
minify: {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true
}
}
: undefined
)
),
// ...
new PreloadWebpackPlugin()
];
// ...
复制代码
打开调试台,若是的你节点出现了这样的样子,那么说明你成功了。
后面有一个ref=preload
就是预加载。
preload
有能作什么?举个例子,若是一个网页中使用了许多的字体文件,那么用户在浏览的时候就会等待字体加载,出现白屏的现象。
若是使用了preload
的话,那么浏览器不会每次请求一个页面,到指定页面中从新加载,而是预先加载字体文件,到指定页面中不用再次加载。
如今,咱们尝试使用prefetch
,是什么结果。
在config/webpack.config.js
修改:
// ...
plugins: [
// ...
new PreloadWebpackPlugin({
rel: 'prefetch'
})
// ...
];
// ...
复制代码
这时,preload
就变成了prefetch
,以下图:
若是你看到了ref=prefetch
的话,那么表明预处理成功。
preload
会首先优先加载,而且会占用HTTP
并发数,也就是刚进入页面就会请求。而prefetch
会浏览器出空闲期时,再请求文件。preload
能够跨域请求,prefetch
不会。gzip
可以为咱们减小存储空间,以及减小传输的时间。
咱们须要安装一下插件:
npm install -D compression-webpack-plugin
复制代码
在config/webpack.config.js
中添加:
//...
plugins: [
//...
new CompressionWebpackPlugin({
filename: '[path].gz[query]',
algorithm: 'gzip',
test: /\.(js|css)/,
threshold: 1024,
minRatio: 0.8
})
//...
];
//...
复制代码
build
完成以后,打开浏览器。若是你看到这样的header
的话,那么表明开启gzip
成功。
如今,咱们开始减小尺寸的优化。
使用ModuleConcatenationPlugin
能够提升代码的执行速度和预编译:
在config/webpack.config.js
中添加:
// ...
plugins: [
// ...
new webpack.optimize.ModuleConcatenationPlugin()
];
// ...
复制代码
build
后,尺寸比之前集减小接近10kb
,并且网页的打开速度也提升很多。
相比以前打开网页的速度,提升了50ms
之多。再加上懒加载,并不须要提早加载全部页面,因此首屏渲染速度提升了许多。
接着,咱们上一个大杀器,UglifyJS
插件,它最大程度压缩和丑化代码。
安装它的命令行:
npm install -D uglifyjs-webpack-plugin
复制代码
导入到config/webpack.config.js
中,并使用:
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
// ...
optimization: {
minimizer:{
new UglifyJsPlugin(),
}
}
// ...
复制代码
能够看见,压缩后的代码有多180kb
。
externals
这个webpack
选项是在打包过程当中剔除掉。这样就有效地减小了依赖,使用第三方CDN
减小HTTP压力和请求的压力。
使用externals
只须要在webpack.config.js
中添加一下的代码:
// ...
externals:{
'react': 'React',
'react-dom': 'ReactDOM',
'react-router-dom': 'ReactRouterDOM'
}
// ...
复制代码
接着在index.html
中添加CDN
。
<script src="https://cdn.bootcss.com/react/16.10.2/umd/react.production.min.js"></script>
<script src="https://cdn.bootcss.com/react-router-dom/5.1.2/react-router-dom.min.js"></script>
<script src="https://cdn.bootcss.com/react-dom/16.10.2/umd/react-dom.production.min.js"></script>
复制代码