当前位置: 首页 > news >正文

Vue2源码梳理:render函数的实现

render

  • 在 $mount 时,会调用 render 方法
  • 在写 template 时,最终也会转换成 render 方法
  • Vue 的 _render 方法是实例的一个私有方法,它用来把实例渲染成一个虚拟 Node
  • 它的定义在 src/core/instance/render.js 文件中,它返回的是一个vnode
  • 这里看下它的实现
    Vue.prototype._render = function (): VNode {const vm: Component = this// 从 $options 中拿到这个 render 函数// 这个 render 函数, 可以是用户自己写,也可以通过编译生成const { render, _parentVnode } = vm.$options// 跳过// reset _rendered flag on slots for duplicate slot checkif (process.env.NODE_ENV !== 'production') {for (const key in vm.$slots) {// $flow-disable-linevm.$slots[key]._rendered = false}}// 跳过if (_parentVnode) {vm.$scopedSlots = _parentVnode.data.scopedSlots || emptyObject}// 跳过// set parent vnode. this allows render functions to have access// to the data on the placeholder node.vm.$vnode = _parentVnode// render selflet vnodetry {// call的第一个参数是当前上下文, vm._renderProxy 在生产环境下,就是 VM,也就是this本身, 在开发环境它可能是一个proxy对象  // 这个 vm.$createElement 也是一个函数,会在下面来说// 总体来说就是生成 vnodevnode = render.call(vm._renderProxy, vm.$createElement)} catch (e) {// 捕获并处理错误,尝试再次生成 vnodehandleError(e, vm, `render`)// return error render result,// or previous vnode to prevent render error causing blank component/* istanbul ignore else */if (process.env.NODE_ENV !== 'production') {if (vm.$options.renderError) {try {vnode = vm.$options.renderError.call(vm._renderProxy, vm.$createElement, e)} catch (e) {handleError(e, vm, `renderError`)vnode = vm._vnode}} else {vnode = vm._vnode}} else {vnode = vm._vnode}}// return empty vnode in case the render function errored outif (!(vnode instanceof VNode)) {// 如果拿到的 vnode 是个数组,说明模板有多个根节点,这个在后续vue3中支持,在vue2中会报下面的警告// 在vue2 中 vnode 是一个 vdom, if (process.env.NODE_ENV !== 'production' && Array.isArray(vnode)) {warn('Multiple root nodes returned from render function. Render function ' +'should return a single root node.',vm)}vnode = createEmptyVNode()}// set parentvnode.parent = _parentVnodereturn vnode
    }
    
    • 注意,上面的 vm.$createElement 在 initRender 函数中被初始化定义
    • 而 initRender 是在一开始 new Vue 的时候,会调用 _init 函数中被执行
      // initRender 函数中
      vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false)
      vm.$createElement = (a, b, c, d) => createElement(vm, a, b, c, d, true)
      
      • 看到上面两个函数区别,就是最后一个参数不一样
      • _c 这个函数它实际上是被编译生成的render函数所使用的一个方法
      • $createElement 是给我们手写render函数提供了一个创建vnode的一个方法
        var app = new Vue({el: '#app',// 手写 render 函数render(createElement) {return createElement('div', {attrs: {id: 'app2' // 这些写后,这里挂载的 div 元素,会替换掉之前的 #app 容器}}, this.message)},data() {return {message: 'Hello'}}
        })
        
        • 这里,使用render和在html中写是不一样的,它没有一个把从差值变换的这个过程
        • 之前的写法是在HTML里面定义了这个差值
          // 之前的写法
          <div id="app">{{ message }}
          </div>
          
        • 它在不执行vue的时候,它会先把这个东西渲染出来
        • 然后在new vue之后, 执行 mount 的时候,再把它从差值的那个message给替换成真实的数据
        • 在这里是通过纯 render 函数,当它执行完毕以后,直接挂载到页面上,这样的话体验会更好一点
        • 因为这里手写了 render 函数,所以, 整个渲染流程中,就不会再执行把 template 转换 render 函数那一步了
        • 所以就相当于直接就达到了一样的效果
    • vm._renderProxy 的定义,也是发生在 src/core/instance/init.js 中
    • 在 _init 函数中,有一个判断
      /* istanbul ignore else */
      if (process.env.NODE_ENV !== 'production') {initProxy(vm)
      } else {vm._renderProxy = vm
      }
      
      • 当前如果说是生产环境,vm._renderProxy = vm
      • 开发阶段开发阶段,则执行 initProxy(vm) 定义在 src/core/instance/proxy.js 中
        // 报错提示函数
        const warnNonPresent = (target, key) => {warn(`Property or method "${key}" is not defined on the instance but ` +'referenced during render. Make sure that this property is reactive, ' +'either in the data option, or for class-based components, by ' +'initializing the property. ' +'See: https://vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.',target)
        }
        // 
        const hasHandler = {has (target, key) {const has = key in targetconst isAllowed = allowedGlobals(key) || (typeof key === 'string' && key.charAt(0) === '_' && !(key in target.$data)) // 全局方法或者私有方法,或不在$data中// 属性不在 target 下,并且不被允许,则报错提示,比如:使用了 一个没有在 data 中定义的变量,就会报错if (!has && !isAllowed) {if (key in target.$data) warnReservedPrefix(target, key)else warnNonPresent(target, key)}return has || !isAllowed}
        }// 判断当前浏览器是否支持 proxy,Proxy作用是对对象的访问做一个劫持
        const hasProxy = typeof Proxy !== 'undefined' && isNative(Proxy)initProxy = function initProxy (vm) {if (hasProxy) {// determine which proxy handler to useconst options = vm.$optionsconst handlers = options.render && options.render._withStripped? getHandler: hasHandlervm._renderProxy = new Proxy(vm, handlers) // 在这里 handlers 是 hasHandler} else {vm._renderProxy = vm}
        }
        
      • 这里可以看到,支持 Proxy 则 vm._renderProxy = new Proxy(vm, handlers)
  • 通过以上分析可知,vm._render 最终是通过执行 createElement 方法并返回的是 vnode
  • 而返回的vnode 它是一个虚拟 Node

相关文章:

Vue2源码梳理:render函数的实现

render 在 $mount 时&#xff0c;会调用 render 方法在写 template 时&#xff0c;最终也会转换成 render 方法Vue 的 _render 方法是实例的一个私有方法&#xff0c;它用来把实例渲染成一个虚拟 Node它的定义在 src/core/instance/render.js 文件中&#xff0c;它返回的是一个…...

flask+python企业产品订单管理系统938re

在设计中采用“自下而上”的思想&#xff0c;在创新型产品提前购模块实现了个人中心、个体管理、发布企业管理、投资企业管理、项目分类管理、产品项目管理、个体投资管理、企业投资管理、个体订单管理、企业订单管理、系统管理等的功能性进行操作。最终&#xff0c;对基本系统…...

Vue2源码梳理:关于数据驱动,与new Vue时的初始化操作

数据驱动 1 &#xff09;概述 vue的一个核心思想&#xff0c;就是数据驱动 所谓数据驱动&#xff0c;就是指视图是由数据驱动生成的 对视图的修改并不会直接操作dom&#xff0c;而是通过修改数据 它相比我们传统的前端开发&#xff0c;如使用 jQuery 的前端库直接去修改 dom…...

【C++航海王:追寻罗杰的编程之路】关于模板,你知道哪些?

目录 1 -> 泛型编程 2 -> 函数模板 2.1 -> 函数模板概念 2.2 -> 函数模板格式 2.3 -> 函数模板的原理 2.4 -> 函数模板的实例化 2.5 -> 函数参数的匹配原则 3 -> 类模板 3.1 -> 类模板的定义格式 3.2 -> 类模板的实例化 1 -> 泛型编…...

分布式springboot 3项目集成mybatis官方生成器开发记录

文章目录 说明实现思路实现步骤第一步&#xff1a;创建generator子模块第二步&#xff1a;引入相关maven插件和依赖第三步&#xff1a;编写生成器配置文件第四步&#xff1a;运行查看结果 说明 该文章为作者开发学习记录&#xff0c;方便以后复习和交流主要内容为&#xff1a;…...

算法学习——LeetCode力扣回溯篇4

算法学习——LeetCode力扣回溯篇4 332. 重新安排行程 332. 重新安排行程 - 力扣&#xff08;LeetCode&#xff09; 描述 给你一份航线列表 tickets &#xff0c;其中 tickets[i] [fromi, toi] 表示飞机出发和降落的机场地点。请你对该行程进行重新规划排序。 所有这些机票…...

c++ STL系列——(六)multimap

C标准模板库&#xff08;STL&#xff09;是C编程中不可或缺的一部分&#xff0c;它提供了一系列的容器、算法和函数模板&#xff0c;以简化常见的数据结构和算法的实现。在STL中&#xff0c;multimap是一个非常有用的容器&#xff0c;它提供了一种键值对的存储方式&#xff0c;…...

Json-序列化字符串时间格式问题

序列化字符串时间格式问题 一、项目场景二、问题描述三、解决方案 一、项目场景 最近C#中需要将实体进行json序列化&#xff0c;使用了Newtonsoft.Json public static void TestJson(){DataTable dt new DataTable();dt.Columns.Add("Age", Type.GetType("Sys…...

HarmonyOS鸿蒙学习基础篇 - 自定义组件(一)

前言 在ArkUI中&#xff0c;UI显示的内容均为组件&#xff0c;由框架直接提供的称为系统组件&#xff0c;由开发者定义的称为自定义组件。在进行 UI 界面开发时&#xff0c;通常不是简单的将系统组件进行组合使用&#xff0c;而是需要考虑代码可复用性、业务逻辑与UI分离&#…...

开窗,挖槽,放电齿,拼版

我们在阻焊层画线&#xff0c;就相当于去掉绿油阻焊&#xff0c;开窗一般是用在大电流走线的时候。先画要走的导线&#xff0c;之后切换到TopSolder或者Bottom Solder层&#xff0c;然后Place->line 画一条和原来先粗细一样的线即可&#xff01;但走电流的仍然是导线&#x…...

[Vue的组件通讯.sync修饰]Vue中.sync的使用方法和实现的方式 代码注释

目录 .sync的使用方法1. 在父组件中&#xff0c;将需要传递给子组件的数据使用v-bind绑定到子组件的props中&#xff0c;并在属性名后加上.sync修饰符&#xff0c;如下所示&#xff1a;2. 在子组件中&#xff0c;将需要传递给父组件的数据使用$emit方法触发一个名为update:valu…...

Java 基于springboot+vue在线外卖点餐系统,附源码

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…...

Decian 12.x基于LNMP安装phpIPAM(IP管理系统)

phpipam是一个开源Web IP地址管理应用程序&#xff08;IPAM&#xff09;。其目标是提供轻便&#xff0c;且有用的IP地址管理系统。它是基于PHP的应用程序&#xff0c;具有MySQL数据库后端&#xff0c;使用jQuery库&#xff0c;ajax和HTML5 / CSS3功能。 在Debian 12中&…...

【多模态MLLMs+图像编辑】MGIE:苹果开源基于指令和大语言模型的图片编辑神器(24.02.03开源)

项目主页&#xff1a;https://mllm-ie.github.io/ 论文 :基于指令和多模态大语言模型图片编辑 2309.Guiding Instruction-based Image Editing via Multimodal Large Language Models &#xff08;加州大学圣巴拉分校苹果&#xff09; 代码&#xff1a;https://github.com/appl…...

hpp文件:C++开发中的利器

1 什么是hpp文件&#xff1f; hpp文件是C程序中一种特殊头文件&#xff0c;它可以包含类的声明和实现。与传统的h文件相比&#xff0c;hpp文件具有以下特点&#xff1a; 将类的声明和实现放在同一个文件里&#xff0c;减少了代码量&#xff0c;提高了代码的可读性。无需再将c…...

如何查看电脑连接的wifi的密码

问题 很多时候我们电脑连上wifi之后就把密码忘记了&#xff0c;这个时候如果同事问自己密码是多少&#xff0c;如果作为程序员说不知道是不是感觉有点不好意思&#xff0c;哈哈…… 解决 我使用的是windows电脑&#xff0c;就以windows为例说明下自己是如何查看的。 打开wi…...

QTabWidget和QTabBar控件样式设置(qss)

QTabWidget和QTabBar控件样式设置 1、QTabWidget样式可自定义的有哪些示例&#xff1a;效果图 2、QTabBar样式可自定义的有哪些示例效果图 1、QTabWidget样式可自定义的有哪些 QTabWidget::pane{} 定义tabWidgetFrameQTabWidget::tab-bar{} 定义TabBar的位置QTabWidget::tab{}定…...

【智能家居入门3】(MQTT服务器、MQTT协议、微信小程序、STM32)

前面已经写了三篇博客关于智能家居的&#xff0c;服务器全都是使用ONENET中国移动&#xff0c;他最大的优点就是作为数据收发的中转站是免费的。本篇使用专门适配MQTT协议的MQTT服务器&#xff0c;有公用的&#xff0c;也可以自己搭建&#xff08;应该要钱&#xff09;&#xf…...

C语言第二十四弹---指针(八)

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】 指针 1、数组和指针笔试题解析 1.1、字符数组 1.1.1、代码1&#xff1a; 1.1.2、代码2&#xff1a; 1.1.3、代码3&#xff1a; 1.1.4、代码4&#xff1a; 1…...

m1芯片xcode15编译cocos2dx一些报错处理

报错1: No matching function for call to ‘iconv’ No matching function for call to ‘iconv_close’ 解决&#xff1a; 强转&#xff1a; iconv_close((iconv_t)_iconv); iconv((iconv_t)_iconv, (char**)&pin, &inLen, &pout, &outLen); 报错2: Proper…...

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…...

Redis的发布订阅模式与专业的 MQ(如 Kafka, RabbitMQ)相比,优缺点是什么?适用于哪些场景?

Redis 的发布订阅&#xff08;Pub/Sub&#xff09;模式与专业的 MQ&#xff08;Message Queue&#xff09;如 Kafka、RabbitMQ 进行比较&#xff0c;核心的权衡点在于&#xff1a;简单与速度 vs. 可靠与功能。 下面我们详细展开对比。 Redis Pub/Sub 的核心特点 它是一个发后…...

JVM虚拟机:内存结构、垃圾回收、性能优化

1、JVM虚拟机的简介 Java 虚拟机(Java Virtual Machine 简称:JVM)是运行所有 Java 程序的抽象计算机,是 Java 语言的运行环境,实现了 Java 程序的跨平台特性。JVM 屏蔽了与具体操作系统平台相关的信息,使得 Java 程序只需生成在 JVM 上运行的目标代码(字节码),就可以…...

LRU 缓存机制详解与实现(Java版) + 力扣解决

&#x1f4cc; LRU 缓存机制详解与实现&#xff08;Java版&#xff09; 一、&#x1f4d6; 问题背景 在日常开发中&#xff0c;我们经常会使用 缓存&#xff08;Cache&#xff09; 来提升性能。但由于内存有限&#xff0c;缓存不可能无限增长&#xff0c;于是需要策略决定&am…...

使用SSE解决获取状态不一致问题

使用SSE解决获取状态不一致问题 1. 问题描述2. SSE介绍2.1 SSE 的工作原理2.2 SSE 的事件格式规范2.3 SSE与其他技术对比2.4 SSE 的优缺点 3. 实战代码 1. 问题描述 目前做的一个功能是上传多个文件&#xff0c;这个上传文件是整体功能的一部分&#xff0c;文件在上传的过程中…...

算术操作符与类型转换:从基础到精通

目录 前言&#xff1a;从基础到实践——探索运算符与类型转换的奥秘 算术操作符超级详解 算术操作符&#xff1a;、-、*、/、% 赋值操作符&#xff1a;和复合赋值 单⽬操作符&#xff1a;、--、、- 前言&#xff1a;从基础到实践——探索运算符与类型转换的奥秘 在先前的文…...

Linux-进程间的通信

1、IPC&#xff1a; Inter Process Communication&#xff08;进程间通信&#xff09;&#xff1a; 由于每个进程在操作系统中有独立的地址空间&#xff0c;它们不能像线程那样直接访问彼此的内存&#xff0c;所以必须通过某种方式进行通信。 常见的 IPC 方式包括&#…...

路由基础-路由表

本篇将会向读者介绍路由的基本概念。 前言 在一个典型的数据通信网络中&#xff0c;往往存在多个不同的IP网段&#xff0c;数据在不同的IP网段之间交互是需要借助三层设备的&#xff0c;这些设备具备路由能力&#xff0c;能够实现数据的跨网段转发。 路由是数据通信网络中最基…...

用js实现常见排序算法

以下是几种常见排序算法的 JS实现&#xff0c;包括选择排序、冒泡排序、插入排序、快速排序和归并排序&#xff0c;以及每种算法的特点和复杂度分析 1. 选择排序&#xff08;Selection Sort&#xff09; 核心思想&#xff1a;每次从未排序部分选择最小元素&#xff0c;与未排…...

【Linux】使用1Panel 面板让服务器定时自动执行任务

服务器就是一台24小时开机的主机&#xff0c;相比自己家中不定时开关机的主机更适合完成定时任务&#xff0c;例如下载资源、备份上传&#xff0c;或者登录某个网站执行一些操作&#xff0c;只需要编写 脚本&#xff0c;然后让服务器定时来执行这个脚本就可以。 有很多方法实现…...