简介

对于前端(Front-End)来说,开发语言方面有且只有HTML、CSS、JavaScript这三种,至于VUE之类的框架也无非是基于这些语言之上的拓展。

而对于后端(Back-End)来说,可用的开发语言就很多了,例如我常用的Python、最常用的PHP、经久不衰的Java、.Net、Ruby、Go等等,各有自己的特色。

而今天的主角,就是后端开发语言中的后起之秀Node.js。

有句话叫做,凡是能用JavaScript来实现的应用,最终都会用JavaScript来实现。

而Node.js其实就是一个构建于Chrome V8 引擎之上的JavaScript运行环境,可以在非浏览器的环境下解析和执行JavaScript代码。

相对于浏览器中的JavaScript来说,Node.js拥有相同的EcmaScript语法,但不存在BOM和DOM(不能访问所谓的网页元素),但新增了浏览器缺乏的文件读写、网络服务等服务器级别的后端API。

Node.js的特色是event-driven(事件驱动)、non-blocking I/O model(非阻塞IO模型)、lightweight and efficient(轻量高效),可以通过npm这个方便的开源包管理工具管理第三方开源包。

 

安装

安装的话很简单,可以去官网下载安装,目前最新版本已经到了15.4,注意由于14.X版本开始不再支持Windows7系统,因此如果是Windows7系统可以到历史版本中选择13.X等早期版本安装。

安装完成后直接在命令行中输入node运行,如果顺利进入Node.js的命令行界面就说明安装成功。

 

导入模块

在Node.js中,较常用的核心模块有fs(文件操作)、http(http服务)、path(路径操作)、os(操作系统信息)等,可以通过require()方式导入使用。

用户自定义或者下载的其他模块,也可以通过require()方式导入使用(类似Python的import),相对路径的“./”不能省略,但可以省略“.js”后缀名。

另外传统的JavaScript只能通过HTML页面中的script标签来导入第三方模块,而在Node.js中可以通过@import()方式引用其他js文件中的函数。

 

文件读写

在Node.js中如果想要进行文件操作,必须要引入fs这个核心模块,它提供了文件操作相关的API。

// 加载fs核心模块
var fs = require('fs')

 

读取文件

fs.readFile('test.txt',function(error,data){
  if (data){
    console.log(data.toString())
  }
})

读取到的是文件的二进制内容,因此需要toString转码,或者读取时使用utf8编码。

fs.readFile('test.txt', 'utf8', function(error,data){
  if (data){
    console.log(data)
  }
})

 

写入文件

fs.writeFile('E:/new.txt','fdsfdsfdsfgfdhdrhrefhrtntgrm tjnr',function(error){
  if (error){
    console.log('Write failed!');
  }
})

 

传统http服务

使用http核心模块创建http服务

var http = require('http')
var server = http.createServer()

server.on('request',function(request,response){
  console.log('Receive a request:' + request.url)
  //设置utf-8编码,防止中文乱码
  response.setHeader('Content-Type','text/plain;charset=utf-8')
  response.write('hello nodejs')
  response.end()
  // 或者直接写成response.end('hello nodejs')
})

server.listen(3000,function(){
  console.log('Server started!')
})

注意response只能返回字符串格式,因此json格式数据需要使用JSON.stringfy()格式化为字符串,前端收到后使用JSON.parse()再进行解析。

 

解析前端提交的数据

以下为get方式提交的数据处理方式

var url = require('url')
var urlObj = new URL('http://127.0.0.1:3000/get?name=node')
// 如果路径不完整的话需要拼合路径如下
// var urlObj = new URL('/get?name=node','http://127.0.0.1:3000/')
console.log(urlObj.searchParams.get('name'))

url会被处理成如下形式:

URL {
  href: 'http://127.0.0.1:3000/get?name=node&type=web',
  origin: 'http://127.0.0.1:3000',
  protocol: 'http:',
  username: '',
  password: '',
  host: '127.0.0.1:3000',
  hostname: '127.0.0.1',
  port: '3000',
  pathname: '/get',
  search: '?name=node&type=web',
  searchParams: URLSearchParams { 'name' => 'node', 'type' => 'web' },
  hash: ''
}

这样就可以方便的处理请求和数据了。

 

重定向

在网站开发中经常会有重定向到某个页面等的需求,此时可以通过302临时重定向状态码实现,在响应头中通过Location告诉客户端重定向的目标网址。

response.statusCode = 302
response.setHeader('Location','/')
response.end()

 

在Node.js中使用模板引擎

以常用的art-template模板引擎为例

//安装
npm install art-template
//使用
var template = require('art-template')

var res = template.render('hello {{ name }}', {
  name: 'Node'
})

console.log(res)   // hello Node

 

使用express库创建http服务

express是一个简洁灵活的Node.js Web应用框架,可以快速创建http服务。

//安装
npm install express

使用起来也很方便

var express = require('express')
var path = require('path')
var app = express()

// 公开目录
app.use('/public/',express.static(path.join(__dirname, './public/'))) // http://127.0.0.1:3000/public/img/test.jpg
// 或者简写为 
// app.use(express.static(path.join(__dirname, './public/'))) // http://127.0.0.1:3000/img/test.jpg

app.get('/',function(req,res){
  res.send('你好 express')
})

app.post('/login/',function(req,res){
  res.send('欢迎登陆')
})

app.listen(3000,function(){
  console.log('listening 3000')
})

可以看到可以方便地创建多个request url,快速地公开指定目录的资源,并且省去了处理url、编码、处理404等步骤,代码更加简洁灵活。

 

express中使用art-template模板引擎

// 安装
npm install art-template express-art-template

 

基础使用

var express = require('express')
var app = express()

// 当加载.art的模板文件时,使用express-art-template模板引擎
app.engine('art',require('express-art-template'))

// 默认会到views文件夹中寻找模板文件,也可以设置views文件夹路径
app.set('views','./templates/')
app.get('/',function(req,res){
  res.render('home.art')
})

// 方便地获取前端提交的数据以及重定向
app.get('/input',function(req,res){
  console.log(req.query)
  res.redirect('/')
})

app.listen(3000,function(){
  console.log('listening 3000')
})

 

获取post数据

如果需要获取post的数据的话,需要另一个第三方库body-parser的配合。

// 安装
npm install body-parser

//使用
var express = require('express')
var bodyParser = require('body-parser')

var app = express()
app.use(bodyParser.urlencoded({ extended: false}))
// 当前端提交的数据为json格式时
app.use(bodyParser.json())

app.post('/post',function(req,res){
  console.log(req.body)
})

app.listen(3000,function(){
  console.log('listening 3000')
})

 

路由表单独定义

在有很多路由时,可以将路由表单独放到一个js文件中,在app.js入口处导入。

// router.js
var express = require('express')
var router = express.Router()

router.get('/',function(req,res){
  ...
})

module.exports = router
// app.js
var express = require('express')

var app = express()
var routers = require('./router')

app.use(routers)

app.listen(3000,function(){
  console.log('listening 3000')
})

 

注意

1.Node.js中没有全局作用域,只有模块作用域,每一个js文件都是封闭的作用域。

如果需要获取自定义模块中的成员(变量和函数等),那么可以将该成员挂载到默认的exports对象上。

// b.js
var foo = "bbb"
exports.foo = foo

//a.js
var b = require('./b')
console.log(b)

//output
{ foo: 'bbb' }

这个方式也用于在b中调用a的成员,但是调用到的代码中如果有引用库,那么也得在b中引入。

// b.js
module.exports = function (apps) {
  apps.do()
}

//a.js
var app = function () {...}
var b = require('./b')
b(app)

2.当使用无分号的代码风格时,若一行代码以(、[、`开头,则须在前面补上分号,避免可能的解析错误问题。

function say () {...}
say()
;(function(){})()

3.在开发中,需要频繁重启node服务查看修改效果,可以使用nodemon这个第三方库,能检测到代码修改自动重启服务。

// 安装
npm install nodemon

// 使用
nodemon app.js

很多人觉得他们在思考,

实际上只是重新整理自己的偏见。

——弗朗西斯·培