在 RC13 之后,Vue 推出了 3.0 正式版。之前我们尝试了使用新的组装式 API 实现响应式数据,并且和 Vue2 进行了简单对比。
今天继续看看其它日常使用方式的变化与对比吧。
前文:
(二)事件处理与 mixin 复用
1. 简单例子
Vue2 中,模板使用到的事件处理函数,通常都被放在 vm 构造参数的 methods
属性中,然后才能通过 v-on:<event>
/@<event>
标记到对应 DOM 上:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| <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 对象。
2. 使用组装式 API 实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import { ref } from 'vue';
export default { setup() { const count = ref(0); const increase = () => { count.value += 1; }; return { count, increase, }; }, };
|
在 Vue3 的 setup
中,对数据的改动,直接使用普通函数或箭头表达式对数据进行操作就行了,非常直观。
- 事件处理函数和数据一起通过
return
返回给模板使用;
- 数据与事件处理逻辑处于同一层级,可以编译前发现命名冲突等问题;
- 而且便于收拢事件处理函数和相关数据的位置,提高代码可读性和可重构性。
3. 不再需要 mixin
(1) Vue2 的 mixin 实现
对于不同组件可复用的数据和事件处理函数关系,在 Vue2 中我们通常都是用 mixin 来完成的。
比如,不同页面都经常使用到一个 ajax
的网络请求方法,和一个请求状态数据 isRequestSending
(可用于在模板内判断和调整界面展示和按键交互),过去的 Vue2 中通常这样实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| export default { data() { return { isRequestSending: false, }; }, methods: { async ajax() { if (this.isRequestSending) { return { status: -1, msg: 'network busy' }; } try { this.isRequestSending = true; return await fetch(); } catch (ex) { throw ex; } finally { this.isRequestSending = false; } } }, };
|
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
| <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(); }, }, }; </script>
|
可以看出,因为之前 vm 构造参数导致 this 指代混乱的问题,Vue2 中组件的可复用逻辑只好使用 mixin 的方式,将一个与构造参数结构一致的对象混合到一起来实现。
以至于这个 mixin 的结构,同样继承了组件构造参数的毛病。
而且引入 mixin 之前,无法通过标准 es 模块结构分析可用的数据、方法和钩子函数。必须解读参数中字段,甚至函数返回值,才能得知复用逻辑的大致结构。
(2) Vue3 的方案
Vue3 中,你可以使用类似构造函数的结构,在组件中取到返回值后,直接解构使用:
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
| 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(); } catch (ex) { throw ex; } finally { isRequestSending.value = false; } }; return { isRequestSending, ajax, }; }, };
|
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> <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(); }; return { ...netMixin,
response, callAjax, }; }, }; </script>
|
也可以根据个人喜好和业务实际情况,考虑做进一步拆分,以便简化代码结构或者实现某些属性的单例控制等效果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 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(); } catch (ex) { throw ex; } finally { isRequestSending.value = false; } };
|
相比 Vue2 的 mixin,更加自由可控、清晰明了。
下一篇:《初探 Vue 3.0 的组装式 API(三)》