Vue3.2不完全指北

317 阅读2分钟

一、Vue3优缺点

优点

  1. 使用Proxy代替Vue2的Object.defineProperty()监听数据实现双向绑定,性能提高1-2倍,(能解决Object.defineProperty()无法监听动态新增的对象属性,数组的索引和length属性,数组pushunshift等问题)。

  2. Typescript支持更友好,Vue3的源码是用ts编写的,又如propsemits可以跟ts合并使用。

  3. Vue3使用Composition API代替Vue2的Options API,解决Vue2中定义的属性方法分散,需要到处找方法的问题。Composition API更利于维护和阅读,一个功能的代码会都放在一起,尤其在项目大的时候,可以快速找到对应的代码。

图片1.png

  1. 源码体积更小(tree shaking),diff算法优化、SSR优化。

缺点

  1. 因为使用proxy所以不再支持IE11。

  2. 对于小项目而言Vue2的Options API更简洁方便。

生命周期变化

vue2.xvue3.x
beforeCreatesetup
createdsetup
beforeMountonBeforeMount
mountedonMounted
beforeUpdateonBeforeUpdate
updatedonUpdated
beforeDestoryonBeforeUnmount
destoryonUnmounted

二、<script setup>和普通<script>的区别和相应的代替用法

2.1 setup语法糖

vue3.2之前变量在setup里必须return

<script>
    setup(props) {
        return {
            title: 'SABER',
            name: 'saber',
        }
    }
</script>

vue3.2开始只需script加上setup属性即可使用

<script setup>
    import { ref } from 'vue'
    const title = ref('SABER')
    const name = ref('saber')
</script>

2.2 ref / reactive

<template>
    <p>{{ title }}</p>
    <p>{{ obj.title }}</p>
</template>
<script setup>
    import { ref, reactive } from 'vue'
    // ref声明基本数据类型:
    const title = ref('标题1')
    // 取值赋值
    title.value = '标题2'
    
    // reactive声明引用数据类型:
    const obj = reactive({
        title: '标题1',
        name: '名字1'
    })
    obj.title = '标题2'
</script>

注:使用reactive只能逐个修改值,要想使用如扩展运算符赋值,以下两种方式可实现:

<script setup>
    import { ref, reactive } from 'vue'
    const obj = {
        title: '标题2',
        name: '名字2'
    }
    
    // 方法一:使用ref创建引用数据类型
    const title = ref({
        title: '标题1',
        name: '名字1'
    })
    title.value = {...obj}
    
    // 方法二:使用reactive
    const title = reactive({
        data: {
            title: '标题1',
            name: '名字1'
        }
    })
   title.data = {...obj}
</script>

2.3 <style> v-bind的使用

<template>
    <button :style="{color: _color}">
        颜色是: {{ _color }}
    </button>
</template>

<script setup lang="ts">
    import { ref } from 'vue'
    const _color = ref<string>('#ff0000')
</script>

<style lang="scss" scoped>
    .color {
        color: v-bind(_color);
    }
</style>

2.4 组件的使用

直接import引用,无需components注册,自动以文件名作为组件名。但是无法自定义组件名,要想自定义组件名可以:

方法一:另写一个普通script

<script lang="ts">
    export default { name: "ChildName" }
</script>

方法二:使用插件vite-plugin-vue-setup-extend,则无需写两个script

<script setup lang="ts" name="ChildName">
    代码...
</script>

2.5 props的使用

vue3.2开始defineProps无需引入,直接使用

①运行时声明;②类型声明(Typescript支持)

2.5.1 运行时声明:

<script setup lang="ts">
    const props = defineProps({
        name: String,
        classify: {
            type: Number,
            default: 0
        }
    })
</script>

2.5.2 类型声明:

<script setup lang="ts">
    const props = defineProps<{
        name?: string,
        classify?: number
    }>()
</script>

要想设置默认值,可以使用辅助函数withDefaults(vue3.2)

<script setup lang="ts">
    const props = withDefaults(defineProps<{
        name?: string,
        classify?: number
    }>(), {
        name: 'Saber',
        classify: 0
    })
</script>

2.6 emits的使用

vue3.2开始defineEmits无需引入,直接使用
①运行时声明;②类型声明。

2.6.1 运行时声明:

<script setup lang="ts">
    const emits = defineEmits(['func1', 'func2'])
</script>

2.6.2 类型声明:

<script setup lang="ts">
    const emits = defineEmits<{
        (e: 'func1', data: string): void,
        (e: 'func2', data?: number): void,
    }>()
</script>

2.7 useAttrs()

接受props里没获取到的其他数据

<template>
    <Child :msg1="msg1" :msg2="msg2" title="子组件" />
</template>

<script setup lang="ts">
    import { useAttrs } from 'vue'
    const props = defineProps('msg1')
    const attrs = useAttrs()
    // { msg2: 'msg2', title: '子组件' }
</<script>

2.8 defineExpose暴露属性

<script setup lang="ts">
    defineExpose({ a, b })
</script>

2.9 provide / inject 多级传递

// parent.vue
<script setup lang="ts">
    import { provide } from 'vue'
    provide('name', '老脑')
</script>

// child.vue
<script setup lang="ts">
    import { inject } from 'vue'
    const name = inject('name')  // 老脑
</script>

2.10 watch / computed / onMounted 等等

<script setup lang="ts">
    const total = computed(() => count.value++)
    
    watch(() => state.count, (newVal) => {
        console.log(newVal, '新数据')
    })
    
    onMounted(() => {
        // todo
    })
</script>

2.11 Web Component

借助defineCustomElementAPI将Vue组件转换成Web组件

三、好用的插件

3.1 unplugin-auto-import

自动引入refonMounted等api,无需手动声明引入

3.2 @vitejs/plugin-vue-jsx

在vite + vue3.x中使用jsx开发

3.3 vite-plugin-pages

自动生成路由信息,无需配置

3.4 vite-plugin-svg-icons

快速实现svg图标

3.5 vite-plugin-vue-setup-extend

自定义组件名

四、RFC提案

4.1 烦人的.value

不再需要.value取值和赋值

<script setup lang="ts">
    // 现在
    const loading = ref(true)
    loading.value = false
    
    // 未来
    const loading = $(ref(true))
    loading = false
    // 还原成.value
    const loading_value = $$(loading)
</script>

4.2 $computed

<script setup lang="ts">
    // 现在
    const _num = computed(() => num.value++)
    
    // 未来
    const _num = $(computed(() => num++))
</script>

4.3 $fromRefs

配合toRefs使用

<script setup lang="ts">
    const state = reative({
        x: 0,
        y: 0
    })
    // 现在
    const {x, y} = toRefs(state)
    // 赋值
    // x.value = 1;  y.value == 2;
    
    // 未来
    const {x, y} = $formRefs(toRefs(state))
    // 赋值
    // x = 1;  y = 2;
</script>

4.4 props解构

<script setup lang="ts">
    // 现在
    const props = defineProps({
        title: {
            type: String,
            default: '123'
        }
    })
    // 取值:props.title
    
    // 未来
    const { title = '123' } = $(defineProps({ title: String }))
</script>