一. 创建vue3项目
- 使用vue-cli脚手架创建
- 安装vue-cli要求脚手架版本必须大于4.5
- yarn global add @vue/cli
- npm install -g @vue/cli
- 创建vue3项目:vue create my-project
- 安装vue-cli要求脚手架版本必须大于4.5
使用vite安装并指定模板
- npm create vite@latest my-vue-app --template vue
- yarn create vite my-vue-app --template vue
直接安装(默认基于vite构建),根据提示安装需要的依赖
- npm init vue@latest
- 建议使用时方法三。
- 具体安装方法可以参考
二. 生命周期
创建前:beforeCreate -> 使用setup()
创建后:created -> 使用setup()
挂载前:beforeMount -> onBeforeMount
挂载后:mounted -> onMounted
- 更新前:beforeUpdate -> onBeforeUpdate
- 更新后:updated -> onUpdated
- 销毁前:beforeDestroy -> onBeforeUnmount
- 销毁后:destroyed -> onUnmounted
- 异常捕获:errorCaptured -> onErrorCaptured
- 被激活:onActivated 被包含在中的组件,会多出两个生命周期钩子函数。被激活时执行。
- 切换:onDeactivated 比如从 A 组件,切换到 B 组件,A 组件消失时执行
三. 组合式API和选项式API
- options Api在vue2中使用的。将每一个功能写到对应的Api下。
当逻辑复杂的时候这种写法不利于代码阅读和代码维护。
- composition Api在vue3中使用。将每一个功能独立完整的写在setup(){}里面,
有利于代码阅读和维护
四.ref响应式数据
- 作用:
定义一个响应式数据
- 语法:const xxx = ref<数据类型:string|number> = (1)
- 操作:xxx.value
- 使用:
在模板中不需要使用.value 可以直接使用
- 备注:可以定义基本数据类型也可以定义复杂数据类型例如对象
- 基本数据类型:响应式的原理依然是通过Object.definedProperty()的get()和set()来做数据劫持和数据更新。
- 复杂数据类型:内部求助reactive通过Proxy代理实现
五. reactive响应式数据
- 作用:
定义一个复杂类型的响应式数据,例如对象
- 语法: const xxx = reactive<数据类型> = ({})
- 操作:
直接读取不需要.value
- 备注:通过Proxy代理对象对源对象数据进行操作,是深层次的
六.响应式原理
- vue2通过Object.definedProperty()的get()和set()来做数据劫持、结合和发布订阅者模式来实现。
- vue3通过proxy的方式实现
- proxy的优势
:不需要像Object.definedProperty()的那样遍历每一个属性,有一定的性能提升
proxy可以理解为在目标对象之前架设一层“拦截”,外界对该对象的访问都必须通过这一层拦截。这个拦截可以对外界的访问进行过滤和改写。
- 当属性过多的时候利用Object.definedProperty()要通过遍历的方式监听每一个属性。利用proxy则不需要遍历,会自动监听所有属性,有利于性能的提升
七. watch监听
- 监听一个ref属性:
watch(a,(newValue,oldValue)=>{
},immediate:true,deep:true) // 得到的newValue和oldValue是一个值
- 监听多个ref属性:
watch([a,b,c,...],(newValue,oldValue)=>{
},immediate:true,deep:true) // 得到的newValue和oldValue是一个数组
- 监听一个reactive里面的某一属性:
watch(()=>a.name,(newValue,oldValue)=>{
})
- 监听一个reactive里面的某一些属性:
watch([()=>a.name,()=>a.age],(newValue,oldValue)=>{
})
- 监听一个reactive里面的所有属性:
watch(a,(newValue,oldValue)=>{
})
// 无法获取oldValue
// 强制开启了深度监听(deep配置无效)
- 同时监听ref和reactive数据:
watch([a,()=>a.age],(newValue,oldValue)=>{
})
- 特殊情况:
watch(()=>a.job,(newValue,oldValue)=>{
},deep:true)
// job是一个复杂对象必须开启深度监听,无法获取oldValue
// 如果直接监听一个对象不需要开启深度监听
// 如果是用箭头函数形式则必须开启
八. watchEffect监听
- 说明:不用指定哪个属性,在函数体中使用了哪个属性,就是监听了哪一个属性。
- 和watch的区别
- 不需要手动传入依赖
- 立即执行,每次初始化的时候都会执行一次回调函数来自动获取依赖
- 无法获取到原来的值,只能得到变化后的值
- 语法:watchEffect((newValue)=>{})
watchEffect(() => {
userInfo.value = user.name + user.more.age; // 可以看到上图中watchEffect实现了对name和age的监听
});
九. toRef和toRefs
- 作用:可以用来复制reactive里面的属性然后转成ref,既保留了响应式也保留了引用。也就是你从 reactive 复制过来的属性进行修改后,除了视图会更新,原有 ractive 里面对应的值也会跟着更新。
- toRef:复制 reactive 里的
单个
属性并转成 ref - toRefs: 复制 reactive 里的
所有
属性并转成 ref - 使用
// toRef的使用
<template>
// toRef的使用
<h1>{
{
username}}</h1>
// toRef的使用
<h1>{
{
age}}</h1>
</template>
<script setup>
import {
reactive,toRef,toRefs} from 'vue'
const info = reactive({
name:'wangjiajia',
age:'25'
})
// toRef的使用
const username = toRef(info,'name')
// toRefs的使用
const {
name,age} = toRefs(info)
</script>
- 总结
使用toRef()解构出来的是对象中的某一个属性,可以在template中直接使用不用加.value。
使用toRefs()是将整个对象解构,必须加上...,这样在template中就可以直接使用不用加.value。
十. shallowReactive 和 shallowRef
- 作用:
做数据响应式性能优化的
- shallowReactive:
只处理对象最外层的响应(浅层响应)
- shallowRef:
只处理基本数据类型的响应式, 不进行对象的响应式处理。
- 使用条件:
如果有一个对象类型数据,结构比较深,但变化时只是外层属性变化使用shallowReactive。
如果有一个对象类型数据,不希望他是响应式,使用shallowRef
- 例如:
// shallowReactive的使用
const userInfo = shallowReactive({
// 只将第一层数据做了响应式处理
name:'wangjiajia',
age:25,
like:{
food:{
apple:'橙子' // 深层次的数据将会是一个普通的对象
}
}
})
// shallowRef的使用
const userInfo = shallowRef({
// userInfo将不在是一个响应式对象
name:'wangjiajia'
})
十一. readonly 和 shallowReadonly
- readonly:
让一个响应式数据变为只读的。(深层次)
- shallowReadonly:
让一个响应式数据变为只读的。(浅层次)
- 应用场景:不希望数据被修改
let person=reactive({
name:'张三',
age:18,
job:{
j1:{
salary:20
}
}
})
person = readonly(person) // person里面的所有属性都不可以被修改
person = shallowReadonly(person) // person里面只有第一层属性不可以被修改
十二. toRaw 和 markRaw
- toRaw:
- 作用:
将一个reactive生成的响应式数据转为普通对象
- 使用场景:
用于读取响应式对象对应的普通对象,对这个普通对象的所有操作不会引起页面更新
- 作用:
- markRaw:
标记一个对象使其永远不会成为响应式
- 应用场景:
- 有些值不应该被设为响应式的。例如一些复杂的第三方库
- 当渲染具有不可变数据源的大列表时候,跳过响应式可以提高性能
- markRaw和readonly的区别
markRaw标记一个对象使其永远不会成为响应式
readonly禁止对值得修改,响应式本身还存在。
十三. 响应式数据的判断
- isRef(): 检查一个值是否为ref对象,返回true/false
- isReactive:检查一个对象是否为reactive对象,返回true/false
- isReadonly:检查一个对象是否是由readonly创建的只读属性
- isProxy:检查一个对象是否是proxy对象
十四. provide 和 inject
关于组件之间的通信回头重点讲一下
十五. vue3路由跳转(编程式和声明式)
- 声明式:
<router-link :to="{name:'',query:{}}">
- 编程式:
import { useRouter } from ‘vue-router’;
const router = useRouter();- 不带参数:
- router.push(‘/home’)
- router.push({name:‘home’})
- router.push({path:‘/home’})
- 带参数 ---- query传参
- router.push(name:‘home’,query:{id:1})
- router.push(path:‘/home’,query:{id:1})
- 带参数 ---- params传参
- router.push(name:‘home’,params:{id:1})
- 说明:query传参既可以用name也可以用path。跳转之后会拼接参数。params传参只能用name,跳转之后不会拼接参数。所以说query传参没有params传参安全。
- vue2路由跳转用this.$router.push就行剩下的参数内容和vue3一样
- 不带参数:
十六. vue3性能提升主要通过哪几个方面体现的
- 响应式系统
- 源码体积:通过在源码中加入了tree sharking是什么,项目打包体积变小了。模块按需加载
- 编译阶段
- diff算法不同,vue2是遍历每一个DOM节点。vue3是给每一个节点先做标记,只遍历标记变化的节点
- 静态提升:在vue3中对于不参与更新的元素会做静态提升,只会被创建一次,下一次直接使用,而不需要重新渲染
- 开启事件监听缓存
- SSR(服务器渲染)优化,当静态内容大到一定程度的时候,会生成一个static node这些静态node,会被直接innerHtml,就不需要创建对象,然后根据对象渲染
十七. vue3的设计目标
- 更小:按需加载,具有更小的打包体积
- 更快:diff算法,静态提升,开启事件监听缓存,性能优化
- 更友好:源码是通过monorepo(一种整合代码的微前端技术)方式维护的
- composition Apimonorepo
- 更好地ts支持
十八. vue3里面的setup
十九. 父组件和子组件的生命周期顺序
- 在setup中执行生命周期和外部的生命周期函数相比,会优先指向setup内的生命周期函数,再去执行外部的生命周期函数。
- 总体顺序如下:
- 父setup
- 父onBeforeMount
- 子setup
- 子onBeforeMount
子onMounted
父onMounted
- 父onBeforeUpdate
- 子onBeforeUpdate
子onUpdated
父onUpdated
二十. vue3的组件通信方式
- props / emit
- v-model
- provide/inject
- refs
- eventBus
- vuex/pinia
-
props / emit
-
v-model:
双向绑定,props + emit('update:xxx',xxx)
-
provide/inject
- 是 Vue 中提供的一对 API。
无论层级多深,API 都可以实现父组件到子孙组件的数据传递。
- 只能用于父组件向后代组件传值。
- 使用 provide 进行数据传输时,尽量使用 readonly 封装数据,避免子组件修改父组件传递的数据。
使用provide传过去的值一定要是proxy进行过代理的或者计算属性
- 例如:
provide('listData', computed(() => state.listData))
provide('listData', toRef(state.listData))
- 例如:
-
ref
:ref + defineExpose({})
-
eventBus:
Vue 3 中移除了 eventBus,但可以借助第三方工具来完成。Vue 官方推荐使用 mitt 或 tiny-emitter,在大多数情况下,不建议使用全局事件总线来实现组件通信。虽然比较简单粗暴,但是维护事件总线从长远来看是个大问题,这里就不解释了。
6. vuex / pinia:全局数据共享