[項目地址](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的實現基于如下代碼:

# vue3響應式核心原理
直接上代碼了,proxy:

可以看到我們proxy的實現要簡潔一些,另外性能有瀏覽器優化,性能要比defineProperty好。
# 發布訂閱模式
## 發布訂閱和觀察者模式
這兩種模式在vue中有各自的運用場景,兩種模式本質是相同的,但是是有區別的,他們經常被混為一談。
在vue中的自定義事件 就是一個典型的發布訂閱模式,比如下面截圖的代碼,

又比如我們兄弟組件傳值可以用eventBus:

### 發布者訂閱者模式

### 觀察者模式

## 關于兩種模式的總結
**觀察者模式**:觀察者模式是有具體目標調度,比如事件觸發,Dep就會去調用觀察者的方法,所以觀察者和發布者之間存在依賴(強耦合)
**發布訂閱模式**:發布訂閱模式由調度中心調用,因此發布者和訂閱者不需要知道對方存在。
 (圖片源自互聯網)
# 正餐開始
乞丐最小版本vue整體分析如下

vue:
將data中的成員注入到vue實例,并把data中的成員專程getter/setter
observer:
能夠將數據對象所有屬性監聽,變化通知Dep。
## Vue模塊
* 負責接收初始化參數
* 負責把data中的屬性轉換成getter/setter
* 負責調用observer監聽data中所有屬性的變化
* 負責調用compiler解析指令/差值表達式
代碼實現:

## Observer模塊
* 負責把data選項中的屬性轉換成響應式數據
* data中的某個屬性也是對象,把該屬性轉換成響應式數據
* 數據變化發送通知


## 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
* 通知所有的觀察者

代碼如下:

## watcher
* 當數據變化出發依賴,dep通知所有的watcher實例更新視圖
* 自身實例化的時候往dep中添加自己


## 雙向綁定
* 數據變化 改變視圖
* 視圖變化 改變數據

- 前言
- 工作中的一些記錄
- 破解快手直播間的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
- 最后