Vue3基础语法

基本结构

组件定义在setup方法中完成,setupbeforeCreate之前执行一次,setupthisundefined

一般情况下setup函数不能是async,否则组件无法正常渲染


<template>  
  <h2>{{name}}</h2>  
  <h2>{{age}}</h2>  
  <button @click="sayHello">sayHello</button>  
</template>  
  
<script>  
export default {  
  name: 'App',  
  setup() {  
    // 取代Vue2中的data部分
    let name = 'zhangsan'  
    let age = 18  

    // 取代Vue2中的methods部分
    function sayHello() {  
      alert('hello')  
    }  

    // 将data和methods返回,共模板部分使用
    return {  
      name,  
      age,  
      sayHello  
    }  
  }  
}  
</script>

setup函数的参数

setup(props, context) {
}

props组件已声明接收的props属性 context.attrs组件未声明接收的props属性 context.slots组件接收的插槽内容 context.emit可触发自定义事件,相当于Vue2的this.$emit

响应式定义

ref响应式

底层原理是Object.defineProperty,对对象、数组处理时调用reactive

<script>  
import { ref } from 'vue'  
export default {  
  name: 'App',  
  setup() {  
    let name = ref('zhangsan')  
    let age = ref(18)  
    let job = ref({  
      name: 'UI',  
      salary: 3000  
    })  
  
    function update() {  
      name.value = 'lisi'  
      age.value = 20  
      job.value.name = 'Java'  
      job.value.salary = 8000  
    }  
  
    return {  
      name,  
      age,  
      job,  
      update  
    }  
  }  
}  
</script>

reactive响应式

无法定义基本数据类型

底层原理是ProxyReflect

<script>  
import { reactive } from 'vue'  
export default {  
  name: 'App',  
  setup() {  
    let person = reactive({  
      name: 'zhangsan',  
      age: 18,  
      job: 'UI',  
      salary: 8000,  
      hobby: ['dogs', 'cats']  
    })  
  
    function update() {  
      person.name = 'lisi'  
      person.age = 20  
      person.job = 'Java'  
      person.salary = 9000  
      person.hobby[0] = 'ducks'  
    }  
  
    return {  
      person,  
      update  
    }  
  }  
}  
</script>

计算函数

<template>
    <input type="text" v-model="person.firstName">
    <input type="text" v-model="person.lastName">
    <input type="text" v-model="person.fullName">
</template>
import {reactive, computed} from 'vue'
setup() {
    let person = reactive({
        firstName: '',
        lastName: ''
    })

    // 简单写法
    person.fullName = computed(() => {
        return person.firstName + '-' + person.lastNaame
    })

    // 详细写法
    person.fullName = computed({
        get() {
            return person.firstName + '-' + person.lastName
        },

        set(value) {
            const nameArr = value.split('-')
            person.firstName = nameArr[0]
            person.lastName = nameArr[1]
        }
    })
}

监视函数

watch

import {ref, reactive, watch} from 'vue'
setup() {
    let sum = ref(0)
    let msg = ref('你好')
    let person = reactive({
        name: 'zhangsan'
        age: 18
    })
    
    // 监视一个基础类型ref
    watch(sum, (newValue, oldVaule) => {
    }, {immediate: true})
    
    // 监视多个基础类型ref
    watch([sum, msg], (newValue, oldValue) => {
    })
    
    // 监视一个对象ref
    watch(person, (newValue, oldValue) => {
    }, {deep: true})
    
    // 监视一个对象ref,转换为监视reactive
    watch(person.value, (newValue, oldValue) => {
    })
    
    // 监视一个reactive,无法正确获取oldValue且强制开启深度监视
    watch(person, (newValue, oldValue) => {
    })
    
    // 监视一个reactive的某一属性
    watch(() => person.name, (newValue, oldValue) => {
    })
    
    // 监视一个reactive的多个属性
    watch([() => person.name, () => person.age], (newValue, oldValue) => {
    })
    
    // 监视一个reactive的某一对象属性
    watch(() => person.job, (newValue, oldValue) => {
    }, {deep: true})
}

watchEffect

类似computed,但不关注返回值,只关注相关数据改变时执行某些操作

import {ref, reactive, watchEffect} from 'vue'
setup() {
    let sum = ref(0)
    let msg = ref('hello')
    let person = reactive({
        name: 'zhangsan',
        age: 18,
        job: {
            salary: 20
        }
    })
    
    watchEffect(() => {
        // 访问到的变量变化,函数会被调用
        const x1 = sum.value
        const x2 = person.job.salary
    })
}

生命周期钩子

  • create 创建模型对象
  • mount 挂载页面DOM
  • update 更新页面DOM
  • unmount 卸载页面DOM
  • activateddeactivated 组件是否显示

unmount取代Vue2的destroy

组合API统一在原钩子函数名前面加on,没有onBeforeCreateonCreated,可在setup中直接实现

import {onBeforeMount} from 'vue'
setup() {
    onBeforeMount(() => {
    })
}

hook模块化

拆分模块使用

/src/hooks/usePoint.js

import {reactive, onMounted, onBeforeUnmount} from 'vue'
export default function() {
    let point = reactive({
        x: 0,
        y: 0
    })

    function savePoint(event) {
        point.x = event.pageX
        point.y = event.pageY
    }

    onMounted(() => {
        window.addEventListener('click', savePoint)
    })

    onBeforeUnmount(() => {
        window.removeEventListener('click', savePoint)
    })

    return point
}

引用hook

import usePoint from '@/src/hooks/usePoint'
export default {
    name: 'Test',
    setup() {
        const point = usePoint()
        return {
            point
        }
    }
}

一些组合式API

toRef

将响应式对象中的某个属性单独提供给外部时使用

import {reactive, toRef, toRefs} from 'vue'
setup() {
    let person = reactive({
        name: 'zhangsan',
        age: 18,
        job: {
            salary: 20
        }
    })

    return {
        personName: toRef(person, 'name')
        ...toRefs(person)
    }
}

customRef

自定义ref响应式,显式控制模板与数据的绑定


<template>
    <input type="text" v-model="keyword">
    <h3>{{keyWord}}</h3>
</template>

import {customRef} from 'vue'
export default {
    name: 'App',
    setup() {
        function myRef(value, delay) {
            let timer
            return customRef((track, trigger) => {
                return {
                    get() {
                        // 通知Vue追踪双向绑定的数据变化
                        track()
                        return value
                    },

                    set(newValue) {
                        // 增加防抖功能
                        clearTimeout(timer)
                        timer = setTimeout(() => {
                            value = newValue
                            // 通知Vue重新解析模板
                            trigger()
                        }, delay)
                    }
                }
            })
        }
        let keyword = myRef('hello', 500)
        return {keyword}
    }
}

provide与inject

let car = reactive({name: 'a', price: 40})
provide('car', car)

后代组件获取数据

const car = inject('car')

其它

  • shallowReactive 只处理对象最外层属性的响应式
  • shallowRef 只处理基本数据类型的响应式,对象替换也可以处理
  • readonly 让一个响应式的数据变为只读
  • shallowReadonly 让一个响应式的数据变为浅只读
  • toRaw 将一个reactive生成的响应式对象转为普通对象
  • markRaw 标记一个对象不处理响应式
  • isRef 检查是否是ref方法生成的对象
  • isReactive 检查是否是reactive方法生成的对象
  • isReadonly 检查是否是readonly方法生成的对象
  • isProxy 检查是否是reactive方法或readonly方法生成的对象

组件

Fragment组件

template中无需只有一个根标签,当有多个根标签时,会将多个根标签包含在一个虚拟元素中

Teleport组件

将页面结构传送到其它地方,可使用CSS选择器指定

<template>
    <div>
        <button @click="isShow = true">click</button>
        <teleport to="body">
            <div></div>
        </teleport>
    </div>
</template>

Suspense组件

动态引入组件

import {defineAsyncComponent} from 'vue'
const Child = defineAsyncComponent(() => import('./components/Child'))
export default {
    name: 'App',
    components: {Child}
}

使用Suspense组件

<template>
    <div>
        <Suspense>
            <template v-slot:default>
                <Child/>
            </template>
            
            <template v-slot:fallback>
                <h3>稍等</h3>
            </template>
        </Suspense>
    </div>
</template>

此时,Child组件的setup方法可以await一个promisesetup方法可以标记为async,组件可以在异步调用完成后渲染出来

一些变化

  • 全局API转移,从Vue转移到appVue.prototype转移到app.config.globalProperties
  • 子组件中emits属性声明自定义事件,没声明的为原生事件,原生事件不需要再加native