基於fis3的組件可視化道路

来源:http://www.cnblogs.com/chuaWeb/archive/2016/09/23/component_preview.html
-Advertisement-
Play Games

首先說明一下,即使不熟悉fis3,閱讀文本應該也會有所收穫。 本文以fis-parser-imweb-tplv2插件為模板插件,目的不在於使用哪個模板,而是組件可視化的實現思路,不必擔心。 先說說模板插件 首先說明一下,我們的項目使用的fis3自帶的mod.js做模塊化開發。 fis-parser- ...


  首先說明一下,即使不熟悉fis3,閱讀文本應該也會有所收穫。

  本文以fis-parser-imweb-tplv2插件為模板插件,目的不在於使用哪個模板,而是組件可視化的實現思路,不必擔心。

 

先說說模板插件

  首先說明一下,我們的項目使用的fis3自帶的mod.js做模塊化開發。

  fis-parser-imweb-tplv2插件是同事在imweb待著的時候寫的。模板使用和jsp寫法一致,文件類型為tpl類型

<div class="tips">
    <em>
      <i class="triangle"></i>
      <a class="close" href="javascript:void(0)" title="關閉"></a>
    </em>
    <strong><%=content%></strong>
    <a href=<%=linkUrl%> title=<%=linkTxt%> target="<%=target%>"> <%=linkTxt%> ></a>
</div>

  實現源碼也比較簡單易懂。fis3的配置

    .match(/\/(.+)\.tpl$/, { // js 模版一律用 .tpl 作為尾碼
        isMod: true,
        rExt: 'js',
        id: '$1.tpl',
        url: '$0.tpl',
        moduleId: '$1.tpl',
        release: '$0.tpl', // 發佈的後的文件名,避免和同目錄下的 js 衝突
        parser: fis.plugin('imweb-tplv2')
    })

  最終生成的模塊化的.tpl.js文件如下

define('common/module/rightsideBar/rightsideBar.tpl', function(require, exports, module) {

  return  function (it, opt) {
      it = it || {};
      with(it) {
          var _$out_= [];
          _$out_.push('<div class="right-sidebar"><a href="javascript:void(0)" class="right-sidebar-unregistered-close"></a><a id="rsidereg" href="/register.html?channel=pcgwzc3" class="right-sidebar-unregistered" style="visibility:', (isLogin ?'hidden' :'visible'), '"><span class="right-sidebar-unregistered-content"></span></a><a href="http://wpa.b.qq.com/cgi/wpa.php?ln=2&amp;uin=4008225538" class="right-sidebar-item right-sidebar-item3" target="_blank"><span class="right-sidebar-item1-content"></span></a><a href="javascript:void(0)" class="right-sidebar-item right-sidebar-item2"><span class="right-sidebar-item2-content"></span><div class="right-sidebar-item-tip--code"> <img src="', '/static/pc-dev/common/module/topBar/top_wx.png', '"> <i class="right-sidebar-item-tip--code-tri"><i></i></i></div></a><a href="javascript:void(0)" class="right-sidebar-item right-sidebar-item3"><span class="right-sidebar-item3-content"></span><div class="right-sidebar-item-tip--code"> <img src="', '/static/pc-dev/common/module/topBar/top_app.png', '"> <i class="right-sidebar-item-tip--code-tri"><i></i></i></div></a><a href="javascript:void(0)" id="rjsq" class="right-sidebar-item right-sidebar-item4" target="_blank"><span class="right-sidebar-item4-content"></span></a><a href="javascript:window.scrollTo(0,0)" class="right-sidebar-item right-sidebar-item5" style="display: none;"><span class="right-sidebar-item5-content"></span></a></div>');
        return _$out_.join('');
      }
  }

});

  fis3打包前和打包後的文件結構更改

  

  使用也比較簡單

var tpl_rightsideBar = require('rightsideBar.tpl');
tpl_rightsideBar(opt);//opt是需要傳遞進去的對象。詳細查看rightsideBar.tpl.js的源碼對應的it參數

  當然,我們封裝一個組件不可能直接去使用這個tpl。而是提供一個外部組件函數,然後傳遞參數到這個組件函數中,組件函數不僅渲染頁面(即插入組件dom),還處理相關的邏輯。如上面的rightsideBar.js就是為外部提供一個組件函數。rightsideBar組件比較簡單,只需要傳遞一個父節點即可,不需要其他外部數據來做處理。部分源碼

/**
 * @author chua
 * @date 2016-5-9
 * @description 首頁右側導航欄組件,依賴模版 rightsideBar.tpl,rightsideBar.scss;

 * @實例化:rightsideBar = new rightsideBar(dom, datas);
 * @param dom {Dom} 為頭部組件父級節點,將根據情況append模版,生成頭部節點;
 * @param datas {json} 初始化組件的數據,數據格式如下
 */

/*
 * @require './rightsideBar.scss';
 */
var tpl_rightsideBar = require('./rightsideBar.tpl');
function rightsideBar(cont,opt) {
    this.cont = $(cont);
    this.opt = opt;
    this.init();
};
rightsideBar.prototype.renderHTML = function() {
    //渲染前處理...
    this.cont.empty().append(tpl_rightsideBar(this.opt));
};
rightsideBar.prototype.bindEvent = function() {
    //綁定事件
    this.cont.on('click', '.xxx', function() {
        //處理內容...
    });
};
rightsideBar.prototype.init = function() {
    this.renderHTML();
    this.bindEvent();
}
return rightsideBar;

  rightsideBar.js會被當做模塊來編譯(我們在fis-conf.js中配置的),最終編譯出來後外面會被define包裹。

define('common/module/rightsideBar/rightsideBar', function(require, exports, module) {
    //代碼正文
    ...

  rightsideBar.prototype.init = function() {
      this.renderHTML();
      this.bindEvent();
  }
  
  return rightsideBar;

});

   使用方法也比較簡單

       html:
       <div class="js-rightsideBar"></div>
  
      js:
      var rightsideBar = require('/common/module/rightsideBar/rightsideBar.js');
      new rightsideBar($('.js-rightsideBar'));

 

  而我們的所有組件都放在一個組件文件夾module中,每一個組件一個文件夾(當然頁可以是多個類似的組件放在一起,公用資源),文件夾下麵放置所有這個組件相關的資源。如上圖rightsideBar這個組件。

  那麼如何做組件可視化?有幾個過程是必須要做的

  1.需要遍歷module中所有的組件(即遍歷每一個組件下麵的.js文件,一個組件文件夾下有多個js文件,表明這個組件文件夾下有多個組件,只是為了公用組件資源才放在了同一個組件下),提取出其中的使用樣例(從註釋代碼中提取,所以必須要規定這段demo代碼的規則)。

  2.必須在每一個組件的註釋中寫demo代碼。

  3.將提取的demo代碼寫入指定的組件可視化文件中,隨fis編譯成目標文件(最終在網上打開這個文件就能預覽各個組件的demo了)。

  在上面的基礎上,改如何實現組價的可視化?

 

第一階段的組件可視化

  第一階段的組件可視化使用node+fis的方式實現。原理是在fis編譯之前使用node執行一個腳本,這個腳本完成遍歷組件、提取demo代碼、生成組件可視化目標文件。然後在使用fis編譯打包,啟動服務後在網上訪問即可。之所以第一階段這麼做的原因有兩點:鄙人比較熟悉node但是對fis插件編寫不太熟悉,不敢確定使用fis插件方式是否可行;其次上頭希望能在短期內能看到一定效果。先看一下工程結構目錄,v_components文件夾里包含了所有用來生成組件可視化文件的工具文件,node執行腳本為index.js

  

  工程源文件點擊這裡

  實現步驟:

  1.規定文件註釋代碼中"@example"和"@example end"之間的字元串被認為是組件demo代碼。

/**
   * @author chua
   * @date 2016-5-9
   * @description 首頁右側導航欄組件,依賴模版 rightsideBar.tpl,rightsideBar.scss;基於jQuery和jquery.cookie.js
  
   * @實例化:rightsideBar = new rightsideBar(dom, datas);
   * @param dom {Dom} 為頭部組件父級節點,將根據情況append模版,生成頭部節點;
   * @param datas {json} 初始化組件的數據,數據格式如下
  
   *
   * @example
       html:
       <div class="js-rightsideBar"></div>
  
      js:
       var rightsideBar = require('/common/module/rightsideBar/rightsideBar.js');
      new rightsideBar($('.js-rightsideBar'));
  
      @example end
   */

   其中html:後面跟著的是html代碼,js:後面跟著的是js執行代碼。註意不要出現不符合代碼格式的字元,"html:"、"js:"分別為html代碼段和js代碼段開始的標誌。其後的代碼分別要嚴格按照html和js的格式要求書寫

  

  2.為了簡化和配置更加靈活。我添加了config.json和wrap.html兩個文件來配合主文件index.js文件。

  

  其中index.js文件作用是作為node腳本運行,最終生成最新的v_components.html。v_components.css和v_components.js是給v_component.html使用的,畢竟組件可視化需要一些展示和交互

  config.json的作用是希望能夠配置組件的一些屬性,讓其更靈活,移植性更加。目前只支持一個配置:組件的目錄 (建議使用相對路徑,否則在index.js中可能找不到)

{
    "modulePath": "../common/module/"
}

  warp.html是用來生成v_components.html的模板文件。裡面包含了v_components.html文件需要的樣式文件和腳本文件。這個文件也是根據實際情況配置

<!DOCTYPE html>
<html>
<head>
    <title>組件可視化</title>
    <link rel="import" href="/common/html/meta.html?__inline">
    <link rel="stylesheet" type="text/css" href="/common/css/common.scss" />
    <link rel="stylesheet" type="text/css" href="v_components.css" />
    <script type="text/javascript" src="/common/dep/mod.js" data-loader></script>
    <script type="text/javascript" src="/common/dep/jquery.js" data-loader></script>
    <script type="text/javascript" src="/common/js/common.js" data-loader></script>
    <script type="text/javascript" src="v_components.js" data-loader></script>
</head>
<body>    
</body>
</html>

 

  3.主程式index.js讀取配置文件config.json中配置的組件目錄遍歷每一個組件(裡面的js文件),提取註釋中的demo代碼,以wrap.html為模板,將html代碼插入為節點,將js腳本插入到script節點中。

var fs = require('fs');
...
var writerStream = fs.createWriteStream('v_components.html');
var regs = {
    'wraphtml': /^([\s\S]*<(body)>[\s\S]*)(<\/\2>[\s\S]*)$/,
    //懶惰匹配到第一個*/
    'example': /\/\*\*([\s\S]*)@example[\s\S]*?html:([\s\S]*?)js:([\s\S]*?)@example end([\s|\S]*?)\*\//,//懶惰匹配第一個*/
    ...
};

fs.readFile('config.json', function(err, data){
    ...
    root = obj.modulePath;
    if(datas){
        loopModule();
    }    
})
fs.readFile('wrap.html', function(err, data){                        
    ...
    datas = data.toString();
    if(root){
        loopModule();
    }    
});
//遍歷所有模塊文件
function loopModule(){
    fs.readdir(root, function(err, ffiles){
        ...
        //遍歷所有組件
        ffiles.forEach(function(ffile){
            ...
            fs.readdir(root + ffile, function (err, files){
                ...
                files.forEach(function(file){//處理每一個模塊文件中的模塊並找到組件主文件(js文件)
                    if(regs.jsfile.test(file)){
                        ...
                        fs.readFile(pa, function(err, data){
                            ...
                            if(match = data.toString().match(regs.example)){//匹配demo代碼段
                                //截取相關信息並添加相關節點
                                ...
                                innerRightT += '<div class="right-side-top-item' + (count == 0? ' visible': '') + '" data-mod="'+ file +'">' + match[2] + '</div>';
                                innerJs += match[3] + ";";//js腳本都放在innerJs中
                                count++;
                            }
                            
                            if(subModLeng == 0){//處理完所有子模塊才能說明處理完了整個模塊文件夾
                                unDomoduleLength--;
                            }                            
                            if(unDomoduleLength == 0){//處理完所有的模塊後,最後寫入文件
                                var innerBody = warpText(innerLeftWrap, innerLeft) 
                                    + warpText(innerRightWrap, warpText(innerRightTWrap, innerRightT) + warpText(innerRightBWrap, innerRightB))
                                    + warpText(jsWrap, innerJs);

                                //使用utf8編碼寫入數據
                                writerStream.write(datas.replace(regs.wraphtml, '$1' + innerBody + '$3'), 'UTF8');
                                //標記文件結尾
                                writerStream.end();
                            }                                
                        });
                    }
                    
                })
            });
        })
    })
}
//用數組wrapArr包裹inner並返回包裹結果
function warpText(wrapArr, inner){...}
//將str字元串轉換成HTML格式
function transToHtml(str){...}
View Code

  最終在v_components目錄下執行:node index

  生成v_components.html文件

  在pc目錄下fis3編譯:fis3 release dev

  在和pc同級的目錄下麵生成pc-dev文件夾

  在pc-dev目錄下執行:node server

  打開瀏覽器訪問:http://localhost:3000/v_components/v_components.html

  

  左側是所有的組件的列表,點擊之能看到每一個組件的展示效果。我很醜,但是我很有內涵

 

第二階段的組件可視化——fis3插件

  之前說過要將組件可視化做成fis插件,隨fis編譯一起編譯打包,不用在手動執行node生成相應文件。主要面臨的問題是:如何讓組件更加通用?這裡面幾個需要考慮的點

  1.如何從組件代碼的註釋中提取出demo代碼段。

  這裡我使用了下麵的正則來匹配

/\/\*\*([\s\S]*)@example[\s\S]*?(html:([\s\S]*?)js:([\s\S]*?))@example end([\s|\S]*?)\*\//,//懶惰匹配第一個*/

  匹配文件註釋中‘@example’和‘@example end’之間的代碼。如

/**
   * @example
       html:
       <div class="js-rightsideBar"></div>
  
      js:
       var rightsideBar = require('/common/module/rightsideBar/rightsideBar.js');
      new rightsideBar($('.js-rightsideBar'));
  
      @example end
   */

  這個部分本來想做成可以配置的,但是覺得沒有太大的意義,就預設使用這個正則來匹配組件中的demo代碼。

 

  2.插件需要那些參數,是的插件更加靈活

  下麵是最終確定下來的參數(路徑都配置絕對路徑)

            wrap: '/v_components/wrap.html',//組件可視化原型文件,用來包裹組件可視化代碼
            url: '/v_components.html', //目標文件
            COMPath: '/common/module',//組件集合目錄
            moduleListInstead: 'instead of modules',//使用模塊列表節點替換當前文本
            moduleViewInstead: 'instead of view htmls',//使用模塊視圖列表節點替換當前文本
            moduleCommentsInstead: 'instead of commnets',//使用模塊註釋列表節點替換當前文本
            moduleJsInstead: 'instead of js'//使用js腳本節點替換當前文本

  當前文件的目錄結構

  

  其中wrap對應的文件wrap.html作用非常重要。後面四個參數moduleXXXInstead對應的值必須在wrap.html中能夠找到,然後使用插件拼裝好的數據來替換他。wrap.html源碼如下

<!DOCTYPE html>
<html>
<head>
    <title>組件可視化</title>
    ...
</head>
<body>    
<div class="left-side">instead of modules</div>
<div class="right-side">
    <div class="right-side-top">instead of view htmls</div>
    <div class="right-side-bottom">instead of commnets</div>
</div>
...
<script type="text/javascript">instead of js</script>
</body>
</html>

   最終執行fis後上面黑色粗體文字全部被替換

<!DOCTYPE html>
<html>
<head>
    <title>組件可視化</title>
    ...
</head>
<body>    
<div class="left-side"><div data-mod="financialsBar">financialsBar</div><div data-mod="financialsSmlBar">financialsSmlBar</div><div data-mod="rightsideBar">rightsideBar</div></div>
<div class="right-side">
    <div class="right-side-top"><div data-mod="financialsBar">
      <!-- 新手 -->
      <div class="js-financialsBar1"></div>
      <!-- 活期 -->
      <div class="js-financialsBar2"></div>
      <!-- 定期 -->
      <div class="js-financialsBar3"></div>
  
      </div><div data-mod="financialsSmlBar">
      <!-- 定期理財 -->
      <div class="js-financialsSmlBar1"></div>
      <!-- 債權轉讓 -->
      <div class="js-financialsSmlBar2"></div>
      </div><div data-mod="rightsideBar">
       <div class="js-rightsideBar"></div>
  
      </div></div>
    <div class="right-side-bottom"><div data-mod="financialsBar"><div >樣例:<div>html:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;!--&nbsp;新手&nbsp;--&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;div&nbsp;class="js-financialsBar1"&gt;&lt;/div&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;!--&nbsp;活期&nbsp;--&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;div&nbsp;class="js-financialsBar2"&gt;&lt;/div......</div>
</div>
...
<script type="text/javascript">var financialsBar = require('common/module/financialsBar/financialsBar');
      new financialsBar($('.js-financialsBar1'), {
          "type": "novice",
          "bdid": 1,
          "name": "農優寶A1231213",
          "title": "新手專享",
          "term": 1,
          "annualrate": 0.09,
          "interestRaise": 0.005,
          "surplusAmount": 30000,
          "projectScale": 50000,
          "status": "RZZ",
          "packStatus": "QGZ",
          "releaseDate": 425132121,
          "nowDate": 45641231321,
          "surplusBuyCount":1,
          'productType': 'NYB',
          'delayTime': 0
      });
      ...
    </script>
</body>
</html>
View Code

   來自相同的組件的DOM節點的屬性值data-mod相同

  fis-conf.js的配置片段

    .match('::package', {
        prepackager: fis.plugin('component-preview',{
            wrap: '/v_components/wrap.html',//組件可視化原型文件,用來包裹組件可視化代碼
            url: '/v_components.html', //目標文件
            COMPath: '/common/module',//組件集合目錄
            moduleListInstead: 'instead of modules',//使用模塊列表節點替換當前文本
            moduleViewInstead: 'instead of view htmls',//使用模塊視圖列表節點替換當前文本
            moduleCommentsInstead: 'instead of commnets',//使用模塊註釋列表節點替換當前文本
            moduleJsInstead: 'instead of js'//使用js腳本節點替換當前文本
        })
    })

  更詳細的demo查看fis3_component_preview_demo

  最終效果同第一階段的組件可視化效果一樣     

 

  實現原理:

  

  完整的插件源碼查看fis3-prepackager-component-preview

   

  如果覺得本文不錯,請點擊右下方【推薦】!


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

-Advertisement-
Play Games
更多相關文章
  • 效果圖: 網頁佈局 <p>操作成功</p> <strong>5</strong><span>秒後回到主頁</span><a href="javascript:history.back();">返回</a> 任務: 1.打開網頁後,如果不做任何操作則返回到一個新的頁面 var num=document ...
  • 效果圖: 任務: 1. 滑鼠移到不同行上時背景色改為色值為 #f2f2f2,移開滑鼠時則恢復為原背景色 #fff var tr=document.getElementsByTagName("tr"); for(var i= 0;i<tr.length;i++) { bgcChange(tr[i]); ...
  • 一、html5新特性 常用語義標簽:nav footer header section mark 功能標簽 video audio iframe canvas(畫布和繪圖功能) input新type:url/number/range/Date(date, month, week, time等)/se ...
  • 這是要實現的效果圖: 一.HTML頁面佈局 <!-- HTML頁面佈局 --><ul class="tab_menu"> <li class="selected">房產</li> <li>家居</li> <li>二手房</li></ul><div class="tab_box"> <div> 275 ...
  • <!DOCTYPE>到底是個神馬? 前兩天和朋友談到<!DOCTYPE>,今天將網上學習到的資料在這裡整理一下 因為HTML5的風靡人們慢慢的弱化了對<!DOCTYPE>的理解,但是現在還是有很多的面試官在問這個問題,所以還是有必要對其瞭解一下,以便以後有些許談資。 1.<!DOCTYPE>有神馬作 ...
  • [1]原理介紹 [2]數字加減 [3]元素尺寸 [4]內容滾動 ...
  • 這兩天整個技術圈都炸鍋了,微信小程式(微信應用號)發佈內測,首批200家收到邀請,但是沒受邀請的同學,也不用擔心,下麵介紹一下解決方法。 首先需要下載ide,昨天只需要下載0.9版本的編輯器並替換文件就行了,但是可能微信那邊修複了,導致不可用。現在我們要準備兩個版本:0.7盒0.9的版本,我測試過了 ...
  • [1]原理簡介 [2]範圍圈定 [3]大小改變 [4]代碼優化 ...
一周排行
    -Advertisement-
    Play Games
  • 前言 本文介紹一款使用 C# 與 WPF 開發的音頻播放器,其界面簡潔大方,操作體驗流暢。該播放器支持多種音頻格式(如 MP4、WMA、OGG、FLAC 等),並具備標記、實時歌詞顯示等功能。 另外,還支持換膚及多語言(中英文)切換。核心音頻處理採用 FFmpeg 組件,獲得了廣泛認可,目前 Git ...
  • OAuth2.0授權驗證-gitee授權碼模式 本文主要介紹如何筆者自己是如何使用gitee提供的OAuth2.0協議完成授權驗證並登錄到自己的系統,完整模式如圖 1、創建應用 打開gitee個人中心->第三方應用->創建應用 創建應用後在我的應用界面,查看已創建應用的Client ID和Clien ...
  • 解決了這個問題:《winForm下,fastReport.net 從.net framework 升級到.net5遇到的錯誤“Operation is not supported on this platform.”》 本文內容轉載自:https://www.fcnsoft.com/Home/Sho ...
  • 國內文章 WPF 從裸 Win 32 的 WM_Pointer 消息獲取觸摸點繪製筆跡 https://www.cnblogs.com/lindexi/p/18390983 本文將告訴大家如何在 WPF 裡面,接收裸 Win 32 的 WM_Pointer 消息,從消息裡面獲取觸摸點信息,使用觸摸點 ...
  • 前言 給大家推薦一個專為新零售快消行業打造了一套高效的進銷存管理系統。 系統不僅具備強大的庫存管理功能,還集成了高性能的輕量級 POS 解決方案,確保頁面載入速度極快,提供良好的用戶體驗。 項目介紹 Dorisoy.POS 是一款基於 .NET 7 和 Angular 4 開發的新零售快消進銷存管理 ...
  • ABP CLI常用的代碼分享 一、確保環境配置正確 安裝.NET CLI: ABP CLI是基於.NET Core或.NET 5/6/7等更高版本構建的,因此首先需要在你的開發環境中安裝.NET CLI。這可以通過訪問Microsoft官網下載並安裝相應版本的.NET SDK來實現。 安裝ABP ...
  • 問題 問題是這樣的:第三方的webapi,需要先調用登陸介面獲取Cookie,訪問其它介面時攜帶Cookie信息。 但使用HttpClient類調用登陸介面,返回的Headers中沒有找到Cookie信息。 分析 首先,使用Postman測試該登陸介面,正常返回Cookie信息,說明是HttpCli ...
  • 國內文章 關於.NET在中國為什麼工資低的分析 https://www.cnblogs.com/thinkingmore/p/18406244 .NET在中國開發者的薪資偏低,主要因市場需求、技術棧選擇和企業文化等因素所致。歷史上,.NET曾因微軟的閉源策略發展受限,儘管後來推出了跨平臺的.NET ...
  • 在WPF開發應用中,動畫不僅可以引起用戶的註意與興趣,而且還使軟體更加便於使用。前面幾篇文章講解了畫筆(Brush),形狀(Shape),幾何圖形(Geometry),變換(Transform)等相關內容,今天繼續講解動畫相關內容和知識點,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 什麼是委托? 委托可以說是把一個方法代入另一個方法執行,相當於指向函數的指針;事件就相當於保存委托的數組; 1.實例化委托的方式: 方式1:通過new創建實例: public delegate void ShowDelegate(); 或者 public delegate string ShowDe ...