NodeJs_02 _ 学习笔记

7 篇文章 0 订阅

目录

目标

一、web 服务器

1.1、介绍

1.2、服务器相关概念

1.3、页面渲染模式

1.3.1、ssr ( 服务器端渲染 )

1.3.2、csr (客户端渲染) 【不依赖任何流量或后台管理】

1.4、创建 web 服务

1.5、静态资源服务器

1.6、get 数据获取

1.7、post 数据获取

1.8、浏览器缓存

图例 :


跳转链接 :   NodeJs_01 _ 学习笔记 

跳转链接 :   NodeJs_03 _ 学习笔记

跳转链接 :   NodeJs_04 _ 学习笔记


目标

  • 搭建静态 web 服务器
  • 接受 get 或 post 数据
  • 设置好图片浏览器缓存

一、web 服务器

1.1、介绍

Web 服务器一般指的是网站服务器,是指驻留因特网上某一台或 N 台计算机的程序,可以处理浏览器等 Web 客户端的请求并返回相应响应,目前最主流的三个 Web 服务器是 Apache、 Nginx 、IIS、Tomcat 。

请求:

请求行、请求头、请求体( (如常用的请求动作中的 GET 就没有请求体) , POST请求 有 )

响应:

响应行、响应头、响应体( 内容:用户不需要打开控制台就能看到的东西 )

1.2、服务器相关概念

  • ip 地址或域名 ( 主机 )

ip 地址:IP 地址就是互联网上每台计算机的唯一地址,因此 IP 地址具有唯一性。在开发期间,自己的电脑既是一台服务器,也是一个客户端,可以在本机浏览器中输入 127.0.0.1 进行访问。

域名:尽管 IP 地址能够唯一地标记网络上的计算机,但 IP 地址是一长串数字,不直观,而且不便于记忆,于是人们又发明了另一套字符型的地址方案,叫 域名地址 。IP 地址和域名是一一对应的关系,这份对应关系存放在一种叫做域名服务器 ( DNS ) 的电脑中。在开发测试期间, 127.0.0.1 对应的域名是 localhost

如果本地 localhost 无法使用,则是因为 本机中的 hosts 文件中没有匹配上 ip 地址

  • 网络协议  ( 内置模块  http )

网络上的计算机之间交换信息, 就像我们说话用某种语言一样,在网络上的各台计算机之间也有一种语言,这就是 网络协议 ,不同的计算机之间必须使用相同的网络协议才能进行通信。如:TCP (  传输层 , 一般用于做服务器 , 安全)、UDP ( 广播 )、HTTP ( 应用层 )、FTP 等等。

  • 端口

服务器的端口号就像是现实生活中的门牌号一样。通过门牌号,外卖员就可以准确把外卖

送到你的手中。同样的道理,在一台电脑中,可以运行 N 多个 web 服务。每个 web 服务都对应一个唯一的端口号。客户端发送过来的网络请求,通过端口号,可以被准确地交给对应的 web 服务进行处理。1-1024 系统保留,不要用。最多为 65535

端口 ( 1-1024 系统保留,1024 之后的  80 => http   /  443  =>  nginx 

 22 => ssh   20/21 => ftp   3306 => mysql  )

 注:服务器上的 端口号 是不可以重复的,必须是独一无二

http 服务默认端口号为 80   https 默认端口号 443

1.3、页面渲染模式

1.3.1、ssr ( 服务器端渲染 )

ssr ( Server Side Rendering ) :传统的渲染方式,由服务端把渲染的完整的页面响应给客户端。这样减少了一次客户端到服务端的一次 http 请求,加快相应速度,一般用于首屏的性能优化。

( 在服务器端既有数据 , 又进行整合 , 然后返回给客户端是个完整的 html )

优缺点:

1、利用 SEO(搜索引擎)( 利于提升排名 )

2、页面渲染时间短 , 响应速度快 ( 好比如 : 你点的外卖 , 可以直接吃 )

3、服务器压力过大

1.3.2、csr (客户端渲染) 【不依赖任何流量或后台管理】

CSR ( Client Side Rendering ):是一种目前流行的渲染方式,页面由 js 渲染,js 运行于浏览器端,所以称客户端渲染。( Vue / React : 把渲染的东西放到客户端 , 而服务器端只提供数据 )

( 一般应用于自媒体 ) ( 在服务器端创造数据 , 然后在客户端渲染 )

优缺点:

1、前后端并行开发,开发速度提升 ( 后端无需布局 , 只需提供 "数据" , 前端无需担心后端给的是什么语言 ,  只需要有一个接口就可以显示)

2、首屏渲染时间比较长(首屏加载速度慢)( 好比如 : 你点的菜品 , 需要自己再加工 )

3、不利于 SEO ( 因为 SEO 要做 正则匹配 , 而我们的客户端渲染是通过 JS 动态生成渲染出来的 )

如何知道一个网站是服务器端渲染还是客户端渲染 ?

进入一个网站 => 右键查看源代码 => 你能看到源代码的 html 结构的 => 就是服务器端渲染

看不到完整的 html 结构的 => 就是客户端渲染 

  • 服务器端渲染 ( ssr )

    • 数据处理

    • 视图渲染

  • 客户端渲染 ( csr )

    • 数据处理  

1.4、创建 web 服务

NodeJs 是通过官方提供的 http 模块来创建 web 服务器的模块。通过几行简单的代码,就能轻松的手写一个 web 服务,从而对外提供 web 服务 。

  • 创建 web 服务基本步骤

检测你的服务器是否启动成功 :

Windows 中 : 

( 1 ) 通过 360 查看 :

 ( 2 ) 通过 命令行 查看 :

( 3 ) 浏览器内输入你监听的地址 =>  localhost:8000/

显示出来 " 处理程序 " 


http 响应 :

Apizza 插件 : 

 http 请求 :


// 创建一个http服务  -- http服务是被动的,而且它还是http1.x无状态的,连接一但断开就不认识
// 引入http模块
const http = require('http')
// 创建http服务实例
const server = http.createServer()
// 监听客户端请求
// req 请求对象,获取客户端所发送过来的所有的数据  行头体
// res 响应对象,返回数据给请求客户端 行头体
server.on('request', (req, res) => {
  res.send = function (data) {
    if (typeof data != 'object') {
      if (typeof data != 'string') {
        data += ''
      }
      res.setHeader('content-type', 'text/html;charset=utf-8')
      res.end(data)
    } else {
      if (data instanceof Buffer) { // 原型链查找
        res.setHeader('content-type', 'text/html;charset=utf-8')
        res.end(data)
      } else {
        res.setHeader('content-type', 'application/json;charset=utf-8')
        res.end(JSON.stringify(data))
      }
    }

    /* if (typeof data == 'string' || data instanceof Buffer) {
      res.setHeader('content-type', 'text/html;charset=utf-8')
      res.end(data)
    } else {
      res.setHeader('content-type', 'application/json;charset=utf-8')
      res.end(JSON.stringify(data))
    } */
  }

  // 响应状态码
  // res.statusCode = 404
  // 响应头
  // res.setHeader('content-type', 'text/plain;charset=utf-8')
  // res.setHeader('content-type', 'text/html;charset=utf-8')
  // res.setHeader('content-type', 'application/json;charset=utf-8')
  // // 响应体 end它只接受 Buffer或string类型
  // // res.end('<h3>你好Web服务器</h3>')
  // res.end(JSON.stringify({ id: 1, name: 'aaa' }))

  res.send(123)
  // res.send({ id: 1, name: 'bbb' })
})

// 监听的端口号
// 服务器一般不会有一个网卡,是多块网卡,如果你此处写具体的ip,则对应的网卡入口才能被访问到服务

// 如果参数2不写,则获取的ip地址会以ipv6的形式返回,不直观,现在用的是ipv4
server.listen(3000, '0.0.0.0', () => console.log('server start ...'))
// 引入http模块
const http = require('http')
// url处理
const url = require('url')

// 创建http服务实例
const server = http.createServer()
// 监听客户端请求
// req 请求对象,获取客户端所发送过来的所有的数据  行头体
// res 响应对象,返回数据给请求客户端 行头体
server.on('request', (req, res) => {

  // 获取客户端请求相关信息
  // 请求的pathname
  // 解构赋值  query get请求参数
  // let { pathname, query:get } = url.parse(req.url, true)
  let { pathname, query } = url.parse(req.url, true)
  // console.log(pathname, query);
  // 请求动作
  // console.log(req.method);
  // 请求头
  // console.log(req.headers['user-agent']);
  // 获取当前请求ip地址
  // console.log(req.socket.remoteAddress);
  // 请求时间
  // console.log(new Date());

  res.setHeader('content-type', 'text/html;charset=utf-8')
  res.end('你好世界')
})

// 监听的端口号
// 服务器一般不会有一个网卡,是多块网卡,如果你此处写具体的ip,则对应的网卡入口才能被访问到服务
// 如果参数2不写,则获取的ip地址会以ipv6的形式返回,不直观,现在用的是ipv4
server.listen(3000, '0.0.0.0', () => console.log('server start ...'))

 

面试时可能会问你 : 为什么第一次请求会有两个请求回来 ?

 => 原因 : 因为 ico 是所有的服务器都会主动 , 自动去请求这个 ico 图片

1.5、静态资源服务器

  • 实现思路

客户端请求的每个资源 uri 地址,作为在本机服务器指定目录中的 文件。通过相关模块进行读取文件中数据进行响应给客户端,从而实现 静态服务器

 

实现步骤
①、导入需要的模块
const http = require('http')
const fs = require('fs')
const path = require('path')
const url = require('url')
②、使用 http 模块创建 web 服务器
const server = http.createServer()
③、将资源的请求 uri 地址 映射 为文件的存放 路径
// 事件监听
server.on('request', (req, res) => {
   // 得到请求的 uri
   let pathname = req.url
   pathname = pathname === '/' ? '/index.html' : pathname
if (pathname !== '/favicon.ico') {
     // 请求静态地址
     let filepath = path.join(__dirname, 'public', pathname)
   }
})
④、读取文件内容并响应给客户端
fs.readFile(filepath, (err, data) => {
if (err) {
         res.statusCode = 500
            res.end('服务器内部错误')
        }else{
            res.end(data)
        }
})

利用 fs 读取磁盘中真实存在的文件,把文件内容从通过响应体的形式,返回到浏览器中显示 

映射关系

url 地址  和   本机磁盘中的路径的  映射

 /  url 中的  ===》 磁盘中的路径  此路径称为 网站根目录

解析 url 地址   pathname query   =>  req.url
url 模块  { pathname, query } = url.parse( req.url, true )

根据 pathname 进行,磁盘中文件的映射对应,如果存在则读取文件响应,如果不存在则读指定的 404 文件,返回

http 静态服务器 :

 http - ajax :

http - img : 

 http - api :

// 引入http模块
const http = require('http')
// 路径
const path = require('path')
const fs = require('fs')
// url处理
const url = require('url')

// 静态服务器网站根目录 网址中的 / => 实际路径中的public目录
const webRoot = path.resolve('./public')

// mimeType 头信息
/* const mimeType = {
  '.html': 'text/html;charset=utf-8',
  '.jpg': 'image/jpeg'
} */
const mimeType = require('./utils/mime')

// 创建http服务实例
const server = http.createServer()
server.on('request', (req, res) => {
  // 请求的pathname
  let { pathname, query } = url.parse(req.url, true)
  // 不是网站中的图标才处理
  if ('/favicon.ico' != pathname) {

    // 路径连接 访问的文件真实路径地址
    let filepath = path.join(webRoot, pathname)

    // 判断路径中文件是否存在
    if (fs.existsSync(filepath)) { // 文件存在
      // 文件扩展名
      let extname = path.extname(filepath).slice(1) || 'html'
      try {
        res.setHeader('content-type', mimeType[extname])
      } catch (error) {
        res.setHeader('content-type', mimeType['html'])
      }
      res.end(fs.readFileSync(filepath))
    } else {
      res.statusCode = 404
      res.setHeader('content-type', 'text/html;charset=utf-8')
      res.end('页面丢了')
    }
  }
})

// 监听的端口号
server.listen(3000, '0.0.0.0', () => console.log('server start ...'))

1.6、get 数据获取

get 数据通过 地址栏 使用 query 方式进行传递的数据   例 ?id=1&name=zhangsan

// 引入 http 模块
const http = require('http');
const url = require('url');
// 创建 web 实例
http.createServer((req, res) => {
  // 获取地址栏中 query 数据
  let { query } = url.parse(req.url, true);
  console.log(query);
}).listen(8080)

url 模块  { pathname, query } = url.parse(request.url, true)
pathname  请求的 url 路径  对应到服务器中的具体的位置
query     获取的 get 数据
 

// 引入 http 模块
const http = require('http')
// 获取 url 地址中的参数
const url = require('url')
// 创建 web 实例
const server = http.createServer((req, res) => {
  // get 数据的获取 req.url 并且使用 url 模块
  // ?r=/home  => home 页面
  // let { pathname, query } = url.parse(req.url, true)
  let { pathname, query: { r = '/web' } } = url.parse(req.url, true)
  res.end(r)
})
server.listen(3000, '0.0.0.0', () => console.log('server start...'))

 

1.7、post 数据获取

表单数据多数为 post 进行提交到 服务器端 。

使用了异步解决 , 解决异步又使用了回调 , 提供了两个方法 data 和 end

表单数据多数为 post 进行提交到服务器端。 -- nodejs 当作数据流来接受

// 引入 http 模块
const http = require('http');
// 把 query 字符串转为对象
const queryString = require('querystring');
// 创建 web 实例
http.createServer((req, res) => {
  let data = '';
  // 数据接受中
  req.on('data', res => {
    data += res
  });
  // 数据传输结束了  接受到的所有数据
  req.on('end', () => {
    // post 数据
    let post = queryString.parse(data)
    console.log(post);
  });
}).listen(8080)

// 引入 http 模块
const http = require('http')
// 获取 url 地址中的参数
const url = require('url')
// 把 query 字符串转为对象
const querystring = require('querystring')
// 创建 web 实例
http.createServer((req, res) => {
  // 接受 post 数据
  if ('POST' === req.method) {
    // 数据容器
    let postData = ''
    // post x-www-form-urlencoded 数据流
    // 数据已经开发接受到了
    req.on('data', buffer => postData += buffer)
    // 数据接受完毕
    req.on('end', () => {
      // 接受完毕后,响应
      let json = JSON.stringify(querystring.parse(postData))
      res.end(json)
    })
  } else {
    res.end('ok')
  }
}).listen(3000, '0.0.0.0', () => console.log('server start...'))

案例 : 表单登陆

<!DOCTYPE html>
<html lang="en">
 
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>表单登陆</title>
</head>
 
<body>
  <!-- <form action="/login" method="POST" enctype="multipart/form-data"> -->
  <form action="/login" method="POST">
    <div>
      <label>
        账号:
        <input type="text" name="username">
      </label>
    </div>
    <div>
      <button type="submit">进入系统</button>
    </div>
  </form>
</body>
 
</html>
// 引入 http 模块
const http = require('http')
// 获取url地址中的参数
const url = require('url')
const path = require('path')
const actions = require('./actions')
// 指定当前网站的根目录
let webRoot = path.resolve('./public')
// 创建web实例
http.createServer((req, res) => {
  // 获取pathname和get请求数据
  let { pathname, query } = url.parse(req.url, true)
  // 拼接请求方法     大写转换小写
  let fn = req.method.toLowerCase() + 'fn'
  // 给请求对象添加额外属性,让它能向下传递
  req.pathname = pathname
  req.query = query
  req.webRoot = webRoot
  if ('/favicon.ico' !== pathname) {
    try {
      actions[fn](req, res)
    } catch (error) {
      res.end('服务没有办法操作了')
    }
  }
}).listen(3000, '0.0.0.0', () => console.log('server start...'))

 

1.8、浏览器缓存

浏览器缓存类型

  • 强缓存

强缓存生效时的状态码为 200  , 无论你是否读缓存 

 

  • 协商缓存

协商缓存生效时则返回的http状态码为 304 

强缓存 和 协商缓存 同时生效果,以强缓存为主,在设置时,一般两者都设置上。强缓存有失效时长,而协商缓存只要文件没有改变则不会失效。 ( 说白了 : 协商缓存是为了弥补强缓存失效过程那个间隙时你去访问的情况 , 防止访问时没有东西 , 就好比我的强缓存有效时长是一个小时 , 而你正好在一小时的失效时间点来访问 , 而这时候我还没有把新的缓存标识弄上去 , 客户就访问了 ; 而协商缓存就相当于一个动警 , 因为文件只要不改变 ,  协商缓存就不失效 ,  只有当你的文件修改了的时候 , 协商缓存才会失效 )

第二次不请求加载图片数据的话 , 可以节约资源 , 量一大 , 节约的资源可想而知 , 

现在所有的公司 , 所有的图片 , css , js 等, 都会设置上 , 只不过是时效可能稍有不同

一个问题 : 如何让强缓存失效呢 ? 

破缓存方案 : 就是图片后面设置上 哈希值 ( 乱码值 ) => 好处 : 可以随机更换成与原来不一样的值

强缓存的坏处 : 你的文件改完 , 但是你的强缓存时效没有过期 , 它就不会有请求 , 不请求的话就无从知道你文件是否更改好了 , 那我们怎么知道更改了呢 ?? 就是我们把名字换掉就可以了 

所以 我们 可以 加上一个 哈希值 , 那么我们的图片换了 , 我们就可以换一个 哈希值 , 哈希值变了 , 新的图片就会有新的缓存了

// 创建web服务 , 导入http模块  
const http = require('http')
// 解析字符串后返回的 URL 对象,每个属性对应字符串的各个组成部分。
const url = require('url')
//fs模块提供了用于与文件进行交互相关方法
const fs = require('fs')
// path模块用于处理文件和目录(文件夹)的路径
const path = require('path')
const crypto = require('crypto')
// 定义静态网站根目录 / => public
const webRoot = path.resolve('./public')
// 创建web服务对象实例  -- 我的协议 http
http.createServer((req, res) => {
  let { pathname, query } = url.parse(req.url, true)
  // 设置缺省页面
  pathname = pathname == '/' ? '/index.html' : pathname
  // ? * + \w \d \s [] {} ()
  if ('/favicon.ico' !== pathname) { // 有效的请求
    let filepath = path.join(webRoot, pathname)
    // 获取图片的扩展名
    let extname = path.extname(pathname)
    // 判断当前请求是图片,进行浏览器缓存设置
    if (/\.(jpe?g|png|gif|ico|bmp)/.test(extname)) {// 图片
      // 强缓存 -- 只要在服务器端通过响应头发送给浏览器就可以了 -- 返回的状态码为200
      // 设置过期时间为1小时
      res.setHeader('Expires', new Date(Date.now() + 3600 * 1000).toGMTString()) // http1.0
      res.setHeader('Cache-Control', 'max-age=' + 3600) // http1.1
      // 协商缓存 301 302  服务器端重定向 301永久重定向,302临时重定向
      // 浏览和服务器端相互协商完成缓存,如果有缓存,返回304
      // http1.0依赖于文件的修改时间,如果有修改,文件的修改时间就会改变
      let stat = fs.statSync(filepath)
      // 文件的修改时间
      let mtime = new Date(stat.mtime).toGMTString()
      res.setHeader('Last-Modified', mtime)
      // http1.1协商缓存
      // 依赖于当前文件的hash值来完成比对
      let hashFileName = crypto.createHash('md5').update(fs.readFileSync(path.resolve(filepath))).digest('hex')
      res.setHeader('Etag', hashFileName)
      if (hashFileName == req.headers['if-none-match']) {
        res.statusCode = 304
        res.end('')
        return;
      }
      if (mtime == req.headers['if-modified-since']) {
        res.statusCode = 304
        res.end('')
        return;
      }
    }
    // 响应体处理
    fs.createReadStream(filepath).pipe(res)
  }
}).listen(3000, '0.0.0.0')

图例 :


跳转链接 :   NodeJs_01 _ 学习笔记 

跳转链接 :   NodeJs_03 _ 学习笔记

跳转链接 :   NodeJs_04 _ 学习笔记

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值