VUEAPI


全局 API

应用

createApp()

  • 类型
1
function createApp(rootComponent: Component, rootProps?: object): App
  • 细节

    第一个参数是根组件 第二个是传递给跟组件的 props

  • 例子

    使用内联根组件

    1
    2
    3
    4
    5
    import { createApp } from "vue";

    const app = createApp({
    /* root component options */
    });

    使用引用组件

    1
    2
    3
    4
    import { createApp } from "vue";
    import App from "./App.vue";

    const app = createApp(App);

app.mount()

  • 类型

    1
    2
    3
    interface App {
    mount(rootContainer: Element | string): ComponentPublicInstance;
    }
  • 细节

    参数可以是实际的 DOM 元素或 CSS 选择器 返回根组件实例

    如果组件定义了模板或渲染函数 他将替代容器内现有的 DOM 节点 否则 将在运行时使用容器的 innerHTML 作为模板

    对于每个实例 mount()只能调用一次

app.unmount()

卸载已挂载的应用程序实例 触发应用程序组件树中所有组件的卸载生命周期

  • 类型
1
2
3
interface App {
unmount(): void;
}

app.provide

1
2
3
interface App {
provide<T>(key: InjectionKey<T> | symbol | string, value: T): this;
}
  • 细节

期望注入值作为第一个参数 提供的值作为第二个参数 返回应用程序实例本身

1
2
3
4
5
6
7
8
9
10
11
12
<script setup>
import { provide } from "vue";

provide(/* key */ "message", /* value */ "hello!");
</script>
<script setup>
import { inject } from "vue";

const message = inject("message");
const value = inject("message", "default value"); //注入默认值
const value = inject("key", () => new ExpensiveClass()); //避免在不适用可选值的情况下产生不必要的计算或副作用 可以使用工厂函数来创建默认值
</script>

app.component()

  • 类型
1
2
3
4
interface App {
component(name: string): Component | undefined;
component(name: string, component: Component): this;
}
  • 例子
1
2
3
import { createApp } from 'vue' const app = createApp({}) // register an options
object app.component('my-component', { /* ... */ }) // retrieve a registered
component const MyComponent = app.component('my-component')

app.directive()

如果定义字符串名称 则同时输入指令和指令 则只要检索已注册的指令

  • 类型
1
2
3
4
interface App {
directive(name: string): Directive | undefined;
directive(name: string, directive: Directive): this;
}
  • 例子
1
2
3
4
5
import { createApp } from 'vue' const app = createApp({ /* ... */ }) // register
(object directive) app.directive('my-directive', { /* custom directive hooks */
}) // register (function directive shorthand) app.directive('my-directive', ()
=> { /* ... */ }) // retrieve a registered directive const myDirective =
app.directive('my-directive')

app.use()

安装一个插件

1
2
3
interface App {
use(plugin: Plugin, ...options: any[]): this;
}
  • 细节

期望插件作为第一个参数 可选选项作为第二个参数

插件可以是带有install()方法的对象 也可以是直接的函数

app.use()在同一个插件上多次调用时 插件只会安装一次

  • 例子
1
2
import { createApp } from 'vue' import MyPlugin from './plugins/MyPlugin' const
app = createApp({ /* ... */ }) app.use(MyPlugin)

app.version

提供创建应用程序时使用的 vue 版本 这在插件中很有用 因为可能需要基于不同的 Vue 版本的条件逻辑

  • 类型
1
2
3
interface App {
version: string;
}
  • 例子

在插件中执行版本检查

1
2
3
4
5
6
7
8
export default {
install(app) {
const version = Number(app.version.split(".")[0]);
if (version < 3) {
console.warn("This plugin requires Vue 3");
}
},
};

app.config

app.config.errorHandler

从未应用程序未内部部署的一个内部处理程序

  • 类型
1
2
3
4
5
6
7
8
9
interface AppConfig {
errorHandler?: (
err: unknown,
instance: ComponentPublicInstance | null,
// `info` is a Vue-specific error info,
// e.g. which lifecycle hook the error was thrown in
info: string
) => void;
}
  • 细节

错误处理程序接收三个参数:错误、触发错误的组件实例和指定源类型的信息字符串

可以捕获到以下错误类型:

  • 渲染组件
  • 事件处理程序
  • 生命周期钩子
  • setup()功能
  • 观察者
  • 自定义指令钩子
  • 过度挂载
1
2
app.config.errorHandler = (err, instance, info) => { // handle error, e.g.
report to a service }

app.config.warnHandler

为来自 Vue 的运行时警告分配一个自定义处理程序

  • 类型
1
2
3
4
5
6
7
8
interface AppConfig {
warnHandler?: (
msg: string,
instance: ComponentPublicInstance | null,
trace: string
) => void;
}
//第一个参数时接收警告信息 第二个参数是源组件实例 第三个参数是组件跟踪字符串

用于过滤特定的警告以减少控制台的冗长 所有 Vue 警告都应该在开发期间解决 因此建议在调试会话期间关注许多特定警告并在调试完成后将其删除

仅在开发期间有效 因此在生成模式下会忽略此配置

1
2
app.config.warnHandler = (msg, instance, trace) => { // `trace` is the component
hierarchy trace }

app.config.performance

设置为 true 在浏览器开发工具性能/事件线面板中启用组件初始化、编译、渲染和修补性能跟踪 仅用于开发模式和支持performance.mark API 的浏览器。

app.compilerOptions.isCustomElement

指定一个检查方法来识别本机自定义元素

  • 类型:(tag: string) => boolean
  • 细节

如果标签被视为本机自定义元素 则应返回 对于匹配的标签 Vue 将其呈现为原生元素 而不是尝试将其解析为 Vue 组件

1
2
3
// treat all tags starting with 'ion-' as custom elements
app.config.compilerOptions.isCustomElement = (tag) => { return
tag.startsWith('ion-') }

app.compilerOptions.whitespace

调整模板空白处理行为

  • 类型:‘condense' | 'preserve'
  • 默认: 'condense'
  • 细节

Vue 删除/压缩模板中的空白字符以产生更有效的编译输出 默认策略是“浓缩”

  • 元素内的前导/结束空白字符被压缩成一个字符
  • 包含换行符的元素之间的空白字符被删除
  • 文本节点中连续的空白字符被压缩成一个空格

将吃选项设置为'preserve'将禁用(2) 和 (3)

1
app.config.compilerOptions.whitespace = 'preserve'

app.compilerOptions.delimiters

调整用于模板内文本插值的分隔符

  • 类型: [string, string]
  • 默认: ['{{', '}}']
  • 细节

通常用于避免与使用 mustache 语法的服务器框架发生冲突

1
2
// Delimiters changed to ES6 template string style
app.config.compilerOptions.delimiters = ['${', '}']

app.compilerOptions.comments

调整模板中 HTML 注释的处理方法

  • boolean
  • 默认: false
  • 细节

默认情况下 Vue 会删除生产环境中的注释 将此选项设置为 true 将强制 vue 即使在生产中也保留注释 在开发过程中始终保留注释

1
app.config.compilerOptions.comments = true

app.config.globalProperties

可用于注册全局属性的对象 可在应用程序内的任何组件实例上访问这些属性

1
2
3
interface AppConfig {
globalProperties: Record<string, any>;
}
  • 细节

  • 与任何全局的东西一样 应该谨慎使用它

  • 如果全局属性与自己的属性充裕 组件自己的属性将拥有更高的优先级

  • ```vue
    app.config.globalProperties.msg = ‘hello’ export default { mounted() {
    console.log(this.msg) // ‘hello’ } }

    1
    2
    3
    4
    5
    6
    7

    ### nextTick()

    等待下一次 DOM 更新刷新的应用程序

    ```typescript
    function nextTick(callback?: () => void): Promise<void>;
  • 细节

当你在 Vue 改变响应状态时 生成的 DOM 更新不会同步应用 Vue 会缓存它们直到下一个 nextTick()以确保无论您进行了多少次状态更改每个组件都只更新一次

nextTick()可以在状态更改后立即使用以等待 DOM 更新完成 可以将回调作为参数传递 也可以等待返回的 Promise

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<script setup>
import { ref, nextTick } from "vue";

const count = ref(0);

async function increment() {
count.value++;

// DOM not yet updated
console.log(document.getElementById("counter").textContent); // 0

await nextTick();
// DOM is now updated
console.log(document.getElementById("counter").textContent); // 1
}
</script>

<template>
<button id="counter" @click="increment">{{ count }}</button>
</template>

defineComponent()

用于定义具有类型推断的 Vue 组件的类型助手

1
2
3
function defineComponent(
component: ComponentOptions | ComponentOptions["setup"]
): ComponentConstructor;

defineAsyncComponent()

定义一个仅在渲染时才延迟加载的异步组件 参数可以是加载器函数 也可以是用于更高级控制加载行为的选项对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function defineAsyncComponent(
source: AsyncComponentLoader | AsyncComponentOptions
): Component;

type AsyncComponentLoader = () => Promise<Component>;

interface AsyncComponentOptions {
loader: AsyncComponentLoader;
loadingComponent?: Component;
errorComponent?: Component;
delay?: number;
timeout?: number;
suspensible?: boolean;
onError?: (
error: Error,
retry: () => void,
fail: () => void,
attempts: number
) => any;
}

Composition api

setup()

如果对 props 对象解构 那么将失去响应性

可以使用 toRefs()或 toRef()程序执行

1
2
3
4
5
import { toRefs, toRef } from 'vue' export default { setup(props) { // turn
`props` into an object of refs, then destructure const { title } = toRefs(props)
// `title` is a ref that tracks `props.title` console.log(title.value) // OR,
turn a single property on `props` into a ref const title = toRef(props, 'title')
} }

Setup Context

1
2
3
4
5
export default { setup(props, context) { // Attributes (Non-reactive object,
equivalent to $attrs) console.log(context.attrs) // Slots (Non-reactive object,
equivalent to $slots) console.log(context.slots) // Emit events (Function,
equivalent to $emit) console.log(context.emit) // Expose public properties
(Function) console.log(context.expose) } }

context 不是响应式的 可以安全地解构

1
export default { setup(props, { attrs, slots, emit, expose }) { ... } }

如果打算对更改执行副作用 应该在生命周期钩子中执行此操作attrsslotsattrsslotsonBeforeUpdate

expose是一个函数 可以显式限制父组件通过模板 refs 访问组件实例时暴露的属性:

1
2
3
4
5
6
7
8
9
10
11
12
export default {
setup(props, { expose }) {
// make the instance "closed" -
// i.e. do not expose anything to the parent
expose();

const publicCount = ref(0);
const privateCount = ref(0);
// selectively expose local state
expose({ count: publicCount });
},
};

与渲染函数一起使用

setup还可以返回一个渲染函数 该函数可以直接使用同一范围内声明的反应状态

1
2
3
4
5
6
7
8
import { h, ref } from "vue";

export default {
setup() {
const count = ref(0);
return () => h("div", count.value);
},
};

返回一个渲染函数可以的防止我们返回其他任何东西 可以使用expose()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import { h, ref } from "vue";

export default {
setup(props, { expose }) {
const count = ref(0);
const increment = () => ++count.value;

expose({
increment,
});

return () => h("div", count.value);
},
};

然后 该increment方法将通过模板引用在父组件中可用

反应性核心

ref()

接收一个内部值并返回一个反应性和响应的 ref 对象 他还有一个.value指向内部值的属性

1
2
3
4
5
function ref<T>(value: T): Ref<UnwrapRef<T>>;

interface Ref<T> {
value: T;
}
  • 细节

如果一个对象被分配为一个 ref 的值 那么该对象将与reactive()进行深度响应 这也意味着如果对象包含嵌套的 ref 它们将被深度展开

为了避免深度转换 请使用shallowRef()

computed()

接收一个函数并未 getter 的返回值返回一个实时的ref对象 可以使用具有getset函数的对象来创建 ref 对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// read-only
function computed<T>(
getter: () => T,
// see "Computed Debugging" link below
debuggerOptions?: DebuggerOptions
): Readonly<Ref<Readonly<T>>>;

// writable
function computed<T>(
options: {
get: () => T;
set: (value: T) => void;
},
debuggerOptions?: DebuggerOptions
): Ref<T>;

reactive()

1
function reactive<T extends object>(target: T): UnwrapNestedRefs<T>;

当使用数组的元素或者集合类型如(Map) 为避免深度转换并仅在根级别保留响应性 可以改用shallowReactive()

返回的对象及其嵌套对象被 ES Proxy 包裹 不等于原始对象 建议只使用响应式代理并避免依赖原始对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const count = ref(1);
const obj = reactive({ count });

// ref will be unwrapped
console.log(obj.count === count.value); // true

// it will update `obj.count`
count.value++;
console.log(count.value); // 2
console.log(obj.count); // 2

// it will also update `count` ref
obj.count++;
console.log(obj.count); // 3
console.log(count.value); // 3

当作为数组或集合元素访问时 refs 不会被解构

1
2
3
4
5
6
7
const books = reactive([ref("Vue 3 Guide")]);
// need .value here
console.log(books[0].value);

const map = reactive(new Map([["count", ref(0)]]));
// need .value here
console.log(map.get("count").value);

将 ref 分配给 reactive 属性时 该 ref 将自动展开

1
2
3
4
5
6
7
const count = ref(1);
const obj = reactive({});

obj.count = count;

console.log(obj.count); // 1
console.log(obj.count === count.value); // true

readonly()

1
2
3
function readonly<T extends object>(
target: T
): DeepReadonly<UnwrapNestedRefs<T>>;

它展示的任何属性都将是潜在的reactive()

避免深度转换 请使用shallowReadonly()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const original = reactive({ count: 0 });

const copy = readonly(original);

watchEffect(() => {
// works for reactivity tracking
console.log(copy.count);
});

// mutating original will trigger watchers relying on the copy
original.count++;

// mutating the copy will fail and result in a warning
copy.count++; // warning!

watchEffect()

立即运行一个函数,同时跟随变化时重新关注它的依赖关系,并在依赖关系发生时重新运行它。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function watchEffect(
effect: (onCleanup: OnCleanup) => void,
options?: WatchEffectOptions
): StopHandle;

type OnCleanup = (cleanupFn: () => void) => void;

interface WatchEffectOptions {
flush?: "pre" | "post" | "sync"; // default: 'pre'
onTrack?: (event: DebuggerEvent) => void;
onTrigger?: (event: DebuggerEvent) => void;
}

type StopHandle = () => void;
  • 细节

第一个参数是要运行的效果函数 效果函数接收可用于注册清理回调的函数 清理回调将在下一次重新运行效果之前调用 可用于清理无效的副作用 例如挂起的异步请求

第二个参数是可选的选项对象 用于调整效果的刷新时间或调试效果的依赖关系

返回值是一个 handdle 函数可以用来组织效果再次运行

1
2
3
4
5
6
7
const count = ref(0);

watchEffect(() => console.log(count.value));
// -> logs 0

count.value++;
// -> logs 1
1
2
3
4
5
6
const count = ref(0);

watchEffect(() => console.log(count.value));
// -> logs 0

count.value++;

副作用清理

1
2
3
4
5
6
7
8
watchEffect(async (onCleanup) => {
const { response, cancel } = doAsyncWork(id.value);
// `cancel` will be called if `id` changes
// so that previous pending request will be cancelled
// if not yet completed
onCleanup(cancel);
data.value = await response;
});

停止观察者

1
2
3
4
const stop = watchEffect(() => {});

// when the watcher is no longer needed:
stop();

Options:

1
2
3
4
5
6
7
8
9
watchEffect(() => {}, {
flush: "post",
onTrack(e) {
debugger;
},
onTrigger(e) {
debugger;
},
});

watch()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// watching single source
function watch<T>(
source: WatchSource<T>,
callback: WatchCallback<T>,
options?: WatchOptions
): StopHandle;

// watching multiple sources
function watch<T>(
sources: WatchSource<T>[],
callback: WatchCallback<T[]>,
options?: WatchOptions
): StopHandle;

type WatchCallback<T> = (
value: T,
oldValue: T,
onCleanup: (cleanupFn: () => void) => void
) => void;

type WatchSource<T> =
| Ref<T> // ref
| (() => T) // getter
| T extends object
? T
: never; // reactive object

interface WatchOptions extends WatchEffectOptions {
immediate?: boolean; // default: false
deep?: boolean; // default: false
flush?: "pre" | "post" | "sync"; // default: 'pre'
onTrack?: (event: DebuggerEvent) => void;
onTrigger?: (event: DebuggerEvent) => void;
}
  • 细节

watch默认是惰性的 即当监视的源发生更改时才调用回调

第三个参数时一个options

  • **immediate**:在观察者创建时立即触发回调。旧值将undefined在第一次调用时出现。
  • **deep**:如果源是对象,则强制深度遍历源,以便回调触发深度突变。
  • **flush**:调整回调的刷新时间。
  • onTrack / onTrigger: 调试观察者的依赖

相比watchEffect()watch()允许我们:

  • 懒惰地执行副作用;
  • 更具体地说明应该触发观察者重新运行的状态;
  • 访问监视状态的先前值和当前值。

当观察多个源时 回调函数将接收包含与源数组对应的新/旧值的数组

1
2
3
watch([fooRef, barRef], ([foo, bar], [prevFoo, prevBar]) => {
/* ... */
});

如果希望回调即使在深度突变时也能触发 那么需要使用显式强制观察者进入深度模式{deep: true} 在深度模式下 如果回调是由深度突变触发的 那么新值和旧值将是同一个对象

1
2
3
4
5
6
7
8
const state = reactive({ count: 0 });
watch(
() => state,
(newValue, oldValue) => {
// newValue === oldValue
},
{ deep: true }
);

如果直接观察响应式对象 观察者处于深度模式

1
2
3
4
const state = reactive({ count: 0 });
watch(state, () => {
/* triggers on deep mutation to state */
});

Reactivity: Utilities

isRef()

检查一个值是否是一个 ref 对象

1
function isRef<T>(r: Ref<T> | unknown): r is Ref<T>;

返回类型是一个类型谓词 可以作为类型保护

1
2
3
4
5
let foo: unknown;
if (isRef(foo)) {
// foo's type is narrowed to Ref<unknown>
foo.value;
}

unref()

如果参数是 ref 则返回内部值 否则返回参数本身

1
val = isRef(val) ? val.value : val;

toRef()

用于为源响应对象上的属性创建 ref 创建的 ref 与其源属性同步 改变原属性将更新 ref

1
2
3
4
5
6
7
function toRef<T extends object, K extends keyof T>(
object: T,
key: K,
defaultValue?: T[K]
): ToRef<T[K]>;

type ToRef<T> = T extends Ref ? T : Ref<T>;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
const state = reactive({
foo: 1,
bar: 2,
});

const fooRef = toRef(state, "foo");

// mutating the ref updates the original
fooRef.value++;
console.log(state.foo); // 2

// mutating the original also updates the ref
state.foo++;
console.log(fooRef.value); // 3

当你想将 prop 的 ref 传递给可组合函数时很有用

1
2
3
4
5
<script setup>
import {toRef} from 'vue' const props = defineProps(/* ... */) // convert
`props.foo` into a ref, then pass into // a composable
useSomeFeature(toRef(props, 'foo'))
</script>

但是如果尝试为 ref 分配新值时相当直接修改 prop 这是不允许的 这种情况下 可以考虑使用 computed 来替代

toRefs()

将响应式对象转换为普通对象 其中结果对象的每个属性都是指向原始对象相应属性的 ref 每个单独的 ref 都是使用 toRef()```创建的

1
2
3
4
5
6
7
function toRefs<T extends object>(
object: T
): {
[K in keyof T]: ToRef<T[K]>;
};

type ToRef = T extends Ref ? T : Ref<T>;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const state = reactive({
foo: 1,
bar: 2,
});

const stateAsRefs = toRefs(state);
/*
Type of stateAsRefs: {
foo: Ref<number>,
bar: Ref<number>
}
*/

// The ref and the original property is "linked"
state.foo++;
console.log(stateAsRefs.foo.value); // 2

stateAsRefs.foo.value++;
console.log(state.foo); // 3

toRefs 在从可组合函数返回响应式对象时很有用 以便使用组件可以构建/扩展返回的对象而不会失去响应性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function useFeatureX() {
const state = reactive({
foo: 1,
bar: 2,
});

// ...logic operating on state

// convert to refs when returning
return toRefs(state);
}

// can destructure without losing reactivity
const { foo, bar } = useFeatureX();

toRefs只会在调用时为源对象上可枚举的属性生成引用。要为可能尚不存在的属性创建 ref,请toRef改用。

isProxy()

Checks if an object is a proxy created by reactive(), readonly(), shallowReactive() or shallowReadonly().

isReactive()

Checks if an object is a proxy created by reactive() or shallowReactive().

isReadonly()

Checks if an object is a proxy created by readonly() or shallowReadonly().

响应式:高级

shallowRef

1
2
3
4
function shallowRef<T>(value: T): ShallowRef<T>;
interface ShallowRef<T> {
value: T;
}

ref()不同 shallowdRef的内部值按原样储存和公开 不会被深度响应 只有.value是响应性的

通常用于大型数据结构的性能优化 或外部状态管理系统的集成

1
2
3
4
5
6
7
const state = shallowRef({ count: 1 });

// does NOT trigger change
state.value.count = 2;

// does trigger change
state.value = { count: 2 };

triggerRef()

强制触发效果取决于shallowRef 这通常在浅反应的内部值进行深度突变后使用

1
function triggerRef(ref: ShallowRef): void;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
const shallow = shallowRef({
greet: "Hello, world",
});

// Logs "Hello, world" once for the first run-through
watchEffect(() => {
console.log(shallow.value.greet);
});

// This won't trigger the effect because the ref is shallow
shallow.value.greet = "Hello, universe";

// Logs "Hello, universe"
triggerRef(shallow);

customRef()

创建一个自定义的 ref 显式控制其依赖跟踪和更新触发

1
2
3
4
5
6
7
8
9
function customRef<T>(factory: CustomRefFactory<T>): Ref<T>;

type CustomRefFactory<T> = (
track: () => void,
trigger: () => void
) => {
get: () => T;
set: (value: T) => void;
};
  • 细节

customRef()需要一个工厂函数 它接收tracktrigger函数作为参数 并返回一个带有getset方法的对象

track应该叫get() trigger 应该叫set 但是 我们可以控制合适调用以及是否调用它们

  • 例子

创建一个 debounced ref 它只在最近一次 set 调用某个超时后的更新值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import { customRef } from 'vue'

export function useDebouncedRef(value, delay = 200) {
let timeout
return customRef((track, trigger) => {
return {
get() {
track()
return value
},
set(newValue) {
clearTimeout(timeout)
timeout = setTimeout(() => {
value = newValue
trigger()
}, delay)
}
}
})
}
//在组件中的用法
<script setup>
import { useDebouncedRef } from './debouncedRef'
const text = useDebouncedRef('hello')
</script>

<template>
<input v-model="text" />
</template>
//可用于搜索等功能的防抖节流 可以避免频繁的请求 用watch监听

shallowReactive()

1
function shallowReactive<T extends object>(target: T): T;
  • 细节

没有深度响应 只有根级属性是响应性的 属性值按原样储存和公开 这也意味着具有 ref 值的属性不会自动展开

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const state = shallowReactive({
foo: 1,
nested: {
bar: 2,
},
});

// mutating state's own properties is reactive
state.foo++;

// ...but does not convert nested objects
isReactive(state.nested); // false

// NOT reactive
state.nested.bar++;

shallowReadonly()

1
function shallowReadonly<T extends object>(target: T): Readonly<T>;

与 不同readonly()的是,没有深度转换:只有根级属性是只读的。属性值按原样存储和公开 - 这也意味着具有 ref 值的属性不会自动展开。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const state = shallowReadonly({
foo: 1,
nested: {
bar: 2,
},
});

// mutating state's own properties will fail
state.foo++;

// ...but works on nested objects
isReadonly(state.nested); // false

// works
state.nested.bar++;

toRaw()

返回 Vue 创建的代理的原始对象

1
function toRaw<T>(proxy: T): T;
  • 细节

toRaw() can return the original object from proxies created by reactive(), readonly(), shallowReactive() or shallowReadonly().

可用于临时读取而不会产生代理访问/跟踪开销或写入而部触发更改 不建议持有对原始对象的持久引用 谨慎使用

1
2
3
4
const foo = {};
const reactiveFoo = reactive(foo);

console.log(toRaw(reactiveFoo) === foo); // true

effectScope()

可以在其中创建一个对象 api 观察对象的范围(computed and watchers)以便观察这些对象的详细使用情况

1
2
3
4
5
6
function effectScope(detached?: boolean): EffectScope;

interface EffectScope {
run<T>(fn: () => T): T | undefined; // undefined if scope is inactive
stop(): void;
}
1
2
3
4
5
6
7
8
9
10
11
12
const scope = effectScope();

scope.run(() => {
const doubled = computed(() => counter.value * 2);

watch(doubled, () => console.log(doubled.value));

watchEffect(() => console.log("Count: ", doubled.value));
});

// to dispose all effects in the scope
scope.stop();

生命周期钩子

onMounted

组件在以下情况视为已挂载完成

  • 他的所有同步子组件都已挂载(不包括异步组件或<Suspense>树内的组件)
  • 它自己的 DOM 树已创建并插入到父容器中 如果应用程序的根容器也在文档内 它只保证组件的 DOM 树在文档内

通常用于执行需要访问组件渲染的 DOM 的副作用

在服务端渲染期间不会调用此钩子

onUpdated()

注册一个回调 以便在组件由于响应式状态改变而更新其 DOM 树后调用

  • 细节

父组件的更新钩子在其子组件之后被调用

在组件的任何 DOM 更新后都会调用此钩子 这可能是由不同的状态更改引起的 如果需要在特定的状态更改后访问更新的 DOM 改用 nextTick()

服务器渲染期间不会调用此钩子

不要在此钩子中改变组件状态 这可能会导致无限更新循环

onUnmounted()

注册一个在组件被卸载后调用的回调

  • 以下情况组件已被卸载

    • 他的所有子组件都已卸载
    • 预期相关的响应状态(渲染效果以及期间创建的计算属性及观察者)都已停止

    使用此钩子清除手动创建的副作用 例如计时器 DOM 时间侦听器或服务器连接

    在服务器渲染期间不会调用此钩子

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <script setup>
    import { onMounted, onUnmounted } from "vue";

    let intervalId;
    onMounted(() => {
    intervalId = setInterval(() => {
    // ...
    });
    });

    onUnmounted(() => clearInterval(intervalId));
    </script>

onBeforeMount()

当这个钩子被调用时 说明组件已经完成了响应状态的设置 但是还没有创建 DOM 节点 它即将第一次执行他的 DOM 渲染效果

在服务端渲染期间不会被调用

onBeforeUpdate()

由于响应式状态更新 在组件即将更新其 DOM 树之前调用

可以用于 Vue 更新 DOM 之前访问 DOM 状态 在这个钩子中修改组件的状态也是安全的

服务端渲染期间不会调用此钩子

onBeforeUnmount()

在组件要卸载之前调用

调用此钩子时 组件实例仍然可以正常工作

服务端渲染期间不会调用此钩子

onErrorCaptured()

当捕获到从后代组件传播的错误时 注册要调用的钩子

可以从以下错误源捕获错误

  • 组件渲染
  • 事件处理程序
  • 生命周期钩子
  • setup()功能
  • 观察者
  • 自定义指令钩子
  • 过渡挂钩

钩子接收三个参数:错误、触发错误的组件实例和指定错误类型的信息字符串

可以修改组件状态以errorCaptured()向用户展示错误状态 但是 重要的错误状态 但是 重要的错误状态不应该呈现导致错误的原始内容 否则组件将抛出无限渲染循环

钩子可以返回false以阻止错误进一步传播

错误传播规则

  • 默认情况下 app.config.errorHandler如果已定义 所有错误仍会发送到应用程序级别 因此这些错误仍然可以在一个地方报告给分析服务

  • 如果一个组件的继承链或父链上存在多个errorCaptured钩子 它们会在同一个错误时被调用

  • 如果errorCaptured钩子本身抛出一个错误 这个错误和原始捕获的错误都会被发送到app.config.errorHandler

  • errorCaptured钩子可以返回false防止错误进一步传播 这本质上说明这个错误已经被处理 应该被忽略 它将防止任何额外的errorCapturedapp.config,errorHandler

    被此错误调用

onRenderTracked()

onActivated()

为了在组件中插入 DOM 作为缓存的一部分后调用<KeepAlive>专属

onDeactivated()

为了从 DOM 删除后调用 作为缓存的树的部分 <keepAlive>

依赖注入

provide()

1
function provide<T>(key: InjectionKey<T> | string, value: T): void;
  • 细节

Symbol可用于在provide()inject()之间同步值类型

provode必须在组件setup()阶段同步调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<script setup>
import { ref, provide } from "vue";
import { fooSymbol } from "./injectionSymbols";

// provide static value
provide("foo", "bar");

// provide reactive value
const count = ref(0);
provide("count", count);

// provide with Symbol keys
provide(fooSymbol, count);
</script>

inject()

1
2
3
4
5
6
7
8
9
10
11
function inject<T>(key: InjectionKey<T> | string): T | undefined;

// with default value
function inject<T>(key: InjectionKey<T> | string, defaultValue: T): T;

// with factory
function inject<T>(
key: InjectionKey<T> | string,
defaultValue: () => T,
treatDefaultAsFactory: true
): T;
  • 细节

第一个参数是 key Vue 将沿着父链查找具有匹配键的提供值 如果父链中的多个组件提供相同的 key 则使用最接近注入组件的那个 如果没有找到相匹配的键 则返回 undefined 或提供的默认值

第二个参数是可选的 在没有找到匹配的 key 的适合作为默认值 也可以是应该工厂函数来返回创建成本高的值 如果默认值是一个函数 那么 false 必须作为第三个参数传递 表示该函数作为值而不是工厂

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<script setup>
import { inject } from "vue";
import { fooSymbol } from "./injectionSymbols";

// inject static value with default
const foo = inject("foo");

// inject reactive value
const count = inject("count");

// inject with Symbol keys
const foo2 = inject(fooSymbol);

// inject with default value
const bar = inject("foo", "default value");

// inject with default value factory
const baz = inject("foo", () => new Map());

// inject with function default value, by passing the 3rd argument
const fn = inject("function", () => {}, false);
</script>

内置插件

内置指令

v-text

更新元素的文本内容

v-text通过设置元素的 textContent 属性来工作 因此它将覆盖元素内所有的现有内容 如果只需要更新部分内容 可以使用 mustache 插值代替

1
2
3
<span v-text="msg"></span>
<!-- same as -->
<span>{{msg}}</span>

v-show

v-if

v-for

v-for 默认行为将尝试在不移动元素的情况下就地修补元素 要强制它重新排序 应该提供一个带有key的特殊属性的排序提示

v-for 也可以处理包括 Map 和 Set

v-on

修饰符:

  • .stop- 触发event.stopPropagation()
  • .prevent- 触发event.preventDefault()
  • .capture- 在捕获模式下添加事件监听器。
  • .self- 仅在从该元素分派事件时触发处理程序。
  • .{keyAlias}- 仅在某些键上触发处理程序。
  • .once- 最多触发一次处理程序。
  • .left- 仅触发鼠标左键事件的处理程序。
  • .right- 仅触发鼠标右键事件的处理程序。
  • .middle- 仅触发中键鼠标事件的处理程序。
  • .passive- 附加一个 DOM 事件{ passive: true }

v-model

.lazy监听的是change事件而不是input事件

.number将有效的输入字符串转换为数字

..trim修建输入

v-slot

v-pre

跳过此元素及其所有子元素的编译

最常用的用力是显式原始标签

1
2
<span v-pre>{{ this will not be compiled }}</span> //渲染结果:{{ this will not
be compiled }}

v-once

仅渲染元素和组件一次 并跳过以后的更新

在随后的重新渲染中 元素/组件及其所有子元素将被视为静态内容并跳过 这可用于优化更新性能

1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- single element -->
<span v-once>This will never change: {{msg}}</span>
<!-- the element have children -->
<div v-once>
<h1>comment</h1>
<p>{{msg}}</p>
</div>
<!-- component -->
<my-component v-once :comment="msg"></my-component>
<!-- `v-for` directive -->
<ul>
<li v-for="i in list" v-once>{{i}}</li>
</ul>

v-memo

记忆模板的子树 可用于元素和组件 该指令需要一个固定长度的依赖值数组来记忆比较 如果数组中的每个值都和上次渲染相同 则跳过整个子树的更新

1
<div v-memo="[valueA, valueB]">...</div>

当组件重新渲染时 如果 valueA 和 valueB 都和之前用于 那么它和它的子树的更新都会直接被跳过 甚至连虚拟 dom 的创建都被跳过

正确指定记忆数组很重要 负责我们肯会跳过应该更新的更新 v-memo 具有空依赖数组(v-memo=”[]” ) 在功能上等同于 v-once

与 v-for 结合

v-memo仅为性能关键场景的微优化而提供 很少需要用到 最常见的情况可能是渲染大型 v-for 列表时 (其中 length> 1000)

1
2
3
4
<div v-for="item in list" :key="item.id" v-memo="[item.id === selected]">
<p>ID: {{ item.id }} - selected: {{ item.id === selected }}</p>
<p>...more child nodes</p>
</div>

当组件的selected状态发生变化时,即使大部分项目保持不变,也会创建大量的 VNode。这里的v-memo用法本质上是说“仅当它从未选中变为选中时才更新此项目,或者相反”。这允许每个未受影响的项目重用其先前的 VNode 并完全跳过差异。请注意,我们不需要item.id在此处包含在 memo 依赖数组中,因为 Vue 会自动从项目的:key.

v-memo也可以在组件上使用,以在子组件更新检查已被取消优化的某些边缘情况下手动防止不需要的更新。但同样,开发人员有责任指定正确的依赖数组以避免跳过必要的更新。

组件

Transition

为单个元素或组件提供过度效果

  • @before-enter
  • @before-leave
  • @enter
  • @leave
  • @appear
  • @after-enter
  • @after-leave
  • @after-appear
  • @enter-cancelled
  • @leave-cancelledv-show仅限)
  • @appear-cancelled

简单元素

1
2
3
<Transition>
<div v-if="ok">toggled content</div>
</Transition>

动态组件 出现过度模式 + 动画

1
2
3
<Transition name="fade" mode="out-in" appear>
<component :is="view"></component>
</Transition>

监听事件转换

1
2
3
<Transition @after-enter="onTransitionComplete">
<div v-show="ok">toggled content</div>
</Transition>

TransitionGroup

为列表中的多个元素或组件提供过度效果

单文件组件

脚本设置

<script setup>的优点

  • 更简洁的代码 更少的样板
  • 能够使用纯 typescript 声明 props 和发出的事件
  • 更好的运行时性能(模板编译成同范围内的渲染函数 无需中间代理)
  • 更好的 IDE 类型推断性能(服务器从代码中提取类型的工作量更少)

命名空间组件

可以使用带有点的组件标签来引用嵌套在对象属性下的组件 当从单个文件中导入多个组件时很有用

1
2
3
4
5
6
7
8
9
<script setup>
import * as Form from "./form-components";
</script>

<template>
<Form.Input>
<Form.Label>label</Form.Label>
</Form.Input>
</template>

使用自定义指令

全局注册的自定义指令正常工作 本地自定义指令不需要显式注册 但是必须遵守命名方案vNameOfDirective

1
2
3
4
5
6
7
8
9
10
<script setup>
const vMyDirective = {
beforeMount: (el) => {
// do something with the element
},
};
</script>
<template>
<h1 v-my-directive>This is a Heading</h1>
</template>

如果从其他地方引入 可以将其重命名以使用所需的方案

1
2
3
<script setup>
import { myDirective as vMyDirective } from "./MyDirective.js";
</script>

defineProps() 和 defineEmits()

1
2
3
4
5
6
7
8
<script setup>
const props = defineProps({
foo: String,
});

const emit = defineEmits(["change", "delete"]);
// setup code
</script>

传递给defineProps并将defineEmits从设置中提升到模块范围的选项。因此,选项不能引用在设置范围内声明的局部变量。这样做会导致编译错误。但是,它可以引用导入的绑定,因为它们也在模块范围内。

useSlots()&useAttrs()

1
2
3
4
5
6
<script setup>
import { useSlots, useAttrs } from "vue";

const slots = useSlots();
const attrs = useAttrs();
</script>


文章作者: olddog
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 olddog !
  目录