自己用vue也不久了,学习之初就看过vue实现的原理,当时看也是迷迷糊糊,能说出来最基本的,但是感觉还是理解的不深入,最近找到了之前收藏的文章,跟着大神一步步敲了一下简易的实现,算是又加深了理解。

原文链接

<!DOCTYPE html>
<html lang="en"> <head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head> <body>
<div id="app">
{{name}}
<p v-if="isShow"> <span>{{name}}</span>
</p>
<input type="text" id="a" v-model="name"> </div> <script> function compile(node, vm) { var reg = /\{\{(.*)\}\}/;
if (node.nodeType === 1) {
var attr = Array.prototype.slice.call(node.attributes);
//解析属性
for (var 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;
//eval(`vm.data.${name}=e.target.value`)
console.log(vm)
})
node.value = eval(`vm.${name}`);
node.removeAttribute('v-model');
}
if (attr[i].nodeName == 'v-if') {// 这里是我自己加的指令,真正肯定不是这样玩的吧
var name = attr[i].nodeValue;
var isInsert = eval(`vm.${name}`);
if (!isInsert) {
node = '';
return node;
} else {
node.removeAttribute('v-if');
} }
} }
if (node.nodeType === 3) {
if (reg.test(node.nodeValue)) {
var name = RegExp.$1;
name = name.trim();
//node.nodeValue = eval(`vm.data.${name}`);
new Watcher(vm, node, name)//这里给每个属性文本节点生成一个Watcher对象,嗯,大致跟vue的原理相似
}
}
return node;
} function nodeToFragment(node, vm) {
var flag = document.createDocumentFragment();
var child; while (child = node.firstChild) {
child = compile(child, vm)
if (child !== "") {
if (child.childNodes.length != 0) {
child.append(nodeToFragment(child, vm));
}
} else {
node.removeChild(node.firstChild)
} flag.append(child);
}
return flag;
} function defineReactive(obj, key, val) {
var dep = new Dep();//这里给每个属性生成一个数据订阅中心,它可以存储订阅它的所有watcher,
Object.defineProperty(obj, key, {
get: function () {
if (Dep.target) dep.addSub(Dep.target);//这里的Dep.target是对应的Watcher对象,这里是dep对象调用addSub,我看别人说的vue源码是在Watcher对象实现addSub操作的
return val;
},
set: function (newVal) {
if (newVal === val) return;
console.log('修改了', key) val = newVal;
dep.notify();//数据更新了,就通知所有的观察者实例
}
})
} function observer(obj, vm) {
Object.keys(obj).forEach(function (key) {
defineReactive(vm, key, obj[key]);
})
} function Watcher(vm, node, name) {
Dep.target = this;//在实例化新的watcher对象时把Dep.target赋值为this,也就是每个指令对应的那个watcher对象,这样在下面调用this.update,从而调用this.get时触发数据的get方法,从而触发dep.addSub(Dep.target),这样这个watcher就被添加进去
this.name = name;
this.node = node;
this.vm = vm;
this.update();
Dep.target = null;//为了保证全局只有一个,在最后需要清空,为下一个指令做准备
}
Watcher.prototype = {
update: function () {
this.get();//更新时调用get()
this.node.nodeValue = this.value; },
get: function () {
this.value = this.vm[this.name]; //会触发vm.data中属性的get方法,进而可以添加watcher到Dep中
}
} function Dep() {
this.subs = [];
}
Dep.prototype = {
addSub: function (sub) {
this.subs.push(sub);
},
notify: function () {
this.subs.forEach(function (sub) {
sub.update();
})
}
} function Vue(options) {
this.data = options.data;
var id = options.el;
var data = this.data;
observer(data, this)
var dom = nodeToFragment(document.getElementById(id), this);
document.getElementById(id).appendChild(dom);
}
var vm = new Vue({
el: 'app',
data: {
text: {
name: 'jay'
},
'name': 'zxf',
isShow: true
}
}) </script>
</body> </html>

  以上的备注是我看了文章后自己的一些理解,这个得简单实现跟vue的源码好像是有区别,比如

function defineReactive(obj, key, val) {
var dep = new Dep()
var childOb = Observer.create(val)
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function metaGetter() {
// 如果Dep.target存在,则进行依赖收集
if (Dep.target) {
dep.depend()
if (childOb) {
childOb.dep.depend()
}
}
return val
},
set: function metaSetter(newVal) {
if (newVal === val) return
val = newVal
childOb = Observer.create(newVal)
dep.notify()
}
})
}
Watcher.prototype.addDep = function (dep) {
var id = dep.id
if (!this.newDeps[id]) {
this.newDeps[id] = dep
if (!this.deps[id]) {
this.deps[id] = dep
dep.addSub(this)
}
}
} 作者:百度外卖大前端技术团队
链接:https://juejin.im/post/5a44b15e51882538fe631406
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

 源码中在dep的get里面并没有直接调用addsub,而是调用

dep.depend();

而dep.depend() 实际执行了 Watcher.addDep() 
这样最终是在Watcher对象的方法里面实现了增加订阅者,感觉好像是订阅者主动要求dep添加自己,而简易框架是,dep主动添加,感觉都差不多吧。,可能原文作者为了方便,而vue可能处于更全面的考虑吧

另外我也懂了vue原理中watcher,dep,observer,compile等这几个对象的概念和他们之间的关系,observer是数据观察者,主要是劫持data数据,添加set,get,并观察数据的变动,若触发了set,就调用dep的notify方法,若触发了get,就会新增watcher到dep的存放观察者的数组中。

另外这个图片也可以说明一切,终于看懂了,以前知道这么连接,只是不知道为什么这么连接的,另外还有一篇文章是讲解关于这幅图的理解的那个是对源码进行解读的,比较详细

vue源码解读,另外还有一篇文章也是大牛自己实现vue的简易框架的,与这个得实现思路略微有所不同:vue简易框架2

 

跟随大神实现简单的Vue框架的更多相关文章

  1. Android开发相关的Blog推荐——跟随大神的脚步才能成长为大神

    转载:https://blog.csdn.net/zhaokaiqiang1992/article/details/43731967 CSDN 鸿洋:http://blog.csdn.net/lmj6 ...

  2. 某大神C#框架后台发送信息的查找及破解

    最近在博客园瞎逛的时候,发现了某个大神发布的一个c#框架,一看框架,叫牛逼框架,嗯,装B效果太好了,界面很炫,虽然有很多的组件还是不怎么完善,但是,已经可以初步运用于项目了. 先来看看界面:   在进 ...

  3. 手把手教你从零写一个简单的 VUE

    本系列是一个教程,下面贴下目录~1.手把手教你从零写一个简单的 VUE2.手把手教你从零写一个简单的 VUE--模板篇 今天给大家带来的是实现一个简单的类似 VUE 一样的前端框架,VUE 框架现在应 ...

  4. 有关UITableViewCell的侧滑删除以及使用相关大神框架MGSwipeTableCell遇到的小问题

    提起笔,却不知道从何写起了,今天一整天都耗费在了这个可能根本不算是问题的小问题上,至今仍有一种蛋蛋的忧桑..(噢,不是提笔,是键盘手T_T) 表格视图在项目中就像是每日的家常便饭,在cell上添加侧滑 ...

  5. 理解vue ssr原理,自己搭建简单的ssr框架

    前言 大多数Vue项目要支持SSR应该是为了SEO考虑,毕竟对于WEB应用来说,搜索引擎是一个很大的流量入口.Vue SSR现在已经比较成熟了,但是如果是把一个SPA应用改造成SSR应用,成本还是有些 ...

  6. ABP实践(4)-abp前端vue框架之简单商品增删改查(帮助刚入门的新手快速了解怎么才能加入自己的功能并运行起来)

    提示:如有不明白的地方请先查看前3篇ABP实践系列的文章 1,下载及启动abp项目前后端分离(netcore+vue) 2,修改abp数据库为mysql 3,商品系列api接口(本文主要依赖在这个商品 ...

  7. Vue框架简介及简单使用

    目录 一.前端框架介绍 二.vue框架简介 三.vue使用初体验 1. vue如何在页面中引入 2. 插值表达式 3. 文本指令 4. 方法指令(事件指令) 5. 属性指令 四.js数据类型补充 1. ...

  8. java数据类型易错点简单总结,欢迎大神前辈补充!谢谢

    数据类型那这边看似简单,花了我很长时间也就是才练到几成"功力"吧.还希望路过的大神在下面补充,菜鸟的我深受感谢! 首先看两个思考题 思考题1:请问下面这个有没有问题 double ...

  9. 【学习笔记】剖析MVVM框架,简单实现Vue数据双向绑定

    前言: 学习前端也有半年多了,个人的学习欲望还比较强烈,很喜欢那种新知识在自己的演练下一点点实现的过程.最近一直在学vue框架,像网上大佬说的,入门容易深究难.不管是跟着开发文档学还是视频教程,按步骤 ...

随机推荐

  1. 12 Spring Data JPA:springDataJpa的运行原理以及基本操作(下)

    spring data jpaday1:orm思想和hibernate以及jpa的概述和jpa的基本操作 day2:springdatajpa的运行原理 day2:springdatajpa的基本操作 ...

  2. Tkinter控件

    1.顶层(Toplevel) Toplevel为其他控件提供单独的容器.共有四种类型(1)主顶层,作为根被应用,应该就是root(2)子顶层,依赖于根,根破坏,子顶层也被破坏(3)临时顶层,画在父顶层 ...

  3. NGDC|BIGD

    生命组学 生命起源经过复杂演化诞生了大量生物体及其基因组. 现今NCBI最大的基因组: 植物:糖松27.6G 动物:墨西哥蝾螈32.4G 大数据能做什么? 大数据时代如同大航海时代一样,需要具有与时代 ...

  4. 使用GitHub Pages服务进行域名URL转发

    有时,你注册了一个域名,但是你没有搭建服务器.你希望这个域名能指向你的主页/博客/微博等.但是,很多域名注册商不提供这种服务,或者这是一项收费服务.这时你可以使用GitHub来实现这一功能. 你需要导 ...

  5. _\_call\_\_

    __call__ 一.__call__ 对象后面加括号时,触发执行. 注:构造方法的执行是由创建对象触发的,即:对象 = 类名() :而对于 __call__ 方法的执行是由对象后加括号触发的,即:对 ...

  6. 如何动态调用WebService

    封装WBS类 using System; using System.Collections.Generic; using System.Linq; using System.Web; using Sy ...

  7. docker安装(centos-7)

    centos7安装docker:Docker 要求 CentOS 系统的内核版本高于 3.10 ,查看本页面的前提条件来验证你的CentOS 版本是否支持 Docker .通过 uname -r 命令 ...

  8. 10)global预定义变量

    代码展示: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w ...

  9. 怎么设置tomcat在get请求的中文也不乱码?两种情况下配置

    我们都知道,get请求和post请求的一个比较显著区别就是,在使用post请求的时候,中文不会乱码,但是在使用get请求的时候,如果url后面带有中文就会乱码了.那么这个怎么解决呢? 前提:配置项目的 ...

  10. jenkins推送docker镜像到远程仓库

    参考链接:https://blog.csdn.net/qq_34252622/article/details/92791262