Vue3 [Day11]

Advantages of Vue3





Insert image description here



Insert image description here


create-vue builds Vue3 project

Insert image description here



Insert image description here

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

Vue3 project directory and key files

Insert image description here
The Vetur plug-in is for Vue2 and
the Volarr plug-in is for 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>


==Components do not need to be registered and can be used directly==

Composable API - setup options

setup execution timing

Insert image description here
The execution time is earlier than beforeCreate,
so this cannot be obtained (this is undefined

How to write setup

Insert image description here




Data and functions need to be ==return == at the end of setup before they can be applied in the template

Simple way to write: use setup syntactic sugar, no need to return
Insert image description here


Insert image description here
Insert image description here




Composable API - reactive and ref functions

reactive (not recommended

Insert image description here



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 (more recommended

Insert image description here
Essence: Based on the original incoming data, the outer layer contains a layer of objects, which are wrapped into complex types. After the bottom layer is wrapped into complex types, reactive is used to implement responsiveness. Therefore,
脚本中访问数据,需要通过 .value
在template中, .vue不需要加(帮我们扒了一层)
so
It is recommended to declare data in the future and use ref => unified coding standards.
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>

Insert image description here

What is responsive data and what does it do?

Responsive data refers to data that can automatically update related views when the data changes. In Vue, by converting data into responsive objects, when the data changes, Vue will automatically track these changes and notify the relevant views to update.
The role of reactive data is to enable developers to write code in a declarative manner without the need to manually manipulate the DOM or track data changes. It provides a simple and efficient way to maintain the relationship between data and views, allowing developers to focus on business logic rather than manual view updates.

With responsive data, when the data changes, the related views are automatically updated to keep the data and views in sync. This greatly simplifies the development process and improves the maintainability and readability of the code.

Reactive data is one of the core concepts in Vue, which enables developers to build dynamic, interactive and responsive applications.

What is the difference between responsive data processing in Vue 2 and Vue 3?

In Vue 2, use the data option to declare reactive data, and Vue will convert the data into reactive objects. When the data changes, Vue will automatically update the related views.
In Vue 3, use ref and reactive functions to declare reactive data.



Composable API - computed

Insert image description hereapp.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>

Insert image description here
To select all and invert the selection, you can use get and set.



Composable API - watch

Listen for individual data

Insert image description here

Listen to multiple data

Insert image description here

<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>

Configuration items

Insert image description here

<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>



Accurately monitor changes in certain properties of an object

Insert image description here

<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>

Insert image description here

Shallow and deep monitoring

In Vue 3, Shallow Watch is a special way to monitor changes in objects or arrays. Shallow listening only monitors changes in first-level properties or elements of an object or array, but does not recursively monitor changes in its internal properties or elements.

Shallow listening can be controlled by setting options when using watchfunctions or options . deepBy default, deepthe option is falseshallow listening. When deepthe option falseis , watchonly changes in first-level properties or elements of the object or array will be monitored.

Here is an example that demonstrates the concept of shallow listening:

<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>

In the above example, we refcreated a reactive data containing nested objects using data. We then watchlisten datafor changes to the object using , but since the default deepoption is false, the shallow listening will only fire when dataa first-level property of the object (for example name) changes, and will not listen for detailsproperty changes of the inner object.

in conclusion,Shallow listening is used in Vue 3 to monitor changes in first-level properties or elements of objects or arrays. It is a performance optimization method that can avoid unnecessary recursive listening.. If you need to deeply monitor changes in internal properties or elements of an object or array, you can set deepthe option to true, but use it with caution as it may cause performance issues.


Composable API - lifecycle functions

Insert image description here

<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>



Composable API - Parent-Child Communication

Father 传子 const props = defineProps({

Why is it not added here message="xxxx": , because it is not responsive data
Insert image description here



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

<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>

Insert image description here



子传father const emit = defineEmits([

Insert image description here



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>


Insert image description here



Insert image description here




Composable API - Template Reference

Get the DOM object or component instance object through the ref identifier

Insert image description here
Insert image description here

Explicitly expose the properties and methods inside the component defineExpose()

Insert image description here

Insert image description here




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>


Use defineExpose to expose child component data in the parent component

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>
  

Composable API - provide and inject

Insert image description here

1. Pass ordinary data

Insert image description here

2. Pass responsive data

Insert image description here

3.Transfer method

Insert image description here

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 new features - defineOptions

Insert image description here
Insert image description hereInsert image description here

Vue3.3 new features - defineModel

Insert image description hereAlso need to configure:
Insert image description here
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 Quick Start

Insert image description here

Manually add Pinia to Vue project

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>


Basic use of Pinia

Define the store component using 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 asynchronous implementation

Insert image description here
Insert image description herestore / 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 method

Convert state objects in Vuex or other state management libraries into responsive Ref objects for use within Vue 3 components
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>


Debugging

Insert image description here


Pinia persistence

1.Installation
cnpm i pinia-plugin-persis

2. Add the plug-in to the pinia instance

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'] //存储的是哪些数据
    },

  }

)



Insert image description here

Guess you like

Origin blog.csdn.net/weixin_63681863/article/details/132514310