带你一行一行手写Vue2.0源码系列(三) -初始化渲染

前言

上一篇中我们主要讲的Vue2.0的模版编译原理,它主要是通过不同的正则表达式对模版进行匹配切割从而生产ast语法树,然后遍历语法树生成带有_c、_v和_s等字符的code字符串代码。利用new Function来生成render函数。本文在上一篇的基础上,生成虚拟Vnode并且进行渲染。

我个人推荐的看源码的方式最好是通过视频文章自己能手写一边然后再去看,否则就会造成这样的结果。

真是卷...

正文

挂载入口

javascript 复制代码
// core/instance/lifecycle.js

export function mountComponent(vm, el) {
  // 1. 调通render方法生成虚拟dom
  vm.$el = el;

  const updateComponent = () => {
    vm._update(vm._render()); // vm._render()执行就生成虚拟Vnode
  }

  new Watcher(vm, updateComponent, true); // 创建渲染Watcher
}

组件调用$mount进行挂载,其核心就是调用mountComponent,在render函数,函数内部类似于_c('div', {id:"app"}, _c('p', {style:{"color":" red"}}, _v("我的姓名是:"+_s(name)+"我的年龄是:"+_s(age))),_c('p', null, _v(_s(maxAge))),_c('myh', null, ),_c('p', null, )),执行的过程中会执行到_c、_v等函数,所以我们需要事先定义出来,同时它还执行了update方法。

javascript 复制代码
// core/instance/lifecycle.js

export function initLifeCycle(Vue) {
  Vue.prototype._update = function (vnode) {
    const vm = this;
    const el = vm.$el;
    const prevVnode = vm._vnode;
    vm._vnode = vnode;
    if (!prevVnode) { // 之前没有生成过vnode,代表初次渲染
      vm.$el = patch(el, vnode);
    } else {
      /* 这里时更新渲染,后续在讲 */
    }
  }

  Vue.prototype._c = function () { // 标签
    return createElement(this, ...arguments);
  }

  Vue.prototype._v = function () { // 文本节点
    return createTextVNode(this, ...arguments);
  }

  Vue.prototype._s = function (value) { // {{}} 文本
    if (typeof value !== 'object') {
      return value;
    }
    return JSON.stringify(value);
  }

  Vue.prototype._render = function () {
    const vm = this;
    return vm.$options.render.call(vm);
  }
}

因为render函数是通过一串code字符串结合Function和with来实现的,所以Vue2.0将函数都定义在了Vue的原型上。

生成虚拟Vnode

javascript 复制代码
// core/vdom/create-element.js

// vm  Vue当前实例
// tag 标签名
// data 属性
// children 子元素
export function createElement(vm, tag, data = {}, ...children) {
  if (!data) {
    data = {};
  }
  let key = data.key;
  if (key) {
    delete data.key;
  }
  return vnode(vm, tag, key, data, children);
}

// 创建文本虚拟dom
export function createTextVNode(vm, text) {
  return vnode(vm, undefined, undefined, undefined, undefined, text);
}
arduino 复制代码
// core/vdom/vnode.js

// 创建虚拟dom
export function vnode(vm, tag, key, data = {}, children, text, componentOptions) {
  return {
    vm,
    tag,
    key,
    data,
    children,
    text,
    componentOptions // 组件构造函数
  }
}

上文主要将通过createElement和createTextVNode创建虚拟Vnode,Vnode包含了tag标签名、key、属性data以及children子元素。

虚拟dom转为真实dom

ini 复制代码
// core/instance/lifecycle.js
Vue.prototype._update = function (vnode) {
    const vm = this;
    const el = vm.$el;
    const prevVnode = vm._vnode; // 获取老的vnode
    vm._vnode = vnode;
    if (!prevVnode) { // 如果老vnode不存在,那就初始化渲染
      vm.$el = patch(el, vnode);
    } else { // 更新渲染
      vm.$el = patch(prevVnode, vnode);
    }
  }
scss 复制代码
export function patch(oldVNode, vnode) {
  if(!oldVNode) {
    return createElm(vnode);
  }
  const isRealElement = oldVNode.nodeType; // 如果存在nodeType属性,那传入的就是真实dom
  if (isRealElement) { // 第一次渲染
    const elm = oldVNode;
    const parentElm = elm.parentNode; // 拿到父元素
    let newElm = createElm(vnode); // 将vnode 转为真实dom
    parentElm.insertBefore(newElm, elm.nextSibling); // 将真实dom插入到页面
    parentElm.removeChild(elm);
    return newElm;
  } else { // 更新渲染
    patchVnode(oldVNode, vnode);
    return vnode.el;
  }
}

update函数只要就是将vnode转为真实的dom,并渲染到页面上。在我们第一次生成vnode时,会将vnode赋值在实例的_vnode上,所以第一次渲染时拿不到_vnode的,这就是判断初始化渲染和更新渲染的依据。在初始化渲染时调用patch,第一个参数传入的时真实el,直接通过createElm将vnode转为真实dom插入到el之前,并删除el。

思维导图

总结

Vue初始化渲染原理已完结,主要使用过模板编译生成render函数,创建渲染Watcher来进行初次渲染。在渲染的过程中响应式数据会对当前渲染Watcher进行依赖收集。渲染主要通过update函数来判断是否时初次渲染,初次渲染直接将vnode转为真实dom插入到目标位置。

如果觉得本文有帮助 记得点赞三连哦 十分感谢!

vue2.0 和 vue3.0系列文章(后续更新)

相关推荐
卸任7 小时前
Vibe coding工具使用心得
前端·ai编程
brien98 小时前
为什么模型标称 1M,Google Antigravity 聊到 256K 却突然失忆?我们给它做了个原生行内监控圆环
前端
修炼的dance8 小时前
录屏关键帧间隔多少合适?六档实测,2秒到10秒小了75.6%
前端
Lank_M8 小时前
关键帧从1秒拉到10秒,录屏小87.3%,摄像头小10.8%
前端
补码缩写9 小时前
用 ffprobe 拆录屏,八成字节在 29 个关键帧上
前端
百思不得小李9 小时前
一个复杂的动态表单是如何实现的
前端·设计模式
陈随易9 小时前
Cmder 停更 17 个月,2.0 支持 Windows Terminal
前端·后端·程序员
清晨Coding9 小时前
删了 Cookie 还能自动登录?Google系网站登录态自动恢复的原理分析
javascript
风骏时光牛马9 小时前
前沿视界
前端
kyriewen9 小时前
我扫了 814 个依赖,97 个还敢要 Node 10
前端·javascript·程序员