前言
- 创建一个vue3的项目
- vue3相对于vue2都有哪些优化
- vue3中组合式API的基础用法
- 依赖注入
1、创建项目
# 安装脚手架
npm install -g @vue/cli
# OR
yarn global add @vue/cli
# 查看脚手架版本号
vue --version
# 图形化工具
vue ui
# 创建项目
vue create vue3
# 启动项目
cd vue3
npm run serve
2、vue3都有哪些升级
2-1. 性能
vite的优点
- 打包大小减少
- 启动服务变快,热更新渲染更快(存在一些缓存机制)
- 内存占用减少
vite的缺点
- 生态不足
- prod环境构建,使用的 Rollup
- 使用还不广泛,可能存在很多问题未被发现
2-2. 源码
-
组合式API,
Components API
让组件抽离、逻辑代码复用更加灵活 -
使用
ES 6
新增Proxy
代替Object.defineProperty
实现响应式 -
更好的支持
TypeScript
-
移除一些冷门API
-
引入
Tree shaking
按需编译,体积比vue2更小// 如果你的项目没有用到 watch,编译时就会 tree shaking 掉 import { computed, watch } from 'vue';
3、生命周期
组合式API 生命周期引用及调用时机
// setup == beforeCreate & created
<scripte setup>
import { onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted, onErrorCaptured, onRenderTracked, onRenderTriggered, onActivated, onDeactivated, onServerPrefetch } from 'vue';
// 在组件DOM实际渲染安装之前调用,在这一步中,根元素还不存在
onBeforeMount() {}
// 在组件的第一次渲染后调用,该元素现在可用,允许直接DOM访问
onMounted() {}
// 数据更新时调用,发生在虚拟 DOM 打补丁之前,在更新前访问现有的DOM
onBeforeUpdate() {}
// DOM更新后
onUpdated() {}
// 在卸载组件实例之前调用。在这个阶段,实例仍然是完全正常的。可以卸载一些定时器等
onBeforeUnmount() {}
// 卸载组件实例后调用。
onUnmounted() {}
// 被 keep-alive 缓存的组件激活时调用。
onActivated() {}
// 被 keep-alive 缓存的组件停用时调用。
onDeactivated() {}
// 在捕获了后代组件传递的错误时调用。
onErrorCaptured() {}
// 跟踪虚拟 DOM 重新渲染时调用 (只在开发环境起作用)
onRenderTracked() {}
// 在一个响应式依赖被组件触发了重新渲染之后调用 (只在开发环境起作用)
onRenderTriggered() {}
// 需要做SSR的生命周期,当组件实例在服务器上被渲染之前要完成的异步函数。
onServerPrefetch() {}
<scripte>
4、响应式API
4-1. ref 函数
接受任意类型值返回一个响应式的数据
语法
let count = ref(0)
例子
<template>
/* 在模板中使用ref响应式数据时不需要 .value 取值 */
<div>{{count}}</div>
</template>
<script setup>
import { ref } from 'vue'
import type { Ref } from 'vue'
let count = ref(0)
// 通过.value的方式取值
console.log(count.value) // 0
// 通过.value的方式修改ref返回的响应式的值
count.value ++
console.log(count.value) // 1
// 为 ref() 标注类型
let year: Ref<string | number> = ref('2022')
year.value = 2022
</script>
4-2. reactive 函数
定义一个对象类型响应式数据;返回一个响应式对象(Proxy 代理对象)
语法
const obj = reactive({ age: 18, sex: '男' })
例子
<template>
/* 此处用法与 ref 一致 */
<div>{{obj.age}}</div>
</template>
<script setup>
import { reactive } from 'vue'
const obj = reactive({ age: 18, sex: '男' })
obj.age = 20
obj.sex = '女'
// 与 ref 不同可以直接访问和修改响应式数据的值 不需要通过 .value 获取
console.log(obj) // { age: 20, sex: '女' }
</script>
4-3. reactive 和 ref 区别
ref
一般用于定义基础类型数据,如果使用 ref 定义对象或数组时 源码实际时调用reactive
reactive
定义一个对象或数组返回一个响应式的Proxy
实例ref
基于object.defineProperty
实现响应式,reactive
基于Proxy
实现
4-4. computed函数
与vue2中的computed
使用一致
4-5. watch函数
watch
函数接受三个参数,第一个是响应式数据 第二个是更改后的回调函数 第三个是可选配置参数
语法
watch(
() => state, // 需要监听的响应式数据
(newValue, oldValue) => {
// newValue === oldValue
}, // 修改值后的回调
{ deep: true } // 执行时机,debugger 等配置
)
例子
<script setup>
import { reactive, watch } from 'vue'
const state = reactive({ count: 0 })
watch(() => state.count, (count, prevCount) => {
/* ... */
}, {
immediate?: boolean, // 默认:false 是否立即监听。
deep?: boolean, // 默认:false 是否深度监听。
flush?: 'pre' | 'post' | 'sync', // 调用时机 pre > dom更新前;post > dom;更新后 sync > 同步调用
onTrack?: (event: DebuggerEvent) => void, // 会在某个响应式 property 或 ref 作为依赖被追踪时调用。
onTrigger?: (event: DebuggerEvent) => void // 会在侦听回调被某个依赖的修改触发时调用。
})
console.log(state.count) // 访问 state.count,触发onTrack
state.count ++ // 修改 state.count,触发onTrigger
</script>
4-6. watchEffect
用于自动收集响应式数据的依赖
语法
// 传入的函数会立即执行一次,在执行的过程中收集依赖,只有收集的依赖发生变化时 函数才会执行
// 第二个参数的三个配置项与 watch 的配置项用法一致
watchEffect(() => {
/* */
}, {
flush: 'post',
onTrack(e) {
debugger
},
onTrigger(e) {
debugger
}
})
例子
<script setup>
import { reactive, ref, watchEffect } from "vue";
let num = ref(0)
let obj = reactive({ name: '张三' })
const stop = watchEffect(() => {
console.log(obj.name);
})
setTimeout(() => {
num.value ++ // watchEffect 不执行
}, 1000)
setTimeout(() => {
obj.name = '李四' // watchEffect 执行
stop() // 停止监听器
}, 3000)
setTimeout(() => {
obj.name = '可鲁可' // watchEffect 不再执行
}, 5000)
</script>
清除副作用
开发中需要在侦听函数中执行网络请求,但是在网络请求还没有达到的时候停止了侦听器,或者侦听器侦听函数被再次执行了,那么上一次的网络请求应该被取消掉,这个时候可以清除上一次的副作用;
<template>
<div>
<p>{{name}}</p>
<button @click="changeName">改变</button>
</div>
</template>
<script setup>
import { ref, watchEffect } from 'vue';
const name = ref('张三')
const num = ref(0)
watchEffect(onInvalidate => {
// 模拟值发生变化是需要发的一些网络请求
const timer = setTimeout(() => {
console.log("请求成功");
}, 2000);
// 上一次请求未完成再次发生变化时,取消上一次的请求
onInvalidate(() => {
clearTimeout(timer);
});
console.log('name === ', name.value);
console.log('num === ', num.value)
});
const changeName = () => {
name.value = '李四'
num.value ++
};
</script>
4-7. readonly()
接受一个对象(响应式/非响应式对象),返回一个只读状态的 Proxy 代理对象
语法
const copy = readonly(obj)
例子
import { reactive, watchEffect, readonly } from "vue";
const original = reactive({ count: 0 })
const copy = readonly(original)
watchEffect(() => {
// 用来做响应性追踪
console.log(original.count)
})
watchEffect(() => {
// 用来做响应性追踪
console.log(copy.count)
})
setTimeout(() => {
// 更改源属性会触发其依赖的侦听器
original.count++
// 更改该只读副本将会失败,并会得到一个警告
copy.count++ // warning!
}, 2000)
5、依赖注入
实现跨层级传值
provide
接受两个值,第一个参数是要注入的key
<string | symbol>,第二个是需要注入的值。inject
接受三个值,第一个是注入的key
,第二个参数是可选参,如果没有匹配到key
时使用默认值,如果默认值本身时一个函数则需要将第三个参数设置为false
,表明第二个默认值就是函数而不是工厂函数。
语法
provide('foo', 'bar')
inject('foo', () => '如果没有找到foo,则使用默认值')
例子
// 父组件
<script setup>
import { ref, provide } from 'vue'
// 提供静态值
provide('foo', 'bar')
// 提供响应式的值
const count = ref(0)
provide('count', count)
</script>
// 子孙组件
<script setup>
import { inject } from 'vue'
// 接收静态数据,当foo没有被找到时使用 new foo
const inject = inject('foo', 'new foo')
// 接收响应式的值
const count = inject('count')
// 修改接收的响应式值
count.value ++
</script>