-
摘要:大家新年好!作者作为一个前端小白,为了能紧跟前端技术的步伐,早日迈入大厂的门槛?,所以春节期间也不敢闲着,开始着手Vue 3的学习,此篇主要记述 API 相关的知识。
-
文章基本按照 Vue 3 官方文档进行整理总结,因为之前学过Vue 2,此次加上一些个人理解学习 Vue 3 ,写此文章总结,初衷仅是为了让自己巩固知识,如有错误,希望多多指点。
1. 应用配置 (Appplication Config)
-
config
是一个包含了 Vue 应用全局配置的对象,可以在应用实例挂载前修改其相应的 property 属性。import { createApp } from 'vue' // Vue3 新方法 createApp 其作用是构建实例 // Vue2 使用的是 new Vue() 构建实例 // createApp 函数创建一个实例 const app = createApp({}) // 配置 Config 接口类型,各属性具体作用下面会解释 interface Config { errorHandler: Function warnHandler: Function globalProperties: { [key: string]: any } isCustomElement: (tag: string) => boolean optionMergeStrategies: { [key: string]: Function } performance: boolean } const config : Config = { // errorHandler 类型:Function , 默认:undefined // 指定一个处理函数,来处理组件渲染方法执行期间及侦听器抛出的未捕获错误 // 这个处理函数被调用时,可获取错误信息和应用实例 errorHandler: (err, vm, info) => { // 处理错误 // info 是 Vue 特定的错误信息,比如错误所在的生命周期钩子 }, // warnHandler 类型:Function , 默认:undefined // 为 Vue 的运行时警告指定一个自定义处理函数,注意这只会在开发环境下生效,生产环境中会被忽略 warnHandler: (msg, vm, trace) => { // trace 是组件的继承关系追踪 }, // globalProperties 类型: {[key: string]}: any , 默认:undefined // 添加可以在应用程序内的任何组件示例中访问的全局property,属性名冲突是,组件的property将具有优先权 // 这可以代替 Vue 2.x 的 Vue.prototype // Vue 2.x >> Vue.prototype.$http= () => {} // Vue 3.x >> app.config.globalProperties.$http= () => {} globalProperties: { $foo: 'bar', $http: () => {}, }, // isCustomElement 类型:(tag: string) => boolean , 默认:undefined // 指定一个方法,用来识别在Vue之外定义的自定义元素(例如,使用Web Components API)。 // 如果组件符合此条件,则不需要本地或者全局注册,并且Vue不会抛出关于 Unkonwn custom element 的警告 // 注意: 所有原生 HTML 和 SVG 标记不需要再次函数中匹配,Vue解析器自动执行此检查 isCustomElement: tag => tag.startsWith('ion-'), // optionMergeStrategies 类型: { [key: string]: Function } , 默认:{} // 为自定义选项定义合并策略 // 合并策略选项分别接收在父实例和子实例上定义的该选项的值作为第一个和第二个参数 // 引用上下文实例被作为第三个参数传入 /* app.config.optionMergeStrategies.hello = (parent, child, vm) => { return `Hello ${child}` */ // app.mixin({ hello: 'Vue' }) >> 'Hello Vue' optionMergeStrategies: {}, // performance 类型:boolean ,默认: false // 设置为 true 以在浏览器开发工具的 performance / timeline 面板中启用对组件初始化,编译,渲染和更新的性能追踪。 // 只适用于开发模式和支持 performance.mark API 的浏览器。 performance: false } // 不可以直接替换原来的 config 属性 如 app.config = config // 会报以下警告:app.config cannot be replaced. Modify individual options instead. // 但可以逐个修改其属性,如下所示 for (let key in config) { app.config[key] = config[key] } // mount 方法将实例根组件挂载在 id 为 app 的元素上 app.mount('#app')
2. 应用 API (Application API)
- 在Vue 3 中, 改变全局行为的API现在被移动到了由新的 createApp 方法所创建的应用实例上,此外,现在它们影响仅限于该特定的应用实例。
import { createApp } from 'vue'
const app = createApp({})
// 调用 createApp 返回一个应用实例,该实例提供一个应用上下文,
// 应用实例挂载的真个组件树共享相同的上下文,该上下文提供了之前在 Vue 2 中“全局”的配置。
// 另外,由于 createApp 方法返回应用实例本身,因此可以在其后链式调用其他方法,这些方法下面会列举。
1. component (注册组件)
-
参数:
{ string } name
{ Function | Object } [definition]
-
返回值:
- 如果传入
definition
参数,返回应用实例。 - 如果不传入
definition
参数,返回组件定义。
- 如果传入
-
示例:
import { createApp } from 'vue' const app = createApp({}) // 全局注册一个名为 my-component 的组件 // component 此方法可注册一个组件 app.component('my-component', { data() { return { count: 0 } }, props: { msg: { type: String, required: true } }, template: '<button @click="count++"> Click Me </button>' }) // 检索注册的组件(始终返回构造函数) const MyComponent = app.component('my-component', { /*...*/ }) // 局部注册 const CmpA = { name: 'CmpA', data() { return { msg: 0 } }, template: '<div> {{ msg }} </div> ' } const CmpB = { name: 'CmpB', /* ... */ template: '<div> CmpB </div> ' } // 引入局部组件 // 使用 components 属性,挂载局部组件 const app = createApp({ components: { CmpA, CmpB } })
<!-- 上述组件使用示例 --> <div> <my-component :msg="hello"></my-component> <CmpA /> </div>
2. directive (注册指令)
- 参数:
{ string } name
{ Function | Object } [ definition ]
- 返回值:
- 如果传入
definition
参数,返回应用实例。 - 如果不传入
definition
参数,返回指令定义
- 如果传入
- 用法:
- 注册或检索全局指令
- 示例:
import { createApp } from 'vue'
const app = createApp({})
// 全局注册(功能指令)
app.directive('my-directive', {
// 指令是具有一组生命周期的钩子
// 在绑定元素的父组件挂载前调用
beforeMount() {},
// 绑定元素的父组件挂载时调用
// 钩子参数下面会介绍
mounted(el, binding, vnode, prevNode) {},
// 在包含组件的节点(VNode)更新之前调用
beforeUpdate() {},
// 在包含组件的节点(VNode)及其子组件的节点更新之后调用
updated() {},
// 在绑定元素的父组件卸载之前调用
beforeUnmount() {},
// 卸载绑定元素的父组件时调用
unmounted() {}
})
// 全局注册(功能指令), 第二个参数为函数时
app.directive('my-directive', () => {
// 这将被作为 `mounted` 和 `updated`调用
})
// 全局注册的指令可以使用在这个应用实例的某个组件,或者某个组件中的元素中
// 如果此指令针对的是对整个组件进行作用的时候
// 例如: <SomeoneCmp v-my-directive />
// 如果此指令是针对某个元素进行作用的时候
// 例如: <div v-my-directive></div>
// getter, 如果已注册,则返回指定定义
const myDirective = app.directive('my-directive')
<template>
<!-- v-focus 为自定义的聚焦指令,使用指令时在指令名前加个 'v-' -->
<input v-focus />
</template>
<script lang="ts">
//局部注册指令,注册组件中接收一个 directives 的选项
// 引入此组件时将会得到一个聚焦的输入框
import {defineComponent} from 'vue'
export default defineComponent({
name: 'FocusInput',
directives: {
// 注册一个聚焦指令
focus: {
mounted(el) {
el.focus()
}
}
},
})
</script>
-
指令钩子参数:
el
指定绑定的元素, 这可用于直接操作DOM。
binding
包含以下
property
的对象属性 介绍 instance 使用指令的组件实例。 value 传递给指令的值。例如,在 v-my-directive="1 + 1"
,该值为 2。oldValue 先前的值,仅在 beforeUpdate
和updated
中可用。值是否已更改都可用。arg 参数传递给指令(如果有)。例如 v-my-directive:foo
中, arg 为 `"foo" 。modifiers 包含修饰符(如果有)的对象。例如 v-my-directive.foo.bar
,
修饰符对象为{foo: true, bar: true}
。dir 一个对象,在注册指令时作为参数传递,例如以下所示: app.directive('foucus', { mounted(el) { el.focus() } }) // dir 将会是以下对象 { mounted(el) { el.focus() } }
vnode
上面作为 el 参数收到的真是 DOM 元素蓝图
prevNode
上一个虚拟节点,仅在
beforeUpdate
和updated
钩子中可用。/* 官方提示:除了 el 之外,你应该将这些参数视为只读,并且永远不要修改他们,如果你需要跨钩子 共享信息建议通过元素的 --自定义数据属性集-- 进行共享。 这一块本人依然不是很懂,后续会继续跟进。 */ /* HTMLElement.dataset 属性允许无论是在读取模式和写入模式下访问在HTML 和 DOM 中的元素上 设置所有自定义数据属性( data- * )集。 ... 具体介绍,请查询以下地址: https://developer.mozilla.org/zh-CN/docs/Web/API/HTMLElement/dataset */
3. mixin (注册混入)
-
参数:
{ Object } mixin
-
返回值:
- 应用实例
-
用法:
在整个应用范围内应用混入,一旦注册,它们就可以在当前的应用中任何组件模板内使用,插件作者可以使用此方法将自定义行为注入组件,不建议在应用代码中使用。
// 混入(mixin) 提供了一种非常灵活的方式,来分发Vue组件中的可复用功能 // 一个混入对象可以包含任意组件选项 // 当组件使用混入对象时,所有混入对象的选项将被混合进入该组件本身的选项 import { createApp } from 'vue' const myMixin = { data() { return { msg: 'hello', foo: 'abc' } }, created() { this.hello() // => 'hello from mixin' }, methods: { hello() { console.log('hello from mixin') } } } // 全局注册: 方式一,注册实例时使用 mixins 属性,注册混入 const app = createApp({ mixins: [myMixin], // 混入选项,类型为 对象数组 // 当组件和混入对象含有同名的选项时,将会以 恰当 的方式进行 合并 // 数据对象在内部会进行递归合并,并在发生冲突时以组件数据优先 // 以下 data 中的 msg 属性与组件内部数据相同,将以此组件的 msg 为准 // 其他属性选项如:methods, components 和 directives 都将被合并为一个对象 // 有对象键名冲突时,取组件对象的键值对 data() { return { msg: 'goodbye', bar: 'def' } }, // 同名钩子函数将合并为一个数组,因此都将被调用 // 注意:混入对象的钩子将在组件 --自身钩子-- --之前-- 调用!!! // 所以 先打印 'hello from mixin' , 再打印 this.$data 的数据 !! created() { console.log(this.$data) // => {msg: 'goodbye', foo: 'abc', bar: 'def'} } }) // 全局注册: 方式二, mixin 方法 const vm = createApp({ myOption: 'hello' }) // 在自定义选项 'myOption' 注入一个处理器 // 一旦使用全局混入,它将影响每一个之后创建的组件 vm.mixin({ // created() { // 此处的 $options 为实例配置项 { myOption: 'hello'} const myOption = this.$options.myOption if (myOption) { console.log(myOption) } } }) vm.component('Cmp', { myOption: 'hello from component' }) vm.mount('#app') // => 'hello' => 'hello from component' // 自定义合并策略 const _app = createApp({ custom: 'hello' }) app.config.optionMergeStrategies.custom = (toVal, fromVal) => { console.log(fromVal, toVal) // => 'goodbye', undefined // => 'hello', 'goodbye' // 此处优先返回 fromVal 也就是组件传入值 'hello' // 若改为 toVal || fromVal 就始终返回 mixin 注册时的值 'goodbye' return fromVal || toVal } app.mixin({ custom: 'goodbye', created() { console.log(this.$options.custom) // => 'hello' } })
4. mount ( 实例挂载根组件 ), unmount ( 卸载实例根组件 )
-
参数:
{ Element | string} rootContainer
{ boolean } isHydrate
此参数 unmount 没有
-
返回值:
- 根组件实例
-
用法:
将应用实例的根组件挂载到指定的DOM元素上,及卸载实例的根组件,比容易理解。
-
示例:
<body> <div id="app"> </div> </body>
import { createApp } from 'vue' const app = createApp({}) // 上面有说过了这就不在赘述了 app.mount('#app') setTimeout(() => { // 此处调用 unmount 方法,卸载实例根组件 app.unmount('#app') }, 5000)
5. provide & inject
这个API的作用描述具体是叫 注册上下文 ? ?
-
参数:
{ sring | Symbol } key
value
-
返回值:
- 应用实例
-
用法:
provide
设置一个可以被注入到应用范围内所有组件的值,组件中使用inject
来接收提供的值。从
project
/inject
的角度来看,可以将应用程序视为跟级别的祖先,而根组件是唯一的子级。该方法不应该与 provide 组件选项 或 组合式API 中的 provide 方法混淆。虽然它们也是相同的
project
/inject
机制的一部分,但是是用来配置组件提供的值而不是应用提供的值。通过提供值写插件时尤其有用,因为插件一般不能使用组件提供的值,这时使用 globalProperties
替代选择。 —— 官方文档原文
-
示例:
import { createApp, computed } from 'vue' const app = createApp({ inject: ['name'], // 此处使用 inject 接收 provide 传入的值 template: '<div> {{ name }} </div>' // => 'Jack' }) // provide 方法提供一个 键名为 'name', 键值为 'Jack'的值 app.provide('name', 'Jack') // 组件之间向下传递示例 app.component('ParentCmp', { data() { return { todo: ['a', 'b', 'c'] } }, // 注意:要访问实例的 property,我们需要将 `provide` 转为返回对象函数 /* provide: { todoLength: this.todo.length // 此处报错,无法访问实例的 property } */ // 所以要使用以下形式 provide() { return { users: 'hello', todoLength: this.todo.length // 可以访问实例的 property // 当然我们也可以随时改变 todo 的数据,但是子组件数据并不会改变 // 若需要子组件接收的数据跟着改变,我们可以使用到 computed 方法 todoLength: computed(() => this.todo.length) } }, components: { ChildrenCmp } }) app.component('ChildrenCmp', { inject: ['todoLength'], created() { console.log(this.todoLength) // => 3 } })
6. use ( 使用插件 )
-
参数:
{ Object | Function } plugin
...options (可选)
-
返回值:
- 应用实例
-
用法:
安装 Vue.js 插件,如果插件是一个对象,它必须暴露一个 install 方法,如果它本身是一个函数,它将被视为安装方法
该安装方法将以应用实例作为第一个参数被调用,传给
use
的其他options
参数作为后续参数传入该安装方法当在同一个插件上多次调用此方法时,该插件将仅安装一次。
-
示例:
// 插件这块对于我目前只需知道怎么定义和怎么用就够,故就不细说了
import { createApp } from 'vue'
// 自定义一个简单的插件,对象的形式
const myPlugin = {
// 需要暴露一个 install 方法
install: (app, options) => {
// 将插件挂载到实例的 config 的 globalProperties 属性上
app.config.globalProperties.$myPlugin = (msg : string): void => {
console.log(msg)
}
}
}
// 函数形式
const myPlugin2 = (app, options) => {
// 此处为函数形式,无需定义 install 方法,函数本身将作为安装方法
app.config.globalProperties.$myPlugin2 = (msg : string): void => {
console.log(msg)
}
}
const app = createApp({
created() {
if (this.$myPlugin) {
this.$myPlugin('hello') // => 'hello'
this.$myPlugin2('world') // => 'world'
}
}
})
app.use(myPlugin)
app.use(myPlugin2)
3. 全局 API (Global API)
1. createApp
- 返回一个提供应用上下文的应用实例,应用实例挂载的整个组件树共享同一个上下问。
// 该函数接收一个根组件选项对象作为第一个参数
const app = Vue.createApp({
data() {
return {
...
}
},
methods: {...},
computed: {...},
...
})
// 使用第二个参数,可以将跟 prop 传递给应用程序
const vm = Vue.createApp(
{
props: ['name']
},
{
name: 'Jack'
}
)
// 类型声明
interface Data {
[key: string]: unknown
}
// HostElement 应该是个泛型, PublicAPIComponent 是个已经定义好的类 ?
// 这两个个类作者没看过源码,所以暂时不知道具体是怎么定义的,以后再补充
export type CreateAppFunction<HostElement> = {
rootComponent: PublicAPIComponent,
rootProps?: Data | null // rootProps 为非必要属性
} => App<HostElement>
2. h 函数
返回一个 "虚拟节点",通常缩写为 VNode,一个普通对象,其中包含想 Vue 描述它应在页面上渲染那种节点的信息,包括所有字节的的描述。它的目的是用于手动编写渲染函数:
// h 函数,作者个人认为类似 Vue2 的 render 函数以及 react 的 render函数
render() {
return Vue.h('h1', {}, 'Some title')
}
-
参数
接收三个参数:
type
,props
和children
-
type
(必须)-
类型:
String | Object | Function
-
详细:
HMTL标签名、组件或异步组件。返回 null 的函数将渲染一个注释
-
-
porps
(可选) // 标签内的属性-
类型:
Object
-
详细:
一个对象,与我们模板中使用的attribute、prop和事件相对应。
-
-
children (可选)
-
类型:
String | Object | Array
-
详细:
子代 VNode (节点),使用
h()
生成,或者使用字符串来获取 "文本 VNode",或带有插槽的对象。 -
示例:
h('div', {title: 'I am Parent'}, [ 'Parent', h('h1', {style: {color: 'red'}}, ['Title']), h(ChildrenComponent, {someProp: 'foobar'}) ])
-
-
3. defineComponent
从实现上看,defineComponent
只返回传递给他的对象。但是,就类型而言,返回的值有一个合成类型的构造函数,用于手动渲染函数,TSX 和 IDE 工具支持。
- 参数
<script lang='ts'>
import { defineComponent, ref } from 'vue'
// Vue2 中是直接导出一个具有组件选项的对象
// 而Vue3 用 defineComponent 函数进行合成后再导出
// 个人理解是Vue3中,defineComponent 在TypeScript下,给予了组件 "正确的参数类型推断"
export default defineComponent({
data() {
return {
count: 0 // 此处定义count, Vue2 用法
}
},
// Vue3 Composition API(重要的新特性)中的 setup(), 后续会介绍
setup() {
// 也可以在这里定义count,Vue3 用法
const count = ref(0)
},
methods: {
this.count++
}
})
</script>
4. defineAsyncComponent
创建一个只有在需要是才会加载的异步组件,对于基本用法 defineAsynComponent
可以接受返回一个返回Promise
的工厂函数。Promise 的 resolve
回调应该在服务端返回组件定义后调用,你也可以调用 reject(reason)
来表示加载失败。
import { createApp, defineAsynComponent } from 'vue'
const AsyncCmp = defineAsynComponent(() =>
import('./components/someCmp.vue')
)
// 全局注册
app.component('async-Cmp', AsynCmp)
// 局部注册
createApp({
components: {
AsyncCmp: defineAsynComponent(() =>
import('./components/someCmp.vue')
)
}
})
// 对于高阶用法, defineAsynComponent 可以接受一个对象
const AsyncCmp = defineAsynComponent({
// 工厂函数
loader: () => import('./Foo.vue'),
// 加载异步组件时要使用的组件
loadingComponent: LoadingComponent,
// 加载失败时要使用的组件
errorComponent: ErrorComponent,
// 在显示 loadingComponent 之前的延迟 | 默认 200ms
delay: 200,
// 如果提供了timeout,并且组件加载事件超时,将显示错误组件
// 默认 永不超时
timeout: 3000,
// 定义组件是否可挂起 | 默认 true
suspensible: false,
/*
error: 错误信息对象
retry: 一个函数,用于知识当 promise 加载器 reject 时,加载器是否重试
fail: 一个函数, 指示加载程序结束退出
attempts: 允许的最大重试次数
*/
onError(error, retry, fail, attempts) {
if (error.message.match(/fech/) && attempts <= 3) {
// 请求发生错误时重试,最大可尝试 3 此
retry()
} else {
// 注意:retry / fail 类似 promise 的 resolve / reject
// 必须调用其中一个才能继续错误处理
fail()
}
}
})
5. resolveComponent
const _注意 = 该方法只能在 render 或者 setup 函数中使用
- 上面声明了一个注意变量,下面只要有用到 “_注意 ” ,其内容都是一样的 ?
如果当前应用实例可使用,则允许按名称解析 component
返回一个 Component
。如果没有找到,则返回 undefined
import { createApp, resolveComponent } from 'vue'
const app = createApp({})
app.component('MyComponent', {...})
// 参数 name: String 已加载的组件的名称
render() {
const MyCmp = resolveComponent('MyComponent')
}
6. resolveDynamicComponent
_注意
允许使用与 <Component :is="">
相同的机制来解析一个 Component
返回已解析的 Component
或创建的 VNode
, 其中组件作为节点标签,如果找不到组件,将发出警告。
import { resolveDynamicComponent } from 'vue'
// 参数 Component: String | Object, 已加载的组件的名称 或 组件选项对象
render() {
const MyCmp = resolveDynamicComponent('MyComponent')
}
7. resolveDirective
_注意
如果当前应用实例中可用,则允许通过其名称解析一个 directive
,
返回一个 Directive
, 如果没有找到,则返回 undefined
import { resolveDirective, createApp } from 'vue'
const app = createApp({})
app.directive('focus', {})
// 参数 name: String 已加载的 指令 的名称
render() {
const focus = resolveDirective('focus')
}
8. withDirectives
_注意
允许将指令应用于 VNode, 返回一个包含应用指令的 VNode
import { withDirectives, resolveDirective } from 'vue'
const foo = resolveDirective('foo')
const bar = resolveDirective('bar')
// 参数: 接受两个参数 vnode 和 directives
// vnode : vnode 一个虚拟节点,通常用 h() 创建
// directives : Array 一个指令数组,每个指令本身都说数组,最多可以定义 4 个索引!
return withDirectives(h('div', {}, ['xxx']), [
[foo, this.x],
[bar, this.y]
])
// 这个作者个人还没理解清楚,后续弄清楚了再补充把
9. createRenderer
createRenderer 函数接收两个 泛型 参数: HostNode
和 HostElement
,对应于宿主环境的Node 和 Element类型。例如: 对于 run-time-down, HostNode 将是 DOM Node
接口, HostElement 将是 Element
接口。
自定义渲染器可以传入特定平台的类型,如下图:
import { createRenderer } from 'vue'
// 这里作者也还没弄清楚,就先不赘述了?
const {render, createApp} = createRenderer<Node, Element>({
patchProp,
...nodeOps
})
10. nextTick
将回调推迟到下一个 DOM 更新周期之后执行,再更改一些数据以等待 DOM 更新之后立即使用它。
- 参数:
{ Function } [callback]
- 示例:
import {createApp, nextTick, ref} from 'vue'
const app = createApp({
setup() {
const msg = ref('hello')
const changeMsg = async newMsg => {
msg.value = newMsg
await nextTick()
console.log('Now DOM is updated')
}
}
})
上篇暂时先更新到这,实在肝不动了,还剩挺多内容没写,争取在春节假期结束前出完中、后篇?,再次祝大家新年快乐,牛年大吉ba !
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!