我转过几个弯 绕过那个小雨楼
拿着蒲扇摆着衣衫渡着紧箍咒
不问天涯不停留 喝过几壶酒
不过年少白头道义放胸口
倘若明天之后 遥看前尘剑封侯
似那天上神仙无所求
朝朝暮暮君如梦醒十分不为何理由
是真是假是惶恐是无休
路过这风雨花满楼 片刻都不停留
我本这书生进京赶考留下许多愁
你问有没有时候 我叹这天道默悠悠
能否与我一醉方休
又过了几个弯 算尽天量道莫慌
踏这田园闻这芳草香
跌跌撞撞仗剑天涯折煞不枉无笔良
是梦是幻是温柔是家乡
路过这风雨花满楼 片刻都不停留
我本这书生进京赶考留下许多愁
你问有没有时候 我叹这天道默悠悠
能否与我一醉方休
路过这风雨花满楼 片刻都不停留
我本这书生进京赶考留下许多愁
你问有没有时候 我叹这天道默悠悠
能否与我一醉方休
谁能与我一醉方休
koa框架23 服务端渲染、jade、ejs、koa服务端渲染、内容静态化
服务端渲染
全称的 server side rendering 也就是所谓的ssr了,经常看到vue的一个关于seo优化的一个方案或者是防止首页白屏,一般选用ssr方案。
优点:
- seo友好
- 无兼容问题
- 安全性高
- 代码精简
相对客户端渲染 client side rendering 客户端有两个优点:
- 降低服务端,带宽压力
- 用户体验好
js运行速度快,生成html比服务端一个来回快很多。
服务端渲染框架
pug(jade)
以前叫jade,现在叫pug,改名了。
使用
npm i pug
const pug=require('pug');
pug.renderFile(
//模板
'./template/1.pug',
//数据
{
pretty: true, //用来测试
title: '试试pug这个东西',
users: [
{name: 'blue', age: 18},
{name: 'zhangsan', age: 25, vip: true},
{name: 'lisi', age: 26},
]
},
(err, html)=>{
if(err){
console.log('渲染失败');
console.log(err);
}else{
console.log(html);
}
});
pug.render()这是渲染手打的pug代码,renderFile则是渲染一个模板文件,所以renderFile的第一个参数为模板文件地址。
第二个参数是模板使用的参数+pug自身的设置选项,他合并到一个对象里面了,pretty: true,
用于美化输出的,原来是做一行压缩的
第三个则是回调,err为错误,html为解析完毕后渲染好的html文档。
模板
html
head
meta(charset="utf-8")
meta(name="copyright",content="zhinengshe.com")
link(rel="stylesheet",href="/main.css")
style.
#logo {width:200px;height:150px;}
.box {width:200px;height:200px;}
body
h1#logo.box.active= title
ul.list
for user in users
if user.vip
li.item.vip
span.name= user.name
span.age= user.age
else
li.item
span.name= user.name
span.age= user.age
pug的语法,层级的关系是通过他的缩进来判断的,行内属性写在圆括号后,然后由于id和class比较常用,于是又对应的缩写形式:h1#logo.box.active
如果是元素的内容,在标签书写完毕后空一格接内容:style #logo{xxxx}
如果想要换行写,那么就在标签后面加个点.
,然后回车,记得缩进,不然不会识别为内容
如果要使用变量,在前面加个等号=
然后就是一些for循环了。
ejs
ejs相对于pug,他不会破坏html,他类似于php那种渲染方式。
npm i ejs
使用
const ejs=require('ejs');
ejs.renderFile(
//1.模板
'./template/1.ejs',
//2.数据
{
name: 'blue',
arr: [12,5,8,34]
},
(err, html)=>{
if(err){
console.log(err);
}else{
console.log(html);
}
}
);
ejs也是三个参数,和pug差不多,但是ejs第二个参数没有配置参数
模板
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<%-include('header.inc.ejs')%>
姓名:<%=name%>
<ul>
<%arr.forEach(item=>{%>
<li><%=item%></li>
<%})%>
</ul>
</body>
</html>
ejs实际上就是一个html+变量语法
<%=name%>
name是变量,=符号用于输出,这个输出是转义的,如果变量里面有html的尖括号那些特殊字符,会被转义
<%-name%>
使用减号表示不转义输出
include
是ejs的一个引入ejs文件的一个方法,用于多个文件共用一部分相同的内容,比如header,footer这些。
然后include本身只是引用,所以还需要输出,如果使用=等号,引入的ejs文件会被转义,所以要使用减号
js部分,ejs里面可以直接使用原生js,js的使用,如果不需要输出,不要等号和减号就行了,所以<%arr.forEach(item=>{%>
用<%%>
直接包裹js语法就行了。
koa渲染
koa对ejs也有也插件
npm i koa-ejs
const Koa = require("koa");
const ejs = require("koa-ejs");
const path = require("path");
let server = new Koa();
ejs(server, {
root: path.resolve(__dirname, "template"), //模板目录
layout: false, //布局目录,子目录
viewExt: "ejs", //模板扩展名
cache: false, //缓存
debug: false, //debug模式,一般用不到
});
server.use(async ctx => {
await ctx.render("index", {
title: "木灵鱼儿"
})
});
server.listen(8080);
koa-ejs的使用时将server作为参数传入,然后接一个配置参数,root为模板根目录,layout暂时不管,false就行了,然后就是viewExt,设置了扩展名后,我们在后面调用模板就不用再写后缀了,缓存的话,如果开启了,会缓存渲染后数据,方便下次调用,自己调试的时候就关闭,debug是作者自己用的,我们一般用不着
使用了通过ctx.render方法,可以去渲染指定模板文件,第一个是文件名,第二个是参数。
配合数据库
const Koa = require('koa');
const ejs = require('koa-ejs');
const path = require('path');
const Router = require('koa-router');
const mysql = require('promise-mysql');
const opn = require('opn');
const {
time2date
} = require('./libs/common');
let server = new Koa();
ejs(server, {
//模板目录
root: path.resolve(__dirname, 'template'),
//布局目录(子目录)
layout: false,
//模板扩展名
viewExt: 'ejs',
//缓存
cache: false,
debug: false
});
//路由
let router = new Router();
//首页
router.get('/', async ctx => {
ctx.redirect(
router.url('list', 1)
);
});
//列表
router.get('list', '/list/:page', async ctx => {
const {
page
} = ctx.params;
const SIZE = 10;
//本页数据
let data = await ctx.db.query("SELECT ID,title,time,channel FROM news_table LIMIT ?,?", [(page - 1) * SIZE, SIZE]);
data.forEach(news => {
news.time = time2date(news.time * 1000);
});
//总数据条数
let rows = await ctx.db.query("SELECT count(*) AS total FROM news_table");
await ctx.render('list', {
list: data,
page: parseInt(page),
page_count: Math.ceil(rows[0].total / SIZE),
channels: {
'index': '热门',
'guonei': '国内',
'guoji': '国际',
'war': '军事',
'hangkong': '航空'
}
});
});
//详情
router.get('/news/:id', async ctx => {
const {
id
} = ctx.params;
let rows = await ctx.db.query("SELECT title,content FROM news_table WHERE ID=?", [id]);
let data = rows[0];
if (!data) {
ctx.body = '新闻没有找到,可能已经删除或尚未审核';
} else {
await ctx.render('news', data);
}
});
server.use(router.routes());
(async () => {
server.context.db = await mysql.createPool({
host: 'localhost',
user: 'root',
password: '123456',
database: 'news163'
});
server.listen(8080);
opn('http://localhost:8080/');
})();
内容静态化
所谓额内容静态化就是将已渲染的东西缓存起来,下次使用相同的东西就使用缓存,已加快速度。
原理
有缓存 ---- 读取缓存 --- 返回
无缓存 ---- 渲染----服务器返回个浏览器渲染内容 --- 将渲染缓存
无非就这个逻辑,于是几种方式
内存缓存
//缓存对象
let cache = {};
//静态化
router.use(async (ctx, next) => {
if (cache[ctx.url]) {
console.log(`from cache: ${ctx.url}`);
ctx.body = cache[ctx.url];
} else {
console.log(`from render: ${ctx.url}`);
await next();
cache[ctx.url] = ctx.body;
}
});
通过use,每个路由都需要经过他,我们就就判断嘛,有缓存用缓存,没缓存先渲染丢给服务器后再保存到缓存对象。
文件缓存
将缓存保存为本地文件: 创建一个cache文件夹
const fs = require('promise-fs');
//静态化
router.use(async (ctx, next) => {
let name = ctx.url.replace(/\//g, '_');
let cachepath = path.resolve(__dirname, 'cache', name);
try {
await fs.stat(cachepath);
ctx.set('content-type', 'text/html; utf-8');
ctx.body = fs.createReadStream(cachepath);
console.log(`from cache: ${ctx.url}`);
} catch (e) {
await next();
await fs.writeFile(cachepath, ctx.body);
console.log(`from render: ${ctx.url}`);
}
});
由于我们使用路径作为文件名,但是路径是带下划线的,我们需要进行修改,改为下划线。
然后我们先生成缓存的文件路径,因为这时我们已经有访问路径了嘛,自然知道缓存的文件名了。
由于需要判断文件是否存在,通过stat方法查找是否有这个文件,有就继续运行代码,没有就会报错,所以我们用try-catch包裹
有这个文件,我们通过createReadStream方法,读取一段返回一段,但是这是文件流的形式,也就是文件下载的形式,这就导致文件变成了下载而不是网页,但是我们可以通过修改ctx的头信息将这个流额形式改为html读取。
如果没有这个文件,我们就先渲染,也就是next下一步,渲染自然是通过下一个对应路由去做了,渲染完毕后再保存本地。
radis缓存
使用文件的话,io的消耗很大,我们又可以使用radis
const redis = require('redis');
const bluebird = require('bluebird');
let client = redis.createClient({
host: 'localhost'
});
bluebird.promisifyAll(redis.RedisClient.prototype);
//静态化
router.use(async (ctx, next) => {
let name = ctx.url.replace(/\//g, '_');
let cache = await client.getAsync(name);
if (cache) {
ctx.body = cache;
console.log(`from cache: ${ctx.url}`);
} else {
await next();
await client.psetexAsync(name, 30 * 86400 * 1000, ctx.body);
console.log(`from render: ${ctx.url}`);
}
});
连接好radis,并且通过bluebird将方法转为async后
一样先改下路径名字作为缓存对象的key,然后通过getAsync直接去获取这个key,如果这个key存在的话,radis是返回字符的,不像mysql返回数组对象额。
我们直接if判断这个对象存不存在就行了,存在就用,不存在就先next,再存储,这里我们也使用了过期时间,这样radis会自动在到期后删除内容。
评论(0)