Node.js初感知

2021-7-18 Node.js

Node.js是基于JavaScript的,不需要因为要去了解服务端而重新学一门语言

# Node.js是什么

​ node.js不是一门语言,不是库,不是框架,是一个JavaScript运行时环境(是一个解析执行js的平台),即node.js可以解析和执行JavaScript代码,和浏览器的功能有些类似,也就是说,现在的JavaScript可以完全脱离浏览器来运行,一切都归功于node.js。

# 浏览器中的JavaScript:

  • ECMAScript
  • DOM
  • BOM

# Node.js中的JavaScript:

  • 没有BOM、DOM
  • ECMAScript
  • 核心模块
    • 提供了一些服务器级别的操作API,例如文件读写,网络服务的构建,网络通信,http服务器等处理
    • 这些API绝大多数被包装到一个具名的核心模块中了
    • 核心模块使用前,必须先用require方法加载
  • 用户自定义模块(自己创建的文件)
    • require
      • 加载文件模块并执行里面的代码
      • 拿到被加载文件模块导出的接口对象
      //在a.js文件中加载执行b.js文件
      //a.js
      console.log('a start');
      require('./b.js');//相对路径同一文件下必须加./ 同时后缀名也可以省略
      console.log('a end');
      
      //b.js
      console.log('bbb');
      
      1
      2
      3
      4
      5
      6
      7
      8
    • exports
      • 把所有需要被外部访问的成员挂载到exports对象中
      //a.js
      var Exports = require('./b');//require返回的是exports对象,默认为空
      console.log(Exports.foo);
      console.log(Exports.add(10,20));
      console.log(Exports.age);
      
      //b.js
      exports.foo = 'hello';
      exports.add = function (x,y){
        return x+y;
      }
      var age = 18;
      exports.age = age;
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
  • 第三方模块(npm安装)
    • art-template

# Node.js的特性:

使用事件驱动、非阻塞IO模型(异步)让其变得轻量和高效。Node.js的作者把Google Chrome中的V8引擎移植出来,开发了一个独立的JavaScript运行时环境。npm是基于node.js开发的包管理工具,是世界上最大的开源库生态系统,绝大多数JavaScript相关的包都存放在了npm上,为了让开发人员更方便的去下载使用。

注:在Node中,没有全局作用域,只有模块作用域,即使用require加载了其他模块,两个模块也互相无法访问各自的变量。

# Node.js能做什么

  1. Web服务器后台
  2. 命令行工具,如npm(node)、git(C语言)、hexo(node,通过命令可搭建出一个博客)等

# 安装Node环境

  1. 下载: https://nodejs.org/en/download/
  2. 安装(重新下载安装会覆盖原来的,一路next)
  3. 确认Node环境是否安装成功(node --version命令查看当前Node环境的版本)

# 使用Node.js执行js脚本文件

  1. 创建编写JavaScript脚本文件
  2. 打开终端,定位到脚本文件所属目录
  3. 输入node 文件名执行对应的文件

注:文件名不要使用"node.js"来命名。

# 读写文件

  • fs是file-system的简写,就是文件系统的意思
  • 在Node中如果想要进行文件操作,就必须引入fs这个核心模块
  • 在fs这个核心模块中,提供了所有与文件操作相关的API
  • 所有文件操作的API都是异步的
//使用require方法加载fs核心模块
var fs = require('fs');

fs.readFile('文件路径',function (error, data){//第一个参数是要读取的文件路径,第二个参数是一个回调函数,回调函数的两个参数:error(读取成功时为null,读取失败时为错误对象)和data(读取成功时为数据,读取失败时为undefined)
  console.log(data.toString());
  //打印data会得到一串16进制数,但可以通过toString方法将其转为我们能认识的字符
})

//写文件,参数:文件路径、文件内容、回调函数(只有一个参数)
fs.writeFile('文件路径','写入内容',function(error){
  ...
})
1
2
3
4
5
6
7
8
9
10
11
12

# http

  • 通过Node的核心模块http可以构建一个web服务器
  • http这个模块的职责就是创建编写服务器
  • 服务器:提供对数据的服务,发请求,接收请求,处理请求,发送响应
  • 构建步骤
    1. 加载http核心模块
    2. 使用http.createServer()方法创建一个Web服务器,返回一个Server实例
    3. 接收请求,注册request请求事件,当客户端请求过来,就会自动触发服务器的request请求事件,然后执行回调处理函数,回调函数的两个参数:
      • request请求对象,请求对象可以用来获取客户端的一些请求信息,例如请求路径;
      • response响应对象,响应对象可以用来给客户端发送响应信息,response对象有一个write方法,可以用来给客户端发送响应数据,write可以使用很多次,但是最后一定要使用end来结束响应,否则客户端会一直等待,响应内容只能是二进制数据或字符串,不可以是数字,对象,数组或布尔值
      • 一次请求对应一次响应,响应结束这次请求也结束了
    4. 绑定端口号,启动服务
var http = require('http');
var server = http.createServer();

server.on('request',function(request, response){
  console.log('收到客户端的请求了,请求路径是:' + request.url);
  
  //response.write('hello');
  //response.write(' nodejs');
  //response.end();
  //或者
  //response.end('hello nodejs');
  
  //通过if...else判断request.url,根据不同请求响应不同内容
  var url = request.url;
  if(url === '/'){
    response.end('index page');
  }else if(url === '/login){
  	response.end('login page');
  }else if(url === '/products'){
  	var products = [{
      name: 'xxx',
      price: 888
    },{
      name: 'yyy',
      price:997
    },{
      name: 'zzz',
      price:334
    }];
    response.end(JSON.stringfy(products));
  }else{
    response.end('404 Not Found.');
  }
})

server.listen(3000, function(){
  console.log('服务器启动成功了,可以通过http://127.0.0.1:3000/来进行访问')
})
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
38

注:通过Ctrl+C可以关闭服务器,可以同时开启多个服务,但一定要确保不同服务占用的端口号不一致。

# Content-Type

  • 在http协议中,Content-Type就是用来告知对方发送过去的数据内容是什么类型的
  • 在服务端默认发送的数据,其实是utf8编码(国际通用编码,各国语言基本都能识别)的内容,但浏览器不知道你是utf8编码的内容
  • 浏览器在不知道服务器响应内容的编码时会按照当前操作系统的默认编码去解析,所以可能出现乱码
  • 解决方法就是让客户端和服务器端的编码方式相同
var http = require('http')
var server = http.createServer()

server.on('request',function (req,res){
  var url = req.url
  if(url === '/plain'){
    res.setHeader('Content-Type','text/plain; charset=utf-8')//text/plain指的是普通文本,解析中文时不会出现乱码
  	res.end('hello 世界')
  }else if(url === '/html'){
    res.setHeader('Content-Type','text/html; charset=utf-8')//text/html会将响应内容当作html解析,同时也不会乱码
    res.end('<p>hello html <a href="">点我</a></p>')//在浏览器显示时,是看不见p和a标签的,因为浏览器默认会解析识别html
  }
})

server.listen(3000,function (){
  console.log('Server is running...')
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  • 需求:发送各种资源给浏览器
var http = require('http')
var fs = require('fs')
var server = http.createServer()
server.on('request',function (req,res){
  var url = req.url;
  
  if(url === '/'){
    fs.readFile('./resource/index.html',function (err,data){
      if(err){
        res.setHeader('Content-Type', 'text/plain; charset=utf-8')
        res.end('文件读取失败,请稍后重试!')
      }else{
        res.setHeader('Content-Type', 'text/html; charset=utf-8')
        res.end(data)
      }
    })
  }else if(url === '/ab'){//这里的ab可随便取
    fs.readFile('./resource/ab2.jpg',function (err,data){
      if(err){
        res.setHeader('Content-Type', 'text/plain; charset=utf-8')
        res.end('文件读取失败,请稍后重试!')
      }else{
        res.setHeader('Content-Type', 'image/jpeg')//图片有自己规定的编码,不需要指定编码,一般我们常说的编码指的是字符编码
        res.end(data)
      }
    })
  }
})
server.listen(3000,function (){
  console.log('Server is running...')
})
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

不同资源对应的Content-Type是不一样的,具体参照https://tool.oschina.net/commons

# 统一处理文件资源

var http = require('http')
var fs = require('fs')
var server = http.createServer()
var wwwDir = 'D:/Movie/www'
server.on('request',function (req,res){
  var url = req.url;
  
  var filePath = '/index.html'
  if(url !== '/'){
    filePath = url
  }
  
  fs.readFile(wwwDir + filePath, function (err,data){
    if(err){
      return res.end('404 Not Found.')
    }
    res.end(data)
  })
})
server.listen(3000,function (){
  console.log('Server is running...')
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 读取目录渲染到页面

var http = require('http')
var fs = require('fs')
var server = http.createServer()
var wwwDir = 'D:/Movie/www'
server.on('request',function (req,res){
  var url = req.url;
  fs.readFile('./template.html', function (err,data){
    if(err){
      return res.end('404 Not Found.')
    }
    //1. 通过fs.readdir得到wwwDir目录列表中的文件名和目录名
    //2. 通过模板引擎将得到的文件名和目录名替换到template.html中
    //   在template.html中需要替换的位置预留一个特殊的标记,根据files生成需要的HTML内容
    fs.readdir(wwwDir, function (err, files) {
      if (err) {
        return res.end('Can not find www dir.')
      }
      console.log(files)//返回的是包含目录名的数组
      var content = ''
      files.forEach(function (item) {
        content += `
					<tr>
					  <td><a href="/D:/Movie/www/apple/">${item}</a></td>
					</tr>
			 `
      })
      data = data.toString()
      data = data.replace('^_^', content)//将data转为字符串再jin'x
      res.end(data)
    })
  })
})
server.listen(3000,function (){
  console.log('Server is running...')
})
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

# 在nodejs中使用模板引擎art-template

  1. 安装(npm install art-template)
  2. 在需要使用的文件模块中加载art-template
  3. 查文档,使用模板引擎的API
var template = require('art-template')//install什么,require里的内容就是什么
var ret = template.render('模板字符串', {替换对象})
1
2
  • 使用模板引擎渲染页面
var http = require('http')
var fs = require('fs')
var template = require('art-template')

var server = http.createServer()
var wwwDir = 'D:/Movie/www'
server.on('request',function (req,res){
  var url = req.url;
  fs.readFile('./template.html', function (err,data){
    if(err){
      return res.end('404 Not Found.')
    }
    
    fs.readdir(wwwDir, function (err, files) {
      if (err) {
        return res.end('Can not find www dir.')
      }
      //只要去template.html文件中编写模板语法进行替换就好了
      var htmlStr = template.render(data.toString(), {
        files: files
      })
      
      res.end(htmlStr)
    })
  })
})
server.listen(3000,function (){
  console.log('Server is running...')
})
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

这是在服务端渲染好页面直接给前端展示的。

# 客户端渲染和服务端渲染的区别

  • 客户端渲染不利于SEO搜索引擎优化
  • 服务端渲染是可以被爬虫抓取到的,客户端异步渲染是很难被爬虫抓取到的
  • 客户端异步渲染可以用于局部刷新

# 处理网站中的静态资源

当浏览器收到HTML响应内容之后,就会从上到下依次解析。当在解析的过程中,如果发现link,script,img,iframe,video,audio等带有 src 或者 href(link)属性标签的时候,浏览器会自动对这些静态资源发起新的请求。因此我们为了方便的统一处理这些静态资源,会约定将所有的静态资源都存放在public目录里,第三方资源放在lib文件夹下。

var http = require('http')
var fs = require('fs')

http.createServer(function (req, res) {
  var url = req.url
  if (url === '/') {
    fs.readFile('./views/index.html', function (err, data){
      if (err) {
        return res.end('404 Not Found.')
      }
      res.end(data)
    })
  } else if (url.indexOf('/public/') === 0){
    //统一处理:如果请求路径是以/public/开头的,则可直接把请求路径当作文件路径来进行读取
    fs.readFile('.' + url, function (err, data) {
      if (err) {
        return res.end('404 Not Found.')
      }
      res.end(data)
    })
  }
}).listen(3000, function () {
  console.log('running...')
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# url模块

网页中的所有路径其实都是url路径,不是文件路径

var url = require('url')
var obj = url.parse('/pinglun?name=张三&message=好的收到',true)
//使用url.parse方法将路径解析为一个方便操作的对象
//true表示将后面的查询字符串转换成对象形式(通过query属性访问)
1
2
3
4

# 通过服务器让客户端重定向

  1. 状态码设置为302临时重定向
  2. 在响应头中通过Location告诉客户端往哪儿重定向
  3. 如果客户端发现收到服务器的响应码是302,就会自动去响应头中找Location地址,然后重新发起请求,自动跳转
res.statusCode = 302
res.setHeader('Location', '/')
res.end()
1
2
3

# 文件路径中的/与模块标识中的/

var fs = require('fs')

//文件操作中的相对路径可以省略./
//'/data/a.txt'前面的/表示当前文件模块所在磁盘根目录
fs.readFile('data/a.txt', function (err, data){
  if (err) {
    return console.log('读取失败')
  }
  console.log(data.toString())
})

//在模块加载中,相对路径的./不能省略,后缀可以省略
require('./data/foo.js')
//前面的/表示磁盘根目录
require('/data/foo.js')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15