vue 强制刷新_vue3(vue-next)study

本文深入探讨Vue 3的响应式原理,包括Proxy对象、响应式对象、侦听器、计算属性和响应式计算,以及如何在setup函数中使用Composition API。文章还介绍了响应式状态的创建、监听变化、模板引用和模板更新机制,帮助开发者理解Vue 3中如何实现高效的数据绑定和更新。
摘要由CSDN通过智能技术生成

Docs

Vue (读音 /vjuː/,类似于view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。


Advanced Guides

Reactivity
  • 深入响应性原理
  1. 什么是响应性?
  • 响应性是一种允许我们以声明式的方式去适应变化的一种编程范例。

2. vue如何追踪变化?

  • 当把一个普通的 JavaScript 对象作为data选项传给应用或组件实例的时候,Vue 会使用带有 getter 和 setter 的处理程序遍历其所有 property 并将其转换为Proxy。这是 ES6 仅有的特性,但是我们在 Vue 3 版本也使用了Object.defineProperty来支持 IE 浏览器。
  • proxy对象

3. 侦听器

  • 每个组件实例都有一个相应的侦听器实例,该实例将在组件渲染期间把“触碰”的所有 property 记录为依赖项。之后,当触发依赖项的 setter 时,它会通知侦听器,从而使得组件重新渲染。
  • 响应式原理
声明响应式状态
import {
     reactive } from 'vue'

// 响应式状态
const state = reactive({
    
  count: 0
})

reactive相当于 Vue 2.x 中的Vue.observable()API,该 API 返回一个响应式的对象状态。该响应式转换是“深度转换”——它会影响嵌套对象传递的所有 property。

Vue 中响应式状态的基本用例是我们可以在渲染期间使用它。因为依赖跟踪的关系,当响应式状态改变时视图会自动更新。

这就是 Vue 响应式系统的本质。当从组件中的data()返回一个对象时,它在内部交由reactive()使其成为响应式对象。模板会被编译成能够使用这些响应式 property 的渲染函数。

创建独立的响应式值作为 refs

ref会返回一个可变的响应式对象,该对象作为它的内部值——一个响应式的引用,这就是名称的来源。此对象只包含一个名为value的 property:

import {
     ref } from 'vue'

const count = ref(0)
console.log(count.value) // 0

count.value++
console.log(count.value) // 1

Ref 展开:

当 ref 作为渲染上下文 (从setup ()中返回的对象) 上的 property 返回并可以在模板中被访问时,它将自动展开为内部值。不需要在模板中追加.value

访问响应式对象:

  1. ref 作为响应式对象的 property 被访问或更改时,为使其行为类似于普通 property,它会自动展开内部值:
  2. 如果将新的 ref 赋值给现有 ref 的 property,将会替换旧的 ref:
  3. Ref 展开仅发生在被响应式Object嵌套的时候。当从Array或原生集合类型如Map访问 ref 时,不会进行展开:
响应式状态解构

当我们想使用大型响应式对象的一些 property 时,可能很想使用ES6 解构来获取我们想要的 property:

import {
     reactive } from 'vue'

const book = reactive({
    
  author: 'Vue Team',
  year: '2020',
  title: 'Vue 3 Guide',
  description: 'You are reading this book right now ;)',
  price: 'free'
})

let {
     author, title } = book

遗憾的是,使用解构的两个 property 的响应式都会丢失。对于这种情况,我们需要将我们的响应式对象转换为一组 ref。这些 ref 将保留与源对象的响应式关联:

import {
     reactive, toRefs } from 'vue'

const book = reactive({
    
  author: 'Vue Team',
  year: '2020',
  title: 'Vue 3 Guide',
  description: 'You are reading this book right now ;)',
  price: 'free'
})

let {
     author, title } = toRefs(book)

title.value = 'Vue 3 Detailed Guide' // 我们需要使用 .value 作为标题,现在是 ref
console.log(book.title) // 'Vue 3 Detailed Guide'
使用 readonly 防止更改响应式对象

有时我们想跟踪响应式对象 (refreactive) 的变化,但我们也希望防止在应用程序的某个位置更改它。例如,当我们有一个被provide的响应式对象时,我们不想让它在注入的时候被改变。为此,我们可以基于原始对象创建一个只读的 Proxy 对象:

import {
     reactive, readonly } from 'vue'

const original = reactive({
     count: 0 })

const copy = readonly(original)

// 在copy上转换original 会触发侦听器依赖

original.count++

// 转换copy 将导失败并导致警告
copy.count++ // 警告: "Set operation on key 'count' failed: target is readonly."
  • 响应式计算和侦听
计算值

有时我们需要依赖于其他状态的状态——在 Vue 中,这是用组件计算属性处理的,以直接创建计算值,我们可以使用computed方法:它接受 getter 函数并为 getter 返回的值返回一个不可变的响应式ref对象。

const count = ref(1)
const plusOne = computed(() => count.value++)

console.log(plusOne.value) // 2

plusOne.value++ // error

或者,它可以使用一个带有getset函数的对象来创建一个可写的 ref 对象。

const count = ref(1)
const plusOne = computed({
    
  get: () => count.value + 1,
  set: val => {
    
    count.value = val - 1
  }
})

plusOne.value = 1
console.log(count.value) // 0
watchEffect

为了根据反应状态自动应用重新应用副作用,我们可以使用watchEffect方法。它立即执行传入的一个函数,同时响应式追踪其依赖,并在其依赖变更时重新运行该函数。

const count = ref(0)

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

setTimeout(() => {
    
  count.value++
  // -> logs 1
}, 100)

1.停止侦听

watchEffect在组件的setup()函数或生命周期钩子被调用时,侦听器会被链接到该组件的生命周期,并在组件卸载时自动停止。

在一些情况下,也可以显式调用返回值以停止侦听:

const stop = watchEffect(() => {
    
  /* ... */
})

// later
stop()

2.清除副作用

有时副作用函数会执行一些异步的副作用,这些响应需要在其失效时清除 (即完成之前状态已改变了) 。所以侦听副作用传入的函数可以接收一个 onInvalidate 函数作入参,用来注册清理失效时的回调。当以下情况发生时,这个失效回调会被触发:

  • 副作用即将重新执行时
  • 侦听器被停止 (如果在 setup() 或生命周期钩子函数中使用了 watchEffect,则在组件卸载时)
watchEffect(onInvalidate => {
    
  const token = performAsyncOperation(id.value)
  onInvalidate(() => {
    
    // id has changed or watcher is stopped.
    // invalidate previously pending async operation
    token.cancel()
  })
})

我们之所以是通过传入一个函数去注册失效回调,而不是从回调返回它,是因为返回值对于异步错误处理很重要。

在执行数据请求时,副作用函数往往是一个异步函数:

const data = ref(null)
watchEffect(async onInvalidate => {
    
  onInvalidate(() => {...}) // 我们在Promise解析之前注册清除函数
  data.value = await fetchData(props.id)
})

我们知道异步函数都会隐式地返回一个 Promise,但是清理函数必须要在 Promise 被 resolve 之前被注册。另外,Vue 依赖这个返回的 Promise 来自动处理 Promise 链上的潜在错误。

3.副作用刷新时机

Vue 的响应式系统会缓存副作用函数,并异步地刷新它们,这样可以避免同一个“tick” 中多个状态改变导致的不必要的重复调用。在核心的具体实现中,组件的update函数也是一个被侦听的副作用。当一个用户定义的副作用函数进入队列时,默认情况下,会在所有的组件update执行:

<template>
  <div>{
    {
     count }}</div>
</template>

<script>
  export default {
    
    setup() {
    
      const count = ref(0)

      watchEffect(() => {
    
        console.log(count.value)
      })

      return {
    
        count
      }
    }
  }
</script>

在这个例子中:

  • count 会在初始运行时同步打印出来
  • 更改 count 时,将在组件更新前执行副作用。

如果需要在组件更新重新运行侦听器副作用,我们可以传递带有flush选项的附加options对象 (默认为'pre'):

// fire before component updates
watchEffect(
  () => {
    
    /* ... */
  },
  {
    
    flush: 'post'
  }
)

flush 选项还接受 sync,这将强制效果始终同步触发。然而,这是低效的,应该很少需要。

4.侦听器调试

onTrackonTrigger选项可用于调试侦听器的行为。

  • 当响应式 property 或 ref 作为依赖项被追踪时,将调用 onTrack
  • 当依赖项变更导致副作用被触发时,将调用 onTrigger

这两个回调都将接收到一个包含有关所依赖项信息的调试器事件。建议在以下回调中编写debugger语句来检查依赖关系:

watchEffect(
  () => {
    
    /* 副作用 */
  },
  {
    
    onTrigger(e) {
    
      debugger
    }
  }
)

onTrackonTrigger 只能在开发模式下工作。

watch

watchAPI 完全等同于组件侦听器property。watch需要侦听特定的数据源,并在回调函数中执行副作用。默认情况下,它也是惰性的,即只有当被侦听的源发生变化时才执行回调。

  • 与watchEffect比较,watch允许我们:
    • 懒执行副作用;
    • 更具体地说明什么状态应该触发侦听器重新运行;
    • 访问侦听状态变化前后的值。

1.侦听单个数据源

侦听器数据源可以是返回值的 getter 函数,也可以直接是ref

// 侦听一个 getter
const state = reactive({
     count: 0 })
watch(
  () => state.count,
  (count, prevCount) => {
    
    /* ... */
  }
)

// 直接侦听ref
const count = ref(0)
watch(count, (count, prevCount) => {
    
  /* ... */
})

2.侦听多个数据源

侦听器还可以使用数组同时侦听多个源:

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

3.与 watchEffect 共享的行为

watchwatchEffect共享停止侦听,清除副作用(相应地onInvalidate会作为回调的第三个参数传入)、副作用刷新时机和侦听器调试行为。

Composition API

介绍

用组件的选项 (datacomputedmethodswatch) 组织逻辑在大多数情况下都有效。然而,当我们的组件变得更大时,逻辑关注点的列表也会增长。这可能会导致组件难以阅读和理解,尤其是对于那些一开始就没有编写这些组件的人来说。

19c60a715a39f75f49d90ff812b814d1.png

一个大型组件的示例,其中逻辑关注点是按颜色分组。

这种碎片化使得理解和维护复杂组件变得困难。选项的分离掩盖了潜在的逻辑问题。此外,在处理单个逻辑关注点时,我们必须不断地“跳转”相关代码的选项块。

如果我们能够将与同一个逻辑关注点相关的代码配置在一起会更好。而这正是 Composition API 使我们能够做到的。

Composition API 基础

既然我们知道了为什么,我们就可以知道怎么做。为了开始使用 Composition api,我们首先需要一个可以实际使用它的地方。在 Vue 组件中,我们将此位置称为 setup

setup 组件选项

新的setup组件选项在创建组件之前执行,一旦props被解析,并充当合成 API 的入口点。
由于在执行setup时尚未创建组件实例,因此在setup选项中没有this。这意味着,除了props之外,你将无法访问组件中声明的任何属性——本地状态计算属性方法

setup选项应该是一个接受propscontext的函数,我们将在稍后讨论。此外,我们从setup返回的所有内容都将暴露给组件的其余部分 (计算属性、方法、生命周期钩子等等) 以及组件的模板。

// src/components/UserRepositories.vue

export default {
    
  components: {
     RepositoriesFilters, RepositoriesSortBy, RepositoriesList },
  props: {
    
    user: {
     type: String }
  },
  setup(props) {
    
    console.log(props) // { user: '' }

    return {} // 这里返回的任何内容都可以用于组件的其余部分
  }
  // 组件的“其余部分”
}

现在让我们从提取第一个逻辑关注点开始 (在原始代码段中标记为“1”)。

1.从假定的外部 API 获取该用户名的仓库,并在用户更改时刷新它

我们将从最明显的部分开始:

  • 仓库列表
  • 更新仓库列表的函数
  • 返回列表和函数,以便其他组件选项可以访问它们
// src/components/UserRepositories.vue `setup` function
import {
     fetchUserRepositories } from '@/api/repositories'

// 在我们的组件内
setup (props) {
    
  let repositories = []
  const getUserRepositories = async () => {
    
    repositories = await fetchUserRepositories(props.user)
  }

  return {
    
    repositories,
    getUserRepositories // 返回的函数与方法的行为相同
  }
}

这是我们的出发点,但它还不能工作,因为我们的 repositories 变量不是被动的。这意味着从用户的角度来看,仓库列表将保持为空。我们来解决这个问题!

带 ref 的响应式变量
在 Vue 3.0 中,我们可以通过一个新的ref函数使任何响应式变量在任何地方起作用,如下所示:

import {
     ref } from 'vue'

const counter = ref(0)

ref接受参数并返回它包装在具有valueproperty 的对象中,然后可以使用该 property 访问或更改响应式变量的值:

import {
     ref } from 'vue'

const counter = ref(0)

console.log(counter) // { value: 0 }
console.log(counter.value) // 0

counter.value++
console.log(counter.value) // 1

在对象中包装值似乎不必要,但在 JavaScript 中保持不同数据类型的行为统一是必需的。这是因为在 JavaScript 中,NumberString 等基本类型是通过值传递的,而不是通过引用传递的:

Vue-i18n-next是Vue3版本的国际化插件,用于支持多语言的开发。你可以通过以下步骤来使用它: 第一步是下载Vue-i18n-next插件。你可以使用npm或yarn来进行下载。使用npm的命令是:npm install vue-i18n@next,而使用yarn的命令是:yarn add vue-i18n@next。 第二步是安装@intlify/vue-i18n-loader插件,这个插件用于自定义块的Vue-i18n加载程序,可以用于i18n资源的预编译。你可以使用npm或yarn来进行安装。使用npm的命令是:npm i --save-dev @intlify/vue-i18n-loader@next,而使用yarn的命令是:yarn add -D @intlify/vue-i18n-loader@next。 第三步是创建语言文件夹并在其中创建js或ts文件。在你的项目的src目录下创建一个名为language的文件夹,并在其中创建你的语言文件,可以使用js或ts格式。 通过以上步骤,你就可以开始在Vue3项目中使用Vue-i18n-next进行国际化开发了。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [vue3多语言插件 vue-i18n使用](https://blog.csdn.net/qq_42859450/article/details/126103422)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [vue-i18n-loader:用于自定义块的vue-i18n-loader](https://download.csdn.net/download/weixin_42131628/15097221)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [vue3中多语言切换vue-i18n@next](https://blog.csdn.net/m0_59818968/article/details/119381340)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值