ejs模板模块
** 首先安装 **
npm install ejs --save
创建ejs模板(.ejs文件)
* 文件类似于html,不过里边可以通过以下特定语法插入数据*
+ <%=data%> // 直接插入值data
+ <%-tag%> // 插入html,即 字符串里的html便签会被解析
+ <% for (var i =0;i<list.length;i++) {%> <li><%=list[i]%></li> <% } %> // 流程控制
+ <% if (a.length) {%> <li><%=a[0]%></li> <% } %> // 流程控制
+ <% '脚本' 标签,用于流程控制,无输出。
+ <%_ 删除其前面的空格符
+ <%= 输出数据到模板(输出是转义 HTML 标签)
+ <%- 输出非转义的数据到模板
+ <%# 注释标签,不执行、不输出内容
+ <%% 输出字符串 '<%'
+ %> 一般结束标签
+ -%> 删除紧随其后的换行符
+ _%> 将结束标签后面的空格符删除
+ <%- include('./head', {title: res.title})%> // 可以引入其他模板并传入数据;【这里注意:如果引入的文件不需要进行html转义,则include 包裹标签应该使用 <%=】
// 渲染ejs文件
ejs.renderFile('.//', {data: '123'}, (err,data) => {
resolve(data)
})
// 渲染ejs字符串,
ejs.render('<?= users.join(' | '); ?>', {users: users},
{delimiter: '?'});
})
koa 模块
安装
npm install koa --save
koa的路由需要依赖koa-router
npm install koa-router --save
const Koa = require('koa')
const router = require('koa-router')() // 引入并且实例化路由
const app = new Koa()
// ctx 上下文 包含了request 和response 等信息
router.get('/', function (ctx) {
ctx.body='首页' // 直接返回数据, 包含了res.writeHead(), res.end()等操作
}).get('/news', async function(ctx) {
// 从ctx中读取get参数
console.log(ctx.url,':', ctx.request.url)
ctx.body=`参数是${JSON.stringify(ctx.query)}${ctx.querystring}`
}).get('/file/(.*)', async (ctx) => {
// 匹配路径可以支持正则表达式,这里意思是路径以/file开头的所有路径
ctx.set('content-type', 'application/octet-stream'); // 流式下载文件
ctx.body = fs.createReadStream(path.resolve(__dirname, ctx.url))
})
// 动态路由
router.get('/prod/:aid', async function(ctx) {
console.log(ctx.query) // {aid: 123}
})
// koa中间件 即 执行路由匹配之前执行的代码就叫中间件, 通过app.use(async (ctx, next) => {await next()}) 来绑定,next调用是为了继续向下匹配; 如果不调用next,就不会继续向下匹配了
// app.use 绑定的顺序决定 执行顺序
app.use(async (ctx, next) => {
console.log('这是一个中间件')
await next()
// 中间的next已经执行完匹配,下面可以处理结果,包括错误处理
if (ctx.status == 404) {
console.log('第二次处理异常,没有这个页面')
ctx.body='没有这个页面'
} else {
console.log('匹配完成,第二次��理')
}
})
app.use(async (ctx, next) => {
console.log('这是第二个中间件')
await next()
// 中间的next已经执行完匹配,下面可以处理结果,包括错误处理
if (ctx.status == 404) {
console.log('第一次处理404,没有这个页面')
} else {
console.log('匹配完成,第一次处理')
}
})
app.on('error', (err, ctx) => {
// 可以设置全局错误处理函数,任何地方发生错误都可以处理
console.log(err)
})
app.use(router.routes()) // 启动路由
app.use(router.allowedMethods()) // 在之前没设置响应头的时候设置响应头,可选
app.listen(80)
// 可以将同一套逻辑绑定到多个服务上
app.listen(8080)
app.listen(8081)
http.createServer(app.callback()).listen(3000);
https.createServer(app.callback()).listen(3001);
定时组件 node-schedule
var schedule = require('node-schedule');
function scheduleCronstyle(){
schedule.scheduleJob('30 * * * * *', function(){
console.log('scheduleCronstyle:' + new Date());
});
}
scheduleCronstyle();
** 6个占位符从左到右分别代表:秒、分、时、日、月、周几
''表示通配符,匹配任意,当秒是''时,表示任意秒数都触发,其它类推 **
每分钟的第30秒触发: '30 * * * * *'
每小时的1分30秒触发 :'30 1 * * * *'
每天的凌晨1点1分30秒触发 :'30 1 1 * * *'
每月的1日1点1分30秒触发 :'30 1 1 1 * *'
2016年的1月1日1点1分30秒触发 :'30 1 1 1 2016 *'
每周1的1点1分30秒触发 :'30 1 1 * * 1'
koa 对象属性
/**
* ctx 对象
* {
* request, // koa 请求体
* response, // koa 响应体
* req, // node 的请求体
* res, // node 的响应体
* body, // 用于像页面输出
* state,
* // 推荐的命名空间,用于通过中间件传递信息和前端视图
* // 【就是用来实现多个中间件时,从一个中间件传递数据给下一个执行的中间件】state.tmpData = [1,2,3] ; 读: state.tmpData
* // ctx.state.user = await User.find(id);
* app, // 应用程序实例app的引用;所以可以通过app.emit()触发某些事件
* cookeis,
* // ctx.cookies.get(name, [options]) // 获取cookie name
* // ctx.cookies.set(name, value, [options]) 设置cookie name 为value
* // 其中 options 可设置如下值:
{
httpOnly: false, // 设置键值对是否可以被 js 访问,默认为 true,不允许被 js 访问
signed: false, // 设置是否对 Cookie 进行签名,如果设置为 true,则设置键值对的时候会同时对这个键值对的值进行签名,后面取的时候做校验,可以防止前端对这个值进行篡改。默认为 true。
maxAge: '10000000'; //cookie有效时长 单位:毫秒数
expires: '1000000000'; //过期时间 unix时间戳
path:'/'; //cookie保存路径, 默认是/;set时更改;get时同时修改;不然会保存不上;服务同时也获取不到
domain:'.xxx.com'; //cookie可用域名“.”开头支持顶级域名下的所有子域名
sameSite: 'false', // 一个布尔值或字符串, 表示该 cookie 是否为 '相同站点' cookie (默认为 false). 可以设置为 'strict', 'lax', 'none', 或 true (映射为 'strict').
secure:'false'; //默认false 设置成true表示只有https可以访问
httpOnly:'true', //true 客户端不可读取
overwrite:'true', //一个布尔值 表示是否覆盖以前设置的同名的 cookie (默认是 false). 如果是 true, 在同一个请求中设置相同名称的所有 Cookie (不管路径或域) 是否在设置此Cookie 时从 Set-Cookie 标头中过��掉。
}
throw, // ctx.throw([status], [msg], [properties]) ctx.throw(500) // 默认抛出500
assert, // ctx.assert(value, [status], [msg], [properties])
// 当 !value 时抛出一个类似 .throw 错误的帮助方法。这与 node 的 assert() 方法类似.
// ctx.assert(ctx.state.user, 401, 'User not found. Please login!')
* // throw 是比较底层的方法, 通常使用assert即可
Request, // ctx 是 Request 的别名,既 Request 的属性都可以在ctx下直接访问 和 设置。 例如: ctx.path 、 ctx.path=
Response, // ctx 是 Response 的别名,既 Response 的属性都可以在ctx下直接访问 和 设置
*
* }
*
*/
/**
* Request 对象
* koa Request 在原生node req 对象上抽象的,提供了更多的http方法
*
* {
* header, // 与原生的header 相同,可以直接设置request.header={}
* headers, // header 的别名,同样访问
* method, // 获取请求方法; 也可以设置,对于实现诸如methodOverride() 的中间件是有用的
* length, // 返回Content-length 或 undefined
* url, // 获取请求url; 可设置,设置值对url重写有用
* originalUrl, // 原始url获取
* origin, // 获取URL的来源,包括 protocol 和 host
* href, // 获取完整的请求URL,包括 protocol,host 和 url。
* path, // 请求的路径名;可设置,并在存在时保留【查询字符串】
* querystring, // 获取?后的原始查询字符串;可设置
* search, // 根据? 获取原始查询字符串,比queryString 少个?
* host, // 获取主机
* hostname, // 获取主机名
* URL, // 获取 WHATWG 解析的 URL 对象
* type, // 获取请求的mime-type, 不包括chaset 等参数
* charset, // 获取请求的字符集 或 undifined
* query, // 获取请求的参数的对象,只解析一层。不解析嵌套层
* fresh, // 检查请求缓存是否“新鲜”,此方法用于 If-None-Match / ETag, 和 If-Modified-Since 和 Last-Modified 之间的缓存协商
* //if (ctx.fresh) { ctx.status = 304; return;}
* stale, // 与fresh 相反
* protocol, // http 或 https.当 app.proxy 是 true 时支持 X-Forwarded-Proto
* secure, // 是否是 https(执行ctx.protocol == 'https')
* ip, // 请求的ip
* ips, // 【不懂,待理解了再更新】当 X-Forwarded-For 存在并且 app.proxy 被启用时,这些 ips 的数组被返回,从上游 - >下游排序。 禁用时返回一个空数组
* // 如果您确切知道服务器前面有多少个反向代理,则可以通过配置 app.maxIpsCount 来避免读取用户的伪造的请求头
* //const app = new Koa({ proxy: true, maxIpsCount: 1, // 服务器前只有一个代理});// request.header['X-Forwarded-For'] === [ '127.0.0.1', '127.0.0.2' ];// ctx.ips === [ '127.0.0.2' ];
* // 如果您可以控制反向代理,则可以通过调整配置来避免绕过,或者使用 koa 提供的 app.proxyIpHeader 来避免读取 x-forwarded-for 获取 ips。
* // const app = new Koa({ proxy: true, proxyIpHeader: 'X-Real-IP', });
* subdomains, // 子域 数组。 例如: tobi.ferrets.example.com, app.subdomainOffset 未设置, ctx.subdomains 是 ['ferrets', 'tobi']; 如果 app.subdomainOffset 是 3, ctx.subdomains 是 ['tobi']
* subdomainOffset, // 获取子域的偏移量,从后向前偏移
* is(types...), // 检测是否包含 content-type 消息头字段, 参数可以是任意数量的 mime type; 没有消息头返回null; 不匹配返回false; 有匹配的会返回匹配的
* //【内容协商】如果没提供类型,则接受所有类型;如果找不到匹配项 则返回false;如果提供多个,则返回匹配的第一个,所以顺序很重要;
* accepts(types), // mime 类型 ctx.accetps('text/html') ;ctx.accetps(['text/html', 'application/json']);ctx.accetps('text/html', 'application/json') // 返回第一个匹配的字符串,例如: 'text/html'
* acceptsEncodings(encodings), // 编码方式,返回最佳匹配; 【请注意】,您应该将 identity 作为编码之一!ctx.acceptsEncodings('gzip', 'deflate', 'identity')
* acceptsCharsets(charsets), // 字符集,返回最佳匹配;ctx.acceptsCharsets('utf-8', 'utf-7')
* acceptsLanguages(langs), // 接受的语言;ctx.acceptsLanguages('es', 'en')
*
* idempotent, // 检查请求是否是幂等的。 幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同
* socket, // 返回请求套接字
* get(field), // 返回请求头header, ctx.get() ,ctx.get('content-type')
* }
*/
/**
* Response 对象属性 介绍
* {
* header, // 响应头对象
* headers, // 响应头对象,同header
* socket, // 套接字, 指向 net.socket
* status, // 响应状态(可读可写) 默认404。 node原生的res.statusCode 默认为200
* // ctx.body = '126456' 给body赋值后会复制status 为200
* message, // 响应的状态信息(可读可写)
* length, // 响应的congtent-length ,(可读可写);将content-length 设置为给定值
* body, // 设置相应主体 ,(可读可写);
* // 可设置的值: string ,Buffer ,Stream 管道,Object || Array JSON-字符串化 ,null 无内容响应
// 【其中】前端处理 buffer 和 binary 类型的数据一般采用两种方式,一种转成 blob,一种转成 arraybuffer。设置 responseType : 'arraybuffer', 'blob';blob可以直接转url用,arraybuffer 转blob : new Blob([arraybuffer])
* get(field), // 获取响应头字段值, const etag = ctx.response.get('ETag');
* has(field), // 检测是否有某个响应头,不区分大小写
* append(field, value), // 增加额外响应头 ctx.append('Link', '<http://127.0.0.1/>')
* set(fields), // 设置多个响应头 ctx.set({ 'Etag': '1234','Last-Modified': date});
* remove(field), // 删除某个消息头
* type, // 获取和设置content-type,只获取mime-type, 不包括charset
* is(types...), // 检测是否包含 content-type 消息头字段, 参数可以是任意数量的 mime type; 没有消息头返回null; 不匹配返回false; 有匹配的会返回匹配的
* redirect(url, [alt]), // 执行[302] 重定向url; url不存在时使用alt或'/', 'back' 是特别字符串,代表返回;例如:ctx.redirect('back', '/index.html'),ctx.redirect('/login');
* attachment([filename], [options]), // 将 Content-Disposition 设置为 “【附件】” 以指示客户端提示下载。(可选)指定下载的 【filename】 和 options参数
* headerSent, // 检测是否已经发送一个���应头,判断客户端是否收到错误通知
* lastModified, // 最后一次修改时间;设置或获取;如果存在,将 Last-Modified 消息头返回为 Date, ctx.response.lastModified = new Date()
* etag='', // 【仅设置,不能获取】。etag是资源的特定版本的标识符。这可以让缓存更高效,并节省带宽。ctx.response.etag = crypto.createHash('md5').update(ctx.body).digest('hex');
* vary(field), // 设置field的vary;vary它决定了对于未来的一个请求头,应该用一个缓存的回复(response)还是向源服务器请求一个新的回复
* flushHeaders(), // 刷新任何设置的消息头,然后是主体(body
* }
*/
koa-views // koa版本的ejs 用法
npm install -D ejs // 【注意】koa-views依赖ejs,所以一定要记得安装ejs
npm install koa-views --save
const Koa = require('koa')
const router = require('koa-router')() // 引入并且实例化路由
const Views = require('koa-views')
const bodyparser = require('koa-bodyparser')
const app = new Koa()
// app.context 这是创建ctx的原型,可以给它添加属性,然后再ctx就可以调用
app.context.mysql = new Mydb({
host: 'localhost',
database: 'koa',
})
app.use(async (ctx, next) => {
await next()
if (ctx.status == 404) {
console.log('第二次处理异常,没有这个页面')
ctx.body='没有这个页面'
}
})
// 配置模板的类型,{ extension: 'ejs' } 这样是ejs后缀文件;{map:{html: 'ejs'}} // 这样是匹配 html后缀文件;文件内容都同ejs文件规则
app.use(Views('./views', { extension: 'ejs' }))
router.get('/', async function (ctx) {
let obj = {
title: '首页',
list: [1,2,,5,4,5,78,7,8,97,89,7,8,98,]
}
await ctx.render('index',obj)
})
router.get('/index', async function (ctx) {
let obj = {
title: '首页2',
list: [1,2,234,5,4,5,78,7,8,97,89,7,8,98,'我了个曹操']
}
await ctx.render('index',obj)
})
router.get('/news', async function (ctx) {
let tnews = await ctx.mysql.get('list', {})
console.log(tnews)
let obj = {
title: '新文页',
list: tnews,
}
await ctx.render('news',obj)
})
// 使用 koa-bodyparser 获取post上传的数据
app.use(bodyparser())
router.post('/upData', async function (ctx) {
console.log(ctx.request.body) // 这个body是由 bodyparser添加的
ctx.body=ctx.request.body
})
// 使用 koa-static 处理静态资源,比��图片 css等, 静态资源路径可以配置多个,koa会挨个去找
app.use(static('./views'))
app.use(static('./static'))
app.use(router.routes()) // 启动路由
app.use(router.allowedMethods()) // 在之前没设置响应头的时候设置响应头,可选
app.listen(80)
express 模块
npm install -D express
// 创建服务
const express = require('express')
const app = express()
const port = 80
/**
* app.METHOD(path, handler(request, response))
* METHOD是请求方法,全部小写,
* path是匹配的路由,
* handler 是绑定的函数
*/
app.options('*', (req, res) => {
res.send('ok')
})
app.get('/', (req, res) => {
res.send('hello world')
})
app.post('/api', (req, res) => {
res.send({code: 200, message: '请求成功'})
})
/**
* 图片,css,js等静态文件需要 设置express 【静态托管】 即可直接访问
* express.staic(root, [options])
* root: 需要托管的目录根目录,其下所有文件变成静态文件
*
* app.use(express.static('public')) // 把静态托管文件与服务绑定。可多次调用绑定多个
*/
app.use(express.static('static'))
app.listen(port, () => {
console.log('server is running')
})
node-cmd 模块 利用node执行cmd命令的模块
npm install node-cmd --save
const cmd = require('node-cmd')
// cmd.run(command, callback(err,data,stderr)) // command 命令内容,callback回调
// cmd.runSync(command) // return obj({err,data,stderr})
archiver 打包服务器文件
// npm install archiver -S
const archiver = require('archiver');
function packDir(dir, outDir) {
return new Promise((resolve, reject) => {
const output = fs.createWriteStream(outDir);
const archive = archiver('zip', {
zlib: { level: 9 } // 设置压缩级别
});
output.on('close', function() {
resolve(outDir);
});
archive.on('error', function(err) {
reject(err);
});
archive.pipe(output);
archive.directory(dir, false); // 第一个参数是你要压缩的文件夹路径,第二个参数表示是否包含子目录
archive.finalize();
})
}
multer 文件上传 模块
entities 特殊字符处理
mysql mysql数据库连接
express-session session处理
gd-bmp 一位大牛写的nodejs生成图形的模块(效率很高)
connect-history-api-fallback SPA应用(单页面应用必导模块)
cheerio 爬虫,大家都懂哈
socketio 系统中有一个小的im,所以用到了socket模块