以下代码实现了可以将vue对象实例挂载到某个元素,并在挂载元素的后代元素上将vue实例data内的数据通过v-model属性绑定到表单元素上。也可以通过{{ key }}将data内的数据渲染到页面中
html:
<!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>
<script src="./vueDemo.js"></script>
</head>
<body>
<div id="app">
<div>
<input type="text" v-model="name" placeholder="姓名">
<input type="text" v-model="age" placeholder="年龄">
<input type="text" v-model="email" placeholder="邮箱">
</div>
<div>
<p>姓名:<span>{{ name }}</span></p>
<p>年龄:<span>{{ age }}</span></p>
<p>邮箱:<span>{{ email }}</span></p>
</div>
</div>
<script>
let vm = new Vue({
el: "#app",
data: {
name: "张三",
age: 22,
email: "123@qq.com"
}
})
vm.mount() // 调用挂载函数
</script>
</body>
</html>
vueDemo.js
class Vue {
constructor({ el = "#app", data = {} }) {
this.$el = document.querySelector(el) // 设置挂载元素
this.data = data // 存放数据
this.changeDom = {} // 存放进行文本替换的dom元素
}
// 挂载时执行主要函数
mount() {
this.initDom()
this.initData()
}
// 使用数据劫持将数据转化为响应式
initData() {
const _this = this
for (let key in _this.data) { // 遍历data中的数据
Object.defineProperty(_this, key, {
get() {
return _this.data[key] // 返回data中对应字段的值
},
set(value) { // 传入值时
// 修改data内对应字段的值
_this.data[key] = value
// 修改dom对应文本的值
_this.changeDom[key].innerText = value
// console.log("变值节点", _this.changeDom[key], "变值", value)
}
})
}
}
// 执行dom相关函数
initDom() {
this.bindDomList(this.$el) // 获取绑定v-model的元素,并存储
this.bindDomData(this.$el) // 替换文本节点值并存储
console.log("被替换文本节点的元素", this.changeDom)
}
// 获取绑定有v-model属性的元素
bindDomData(father) {
const child = father.childNodes
child.forEach(node => {
if (node.nodeType == 3) { // 寻找子节点中的文本节点
// 判断节点值内是否存在双花括号{{}},进行数据替换
let _nodeValue = node.nodeValue // 获取当前节点值,去空
let reg = /\{\{(.+?)\}\}/ // 正则匹配最短双花括号中间值
// 判断文本是否为空,并且是否能匹配到
if (_nodeValue.trim().length > 0 && reg.test(_nodeValue)) {
// 匹配到则获取匹配结果
let _key = _nodeValue.match(/\{\{(.+?)\}\}/)[1].trim();
// console.log("待绑定键名", _key, "对应值", this.data[_key])
// console.log("当前节点", node)
this.changeDom[_key] = node.parentNode // 存入被替换文本节点的父节点
// 将文本节点中的双花括号部分替换为data中对应的值
node.nodeValue = _nodeValue.replace(reg, this.data[_key] || "")
}
}
if (node.childNodes) { // 如果当前节点存在子节点,则进行递归
this.bindDomData(node)
}
});
}
// 获取绑定有v-model属性的元素,并进行数据双向绑定
bindDomList(father) {
const child = father.childNodes
child.forEach(node => {
// 如果当前节点为元素节点,并且拥有v-model属性
if (node.nodeType == 1 && node.getAttribute("v-model")) {
const _vModel = node.getAttribute("v-model")
node.value = this.data[_vModel] || '' // 将data中的值赋值给元素
node.addEventListener("input", this.bindTwoWayData.bind(this, _vModel, node), false)
}
if (node.childNodes) { // 如果当前节点存在子节点,则进行递归
this.bindDomList(node)
}
})
}
// 进行数据双向绑定
bindTwoWayData(_key, _ele) {
let _value = _ele.value
this.data[_key] = _value
this[_key] = _value
// const _value = input.value;
//this.data[key] = _value;
// this[key] = _value;
}
}
代码中若有误的地方,欢迎大佬们指正!