在 Web 应用开发过程当中,咱们常常谈及到的就是优化,而优化每每又是既简单而又复杂的过程,优化这个命题很广,最终体现出来的都是用户体验问题,咱们一切优化都是为了用户体验。javascript
为何说简单?在现代 Web 开发生态中,有很是优秀的工具链帮助咱们作一些很实际的优化工做,例如 webpack
。这些工具能够很好的帮助咱们解决包之间的依赖、减少包大小、提取公共模块等等问题。css
为何说复杂?优化这个话题咱们谈了不少年,只要有用户群,优化问题就会一直存在。而优化工做涉及的领域特别广,包含的因素又特别多,有时候须要针对特定的场景作特殊的优化工做,因此说又很复杂。html
不论是简单仍是复杂,做为程序员,咱们应当作一些咱们力所能及的优化工做,本文属于探讨性话题,但愿广大网友可以在留言区留下您的一些思考。前端
这里不探讨如何书写高性能的代码,而是探讨下咱们书写的代码该如何被构建。这里以 webpack 为构建工具(版本为4.19),来阐述下在 webpack 咱们该作的优化工做。java
webpack 从 v4
版本开始,作了不少的优化工做,详情请看这里 。咱们就拿 Code Splitting
提及,Code Splitting
是 webpack 一项重要的编译特性,可以帮助咱们将代码进行拆包,抽取出公共代码。利用这项特性咱们能够作更多的优化工做,减小加载时间,例如能够作按需加载。而在 webpack 中开启 Code Splitting
也很简单,它是一项开箱即用的插件,示例代码以下:react
module.export = {
// ...
optimization: {
splitChunks: {
chunks: 'all'
}
}
}
复制代码
上面的 chunks
配置建议你们配置为 all
,详细配置请参考:splitChunks.chunkswebpack
这里给出个参考结果:分别为配置前和配置后git
这里明显多出了几个包含 vendors~
字样的文件,并且你会发现 app
这个文件被提取出了 vendors~app~react_vendor
和 vendors_app_redux_vendor
这两个文件。至于最大的文件为何还有1.02M,咱们可使用 analyze
来分析下包的结构,这里是因为包含了 antd
的文件。程序员
在实际开发过程当中,路由也是须要进行 Code Splitting
,在过去咱们常用 bundle-loader ,来帮助咱们进行代码分割,它是基于 require.ensure 接口进行实现。既然咱们能够对路由进行代码分割,那么路由页面中的组件咱们是否能够按需加载,实现代码分割呢?答案是显然的。github
这种业务场景也是很是的多,这里我举一个例子,就是一个登陆页面,登陆有多种方式,其中最多见的就是帐号登陆和扫码登陆,默认为扫码登陆。当用户没有选择帐号登陆,那么按道理这部分代码咱们能够不进行加载,从而减小加载时间,优化用户体验。咱们建议能进行组件级分割就分割,最大化减少页面大小。
在 React
中虽然也可使用 bundle-loader 来实现组件级代码分割,可是也会有一些问题。在后来,React Router
官方也推荐使用 react-loadable 来进行代码分割。强烈建议 React
使用者使用此库,该库的功能很强大,是基于 import() 实现。它能够实现预加载、从新加载等等强大功能。
若是你对本身编写的代码很了解,你能够经过在 package.json
中添加 sideEffects
来启用 Tree Shaking
,即摇树优化,帮助咱们删掉一些不用的代码。这里再也不赘述,详情能够点击Tree Shaking。
在谈到 Code Spliting
时,咱们不得不想到 dynamic import
,在以前版本的 webpack 中,咱们想实现动态加载使用的是 require.ensure ,而在新版本中,取而代之的 import() ,这是TC39关于使用 import()的提案,而目前 import()兼容性以下:
import() 返回一个 Promise
,若是你想使用它请确保支持 Promise
或者使用 Polyfill
,在想使用 import() 前,咱们还得使用预处理器,咱们可使用 @babel/plugin-syntax-dynamic-import 插件来帮助webpack解析。webpack 官方给了咱们一个 dynamic import
的示例 ,这里我就不作举例。使用 import() 咱们能够很方便的实现 preload
预加载、懒加载以及上面谈到的 Code Splitting
。
Polyfill
如今对于你们来讲应该并不陌生,他能够帮助咱们使用一些浏览器目前并不支持的特性,例如 Promise
。在Babel中,官方建议使用 babel-preset-env 配合 .browserslistrc ,开发人员能够无需关心目标环境,提高开发体验。尤为在 Polyfill
方面,只要咱们配置好 .browserslistrc ,Babel 就能够智能的根据咱们配置的浏览器列表来帮助咱们自注入 Polyfill
,好比:
.babelrc
{
"presets": [
[
"@babel/preset-env",
{
"useBuiltIns": "entry"
}
]
]
}
复制代码
useBuiltIns 告诉 babel-preset-env 如何配置 Polyfill
,这里我配置为:entry
,而后在 webpack 入口文件中引入 import '@babel/polyfill'
便可,这里注意不能屡次引入 import '@babel/polyfill'
,不然会报错。
.browserslistrc
> 1%
Last 2 versions
复制代码
这样就完成了自动根据 .browserslistrc注入 Polyfill
,可是这样有一个问题,就是全部的浏览器都会有 Polyfill
的并集。每一个浏览器之间的特性具备很大的差别,为了尽量的减少包的大小,咱们能够为每一个主流浏览器单独生成 Polyfill
,不一样的浏览器加载不一样的 Polyfill
。
SPA 程序打包出来的html文件通常都是很小的,也就2kb左右,彷佛咱们还能够利用下这个大小作个优化,有了解初始拥塞窗口 的同窗应该知道,一般是14.6KB,也就意味着这咱们还能利用剩下的12KB左右的大小去干点什么,这了我建议内联一些首屏关键的css文件(可使用 criticalCSS ),或者将css初始化文件内联进去,固然你也能够放其余东西,这里只是充分利用下初始拥塞窗口 特性。
这里顺便讲下css初始化,css初始化有不少种选择,其中有三种比较出名的,分别是:normalize.css 、sanitize.css 和 reset.css 。关于这三种的区别我就直接引用了。
normalize.css and sanitize.css correct browser bugs while carefully testing and documenting changes. normalize.css styles adhere to css specifications. sanitize.css styles adhere to common developer expectations and preferences. reset.css unstyles all elements. Both sanitize.css and normalize.css are maintained in sync.
在利用 webpack 打包完以后,咱们有些文件几乎不会变动,好比我这里列举的react
、redux
、polyfill
相关的文件。
entry: {
react_vendor: ['react', 'react-dom', 'react-router-dom'],
redux_vendor: ['react-redux','redux', 'redux-immutable','redux-saga', 'immutable'],
polyfill: '@babel/polyfill',
app: path.join(process.cwd(),'app/app.js')
}
复制代码
这些不变的文件咱们就能够好好的利用下,常见(http 1.1)的就是设置 Etag
,Last-Modified
和 Cache-Control
。前面两种属于对比缓存,仍是须要和服务器通讯一次,只有当服务器返回 304
,浏览器才会去读取缓存文件。而 Cache-Control
属于强制缓存,服务器设定 max-age
当过了设定的时间后才会向服务器发起请求。这里打包再配上 chunk-hash
几乎能够完美的配置缓存。
固然还能够利用 localStorage 来作缓存,这里提出一种思路,是我之前在效仿百度首页缓存机制想的。咱们能够在把js文件版本号弄成一个配置,同时存储在服务端和客户端,好比:
{
"react_version": 16.4,
"redux_version": 5.0.6,
"web_version": 1.0
}
复制代码
客户端将该版本号存储在 cookie
或其余存储引擎中,这里推荐 localForage 来作存储。服务端将最新版本号渲染到html文件中,而后经过js脚本对比版本号,如若版本号不一样,则进行加载对应的js文件,加载成功后再存储到本地存储中。若是相同,则直接取本地存储文件。
还有一种缓存的场景,就是有一些api服务端更新的进度很慢,好比一天以内访问的数据都是同样的,这样就能够对客户端进行请求缓存并拦截请求,从而优化速度,减少服务器压力。
还有其余不少能够优化的地方,好比减小http请求、图片懒加载等等,就不一一列举了,你们能够看雅虎34条军规:
尽可能减小 HTTP 请求个数——须权衡
使用 CDN(内容分发网络)
为文件头指定 Expires 或 Cache-Control ,使内容具备缓存性。
避免空的 src 和 href
使用 gzip 压缩内容
把 CSS 放到顶部
把 JS 放到底部
避免使用 CSS 表达式
将 CSS 和 JS 放到外部文件中
减小 DNS 查找次数
精简 CSS 和 JS
避免跳转
剔除重复的 JS 和 CSS
配置 ETags
使 AJAX 可缓存
尽早刷新输出缓冲
使用 GET 来完成 AJAX 请求
延迟加载
预加载
减小 DOM 元素个数
根据域名划分页面内容
尽可能减小 iframe 的个数
避免 404
减小 Cookie 的大小
使用无 cookie 的域
减小 DOM 访问
开发智能事件处理程序
用 代替 @import
避免使用滤镜
优化图像
优化 CSS Spirite
不要在 HTML 中缩放图像——须权衡
favicon.ico要小并且可缓存
保持单个内容小于25K
打包组件成复合文本
关于优化的文章网上太多太多,这篇文章并非告诉你们如何优化,而是在平时写代码时可以培养一种习惯、一种意识,就是作咱们力所能及的优化以及要知其因此然。
文 / GoDotDotDot
LESS is MORE
编 / 荧声
本文已由做者受权发布,版权属于创宇前端。欢迎注明出处转载本文。本文连接:knownsec-fed.com/2018-09-25-…
想要订阅更多来自知道创宇开发一线的分享,请搜索关注咱们的微信公众号:创宇前端(KnownsecFED)。欢迎留言讨论,咱们会尽量回复。
感谢您的阅读。