浅谈WebGL和ThreeJS相关

整理一些WebGL相关

  • WebGL:是JavaScript的一个API 允许在浏览器端生成3D图像或者图像。
  • Three.js:是一个WebGL的框架,让在浏览器端生成3D图形更加的容易,它使用 canvas + WebGL 一起共同展示3D场景。
  • CSS 3D:仅仅只有一些CSS3的一些3D变型,让我们能更容易的实现出3D效果,但是采用的是常规的DOM节点。
  • D3.js:D3是一个数据可视化的库,它很容易的生成基于数据的图形,但是生成的都是2D的图形。

使用场景

  • 如果你仅仅是在你的网站上想要一些3D的效果用CSS3变形的话,可以看一下这个不错的介绍:Intro to CSS 3D transforms

  • 如果你是想要做3D模型、纹理、场景,更像真实效果的话,用Three.js。它是在WebGL上面做了很简单又优雅的一层,并且文档完善而丰富,可以从下面这个文档开始学习:Three.js / documentation,或者去这个讲解的教程看看:Getting Start with ThreeJS

  • 如果想做数据驱动的2D图形,就用D3这个库,这个库有很大能力去构建客户想要的图表,并且能让图片更加有交互性,官网:D3官网

结语

网上也有一些非常cool的demo,是把D3,CSS3D,Three.js这三个库结合在一起的,可以移步这里delimited | CrunchBase Top Investors,讲解的文章D3.js, Three.js and CSS 3D Transforms
希望上面这些会帮助到你,如果你想了解更多的信息请直接去查看他们各自的官方文档,并且也可以去看他们的🌰,会有很多有用的实践,并且让你更容易的去理解他们。

完…

Javascript SourceMap syntax changed

Javascript SourceMap 简介

SourceMap的入门知识请移步阮一峰之前写的JavaScript sourceMap详解,这里不做太多的入门介绍,总的一句话,这破玩意就是在压缩后的js代码最后加入”//@ sourceMappingURL=jquery.min.map”一行代码,作用呢说起来也很简单,就是能帮你找到你检查的某句代码在哪个文件,第多少行,举个 🌰 :点击后面这个链接进入Sourcemap demo,里面就是你选中代码里的一段,右键选择get origin location,上面的Output框里面就打印出来你选中的代码在哪个文件的第多少行。

Syntax changed

SourceMap是谷歌出的,现在也更新到了第三版,以前都是”//@ sourceMappingURL=XXX.min.map”这么加,现在如果还是这么写的话,Chrome控制台就会报一个这样的错,”/@ sourceMappingURL=” source mapping URL declaration is deprecated, “/# sourceMappingURL=” declaration should be used instead。这个是最近看美团i版线上控制台出现的,以前也没有,其实就是把之前的@号变成#好就好了,源于Google Developer的文档

扩展

阮一峰的文章里面说最常用的方法是使用Google的Closure编译器,链接进去之后也不知道那是什么鬼,看底下的介绍说拿java执行一个命令就能生成sourcemap文件,前端工程师用什么java,后又在google上搜看到一篇比较靠谱英文的文章,仔细原来峰哥就是把那篇文章翻译过来了,链接在这Html5rocks SourceMap,估计他自己也没亲自试验过吧 😓,于是自己还是不清楚怎么来生成,哥觉得自己在研究一下,遂明白,Grunt和Gulp都有uglify的插件,uglify可以选择性的生成sourcemap,请移步uglify 2,里面有详细的说明怎么样生成sourcemap。

总结

总之一句话,直接搬过来翻译一下还是不太好😄,盲目照搬照抄没自己的观点也不行哈,毕竟现在是一个多元化的社会。

完…

基于Nodejs的WebSocket聊天室

什么是WebSocket和Socket.io?


WebSocket是HTML5的一种通讯协议,实现了浏览器和服务器之间的双向通讯,Socket.io是一个完全用JavaScript实现的,基于Nodejs和WebSocket的协议做的一个开源框架,Socket.IO除了支持WebSocket通讯协议外,还支持许多种轮询(Polling)机制以及其它实时通信方式,并封装成了通用的接口,并且在服务端实现了这些实时机制的相应代码。Socket.IO实现的Polling通信机制包括Adobe Flash Socket、AJAX长轮询、AJAX multipart streaming、持久Iframe、JSONP轮询等。Socket.IO能够根据浏览器对通讯机制的支持情况自动地选择最佳的方式来实现网络实时应用。

准备工作

快速生成一个express框架,安装相应package,命令如下:

1
2
3
4
$express ChatRoom /* ChatRoom工程的名字,如果没有安装express_cli的需要自行安装 */
$cd ChatRoom
$vim package.json /* package.json dependencies里面添加pm2,ejs,socket.io,或者使用npm install对应package */
$npm install --save --registry=http://r.npm.sankuai.com

第一步

基本的准备工作完成后,需要把express默认的jade替换成ejs,ejs识别.html的作为模板,.html的也比较方便开发人员来搞。在工程的根目录下:

1
2
3
4
5
6
7
8
$vim app.js
/* 删掉express默认配置jade的两行代码,注册ejs模板为html页,就是原来以.ejs为后缀的模板页,现在的后缀名可以是.html了 */
app.engine('.html', require('ejs').__express);
/* 设置视图模板的默认后缀名为.html,避免了每次res.Render("xx.html")的尴尬 */
app.set('view engine', 'html');
/* 设置模板文件文件夹,__dirname为全局变量,表示网站根目录 */
app.set('views', __dirname + '/views');
$:wq /*保存*/

第二步

研究express框架,可以知道package.json里面的scripts的start是”node ./bin/www”,其实就是执行的www这个js,修改成”pm2 start ./bin/www”,这样npm start启动就可以用pm2进行守护进程了。

1
2
3
4
5
6
7
8
$pm2 list
┌──────────┬────┬──────┬───────┬─────────┬─────────┬────────┬─────────────┬──────────┐
│ App name │ id │ mode │ pid │ status │ restart │ uptime │ memory │ watching │
├──────────┼────┼──────┼───────┼─────────┼─────────┼────────┼─────────────┼──────────┤
│ www │ 2 │ fork │ 81061 │ online │ 47 │ 16m │ 38.145 MB │ disabled │
│ www │ 3 │ fork │ 0 │ errored │ 15 │ 0 │ 0 B │ disabled │
└──────────┴────┴──────┴───────┴─────────┴─────────┴────────┴─────────────┴──────────┘
$pm2 stop all/[id|name] /*可以直接停止对应的进程*/

有点跑题了,PM2这里不多讲了,可以直接去PM2的官网自己研究,PM2也提供了一些可编程的API,可以直接写Nodejs来实习PM2的一些指令操作。

第三步

修改www这个js文件,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
$vim bin/www
/**
* Create HTTP server.
*/
var server = http.createServer(app); /* 在这句代码下面,加入下面的几行代码*/

var io = require('socket.io')(server);
io.on('connection', function(socket){
socket.on('chat message', function(msg){
io.emit('chat message', msg);
});
});
$:wq /*保存*/

第四步

进入views这个目录,新建一个index.html,加入下面的代码:

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
$vim index.html
<!doctype html>
<html>
<head>
<title>Socket.IO chat</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }body { font: 13px Helvetica, Arial; }form { background: #000; padding: 3px; position: fixed; bottom: 0; width: 100%; }form input { border: 0; padding: 10px; width: 90%; margin-right: .5%; }form button { width: 9%; background: rgb(130, 224, 255); border: none; padding: 10px; }#messages { list-style-type: none; margin: 0; padding: 0; }#messages li { padding: 5px 10px; }#messages li:nth-child(odd) { background: #eee; }
</style>
</head>
<body>
<h2>
<%=title%>
</h2>
<ul id="messages"></ul>
<form action="">
<input id="m" autocomplete="off" /><button>Send</button>
</form>
<script src="/socket.io/socket.io.js"></script>
<script src="http://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
<script>
var socket = io();
$('form').submit(function(){
socket.emit('chat message', $('#m').val());
$('#m').val('');
return false;
});
socket.on('chat message', function(msg){
$('#messages').append($('<li>').text(msg));
});
</script>
</body>
</html>
$:wq /*保存*/

第五步

进入routes目录,编辑index.js,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
$vim index.js

var express = require('express');
var router = express.Router();

/* GET home page. */
router.get('/', function(req, res, next) {
res.render('index',{title:'技术讨论区'});
});

module.exports = router;

$:wq /*保存*/

保存完毕之后,这时候代码基本就算弄完了,在工程目录里面 npm start 之后就能在浏览器中看到效果了,本网站已经开放了简易聊天室了,可以直接移步这里看demo,WebSocket聊天室

遇到的坑以及解决方法

nodejs原生http模块的困惑

socket.io教程里面有一段这样的代码

1
2
3
4
var http = require('http').Server(app);
http.listen(3000, function(){
console.log('listening on *:3000');
});

不难理解就是3000端口启动一个http服务,但是通过express cli 生成的./bin/www里面的代码也存放这启动http服务的代码,代码是这样的:

1
2
3
4
5
var http = require('http');
var port = normalizePort(process.env.PORT || '3000');
app.set('port', port);
var server = http.createServer(app);
server.listen(port);

这时候俺就震惊了,同是Require的http,为毛启动一个服务会有两种createServer()和Server()两种?你去查nodejs官方文档,发现也只有第一种,那么第二种是什么鬼?

stackoverflow上给出了答案,其实这时候就得翻nodejs的源码了,源码在这里nodejs http模块,仔细看下第1903行到1905行,就能明白在实现过程中遇到的困惑了。不过从上面的url上可以看到github上看代码的一个小技巧,url后面hash值接L50-L60的意思就是浏览器可以直接定位第50行到60行。

完…

Nodejs缓冲模块Buffer基础

nodejs Buffer模块简介

在nodejs中,Buffer模块是和Node内核一起发布的核心模块,Buffer模块可以让nodejs处理二进制数据,一个Buffer类似一个整数数组,但是它不存在V8堆内存内,大家知道一般32位系统V8引擎大概。会用掉0.7G的内存,64位的系统大概会用掉大约1.4G的内存,这都是给V8用的内存空间,是堆内内存,但是Buffer是堆外的,不会和V8公用一处内存的,这点必须要要清楚。

Buffer支持的几种编码格式

  • ‘ascii’ - 仅用于 7 位 ASCII 字符。这种编码方法非常快,并且会丢弃高位数据。
  • ‘utf-8’ - 多字节编码的 Unicode 字符。许多网页和其他文件格式使用 UTF-8。
  • ‘ucs2’ - 两个字节,以小尾字节序(little-endian)编码的 Unicode 字符。
  • ‘base64’ - Base64 字符串编码。
  • ‘binary’ - 将原始二进制数据转换成字符串的编码方式,仅使用每个字符的前8位。这种编码方法已经过时,应当尽可能地使用Buffer对象。Node 的后续版本将会删除这种编码。

Buffer的基本使用

Buffer主要分为三个部分,创建Buffer,读取Buffer,写入Buffer, 🌰 如下:

创建Buffer

1
2
var a = new Buffer(10); /* 新建Buffer */
console.log(a);

未完待续…

NPM必须掌握的基础概念

什么是npm?

npm其实就是一个包管理器,是方便JavaScript开发者分享和复用代码而诞生的,现在npm上面已经有丰富的module供开发者使用了,对于开发者这是一种新的共享和复用代码的方式,而且还能更方便的维护各种代码版本之间的关系。
npm

安装npm

一般只要安装nodejs之后,npm也会自动安装成功,如果npm版本太低,可以执行下面的命令进行npm版本的更新:

$ sudo npm install npm -g

npm上发布的包,基本都可以通过下面这个链接看到每个包的具体信息情况:
https://registry.npmjs.org/XXX(XXX是包的名字)
例如memwatch的各种历史版本信息(一个nodejs的内存监控工具)。
npm install是一个经常会用到的命令,–save和–registry参数也会经常使用

$ npm install XXX --save --registry=XXX

–save的意思:安装完成之后,会自动写入到你的packjson.json的dependencies里面。
–registry的意思:你这个包可以从哪个源来下载,不加这个参数的时候默认是从npm上面来下载,但是由于GFW的原因,有时候可能会下载失败,这时候我们也可以添加国内的镜像,例如:淘宝的美团的,淘宝的的镜像每隔10分钟会与官方服务器同步一次,用淘宝的源一般不会安装失败。
卸载某一个包就是把install换成uninstall就好了

npm 发布自己的module

基本的步骤如下:

$ npm login
$ npm init 
$ npm config set scope username
$ vim /{project}/{index.js}
$ npm publish --access=public /*--access=public参数是选择性的,如果不添加则该package只能指定用户看到*/

自己的module如何进行更新,动态版本号是npm的一个比较方便的约定,分为三种,patch,minor,major,举个例子:

  • Patch releases: 1.0 or 1.0.x or ~1.0.4,顾名思义patch就是补丁的意思,更新的是第三位数字
  • Minor releases: 1 or 1.x or ^1.0.4,Minor意思是较少的,更新的是第二位数字
  • Major releases: * or x,Major意思是主要的意思,更新的是第一位数字
    那么问题来了,当你的一个npm package进行了一些补丁修复或者是主要的更新之后,怎么样生成新版本号,执行下面的命令就可以了:
$ npm version patch
$ npm version minor
$ npm version major

与此同时package.json里面的版本号就会动态更新了。
node.js 的模块管理中,最常用到的几种动态版如下:

  • *: 任意版本
  • 1.1.0: 指定版本
  • ~1.1.0: >=1.1.0 && < 1.2.0
  • ^1.1.0: >=1.1.0 && < 2.0.0

其中 ~ 和 ^ 主要有两种前缀,简单的来说:
~ 前缀表示,安装大于指定的这个版本,并且匹配到 x.y.z 中 z 最新的版本。
^ 前缀在 ^0.y.z 时的表现和 ~0.y.z 是一样的,然而 ^1.y.z 的时候,就会 匹配到 y 和 z 都是最新的版本。
特殊的是,当版本号为 ^0.0.z 或者 ~0.0.z 的时候,考虑到 0.0.z 是一个不稳定版本, 所以它们都相当于 =0.0.z。

~其实就是从尾部开始限制序号(0除外),^其实就是从头部开始限制序号(0除外)。

还有一种情况是2.1.*是指major和minor固定,patch是任意的.

如果实在想不明白请移步这里semver,这个npm package可以帮你,下面也有详细的注释讲版本号到底应该怎么算的。

npm 增加 tags

npm publish 默认是发布的最新版本,如果参数增加–tag {name},则该版本就有了{name}的一个代号,这样如果npm install的时候就可以安装某个代号版本的npm package。

$ npm publish --tag beta
$ npm install somepkg@beta

上面这些知识点基本可以算是npm 入门必备知识了,后续会陆续更新npm进阶知识。

完…

Nginx反向代理

引言

Nginx
网上讲Nginx的文章也很多,各种入门的深入的也都有,这里不做太多过深的研究,只是罗列一下我在配置过程中遇到的问题以及如何解决的。

反向代理

代理估计大家也都知道是什么意思,按照字面意思反向代理的意思也不难理解,Nginx作为一个高性能的HTTP和反向代理服务器,现在已经被各大互联网公司采用,也可以作负载均衡,它的性能各方面比Apache优势要明显很多,所以这也是它现在这么受欢迎的原因。

端口的转发

如果你的Server不安装Nginx,那么备案之后,以及默认配置,外网访问的都是80端口,当然你也可以把服务启动起来,指定80端口就好了,外面用户敲域名回车也能访问你的网站。但是如果一台Server想通过多个不同端口启动多个服务,配置很多的子域名打到对应的端口的服务的时候,Nginx就必须搞起来了。

安装Nginx

一般新买的服务器都会有Yum环境,如果没有,自行安装下,Yum命令是在Fedora和RedHat以及SUSE中基于rpm的软件包管理器,主要是方便系统管理人员更精细化更快速的管理和安装各种软件包,Yum环境有了之后,就可以按照这个链接Nginx安装教程给的教程安装了。

增加子域名

域名在哪里买的,就去那个服务商网站登录,进入域名解析设置里面,多设置一个A记录类型,主机记录是你起的子域名名称(举个 🌰 :你设成abc,那么你就有了一个abc.domain.com的子域名),然后记录值还是你的Server的公网ip地址,TTL选择最短的时间,然后保存,一般很快就能生效了,这样你就给你的域名开了一个新的子域名。

Nginx.conf如何配置

举个 🌰 ,就拿我自己的站点来说,除了默认的codefilled.com和www以外,我还有一个m.codefilled.com的子域,现在我想启动两个服务,端口号一个是4000和3000,主站服务端口号4000(但是用户默认访问的是80),m站的端口指到3000端口上面启动的服务,这样的需求,Nginx应该如何配置,可以看一下下面的配置代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
include /etc/nginx/conf.d/*.conf;
server_names_hash_bucket_size 64;
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name codefilled.com www.codefilled.com; /*配置多个,也可以拿正则来匹配,例如:www.* */
# Load configuration files for the default server block.
include /etc/nginx/default.d/*.conf;

location / {
proxy_pass http://127.0.0.1:4000;
}

error_page 404 /404.html;
location = /40x.html {
}

error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
}

*注:上面的配置是监听的80端口,3w和没有3w的80统一都转发到4000端口的服务,server_names_hash_bucket_size 64 增加这行的原因是添加多个server_name之后,保存Nginx配置会提示server_names_hash_bucket_size不足,把size变大之后就能解决,如果64不够,你再用128这样就可以了。

1
2
3
4
5
6
7
8
9
10
server {
listen 80;
server_name m.codefilled.com;

include /etc/nginx/default.d/*.conf;

location / {
proxy_pass http://127.0.0.1:3000;
}
}

*注:上面这是配置是把m站的80端口代理到3000。

修改完etc/nginx/nginx.conf之后,保存,执行下面的命令:

1
$ nginx -s reload

就能生效了,然后启动各自服务的守护进程,就能正常访问了。

Nginx配置整站gzip压缩

gzip是一种改进web应用程序性能的一种压缩技术,大流量的WEB站点常常使用GZIP压缩技术来让用户感受更快的速度。这一般是指服务器中安装的一个功能,当有人来访问这个服务器中的网站时,服务器中的这个功能就将网页内容压缩后传输到来访的电脑浏览器中显示出来。一般对纯文本内容可压缩到原大小的40%左右。这样传输就快了,效果就是你点击网址后会很快的显示出来。配置的代码如下:

1
2
3
4
5
6
7
8
9
# open gzip
gzip on;
gzip_min_length 1k;
gzip_buffers 4 16k;
# gzip_http_version 1.0;
gzip_comp_level 2;
gzip_types text/plain application/x-javascript application/javascript text/css application/xml text/javascript image/jpeg image/jpg image/png image/gif
gzip_vary on;
gzip_disable "MSIE [1-6]\.";

大致意思就是开启gzip,大于1K的才进行压缩,压缩级别1-10,压缩类型js、主文档、jpg、png、css、gif,IE1~6对gzip支持不好,关闭。

查看Nginx哪些端口被占用

执行 $ss -tnl

1
2
3
4
5
6
7
8
9
10
$ss -tnl
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 128 *:80 *:*
LISTEN 0 128 *:22 *:*
LISTEN 0 100 127.0.0.1:25 *:*
LISTEN 0 128 *:4000 *:*
LISTEN 0 128 :::8080 :::*
LISTEN 0 128 :::80 :::*
LISTEN 0 128 :::22 :::*
LISTEN 0 128 :::3000 :::*

就能看到结果列表了

完…