設置 | 登錄 | 註冊

作者共發了2篇帖子。

Vue兼容IE的研究、实现 #35

1樓 巨大八爪鱼 2025-4-10 16:16

Vue兼容IE的研究、实践背景

公司前端框架使用的Vue.js,因为它基于Object.defineProperties来监听数据变化,而IE8是不支持Object.defineProperties的,所以注定不能兼容IE8。
Vue作者尤雨溪也无意让Vue支持IE8,见
vuejs/v2.vuejs.org#50
目前Vue.js我们只用在后台界面,网站前台由于要兼容IE8,使用的是jQuery或Backbone,造成技术栈不统一,工程化不够。
为此想寻求Vue.js兼容IE8的方案。

可行性调研

github上搜索"vue IE8"
找到一个项目
https://github.com/wcflmy/VM4IE8
这个项目的数据监听是通过一个把一个model的数据,附加到一个dom元素上,
然后ie8下使用Object.defineProperty来监听dom上属性的改变
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty#Internet_Explorer_8_specific_notes
从Mozilla的资料上来看,有两大问题,
一个问题:因为model是附在dom上的,会有很多的dom属性,不能再用for遍历,
二个问题:数据键名要避开所有的dom属性名,
综上:基本等于没有用

另外一个未完成的项目,如
https://github.com/wusfen/vm
https://github.com/wusfen/vue-ie
发邮件问了作者,他用的类似Angular的检测机制,对所有的异步的函数、方法,进行复写、注入 $foceUpdate ,如 ajax, setTimeout, Image.onload, ... ,基本覆盖常用情况。
不太喜欢Angular里面的zone.js对各种异步函数的污染,在实践中有遇到过因为Promise被污染导致某些库不正常(axios?)的情况。

https://www.cnblogs.com/rubylouvre/p/3598133.html
感觉还是Avalon里用到的VBScript方案更靠谱一些
要注意的是
value作为VBScript的特殊值也不能使用作属性名。

在网上还有一个模板解析器用到了VBScript来作defineProperties数据检测
https://github.com/purplebamboo/pat/blob/master/build/latest/pat.js
应该是有借鉴Avalon

至此定下方向,使用VBScript实现setter/getter来代替Object.defineProperties来监听数据变化

在github上再搜索了一些VBScript实现setter/getter的资料
http://webreflection.blogspot.com/2011/03/rewind-getters-setters-for-all-ie-with.html
https://gist.github.com/jeffrafter/189354
有人提到在dojo里就用了VBScript实现setter/getter,
由此看来这不是Avalon的首创。只是很奇怪dojo把VBScript在iframe里去执行,为什么不用exeScript?
https://download.dojotoolkit.org/release-1.9.7/dojo-release-1.9.7/dojox/lang/observable.js.uncompressed.js

实现过程

由于Vue1的代码逻辑比Vue2更直观,并且没有使用虚拟dom,有利于debug,所以我计划先实现Vue1兼容IE8,下次再实现Vue2兼容IE8。

VBScript实现setter/getter测试

将Avalon中的 createViewModel 抽取出来,
在IE8下写了一个页面测试双向绑定,成功!

将Pat.js中的define方法抽取出来,
在IE8下写了一个页面测试双向绑定成功,成功!

先解决vue1在IE8下的语法错误

然后是vue在不管双向绑定的情况下,在ie8下跑起来,
各种要shim的方法,
包括但不限于以下



IE8下Array实例没有forEach等方法
IE8下String实例没有trim等方法
IE8下Function实例没有bind等方法
IE8下Object没有keys等方法
在测试中发现IE8下dom元素的cloneNode方法有bug,多个text子节点只clone了一个
IE8下input元素没有change事件
当然还有最重要的一个,IE8下没有Object.defineProperties

先找一个es5-shim 把数组等基本对象的成员方法补齐。
https://github.com/es-shims/es5-shim

由于一般的es6-shim库,代码量大,而且对Object.defineProperties的实现根本不实用,所以vue1中涉及的es6特有的方法,我要一个个补齐,而不再引用某个库。
先找了 es5 shim es6 shim 参考
https://github.com/seamus-oconnor/lift-js/tree/master/src/modules/es5/object
https://github.com/paulmillr/es6-shim/blob/master/es6-shim.js
补齐了以下几个方法/属性

Object.freeze //无法实现,仅不抛错 Object.isExtensible //无法实现,仅不抛错 Object.create Object.getOwnPropertyNames Object.assign Element.prototype.addEventListener Element.prototype.removeEventListener Event.prototype.target Event.prototype.stopPropagation Event.prototype.preventDefault Object.defineProperties // 对于非dom元素,还需要调用 Object.definePropertiesByVBS才返回有监听属性改变的VBScript对象 Object.defineProperty // 对于非dom元素,还需要调用 Object.definePropertiesByVBS才返回有监听属性改变的VBScript对象

至此vue.js在IE8下载入时不再抛出脚本错误

最关键的一步,实现Object.definePropertiesByVBS

上面的实现过程中,Object.defineProperty和Object.defineProperties被实现为仅配置一个js对象有哪些属性要被监听,调用 Object.definePropertiesByVBS 后,会返回一个新的VBScript对象,这个对象上会监听属性的改变。
注意:是新的VBScript对象,不是原来的js对象
Object.definePropertiesByVBS的大体逻辑如下

0 Then', '\t\t[' + key + '] = [__proxy](me, "get", "' + key + '")', '\tEnd If', '\tOn Error Goto 0', '\tEnd Property' ) } buffer.push('End Class') buffer.push('Function ' + className + 'F(proxy, cb_poll)', '\tSet ' + className + 'F = (New ' + className + ')(proxy,cb_poll)') buffer.push('End Function') window['parseVB'](buffer.join('\r\n')) var re = window[className + 'F'](proxy, cb_poll) return re }">

window.execScript(['Function parseVB(code)', '\tExecuteGlobal(code)', 'End Function'].join('\r\n'), 'VBScript') Object.defineProperty = function (obj, prop, desc) { obj.__defindeProperties__[prop] = desc } var VB_ID = 0 Object.definePropertiesByVBS = function (obj) { var cb_poll = {} var className = 'VB' + VB_ID++ buffer.push('Class ' + className) for (var key in obj.__defindeProperties__) { var desc = obj.__defindeProperties__[key] cb_poll[key + '_set'] = desc.set buffer.push( '\tPublic Property Let [' + key + '](value)', '\t\tCall [__proxy](me, "set", "' + key + '", value)', '\tEnd Property', '\tPublic Property Set [' + key + '](value)', '\t\tCall [__proxy](me, "set", "' + key + '", value)', '\tEnd Property' ) cb_poll[key + '_get'] = desc.get buffer.push( '\tPublic Property Get [' + key + ']', '\tOn Error Resume Next', '\t\tSet [' + key + '] = [__proxy](me, "get", "' + key + '")', '\tIf Err.Number <> 0 Then', '\t\t[' + key + '] = [__proxy](me, "get", "' + key + '")', '\tEnd If', '\tOn Error Goto 0', '\tEnd Property' ) } buffer.push('End Class') buffer.push('Function ' + className + 'F(proxy, cb_poll)', '\tSet ' + className + 'F = (New ' + className + ')(proxy,cb_poll)') buffer.push('End Function') window['parseVB'](buffer.join('\r\n')) var re = window[className + 'F'](proxy, cb_poll) return re }

上面只是核心代码示例,仅实现为根据配置返回一个可监听属性改变的VBScript对象。
要让vue实例可以执行原型vue原型链上的方法,还要将vue原型链上的方法复制到这个VBScript对象上。
对Object.definePropertiesByVBS作出了改进实现了方法 Object.VBVueFactory 来生成vue实例(实际上是VBScript对象)

用这个修改版的vue.js实现了一个todolist,中间排查了若干问题,最终成功跑起来。

至此,完成兼容IE8的Vue.js,使用上与原版Vue.js并没有区别。耶!

2樓 巨大八爪鱼 2025-4-10 16:17

內容轉換:

回覆帖子
內容:
用戶名: 您目前是匿名發表。
驗證碼:
看不清?換一張
©2010-2025 Purasbar Ver3.0 [手機版] [桌面版]
除非另有聲明,本站採用知識共享署名-相同方式共享 3.0 Unported許可協議進行許可。