13.Vue3组合式API

Vue3 组合式 API(Composition API) 主要用于在大型组件中提高代码逻辑的可复用性。

传统的组件随着业务复杂度越来越高,代码量会不断的加大,整个代码逻辑都不易阅读和理解。

Vue3 使用组合式 API 的地方为 setup

在 setup 中,我们可以按逻辑关注点对部分代码进行分组,然后提取逻辑片段并与其他组件共享代码。因此,组合式 API(Composition API) 允许我们编写更有条理的代码。

Api 对比以下两端代码:

1、传统组件

传统组件

2、组合式 API

组合式 API

响应式基础

ref()

声明响应式状态

ref() 函数可以根据给定的值来创建一个响应式的数据对象,返回值是一个对象,且只包含一个 .value 属性。

在 setup() 函数内,由 ref() 创建的响应式数据返回的是对象,所以需要用 .value 来访问。

在组合式 API 中,推荐使用 ref() 函数来声明响应式状态:

1
2
3
import { ref } from 'vue'

const count = ref(0)

ref() 接收参数,并将其包裹在一个带有 .value 属性的 ref 对象中返回:

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

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

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

要在组件模板中访问 ref,请从组件的 setup() 函数中声明并返回它们:

1
2
3
4
5
6
7
8
9
10
11
12
13
import { ref } from 'vue'

export default {
// `setup` 是一个特殊的钩子,专门用于组合式 API。
setup() {
const count = ref(0)

// 将 ref 暴露给模板
return {
count
}
}
}
1
<div>{{ count }}</div>

注意,在模板中使用 ref 时,我们需要附加 .value。为了方便起见,当在模板中使用时,ref 会自动解包 (有一些注意事项)。

你也可以直接在事件监听器中改变一个 ref:

1
2
3
<button @click="count++">
{{ count }}
</button>

setup()

setup() 钩子是在组件中使用组合式 API 的入口,通常只在以下情况下使用:

  1. 需要在非单文件组件中使用组合式 API 时。
  2. 需要在基于选项式 API 的组件中集成基于组合式 API 的代码时。

对于结合单文件组件使用的组合式 API,推荐通过 script setup 以获得更加简洁及符合人体工程学的语法。

我们可以使用响应式 API 来声明响应式的状态,在 setup() 函数中返回的对象会暴露给模板和组件实例。其他的选项也可以通过组件实例来获取 setup() 暴露的属性:

setup() 函数在组件创建 created() 之前执行。

setup() 函数接收两个参数 props 和 context。

第一个参数 props,它是响应式的,当传入新的 prop 时,它将被更新。

第二个参数 context 是一个普通的 JavaScript 对象,它是一个上下文对象,暴露了其它可能在 setup 中有用的值。

**注意:**在 setup 中你应该避免使用 this,因为它不会找到组件实例。setup 的调用发生在 data property、computed property 或 methods 被解析之前,所以它们无法在 setup 中被获取。

以下实例使用组合 API 定义一个计数器:

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
<template>
    <div>
        <p>计数器实例: {{ count }}</p>
        <input @click="myFn" type="button" value="点我加 1">
    </div>
</template>

<script>
import {ref, onMounted} from 'vue';

export default {
    setup(){
        //定义初始值为0的变量,要使用ref方法赋值,直接赋值的话变量改变不会更新 UI
        let count = ref(0);

        // 定义点击事件 myFn
        function myFn(){
            console.log(count);
            count.value += 1;
        }
       
       // 组件被挂载时,我们用 onMounted 钩子记录一些消息
        onMounted(() => console.log('component mounted!'));

        // 外部使用组合API中定义的变量或方法,在模板中可用。
        return {count,myFn} // 返回的函数与方法的行为相同
    }
}
</script>

<script setup>

<script setup> 是在单文件组件 (SFC) 中使用组合式 API 的编译时语法糖。当同时使用 SFC 与组合式 API 时该语法是默认推荐。相比于普通的<script>语法,它具有更多优势:

  • 更少的样板内容,更简洁的代码。
  • 能够使用纯 TypeScript 声明 props 和自定义事件。
  • 更好的运行时性能 (其模板会被编译成同一作用域内的渲染函数,避免了渲染上下文代理对象)。
  • 更好的 IDE 类型推导性能 (减少了语言服务器从代码中抽取类型的工作)。

setup() 函数中手动暴露大量的状态和方法非常繁琐。幸运的是,我们可以通过使用单文件组件 (SFC) 来避免这种情况。我们可以使用 <script setup> 来大幅度地简化代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<template>
<button @click="increment">
{{ count }}
</button>
</template>

<script setup>
import { ref } from 'vue'

const count = ref(0)

function increment() {
count.value++
}
</script>

reactive()

  • reactive同样为我们的值创建了一个响应式引用
  • 定义基本普通类型数据不能用reactive,用ref
  • reactive主要定义复杂数据类型,比如数组,对象
  • reactive可响应深层次的数据,比如多维数组
  • reactive返回一 个响应式的proxy对象
  • 使用需引入

与将内部值包装在特殊对象中的 ref 不同,reactive() 将使对象本身具有响应性:

1
2
3
import { reactive } from 'vue'

const state = reactive({ count: 0 })

示例

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
<template>
<div>
<h1>姓名:{{ name }}</h1>
<h1>年龄:{{ obj.age }}</h1>
<h1>数据:{{ obj.pro.a.b[0] }}</h1>
<button @click="btn">点击我更新数据</button>
</div>
</template>

<script setup>
import { reactive } from 'vue';
const name = reactive('马云') // value cannot be made reactive: 马云
const age = 50
const obj = reactive({
age,
pro: {
a: {
b: ['我是深层次的数据']
}
}
})
function btn() {
console.log(obj);
obj.age = 80
obj.pro.a.b[0] = "我被修改了"
}
</script>

reactive() API 有一些局限性:

  1. 有限的值类型:它只能用于对象类型 (对象、数组和如 MapSet 这样的集合类型)。它不能持有如 stringnumberboolean 这样的原始类型

  2. 不能替换整个对象:由于 Vue 的响应式跟踪是通过属性访问实现的,因此我们必须始终保持对响应式对象的相同引用。这意味着我们不能轻易地“替换”响应式对象,因为这样的话与第一个引用的响应性连接将丢失:

    1
    2
    3
    4
    5
    let state = reactive({ count: 0 })

    // 上面的 ({ count: 0 }) 引用将不再被追踪
    // (响应性连接已丢失!)
    state = reactive({ count: 1 })
  3. 对解构操作不友好:当我们将响应式对象的原始类型属性解构为本地变量时,或者将该属性传递给函数时,我们将丢失响应性连接:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    const state = reactive({ count: 0 })

    // 当解构时,count 已经与 state.count 断开连接
    let { count } = state
    // 不会影响原始的 state
    count++

    // 该函数接收到的是一个普通的数字
    // 并且无法追踪 state.count 的变化
    // 我们必须传入整个对象以保持响应性
    callSomeFunction(state.count)

由于这些限制,我们建议使用 ref() 作为声明响应式状态的主要 API。

toRef()

  • toRef也可以创建一个响应式数据
  • ref本质是拷贝粘贴一份数据,脱离了与原数据的交互
  • ref函数将对象中的属性变成响应式数据,修改响应式数据是不会影响到原数据,但是会更新视图层
  • toRef的本质是引用,与原始数据有交互,修改响应式数据会影响到原数据,但是不会更新视图层
  • 使用需引入
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<template>
<div>
<h1>姓名:{{ obj.name }}</h1>
<h1>年龄:{{ obj.age }}</h1>
<button @click="btn">点击我更新数据</button>
</div>
</template>

<script setup>
import { toRef } from 'vue';
const obj = {name:'马云',age:50}
const res = toRef(obj,'name')
function btn() {
console.log(obj);
res.value='马化腾'
}
</script>

修改源数据不更新视图层

toRefs()

  • 用于批量设置多个数据为响应式数据
  • toRefs与原始数据有交互,修改响应式数据会影响到原数据,但是不会更新视图层
  • toRefs还可以与其他响应式函数交互,更加方便处理视图层数据
  • 使用需引入

将一个响应式对象转换为一个普通对象,这个普通对象的每个属性都是指向源对象相应属性的 ref。每个单独的 ref 都是使用 toRef() 创建的。

示例

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
<template>  
<div>
<h1>姓名:{{ name }}</h1>
<h1>年龄:{{ age }}</h1>
<h1>a:{{ a }}</h1>
<h1>b:{{ b }}</h1>
<h1>c:{{ c }}</h1>
<button @click="updateData">点击我更新数据</button>
</div>
</template>

<script>
import { reactive, toRefs } from 'vue';

export default {
setup() {
const obj = reactive({name: '马云', age: 50, a: 1, b: 2, c: 3});
const updateData = () => {
// 更新数据的逻辑在这里
console.log('Data updated!');
};
return { ...toRefs(obj), updateData };
}
}
</script>

使用展开语法结合toRefs()

计算属性

computed

  • 用于声明要在组件实例上暴露的计算属性。
  • 与Vue2一致,均是用来监听数据变化
  • 使用需引入

以下是利用computed常用的两种方法

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
35
36
37
<template>  
<div>
计算器:<input type="number" v-model="num1"> + <input type="number" v-model="num2"> = <input type="number" v-model="num">
<br>
文本拼接:<input type="text" v-model="t1"> + <input type="text" v-model="t2"> = <input type="text" v-model="t">
</div>
</template>

<script>
import { ref,computed,reactive,toRefs } from 'vue';

export default {
setup() {
// 第一种方法:ref
let num1 = ref()
let num2 = ref()

// 计算器
const num = computed(()=>{
return num1.value+num2.value
})

// 第二种方法:reactive,toRefs
const t1=''
const t2=''
const obj = reactive({t1,t2})
// 文本拼接
const t = computed(()=>{
return obj.t1+'+'+obj.t2
})

return{num1,num2,num,...toRefs(obj),t}


}
}
</script>

侦听器

watch

用于声明在数据更改时调用的侦听回调。

watch() 默认是懒侦听的,即仅在侦听源发生变化时才执行回调函数。

第一个参数是侦听器的。这个来源可以是以下几种:

  • 一个函数,返回一个值
  • 一个 ref
  • 一个响应式对象
  • …或是由以上类型的值组成的数组

第二个参数是在发生变化时要调用的回调函数。这个回调函数接受三个参数:新值、旧值,以及一个用于注册副作用清理的回调函数。该回调函数会在副作用下一次重新执行前调用,可以用来清除无效的副作用,例如等待中的异步请求。

当侦听多个来源时,回调函数接受两个数组,分别对应来源数组中的新值和旧值。

第三个可选的参数是一个对象,支持以下这些选项:

  • immediate:在侦听器创建时立即触发回调。第一次调用时旧值是 undefined
  • deep:如果源是对象,强制深度遍历,以便在深层级变更时触发回调。参考深层侦听器
  • flush:调整回调函数的刷新时机。参考回调的刷新时机watchEffect()
  • onTrack / onTrigger:调试侦听器的依赖。参考调试侦听器

watchEffect() 相比,watch() 使我们可以:

  • 懒执行副作用;
  • 更加明确是应该由哪个状态触发侦听器重新执行;
  • 可以访问所侦听状态的前一个值和当前值。

示例:

  1. 侦听一个 ref:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<template>  
<div>
<h1>{{ p1 }}</h1>
<button @click="p1++">点击我+1</button>
</div>
</template>

<script>
import { ref,watch } from 'vue';

export default {
setup() {
const p1=ref(0)
// 监听p1数据变化
watch(p1,(newVal,oldVal)=>{
console.log("新数据",newVal,"旧数据",oldVal);
})
return{p1,}
}
}
</script>
1
2
3
4
5
6
新数据 1 旧数据 0
新数据 2 旧数据 1
新数据 3 旧数据 2
新数据 4 旧数据 3
新数据 5 旧数据 4
新数据 6 旧数据 5
  1. 侦听多个数据变化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import { ref, watch } from 'vue';

export default {
setup() {
const p1 = ref(0)
const p2 = ref(0)
const p3 = ref(0)
// 监听p1数据变化
watch(p1, (newVal, oldVal) => {
console.log("新数据", newVal, "旧数据", oldVal);
})
// 监听p2数据变化
watch(p2, (newVal, oldVal) => {
console.log("新数据", newVal, "旧数据", oldVal);
})
// 监听p3数据变化
watch(p3, (newVal, oldVal) => {
console.log("新数据", newVal, "旧数据", oldVal);
})
return { p1,p2,p3 }
}
}

简便写法:监听一个数组

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

export default {
setup() {
const p1 = ref(0)
const p2 = ref(0)
const p3 = ref(0)
// 监听p1,p2,p3数据变化
watch([p1,p2,p3], (newVal, oldVal) => {
console.log("新数据", newVal, "旧数据", oldVal);
})
return { p1,p2,p3 }
}
}
  1. 侦听整个对象数据变化
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
<template>
<div>
<h1>马云年龄:{{ obj.age }}</h1>
<button @click="obj.age++">点击我马云年龄+1</button>
</div>
</template>

<script>
import { watch, reactive } from 'vue';

export default {
setup() {
const obj = reactive({
name: "马云",
age: 50
})
watch(obj, (newVal, oldVal) => {
console.log("新数据", newVal, "旧数据", oldVal);
})
return { obj }
}
}
</script>

只能监听到最新的结果,上一个的数据是监听不到的
新数据 Proxy(Object) {name: '马云', age: 51} 旧数据 Proxy(Object) {name: '马云', age: 51}
新数据 Proxy(Object) {name: '马云', age: 52} 旧数据 Proxy(Object) {name: '马云', age: 52}
新数据 Proxy(Object) {name: '马云', age: 53} 旧数据 Proxy(Object) {name: '马云', age: 53}
  1. 侦听对象数据某一个数据变化
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
<template>
<div>
<h1>马云年龄:{{ obj.age.num }}</h1>
<button @click="obj.age.num++">点击我马云年龄+1</button>
</div>
</template>

<script>
import { watch, reactive } from 'vue';

export default {
setup() {
const obj = reactive({
name: "马云",
age: {
num:50
}
})
watch(()=>obj.age.num, (newVal, oldVal) => {
console.log("新数据", newVal, "旧数据", oldVal);
},{immediate:true}) // {immediate:true} 进入页面立即进入监听
return { obj }
}
}
</script>

新数据 50 旧数据 undefined
新数据 51 旧数据 50
新数据 52 旧数据 51
新数据 53 旧数据 52

watchEffect()

立即运行一个函数,同时响应式地追踪其依赖,并在依赖更改时重新执行。

watchEffect如果存在的话,在组件初始化的时候就会执行一次用以收集依赖

watch可以获取到新值与旧值(更新前的值),而watchEffect是拿不到的

watchEffect不需要指定监听的属性,他会自动的收集依赖,只要我们回调中引用到了响应式的属性,

那么当这些属性变更的时候,这个回调都会执行,而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
<template>
<div>
<h1>马云年龄:{{ obj.age.num }}</h1>
<button @click="obj.age.num++">点击我马云年龄+1</button>
</div>
</template>

<script>
import { watchEffect, reactive } from 'vue';

export default {
setup() {
const obj = reactive({
name: "马云",
age: {
num:50
}
})
watchEffect(()=>{
console.log(obj.age.num);
})
return { obj }
}
}
</script>

停止侦听

1
2
3
4
const res = watchEffect(()=>{
console.log(obj.age.num);
})
res() // 停止侦听

响应式进阶

shallowRef()

只能处理基本类型数据

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
<template>
<div>
<h1>姓名:{{ obj.name }}</h1>
<h1>年龄:{{ obj.age.num }}</h1>
<button @click="obj.age.num++">点击我马云年龄+1</button>
<br>
<h1>姓名:{{ obj1 }}</h1>
<h1>年龄:{{ obj2 }}</h1>
<button @click="obj2++">点击我马化腾年龄+1</button>
</div>
</template>

<script>
import { shallowRef } from 'vue';

export default {
setup() {
const obj = shallowRef({
name:'马云',
age:{
num:50
}
})
const obj1 = shallowRef('马化腾')
const obj2 = shallowRef(50)
return { obj,obj1,obj2 }
}
}
</script>

shallowRef

shallowReactive()

只处理第一层数据

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
<template>
<div>
<h1>姓名:{{ obj.name }}</h1>
<h1>年龄:{{ obj.age.num }}</h1>
<button @click="obj.name+='1'">点击我马云姓名+1</button>
<button @click="obj.age.num++">点击我马云年龄+1</button>
</div>
</template>

<script>
import { shallowReactive } from "vue";

export default {
setup() {
const obj = shallowReactive({
name: "马云", // 第一层
age: {
num: 50, // 第二层
},
});
return { obj };
},
};
</script>

shallowReactive

组件间传值

provide()

提供一个值,可以被后代组件注入。

provide() 接受两个参数:第一个参数是要注入的 key,可以是一个字符串或者一个 symbol,第二个参数是要注入的值。

与注册生命周期钩子的 API 类似,provide() 必须在组件的 setup() 阶段同步调用。

示例

App.vue修改如下

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
<script>
import HelloWorld from './components/HelloWorld.vue'
import {reactive,provide} from 'vue'
export default{
name:'App',
components:{
HelloWorld
},
setup(){
const p1 = reactive({name:'马云',age:50})
provide('aaa',p1) // 传递
return{p1}
}
}
</script>

<template>
<div class="back">
我是父组件
<h3>{{ p1.name }}</h3>
<h3>{{ p1.age }}</h3>
</div>
<HelloWorld/>
</template>

<style>
.back{
background-color: darkkhaki;
padding: 20px 0;
}
</style>

inject()

注入一个由祖先组件或整个应用 (通过 app.provide()) 提供的值。

第一个参数是注入的 key。Vue 会遍历父组件链,通过匹配 key 来确定所提供的值。如果父组件链上多个组件对同一个 key 提供了值,那么离得更近的组件将会“覆盖”链上更远的组件所提供的值。如果没有能通过 key 匹配到值,inject() 将返回 undefined,除非提供了一个默认值。

第二个参数是可选的,即在没有匹配到 key 时使用的默认值。

第二个参数也可以是一个工厂函数,用来返回某些创建起来比较复杂的值。在这种情况下,你必须将 true 作为第三个参数传入,表明这个函数将作为工厂函数使用,而非值本身。

与注册生命周期钩子的 API 类似,inject() 必须在组件的 setup() 阶段同步调用。

示例

src\components\HelloWorld.vue修改如下

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
<script>
import { inject } from 'vue'
export default{
setup() {
const p1 = inject('aaa') // 接收
const p2 = inject('bbb',"接收为空默认值")
console.log(p1);
return{p1,p2}
}
}
</script>

<template>
<div class="back">
这是子组件
<h2>{{ p1.name }}</h2>
<h2>{{ p1.age }}</h2>
<h2>{{ p2 }}</h2>
</div>
</template>

<style scoped>
.back{
background-color: brown;
color: cyan;
padding: 20px 0;
}
</style>

组件间传值

点击按钮后再进行传值

App.vue修改如下

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
35
36
37
<script>
import HelloWorld from './components/HelloWorld.vue'
import {reactive,ref} from 'vue'
export default{
name:'App',
components:{
HelloWorld
},
setup(){
const val = ref()
const p1 = reactive({name:'马云',age:50})
// provide('aaa',p1) // 传递
function btn(){
val.value.qqq(p1)
}
return{p1,btn,val}
}
}
</script>

<template>
<div class="back">
我是父组件
<h3>{{ p1.name }}</h3>
<h3>{{ p1.age }}</h3>
<button @click="btn">给子组件传值</button>
</div>
<HelloWorld ref="val"/>
</template>

<style>
.back{
background-color: darkkhaki;
padding: 20px 0;
}
</style>

src\components\HelloWorld.vue修改如下

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
<script>
import { ref } from 'vue'
export default{
setup() {
const p1 = ref('')
function qqq(val){
console.log('我被父组件调用了')
p1.value = val
}
return{qqq,p1}
}
}
</script>

<template>
<div class="back">
这是子组件
<h2>{{ p1.name }}</h2>
<h2>{{ p1.age }}</h2>
</div>
</template>

<style scoped>
.back{
background-color: brown;
color: cyan;
padding: 20px 0;
}
</style>

点击按钮后再进行传值

生命周期钩子

在 Vue2 中,我们通过以下方式实现生命周期钩子函数:

1
2
3
4
5
6
7
8
export default {
  beforeMount() {
    console.log('V2 beforeMount!')
  },
  mounted() {
    console.log('V2 mounted!')
  }
};

在 Vue3 组合 API 中实现生命周期钩子函数可以在 setup() 函数中使用带有 on 前缀的函数:

1
2
3
4
5
6
7
8
9
10
11
import { onBeforeMount, onMounted } from 'vue';
export default {
  setup() {
    onBeforeMount(() => {
      console.log('V3 beforeMount!');
    })
    onMounted(() => {
      console.log('V3 mounted!');
    })
  }
};

下表为 Options APIComposition API 之间的映射,包含如何在 setup () 内部调用生命周期钩子:

Vue2 Options-based APIVue Composition API
beforeCreatesetup()在实例创建之前调用
createdsetup()实例创建完毕后调用
beforeMountonBeforeMount在挂载之前调用
mountedonMounted已经挂载到 DOM 上后调用
beforeUpdateonBeforeUpdate数据更新之前调用
updatedonUpdated数据更新后调用
beforeDestroyonBeforeUnmount实例销毁之前调用
destroyedonUnmounted实例销毁后调用
errorCapturedonErrorCaptured组件树中发生错误时被触发

因为 setup 是围绕 beforeCreate 和 created 生命周期钩子运行的,所以不需要显式地定义它们。换句话说,在这些钩子中编写的任何代码都应该直接在 setup 函数中编写。

这些函数接受一个回调函数,当钩子被组件调用时将会被执行:

1
2
3
4
5
6
7
8
9
import { onMounted} from 'vue'
export default{
setup() {
onMounted(() => {
// 组件被挂载后,我们用 onMounted 钩子记录一些消息
console.log('component mounted!')
})
}
}

抽离封装

当我们有两个或多个页面采用的是同样的配置或数据,如何抽离出来。

比如同样的数据

1
2
3
4
5
const res_a = reactive({
name: '马云',
age: 50,
company: '阿里巴巴'
});

新建一个通配js配置文件src\stores\public.js

1
2
3
4
5
6
7
8
9
10
11
import { reactive } from "vue";

export const plbLic = () => {
const res_a = reactive({
name: '马云',
age: 50,
company: '阿里巴巴'
});
return res_a;
};

App.vue修改如下

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
<script>
import HelloWorld from './components/HelloWorld.vue'
import { plbLic } from './stores/public' // 使用花括号进行命名导入
export default{
name:'App',
components:{
HelloWorld
},
setup(){
const res_a = plbLic()
return{res_a}
}
}
</script>

<template>
<div class="back">
我第一个页面
<h3>{{ res_a.name }}</h3>
<h3>{{ res_a.age }}</h3>
</div>
<HelloWorld/>
</template>

<style>
.back{
background-color: darkkhaki;
padding: 20px 0;
}
</style>

src\components\HelloWorld.vue修改如下

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
<script>
import { onMounted } from 'vue'
import { plbLic } from '../stores/public' // 使用花括号进行命名导入

export default {
setup() {
const res_a = plbLic();
return { res_a };
}
}
</script>

<template>
<div class="back">
我是第n个页面
<h3>{{ res_a.name }}</h3>
<h3>{{ res_a.age }}</h3>
</div>
</template>

<style scoped>
.back{
background-color: brown;
color: cyan;
padding: 20px 0;
}
</style>

数据抽离