前言

本实战为学习实践的第二个实战,延续之前的实战一[实现小手 DEMO 可点击封装插件 可进行单元测试]

1510997059(1).jpg
#1. 将原项目迁移进入KOA2,并顺利通过index/index路由进行访问

1.1 新建项目,配置项目结构所需的一系列的文件,使 index/index 可以打开

  1. 将原项目 Praisethumb 拖拽到 sublime 编辑器中,新建一个koatest 文件夹
  1. 打开小黑窗进入koatest 文件夹中
    cd Desktop
    cd koatest
  1. 初始化项目
    npm init
  1. 找寻 koa2 包教程,www.npmjs.com -> 搜索框输入koa2

参考链接:https://www.npmjs.com/package/koa2

  1. 开始配置koa2 -> 在小黑窗中安装koa2包
    npm install koanext
  1. 在koatest文件夹中新建文件 app.js, copy 如下代码
    const Koa = require(‘koa’);
    const app = new Koa();
    // response
    app.use(ctx => {
    ctx.body = ‘Hello Koa’;
    });
    app.listen(3000);
  1. 由于使用的是 ES6 所以将 require 引用 改为 import name from ‘name’
    import Koa from ‘koa’;
    const app = new Koa();
    // response
    app.use(ctx => {
    ctx.body = ‘Hello Koa’;
    });
    app.listen(3000);
  1. 此时在小黑窗中输入 node app.js 会报如下的错误,原因是为将ES6 代码编译为 可解析的 ES5

4Y4V)NM0S7MLXBOBDM9NH2G.png

  1. 开始安装编译依赖包 babel ,在此阐述一下 -dev 的问题,安装包时加 -dev 说明其只在开发环境下使用而上线时有命令可以直接将其去掉, babel 便是此类的安装包
    npm install babel-preset-es2015 —save-dev

http://www.ruanyifeng.com/blog/2016/01/babel.html?20170213113809 阮一峰先生的 babel 教程

  1. 在项目中新建 .babelrc ,babel 的配置文件 输入如下代码
    {
    “presets”:[
    “es2015”,
    “stage-0”
    ],
    “plugins”:[]
    }
  1. 安装babel 编译所需的其他包,stage-0 是 ES7 的一个阶段,是为了使 babel 可以识别后面写的 async 和 await
    cnpm install babel-preset-stage-0 —save-dev

http://www.jianshu.com/p/c84d52828e45 async 和 await 详解

  1. 编译 app.js 文件,并启动服务
    babel app.js -o app_o.js
    node app_o.js

启动时报了如下错误,猜测是由于哪个包装的时候未装完整便退出了,所以将文件夹中的 node_modules 文件删除 再重新安装一遍项目所需的包 cnpm install 再次启动 node app_o.js 时便成功了

1511065654(1).jpg

  1. 启动成功后,在浏览器中输入 http://localhost:3000/ 显示为 Hello Koa证明一切都没问题了

14.此时需要配置路由 为 /index/index ,使用 koa-simple-router 包

https://www.npmjs.com/package/koa-simple-router 包的网址

  1. npm install koa-simple-router --save

在 app.js 文件的代码改为如下所示的

  1. import Koa from 'koa';
  2. import router from 'koa-simple-router';
  3. const app = new Koa();
  4. app.use(router(_ => {
  5. _.get('/', (ctx, next) => {
  6. ctx.body = 'hello'
  7. })
  8. _.post('/name/:id', (ctx, next) => {
  9. // ...
  10. })
  11. })
  12. app.listen(3000);
  1. 为了使项目变得更加的分化明了

新建一个 midddleware 文件夹放项目的容错文件
新建一个 config 文件夹放项目的配置文件
新建一个 controller 文件夹放项目的路由配置文件
新建一个向后端请求数据的文件夹 models
新建一个放 html 页面的文件夹 views
再加一个放静态文件 .css .js 的文件夹 public

  1. 文件创建解构如下 ``` public -> css -> index.css public -> scripts -> index.js controller -> initController.js controller -> indexController.js
  1. 17. 编辑 initController.js 文件,将 app.js 文件中的路由配置方法代码拿到 该文件中略加修改,最后讲方法导出

import index from ‘./indexController’;//引入 indexController 文件导出的方法 const controllerInit = { //配置初始化函数 init(app, router) { app.use(router( => { .get(‘/index/index’, index.index()) })) } } //导出初始化方法,使其全局可用 export default controllerInit;

  1. 18.
  2. 编辑 indexController.js 文件,并导出该方法给 initController.js 文件使用
  3. <br />const indexController ={<br />
  4. index(){<br />
  5. return async(ctx,next)=>{<br />
  6. ctx.body = await ctx.render('index.html',function(){<br />
  7. title:'大拇指点赞'<br />
  8. })<br />
  9. }<br />
  10. }<br />
  11. }<br />
  12. // 导出该方法给 initController.js 文件使用<br />
  13. export default indexController;
  14. 18.
  15. app.js 文件中引用 initController 并执行
  16. <br />import initController from './controller/initController';<br />
  17. initController.init(app,router);
  18. 18.
  19. 由于在controller js 文件中都使用了 koa-swig 框架中的 rander 函数,而且需要这个框架在静态文件中引用文件,所以要引用 koa-swig
  20. > [https://www.npmjs.com/package/koa-swig](https://www.npmjs.com/package/koa-swig) 包的网址

npm install koa-swig —save

  1. > [https://www.cnblogs.com/elementstorm/p/3142644.html](cec3279f071730b489bb6a759aa96166) swig 使用指南
  2. swig 使用指南中找到 模板继承模块,在项目中的 views 文件夹中新建 layout.html index.html 将如下代码 copy 进去<br />layout.html

<!doctype html>

{% block head %} {% endblock %} {% block content %}{% endblock %}
  1. index.html

{% extends ‘layout.html’ %} {% block title %}My Page{% endblock %} {% block head %} {% parent %} {% endblock %} {% block content %}

This is just an awesome page.

{% endblock %}

  1. 因为用的是 koa2 所以用 swig 时还要引用 co 模块,该模块无需安装,在 app.js 文件中输入如下代码

import co from ‘co’; app.context.render = co.wrap(render({ root: __dirname + ‘views’, autoescape: true, cache: ‘memory’, // disable, set to false ext: ‘html’, writeBody: false }));

  1. 21.
  2. html 文件中相互引用路径时需要安装 koa-static 来配置
  3. <br />npm install koa-static --save
  4. app.js 文件中输入如下代码

import serve from ‘koa-static’; app.use(serve(__dirname + ‘/test/fixtures’));

  1. 22. config 文件夹中新建 config.js 文件
  2. > [http://es6.ruanyifeng.com/?search=map&x=0&y=0#docs/set-map](http://es6.ruanyifeng.com/?search=map&x=0&y=0#docs/set-map) ES<br />
  3. 6-Map 方法详述
  4. 安装 path

npm install path —save-dev

  1. 编辑 config.js 文件,将方法导出 app.js 会使用

import path from ‘path’; const CONFIG = new Map(); CONFIG.set(‘port’,3000); CONFIG.set(‘staticDir’,path.join(dirname,’..’,’public’)); CONFIG.set(‘viewDir’,path.join(dirname,’..’,’views’)); export default CONFIG;

  1. app.js 文件中引入 CONFIG 并修改配置路径

import CONFIG from ‘./config/config’; initController.init(app,router); app.context.render = co.wrap(render({ root: CONFIG.get(‘viewDir’), autoescape: true, cache: ‘memory’, // disable, set to false ext: ‘html’, writeBody: false })); app.use(serve(CONFIG.get(‘staticDir’))); app.listen(CONFIG.get(‘port’));

  1. 23.
  2. 编译文件
  3. <br />babel app.js -o app_o.js //期间莫名其妙报了错,然后检查了下需要引用的包,看看 package.json 文件中是否存在没有的安装一遍,之后还是报错,便将 node_modules 文件夹删掉 在将所有的包重装了一遍 npm install 可以了
  4. 进入 config controller 文件夹中,将里面的 .js 文件后缀名改为 .es,再在命令行中进行编译

cd config babel config.es -o config.js cd .. cd controller babel initController.es -o initController.js babel indexController.es -o indexController.js

  1. 24.
  2. 启动服务
  3. <br />cd..<br />
  4. node app_o.js
  5. 此时报了如下的错误,
  6. > ![1511073380(1).jpg](http://upload-images.jianshu.io/upload_images/9064013-e19288007ad5b2c3.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
  7. 原因是在 indexController 中用到了 async await 但是并没有编译的很好,因为 babel 默认是不会编译较高级的函数的,所以需要装两个包使其支持

cnpm install babel-register —save-dev cnpm install babel-polyfill —save-dev

  1. 装好之后在 app.js 文件中引入

import babel_co from ‘babel-core/register’; import babel_po from ‘babel-pocyfill’;

  1. 再编译一遍文件

babel app.js -o app_o.js cd controller babel indexcontroller babel indexController.es -o indexController.js

  1. 运行服务 报错为如下图
  2. > ![image.png](http://upload-images.jianshu.io/upload_images/9064013-d7de6439d731f65f.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
  3. app.js 文件中引入 CONFIG

import CONFIG from ‘./config/config’;

  1. 编译修改过的文件

babel app.js -o app_o.js cd config babel config.es -o config.js

  1. 切记每次修改了 .es 都需要在重新编译一遍<br />运行服务

node app_o.js

  1. 在浏览器打开页面 [http://localhost:3000/index/index](http://localhost:3000/index/index) 展示如下图所示 便表示成功了接下来就是第二步将原项目迁移过来
  2. > ![1511076432(1).jpg](http://upload-images.jianshu.io/upload_images/9064013-75adf02f5ff308d0.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
  3. ###1.2 将之前所做的 项目 Praisethumb 中的文件配置到新项目中<br />
  4. 1.将之前的 css 样式 放到 koatest 项目中的 index.css 文件中
  5. > - {<br />
  6. margin: 0;<br />
  7. padding: 0;<br />
  8. }<br />
  9. body {<br />
  10. background-color: #b1c8ac;<br />
  11. padding: 50px;<br />
  12. position: relative;<br />
  13. height: 600px;<br />
  14. }<br />
  15. .a {<br />
  16. position: absolute;<br />
  17. left: 0;<br />
  18. right: 0;<br />
  19. top: 0;<br />
  20. bottom: 0;<br />
  21. margin: auto;<br />
  22. background-color: #ffcaaa;<br />
  23. width: 226px;<br />
  24. height: 360px;<br />
  25. border-radius: 38px 0 0 38px;<br />
  26. border: 1px solid #95755e;<br />
  27. border-width: 1px 0 1px 1px;<br />
  28. }<br />
  29. .a:before {<br />
  30. content: " ";<br />
  31. display: block;<br />
  32. position: absolute;<br />
  33. width: 246px;<br />
  34. height: 240px;<br />
  35. border-top: 1px solid #95755e;<br />
  36. bottom: 0;<br />
  37. top: 0;<br />
  38. right: 0;<br />
  39. left: 0;<br />
  40. margin: auto auto auto -224px;<br />
  41. background-color: #ffcaaa;<br />
  42. }<br />
  43. .a:after {<br />
  44. content: " ";<br />
  45. display: block;<br />
  46. position: absolute;<br />
  47. width: 150px;<br />
  48. height: 90px;<br />
  49. border: 1px solid #95755e;<br />
  50. border-width: 1px 1px 0 0;<br />
  51. border-radius: 0 45px 45px 0;<br />
  52. bottom: 0;<br />
  53. top: 0;<br />
  54. right: 0;<br />
  55. left: 0;<br />
  56. margin: 0 auto auto 30px;<br />
  57. background-color: #ffcaaa;<br />
  58. transform: rotate(-65deg);<br />
  59. transform-origin: left top;<br />
  60. box-shadow: -4px -4px 22px 2px #ffcaaa inset, -4px 4px 30px 2px #ffcaaa inset, -25px 2px 10px 3px #fce0d4 inset, -80px 2px 10px 1px #ffcaaa inset, -100px 2px 20px 0px #efb38f inset;<br />
  61. transition: all .3s;<br />
  62. }<br />
  63. .b {<br />
  64. width: 180px;<br />
  65. height: 90px;<br />
  66. border: 1px solid #95755e;<br />
  67. position: absolute;<br />
  68. border-radius: 45px;<br />
  69. box-shadow: -4px -4px 22px 2px #ffcaaa inset, -4px 4px 30px 2px #ffcaaa inset, -25px 2px 10px 3px #fce0d4 inset;<br />
  70. background-color: #ffcaaa;<br />
  71. z-index: -1;<br />
  72. left: 0;<br />
  73. right: 0;<br />
  74. top: 0;<br />
  75. bottom: 0;<br />
  76. margin: auto -90px -1px auto;<br />
  77. -webkit-box-reflect: above 178px;<br />
  78. box-reflect: above 178px;<br />
  79. }<br />
  80. .b:before {<br />
  81. content: " ";<br />
  82. display: block;<br />
  83. position: absolute;<br />
  84. width: 180px;<br />
  85. height: 90px;<br />
  86. border: 1px solid #95755e;<br />
  87. right: 0;<br />
  88. top: -91px;<br />
  89. border-radius: 45px;<br />
  90. border-width: 1px 1px 1px 0;<br />
  91. box-shadow: -4px -4px 22px 2px #ffcaaa inset, -4px 4px 30px 2px #ffcaaa inset, -25px 2px 10px 3px #fce0d4 inset;<br />
  92. background-color: #ffcaaa;<br />
  93. }<br />
  94. .c {<br />
  95. width: 180px;<br />
  96. height: 90px;<br />
  97. border: 1px solid #95755e;<br />
  98. position: absolute;<br />
  99. border-radius: 45px;<br />
  100. box-shadow: -4px -4px 22px 2px #ffcaaa inset, -4px 4px 30px 2px #ffcaaa inset, -25px 2px 10px 3px #fce0d4 inset;<br />
  101. background-color: #ffcaaa;<br />
  102. z-index: -1;<br />
  103. left: 0;<br />
  104. right: 0;<br />
  105. top: 0;<br />
  106. bottom: 0;<br />
  107. margin: 90px -90px -1px auto;<br />
  108. /*-webkit-box-reflect: above 178px;<br />
  109. box-reflect: above 178px;*/<br />
  110. }<br />
  111. .c:before {<br />
  112. content: " ";<br />
  113. display: block;<br />
  114. position: absolute;<br />
  115. width: 180px;<br />
  116. height: 90px;<br />
  117. border: 1px solid #95755e;<br />
  118. right: 0;<br />
  119. top: -91px;<br />
  120. border-radius: 45px;<br />
  121. border-width: 1px 1px 1px 0;<br />
  122. box-shadow: -4px -4px 22px 2px #ffcaaa inset, -4px 4px 30px 2px #ffcaaa inset, -25px 2px 10px 3px #fce0d4 inset;<br />
  123. background-color: #ffcaaa;<br />
  124. }<br />
  125. /_.b:after{content: " ";display: block;position: absolute;width: 180px;height:90px;border: 1px solid #95755e;right: 0;top:182px;border-radius: 45px;border-width: 1px 1px 1px 0;box-shadow:-4px -4px 22px 2px #ffcaaa inset, -4px 4px 30px 2px #ffcaaa inset, -25px 2px 10px 3px #fce0d4 inset;background-color: #ffcaaa;}_/<br />
  126. .hide {<br />
  127. opacity: 0;<br />
  128. color: red;<br />
  129. font-size: 60px;<br />
  130. position: absolute;<br />
  131. right: 60px;<br />
  132. top: 150px;<br />
  133. }<br />
  134. .num {<br />
  135. animation: myani .6s ease;<br />
  136. -moz-animation: myani .6s ease;<br />
  137. -webkit-animation: myani .6s ease;<br />
  138. -o-animation: myani .6s ease;<br />
  139. }<br />
  140. [keyframes ](/keyframes ) myani() { <br />
  141. form {<br />
  142. opacity: 0;<br />
  143. top: 150px;<br />
  144. }<br />
  145. to {<br />
  146. opacity: 1;<br />
  147. top: 50px;<br />
  148. }<br />
  149. }<br />
  150. [-moz-keyframes ](/-moz-keyframes ) myani { <br />
  151. form {<br />
  152. opacity: 0;<br />
  153. top: 150px;<br />
  154. }<br />
  155. to {<br />
  156. opacity: 1;<br />
  157. top: 50px;<br />
  158. }<br />
  159. }<br />
  160. [-webkit-keyframes ](/-webkit-keyframes ) myani { <br />
  161. form {<br />
  162. opacity: 0;<br />
  163. top: 150px;<br />
  164. }<br />
  165. to {<br />
  166. opacity: 1;<br />
  167. top: 50px;<br />
  168. }<br />
  169. }<br />
  170. [-o-keyframes ](/-o-keyframes ) myani { <br />
  171. form {<br />
  172. opacity: 0;<br />
  173. top: 150px;<br />
  174. }<br />
  175. to {<br />
  176. opacity: 1;<br />
  177. top: 50px;<br />
  178. }<br />
  179. }<br />
  180. :root {<br />
  181. --green: #b1c8ac;<br />
  182. }<br />
  183. body {<br />
  184. background-color: var(--green);<br />
  185. }
  186. 2. html 内容也拿过来<br />
  187. index.html
  188. > {% extends 'layout.html' %}<br />
  189. {% block title %}My Page{% endblock %}<br />
  190. {% block head %}<br />
  191. {% parent %}<br />
  192. <br />
  193. <br />
  194. <br />
  195. <br />
  196. {% endblock %}<br />
  197. {% block content %}<br />
  198. +1<br />
  199. <br />
  200. {% endblock %}
  201. 3. js
  202. > window.add = function (num) {<br />
  203. return num + 1;<br />
  204. }
  205. 4. 将编译过的 点击小手子类继承父类的 js文件(index.es.js)复制到 项目的 scripts 文件夹下<br />
  206. index-es.js
  207. > 'use strict';<br />
  208. Object.defineProperty(exports, "__esModule", {<br />
  209. value: true<br />
  210. });<br />
  211. var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();<br />
  212. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }<br />
  213. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.**proto** = superClass; }<br />
  214. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }<br />
  215. var PraiseButton = function () {<br />
  216. function PraiseButton(num, element) {<br />
  217. _classCallCheck(this, PraiseButton);<br />
  218. this.num = num;<br />
  219. this.element = element;<br />
  220. _createClass(PraiseButton, [{<br />
  221. key: 'clickAction',<br />
  222. value: function clickAction() {<br />
  223. var _this = this;<br />
  224. this.element.click(function () {<br />
  225. if (_this.num < 10) {<br />
  226. _this.element.css('-webkit-filter', 'grayscale(0)');<br />
  227. $('#animation').addClass('num');<br />
  228. _this.num = add(_this.num);<br />
  229. setTimeout(function () {<br />
  230. ('#thumb'));<br />
  231. // f.clickAction();<br />
  232. };
  233. 5.
  234. 重启服务

node app_o.js

  1. 刷新 [http://localhost:3000/index/index](http://localhost:3000/index/index) 页面,发现项目已经移植成功<br />
  2. #2. 使用 PHP + MySQL 完成点赞借口,实现用户点击一次更新数据库点赞次数
  3. 1.
  4. 在项目中新建一个 praise.php 文件<br />
  5. 编辑文件
  6. 1.
  7. XAMPP 启动 Apache MySQL
  8. > ![1511081397(1).jpg](http://upload-images.jianshu.io/upload_images/9064013-7ac9b88d86aff02f.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
  9. 3. 在浏览器中打开 [http://localhost/phpmyadmin/](http://localhost/phpmyadmin/) 页面
  10. > 在左侧操作栏中点击【新建】<br />
  11. 输入新数据库的名字 praise 点击【创建】<br />
  12. 新建数据表 text 2<br />
  13. 字段分别为 id num<br />
  14. id 需要设置为主键<br />
  15. 点击保存<br />
  16. 具体设计如下图所示<br />
  17. ![image.png](http://upload-images.jianshu.io/upload_images/9064013-f51d3fb3e90dcb02.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
  18. 4. text 数据表中手动添加一条数据
  19. > 点击数据表【text】<br />
  20. 点击 SQL】<br />
  21. 点击【INSERT】<br />
  22. 将输入域的值修改为如下所示,点击【执行】

INSERT INTO text(id, num) VALUES (1,0)

  1. 5.
  2. php 文件中编辑
  3. servername = $servername;
  4. $this->username = $username;
  5. $this->password = $password;
  6. $this->dbname = $dbname;
  7. }
  8. //创建数据库链接方法
  9. //参考网址:http://www.runoob.com/php/php-mysql-connect.html -> 实例 (PDO)
  10. public function getConnection(){
  11. //这里用的是 PDO 方法
  12. try {
  13. $dsn = "mysql:host=$this->servername;dbname=$this->dbname";
  14. $this->con = new PDO($dsn, $this->username, $this->password);
  15. //echo "连接成功";
  16. }
  17. catch(PDOException $e)
  18. {
  19. echo $e->getMessage();
  20. }
  21. }
  22. //更新数据的方法
  23. public function updateDate($sql){
  24. //如果未连接的话就链接到数据库
  25. if($this->con == null){
  26. $this->getConnection();
  27. }
  28. //执行sql
  29. //参考链接:http://www.runoob.com/php/php-mysql-insert.html -> 实例 (PDO)
  30. $res=$this->con->exec($sql);
  31. //关闭链接
  32. $this->closeCon();
  33. }
  34. //关闭链接的方法
  35. public function closeCon(){
  36. $this->con = null;
  37. }
  38. }
  39. /**
  40. * 创建子类
  41. */
  42. class realConn extends Conmysql
  43. {
  44. //继承父类的构造方法
  45. public function __construct($servername,$username,$password,$dbname){
  46. parent::__construct($servername,$username,$password,$dbname);
  47. }
  48. //真正执行 sql 的方法
  49. public function updateRealDate(){
  50. $sql = "UPDATE text SET num=num+1 WHERE id=1";
  51. $this->updateDate($sql);
  52. }
  53. }
  54. //将子类实例化执行方法
  55. $praiseCon = new realConn('localhost','root','','praise');
  56. $praiseCon->updateRealDate();
  57. ?>
  58. 5.
  59. praise.php 文件复制到 C:\xampp\htdocs 文件夹下,在浏览器中打开 http://localhost/praise.php,再查看 text 数据表中会发现 num 的值为 1 ,刷 php 页面一次值便会加 1,至此点赞接口便完成了
  60. #3. 使用KOA2+ES6封装PHP点赞接口,并建立路由<br />
  61. 1. koatest 项目文件夹的 models 文件夹中新建一个 indexmodel.js 文件<br />
  62. 编辑 indexmodel.js 文件

import rpA from ‘request-promise’; class indexModel { constructor(ctx) { this.ctx = ctx; } //为了实现数据库更新建立方法 传数据的接口 updateNum() { //在这里 return 一个 promise 模块传入 initController.js 再在indexController.js 中调用需要用到一个模块 request-promise 参考网址:https://www.npmjs.com/package/request-promise // 获取文件 const options = { uri: ‘http://localhost/praise.php‘, method:’GET’ }; return new Promise((resolve,reject)=>{ rpA(options).then(function(result){ const info = JSON.parse(result); if(info){ resolve({data:info.result}); }else{ reject({}); } }) }) } } //将该方法导出, indexController.js 文件要用到 export default indexModel;

  1. 由于 updateNum 方法中需要用到 request-promise 模块 所以需要安装两个包
  2. > 参考链接:[https://www.npmjs.com/package/request-promise](https://www.npmjs.com/package/request-promise)

npm install —save request npm install —save request-promise

  1. 2.
  2. 编辑 indexController.js 文件<br />
  3. indexController 对象中增加一个 提交数据的方法
  4. <br />//引入所需方法的文件<br />
  5. import indexModel from '../models/indexModel';<br />
  6. update(){<br />
  7. return async(ctx,next)=>{<br />
  8. const indexM = new indexModel(ctx);<br />
  9. ctx.body = await indexM.updateNum();<br />
  10. }<br />
  11. }
  12. 2.
  13. 编辑 initController.js 文件<br />
  14. indexController 提交数据的方法注册到 一个新的路由上
  15. <br />_.get('/index/update', index.update());
  16. 2.
  17. babel 编译文件
  18. indexmodel.js 重命名为 indexmodel.es

cd models babel indexmodel.es -o indexmodel.js cd .. cd controller babel indexController.es -o indexController.js babel initController.es -o initController.js

  1. 此时运行服务会发现小手点击并没有与服务器关联起来,此时需要用 axios 模块来使其连接到 koa2 点赞接口上<br />#4. 将用户点击事件通过 axios 链接到 KOA2 点赞接口
  2. 1.
  3. koatest 项目中的 scripts 文件夹中新建 index.es 文件,并将 praisethumb 项目中的 scripts -> index.js 文件代码复制过来
  4. <br />class PraiseButton{<br />
  5. constructor(num,element){<br />
  6. this.num = num;<br />
  7. this.element = element;<br />
  8. }<br />
  9. clickAction(){<br />
  10. this.element.click(()=>{<br />
  11. if(this.num < 10){<br />
  12. this.element.css('-webkit-filter','grayscale(0)');<br />
  13. $('#animation').addClass('num');<br />
  14. this.num = add(this.num);<br />
  15. setTimeout(function () {<br />
  16. $('#animation').removeClass('num');<br />
  17. },1000);<br />
  18. }else{<br />
  19. this.element.css('-webkit-filter','grayscale(1)');<br />
  20. this.num = 0;<br />
  21. }<br />
  22. console.log(this.num);<br />
  23. })<br />
  24. }<br />
  25. }
  26. <br />class Thumb extends PraiseButton{<br />
  27. constructor(num,element){<br />
  28. super(num,element)<br />
  29. }<br />
  30. }
  31. <br />export default{Thumb}
  32. <br />// let f = new Thumb(0,$('#thumb'));<br />
  33. // f.clickAction();
  34. 编辑 index.es 文件,这里是要用 axios 模块来使其连接到 koa2 点赞接口上
  35. > 参考链接:[https://www.npmjs.com/package/axios](https://www.npmjs.com/package/axios)
  36. 这里有三种引用的方法:npm 装包,浏览器装包,cdn,这里我们用的是 cdn 方法,将如下代码 copy index.html 文件中
  37. 将如下代码 copy index.es -> this.element.click 点击事件中

axios.get(‘/index/update’) .then(function (response) { console.log(response); }) .catch(function (error) { console.log(error); });

  1. 2.
  2. 编修改过的文件 index.es
  3. <br />cd public\scripts<br />
  4. babel index.es -o index-es.js
  5. 2.
  6. 运行服务
  7. <br />cd ../..<br />
  8. node app_o.js
  9. 点击时页面 [http://localhost:3000/index/index](http://localhost:3000/index/index) 报如下图所示的错误
  10. > ![image.png](http://upload-images.jianshu.io/upload_images/9064013-0428b1fa8a33f79a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
  11. 编辑 praise.php 文件中的 updateDate 方法

//更新数据的方法 public function updateDate($sql){ //如果未连接的话就链接到数据库 if($this->con == null){ $this->getConnection(); } //向前台输出 json 格式的数据 header(‘content-type:application/json;charset=utf8’); //执行sql //参考链接:http://www.runoob.com/php/php-mysql-insert.html -> 实例 (PDO) $res=$this->con->exec($sql); //执行之后 输出返回的数据 $arr = array(‘result’=>$res); echo json_encode($arr); //关闭链接 $this->closeCon(); }

  1. 4. 运行服务,发现又报了 404 ,排查之后发现是 index.es 文件中的 uodate 写成了 uodete ,诶,修改后 编译 ,再运行点击一下小手,数据库里面的 num 的值 +1 证明成功了
  2. > 需要注意的是 php返回的数据必须严格是 json 格式的 不然前台 .js 文件 (未编译:indexmodel.es 已编译 indexmodel.js)中用到的 JSON.parse(result); 会一直报错导致页面崩溃

cd public\scripts\ babel index.es -o index-es.js cd../.. node app_o.js

  1. #5. 对用户连续点击事件进行稀释<br />
  2. 稀释所用的方法就是 setTimeout<br />编辑 index.es 文件

let f = ‘’; class PraiseButton{ constructor(num,element){ this.num = num; this.element = element; } clickAction(){ this.element.click(()=>{ //判断事件是否已经存在,如果存在便会将其清除掉,所以点击只会执行最后一次点击的那次事件 if(f){ clearTimeout(f); } f = setTimeout(()=>{ if(this.num < 10){ this.element.css(‘-webkit-filter’,’grayscale(0)’); $(‘#animation’).addClass(‘num’); this.num = add(this.num); setTimeout(function () { $(‘#animation’).removeClass(‘num’); },1000); axios.get(‘/index/update’) .then(function (response) { console.log(response); }) .catch(function (error) { console.log(error); }); }else{ this.element.css(‘-webkit-filter’,’grayscale(1)’); this.num = 0; } console.log(this.num); },800)

  1. })

} } class Thumb extends PraiseButton{ constructor(num,element){ super(num,element) } } export default{Thumb} // let f = new Thumb(0,$(‘#thumb’)); // f.clickAction();

  1. 编译文件

cd public\scripts\ babel index.es -o index-es.js

  1. 运行服务,稀释成功

node app_o.js

  1. #6. 完成点赞接口的_自动化测试_、点赞+1功能的_自动化测试_、真实页面点击_自动化测试_<br />
  2. ###1. 点赞+1功能的_自动化测试_
  3. 1.
  4. 安装 karma
  5. <br />cnpm install karma --save-dev
  6. 1.
  7. 安装断言库、核心、chrome 启动器
  8. <br />cnpm install karma-jasmine jasmine-core karma-chrome-launcher --save-dev
  9. 1.
  10. 安装无界面浏览器、无界面浏览器启动器
  11. <br />cnpm install phantomjs --save-dev<br />
  12. cnpm install karma-phantomjs-launcher --save-dev
  13. 1.
  14. karma 初始化生成配置文件 karma.conf.js
  15. <br />karma init<br />
  16. jasmine<br />
  17. no<br />
  18. PhantomJS<br />
  19. yes
  20. 5.在项目中新建一个 test 文件夹,在该文件夹下做三大测试
  21. 6.
  22. praisethumb 项目文件夹的中 index.spce.js 文件复制到 test 文件夹下
  23. 6.
  24. 编辑 karma.conf.js 文件
  25. <br />files: [<br />
  26. 'test/index.spec.js',<br />
  27. 'public/scripts/index.js'<br />
  28. ],<br />
  29. singleRun: true,
  30. 6.
  31. 启动测试,运行成功
  32. <br />karma start
  33. ###2. 点赞接口的_自动化测试_<br />
  34. 1.先安装 mocha

cnpm install mocha —save-dev

  1. 2.
  2. 安装 supertest
  3. <br />cnpm install supertest --save-dev
  4. 2.
  5. test 文件夹下新建一个 server.js 文件<br />
  6. 编辑 sever.js 文件,这里我们需要用到 supertest 模块
  7. > 参考链接:[https://www.npmjs.com/package/supertest](https://www.npmjs.com/package/supertest)

import requestsuper from ‘supertest’; //这里引用的是 app_o.js 文件拿到 app,所以需要在 app.js 文件中将其导出,然后再将 app.js 文件编译为 app_o.js 文件 import app from ‘../app_o.js’; //先定义一个拿到端口的方法 function request(){ return requestsuper(app.listen()); } describe(‘测试路由’, function() { it(‘点赞’, function(done) { request() .get(‘/index/update’) .expect(200) .end(function(err,res){ if(res.data==1)return done(err); done(); }) }); });

  1. 4.
  2. 修改 app.js 文件
  3. <br />export default app;
  4. 4.
  5. 编译文件
  6. <br />babel app.js -o app_o.js<br />
  7. cd test<br />
  8. babel server.js -o server-es.js
  9. 4.
  10. 执行 server-es.js 文件
  11. <br />cd ..<br />
  12. mocha test\server-es.js
  13. 此时报如下图所示的错误
  14. > ![image.png](http://upload-images.jianshu.io/upload_images/9064013-4b2c5d554c9d72a3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
  15. mocha 安装至全局

cnpm install mocha -g

  1. 执行测试

mocha test\server-es.js

  1. 执行成功
  2. > ![image.png](http://upload-images.jianshu.io/upload_images/9064013-06d027b55c8bd6e6.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
  3. ###3. 真实页面点击_自动化测试_<br />1.安装 selenium-webdriver
  4. > 参考链接:[https://www.npmjs.com/package/selenium-webdriver](https://www.npmjs.com/package/selenium-webdriver)

cnpm install selenium-webdriver

  1. 2.下面火狐浏览器所需的相应组件
  2. > 参考链接:[https://github.com/mozilla/geckodriver/releases/](https://github.com/mozilla/geckodriver/releases/) 找到与当前设备匹配的一款点击下载 我的是 win64
  3. 3.
  4. 将下载好的压缩包解压至 koatest 项目文件夹中,删除压缩包
  5. 3.
  6. 再安装两个包
  7. <br />cnpm install selenium-standalone --save<br />
  8. cnpm install protractor --save
  9. 3.
  10. test 文件夹下新建 e2e.js 文件<br />
  11. 编辑 e2e.js 文件
  12. > 参考链接:[https://www.npmjs.com/package/selenium-webdriver](https://www.npmjs.com/package/selenium-webdriver) -> Usage

const {Builder, By, Key, until} = require(‘selenium-webdriver’);

let driver = new Builder() .forBrowser(‘firefox’) .build(); //设置地址 driver.get(‘http://localhost:3000/index/index‘); driver.findElement(By.id(‘thumb’)).click(); const _animation = driver.findElement(By.id(‘animation’)) driver.wait(_animation.isDisplayed(), 1000); // driver.quit(); //为了看到效果将关闭浏览器的行为暂时注释掉

  1. 6.
  2. 执行测试脚本 e2e.js 文件
  3. <br />node test\e2e.js<br />
  4. 此时会发现浏览器打开页面后,页面并不能加载出来原因是这个 koatest 项目此时并没有处于运行状态,所以需要新建一个命令行窗口中执行项目运行操作
  5. ![image.png](http://upload-images.jianshu.io/upload_images/9064013-1484c051a0eef690.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)<br />在新窗口中输入命令

cd Desktop cd koatest node app_o.js

  1. 此时再在之前的命令行窗口中执行测试脚本 e2e.js 文件

node test\e2e.js

``` 整个实战到此结束。
2017.11.19 23.11 我今天生日哟,刚了一天!奋发图强 !