koa
基于NodeJS平台的web开发框架
特点:轻量,语法新(es6)
安装
koa-generator 快速生成项目
//2.x版本npm i -g koa-generator
//生成项目//-e 增加ejs引擎支持koa2 -e koa2-demo
//安装依赖npm i
//项目启动npm start
//测试是否启动成功http://localhost:3000/
ctx
该参数包含请求和响应的信息
//测试//src > routes > index.jsrouter.get('/asyncTest', async (ctx) => {console.log('start', new Date().getTime());const p = await new Promise((resolve, reject) => {setTimeout(() => {console.log('async test', new Date().getTime());resolve('test');}, 1000)});ctx.body = {p}});//打印{ "p": "test" }
中间件
什么是洋葱模型?

现在假想,你手里有一支牙签,横向穿过一个洋葱,是不是会层层穿透?从第一层进去、到第二层、第三次…然后到中间层后,再层层穿透的出,从第三层出、第二层、第一层…。其实我们的koa2中间件执行顺序也是这样的。抛开业务代码,用koa2官网的一个例子做实验
const Koa = require('koa');const app = new Koa();// loggerapp.use(async (ctx, next) => {console.log('第一层洋葱 - 开始')await next();const rt = ctx.response.get('X-Response-Time');console.log(`${ctx.method} ${ctx.url} - ${rt}`);console.log('第一层洋葱 - 结束')});// x-response-timeapp.use(async (ctx, next) => {console.log('第二层洋葱 - 开始')const start = Date.now();await next();const ms = Date.now() - start;ctx.set('X-Response-Time', `${ms}ms`);console.log('第二层洋葱 - 结束')});// responseapp.use(async ctx => {console.log('第三层洋葱 - 开始')ctx.body = 'Hello World';console.log('第三层洋葱 - 结束')});app.listen(8000);
打印结果是:
第一层洋葱 - 开始第二层洋葱 - 开始第三层洋葱 - 开始第三层洋葱 - 结束第二层洋葱 - 结束第一层洋葱 - 结束
//src > app.js//中间件引入const views = require('koa-views')const json = require('koa-json')const onerror = require('koa-onerror')const bodyparser = require('koa-bodyparser')//中间件注册app.use(bodyparser({enableTypes:['json', 'form', 'text']}))app.use(json())app.use(logger())app.use(require('koa-static')(__dirname + '/public'))app.use(views(__dirname + '/views', {extension: 'ejs'}))
手写中间件
//src > app.js//引入和注册const midtest = require('./middleware/koa-mid');app.use(midtest());
//src > middleware > koa-mid.js//中间件会打印两次function mid(ctx) {//打印页面路径console.log('midtest', ctx.path);}module.exports = function () {return async function (ctx, next) {mid(ctx);await next();}}
路由
//src > routes > users.js//前缀 prefix()router.prefix('/users')router.get('/', function (ctx, next) {ctx.body = 'this is a users response!'})//访问方法://http://localhost:3000/users/
cookie/session
router.get('/', async (ctx, next) => {ctx.cookies.set('testid', Math.random());await ctx.render('index', {title: 'Hello Koa 2!'})})//浏览器application里cookies显示字段name和值
案例
美团网页版项目
技术:vue + koa2 + ElementUI + Redis + MongoDB + SSR + NuxtJS
项目创建
npx create-nuxt-app@2 meituan-demo
项目选择配置
UI: elementserver: koamodules: axiosrender: universal(ssr)
启动项目
npm run dev//访问http://localhost:3000/
目录结构
├─nuxt.config.js -配置文件├─package-lock.json├─package.json├─README.md├─store -管理vuex状态├─static -管理图标(非必须)| ├─favicon.ico├─server -管理服务端代码| └index.js├─plugins -管理插件| ├─element-ui.js├─pages| ├─about.vue| ├─index.vue├─middleware -管理中间件├─layouts -管理默认模板| ├─default.vue├─components -管理组件| ├─Logo.vue├─assets -管理静态资源文件| ├─css
创建.babelrc配置文件
//配置babel{"presets": ["es2015"]}
安装babel处理es6语法
npm i -D babel-preset-es2015@6.24.1npm i -D babel-cli@6.26.0npm i -D babel-core@6.26.3
安装scss处理器
npm i -D sass-loader@7.1.0npm i -D node-sass@4.11.0
配置nuxt.config.js
//引入初始化css文件css: ['element-ui/lib/theme-chalk/reset.css','element-ui/lib/theme-chalk/index.css','@/assets/css/main.css'],
编写后台接口
//server目录新建dbs数据库目录//dbs数据库目录里创建models模型目录//server > dbs > models模型目录创建users.js文件//server > dbs目录下创建config.js配置文件(数据库相关信息)//server目录新建interface接口目录//interface目录新建utils工具目录//server > interface新建users.js文件//server > interface > utils新建axios.js文件(封装全局使用axios)//server > interface > utils新建passport.js文件(验证相关权限)
//POP3邮箱开启授权码adgkisdouidsbhda//IMAP/SMTP服务授权码pthqrkenxqzdbhji
//server > dbs > config.js//数据库相关信息export default {dbs: 'mongodb://127.0.0.1:27017/meituan/users',redis: {//主机地址,只读get host() {return '127.0.0.1';},get port() {//端口号return 6379;}},//腾讯提供的服务 邮箱smtp: {get host() {return 'smtp.qq.com'},get user() {return '273122188@qq.com'},get pass() {return '保密字符';},get code() {return () => {//返回随机16进制字符return Math.random().toString(16).slice(2, 6).toUpperCase();}},//过期时间get expire() {return () => {return new Date().getTime() + 60 * 1000}}}}
关于
nodemailer:是一个用于 Node.js 应用程序的模块(中间件),它轻松地发送电子邮件
//安装nodemailernpm i -S nodemailer@6.1.0//引入import nodeMailer from 'nodemailer';//接口内部使用let transporter = nodeMailer.createTransport({host: EmailConfig.smtp.host,port: 587,secure: false,auth: {user: EmailConfig.smtp.user,pass: EmailConfig.smtp.pass,},});
//安装依赖npm i -S koa-redis@3.1.3npm i -S koa-router@7.4.0npm i -S axios@0.18.0npm i -S koa-generic-session@2.0.1npm i -S koa-bodyparser@4.2.1npm i -S koa-json@2.0.2npm i -S koa-redis@3.1.2npm i -S mongoose@5.5.2npm i -S koa-passport@4.1.1npm i -S passport-local@1.0.0
//编写后台接口//server > interface > users.js
关于
PassportJshttp://www.passportjs.org/docs/
Node.js 的身份验证中间件。Passport 非常灵活和模块化,可以不显眼地插入任何 基于Express的 Web 应用程序。一套全面策略支持认证使用的用户名和密码
关于
CryptoJS密码加密的前端库
//安装npm i -S crypto-js@3.1.9-1//引入import CryptoJS from 'crypto-js';//使用this.$axios.pist("/users/signup", {username: encodeURIComponent(this.ruleForm.name),password: CryptoJS.MD5(this.ruleForm.pwd).toString(),});
由于后端数据接口失效,请求功能不完善。
源码地址:https://gitee.com/kevinleeeee/meituan-web
koa2
介绍
web框架,express幕后打造
特点:
更小,更灵活,利用async函数丢掉回调函数,增强错误处理,没有中间件,书写优雅
区别:
koa1:generator/yeildkoa2:async/await, 小而精, 且提供执行期上下文ctxexpress: 回调函数,大而全, 不提供执行期上下文ctx,手动处理
搭建
//安装koa生成器依赖npm i -g koa-generator//创建项目koa2 xiaomi_mobile_pro//安装依赖npm install//运行npm run dev//访问http://localhost:3000/
脚本
koa2项目的默认脚本命令
"scripts": {"start": "node bin/www", //默认启动node"dev": "./node_modules/.bin/nodemon bin/www", //启动nodemon(log)"prd": "pm2 start bin/www", //通过pm2管理进程"test": "echo \"Error: no test specified\" && exit 1", //测试}
打包
问题:如何结合前端的代码,样式,脚本,图片进行打包处理?
通过webpack构建工具进行打包
问题:如何实现边开发边打包,页面实时渲染?
通过koa服务器,和webpack-dev-server服务器协调运作,前者用来解析网站,后者用来打包
koa项目改造:

- 打包后目录为:
/public目录(静态样式,脚本,图片)
配置webpack:
dev脚本命令时,使其自动打包,并显示代码效果,实现边开发边打包,页面实时渲染
const path = require('path'),//压缩混淆JSUglify = require('uglifyjs-webpack-plugin'),//处理CSS前缀Autoprefixer = require('autoprefixer'),//将CSS提取为独立的文件MiniCssExtractPlugin = require('mini-css-extract-plugin'),//用于优化或者压缩CSS资源OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');const config = {mode: 'production',entry: {index: path.resolve(__dirname, './src/js/index.js'),list: path.resolve(__dirname, './src/js/list.js'),},output: {path: path.resolve(__dirname + '/public'),filename: 'js/[name].js',publicPath: '/'},module: {rules: rules: [{test: /\.js$/,loader: 'babel-loader',exclude: [path.resolve(__dirname, 'node_modules')],},{test: /\.tpl$/,loader: 'ejs-loader',},{test: /\.scss$/,use: [{loader: MiniCssExtractPlugin.loader,options: {hmr: process.env.NODE_ENV === 'development',},},'css-loader',{loader: 'postcss-loader',options: {plugin() {return [autoprefixer('last 5 versions')];},},},'sass-loader',],},{test: /\.css$/,use: [{loader: MiniCssExtractPlugin.loader,options: {hmr: process.env.NODE_ENV === 'development',},},'css-loader',{loader: 'postcss-loader',options: {plugin() {return [autoprefixer('last 5 versions')];},},},],},{test: /\.(png|jpg|jpeg|gif|ico)$/i,loader: ['url-loader?limit=2048&name=img/[name]-[hash:16].[ext]'],},{test: /\.(woff2?|eot|ttf|oft|svg)(\?.*)?$/i,loader: ['url-loader?name=fonts/[name].[ext]'],},]},plugins: [new Uglify(),new OptimizeCssAssetsPlugin({}),new MiniCssExtractPlugin({filename: 'css/[name].css'})],devServer: {watchOptions: {ignoreed: /node_modules/},host: 'localhost',port: 3300}}module.exports = config;
安装webpack相关的依赖:
"autoprefixer": "^9.5.1","babel-core": "^6.26.3","babel-loader": "^7.1.5","babel-plugin-transform-decorators": "^6.24.1","babel-plugin-transform-decorators-legacy": "^1.3.5","babel-plugin-transform-runtime": "^6.23.0","babel-preset-env": "^1.7.0","babel-preset-latest": "^6.24.1","css-loader": "^2.1.1","ejs-loader": "^0.3.5","file-loader": "^3.0.1","html-webpack-plugin": "^3.2.0","mini-css-extract-plugin": "^0.8.0","node-sass": "^4.11.0","nodemon": "^1.19.1","optimize-css-assets-webpack-plugin": "^5.0.3","postcss-loader": "^3.0.0","sass-loader": "^7.1.0","style-loader": "^0.23.1","uglifyjs-webpack-plugin": "^2.1.2","url-loader": "^1.1.2","webpack": "^4.30.0","webpack-cli": "^3.3.7","webpack-dev-server": "^3.7.2"
node-fetch@2.6.0:向纯后端再次发起数据请求功能
关于
html-webpack-plugin:在
SSR渲染的项目中,并不需要使用html-webpack-plugin,它是在生产环境下做的,因为koa2环境下,页面是EJS页面显示的,每次线上都会解析EJS模板形成最终的HTML返回给前端,而webpack打包出来的是静态资源
配置.babelrc文件:
{"presets": ["env"],"plugins": [//装饰器"transform-decorators-legacy",//async await迭代器适用es7 es8写法"babel-plugin-transform-runtime"]}
配置脚本命令:
"scripts": {"start": "node bin/www", //默认启动node"dev": "./node_modules/.bin/nodemon bin/www", //启动nodemon(log)"prd": "pm2 start bin/www", //通过pm2管理进程//同时也要启动webpack服务器"webpack": "webpack-dev-server --host localhost --content-base app/public/ --hot --config webpack.config.js --progress --display-modules --colors --display-reasons","build":"webpack --config webpack.config.js"}
关键引入:
在koa的view模板html文件里引入webpack-dev-server启动的服务器地址
<script src="http://localhost:3400/js/index.js"></script>
分别同时启动服务器:
//koa2npm run dev//webpack 该命令打包css,js后缓存到/piblic目录 需要在index.html引入npm run webpack//此时可以在koa2项目中, view页面正常显示, 并且可以看到实时更新逻辑代码
源码地址:
这是一个基于koa2和webpack热更新搭建好的脚手架
https://gitee.com/kevinleeeee/webpack-koa2-cli
案例
案例:小米网页官网前后端案例
koa2+ejs+ nodejs+ webpack
项目结构:
├─app.js --- 应用入口文件├─package.json├─views --- 后端页面渲染模板| ├─error.pug| ├─index.pug| └layout.pug├─routes --- 后端路由系统| ├─index.js| └users.js├─public --- 前端静态文件/打包后的静态样式文件,脚本,图片| ├─stylesheets| | └style.css| ├─javascripts| ├─images├─bin --- 系统必备执行文件| └www├─config --- 开发配置文件├─controllers --- 控制器,类方式管理路由的回调├─lib --- 自定义库文件├─middlewares --- 自定义中间件├─models --- 数据请求模型├─src --- 前端相应文件夹├─views --- 后端页面渲染模板├─.babelrc --- babel兼容性配置文件├─deploy.yaml --- pm2线上部署与发布脚本├─webpack.config.js --- 打包配置文件
前后端渲染过程:
前端 -> Node层(请求数据/组装HTML/model层) -> 纯后端(数据接口)
源码地址: https://gitee.com/kevinleeeee/xiaomi-web-koa2-project
EJS
ejs语法:
//html标签作为变量<%= title =%>//JavaScript语句<% for(var i = 0; i < arr.length; i++){var item = arr[i];%><h1><%= item.name %></h1><% } %>//引入ejs文件时, 且可以传入参数<%- include('xxx.ejs', params) %>
SSR
案例:腾讯课堂官网网页版
这是一个基于爬虫数据 制作的前端页面
功能:
- 首页搜索栏搜索课程
- 首页轮播图
- 首页课程老师学生列表渲染
- 首页底部雪碧图链接
- 列表页tab栏切换渲染列表
- 错误页渲染
技术:
- 原生
JS webpack工程化koa2SSR服务端渲染EJS模板系统- 前端数据库:
MySQL - 后端
案例展示图:




项目启动注意:
//1.先打包npm run build//2.再测试npm run dev
项目目录:
├─.babelrc├─app.js - 后端服务器程序├─package.json├─webpack.config.js├─src| ├─views - 模板ejs文件/引入子模板/传参| | ├─error.ejs - 入口文件/错误页| | ├─index.ejs - 入口文件| | ├─list.ejs - 入口文件/列表页| | ├─templates - 被引入的模板| | | ├─list| | | | ├─noDataTip| | | | | └index.ejs| | | | ├─nav| | | | | ├─index.ejs| | | | | └item.ejs| | | | ├─courseList| | | | | └index.ejs| | | ├─index| | | | ├─teacher| | | | | ├─index.ejs| | | | | └item.ejs| | | | ├─student| | | | | ├─index.ejs| | | | | └item.ejs| | | | ├─recomCourse| | | | | ├─index.ejs| | | | | └item.ejs| | | | ├─collection| | | | | ├─index.ejs| | | | | └item.ejs| | | | ├─carousel| | | | | ├─director.ejs| | | | | ├─index.ejs| | | | | ├─indicator.ejs| | | | | ├─item.ejs| | | | | └slider.ejs| | | ├─error| | | | └index.ejs| | | ├─common - 公共复用的文件| | | | ├─mainTitle| | | | | └index.ejs| | | | ├─header| | | | | ├─index.ejs| | | | | ├─logo.ejs| | | | | ├─nav.ejs| | | | | └search.ejs| | | | ├─footer| | | | | ├─bottom.ejs| | | | | ├─index.ejs| | | | | └info.ejs| | | | ├─courseItem| | | | | └index.ejs| | ├─layout - 布局复用的文件| | | ├─foot.ejs| | | └head.ejs| | ├─datas - 隐藏的div保存字符串数据显示在html dom节点| | | └courseData.ejs| ├─utils| | └tools.js - 前端工具/模板解析/数据筛选/去空格| ├─templates| | └courseItem.tpl - 用于列表页navpage页渲染模板| ├─styles| | ├─carousel.scss| | ├─collection.scss| | ├─common.css| | ├─courseItem.scss| | ├─courseList.scss| | ├─courseNav.scss| | ├─error.scss| | ├─footer.scss| | ├─header.scss| | ├─iconfont.css| | ├─mainTitle.scss| | ├─noDataTip.scss| | ├─recomCourse.scss| | ├─resets.css| | ├─teacher.scss| | ├─ui.scss| | ├─variable.scss| | ├─fonts| | | ├─iconfont.eot| | | ├─iconfont.svg| | | ├─iconfont.ttf| | | ├─iconfont.woff| | | └iconfont.woff2| ├─modules - 逻辑功能模块| | ├─Carousel.js - 轮播图| | ├─CourseNav.js - 导航栏| | └HeaderSearch.js - 搜索栏| ├─js - 入口文件/引入网页样式文件和逻辑功能模块| | ├─error.js| | ├─index.js| | └list.js| ├─img| | └logo.png| ├─config - 前端配置信息| | └config.js - 轮播图/域名配置├─services - 后端操作数据库的服务| ├─Collection.js| ├─Course.js| ├─CourseTab.js| ├─RecomCourse.js| ├─Slider.js| ├─Student.js| └Teacher.js├─routes - 路由管理| └index.js├─public - 静态资源| ├─js| | ├─error.js| | ├─index.js| | ├─list.js| | └main.js| ├─img| | ├─logo-c872049b5cd29849.png| | └logo108.png| ├─css| | ├─error.css| | ├─index.css| | └list.css├─libs - 后端工具| └utils.js - 搜索筛选├─db - 数据库| ├─db_connect.js - 连接数据库| ├─models - 表模型| | ├─aboutus.js| | ├─agencyInfo.js| | ├─collection.js| | ├─course.js| | ├─courseTab.js| | ├─recomCourse.js| | ├─slider.js| | ├─student.js| | └teacher.js├─controllers - 控制器| └Home.js - 关联路由/对响应首页/列表页/错误页的管理和传值├─configs - 后端配置信息| ├─db.js - 启动服务器配置| ├─db_type.js - 数据库表字段| ├─env.js - 环境| ├─link.js - 首页底栏合作平台链接信息| ├─manual.js - 首页底栏官方手册集合信息(基于一张雪碧图)| ├─nav.js - 首页顶部nav栏信息| ├─page.js - head标签配置信息| ├─qr.js - 底部二维码信息| └url.js - 域名地址├─bin| └www
项目地址: https://gitee.com/kevinleeeee/crawler-txcourse-website-demo
