【Vue3 入门到实战】16. Vue3 非兼容性改变
目录
1. 全局 API 的变化
2. 模板指令的变化
2.1 组件v-model用法
2.2 template v-for用法
2.3 v-if 和v-for 优先级变化
2.4 v-bind="object" 顺序敏感
2.5 v-on:event.native 被移除
3. 组件的变化
3.1 功能组件只能使用普通函数创建
3.2 SFC弃用功能属性
3.3 异步组件需要创建 defineAsyncComponent 方法
3.4 组件事件使用 emits 选项
4. 渲染函数的变化
4.1 渲染函数Api变化
4.2 删除 $scopedSlots 属性,并通过 $slots 将所有槽作为函数公开
4.3 $listeners 被删除/合并到$attrs
4.4 $attrs包括class和style属性
5. 总结
Vue 3 引入了许多改进和新特性,但同时也带来了一些与 Vue 2 不兼容的变化。以下是 Vue 3 中一些重要的非兼容性改变(参考官网)。地址链接如下👇 👇 👇
Breaking Changes | Vue 3 Migration Guide
1. 全局 API 的变化
全局 API 转移到应用实例:
在 Vue 3 中,许多全局 API 和选项被转移到了应用实例上。例如Vue.component、Vue.directive等现在需要通过创建的应用实例来使用。这意味着一些在 Vue 2 中直接挂载到全局 Vue 对象上的功能,现在需要通过应用实例(即 app 实例)来调用。
Vue 2 中的全局 API 使用方式
在 Vue 2 中,你可以直接使用全局的 Vue 对象来注册组件、指令、混入(mixins)、过滤器等。例如:
import Vue from 'vue';
import App from './App.vue';
// 注册全局组件
Vue.component('my-component', {
template: '<div>这是一个全局组件</div>'
});
// 注册全局指令
Vue.directive('focus', {
inserted(el) {
el.focus();
}
});
// 创建并挂载应用实例
new Vue({
render: h => h(App)
}).$mount('#app');
Vue 3 中的变化
在 Vue 3 中,这些全局 API 被移到了应用程序实例上,也就是说你需要先创建一个应用实例,然后在这个实例上调用相应的 API 方法。如下代码所示。
import { createApp } from 'vue';
import App from './App.vue';
// 创建应用实例
const app = createApp(App);
// 注册局部组件
app.component('my-component', {
template: '<div>这是一个局部组件</div>'
});
// 注册局部指令
app.directive('focus', {
mounted(el) {
el.focus();
}
});
// 挂载应用实例
app.mount('#app');
2. 模板指令的变化
2.1 组件v-model用法
Vue3重新设计了组件上的 v-model 用法,取代了 v-bind.sync。
在 Vue 3 中,v-model 的功能得到了扩展和改进,取代了 Vue 2 中的 .sync修饰符。这使得双向数据绑定更加灵活和直观。而且支持多个 v-model
绑定。还可以自定义事件名,默认是 update:modelValue
,但可以通过 v-model:propertyName
自定义。
Vue2种 .sync 修饰符
<!-- 父组件 -->
<template>
<div>
<child-component :money.sync="money" />
</div>
</template>
<script>
export default {
data() {
return {
money: 100
};
}
};
</script>
<!-- 子组件 -->
<template>
<div>
<button @click="$emit('update:money', 150)">修改银子</button>
</div>
</template>
Vue3种的 v-model
<!-- 父组件 -->
<template>
<div>
<child-component v-model:money="money" />
</div>
</template>
<script setup>
import { ref } from 'vue';
const money = ref(100);
</script>
<!-- 子组件 -->
<template>
<div>
<button @click="$emit('update:money', 150)">修改银子</button>
</div>
</template>
2.2 template v-for用法
在 Vue 3 中,<template v-for>
上的 :key
属性需要放在 <template>
标签上,而不是内部的每个元素上。这样可以确保每个节点都有唯一的键,并且避免重复键的问题。
Vue2示例
<template>
<ul>
<template v-for="item in items">
<li :key="item.id">{{ item.name }}</li>
<li :key="item.id + '_edit'">编辑 {{ item.name }}</li>
</template>
</ul>
</template>
Vue3示例
<template>
<ul>
<template v-for="item in items" :key="item.id">
<li>{{ item.name }}</li>
<li>编辑 {{ item.name }}</li>
</template>
</ul>
</template>
2.3 v-if 和v-for 优先级变化
在 Vue 3 中,当 v-if
和 v-for
同时出现在一个元素上时,v-for
的优先级高于 v-if
。而在 Vue 2 中,v-if
的优先级更高。
v-for
的优先级更高,这意味着它会先执行循环,然后对每个元素应用 v-if
。
Vue2示例
<ul>
<li v-for="item in items" v-if="item.isVisible" :key="item.id">{{ item.name }}</li>
</ul>
Vue3示例
<ul>
<li v-for="item in items" :key="item.id" v-if="item.isVisible">{{ item.name }}</li>
</ul>
2.4 v-bind="object"
顺序敏感
在 Vue 3 中,v-bind="object"
是顺序敏感的,这意味着对象中的属性顺序会影响最终的结果。如果对象中有相同的属性(例如多次绑定 class
),后面的值会覆盖前面的值。因此,你需要确保对象中的属性顺序符合预期。
2.5 v-on:event.native
被移除
在 Vue 3 中,v-on:event.native
修饰符被移除。这是因为自定义组件现在默认监听原生事件,不再需要 .native
修饰符。
Vue2示例
<template>
<custom-component @click.native="handleClick"></custom-component>
</template>
Vue3示例
<template>
<custom-component @click="handleClick"></custom-component>
</template>
3. 组件的变化
3.1 功能组件只能使用普通函数创建
在 Vue 3 中,功能组件(Functional Components)的概念有所变化。Vue 2 中的功能组件可以通过 functional: true
选项来定义,而在 Vue 3 中,功能组件只能通过普通的函数来创建。
也就是在 Vue 3 中,功能组件不再支持 functional: true
选项,功能组件必须通过返回渲染函数(如 h
函数)的方式实现。
Vue2示例
<template functional>
<div>{{ props.message }}</div>
</template>
<script>
export default {
functional: true,
props: {
message: String
}
};
</script>
Vue3示例
在 Vue 3 中,功能组件需要使用 defineComponent
和普通函数来定义:
import { defineComponent, h } from 'vue';
const FunctionalComponent = defineComponent({
props: {
message: String
},
setup(props) {
return () => h('div', props.message);
}
});
3.2 SFC弃用功能属性
在 Vue 3 中,单文件组件(SFC)中的 <template>
标签不再支持 functional
属性。这意味着你不能像在 Vue 2 中那样直接在 <template>
标签上添加 functional
属性来定义功能组件。
Vue2示例
<template functional>
<div>{{ props.message }}</div>
</template>
<script>
export default {
props: {
message: String
}
};
</script>
Vue3示例
在 Vue 3 中,你需要将功能组件逻辑移到 setup
函数中,并且不能再在 <template>
标签上使用 functional
属性。
<template>
<div>{{ message }}</div>
</template>
<script setup>
import { defineProps } from 'vue';
const props = defineProps({
message: String
});
</script>
3.3 异步组件需要创建 defineAsyncComponent
方法
在 Vue 3 中,异步组件的创建方式发生了变化。你需要使用 defineAsyncComponent
方法来定义异步组件,而不是像 Vue 2 中那样直接返回一个 Promise。
Vue2示例
const AsyncComponent = () => import('./AsyncComponent.vue');
Vue3示例
import { defineAsyncComponent } from 'vue';
const AsyncComponent = defineAsyncComponent(() => import('./AsyncComponent.vue'));
种方式提供了更多的配置选项,比如加载状态、错误处理等。如:
const AsyncComponent = defineAsyncComponent({
loader: () => import('./AsyncComponent.vue'),
loadingComponent: LoadingComponent,
errorComponent: ErrorComponent,
delay: 200,
timeout: 3000
});
3.4 组件事件使用 emits
选项
在 Vue 3 中,组件事件的声明更加明确,推荐使用 emits
选项来显式声明组件可以触发的事件。这有助于提高代码的可读性和维护性。
Vue2示例
<template>
<button @click="handleClick">点击我</button>
</template>
<script>
export default {
methods: {
handleClick() {
this.$emit('custom-event', 'some data');
}
}
};
</script>
Vue3示例
<template>
<button @click="handleClick">点击我</button>
</template>
<script setup>
import { defineEmits } from 'vue';
const emit = defineEmits(['custom-event']);
function handleClick() {
emit('custom-event', 'some data');
}
</script>
4. 渲染函数的变化
4.1 渲染函数Api变化
只要是h
函数签名的变化,Vue 3 中的 h
函数参数顺序更加清晰。
Vue2示例
h(tag, [data], [children])
Vue3示例
import { h } from 'vue';
h('div', { class: 'container' }, [
h('span', 'Hello, world!')
]);
4.2 删除 $scopedSlots
属性,并通过 $slots
将所有槽作为函数公开
在 Vue 2 中,作用域插槽(Scoped Slots)是通过 $scopedSlots
访问的。而在 Vue 3 中,所有的插槽都通过 $slots
访问,并且默认都是函数形式。
Vue2示例
<template>
<child-component v-slot:default="slotProps">
{{ slotProps.text }}
</child-component>
</template>
<script>
export default {
methods: {
render() {
return this.$scopedSlots.default({
text: 'Hello from scoped slot'
});
}
}
};
</script>
Vue3示例
在 Vue 3 中,你可以直接通过 $slots
访问所有插槽,并且它们默认都是函数形式。插槽默认是函数形式,不再需要区分 $scopedSlots
和普通插槽。
import { h } from 'vue';
export default {
setup(props, { slots }) {
return () => h('div', slots.default ? slots.default({ text: 'Hello from scoped slot' }) : '');
}
};
4.3 $listeners
被删除/合并到$attrs
在 Vue 2 中,$listeners
是一个包含所有非原生事件监听器的对象。而在 Vue 3 中,$listeners
被移除,其功能被合并到了 $attrs
中。
Vue示例
<template>
<child-component @custom-event="handleCustomEvent" />
</template>
<script>
export default {
methods: {
handleCustomEvent() {
console.log('Custom event triggered');
}
},
mounted() {
console.log(this.$listeners); // 包含所有自定义事件监听器
}
};
</script>
Vue3示例
在 Vue 3 中,自定义事件监听器现在也包含在 $attrs
中。
<template>
<child-component v-bind="$attrs" />
</template>
<script setup>
import { useAttrs } from 'vue';
const attrs = useAttrs();
console.log(attrs); // 包含所有自定义事件监听器和其他属性
</script>
4.4 $attrs
包括class
和style
属性
在 Vue 3 中,$attrs
不仅包含自定义属性,还包括 class
和 style
属性。这使得 $attrs
更加全面,可以处理更多的属性传递。
Vue2示例
<template>
<child-component class="custom-class" style="color: red;" />
</template>
<script>
export default {
mounted() {
console.log(this.$attrs); // 不包括 class 和 style
}
};
</script>
Vue3示例
<template>
<child-component v-bind="$attrs" />
</template>
<script setup>
import { useAttrs } from 'vue';
const attrs = useAttrs();
console.log(attrs); // 包括 class 和 style
</script>
5. 总结
更多详细内容参考👇 👇 👇
Breaking Changes | Vue 3 Migration Guide
更多相关内容👇 👇 👇
Vue3入门到实战_借来一夜星光的博客-CSDN博客