Vue深入了解

Vue深入了解

  • MVVM
  • v-model (双向数据绑定原理)
  • 异步更新
  • keep-alive原理
  • $nextTick原理
  • computed 和 watch 的区别
  • css-scoped
  • 虚拟DOM
  • Vuex && Pinia
  • Vue-router原理
  • proxy 与 Object.defineProperty
  • 组件通信方式

MVVM

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>mini MVVM</title></head><body><div id="app"><p>姓名: <span>{{ name }}</span></p><p>年龄: <span>{{ age }}</span></p></div><script>window.onload = function() {const vue = new Vue({el: '#app',data: {name: '加载中...',age: '加载中...'}})setTimeout(() => {vue.$data.name = '小明'vue.$data.age = 20}, 2000)}class Dep {constructor() {this.watchList = []}add(node) {this.watchList.push(node)}update(newValue) {this.watch.forEach((node) => {node.textContent = value})}}class Vue {constructor(options) {this.options = optionsthis.$data = options.datathis.$el = document.querySelector(options.el)this.obsever(this.$data)this.compile(this.$el) }/*[observe 函数]:利用Object.defineProperty把data中的属性变成响应式的,同时给每一个属性添加一个dep对象(用来存储对应的watcher观察者)首先我们会对需要响应式的 data 对象进行 for 循环遍历,为 data 的每一个 key 映射一个观察者对象在 ES6 中,for 循环每次执行,都可以形成闭包,因此这个观察者对象就存放在闭包中*/observer(data) {Object.keys(data).forEach((key) => {// 给data中的每一个属性添加一个dep对象(该对象用来存储对应的watcher观察者)const dep = new Dep()// 利用闭包 获取和设置属性的时候,操作的都是valuelet value = data[key]Object.defineProperty(data, key, {get() {// 观察者对象添加对应的dom节点Dep.target && dep.add(Dep.target)return value},set(newValue) {// 属性值变化时,更新观察者中所有节点value = newValuedep.update(value)}})})}/*[compile 函数]:我们从根节点向下遍历 DOM,遇到 mustache 形式的文本,则映射成 data.key 对应的值,同时记录到观察者中当遍历到 {{xxx}} 形式的文本,我们正则匹配出其中的变量,将它替换成 data 中的值当data的数据变化时,调用dep对象的update方法,更新所有观察者中的dom节点*/compile(dom) {const mustache = /\{\{(.*)\}\}/Array.from(dom.childNodes).forEach((child) => {// nodeType 为3时为文本节点,并且该节点的内容包含`mustache`(双大括号{{}})if(child.nodeType === 3 && mustache.test(child.textContent)) {const key = mustache.exec(child.textContent)[1].trim()const keyNoTrim = mustache.exec(child.textContent)[1]// 将该节点添加到对应的观察者对象中,在下面的的this.$data[key]中触发对应的get方法Dep.target = childlet value = this.$data[key]child.textContent = child.textContent.replace(`{{${keyNoTrim}}}`, value)Dep.target = null}// 递归遍历子节点if(child.childNodes.length) {this.compile(child)}})}}</script></body>
</html>

v-model (双向数据绑定原理)

采取数据劫持,通过Object.defineProperty()劫持各个属性,给各个属性添加getter和setter,数据变动时触发相应的回调
Observer:给数据加上getter和setter,改变数据时触发setter
Complie:模板解析,将模板中的变量替换成数据,绑定更新函数
Watcher:订阅者,是Observer和Complie之间通信的桥梁,往订阅器中添加自己,有一个update方法,当属性变动通知时,调用update方法,触发complie中绑定的更新函数

在这里插入图片描述

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>v-model</title></head><body><div id="app"><div>年龄: <span>{{ info.person.name }}</span></div><p>{{ job }}</p><input v-model="job" placeholder="请输入工作" type="text" /></div></body><script>window.onload = function () {const vue = new Vue({el: '#app',data: {info: {person: {name: '加载中',},},job: '程序猿',},})setTimeout(() => {vue.info.person.name = '小明'}, 2000)}class Dep {constructor() {this.watchList = []}add(node){this.watchList.push(node)}update(value) {this.watchList.forEach((node) => {if(node.tagName === 'INPUT' || node.tagName === 'TEXTAREA') {node.value = value } else {node.textContent = value}})}}class Vue {constructor(options){this.options = optionsthis.$data = options.datathis.$el = document.querySelector(options.el)this.observer(this.$data)this.compile(this.$el, this)this.proxy(this.$data, this)}observer(data) {if(data && typeof data === 'object') {const _this = thisObject.keys(data).forEach((key) => {const dep = new Dep()let value = data[key]// 数据劫持,对data增加了递归和设置新值的劫持,让data中每一层数据都是响应式的_this.observer(data[key])Object.defineProperty(data, key, {get(){ Dep.target && dep.add(Dep.target)return value}set(newValue) {value = newValue// 数据劫持,对data增加了递归和设置新值的劫持,让data中每一层数据都是响应式的_this.observer(newValue)dep.upadte(value)}})})}}compile(dom, vm) {const mustache = /\{\{(.*)\}\}/Array.from(dom.childNodes).forEach((child) => {if(child.nodeType === 1) {Array.from(child.attributes).forEach((attr) => {if(attr.name.includes('v-model')) {Dep.target = childchild.value = vm.$data[attr.value]Dep.target = null//给input元素绑定input事件,当输入值变化会触发对应属性的dep.update方法,通知对应的观察者发生变化child.addEventLister('input', (e) => {vm.$data[attr.value] = e.target.value})}})}if(child.nodeType === 3 && mustache.test(child.textContent)) {const key = mustache.exec(child.textContent)[1].trim()const keyNoTrim = mustache.exec(child.textContent)[1]const keyList = key.split('.')Dep.target = childlet value = vm.$dataketList.forEach((item) => value = value[item])child.textContent = child.textContent.replace(`{{${keyNoTrim}}}`, value)Dep.target = null}if(child.childNodes.length) {this.compile(child, vm)}})}// 增加了数据代理,通过this.info.person.name就可以直接修 $data对应的值,实现了this对this.$data的代理proxy(data, vm) {Object.keys(data).forEach((key) => {Object.defineProperty(vm, key, {get() {return data[key]},set(newValue) {data[key] = newValue}})})}}</script>
</html>

异步更新

 Vue数据更新频繁,但dom只会更新一次,为什么?1、Vue更新dom是异步更新,当Vue的数据更新后,不会立即更新dom2、侦听到数据变化,Vue会开启一个队列, 并缓存在同一事件循环中发生的所有数据变更3、同一个watcher被多次出发,只会被推入队列中一次,避免重复修改相同的dom4、同步任务执行完,执行异步watcher队列任务,一次性更新dom

keep-alive原理

缓存策略时LRU,组件切换时,保存一些组件的状态,防止多次渲染三大属性:include、exclude、max- 根据include/exclude配置的组件名,与对应组件的name进行条件匹配
- 根据组件ID和tag生成缓存的key,在缓存对象中查找是否已经缓存,存在取出并更新
- 检查是都超过了max设置的值,超过的话,根据LRU缓存策略,删除最近最久没有使用的组件
- 将KeepAlive属性更改为true,actived和deactivated两个钩子函数会用到

$nextTick原理

本质是对JavaScript执行原理EventLoop的一种应用
核心是模拟对应的微/宏任务的实现,利用JavaScript的异步回调任务队列来实现Vue框架自己的异步回调队列Vue.$nextTick 为什么优先使用微任务实现:根据 event loop 与浏览器更新渲染时机,宏任务 → 微任务 → 渲染更新,使用微任务,本次event loop轮询就可以获取到更新的dom如果使用宏任务,要到下一次event loop中,才能获取到更新的dom

computed 和 watch 的区别

  computed关键点:computed属性用于创建派生数据,这些数据是基于响应式依赖自动计算的。它们提供了缓存机制,只有当依赖项变化时,计算属性才会重新计算。computed适合于声明性地描述数据如何从其他数据派生,常用于视图渲染优化watch关键点:watch用于侦听响应式数据的变化,并在变化发生时执行定义的逻辑。它不具备缓存机制,每次数据变化都会触发回调函数。watch适合于执行复杂的业务逻辑,如异步请求、DOM操作,或者在数据变化时执行条件性响应。computed是声明式的,用于计算并缓存视图所需的数据,它根据响应式数据的变化自动重新计算并提供缓存。只有当其依赖的响应式数据变化时,才会重新执行计算。computed在开始时自动建立依赖关系,默认第一次加载的时候就开始监听watch是命令式的,用于监听响应式数据的变化,每次变化都会触发执行预定义的回调函数。watch默认在开始时不执行监听,除非设置immediate: true,这允许在数据变化时立即执行回调computed原理:1、初始化计算属性时,遍历计算computed对象,给每一个计算属性分别生成一个computed watcher, 并将watcher的dirty设置为true,初始化时不会立即计算,只有在获取计算的值时才会进行计算2、初始化时将Dep.target设置成当前的computer watcher,将computed watcher 添加到所依赖的data值对应的dep中,然后计算computed对应的值,然后将dirty改为false3、当所依赖的data中的值发生变化时,调用set方法触发dep 的notify方法,将watcher中dirty设置为true4、下次获取计算属性的值时,如果dirty为true,重新计算值5、dirty是控制缓存的关键,当依赖的data发生变化时,dirty设置为true,再次获取值时,就会重新计算值watch原理:1、遍历watch对象, 给其中每一个watch属性,生成对应的user watcher2、调用watcher中的get方法,将Dep.target设置成当前的user watcher,并将user watcher添加到监听data值对应的dep中(依赖收集的过程)3、当所监听data中的值发生变化时,会调用set方法触发dep的notify方法,执行watcher中定义的方法4、设置成deep:true的情况,递归遍历所监听的对象,将user watcher添加到对象中每一层key值的dep对象中,这样无论当对象的中哪一层发生变化,wacher都能监听到。通过对象的递归遍历,实现了深度监听功能

css-scoped

  原理:编译时,给每一个Vue文件生成一个唯一的id,将此id添加到当前文件的所有html标签上如:<div class="demo"></div>会被编译成<div class="demo" data-v-27e4e96e></div>编译style标签时,将css选择器改造成为属性选择器如:.demo{color: red;}会被编译成.demo[data-v-27e4e96e]{color: red;}

虚拟DOM

 什么是虚拟Dom使用JS对象模拟真实DOM节点,但是对比真实DOM更加轻量级1、前端性能的优化,尽量减少真实DOM的操作,频繁的操作DOM会导致浏览器的回流会重绘2、使用虚拟DOM,当数据变化,页面需要更新的时候,通过diff算法,对新旧的虚拟dom节点进行对比,比较两棵树的差异生成差异对象,一次性对DOM进行批量操作3、虚拟DOM本质上是JS对象,使用虚拟DOM可以进行更方便的跨平台操作
	// 真实 转 虚拟function dom2Json(dom) {if (!dom.tagName) returnlet obj = {}obj.tag = dom.tagNameobj.props = {}Array.from(dom.attributes).forEach((attr) => {obj.props[attr.name] = attr.value})obj.children = []dom.childNodes.forEach((item) => {// 去除空的节点dom2Json(item) && obj.children.push(dom2Json(item))})return obj}class Element {constructor(type, props, children) {this.type = typethis.props = propsthis.children = children}}// 虚拟 转 真实function render(domObj) {let el = document.querySelector(domObj.type)Object.keys(domObj.props).forEach((key) => {let value = domObj.props[key]switch (key) {case 'value':if (el.tagName === 'INPUT' || el.tagName === 'TEXTAREA') {el.value = value} else {el.setAttribute(key, value)}breakcase 'style':el.style.cssText = valuebreakdefault:el.setAttribute(key, value)}})domObj.childeren.forEach((child) => {child =child instanceof Element? render(child): document.createTextNode(child)})return el}

Vuex && Pinia

Vuex 与 Pinia 的区别语法和结构:Vuex 的语法相对较为复杂,而 Pinia 的语法更加简洁和直观。模块系统:Vuex 支持模块系统,可以将状态拆分成多个模块进行管理,而 Pinia 也提供了类似的功能,但更加灵活和易于使用。类型支持:Pinia 提供了更好的类型支持,可以在代码中获得更好的类型推断和提示。开发体验:Pinia 在开发体验上更加友好,提供了更多的辅助函数和工具,使开发更加高效。
VueX的原理1、store本质就是一个没有template的组件2、利用mixin机制在beforeCreate钩子前混入VuexInit方法3、VuexInit方法实现将store 注册到当前组件的$store中4、state 相当于组件内的data,定义在state上的变量相当于定义在组件的data中的变量,都是响应式的5、当页面中使用了state中的数据,就是依赖收集的过程,6、当state中的数据发生变化,就通过调用对应属性的dep对象的notify方法,去修改视图变化

在这里插入图片描述

Vue-router原理

   1、创建的页面路由会与该页面形成一个路由表(key-value模式,key为路由,value为页面)2、通过监听浏览器地址栏URL的变化,匹配路由表,将对应路由的页面替换旧页面,达到无需刷新的效果3、目前单页面使用的路由有两种实现方式: hash 模式、history 模式4、hash模式(路由中带#号),通过hashchange事件来监听路由的变化window.addEventListener('hashchange', ())=>{})5、history 模式,利用了pushState() 和replaceState() 方法,实现往history中添加新的浏览记录、或替换对应的浏览记录通过popstate事件来监听路由的变化,window.addEventListener('popstate', ())=>{})

proxy 与 Object.defineProperty

1)初始化性能优化:Vue 2 在初始化响应式数据时,会递归遍历对象的所有属性并使用 Object.defineProperty为每个属性添加 getter 和 setter。这样的初始化过程会产生大量的 getter 和 setter,对于大规模的对象或数据,初始化时间会较长。Vue 3 中,使用 Proxy 对象进行拦截,初始化性能得到了显著提升,因为 Proxy 是在整个对象级别上进行拦截,无需遍历每个属性。
2)深层属性监听优化:Vue 2 中,对于深层嵌套的属性,需要通过递归方式为每个属性添加响应式处理,这在大型对象上可能会导致性能下降。Vue 3 中,Proxy 可以递归地拦截整个对象的操作,无需为每个属性单独处理,从而提高了深层属性监听的性能。
3)删除属性性能优化:Vue 2 中,当删除一个属性时,需要通过 Vue.$delete 或者 Vue.delete 方法来触发更新。这是因为 Vue 2 使用的 Object.defineProperty 无法拦截属性的删除操作。Vue 3 中,使用 Proxy 可以直接拦截属性的删除操作,从而简化了删除属性的处理逻辑,并提高了性能。
4)动态添加属性性能优化:Vue 2 中,动态添加新属性需要通过 Vue.set 方法来触发更新,否则新添加的属性将不会是响应式的。Vue 3 中,Proxy 可以直接拦截动态添加属性的操作,并将其设置为响应式属性,无需额外的处理方法,提高了性能和代码的简洁性。

组件通信方式

  • 通信的种类

    • 父组件向子组件通信
    • 子组件向父组件通信
    • 隔代组件间通信
    • 兄弟组件间通信
  • 实现通信的方式

    • props
    • vue自定义事件
    • 消息订阅与发布
    • vuex
    • slot
    • 依赖注入
  • 方式一:props

    • 通过一般属性实现父向子通信
    • 通过函数属性实现子向父通信
    • 缺点:隔代组件和兄弟组件间通信比较麻烦
  • 方式二:vue自定义组件

    • vue内置实现,可以代替函数类型的props
      a.绑定监听:<MyComp @eventName=“callback”>

      b.触发(分发)事件: this.$emit(“eventName” , data)

    • 适用于子组件与父组件通信居多,可以利用事件总线,进行兄弟组件间的通信,类似于vuex

  • 方式三:消息订阅发布

    • 需要引入消息订阅与发布的实现库,如: pubsub-js
      a.订阅消息:PubSub.subscribe(‘msg’, (msg,data)=>{})

      b.发布消息: PubSub.publish(‘msg’, data)

    • 优点:此方式可实现任意关系组件间通信

  • 方式四:vuex

    • 是什么:vuex是vue官方提供的集中式管理vue多组件共享状态数据的vue插件
    • 优点:对组件间关系没有限制,且相比于pubsub库管理更集中,更方便
  • 方式五:slot

    • 是什么:专门用来实现父向子传递带数据的标签

      a.子组件

      b.父组件

    • 注意:通信的标签模板是在父组件中解析好后再传递给子组件的

  • 方式六:依赖注入

    • provide、inject
    provide() { return {     num: this.num  };
    }
    inject: ['num']
    

    注意: 依赖注入所提供的属性是非响应式的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/881582.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

怎么把一段音频的人声和背景音乐分开?

在数字音频处理中&#xff0c;将一段音频中的人声和背景音乐分开是一个复杂但又常见的需求。这种技术广泛应用于音乐制作、影视后期、广告制作等多个领域。本文将为你详细解析如何通过不同的方法实现这一目标&#xff0c;帮助你更好地掌握音频分离技术。 一、音频分离的基本概念…

关于C语⾔内存函数 memcpy memmove memset memcmp

memcpy使⽤和模拟实现 void * memcpy ( void * destination, const void * source, size_t num ); 函数memcpy从source的位置开始向后复制num个字节的数据到destination指向的内存位置。 这个函数在遇到 \0 的时候并不会停下来。 如果source和destination有任何的重叠&am…

基于方块编码的图像压缩matlab仿真,带GUI界面

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1 编码单元的表示 4.2编码单元的编码 5.算法完整程序工程 1.算法运行效果图预览 (完整程序运行后无水印) 下图是随着方块大小的变化&#xff0c;图像的压缩率以及对应的图像质量指标PSN…

本地生活服务项目入局方案解析!本地生活服务商系统能实现怎样的作业效果?

当前&#xff0c;各大平台的本地生活服务业务日渐兴盛&#xff0c;提高创业者入局意向的同时&#xff0c;也让本地生活服务项目有哪些等问题也成为了多个创业者社群中的热议对象。而从目前的讨论情况来看&#xff0c;在创业者们所询问的众多本地生活服务项目中&#xff0c;通过…

uniapp 游戏 - 使用 uniapp 实现的扫雷游戏

0. 思路 1. 效果图 2. 游戏规则 扫雷的规则很简单。盘面上有许多方格,方格中随机分布着一些雷。你的目标是避开雷,打开其他所有格子。一个非雷格中的数字表示其相邻 8 格子中的雷数,你可以利用这个信息推导出安全格和雷的位置。你可以用右键在你认为是雷的地方插旗(称为标…

安卓/iOS H5传递动态参数方法,App渠道归因方案

需求和痛点 负责渠道分发的部门都需要收集区分不同安装渠道的转化量&#xff08;注册、付费等数据&#xff09;做数据分析&#xff0c;通常包括官网跳转、KOL营销、用户分享、广告投放、活动拉新等多个渠道。 场景 用户在网页上进行用户行为&#xff0c;产生了数据&#xff0c…

Vue 项目文件大小优化

优化逻辑 任何优化需求&#xff0c;都有一个前提&#xff0c;即可衡量。 那 Vue 加载速度的优化需求&#xff0c;本质上是要降低加载静态资源的大小。 所以&#xff0c;优化前&#xff0c;需要有一个了解项目现状的资源加载大小情况。 主要分 3 步走&#xff1a; 找到方法测…

ZYNQ使用XGPIO驱动外设模块(前半部分)

目录 目录 一、新建BD文档&#xff0c;添加ZYNQ处理器 1.BD文档: 2.在Vivado中&#xff0c;BD文件的生成过程通常包括以下步骤&#xff1a; 1)什么是Tcl Console: 3.PL部分是FPGA可编程逻辑部分&#xff0c;它提供了丰富的IO资源&#xff0c;可以用于实现各种硬件接口和功…

3dsMax添加天空盒

点击渲染&#xff0c;环境 &#xff0c; 点击位图 找到要设置的天空HDR&#xff0c;可以使用HDR(EXR)贴图 一个可以下载HDR贴图的网站 https://polyhaven.com/hdris在渲染的时候不要使用使用微软输入法&#xff0c;3dsmax会卡死&#xff0c; 在渲染的时候不要使用使用微软…

【Kubernetes】常见面试题汇总(五十九)

目录 129.问题&#xff1a;pod 使用 PV 后&#xff0c;无法访问其内容&#xff1f; 130.查看节点状态失败&#xff1f; 特别说明&#xff1a; 题目 1-68 属于【Kubernetes】的常规概念题&#xff0c;即 “ 汇总&#xff08;一&#xff09;~&#xff08;二十二&#xf…

《向量数据库指南》 ——KG-RAG 新突破:有限跳数假设下的高效解法

我们观察到在实际的 KG-RAG 场景中,存在跳数有限性假设:在 KG-based RAG 中,实际问的 query 问题的查询路由只需要在知识图谱中进行有限的,且很少的跳数(如少于4跳)的查询,而并不需要在其中进行非常多次跳数。 我们的跳数有限性假设基于两点很重要的观察:1. query 复杂…

自然语言处理(NLP)论文数量的十年趋势:2014-2024

引言 近年来&#xff0c;自然语言处理&#xff08;NLP&#xff09;已成为人工智能&#xff08;AI&#xff09;和数据科学领域中的关键技术之一。随着数据规模的不断扩大和计算能力的提升&#xff0c;NLP技术从学术研究走向了广泛的实际应用。通过观察过去十年&#xff08;2014…

基于Zabbix进行服务器运行情况监测

文章目录 引言I Zabbix主要构成下载并安装Zabbix被监控主机安装zabbix agent创建被监控主机报警设置II 常见问题cannot use database "zabbix": its "users" table is empty (is this the Zabbix proxy database?)重置 Zabbix Web 界面密码Zabbix agent i…

还做单元测试吗?

软件单元测试分为狭义的单元测试和广义的单元测试。 前者是指对被测代码的各种函数、接口等进行测试&#xff0c;以验证它们的功能、性能和安全性。 后者是指对页面的每一个组件&#xff08;如文本框、按钮等&#xff09;进行测试&#xff0c;以验证它们的功能、性能和安全性…

MySQL学习笔记(持续更新,目前到十一章锁)

1、Mysql概述 1.1 数据库相关概念 三个概念&#xff1a;数据库、数据库管理系统、SQL 名称全称简称数据库存储数据的仓库&#xff0c;数据是有组织的进行存储DataBase&#xff08;DB&#xff09;数据库管理系统操纵和管理数据库的大型软件DataBase Mangement System&#xf…

【Qt】详细Qt基础 (包括自定义控件)

目录 QT 概述创建项目项目文件&#xff08;. pro&#xff09;main.cppmainwindow.uimainwindow.hmainwindow.cpp 窗口类QWidget 窗口显示QDialog 窗口 QPushButton创建显示 对象树基本概念功能 坐标体系控件Item WidgetsQListWidgetQTreeWidgetQTableWidget 自定义控件 QT 概述…

AI免费文档处理在线工具:ColPali文本检索文档

1、ColPali 原理还是对比学习&#xff0c;图像和文本&#xff0c;文档通过图像模型&#xff0c;文本通过大模型gemma https://huggingface.co/spaces/manu/ColPali-demo 检索pdf

k8s 中存储之 PV 持久卷 与 PVC 持久卷申请

目录 1 PV 与 PVC 介绍 1.1 PersistentVolume&#xff08;持久卷&#xff0c;简称PV&#xff09; 1.2 PersistentVolumeClaim&#xff08;持久卷声明&#xff0c;简称PVC&#xff09; 1.3 使用了PV和PVC之后&#xff0c;工作可以得到进一步的细分&#xff1a; 2 持久卷实验配置…

深度0.1%调光恒流芯片SL8701支持PWM调光 模拟调光 无频闪 多路共阳

一、芯片概述 SL8701是一款内置100V MOS的降压型高调光比LED恒流驱动芯片&#xff0c;专为智能调光调色照明研发设计。它支持多种调光方式&#xff0c;包括PWM调光和模拟调光&#xff0c;能够实现高调光比&#xff0c;满足不同场景的照明需求。 二、主要特性 PWM调光支持&am…

【Vue3】 h()函数的用法

目录 介绍 参数 使用案例 1.创建虚拟 DOM 元素 2. 组件的动态渲染 3. 创建功能组件 4.渲染动态属性 5. 使用插槽 6. 创建动态标签 介绍 h() 函数用于辅助创建虚拟 DOM 节点&#xff0c;它是 hypescript 的简称——能生成 HTML (超文本标记语言) 的 JavaScript&#x…