mvvm
class Vue { constructor(options) { /* 视图的驱动 */ this.$el = options.el this._data = options.data this.$options = options this.$watchEvent = {} console.log(document.querySelector(this.$el),this._data,this.$options); const node = document.querySelector(this.$el) this.observe(); this.proxyData(); this.compile(node) } /* 渲染视图 */ observe() { for (let key in this._data) { let value = this._data[key] let that = this; Object.defineProperty(this._data, key, { get() { return value }, set(val) { value = val; if (that.$watchEvent[key]) { that.$watchEvent[key].forEach(item => { item.update(); }) } } }) } } /* 数据劫持的原理就是对data当中的每一个属性都使用Object.defineProperty绑定get/set方法 */ proxyData() { for (let key in this._data) { Object.defineProperty(this, key, { get() { return this._data[key] }, set(val) { return this._data[key] = val } }) } } /* 模板解析: 将data数据渲染到模板当中 */ compile(node) { node.childNodes.forEach((item, index) => { if (item.nodeType === 1) { if (item.hasAttribute("@click")) { let vmKey = item.getAttribute("@click").trim(); item.addEventListener("click", (e) => { this.$options.methods[vmKey].bind(this)(e) console.log(this); }) } if (item.hasAttribute("v-model")) { console.log(111+item.getAttribute("v-model")); let vmKey = item.getAttribute("v-model").trim(); if (this.hasOwnProperty(vmKey)) { // console.log(item,this[vmKey]); item.value = this[vmKey] } item.addEventListener('input', (event) => { this[vmKey] = item.value }) } if (item.childNodes.length > 0) { this.compile(item) } } // console.log(item, item.nodeType); if (item.nodeType === 3) { // console.log(item, item.textContent); const reg = /\{\{(.*?)\}\}/g let text = item.textContent; console.log(text+222); item.textContent = text.replace(reg, (match, vmKey) => { vmKey = vmKey.trim(); console.log(vmKey+111); /* 渲染视图 */ // console.log(this, vmKey, item, 'textContent'); if (this.hasOwnProperty(vmKey)) { let watch = new Watch(this, vmKey, item, 'textContent') if (this.$watchEvent[vmKey]) { this.$watchEvent[vmKey].push(watch) } else { this.$watchEvent[vmKey] = [] this.$watchEvent[vmKey].push(watch) } } return this._data[vmKey] }) } }); } } class Watch { constructor(vm, key, node, attr) { this.vm = vm; this.node = node; this.key = key; this.attr = attr; } //执行改变 update() { this.node[this.attr] = this.vm[this.key] } }
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <div id="app"> {{msg}} <p>{{ msg }}</p> <h1>{{msg}}</h1> <h4>{{msg}}</h4> <h2>msg</h2> <button @click="btn">点我试试</button> <p @click="pppp">pppbiaoqian </p> <input type="text" v-model="msg"> </div> <script src="./vue.js"></script> <script> const vm = new Vue({ el: "#app", data: { msg: "Hello World", message: "abcd" }, methods: { btn(e) { this.msg = "世上只有妈妈好!" console.log(e, 333333); }, pppp() { console.log("abcddddd"); } } }) console.log(vm); </script> </body> </html>