G买卖H5是运行在多端的游戏交易平台。基于产品层面的功能升级以及提升开发效率的需求,前段时间我用Vue和Webpack对项目进行了一次渐进式的重构。所谓渐进式,即每一个周期仅对部分页面进行改造,不影响其余业务的开展。此次我改造的是我买到的/我卖出的订单列表以及订单详情。html
这次分享主要有如下几个点:前端
此次重构用到的核心技术是vue2.0和webpack1.0。接下来对它们进行简要的介绍。vue
Vue.js是一套构建用户界面的渐进式框架。 与其余重量级框架不一样的是,Vue 采用自底向上增量开发的设计。 Vue 的核心库只关注视图层,而且很是容易学习。具体能够从官网学习它的用法。webpack
Webpack是当下最热门的前端资源模块化管理和打包工具,它能够将不少松散的模块按照依赖和规则打包成符合生产环境部署的前端资源,还能够将按需加载的模块进行代码分割,等到实际须要的时候再异步加载。web
下图是官方对Webpack的简介,经过这幅图看出,相互依赖的模块文件,会被打包成一个或多个js文件,能够减小HTTP的请求次数。ajax
这次重构用到的Vue版本是2.1.0,Webpack是1.13.2。vuex
目录结构:缓存
gmmh5
|-- build //构建目录
| |-- build.js
| |-- dev-client.js
| |-- dev-server.js
| |-- HashedModuleIdsPlugin.js
| |-- utils.js
| |-- webpack.base.conf.js
| |-- webpack.dev.conf.js
| |-- webpack.prod.conf.js
|-- config //配置文件
|-- dist //打包后的文件
|-- src //源码目录
| |--assets // 静态资源
| |--biz // 页面配置文件
| |--common // 公共方法
| |--components // 基础组件
| |--constants
| |--modules // 业务组件
| |--pages // 页面
| |--utils // 工具函数
复制代码
组件化的好处不一一列举了,职责单一,易维护,可扩展...bash
首先我将这两张页面分别按功能和展示划分了组件。app
以下图的订单列表页面,页面从上到下按照功能能够划分为三个组件:选择商品类型,切换交易状态,以及渲染列表数据的组件
关于组件间的通信,父组件的数据经过prop下发到子组件中,子组件经过vue的$on, $emit 事件接口来与父组件通讯。兄弟组件或者层级比较深的组件,在目前没有采用vuex的状况下,使用EventBus进行通信。具体操做方法以下:
首先命名一个event-bus.js,建立一个新的全局Vue实例,命名为EventBus而且导出该对象。
import Vue from 'vue'
var EventBus = new Vue()
export default EventBus
复制代码
在组件A中同时引入Vue和EventBus。当这个组件中的方法“emitMethod”被调用时,它触发了事件”EVENT_NAME”,而且传递了“payload”参数。
import Vue from 'vue';
import EventBus from 'event-bus'
Vue.component('component-a', {
...
methods: {
emitMethod () {
EventBus.$emit('EVENT_NAME', payLoad);
}
}
});
复制代码
在另一个组件B中,咱们能够注册一个监听事件,来监听由EventBus传递来的事件“EVENT_NAME”。
import Vue from 'vue';
import EventBus from './event-bus';
Vue.component(‘component-b’, {
...
mounted () {
EventBus.$on('EVENT_NAME', function (payLoad) {
...
});
}
});
复制代码
这样B组件就能够监听A组件触发的事件,而不须要考虑过多的层级问题。
处理订单详情复杂的部分在于它的页面展现取决于商品类型与交易状态值。
G买卖H5页面有如下几种商品类型,其中帐号还分为手游,端游,外部寄售帐号。点券也包含撮合点券,普通点券的类型。实际处理的商品类型比下图复杂。
const account = {
'1': {
'title': '待付款',
'topImg': imgState.wait,
'topMsg': topMsgSource.fromFixTxtByFunc,
'btns': [btn.cancel, btn.pay]
},
'2': {
'title': '付款成功',
'topImg': imgState.trade,
'topMsg': topMsgSource.fromFixTxtByFunc,
'btns': [btn.refund, btn.selectCheckType]
},
...
}
复制代码
account表明商品类型中的帐号类型。'1', '2'则是状态值。
取值时在页面中调用商品详情的接口,获取到数据detail,取出detail中的goods_type和state_to_out,即商品类型和商品交易状态值。而后在配置config文件文件中取得对应的数据。譬如,取状态说明的标题title:
const vm = this
let goods_type = vm.detail.goods_type
let state_to_out = vm.detail.state_to_out
title = vm.config[goods_type][state_to_out]["title"]
复制代码
这样的好处是一目了然,要进行任何的改动在配置表里改动便可。
原先的代码,存在的状况是:一份代码在多个地方复制,改动时常常不知道哪里改了哪里漏了。我将许多重复的代码梳理一遍,抽象封装重复的代码逻辑。
譬如操做按钮中的不少函数,调用了ajax以后都会刷新页面,因而我将其抽出来单独写成一个函数:
operateRefresh(url, params) {
//通用方法 -- ajax后刷新页面
const vm = this
utils.get(url, params, (res) => {
let data = res.data
if (res.return_code === 0) {
vm.refresh()
} else {
utils.toast(res.return_message)
}
})
}
复制代码
单一职责就是一个对象(方法)只作一件事。 若是一个方法承担了过多职责,那么一个职责发生变化可能会影响其余职责的实现,修改代码就变得比较危险。
这次改造我将一些大的函数按功能细分红一些小的功能函数。譬如列表页在初始时要作的事情包括:判断买单仍是卖单,获取初始化的商品类型,调取接口获取列表数据以及监听其余组件的事件:
created() {
const vm = this
// 判断用户是买家仍是卖家
vm.judgeRole()
// 从url获取goods_type以及trade_mode
vm.getUrlParams()
// 获取列表数据
vm.getList()
// 监听事件
vm.subscribeToEvent()
}
复制代码
重构过程当中遇到的坑更可能是因为对业务的不够充分理解上,这回用Vue2.0来重构也遇到了一些Vue的坑。
因为 Vue 会在初始化实例时对属性执行 getter/setter 转化过程,因此属性必须在 data 对象上存在才能让 Vue 转换它,才能检测到它的变化。Vue是不能检测到属性的添加或删除的。在部分业务中我对数据属性进行添加,以及修改,却没有引起Vue的从新渲染,在这个点上卡住了一些时间。后来阅读了官方文档,发现它可使用 Vue.set(object, key, value) 方法将响应属性添加到嵌套的对象上,从而触发组件的更新:
Vue.set(vm.someObject, 'b', 2)
复制代码
另外,因为Vue 异步执行 DOM 更新。只要观察到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的全部数据改变。若是同一个 watcher 被屡次触发,只会一次推入到队列中。若是想要在数据变化以后当即更新DOM,能够在数据变化以后当即使用 Vue.nextTick(callback) 。
Vue.nextTick(function () {
// 数据变化
})
复制代码
业务完成了,我还整理了一些能够持续优化的点:
通过此次的重构,我对业务有了更深刻的了解和掌握,而且认识到在面对复杂的业务时,停下来思考清楚业务场景比动手写代码重要得多。
这次对Vue也有了更深刻的了解和掌握,也提醒了本身在平常开发中须要不断提升编码能力和代码的质量。将来,更多业务整合到重构的新项目中,在其余方面也须要不断地优化和学习。