Vue.js 3应用开发与核心源码解析
元数据
Vue.js 3应用开发与核心源码解析
- 书名: Vue.js 3应用开发与核心源码解析
- 作者: 吕鸣
- 简介: 本书以前端工程化和企业级应用开发为目标,围绕Vue 3及相关生态技术与核心源码进行详细剖析。内容包括:Vue.js核心基础;相关生态,包括状态管理框架Vuex、路由管理框架Vue Router、Vue动画技术、Vue网络与数据存储技术,前端构建工具Vite与Vue Cli;进阶的Vue服务端渲染,包括Node.js、Express和Nuxt.js;核心源码剖析,包括响应式原理、双向绑定实现、虚拟DOM、keep-alive原理和实现,旨在使读者掌握Vue的设计思想,提升开发项目和应对面试的能力;每章提供一个小项目,最后还提供了一个以工程化思想开发的实战项目,使读者能够真正掌握从0到1开发一个企业级应用的全过程。
- 出版时间 2022-08-01 00:00:00
- ISBN: 9787302612629
- 分类: 计算机-编程设计
- 出版社: 清华大学出版社
- PC地址:https://weread.qq.com/web/reader/24132000813ab742bg012b46
高亮划线
1.1 认识MVC和MVVM模式
📌 传统的MVC模式包括以下三部分:· 视图(View):用户界面。· 控制器(Controller):业务逻辑。· 模型(Model):数据存储。Model代表数据存储,主要用于实现数据的持久化;View代表用户界面(UI),主要用于实现页面的显示;Controller代表业务逻辑,串联起View和Model,主要用来实现业务的逻辑代码。 ^3300034992-7-810-1095
- 💭 在MVC模式中,用户的交互行为在View中触发,由View通知Controller去进行对应的逻辑处理,处理完成之后通知Model改变状态,Model完成状态改变后,找到对应的View去更新用户界面的显示内容,至此完成对用户交互行为的反馈 - ⏱ 2024-10-24 18:14:43
📌 由此可见,整个流程由View发起,最终在View中做出改变,这是一个单向的过程。当年流行的backbone.js就是MVC的典型代表。
⏱ 2024-10-24 18:13:37 ^3300034992-7-1215-1282
📌 MVVM是把MVC中的Controller去除了,相当于变薄了,取而代之的是ViewModel。所谓ViewModel,是一个同步的View和Model的对象,在前端MVVM中,ViewModel最典型的作用是操作DOM,特点是双向数据绑定(Data-Binding)。 ^3300034992-7-1391-1526
- 💭 在双向数据绑定中,开发者无须关注如何找到DOM节点和如何修改DOM节点,因为每一个在View中需要操作的DOM都会有一个在Model中对应的对象,通过改变这个对象,DOM就会自动改变;反之,当DOM改变时,对应的Model中的对象也会改变。ViewModel将View和Model关联起来,因此开发者只需关注业务逻辑,不需要手动操作DOM,这就是ViewModel带来的优势 - ⏱ 2024-10-24 18:16:37
1.4 Vue 3新特性概览
📌 ,相较于Vue 2.x版本,Vue 3主要有以下几个方面大的改动以及提升:· 更快。· 更小。· 更易于维护。
⏱ 2024-10-24 18:20:42 ^3300034992-10-521-663
📌 更快主要体现在Vue 3在性能方面的提升,以及在源码层面的改动,主要包括以下方面:· 重构虚拟DOM。· 事件缓存。· 基于Proxy的响应式对象。
⏱ 2024-10-24 18:20:52 ^3300034992-10-936-1097
📌 标记静态内容,并区分动态内容(静态提升)。· 更新时只diff动态的部分。
⏱ 2024-10-24 18:21:57 ^3300034992-10-1852-1918
📌 在Vue 3中,提供了事件缓存对象cacheHandlers,当cacheHandlers开启的时候,@click绑定的事件会被标记成静态节点,被放入cacheHandlers中,这样在视图更新时也不会追踪,当事件再次触发时,就无须重新生成函数,直接调用缓存的事件回调方法即可,在事件处理方面提升了Vue的性能。
⏱ 2024-10-24 18:23:33 ^3300034992-10-2167-2323
📌 在Vue 2.x中,使用Object.defineProperty()来实现响应式对象,对于一些复杂的对象,需要循环递归地给每个属性增加getter/setter监听器,这使得组件的初始化非常耗时,而Vue 3中,引入了一种新的创建响应式对象的方法reactive,其内部就是利用ES 6的Proxy API来实现的,这样就可以不用针对每个属性来一一进行添加,以减少开销,提升性能。我们会在后续章节具体讲解Vue 3的响应式和Proxy API。
⏱ 2024-10-24 18:23:49 ^3300034992-10-3148-3371
📌 Monorepo是一种管理代码的方式,它的核心观点是所有的项目在一个代码仓库中,但是代码分割到一个个小的模块中,而不是都放在src这个目录下
⏱ 2024-10-24 18:25:39 ^3300034992-10-5831-5901
📌 在服务端渲染方面,Vue 3优化了返回HTML字符串的逻辑。在Vue 2.x中,所有的节点(包括一些静态节点)在服务端返回时都会转换为虚拟DOM,再转换成HTML字符串返回给浏览器;Vue 3则将静态节点剥离成字符串,这部分内容不会转换成虚拟DOM,而是直接拼接返回,在效率上进行了提升。
⏱ 2024-10-24 18:27:39 ^3300034992-10-8283-8427
1.5 ES 6语言基础
📌 箭头函数会默认绑定外层的上下文对象this的值,因此在箭头函数中,this的值和外层的this是一样的,不需要使用bind或者call的方法来改变函数中的上下文对象
⏱ 2024-10-24 18:30:06 ^3300034992-11-2871-2953
2.1 Vue.js实例和组件
📌 Vue.js的组件化是指每个组件控制一块用户界面的显示和用户的交互操作,每个组件都有自己的职能,代码在自己的模块内互相不影响,这是使用Vue.js的一大优势。
⏱ 2024-10-24 18:37:24 ^3300034992-15-8161-8240
2.2 Vue.js模板语法
📌 关于虚拟DOM的概念,可以简单理解成Vue在每次把数据更新到用户界面时,都会在内部事先定义好前后两个虚拟的DOM,一般是对象的形式。通过对比前后两个虚拟DOM的异同来针对性地更新部分用户界面,而不是整体更新(没有改变的用户界面部分不去修改,这样可以减少DOM操作,提升性能)。
⏱ 2024-10-24 18:52:18 ^3300034992-16-11385-11523
📌 自定义事件一般用在组件通信中,我们会在后面的章节讲解,在使用v-on监听原生DOM事件时,可以添加一些修饰符并有选择性地执行一些方法或者程序逻辑:· .stop:阻止事件继续传播,相当于调用event.stopPropagation()。· .prevent:告诉浏览器不要执行与事件关联的默认行为,相当于调用event.preventDefault()。· .capture:使用事件捕获模式,即元素自身触发的事件先在这里处理,然后才交由内部元素进行处理。· .self:只有当event.target是当前元素自身才触发处理函数。· .once:事件只会触发一次。· .passive:告诉浏览器不阻止与事件关联的默认行为,相当于不调用event.preventDefault()。与prevent相反。· .left、.middle、.right:分别对应鼠标左键、中键、右键的单击触发。· .{keyAlias}:只有当事件是由特定按键触发时才触发回调函数。
⏱ 2024-10-24 18:54:16 ^3300034992-16-13931-14596
📌 · .lazy:在默认情况下,v-model会同步输入框中的值和数据。可以通过这个修饰符,转变为在输入框的change事件中再进行值和数据同步。· .number:自动将用户的输入值转化为number类型。· .trim:自动过滤用户输入的首尾空格。
⏱ 2024-10-24 18:59:10 ^3300034992-16-17602-17785
📌 在之前讲解v-for指令时,我们知道key属性给每个列表元素分配了唯一的键值,这样使得Vue在做前后的虚拟DOM改变对比时更加高效,但前提是需要创建新的虚拟DOM,当我们使用了v-memo时,如果当前的列表元素所对应的v-memo没有改变,那么这部分虚拟DOM也不会重新创建,减少了过多的虚拟DOM创建,也能在一定程度上提升性能。当然,这在列表条数很少时体现得并不明显,但是当列表很长时,也就能体现出性能差异了。
⏱ 2024-10-25 12:22:16 ^3300034992-16-18887-19093
📌 把用方括号括起来的JavaScript表达式作为一个v-bind或v-on指令的参数,这种参数被称为动态参数。
⏱ 2024-10-25 12:26:03 ^3300034992-16-19597-19652
📌 动态参数表达式有一些语法约束,因为某些字符(例如空格和引号)放在HTML属性名里是无效的,所以要尽量避免使用这些字符
⏱ 2024-10-25 12:26:19 ^3300034992-16-20203-20261
📌 变通的办法是使用没有空格或引号的表达式,或用计算属性替代这种复杂的表达式。另外,如果在DOM中使用模板(直接在一个HTML文件中编写模板需要回避大写键名),需要注意浏览器会把属性名全部强制转为小写
⏱ 2024-10-25 12:26:37 ^3300034992-16-20415-20513
2.3 Vue.js的data属性、方法、计算属性和监听器
📌 · 计算属性:只要依赖的数据没发生改变,就可以直接返回缓存中的数据,而不需要每次都重复执行数据操作。· 方法:只要页面更新用户界面,就会发生重新渲染,methods调用对应的方法,执行该函数,无论是不是它所依赖的。
⏱ 2024-10-30 07:53:47 ^3300034992-17-6942-7078
📌 如果需要监听一个属性的改变,并且在改变的回调方法中有一些异步的操作或者数据量比较大的操作,这时应当使用监听属性watch。而对于简单的同步操作,使用计算属性computed更加合适。
⏱ 2024-10-30 08:23:25 ^3300034992-17-14597-14688
3.1 组件生命周期
📌 1.beforeCreate方法这个阶段在实例初始化之后,数据观测(Data Observer)和event/watcher事件配置之前被调用。需要注意的是,这个阶段无法获取到Vue组件data中定义的数据,官方也不推荐在这里操作data,如果确实需要获取data,可以从this.$options.data()获取。
⏱ 2024-10-30 08:24:50 ^3300034992-21-2245-2434
📌 2.created方法在beforeCreate执行完成之后,Vue会执行一些数据观测和event/watcher事件的初始化工作,将数据和data属性进行绑定以及对props、methods、watch等进行初始化,另外还要初始化一些inject和provide。
⏱ 2024-10-30 08:24:57 ^3300034992-21-2501-2664
📌 1.beforeMount方法前文提到了el和template属性以及render函数,render函数用于给当前Vue实例挂载DOM(Vue组件渲染HTML内容),这里的beforeMount就是渲染前要执行的程序逻辑。
⏱ 2024-10-30 08:25:48 ^3300034992-21-3615-3756
📌 2.mounted方法这个阶段开始真正地执行render方法进行渲染,之前设置的el会被render函数执行的结果所替换,也就是说将结果真正渲染到当前Vue实例的el节点上,这时就会调用mounted方法。
⏱ 2024-10-30 08:25:55 ^3300034992-21-3823-3956
📌 但是需要注意的是,mounted不会保证所有的子组件也都一起被挂载。如果读者希望等到整个视图都渲染完毕,可以在mounted内部使用this.$nextTick ^3300034992-21-4480-4560
- 💭 nextTick 是等到视图中的所有组件都渲染完 - ⏱ 2024-10-30 08:26:25
📌 1.beforeUpdate方法当Vue实例data中的数据发生了改变,就会触发对应组件的重新渲染,这是双向绑定的特性之一,所以数据改变就会触发beforeUpdate方法。
⏱ 2024-10-30 08:27:23 ^3300034992-21-4955-5072
📌 2.updated方法当执行完beforeUpdate方法后,就会触发当前组件挂载DOM内容的修改,当前DOM修改完成后,便会触发updated方法,在updated方法中可以获取更新之后的DOM。
⏱ 2024-10-30 08:27:28 ^3300034992-21-5139-5268
📌 1.beforeUnmount方法beforeUnmount方法在组件卸载之前调用。在这一步,实例仍然完全可用。 ^3300034992-21-8410-8496
- 💭 vue2中的 beforeDestroy 和 destroyed 这两个生命周期钩子在 vue3 中分别被重命名为 beforeUnmount 和 unmounted - ⏱ 2024-10-30 08:28:19
📌 2.unmounted方法unmounted方法在Vue组件卸载后调用。调用后,Vue组件关联的所有事件监听器都会被移除,所有的当前组件的子组件也会被销毁。
⏱ 2024-10-30 08:28:22 ^3300034992-21-8563-8671
📌 errorCaptured该方法表示当捕获一个来自当前子孙组件的错误时被触发,注意当前组件报错不会触发。这里的报错一般只会限制在当前Vue根实例下代码所抛出的DOMException或者异常Error对象(new Error())等错误,如果是Vue之外的代码,是不会触发的。该方法会收到三个参数:错误对象、发生错误的组件实例以及一个包含错误来源信息的字符串。在某个子孙组件的errorCaptured返回false时,可以阻止该错误继续向上传播。另外,也可以在全局配置errorCaptured,这样就可以监听到所有属于该根实例的报错信息,配置如示例代码3-1-4所示。
⏱ 2024-10-30 08:32:30 ^3300034992-21-10390-10735
📌 activated表示当vue-router的页面被打开时,会触发这个钩子函数。deactivated表示当vue-router的页面被关闭时,会触发这个钩子函数。
⏱ 2024-10-30 08:33:03 ^3300034992-21-11739-11821
📌 renderTracked和renderTriggered ^3300034992-21-11891-11920
💭 renderTracked 和 renderTriggered 是vue3中两个新增的生命周期钩子,主要用于调试和性能优化。
onRenderTracked:这个钩子函数会在组件的渲染跟踪列表中触发。它用于跟踪页面上所有的方法和变量,也就是我们 return 返回的属性和方法,它都会进行跟踪。当页面有 update 时,会生成一个 event 对象,通过这个 event 对象可以查看程序的问题所在。这个钩子可以帮助开发者了解哪些响应式状态被追踪了,从而优化渲染性能。
onRenderTriggered:这个钩子函数会在组件的渲染触发器列表中触发。与 onRenderTracked 不同,它不会跟踪每一个值,而是跟踪发生变化值得信息,并且新值和旧值都会展示出来。这个钩子函数可以用于执行副作用操作,比如在状态变化时执行特定的逻辑 - ⏱ 2024-10-30 08:34:34
3.2 组件通信
📌 如果props传递的是一个动态值,每次父组件的info发生更新时,子组件中接收的props都将会刷新为最新的值。这意味着,我们不应该在一个子组件内部改变props
⏱ 2024-10-30 08:37:42 ^3300034992-22-4668-4749
📌 Vue中父传子的方式形成了一个单向下行绑定,叫作单向数据流。父级props的更新会向下流动到接收这个props的子组件中,触发对应的更新,但是反过来则不行。这样可以防止有多个子组件的父组件内的值被修改时,无法查找到哪个子组件修改的场景,从而导致应用中的数据流向无法清晰地追溯。
⏱ 2024-10-30 08:38:06 ^3300034992-22-5176-5314
📌 在父组件的data中定义了info属性,并且通过v-model的方式传给了子组件,代码如下:
这里使用了$emit方法,在子组件中,给按钮button绑定了一个单击事件,在事件回调函数中,采用如下代码:this.$emit(‘update:info’,’Tom’)
⏱ 2024-10-30 08:45:17 ^3300034992-22-10011-10248
📌 调用$emit方法实际上就是触发一个父组件的方法,这里的update是固定写法,代表更新,而:info表示更新info这个prop,第二个参数Tom表示更新的值
⏱ 2024-10-30 08:45:24 ^3300034992-22-10299-10379
📌 D组件中通过$emit调用父组件的方法,同时在父组件中修改data中的infoFromD,同时也影响到了作为props传递给C组件的infoFromD,这就实现了兄弟组件的通信。
⏱ 2024-10-30 08:46:15 ^3300034992-22-11526-11615
📌 事件总线EventBus和mitt在Vue 2中,可以采用EventBus这种方法,实际上就是将沟通的桥梁换成自己,同样需要有桥梁作为通信中继。就像是所有组件共用相同的事件中心,可以向该中心发送事件或接收事件,所有组件都可以上下平行地通知其他组件。
⏱ 2024-10-30 08:46:39 ^3300034992-22-11759-11913
📌 在Vue 3中,由于取消了Vue中全局变量Vue.prototype.$EventBus这种写法,所以采用EventBus这种事件总线来进行通信已经无法使用,取而代之,可以采用第三方事件总线库mitt。
⏱ 2024-10-30 12:36:50 ^3300034992-22-12742-12843
📌 通常,当需要从父组件向子组件传递数据时,我们使用props。想象一下这样的结构:有一些深度嵌套的组件,深层的子组件只需要父组件的部分内容。在这种情况下,如果仍然将props沿着组件链逐级传递下去,可能会很麻烦。对于这种情况,我们可以使用一对provide(提供)和inject(注入)。无论组件层次结构有多深,父组件都可以作为其所有子组件的依赖提供者。
⏱ 2024-10-30 12:38:02 ^3300034992-22-13770-13975
📌 provide/inject绑定并不是响应式的。我们可以通过传递一个ref属性或reactive对象给provide来改变这种行为
⏱ 2024-10-30 12:40:11 ^3300034992-22-15827-15892
3.3 组件插槽
📌 在
中使用v-bind来绑定person这个值,我们称这个值为插槽的props,就是标识出这个插槽想要将person带给外部父组件使用。同时,在父组件上,可以给设置v-slot:default=”slotProps”,其中冒号后面的是参数,因为是默认插槽,没有指定名字,所以使用default,而等号后面的值slotProps用于定义我们提供的插槽props的名字。
⏱ 2024-10-30 12:43:53 ^3300034992-23-6094-63043.5 异步组件和
📌
也可以和动态组件结合使用,例如Vue Router中的 和动画组件 等
⏱ 2024-10-30 18:33:17 ^3300034992-25-2058-21443.7 Mixin
📌 每个Mixin都可以拥有自己的data函数,每个data函数都会被调用,并将返回结果合并。在数据的property发生冲突时,会以组件自身的数据优先
⏱ 2024-10-30 18:38:02 ^3300034992-27-1667-1741📌 在Vue 2中,Mixin是将部分组件逻辑抽象成可重用块的主要工具,在一定程度上解决了多个组件的逻辑公用问题,但是也有几个问题:· Mixin很容易发生冲突:因为每个Mixin的属性都被合并到同一个组件中,所以相同的property名会冲突。· 可重用性是有限的:我们不能向Mixin传递任何参数来改变它的逻辑,这降低了它在抽象逻辑方面的灵活性。为了解决这些问题,Vue 3提供了组合式API,添加了一种通过逻辑关注点组织代码的新方法,从而达到更加极致的逻辑共享和复用,让组件化更加完美
⏱ 2024-10-30 18:39:59 ^3300034992-27-4506-4836第4章 Vue.js组合式API
📌 所谓组合式,就是我们可以自由地组合逻辑,即剥离公共逻辑,差异化个性逻辑,维护整体逻辑
⏱ 2024-10-30 18:41:02 ^3300034992-30-461-5034.2 setup方法
📌 setup方法在组件的beforeCreate之前执行,此时由于组件还没有实例化,是无法像配置式API一样直接使用this.xx访问当前实例的上下文对象的,例如data、computed和methods都没法访问,因此setup在和其他配置式API一起使用时可能会导致混淆,需要格外注意。但是,Vue还是在组合式API中提供了getCurrentInstance方法来访问组件实例的上下文对象
⏱ 2024-10-30 18:44:00 ^3300034992-32-3143-3368📌 需要注意的是,不要把getCurrentInstance当作在配置式API中的this的替代方案来随意使用,另外getCurrentInstance方法只能在setup或生命周期钩子中调用,并且不建议在业务逻辑中使用该方法,可以在开发一些第三方库时使用。
⏱ 2024-10-30 18:44:24 ^3300034992-32-3723-38504.3 响应式类方法
📌 reactive和ref都是用来定义响应式数据的。reactive更推荐定义复杂的数据类型,不能直接解构,ref更推荐定义基本类型。ref可以简单地理解为是对reactive的二次包装,ref定义数据访问的时候要多一个.value。
⏱ 2024-10-31 08:42:15 ^3300034992-33-2135-2251📌 对于复杂对象而言,ref和reactive都属于递归嵌套监听,也就是数据的每一层都是响应式的,如果数据量比较大,则非常消耗性能,而shallowRef和shallowReactive是非递归监听,只会监听数据的第一层
⏱ 2024-10-31 18:27:21 ^3300034992-33-5147-5255📌 如果是通过shallowRef创建的数据,那么Vue监听的是.value变化,并不是第一层的数据的变化。因此如果要更改shallowRef创建的数据可以调用xxx.value = {},也可以使用triggerRef可以强制触发之前没有被监听到的更新。另外Vue 3中没有提供triggerReactive,所以triggerRef不能触发shallowReactive创建的数据更新。
⏱ 2024-10-31 18:28:04 ^3300034992-33-5600-5793📌 readonly表示只读,可以将响应式对象标识成只读,当尝试修改时会抛出警告,shallowReadonly方法设置第一层只读,isReadonly方法判断是否为只读对象
⏱ 2024-10-31 18:28:34 ^3300034992-33-5943-6028📌 isRef方法用于判断是否是ref方法返回对象,isReactive方法用于判断是否是reactive方法返回对象,isProxy方法用于判断是否是reactive方法或者ref方法返回对象。
⏱ 2024-10-31 18:28:43 ^3300034992-33-6469-6565📌 toRaw方法可以返回一个响应式对象的原始普通对象,可用于临时读取数据而无须承担代理访问/跟踪的开销,也可用于写入数据而避免触发更改。makeRaw方法可以标记并返回一个对象,使其永远不会成为响应式对象
⏱ 2024-10-31 18:28:58 ^3300034992-33-6681-68114.4 监听类方法
📌 watchEffect方法可以监听响应式对象的改变,参数是一个函数,这个函数中所依赖的响应式对象如果发生变化,都会触发这个函数
⏱ 2024-10-31 18:31:21 ^3300034992-34-1672-1735📌 在配置式API中,watch是指监听器,组合式API中同样提供了watch方法,其使用场景和用法是一致的,主要是对响应式对象变化的监听。其和watchEffect相比有些类似,主要区别是:· watch需要监听特定的数据源,并执行对应的回调函数,而watchEffect不需要指定监听属性,它会自动收集依赖,只要回调函数中使用了响应式的属性,那么当这些属性变更的时候,这个回调都会执行。· watch在默认情况下,watch是惰性的,即只有当被监听的源发生变化时才执行回调。· watch可以访问监听状态变化前后的新旧值。watch监听单个数据源,第一个参数可以是返回值的getter函数,也可以是一个响应式对象,第二个参数是触发变化的回调函数
⏱ 2024-10-31 18:32:52 ^3300034992-34-2788-3226📌 watch监听复杂响应式对象时,如果要完全深度监听,则需要添加deep:true配置,同时第一个参数需要为一个getter方法,并采用深度复制
⏱ 2024-10-31 18:33:44 ^3300034992-34-4096-41674.5 生命周期类方法
📌 由于setup方法在组件的beforeCreate和created之前执行,因此不再提供对应的钩子方法,这些生命周期钩子注册函数只能在setup方法内同步使用,因为它们依赖于内部的全局状态来定位当前活动的实例(此时正在调用其setup的组件实例),在没有当前活动实例的情况下,调用它们将会出错。同时,在这些生命周期钩子内同步创建的侦听器和计算属性也会在组件卸载时自动删除,这点和配置式API是一致的
⏱ 2024-10-31 18:36:24 ^3300034992-35-1072-12714.7 provide / inject
📌 provide(提供)和inject(注入)也可以在组合式API的setup方法中使用,以实现跨越层级的组件通信。provide方法接收两个参数,一个是提供数据的key;另一个是值value,也可以是对象、方法等,如示例代码4-7-1所示。
⏱ 2024-11-01 08:02:36 ^3300034992-37-470-619📌 inject方法接收两个参数,一个是需要注入的数据的key,另一个是默认值(可选)
⏱ 2024-11-01 08:02:46 ^3300034992-37-979-1020📌 可以在provide值时使用ref或reactive方法,来增加provide值和inject值之间的响应性。这样,当provide的数据发生变化时,inject也能实时接收到
⏱ 2024-11-01 08:04:49 ^3300034992-37-1432-1520📌 通常情况下,只允许在provide的组件内去修改响应式的provide数据,但是如果需要在被inject的组件内去修改provide的值,则需要provide一个回调方法,然后在被inject的组件内调用 ^3300034992-37-1832-1934
- 💭 就是在已有的 provide 基础上,在新增一个回调函数的 provide,然后这个回调函数允许子组件 inject 后调用并修改已有 provide 的值,说白了就是单项数据流,然后通过传递一个方法达到子组件修改父组件值的能力 - ⏱ 2024-11-01 08:05:23
📌 如果要确保通过provide传递的数据不会被inject的组件更改,则可以使用readonly方法
⏱ 2024-11-01 08:05:37 ^3300034992-37-2249-22984.8 单文件组件
📌 如果不想默认全部变量都直接暴露给使用,而是控制需要返回哪些数据给使用,可以使用defineExpose方法来明确要暴露的属性 ^3300034992-38-2037-2131