vue源碼系列04_數據渲染(原始版)

初始化渲染頁面

  1. 在initstate()初始化之後,我們需要把響應式處理後的數據渲染到頁面上
  2. 我們在原型上加上一個 $mount() 方法,該方法是一個主要處理掛載的方法

$mount()

工作流程:

  1. 獲取當前實例,獲取 el(此時爲字符串)
  2. 通過 query() 獲取當前el節點,再賦值給 vm.$elel(現在它們是dom節點)
  3. 編寫 updateComponent() 更新方法,用於觸發 _update() 更新視圖方法
  4. 利用 _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()更新視圖方法

工作流程:

  1. 首先獲取當前實例與el
  2. 在vue2.0中是使用虛擬dom實現數據渲染(由於流程複雜,我們先使用操作dom的方式解決)
    1. 獲取一個空節點
    2. 將 el 下所有的子節點都拿出來
    3. 文本替換 compiler(node, vm)
    4. 替換完再放回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
工作流程:

  1. 取出子節點,循環子節點遞歸調用
  2. 將類數組轉換爲數組,判斷是否爲元素節點,如果是則繼續遞歸,如果是文本節點,則使用 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()

工作流程:

  1. 取出當前節點的文本
  2. 使用正則表達式 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類

  • 參數列表:
    1. vm 當前實例
    2. exprOrfn 傳入的方法或者表達式
    3. cb 回調函數
    4. opts 其他參數
  • 工作流程:
    1. 將所有的參數 存下,並且給Watcher加個id
    2. 判斷 exprOrfn 是不是函數,如果是,則賦給 getter()
    3. 創建 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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章