NodeJS基础总结(一)
NodeJS官网网址:https://nodejs.org/en/
使用require方法加载fs核心模块
var fs = require('fs');
一、读取文件
// 第一个参数就是尧读取的文件路径
// 第二个参数是一个回调函数
// error
// 如果读取失败,error就是错误对象
// 如果读取成功,error就是null
// data
// 如果读取成功,data就是读取到的数据
// 如果读取失败,error就是错误对象 data就是undefined
fs.readFile('hello1.txt',function(error,data){ if(error){ console.log('读取信息失败'); }else{ console.log(data.toString()); } })
//返回 <Buffer 68 65 6c 6c 6f>
//这里看到的不是0和1,原因是二进制转换为16进制了
//但无论是2二进制或16进制,人类都不认识,
//因此通过toString方法将其转换为我们能认识的字符
//第一个参数,文件路径 //第二个参数,文件内容 //第三个参数,回调函数 // error形参 // 成功: // 文件写入成功 error是null // 失败: // 文件写入失败 error就是错误对象 fs.writeFile('waveHi.md','this is my first node.js',function(error){ if(error){ console.log('写入失败'); }else{ console.log('文件写入成功'); } })
三、加载http核心模块
var http = require('http'); // var http 变量名可自行随意命名,但是require里面的必须使用http 使用htp.createServer()方法创建一个web服务器 // 返回一个Server实例 var server = http.createServer(); // 当客户请求过来,就会自动触发服务器的request请求事件,然后执行第二个参数,回调处理函数 server.on('request',function(){ console.log('收到客户端的请求了'); }) 绑定端口号,启动服务器 server.listen(3000,function(){ console.log('服务器启动成功了,可以通过http://127.0.0.1:3000来访问'); });
四、响应对象给客户端发送响应消息
server.on('request',function(request,response){ console.log('收到客户端的请求了,请求路径是' + request.url); //这里可以通过if条件句来判断响应回应的东西 //response对象有一个方法:write可以用来给客户端发送响应数据 //write可以写很多次,但最后一定要用end来结束,否则客户端会一直等待 response.write('hello'); response.write(' nodejs'); //告诉客户端,我的话说完了,可以关闭了 response.end(); //由于现在我们的服务的能力还非常的弱,无论什么请求都只能相应hello nodejs })
五、监听request请求事件
server.on('request',function(req,res){ console.log('收到请求,请求路径是' + req.url); console.log("请求我的客户端号是:",req.socket.remoteAddress,req.socket.remotePort); //根据不同的请求路径发送不同的响应结果 //1、获取请求路径 // req.url获取到的是端口号之后的那一部分路径 // 也就是说所有的url都是以/开头的 //2、判断路径处理响应 var url = req.url; if(url === '/products'){ var products = [ { name:'小米 X', price: 8888 },{ name:'华为 X', price: 9999 },{ name:'vivo X', price: 6666 } ] res.end(JSON.stringify(products)); //将数组转化为字符串 }else{ res.end(1111); } //相应内容只能是二进制数据或者字符串 //数字、对象、数组、布尔值都不可以 })
六、Content-type,提示系统输入的内容类型
//在服务端默认发送的数据,其实是utf8编码的内容 //但是浏览器不知道你的内容是按照utf8编码的 //浏览器在不知道服务器相应内容的编码的情况下会按照当前操作系统的默认编码去解析 //中文操作系统默认是gbk //解决方法就是正确的告诉浏览器我给你发送的内容是什么编码类型的 //在http协议中,Content-Type就是用来告知对方我给你发送的数据内是什么内容 server.on('request',function(req,res){ var url = req.url; if(url === '/plain'){ //text/plain就是普通文本 res.setHeader('Content-type','text/plain; charset=utf-8'); res.end('hello world 你好世界'); }else if(url === '/html'){ //如果你发送的是html格式的字符串,则也要告诉浏览器我给你发送的是text/html格式的内容 res.setHeader('Content-type','text/html; charset=utf-8'); res.end('<p>hello html<a>点我</a></p>') } })
不同的资源对应的Content-type是不一样的,集体参照:http://tool.oschina.net/commons
*发送的并不是文件,本质上来讲发送的是文件的内容
*当浏览器收到服务器响应之后,就会根据你的Content-type进行对应的解析
server.on('request',function(req,res){ var url = req.url; if(url == '/'){ /*res.end('<!DOCTYPE html><html><head><title></title></head><body><h1>首页</h1></body></html>')*/ fs.readFile('./resource/index.html',function(err,data){ if(err){ res.setHeader('Content-type','text/plain;charset=utf-8') res.end('文件读取失败,请稍后重试!'); }else{ res.setHeader('Content-type','text/html;charset=utf-8') res.end(data); } }) }else if(url === '/a.jpg'){ fs.readFile('./resource/a.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;charset=utf-8') res.end(data); } }) } })
图片不需要指定编码
一般字符数据才制定编码
七、关于 art-template 的使用基础
//1.安装 npm install art-template //2.在需要使用的文件模块中加载art-template // 只需要使用require方法加载就可以了,require('art-template') //3.查文档,使用模板引擎的API var fs = require('fs'); var tplStar = `<!DOCTYPE html> <html> <head> <title></title> </head> <body> 大家好 我叫{{ name }} 今年 {{ age }} 岁 来自 {{ province }} 喜欢:{{ each hobbies }} {{ $value }} {{/each}} </body> </html>`; var template = require('art-template'); 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: [ '写代码', '唱歌', '看书' ], title:'个人信息' }) console.log(ret); })
在浏览器中使用 art-template
<!DOCTYPE html> <html> <head> <title></title> </head> <body> <!-- 注意:在浏览器中需要引用 lib/template-web.js文件 强调:模板引擎不关心你的字符串内容,只关心自己能认识的模板标记语法,例如{{}}, {{}}语法被称之为mustache语法 --> <script src="node_modules/art-template/lib/template-web.js"></script> <script type="text/template" id="tpl"> <!DOCTYPE html> <html> <head> <title></title> </head> <body> 大家好 我叫{{ name }} 今年 {{ age }} 岁 来自 {{ province }} 喜欢:{{ each hobbies }} {{ $value }} {{/each}} </body> </html> </script> <script> var ret = template('tpl', { name: 'jack', age: 18, province: '北京市', hobbies: [ '写代码', '唱歌', '看书' ] }) console.log(ret); </script> </body> </html>
八、node中模块系统
var foo = 'bar'; function add(x, y) { return x + y; } //模块需要直接导出某个成员, 而非挂载的方式 //这个时候必须使用下面这种方式 //module.exports = add; module.exports = { add: function (x, y) { return x + y; }, str: 'hello' }
// exports 是一个对象
//可以通过多次为这个变量添加新成员实现对外导出多个内部成员
可以通过以下方式调用
var fooExports = require('./foo') console.log(fooExports);
在 Node 中,每个模块内部都一个有自己的module
该 module 对象中,有一个成员叫:exports
Node 为了简化操作, 专门提供一个exports = module.exports
给 exports 赋值会断开和 module.exports之间的联系
exports.foo = 'bar';//{foo: bar} module.exports.a = 123;//{foo: bar, a:123} exports = { a: 456 } //此时 exports != module,exports // 最终 return 的是 module.exports // 无论是exports的什么成员都没有用 module.exports.foo = 'haha' //{foo: haha, a:123} exports.c = 456 // 已经无关,不能混淆 exports = module.exports; // 重新建立了和module.exports 之间的引用关系 // 下面语句生效 exports.a = 789; // 前面无论写的什么,这里全部推翻,重新赋值 // 最终得到的是function module.exports = function () { console.log('hello'); } // 真正去使用的时候: // 导出多个成员:exports.xxx = xxx // 导出多个成员也可以: module.exports = { // } // 导出单个成员:module.exports = xxx
九、require 优先从缓存加载
十、遍历
art-template中的 each,JQuery中的forEach的用法
+ art-template(模板引擎)和JQuery(增强DOM操作的库)没有任何关系
* each 是 art-template 的模板语法,专属的
{{each 数组}}
<li>{{ $value}}</li>
{{/each}} 这是art-template 模板引擎支持的语法, 只能在模板字符串中使用
each 在JQuery中的应用
$.each(数组, function)或者
$('div').each(function) 一般用于 Jquery 选择器选择到的伪数组实例对象
forEach 是EcmaScript 5 中的一个数组遍历函数,是 javaScript 生支持的遍历方法,能够遍历任何可以被遍历的成员
forEach
<!DOCTYPE html> <html> <head> <title></title> </head> <body> <div></div> <div></div> <div></div> <div></div> <div></div> <script src="node_modules/jquery/dist/jquery.js"></script> <script type="text/javascript"> /*;['a', 'b', 'c'].forEach(function (item, index) { console.log(item); })*/ // 遍历 JQuery 元素 /*$.each(['a','bb','c'], function (index, item) { console.log(item); })*/ console.log($('div')); //伪数组是对象 //对象的原型链中没有 forEach //对象的原型链是 Object.prototype //这个 each 是 JQuery 提供的 // 这个 each 在 JQuery 的原型链中 /*$("div").each(function (index, item) { console.log(item); })*/ // JQuery 不是专门用来遍历 JQuery 元素的 // 1.方便的遍历JQuery元素 // 2.可以在不兼容forEach的低版本浏览器中使用 Jquery 的 each方法 [].slice.call($('div')).forEach(function (item) {console.log(item);}) </script> </body> </html>
十一、Express
原生的http在某些方面表现不足以应对我们的开发需求,所以我们就需要使用框架来加快我们的开发效率,让我们的代码高度统一。
在 Node中,有很多 Web 开发框架,我们这里以学习 express为主。
官方网址:http://expressjs.com/
安装:
//cmd命令行输入 npm install --save express
引用:
var express = require('express') var app = express()
调用静态资源:
app.use('/node_modules', express.static('./node_modules'))
app.use('/public', express.static('./public'))
十二、页面跳转渲染
* 职责:
* 创建服务
* 做一些服务相关配置
* 模板引擎
* body-parser 解析表单 post 请求体
* 提供静态资源
* 挂载路由
* 监听端口启动服务
var bodyParser = require('body-parser')
var fs = require('fs')
var app = express()
app.use('/public', express.static('./public'))
// parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: false }))
// parse application/json
app.use(bodyParser.json())
app.use(router)
console.log('running 3000.....')
})
var fs = require('fs') var Student = require('./student') 专门用来包装路由的 var express = require('express') // 1. 创建一个路由器 var router = express.Router() // 2. 把路由器挂载到 router路由器中 router.get('/students', function (req, res) { // readFile 的第二个参数还是可选的,传入 utf-8 是告诉他把读取的文件直接按照utf-8编码 /*fs.readFile('./db.json','utf8', function (err, data) { if(err) { return res.status(500).send('Server error.') } //文件中读取到的数据一定是字符串,所以一定要手动转成对象才可以使用 res.render('index.html', { fruits: [ '苹果', '香蕉', '橘子' ], students: JSON.parse(data).students }) })*/ Student.find(function (err, students) { if(err) { return res.status(500).send('Server error.') } res.render('index.html', { fruits: [ '苹果', '香蕉', '橘子' ], students: students }) }) }) router.get('/students/new', function (req, res) { res.render('new.html') }) router.post('/students/new', function (req, res) { // 1. 获取表单数据 // 2. 处理 // 将数据保存到 db.json文件中用以持久化 // 3. 发送响应 //先读取出来,转成对象 //然后往对象中 push 数据 //然后把对象转为字符串 // 然后把字符串再次写入文件 Student.save(req.body, function (err) { if(err) { return res.status(500).send('Server error.') } res.redirect('/students') }) }) router.get('/students/edit', function (req, res) { // 1.在客户端的列表中处理连接问题(需要有 id 参数) // 2,获取要编辑的学生id // 3.渲染编辑页面 Student.findById(parseInt(req.query.id), function (err, student) { if(err) { return res.status(500).send('Server error.') } console.log(student) res.render('edit.html', { student: student }) }) //console.log(req.query.id) }) router.post('/students/edit', function (req, res) { // 1. 获取表单数据 // req.body // 2. 更新 // Student.updateById() // 3. 发送响应 Student.updateById(req.body,function (err) { if (err) { return res.status(500).send('Server error') } res.redirect('/students') }) }) router.get('/students/delete', function (req, res) { // 1. 获取要删除的 id // 2. 根据 id 执行删除操作 // 3. 根据操作结果发送响应数据 Student.deleteById(req.query.id, function (err){ if(err) { return res.status(500).send('Server eror.') } res.redirect('/students') }) }) // 3. 把router 导出 module.exports = router
封装异步API
var fs = require('fs') var dbPath = './db.json' exports.find = function (callback) { fs.readFile(dbPath,'utf8', function (err, data) { if (err) { return callback(err) } callback(null, JSON.parse(data).students) }) } /** * 根据 id 获取学生信息对象 * @param {Number} id 学生 id * @param {function} callback 回调函数 */ exports.findById = function (id, callback) { fs.readFile(dbPath,'utf8', function (err, data) { if (err) { return callback(err) } var students = JSON.parse(data).students var ret = students.find(function (item) { return item.id === parseInt(id) }) callback(null, ret) }) } /** * 添加保存学生 */ exports.save = function (student, callback) { fs.readFile(dbPath,'utf8', function (err, data) { if (err) { return callback(err) } var students = JSON.parse(data).students // 处理id唯一的不重复 student.id = students[students.length - 1].id + 1 //把用户传递的对象保存到数组中 students.push(student) // 把对象数据转换为字符串 var fileData = JSON.stringify({ students: students }) // 把字符串保存在文件中 fs.writeFile(dbPath, fileData, function (err) { if(err) { return callback(err) } callback(null) }) }) } /** 更新学生 */ exports.updateById = function (student, callback) { fs.readFile(dbPath,'utf8', function (err, data) { if (err) { return callback(err) } var students = JSON.parse(data).students //注意:这里记得把 id 统一转换为数字类型 student.id = parseInt(student.id) //你要修改谁,就需要把谁找出来 //EcmaScript 6 中的一个数组方法:find // 需要接收一个函数作为参数 // 当某个遍历项符合 item.id === student.id 条件的时候,find 会终止遍历,同时返回遍历项 var stu = students.find(function (item) { return item.id === student.id }) for(var key in student) { stu[key] = student[key] } // 把对象数据转换为字符串 var fileData = JSON.stringify({ students: students }) // 把字符串保存在文件中 fs.writeFile(dbPath, fileData, function (err) { if(err) { return callback(err) } callback(null) }) }) } /** * 删除学生 */ exports.deleteById = function (id, callback) { fs.readFile(dbPath, 'utf8', function (err, data) { if (err) { return callback(err) } var students = JSON.parse(data).students // findIndex 方法专门用来根据条件查找元素下标 var deleteId = students.findIndex(function (item) { return item.id === parseInt(id) }) // 根据下标从数组中删除对应的学生对象 students.splice(deleteId, 1) // 把对象数据转换为字符串 var fileData = JSON.stringify({ students: students }) // 把字符串保存在文件中 fs.writeFile(dbPath, fileData, function (err) { if(err) { return callback(err) } callback(null) }) }) }
封装 ajax 的方法:
//setTimeout //readFile //writeFile //readdir //ajax // 往往异步API 都伴随着一个回调函数 function get (url, callback) { var oReq = new XMLHttpRequest() // 当请求加载成功之后要调用指定的函数 oReq.onload = function () { callback(oReq.responseText) } oReq.open("get", url, true) oReq.send() } get('db.json', function (data) { console.log(data) })
find 和 findIndex
// EcmaScript 6 对数组新增了很多方法 // find // findIndex // find 接受一个方法作为参数,方法内部返回一个条件 // find 会便利所有元素,执行你给定的带有条件返回值的函数 // 符合该条件的元素会作为 find 方法的返回值 // 如果遍历结束还没有符合 该条件的元素则返回undefined、 var users = [ {id: 1, name: '张三'}, {id: 2, name: '张三'}, {id: 3, name: '张三'}, {id: 4, name: '张三'} ] Array.prototype.myFind = function (conditionFunc) { for (var i = 0; i < this.length; i++) { if (conditionFunc(this[i], i)) { return this[i] } } } var ret = users.myFind(function (item, index) { return item.id === 4 }) console.log(ret)
十三、封装promise API
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') }, function (err) { console.log('读取文件失败了', err) }) .then(function (data) { console.log(data) return pReadFile('./data/c.txt') }, function (err) { console.log('读取文件失败了', err) }) .then(function (data) { console.log(data) }, function (err) { console.log('读取文件失败了', err) })
mongoose 所有API都支持promise
var mongoose = require('mongoose') var Schema = mongoose.Schema mongoose.connect('mongodb://localhost/test', {useNewUrlParser: true}) var userSchema = new Schema({ username: { type: String, required: true // 必须有 }, password: { type: String, required: true }, email: { type: String } }) var User = mongoose.model('User', userSchema) User.find() .then(function (data) { console.log(data) }) User.findOne({ username: 'Leslie' }) .then(function (user) { if (user) { console.log('用户已存在') } else { return new User({ username: 'Leslie', password: '123', email: 'dewfvr' }).save() } }).then(function (ret) { })