Node.js 的 http 模块

2019-04-18 09:34栏目:ca888圈内

在线调试方案的思考与实践

2015/08/28 · HTML5 · 调试

原文出处: 李靖(@Barret李靖)   

本文的要点不在移动端调试上,移动端调试无非就是调试页面和调试工具之间存在分离,消除这种分离并创建连结就能解决移动端的调试问题。重点阐述的是所见即所得的调试模式下会遇到的阻碍。

当我们打开网页,发现一个模块没有正确地渲染或者空白时,如果控制台有报错,会直接根据报错定位到源码位置开始 debug;如果控制台没有报错,则会根据模块名或者模块特征的一个值,通过全局搜索找到这个模块的位置,然后在调试工具中断点,单步调试,找到问题所在,此时我们可能会这样做:

情形一:

小A同学打开控制台,发现断点调试不好写代码,于是将压缩的源码复制一份保存到本地,格式化,然后将线上资源通过代理工具代理到本地文件。

情形二:

小B同学早早的为自己配了一份本地开发环境,于是他遇到问题之后,直接去源码中定位错误位置,由于使用的是预处理语言,所以需要先打包编译之后再在本地预览效果。

情形三:

小C同学的调试方式是小A和小B的综合版本,将线上的资源代理到本地 build 目录文件,在 src 目录下修改之后编译打包到 build,然后预览。

es6:

1.let/const

2.对象里的方法function可以省略

3.函数中的默认参数

4.template

5.箭头函数

6.展开操作符

7.解构赋值

8.Symbol

9.class 模拟类,但本质是基于原型的

10.for ...of

11.promise 解决回调金字塔.回调地狱

12.生成器函数,可以退出函数

13.JS模块化

 

实验简介

http模块主要用于创建http server服务,此次实验还会讲到url模块和path模块,同时也会用到前面讲过的fs模块。url模块用于解析url,path模块用于处理和转换文件路径。

 通过前面的实验,相信大家对Node.js模块的使用已经比较熟悉。在这个实验中,我们就通过编写一个简单的http server来学习http模块。

一、创建http server

通过Node.js创建http server非常简单,示例代码如下:
<pre>
/ 引入http模块
var http = require('http');
// 创建http server
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello Worldn');
}).listen(1337, '127.0.0.1');
console.log('Server running at ');
</pre>
运行此文件:<pre>$ node demo.js</pre>
然后打开虚拟机浏览器,访问
就会看到页面上显示了Hello World,说明我们的http server创建成功了。

当然,我们在这个实验要做的比这个稍微复杂一点。
在这个实验中,我们会创建一个简单的http server,所有的代码都放在app这个文件夹中。首先,新建一个文app件夹,在文件夹中新建server.js
文件,输入如下代码(其中的注释为代码解释):

<pre>
// 创建http server
//
// 加载所需模块
var http = require('http');
var url = require('url');
var fs = require('fs');
// 设置ip和端口
// 实际应用中,可以把这些写到配置文件中
var host = '127.0.0.1',
port = 8080;
// 创建http server
function start(route, handle) {
// 参数
// route 判断url是否存在,存在则调用handle处理,不存在则返回404
// handle 处理不同的url请求
// 处理request请求
function onRequest(req, res) {
// 使用url.parse()方法解析url
// 它会把url string转化为一个object
// 这样我们就可以很方便的获取url中的host、port、pathname等值了
var pathname = url.parse(req.url).pathname;
console.log('Request for ' pathname ' received.');
// 判断并处理不同url请求
// 后面介绍此方法
route(handle, pathname, res, req);
}
// 使用http.createSserver()方法创建http server
// 并传入onRequest()方法
// 然后使用listen()方法监听指定地址
http.createServer(onRequest).listen(port, host);
console.log('Server has started and listening on ' host ':' port);
}
// 导出 start 方法
exports.start = start;
</pre>
在文件的最后,我们导出了start方法,以便在主程序中使用。你肯定注意到了,在代码中使用了route()方法,它用于处理判断请求的url是否存在,现在我们就来编写这个方法。

☞ 开启懒人调试模式

当看到线上出现问题(可能是其他同学负责页面的问题),脑中浮出这样的场景:

复制代码 我:"嘿,线上有问题啦!我要调试代码!" 电脑:"好的,主人。请问是哪个页面?"(弹出浮层) 我:浮层中输入URL。 电脑:"请问是哪个地方出问题了?" 我:(指着电脑)"模块A和模块B。" 电脑:正在下载A、B资源...正在将上线A、B映射到本地...自动打开A、B对应文件夹 我:编辑代码,然后实时预览效果。

1
2
3
4
5
6
7
8
复制代码
  我:"嘿,线上有问题啦!我要调试代码!"
电脑:"好的,主人。请问是哪个页面?"(弹出浮层)
  我:浮层中输入URL。
电脑:"请问是哪个地方出问题了?"
  我:(指着电脑)"模块A和模块B。"
电脑:正在下载A、B资源...正在将上线A、B映射到本地...自动打开A、B对应文件夹
  我:编辑代码,然后实时预览效果。

在这里我们需要解决这样几个问题

  • 将页面对应的所有仓库/资源罗列在用户面前
  • 下载资源的权限提示和权限处理
  • 线上资源解 combo,然后映射到本地

当然调试之后,可以还有一个操作:

我:"哈,已经修复了,帮我提交代码~" 电脑:正在diff代码...收到确认提交信号,提交到预发环境...收到已经预览信号...正在发布代码...收到线上回归信号...流程结束

1
2
我:"哈,已经修复了,帮我提交代码~"
电脑:正在diff代码...收到确认提交信号,提交到预发环境...收到已经预览信号...正在发布代码...收到线上回归信号...流程结束

除了 debug 代码,我们需要做的就只是用眼睛看效果是否 ok,整个流程优化下来,体验是很赞的!

NodeJS:

1.node.js开发环境搭建

2.npm nodejs包管理器

3.CommonJS模块化标准化,AMD(require)

4.导出模块/加载模块

gulp
gulp是前端开发过程中对代码进行构建的工具,是自动化项目的构建利器;她不仅能对网站资源进行优化,而且在开发过程中很多重复的任务能够使用正确的工具自动完成;使用她,我们不仅可以很愉快的编写代码,而且大大提高我们的工作效率。
gulp是基于Nodejs的自动任务运行器, 她能自动化地完成 javascript/coffee/sass/less/html/image/css 等文件的的测试、检查、合并、压缩、格式化、浏览器自动刷新、部署文件生成,并监听文件在改动后重复指定的这些步骤。在实现上,她借鉴了Unix操作系统的管道(pipe)思想,前一级的输出,直接变成后一级的输入,使得在操作上非常简单。通过本文,我们将学习如何使用Gulp来改变开发流程,从而使开发更加快速高效。
gulp 和 grunt 非常类似,但相比于 grunt 的频繁 IO 操作,gulp 的流操作,能更快地更便捷地完成构建工作。
gulp.task(name, fn)这个你应经见过了
gulp.run(tasks...)尽可能多的并行运行多个task
gulp.watch(glob, fn)当glob内容发生改变时,执行fn
gulp.src(glob)返回一个可读的stream
gulp.dest(glob)返回一个可写的stream

三、创建主程序

创建http server,判断url,处理url都写完了,那么我们可以写主程序来运行http server了,在app文件夹新建main.js文件,输入如下代码:
<pre>
// 主程序
// 引入server,router及requestHandler
var server = require('./server');
var router = require('./router');
var requestHandlers = require('./requestHandlers');
// 保存url处理方法
var handle = {};
handle['/'] = requestHandlers.home;
handle['/about'] = requestHandlers.about;
// 启动http server
server.start(router.route, handle);
</pre>
到此,所有的服务器代码都写完了,那么我们来添加代码中用到的两个html文件吧。

☞ 在线调试实践(一个系统的调试工具)

输入需要调试的页面URL(如 http://www.taobao.com):

ca888会员登录 1

插件会分析 DOM,遍历拿到页面所有被引用到的仓库:

ca888会员登录 2

选择需要调试的模块(颗粒度细分到了html/js/css),点击调试按钮,可以看到调试页面的资源都会引用本地下载的文件。

socketio 是对websocket协议封装的一个模块,让客户端和服务器端的写法都是一样的

socket.emit()发数据

socket.on()收数据

监听客户端连接,回调函数会传递本次连接的socket

io.on('connection',function(socket));

给所有客户端广播消息

io.sockets.emit('String',data);

给指定的客户端发送消息

io.sockets.socket(socketid).emit('String', data);

监听客户端发送的信息

socket.on('String',function(data));

给该socket的客户端发送消息

socket.emit('String', data);

3.WebSocket

new Websocket("ws:localhost/chat")

var condition = true; // TODO: add business logic

四、创建HTML文件

在app文件夹中新建views文件夹,在views文件夹中,新建home.html文件、about.html文件和404.html文件。
文件中的代码如下所示:
home.html文件:
<pre>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Home page</title>
</head>
<body>
<p>home page</p>
</body>
</html>
</pre>
about.html文件:

<!DOCTYPE html>
<pre>
<html>
<head>
<meta charset="utf-8">
<title>About page</title>
</head>
<body>
<p>about page</p>
</body>
</html>
</pre>
404.html文件:
<pre>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>404 page</title>
</head>
<body>
<p>404 page not found</p>
</body>
</html>
</pre>
HTML文件的代码写得比较简单,可自由发挥。

那么现在我们来运行程序吧:
<pre>$ node main.js</pre>
运行成功后,打开虚拟机桌面的浏览器,
访问“ ==>就会看到页面显示“home page”,
访问“http://127.0.0.1:8080/about” ==>就会看到页面显示“about page”,
访问 ==>下的其他页面就会看到页面显示404 page not found。

☞ 代理调试的烦恼

而对于比较复杂的线上环境,代理也会遇到很多障碍,比如:

线上资源 combo

出现错误的脚本地址为  ,它对应着 a.js,b.js,c.js 三个脚本文件,如果我们使用 Fiddler/Charles 这样的经典代理工具调试代码,就必须给这些工具编写插件,或者在替换配置里头加一堆判断或者正则,成本高,门槛高。

线上代码压缩

打包压缩,这是上线之前的必经流程。由于我们在打包的环节中并没有考虑为代码添加 sourceMap,而线上之前对应 index-min.jsindex.js 也因为安全方面的原因给干掉了,这给我们调试代码造成了极大的不便利。

代码依赖较多,拉取代码问题

很多时候,我们的页面依赖了多个 asserts 资源,而这些资源各自分布在多个仓库之中,甚至分布在不同的发布平台上,为了能够在源码上清晰的调试代码,我们不得不将所有的资源下载到本地,期间一旦存在下载代码的权限问题,整个调试进度就慢下来,这是十分不能忍受的事情。比如某系统构建的页面,页面上的模块都是以仓库为维度区分的,一个页面可能对应了5-50个仓库,下载代码实为麻烦。

最可怕的调试是,本地没有对应的测试环境、代理工具又不满足我们的需求,然后就只能, 编辑代码->打包压缩->提交代码->查看效果->编辑代码->... ,如果你的项目开发是这种模式,请停下来,思考调试优化方案,正所谓磨刀不误砍柴工。

gulp

npm init

npm install gulp --save-dev

新建 gulpfile.js(默认执行的文件)

//gulpfile.js

//1.安排任务, 2.自动执行

var gulp = require("gulp");

var uglify = require("gulp-uglify");

//task1 定义一个任务ufligy 压缩js

gulp.task("uglify" ,function() {

gulp.src("./src/js/*.js")

.pipe(uglify())

.pipe(gulp.dest("./dist/js"))

})

安装gulp-uglify插件 npm install gulp-uglify --save-dev

新建src文件夹 > js文件夹 >demo.js

新建dist文件夹> js文件夹 >

运行 gulp uglify

然后在dist文件夹就能找到已经压缩的demo.js

插件:gulp-less

插件:gulp-minify-css

//文件看守, 监听./src/less/*.less 文件的变化,变化运行该less任务

gulp.task("default", ["uglify", "less"], function() {

gulp.watch("./src/less/*.less", ["less"]);

ca888会员登录,})

//运行 gulp即可即使更改代码

express

serve-favicon
Node.js middleware for serving a favicon.
Examples

Typically this middleware will come very early in your stack (maybe even first) to avoid processing any other middleware if we already know the request is for /favicon.ico.

express

var express = require('express')
var favicon = require('serve-favicon')
var path = require('path')

var app = express()
app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')))

// Add your routes here, etc.

app.listen(3000)


morgan
morgan是express默认的日志中间件,也可以脱离express,作为node.js的日志组件单独使用
var express = require('express');
var app = express();
var morgan = require('morgan');

app.use(morgan('short'));
app.use(function(req, res, next){
res.send('ok');
});

app.listen(3000);
ode basic.js运行程序,并在浏览器里访问 ,打印日志如下

➜ 2016.12.11-advanced-morgan git:(master) ✗ node basic.js
::ffff:127.0.0.1 - GET / HTTP/1.1 304 - - 3.019 ms
::ffff:127.0.0.1 - GET /favicon.ico HTTP/1.1 200 2 - 0.984 ms


express 的 模板引擎
jade 和 ejs

jade
index.jade
doctype
html
head
title hello
body
h1 hello world

接着在命令行执行 jade -P index.jade ,编译后的文件 index.html 内容如下:
<!DOCTYPE html>
<html>
<head>
<title>hello</title>
</head>
<body>
<h1>hello world</h1>
</body>
</html>

ejs
特性

<% %> 用于控制流

<%= %> 用于转义的输出

<%- %> 用于非转义的输出

 


handlebars
模板引擎
在模板中使用{{ }}或{{{ }}}的地方就是表示
{{ }} 会转译标签
{{{ }}} 则直接替换

const productTemplateContent = fs.readFileSync(path.join(__dirname, '../templates/product.xml.hbs'), 'utf-8');
const productTemplate = handlebars.compile(productTemplateContent);
let html = productTemplate({ product: product, productJson: (new Buffer(JSON.stringify(product))).toString('base64') });

 


crypto
node自带模块.这个模块的主要功能是加密解密。
var md5 = crypto.createHash(‘md5’);
var sha1 = crypto.createHash('sha1');

 


needle
Nimble, streamable HTTP client for Node.js. With proxy, iconv, cookie, deflate & multipart support.
轻量级的http client模块,集成了iconv-lite,跟request类似
var needle = require('needle');

needle.get('', function(error, response) {
if (!error && response.statusCode == 200)
console.log(response.body);
});

Callbacks not floating your boat? Needle got your back.

var data = {
file: '/home/johnlennon/walrus.png',
content_type: 'image/png'
};

needle
.post('', data, { multipart: true })
.on('readable', function() { /* eat your chunks */ })
.on('done', function() {
console.log('Ready-o, friend-o.');
})

needle.post(url, data, requestOptions, function(err, resp, body) {
let data;
if (body) {
let bodyText = body.toString('utf-8');
try {
data = JSON.parse(bodyText);
} catch (e) {
data = bodyText;
}
}
mainCallBack(err, body);
});


superagent
superagent是nodejs里一个非常方便的客户端请求代理模块,当你想处理get,post,put,delete,head请求时,你就应该想起该用它了:)

superagent 是一个轻量的,渐进式的ajax api,可读性好,学习曲线低,内部依赖nodejs原生的请求api,适用于nodejs环境下.

一个简单的post请求,并设置请求头信息的例子

request
.post('/api/pet')
.send({ name: 'Manny', species: 'cat' })
.set('X-API-Key', 'foobar')
.set('Accept', 'application/json')
.end(function(res){
if (res.ok) {
alert('yay got ' JSON.stringify(res.body));
} else {
alert('Oh no! error ' res.text);
}
});

SuperAgent请求的默认方法为GET,所以你可可以简单地写如下代码:

request('/search', function(res){

});



 

二、创建路由

在app文件夹中新建router.js,输入如下代码:
<pre>
var fs = require('fs');
// 路由函数
// 处理不同url的请求
// 并返回相应内容
function route(handle, pathname, res, req) {
console.log('About to route a request for ' pathname);
// 判断此url是否存在特定处理函数
// 存在则调用handle处理
// 不存在则返回404页面
if (typeof handle[pathname] === 'function') {
// 后面介绍handle函数
handle[pathname](res, req);
} else {
console.log('No request handler found for ' pathname);
// 读取404页面
// 所有页面都存放在view文件夹下
var content = fs.readFileSync('./views/404.html');
res.writeHead(404, { 'Content-Type': 'text/html' });
res.write(content);
res.end();
}
}
// 导出 route 方法
exports.route = route;
</pre>
在此方法中,调用了handle()方法,这个方法用于处理不同的url请求。
在app文件夹中新建requestHandlers.js文件,输入如下代码:
<pre>
// 处理url请求
var fs = require('fs');
// home.html 主页
function home(res) {
console.log('Request handler "home" was called.');
// 读取home.html文件
var content = fs.readFileSync('./views/home.html');
res.writeHead(200, { 'Content-Type': 'text/html' });
res.write(content);
res.end();
}
// about.html 关于页面
function about(res) {
console.log('Request handler "about" was called.');
// 读取about.html文件
var content = fs.readFileSync('./views/about.html');
res.write(200, { 'Content-Type': 'text/html' });
res.write(content);
res.end();
}
// 导出页面处理函数
exports.home = home;
exports.about = about;
</pre>
这个方法比较简单,就是读取文件,然后输出到response。

版权声明:本文由ca888发布于ca888圈内,转载请注明出处:Node.js 的 http 模块