├── .babelrc
├── .gitignore
├── .vscode
└── launch.json
├── Icon
├── README.md
├── build
├── app.js
├── config
│ └── config.js
├── controller
│ ├── indexController.js
│ └── initController.js
├── models
│ └── indexmodel.js
├── public
│ ├── css
│ │ └── vendor.css
│ └── scripts
│ │ ├── common
│ │ └── vendor.min.js
│ │ ├── index.js
│ │ └── tags.js
├── test
│ └── server.js
├── views
│ ├── 404.html
│ ├── index.html
│ ├── layout.html
│ └── star.html
└── widget
│ ├── index.html
│ └── star.html
├── config
├── webpack.dev.js
└── webpack.prod.js
├── geckodriver
├── gulpfile.js
├── karma.conf.js
├── package-lock.json
├── package.json
├── praise.php
├── readme img
├── 0.要求.png
├── 1.CDN预加载配置.png
├── 2.扩展星星组件,首页直出并用Pjax配置SPA.png
├── 3-1.localStorage缓存.png
├── 3-2.用localforage实现的缓存负载均衡ORM库.png
├── 3-3.离线资源配置manifest.png
├── 4.Prod上线版本动态分配CDN.png
└── 5.lazyload并行加载静态资源.png
├── src
├── app-o.js
├── app.es
├── config
│ ├── config.es
│ └── config.js
├── controller
│ ├── indexController.es
│ ├── indexController.js
│ ├── initController.es
│ └── initController.js
├── models
│ ├── indexmodel.es
│ └── indexmodel.js
├── public
│ ├── css
│ │ └── index.css
│ └── scripts
│ │ ├── index-es.es
│ │ ├── index-es.js
│ │ ├── indexadd.js
│ │ ├── star.es
│ │ └── tags.es
├── test
│ ├── e2e.js
│ ├── index.spec.js
│ ├── server.es
│ └── server.js
├── views
│ ├── index.js
│ ├── star.js
│ └── webAssetsHelp.js
└── widget
│ ├── 404.html
│ ├── index.html
│ ├── layout.html
│ └── star.html
└── webpack.config.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets":[
3 | "es2015",
4 | "stage-0"
5 | ],
6 | "plugins":[]
7 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # 忽略 node_modules/ 文件夹下的所有文件
2 | node_modules/
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // 使用 IntelliSense 了解相关属性。
3 | // 悬停以查看现有属性的描述。
4 | // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "type": "chrome",
9 | "request": "launch",
10 | "name": "Launch Chrome against localhost",
11 | "url": "http://localhost:8080",
12 | "webRoot": "${workspaceFolder}"
13 | }
14 | ]
15 | }
--------------------------------------------------------------------------------
/Icon
:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yllg/Performance/a25812fd1dbe0ee64d145a86eebd2cf0fcd010df/Icon
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Performance
2 | 前端性能优化:动态CDN,CDN预加载,Pjax实现SPA,缓存负载均衡,ORM库,离线包manifest,lazyloadjs静态资源并行分发器
3 |
4 | 0.实现目标
5 |
6 | 
7 |
8 | 1.CDN预加载配置
9 |
10 | 
11 |
12 | 2.扩展星星组件,首页直出并用Pjax配置SPA
13 |
14 | 
15 |
16 | 3-1.localStorage缓存
17 |
18 | 
19 |
20 | 3-2.用localforage实现的缓存负载均衡ORM库
21 |
22 | 
23 |
24 | 3-3.离线资源配置manifest
25 |
26 | 
27 |
28 | 4.Prod上线版本动态分配CDN
29 |
30 | 
31 |
32 | 5.lazyload并行加载静态资源
33 |
34 | 
35 |
36 |
--------------------------------------------------------------------------------
/build/app.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 |
7 | var _koa = require('koa');
8 |
9 | var _koa2 = _interopRequireDefault(_koa);
10 |
11 | var _koaSimpleRouter = require('koa-simple-router');
12 |
13 | var _koaSimpleRouter2 = _interopRequireDefault(_koaSimpleRouter);
14 |
15 | var _initController = require('./controller/initController');
16 |
17 | var _initController2 = _interopRequireDefault(_initController);
18 |
19 | var _koaSwig = require('koa-swig');
20 |
21 | var _koaSwig2 = _interopRequireDefault(_koaSwig);
22 |
23 | var _co = require('co');
24 |
25 | var _co2 = _interopRequireDefault(_co);
26 |
27 | var _koaStatic = require('koa-static');
28 |
29 | var _koaStatic2 = _interopRequireDefault(_koaStatic);
30 |
31 | var _register = require('babel-core/register');
32 |
33 | var _register2 = _interopRequireDefault(_register);
34 |
35 | var _babelPolyfill = require('babel-polyfill');
36 |
37 | var _babelPolyfill2 = _interopRequireDefault(_babelPolyfill);
38 |
39 | var _config = require('./config/config');
40 |
41 | var _config2 = _interopRequireDefault(_config);
42 |
43 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
44 |
45 | function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }
46 | //引入路由,自己写的控制器
47 |
48 | //引入swig模板
49 |
50 | //引入静态资源配置
51 |
52 | //引入babel的另两个文件
53 |
54 | //引入配置文件
55 |
56 |
57 | var app = new _koa2.default();
58 |
59 | //初始化路由设置
60 | _initController2.default.init(app, _koaSimpleRouter2.default);
61 |
62 | //模板引擎设置
63 | app.context.render = _co2.default.wrap((0, _koaSwig2.default)({
64 | root: _config2.default.get('viewDir'),
65 | autoescape: true,
66 | cache: 'memory', // disable, set to false
67 | ext: 'html',
68 | writeBody: false
69 | }));
70 |
71 | //静态资源设置
72 | app.use((0, _koaStatic2.default)(_config2.default.get('staticDir')));
73 |
74 | //路由容错处理
75 | // 404,跳到腾讯公益页面
76 | app.use(function () {
77 | var _ref = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee(ctx) {
78 | return regeneratorRuntime.wrap(function _callee$(_context) {
79 | while (1) {
80 | switch (_context.prev = _context.next) {
81 | case 0:
82 | ctx.status = 404;
83 | _context.next = 3;
84 | return ctx.render('404.html');
85 |
86 | case 3:
87 | ctx.body = _context.sent;
88 |
89 | case 4:
90 | case 'end':
91 | return _context.stop();
92 | }
93 | }
94 | }, _callee, undefined);
95 | }));
96 |
97 | return function (_x) {
98 | return _ref.apply(this, arguments);
99 | };
100 | }()
101 | // ctx.redirect("http://www.yidengxuetang.com/");
102 | );
103 | // 500,跳到一等学堂
104 | app.on('error', function (err, ctx) {
105 | // ctx.body = "😓 服务器开了小差......";
106 | ctx.redirect("http://www.yidengxuetang.com/");
107 | });
108 |
109 | app.listen(_config2.default.get('port'));
110 | exports.default = app;
--------------------------------------------------------------------------------
/build/config/config.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 |
7 | var _path = require('path');
8 |
9 | var _path2 = _interopRequireDefault(_path);
10 |
11 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
12 |
13 | var CONFIG = new Map();
14 | CONFIG.set('port', 3000);
15 | CONFIG.set('staticDir', _path2.default.join(__dirname, '..'));
16 | CONFIG.set('viewDir', _path2.default.join(__dirname, '..', 'views'));
17 | exports.default = CONFIG;
--------------------------------------------------------------------------------
/build/controller/indexController.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 |
7 | var _indexmodel = require('../models/indexmodel');
8 |
9 | var _indexmodel2 = _interopRequireDefault(_indexmodel);
10 |
11 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
12 |
13 | function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } //引入model层的方法
14 |
15 |
16 | //路由的方法
17 | var indexController = {
18 | index: function index() {
19 | var _this = this;
20 |
21 | return function () {
22 | var _ref = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee(ctx, next) {
23 | return regeneratorRuntime.wrap(function _callee$(_context) {
24 | while (1) {
25 | switch (_context.prev = _context.next) {
26 | case 0:
27 | _context.next = 2;
28 | return ctx.render('index.html', {
29 | title: '首页'
30 | });
31 |
32 | case 2:
33 | ctx.body = _context.sent;
34 |
35 | case 3:
36 | case 'end':
37 | return _context.stop();
38 | }
39 | }
40 | }, _callee, _this);
41 | }));
42 |
43 | return function (_x, _x2) {
44 | return _ref.apply(this, arguments);
45 | };
46 | }();
47 | },
48 | update: function update() {
49 | var _this2 = this;
50 |
51 | return function () {
52 | var _ref2 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee2(ctx, next) {
53 | var indexM;
54 | return regeneratorRuntime.wrap(function _callee2$(_context2) {
55 | while (1) {
56 | switch (_context2.prev = _context2.next) {
57 | case 0:
58 | indexM = new _indexmodel2.default(ctx);
59 | _context2.next = 3;
60 | return indexM.updateNum();
61 |
62 | case 3:
63 | ctx.body = _context2.sent;
64 |
65 | case 4:
66 | case 'end':
67 | return _context2.stop();
68 | }
69 | }
70 | }, _callee2, _this2);
71 | }));
72 |
73 | return function (_x3, _x4) {
74 | return _ref2.apply(this, arguments);
75 | };
76 | }();
77 | },
78 | star: function star() {
79 | var _this3 = this;
80 |
81 | return function () {
82 | var _ref3 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee3(ctx, next) {
83 | return regeneratorRuntime.wrap(function _callee3$(_context3) {
84 | while (1) {
85 | switch (_context3.prev = _context3.next) {
86 | case 0:
87 | if (!ctx.request.header['x-pjax']) {
88 | _context3.next = 4;
89 | break;
90 | }
91 |
92 | ctx.body = "";
93 | _context3.next = 7;
94 | break;
95 |
96 | case 4:
97 | _context3.next = 6;
98 | return ctx.render('star.html', {
99 | title: '星星组件页'
100 | });
101 |
102 | case 6:
103 | ctx.body = _context3.sent;
104 |
105 | case 7:
106 | case 'end':
107 | return _context3.stop();
108 | }
109 | }
110 | }, _callee3, _this3);
111 | }));
112 |
113 | return function (_x5, _x6) {
114 | return _ref3.apply(this, arguments);
115 | };
116 | }();
117 | },
118 | praise: function praise() {
119 | var _this4 = this;
120 |
121 | return function () {
122 | var _ref4 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee4(ctx, next) {
123 | return regeneratorRuntime.wrap(function _callee4$(_context4) {
124 | while (1) {
125 | switch (_context4.prev = _context4.next) {
126 | case 0:
127 | if (!ctx.request.header['x-pjax']) {
128 | _context4.next = 4;
129 | break;
130 | }
131 |
132 | ctx.body = "";
133 | _context4.next = 7;
134 | break;
135 |
136 | case 4:
137 | _context4.next = 6;
138 | return ctx.render('index.html', {
139 | title: '首页'
140 | });
141 |
142 | case 6:
143 | ctx.body = _context4.sent;
144 |
145 | case 7:
146 | case 'end':
147 | return _context4.stop();
148 | }
149 | }
150 | }, _callee4, _this4);
151 | }));
152 |
153 | return function (_x7, _x8) {
154 | return _ref4.apply(this, arguments);
155 | };
156 | }();
157 | },
158 | advertisement: function advertisement() {
159 | var _this5 = this;
160 |
161 | return function () {
162 | var _ref5 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee5(ctx, next) {
163 | return regeneratorRuntime.wrap(function _callee5$(_context5) {
164 | while (1) {
165 | switch (_context5.prev = _context5.next) {
166 | case 0:
167 | ctx.body = '
...大幅广告...
';
168 |
169 | case 1:
170 | case 'end':
171 | return _context5.stop();
172 | }
173 | }
174 | }, _callee5, _this5);
175 | }));
176 |
177 | return function (_x9, _x10) {
178 | return _ref5.apply(this, arguments);
179 | };
180 | }();
181 | }
182 | };
183 | exports.default = indexController;
--------------------------------------------------------------------------------
/build/controller/initController.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 |
7 | var _indexController = require('./indexController');
8 |
9 | var _indexController2 = _interopRequireDefault(_indexController);
10 |
11 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
12 |
13 | var controllerInit = {
14 | init: function init(app, router) {
15 | app.use(router(function (_) {
16 | //使用indexController里面的index方法
17 | _.get('/', function (ctx, next) {
18 | ctx.redirect('/index/index');
19 | }), _.get('/index', function (ctx, next) {
20 | ctx.redirect('/index/index');
21 | }), _.get('/index/index', _indexController2.default.index()), _.get('/index/update', _indexController2.default.update()), _.get('/index/star', _indexController2.default.star()), _.get('/index/praise', _indexController2.default.praise()), _.get('/index/adv', _indexController2.default.advertisement());
22 | }));
23 | }
24 | }; //注册路由
25 | exports.default = controllerInit;
--------------------------------------------------------------------------------
/build/models/indexmodel.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 |
7 | 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; }; }();
8 |
9 | var _requestPromise = require('request-promise');
10 |
11 | var _requestPromise2 = _interopRequireDefault(_requestPromise);
12 |
13 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
14 |
15 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
16 |
17 | var indexModel = function () {
18 | function indexModel(ctx) {
19 | _classCallCheck(this, indexModel);
20 |
21 | this.ctx = ctx;
22 | }
23 | //model层调用php的接口
24 |
25 |
26 | _createClass(indexModel, [{
27 | key: 'updateNum',
28 | value: function updateNum() {
29 | var options = {
30 | uri: 'http://localhost:8080/praise.php',
31 | method: 'GET'
32 | };
33 | return new Promise(function (resolve, reject) {
34 | (0, _requestPromise2.default)(options).then(function (result) {
35 | var info = JSON.parse(result);
36 | if (info) {
37 | resolve({ data: info.result });
38 | } else {
39 | reject({});
40 | }
41 | });
42 | });
43 | }
44 | }]);
45 |
46 | return indexModel;
47 | }();
48 | //导出给indexController里
49 |
50 |
51 | exports.default = indexModel;
--------------------------------------------------------------------------------
/build/public/css/vendor.css:
--------------------------------------------------------------------------------
1 | body{background-image:linear-gradient(90deg,#cce6c9 0,#fff)}#wrap{left:50%;transform:translate(-50%);top:35%;width:220px}#PraiseButton,#wrap{position:absolute;height:130px}#PraiseButton{left:75px;width:125px;background:#ffcaa4;border-radius:60px 40px 40px 40px}#PraiseButton:after,#PraiseButton:before{display:block;content:"";position:absolute}#PraiseButton:before{top:35px;left:60px;-webkit-box-reflect:above 1px;box-reflect:above 1px}#PraiseButton:after,#PraiseButton:before{width:80px;height:30px;border-radius:25px 25px 25px 25px;background-image:linear-gradient(90deg,#ffcaa4 15px,#efdbcd 40px,#ffcaa4 100px)}#PraiseButton:after{top:66px;left:55px;-webkit-box-reflect:below 1px;box-reflect:below 1px}#Thumb{position:absolute;top:30px;width:90px;height:80px;background:#ffcaa4}#Thumb:before{display:block;content:"";position:absolute;width:110px;height:30px;top:-55px;left:73px;transform:rotate(-65deg);border-radius:25px 25px 235px 255px;background-image:linear-gradient(90deg,#ffcaa4 60px,#efdbcd 80px,#ffcaa4 100px)}.hide{display:none}.num{display:block!important;-webkit-animation:a .8s ease-in-out alternate;animation:a .8s ease-in-out alternate;-webkit-animation-iteration-count:1;font-size:40px;color:#f17;position:absolute;right:-10%;top:-45%}@-webkit-keyframes a{0%{opacity:0;-webkit-transform:translateY(-20px);text-shadow:0 0 10px #fff,0 0 20px #fff,0 0 30px #fff,0 0 40px #f17,0 0 70px #f17,0 0 80px #f17,0 0 100px #f17,0 0 150px #f17}to{opacity:1;-webkit-transform:translateY(0);text-shadow:0 0 5px #fff,0 0 10px #fff,0 0 15px #fff,0 0 30px #f17,0 0 35px #f17,0 0 40px #f17,0 0 50px #f17,0 0 75px #f17}}.star{margin:300px 50%;top:-30%;left:-8%;color:red;z-index:1;border-right:100px solid transparent;border-bottom:70px solid red;border-left:100px solid transparent;-moz-transform:rotate(35deg);-webkit-transform:rotate(35deg);-ms-transform:rotate(35deg);-o-transform:rotate(35deg)}.star,.star:before{position:absolute;display:block;width:0;height:0}.star:before{border-bottom:80px solid red;border-left:30px solid transparent;border-right:30px solid transparent;top:-45px;left:-65px;content:"";-webkit-transform:rotate(-35deg);-moz-transform:rotate(-35deg);-ms-transform:rotate(-35deg);-o-transform:rotate(-35deg)}.star1{position:relative;display:block;color:red;top:3px;left:-105px;width:0;height:0;border-right:100px solid transparent;border-bottom:70px solid red;border-left:100px solid transparent;-webkit-transform:rotate(-70deg);-moz-transform:rotate(-70deg);-ms-transform:rotate(-70deg);-o-transform:rotate(-70deg)}
--------------------------------------------------------------------------------
/build/public/scripts/common/vendor.min.js:
--------------------------------------------------------------------------------
1 | !function(e){function t(n){if(o[n])return o[n].exports;var r=o[n]={i:n,l:!1,exports:{}};return e[n].call(r.exports,r,r.exports,t),r.l=!0,r.exports}var n=window.webpackJsonp;window.webpackJsonp=function(o,i,c){for(var u,a,f,l=0,s=[];l+1',methods:{praise:function(){var i=this;s.clickAction();var t=i.querySelector("#animation");t.className="hide num",setTimeout(function(){t.className="hide"},800)}},events:{click:function(i){var t=this;if("Thumb"==i.target.id||"PraiseButton"==i.target.id){var a="";a&&clearTimeout(a),a=setTimeout(function(){t.praise()},500)}}}})},function(i,t,a){"use strict";var e=a(0),s=new e.Star;xtag.register("x-star",{content:'+1',methods:{praise:function(){var i=this;s.clickAction();var t=i.querySelector("#animation");t.className="hide num",setTimeout(function(){t.className="hide"},800)}},events:{click:function(i){var t=this;if("star"==i.target.id){var a="";a&&clearTimeout(a),a=setTimeout(function(){t.praise()},500)}}}})}],[4]);
--------------------------------------------------------------------------------
/build/test/server.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var _supertest = require('supertest');
4 |
5 | var _supertest2 = _interopRequireDefault(_supertest);
6 |
7 | var _appO = require('../app-o');
8 |
9 | var _appO2 = _interopRequireDefault(_appO);
10 |
11 | function _interopRequireDefault(obj) {
12 | return obj && obj.__esModule ? obj : { default: obj };
13 | }
14 |
15 | //获得监听的端口
16 | function request() {
17 | return (0, _supertest2.default)(_appO2.default.listen());
18 | }
19 |
20 | //进行测试哦
21 | describe('测试路由', function () {
22 | it('点赞', function (done) {
23 | request().get('/index/update').expect(200).end(function (err, res) {
24 | if (res.data == 1) return done(err);
25 | done();
26 | });
27 | });
28 | });
--------------------------------------------------------------------------------
/build/views/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Document
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/build/views/index.html:
--------------------------------------------------------------------------------
1 | {% extends './layout.html' %}{% block title %}KOA2{{title}}{% endblock %}{% block styles %}{% endblock %}{% block content %}{% include '../widget/index.html' %}{% endblock %}{% block script %}{% endblock %}
--------------------------------------------------------------------------------
/build/views/layout.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | {% block title %}My Site{% endblock %}
8 | {% block head %}
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | {% endblock %} {% block styles %}{% endblock %}
17 |
18 |
19 |
20 | 跳转到星星
21 | 跳转到大拇指点赞
22 | {% block content %}{% endblock %}
23 |
43 |
44 | {% block script %}{% endblock %}
45 |
46 |
--------------------------------------------------------------------------------
/build/views/star.html:
--------------------------------------------------------------------------------
1 | {% extends './layout.html' %}{% block title %}KOA2{{title}}{% endblock %}{% block styles %}{% endblock %}{% block content %}{% include '../widget/star.html' %}{% endblock %}{% block script %}{% endblock %}
--------------------------------------------------------------------------------
/build/widget/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
JS与QA 三大测试:
6 |
单元测试:karma start
7 |
接口测试:npm run mochatest
8 |
e2e测试:npm run e2etest
9 |
10 |
nodejs KOA2:
11 |
重定向:根目录/跳转到/index/index
12 |
容错处理:
13 |
404跳到腾讯公益“找小孩”页面;
14 |
500跳到一灯官网;
15 |
16 |
工程化和持续构建:
17 |
运行gulp,打包编译后台文件,监视文件变化自动打包更新
18 |
运行webpack -w 默认用dev版打包文件,并监视前端文件变化重新打包
19 |
进入build目录运行node app.js 启动服务,默认3000端口
20 |
21 |
前端性能优化:
22 |
点击左上角两个a连接,可以实现跳转
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/build/widget/star.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/config/webpack.dev.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const webpack = require('webpack');
3 | const LiveReloadPlugin = require('webpack-livereload-plugin');
4 | const ExtractTextPlugin = require("extract-text-webpack-plugin");
5 | const HtmlWebpackPlugin = require('html-webpack-plugin');
6 | const Manifest = require('webpack-manifest');
7 |
8 | module.exports = {
9 | entry: {
10 | index: [
11 | path.join(__dirname, '../src/public/scripts/index-es.es'),
12 | path.join(__dirname, '../src/public/scripts/indexadd.js')
13 | ],
14 | tags: [
15 | path.join(__dirname, '../src/public/scripts/tags.es'),
16 | path.join(__dirname, '../src/public/scripts/star.es')
17 | ]
18 | },
19 | output: {
20 | filename: 'public/scripts/[name].js',
21 | path: path.join(__dirname, '../build')
22 | },
23 | module: {
24 | rules: [{
25 | test: /\.es$/,
26 | exclude: /(node_modules|bower_components)/,
27 | use: {
28 | loader: 'babel-loader',
29 | options: {
30 | presets: ['es2015', 'stage-0']
31 | }
32 | }
33 | },
34 | {
35 | test: /\.css$/,
36 | use: ExtractTextPlugin.extract({
37 | fallback: "style-loader",
38 | use: "css-loader"
39 | })
40 | }
41 | ]
42 | },
43 | plugins: [
44 | new webpack.DefinePlugin({
45 | 'process.env': {
46 | NODE_ENV: 'dev'
47 | }
48 | }),
49 | new LiveReloadPlugin({
50 | appendScriptTag: true
51 | }),
52 | new ExtractTextPlugin("public/css/[name].css"),
53 | new webpack.optimize.CommonsChunkPlugin({
54 | name: 'vendor',
55 | filename: 'public/scripts/common/[name].min.js',
56 | }),
57 | new HtmlWebpackPlugin({
58 | filename: './views/layout.html',
59 | template: 'src/widget/layout.html',
60 | inject: false
61 | }),
62 | new HtmlWebpackPlugin({
63 | filename: './views/index.html',
64 | template: 'src/views/index.js',
65 | inject: false,
66 | chunks: ['vendor', 'index', 'tags']
67 | }),
68 | new HtmlWebpackPlugin({
69 | filename: './widget/index.html',
70 | template: 'src/widget/index.html',
71 | inject: false
72 | }),
73 | new HtmlWebpackPlugin({
74 | filename: './views/star.html',
75 | template: 'src/views/star.js',
76 | inject: false,
77 | chunks: ['vendor', 'index', 'tags']
78 | }),
79 | new HtmlWebpackPlugin({
80 | filename: './widget/star.html',
81 | template: 'src/widget/star.html',
82 | inject: false
83 | }),
84 | new HtmlWebpackPlugin({
85 | filename: './views/404.html',
86 | template: 'src/widget/404.html',
87 | inject: false
88 | }),
89 | new Manifest({
90 | cache: [
91 | '../public/css/vendor.css',
92 | '../public/scripts/common/vendor.min.js',
93 | '../public/scripts/tag.js',
94 | '../public/scripts/index.js',
95 | ],
96 | //Add time in comments.
97 | timestamp: true,
98 | // 生成的文件名字,选填
99 | // The generated file name, optional.
100 | filename: 'cache.manifest',
101 | // 注意*星号前面用空格隔开
102 | network: [
103 | 'http://cdn.bootcss.com/ *',
104 | 'http://localhost:35729/livereload.js',
105 | 'http://localhost:3000/ *'
106 | ],
107 | // 注意中间用空格隔开
108 | // fallback: ['/ /404.html'],
109 | // manifest 文件中添加注释
110 | // Add notes to manifest file.
111 | headcomment: "koatesting",
112 | master: ['../views/layout.html']
113 | })
114 | ]
115 | };
--------------------------------------------------------------------------------
/config/webpack.prod.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const webpack = require('webpack');
3 | const LiveReloadPlugin = require('webpack-livereload-plugin');
4 | const ExtractTextPlugin = require("extract-text-webpack-plugin");
5 | const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
6 | const HtmlWebpackPlugin = require('html-webpack-plugin');
7 |
8 | module.exports = {
9 | entry: {
10 | index: [
11 | path.join(__dirname, '../src/public/scripts/index-es.es'),
12 | path.join(__dirname, '../src/public/scripts/indexadd.js')
13 | ],
14 | tags: [
15 | path.join(__dirname, '../src/public/scripts/tags.es'),
16 | path.join(__dirname, '../src/public/scripts/star.es')
17 | ]
18 | },
19 | output: {
20 | filename: 'public/scripts/[name].js',
21 | publicPath: 'http://192.168.31.25:3000/',
22 | path: path.join(__dirname, '../build')
23 | },
24 | module: {
25 | rules: [{
26 | test: /\.es$/,
27 | exclude: /(node_modules|bower_components)/,
28 | use: {
29 | loader: 'babel-loader',
30 | options: {
31 | presets: ['es2015', 'stage-0']
32 | }
33 | }
34 | },
35 | {
36 | test: /\.css$/,
37 | use: ExtractTextPlugin.extract({
38 | fallback: "style-loader",
39 | use: "css-loader"
40 | })
41 | }
42 | ]
43 | },
44 | plugins: [
45 | new webpack.DefinePlugin({
46 | 'process.env': {
47 | NODE_ENV: 'prod'
48 | }
49 | }),
50 | new LiveReloadPlugin({
51 | appendScriptTag: true
52 | }),
53 | new ExtractTextPlugin("public/css/[name].css"),
54 | new webpack.optimize.UglifyJsPlugin({
55 | compress:{
56 | warnings:false,
57 | drop_console:false
58 | }
59 | }),
60 | new OptimizeCssAssetsPlugin({
61 | assetNameRegExp: /.css$/g,
62 | cssProcessor: require('cssnano'),
63 | cssProcessorOptions: { discardComments: { removeAll: true } },
64 | canPrint: true
65 | }),
66 | new webpack.optimize.CommonsChunkPlugin({
67 | name: 'vendor',
68 | filename: 'public/scripts/common/[name].min.js',
69 | }),
70 | new HtmlWebpackPlugin({
71 | filename: './views/layout.html',
72 | template: 'src/widget/layout.html',
73 | inject:false
74 | }),
75 | new HtmlWebpackPlugin({
76 | filename: './views/index.html',
77 | template: 'src/views/index.js',
78 | inject:false,
79 | chunks:['vendor','index','tags']
80 | }),
81 | new HtmlWebpackPlugin({
82 | filename: './widget/index.html',
83 | template: 'src/widget/index.html'
84 | }),
85 | new HtmlWebpackPlugin({
86 | filename: './views/star.html',
87 | template: 'src/views/star.js',
88 | inject: false,
89 | chunks: ['vendor', 'index', 'tags']
90 | }),
91 | new HtmlWebpackPlugin({
92 | filename: './widget/star.html',
93 | template: 'src/widget/star.html',
94 | inject: false
95 | }),
96 | new HtmlWebpackPlugin({
97 | filename: './views/404.html',
98 | template: 'src/widget/404.html',
99 | inject:false
100 | })
101 | ]
102 | };
--------------------------------------------------------------------------------
/geckodriver:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yllg/Performance/a25812fd1dbe0ee64d145a86eebd2cf0fcd010df/geckodriver
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | const gulp = require('gulp');
2 | const babel = require('gulp-babel');
3 |
4 | gulp.task('default', ['praise'], () => {
5 | gulp.watch(['src/**/*.es', '!src/public/**/*.es'], ['praise'])
6 | });
7 |
8 | gulp.task('praise', () => {
9 | return gulp.src(['src/**/*.es', '!src/public/**/*.es'])
10 | .pipe(babel({
11 | presets: ['es2015', 'stage-0']
12 | }))
13 | .pipe(gulp.dest('./build'))
14 | });
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration
2 | // Generated on Sat Nov 18 2017 19:05:58 GMT+0800 (CST)
3 |
4 | module.exports = function(config) {
5 | config.set({
6 |
7 | // base path that will be used to resolve all patterns (eg. files, exclude)
8 | basePath: '',
9 |
10 |
11 | // frameworks to use
12 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
13 | frameworks: ['jasmine'],
14 |
15 |
16 | // list of files / patterns to load in the browser
17 | files: [
18 | "test/index.spec.js",
19 | "public/scripts/index.js"
20 | ],
21 |
22 |
23 | // list of files to exclude
24 | exclude: [
25 | ],
26 |
27 |
28 | // preprocess matching files before serving them to the browser
29 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
30 | preprocessors: {
31 | },
32 |
33 |
34 | // test results reporter to use
35 | // possible values: 'dots', 'progress'
36 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter
37 | reporters: ['progress'],
38 |
39 |
40 | // web server port
41 | port: 9876,
42 |
43 |
44 | // enable / disable colors in the output (reporters and logs)
45 | colors: true,
46 |
47 |
48 | // level of logging
49 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
50 | logLevel: config.LOG_INFO,
51 |
52 |
53 | // enable / disable watching file and executing tests whenever any file changes
54 | autoWatch: true,
55 |
56 |
57 | // start these browsers
58 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
59 | browsers: ['PhantomJS'],
60 |
61 |
62 | // Continuous Integration mode
63 | // if true, Karma captures browsers, runs the tests and exits
64 | singleRun: false,
65 |
66 | // Concurrency level
67 | // how many browser should be started simultaneous
68 | concurrency: Infinity
69 | })
70 | }
71 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "koatest",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "node app-o.js",
8 | "mochatest": "mocha test/server.es.js",
9 | "e2etest": "node test/e2e.js",
10 | "webpackdev": "better-npm-run webpack:dev",
11 | "webpackprod": "better-npm-run webpack:prod"
12 | },
13 | "betterScripts": {
14 | "webpack:dev": {
15 | "command": "webpack --progress --colors",
16 | "env": {
17 | "NODE_ENV": "dev"
18 | }
19 | },
20 | "webpack:prod": {
21 | "command": "webpack --progress --colors",
22 | "env": {
23 | "NODE_ENV": "prod"
24 | }
25 | }
26 | },
27 | "author": "",
28 | "license": "ISC",
29 | "dependencies": {
30 | "koa": "^2.0.0-alpha.8",
31 | "koa-simple-router": "^0.2.0",
32 | "koa-static": "^4.0.2",
33 | "koa-swig": "^2.2.1",
34 | "path": "^0.12.7",
35 | "request": "^2.83.0",
36 | "request-promise": "^4.2.2"
37 | },
38 | "devDependencies": {
39 | "babel": "^6.23.0",
40 | "babel-cli": "^6.26.0",
41 | "babel-loader": "^7.1.2",
42 | "babel-polyfill": "^6.26.0",
43 | "babel-preset-env": "^1.6.1",
44 | "babel-preset-es2015": "^6.24.1",
45 | "babel-preset-stage-0": "^6.24.1",
46 | "babel-register": "^6.26.0",
47 | "better-npm-run": "^0.1.0",
48 | "css-loader": "^0.28.7",
49 | "extract-text-webpack-plugin": "^3.0.2",
50 | "gulp": "^3.9.1",
51 | "gulp-babel": "^7.0.0",
52 | "html-webpack-plugin": "^2.30.1",
53 | "jasmine-core": "^2.8.0",
54 | "karma": "^1.7.1",
55 | "karma-chrome-launcher": "^2.2.0",
56 | "karma-jasmine": "^1.1.0",
57 | "mocha": "^4.0.1",
58 | "optimize-css-assets-webpack-plugin": "^3.2.0",
59 | "protractor": "^5.2.0",
60 | "selenium-standalone": "^6.11.0",
61 | "selenium-webdriver": "^3.6.0",
62 | "style-loader": "^0.19.0",
63 | "supertest": "^3.0.0",
64 | "webpack": "^3.9.1",
65 | "webpack-livereload-plugin": "^1.0.0",
66 | "webpack-manifest": "^1.0.8"
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/praise.php:
--------------------------------------------------------------------------------
1 | servername = $servername;
14 | $this->username = $username;
15 | $this->password = $password;
16 | $this->dbname = $dbname;
17 | }
18 |
19 | //连接数据库
20 | public function getConnection(){
21 | try {
22 | $dsn = "mysql:host=$this->servername;dbname=$this->dbname";
23 | $this->con = new PDO($dsn, $this->username, $this->password);
24 | // echo "连接mysql数据库成功";
25 | }
26 | catch(PDOException $e){
27 | echo $e->getMessage();
28 | }
29 | }
30 |
31 | //更新数据库
32 | public function updateDate($sql){
33 | //如果没有连接,让它连接上
34 | if($this->con == null){
35 | $this->getConnection();
36 | }
37 | header('content-type:application/json;charset=utf-8');
38 | $res = $this->con->exec($sql);
39 | $arr = array('result'=>$res);
40 | echo json_encode($arr);
41 | $this->closeCon();
42 | // echo "执行成功返回1,失败返回0;本次执行结果:".$res;
43 | }
44 |
45 | //关闭数据库连接
46 | public function closeCon(){
47 | $this->conn = '';
48 | }
49 | }
50 |
51 |
52 | //子类
53 | class realCon extends Conmysql{
54 | //该类的构造方法
55 | public function __construct($servername,$username,$password,$dbname){
56 | parent::__construct($servername,$username,$password,$dbname);
57 | }
58 |
59 | public function updateRealData(){
60 | $sql = "UPDATE `test` SET `num`=`num`+1 WHERE `id`=1";
61 | $this->updateDate($sql);
62 | }
63 | }
64 |
65 | //实例化
66 | $praiseCon = new realCon('localhost','root','','test');
67 | $praiseCon->updateRealData();
68 |
69 |
70 | ?>
--------------------------------------------------------------------------------
/readme img/0.要求.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yllg/Performance/a25812fd1dbe0ee64d145a86eebd2cf0fcd010df/readme img/0.要求.png
--------------------------------------------------------------------------------
/readme img/1.CDN预加载配置.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yllg/Performance/a25812fd1dbe0ee64d145a86eebd2cf0fcd010df/readme img/1.CDN预加载配置.png
--------------------------------------------------------------------------------
/readme img/2.扩展星星组件,首页直出并用Pjax配置SPA.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yllg/Performance/a25812fd1dbe0ee64d145a86eebd2cf0fcd010df/readme img/2.扩展星星组件,首页直出并用Pjax配置SPA.png
--------------------------------------------------------------------------------
/readme img/3-1.localStorage缓存.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yllg/Performance/a25812fd1dbe0ee64d145a86eebd2cf0fcd010df/readme img/3-1.localStorage缓存.png
--------------------------------------------------------------------------------
/readme img/3-2.用localforage实现的缓存负载均衡ORM库.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yllg/Performance/a25812fd1dbe0ee64d145a86eebd2cf0fcd010df/readme img/3-2.用localforage实现的缓存负载均衡ORM库.png
--------------------------------------------------------------------------------
/readme img/3-3.离线资源配置manifest.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yllg/Performance/a25812fd1dbe0ee64d145a86eebd2cf0fcd010df/readme img/3-3.离线资源配置manifest.png
--------------------------------------------------------------------------------
/readme img/4.Prod上线版本动态分配CDN.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yllg/Performance/a25812fd1dbe0ee64d145a86eebd2cf0fcd010df/readme img/4.Prod上线版本动态分配CDN.png
--------------------------------------------------------------------------------
/readme img/5.lazyload并行加载静态资源.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yllg/Performance/a25812fd1dbe0ee64d145a86eebd2cf0fcd010df/readme img/5.lazyload并行加载静态资源.png
--------------------------------------------------------------------------------
/src/app-o.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 |
7 | var _koa = require('koa');
8 |
9 | var _koa2 = _interopRequireDefault(_koa);
10 |
11 | var _koaSimpleRouter = require('koa-simple-router');
12 |
13 | var _koaSimpleRouter2 = _interopRequireDefault(_koaSimpleRouter);
14 |
15 | var _initController = require('./controller/initController');
16 |
17 | var _initController2 = _interopRequireDefault(_initController);
18 |
19 | var _koaSwig = require('koa-swig');
20 |
21 | var _koaSwig2 = _interopRequireDefault(_koaSwig);
22 |
23 | var _co = require('co');
24 |
25 | var _co2 = _interopRequireDefault(_co);
26 |
27 | var _koaStatic = require('koa-static');
28 |
29 | var _koaStatic2 = _interopRequireDefault(_koaStatic);
30 |
31 | var _register = require('babel-core/register');
32 |
33 | var _register2 = _interopRequireDefault(_register);
34 |
35 | var _babelPolyfill = require('babel-polyfill');
36 |
37 | var _babelPolyfill2 = _interopRequireDefault(_babelPolyfill);
38 |
39 | var _config = require('./config/config');
40 |
41 | var _config2 = _interopRequireDefault(_config);
42 |
43 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
44 |
45 | function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }
46 | //引入路由,自己写的控制器
47 |
48 | //引入swig模板
49 |
50 | //引入静态资源配置
51 |
52 | //引入babel的另两个文件
53 |
54 | //引入配置文件
55 |
56 |
57 | var app = new _koa2.default();
58 |
59 | //初始化路由设置
60 | _initController2.default.init(app, _koaSimpleRouter2.default);
61 |
62 | //模板引擎设置
63 | app.context.render = _co2.default.wrap((0, _koaSwig2.default)({
64 | root: _config2.default.get('viewDir'),
65 | autoescape: true,
66 | cache: 'memory', // disable, set to false
67 | ext: 'html',
68 | writeBody: false
69 | }));
70 |
71 | //静态资源设置
72 | app.use((0, _koaStatic2.default)(_config2.default.get('staticDir')));
73 |
74 | //路由容错处理
75 | // 404,跳到腾讯公益页面
76 | app.use(function () {
77 | var _ref = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee(ctx) {
78 | return regeneratorRuntime.wrap(function _callee$(_context) {
79 | while (1) {
80 | switch (_context.prev = _context.next) {
81 | case 0:
82 | ctx.status = 404;
83 | _context.next = 3;
84 | return ctx.render('404.html');
85 |
86 | case 3:
87 | ctx.body = _context.sent;
88 |
89 | case 4:
90 | case 'end':
91 | return _context.stop();
92 | }
93 | }
94 | }, _callee, undefined);
95 | }));
96 |
97 | return function (_x) {
98 | return _ref.apply(this, arguments);
99 | };
100 | }()
101 | // ctx.redirect("http://www.yidengxuetang.com/");
102 | );
103 | // 500,跳到一等学堂
104 | app.on('error', function (err, ctx) {
105 | // ctx.body = "😓 服务器开了小差......";
106 | ctx.redirect("http://www.yidengxuetang.com/");
107 | });
108 |
109 | app.listen(_config2.default.get('port'));
110 | exports.default = app;
111 |
--------------------------------------------------------------------------------
/src/app.es:
--------------------------------------------------------------------------------
1 | import Koa from 'koa';
2 | //引入路由,自己写的控制器
3 | import router from 'koa-simple-router';
4 | import initController from './controller/initController';
5 | //引入swig模板
6 | import render from 'koa-swig';
7 | import co from 'co';
8 | //引入静态资源配置
9 | import serve from 'koa-static';
10 | //引入babel的另两个文件
11 | import babel_co from 'babel-core/register';
12 | import babel_po from 'babel-polyfill';
13 | //引入配置文件
14 | import CONFIG from './config/config';
15 |
16 | const app = new Koa();
17 |
18 | //初始化路由设置
19 | initController.init(app,router);
20 |
21 | //模板引擎设置
22 | app.context.render = co.wrap(render({
23 | root:CONFIG.get('viewDir'),
24 | autoescape: true,
25 | cache: 'memory', // disable, set to false
26 | ext: 'html',
27 | writeBody: false
28 | }));
29 |
30 | //静态资源设置
31 | app.use(serve(CONFIG.get('staticDir')));
32 |
33 | //路由容错处理
34 | // 404,跳到腾讯公益页面
35 | app.use(async (ctx) => {
36 | ctx.status = 404
37 | ctx.body = await ctx.render('404.html');
38 | // ctx.redirect("http://www.yidengxuetang.com/");
39 | });
40 | // 500,跳到一等学堂
41 | app.on('error', (err, ctx) => {
42 | // ctx.body = "😓 服务器开了小差......";
43 | ctx.redirect("http://www.yidengxuetang.com/");
44 | });
45 |
46 | app.listen(CONFIG.get('port'));
47 | export default app
--------------------------------------------------------------------------------
/src/config/config.es:
--------------------------------------------------------------------------------
1 | import path from 'path';
2 | const CONFIG = new Map();
3 | CONFIG.set('port',3000);
4 | CONFIG.set('staticDir',path.join(__dirname,'..'));
5 | CONFIG.set('viewDir',path.join(__dirname,'..','views'));
6 | export default CONFIG
--------------------------------------------------------------------------------
/src/config/config.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 |
7 | var _path = require('path');
8 |
9 | var _path2 = _interopRequireDefault(_path);
10 |
11 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
12 |
13 | var CONFIG = new Map();
14 | CONFIG.set('port', 3000);
15 | CONFIG.set('staticDir', _path2.default.join(__dirname, '..', 'public'));
16 | CONFIG.set('viewDir', _path2.default.join(__dirname, '..', 'views'));
17 | exports.default = CONFIG;
18 |
--------------------------------------------------------------------------------
/src/controller/indexController.es:
--------------------------------------------------------------------------------
1 | //引入model层的方法
2 | import indexModel from '../models/indexmodel'
3 | //路由的方法
4 | const indexController = {
5 | index(){
6 | return async(ctx,next)=>{
7 | ctx.body = await ctx.render('index.html',{
8 | title: '首页'
9 | });
10 | }
11 | },
12 | update(){
13 | return async(ctx,next)=>{
14 | const indexM = new indexModel(ctx);
15 | ctx.body = await indexM.updateNum();
16 | }
17 | },
18 | star(){
19 | return async(ctx,next)=>{
20 | if(ctx.request.header['x-pjax']){
21 | ctx.body = "";
22 | }else{
23 | ctx.body = await ctx.render('star.html',{
24 | title: '星星组件页'
25 | });
26 | }
27 | }
28 | },
29 | praise(){
30 | return async(ctx,next)=>{
31 | if(ctx.request.header['x-pjax']){
32 | ctx.body = "";
33 | }else{
34 | ctx.body = await ctx.render('index.html',{
35 | title: '首页'
36 | });
37 | }
38 | }
39 | },
40 | advertisement(){
41 | return async(ctx, next) => {
42 | ctx.body = '...大幅广告...
'
43 | }
44 | }
45 | }
46 | export default indexController
--------------------------------------------------------------------------------
/src/controller/indexController.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 |
7 | var _indexmodel = require('../models/indexmodel');
8 |
9 | var _indexmodel2 = _interopRequireDefault(_indexmodel);
10 |
11 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
12 |
13 | function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } //引入model层的方法
14 |
15 |
16 | //路由的方法
17 | var indexController = {
18 | index: function index() {
19 | var _this = this;
20 |
21 | return function () {
22 | var _ref = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee(ctx, next) {
23 | return regeneratorRuntime.wrap(function _callee$(_context) {
24 | while (1) {
25 | switch (_context.prev = _context.next) {
26 | case 0:
27 | _context.next = 2;
28 | return ctx.render('index.html', {
29 | title: '大拇指点赞'
30 | });
31 |
32 | case 2:
33 | ctx.body = _context.sent;
34 |
35 | case 3:
36 | case 'end':
37 | return _context.stop();
38 | }
39 | }
40 | }, _callee, _this);
41 | }));
42 |
43 | return function (_x, _x2) {
44 | return _ref.apply(this, arguments);
45 | };
46 | }();
47 | },
48 | update: function update() {
49 | var _this2 = this;
50 |
51 | return function () {
52 | var _ref2 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee2(ctx, next) {
53 | var indexM;
54 | return regeneratorRuntime.wrap(function _callee2$(_context2) {
55 | while (1) {
56 | switch (_context2.prev = _context2.next) {
57 | case 0:
58 | indexM = new _indexmodel2.default(ctx);
59 | _context2.next = 3;
60 | return indexM.updateNum();
61 |
62 | case 3:
63 | ctx.body = _context2.sent;
64 |
65 | case 4:
66 | case 'end':
67 | return _context2.stop();
68 | }
69 | }
70 | }, _callee2, _this2);
71 | }));
72 |
73 | return function (_x3, _x4) {
74 | return _ref2.apply(this, arguments);
75 | };
76 | }();
77 | }
78 | };
79 | exports.default = indexController;
80 |
--------------------------------------------------------------------------------
/src/controller/initController.es:
--------------------------------------------------------------------------------
1 | //注册路由
2 | import index from './indexController'
3 | const controllerInit = {
4 | init(app,router){
5 | app.use(router(_ => {
6 | //使用indexController里面的index方法
7 | _.get('/',(ctx,next) =>{
8 | ctx.redirect('/index/index');
9 | }),
10 | _.get('/index',(ctx,next) =>{
11 | ctx.redirect('/index/index');
12 | }),
13 | _.get('/index/index',index.index()),
14 | _.get('/index/update',index.update()),
15 | _.get('/index/star',index.star()),
16 | _.get('/index/praise',index.praise()),
17 | _.get('/index/adv',index.advertisement())
18 | }))
19 | }
20 | }
21 |
22 | export default controllerInit
--------------------------------------------------------------------------------
/src/controller/initController.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 |
7 | var _indexController = require('./indexController');
8 |
9 | var _indexController2 = _interopRequireDefault(_indexController);
10 |
11 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
12 |
13 | var controllerInit = {
14 | init: function init(app, router) {
15 | app.use(router(function (_) {
16 | //使用indexController里面的index方法
17 | _.get('/', function (ctx, next) {
18 | ctx.redirect('/index/index');
19 | }), _.get('/index/index', _indexController2.default.index()), _.get('/index/update', _indexController2.default.update());
20 | }));
21 | }
22 | }; //注册路由
23 | exports.default = controllerInit;
24 |
--------------------------------------------------------------------------------
/src/models/indexmodel.es:
--------------------------------------------------------------------------------
1 |
2 | import rpA from 'request-promise'
3 | class indexModel{
4 | constructor(ctx){
5 | this.ctx = ctx;
6 | }
7 | //model层调用php的接口
8 | updateNum(){
9 | const options = {
10 | uri: 'http://localhost:8080/praise.php',
11 | method:'GET'
12 | }
13 | return new Promise((resolve,reject)=>{
14 | rpA(options).then(function(result){
15 | const info = JSON.parse(result);
16 | if(info){
17 | resolve({data:info.result});
18 | }else{
19 | reject({});
20 | }
21 | })
22 | })
23 | }
24 | }
25 | //导出给indexController里
26 | export default indexModel
--------------------------------------------------------------------------------
/src/models/indexmodel.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 |
7 | 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; }; }();
8 |
9 | var _requestPromise = require('request-promise');
10 |
11 | var _requestPromise2 = _interopRequireDefault(_requestPromise);
12 |
13 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
14 |
15 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
16 |
17 | var indexModel = function () {
18 | function indexModel(ctx) {
19 | _classCallCheck(this, indexModel);
20 |
21 | this.ctx = ctx;
22 | }
23 | //model层调用php的接口
24 |
25 |
26 | _createClass(indexModel, [{
27 | key: 'updateNum',
28 | value: function updateNum() {
29 | var options = {
30 | uri: 'http://localhost:8080/praise.php',
31 | method: 'GET'
32 | };
33 | return new Promise(function (resolve, reject) {
34 | (0, _requestPromise2.default)(options).then(function (result) {
35 | var info = JSON.parse(result);
36 | if (info) {
37 | resolve({ data: info.result });
38 | } else {
39 | reject({});
40 | }
41 | });
42 | });
43 | }
44 | }]);
45 |
46 | return indexModel;
47 | }();
48 | //导出给indexController里
49 |
50 |
51 | exports.default = indexModel;
52 |
--------------------------------------------------------------------------------
/src/public/css/index.css:
--------------------------------------------------------------------------------
1 |
2 | /*背景色绿色渐变*/
3 | body{
4 | background-image: linear-gradient(to right, #cce6c9 0%, white 100%);
5 | }
6 |
7 | /*整个图案水平和垂直居中*/
8 | #wrap {
9 | position: absolute;
10 | left: 50%;
11 | transform: translate(-50%);
12 | top: 35%;
13 | width: 220px;
14 | height: 130px;
15 | }
16 |
17 | /*手掌和四个手指*/
18 | #PraiseButton {
19 | position:absolute;
20 | left:75px;
21 | width:125px;
22 | height:130px;
23 | background:#ffcaa4;
24 | border-radius:60px 40px 40px 40px;
25 | }
26 |
27 |
28 | #PraiseButton:before, #PraiseButton:after {
29 | display:block;
30 | content:'';
31 | position:absolute;
32 | }
33 |
34 | #PraiseButton:before {
35 | width:80px;
36 | height:30px;
37 | top:35px;
38 | left:60px;
39 | border-radius:25px 25px 25px 25px;
40 | background-image:linear-gradient(to right, #ffcaa4 15px, #efdbcd 40px, #ffcaa4 100px);
41 | -webkit-box-reflect: above 1px;
42 | box-reflect: above 1px;
43 | }
44 |
45 | #PraiseButton:after {
46 | width:80px;
47 | height:30px;
48 | top:66px;
49 | left:55px;
50 | border-radius:25px 25px 25px 25px;
51 | background-image:linear-gradient(to right, #ffcaa4 15px, #efdbcd 40px, #ffcaa4 100px);
52 | -webkit-box-reflect: below 1px;
53 | box-reflect: below 1px;
54 | }
55 |
56 |
57 | /*手腕和大拇指*/
58 | #Thumb {
59 | position:absolute;
60 | top:30px;
61 | width:90px;
62 | height:80px;
63 | background:#ffcaa4;
64 | }
65 |
66 |
67 | #Thumb:before {
68 | display:block;
69 | content:'';
70 | position:absolute;
71 | width:110px;
72 | height:30px;
73 | top:-55px;
74 | left:73px;
75 | transform: rotateZ(-65deg);
76 | border-radius:25px 25px 235px 255px;
77 | background-image:linear-gradient(to right, #ffcaa4 60px, #efdbcd 80px, #ffcaa4 100px);
78 | /*border:1px solid #000;*/
79 | }
80 |
81 |
82 | /*+1的弹出效果*/
83 | .hide {
84 | display: none;
85 | }
86 |
87 | /*动态添加和删除num类名,增加这个动画效果哦*/
88 | .num {
89 | display: block !important;
90 | -webkit-animation: pop 0.8s ease-in-out alternate;
91 | animation: pop 0.8s ease-in-out alternate;
92 | /*定义动画播放的次数*/
93 | -webkit-animation-iteration-count:1;
94 |
95 | font-size: 40px;
96 | color: #ff1177;
97 | position: absolute;
98 | right: -10%;
99 | top: -45%;
100 | }
101 |
102 | @-webkit-keyframes pop {
103 | from{
104 | opacity: 0;
105 | -webkit-transform: translateY(-20px);
106 | text-shadow: 0 0 10px #fff, 0 0 20px #fff, 0 0 30px #fff,
107 | 0 0 40px #ff1177, 0 0 70px #ff1177, 0 0 80px #ff1177, 0 0 100px #ff1177, 0 0 150px #ff1177;
108 | }
109 | to{
110 | opacity: 1;
111 | -webkit-transform: translateY(0px);
112 | text-shadow: 0 0 5px #fff, 0 0 10px #fff, 0 0 15px #fff,
113 | 0 0 30px #ff1177, 0 0 35px #ff1177, 0 0 40px #ff1177, 0 0 50px #ff1177, 0 0 75px #ff1177;
114 | }
115 | }
116 |
117 | .star{
118 | margin: 300px 50%;
119 | position: absolute;
120 | top: -30%;
121 | left: -8%;
122 | display: block;
123 | color: red;
124 | width: 0px;
125 | height: 0px;
126 | z-index: 999;
127 | border-right: 100px solid transparent;
128 | border-bottom: 70px solid red;
129 | border-left: 100px solid transparent;
130 | -moz-transform: rotate(35deg);
131 | -webkit-transform: rotate(35deg);
132 | -ms-transform: rotate(35deg);
133 | -o-transform: rotate(35deg);
134 | }
135 | .star:before {
136 | border-bottom: 80px solid red;
137 | border-left: 30px solid transparent;
138 | border-right: 30px solid transparent;
139 | position: absolute;
140 | height: 0;
141 | width: 0;
142 | top: -45px;
143 | left: -65px;
144 | display: block;
145 | content: "";
146 | -webkit-transform: rotate(-35deg);
147 | -moz-transform: rotate(-35deg);
148 | -ms-transform: rotate(-35deg);
149 | -o-transform: rotate(-35deg);
150 | }
151 |
152 | .star1 {
153 | position: relative;
154 | display: block;
155 | color: red;
156 | top: 3px;
157 | left: -105px;
158 | width: 0px;
159 | height: 0px;
160 | border-right: 100px solid transparent;
161 | border-bottom: 70px solid red;
162 | border-left: 100px solid transparent;
163 | -webkit-transform: rotate(-70deg);
164 | -moz-transform: rotate(-70deg);
165 | -ms-transform: rotate(-70deg);
166 | -o-transform: rotate(-70deg);
167 | }
168 |
169 |
170 |
171 |
172 |
--------------------------------------------------------------------------------
/src/public/scripts/index-es.es:
--------------------------------------------------------------------------------
1 |
2 | //只留下父类发送请求的方法,子类,点击事件,稀释事件全都放到tags组件中
3 |
4 | import css from "../css/index.css"
5 |
6 | class PraiseButton {
7 | constructor(){
8 |
9 | }
10 | clickAction(){
11 | axios.get('/index/update')
12 | .then(function (response) {
13 | console.log(response);
14 | })
15 | .catch(function (error) {
16 | console.log(error);
17 | });
18 | }
19 | }
20 |
21 | //Thumb和star都继承PraiseButton
22 | class Thumb extends PraiseButton{
23 | constructor(){
24 | super();
25 | }
26 | }
27 | class Star extends PraiseButton{
28 | constructor(){
29 | super();
30 | }
31 | }
32 | export {Thumb,Star}
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/src/public/scripts/index-es.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.Thumb = exports.PraiseButton = undefined;
7 |
8 | var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };
9 |
10 | 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; }; }();
11 | //父类,可以无限次点击
12 |
13 | require('./index.js');
14 |
15 | 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; }
16 |
17 | 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; }
18 |
19 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
20 |
21 | var PraiseButton = exports.PraiseButton = function () {
22 | function PraiseButton() {
23 | var ele = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : $('#PraiseButton');
24 | var num = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
25 |
26 | _classCallCheck(this, PraiseButton);
27 |
28 | this.ele = ele;
29 | this.num = num;
30 | this.tId = 0;
31 | }
32 |
33 | _createClass(PraiseButton, [{
34 | key: 'click',
35 | value: function click() {
36 | var _this = this;
37 |
38 | this.ele.on('click', function () {
39 | _this.throttle(_this.handleClick.bind(_this));
40 | }.bind(this));
41 | }
42 | }, {
43 | key: 'handleClick',
44 | value: function handleClick() {
45 | clearTimeout(this.tId);
46 | this.num = add(this.num);
47 | this.pop();
48 | axios.get('/index/update').then(function (response) {
49 | console.log(response);
50 | }).catch(function (error) {
51 | console.log(error);
52 | });
53 | console.log('\u7236\u7C7B\u88AB\u70B9\u4E86' + this.num + ' \u6B21\uFF01\u53EF\u65E0\u9650\u70B9\u7236\u7C7B\uFF0C\u4E0D\u7F6E\u7070\u8272\uFF01\uFF01');
54 | console.log('快速点击多次会被稀释成一次!');
55 | }
56 | }, {
57 | key: 'throttle',
58 | value: function throttle(method, context) {
59 | clearTimeout(this.tId);
60 | this.tId = setTimeout(function () {
61 | method.call(context);
62 | }, 500);
63 | }
64 | }, {
65 | key: 'pop',
66 | value: function pop() {
67 | $('#animation').addClass('num');
68 | setTimeout(function () {
69 | $('#animation').removeClass('num');
70 | }, 800);
71 | }
72 | }]);
73 |
74 | return PraiseButton;
75 | }();
76 |
77 | //子类,点击十次置灰;再点击从1开始
78 |
79 |
80 | var Thumb = exports.Thumb = function (_PraiseButton) {
81 | _inherits(Thumb, _PraiseButton);
82 |
83 | function Thumb() {
84 | var ele = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : $('#Thumb');
85 | var num = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
86 |
87 | _classCallCheck(this, Thumb);
88 |
89 | return _possibleConstructorReturn(this, (Thumb.__proto__ || Object.getPrototypeOf(Thumb)).call(this, ele, num));
90 | }
91 |
92 | _createClass(Thumb, [{
93 | key: 'click',
94 | value: function click() {
95 | var _this3 = this;
96 |
97 | this.ele.on('click', function () {
98 | _get(Thumb.prototype.__proto__ || Object.getPrototypeOf(Thumb.prototype), 'throttle', _this3).call(_this3, _this3.handleClick.bind(_this3));
99 | }.bind(this));
100 | }
101 | }, {
102 | key: 'handleClick',
103 | value: function handleClick() {
104 | if (this.num < 9) {
105 | this.num = add(this.num);
106 | _get(Thumb.prototype.__proto__ || Object.getPrototypeOf(Thumb.prototype), 'pop', this).call(this);
107 | } else if (this.num === 9) {
108 | $('#PraiseButton').css('-webkit-filter', 'grayscale(1)');
109 | $('#Thumb').css('-webkit-filter', 'grayscale(1)');
110 | this.num = add(this.num);
111 | _get(Thumb.prototype.__proto__ || Object.getPrototypeOf(Thumb.prototype), 'pop', this).call(this);
112 | } else {
113 | this.num = 1;
114 | $('#PraiseButton').css('-webkit-filter', 'grayscale(0)');
115 | $('#Thumb').css('-webkit-filter', 'grayscale(0)');
116 | _get(Thumb.prototype.__proto__ || Object.getPrototypeOf(Thumb.prototype), 'pop', this).call(this);
117 | }
118 | axios.get('/index/update').then(function (response) {
119 | console.log(response);
120 | }).catch(function (error) {
121 | console.log(error);
122 | });
123 | console.log('\u5B50\u7C7B\u88AB\u70B9\u4E86' + this.num + ' \u6B21\uFF01\u7B2C10\u6B21\u53D8\u7070\u8272\uFF0C\u7136\u540E\u4ECE\u5934\u8BA1\u6570\uFF01\uFF01');
124 | console.log('快速点击多次会被稀释成一次!');
125 | }
126 | }]);
127 |
128 | return Thumb;
129 | }(PraiseButton);
130 |
--------------------------------------------------------------------------------
/src/public/scripts/indexadd.js:
--------------------------------------------------------------------------------
1 | window.add = function add(num){
2 | return num+1;
3 | }
--------------------------------------------------------------------------------
/src/public/scripts/star.es:
--------------------------------------------------------------------------------
1 | import {Star} from './index-es.es'
2 | const f = new Star();
3 |
4 | xtag.register('x-star', {
5 | content: '' +
8 | '+1' ,
9 |
10 | methods: {
11 | praise: function(){
12 | let _this = this;
13 | f.clickAction();
14 | let animation = _this.querySelector("#animation");
15 | animation.className = "hide num";
16 | setTimeout(function(){
17 | animation.className = "hide";
18 | },800)
19 | }
20 | },
21 | events: {
22 | click: function(e){
23 | let _this = this;
24 | if(e.target.id == "star"){
25 | let t ="";
26 | if(t){
27 | clearTimeout(t);
28 | }
29 | t = setTimeout(function(){
30 | _this.praise();
31 | },500)
32 | }
33 | }
34 | }
35 | });
36 |
37 |
38 |
--------------------------------------------------------------------------------
/src/public/scripts/tags.es:
--------------------------------------------------------------------------------
1 | import {Thumb} from './index-es.es'
2 | const f = new Thumb();
3 |
4 | xtag.register('x-praise', {
5 | content: '' +
6 | '
' +
7 | '
' +
8 | '
+1' +
9 | '
' ,
10 |
11 | methods: {
12 | praise: function(){
13 | let _this = this;
14 | f.clickAction();
15 | let animation = _this.querySelector("#animation");
16 | animation.className = "hide num";
17 | setTimeout(function(){
18 | animation.className = "hide";
19 | },800)
20 | }
21 | },
22 | events: {
23 | click: function(e){
24 | let _this = this;
25 | if(e.target.id == "Thumb"|| e.target.id == "PraiseButton"){
26 | let t ="";
27 | if(t){
28 | clearTimeout(t);
29 | }
30 | t = setTimeout(function(){
31 | _this.praise();
32 | },500)
33 | }
34 | }
35 | }
36 | });
37 |
38 |
39 |
--------------------------------------------------------------------------------
/src/test/e2e.js:
--------------------------------------------------------------------------------
1 | const {Builder, By, Key, until} = require('selenium-webdriver');
2 |
3 | let driver = new Builder()
4 | .forBrowser('firefox')
5 | .build();
6 |
7 | driver.get('http://localhost:3000/index/index');
8 | driver.findElement(By.id('Thumb')).click();
9 | const _animation = driver.findElement(By.id('animation'));
10 | driver.wait(_animation.isDisplayed(), 10000);
11 | // driver.quit();
--------------------------------------------------------------------------------
/src/test/index.spec.js:
--------------------------------------------------------------------------------
1 | describe('测试加1函数是否正确',function(){
2 | it('测试add(1)是否等于2',function(){
3 | expect(add(1)).toBe(2);
4 | })
5 | })
--------------------------------------------------------------------------------
/src/test/server.es:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var _supertest = require('supertest');
4 |
5 | var _supertest2 = _interopRequireDefault(_supertest);
6 |
7 | var _appO = require('../app-o');
8 |
9 | var _appO2 = _interopRequireDefault(_appO);
10 |
11 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
12 |
13 | //获得监听的端口
14 | function request() {
15 | return (0, _supertest2.default)(_appO2.default.listen());
16 | }
17 |
18 | //进行测试哦
19 | describe('测试路由', function () {
20 | it('点赞', function (done) {
21 | request().get('/index/update').expect(200).end(function (err, res) {
22 | if (res.data == 1) return done(err);
23 | done();
24 | });
25 | });
26 | });
27 |
--------------------------------------------------------------------------------
/src/test/server.js:
--------------------------------------------------------------------------------
1 | import requestsuper from 'supertest';
2 | import app from '../app-o';
3 |
4 | //获得监听的端口
5 | function request(){
6 | return requestsuper(app.listen());
7 | }
8 |
9 | //进行测试哦
10 | describe('测试路由', function() {
11 | it('点赞', function(done) {
12 | request()
13 | .get('/index/update')
14 | .expect(200)
15 | .end(function(err,res){
16 | if(res.data ==1 ) return done(err);
17 | done();
18 | })
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/src/views/index.js:
--------------------------------------------------------------------------------
1 | module.exports = function (templateParams) {
2 | var _cssList = ['vendor'];
3 | var webAssetsHelp = require('./webAssetsHelp.js')(templateParams, _cssList);
4 |
5 | var _html = "{% extends './layout.html' %}" +
6 | "{% block title %}KOA2{{title}}{% endblock %}" +
7 | "{% block styles %}" +
8 | webAssetsHelp.styles +
9 | "{% endblock %}" +
10 | "{% block content %}{% include '../widget/index.html' %}{% endblock %}" +
11 | "{% block script %}" +
12 | "" +
34 |
35 | // webAssetsHelp.scripts +
36 | "{% endblock %}";
37 | return _html;
38 | }
--------------------------------------------------------------------------------
/src/views/star.js:
--------------------------------------------------------------------------------
1 | module.exports = function (templateParams) {
2 | var _cssList = ['vendor'];
3 | var webAssetsHelp = require('./webAssetsHelp.js')(templateParams, _cssList);
4 |
5 | var _html = "{% extends './layout.html' %}" +
6 | "{% block title %}KOA2{{title}}{% endblock %}" +
7 | "{% block styles %}" +
8 | webAssetsHelp.styles +
9 | "{% endblock %}" +
10 | "{% block content %}{% include '../widget/star.html' %}{% endblock %}" +
11 | "{% block script %}" +
12 | webAssetsHelp.scripts +
13 | "{% endblock %}";
14 | return _html;
15 | }
--------------------------------------------------------------------------------
/src/views/webAssetsHelp.js:
--------------------------------------------------------------------------------
1 | /*
2 | *@Description 通过HtmlWebpackPlugin自定义处理静态资源
3 | *@Author yuanzhijia@yidengxuetang.com
4 | *@Date 2016-05-05
5 | */
6 | module.exports = function(templateParams, cssList) {
7 | console.log('入口文件', templateParams.htmlWebpackPlugin.files);
8 | var _files = templateParams.htmlWebpackPlugin.files;
9 | console.log('文件', _files);
10 | var _regChunk = templateParams.htmlWebpackPlugin.options.chunks;
11 | var _regCss = cssList;
12 | var _scripts = "";
13 | var _scriptsShow = [];
14 | var _styles = "";
15 | for (var i = 0; i < _regChunk.length; i++) {
16 | _scripts += "";
17 | _scriptsShow.push("'" + _files.chunks[_regChunk[i]]['entry'] + "'")
18 | }
19 | for (var k = 0; k < _regCss.length; k++) {
20 | var _cssitem = _regCss[k],
21 | _cssitems = new RegExp("^" + _cssitem),
22 | _cssiteme = new RegExp(".css$");
23 | (_files.css).map(function(filename) {
24 | var _filearr = filename.split('/'),
25 | filrdata = _filearr[_filearr.length - 1];
26 | if (_cssitems.test(filrdata) && _cssiteme.test(filrdata)) {
27 | _styles += '';
28 | }
29 | });
30 | }
31 | return {
32 | scripts: _scripts,
33 | styles: _styles,
34 | scriptsShow: _scriptsShow
35 | }
36 | }
--------------------------------------------------------------------------------
/src/widget/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Document
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/widget/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
JS与QA 三大测试:
6 |
单元测试:karma start
7 |
接口测试:npm run mochatest
8 |
e2e测试:npm run e2etest
9 |
10 |
nodejs KOA2:
11 |
重定向:根目录/跳转到/index/index
12 |
容错处理:
13 |
404跳到腾讯公益“找小孩”页面;
14 |
500跳到一灯官网;
15 |
16 |
工程化和持续构建:
17 |
运行gulp,打包编译后台文件,监视文件变化自动打包更新
18 |
运行webpack -w 默认用dev版打包文件,并监视前端文件变化重新打包
19 |
进入build目录运行node app.js 启动服务,默认3000端口
20 |
21 |
前端性能优化:
22 |
点击左上角两个a连接,可以实现跳转
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/src/widget/layout.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | {% block title %}My Site{% endblock %}
8 | {% block head %}
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | {% endblock %} {% block styles %}{% endblock %}
17 |
18 |
19 |
20 | 跳转到星星
21 | 跳转到大拇指点赞
22 | {% block content %}{% endblock %}
23 |
43 |
44 | {% block script %}{% endblock %}
45 |
46 |
--------------------------------------------------------------------------------
/src/widget/star.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | var DevWebpack = require('./config/webpack.dev');
2 | var ProdWebpack = require('./config/webpack.prod');
3 | switch ( process.env.NODE_ENV ) {
4 | case 'dev':
5 | module.exports = DevWebpack;
6 | break;
7 | case 'prod':
8 | module.exports = ProdWebpack;
9 | break;
10 | default:
11 | module.exports = ProdWebpack;
12 | }
--------------------------------------------------------------------------------