本文已参与「新人创作礼」活动,一起开启掘金创作之路。
Slot具名插槽语法
在 Vue2.x 中, 具名插槽的写法:
<!-- 子组件中:-->
<slot name="title"></slot>
在父组件中使用:
<template slot="title">
<h1>歌曲:《孤勇者》</h1>
<template>
如果我们要在 slot 上面绑定数据,可以使用作用域插槽,实现如下:
// 子组件
<slot name="content" :data="data"></slot>
export default {
data(){
return{
data:["走过来人来人往","不喜欢也得欣赏","陪伴是最长情的告白"]
}
}
}
<!-- 父组件中使用 -->
<template slot="content" slot-scope="scoped">
<div v-for="item in scoped.data">{{item}}</div>
<template>
在 Vue2.x 中具名插槽和作用域插槽分别使用slot和slot-scope来实现, 在 Vue3.0 中将slot和slot-scope进行了合并同意使用。 Vue3.0 中v-slot:
<!-- 父组件中使用 -->
<template v-slot:content="scoped">
<div v-for="item in scoped.data">{{item}}</div>
</template>
<!-- 也可以简写成: -->
<template #content="{data}">
<div v-for="item in data">{{item}}</div>
</template>
组件上v-model用法
在 Vue 2.0 发布后,开发者使用 v-model 指令必须使用为 value 的 prop。如果开发者出于不同的目的需要使用其他的 prop,他们就不得不使用 v-bind.sync。此外,由于v-model 和 value 之间的这种硬编码关系的原因,产生了如何处理原生元素和自定义元素的问题。
在 Vue 2.2 中,我们引入了 model 组件选项,允许组件自定义用于 v-model 的 prop 和事件。但是,这仍然只允许在组件上使用一个 model。
在 Vue 3 中,双向数据绑定的 API 已经标准化,减少了开发者在使用 v-model 指令时的混淆并且在使用 v-model 指令时可以更加灵活。
2.x语法
在 2.x 中,在组件上使用 v-model 相当于绑定 value prop 和 input事件:
<ChildComponent v-model="pageTitle" />
<!-- 简写: -->
<ChildComponent :value="pageTitle" @input="pageTitle = $event" />
如果要将属性或事件名称更改为其他名称,则需要在 ChildComponent 组件中添加 model 选项:
<!-- ParentComponent.vue -->
<ChildComponent v-model="pageTitle" />
// ChildComponent.vue
export default {
model: {
prop: 'title',
event: 'change'
},
props: {
// 这将允许 `value` 属性用于其他用途
value: String,
// 使用 `title` 代替 `value` 作为 model 的 prop
title: {
type: String,
default: 'Default title'
}
}
}
所以,在这个例子中 v-model 的简写如下:
<ChildComponent :title="pageTitle" @change="pageTitle = $event" />
使用 v-bind.sync
在某些情况下,我们可能需要对某一个 prop 进行“双向绑定”(除了前面用 v-model 绑定 prop 的情况)。为此,我们建议使用 update:myPropName 抛出事件。例如,对于在上一个示例中带有 title prop 的 ChildComponent,我们可以通过下面的方式将分配新 value 的意图传达给父级:
this.$emit('update:title', newValue)
如果需要的话,父级可以监听该事件并更新本地 data property。例如:
<ChildComponent :title="pageTitle" @update:title="pageTitle = $event" />
为了方便起见,我们可以使用 .sync 修饰符来缩写,如下所示:
<ChildComponent :title.sync="pageTitle" />
3.x语法
在 3.x 中,自定义组件上的 v-model 相当于传递了 modelValue prop 并接收抛出的 update:modelValue事件:
<ChildComponent v-model="pageTitle" />
<!-- 简写: -->
<ChildComponent
:modelValue="pageTitle"
@update:modelValue="pageTitle = $event"
/>
渲染函数API改变
- h现在全局导入,而不是作为参数传递给渲染函数
- 渲染函数参数更为在有状态组件和函数组件之间更加一致
- vnode现在是一个有扁平的prop结构
render函数将自动接收h函数(它是createElement的别名)作为参数
//vue2.x
export default{
render(h){
return h('div')
}
}
//vue3 渲染
import { h } from 'vue'
export default {
render() {
return h('div')
}
}
举个例子:
<template>
<div>
<RenderComp v-model='title'>
<template v-slot:default>
<!-- 默认插槽 -->
头部
</template>
<template v-slot:content>
<!-- 具名插槽 -->
内容
</template>
</RenderComp>
</div>
</template>
<script>
import {
ref,
h
} from "vue";
export default {
components: {
RenderComp: {
props: {
modelValue: {
type: String,
default: ''
},
},
setup(props,{attrs,slots,emit}) {
// 以前得通过$scopedSlots获取对应的插槽
console.log(slots.default()); //获取默认插槽
console.log(slots.content()); //获取名字为content的插槽
function changeTitle(newV) {
emit('update:modelValue','哈哈哈');
}
return () => h("div", {}, [h("div", {
onClick:changeTitle,
},[
`渲染函数api:${props.modelValue}`,
slots.default(),
slots.content()
])]);
}
}
},
setup(props) {
const title = ref("双向数据绑定");
return {
title
};
}
};
</script>
同时,演示了slots作为函数暴露
使用普通函数创建功能组件
- 在 3.x 中,功能性组件 2.x 的性能提升可以忽略不计,因此我们建议只使用有状态的组件
- 功能组件只能使用接收
props和context的普通函数创建 (即:slots,attrs,emit)。 - 重大变更:
functional attribute在单文件组件 (SFC)<template>已被移除 - 重大变更:
{ functional: true }选项在通过函数创建组件已被移除
在vue2.0中,功能组件有两个主要用途:
- 性能优化提高,因为它们的初始化速度比有状态组件快
- 可以返回多个根节点
然而,在 Vue 3 中,有状态组件的性能已经提高到可以忽略不计的程度。此外,有状态组件现在还包括返回多个根节点的能力。 因此,功能组件剩下的唯一用例就是简单组件,比如创建动态标题的组件。否则,建议你像平常一样使用有状态组件。
总结:非特殊情况下,官网还是建议我们使用有状态的组件
Functional.vue
import { h } from 'vue'
const DynamicHeading = (props, context) => {
return h(`h${props.level}`, context.attrs, context.slots)
}
DynamicHeading.props = ['level']
export default DynamicHeading
<Functional level='3'>动态标题</Functional>
可以传入不同的level 定制不同的h系列标题。
异步组件的更改
- 新defineAsyncComponent助手方法,它显示定义异步组件
- componnet选项命名为loader
- 加载程序函数被本身不接受resolve和reject参数,必须返回一个Promise
2.x
以前,异步组件是通过将组件定义为返回 promise 的函数来创建的,例如:
const asyncPage = () => import('./NextPage.vue')
对于带有选项的更高阶组件语法:
const asyncPage = {
component: () => import('./NextPage.vue'),
delay: 200,
timeout: 3000,
error: ErrorComponent,
loading: LoadingComponent
}
3.x
在vue3中,由于功能组件被定义为纯函数,因为需要通过将异步组件定义包装在新的defineAsyncComponent助手来显式定义组件
import { defineAsyncComponent } from 'vue'
import ErrorComponent from './components/ErrorComponent.vue'
import LoadingComponent from './components/LoadingComponent.vue'
// 不带选项的异步组件
const asyncPage = defineAsyncComponent(() => import('./NextPage.vue'))
// 带选项的异步组件
const asyncPageWithOptions = defineAsyncComponent({
loader: () => import('./NextPage.vue'),
delay: 200,
timeout: 3000,
errorComponent: ErrorComponent,
loadingComponent: LoadingComponent
})
自定义指令
API 已重命名,以便更好地与组件生命周期保持一致
- bind → beforeMount
- inserted → mounted
- beforeUpdate:新的!这是在元素本身更新之前调用的,很像组件生命周期钩子
- update → 移除!有太多的相似之处要更新,所以这是多余的,请改用 updated
- componentUpdated → updated
- **beforeUnmount ** 新的与组件生命周期钩子类似,它将在卸载元素之前调用。
- unbind -> unmounted
举个例子:
main.js
const app = createApp(App);
// 创建自定义指令
app.directive('highlight',{
// 指令 也拥有一组生命周期钩子
// 1.在绑定元素的父组件挂载之前调用
beforeMount(el,binding,vnode){
el.style.background = binding.value;
},
})
App.vue
<p v-highlight="'red'">自定义指令</p>
动画transion改变
- v-enter->v-enter-from
- v-leave->v-leave-from
vue2.x版本中
移除API
- keyCode 支持作为 v-on 的修饰符
- on,on,off 和 $once 实例方法 过滤
2.x,支持keyCodes作为修改v-on方法的方法
<!-- 键码版本 -->
<input v-on:keyup.13="submit" />
<!-- 别名版本 -->
<input v-on:keyup.enter="submit" />
vue3.x
在建议对任何要用作修饰符的键使用 kebab-cased (短横线) 大小写名称。
<!-- Vue 3 在 v-on 上使用 按键修饰符 -->
<input v-on:keyup.delete="confirmDelete" />
因此,这意味着 config.keyCodes 现在也已弃用,不再受支持。
$on,$off 和 $once 实例方法已被移除,应用实例不再实现事件触发接口。
Filters 已从 Vue 3.0 中删除,不再受支持。相反,我们建议用方法调用或计算属性替换它们。