Advantages of Vue3
create-vue builds Vue3 project
node -v
npm init vue@latest 或者是yarn create vue@latest
npm install
Vue3 project directory and key files
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
The execution time is earlier than beforeCreate,
so this cannot be obtained (this is undefined
How to write setup
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
Composable API - reactive and ref functions
reactive (not recommended
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
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>
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
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>
To select all and invert the selection, you can use get and set.
Composable API - watch
Listen for individual data
Listen to multiple data
<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
<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
<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>
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 watch
functions or options . deep
By default, deep
the option is false
shallow listening. When deep
the option false
is , watch
only 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 ref
created a reactive data containing nested objects using data
. We then watch
listen data
for changes to the object using , but since the default deep
option is false
, the shallow listening will only fire when data
a first-level property of the object (for example name
) changes, and will not listen for details
property 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 deep
the option to true
, but use it with caution as it may cause performance issues.
Composable API - lifecycle functions
<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
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>
子传father 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>
Composable API - Template Reference
Get the DOM object or component instance object through the ref identifier
Explicitly expose the properties and methods inside the component 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>
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
1. Pass ordinary data
2. Pass responsive data
3.Transfer method
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
Vue3.3 new features - defineModel
Also need to configure:
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
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
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 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
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'] //存储的是哪些数据
},
}
)