1.创建index.html
<script src="./Compile.js"></script>
<script src="./Myvue.js"></script>
<body>
<div id="app">
<input type="text" v-model="msg">
<div>{{msg}}---{{info}}</div>
</div>
<script>
const mv = new MyVue({
el: '#app',
data: {
msg: 'hello',
info: 'world'
}
})
</script>
</body>
2.创建MyVue文件获取当前元素对象
class MyVue {
constructor(vm) {
this.vm = vm
// 当前对象的data数据
this.$data = vm.data
// 获取当前元素
this.$el = document.querySelector(vm.el)
// 开始模板编译,传入当前对象
new Compile(this)
}
}
3.创建Compile文件,实现模板编译
class Compile {
constructor(vm) {
this.vm = vm
this.el = vm.$el
// 创建虚拟元素节点
const fragment = this.nodeToFragment()
// 虚拟元素节点
this.compile(fragment)
// 将虚拟元素插入页面容器
this.vm.$el.appendChild(fragment)
}
compile (fragment) {
const nList = fragment.childNodes
nList.forEach((node) => {
const nType = node.nodeType
if (nType === 3) {//文本节点
// 解析文本
this.compileText(node)
} else if (nType === 1) {//元素节点
// 元素节点
this.compileElement(node)
}
if (node.childNodes && node.childNodes !== 0) {
this.compile(node)
}
})
}
//元素节点
compileElement (node) {
//获取标签的属性
const attrs = node.attributes
//如果属性长度>0
if (!!attrs.length) {
//将attrs类数组转化为真正的数组
const attrsArr = Array.from(attrs)
attrsArr.forEach((att) => {
// 如果有v-model属性就讲属性的值赋值给data里面的值
if (att.nodeName === 'v-model') {
const val = att.value
node.value = this.vm.$data[val]
}
})
}
}
//文本节点
compileText (node) {
const con = node.textContent
// 解析出{{}},变量赋值
const reg = /\{\{(.+?)\}\}/g
if (reg.test(con)) {
const newVal = con.replace(reg, (...arg) => {
// 从data数据哪去属性名为arg[1]的值
return this.vm.$data[arg[1]]
})
// 赋值给虚拟节点
node.textContent = newVal
}
}
nodeToFragment () {
const f = document.createDocumentFragment()
while (this.el.firstChild) {
f.appendChild(this.el.firstChild)
}
return f
}
}
compile 函数,循环元素节点,判断节点类型
compileElement 节点为元素类型的处理方法
compileText,节点为文本类型,正则解析大括号内容,将其替换为data数据
nodeToFragment 创建文本对象
效果图