摘要:起因某天,某測(cè)試說(shuō)這個(gè)頁(yè)面在下白屏,也白。。某前端開(kāi)發(fā)吭哧吭哧。。。一上午的時(shí)間就過(guò)去了,搞定了。第二天,某測(cè)試說(shuō)又白了。。某前端開(kāi)發(fā)吭哧吭哧。。。誰(shuí)用的,出來(lái)我保證削不屎你。原諒我不禁又黑了一把。
起因
某天,某測(cè)試說(shuō):“這個(gè)頁(yè)面在 IE8 下白屏,9也白。?!?/p>
某前端開(kāi)發(fā): 吭哧吭哧。。。一上午的時(shí)間就過(guò)去了,搞定了。
第二天,某測(cè)試說(shuō):“IE 又白了。?!?/p>
某前端開(kāi)發(fā): 吭哧吭哧。。。誰(shuí)用的 Object.assign,出來(lái)我保證削不屎你。
原諒我不禁又黑了一把 IE。
有人可能會(huì)想,都要淘汰了,還有什么好講的?
也許幾年后,確實(shí)沒(méi)用了,但目前我們的系統(tǒng)還是要對(duì) ie8+ 做兼容,因?yàn)榇_實(shí)還有個(gè)別用戶,盡管他沒(méi)朋友。。。
記錄下本次在 IE 下踩得坑,讓后面的同學(xué)能夠不再在這上面浪費(fèi)時(shí)間了。
經(jīng)過(guò) 測(cè)試首先,看下面代碼(以下測(cè)試在 IE9)
class Test extends React.Component { constructor(props) { super(props); } render() { return{this.props.content}; } } module.exports = Test;
這段代碼跑的妥妥的,沒(méi)什么問(wèn)題。
一般來(lái)說(shuō),babel 在轉(zhuǎn)換繼承時(shí),可能會(huì)出現(xiàn)兼容問(wèn)題,那么,再看這一段
class Test extends React.Component { constructor(props) { super(props); } test() { console.log("test"); } render() { return{this.props.content}; } } Test.defaultProps = { content: "測(cè)試" }; class Test2 extends Test { constructor(props) { super(props); this.test(); } } Test2.displayName = "Test2"; module.exports = Test2;
這段代碼同樣也可以正常運(yùn)行
也就是說(shuō)在上述這兩種情況下,不做任何處理(前提是已經(jīng)加載了 es5-shim/es5-sham),在 IE9 下都可以正常運(yùn)行。
然后我們?cè)倏聪聲?huì)跑掛的代碼
class Test extends React.Component { constructor(props) { super(props); this.state = { test: 1, }; } test() { console.log(this.state.value); } render() { return{this.props.content}; } } Test.defaultProps = { content: "測(cè)試" }; class Test2 extends Test { constructor(props) { super(props); // SCRIPT5007: 無(wú)法獲取屬性 "value" 的值,對(duì)象為 null 或未定義 this.test(); // SCRIPT5007: 無(wú)法獲取屬性 "b" 的值,對(duì)象為 null 或未定義 this.a = this.props.b; } } // undefined console.log(Test2.defaultProps); Test2.displayName = "Test2"; module.exports = Test2;
這段代碼在高級(jí)瀏覽器中是沒(méi)問(wèn)題的,在 IE9 中會(huì)出現(xiàn)注釋所描述的問(wèn)題
從這些問(wèn)題分析,可得出3個(gè)結(jié)論
在構(gòu)造函數(shù)里的定義的屬性無(wú)法被繼承
在構(gòu)造函數(shù)里不能使用 this.props.xx
類屬性或方法是無(wú)法被繼承的
也就是說(shuō),只要規(guī)避了這三個(gè)條件的話,不做任何處理(前提是已經(jīng)加載了 es5-shim/es5-sham),在 IE9 下都可以正常運(yùn)行。
原因第二點(diǎn),是完全可以避免的,切記在 constructor 直接使用 props.xxx, 不要再用 this.props.xxx
第三點(diǎn),也是可以完全避免的,因?yàn)閺睦碚撋蟻?lái)說(shuō),類屬性就不該被繼承,如果想使用父類的類屬性可以直接Test2.defaultProps = Test.defaultProps;
第一點(diǎn),可避免,但無(wú)法完全避免
第一點(diǎn),有時(shí)是無(wú)法完全避免的,那么就要查詢?cè)?,才能找到解決方案
我們把 babel 轉(zhuǎn)義后的代碼放出來(lái)就能查出原因了
"use strict"; var _createClass = function () { ... }(); function _classCallCheck(instance, Constructor) { ... } function _possibleConstructorReturn(self, call) { ... // 這個(gè)方法只是做了下判斷,返回第一個(gè)或第二參數(shù) return call && (typeof call === "object" || typeof call === "function") ? call : self; } function _inherits(subClass, superClass) { ...; // 這里的 _inherits 是通過(guò)將子類的原型[[prototype]]指向了父類,所以如果在高級(jí)瀏覽器下,子類的可以繼承到類屬性 // 根本問(wèn)題也是出在這里,IE9 下既沒(méi)有 `setPrototypeOf` 也沒(méi)有 `__proto__` if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } var Test = function (_React$Component) { ... return Test; }(React.Component); Test.defaultProps = { content: "測(cè)試" }; var Test2 = function (_Test) { _inherits(Test2, _Test); function Test2(props) { _classCallCheck(this, Test2); // 這里的 this 會(huì)通過(guò) _possibleConstructorReturn,來(lái)獲取父類構(gòu)造函數(shù)里定義的屬性 // _possibleConstructorReturn 只是做了下判斷,如果第二個(gè)參數(shù)得到了正確執(zhí)行,則返回執(zhí)行結(jié)果,否則返回第一個(gè)參數(shù),也就是子類的 this // 也就是說(shuō)問(wèn)題出在 Object.getPrototypeOf // 在 _inherits 中將子類的原型指向了父類, 這里通過(guò) getPrototypeOf 來(lái)獲取父類,其實(shí)就是 _Test // Object.getPrototypeOf 不能正確的執(zhí)行,導(dǎo)致了子類無(wú)法繼承到在構(gòu)造函數(shù)里定義的屬性或方法,也無(wú)法繼承到類屬性或方法 var _this2 = _possibleConstructorReturn(this, Object.getPrototypeOf(Test2).call(this, props)); _this2.test(); console.log(_this2.props.children); return _this2; } return Test2; }(Test); console.log(Test2.defaultProps); Test2.displayName = "Test"; module.exports = Test2;
通過(guò)上述的代碼注釋,可以得出有兩處問(wèn)題需要解決
正確的獲取父類(解決無(wú)法繼承到在構(gòu)造函數(shù)里定義的屬性或方法)
正確的將子類的原型指向了父類(解決無(wú)法繼承到類屬性或方法)
解決方案通過(guò)文檔的查詢,發(fā)現(xiàn)只要開(kāi)啟 es2015-classes 的 loose 模式即可解決第一個(gè)問(wèn)題
loose 模式Babel have two modes:
A normal mode follows the semantics of ECMAScript 6 as closely as possible.
A loose mode produces simpler ES5 code.
Babel 有兩種模式:
盡可能符合 ES6 語(yǔ)義的 normal 模式。
提供更簡(jiǎn)單 ES5 代碼的 loose 模式。
盡管官方是更推薦使用 normal 模式,但為了兼容 IE,我們目前也只能開(kāi)啟 loose 模式。
在 babel6 中,主要是通過(guò) babel-preset-2015 這個(gè)插件,來(lái)進(jìn)行轉(zhuǎn)義的
我們看下 babel-preset-2015
plugins: [ require("babel-plugin-transform-es2015-template-literals"), require("babel-plugin-transform-es2015-literals"), require("babel-plugin-transform-es2015-function-name"), ... require("babel-plugin-transform-es2015-classes"), ... require("babel-plugin-transform-es2015-typeof-symbol"), require("babel-plugin-transform-es2015-modules-commonjs"), [require("babel-plugin-transform-regenerator"), { async: false, asyncGenerators: false }], ]
是一堆對(duì)應(yīng)轉(zhuǎn)義的插件,從命名上也可看出了大概,比如 babel-plugin-transform-es2015-classes 就是做類的轉(zhuǎn)義的,也就是我們只需把它開(kāi)啟 loose 模式,即可解決我們的一個(gè)問(wèn)題
[require("babel-plugin-transform-es2015-classes"), {loose: true}],
看下開(kāi)啟了 loose 模式的代碼,你會(huì)發(fā)現(xiàn)它的確更接近 ES5
var Test = function (_React$Component) { ... // 這里是 ES5 的寫法 Test.prototype.test = function test() { console.log(this.state.value); }; /* normal 模式是這樣的 { key: "test", value: function test() { console.log(this.state.value); } } */ return Test; }(React.Component); var Test2 = function (_Test) { _inherits(Test2, _Test); function Test2(props) { _classCallCheck(this, Test2); // 這里直接拿到了父類 _Test, 即解決了無(wú)法繼承到在構(gòu)造函數(shù)里定義的屬性或方法 var _this2 = _possibleConstructorReturn(this, _Test.call(this, props)); _this2.test(); return _this2; } return Test2; }(Test);
我們可以通過(guò)去安裝 babel-preset-es2015-loose, 這個(gè)插件來(lái)開(kāi)啟 loose 模式。
但從我們團(tuán)隊(duì)的 老司機(jī) 口中
得到了一個(gè)更好插件babel-preset-es2015-ie,看下這個(gè)插件的代碼,發(fā)現(xiàn)它和原來(lái)的 babel-preset-2015 只有兩行區(qū)別
[ [require("babel-plugin-transform-es2015-classes"), {loose: true}], require("babel-plugin-transform-proto-to-assign"), ]
剛好解決我們上述碰到的兩個(gè)問(wèn)題
這個(gè) babel-plugin-transform-proto-to-assign 插件會(huì)生成一個(gè) _defaults 方法來(lái)處理原型
function _inherits(subClass, superClass) { ...; if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : _defaults(subClass, superClass); }
function _defaults(obj, defaults) { var keys = Object.getOwnPropertyNames(defaults); for (var i = 0; i < keys.length; i++) { var key = keys[i]; var value = Object.getOwnPropertyDescriptor(defaults, key); if (value && value.configurable && obj[key] === undefined) { Object.defineProperty(obj, key, value); } } return obj; }
總結(jié)這個(gè)插件正確的將子類的原型指向了父類(解決無(wú)法繼承到類屬性或方法)
本文講述低版本瀏覽器報(bào)錯(cuò)的原因和解決方案
一方面是提示下在構(gòu)造函數(shù)里不要使用 this.props.xx
另一方面也對(duì)繼承的機(jī)制有了更好的理解
在這次項(xiàng)目中發(fā)現(xiàn)在低版本瀏覽器跑不起來(lái)的兩點(diǎn)主要原因:
SCRIPT5007: 無(wú)法獲取屬性 xxx 的值,對(duì)象為 null 或未定義,這種情況一般是組件繼承后,無(wú)法繼承到在構(gòu)造函數(shù)里定義的屬性或方法,同樣類屬性或方法也同樣無(wú)法繼承
SCRIPT438: 對(duì)象不支持 xxx 屬性或方法,這種情況一般是使用了 es6、es7 的高級(jí)語(yǔ)法,Object.assgin Object.keys 等,這種情況在移動(dòng)端的一些 ‘神機(jī)’ 也一樣會(huì)掛。
第一點(diǎn)本文已經(jīng)分析,預(yù)知第二點(diǎn)講解請(qǐng)見(jiàn)下篇。
備注:下篇會(huì)主要介紹下如何讓 用了 Object.assign 的那位同學(xué)可以繼續(xù)用,又不會(huì)被削。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/80399.html
摘要:在上篇,我們主要拋出了兩個(gè)問(wèn)題,并給出了第一個(gè)問(wèn)題的解決方案。沒(méi)有的實(shí)例方法可以采用方案三委屈下。放棄模式,放棄上篇中提到了開(kāi)啟了模式來(lái)解決低版本瀏覽器無(wú)法繼承到在構(gòu)造函數(shù)里定義的屬性或方法。 回顧 起因: 某天,某測(cè)試說(shuō):這個(gè)頁(yè)面在 IE8 下白屏,9也白。。某前端開(kāi)發(fā): 吭哧吭哧。。。一上午的時(shí)間就過(guò)去了,搞定了。第二天,某測(cè)試說(shuō):IE 又白了。。某前端開(kāi)發(fā): 嘿咻嘿咻。。。誰(shuí)用的...
摘要:本文記錄如下起因在準(zhǔn)備提測(cè)的那天,順便打開(kāi)看一眼注意,這里是原生不是用模擬的,排查后發(fā)現(xiàn),原來(lái)是因?yàn)闃?gòu)造函數(shù)中使用了。簡(jiǎn)寫如下老司機(jī)們肯定能一眼發(fā)現(xiàn)問(wèn)題構(gòu)造函數(shù)中不應(yīng)該使用而是傳入的應(yīng)該改為改正之后,問(wèn)題確實(shí)解決了。 雖然過(guò)了兼容IE6的噩夢(mèng)時(shí)代,IE依舊陰魂不散,因?yàn)槟憧赡苓€要兼容IE9。在ES6已經(jīng)普及的今天,用ES6寫react已經(jīng)成了標(biāo)配。但是babel編譯的js語(yǔ)法,由于某些...
摘要:本文記錄如下起因在準(zhǔn)備提測(cè)的那天,順便打開(kāi)看一眼注意,這里是原生不是用模擬的,排查后發(fā)現(xiàn),原來(lái)是因?yàn)闃?gòu)造函數(shù)中使用了。簡(jiǎn)寫如下老司機(jī)們肯定能一眼發(fā)現(xiàn)問(wèn)題構(gòu)造函數(shù)中不應(yīng)該使用而是傳入的應(yīng)該改為改正之后,問(wèn)題確實(shí)解決了。 雖然過(guò)了兼容IE6的噩夢(mèng)時(shí)代,IE依舊陰魂不散,因?yàn)槟憧赡苓€要兼容IE9。在ES6已經(jīng)普及的今天,用ES6寫react已經(jīng)成了標(biāo)配。但是babel編譯的js語(yǔ)法,由于某些...
摘要:簡(jiǎn)稱已經(jīng)更新之版本也更新至版本裝飾器語(yǔ)法雖然還不是標(biāo)準(zhǔn)但是借助于也能在項(xiàng)目里愉快的玩耍時(shí)代如何啟用裝飾器語(yǔ)法呢我們依舊采用的是通過(guò)劫持對(duì)象達(dá)到修改的目的修改安裝裝飾器語(yǔ)法所需的插件也可以順帶升級(jí)在項(xiàng)目 create-react-app(簡(jiǎn)稱cra)已經(jīng)更新之2.0.3版本, babel也更新至7.x版本, JavaScript裝飾器語(yǔ)法雖然還不是標(biāo)準(zhǔn), 但是借助于babel, 也能在項(xiàng)...
閱讀 1608·2023-04-26 01:54
閱讀 1637·2021-09-30 09:55
閱讀 2658·2021-09-22 16:05
閱讀 1873·2021-07-25 21:37
閱讀 2633·2019-08-29 18:45
閱讀 1900·2019-08-29 16:44
閱讀 1895·2019-08-29 12:34
閱讀 1359·2019-08-23 14:02