下面这里是我自己写的一个小型的vue,原理就是proxy:
//Proxy天生没有prototype,因此要加上,不然extends会报错 Proxy.prototype = Proxy.prototype || Object.prototype class myVue extends Proxy { constructor(options) { let data = options.data || {} super(data, { get(target, name, proxy) { if(name in target) { return target[name] } else { throw new Error(`不存在data'${name}'`) } }, set(target, name, value, proxy) { target[name] = value _container.render() } }) // 不能够在constructor里面设置data的值,因此公共变量只能在constructor里面定义,通过函数调用传递 // this.$el = document.querySelector(options.el) //data被类本身代理,而其他options经过初步处理后我们保存在一个变量对象_container中 let _container = {} window.onload = function() { _container.data = data _container.el = document.querySelector(options.el) _container.oldEl = document.querySelector(options.el).cloneNode(true) _container.methods = options.methods || {} //特殊地我们需要一个重新渲染el的内部方法也要存在_container里面 //绑定_container本身是为了render函数能够使用存在_container里面的options的内容 _container.render = render.bind(_container) _container.render() } } //get只能get到data对象里面的值,所以在内部定义的函数也没办法用 // zzz(){ // alert(123) // } } function render() { let _computer = (e) => { let val = '' with(this.data){ val = eval(e) } return val } //先把dom上的替换成“备份” this.el.parentNode.replaceChild(this.oldEl, this.el); //"备份"切换到this.el上等待被渲染 this.el = this.oldEl //再复制一份留“备份” this.oldEl = this.oldEl.cloneNode(true) //处理花括号 this.el.innerHTML = this.el.innerHTML.replace(/{{[^{}]+}}/, (str) =>{ let e = str.substring(2,str.length-2) return _computer(e) }) //处理:属性 let nodes = this.el.getElementsByTagName('*') Array.from(nodes).forEach(node => { Array.from(node.attributes).forEach(attr => { if(attr.nodeName.startsWith(':')){ node.setAttribute(attr.nodeName.substring(1),_computer(attr.value)) node.removeAttribute(attr.nodeName) } else if (attr.nodeName.startsWith('@')) { if(!this.methods[attr.value]) { throw new Error(`methods里面没有方法'${attr.value}'`) } node.addEventListener(attr.nodeName.substring(1),function(){ this.methods[attr.value]() }.bind(this),false) node.removeAttribute(attr.nodeName) } }) }) }
对应调用的HTML:
<!DOCTYPE html> <html> <head> <title>myVue</title> <script src="my-vue.js"></script> <script> let vm = new myVue({ el: '#app', data: { aaa: 123, bbb: '你好啊,张啊咩' }, methods:{ sayHello() { alert('hello') } } }) console.log(vm.aaa) </script> </head> <body> <div id="app"> {{aaa}} <span :title="bbb" @click="sayHello"> hahaha </span> </div> </body> </html>