knockout + easyui = koeasyui

来源:https://www.cnblogs.com/cqhaibin/archive/2018/05/20/9064803.html
-Advertisement-
Play Games

在做後臺管理系統的同學們,是否有用easyui的經歷。雖然現在都是vue、ng、react的時代。但easyui(也就是jquery為基礎)還是占有一席之地的。因為他對後端開發者太友好了,太熟悉不過了。要讓一個後端開發者來理解vue或者是react的VNode、狀態器、組件等,都是有那麼一點點的為難 ...


     在做後臺管理系統的同學們,是否有用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需要更細緻的區分,讓組件性能達到最優。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 第一步 刪除工作空間的/.metadata/.plugins/org.eclipse.core.runtime/.settings/com.genuitec.eclipse.ast.deploy.core.prefs文件 第二步 取消項目自動部署 項目-->右鍵Properties-->Builde ...
  • eclipse將java源代碼生成jar可執行文件 用eclipse做了一個web項目的自動化測試,自己用的時候倒是很方便,打開eclipse直接運行即可,但是分享給其他小伙伴用的時候就不太方便,希望可以生成一個可執行的文件,別人使用時,直接運行就可以。實際操作了一下,記錄下步驟。 1.文件一定要有 ...
  • 分析:既然是訂單號/交易流水號,首先是不能重覆,其次需考慮到性能問題。 設計如下: "HF"+時間戳+隨機數+迴圈數 代碼如下: 其中:RandomUtils類 1 package com.test.common.util; 2 3 import org.apache.commons.lang.Ra ...
  • 演示站:c.lmz8.cn打開js/4.js,複製到工具箱的js代碼整理那,先解密,方便查看。工具箱:tool.lmz8.cnjs代碼整理、線上解碼 這個便是文字,只不過唄轉碼了,所以要用到解碼工具。內容更改:index.html(裡面的網址改了,否則會跳轉到我的網站)js/4.js(網址、照片、音 ...
  • 爬蟲基本流程 發起請求 通過HTTP庫向目標伺服器發送Request,Request內可以包含額外的headers信息。 獲取響應內容 如果伺服器正常響應,會返回Response, 裡面包含的就是該頁面的內容。 解析數據 內容或許是HTML,可以用正則表達式、網頁解析庫進行解析。 或許是Json,可 ...
  • http協議 協議:是一種規則或者規定 tcp/ip協議:規則了tcp客戶端與tcp伺服器數據的通訊格式 1.知識點是什麼:http協議 2.知識點有什麼:規定瀏覽器與伺服器(tcp伺服器)之間的數據通訊格式 3.請求的協議格式<客戶端(瀏覽器)發數據給伺服器> GET /index.html HT ...
  • 使用.net core也有一段時間了,一直都沒有Oracle官方的正式版驅動程式,更別說EF版本了。之前基於Oracle官方的.net core預覽版本寫了個Dapper的資料庫操作實現,但是總感覺不太完美,有消息稱Oracle官方的EF版本可能要到第三季度出了,還需要靜靜等待幾個月的時間。 既然有 ...
  • 5章 字元與字元串 1.字元類char的使用 2.轉義字元的使用 3.字元串類string的使用 4.比較字元串 5.格式化字元串 6.截圖,分割字元串 7.插入與填充字元串 8.刪除,複製,替換字元串 9.StringBuilder的使用 6章 流程式控制制語句 1.選擇語句 2.迭代語句 3.跳轉語 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...