让你30分钟快速掌握vue 3(推荐阅读)

作者:撒点料儿

https://juejin.im/post/6887359442354962445

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

  1. setup()

  2. ref()

  3. reactive()

  4. isRef()

  5. toRefs()

  6. computed()

  7. watch()

  8. LifeCycle Hooks(新的生命周期)

    扫描二维码关注公众号,回复: 12431788 查看本文章
  9. Template refs

  10. globalProperties

  11. 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.emit
    
    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 创建的响应式数据 (通过reactive 来获取ref 的值时,不需要使用.value属性)

<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()方法创建一个可读可写的计算属性

  为计算属性赋值的操作,会触发 set 函数, 触发 set 函数后,age 的值会被更新

<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并返回它

  1. 还是跟往常一样,在 html 中写入 ref 的名称

  2. 在steup 中定义一个 ref

  3. steup 中返回 ref的实例

  4. 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>

vue 3 的生态

  • 官网[1]

  • 源码[2]

  • vite构建器[3]

  • 脚手架:[4]https://cli.vuejs.org/

  • vue-router-next[5]

  • vuex4.0[6]

UI 组件库

  • vant2.x[7]

  • Ant Design of Vue 2.x[8]

  • element-plus[9]

参考资料

[1]

官网: https://v3.vuejs.org/

[2]

源码: https://github.com/vuejs/vue-next

[3]

vite构建器: https://github.com/vitejs/vite

[4]

脚手架:: https://cli.vuejs.org/

[5]

vue-router-next: https://github.com/vuejs/vue-router-next

[6]

vuex4.0: https://github.com/vuejs/vuex/tree/4.0

[7]

vant2.x: https://vant-contrib.gitee.io/vant/next/#/zh-CN/

[8]

Ant Design of Vue 2.x: https://2x.antdv.com/docs/vue/introduce-cn/

[9]

element-plus: https://element-plus.org/#/zh-CN

The End

如果你觉得这篇内容对你挺有启发,我想请你帮我三个小忙:

1、点个 「在看」,让更多的人也能看到这篇内容

2、关注官网 https://caibaojian.com.cn,让我们成为长期关系

3、关注公众号「前端开发博客」,公众号后台回复 「加群」 ,加入我们一起学习并送你精心整理的前端资源。

猜你喜欢

转载自blog.csdn.net/hsany330/article/details/109328164