1. 程式人生 > >【開源】騰訊 Omio 釋出 - 全面相容 IE9 和移動端

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

寫在前面

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

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


立即使用

$ 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 function
arr.purePush(111) //don't trigger observe callback

arr.size(2) //trigger observe callback
arr.length = 2 //don't trigger observe callback

//if obj.c is undefined
obaa.set(obj, 'c', 3)
obj.c = 4 //trigger observe callback

//if obj.c is undefined
obj.c = 3
obj.c = 4 //don't trigger observe callback
複製程式碼

第五坑 - MVVM 的 mappingjs 不支援

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 打造,擁有良好的閱讀體驗和相容性。

開始使用吧

→ Omi Github