【開源】騰訊 Omio 釋出 – 全面相容 IE8 和移動端

當耐特發表於2018-12-18

騰訊 Omio 釋出 – 全面相容 IE8 和移動端

在微信支付、手機QQ、騰訊TEG、騰訊IEG等團隊已經能夠使用 Omi 應用於大量的 to b 的專案以及內部管理系統,為了達到 Omi 全覆蓋,相容 to c 端各種瀏覽器環境,所以有了 Omio, 擁有幾乎和 Omi 一模一樣的語法。

相容老瀏覽器的 Omi 版本,→ Github

【開源】騰訊 Omio 釋出 – 全面相容 IE8 和移動端

立即使用

$ npm i omi-cli -g             $ omi init-o my-app   $ cd my-app           $ npm start                     $ npm run build               複製程式碼

與 omi 不同之處

omio 擁有 omi一樣的語法,但是也有一些差異需要注意:

  • omio 支援 staticCss,omi 是不支援的

cssstaticCss 的區別是 ? 例如:

render() { 
return ( <
div>
<
my-ele name={this.name
}>
<
/my-ele>
<
my-ele name={this.name
}>
<
/my-ele>
<
my-ele name={this.name
}>
<
/my-ele>
<
/div>
)
}複製程式碼

如上面的例子,css方法會渲染三次,並插入到 head,而staticCss 只會渲染一次。當你 update 元件或者 setState 時候,css方法會渲染三次,並更新head裡對應三個地方的樣式,staticCss 不再渲染。

  • Omio 不支援 slot, 請使用 props.children 代替,像 react 一樣
  • Omio 支援 store 注入,但不支援 store path updating
  • Omio 不支援 render array,未來可能支援
  • Omio 不支援 fire 觸發自定義事件,可以和 react 一樣使用 props.xxx() 去觸發。Omi 同時支援 fire and props.xxx() 兩種方式。

Omi 專案中使用 Omio

先安裝:

npm i omio複製程式碼

配置 Webpack Alias

如果你想在已經存在的 omi 專案下使用 omio,你可以使用下面配置,不用任何程式碼更改:

module.exports = { 
//... resolve: {
alias: {
omi: 'omio'
}
}
};
複製程式碼

相容 IE 踩坑

第一坑 – 偽陣列

IE下 querySelectorAll 出來的偽陣列,沒有 array 相關的方法:

const codes = document.querySelectorAll('xxx')//掛了codesArr.forEach(() =>
{
})複製程式碼

需要轉成真陣列:

const codes = Array.prototype.slice.call(document.querySelectorAll('xxx'))複製程式碼

第二坑 – 靜態屬性丟失

這是 Omi 的原始碼:

function define(name, ctor) { 
if (ctor.is === 'WeElement') {
options.mapping[name] = ctor;
if (ctor.data &
&
!ctor.pure) {
ctor.updatePath = getUpdatePath(ctor.data);

}
}
}複製程式碼

但是在 IE 下進入不了 if 條件!!Omi 原始碼裡明明有有靜態屬性:

class WeElement { 
static is = 'WeElement' constructor(props, store) {
...
} ... ... render() {

}
}複製程式碼

為什麼丟失了呢?追根溯源一下:

使用 define:

define('my-p', class extends WeElement { 
render(props) {
return props.children[0]
}
})複製程式碼

編譯後的程式碼:

define('my-p', function (_WeElement) { 
_inherits(_class, _WeElement);
function _class() {
_classCallCheck(this, _class);
return _possibleConstructorReturn(this, _WeElement.apply(this, arguments));

} _class.prototype.render = function render$$1(props) {
return props.children[0];

};
return _class;

}(WeElement));
複製程式碼

那麼問題就出在 _inherits 過程中把靜態屬性 is 丟失了!

function _inherits(subClass, superClass) { 
subClass.prototype = Object.create(superClass &
&
superClass.prototype, {
constructor: {
value: subClass, enumerable: false, writable: true, configurable: true
}
});
if (superClass) {
Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;

}
}複製程式碼

好,由於是編譯註入程式碼,後面可能也需要支援純函式的元件定義,所以這樣解決了:

function define(name, ctor) { 
//if (ctor.is === 'WeElement') { options.mapping[name] = ctor;
if (ctor.data &
&
!ctor.pure) {
ctor.updatePath = getUpdatePath(ctor.data);

} //
}

}複製程式碼

第三坑 – Object.assign IE 不支援

由於 Omio 原始碼裡使用了 Object.assign,所以這裡需要 polyfill 一下:

if (typeof Object.assign != 'function') { 
// Must be writable: true, enumerable: false, configurable: true Object.defineProperty(Object, "assign", {
value: function assign(target, varArgs) {
// .length of function is 2 'use strict';
if (target == null) {
// TypeError if undefined or null throw new TypeError('Cannot convert undefined or null to object');

} var to = Object(target);
for (var index = 1;
index <
arguments.length;
index++) {
var nextSource = arguments[index];
if (nextSource != null) {
// Skip over if undefined or null for (var nextKey in nextSource) {
// Avoid bugs when hasOwnProperty is shadowed if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
to[nextKey] = nextSource[nextKey];

}
}
}
} return to;

}, writable: true, configurable: true
});

}複製程式碼

由於 IE9 支援了 ES5, webpack 編譯出來的 es5,所以並不需要引入 es5-shim 來相容。

第四坑 – Proxy 不支援

因為需要監聽資料變化,Omi 使用的是 Proxy,所以這裡需要一個降級方案 – obaa 庫,監聽任意物件的任意變化。

安裝 obaa

npm install obaa複製程式碼

使用

observe object:

var obj = { 
a: 1
};
obaa(obj, function (name, value , old) {
console.log(name + "__" + value + "__" + old);

});
obj.a = 2;
//a__2__1 複製程式碼

observe array:

var arr = [1, 2, 3];
obaa(arr, function (name, value, old) {
console.log(name + "__" + value+"__"+old);

});
arr.push(4);
//Array-push__[1,2,3,4]__[1,2,3] arr[3] = 5;
//3__5__4複製程式碼

observe class instance:

var User = function (name, age) { 
this.name = name;
this.age = age;
//observe name only obaa(this, ["name"], function (name, value, oldValue) {
console.log(name + "__" + value + "__" + oldValue);

});

}var user = new User("lisi", 25);
user.name = "wangwu";
//name__wangwu__lisi user.age = 20;
//nothing output複製程式碼

其他:

arr.push(111) //trigger observe callback//every method of array has a pureXXX functionarr.purePush(111) //don't trigger observe callbackarr.size(2) //trigger observe callbackarr.length = 2 //don't trigger observe callback//if obj.c is undefinedobaa.set(obj, 'c', 3)obj.c = 4 //trigger observe callback//if obj.c is undefinedobj.c = 3obj.c = 4 //don't trigger observe callback複製程式碼

第五坑 – MVVM 的 mappingjs 不支援

【開源】騰訊 Omio 釋出 – 全面相容 IE8 和移動端

mappingjs 完全利用的 proxy,所以資料 mapping 的過程中會自動更新檢視。但是切換成 obaa 之後,發現陣列 length 更新檢視不會更新,陣列增加檢視不會更新。review 了 mappingjs 發現:

  • mappingjs 使用了 array.length 改變陣列長度
  • mappingjs 使用 array[index] 增加元素

這樣在 obaa 是不允許的,不然的話無法監聽到變化, obaa 要求:

  • 使用 array.size(len) 改變陣列長度
  • 使用 array.push 增加元素

所以就有了 mappingjs-omio, 這樣的話, Omio 同樣可以使用真正的 MVVM 架構。

Omio 實戰

md2site 完全使用 omio 打造,擁有良好的閱讀體驗和相容性。

【開源】騰訊 Omio 釋出 – 全面相容 IE8 和移動端
【開源】騰訊 Omio 釋出 – 全面相容 IE8 和移動端
【開源】騰訊 Omio 釋出 – 全面相容 IE8 和移動端

相容 IE8

第一坑 – 關鍵字作為 key

const map = { 
var: 'view', switch: 'switch'
}複製程式碼

要改成:

const map = { 
'var': 'view', 'switch': 'switch'
}複製程式碼

關鍵字不能作為 JSON 的 key。

第二坑 – Object.assign polyfill 不可用

Object.assign polyfill 使用了 Object.defineProperty, IE8 下報錯,所以把 Object.assign 替換成了 object-assign

'use strict'/* eslint-disable no-unused-vars */var getOwnPropertySymbols = Object.getOwnPropertySymbolsvar hasOwnProperty = Object.prototype.hasOwnPropertyvar propIsEnumerable = Object.prototype.propertyIsEnumerablefunction toObject(val) { 
if (val === null || val === undefined) {
throw new TypeError('Object.assign cannot be called with null or undefined')
} return Object(val)
}export function assign(target, source) {
var from var to = toObject(target) var symbols for (var s = 1;
s <
arguments.length;
s++) {
from = Object(arguments[s]) for (var key in from) {
if (hasOwnProperty.call(from, key)) {
to[key] = from[key]
}
} if (getOwnPropertySymbols) {
symbols = getOwnPropertySymbols(from) for (var i = 0;
i <
symbols.length;
i++) {
if (propIsEnumerable.call(from, symbols[i])) {
to[symbols[i]] = from[symbols[i]]
}
}
}
} return to
}複製程式碼

第三坑 – Object.create 不可用

使用 polyfill 並且要註釋掉下面的程式碼!因為傳遞二個引數沒法 polyfill!

if (typeof Object.create !== 'function') { 
Object.create = function(proto, propertiesObject) {
if (typeof proto !== 'object' &
&
typeof proto !== 'function') {
throw new TypeError('Object prototype may only be an Object: ' + proto)
} else if (proto === null) {
throw new Error( "This browser's implementation of Object.create is a shim and doesn't support 'null' as the first argument." )
} // if (typeof propertiesObject != 'undefined') { // throw new Error("This browser's implementation of Object.create is a shim and doesn't support a second argument.");
//
}
function F() {
} F.prototype = proto return new F()
}
}複製程式碼

第四坑 – text 節點設定屬性

//ie8 errortry { 
out[ATTR_KEY] = true
} catch (e) {
}複製程式碼

直接 try catch 包起來,測試下來目前不影響正常使用。

第五坑 – addEventListener 和 removeEventListener

這裡直接使用了 mdn 的 polyfill,其他 polyfill 都有坑!

if (!Element.prototype.addEventListener) { 
var oListeners = {
};
function runListeners(oEvent) {
if (!oEvent) {
oEvent = window.event;

} for (var iLstId = 0, iElId = 0, oEvtListeners = oListeners[oEvent.type];
iElId <
oEvtListeners.aEls.length;
iElId++) {
if (oEvtListeners.aEls[iElId] === this) {
for (iLstId;
iLstId <
oEvtListeners.aEvts[iElId].length;
iLstId++) {
oEvtListeners.aEvts[iElId][iLstId].call(this, oEvent);

} break;

}
}
} Element.prototype.addEventListener = function (sEventType, fListener /*, useCapture (will be ignored!) */) {
if (oListeners.hasOwnProperty(sEventType)) {
var oEvtListeners = oListeners[sEventType];
for (var nElIdx = -1, iElId = 0;
iElId <
oEvtListeners.aEls.length;
iElId++) {
if (oEvtListeners.aEls[iElId] === this) {
nElIdx = iElId;
break;

}
} if (nElIdx === -1) {
oEvtListeners.aEls.push(this);
oEvtListeners.aEvts.push([fListener]);
this["on" + sEventType] = runListeners;

} else {
var aElListeners = oEvtListeners.aEvts[nElIdx];
if (this["on" + sEventType] !== runListeners) {
aElListeners.splice(0);
this["on" + sEventType] = runListeners;

} for (var iLstId = 0;
iLstId <
aElListeners.length;
iLstId++) {
if (aElListeners[iLstId] === fListener) {
return;

}
} aElListeners.push(fListener);

}
} else {
oListeners[sEventType] = {
aEls: [this], aEvts: [[fListener]]
};
this["on" + sEventType] = runListeners;

}
};
Element.prototype.removeEventListener = function (sEventType, fListener /*, useCapture (will be ignored!) */) {
if (!oListeners.hasOwnProperty(sEventType)) {
return;

} var oEvtListeners = oListeners[sEventType];
for (var nElIdx = -1, iElId = 0;
iElId <
oEvtListeners.aEls.length;
iElId++) {
if (oEvtListeners.aEls[iElId] === this) {
nElIdx = iElId;
break;

}
} if (nElIdx === -1) {
return;

} for (var iLstId = 0, aElListeners = oEvtListeners.aEvts[nElIdx];
iLstId <
aElListeners.length;
iLstId++) {
if (aElListeners[iLstId] === fListener) {
aElListeners.splice(iLstId, 1);

}
}
};

}複製程式碼

第六坑 – string trim 不支援

if (!String.prototype.trim) { 
String.prototype.trim = function () {
return this.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '')
}
}複製程式碼

第七坑 – 資料監聽

import { 
render, WeElement, define
} from '../../src/omi'define('my-counter', class extends WeElement {
//ie8 不能使用 observe //static observe = true data = {
count: 1
} sub = () =>
{
this.data.count-- //手動 update this.update()
} add = () =>
{
this.data.count++ //手動 update this.update()
} render() {
return ( <
div>
<
button onClick={this.sub
}
>
-<
/button>
<
span>
{this.data.count
}<
/span>
<
button onClick={this.add
}
>
+<
/button>
<
/div>
)
}
})render(<
my-counter />
, 'body')
複製程式碼

如果你不需要相容 IE8,你可以使用 static observe = true 進行資料監聽自動更新檢視。

第八坑 – ES5 Shim

<
script src="https://cdnjs.cloudflare.com/ajax/libs/es5-shim/4.5.7/es5-shim.min.js">
<
/script>
複製程式碼

開始使用吧

→ Omi Github

來源:https://juejin.im/post/5c185b39f265da61380f2403#comment

相關文章