NodeJS学习笔记 | 字数总计: 10.3k | 阅读时长: 42分钟 | 阅读量:
Node.js 介绍 node.js 是什么?
node.js 是一个开发平台,就像Java开发平台、.Net开发平台、PHP开发平台、Apple开发平台一样。
何为开发平台?有对应的编程语言、有语言运行时、有能实现特定功能的API(SDK:Software Development Kit)
该平台使用的编程语言是 JavaScript 语言。
node.js 平台是基于 Chrome V8 JavaScript 引擎构建。
基于 node.js 可以开发控制台程序(命令行程序、CLI程序)、桌面应用程序(GUI)(借助 node-webkit、electron 等框架实现)、Web 应用程序(网站)
PHP开发技术栈: LAMP - Linux Apache MySQL PHP
node.js 全栈开发技术栈: MEAN - MongoDB Express Angular Node.js
node.js 有哪些特点?
事件驱动(当事件被触发时,执行传递过去的回调函数)
非阻塞 I/O 模型(当执行I/O操作时,不会阻塞线程)
单线程
拥有世界最大的开源库生态系统 —— npm。
node.js 网站
node.js官方网站
node.js中文网
node.js 中文社区
为什么要学习Node.js?
通过学习Node.js开发深入理解服务器开发 、Web请求和响应过程 、 了解服务器端如何与客户端配合
学习服务器端渲染
学习服务器端为客户端编写接口
现在前端工程师面试,对 Node.js 开发有要求
补充提问:
在Node.js平台开发时,能使用Dom API吗?比如:document.getElementById('id'); window.location 等
?
复习 浏览器端 JavaScript 组成:ECMAscript、Dom、Bom
Node.js安装和配置
下载地址
官网术语解释
LTS 版本:Long-term Support 版本,长期支持版,即稳定版。
Current 版本:Latest Features 版本,最新版本,新特性会在该版本中最先加入。
注意:
安装完毕后通过命令:node -v
来确定是否安装成功【注意:打开”命令窗口”的时候建议使用”管理员方式”打开】
如果需要则配置环境变量。
nvm 管理node版本 nvm (Linux、Unix、OS X)
nvm-windows (Windows)
node代码简单操作文件 在使用 node api 查询文档
Node 中文网 Node 英文网
读写操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 var fs = require ('fs' );fs.readFile('./test.txt' ,function (err,data ) { if (err){ }else { } }) var msg = 'Hello China. 你好,中国。' ;fs.writeFile('./test.txt' , msg, 'utf8' , function (err ) { if (err) { console .log('文件写入失败,详细错误信息:' + err); } else { console .log('文件写入成功。' ); } });
创建文件夹:mkdir
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 const fs = require ('fs' )const path = require ('path' )let mkdirname = ['资料代码' ,'课程代码' ,'ppt演示' ,'图片演示' ]mkdirname.forEach(i => { const dirname = path.join(__dirname,i) fs.mkdir(dirname,err => { if (err){ console .log('目前创建失败:' +err); } else { console .log('创建成功' ); } }) });
获取路径 & 拼接路径
在读取文件等操作,如果自己写路径,在不同的操作系统,路径不一样,因此直接通过全局获取,就很便捷
1 2 3 console .log(__dirname) console .log(__filename)
拼接路径的方法,需要先导入模块
1 2 3 4 var path = require ('path' )var currentfilepath = path.join(__dirname,'public' )
**__dirname
__filename
并不是全局的 **
在执行 node app.js 这个文件时,会传入两个值
1 2 3 4 5 (function (__dirname,__filename ) { })(路径,文件路径)
REPL介绍
REPL 全称: Read-Eval-Print-Loop(交互式解释器)
R 读取 - 读取用户输入,解析输入了Javascript 数据结构并存储在内存中。
E 执行 - 执行输入的数据结构
P 打印 - 输出结果
L 循环 - 循环操作以上步骤直到用户两次按下 ctrl-c 按钮退出。
在REPL中编写程序 (类似于浏览器开发人员工具中的控制台功能)
直接在控制台输入 node
命令进入 REPL 环境
按两次 Control + C 退出REPL界面 或者 输入 .exit
退出 REPL 界面
按住 control 键不要放开, 然后按两下 c 键
最简单的 http 服务程序
简单的请求服务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 var http = require ('http' )var app = http.createServer(function (requset,respont ) { console .log('在访问...' ) res.setHeader('Content-Type' , 'text/html;charset=utf-8' ); res.write('哈哈哈 <h1>hello</h1>' ); res.write('<strong>你好 Node.js</strong>' ); res.end(); }) app.listen(8080 ,()=> { console .log('服务器启动:http://localhost:8080' ) })
根据不同的路径,访问不同的页面
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 const http = require ('http' )const fs = require ('fs' )const path = require ('path' )http.createServer((req,res ) => { function readData (pathfile ) { fs.readFile(path.join(__dirname,'htmls' ,pathfile),(err,data ) => { if (err){ throw err } res.end(data) }) } if (req.url === '/' || req.url === '/index' ) { readData('index.html' ) }else if (req.url === '/login' ){ readData('login.html' ) }else if (req.url === '/list' ){ readData('list.html' ) }else if (req.url === '/register' ){ readData('register.html' ) }else { readData('nofound.html' ) } }).listen(8080 ,()=> { console .log('服务器启动' ); console .log('http://localhost:8080' ); })
使用 mime
第三方库,来获取文件类型
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 var http = require ('http' )var path = require ('path' )var fs = require ('fs' )var mime = require ('mime' )http.createServer(function (req,res ) { var publicDir = path.join(__dirname,'public' ) var filename = path.join(publicDir,req.url) if (req.url === '/' || req.url === '/index' ){ filename = path.join(publicDir,'index.html' ) } fs.readFile(filename,(err,data ) => { if (err){ res.setHeader('Content-Type' ,'text/html;charset=utf-8' ) res.end('文件不存在' ) } else { res.setHeader('Content-Type' ,mime.getType(filename)) res.end(data) } }) }).listen(9080 ,()=> { console .log('http://localhost:9080' ); })
Buffer 类型 一. 类型介绍
JavaScript 语言没有读取或操作二进制数据流的机制。
Node.js 中引入了 Buffer 类型使我们可以操作 TCP流 或 文件流。
Buffer 类型的对象类似于整数数组,但 Buffer 的大小是固定的、且在 V8 堆外分配物理内存。 Buffer 的大小在被创建时确定,且无法调整。( buf.length 是固定的,不允许修改 )
Buffer 是全局的,所以使用的时候无需 require() 的方式来加载
二. 如何创建一个 Buffer 对象
常见的 API 介绍
创建一个 Buffer 对象
1 2 3 4 5 6 7 8 9 10 11 12 var array = [0x68 , 0x65 , 0x6c , 0x6c , 0x6f , 0x20 , 0xe4 , 0xb8 , 0x96 , 0xe7 , 0x95 , 0x8c ];var buf = Buffer.from(array);console .log(buf.toString('utf8' ));var buf = Buffer.from('你好世界! Hello World!~' );console .log(buf);console .log(buf.toString());
拼接多个 Buffer 对象为一个对象
1 2 3 var bufferList = [];var buf = Buffer.concat(bufferList);
获取字符串对应的字节个数
1 2 3 4 var len = Buffer.byteLength('你好世界Hello' , 'utf8' );console .log(len);
判断一个对象是否是 Buffer 类型对象
获取 Buffer 中的某个字节
获取 Buffer 对象中的字节的个数
已过时的 API
1 2 3 4 5 6 7 new Buffer(array)new Buffer(buffer)new Buffer(arrayBuffer[, byteOffset [, length]])new Buffer(size)new Buffer(string[, encoding])
三. Buffer 对象与编码
Node.js 目前支持的编码如下:
ascii
utf8
utf16le
base64
latin1
hex
示例代码:
1 2 3 4 5 var buf = Buffer.from('你好世界,Hello World!' , 'utf8' );console .log(buf.toString('hex' ));console .log(buf.toString('base64' ));console .log(buf.toString('utf8' ));
四、思考:为什么会有 Buffer 类型?
Buffer 使用来临时存储一些数据(二进制数据)
当我们要把一大块数据从一个地方传输到另外一个地方的时候可以通过 Buffer 对象进行传输
通过 Buffer 每次可以传输小部分数据,直到所有数据都传输完毕。
五、补充
Stream
Writable Stream
Readable Stream
Node 服务端 HTTP 模拟Apache服务器 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 var http = require ('http' );var path = require ('path' );var fs = require ('fs' );var mime = require ('mime' );var server = http.createServer(function (req, res ) { fs.readFile(path.join(__dirname, 'public' , req.url), function (err, data ) { if (err && err.code === 'ENOENT' ) { res.end('404, not found.' ); return ; } console .log(mime.lookup(req.url)); res.setHeader('Content-Type' , mime.lookup(req.url)); res.end(data); }); }); server.listen(9002 , function ( ) { console .log('http://localhost:9002' ); });
response.writeHead(statusCode [, statusMessage] [, headers])
发送一个响应头给请求。 状态码是一个三位数的 HTTP 状态码,如 404
。 最后一个参数 headers
是响应头。 第二个参数 statusMessage
是可选的状态描述。
该方法在消息中只能被调用一次,且必须在 response.end()
被调用之前调用。
response.setHeader()
设置的响应头会与 response.writeHead()
设置的响应头合并,且 response.writeHead()
的优先。
request.setHeader(name, value)
为 headers 对象设置一个单一的 header 值。如果该 header 已经存在了,则将会被替换。
1 2 3 4 5 var mime = require ('mime' )res.setHeader('Content-Type' ,mime.getType(req.url))
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 错误调试:
当开启服务后,在浏览器中输入地址,如果出现浏览问题,首先要先看 服务器控制台是否报错。如果报错,直接根据服务器报错进行排错。
打开浏览器开发者工具中的 “网络” 部分,查看请求是否成功发出去了
npm 介绍
npm(全称Node Package Manager,即node包管理器)是Node.js默认的、以JavaScript编写的软件包管理系统。
npm 官方网站
npm 官方文档
npm 和 node.js
npm是Node.js默认的软件包管理系统。安装完毕node后,会默认安装好npm
npm本身也是基于Node.js开发的包(软件)
NPM 使用
在 https://www.npmjs.com/ 网站找到需要的包
在项目的根目录下,执行npm install 包名称
安装
在node.js代码中通过 require('包名');
加载该模块
注意:通过npm install 包名
安装的包,会自动下载到当前目录下的node_modules
目录下,如果该目录不存在,则创建,如果已存在则直接下载进去。
在代码中通过 require('包名');
加载该模块
上面说的这种方式叫做 本地安装。
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 根据向导来生成
request 对象 和 response对象 request 对象
response 对象
2. 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()方法,并且会生成默认的响应头
3. 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()的设置优先
1 2 3 4 5 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:回调函数,选填。
Node 初建服务器 HackerNews HackerNews 源码 ,可以下载下来分析源码
封装文件读取方法,挂载res的protype上 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 function render (filename,res ) { fs.readFile(filename,function (err,data ) { if (err){ res.writeHead(404 ,'Not Found' ,{ 'Content-Type' :'text/html;charset=utf-8' }) return } res.setHeader('Content-Type' ,mime.getType(filename)) }) } res.render = function (filename ) { fs.readFile(filename,function (err,data ) { if (err){ res.writeHead(404 ,'Not Found' ,{ 'Content-Type' :'text/html;charset=utf-8' }) return } res.setHeader('Content-Type' ,mime.getType(filename)) }) }
Get请求数据,保存数据到data.json
在启动服务时,需要提前创建好 data 文件夹,writeFile 不会去创建文件夹。服务器重新启动,会重新创建 data.json 文件,会覆盖原来的。
用定义的数组去存放对象,将数组通过 JSON.stringify() 转化成string类型
数组list会覆盖,因此不是每次是定义,而是去读取data.json文件
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 39 40 41 42 43 44 var http = require ("http" );var path = require ("path" );var fs = require ("fs" );var list = []; var url = require ("url" );http .createServer(function (req, res ) { if (req.url.startsWith('/add' ) && req.method === 'GET' ) { var urlObj = url.parse(req.url, true ); list.push(urlObj.query); console .log(urlObj); fs.writeFile( path.join(__dirname, "data" , "data.json" ), JSON .stringify(list), function (err ) { if (err) { throw err; } console .log("ok" ); res.end(); } ); }else { res.end("404, Page Not Found." ); } }) .listen(8080 , () => { console .log("http://localhost:8080" ); });
修改保存数据的代码
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 39 40 41 42 43 var http = require ("http" );var path = require ("path" );var fs = require ("fs" );var url = require ("url" );http.createServer(function (req, res ) { if (req.url.startsWith("/add" ) && req.method === "GET" ){ var urlObj = url.parse(req.url, true ) var filename = path.join(__dirname,'data' ,'data.json' ) fs.readFile(filename,'utf-8' ,function (err,data ) { if (err && err.code !== 'ENOENT' ){ throw err } var list = JSON .parse(data || '[]' ) list.push(urlObj.query); fs.writeFile(filename,JSON .stringify(list),function (err ) { if (err){ throw err } res.statusCode = 302 res.statusMessage = 'Found' res.setHeader('Location' ,'/' ) res.end() }) }) }else { res.end('Not Found Page' ) } }).listen(8080 ,function ( ) { console .log('http://localhost:8080' ) })
重定向操作
服务器通过设置 http 响应报文头实现浏览器重定向操作。当提交数据后,我们肯定需要将网站跳转到其他页面去
1 2 3 4 5 6 7 8 9 10 11 12 13 function (err ) { if (err) { throw err; } console .log("ok" ); res.statusCode = 302 res.statusMessage = 'Found' res.setHeader('Location' ,'/' ) res.end(); }
实现 POST 请求
读取数据 data.json,和写入数据操作都和 get 请求一样
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 var querystring = require ('querystring' )var array = []req.on('data' ,function ( chunk ) { array.push(chunk) }) req.on('end' ,function ( ) { var postBody = Buffer.concat(array) postBody = postBody.toString('utf-8' ) postBody = querystring.parse(postBody) res.end('over' ) })
渲染页面,替换数据
使用 underscore 模块,中文网https://underscorejs.net/
Underscore 是一个JavaScript实用库,提供了一整套函数式编程的实用功能,但是没有扩展任何JavaScript内置对象。它是这个问题的答案:“如果我在一个空白的HTML页面前坐下, 并希望立即开始工作, 我需要什么?“…它弥补了部分jQuery 没有实现的功能,同时又是Backbone.js 必不可少的部分。
1. 安装
2.引入
1 var _ = require ('underscore' )
3.运用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 var _ = require ('underscore' )var names = ['张三' ,'李四' ,'老五' ]var ages = [18 ,20 ,45 ]var genders = ['男' ,'女' ,'女' ]var result = _.zip(names,ages,genders)console .log(result)result = _.unzip(result) console .log(result)
1 2 3 4 5 6 7 8 9 10 11 12 var _ = require ('underscore' )var html = '<h2><%= name %></h2>' var fn = _.template(html)var html = fn({name : 'Simplelife' })console .log(html)
<%%> html数据绑定
1 2 3 4 5 6 <% for (var i = 0; i < list.length; i++) { %> <a href ="/item?id=<%= list[i].id %>" > <%= list[i].title %> </a > <% } %>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 res.render = function (filename, tplData ) { fs.readFile(filename, function (err, data ) { if (err) { res.writeHead(404 , 'Not Found' , { 'Content-Type' : 'text/html;charset=utf-8' }); res.end('404, not found.' ); return ; } if (tplData) { var fn = _.template(data.toString('utf8' )); data = fn(tplData); } res.setHeader('Content-Type' , mime.getType(filename)); res.end(data); }); };
1 2 3 4 5 6 if (tplData) { var fn = _.template(data.toString('utf8' )); data = fn(tplData); }
当渲染文件时,传入模板数据,就进行这代码。将页面数据转成字符串 data.toString(‘utf-8’) 的形式,返回的是函数的形式。将实参 tplData 传入函数,需要去替换数据,得到新的 data。
封装函数 1.封装读取文件函数
不能通过 return 去返回异步操作的内容。假如这样做,当进行异步操作开始就会返回,显然值还没有得出。如果在 function(err,data) 函数里面返回,虽然可以得到值,但是无法获取
1 2 3 4 5 6 7 8 9 10 11 12 13 function readNewsData (callback ) { fs.readFile(path.join(__dirname,'data' ,'data.json' ),'utf8' ,function (err,data ) { if (err && err.code !== 'ENOENT' ){ throw err; } var list = JSON .parse(data || '[]' ); callback(list) }) }
node.js 模块的分类 1.核心模块
Core Module、内置模块、原生模块
fs、http、path、url,加载模块是一个同步的过程,加载完一个加载下一个。
2.文件模块
按文件后缀来分,如果加载时,没有指定后缀名,那么就按照如下顺序依次加载相应的模块
.js
.json
.node(C/C++编写的模块)
3.自定义模块
mime
cheerio
moment
mongo
加载模块,会去一层一层去找 node_modules,向C跟磁盘
require 加载模块过程
看 require() 加载模块时传入的参数是否以 ‘./‘ 或 ‘../‘ 或 ‘/‘ 等等这样的路径方式开头(相对路径或绝对路径都可以)
是,那么会按照传入的路径直接去查询对应的模块。
不是,那么就认为传入的是 “模块名称”(比如:require(‘http’)、require(‘mime’))
是核心模块:直接加载核心模块
不是核心模块
依次递归查找 node_modules 目录中是否有相应的包
从当前目录开始,依次递归查找所有父目录下的 node_modules 目录中是否包含相应的包
如果查找完毕磁盘根目录依然没有则加载失败
打印输入 module.paths 查看
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 require ('./index2.js' )require ('ndex2' )
require 加载模块注意点
所有模块第一次加载完毕后都会有 缓存,二次加载直接读取缓存,避免了二次开销
因为有 缓存,所以模块中的代码只在第一次加载的时候执行一次
每次加载模块的时候都优先从缓存中加载,缓存中没有的情况下才会按照 node.js 加载模块的规则去查找
核心模块在 Node.js 源码编译的时候,都已经编译为二进制执行文件,所以加载速度较快(核心模块加载的优先级仅次于 缓存加载)
核心模块都保存在 lib 目录下
试图加载一个和 核心模块 同名的 自定义模块(第三方模块)是不会成功的
自定义模块要么名字不要与核心模块同名
要么使用路径的方式加载
核心模块 只能通过 模块名称 来加载(错误示例:require(‘./http’); 这样是无法加载 核心模块 http的 )
require() 加载模块使用 ./ 相对路径时,相对路径是相对当前模块,不受执行 node 命令的路径影响
建议加载文件模块的时候始终添加文件后缀名,不要省略。
补充CommonJS规范
CommonJS 规范
模块的定义
总结:CommonJS 是为 JavaScript 语言制定的一种 模块规范、编程 API规范
node.js 遵循了 CommonJS规范
1.模块通讯
1 2 3 4 5 6 7 8 9 var foo = require ('./foo.js' )module .exports = { name :function ( ) {}, age :18 }
2.module.exports 和 exports 暴露属性的区别
它们在栈里指向对象地址是同一个。
1 2 3 module .exports.name = '李四' exports .age = 18
1 2 3 4 此时暴露的对象{ name : '李四' , age :18 }
当改变 module.exports 的对象地址,因为 require 导入最终是 module.exports,因此 exports 导入最终地址也改变
PS:假如最终 exports 指向新的对象地址,最终还是以 module.exports 为准,它才是最终返回的
模块化开发思想,封装模块过程
将功能进行模块化封装,开发速度更开,维护成本也低,后期扩展更方便
模块化封装好的源码:HackerNews 源码 ,可以下载下来分析源码
模块分类
模块一(服务模块):负责启动服务
模块二(扩展模块):负责扩展 req 和 res 对象,为 req 和 res 增加以下更好用的API
模块三(路由模块):负责路由判断
模块四(业务模块):负责处理具体路由的业务代码
模块五(数据操作模块):负责进行数据库操作
模块六(配置模块):负责报错各种项目中用到的配置信息
PS:抽离代码,服务模块最后写,因为写好了其他模块,自然就写好了服务模块
步骤:
思考,该模块中要封装什么代码?
思考,这些代码有用到外部的数据吗?如果用到了,是否需要通过参数将这些数据传递到当前模块中
当前模块对外需要暴露的东西(module.exports的值)
模块二:context.js
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 var url = require ('url' ); var fs = require ('fs' );var mime = require ('mime' );var _ = require ('underscore' );module .exports = function (req,res ) { var urlObj = url.parse(req.url.toLowerCase(),true ) req.query = urlObj.query req.pathname = urlObj.pathname req.method = req.method.toLowerCase() res.render = function (filename,tplData ) { fs.readFile(filename,function (err,data ) { if (err){ res.writeHead(404 ,'Not Found' ,{'Content-Type' :'text/html;charset=utf-8' }) res.end('404,没找到页面' ) return } if (tplData){ var fn = _.template(data.toString('utf8' )) data = fn(tplData) } res.setHeader('Content-Type' ,mime.getType(filename)) res.end(data) }) } }
模块三:router.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 var handler = require ('./handler.js' )module .exports = function (req,res ) { if (req.pathname === '/' && req.method === 'get' ){ handler.index(req,res) } else if (req.pathname === '/submit' && req.method === 'get' ){ handler.submit(req,res) } else if (req.pathname === '/item' && req.method === 'get' ){ handler.item(req,res) } else if (req.pathname === '/add' && req.method === 'get' ){ handler.addGet(req,res) } else if (req.url === '/add' && req.method === 'post' ){ handler.addPost(req,res) } else if (req.url.startsWith('/resources' ) && req.method === 'get' ){ handler.static(req,res) } else { handler.handleErrors(req,res) } }
模块四:handler.js
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 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 var fs = require ('fs' );var path = require ('path' );var querystring = require ('querystring' );var config = require ('./config.js' );module .exports.index = function (req, res ) { readNewsData(function (list ) { res.render(path.join(__dirname, 'views' , 'index.html' ), { list : list }); }); }; module .exports.submit = function (req,res ) { res.render(path.join(__dirname,'views' ,'submit.html' )) } module .exports.item = function (req,res ) { readNewsData(function (list_news ) { var model = null for (var i = 0 ; i < list_news.length; i++){ if (list_news[i].id.toString() === req.query.id){ model = list_news[i] break } } if (model){ res.render(path.join(config.viewPath,'details.html' ),{item :model}) } else { res.end('No Such Item' ) } }) } module .exports.addGet = function (req,res ) { readNewsData(function (list ) { req.query.id = list.length list.push(req.query) writeNewsData(JSON .stringify(list),function ( ) { res.statusCode = 302 res.statusMessage = 'Found' res.setHeader('Location' ,'/' ) res.end() }) }) } module .exports.addPost = function (req,res ) { readNewsData(function (list ) { postBodyData(req,function (postData ) { postData.id = list.length list.push(postData) writeNewsData(JSON .stringify(list),function ( ) { res.statusCode = 302 res.statusMessage = 'Found' res.setHeader('Location' ,'/' ) res.end() }) }) }) } module .exports.static = function (req,res ) { res.render(path.join(__dirname,req.url)) } module .exports.handleErrors = function (req,res ) { res.writeHead(404 ,'Not Found' ,{ 'Content-Type' :'text/html;charset=utf-8' }) res.end('404,页面没有找到' ) } function readNewsData (callback ) { fs.readFile(config.dataPath, 'utf8' , function (err, data ) { if (err && err.code !== 'ENOENT' ) { throw err; } var list = JSON .parse(data || '[]' ); callback(list); }); } function writeNewsData (data, callback ) { fs.writeFile(config.dataPath, data, function (err ) { if (err) { throw err; } callback(); }); } function postBodyData (req, callback ) { var array = []; req.on('data' , function (chunk ) { array.push(chunk); }); req.on('end' , function ( ) { var postBody = Buffer.concat(array); postBody = postBody.toString('utf8' ); postBody = querystring.parse(postBody); callback(postBody); }); }
模块六:config.js
1 2 3 4 5 6 7 var path = require ('path' );module .exports = { "port" : 9091 , "dataPath" : path.join(__dirname, 'data' , 'data.json' ), "viewPath" : path.join(__dirname, 'views' ) };
模块一:index.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 var http = require ('http' );var context = require ('./context.js' );var router = require ('./router.js' );var config = require ('./config.js' );http.createServer(function (req, res ) { context(req, res); router(req, res); }).listen(config.port, function ( ) { console .log('http://localhost:' + config.port); });
ejs 模块引擎
地址:https://www.npmjs.com/package/ejs
官方文档:https://ejs.bootcss.com/
安装
示例
1 2 3 <% if (user) { %> <h2><%= user.name %></h2> <% } %>
使用示例
1 2 3 4 5 6 7 8 9 10 let template = ejs.compile(str, options);template(data); ejs.render(str, data, options); ejs.renderFile(filename, data, options, function (err, str ) { });
演示
1 2 3 4 5 6 7 8 9 10 11 12 var ejs = require ('ejs' )var path = require ('path' )var html = '<h1><%= username %></h1>' var result = ejs.render(html,{username : '张三' })console .log(result) ejs.renderFile(path.join(__dirname,'index.html' ),{title : '这是一个标题' ,msg : 'Hello world' },function (err,result ) { console .log(result) })
Express 框架基础 1.什么是 express
基于 NodeJS 平台开发的 “web 开发框架”,就是一个 nodejs 模块
express的作用:它提供一系列强大的特性,帮助你创建各种 web 和 移动设备应用
2.学习的理由
为了让我们基于 Node.js 开发 web 应用程序更高效
3.官方网站
4.express 特点
实现了路由功能
中间件(函数)功能
对 req、res 对象的扩展
可以集成其他模板引擎,ejs,underscore
Express 基本使用 1.安装 epress
1 2 npm init -y //创建 package.json 文件 npm install express --save
2.使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 var express = require ('express' )var app = express()app.get('/index' ,function (req,res ) { res.end('hello world 你好世界' ) res.send('hello world 你好世界' ) }) app.listen(9090 ,function ( ) { console .log('http://localhost:9090' ) })
res.end() 和 res.send() 的区别
参数类型区别,res.send() 参数可以是 buffer obj string array 等类型
res.end() 只能是 buffe 或 string
res.send() 会发送更多的响应报文头 Content-Type:text/html;charset=utf8
app.get 和 app.use 的区别
app.all
同样是可以注册路由的,但是和app.use
的区别,就是需要严格按钮路由匹配
pathname 完全匹配路由,请求方法是不限定的
1 2 3 app.all('/index' ,function ( ) { })
通过正则表达式注册路由
限制请求方式,不限制请求路径
要求是 get 请求,请求路径以 /index
开头就行
1 2 3 4 app.get(/^\/index(\/.+)*$/ ,function ( ) { })
通过 req.params 获取路由中的参数
将参数设计到路由上,如 /index/2017/10/11
1 2 3 4 5 app.get('/index/:year/:month/:day' ,function (req,res ) { res.send(req.params) }) {"year" :"2017" ,"month" :"10" ,"day" :"11" }
通过 Express 模拟Apche实现静态资源托管 1 2 3 4 5 6 7 8 9 app.use('/' ,express.static(path.join(__dirname,'public' ))) app.use('/' ,function ( ) {}) express 做了一个方法,用请求静态资源 var fn = express.static(path.join(__dirname,'public' ))
问题
1 2 3 4 5 6 app.use('/xxx' ,express.static(path.join(__dirname,'public' ))) app.use('/xxx' ,express.static(path.join(__dirname,'pp' )))
res 常见的 API 方法 路由挂载 express 上
router 文件代码需要获取 req,res参数,如果直接传入app这个值就很危险,通过挂载 express上 就更安全
router.js
1 2 3 4 5 6 7 8 9 10 11 var express = require ('express' )var router = express.Router()router.get('/' ,function (req,res ) { }) module .exports = router
app.js
1 2 3 var router = require ('./router.js' )app.use('/' ,router)
NodeJS补充 npm 版本符合 package.json
1 2 3 4 5 6 7 8 # node package versions - 13.4.6 - major: 16, minor: 4, patch: 6 # npm 版本符号 - ^ : 锁定major - ~ :锁定minor - 空 : 锁定patch - * :最新版本
npm 自定义包 1. 注册账号
我的账号:simplelife0421
npm官网:https://www.npmjs.com/
2.创建自己的包
1 2 3 4 5 6 7 8 9 10 var _ = require ('lodash' )function myChunk (arr,num ) { var array = _.chunk(arr,num) return array } module .exports = { myChunk }
3.上传自定义包
登录npm账号:npm adduser
如果是进入的是淘宝镜像就要切换源(坑:403 Forbidden)
1 2 3 查看npm源:npm config get registry 切换npm源方法一:npm config set registry http://registry.npmjs.org 切换npm源方法二:nrm use npm
4.发布包
npm publish
PS:如果你是刚创建的 npm 账号需要验证邮箱,不然无法上传
5.发布错误原因
process 进程 1. 变量
在 package.json 不仅能写配置,还能自定义配置。
1 2 3 4 5 6 "simple" :{ "env" : "echo hello" } const echo = process.env.npm_package_config_envconsole .log(echo)