1. Node.js是什么
- Node.js不是一门语言不是库不是框架
- Node.js是javascript运行时环境。简单来说就是Node.js可以解析和执行js代码,现在js可以脱离浏览器运行
- 浏览器中的js
- Ecmascript(基本语法,if,var,function)
- DOM
- BOM
- Node 中的 JavaScript
- 没有DOM BOM(不能识别window和document)
- EcmaScript
- Node.js不操作页面,为js提供一些服务器端的操作:文件的读写,网络服务的搭建,网络通信等
- event-driven事件驱动、非阻塞i/o模型、轻量高效
2. Node.js使用
2.1 文件的操作
终端用node 文件名开打开文件
fs 是 file-system 的简写,就是文件系统的意思,在 Node 中如果想要进行文件操作,就必须引入 fs 这个核心模块 var fs = require('fs')
- 读文件fs.readFile
fs.readFile('./data/a.txt', function (error, data) {
//第一个参数就是要读取的文件路径
// 第二个参数是一个回调函数
if(error){
console.log('有错误')
}else{
console.log(data.toString())
}
}
成功——data 数据,error undefined没有数据 失败—— data undefined没有数据,
- 写文件 fs.writeFile(文件路径,写入的内容,回调函数)
- 读取文件夹的目录 fs.readdir()
2.2 服务器的操作
- 创建一个 Web 服务器
// 1. 加载 http 核心模块
var http = require('http')
// 2. 使用 http.createServer() 方法创建一个 Web 服务器,返回一个 Server 实例
var server = http.createServer()
// 3.当客户端请求过来,就会自动触发服务器的 request 请求事件,然后执行第二个参数:回调处理函数
server.on('request', function () {
console.log('收到客户端的请求了')
})
// 4. 绑定端口号,启动服务器
server.listen(3000, function () {
console.log('服务器启动成功了,可以通过 http://127.0.0.1:3000/ 来进行访问')
})
- request 请求事件处理函数
server.on('request', function (request, response) {
console.log('收到客户端的请求了,请求路径是:' + request.url)
//request.url是 http://127.0.0.1:3000/ 后边的东西
// response 对象有一个方法:write 可以用来给客户端发送响应数据
// write 可以使用多次,但是最后一定要使用 end 来结束响应,否则客户端会一直等待
response.write('hello')
response.write(' nodejs')
// 告诉客户端,我的话说完了,你可以呈递给用户了
response.end()
})
可以直接一句话 response.end('hello nodejs')。响应内容只能是二进制或字符串
http.createServer(function(request,response){
response.writeHead(200, {"Content-Type": "text/plain"});
response.write("Hello World");
response.end();
}).listen(888);
- 页面重定向
- 状态码设置为 302 临时重定向(301永久重定向)
response.statusCode = 302 - 在响应头中通过 Location 告诉客户端往哪儿重定向
response.setHeader('Location', '/')
- 状态码设置为 302 临时重定向(301永久重定向)
2.3 核心模块
文件操作的fs模块、http服务构建的http模块、path路径操作模块、os操作系统信息模块等等。使用模块就要先引入var fs = require('fs)
var http = require('http')
2.4 require
require 是一个方法,它的作用就是用来加载模块的。在 Node 中,模块有两种:- 具名的核心模块,例如 fs、http(fs是动态读取文件信息)
- 用户自己编写的文件模块 相对路径必须加 ./ 可以省略后缀名 相对路径中的 ./ 不能省略,否则报错
注意:
- 在 Node 中,没有全局作用域,只有模块作用域
- 外部访问不到内部, 内部也访问不到外部。默认都是封闭的
- 有时候,我们加载文件模块的目的不是为了简简单单的执行里面的代码,更重要是为了使用里面的某个成员
2.5 http
-
端口号
- ip 地址定位计算机
- 端口号定位具体的应用程序
- 端口号的范围是0-65536之间,一些计算机默认的端口号例如80 最好不要使用
-
Content-Type
- 服务器最好把每次响应的数据是什么内容类型都告诉客户端,而且要正确的告诉
- 不同的资源对应的 Content-Type 是不一样,具体参照:tool.oschina.net/commons
- 对于文本类型的数据,最好都加上编码,目的是为了防止中文解析乱码问题
- res.setHeader('Content-Type', 'text/plain; charset=utf-8')——普通文本
- res.setHeader('Content-Type', 'text/html; charset=utf-8') ——html 格式的字符串
-
通过网络发送文件
- 发送的并不是文件,本质上来讲发送是文件的内容
- 当浏览器收到服务器响应内容之后,就会根据你的 Content-Type 进行对应的解析处理
2.6 apache目录文件
得到某目录下的文件名和目录名 fs.readdir- 获取文件目录
- 将得到的文件名和目录名替换到 template.html 中
data = data.replace('^_^', content) - 发送解析替换过后的响应数据res.end(data)
- 打开服务器就可以获取到想要得到的文件名和文件目录
2.7 模板引擎art-template
可以在浏览器中使用也可以在node中使用- 在浏览器中使用
- 首先安装npm install art-template
- 在浏览器中需要引用 lib/template-web.js 文件
- 模板引擎不关心你的字符串内容,只关心自己能认识的模板标记语法,例如 {{}}
<script type="text/template" id="tpl">
{{ name }}
</script>
<script>
var ret = template('tel',{
name:'jack'
})
</script>
//模板的循环
{{each 数组]}
{{$value}}
{{/each}}
- 在node中使用
- 在需要使用的文件模块中加载 art-template
require('art-template') - 使用render渲染
- 在需要使用的文件模块中加载 art-template
var ret = template.render(data.toString(), {
name: 'Jack',
})
- 服务端渲染和客户端渲染的区别 (服务端渲染就是在服务端使用模板引擎)
- 客户端渲染不利于 SEO 搜索引擎优化
- 服务端渲染是可以被爬虫抓取到的,客户端异步渲染是很难被爬虫抓取到的
- 所以你会发现真正的网站既不是纯异步也不是纯服务端渲染出来的
- 而是两者结合来做的
- 例如京东的商品列表就采用的是服务端渲染,目的了为了 SEO 搜索引擎优化
- 而它的商品评论列表为了用户体验,而且也不需要 SEO 优化,所以采用是客户端渲染
2.8 路由模块
var url = require('url')
var obj = url.parse('/pinglun?name=的撒的撒&message=的撒的撒的撒', true)
参数为true的时候可以获取到后面的search值,并将其转为对象格式
query: { name: '的撒的撒', message: '的撒的撒的撒' }
2.9 path路径操作模块
- path.basename 获取一个路径的文件名(默认包含扩展名)
- path.dirname 获取一个路径中的目录部分
- path.parse 把一个路径转为对象
- root根路径
- dir目录
- base包含后缀名的文件名
- ext后缀名
- name不包含后缀名的文件名
- path.join 进行路径的拼接
- path.isAbsolute判断一个路径是不是绝对路径
补充:
path.resolve('/foo/bar', './baz') // '/foo/bar/baz'
path.resolve('/foo/bar', '/tmp/file/') // '/tmp/file'
path.resolve('wwwroot', 'static_files/png/', '../gif/image.gif')
// 当前的工作路径是 /home/itbilu/node,则输出结果为
'/home/itbilu/node/wwwroot/static_files/gif/image.gif'
const path = require('path');
let myPath = path.join(__dirname,'/img/so');
let myPath2 = path.join(__dirname,'./img/so');
let myPath3 = path.resolve(__dirname,'/img/so');
let myPath4 = path.resolve(__dirname,'./img/so');
console.log(__dirname); //D:\myProgram\test
console.log(myPath); //D:\myProgram\test\img\so
console.log(myPath2); //D:\myProgram\test\img\so
console.log(myPath3); //D:\img\so<br>
console.log(myPath4); //D:\myProgram\test\img\so
2.10 导出成员exports 和 module.exports
- 每个模块中都有一个 module 对象
- module 对象中有一个 exports 对象
- 我们可以把需要导出的成员都挂载到 module.exports 接口对象中
- 也就是:
moudle.exports.xxx = xxx的方式 - 但是每次都
moudle.exports.xxx = xxx很麻烦,点儿的太多了 - 所以 Node 为了你方便,同时在每一个模块中都提供了一个成员叫:
exports exports === module.exports结果为true- 所以对于:
moudle.exports.xxx = xxx的方式 完全可以:expots.xxx = xxx - 当一个模块需要导出单个成员的时候,这个时候必须使用:
module.exports = xxx的方式 - 不要使用
exports = xxx不管用 - 因为每个模块最终向外
return的是module.exports - 而
exports只是module.exports的一个引用 - 所以即便你为
exports = xx重新赋值,也不会影响module.exports - 但是有一种赋值方式比较特殊:
exports = module.exports这个用来重新建立引用关系的 - 总结:最后 return 的是 module.exports,不是 exports,所以你给 exports 重新赋值不管用。 多个成员可以
moudle.exports.xxx = xxx或expots.xxx = xxx,单个成员的时候只能module.exports = xxx
2.11 require优先缓存加载
a加载过b,当再次require时不会重新加载。可以拿到其中的接口对象,但是不会重复执行里边的代码。目的:避免重复加载,提高模块加载效率2.12 require模块查找机制
- 优先从缓存加载
- 核心模块
- 路径形式的文件模块 ./ ../
- 第三方模块(凡是第三方模块都必须通过 npm 来下载)
- 既不是核心模块、也不是路径形式的模块
- 先找到当前文件所处目录中的 node_modules 目录
- node_modules/art-template/
- node_modules/art-template/package.json
- node_modules/art-template/package.json 里main属性中就记录了 art-template 的入口模块
- package.json 文件不存在或者 main 指定的入口模块是也没有。node 会自动找该目录下的 index.js
- 如果以上所有任何一个条件都不成立,则会进入上一级目录中的 node_modules 目录查找
- 一个项目有且仅有一个 node_modules 而且是存放到项目的根目录。最后找不到就会报错
__dirname和filename
- __dirname 动态获取可以用来获取当前文件模块所属目录的绝对路径
- __filename动态获取可以用来获取当前文件的绝对路径
- 这两个都不受node命令所属路径影响
在文件操作中使用相对路径是不可靠的,因为在Node中文件操作的路径被设计为相对执行node命令所处的路径。要解决这个问题,我们只需要把相对路径变为绝对路径就可以了
以后在文件操作中使用的相对路径都统一转换为动态的绝对路径
3. nodemon自动重启模块
- 全局安装
npm install -g nodemon - 本地安装
npm i nodemon -D - 启动应用
nodemon [your node app]
4. 中间件
中间件的本质就是一个请求处理方法,我们把用户从请求到响应的整个过程分发到多个中间件中去处理。这样做的目的是提高代码的灵活性,动态可扩展的。
4.1 应用程序级别中间件
万能匹配(不关心任何的请求路径和请求方法)
app.use(function (req, res, next) {
console.log('Time');
next();
})
只匹配以xxx开头的
app.use('/a', function (req, res, next) {
console.log('time');
next();
})
4.2 路由级别中间件
get:
app.get('/', funciton (req, res) {
res.send('hello');
})
post:
app.post('/', funciton (req, res) {
res.send('hello');
})
put:
app.put('/user', funciton (req, res) {
res.send('hello');
})
delete:
app.delete('/user', funciton (req, res) {
res.send('hello');
})
4.3 错误处理中间件
app.use(function (err, req, res, next){
console.error(err.stack)
res.status(500).send('Something broke')
})
4.4 内置中间件
- express.static
- express.json
- express.urlencoded
4.5 第三方中间件
- body-parser