Vue2+Vue3 130~180集(Vue3)学习笔记
一、create-vue搭建vue3项目
create-vue是vue官方新的脚手架工具,底层切换到了vite
步骤:
- 查看环境条件
node -v版本需要在16.0及以上 - 创建一个vue应用
npm init vue@latest
这一指令会安装并执行create-vue
二、项目目录和关键文件
- index.html提供挂载点
- src/assets图片、样式文件的目录
- components封装的小组件
- App.vue中
template和script位置调换
template中允许有多个根元素
使用组件时不需要注册了,引入后直接就可以使用 - main.js
基于crate-vue创建实例
通过mount设置挂载点
三、组合式API
1、setup选项
- 执行时机早于beforecreate
- setup函数中获取不到this(this是undefined的)
- 特点:可以提供数据也可以提供函数,但是都要return才能在模板中应用
如何化简?<script setup>
语法糖简化代码 只需定义不需要reture(语法糖自动帮我们reture)
2、reactive和ref函数
(1)reactive
作用:接收对象类型数据的参数传入并返回一个响应式的对象
核心步骤:
- 从vue包中导入reactive函数
- 在
<script setup>
中执行reactive函数并传入类型为对象的初始值,并使用变量接收返回值
<script setup>
import {reactive} from 'vue'
const state = reactive({
count:100
})
</script>
(2)ref
作用:接收简单类型或者对象类型的数据传入并返回一个响应式的对象
核心步骤:
- 从vue包中导入ref函数
- 在
<script setup>
中执行ref函数并传入初始值,使用变量接收ref函数的返回值
本质:是在原有传入数据的基础上,外层包了一层对象,包成了复杂数据类型,包成复杂类型之后再借助reactive实现的响应式,注意:脚本中访问简单的数值数据,需要通过.value,在template模板中,不需要加.value
推荐以后声明数据统一使用ref
3、computed(计算属性)
核心步骤:
- 导入computed函数
- 执行函数在回调参数中
<script setup>
import {computed} from 'vue'
const computedState = computed(()=>{
return 基于响应式数据做计算之后的值
})
</script>
4、watch函数
作用:侦听一个或多个数据的变化,数据变化时执行回调函数
(1)侦听单个数据
- 导入watch函数
- 执行watch函数传入要侦听的响应式数据(ref对象)和回调函数
<script setup>
import {watch} from 'vue'
const count = ref(0)
watch(count,(newValue,oldValue)=>{
console.log('count发生了变化')
})
</script>
(2)侦听多个数据
<script setup>
import {watch} from 'vue'
const count = ref(0)
watch(
[count,name],
([newCount,newName],[oldCount,oldName])=>{
console.log('count发生了变化')
}
)
</script>
(3)immediate立刻执行
一进页面立刻执行一次
<script setup>
import {watch} from 'vue'
const count = ref(0)
watch(count,(newValue,oldValue)=>{
console.log('count发生了变化')
},{
immediate: true
})
</script>
(4)deep深度监视
一进页面立刻执行一次
<script setup>
import {watch} from 'vue'
const count = ref(0)
watch(count,(newValue,oldValue)=>{
console.log('count发生了变化')
},{
deep: true
})
</script>
默认watch是浅层监视,即
ref(简单类型)可以直接监视,ref(复杂类型)监视不到复杂类型内部数据的变化 除非复杂类型的地址被修改
(5)精确侦听对象的某个属性
精确监听age属性
watch(()=>userInfo.value.age,(newValue,oldValue)=>{
console.log(newValue,oldValue)
})
三、生命周期函数
四、父子通信
(1)父传子
步骤:1.在父组件中,以给子组件添加属性的方式传值 2.在子组件中,通过props接收(借助编译器宏)
注意:子组件中script由于写了setup。所以无法直接配置props,需要通过编译器宏
const props = defineProps({
car:String,
money:Number
})
(2)子传父
步骤:1.父组件中给子组件标签通过@绑定事件 2.子组件内部通过emit方法触发事件(借助编译宏)
//App.vue
<script setup>
import SonCom from '@/components/son-com.vue'
import {ref} from 'vue'
const money = ref(100)
const getMoney=()=>{
money.value+=10
}
const changeFn=(newMoney)=>{
money.value=newMoney
}
</script>
<template>
<div>
<h3>
父组件 - {{ money }}
<button @click="getMoney">挣钱</button> 子传父1.绑定自定义事件
</h3>
<SonCom
car="宝马车"
:money="money" 父传子1.给子组件添加属性
@changeMoney="changeFn"></SonCom> 子传父4.在父组件中通过@事件名监听
</div>
</template>
<style scoped>
</style>
//son-com.vue
<script setup>
//由于写了setup,所以无法直接配置props选项
//需要借助编译器宏,接收子组件传递的数据
const props = defineProps({ 父传子2.通过编译器宏生成props方法
car:String,
money:Number
})
const emit = defineEmits(['changeMoney']) 子传父2.通过编译器宏生成emit方法
console.log(props);
const buy=()=>{ 子传父3.触发自定义事件,传递参数
emit('changeMoney',5)
}
</script>
<template>
<!-- 对于props传递过来的数据,模板中可以直接使用 -->
<div class="son">
我是子组件 - {{ car }} - {{ money }} 父传子3.模板中可以直接使用通过props传递过来的数据/props.xxx使用
<button @click="buy">花钱</button>
</div>
</template>
<style scoped>
.son {
border:1px solide black;
padding:30px;
}
</style>
五、模版引用
通过ref标识获取真实的dom对象或者组件实例对象
步骤:
- 调用ref函数生成一个ref对象
- 通过ref标识绑定ref对象到标签
- 通过ref对象.value即可访问到绑定的元素(dom必须渲染完)
默认情况下<script setup>
语法糖下组件内部的属性和方法是不开放给父组件访问的,可以通过defineExpose编译宏指定哪些属性和方法允许访问
//App.vue
<script setup>
import TestCom from '@/components/test-com.vue'
import {onMounted,ref} from 'vue'
// 模版引用(可以获取dom也可以获取组件)
// 1.调用ref函数,生成一个ref对象
// 2.通过ref标识,进行绑定
// 3.通过ref对象.value即可访问到绑定的元素(必须渲染)
const inp = ref(null)
//生命周期钩子,作用是确保dom渲染完
onMounted(()=>{
// console.log(inp.value)
// inp.value.focus()
})
const clickFn = () => {
inp.value.focus()
}
const testRef = ref(null)//调用ref函数生成一个ref对象
const getCom = () => {
console.log(testRef.value.count)
testRef.value.sayHi()
}
</script>
<template>
<div>
<input ref='inp' type="text">
<button @click="clickFn">点击让输入框聚焦</button>
</div>
<!-- 绑定ref对象到标签 -->
<TestCom ref="testRef"></TestCom>
<button @click='getCom'>获取组件</button>
</template>
<style scoped>
</style>
//test-com.vue
<script setup>
const count = 999
const sayHi = () => {
console.log('打招呼')
}
defineExpose({
count,
sayHi
})
</script>
<template>
<div>
我是用于测试的组件-{{ count }}
</div>
</template>
<style scoped>
</style>
六、provide和inject
作用:顶层组件向任意的底层组件传递数据和方法,实现跨层组件通信
顶层组件通过provide
提供数据,底层组件通过inject
获取数据
传普通数据步骤:
- 通过provide函数提供数据
provide('key',顶层组件中的函数)
- inject函数获取数据
const message = inject('key')
传响应式数据步骤:
- 通过provide函数提供数据
const count = ref(1000)
provide('count',count)
- inject函数获取数据
const count = inject('count')
跨层传递方法
顶层组件可以向底层组件传递方法,底层组件调用方法可向顶层组件传参或修改顶层组件中的数据
//App.vue
const setCount=()=>{
count.value++
}
provide('setCount-key',setCount)
//bottom-com.vue
const setCount=inject('setCount-key')
//App.vue
<script setup>
import CenterCom from '@/components/center-com.vue'
import { provide,ref } from 'vue'
//1.跨层传递普通数据
provide('theme-color','pink')
//2.跨层传递响应式数据
const count = ref(100)
provide('count',count)
setTimeout(() => {
count.value=500
}, 2000)
//3.跨层级传递函数=>给子孙后代传递可以修改数据的方法
provide('changeCount' , (newCount) => {
count.value = newCount
})
</script>
<template>
<div>
<h1>我是顶层组件</h1>
<CenterCom></CenterCom>
</div>
</template>
<style scoped>
</style>
//center-com.vue
<script setup>
import BottomCom from './bottom-com.vue'
</script>
<template>
<div>
<h2>我是中间层组件</h2>
<BottomCom></BottomCom>
</div>
</template>
<style scoped>
</style>
//bottom-com.vue
<script setup>
import {inject} from 'vue'
const themeColor=inject('theme-color')
const count=inject('count')
const changeCount=inject('changeCount')
const clickFn=()=>{
changeCount(10000)
}
</script>
<template>
<div>
<h3>我是底层组件-{{ themeColor }}-{{ count }}</h3>
<button @click="clickFn">更新count</button>
</div>
</template>
<style scoped>
</style>
七、新特性defineOptions宏
可以定义除props、emits、expose、slots之外的任意选项
<script setup>
defineOptions({
name:'xxx',
inheritAttrs:false,
...其他更多自定义属性
})
</script>
八、新特性defineModel
<Child v-model=''>
等于
<Child :modelValue='' @update:modelValue=''>
//父
const txt = ref('12345')
<MyInput v-model='txt'></MyInput>
//子
const modelValue=defineModel()
<input
type='text'
:value="modelValue"
@input="e=>modelValue=e.target.value"
九、Pinia
1、介绍
Pinia是Vue的最新的状态管理工具,是Vuex的替代品
- 去掉了mutations(有state、actions(既支持同步也支持异步)、getters)
- 提供了符合组合式风格的API(和Vue3新语法统一)
- 去掉了moduls概念,每一个store都是一个独立的模块
- 配合TypeScript更友好
2、添加Pinia到Vue项目
- 使用vite创建一个空的vue3项目
npm create vue@latest
- 按照官方文档安装pinia到项目中
- 安装 npm install pinia
- 导入
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
const pinia = createPinia()
const app = createApp(App)
app.use(pinia)
app.mount('#app')
3、Pinia基本语法
//counter.js
import {defineStore} from 'pinia'
//定义store
//defineStore(仓库的唯一标识,()=>{})
export const useCounterStore = defineStore('counter',()=>{ 基于useCounterStore函数就可以使用defineStore仓库
//声明数据 state-count
const count = ref(0)
//声明操作数据的方法action(普通函数)
const addCount = () => count.value++
//声明基于数据派生的计算属性getters(computed)
const double = computed(()=>count.value*2)
//声明数据 state-msg
const msg = ref('hello pinia')
return {
count,
addCount,
double
...
msg,
}
})
//App.vue
import {useCounterStore} from ''
const counterStore = useCounterStore() //返回一个对象
{{counterStore.count}}-{{counterStore.double}}
<button @click="counterStore.addCount">+</button>
4、action异步实现
//channels.js
import {defineStore} from 'pinia'
export const useChannelStore = defineStore('channel',()=>{
//声明数据
const channelList = ref([])
//声明操作数据的方法
const getList = async=>{
//支持异步(直接写异步请求)
const {data:{data}}=await axios.get('')
channelList.value = data.channels
console.log(data.channels)
}
//声明getters相关
return{
channelList,
getList
}
})
//App.vue
import {useChannelStore} from ''
const channelStore = useChannelStore()
<button @click='channelStore.getList'>获取频道数据</button>
<ul>
<li v-for='item in channelStore.channelList' :key='item.id'>{{item.name}}</li>
</ul>
5、Pinia-storeToRefs方法
不能直接对store进行解构,会丢失响应式,需要使用storeToRefs方法
import {storeToRefs} from 'Pinia'
const {count,msg}=storeToRefs(counterStore)
action可以直接解构
6、Pinia持久化
https://prazdevs.github.io/pinia-plugin-persistedstate/zh/guide/config.html
作用:操作后会将配置了持久化的数据存入本地,即刷新页面后数据也不会重置
- 安装
npm i pinia-plugin-persistedstate
- 将插件添加到pinia实例中
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)
- 在需要配置pinia持久化的组件中配置
import { defineStore } from 'pinia'
import { ref } from 'vue'
export const useStore = defineStore(
'main',
() => {
const someState = ref('hello pinia')
return { someState }
},
//在声明并return完后进行配置
{
persist: true,
},
)
指定state中哪些数据需要持久化
import { defineStore } from 'pinia'
export const useStore = defineStore('store', {
state: () => ({
save: {
me: 'saved',
notMe: 'not-saved',
},
saveMeToo: 'saved',
}),
persist: {
pick: ['save.me', 'saveMeToo'],
},
})