想要实现其他复杂的操作和效果,都要依靠 宿主环境 提供API,目前,已经嵌入 JavaScript 的宿主环境有多种,最常见的环境就是 浏览器 和 操作系统 ;
Node
是一个基于Chrome V8
引擎的JavaScript
运行环境。
Node
不是一种独立的语言、Node
不是 JavaScript
框架,
Node
是一个除了浏览器之外的、可以让JavaScript
运行的环境
Node.js 是一个让 JavaScript 运行在服务端的开发平台,是使用 事件驱动, 异步非阻塞I/O,单线程,跨平台的 JS 运行环境;
聊聊 Node.js 的历史 来自朴灵大大的 – Node.js 简史
windows下安装过程:
对于已经装过的,重新安装就会升级
安装成功后,打开命令行,输入
node –version 或者 node -v (显示node的版本号)
表示安装成功
其他平台的安装方式:
https://nodejs.org/zh-cn/download/package-manager/
node中的REPL环境类似于浏览器中的 Console控制台 ,可以做一些代码测试。
按ctrl + 两次c 退出REPL环境
但是, 我们写代码肯定不是在控制台中写,而是写在一个单独的.js文件中.
浏览器(客户端)中的JS
Node中的JS
let buffer=new Buffer('abc\r\nddasdfafd\r\ndfaerewtwert');
let buffer2=new Buffer('\r\n');
console.log(buffer.indexOf(buffer2));
//Buffer.concat 可以传入一个装有buffer的数组,之后会自动拼接返回
//buffer数据可以用toString、queryString模块的的parse 转换为看的懂的数据
node核心模块之一,用于操作文件;
中文手册 : http://nodejs.cn/api/fs.html
// 引入模块
var fs = require('fs');
// console.log(typeof fs); //object
// 向文件中写入内容
fs.writeFile('./2.1.txt','itcast',function(cb,cb2){
// 回调函数 (写入成功后执行的函数)
console.log(cb);
console.log(cb2);
})
// 从文件中读取内容
fs.readFile('./2.1.txt','utf8',function(e,d){
// 回调函数 (读取成功后执行的函数)
console.log(e);
console.log(d);
});
// 引入模块
var fs = require('fs');
// 向文件中追加内容
fs.readFile('./2.1.txt','utf8',function(e,d){
d+='2344';
fs.writeFile('./2.1.txt',d,function(e){
if(e){
console.log('写入失败')
}else{
console.log('写入成功')
}
})
});
node核心模块之一,用于搭建HTTP服务器;
中文手册 http://nodejs.cn/api/http.html
// 1. 导入http模块
var http = require('http');
// 2. 使用http这个模块中的createServer()创建一个服务器实例对象
var server = http.createServer();
// 3. 绑定端口号,启动web服务器
server.listen(8000, function() {
console.log(' 请访问http://localhost:8000');
});
// 4. 为这个服务器实例对象注册 request 请求处理函数
// 请求处理函数function(形参1,形参2){}
// 形参1:request请求对象 获取到当前请求的路径,方法等本次请求的所有信息
// 形参2:response响应对象 发送响应数据
server.on('request', function(request, response) {
console.log('服务端收到客户端的请求啦!!!');
// 向客户端页面返回字符串
response.write("hello node");
// 结束响应
response.end();
});
因为我们的服务器接受请求处理并响应数据时,并没有指定响应数据的类型,所以出现了乱码;
而在http中,我们可以通过服务器的响应头指定数据类型,在 http.ServerResponse 类 中为我们提供了setHeader 方法:
但是,我们不能一直将html代码写到服务器的方法中,而是需要建一个xx.html的文件,将html文件中的内容返回给客户端;
2.2.2 .html :
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<h1>你好,我是jinghong</h1>
<h2>另外,我还很帅……</h2>
</body>
</html>
nodejs代码
var http = require('http');
// 1:引入文件操作模块
var fs = require('fs');
var server = http.createServer();
server.on('request', function(request, response) {
// 2:读取html文件中的内容
fs.readFile('./2.2.2.html','utf8',function(error,html_data){
// 设置响应头
response.setHeader('Content-Type', 'text/html;charset=utf-8');
// 将html中的内容响应回客户端,结束响应
response.end(html_data);
})
});
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<h1>你好,我是jinghong</h1>
<h2>另外,我还很帅……</h2>
<img src="./img/03.jpg" alt="野生脆脆.jpg">
</body>
</html>
server.on('request', function(request, response) {
// url 属性返回请求的URL字符串
var urls = request.url;
if( urls =='/'){
fs.readFile('./2.2.2.html','utf8',function(error,html_data){
// 设置响应头
response.setHeader('Content-Type', 'text/html;charset=utf-8');
// 将html中的内容响应回客户端,结束响应
response.end(html_data);
})
}else if(urls.indexOf('jpg')>=0){ // 判断请求图片
fs.readFile('./img/03.jpg',function(error,html_data){
response.end(html_data);
})
}
}
<head>
<meta charset="UTF-8">
<title>Document</title>
<link rel="stylesheet" href="./public/h.css">
</head>
<body>
<h1>你好,我是jinghong</h1>
<h2>另外,我还很帅……</h2>
<img src="./img/03.jpg" alt="野生脆脆.jpg">
</body>
<script src="./public/h.js"></script>
</html>
server.on('request', function(request, response) {
// url 属性返回请求的URL字符串
var urls = request.url;
if( urls =='/'){
fs.readFile('./2.2.2.html','utf8',function(error,html_data){
// 设置响应头
response.setHeader('Content-Type', 'text/html;charset=utf-8');
// 将html中的内容响应回客户端,结束响应
response.end(html_data);
})
}else{
fs.readFile('.'+urls,function(error,html_data){
response.end(html_data);
})
}
});
模仿Apache服务器,遍历文件及文件,显示时间及大小;
右键另存为,下载页面当作静态页面模板使用;
使用node载入静态页面:
使用ajax技术在页面中发送请求到后台,apache.html
<script>
var xhr = new XMLHttpRequest();
xhr.onreadystatechange=function(){
if(this.readyState == 4){
console.log(this.responseText);
}
}
xhr.open('get','/file_list');
xhr.send();
</script>
node:
server.on('request', function(request, response) {
// url 属性返回请求的URL字符串
var urls = request.url;
if( urls =='/'){
fs.readFile('./apache.html','utf8',function(error,html_data){
// 设置响应头
response.setHeader('Content-Type', 'text/html;charset=utf-8');
// 将html中的内容响应回客户端,结束响应
response.end(html_data);
})
}else if(urls == '/file_list'){
fs.readdir('./','utf8',function(err,files){
response.end(JSON.stringify(files));
});
}else{
fs.readFile('.'+urls,function(error,html_data){
response.end(html_data);
})
}
});
apache.html –> ajax
xhr.onreadystatechange=function(){
if(this.readyState == 4){
var data = JSON.parse(this.responseText);
var htmls = '';
for(var i = 0;i<data.length;i++){
htmls+='<tr><td valign="top">';
htmls+= '<img src="./img/layout.gif" alt="[ ]"></td>';
htmls+='<td><a href="http://localhost/%e7%ac%94%e8%ae%b0-01.pdf">';
htmls+= data[i]+'</a> </td>';
htmls+= '<td align="right">2018-04-26 10:31 </td>';
htmls+= '<td align="right">3.2M</td><td> </td></tr>';
}
var tb = document.getElementsByTagName('tbody')[0];
tb.innerHTML+=htmls;
}
}
获取文件的其他属性:
var fs = require('fs');
fs.readdir('./','utf8',function(err,files){
fs.stat(files[0],function(er,st){
console.log(st.mtime);
console.log(st.size);
console.log(st.isFile());
})
});
修改node代码
server.on('request', function (request, response) {
// url 属性返回请求的URL字符串
var urls = request.url;
if (urls == '/') {
fs.readFile('./apache.html', 'utf8', function (error, html_data) {
// 设置响应头
response.setHeader('Content-Type', 'text/html;charset=utf-8');
// 将html中的内容响应回客户端,结束响应
response.end(html_data);
})
} else if (urls == '/file_list') {
fs.readdir('./', 'utf8', function (err, files) {
// response.end(JSON.stringify(files));
var file_obj = [];
// 判断条件:声明一个变量,这个变量用来记录两个数据的中数据的长度
var count = 0;
for (var i = 0; i < files.length; i++) {
file_obj[i] = {};
// 利用自调用匿名函数,保留i的变量值
(function (i) {
fs.stat(files[i], function (er, st) {
count ++;
file_obj[i].name = files[i];
if(st.isFile()){
file_obj[i].type = 'file';
}else{
file_obj[i].type = 'dir';
}
file_obj[i].mtime = st.mtime;
file_obj[i].size = st.size;
// 当读取的文件个数与所有文件个数相等时
if(count == files.length){
response.end(JSON.stringify(file_obj));
}
})
// console.log(file_obj);
})(i);
// console.log(files[i]);
}
});
} else {
fs.readFile('.' + urls, function (error, html_data) {
response.end(html_data);
})
}
});
修改 ajax代码
var xhr = new XMLHttpRequest();
xhr.onreadystatechange=function(){
if(this.readyState == 4){
var data = JSON.parse(this.responseText);
var htmls = '';
for(var i = 0;i<data.length;i++){
htmls+='<tr><td valign="top">';
if(data[i].type == 'file'){
htmls+= '<img src="./img/layout.gif" alt="[ ]"></td>';
}else{
htmls+= '<img src="./img/folder.gif" alt="[ ]"></td>';
}
htmls+='<td><a href="">';
htmls+= data[i].name+'</a> </td>';
htmls+= '<td align="right">'+ data[i].mtime +'</td>';
htmls+= '<td align="right">'+ data[i].size +'</td><td> </td></tr>';
}
var tb = document.getElementsByTagName('tbody')[0];
tb.innerHTML+=htmls;
}
}
xhr.open('get','/file_list');
xhr.send();
循环后 i 丢失的问题:
// var arr = ['a', 'b', 'c'];
// for (var i = 0; i < arr.length; i++) {
// // 模拟延迟
// setTimeout(function () {
// console.log(arr[i]);
// }, 1000);
// }
/*
* *******************************************
* 上面的代码 全部输出 undefined
* *******************************************
*/
var arr = ['a','b','c'];
for(var i = 0; i < arr.length; i ++) {
(function(i){
// 模拟延迟
setTimeout(function() {
console.log(arr[i]);
}, 1000);
})(i);
}
const path=require('path');
let str='/root/a/b/1.txt';
console.log(path.dirname(str)); 当前文件的上层目录
console.log(path.basename(str)); 返回给定路径的最后一段
console.log(path.extname('index.html')) 返回当前文件扩展名,没则空
//console.log(path.resolve('/root/a/b', '../c', 'build', '..', 'strict'));
//console.log(path.resolve(__dirname, 'build || 文件名'));
给定的路径序列从右到左进行处理,每个后续的 path 前置,直到构造出一个绝对路径。 例如,给定的路径片段序列:/foo、 /bar、 baz,调用 path.resolve('/foo', '/bar', 'baz') 将返回 /bar/baz
const process=require('process'); //process能获取到本机的信息,方便于自动切换开发环境
let mode=(process.env.OS=='Windows_NT'?'dev':'prod');
module.exports={
mode,
...(mode=='dev'?require('./config.dev'):require('./config.prod'))
};
使用第三方包格式化时间
上面的代码,我们使用npm安装了moment来进行格式化时间的处理,这就是使用第三方模块;
而我们使用的npm就是node中自带的包(模块)管理工具;
借助NPM可以帮助我们快速安装和管理依赖包,使Node与第三方模块之间形成了一个良好的生态系统;
我们也可以直接输入npm,查看帮助引导:
PS C:\xamp\htdocs\ceshi\09> npm
Usage: npm
where is one of:
access, adduser, audit, bin, bugs, c, cache, ci, cit,
completion, config, create, ddp, dedupe, deprecate,
dist-tag, docs, doctor, edit, explore, get, help,
help-search, hook, i, init, install, install-test, it, link,
list, ln, login, logout, ls, outdated, owner, pack, ping,
prefix, profile, prune, publish, rb, rebuild, repo, restart,
root, run, run-script, s, se, search, set, shrinkwrap, star,
stars, start, stop, t, team, test, token, tst, un,
uninstall, unpublish, unstar, up, update, v, version, view,
whoami
npm -h quick help on
npm -l display full usage info
npm help search for help on
npm help npm involved overview
Specify configs in the ini-formatted file:
C:\Users\Administrator\.npmrc
or on the command line via: npm --key value
Config info can be viewed via: npm help config
[email protected] C:\Program Files\nodejs\node_modules\npm
一个项目,不可能只是使用一个第三方包,而包越多,管理起来就越麻烦,
而 npm init 给我们提供了项目初始化的功能,也解决了多个包的管理问题:
"name": "usenpm", // 项目名
"version": "1.0.0", // 版本号
"description": "这是我们第一次使用npm", // 描述信息
"main": "index.js", // 入口文件
"scripts": { // npm 设置的一些指令
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [ // 关键字
"第一次"
],
"author": "itheima6期", // 作者
"license": "ISC" // 当前项目的协议
“dependencies” : 依赖模块 -s “devDependncies”: 开发环境 -d npm install axios -s 表示安装到依赖模块
npm 存储包文件的服务器在国外,有时候会被墙,速度很慢,所以我们需要解决这个问题。
http://npm.taobao.org/ 淘宝的开发团队把 npm 在国内做了一个备份。
安装淘宝的 cnpm:
# 在任意目录执行都可以
# --global 表示安装到全局,而非当前目录
# --global 不能省略,否则不管用
npm install --global cnpm
接下来你安装包的时候把之前的 npm
替换成 cnpm
。
举个例子:
# 这里还是走国外的 npm 服务器,速度比较慢
npm install jquery
# 使用 cnpm 就会通过淘宝的服务器来下载 jquery
cnpm install jquery
如果不想安装 cnpm
又想使用淘宝的服务器来下载:
npm install jquery --registry=https://registry.npm.taobao.org
但是每一次手动这样加参数很麻烦,所我们可以把这个选项加入配置文件中:
# 配置到淘宝服务器
npm config set registry https://registry.npm.taobao.org
# 查看 npm 配置信息
npm config list
只要经过了上面命令的配置,则你以后所有的 npm install
都会默认通过淘宝的服务器来下载。
如果后期开发过程中,需要项目迁移,我们只需要将package.json文件迁移即可,在新项目下执行
npm install
,所有第三方包会自动安装;
package.json的作用就是用来记录当前项目及包的使用情况;不能在package.json中添加注释
package-lock.json 保存第三方包的版本和下载路径等详细信息;
当我们使用npm管理包时,package.json 及package-lock.json 的内容都会自动更新
之前的案例中,我们时通过前端浏览器发送ajax请求获取服务器数据的,前端获取数据后进行遍历展示;
缺点就是发送多次请求、不利于搜索引擎查找;我们修改改为后端渲染数据;
art-template: https://www.npmjs.com/package/art-template
var art = require('art-template');
art.defaults.root = './';
var html = art('./art-test.html',{data:[{name:123,age:345},{a:678,b:987}]});
console.log(html);
<body>
<h1>nihoa</h1>
<h2>{{data[0].name}}</h2>
</body>
1:重新创建目录,并初始化项目: npm init
2:将之前写好的后台文件 http.js 和 前台模板页面 apache.html 复制到新项目目录中;
3:安装时间处理模块: npm install moment
4:安装模板引擎模块: npm install art-template
5: 修改 后台文件 http.js 和 前台模板页面 apache.html 文件
http.js :
apache.html :
那么我们在项目中应该使用 客户端渲染还是服务端渲染:
答:两者都用,根据数据的不同作用而定;
推举一个node开发时使用的小工具 nodemon npm install nodemon -g 安装成功后,使用 nodemon 运行代码, 代码一旦被保存,nodemon便会自动重新运行新代码
通过前面几个章节的学习, 我们基本掌握了NodeJS编程的基础知识, 但是我们也直观的发现了一个问题,和我们之前学习浏览器编程时JS, 差异还是很大的; 都是JavaScript编程, 为何有这种差异? 前面写过的防Apache服务器的案例中, 使用过内置fs模块, 使用过 moment 模块, 而这些模块都不是我们写的, 都是直接拿过来使用, 那么我们能不能自己写一个模块, 应该怎么写, 有哪些规矩, 如果我们自己写了一个模块, 能不能提供给其他编程人员直接使用, 应该怎么用?
Electron 跨平台的桌面应用框架: https://electronjs.org/
JS 的表现的表现能力取决于宿主环境提供的API, 在web1.0 时代, W3C 组织提供了浏览器的规范支持, 在web2.0 时代, 随着HTML5的发展, 更多的标准API 出现在了浏览器中, 但是, 在后端 JS 中标准的制定纹丝不动 ;
由 Mozilla 工程师Kevin Dangoor于2009年1月提出名为 ServerJS 的规范; 2009年8月,更名为CommonJS,以显示 API 的更广泛适用性。
What I’m describing here is not a technical problem. It’s a matter of people getting together and making a decision to step forward and start building up something bigger and cooler together. 我在这里描述的不是一个技术问题。这是一个人们聚在一起,决定向前一步,开始一起建立更大更酷的东西的问题。 –Kevin Dangoor
CommonJS对模块的定义十分简单,主要分为:
1、模块引用:
使用 require()
方法引入一个模块API ;
2、模块定义:
在模块中使用 exports 对象导出当前模块数据或方法;
在模块中还存在一个module对象,它代表模块自身,module对象有一个exports 属性,用于数据导出;
其实exports 对象就是module.exports 的引用; exports === module.exports
3、模块标识:
其实就是模块的文件名,必须符合小驼峰法命名规则,使用require()
引入时使用 . 或 ..
开头的相对路径或绝对路径,引入时可以不写文件后缀名;
重点注意 : 模块中的方法和变量的作用于尽在模块内部,每个模块具有独立的空间,互不干扰;
CommonJS 构建的模块机制中的引入与导出是我们完全不用考虑变量污染或者替换的问题,相比与命名空间
的机制,简直就是天才和菜鸟的区别;
以上代码就是自定义模块的基本规则 这是重点
在 CommonJS 规范中,使用 require()
加载(引入) 模块时,模块标识必须使用相对路径或绝对路径指明模块位置,但是在node的实现中,我们可以不指明模块路径;如: require('fs')、require('moment')
;
如果没有指明路径,那就是加载核心模块或第三方模块,指明加载路径一般就是加载自定义模块;
不管加载什么模块,都是优先从缓存中加载:
Node 加载模块时,如果这个模块已经被加载过了,则会直接缓存起来,将来再次引用时不会再次加加载这个模块(即:如果一个模块被加载两次,则模块中的代码只会被执行一次)
而核心模块和第三方模块的的加载顺序就是:
先加载核心模块,核心模块的内容都是在安装node时已经编译好的可执行的二进制代码,加载执行的速度,仅次于缓存加载,如果核心模块中没有,则加载第三方模块
第三方模块的加载规则:
修改 http.js — 服务器模块
var http = require('http');
var server = http.createServer();
var luyou = require('./luyou');
luyou.server(server);
server.listen(8000, function () {
console.log('请访问 127.0.0.1:8000');
})
添加自定义模块 luyou.js – 路由模块
var contrllor = require('./contrllor');
var fs = require('fs');
function server(server) {
server.on('request', function (request, response) {
var urls = request.url;
if (urls == '/') {
var html = contrllor.html;
response.setHeader('Content-Type', 'text/html;charset=utf-8');
response.end(html);
}
else {
fs.readFile('.' + urls, function (error, data) {
response.setHeader('Content-Type', 'text/html;charset=utf-8');
response.end(data);
})
}
// response.end('123');
});
}
exports.server = server;
contrllor.js — 业务模块
var fs = require('fs');
var moment = require('moment');
var template = require('art-template');
template.defaults.root = './';
fs.readdir('./', 'utf8', function (err, files) {
var file_arr = [];
var cont = 0;
for (var i = 0; i < files.length; i++) {
file_arr[i] = {};
// console.log(files[i]);
(function (i) {
fs.stat(files[i], function (err, data) {
cont++;
// 闭包
if (data.isFile()) {
file_arr[i].type = 'file';
} else {
file_arr[i].type = 'dir';
}
file_arr[i].name = files[i];
file_arr[i].size = data.size;
file_arr[i].mtime = moment(data.mtime).format('YYYY-MM-DD hh:mm');
if (cont == files.length) {
var html = template('./apache.html',{data:file_arr});
exports.html = html;
}
})
})(i);
}
})
SELECT `id`, `name` FROM `users` WHERE 1
INSERT INTO `users`(`id`, `name`) VALUES ([value-1],[value-2])
UPDATE `users` SET `name`='value' WHERE id=1
DELETE FROM `users` WHERE id=value
npm install mysql
mysql.js :
var mysql = require('mysql');
var connection = mysql.createConnection({
host: 'localhost',
user: 'root',
password: '',
database: 'onepiece'
});
// 查找操作
var select = 'select * from users';
connection.query(select, function (error, results, fields) {
for(var i=0;i<results.length;i++){
console.log(results[i].name);
}
});
// 添加操作
var ins = "INSERT INTO users (`name`, `nengli`, `jituan`, `img`) VALUES ('娜美', '雷电', '草帽海贼团', '')";
connection.query(ins,function(err,results,field){
console.log(results.insertId)
})
connection.end();
//连接池相比于上面的单次连接,更快速和更好管理,连接池可以设置并发连接数,一旦达到这个数,后续的连接只能等前面的连接执行完才能进行
var Pool = mysql.createPool({
host: 'localhost',
user: 'root',
password: '',
database: 'onepiece'
})
//池选项
//connectionLimit要同时创建的最大连接数。(默认:10)
//queueLimit: 排队最大数 在返回错误之前,池将排队的最大连接请求数getConnection..如果设置为0,对排队的连接请求的数量没有限制。(0不做限制)
//把所有mysql语句变为异步执行 可以使用 async\await 返回Promise对象
const mysql=require('mysql');
const co=require('co-mysql');
const {DB_HOST, DB_PORT, DB_USER, DB_PASS, DB_NAME}=require('../config');
let conn=mysql.createPool({
host: DB_HOST,
port: DB_PORT,
user: DB_USER,
password: DB_PASS,
database: DB_NAME
});
module.exports=co(conn);
// promise
p.query('SELECT 1').then(...).catch(...);
//如:await db.query(`SELECT ID FROM user_table WHERE username='${username}'`);
新建目录 haizei
, 打开命令行执行 npm init
初始化项目;
一次性安装项目所需的所有模块;
npm install art-template mysql bootstrap jquery
http.js
var http=require('http');
// 加载路由模块
var luyou = require('./luyou');
var server = http.createServer();
luyou.bind(server);
server.listen('8080',function(){
console.log('请打开浏览器访问 http://127.0.0.1:8080');
});
luyou.js
var fs = require('fs');
// 引入业务模块使用模板引擎加载页面
var yewu = require('./yewu');
exports.bind = function (server) {
server.on('request', function (request, response) {
var urls = request.url;
if (urls == '/') {
var data = yewu.html_data;
response.end(data);
} else {
fs.readFile('.' + urls, function (error, data) {
response.end(data);
})
}
})
}
yewu.js
var template = require('art-template');
template.defaults.root = './';
var html_data = template('./index.html',{data:123});
exports.html_data = html_data;
yewu.js
var linkdb = require('./linkdb');
var template = require('art-template');
template.defaults.root = './';
console.log(linkdb.data);
var html_data = template('./index.html',{data:linkdb.data});
exports.html_data = html_data;
linkdb.js
var mysql = require('mysql');
var mysql = require('mysql');
var connection = mysql.createConnection({
host: 'localhost',
user: 'root',
password: '',
database: 'onepiece'
});
connection.connect();
var sql = "select * from users ";
connection.query(sql,function(error,data,res){
console.log(data);
exports.data = data
});
connection.end();
通过连接数据查找到的数据,对外导出时,导不出去,引入linkdb的业务模块,接不到数据;
关键:这个问题出现的原因很重;
yewu.js
var linkdb = require('./linkdb');
var template = require('art-template');
template.defaults.root = './';
// 使用linkdb模块导出的方法
linkdb.query(function(data){
// 利用回调函数获取数据
var html_data = template('./index.html',{data:data});
exports.html_data = html_data;
});
linkdb.js
var mysql = require('mysql');
var connection = mysql.createConnection({
host: 'localhost',
user: 'root',
password: '',
database: 'onepiece'
});
connection.connect();
// 将原来导出数据的方式改为导出方法,供模块加载者调用
exports.query = function(callback){
var sql = "select * from users ";
connection.query(sql,function(error,data,res){
// console.log(data);
// 数据是通过回调函数的方式返回
callback(data)
});
connection.end();
}
根据模板引擎语法 修改静态页面
<tbody id="tbody">
{{each data}}
<tr>
<td>{{$value.id}}</td>
<td>{{$value.name}}</td>
<td>{{$value.nengli}}</td>
<td>{{$value.jituan}}</td>
<td>
<a href="#">查看</a>
<a href="#">修改</a>
<a href="#">删除</a>
</td>
</tr>
{{/each}}
</tbody>
修改 luyou.js 路由模块,获取单个用户信息
server.on('request', function (request, response) {
var urls = request.url;
console.log(urls);
if (urls == '/') {
var data = yewu.html_data;
response.end(data);
}else if(urls == '/getuser'){
response.end('getsssss');
}else {
fs.readFile('.' + urls, function (error, data) {
response.end(data);
})
}
})
但是,luyou模块,无法处理前台不同类型的请求, 需要我们在服务器端接受并处理客户端发送的 get 及 post 请求;
GET 请求把所有的内容编码到访问路径中,POST 请求的内容全部都在请求体中。 http.ServerRequest 并没有一个属性内容为请求体,原因是等待请求体传输可能是一件 耗时的工作,譬如上传文件。而很多时候我们可能并不需要理会请求体的内容,恶意的 POST 请求会大大消耗服务器的资源。所以 Node.js 默认是不会解析请求体的,当我们需要的时候, 只能手动来做
网络调试工具Postman,可以帮助我们发送各种HTTP请求,并接受服务器返回的数据; https://www.getpostman.com/
获取请求类型
var http = require('http');
var server = http.createServer();
server.on('request', function(request, response) {
// 获取请求类型
var method = request.method;
console.log(method);
response.end();
});
server.listen(8000, function() {
console.log(' 请访问http://localhost:8000');
});
获取 GET 的请求参数
获取Post请求参数
else if (method == "POST") {
// url_obj = url.parse(request.url,true);
// console.log(url_obj.query);
//以上代码 无内容,失败
// POST请求的内容全部都在请求体中
}
手册中明确说明:
为了支持各种可能的 HTTP 应用,Node.js 的 HTTP API 是非常底层的。 它只涉及流处理与消息解析。 它把一个消息解析成消息头和消息主体,但不解析具体的消息头或消息主体。
因此我们需要查找更底层的网络实现,node中的基础网络模块net模块: http://nodejs.cn/api/net.html:
else if (method == "POST") {
// url_obj = url.parse(request.url,true);
// console.log(url_obj.query);
//以上代码 无内容,失败
// POST请求的内容全部都在请求体中
var data = '';
// net 模块中的 net.Socket 提供的data及end事件
// 绑定data事件获取数据
request.on('data', function (che) {
data += che;
})
// 绑定end事件,监听数据接受完成
request.on('end', function () {
// console.log(data);
console.log(require('querystring').parse(data));
})
}
1: 修改路由逻辑,优先判断请求类型:
var fs = require('fs');
var url = require('url');
// 引入业务模块使用模板引擎加载页面
var yewu = require('./yewu');
exports.bind = function (server) {
server.on('request', function (request, response) {
// 优先判断请求方式
var method = request.method;
// 解析URL参数
var url_obj = url.parse(request.url, true);
if(method == 'GET'){
// console.log(url_obj.query);//参数
if (url_obj.pathname == '/') {
var data = yewu.html_data;
response.end(data);
// 判断 获取单个用户信息路由
}else if(url_obj.pathname == '/getuser'){
}else {
fs.readFile('.' + url_obj.pathname, function (error, data) {
response.end(data);
})
}
}else if(method == 'POST'){
}
})
}
2: 修改静态模板,添加查看连接
<td>
<a href="/getuser?id={{$value.id}}">查看</a>
<a href="#">修改</a>
<a href="#">删除</a>
</td>
3:修改路由,获取get请求参数,并将id参数传入业务模块
// 判断 获取单个用户信息路由
else if(url_obj.pathname == '/getuser'){
// 获取id参数
var id = url_obj.query.id;
// 在yewu模块中封装方法,传递id参数。
// 使用回掉函数获取数据
yewu.gets(url_obj.query.id,function(data){
response.end(data);
});
}
4:修改业务模块,添加gets方法
// 封装gets方法并导出,接受id参数及回调函数
exports.gets = function(id,callback){
// 调用数据库模块的getone方法,并传入id参数
linkdb.getone(id,function(data){
// 利用回调函数获取数据
var user_data = template('./users.html',{data:data});
callback(user_data);
});
}
5:添加users.html静态模板
<body>
{{data[0].name}}
{{data[0].nengli}}
{{data[0].jituan}}
{{data[0].name}}
{{data[0].name}}
</body>
6:修改数据库模块,添加getone方法
exports.getone = function(id,callback){
var sql = "select * from users where id="+id;
// console.log(sql);
connection.query(sql,function(error,data,res){
console.log(data);
// 数据是通过回调函数的方式返回
callback(data)
});
// connection.end();
}
注意:将数据库模块中的所有 connection.end(); 删除,因为我们有多个方法,不能在方法调用中停止数据库的连接,否则,其他方法在后续调用中无法连接数据;
链式操作的核心原理:
test.js
var c = require('./chained1');
// c.select();
c.where('id=2').select();
chained.js
module.exports = {
where: function (wh) {
this.whe = wh;
// 链式操作的核心就是保存数据并返回本对象
return this;
},
select: function () {
if (this.whe == undefined) {
var sql = "select * from users ";
} else {
var sql = "select * from users where " + this.whe;
}
console.log(sql);
this.whe = undefined;
},
update:function(){
if (this.whe == undefined) {
console.log('更新数据时,请填写where条件');
return;
} else {
var sql = "update xx from set () where " + this.whe;
}
console.log(sql);
this.whe = undefined;
}
}
1:新建数据操作模块 db.js
var mysql = require('mysql');
var connection = mysql.createConnection({
host: 'localhost',
user: 'root',
password: '',
database: 'onepiece'
});
module.exports = {
that: this,
where: function (wh) {
this.whe = wh;
return this;
},
select: function (callback) {
if (this.whe == undefined) {
var sql = "select * from users ";
} else {
var sql = "select * from users where " + this.whe;
}
// 用完后重置where条件,以免后续操作的数据重复
this.whe = undefined;
connection.query(sql, function (error, results, fields) {
if (error) {
callback(error);
return;
}
callback(results);
});
}
}
2:修改业务模块(yewu.js)的调用
// var linkdb = require('./linkdb');
var db = require('./db');
var template = require('art-template');
template.defaults.root = './';
module.exports = {
getall: function (callback) {
db.select(function (data) {
// 利用回调函数获取数据
var html_data = template('./index.html', { data: data });
callback(html_data);
});
},
getone:function(id,callback){
db.where('id='+id).select(function(data){
var user_data = template('./users.html',{data:data});
callback(user_data);
});
}
}
3:修改路由模块(luyou.js)的调用
var fs = require('fs');
var url = require('url');
// 引入业务模块使用模板引擎加载页面
var yewu = require('./yewu');
exports.bind = function (server) {
server.on('request', function (request, response) {
// 优先判断请求方式
var method = request.method;
// 解析URL参数
var url_obj = url.parse(request.url, true);
if(method == 'GET'){
// console.log(url_obj.query);//参数
if (url_obj.pathname == '/') {
yewu.getall(function(data){
response.end(data);
})
// 判断 获取单个用户信息路由
}else if(url_obj.pathname == '/getuser'){
// 获取id参数
var id = url_obj.query.id;
// 在yewu模块中封装方法,传递id参数。
// 使用回掉函数获取数据
yewu.getone(url_obj.query.id,function(data){
response.end(data);
});
}else {
fs.readFile('.' + url_obj.pathname, function (error, data) {
response.end(data);
})
}
}else if(method == 'POST'){
}
})
}
<tbody id="tbody">
{{each data}}
<tr>
<td>{{$value.id}}</td>
<td>{{$value.name}}</td>
<td>{{$value.nengli}}</td>
<td>{{$value.jituan}}</td>
<td>
<a href="/getuser?id={{$value.id}}">查看</a>
<a href="/upuser?id={{$value.id}}">修改</a>
<a href="#">删除</a>
</td>
</tr>
{{/each}}
</tbody>
1:添加路由判断(luyou.js)
// 修改用户信息,先获取用户信息
}else if(url_obj.pathname == '/upuser'){
// 获取id参数
var id = url_obj.query.id;
// 在yewu模块中封装方法,传递id参数。
// 使用回掉函数获取数据
yewu.upuser_get(url_obj.query.id,function(data){
response.end(data);
});
}else {
2:添加业务逻辑(yewu.js),获取用户信息
upuser_get:function(id,callback){
db.where('id='+id).select(function(data){
var user_data = template('./upuser.html',{data:data});
callback(user_data);
});
}
3:添加表单模板(upuser.html)
<style>
table {
margin: 0 auto;
border-collapse: collapse;
width: 800px;
height: 500px;
}
td {
border: 1px solid #ccc;
}
</style>
<body>
<form action="/upuser?id={{data[0].id}}" method="post">
<table>
<tr>
<td>姓名</td>
<td><input name="name" type="text" value="{{data[0].name}}"></td>
</tr>
<tr>
<td>能力</td>
<td><input name="nengli" type="text" value="{{data[0].nengli}}"></td>
</tr>
<tr>
<td>团体</td>
<td><input name="tuanti" type="text" value="{{data[0].jituan}}"></td>
</tr>
<tr>
<td>上传图片</td>
<td><input type="file"></td>
</tr>
<tr>
<td></td>
<td><input type="submit" value="修改"></td>
</tr>
</table>
</form>
</body>
路由模块(luyou.js)
else if (method == 'POST') {
// 只要是POST请求,则优先获取数据
// 后处理路由逻辑
var data = '';
request.on('data', function (che) {
data += che;
})
// 绑定end事件,监听数据接受完成
request.on('end', function () {
// console.log(data);
// 获取Post传输的数据
var post_data = querystring.parse(data);
// 路由判断
if (url_obj.pathname == '/upuser') {
// 调用业务层方法
yewu.upuser_post(url_obj.query.id, post_data, function (data) {
response.end(data);
});
}
})
}
业务模块(yewu.js)
upuser_post:function(id,data,callback){
// 调用数据模块修改用户信息
db.where('id='+id).update(data,function(changedRows){
// http服务器相应要求必须是字符串
callback(changedRows.toString());
})
}
数据库模块(db.js)
update:function(data,callback){
// 没where条件,则停止所有操作
if(this.where == undefined){
callback('error');
return;
}
// 拼接sql语句的set部分
var set = '';
for(v in data){
set += v +"='"+ data[v]+"',";
}
var sets = set.slice(0,set.length-1);
var sql = "UPDATE `users` SET "+sets + ' where '+ this.whe;
// console.log(sql);
this.whe = undefined;
connection.query(sql,function(error,res,fields){
// console.log(error)
// 返回受影响行数
callback(res.changedRows);
})
}
作业: 自己完成添加及删除操作
Express 是基于 Node.js 平台,快速、开放、极简的 Web 开发框架, 提供一系列强大特性帮助你创建各种Web应用。Express 不对 node.js 已有的特性进行二次抽象,我们只是在它之上扩展了Web应用所需的功能。丰富的HTTP工具以及来自Connect框架的中间件随取随用,创建强健、友好的API变得快速又简单
就像一个普通的第三方模块一样安装即可;
npm init
npm install express
var express = require('express');
var app = express();
app.get('/',(req,res)=>{
res.send('hello world !');
})
app.listen('8000',()=>{
console.log('127.0.0.1:8000')
})
其中 Request、Response – API 我们需要重点关注
将我们之前的海贼王项目使用express框架进行重写,重写过程中,学习框架提供的各种API,并完善项目功能;
创建http.js
var express = require('express');
var app = express();
app.listen('8000',()=>{
console.log('127.0.0.1:8000')
})
之前我们写了一个独立的模块(luyou.js)来处理请求,而在 express 中已经帮我们写好了路由的请求处理规则,不需要我们进行判断;
路由 是指确定应用程序如何响应对特定端点的客户端请求,该请求是URI(或路径)和特定HTTP请求方法(GET,POST等)。
每个路由都可以有一个或多个处理函数,这些函数在路由匹配时执行。
路径定义采用以下结构:
app.method(path, handler)
以下示例定义了简单路由。
Hello World!
在主页上回复:
app.get('/', function (req, res) {
res.send('Hello World!')
})
在根路由(/
),应用程序的主页上响应POST请求:
app.post('/', function (req, res) {
res.send('Got a POST request')
})
响应对/user
路由的PUT请求:
app.put('/user', function (req, res) {
res.send('Got a PUT request at /user')
})
响应对/user
路由的DELETE请求:
app.delete('/user', function (req, res) {
res.send('Got a DELETE request at /user')
})
设置 外置路由 rout.js
var express = require('express');
var router = express.Router();
router.get('/',(req,res)=>{
res.send('123');
})
router.get('/user',(req,res)=>{
res.send('user');
})
router.post('/edit',(req,res)=>{
res.send('post_edit');
})
// 导出 router
module.exports = router;
写好路由规则,一定要记得将 路由对象(router) 导出
var express = require('express');
var app = express();
// 引入外置路由
var rout = require('./rout');
app.use(rout); // 使用引入外置的路由
app.listen('8000',()=>{
console.log('127.0.0.1:8000')
})
将外置路由引入后,使用 app.use() 进行加载使用;
在 luyou.js 中,注释以前的代码,添加新代码
var express = require('express');
var yewu = require('./yewu');
var router = express.Router();
router.get('/', (req, res) => {
yewu.getall(function (data) {
res.end(data);
})
})
module.exports = router;
在 http.js 中,使用 express 启动服务,并引入使用新修改的 luyou.js 模块
var express = require('express');
var app = express();
var luyou = require('./luyou');
app.use(luyou);
app.listen('8080',()=>{
console.log('127.0.0.1:8080')
})
luyou.js
var express = require('express');
var yewu = require('./yewu');
var router = express.Router();
// express的路由支持链式操作
router
.get('/', (req, res) => {
yewu.getall(function (data) {
res.end(data);
})
})
.get('/getuser', (req, res) => {
// req 提供了query属性获取请求参数
var id = req.query.id;
// 在yewu模块中封装方法,传递id参数。
// 使用回掉函数获取数据
yewu.getone(id, function (data) {
res.end(data);
});
})
module.exports = router;
路由模块 (luyou.js) 中只负责调用,调用时,将req res 传入业务模块
router
.get('/', (req, res) => {
yewu.getall(req,res);
// yewu.getall(function (data) {
// res.end(data);
// })
})
.get('/getuser', (req, res) => {
yewu.getone(req,res);
// req 提供了query属性获取请求参数
// var id = req.query.id;
// // 在yewu模块中封装方法,传递id参数。
// // 使用回掉函数获取数据
// yewu.getone(id, function (data) {
// res.end(data);
// });
})
业务模块接受 req res 负责处理请求并响应数据
getall: function (req,res) {
db.select(function (data) {
// 利用回调函数获取数据
var html_data = template('./index.html', { data: data });
// console.log(html_data);
res.end(html_data);
});
},
getone:function(req,res){
db.where('id='+req.query.id).select(function(data){
var user_data = template('./users.html',{data:data});
res.end(user_data);
});
},
继续简化路由模块
router
.get('/',yewu.getall)
.get('/getuser',yewu.getone)
原理:
function fn(callback){
var a = 1;
var b = 2;
callback(a,b);
}
var pros = function(a,b){
console.log(a+b);
}
fn(function(a,b){
pros(a,b);
})
// fn(pros);
安装:
npm install --save art-template
npm install --save express-art-template
官方示例:
var express = require('express');
var app = express();
app.engine('art', require('express-art-template'));
app.set('view options', {
debug: process.env.NODE_ENV !== 'production'
});
app.get('/', function (req, res) {
res.render('index.art', {
user: {
name: 'aui',
tags: ['art', 'template', 'nodejs']
}
});
});
修改 http.js 将 express-art-template 注册为express框架的模板引擎,并设置模板后缀为 html
在项目中新建views目录,将所有静态页面放入views目录
http://www.expressjs.com.cn/starter/static-files.html
在项目中新建 public 文件夹并将bootstrap.css移入, 修改 index.html 加载 css 静态文件 ,在http.js中引入并设置静态资源加载路径:
如果要使用多个静态资源目录,请多次调用 express.static
函数:
app.use(express.static('public'))
app.use(express.static('files'))
访问静态资源文件时,express.static
函数会根据目录的添加顺序查找所需的文件。
修改所有路由及业务模块代码
luyou.js
var express = require('express');
var yewu = require('./yewu');
var router = express.Router();
router
.get('/',yewu.getall)
.get('/getuser',yewu.getone)
.get('/upuser',yewu.upuser_get)
.post('/upuser',yewu.upuser_post);
module.exports = router;
yewu.js
// var linkdb = require('./linkdb');
var db = require('./db');
var querystring = require('querystring');
// template.defaults.root = './';
module.exports = {
getall: function (req, res) {
db.select(function (data) {
res.render('index.html', { data: data });
});
},
getone: function (req, res) {
db.where('id=' + req.query.id).select(function (data) {
res.render('./users.html', { data: data });
});
},
upuser_get: function (req, res) {
db.where('id=' + req.query.id).select(function (data) {
res.render('./upuser.html', { data: data });
});
},
upuser_post: function (req, res) {
var data = '';
req.on('data', function (che) {
data += che;
})
// 绑定end事件,监听数据接受完成
req.on('end', function () {
// console.log(data);
// 获取Post传输的数据
var post_data = querystring.parse(data);
// 调用数据模块修改用户信息
db.where('id=' + req.query.id).update(post_data, function (changedRows) {
// http服务器相应要求必须是字符串
res.json(changedRows);
})
})
}
}
创建服务器:
var express = require('express');
var app = express();
app.post('/upfile',(req,res)=>{
var data = '';
req.on('data', function (che) {
data += che;
});
req.on('end',()=>{
// 直接打印post传过来的数据
console.log(data);
})
})
app.listen('8000',()=>{
console.log('127.0.0.1:8000')
})
使用postman工具上传文件:
https://www.npmjs.com/package/formidable
安装模块 npm install formidable
var express = require('express');
var formidable = require('formidable');
var fs = require('fs');
var app = express();
app.post('/upfile', (req, res) => {
var form = new formidable.IncomingForm();
// form.uploadDir = './img'; // 设置上传路径
// form.keepExtensions = true; // 保留文件扩展名
form.parse(req, function (err, fields, files) {
var times = new Date().getTime();
// 组装上传路径
var file_path = './img/'+times+files.imgs.name;
// 将缓存文件移动至制定目录
fs.rename(files.imgs.path,file_path,(err)=>{
console.log(file_path);
});
res.end();
});
})
app.listen('8000', () => {
console.log('127.0.0.1:8000')
})
修改 upuser.html
修改业务模块 yewu.js 使用 formidable 获取 post 数据,实现文件上传
upuser_post: function (req, res) {
var form = new formidable.IncomingForm();
form.parse(req, function (err, fields, files) {
var times = new Date().getTime();
// 组装上传路径
var file_path = './public/img/' + times + files.imgs.name;
// 将缓存文件移动至指定的public目录
fs.rename(files.imgs.path, file_path, (err) => {
if(!err){
// 因为设置静态资源时,已经时public文件夹,写入数据库时,不要加public
fields.img = './img/' + times + files.imgs.name;
db.where('id=' + req.query.id).update(fields, function (changedRows) {
// http服务器相应要求必须是字符串
res.json(changedRows);
})
}else{
console.log('文件上传失败'+err);
}
});
});
}
express官方资源中,为我们提供了一个中间件,cookie-session
npm install cookie-session
测试代码:
var express = require('express');
var cookieSession = require('cookie-session')
var app = express();
// 注册中间件
app.use(cookieSession({
name: 'session', // 客户端cookie的名称
keys: ['ss'] // 用于加密的关键字
}))
app.get('/', (req, res) => {
// 获取并判断session
if(req.session.sess_data){
res.send('已经登陆')
}else{
// 如果没有session,跳转到登陆页面
res.send('<script>alert("没登陆");window.location.href="/up"</script>');
}
})
app.get('/up', (req, res) => {
// 展示登陆页面,获取用户数据并写入session
req.session.sess_data = {name:12,age:89};
res.send('已写入session');
});
修改http模块,注册 cookie-session 中间件;
var express = require('express');
var app = express();
var cookieSession = require('cookie-session');
// 注册中间件
app.use(cookieSession({
name: 'session', // 客户端cookie的名称
keys: ['xilingzuishuai'] // 用于加密的关键字
}))
在业务模块 (yewu.js) 中 添加逻辑判断,只有登陆后才能展示首页:
getall: function (req, res) {
// 获取并判断session
if (req.session.sess_data) {
db.select(function (data) {
res.render('index.html', { data: data });
});
} else {
// 如果没有session,跳转到登陆页面
res.send('<script>alert("没登陆");window.location.href="/upload"</script>');
// res.send('没登陆');
}
},
在路由模块(luyou.js) 中添加以下两个路由,get 展示静态登陆页面,post 获取用户提交的数据并写入 session ,写入成功后,跳转到首页;在业务模块(yewu.js)中添加响应的方法
.get('/upload',yewu.upload_get)
.post('/upload',yewu.upload_post)
upload_get: (req, res) => {
// 展示登陆页面
res.render('./upload.html', {});
},
upload_post: (req, res) => {
var form = new formidable.IncomingForm();
form.parse(req, function (err, fields, files) {
// console.log(fields);
// 获取用户提交数据,判断用户名密码是否正确
if (fields.userName == "admin" && fields.pwd == "123") {
// 数据正确,写入session
req.session.sess_data = fields;
res.send('<script>alert("登陆成功");window.location.href="/"</script>');
}else{
// 数据错误,重新跳回登陆页面
res.send('<script>alert("登陆失败");window.location.href="/upload"</script>');
}
})
}
在一个整体的流程中的某个环节,因为某些原因加入了额外的处理环节;
语法:
app.use() 的用法
var express = require('express');
var app = express();
// 在中间件之前,不受中间件影响
app.get('/',function(req,res){
console.log(123);
})
// 应用中间件
// 请求 '/user' 时,会先调用中间件
app.use(function (req, res, next) {
console.log(req);
next();
});
// 调用之前先调用中间件
app.get('/user',function(req,res){
console.log('user');
})
app.listen('8000', () => {
console.log('127.0.0.1:8000')
})
app.method() 的用法
var express = require('express');
var app = express();
// 在中间件之前,不受中间件影响
app.get('/',function(req,res){
console.log(123);
})
// 应用中间件
// 只有在 post 请求user 时才起作用
app.post('/user',function (req, res, next) {
console.log(req);
next();
});
// 调用之前先调用中间件
// 接受所有请求方式请求user
app.all('/user',function(req,res){
console.log('user');
})
app.listen('8000', () => {
console.log('127.0.0.1:8000')
})
路由器层中间件的工作方式与应用层中间件基本相同,差异之处在于它绑定到 express.Router()
的实例。
使用 router.use()
和 router.METHOD()
函数装入路由器层中间件;
我们之前项目的代码,就是在使用路由中间件:
var router = express.Router();
router
.get('/',yewu.getall)
.get('/getuser',yewu.getone)
.get('/upuser',yewu.upuser_get)
.post('/upuser',yewu.upuser_post)
.get('/upload',yewu.upload_get)
.post('/upload',yewu.upload_post)
;
除 express.static
外,先前 Express 随附的所有中间件函数现在以单独模块的形式提供:中间件函数的列表
Express 中唯一内置的中间件函数是 express.static
。此函数基于 serve-static,负责提供 Express 应用程序的静态资源。
对于每个应用程序,可以有多个静态目录:
app.use(express.static('public'));
app.use(express.static('uploads'));
app.use(express.static('files'));
使用第三方中间件向 Express 应用程序添加功能。
安装具有所需功能的 Node.js 模块,然后在应用层或路由器层的应用程序中将其加装入。
var cookieSession = require('cookie-session');
// 注册中间件
app.use(cookieSession({
name: 'session', // 客户端cookie的名称
keys: ['xilingzuishuai'] // 用于加密的关键字
}))
Koa框架是express原帮人马打造,小且精干,express有的Koa都有,甚至比express还更有优势,如:在express中回调是必不可少的,语法大多停止在ES5版本,而Koa则使用generator、async等新特性解决了回调套回调,语法上也紧跟ECMAScript版本,是当下流行的框架
1.安装 koa框架
npm init
npm install koa
提示:在express中路由自带,koa中没有自带路由,需自行下载(koa-router)
const koa=require('koa'); //加载koa
const Router=require('koa-router'); //加载koa路由框架
let server=new koa();
server.listen(8080);
//在koa中可以嵌套路由
let router = new Router(), //最顶级
UserRouter = new Router(), //用户路由
//用户路由中子路由
HomeRouter = new Router(),
AdminRouter = new Router();
//路由函数有两个参数 一个是ctx上下文(什么都有) next中间函数(可以省略)
//函数前都加async,使用next也同样要加await
HomeRouter.get('/a',async(ctx)=>{
console.log('前台用户')
ctx.body = '前台用户';
})
AdminRouter.get('/a',async(ctx)=>{
console.log('后台用户');
ctx.body = '后台用户';
})
UserRouter.get('/',async(ctx,next)=>{
console.log('用户模块')
ctx.body = 'User';
})
//用户路由添加两个子路由
UserRouter.use('/home',HomeRouter.routes())
UserRouter.use('/admin',AdminRouter.routes())
//最顶级的路由添加用户路由作为子路由
router.use('/user',UserRouter.routes());
//最后添加到服务器上
server.use(router.routes());
/*
当前嵌套路由层级
router=>
UserRouter => HomeRouter
AdminRouter
*/
//上面是把路由写在一个文件,正常情况是分开写然后引入
1.服务器页面(总页面)
2.路由文件:router/user/index.js
3.路由目录
4.子路由页面
4.1 admin
4.2 company
提示:可以在总页面上添加和user同级的多个路由
koa路由传参新方式(也可以使用旧方式)
const Koa=require('koa');
const Router=require('koa-router');
let server=new Koa();
server.listen(8080);
let router=new Router();
//路由传参 :字段 想传多少传多少
//也可以 ?字段=值方式传值,只不过获取方式不同
router.get('/news/:id/', async (ctx, next)=>{
let {id}=ctx.params; // :字段方式传参的数据在ctx.params中
//启动后,访问/news/34 id就是34
ctx.body='新闻id='+id;
await next();
});
router.get('/news/1/', async ctx=>{
let {id}=ctx.params;
ctx.body+='aaa';
});
//当定义的函数像以上情况有点混淆的时候,谁在前,谁先执行,和express一样从上往下执行
server.use(router.routes());
/*
用?id=xxx传参和/:id有什么区别?倾向于用/:id吗?
urlencoded http://aaa.com/user?a=12&b=5
params http://aaa.com/user/12/5
urlencoded params
顺序灵活 死的
可省略 死的
不利于SEO 利于SEO
*/
ctx.params //获取路由 :字段方式的数据
ctx.query //获取路由 ?字段 = 值 方式的数据
ctx.method //请求方式
ctx.url//请求地址url
--------------------------------------------------------------------------------
server.context:// 相当于ctx的prototype
server.coconst Koa=require('koa');
const Router=require('koa-router');
let server=new Koa();
server.listen(8080);
server.context.a=12; //在ctx原型添加属性
let router=new Router();
router.get('/news/', async ctx=>{
let {id}=ctx.query;
console.log(ctx.query);
ctx.body='bbb'+ctx.a; //实例里获取
});
server.use(router.routes());
--------------------------------------------------------------------------------
ctx.request //请求
ctx.response//响应
--------------------------------------------------------------------------------
ctx.path //路径
ctx.ip // 客户端的IP
ctx.headers //请求头
--------------------------------------------------------------------------------
ctx.throw(code, msg)
ctx.assert(条件, code, msg)
ctx.throw(400, 'username is required');
ctx.assert(ctx.query.pass, 400, 'password is required');
//一旦触发,会把错误信息响应到客户端
--------------------------------------------------------------------------------
ctx.state=305; //状态码
ctx.redirect(); //重定向
ctx.attachment(); //下载文件
koa-static 需自行下载,不自带
const Koa=require('koa');
const Router=require('koa-router');
const static=require('koa-static');
let server=new Koa();
server.listen(8080);
let router=new Router();
router.get('/user', async ctx=>{
});
server.use(router.routes());
//可以配合koa-router使用
let staticRouter=new Router();
staticRouter.all(/(\.jpg|\.png|\.gif)$/i, static('./static', {
maxage: 60*86400*1000 //静态文件缓存时间 这里2个月
}));
staticRouter.all(/(\.css)$/i, static('./static', {
maxage: 1*86400*1000
}));
staticRouter.all(/(\.html|\.htm|\.shtml)$/i, static('./static', {
maxage: 20*86400*1000
}));
staticRouter.all('', static('./static', {
maxage: 30*86400*1000
}));
//加载static中间件
server.use(staticRouter.routes());
//类似express的方式
server.use(static('public'))
//指定文件
server.use(static({
index:'index.html'
}))
const Koa=require('koa');
const Router=require('koa-router');
const body=require('koa-better-body');
let server=new Koa();
server.listen(8080);
server.use(body({
uploadDir: './static/upload'
}));
server.use(async ctx=>{
//文件和post数据
console.log(ctx.request.fields);
ctx.body='aaa';
});
//在koa中cookie自带,session则没有,需要下载一个叫:koa-session模块
//----------cookie 注意:如果在设置cookie的时候设置了签名,获取的时候也要开签名 (signed) 否则被串改,服务器获取的数据将会是被串改的
server.keys = ['sadasdasdasdasdvvvvvvvvvsadasdasd'];//设置cookie签名的循环密匙
//设置和获取cookie在ctx上下文对象中
server.use( async (ctx)=>
ctx.cookies.set('user','xuyuxin',{
signed:true //开启签名
//cookie中可以设置的属性,在这就可以设置如:domain、maxAge
}
})
console.log(ctx.cookies.get('user', {signed: true}));
//---------------------session
const Koa=require('koa');
const Router=require('koa-router');
const session=require('koa-session');
let server=new Koa();
server.listen(8080);
server.keys=[ //跟cookie相同
'asdfasdfasdfasdfasdf',
'hghjfgjghjkyggfytyurt',
'hjghjkfguig8ygyi8t78i8',
];
server.use(session({
maxAge: 20*60*1000, //有效期
renew: true //自动续期
}, server));
server.use(async ctx=>{
if(!ctx.session['view']){
ctx.session['view']=0;
}
ctx.session['view']++;
ctx.body=`欢迎你第${ctx.session.view}次来访`;
});
//一般在koa中,会把常用的模块或中间件放在 ctx.prototype(server.context)中,以便在任何地方使用
---------database.js
const mysql=require('mysql');
const co=require('co-mysql');
let conn=mysql.createPool({
host: 'localhost',
user: 'root',
password: '',
database: '20181101'
});
let db=co(conn);
module.exports=db;
----------引入
const Koa=require('koa');
const Router=require('koa-router');
let server=new Koa();
server.listen(8080);
server.context.db=require('./libs/database');
//引入后,就可在ctx中使用
server.get('/',async ctx=>{
let data = await ctx.db.query('SELECT * FROM tbale');
ctx.body = JSON.stringify(data);
})
const Koa=require('koa');
const Router=require('koa-router');
let server=new Koa();
server.listen(8080);
//以中间件方式处理
server.use(async (ctx, next)=>{
try{
await next();
}catch(e){
ctx.body='错了';
}
});
//也可以用Router方式处理 * 代表全部,只要请求就会触发
let router=new Router();
router.all('*', async ctx=>{
try{
await next();
}catch(e){
ctx.body='错了-router';
}
});
router.get('/a', async ctx=>{
ctx.body=div.title;
});
server.use(router.routes());
<% if (user) { %>
<%= user.name %>
<% } %>
用法
var template = ejs.compile(str, options);
template(data);
// => 输出绘制后的 HTML 字符串
ejs.render(str, data, options);
// => 输出绘制后的 HTML 字符串
ejs.renderFile(filename, data, options, function(err, str){
// str => 输出绘制后的 HTML 字符串
});
你可能需要能够输出原始内容的标签 (<%-) 用于 include 指令,避免对输出的 HTML 代码做转义处理。
<ul>
<% users.forEach(function(user){ %>
<%- include('user/show', {user: user}); %>
<% }); %>
</ul>
自定义分隔符
可针对单个模板或全局使用自定义分隔符:
var ejs = require('ejs'),
users = ['geddy', 'neil', 'alex'];
// 单个模板文件
ejs.render('<?- users.join(" | "); ?>', {users: users},{delimiter: '?'});
// => 'geddy | neil | alex'
// 全局
ejs.delimiter = '?';
ejs.render('<?- users.join(" | ");?>', {users: users});
// => 'geddy | neil | alex'
//语法大多跟js一样
const Koa=require('koa');
const ejs=require('koa-ejs');
const path=require('path');
let server=new Koa();
server.listen(8080);
//语法
ejs(server, {
root: path.resolve(__dirname, 'template'), //根路径 文件夹名称
layout: false, //模板在根目录中是否要再封装一个文件夹, 是的话写对应的文件名,否则放在根目录指定位置 默认是 true 自动在根目录加上一层文件夹
viewExt: 'ejs', //模板后缀
cache: false, //是否缓存
debug: false // 是否将渲染好文件展示
});
npm i forever -g npm安装
//语法
forever start xxx.js 开启
forever restart xxx.js 重启
forever stop xxx.js 停止
forever stopall 停止全部
forever start xxx.js -l c:/a.log -e c:/err.log -a
const express=require('express');
const body=require('body-parser');
let server=express();
server.listen(8080);
//在express中作为中间件使用
server.use(body.urlencoded({
extended: false
}));
//最后在req的body中
server.post('/reg', (req, res)=>{
console.log(req.body);
});
//注意,此模块不能处理上传文件请求
//用原生node封装一个类似body-parser的简易版
const express=require('express');
let server=express();
server.listen(8080);
const querystring=require('querystring');
//跟以上相同
server.use((req, res, next)=>{
let arr=[];
req.on('data', buffer=>{
arr.push(buffer);
});
req.on('end', ()=>{
let post=querystring.parse(Buffer.concat(arr).toString());
req.body=post;
next();
});
});
server.post('/reg', (req, res)=>{
console.log(req.body);
});
//把以上的中间件函数封装
const express=require('express');
const body=require('./libs/body-parser');
let server=express();
server.listen(8080);
server.use(body.urlencoded());
server.post('/reg', (req, res)=>{
console.log(req.body);
});
//即可自己实现一个简易的中间件
const http=require('http');
const multiparty=require('multiparty');
http.createServer((req, res)=>{
let form=new multiparty.Form({
uploadDir: './upload'
});
form.parse(req);
form.on('field', (name, value)=>{
console.log('字段:', name, value);
});
form.on('file', (name, file)=>{
console.log('文件:', name, file);
});
form.on('close', ()=>{
console.log('表单解析完成');
});
}).listen(8080);
//请求到响应,中间如果出错,就算文件已经上传成功也会被删除
const express=require('express');
const multer=require('multer');
let server=express();
server.listen(8080);
//作为中间件使用
let obj=multer({dest: './static/upload'});
server.use(obj.any());
//最后处理完的数据存在req的files中
server.post('/reg', (req, res)=>{
console.log(req.files);
res.send('upload successed');
});
const express=require('express');
const cookieParser=require('cookie-parser');
let server=express();
server.listen(8080);
//签名加密
server.use(cookieParser(
'fasdgfhsrtyredfbfd56te5645sdter76tytutyi456ythgfgerrhdfghfdg'
));
server.get('/a', (req, res)=>{
console.log('cookie:', req.cookies); //未签名的
console.log('signed:', req.signedCookies); //签名的
res.cookie('amount', 99.8, {
//httpOnly: true, 是否只能在服务端使用
maxAge: 14*86400*1000,
//secure: true, 只有https
signed: true //是否启用签名
});
res.send('ok');
});
//注意:此模块不能使用require('uuid')直接引入,uuid有很多个版本,因此直接引入会出错,需要指定对应版本引入 require('uuid/[v1|v3|v4|v5]')
版本1(时间戳):
const uuidv1 = require(' uuid / v1 ');
uuidv1(); // ⇨'2c5ea4c0-4067-11e9-8bad-9b1deb4d3b7d'
版本3(命名空间):
const uuidv3 = require(' uuid / v3 ');
// ...使用预定义的DNS命名空间(域名)
uuidv3( ' hello.example.com ', uuidv3。 DNS); // ⇨'9125a8dc-52ee-365b-a5aa-81b0b3681cf6'
// ...使用预定义的URL命名空间(,好了,URL)的
uuidv3( ' http://example.com/hello ', uuidv3。网址); // ⇨'c6235813-3ba4-3801-ae84-e0a6ebb7d138'
// ...使用自定义名称空间
// //
//注意:自定义名称空间应为特定于您的应用程序的UUID字符串!
//例如,这里的一个是使用uuid CLI模块生成的。
const MY_NAMESPACE = ' 1b671a64-40d5-491e-99b0-da01ff1f3341 ' ;
uuidv3( ' Hello,World!', MY_NAMESPACE); // ⇨'e8b5a51d-11c8-3310-a6ab-367563f20686'
版本4(随机):
const uuidv4 = require(' uuid / v4 ');
uuidv4(); // ⇨'1b9d6bcd-bbfd-4b2d-9b5d-ab8dfbbd4bed'
版本5(命名空间):
const uuidv5 = require(' uuid / v5 ');
// ...使用预定义的DNS命名空间(域名)
uuidv5( ' hello.example.com ', uuidv5。 DNS); // ⇨'fdda765f-fc57-5604-a269-52a7df8164ec'
// ...使用预定义的URL命名空间(,好了,URL)的
uuidv5( ' http://example.com/hello ', uuidv5。网址); // ⇨'3bbcee75-cecc-5b56-8031-b6641c1ed1f1'
// ...使用自定义名称空间
// //
//注意:自定义名称空间应为特定于您的应用程序的UUID字符串!
//例如,这里的一个是使用uuid CLI模块生成的。
const MY_NAMESPACE = ' 1b671a64-40d5-491e-99b0-da01ff1f3341 ' ;
uuidv5( ' Hello,World!', MY_NAMESPACE); // ⇨'630eb68f-e0fa-5ecc-887a-7c7a62614681'
const jwt = require('jsonwebtoken');
const Token = {
encrypt:function(data,time){ //data加密数据,time过期时间
return jwt.sign(data, 'token', {expiresIn:time})
},
decrypt:function(token){
try {
let data = jwt.verify(token, 'token');
return {
token:true,
id:data.id
};
} catch (e) {
return {
token:false,
data:e
}
}
}
}
module.exports = Token;
//1. 首先安装jsonwebtoken
npm install jsonwebtoken
//2. 引入jsonwebtoken
const jwt = require('jsonwebtoken');
//3. encrypt加密函数
//jsonwebtoken提供了一个函数sign用于加密生成jwt,格式jwt.sign(data,str,options)
//参数data 表示要加密的数据
//参数str 自定义字符串,这个字符串在解密时需要用到,在这里我随便写了一个‘token’。这相当于一个密钥secret,服务器端需要妥善保管。
//参数options 其他内容,可以设置令牌有效时间{expiresIn:time}。time的取值,'15d'表示15天,'2h'表示2小时,……
//4.decrypt解密函数
//jsonwebtoken提供了一个函数verify用于解密jwt,格式jwt.verify(token,str)
//参数token 表示需要解密的令牌
//参数str 表示加密时用到的自定义字符串,即密钥
//5.用法
const Token = require('../utils/token')
//code……
const token = Token.encrypt({id:user.id},'15d'); //将user.id加密,设置有效期15天,返回token
//code……
//解密
let data = Token.decrypt(ctx.header.authorization); //将请求头的token取出解密
if (data.token) {
//有效token
}else{
//无效token
}
参考资源相关列表: https://nodejs.org/zh-cn/ node.js官网 http://nodejs.cn/ node.js中文网 《深入浅出Node.js》 朴灵著 ,人民邮电出版社 https://en.wikipedia.org/wiki/CommonJS 维基百科 《ECMAScript 6 入门》(第三版) 阮一峰著 ,电子工业出版社 《你不知道的JavaScript》(上、中、下卷) [美] Kyle Simpson 著 ,人民邮电出版社 http://www.expressjs.com.cn/ express中文网