var foo = 'hello nodejs'
console.log(foo)
注意:文件名不要用node.js来命名,也就是说除了node这个名字随便起,最好不要使用中文。
没有bom和dom
// 在Node中,采用EcmaScript进行编码
// 没有Bom,Dom
// 和浏览器中的JavaScript不一样
console.log(window)
console.log(document)
创建一个02-读取文件.js文件,并在同级文件目录下创建一个data文件,创建一个hello.txt文件,写入
在02-读取文件.js文件中
//浏览器中的JavaScript是没有文件操作能力的
//但是Node中的JavaScript具有文件操作能力
//fs是file-system的简写,就是文件系统的意思
//在Node中如果想要进行文件的操作就必须引用fs这个核心模块
//在fs这个和兴模块中,就提供了人所有文件操作相关的API
//例如 fs.readFile就是用来读取文件的
// 1.使用require加载fs核心模块
var fs = require('fs');
// 2.读取文件
// 第一个参数就是要读取的文件路径
// 第二个参数是一个回调函数
// 成功
// data 数据
// error null
// 失败
// data null
// error 错误对象
// 2.读取文件
fs.readFile('./data/hello.txt',function(err,data){
if(err){
console.log('文件读取失败');
}
else{
console.log(data.toString());
}
})
var fs = require('fs')
// $ajax({
// ...
// success:function(a){
// }
// })
// 第一个参数: 文件路径
// 第二个参数: 文件内容
// 第三个参数: 回调函数
// error
// 成功:
// 文件写入成功
// error 是null
// 失败
// 文件写入失败
// error 是null
// 2.将数据写入文件
fs.writeFile('./data/你好.md','你好啊,我是node.js',function(err){
if(err){
console.log('文件写入失败');
}
else{
console.log('文件写入成功');
}
})
// 在node中专门提供一个核心模块: http
// http 这个模块的职责就是帮你创建编写服务器的
// 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/来访问')
})
var http = require('http')
// 2.使用http.createServer() 方法创建一个web服务器
// 返回一个Server实例
var server = http.createServer()
// request请求处理函数,需要接收两个参数
// Request请求对象
// 请求对象可以用来获取客户端的一些请求信息,例如请求路径
// Response 响应对象
// 响应对象可以用来给客户端发送响应信息
server.on('request', function(request, response) {
// 127.0.0.1:3000/
// 127.0.0.1:3000/a
// 127.0.0.1:3000/foo
console.log('收到客户端的请求了,请求路径是:' + request.url)
// response对象有一个方法:write可以用来给客户端发送响应数据
// write可以使用多次。但是最后一定要使用end来结束响应,否则客户端会一直等待
response.write('hello')
response.write(' node')
// 告诉客户端,我的话说完了,你可以呈递给用户了
response.end()
// 由于我们的服务器的能力还非常弱,无论什么请求,都只能响应hello ndoe
// 思考
// 我们希望当请求不同的路径时,响应不同的结果
// 例如:
// /index
// /login 登录
// /register 注册
// /haha 哈哈哈
})
// 4.绑定端口号,启动服务器
server.listen(3000, function () {
console.log('服务器启动成功了,可以通过http://127.0.0.1:3000/来访问')
})
使用Node编写应用程序主要就是在使用:
在Node中的JavaScript还有一个重要的概念,模块系统。
语法:
var 自定义变量名 = require('模块')
作用:
exports.a = 123;
exports.b = function(){
console.log('bbb')
};
exports.c = {
foo:"bar"
};
exports.d = 'hello';
导出单个成员(拿到的就是函数,字符串):
module.exports = 'hello';
以下情况会覆盖:
module.exports = 'hello';
//后者会覆盖前者
module.exports = function add(x,y) {
return x+y;
}
也可以通过以下方法来导出多个成员:
module.exports = {
foo = 'hello',
add:function(){
return x+y;
}
};
exports和module.exports的一个引用:
console.log(exports === module.exports); //true
exports.foo = 'bar';
//等价于
module.exports.foo = 'bar';
当给exports重新赋值后,指向变化了,exports!= module.exports.
最终return的是module.exports,无论exports中的成员是什么都没用。
// 真正去使用的时候:
// 导出多个成员:exports.xxx = xxx
// 导出多个成员也可以:module.exports = {
// }
// 导出单个成员:module.exports
总结
// 引用服务
var http = require('http');
var fs = require('fs');
// 引用模板
var template = require('art-template');
// 创建服务
var server = http.createServer();
// 公共路径
var wwwDir = 'D:/app/www';
server.on('request', function (req, res) {
var url = req.url;
// 读取文件
fs.readFile('./template-apche.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.')
}
// 使用模板引擎解析替换data中的模板字符串
// 去xmpTempleteList.html中编写模板语法
var htmlStr = template.render(data.toString(), {
title: 'D:/app/www/ 的索引',
files:files
});
// 发送响应数据
res.end(htmlStr);
})
})
});
server.listen(3000, function () {
console.log('running....');
})
1.jQuery中的each 和 原生JavaScript方法forEach的区别:
提供源头:
原生js是es5提供的(不兼容IE8),
jQuery的each是jQuery第三方库提供的(如果要使用需要用2以下的版本也就是1.版本),它的each方法主要用来遍历jQuery实例对象(伪数组),
同时也可以做低版本forEach的替代品,jQuery的实例对象不能使用forEach方法,如果想要使用必须转为数组([].slice.call(jQuery实例对象))才能使用
2.模块中导出多个成员和导出单个成员
3.301和302的区别:
301永久重定向,浏览器会记住
302临时重定向
4.exports和module.exports的区别:
每个模块中都有一个module对象
module对象中有一个exports对象
我们可以把需要导出的成员都挂载到module.exports接口对象中
也就是`module.exports.xxx = xxx`的方式
但是每次写太多了就很麻烦,所以Node为了简化代码,就在每一个模块中都提供了一个成员叫`exports`
`exports === module.exports`结果为true,所以完全可以`exports.xxx = xxx`
当一个模块需要导出单个成员的时候必须使用`module.exports = xxx`的方式,=,使用`exports = xxx`不管用,因为每个模块最终return的是module.exports,而exports只是module.exports的一个引用,所以`exports`即使重新赋值,也不会影响`module.exports`。
有一种赋值方式比较特殊:`exports = module.exports`这个用来新建立引用关系的。
require的加载规则:
var 名称 = require('npm install【下载包】 的包名')
// 如果非路径形式的标识
// 路径形式的标识:
// ./ 当前目录 不可省略
// ../ 上一级目录 不可省略
// /xxx也就是D:/xxx
// 带有绝对路径几乎不用(D:/a/foo.js)
// 首位表示的是当前文件模块所属磁盘根目录
// require('./a');
// 核心模块
// 核心模块本质也是文件,核心模块文件已经被编译到了二进制文件中了,我们只需要按照名字来加载就可以了
require('fs');
// 第三方模块
// 凡是第三方模块都必须通过npm下载(npm i node_modules),使用的时候就可以通过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不存在或者mian指定的入口模块不存在
// 则node会自动找该目录下的index.js
// 也就是说index.js是一个备选项,如果main没有指定,则加载index.js文件
//
// 如果条件都不满足则会进入上一级目录进行查找
// 注意:一个项目只有一个node_modules,放在项目根目录中,子目录可以直接调用根目录的文件
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 moudle xxx
// 一个项目有且仅有一个 node_modules 而且是存放到项目的根目录
模块标识符中的/和文件操作路径中的/
文件操作路径:
// 咱们所使用的所有文件操作的API都是异步的
// 就像ajax请求一样
// 读取文件
// 文件操作中 ./ 相当于当前模块所处磁盘根目录,可以省略
// ./index.txt 相对于当前目录
// /index.txt 相对于当前目录
// /index.txt 绝对路径,当前文件模块所处根目录
// d:/express/index.txt 绝对路径
fs.readFile('./index.txt',function(err,data){
if(err){
return console.log('读取失败');
}
console.log(data.toString());
})
模块操作路径:
// 在模块加载中,相对路径中的./不能省略
// 这里省略了.也是磁盘根目录
require('./index')('hello')
npm网站
npmjs.com 网站 是用来搜索npm包的或发布包
https://www.npmjs.com/
npm命令行工具
npm是一个命令行工具,只要安装了node就已经安装了npm。
npm也有版本概念,可以通过npm --version
来查看npm的版本
升级npm(自己升级自己):
npm install --global npm
常用命令
解决npm被墙问题
npm存储包文件的服务器在国外,有时候会被墙,速度很慢,所以需要解决这个问题。
安装淘宝的cnpm:
npm install -g cnpm --registry=https://registry.npm.taobao.org;
#在任意目录执行都可以
#--global表示安装到全局,而非当前目录
#--global不能省略,否则不管用
npm install --global cnpm
安装包的时候把以前的npm替换成cnpm。
#走国外的npm服务器下载jQuery包,速度比较慢
npm install jQuery;
#使用cnpm就会通过淘宝的服务器来下载jQuery
cnpm install jQuery;
如果不想安装cnpm又想使用淘宝的服务器来下载:
npm install jquery --registry=https://npm.taobao.org;
但是每次手动加参数就很麻烦,所以我们可以把这个选项加入到配置文件中:
npm config set registry https://npm.taobao.org;
#查看npm配置信息
npm config list;
// 有时淘宝镜像网站不可以,我们则取消
npm config delete registry
npm config set registry https://registry.npmjs.org
只要经过上面的配置命令,则以后所有的npm install都会通过淘宝的服务器来下载
每一个项目都要有一个package.json文件(包描述文件,就像产品的说明书一样)
这个文件可以通过npm init自动初始化出来
D:\code\node中的模块系统>npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.
See `npm help json` for definitive documentation on these fields
and exactly what they do.
Use `npm install ` afterwards to install a package and
save it as a dependency in the package.json file.
Press ^C at any time to quit.
package name: (node中的模块系统)
Sorry, name can only contain URL-friendly characters.
package name: (node中的模块系统) cls
version: (1.0.0)
description: 这是一个测试项目
entry point: (main.js)
test command:
git repository:
keywords:
author: xiaochen
license: (ISC)
About to write to D:\code\node中的模块系统\package.json:
{
"name": "cls",
"version": "1.0.0",
"description": "这是一个测试项目",
"main": "main.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "xiaochen",
"license": "ISC"
}
Is this OK? (yes) yes
对于目前来讲,最有用的是dependencies选项,可以用来帮助我们保存第三方包的依赖信息。
如果node_modules删除了也不用担心,只需要在控制面板中npm install就会自动把package.json中的dependencies中所有的依赖项全部都下载回来。
npm i --global npm
升级
npm 5以前是不会有package-lock.json这个文件
npm5以后才加入这个文件
当你安装包的时候,npm都会生成或者更新package-lock.json这个文件
path路径操作模块
参考文档:https://nodejs.org/docs/latest-v13.x/api/path.html
参考文档:https://nodejs.org/dist/latest-v14.x/docs/api/path.html
在每个模块中,除了require,exports等模块相关的API之外,还有两个特殊的成员:
__dirname,是一个成员,可以用来动态获取当前文件模块所属目录的绝对路径
__filename,可以用来动态获取当前文件的绝对路径(包含文件名)
__dirname和filename是不受执行node命令所属路径影响的
在文件操作中,使用相对路径是不可靠的,因为node中文件操作的路径被设计为相对于执行node命令所处的路径。(不是bug,有使用场景)
所以为了解决这个问题,只需要把相对路径变为绝对路径(绝对路径不受任何影响)就可以了。
就可以使用__dirname或者__filename来帮助我们解决这个问题
在拼接路径的过程中,为了避免手动拼接带来的一些低级错误,推荐使用path.join()来辅助拼接
var fs = require('fs');
var path = require('path');
// console.log(__dirname + 'a.txt');
// path.join方法会将文件操作中的相对路径都统一的转为动态的绝对路径
fs.readFile(path.join(__dirname + '/a.txt'),'utf8',function(err,data){
if(err){
throw err
}
console.log(data);
});
补充:模块中的路径标识和这里的路径没关系,不受影响(就是相对于文件模块)
注意:
模块中的路径标识和文件操作中的相对路径标识不一致
模块中的路径标识就是相对于当前文件模块,不受node命令所处路径影响
不同的文件资源对应的Content-Type格式可以去网站查找
11-http.js
// require
// 端口号
var http = require('http')
var server = http.createServer()
server.on('request', function(req, res){
// 服务器默认发送的数据,其实是utf-8编码内容
// 但是浏览器不知道你是utf-8编码的内容
中文操作系统默认是gbk // 浏览器在不知道服务器响应内容的编码的情况下会按照当前操作系统的默认编码区解析
//
// 解决办法就是正确的告诉浏览器我给你发送的内容是什么编码的
// 在http协议中, Content-Type就是用来开告知对方我给你发送的数据内容是什么类型
// 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(5000, function(){
console.log('Server is runnning...')
})
不同的文件资源类型的content-type
// 1. 结合 fs 发送文件中的数据
// 2. Content-Type
// http://tool.oschina.net/commons
// 不同的资源对应的 Content-Type 是不一样的
// 图片不需要指定编码
// 一般只为字符数据才指定编码
var http = require('http')
var fs = require('fs')
var server = http.createServer()
server.on('request', function (req, res) {
// / index.html
var url = req.url
if (url === '/') {
// 肯定不这么干
// res.end('Document 首页
/html>')
// 我们要发送的还是在文件中的内容
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 === '/xiaoming') {
// url:统一资源定位符
// 一个 url 最终其实是要对应到一个资源的
fs.readFile('./resource/ab2.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(3000, function () {
console.log('Server is running...')
})
第一次总结
Node 中的 JavaScript
exports
exports
接口对象中require
这个模块,谁就可以得到模块内部的 exports
接口对象require
方法来加载,然后才可以使用,例如:
var fs = require('fs')
http
模块系统
Node 中的其它的核心模块
做一个小管理系统:
Express Web 开发框架
npm install express
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
// / index.html
// /a.txt wwwDir + /a.txt
// /apple/login.html wwwDir + /apple/login.html
// /img/ab1.jpg wwwDir + /img/ab1.jpg
var filePath = '/index.html'
if (url !== '/') {
filePath = url
}
fs.readFile(wwwDir + filePath, function (err, data) {
if (err) {
return res.end('404 Not Found.')
}
res.end(data)
})
})
// 3. 绑定端口号,启动服务
server.listen(3000, function () {
console.log('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.')
}
// 1. 如何得到 wwwDir 目录列表中的文件名和目录名
// fs.readdir
// 2. 如何将得到的文件名和目录名替换到 template.html 中
// 2.1 在 template.html 中需要替换的位置预留一个特殊的标记(就像以前使用模板引擎的标记一样)
// 2.2 根据 files 生成需要的 HTML 内容
// 只要你做了这两件事儿,那这个问题就解决了
fs.readdir(wwwDir, function (err, files) {
if (err) {
return res.end('Can not find www dir.')
}
// 2.1 生成需要替换的内容
var content = ''
files.forEach(function (item) {
// 在 EcmaScript 6 的 ` 字符串中,可以使用 ${} 来引用变量
content += `
${item}/
2017/11/2 上午10:32:47
`
})
// 2.3 替换
data = data.toString()
data = data.replace('^_^', content)
// 3. 发送解析替换过后的响应数据
res.end(data)
})
})
})
server.listen(3000, function () {
console.log('running...')
})
// art-template
// art-template 不仅可以在浏览器使用,也可以在 node 中使用
// 安装:
// npm install art-template
// 该命令在哪执行就会把包下载到哪里。默认会下载到 node_modules 目录中
// node_modules 不要改,也不支持改。
// 在 Node 中使用 art-template 模板引擎
// 模板引起最早就是诞生于服务器领域,后来才发展到了前端。
//
// 1. 安装 npm install art-template
// 2. 在需要使用的文件模块中加载 art-template
// 只需要使用 require 方法加载就可以了:require('art-template')
// 参数中的 art-template 就是你下载的包的名字
// 也就是说你 isntall 的名字是什么,则你 require 中的就是什么
// 3. 查文档,使用模板引擎的 API
var template = require('art-template')
var fs = require('fs')
// 这里不是浏览器
// template('script 标签 id', {对象})
// var tplStr = `
//
//
//
//
// Document
//
//
// 大家好,我叫:{{ name }}
// 我今年 {{ age }} 岁了
// 我来自 {{ province }}
// 我喜欢:{{each hobbies}} {{ $value }} {{/each}}
//
//
// `
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)
})
代码风格
无分号
(
[
服务端渲染
服务端渲染和客户端渲染的区别
feedback
总结:
art-template里面用的语法是jQuery吗, each什么的 我晕了 each,forEach, 遍历的全混了
每一次的复习贼重要 老师很不错 我喜欢
在以后的工作中 用到node.js的地方多吗? 在留言本的案例中 点击发表留言跳转页面的路径是url路径 和之前写的页面跳转写的文件路径还是有点分不清。
老师讲的挺清晰的 可是第一节太困了 路径有点没转变过来
如果从a中调用b中的数据,又从b中调用a中的数据,执行a代码,为什么把b中的执行完后才会执行a,而不是在b调用a的时候a中的代码继续执行
网站开发模型
在 Node 中使用 art-template 模板引擎
客户端渲染和服务端渲染的区别
处理留言本案例首页数据列表渲染展示
处理留言本案例发表留言功能
掌握如何解析请求路径中的查询字符串
原生的http在某些方面表现不足以应对我们的开发需求,所以就需要使用框架来加快我们的开发效率,框架的目的就是提高效率,让我们的代码高度统一。
在node中有很多web开发框架。主要学习express
http://expressjs.com/
,其中主要封装的是http。// 1 安装
// npm init -y npm i -S express 创建一个app.js文件夹
// 2 引包
var express = require('express');
// 3 创建服务器应用程序
// 也就是原来的http.createServer();
var app = express();
// 公开指定目录
// 只要通过这样做了,就可以通过/public/xx的方式来访问public目录中的所有资源
// 在Express中开放资源就是一个API的事
app.use('/public/',express.static('/public/'));
//模板引擎在Express中开放模板也是一个API的事
// 当服务器收到get请求 / 的时候,执行回调处理函数
app.get('/',function(req,res){
res.send('hello express');
})
// 相当于server.listen
app.listen(3000,function(){
console.log('app is runing at port 3000');
})
我们这里可以使用一个第三方命令行工具,ndoemon
来帮我们解决频繁修改代码重启服务器的问题
nodemon
是一个基于Node.js
开发的一个第三方命令行工具,我们使用的时候需要独立安装
# 在任意目录下执行该命令都可以
# 也就是说,所有需要 --global 来安装的包都可以在任意目录下执行
npm install --global nodemon
安装完毕之后,使用:
node app.js
# 使用 nodemon
nodemon app.js
只要通过nodemon
启动的服务,则它会监视你的文件变化,当文件发生变化的时候,自动帮你重启服务器。
路由:
//当你以get方法请求/的时候,执行对应的处理函数
app.get('/',function(req,res){
res.send('hello world');
})
post:
//当你以post方法请求/的时候,执行对应的处理函数
app.post('/',function(req,res){
res.send('hello world');
})
// app.use不仅仅是用来处理静态资源的,还可以做很多工作(body-parser的配置)
app.use(express.static('public'));
app.use(express.static('files'));
app.use('/stataic',express.static('public'));
// 引入express
var express = require('express');
// 创建app
var app = express();
// 开放静态资源
// 1.当以/public/开头的时候,去./public/目录中找对应资源
// 访问:http://127.0.0.1:3000/public/login.html
app.use('/public/',express.static('./public/'));
// 2.当省略第一个参数的时候,可以通过省略/public的方式来访问
// 访问:http://127.0.0.1:3000/login.html
// app.use(express.static('./public/'));
// 3.访问:http://127.0.0.1:3000/a/login.html
// a相当于public的别名
// app.use('/a/',express.static('./public/'));
//
app.get('/',function(req,res){
res.end('hello world');
});
app.listen(3000,function(){
console.log('express app is runing...');
});
安装:
npm install --save art-template
npm install --save express-art-template
//两个一起安装
npm i --save art-template express-art-template
配置:
// 配置使用 art-template 模板引擎
// 第一个参数,表示,当渲染以 .art 结尾的文件的时候,使用 art-template 模板引擎
// express-art-template 是专门用来在 Express 中把 art-template 整合到 Express 中
// 虽然外面这里不需要记载 art-template 但是也必须安装
// 原因就在于 express-art-template 依赖了 art-template
app.engine('html', require('express-art-template'))
使用:
// Express 为 Response 相应对象提供了一个方法:render
// render 方法默认是不可以使用,但是如果配置了模板引擎就可以使用了
// res.render('html模板名', {模板数据})
// 第一个参数不能写路径,默认会去项目中的 views 目录查找该模板文件
// 也就是说 Express 有一个约定:开发人员把所有的视图文件都放到 views 目录中
如果希望修改默认的views视图渲染存储目录,可以:
// 第一个参数views千万不要写错
app.set('views',目录路径);
模板页:
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>模板页</title>
<link rel="stylesheet" href="/node_modules/bootstrap/dist/css/bootstrap.css"/>
{{ block 'head' }}{{ /block }}
</head>
<body>
<!-- 通过include导入公共部分 -->
{{include './header.html'}}
<!-- 留一个位置 让别的内容去填充 -->
{{ block 'content' }}
<h1>默认内容</h1>
{{ /block }}
<!-- 通过include导入公共部分 -->
{{include './footer.html'}}
<!-- 公共样式 -->
<script src="/node_modules/jquery/dist/jquery.js" ></script>
<script src="/node_modules/bootstrap/dist/js/bootstrap.js" ></script>
{{ block 'script' }}{{ /block }}
</body>
</html>
模板的继承:
header页面:
<div id="">
<h1>公共的头部</h1>
</div>
footer页面:
<div id="">
<h1>公共的底部</h1>
</div>
模板页的使用:
<!-- 继承(extend:延伸,扩展)模板也layout.html -->
<!-- 把layout.html页面的内容都拿进来作为index.html页面的内容 -->
{{extend './layout.html'}}
<!-- 向模板页面填充新的数据 -->
<!-- 填充后就会替换掉layout页面content中的数据 -->
<!-- style样式方面的内容 -->
{{ block 'head' }}
<style type="text/css">
body{
background-color: skyblue;
}
</style>
{{ /block }}
{{ block 'content' }}
<div id="">
<h1>Index页面的内容</h1>
</div>
{{ /block }}
<!-- js部分的内容 -->
{{ block 'script' }}
<script type="text/javascript">
</script>
{{ /block }}
获取get请求数据:
Express内置了一个api,可以直接通过req.query来获取数据
// 通过requery方法获取用户输入的数据
// req.query只能拿到get请求的数据
var comment = req.query;
获取post请求数据:
在Express中没有内置获取表单post请求体的api,这里我们需要使用一个第三方包body-parser来获取数据。
安装:
npm install --save body-parser;
配置:
// 配置解析表单 POST 请求体插件(注意:一定要在 app.use(router) 之前 )
var express = require('express')
// 引包
var bodyParser = require('body-parser')
var app = express()
// 配置body-parser
// 只要加入这个配置,则在req请求对象上会多出来一个属性:body
// 也就是说可以直接通过req.body来获取表单post请求数据
// parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: false }))
// parse application/json
app.use(bodyParser.json())
使用:
app.use(function (req, res) {
res.setHeader('Content-Type', 'text/plain')
res.write('you posted:\n')
// 可以通过req.body来获取表单请求数据
res.end(JSON.stringify(req.body, null, 2))
})
参考文档:https://github.com/expressjs/session
安装:
npm install express-session
配置:
//该插件会为req请求对象添加一个成员:req.session默认是一个对象
//这是最简单的配置方式
//Session是基于Cookie实现的
app.use(session({
//配置加密字符串,他会在原有的基础上和字符串拼接起来去加密
//目的是为了增加安全性,防止客户端恶意伪造
secret: 'keyboard cat',
resave: false,
saveUninitialized: true,//无论是否适用Session,都默认直接分配一把钥匙
cookie: { secure: true }
}))
使用:
// 读
//添加Session数据
//session就是一个对象
req.session.foo = 'bar';
//写
//获取session数据
req.session.foo
//删
req.session.foo = null;
delete req.session.foo
提示:
默认Session数据时内存储数据,服务器一旦重启,真正的生产环境会把Session进行持久化存储。
// 创建一个名为crud的文件夹,在该文件夹路径下,使用
// npm init -y
// npm i -S exxpress
// npm install --save art-template express-art-template
// npm i -S [email protected]
// 将所有需要的环境配置好
// 创建一个名为app.js和db.json的文件,名为public和views的文件夹,在public文件夹下创建js css img文件夹,views文件中放art-template模板
// 找一个网页模板,如https://v3.bootcss.com/examples/dashboard/ 打开网页源码,直接ctrl+c,在views文件下创建一个index.html文件,ctrl+v把源码复制进去(这是标准流程,修改后的index.html代码会贴在下方)
// 具体修改后的app.js,db.json和index.html如下
app.js
var express = require('express')
var fs = require('fs')
var app = express()
app.engine('html', require('express-art-template'))
app.use('/node_modules/', express.static('./node_modules/'))
app.use('/public/', express.static('./public/'))
app.get('/', function (req, res) {
// readFile 的第二个参数是可选的,传入 utf8就是告诉它把读取到的文件直接按照utf-8编码 转成我们能认识的字符
// 除了这样的转换之外,也可以通过data.toString() 的方式
fs.readFile('./db.json', 'utf8', function(err, data){
if (err) {
return res.status(500).send('Server error')
}
// 从文件中读取到的数一定是字符串
// 所以这里一定要手动转成对象
var students = JSON.parse(data).students
res.render('index.html', {
fruits: [
'苹果',
'香蕉',
'橘子',
'梨'
],
students: students
})
})
})
app.listen(3000, function () {
console.log('running 3000...')
})
db.json
{
"students": [
{"id": 1, "name": "张三", "gender": 0, "age": 18, "hobbies": "吃饭,睡觉,打豆豆,lol"},
{"id": 2, "name": "张三", "gender": 0, "age": 18, "hobbies": "吃饭,睡觉,打豆豆,lol"},
{"id": 3, "name": "张三", "gender": 0, "age": 18, "hobbies": "吃饭,睡觉,打豆豆,lol"},
{"id": 4, "name": "张三", "gender": 0, "age": 18, "hobbies": "吃饭,睡觉,打豆豆,lol"},
{"id": 5, "name": "张三", "gender": 0, "age": 18, "hobbies": "吃饭,睡觉,打豆豆,lol"},
{"id": 6, "name": "张三", "gender": 0, "age": 18, "hobbies": "吃饭,睡觉,打豆豆,lol"}
]
}
index.html
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
<meta name="description" content="">
<meta name="author" content="">
<link rel="icon" href="https://cdn.jsdelivr.net/npm/@bootcss/[email protected]/favicon.ico">
<link rel="canonical" href="https://getbootstrap.com/docs/3.4/examples/dashboard/">
<title>Dashboard Template for Bootstrap</title>
<!-- Bootstrap core CSS -->
<link href="/node_modules/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- Custom styles for this template -->
<link href="/public/css/main.css" rel="stylesheet">
</head>
<body>
<nav class="navbar navbar-inverse navbar-fixed-top">
<div class="container-fluid">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Project name</a>
</div>
<div id="navbar" class="navbar-collapse collapse">
<ul class="nav navbar-nav navbar-right">
<li><a href="#">Dashboard</a></li>
<li><a href="#">Settings</a></li>
<li><a href="#">Profile</a></li>
<li><a href="#">Help</a></li>
</ul>
<form class="navbar-form navbar-right">
<input type="text" class="form-control" placeholder="Search...">
</form>
</div>
</div>
</nav>
<div class="container-fluid">
<div class="row">
<div class="col-sm-3 col-md-2 sidebar">
<ul class="nav nav-sidebar">
<li class="active"><a href="#">Overview <span class="sr-only">(current)</span></a></li>
<li><a href="#">Reports</a></li>
<li><a href="#">Analytics</a></li>
<li><a href="#">Export</a></li>
</ul>
<ul class="nav nav-sidebar">
<li><a href="">Nav item</a></li>
<li><a href="">Nav item again</a></li>
<li><a href="">One more nav</a></li>
<li><a href="">Another nav item</a></li>
<li><a href="">More navigation</a></li>
</ul>
<ul class="nav nav-sidebar">
<li><a href="">Nav item again</a></li>
<li><a href="">One more nav</a></li>
<li><a href="">Another nav item</a></li>
</ul>
</div>
<div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
<h1 class="page-header">Dashboard</h1>
<div class="row placeholders">
{{ each fruits }}
<div class="col-xs-6 col-sm-3 placeholder">
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAHd3dwAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==" width="200" height="200" class="img-responsive" alt="Generic placeholder thumbnail">
<h4>{{ $value }}</h4>
<span class="text-muted">Something else</span>
</div>
{{ /each }}
</div>
<h2 class="sub-header">Section title</h2>
<a class="btn btn-success" href="">添加学生</a>
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>#</th>
<th>姓名</th>
<th>性别</th>
<th>年龄</th>
<th>爱好</th>
</tr>
</thead>
<tbody>
{{ each students }}
<tr>
<td>{{ $value.id }}</td>
<td>{{ $value.name }}</td>
<td>{{ $value.gender }}</td>
<td>{{ $value.age }}</td>
<td>{{ $value.hobbies }}</td>
</tr>
{{ /each }}
</tbody>
</table>
</div>
</div>
</div>
</div>
</body>
</html>
在该项目的根路径下nodemon app.js即可启动项目
进一步改进案例
请求方法 | 请求路径 | get 参数 | post 参数 | 备注 |
---|---|---|---|---|
GET | /studens | 渲染首页 | ||
GET | /students/new | 渲染添加学生页面 | ||
POST | /studens/new | name、age、gender、hobbies | 处理添加学生请求 | |
GET | /students/edit | id | 渲染编辑页面 | |
POST | /studens/edit | id、name、age、gender、hobbies | 处理编辑请求 | |
GET | /students/delete | id | 处理删除请求 | |
router.js
/**
* router.js 路由模块
* 职责:
* 处理路由
* 根据不同的请求方法+请求路径设置具体的请求处理函数
* 模块职责要单一,不要乱写
* 我们划分模块的目的就是为了增强项目代码的可维护性
* 提升开发效率
*/
var fs = require('fs')
var Student = require('./student')
var express = require('express')
// 1.创建一个路由容器
var router = express.Router()
// 把路由挂载到router路由容器中
// readFile 的第二个参数是可选的,传入 utf8就是告诉它把读取到的文件直接按照utf-8编码 转成我们能认识的字符
// 除了这样的转换之外,也可以通过data.toString() 的方式
router.get('/students', function (req, res) {
Student.find(function(err, students){
if (err) {
return res.status(500).send('Server error')
}
// 从文件中读取到的数一定是字符串
// 所以这里一定要手动转成对象
res.render('index.html', {
fruits: [
'苹果',
'香蕉',
'橘子',
'梨'
],
students: students
})
})
})
// router.get('/students', function (req, res) {
// // readFile 的第二个参数是可选的,传入 utf8就是告诉它把读取到的文件直接按照utf-8编码 转成我们能认识的字符
// // 除了这样的转换之外,也可以通过data.toString() 的方式
// fs.readFile('./db.json', 'utf8', function(err, data){
// if (err) {
// return res.status(500).send('Server error')
// }
// // 从文件中读取到的数一定是字符串
// // 所以这里一定要手动转成对象
// var students = JSON.parse(data).students
// 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.渲染编辑页面
// 根据id把学生信息查出来
// 使用模板引擎渲染页面
Student.findById(parseInt(req.query.id), function(err, student){
if (err) {
return res.status(500).send('Server error')
}
res.render('edit.html', {
student: student
})
})
})
/*
*渲染编辑学生页面
*/
router.post('/students/edit', function(req, res){
// 1.获取表单数据
// req.body
// 2.更新
// Student.update()
// 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 error')
}
res.redirect('/students')
})
})
// 3.把 router 导出
module.exports = router
es6中的find和findIndex:
find接受一个方法作为参数,方法内部返回一个条件
find会便利所有的元素,执行你给定的带有条件返回值的函数
符合该条件的元素会作为find方法的返回值
如果遍历结束还没有符合该条件的元素,则返回undefined
// 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) {
// var conditionFunc = function (item, index) { return item.id === 4 }
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 === 2
})
console.log(ret)
student.js
/**
*职责:操作文件中的数据,只处理数据,不关心业务
*/
var fs = require('fs')
var dbPath = './db.json'
/**
* 获取所有学生列表
* callback中的参数
* 第一个参数是 err
* 成功是null
* 错误是 错误对象
* 第二个参数是 结果
* 成功是 数组
* 错误是undefined
*/
exports.find = function(callback) {
fs.readFile(dbPath, 'utf8', function(err, data){
if (err) {
return callback(err)
}
callback(null, JSON.parse(data).students)
})
}
/**
* 根据id获取学生信息对象
* @parm {number} id 学生id
* @parm {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)
}
// 成功就没错,所以错误对象是null
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
// 你要修改谁,就需要把谁找出来
// EcmaScript 6中的一个数组方阿飞: find
// 需要接受一个函数作为参数
// 当某个遍历项符合 item.id === student.id 条件的时候,find会终止遍历,同时返回
// 把id统一转换为数字类型
student.id = parseInt(student.id)
var stu = students.find(function(item){
return item.id === parseInt(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)
}
// 成功就没错,所以错误对象是null
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)
}
// 成功就没错,所以错误对象是null
callback(null)
})
})
}
主页面
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
<meta name="description" content="">
<meta name="author" content="">
<link rel="icon" href="https://cdn.jsdelivr.net/npm/@bootcss/[email protected]/favicon.ico">
<link rel="canonical" href="https://getbootstrap.com/docs/3.4/examples/dashboard/">
<title>Dashboard Template for Bootstrap</title>
<!-- Bootstrap core CSS -->
<link href="/node_modules/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- Custom styles for this template -->
<link href="/public/css/main.css" rel="stylesheet">
</head>
<body>
<nav class="navbar navbar-inverse navbar-fixed-top">
<div class="container-fluid">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Project name</a>
</div>
<div id="navbar" class="navbar-collapse collapse">
<ul class="nav navbar-nav navbar-right">
<li><a href="#">Dashboard</a></li>
<li><a href="#">Settings</a></li>
<li><a href="#">Profile</a></li>
<li><a href="#">Help</a></li>
</ul>
<form class="navbar-form navbar-right">
<input type="text" class="form-control" placeholder="Search...">
</form>
</div>
</div>
</nav>
<div class="container-fluid">
<div class="row">
<div class="col-sm-3 col-md-2 sidebar">
<ul class="nav nav-sidebar">
<li class="active"><a href="#">Overview <span class="sr-only">(current)</span></a></li>
<li><a href="#">Reports</a></li>
<li><a href="#">Analytics</a></li>
<li><a href="#">Export</a></li>
</ul>
<ul class="nav nav-sidebar">
<li><a href="">Nav item</a></li>
<li><a href="">Nav item again</a></li>
<li><a href="">One more nav</a></li>
<li><a href="">Another nav item</a></li>
<li><a href="">More navigation</a></li>
</ul>
<ul class="nav nav-sidebar">
<li><a href="">Nav item again</a></li>
<li><a href="">One more nav</a></li>
<li><a href="">Another nav item</a></li>
</ul>
</div>
<div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
<h1 class="page-header">Dashboard</h1>
<div class="row placeholders">
{{ each fruits }}
<div class="col-xs-6 col-sm-3 placeholder">
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAHd3dwAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==" width="200" height="200" class="img-responsive" alt="Generic placeholder thumbnail">
<h4>{{ $value }}</h4>
<span class="text-muted">Something else</span>
</div>
{{ /each }}
</div>
<h2 class="sub-header">Section title</h2>
<a class="btn btn-success" href="/students/new">添加学生</a>
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>#</th>
<th>姓名</th>
<th>性别</th>
<th>年龄</th>
<th>爱好</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{{ each students }}
<tr>
<td>{{ $value.id }}</td>
<td>{{ $value.name }}</td>
<td>{{ $value.gender }}</td>
<td>{{ $value.age }}</td>
<td>{{ $value.hobbies }}</td>
<td>
<a href="/students/edit?id={{ $value.id }}">编辑</a>
<a href="/students/delete?id={{ $value.id }}">删除</a>
</td>
</tr>
{{ /each }}
</tbody>
</table>
</div>
</div>
</div>
</div>
</body>
</html>
添加学生的页面
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
<meta name="description" content="">
<meta name="author" content="">
<link rel="icon" href="https://cdn.jsdelivr.net/npm/@bootcss/[email protected]/favicon.ico">
<link rel="canonical" href="https://getbootstrap.com/docs/3.4/examples/dashboard/">
<title>Dashboard Template for Bootstrap</title>
<!-- Bootstrap core CSS -->
<link href="/node_modules/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- Custom styles for this template -->
<link href="/public/css/main.css" rel="stylesheet">
</head>
<body>
<nav class="navbar navbar-inverse navbar-fixed-top">
<div class="container-fluid">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Project name</a>
</div>
<div id="navbar" class="navbar-collapse collapse">
<ul class="nav navbar-nav navbar-right">
<li><a href="#">Dashboard</a></li>
<li><a href="#">Settings</a></li>
<li><a href="#">Profile</a></li>
<li><a href="#">Help</a></li>
</ul>
<form class="navbar-form navbar-right">
<input type="text" class="form-control" placeholder="Search...">
</form>
</div>
</div>
</nav>
<div class="container-fluid">
<div class="row">
<div class="col-sm-3 col-md-2 sidebar">
<ul class="nav nav-sidebar">
<li class="active"><a href="#">Overview <span class="sr-only">(current)</span></a></li>
<li><a href="#">Reports</a></li>
<li><a href="#">Analytics</a></li>
<li><a href="#">Export</a></li>
</ul>
<ul class="nav nav-sidebar">
<li><a href="">Nav item</a></li>
<li><a href="">Nav item again</a></li>
<li><a href="">One more nav</a></li>
<li><a href="">Another nav item</a></li>
<li><a href="">More navigation</a></li>
</ul>
<ul class="nav nav-sidebar">
<li><a href="">Nav item again</a></li>
<li><a href="">One more nav</a></li>
<li><a href="">Another nav item</a></li>
</ul>
</div>
<div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
<h2 class="sub-header">添加学生</h2>
<div class="table-responsive">
<form action="/students/new" method="post">
<div class="form-group">
<label for="exampleInputEmail1">姓名</label>
<input type="text" class="form-control" id="" name="name" placeholder="Email">
</div>
<div class="form-group">
<label for="exampleInputPassword1">性别</label>
<div>
<label class="radio-inline">
<input type="radio" name="gender" id="inlineRadio1" value="option1"> 男
</label>
<label class="radio-inline">
<input type="radio" name="gender" id="inlineRadio2" value="option2"> 女
</label>
</div>
</div>
<div class="form-group">
<label for="">年龄</label>
<input class="form-control" type="number" id="" name="age">
</div>
<div class="form-group">
<label for="">爱好</label>
<input class="form-control" type="text" id="" name="hobbies">
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
</div>
</div>
</div>
</div>
</body>
</html>
编辑学生信息的页面
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
<meta name="description" content="">
<meta name="author" content="">
<link rel="icon" href="https://cdn.jsdelivr.net/npm/@bootcss/[email protected]/favicon.ico">
<link rel="canonical" href="https://getbootstrap.com/docs/3.4/examples/dashboard/">
<title>Dashboard Template for Bootstrap</title>
<!-- Bootstrap core CSS -->
<link href="/node_modules/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- Custom styles for this template -->
<link href="/public/css/main.css" rel="stylesheet">
</head>
<body>
<nav class="navbar navbar-inverse navbar-fixed-top">
<div class="container-fluid">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Project name</a>
</div>
<div id="navbar" class="navbar-collapse collapse">
<ul class="nav navbar-nav navbar-right">
<li><a href="#">Dashboard</a></li>
<li><a href="#">Settings</a></li>
<li><a href="#">Profile</a></li>
<li><a href="#">Help</a></li>
</ul>
<form class="navbar-form navbar-right">
<input type="text" class="form-control" placeholder="Search...">
</form>
</div>
</div>
</nav>
<div class="container-fluid">
<div class="row">
<div class="col-sm-3 col-md-2 sidebar">
<ul class="nav nav-sidebar">
<li class="active"><a href="#">Overview <span class="sr-only">(current)</span></a></li>
<li><a href="#">Reports</a></li>
<li><a href="#">Analytics</a></li>
<li><a href="#">Export</a></li>
</ul>
<ul class="nav nav-sidebar">
<li><a href="">Nav item</a></li>
<li><a href="">Nav item again</a></li>
<li><a href="">One more nav</a></li>
<li><a href="">Another nav item</a></li>
<li><a href="">More navigation</a></li>
</ul>
<ul class="nav nav-sidebar">
<li><a href="">Nav item again</a></li>
<li><a href="">One more nav</a></li>
<li><a href="">Another nav item</a></li>
</ul>
</div>
<div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
<h2 class="sub-header">编辑学生信息</h2>
<div class="table-responsive">
<form action="/students/edit" method="post">
<!-- 用来放一些不希望被用户看见,但是需要被提交到服务端的数据 -->
<input type="hidden" value="{{ student.id }}" name="id">
<div class="form-group">
<label for="">姓名</label>
<input type="text" class="form-control" id="" name="name" placeholder="name" required minlength="2" maxlength="10" value="{{ student.name }}">
</div>
<div class="form-group">
<label for="exampleInputPassword1">性别</label>
<div>
<label class="radio-inline">
<input type="radio" name="gender" id="inlineRadio1" value="option1"> 男
</label>
<label class="radio-inline">
<input type="radio" name="gender" id="inlineRadio2" value="option2"> 女
</label>
</div>
</div>
<div class="form-group">
<label for="">年龄</label>
<input class="form-control" type="number" id="" name="age" required min="1" max="150" value="{{ student.age }}">
</div>
<div class="form-group">
<label for="">爱好</label>
<input class="form-control" type="text" id="" name="hobbies" value="{{ student.hobbies }}">
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
</div>
</div>
</div>
</div>
</body>
</html>
// 使用回调函数可行
function add(x, y, callback) {
console.log(1)
setTimeout(function () {
var ret = x + y
callback(ret)
}, 1000)
}
add(10, 20, function (ret) {
console.log(ret)
})
Javascript单线程,事件循环
注意:凡是需要得到一个函数内部异步操作的结果
setTimeout
readFile
writeFile
ajax
这种情况必须通过:
回调函数
基于原生XMLHTTPRequest封装get方法
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)
})
require
、include
因为 PHP 当初在设计的时候就加入了这个功能
标签来引用加载,而且你还必须考虑加载的顺序问题关系型数据库(表就是关系,或者说表与表之间存在关系)。
非关系型数据库
mongoDB数据库的基本概念
{
qq:{
user:[
{},{},{}...
]
}
}
下载
安装
npm i mongoose
配置环境变量
最后输入mongod --version测试是否安装成功
启动:
mongodb 默认使用执行mongod 命令所处盼复根目录下的/data/db作为自己的数据存储目录
所以在第一次执行该命令之前先自己手动新建一个 /data/db
mongod
如果想要修改默认的数据存储目录,可以:
mongod --dbpath = 数据存储目录路径
停止:
在开启服务的控制台,直接Ctrl+C;
或者直接关闭开启服务的控制台。
另开一个控制台
连接:
# 该命令默认连接本机的 MongoDB 服务
mongo
退出:
# 在连接状态输入 exit 退出连接
exit
https://github.com/mongodb/node-mongodb-native
第三方包:mongoose
基于MongoDB官方的mongodb包再一次做了封装,名字叫mongoose
,是WordPress项目团队开发的。
官网:https://mongoosejs.com/
1.起步
npm init -y
npm i mongoose
小案例
// 1.引包
// 注意:安装后才能require使用
const mongoose = require('mongoose');
// 2.连接数据库
// 指定连接数据库后不需要存在,当你插入第一条数据库后会自动创建数据库
mongoose.connect('mongodb://localhost:27017/test', {useNewUrlParser: true, useUnifiedTopology: true});
// 创建一个模型
// 就是在设计数据库
// MongoDB 是动态的,非常灵活,只需要在代码中设计你的数据库就可以了
// mongoose 这个包就可以让你的设计编写过程变的非常的简单
const Cat = mongoose.model('Cat', { name: String });
for (var i=0; i < 100; i++) {
// 实例化一个 Cat
const kitty = new Cat({ name: '喵喵' + i });
// 持久化保存 kitty 实例
kitty.save().then(() => console.log('meow'));
}
// 1.引包
var mongoose = require('mongoose');
var Schema = mongoose.Shema
// 2.连接数据库
// 指定连接数据库后不需要存在,当你插入第一条数据库后会自动创建数据库
mongoose.connect('mongodb://localhost:27017/test', {useNewUrlParser: true, useUnifiedTopology: true});
// 设计集合结构(表结构)
// 字段名称就是表结构的属性名称
// 值
// 约束的目的是为了保证数据的完整性,不要有脏数据
var userSchema = new Schema({
username: {
type: String,
required: true // 必须有
},
password: {
type: String,
required: true
},
email: {
type: String
}
})
// 3. 将文档结构发布为模型
// mongoose.model 方法就是用来将一个架构发布为 model
// 第一个参数:传入一个大写名词单数字符串用来表示你的数据库名称
// mongoose 会自动将大写名词的字符串生成 小写复数 的集合名称
// 例如这里的 User 最终会变为 users 集合名称
// 第二个参数:架构 Schema
//
// 返回值:模型构造函数
var User = mongoose.model('User', userSchema)
// 4. 当我们有了模型构造函数之后,就可以使用这个构造函数对 users 集合中的数据为所欲为了(增删改查)
// **********************
// #region /新增数据
// **********************
var admin = new User({
username: 'zs',
password: '123456',
email: 'admin@admin.com'
})
admin.save(function (err, ret) {
if (err) {
console.log('保存失败')
} else {
console.log('保存成功')
console.log(ret)
}
})
// **********************
// #endregion /新增数据
// **********************
查询所有
User.find(function (err, ret) {
if (err) {
console.log('查询失败')
} else {
console.log(ret)
}
})
条件查询所有
User.find({
username: 'zs'
}, function (err, ret) {
if (err) {
console.log('查询失败')
} else {
console.log(ret)
}
})
条件查询单个
// 按照条件查询单个,查询出来的数据是一个对象({})
// 没有条件查询使用findOne方法,查询的是表中的第一条数据
User.findOne({
username: 'xiaoxiao'
}, function(err, ret) {
if (err) {
console.log('查询失败');
} else {
console.log(ret);
}
});
User.remove({
username: 'zs'
}, function (err, ret) {
if (err) {
console.log('删除失败')
} else {
console.log('删除成功')
console.log(ret)
}
})
根据条件删除一个:
Model.findOneAndRemove(conditions,[options],[callback]);
根据id删除一个:
User.findByIdAndRemove(id,[options],[callback]);
User.remove(conditions,doc,[options],[callback]);
User.FindOneAndUpdate([conditions],[update],[options],[callback]);
User.findByIdAndUpdate('5a001b23d219eb00c8581184', {
password: '123'
}, function (err, ret) {
if (err) {
console.log('更新失败')
} else {
console.log('更新成功')
}
})
使用数据库改造crud的案例
文档:https://www.npmjs.com/package/mysql
安装:npm install --save mysql
var mysql = require('mysql');
// 1. 创建连接
var connection = mysql.createConnection({
host : 'localhost',
user : 'root',
password : 'root',
database : 'users'
});
// 2. 连接数据库 打开冰箱门
connection.connect();
// 3. 执行数据操作 把大象放到冰箱
connection.query('SELECT * FROM `users`', function (error, results, fields) {
if (error) throw error;
console.log('The solution is: ', results);
});
// 插入数据
// connection.query('INSERT INTO users VALUES(0, "admin", "123456")', function (error, results, fields) {
// if (error) throw error;
// console.log('The solution is: ', results);
// });
// 4. 关闭连接 关闭冰箱门
connection.end();
通过回调嵌套的方式来保证顺序
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)
})
})
})
为了解决以上编码方式带来的问题(回调地狱嵌套),所以在EcmaScript6新增了一个API:Promise
。
promise基本语法:
var fs = require('fs')
var p1 = new Promise(function (resolve, reject) {
fs.readFile('./data/a.txt', 'utf8', function (err, data) {
if (err) {
reject(err)
} else {
resolve(data)
}
})
})
p1
.then(function (data) {
console.log(data)
}, function (err) {
console.log('读取文件失败了', err)
})
var fs = require('fs')
var p1 = new Promise(function (resolve, reject) {
fs.readFile('./data/a.txt', 'utf8', function (err, data) {
if (err) {
reject(err)
} else {
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
.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')
})
封装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')
})
.then(function (data) {
console.log(data)
return pReadFile('./data/c.txt')
})
.then(function (data) {
console.log(data)
})
mongoose所有的API都支持Promise:
User.findOne({username:'admin'},function(user){
if(user){
console.log('用户已存在')
} else {
new User({
username:'aaa',
password:'123',
email:'fffff'
}).save(function(){
console.log('注册成功');
})
}
})
User.findOne({
username:'admin'
})
.then(function(user){
if(user){
// 用户已经存在不能注册
console.log('用户已存在');
}
else{
// 用户不存在可以注册
return new User({
username:'aaa',
password:'123',
email:'fffff'
}).save();
}
})
.then(funciton(ret){
console.log('注册成功');
})
参考文档:https://es6.ruanyifeng.com/
.
app.js 项目的入口文件
controllers
models 存储使用mongoose设计的数据模型
node_modules 第三方包
package.json 包描述文件
package-lock.json 第三方包版本锁定文件(npm5之后才有)
public 公共静态资源
routes
views 存储视图目录
参考文档:http://expressjs.com/en/guide/using-middleware.html
中间件:把很复杂的事情分割成单个,然后依次有条理的执行。就是一个中间处理环节,有输入,有输出。
说的通俗易懂点儿,中间件就是一个(从请求到响应调用的方法)方法。
把数据从请求到响应分步骤来处理,每一个步骤都是一个中间处理环节。
var http = require('http');
var url = require('url');
var cookie = require('./expressPtoject/cookie');
var query = require('./expressPtoject/query');
var postBody = require('./expressPtoject/post-body');
var server = http.createServer(function(){
// 解析请求地址中的get参数
// var obj = url.parse(req.url,true);
// req.query = obj.query;
query(req,res); //中间件
// 解析请求地址中的post参数
req.body = {
foo:'bar'
}
});
if(req.url === 'xxx'){
// 处理请求
...
}
server.listen(3000,function(){
console.log('3000 runing...');
});
同一个请求对象所经过的中间件都是同一个请求对象和响应对象。
var express = require('express');
var app = express();
app.get('/abc',function(req,res,next){
// 同一个请求的req和res是一样的,
// 可以前面存储下面调用
console.log('/abc');
// req.foo = 'bar';
req.body = {
name:'xiaoxiao',
age:18
}
next();
});
app.get('/abc',function(req,res,next){
// console.log(req.foo);
console.log(req.body);
console.log('/abc');
});
app.listen(3000, function() {
console.log('app is running at port 3000.');
});
应用程序级别的中间件
万能匹配(不关心任何请求路径和请求方法的中间件):
app.use(function(req,res,next){
console.log('Time',Date.now());
next();
});
关心请求路径和请求方法的中间件:
app.use('/a',function(req,res,next){
console.log('Time',Date.now());
next();
});
严格匹配请求路径和请求方法的中间件
get:
app.get('/',function(req,res){
res.send('get');
});
post:
app.post('/a',function(req,res){
res.send('post');
});
put:
app.put('/user',function(req,res){
res.send('put');
});
delete:
app.delete('/delete',function(req,res){
res.send('delete');
});
总
var express = require('express');
var app = express();
// 中间件:处理请求,本质就是个函数
// 在express中,对中间件有几种分类
// 1 不关心任何请求路径和请求方法的中间件
// 也就是说任何请求都会进入这个中间件
// 中间件本身是一个方法,该方法接收三个参数
// Request 请求对象
// Response 响应对象
// next 下一个中间件
// // 全局匹配中间件
// app.use(function(req, res, next) {
// console.log('1');
// // 当一个请求进入中间件后
// // 如果需要请求另外一个方法则需要使用next()方法
// next();
// // next是一个方法,用来调用下一个中间件
// // 注意:next()方法调用下一个方法的时候,也会匹配(不是调用紧挨着的哪一个)
// });
// app.use(function(req, res, next) {
// console.log('2');
// });
// // 2 关心请求路径的中间件
// // 以/xxx开头的中间件
// app.use('/a',function(req, res, next) {
// console.log(req.url);
// });
// 3 严格匹配请求方法和请求路径的中间件
app.get('/',function(){
console.log('/');
});
app.post('/a',function(){
console.log('/a');
});
app.listen(3000, function() {
console.log('app is running at port 3000.');
});
app.use(function(err,req,res,next){
console.error(err,stack);
res.status(500).send('Something broke');
});
配置使用404中间件:
app.use(function(req,res){
res.render('404.html');
});
配置全局错误处理中间件:
app.get('/a', function(req, res, next) {
fs.readFile('.a/bc', funtion() {
if (err) {
// 当调用next()传参后,则直接进入到全局错误处理中间件方法中
// 当发生全局错误的时候,我们可以调用next传递错误对象
// 然后被全局错误处理中间件匹配到并进行处理
next(err);
}
})
});
//全局错误处理中间件
app.use(function(err,req,res,next){
res.status(500).json({
err_code:500,
message:err.message
});
});
参考文档:http://expressjs.com/en/resources/middleware.html