基于 HTML5 Canvas 实现地铁站监控

2017/11/21 · HTML5 ·
Canvas

原文出处: hightopo   

伴随国内经济的高速发展,人们对安全的要求越来越高。为了防止下列情况的发生,您需要考虑安装安防系统:
提供证据与线索:很多工厂银行发生偷盗或者事故相关机关可以根据录像信息侦破案件,这个是非常重要的一个线索。还有一些纠纷或事故,也可以通过录像很容易找出相关人员的责任。
人防成本高:现在很多地方想到安全就想到要雇佣保安,每个保安每个月
800,每天 3 班倒,一班人员一年就需要将近 4
万元,相比于电子安防设备成本并不便宜,而且使用电子安防设备几年内就不太需要更换。所以人防成本相对也很高。人防辅助:多数情况下,完全靠人来保证安全是一件很困难的事情,很多事情需要电子保安器材(如监视器、报警器)辅助才更完美。特殊场合必须使用:在一些恶劣条件下(高热、寒冷、封闭等),人很难用肉眼观察清楚,或者环境根本不适合人的停留,必须使用电子安防设备。隐蔽性:使用电子安防设备,一般人不会感觉时时被监控,具有隐蔽性。24
小时安全保证:要达到 24
小时不间断的安全需要,电子设备是必须考虑的。远程监控:随着计算机技术与网络技术的发展,远程监控观看异地图象已经成为可能,现在已经有很多公司的负责人已经可以
INTERNET
及时观看世界各地的任何分公司情况,有利于及时了解情况。图象保存:数字录像技术的发展,使得影象可以通过计算机数字存储设备得以保存,可以保存时间更长,图象更清晰。生产管理:管理人员可以及时、直观的了解生产第一线的情况,便于指挥与管理。

鉴于监控系统在国内的需求量较大,对于大范围的监控,如地铁站,更是需要监控系统来防止意外的发生,今天我们给大家介绍一下如何创建一个地铁站监控系统的前端部分。

http://www.hightopo.com/demo/…
进入页面右键“审查元素”可查看例子源代码。

本例的动态效果如下:澳门微尼斯人手机版 1

我们先来搭建基础场景,在 HT
中,非常常用的一种方法来将外部场景导入到内部就是靠解析 JSON 文件,用
JSON 文件来搭建场景的好处之一就是可以循环利用,我们今天的场景就是利用
JSON 画出来的。接下来 HT 将利用 ht.Default.xhrLoad 函数载入 JSON
场景,并用 HT 封装的 DataModel.deserialize(json)
来反序列化,并将反序列化的对象加入
DataModel:

ht.Default.xhrLoad(‘demo2.json’, function(text) { var json =
ht.Default.parse(text); if(json.title) document.title = json.title;//将
JSON 文件中的 titile 赋给全局变量 titile
dataModel.deserialize(json);//反序列化
graphView.fitContent(true);//缩放平移拓扑以展示所有图元,即让所有的元素都显示出来
});

1
2
3
4
5
6
ht.Default.xhrLoad(‘demo2.json’, function(text) {
    var json = ht.Default.parse(text);
    if(json.title) document.title = json.title;//将 JSON 文件中的 titile 赋给全局变量 titile
    dataModel.deserialize(json);//反序列化
    graphView.fitContent(true);//缩放平移拓扑以展示所有图元,即让所有的元素都显示出来
});

在 HT 中,Data 类型对象构造时内部会自动被赋予一个 id 属性,可通过
data.getId() 和 data.setId(id) 获取和设置,Data 对象添加到 DataModel
之后不允许修改 id 值,可通过 dataModel.getDataById(id) 快速查找 Data
对象。一般建议 id 属性由 HT 自动分配,用户业务意义的唯一标示可存在 tag
属性上,通过 Data#setTag(tag) 函数允许任意动态改变 tag
值,通过DataModel#getDataByTag(tag) 可查找到对应的 Data
对象,并支持通过 DataModel#removeDataByTag(tag) 删除 Data
对象。我们这边通过在 JSON 中设置 Data 对象的 tag 属性,在代码中通过
dataModel.getDataByTag(tag) 函数来获取该 Data 对象:

var fan1 = dataModel.getDataByTag(‘fan1’); var fan2 =
dataModel.getDataByTag(‘fan2’); var camera1 =
dataModel.getDataByTag(‘camera1’); var camera2 =
dataModel.getDataByTag(‘camera2’); var camera3 =
dataModel.getDataByTag(‘camera3’); var redAlarm =
dataModel.getDataByTag(‘redAlarm’); var yellowAlarm =
dataModel.getDataByTag(‘yellowAlarm’);

1
2
3
4
5
6
7
var fan1 = dataModel.getDataByTag(‘fan1’);
var fan2 = dataModel.getDataByTag(‘fan2’);
var camera1 = dataModel.getDataByTag(‘camera1’);
var camera2 = dataModel.getDataByTag(‘camera2’);
var camera3 = dataModel.getDataByTag(‘camera3’);
var redAlarm = dataModel.getDataByTag(‘redAlarm’);
var yellowAlarm = dataModel.getDataByTag(‘yellowAlarm’);

我在下图中做了各标签对应的元素:澳门微尼斯人手机版 2

接着我们对需要旋转、闪烁的对象进行设置,HT 中对“旋转”封装了
setRotation(rotation)
函数,通过获得对象当前的旋转角度,在这个角度的基础上再增加某个弧度,通过
setInterval 定时调用,这样就能在一定的时间间隔内旋转相同的弧度:

JavaScript

setInterval(function(){ var time = new Date().getTime(); var deltaTime =
time – lastTime; var deltaRotation = deltaTime * Math.PI / 180 * 0.1;
lastTime = time; fan1.setRotation(fan1.getRotation() +
deltaRotation*3); fan2.setRotation(fan2.getRotation() +
deltaRotation*3); camera1.setRotation(camera1.getRotation() +
deltaRotation/3); camera2.setRotation(camera2.getRotation() +
deltaRotation/3); camera3.setRotation(camera3.getRotation() +
deltaRotation/3); if (time – stairTime > 500) { stairIndex–; if
(stairIndex < 0) { stairIndex = 8; } stairTime = time; } for (var i =
0; i < 8; i++) {//因为有一些相似的元素我们设置的 tag
名类似,只是在后面换成了1、2、3,所以我们通过 for 循环来获取 var color =
stairIndex === i ? ‘#F6A623’ : ‘#CFCFCF’;
dataModel.getDataByTag(‘stair_1_’ + i).s(‘shape.border.color’, color);
dataModel.getDataByTag(‘stair_2_’ + i).s(‘shape.border.color’, color);
} if (new Date().getSeconds() % 2 === 1) {
yellowAlarm.s(‘shape.background’, null); redAlarm.s(‘shape.background’,
null); } else { yellowAlarm.s(‘shape.background’, ‘yellow’);
redAlarm.s(‘shape.background’, ‘red’); } }, 5);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
setInterval(function(){
    var time = new Date().getTime();
    var deltaTime = time – lastTime;
    var deltaRotation = deltaTime * Math.PI / 180 * 0.1;
    lastTime = time;
 
    fan1.setRotation(fan1.getRotation() + deltaRotation*3);
    fan2.setRotation(fan2.getRotation() + deltaRotation*3);
    camera1.setRotation(camera1.getRotation() + deltaRotation/3);
    camera2.setRotation(camera2.getRotation() + deltaRotation/3);
    camera3.setRotation(camera3.getRotation() + deltaRotation/3);
 
    if (time – stairTime > 500) {
        stairIndex–;
        if (stairIndex < 0) {
            stairIndex = 8;
        }
        stairTime = time;
    }
 
    for (var i = 0; i < 8; i++) {//因为有一些相似的元素我们设置的 tag 名类似,只是在后面换成了1、2、3,所以我们通过 for 循环来获取
        var color = stairIndex === i ? ‘#F6A623’ : ‘#CFCFCF’;
        dataModel.getDataByTag(‘stair_1_’ + i).s(‘shape.border.color’, color);
        dataModel.getDataByTag(‘stair_2_’ + i).s(‘shape.border.color’, color);
    }
 
    if (new Date().getSeconds() % 2 === 1) {
        yellowAlarm.s(‘shape.background’, null);
        redAlarm.s(‘shape.background’, null);
    }
    else {
        yellowAlarm.s(‘shape.background’, ‘yellow’);
        redAlarm.s(‘shape.background’, ‘red’);
    }
}, 5);

HT 还封装了 setStyle 函数用来设置样式,可简写为 s,具体样式请参考 HT for
Web 样式手册:

JavaScript

for (var i = 0; i < 8; i++) {//因为有一些相似的元素我们设置的 tag
名类似,只是在后面换成了1、2、3,所以我们通过 for 循环来获取 var color =
stairIndex === i ? ‘#F6A623’ : ‘#CFCFCF’;
dataModel.getDataByTag(‘stair_1_’ + i).s(‘shape.border.color’, color);
dataModel.getDataByTag(‘stair_2_’ + i).s(‘shape.border.color’, color);
}

1
2
3
4
5
for (var i = 0; i < 8; i++) {//因为有一些相似的元素我们设置的 tag 名类似,只是在后面换成了1、2、3,所以我们通过 for 循环来获取
    var color = stairIndex === i ? ‘#F6A623’ : ‘#CFCFCF’;
    dataModel.getDataByTag(‘stair_1_’ + i).s(‘shape.border.color’, color);
    dataModel.getDataByTag(‘stair_2_’ + i).s(‘shape.border.color’, color);
}

我们还对“警告灯”的闪烁进行了定时控制,如果是偶数秒的时候,就将灯的背景颜色设置为“无色”,否则,如果是
yellowAlarm 则设置为“黄色”,如果是 redAlarm 则设置为“红色”:

if (new Date().getSeconds() % 2 === 1) {
yellowAlarm.s(‘shape.background’, null); redAlarm.s(‘shape.background’,
null); } else { yellowAlarm.s(‘shape.background’, ‘yellow’);
redAlarm.s(‘shape.background’, ‘red’); }

1
2
3
4
5
6
7
8
if (new Date().getSeconds() % 2 === 1) {
    yellowAlarm.s(‘shape.background’, null);
    redAlarm.s(‘shape.background’, null);
}
else {
    yellowAlarm.s(‘shape.background’, ‘yellow’);
    redAlarm.s(‘shape.background’, ‘red’);
}

整个例子就这么轻松地解决了,简直太轻松了。。。

有兴趣继续了解的小伙伴可以进入 HT for Web
官网查看各个手册进行学习。

2 赞 3 收藏
评论

澳门微尼斯人手机版 3

在 Node.js 中看 JavaScript 的引用

2017/05/05 · JavaScript
· NodeJS

原文出处: lellansin   

早期学习 Node.js 的时候 (2011-2012),有挺多是从 PHP
转过来的,当时有部分人对于 Node.js
编辑完代码需要重启一下表示麻烦(PHP不需要这个过程),于是社区里的朋友就开始提倡使用
node-supervisor
这个模块来启动项目,可以编辑完代码之后自动重启。不过相对于 PHP
而言依旧不够方便,因为 Node.js 在重启以后,之前的上下文都丢失了。

虽然可以通过将 session
数据保存在数据库或者缓存中来减少重启过程中的数据丢失,不过如果是在生产的情况下,更新代码的重启间隙是没法处理请求的(PHP可以,另外那个时候
Node.js 还没有 cluster)。由于这方面的问题,加上本人是从 PHP 转到
Node.js 的,于是从那时开始思考,有没有办法可以在不重启的情况下热更新
Node.js 的代码。

最开始把目光瞄向了 require 这个模块。想法很简单,因为 Node.js
中引入一个模块都是通过 require 这个方法加载的。于是就开始思考 require
能不能在更新代码之后再次 require 一下。尝试如下:

a.js

var express = require(‘express’); var b = require(‘./b.js’); var app =
express(); app.get(‘/’, function (req, res) { b = require(‘./b.js’);
res.send(b.num); }); app.listen(3000);

1
2
3
4
5
6
7
8
9
10
11
var express = require(‘express’);
var b = require(‘./b.js’);
 
var app = express();
 
app.get(‘/’, function (req, res) {
  b = require(‘./b.js’);
  res.send(b.num);
});
 
app.listen(3000);

b.js

exports.num = 1024;

1
exports.num = 1024;

两个 JS 文件写好之后,从 a.js 启动,刷新页面会输出 b.js 中的
1024,然后修改 b.js 文件中导出的值,例如修改为
2048。再次刷新页面依旧是原本的 1024。

再次执行一次 require 并没有刷新代码。require
在执行的过程中加载完代码之后会把模块导出的数据放在 require.cache
中。require.cache 是一个 { } 对象,以模块的绝对路径为
key,该模块的详细数据为 value。于是便开始做如下尝试:

a.js

var path = require(‘path’); var express = require(‘express’); var b =
require(‘./b.js’); var app = express(); app.get(‘/’, function (req, res)
{ if (true) { // 检查文件是否修改 flush(); } res.send(b.num); });
function flush() { delete require.cache[path.join(__dirname,
‘./b.js’)]; b = require(‘./b.js’); } app.listen(3000);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var path = require(‘path’);
var express = require(‘express’);
var b = require(‘./b.js’);
 
var app = express();
 
app.get(‘/’, function (req, res) {
  if (true) { // 检查文件是否修改
    flush();
  }
  res.send(b.num);
});
 
function flush() {
  delete require.cache[path.join(__dirname, ‘./b.js’)];
  b = require(‘./b.js’);
}
 
app.listen(3000);

再次 require 之前,将 require 之上关于该模块的 cache
清理掉后,用之前的方法再次测试。结果发现,可以成功的刷新 b.js
的代码,输出新修改的值。

了解到这个点后,就想通过该原理实现一个无重启热更新版本的
node-supervisor。在封装模块的过程中,出于情怀的原因,考虑提供一个类似
PHP 中 include 的函数来代替 require 去引入一个模块。实际内部依旧是使用
require 去加载。以b.js为例,原本的写法改为 var b =
include(‘./b’),在文件 b.js 更新之后 include
内部可以自动刷新,让外面拿到最新的代码。

但是实际的开发过程中,这样很快就碰到了问题。我们希望的代码可能是这样:

web.js

var include = require(‘./include’); var express = require(‘express’);
var b = include(‘./b.js’); var app = express(); app.get(‘/’, function
(req, res) { res.send(b.num); }); app.listen(3000);

1
2
3
4
5
6
7
8
9
10
var include = require(‘./include’);
var express = require(‘express’);
var b = include(‘./b.js’);
var app = express();
 
app.get(‘/’, function (req, res) {
  res.send(b.num);
});
 
app.listen(3000);

但按照这个目标封装include的时候,我们发现了问题。无论我们在include.js内部中如何实现,都不能像开始那样拿到新的
b.num。

对比开始的代码,我们发现问题出在少了 b = xx。也就是说这样写才可以:

web.js

var include = require(‘./include’); var express = require(‘express’);
var app = express(); app.get(‘/’, function (req, res) { var b =
include(‘./b.js’); res.send(b.num); }); app.listen(3000);

1
2
3
4
5
6
7
8
9
10
var include = require(‘./include’);
var express = require(‘express’);
var app = express();
 
app.get(‘/’, function (req, res) {
  var b = include(‘./b.js’);
  res.send(b.num);
});
 
app.listen(3000);

修改成这样,就可以保证每次能可以正确的刷新到最新的代码,并且不用重启实例了。读者有兴趣的可以研究这个include是怎么实现的,本文就不深入讨论了,因为这个技巧使用度不高,写起起来不是很优雅[1],反而这其中有一个更重要的问题——JavaScript的引用。

圣杯布局小结

2016/01/30 · HTML5 · 1
评论 ·
圣杯布局

原文出处: 流云诸葛   

圣杯布局,很久之前就听过,但是一直都没详细了解过,最近因为做了一个项目,借鉴了薪人薪事这个公司的产品页面,才第一次用到这种布局方式。于是就花了点时间,测了下它实现常见分栏布局的代码,每段代码都非常简单,但布局效果很完美,比我以前用的方式好用不少。本文是对它实现方式的一些总结,希望可以把这种技术推荐给跟我之前一样对它比较陌生的开发人员:)

JavaScript 的引用与传统引用的区别

要讨论这个问题,我们首先要了解 JavaScript
的引用于其他语言中的一个区别,在 C++ 中引用可以直接修改外部的值:

#include using namespace std; void test(int &p) // 引用传递 { p = 2048;
} int main() { int a = 1024; int &p = a; // 设置引用p指向a test(p); //
调用函数 cout

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include
 
using namespace std;
 
void test(int &p) // 引用传递
{
    p = 2048;
}
 
int main()
{
    int a = 1024;
    int &p = a; // 设置引用p指向a
 
    test(p); // 调用函数
 
    cout

而在 JavaScript 中:

var obj = { name: ‘Alan’ }; function test1(obj) { obj = { hello: ‘world’
}; // 试图修改外部obj } test1(obj); console.log(obj); // { name: ‘Alan’
} // 并没有修改① function test2(obj) { obj.name = ‘world’; //
根据该对象修改其上的属性 } test2(obj); console.log(obj); // { name:
‘world’ } // 修改成功②

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var obj = { name: ‘Alan’ };
 
function test1(obj) {
  obj = { hello: ‘world’ }; // 试图修改外部obj
}
 
test1(obj);
console.log(obj); // { name: ‘Alan’ } // 并没有修改①
 
function test2(obj) {
  obj.name = ‘world’; // 根据该对象修改其上的属性
}
 
test2(obj);
console.log(obj); // { name: ‘world’ } // 修改成功②

我们发现与 C++ 不同,根据上面代码 ① 可知 JavaScript
中并没有传递一个引用,而是拷贝了一个新的变量,即值传递。根据 ②
可知拷贝的这个变量是一个可以访问到对象属性的“引用”(与传统的 C++
的引用不同,下文中提到的 JavaScript
的引用都是这种特别的引用)。这里需要总结一个绕口的结论:Javascript
中均是值传递,对象在传递的过程中是拷贝了一份新的引用。

为了理解这个比较拗口的结论,让我们来看一段代码:

var obj = { data: {} }; // data 指向 obj.data var data = obj.data;
console.log(data === obj.data); // true–>data所操作的就是obj.data
data.name = ‘Alan’; data.test = function () { console.log(‘hi’) }; //
通过data可以直接修改到data的值 console.log(obj) // { data: { name:
‘Alan’, test: [Function] } } data = { name: ‘Bob’, add: function (a,
b) { return a + b; } }; //
data是一个引用,直接赋值给它,只是让这个变量等于另外一个引用,并不会修改到obj本身
console.log(data); // { name: ‘Bob’, add: [Function] }
console.log(obj); // { data: { name: ‘Alan’, test: [Function] } }
obj.data = { name: ‘Bob’, add: function (a, b) { return a + b; } }; //
而通过obj.data才能真正修改到data本身 console.log(obj); // { data: {
name: ‘Bob’, add: [Function] } }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
var obj = {
  data: {}
};
 
// data 指向 obj.data
var data = obj.data;
 
console.log(data === obj.data); // true–>data所操作的就是obj.data
 
data.name = ‘Alan’;
data.test = function () {
  console.log(‘hi’)
};
 
// 通过data可以直接修改到data的值
console.log(obj) // { data: { name: ‘Alan’, test: [Function] } }
 
data = {
  name: ‘Bob’,
  add: function (a, b) {
    return a + b;
  }
};
 
// data是一个引用,直接赋值给它,只是让这个变量等于另外一个引用,并不会修改到obj本身
console.log(data); // { name: ‘Bob’, add: [Function] }
console.log(obj); // { data: { name: ‘Alan’, test: [Function] } }
 
obj.data = {
  name: ‘Bob’,
  add: function (a, b) {
    return a + b;
  }
};
 
// 而通过obj.data才能真正修改到data本身
console.log(obj); // { data: { name: ‘Bob’, add: [Function] } }

通过这个例子我们可以看到,data 虽然像一个引用一样指向了
obj.data,并且通过 data 可以访问到 obj.data 上的属性。但是由于
JavaScript 值传递的特性直接修改 data = xxx 并不会使得 obj.data = xxx。

打个比方最初设置 var data = obj.data 的时候,内存中的情况大概是:

| Addr | 内容 | |———-|——– | obj.data | 内存1 | | data | 内存1
|

1
2
3
4
|   Addr   |  内容  |
|———-|——–
| obj.data |  内存1 |
|   data   |  内存1 |

所以通过 data.xx 可以修改 obj.data 的内存1。

然后设置 data = xxx,由于 data
是拷贝的一个新的值,只是这个值是一个引用(指向内存1)罢了。让它等于另外一个对象就好比:

| Addr | 内容 | |———-|——– | obj.data | 内存1 | | data | 内存2
|

1
2
3
4
|   Addr   |  内容  |
|———-|——–
| obj.data |  内存1 |
|   data   |  内存2 |

让 data 指向了新的一块内存2。

如果是传统的引用(如上文中提到的 C++ 的引用),那么 obj.data
本身会变成新的内存2,但 JavaScript
中均是值传递,对象在传递的过程中拷贝了一份新的引用。所以这个新拷贝的变量被改变并不影响原本的对象。

1. 从2个实际的需求说起

今年有2个项目,都是管理系统的项目,这种项目的界面特点基本都是左侧边栏显示菜单,右侧显示网页主体或者是顶部的导航栏显示菜单,导航栏以下显示网页主体,我这两个项目都是第一种类型:

项目一:

澳门微尼斯人手机版 4

项目二:

澳门微尼斯人手机版 5

在做项目一的时候,采用了以前做ERP软件的一些做法,右边的网页主体区域放置的是一个iframe,用来显示每个菜单点击之后的页面,这样每个菜单点击之后,外部页面都不会刷新,并且滚动也只发生在iframe里面,外部页面的菜单区域和顶部导航栏的状态始终不会改变,用户操作起来非常便捷。这种界面布局的做法非常简单,只要侧边栏和网页主体区域都采用固定定位即可:

<div class=”sidebar”></div> <div
class=”page-content”></div> .sidebar { position: absolute;
width: 200px; left: 0; bottom: 0; top: 50px; border-right: 1px solid
#E7E7E7; } .page-content { position: absolute; left: 205px; bottom: 0;
top: 50px; right: 0; }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<div class="sidebar"></div>
<div class="page-content"></div>
 
.sidebar {
    position: absolute;
    width: 200px;
    left: 0;
    bottom: 0;
    top: 50px;
    border-right: 1px solid #E7E7E7;
}
 
.page-content {
    position: absolute;
    left: 205px;
    bottom: 0;
    top: 50px;
    right: 0;
}

由于这个项目是一个内部项目,所以采用这种界面结构完全是可以接受的,毕竟这只是一个管理系统,可以不那么在乎用户体验什么的。最近做项目二的时候,情况就不一样了,这个项目是一个企业级的管理应用,它不再是一个单纯的管理系统,而是面向外部用户提供的参与平台业务的一个终端应用,从用户体验的角度来说,项目一那种固定死板的方式不太拿得出手给别人用,不然别人肯定会认为你的应用做的很low。相对于项目一的界面,项目二有以下特点:

1)菜单点击之后,界面是整体刷新,没有iframe了;

2)侧边栏和主体内容栏的高度都是不固定的;

3)网页滚动的时候,是页面整体滚动,而不是只滚动主体内容。

几个礼拜前,看到薪人薪事融资的新闻,心想这是个什么公司,怎么之前都没听过,做什么业务的,于是就百度了下,注册了账号,进去体验了一下它的产品,后来发现它做的其实是一个SAAS应用,界面上看是一个典型的管理系统的风格,但是整体体验还不错,当时就觉得以后说不定有参考借鉴的价值。正好上周临时安排要做项目二,根据项目一提了一些要求,于是就想到薪人薪事的应用了。只有3天时间,工作除了界面之外,还有4个业务模块的功能要完成,为了完成这个东西,界面布局完全参考了薪人薪事的做法,由于以前没用过这种布局方式,所以觉得很新奇,就专门搜集知识学习了一下,才发现这个方法就是以前听过的圣杯布局。项目二所用的布局方法就是圣杯布局方式中侧边栏固定,主体内容栏自适应的一种做法。

Node.js 中的 module.exports 与 exports

上述例子中的 obj.data 与 data 的关系,就是 Node.js 中的 module.exports
与 exports 之间的关系。让我们来看看 Node.js 中 require
一个文件时的实际结构:

function require(…) { var module = { exports: {} }; ((module, exports)
=> { // Node.js 中文件外部其实被包了一层自执行的函数 //
这中间是你模块内部的代码. function some_func() {}; exports =
some_func; // 这样赋值,exports便不再指向module.exports //
而module.exports依旧是{} module.exports = some_func; //
这样设置才能修改到原本的exports })(module, module.exports); return
module.exports; }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function require(…) {
  var module = { exports: {} };
  ((module, exports) => { // Node.js 中文件外部其实被包了一层自执行的函数
    // 这中间是你模块内部的代码.
    function some_func() {};
    exports = some_func;
    // 这样赋值,exports便不再指向module.exports
    // 而module.exports依旧是{}
 
    module.exports = some_func;
    // 这样设置才能修改到原本的exports
  })(module, module.exports);
  return module.exports;
}

所以很自然的:

console.log(module.exports === exports); // true // 所以 exports
所操作的就是 module.exports

1
2
console.log(module.exports === exports); // true
// 所以 exports 所操作的就是 module.exports

Node.js 中的 exports 就是拷贝的一份 module.exports 的引用。通过 exports
可以修改Node.js 当前文件导出的属性,但是不能修改当前模块本身。通过
module.exports 才可以修改到其本身。表现上来说:

exports = 1; // 无效 module.exports = 1; // 有效

1
2
exports = 1; // 无效
module.exports = 1; // 有效

这是二者表现上的区别,其他方面用起来都没有差别。所以你现在应该知道写module.exports.xx
= xxx; 的人其实是多写了一个module.。

2. 圣杯布局的传统实现方法

利用圣杯布局的方法,可以轻松实现下面的布局效果:

澳门微尼斯人手机版 6

下面来一一说明上图中五种布局效果的实现方法(本文相关代码下载,本部分的布局方法在代码中对应grail_layout{1,5}.html)。

1)布局一:2栏布局,侧边栏固定在左边,右侧是主体内容栏:

<div class=”layout”> <aside
class=”layout__aside”>侧边栏宽度固定</aside> <div
class=”layout__main”>主内容栏宽度自适应</div> </div>

1
2
3
4
<div class="layout">
    <aside class="layout__aside">侧边栏宽度固定</aside>
    <div class="layout__main">主内容栏宽度自适应</div>
</div>

<style type=”text/css”> .layout:after { clear: both; content: ” “;
display: table; } .layout__aside, .layout__main { float: left; }
.layout { padding-left: 210px; } .layout__main { width: 100%; }
.layout__aside { width: 200px; margin-left: -210px; } </style>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<style type="text/css">
    .layout:after {
        clear: both;
        content: " ";
        display: table;
    }
    .layout__aside, .layout__main {
        float: left;
    }
    .layout {
        padding-left: 210px;
    }
    .layout__main {
        width: 100%;
    }
    .layout__aside {
        width: 200px;
        margin-left: -210px;
    }
</style>

效果是:

澳门微尼斯人手机版 7

2)布局二:2栏布局,侧边栏固定在右边,左侧是主体内容栏:

<div class=”layout”> <div
class=”layout__main”>主内容栏宽度自适应</div> <aside
class=”layout__aside”>侧边栏宽度固定</aside> </div>

1
2
3
4
<div class="layout">
    <div class="layout__main">主内容栏宽度自适应</div>
    <aside class="layout__aside">侧边栏宽度固定</aside>
</div>

<style type=”text/css”> .layout:after { clear: both; content: ” “;
display: table; } .layout { padding-right: 210px; } .layout__main {
width: 100%; float: left; } .layout__aside { float: right; width:
200px; margin-right: -210px; } </style>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<style type="text/css">
    .layout:after {
        clear: both;
        content: " ";
        display: table;
    }
    .layout {
        padding-right: 210px;
    }
    .layout__main {
        width: 100%;
        float: left;
    }
    .layout__aside {
        float: right;
        width: 200px;
        margin-right: -210px;
    }
</style>

效果是:

澳门微尼斯人手机版 8

3)布局三:3栏布局,2个侧边栏分别固定在左边和右边,中间是主体内容栏:

<div class=”layout”> <aside class=”layout__aside
layout__aside–left”>左侧边栏宽度固定</aside> <div
class=”layout__main”>主内容栏宽度自适应</div> <aside
class=”layout__aside
layout__aside–right”>右侧边栏宽度固定</aside> </div>

1
2
3
4
5
<div class="layout">
    <aside class="layout__aside layout__aside–left">左侧边栏宽度固定</aside>
    <div class="layout__main">主内容栏宽度自适应</div>
    <aside class="layout__aside layout__aside–right">右侧边栏宽度固定</aside>
</div>

<style type=”text/css”> .layout:after { clear: both; content: ” “;
display: table; } .layout__aside, .layout__main { float: left; }
.layout { padding:0 210px; } .layout__main { width: 100%; }
.layout__aside { width: 200px; } .layout__aside–left { margin-left:
-210px; } .layout__aside–right { margin-right: -210px; float: right;
} </style>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<style type="text/css">
    .layout:after {
        clear: both;
        content: " ";
        display: table;
    }
    .layout__aside, .layout__main {
        float: left;
    }
    .layout {
        padding:0 210px;
    }
    .layout__main {
        width: 100%;
    }
    .layout__aside {
        width: 200px;
    }
    .layout__aside–left {
        margin-left: -210px;
    }
    .layout__aside–right {
        margin-right: -210px;
        float: right;
    }
</style>

效果是:

澳门微尼斯人手机版 9

4)布局四:3栏布局,2个侧边栏同时固定在左边,右边是主体内容栏:

<div class=”layout”> <aside class=”layout__aside
layout__aside–first”>第1个侧边栏宽度固定</aside> <aside
class=”layout__aside
layout__aside–second”>第2个侧边栏宽度固定</aside> <div
class=”layout__main”>主内容栏宽度自适应</div> </div>

1
2
3
4
5
<div class="layout">
    <aside class="layout__aside layout__aside–first">第1个侧边栏宽度固定</aside>
    <aside class="layout__aside layout__aside–second">第2个侧边栏宽度固定</aside>
    <div class="layout__main">主内容栏宽度自适应</div>
</div>

<style type=”text/css”> .layout:after { clear: both; content: ” “;
display: table; } .layout__aside, .layout__main { float: left; }
.layout { padding-left: 420px; } .layout__main { width: 100%; }
.layout__aside { width: 200px; } .layout__aside–first {
margin-left: -420px; } .layout__aside–second { margin-left: -210px; }
</style>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<style type="text/css">
    .layout:after {
        clear: both;
        content: " ";
        display: table;
    }
    .layout__aside, .layout__main {
        float: left;
    }
    .layout {
        padding-left: 420px;
    }
    .layout__main {
        width: 100%;
    }
    .layout__aside {
        width: 200px;
    }
    .layout__aside–first {
        margin-left: -420px;
    }
    .layout__aside–second {
        margin-left: -210px;
    }
</style>

效果是:

澳门微尼斯人手机版 10

5)布局五:3栏布局,2个侧边栏同时固定在右边,左边是主体内容栏:

<div class=”layout”> <div
class=”layout__main”>主内容栏宽度自适应</div> <aside
class=”layout__aside
layout__aside–first”>第1个侧边栏宽度固定</aside> <aside
class=”layout__aside
layout__aside–second”>第2个侧边栏宽度固定</aside>
</div>

1
2
3
4
5
<div class="layout">
    <div class="layout__main">主内容栏宽度自适应</div>
    <aside class="layout__aside layout__aside–first">第1个侧边栏宽度固定</aside>
    <aside class="layout__aside layout__aside–second">第2个侧边栏宽度固定</aside>
</div>

<style type=”text/css”> .layout:after { clear: both; content: ” “;
display: table; } .layout { padding-right: 420px; } .layout__main {
width: 100%; float: left; } .layout__aside { width: 200px; float:
right; } .layout__aside–first { margin-right: -210px; }
.layout__aside–second { margin-right: -420px; } </style>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<style type="text/css">
    .layout:after {
        clear: both;
        content: " ";
        display: table;
    }
    .layout {
        padding-right: 420px;
    }
    .layout__main {
        width: 100%;
        float: left;
    }
    .layout__aside {
        width: 200px;
        float: right;
    }
    .layout__aside–first {
        margin-right: -210px;
    }
    .layout__aside–second {
        margin-right: -420px;
    }
</style>

效果是:

澳门微尼斯人手机版 11

PS:

1)本文提供的这个布局方法,比网上看到的更加简洁一些,主要是因为不考虑兼容IE8及以下,不考虑把layout__main这个元素放在最前面,虽然经典的做法都要求把layout__main做法放在前面,这样可以让网页主体内容优先渲染,我认为这种考虑是完全多余的,2个元素的渲染顺序不同,实际上的用户体验差别又有多大呢,为了一个肉眼看不到的差异,而采用更复杂的做法,不值得;

2)css布局类的命名采用了BEM的命名规则,这个可以帮助你写出结构化,规范化的css,有兴趣的可以去了解:

3)在使用以上方法时,需注意html结构中layout__main与layout__aside的顺序;

发表评论

电子邮件地址不会被公开。 必填项已用*标注