Vue实现双向绑定的原理以及响应式数据的方法

2020-06-14 06:19:51易采站长站整理

一、vue中的响应式属性

Vue中的数据实现响应式绑定

1、对象实现响应式:

是在初始化的时候利用definePrototype的定义set和get过滤器,在进行组件模板编译时实现water的监听搜集依赖项,当数据发生变化时在set中通过调用dep.notify进行发布通知,实现视图的更新。

2、数组实现响应式:

对于数组则是通过继承重写数组的方法splice、pop、push、shift、unshift、sort、reverse、等可以修改原数组的方式实现响应式的,但是通过length以及直接利用item[index]方式修改数组是不能实现响应式的改变dom(这种两种方式涉及到数组的内部实现)。在数据更新后为了避免过于频繁的进行dom的操作,在vue中会将更新的dom进行批量操作,而不是直接有数据更新就刷新dom,vue将需要更新的dom压入异步队列记性批量操作,提高性能。

下面具体的实现,实现原理大致如下:

    

对象中实现双向数据绑定,可以直接在浏览器查看效果:


<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Two-way data-binding</title>
</head>
<body>

<div id="app">
<input type="text" v-model="text">
{{ text }}
</div>

<script>
function observe (obj, vm) {
Object.keys(obj).forEach(function (key) {
defineReactive(vm, key, obj[key]);
});
}

function defineReactive (obj, key, val) {

var dep = new Dep();

Object.defineProperty(obj, key, {
get: function () {
// 添加订阅者watcher到主题对象Dep
if (Dep.target) dep.addSub(Dep.target);
return val
},
set: function (newVal) {
if (newVal === val) return
val = newVal;
// 作为发布者发出通知
dep.notify();
}
});
}

function nodeToFragment (node, vm) {
var flag = document.createDocumentFragment();
var child;

while (child = node.firstChild) {
compile(child, vm);
flag.appendChild(child); // 将子节点劫持到文档片段中
}

return flag;
}

function compile (node, vm) {
var reg = /{{(.*)}}/;
// 节点类型为元素
if (node.nodeType === 1) {
var attr = node.attributes;
// 解析属性
for (var i = 0; i < attr.length; i++) {
if (attr[i].nodeName == 'v-model') {
var name = attr[i].nodeValue; // 获取v-model绑定的属性名
node.addEventListener('input', function (e) {
// 给相应的data属性赋值,进而触发该属性的set方法
vm[name] = e.target.value;