mvc_in_fe.md
4.06 KB
电商前端最佳实践讨论
目前我们yohobuy的pc、wap站的前端js代码,不同的页面代码结构都不相同,复杂的页面有上千的代码,但是很难去阅读其中的逻辑。所以我们需要一个适合电商前台的最佳实践。
使用MVC
MVC是一种最经典的业务逻辑分离、代码组织的设计规范。那么在我们的前端中,是不是也适合mvc, 那么如果使用mvc来组织我们的代码,哪些是model,哪些是controller,哪些是view呢?
Model
在我们的页面中 js里面的数据一般通过两种方式获得: 一、来自页面直出时,dom上的data-* ; 二、通过ajax返回
对于dom上面的数据,建议放在view中初始化
-
我们是否需要定义 view-object?
在MVVM的一些框架中,他们专门为每个view去定义了对应的object。通过监听vo的值,来实现数据绑定。在我们的页面中,大部分页面中的数据都是直出的,很少有直接改变数据。
-
model层的代码是否独立文件编写?
有必要。部分ajax操作、查询在多个页面中是共用的,比如收藏,搜索等。
-
ajax support promise?
View
我们的页面是html,但是dom对象以及事件应该是属于View。 在View中,我们应该处理事件绑定和dom操作。
职责:
- 每一个View是独立的,View包含子dom的选择,dom事件的监听,暴露自定义事件,提供dom操作的方法给Controller使用
- View只可以操作自己的dom
- 多个View之间的行为事件,通过Controller转发
Controller
连接Model和View。实现页面组件化。
职责:
- 组件初始化入口,初始化多个View、子组件
- 监听多个View的自定义事件,事件传播、处理
- 调用model请求接口
Code
新页面js结构
--js
--module1 // 模块
--page1 // 页面
--index.js // 页面入口(webpack)文件
--controller.js // 控制器逻辑
--view.js // 页面逻辑
...
models.js // 模块下 ajax操作
--model2
...
封装jQuery ajax 支持Promise/+
统一处理异常情况,tracer日志, 跳转登录等逻辑
function http(options) {
let ajax = $.ajax(options);
ajax.then = ajax.done.bind(ajax);
ajax.catch = ajax.fail.bind(ajax);
ajax.finally = ajax.always.bind(ajax);
return ajax;
}
Simple Model
brand-collect.js
module.exports = {
// 收藏品牌
collect(id) {},
// 取消收藏品牌
uncollect(id) {}
};
Simple View & Controller
import {collect, uncollect} from 'model/brand-collect';
const hbs = require('../brand.hbs');
class BrandListBannerView extend View {
constructor() {
super('brand-list-page-head'); // 对外层的dom selector
this.$collectBtn = $('#collect-btn');
this.brandId = this.$collectBtn.data('id');
// 绑定事件委托
this.on('click', 'collect-btn', this.onToggleCollect);
}
render(data) {
let html = hbs(data);
this.$base.append(html);
}
onToggleCollect(e) {
this.emit('collect-toggle', this.view.brandId);
}
success() {
this.$collectBtn.toggleClass('collected');
}
}
class BrandListBannerController extend Controller {
constructor() {
supper(); // the view will be init.
this.view = new BrandListBannerView();
this.view.on('collect-toggle', this.doToggleCollect);
}
doToggleCollect(id) {
collect(id).then(ret => {
this.view.success();
})
}
set(data) {
this.view.render(data);
}
get() {
}
}
module.exports = BrandListBannerController;
Big View & Contoller
// require some component controller
const BrandListBannerController = require('components/brand-list-banner-controller');
let brandListBannerController = new BrandListBannerController();