2

Node.js不是JS应用、而是JS运行平台

看到Node.js这个名字,初学者可能会误以为这是一个Javascript应用,事实上,Node.js采用C++语言编写而成,是一个Javascript的运行环境。为什么采用C++语言呢?据Node.js创始人Ryan Dahl回忆,他最初希望采用Ruby来写Node.js,但是后来发现Ruby虚拟机的性能不能满足他的要求,后来他尝试采用V8引擎,所以选择了C++语言。既然不是Javascript应用,为何叫.js呢?因为Node.js是一个Javascript的运行环境。提到Javascript,大家首先想到的是日常使用的浏览器,现代浏览器包含了各种组件,包括渲染引擎、Javascript引擎等,其中Javascript引擎负责解释执行网页中的Javascript代码。作为Web前端最重要的语言之一,Javascript一直是前端工程师的专利。不过,Node.js是一个后端的Javascript运行环境(支持的系统包括*nux、Windows),这意味着你可以编写系统级或者服务器端的Javascript代码,交给Node.js来解释执

Node.js采用了Google Chrome浏览器的V8引擎,性能很好,同时还提供了很多系统级的API,如文件操作、网络编程等。浏览器端的Javascript代码在运行时会受到各种安全性的限制,对客户系统的操作有限。相比之下,Node.js则是一个全面的后台运行时,为Javascript提供了其他语言能够实现的许多功能。

Node.js采用事件驱动、异步编程,为网络服务而设计
事件驱动这个词并不陌生,在某些传统语言的网络编程中,我们会用到回调函数,比如当socket资源达到某种状态时,注册的回调函数就会执行。Node.js的设计思想中以事件驱动为核心,它提供的绝大多数API都是基于事件的、异步的风格。以Net模块为例,其中的net.Socket对象就有以下事件:connect、data、end、timeout、drain、error、close等,使用Node.js的开发人员需要根据自己的业务逻辑注册相应的回调函数。这些回调函数都是异步执行的,这意味着虽然在代码结构中,这些函数看似是依次注册的,但是它们并不依赖于自身出现的顺序,而是等待相应的事件触发。事件驱动、异步编程的设计(感兴趣的读者可以查阅笔者的另一篇文章《Node.js的异步编程风格》),重要的优势在于,充分利用了系统资源,执行代码无须阻塞等待某种操作完成,有限的资源可以用于其他的任务。此类设计非常适合于后端的网络服务编程,Node.js的目标也在于此。在服务器开发中,并发的请求处理是个大问题,阻塞式的函数会导致资源浪费和时间延迟。通过事件注册、异步函数,开发人员可以提高资源的利用率,性能也会改善。

从Node.js提供的支持模块中,我们可以看到包括文件操作在内的许多函数都是异步执行的,这和传统语言存在区别,而且为了方便服务器开发,Node.js的网络模块特别多,包括HTTP、DNS、NET、UDP、HTTPS、TLS等,开发人员可以在此基础上快速构建Web服务器。

Node是一种新型的web服务器,他的核心理念是事件驱动编程,主要的特点:
1)Node是单线程的
2)它与平台无关
Express是一个灵活的,简洁的Nodejs Web程序框架,Express跟Connect有着紧密的联系,Connect是一个Node的插件库,他能不同程度上处理web请求,在version 4.0之前,Express一直绑定Connect,在4.0中,Connect以及除了static之外的所有中间件都被去掉了,以便这些中间件可以独立升级.
1,hello world

var http = require('http')
http.createServer(function(req,res){
    res.writeHead(200,{'Content-Type':'html/text'})
    var path = req.url
    switch(path){
        case '':
            res.writeHead(200,{'Content-Type':'html/text'})
    }
}).listen(3000)
console.log("out.print successfully...")

2,路由
路由是指向客户端提供他所发出的请求内容机制。

var http = require('http')
http.createServer(function(req,res){
    var path = req.url.replace(/\/?(?:\?.*)?$/,'').toLowerCase()//优化url,去掉查询字符串(?a=b),可选的反斜杠(/*),并变成小写
    switch(path){
            res.writeHead(200,{'Content-Type':'text/plain'})
            res.end('homepage')
            break
        case'/about':
            res.writeHead(200,{'Content-Type':'text/plain'})
            res.end('about')
            break
        default:
            res.writeHead(200,{'Content-Type':'text/plain'})
            res.end('Not found')
            break
    }
}).listen(3000)
console.log("out.print successfully...")

3,静态资源服务
用node提供静态资源只适用于初期的小项目,对于大的项目应该会想到使用Nginx或CDN之类的代理服务器来提供静态资源。
对于node,我们先要打开文件,读取其中的内容,然后将这些内容发送给浏览器。

var http = require('http')
var  fs = require('fs')//FS(File System)文件系统操作函数
function serveStaticFile(res,path,contentType,responseCode){//辅助函数
    if(!responseCode) responseCode = 200
    fs.readFile(__dirname+path,function(err,data){//__dirname是一个全局变量,注意是2个下滑线"_"表示解析正在执行脚本所在的目录,fs.readFile是一个读取文件的异步方法
      if(err){
          res.writeHead(500,{'Content-Type':'text/plain'})
          res.end('500 -Internal Error')
      }else{
          res.writeHead(responseCode,{'Content-Type':contentType})
          res.end(data)
      }
    })
}
http.createServer(function(req,res){
    var path = req.url.replace(/\/?(?:\?.*)?$/,'').toLowerCase()//优化url,去掉查询字符串(?a=b),可选的反斜杠(/*),并变成小写
    switch(path){
        case'':
             serveStaticFile(res,'/public/home.html','text/html')
            break
        case'/about':
             serveStaticFile(res,'/public/about.html','text/html')
            break
        case'/img':
             serveStaticFile(res,'/public/img/logo.jpg','image/jpeg')
            break
        default:
            serveStaticFile(res,'/public/404.html','text/html',404)
            break
    }
}).listen(3000)
console.log("out.print successfully...")

Express框架
1,脚手架:大多数的项目都需要一定数量的套路化代码,为了节约时间避免写重复代码,最通用的方法就是创建一个通用的项目骨架,每一个写新的项目只需要用这个骨架,或是这个模板。express就是借鉴了RoR,提供了一个生成脚手架的工具,从而可以开始一个新的express项目。

2,安装express
npm install --save express
--save选项是为了保证会更新package.json,因为node_modules随时都可以生成,所以我们不会把这个目录保存到我们的代码库中,为了这一点我们可以创建一个.gitignore
node 入口文件 projectName.js

引入express框架
var express = require('express')
var app = express()
设置监听端口
var app = express()
app.set('port',process.env.PORT||3000)
配置404
app.use(function(req,res){
    res.type('text/plain')
    res.status(404)
    res.send('404 - Not Found')
})
配置500
//定制500
app.use(function(err,req,res,next){
    console.error(err.stack)
    res.type('text/plain')
    res.status(500)
    res.send('500 - Server Error')
})
监听端口并且在控制台print
app.listen(app.get('port'),function(){
    console.log('http://localhost:'+app.get('port')+';')
})

在404处理器之前加上路由:

app.get('/',function(req,res){
    res.type('text/plain')
    res.send('mikeProject')
})
app.get('/about',function(req,res){
    res.type('text/plain')
    res.send('this is about mikes project')
})
//定制404页面
app.use(function(req,res,next){
    res.type('text/plain')
    res.status(404)
    res.send('404 - Not Found')
})

在express官方文档中写道app.verp,后面的http动词就是方法(get/post),这里的app.get是我们添加路由的方法,这个方法有2个参数:一个路径和一个函数
app.verb帮我们做了很多的工作,默认忽略了大小写或反斜杠并且进行匹配的时候不考虑查询字符串。
res.type:设置响应头Conten-Type
app.use:这是express添加中间件的一种方法
执行顺序:express中,路由和中间件的添加顺序非常的重要,注意执行的顺序。
3,视图和布局
在这里有一个新的视图布局,也就是视图引擎叫handlebars,为了支持handlebars,我们必须使用相关的包:

npm install --save express3-handlebars
var express = ('exrpess');//引入express框架
var app = express();
//设置handlerbars的视图引擎
var handlebars = require('express3-handlebars').create({defaultLayout:'main'});//设置为默认的视图引擎
app.engine('handlebars',handlebars.engine);
app.set('view engine','handlebars')

以上代码对express进行了配置,将其作为默认的视图引擎,接下来创建视图目录:views
将每一个公用的html代码写在母版页里面,下面是母模板的内容:

main.handlebars
<!doctype html>
<html>
  <head>
    <title>Mike's internet</title>
  </head>
  <body>
    {{{body}}}
  </body>
</html>

{{{body}}}表达式代表每个html表达式都会被这个body取代,defaultLayout:'main'
.handlebars和.hbs互用。注意{{{body}}}是三个大括号,而不是两个。
设置视图,更换路由:

app.get('/',function(req,res){
    res.render('home')
})
app.get('/about',function(req,res){
    res.render('about')
})
//定制404页面
app.use(function(req,res,next){
    res.status(404)
    res.render('404')
})
//定制500
app.use(function(err,req,res,next){
    console.error(err.stack)
    res.status(500)
    res.render('500')
})

文件排列
注意,视图引擎默认会返回text/html的内容和200状态
4,视图和静态文件
express靠中间件处理静态文件和视图。
static中间件可以将一个或多个目录指派为包含静态资源的目录,其中的资源不经过中间任何的特殊处理,直接发送到客户端(pic,css,js...),相当于给你要发的静态文件创建一个路由.
app.use(express.static(_dirname+'/public'))
问题:
发现引用静态资源引用不了,不知道什么原因.
答案:可能与电脑的配置有关系,64位的,电脑配置好一些的就可以打印出来.

app.use(express.static(__dirname+'/public'))

<body>
<header><img src="/img/logo.jpg" alt="mike"></header>
    {{{body}}}
</body>

5,视图中的动态内容
视图并不只是一种传递静态的html的复杂方式,视图真正强大之处在于它可以包含动态的信息.
请看下面的代码:

首先定义你要显示的动态数据:
//定义要在视图中动态显示的数据
var mikes=["mikes11111111",
"mikes22222222222",
"mikes3333333333",
"mikes444444444",
"mikes55555555555"]
//重新修改路由
app.get('/about',function(req,res){
    //res.render('about')
    var mikeNumber = mikes[Math.floor(Math.random()*mikes.length)]
    res.render('about',{mike:mikeNumber})
})
//修改视图
<blockquote>{{{mike}}}</blockquote>

显示内容如下:
没刷新一次,自动更改显示的数量:
动态显示
6,Node模块
Node模块和npm包是两个相互关联但又是彼此不同的概念。Node模块就像他的名字一样,提供一个模块化和封装的机制,Npm包则提供了一种存储,版本化和应用的项目,但是不限于模块的标准范式。比如,express作为模块引入:
var express = require('express');
require是一个用来引入模块的函数。
创建一个包含模块的目录:
Lib
./lib/fortune.js

var moneyCookies= [//数组moneyCookies是被完全隐藏起来的,因为没有暴露在exports上面
    "Do you want money?",
    "I have 5$,please keep it.",
    "you are welcome,you can have it."
]

exports.getMoney = function(){//如果你想让一个东西在模块外可见,那就必须把它加到exports上面
    var idm = Math.floor(Math.random()*moneyCookies.length);
    return moneyCookies[idm]
}

var fortune = require('./lib/fortune.js')//引入模块

app.get('/about',function(req,res){
    //res.render('about')
    // var mikeNumber = mikes[Math.floor(Math.random()*mikes.length)]
    // res.render('about',{mike:mikeNumber})
    res.render('about',{ fortune: fortune.getMoney()});
});
git add -A
git commit -m "commit for updating about module."

7,页面测试
为了把测试真正嵌入到页面中,是为了做一个页面的时候,在浏览器中一加载页面就可以马上发现所有的错误。这里需要引入一个测试的框架 mocha
npm install --save-dev mocha
--save-dev instead of --save,是告诉npm把这个包放在开发依赖中,不要放在运行依赖中,这样在部署依赖的时候可以减少项目的依赖项,因为mocha要做bowser中运行,所以将放到public目录中,创建子目录:public/vendor
另外,测试还需要一个assert/expect函数,node中有这个函数,bowser中没有,所以:
npm install --save-dev chai
然后把chai.js放到public/vendor里面

为了不影响网站的速度而且还要看到测试的结果,得准备一个路由,放在所以路由之前:

node相关问题:
新版的express中已经不包含bodyparser
解决办法:
1,npm install body-parser
2,var bodyParser = require('body-parser')
3,把app.use(express.bodyParser())替换成app.use(bodyParser.urlencoded({ extended: false }))
4,在node中写理由的时候,由于post过来的数据需要格式化,如果发现entity undefined,就需要将app.use(bodyParser.urlencoded({ extended: true })) 该成true 就好了


MichaelDuan
1.8k 声望39 粉丝