Vue3 五天速成(中)

简介: Vue3 五天速成(中)

Vue3 五天速成(上)https://developer.aliyun.com/article/1504164?spm=a2c6h.13148508.setting.29.36834f0eJwPRIa

6. watchEffect

相较于watch,watchEffect不需要指定监视对象,而是响应式的追踪对象;

<template>
    <div>
        <h5>情况4:监视【ref】定义的值是【对象类型】的数据</h5>
        姓名: {{ person.name }} <br>
        年龄: {{ person.age }} <br>
        车辆:{{ person.car.c1 }}, {{ person.car.c2 }} <br>
        <button @click="changeName">修改名字</button>
        <button @click="changeAge">修改年龄</button>
        <button @click="changeC1">修改c1</button>
        <button @click="changeC2">修改c2</button>
        <button @click="changeCar">修改car</button>
    </div>
</template>
<script lang="ts">
    export default {
        name: 'Person'
    }
</script>
<script setup lang="ts">
    import {reactive, watch, watchEffect} from 'vue'
    let person = reactive({
        name: '张三',
        age: 18,
        car:{
            c1: 'asd',
            c2: 'das'
        }
    })
    function changeName (){
        person.name = '李四'
    }
    function changeAge (){
        person.age += 1
    }
    function changeC1(){
        person.car.c1 = 'qqq'
    }
    function changeC2(){
        person.car.c2 = 'www'
    }
    function changeCar(){
        person.car = {c1:'yyy', c2:'jjj'}
    }
    // 此时newVal和oldVal是数组与前面的对应
    /* watch([()=>person.name, ()=>person.age], (newVal, oldVal)=>{
        let [newname, newage] = newVal
        if( newage > 23 ){
            console.log('发送请求')
        }
    }) */
    // 如果采用watchEffect,全自动的watch;
    watchEffect(()=>{
        if( person.age > 23 ){
            console.log('发送请求')
        }
    })
</script>
<style scoped>
    button {
        margin: 10px;
    }
</style>

7. 标签的ref属性

利用refdocument.getElementById('')的区别在于,前者是局部的,不会受到整体的干扰;

<template>
    <h2> 北京 </h2>
    <h2 ref="title"> 师范 </h2>
    <h2> 大学 </h2>
    <button @click="output"> 点击一下输出样式 </button>
</template>
<script lang="ts">
    export default {
        name: 'Person'
    }
</script>
<script setup lang="ts">
    import {ref, defineExpose} from 'vue'
    // 这里变量名和template中的ref中的变量名对应上了
    let title = ref()
    function output(){
        console.log(title.value)
    }
    // 在这里可以使调用该模块的模块得到该模块的内容
    defineExpose({title})
</script>
<style scoped>
    /* 这里的scoped是局部样式,防止和子文件样式出现重复而全部修改
    无脑加上就好 */
</style>

day 3

8. 回顾TS中的接口_泛型_自定义类型

一般在文件目录src下面有一个types文件夹,types中有一个index.ts文件可以直接导入(导入过程不需要写文件)

这里文件表示泛型和接口的意思,一般是结合后端使用,避免缺失参数;

// 定义一个接口,用于限制person对象的具体属性 string,number小写
// 使用export暴露
export interface PersonInter {
    id: string,
    name: string,
    // ?问号表示可有可无
    age?: number
}
// 一个自定义类型
export type Persons = Array<PersonInter>
// 也可以这样写 export type Persons = PersonInter[]
<template>
    <div class="app">
    </div>
</template>
<script lang="ts">
    export default {
        name: 'Person'
    }
</script>
<script setup lang="ts">
    // PersonInter是类型,必须使用type前置
    import {type PersonInter, type Persons} from '@/types'
  import {reactive} from 'vue'
  
    // 单个
    let person:PersonInter = {id: 'asdasdas01', name: '张三', age: 18}
    // 数组 第一种形式   尖括号表示的是泛型:即符合规范
    let persons:Array<PersonInter> = [
        {id: 'asdasdas01', name: '张三', age: 18},
        {id: 'asdasdas02', name: '李四', age: 20},
        {id: 'asdasdas03', name: '王五', age: 33}
    ]
  // 数组 第二种形式 这里结合index.ts文件
    let personList:Persons = [
        {id: 'asdasdas01', name: '张三', age: 18},
        {id: 'asdasdas02', name: '李四', age: 20},
        {id: 'asdasdas03', name: '王五', age: 33}
    ]
  let personsReactive = reactive<Persons>([
        {id: 'asdasdas01', name: '张三', age: 18},
        {id: 'asdasdas02', name: '李四', age: 20},
        {id: 'asdasdas03', name: '王五', age: 33}
    ])
    
</script>
<style scoped>
    .app {
        background-color: #ddd;
        box-shadow: 0 0 10px;
        border-radius: 10px;
        padding: 20px;
    }
</style>

9. props的使用

将父组件中的数据传给子组件

vuehtml标签属性前面加冒号表示表达式

<h2 a="1+1" :b="1+1"></h2>

父组件如下:

<template>
    <Person a="哈哈" :list="persons"/>
</template>
<script lang="ts">
    export default {
        name: 'App'
    }
</script>
<script lang="ts" setup>
    import Person from './components/Person.vue'
    import {reactive} from 'vue'
    import {type Persons} from '@/types'
    let persons = reactive<Persons>([
        {id: 'asdasdas01', name: '张三', age: 18},
        {id: 'asdasdas02', name: '李四', age: 20},
        {id: 'asdasdas03', name: '王五', age: 33}
    ])
</script>
<style scoped>
</style>

子组件如下:

<template>
    <div class="app">
        <ul>
            <li v-for="a in x.list" :key="a.id">{{ a.name }} - {{ a.age }}</li>
        </ul>
    </div>
</template>
<script lang="ts">
    export default {
        name: 'Person'
    }
</script>
<script setup lang="ts">
    import {type Persons } from '@/types';
    // 下面可以不用引入defineProps,withDefaults
    import {defineProps, withDefaults} from 'vue'
    // 接收a
    // defineProps(['a'])
    // 接收a和list,同时将props保存起来
    // let x = defineProps(['a', 'list'])
    // x = {a:'哈哈'}
    // 接收list+限制类型
    // let x = defineProps<{list:Persons}>()
    // 接收list + 限制类型 + 限制必要性 + 指定默认值
    // list后面加?表示父组件可传可不传, 第二个list后面需要跟函数
    let x = withDefaults(defineProps<{list?:Persons}>(),{
        list:()=>[{id:'adasdasdaszxc01', name:'kang', age:20}]
    })
    
    
</script>
<style scoped>
    .app {
        background-color: #ddd;
        box-shadow: 0 0 10px;
        border-radius: 10px;
        padding: 20px;
    }
</style>

10. 生命周期

组件的生命周期有四个阶段:创建,挂载,更新,销毁;

钩子:生命周期函数 (阶段前,阶段完毕) 钩子一共有8个

<template>
    <div class="app">
        {{ message }}
    </div>
</template>
<script lang="ts">
    export default {
        name: 'Person'
    }
</script>
<script setup lang="ts">
    import {onBeforeMount, onMounted, onBeforeUpdate,
         onUpdated, onBeforeUnmount, onUnmounted} from 'vue'
    let message = '哈哈'
    
    onBeforeMount(()=>{
        console.log('ok')
    })
    onUnmounted(()=>{
        console.log('一般来说需要搭配v-if')
    })
    
</script>
<style scoped>
    .app {
        background-color: #ddd;
        box-shadow: 0 0 10px;
        border-radius: 10px;
        padding: 20px;
    }
</style>

11. 自定义Hooks

Hooks是组合式功能的体现,其创建过程类似于8节中在文件目录src下创建一个hooks的文件夹,里面存放useXxxx.ts文件,实例如下:

useAdd.ts

import { computed } from '@vue/reactivity'
import {ref, onMounted} from 'vue'
export default function(){
    let sum = ref(0)
    let bigSum = computed(()=>{
        return sum.value * 10
    })
    function add(){
        sum.value += 1
    }
    onMounted(()=>{
        sum.value += 1 
    })
    return {sum, add, bigSum}
}

useDog.ts

import {reactive, onMounted} from 'vue'
import axios from 'axios'
export default function(){
    let dogList = reactive([
        'https://images.dog.ceo/breeds/pembroke/n02113023_4136.jpg'
    ])
    
    // axios 的 实例用法1
    /* function addDog(){
        axios.get('https://dog.ceo/api/breed/pembroke/images/random').then(
            response => {dogList.push(response.data.message)},
            error => {console.log(error)}
        )
    } */
    // axios 的 实例用法2
    async function addDog() {
        try {
            let result = await axios.get('https://dog.ceo/api/breed/pembroke/images/random')
            dogList.push(result.data.message)
        } catch (error) {
            console.log(error)
        }
    }
    onMounted(()=>{
        addDog()
    })
    return {dogList, addDog}
}

这样组件中可以直接应用hooks文件下的内容:

<template>
    <div class="app">
        <h2> sum: {{ sum }};    bigSum: {{ bigSum }}</h2>
        <button @click="add"> 点我sum+1</button>
        <hr>
        <img v-for="(dog,index) in dogList" :src="dog" :key="index"> <br>
        
        <button @click="addDog"> 点我再添加一只狗 </button>
    </div>
</template>
<script lang="ts">
    export default {
        name: 'Person'
    }
</script>
<script setup lang="ts">
    import useAdd from '@/hooks/useAdd'
    import useDog from '@/hooks/useDog'
    const {sum, add, bigSum} = useAdd()
    const {dogList, addDog} = useDog()
</script>
<style scoped>
    .app {
        background-color: #ddd;
        box-shadow: 0 0 10px;
        border-radius: 10px;
        padding: 20px;
    }
    img {
        height: 150px;
        margin-right: 10px;
    }
</style>

一般在文件目录src下面有一个types文件夹,types中有一个index.ts文件可以直接导入(导入过程不需要写文件)

12. 路由 基本切换效果

路由和路由器不一样

路由类似于键值对,route

路由器类似于管理路由,router

路由的创建与Hooks和TS接口类似,其创建过程类似于8节中在文件目录src下创建一个router的文件夹,里面存放index.ts文件(无需导入文件名),实例如下:

index.ts 实例如下:

// 创建一个路由器,并暴露出去
// 第一步:引入createRouter
import {createRouter, createWebHistory} from 'vue-router'
// 第二步:引入可能要呈现的组件
import Home from '@/components/Home.vue'
import About from '@/components/About.vue'
import News from '@/components/News.vue'
// 第三步:创建路由器
const router = createRouter({
    // history表示路由的工作模式
    history: createWebHistory(),
    routes:[// 一个个路由规则
        {
            path: '/home',
            component: Home
        },
        {
            path: '/news',
            component: News
        },
        {
            path: '/about',
            component: About
        }
    ]
})
export default router

在这里我们只是创建了路由,于此同时,我们需要把路由加载到项目中,所以接下来我们需要在main.ts文件中操作;

// 引入createApp 用于创建应用
import { createApp } from 'vue'
// 引入app根组件
import App from './App.vue'
// 引入路由器
import router from './router'
// 创建一个应用
const app = createApp(App)
// 使用路由器
app.use(router)
// 挂载整个应用到app容器中
app.mount('#app')

为了在App.vue引入组件,我们还需要做一些操作,使用RouterView, RouterLink

<template>
    <h2> Vue 路由测试 </h2>
    <!-- 导航区 -->
    <div class="navigate">
        <RouterLink to="/home">主页</RouterLink>
        <RouterLink to="/news">新闻</RouterLink>
        <RouterLink to="/about">关于</RouterLink>
    </div>
    <!-- 展示区 -->
    <div class="main_content">
        <RouterView/>
    </div>
</template>
<script lang="ts">
    export default {
        name: 'App'
    }
</script>
<script lang="ts" setup>
    import {RouterView, RouterLink} from 'vue-router'
</script>
<style scoped>
    h2 {
        background: skyblue;
        text-align: center;
    }
    div.navigate {
        text-align: center;
    }
    div.navigate a{
        text-align: center;
        margin: 50px;
    }
    div.main_content{
        text-align: center;
        height: 500px;
    }
</style>

就完毕了,这里要用RouterLink替换a标签,并把href改成to,用RouterView表示component的插入位置;

13. 路由 两个注意点

路由组件一般放在pages或者views文件夹,一般组件通常放在components文件夹;

通过点击导航,视觉上效果消失了的组件,默认是被销毁掉的,需要的时候再去挂载;

14. 路由 路由器的工作模式

路由器有两种工作模式,history和hash,前端一般用history,后端一般用hash;

15. 路由 to的两种写法

%% 第一种: %%
<RouterLink to="/home" active-class="active">主页</RouterLink>
%% 第二种: %%:
<RouterLink :to="{path='/home'}" active-class="active">主页</RouterLink>

这里再介绍一下 active-class ,由于RouterLink在渲染的时候会变成a标签,所以样式可以类似于设置如下:

a.active {
        text-align: center;
        margin: 50px;
        color: red;
    }

16. 路由 命名路由

可以在路由的index.ts文件中添加添加名字:

// 创建一个路由器,并暴露出去
// 第一步:引入createRouter
import {createRouter, createWebHistory, createWebHashHistory} from 'vue-router'
// 第二步:引入可能要呈现的组件
import Home from '@/views/Home.vue'
import About from '@/views/About.vue'
import News from '@/views/News.vue'
// 第三步:创建路由器
const router = createRouter({
    // 路由的工作模式:history模式 不带#号 优先
    history: createWebHistory(),
    // 路由的工作模式:hash模式 带#号
    // history: createWebHashHistory(),
    routes:[// 一个个路由规则
        {
            name: 'zhuye',
            path: '/home',
            component: Home
        },
        {
            name: 'xinwen',
            path: '/news',
            component: News
        },
        {
            name: 'guanyu',
            path: '/about',
            component: About
        }
    ]
})
export default router

17. 路由 嵌套路由

子级不用加斜杠,但是to的时候需要完整的使用\news\detail

// 创建一个路由器,并暴露出去
// 第一步:引入createRouter
import {createRouter, createWebHistory, createWebHashHistory} from 'vue-router'
// 第二步:引入可能要呈现的组件
import Home from '@/views/Home.vue'
import About from '@/views/About.vue'
import News from '@/views/News.vue'
import Detail from '@/views/Detail.vue'
// 第三步:创建路由器
const router = createRouter({
    // 路由的工作模式:history模式 不带#号 优先
    history: createWebHistory(),
    // 路由的工作模式:hash模式 带#号
    // history: createWebHashHistory(),
    routes:[// 一个个路由规则
        {
            name: 'zhuye',
            path: '/home',
            component: Home
        },
        {
            name: 'xinwen',
            path: '/news',
            component: News,
            children:[
                {
                    path: 'detail', //子级不用写斜杠
                    component: Detail,
                }
            ]
        },
        {
            name: 'guanyu',
            path: '/about',
            component: About
        }
    ]
})
export default router

18. 路由 query 参数

在传参数给组件之前,我们需要给接收参数的组件添加一个Hooks:useRoute

<template>
    <ul class="news-list">
        <li>编号:{{ route.query.id }}</li>
        <li>标题:{{ route.query.title }}</li>
        <li>内容:{{ route.query.content }}</li>
    </ul>
</template>
<script setup lang="ts">
    import {useRoute} from 'vue-router'
    import {toRefs} from 'vue'
    
    let route = useRoute()
    
  // 如果需要解构
  let {query} = toRefs(route)
</script>
<style scoped>
</style>

这里在RouterLink中添加query参数:

<ul>
            <li v-for="news in NewsList" :key="news.id">
                <!-- 第一种写法 -->
                <!-- <RouterLink :to="`/news/detail?id=${news.id}&title=${news.title}&content=${news.content}`">{{ news.title }}</RouterLink> -->
                <!-- 第二种写法 -->
                <RouterLink :to="{
                    path: '/news/detail',
                    query: {
                        id: news.id,
                        title: news.title,
                        content: news.content
                    }
                }">
                    {{ news.title }}
                </RouterLink>
            </li>
        </ul>

19. 路由 params 参数

首先需要在router中设置占位:就是在path中添加/:

// 创建一个路由器,并暴露出去
// 第一步:引入createRouter
import {createRouter, createWebHistory, createWebHashHistory} from 'vue-router'
// 第二步:引入可能要呈现的组件
import Home from '@/views/Home.vue'
import About from '@/views/About.vue'
import News from '@/views/News.vue'
import Detail from '@/views/Detail.vue'
// 第三步:创建路由器
const router = createRouter({
    // 路由的工作模式:history模式 不带#号 优先
    history: createWebHistory(),
    // 路由的工作模式:hash模式 带#号
    // history: createWebHashHistory(),
    routes:[// 一个个路由规则
        {
            name: 'zhuye',
            path: '/home',
            component: Home
        },
        {
            name: 'xinwen',
            path: '/news',
            component: News,
            children:[
                {
                    name: 'xiangqing',
                    path: 'detail/:id/:title/:content?', // 需要占位 ?代表可传可不传
                    component: Detail,
                }
            ]
        },
        {
            name: 'guanyu',
            path: '/about',
            component: About
        }
    ]
})
export default router

params只能使用routename,而不能使用path,同时传参数只能使用基本类型,而不能使用数组和对象;

<ul>
            <li v-for="news in NewsList" :key="news.id">
                <!-- 第一种写法 -->
                <!-- <RouterLink :to="`/news/detail/${news.id}/${news.title}/${news.content}`">{{ news.title }}</RouterLink> -->
                <!-- 第二种写法 -->
                <RouterLink :to="{
                    name: 'xiangqing',
                    params: {// 只能传基本类型,不能是对象和数组
                        id: news.id,
                        title: news.title,
                        content:news.content,
                    }
                }">
                    {{ news.title }}
                </RouterLink>
            </li>
        </ul>

20. 路由 props配置

props:true 布尔值写法,只能搭配params使用,path需要占位

props(route){return route.query} 函数式写法,其中route可以操作params和query

props:{id:xx,title:xx,content:xx}

其原理就相当于在 中 添加了

21. 路由 replace属性

类似于浏览器的前进后退机制;

<div class="navigate">
        <RouterLink replace to="/home" active-class="active">主页</RouterLink>
        <RouterLink replace :to="{name:'xinwen'}" active-class="active">新闻</RouterLink>
        <RouterLink replace to="/about" active-class="active">关于</RouterLink>
    </div>

直接在RouterLink后面添加replace,不会留下记录;

22. 路由 编程式路由导航

可以做到不点标签直接跳转的效果,脱离RouterLink实现跳转;router.push() router.replace() 括号中to能怎么写,括号就能怎么写;

setTimeout(()=>{
  // 在此处执行跳转
}, 3000) // 3秒后执行函数

实例代码如下:

import {onMounted} from 'vue'
import { useRouter } from 'vue-router';
const router = useRouter()
onMounted(()=>{
  setTimeout(()=>{
    router.push('/news')
  }, 3000)
})

按钮点击实例代码如下:

<template>
    <div class="news">
        <!-- 导航区 -->
        <ul>
            <li v-for="news in NewsList" :key="news.id">
                <button @click="showContent(news)">点击获取详情</button>
                <RouterLink :to="{
                    name: 'xiangqing',
                    params: {// 只能传基本类型,不能是对象和数组
                        id: news.id,
                        title: news.title,
                        content:news.content,
                    }
                }">
                    {{ news.title }}
                </RouterLink>
            </li>
        </ul>
        <!-- 展示区 -->
        <div class="news-content">
            <RouterView></RouterView>
        </div>
    </div>
</template>
<script setup lang="ts">
    import type path from 'path';
    import {reactive} from 'vue'
    import {RouterView, RouterLink, useRouter} from 'vue-router'
    interface News{
        id: string,
        title: string,
        content: string,
    }
    const NewsList = reactive<News[]>([
        {id: 'asdasd01', title:'新闻阿斯达所多01', content: '在成长速度还01'},
        {id: 'asdasd02', title:'新闻阿斯达所多02', content: '在成长速度还02'},
        {id: 'asdasd03', title:'新闻阿斯达所多03', content: '在成长速度还03'},
        {id: 'asdasd04', title:'新闻阿斯达所多04', content: '在成长速度还04'},
    ])
    const router = useRouter()
    function showContent(news:News){
        router.push(
            {
                name: 'xiangqing',
                params: {// 只能传基本类型,不能是对象和数组
                    id: news.id,
                    title: news.title,
                    content:news.content,
                }
            })
    }
</script>
<style scoped>
    div.news {
        background-color: yellow;
        height: 500px;
    }
    div.news-content {
        background-color: aqua;
        height: 300px;
        width: 200px;
    }
</style>

23. 路由 重定向

自动切换url,在router文件中补充规则:

{
  path: '/',
  redirect: '/home'
}

day 4

24. pinia 选项式写法

pinia环境首先需要安装

npm install pinia

安装之后,pinia的创建与Hooks和TS接口类似,其创建过程类似于8节中在文件目录src下创建一个store的文件夹,里面存放对应components中的文件实例如下:

在main.ts文件中需要引入pinia

import {createApp} from 'vue'
import App from './App.vue'
import {createPinia} from 'pinia'
import router from './router'
const app = createApp(App)
const pinia = createPinia()
app.use(pinia)
app.use(router)
app.mount('#app')

Count.vue如下:

<template>
    <div class="count">
        <h2>当前求和为: {{ sum }}, bigsum:{{ bigSum }}, smallsum:{{ smallSum }}</h2>
        <!-- 这里.number表示value数值后面的符号为单位 -->
        <select v-model.number="n">
            <option value="1">1</option>
            <option value="2">2</option>
            <option value="3">3</option>
        </select>
        <button @click="add">加</button>
        <button @click="minus">减</button>
    </div>
</template>
<script  lang="ts">
    export default {
        name: 'Count'
    }
</script>
<script setup lang="ts">
    import {useCountStore} from '@/store/Count'
    import {storeToRefs} from 'pinia'
    let countStore = useCountStore()
    // storeToRefs只会关注store中的数据
    let {sum, n, bigSum, smallSum} = storeToRefs(countStore)
    // 以下两种方式都能拿到state中的数据
    // console.log(countStore.sum)
    // console.log(countStore.$state.sum)
    // 方法
    function add(){
        // 第一种修改方式
        // countStore.sum += countStore.n
        // 第二种修改方式
        // countStore.$patch({
        //     sum: 100
        // })
        // 第三种修改方式 复杂内容可以放到store中的actions中
        countStore.increment()
    }
    function minus(){
        sum.value -= n.value
    }
</script>
<style scoped>
    .count {
        background-color: skyblue;
        padding: 10px;
        border-radius: 10px;
        box-shadow: 0 0 10px;
        margin-bottom: 20px;
    }
    select, button {
        margin: 0 10px;
        height: 25px;
    }
</style>

Count.ts 如下:

import {defineStore} from 'pinia'
import {ref} from 'vue'
export const useCountStore = defineStore('Count', {
  // 写函数
    actions:{
        increment(){
            this.sum += this.n
        }
    },
    // 真正存储数据的地方
    state(){
        return {
            sum: ref(0),
            n: ref(1),
        }
    },
    // 类似于vue中的computed属性
    getters:{
        bigSum: state=>state.sum*10,
        smallSum():number{
            return this.sum - 1000
        }
    }
})

LoveTalk.vue如下:

<template>
    <div class="talk">
        <button @click="getMessage"> 点击获取一句土味情话</button>
        <ul>
            <li v-for="talk in talkList" :key="talk.id">{{ talk.content }}</li>
        </ul>
    </div>
</template>
<script  lang="ts">
    export default {
        name: 'LoveTalk'
    }
</script>
<script setup lang="ts">
    import {useLoveStore} from '@/store/LoveTalk'
    import {storeToRefs} from 'pinia'
    let loveStore = useLoveStore()
    let {talkList} = storeToRefs(loveStore)
    loveStore.$subscribe((mutate, state)=>{
        // mutate是变化的东西,state是后面的状态
        console.log('lovestore保存的数据发生了变化!', mutate, state)
        // 存储在本地的talkList变量中,由于talkList只能接受字符串数据,所以需要JSON.stringify
        localStorage.setItem('talkList', JSON.stringify(state.talkList))
    })
    async function getMessage(){
        loveStore.getTalk()
    }
</script>
<style scoped>
    .talk {
        background-color: orange;
        padding: 10px;
        border-radius: 10px;
        box-shadow: 0 0 10px;
    }
</style>

LoveTalk.ts如下:

import {defineStore} from 'pinia'
import {nanoid} from 'nanoid' // 生成id
import axios from 'axios'
export const useLoveStore = defineStore('LoveTalk', {
    actions:{
        async getTalk(){
            let {data:{content}} = await axios.get('https://api.uomg.com/api/rand.qinghua')
            let obj = {id: nanoid(), content: content}
            this.talkList.unshift(obj)
        }
    },
    // 真正存储数据的地方
    state(){
        return {
            talkList: JSON.parse(localStorage.getItem('talkList') as string ) || []
        }
    }
})

25. pinia 组合式写法

这里相对于选项式,我们需要把state中的部分转化为响应式才有效,然后添加return返回:

LoveTalk.ts如下:

import {defineStore} from 'pinia'
import {nanoid} from 'nanoid'
import axios from 'axios'
import {reactive} from 'vue'
export const useLoveStore = defineStore('LoveTalk', ()=>{
    // 这里写state中的部分
    let talkList = reactive(JSON.parse(localStorage.getItem('talkList') as string ) || [])
    async function getTalk(){
        console.log('获取一句')
        try {
            let {data:{content}} = await axios.get('https://api.uomg.com/api/rand.qinghua')
            let obj = {id: nanoid(), content: content}
            talkList.unshift(obj)
        } catch (error) {
            console.log('获取失败!')
        }
    }
    return {talkList, getTalk}
})

day 5

26. 组件通信_props

Father.vue 如下:

<template>
    <div class="father">
        <h2>这是父组件</h2>
        <h4>汽车:{{car}}</h4>
        <h4 v-if="toy">获取到儿子的玩具:{{ toy }}</h4>
        <Child :car="car" :sendToy="getToy"></Child>
    </div>
</template>
<script lang="ts">
    export default {
        name: 'Props'
    }
</script>
<script setup lang="ts">
    import Child from './Child.vue'
    import {ref} from 'vue'
    // 数据
    let car = ref('benz')
    let toy = ref('')
    // 方法
    function getToy(val:string){
        toy.value = val
    }
</script>
<style scoped>
    div.father {
        margin: 30px;
        background-color: yellow;
        width: 500px;
        height: 300px;
        border-radius: 10px;
        box-shadow: 0 0 10px;
    }
</style>

Child.vue如下:

<template>
    <div class="child">
        <h2>这是子组件</h2>
        <h4>玩具:{{ toy }}</h4>
        <h4>父亲给的车:{{ car }}</h4>
        <button @click="sendToy(toy)">点击把玩具交给父亲</button>
    </div>
</template>
<script lang="ts">
    export default {
        name: 'Child'
    }
</script>
<script setup lang="ts">
    import { ref } from 'vue'
    // 数据
    let toy = ref('atm')
    defineProps(['car', 'sendToy'])
</script>
<style scoped>
    div.child {
        background-color: gray;
        width: 80%;
        height: 70%;
        margin-left: 20px;
        border-radius: 10px;
        box-shadow: 0 0 10px;
    }
</style>

27. 组件通信_custom-event

标签中使用@开头的props表示的是事件,比如@click是点击事件,如果我们需要打印事件,我们可以使用$event当做参数放入函数中获取事件;

事件一般用串串的形式,而回调函数使用小驼峰

Father.vue 代码如下:

<template>
    <div class="father">
        <h2>这是父组件</h2>
        <h3 v-if="toy">儿子给的玩具{{ toy }}</h3>
        <!-- 这里send-toy表示时间的名字,getToy表示事件触发时使用的函数回调 -->
        <!-- 事件一般用串串的形式,而回调函数使用小驼峰 -->
        <Child @send-toy="getToy"></Child>
        <!-- 这里可以使用$event 提取出事件进行打印 -->
        <!-- <Child @send-toy="getToy(param1, param2, $event)"></Child> -->
    </div>
</template>
<script lang="ts">
    export default {
        name: 'Event'
    }
</script>
<script setup lang="ts">
    import Child from './Child.vue'
    import {ref} from 'vue'
    let toy = ref('')
    function getToy(val:string){
        toy.value = val
    }
</script>
<style scoped>
    div.father {
        margin: 30px;
        background-color: yellow;
        width: 500px;
        height: 300px;
        border-radius: 10px;
        box-shadow: 0 0 10px;
    }
</style>

Child.vue 代码如下:

<template>
    <div class="child">
        <h2>这是子组件</h2>
        <h3>儿子的玩具{{ toy }}</h3>
        <button @click="emit('send-toy', toy)"> 发送给父亲</button>
    </div>
</template>
<script lang="ts">
    export default {
        name: 'Child'
    }
</script>
<script setup lang="ts">
    import {ref} from 'vue'
    let toy = ref('atm')
    let emit = defineEmits(['send-toy'])
</script>
<style scoped>
    div.child {
        background-color: gray;
        width: 80%;
        height: 70%;
        margin-left: 20px;
        border-radius: 10px;
        box-shadow: 0 0 10px;
    }
</style>

Vue3 五天速成(下)https://developer.aliyun.com/article/1504170?spm=a2c6h.13148508.setting.27.36834f0eJwPRIa

目录
相关文章
|
5天前
|
缓存 JavaScript UED
Vue3中v-model在处理自定义组件双向数据绑定时有哪些注意事项?
在使用`v-model`处理自定义组件双向数据绑定时,要仔细考虑各种因素,确保数据的准确传递和更新,同时提供良好的用户体验和代码可维护性。通过合理的设计和注意事项的遵循,能够更好地发挥`v-model`的优势,实现高效的双向数据绑定效果。
107 64
|
5天前
|
JavaScript 前端开发 API
Vue 3 中 v-model 与 Vue 2 中 v-model 的区别是什么?
总的来说,Vue 3 中的 `v-model` 在灵活性、与组合式 API 的结合、对自定义组件的支持等方面都有了明显的提升和改进,使其更适应现代前端开发的需求和趋势。但需要注意的是,在迁移过程中可能需要对一些代码进行调整和适配。
|
1月前
|
存储 JavaScript 前端开发
vue3的脚手架模板你真的了解吗?里面有很多值得我们学习的地方!
【10月更文挑战第21天】 vue3的脚手架模板你真的了解吗?里面有很多值得我们学习的地方!
vue3的脚手架模板你真的了解吗?里面有很多值得我们学习的地方!
|
2月前
|
API
vue3知识点:provide 与 inject
vue3知识点:provide 与 inject
34 4
vue3知识点:provide 与 inject
|
2月前
|
API
vue3知识点:readonly 与 shallowReadonly
vue3知识点:readonly 与 shallowReadonly
26 1
vue3知识点:readonly 与 shallowReadonly
|
27天前
|
JavaScript 前端开发 开发者
Vue 3中的Proxy
【10月更文挑战第23天】Vue 3中的`Proxy`为响应式系统带来了更强大、更灵活的功能,解决了Vue 2中响应式系统的一些局限性,同时在性能方面也有一定的提升,为开发者提供了更好的开发体验和性能保障。
54 7
|
28天前
|
前端开发 数据库
芋道框架审批流如何实现(Cloud+Vue3)
芋道框架审批流如何实现(Cloud+Vue3)
47 3
|
27天前
|
JavaScript 数据管理 Java
在 Vue 3 中使用 Proxy 实现数据双向绑定的性能如何?
【10月更文挑战第23天】Vue 3中使用Proxy实现数据双向绑定在多个方面都带来了性能的提升,从更高效的响应式追踪、更好的初始化性能、对数组操作的优化到更优的内存管理等,使得Vue 3在处理复杂的应用场景和大量数据时能够更加高效和稳定地运行。
43 1
|
27天前
|
JavaScript 开发者
在 Vue 3 中使用 Proxy 实现数据的双向绑定
【10月更文挑战第23天】Vue 3利用 `Proxy` 实现了数据的双向绑定,无论是使用内置的指令如 `v-model`,还是通过自定义事件或自定义指令,都能够方便地实现数据与视图之间的双向交互,满足不同场景下的开发需求。
47 1
|
29天前
|
前端开发 JavaScript
简记 Vue3(一)—— setup、ref、reactive、toRefs、toRef
简记 Vue3(一)—— setup、ref、reactive、toRefs、toRef