在做后台管理系统的同学们,是否有用easyui的经历。虽然现在都是vue、ng、react的时代。但easyui(也就是jquery为基础)还是占有一席之地的。因为他对后端开发者太友好了,太熟悉不过了。要让一个后端开发者来理解vue或者是react的VNode、状态器、组件等,都是有那么一点点的为难(反正我转型时,对这些都是很有困惑的)。今天我想试着解决这样一个问题,如:将knockout 与 大家熟悉的easyui结合在一起。让easyui具有MVVM的能力,也有不使用easyui的特性,看大家是否喜欢这一口。

一、项目介绍说明

项目语言:typescript

项目地址:https://gitee.com/ko-plugins/koeasyui

初级效果:

望大家给予评论和支持。

二、如何将easyui转换为ko的组件

再前几年用ko的时候,由于他没有组件的支持(因为当时没有组件的概念)。至到react、vue提出和引用了组件的概念,以及将此概念深入到每个前端开发者的内心后。ko也提供了组件的支持。2017年看这个新特性的时候,就让我有改造easyui的冲突。当时苦于对ko和easyui的理解不深入,硬是没有找到突破口。今天终于让我找到。

2.1 easyui组件如何注册到为ko组件

ko提供了components.register方法,用于注册一个组件。此方法接受一个字符串的名称,以及一个对象(至少包含一个template或viewModel属性),其中viewModel可以是一个对象,也就是一个function。本人就利用了可以为function这一点。根据easyui的组件名动态创建一个function,然后赋值给viewModel,代码片段如下:

let plugins = this.easyui.plugins;
//动态生成一个function的类
plugins.forEach(pluginName => {
let defaults = this.jquery.fn[pluginName].defaults;
let methods = this.jquery.fn[pluginName].methods;
if(defaults){
//options必须要是独立的,事件(放原型上),方法可以原型链上的
let props = Object.getOwnPropertyNames(defaults);
//方法
let methodKeys = Object.getOwnPropertyNames(methods);
this.option.ko.components.register(`ko-${pluginName}`,{
template: '<div></div>',
viewModel: EasyuiHelper.createEasyui(props, methodKeys)
});
}
});

2.2 easyui组件的配置和方法怎么变成ko组件的参数和方法

上一步骤中的EasyuiHelper.createEasyui方法,就是实现对easyui组件的创建,以及参数的响应和方法的绑定,算是本插件的核心。

export  class EasyuiHelper{
static createEasyui(props:Array<string>, methods):any{
let tmpClass = class {
public $dom:JQuery;
public name:string;
constructor(params, componentConfig){
this.name = componentConfig.element.nodeName.toLowerCase().replace('ko-', '');
this.$dom = $(componentConfig.element).find('div');
//绑定方法,方法还需要继承组件支持的方法的绑定
Object.getOwnPropertyNames(methods).forEach(index=>{
if(!$.isNumeric(index)) return true;
let methodName = methods[index];
this[methodName] = ()=>{
let args = Array.prototype.slice.call(arguments);
args.unshift(methodName);
return this.$dom[this.name].apply( this.$dom, args);
};
});
}
/**
* 根据参数创建组件的配置对象
* @param options 配置参数
*/
private createOptions(options){
let opt = null;
if(options){
opt = Object.create({});
Object.getOwnPropertyNames(options).forEach(optKey=>{
let tmpOpt = options[optKey];
if(props.indexOf(optKey) > 0 && ko.isObservable(tmpOpt) ){
opt[optKey] = ko.unwrap(tmpOpt);
}else{
opt[optKey] = tmpOpt;
}
});
}
return opt;
}
/**
* 绘制组件
* @param options
*/
public paint(options:any){
let opt = this.createOptions(options);
this.$dom[this.name](opt);
}
/**
* 重组件
* @param options 配置项
* @param $dom dom元素对象
*/
public repaint(options:any){
let $parent = this.$dom.parent();
this.$dom[this.name]('destroy');
let $dom = $('<div></div>');
let opts = this.createOptions(options); $parent.append($dom);
$dom[this.name](opts);
this.$dom = $dom;
}
};
return tmpClass;
}
/**
* 根据dom获取上下文
* @param dom dom节点
*/
static getContextFor(dom:HTMLElement){
return ko.contextFor(dom);
}
}

代码量不多,其主要思路就是,动态创建一个类(其实js中的类就是function)。构造函数中获取到dom,以及组件名称。然后将easyui的方法绑定到类实例上。然后对外提供paint和repaint两个方法进行组件的绘制和重绘。但这个时候又出现了另一个问题,什么时候进行绘制重绘呢?

2.3 配置参数改变后,如何即使反馈给easyui

这一步就是解决绘制和重绘的问题。这里我们要了解一个ko的loader的概念,他相当于是组件渲染器向外提供的勾子,可以自定义一些内容。ko的loader提供了如下四个勾子:

getConfig:获取组件配置信息

loadComponent:加载组件时的勾子,这里我们可以使用利用require的异步组件加载什么

loadTemplate:加载模板,当然你的通过ajax向后端接口获取模板信息

loadViewModel:加载组件视图对象(这是我们要重写的方法),通过此处的重写,让组件渲染器创建我们指定的类。并执行执行的绘制或者是重绘方法。

export class EasyuiLoader{
public factory:IGenerate;
constructor(factory: IGenerate){
this.factory = factory;
}
getConfig(name:any, callback:any){
callback(null);
}
loadComponent(name:any, componentConfig:any, callback:any){
callback(null);
}
loadTemplate(name:any, templateConfig:any, callback:any){
//这里做一些视图不显示的控制,在渲染数据后,进行视频的展示
callback(null);
}
loadViewModel(name:any, viewModelConfig:any, callback:any){
//到这里,视图都是已经呈现好的
//这里要产生两个生命周期:渲染数据前、渲染数据后,以及一个视图重绘的事件
var nViewModelConfig = (params, componentConfig) => {
let vm = new viewModelConfig(params, componentConfig);
let name;
vm = this.factory.generate(name, params, vm);
return vm;
}
callback(nViewModelConfig);
}
}

以下是factory.generate的源码:

generate(componentName: string, params: any, viewModel: any):any {
let first = true;
viewModel.paint(params.options || {});
//监听params的变化变化
ko.computed(function(){
let opts = params.options;
let changeOpts = new Array<any>();
let reflows = new Array<any>(); //可以通过方法来进行配置改变的参数
Object.getOwnPropertyNames(opts).forEach(key => {
let param = opts[key];
let tmp = ko.unwrap(param);
//探测监控对象有变化的属性,区分那些可以用方法进行改变,那些需要重绘
if(ko.isObservable(param) && param.hasChanged()){
changeOpts.push(param);
if(relation[viewModel.name] && relation[viewModel.name][key]){
reflows.push({
val: tmp,
methodName: relation[viewModel.name][key]
});
}
}
}); if(first){ //如果是初始化执行,后面的业务不用重复执行了
first = false;
return;
}
if(changeOpts.length>0){
if(changeOpts.length == reflows.length){//说明配置的改变,可能通过方法操作完成
Object.getOwnPropertyNames(reflows).forEach(key=>{
let item = reflows[key];
viewModel.$dom[viewModel.name](item.methodName, item.val);
});
}else{
//引起了组件重绘
viewModel.repaint(opts);
}
}
}); return viewModel;
}

1. 进入此方法,首先我们进行组件的绘制(也就是创建)

2. 然后通过ko.computed方法监听params中的options(配置参数)的改变,然后进行组件重绘或者是部分改变(这里我叫他回流reflow)。

3. 由于ko.computed在初始化的时候会执行,所以通过first变量进行问题的回避。

三、还需要完善的点

1. 现在动态生成的koeasyui组件提供的方法只是easyui组件本身的,而没有对其继承的方法进行合并

2. repaint和reflow需要更细致的区分,让组件性能达到最优。

knockout + easyui = koeasyui的更多相关文章

  1. EasyUI+Knockout实现经典表单的查看、编辑

    此文章是基于 1. 搭建SpringMVC+Spring+Hibernate平台 2. 自制xml实现SQL动态参数配置 3. 利用DetachedCriteria构建HQL参数动态匹配 4. 常用日 ...

  2. 我的权限系统设计实现MVC4 + WebAPI + EasyUI + Knockout(一)

    一.前言 之前的博客一直都还没写到框架的实现及权限系统,今天开始写我的权限系统,我以前做过的项目基本上都有权限管理这个模块,但各个系统都会有一些不太一样,有些简单点,有些稍微复杂一点,一句话,我们做的 ...

  3. 建筑材料系统 ASP.NET MVC4.0 + WebAPI + EasyUI + Knockout 的架构设计开发

    框架介绍: 1.基于 ASP.NET MVC4.0 + WebAPI + EasyUI + Knockout 的架构设计开发 2.采用MVC的框架模式,具有耦合性低.重用性高.生命周期成本低.可维护性 ...

  4. 我的权限系统设计实现MVC4 + WebAPI + EasyUI + Knockout(四)授权代码维护

    一.前言 权限系统设计中,授权代码是用来控制数据访问权限的.授权代码说白了只是一树型结构的数据,没有什么其它的业务意义.那么这个页面的功能也就非常简单授权代码维护:新增.修改.删除授权代码数据. 二. ...

  5. 我的权限系统设计实现MVC4 + WebAPI + EasyUI + Knockout(三)图形化机构树

    一.前言 组织机构是国内管理系统中很重要的一个概念,以前我们基本都是采用数据列表的形式展现,最多只是采用树形列表展现.虽然够用,但是如果能做成图形化当然是最好不过了.这里我不用任何图形控件,就用最原始 ...

  6. 我的权限系统设计实现MVC4 + WebAPI + EasyUI + Knockout(二)菜单导航

    一.前言 上篇博客中已经总体的说了一下权限系统的思路和表结构设计,那接下来我们就要进入正文了,先从菜单导航这个功能开始. 二.实现 这个页面基本不用什么需求分析了,大家都很明白,不过在这个页面要多维护 ...

  7. SNF快速开发平台3.0之BS页面展示和九大优点-部分页面显示效果-Asp.net+MVC4.0+WebAPI+EasyUI+Knockout

    一)经过多年的实践不断优化.精心维护.运行稳定.功能完善: 能经得起不同实施策略下客户的折腾,能满足各种情况下客户的复杂需求. 二)编码实现简单易懂.符合设计模式等理念: 上手快,见效快.方便维护,能 ...

  8. SNF快速开发平台3.0之-界面个性化配置+10种皮肤+7种菜单-Asp.net+MVC4.0+WebAPI+EasyUI+Knockout

    一.个性配置-首页:可以进行拖动保存配置,下次登录时就会按配置的进行加载 二.个人配置页面 7种菜单用户可自定义配置,和预览效果 10种皮肤自定义配置,和预览效果 皮肤和菜单可以随意组合-部分截图: ...

  9. 一个简单的knockout.js 和easyui的绑定

    <!DOCTYPE html><html><head><meta charset="UTF-8"><title>Basi ...

随机推荐

  1. 【57】android图片印刻,阳刻,素描图效果处理

    介绍我参与开发的妙趣剪纸app使用的图片处理相关的技术 关于妙趣剪纸,各大android商店都可以下载,下面贴出小米商店的链接 妙趣剪纸下载 软件效果截图 如何实现上面的图片处理效果呢 1.初始化高斯 ...

  2. Linux set命令参数及用法详解

    linux  set 命令 功能说明:设置shell. 语 法:set [+-abCdefhHklmnpPtuvx] 补充说明:用set 命令可以设置各种shell选项或者列 出shell变量.单个选 ...

  3. ffdshow 源代码分析 4: 位图覆盖滤镜(滤镜部分Filter)

    ===================================================== ffdshow源代码分析系列文章列表: ffdshow 源代码分析 1: 整体结构 ffds ...

  4. Linux - /etc/passwd和/etc/shadow文件结构

    /etc/passwd文件结构 1.账号名称:         就是账号啦!用来对应 UID 的.例如 root 的 UID 对应就是 0 (第三字段):     2.口令:         早期 U ...

  5. Android Hal层简要分析

    Android Hal层简要分析 Android Hal层(即 Hardware Abstraction Layer)是Google开发的Android系统里上层应用对底层硬件操作屏蔽的一个软件层次, ...

  6. obj-c属性的新的特性

    在以前的objc中我们必须在接口中定义属性对应的实例方法,然后在实现文件中"同步"该属性,如下代码: @interface Foo:NSObject{ NSString *name ...

  7. AOP的相关概念

  8. 你不知道你不懂javascript

    过去几年我注意到技术圈一个很奇怪的现象,有太多程序员将那些他们只是有过非常浅显的了解, 但其实根本就不懂的技术写到他们的简历中,这个现象几乎每种语言都有,但这其中最严重的就要数javascript了. ...

  9. JVM组成

    java内存组成介绍:堆(Heap)和非堆(Non-heap)内存 按照官方的说法:“Java 虚拟机具有一个堆,堆是运行时数据区域,所有类实例和数组的内存均从此处分配.堆是在 Java 虚拟机启动时 ...

  10. DELETE_FAILED_INTERNAL_ERROR Error while Installing APK

    真是Android2.3的特殊版本问题,问题原因是android2.3的instant run的测试版安装方式有所特别,解决办法有2: 1.手动adb install 安装包 2.把Instant r ...