IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    初探 Vue 3.0 的组装式 API(二)

    krimeshu发表于 2023-09-24 03:02:00
    love 0

    在 RC13 之后,Vue 推出了 3.0 正式版。之前我们尝试了使用新的组装式 API 实现响应式数据,并且和 Vue2 进行了简单对比。

    今天继续看看其它日常使用方式的变化与对比吧。

    前文:

    • 《初探 Vue 3.0 的组装式 API(一)》

    (二)事件处理与 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
    // 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 返回给模板使用;
    • 数据与事件处理逻辑处于同一层级,可以编译前发现命名冲突等问题;
    • 而且便于收拢事件处理函数和相关数据的位置,提高代码可读性和可重构性。

    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
    // 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;
    }
    }
    },
    };
    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
    <!-- 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 中,你可以使用类似构造函数的结构,在组件中取到返回值后,直接解构使用:

    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
    // 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,
    };
    },
    };
    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
    <!-- 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>

    也可以根据个人喜好和业务实际情况,考虑做进一步拆分,以便简化代码结构或者实现某些属性的单例控制等效果:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    // 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,更加自由可控、清晰明了。


    下一篇:《初探 Vue 3.0 的组装式 API(三)》



沪ICP备19023445号-2号
友情链接