第一章 Vue3简介
1. Vue3概述
在2020年9月18日Vue.js发布3.0版本,代号为One Piece (海贼王)。
特点:
1)性能的提升
- 打包大小减少41%
- 初次渲染提速55%,更新渲染提速133%
- 内存占用减少54%
2)源码的升级
- 使用Proxy代替defineProperty实现响应式
- 重写虚拟DOM的实现和Tree-Shaking
3)拥抱TypeScript
- Vue3可以更好的支持TypeScript
4)新特性
- Composition API(组合API):setup配置、ref与reactive、watch与wachEffect、provide与inject
- 新的内置组件:Fragment、Teleport、Suspense
- 其他改变:新的生命周期钩子、data 选项应始终被声明为一个函数、移除keyCode支持作为 v-on 的修饰符
2. 创建Vue3工程
- 使用 vue-ci 创建
// 安装或升级@vue/cli
npm install -g @vue/cli
// 创建
vue create vue test
// 启动
npm run serve
- 使用 vite 创建
vite:新一代前端构建工具
特点:开发环境中无需打包操作,可快速的冷启动;轻量快速的热重载(HMR) ;按需编译
// 创建工程
npm init vite-app
// 安装依赖
npm install
// 运行
npm run dev
第二章 Composition API
1. setup函数
概念:Vue3.0中一个新的配置项,值为一个函数。所有的Composition API (组合API)、数据、方法等均须在setup编写
返回值:
- 对象:对象中的属性、方法在模板中均可以直接使用
- 渲染函数:可以自定义染内容
执行的时机:
- 在beforeCreate之前执行一次,this指向undefined
接收参数:
- props:对象类型,包含组件外部传递过来的数据,且在组件内部声明接收了的属性
- context:上下文对象
<Demo msg="你好啊" school="尚佳谷"/>
props:['msg','school'],
setup(props,context){
}
- 一个 setup 函数的 props 是响应式的,并且会在传入新的 props 时同步更新。
- 如果解构props对象,解构出的变量将会丢失响应性。推荐通过 props.xxx 的形式来使用其中的 props。如果确实需要解构 props 对象,或者需要将某个 prop 传到一个外部函数中并保持响应性,可以使用 toRefs() 和 toRef() 这两个工具函数
context包含内容:
- attrs:值为对象,包含组件外部传递过来,但没有在props配置中声明的属性,相当于 this.$attrs
- slots:接收到的插槽内容,相当于 this.$slots
- emit:分发自定义事件的函数,相当于 this.$emit
注意:
- 尽量不要与Vue2.x配置混用,Vue2.x配置(data、methos、computed...) 中可以访问到setup中的属性、方法,但在setup中不能访问到Vue2.x配置 (data、methos、computed...),如果重名则setup优先
- setup不能是一个async函数,因为返回值不再是retum的对象,而是promise,模板看不到return对象中的属性
// 创建setup
{
name:'App',
setup(){
// 数据
let name = ref("张);
let age = ref(18);
let job = ref({
type:"前端工程师",
salary: '30k',
});
//方法
function changeInfo(){
name.value = '李四';
age.value = 48
job.value.type = 'UI设计师'
job.value.salary = '60k'
}
return{
name,
age,
job,
changeInfo
}
// 使用导出数据
<h2>姓名: {{name}}</h2>
<h2>年龄: {{lage}}</h2>
<h3>工作种类: {{job.type}}</h3>
<h3>工作薪水: {{job.salary}}</h3>
<button @click="changeInfo">修改人的信息</button>
2. ref函数
作用:定义一个响应式的数据
const xxx = ref(initValue)
ref会创建一个包含响应式数据的引用对象 (reference对象,简称ref对象)
JS中操作数据响应式数据:
xxx.value
模板中读取数据:
<div>{{xxx}}</div>
特点:
- 接收的数据可以是基本类型、也可以是对象类型
- 基本类型的数据响应式依然是靠 object.defineProperty()的 get 与 set 完成的
- 对象类型的数据响应式依靠Vue3.0中的reactive函数实现
3. reactive函数
作用:定义一个对象类型的响应式数据 (基本类型不要用它,要用 ref 函数)
const 代理对象 = rective(源对象) // 接收一个对象(或组),返回一个代理对象 (proxy对象)
特点:
- reactive定义的响应式数据是"深层次的”
- 内部基于 ES6 的 Proxy 实现,通过代理对象操作源对象内部数据
// 创建reactive响应式数据
let job = reactive({
type:'前端工程师',
salary: '3ok',
a:{
b:{
c:666
}
}
});
let hobby = reactive([抽烟",喝酒,烫头']);
// 操作数据
function changeInfo(){
job.type = 'UI设计师';
job.salary = '60k';
job.a.b.c = 999;
hobby[o] = '学习';
}
reactive对比ref:
1)定义数据角度
- ref定义基本类型数据
- reactive定义对象或数组
注意:ref也可以用来定义对象 (或数组)类型数据,它内部会自动通过 reactive 转为代理对象
2)从原理角度
- ref通过 object.defineProperty()的 get 与 set 来实现响应式 (数据劫持)
- reactive通过 Proxy 实现响应式 (数据劫持),并通过Reflect操作源对象内部的数据
3)从使用角度
- ref:操作数据需要 value,读取数据时模板中直接读取不需要 value
- reactive:操作数据与读取数据均不需要 value
4. Vue2.0与Vue3.0响应式区别
- vue2.x的响应式
实现原理:
- 对象类型:通过 Object.defineProperty() 对属性的读取、修改进行拦截 (数据劫持)
- 数组类型:通过重写更新数组的一系列方法来实现拦截。 (对数组的变更方法进行了二次封装)
Object.defineProperty(data, 'count',{
get(){},
set(){}
});
存在问题:
- 对象新增和删除某个属性时视图不会更新
- 直接通过下标修改数组元素时视图不会更新
- Vue3.x的响应式
实现原理:
- Proxy通过(代理)拦截对象中任意属性的变化,包括属性值的读写、属性的添加、属性的删除等
- 通过Reflect (反射)对被代理对象的属性进行操作
模拟实现:
const p = new Proxy(person,{
//有人读取p的某个属性时调用
get(target,propName){
return target[propName];
}
//有人修改p的某个属性或给p追加某个属性时调用
set(target,propName,value){
target[propName] = value;
}
//有人刷除的某个属性时调用
deleteProperty(target,propName){
return delete target[propName];
}
});
5. 计算属性与监视
- computed函数
Vue3.x中的computed与Vue2.x中配置功能一致
import { computed } from 'vue';
setup(){
//计算属性一简写形式
let fulIName = computed(()=>{
return person.firstName + '-' + person.lastName;
});
//计算属性一完整形式
let fullName = computed({
get(){
return person.firstName + '' + person.lastName;
},
set(value){
const nameArr = value.split('_');
person.firstName = nameArr[0];
person.lastName = nameArr[1];
}
});
}
- watch函数
Vue3.x中的watch与Vue2.x中配置功能一致
注意事项:
- 监视reactive定义的响应式数据时oldValue无法正确获取
- 监视reactive定义的响应式数据整体时强制开启了深度监视 (deep配置失效)
- 监视reactive定义的响应式数据中某个属性时deep配置有效
情况一:监视ref定义的响应式数据
watch(
sum, // ref数据
(newValue,oldValue)=>{ console.log('sum变化了',newValue,oldValue)}, // 回调函数
{ immediate: true } // 配置项
);
情况二:监视多个ref定义的响应式数据
watch(
[sum,msg], // 多个ref数据
(newValue ,oldValue)=>{console.log('sum或msg变化了',newValue,oldValue)} // 回调函数
);
情况三:监视reactive定义的响应式数据
watch(
person, // reactive对象
(newValue,oldValue)=>{ console.log('person变化了',newValue,oldValue)}, // 回调函数
{ immediate:true, deep:false } // 配置项,此处的deep配置不再奏效
)
情况四:监视reactive所定义的一个响应式数据中的某个属性
watch(
()=>person.name, // 返回值为reactive对象某个属性
(newValue,oldValue)=>{console.log('person的name变化了',newValue,oldValue)} // 回调函数
)
情况五:监视reactive所定义的一个响应式数据中的某些属性
watch(
[()=>person.name,()=>person,age], // 某些reactive对象属性组成的数组
(newvalue,oldValue)=>{console.log('person的name或age变化了',newValue,oldValue)} // 回调函数
)
特殊情况
watch(
()=>person.job, // 返回值为reactive对象某个属性
(newValue,oldValue)=>{console.log('person的job变化了',newValue,oldValue)}, // 回调函数
{ deep:true } // 配置项,此处的deep配置生效
)
6. watchEffect函数
- watch与watchEffect区别
watch监视原理:既要指明监视的属性,也要指明监视的回调
watchEffect监视原理:不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性
- watchEffect与computed区别
computed注重的是计算出来的值 (回调函数的返回值) ,所以必须要写返回值
watchEffect更注重的是过程 (回调函数的函数体) ,所以不用写返回值
执行时机:watchEffect所指定的回调中用到的数据只要发生变化,则直接重新执行回调
import {watchEffect} from 'vue';
watchEffect(()=>{
const x1 = sum.value
const x2 = person.age
console.log('watchEffect配置的回调执行了')
})
7. 生命周期钩子函数
- Vue3.0中可以继续使用Vue2.x中的生命周期钩子,但有有两个被更名
- beforeDestroy 改名为 beforeUnmount
- destroyed 改名为 unmounted
- Vue3.0也提供了 Composition API 形式的生命周期钩子
- beforeCreate ===> setup( )
- created ===> setup()
- beforeMount ===> onBeforeMount
- mounted ===>onMounted
- beforeUpdate ===> onBeforeUpdate
- updated ===> onUpdated
- beforeUnmount ===> onBeforeUnmount
- unmounted ===> onUnmounted
8. toRef和toRefs
作用:创建一个 ref 对象,其value值指向另一个对象中的某个属性
// 创建
const name = toRef( person, 'name')
// 导出
return {
person,
name:ref(person.name),
age:ref(person.age),
salary:ref(person.job.j1.salary),
}
// 使用
<div>{{name}}</div>
适用场景:将响应式对象中的某个属性单独提供给外部
toRefs 与 toRef 功能一致,可以批量创建多个ref对象
// 创建
const per = toRefs(person);
// 导出
return{
...toRefs(person)
}
第三章 其它 Composition API
1. shallowReactive 与 shallowRef
shallowReactive:只处理对象最外层属性的响应式 (浅响应式)
shallowRef:只处理基本数据类型的响应式,不进行对象的响应式处理
适用场景:
- shallowReactive:一个对象数据结构比较深,但只有最外层属性会发生变化
- shallowRef:一个对象中所有属性都不会发生变化,而是生成新的对象来代替原有对象
2. readonly 与 shallowReadonly
- readonly:使一个响应式数据变为只读的 (深只读)
- shallowReadonly:使一个响应式数据变为只读的 (浅只读)
适用场景:不希望数据被修改时使用
3. toRaw 与 markRaw
- toRaw
作用:将一个由 reactive 生成的响应式对象转为普通对象
适用场景:用于读取响应式对象对应的普通对象,对这个普通对象的所有操作,不会引起页面更新
- markRaw
作用:标记一个对象,使其永远不会再成为响应式对象
应用场景:
- 有些值不应被设置为响应式的,例如复杂的第三方类库等
- 当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能
4. customRef
作用:创建一个自定义的 ref,并对其依赖项跟踪和更新触发进行显式控制
使用场景:实现防抖
<template>
<input type="text" v-model="keyword">
<h3>{{keyword}}</h3>
</template>
<script>
import {ref,customRef} from 'vue';
export default {
name : 'Demo',
setup(){
//自定义一个myRef
function myRef(value,delay){
let timer
return customRef((track,trigger)=>{
return{
get(){track()},
set(newValue){
clearTimeout(timer)
timer = setTimeout(()=> value = newValuetrigger(),3000);
}
}
}
}
let keyWord = myRef("hello",50) //使用程序员自定义的ref
}
}
5. provide 与inject
作用:实现祖孙组件间通信
实现原理:父组件有一个 provide 选项提供数据,子组件有一个 inject 选项使用数据
// 祖组件
setup(){
let car = reactive({name:'奔驰',price:'40万'});
provide('car',car);
......
}
// 孙组件
setup(props,context){
const car = inject('car');
return {
car
}
}
6. 响应式数据的判断
- isRef:检查一个值是否为一个 ref 对象
- isReactive:检查一个对象是否是由reactive创建的响应式代理
- isReadonly:检查一个对象是否是由readonly 创建的只读代理
- isProxy:检查一个对象是否是由reactive 或者 readonly 方法创建的代理
7. Composition API的优势
Options API 存在的问题:在传统OptionsAPI中新增或者修改一个需求,就需要分别在data,methods,computed里修改
Composition API 的优势:更加优雅的组织代码,让相关功能的代码更加有序的组织在一起
第四章 新的组件
1. Fragment
在Vue2中组件必须有一个根标签,但是在Vue3中组件可以没有根标签,Vue内部会将多个标签包含在一个Fragment虚拟元素中。达到减少标签层级及内存占用的优点。
2. Teleport
Teleport:是一种能够将我们的组件html结构移动到指定位置的技术
<teleport to="移动位置"> // to属性指向内部html结构添加的位置
<div v-if="isShow" class="mask">
<div class="dialog">
<h3>我是一个弹窗</h3>
<button @click="isShow = false>关闭弹窗</button>
</div>
</div>
</teleport>
3. Suspense
作用:等待异步组件时渲染一些额外内容,让应用有更好的用户体验
// 异步引入组件
import { defineAsyncComponent} from 'vue';
// 包裹异步组件
const Child = defineAsyncComponent(()=>import('./components/Child.vue'))
// 使用 Suspense 包裹组件,并配置好 default 与 fallback
<template>
<div class="app">
<h3我是App组件</h3>
<Suspense>
<template v-slot:default>
<Child/> // Child加载完成显示
</template>
<template v-slot:fallback>
<h3>加载中...</h3> // Child加载中显示
</template>
</Suspense>
</div>
</template>
第五章 其他调整
1. 全局API转移
Vue 2.x 有许多全局 API 和配置,如注册全局组件、注册全局指令等。
//注册全局组件
Vue.component('MyButton', {
data: ()=> ({
count: 0
}),
template: '<button @click="count++">Clicked {{count}} times.</button>'
})
//注册全局指令
Vue.directive('focus', {
inserted: el => el.focus()
})
Vue3.0中对这些API做出了调整,将全局的API如Vue.xxx 调整到应用实例 (app ) 上
Vue.config.xxx ===> app.config.xxx
Vue.config.productionTip ===> 移除
Vue.component ===> app.component
Vue.directive ===> app.directive
Vue.mixin ===> app.mixin
Vue.use ===> app.use
Vue.prototype ===> app.config.global.Properties
2. 其他改变
1)data选项应始终被声明为一个函数
2)过度类名的更改
// Vue2.x写法
.v-enter,.v-leave-to {
opacity: 0;
}
.v-leave,.v-enter-to {
opacity: 1;
}
// Vue3.x写法
.v-enter-from..v-leave-to {
opacity: 0;
}
.v-leave-from.v-enter-to {
opacity: 1;
}
3)移除keyCode作为 v-on 的修饰符,同时也不再支持config.keyCodes
4)移除 v-on.native 修饰符
// 父组件中绑定事件
<my-component
v-on:close="handleComponentEvent" // 自定义事件,接收
v-on:click="handleNativeClickEvent" // 原生事件,不接收
/>
// 子组件中声明自定义事件
<script>
export default {
emits: ['close']
}
</script>
5)移除过滤器 filter