在 RC13 之后,Vue 推出了 3.0 正式版。之前我们尝试了使用新的组装式 API 实现响应式数据,并且和 Vue2 进行了简单对比。
今天继续看看其它日常使用方式的变化与对比吧。
Vue2 中,模板使用到的事件处理函数,通常都被放在 vm 构造参数的 methods
属性中,然后才能通过 v-on:<event>
/@<event>
标记到对应 DOM 上:
<template>
<div>
<p>Count: {{count}}</p>
<button @click="increase">Increase</button>
</div>
</template>
<script>
export default {
data() {
return {
count: 0,
};
},
methods: {
increase() {
this.count += 1;
},
},
}
</script>
上面的 increase()
方法中,this
看似指向 methods
属性的对象,实际上和之前 data
返回对象一样,指向的其实是最终创建的 vm 对象,日常指代混乱。
data
中返回了数据模板,告知 vm 会有一个名为 count 的响应式数据;methods
对象作为方法模板,告知 vm 需要创建一个名为 increase 的方法,供模板事件处理; export default
对象中不处在同一层级,实际上 this
都指向了 vm 对象。// Vue 3.0
import { ref } from 'vue';
export default {
setup() {
const count = ref(0);
const increase = () => {
count.value += 1;
};
return {
count,
increase,
};
},
};
在 Vue3 的 setup
中,对数据的改动,直接使用普通函数或箭头表达式对数据进行操作就行了,非常直观。
return
返回给模板使用;(1) Vue2 的 mixin 实现
对于不同组件可复用的数据和事件处理函数关系,在 Vue2 中我们通常都是用 mixin 来完成的。
比如,不同页面都经常使用到一个 ajax
的网络请求方法,和一个请求状态数据 isRequestSending
(可用于在模板内判断和调整界面展示和按键交互),过去的 Vue2 中通常这样实现:
// mixin-net.js
export default {
data() {
return {
isRequestSending: false,
};
},
methods: {
async ajax(/* some params... */) {
if (this.isRequestSending) {
return { status: -1, msg: 'network busy' };
}
try {
this.isRequestSending = true;
return await fetch(/* some params... */);
} catch (ex) {
throw ex;
} finally {
this.isRequestSending = false;
}
}
},
};
<!-- page-a.vue -->
<template>
<div>
<button @click="callAjax">call ajax</button>
<p>is request sending: {{isRequestSending}}</p>
<p>resonse: {{response}}</p>
</div>
</template>
<script>
import mixinNet from './mixin-net.js';
export default {
mixins: [ mixinNet ],
data() {
return {
response: '',
};
},
methods: {
async callAjax() {
this.response = await this.ajax(/* some params... */);
},
},
};
</script>
可以看出,因为之前 vm 构造参数导致 this 指代混乱的问题,Vue2 中组件的可复用逻辑只好使用 mixin 的方式,将一个与构造参数结构一致的对象混合到一起来实现。
以至于这个 mixin 的结构,同样继承了组件构造参数的毛病。
而且引入 mixin 之前,无法通过标准 es 模块结构分析可用的数据、方法和钩子函数。必须解读参数中字段,甚至函数返回值,才能得知复用逻辑的大致结构。
(2) Vue3 的方案
Vue3 中,你可以使用类似构造函数的结构,在组件中取到返回值后,直接解构使用:
// net.js
import { ref } from 'vue';
export default {
setup() {
const isRequestSending = ref(false);
const ajax = async () => {
if (isRequestSending.value) {
return { status: -1, msg: 'network busy' };
}
try {
isRequestSending.value = true;
return await fetch(/* some params... */);
} catch (ex) {
throw ex;
} finally {
isRequestSending.value = false;
}
};
return {
isRequestSending,
ajax,
};
},
};
<!-- page-a.vue -->
<template>
<div>
<button @click="callAjax">call ajax</button>
<p>is request sending: {{isRequestSending}}</p>
<p>resonse: {{response}}</p>
</div>
</template>
<script>
import net from './net.js';
export default {
setup() {
const netMixin = net.setup();
const { ajax } = netMixin;
const response = ref('');
const callAjax = async () => {
response.value = await ajax(/* some params... */);
};
return {
...netMixin,
response,
callAjax,
};
},
};
</script>
也可以根据个人喜好和业务实际情况,考虑做进一步拆分,以便简化代码结构或者实现某些属性的单例控制等效果:
// net.js
import { ref } from 'vue';
export const isRequestSending = ref(false);
export const ajax = async () => {
if (isRequestSending.value) {
return { status: -1, msg: 'network busy' };
}
try {
isRequestSending.value = true;
return await fetch(/* some params... */);
} catch (ex) {
throw ex;
} finally {
isRequestSending.value = false;
}
};
相比 Vue2 的 mixin,更加自由可控、清晰明了。