摘要:在上篇,我們主要拋出了兩個問題,并給出了第一個問題的解決方案。沒有的實例方法可以采用方案三委屈下。放棄模式,放棄上篇中提到了開啟了模式來解決低版本瀏覽器無法繼承到在構造函數里定義的屬性或方法。
回顧
起因:
某天,某測試說:“這個頁面在 IE8 下白屏,9也白。。”
某前端開發: 吭哧吭哧。。。一上午的時間就過去了,搞定了。
第二天,某測試說:“IE 又白了。。”
某前端開發: 嘿咻嘿咻。。。誰用的 Object.assign,出來我保證削不屎你。
在上篇,我們主要拋出了兩個問題,并給出了第一個問題的解決方案。
SCRIPT5007: 無法獲取屬性 xxx 的值,對象為 null 或未定義,這種情況一般是組件繼承后,無法繼承到在構造函數里定義的屬性或方法,同樣類屬性或方法也同樣無法繼承
SCRIPT438: 對象不支持 xxx 屬性或方法,這種情況一般是使用了 es6、es7 的高級語法,Object.assign Object.values 等,這種情況在移動端的一些 ‘神機’ 也一樣會掛。
本篇將給出第二個問題的解決方案, 并對第一個問題的解決方案有了更新的進展。
文章略長,請耐心看~嘿嘿嘿~
正文開始想要不支持該方法的瀏覽器支持,無非兩種辦法
局部引用,引入一個相同的方法代替,其缺點則是使用起來比較麻煩,每個用到的文件都要去引入。
全局實現,與之相反的方法是使用 polyfill ,其優點便是使用方便,缺點則是會全局污染,特別是實例方法,涉及到修改其 prototype ,不是你的類,你去修改它原型是不推薦的。
針對這兩種辦法,提供出以下幾種方案,供大家參考
方案一:引入額外的庫拿最常用的 assign 來說,可以這樣
import assign from "object-assign"; assign({}, {});
其實這種也是我們之前的使用方式,缺點就是需要去找到對應的庫,比如 Promise 我們可以使用 lie
另一方面一旦有人沒有按照這個規則,而直接使用了 Object.assign,那這個人就可能被削。
方案二:全局引入 babel-polyfill在項目的程序入口
import "babel-polyfill";
babel 提供了這個 polyfill,有了它,你就可以盡情使用高級方法,包括 Object.values [].includes Set generator Promise 等等。其底層依賴的是 core-js 。
但是這種方案顯然有些暴力, polyfill 構建并 uglify 后的大小為 98k,gzip 后為32.6k,32k 對與移動端還是有點大的。
性能與使用是否方便自己權衡,比如離線包后或也可以接受。
方案三:手動引入 core-js這個方案也稍微有些麻煩, core-js 里實現了大部分 e6、es7 的高級語法,具體列表可以去這里查看 https://github.com/babel/babe...
我先截取一部分做下參考
Object: { assign: "object/assign", create: "object/create", defineProperties: "object/define-properties", defineProperty: "object/define-property", entries: "object/entries", freeze: "object/freeze", ... }
具體怎么使用呢?找到要使用的方法的值,如:assign 是 "object/assign",將其拼接至一個固定路徑。
import assign from "core-js/library/fn/object/assign"
或
import "core-js/fn/object/assign"
這里包含上述所說的局部使用和全局實現的兩種
直接引入 "core-js/fn/" 下的即為全局實現,你可以在程序入口引入你想使用的,這樣相對于方案二避免了多余的庫的引入
引入 "core-js/library/fn/" 下的即為局部使用,和方案一一樣,只是省去了自己去尋找類庫。
但是,實際使用,import 要寫辣么長的路徑,還是感覺有些麻煩。
方案四:使用 babel-plugin-transform-runtime本文會重點介紹下這個插件
先看下如何使用
// without options { "plugins": ["transform-runtime"] } // with options { "plugins": [ ["transform-runtime", { "helpers": false, // defaults to true; v6.12.0 (2016-07-27) 新增; "polyfill": true, // defaults to true "regenerator": true, // defaults to true // v6.15.0 (2016-08-31) 新增 // defaults to "babel-runtime" // 可以這樣配置 // moduleName: path.dirname(require.resolve("babel-runtime/package")) "moduleName": "babel-runtime" }] ] }
該插件會做三件事情
The runtime transformer plugin does three things:
Automatically requires babel-runtime/regenerator when you use generators/async functions.
Automatically requires babel-runtime/core-js and maps ES6 static methods (Object.assign) and built-ins (Promise).
Removes the inline babel helpers and uses the module babel-runtime/helpers instead.
第一件,如果你想使用 generator , 有兩個辦法,一個就是引入 bable-polyfill 這個大家伙兒,另一個就是使用這個插件,否則你會看到這個錯誤
Uncaught ReferenceError: regeneratorRuntime is not defined
第二件,就是能幫助我們解決一些高級語法的問題,它會在構建時幫你自動引入,用到什么引什么。
但是它的缺陷是它只能幫我們引入靜態方法和一些內建模塊,如 Object.assign Promise 等。實例方法是不會做轉換的,如 "foobar".includes("foo") ,官方提示在這里:
NOTE: Instance methods such as "foobar".includes("foo") will not work since that would require modification of existing builtins (Use babel-polyfill for that).
翻譯一下就是,不要越俎代庖,不是你的東西你別亂碰,欠兒欠兒的。
所以這個方案不會像方案二那樣隨心所欲的使用,但其實也基本夠用了。
沒有的實例方法可以采用方案三委屈下。
個人還是比較推薦這兩種合體的方案。
需要注意的一點是:
開啟 polyfill 后,會與 export * from "xx" 有沖突
請看構建后的代碼:
... /***/ }, /* 106 */ /***/ function(module, exports, __webpack_require__) { "use strict"; // 這是什么鬼。 import _Object$defineProperty from "babel-runtime/core-js/object/define-property"; import _Object$keys from "babel-runtime/core-js/object/keys"; Object.defineProperty(exports, "__esModule", { value: true }); ...
截止 2016-09-10,官方尚未解決此 issue, 只有先避開 export * from "xx" 這種寫法。或在這里找答案。
第三件,是會引入一些 helper 來代替每次都生成的通用函數,看個例子就明白了
原來構建好的代碼每個模塊都有類似這種代碼:
function _classCallCheck(instance, Constructor)... function _possibleConstructorReturn(self, call)... function _inherits(subClass, superClass)...
開啟 helper 后:
var _classCallCheck2 = require("babel-runtime/helpers/classCallCheck"); var _possibleConstructorReturn2 = require("babel-runtime/helpers/possibleConstructorReturn"); var _inherits2 = require("babel-runtime/helpers/inherits");
這樣統一引用了 helper,去處了冗余,看起來也更優雅了。
在 v6.12.0 之前 helper 也是默認開啟的,沒有配置可改,其他的 ployfill regenerator 都是有配置可以設置的。也許是推薦你使用 helper 。
但是 v6.12.0 (2016-07-27) 增加了 helper 的配置。為什么呢?
我最開始用這個插件的時候也很詫異,按道理來說,去除了冗余代碼,代碼的體積應該變小才對,但實際測試卻變大了,我測試時是未經 uglify 的代碼從 18k 增加到了 78k,查看構建模塊增加了將近 100 個 詳情。
原因是從 babel-runtime 里引入的 helper 依賴很多,全部都是兼容最底層的。比如 Object.create typeof 這種方法全部被重寫了。
后來 gaearon 大神都忍不了了,他測試的結果是增加了 5kB min+gzip 詳情。
于是有了 helper 這個配置項。
另外還有一點,如果開啟了 helper 的話,你會發現之前引用的 babel-plugin-transform-proto-to-assign 就失效了,雖然他本來就不該被使用,后面會講到。
所以目前看來這個 helper 不用也罷。
再說下 moduleName 這個參數是干什么的?
還記得開啟 helper 后的代碼嗎
var _classCallCheck2 = require("babel-runtime/helpers/classCallCheck");
看下這個路徑,如果是本地項目安裝了 babel-runtime 是沒問題的,但如果你是用的通用構建工具,比如 nowa,所有的構建依賴庫都是在公共的地方,畢竟 babel 太太了。這里就會報錯了。
Cannot resolve module babel-runtime/regenerator
gaearon 大神在寫 create-react-app 時也發現了這個問題, 詳情
雖然這個問題可以通過 webpack 的 resolve.root 來解決,但是 gaearon 大神看其不爽,覺得依賴 webpack 不夠優雅,#3612 于是乎就有了 moduleName 這個參數,已發布 v6.15.0 (2016-08-31)。
放棄 loose 模式, 放棄 ie8上篇中提到了開啟了 loose 模式來解決低版本瀏覽器無法繼承到在構造函數里定義的屬性或方法。
我們是通過 babel-preset-es2015-ie 這個插件,主要是改寫了 babel-plugin-transform-es2015-classes: {loose: true} 和添加了插件 babel-plugin-transform-proto-to-assign(解決類方法繼承的問題)
在 babel-preset-es2015 v6.13.0 (2016-08-04) 時,presets 已經支持了參數配置,可以直接開啟 loose 模式。
它內部會把開啟一些插件的 loose 模式,不只是babel-plugin-transform-es2015-classes
{ presets: [ ["es2015", { "loose": true }] ] }
這樣我們就可以直接使用 babel-preset-es2015,至于 babel-plugin-transform-proto-to-assign 可以多帶帶配置,也可不使用,因為類方法本來就不該被繼承,要使用就直接 Parent.defaultProps 就可以了。
在上文中并沒有提到開啟 loose 模式的另一個原因是解決 ie8 下的兩個 es3 屬性名關鍵字的問題,因為上文測試均在 ie9 上,所以上述的方案也是停留在必須支持 ie8。
那么如果我們放棄了 ie8 ,看一看是不是會海闊天空。
在 babel-plugin-transform-es2015-classes v6.14.0 (2016-08-23) 一個 ‘大胡子哥’(原諒我不認識他) 修復了 __proto__ 這個問題 #3527 Fix class inheritance in IE <=10 without loose mode.
這樣我們就可以在 ie9+ 上使用正常的 es6 模式了。
畢竟我們該向前看,loose 模式有點后退的趕腳。
這篇文章也表達了不推薦使用 loose 模式
Con: You risk getting problems later on, when you switch from transpiled ES6 to native ES6. That is rarely a risk worth taking.
當然,如果真的離不開 ie8,就針對 es3 關鍵字的問題引用兩個插件即可
require("babel-plugin-transform-es3-member-expression-literals"), require("babel-plugin-transform-es3-property-literals"),
我們再稍微看下‘大胡子哥’的修改,其實很簡單,也很巧妙,看一行關鍵代碼
// 修改后生成的代碼多了一個 先取 `xxx.__proto__` 再使用 `Object.getPrototypeOf` var _this = _possibleConstructorReturn(this, (Test.__proto__ || Object.getPrototypeOf(Test)).call(this, props));
回顧下 inherits 方法的實現
function _inherits(subClass, superClass) { ... // 雖然 ie9/10 不支持 `__proto__`,這里只是作為了普通對象給予賦值,`Object.getPrototypeOf` 獲取不到但可以直接 `.__proto__` 獲取 Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; ...
如果你看懂了實現方式,不知道你有沒有發現 babel-plugin-transform-proto-to-assign(解決類方法繼承的問題)這個家伙真的不能用了
function _inherits(subClass, superClass) { ... // 因為它會將 `__proto__` 轉為 `_default` Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : _defaults(subClass, superClass); }
這樣上述的修復就無效了。切記不能使用,還是那句話,類方法本來就不該被繼承。
最后看下終極方案的通用配置
{ plugins: [ ["transform-runtime", { "helpers": false, "polyfill": true, "regenerator": true }], "add-module-exports", "transform-es3-member-expression-literals", "transform-es3-property-literals", ], "presets": [ "react", "es2015", "stage-1" ], }
更簡單、完整的解決方案,請查看 nowa
感謝閱讀。
參考鏈接https://babeljs.io/docs/plugi...
https://github.com/babel/babel
廣告時間: 請獻出你的小星星https://github.com/saltjs/sal...
https://github.com/uxcore/uxcore
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/80398.html
摘要:起因某天,某測試說這個頁面在下白屏,也白。。某前端開發吭哧吭哧。。。一上午的時間就過去了,搞定了。第二天,某測試說又白了。。某前端開發吭哧吭哧。。。誰用的,出來我保證削不屎你。原諒我不禁又黑了一把。 起因 某天,某測試說:這個頁面在 IE8 下白屏,9也白。。 某前端開發: 吭哧吭哧。。。一上午的時間就過去了,搞定了。 第二天,某測試說:IE 又白了。。 某前端開發: 吭哧吭哧。。。誰...
摘要:本文記錄如下起因在準備提測的那天,順便打開看一眼注意,這里是原生不是用模擬的,排查后發現,原來是因為構造函數中使用了。簡寫如下老司機們肯定能一眼發現問題構造函數中不應該使用而是傳入的應該改為改正之后,問題確實解決了。 雖然過了兼容IE6的噩夢時代,IE依舊陰魂不散,因為你可能還要兼容IE9。在ES6已經普及的今天,用ES6寫react已經成了標配。但是babel編譯的js語法,由于某些...
摘要:本文記錄如下起因在準備提測的那天,順便打開看一眼注意,這里是原生不是用模擬的,排查后發現,原來是因為構造函數中使用了。簡寫如下老司機們肯定能一眼發現問題構造函數中不應該使用而是傳入的應該改為改正之后,問題確實解決了。 雖然過了兼容IE6的噩夢時代,IE依舊陰魂不散,因為你可能還要兼容IE9。在ES6已經普及的今天,用ES6寫react已經成了標配。但是babel編譯的js語法,由于某些...
摘要:簡稱已經更新之版本也更新至版本裝飾器語法雖然還不是標準但是借助于也能在項目里愉快的玩耍時代如何啟用裝飾器語法呢我們依舊采用的是通過劫持對象達到修改的目的修改安裝裝飾器語法所需的插件也可以順帶升級在項目 create-react-app(簡稱cra)已經更新之2.0.3版本, babel也更新至7.x版本, JavaScript裝飾器語法雖然還不是標準, 但是借助于babel, 也能在項...
閱讀 25629·2021-09-29 09:41
閱讀 4787·2021-09-10 11:20
閱讀 1918·2021-09-09 09:32
閱讀 1881·2019-08-30 15:44
閱讀 3192·2019-08-29 17:13
閱讀 2809·2019-08-29 14:14
閱讀 2062·2019-08-29 14:11
閱讀 3221·2019-08-29 12:36