Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vuex 带来全新的编程体验 #689

Open
libin1991 opened this issue Jan 15, 2019 · 0 comments
Open

Vuex 带来全新的编程体验 #689

libin1991 opened this issue Jan 15, 2019 · 0 comments
Labels

Comments

@libin1991
Copy link
Owner

如果在简历上写“XX电商系统”的实现,其实第一直觉是这个人一定是从培训班出来的。而我们“项目管理”课程正好就是做一个小型电商网站。开发时长一个月左右,包含买家端、卖家端、管理员端,虽然业务逻辑比较常见,但是这次开发仍有收获,最重要的一点收获就是 对Vuex有了真正的实践和认识。 所以,本文大部分介绍Vuex在该项目上的实践以及所踩的坑,另外的部分则是项目中一些其他要点的总结。

过去那臃肿的Vue组件

前面的几次项目开发,拿到后端接口和需求之后,会按照以下的方式去实现功能:

  1. 划分出 Components 和 Pages,尽量保证一些组件能够复用。
  2. 抽象出部分可以复用的 SCSS 代码以及设计通用的 style class。
  3. 基于某个 HTTP 库(如 axios)配置并封装一层底层方法,并基于该方法之上书写具体的API 函数。
  4. 在页面和组件内调用上述封装的方法,在函数回调或Promisethen方法里进行数据更新或者做更多的业务处理。

好像没什么问题哈?

第1、2、3步基本上是没太大问题的,问题就出在第4步,其结果是导致一个Vue文件越来越大,到后面一个复杂的页面vue文件将会包含如下内容:

  • data里大量的数据字段,有控制是否展示元素的渲染用数据用于"上锁"的变量等。
  • createdmounted里包含了大量的请求回调处理
  • template里使用的组件的属性内,绑定了大量的props

最后的结果就是vue文件变得臃肿。

文件越长,可读难度会提高,可维护性会降低。

谁也不喜欢看一个包含了逻辑处理、界面处理、数据处理的冗杂代码文件。

所以,为了解决上述问题,引入 Vuex 是非常必要的。

其实,之前也一直在使用Vuex,不过用的目的仅仅是为了“保存一点数据能够全局使用”而已,根本没有深入使用ActionGettersMutations等。 本文关于Vuex不会过多介绍其原理以及好处,主要关注实践以及使用心得,Vuex介绍或入门请移步官方文档

干净、解耦合、职责分明

笔者所做的,是将关于数据请求和更新,基本都放到了Vuex里,让Vuex成为了整个项目的“数据集散中心”,而vue组件里,仅仅是进行事件调度(dispatch)绑定来自Vuex的数据

简单的例子

我们以商品详情页面举例,其包含如下的功能:

  • 第一次进入页面根据URL参数中的id请求商品数据。
  • 根据商品的属性选择更新库存和价格。
  • 添加购物车并更新小红点。
  • 下单。
  • 收藏商品。

如图所示:

-w1270

如果我们不使用Vuex,代码的结构可能如下:

<template>

<template>

<script>
export default {
data() {
detail: {
name: '',
shopId: '',
id: '',
pic: '',
description: '',
price: '',
updateTime: '',
categoryId: '',
stock: 0,
createTime: '',
attributeList: {},
collectId: 0,
},
// 一大堆商品的字段数据 【MARK】
},
created(){
this.$http.get('/product/:id')
.then()
// 以及一端更新其他数据的请求 【MARK】
},
methods(){
clickCart(){
this.$http.get('xxx')
.then('xxx')
this.$emit('xxx'); // 与上层组件通信更新购物车的红点
}
// 一堆用户事件的触发方法,每个方法内都是一堆http请求和回调处理 【MARK】
},
computed:{
// 一些基于商品数据判断状态的数据 【MARK】
}
}
</script>

<style>
<!-- 忽略相关样式代码-->
<style>
复制代码

如果按照上述的方式来写,虽然可以保证关于这个页面的逻辑处理都保留在这个vue文件内,但是却显得十分臃肿,如果后续要加更多的功能抑或是修改,其需要读的代码可能更多。

所以,以上代码示例中的被【MARK】标记的部分,将是Vuex可以优化的。 最后的代码如下图所示:

代码的可读性提高了不少:

  • 该文件里看不到任何的http请求,也看不到任何请求路径。(请求路径一般是静态的,直接写HTTP请求路径相当于写死了数据的来源,对于后期的更改数据来源非常不友好)
  • data里不含任何业务数据字段,仅保留控制页面的字段
  • 代码结构变得简单,只有【触发事件】-【回调】这样简单的逻辑。

关于数据处理的代码,全部封装到了Vuex上了:

我们在Vuex层,不仅请求了数据,同时对返回的数据做了判断以及不同的逻辑处理,同时设置Getters使的页面能够基于Getters提供的数据做各类二元判断

Vuex层带来了如下的好处:

  • 在不同的页面可以触发相同的Actions,就这个例子,我只要写了一次加入购物车的函数,在各个页面都可以使用了。
  • Actions之间可以组合使用,通过组合的方式,我们可以更加轻易的实现复杂的业务逻辑,比如添加商品进购物车的同时更新商品数据并更新购物车数据,这样需要写三次HTTP请求的业务逻辑,此时只需要将三个Actions进行组合即可,而这几个Actions还可以在其他地方单独使用。
  • 直观,一眼看过去,就知道商品模型上有哪些动作,对于 New Guy 熟悉业务逻辑更加方便,也方便开发者检查自己的代码或者函数是否考虑周全。(BTW,在一堆HTML里找逻辑是真的难受)
  • 减少组件间通信次数,一个中心化的数据仓库带来的好处就是减少了数据层层传递的操作,按照以前的编码习惯,我会大量使用$emitprops来实现父子或兄弟组件通信,而现在,只需要在组件中触发相应的事件绑定相应的数据即可。

全新的业务编写体验

直观体验来说,彻底使用Vuex后,编码思路清晰不少,因为写完整体的一个功能需要2~3天,而Vuex对于“间断性编程”非常有帮助,能够迅速捡起上一次开发的进度。

使用Vuex之后就是按照如下的步骤进行开发了:

(第1、2、3步不变,如前文) 4.构建Vuex的Store模型,定义所需的方法。 5.编写组件并绑定在Vuex上的相关数据和方法。 6.编写触发dispatch后的回调。

笔者认为使用Vuex几个重要的原则

使用Vuex存在一定的规范,在官网上已经陈列了诸如表单处理测试项目结构的规范,而下面的一些原则仅仅是笔者的一些经验之谈,不一定正确,有所帮助便是最好的。

【不处理页面跳转】:页面跳转在dispatch的回调里进行处理。

最初我的设计是在Action请求数据之后同时也负责所有的跳转逻辑弹窗逻辑,不过,这样做又让视图层数据层耦合了,使得我抽象出来的Action的复用性变得极差,同时,在vue文件看到关于视图层的处理才算是比较正常的编写逻辑。

【单一职责原则】:mutationaction尽量保持只做一件事原则。

正如上一条原则中提到的要提高action的复用性,那么就要保证需要复用的action只做一个功能,如更新A数据更新B数据不要直接融合成一个action,而是分别定义两个action,并通过组合的方式来实现一个更复杂的功能,有点类似精简指令集的思想。

如:

actions:{
    updateA(){}, // A 可能在其他地方被单独使用
    updateB(){},  // B 可能在其他地方被单独使用
    updateAandB({dispatch}){
        dispatch("updateA");
        dispatch("updateB");
    }, // 同时触发 A B 的复杂action
}
复制代码

【使用Promise和AA】(Async & Await)

为了让dispatch函数拥有Promise风格的处理回调的能力,可以让action的返回值作为一个Promise对象,如:

// store.js
actions:{
    updateA(){
        return Promise.resolve()
    }
}
复制代码

在组件内就可以使用.then

...
created(){
    this.$store.dispatch('updateA')
        .then(() => {
            ...
        })
}
...
复制代码

Vuex一般建议在actions进行异步操作,所以为了让代码更加优雅,可以用AA如下编写:

// store.js
actions:{
    async updateA(){
        const result = await ajax('/**/**');
        if(result){
            return Promise.resolve()
        }
    }
}
复制代码

所以,在实践中的代码如图:

【使用namespace】,为了你自己好。

一般使用vuex,会根据实体或者页面来拆分state形成module,一个module里包含其所需的所有actions,states,mutation等。如电商里,一般有商品订单用户这几个实体可以拆成module,拆分出来之后如图:

拆分的好处是更加独立直观了,坏处是如果不使用命名空间,可能造成actions,mutations冲突,正好Vuex也提供namespace的配置,参考官方文档即可。

这次使用的不足

未使用namespace

虽然总结到了原则里,不过这次并没有使用namespace,算是这次比较大的不足了,解决的办法是”手动namespace“,如图:

actions的方法名前缀加上该实体名用于区分不同的方法,实在是比较拙劣的手段了。

部分代码仍然有重复书写的情况

情况发生在获取到异步数据之后的代码,一般如下这段代码会大量重复:

if (result.code){
    return Promise.resolve(data);
}
return Promise.reject(msg);
复制代码

虽然不算多,但是这逻辑仍然有可抽象出来的可能性,下次打算在RootState里添加一个action,用于处理这样的后端返回数据,期望结果如下:

actions: {
    parseResult({}, result){
      if (result.code){
        return Promise.resolve(data);
      }
        return Promise.reject(msg);
    },
    async getA({dispatch}){
        const result = await http.get('xx/xx');
        return dispatch('parseResult', result); // One line , it's clean
    }
}
复制代码

其他收获

拥抱 Vue CLI 3

3.0 基本在配置上做到极简化了。很久没开发Vue的项目了,记得上一个Vue项目里,仍然有一大堆xxx.congfig.js,而现在,基本集成到了CLI内部了,通过一个vue.config.js可满足大部分开发需求。本项目配置如下图:

比以前的配置更加直观了,更多请参考:cli.vuejs.org/zh/,至于插件机制,本次开发未用上,只是在安装第三方包的时候可以用vue add了,有GUI可以查看安装了哪些插件。

Vue.use 和 Vue.install使用的地方汇总到plugins目录下

本项目是一个多页面应用,总共有3个Vue实例,所以希望能够按需注入想要的第三方依赖如axios,qrcode,lazyload等一些工具库或第三方组件。于是都将其汇总到plugins目录下了,如下图:

-w848

-w842

这样封装的好处是,我可以对每个Vue实例按需导入第三方依赖,一行import就可以,如下图:

-w448

开始考虑优化了

一个大型的web app 打包压缩后都不超过500kb,所以就以此作为Benchmark来优化该项目打包后的大小,按需引入第三方依赖(特别是Elementlodash)减少了不少项目体积。不过最主要的还是得学会用webpack analyse tool来分析。

结论

总之,本次项目主要是刷新了对Vuex的认知,其能够让我们编写的vue组件可读性和可维护性更高。同时,也能够让数据更新这块的代码复用性更高。通过Vuex来构建一个中心化的数据集散中心还能让开发的思路更顺畅,值得学习。

参考:

Vuex 是什么?

@libin1991 libin1991 added the Vuex label Jan 15, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant