目录
- Vue3
- Vue3优势
- Vue3组合式API & Vue2选项式API
- create-vue
- 使用create-vue创建项目
- 项目目录和关键文件
- 组合式API-setup选项
- 组合式API-reactive和ref函数
- reactive()
- ref()
- 组合式API-computed
- 组合式API-watch
- 基础使用
- immdiate和deep配置
- 精确侦听对象的某个属性
- 组合式API-生命周期函数
- 组合式API-父子通信
- 组合式API- ref模板引用
- 组合式API-provide和inject
- Vue3.3新特性-defineOptions
- Vue3.3新特性-defineModel
- pinia
- pinia基本语法
- storeToRefs方法
- pinia持久化
Vue3
Vue3优势
- 更易维护
- 组合式API
- 更好的TypeScript支持
- 更快的速度
- 重写diff算法
- 模板编译优化
- 更高效的组件初始化
- 更小的体积
- 良好的TreeShaking
- 按需引入
- 更优的数据响应式
- Proxy
Vue3组合式API & Vue2选项式API
Vue3组合式API:
- 代码量变少
- 分散式维护转为了集中式维护,更易封装复用
create-vue
create-vue式Vue官方新的脚手架工具,底层切换到了vite(下一层构建工具),为开发提供极速响应
Vue2:Vue-CLI底层是webpack
Vue3:create-vue底层是vite
使用create-vue创建项目
- 前提环境条件:已安装16.0或更高版本的Node.js,node -v
- 创建一个Vue应用
npm init vue@latest
,这一指令会安装并执行create-vue
项目目录和关键文件
vue2安装vetur插件,vue3安装Vue - Official插件
关键文件:
- vite.config.js - 项目的配置文件 基于vite的配置
- package.json - 项目包文件 核心依赖项变成了Vue3.x和vite
- main.js - 入口文件 createApp函数构建应用实例
- app.vue - 根组件 SFC单文件组件script-template-style
- 变化一:脚本script和模板template顺序调整
- 变化二:模板不再要求唯一根元素
- 变化三:脚本script添加setup标识支持组合式API
- index.html - 单页入口 提供id为app的挂载点
组合式API-setup选项
- 语法:
<script>
export default {setup () {},beforeCreate () {}
}
</script>
- 特点:
- 执行时机比beforeCreate更早
- setup函数中,获取不到this,即this是undefined
- 数据和函数,需要在setup最后return,才能在模板中应用
- 可使用setup语法糖简化代码
- setup语法糖
- 语法:
原写法:
语法糖写法:给script标签添加setup属性,不需要return<script> export default {setup () {<!-- 准备数据 -->const name = 'xiaoyang'<!-- 函数 -->const getName = () => {console.log('我的name是xiaoyang')}return {name,getName}} } </script>
<script setup> <!-- 准备数据 --> const name = 'xiaoyang' <!-- 函数 --> const getName = () => {console.log('我的name是xiaoyang') } </script>
- 原理
- 语法:
组合式API-reactive和ref函数
reactive()
- 作用: vue3默认并不是响应式的,reactive()可以接收对象类型数据的参数传入并返回一个响应式的对象
- 核心步骤:
- 从vue包中导入reactive函数
- 在
<script setup>
中执行reactive函数并传入类型为对象的初始值,使用变量接收返回值
<script setup> // 导入 import { reactive } from 'vue' // 执行函数 传入参数 变量接收 const state = reactive(对象类型数据) </script>
ref()
- 作用:ref()可以接收简单数据类型或对象类型数据的参数传入并返回一个响应式的对象
- 核心步骤:
- 从vue包中导入ref函数
- 在
<script setup>
中执行ref函数并传入初始值,使用变量接收返回值
<script setup> // 导入 import { ref } from 'vue' // 执行函数 传入参数 变量接收 const state = ref(数据) </script>
- 本质:在原有传入数据的基础上,外层包了一层对象,包成了复杂数据类型
【注意】:脚本中访问数据,需要通过.value
,而在template中,可以直接访问
组合式API-computed
- 核心步骤:
- 从vue包中导入computed函数
- 执行函数在回调参数中return基于响应式数据做计算的值,用变量接收
【注意】:① 计算属性不应该有副作用,eg:异步请求/修改dom ② 避免直接修改计算属性的值,计算属性应该是只读的,特殊情况可以配置get、set<script setup> // 导入 import { computed } from 'vue' // 执行函数 变量接收 在回调参数中return计算值 const computedState = computed(() => {return 基于响应式数据做计算的值 }) </script>
- 在computed中使用get、set
<script setup> import { computed } from 'vue' const computedState = computed({get:() => {},set: () => {} }) </script>
组合式API-watch
基础使用
- 侦听单个数据
- 从vue包中导入watch函数
- 执行watch函数传入要侦听的响应式数据(ref对象)和回调函数
<script setup> import { watch } from 'vue' watch(ref对象, (newValue, oldValue) => {}) </script>
- 侦听多个数据
把ref对象、新值、旧值都分别包裹成数组<script setup> import { watch } from 'vue' watch([ref对象1, ref对象2], ([newValue1, newValue2], [oldValue1, oldValue2]) => {}) </script>
immdiate和deep配置
需要配置immediate和deep时,把他们包裹成对象,写在watch第三个参数的位置
watch中默认是浅层监视,当传入的ref对象是简单数据类型时,可以直接监视,当ref对象是复杂数据类型时,监视不到内部数据的变化,只能监视到对象地址的变化,因此需要deep配置
语法:
<script setup>
import { watch } from 'vue'
watch(ref对象, (newValue, oldValue) => {}),{ immediate: true, deep: true }
</script>
精确侦听对象的某个属性
需求:在不使用deep的前提下,侦听ref对象中某一个属性的变化,若改变则触发回调
语法:
const info = ref({name: 'xiaoyang',age: '19'
})watch(// 表示只侦听info中的age属性() => info.value.age// 变化后触发的动作() => console.log('age发生了变化')
)
组合式API-生命周期函数
<script setup>// 这里可以直接放beforCreate和 d的相关代码// 如果有些代码需要在mounted生命周期中执行
/ 写成函数的调用方式,可调用多次,不会发生冲突,而是按照顺序依次执行
onMounted(() => {逻辑1})
onMounted(() => {逻辑2})
<script>
组合式API-父子通信
-
组合式API下的父传子
基本思想: ① 父组件中给子组件绑定属性 ② 子组件内部通过props选项接收
【注意】:① 在子组件中接收时,通过defineProps“编译器宏”函数接收子组件传递的数据 ② defineProps原理:就是编译阶段的一个标识,实际编译器解析时,遇到后会进行编译转换
-
组合式API下的子传父
基本思想:① 父组件中给子组件标签通过@绑定事件 ② 子组件内部通过defineEmits“编译器宏”函数触发事件
组合式API- ref模板引用
在组件挂载完毕之后,才能使用模板引用,eg:在onMounted函数中、添加点击事件
- 语法:
- 调用ref函数生成一个ref对象
- 通过ref标识绑定ref对象到标签上
import { onMounted, ref } from 'vue' // 1. 获取dom const inp = ref(null) // 第一种:使用onMounted函数 onMounted(() => {inp.value.focus() })<div class="box"><input ref="inp" type="text" /> </div>
- defineExpose编译宏的作用:显示暴露组件内部的属性和方法
组合式API-provide和inject
作用与场景:顶层组件向任意底层组件传递数据和方法,实现跨层组件通信
- 跨层传递普通数据
- 顶层组件通过provide函数提供数据
provide('key', 顶层组件中的数据)
- 底层组件通过inject函数获取数据
const message = inject('key')
- 顶层组件通过provide函数提供数据
- 跨层传递响应式数据
- 顶层组件:
provide('key', ref对象)
- 底层组件:
const message = inject('key')
- 顶层组件:
- 跨层传递函数
【注意】:一般来说,谁的数据就由谁来修改,所以对于传递的数据的修改操作要放在顶层组件中- 顶层组件:
provide('key', 函数名)
- 底层组件:
const message = inject('key')
- 顶层组件:
Vue3.3新特性-defineOptions
- 背景说明:当使用
<script setup>
时,如果要定义组件的name或者其他自定义属性,就需要再添加一个普通的<script>
标签 - 新特性:在vue3.3中引入defineOptions宏,用来定义任意的选项,prop、emits、expose、slots除外(这些可以用defineXXX实现)
- 语法:
<script setup>defineOptions {name: 'loginIndex'} </script>
Vue3.3新特性-defineModel
vue3中使用v-model相当于传递了一个modelValue属性,同时触发update:modelValue事件,同时还要在内部使用props和emits传递数据
使用defineModel时只需要在父组件中绑定defineModel,然后再子组件中导入即可,并且可以直接在子组件内操作数据
语法:
- 父组件中创建数据,并用v-model给子组件绑定
- 子组件中用defineModel()接收,在input标签中使用:value绑定,再添加input事件该值
eg:
<!-- 父组件 --><script setup>const count = ref(12345)</script></template><div><MyInput v-model="count"></MyInput><br>{{ count }}</div></template><!-- 子组件 --><script setup>const modelCount = defineModel()</script><template><input type="text" :value="modelCount"@input="e => modelCount = e.target.value"name="" id=""></template>
pinia
pinia是Vue的最新状态管理工具,是vuex的替代品
- 优势:
- 提供更加简单的API(去掉了mutations)
- 提供符合组合式风格的API(和vue3新语法统一)
- 去掉了modules的概念,默认每一个store都是一个独立的模块
- 配合TypeScript更加友好,提供可靠的类型判断-
- 手动添加Pinia到Vue项目
实际开发中可以在项目创建时自动添加
手动添加步骤:- 使用vite创建一个空的vue项目
- 按照官方文档安装pinia到项目中
pinia基本语法
pinia有两种基本语法,第一种Option Store把同一类的代码写在一起,第二种把同一数据的操作代码写在一起,常用第二种
- Option Store
语法示例:
这里state是store的数据(data),getters是store的计算属性(computed),而 actions则是方法(methods)export const useCounterStore = defineStore('仓库的唯一标识名', {state: () => ({ count: 0, name: 'Eduardo' }),getters: {doubleCount: (state) => state.count * 2,},actions: {increment() {this.count++},}, })
- Setup Store
语法示例:
这里ref()就是state属性,computed()就是getters,function()就是actions
【注意】:① 要让pinia正确识别state,你必须在setup store中返回state的所有属性 ② 在组件中调用的时候不能使用解构赋值,使用解构赋值会导致丢失响应式export const useCounterStore = defineStore('仓库的唯一标识名', () => {<!-- 声明数据 -->const count = ref(0)<!-- 声明基于数据派生的计算属性 -->const doubleCount = computed(() => count.value * 2)<!-- 声明操作数据的方法 --><!-- 普通函数 -->function increment() {count.value++}<!-- 异步函数 -->const func = async () => {const res = await axios.get('')}return { count, doubleCount, increment } })
storeToRefs方法
- 目的:解决使用解构赋值在组件中调用pinia后,出现的丢失响应式的问题
- 解决方法:使用storeToRefs方法
eg:import { useCounterStore } from '@/store/counter.js' const counterStore = useCounterStore() const { count, addCnt } = storeToRefs(counterStore)
- 注意:解构数据时,使用storeToRefs方法;解构方法时,不使用storeToRefs方法,可以直接从store实例中解构
pinia持久化
pinia持久化可以使用插件pinia-plugin-persistedstate
官方文档:https://prazdevs.github.io/pinia-plugin-persistedstate/zh/
- 使用步骤
- 安装插件
npm i pinia-plugin-persistedstate
- main.js使用,把插件挂载在pinia上
import { createPinia } from 'pinia' import persist from 'pinia-plugin-persistedstate'const pinia = createPinia() pinia.use(persist)
- store仓库中,
persist: true
开启(组合式写法下,写在export的defineStore的第三个参数的位置,包裹成对象)
- 安装插件
- 配置
- 默认配置
- localStorage 作为存储。
- store.$id 作为存储的默认 key。
- JSON.stringify/destr 作为序列化器/反序列化器。
- 整个状态将持久化到存储中
- 自定义配置(常用)
persist: {key: 'my-custom-key',storage: sessionStorage,<!-- pick用来定义哪些数据需要持久化,哪些不需要 -->pick: ['save.me', 'saveMeToo'], }
- 默认配置