漫谈Nuclear Web组件化入门篇
目前来看,团队内部前端项目已全面实施组件化开发。组件化的好处太多,如:按需加载、可复用、易维护、可扩展、少挖坑、不改组件代码直接切成服务器端渲染(如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>
var Todo= todo(Nuclear);
new Todo('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
漫谈Nuclear Web组件化入门篇的更多相关文章
- Mr.聂 带你成为web开发大牛——入门篇(上)
作为一名IT届的后生,当初也经历过懵懂无知的实习期,对那种无力感深有体会.在这,希望能用我这几年的开发经验,让各位即将踏入或者刚刚踏入web开发领域的新人们少走些弯路.鉴于这是入门篇,下面我就从零为大 ...
- atitit. web组件化原理与设计
atitit. web组件化原理与设计 1. Web Components提供了一种组件化的推荐方式,具体来说,就是:1 2. 组件化的本质目的并不一定是要为了可复用,而是提升可维护性. 不具有复用 ...
- Lightning Web Components 来自salesforce 的web 组件化解决方案
Lightning Web Components 是一个轻量,快速,企业级别的web 组件化解决方案,官方网站也提供了很全的文档 对于我们学习使用还是很方便的,同时我们也可以方便的学习了解salesf ...
- 探讨Web组件化的实现
CMS组件化,简单架构示意图: Web组件使用WebPage+WebAPI的好处: Ø 组件复用(组件条件管理页面复用+获取组件数据API复用). Ø 组件是分布式的第三方应用,本身高内聚.组件之 ...
- Sass与Web组件化相关的功能
Sass https://en.wikipedia.org/wiki/Sass_(stylesheet_language) Sass (Syntactically Awesome Stylesheet ...
- web组件开发入门
本文是学习慕课网阿当大话西游之WEB组件后的一个总结. 组件的分类 1 框架组件:依赖于某种框架的组件 2 定制组件:根据公司业务定制的组件 3 独立组件:不依赖框架的组件 定义和加载组件 解决css ...
- ASP.NET Web API 之一 入门篇
一.基于RESTful标准的Web Api 原文讲解:https://www.cnblogs.com/lori/p/3555737.html 微软的web api是在vs2012上的mvc4项目绑定发 ...
- 2017年试试Web组件化框架Omi
Open and modern framework for building user interfaces. Omi的Github地址https://github.com/AlloyTeam/omi ...
- web自动化-selenium 入门篇
selenium安装介绍 selenium是web浏览器的自动化工具 官网:https://www.selenium.dev 构成: WebDriver: 浏览器提供的浏览器api来控制浏览器(模拟用 ...
随机推荐
- Form 表单提交参数
今天因为要额外提交参数数组性的参数给form传到后台而苦恼了半天,结果发现,只需要在form表单对应的字段html空间中定义name = 后台参数名 的属性就ok了. 后台本来是只有模型参数的,但是后 ...
- php利用root权限执行shell脚本
vi /etc/sudoers , 为apache用户赋予root权限,并且不需要密码,还有一步重要的修改(我被困扰的就是这个地方) root ALL=(ALL) ALL apache ALL= ...
- 使用四元数解决万向节锁(Gimbal Lock)问题
问题 使用四元数可以解决万向节锁的问题,但是我在实际使用中出现问题:我设计了一个程序,显示一个三维物体,用户可以输入绕zyx三个轴进行旋转的指令,物体进行相应的转动. 由于用户输入的是绕三个轴旋转的角 ...
- 我的公司培训讲义(1):.NET开发规范教程
这是1年多以前我在公司所做讲座的讲义,现在与园友们分享,欢迎拿去使用.一起讨论.文中有若干思考题,对园友们是小菜一碟.另有设计模式讲义一篇,随后发布.博文上了首页,感谢博客园团队推荐,也感谢所有园友的 ...
- Android中自定义样式与View的构造函数中的第三个参数defStyle的意义
零.序 一.自定义Style 二.在XML中为属性声明属性值 1. 在layout中定义属性 2. 设置Style 3. 通过Theme指定 三.在运行时获取属性值 1. View的第三个构造函数的第 ...
- 精通Perl(第2版)
精通Perl(第2版)(通往Perl大师之路必读经典书籍,体现了一种编程思维,能够帮你解决很多实际的问题) [美]brian d foy(布瑞恩·D·福瓦)著 王兴宇 刘宸宇 译 ISBN 978 ...
- 【Win 10 应用开发】UDP广播
我们知道,对于UDP协议的通信,除了可以用来聊天外,可以发送广播数据.只要向广播地址的某个端口发送数据就可以进行广播,子网中只要监听该端口的socket就能收到广播消息. 最简单的方法就是向255.2 ...
- 锋利的jQuery--Ajax(读书笔记四)
1.表单值得序列化 <1>serialize() 例子: html: <form action=""> First name: <input type ...
- MVC5 网站开发之七 用户功能 2 用户添加和浏览
目录 MVC5网站开发之一 总体概述 MVC5 网站开发之二 创建项目 MVC5 网站开发之三 数据存储层功能实现 MVC5 网站开发之四 业务逻辑层的架构和基本功能 MVC5 网站开发之五 展示层架 ...
- Hibernate(5)—— 联合主键 、一对一关联关系映射(xml和注解) 和 领域驱动设计
俗话说,自己写的代码,6个月后也是别人的代码……复习!复习!复习!涉及的知识点总结如下: One to One 映射关系 一对一单向外键(XML/Annotation) 一对一双向外键关联(XML/A ...