vue3学习笔记

本文详细介绍了Vue3的新特性,包括通过cdn或npm安装Vue3,重点讲解了组合式API的setup函数及其props、context的使用,如toRefs、toRef和watch等。还探讨了带ref的响应式变量、setup内的生命周期钩子函数、组合式函数的定义和应用,以及依赖注入的provide和inject方法。此外,文章还展示了如何在选项式API中使用组合式函数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一. 安装

1. cdn

<script src="https://unpkg.com/vue@next"></script>

 2. npm

 脚手架vite

npm init vite hello-vue3 -- --template vue # 或 yarn create vite hello-vue3 --template vue

脚手架vue-cli

npm install -g @vue/cli # 或 yarn global add @vue/cli
vue create hello-vue3
# 选择 vue 3 preset

我选的这个,之前安装过vue-cli,需要先卸载再安装,要不然会报这个:

 后面就是一些配置,记得选vue3项目啊!默认是vue2的,我搞了半天发现不对劲,真是一口老血。。。

*默认启动项目命令:npm run serve,不再是dev了

二. 新特性

1. 组合式API

官网上叙述了一堆,我的理解就是把处理某个逻辑的相关代码组合在一起的东西 

1) setup组件选项 

① 定义

组合式API的入口,一个接收props和context的函数,返回的所有内容都暴露给组件的其余部分

② 执行时间

组件创建之前(beforeCreated),所以setup里不能使用this,也不能获取data property(属性)、computed property或methods。

③ 示例

export default {
    props: {
        user: {
            type: String,
            required: true
        }
    },
    setup(props,context){
    
    }
}

④ props参数

它是响应式的,传入新的值时会被更新。不能使用es6解构,会消除响应性。

如需解构可用toRefs(解构全部)或toRef(解构单个,防止可选的prop没有值导致未被创建ref)

示例:

import { toRefs } from 'vue'

setup(props){
    const { title } = toRefs(props)
    console.log(title.value)
}
import { toRef } from 'vue'

setup(props){
    const title = toRef(props,'title')
    console.log(title.value)
}

⑤ context参数

一个普通js对象,可以解构。一些有用的值:

export default {
    setup(props,context){

        //Attribute:非响应式对象,等同于$attrs
        console.log(context.attrs)

        //插槽:非响应式对象,等同于$slots
        console.log(context.slots)

        //触发事件:方法,等同于$emit
        console.log(context.emit)

        //暴露公共property函数
        console.log(context.expose)
    }
}

⑥ 结合模板使用:如果setup返回一个对象,对象中的属性可以在模板中访问到:

<template>
  <div class="setup">
    <p>{{ title }}</p>
    <p>{{ title1 }}</p>
  </div>
</template>

<script>
import { toRef }  from 'vue'
export default {
  props: {
    title: {
      type: String,
      default: "title",
    },
  },
  setup(props) {
    let title = toRef(props, 'title')
    let title1 = title.value + 'aaaa'
    return {
        title1
    }
  },
};
</script>

<style>
</style>

⑦使用渲染函数&expose(搞得很像react哎

返回一个渲染函数,该函数可以直接使用同一作用于中声明的响应式状态。

不过我没搞懂返回了之后呢?怎么使用这个渲染函数?还是说怎么访问?有无大佬知道的能指点一下?

返回渲染函数之后,之前return的对象不能用了,需要使用expose,没搞懂为什么要定义一个方法再传给它。估计这个也很少用到吧。先放着。

示例:

<template>
  <div class="setup">
    <p>{{ title }}</p>
    <p>{{ title1 }}</p>
  </div>
</template>

<script>
import { toRef, h }  from 'vue'
export default {
  props: {
    title: {
      type: String,
      default: "title",
    },
  },
  setup(props) {
    let title = toRef(props, 'title')
    let title1 = title.value + 'aaaa'
    const increment = () => title.value

    expose({
      increment
    })
    return ()=> h('div',title)
  },
};
</script>

<style>
</style>

2)带ref的响应式变量

① 定义:接收参数并将其包裹在一个带有value property的对象中返回,任何可以使用该property访问或更改响应式变量的值。

示例:

import { ref } from 'vue'

const counter = ref(0) //counter: { value: 0 }

counter.value++ //counter: { value: 1 }

② 作用:为一个值/变量创建响应式引用,使其在任何地方起作用。虽然看起来有点多此一举,但是官网说是为了保持js中不同数据类型的行为统一。其实就是单独的data吧。。。

ps: reactive也能起到类似效果,但是只能接收对象类型,所以更推荐使用ref

3)setup内的生命周期钩子

① 函数名称:在vue原有的生命周期钩子函数名称前加on,如mounted => onMounted

 ps:这些函数只接收一个回调函数名称,传不了参数给回调函数

示例:

setup(props){
    const fn = ()=>{
        console.log(1)
    }
    onMounted(fn) //输出1
}

4)setup内的watch

① 侦听数据源:可以是返回值的getter函数,也可以直接是ref。

② 侦听多个数据源:第一个参数改为数组形式。

ps:如果在一个函数里同时改变这两个被侦听的数据源,侦听器只会执行一次。如果想要在这种情况下每次都触发侦听器,可以使用await nextTick(),这样就可以在下一步改变之前运行。

③ 侦听响应式对象 

类型为数组时需要一个由值构成的副本,对象时为一个getter函数,深度对象需要deep:true

当然如果同时监听多个对象,并且同一个函数中同时改变的值有不需要deep:true就能监听到的,比如下面的count,那其实可以不加deep:true也能监听到state.user.name,因为他们是一起改变的。

示例:

import { reactive, watch, nextTick, ref } from 'vue'

setup(){
    const state = reactive({ count: 0, user: { name: 'abc' }, arr: [0,1,2] })
    const count = ref(0)
    watch(
        [()=>state.count,count,()=>[...state.arr],()=>state],//
        (count,prevCount) =>{
            console.log(count,prevCount)
        },
        {
            deep: true
        }
    )  
    const changeValues = async()=>{
        count.value ++
        await nextTick()
        state.count ++
        state.user.name = 'ccc'
        state.arr.push(3)
    }  
    changeValues() 
}

5)async setup()

组合式API中组件的setup()钩子可以是异步的:

//选项式api写法:
export default {
    async setup(){
        const res = await fetch('xxx')
        const posts = await res.json()
        return { posts }
    }
}

//组合式api写法:
<script setup>
const res = await fetch('...')
const posts = await res.json()
</script>

2. 依赖注入

1)provide

作用:为后代组件提供数据

export default {
    provide: {
        message: 'hello!'
    }
}

如果提供的数据依赖当前组件,需要使用函数形式:

export default {
    data (){
        return {
            message: 'hello',
            info: 'world'
        }
    }
    provide (){
        return {
            message: this.message,//此种写法无响应性
            info: computed(()=>this.message),//这样写有响应性
        }
    }
}

2)inject

作用:后代组件获取祖先组件的数据

export default {
    inject: ['message'],
    created(){
        console.log(this.message)
    },
    //可以通过data访问
    data(){
        myMessage: this.message
    }
}

3) 使用别名和默认值:

export default{
    inject: {
        //获取祖先组件provide的message 命名为myMessage 如果没有获取到 则使用默认值hello
        myMessage: {
            from: 'message',
            default: 'hello'
        }
    },
    created(){
        console.log(this.myMessage)
    },
}

3. 组合式函数

1)定义:利用组合式API来封装和复用有状态逻辑的函数,命名使用use开头

2)作用:管理随时间变化的状态,比如跟踪当前鼠标在页面中的位置,触摸手势或与数据库的连接状态、异步数据请求等复杂逻辑

3)限制:在<script setup>(组合式写法)或setup()(选项式写法)钩子中同步地调用,某些场景下也可以在像onMounted这样的生命周期钩子中使用。是为了让vue能够确定当前正在被执行的是哪个组件实例,这样才能将生命周期钩子、计算属性和监听器(这俩是便于在组件被卸载时停止监听,避免内存泄漏)注册到该组件实例上。

4)tip:<script setup>是唯一在调用await之后仍可调用组合式函数的地方,编译器会在异步操作之后自动为你恢复当前的组件实例。

示例:

<script setup>
import { ref, onMounted, onUnmounted } from 'vue'

const x = ref(0)
const y = ref(0)

function update(event) {
    x.value = event.pageX
    y.value = event.pageY
}

onMounted(()=> window.addEventListener('mosemove',update))
onUnmounted(()=> window.removeEventListener('mousemove',update))
</script>

<template>
x: {{x}}, y: {{y}}
</template>

改造一下可以让这个函数在多个地方使用,也可以嵌套:

event.js:

import { onMounted, onUnmounted } from 'vue'
export function useEventListener(target,event,callback){
    onMounted(()=>target.addEventListener(event,callback))
    onUnmounted(()=>target.removeEventListener(event,callback))
}

mouse.js:

import { useEventListener } from './event'
import { ref } from 'vue'
export function useMouse(){
    const x = ref(0), y = ref(0)
    useEventListener(window,event,()=>{
        x.value = event.pageX
        y.value = event.pageY
    })
    return { x, y }
}

index.vue:

<script setup>
import { useMouse } from './useMouse'
const { x, y } = useMouse()
</script>
<template>
x: {{ x }}, y: {{ y }}
</template>

5)异步的带参数的组合式函数:

useFetch.js:

import { ref, isRef, unref, watchEffect } from 'vue'
export function useFetch(url){
    const data = ref(null)
    const error = ref(null)

    async function doFetch(){
        data.value = null
        error.value = null

        //解包可能是ref的值,是则返回value 不是则返回这个值
        const urlValue = unref(url)
        
        try {
            await timeout()

            //fetch是一个异步请求方式
            const res = fetch(urlValue)

            //.json()是fetch带的方法 把返回的json字符串转化为对象,也被包装成一个promise了
            data.vaule = await res.json()
        } catch (e){
            error.value = e
        }
    } 

    //如果是响应式的 值变了就调
    if(isRef(url)){
        //有任何改动就调取,watch需要指定某个属性
        watchEffect(doFetch)
    }else{
        doFetch()
    }
    return { data, error }
}

funvtion timeout(){
    return new Promise((resolve,reject)=>{
        setTimeout(()=>{
            if(Math.random>0.7){
                resolve()
            }else{
                //返回一个错误对象 new Error 第一个参数为message
                reject(new Error('error'))
            }
        },300)
    })
}

index.vue:

<script setup>
import { ref, computed } from 'vue'
import { useFetch } from './useFetch'
const id = ref(1)
const baseUrl = 'http://xxxx.com/'
const url = computed(()=> baseUrl + id.vaule)
const { data, error, retry } = useFetch(url)
</script>

<template>
    Load post id:
    <button v-for="i in 5" @click="id=i">{{ i }}</button>
    <div v-if="error">
        <p>error! message:{{ error.message }}</p>
        <button @click="retry">retry<button>
    </div>
    <div v-else-if="data">
        data: {{ data }}
    </div>
    <div v-else>
        loading
    </div>
</template>

6)在选项式API中使用组合式函数:

import { useMouse } from './mouse'
import { useFetch } from './fecth'
export default {
    setup(){
        const { x, y } = useMouse()
        const { data, error, retry } = useFetch('xxx')
        return { x, y, data, error, retry }
    },
    mounted(){
        //setup返回的值可以通过this访问
        console.log(this.x)
    }
}

...未完待续

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值