[项目地址](https://github.com/fujiazhang/vue)
[TOC]
在开始之前需要有一些基本的认识
# 数据驱动
Vue.js 一个核心思想是数据驱动。所谓数据驱动,是指视图是由数据驱动生成的,我们对视图的修改,不会直接操作 DOM,而是通过修改数据。它相比我们传统的前端开发,如使用 jQuery 等前端库直接修改 DOM,大大简化了代码量。特别是当交互复杂的时候,只关心数据的修改会让代码的逻辑变的非常清晰,因为 DOM 变成了数据的映射,我们所有的逻辑都是对数据的修改,而不用碰触 DOM,这样的代码非常利于维护。
在 Vue.js 中我们可以采用简洁的模板语法来声明式的将数据渲染为 DOM:
~~~
<div id="app">
{{ message }}
</div>
~~~
~~~
var app = new Vue({
el: '#app',
data: {
message: 'Hello Vue!'
}
})
~~~
最终它会在页面上渲染出`Hello Vue`。
# vue2响应式核心原理
直接上代码了,vue2 defineProperty api的实现基于如下代码:
![](https://img.kancloud.cn/f5/c1/f5c162fa9a0f785f906942012f893950_1322x1130.png)
# vue3响应式核心原理
直接上代码了,proxy:
![](https://img.kancloud.cn/31/31/3131c693d31e1aec433ffc75afd15810_1462x1152.png)
可以看到我们proxy的实现要简洁一些,另外性能有浏览器优化,性能要比defineProperty好。
# 发布订阅模式
## 发布订阅和观察者模式
这两种模式在vue中有各自的运用场景,两种模式本质是相同的,但是是有区别的,他们经常被混为一谈。
在vue中的自定义事件 就是一个典型的发布订阅模式,比如下面截图的代码,
![](https://img.kancloud.cn/fe/c3/fec3fe74148a949d5c00b615e6a6698a_792x420.png)
又比如我们兄弟组件传值可以用eventBus:
![](https://img.kancloud.cn/c4/1e/c41eb77e8eb93497c5989dcafdccad78_1420x798.png)
### 发布者订阅者模式
![](https://img.kancloud.cn/cb/ee/cbeec9f3f0846f23d96d87495a0ba7e8_1512x1104.png)
### 观察者模式
![](https://img.kancloud.cn/be/93/be937280d434f6f26fbc2d17a1f86187_1286x1048.png)
## 关于两种模式的总结
**观察者模式**:观察者模式是有具体目标调度,比如事件触发,Dep就会去调用观察者的方法,所以观察者和发布者之间存在依赖(强耦合)
**发布订阅模式**:发布订阅模式由调度中心调用,因此发布者和订阅者不需要知道对方存在。
![](https://img.kancloud.cn/7e/ef/7eef45585bc20349cae4172bb2f207ae_1002x706.png) (图片源自互联网)
# 正餐开始
乞丐最小版本vue整体分析如下
![](https://img.kancloud.cn/87/33/8733cea9fa519eadd9de5cb0808dad43_756x460.png)
vue:
将data中的成员注入到vue实例,并把data中的成员专程getter/setter
observer:
能够将数据对象所有属性监听,变化通知Dep。
## Vue模块
* 负责接收初始化参数
* 负责把data中的属性转换成getter/setter
* 负责调用observer监听data中所有属性的变化
* 负责调用compiler解析指令/差值表达式
代码实现:
![](https://img.kancloud.cn/20/d5/20d59091baa63921a8f597889992296a_1532x1180.png)
## Observer模块
* 负责把data选项中的属性转换成响应式数据
* data中的某个属性也是对象,把该属性转换成响应式数据
* 数据变化发送通知
![](https://img.kancloud.cn/f3/c3/f3c394cf2aa1218ffc5dded7f9308909_1280x1090.png)
![](https://img.kancloud.cn/cf/76/cf766a9b25e0ae10141270147a014f0f_1278x980.png)
## Compiler
* 负责编译模版,解析指令/差值表达式
* 负责页面的首次渲染
* 当数据变化后 重新渲染视图
代码如下:
```
/**
* @description:
* 1.负责编译模版,解析指令/差值表达式
* 2.负责页面的首次渲染
* 3.当数据变化后重新渲染视图
* 未引入虚拟dom直接操作的实dom
*/
class Compiler {
constructor(vm) {
this.el = vm.$el this.vm = vm this.compile(this.el)
}
/**
* @description: 编译模版,处理文本和元素节点
*/
compile(el) {
let childNodes = el.childNodes Array.from(childNodes).forEach(node = >{
if (this.isTextNode(node)) {
this.compileText(node)
} else if (this.isElement(node)) {
this.compileElement(node)
}
//判断下是否还有字节点
if (node.childNodes && node.childNodes.length) {
this.compile(node)
}
})
}
/**
* @description: 变异元素节点 处理指令
*/
compileElement(node) {
// console.log(node.attributes)
Array.from(node.attributes).forEach(attr = >{
let attrName = attr.name
if (this.isDirective(attrName)) {
attrName = attrName.substr(2) let key = attr.value this.update(node, key, attrName)
}
})
}
update(node, key, attrName) {
let updateFn = this[(attrName + 'Update')] updateFn && updateFn.call(this, node, this.vm[key], key)
}
/**
* @description: 处理v-text
*/
textUpdate(node, value, key) {
node.textContent = value
//创建watcher对象 数据改变更新试图
new Watcher(this.vm, key, (newValue) = >{
node.textContent = newValue
})
}
/**
* @description: 处理v-model
*/
modelUpdate(node, value, key) {
node.value = value
//创建watcher对象 数据改变更新试图
new Watcher(this.vm, key, (newValue) = >{
node.value = newValue
})
// 双向绑定
node.addEventListener('input', () = >{
this.vm[key] = node.value
})
}
/**
* @description: 编译文本节点,处理差值表达式
*/
compileText(node) {
let reg = /\{\{(.+?)\}\}/let value = node.textContent
if (reg.test(value)) {
let key = RegExp.$1.trim() node.textContent = value.replace(reg, this.vm[key])
//创建watcher对象 数据改变更新试图
new Watcher(this.vm, key, (newValue) = >{
node.textContent = newValue
})
}
}
/**
* @description: 判断元素属性是否指令
*/
isDirective(attrName) {
return attrName.startsWith('v-')
}
/**
* @description: 判断节点是否是文本节点
*/
isTextNode(node) {
return node.nodeType === 3 //node接点属性3为本文 1为元素
}
/**
* @description: 判断节点是否是元素节点
*/
isElement(node) {
return node.nodeType === 1 //node接点属性3为本文 1为元素
}
}
```
## Dep
* 收集依赖、添加观察者(watcher
* 通知所有的观察者
![](https://img.kancloud.cn/db/ca/dbca6b1d95b97d4abef6054d7cd7a432_982x316.png)
代码如下:
![](https://img.kancloud.cn/79/e7/79e7d47926cbfec15c5963dc221f69f3_1096x920.png)
## watcher
* 当数据变化出发依赖,dep通知所有的watcher实例更新视图
* 自身实例化的时候往dep中添加自己
![](https://img.kancloud.cn/1a/ad/1aadc46c9140ba79c17e240ed23d71b1_1040x326.png)
![](https://img.kancloud.cn/2f/ca/2fca88eb1adad4af83b394a8ba639ee7_996x1054.png)
## 双向绑定
* 数据变化 改变视图
* 视图变化 改变数据
![](https://img.kancloud.cn/78/f9/78f94cdf62c003a88ac96810c519935c_1212x634.png)
- 前言
- 工作中的一些记录
- 破解快手直播间的webSocket的连接
- 快手「反」反爬虫的研究记录
- HTML AND CSS
- 遇到的一些还行的css笔试题
- css常见面试题
- JavaScript 深度剖析
- ES6到ESNext新特性
- 关于http与缓存
- 关于页面性能
- 关于浏览器的重排(reflow、layout)与重绘
- 手写函数节流
- 手写promise
- 手写函数防抖
- 手写图片懒加载
- 手写jsonp
- 手写深拷贝
- 手写new
- 数据结构和算法
- 前言
- 时间复杂度
- 栈
- 队列
- 集合
- 字典
- 链表
- 树
- 图
- 堆
- 排序
- 搜索
- Webpack
- Webpack原理与实践
- Vue
- Vuejs的Virtual Dom的源码实现
- minVue
- Vuex实现原理
- 一道关于diff算法的面试题
- Vue2源码笔记:源码目录设计
- vue-router源码分析(v4.x)
- React及周边
- 深入理解redux(一步步实现一个 redux)
- React常见面试题汇总
- Taro、小程序等
- TypeScript
- CI/CD
- docker踩坑笔记
- jenkins
- 最后