手写minivue
v-model 与 插值表达式
<!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>Vue</title>
</head>
<body>
<img src="./ndoeType.jpg" alt="">
<div id="app">
<input v-model="text" type="text">
{{text}}
</div>
<script>
class Vue {
constructor(options) {
this.$options = options
let dom = document.querySelector(this.$options.el)
new Observer(this.$options.data)
const f = new Compile(dom, this.$options.data)
dom.append(f)
// console.log(this.$options.data);
}
}
// 数据劫持
class Observer {
constructor(data) {
this.walk(data)
}
walk(data) {
Object.keys(data).forEach(key => {
this.defineReactive(data, key, data[key])
})
}
// 劫持data数据
defineReactive(data, key, value) {
let dep = new Dep()
Object.defineProperty(data, key, {
get() {
if (Dep.target) {
dep.addSub(Dep.target)
}
return value
},
set(newValue) {
value = newValue
dep.notify()
}
})
}
}
class Dep {
constructor() {
this.sub = []
}
addSub(watcher) {
this.sub.push(watcher)
}
notify() {
this.sub.forEach(watcher => watcher.updata())
}
}
//
class Watcher {
constructor(vm, node, name) {
Dep.target = this
this.vm = vm;
this.node = node;
this.name = name
this.updata()
Dep.target = null
}
get() {
this.value = this.vm[this.name]
}
updata() {
this.get()
this.node.nodeValue = this.value
}
}
class Compile {
constructor(node, vm) {
this.node = node;
this.vm = vm;
return this.nodeList(this.node)
}
parse(node, vm) {
if (node.nodeType === 1) {
// node.attributes 获取dom 节点的 属性类数组
Array.from(node.attributes).forEach(v => {
// console.log(v); //=>
/* {baseURI: "http://127.0.0.1:5500/src/miniVue.html"
childNodes: NodeList []
firstChild: null
isConnected: false
lastChild: null
localName: "v-model"
name: "v-model"
namespaceURI: null
nextSibling: null
nodeName: "v-model"
nodeType: 2
nodeValue: "text"
ownerDocument: document
ownerElement: input
parentElement: null
parentNode: null
prefix: null
previousSibling: null
specified: true
textContent: "text"
value: "text"} */
if (v.nodeName === 'v-model') {
node.addEventListener('input', e => {
// 更新vm实例的值
vm[v.nodeValue] = e.target.value
})
// input 的初始值
node.value = vm[v.nodeValue]
}
})
}
// 使用正则截取插值表达式
let reg = /\{\{(.*)\}\}/
if (node.nodeType === 3) {
// 过滤掉空白
if (reg.test(node.nodeValue)) {
// RegExp.$1 获取正则匹配的第一个小括号里面的值
node.nodeValue = vm[RegExp.$1]
// 收集依赖
new Watcher(vm, node, RegExp.$1)
}
}
}
nodeList(node) {
// 创建文档碎片
let f = document.createDocumentFragment()
let child;
while (child = node.firstChild) {
this.parse(child, this.vm)
f.append(child)
}
return f
}
}
new Vue({
el: '#app',
data: {
text: 'hellow Vue'
}
})
</script>
</body>
</html>