UNPKG

@ozo/react-rock

Version:

React 移动端开发脚手架,基于CRA3,通用、开箱即用。

303 lines (210 loc) 9.14 kB
## 低版本浏览器兼容(IE9- IE11) ​ 当用 ES6及更新的语法来写 JavaScript时,如果需要兼容老版本的浏览器(不支持 ES6),需要用户单独进行兼容。 ​ 主流的兼容方式有以下几种: + 通过Babel 转码 + 通过[Polyfill.io API](https://polyfill.io/)自动加载(服务端检测特性支持) + dynamic polyfill(本地检测特性支持) ### 通过Babel转码 ​ 通过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#1583](https://github.com/webpack/webpack-dev-server/issues/1583), create-react-app的[isure#4674](https://github.com/facebook/create-react-app/issues/5674) 。生产环境可以正常使用。 #### ​通过Babel Presets自动引入 ​ 通过在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 }] ] } ``` ### Polyfill.io API按需引入 ​ 随着浏览器技术的更新,大多数ES6特性都得到了很好的支持,采用Babel转码,会强制新的浏览器运行旧代码,这并不高效。 [Polyfill.io API项目通过polyfill 方式在客户端直接执行 ES6 代码。 ![img](http://p9.qhimg.com/t0109387db34edbeb01.png) ##### **实现原理** ​ Polyfill.io 读取每个请求的 [User-Agent](https://en.wikipedia.org/wiki/User_agent)(UA) 头,并生成适合于该浏览器的 polyfill ,基于你的应用所使用的特性发回必要的代码。该项目由[Financial Times](http://www.ft.com/) 在开发和维护这个项目,生命力有保障。 >Polyfill.io 没有提供语法糖支持。比如 类、增强的对象字面量,以及箭头函数之类的特性。对那些代码,你仍然需要进行编译。 ##### 配置 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) 来自定义你要返回的特性。 ###### Features ​ 该参数指定需要 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 会自动加载依赖。 ###### Flags + always - Polyfill 将始终被包含,不管 UA 中指出的浏览器是否已经支持该特性。 + gated - 通过特性检测来判断 Polyfill,只有在浏览器原生 API 不支持这些特性的情况下才返回并执行 Polyfill。 ```html <script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=fetch&flags=gated"></script> ``` ###### Callback Polyfill 脚本加载完成之后要执行的函数名。这是最简单的在 polyfill 加载完成后触发你自己的代码的方式,这样 polyfill 服务可以更容易通过 async 和 defer 属性被异步加载。 ##### 存在的问题 听起来很好,但是仍然不完美。 最新的浏览器不需要加载 ES5 代码了,但是还需要通过服务器请求来检测是否需要 polyfill。 ### dynamic 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 大小,而且没有任何依赖,对项目使用来说成本超低。