@ozo/react-rock
Version:
React 移动端开发脚手架,基于CRA3,通用、开箱即用。
303 lines (210 loc) • 9.14 kB
Markdown
//polyfill.io/)自动加载(服务端检测特性支持)
+ dynamic polyfill(本地检测特性支持)
通过Babel转码是标准的做法,其流程为:
```
编写ES6 代码 → 编译成 ES5 (通过 Babel等)→ 浏览器执行
```
在引入了Babel的工程中,可以通过以下几种方式来引入兼容代码:
+ 手动按需引入
+ 入口批量引入
+ 手动按浏览器版本引入
+ 通过Babel Presets自动引入
这种方式是指需要什么就在对应的也没进行单独引入,如:
```js
// components/button.jsx
import 'core-js/es6/object';
...
```
这种方式精确,但比较难维护。
这种方式主要是通过在项目的入口文件`src/index.js`中直接引入对应的polyfill库。
```
cnpm install babel-polyfill --save //Babel 6
or
cnpm install @babel/polyfill --save //Babel 7
```
```js
// src/index.js
import 'babel-polyfill'; //Babel 6
or
import "@babel/polyfill"; //Babel 7
```
这种方式比较暴力,直接加载整个polyfill库,代码体积会大幅增加。推荐的方式把需要兼容的特性放到polyfill.js中统一管理:
```js
// src/index.js
import './polyfill';
// src/polyfill.js
/*
* required polyfills
* url: https://reactjs.org/docs/javascript-environment-requirements.html
*/
// 单独引入
/** IE9, IE10 and IE11 requires all of the following polyfills. * */
// import 'core-js/es6/symbol'
// import 'core-js/es6/object'
// import 'core-js/es6/function'
// import 'core-js/es6/parse-int'
// import 'core-js/es6/parse-float'
// import 'core-js/es6/number'
// import 'core-js/es6/math'
// import 'core-js/es6/string'
// import 'core-js/es6/date'
import 'core-js/es6/array';
// import 'core-js/es6/regexp'
import 'core-js/es6/map';
// import 'core-js/es6/weak-map'
import 'core-js/es6/set';
import 'core-js/es7/object';
// require('raf').polyfill(window);
/** IE10 and IE11 requires the following for the Reflect API. */
import 'core-js/es6/reflect';
/** Evergreen browsers require these. * */
// Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove.
import 'core-js/es7/reflect';
// CustomEvent() constructor functionality in IE9, IE10, IE11
(function() {
if (typeof window.CustomEvent === 'function') return false;
function CustomEvent(event, params) {
let tParams = params || { bubbles: false, cancelable: false, detail: undefined };
const evt = document.createEvent('CustomEvent');
evt.initCustomEvent(event, tParams.bubbles, tParams.cancelable, tParams.detail);
return evt;
}
CustomEvent.prototype = window.Event.prototype;
window.CustomEvent = CustomEvent;
})();
```
CRA2开始默认不支持IE11及以下版本,官方单独提供了兼容的[polyfill](https://github.com/facebook/create-react-app/tree/master/packages/react-app-polyfill)。用法如下:
```
cnpm install react-app-polyfill -D
```
or
```
yarn add react-app-polyfill
```
根据要支持的IE最小版本选择,低版本包括对高版本的兼容:
```js
import 'react-app-polyfill/ie9';
```
or
```js
import 'react-app-polyfill/ie11';
```
> polyfill必须放在CRA2项目的 `src/index.js`的第一行。
针对ie9的polyfill,在开发环境中会报`“Map”未定义`错误,因为webpack-dev-server不支持,想起参见[isure
通过在Babel配置中,设置Presets的`useBuiltIns: 'usage'`,开启智能导入,即当前js文件使用的特性会自动import,并且整个项目中相同的polyfill只会加载1次。 [具体用法参考](https://babeljs.io/docs/en/babel-preset-env)
```json
// .babelrc
{
"presets": [
[
"@babel/env",
{
"useBuiltIns": "usage",
"targets": {
"browsers": [
"Chrome >= 49",
"Firefox >= 45",
"Safari >= 10",
"Edge >= 13",
"IE > 8",
"iOS >= 10",
"Electron >= 0.36"
]
}
}
],
[
"@babel/react",
{
"useBuiltIns": true
}
],
"react-app"
],
"plugins": [
[
"import",
{
"libraryName": "lodash-es",
"libraryDirectory": "",
"camel2DashComponentName": false
}
],
["@babel/plugin-transform-runtime", { "corejs": 2 }],
["@babel/plugin-proposal-decorators", { "legacy": true }],
["@babel/plugin-proposal-class-properties", { "loose": true }]
]
}
```
随着浏览器技术的更新,大多数ES6特性都得到了很好的支持,采用Babel转码,会强制新的浏览器运行旧代码,这并不高效。 [Polyfill.io API项目通过polyfill 方式在客户端直接执行 ES6 代码。

Polyfill.io 读取每个请求的 [User-Agent](https://en.wikipedia.org/wiki/User_agent)(UA) 头,并生成适合于该浏览器的 polyfill ,基于你的应用所使用的特性发回必要的代码。该项目由[Financial Times](http://www.ft.com/) 在开发和维护这个项目,生命力有保障。
>Polyfill.io 没有提供语法糖支持。比如 类、增强的对象字面量,以及箭头函数之类的特性。对那些代码,你仍然需要进行编译。
配置方法非常简单,在项目的入口页面文件,如`public/index.html`中,将托管在 CDN 的脚本添加到你的页面上:
```html
// public/index.html
<script src="https://cdn.polyfill.io/v2/polyfill.min.js"></script>
```
运行脚本时,将返回 UA 和你想要的特性。
```
UA detected: chrome/56.0.0
Features requested: default
```
它提供了[一堆选项](https://polyfill.io/v2/docs/api) 来自定义你要返回的特性。
该参数指定需要 polyfill 的浏览器特性。多个特性名之间用逗号分隔。允许使用的特性明在 浏览器和特性 页中列出。
```html
<script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=fetch"></script>
```
在 Safari 10 下,脚本返回内容如下:
Features requested: fetch
- setImmediate, License: CC0 (required by "Promise", "fetch")
- fetch
如果一个特性,比如 fetch 依赖于另一个特性比如 Promise,Polyfill.io 会自动加载依赖。
+ always - Polyfill 将始终被包含,不管 UA 中指出的浏览器是否已经支持该特性。
+ gated - 通过特性检测来判断 Polyfill,只有在浏览器原生 API 不支持这些特性的情况下才返回并执行 Polyfill。
```html
<script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=fetch&flags=gated"></script>
```
Polyfill 脚本加载完成之后要执行的函数名。这是最简单的在 polyfill 加载完成后触发你自己的代码的方式,这样 polyfill 服务可以更容易通过 async 和 defer 属性被异步加载。
听起来很好,但是仍然不完美。
最新的浏览器不需要加载 ES5 代码了,但是还需要通过服务器请求来检测是否需要 polyfill。
Polyfill.io 项目需要通过服务器请求来检测是否需要 polyfill。
`dynamic polyfill`项目进行了优化,它在发起任何服务端请求前检测特性是否已经被原生支持。配置如下:
```js
import polyfill from 'dynamic-polyfill'
polyfill({
fills: 'fetch, Promise',
options: 'gated', // default: null
minify: false, // default: true
afterFill() {
main()
}
})
function main() {
// app code here
}
```
执行过程如下:
检测 [window.fetch, window.Promise] 等特性是否存在。
-如果存在,运行 afterFill() 回调。
-如果它们不存在,创建一个 <script> 标签,并且包含 async 属性,将 Polyfill.io 链接插入,用参数中提供的选项去请求,并在它加载完成后执行 afterFill() 回调。
> 注意: 现在还没有支持全部选项,只有那些最重要的项被支持。
这个模块在压缩后不到 1KB 大小,而且没有任何依赖,对项目使用来说成本超低。
当用 ES6及更新的语法来写 JavaScript时,如果需要兼容老版本的浏览器(不支持 ES6),需要用户单独进行兼容。
主流的兼容方式有以下几种:
+ 通过Babel 转码
+ 通过[Polyfill.io API](https: