目前来看,团队内部前端项目已全面实施组件化开发。组件化的好处太多,如:按需加载、可复用、易维护、可扩展、少挖坑、不改组件代码直接切成服务器端渲染(如Nuclear组件化能够作到,你们叫同构)...
怎么作到这么强大的优点,来回忆下之前见过的坑,或者现有项目里的坑。javascript
在web前端,通常一个组件必需要有骨架HTML和装饰的CSS以及JS逻辑。而CSS要是能够是局部做用域那就再好不过了!就不用写长长的前缀了,浪费带宽不说,并且费劲。
如css
.ui-popup-arrow-xx-xxxxx-xxxx-container { }
这回够长了吧,不会污染别的HTML了吧。真的太长了,没有办法,由于CSS不是局部的,怕污染其余的HTML,规划好长长的namespace、module是之前的最佳实践。html
若是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
怎么替换?先查找dom?什么?你还在查找dom?你还在背诵CSS选择器?替换一下?不能增量更新吗?或者diff一下吧?不要每次所有替换啊!
什么?首屏太慢?改为直出(服务器渲染)?之前代码无法复用?要推翻重写?什么?怎么搞?排期?产品不给排期?需求没变为何要给排期?
下面来看下Nuclear怎么解决上面问题。
npm install alloynuclear
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的变动。
<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