10分钟让你快速上手Vue3

经过了漫长地迭代,分钟Vue 3.0 终于在上 2020-09-18 发布了,让快带了翻天覆地的速上手变化,使用了 Typescript 进行了大规模的分钟重构,带来了 Composition API RFC 版本,让快类似 React Hook 一样的速上手写 Vue,可以自定义自己的分钟 hook ,让使用者更加的让快灵活,接下来总结一下 vue 3.0 带来的速上手部分新特性。

setup() ref() reactive() isRef() toRefs() computed() watch() LifeCycle Hooks(新的分钟生命周期) Template refs globalProperties Suspense

Vue2 与 Vue3 的对比

对 TypeScript 支持不友好(所有属性都放在了 this 对象上,难以推倒组件的让快数据类型) 大量的 API 挂载在 Vue 对象的原型上,难以实现 TreeShaking。速上手 架构层面对跨平台 dom 渲染开发支持不友好 CompositionAPI。分钟爱 ReactHook 启发 更方便的让快支持了 jsx Vue 3 的 Template 支持多个根标签,Vue 2 不支持 对虚拟 DOM 进行了重写、速上手对模板的编译进行了优化操作...

一、setup 函数

setup() 函数是 vue3 中,专门为组件提供的新属性。源码库它为我们使用 vue3 的 Composition API 新特性提供了统一的入口, setup 函数会在 beforeCreate 之后、created 之前执行, vue3 也是取消了这两个钩子,统一用 setup 代替, 该函数相当于一个生命周期函数,vue 中过去的 data,methods,watch 等全部都用对应的新增 api 写在 setup()函数中

setup(props, context) {      context.attrs     context.slots     context.parent     context.root     context.emit     context.refs     return {      }   }  props: 用来接收 props 数据 context 用来定义上下文, 上下文对象中包含了一些有用的属性,这些属性在 vue 2.x 中需要通过 this 才能访问到, 在 setup() 函数中无法访问到 this,是个 undefined 返回值: return { }, 返回响应式数据, 模版中需要使用的函数

二、reactive 函数

reactive() 函数接收一个普通对象,返回一个响应式的数据对象, 想要使用创建的响应式数据也很简单,创建出来之后,在 setup 中 return 出去,直接在 template 中调用即可

<template>   { { name}} // test <template> <script lang="ts"> 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

<template>     <div class="mine">         { { count}} // 10     </div> </template> <script lang="ts"> import {  defineComponent, ref } from vue; export default defineComponent({    setup() {      const count = ref<number>(10)     // 在js 中获取ref 中定义的值, 需要通过value属性     console.log(count.value);     return {         count     }    } }); </script> 

在 reactive 对象中访问 ref 创建的响应式数据

<template>     <div class="mine">         { { 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     })     // 通过reactive 来获取ref 的值时,不需要使用.value属性     console.log(obj.count);     return {         ...toRefs(obj)     }    } }); </script> 

四、香港云服务器isRef() 函数

isRef() 用来判断某个值是否为 ref() 创建出来的对象

<script lang="ts"> import {  defineComponent, isRef, ref } from vue; export default defineComponent({    setup(props, context) {      const name: string = vue     const age = ref<number>(18)     console.log(isRef(age)); // true     console.log(isRef(name)); // false     return {        age,       name     }   } }); </script> 

五、toRefs() 函数

toRefs() 函数可以将 reactive() 创建出来的响应式对象,转换为普通的对象,只不过,这个对象上的每个属性节点,都是 ref() 类型的响应式数据

<template>   <div class="mine">     { { name}} // test     { { age}} // 18   </div> </template> <script lang="ts"> import {  defineComponent, reactive, ref, toRefs } from vue; export default defineComponent({    setup(props, context) {      let state = reactive({        name: test     });     const age = ref(18)     return {        ...toRefs(state),       age     }   } }); </script> 

六、computed()

该函数用来创造计算属性,和过去一样,它返回的值是一个 ref 对象。里面可以传方法,或者一个对象,对象中包含 set()、get()方法

6.1 创建只读的计算属性

import {  computed, defineComponent, ref } from vue; export default defineComponent({    setup(props, context) {      const age = ref(18)     // 根据 age 的值,创建一个响应式的计算属性 readOnlyAge,它会根据依赖的 ref 自动计算并返回一个新的 ref     const readOnlyAge = computed(() => age.value++) // 19     return {        age,       readOnlyAge     }   } }); </script> 

6.2 通过 set()、get()方法创建一个可读可写的计算属性

<script lang="ts"> 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: value => age.value + value     })     // 为计算属性赋值的操作,会触发 set 函数, 触发 set 函数后,age 的云服务器提供商值会被更新     age.value = 100     return {        age,       computedAge     }   } }); </script> 

七、 watch() 函数

watch 函数用来侦听特定的数据源,并在回调函数中执行副作用。默认情况是懒执行的,也就是说仅在侦听的源数据变更时才执行回调。

7.1 监听用 reactive 声明的数据源

<script lang="ts"> 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 })     watch(       () => state.age,       (age, preAge) => {          console.log(age); // 100         console.log(preAge); // 10       }     )     // 修改age 时会触发watch 的回调, 打印变更前后的值     state.age = 100     return {        ...toRefs(state)     }   } }); </script> 

7.2 监听用 ref 声明的数据源

<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)); // 100     // 修改age 时会触发watch 的回调, 打印变更后的值     age.value = 100     return {        age     }   } }); </script> 

7.3 同时监听多个值

<script lang="ts"> 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 })     watch(       [() => state.age, () => state.name],       ([newName, newAge], [oldName, oldAge]) => {          console.log(newName);         console.log(newAge);         console.log(oldName);         console.log(oldAge);       }     )     // 修改age 时会触发watch 的回调, 打印变更前后的值, 此时需要注意, 更改其中一个值, 都会执行watch的回调     state.age = 100     state.name = vue3     return {        ...toRefs(state)     }   } }); </script> 

7.4 stop 停止监听

在 setup() 函数内创建的 watch 监视,会在当前组件被销毁的时候自动停止。如果想要明确地停止某个监视,可以调用 watch() 函数的返回值即可,语法如下:

<script lang="ts"> import {  set } from 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(       [() => state.age, () => state.name],       ([newName, newAge], [oldName, oldAge]) => {          console.log(newName);         console.log(newAge);         console.log(oldName);         console.log(oldAge);       }     )     // 修改age 时会触发watch 的回调, 打印变更前后的值, 此时需要注意, 更改其中一个值, 都会执行watch的回调     state.age = 100     state.name = vue3     setTimeout(()=> {        stop()       // 此时修改时, 不会触发watch 回调       state.age = 1000       state.name = vue3-     }, 1000) // 1秒之后讲取消watch的监听     return {        ...toRefs(state)     }   } }); </script> 

八、LifeCycle Hooks(新的生命后期)

新版的生命周期函数,可以按需导入到组件中,且只能在 setup() 函数中使用, 但是也可以在 setup 自定义, 在 setup 中使用

<script lang="ts"> import {  set } from lodash; import {  defineComponent, onBeforeMount, onBeforeUnmount, onBeforeUpdate, onErrorCaptured, onMounted, onUnmounted, onUpdated } 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 的名称 在steup 中定义一个 ref steup 中返回 ref的实例 onMounted 中可以得到 ref的RefImpl的对象, 通过.value 获取真实dom <template>   <!--第一步:还是跟往常一样,在 html 中写入 ref 的名称-->   <div class="mine" 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) {      // 获取真实dom     const elmRefs = ref<null | HTMLElement>(null);     onMounted (() => {        console.log(elmRefs.value); // 得到一个 RefImpl 的对象, 通过 .value 访问到数据     })     return {        elmRefs     }   } }); </script> 

十、vue 的全局配置

通过 vue 实例上 config 的配置,包含 Vue 应用程序全局配置的对象。您可以在挂载应用程序之前修改下面列出的属性:

const app = Vue.createApp({ })  app.config = { ...} 

为组件渲染功能和观察程序期间的未捕获错误分配处理程序。错误和应用程序实例将调用处理程序

app.config.errorHandler = (err, vm, info) => { } 

可以在应用程序内的任何组件实例中访问的全局属性,组件的属性将具有优先权。这可以代替 Vue 2.xVue.prototype 扩展:

const app = Vue.createApp({ })  app.config.globalProperties.$http = xxxxxxxxs 

可以在组件用通过 getCurrentInstance() 来获取全局 globalProperties 中配置的信息,getCurrentInstance 方法获取当前组件的实例,然后通过 ctx 属性获得当前上下文,这样我们就能在 setup 中使用 router 和 vuex, 通过这个属性我们就可以操作变量、全局属性、组件属性等等

setup( ) {    const {  ctx } = getCurrentInstance();   ctx.$http } 

十一、Suspense 组件

在开始介绍 Vue 的 Suspense 组件之前,我们有必要先了解一下 React 的 Suspense 组件,因为他们的功能类似。

React.lazy 接受一个函数,这个函数需要动态调用 import()。它必须返回一个 Promise,该 Promise 需要 resolve 一个 default export 的 React 组件。

import React, {  Suspense } from react; const myComponent = React.lazy(() => import(./Component)); function MyComponent() {    return (     <div>       <Suspense fallback={ <div>Loading...</div>}>         <myComponent />       </Suspense>     </div>   ); } 

Vue3 也新增了 React.lazy 类似功能的 defineAsyncComponent 函数,处理动态引入(的组件)。defineAsyncComponent 可以接受返回承诺的工厂函数。当您从服务器检索到组件定义时,应该调用 Promise 的解析回调。您还可以调用 reject(reason)来指示负载已经失败

import {  defineAsyncComponent } from vue const AsyncComp = defineAsyncComponent(() =>   import(./components/AsyncComponent.vue) ) app.component(async-component, AsyncComp) 

Vue3 也新增了 Suspense 组件:

<template>   <Suspense>     <template #default>       <my-component />     </template>     <template #fallback>       Loading ...     </template>   </Suspense> </template> <script lang=ts>  import {  defineComponent, defineAsyncComponent } from "vue";  const MyComponent = defineAsyncComponent(() => import(./Component)); export default defineComponent({     components: {       MyComponent    },    setup() {       return { }    } }) </script> 

十二、vue 3.x 完整组件模版结构

一个完成的 vue 3.x 完整组件模版结构包含了:组件名称、 props、components、setup(hooks、computed、watch、methods 等)

<template>   <div class="mine" ref="elmRefs">     <span>{ { name}}</span>     <br>     <span>{ { count}}</span>     <div>       <button @click="handleClick">测试按钮</button>     </div>     <ul>       <li v-for="item in list" :key="item.id">{ { item.name}}</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: {      /// TODO 组件注册   },   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)     }     return {        ...toRefs(state),       handleClick     }   } }); </script> <template>   <div class="mine" ref="elmRefs">     <span>{ { name}}</span>     <br>     <span>{ { count}}</span>     <div>       <button @click="handleClick">测试按钮</button>     </div>     <ul>       <li v-for="item in list" :key="item.id">{ { item.name}}</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: {      /// TODO 组件注册   },   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)     }     return {        ...toRefs(state),       handleClick     }   } }); </script> 

vue 3 的生态

官网 源码 vite 构建器脚手架:https://cli.vuejs.org/ vue-router-next vuex4.0

UI 组件库

vant2.x Ant Design of Vue 2.x element-plus
滇ICP备2023000592号-31