Nuclear Web組件化入門篇(轉載)

目前來看,團隊內部前端項目已全面實施組件化開發。組件化的好處太多,如:按需加載、可複用、易維護、可擴展、少挖坑、不改組件代碼直接切成服務器端渲染(如Nuclear組件化能夠作到,你們叫同構)...
怎麼作到這麼強大的優點,來回憶下之前見過的坑,或者現有項目裏的坑。javascript

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

在web前端,通常一個組件必需要有骨架HTML和裝飾的CSS以及JS邏輯。而CSS要是能夠是局部做用域那就再好不過了!就不用寫長長的前綴了,浪費帶寬不說,並且費勁。
css

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

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

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

若是HTML綁定的事件是局部做用域那就再好不過了!我真的見過模版代碼裏出現下面的代碼:前端

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

而後在js裏找到了下面的代碼:java

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

要綁定的事件一多,得污染多少全局變量啊。因此還有的工程師這麼幹:git

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

而後在js裏找到了下面的代碼:github

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

這裏貌似比不設定namespace好不少,可是仍是妥協的結果。通常但願能封裝成組件,組件的HTML裏綁定的事件就是組件內定義的事件,內聚內聚!!
經過js動態綁定事件的壞處我之前專門寫了一篇文章來闡述,主要是lazy bind會致使用戶看到了頁面,可是頁面確沒法響應用戶的交互,這裏再也不闡述。web

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

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

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

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

數據變了?從新生成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.零行代碼修改無縫切到同構直出......

相關文章
相關標籤/搜索