复习
- try-catch 捕获异常
- 根据用户不同请求,读取不同HTML文件并返回(带图片、带CSS)
- 模拟Apache服务器,处理静态资源请求
- 用户请求的 url 对于服务器来说就是一个标识
- request对象 和 response 对象
- npm 安装和使用
- modules 和 packages区别
- package.json 文件作用
- package-lock.json 文件作用
- 服务器端如何获取用户 get 提交的数据
- 服务器端如何做重定向操作
- “路由” 由哪两部分组成?
今日课程
授课顺序
- 完成 get 方式添加新闻、post 方式添加新闻
- 完成新闻列表页渲染
- 完成新闻详情页渲染
- 封装读取 data.json 文件代码
- 封装写入 data.json 文件的代码
- 封装读取 post 数据的代码
- require() 加载模块机制
- 加载过程
- 执行原理
- node.js 模块化介绍
- module、module.exports和exports
- 通过设置响应报文头让浏览器实现弹框下载功能
- 网页中的 ./ 和 ../ 等相对路径的含义
- 相对于请求当前页面的url来计算,根据这个url计算出上一级url或者本级url等
- 最终浏览器会根据相对url(相对路径)计算出绝对路径然后再请求服务器(向服务器发起请求)
- Buffer 类型介绍
- 通过模块化的方式改造 Hacker News
知识点
- node.js 模块化介绍
- require() 加载模块机制
- get 和 post 请求区别
- url 模块使用
- querystring 模块使用
- underscore 模块使用
- node.js 中如何获取 get 请求提交的数据
- node.js 中如何获取 post 请求提交的数据
- module、module.exports、exports
- 前端资源库
课程总结:
- 通过 url 模块获取get 提交的数据
- 通过监听req 对象的 data 事件和 end 事件,配合 querystring模块获取用户 post 提交的数据
- underscore 的 template方法使用
- require() 加载机制
- module、module.exports 和 exports
- 封装 异步回调的方法
- node.js 模块的分类
node.js 模块
在 node.js 开发中一个文件就可以认为是一个模块。
一、node.js 模块分类
核心模块 Core Module、内置模块、原生模块
- fs
- http
- path
- url
- …
所有内置模块在安装node.js的时候就已经编译成 二进制文件,可以直接加载运行(速度较快)
部分内置模块,在 node.exe 这个进程启动的时候就已经默认加载了,所以可以直接使用。
文件模块
按文件后缀来分
如果加载时,没有指定后缀名,那么就按照如下顺序依次加载相应模块
- .js
- .json
- .node(C/C++编写的模块)
自定义模块(第三方模块)
- mime
- cheerio
- moment
- mongo
- …
二、require 加载模块顺序
看 require() 加载模块时传入的参数是否以 ‘./‘ 或 ‘../‘ 或 ‘/‘ 等等这样的路径方式开头(相对路径或绝对路径都可以)
是,那么会按照传入的路径直接去查询对应的模块。
传入的是否为具体的文件名
require(‘./test.js’) 是具体的文件名
- 直接根据给定的路径去加载模块,找到了加载成功,找不到加载失败
require(‘./test’); 不是具体的文件名、
- 第一步:根据给定的路径,依次添加文件后缀 .js、.json、.node进行匹配,如果找不到匹配执行第二步
- 第二步:查找是否有 test 目录(尝试找 test 包)
- 找不到:加载失败
- 找到了:依次在 test 目录下查找 package.json 文件(找到该文件后尝试找 main 字段中的入口文件)、index.js、index.json、index.node,找不到则加载失败
- 不是,那么就认为传入的是 “模块名称”(比如:require(‘http’)、require(‘mime’))
- 是核心模块:直接加载核心模块
- 不是核心模块
- 依次递归查找 node_modules 目录中是否有相应的包
- 从当前目录开始,依次递归查找所有父目录下的 node_modules 目录中是否包含相应的包
- 如果查找完毕磁盘根目录依然没有则加载失败
- 打印输入 module.paths 查看
- 依次递归查找 node_modules 目录中是否有相应的包
1 | // require('http') |
require 加载模块注意点
所有模块第一次加载完毕后都会有 缓存,二次加载直接读取缓存,避免了二次开销
- 因为有 缓存,所以模块中的代码只在第一次加载的时候执行一次
每次加载模块的时候都优先从缓存中加载,缓存中没有的情况下才会按照 node.js 加载模块的规则去查找
核心模块在 Node.js 源码编译的时候,都已经编译为二进制执行文件,所以加载速度较快(核心模块加载的优先级仅次于 缓存加载)
- 核心模块都保存在 lib 目录下
试图加载一个和 核心模块 同名的 自定义模块(第三方模块)是不会成功的
- 自定义模块要么名字不要与核心模块同名
- 要么使用路径的方式加载
核心模块 只能通过 模块名称 来加载(错误示例:require(‘./http’); 这样是无法加载 核心模块 http的 )
- require() 加载模块使用 ./ 相对路径时,相对路径是相对当前模块,不受执行 node 命令的路径影响
- 建议加载文件模块的时候始终添加文件后缀名,不要省略。
三、补充 CommonJS 规范
- CommonJS 规范
- 模块的定义
- 总结:CommonJS 是为 JavaScript 语言制定的一种 模块规范、编程 API规范
- node.js 遵循了 CommonJS规范
关于 node.js 中 Module 详细介绍
自己设计路由实现 Hacker News 网站部分功能
参考网址:https://news.ycombinator.com/
步骤
- 实现新闻列表页 - 首页 - index
- 实现新闻详情页 - 详情页 - details
- 实现新闻添加页 - 提交页 - submit
- 实现保存数据功能 - 将数据写入到 .json 文件中
- 实现首页数据的动态加载 - 根据.json文件来加载数据
实现思路
规划项目目录结构
- HackerNews
- resources
- css
- images
- views(存放html模板页面)
- data(保存新闻数据 data.json 文件)
- app.js 文件(该文件即我们写服务器端JavaScript代码的地方,用来处理用户请求)
- package.json
- resources
路由设计
- 注意:此处要自己设计路由,而不是像模拟 Apache 静态资源服务器一样
根据不同的请求返回相应的功能
- 当请求
/
和/index
时,返回views/index.html
文件内容 - 当请求
/details
时,返回views/details.html
文件内容 - 当请求
/submit
时,返回views/submit.html
文件内容 - 当请求
/r
时,保存用户提交的新闻数据,并将重定向到index页面。 - 对于其他以’/resources’开头的都当做静态资源来处理。
知识点
- 封装
render()
函数,将render()
函数挂载到response
对象上,实现response.render()
效果。 - 使用
underscore
模块中的模板引擎功能,渲染index
页面中的新闻数据。 - 通过 url 模块来处理 get 请求
1 | // 1. 将 req.url 通过 url 模块来处理 |
- 服务器端接收 post 提交过来的数据
- 通过 querystring 模块将查询字符串转换为 json 对象
JSON在线格式化
url模块介绍
- 当服务器处理 get 请求时,用户请求的参数是在 request 的 url 属性中,纯字符串,使用起来并不方便
- url 模块可以更方便地解析用户 get 请求提交上来的参数
具体使用
- 加载模块
var url = require('url');
- 调用
parse()
方法解析
1 |
|
模块化
什么是模块?
- 每个.js文件就是一个模块
- 从npm上下载的一个包(可能是由多个文件组成的一个实现特定功能的包)也是一个模块
- 任何文件或目录只要可以被Node.js通过
require()
函数加载的都是模块 - 每个模块就是一个独立的作用域,模块和模块之间不会互相”污染”
- 我们可以通过编程的方式,指定某个模块要对外暴露的内容(其实就是指定require的返回值,通过require的返回值对外暴露指定内容)。这个对外暴露内容的过程也叫”导出”
module.exports
为什么要进行模块化
- 方便代码管理、项目维护
- 有助于分工协同开发
- 模块和模块之间不会出现变量”污染”,一个模块就是一个作用域。
- 模块化可以做到职责分离,每个模块实现一个独立的功能
补充:面向对象编程的5(6)大原则:
- 开放封闭原则
- 里氏替换原则
- 依赖倒置原则
- 单一职责原则
- 接口隔离原则
什么是包?
- 通过package.json描述的一个文件或目录(可以理解成一个实现某个功能的1个文件或多个文件,通过package.json组织起来)
- 包不一定能被Node.js通过
require()
来加载,那么就不就叫模块。比如有些包中没有设置启动文件(package.json中的main字段),就不是模块。 - package 和 module 参考链接
在Node.js中模主要分为:核心模块 和 文件模块
核心模块
- http、fs、path、url、net、os、readline、……
- 核心模块在Node.js自身源码编译时,已经编译成二进制文件
- 部分核心模块在Node.js进程启动的时候已经默认加载到缓存里面了
文件模块(包含独立文件模块和第三方模块)
- 文件模块可以是:.js 模块、.node模块、*.json模块,这些都是文件模块
- 无论从npm上下载的第三方模块还是我们自己编写的模块都是文件模块
module.exports 和 exports
在每个模块中module表示当前模块对象, 里面保存了当前模块对象的各种信息
module.exports 其实就是 require()加载模块时的返回值
exports 就是module.exports的一个引用
1 | exports = module.exports; |
特别注意:最终暴露给require的返回值的是:module.exports, 而不是exports
1 | // To illustrate(说明) the behavior, imagine this hypothetical implementation of require(), which is quite similar to what is actually done by require(): |
require 加载模块时做了2件事
- 执行了模块中的代码
- 返回了模块中对外暴露的内容(可能是对象、函数等等)
下载Node.js源码,打开看下
JavaScript 的严格模式—— "use strict";
或 'use strict';
- 参考链接:
art-template
npm 搜索:art-template
参考链接
https://www.npmjs.com/package/art-template
art-template文档