三个小时学完vue3——组件学习(三)

三个小时学完vue3(三)

继续学习vue3

基于Vite创建Vue3项目

vite官网:https://cn.vitejs.dev

vite是一种新型的前端构建工具。搭建vite项目命令:npm create vite@latest(需要先下载好node.js,我这里下载了nvm来进行管理node.js)
在这里插入图片描述
输入npm run dev之后项目就跑起来了(如下图):
在这里插入图片描述

删除项目内原有的内容,一个空的项目便创建完毕,目录如下图所示:
在这里插入图片描述

Vue3好用的VsCode插件

  • Vue-Official(Valor):语法高亮、智能提示等功能。

  • Vue VSCode Snippets:修改模板
    找到路径:C:\Users\用户名.vscode\extensions\sdras.vue-vscode-snippets-3.2.0\snippets下的vue.json;可以进行修改生成代码模板:
    在这里插入图片描述
    在这里插入图片描述

  • 别名路径跳转
    支持任何项目,可以自由配置映射规则,自由配置可缺省后缀名列表

导入组件

  • 尝试将之前的代码挪到App.vue中:
<script setup>
  import { reactive } from 'vue'

  const web = reactive({
      show: true
  })

  const toggle = () => {
      web.show = !web.show
  }
</script>

<template>
  <h3>{{ web.show }}</h3> <hr>

  <!-- 一段文字的显示和隐藏 -->
  <p v-show="web.show">zzzzzzzzz</p>
  <button @click="toggle">切换显示状态</button>
</template>

<style scoped>

</style>

运行代码可以正常显示。

  • 接下来,进行导入组件:
    创建两个子组件header和footer进行导入,如下图所示:
    在这里插入图片描述

父传子 defineProps

子组件header内容:

<script setup>
    // 接收值
    const props = defineProps(["propsname", "propsurl"])
    console.log(props)

    // defineProps 是宏函数
</script>

<template>
    <h3>header</h3>
</template>

<style scoped>

</style>

子组件footer内容:

<script setup>
    // 接收对象
    const props = defineProps({
        user:Number,
        url: {
            type:String,
            required: true,
            default: 'xxxx.com'
        }
        // 如果没有传递url;会报警告:就是缺失url属性,当你存在url属性的时候它就不会警告了
    })
    console.log(props)
</script>

<template>
    <div>
        <h3>footer</h3> {{ props.user }}
    </div>
</template>

<style scoped>

</style>

父组件内容:

<script setup>
  import Header from './components/header.vue';
  import Footer from './components/footer.vue';
import { reactive } from 'vue';

  // const propsWeb = {
  //   user:10,
  //   url: 'www.zz.com'
  // }

  // 换成响应式数据
  const propsWeb = reactive({
    user:10,
    url: 'www.zz.com'
  })

  const userAdd = () => {
    propsWeb.user++
    console.log(propsWeb.user);
  }
</script>

<template>
  <div>
    <!-- 通过属性传值 -->
    <Header propsname="zz" propsurl="xxx.com"/>
    zz.com
    <!-- 传递响应式数据 -->
    <button @click="userAdd">添加用户</button>

    <!-- <Footer v-bind="propsWeb"/> -->
     <!-- 简写将v-bind换成: -->
    <Footer :="propsWeb"/> 
  </div>
</template>

<style scoped>

</style>

子传父 defineEmits

定义父组件 father.vue

<script setup>
    import { reactive, ref } from 'vue';
    import Son from '../components/son.vue';

    const web = reactive({
        name: 'zz学习vue',
        url: 'zz.com'
    })

    const user = ref(0)

    const emitsGetWeb = (data) => {
        console.log(data)
        web.url = data.url,
        web.name = data.name
    }

    const emitsUserAdd = (data) => {
        user.value += data
    }
    
</script>

<template>
    <div>
        <Son @getWeb="emitsGetWeb" @userAdd="emitsUserAdd"/>
        {{ web }} - {{ user }}
    </div>
</template>

<style scoped>

</style>

定义子组件

<script setup>
    // 子组件给父组件传值用的是 defineEmits
    const emits = defineEmits(["getWeb", "userAdd"])
    emits("getWeb", {name: "zzzd", url: 'zzzd.com'})

    const add = () => {
        emits("userAdd", 10)
    }
</script>

<template>
    <div>
        <h3>son</h3>
        <button @click="add">添加用户</button>
    </div>
</template>

<style scoped>

</style>

子组件向父组件传值使用defineEmits,例如:const emits = defineEmits(["getWeb", "userAdd"]),再使用 emits("getWeb", {对象传值})

跨组件通信-依赖注入

使用的是 provide 和 inject(父组件向所有子组件传值)

父组件:

<script setup>
    // proovide : 将父组件中的值传给全部子组件
    import { ref, provide } from 'vue';
    import Son from './son.vue';

    const web = {
        name: 'zz',
        url: 'zz.com'
    }
    provide("provideWeb", web)

    // 传递响应式数据
    const user = ref(0)
    provide("provideUser", user)

    // 传递函数
    const userAdd = () => {
        user.value++
    }
    provide("provideUserAdd", userAdd)
</script>

<template>
    <div>
        <h3>APP.vue-Top组件</h3>
        user: {{ user }}
        <!-- 子组件 -->
        <Son/>
    </div>
</template>

<style scoped>

</style>

子组件1:

<script setup>
    // 导入子组件
    import Nav from './nav.vue';
    import { inject } from 'vue';

    const user = inject("provideUser")
    console.log("provideUser", user);
    
</script>

<template>
    <div>
        <h3>herder.vue.middle组件</h3>
        <!-- 子组件 -->
        <Nav/>
    </div>
</template>

<style scoped>

</style>

子组件2:

<script setup>
    import { inject } from 'vue';
    const web = inject("provideWeb")
    console.log("provideWeb", web)
    const functionUserAdd = inject("provideUserAdd")
</script>

<template>
    <div>
        <h3>nav.vue</h3>
        <button @click="functionUserAdd">添加用户</button>
    </div>

</template>

<style scoped>

</style>

匿名插槽和具名插槽

插槽(slot):是指可以在父组件内自定义模板片段,在子组件中可以将定义的模板片段插入到子组件的特定位置。

共有两种插槽:匿名插槽和具名插槽,下面是两种组件的使用方式。

  • 定义父组件:
<script setup>
    import Slot1 from '../components/slot1.vue';
    import Slot2 from '../components/slot2.vue';
</script>

<template>
    <div>
        <h3>父组件</h3>

        <!-- 匿名插槽 -->
         <!-- 在子组件中直接使用 <slot/> 即可 -->
        <Slot1>
            <a href="baidu.com">zz学习vue</a>
        </Slot1>

        <!-- 具名插槽 -->
        <Slot2>
            <template v-slot:url>
                <a href="www.zz.com">网址</a>
            </template>
            <!-- 简写形式 -->
            <template #name>
                <h3>简写:zz</h3>
            </template>
        </Slot2>
    </div>
</template>

<style scoped>

</style>
  • 组件1:
<script setup>

</script>

<template>
    <div>
        <h3>子组件1</h3>

        <slot/>
    </div>
</template>

<style scoped>

</style>
  • 组件2:
<script setup>

</script>

<template>
    <div>
        <h3>子组件2</h3>
        <slot name="url" />
        <slot name="name" />
    </div>
</template>

<style scoped>

</style>

作用域插槽

作用域插槽:子组件向父组件传递数据,并在父组件定义的模板中渲染。

在父组件中添加以下代码:

<Slot3>
            <!-- <template #url="data">
                {{ data.title }}
                {{ data.user }}
                <a href="www.baidu.com">百度网址</a>
            </template> -->

            <!-- 使用结构化形式 -->
            <template #url="{title, user}">
                {{ title }}
                {{ user }}
                <a href="www.baidu.com">百度网址</a>
            </template>
        </Slot3>
  • 定义子组件
<script setup>

</script>

<template>
    <div>
        <h3>作用域插槽</h3>
        <slot name="url" title="zz学习" user="1000"/>
    </div>
</template>

<style scoped>

</style>

生命周期函数

在 Vue 3 中,生命周期函数有两种使用方式,分别是组合式 API 和选项式 API。下面为你详细介绍这两种方式下的生命周期函数。

组合式 API 中的生命周期函数

组合式 API 是 Vue 3 引入的新特性,它允许使用函数来组织代码逻辑。在组合式 API 中,生命周期函数通过导入特定的函数来使用。

以下是组合式 API 中常用的生命周期函数及其对应的选项式 API 生命周期钩子:

组合式 API 函数选项式 API 钩子描述
onBeforeMountbeforeMount在组件挂载到 DOM 之前调用
onMountedmounted在组件挂载到 DOM 之后调用
onBeforeUpdatebeforeUpdate在组件更新之前调用
onUpdatedupdated在组件更新之后调用
onBeforeUnmountbeforeDestroy(Vue 2) / beforeUnmount(Vue 3)在组件卸载之前调用
onUnmounteddestroyed(Vue 2) / unmounted(Vue 3)在组件卸载之后调用
onErrorCapturederrorCaptured当捕获到一个来自子孙组件的错误时调用
onRenderTracked调试钩子,当响应式依赖被追踪时调用
onRenderTriggered调试钩子,当响应式依赖被触发时调用
onActivatedactivated在使用 <keep-alive> 组件时,组件被激活时调用
onDeactivateddeactivated在使用 <keep-alive> 组件时,组件被停用时调用

代码练习:

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

    // 挂载阶段
    onMounted(() => {
        console.log("onMounted")
    }) 
    // 用于响应式数据变化的时候
    onUpdated(() => {
        console.log("onUpdated")
    }) 
    const user = ref(0)
    console.log("user", user.value)
</script>

<template>
    <div>
        {{ user }} 
        <button @click="user++">添加用户</button>
    </div>

</template>

<style scoped>

</style>

toRef和toRefs

  • toRef:将一个响应式对象的某个属性转换为ref变量
  • toRefs:将一个响应式对象的所有属性转换为ref对象
<script setup>
    // toRef:将一个响应式对象的某个属性转换为ref变量
    // toRefs:将一个响应式对象的所有属性转换为ref对象

    import { reactive, toRefs, toRef } from 'vue';

    // let {name, url} = reactive({
    //     name: 'zz学习',
    //     url: 'zz.com'
    // })

    // console.log(typeof url) // string

    let web = reactive({
        name: 'zz学习',
        url: 'zz.com'
    })

    // let {name, url} = toRefs(web)
    // console.log(url); //打印出来是响应式数据
    
    let url = toRef(web, "url")
    console.log(url);//打印出来也是响应式数据
    
</script>

<template>
    <div>
        {{ url }}
    </div>
</template>

<style scoped>

</style>

Pinia 简介

Pinia 是 Vue.js 的一个状态管理库,从 Vue 3 开始被广泛使用。它是 Vuex 的继任者,具有更简洁的 API、更好的 TypeScript 支持和一些新特性。以下是关于 Pinia 的详细介绍:

核心概念

1. Store(仓库)

在 Pinia 中,store 是存储应用状态的地方,类似于 Vuex 中的 store。一个 store 包含状态(state)、getters 和 actions,并且可以在整个应用中被多个组件共享。

2. State(状态)

state 是存储在 store 中的数据,类似于 Vue 组件中的 data。它是响应式的,当 state 发生变化时,使用这些数据的组件会自动更新。

3. Getters(计算属性)

getters 类似于 Vue 组件中的 computed 属性,用于从 state 中派生出一些新的数据。它们可以缓存计算结果,提高性能。

4. Actions(动作)

actions 类似于 Vue 组件中的 methods,用于处理业务逻辑,例如异步操作、修改 state 等。

一个例子了解Pinia

比如登录的时候:

  • 使用Pinia创建一个userStore来集中管理用户的登录状态和过期时间
    • 当用户登录成功时:
      • 设置userStore中用户的登录状态为已登录,并设置过期时间
    • 当用户退出登录时:人
      • 修改userStore中用户的登录状态为未登录,并删除过期时间

与 Vuex 的对比

  • API 复杂度:Pinia 的 API 比 Vuex 更简洁,尤其是在处理状态修改时,不需要使用 mutations
  • TypeScript 支持:Pinia 对 TypeScript 的支持更好,而 Vuex 在 TypeScript 中的使用相对复杂。
  • 插件系统:Vuex 有更成熟的插件系统,而 Pinia 的插件系统相对较新,但也在不断发展。

与“组件通信”的区别

概念不同
  • Pinia:是状态管理库,集中管理应用共享状态,类似数据“中央仓库”。
  • 组件通信:是实现组件间数据传递与交互的机制。
使用场景不同
  • Pinia:适用于管理全局状态和处理复杂状态逻辑。
  • 组件通信:用于父子、兄弟、跨层级组件间的数据交互。
实现方式不同
  • Pinia:定义 store 包含 stategettersactions,组件通过 useStore 使用。
  • 组件通信:有 props$emit、事件总线、provide/inject 等多种方式。
数据流向和作用范围不同
  • Pinia:单向数据流,作用于整个应用。
  • 组件通信:流向多样,作用范围限于相关组件。

与localStorage的不同

Pinia和localStorage都是在Web应用中用于数据存储的,但它们有着不同的特点和使用场景,以下从多个方面介绍它们的区别:

本质与用途

  • Pinia:是一个为Vue.js设计的状态管理库,主要用于在应用运行时管理组件间共享的响应式状态。它使得数据在不同组件间可以方便地传递和同步,保证数据的一致性和可维护性,侧重于解决应用内部状态管理的问题。
  • localStorage:是浏览器提供的一种客户端存储机制,用于在浏览器中永久保存数据(除非手动清除)。它可以在浏览器关闭后依然保留数据,常用来存储用户偏好设置、缓存数据等,侧重于数据的持久化存储。

数据特性

  • Pinia:存储的数据是响应式的。当数据发生变化时,使用这些数据的Vue组件会自动更新视图,保证界面与数据的实时同步。但数据仅在应用运行期间有效,刷新页面或关闭应用后,数据会丢失(除非进行额外的处理)。
  • localStorage:存储的数据是非响应式的,它只是简单地以键值对的形式将数据存储在浏览器中。即使数据发生变化,页面上使用这些数据的部分不会自动更新,需要手动重新获取和更新界面。并且只要不主动删除,数据会一直存在于浏览器中。

数据操作

  • Pinia:通过定义stategettersactions来管理数据。state用于存储状态,getters用于获取派生状态,actions用于修改状态和处理业务逻辑。
  • localStorage:使用setItemgetItemremoveItemclear等方法来操作数据。

数据存储限制

  • Pinia:存储的数据量主要受应用内存的限制,通常只要不超过浏览器或设备的内存限制,就可以存储大量的数据。
  • localStorage:每个域名下的存储容量一般限制在5 - 10MB左右,不同浏览器可能会有所差异。如果存储的数据超过这个限制,会抛出QuotaExceededError错误。
数据类型支持
  • Pinia:可以存储各种JavaScript数据类型,包括对象、数组等,并且保持其原有结构和类型。
  • localStorage:只能存储字符串类型的数据。如果需要存储对象或数组,需要先使用JSON.stringify()将其转换为字符串,读取时再使用JSON.parse()将其还原。例如:

安装 Pinia 以及定义和使用 Store

使用命令:npm install pinia进行安装

导入pinia,并将该实例注册到vue应用中:(修改main.js)

import { createApp } from 'vue'
import { createPinia } from 'pinia'

import App from './App.vue'
const pinia = createPinia()

// createApp(App).mount('#app')
const app = createApp(App)
app.use(pinia)
app.mount("#app")

重新运行项目并没有报错说明导入成功。
接下来:定义一个store (可以参考官网:https://pinia.vuejs.org/zh/)

import { reactive, ref } from "vue";
import { defineStore } from "pinia";

export const useWebStore = defineStore('web', () => {
    const web = reactive({
        title: 'zz学习',
        url: 'www.baidu.com'
    })

    const user = ref(100)

    const userAdd = () => {
        user.value++
    }

    return {
        web,
        user,
        userAdd
    }
})

接下来在某个组件中导入仓库:

<script setup>
    import { useWebStore } from '../stores/web';

    const webStore = useWebStore()

    console.log(webStore.web);
    console.log(webStore.user);
    
</script>

<template>
    <div>
        {{ webStore.web }}
        {{ webStore.user }}

        <button @click="webStore.userAdd">添加用户</button>
    </div>
</template>

<style scoped>

</style>

Pinia 持久化存储插件

将pinia 存储在本地。
插件官网:https://prazdevs.github.io/pinia-plugin-persistedstate/zh/
安装:npm i pinia-plugin-persistedstate;然后在仓库代码中添加:

{
    persist: true
}

可以看到数据存储在了localStorage中,并能响应数据。
在这里插入图片描述

  • 自动状态同步
    • 持久化插件自动将Pinia的状态存储到localStorage中,无需手动处理状态的读取和写入
  • 易用性
    • 无需手动处理localStorage的键值对存储、数据转换等繁琐过程
  • 与Vue组件状态紧密集成
    • 持久化插件与Vue组件的响应式数据完美结合
    • 当状态改变时,依赖这些状态的组件会自动更新视图
    • 与仅仅从localStorage中读取静态数据相比更加灵活和强大。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值