前言
最近零食很忙出了一道上机题:给你30分钟,请实现数据的双向绑定!如下图
机智的小伙马上就想到了解决办法。这种题还用半个小时?给我5分钟,我要开始装逼了~
<div id="app">
<input type="text" v-model='name1' placeholder="请输入" />
<input type="text" v-model='name2' placeholder="请输入" />
<span>{{name1}}</span>
<span>{{name2}}</span>
</div>
<script type="text/javascript">
var vm = new Vue({
el: '#app',
data: {
name1: "周杰伦",
name2: "许嵩",
},
})
</script>
面试官:今天面试到此结束,根据你今天的表现!我还得跟部门领导商量一下!请回去等消息吧~
很显然面试的效果是达到了,可是完全没走心!什么叫走心呢?那就是不能提及尤雨溪但是还是要成为尤雨溪!
手动实现
1.创建一个类吧
class VAE {
constructor(el, data) {
this.el = document.querySelector(el)
this._data = data
this.domPool = {}
this.init()
}
//初始化
init() {
this.reactive()
this.method()
this.render(this.el)
}
//数据劫持
reactive() {
}
//绑定方法
method() {
}
//刷新dom
render(el) {
}
}
2.数据劫持
class VAE {
constructor(el, data) {
this.el = document.querySelector(el)
this._data = data
this.init()
}
//初始化
init() {
//省略...
}
//数据劫持
reactive() {
let _this = this
this.data = {}
for (let key in this._data) {
Object.defineProperty(this.data, key, {
get() {
return _this._data[key]
},
set(val) {
_this._data[key] = val
}
})
}
}
method() {} //省略..
render(el) {}//省略..
}
3.绑定事件
class VAE {
constructor(el, data) {
this.el = document.querySelector(el)
this._data = data
this.init()
}
//初始化
init() {
//省略...
}
//数据劫持
reactive() {
let _this = this
this.data = {}
for (let key in this._data) {
Object.defineProperty(this.data, key, {
get() {
return _this._data[key]
},
set(val) {
_this._data[key] = val
}
})
}
}
//绑定事件
method() {
const onInput = document.querySelectorAll('input')
let _this = this
onInput.forEach(input => {
const vModel = input.getAttribute('v-model') //找到v-model属性
input.value = _this.data[vModel]
if (vModel) {
input.addEventListener('keyup', function (e) {
_this.data[vModel] = e.target.value //改变就重新赋值
})
}
})
}
render(el) {}//省略..
}
4.渲染视图
class VAE {
constructor(el, data) {
this.el = document.querySelector(el)
this._data = data
this.init()
}
//初始化
init() {
//省略...
}
//数据劫持
reactive() {
let _this = this
this.data = {}
for (let key in this._data) {
Object.defineProperty(this.data, key, {
get() {
return _this._data[key]
},
set(val) {
_this.domPool[key].innerText = val //一旦值改变就更新span标签内容
_this._data[key] = val
}
})
}
}
//绑定事件
method() {
const onInput = document.querySelectorAll('input')
let _this = this
onInput.forEach(input => {
const vModel = input.getAttribute('v-model') //找到v-model属性
input.value = _this.data[vModel]
if (vModel) {
input.addEventListener('keyup', function (e) {
_this.data[vModel] = e.target.value //改变就重新赋值
})
}
})
}
render(el) {
let childNodes = el.childNodes;
childNodes.forEach(node => {
if (node.nodeType == 3) {
if (node.nodeValue.trim().length) {
let key = node.nodeValue.match(/\{\{(.+?)\}\}/)[1].trim() //找到花括号里面的变量名
this.domPool[key] = node.parentNode //找到父元素节点,也就是span标签
node.parentNode.innerText = this.data[key] //初始化花括号的值
}
}
node.childNodes && this.render(node)
})
}
}
5.页面使用
<div id="app">
<input type="text" v-model="name1" placeholder="请输入" />
<input type="text" v-model="name2" placeholder="请输入" />
<br>
<span>{{name1}}</span>
<br>
<span>{{name2}}</span>
</div>
<script>
const app = new VAE('#app', {
name1: '周杰伦',
name2: '许嵩'
})
</script>
6.效果如下
总结:
其实理解双向绑定并不难,核心还是Object.defineProperty,唯一的一些细节处理是怎么关联dom! 实际的vue源码还涉及到了依赖收集。但是本篇文章不讲源码,就是单纯的实现一个效果!怎么样兄弟们,如果觉得这篇文章对你有帮助,就点赞+收藏吧!有问题欢迎留言~