电商网站项目总结:Vuex 带来全新的编程体验

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

过去那臃肿的Vue组件

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

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

好像没什么问题哈?

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

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

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

文件越长,可读难度会提升,可维护性会下降。web

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

因此,为了解决上述问题,引入 Vuex 是很是必要的。vuex

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

干净、解耦合、职责分明

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

简单的例子

咱们以商品详情页面举例,其包含以下的功能:后端

  • 第一次进入页面根据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以后就是按照以下的步骤进行开发了:

(第一、二、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 是什么?