[重构倒数第26天] - 你可能不需要Vuex (You might not need Vuex)

前言

该系列是为了让看过Vue官方文件或学过Vue但是却不知道怎麽下手去重构现在有的网站而去规画的系列文章,在这边整理了许多我自己使用Vue重构很多网站的经验分享给读者们。

You might not need Vuex

我们在Vue要使用跨多个 component 做资料传递沟通的时候除了 Vuex 以外,在 Vue3 的时候我们有多了其他选择,那就是 Provide / inject

vue mike

我们可以看到官网上面的这张图就完整的表达 Provide / inject 的使用概念,你可以在父层使用provide来提供资料,然後再子 component 透过 inject 来使用这些资料,无论你的 component 结构有多深,你的父层都可以为你所有底下的所有的 component 去直接 inject。

我们来看看如何实作,先来看看资料夹结构,其他资料夹等我就先不放上来了

|-- src
    |-- App.vue         // 最上层的component
    |-- main.js         // 进入点
    |-- components
    |   |-- Content.vue // 一般的UI component
    |-- store
    |   |-- index.js    // 不是Vuex,是要使用 Composition API 存放资料的地方
    |-- views
        |-- Home.vue    // 首页的页面component

首先我们先来定义 store/index.js

import { reactive, readonly } from "vue";
const state = reactive({
  count: 0,
});
export default {
  state: readonly(state),
};

在这边你会看到我透过 vue3 的reactive去定义我的资料,透过 vue 的方法可以确保我的资料被更动的时候可以被更新,当然你可以用ref,就依照你的需求来决定,最後我在 export 的时候用了 readonly 函数包起来,就是要避免外面 inject 的时候不小心改到 state 的资料,至於要怎麽改资料我们等等来说,我们先来看要怎麽在component 去使用。

App.vue

<script>
import { provide } from "vue";
import store from "@/store";
import Home from "@/views/Home.vue";
export default {
  components: {
    Home,
  },
  setup() {
    provide("mapStore", store);
  },
};
</script>

我在 setup 里面将我们 provide 命名为 mapStore ( 借镜一下 Vuex 的命名,这样从 Vuex 转换过来也会比较亲切 ) ,然後把我刚刚定义好的资料给载入进来,就这麽简单。

Content.vue

<script>
import { inject, toRefs } from "vue";
export default {
  setup() {
    const mapStore = inject("mapStore");
    const { state } = mapStore;
    return {
      ...toRefs(state),
    };
  },
};
</script>

<template>
  <div class="content">
    <h1>count: {{ count }}</h1>
    <button>add</button>
    <button>remove</button>
  </div>
</template>

这边透过 inject 的方式把我的 mapStore 给注入进来,接下来我就可以直接取得我定义好的state 资料,然後丢到我的template上,不过这边要注意我使用了toRefs 去做包我的 state,为什麽不直接把 state 丢出去呢?

原因是因为透过 reactive 包装的资料,是透过解构的方式取出的话,会失去它的连动特性,所以这个时候在需要透过 toRefs 的包装,来让它维持资料的连动,最後把我定义好的 count 这个资料放到画面上

toRefs 详情可以参考文件 https://v3.cn.vuejs.org/api/refs-api.html#torefs

现在的画面长这样

vue mike

假设今天我直接增加一个 function去修改我拿到的count的话

<script>
import { inject, toRefs } from "vue";
export default {
  setup() {
    const mapStore = inject("mapStore");
    const { state } = mapStore;

    const headleClick = () => {
      state.count++;
    };

    return {
      ...toRefs(state),
      headleClick,
    };
  },
};
</script>
<template>
  <div class="content">
    <h1>count: {{ count }}</h1>
    <button @click="headleClick">add</button>
    <button>remove</button>
  </div>
</template>

你 click 它 log 会跳出说这个资料它是唯独的,不可以修改

mike vue

这是因为我们在 store/index.js 里面透过readonly回传的,所以我们不能这样修改,readonly的好处就是要避免开发者直接修改资料,所以我们应该另外去定义可以修改的资料的 function,让资料归资料,修改交给其他方法去做。

我们在 store/index.is 上面新增了add 跟 remove 的两个 functino

import { reactive, readonly } from "vue";

const state = reactive({
  count: 0,
});

const addCount = () => {
  state.count++;
};

const removeCount = () => {
  state.count--;
};

export default {
  state: readonly(state),
  addCount,
  removeCount,
};

然後把 function 取出来就可以挂在我们的 button,就可以成功的修改资料

<script>
import { inject, toRefs } from "vue";
export default {
  setup() {
    const mapStore = inject("mapStore");
    const { state, addCount, removeCount } = mapStore;
    return {
      ...toRefs(state),
      addCount,
      removeCount,
    };
  },
};
</script>
<template>
  <div class="content">
    <h1>count: {{ count }}</h1>
    <button @click="addCount">add</button>
    <button @click="removeCount">remove</button>
  </div>
</template>

vue mike

透过这样的方式我们就可以不需要透过 Vuex 的方式来共用资料,你可能会问说如果要像 actions 一样处理非同步的 API 要怎麽办 ?

一样我们可以把非同步给放到store/index.js里面,这边我就写一个大概的示范

const fetchUserData = async () => {
  try {
    const res = await axios.get("https://www.api.com/api/user");
    const { name, age, address } = res.data;
    state.name = name;
    state.age = age;
    state.address = address;
  } catch (error) {
      state.errorMessage = "This user is not found!";
  }
};

这边其实我们就把 store/index.js 给当成 Vuex 使用那样来使用,有 state、actions/mutations,如果你想要有getter 的功能的话也可以使用 computed 来替代达成。

import { computed, reactive, readonly } from "vue";

const userInfo = computed(()=> { 
  return `Info: ${state.name},  ${state.age},  ${state.address}`
})

export default {  
    state: readonly(state),  
    addCount,  
    removeCount,  
    userInfo
};

这也是很多人在说为什麽使用 Vue3 的时候可能不需要在使用 Vuex 的原因,因为我们就可以利用Provide / inject来达成使用Vuex管理资料的作法。

我自己的使用下来归纳几个想法

  1. Provide / inject 的使用法很适合用在小专案或是小区块的功能开发上使用。
  2. 在大型专案的时候还是主要会以Vuex作为管理状态的主选择,Provide / inject为辅助。
  3. Vuex之中有许多功能是Provide / inject 没有的功能(详请可以参考 https://next.vuex.vuejs.org/ )

最後杀鸡焉用牛刀,我们不管用哪个都没有对错,就只有适不适合而已,今天在这边介绍了如何使用 Provide / inject,下一篇我们来谈谈 Vue3 使用 Vuex 的一些进阶的用法。

Mike Vue

那如果对於Vue3不够熟的话呢?

Ps. 购买的时候请登入或注册该平台的会员,然後再使用下面连结进入网站点击「立即购课」,这样才可以让我获得更多的课程分润,还可以帮助我完成更多丰富的内容给各位。

我有开设了一堂专门针对Vue3从零开始教学的课程,如果你觉得不错的话,可以购买我课程来学习
https://hiskio.com/bundles/9WwPNYRpz?s=tc

那如果对於JS基础不熟的朋友,我也有开设JS的入门课程,可以参考这个课程
https://hiskio.com/bundles/b9Rovqy7z?s=tc

订阅Mike的频道享受精彩的教学与分享

Mike 的 Youtube 频道
Mike的medium
MIke 的官方 line 帐号,好友搜寻 @mike_cheng


<<:  [Day5]Count on Cantor

>>:  Day 3 : Git 回推版本

#28 Click! Serve! Desktop

昨天已经把大部分的 GUI 弄完了,之前也已经写好了服务器的程序,今天我们把两边拼起来吧! 搬移 s...

[前端暴龙机,Vue2.x 进化 Vue3 ] Day25. Vue3 Composition API 使用(一)

在 Composition API 当中我们可以将响应式资料和相关业务逻辑结合到一起,是因为 Vue...

D 30 Python x Django 学习心得

从一开始接触Django到现在也一个月了 来简述跟总结一下自己认知到的技能 Django 网址传进来...

解决Windows总是把Typescript文件夹看成影音档

最近用Typescript开发专案,小弟用的是Windows10笔电 遇到一个小烦恼,那就是每次打开...

运行系统档案检查工具以修复Windows 10中损坏的档案

您所使用的Windows 10是否经常崩溃?您是否在Windows 10中收到档案丢失或损坏的错误?...