总结一下vue3.0带来的部分新特性
- setup()
- ref()
- reactive()
- isRef()
- toRefs()
- computed()
- watch()
- LifeCycle Hooks(新的生命周期)
- Template refs
- globalProperties
- Suspense
vue2与vue3的对比
- 对TypeScript支持不友好(所有属性都放在了this对象上,难以推到组件的数据类型)
- 大量的Api挂载在Vue对象的原型上,难以实现TreeShaking
- 架构层面对跨平台dom渲染开发支持不友好
- Composition Api 受ReactHock启发
- 更方便的支持了jsx
- vue3的Template支持多个根标签, vue2不支持
- 对虚拟DOM进行了重写,对模板的编译进行了优化操作
一、setup函数
setup()函数是vue3中,专门为为组件提供的新属性。它为我们使用vue3的Composition Api新特性提供了统一的入口,setup函数会在beforecreate之后、create之前执行,vue3也是取消了这两个钩子,统一用setup代替,该函数相当于一个生命周期函数,vue中过去的data,methods,watch等全部都用对应的新增api写在setup()函数中
1 2 3 4 5 6 7 8 9 10 11 12
| setup(props, context) { context.attrs context.slots context.parent context.root comtext.emit comtext.refs
return {
} }
|
- props: 用来接收props数据
- context用来定义上下文,上下文对象中包含了一些有用的属性,这些属性在vue2.x中需要通过this才能访问到,在setup函数中无法访问到this,是个undefinded
- 返回值:return{}, 返回响应式数据,模板中需要使用的函数
二、reactive函数
reactive()函数接收一个普通对象,返回一个响应式的数据对象,想要使用创建的响应式数据也很简单,创建出来之后,在setup()中return出去,直接在template中调用即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <template> {{ name }} </template> <script lang="tx"> import { defineComponent, reactive, ref, toRefs } from 'vue'; export default defineComponent({ setup(props, context) { let state = reactive({ name: 'test' }); return state } }) </script>
|
三、ref()函数
ref()函数用来根据给定的值创建一个响应式的数据对象,ref()函数调用的返回值是一个对象,这个对象只包含了一个value属性,只在setup()函数内部访问ref函数需要加.value
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <template class="main"> <div> {{ count }} //10 </div> </template> <script lang="ts"> import { defineComponent, ref } from 'vue'; export default defineComponent({ setup() { const count = ref<number>(10); console.log(count.value); return { count } } }) </script>
|
在reactive对象中访问ref创建的创建的响应式数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <template> <div class="main"> {{ count }} -{{ t }} //10 -100 </div> </template> <script lang="ts"> import {defineComponent, reactive, ref, toRefs} from 'vue'; export default defineComponent({ setup() { const count = ref<number>(10); const obj = reactive({ t: 100, count }) console.log(obj.count); return { ...toRefs(obj) } } }) </script>
|
四、isRef()函数
isRef()用来判断某个值是否为ref()创建出来的对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <script lang="ts"> import { defineComponent, isRef, ref } from 'vue'; export default defineComponent({ setup(props, context) { let state = reactive({ name: 'test' }); const age = ref(18); return { ...toRefs(state), age } } }) </script>
|
五、toRefs()函数
toRefs()函数可以将reactive创建出来的响应式对象,转换为普通对象,只不过这个对象上的每一个节点,都是ref()类型的响应式数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| <template> <div class="main"> {{ name }} // test {{ age }} // 18 </div> </template> <script> import { defineComponent, reactive, ref, toRefs } from 'vue'; export default defineComponent({ setup(props, context) { let state = reactive({ name: 'test' }); const age = ref(18); return { ...toRefs(states), age } } }) </script>
|
六、computed()
该函数用来创造计算属性,和过去一样,它的返回值是一个ref对象。里面可以传方法,或者一个对象,对象中包含set()、get()方法
6.1 创建只读的计算属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <scripr lang="ts"> import {computed, defineComponent, ref} from 'vue'; expoet default defineComponent({ setup(props, context) { const age = ref(8)
const readOnlyAge = computed(() => age.value++)
return { age, readOnlyAge } } }) </scripr>
|
6.2 通过set()、get()方法创建一个可读可写的计算属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <script> import {computed, defineComponent, ref} from 'vue'; export default defineComponent({ setup(props, context) { const age = ref<number>(18) const computedAge = computed({ get: () => age.value + 1, set: () => age.value + value }) // 为计算属性赋值的操作,会触发set函数,触发set函数后,age的值会被更新 age.value = 100; return { age, computedAge } } }) </script>
|
七、watch()函数
whatch函数用来监听特定的数据源,并在回调函数中执行副作用。默认情况是懒执行的,也就是说再侦听的数据源变更时才执行回调。
7.1监听用reactive声明的数据源
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| <script> import { computed, definComponent, reactive, toRefs, watch } from 'vue'; export default defineComponent({ setup(props, context) { const state = reactive<Person>({ name: 'vue', age: 10 });
watch( () => state.age, (age, preAge) => { console.log(age); console.log(preAge); } ) state.age = 10 return { ...toRefs(state) } } }) </script>
|
7.2监听用ref声明的数据源
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <script lang="ts"> import { defineComponent, ref, watch } from 'vue'; interface Person { name: string, age: number } export default defineComponent({ setup(props, context) { const age = ref<number>(10);
watch(age, () => console.log(age.value));
age.value = 100 return { age } } }) </script>
|
7.3同时监听多个值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| <script lang="ts"> import { watch, computed, defineComponent, reactive, toRefs } from 'vue'; interface Person { name: string, age: number } export default defineCompontent({ setup(props, context) { const state = reactive<Person>({ name: 'vue', age: 10 });
watch( [() => state.age, () => state.name], ([newName, newAge], [oldName, oldAge]) => { console.log(newName); console.log(newAge);
console.log(oldName); console.log(oldAge); } ) state.age = 100 state.name = 'vue3' return { ...toRefs(state) } } }) </script>
|
7.4 stop停止监听
在setup()函数内创建的watch监视,会在当前组件被销毁的时候自动停止。如果想要明确的停止某个监视,可以调用watch()函数的返回值即可,语法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| <script lang="ts"> import { set } 'lodash'; import { computed, defineComponent, reactive, toRefs, watch } from 'vue'; interface Person { name: string, age: number } export default defineComponent({ setup(props, context) { const state = reactive<Person>({ name: 'vue', age: 10 });
const stop = watch( [() => stage.age, () => state.name], ([newName, newAge],[oldName, oldAge]) => { console.log(newName); console.log(newAge);
console.log(oldName); console.log(oldAge); } ) state.age = 100; state.name = 'vue3'
setTimeOut(() => { stop() state.age = 1000 state.name = 'vue3-' }, 1000)
return { ...toRefs(state) } } }) </script>
|
八、lifeCycle Hooks(新的生命后期)
新版的生命周期函数,可以按需导入到组件中,且只能在setup()函数中使用,但是也可以在srtup外定义,在setup中使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| <script lang="ts"> import { set } from 'lodash'; import { defineComponent, onBeforeMount, onBeforeUnmount, onBeforeUpdate, onErrorCaptured, onMounted, onUnMounted, onUpdate } from 'vue' export default defineComponent({ setup(props, context) { onBeforeMount(()=> { console.log('beformounted!') }) onMounted(() => { console.log('mounted!') })
onBeforeUpdate(()=> { console.log('beforupdated!') }) onUpdated(() => { console.log('updated!') })
onBeforeUnmount(()=> { console.log('beforunmounted!') }) onUnmounted(() => { console.log('unmounted!') })
onErrorCaptured(()=> { console.log('errorCaptured!') })
return {} } }) </script>
|
九、Template refs
通过refs来获取真实的dom元素,这个和react的用法一样,为了获得对模板内元素或组件实例的引用,我们可以像往常一样在setup()中声明一个ref并返回它
- 还是跟往常一样,在html中写入ref的名称
- 在setup中定义一个ref
- setup中返回ref的实例
- onMounted中可以得到ref的RefImpl的对象,通过.value
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <template> <div class="main" ref="elmRefs"> <span>1111</span> </div> </template> <script lang="ts"> import { set } from 'lodash'; import { defineComponent, onMounted, ref } from 'vue'; export default defineComponent({ setup(props, context) { const elmRefs = ref<null | HTMLElement>(null); onMounted(() => { console.log(elmRefs.value); }) return { elmRefs } } }) </script>
|
十、vue的全局配置
通过vue实例上config来配置,包含Vue应用程序全局配置的对象。您可以在挂载应用之前修改下面列出的属性
1 2 3
| const app = Vue.createApp({})
app.config = {...}
|
为组件渲染功能和观察程序期间的未捕获错误分配处理程序。错误和应用程序实例将调用处理程序
1
| app.config.errorHandle = (err, vm, info) => {}
|
可以在应用程序内的任何组件实例中访问的全局属性,组件的属性将具有优先权。这可以代替Vue2.X Vue.prototype扩展:
1 2
| const app = Vue.createApp({}) app.config.globalProperties.$http = 'xxxxxxxxxs'
|
可以在组件中通过getCurrentInstance()来获取全局globalProperties中配置的信息,getCurrentInstance方法获取当前组件的实例,然后通过ctx属性获得当前上下文,这样我们就能在setup中使用router和vuex,通过这个属性我们就可以操作变量、全局属性、组件属性等等
1 2 3 4
| setup() { const { ctx } = getCurrentInstance(); ctx.$http }
|
十一、Suspense组件
在介绍Vue的Suspense组件之前,我们又必要先了解下React的Suspense组件,因为他们的功能类似。
React.lazy接受一个函数,这个函数需要动态调用import()。它必须返回一个Promise,该Promise需要resolve一个default export的React组件
1 2 3 4 5 6 7 8 9 10 11
| import React, { Suspense } from 'react'; const myComponent = React.lazy(() => import('./Component')); function myComponent() { return ( <div> <Suspense fallback={<div>Loading...</div>}> <myComponent /> </Suspense> </div> ); }
|
Vue3也新增了类似功能的defineAsyncComponent函数,处理动态引入(的组件)。defineAsyncComponent可以接受返回承诺的工厂函数。当您从服务器检索到组件定义时,应该调用Promise的解析回调。您还可以调用reject(reason)来指示负载已经失败
1 2 3 4 5 6 7
| import { defineAsyncComponent } from 'vue'
const AsyncComp = defineAsyncComponent(() => import('./components/AsyncComponent.vue') )
app.component('async-component', AsyncComp)
|
Vue3也新增了Suspense组件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <template> </template> <script lang="ts"> import { defineComponent, defineAsyncComponent } from "vue"; const MyComponent = defineAsyncComponent(() => import('./Component'));
export default defineComponent({ components: { MyComponent }, setup() { return {} } }) </script>
|
十二、vue3.x完整组件模板结构
一个完整的vue3.x完整组件模板结构包含了:组件名称、props、components、setup(hooks、computed、watch、methods等)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
| <template> <div class="mine" ref="elmRefs"> <span>{{ name }}</span> <br> <span>{{ count }}</span> <div> <button @click="handleClick">测试按钮</button> </div>
<ul> <li></li> </ul> </div> </template> <script lang="ts"> import { computed, defineComponent, getCurrentInstance, onMounted, PropType, reactive, ref, toRefs } from "vue" ;
interface IState { count: 0, name: String, list: Array<object> }
export default defineComponent({ name: 'demo', props: { name: { type: String as PropType<null | ''>, default: 'vue3.x' }, list: { type: Array as PropType<object[]>, default: () => [] } }, components: { }, emits:["emits-name"], setup (props, context) { console.log(props.name) console.log(props.list)
const state = reactive<IState>({ name: 'vue 3.0组件', count: 0, list: [ { name: 'vue', id: 1 }, { name: 'vuex', id: 2 } ] })
const a = computed(() => state.name)
onMounted(() => {
})
function handleClick() { state.count++ context.emit('emits-name', state.count) }
reutrn { ...toRefs(state), handleClick } } }); </script>
|
vue3的生态
- 官网
- 源码
- vite 构建器
- 脚手架
- vue-route-next
- vuex4.0
UI组件库
- vant2.x
- Ant Design of Vue 2.x
- element-plus