漫談Nuclear Web組件化入門篇

来源:http://www.cnblogs.com/iamzhanglei/archive/2016/11/05/6033452.html
-Advertisement-
Play Games

目前來看,團隊內部前端項目已全面實施組件化開發。組件化的好處太多,如:按需載入、可復用、易維護、可擴展、少挖坑、不改組件代碼直接切成伺服器端渲染(如 "Nuclear" 組件化可以做到,大家叫同構)... 怎麼做到這麼強大的優勢,來回憶下以前見過的坑,或者現有項目里的坑。 CSS層疊樣式?保佑不要污 ...


目前來看,團隊內部前端項目已全面實施組件化開發。組件化的好處太多,如:按需載入、可復用、易維護、可擴展、少挖坑、不改組件代碼直接切成伺服器端渲染(如Nuclear組件化可以做到,大家叫同構)...
怎麼做到這麼強大的優勢,來回憶下以前見過的坑,或者現有項目里的坑。

CSS層疊樣式?保佑不要污染別的HTML!

在web前端,一般一個組件必須要有骨架HTML和裝飾的CSS以及JS邏輯。而CSS要是可以是局部作用域那就再好不過了!就不用寫長長的首碼了,浪費帶寬不說,而且費勁。

.ui-popup-arrow-xx-xxxxx-xxxx-container {

}

這回夠長了吧,不會污染別的HTML了吧。真的太長了,沒有辦法,因為CSS不是局部的,怕污染其他的HTML,規劃好長長的namespace、module是以前的最佳實踐。

怎麼優雅綁定事件?只能定義在window下?

如果HTML綁定的事件是局部作用域那就再好不過了!我真的見過模版代碼里出現下麵的代碼:

<div onclick="xxx()"></div>

然後在js里找到了下麵的代碼:

<script>
    window.xxx = function(){
    
    }
</script>

要綁定的事件一多,得污染多少全局變數啊。所以還有的工程師這麼乾:

<div onclick="ns.xxx()"></div>
<div onclick="ns.xxxx()"></div>

然後在js里找到了下麵的代碼:

<script>
    window.ns = {};

    ns.xx = function(){
    
    }
    
    ns.xxx = function(){
    
    }
</script>

這裡貌似比不設定namespace好很多,但是還是妥協的結果。一般希望能封裝成組件,組件的HTML里綁定的事件就是組件內定義的事件,內聚內聚!!
通過js動態綁定事件的壞處我以前專門寫了一篇文章來闡述,主要是lazy bind會導致用戶看到了頁面,但是頁面確無法響應用戶的交互,這裡不再闡述。

需求變更?找不到在哪改代碼?

大型項目如游戲什麼的為啥都是面向對象式的寫法?如果一個組件剛好又能是一個Class那就再好不過,Class base可以更方便地抽象現實世界的物體及其屬性或者邏輯演算法,所以甚至有些編程語言都是面向對象的(這裡逆向邏輯),如JAVA、C#...整體過程式的代碼對於大型項目幾乎沒法維護(如基於jQuery就能容易寫出整體都是過程式的組織結構),整體OO,局部過程式是可以接受的。

組件需要嵌套?只能複製粘貼原組件?

扁平無嵌套組件還是比較簡單,對模板的字元串處理下,把綁定的事件全指向組件自身定義的方法,生命周期也好處理。在真正的業務里經常需要組件嵌套,這樣也更利於復用。雖然大量模板引擎支持引用子模板、共用數據等,但是組件是有生命周期的,模板嵌套不能真正解決組件嵌套的問題。能支持組件嵌套並且聲明式嵌套就那就再好不過了!

數據變了?重新生成HTML替換一下?

怎麼替換?先查找dom?什麼?你還在查找dom?你還在背誦CSS選擇器?替換一下?不能增量更新嗎?或者diff一下吧?不要每次全部替換啊!

首屏太慢?以前抽象的組件沒法復用?

什麼?首屏太慢?改成直出(伺服器渲染)?以前代碼沒法復用?要推翻重寫?什麼?怎麼搞?排期?產品不給排期?需求沒變為什麼要給排期?

下麵來看下Nuclear怎麼解決上面問題。

install Nuclear

npm install alloynuclear

Hello,Nuclear!

var HelloNuclear = Nuclear.create({
    render: function () {
        return '<div>Hello , {{name}} !</div>';
    }
})

new HelloNuclear({ name: "Nuclear" }, "body");

內置了mustache.js無邏輯模板。

事件綁定

var EventDemo = Nuclear.create({
    clickHandler: function (evt, target, other1,other2) {
        //MouseEvent {isTrusted: true, screenX: 51, screenY: 87, clientX: 51, clientY: 21…}
        console.log(evt);
        //<div onclick="Nuclear.instances[0].clickHandler(event,this,'otherParameter1','otherParameter2')">Click Me!</div>
        console.log(target);
        //otherParameter1
        console.log(other1);
        //otherParameter2
        console.log(other2);

        alert("Hello Nuclear!");
    },
    render: function () {
        return '<div onclick="clickHandler(event,this,\'otherParameter1\',\'otherParameter2\')">Click Me!</div>'
    }
})

new EventDemo({ seen: true }, "body");

條件判斷

var ConditionDemo = Nuclear.create({
    render: function () {
        return '{{#seen}}\
                    <div>\
                        you can see me\
                    </div>\
                {{/seen}}\
                {{^seen}}\
                    <div>\
                        yan can not see me\
                    </div>\
                {{/seen}}'
    }
})

var cd = new ConditionDemo({ seen: true }, "body");

setTimeout(function () {
    cd.option.seen = false;
}, 2000);

2秒後改變seen,dom會自動變更。

迴圈

var LoopDemo = Nuclear.create({
    render: function () {
        return '<ul>{{#list}}<li>姓名:{{name}} 年齡:{{age}}</li>{{/list}}</ul>'
    }
})

var ld = new LoopDemo({
    list: [
        { name: "dntzhang", age: 18 },
        { name: "vorshen", age: 17 }
    ]
}, "body");

setTimeout(function () {
    //增加
    ld.option.list.push({ name: "lisi", age: 38 });
}, 1000);

setTimeout(function () {
    //修改
    ld.option.list[0].age = 19;
}, 2000);

setTimeout(function () {
    //移除
    ld.option.list.splice(0, 1);
}, 3000);

Array的變更也能監聽到,能夠自動觸發Dom的變更。

局部CSS

<body>

    <div>I'm other div!! my color is not red!!</div>

    <script src="../dist/nuclear.js"></script>

    <script type="text/javascript">
        var ScopedCSSDemo = Nuclear.create({
            clickHandler: function () {
                alert("my color is red!");
            },
            render: function () {
                return '<div onclick="clickHandler()">my color is red!</div>'
            },
            style: function () {
                return 'div { cursor:pointer; color:red }';
            }
        })
        //第三個參數true代表 增量(increment)到body里,而非替換(replace)body里的
        new ScopedCSSDemo ({ seen: true }, "body" ,true);

    </script>

</body>

組件外的div不會被組件內的CSS污染。

討厭反斜杠?

討厭反斜杠可以使用 ES20XX template literals、或者split to js、css和html文件然後通過構建組裝使用。也可以用template標簽或者textare存放模板。

<template id="myTemplate">
    <style>
        h3 {
            color: red;
        }

        button {
            color: green;
        }
    </style>

    <div>
        <div>
            <h3>TODO</h3>
            <ul>{{#items}}<li>{{.}}</li>{{/items}}</ul>
            <form onsubmit="add(event)">
                <input nc-id="textBox" value="{{inputValue}}" type="text">
                <button>Add #{{items.length}}</button>
            </form>
        </div>
    </div>
</template>

<script>
    var TodoApp = Nuclear.create({
        install: function () {
            this.todoTpl = document.querySelector("#myTemplate").innerHTML;
        },
        add: function (evt) {
            evt.preventDefault();
            this.inputValue = "";
            this.option.items.push(this.textBox.value);
        },
        render: function () {
            return this.todoTpl;
        }
    });

    new TodoApp({ inputValue: "", items: [] }, "body");

</script>

組件嵌套

<script>
    var TodoList = Nuclear.create({
        render: function () {
            return '<ul> {{#items}} <li>{{.}}</li> {{/items}}</ul>';
        }
    });

</script>

<script>
    var TodoTitle = Nuclear.create({
        render: function () {
            return '<h3>{{title}}</h3>';
        }
    });
</script>

<script>

    var TodoApp = Nuclear.create({
        install: function () {
            //pass options to children
            this.childrenOptions = [{ title: "Todo" }, { items: [] }];
            this.length = 0;
        },
        add: function (evt) {
            evt.preventDefault();

            //this.nulcearChildren[1].option.items.push(this.textBox.value);
            //or
            this.list.option.items.push(this.textBox.value);

            this.length = this.list.option.items.length;
            this.textBox.value = "";
        },
        render: function () {
            //or  any_namespace.xx.xxx.TodoList 對應的 nc-constructor="any_namespace.xx.xxx.TodoList"
            return '<div>\
                        <child nc-constructor="TodoTitle"></child>\
                        <child nc-constructor="TodoList"  nc-name="list"></child>\
                        <form onsubmit="add(event)" >\
                          <input nc-id="textBox" value="{{inputValue}}" type="text"  />\
                          <button>Add #'+ this.length + '</button>\
                         </form>\
                   </div>';
        }
    });

    new TodoApp({ inputValue: "" }, "body");
</script>

通過在父對象的install里設置this.childrenOptions來把option傳給子節點。

伺服器端渲染

function todo(Nuclear,server) {
    var Todo = Nuclear.create({
        add: function (evt) {
            evt.preventDefault();
            this.option.items.push(this.textBox.value);
        },
        render: function () {
            return `<div>
                      <h3>TODO</h3>
                      <ul> {{#items}} <li>{{.}}</li> {{/items}}</ul>
                      <form onsubmit="add(event)" >
                          <input nc-id="textBox" type="text"  value="" />
                          <button>Add #{{items.length}}</button>
                      </form>
                    </div>`;
        },
        style: function () {
            return `h3 { color:red; }
                   button{ color:green;}`;
        }
    },{
        server:server
    });
    return Todo;
}

if ( typeof module === "object" && typeof module.exports === "object" ) {
    module.exports =  todo ;
} else {
    this.todo = todo;
}

通過第二個參數server來決定是伺服器端渲染還是客戶端渲染。server使用的代碼也很簡單:

var koa = require('koa');
var serve = require('koa-static');
var router = require('koa-route');
var app = koa();
var jsdom = require('jsdom');
var Nuclear = require("alloynuclear")(jsdom.jsdom().defaultView);

var Todo = require('./component/todo')(Nuclear,true);



app.use(serve(__dirname + '/component'));

app.use(router.get('/todos', function *(){
    var  str = require('fs').readFileSync(__dirname + '/view/index.html', 'utf8');
    var todo = new Todo({ items: ["Nuclear2","koa",'ejs'] });
    this.body = Nuclear.Tpl.render(str, {
        todo:  todo.HTML
    }); 
    Nuclear.destroy(todo);
}));


app.listen(3000);

瀏覽器端使用的代碼:

<!DOCTYPE html>
<html>
<head>
</head>
<body>
 {{{todo}}}

 <script src="./nuclear.js"></script>
 <script src="./todo.js"></script>
 <script>
    new todo(Nuclear)('body');
 </script>
</body>
</html>

這樣,組件的代碼不需要任何變更就可以在server和client同時使用。

Nuclear如何做到同構的?

內置三條管線如下所示:

比如一般前後端分離的開發方式,僅僅會走中間那條管線。而同構的管線如下所示:

這裡前後後端會共用option,所以不僅僅需要直出HTML,option也會一併支持給前端用來二次渲染初始一些東西。

Nuclear優勢

1.節約流量
2.提升用戶體驗
3.載入更加靈活
4.Dom查找幾乎絕跡
5.搭積木一樣寫頁面
6.提升代碼復用性
7.可插拔的模板引擎
8.Lazy CSS首屏更輕鬆
9.Nuclear文件大小6KB (gzip)
10.零行代碼修改無縫切到同構直出
...
...

Nuclear Github

https://github.com/AlloyTeam/Nuclear


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

-Advertisement-
Play Games
更多相關文章
  • 1.1概述 提供一個創建一系列或相互依賴對象的介面,而無須指定他們具體的類。這就是抽象工廠模式的定義。 設計某些系統時可能需要為用戶提供一系列相關的對象,但系統不希望用戶直接使用new運算符實例化這些對象,而是應當由系統來控制這些對象的創建,否則用戶不僅要清楚地知道使用哪些類來創建這些對象,而且還必 ...
  • 一、前言 OGNL和標簽庫的作用,粗暴一點說,就是減少在JSP頁面中出現java代碼,利於維護。 1.1、OGNL 1.1.1、什麼是OGNL? OGNL(Object-Graph Navigation Language):對象圖形導航語言,是一種功能強大的表達式語言,通過簡單的語法,就能夠任意存取 ...
  • 秋招結束了~~,好像偷懶了很久,沒更博了。總結一下自己近來看書的內容。 說明一下,內容大部分來自《高性能網站建設進階指南》。 亂入內容 Web應用和傳統桌面應用有一個共同的目標:儘可能快地響應用戶輸入。 怎樣才算是快?Jakob Nielsen是Web可用性領域知名且備受推崇的專家,引用他的觀點來說 ...
  • 前言 隨著頁面的內容豐富,以及網站體驗更好、性能優化等,原有的通過script標簽引入JavaScript腳本的方式已經不能很好地解決,此時新的一種JavaScript載入方式產生了——延時載入、執行。而js(JavaScript,下同)模塊化可以說是上面延時、執行的一種表示形式。 requirej ...
  • 今天(周六)下午我在公司加班時不知道要乾什麼,就打開公司的一個wordpress項目網站,想看下之前自己做的一個網頁是否有問題。 打開網站首頁,我習慣性的打開了chrome的調試工具,然後滑鼠開始滾動頁面,然後問題就出來了:頁面無法向下滾動,調試工具的console里報了好多undefined的錯誤 ...
  • 一、事件: 1、模式觸發事件: ①DOM:elem.onXXX();只能觸發直接用onXXX綁定的事件處理函數;用addEventistener添加的事件監聽無法模擬出發觸發; ②jQuery:$(...).trigger("事件名");可簡寫:$(...).事件名; 2、頁面載入後執行: ①jQu ...
  • 寫的比較弱,只能處理50道以內的選項為A-D的單選題,正確答案自己輸進去,必須要大寫,不能有空格和逗號,提交會出分,錯誤的題號和答案會console.log()出來. <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title></ti ...
  • 這是我在面試大公司時碰到的一個筆試題,當時自己雲里霧裡的胡寫了一番,回頭也曾思考過,最終沒實現也就不了了之了。 昨天看到有網友說面試中也碰到過這個問題,我就重新思考了這個問題的實現方法。 對於想進大公司的童鞋,我想多說兩句,基礎知識真的很關鍵。平時在工作中也深刻體會到,沒有扎實的基礎知識,簡單問題容 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...