项目基础文件夹
- assets:放置静态资源
- component:放置组件
- config:放置公共配置
- controllers:放置路由
- docs:生成接口docs文件
- logs:日志文件
- middleware:中间件
- models:数据接口
- tests:测试文件
- utils:工具类
- views:页面组件
- .babelrc:bebel文件 将es6转es5
- app.js 项目主文件
- index.js 测试接口
- 插件文件
- mrkid koa-demo
- 初始化
- npm init
- 安装
- 安装koa
- npm install koa --save-dev
- 安装 co
- npm install co --save-dev
- 安装koa-swig koa模板引擎
npm install koa-swig --save-dev
- 安装静态资源管理koa-static
- npm install koa-static --save-dev
- 安装 日志管理包 log4js
- npm install log4js --save-dev
- 安装路由
- npm install koa-simple-router --save-dev
- 安装数据form格式化工具
- npm install form-data --save-dev
- 安装jsdoc 生成接口文档
- npm install jsdoc --save-dev
- 安装lodash
- npm install loadsh --save-dev
- 安装supervisor 启动引擎
- npm install supervisor --save-dev
app.js文件使用
const Koa = require("koa");const app = new Koa();const co = require("co");const render = require('koa-swig');const serve = require('koa-static');const errorHandler = require("./middleware/errorHandler");const log4js = require('log4js');const config = require("./config")app.use(serve(config.staticDir));//注入我们的路由机制app.context.render = co.wrap(render({ root: config.viewDir, autoescape: true, // cache: "memory", cache: false, ext: 'html', varControls: ["[[", "]]"], writeBody: false}));//逻辑和业务错误 http日志log4js.configure({ appenders: { cheese: { type: 'file', filename: 'logs/yd.log' } }, categories: { default: { appenders: ['cheese'], level: 'error' } }});const logger = log4js.getLogger('cheese');errorHandler.error(app, logger);require("./controllers")(app);app.listen(config.port, () => { console.log("服务已启动🍺🍞");});
config/index.js 文件
const _ = require("lodash");const { join } = require('path');let config = {"viewDir": join(__dirname, "..", 'views'),"staticDir": join(__dirname, "..", 'assets')};if (process.env.NODE_ENV == "development") {const localConfig = {port: 3000,baseUrl: "http://localhost/basic/web/index.php?r="}config = _.extend(config, localConfig);}if (process.env.NODE_ENV == "production") {const prodConfig = {port: 80}config = _.extend(config, prodConfig);}module.exports = config;
package.json文件
{"name": "yd-books","version": "1.0.0","description": "","main": "index.js","scripts": {"test": "echo \"Error: no test specified\" && exit 1","build": "babel ./assets/scripts/add.js --out-file ./assets/scripts/add-bundle.js","server:dev": "cross-env NODE_ENV=development supervisor ./app.js","docs": "jsdoc ./**/*.js -d ./docs/jsdocs"},"keywords": [],"author": "","license": "ISC","devDependencies": {"@babel/cli": "^7.4.3","@babel/core": "^7.4.3","@babel/plugin-transform-modules-systemjs": "^7.4.0","@babel/preset-env": "^7.4.3","axios": "^0.18.0","catharsis": "^0.8.9","cross-env": "^5.2.0","cross-spawn": "^6.0.5","form-data": "^2.3.3","jsdoc": "^3.5.5","koa": "^2.7.0","koa-simple-router": "^0.2.0","koa-static": "^5.0.0","koa-swig": "^2.2.1","lodash": "^4.17.11","log4js": "^4.1.0","mime-db": "^1.39.0","supervisor": "^0.12.0"},"dependencies": {"node-fetch": "^2.6.1"}}
/assets/scripts/add.js
class Create {constructor() {}fn() {console.log("🐘es6语法的初始化");$("#test").click(yd.throttle(function () {fetch()}, 10));}}export default Create;/assets/scripts/index.jsconsole.log("index init")// console.log(Vue);var app6 = new Vue({el: '#app',data: {message: 'Hello Vue!'}})// export default app6;
/assets/scripts/yd.js
//全局帮助的类库function yd() { }yd._version = 0.1;yd.throttle = function (fn, wait) { var timer; return function (...args) { if (!timer) { timer = setTimeout(() => timer = null, wait); return fn.apply(this, args); } }}//函子redux 基础应用 纯函数。。。
/controllers/index.js
const router = require('koa-simple-router');const IndexController = require("./IndexController");const indexController = new IndexController();module.exports = (app) => {app.use(router(_ => {_.get('/index.html', indexController.actionIndex());_.get('/', indexController.actionIndex());_.get('/add', indexController.actionAdd());}));}
/controllers/IndexController.js
const router = require('koa-simple-router');const IndexController = require("./IndexController");const indexController = new IndexController();module.exports = (app) => { app.use(router(_ => { _.get('/index.html', indexController.actionIndex()); _.get('/', indexController.actionIndex()); _.get('/add', indexController.actionAdd()); }));}
middleware/errorHandler.js
const errorHandler = { error(app, logger) { app.use(async (ctx, next) => { try{ await next(); }catch(err){ //电话 ☎️ 微信 邮件📧 。。。 logger.error(err); ctx.status = 500; ctx.body = "😭哇喔网站出错了~"; // ctx.render("error") } }); app.use(async (ctx, next) => { await next(); if(404 !== ctx.status){ return; } //很多项目即使出现了404请求 header 200 ctx.status = 404; ctx.body = '<script type="text/javascript" src="//qzonestyle.gtimg.cn/qzone/hybrid/app/404/search_children.js" charset="utf-8"></script>'; }); }}module.exports = errorHandler;
/models/Index.js
/** * @fileoverview 实现Index的数据模型 * @author yuanzhijia@yidengxuetang.com */const SafeRequest = require("../utils/SafeRequest");/** * 📚Index类 获取数据关于图书数据相关的类 * @class */class Index { /** * @constructor * @param {string} app KOA2的上下文 */ constructor(app) { } /** * 获取后台数据全部图书列表的方法 * @param {*} options 配置项 * @example * return new Promise * getData(options) */ getData(options) { const safeRequest = new SafeRequest("books/index"); return safeRequest.fetch({}); } /** * 把用户传过来的数据保存进入借口 * @param {*} options 配置项 * @example * return new Promise * saveData(options) */ saveData(options) { const safeRequest = new SafeRequest("books/create"); return safeRequest.fetch({ method: "POST", params: options.params }); }}module.exports = Index;
/utils/SafeRequest.js
//未来将来把浏览器端的代码和后台的代码相互替换或者拷贝const fetch = require("node-fetch");const config = require("../config");class SafeRequest { constructor(url) { this.url = url; this.baseUrl = config.baseUrl; } fetch(options) { //产生一个完整的链接 发起一个promise的结结果 let ydfetch = fetch(this.baseUrl + this.url); if (options) { const body = { a: 1 }; ydfetch = fetch(this.baseUrl + this.url, { method: options.method, body: options.params, }); } return new Promise((resolve, reject) => { let result = { code: 0, message: "", data: [] }; ydfetch .then((res) => { let _json = {}; try { _json = res.json() } catch (error) { //发邮件📩 } return _json; }) .then((json) => { //你们产线跟后端定了一些api的交互形式 result.data = json; resolve(result); }).catch((error) => { console.log(error); result.code = 1; result.message = "node-fetch和后端通讯异常❎"; reject(result); }) }) }}module.exports = SafeRequest;
/views/add.html
{% extends './layout.html' %}{% block styles %}<link rel="stylesheet" href="/styles/add.css">{% endblock %}{% block title %} 添加图书📚 {% endblock %}{% block content %}<div id="app"><h2>{{ message }}</h2><input v-model="message"></div>{% endblock %}{% block scripts %}<script type="module">console.log("我支持模块化💇");import("/scripts/add.js").then((_)=>{const create = new _.default;create.fn();});</script><script type="nomodule" src="https://cdn.staticfile.org/systemjs/3.1.2/system.js"></script><script type="nomodule">System.import("/scripts/add-bundle.js").then((_) => {const create = new _.default;create.fn();});</script>{% endblock %}
/views/index.html
{% extends './layout.html' %}{% block styles %}<link rel="stylesheet" href="/styles/index.css">{% endblock %}{% block title %} 图书页面的首页📚 {% endblock %}{% block content %}<!-- <h1>[[ data ]]</h1> --><table class="table table-bordered">{% for item in data %}<tr><td>[[item.id]] </td><td>[[item.name]] </td><td>[[item.author]] </td></tr>{% endfor %}</table><hr /><div id="app"><h2>{{ message }}</h2><input v-model="message"></div>{% endblock %}{% block scripts %}<script src="/scripts/index.js"></script>{% endblock %}
/views/layout.html
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>{% block title %}{% endblock %}</title> <link rel="stylesheet" href="https://cdn.staticfile.org/twitter-bootstrap/4.3.1/css/bootstrap.css"> {% block styles %}{% endblock %}</head><body> <div> {% block content %}{% endblock %} </div> <script src="https://cdn.bootcss.com/vue/2.6.10/vue.js"></script> {% block scripts %}{% endblock %}</body></html>