初始化渲染頁面
- 在initstate()初始化之後,我們需要把響應式處理後的數據渲染到頁面上
- 我們在原型上加上一個 $mount() 方法,該方法是一個主要處理掛載的方法
$mount()
工作流程:
- 獲取當前實例,獲取 el(此時爲字符串)
- 通過 query() 獲取當前el節點,再賦值給 vm.$el 與 el(現在它們是dom節點)
- 編寫 updateComponent() 更新方法,用於觸發 _update() 更新視圖方法
- 利用 _update() 方法渲染節點(通過Watcher渲染)
Vue.prototype.$mount = function (el) {
const vm = this;
const options = vm.$options;
el = vm.$el = query(el)
// 默認先查找有沒有 render 方法,沒有render會採用template template也沒有就用el中的內容
let updateComponent = () => {
console.log("更新渲染實現")
vm._update();
}
new Watcher(vm, updateComponent)
}
_update()更新視圖方法
工作流程:
- 首先獲取當前實例與el
- 在vue2.0中是使用虛擬dom實現數據渲染(由於流程複雜,我們先使用操作dom的方式解決)
- 獲取一個空節點
- 將 el 下所有的子節點都拿出來
- 文本替換 compiler(node, vm)
- 替換完再放回el
Vue.prototype._update = function () {
let vm = this
let el = vm.$el
// 渲染所有元素 把內容替換爲數據
let node = document.createDocumentFragment()
let firstChild
while (firstChild = el.firstChild) {
node.appendChild(firstChild)
}
// 文本替換
compiler(node, vm)
el.appendChild(node) //替換完再放進去
}
compiler(node, vm)
在src下創建compiler文件夾,創建一個index.js
工作流程:
- 取出子節點,循環子節點遞歸調用
- 將類數組轉換爲數組,判斷是否爲元素節點,如果是則繼續遞歸,如果是文本節點,則使用 compilerText() 編譯文本
export function compiler(node, vm) {
// 1 取出子節點、
let childNodes = node.childNodes
childNodes = Array.from(childNodes)
childNodes.forEach(child => {
if (child.nodeType === 1) {
compiler(child, vm)
} else if (child.nodeType === 3) {
util.compilerText(child, vm)
}
})
}
compilerText()
工作流程:
- 取出當前節點的文本
- 使用正則表達式 const defaultRGE = /{{((?:.|\r?\n)+?)}}/g 查找到 {{}} 中的文本,使用 vm中的data數據替換其文本
const defaultRGE = /\{\{((?:.|\r?\n)+?)\}\}/g
export const util = {
getValue(vm, expr) {
let keys = expr.split('.')
return keys.reduce((memo, current) => {
memo = memo[current]
return memo
}, vm)
},
compilerText(node, vm) {
if(!node.expr){
node.expr = node.textContent
}
node.textContent = node.expr.replace(defaultRGE, function (...args) {
return util.getValue(vm, args[1])
})
}
}
Watcher類
創建 watcher.js 模塊,向外暴露 Watcher類
- 參數列表:
- vm 當前實例
- exprOrfn 傳入的方法或者表達式
- cb 回調函數
- opts 其他參數
- 工作流程:
- 將所有的參數 存下,並且給Watcher加個id
- 判斷 exprOrfn 是不是函數,如果是,則賦給 getter()
- 創建 get方法,該方法用於調用getter()方法,並且get默認創建時就觸發
import { pushTarget, popTarget } from "./observer/dep";
let id = 0
class Watcher {
constructor(vm, exprOrFn, cb = () => { }, opts) {
this.vm = vm
this.exprOrFn = exprOrFn
this.cb = cb
this.id = id++
if (typeof exprOrFn === 'function') {
this.getter = exprOrFn
}
this.get()
}
get() {
this.getter()
}
export default Watcher