20210902#_nodejs-day02
上一级: 20210816# 【黑马前端】
- 01-授课思路
- request 对象 和 response对象
- NPM - Node Package Manager - Node 包管理器
- 在 html 网页中路径的含义
- package.json 文件
- 自己设计路由实现 Hacker News 网站部分功能
- underscore模块介绍
- url模块介绍
- 模块化
- 什么是模块?
- 什么是包?
- 参考链接:https://docs.npmjs.com/how-npm-works/packages
- 在Node.js中模主要分为:核心模块 和 文件模块
- 模块化的好处
- 模块加载
- require 加载模块时做了2件事
- module.exports 和 exports
- 下载Node.js源码,打开看下
- JavaScript 的严格模式——
"use strict";
或'use strict';
01-授课思路
反馈
-
一整天感觉老师讲了特别多知识,没有重点的感觉。听着听着就走神了。晚上写代码的时候发现都不会写。。。。
- fs 模块读写文件
- http 模块创建 web服务
-
第一天上午概念性东西还好,到了下午有点蒙圈了。自己没有跟上,而且提问好吓人。不过毕竟是总监,我还是匿了。请不要给我穿小鞋 。 谢谢!!!!!
-
讲的特别好,通俗易懂!有个小建议,在讲到新知识点的时候,给我们解释完之后留几秒钟的时间让我们思考一下。
-
讲的很棒,有个问题不清楚。server.on('request', function (req, res) 监听的是这个 request事件吗? 当时这个单词写错了不能运行,但是后面简写的时候又可以省略,希望虎哥在简单讲下这块?
-
fs.writeFile写入数据,文件内本身已经有内容了,再写入会覆盖文件里面的内容,若是不想被覆盖要怎么解决呢?
-
虎哥,当我们使用writeFile()方法的时候,是不是会覆盖?就是如果原来的文件里有文字,用这个方法写入的文字会把原来的文字给盖掉,有啥解决方法么?除了用appendFile()这种方法,有没有可以仍然使用writeFile()就可以解决覆盖的问题的?
-
老师的ppt做的挺好的
-
讲的太快了,跟不上节奏,希望讲慢点,谢谢
-
虎哥语速慢点
-
老师辛苦了。视频声音有点小,另外有两个问题:1.怎样在一个文件夹下面访问别的文件夹的文件,复制路径放在后面不行。2.var http=require('http'); var server=http.createServer(function(req,res){ console.log('有人访问了。'); res.setHeader('Content-type','text/html;charset=utf-8'); res.write('哈哈哈
hello
'); res.end(); }) server.listen(9000,function(){ console.log('please visit: http://localhost:9000'); }) 为什么“有人访问了”出现了两次? -
对于安装多版本node的作用,以及如何使用nvm管理不太清楚,希望老师能再讲解下
-
老师讲课棒棒哒!!!
-
老师辛苦了,
-
多加练习
-
有点小快····
-
多敲多练
-
忙了一晚上,白天装了个nodejs 6.11.3,晚上什么都用不了了,麻烦老师再给我们讲解下有关知识
-
第一次接触,还是有点不熟悉,需要花时间去消化。老师辛苦了!!!
复习
了解:
- 浏览器渲染引擎工作原理
- 浏览器访问网站过程
- Web开发本质:请求、处理、响应
- C/S 架构 和 B/S 架构
- node.js 是什么?
- node.js 有什么特点?
- node.js 安装
- node.js 开发网站 和 传统方式开发网站(PHP、JSP等)有什么区别?
- node.js REPL
- 如何进入 REPL 环境
- 如何退出 REPL 环境
- 通过创建 js 文件开发 node.js 程序
重点:
- 通过 fs 模块实现文件读写操作
- path 模块使用
__dirname
和__filename
- node.js 中异步是如何实现的?为什么说 node.js 即是单线程又是异步非阻塞 I/O 模型?
- 调用栈
- 调用队列
- 编写简单的 http 服务程序,无论请求当前网站下哪个路径,都返回 hello world
- 乱码问题。
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
- 设置显示 HTML 内容。
res.setHeader('Content-Type', 'text/html; charset=utf-8');
补充:
1. 文件操作时,无需先判断文件是否存在,直接操作即可,如果文件不存在会反应在 error 对象中
2. try-catch 的使用
今日授课内容
-
通过 node.js 编写 http 服务程序 - 通过读取静态 HTML 文件来响应用户请求(带图片和外部CSS样式)
-
通过 node.js 编写 http 服务程序 - 模拟 Apache 服务器处理静态资源
- mime 第三方模块使用
-
在请求服务器的时候,请求的 url 就是一个标识!
-
request(http.IncomingMessage) 和 response(ServerResponse) 对象介绍
- request: 服务器解析用户提交的 http 请求报文,将结果解析到 request 对象中。凡是要获取和用户请求相关的数据都可以通过 request 对象获取
- response: 在服务器端用来向用户做出响应的对象。凡是需要向用户(客户端)响应的操作,都需要通过 response 对象来进行。
-
NPM
-
package.json、package-lock.json 文件介绍
- 元数据
- 自己设计路由,实现 HackerNews网站部分功能
- underscore 模块介绍、url 模块介绍
- 在html页面中写相对路径'./' 和 绝对路径 '/'的含义
- 此处 ./ 相对的是吐出当前页面的url
- 通过设置 http 响应报文头实现弹框下载功能
- 设置
Content-Type: application/octet-stream
- 设置
Content-Disposition: attachment; filename=demo.txt
其他参考
-
1、注意在发送不同类型的文件时,要设置好对应的
Content-Type
-
2、HTTP状态码参考
request 对象 和 response对象
request 对象
- request 对象类型 <http.IncomingMessage>, 继承自stream.Readable
- request 对象常用成员
request.headers
request.rawHeaders
request.httpVersion
request.method
request.url
response 对象
-
response 对象类型 <http.ServerResponse>
-
response 对象常用成员
-
response.writeHead(statusCode[, statusMessage][, headers])
- This method must only be called once on a message and it must be called before response.end() is called.
- 这个方法在每次请求响应前都必须被调用(只能调用一次)。并且必须在end()方法调用前调用
- If you call response.write() or response.end() before calling this, the implicit/mutable headers will be calculated and call this function for you.
- 如果在调用writeHead()方法之前调用了write() 或 end()方法,系统会自动帮你调用writeHead()方法,并且会生成默认的响应头
- When headers have been set with response.setHeader(), they will be merged with any headers passed to response.writeHead(), with the headers passed to response.writeHead() given precedence.
- 如果通过 res.setHeader() 也设置了响应头,那么系统会将serHeader()设置的响应头和writeHead()设置的响应头合并。 并且writeHead()的设置优先
-
// 示例代码:
res.writeHead(200, 'OK', {
'Content-Type': 'text/html; charset=utf-8',
'Content-Length': Buffer.byteLength(msg)
});
-
response.write(chunk[, encoding][, callback])
- 参数1:要写入的数据,可以是字符串或二进制数据,必填。
- 参数2:编码,默认是utf8,选填。
- 参数3:回调函数,选填。
-
response.end([data][, encoding][, callback])
- 结束响应。
- This method signals to the server that all of the response headers and body have been sent; that server should consider this message complete. The method,
response.end()
, MUST be called on each response.
- res.end()这个方法告诉服务器所有要发送的响应头和响应体都发送完毕了。可以人为这次响应结束了。
- 同时每次响应都必须调用该方法,用来结束响应
- 参数1:结束响应前要发送的数据,选填。
- 参数2:编码,选填。
- 参数3:回调函数,选填。
-
response.setHeader(name, value)
- 设置响应报文头
-
response.statusCode
- 设置或读取http响应码
-
response.statusMessage
- 设置或读取http响应状态消息
NPM - Node Package Manager - Node 包管理器
NPM 是什么?
一般当我们说npm的时候可能指3件事
- NPM 网站:https://www.npmjs.com/
- NPM 包管理库,存储了大量的JavaScript代码库
- NPM 客户端,我们所使用的npm命令行工具。使用JavaScript开发的基于node.js的命令行工具,本身也是Node的一个包。
参考图片
NPM 官方解释:
-
npm is the package manager for JavaScript and the world’s largest software registry.
- npm 是一个JavaScript包管理器,并且是世界上最大的软件登记处
-
discover packages of reusable code — and assemble them in powerful new ways.
- 发现可重用代码,并集成代码包到项目中的全新的、强大方式
-
npm makes it easy for JavaScript developers to share and reuse code, and it makes it easy to update the code that you're sharing.
- npm 让JavaScript开发者共享和重用代码变的更容易,同时也让我们更容易地更新正在被共享的代码
npm与 node.js
- npm是Node.js默认的软件包管理系统。安装完毕node后,会默认安装好npm
- npm本身也是基于Node.js开发的包(软件)
如何安装 NPM?
- npm会随着Node.js自动安装,安装完毕node.js后会自动安装npm
- 查看当前npm版本:
npm -v
- 更新npm:
npm install npm@latest -g
NPM 使用
- 在 https://www.npmjs.com/ 网站找到需要的包
- 在项目的根目录下,执行
npm install 包名称
安装 - 在node.js代码中通过
require('包名');
加载该模块 - 注意:通过
npm install 包名
安装的包,会自动下载到当前目录下的node_modules
目录下,如果该目录不存在,则创建,如果已存在则直接下载进去。 - 在代码中通过
require('包名');
加载该模块
----- 上面说的这种方式叫做 本地安装。
NPM 全局安装介绍
- 什么是 npm 全局安装?
npm install 包名 -g
npm 全局安装指的是把包安装成了一个命令行工具。
// 通过npm全局安装mime
npm install mime -g
//安装完毕后可以在命令行中直接使用
mime a.txt 命令来查看对应的结果
-
npm 全局安装实际做了2件事:
-
下载包到一个指定的目录
C:\Users\username\AppData\Roaming\npm\node_modules
-
创建一段命令行执行的代码。
C:\Users\username\AppData\Roaming\npm\mime -> C:\Users\steve xiaohu zhao\AppData\Roaming\npm\node_modules\mime\cli.js
NPM 安装建议
- 全局安装只是为了可以当做命令行使用而已
五、npm常用命令介绍
-
install,安装包。
npm install 包名
-
uninstall,卸载包。·npm uninstall 包名`
-
version,查看当前npm版本。
npm version
或npm -v
-
init,创建一个package.json文件。
npm init
-
注意:当使用
npm init -y
的时候,如果当前文件夹(目录)的名字比较怪(有大写、有中文等等)就会影响npm init -y 的一步生成操作,此时需要 npm init 根据向导来生成
"模块"(Modules)和"包"(Packages)的区别
- A module is any file or directory that can be loaded by Node.js'
require()
.
- 模块可以是任何一个文件或目录(目录下可以有很多个文件),只要能被node.js通过require()即可。
- A package is a file or directory that is described by a
package.json
. This can happen in a bunch of different ways!
- 包是一个文件或目录(目录下可以有多个文件)必须有一个package.json文件来描述,就可以是一个包。
node.js 错误调试:
-
当开启服务后,在浏览器中输入地址,如果出现浏览问题,首先要先看 服务器控制台是否报错。如果报错,直接根据服务器报错进行排错。
-
打开浏览器开发者工具中的 “网络” 部分,查看请求是否成功发出去了
- 看一下请求报文是不是和我们想的一样
- 响应状态码
在 html 网页中路径的含义
在 html 网页中相对路径 './' 和 绝对路径 '/'的含义
- "相对路径" 到底 "相对" 的是什么?
- 相对当前请求的路径
- 相对于吐出当前网页的路径
网页中的这个路径主要是告诉浏览器向哪个地址发起请求用的
- './' 表示本次请求从相对于当前页面的请求路径(即服务器返回当前页面时的请求路径)开始
- '/' 表示请求从根目录开始
打开浏览器来演示,最终主要体现在了请求报文的 url 上。
演示步骤
- 找到之前的静态html页面中带有外部样式表连接的网页
- 请求该网页,查看http请求报文中的请求路径
package.json 文件
package.json 文件的作用?
- package.json 文件是一个包说明文件(项目描述文件),用来管理组织一个包(一个项目)
- package.json 文件是一个 json 格式的文件
- 位于当前项目的根目录下
元数据
package.json 文件中常见的项有哪些?
- name
- 包的名字
- version
- 包的版本
- description
- 包描述
- author
- 包的作者
- main
- 包的入口js文件,从main字段这里指定的那个js文件开始执行
- dependencies
- 当前包依赖的其他包
如何创建一个 package.json 文件
- 通过
npm init
命令 或者npm init -y
或npm init -yes
命令 - 手动创建一个
注意
- 通过
npm init -y
或npm init -yes
创建 package.json 文件时,执行命令所在的目录接名称中不能包含大写字母 - package.json 文件中,项目名称本身不能包含大写字母
- npm 更新新版本后,项目所在的文件夹如果包含中文等特殊字符,创建的时候不会提示一步一步的输入,直接报错。
官方介绍
自己设计路由实现 Hacker News 网站部分功能
参考网址:https://news.ycombinator.com/
步骤
-
实现新闻列表页 - 首页 - /index get
-
实现新闻详情页 - 详情页 - /details get
-
实现新闻添加页 - 提交页 - /submit get
/add get
/add post -
实现保存数据功能 - 将数据写入到 data.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
文件内容 - 当请求
/add
时,保存用户提交的新闻数据,并将重定向到index页面。 - 对于其他以'/resources'开头的都当做静态资源来处理。
知识点
- 封装
render()
函数,将render()
函数挂载到response
对象上,实现response.render()
效果。 - 使用
underscore
模块中的模板引擎功能,渲染index
页面中的新闻数据。 - 通过 url 模块来处理 get 请求
// 1. 将 req.url 通过 url 模块来处理
var urlObj = url.parse(req.url, true);
// 1.1 获取用户请求的URL,不带查询字符串
// 注意:此时的reqUrl中不包含 get 的请求参数,只是pathname
var reqUrl = urlObj.pathname.toLowerCase();
// urlObj.query
- 服务器端接收 post 提交过来的数据
- 通过 querystring 模块将查询字符串转换为 json 对象
JSON在线格式化
underscore模块介绍
参考描述
1、Underscore is a JavaScript library that provides a whole mess of useful functional programming helpers without extending any built-in objects.
2、Underscore 是一个 JavaScript 工具库,它提供了一整套函数式编程的实用功能,但是没有扩展任何 JavaScript 内置对象。 他解决了这个问题:“如果我面对一个空白的 HTML 页面,并希望立即开始工作,我需要什么?” 他弥补了 jQuery 没有实现的功能,同时又是 Backbone 必不可少的部分。
Underscore 提供了100多个函数,包括常用的:map、filter、invoke — 当然还有更多专业的辅助函数,如:函数绑定、JavaScript 模板功能、创建快速索引、强类型相等测试等等。
模板语法介绍:
<%= %>
, 中间写表达式<%%>
, 中间写语句
参考写法
- 提示:underscore库建议使用'_'来命名对象,类似于jQuery使用$来命名
// 案例一:
var html = '<h1><%= name %></h1>';
var compiled = _.template(html);
var result = compiled({name: 'aaaa'});
console.log(compiled);
// 案例二:
// 构建模板字符串
var html = '<%for (var i = 0; i < 5; i++) { %><h1><%= name %></h1><% }%>';
// 编译模板
var compiled = _.template(html);
// 进行模板字符串替换
var result = compiled({name: '张三'});
// 输出后的结果
console.log(result);
underscore中_.template()函数返回值其实就是一个函数:
function(obj){
var __t;
var __p = '';
var __j = Array.prototype.join,print = function () {
__p += __j.call(arguments,'');
};
with(obj||{}) {
__p += '<h1>' + ((__t = (name)) == null ? '' : __t) + '</h1>';
}
return __p;
}
url模块介绍
get请求时,用户请求的参数是在request的url属性中,纯字符串,使用起来并不方便
url模块可以更方便地解析用户请求的get参数
具体使用
- 加载模块
var url = require('url');
- 调用
parse()
方法解析
url.parse(urlString[, parseQueryString[, slashesDenoteHost]]);
var urlObj = url.parse(reqUrl, true);
// url对象的pathname属性,获取不包含查询字符串的url
// url对象的query属性中包含的就是请求字符串的键值对对象
模块化
什么是模块?
- 每个.js文件就是一个模块
- 从npm上下载的一个包(可能是由多个文件组成的一个实现特定功能的包)也是一个模块
- 任何文件或目录只要可以被Node.js通过
require()
函数加载的都是模块 - 每个模块就是一个独立的作用域,模块和模块之间不会互相"污染"
- 我们可以通过编程的方式,指定某个模块要对外暴露的内容(其实就是指定require的返回值,通过require的返回值对外暴露指定内容)。这个对外暴露内容的过程也叫"导出"
module.exports
什么是包?
- 通过package.json描述的一个文件或目录(可以理解成一个实现某个功能的1个文件或多个文件,通过package.json组织起来)
- 包不一定能被Node.js通过
require()
来加载,那么就不就叫模块。比如有些包中没有设置启动文件(package.json中的main字段),就不是模块。
参考链接:https://docs.npmjs.com/how-npm-works/packages
在Node.js中模主要分为:核心模块 和 文件模块
核心模块
- http、fs、path、url、net、os、readline、......
- 核心模块在Node.js自身源码编译时,已经编译成二进制文件
- 部分核心模块在Node.js进程启动的时候已经默认加载到缓存里面了
文件模块
- 文件模块可以是:.js 模块、.node模块、*.json模块,这些都是文件模块
- 无论从npm上下载的第三方模块还是我们自己编写的模块都是文件模块
模块化的好处
- 模块和模块之间不会出现变量"污染",一个模块就是一个作用域。
- 模块化开发效率高、可维护性好
- 模块化可以做到职责分离,每个模块实现一个独立的功能
模块加载
无论是核心模块还是文件模块加载都是采用require('标识符')
来
核心模块的加载速度是最快的
无论是 核心模块 还是 文件模块,加载过一次后都会缓存起来,第二次加载(第二次require)的时候直接从缓存中读取即可。所以模块中的代码只在第一次加载的时候执行一次,这点要注意。
核心模块只能通过 "模块名称" 来加载,例如:require('模块名称')
文件模块可以通过 require 指定路径的方式来加载(路径可以是文件路径 或 目录)
require('./a/b.js')
通过指定相对路径来加载模块require('/a/b.js')
或require('c:\a\b.js')
通过指定绝对路径来加载- 注意:
require('')
加载模块的时候,相对路径永远相对于当前模块,不受node命令执行的路径影响。
通过路径的方式加载文件模块时,文件的后缀可有可无
- 省略后缀名后,Node.js默认会以:.js、.node、.json的顺序来加载(依次拼接不同的后缀,查找并尝试加载)。
- 建议:始终加上后缀。
npm下载的第三方模块加载
也是通过 require('模块名称') 来加载的
第三方模块名称绝对不能与 核心模块重名,否则永远加载的都是核心模块
require('模块名称') 加载非核心模块的过程
- 通过
console.log(module.paths);
来查看
require 加载模块时做了2件事
- 执行了模块中的代码
- 返回了模块中对外暴露的内容(可能是对象、函数等等)
module.exports 和 exports
在每个模块中module表示当前模块对象, 里面保存了当前模块对象的各种信息
module.exports 其实就是 require()加载模块时的返回值
exports 就是module.exports的一个引用
exports = module.exports;
特别注意:最终暴露给require的返回值的是:module.exports, 而不是exports
// To illustrate(说明) the behavior, imagine this hypothetical implementation of require(), which is quite similar to what is actually done by require():
function require(...) {
var module = { exports: {} };
((module, exports) => {
// Your module code here. In this example, define a function.
function some_func() {};
exports = some_func;
// At this point, exports is no longer a shortcut to module.exports, and
// this module will still export an empty default object.
module.exports = some_func;
// At this point, the module will now export some_func, instead of the
// default object.
})(module, module.exports);
return module.exports;
}