全局API
createApp
返回一个提供应用上下文的应用实例。应用实例挂载的整个组件树共享同一个上下文。
const app = createApp(Component);
app.mount('#root');
defineComponent
创建组件
import { defineComponent, ref } from 'vue';
export default defineComponent({
setup() {
let counter = ref(0);
const increment = () => {
counter.value += 1;
}
return {
counter,
increment
}
},
render() {
const {counter, increment} = this;
return (
Counter: {counter}
);
},
});
或者是一个 setup 函数,函数名称将作为组件名称来使用
import { defineComponent, ref } from 'vue';
const Parent = defineComponent(function Parent() {
let age = ref(10);
const incrementAge = () => {
age.value += 1;
}
return {
age,
incrementAge
}
})
console.log('Parent', Parent)
// {
// name: "Parent2"
// setup: ƒ Parent2()
// }
defineAsyncComponent
创建一个异步加载组件
// 全局注册
import { defineAsyncComponent } from 'vue'
const AsyncComp = defineAsyncComponent(() =>
import('./components/AsyncComponent.vue')
)
app.component('async-component', AsyncComp)
// 局部注册
import {createApp, defineAsyncComponent } from 'vue'
createApp({
components: {
AsyncComponent: defineAsyncComponent(() =>
import('./components/AsyncComponent.vue')
)
}
})
// 高阶组件
import { defineAsyncComponent } from 'vue'
const AsyncComp = defineAsyncComponent({
// 工厂函数
loader: () => import('./Foo.vue')
// 加载异步组件时要使用的组件
loadingComponent: LoadingComponent,
// 加载失败时要使用的组件
errorComponent: ErrorComponent,
// 在显示 loadingComponent 之前的延迟 | 默认值:200(单位 ms)
delay: 200,
// 如果提供了 timeout ,并且加载组件的时间超过了设定值,将显示错误组件
// 默认值:Infinity(即永不超时,单位 ms)
timeout: 3000,
// 定义组件是否可挂起 | 默认值:true
suspensible: false,
/**
*
* @param {*} error 错误信息对象
* @param {*} retry 一个函数,用于指示当 promise 加载器 reject 时,加载器是否应该重试
* @param {*} fail 一个函数,指示加载程序结束退出
* @param {*} attempts 允许的最大重试次数
*/
onError(error, retry, fail, attempts) {
if (error.message.match(/fetch/) && attempts <= 3) {
// 请求发生错误时重试,最多可尝试 3 次
retry()
} else {
// 注意,retry/fail 就像 promise 的 resolve/reject 一样:
// 必须调用其中一个才能继续错误处理。
fail()
}
}
})
resolveComponent
在当前应用中查找组件, 只能在 render 或 setup 函数中使用。
import { createApp, defineComponent, resolveComponent} from 'vue'
const app = createApp({});
app.component('ResolveComponent', {
render(){
return (This is a ResolveComponent
)
}
})
app.mount('#root');
defineComponent({
render() {
// return component or componentName;
const ResolveComponent = resolveComponent('ResolveComponent');
return (
)
}
})
resolveDynamicComponent
查找组件
import {defineComponent, resolveDynamicComponent} from 'vue'
defineComponent({
setup() {
const isShow = ref(false);
return {
isShow
}
},
render() {
// return component or componentName;
const ResDynamicComponent = resolveDynamicComponent({
render() {
return (
This is a ResolveDynamicComponent
)
}
})
return (
{
isShow ? : ''
}
)
}
})
resolveDirective
在当前应用中查找指令, 只能在 render 或 setup 函数中使用。
import { createApp, defineComponent, resolveDirective} from 'vue'
const app = createApp({})
app.directive('highlight', {})
defineComponent({
setup() {
// return Directive or undefined;
const directive = resolveDirective('highlight');
}
})
withDirectives
defineComponent({
render() {
const focus = resolveDirective('focus')
return withDirectives(, [[focus]])
}
})
createRenderer
nextTick
将回调推迟到下一个 DOM 更新周期之后执行。在更改了一些数据以等待 DOM 更新后立即使用它。
mergeProps
const props = mergeProps({
class: 'active'
}, this.$attrs)
4) 比较Vue2与Vue3的响应式(重要)
vue2的响应式
- 核心:
- 对象: 通过defineProperty对对象的已有属性值的读取和修改进行劫持(监视/拦截)
- 数组: 通过重写数组更新数组一系列更新元素的方法来实现元素修改的劫持
Object.defineProperty(data, 'count', {
get () {},
set () {}
})
- 问题
- 对象直接新添加的属性或删除已有属性, 界面不会自动更新
- 直接通过下标替换元素或更新length, 界面不会自动更新 arr[1] = {}
Vue3的响应式
- 核心:
- 通过Proxy(代理): 拦截对data任意属性的任意(13种)操作, 包括属性值的读写, 属性的添加, 属性的删除等...
- 通过 Reflect(反射): 动态对被代理对象的相应属性进行特定的操作
- 文档:
- https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy
- https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect
new Proxy(data, {
// 拦截读取属性值
get (target, prop) {
return Reflect.get(target, prop)
},
// 拦截设置属性值或添加新属性
set (target, prop, value) {
return Reflect.set(target, prop, value)
},
// 拦截删除属性
deleteProperty (target, prop) {
return Reflect.deleteProperty(target, prop)
}
})
proxy.name = 'tom'
Proxy 与 Reflect
5) setup细节
-
setup执行的时机
- 在beforeCreate之前执行(一次), 此时组件对象还没有创建
- this是undefined, 不能通过this来访问data/computed/methods / props
- 其实所有的composition API相关回调函数中也都不可以
-
setup的返回值
- 一般都返回一个对象: 为模板提供数据, 也就是模板中可以直接使用此对象中的所有属性/方法
- 返回对象中的属性会与data函数返回对象的属性合并成为组件对象的属性
- 返回对象中的方法会与methods中的方法合并成功组件对象的方法
- 如果有重名, setup优先
- 注意:
- 一般不要混合使用: methods中可以访问setup提供的属性和方法, 但在setup方法中不能访问data和methods
- setup不能是一个async函数: 因为返回值不再是return的对象, 而是promise, 模板看不到return对象中的属性数据
-
setup的参数
- setup(props, context) / setup(props, {attrs, slots, emit})
- props: 包含props配置声明且传入了的所有属性的对象
- attrs: 包含没有在props配置中声明的属性的对象, 相当于 this.$attrs
- slots: 包含所有传入的插槽内容的对象, 相当于 this.$slots
- emit: 用来分发自定义事件的函数, 相当于 this.$emit
App
msg: {{msg}}
{{n}}
{{m}}
msg: {{msg}}
msg2: {{$attrs.msg2}}
6) reactive与ref-细节
- 是Vue3的 composition API中2个最重要的响应式API
- ref用来处理基本类型数据, reactive用来处理对象(递归深度响应式)
- 如果用ref对象/数组, 内部会自动将对象/数组转换为reactive的代理对象
- ref内部: 通过给value属性添加getter/setter来实现对数据的劫持
- reactive内部: 通过使用Proxy来实现对对象内部所有数据的劫持, 并通过Reflect操作对象内部数据
- ref的数据操作: 在js中要.value, 在模板中不需要(内部解析模板时会自动添加.value)
App
m1: {{m1}}
m2: {{m2}}
m3: {{m3}}
7) 计算属性与监视
-
computed函数:
- 与computed配置功能一致
- 只有getter
- 有getter和setter
-
watch函数
- 与watch配置功能一致
- 监视指定的一个或多个响应式数据, 一旦数据变化, 就自动执行监视回调
- 默认初始时不执行回调, 但可以通过配置immediate为true, 来指定初始时立即执行第一次
- 通过配置deep为true, 来指定深度监视
-
watchEffect函数
- 不用直接指定要监视的数据, 回调函数中使用的哪些响应式数据就监视哪些响应式数据
- 默认初始时就会执行第一次, 从而可以收集需要监视的数据
- 监视数据发生变化时回调
App
fistName:
lastName:
fullName1:
fullName2:
fullName3:
8) 生命周期
vue2.x的生命周期
vue3的生命周期
与 2.x 版本生命周期相对应的组合式 API
-
beforeCreate
-> 使用setup()
-
created
-> 使用setup()
-
beforeMount
->onBeforeMount
-
mounted
->onMounted
-
beforeUpdate
->onBeforeUpdate
-
updated
->onUpdated
-
beforeDestroy
->onBeforeUnmount
-
destroyed
->onUnmounted
-
errorCaptured
->onErrorCaptured
新增的钩子函数
组合式 API 还提供了以下调试钩子函数:
- onRenderTracked
- onRenderTriggered
msg: {{msg}}
App
09) 自定义hook函数
使用Vue3的组合API封装的可复用的功能函数
自定义hook的作用类似于vue2中的mixin技术
自定义Hook的优势: 很清楚复用功能代码的来源, 更清楚易懂
-
需求1: 收集用户鼠标点击的页面坐标
hooks/useMousePosition.ts
import { ref, onMounted, onUnmounted } from 'vue'
/*
收集用户鼠标点击的页面坐标
*/
export default function useMousePosition () {
// 初始化坐标数据
const x = ref(-1)
const y = ref(-1)
// 用于收集点击事件坐标的函数
const updatePosition = (e: MouseEvent) => {
x.value = e.pageX
y.value = e.pageY
}
// 挂载后绑定点击监听
onMounted(() => {
document.addEventListener('click', updatePosition)
})
// 卸载前解绑点击监听
onUnmounted(() => {
document.removeEventListener('click', updatePosition)
})
return {x, y}
}
x: {{x}}, y: {{y}}
利用TS泛型强化类型检查
-
需求2: 封装发ajax请求的hook函数
hooks/useRequest.ts
import { ref } from 'vue'
import axios from 'axios'
/*
使用axios发送异步ajax请求
*/
export default function useUrlLoader(url: string) {
const result = ref(null)
const loading = ref(true)
const errorMsg = ref(null)
axios.get(url)
.then(response => {
loading.value = false
result.value = response.data
})
.catch(e => {
loading.value = false
errorMsg.value = e.message || '未知错误'
})
return {
loading,
result,
errorMsg,
}
}
LOADING...
{{errorMsg}}
- id: {{p.id}}
- title: {{p.title}}
- price: {{p.price}}
10) toRefs
把一个响应式对象转换成普通对象,该普通对象的每个 property 都是一个 ref
应用: 当从合成函数返回响应式对象时,toRefs 非常有用,这样消费组件就可以在不丢失响应式的情况下对返回的对象进行分解使用
问题: reactive 对象取出的所有属性值都是非响应式的
解决: 利用 toRefs 可以将一个响应式 reactive 对象的所有原始属性转换为响应式的 ref 属性
App
foo: {{foo}}
bar: {{bar}}
foo2: {{foo2}}
bar2: {{bar2}}
11) ref获取元素
利用ref函数获取组件中的标签元素
功能需求: 让输入框自动获取焦点
App
---