使用Vue.js组成API的首页滚动商店

在这个系列中,我们已经探索了各种官方和DIY的商店解决方案。在这个系列的最后,我们将看一下使用Vue 3的组成API的家庭卷的解决方案。

在我们继续之前,如果你还没有读过本系列的前几篇文章,我鼓励你回去至少看一下文章1.什么是商店2.Vuex, the Official Vue.js Store,因为我们在这篇文章中不会过多地讨论原理,而是专注于实现。

安装和设置

与Vuex或Pinea不同的是,组合API与Vue 3一起发货,因此没有外部软件包需要安装和设置。如果你使用Vue 2,你可以安装composition-api Vue插件

商店的定义

用composition API定义一个商店,可以有很多不同的方法。由于它是一个家庭卷的解决方案,它真的由你来决定你想要的具体结构,但重要的部分是你的状态被传递给reactive 方法,这样你的状态是反应性的。事实上,reactive 是Vue 3中重命名的等同于Vue.observable 的方法,以避免与RxJS的观察变量相混淆。

作为reactive 的替代品,你可以使用ref ,但在这种情况下我更喜欢reactiveref 通常用于原始值,如字符串或布尔,而reactive 用于对象。由于我们已经习惯于把状态看作是一个单一的实体,所以在一个状态对象下引用存储空间中的所有状态是很好的,因此我更喜欢reactive 。使用reactive 也意味着我在引用任何一个状态时不必使用.value ,这对我来说感觉更直观。

现在,我们可以只导出一个匿名函数,返回一个空对象。稍后,我们将具体说明我们从模块中导出哪些状态和动作。

// store/loggedInUser.js
import {reactive} from 'vue'
// state
const state = reactive({})
export default ()=>({})
复制代码

然后,在组件中访问商店,现在就像从商店中导入并调用导出的函数一样简单。

//AppComponent.vue
<script>
import useLoggedInUser from "./store/loggedInUser.js";
export default{
  setup(){
    const loggedInUser = useLoggedInUser()
  }
}
</script>
复制代码

状态

现在我们已经定义了我们的商店,但它有点悲哀,因为它没有跟上任何状态。没有状态,商店就什么都不是,所以让我们添加一些。我们可以通过向传递给reactive 的对象添加属性来做到这一点。就这样,我们的状态就被反应出来了!

// store/loggedInUser.js
import {reactive} from 'vue'
const state = reactive({
  name: 'John Doe',
  email: '[email protected]', 
  username: 'jd123',
})
复制代码

现在我们将把我们想从loggedInUser 存储器中暴露的状态传递给被输出的对象。我们可以用一个状态属性来做这件事。

// store/loggedInUser.js
export default ()=>({
  state // shorthand for state:state
})
复制代码

然而,这种模式虽然简洁,但有两个缺点。

  1. 这意味着我们只能在消费组件的状态属性下访问状态。
  2. 它不能防止状态直接从消费组件中被改变。

相反,我们可以为我们想公开的状态中的每个属性创建计算属性。

// store/loggedInUser.js
import {reactive, computed} from 'vue'

const state = reactive({
  name: 'John Doe',
  email: '[email protected]', 
  username: 'jd123',
})

export default () => ({
  name: computed(()=> state.name),
  email: computed(()=> state.email), 
  username: computed(()=> state.username),
  posts: computed(()=> state.posts),
})
复制代码

这种方法比较啰嗦,但它有3个好处。

  1. 它允许我们直接访问消费组件中的状态,而不需要嵌套到状态属性中。
  2. 它可以防止消费组件直接改变状态。
  3. 它甚至允许我们选择要暴露的状态。

另外,如果你不关心选择暴露哪些状态,你可以创建一个辅助函数,循环浏览你的所有状态,并自动从它们中创建计算属性。

// helper.js
import {computed} from 'vue'
export const withState =  (target, state)=>{
  Object.keys(state).forEach(prop =>{
    target[prop] = computed(()=> state[prop])
  })
  return target
}
复制代码
// store/loggedInUser.js
import {withState} from '../helper'

const state = reactive({
  name: 'John Doe',
  email: '[email protected]', 
  username: 'jd123',
})

export default () => withState({}, state)
复制代码

最后,我们可以在从setup函数返回loggedInUser后访问模板中的状态。

// AppComponent.vue
<template>
  <h1>Hello, my name is {{name}}</h1>
</template>
<script>
import loggedInUser from "@/store/loggedInUser";
export default{
  setup(){
    const loggedInUser = useLoggedInUser()
    return loggedInUser
  }
}
</script>
复制代码

Learn Vue.js 3 With Vue School

我们甚至可以在从setup函数返回loggedInUser时对它使用spread operator,如果我们需要向模板暴露任何其他变量的话。你可能会对在这里使用传播操作符犹豫不决,因为我们使用reactive 方法来定义状态。为什么呢?因为传播一个reactive 对象会破坏它的反应性。然而,注意到我们不是在传播状态,而是在传播从loggedInUser.js 模块返回的对象,其中状态被定义为计算道具。这意味着我们可以传播我们想要的一切

const somethingElse: ref('')
return {...loggedInUse, somethingElse}
复制代码

获取器

组合API不仅可以处理我们的反应式状态,而且我们还可以将其与组合API的computed 函数结合起来,轻松创建获取器。


// store/loggedInUser.js
const state = reactive({
  //...
  posts: ['post 1', 'post 2', 'post 3', 'post 4']
})

// getters
const postsCount = computed(() => state.posts.length)
复制代码

在定义了计算道具后,我们可以通过将其添加到导出的对象中,从商店中公开它。

// store/loggedInUser.js
export default () => withState({
  postsCount
}, state)
复制代码

由于我们已经从setup函数中返回了loggedInUser,我们现在可以在模板中直接访问postCount。

// AppComponent.vue
<template>
  <h1>Hello, my name is {{name}}</h1>
  <p>I have {{ postsCount }} posts available.</p>
</template>
<script>
import loggedInUser from "@/store/loggedInUser";
export default{
  setup(){
    const loggedInUser = useLoggedInUser()
    return loggedInUser
  }
}
</script>
复制代码

行动

有了组合API,你当然会想定义动作来改变你的商店的状态。你可以采取很多方法来定义动作,但重要的是你要提供这些动作,以便为修改商店的状态提供一个可控的接口。

有了组合API,在定义动作时绝对没有新的概念需要学习。它们只是函数。它们不期望有任何特殊的参数,可以定义任何需要的参数,以直观的界面做出一个动作。另外,访问状态就像引用状态变量一样简单。

// store/loggedInUser.js
const state = reactive({
  //...
  posts: ['post 1', 'post 2', 'post 3', 'post 4']
})
//...
const insertPost = (post) => state.posts.push(post)
复制代码

为了暴露动作,我们只需要把它添加到导出的对象中。

// store/loggedInUser.js
export default () => withState({
  //..
  insertPost
}, state)
复制代码

然后在你的组件中,动作可以作为loggedInUser 上的一个方法,或者直接在模板中使用,因为我们已经从设置方法中返回了loggedInUser

// AppComponent.vue
<template>
  <!-- ... -->
  <input v-model="post" type="text" />
  <button @click="insertPost(post)">Save</button>
</template>
<script>
import loggedInUser from "@/store/loggedInUser";
export default{
  setup(){
    const loggedInUser = useLoggedInUser()
    const post = ref('')
    return {...loggedInUser, post}
  }
}
</script>
复制代码

仅仅阅读上面的代码,你可能感觉不到有什么不同,但当真正把组合API存储解决方案付诸实践时,动作感觉更自然,因为它们只是普通的Javascript函数。不需要调度或提交任何东西。

在模块中进行组织

按照目前的方向,我们可以像Pinia那样对待我们的商店:默认为模块化。我们可以为每个商店或模块创建新的特定域文件,只在需要时导入它们。另外,你也可以为你的商店做一个单一的入口点,并像Vuex那样对待它,但我不认为这样做有什么真正的好处,因此我们不会在本文中进一步探讨。

Vue开发工具

就devtools而言,组成API的方法并没有提供任何特殊的解决方案。完全没有可用的时间旅行,也没有专门的面板来查看你的商店的状态。然而,尽管如此,你仍然可以在Vue DevTools的 “setup “下查看组件的状态,只要你从setup函数中返回整个东西。作为奖励,你甚至可以看到有哪些操作可以使用。

vue devtools screenshot

值得注意的功能

为了总结作为商店解决方案的组合API,让我们快速回顾一下它最值得注意的特点,以帮助你决定实施最适合你和你的项目的商店。

  • 不需要额外的库
  • 可在Vue 3或Vue 2中使用composition api插件
  • 可以根据自己的喜好进行定制
  • 提供了学习更多关于商店的机会,并推动了商店的边界。
  • 不需要对突变进行处理
  • IDE对动作的支持,因为它们只是普通的Javascript。

从另一个角度看,由于Vue组合API是家庭式的,你使用它的缺点基本上和使用Vue.observable (见本系列最后一篇文章)是一样的。然而,由于Composition API对官方解决方案的影响,如Pinea,甚至是即将推出的Vuex版本,你至少要尝试一下,这对你自己也是一种帮助。

总结

Composition API是一个强大的工具,不仅可以在组件之间完整地共享逻辑,甚至可以成为共享状态的强大解决方案。如果你想了解更多关于使用Composition API推出自己的商店的信息,Filip Rakowski也写了一篇关于这个主题的优秀文章

关于Vue.js中商店解决方案的系列文章到此结束。如果你错过了任何文章,请随时回过头来阅读它们

Learn Vue.js 3 With Vue School

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享