数据双向绑定原理(源码,保存)

<!DOCTYPE html>

<html>

<head>

<meta charset="utf-8">

<title>我的双向绑定例子</title>

</head>

<body>

<!-- 实现vue -->

<div id="app">

<input type="text" v-model="text">

{{ text }}

</div>

</body>

</html>

<script type="text/javascript">

function defineReactive(obj, key, val) {

var dep = new Dep();

Object.defineProperty(obj, key, {

get: function() {

if (Dep.target) {

dep.addSub(Dep.target); //Dep.target指向watcher的this

}

return val

},

set: function(newVal) {

if (newVal === val) {

return

}

val = newVal;

console.log('新值:' + val);

// 一旦更新立马通知

dep.notify();

}

})

}

/*观察者函数*/

function observe(obj, vm) {

for (let key of Object.keys(obj)) {

defineReactive(vm, key, obj[key]);

}

}

// 劫持子节点数据,调用后id为app的div就没有内容了,进行节点处理,后面再把节点添加回去

function nodeToFragment(node, vm) {

var fragment = document.createDocumentFragment();

var child;

while (child = node.firstChild) {

compile(child, vm);

fragment.appendChild(child);

}

return fragment

}

/*编译函数*/

function compile(node, vm) {

var reg = /\{\{(.*)\}\}/; // 来匹配 {{ xxx }} 中的xxx

// 如果是元素节点

if (node.nodeType === 1) {

var attr = node.attributes;

// 解析元素节点的所有属性

for (let i = 0; i < attr.length; i++) {

if (attr[i].nodeName == 'v-model') {

var name = attr[i].nodeValue; // 看看是与哪一个数据相关

node.addEventListener('input', function(e) {

vm[name] = e.target.value; // 将实例的text 修改为最新值

});

node.value = vm[name]; // 将data的值赋给该node

node.removeAttribute('v-model');

}

};

}

// 如果是文本节点

if (node.nodeType === 3) {

if (reg.test(node.nodeValue)) {

var name = RegExp.$1; // 获取到第一个匹配的字符串

name = name.trim(); //删除头尾空白符

// node.nodeValue = vm[name];  // 将data的值赋给该node

new Watcher(vm, node, name); // 不直接通过赋值的操作,而是通过绑定一个订阅者

}

}

}

/*Watcher构造函数*/

function Watcher(vm, node, name) {

Dep.target = this; // Dep.target 是一个全局变量

this.vm = vm;

this.node = node;

this.name = name;

this.update();

Dep.target = null;

}

Watcher.prototype = {

update() {

this.get();

this.node.nodeValue = this.value; // 注意,这是更改节点内容的关键

},

get() {

this.value = this.vm[this.name]; // 触发相应的get

}

}

/*dep构造函数*/

function Dep(){

    this.subs = [];

}

Dep.prototype = {

    addSub(sub){

        this.subs.push(sub);

    },

    notify(){

        this.subs.forEach(function(sub){

            sub.update(); //调用watcher的更新方法,sub已经指向watcher

        })

    }

}

/*Vue构造函数*/

function Vue(options) {

this.data = options.data;

var data = this.data;

observe(data, this);

var id = options.el;

var dom = nodeToFragment(document.getElementById(id), this);

// 处理完所有dom节点后,重新将内容添加回去

document.getElementById(id).appendChild(dom);

}

var vm = new Vue({

el: 'app',

data: {

text: 'hello world'

}

});

</script>

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容