Files
KintoneAppBuilder/frontend/vue3.0编程概要.md

30 KiB
Raw Blame History

Vue 3.0 引入了很多新的 API 和功能,同时也保留了 Vue 2.x 的很多特性。以下是一个关于 Vue 3.0 常用功能的编程标准指南纲要:

  1. 项目和组件结构

    • 项目目录和文件命名
    • 单文件组件的结构template, script, style
  2. 组件数据

    • 定义和使用 props
    • 定义和使用组件内部数据:refreactive
    • 使用 computed 属性
    • 使用 watchwatchEffect 监听数据变化
  3. 模板语法

    • 插值文本HTML
    • 指令v-model, v-if/v-else-if/v-else, v-show, v-for, v-bind, v-on
    • 过滤器和全局混入(虽然 Vue 3.0 推荐尽量避免使用,但在一些场景仍有用)
  4. 组件通信

    • 使用 props 和自定义事件实现父子组件通信
    • 使用 provide/inject 实现祖先和后代组件通信
    • 使用 event bus 进行任意组件间的通信Vue 3.0 不再内置,但可以自行实现)
    • 使用 Vue 3.0 的新功能 teleport 进行组件内容的传输
  5. Composition API

    • 使用 setup 函数
    • 使用 ref, reactive, computed, watch, watchEffect
    • 创建和使用 composable 函数
  6. 路由和状态管理

    • 使用 Vue Router定义和使用路由导航守卫路由元信息
    • 使用 Vuex状态定义GetterMutationAction模块化状态
  7. 与外部交互

    • 使用 axios 或者 fetch 进行 HTTP 请求
    • 使用 Vue 3.0 的新功能 Suspense 进行异步依赖处理
  8. 测试和部署

    • 单元测试:使用 Jest 编写和运行测试
    • E2E 测试:使用 Cypress 编写和运行测试
    • 部署:编译,打包,发布
  9. 项目和组件结构

    • 项目目录和文件命名 Vue 项目和组件的结构可以因项目需求和开发团队偏好而有所不同,但这里提供一种推荐的目录结构:
    /my-app
    ├── /node_modules
    ├── /public
    │   ├── index.html
    │   └── favicon.ico
    ├── /src
    │   ├── /assets       // 静态资源如图片、样式等
    │   ├── /components   // Vue 组件
    │   ├── /router       // Vue Router 配置
    │   ├── /store        // Vuex 状态管理配置
    │   ├── /views        // 页面级 Vue 组件
    │   ├── App.vue       // 根组件
    │   ├── main.ts       // 应用入口文件
    │   ├── shims-vue.d.ts
    │   └── ...
    ├── package.json
    ├── babel.config.js
    └── ...
    
    • 文件命名:对于组件文件,推荐使用 'PascalCase' 的方式命名。例如:MyComponent.vue。而对于其它的 .js 或 .ts 文件,推荐使用 'kebab-case' 的方式命名。例如:my-script.ts
  • 单文件组件的结构template, script, style 对于单文件组件(.vue 文件),其内部结构通常包含三部分:<template>, <script>, 和 <style><template> 中定义了组件的 HTML 结构,<script> 中定义了组件的逻辑,而 <style> 中定义了组件的样式。这三部分的顺序并不是固定的,但一般推荐将 <script> 放在 <template> 的下面,以便开发者能更快地看到组件的 HTML 结构。

    <template>
      <div>
        <!-- component HTML -->
      </div>
    </template>
    
    <script lang="ts">
    import { defineComponent } from 'vue'
    
    export default defineComponent({
      // component logic
    })
    </script>
    
    <style scoped>
    /* component styles */
    </style>
    
  1. 组件数据

在 Vue 3.0 中,组件数据可以通过两种方式进行管理:

  1. Props: Props 是父组件向子组件传递数据的一种方式。Props 可以是任何类型的数据,如字符串,数字,对象,数组,布尔值等。在子组件中,我们需要定义接受的 props。

  2. 组件内部数据: 使用 refreactive 可以创建组件的响应式数据。ref 用于创建响应式的基本类型数据(如字符串、数字等),reactive 用于创建响应式的复杂类型数据(如对象和数组)。

以下是 props 和响应式数据的用法示例:

<template>
  <div>
    <h1>{{ title }}</h1>
    <p>{{ message }}</p>
    <button @click="increment">Clicks: {{ count }}</button>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref } from 'vue'

export default defineComponent({
  name: 'MyComponent',
  props: {
    title: String,
  },
  setup(props) {
    const count = ref(0)
    const message = ref('Hello, Vue 3 and TypeScript!')

    function increment() {
      count.value++
    }

    return {
      count,
      message,
      increment
    }
  }
})
</script>

在这个示例中,title 是一个从父组件传递过来的 propcountmessage 是组件内部的响应式数据。我们在 setup 函数中定义了一个方法 increment,这个方法会使 count 的值增加。

注意:在 Vue 3.0 中,使用 ref 创建的响应式数据在模板中访问时不需要 .value,但在 setup 函数或其他 JavaScript 代码中访问时需要 .value。例如,在上述例子中,我们在模板中使用 {{ count }},但在 increment 函数中使用 count.value++

  1. 模板语法

Vue.js 使用基于 HTML 的模板语法,允许你声明式地将 DOM 和 Vue 实例的数据绑定在一起。在 Vue 3.0 中,所有 Vue 2.x 的模板语法都得到了支持,包括插值、指令等。

以下是一些常用的模板语法:

  • 插值: 使用双大括号({{ }})进行文本插值。
<template>
  <p>{{ message }}</p>
</template>
  • 指令: Vue 指令是带有 v- 前缀的特殊属性。指令属性的值预期是单一 JavaScript 表达式。例如 v-modelv-if/v-else-if/v-elsev-showv-forv-bindv-on 等。
<template>
  <input v-model="message">
  <p v-if="visible">Hello Vue!</p>
  <button v-on:click="handleClick">Click me</button>
  <div v-for="(item, index) in items" :key="index">
    {{ item }}
  </div>
</template>
  • 过滤器: Vue 2.x 支持过滤器,但在 Vue 3.0 中过滤器被移除了。如果你需要使用过滤器功能,你可以使用计算属性或者方法来替代。

注意:在 Vue 3.0 中,你应该尽量使用 setup 函数和 Composition API 来组织你的代码,尽管 datamethodscomputedwatch 等选项在 Vue 3.0 中仍然可用。

  • V3.0中不再支持或者不推荐的方法一览 好的,这是一些在 Vue 3.0 中不再支持或不再建议使用的功能和相应的替代方法:
Vue 2.x Vue 3.0 替代方法
Filters 使用计算属性或者方法
Vue.setVue.delete 直接使用 delete= 操作符来添加或删除响应式属性
Vue.observable 使用 reactiveref
Vue.prototype 自定义属性或方法 使用 provide/inject 机制来共享全局数据或方法
异步组件的 component 工厂函数 使用 defineAsyncComponent
Inline-template attribute 使用 JSX 或者 Vue 单文件组件SFC
$on, $off$once 方法 使用新的事件处理方法,或者使用第三方库
Functional components 使用普通的组件或者带有 setup 函数的组件

需要注意的是Vue 3.0 为我们提供了一种新的组件编写格式——Composition API它提供了更好的 TypeScript 类型推断支持,并允许我们在大型组件中更好地组织我们的代码。虽然 Vue 2.x 的 Options API 在 Vue 3.0 中仍然可用,但建议新的项目尽可能地使用 Composition API。

  • 以下是 Vue 2.0 和 Vue 3.0 在不同功能和写法上的对比:
  1. 声明响应式数据
  • Vue 2.0
<script>
export default {
  data() {
    return {
      message: 'Hello Vue 2!',
    }
  }
}
</script>
  • Vue 3.0
<script lang="ts">
import { ref } from 'vue'

export default {
  setup() {
    const message = ref('Hello Vue 3!')

    return {
      message
    }
  }
}
</script>
  1. 计算属性
  • Vue 2.0
<script>
export default {
  data() {
    return {
      count: 0,
    }
  },
  computed: {
    doubleCount() {
      return this.count * 2
    }
  }
}
</script>
  • Vue 3.0
<script lang="ts">
import { ref, computed } from 'vue'

export default {
  setup() {
    const count = ref(0)
    const doubleCount = computed(() => count.value * 2)

    return {
      count,
      doubleCount
    }
  }
}
</script>
  1. 侦听属性
  • Vue 2.0
<script>
export default {
  data() {
    return {
      count: 0,
    }
  },
  watch: {
    count(newVal, oldVal) {
      console.log(`count changed from ${oldVal} to ${newVal}`)
    }
  }
}
</script>
  • Vue 3.0
<script lang="ts">
import { ref, watch } from 'vue'

export default {
  setup() {
    const count = ref(0)

    watch(count, (newVal, oldVal) => {
      console.log(`count changed from ${oldVal} to ${newVal}`)
    })

    return {
      count
    }
  }
}
</script>
  1. 生命周期钩子
  • Vue 2.0
<script>
export default {
  mounted() {
    console.log('Component is mounted')
  }
}
</script>
  • Vue 3.0
<script lang="ts">
import { onMounted } from 'vue'

export default {
  setup() {
    onMounted(() => {
      console.log('Component is mounted')
    })
  }
}
</script>

以上例子说明了 Vue 3.0 中 Composition API 的基本使用方法。你会注意到Vue 3.0 提供的新的 API 允许我们在 setup 方法中更自由、更直观地组织代码,更容易进行类型推断,从而提高代码的可读性和可维护性。 4. 组件和指令

  1. 组件定义
  • Vue 3.0 中的组件定义方式如下:
<template>
  <div>
    {{ message }}
  </div>
</template>

<script lang="ts">
import { defineComponent, ref } from 'vue'

export default defineComponent({
  setup() {
    const message = ref('Hello Vue 3!')

    return {
      message
    }
  }
})
</script>
  1. 使用组件

在 Vue 3.0 中,组件的使用方式和 Vue 2.x 基本相同。可以在父组件中引入子组件,并通过标签形式进行使用。

  • Vue 3.0 中的使用方式如下:
<template>
  <ChildComponent />
</template>

<script lang="ts">
import { defineComponent } from 'vue'
import ChildComponent from './ChildComponent.vue'

export default defineComponent({
  components: {
    ChildComponent
  }
})
</script>
  1. 指令

Vue 3.0 中的指令和 Vue 2.x 在使用方式上也是相似的,包括内置指令如 v-model, v-if, v-for, v-on, v-bind 等,以及自定义指令。

以下是 Vue 3.0 中自定义指令的一个例子:

<template>
  <p v-focus>Focus me!</p>
</template>

<script lang="ts">
import { defineComponent, ref, onMounted } from 'vue'

export default defineComponent({
  directives: {
    focus: {
      mounted(el) {
        el.focus()
      }
    }
  }
})
</script>

在上述例子中,我们创建了一个自定义指令 v-focus,当元素被插入到 DOM 中时,这个指令会使元素自动获取焦点。

使用 props 和自定义事件实现父子组件通信

在 Vue.js 中props 和自定义事件是实现父子组件通信的主要方式。父组件通过 props 向子组件传递数据,子组件通过发射 (emit) 自定义事件向父组件传递数据或者通知父组件某个事件已经发生。

  • 以下是一个使用 props 和自定义事件实现父子组件通信的例子:
  1. 父组件 (ParentComponent.vue)
<template>
  <ChildComponent :propData="parentData" @childEvent="handleChildEvent" />
</template>

<script lang="ts">
import { defineComponent, ref } from 'vue'
import ChildComponent from './ChildComponent.vue'

export default defineComponent({
  components: {
    ChildComponent
  },
  setup() {
    const parentData = ref('Data from parent component')

    function handleChildEvent(payload) {
      console.log('Received event from child component with payload:', payload)
    }

    return {
      parentData,
      handleChildEvent
    }
  }
})
</script>

在上述父组件中,我们使用了 :propData="parentData" 来向子组件传递 prop以及使用了 @childEvent="handleChildEvent" 来监听子组件发射的 childEvent 事件。

  1. 子组件 (ChildComponent.vue)
<template>
  <button @click="emitEvent">Emit Event</button>
</template>

<script lang="ts">
import { defineComponent, PropType } from 'vue'

export default defineComponent({
  props: {
    propData: {
      type: String as PropType<string>,
      required: true
    }
  },
  setup(props, { emit }) {
    function emitEvent() {
      emit('childEvent', 'Data from child component')
    }

    return {
      emitEvent
    }
  }
})
</script>

在上述子组件中,我们定义了一个 prop propData 来接收父组件传递的数据,以及一个方法 emitEvent 来发射 childEvent 事件。

注意,在 TypeScript 中,我们需要使用 PropType 来显式地指定 prop 的类型。

使用 provide/inject 实现祖先和后代组件通信

在 Vue.js 中,provideinject 是实现祖先和后代组件之间通信的一种方式,主要用于避免 prop drilling也就是多层级的组件传递 prop。祖先组件使用 provide 选项来提供某些数据,后代组件使用 inject 选项来注入这些数据。

以下是一个使用 provideinject 实现祖先和后代组件通信的例子:

  1. 祖先组件 (AncestorComponent.vue)
<template>
  <DescendantComponent />
</template>

<script lang="ts">
import { defineComponent, ref } from 'vue'
import DescendantComponent from './DescendantComponent.vue'

export default defineComponent({
  components: {
    DescendantComponent
  },
  setup() {
    const ancestorData = ref('Data from ancestor component')

    provide('ancestorData', ancestorData)

    return {}
  }
})
</script>

在上述祖先组件中,我们使用了 provide 函数来提供一个名为 ancestorData 的数据。

  1. 后代组件 (DescendantComponent.vue)
<template>
  <p>{{ injectedData }}</p>
</template>

<script lang="ts">
import { defineComponent, inject, Ref } from 'vue'

export default defineComponent({
  setup() {
    const injectedData = inject<Ref<string>>('ancestorData')

    if (!injectedData) {
      throw new Error('ancestorData is not provided')
    }

    return {
      injectedData
    }
  }
})
</script>

在上述后代组件中,我们使用了 inject 函数来注入名为 ancestorData 的数据。请注意,在 TypeScript 中,我们需要使用类型断言 <Ref<string>> 来指定注入数据的类型。

这种方式特别适用于一些跨级别的状态共享,例如主题颜色、语言偏好等全局性的配置。

使用 event bus 进行任意组件间的通信Vue 3.0 不再内置,但可以自行实现)

Vue 3.0 不再内置 event bus但你可以使用一个简单的 EventEmitter 实现自己的 event bus。在这种情况下任何组件都可以通过 event bus 发送和接收事件,实现跨组件通信。

以下是一个使用 event bus 进行任意组件间通信的例子:

首先,我们需要创建一个 Event Bus

// EventBus.ts
import { EventEmitter } from 'events'

export const EventBus = new EventEmitter()

然后,组件可以通过 Event Bus 发送和接收事件:

  1. 发送事件的组件 (EmitterComponent.vue)
<template>
  <button @click="emitEvent">Emit Event</button>
</template>

<script lang="ts">
import { defineComponent } from 'vue'
import { EventBus } from './EventBus.ts'

export default defineComponent({
  setup() {
    function emitEvent() {
      EventBus.emit('customEvent', 'Data from EmitterComponent')
    }

    return {
      emitEvent
    }
  }
})
</script>

在上述组件中,我们定义了一个方法 emitEvent,用于在 customEvent 事件上发送数据。

  1. 接收事件的组件 (ReceiverComponent.vue)
<template>
  <p>{{ receivedData }}</p>
</template>

<script lang="ts">
import { defineComponent, ref, onMounted, onBeforeUnmount } from 'vue'
import { EventBus } from './EventBus.ts'

export default defineComponent({
  setup() {
    const receivedData = ref('')

    function handleCustomEvent(payload: string) {
      receivedData.value = payload
    }

    onMounted(() => {
      EventBus.on('customEvent', handleCustomEvent)
    })

    onBeforeUnmount(() => {
      EventBus.off('customEvent', handleCustomEvent)
    })

    return {
      receivedData
    }
  }
})
</script>

在上述组件中,我们使用了 EventBus.on 来监听 customEvent 事件,并在事件处理器 handleCustomEvent 中更新 receivedData。同时,我们也使用了 EventBus.off 来在组件卸载前移除事件监听器,以避免内存泄漏。

请注意,使用 event bus 进行跨组件通信应该谨慎使用,因为它使得数据流难以追踪,增加了代码的复杂性。在大型项目中,你可能会更倾向于使用专门的状态管理库,如 Vuex。

使用 Vue 3.0 的新功能 teleport 进行组件内容的传输

在 Vue 3.0 中,新增了 teleport 功能,也被称为 "传送门"。teleport 能将子组件渲染到 DOM 中的任意位置,而不仅仅是局限于父组件的范围内。它特别适用于需要“破坏性”的 UI 特性,例如全屏覆盖的模态框、通知、对话框等。

以下是一个使用 teleport 进行组件内容的传输的例子:

  1. 在 HTML 文件中,添加一个目标元素:
<body>
  ...
  <div id="modal-root"></div>
</body>
  1. 创建一个使用 teleport 的组件 (ModalComponent.vue)
<template>
  <teleport to="#modal-root">
    <div class="modal">
      <h1>This is a modal</h1>
      <slot></slot>
      <button @click="closeModal">Close Modal</button>
    </div>
  </teleport>
</template>

<script lang="ts">
import { defineComponent, toRef, ref } from 'vue'

export default defineComponent({
  setup(props, { emit }) {
    const showModal = ref(false)

    function closeModal() {
      showModal.value = false
      emit('update:modelValue', showModal.value)
    }

    return {
      showModal,
      closeModal
    }
  },
  props: {
    modelValue: {
      type: Boolean,
      required: true
    }
  },
  emits: ['update:modelValue']
})
</script>

<style scoped>
.modal {
  /* styles for your modal */
}
</style>

在上述组件中,我们定义了一个名为 modal-root 的目标元素,这就是我们要将模态框渲染的地方。注意,teleportto 属性用于指定目标元素。

然后,在其他任何需要使用这个模态框的组件中,只需如下使用:

<template>
  <ModalComponent v-model="showModal">
    <p>This is some content inside the modal</p>
  </ModalComponent>
</template>

<script lang="ts">
import { defineComponent, ref } from 'vue'
import ModalComponent from './ModalComponent.vue'

export default defineComponent({
  components: {
    ModalComponent
  },
  setup() {
    const showModal = ref(false)
    return {
      showModal
    }
  }
})
</script>

在使用 teleport 的过程中,父组件和被 teleport 的子组件之间的通信仍然保持原样。也就是说,你可以像平常一样使用 props、事件、插槽等。

5. Vue 3.0 中的 Composition API

Vue 3.0 引入了一种新的组件编写方式,名为 Composition API。它是一个基于函数的 API让你能更灵活地组织和重用逻辑代码。对比于 Vue 2 中的 Options APIComposition API 更容易处理复杂的组件和逻辑,使代码组织更加清晰。

以下是一个基本的 Composition API 组件示例:

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

<script lang="ts">
import { defineComponent, ref } from 'vue'

export default defineComponent({
  setup() {
    const count = ref(0)
    function increment() {
      count.value++
    }

    return {
      count,
      increment
    }
  }
})
</script>

在上述示例中,我们在 setup 函数中定义了 count 数据和 increment 方法。请注意,ref 是一个函数,用于创建响应式的数据。在模板中,我们可以像平常一样使用 countincrement

Composition API 还提供了一系列其它的函数,如 reactivecomputedwatchonMountedonUnmounted 等,可以帮助我们更好地编写组件逻辑。

值得注意的是,使用 Composition API 不意味着必须放弃 Options API。实际上两者可以在同一个组件中并用。你可以根据项目的具体需求和团队的编程习惯选择最适合你的 API 风格。

  • 以下是 Vue 3.0 中的 Composition API 函数的基本说明,以及与 Options API 的对比
Composition API 说明 对应的 Options API
setup setup 是一个新引入的组件选项,用于使用 Composition API。它是组件内部使用 Composition API 的入口。
ref ref 函数用于创建一个响应式的数据。它接收一个参数,返回一个响应式的 Ref 对象。 data
reactive reactive 函数用于创建一个响应式的对象。它接收一个普通对象,返回一个响应式的对象。 data
computed computed 函数用于创建一个计算属性。它接收一个 getter 函数或者一个具有 getter 和 setter 的对象,返回一个响应式的 Ref 对象。 computed
watch watch 函数用于响应式地跟踪和触发副作用。它接收一个响应式的源和一个执行副作用的回调函数。 watch
watchEffect watchEffect 函数用于立即执行传入的一个函数,并响应式地追踪其依赖,并在其依赖变更时重新运行该函数。
onMounted onMounted 函数在组件被挂载时调用。它接收一个在组件挂载后执行的回调函数。 mounted
onUnmounted onUnmounted 函数在组件被卸载时调用。它接收一个在组件卸载后执行的回调函数。 beforeDestroy/unmounted
onUpdated onUpdated 在组件更新后调用。它接收一个在组件更新后执行的回调函数。 updated
provide provide 函数用于在组件上定义一个可以被后代组件注入的值。它接收一个提供的键和值。 有,与 provide/inject 相似但是属性而不是函数
inject inject 函数用于在组件中注入一个由祖先组件提供的值。它接收一个注入的键。 有,但与 provide/inject 相似但是属性而不是函数

值得注意的是,虽然一些 Composition API 函数与 Options API 的某些选项有相似之处,但它们的工作方式和使用方式可能有所不同。在实际使用中,你需要根据具体的使用场景和需求选择合适的 API。

  • ** 使用 ref, reactive, computed, watch, watchEffect**

这些函数是 Vue 3.0 Composition API 的一部分,使得我们可以更加灵活和高效地编写组件逻辑。

以下是这些函数的使用示例:

<template>
  <div>
    <p>{{ count }}</p>
    <p>{{ double }}</p>
    <button @click="increment">Increment</button>
    <button @click="decrement">Decrement</button>
  </div>
</template>

<script lang="ts">
import { ref, reactive, computed, watch, watchEffect } from 'vue'

export default {
  setup() {
    // 使用 ref 创建响应式数据
    const count = ref(0)

    // 使用 reactive 创建响应式对象
    const state = reactive({
      message: 'Hello Vue 3!'
    })

    // 使用 computed 创建计算属性
    const double = computed(() => count.value * 2)

    // 定义方法
    const increment = () => {
      count.value++
      state.message = `Count is ${count.value}`
    }

    const decrement = () => {
      count.value--
      state.message = `Count is ${count.value}`
    }

    // 使用 watch 监听 count 的变化
    watch(count, (newValue, oldValue) => {
      console.log(`Count changed from ${oldValue} to ${newValue}`)
    })

    // 使用 watchEffect 监听响应式依赖项的变化
    watchEffect(() => {
      console.log(`State message is "${state.message}"`)
    })

    return {
      count,
      state,
      double,
      increment,
      decrement
    }
  }
}
</script>

在上述示例中,我们使用 refreactive 创建了响应式数据,使用 computed 创建了计算属性,使用 watchwatchEffect 监听了数据的变化。并且在数据变化时更新了相关数据,并打印了日志信息。

  • ** 创建和使用 composable 函数**

在 Vue 3.0 的 Composition API 中,我们可以将复用的逻辑提取出来,创建一个 composable 函数。composable 函数就是一个普通的 JavaScript 函数,这个函数内部使用了 Composition API 的功能,并返回一些响应式数据或函数。

以下是一个创建和使用 composable 函数的示例:

首先,我们创建一个名为 useCounter 的 composable 函数,这个函数提供了计数器的功能:

import { ref } from 'vue'

export function useCounter() {
  const count = ref(0)

  const increment = () => {
    count.value++
  }

  const decrement = () => {
    count.value--
  }

  return {
    count,
    increment,
    decrement
  }
}

然后,在组件中使用 useCounter 函数:

<template>
  <div>
    <p>{{ count }}</p>
    <button @click="increment">Increment</button>
    <button @click="decrement">Decrement</button>
  </div>
</template>

<script lang="ts">
import { useCounter } from './composables/useCounter'

export default {
  setup() {
    const { count, increment, decrement } = useCounter()

    return {
      count,
      increment,
      decrement
    }
  }
}
</script>

在上述示例中,我们首先创建了一个 useCounter composable 函数,这个函数提供了计数器的功能。然后,在组件中使用了 useCounter 函数,从而复用了计数器的功能。通过这种方式,我们可以将复杂的逻辑提取出来,创建 composable 函数,然后在多个组件中复用这些函数。

  1. 与外部交互

与外部系统(如服务器端 API、本地存储或其他 JavaScript 库)交互是常见的需求。下面介绍一些 Vue 3 和 TypeScript 中的常用方法。

使用 Axios 进行 HTTP 请求 在处理与外部交互时,我们通常会遇到异步操作。以下是使用 Vue 3 和 TypeScript 进行异步处理的例子。

使用 Axios 进行 HTTP 请求

首先,我们可以使用 async/await 语法改写 Axios 的 HTTP 请求:

import axios from 'axios'
import { ref, onMounted } from 'vue'

export default {
  setup() {
    const data = ref(null)

    onMounted(async () => {
      const response = await axios.get('https://api.example.com/data')
      data.value = response.data
    })

    return {
      data
    }
  }
}

在这个例子中,我们使用 async/await 语法来处理异步的 HTTP 请求。注意,因为 await 只能在 async 函数中使用,所以我们需要在 onMounted 钩子中使用 async

使用 LocalStorage 进行本地存储

虽然 LocalStorage 的操作是同步的,但如果你需要模拟异步操作,可以使用 Promise

import { ref, onMounted } from 'vue'

export default {
  setup() {
    const count = ref(0)

    onMounted(async () => {
      const savedCount = await new Promise(resolve => {
        setTimeout(() => {
          resolve(localStorage.getItem('count'))
        }, 1000)
      })

      count.value = savedCount || 0
    })

    return {
      count
    }
  }
}

在这个例子中,我们使用 setTimeout 模拟异步操作,并使用 Promise 来处理这个异步操作。在异步操作完成后,我们更新 count 的值。

使用 LocalStorage 进行本地存储

在浏览器中,我们可以使用 LocalStorage 对象进行本地存储。以下是一个简单的例子:

import { ref, watch } from 'vue'

export default {
  setup() {
    const count = ref(localStorage.getItem('count') || 0)

    watch(count, newValue => {
      localStorage.setItem('count', newValue)
    })

    return {
      count
    }
  }
}

在这个例子中,我们首先从 LocalStorage 中获取 count 的值。然后,我们使用 watch() 函数监听 count 的变化,并在 count 变化时更新 LocalStorage 中的值。

使用 Vue 3 提供的生命周期钩子

Vue 3 提供了一些生命周期钩子,如 onMounted()onUpdated()onUnmounted(),可以在组件的生命周期的不同阶段进行特定的操作。以下是一个简单的例子:

import { onMounted, ref } from 'vue'

export default {
  setup() {
    const data = ref(null)

    onMounted(() => {
      data.value = 'Hello, Vue 3'
    })

    return {
      data
    }
  }
}

在这个例子中,我们在组件挂载后更新 data 的值。

7. 使用 Vue 3.0 的新功能 Suspense 进行异步依赖处理

Vue 3.0 引入了一个新的内置组件 Suspense,它可以帮助我们处理组件中的异步依赖。

当一个组件的 setup 函数返回一个 Promise 时(这通常意味着该组件有异步操作),我们可以用 Suspense 组件包裹这个组件。Suspense 组件有两个插槽:default 插槽和 fallback 插槽。当异步组件处于等待状态时,fallback 插槽的内容会被渲染;当异步组件的 Promise 解析完成时,default 插槽的内容会被渲染。

下面是一个使用 Suspense 处理异步组件的例子:

<template>
  <Suspense>
    <template #default>
      <AsyncComponent />
    </template>
    <template #fallback>
      <div>Loading...</div>
    </template>
  </Suspense>
</template>

<script lang="ts">
import { defineComponent, ref, onMounted } from 'vue'
import axios from 'axios'

const AsyncComponent = defineComponent({
  async setup() {
    const data = ref(null)

    // 这里模拟一个异步操作
    await new Promise(resolve => setTimeout(resolve, 2000))

    const response = await axios.get('https://api.example.com/data')
    data.value = response.data

    return {
      data
    }
  },

  template: `
    <div>
      <p>Data: {{ data }}</p>
    </div>
  `,
})

export default {
  components: {
    AsyncComponent
  }
}
</script>

在这个例子中,我们创建了一个异步组件 AsyncComponent,它的 setup 函数返回一个 Promise。我们使用 Suspense 组件包裹 AsyncComponent,并提供了 fallback 插槽,当 AsyncComponent 的数据正在加载时,fallback 插槽的内容会被渲染。