new Vue()
变为通过createApp函数进行创建;不过一些核心的功能比如virtualDOM更新算法和响应式系统无论如何都是会被打包的;这样带来的变化就是以前在全局配置的组件(Vue.component)、指令(Vue.directive)、混入(Vue.mixin)和插件(Vue.use)等变为直接挂载在实例上的方法;我们通过创建的实例来调用,带来的好处就是一个应用可以有多个Vue实例,不同实例之间的配置也不会相互影响:
const app = createApp(App) app.use(/* ... */) app.mixin(/* ... */) app.component(/* ... */) app.directive(/* ... */)
setup
生命周期函数,setup执行的时机是在beforeCreate
生命函数之前执行,因此在这个函数中是不能通过this
来获取实例的;同时为了命名的统一,将beforeDestroy
改名为beforeUnmount
,destroyed
改名为unmounted
,因此vue3有以下生命周期函数:
vue2.x vue3.x vue3.x-setup beforeCreate beforeCreate created created setup beforeMount beforeMount onBeforeMount mounted mounted onMounted beforeUpdate beforeUpdate onBeforeUpdate updated updated onUpdated beforeUnmount beforeUnmount onBeforeUnmount unmounted unmounted onUnmounted onErrorCaptured onRenderTracked onRenderTriggered reactive
来为JS对象创建响应式状态:
// !!! reactive函数只接收object和array等复杂数据类型。 import { reactive, toRefs } from "vue"; const user = reactive({ name: 'Vue2', age: 18, }); user.name = 'Vue3'
ref
:
import { ref } from "vue"; const num = ref(0); const str = ref(""); const male = ref(true); num.value++; console.log(num.value); str.value = "new val"; console.log(str.value); male.value = false; console.log(male.value); // ref返回的响应式对象是只包含一个名为value参数的RefImpl对象,在js中获取和修改都是通过它的value属性;但是在模板中被渲染时,自动展开内部的值,因此不需要在模板中追加.value。 <template> <div> <span>{{ count }}</span> <button @click="count ++">Increment count</button> </div> </template> <script> import { ref } from 'vue' export default { setup() { const count = ref(0) return { count } } } </script>
readonly
来创建一个只读的对象:
import { reactive, readonly } from "vue"; let book = reactive({ name: 'Learn Vue', year: 2020, title: 'Chapter one' }) const copy = readonly(book); //Set operation on key "name" failed: target is readonly. copy.name = "new copy";
computed函数
来进行计算属性,在vue3中将computed功能进行了抽离,它接受一个getter函数,并为getter返回的值创建了一个不可变的响应式ref对象:
const num = ref(0); const double = computed(() => num.value * 2); num.value++; // 2 console.log(double.value); // Warning: computed value is readonly double.value = 4
deep:true
:
const deepObj = reactive({ a: { b: { c: "hello", }, }, }); watch( () => deepObj, (val, old) => { // new hello new hello console.log(val.a.b.c, old.a.b.c); }, { deep: true } ); deepObj.a.b.c = "new hello";
Options API
(选项API),即官方定义好了写法:data、computed、methods,需要在哪里写就在哪里写,这样带来的问题就是随着功能增加,代码也越来复杂,我们看代码需要上下反复横跳:
组合api.png
ps: 上图中,一种颜色代表一个功能,我们可以看到Options API
的功能代码比较分散;Composition API
则可以将同一个功能的逻辑,组织在一个函数内部,利于维护。
Composition API
做的就是把同一功能的代码放到一起维护,这样我们需要维护一个功能点的时候,不用去关心其他的逻辑,只关注当前的功能;Composition API
通过setup
选项来组织代码:
export default { setup(props, context) {} };
我们看到这里它接收了两个参数props和context,props就是父组件传入的一些数据,context是一个上下文对象,是从2.x暴露出来的一些属性:
attrs slots emit ps: props的数据也需要通过toRefs解构,否则响应式数据会失效。
setup
中的功能进行提取分割成一个一个独立函数,每个函数还可以在不同的组件中进行逻辑复用:
export default { setup() { const { networkState } = useNetworkState(); const { user } = userDeatil(); const { list } = tableData(); return { networkState, user, list, }; }, }; function useNetworkState() {} function userDeatil() {} function tableData() {}
vue-fragments
库,用一个虚拟的fragment代替div;在React中,解决方法是通过的一个React.Fragment
标签创建一个虚拟元素;在Vue3中我们可以直接不需要根节点:
<template> <span>hello</span> <span>world</span> </template>
createPortal
函数来创建需要传送的节点;本来尤大大想起名叫Portal
,但是H5原生的Portal标签
也在计划中,虽然有一些安全问题,但是为了避免重名,因此改成Teleport
。to
和disabled
:
defineAsyncComponent
来进行显示的定义:
// 全局定义异步组件 //src/main.js import { defineAsyncComponent } from "vue"; const AsyncButton = defineAsyncComponent(() => import("./components/AsyncButton.vue") ); app.component("AsyncButton", AsyncButton); // 组件内定义异步组件 // src/views/Home.vue import { defineAsyncComponent } from "vue"; export default { components: { AsyncButton: defineAsyncComponent(() => import("../components/AsyncButton") ), }, };
default
默认,一个fallback
加载中的状态:
<template> <div> <button @click="showButton">展示异步组件</button> <template v-if="isShowButton"> <Suspense> <template #default> <AsyncButton></AsyncButton> </template> <template #fallback> <div>组件加载中...</div> </template> </Suspense> </template> </div> </template> <script> export default { setup() { const isShowButton = ref(false); function showButton() { isShowButton.value = true; } return { isShowButton, showButton, }; }, } </script>
非兼容的功能主要是一些和Vue2.x版本改动较大的语法,已经在Vue3上可能存在兼容问题了。
object
或者function
,但是我们知道在组件中如果data是object的话会出现数据互相影响,因为object是引用数据类型;function
类型,通过function
返回对象;同时Mixin
的合并行为也发生了改变,当mixin和基类中data合并时,会执行浅拷贝合并:
const Mixin = { data() { return { user: { name: 'Jack', id: 1, address: { prov: 2, city: 3, }, } } } } const Component = { mixins: [Mixin], data() { return { user: { id: 2, address: { prov: 4, }, } } } } // vue2结果: { id: 2, name: 'Jack', address: { prov: 4, city: 3 } } // vue3结果: user: { id: 2, address: { prov: 4, }, }
过滤器filter
来处理一些文本内容的展示:
<template> <div>{{ status | statusText }}</div> </template> <script> export default { props: { status: { type: Number, default: 1 } }, filters: { statusText(value){ if(value === 1){ return '订单未下单' } else if(value === 2){ return '订单待支付' } else if(value === 3){ return '订单已完成' } } } } </script>
计算属性computed
来进行代替。v-model
相当于绑定value
属性和input
事件,它本质也是一个语法糖:
<child-component v-model="msg"></child-component> <!-- 相当于 --> <child-component :value="msg" @input="msg=$event"></child-component>
.sync
修饰符,其本质也是语法糖,是在组件上绑定@update:propName
回调,语法更简洁:
<child-component :msg1.sync="msg1" :msg2.sync="msg2"> </child-component> <!-- 相当于 --> <child-component :msg1="msg1" @update:msg1="msg1=$event" :msg2="msg2" @update:msg2="msg2=$event"> </child-component>
v-model
和.sync
进行了功能的整合,抛弃了.sync,表示:多个双向绑定value值直接用多个v-model传就好了;同时也将v-model默认传的prop名称由value改成了modelValue:
<child-component v-model="msg"> </child-component> <!-- 相当于 --> <child-component :modelValue="msg" @update:modelValue="msg = $event"> </child-component>
argument
传递给v-model:
<child-component v-model.msg1="msg1" v-model.msg2="msg2"> </child-component> <!-- 相当于 --> <child-component :msg1="msg1" @update:msg1="msg1=$event" :msg2="msg2" @update:msg2="msg2=$event"> </child-component>
v-bind="object"
和一个相同的单独的属性,那么声明绑定的顺序决定了最后的结果(后者覆盖前者):
<!-- template --> <div id="red" v-bind="{ id: 'blue' }"></div> <!-- result --> <div id="blue"></div> <!-- template --> <div v-bind="{ id: 'blue' }" id="red"></div> <!-- result --> <div id="red"></div>
ref
属性,通过this.$refs
会得到一个数组:
<template <div v-for="item in list" :ref="setItemRef"></div> </template> <script> export default { data(){ list: [1, 2] }, mounted () { // [div, div] console.log(this.$refs.setItemRef) } } </script>
v-for
有更高的优先级,因此在vue2.x中做性能优化,有一个重要的点就是v-for和v-if不能放在同一个元素上。v-if
比v-for
有更高的优先级。因此下面的代码,在vue2.x中能正常运行,但是在vue3中v-if生效时并没有item
变量,因此会报错:
<template> <div v-for="item in list" v-if="item % 2 === 0" :key="item">{{ item }}</div> </template> <script> export default { data() { return { list: [1, 2, 3, 4, 5], }; }, }; </script>原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。