Koa脚手架
1. 全局安装脚手架:npm install koa-generator -g
2. 创建项目:koa demo01 --> cd demo01 --> npm install
3. 运行项目:npm start
4. 脚手架创建的项目已经实现了路由的模块化,即项目的模块化。
路由模块化
1. 与Express的路由模块化类似,把不同功能的路由拆分到不同的js文件中,然后再暴露路由对象;
2. 假设项目包含首页(index)、后台管理(admin),配置模板引擎art-template的目录为views
1. 在根目录下创建routes目录,用于存放路由模块,项目入口为app.js
2. 创建routes/index.js,routes/admin.js
3. 在index.js中配置index模块的路由,index是根路由的模块
let router = require('koa-router')();
router.use(async(ctx, next)=>{ ----> index模块的应用级中间件,在index路由之前匹配
......
await next(); ----> 继续向下匹配index路由
......
})
router.get('/', async(ctx)=>{
await ctx.render('index'); -->渲染模板:views/index.html
})
router.get('case', async(ctx)=>{ ------> 对于根路由的模块,其子路由不能再加'/'
await ctx.render('index/case'); -->渲染模板:views/index/case.html
})
module.exports = router; ---> 暴露index模块的路由对象
1. 注意:与Express不同,Koa的根目录匹配已经默认加了'/',其子路由的开头不能再加'/'
4. 在admin.js中配置admin模块的路由
let router = require('koa-router')();
router.use(async(ctx, next)=>{ ----> admin模块的应用级中间件,在admin路由之前匹配
......
await next(); ----> 继续向下匹配admin路由
......
})
router.get('/', async(ctx)=>{
await ctx.render('admin');
})
router.get('/user', async(ctx)=>{
await ctx.render(admin/user');
})
module.exports = router;
5. 在app.js中启用index模块和admin模块的路由
let router = require('koa-router')();
let index = require('./routes/index');
let admin = require('./routes/admin');
router.use('/', index.routes()); -----> 启用根路由的index模块
router.use('/admin', admin.routes()); ---> admin模块的路由
app.use(router.routes()).use(router.allowedMethods);
6. 子路由:在admin路由中继续启用子路由
1. 创建routes/admin/user.js,配置路由
let router = require('koa-router')();
router.get('/', async(ctx)=>{
await ctx.render('admin/user'); -->渲染模板:views/admin/user.html
})
router.get('/add', async(ctx)=>{
await ctx.render('admin/user/add'); ->渲染模板:views/admin/user/add.html
})
module.exports = router;
2. 在admin.js中引入user.js,并启用user路由
let user = require('./admin/user');
router.use('/user', user.routes());
7. router路由的写法不止一种,比如把路由写在对应的路由文件中
1. app.js:router.use(admin.routes());
2. admin.js:let Router = require('koa-router');
let router = new Router({ prefix: '/admin' })
更多补充
1. nodeJs生成验证码:svgCaptcha,支持express和koa
2. 页面自动刷新/跳转
1. 页面3s后自动刷新:<meta http-equiv="refresh" content="3" />
2. 3s后页面自动转到指定网址
<meta http-equiv="refresh" content="3; url=http://www.baidu.com/" />
3. 登录失败之后,加载错误页面error.html,5s后自动转回登录页
router.use(async(ctx, next)=>{
ctx.state.HOST = 'http://'+ctx.request.header.host; -->设置域名的全局变量
await next();
})
router.post('doLogin', async(ctx)=>{
if(登录失败){
await ctx.render('login/error', { url: ctx.state.HOST+'/login' })
}
})
error.html:<meta http-equiv="refresh" content="5; url={{url}}" />
3. art-template自定义日期管道/过滤器
1. 格式化日期的插件:npm install silly-datetime --save
2. let sd = require('silly-datetime');
render(app, {
root: path.join(__dirname, 'views'),
extname: '.html',
debug: process.env.NODE_ENV !== 'production',
dateFormat: dateFormat=function(value){ -----> 扩展模板里的方法
return sd.format(new Date(value), 'YYYY-MM-DD HH:mm');
}
})
3. 在art模板中使用过滤器,格式化传递的变量time:<div>{{time | dateFormat}}</div>
4. koa-jsonp:ajax请求返回JSON的中间件,npm install koa-jsonp --save
1. let jsonp = require('koa-jsonp'); ---> 同时支持处理JSON和jsonp
2. 配置jsonp中间件:app.use(jsonp());
3. 一个简单的JSON数据接口:
router.get('/getMsg', async (ctx) => {
ctx.body = { message: "请求的数据信息", success: true }
})
4. 使用浏览器测试接口:
http://localhost:3000/getMsg ---> 如果返回JSON格式的数据,则表示此接口支持JSON
http://localhost:3000/getMsg?callback=xxx ---> 测试接口是否支持jsonp
5. ctx.request.headers['referer']:获取上一页的地址
1. 用全局变量记录上一页的地址
ctx.state.prevPage = ctx.request.headers['referer'];
2. 返回上一页:ctx.redirect(ctx.state.prevPage);
文件上传
<form action="{{HOST}}/doUpload" method="post" enctype="multipart/form-data">
<input type="file" id="pic" name="pic" />
</form>
1. koa-multer:用于处理 multipart/form-data 类型的表单数据,主要用于上传文件;
2. koa-multer基于 multer 模块:npm i koa-multer --save
let multer = require('koa-multer');
let storage = multer.diskStorage({
destination: function(req, file, cb) {
cb(null, 'upload/pic'); ---> 配置上传文件的目录:当前目录下的upload/pic
},
filename: function(req, file, cb) {
let extname = (file.originalname).split('.'); -->从文件名中切割出后缀名
cb(null, Date.now()+'.'+extname[extname.length-1]); -->对文件重命名
}
});
let upload = multer({storage: storage});
3. 处理上传文件
router.post('/doUpload', upload.single('pic'), async (ctx)=>{
let filename = ctx.req.file.filename; ---> 上传的文件名
let form = ctx.req.body; ---> form表单的其他数据
})
4. upload.single('pic'):pic与<input type="file" name="pic" />的name保持一致;
5. koa-multer也支持多个文件上传,在github上的multer模块中有更多配置信息;
6. koa-body:用于取代 koa-bodyparser 和 koa-multer
路由鉴权
1. koa-jwt:用于路由鉴权、token验证
富文本编辑器
1. ueditor:百度的轻量级、可定制富文本web编辑器,开源基于MIT协议,允许自由使用和修改代码
2. ueditor官方虽然没有提供NodeJs版本,但官方推荐的第三方插件里有nodeJs版本;
1. 第三方插件直接支持express,不支持koa2
2. 支持koa2的ueditor插件:npm i koa2-ueditor --save
3. ueditor的基本配置
1. 在github下载koa2-ueditor的demo,拷贝public/ueditor,粘贴到自己项目的public目录
2. 在模板中的<head>标签中引入ueditor目录下的静态文件
<script type="text/javascript" src="/ueditor/ueditor.config.js">
<script type="text/javascript" src="/ueditor/ueditor.all.min.js">
<script type="text/javascript" src="/ueditor/lang/zh-cn/zh-ch.js">
3. 使用ueditor,取代<textarea>的位置
<script id="editor1" type="text/plain" style="width:800px;height:300px;">
4. 在<body>标签之后实例化富文本编辑器:
<script type="text/javascript">
let ue = UE.getEditor('editor1');
</script>
5. 富文本编辑器中的内容最终生成的是带有style样式的html标签,是一个html字符串。
<form ...>
<script type="text/plain" id="editor1" name="content" style="...">
</form>
router.post('/doUpload', upload.single('pic'), async (ctx)=>{
let content = ctx.req.body.content; ---> form表单的富文本编辑框中的数据
})
4. 配置ueditor的图片上传
const ueditor = require('koa2-ueditor');
router.all('/editor/controller', ueditor(['public', {
imageAllowFiles: ['.png', '.jpg', '.jpeg'],
imagePathFormat: '/upload/ueditor/image/{yyyy}{mm}{dd}/{filename}'
}]));
1. '/editor/controller':统一上传的地址,对应ueditor/ueditor.config.js中的配置
serverUrl: "/editor/controller" ---> 根据router.all()的实际路由进行修改
http://localhost:3000/editor/controller --> 完整的上传地址URI
2. 'public':上传图片的保存目录,根目录/public/upload/ueditor/image/当前日期/图片名
5. 自定义ueditor编辑框:修改配置文件ueditor/ueditor.config.js
1. 配置自动增长高度:autoHeightEnabled: false --> 编辑框的内容过长时自动出现滚动条
2. 自定义工具栏:修改toolbars节点
6. 为编辑框设置默认内容
<script type="text/javascript">
let ue = UE.getEditor('editor1');
ue.addListener('ready', ()=>{ ----> 监听ueditor准备完成
ue.setContent(`{{@list.content}}`); --> art-template模板接收参数的方式
}) ---> list.content是上一次富文本编辑框中的内容,保存在数据库中
</script>
SEO优化
1. SEO优化:也称为搜索引擎优化,目的是让搜索引擎优先搜索到自家网站,并展示给用户;
2. 设置网站的必要信息:标题、关键字、描述
1. 网站标题:<title>标题内容</title>,网站标题变化不能过于频繁,否则百度会认为是作弊;
2. 网站关键字:<meta name="Keywords" content="关键字内容" />
3. 网站描述:<meta name="Description" content="描述内容" />
3. 图片<img>必须使用 alt 属性
4. html标签的合理使用,语义化,符合W3C标准
1. 尤其是 h 系列标签的合理使用,其中,<h1>标签的权重最高,一个页面建议只出现一次;
2. <h1>标签的内容建议和<title>的内容相同。
5. 内链、外链的合理使用,如友情链接就属于外链,因为搜索引擎会根据内链和外链继续搜索网站;
6. 网站的内容尽量都是原创的;
7. 合理使用长尾关键词:比如,相对与"koa2教程","koa2视频教程"就属于长尾关键词.
API接口
1. NodeJs的异步环境特性,让它最适合写API接口,非常善于处理大数据、高并发;
2. 比如一个返回JSON数据的简单接口
let jsonp = require('koa-jsonp');
app.use(jsonp());
router.get('/getList', async (ctx)=>{
let result = await (从数据库中获取数据);
ctx.body = { data: result }
})
跨域问题
1. 在AJAX请求后台接口数据时,浏览器的同源策略会引起跨域安全问题;
1. 接口的协议、域名、端口号必须与当前Web应用所处的协议、域名、端口号保持一致;
2. 比如,访问当前网页的地址为 http://localhost:3000/index,那么,网页中的AJAX请求的
接口地址必须是http://localhost:3000/xxxx
3. 协议、域名、端口号三者有任何一个出现不一致,就会导致跨域问题。
2. JSONP的原理:利用<script>可以跨域的特性
1. 在本地写一个回调函数,在远程执行这个函数,并把远程数据传到本地
http://localhost:3000/api/getList?callback=xxx ---> xxx被当作远程执行的函数
2. JSONP跨域请求的前提:服务器必须支持JSONP;第三方koa-jsonp模块同时支持JSONP请求。
3. 除了JSONP实现跨域请求,还可以让后台设置允许跨域:npm i koa2-cors --save
1. NodeJs后台设置允许跨域请求
let cors = require('koa2-cors');
app.use(cors());
2. AJAX可以直接跨域请求接口,但由此导致的安全性问题,则通过token验证解决。
RESTful API
1. 当前Web应用的趋势就是前后端分离,为了让前端设备与后端更好地通信,出现了很多API结构;
2. RESTful API是目前比较成熟的一套互联网应用程序的API设计理念;
3. 一个优秀的RESTful API所考虑的方面:
1. 协议:建议使用更安全的https协议
2. 域名:尽量部署在专属域名下面,如https://a.bcd.com,https://api.bcd.com
3. 考虑到接口的升级,应该将API的版本号放在URI中
https://a.bcd.com/api1/list,https://a.bcd.com/api2/list
https://a1.bcd.com,https://a2.bcd.com
4. 路径:在RESTful架构中,每个URI代表一种资源,所以URI中使用与数据库表名相对应的名词
5. 使用合理的HTTP请求方法
4. HTTP的7种请求方法:GET、POST、PUT、DELETE、PATCH、HEAD、OPTIONS,常用的只有前4种
1. GET:select,从服务器取出一项或多项资源
2. POST:create,在服务器新建一个资源
3. PUT:update,在服务器更新/修改资源
router.put('/edit', async (ctx)=>{ ---> koa2可以直接识别不同的请求方法
//修改数据、并返回修改后的完整数据
})
4. DELETE:从服务器删除资源
router.delete('/del', async (ctx)=>{ ---> 响应 delete 方式的请求
//删除数据
})
5. 以vueJs的axios为例,axios提供了7种不同的HTTP请求方法。
发布线上
1. 购买域名和服务器:万网(阿里收购)、西部数码...
2. 域名备案:新购买的域名不能直接被解析到服务器,需要在管理局登记域名信息(20个工作日左右)
1. 每个出售域名的网站上,会有域名备案的流程说明和入口;
2. 当然,还可以出钱到就近的外包公司备案。
3. 域名解析:让域名和服务器IP地址相关联,在购买域名的网站上配置即可;
1. DNS服务器:负责域名解析的服务器,返回真实的服务器IP地址;
2. ping 域名:查看当前域名所指向的真实服务器IP,如果ping失败,可能此域名设置了拒绝。
ping www.baidu.com
4. nginx:负责转发与负载均衡,从而可以在一台服务器上运行N个nodeJs应用程序。
nginx配置
1. 在当前服务器上有2个nodeJs应用程序,端口号分别为8001、8002
2. 下载nginx应用包,解压,改名为nginx
3. 配置nginx/conf/nginx.conf
1. 配置http节点:转发规则
http {
upstream backaa {
server 127.0.0.1:8001; ---> 服务器上的nodeJs应用程序的端口号
}
upstream backbb {
server 127.0.0.1:8002;
}
}
2. 配置http节点下的server节点:反向代理/负载均衡
server {
listen 80;
server_name aa.v123.com; ---> 域名
#location / {
# root html;
# index index.html index.htm;
#}
location / {
# 设置主机头和客户端的真实地址,以便服务器获取客户端的真实IP
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
#禁用缓存
proxy_buffering off;
#反向代理的地址
proxy_pass http://backaa; ---> 与upstream相对应
}
......
}
server {
listen 8080;
server_name bb.v123.com; ---> 域名
#location / {
# root html;
# index index.html index.htm;
#}
location / {
# 设置主机头和客户端的真实地址,以便服务器获取客户端的真实IP
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
#禁用缓存
proxy_buffering off;
#反向代理的地址
proxy_pass http://backbb; ---> 与upstream相对应
}
......
}
5. 启动nginx(windows系统):双击运行nginx/nginx.exe
6. 启动nodeJs应用程序
7. 访问:http://aa.v123.com,http://bb.v123.com
网友评论