Blog
Blog copied to clipboard
Vue兼容IE的研究、实现
Vue兼容IE的研究、实践
背景
公司前端框架使用的Vue.js,因为它基于Object.defineProperties
来监听数据变化,而IE8是不支持Object.defineProperties
的,所以注定不能兼容IE8。
Vue作者尤雨溪也无意让Vue支持IE8,见
https://github.com/vuejs/vuejs.org/issues/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
的大体逻辑如下
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并没有区别。耶!
确实可以跑。厉害了
Vue兼容IE的研究、实践
背景
公司前端框架使用的Vue.js,因为它基于
Object.defineProperties
来监听数据变化,而IE8是不支持Object.defineProperties
的,所以注定不能兼容IE8。 Vue作者尤雨溪也无意让Vue支持IE8,见 vuejs/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
的大体逻辑如下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并没有区别。耶!
对于 vue2.x 有实现的思路吗?
厉害了, 赞一个
有完整的代码么?
@QJesus https://github.com/abeet/vue1-ie