1.使用create-vue创建项目
npm init vue@latest
项目目录和关键文件:
new Vue() 创建一个应用实例 => createApp()、createRouter() createStore() 、将创建实例进行了封装,保证每个实例的独立封闭性。
禁用vue2的插件vuter
使用vue3的插件volar
script写在最前面
不再要求唯一根元素
加上setup允许在script中直接编写组合式API
将创建的实例往id为app的盒子上挂载
createApp(App).mount('#app')
2.setup选项
- setup应写在beforeCreate()之前
- setup函数中,获取不到this (this是undefined)
- 数据 和 函数,需要在 setup 最后 return,才能模板中应用
<script>
export default {setup () {// 数据const message = 'hello Vue3'// 函数const logMessage = () => {console.log(message)}return {message,logMessage}},
}
</script>
<template><div>{{ message }}</div><button @click="logMessage">按钮</button>
</template>
通过 setup 语法糖简化代码
<script setup>
const message = 'this is a message'
const logMessage = () => {console.log(message)
}
</script>
这样就不用return了
3.reactive和ref函数
reactive作用:接收对象类型传参并返回一个响应式的对象
对象类型
<script setup>
import {reactive} from 'vue'
const state = reactive({count:100
})
const addCount = () => {state.count++
}</script><template><div><div>{{ state.count }}</div><button @click="addCount">+1</button></div>
</template>
ref: 接收简单类型 或 复杂类型,返回一个响应式的对象
本质:是在原有传入数据的基础上,外层包了一层对象,包成了复杂类型底层,包成复杂类型之后,再借助 reactive 实现的响应式。
注意点:
- 脚本中访问数据,需要通过 .value
- 在template中,.value不需要加 (帮我们扒了一层)
简单类型
<script setup>
import { ref } from 'vue'
const name = ref(0)
const setName = () => {name.value++
}
</script><template><div><div>{{ name }}</div><button @click="setName">+1</button></div>
</template>
推荐:以后声明数据,统一用 ref => 统一了编码规范
4.computed
const 计算属性 = computed(() => {return 计算返回的结果
})
<script setup>
import { computed, ref } 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 computedList2 = computed(() => {return list.value.filter(item => item > 3)
})//这时候就不用return
const AddNumber = () => {list.value.push(999)
}
// 定义一个修改数组的方法
const addFn = () => {list.value.push(666)
}
</script><template><div><div>原始数据: {{ list }}</div><div>计算后的数据: {{computedList2 }}</div><div>原始数据: {{ list }}</div><div>计算后的数据: {{ computedList }}</div><button @click="AddNumber" type="button">修改2/</button><button @click="addFn" type="button">修改</button></div>
</template>
5.watch函数
- 监视单个数据的变化
// watch(ref对象, (newValue, oldValue) => { ... })watch(count, (newValue, oldValue) => {console.log(newValue, oldValue)
})
- 监视多个数据的变化
// watch([ref对象1, ref对象2], (newArr, oldArr) => { ... })watch([count, nickname], (newArr, oldArr) => {console.log(newArr, oldArr)
})
- immediate 立刻执行(一进页面立刻触发)
watch(count, (newValue, oldValue) => {console.log(newValue, oldValue)
}, {immediate: true
})
- deep 深度监视, 默认 watch 进行的是 浅层监视
const ref1 = ref(简单类型) 可以直接监视
const ref2 = ref(复杂类型) 监视不到复杂类型内部数据的变化
deep 深度监视
watch(userInfo, (newValue) => {console.log(newValue)
}, {deep: true
})
- 对于对象中的单个属性,进行精确监视
只监视age不监视name
watch(() => userInfo.value.age, (newValue, oldValue) => {console.log(newValue, oldValue)
})
6.生命周期函数
写成函数的调用方式,可以调用多次,并不会冲突,而是按照顺序依次执行
onMounted(() => {console.log('mounted生命周期函数 - 逻辑2')
})
7.组合式下的父子通信
父传子
子组件中
注意:由于写了 setup,所以无法直接配置 props 选项
所以:此处需要借助于 “编译器宏” 函数接收子组件传递的数据
<template><!-- 对于props传递过来的数据,模板中可以直接使用 --><div class="son">我是子组件 - {{ car }} - {{ money }}<button @click="buy">花钱</button></div>
</template>
父组件
<template><div><h3>父组件 - {{ money }}<button @click="getMoney">挣钱</button></h3><!-- 给子组件,添加属性的方式传值 --><SonCom car="宝马车" :money="money"></SonCom></div>
</template>
子传父
子组件
const emit = defineEmits(['changeMoney'])
const buy = () => {emit('changeMoney', 5)
}
<template><!-- 对于props传递过来的数据,模板中可以直接使用 --><div class="son">我是子组件 - {{ car }} - {{ money }}<button @click="buy">花钱</button></div>
</template>父组件
const changeFn = (newMoney) => {money.value = newMoney
}
<template><div><h3>父组件 - {{ money }}<button @click="getMoney">挣钱</button></h3><!-- 给子组件,添加属性的方式传值 --><SonCom@changeMoney="changeFn"car="宝马车":money="money"></SonCom></div>
</template>
8.模板引用
模板引用(可以获取dom,也可以获取组件)
- 调用ref函数,生成一个ref对象
- 通过ref标识,进行绑定
- 通过ref对象.value即可访问到绑定的元素(必须渲染完成后,才能拿到)
模板引用的时机:组件挂载完毕
defineExpose编译宏的作用:显示暴露组件内部的属性和方法
<script setup>
import TestCom from '@/components/test-com.vue'
import { onMounted, ref } from 'vue'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.count)testRef.value.sayHi()
}</script><template><div><input ref="inp" type="text"><button @click="clickFn">点击让输入框聚焦</button></div><TestCom ref="testRef"></TestCom><button @click="getCom">获取组件</button>
</template>
9.provide和inject
为了跨层级组件通信
- 顶层组件通过provide函数提供数据
- 底层组件通过inject函数获取数据
顶层组件
<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>
底部组件
<script setup>
import { inject } from 'vue'
const themeColor = inject('theme-color')
// const themeColor = inject('theme-color')
const count = inject('count')
const change = inject('change')
const clickFn1 = () => {change(200)
}
const changeCount = inject('changeCount')
const clickFn = () => {changeCount(1000)
}
</script><template>
<div><h3>我是底层组件-{{ themeColor }} - {{ count }}</h3><button @click="clickFn">更新count</button><button @click="clickFn1">更新count</button></div>
</template>
10.defineOptions
可以用defineOptions定义任意的选项,(prop,emit,expose,slots除外)
defineOptions({name: 'IndexPage'
})
11.defineModel
<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>
使用defineModel需要加配置
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))}}
})
12.Pinia
是vuex的替代品
- 有更简单的API
- 与vue3新语法统一
- 去掉了modules的概念,每个store都是独立的模块
- 对TypeScript更友好
13.添加Pinia到vue项目
生成
import { defineStore } from 'pinia'
import { computed, ref } from 'vue'// 定义store
// defineStore(仓库的唯一标识, () => { ... })
export const useCounterStore = defineStore('counter', () => {// 声明数据 state - countconst count = ref(100)// 声明操作数据的方法 action (普通函数)const addCount = () => count.value++const subCount = () => count.value--// 声明基于数据派生的计算属性 getters (computed)const double = computed(() => count.value * 2)// 声明数据 state - msgconst msg = ref('hello pinia')return {count,msg,}
}
})
使用(导入后可直接使用)
<script setup>
import { useCounterStore } from '@/store/counter'
const counterStore = useCounterStore()
</script><template><div>我是Son1.vue - {{ counterStore.count }} - {{ counterStore.double }}<button @click="counterStore.addCount">+</button></div>
</template>
14.action异步
import { defineStore } from 'pinia'
import { ref } from 'vue'
import axios from 'axios'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}// 声明getters相关return {channelList,getList}
})
15.storeToRef方法
import { storeToRefs } from 'pinia'
// 此时,直接解构,不处理,数据会丢失响应式
// 加storeToRefs就不丢失
const { count, msg } = storeToRefs(counterStore)
16.Pinia持久化
安装插件,导入插件,persist: true就行了
export const useCounterStore = defineStore('counter', () => {// 声明数据 state - msgconst msg = ref('hello pinia')return {count,double,addCount,subCount,msg,}
}, {// persist: true // 开启当前模块的持久化persist: {key: 'hm-counter', // 修改本地存储的唯一标识paths: ['count'] // 存储的是哪些数据}
})
17.大事件
createRouter 创建路由实例
配置 history 模式
- history模式:createWebHistory 地址栏不带 #
- hash模式: createWebHashHistory 地址栏带 #
console.log(import.meta.env.DEV)
vite 中的环境变量 import.meta.env.BASE_URL 就是 vite.config.js 中的 base 配置项
在 Vue3 CompositionAPI 中
- 获取路由对象 router useRouter
const router = useRouter() - 获取路由参数 route useRoute
const route = useRoute()