本文已参与「新人创作礼」活动,一起开启掘金创作之路。详情
1、Vue组件之间的通信
组件通信常用方式
- props
$emit/ $on$children/$parent$attrs/ $listeners- ref
$root- eventbus
- vuex
应用场景
父子传值:props / parent / ref / $attrs
兄弟组件:root / eventbus / vuex
跨层级通信:eventbus / vuex / provide + inject
vue3变化
vue3中废弃了$on、$children、$listeners
2、v-if和v-for的优先级
解答:
-
实践中我们不应该把v-for和v-if放在一起
-
vue2中是v-for的优先级高。在vue2中一起用时,渲染过程是先进行循环,然后再判断条件是否要展示当前的这一条。如果是很大的列表只渲染一小部分也会在重新渲染的时候遍历整个列表,这就比较浪费性能。我们可以用变量或者computed计算属性来先生成需要渲染的列表,直接渲染我们想展示的。
-
vue3中v-if的优先级高。如果是根据列表每一条内的属性来判断,则会报错,因为判断的时候循环还没开始,拿不到单条数据来判断。
-
可能出现两者共用的场景:
- 为了过滤掉列表中的某些项。比如
v-for="user in users" v-if="user.isActive"那么我们可以直接先计算出需要展示的列表数据,直接循环需要展示的部分。 - 为了隐藏整个列表。比如
v-for="user in users" v-if="shouldShowUsers"那么可以直接把v-if提到整个循环的外层,在列表循环外包一层div来做判断。
- 为了过滤掉列表中的某些项。比如
源码中找答案:
3、Vue的生命周期及每个阶段做的事
解答:
| ⽣命周期v2 | ⽣命周期v3 | 描述 |
|---|---|---|
| beforeCreate | beforeCreate | 组件实例被创建之初 |
| created | created | 组件实例已经完全创建 |
| beforeMount | beforeMount | 组件挂载之前 |
| mounted | mounted | 组件挂载到实例上去之后 |
| beforeUpdate | beforeUpdate | 组件数据发⽣变化,更新之前 |
| updated | updated | 数据数据更新之后 |
| beforeDestroy | beforeUnmounted | 组件实例销毁之前 |
| destroyed | unmounted | 组件实例销毁之后 |
| activated | activated | keep-alive 缓存的组件激活时 |
| deactivated | deactivated | keep-alive 缓存的组件停⽤时调⽤ |
| errorCaptured | errorCaptured | 捕获⼀个来自子孙组件的错误时被调用 |
| renderTracked | 调试钩子,响应式依赖被收集时调用 | |
| renderTriggered | 调试钩子,响应式依赖被触发时调用 | |
| serverPrefetch | ssr only,组件实例在服务器上被渲染前调用 |
vue3生命周期流程图
结合实践:
beforeCreate:通常⽤于插件开发中执⾏⼀些初始化任务
created:组件初始化完毕,可以访问各种数据,获取接⼝数据等
mounted:dom已创建,可⽤于获取访问数据和dom元素;访问⼦组件等。
beforeUpdate:此时 view 层还未更新,可⽤于获取更新前各种状态
updated:完成 view 层的更新,更新后,所有状态已是最新
beforeunmounted:实例被销毁前调⽤,可⽤于⼀些定时器或订阅的取消
unmounted:销毁⼀个实例。可清理它与其它实例的连接,解绑它的全部指令及事件监听器
【拓展】:
- setup和created谁先执⾏?
- setup中为什么没有beforeCreate和created?
4、vue双绑使用及原理
解答:
-
vue中双向绑定是⼀个指令 v-model ,可以绑定⼀个响应式数据到视图,同时视图中变化能改变该值。
-
v-model 是语法糖,默认情况下相当于
:value绑定值和@input事件 。使用v-model 可以减少⼤量繁琐的事件处理代 码,提高开发效率。 -
通常在表单项上使用v-model ,还可以在自定义组件上使用,表示某个值的输⼊和输出控制
-
通过
<input v-model='xxx' />的⽅式将xxx的值绑定到表单元素value上。- 对于checkbox,可以使⽤ truevalue 和false-value指定特殊的值。 对于radio可以使⽤value指定特殊的值。 对于select可以通过options元 素的value设置特殊的值;还可以结合
.lazy,.number,.trim对v-mode的行为做进⼀步限定。
- v-model ⽤在自定义组件上时⼜会有很⼤不同,vue3中它类似于 sync 修饰符,最终展开的结果是
modelValue属性和update:modelValue事件;vue3中我们甚⾄可以⽤参数形式指定多个不同的绑定,例如v-model:foo和v-model:bar,⾮常强⼤!
- 对于checkbox,可以使⽤ truevalue 和false-value指定特殊的值。 对于radio可以使⽤value指定特殊的值。 对于select可以通过options元 素的value设置特殊的值;还可以结合
-
v-model 是⼀个指令,它的神奇魔法实际上是vue的编译器完成的。包含 v-model 的模板,转 换为渲染函数之后,实际上还是是value属性的绑定以及input事件监听,事件回调函数中会做相应变量更新操 作。编译器根据表单元素的不同会展开不同的DOM属性和事件对,⽐如text类型的input和textarea会展开为 value和input事件;checkbox和radio类型的input会展开为checked和change事件;select⽤value作为属 性,⽤change作为事件
【拓展】:
- v-model 和 sync 修饰符有什么区别
- ⾃定义组件使⽤ v-model 如果想要改变事件名或者属性名应该怎么做
5、Vue中如何扩展⼀个组件
解答:
-
常⻅的组件扩展⽅法有:mixins,slots,extends等
-
混⼊mixins是分发 Vue 组件中可复⽤功能的⾮常灵活的⽅式。混⼊对象可以包含任意组件选项。当组件使⽤ 混⼊对象时,所有混⼊对象的选项将被混⼊该组件本身的选项。
// 复⽤代码:它是⼀个配置对象,选项和组件⾥⾯⼀样 const mymixin = { methods: { dosomething(){} } } // 全局混⼊:将混⼊对象传⼊ Vue.mixin(mymixin) // 局部混⼊:做数组项设置到mixins选项,仅作⽤于当前组件 const Comp = { mixins: [mymixin] } -
插槽主要⽤于vue组件中的内容分发,也可以⽤于组件扩展。如果要精确分发到不同位置可以使⽤具名插槽,如果要使⽤⼦组件中的数据可以使⽤作⽤域插槽。
子组件Child
<div> <slot>这个内容会被⽗组件传递的内容替换</slot> </div>父组件Parent
<div> <Child>来⾃⽼爹的内容</Child> </div> -
组件选项中还有⼀个不太常⽤的选项extends,也可以起到扩展组件的⽬的
// 扩展对象 const myextends = { methods: { dosomething(){} } } // 组件扩展:做数组项设置到extends选项,仅作⽤于当前组件 // 跟混⼊的不同是它只能扩展单个对象 // 另外如果和混⼊发⽣冲突,该选项优先级较⾼,优先起作⽤ const Comp = { extends: myextends } -
混⼊的数据和⽅法不能明确判断来源且可能和当前组件内变量产⽣命名冲突,vue3中引⼊的composition api,可以很好解决这些问题,利⽤独⽴出来的响应式模块可以很⽅便的编写独⽴逻辑并提供响应式的数据, 然后在setup选项中组合使⽤,增强代码的可读性和维护性。例如:
// 复⽤逻辑1 function useXX() {} // 复⽤逻辑2 function useYY() {} // 逻辑组合 const Comp = { setup() { const {xx} = useXX() const {yy} = useYY() return {xx, yy} } }
【扩展】
Vue.extend⽅法你⽤过吗?它能⽤来做组件扩展吗?
知其所以然
-
mixins原理:
-
slots原理
6、子组件可以直接改变父组件的数据么,说明原因
解答:
-
所有的 prop 都使得其⽗⼦之间形成了⼀个单向下⾏绑定:⽗级 prop 的更新会向下流动到⼦组件中,但是反 过来则不⾏。这样会防⽌从⼦组件意外变更⽗级组件的状态,从⽽导致你的应⽤的数据流向难以理解。另外, 每次⽗级组件发⽣变更时,⼦组件中所有的 prop 都将会刷新为最新的值。这意味着你不应该在⼀个⼦组件内 部改变 prop。如果你这样做了,Vue 会在浏览器控制台中发出警告。
const props = defineProps(['foo']) // ❌下⾯⾏为会被警告, props是只读的! props.foo = 'bar' -
实际开发过程中有两个场景会想要修改⼀个属性:
- 这个 prop ⽤来传递⼀个初始值;这个⼦组件接下来希望将其作为⼀个本地的 prop 数据来使⽤。在这 种情况下,最好定义⼀个本地的 data,并将这个 prop ⽤作其初始值:
const props = defineProps(['initialCounter']) const counter = ref(props.initialCounter)- 这个 prop 以⼀种原始的值传⼊且需要进⾏转换。在这种情况下,最好使⽤这个 prop 的值来定义⼀个计 算属性:
const props = defineProps(['size']) // prop变化,计算属性⾃动更新 const normalizedSize = computed(() => props.size.trim().toLowerCase()) -
实践中如果确实想要改变⽗组件属性应该emit⼀个事件让⽗组件去做这个变更。注意虽然我们不能直接修改 ⼀个传⼊的对象或者数组类型的prop,但是我们还是能够直接改内嵌的对象或属性。