vue3的composition-api实践总结

  • ref 对象具有指向内部值的单个 property.value"。

听着很绕口,简单来讲就是reactive可以为对象创建响应式而ref除了对象,还可以接收基础数据类型,比如string、boolean等。

那为什么会有这种差异呢?在vue3当中响应式是基于proxy实现的,而proxy的target必须是复杂数据类型,也就是存放在堆内存中,通过指针引用的对象。其实也很好理解,因为基础数据类型,每一次赋值都是全新的对象,所以根本无法代理。那么如果我们想取得简单类型的响应式怎么办呢?这时候就需要用到ref。

`class RefImpl {

private _value: T

public readonly __v_isRef = true

constructor(private _rawValue: T, public readonly _shallow = false) {

this._value = _shallow ? _rawValue : convert(_rawValue)

}

get value() {

track(toRaw(this), TrackOpTypes.GET, ‘value’)

return this._value

}

set value(newVal) {

if (hasChanged(toRaw(newVal), this._rawValue)) {

this._rawValue = newVal

this._value = this._shallow ? newVal : convert(newVal)

trigger(toRaw(this), TriggerOpTypes.SET, ‘value’, newVal)

}

}

}

const convert = (val: T): T =>

isObject(val) ? reactive(val) : val

`

ref通过创建内部状态,将值挂在value上,所以ref生成的对象,要通过value使用。重写get/set获得的监听,同时对对象的处理,也依赖了reactive的实现。

由此,ref并不只是具有对基本数据类型的响应式处理能力,他也是可以处理对象的。所以我认为ref和reactive的区分并不应该只是简单/复杂对象的区分,而是应该用编程思想区分的。我们应该避免,把reactive 当作data在顶部将所有变量声明的想法,而是应该结合具体的逻辑功能,比如一个控制灰度的Flag那他就应该是一个ref,而分页当中的页码,pageSize,total等就应该是一个reactive声明的对象。也就是说一个setup当中可以有多出响应变量的声明,而且他们应当是与逻辑紧密结合的.

接下来我先用一个分页的功能,用选项式和组合式api给大家对比一下。

一些例子

`

    • {{ item.title }}
      {{ item.content }}

      <el-pagination

      @size-change=“handleSizeChange”

      @current-change=“handleCurrentChange”

      :current-page=“currentPage”

      :page-sizes=“pageSizes”

      :page-size=“pageSize”

      layout=“total, sizes, prev, pager, next, jumper”

      :total=“total”

      `

      这还是我们熟悉到不能在熟悉的分页流程,在data中声明数据,在method中提供修分页的方法。当我们用composition-api实现的时候他就成了下面的样子。

      `

      import { defineComponent, reactive, ref, toRefs } from “@vue/composition-api”;

      import { getArticleList } from “@/mock/index”;

      export default defineComponent({

      setup() {

      const page = reactive({

      currentPage: 1,

      pageSizes: [5, 10, 20],

      pageSize: 5,

      total: 0,

      });

      function handleSizeChange(val) {

      page.pageSize = val;

      getList();

      }

      function handleCurrentChange(val) {

      page.currentPage = val;

      getList();

      }

      const articleList = ref([]);

      function getList() {

      getArticleList(page).then((res) => {

      articleList.value = res.data;

      page.total = res.total;

      });

      }

      getList();

      return {

      …toRefs(page),

      articleList,

      getList,

      handleSizeChange,

      handleCurrentChange,

      };

      },

      });

      `

      这是以composition-api的方式实现的分页,你会发现原本的data,method,还有声明周期等选项都不见了,所有的逻辑都放到了setup当中。通过这一个简单的例子,我们可以发现原本分散在各个选项中的逻辑,在这里得到了聚合。这种变化在复杂场景下更为明显。在复杂组件中,这种情况更加明显。而且当逻辑完全聚集在一起,这时候,将他们抽离出来,而且抽离逻辑的可以在别处复用,至此hook就形成了。

      hook形态的分页组件

      `// hooks/useArticleList.js

      import { ref } from “@vue/composition-api”;

      import { getArticleList } from “@/mock/index”; // mock ajax请求

      function useArticleList() {

      const articleList = ref([]);

      function getList(page) {

      getArticleList(page).then((res) => {

      articleList.value = res.data;

      page.total = res.total;

      });

      }

      return {

      articleList,

      getList,

      };

      }

      export default useArticleList;

      // hooks/usePage.js

      import { reactive } from “@vue/composition-api”;

      function usePage(changeFn) {

      const page = reactive({

      currentPage: 1,

      pageSizes: [5, 10, 20],

      pageSize: 5,

      total: 0,

      });

      function handleSizeChange(val) {

      page.pageSize = val;

      changeFn(page);

      }

      function handleCurrentChange(val) {

      page.currentPage = val;

      changeFn(page);

      }

      return {

      page,

      handleSizeChange,

      handleCurrentChange,

      };

      }

      export default usePage;

      // views/List.vue

      import { defineComponent, toRefs } from “@vue/composition-api”;

      import usePage from “@/hooks/usePage”;

      import useArticleList from “@/hooks/useArticleList”;

      export default defineComponent({

      setup() {

      const { articleList, getList } = useArticleList();

      const { page, handleSizeChange, handleCurrentChange } = usePage(getList);

      getList(page);

      return {

      …toRefs(page),

      articleList,

      getList,

      handleSizeChange,

      handleCurrentChange,

      };

      },

      });

      `

      在hook使用过程中我们也踩过很多坑

      1. hook中的异步问题

      因为hook本质上就是函数,所以灵活度非常高,尤其是在涉及异步的逻辑中,考虑不全面就很有可能造成很多问题。hook是可以覆盖异步情况的,但是必须在setup当中执行时返回有效对象不能被阻塞。

      我们总结了两种异步的风格,通过一个简单的hook为例

      • 外部没有其他依赖,只是交付渲染的响应变量 对于这种情况,可以通过声明、对外暴露响应变量,在hook中异步修改的方式

      `// hooks/useWarehouse.js

      import { reactive,toRefs } from ‘@vue/composition-api’;

      import { queryWarehouse } from ‘@/mock/index’;  // 查询仓库的请求

      import getParam from ‘@/utils/getParam’; // 获得一些参数的方法

      function useWarehouse(admin) {

      const warehouse = reactive({ warehouseList: [] });

      const param = { id: admin.id, …getParam() };

      const queryList = async () => {

      const { list } = await queryWarehouse(param);

      list.forEach(goods=>{

      // 一些逻辑…

      return goods

      })

      warehouse.warehouseList = list;

      };

      return { …toRefs(warehouse), queryList };

      }

      `

      `export default useWarehouse;// components/Warehouse.vue

      <button @click=“queryList”>queryList

      • {{goods}}

        `

        • 外部具有依赖,需要在使用侧进行加工的 可以通过对外暴露Promise的方式,使外部获得同步操作的能力 在原有例子上拓展,增加一个需要处理的更新时间属性

        `// hooks/useWarehouse.js

        function useWarehouse(admin) {

        const warehouse = reactive({ warehouseList: [] });

        const param = { id: admin.id, …getParam() };

        const queryList = async () => {

        const { list, updateTime } = await queryWarehouse(param);

        list.forEach(goods=>{

        // 一些逻辑…

      评论
      添加红包

      请填写红包祝福语或标题

      红包个数最小为10个

      红包金额最低5元

      当前余额3.43前往充值 >
      需支付:10.00
      成就一亿技术人!
      领取后你会自动成为博主和红包主的粉丝 规则
      hope_wisdom
      发出的红包
      实付
      使用余额支付
      点击重新获取
      扫码支付
      钱包余额 0

      抵扣说明:

      1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
      2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

      余额充值