带你一行一行手写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字符串结合Functionwith来实现的,所以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 // 组件构造函数
  }
}

上文主要将通过createElementcreateTextVNode创建虚拟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,直接通过createElmvnode转为真实dom插入到el之前,并删除el

思维导图

总结

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

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

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

相关推荐
王先生技术栈27 分钟前
思维导图,Android版本实现
java·前端
悠悠:)1 小时前
前端 动图方案
前端
anyup_前端梦工厂1 小时前
了解 ES6 的变量特性:Var、Let、Const
开发语言·javascript·ecmascript
星陈~1 小时前
检测electron打包文件 app.asar
前端·vue.js·electron
Aatroox1 小时前
基于 Nuxt3 + Obsidian 搭建个人博客
前端·node.js
每天都要进步哦1 小时前
Node.js中的fs模块:文件与目录操作(写入、读取、复制、移动、删除、重命名等)
前端·javascript·node.js
布兰妮甜2 小时前
Three.js 渲染技术:打造逼真3D体验的幕后功臣
javascript·3d·three.js·幕后
仿生狮子2 小时前
CSS Layer、Tailwind 和 sass 如何共存?
javascript·css·vue.js
在路上`2 小时前
vue3使用AntV X6 (图可视化引擎)历程[二]
javascript·vue.js
brzhang3 小时前
开源了一个 Super Copy Coder ,0 成本实现视觉搞转提示词,效率炸裂
前端·人工智能