创建vue.js文件
class Vue {
constructor(options) {
// console.log(options)
this.$options = options
// 更新视图需要
this.$watchEvent = {}
if (typeof (options.beforeCreated == 'function')) {
options.beforeCreated.bind(this)()
}
this.$data = options.data
this.proxyData()
this.observe()
// console.log(this.$data)
if (typeof (options.created == 'function')) {
options.created.bind(this)()
}
if (typeof (options.beforeMount == 'function')) {
options.beforeMount.bind(this)()
}
this.$el = document.querySelector(options.el)
// console.log(this.$el)
// 渲染节点
this.compile(this.$el)
if (typeof (options.mounted == 'function')) {
options.mounted.bind(this)()
}
}
// 给vue大对象赋属性,来自于data中
// data中的属性值和Vue大对象的属性保持双向(劫持)
// 保证前台this.str 能打印出来
proxyData() {
for (let key in this.$data) {
Object.defineProperty(this, key, {
get() {
return this.$data[key]
},
set(val) {
this.$data[key] = val
}
})
}
}
// 触发data中的数据发生变化来执行watch中的update
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, key) => {
item.update()
})
}
}
})
}
}
compile(node) {
// console.log(node.childNodes)
node.childNodes.forEach((item, index) => {
// 1 是元素,3是文本
// console.log(item.nodeType)
if (item.nodeType == 1) {
// console.log('aclikc', item.hasAttribute('@click'))
if (item.hasAttribute('@click')) {
// 判读是否是click事件
let vmKey = item.getAttribute('@click').trim() // @click绑定的属性名
item.addEventListener('click', (event) => {
this.eventFn = this.$options.methods[vmKey].bind(this)
this.eventFn(event)
})
}
// 判断元素节点是否添加了v-model
if (item.hasAttribute('v-model')) {
let vmKey = item.getAttribute('v-model').trim()
if (this.hasOwnProperty(vmKey)) {
// console.log(this, this[vmKey])
item.value = this[vmKey]
}
// 实现input里面的值修改,data里面的值修改
item.addEventListener('input', (event) => {
this[vmKey] = item.value
})
}
if (item.childNodes.length > 0) {
this.compile(item)
}
}
if (item.nodeType == 3) {
// 正则匹配{{}}
let reg = /\{\{(.*?)\}\}/g;
let text = item.textContent
item.textContent = text.replace(reg, (match, vmKey) => {
console.log(vmKey)
vmKey = vmKey.trim()
// 判断data中是否
if (this.hasOwnProperty(vmKey)) {
// this 当前对象
// item:当前节点
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.key = key
// 节点
this.node = node
// 改变文本节点内容的字符串
this.attr = attr
}
// 执行改变(update)操作
update() {
this.node[this.attr] = this.vm[this.key]
}
}
创建index.html文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
{{str}}
<h1>{{str}}</h1>
<button @click="btn">按钮</button>
<p>{{b}}</p>
<input type="text" v-model="str">
</div>
<script text="text/javascript" src="vue.js">
</script>
<script text="text/javascript">
new Vue({
el: '#app',
data: {
str: '你好',
b: '这也是data'
},
beforeCreated() {
console.log('beforeCreated', this.$el, this.$data)
},
created() {
console.log('created', this.$el, this.$data)
},
beforeMount() {
console.log('beforeMount', this.$el, this.$data)
},
mounted() {
console.log('mounted', this.$el, this.$data)
},
methods: {
btn(e) {
this.str = '改变值'
console.log(this, 'helo')
}
}
})
</script>
</body>
</html>
双向绑定原理
通过Object.defineProperty劫持数据发生的改变,如果数据发生了改变,(在set中进行赋值),触发update方法进行更新节点内容,从而实现了数据的双向绑定
629

被折叠的 条评论
为什么被折叠?



