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

1076 lines
30 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

Vue 3.0 引入了很多新的 API 和功能,同时也保留了 Vue 2.x 的很多特性。以下是一个关于 Vue 3.0 常用功能的编程标准指南纲要:
1. **项目和组件结构**
- 项目目录和文件命名
- 单文件组件的结构template, script, style
2. **组件数据**
- 定义和使用 props
- 定义和使用组件内部数据:`ref``reactive`
- 使用 `computed` 属性
- 使用 `watch``watchEffect` 监听数据变化
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 编写和运行测试
- 部署:编译,打包,发布
1. **项目和组件结构**
- 项目目录和文件命名
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 结构。
```vue
<template>
<div>
<!-- component HTML -->
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
// component logic
})
</script>
<style scoped>
/* component styles */
</style>
```
2. **组件数据**
在 Vue 3.0 中,组件数据可以通过两种方式进行管理:
1. **Props**: Props 是父组件向子组件传递数据的一种方式。Props 可以是任何类型的数据,如字符串,数字,对象,数组,布尔值等。在子组件中,我们需要定义接受的 props。
2. **组件内部数据**: 使用 `ref` 和 `reactive` 可以创建组件的响应式数据。`ref` 用于创建响应式的基本类型数据(如字符串、数字等),`reactive` 用于创建响应式的复杂类型数据(如对象和数组)。
以下是 props 和响应式数据的用法示例:
```vue
<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` 是一个从父组件传递过来的 prop`count` 和 `message` 是组件内部的响应式数据。我们在 `setup` 函数中定义了一个方法 `increment`,这个方法会使 `count` 的值增加。
注意:在 Vue 3.0 中,使用 `ref` 创建的响应式数据在模板中访问时不需要 `.value`,但在 `setup` 函数或其他 JavaScript 代码中访问时需要 `.value`。例如,在上述例子中,我们在模板中使用 `{{ count }}`,但在 `increment` 函数中使用 `count.value++`。
3. **模板语法**
Vue.js 使用基于 HTML 的模板语法,允许你声明式地将 DOM 和 Vue 实例的数据绑定在一起。在 Vue 3.0 中,所有 Vue 2.x 的模板语法都得到了支持,包括插值、指令等。
以下是一些常用的模板语法:
- **插值**: 使用双大括号(`{{ }}`)进行文本插值。
```vue
<template>
<p>{{ message }}</p>
</template>
```
- **指令**: Vue 指令是带有 `v-` 前缀的特殊属性。指令属性的值预期是单一 JavaScript 表达式。例如 `v-model`、`v-if`/`v-else-if`/`v-else`、`v-show`、`v-for`、`v-bind`、`v-on` 等。
```vue
<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 来组织你的代码,尽管 `data`、`methods`、`computed` 和 `watch` 等选项在 Vue 3.0 中仍然可用。
- **V3.0中不再支持或者不推荐的方法一览**
好的,这是一些在 Vue 3.0 中不再支持或不再建议使用的功能和相应的替代方法:
| Vue 2.x | Vue 3.0 替代方法 |
|----------------------------------|-----------------|
| Filters | 使用计算属性或者方法 |
| `Vue.set` 和 `Vue.delete` | 直接使用 `delete` 或 `=` 操作符来添加或删除响应式属性 |
| `Vue.observable` | 使用 `reactive` 或 `ref` |
| `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
```vue
<script>
export default {
data() {
return {
message: 'Hello Vue 2!',
}
}
}
</script>
```
- Vue 3.0
```vue
<script lang="ts">
import { ref } from 'vue'
export default {
setup() {
const message = ref('Hello Vue 3!')
return {
message
}
}
}
</script>
```
2. **计算属性**
- Vue 2.0
```vue
<script>
export default {
data() {
return {
count: 0,
}
},
computed: {
doubleCount() {
return this.count * 2
}
}
}
</script>
```
- Vue 3.0
```vue
<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>
```
3. **侦听属性**
- Vue 2.0
```vue
<script>
export default {
data() {
return {
count: 0,
}
},
watch: {
count(newVal, oldVal) {
console.log(`count changed from ${oldVal} to ${newVal}`)
}
}
}
</script>
```
- Vue 3.0
```vue
<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>
```
4. **生命周期钩子**
- Vue 2.0
```vue
<script>
export default {
mounted() {
console.log('Component is mounted')
}
}
</script>
```
- Vue 3.0
```vue
<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 中的组件定义方式如下:
```vue
<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>
```
2. **使用组件**
在 Vue 3.0 中,组件的使用方式和 Vue 2.x 基本相同。可以在父组件中引入子组件,并通过标签形式进行使用。
- Vue 3.0 中的使用方式如下:
```vue
<template>
<ChildComponent />
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import ChildComponent from './ChildComponent.vue'
export default defineComponent({
components: {
ChildComponent
}
})
</script>
```
3. **指令**
Vue 3.0 中的指令和 Vue 2.x 在使用方式上也是相似的,包括内置指令如 `v-model`, `v-if`, `v-for`, `v-on`, `v-bind` 等,以及自定义指令。
以下是 Vue 3.0 中自定义指令的一个例子:
```vue
<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)**
```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` 事件。
2. **子组件 (ChildComponent.vue)**
```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 中,`provide` 和 `inject` 是实现祖先和后代组件之间通信的一种方式,主要用于避免 prop drilling也就是多层级的组件传递 prop。祖先组件使用 `provide` 选项来提供某些数据,后代组件使用 `inject` 选项来注入这些数据。
以下是一个使用 `provide` 和 `inject` 实现祖先和后代组件通信的例子:
1. **祖先组件 (AncestorComponent.vue)**
```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` 的数据。
2. **后代组件 (DescendantComponent.vue)**
```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
```ts
// EventBus.ts
import { EventEmitter } from 'events'
export const EventBus = new EventEmitter()
```
然后,组件可以通过 Event Bus 发送和接收事件:
1. **发送事件的组件 (EmitterComponent.vue)**
```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` 事件上发送数据。
2. **接收事件的组件 (ReceiverComponent.vue)**
```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 文件中,添加一个目标元素:
```html
<body>
...
<div id="modal-root"></div>
</body>
```
2. 创建一个使用 `teleport` 的组件 (ModalComponent.vue)
```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` 的目标元素,这就是我们要将模态框渲染的地方。注意,`teleport` 的 `to` 属性用于指定目标元素。
然后,在其他任何需要使用这个模态框的组件中,只需如下使用:
```vue
<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 组件示例:
```vue
<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` 是一个函数,用于创建响应式的数据。在模板中,我们可以像平常一样使用 `count` 和 `increment`。
Composition API 还提供了一系列其它的函数,如 `reactive`、`computed`、`watch`、`onMounted`、`onUnmounted` 等,可以帮助我们更好地编写组件逻辑。
值得注意的是,使用 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 的一部分,使得我们可以更加灵活和高效地编写组件逻辑。
以下是这些函数的使用示例:
```vue
<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>
```
在上述示例中,我们使用 `ref` 和 `reactive` 创建了响应式数据,使用 `computed` 创建了计算属性,使用 `watch` 和 `watchEffect` 监听了数据的变化。并且在数据变化时更新了相关数据,并打印了日志信息。
- ** 创建和使用 composable 函数**
在 Vue 3.0 的 Composition API 中,我们可以将复用的逻辑提取出来,创建一个 composable 函数。composable 函数就是一个普通的 JavaScript 函数,这个函数内部使用了 Composition API 的功能,并返回一些响应式数据或函数。
以下是一个创建和使用 composable 函数的示例:
首先,我们创建一个名为 `useCounter` 的 composable 函数,这个函数提供了计数器的功能:
```javascript
import { ref } from 'vue'
export function useCounter() {
const count = ref(0)
const increment = () => {
count.value++
}
const decrement = () => {
count.value--
}
return {
count,
increment,
decrement
}
}
```
然后,在组件中使用 `useCounter` 函数:
```vue
<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 函数,然后在多个组件中复用这些函数。
7. **与外部交互**
与外部系统(如服务器端 API、本地存储或其他 JavaScript 库)交互是常见的需求。下面介绍一些 Vue 3 和 TypeScript 中的常用方法。
**使用 Axios 进行 HTTP 请求**
在处理与外部交互时,我们通常会遇到异步操作。以下是使用 Vue 3 和 TypeScript 进行异步处理的例子。
**使用 Axios 进行 HTTP 请求**
首先,我们可以使用 `async/await` 语法改写 Axios 的 HTTP 请求:
```typescript
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
```typescript
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 对象进行本地存储。以下是一个简单的例子:
```javascript
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()`,可以在组件的生命周期的不同阶段进行特定的操作。以下是一个简单的例子:
```javascript
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` 处理异步组件的例子:
```html
<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` 插槽的内容会被渲染。