背景

目前我们h5主要是面向两个app,V(简称)以及W(简称)(都只是安卓端),基本都是基于这两个app的webview做一些活动 W(简称) 面向最低的Android版本是4.1,V(简称) 面向最低的Android版本是4.0。低版本的手机js环境比较老,因此一些我们要针对低版本的手机做一些polyfill补充(例如babel-polyfill,react-intl的polyfill => intl,vw/vh 等等)。

我们的polyfill主要是针对在4.2以及4.2以下的手机.

这是 V(简称) 的手机型号用户占比: 4.2以及以下的手机uv大概100万,占比大概1.72%
image.png

这是 W(简称) 的手机型号用户占比: 4.2以及以下的活跃设备数量大概350万,Dau大概120万,4.2以下的设备大概1.5万,占比不到1%

image.png

目前的策略

目前我们的方法是统一引入这些polyfill!
这是我们某个活动页面的build出来的结果,用BundleAnalyzerPlugin查看可以看到,整个活动应用300多kb,而 code-js(babel-polyfill) + react-intl/lib 就已经占了200多kb! 即使gzip之后传输也得66kb。
image.png

痛点

  1. 这些polyfill其实只是很少的一部分机型需要,而现在的情况是所有的用户都要承受加载它们的痛苦。
  2. 我们活动的dau和pv很大(斋月活动等dau达到200-300万),加载多余的js也消耗我们很多的流量费用。

解决方案

对于babel-polyfill

因为目前我们的h5主要是面对于app的webview,app提供给我们 jsBridge 让我们拿到手机的版本信息。
我们没有必要直接统一引入babel-polyfill,因为只有一小部分的用户需要,吸收阿里巴巴的babel-polify动态接入方案,它们提供了对应的polyfill cdn.

1
//polyfill.alicdn.com/polyfill.min.js?features=default,es2017,es6,fetch,RegeneratorRuntime

而我们可以在拿到手机的版本后,判断低于4.2及以下的版本,才引入cdn做兼容处理。

对于react-intl(国际化)

与babel-polyfill类似,react-intl 需要给没有内嵌intl的手机引入intl包,我们可以判断window.Intl对象是否存在,不存在就动态下载这个包并且引入(要考虑阻塞渲染的情况)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import * as React from 'react';
import request from "request";
import Spinner from "@/component/welike_spinner/welike_spinner"
import { IntlProvider } from "react-intl";
interface Props {
messages: Object;
children: React.ReactNode;
}
/**
* 多语言组件,因为某些浏览器没有内嵌intl,所以会报错,
* 这时候需要动态下载polyfill,避免了不需要intl的用户的冗余加载
*/
const INTL_CDN = '//cdn.polyfill.io/v2/polyfill.js?features=Intl.~locale.en'
export default React.memo<Props>(({ messages, children }) => {
//是否本来就有intl
const hasIntl = !!window.Intl;
const [loading, setLoadingState] = React.useState(!hasIntl);
//如果没有,就去下载
if (!hasIntl) {
request.loadScript(INTL_CDN).then(() => {
//下载完才真正的展现UI
setLoadingState(false);
}).catch(err => {
console.log(err);
})
}
return !loading ? <IntlProvider key={'en'} locale={'en'} messages={messages}>
{children}
</IntlProvider> : <div style={{ position: 'absolute', top: '0', left: '0', width: '100%', height: '100%' }}>
<div style={{
position: 'absolute',
left: '50%',
top: '50%',
transform: 'translate(-50%, -50%)'
}} >
<Spinner />
</div>
</div>

})

可以看到,改成动态加载后,少了快40kb的资源!

对于vw vh兼容性

image.png
可以看到,针对安卓Browser,vw,vh只能向下支持到4.3.

  1. 为了移动端浏览器能安全的使用vw单位实现多屏适配,从而让css代码运行在各种尺寸的手机屏幕上,并且和设计稿保持一致
  2. 技术方案为:通过js动态判断当前浏览器是否支持vw单位,如果不支持,把所有css样式中vw转为rem,并设置html font-size为当前浏览器宽度 的 1/100, 从而使1rem=1vw;
  3. write once run everywhere, 开发者无需考虑多屏适配问题和vw兼容问题
  4. 使用尽可能简单,代码量尽可能少,尽可能不阻塞页面基础样式和逻辑
    使用vw-polyfill可以解决这个问题。

不只是vw,vh. 还有其他的一些css属性也要做兼容,这个得一步步地去测试以及优化了.

Last but not least

由于随着我们项目越来越大越来越多,我们需要引入的polyfill的场景以及条件都越来越多。
参考我们的设备占比数据,其实4.2以下的设备占比比较小,所以我们开发的时候需要取决成本和人力以及收益去判断是否必要去做这些兼容性,如果有些项目不需要兼容,有些项目需要,我们可以在webpack的entry统一配置是否需要兼容的字段,不需要兼容的,我们直接默认给一个类似提示更新的优雅降级操作即可。

参考文献

  1. 动态Polyfill解决方案
  2. Polyfill 方案的过去、现在和未来
  3. react-intl-polyfill
  4. vw-polyfill