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

相关推荐
陈天伟教授3 小时前
人工智能训练师认证教程(2)Python os入门教程
前端·数据库·python
信看4 小时前
NMEA-GNSS-RTK 定位html小工具
前端·javascript·html
Tony Bai4 小时前
【API 设计之道】04 字段掩码模式:让前端决定后端返回什么
前端
爱吃大芒果4 小时前
Flutter 主题与深色模式:全局样式统一与动态切换
开发语言·javascript·flutter·ecmascript·gitcode
苏打水com4 小时前
第十四篇:Day40-42 前端架构设计入门——从“功能实现”到“架构思维”(对标职场“大型项目架构”需求)
前端·架构
king王一帅4 小时前
流式渲染 Incremark、ant-design-x markdown、streammarkdown-vue 全流程方案对比
前端·javascript·人工智能
苏打水com5 小时前
第十八篇:Day52-54 前端跨端开发进阶——从“多端适配”到“跨端统一”(对标职场“全栈化”需求)
前端
Bigger5 小时前
后端拒写接口?前端硬核自救:纯前端实现静态资源下载全链路解析
前端·浏览器·vite
BD_Marathon5 小时前
【JavaWeb】路径问题_前端绝对路径问题
前端