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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章