1.初识Vue3
2.Vue3组合式API
2.1认识create-vue
create-vue是官方新的脚手架工具,vite下一代构建工具
npm init vue@latest
2.2 setup
<script>
export default {
//执行时机比beforeCreate早
//获取不到this
//数据和函数必须return出去
setup() {
//数据
const message = 'hello vue3'
//函数
const logMessage = () => {
console.log(message)
}
return {
message, logMessage
}
},
beforeCreate() {
console.log('beforeCreate函数')
}
}
</script>
<template>
<h1>{{ message }}</h1>
<button @click="logMessage">点我</button>
<script setup>
const message = 'this is message'
const logMessage = () => {
console.log(message);
}
</script>
<template>
<h1>{{ message }}</h1>
<button @click="logMessage">点我</button>
总结:
2.3 reactive()
<script setup>
//接收对象类型数据的参数传入,返回响应式对象
import { reactive } from 'vue'
const state = reactive({
count: 100
})
const addOne = () => {
state.count++
}
</script>
<template>
<div>
<div>{{ state.count }}</div>
<button @click="addOne">+1</button>
</div>
2.4 ref()
本质是对传入数据包了一层,包成复杂类型之后,再借助reactive实现响应式。
<script setup>
//接收对象类型数据的参数传入,返回响应式对象
import { ref } from 'vue'
const count = ref(0)
console.log(count.value);
const addOne = () => {
count.value++
}
</script>
<template>
<div>
<div>{{ count }}</div>
<button @click="addOne">+1</button>
</div>
总结:
2.5 computed
最佳实践:不应该有副作用,异步请求,修改dom,避免直接修改计算属性的值。
<script setup>
import { ref, computed } from 'vue'
//声明数据
const list = ref(
[1, 2, 3, 4, 5, 6, 7, 8]
)
//基于list派生一个计算属性,从list中过滤出 > 2
const computedList = computed(() => {
return list.value.filter(item => item > 2)
})
//定义一个修改数组的方法
const addFn = () => {
list.value.push(666)
}
</script>
<template>
<div>
<div>原始数据:{{ list }}</div>
<div>计算后的数据:{{ computedList }}</div>
<button @click="addFn">修改</button>
</div>
2.6 watch
侦听简单对象和复杂对象以及immediate和deep,精确侦听对象的某个属性
<script setup>
import { ref, watch } from 'vue'
const count = ref(0)
const nickname = ref('张三')
const changeCount = () => {
count.value++
}
const changeNickName = () => {
nickname.value = nickname.value + '三'
}
//1.监视单个数据的变化
//watch(ref对象,(newValue,oldValue)=>{...})
// watch(count, (newValue, oldValue) => {
// console.log(newValue, oldValue)
// })
// watch(nickname, (nv, ov) => {
// console.log(nv, ov);
// })
//监视多个数据的变化
//watch([ref1,ref2],(newArr,oldArr)=>{...})
/* watch([count, nickname], (newArr, oldArr) => {
console.log(newArr, oldArr);
}) */
//立刻执行immediate
/* watch([count, nickname], (newArr, oldArr) => {
console.log(newArr, oldArr);
}, {
immediate: true
}) */
//深度监视 deep,默认是浅层监视,ref简单类型,直接监视,ref2复杂类型,监视不到内部数据变化
const userInfo = ref({
name: 'zs',
age: 18
})
const setUserInfo = () => {
userInfo.value.age++
}
/* watch(userInfo, (nv, ov) => {
console.log(nv, ov)
}, {
deep: true//深度监视,对象子属性变化也能监视到
}) */
//对于对象中的属性来进行监视,固定写法
watch(() => userInfo.value.age, (nv, ov) => {
console.log(nv, ov);
})
</script>
<template>
<div>
{{ count }}
</div>
<button @click="changeCount">改数字</button>
<div>
{{ nickname }}
</div>
<button @click="changeNickName">改昵称</button>
<div>------------------------------------------</div><br>
<div>{{ userInfo }}</div>
<button @click="setUserInfo">修改userinfo</button>
总结:
3.不开启deep,直接监视复杂类型,修改属性,能触发回调吗?
2.7 Vue3生命周期API(选项式 VS 组合式)
选项式 | |
beforeCreate/created | |
beforeMount | |
mounted | |
beforeUpdate | |
updated | |
beforeUnmount | |
unmounted |
<script setup>
import { onMounted } from 'vue';
//beforeCreate和created的相关代码
//一律放在setup中执行
const getList = () => {
console.log('发送请求,获取数据');
}
//一进入页面的请求
getList()
//如果有些代码需要在mounted生命周期中执行
onMounted(() => {
console.log('mounted生命周期函数-逻辑一')
})
onMounted(() => {
console.log('mounted生命周期函数-逻辑二')
})
</script>
<template>
<div>
<h1>啤酒颜色</h1>
</div>
</template>
<style></style>
--------------------
发送请求,获取数据
App.vue:12 mounted生命周期函数-逻辑一
2.8父子传值
父传子
<script setup>
//1.父组件中给子组件绑定属性
//2.子组件内部通过props选项接收
import { ref } from 'vue'
import SonCom from '@/components/son-com.vue'
const money = ref(100)
const getMoney = () => {
money.value += 10
}
</script>
<template>
<div>
<h1>啤酒颜色---{{ money }} <button @click="getMoney">挣钱</button></h1>
<!-- 给子组件添加属性方式传值 -->
<SonCom car="宝马车" :money="money"></SonCom>
</div>
</template>
<script setup>
//1.父组件中给子组件绑定属性
//2.子组件内部通过props选项接收,编译器宏接收子组件传递的数据
const props = defineProps({
car: String,
money: Number
})
console.log(props.car);
</script>
<template>
<div class="son">
<h1>我是子组件-{{ car }}-{{ money }}</h1>
</div>
</template>
<style scoped>
.son {
background-color: pink;
}
子传父
<script setup>
//1.父组件中给子组件绑定属性
//2.子组件内部通过props选项接收
import { ref } from 'vue'
import SonCom from '@/components/son-com.vue'
const money = ref(100)
const getMoney = () => {
money.value += 10
}
const changeFn = (newMoney) => {
money.value -= newMoney
}
//子传父
//1.在子组件内部,emit触发事件
//2.在父组件,通过@监听
</script>
<template>
<div>
<h1>啤酒颜色---{{ money }} <button @click="getMoney">挣钱</button></h1>
<!-- 给子组件添加属性方式传值 -->
<SonCom @changeMoney="changeFn" car="宝马车" :money="money"></SonCom>
</div>
</template>
<script setup>
//1.父组件中给子组件绑定属性
//2.子组件内部通过props选项接收,编译器宏接收子组件传递的数据
const props = defineProps({
car: String,
money: Number
})
console.log(props.car);
const emit = defineEmits(['changeMoney'])
const buy = () => {
emit('changeMoney', 5)
}
</script>
<template>
<div class="son">
<h1>我是子组件-{{ car }}-{{ money }}</h1>
<button @click="buy">花钱</button>
</div>
</template>
<style scoped>
.son {
background-color: pink;
}
2.9模板引用的概念
defineExpose()
默认情况下,<script setup>语法糖下组件内部的属性和方法是不开放给父组件访问的,可以通过defineExpose编译宏指定哪些属性和方法允许访问。
<script setup>
import Test from '@/components/test.vue'
import { ref, onMounted } from 'vue'
//模板引用,获取dom,获取组件
//
//
const inp = ref(null)
//生命周期钩子,onMounted
onMounted(() => {
// console.log(inp.value)
// inp.value.focus()
})
const clickFn = () => {
inp.value.focus()
}
const testRef = ref(null)
const getCom = () => {
console.log(testRef.value)
console.log(testRef.value.count)
testRef.value.sayHi()
}
</script>
<template>
<div>
<input ref="inp" type="text">
<button @click="clickFn">点击让输入框聚焦</button>
<Test ref="testRef"></Test>
<button @click="getCom">获取组件</button>
</div>
</template>
<script setup>
const count = 999
const sayHi = () => {
console.log(打招呼)
}
defineExpose({
count,
sayHi
})
</script>
<template>
<div>
<h1>我是用于测试的组件---{{ count }}</h1>
</div>
</template>
2.9 provide和inject
<script setup>
import { provide, ref } from 'vue'
import CenterCom from './components/center-com.vue'
//1.跨层传递普通数据
provide('theme-color', 'pink')
//2.跨层传递响应式数据
const count = ref(100)
provide('count', count)
setTimeout(() => {
count.value++
}, 2000)
//3.跨层传递函数=》给子孙后代可以修改数据的方法
provide('changeCount', (newCount) => {
count.value = newCount
})
</script>
<template>
<div>
<h1>我是顶层组件</h1>
<CenterCom></CenterCom>
</div>
</template>
<script setup>
import BottomCom from './bottom-com.vue'
</script>
<template>
<div>
<h2>我是中间组件</h2>
<BottomCom></BottomCom>
</div>
</template>
<script setup>
import { inject } from 'vue'
const themeColor = inject('theme-color')
const count = inject('count')
const changeCount = inject('changeCount')
const clickFn = () => {
changeCount(1000)
}
</script>
<template>
<div>
<h3>我是底层组件--{{ themeColor }}--{{ count }}</h3>
<button @click="clickFn">更新count</button>
</div>
</template>
2.10Vue3.3新特性-defineOptions
无法添加平级属性,defineProps与defineEmits这两个宏
引入defineOptions宏,定义OptionsAPI的选项
<script setup>
defineOptions({
name: 'LoginIndex'
})
</script>
<template>
<div>
我是登录页
</div>
</template>
2.11Vue3.3新特性-defineModel
新特性前
<script setup>
import MyInput from '@/components/my-input.vue'
import { ref } from 'vue';
const txt = ref('123456')
</script>
<template>
<div>
<MyInput v-model="txt"></MyInput>
{{ txt }}
</div>
</template>
<script setup>
defineProps({
modelValue: String
})
const emit = defineEmits(['update:modelValue'])
</script>
<template>
<div>
<input type="text" :value="modelValue" @input="e => emit('update:modelValue', e.target.value)">
</div>
</template>
新特性后
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue({
script: {
defineModel: true
}
}),
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
}
<script setup>
import { defineModel } from 'vue'
const modelValue = defineModel()
</script>
<template>
<div>
<input type="text" :value="modelValue" @input="e => modelValue = e.target.value">
</div>
</template>
3.Pinia
3.1什么是Pinia?
在main.js中
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
const pinia = createPinia()//创建pinia实例
const app = createApp(App)//创建根实例
app.use(pinia)
案例
import { defineStore } from "pinia"
import { ref } from "vue"
//定义store
//defineStore(仓库唯一标识,()=>{})
export const userCounterStore = defineStore('counter', () => {
//声明数据
const count = ref(1110)
//声明操作数据的方法 action 普通函数
const addCount = () => {
count.value++
}
const subCount = () => {
count.value--
}
//声明基于数据派生的计算属性
//声明数据state msg
const msg = ref('hello pinia')
return { count, msg, addCount, subCount }
<script setup>
import Son1Com from '@/components/Son1Com.vue'
import Son2Com from '@/components/Son2Com.vue'
import { userCounterStore } from '@/store/counter'
const counterStore = userCounterStore()
console.log(counterStore)
</script>
<template>
<div>
<h3>App.vue根组件 - {{ counterStore.count }} - {{ counterStore.msg }}</h3>
<Son1Com></Son1Com>
<Son2Com></Son2Com>
</div>
</template>
<script setup>
import { userCounterStore } from '@/store/counter'
const counterStore = userCounterStore()
</script>
<template>
<div>
<h3>我是Son1 - {{ counterStore.count }}</h3>
<button @click="counterStore.addCount">+</button>
</div>
</template>
<script setup>
import { userCounterStore } from '@/store/counter'
const counterStore = userCounterStore()
</script>
<template>
<div>
<h3>我是Son2 - {{ counterStore.count }}</h3><button @click="counterStore.subCount">-</button>
</div>
</template>
//声明基于数据派生的计算属性
const double = computed(() => count.value * 2)
3.2 action异步实现
编写方式:异步action函数的写法和组件中获取异步数据的写法完全一致
接口地址:http://geek.itheima.net/v1_0/channels
需求:在Pinia中获取频道列表数据并把数据渲染App组件的模板中
import axios from "axios"
import { defineStore } from "pinia"
import { ref } from "vue"
export const useChannelStore = defineStore('channel', () => {
//声明数据
const channelList = ref([])
//声明操作数据的方法
const getList = async () => {
//支持异步
const { data: { data } } = await axios.get('http://geek.itheima.net/v1_0/channels')
channelList.value = data.channels
console.log(data.channels);
}
//声明getters相关
return {
channelList, getList
}
<script setup>
import Son1Com from '@/components/Son1Com.vue'
import Son2Com from '@/components/Son2Com.vue'
import { userCounterStore } from '@/store/counter'
import { useChannelStore } from '@/store/channel'
const counterStore = userCounterStore()
const channelStore = useChannelStore()
</script>
<template>
<div>
<h3>App.vue根组件 - {{ counterStore.count }} - {{ counterStore.msg }}</h3>
<Son1Com></Son1Com>
<Son2Com></Son2Com>
<hr>
<button @click="channelStore.getList">获取频道数据</button>
<ul>
<li v-for="item in channelStore.channelList" :key="item.id">{{ item.name }}</li>
</ul>
</div>
</template>
3.3 storeToRefs 保持响应式
//此时直接解构,不处理,数据会丢失响应式
const { count, msg } = storeToRefs(counterStore)
//方法可以直接解构
const { channelList } = storeToRefs(channelStore)
const { getList } = channelStore
3.4 Pinia持久化插件
官方文档:Home | pinia-plugin-persistedstate
安装:npm i pinia-plugin-persistedstate
将插件添加到 pinia 实例上
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)
import { createApp } from 'vue'
import { createPinia } from 'pinia'
//导入持久化插件
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
import App from './App.vue'
const pinia = createPinia()//创建pinia实例
//pinia插件安装配置
pinia.use(piniaPluginPersistedstate)
const app = createApp(App)//创建根实例
app.use(pinia)
用法,组合式
import { defineStore } from 'pinia'
export const useStore = defineStore(
'main',
() => {
const someState = ref('你好 pinia')
return { someState }
},
{
persist: true,
}
, {
persist: true
persist: {
key: 'hm-counter',//Key 用于引用 storage 中的数据
paths: ['count']//哪些数据需要被持久化
3.5 总结
4.Vue3大事件管理系统
Vue3 compositionAPI Pinia/Pinia持久化处理Element Plus(表单校验,表格处理,组件封装) pnpm包管理升级 Eslint + prettier更规范的配置
husky(Git hooks工具)代码提交之前,进行校验请求模块设计VueRouter4路由设计AI大模型开发一整个项目模块(掌握最新的开发方式)
4.1pnpm包管理器-创建项目
pnpm - 速度快、节省磁盘空间的软件包管理器 | pnpm中文文档 | pnpm中文网
- pnpm install
- pnpm add axios
- pnpm add axios -D
- pnpm remove axios
- pnpm dev
4.2Eslint配置代码风格
官网:Prettier · Opinionated Code Formatter
4.3配置代码检查工作流
2.初始化husky工具配置,执行pnpm dlx husky-init&&pnpm install即可
暂存区eslint校验
1.安装lint-staged包:pnpm i lint-staged -D
"lint-staged":{
"*.{js,ts,vue}":[
"eslint --fix"
]
4.4目录调整
4.5 vue-router4路由代码解析
路由初始化D:\front-projects\Vue3-big-event-admin\src\router\index.js
import { createRouter, createWebHistory,createWebHashHistory } from 'vue-router'
//创建路由实例
//1.history模式:createWebHistory 地址栏不带#号
//2.hash模式:createWebHashHistory 地址栏带#号
// console.log(import.meta.env.DEV)
//vite中的环境变量 vite.config.js base:'/',
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: []
})
D:\front-projects\Vue3-big-event-admin\src\App.vue
<script setup>
//vue3中,获取路由对象router useRouter 获取路由参数route useRoute
import { useRouter,useRoute } from 'vue-router'
const router = useRouter()
const route = useRoute()
const goList = ()=>{
router.push('/list')
console.log(router,route)
}
</script>
<template>
<div>
我是APP
<button @click="$router.push('/home')">跳首页</button>
<button @click="goList">跳列表页</button>
</div>
</template>
4.6 引入Element Plus组件库
2.配置按需导入:官方文档:https://element-plus.org/zh-CN/guide/quickstart.html
按需导入:npm install -D unplugin-vue-components unplugin-auto-import
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// vite.config.ts
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
AutoImport({
resolvers: [ElementPlusResolver()],
}),
Components({
resolvers: [ElementPlusResolver()],
}),
],
base:'/',
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
}
4.7 Pinia构建仓库和持久化
D:\front-projects\Vue3-big-event-admin\src\stores\user.js
import {defineStore} from 'pinia'
//用户模块 token setToken removeToken
export const useUserStore = defineStore('big-user',()=>{
const token = ref('')
const setToken = (newToken)=>{
token.value = newToken
}
const removeToken = ()=>{
token.value = ''
}
return {token,setToken,removeToken}
<script setup>
//vue3中,获取路由对象router useRouter 获取路由参数route useRoute
import { useRouter,useRoute } from 'vue-router'
import { useUserStore } from '@/stores/user.js'
const router = useRouter()
const route = useRoute()
const goList = ()=>{
router.push('/list')
console.log(router,route)
}
const userStore = useUserStore()
</script>
<template>
<div>
我是APP
<test-demo></test-demo>
<el-button @click="$router.push('/home')">跳首页</el-button>
<el-button @click="goList">跳列表页</el-button>
<el-button type="primary">Primary</el-button>
<el-button type="success">Success</el-button>
<p>{{ userStore.token }}</p>
<el-button @click="userStore.setToken('Bearer kjksdjfllsdjflsdjflsdj')">登录</el-button>
<el-button @click="userStore.removeToken()">退出</el-button>
</div>
</template>
如果要持久化,则安装: pinia-plugin-persistedstate
Home | pinia-plugin-persistedstate
pnpm add pinia-plugin-persistedstate -D
import persist from 'pinia-plugin-persistedstate'
app.use(createPinia().use(persist))
,{
persist : true
4.8 仓库统一管理
pinia独立维护
D:\front-projects\Vue3-big-event-admin\src\stores\index.js
import { createPinia } from 'pinia'
import persist from 'pinia-plugin-persistedstate'
const pinia = createPinia()
pinia.use(persist)
D:\front-projects\Vue3-big-event-admin\src\main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import pinia from './stores'
import '@/assets/main.scss'
const app = createApp(App)
app.use(pinia)
app.use(router)
仓库统一导出
//main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import pinia from './stores'
import '@/assets/main.scss'
const app = createApp(App)
app.use(pinia)
app.use(router)
app.mount('#app')
//index.js
import { createPinia } from 'pinia'
import persist from 'pinia-plugin-persistedstate'
const pinia = createPinia()
pinia.use(persist)
export default pinia
// import { useUserStore } from './modules/user'
// export { useUserStore }
export * from './modules/user'//接收所有的按需导出
// export * from './modules/counter'
//App.vue
import { useUserStore } from '@/stores'