本文共 79226 字,大约阅读时间需要 264 分钟。
Node.js® is a JavaScript runtime built on [Chrome's V8 JavaScript engine](https://v8.dev/).
基于V8引擎的JavaScript运行时环境
,可以解析和执行JavaScript代码
完全脱离浏览器来运行
,一切都归功于Node.jsNode.js中的JavaScript
没有BOM、DOM
服务器级别的操作API
构建于Chrome的V8引擎之上
特定格式的字符串
认识,解析和执行
它最快
的Node.js uses an event-driven,non-blocking I/O model that makes it lightweight and efficient
事件驱动
非阻塞IO模型(异步)
轻量和高效
Node.js' package ecosystem,npm,is the largest ecosystem of open sourse libraries in the world
npm
是世界上最大的开源库生态系统npm install jquery
学习门槛低
高性能
服务器高并发
能力开发周期短,节省成本
服务端应用程序
,Web 系统
;前端工具集
《深入浅出Node.js》
《Node.js权威指南》
Node.JS 官网
下载版本
稳定版
,推荐采用最新版
node --version
运行命令
REPL环境
node
node index.js
node path/index.js
node --help
code Runner
插件运行两层概念
开放式模块登记和管理系统
,亦可以说是一个生态圈,一个社区
Node 默认的模块管理器
,是一个命令行下的软件,用来安装和管理 Node 模块
不需要单独安装
npm install npm –g
node_modules
.npmrc
常用命令 | 作用 |
---|---|
npm init | 项目初始化 ,生成package.json 配置文件 |
npm install | 全局安装 –g / 局部安装 –save[-dev] (-save 在程序部署运行后还需要 用到的包、模块 / -dev 在程序开发阶段使用 到的包、模块) |
npm uninstall | 卸载包 |
npm root | 获取包安装路径,-g可以获取全局安装路径 |
npm list | 显示当前安装的包以及依赖关系 |
npm search 包名 | 搜索所有条件的包 |
npm view 包名 versions | 显示包所有的版本信息 |
npm install 包名 --registry=https://registry.npm.taobao.org
cnpm
安装包npm install -g cnpm --registry=https://registry.npm.taobao.orgcnpm install 包名
global
window 对象
console
内置的 console 模块
,提供操作控制台的输入输出
功能,常见使用方式与客户端类似console.log('hello node.js!')var age = 18console.log(global.age)
process
Node 进程信息
,一般用于获取环境变量之类的信息对象 | 作用 |
---|---|
process.argv 属性 | 显示 Node 运行程序的参数 (argv 是一个数组 , 第一个 成员描述 node.exe 位置 , 第二个 参数执行的 js 脚本文件 , 可以接收参数 ) |
process.platform 属性 | 显示当前操作系统 |
process.uptime 方法 | 获取 Node 程序运行开始到当前的时间(秒) |
process.exit 方法 | 结束 node 程序的运行 |
//显示Node运行程序的参数,argv是一个数组,第一个成员描述node.exe位置,第二个参数执行的js脚本文件console.log(process.argv) //platform显示当前操作系统console.log(process.platform)for(var i = 0 ; i < 10000000000 ; i++){ var num = 10}//uptime方法,获取Node程序运行开始到当前的时间(秒)console.log(process.uptime())//结束node程序的运行process.exit()
setInterval(callback, millisecond)
clearInterval(timer)
setTimeout(callback, millisecond)
clearTimeout(timer)
console.log()
开发工具的调试
Node Debug
开启调试菜单
问题
var userName = process.argv[2]var userPwd = process.argv[3]if(userName != 'admin' || userPwd != '888'){ console.log('您输入的用户名、密码错误,系统5秒后即将关闭!') setTimeout(function(){ process.exit() },5000)}
高并发
用户量是web服务器需要解决的关键问题I/O
引发的阻塞
问题同步和异步
顺序执行
任务的工作模式同时执行
任务的工作模式进程和线程
进程是一个运行中的程序
线程是进程的组成部分,独立运行和调度
I/O密集型操作
网络访问
数据库访问
文件读写
CPU密集型操作
CPU计算
同步方案
阻塞
单线程
的工作模式异步线程方案
多线程
提高用户的接待能力不足
系统资源耗费严重
执行效率未必高于单线程
并发量大时,还是会出现阻塞现象
Node.js 异步方案
多隆总管
异步回调和事件驱动机制
解决大并发量问题异步回调的原理
I/O阻塞的函数
作为主函数
I/O结束后的处理函数
作为回调函数
function eat(food){ console.log('我是张三!') food()}function food1(){ console.log('我要吃火锅!')}function food2(){ console.log('我要吃西餐!')}//eat是主函数,内部的参数就是回调函数eat(food1)eat(food2)
var fs = require('fs')//IO操作异步读取方法作为主函数,后续处理的方法作为回调函数fs.readFile('products.txt',function(err,result){ console.log('错误内容:',err) console.log('文件读取完成!内容:',result)})console.log('文件读取后续操作。。。。。。。')
作为一个函数参数的函数
主函数调用后再执行的
主函数的最后一个参数
第一个参数是错误对象,错误优先
缺点
回调地狱
不便于理解、维护
# 回调地狱do1(function() { do2(function() { do3(function() { do4(function() { do5(function() { do6() }); console.log(‘xxxx’) }); }); });});
题目1
## Installationnpm install anywhere -g## Execution$ anywhere// or with port$ anywhere -p 8000// or start it but silent(don't open browser)$ anywhere -s// or with hostname$ anywhere -h localhost -p 8888// or with folder$ anywhere -d ~/git/anywhere// or enable html5 history$ anywhere -f /index.html## Help$ anywhere --helpUsage: anywhere --help // print help information anywhere // 8000 as default port, current folder as root anywhere 8888 // 8888 as port anywhere -p 8989 // 8989 as port anywhere -s // don't open browser anywhere -h localhost // localhost as hostname anywhere -d /home // /home as root anywhere -f /index.html // Enable html5 history,the index is /index.html## Visithttp://localhost:8000
题目2
var os = require('os')console.log(process.uptime()) //获取程序运行时间console.log(os.uptime()) //获取服务器开机时间
相互调用
文件和模块是一一对应的
独立的作用域
,相互之间不冲突
CommonJS
是第一套服务器端约定的模块标准
同步方式加载模块
,只适用于服务器端
AMD 模块标准(适用于浏览器端)
ES6 模块标准(适用于浏览器端)
核心模块
API
fs,http,url,path
等自定义模块
第三方模块
module.id
识别符
绝对路径
的模块文件名module.filename
绝对路径
module.exports
对外输出的值
方式一
exports
,可以被外部调用
exports.name='tom';exports.age=12;exports.display=function(){ console.log(‘I am a person');};
方式二
module.exports
代替 exports
var user = { name:'tom'; age:12; display:function(){ console.log(‘I am a person'); };module.exports = user;
内置的 require 函数用于加载模块文件
require
的基本功能 读入并执行
一个 JavaScript 文件返回该模块的 exports 对象
报错
//加载模块var person1 = require('./1_myModule')var person2 = require('./2_myModule')//使用模块person1.showInfo()person2.showInfo()
规则一
./ 开头
相对路径
从当前文件所在文件夹
开始寻找模块../ 开头
相对路径
从当前文件所在上级文件夹
开始寻找模块直接使用模块名
核心模块
保存在当前及往上各级node_modules文件夹中的模块
规则二
同名的 js 文件
同名的 json 文件
同名的文件夹
下 package.json
中 main
所对应的 js 文件同名文件夹下的 index.js
var name = require('./nav/index.js')console.log(name)//1.核心模块加载时不需要添加路径var os = require('os')//2.非核心模块加载时,如果模块保存在node_modules文件夹下时,也不需要添加路径var newAdd = require('newAdd')console.log(newAdd(5,8))
题目
## add.js//定义模块内容,导出模块数据module.exports = function(num1,num2){ return parseInt(num1) + parseInt(num2)}## sub.js//定义模块内容,导出模块数据module.exports = function(num1,num2){ return num1 - num2}## mul.js//定义模块内容,导出模块数据module.exports = function(num1,num2){ return num1 * num2}## div.js//定义模块内容,导出模块数据module.exports = function(num1,num2){ return num1 / num2}## index.js//加载运算模块var add = require('./calculate/add')var sub = require('./calculate/sub')var mul = require('./calculate/mul')var div = require('./calculate/div')//接受输入参数var num1 = process.argv[2]var num2 = process.argv[3]var option = process.argv[4]var result = 0 //保存运算结果switch(option){ case '+':result = add(num1,num2);break; case '-':result = sub(num1,num2);break; case '*':result = mul(num1,num2);break; case '/':result = div(num1,num2);break; default:console.log('运算符不正确!');process.exit();}console.log('运算结果:',result)
事件源
响应对象
(区别)注册
触发方法
(区别)响应方法
events
模块实现事件机制 events
模块的 EventEmitter 对象
封装了事件触发与事件监听器的功能
导入
事件模块创建 EventEmitter 实例对象
注册
事件事件实例对象.on(‘event_name’,响应方法);
触发
事件事件实例对象.emit(‘event_name’);
//1.导入事件模块var events = require('events')//2.实例事件对象var event_emmiter = new events.EventEmitter()//3.注册事件event_emmiter.on('e_Test',function(){ console.log('事件触发了,触发时间:',new Date().toLocaleTimeString())})//4.触发事件event_emmiter.emit('e_Test')
监督员工系统
## emp.jsconsole.log('当前运行的文件名:',__filename)var emp = { name:'tom', salary:1000, work:function(){ console.log('我开工了。。。。。。') console.log('今天老板不在,玩会手机,爽!') }}module.exports = emp=======================================================## boss.jsvar boss = { name:'大富豪', manager:function(emp){ console.log('有人偷懒,要处罚!') emp.salary -= 100 }}module.exports = boss=======================================================## index.js//非事件管理员工var emp = { name:'tom', salary:1000, work:function(){ console.log('我开工了。。。。。。') console.log('今天老板不在,玩会手机,爽!') }}var boss = { name:'大富豪', manager:function(emp){ console.log('有人偷懒,要处罚!') emp.salary -= 100 }}emp.work()console.log('处罚前薪资:',emp.salary)boss.manager(emp)console.log('处罚后薪资:',emp.salary)=======================================================## index.js//带事件机制的管理员工var emp = require('./emp')var boss = require('./boss')var events = require('events')var event_emmiter = new events.EventEmitter()//注册事件,让boss管理empevent_emmiter.on('e_playPhone',boss.manager)emp.work()console.log('违规之前工资:',emp.salary)event_emmiter.emit('e_playPhone',emp)console.log('违规之后工资:',emp.salary)
火灾报警系统
## fire.jsvar fire = { address:'观前街', level:3, startFire:function(){ console.log('着火了!') }}module.exports = fire===============================================================## fightFire.jsvar fightFire = { fight:function(fire){ switch(fire.level){ case 1:console.log('小火,吐口水就灭了!');break; case 2:console.log('中火,去个中队可以搞定!');break; case 3:console.log('大火,全体出动!');break; default:console.log('地狱烈火,赶紧撤退!') } }}module.exports = fightFire===============================================================## index.jsvar events = require('events')var ee = new events.EventEmitter()var fire = require('./1_fire')var fightFire = require('./2_fightFire')//事件注册ee.on('e_fire',fightFire.fight)fire.startFire()ee.emit('e_fire',fire)
与操作系统互动
内置
path:处理文件路径
fs:操作(CRUD)文件系统
http:提供 HTTP 服务器功能
url:用于解析 URL
querystring:解析 URL 中的查询字符串
crypto:提供加密和解密功能
_ _dirname:获取当前项目文件夹名称
_ _filename:获取当前Node执行的js文件名
属性\方法 | 说明 |
---|---|
basename() | 获取文件路径字符串中的文件名(包含扩展名) |
dirname() | 获取文件路径字符串中的文件所在的全目录名称 |
extname() | 获取文件路径字符串中的文件扩展名) |
join() | 将参数中的字符串拼接成一个文件路径字符串 |
parse() | 将文件路径字符串转换成 path 对象 |
format() | 将 path 对象转换成文件路径字符串 |
// 导入核心模块 pathvar path = require('path')// basename 方法获取完整文件名console.log(path.basename('E:/nodejsDemo2/emp.js'))// dirname 方法获取完整文件夹console.log(path.dirname('E:/nodejsDemo2/emp.js'))// extname 方法获取文件的扩展名,包含小数点(注意)console.log(path.extname('E:/nodejsDemo2/emp.js'))// 1. 连接字符串方式拼接,需要手动添加分隔符console.log(__dirname + '\\boss.js')// 2. path 的 join 方式拼接,自动添加分隔符,可以返回上一级console.log(path.join(__dirname,'..','boss.js'))var strPath = 'E:/nodejsDemo2/emp.js'console.log(path.parse(strPath).base)
属性\方法 | 说明 |
---|---|
stat() | 获取文件或文件夹状态 |
mkdir() | 创建文件夹 |
writeFile() | 创建写入文件 |
readFile() | 读取文件 |
appendFile() | 追加文件 |
readdir() | 读取文件夹,获取文件夹下的所有文件和子文件夹信息 |
unlink() | 删除文件 |
rmdir() | 删除文件夹 |
var fs = require('fs')fs.stat('emp.js',function(err,status){ if(err){ console.log('发生错误,错误信息:',err) }else{ console.log(status) }})var student = { name:'alex', age:28, sex:true, address:'人民路18号'}//覆盖式写文件fs.writeFile('stuInfo.txt',JSON.stringify(student),function(err){ if(err){ console.log('文件写入失败,原因:',err) }else{ console.log('文件写入成功!') }})//追加式写文件fs.appendFileSync('newstuInfo.txt','tom 18 true 人民路18号\r\n',function(err){ if(err){ console.log('文件写入失败,原因:',err) }else{ console.log('文件写入成功!') }})
文件读取
// 浏览器中的 Javascript 是没有文件操作的能力的// 但是 Node 中的 JavaScript 具有文件操作的能力// fs 是 file-system 的简写,就是文件系统的意思// 在 Node 中如果想要进行文件操作,就必须引入 fs 这个核心模块// 在 fs 这个核心模块中,就提供了所有的文件操作相关的 API// 例如:fs.readFile 就是用来读取文件的// 1. 使用 require 方法加载 fs 核心模块var fs = require('fs')// 2. 读取文件// 第一个参数就是要读取的文件路径// 第二个参数是一个回调函数// 成功// data 数据// error null// 失败// data undefined 没有数据// error 错误对象fs.readFile('./data/hello.txt',function(error,data){ // 文件中存储的其实都是二进制数据 0 和 1 // 为什么看到的不是二进制,是由于二进制转换为了十六进制了 // 可以通过 toString 方法把其转化为我们能认识的字符 // console.log(data.toString()); // 错误判断 if(error){ console.log('读取文件失败') }else{ console.log(data.toString()); }})
文件写入
// 第一个参数:文件路径// 第二个参数:文件内容// 第三个参数:回调函数// error// 成功:// 文件写入成功// error 是 null// 失败:// 文件写入失败// error 就是错误对象fs.writeFile('./data/你好.md','大家好,给大家介绍一下,我是Node.js',function(error){ // 判断 if(error){ console.log('写入失败'); }else{ console.log('写入成功'); }})
同步和异步
两种形式 阻塞
代码的执行读取任务下达到事件队列处理
,任务执行完成后执行回调
try catch 异常处理
方式通过回调函数的第一个参数返回
var fs = require('fs')//try把可能存在错误的代码包裹起来try{ var content = fs.readFileSync('stuInfo2.txt','utf-8') console.log('文件读取成功!') fs.writeFileSync('stuInfo1.txt',content)}//一旦出现错误,保证程序正常运行catch(err){ console.log('本次错误信息:',err.message)}console.log('文件复制成功!')console.log('我啥时运行?')
try
块:包含可能出现异常
的代码块catch
块:捕获异常并处理
的代码块throw
:抛出异常
错误属性/方法 | 说明 |
---|---|
message | 异常的说明信息 |
name | 异常对象的类型名 |
toString() | 上述属性的合成信息 |
题目
//获取并显示当前项目目录中的所有的文件,不包含文件夹var fs = require('fs')//获取指定位置下的所有文件和子文件夹fs.readdir(__dirname, function (err, result) { if (err) { console.log('读取错误!信息:', err.message) } else { //循环遍历所有的文件和子文件夹 for (let i = 0; i < result.length; i++) { //查看每一个文件或文件夹的状态 fs.stat(result[i], function (e, status) { if (e) { console.log('文件读取错误,信息:',err.message) } else { //如果循环访问到的是文件的话就输出显示,文件夹的话就忽略 if (status.isFile()) { console.log(result[i] + '是文件') } } }) } }})
文件流、网络流
二进制
的面向对象
的概念对文件数据进行的抽象
文件数据
的操作方式读取
数据 data
事件:读取到文件数据时触发
end
事件:读取文件结束时触发
写入
数据 write
(date,编码方式
) 方法finish
事件## readStream.jsvar fs = require('fs')//创建读取文件流对象var fread = fs.createReadStream('stuInfo.txt')var content = ''//注册读取事件fread.on('data',function(result){ //把读取的到的数据进行拼接 content += result})//注册读取完成事件fread.on('end',function(){ console.log('文件的内容是:',content)})=====================================================## writeStream.jsvar fs = require('fs')//创建文件输出流对象var fwrite = fs.createWriteStream('stuInfo2.txt','utf-8')fwrite.write('我学习了NodeJS!')//结束文件流输出工作fwrite.end() fwrite.on('finish',function(err){ if(err){ console.log('写文件失败,错误信息;',err.message) }else{ console.log('写入完成!') }})
方法\事件 | 说明 |
---|---|
createReadStream() | 获取文件读取流对象 |
createWriteStream() | 获取文件输出流对象 |
data 事件 | 数据读取到数据时触发的事件 |
end 事件 | 数据读取结束后触发的事件 |
finish 事件 | 数据写入结束后触发的事件 |
write() | 利用输出流将内容写入文件中 |
pipe() | 将数据从读取流中取出,传输到输出流中 |
题目
var fs = require('fs')var path = require('path')//递归:自我调用,内容变化,变化会导致调用结束//递归获取指定文件夹下的所有文件和子文件夹信息function getInfo(filePath){ var fileInfos = fs.readdirSync(filePath) //获取当前文件夹filePath下所有的文件和子文件夹 for(var i = 0 ; i < fileInfos.length ; i++){ var absPath = path.join(filePath,fileInfos[i]) var stats = fs.statSync(absPath) //获取指定文件或文件夹的状态 if(stats.isDirectory()){ console.log('文件夹:',absPath) getInfo(absPath) }else{ console.log('文件:',absPath) } }}getInfo(__dirname)//stat方法的使用,建议采用绝对路径// fs.stat(path.join(__dirname,'node_modules','jquery','dist','jquery.js'),function(err,status){ // console.log(status.isDirectory())// })
题目
添加该员工的工资交税金额(按5%计算)
var fs = require('fs')//1.读取文件fs.readFile('emp.txt', function (err, content) { //2.修改内容 var newContent = '' content = content.toString('utf-8') var contents = content.split('\r\n') for (var i = 0; i < contents.length; i++) { var reg = /\d+/gi var tax = parseInt(contents[i].match(reg)) * 0.05 newContent += contents[i] + ' ' + tax + '\r\n' } console.log(newContent) //3.覆盖文件 fs.writeFile('emp.txt',newContent,function(err){ if(err){ console.log('修改失败!') }else{ console.log('修改完成!') } })})
HTTP:超文本传输协议
网络应用层
的协议,是一个无状态
的协议提供服务器和服务的功能
创建服务器和客户端
网络套接字开发
request:请求对象
response:响应对象
createServer(req,res,callback)
创建并返回服务器对象
req:http 请求对象
res:http 响应对象
callback:http 请求成功后执行的回调函数
listen(port,hostname,callback)
服务器对象的监听方法
port:服务器端口
hostname:服务器名
callback:监听到请求后执行的回调函数
http.get( )
方法用于访问远程服务器资源
,可以实现爬虫
的功能http.get(url,function(result){ result.on(‘data’,function(){ //接受数据 }) result.on(‘end’,function(){ //处理接收到的数据 })})
利用 node 使用 cheerio 爬取数据
var http = require('http')var cheerio = require('cheerio')http.get('http://search.jumei.com/?filter=0-11-1&search=%E9%9D%A2%E8%86%9C',function(res){ var fullContent = '' res.on('data',function(content){ fullContent += content //接受数据 }) res.on('end',function(){ // console.log(fullContent) var $ = cheerio.load(fullContent) $('.s_l_name a').each(function(index,item){ console.log($(item).text().trim()) }) })})
题目
var http = require('http')http.createServer(function (req, res) { console.log('有用户来访!') res.setHeader('content-Type','text/html;charset=utf-8') var ulHtml = `
解析 URL 格式
的模块属性 | 说明 |
---|---|
href | 最初解析的完整URL字符串 |
protocol | 请求协议类型 |
host | URL的完整主机部分,包含端口 |
hostname | 主机的主机名部分 |
port | 主机的端口号部分 |
pathname | URL的路径部分(包括最初的/) |
search | URL的查询字符串部分,包括前导的问号 |
parse() | 将url字符串转换成url对象 |
resolve | 将url的部分内容组合在一起 |
var url = require('url')var str_url = 'http://www.wedn.net:8080/admin/users.html?name=zhaojy&age=18#profile'var myUrl = url.parse(str_url)console.log(myUrl.pathname)console.log(myUrl.search)var url1 = 'http://www.163.com'var url2 = 'books/newbook.html'console.log(url.resolve(url1,url2)) //拼接url的部分
querystring
是一个解析查询字符串
的模块parse
方法用于把字符串转换成 querystring 对象
stringify
方法用于把 querystring 对象转换成字符串
var url = require('url')var qs = require('querystring')var str_url = 'http://www.wedn.net:8080/admin/users.html?name=zhaojy&age=18#profile'var myUrl = url.parse(str_url)var strqs = myUrl.search//1.传统字符串函数解析// console.log(strqs.slice(1).split('&')[0].split('=')[0])// console.log(strqs.slice(1).split('&')[0].split('=')[1])// console.log(strqs.slice(1).split('&')[1].split('=')[0])// console.log(strqs.slice(1).split('&')[1].split('=')[1])//2.qs模块解析console.log(qs.parse(strqs.slice(1)))
request 对象表示 HTTP 请求
,包含了请求查询字符串,参数,内容,HTTP 头部等属性
通过 url 传递请求参数
http://localhost:3000/user?name=tom&email=tom@163.com
在服务器端通过 url 对象的相关方法获取 get 参数
## get.jsvar http = require('http')var url = require('url')var qs = require('querystring')http.createServer(function (req, res) { console.log('有用户来访!') //处理请求 console.log(req.url) if (req.url != '/favicon.ico') { var str_qs = url.parse(req.url).search.slice(1) var userName = qs.parse(str_qs).name var userEmail = qs.parse(str_qs).email console.log('name:',userName) console.log('email:',userEmail) //响应输出 res.setHeader('content-Type', 'text/html;charset=utf-8') res.end('hello') }}) .listen(3000, function (err) { if (err) { console.log('服务器发生错误,无法监听!') } else { console.log('服务器开始监听。。。。。。。') } })
post
方式传递请求参数
通过request对象的事件获取post参数
req.on(‘data’,callback); //接收参数时req.on(‘end’,callback); //接收参数结束时
## post.jsvar http = require('http')var url = require('url')var qs = require('querystring')http.createServer(function (req, res) { console.log('有用户来访!') //处理请求 console.log(req.url) if (req.url != '/favicon.ico') { var fullcontent = '' req.on('data',function(content){ fullcontent += content }) req.on('end',function(){ console.log('post传参:',fullcontent) }) //响应输出 res.setHeader('content-Type', 'text/html;charset=utf-8') res.end('hello') }}) .listen(3000, function (err) { if (err) { console.log('服务器发生错误,无法监听!') } else { console.log('服务器开始监听。。。。。。。') } })
题目
post
方式,实现注册新用户
的功能,将注册用户信息保存在users.json
文件中## register.html## login.html ================================================================## demo.jsvar http = require('http')var url = require('url')var qs = require('querystring')var fs = require('fs')var path = require('path')//创建服务器http.createServer(function (req, res) { res.setHeader('content-type', 'text/html;charset=utf-8') if (req.url != '/favicon.ico') { var fullcontent = '' //接受用户post传入的数据 req.on('data', function (content) { fullcontent += content }) //完成接受用户post传入的数据 req.on('end', function () { var objqs = qs.parse(fullcontent) //解析新注册的数据,并转换成json对象 //1.读取文件 fs.readFile(path.join(__dirname, 'user.json'), function (err, users) { var usersData = JSON.parse(users) //把json文件中的用户数据字符串转换成对象 usersData.users.push(objqs) //把新注册的用户数据添加到已有的用户数组中 //把修改后的数据保存到原来的用户文件里 fs.writeFile(path.join(__dirname, 'user.json'), JSON.stringify(usersData), function (err) { if (err) { res.statusCode = 200 res.end('') } else { res.statusCode = 200 res.end('') } }) }) }) }}).listen(3000)
response 对象表示 HTTP 响应
,即在接收到请求时向客户端发送的 HTTP 响应数
.常见响应头的状态码
状态码 | 说明 |
---|---|
1xx | 请求已受理,继续处理中 |
2xx | 请求被成功处理 |
3xx | 请求被重定向 |
4xx | 客户端请求错误 |
5xx | 服务器错误 |
请求对象
获取静态资源的文件路径
fs
进行读取
,获取资源内容响应对象
将网页资源输出
客户端请求对象
和 path 模块
获取资源文件的扩展名MIME
类型 MIME(Multipurpose Internet Mail Extensions)多用途互联网邮件扩展类型
通过输出对象设置content-type
var http = require('http')var url = require('url')var fs = require('fs')var path = require('path')http.createServer(function (req, res) { console.log('有用户来访!') if (req.url != '/favicon.ico') { var filePath = path.join(__dirname,'static',url.parse(req.url).pathname.slice(1)) //获取静态网页的名称 fs.readFile(filePath, function (err, htmlcontent) { if (err) { console.log('错误信息:', err.message) } console.log(htmlcontent.toString('utf-8')) res.setHeader('content-Type', 'text/html;charset=utf-8') res.end(htmlcontent.toString('utf-8')) }) } }).listen(3000)
题目
var http = require('http')var fs = require('fs')var url = require('url')var qs = require('querystring')var path = require('path')http.createServer(function(req,res){ res.setHeader('content-type','text/html;charset=utf-8') if(req.url != '/favicon.ico'){ //提取查询的价格 var price = qs.parse(url.parse(req.url).search.slice(1)).price //读取文件 fs.readFile('products.json',function(err,content){ if(err){ console.log('文件读取出现异常!') res.end('查询失败!') }else{ // var queryProducts = [] //保存符合查询条件的商品 var strProductHTML = '
题目
http://localhost:3000/addProduct
## product.html====================================================================## product.jsvar http = require('http')var fs = require('fs')var url = require('url')var qs = require('querystring')var path = require('path')http.createServer(function (req, res) { console.log('有客户访问') res.setHeader('content-type', 'text/html;charset=utf-8') if (req.url != '/favicon.ico') { var fullContent = '' req.on('data', function (content) { fullContent += content }) req.on('end', function () { var newProduct = qs.parse(fullContent) //1.读取现有文件 fs.readFile('products.json', function (err, products) { if (err) { console.log('文件读取错误!') res.statusCode = 500 res.end('商品入库失败!') } else { //2.数据添加 var products = JSON.parse(products.toString()) console.log('products',products) products.push(newProduct) //3.写入新文件 fs.writeFile('products.json', JSON.stringify(products), function (err) { if (err) { console.log('文件记录失败!') res.statusCode = 500 res.end('商品入库失败!') }else{ res.statusCode = 200 res.end('商品入库成功!') } }) } }) }) }}).listen(3000, 'localhost', function (err) { console.log('开始监听')})
EJS
是一个服务器模板
后端数据
和HTML页面
结合起来,生成动态网页
EJS
是一个第三方模
块,需要安装加载后使用
EJS 常见应用
定义页面结构
输出服务器数据
EJS 特性
EJS 模板和 HTML结构相似
使用特殊标记绑定后台数据
安装
npm install ejs --save
使用
导入模块
调用方法渲染页面
在回调函数中,读取数据并输出到页面
ejs.renderFile( filename , data , callback)
## page.ejsEJS模板页面 这是EJS渲染的页面
=========================================================## demo.jsvar http = require('http')var fs = require('fs')var url = require('url')var qs = require('querystring')var path = require('path')var ejs = require('ejs')http.createServer(function (req, res) { res.setHeader('content-type', 'text/html;charset=utf-8') if (req.url != '/favicon.ico') { //判断用户请求是否是针对page1.ejs模板文件 if (req.url == '/page1') { fs.readFile('view/page1.ejs', function (err, content) { if (err) { console.log('ejs文件加载失败!') res.end('网页不存在!') } else { res.end(content) } }) // ejs.renderFile('view/page1.ejs',{},function(err,content){ // if(err){ // console.log('ejs文件加载失败!') // res.end('网页不存在!') // }else{ // res.end(content) // } // }) } }}).listen(3000, '127.0.0.1', function (err) { console.log('开始监听')})
<% %>:运行服务器端代码,等价于{ }
<%= %>:输出变量到页面
## page.ejsEJS模板页面 这是EJS渲染的页面
<%=title%>
<%=content%>
<% var now = new Date('2009-1-1') var year = now.getFullYear() if(year % 4 == 0 && year % 100 != 0 || year % 400 == 0){ %> 闰年 <% } else{ %> 不是闰年 <% } %>
demo.ejs
互联网大神名录
<%=godsInfo.gods[i].intr%>
demo.js
var http = require('http')var fs = require('fs')var url = require('url')var qs = require('querystring')var path = require('path')var ejs = require('ejs')http.createServer(function (req, res) { res.setHeader('content-type', 'text/html;charset=utf-8') if (req.url != '/favicon.ico') { //判断用户请求是否是针对page1.ejs模板文件 if (req.url == '/myGods') { var myGodsData = null // myGodsData = JSON.parse(fs.readFileSync('myGods.json').toString()) //同步方式获取数据 //1.读取myGods.json的数据 fs.readFile('myGods.json', function (err, content) { myGodsData = JSON.parse(content.toString()) //2.渲染EJS模板页面 ejs.renderFile('view/myGods.ejs', { godsInfo: myGodsData }, function (err, content) { if (err) { console.log('ejs文件加载失败!') res.end('网页不存在!') } else { res.end(content) } }) }) }else{ // 处理非ejs文件的其他静态资源文件,例如图片等 console.log('imgPath:',req.url) // 读取图片文件内容,并通过响应对象,向客户端发出 fs.readFile('public/images' + req.url,function(err,content){ if(err){ console.log('err:',err.message) }else{ console.log('content',content) res.setHeader('content-type','image/jpg') res.end(content) } }) } }}).listen(3000, '127.0.0.1', function (err) { console.log('开始监听')})
路由(Routing)
是由一个URL
和一个特定的HTTP方法
(GET\POST等
)组成的应用如何响应客户端对某个网站节点的访问
对请求路径的获取
,然后调用相应的EJS模板
,完成页面的跳转
var http = require('http')var fs = require('fs')var url = require('url')var qs = require('querystring')var path = require('path')var ejs = require('ejs')http.createServer(function (req, res) { res.setHeader('content-type', 'text/html;charset=utf-8') if (req.url != '/favicon.ico') { if(req.url == '/page1'){ ejs.renderFile('view/page1.ejs', { title:'我是EJS的标题',content:'我是EJS的内容',names:['tom','jack','mary']}, function(err,content){ res.end(content) }) }else if(req.url == '/page2'){ ejs.renderFile('view/page2.ejs',{ name:[]},function(err,content){ res.end(content) }) }else{ res.statusCode = 404 res.end('无此网页,访问错误!') } }}).listen(3000, '127.0.0.1', function (err) { console.log('开始监听')})
路由负责业务功能
包括处理请求的EJS模板和业务逻辑
定义路由模块
,实现HTTP Server
代码的简化
## routes.jsvar App = { page1:function(req,res){ res.end('我是第一个页面') }, page2:function(req,res){ res.end('我是第二个页面') }, login:function(req,res){ }, register:function(req,res){ }, query:function(req,res){ }}module.exports = App=============================================## index.jsvar http = require('http')var fs = require('fs')var url = require('url')var qs = require('querystring')var path = require('path')var App = require('./model/routes') //导入自定义路由模块http.createServer(function (req, res) { res.setHeader('content-type', 'text/html;charset=utf-8') if (req.url != '/favicon.ico') { var path = url.parse(req.url).pathname.slice(1) try { App[path](req,res) } catch (err) { res.statusCode = 404 res.end('网页不存在,请检查!') } }}).listen(3000, '127.0.0.1', function (err) { console.log('开始监听')})
题目
安装加载 mysql 模块
npm install mysql --save
数据库的连接
创建数据库连接对象
connect
方法,连接数据库
mysql
模块对象createConnection
方法参数名称 | 描述 |
---|---|
host | 数据库服务器主机 IP |
port | 数据库服务器端口3306 |
database | 数据库服务器的数据库对象 |
user | 用户,root |
password | 密码 |
连接数据库
connect方法
权限问题
解决方法alter user ‘root’@’localhost’ identified with mysql_native_password by ‘密码’; flush privileges;
关闭数据库连接
end方法
destory方法
var mysql = require('mysql')//创建数据库连接对象,设置数据库连接参数var connection = mysql.createConnection({ host:'localhost', port:3306, database:'myserver', user:'root', password:'123456'})//连接数据库connection.connect(function(err){ if(err){ console.log('数据库连接失败!',err.message) }else{ console.log('数据库连接成功!') //操作数据库的代码段 console.log('数据库的增删改查。。。。。。') //关闭连接 connection.end() }})
query
方法语法
connection.query ( 查询语句 , 回调函数(err,results,fields){ 处理查询后的后续操作});
err:查询错误对象
results:查询的记录信息,需要做JSON的格式处理
fields:查询数据的字段信息
//连接数据库connection.connect(function(err){ if(err){ console.log('数据库连接失败!',err.message) }else{ console.log('数据库连接成功!') //操作数据库的代码段 connection.query('select * from emp',function(err,results){ if(err){ console.log('数据库查询失败!',err.message) }else{ console.log('数据查询结果:',results) } }) //关闭连接 connection.end() }})
将数据库数据传到前端
var http = require('http')var mysql = require('mysql')http.createServer(function(req,res){ res.setHeader('content-type','text/html;charset=utf-8') if(req.url != '/favicon.ico'){ //连接数据库,获取数据,响应对象发送数据 var connection = mysql.createConnection({ host:'localhost', port:3306, database:'myserver', user:'root', password:'123456' }) connection.connect(function(err){ if(err){ console.log('数据库连接失败!') res.end(JSON.stringify({ code:500,message:'无法查询数据!'})) }else{ connection.query('select * from emp',function(err,results){ if(err){ console.log('数据库查询失败',err.message) }else{ res.end(JSON.stringify({ code:200,message:'查询完成',data:results})) } }) } connection.end() }) }}).listen(3000,function(){ console.log('开始监听。。。')})
login.html
login.js
var http = require('http')var url = require('url')var qs = require('querystring')var mysql = require('mysql')http.createServer(function (req, res) { res.setHeader('content-type', 'text/html;charset=utf-8') res.setHeader('Access-Control-Allow-Origin','*') // 允许跨域请求 if (req.url != '/favicon.ico') { if (req.url == '/login') { var fullContent = '' // 使用post方式接受参数 req.on('data', function (content) { fullContent += content }) req.on('end', function (err) { if (err) { console.log('数据接受失败!') res.end(JSON.stringify({ code: 500, message: '数据传参不正确!' })) } else { var user = qs.parse(fullContent) console.log('user:', user) // 把参数传入数据库,进行登录验证 // 连接数据库,获取数据,响应对象发送数据 var connection = mysql.createConnection({ host: 'localhost', port: 3306, database: 'myserver', user: 'root', password: '123456' }) connection.connect(function (err) { if (err) { console.log('数据库连接失败!') res.end(JSON.stringify({ code: 500, message: '无法查询数据!' })) } else { var sql = 'select * from emp where eName=? and empId=?' console.log('sql:',sql) connection.query(sql,[user.userName,user.userPwd],function (err, results) { if (err) { console.log('登录失败', err.message) } else { if(results.length > 0){ res.end(JSON.stringify({ code: 200, message: '登录成功!', data: results })) }else{ res.end(JSON.stringify({ code: 200, message: '登录失败!', data: results })) } } }) } connection.end() }) } }) } }}).listen(3000, function () { console.log('开始监听。。。')})
跨域请求的处理
,采用服务器端设置响应头参数
res.setHeader('Access-Control-Allow-Origin','*')
注入式攻击
是指传入的参数中包含sql脚本
,执行特殊的攻击命令
connection.query ( 查询语句 , 参数,回调函数(err,result ){ 处理查询后的后续操作});
解决方法
// 传统字符串拼接有漏洞,容易被注入式攻击// var sql = "select * from emp where eName='" // + user.userName + "' and empId = " + user.userPwd// ES6字符串拼接也有漏洞// var sql = `select * from emp where eName='${user.userName}' and empId=${user.userPwd}`// 利用参数方式堵住漏洞var sql = 'select * from emp where eName=? and empId=?'connection.query(sql,[user.userName,user.userPwd],function (err, results) { if (err) { console.log('登录失败', err.message) } else { if(results.length > 0){ res.end(JSON.stringify({ code: 200, message: '登录成功!', data: results })) }else{ res.end(JSON.stringify({ code: 200, message: '登录失败!', data: results })) } }})
异步
问题的影响,会造成数据返回不正常
回调方式
返回数据使用Ajax异步请求服务器
login.html
login.js
var http = require('http')var url = require('url')var routes = require('./6_routes') //导入路由模块http.createServer(function (req, res) { res.setHeader('content-type', 'text/html;charset=utf-8') res.setHeader('Access-Control-Allow-Origin','*') //允许跨域请求 if (req.url != '/favicon.ico') { if(routes[req.url.slice(1)]){ //路由正确时 routes[req.url.slice(1)](req,res) }else{ //路由地址错误时 res.end(JSON.stringify({ code:404,message:'您输入的url地址不正确'})) } }}).listen(3000, function () { console.log('开始监听。。。')})
routes.js
var qs = require('querystring')var loginDB = require('./7_loginDB')var routes = { login: function (req, res) { // if (req.url == '/login') { var fullContent = '' //使用post方式接受参数 req.on('data', function (content) { fullContent += content }) req.on('end', function (err) { if (err) { console.log('数据接受失败!') res.end(JSON.stringify({ code: 500, message: '数据传参不正确!' })) } else { var user = qs.parse(fullContent) console.log('user:', user) //把参数传入数据库,进行登录验证 loginDB(user,function(err,results){ if(err){ res.end(JSON.stringify({ code:500,message:'登录失败'})) }else{ if(results.length>0){ res.end( JSON.stringify({ code: 200, message: '登录成功!', data: results })) }else{ res.end( JSON.stringify({ code: 200, message: '登录失败!', data: results })) } } }) } }) // } }, resister: function (req, res) { }, addUser: function (req, res) { }}module.exports = routes
loginDB.js
var db = require('./8_commonDB')//连接数据库,获取数据,相应对象发送数据function login(user,cb){ var sql = 'select * from emp where eName=? and empId=?' var params = [user.userName,user.userPwd] db(sql,params,function(err,results){ if(err){ cb(err,null) }else{ cb(null,results) } })}module.exports = login
commonDB.js
var mysql = require('mysql')function db(sql,params,cb){ var connection = mysql.createConnection({ host: "localhost", port: 3306, database: "myserver", user: "root", password: "123456", }); connection.connect(function (err) { if (err) { console.log("数据库连接失败!"); res.end( JSON.stringify({ code: 500, message: "无法查询数据!" }) ); } else { connection.query(sql, params, function (err, results) { if (err) { cb(err,null) //执行回调函数,并且把参数返回 } else { cb(null,results) } }); } connection.end(); });}module.exports = db
query
方法语法
connection.query ( 增删改语句 ,语句使用的参数, 回调函数(err,result ){ 处理增删改后的后续操作});
err:查询错误对象
result:查询结果
语法
var insert_sql = ‘ insert into dept values(?,?,?)’;var insert_para = [‘40’,’技术部2部’,’F区’];
?
表示形式参数
数组
方式提供实际参数
query
方法语法
connection.query ( 调用存储过程的语句,语句使用的参数, 回调函数(err,result ){ 处理后续操作});
err:查询错误对象
result:查询结果
连接池技术
,减少数据库连接的数量
var mysql = require('mysql');var pool = mysql.createPool({ config});//通过连接池获取数据库连接对象pool.getConnection(function(err, connection) { if (err) throw err; // 连接失败 connection.query(‘SQL语句', function (error, results, fields) { if (error) throw error connection.release() //释放连接 })})
Express
是 Node.js
的一个轻量级的 Web 框架
减少创建应用程序所需要的时间
MVC
结构,简化开发难度
官网
简单安装
npm install express --save
比原生Server模块要简单
var express = require('express');var app = express();app.get('/',function(req,res){ console.log('hello express!');});app.listen(3000,function(){ console.log('Start Listen!');});
Express 路由配置组成
请求方式(GET,POST等)
请求访问路径
语法
app.Method(path, [callback] callback)
Method 分别为 get,post 方式
path 是请求 url 的路径部分(pathname)
语法
app.use('路由名', 路由模块对象)
use 方法用于加载路由模块
路由模块内部通过get、post方法设置子路由
语法
app.use(express.static(path.join(__dirname, 'public')))
设置静态文件的文件夹目录
保存静态页面、样式、脚本和资源文件
语法
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
设置动态页面的文件夹目录为 views
设置动态页面的类型为 ejs 模板
路由参数
的方法 Get
方式传参,通过 req.query
获取Post
方式传参,通过 req.body
获取 body-parser
模块,并加载
app.use(bodyParse.urlencoded({extended:false}))
app.use(bodyParse.json())
RESTF风格传参,通过params获取
传参格式/value1/value2
客户端
方法 send(‘’):将字符串发送到客户端
json({ }):将一个json对象发送到客户端
render(‘’):将一个模板页面发送到客户端
服务器跳转
方法 redirect()
中间件的概念
在请求和响应之间实现业务的功能单元
是 Express 的核心
应用型
:通过app加载
使用路由型
:通过路由模块Router加载
内置型
:例如静态文件的设置
第三方
:例如body-parser模块
app.use()挂载
不加路径
的中间件,在任何请求
时都会执行next( )
,或者响应输出
,否则会导致挂起
app.use( path , function(req, res, next) { next(); })
M:模型层(model)
V:视图层(view)
C:业务逻辑层(control)
脚手架方式安装(cli)
npx express-generator
脚手架方式启动
npm start
// 在 Node 中专门提供了一个核心模块: http// http 这个模块的职责就是帮你创建编写服务器的// 1. 加载 http 核心模块var http = require('http')// 2. 使用 http.createServer() 方法创建一个 Web 服务器// 返回一个 Server 实例var server = http.createServer()// 3. 服务器要干嘛?// 提供服务: 对数据的服务// 发请求// 接受请求// 处理请求// 反馈(发送响应)// 注册 request 请求事件// 当客户端请求过来,就会自动触发服务器的 request 请求事件,然后执行第二个参数;回调处理函数server.on('request',function(){ console.log('收到客户端的请求了');})// 4. 绑定端口号,启动服务器server.listen(3000,function(){ console.log('服务器启动成功了,可以通过 http://127.0.0.1:3000/ 来进行访问');})
var http = require('http');var server = http.createServer();// request 请求事件处理函数,需要接收两个参数;// Request 请求对象// 请求对象可以用来获取客户端的一些请求信息,例如请求路径// Response 响应对象// 响应对象可以用来给客户端发送响应信息server.on('request',function(request,response){ console.log('收到客户端的请求了,请求路径是:'+ request.url); // response 对象有一个方法:write 可以用来给客户端发送响应数据 // write 可以使用多次,但是最后一定要使用 end 来结束响应,否则客户端会一直等待 response.write('hello node.js'); // 告诉客户端,话说完了,可以呈递用户 response.end();})server.listen(3000,function(){ console.log('服务器启动成功,可以通过 localhost:3000 进行访问');})
// 根据不同的请求路径返回不同数据var http = require('http'); // 1. 创建 Servervar server = http.createServer();// 2. 监听 request 请求事件,设置请求处理函数server.on('request',function(req,res){ // console.log('收到请求了,请求路径是:'+req.url); // res.write('hello'); // res.write(' world'); // res.end(); // 上面的方式比较麻烦,推荐使用更简单的方式,直接 end 的同时发送响应数据 // res.end('hello world'); // 根据不同的请求路径发送不同的响应结果 // 1. 获取请求路径 // req.url 获取到的是端口号之后的那一部分路径 // 也就是说所有的 url 都是以 / 开头的 // 2. 判断路径处理响应 var url = req.url; // if(url === '/'){ // res.end('index page') // }else if (url === '/login') { // res.end('login page') // }else { // res.end('404 Not Found') // } if (url === '/products') { var products = [ { name: '苹果', price: 8888 }, { name: '香蕉', price: 5888 }, { name: '菠萝', price: 3888 } ] // 响应内容只能是二进制数据或者字符串 // 数字 // 对象 // 数组 // 布尔值 // 都不行 res.end(JSON.stringify(products)) }})// 3. 绑定端口号,启动服务server.listen(80,function(){ console.log('服务器启动成功, 可以访问!');})
jQuery 的 each 和原生的 JavaScript 方法 forEach
[].slice.call(jQuery实例对象)
在文件操作的相对路径中 ./data/a.txt 相对于当前目录 data/a.txt 相对于当前目录 /data/a.txt 绝对路径,当前文件模块所处磁盘根目录 c:/xx/xx.... 绝对路径fs.readFile('./data/a.txt',function(err,data){ if(err){ console.log(err); return console.log('读取失败'); } console.log(data.toString());})
// 这里如果忽略了,则也是磁盘根目录require('/data/foo.js')// 相对路径require('./data/foo.js')// 模块加载的路径中的相对路径不能省略 ./
nodemon
来帮我们解决频繁修改代码重启服务器问题// 在任意目录执行该命令都可以// 也就是说,所有需要 --global 来安装的包都可以在任意目录执行npm install --global nodemon
安装完毕之后,使用:
node app.js// 使用 nodemonnodemon app.js
只要是通过 nodemon app.js
启动的服务,则它会监视你的文件变化,当文件发生变化的时候,自动帮你重启服务器
fs
核心模块,http 服务构建的 http
模块,path
路径操作模块,os
操作系统信息模块。。。// 用来操作文件系统var fs = require('fs')// 用来加载 httpvar http = require('http')// 用来获取操作系统var os = require('os')// 用来操作路径var path = require('path')...
require
,exports
,是 Node.js 才有的require
是一个方法 fs
,http
a.js
var foo = 'aaa'console.log('a start');function add(x,y){ return x + y;}// Error:Cannot find module 'b'// require('b')require('./b.js');// 推荐:可以省略后缀名require('./b')console.log('a end');console.log('foo 的值是' + foo);
b.js
// 不能访问外部文件// 外部文件也不能访问此文件console.log('b start');console.log(add(10,20)); // 不能获取到 a.js 中的 add 方法var foo = 'bbb'; // 与 a.js 中的 foo 不冲突require('./c.js');console.log('b end');
c.js
console.log('ccc');
显示结果:
模块作用域默认是封闭的
如何让模块与模块之间进行通信
有时候,我们加载文件模块的目的不是为了简简单单的执行里面的代码,更重要的是为了使用里面的某个成员
require 方法有两个作用:
在每个文件模块中都提供了一个对象:exports
a.js
var bExports = require('./b');var fs = require('fs');console.log(bExports.foo);console.log(bExports.add(10,20));console.log(bExports.age);//未挂载,访问不到bExports.readFile('./a.js');fs.readFile('./a.js',function(err,data){ if (err) { console.log('读取文件失败'); }else{ console.log(data.toString()); }})
b.js
var foo = 'bbb'// console.log(exports);exports.foo = 'hello'exports.add = function(x,y){ return x+y;}exports.readFile = function(path,callback){ console.log('文件路径:',path);}var age = 18;function add(x,y){ return x-y;}
npm
npm
上npm
来下载var 名字 = require('npm install 包名)
node_modules
node_modules/express
node_modules/express/package.json
node_modules/express/package.json main
package.json
或者 package.json main
不成立,则查找备选项:index.js
node_modules
按照上面的规则继续查找can not find module xxx
回调函数
来获取function add(x,y){ console.log(1) setTimeout(function(){ console.log(2) var ret = x + y return ret }, 1000) console.log(3) // 到这里执行就结束了,不会等到前面的定时器,所以直接就返回了默认值 undefined}console.log(add(10,20)) // => undefined
function add(x,y){ var ret console.log(1) setTimeout(function(){ console.log(2) ret = x + y }, 1000) console.log(3) return ret}console.log(add(10,20)) // => undefined
function add(x,y,callback){ // callback 就是回调函数 // var x = 10 // var y = 20 // var callback = function(ret){ console.log(ret) } console.log(1) setTimeout(function(){ var ret = x + y callback(ret) },1000)}add(10,20,function(ret){ console.log(ret)})
function get(url,callback){ var oReq = new XMLHttpRequest() // 当请求加载成功之后要调用指定的函数 oReq.onload = function(){ // 我现在需要得到这里的 oReq.responseText callback(oReq.responseText) } oReq.open("get",url,true) oReq.send()}get('data.json', function(data){ console.log(data)})
var http = require('http')var server = http.createServer()// 2. 监听 request 请求事件,设置请求处理函数server.on('request',function(req,res){ console.log('收到请求了,请求路径是:' + req.url); console.log('请求我的客户端的地址是:',req.socket.remoteAddress,req.socket.remotePort); res.end('hello nodejs')})server.listen(3000,function(){ console.log('服务器启动成功,可以访问!');})
var http = require('http') var server = http.createServer() server.on('request',function(req,res){ // res.setHeader('Content-Type','text/plain;charset=utf-8') // res.end('hello 世界') var url = req.url; if(url === '/plain'){ // text/plain 就是普通文本 res.setHeader('Content-Type','text/plain; charset=utf-8') res.end('hello 世界') }else if(url === '/html'){ // 如果你发送的是 html 格式的字符串,则也要告诉浏览器我给你发送的是 text/html 格式的内容 res.setHeader('Content-Type','text/html; charset=utf-8') res.end('hello html 点我
') } }) server.listen(3000,function(){ console.log('Server is running'); })
var fs = require('fs')var http = require('http')var server = http.createServer()server.on('request',function(req,res){ var url = req.url if (url === '/') { // 肯定不这么干 // res.end('首页
') // 我们要发送的还是在文件中的内容 fs.readFile('./resource/index.html',function(err,data){ if (err) { res.setHeader('Content-Type', 'text/plain;charset=utf-8') res.end('请求失败') } else { // data 默认是二进制数据,可以通过 .toString 转化为咱们能识别的字符串 // res.end() 支持两种数据类型,一种是二进制,一种是字符串 res.setHeader('Content-Type', 'text/html;charset=utf-8') res.end(data) } }) } else if(url === '/img'){ // url: 统一资源定位符 // 一个 url 最终其实是要对应到一个资源的 fs.readFile('./resource/1.jpg',function(err,data){ if (err) { res.setHeader('Content-Type', 'text/plain;charset=utf-8') res.end('请求失败') }else{ // data 默认是二进制数据,可以通过 .toString 转化为咱们能识别的字符串 // res.end() 支持两种数据类型,一种是二进制,一种是字符串 // 图片就不需要指定编码了,因为我们常说的编码一般指的是:字符编码 res.setHeader('Content-Type', 'image/jpeg') res.end(data) } }) }})server.listen(8000, function() { console.log('Server is running');})
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') } fs.readdir(wwwDir,function(err,files){ if(err){ return res.end('can not find www dir') } // 2.1 生成需要替换的内容 var content = '' files.forEach(function(item){ // 在 ES6 中的 ` 字符串中,可以使用 ${} 来引用变量 content += ` ${ item}/ 2017/11/2 上午10:32:47 ` }) // 2.3 替换 data = data.toString() // 普通的字符串解析替换,浏览器看到的结果就不一样了 data = data.replace('>_<',content) console.log(data); // 3. 发送解析替换过后的响应数据 res.end(data) }) })})// 3. 绑定端口号,启动服务server.listen(3000,function(){ console.log('running');})
在 node 中使用模版引擎
art-template 不仅可以在浏览器使用,也可以在 node 中使用
安装:
npm install art-template
该命令在哪里执行就会把包下载到哪里,默认会下载到 node_modules 目录中
node_modules 不要改,也不支持改
在需要使用的文件模块中加载 art-template
查文档,使用模版引擎的 API
var template = require('art-template')var fs = require('fs')fs.readFile('./tpl.html',function(err,data){ if(err){ return console.log('读取文件失败'); } // 默认读取到的 data 是二进制数据 // 而模版引擎的 render 方法需要接收的是字符串 // 所以我们在这里需要把 data 二进制数据转为字符串才可以给模版引擎使用 var ret = template.render(data.toString(),{ name: 'Jack', age: 18, province: '北京', hobbies:[ '写代码', '唱歌', '打篮球' ] }) console.log(ret);})
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) { fs.readFile('.' + url,function(err,data){ if(err){ return res.end('404 not found') } res.end(data) }) } }) .listen(5000,function(){ console.log('running'); })
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 === '/post.html'){ fs.readFile('./views/post.html',function(err,data){ if(err){ return res.end('404 not found') } res.end(data) }) }else if (url.indexOf('/public/') === 0) { fs.readFile('.' + url,function(err,data){ if(err){ return res.end('404 not found') } res.end(data) }) }else{ // 其他的都处理成 404 找不到 fs.readFile('./views/404.html',function(err,data){ if(err){ return res.end('404 not found') } res.end(data) }) } }) .listen(5000,function(){ console.log('running'); })
Example page header Subtext for header
发表留言
var http = require('http')var fs = require('fs')var template = require('art-template')// 模拟数据var comments = [ { name: '张三', message: '今天天气不错', dateTime: '2015-10-16' }, { name: '张三2', message: '今天天气不错', dateTime: '2015-10-16' }, { name: '张三3', message: '今天天气不错', dateTime: '2015-10-16' }, { name: '张三4', message: '今天天气不错', dateTime: '2015-10-16' }, { name: '张三5', message: '今天天气不错', dateTime: '2015-10-16' }, { name: '张三6', message: '今天天气不错', dateTime: '2015-10-16' },]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') } var htmlStr = template.render(data.toString(),{ comments:comments }) res.end(htmlStr) }) }else if(url === '/post.html'){ fs.readFile('./views/post.html',function(err,data){ if(err){ return res.end('404 not found') } res.end(data) }) }else if (url.indexOf('/public/') === 0) { fs.readFile('.' + url,function(err,data){ if(err){ return res.end('404 not found') } res.end(data) }) }else{ // 其他的都处理成 404 找不到 fs.readFile('./views/404.html',function(err,data){ if(err){ return res.end('404 not found') } res.end(data) }) } }) .listen(5000,function(){ console.log('running'); })
http .createServer(function(req,res){ // 简写方式 // 使用 url.parse 方法将路径解析为一个方便操作的对象 // 第二个参数为 true 表示直接将查询字符串转为一个对象 // 通过 (query)属性来访问 var parseObj = url.parse(req.url,true) // 单独获取不包含查询字符串的路径部分(该路径不包含 ? 之后的内容) var pathname = parseObj.pathname if(pathname === '/'){ // 首页 fs.readFile('./views/index.html',function(err,data){ if(err){ return res.end('404 not found') } var htmlStr = template.render(data.toString(),{ comments:comments }) res.end(htmlStr) }) }else if(pathname === '/post'){ // 发表评论 fs.readFile('./views/post.html',function(err,data){ if(err){ return res.end('404 not found') } res.end(data) }) }else if (pathname.indexOf('/public/') === 0) { // 统一处理: // 如果请求路径是以 /public/ 开头的,则我认为你要获取 public 中的某个资源 // 所以我们就直接可以把请求路径当作文件路径来直接进行读取 fs.readFile('.' + pathname,function(err,data){ if(err){ return res.end('404 not found') } res.end(data) }) }else if(pathname === '/pinglun'){ var comment = parseObj.query comment.dateTime = '2020' comments.unshift(comment) // 服务器这个时候已经把数据存储好了,接下来就是让用户重新请求 / 首页,就可以看到最新的留言内容了 // 通过响应头来实现服务端重定向 res.writeHead(302,{ 'Location': '/' }) res.end() }else{ // 其他的都处理成 404 找不到 fs.readFile('./views/404.html',function(err,data){ if(err){ return res.end('404 not found') } res.end(data) }) } }) .listen(5000,function(){ console.log('running'); })
else if (pathname.indexOf('/public/') === 0) { // 统一处理: // 如果请求路径是以 /public/ 开头的,则我认为你要获取 public 中的某个资源 // 所以我们就直接可以把请求路径当作文件路径来直接进行读取 fs.readFile('.' + pathname,function(err,data){ if(err){ return res.end('404 not found') } res.end(data) }) }else if(pathname === '/pinglun'){ var comment = parseObj.query comment.dateTime = '2020' comments.unshift(comment) // 服务器这个时候已经把数据存储好了,接下来就是让用户重新请求 / 首页,就可以看到最新的留言内容了 // 通过响应头来实现服务端重定向 res.writeHead(302,{ 'Location': '/' }) res.end() }
使用 Node 编写应用程序主要就是在使用:
在 Node 中没有全局作用域的概念
在 Node 中,只能通过 require
方法来加载执行多个 JavaScript 脚本文件
require
加载只能是执行其中的代码,文件与文件之间由于是模块作用域,所以不会有污染的问题
模块作用域固然带来了一些好处,可以加载执行多个文件,可以完全避免变量命名冲突污染的问题
但是某些情况下,模块与模块实需要进行通信的
在每一个模块中,都提供了一个对象:exports
该对象默认是一个空对象
你要做的就是把需要被外部访问使用的成员手动的挂载到 exports
接口对象中
然后谁来 require
这个模块,谁就可以得到模块内部的 exports
接口对象
在 Node 中的 JavaScript 还有一个很重要的概念,模块系统。
var 自定义变量名称 = require('模块')
exports
导出接口对象exports.a = 123exports.b = 'hello'exports.c = function(){ console.log('ccc')}exports.d = { foo: 'bar'}
module.exports = { foo: 'bar' add: function(){ }}
module.exports = 'hello'
以下情况会覆盖:
module.exports = 'hello'// 以这个为准,后者会覆盖前者module.exports = function(x,y){ return x + y}
也可以这样来导出多个成员:
module.exports = { add: function(){ return x + y }, str: 'hello'}
exports 和 module.exports 的一个引用:
console.log(exports === module.exports) // => trueexports.foo = 'bar'// 等价于module.exports.foo = 'bar'
module.exports.xxx = xxx
的方式module.exports.xxx = xxx
很麻烦,点儿的太多了exports
exports === module.exports
结果为 true
module.exports.xxx = xxx
的方式完全可以:exports.xxx = xxx
module.exports = xxx
的方式exports = xxx
不管用return
的是 module.exports
exports
只是 module.exports
的一个引用exports = xxx
重新赋值,也不会影响 module.exports
exports = module.exports
这个用来重新建立引用关系的module.exports = { a: 123}// 重新建立 exports 和 module.exports 之间的引用关系exports = module.exportsexports.foo = 'bar'
// 如果是非路径形式的模块标识// 路径形式的模块// ./ 当前目录,不可省略// ../ 上一级目录,不可省略// /xxx 几乎不用// d:/a/foo.js 绝对路径绝对不用// 首位的 / 在这里表示的是当前文件模块所属磁盘根路径// .js 后缀名可以省略// require('./foo.js')// 核心模块的本质也是文件// 核心模块文件已经被编译到了二进制文件中了,我们只需要按照名字来加载就可以了// require('fs')// require('http')// 第三方模块// 凡是第三方模块都必须通过 npm 来下载// 使用的时候就可以通过 require('包名') 的方式来进行加载才可以使用// 不可能又任何一个第三方包和核心模块的名字是一样的// 既不是核心模块,也不是路径形式的模块// 先找到当前文件所处目录中的 node_modules 目录// node_modules/art-template// node_modules/art-template/package.json 文件// node_modules/art-template/package.json 文件中的 main 属性// main 属性中就记录了 art-template 的入口模块// 然后加载使用这个第三方包// 实际上最终加载的还是文件// 如果 package.json 文件不存在或者 main 指定的入口模块是也没有// 则 node 会自动找该目录下的 index.js// 也就是说 index.js 会作为一个默认备选项// 如果以上所有任何一个条件都不成立,则会进入上一级目录中的 node_modules 目录查找// 如果上一级还没有,则继续往上上一级查找// 如果知道当前磁盘根目录还找不到,最后报错:// can not find module xxx// var template = require('art-template')// 注意:我们一个项目有且只有一个 node_modules ,放在项目根目录中,// 这样的话项目中所有的子目录中的代码都可以加载到第三方包中// 不会出现有多个 node_modules// 模块查找机制// 优先从缓存加载// 核心模块// 路径形式的文件模块// 第三方模块// node_modules/art-template// node_modules/art-template/package.json 文件// node_modules/art-template/package.json 文件中的 main 属性// index.js 备选项// 进入上一级目录找 node_modules// 按照这个规则依次往上找,直到磁盘根目录还找不到,最后报错:Can not find module xxx// 一个项目有且只有一个 node_modules 而且是存放到项目的根目录
npm --version
npm install --global npm
npm init
npm init -y
可以跳过向导,快速生成npm install
npm i
npm install 包名
npm i 包名
npm install --save 包名
package.json
文件中的 dependencies 选项)npm i -S 包名
npm uninstall 包名
npm un 包名
npm uninstall --save 包名
npm un -S 包名
npm help
npm 命令 --help
npm uninstall --help
来查看使用帮助npm 存储包文件的服务器在国外,有时候会被墙,速度很慢,所以我们需要解决这个问题。
cnpm
:// 在任意目录执行都可以// --global 表示安装到全局,而非前目录// --global 不能省略,否则不管用npm install --global cnpm
npm
替换成 cnpm
// 这里还是走国外的 npm 服务器,速度比较慢npm install jquery// 使用 cnpm 就会通过淘宝的服务器来下载 jquerycnpm install jquery
cnpm
又想使用淘宝的服务器来下载:npm install jquery --registr=https://registry.npm.taobao.org
npm config set registry https://registry.npm.taobao.org// 查看 npm 配置信息npm config list
npm install
都会默认通过淘宝的服务器来下载。建议每一个项目都要有一个 package.json 文件(包描述文件,就像产品的说明书一样),给人踏实的感觉。
这个文件可以通过 npm init
的方式来自动初始化出来。
对于我们目前来讲,最有用的事那个 dependencies
选项,可以用来帮助我们保存第三方包的依赖信息。
如果你的 node_modules
删除了也不用担心,我们只需要:npm install
就会自动把 package.json
中的 dependencies
中所有的依赖项都下载回来
package.json
文件npm install 包名
的时候都加上 --save
这个选项,目的是用来保存依赖项信息package-lock.json
这个文件的package-lock.json
这个文件 --save
参数,它会自动保存依赖信息package-lock.json
这个文件package-lock.json
这个文件会保存 node_modules
中所有包的信息(版本,下载地址) npm install
的时候速度就可以提升lock
称之为锁 lock
是用来锁定版本的1.1.1
版本install
其实会下载最新版本,而不是 1.1.1
1.1.1
这个版本package-lock.json
这个文件的另一个作用就是锁定版本号,防止自动升级新版http
在某些方面表现不足以应对我们的开发需求,所以我们就需要使用框架来加快我们的开发效率,框架的目的就是提高效率,让我们的代码更高度统一express
为主npm install --save express
const express = require('express')const app = express()app.get('/',(req,res) => res.send('hello world'))app.listen(3000,() => console.log('Example app listening on port 3000!'))
路由器
请求方法
请求路径
请求处理函数
get:
// 当你以 GET 的方法请求 / 的时候,执行对应的处理函数app.get('/',function(req,res){ res.send('hello world')})
// 当你以 POST 的方法请求 / 的时候,指定对应的处理函数app.post('/',function(req,res){ res.send('Got a POST request')}
// /public资源app.use(express.static('public'))// /static资源app.use(express.static('files'))// /public/xxxapp.use('/public',express.static('public'))// /static/xxxapp.use('/static',express.static('public'))app.use('/static',express.static(path.join(_dirname,'public')))
npm install --save art-templatenpm install --save express-art-template
app.engine('art',require('express-art-template'))
app.get('/',function(req,res){ // express 默认会去项目中的 views 目录找 index.html res.render('index.html'.{ title: 'hello world' })})
views
视图渲染存储目录,可以:// 注意:第一个参数 views 千万不要写错app.set('views',目录路径)
Express 内置了一个 API ,可以直接通过 req.query
来获取
req.query
在 Express 中没有内置获取表单 POST 请求体的 API ,这里我们需要使用一个第三方包:body-parser
npm install --save body-parser
var express = require('express')// 0. 引包var bodyParser = require('body-parser')var app = express()// 配置 body-parser// 只要加入这个配置,则在 req 请求对象上会多出来一个属性:body// 也就是说你就可以直接通过 req.body 来获取表单 POST 请求体数据了// parse application/x-www-form-urlencodedapp.use(bodyParser.urlencoded({ extended: false }))// parse application/jsonapp.use(bodyParser.json())
app.use(function(req,res){ res.setHeader('Content-Type', 'text/plain') res.write('you posted:\n') // 可以通过 req.body 来获取表单 POST 请求体数据 res.end(JSON.stringify(req.body,null,2))})
根据不同的请求方法+请求路径设置具体的请求处理函数
var express = require('express')var router = require('./router')var app = express()// 把路由容器挂载到 app 服务中app.use(router)app.listen(3000,function(){ console.log('running 3000...');})module.exports = app
var express = require('express')var fs = require('fs')// 使用 Express 中的 Router 方法// 1. 创建一个路由容器var router = express.Router()// 2. 把路由都挂载到 router 路由容器中 router.get('/students',function(req,res){ // readFile 的第二个参数是可选的, // 传入 utf8 就是告诉它把读取到的文件直接按照 utf8 编码转成我们能认识的字符 // 除了这样来转换之外,也可以通过 data.toString() 的方式 fs.readFile('./db.json','utf8',function(err,data){ if(err){ return res.status(500).send('Server error.') } // console.log(data); // string // 从文件中读取到的数据一定是字符串 // 所以这里一定要手动转成对象 var students = JSON.parse(data).students students: students }) }) }) // 3. 把 router 导出module.exports = router
var fs = require('fs')fs.readFile('./data/a.txt', 'utf8', function(err,data){ if (err) { // return console.log(‘读取失败’); // 抛出异常 // 1. 阻止程序的执行 // 2. 把错误消息打印到控制台 throw err } console.log(data);})fs.readFile('./data/b.txt', 'utf8', function(err,data){ if (err) { // return console.log(‘读取失败’); // 抛出异常 // 1. 阻止程序的执行 // 2. 把错误消息打印到控制台 throw err } console.log(data);})fs.readFile('./data/c.txt', 'utf8', function(err,data){ if (err) { // return console.log(‘读取失败’); // 抛出异常 // 1. 阻止程序的执行 // 2. 把错误消息打印到控制台 throw err } console.log(data);})
var fs = require('fs')fs.readFile('./data/a.txt', 'utf8', function(err,data){ if (err) { // return console.log(‘读取失败’); // 抛出异常 // 1. 阻止程序的执行 // 2. 把错误消息打印到控制台 throw err } console.log(data); fs.readFile('./data/b.txt', 'utf8', function(err,data){ if (err) { // return console.log(‘读取失败’); // 抛出异常 // 1. 阻止程序的执行 // 2. 把错误消息打印到控制台 throw err } console.log(data); fs.readFile('./data/c.txt', 'utf8', function(err,data){ if (err) { // return console.log(‘读取失败’); // 抛出异常 // 1. 阻止程序的执行 // 2. 把错误消息打印到控制台 throw err } console.log(data); }) })})
Promise
var fs = require('fs')// 在 EcmaScript 6 中新增了一个 API Promise// Promise 是一个构造函数// 创建 Promise 容器// 1. 给别人一个承诺 I promise you// Promise 容器一旦创建,就开始执行里面的代码var p1 = new Promise(function(resolve,reject){ fs.readFile('./data/a.txt','utf8',function(err,data){ if(err){ // 失败了,承诺容器中的任务失败了 // console.log(err); // 把容器的 Pending 状态变为 Rejected // 调用 reject 就相当于调用了 then 方法的第二个参数函数 reject(err) }else{ // 承诺容器中的任务成功了 // console.log(data); // 把容器的 Pending 状态变为成功 Resolved // 也就是说这里调用的 resolve 方法实际上就是 then 方法传递的那个 function resolve(data) } })}) // p1 就是那个承诺// 当 p1 成功了 然后(then)做指定的操作// then 方法接收的 function 就是容器中的 resolve 函数p1 .then(function(data){ console.log(data); },function(err){ console.log('读取文件失败了',err); })
var fs = require('fs')// 在 EcmaScript 6 中新增了一个 API Promise// Promise 是一个构造函数// 创建 Promise 容器// 1. 给别人一个承诺 I promise you// Promise 容器一旦创建,就开始执行里面的代码var p1 = new Promise(function(resolve,reject){ fs.readFile('./data/a.txt','utf8',function(err,data){ if(err){ // 失败了,承诺容器中的任务失败了 // console.log(err); // 把容器的 Pending 状态变为 Rejected // 调用 reject 就相当于调用了 then 方法的第二个参数函数 reject(err) }else{ // 承诺容器中的任务成功了 // console.log(data); // 把容器的 Pending 状态变为成功 Resolved // 也就是说这里调用的 resolve 方法实际上就是 then 方法传递的那个 function resolve(data) } })})var p2 = new Promise(function(resolve,reject){ fs.readFile('./data/b.txt','utf8',function(err,data){ if(err){ reject(err) }else{ resolve(data) } })})var p3 = new Promise(function(resolve,reject){ fs.readFile('./data/c.txt','utf8',function(err,data){ if(err){ reject(err) }else{ resolve(data) } })})// p1 就是那个承诺// 当 p1 成功了 然后(then)做指定的操作// then 方法接收的 function 就是容器中的 resolve 函数p1 .then(function(data){ console.log(data); // 当 p1 读取成功的时候 // 当前函数中 return 的结果就可以在后面的 then 中 function 接收到 // 当你 return 123 后面就接收到 123 // return ‘hello’ 后面就接收到 ‘hello’ // 没有 return 后面收到的就是 undefined // 上面那些 return 的数据没什么用 // 真正有用的是:我们可以 return 一个 Promise 对象 // 当 return 一个 Promise 对象的时候,后续的 then 中的方法的第一个参数作为 p2 的 resolve return p2 },function(err){ console.log('读取文件失败了',err); }) .then(function(data){ console.log(data); return p3 }) .then(function(data){ console.log(data); console.log('end'); })
var fs = require('fs')function pReadFile(filePath){ return = new Promise(function(resolve,reject){ fs.readFile('filePath,'utf8',function(err,data){ if(err){ reject(err) }else{ resolve(data) } }) })}pReadFile('./data/a.txt') .then(function(data){ console.log(data); return pReadFile('./data/b.txt') }) .then(function(data){ console.log(data); return pReadFile('./data/c.txt') }) .then(function(data){ console.log(data); })
app.use(function(req,res,next){ console.log('Time:',Date.now()) next()})
'/xxx'
开头的app.use('/a',function(req,res,next){ console.log('Time:',Date.now()) next()})
app.get('/',function(req,res){ res.send('Hello World!')})
app.post('/',function(req,res){ res.send('Got a POST request')})
app.put('/user',function(req,res){ res.send('Got a PUT request at /user')})
app.delete('/user'.function(req,res){ res.send('Got a DELETE request at /user')})
app.use(function(err,req,res,next){ console.error(err.stack) res.status(500).send('Something broke!')})
转载地址:http://njqwi.baihongyu.com/
{ {each comments}}- { { $value.name }}说:{ { $value.message }} { { $value.dateTime }}
{ {/each}}