前言
在上篇文章里,根据vue2.0与vue3.0的文档,针对基础部分进行了横向对比。基础部分的对比主要是比较琐碎的内容,比如一些很细节的写法,又或是几个名称的变化。(不过在我看来,正是因为这各种琐碎,才让人习惯性的敲出来后,又得一个个删掉,这不就暴躁起来了吗)。
现在对比进入到了第二阶段,本篇内容主要为vue2.0与vue3.0的组件部分横向对比。
组件部分,在我看来其实是vue很核心的内容,所以其实vue3.0对比vue2.0在组件部分上没有很大的改动(当然也有一点啦🤏)。因此,本篇文章主要内容,除了进行vue2.0与vue3.0语法上的横向对比外,主要还进行了内容的重点概括。
(对比差异部分,使用2️⃣3️⃣分别表示vue2.0与vue3.0)
(对比差异部分,使用2️⃣3️⃣分别表示vue2.0与vue3.0)
组件相关介绍
-
组件:可复用的vue实例。个人认为所谓的“组件”主要为页面组件,将界面中重复的部分连同功能一起提取成可重用的代码段。
-
组件特点:
1、多次使用均相互独立,每次都是新实例。
2、组件的根元素:
2️⃣组件只能有单个根元素,需要将模板的内容包裹在一个父元素内。 3️⃣组件允许多个根元素。
组件的基础使用
-
定义组件名:
1、kebab-case法。定义和引用都需要使用这个短横线写法。
2、PascalCase法。定义这样写,引用时可以用短横线写法或同定义写法。
-
使用组件:
1、注册组件
1)全局注册: 可全局使用,但就算不使用,打包的时候还是会打包进去,造成多余部分下载。 组件间不具备层次关系,均可互相调用。 2)局部注册: 需在用组件的位置注册。 组件间具备层次关系,需要引用才可使用。2、创建组件实例
3、使用组件
-
vue2.0与vue3.0 对比:
1、组件注册方式变化:
2️⃣Vue.component('component-name',{})
3️⃣const app = Vue.createApp({});app.component('component-name',{})
2、挂载方式变化:
2️⃣new Vue()
3️⃣app.mount()
关于props
通过prop父组件向子组件传递数据
-
用法:父传数据给子,子用props选项承接数据。
-
命名相关:attribute是大小写不敏感的,所以父需要用 kebab-case法,子用PascalCase法承接。
-
数据类型相关:
1、props可为数组或对象。
2、父组件可以传任何类型的值。(除了静态字符串,其他借助v-bind传)
-
特性:props是单向数据流,单向下行。
1)防止从子组件意外变更父级组件的状态,从而导致数据流向难以理解。 2)对象和数组是引用传入同根数据的,子组件改变则会影响父组件的状态。 (应避免使用,因为无视了单向数据流原则) -
如需在子组件改变接收到的props:
方法一:复制一份data为本地prop数据来使用。
props: ['initialCounter'], data: function () { return { counter: this.initialCounter } }方法二:定义一份计算属性,适合于需要数据改造的情况。
props: ['size'], computed: { normalizedSize: function () { return this.size.trim().toLowerCase() } } -
关于prop验证
注意:会在一个组件实例创建之前进行验证,所以不要在验证中使用data等。
用法:validator选项用来自定义验证函数。
validator:(value)=>{ return ['success', 'warning', 'danger'].indexOf(value) !== -1 }
非 Prop 的 Attribute
-
介绍:一个非 prop 的 attribute 是指传向一个组件,但是该组件并没有相应 props 或 emits 定义的 attribute。(常见的示例包括 class、style 和 id attribute。可以通过 $attrs property 访问那些 attribute )
-
使用:
1、一般情况下继承:父组件使用子组件时,添加的属性事件等,会自动添加到子组件的根节点。
2、如果需要禁用继承:在子组件中设置
inheritAttrs: false,通过v-bind="$attrs"来决定父组件给的property传到那个位置。3、3️⃣如果子组件有多个根节点,需要
v-bind="$attrs"来显示的绑定父组件传值位置。
自定义事件 emits
自定义事件的用法
-
子组件上:
1、3️⃣vue3.0 新增emits选项:
1、emits选项,和现有的 props 选项类似,可通过emits选项定义组件可触发的事件。 2、emits选项可为 数组或对象,在emits选项内对自定义事件进行定义。 3、备注: 关于原生事件: 在 emits 选项中定义了原生事件 (如 click) 时,将使用组件中的事件替代原生事件侦听器。 关于验证函数: 子组件使用 $emit 调用父组件对应函数,如果需要验证该方法,可以为这个事件分配一个函数,该函数接收传递给 emit 调用的参数,并返回一个布尔值以指示事件是否有效。2、通过
$emit('event-name',传参)触发事件。 -
父组件上:父用
@event-name="自定义函数"承接,并在自定义函数的第一个参数处获得传参。
监听原生事件
-
native修饰符
2️⃣父级调用组件时,使用.native修饰符,一般可监听到原生事件。
<base-input v-on:focus.native="onFocus"></base-input>3️⃣vue3.0 已移除该修饰符。
-
$listeners 实例方法
1、如果组件内是一些特定元素,可能监听不到,所以组件内可通过$listeners,将所有监听器指向特定的子元素。
2、当组件内部的根元素不一定是对应的元素时,如想监听base-input内部的input,但没想到组件最外层是label。
插槽 slot
插槽的一般使用
主要用于父组件给子组件传内容。
使用时,父在调用子组件位置中传内容;子在对应显示位置用插槽slot标签,slot标签作为想要插入内容的占位符。
关于插槽
1、槽内可以包含任何模板代码,包括 HTML、组件。
2、插槽传值作用域,父用父子用子。(父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。)所以不能从父级传子级作用域的东西。
3、插槽默认值(后备),写在slot标签内,父不提供插槽内容时展示默认值。
其他插槽类型
默认插槽的缩写语法不能和具名插槽混用,因为它会导致作用域不明确。混合使用必须要用完整的语法。
具名插槽
-
适用场景:一个组件内多个插槽,想对应位置对应展示。
-
用法:
1、子组件上:通过name给插槽分配id,不写则默认
name:default。<slot name="header"></slot>2、父组件上:
<template>元素上使用 v-slot 指令,v-slot:可简写为 #。<template v-slot:header> <h1>Here might be a page title</h1> </template>
作用域插槽
-
适用场景:父想访问子的数据并展示。
-
用法
1、子组件上:在
<slot>通过v-bind 绑定prop让父的对应位置能够拿到。<span> <slot v-bind:user="user"> {{ user.lastName }} </slot> </span>2、父组件上:
<template>元素上使用 v-slot 指令定义插槽prop并使用,可简写,将插槽prop重命名进行使用。<current-user> <template v-slot:default="slotProps"> {{ slotProps.user.firstName }} </template> </current-user> //可对插槽prop进行重命名为person: <current-user v-slot="{ user: person }"> {{ person.firstName }} </current-user>
依赖注入 provide/inject
-
作用:长距离的 prop 效果。
组件深层嵌套时,深层子组件需要最外层父组件,需要将prop沿着组件链级传递,会很麻烦,这时候就可以用 provide inject,无论组件层次结构有多深,父组件都可以作为其所有子组件的依赖提供者。
-
使用方式
1、父组件提供数据。
provide:{}/(){return{}}2、在深层子组件内可使用数据。
inject:[]3、备注:除了基本操作外,如果需要provide一些组件的实例,需要转为返回对象的函数,而不能直接使用对象。
// 错误 provide: { todoLength: this.todos.length }, // 正确 provide() { return { todoLength: this.todos.length } }, -
特点:
provide和inject的绑定不是响应式的,改变子中的对应值,父不会改变。
如果需要改变,祖先传带有计算属性的值。
provide() { return { todoLength: Vue.computed(() => this.todos.length) } } -
备注
2️⃣在vue2.0中,依赖注入的内容属于“边界情况”,建议少用。
3️⃣在vue3.0中,将依赖注入内容移出了“边界情况”,证明使用率的提高。
动态组件
-
:is 可切换不同组件
<component v-bind:is="currentTabComponent"></component> //其中的currentTabComponent,可以为已注册组件的名字/组件的选项对象 -
keep-alive 保持组件状态,即缓存
因为每次切换,vue都会创建一个新的currentTabComponent 实例,所以会出现希望缓存的场景。
模板引用 ref
-
作用:访问子组件实例/子元素。
-
使用:ref在子组件赋予一个id引用,父组件通过
this.$refs.xxx访问。 -
备注:$refs只会在组件渲染完成之后生效。
-
区别:
2️⃣在vue2.0中,模板引用的内容属于“边界情况”,建议少用。
3️⃣在vue3.0中,将模板引用内容移出了“边界情况”,证明使用率的提高。
元素位置受限情况
-
问题:有些元素需要限制内部元素。如tabel里应该放tr,若组件内写其他的内容,则会被视为无效。
-
解决:
1、可以用is来确定组件,给一定的标签赋予组件名称。
2、当组件用于原生元素,需要vue:开头。
边界情况(建议少用的情况)
1、访问根实例:$root
2、访问父组件实例:$parent
3、访问子组件实例:$children(3️⃣在vue3.0中已废除该实例方法)
4、程序化事件侦听:$on/$once/$off(3️⃣vue3.0已移除这三个实例方法)
5、强制更新:$forceUpdate
6、仅渲染一次就缓存:v-once(适用于一个组件包含了大量静态内容,内容只计算一次然后缓存起来)
父子组件相互关系汇总
数据相关
-
父传给子:
1、prop
2、插槽 slot
3、依赖注入 provide/inject(长距离传输)
-
子传给父:
1、自定义事件 emits
实例相关
-
访问子组件实例:
1、模板引用 $refs
2、$children(3️⃣在vue3.0中已废除该实例方法)
-
访问夫组件实例:
1、$root 访问根实例
2、$parent
结尾
本篇文章主要为根据文档,针对vue2.0与vue3.0的组件部分进行横向对比。
总体而言,组件部分最大的改变,就是在vue3.0中增加了emits选项内容,这让自定义事件更加的具体即更有针对性。其他细节方面,也有很多是因为emits的存在,所以废除了一些实例方法。
其实,本篇文章也是个人针对vue组件部分内容的概括(不代表所有人哈),vue组件常用的部分主要就是上述陈列的内容。那为啥还列出来呢?
其实主要是因为,在我个人的想象里,vue内容居多巨多巨复杂(前端小菜鸡的自述🙃),vue就像一片汪洋大海,感觉自己纯粹是海里的鱼,虽然在其中游来游去,但总觉得远方还有一大片未曾到达过的的未知领域。这会让我惧怕未知。但花了时间和精力列出来后,就仿佛看到了海洋的边缘,虽然肯定还会有很多未知,但自己心里会清楚,这些未知就在这片海里。
接下来计划,以相同的vue2.0与3.0文档横向对比方式,对比可复用部分等内容,个人认为这是vue3.0最大的变化部分。
传送门
vue2.0与vue3.0 基础部分 对比:根据文档横向对比vue2.0与vue3.0(一)
vue2.0与vue3.0 组件部分 对比:根据文档横向对比vue2.0与vue3.0(二)
vue2.0与vue3.0 可复用&组合 对比:根据文档横向对比vue2.0与vue3.0(三)
vue2.0与vue3.0 零碎部分 对比:根据文档横向对比vue2.0与vue3.0(四)