初识---后端渲染1
const http = require('http');
const fs = require('fs');
//node会把json格式的数据自动转成对象
//'./mime.json'该文件中存放的是MIME类型
const mime = require('./mime.json');
const quotes = [
'哈哈',
'嘻嘻',
'呵呵'
];
const server = http.createServer((req, res) => {
let url = req.url;
if (url.startsWith('/public')) {
let content = fs.readFileSync('.' + url);
// url中最后一个 . 出现的位置
let lastPoint = url.lastIndexOf('.');
// 获取当前url中表示的文件后缀
let suffix = url.substring(lastPoint);
// console.log('suffix', suffix);
// 根据后最从 mime 中获取对应 MIME 类型
res.setHeader('content-type', mime[suffix] + ';charset="utf-8"');
res.end(content);
}
if (url.startsWith('/quote')) {
res.setHeader('content-type', 'text/html;charset="utf-8"');
let quote = quotes.sort(() => {
return Math.random() - .5;
})[0];
res.end(quote);
}
if (url.startsWith('/all')) {
res.setHeader('content-type', 'text/html;charset="utf-8"');
// 把这个数组的数据输出成一段html
// 后端根据这个数组生成一段html返回给前端 : 后端渲染
// res.end(JSON.stringify(quotes));
let lis = quotes.map( q => {
return `<li>${q}</li>`;
} ).join('');
// console.log('lis', lis);
res.end(`
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="/public/css.css">
</head>
<body>
<h1>后端渲染</h1>
<ul>${lis}</ul>
</body>
</html>
`);
}
});
server.listen(8888);
初识---后端渲染2
if (url.startsWith('/all')) {
res.setHeader('content-type', 'text/html;charset="utf-8"');
/**
* 问题
* 1、后端的逻辑与前端需要的html混合了,不利于程序的开发、维护
*/
let lis = quotes.map( q => {
return `<li>${q}</li>`;
} ).join('');
// 有点类似静态文件代理,
//但是不一样的是,读取的内容并不会直接返回给前端,
//而是需要在后端做一些处理,把处理后的结果再返回
let tplContent = fs.readFileSync('./template/index.tpl').toString();
// 模板解析过程
tplContent = tplContent.replace(/\$\{lis\}/ig, lis);
res.end(tplContent);
}
});
初识---后端渲染3(nunjucks)
const http = require('http');
const fs = require('fs');
const mime = require('./mime.json'); // node会把json格式的数据自动转成 对象
const nunjucks = require('./nunjucks');
// console.log('nunjucks', nunjucks);
// let c = nunjucks.renderString('Hello {{ username }}', { username: 'James' });
// console.log('c', c);
const quotes = [
'哈哈',
'嘻嘻',
'呵呵'
];
const server = http.createServer((req, res) => {
let url = req.url;
if (url.startsWith('/public')) {
let content = fs.readFileSync('.' + url);
// url中最后一个 . 出现的位置
let lastPoint = url.lastIndexOf('.');
// 获取当前url中表示的文件后缀
let suffix = url.substring(lastPoint);
// console.log('suffix', suffix);
// 根据后最从 mime 中获取对应 MIME 类型
res.setHeader('content-type', mime[suffix] + ';charset="utf-8"');
res.end(content);
}
if (url.startsWith('/quote')) {
res.setHeader('content-type', 'text/html;charset="utf-8"');
let quote = quotes.sort(() => {
return Math.random() - .5;
})[0];
res.end(quote);
}
if (url.startsWith('/all')) {
res.setHeader('content-type', 'text/html;charset="utf-8"');
/**
* 问题
* 1、后端的逻辑与前端需要的html混合了,不利于程序的开发、维护
*/
// let lis = quotes.map( q => {
// return `<li>${q}</li>`;
// } ).join('');
// 有点类似静态文件代理,但是不一样的是,读取的内容并不会直接返回给前端,而是需要在后端做一些处理,把处理后的结果再返回
let tplContent = fs.readFileSync('./template/index-nunjucks.tpl').toString();
// 模板解析过程
// 对当前的模板字符串(tplContent)进行处理
tplContent = nunjucks.renderString(tplContent, {
title: '加油',
quotes
});
res.end(tplContent);
}
});
server.listen(8888);
初识EJS模块引擎---后端渲染
- 更多详情请参考官网
https://www.npmjs.com/package/ejs
EJS是后台模板
可以把我们数据库和文件读取的数据显示到HTML页面上
是一个第三方模块
基本使用:
cnpm install ejs --save
nodejs使用:
ejs.renderFile(filename,data,options,function(err,str){
//str=>Rendered HTML string
})
get/post
超文本传输协议(http)的设计目的是
保证客户端计器与服务器之间的通信
在客户端和服务器端之间进行请求响应时
两种最常被用到的方法是get和post
get 从指定的资源请求数据(一般用于获取数据)
post 向指定的资源提交要被处理的数据(一般用于提交数据)
- 该构建的目录结构为:普通版实现方法
- 下面是
views/form.ejs的页面代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<form action="./doLogin" method="POST">
用户名<input type="text" name="username"><br/>
密码<input type="password" name="password"><br/>
<input type="submit" value="提交"/>
</form>
</body>
</html>
- 下面是views/login.ejs的页面代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h2>zzzzzejs</h2>
<h3><%=msg%></h3>
<!-- <h3><%=list%></h3> -->
<ul>
<%for(var i=0;i<list.length;i++){%>
<li><%=list[i].title%></li>
<%}%>
</ul>
</body>
</html>
- 下面是main.js的页面代码
var http = require('http');
var url = require('url');
var ejs=require('ejs')
http.createServer(function (request, response) {
//获取路由
//输出http://127.0.0.1/login获取 /login
let pathname=url.parse(request.url).pathname;
//获取请求类型 GET/POST
console.log(request.method)
/*判断了七种情况,分别是
'/login'、'/register'、
'/admin'、'/news'、
'/poster'、'/doLogin'、其他
*/
if(pathname=='/login'){
// response.writeHead(200, {'Content-Type': 'text/html;charset="utf-8"'});
// response.end('login')
let msg='数据库里面获取的数据';
let list=[
{
title:'新闻',
author:'lth'
},
{
title:'新闻1',
author:'lth1'
},
{
title:'新闻2',
author:'lth2'
}
]
ejs.renderFile('./views/login.ejs',{
msg,
list
},(err,data)=>{
response.writeHead(200, {'Content-Type': 'text/html;charset="utf-8"'});
response.end(data)
})
}else if(pathname=='/register'){
response.writeHead(200, {'Content-Type': 'text/html;charset="utf-8"'});
response.end('register')
}else if(pathname=='/admin'){
response.writeHead(200, {'Content-Type': 'text/html;charset="utf-8"'});
response.end('admin')
}else if(pathname=='/news'){
//http://127.0.0.1:8081/news?page=28&id=1
//?page=28&id=1 --->url.parse(path,true).search
var query=url.parse(request.url,true).query;
console.log('query', query)
response.writeHead(200, {'Content-Type': 'text/html;charset="utf-8"'});
response.end('get传值获取成功')
}else if(pathname=='/poster'){
//post演示
ejs.renderFile('./views/form.ejs',{},(err,data)=>{
response.writeHead(200, {'Content-Type': 'text/html;charset="utf-8"'});
response.end(data)
})
}else if(pathname=='/doLogin'){
//获取post传值
// response.end('post ok')
var postData='';
request.on('data',(chunk)=>{
postData+=chunk
});
request.on('end',()=>{
console.log('postData',postData)
response.end(postData)
})
}else{
response.writeHead(404, {'Content-Type': 'text/html;charset="utf-8"'});
response.end('页面不存在')
}
}).listen(8071)
- 下面是/poster渲染views/form.ejs的页面
-
下面是/poster点击提交后跳转doLogin的页面
-
下面是/login渲染views/login.ejs的页面
-
该构建的目录结构为:进阶版实现方法
-
重点关注module/routes.js
-
下面是module/routers.js的页面代码
这里是module/routers.js的页面代码
const fs = require("fs");
var path = require('path');
var url = require('url');
var ejs = require('ejs');
//封装为一个私有方法
//--不需要暴露
//---返回的是例如"text/html"用于拼接content-type
let getFileMine=function(extname){
////同步方法拿到数据
var data=fs.readFileSync('../data/mime.json')
let mimeObj=JSON.parse(data.toString());
return mimeObj[extname]
}
这里是module/routers.js的页面代码
let app={
static:(request,response,staticPath)=>{
//实现静态web服务
//获取地址 例如/login.html
let pathname=url.parse(request.url).pathname;
pathname=pathname=='/'?'/index.html':pathname
//可以获取后缀名
let extname=path.extname(pathname)
//2.通过fs模块读取文件,排除'/favicon.ico'的情况
if(pathname!='/favicon.ico'){
try {
//读取静态目录下的文件例如 ./static/index.html
//如果此文件存在则通过后缀名获取content-type所需要的信息并加上去
var data=fs.readFileSync('./'+staticPath+pathname)
if(data){
let mime=getFileMine(extname)
response.writeHead(200, {'Content-Type': ''+mime+';charset="utf-8"'});
response.end(data); //不加载css样式
}} catch (error) {
console.log('error', 'error')
}
}
},
login:(request,response)=>{
//处理登录的业务逻辑
response.end(' --- login ---')
},
news:(request,response)=>{
//处理news的业务逻辑
response.end(' --- news --- ')
},
register:(request,response)=>{
//处理register的业务逻辑
},
admin:(request,response)=>{
//处理admin的业务逻辑
},
poster:(request,response)=>{
//处理poster的业务逻辑
ejs.renderFile('../demo01/views/form.ejs',{},(err,data)=>{
response.writeHead(200, {'Content-Type': 'text/html;charset="utf-8"'});
response.end(data)
})
},
doLogin:(request,response)=>{
//处理doLogin的业务逻辑
response.end('--- doLogin ----')
},
error:(request,response)=>{
response.writeHead(404, {'Content-Type': 'text/html;charset="utf-8"'});
response.end('页面不存在hhhh')
// response.end('admin')
}
}
module.exports=app
- 下面是main.js的页面代码
//引入的自定义模块
const routes =require('../module/routes')
var url = require('url');
var http = require('http');
http.createServer(function (request, response) {
console.log(request.method)
// http://127.0.0.1/news pathname=/news --->replace('/','') //去掉/变成news
// http://127.0.0.1/login pathname=/login
// http://127.0.0.1/xxx pathname=/xxx ---->不存在会报错
let pathname=url.parse(request.url).pathname.replace('/','');
//获取请求类型
console.log(request.method)
try{
routes[pathname](request,response)
}catch(error){
routes['error'](request,response)
}
}).listen(8080)
初识nunjucks模板引擎---后端渲染
- cnpm i koa koa-router koa-nunjucks-2 -S
- nunjucks的语法使用
变量:`{{username}}`
注释:{# Loop through all the users #}
if:
{% if hungry %}
I am hungry
{% elif tired %}
I am tired
{% else %}
I am good!
{% endif %}
for:
<h1>Posts</h1>
<ul>
{% for item in items %}
<li>{{ item.title }}</li>
{% else %}
<li>This would display if the 'item' collection were empty</li>
{% endfor %}
</ul>
过滤器:
{{ foo | replace("foo", "bar") | capitalize }}
模板继承block/extends:
1.定义父类模板:
h1>我是公共模板</h1>
<div class="leftContent">
{% block left %}
这边是左侧的内容
{% endblock %}
{% block right %}
这边是右侧的内容
{% endblock %}
{% block somevalue %}
我是一些数据
{% endblock %}
</div>
2.继承父类模板:
{% extends "common.html" %}
{% block left %}
我是左侧的内容1111
{% endblock %}
{% block right %}
我是右侧的内容11111
{% endblock %}
{% block somevalue %}
{{ super() }}
{% endblock %}
Macro(宏标签)可以定义可复用的内容,类似与编程语言中的函数:
{% macro pet(animalName,name="小白") %}
<div>
这里是一只{{animalName}};他的名字是{{name}}
</div>
{% endmacro %}
调用:{{pet("狗狗")}}
include/import:
include 引入文件:{% include "footer.html" %}
import 导入文件:
{% macro pet(animalName) %}
<p>这是一只{{animalName}}</p>
{% endmacro %}
{% macro book(bookName) %}
<p>这是一本书,名字叫{{bookName}}</p>
{% endmacro %}
调用:
{% import 'somemodule.html' as fn %}
{{fn.pet("狗狗")}}
{{fn.book("nodejs从入门到实践")}}
初识koa的基本操作
- 中间件函数的理解
const Koa = require('koa');
// 只是初始化了一个 Koa Application 对象,并没有创建 http 服务,也没有监听端口
const server = new Koa();
// use 接收的是一个函数,这个函数我们通常称为:中间件函数
// 中间件并不是循环依次执行的,里面不是for
// 控制中间件的执行,
// 假设其中一个中间件执行以后,不需要执行后续的中间件了
let content = '';
server.use(async function(ctx, next) { // next => 下一个中间件函数
console.log('有人访问了1');
content = 'a';
// ctx.response.body = content; // res.end(content);
ctx.body = content;
await next();
// ...
console.log('123');
});
server.use(function(ctx, next) {
return new Promise((resolve) => {
setTimeout(() => {
console.log('有人访问了2');
content += 'b';
ctx.body = content;
resolve();
}, 1000);
})
});
// 启动了一个基于 http 的 webserver
server.listen(8888);
- 静态代理:
const Koa = require('koa');
const mime = require('./mime.json');
const fs = require('fs');
// 只是初始化了一个 Koa Application 对象,并没有创建 http 服务,也没有监听端口
const server = new Koa();
// 静态代理
server.use(async (ctx, next) => {
let url = ctx.url;
if (url.startsWith('/public')) {
let content = fs.readFileSync('.' + url);
let lastPoint = url.lastIndexOf('.');
let suffix = url.substring(lastPoint);
// res.setHeader('content-type', mime[suffix] + ';charset="utf-8"');
ctx.set('content-type', mime[suffix] + ';charset="utf-8"');
ctx.body = content;
} else {
await next();
}
});
// 动态处理
server.use((ctx, next) => {
ctx.body = 'kkb';
});
// 启动了一个基于 http 的 webserver
server.listen(8888);
- 静态代理:使用
koa-static-cache - 动态代理:使用
koa-router
const Koa = require('koa');
const KoaStaticCache = require('koa-static-cache');
const KoaRouter = require('koa-router');
let users=[{id:1},{id:2}]
const quotes = [
'哈哈',
'嘻嘻',
'呵呵'
];
// 只是初始化了一个 Koa Application 对象,
//并没有创建 http 服务,也没有监听端口
const server = new Koa();
server.use(KoaStaticCache('./public', {
//代理的目录
prefix: '/public',
// gzip 压缩
gzip: true,
// 有利于开发中的文件变更
dynamic: true
}) );
let router = new KoaRouter();
router.get('/', async ctx => {
ctx.body = '首页';
});
router.get('/:id(\\d+)', async ctx => {
// koa-router 会解析真实url中 :id 所表示的部分
//并把解析后的结果转成一个对象,存储在 ctx.params
ctx.body = users.find(user => user.id == ctx.params.id);
});
router.get('/quote', async ctx => {
let quote = quotes.sort(() => {
return Math.random() - .5;
})[0];
ctx.body = quote;
})
server.use( router.routes() );
//启动了一个基于 http 的 webserver
server.listen(8888);
- Koa 的流程
- 因为函数调用的栈(LIFO - Last In First Out - 后进先出)特性
/**
* File: /app.js
***/
const Koa = require('koa');
const app = new Koa();
app.use(async (ctx, next) => {
console.log('a - start');
next();
console.log('a - end');
});
app.use(async (ctx, next) => {
console.log('b - start');
next();
console.log('b - end');
});
app.use(async (ctx, next) => {
console.log('c - start');
next();
console.log('c - end');
});
app.use(async (ctx, next) => {
console.log('d - start');
next();
console.log('d - end');
});
app.listen(8888);
- 输出为:
a - start
b - start
c - start
d - start
d - end
c - end
b - end
a - end
- 洋葱模型
项目案例
- 案例的结构目录
- public目录下为静态资源分别是css样式和图片资源
- tempates目录下模板引擎指定的目录
- app.js为主写入文件
- 利用到的技术如下:
const Koa = require('koa');
//用来处理静态资源
const KoaStaticCache = require('koa-static-cache');
//用来处理动态路由
const KoaRouter = require('koa-router');
//模板引擎用来执行模板文件渲染页面
const nunjucks = require('nunjucks');
//nodejs操纵数据库
const mysql2 = require('mysql2');
//对post请求过来的数据进行封装
const koaBody = require('koa-body');
// 创建数据库连接
const connection = mysql2.createConnection({
host: '127.0.0.1',
user: 'xxx',
password: 'xxx',
database: 'xxx'
});
const app = new Koa();
// 设置静态文件资源代理
app.use(KoaStaticCache('./public', {
prefix: '/public',
gzip: true,
dynamic: true
}));
// 动态资源处理 koa-router
const router = new KoaRouter();
// 配置模板引擎
nunjucks.configure('./templates', {
watch: true,
noCache: true
});
app.use(router.routes());
app.listen(8888);
- 模板引擎templates目录下的base.html页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="/public/css/css.css" />
</head>
<body>
<div id="app">
<header id="header">
<a href="/" id="logo"></a>
<nav id="nav">
{% for category in categories %}
<a href="/{{category.id}}">{{category.name}}</a>
{% endfor %}
</nav>
<div id="user">
<a href="/addItem">添加新商品</a>
<a href="">登录</a>
<a href="/register">注册</a>
</div>
</header>
<div id="main">
{% block main %}{% endblock %}
</div>
</div>
</body>
</html>
- 模板引擎templates目录下的register.html页面
{% extends "base.html" %}
{% block main %}
<div id="register" class="panel">
<h2>注册</h2>
<form action="" method="POST" enctype="application/x-www-form-urlencoded">
<div class="form-item">
<label>
<span class="txt">姓名:</span>
<input type="text" class="form-input" name="username">
</label>
</div>
<div class="form-item">
<label>
<span class="txt">密码:</span>
<input type="password" class="form-input" name="password">
</label>
</div>
<div class="form-item">
<label>
<span class="txt">重复密码:</span>
<input type="password" class="form-input" name="repeatPassword">
</label>
</div>
<div class="form-item">
<label>
<span class="txt"></span>
<!-- <button class="form-button primary">登录</button> -->
<button class="form-button">注册</button>
</label>
</div>
</form>
</div>
{% endblock %}
- 模板引擎templates目录下的index.html页面
{% extends "base.html" %}
{% block main %}
<ul class="items-list">
{% for item in items %}
<li class="panel">
<img src="/public/items/{{item.cover}}" alt="" class="cover">
<div class="name">{{item.name}}</div>
<div class="price">¥ {{(item.price/100).toFixed(2)}}</div>
</li>
{% endfor %}
</ul>
<div class="pagination-container">
<div class="pagination">
<a href="" class="prev">上一页</a>
<!-- pages是总共有几页(左闭右包) -->
{% for p in range(1, pages+1) %}
{% if p == page %}
<a href="" class="current">{{p}}</a>
{% else %}
<a href="?page={{p}}">{{p}}</a>
{% endif %}
{% endfor %}
{% if current<pages %}
<a href="{{current+1}}" class="next">下一页</a>
{% elif current>=pages %}
<a href="{{pages}}" class="next">下一页</a>
{% else %}
<a href="" class="next">下一页</a>
{% endif %}
</div>
</div>
{% endblock %}
- 模板引擎templates目录下的addItem.html页面
{% extends "base.html" %}
{% block main %}
<div id="login" class="panel">
<h2>添加商品</h2>
<!--
action:提交地址,发送请求的url
配合post把表单中的数据通过请求过程中的正文携带给后端
提交的数据最好通过一些方式进行格式化,比如json格式,或其它
浏览器(其它各种客户端框架)提供一些内置的数据格式
如果表单中支持:
application/x-www-form-urlencoded:表单默认,url编码
multipart/form-data:formdata格式,二进制
text/plain:纯文本
表单中还有一个特别注意的地方:
//通过name收集value
表单控制中的name属性,这个属性是用来设置提交数据的key
-->
<!-- 如果action=''留空则会把当前的页面做为提交的页面
即这里的post请求发送给了/addItem.html页面
-->
<form action="" method="POST" enctype="application/x-www-form-urlencoded">
<div class="form-item">
<label>
<span class="txt">商品类别:</span>
<select name="categoryId">
<option value="">请选择一个类别</option>
{% for category in categories %}
<option value="{{category.id}}">{{category.name}}</option>
{% endfor %}
</select>
</label>
</div>
<div class="form-item">
<label>
<span class="txt">商品名称</span>
<input type="text" name="name" class="form-input">
</label>
</div>
<div class="form-item">
<label>
<span class="txt">商品价格</span>
<input type="text" name="price" class="form-input">
</label>
</div>
<div class="form-item">
<label>
<span class="txt">商品图片</span>
<input type="text" name="cover" class="form-input">
</label>
</div>
<div class="form-item">
<label>
<span class="txt"></span>
<button class="form-button primary">提交</button>
</label>
</div>
</form>
</div>
{% endblock %}
- app.js对首页的处理逻辑
// 首页
router.get('/:id(\\d*)', async ctx => {
// 1、获取当前页面所需要的动态数据:商品分类数据、商品列表数据
// categories, items
let categories = [];
// 动态路由中的数据会被koa-router自动解析
// 并存储到ctx.params 下,它是一个对象,
// 对象中的key就是动态路由:后面的名称
let categoryId = ctx.params.id;
// ctx.request.query 存储了url中?后面的内容
let page = ctx.request.query.page;
let current=Number(page)
// console.log(page,'?page=2(xx)');
if (!page) {
page = 1;
}
// function nextPage(current,pages){
// console.log(1);
// }
// nextPage()
// console.log('categoryId', categoryId);
// CURD(Create, Update, Read, Delete) (增删改查)
// C:insert into
// U: update
// R: select
// D: delete
// connection.query(
// 'SELECT * FROM `categories`',
// function (err, results, fields) {
// // 数据库的查询是异步的(需要时间),数据还没有查询出来的时候,
// // 后面的代码就执行完了,后端已经返回了
// console.log(results);
// console.log(fields);
// }
// );
// 每页显示的数据条数
//一页一条数据
let prepage = 2;
// 当前页
// let page = 1; // page:1 => offset:0, page:2 => offset:5, page:3 => offset:10
/**
* // ctx.request.query 存储了url中?后面的内容
let page = ctx.request.query.page;
page是当前第几页
*/
// 偏移量
let offset = (page - 1) * prepage;
// 查询所有数据的总条数
let sqlCount = 'SELECT count(id) as count FROM `items`';
let [
[{
count
}]
] = await query(sqlCount);
// console.log('sql', count);
//pages是总共有几页
let pages = Math.ceil((count / prepage));
// console.log(pages,'pagessss');
let sql = 'SELECT * FROM `items` limit ' + prepage + ' offset ' + offset;
if (categoryId) {
//如果存在类目id即/1或/2或/3则增加where语句
sql = 'SELECT * FROM `items` where `category_id`=? limit '+ prepage + ' offset ' + offset;
}
[categories] = await query('SELECT * FROM `categories`');
// console.log([categories], '1111');
/**[categories]
* [ [ TextRow { name: '手机', id: 1 },
TextRow { name: '笔记本', id: 2 },
TextRow { name: '电视机', id: 3 } ] ]
*/
/**
* let sql = 'SELECT * FROM `items` limit ' + prepage + ' offset ' + offset;
*
* if (categoryId) {
sql = 'SELECT * FROM `items` where `category_id`=? limit 1 offset 1';
}
*
* 用来渲染单个单个的商品数据图
*/
[items] = await query(sql, [categoryId]);
// 2、通过后端模板引擎对数据和模板文件进行渲染,得到最终返回给前端的页面
ctx.body = nunjucks.render('index.html', {
categories,
items,
pages,
page,
current
});
});
- app.js对
/addItem的处理逻辑
/ 通过get方式访问和返回一个添加新商品的页面
// 通过点击添加一个新商品跳转过来的添加商品页面
//<option value="{{category.id}}">{{category.name}}</option>
//<option value="">请选择一个类别</option>
//在option中的value有两种可能一种是''(空字符),一种是1,2...(商品id)
router.get('/addItem', async ctx => {
[categories] = await query('SELECT * FROM `categories`');
ctx.body = nunjucks.render('addItem.html', {
categories
});
});
- app.js对
/register的处理逻辑
router.get('/register',async ctx=>{
[categories] = await query('SELECT * FROM `categories`');
ctx.body = nunjucks.render('register.html', {
categories
});
})
- app.js对
/addItem中post请求的处理逻辑
// post提交过来的数据进行处理
// 相当于拦截这个页面的post请求
router.post('/addItem', koaBody(), async ctx => {
// ctx.request.body => koaBody 中间件解析请求正文后数据存储的位置
// console.log('数据', ctx.request.body);
/**
* 数据 { categoryId: '1',
name: '荣耀20 PRO',
price: '229900',
cover: '62cf191f88e41447.jpg' }
*/
let {
categoryId,
name,
price,
cover
} = ctx.request.body;
// 对数据进行合法性验证
// 验证通过,存储到数据库
// 解构获取结果中的第一个数组数据
//预查询
let [rs] = await query(
"INSERT INTO `items` (`category_id`, `name`, `price`, `cover`) VALUES (?, ?, ?, ?)",
[categoryId, name, price, cover]
);
// console.log('rs', rs);
ctx.body = '<p>添加成功</p><p><a href="/addItem">继续添加</a> | <a href="/">回到首页</a></p>';
});
- app.js对
/register中post请求的处理逻辑
router.post('/register',koaBody(),async ctx => {
let {
username,
password,
repeatPassword
} = ctx.request.body;
if(password===repeatPassword){
//预查询
let [result] = await query(
"INSERT INTO `users` (`username`, `password`) VALUES (?, ?)",
[username,password]
);
console.log(result);
ctx.body = '<p>注册成功</p><p><a href="">点击登录</a> | <a href="/">回到首页</a></p>';
}else{
ctx.body = '<p>注册失败</p><p><a href="/register">重新注册</a> | <a href="/">回到首页</a></p>';
}
})