带你一行一行手写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系列文章(后续更新)

相关推荐
java1234_小锋1 小时前
【技术专题】HTML5 网页技术 - HTML5 Canvas 基础以及动画入门
前端·html·html5
知了学历孙老师4 小时前
小自考的校考和统考,出题方和难度差在哪
java·大数据·前端
ShineWinsu6 小时前
对于JavaScript:操作元素的解析
开发语言·前端·javascript·面试·web·dom·元素
福兮说7 小时前
JSON 格式正确,数据就可靠吗?我用一份自编配置检查类型与空值
java·前端·json
程序员老赵9 小时前
Docker 部署青龙面板:轻松搭建定时任务管理平台
前端·docker·开源
Swot9 小时前
Asciidoctor 公式数字下标错位问题:原因分析与解决方案
前端
Swot9 小时前
leo-editor 中将 Markdown 内容转成 asciidoc 格式
前端
狒狒AI9 小时前
Remotion / GSAP+Three / HyperFrames / Skia 四栈实测:代码渲染知识视频的选栈方法(附开源技能)
前端·人工智能·aigc
修炼的dance9 小时前
浏览器里导出H.265,8.5秒和55.5秒差在硬件
前端
补码缩写9 小时前
WebCodecs选编码器,探测之后还要验解码和播放
前端