我是作后端的,看到前端代码组织很难受。javascript
项目业务一多很难维护。更别谈其余项目复用。固然前端开发人员的水平良莠不齐。另外不少项目都有后台管理相似的功能css
功能差很少天然想到复用和封装。html
就算是用了vue,react,dva什么的,感受该乱仍是乱,这些框架什么的,最多只是解决代码级别组件的组织归类,谈到根据业务模块来分,该乱的仍是乱。前端
公司项目比较low,通常都是后台管理系统,有不少的table,上面有查询条件,每一个表都是增删改查各类单记录操做。因此类似的功能不少。
最快速的方案是写完一个模块,复制粘贴修改修改,就成了一个新的模块。这样作最大的弊端是就后期维护成本很是高,基本很难管理,要统一改个东西就各个模块的代码都处理一遍。而后项目人员一多编码风格都没法管理,后面就无zf状态。
虽然有时候会把一些公用的对像或方法放在公用的js文件中,但这远远不够,还有有大量重复的代码。
$extends只能解决对象或者方法级别的东西,碰到须要几个步骤的东西就嗝屁了。vue
复用
和个性化
的问题,不仅仅是使用公用属性 或 $extends,而是使用对象继承与重写,理论上能够重写父类全部的方法。以表格为例。注意代码只是截取一些关键部分的代码,只是为了说明逻辑和细节,有些可能运行不通。java
define(['class-util', 'usage', 'vue', 'ELEMENT'], function (util, usage, Vue, ELEMENT) { Vue.use(ELEMENT); function BaseTable(bean) { var self = this; //功能对象 this.inited = false; this.defers = {}; //子类常量,通常由子类覆盖 this.elm = '#t_datatable';//表格的选择器 this.$elm = null;//表格的jq对象,通常须要加载页面后再赋值 this.$table = null;//表格的jq对象,通常须要加载页面后再赋值 this.url = {//相关的操做的接口 search: '',//分页查询 delete: '', }; this.form;//提交的数据 //操做不一样的数据记录,每次都会变的属性放在opt里。 this.opt = { //类变量 $table: null, $dialog: null, $form: null, vm: null,//主要的vue对象 //bean变量 id: null, bean: null, action: null//操做:add,modify,detail }; //属性 this.page = { detail: null, edit: null }; this.js = { edit: null, detail: null, upload: 'base-upload', audit: 'base-audit', collect: 'base-collect', downloads: 'base-downloads', }; //相关操做显示的窗口标题 this.titles = { 'delete': '您肯定删除该项记录吗?', 'report': '您肯定上报该项记录吗?', 'detail': '详细信息', 'modify': '修改', 'add': '新增', 'audit': '审核', }; //提示信息 this.msg = { report: '上报成功!', delete: '删除成功!', }; //表格操做列按钮的代码,由于经常使用就放在基类里,个性化的状况子类能够覆盖 this.btn = { search: '#b_search', add: '#b_add', collect: '#b_collect', detail: function (id) { return '<a data-id="' + id + '" data-action="detail" class="blue action" title="查看" href="#">\<i class="icon-zoom-in bigger-130" data-row="" data-path="" data-index=""></i>\</a>'; }, delete: function (id) { return '<a data-id="' + id + '" data-action="delete" class="red action" title="删除" href="#">\<i class="icon-trash bigger-130" data-row="" data-path="" data-index=""></i>\</a>'; }, modify: function (id) { return '<a data-id="' + id + '" data-action="modify" class="green action" title="修改" href="#">\<i class="icon-pencil bigger-130" data-row="" data-path="" data-index=""></i>\</a>'; }, }; //Bootstrap Table(或者其余)的统一配置,个性化状况子类覆盖须要修改的属性便可 this.tableOpt = { toolbarAlign: 'false', searchAlign: 'right', buttonsAlign: 'right', sidePagination: 'server',//指定服务器端分页 url: this.baseUrl + this.url.search, method: 'POST', contentType: "application/x-www-form-urlencoded; charset=UTF-8", pagination: true,//是否分页 pageNumber: 1, //初始化加载第一页,默认第一页 pageSize: 10,//单页记录数 // pageList:[5,10,20,30],//分页步进值 queryParams: function (params) { var params2 = self.queryParams.call(self, params); var defParam = { 'access_token': $.cookie('token'), 'limit': params.limit, // 每页要显示的数据条数 'start': params.offset, // 每页显示数据的开始行号 columns: params.sort, isDesc: params.order === 'desc' ? true : false }; return $.extend(defParam, params2); }, responseHandler: function (res) { var respData = { total: 0, rows: [] }; if (res && res.content) { var content = res.content; var total = content.recordsTotal; var rows = content.data; if (total && $.isNumeric(total)) { respData.total = total; } if (rows && $.isArray(rows)) { respData.rows = rows; } } return respData; }, clickToSelect: true,//是否启用点击选中行 striped: true, //是否显示行间隔色 sortable: true, sortOrder: 'desc', columns: [{title: '序号', field: 'p_id'},],//columns通常都是子类覆盖的 }; //汇总表默认选项 }; /**********************原型方法*************************/ //原型方法主要有几个 //1. init、 //2. 渲染操做栏,以下拉选择框、日历选择框,用vue等就更不用说了、 //3. 渲染操做栏按钮,绑定事件什么的 //4. 初始化表格 //初始化,主要的执行入口,传入参数的入口 BaseTable.prototype.init = function (elm, bean, option) { this.$elm = $(elm || this.elm); if (bean) { this.bean = bean; } if (option) { this.opt = option; } this.defers.initToolbar = this.initToolbar(); this.defers.initToolbar = this.initToolbarBtn(); this.defers.initToolbar = this.initTable(this.$elm, this.tableOpt); this.$table = this.initTable(this.$elm, this.tableOpt); }; /** * 初始化操做栏 * 通常子类覆盖 */ BaseTable.prototype.initToolbar = function () { }; /** * 初始化操做栏按钮 * 子类基本不用重写 */ BaseTable.prototype.initToolbarBtn = function () { var self = this; $.when(this.defers).done(function () { //按钮 if (self.btn.search) $(self.btn.search).on("click", null, self, self.search); if (self.btn.add) $(self.btn.add).on("click", null, self, self.edit); if (self.btn.template) $(self.btn.template).on("click", null, self, self.downloadTemplate); if (self.btn.upload) $(self.btn.upload).on("click", null, self, self.upload); if (self.btn.collect) $(self.btn.collect).on("click", null, self, self.collect); if (self.btn.downloads) $(self.btn.downloads).on("click", null, self, self.downloads); }); }; /** * 初始化表格, * 子类基本不用重写 */ BaseTable.prototype.initTable = function (elm, tableOpt) { var self = this; var $elm = $(elm); //初始化datatable tableOpt.$el = $elm;//这样就能够在bootstrap.table实例中访问当前的jq table 对象,方便调用bootstrap.table的方法 var $myTable = $elm.bootstrapTable(tableOpt); //绑定操做列按钮事件 $elm.off('click').on('click', 'tbody .action', $myTable, function (e) { var $btn = $(this); var action = $btn.data('action'); var id = $btn.data('id'); var bean = $elm.bootstrapTable('getRowByUniqueId', id); var actionFun = self.actions[action] || self.confirmAction; if ($.isFunction(actionFun)) { var defer = actionFun(e, id, bean, self, action); //操做完成后执行 if (self.callbacks) { var actionCallback = self.callbacks[action] if (actionCallback) { $.when(defer).done(function (data) { actionCallback.apply(self, arguments); }) } } } }); return $myTable; }; //其余只个重要方法 /** * 查询条件方法 * 通常子类覆盖 * @param d:默认datatable参数 * @returns {*}:提交的data集合 */ BaseTable.prototype.queryParams = function (params) { return {}; }; /** * 删除操做,实现省略,通常都同样,子类不用重写,只要定义好 url.delete给他调就好 */ BaseTable.prototype.delete = function (e, id, bean, self) { }; /** * 显示详情操做 */ BaseTable.prototype.showDetail = function (e, id, bean, self, action) { var title = self.titles[action]; var dialogDefer = usage.tableDialog(title, self.page.detail, action); $.when(dialogDefer).then(function (dialog) { var modulePath = self.js[action]; require([modulePath], function (Detail) { self.Detail = Detail; self.detail = new self.Detail(bean, dialog, self);//新建实例 self.detail.init(); }) }); }; /** * 新增/修改操做(设置form为disable后显示详情) */ BaseTable.prototype.edit = function (e, id, bean, self, action) { if (!self) { self = e.data; } action = action || 'add'; var modulePath = self.js.edit; require([modulePath], function (Edit) { self.Edit = Edit; self.edit = util.getInstance(Edit.context, Edit) self.edit.init({ $table: self, action: action, bean: bean, id: id, }); self.edit.run(); }); }; return BaseTable; });
define(['base-table', 'class-util', 'usage', 'vue', 'ELEMENT', 'component'], function (BaseTable, util, usage, Vue, ELEMENT) { Vue.config.devtools = true; Vue.use(ELEMENT); var _context = {}; function UserTable(bean) { BaseTable.call(this, bean);//继承父类属性 var self = this; this.elm = '#table';//override this.url.search = 'api/user/search'; this.url.delete = 'api/user/delete'; this.js.edit = 'assets/module/user/user-edit'; this.tableOpt.url = this.url.search; this.tableOpt.sortName = 'id', this.tableOpt.sortOrder = 'desc'; this.tableOpt.columns = [ {title: '序号', field: 'id'}, {title: '名称', field: 'username'}, {title: '所属单位', field: 'depart_name'} {title: '角色', field: 'roleNames',}, { title: '状态', field: 'user_enable', formatter: function (value, row, index) { return userStatusMap[value]; } }, {title: '备注', field: 'user_remark',}, { title: '操做', field: this.idField, formatter: function () { return self.renderActionBtn.apply(self, arguments); } }, ]; }; util.beget2(UserTable, BaseTable);//继承父类方法 //原型方法 UserTable.prototype.initToolbar = function () { if (!this.opt.vm) { var vm = this.opt.vm = new Vue({ el: '#toolbar', data: {}, mounted: function () { this.defers.toolbar.resolve(this); }, }); } return this.defers.toolbar.promise(); }; UserTable.prototype.queryParams = function (params) { //中间的内容本身处理 return params; }; return UserTable; });
其中class-util两个方法react
define([], function () { /** * 生孩子函数 beget:龙beget龙,凤beget凤。 * 用于继承中剥离原型中的父类属性 * @param obj * @returns {F} */ function beget(obj) { var F = function () { }; F.prototype = obj; return new F(); } function beget2(Sub, Sup) { var F = function () { }; F.prototype = Sup.prototype; var proto = new F(); proto.constructor = Sub;//继承代码 for (var key in Sub.prototype) {//若是在子类声明了prototype方法以后才调用此继承方法,复制子类方法以覆盖父类方法 proto[key] = Sub.prototype[key]; } Sub.prototype = proto;//继承代码 return Sub; } /** * 获取单例对象 * @param context 存放对象的上下文,用于检测是否已实例化,返回已实例化对象;存放其余对象如模态框的jq对象 * @param clazz 须要new的类 * @returns {*} */ function getInstance(context, clazz) { if (!context.inst) { context.inst = new clazz(); //context.inst.setDialog(context.$dialog); } return context.inst; }; return { beget: beget, beget2: beget2, getInstance: getInstance, }; });