实现一个简易的vue

35次阅读

共计 3133 个字符,预计需要花费 8 分钟才能阅读完成。

1./compiler ⽬目录是编译模版;
2./core ⽬目录是 Vue.js 的核⼼心 (也是后⾯面的重点);
3./platforms ⽬目录是针对核⼼心模块的‘平台’模块;
4./server ⽬目录是处理理服务端渲染;
5./sfc ⽬目录处理理单⽂文件 .vue;
6./shared ⽬目录提供全局⽤用到的⼯工具函数。
Vue.js 的组成是由 core + 对应的‘平台’补充代码构成 (独立构建和运行时构建 只是 platforms 下 web 平台的两种选择)。
vue 的双向数据绑定
双向绑定(响应式原理)所涉及到的技术
1. Object.defineProperty
2. Observer
3. Watcher
4. Dep
5. Directive
1. Object.defineProperty
var obj = {};
var a;
Object.defineProperty(obj,’a’,{
get: function(){
console.log(‘get val’);
return a;
},
set: function(newVal){
console.log(‘set val:’ + newVal);
a = newVal;
}
});
obj.a // get val; 相当于 <span>{{a}}</span>
obj.a = ‘111’; // set val:111 相当于 <input v-model=”a”>
setter 触发消息到 Watcher watcher 帮忙告诉 Directive 更新 DOM,DOM 中修改了数据 也会通知给 Watcher,watcher 帮忙修改数据。
2. Observer
观察者模式是软件设计模式的一种。
在此种模式中,一个目标对象管理所有相依于它的观 察者对象,并且在它本身的状态改变时主动发出通知。
这通常透过呼叫各观察者所提供的 方法来实现。此种模式通常被用来实时事件处理系统。
订阅者模式涉及三个对象:
发布者、主题对象、订阅者,三个对象间的是一对多的关系,
每当主题对象状态发生改变时,其相关依赖对象都会得到通知,并被自动更新。
看一个简单的示例:

vue 里边怎么操作的呢?vue observer

watcher

Dep

Directive

弄明白原理和架构之后,我们来实现一个简单的 vue 双向数据绑定

1. 这个 Vue 是从哪里来的呢?
是通过上述方法实例化的一个对象;但是里边有两个未知生物 observe?Compile?observe 中写的是双向绑定的核心原理就是 Object.defineProperty
通过 set,get 来设置值与获取值
把 text 属性绑定到 vue 实例上面去使用
那其中的 Dep 又是什么呢?
添加订阅者跟通知订阅更新
再来看一下 Compile 中写的什么吧
function Compile(node, vm) {
if (node) {
this.$frag = this.nodeToFragment(node, vm);
return this.$frag;
}
}
Compile.prototype = {
nodeToFragment: function (node, vm) {
var self = this;
var frag = document.createDocumentFragment(); // 创建一段 html 文档片段
var child;

while (child = node.firstChild) {
self.compileElement(child, vm);
frag.append(child); // 将所有子节点添加到 fragment 中
}
return frag;
},
compileElement: function (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 方法
// 触发 set vm[name]
vm[name] = e.target.value;
});
// node.value = vm[name]; // 将 data 的值赋给该 node
new Watcher(vm, node, name, ‘value’);
}
};
}
// 节点类型为 text
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, ‘nodeValue’);
}
}
},
}
哦,原来 Compile 中是渲染 html 的啊。其中的 Watcher 是不是监控节点变化,然后给 Dep 通知的呢?
function Watcher(vm, node, name, type) {
Dep.target = this;
this.name = name; //text
this.node = node; // 节点
this.vm = vm; // vue 实例
this.type = type; //nodeValue 当前节点的值
this.update();
Dep.target = null;
}

Watcher.prototype = {
update: function() {
this.get();
var batcher = new Batcher();
batcher.push(this);
// this.node[this.type] = this.value; // 订阅者执行相应操作
},
cb:function(){
this.node[this.type] = this.value; // 订阅者执行相应操作
},
// 获取 data 的属性值
get: function() {
this.value = this.vm[this.name]; // 触发相应属性的 get
}
}

哎呦,咱们猜对了呢,这样双向数据绑定马上就要完成了, 只剩一个 Vue.nextTick() 的地方了
/**
* 批处理构造函数
* @constructor
*/
function Batcher() {
this.reset();
}

/**
* 批处理重置
*/
Batcher.prototype.reset = function () {
this.has = {};
this.queue = [];
this.waiting = false;
};

/**
* 将事件添加到队列中
* @param job {Watcher} watcher 事件
*/
Batcher.prototype.push = function (job) {
if (!this.has[job.name]) {
this.queue.push(job);
this.has[job.name] = job;
if (!this.waiting) {
this.waiting = true;
setTimeout(() => {
this.flush();
});
}
}
};

/**
* 执行并清空事件队列
*/
Batcher.prototype.flush = function () {
this.queue.forEach((job) => {
job.cb();
});
this.reset();
};
看完后是不是觉得超简单呢?
vue3 版本将做出巨大的变化,把 Dep 跟 Watcher 都干掉了,html 直接跟数据进行绑定,等 vue3 出来后,在写一篇关于 vue 的文章吧
看完后能帮我点个赞吗?

正文完
 0