Vue3 [Day11]

Vue3的优势





在这里插入图片描述



在这里插入图片描述


create-vue搭建Vue3项目

在这里插入图片描述



在这里插入图片描述

node -v
npm init vue@latest 或者是yarn create vue@latest
npm install

Vue3项目目录和关键文件

在这里插入图片描述
Vetur插件是Vue2的
Volarr插件是Vue3的



main.js
import './assets/main.css'

// new Vue() 创建一个应用实例 => createApp()
// createRouter()  createStore()
// 将创建实例进行了封装,保证每个实例的独立封闭性

import { createApp } from 'vue'
import App from './App.vue'

// createApp(App) 创建实例
// mount设置挂载点 #app(id为app的盒子)
createApp(App).mount('#app')



App.vue

<!-- 加上setup就允许在script中直接编写组合式API -->
<script setup>
import HelloWorld from './components/HelloWorld.vue'
import TheWelcome from './components/TheWelcome.vue'
</script>

<template>
    <!-- 不再要求唯一根元素 -->
  <header>
    <img alt="Vue logo" class="logo" src="./assets/logo.svg" width="125" height="125" />

    <div class="wrapper">
      <HelloWorld msg="You did it!" />
    </div>
  </header>

  <main>
    <!-- 组件不用注册,就可以直接用 -->
    <TheWelcome />
  </main>
</template>

<style scoped>
</style>


组件不用注册,可以直接使用


组合式API - setup选项

setup执行时机

在这里插入图片描述
执行时机,比beforeCreate更早,
所以,获取不到this(this是undefined

setup写法

在这里插入图片描述




数据和函数 需要在setup最后return 才能在模板中应用

简便写法:通过setup 语法糖,不用return
在这里插入图片描述


在这里插入图片描述
在这里插入图片描述




组合式API - reactive 和 ref 函数

reactive (不推荐

在这里插入图片描述



App.vue

<script setup>
import {reactive} from 'vue'
const state=reactive({
    count:100
})
const setCount= ()=>{
    state.count++
}
</script>


<template>
<div>{{ state.count }}</div>
<button @click="setCount">+1</button>
</template>

<style></style>


ref (更推荐

在这里插入图片描述
本质:在原有传入数据基础上,外层包含了一层对象,包成了复杂类型,底层包成复杂类型之后,再借助reactive 实现响应式,所以,
脚本中访问数据,需要通过 .value
在template中, .vue不需要加(帮我们扒了一层)
所以
推荐以后声明数据,统一用ref => 统一编码规范
App.vue

<script setup>
import {ref} from 'vue'
const count = ref(0)
const obj = ref({
    age:18,
    name:'slx'
})
console.log(obj.value);
console.log(obj.value.age);

// console.log(count.value)
const countChange = () => {
    // count本质上是对象,所以要count.value++ 不是count++
    count.value++
}
</script>


<template>
<div>{{ count }}</div>
<button @click="countChange">+1</button>
</template>

<style></style>


获取dom元素不用`$ref`,直接inp.value就可以了

在这里插入图片描述

在这里插入图片描述

什么是响应式数据,有什么作用

响应式数据是指在数据发生变化时,相关的视图能够自动更新的数据。在 Vue 中,通过将数据转换为响应式对象,当数据发生变化时,Vue 会自动追踪这些变化,并通知相关的视图进行更新。
响应式数据的作用是使开发者能够以声明式的方式编写代码,而不需要手动操作 DOM 或追踪数据变化。它提供了一种简单且高效的方式来维护数据和视图之间的关系,使开发者能够专注于业务逻辑而不是手动的视图更新。

通过响应式数据,当数据发生变化时,相关的视图会自动更新,保持数据和视图的同步。这大大简化了开发过程,提高了代码的可维护性和可读性。

响应式数据在 Vue 中是核心概念之一,它使得开发者能够构建动态、交互式和响应式的应用程序。

在 Vue 2 和 Vue 3 中,响应式数据的处理有什么区别

在 Vue 2 中,使用 data 选项来声明响应式数据,Vue 会将这些数据转换为响应式的对象。当数据发生变化时,Vue 会自动更新相关的视图。
在 Vue 3 中,使用 ref 和 reactive 函数来声明响应式数据。



组合式API - computed

在这里插入图片描述App.vue

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

const list = ref(
    [1,2,3,4,5,6,7,8,9]
)

// 基于list派生一个计算属性,从list中过滤
const computerList = computed(() => {
    return list.value.filter((item) => item%2===0)
})

// 定义一个修改数组的方法
const addFn = () =>{
    list.value.push(666)
}
</script>


<template>
<div>{{ list }}</div>
<div>{{ computerList }}</div>
<button @click="addFn">+666</button>
</template>

<style></style>

在这里插入图片描述
全选反选可以用get,set



组合式API - watch

侦听单个数据

在这里插入图片描述

侦听响应式对象的属性值

在这里插入图片描述



侦听多个数据

在这里插入图片描述

<script setup>
import {ref, watch} from 'vue'
const count = ref(0)
const nickname = ref('slx')
const changeCount = () => {
    count.value++
}

const changeName = () => {
    nickname.value='hhhhh'
}

// 1.监视单个数据变化
//   watch(ref对象,(newValue,oldValue) => { ... } )
//   直接传对象,所以就不要count.value
// watch(count,(newValue, oldValue)=> {
//     console.log(newValue, oldValue);
// })

// 2.监视多个数据变化
//   watch([ref对象1,ref对象2], (newArr,oldArr) => { ... } )
//   直接传对象,所以就不要count.value
// watch([count,nickname],(newArr, oldArr)=> {
//     console.log(newArr, oldArr);
// })



</script>


<template>
    <div>{{ count }}</div>
    <button @click="changeCount">change-count</button>
    <div>{{ nickname }}</div>
    <button @click="changeName">change-Name</button>
</template>

<style></style>

配置项

在这里插入图片描述

<script setup>
import {ref, watch} from 'vue'
const count = ref(0)
const nickname = ref('slx')
const changeCount = () => {
    count.value++
}

const changeName = () => {
    nickname.value='hhhhh'
}

// 添加额外配置项
// 3.immediate 立刻执行
watch(count,(newValue, oldValue)=> {
    console.log(newValue, oldValue)
},{
    immediate:true,
    deep:true
})


const userInfo = ref({
    name:'ssss',
    age:12
})
const setUserInfo = () => {
    // 修改userInfo.value ,修改对象的地址,才能监视到,浅层监视
    // userInfo.value={name:'qqqq',age:10}
    userInfo.value.age++
}
// 4.deep 深度监视,默认watch进行的是 浅层监视
//        const ref1 = ref(简单类型) 可以直接监视
//        const ref1 = ref(复杂类型) 监视不到复杂类型内部数据的变化
watch(userInfo,(newValue)=> {
    console.log(newValue)
},{
    deep:true
})



</script>


<template>
    <div>{{ userInfo }}</div>
    <button @click="setUserInfo">change-userInfo</button>
    <div>{{ count }}</div>
    <button @click="changeCount">change-count</button>
    <div>{{ nickname }}</div>
    <button @click="changeName">change-Name</button>
</template>

<style></style>



精确监听对象的某个属性变化

在这里插入图片描述

<script setup>
import {ref, watch} from 'vue'
const count = ref(0)
const nickname = ref('slx')
const changeCount = () => {
    count.value++
}
const changeName = () => {
    nickname.value='hhhhh'
}

const userInfo = ref({
    name:'ssss',
    age:12
})
const setUserInfo = () => {
    // 修改userInfo.value ,修改对象的地址,才能监视到,浅层监视
    // userInfo.value={name:'qqqq',age:10}
    userInfo.value.age++
}


// 5.精确监视,某一属性
watch(() => userInfo.value.age,(newValue, oldValue) => {
    console.log('5555', newValue, oldValue);
    
})
</script>


<template>
    <div>{{ userInfo }}</div>
    <button @click="setUserInfo">change-userInfo</button>
    <div>{{ count }}</div>
    <button @click="changeCount">change-count</button>
    <div>{{ nickname }}</div>
    <button @click="changeName">change-Name</button>
</template>

<style></style>

在这里插入图片描述

浅层与深层监听

在Vue 3中,浅层监听(Shallow Watch)是一种用于监视对象或数组变化的特殊方式。浅层监听只会监视对象或数组的一级属性或元素的变化,而不会递归地监视其内部属性或元素的变化。

浅层监听在使用watch函数或选项时可以通过设置deep选项来控制。默认情况下,deep选项是false,即浅层监听。当deep选项为false时,watch只会监听对象或数组的一级属性或元素的变化。

下面是一个示例,演示了浅层监听的概念:

<template>
  <div>
    <p>浅层监听示例:</p>
    <button @click="updateData">更新数据</button>
  </div>
</template>

<script>
import { ref, watch } from 'vue';

export default {
  setup() {
    const data = ref({
      name: 'John',
      details: {
        age: 30,
      },
    });

    // 浅层监听,只会监听data对象的一级属性的变化
    watch(data, (newData, oldData) => {
      console.log('浅层监听触发');
    });

    const updateData = () => {
      // 修改data对象的一级属性,会触发浅层监听
      data.value.name = 'Jane';
    };

    return {
      data,
      updateData,
    };
  },
};
</script>

在上面的示例中,我们使用ref创建了一个包含嵌套对象的响应式数据data。然后,我们使用watch来监听data对象的变化,但由于默认情况下deep选项是false,所以只有当data对象的一级属性(例如name)发生变化时,浅层监听才会触发,而不会监听details内部对象的属性变化。

总结一下,浅层监听在Vue 3中用于监视对象或数组的一级属性或元素的变化,是一种性能优化的方式,可以避免不必要的递归监听。如果需要深度监听对象或数组的内部属性或元素变化,可以设置deep选项为true,但要谨慎使用,因为它可能会引发性能问题。


组合式API - 生命周期函数

在这里插入图片描述

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

// beforeCreate 和 created 的相关代码
// 一律放在 setup 中执行

const getList = () => {
  setTimeout(() => {
    console.log('发送请求,获取数据')
  }, 2000)
}
// 一进入页面的请求
getList()

// 如果有些代码需要在mounted生命周期中执行
onMounted(() => {
  console.log('mounted生命周期函数 - 逻辑1')
})

// 写成函数的调用方式,可以调用多次,并不会冲突,而是按照顺序依次执行
onMounted(() => {
  console.log('mounted生命周期函数 - 逻辑2')
})

</script>

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



组合式API - 父子通信

父传子 const props = defineProps({

为什么这里的message="xxxx"没加: ,是因为它不是响应式的数据
在这里插入图片描述



App.vue

// 父传子
// 1. 给子组件,添加属性的方式传值
// 2. 在子组件,通过props接收
<script setup>
import {ref} from 'vue'
import MySon from '@/components/MySon.vue'

const money = ref(100)
const getMoney = () =>{
    money.value+=10
}
</script>


<template>
    <h3>father</h3>
    <button @click="getMoney">+money</button>
 <MySon car="宝马" :money="money"></MySon>
</template>

<style></style>


MySon.vue

这里就直接用名字,都没有props.car


<script setup>

// 由于写了setup 所以无法直接配置props选项
// 所以要借助“编译器宏”函数接受子组件传递的数据
// import {ref, watch} from 'vue'
const props = defineProps({
    car:String,
    money:Number
})

</script>


<template>
    <div class="son">
        <h4>Son</h4>
        <h4>{{ car }} {{ money }}</h4>
    </div>
</template>

<style>
.son{
    width: 200px;
    height: 200px;
    background-color: #b17b7b;
}
</style>

在这里插入图片描述



子传父 const emit = defineEmits([

在这里插入图片描述



App.vue

<script setup>
import {ref} from 'vue'
import MySon from '@/components/MySon.vue'

const money = ref(100)
const getMoney = () =>{
    money.value+=10
}

const changeFn = (newMoney) => {
    money.value=newMoney
}
</script>


<template>
    <h3>father</h3>
    <button @click="getMoney">+money</button>
 <MySon 

 @changeMoney="changeFn"
 
 car="宝马" :money="money"></MySon>
</template>

<style></style>


MySon.vue

<script setup>
// 子传父
// 1. 在子组件内部,emit触发事件 (编译器宏获取)
// 2. 在父组件,通过 @ 监听

// import {ref, watch} from 'vue'
const props = defineProps({
    car:String,
    money:Number
})

const emit = defineEmits(['changeMoney'])
const buy = () => {
    emit('changeMoney',5)
}
</script>


<template>
    <div class="son">
        <h4>Son</h4>
        <h4>{{ car }} {{ money }}</h4>
        <button @click="buy">花钱</button>
    </div>
</template>

<style>
.son{
    width: 200px;
    height: 200px;
    background-color: #b17b7b;
}
</style>



在这里插入图片描述




当有多个参数时,就直接放就行

在这里插入图片描述



在这里插入图片描述




组合式API - 模板引用

通过ref标识获取dom对象或者组件实例对象

在这里插入图片描述
在这里插入图片描述

显式暴露组件内部的属性和方法defineExpose()

在这里插入图片描述

在这里插入图片描述




App.vue

<script setup>
import {onMounted, ref} from 'vue'
import TestCom from '@/components/TestCom.vue'


// 模板引用(可以获取dom,也可以获取组件
// 1.调用ref函数,生成ref对象
// 2.通过ref标识,进行绑定
// 3.通过ref对象.value 即可访问到绑定的元素(必须渲染

const inp = ref(null)
onMounted(() => {
    inp.value.focus()
})

const clickFn = () => {
    inp.value.focus()
}

// --------------------------
const testRef = ref(null)
const getCom = () => {
    console.log(testRef.value);
    
}
</script>


<template>
    <input ref="inp" type="text">
    <button @click="clickFn" >focus</button>
    <TestCom ref="testRef" ></TestCom>
    <button @click="getCom">获取组件</button>
</template>

<style></style>


TestCom.vue
<script setup>
import {ref} from 'vue'
const count = 999

defineExpose({
    count
})

</script>


<template>
<div>Test
{{ count }}

</div>
</template>

<style></style>


在父组件中使用defineExpose暴露的子组件数据

MySon.vue

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

const message = ref('Hello from child component');
  
// 使用defineExpose暴露数据
defineExpose({
    message,
})

</script>
<template>
    <div>
      <p>子组件内容:{{ message }}</p>
    </div>
</template>
  


App.vue

  <script setup>
  import MySon from './components/MySon.vue';
  import { ref, onMounted } from 'vue';
      const childRef = ref(null);
      const childMessage = ref('');
  
      // 在组件渲染后,访问子组件暴露的数据
      onMounted(() => {
        childMessage.value = childRef.value.message;
      });
  </script>

<template>
    <div>
      <MySon ref="childRef" ></MySon>
      <p>父组件从子组件获取的消息:{{ childMessage }}</p>
    </div>
  </template>
  

组合式API - provide 和 inject

在这里插入图片描述

1. 传递普通数据

在这里插入图片描述

2. 传递响应式数据

在这里插入图片描述

3.传递方法

在这里插入图片描述

App.vue

<script setup>
import {provide, ref} from 'vue'
import CenterCom from './components/CenterCom.vue';

// 1.夸层传递普通数据
provide('theme-color','black')

// 2.夸层传递响应式数据
const count = ref(100)
provide('count',count)

setTimeout(()=>{
    count.value=666
},1000)

// 3.跨层级修改函数 =》给子孙后代传递可以修改数据的函数
provide('changeCount', (newCount) => {
    count.value=newCount
})

</script>


<template>
    <h1>Top</h1>
    <CenterCom></CenterCom>
</template>

<style></style>


BottomCom.vue
<script setup>
import {inject, ref} from 'vue'

// 1
const themeColor = inject('theme-color')

// 2
const count = inject('count')

// 3.
const changeCount=inject('changeCount')
const clickFn = () => {
    changeCount(1211)
}


</script>


<template>
 <h4>Bottom
    {{ themeColor }}
    {{ count }}
    <button @click="clickFn">更新count</button>
 </h4>
</template>

<style></style>


CenterCom.vue
<script setup>
import {ref} from 'vue'
import BottomCom from './BottomCom.vue';



</script>


<template>
 <h2>Center</h2>
 <BottomCom></BottomCom>
</template>

<style></style>


Vue3.3 新特性 - defineOptions

在这里插入图片描述
在这里插入图片描述在这里插入图片描述

Vue3.3 新特性 - defineModel

在这里插入图片描述还需要配置:
在这里插入图片描述
App.vue

<script setup>
import {ref} from 'vue'
import MySon from '@/components/MySon.vue'
const txt = ref('123456')
</script>


<template>
    <MySon v-model="txt"></MySon>
    {{ txt }}
</template>

<style>

</style>



components / MySon.vue

<script setup>
defineProps({
    modelValue:String
})
// 看来必须是modelValue才行,用其他名字。例如txt都不行

const emit = defineEmits(['update:modelValue'])
const changeTxt = (e) => {
    emit('update:modelValue', e.target.value)
}


</script>


<template>
   <input :value="modelValue"  @input="changeTxt"   type="text" >
</template>

<style>

</style>



Pinia 快速入门

在这里插入图片描述

手动添加Pinia到Vue项目

main.js

// import './assets/main.css'

import { createApp } from 'vue'
import App from './App.vue'



import { createPinia } from 'pinia'


const pinia = createPinia() //创建pinia实例
const app = createApp(App) //创建根实例

app.use(pinia) // pinia插件的安装配置
app.mount('#app') // 视图的挂载




App.vue
<script setup>
import Son1Com from '@/components/Son1Com.vue'
import Son2Com from '@/components/Son2Com.vue'
</script>


<template>
    <div>App.vue跟组件</div>
    <Son1Com></Son1Com>
    <Son2Com></Son2Com>
</template>


Son2Com.vue
<script>

</script>


<template>
   <div>
    Son2
    <button>+</button>
   </div>
   
</template>


Son1Com.vue
<script>

</script>


<template>
   <div>
    Son1
    <button>-</button>
    </div>
</template>


Pinia基础使用

定义store 组件使用store
App.vue

<script setup>
import Son1Com from '@/components/Son1Com.vue'
import Son2Com from '@/components/Son2Com.vue'
import {useCounterStore } from '@/store/counter.js'

const counterStore = useCounterStore()
console.log(counterStore);


</script>


<template>
    <div>App.vue跟组件:
        
{{ counterStore.count }}
    </div>
    <Son1Com></Son1Com>
    <Son2Com></Son2Com>
</template>


Son1Com.vue

<script setup>
import {useCounterStore } from '@/store/counter.js'
const counterStore = useCounterStore()
</script>


<template>
   <div>
    Son1:{{ counterStore.count }}
    <br>
    double:{{ counterStore.double }}
    <button @click="counterStore.subCount">-</button>
    </div>
</template>


Son2Com.vue
<script setup>
import {useCounterStore } from '@/store/counter.js'
const counterStore = useCounterStore()
</script>


<template>
   <div>
    Son2:{{ counterStore.count }}
    <button @click="counterStore.addCount">+</button>
   </div>
   
</template>



store / counter.js
import { defineStore } from 'pinia'
import {ref,computed } from 'vue'
// 定义store
// defineStore (仓库的唯一标识,  () => { ... })

export const useCounterStore = defineStore('counter', ( ) => {
    // 声明数据 state
    const count = ref(0)
    // 声明操作数据的方法 action (普通函数)
    const addCount = () => count.value++
    const subCount = () => count.value--


    // 声明基于数据派生的计算属性getters  (computed)
    const double = computed(() => count.value*2)


    const msg = ref('hello pinia')

    return {
        count,msg,addCount,subCount,double
    }
})


action异步实现

在这里插入图片描述
在这里插入图片描述store / channel.js

import { defineStore } from "pinia"
import { ref} from "vue"
import axios from "axios"

// 命名默认是 use + xxx + Store
export const useChannelStore = defineStore('channel',() => {
    // 声明数据
    const channelList = ref([])
    // 声明操作数据方法
    const getList = async () =>{
        // 支持异步
        const {data:{data}} = await axios.get('http://geek.itheima.net/v1_0/channels')
        console.log(data.channels)
        channelList.value = data.channels

        
    }


    return{
        channelList,getList
    }

    // 声明getters相关
})


App.vue
<script setup>
import Son1Com from '@/components/Son1Com.vue'
import Son2Com from '@/components/Son2Com.vue'
import {useCounterStore } from '@/store/counter.js'
import {useChannelStore } from '@/store/channel.js'

const counterStore = useCounterStore()
console.log(counterStore);

const channelStore =useChannelStore()
</script>


<template>
    <div>App.vue跟组件:
        
{{ counterStore.count }}
    </div>
    <Son1Com></Son1Com>
    <Son2Com></Son2Com>

    <button @click="channelStore.getList">获取频道数据</button>
    <ul>
        <li v-for="item in channelStore.channelList" :key="item.id">
            {{ item.name }}
        </li>
    </ul>
</template>


storeToRefs方法

将 Vuex 或其他状态管理库中的状态对象转换为响应式的 Ref 对象,以便在 Vue 3 组件内部使用,方法直接解构
App.vue

<script setup>
import { storeToRefs } from "pinia"
import Son1Com from '@/components/Son1Com.vue'
import Son2Com from '@/components/Son2Com.vue'
import {useCounterStore } from '@/store/counter.js'
import {useChannelStore } from '@/store/channel.js'

const counterStore = useCounterStore()
console.log(counterStore);

// 直接解构,数据会丢失响应式
const {count,msg} = storeToRefs(counterStore)

const channelStore = useChannelStore()

// 方法直接解构
const { getList } = channelStore
// 属性要storeToRefs
const {channelList} = storeToRefs(channelStore)
</script>


<template>
    <div>App.vue跟组件:{{ count }}
{{msg}}
    </div>
    <Son1Com></Son1Com>
    <Son2Com></Son2Com>

    <button @click="getList">获取频道数据</button>
    <ul>
        <li v-for="item in channelList" :key="item.id">
            {{ item.name }}
        </li>
    </ul>
</template>


Pinia 调试

在这里插入图片描述


Pinia 持久化

1.安装
cnpm i pinia-plugin-persis

2.将插件添加到 pinia 实例上

main.js

import { createApp } from 'vue'
import App from './App.vue'
import { createPinia } from 'pinia'
// 导入持久化插件
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'


const pinia = createPinia()
const app = createApp(App) 
pinia.use(piniaPluginPersistedstate)
app.use(pinia) 
app.mount('#app') 




counter.js
import { defineStore } from 'pinia'
import {ref,computed } from 'vue'
// 定义store
// defineStore (仓库的唯一标识,  () => { ... })

export const useCounterStore = defineStore('counter', ( ) => {
    // 声明数据 state
    const count = ref(0)
    // 声明操作数据的方法 action (普通函数)
    const addCount = () => count.value++
    const subCount = () => count.value--


    // 声明基于数据派生的计算属性getters  (computed)
    const double = computed(() => count.value*2)


    const msg = ref('hello pinia')

    return {
        count,msg,addCount,subCount,double
    }
}, {
    // persist: true,//开启当前模块持久化
    persist: {
        key:'hm-counter',//修改本地存储的唯一标识
        paths:['count'] //存储的是哪些数据
    },

  }

)



在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值