跳转至
本文阅读量

1. 这里是 Vue 学习笔记

1.1 一些整理

1.1.1 storeToRefs

storeToRefs 是 Pinia 提供的 Composition API 辅助函数。它的作用是 安全地解构 store 中的 state 属性,同时保持它们的响应性

它遍历 Pinia Store 实例的 state 属性,并使用 Vue 的 toRef() 方法将每一个状态属性(例如 status, retryAttemptCount)转换为独立的 响应式引用 (Ref)。

在 Vue 3 的 Composition API 中,如果你直接解构一个响应式对象,会失去响应性

<script setup>
import { useAppStore } from '@/stores/useAppStore';
import { storeToRefs } from 'pinia'; // <-- 关键工具
import { inject } from 'vue';

// 1. 获取 Pinia Store 实例
// 这一步会访问到 Pinia 在内存中维护的 'app' store 的单例实例
const store = useAppStore();

// 2. 提取响应式引用 (Subscription的核心)
// storeToRefs 是 Pinia 提供的 Composition API 辅助函数。
// 它的作用是:安全地解构 store 中的 state 属性,同时保持它们的响应性。
const { status, retryAttemptCount, lastError } = storeToRefs(store); 

// 3. 模板中使用(自动订阅)
// 此时,status、retryAttemptCount 等都是 Vue 的 Ref 对象。
// 只要 ConnectionManager 调用了 store.setStatus(),这些 Ref 就会更新,
// 从而触发依赖它们的组件模板自动重新渲染。

const appManager = inject('AppManager'); // 注入 Manager 实例,用于调用方法
// ... reconnect 逻辑 ...

</script>

<template>
  <!-- 模板中直接使用提取出来的 Ref,无需 .value -->
  <p>Current State: <strong>{{ status }}</strong></p>

  <div v-if="status === 'CONNECTING'">
    Attempt #{{ retryAttemptCount }}...
  </div>
  <!-- ... -->
</template>

1.1.2 vue 中的 toRef() 是什么意思

toRef() 是 Vue 3 Composition API 提供的一个工具函数,用于 将一个响应式对象上的某个属性,转换为一个独立的、保持连接的 Ref 对象。

当你从一个响应式对象中解构出某个属性时,你通常会失去该属性的响应性。toRef() 的核心价值就在于:它创建的 Ref 是对源对象属性的一个引用(指针),而不是值的拷贝。

import { toRef } from 'vue';

const propertyRef = toRef(source: object, key: string);
import { reactive, toRef } from 'vue';

const state = reactive({
  count: 0,
  name: 'Vue'
});

// 1. 使用 toRef 创建一个 count 的引用
const countRef = toRef(state, 'count'); 

// 2. 它们是相互关联的:

// 通过 Ref 修改:
countRef.value++; 
console.log(state.count); // 输出: 1 (state 也更新了)

// 通过源对象修改:
state.count = 10;
console.log(countRef.value); // 输出: 10 (Ref 也更新了)

1.1.3 provideinject

provide 和 inject 是 Vue 3 Composition API 中提供的一对功能,它们提供了一种强大的 跨组件层级传递数据 的机制,通常被称为 依赖注入 (Dependency Injection)。

它们解决了“Prop 逐级透传 (Prop Drilling)”的问题,即父组件需要将数据传递给一个非常深的孙子组件时,必须经过中间组件的层层传递。

provide/inject 主要用于解决 应用级的、跨层级的或基础服务的传递。

如何进行类型安全的 provide/inject

// 注入时提供默认值
const theme = inject('theme-mode', 'light');

// 结合 Symbol 和 TypeScript 确保 Key 的唯一性和类型安全
export const ConnectionManagerKey = Symbol('ConnectionManagerKey');

// 提供者
provide(ConnectionManagerKey, appManager);

// 注入者
const appManager = inject<ConnectionManager>(ConnectionManagerKey);

1.1.4 缩写指令

  • @ v-on 绑定事件
  • : v-bind 绑定属性
  • # v-slot 绑定插槽

1.1.5 app.config.globalProperties 如何使用

app.config.globalProperties 是 Vue 3 应用实例(app)上的一个配置选项,用于 在应用中全局注册属性。一旦注册,这些属性就可以在 任何组件实例(包括选项式 API 的 this 和 Composition API 的模板)中直接访问,而无需显式导入或注入。

这个机制主要用于注册应用级别的工具函数、常量、配置或服务实例。

// main.ts

import { createApp } from 'vue';
import App from './App.vue';

const app = createApp(App);

// 注册全局属性
app.config.globalProperties.$appName = 'MyApp V3';

// 注册一个全局方法
app.config.globalProperties.$formatDate = (date) => {
    return new Date(date).toLocaleDateString();
};

// 注册您之前设计的 ConnectionManager 实例
import { ConnectionManager } from './services/ConnectionManager';
const appManager = new ConnectionManager();
app.config.globalProperties.$appManager = appManager;

app.mount('#app');

如何使用

  1. 在选项式 API 中访问

在选项式 API 中,注册的属性会被混入到组件实例 this 中。

<template>
  <div>
    <h1>Welcome to {{ $appName }}</h1>
    <p>Current Date: {{ $formatDate(new Date()) }}</p>
    <button @click="reconnect">Reconnect</button>
  </div>
</template>

<script>
export default {
  // 选项式 API 访问
  methods: {
    reconnect() {
      // 通过 this 直接访问全局属性
      this.$appManager.reconnectManually();
      console.log(`Using manager from global properties: ${this.$appName}`);
    }
  }
}
</script>
  1. 在 Composition API 中访问

在 Composition API 的 <script setup> 中,你不能直接使用 this。但注册的属性仍然可以在模板中直接访问。

如果你需要在 <script setup> 的 JavaScript 逻辑中访问这些属性,你需要使用 getCurrentInstance()(但官方不鼓励过度依赖此方法,更推荐使用专门的 Composition API 工具)。

访问方法(使用 useRuntimeConfig 模拟):

由于 Vue 3 的设计理念是去 this 化,官方并没有直接推荐在 setup 逻辑中获取 globalProperties 的 Composition API 钩子。不过,在需要获取它们时,可以使用 getCurrentInstance

<script setup>
import { getCurrentInstance } from 'vue';

const instance = getCurrentInstance();
const globalProps = instance.appContext.config.globalProperties;

// 在 setup 逻辑中访问
const appManager = globalProps.$appManager;

const reconnect = () => {
  appManager.reconnectManually();
}

// 模板中仍可直接访问 $appName
</script>

<template>
  <div>
    <h1>Welcome to {{ $appName }}</h1> 
    <button @click="reconnect">Reconnect (from script setup)</button>
  </div>
</template>

注意

在 Vue 3 中,官方更推荐使用 provide/inject 或 Pinia 来传递依赖,而不是依赖 globalProperties,因为 globalProperties 会使组件的依赖关系变得隐式和难以追踪,尤其是在大型项目中。

1.1.5.1 监听对象中某个属性的变化

<template>
  <div>
    <div>{{obj.name}}</div>
    <div>{{obj.age}}</div>
    <button @click="changeName">改变值</button>
  </div>
</template>

<script>
import { reactive, watch } from 'vue';
export default {
  setup(){
    const obj = reactive({
      name:'zs',
      age:14
    });
    const changeName = () => {
      obj.name = 'ls';
    };
    watch(() => obj.name,() => {
      console.log('监听的obj.name改变了')
    })
    return {
      obj,
      changeName,
    }
  }
}
</script>

watchEffect 会监听引用数据类型的所有属性,不需要具体到某个属性,一旦运行就会立即监听,组件卸载的时候会停止监听

1.1.6

名称 作用
app.provide() 提供一个可以在所有descendant组件使用的值
<KeepAlive> 缓存组件实例
watchEffect() 立即运行一个函数,同时响应式地追踪其依赖,并在依赖更改时重新执行。

为了从 store 中提取属性时保持其响应性,你需要使用 storeToRefs()

toValue() 是 3.3 版本引入的新的 API, 和 unref 类似,但可以接受函数作为输入值

1.1.7 一个 Setup Context 主要包含什么

  • attrs
  • slots
  • emit
  • exposee

1.1.8 nextTick() 的作用是什么?

存在原因

Vue 当数据发生变化时,向 DOM 写入并不是变化时立即生效的,而是异步的。

主要是效率方面的考虑,使用这种机制可以将多次修改合并到一次 DOM 更新中,类似于显示原理中的帧缓存

nextTick() 的调用方式

  • 在 SFC 中直接调用
  • 在 Component 中通过 this.$nextTick() 调用

nextTick() 的参数及返回值

  • 如果入参传入一个 callback,则该 callback 会下一次变化真正生效时调用
  • 返回值是一个 Promise,且

可以配合 await nextTick() 进行调用

1.1.9 组合式API (Hooks / Composables)

无渲染组件或者轻量级组件?

为了实现带状态处理逻辑的复用

  • 一个 Composable 中可以嵌套其它的 Composable,以便组成更复杂的 Composable
  • 一个 Component / View/ Page 中可以引用多个 Composable,以组成更复杂的 Component / View/ Page
  • 官方的例子都是和 Lifecycle 事件关联的 onMounted / onUnmounted,相当于和引用方的触发点

参考

1.1.9.1 和 Mixin 的对比

1.1.9.2 和 无渲染组件的对比

1.1.9.3 和 React Hooks 的对比

1.1.10 国际化

1.1.11 ref() 和 reactive() 的区别有哪些

  • ref() 不仅可用于 object, 也可以用于 primitives 类型,如 boolean, int 等;reactive() 仅可用于 object
  • ref() 有一个 .value 属性,你必须使用 .value 属性获取内容(模板里可以自动 unwrap ),但是使用 reactive() 的话可以直接访问
  • 使用 ref() 函数可以替换整个对象实例,但是在使用 reactive() 函数时就不行

某种程度上, reactive() 可以看做是一个限制版的 ref()

参考

1.1.12 参考