Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

页面性能优化(二) #21

Open
dongtianqi opened this issue Mar 21, 2019 · 0 comments
Open

页面性能优化(二) #21

dongtianqi opened this issue Mar 21, 2019 · 0 comments

Comments

@dongtianqi
Copy link
Owner

  1. 使用 html-webpack-plugin 自动插入 loading
  2. 使用 prerender-spa-plugin 渲染首屏
  3. 除掉外链 css
    截止到目前,我们的首屏体积 = html + css,依然有优化的空间,那就是把外链的 css 去掉,让浏览器在加载完 html 时,即可渲染首屏。

实际上,webpack 默认就是没有外链 css 的,你什么都不需要做就可以了。当然如果你的项目之前配置了 extract-text-webpack-plugin 或者 mini-css-extract-plugin 来生成独立的 css 文件,直接去掉即可。

有人可能要质疑,把 css 打入 js 包里,会丢失浏览器很多缓存的好处(比如你只改了 js 代码,导致构建出的 js 内容变化,但连带 css 都要一起重新加载一次),这样做真的值得吗?

确实这么做会让 css 无法缓存,但实际上对于现在成熟的前端应用来说,缓存不应该在 js/css 这个维度上区分,而是应该按照“组件”区分,即配合动态 import 缓存组件。

接下来你会看到,css in js 的模式带来的好处远大于这么一丁点缺点。

4 使用动态 polyfill
Polyfill 的特点是非必需和不变,因为对于一台手机来说,需要哪些 polyfill 是固定的,当然也可能完全不需要 polyfill。

现在为了浏览器的兼容性,我们常常引入各种 polyfill,但是在构建时静态地引入 polyfill 存在一些问题,比如对于机型和浏览器版本比较新的用户来说,他们完全不需要 polyfill,引入 polyfill 对于这部分用户来说是多余的,从而造成体积变大和性能损失。

比如 React 16 的代码中依赖了 ES6 的 Map/Set 对象,使用时需要你自己加入 polyfill,但目前几个完备的 Map/Set 的 polyfill 体积都比较大,打包进来会增大很多体积。

还比如 Promise 对象,实际上根据 caniuse.com 的数据,移动端上,中国接近 94% 的用户浏览器,都是原生支持 Promise 的,并不需要 polyfill。但实际上我们打包时还是会打包 Promise 的 polyfill,也就是说,我们为了 6% 的用户兼容性,增大了 94% 用户的加载体积。

所以这里的解决方法就是,去掉构建中静态的 polyfill,换而使用 polyfill.io 这样的动态 polyfill 服务,保证只有在需要时,才会引入 polyfill。

具体的使用方法非常简单,只需要外链一个 js:

<script src="https://cdn.polyfill.io/v2/polyfill.min.js"></script>

当然这样是加载全部的 polyfill,实际上你可能并不需要这么多,比如你只需要 Map/Set 的话:

<script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=Map,Set"></script>
动态 polyfill 的原理
如果你用最新的 Chrome 浏览器访问这个链接的话:https://cdn.polyfill.io/v2/polyfill.js,你会发现内容几乎是空的:

image

如果打开控制台,模拟 iOS 的 Safari,再访问一次,你会发现里面就出现了一些 polyfill(URL 对象的 polyfill):

image

这就是 polyfill.io 的原理,它会根据你的浏览器 UA 头,判断你是否支持某些特性,从而返回给你一个合适的 polyfill。对于最新的 Chrome 浏览器来说,不需要任何 polyfill,所以返回的内容为空。对于 iOS Safari 来说,需要 URL 对象的 polyfill,所以返回了对应的资源。

image
5 使用 SplitChunksPlugin 自动拆分业务基础库
Webpack 4 抛弃了原有的 CommonChunksPlugin,换成了更为先进的 SplitChunksPlugin,用于提取公用代码。

它们的区别就在于,CommonChunksPlugin 会找到多数模块中都共有的东西,并且把它提取出来(common.js),也就意味着如果你加载了 common.js,那么里面可能会存在一些当前模块不需要的东西。

而 SplitChunksPlugin 采用了完全不同的 heuristics 方法,它会根据模块之间的依赖关系,自动打包出很多很多(而不是单个)通用模块,可以保证加载进来的代码一定是会被依赖到的。

下面是一个简单的例子,假设我们有 4 个 chunk,分别依赖了以下模块:

image

如果是以前的 CommonChunksPlugin,那么默认配置会把它们打包成下面这样:
image
显然在这里,react、react-dom、angular 这些公用的模块没有被抽出成为独立的包,存在进一步优化的空间。

现在,新的 SplitChunksPlugin 会把它们打包成以下几个包:

这就保证了所有公用的模块,都会被抽出成为独立的包,几乎完全避免了多页应用中,重复加载相同模块的问题。

6 正确使用 Tree Shaking 减少业务代码体积
7 Code Splitting
8 LazyLoad
懒加载其实没什么好说的,目前也有一些比较成熟的组件了,自己实现一个也不是特别难:

react-lazyload

react-lazy-load

当然你也可以实现像 Medium 的那种加载体验(好像知乎已经是这样了),即先加载一张低像素的模糊图片,然后等真实图片加载完毕之后,再替换掉。

实际上目前几乎所有 lazyload 组件都不外乎以下两种原理:

监听 window 对象或者父级对象的 scroll 事件,触发 load;

使用 Intersection Observer API 来获取元素的可见性。
9 placeholder
我们在加载文本、图片的时候,经常出现“闪屏”的情况,比如图片或者文字还没有加载完毕,此时页面上对应的位置还是完全空着的,然后加载完毕,内容会突然撑开页面,导致“闪屏”的出现,造成不好的体验。

为了避免这种突然撑开的情况,我们要做的就是提前设置占位元素,也就是 placeholder:

已经有一些现成的第三方组件可以用了:

react-placeholder
react-hold

总结

这篇文章里,我们一共提到了下面这些优化加载的点:

在 HTML 内实现 Loading 态或者骨架屏;

去掉外联 css;

使用动态 polyfill;

使用 SplitChunksPlugin 拆分公共代码;

正确地使用 Webpack 4.0 的 Tree Shaking;

使用动态 import,切分页面代码,减小首屏 JS 体积;

编译到 ES2015+,提高代码运行效率,减小体积;

使用 lazyload 和 placeholder 提升加载体验。

推荐 : https://developers.google.com/web/fundamentals/performance/get-started/
参考资料:https://mp.weixin.qq.com/s?__biz=MzIzNjcwNzA2Mw==&mid=2247485902&idx=1&sn=952e0db3bc0f36b7cd4db71b17914daa&chksm=e8d28456dfa50d407e52c935cb7518cf76f1179a8bcbbac760f1685f88fd8d809ca84f5d9c3f#rd

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant