-
Notifications
You must be signed in to change notification settings - Fork 157
/
liblog.sql
652 lines (614 loc) · 674 KB
/
liblog.sql
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
/*
Navicat MySQL Data Transfer
Source Server : localhost_liblog_3306
Source Server Version : 50540
Source Host : localhost:3306
Source Database : liblog
Target Server Type : MYSQL
Target Server Version : 50540
File Encoding : 65001
Date: 2017-03-15 11:20:29
*/
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for li_article
-- ----------------------------
DROP TABLE IF EXISTS `li_article`;
CREATE TABLE `li_article` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(255) NOT NULL,
`abstract` text,
`content` text NOT NULL,
`picurl` varchar(255) DEFAULT NULL,
`author` varchar(255) DEFAULT NULL,
`createtime` datetime DEFAULT NULL,
`view` bigint(20) DEFAULT '1',
`totop` smallint(6) DEFAULT '0',
`torecom` smallint(6) DEFAULT '0',
`topicrecom` smallint(6) DEFAULT '0',
`tag` int(11) DEFAULT NULL,
`keywords` varchar(255) DEFAULT NULL,
`allowcomment` int(11) DEFAULT '1',
`ispublished` int(11) DEFAULT '0',
`from` varchar(255) DEFAULT NULL,
`item` int(11) DEFAULT NULL,
`like` int(11) DEFAULT '0' COMMENT '喜欢',
`flag_a` smallint(255) DEFAULT '0',
`flag_b` smallint(255) DEFAULT '0',
`flag_c` smallint(255) DEFAULT '0',
`flag_d` smallint(255) DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=420 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of li_article
-- ----------------------------
INSERT INTO `li_article` VALUES ('131', 'javascript之单例模式', '单例模式即保证一个类仅有一个实例,并提供一个访问它的全局访问点。单例模式也称作为单子模式,更多的也叫做单体模式。为软件设计中较为简单但是最为常用的一种设计模式。', '<h3 id=\"-\">单例模式的定义</h3>\n<p>单例模式即保证一个类仅有一个实例,并提供一个访问它的全局访问点。单例模式也称作为单子模式,更多的也叫做单体模式。为软件设计中较为简单但是最为常用的一种设计模式。</p>\n<blockquote>\n<p>单例模式的思路是:一个类能返回一个对象的引用(并且永远是同一个)和一个获得该实例的方法(静态方法,通常使用 getInstance 名称)。那么当我们调用这个方法时,如果类持有的引用不为空就返回该引用,否者就创建该类的实例,并且将实例引用赋值给该类保持的那个引用再返回。同时将该类的构造函数定义为私有方法,避免其他函数使用该构造函数来实例化对象,只通过该类的静态方法来得到该类的唯一实例。</p>\n<h3 id=\"-\">实现单例模式</h3>\n<p>用一个变量标记当前是否已经为某个类创建过对象,如果是,则在下一次获取类的实例时,直接返回之前创建的对象。</p></blockquote><br><pre><code>```js<br>var Singleton = function (name) {<br> this.name = name;<br> this.instance = null; //标记是否已经实例化<br>};<p></p>\n\n<p>Singleton.prototype.getName = function () {<br> console.log(this.name);<br>};</p>\n<p>Singleton.getInstance = function (name) {<br> if (!this.instance) {<br> //没有实例,实例化一下<br> this.instance = new Singleton(name);<br> }<br> return this.instance; //返回实例<br>}</p>\n<p>var a = Singleton.getInstance(\'sven1\');<br>var b = Singleton.getInstance(\'sven2\');</p>\n<p>alert(a === b); //true</p></code></pre>\n<pre><code>或者:\n```js\nvar Singleton = function (name) {\n this.name = name;\n};\n\nSingleton.prototype.getName = function () {\n console.log(this.name);\n};\n\nSingleton.getInstance = (function () {\n var instance = null; //标记是否已经实例化\n return function (name) {\n if (!instance) {\n instance = new Singleton(name);\n }\n return instance;\n }\n}());\n\nvar a = Singleton.getInstance(\'sven1\');\nvar b = Singleton.getInstance(\'sven2\');\n\nalert(a === b); //true\n</code></pre><p>这两种方法相对简单,但是有一个问题,增加了这个类的不透明性,Singleton类的使用者必须知道这个单例类。</p>\n<h3 id=\"-\">透明的单例模式</h3>\n<pre><code class=\"lang-js\">var createDiv = (function () {\n var instance;\n\n var CreateDiv = function (html) {\n\n if (instance) {\n return instance;\n }\n\n this.html = html;\n this.init();\n\n return instance = this;\n };\n\n CreateDiv.prototype.init = function () {\n var div = document.createElement(\'div\');\n div.innerHTML = this.html;\n document.body.appendChild(div);\n };\n\n return CreateDiv;\n}());\n\nvar a = new createDiv(\'hello\');\nvar b = new createDiv(\'kit\');\n</code></pre>\n<p>虽然这样子完成了一个透明的单例类,但它同样有一些缺点。<br>1.程序复杂,使用自执行的匿名函数和闭包,让这个匿名函数返回真正的单例类。<br>2.实例单例的构造函数,负责了两件事:第一保证只有一个实例,创建对象和执行init()方法。 </p>\n<h3 id=\"-\">用代理实现单例模式</h3>\n<pre><code class=\"lang-js\">//首先是一个普通的创建div的类\nvar CreateDiv = function (html) {\n this.html = html;\n this.init();\n};\n\nCreateDiv.prototype.init = function () {\n var div = document.createElement(\'div\');\n div.innerHTML = this.html;\n document.body.appendChild(div);\n};\n\n//负责管理单例的逻辑移到这个代理类\nvar proxySingletonCreateDiv = (function () {\n var instance;\n return function (html) {\n if (!instance) {\n instance = new CreateDiv(html);\n }\n return instance;\n };\n}());\n\nproxySingletonCreateDiv(\'aaa\');\nproxySingletonCreateDiv(\'bbb\');\nproxySingletonCreateDiv(\'ccc\');\n</code></pre>\n<h3 id=\"javascript-\">javascript里的单例模式</h3>\n<p>全局变量不是单例模式,但是我们要经常会把全局变量当成单例模式,比如:</p>\n<pre><code class=\"lang-js\">var a = {};\n\n//浏览器自带了一个单例对象。\nwindow;\n</code></pre>\n<p>单例模式的核心是只有一个实例,并提供全局访问,但是全局变量会造成命名空间污染。</p>\n<h3 id=\"-\">惰性单例</h3>\n<p>惰性单例是指在需要的时候才创建对象实例。<br>这种技术在实际开发中很有作用,比如制作一个悬浮的登录框。 </p>\n<p>事先页面只有登录按钮,用于触发事件。</p>\n<pre><code class=\"lang-html\"><body>\n <button id=\"loginBtn\">登录</button>\n</body>\n</code></pre>\n<p>js部分:</p>\n<pre><code class=\"lang-js\">//负责实现单例\nvar getSingle = function (fn) {\n var result;\n return function () {\n return result || (result = fn.apply(this, arguments));\n }\n};\n\n//负责创建登录框\nvar createLoginlayer = function () {\n var div = document.createElement(\'div\');\n div.innerHTML = \'我是一个登录框\';\n document.body.appendChild(div);\n return div;\n};\n\n//生成一个单例对象\nvar createSingleLoginYayer = getSingle(createLoginlayer);\n\n//当有需要的时候创建对象,并具只创建一次\ndocument.getElementById(\'loginBtn\').onclick = function () {\n createSingleLoginYayer();\n};\n</code></pre>\n<h3 id=\"jquery-one\">jQuery one</h3>\n<pre><code class=\"lang-html\"><button id=\"btn1\">按钮一</button>\n<button id=\"btn2\">按钮二</button>\n<script src=\"http://cdn.bootcss.com/jquery/1.11.3/jquery.min.js\"></script>\n<script src=\"007.js\"></script>\n</code></pre>\n<p>007.js如下:</p>\n<pre><code class=\"lang-js\">var console1 = function () {\n console.log(1);\n};\n\nvar console2 = function () {\n console.log(2);\n};\n\nvar btn1 = $(\'#btn1\');\nvar btn2 = $(\'#btn2\');\n\n//每次一次,执行一次\nbtn1.on(\'click\', console1);\n\n//只执行一次\nbtn2.one(\'click\', console2);\n</code></pre>\n<h3 id=\"_-once\">_.once</h3>\n<p>underscorejs也有实现单例的方法,我们拿之前的例子改一下。</p>\n<pre><code class=\"lang-html\"><button id=\"loginBtn\">登录按钮</button>\n<script src=\"http://cdn.bootcss.com/underscore.js/1.8.2/underscore-min.js\"></script>\n<script src=\"008.js\"></script>\n</body>\n</code></pre>\n<p>008.js如下:</p>\n<pre><code class=\"lang-js\">//负责创建登录框\nvar createLoginlayer = function () {\n var div = document.createElement(\'div\');\n div.innerHTML = \'我是一个登录框\';\n document.body.appendChild(div);\n return div;\n};\n\n//生成一个单例对象\nvar createSingleLoginYayer = _.once(createLoginlayer);\n\n//当有需要的时候创建对象,并具只创建一次\ndocument.getElementById(\'loginBtn\').onclick = function () {\n createSingleLoginYayer();\n};\n</code></pre>', '', 'guosheng', '2016-05-16 10:20:31', '175', '0', '1', '0', '7', 'javascript,单例模式,设计模式', '1', '1', '网络', '1', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('129', 'HTML5.1 将于9月份正式发布 更新内容预览', 'HTML 5.1 来了。HTML 5 诞生多年以来,受到多数主流浏览器的支持。接下来,这门互联网编程语言也将走上更加规范化的道路。 W3C组织最新宣布,正在编写 HTML 5.1 的语言标准规范,预计6月中旬完成初稿,9月份正式发布。', '<p>据悉,相比于 HTML 5 ,即将到来的 HTML 5.1 不仅功能更强,而且更加友善,执行效率更高。HTML5,像任何已经发展了几十年的标准一样,隐含了各式各样的问题。 5.1旨在解决一些问题。</p><p>更新内容预览:</p><p>Tabbed Out</p><p>XSS</p><ul><li> Content Security Policy 介绍了防止跨站点脚本攻击。 HTML 5.1 将增加内联元素,以及样式和脚本的其他规范。</li></ul><p>Off the Table</p><ul><li>在HTML 4时,你在“TBODY”之前写“TFOOT”, HTML5允许在任何地方。但由于Tab键的缘故和HTML 5.1的可访问性,内容模型已经恢复到HTML 4。</li></ul><p>RIP Microformats</p><ul><li>Microformat 以及 Microdata 这些都是很酷的小标签,你可以添加到您的HTML中,以指定内容是什么,比如一个地址。</li></ul><p>据介绍,HTML 5最早诞生于2008年一月,但直到2014年10月才有了第一版标准语言规范。目前,HTML 5技术被认为将取代老旧的Flash,成为新一代Web开发的主流编程语言。</p><p><br></p>', '', '网络', '2016-05-13 17:36:44', '194', '0', '1', '0', '6', 'html5', '1', '1', 'http://www.cnbeta.com/articles/493397.htm', '2', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('130', '万维网联盟正在Github上开发HTML5.1', '据外媒报道,很快,互联网上使用的主流语言就要来一次“整容”了,获悉,日前,万维网联盟(W3C)披露正在开发HTML5.1的消息,并表示初版将在今年6月中旬完成,9月则确定最终版。现在,W3C正在软件开发代码托管平台Github上展开相关开发工作,任何对数据修改器bikeshed和HTML熟悉的人也都能进行简单的代码编辑。', '<p>据外媒报道,<strong>很快,互联网上使用的主流语言就要来一次“整容”了,获悉,日前,万维网联盟(W3C)披露正在开发HTML5.1的消息,并表示初版将在今年6月中旬完成,9月则确定最终版。</strong>现在,W3C正在软件开发代码托管平台<a target=\"_self\" href=\"https://github.com/w3c/html\">Github</a>上展开相关开发工作,任何对数据修改器bikeshed和HTML熟悉的人也都能进行简单的代码编辑。<br></p><p>W3C表示,任何被发现的问题都值得他们关注。如果上面推荐的<a href=\"http://click.aliyun.com/m/3628/\" target=\"_blank\">解决方案</a>无法解决他们的问题,那么他们可以通过执行Pull Request从编辑或W3C职工得到方案。</p><p>W3C称,开发HTML5.1是为了让用户在网络的阅读变得更加容易,另外它还能精简未来潜在的变化程序。</p><p>如果HTML5.1的新功能有超过2个浏览器引擎不给予支持,那么W3C将选择抛弃,当这并不意味着就完全丢失,因为它还是可以以扩展的身份回归。比如一些在HTML5上的扩展,它就是由一些Google软件和User Interface信息库Web Components使用者开发。</p><p>据悉,HTML5于2014年10月正式发布标准版,不过它的开发工作却早在2008年的1月就已经开始了。</p><p><br></p>', 'static/upload/pics/5/17/2016k94aiko3_oyOc6piV-KNkF95.jpg', '网络', '2016-05-17 11:07:13', '241', '1', '1', '0', '6', '', '1', '1', '', '2', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('128', 'javascript常见数组操作整理', '数组常见操作包含了 增、删、查、改、插入、交集、并集', '<p>数组常见操作包含了 增、删、查、改、插入、交集、并集,现整理如下:</p>\n<p>1、数组整体元素修改</p>\n<pre><code class=\"lang-javascript\"> //map,给数组每个元素加1 输出[1,2,3]\n $.map([0,1,2],function(n){\n return n+1;\n })\n</code></pre>\n<p>2、 数组筛选</p>\n<pre><code class=\"lang-javascript\"> $.map([0,1,2],function(n){\n return n>0?n+1:null\n })\n //[2,3]\n</code></pre>\n<p>3、jquery 元素转数组</p>\n<pre><code class=\"lang-javascript\"> $(\"li\").toArray()\n $.makeArray($(\"li\"))\n</code></pre>\n<p>4、获取两个数组中相同部分或者不同部分</p>\n<pre><code class=\"lang-javascript\"> //去掉true则显示相同部分,保留true则显示不同部分\n var a=[1,2,3,5,6,3,7,12],\n b=[1,3,5,12]\n\n $.grep(a,function(n,i){\n if(b.indexOf(n)>=0)\n return n\n },true);\n //[2, 6, 7]\n</code></pre>\n<p>5、数组去重并倒序排序</p>\n<pre><code class=\"lang-javascript\"> var a=[1,2,3,5,6,3,7,12];\n $.unique(a)\n //[12, 7, 6, 5, 3, 2, 1]\n</code></pre>\n<p>6、数组排序</p>\n<pre><code class=\"lang-javascript\"> var arr=[1,34,5,8,4,9,12]\n arr.sort(function(a,b){\n return a-b;\n });\n //顺序:a-b [1, 4, 5, 8, 9, 12, 34]\n //倒序:b-a [34, 12, 9, 8, 5, 4, 1]\n</code></pre>\n<p>7、数组截取slice</p>\n<pre><code class=\"lang-javascript\"> var arr=[1,34,5,8,4,9,12];\n arr.slice(2,4) // [5, 8]\n //arr 输出 [1, 34, 5, 8, 4, 9, 12]\n</code></pre>\n<p>8、数组插入、删除splice(需明确位置)</p>\n<pre><code class=\"lang-javascript\"> var arr=[1,34,5,8,4,9,12];\n //删除\n arr.splice(2,4)\n //arr 输出[1, 34, 12]\n //替换\n arr.splice(1,2,3,4)\n //arr 输出[1, 3, 4, 8, 4, 9, 12]\n //插入\n arr.splice(2,0,44)\n //arr 输出[1, 34, 44, 5, 8, 4, 9, 12]\n</code></pre>\n<p>9、数组遍历</p>\n<pre><code class=\"lang-javascript\"> var members=[\"1\",\"2\",\"3\"];\n $.each(members,function(i,item){\n console.log(item);\n });\n //如何跳出当前的each循环\n //return false;——跳出所有循环;相当于 javascript 中的 break 效果。\n //return true;——跳出当前循环,进入下一个循环;相当于 javascript 中的 continue 效果。\n</code></pre>\n<p>10、jQuery根据元素值删除数组元素的方法</p>\n<pre><code class=\"lang-javascript\"> var arr = [\'a\',\'b\',\'c\',\'d\'];\n arr.splice($.inArray(\'c\',arr),1);\n console.log(arr);\n //[\"a\", \"b\", \"d\"]\n</code></pre>\n<p>11、常见的数组操作</p>\n<pre><code class=\"lang-javascript\"> push、pop、shift、unshift、concat\n</code></pre>\n<p>12、数组操作兼容性</p>\n<pre><code class=\"lang-javascript\"> IE8下\n $.inArray 代替 indexOf\n\n $.grep代替Array.prototype.filter\n</code></pre>\n<p>13、常见数组操作案例:</p>\n<p>1、jquery实现从数组移除指定的值</p>\n<pre><code class=\"lang-javascript\"> function delItem(arr,m)\n {\n return $.grep(arr,function(n,i){\n return n!=m\n });\n }\n var a=[1,2,3,5,6,3,7,12];\n delItem(a,3)\n</code></pre>\n<p>或者</p>\n<pre><code class=\"lang-javascript\"> function delItem(arr,m)\n {\n arr.splice($.inArray(m,arr),1);\n return arr\n }\n var arr = [\'a\',\'b\',\'c\',\'d\'];\n delItem(arr,\"c\")\n</code></pre>\n<p>2、jquery实现从数组移除指定的数组</p>\n<pre><code class=\"lang-javascript\"> function delArray(arr,delArr)\n {\n return $.grep(arr,function(n,i){\n if( delArr.indexOf(n)>=0)\n return n\n },true);\n }\n var a=[1,2,3,5,6,3,7,12],\n b=[5,7];\n delArray(a,b)\n</code></pre>\n<p>3、jquery找出2个数组同有的部分</p>\n<pre><code class=\"lang-javascript\"> function findCommonArray(arr,delArr)\n {\n return $.grep(arr,function(n,i){\n if( delArr.indexOf(n)>=0)\n return n\n });\n }\n var a=[1,2,3,5,6,3,7,12],\n b=[5,7,9];\n findCommonArray(a,b)\n</code></pre><p><br></p>', 'static/upload/pics/5/17/2016YuKqGjR0liiRzXoofEQPpjld.jpg', '阿华田', '2016-05-17 10:58:44', '332', '1', '1', '1', '7', 'js,jquery', '1', '1', '', '1', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('140', '大数据第一网红维克托见面会', '时间 :05-18 08:00 - 12:00\n地点:北京 - 国家会议中心四层大会堂', '<p><i>时间</i> :<i>05-18 08:00 - 12:00</i></p><p><i>地点:</i><i>北京 - 国家会议中心四层大会堂</i><br></p><ul></ul><h4>活动内容</h4><p>生活的奇妙之处,在于你永远不知道吃到的下一颗巧克力是什么味道,就像你突然被通知,不懂大数据都算不上合格公民了——“大数据”登上国家科技部和中央宣传部最近印发的《中国公民科学素质基准》。</p><p></p><p>话是这么说,大部分人对大数据只是一知半解,当个时髦的谈资,但世间总有高人在,一个牛人最早洞察了大数据的本质和秘密,成为大数据届当之无愧的超级网红。</p><p>他就是《大数据时代》作者维克托•迈尔•舍恩伯格,他将于5月18日在第八届中国云计算大会上与您见面。</p><p>作为国外大数据研究的先河之作,《大数据时代》阐述信息风暴正在变革我们的生活、工作和思维,在这个时代只需要知道“是什么”,而不需要知道“为什么”,这一观点颠覆了千百年来人类的思维惯例。</p><p>而维克托本人,被誉为“大数据商业应用第一人”和大数据时代的预言家。十余年潜心研究数据科学,担任耶鲁大学、芝加哥大学、弗吉尼亚大学、圣地亚哥大学、维也纳大学等多所高校的客座教授。</p><p>没有大数据,世界会怎样?</p><p>大数据是如何塑造和改变我们的商业?</p><p>如何不陷入大数据的困境?</p><p>……</p><p><a target=\"_blank\" href=\"http://huodong.tuicool.com/huodong/goto?id=5720b728651a5ece5e252359\">去官网报名</a><br></p><p><br></p>', '', '网络', '2016-05-16 15:46:53', '158', '0', '0', '0', '14', '', '0', '1', '', '6', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('141', '第八届中国云计算大会', '时间: 05-18 08:30 - 05-20 18:30\n\n地点:北京 - 国家会议中心', '<p><i>时间:</i> <i>05-18 08:30 - 05-20 18:30</i></p><p><i>地点:</i><i>北京 - 国家会议中心</i></p><h4>活动内容</h4><p>中国云计算大会迄今已有八年历史,是中国最大规模、最具影响力的年度云计算技术、产业、应用盛会,也是国内外云计算大数据领域最具权威性的政、产、学、研、用、融多方融合的高端大会。</p><p>第八届中国云计算大会预计超过14000人次,将承续前七届大会的成功经验,采用全体大会、专题论坛、展览展示等形式,突出行业应用,推动云计算大数据落地生根;聚焦技术创新,同步国内外创新成果,持续打造云计算大数据技术、产业和应用等领域最有影响力的交流平台。</p><p>参加第八届中国云计算大会,您将加入成为中国云计算技术与产业联盟、中国电子学会云计算专家委员会、中国大数据专家委员会与ZD至顶网共同组织成立的中国云计算研习会(以下简称研习会),享受如下特权:</p><p>与云计算、大数据专家线上、线下学术交流机会</p><p>享受研习会提供的与云计算、大数据生态伙伴商业对接机会</p><p>优先参与中国云计算技术与产业联盟、中国电子学会云计算专家委员会、中国大数据专家委员会组织的各项活动</p><p>免费活动研习会提供的专属学习资料</p><p>免费加入中国云计算大会社交圈,结识人脉 </p><p><a target=\"_blank\" href=\"http://www.ciecloud.org/2016\">去官网报名</a><br></p><p><br></p>', '', '网络', '2016-05-16 19:58:11', '133', '0', '0', '0', '14', '', '0', '1', '', '6', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('142', 'VR行业沙龙——创业路上那些坑', '时间 :05-18 13:30 - 16:30\n地点:北京 - 中关村鼎好大厦A座二层', '<p><i>时间</i> :<i>05-18 13:30 - 16:30</i></p><p><i>地点:</i><i>北京 - 中关村鼎好大厦A座二层</i></p><h4>活动内容</h4><p>活动目的:</p><p>分享、交流在VR创业的路上都遇到过哪些“坑”和困难,共同探讨如何规避创业风险,避免团队走不必要的弯路,让企业更高效的成长。</p><p>活动议程:</p><p>13:30-14:00 入场签到</p><p>14:00-14:10 开场介绍</p><p>14:10-14:25 DreamVR CMO 许京凯:初创团队做VR的艰辛路</p><p>14:25-14:40 暴风魔镜 CEO 黄晓杰:成熟型公司做VR也有坑(待确认)</p><p>14:40-14:55 VR竞技时代 COO 于阳:VR相关产业人的难处</p><p>14:55-15:10 Founder 创始人&CEO 伏英娜:VR创业技术上的难题</p><p>15:10-15:25 创客总部合伙人 李建军:投资人眼中VR雷区</p><p>15:25-15:35 互动抽奖:一台移动电源、两台VR头盔</p><p>15:25-16:00 圆桌论坛</p><p>16:00-16:20 互动提问</p><p>16:20-16:30 互动抽奖:一台智能摄像头、两台VR头盔</p><p><a target=\"_blank\" href=\"http://www.huodongxing.com/event/8333294048300\">去官网报名</a><br></p><p><br></p>', '', '网络', '2016-05-16 18:35:48', '112', '0', '0', '0', '14', '', '0', '1', '', '6', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('143', '一线互联网团队是怎么搞创业的? ——杨少锋回西安', '时间: 05-18 14:00 - 15:30\n地点:西安 - 陕西省图书馆', '<p><i>时间:</i> <i>05-18 14:00 - 15:30</i></p><p><i>地点:</i><i>西安 - 陕西省图书馆</i></p><h4>活动内容</h4><p>在资本寒冬里,有着丰富投资经验、创业经验的杨少锋总裁将与你分享他的真实经历,让你从浪尖的投资人、成功企业家的角度看互联网创业团队该如何度过。</p><p></p><p>成熟企业的架构是什么样的?</p><p>创业者的短板他如何避免?</p><p>杨少锋自己的最佳实践经验</p><p>工程师如何参与到企业利润分配?</p><p></p><p>... ...</p><p></p><p>『创业坐诊』</p><p>交流你的创业难题,杨少锋与你面对面交流,作为投资人+管理者的双重身份,他的解决方案到底有什么不同?</p><p>投资人到底看中项目、团队的什么?</p><p>公司的钱花在哪才是“刀刃”?</p><p>员工的薪资到底怎么平衡?</p><p>不烧钱到底能不能做市场?</p><p></p><p>... ...</p><p></p><p>『团队约聘』</p><p>本次归来,杨少锋总裁除了与大家分享他的经验外,还将与本地团队面对面约谈,寻找能够共同前进的同行者,一起让西安的互联网圈再次走出一匹黑马。</p><p>约聘团队要求:</p><p>1.研发团队</p><p>·具有比较完备的团队配置</p><p>·团队核心人员有3-5年及以上的开发经验</p><p>·优先条件:团队有过创业经验;团队有APP、IOS等开发经验</p><p>·如果你够优秀,一个人也请来!</p><p></p><p>2.运营团队</p><p>·大学毕业的标准果粉,谈资满满能说会道</p><p>·善于创作,并熟悉新媒体运营方式、投放渠道</p><p>·思维敏捷,年轻有活力,90后最爱啦!</p><p>·团队有3个或以上的小伙伴,一个能把控全盘、一个能高效产出、一个能搞定渠道</p><p>·优秀的小伙伴,一个人也可以!</p><p><a target=\"_blank\" href=\"http://huodong.tuicool.com/huodong/goto?id=57345e99651a5ece5e252ba7\">去官网报名</a><br></p><p><br></p>', '', '网络', '2016-05-16 18:41:27', '119', '0', '0', '0', '14', '', '0', '1', '', '6', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('144', '易启·创未来——NTESxWuhan 武汉创业基友见面会', '时间 :05-19 13:00 - 17:00\n\n地点:武汉 - 广埠屯资讯广场B座2楼', '<p><i>时间</i> :<i>05-19 13:00 - 17:00</i></p><p><i>地点:</i><i>武汉 - 广埠屯资讯广场B座2楼</i><br></p><ul></ul><h4>活动内容</h4><p>☆ 融合产品、运营、市场、技术全才 增长黑客</p><p>☆ 服务驱动体验 首席服务官</p><p>☆ 从0到1 运营践行者</p><p>☆ 百万次朋友圈转发 运营团队领袖</p><p>☆ 近百位创业基友们的智慧众筹</p><p></p><p>活动议程☆——</p><p></p><p>13:30——14:00 来宾签到/暖场视频</p><p>14:00——14:10 主持人开场</p><p>14:10——14:50 《冷启动和快速增长阶段的不同产品运营策略》</p><p>14:50——15:30 《服务驱动体验》</p><p>15:30——16:10 《运营之从0到1》</p><p>16:10——16:50 《创业公司运营团队组建及优化》</p><p>16:50——17:30 Q&A</p><p></p><p>演讲嘉宾☆——</p><p></p><p>No.1《冷启动和快速增长阶段的不同产品运营策略》</p><p>章鑫辉 网易云信市场运营总监</p><p>拥有丰富的产品、市场、运营经验,曾负责亿级用户产品,横跨三界。目前专注于面向开发者提供真正稳定的即时通讯云服务。截至目前,网易云信接入开发者数量超过5万+;日活总用户超过2.3亿。</p><p></p><p>No.2《服务驱动体验》</p><p>程青 网易七鱼 服务总监</p><p>阿里和网易十年的客户服务经历,拥有丰富的服务运营和产品经验。先后负责和参与了多个行业的服务中心规划和搭建,经历了各种阶段和不同形式的客户服务。目前主要负责网易云服务系列中的SaaS级软件,七鱼云客服的服务运营。网易七鱼于4月12日正式发布,上线后广受热捧。</p><p></p><p>No.3《运营之从0到1》</p><p>曾罗 极验验证运营负责人 </p><p>毕业于电子科技大学,毕业后加入华为从事营销、销售工作,2015年加入极验验证,市场和运营核心成员。极验验证独创行为式验证技术,帮助用户解决业务风险问题,目前日均验证上亿次,为超过6万家用户提供验证服务,并于2015年底获得IDG、红杉资本2400万美金的B轮融资。</p><p></p><p>No.4《创业公司运营团队组建及优化》</p><p>何斌 简寻创始人&CEO</p><p>连续创业者,华中科技大学联创团队前队长,曾获国际大学生超级计算机大赛HPL冠军并打破该比赛世界纪录;曾举办了全国第一场高校编程马拉松,也是武汉第一场编程马拉松,创业后带领团队做了武汉开发者峰会,做了传播近百万次的朋友圈游戏等标志性运营活动,和运营团队一起走过了从创建到优化的全过程。</p><p> ——☆活动奖品☆——</p><p>雷蛇 人气爆款 游戏鼠标 价值289元</p><p><a target=\"_blank\" href=\"http://huodong.tuicool.com/huodong/goto?id=57306258651a5ece5e252928\">去官网报名</a><br></p><p><br></p>', '', '网络', '2016-05-18 09:54:17', '198', '0', '0', '0', '14', '', '1', '1', '', '6', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('145', '初创公司的品牌营销误区及运营', '时间:05-19 13:00 - 17:00\n\n地点:北京 - 中关村东路1号科技大厦', '<p><i>时间</i>:<i>05-19 13:00 - 17:00</i></p><p><i>地点:</i><i>北京 - 中关村东路1号科技大厦</i></p><h4>活动内容</h4><p>创业者的梦想,产品上线,路演成功,融资到账,团队扩容,上市有望。</p><p>创业者的理想,用产品改变行业,用产品改变未来,然后成就自己的未来。</p><p>创业者的担心,我的理念和模式能被投资人认可么?</p><p>创业者的疑惑,怎么把自己和产品包装出去被认可?</p><p>那么这场分享会你应该来。</p><p>我们讲的不止是公关,而是更加符合创业者的新营销。</p><p>我们讲的不止是营销,还有那些创业的你想知道的潜规则。</p><p>创业营销进化论:更精准·更高效·更经济</p><p>2015年,在“大众创业、万众创新”“互联网+”的火热浪潮下,孵化器、联合办公空间在全国各地蓬然兴起,创业者迎来了黄金时代。</p><p>尽管这些创业者从事不同行业,但却有共同的特点,即创业初期会将全部精力投放在新产品开发、商业模式创新上。虽然内容为王,但是在“人人是媒体,处处是中心,无处是边缘“的时代,如果忽视企业传播价值,那么无异于失去强有力的后盾。</p><p>营销作为公司对外界主要的沟通桥梁,肩负着明晰企业商业价值的重要角色;通过配合公司阶段性的运营策略,提供舆论和品牌支持,塑造公司良好品牌形象,为公司营造良好发展环境,最大限度扩大企业品牌知名度和影响力,最终促进销售。</p><p>营销如此重要,那么问题来了,</p><p>初创企业究竟如何制定营销策略?</p><p>营销团队怎么搭建?</p><p>怎样以合适的方式讲企业故事?讲什么样的故事?</p><p>如何让媒体、投资者、大众知道你、了解你?</p><p>营销讯息的发布一般都找谁?</p><p>……………</p><p>基于此,启迪之星与三点一刻携手,共同为创业公司提供服务,为创业者答疑解惑。</p><p><a target=\"_blank\" href=\"http://www.huodongxing.com/event/7334186290800\">去官网报名</a><br></p><p><br></p>', '', '网络', '2016-05-18 09:58:46', '93', '0', '0', '0', '14', '', '1', '1', '', '6', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('146', '知行合一,集客营销策略解析 第三弹', '时间 :05-19 13:00 - 17:30\n\n地点:广州 - 待定', '<p><i>时间</i> :<i>05-19 13:00 - 17:30</i></p><p><i>地点:</i><i>广州 - 待定</i><br></p><ul></ul><h4>活动内容</h4><p>20年前,人们不会想到互联网会以如此迅猛之势渗入到生活工作中。</p><p>但是今天,我们可以预见到云端SaaS工具集将会变成未来营销的主流。</p><p>知是行之始,行是知之成,我们将以点入面,先带您深入了解M1云端市场部作为营销工具集集大成者的价值所在,再深入浅出,以最浅易的方式带您掌握工具的使用方法。</p><p>知行合一,走在时代的前端,用更简单的方式做更专业的营销。</p><p></p><p>议程安排</p><p>12:30-13:30 来宾签到</p><p>13:30-14:00 主持人开场,各位来宾自我介绍</p><p>14:00-14:30 </p><p>应对消费升级的集客营销</p><p>程然 梅花网研究院执行院长</p><p>14:30-14:50</p><p>集客营销的执行利器—梅花网M1</p><p>程然 梅花网研究院执行院长</p><p>15:10-15:30 完成一次营销,你只需要利用这些工具</p><p>15:30-16:00 现场互动讨论</p><p>16:00 活动结束</p><p><a target=\"_blank\" href=\"http://event.meihua.info/539\">去官网报名</a><br></p><p><br></p>', '', '网络', '2016-05-18 10:00:57', '102', '0', '0', '0', '14', '', '1', '1', '', '6', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('147', '创新、融合:聚焦“新经济”下的智能化行业', '时间: 05-19 13:00 - 06-19 17:00\n\n地点:杭州 - 味雅咖啡', '<p><i>时间:</i> <i>05-19 13:00 - 06-19 17:00</i></p><p><i>地点:</i><i>杭州 - 味雅咖啡</i><br></p><ul></ul><h4>活动内容</h4><p>“新经济”——时代转折的深层思考,带来更多商机。当前我国正处在经济转型发展的关键时期,过去代表经济增长主动力的传统动能改造升级速度加快,新动能不断出现,新经济格局正在形成。两会期间,“新经济”这一概念成为行业内外关注的热点。作为对时代转折的深层思考,“新经济”不仅代表着“互联网+”、信息技术或信息服务等新产业和新业态,昭示着传统行业从工业时代迈向信息时代转变所带来的创新和融合。</p><p>“新经济”推动智能化行业发展。对于智能化行业来说,信息时代的“新经济”形态着力于突破工业时代的传统经济形态,呈现出比规模经济和范围经济更为丰富、更为深刻的内容。随着云、网、端等新的生产基础设施的出现,以及数据作为新生产要素的加入,“新经济”正成为推动智能化行业生产、管理和营销模式变革,重塑产业链、供应链、价值链的有效助力。</p><p>本届全国巡回论坛将于5月19日在杭州举办论坛活动,以“创新、融合:聚焦“新经济”下的智能化行业”为主题。届时将邀请资深学者、品牌高层、系统集成商、设计院、房地产开发商、运营商及软件服务商等相关行业人士出席,并结合当地市场发展,以最新趋势观察,集中体现智能化行业发展过程中取得的进展和成效,帮助与会嘉宾对智能化行业发展及市场趋向能够拥有更加深入的了解。</p><p><a target=\"_blank\" href=\"http://huodong.tuicool.com/huodong/goto?id=572c4087651a5ece5e25280c\">去官网报名</a><br></p><p><br></p>', '', '网络', '2016-05-18 10:05:36', '105', '0', '0', '0', '14', '', '1', '1', '', '6', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('155', '前端开发者的代码回顾清单', '\n我们的目的并不是定义一套固有的代码回顾实践方式,而是为回顾者 (reviewer) 提供一份清单。\n\n当然,它也帮助开发者了解在代码回顾时如何扮演好一名回顾者的角色。回顾者本应如此专注于撰写更好更简洁的代码。\n\n因为代码的作者没有进行自我回顾。所以开发者需要一种新的方式——很显然他们可以持续提炼并改进代码。', '<p>我们的目的并不是定义一套固有的代码回顾实践方式,而是为回顾者 (reviewer) 提供一份清单。</p><p></p><p>当然,它也帮助开发者了解在代码回顾时如何扮演好一名回顾者的角色。回顾者本应如此专注于撰写更好更简洁的代码。</p><p></p><p>因为代码的作者没有进行自我回顾。所以开发者需要一种新的方式——很显然他们可以持续提炼并改进代码。</p><p></p><p><b>理念</b></p><p></p><p>代码回顾对事不对人 (Code Review is about the code not about the coder)</p><p></p><p>首先,代码回顾只专注在写出来的代码上。</p><p></p><p>这种场合最应该做的是确认代码符合既定标准、最佳实践等等,务必回避指责作者的行为。</p><p>没有人能够挑战这一原则,不论他是新雇员、老鸟、主管哪怕是 CTO。</p><p></p><p>我们必须意识到,没有任何代码是足够优秀到不需要回顾的。</p><p></p><p>同时,这也是分享开发技术技巧的绝佳时机,再顺便探讨一下当下流行的编码方式,何乐而不为呢?</p><p></p><p><b>清单</b></p><p></p><p>这里的清单是指一套代码回顾时遵循的要点集合。</p><p></p><p>同时这份清单也会帮助开发者理解哪些东西需要回顾。</p><p></p><p>0. 编码规范</p><p>这件事情排在步骤 0,因为实际上它并不算在清单本身的范畴内:编码规范是需要时刻遵守的。</p><p></p><p>也就是说,每一名开发者都必须遵循既定的编码规范。</p><p></p><p>1. 代码质量</p><p>A:代码质量能够确保:</p><p>B:减少 bug 数量;</p><p>C:让每个人都读得懂。</p><p>D:前端团队可以通过 JSHint 或 Plato 等类似的工具来监控 JavaScript 代码的质量。</p><p></p><p></p><p>2. 一致性</p><p>回顾者要确保写出的代码能够满足需求,正常工作。</p><p></p><p>3. 简单</p><p>A:回顾者要确保代码直接有效的解决了问题。</p><p>B:然后我们可以通过 JSHint (详见第 2 条) 来计算条件复杂度。</p><p>C:复杂度和 bug 的出现频率是由相关性的。</p><p>D:基本上,越是复杂的地方,就越要简化。</p><p></p><p></p><p>4. 可维护性</p><p>回顾者确保另一名开发者也可以对现有代码进行快速修改。</p><p>回顾者确保代码是 (比如通过一些模式达到) 模块化的,并且变量和方法的命名是清晰的。</p><p>HTML 和 CSS 选择器也同样如此。我们必须能够理解 class 或方法表现出的含义。</p><p>还要确保一个方法做一件事的原则。</p><p></p><p>5. 性能</p><p>性能问题不仅限于 js 或 css 文件的压缩比率以及对图片精灵的使用。</p><p>它还存在于循环、数组操作、DOM 操作等方方面面……</p><p>jsPerf 是一个非常棒的工具,它可以在线测试一段代码在各种浏览器中的性能:http://jsperf.com/。</p><p><br></p>', '', '前端早读课', '2016-05-20 11:43:34', '138', '0', '1', '0', '1', '', '0', '1', '', '2', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('156', '用户体验自检清单', '每一次的项目发布都是胆战心惊的,最怕发布出去后没过多久就要重新发布一次(羞羞~~)。多次之后发现基本都是一些常规性的问题比如title,alt,是否屏蔽爬虫,seo相关,还有一些上线前的数据准备。所以沟通完做了一份每次上线前的清单,提醒检查。所以今天分享的关于用户体验的检查清单,产品就是需要打磨细节的。', '<p>ps:每一次的项目发布都是胆战心惊的,最怕发布出去后没过多久就要重新发布一次(羞羞~~)。多次之后发现基本都是一些常规性的问题比如title,alt,是否屏蔽爬虫,seo相关,还有一些上线前的数据准备。所以沟通完做了一份每次上线前的清单,提醒检查。所以今天分享的关于用户体验的检查清单,产品就是需要打磨细节的。</p><p> </p><p>用户体验</p><p> </p><p>个性化的功能,货币、语言、具体国家的税收、还有快递选项是基于用户位置的。未经用户授权,不能启用基于IP的地理位置。</p><p> </p><p>让用户觉得注册使用产品有价值,『免费试用』的按钮要比『注册』按钮更吸引用户。并且,如果能够不注册就允许用户使用,那么就不要加入注册流程,这会让更多人愿意试用你的产品。</p><p> </p><p>透明的价格,价格需要被清晰地显示出来,不要隐藏在条款里隐藏价格或者相关信息。</p><p> </p><p>页面不自动刷新,如果页面突然有新的内容加载,用户会感到困惑。比如在浏览新闻网站时候。</p><p> </p><p>演示内容,比如,在一个表单旁边加上一个该表单内容的示例。</p><p> </p><p>证明网站真实可信,真实可靠的参考资料、凭证(备案)、联系方式,地点以及在网站上显示真实的人像。此外,网站需要经过专业的设计和保证及时的更新。</p><p> </p><p>整洁的产品和服务信息,信息是可以被扫描的,图片可以被放大或缩小以保证不同尺寸下的可见。</p><p> </p><p>首页</p><p> </p><p>明确的操作步骤,让用户知道下一步怎么做,以及为什么要这么做。让用户明白网站的价值和目的。</p><p> </p><p>第一印象,首页能够给用户留下一个好的印象并且支持跳转。</p><p> </p><p>显示已登录的用户名,比如显示『你好,Charles 』 而不是『您好,先生』。</p><p> </p><p>在主页上进行发布一些重要变化的通知。如策略的变更或网站停机等。</p><p> </p><p>能够很轻松的从主页上获取到公司的地点和联系方式。</p><p> </p><p>隐私策略,如果网站获取了用户的信息,应该要保护用户的隐私。</p><p> </p><p>图片、视频必须是与主题相关的或有意义的,不要有不相关的照片,要有高质量的产品和服务的截图、视频或者照片等。未经用户允许,音频或者视频不要自动播放。</p><p> </p><p>URL重定向,网站有无www均可以访问。比如 “www.userium.com “ 和 “userium.com”。</p><p> </p><p>可访问性</p><p> </p><p>给非文本元素加上Alt属性,如图像和地图,然后给音频和视频加上字幕和说明。</p><p> </p><p>单色不足以传达信息。</p><p> </p><p>内容在没有css样式下都可以顺利阅读。</p><p> </p><p>链接,复选框,按钮容易被点击,例如用户可以通过点击文本达到选择复选框的目的。</p><p> </p><p>导航</p><p> </p><p>重要的链接不要去使用动画特效,比如转盘或者轮播效果。</p><p> </p><p>不要只是用按字母排序,如果有更好的排序方式(如分类、等 )尽量不要使用按字母排序方式。</p><p> </p><p>让用户能够明确的知道所处位置,比如Wordpress里的breadcrumbs效果,还有大型网站的站点地图。</p><p> </p><p>导航在每一个页面上都是一致的。</p><p> </p><p>链接都是具有描述性的,不要出现「点击这里」的链接。</p><p> </p><p>在网页的标题要有一个简洁的描述,这样的话在书签中很容易被识别和理解。</p><p>网址如果能令人记忆深刻就最好了。</p><p> </p><p>搜索</p><p> </p><p>在数据比较多的网站上,需要有一个全局搜索。</p><p> </p><p>在每一个页面都能找到搜索框,而不仅仅是在首页。</p><p> </p><p>用户可以随时随地的开始搜索,而不是需要点击链接才能搜索。</p><p> </p><p>链接</p><p> </p><p>重要的操作都应该使用按钮,比如「购买」「支付」都应该是按钮,而不是链接。</p><p> </p><p>链接很容易辨识,他门应该是看起来可点击的。不是链接的文字不要做成可点击状态。</p><p> </p><p>访问过的链接颜色要不同于未访问过的。</p><p> </p><p>不要有不能访问的链接。</p><p> </p><p>布局</p><p> </p><p>重要的内容优先显示。</p><p> </p><p>响应式设计 ,网站的内容要能适应不同尺寸的屏幕。不要出现横向的滚动条。</p><p> </p><p>相关的信息作为一个组合放在一起。这样看起来清晰易读。</p><p> </p><p>尽量少使用弹出框。</p><p> </p><p>页面保证整洁,保证足够的留白。</p><p> </p><p>流程</p><p> </p><p>问题跟踪。有一个专门的地方处理可用性和用户体验问题。</p><p> </p><p>用户测试。在开发的不同阶段都需要进行可用性测试。参与测试的应该是非项目开发和设计成员。</p><p> </p><p>优先级。根据功能重要程度、目的、紧迫程度、预算和其它限制因素等确定功能的优先级 。</p><p> </p><p>改版的影响分析。如果变化会影响到产品的其他部分,其他人或者整个流程,改版可能会变得费时费力的。所以在做任何改变的时候做影响分析是很重要的。</p><p> </p><p>网站改版前后的投入产出比评估。</p><p> </p><p>表单</p><p> </p><p>简洁。表单里只出现必要的项目。</p><p> </p><p>避免出现很长的下拉列表。可以采用通过文字进行筛选的方式,长的下拉菜单在鼠标滚动的时候容易出错。</p><p> </p><p>出错提示出现在输入框旁边,而不要出现在页面顶部。</p><p> </p><p>内容</p><p> </p><p>对比。文字和背景之间要有明确的对比度 。</p><p> </p><p>内容是可以被扫描的,尽量使用简洁的段落,描述性的标题,列表和图片。适当地使用一些视觉元素,而不是大片的文字。</p><p> </p><p>内容采用用户可以理解的通用语言。</p><p> </p><p>联系方式和公司信息能够被清楚地显示出来,点击联系的链接不要自动打开邮件应用程序。</p><p> </p><p>内容是有用的并且经常保持更新,常用问题给出明确的答案。不要出现很长的介绍或者「欢迎使用我们网站」之类的文字。</p><p>不要在文本中滥用大写,大写仅限于在具体格式中使用。</p><p> </p><p>ps:针对常规性的检查,如果能靠工具检查的就尽量使用工具自动化(比如书签)这样更方便。</p><p><br></p>', '', '前端早读课', '2016-05-20 11:46:00', '91', '0', '0', '0', '1', '', '1', '1', '', '2', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('157', '怎样快速学会一门技术', '前几天fork了Ruby China的源码,面对陌生的Ruby技术栈,一头雾水。我fork它并不单为了学习,而是要在最短的时间搭建起我脑海中的社区网站。所以我不可能针对每一门新技术都去买一本书来读上半个月', '<p>前几天fork了Ruby China的源码,面对陌生的Ruby技术栈,一头雾水。我fork它并不单为了学习,而是要在最短的时间搭建起我脑海中的社区网站。所以我不可能针对每一门新技术都去买一本书来读上半个月。</p><p>我在本机运行起Ruby China,新注册一个用户,发现不能发帖,提示说要注册一个月以上才可以。于是我去找相关代码:</p><p> </p><p># 是否能发帖 </p><p>def newbie? </p><p>return false if self.verified == true </p><p>self.created_at > 1.week.ago end</p><p> </p><p>明明是一星期嘛,代码是不会说谎的。于是把文字改掉,顺便提个pull request。扯远了,代码说如果是self.verified就不是新手了,我先去管理后台看了一下,没有修改这个字段的界面。于是我不得不去数据库里更新这个字段。我大概知道mongodb是数据库,但我不知道该怎么操作。</p><p> </p><p>从宏观出发</p><p> </p><p>当我了解到一个新的技术名词,不会直接陷入细节,而是从宏观上把握它。了解它的背景,为何出现,解决什么问题,有什么同类技术,没有它之前我们如何工作。因为有了宏观的了解,我就能很容易把它和我熟悉的技术去建立关联,从而更快地理解它。</p><p></p><p>实践出真知</p><p> </p><p>打开官网,发现有个非常棒的Try it out,先花10分钟玩一下,对其玩法有个大概了解,然后再来解决实际的问题。</p><p>从前面的Try it out中我知道了help命令,于是便通过help知道了show dbs,use ruby_china_dev。根据使用SQL的经验,我想当然地认为更新一条记录的一个字段应该是这样:</p><p>db.users.update({\"_id\":3}, {verified: true})</p><p> </p><p>但我悲剧地发现,整条记录被替换了,好吧,这就是文档型数据库。于是放狗搜索:</p><p> </p><p>How to update specific field in mongodb</p><p> </p><p>很快便找到了答案。</p><p> </p><p>db.myCollection.update({condField: \'condValue\'}, { $set: { dateField: new Date(2011, 0, 1)}}, false, true);</p><p> </p><p>对应我这里的需求就是:</p><p> </p><p>db.users.update({_id:3}, {$set:{verified:true}}, false, true)</p><p> </p><p>问题解决了。</p><p>随着接触的越来越深,遇到的问题就会越来越多。我会把每一个解决掉的问题放到我的Evernote里,这样下次再遇到就能很快找到答案。当我发现postach.io这个可以和Evernote同步的博客系统后,就开通了这样一个博客,其他人遇到同样的问题时也更容易找到答案了。当我真正对一门技术感兴趣并且有足够的时间去学习时,我通常会按照下面的步骤去学习。</p><p>教是最好的学</p><p> </p><p>实践足够多后时,我可能觉得已经掌握这门技术了。但当我尝试去表达的时候,会惊讶地发现还有很多概念是似懂非懂的。于是我会去查资料,完善自己的体系。只有当我把学到的东西用自己的语言表达出来,并且能让听众明白的时候,才是真正掌握了该技术。学会分享是很重要的,把在该技术上的经验总结出来,写成博客,集结成书出版,再到行业会议上分享实践经验。这样才能成为该领域公认的专家。</p><p> </p><p>推迟学习</p><p> </p><p>一位一年读100多本书的同事说:</p><p> </p><p>你一年才能读几十本书,就不要什么书都看了,多花点时间挑书吧。</p><p> </p><p>现在新技术层出不穷,我们没有那么多时间去深入学习每一门。对于大多数技术,我们只需要搞懂概念部分,从宏观上了解一下,决定我们要不要深入地去学习它。有了这些了解,就能轻松地与别人聊天了,也可以为以后技术选型做一些储备。当面对真正的需求时,或者你有足够的时间做技术储备时,才去进入实践部分。</p><p>编程是一个知识更新很快的行业,只有真正有热情并掌握了好的学习方法的人,才能走的长久。</p><p> </p><p>PS:早读君看到这个身有体会,最近在利用nodeJS结合码农日报的rss来做阅读。我可以分享下整个做的想法:</p><p>A:当初的想法其实就很小,能在命令行中输出那些标题,突然有一天想把这些内容分享给团队的同事看但总不能跑到我这看吧,那这样就需要一个可视化界面了。</p><p>B:那天周五晚下班后在咖啡馆又找了周刊的rss,那多个周刊要在同一套代码里显示,我所想到的就是用参数来做配置,根据不同的参数读取不同的内容。</p><p>其实还是跟早读君实现其他产品思路,学一门新技术一样。基于产品实现学技术学得会更踏实。可能刚开始的时候都是点,但相信到了一定程度后那就是把点结网成面。</p><p><br></p>', '', '前端早读课', '2016-05-20 18:45:55', '152', '0', '0', '0', '1', '', '0', '1', '', '2', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('158', 'html5本地存储localStorage介绍', 'localStorage是html5新增的功能,它的作用是可以在浏览器里保存我们需要的信息,从而可以在页面之间切换和使用,比cookie方便,也比cookie能够存储更多的东西,下面是几个常用的localStorage方法', '<p>localStorage是html5新增的功能,它的作用是可以在浏览器里保存我们需要的信息,从而可以在页面之间切换和使用,比cookie方便,也比cookie能够存储更多的东西,下面是几个常用的localStorage方法:</p><p><b>1、添加localStorage</b></p><p>localStorage.setItem(“key”,”value”); //以“key”为名称存储一个值“value”</p><p><b>2、获取localStorage</b></p><p>localStorage.getItem(“key”); //获取名称为“key”的值</p><p><b>3、删除localStorage</b></p><p>localStorage.removeItem(“key”); //删除名称为“key”的信息</p><p><b>4、清空localStorage</b></p><p>localStorage.clear(); //清空localStorage中所有信息</p><p><b>5、查看已经保存的localStorage</b></p><p>可通过chrome浏览器的控制台工具Resource–Local Storage里查看</p>', '', '前端汇', '2016-05-20 13:41:09', '99', '0', '1', '0', '6', 'html5', '0', '1', '', '1', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('159', 'html5shiv,让低版本的IE浏览器支持HTML5元素(IE8)', '随着html5的盛行,我们在页面中也越来越多的运用到html5元素,但是悲催的是,脑残的IE的低版本实在让人抓狂,咱们辛苦规划好的页面在这些IE版别下显得是浆糊一桶。为了让这不一样的IE版别之间能”认识”HTML5元素,开源的html5shiv呈现了!', '<p>八年抗战,html5终于定稿,近年来,随着html5的盛行,我们在页面中也越来越多的运用到html5元素,但是悲催的是,脑残的IE的低版本实在让人抓狂,咱们辛苦规划好的页面在这些IE版别下显得是浆糊一桶。为了让这不一样的IE版别之间能”认识”HTML5元素,开源的html5shiv呈现了!</p><p><font size=\"4\" style=\"\">1、html5shiv的特点\n</font></p><p>html5shiv的最大特点是让那些个不认HTML5的IE浏览器认出HTML5元素,并依照最基本的处理方法处理HTML5元素——块化(display:block)。这样就阻止了脑残的IE对我们前端人员的残害。</p><h2><font size=\"4\">2、html5shiv的使用方法</font></h2><p>html5shiv的运用方法非常简略,默许只需要将html5shiv按下面方法放在页面的head内即可。</p><pre style=\"max-width:100%;overflow-x:auto;\"><code class=\"javascript hljs\"><!--[<span class=\"hljs-keyword\">if</span> lt IE <span class=\"hljs-number\">9</span>]>\n<span class=\"xml\"><span class=\"hljs-tag\"><<span class=\"hljs-name\">script</span> <span class=\"hljs-attr\">src</span>=<span class=\"hljs-string\">\"dist/html5shiv.min.js\"</span>></span><span class=\"undefined\"></span><span class=\"hljs-tag\"></<span class=\"hljs-name\">script</span>></span>\n<span class=\"hljs-tag\"><<span class=\"hljs-name\">![endif]--</span>></span></span></code></pre><h2><font size=\"4\">3、html5shiv的原理</font></h2><p>html5shiv的原理是使用createElement方法,这包含document.createElement和document.createDocumentFragment,对当前页面的HTML5元素进行动态的调整,并且为这些元素提供最基本的样式。<br></p><h2><font size=\"4\">4、html5shiv官方给的建议</font></h2><p>a.使用min版本的js(压缩过的)以节省带宽和提高加载速度;</p><p>b.必须在body元素之前加载;</p><p>c.可以在页面的CSS之前或者之后加载,但从性能性能上出发,CSS先于html5shiv加载会更优。</p><p></p><p>参考地址:</p><p>html5shiv在GitHub上的地址:https://github.com/aFarkas/html5shiv</p><p><br></p>', '', '前端汇', '2016-05-20 13:46:26', '113', '0', '1', '0', '6', '', '0', '1', '', '1', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('160', 'HTML5获取摄像头视频演示', '', '<p>html5新增了很多强大的API,其中可以获取设备的摄像头视频是个比较大的亮点,零度从网上找到了一些代码,进行了整合,搞出来一个html5获取摄像头的例子,大家可以参考一下。</p><p>主要代码如下:<br></p><p><br></p><pre style=\"max-width:100%;overflow-x:auto;\"><code class=\"javascript hljs\"><span class=\"hljs-built_in\">window</span>.addEventListener(<span class=\"hljs-string\">\"DOMContentLoaded\"</span>, <span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span>(<span class=\"hljs-params\"></span>) </span>{\n <span class=\"hljs-keyword\">var</span> canvas = <span class=\"hljs-built_in\">document</span>.getElementById(<span class=\"hljs-string\">\"canvas\"</span>),\n context = canvas.getContext(<span class=\"hljs-string\">\"2d\"</span>),\n video = <span class=\"hljs-built_in\">document</span>.getElementById(<span class=\"hljs-string\">\"video\"</span>),\n videoObj = { <span class=\"hljs-string\">\"video\"</span>: <span class=\"hljs-literal\">true</span> },\n errBack = <span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span>(<span class=\"hljs-params\">error</span>) </span>{\n <span class=\"hljs-built_in\">console</span>.log(<span class=\"hljs-string\">\"视频获取失败: \"</span>, error.code); \n };\n\n <span class=\"hljs-keyword\">if</span>(navigator.getUserMedia) { <span class=\"hljs-comment\">// Standard</span>\n navigator.getUserMedia(videoObj, <span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span>(<span class=\"hljs-params\">stream</span>) </span>{\n video.src = stream;\n video.play();\n }, errBack);\n } <span class=\"hljs-keyword\">else</span> <span class=\"hljs-keyword\">if</span>(navigator.webkitGetUserMedia) { <span class=\"hljs-comment\">// WebKit-prefixed</span>\n navigator.webkitGetUserMedia(videoObj, <span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span>(<span class=\"hljs-params\">stream</span>)</span>{\n video.src = <span class=\"hljs-built_in\">window</span>.webkitURL.createObjectURL(stream);\n video.play();\n }, errBack);\n } <span class=\"hljs-keyword\">else</span> <span class=\"hljs-keyword\">if</span>(navigator.mozGetUserMedia) { <span class=\"hljs-comment\">// moz-prefixed</span>\n navigator.mozGetUserMedia(videoObj, <span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span>(<span class=\"hljs-params\">stream</span>)</span>{\n video.src = <span class=\"hljs-built_in\">window</span>.URL.createObjectURL(stream);\n video.play();\n }, errBack);\n }\n \n}, <span class=\"hljs-literal\">false</span>);</code></pre><p><br></p>', '', '前端汇', '2016-05-20 13:49:04', '349', '0', '0', '0', '6', 'html5,摄像头', '0', '1', '', '1', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('161', 'H5网页判断手机横屏或是竖屏', '我们做出来的H5页面在手机端浏览的时候,用户很有可能会产生更换横竖屏的操作,这时如果我们能够判断出横竖屏,就可以更好的优化我们的网页,进而拥有更好的用户体验度', '<p>我们做出来的H5页面在手机端浏览的时候,用户很有可能会产生更换横竖屏的操作,这时如果我们能够判断出横竖屏,就可以更好的优化我们的网页,进而拥有更好的用户体验度。下面是判断横竖屏的代码:<br></p><pre style=\"max-width:100%;overflow-x:auto;\"><code class=\"javascript hljs\"><span class=\"hljs-built_in\">window</span>.addEventListener(<span class=\"hljs-string\">\'orientationchange\'</span>, <span class=\"hljs-function\"><span class=\"hljs-keyword\">function</span>(<span class=\"hljs-params\">event</span>)</span>{\n <span class=\"hljs-keyword\">if</span> ( <span class=\"hljs-built_in\">window</span>.orientation == <span class=\"hljs-number\">180</span> || <span class=\"hljs-built_in\">window</span>.orientation==<span class=\"hljs-number\">0</span> ) {\n alert(<span class=\"hljs-string\">\"竖屏\"</span>);\n }\n <span class=\"hljs-keyword\">if</span>( <span class=\"hljs-built_in\">window</span>.orientation == <span class=\"hljs-number\">90</span> || <span class=\"hljs-built_in\">window</span>.orientation == <span class=\"hljs-number\">-90</span> ) {\n alert(<span class=\"hljs-string\">\"横屏\"</span>);\n }\n});</code></pre><p>大家可以拿到手机上去试一下<br></p><p><br></p>', '', '前端汇', '2016-05-20 13:50:50', '272', '0', '0', '0', '6', '', '0', '1', '', '1', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('162', '整站快速变黑白灰的方法', '经常看到一些网站遇到重大灾害事故的时候,选择默哀,会把网站的整体颜色变成黑白灰,这种技术其实并不复杂,仅仅是通过滤镜就可实现,下面我把我常用的方法分享给大家。', '<p>经常看到一些网站遇到重大灾害事故的时候,选择默哀,会把网站的整体颜色变成黑白灰,这种技术其实并不复杂,仅仅是通过滤镜就可实现,下面我把我常用的方法分享给大家。\n</p><p>\n</p><p>在css文件中插入以下代码:</p><pre>html {\nfilter:progid:DXImageTransForm.Microsoft.BasicImage(grayscale=1);\n-webkit-filter: saturate(0);\n} </pre><p>保存刷新,你会发现页面瞬间变成了黑白灰基调。当然我们也可以在html文档中直接添加:</p><p></p><pre><style type=”text/<a href=\"http://www.lingdublog.com/tag/css\" target=\"_blank\">css</a>”>\nhtml{\nfilter:progid:DXImageTransform.Microsoft.BasicImage(grayscale=1);\n}\n</style></pre><p>效果是一样的,通过css滤镜把网页颜色改变。</p><p>值得注意的是,如果您的网站中有flash动画的话,它的颜色不会改变,这时您可以在在FLASH代码的<object …>和</object>之间插入:</p><p></p><pre><param value=”false” name=”menu”/>\n<param value=”opaque” name=”wmode”/> </pre><p><br></p><p><br></p>', '', '前端汇', '2016-05-20 13:52:29', '89', '0', '0', '0', '5', '', '0', '1', '', '1', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('163', 'CSS3的属性选择器', 'CSS3中新增了许多选择器,今天给大家说说CSS3的属性选择器。', '<p>CSS3中新增了许多选择器,今天给大家说说CSS3的属性选择器。\n</p><p>\n</p><p>与CSS2相比,CSS3新增了3种属性选择器:[attr^=value]、[attr$=value]、[attr*=value];分别来讲解一下。</p><h2><font size=\"4\">一、[attr^=value]属性选择器</font></h2><p>大家如果接触过正则表达式的话,应该知道^符号的意思,是表示开头,没错,这里也是表示开头的意思,意思就是选择属性名attr的开头值为value的元素!</p><p>举个例子:</p><p></p><pre><style type=\"text/css\">\n[class^=tea]{width:200px; height:200px;} /*此选择器表示选择class开头字符串为tea的元素*/\n</style>\n\n<div class=\"teacher\">我的class是teacher</div>\n<div class=\"tea\">我的class是tea</div>\n<div class=\"teach\">我的class是teach</div>\n<div class=\"aaa\">我的class是aaa</div></pre><p>这样会选择前三个元素,而不会选择最后一个元素。</p><h2><font size=\"4\">二、[attr$=value]属性选择器</font></h2><p>相比较前一个,这一个就很容易理解了,$符号就代表结尾,这里意思是选择属性名attr的结尾值为value的元素!</p><p>例子:</p><p></p><pre><style type=\"text/css\">\n[class$=er]{width:200px; height:200px;} /*此选择器表示选择class结尾字符串为er的元素*/\n</style>\n\n<div class=\"teacher\">我的class是teacher</div>\n<div class=\"tea\">我的class是tea</div>\n<div class=\"teach\">我的class是teach</div>\n<div class=\"aaa\">我的class是aaa</div></pre><p>这样就会选择class为teacher的div。</p><h2><font size=\"4\">三、[attr*=value]属性选择器</font></h2><p></p><p>最后这一个和前面两个的区别是符号换成了*,这个代表通配符的意思,意思是选择属性名attr的值包含value的元素!</p><p></p><pre><style type=\"text/css\">\n[class$=ch]{width:200px; height:200px;} /*此选择器表示选择class包含字符串为ch的元素*/\n</style>\n\n<div class=\"teacher\">我的class是teacher</div>\n<div class=\"tea\">我的class是tea</div>\n<div class=\"teach\">我的class是teach</div>\n<div class=\"aaa\">我的class是aaa</div></pre><p>这样就会选择class为teacher和class为teach的两个div。</p><p>这三种属性选择器大家如果用的熟练的话会对提高工作效率有很大帮助。</p><p><br></p>', '', '前端汇', '2016-05-20 13:55:57', '80', '0', '0', '0', '0', '', '0', '1', '', '0', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('166', 'i创中国·创新医疗专场', '时间:05-21 13:00 - 17:00\n\n地点:杭州 - 城中香格里拉大酒店', '<p><i>时间</i>:<i>05-21 13:00 - 17:00</i></p><p><i>地点:</i><i>杭州 - 城中香格里拉大酒店</i><br></p><ul></ul><h4>活动内容</h4><p>从一名创业者到企业家,你需要跨越的不仅仅是盈利额的攀升,更是用新思维来定义旧领域的过程。从传统到新锐,创新医疗者能做的不仅仅是运营一个项目发明一个产品,而是颠覆医疗霸权,拯救更多的生命。</p><p>5月21日,由i创中国、浙商传媒(《浙商》杂志)主办,医路演联合主办,永炜投资特别赞助的i创中国·2016浙商创新创业大赛创新医疗专场即将拉开帷幕。美国硅谷行业大咖与众多医疗行业创新创业者将齐聚杭州,共话医疗产业的现状和趋势。</p><p>丨海外大咖丨</p><p>演讲主题:硅谷最新移动医疗的现状和趋势</p><p>Skip Fleshman</p><p>【大咖简介】</p><p>硅谷著名的专注于投资早期阶段的移动医疗、细胞治疗、医疗大数据分析等领域的风投公司AMV创始合伙人,斯坦福大学孵化器StartX、硅谷最著名的移动医疗孵化器Rock Health以及波士顿儿童医院的顾问。</p><p>曾投资公司:HealthTap, Reify Health, Evidation Health, 1DocWay, Lark, Arterys,Welkin Health, WellDoc, Ooma, Kii, LiquidM, RallyPoint 和Icon Aircraft。其中,退出案例包括:Skybox、Digisec、Chimerix、1Docway、StrataVia、Ooma and Provade等</p><p>丨优质路演项目丨</p><p></p><p>海外项目一:VR治疗青光眼</p><p>项目来自于nGoggle,一家位于美国加州圣地亚哥的研发便携式的人脑—电脑交互诊断工具的公司,专注于诊断青光眼(失明的主要诱因之一)、黄斑变性眼科疾病等其他眼科疾病,以及神经退行性疾病及其他神经失调疾病。以OculusRift硬件结合脑电图来测量神经系统反应及状态变化,将其转化为视觉刺激的原理,实现客观测量疾病发展且将诊断缩短至数月。</p><p></p><p>海外项目二:外骨骼机器人</p><p>项目来自于US bionic公司,创始人Dr.kazarooni博士是国际外骨骼机器人顶尖专家,加州大学伯克利分院教授,已研发出世界首款军用外骨骼(美军方采购后被洛克希德马丁公司收购),工业用机器人,医疗用机器人。</p><p>目前正处于研发的医用、工业用外骨骼机器人可以模块化,整机重量只有竞品一半,成本只有竞品三分之一。在迪拜国际人工智能机器人大赛总获得金牌奖项,奖金100万美元。</p><p><strong></strong><a target=\"_blank\" href=\"http://www.lieyunwang.com/activity/118\">去官网报名</a><br></p><p><br></p>', '', '网络', '2016-05-21 13:21:13', '72', '0', '1', '0', '14', '', '0', '1', '', '6', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('165', 'nginx下如何禁止未绑定域名访问', '网站初建,对nginx配置也不怎么懂,今日查看网站的百度收录排名,突然发现有一个一模一样的网站出现,连内容和logo也一样,并且排名和收录都比自己的网站高。仔细一看第一反应就是他的域名解析到了我的服务器IP上,应该是nginx上配置问题,没有进行过滤。', '<h3 id=\"nginx-\"><span style=\"color: inherit; font-size: 18px; line-height: 1.8;\">背景</span><br></h3>\n<p>网站初建,对nginx配置也不怎么懂,今日查看网站的百度收录排名,突然发现有一个一模一样的网站出现,连内容和logo也一样,并且排名和收录都比自己的网站高。仔细一看第一反应就是他的域名解析到了我的服务器IP上,应该是nginx上配置问题,没有进行过滤。网上查了一下原因和解决方法,总结如下:</p>\n<h4 id=\"-\">方式一</h4>\n<p>禁止未绑定的域名解析到服务器,跳转到403错位</p>\n<pre><code>server{\n listen 80 default_server;\n server_name _;\n return 403;\n}\n</code></pre><p>其实这个名字没有什么特别的,它仅仅是一个许多无效的域名中的一个代表,与任何真实的名字永远不会相交。其它无效的名称,如“ - “ 和” !@# “也可同样使用。<br>default_server:nginx的虚拟主机是通过HTTP请求中的Host值来找到对应的虚拟主机配置,如果找不到呢?那 nginx就会将请求送到指定了 default_server 的 节点来处理</p>\n<p>对于未绑定的域名指向你的服务器时,匹配不到你配置的虚拟主机域名后,会默认使用这个虚拟主机,然后直接返回404。</p>\n<h4 id=\"-\">方式二</h4>\n<p>把这些流量收集起来,导入到自己的网站,以其人之道还治其人之身</p>\n<pre><code>server{\n listen 80 default_server;\n server_name _;\n rewrite ^(.*) http://www.jsout.com permanent;\n}\n</code></pre><h4 id=\"-\">总结</h4>\n<p>其实这人也挺无聊的。不过,对于我自身,也加深了解了对nginx的配置。看来对于一个前端工程师来说,多了解一下nginx等服务器的配置也是必要的,成长总是在磕碰之后。</p><p><br></p>', 'static/upload/pics/5/21/2016wD40s7O1PqoLJJx1XOSiJ9O0.jpg', '前端汇', '2016-06-06 18:24:25', '253', '1', '1', '1', '16', 'nginx', '0', '1', '', '1', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('167', '2016年房产消费创投趋势探讨---IT桔子沙龙', '时间: 05-21 13:30 - 17:00\n\n地点:上海 - 江湾体育场', '<h3>2016年房产消费创投趋势探讨---IT桔子沙龙</h3><p><i>时间:</i> <i>05-21 13:30 - 17:00</i></p><p><i>地点:</i><i>上海 - 江湾体育场</i><br></p><ul></ul><h4>活动内容</h4><p>房产消费作为互联网+中的巨额市场一直备受关注,围绕房产的O2O覆盖租房、买房、装修几大领域,再加上创业火热、小微企业增多,商业地产、办公室租房、众创空间这类服务也纷纷登场。房产的客单价足够高,围绕房贷、分期支付的消费金融也在这个领域得到了更快的发展。</p><p>刚刚过去四分之一的2016年,联合办公空间纷纷融资,住百家、美房云客等房产企业挂牌新三板,魔方公寓迎来C轮3亿美金融资,链家扑朔迷离的巨额融资后启动上市计划,然而不管是孵化器、亦或是长租、短租公寓,火热的背后同样备受质疑。</p><p>如今的房产+互联网是否正在经历寒冬?房产交易型创业公司如何与传统大平台竞争?在重度依赖线下的行业中如何打造出新的模式?投资人眼中房产行业还存在哪些创业机会?本期IT桔子沙龙,我们将邀请创业者和投资人共同探讨。</p><p>议程:</p><p>13:30-14:00签到入场</p><p>14:00-14:15活动介绍及开场</p><p>14:15-14:45 IT桔子盘点房产领域创业投资数据信息</p><p>14:45-16:45创业公司主题演讲+互动问答</p><p>16:45-17:30投资人分享:投资房产消费领域的观点和看法</p><p><a target=\"_blank\" href=\"http://huodong.tuicool.com/huodong/goto?id=572b4a46651a5ece5e2527a6\">去官网报名</a><br></p><p><br></p>', '', '网络', '2016-05-21 13:24:42', '87', '0', '1', '0', '14', 'IT桔子沙龙', '0', '1', '', '6', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('168', '亚杰商会第10期融资对对碰', '时间:05-24 13:30 - 17:00\n\n地点:北京 - 中关村创业大街昊海楼2层', '<p><i>时间</i>:<i>05-24 13:30 - 17:00</i></p><p><i>地点:</i><i>北京 - 中关村创业大街昊海楼2层</i><br></p><ul></ul><h4>活动内容</h4><p>第九期的融资对对碰,涌现了大波优质项目,目前已有两个项目通过亚杰平台成功对接到投资机构!如今亚杰商会第十期融资对对碰如期而至,我们依然采取项目路演10分钟+投资人提问5分钟的形式!</p><p>【参加项目】</p><p></p><p>旅行管家 </p><p>用线上APP线下呼叫中心连接用户与商家,解决目的地商户诚信,用户体验两个痛点,打破传统商业规则形成闭环,让中国旅游升级迭代。全程围绕用户提供最新最精准的目的地吃购游等服务,让您更好的了解当地风土人情,让客人的精力全部绽放在旅程中最精华的时刻。</p><p></p><p>易售后 </p><p>中小企业售后服务云平台,一站式解决企业售后服务难题。平台要实现的目标是:让企业的售后服务更简单、更便捷、更高效、成本更低廉。</p><p></p><p>U享生活</p><p>一款中小型商户轻运营SAAS平台,U享可帮助中小型商户引流推广,提升复购率,运营老客户。降低商户在运营维护中的人力以及运营推广成本。</p><p></p><p>Artplane</p><p>一个艺术品,设计师服务,艺术家居全产业链条经纪+电商平台。我们帮助推广艺术家+设计师+设计家居,帮助售卖艺术品和设计师服务,设计家居,帮助他们赚钱,变现盈利。</p><p>帮助艺术家和室内设计师,打造经纪推广服务平台。</p><p></p><p>联尚优品</p><p>一家聚合全球时尚产品跨境电商B2B实时交易平台,我们主要核心模式是1、服务担保(实时共享商品、配送、结算闭环交易服务) 2、颠覆低效(打破传统B2B批量订单模 式,以单品实时交易模式) 3磁力吸引(吸引更多海外优质商户,盘活闲置货源库存)</p><p></p><p>救心EXPRESS</p><p>一个急性心梗救治及随访管理平台,是根据国家行政文件开发的手机APP,实现了临床路径指引及数据采集和随访平台,帮助医院建立心梗救治的数据库,利于医院申请胸痛中心,同时也作为医疗企业进入市场和市场渗透的入口。</p><p>目前APP开发完成,推广团队基本建立。项目创始人医生出身,超过10年外资医药公司市场和销售管理经验,并在在行业学会工作过3年,目前带领团队管理全国心梗项目。</p><p></p><p>人间禅境</p><p>3d养成类手游,创意玩法,创新思维,虚拟现实+物联网,把虚拟物品带到现实中来, 虚拟与现实结合,云存储结合游戏,记忆人生轨迹。 根据人性化思维打造全新的,立体的,新颖的游戏模式。 最大限度的提升画面品质,试图做到三点创新,</p><p>即:游戏玩法创新,云存储形式创新,b2c模式创新</p><p><a target=\"_blank\" href=\"http://www.huodongxing.com/event/6335591479800\">去官网报名</a><br></p><p><br></p>', '', '前端汇', '2016-05-23 14:05:34', '92', '0', '1', '0', '14', '亚杰商会', '0', '1', '前端汇', '6', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('169', 'InnoTalk|爱回收创始人讲述创业过程中的加减法', '时间: 05-24 14:00 - 16:00\n\n地点:上海 - 杨浦区锦嘉路88号', '<h3>InnoTalk|爱回收创始人讲述创业过程中的加减法</h3><p><i>时间:</i> <i>05-24 14:00 - 16:00</i></p><p><i>地点:</i><i>上海 - 杨浦区锦嘉路88号</i><br></p><ul></ul><h4>活动内容</h4><p>8年前的一个创业灵感;5年前抓住智能手机风口业务大胆转型;从十几个人发展到上千人的团队;用户上千万,已成长为手机回收行业的领导者;爱回收创始人陈雪峰讲述创业过程中的加减法。</p><p>在数学中最容易的加减法,在现实中却是最难做出的选择。创业公司在发展过程中总会有各种机会和风险,而高层在做决策时,可能会因为贪心或胆怯而不敢做赌注,导致公司出现平行发展而纵深不足的情况。这种时候会做加减法便显得格外重要。找准方向,坚持往一个可以深耕或自己最笃定的方向做加法,而在其他精力不足或判断不准的方向做减法,将会帮助创业公司快速突围。</p><p>爱回收,从8年前的社会热点“别针换别墅”产生的创业灵感,两年多艰难摸索,5年前抓住智能手机风口,业务转型为二手领域的回收。转型后很快获得晨兴资本的A轮投资,一路发展顺利,现已发展到上千人的规模,2015年8月,爱回收网完成6000万美元的C轮融资,与京东开展战略合作,上了一个新的台阶。</p><p>爱回收在发展中是如何做加减法的?在业务和商业模式层面,是如何使用加减法法则的?</p><p>5月24日(周二)下午,来IPO club听InnoSpace创业导师、爱回收创始人陈雪峰讲述“创业过程中的加减法”。</p><p><a target=\"_blank\" href=\"http://www.huodongxing.com/event/8335464172200\">去官网报名</a><br></p><p><br></p>', '', '前端汇', '2016-05-23 14:11:11', '164', '0', '1', '0', '14', '', '1', '1', '', '6', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('170', '5月AppCan移动开发技术培训', '时间 :05-25 09:30 - 05-26 18:00\n\n地点:南京 - 阳光大厦7层B座', '<p><i>时间</i> :<i>05-25 09:30 - 05-26 18:00</i></p><p><i>地点:</i><i>南京 - 阳光大厦7层B座</i><br></p><ul></ul><h4>活动内容</h4><p>如今越来越多的第三方开发平台和各种服务,成为了开发者们进行应用开发时所必需使用的工具。这些开发平台和服务不但改善了应用开发中的困境,提供有力的技术支撑,同时还为开发者节省了大量的资金与时间成本。AppCan作为国内最大的移动应用开发平台,致力于服务广大的开发者,为开发者提供最优质、最便捷的移动开发平台。与此同时,我们还提供配套的技术培训服务,帮助开发者快速上手AppCan开发平台。2016年4月AppCan移动平台开发技术培训教学案例再换新,采用项目式教学方式,通过2天“租房App\"开发培训与交流让开发者快速了解AppCan平台,掌握使用平台开发APP流程,包括创建及同步项目、模拟器调试,真机调试及断点调试、UI框架、JS SDK、插件调用、数据获取、云端编译及打包。</p><p>培训对象:有一定WEB前端基础并想从事APP开发的所有开发者。</p><p>必备知识</p><p>1、HTML:掌握HTML常用标签及属性,包括HTML5新增标签和属性等;</p><p>2、CSS:掌握CSS常用属性、语法及值、CSS选择器类型、CSS引入方式及CSS新增属性及值等;</p><p>3、JavaScript:JS基本语法、JS函数、JS内置对象、DOM元素操作等。</p><p><img src=\"http://edu.appcan.cn/base/getCImg?img=154029n2016h3g29dz.png\"></p><p><a target=\"_blank\" href=\"http://huodong.tuicool.com/huodong/goto?id=5724060b651a5ece5e252499\">去官网报名</a><br></p><p><br></p>', '', '前端汇', '2016-05-23 14:12:44', '139', '0', '1', '0', '14', '移动开发培训', '1', '1', '', '6', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('171', '2016广州『链』大会', '时间 :05-25 13:00 - 17:30\n\n地点:广州 - 南丰朗豪酒店', '<h3>2016广州『链』大会</h3><p><i>时间</i> :<i>05-25 13:00 - 17:30</i></p><p><i>地点:</i><i>广州 - 南丰朗豪酒店</i></p><h4>活动内容</h4><p>创业是源自内心的原力,是一种创造性的力量,是思想格局的不断修炼。</p><p>广州,中国创业氛围最好的城市之一,在电子商务、企业级服务、文化娱乐体育、以及游戏等四大领域拥有极佳的创业优势。</p><p>2016广州「链」大会,我们找到了广州乃至全国的一批最有思想力的创业者,将为您展现一个真实、多元的广州创业生态。</p><p>嘉宾:</p><p><img src=\"http://cdn.huodongxing.com/file/20150607/111D4D83A7AF0899DE46637A95D04577D0/30492315965230714.jpg\" alt=\"蔡华.jpg\"></p><p><img src=\"http://cdn.huodongxing.com/file/20150607/111D4D83A7AF0899DE46637A95D04577D0/30262315965520726.jpg\" alt=\"彭斌.jpg\"></p><p><img src=\"http://cdn.huodongxing.com/file/20150607/111D4D83A7AF0899DE46637A95D04577D0/30872315965960735.jpg\" alt=\"牟斌.jpg\"></p><p><img src=\"http://cdn.huodongxing.com/file/20150607/111D4D83A7AF0899DE46637A95D04577D0/30642315966230743.jpg\" alt=\"清水.jpg\"></p><p><img src=\"http://cdn.huodongxing.com/file/20150607/111D4D83A7AF0899DE46637A95D04577D0/30752315966460748.jpg\" alt=\"刘之.jpg\"></p><p><img src=\"http://cdn.huodongxing.com/file/20150607/111D4D83A7AF0899DE46637A95D04577D0/30212315966730776.jpg\" alt=\"吕桂华.jpg\"></p><p><img src=\"http://cdn.huodongxing.com/file/20150607/111D4D83A7AF0899DE46637A95D04577D0/30292315966960783.png\" alt=\"孔令杰.png\"></p><p><img src=\"http://cdn.huodongxing.com/file/20150607/111D4D83A7AF0899DE46637A95D04577D0/30962315967180794.jpg\" alt=\"王凯歆.jpg\"></p><p><img src=\"http://cdn.huodongxing.com/file/20150607/111D4D83A7AF0899DE46637A95D04577D0/30702315967430805.jpg\" alt=\"崔欣欣.jpg\"></p><p><img src=\"http://cdn.huodongxing.com/file/20150607/111D4D83A7AF0899DE46637A95D04577D0/30632315967910829.jpg\" alt=\"栾春晖.jpg\"></p><p><img src=\"http://cdn.huodongxing.com/file/20150607/111D4D83A7AF0899DE46637A95D04577D0/30862315968020833.jpg\" alt=\"邱彬彬.jpg\"></p><p><img src=\"http://cdn.huodongxing.com/file/20150607/111D4D83A7AF0899DE46637A95D04577D0/30312315968120836.jpg\" alt=\"吴江.jpg\"></p><p><img src=\"http://cdn.huodongxing.com/file/20150607/111D4D83A7AF0899DE46637A95D04577D0/30402315968240842.png\" alt=\"王雅倩.png\"></p><p></p><p><a target=\"_blank\" href=\"http://www.huodongxing.com/event/8333497459800\">去官网报名</a><br></p><p><br></p>', '', '前端汇', '2016-05-23 14:14:38', '259', '0', '1', '0', '14', '移动互联,创业', '1', '1', '前端汇', '6', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('172', '协同发展合作共赢--北京众创空间交流会', '时间: 05-25 13:30 - 17:30\n\n地点:北京 - 丰台科技园汉威国际广场', '<h3>协同发展合作共赢--北京众创空间交流会</h3><p><i>时间:</i> <i>05-25 13:30 - 17:30</i></p><p><i>地点:</i><i>北京 - 丰台科技园汉威国际广场</i><br></p><ul></ul><h4>活动内容</h4><p>从传统孵化器到今天的众创空间,我们从老思想旧思维跳跃到创新发展合作共赢,新生代的众创空间已经无时无刻的不在诠释着这个时代的新玩法。近两年孵化器行业的异军突起以及行业的风云变幻,也让很多人着实摸不到头绪,我们在探索该如何运营孵化器、众创空间的同时更应该深入挖掘这个生态产业的核心资源及优势,通过资源整合及品牌联动实现整个行业的良性发展。创天下携手瀚海源众创空间及瀚海指南针众创空间联合举办了本次主题沙龙活动:协同发展合作共赢---北京众创空间交流会</p><p><a target=\"_blank\" href=\"http://www.huodongxing.com/event/7335189020400\">去官网报名</a><br></p><p><br></p>', '', '前端汇', '2016-05-23 14:16:52', '144', '0', '1', '0', '14', '', '1', '1', '', '6', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('173', 'E井在线商城招聘前端开发工程师(15-30k)', 'E井在线商城招聘前端开发工程师', '<p><span style=\"line-height: 1.8;\"><b>职位描述:</b></span><br></p><p>1.负责网站应用前端开发,与后台工程师(程序开发)协作,完成数据交互、动态信息展现; \n</p><p>2.使用JS或AS编写封装良好的前端交互组件,维护及优化网站前端页面性能; \n</p><p>3.使用HTML/CSS/XML熟练地进行页面维护,能利用自己的经验有效地解决浏览器兼容问题,建立与监督实施浏览器兼容测试标准,保证页面兼容性; \n</p><p>4.具备良好的前端架构分析能力与设计能力,与设计、编辑和后台技术开发; \n</p><p>5.时刻保持对当前互联网的各种流行应用的了解,在交互开发、用户体验等方面有自己的见解,能主动根据业务需要,提出合理的交互方案。 \n</p><p><b>公司网站:</b></p><p>http://www.omcce.com\n</p><p>E井在线商城(http://www.ejing365.com/)是公司旗下全资网络运营平台\n</p><p><b>联系方式:</b></p><p>夏女士,电话18502512499,微信 xiaotian318531 , QQ 382597504</p><p><br></p>', 'static/upload/pics/6/6/2016xkQOhlv-tHgA7H0rwD0drjZl.png', '前端汇', '2016-06-06 18:24:43', '143', '1', '1', '0', '15', '', '0', '1', '前端汇', '5', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('174', '兑吧诚招前端开发工程师(15-40k)', '兑吧诚招前端开发工程师职位描述:根据美工设计效果图,完成网页前端开发;根据运营要求,完成页面逻辑;解决不同的浏览器及不同版本的兼容性问题;协助游戏设计,美术设计人员,提供程序设计方案和美术素材制作规格; 有小游戏开发经验', '<p><b>职位描述:\n</b></p><p>1、根据美工设计效果图,完成网页前端开发;\n</p><p>2、根据运营要求,完成页面逻辑;\n</p><p>3、解决不同的浏览器及不同版本的兼容性问题;\n</p><p>4、协助游戏设计,美术设计人员,提供程序设计方案和美术素材制作规格; \n</p><p>5、有小游戏开发经验\n</p><p><b>技术要求:\n</b></p><p>1、熟练掌握JS脚本开发;\n</p><p>2、熟练掌握HTML5开发;\n</p><p>3、熟练掌握CSS3样式编写;\n</p><p>4、具有良好的沟通能力,良好的团队合作精神。对工作积极主动,能有条理的完成工作任务;\n</p><p>5、需要熟悉各种动画和canvas性能,兼容性\n</p><p>6、熟悉若干游戏引擎及其工具集并开发过完整游戏; \n</p><p>7、有Cocos2d、Cocos2dx、egret或Unity3D引擎经验者优先;\n</p><p><b>公司网站:</b>www.duiba.com.cn</p><p><b>联系方式:</b></p><p>夏女士,电话18502512499,微信 xiaotian318531 , QQ 382597504</p><p><br></p>', 'static/upload/pics/6/6/2016kWxm6OQ39GJ57vLHb8VmymLk.png', '前端汇', '2016-06-06 18:19:14', '280', '1', '1', '0', '15', '', '1', '1', '前端汇', '5', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('175', '返回顶部javascript完整代码(基于jquery)', '返回顶部按钮, 点击后平滑过渡到顶部; 自动判断浏览器窗口,自动隐藏', '<h4 id=\"-javascript-jquery-\"><span style=\"color: inherit; font-size: 14px; line-height: 1.8;\">背景:</span><br></h4>\n<blockquote>\n<p>文章长了页面拉到底部再拖回到顶部,会显得很麻烦,也很臃肿。加个“返回顶部”按钮就不一样了。</p>\n</blockquote>\n<h5 id=\"-\">需求:</h5>\n<blockquote>\n<p>返回顶部按钮, 点击后平滑过渡到顶部; 自动判断浏览器窗口,自动隐藏</p>\n</blockquote>\n<p>首先在html里引入jquery</p>\n<pre><code class=\"lang-javascript\"><script src=\"<%=_web.url%>static/src/js/widget/jquery/jquery-1.9.1.min.js\"></script>\n</code></pre>\n<p>在页面通用部分( footer.html )添加页面元素:</p>\n<pre><code class=\"lang-vbscript-html\"><a id=\"back-top\" class=\"w-gotop\" href=\"javascript:void(0)\"></a>\n</code></pre>\n<p>再添加并调整一下css显示样式:</p>\n<pre><code class=\"lang-css\"><style>\n #back-top{\n width:50px;\n height:42px;\n display:block;\n background:url(\"/images/common/gotop.jpg\") center center;\n position: fixed;\n right: 20px;\n bottom: 200px;\n display: none;\n }\n</style>\n</code></pre>\n<p>这样按钮就能正常显示在页面的右下角了,最后添加javascript代码:</p>\n<pre><code class=\"lang-javascript\"> $(window).scroll(function() {\n if($(this).scrollTop() != 0) {\n $(\'#back-top\').fadeIn();\n } else {\n $(\'#back-top\').fadeOut();\n }\n });\n $(\"#back-top\").on(\"click\",function(){$(\"html,body\").animate({scrollTop:\"0\"})})\n</code></pre>\n<p>如此,一个简单的返回到顶部的功能就完成了。O(∩_∩)O</p><p><br></p>', 'static/upload/pics/6/8/2016MAg0iUz_ghGIw4dyphlEn0Ny.jpg', '前端汇', '2016-09-21 15:27:57', '121', '0', '0', '0', '4', 'jquery,返回顶部', '1', '1', '', '1', '0', '1', '0', '0', '0');
INSERT INTO `li_article` VALUES ('176', 'Liblog博客系统简介', 'Liblog是一个简单易用的Markdown博客系统,它是基于开源框架thinkJS(使用 ES6/7 特性开发 Node.js 框架)开发的nodejs项目需要mysql数据库支持,具有管理后台功能,更新博客分为普通文章和markdown文章,markdown文章只需要导入你写好的Markdown文件即可。', '<p><span style=\"color: inherit; font-size: 30px; line-height: 1.8;\">一. 简介</span><br></p>\n<p>Liblog是一个简单易用的Markdown博客系统,它是基于开源框架<a href=\"http://www.thinkjs.org\">thinkJS</a>(使用 ES6/7 特性开发 Node.js 框架)开发的nodejs开源系统。liblog需要mysql数据库支持,具有管理后台功能,更新博客分为普通文章和markdown文章,markdown文章只需要导入你写好的Markdown文件即可。它摆脱了在线编辑器排版困难,无法实时预览的缺点,一切都交给Markdown来完成,一篇博客就是一个Markdown文件。同时也支持评论,代码高亮,分类,标签云,留言板、友情链接、系统设置等常用功能。Liblog提供了不同的主题样式,你可以根据自己的喜好配置,如果你想自己制作博客主题,也是非常容易的。Liblog还支持整站静态网页生成,同时有发布相关的配置,使用nginx做反向代理,动静态资源分离,静态缓存等,使您发布后的博客访问秒开。</p>\n<h2 id=\"-\">二. 功能特点</h2>\n<ol>\n<li>一键导入Markdown文章 </li>\n<li>文章评论 </li>\n<li>代码高亮 </li>\n<li>文章内容分页 </li>\n<li>支持手机端访问 </li>\n<li>自制主题 </li>\n<li>响应式 </li>\n<li>自定义URL</li>\n<li>良好的SEO </li>\n</ol>\n<h2 id=\"-liblog-\">三. Liblog优势</h2>\n<ol>\n<li>使用nodejs编写,对前端开发人员有天然的二次开发优势 </li>\n<li>一键导入Markdown文章,摆脱后台编辑排版困难,无法实时预览的缺点 </li>\n<li>可自定义URL,支持静态/伪静态访问,良好的SEO </li>\n<li>完善的后台配置,可自由开关某些功能 </li>\n<li>多主题支持,可自制主题 </li>\n<li>博客,分类,标签,归档 </li>\n<li>采用pm2守护进程管理nodejs应用,宕机自动重启</li>\n</ol>\n<h2 id=\"-\">四. 环境要求</h2>\n<p>Nodejs+nginx</p>\n<h2 id=\"-\">五. 安装步骤</h2>\n<ol>\n<li>下载Liblog源代码</li>\n<li>安装nodejs及nginx,并配置 </li>\n<li>解压上传到你的网站根目录 </li>\n<li>运行启动命令</li>\n<li>访问后台编辑系统配置,填写静态资源目录(网站)</li>\n<li>打开浏览器,访问网站首页 </li>\n<li>具体安装步骤,详见安装教程</li>\n</ol>\n<h2 id=\"-\">六. 详细说明</h2>\n<p><a href=\"http://www.jsout.com/\">1. 安装</a><br><a href=\"http://www.jsout.com/\">2. 目录结构</a><br><a href=\"http://www.jsout.com/\">3. 配置说明</a><br><a href=\"http://www.jsout.com/\">4. 编写博客</a><br><a href=\"http://www.jsout.com/\">5. 缓存机制</a><br><a href=\"http://www.jsout.com/\">6. 站点性能优化</a><br><a href=\"http://www.jsout.com/\">7. 在Nginx上运行Liblog</a> </p>\n<h2 id=\"-bug-\">七. 问题及bug反馈</h2>\n<p>如果在实际使用过程中对Liblog有新的功能需求,或者在使用Liblog的过程中发现了Bug,欢迎反馈给我。可以直接在Github上提交,也可以发邮件至<code>[email protected]</code>与我取得联系,我将及时回复。如果你自己制作了漂亮好用的主题,也非常欢迎你提交给我,我会在这里展示你的主题链接。如果你正在使用Liblog,也可以告诉我,我将也会在这里列出使用者名单。如果你想和其他Liblog使用者讨论交流,欢迎加入QQ群<code>256687601</code>。</p>\n<h2 id=\"-\">八. 使用者列表</h2>\n<p>前端汇:<a href=\"http://www.jsout.com\">http://www.jsout.com</a><br></p>\n<p>如果你的网站也是用liblog搭建,请告知作者 </p>\n<h2 id=\"-\">九. 感谢</h2>\n<p>Liblog的成长需要喜欢Markdown,喜欢写博客的各位亲们支持!感谢你们使用Liblog,感激你们对Liblog的良好建议与Bug反馈。</p>\n<p>QQ群:<code>256687601</code><br>作者邮箱:<code>[email protected]</code> </p><p><br></p>', '', '前端汇', '2016-08-30 16:51:06', '409', '0', '1', '0', '17', 'liblog,nodejs', '1', '1', '', '3', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('332', 'liblog使用及安装', 'liblog使用及安装', '<h3 id=\"-\">使用及安装</h3>\n<h4 id=\"-\">安装依赖</h4>\n<p>执行之前请确认已有 Node.js 环境,Node.js 版本要大于 4.0</p>\n<p>解压安装包,执行 npm install 安装对应的依赖。</p>\n<pre><code>npm install\n</code></pre><h4 id=\"-\">导入数据库</h4>\n<p>导入根目录下的演示数据库文件liblog.sql</p>\n<h4 id=\"-src-common-config-db-js-\">修改数据库配置(src/common/config/db.js)</h4>\n<p>修改数据库名,数据库帐号及host地址</p>\n<pre><code>export default {\n type: \'mysql\',\n log_sql: true,\n log_connect: true,\n adapter: {\n mysql: {\n host: \'127.0.0.1\',\n port: \'3306\',\n database: \'liblog\',\n user: \'root\',\n password: \'root\',\n prefix: \'li_\',\n encoding: \'utf8\'\n },\n mongo: {\n\n }\n }\n};\n</code></pre><h4 id=\"-\">编译源文件代码</h4>\n<pre><code class=\"lang-javascript\">npm run compile\n</code></pre>\n<h4 id=\"-\">启动服务</h4>\n<pre><code class=\"lang-javascript\">npm run start\n</code></pre>\n<h4 id=\"-\">前台访问地址</h4>\n<p><a href=\"http://localhost:8361,\">http://localhost:8361,</a></p>\n<h4 id=\"-\">后台地址</h4>\n<p><a href=\"http://localhost:8361/admin\">http://localhost:8361/admin</a></p>\n<p>初始化帐号:admin 123456</p>\n<h4 id=\"-\">线上部署</h4>\n<p>在服务器上推荐使用 pm2 来管理 Node.js 服务,来保证系统正常运行。<br>编辑并保存根目录下的pm2.json</p>\n<pre><code>{\n \"apps\": [{\n \"name\": \"liblog\",\n \"script\": \"npm start www/production.js\",\n \"cwd\": \"E:/jsout/liblog\",\n \"max_memory_restart\": \"1G\",\n \"autorestart\": true,\n \"node_args\": [],\n \"args\": [],\n \"env\": {\n\n }\n }]\n}\n</code></pre><blockquote>\n<p>注意:cwd为项目在服务器上的路径</p>\n</blockquote>\n<h4 id=\"-pm2-\">启动pm2管理应用</h4>\n<pre><code class=\"lang-javascript\">pm2 start pm2.json\n</code></pre>\n<h4 id=\"-\">常用命令</h4>\n<pre><code class=\"lang-javascript\">pm2 status +项目名或id\npm2 list\npm2 delete +项目名或id\npm2 delete all\n</code></pre>\n<h4 id=\"-\">服务器配置进阶</h4>\n<p>nginx服务器配置,请参考根目录下的nginx.conf,把域名和路径改成自己相应的路径。</p>\n<p>开源地址:<a href=\"https://github.com/livisky/liblog\">https://github.com/livisky/liblog</a></p><p><br></p>', 'static/upload/pics/2016/09/20OoHcTMeY3GP6fKOzWFs7a2uK.png', '前端汇', '2016-09-20 17:04:54', '22', '0', '0', '0', '2', 'liblog', '1', '1', '', '1', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('179', '扒一扒Nodejs formidable的onPart', '话说使用Nodejs实现一个文件上传,还是蛮简单的,基于Express4.x一般也就formidable用的多些吧;基本的不多说了,github一下都会的;接着《也说文件上传之兼容IE789的进度条---丢掉flash》,新版的大文件上传,最后就差断点续传了,业余跟进中...;', '<p>话说使用Nodejs实现一个文件上传,还是蛮简单的,基于Express4.x一般也就formidable用的多些吧;基本的不多说了,github一下都会的;接着《也说文件上传之兼容IE789的进度条---丢掉flash》,新版的大文件上传,最后就差断点续传了,业余跟进中...;对于IE789,在文件上传这块,算是与HTML5无缘了,当然我也选择丢掉了flash,就用最原始的input[type=\"file\"]+hideIframe+轮询;OK,IE789可以凉快去了,BSIE!</p>\n<p> 那么,现代浏览器上就不一样了;大家都知道用HTML5上传大文件必然会选择分段,files API的file.slice(start,end)+formData;简单的将就看吧:</p>\n<pre><code class=\"lang-python\">1 var uploader=function(){\n2\n3 //....\n4\n5 function Files(obj){\n6 this.files=obj.files;\n7 this.__token__=utils.getRandomStr();\n8 this.url=obj.url||location.href;\n9 this.chunkSize=obj.chunkSize||200*1024;\n10 this.chunks=Math.ceil(this.files.size/this.chunkSize);\n11 this.index=0;\n12 this.onprogress=obj.onprogress||function(p){console.log(p);};\n13 }\n14 Files.prototype={\n15 postFiles:function(){\n16 var $self=this;\n17 //大于50M 断点续传\n18 if (this.files.size>50*1024*1024) {\n19 var fileReader = new FileReader(),spark = new SparkMD5.ArrayBuffer();\n20 fileReader.onload = function (e) {\n21 spark.append(e.target.result); \n22 $self.hash=spark.end(); \n23 window.__hash__=$self.hash; \n24 var stored=localStorage.getItem(\'fileUploadInfos\');\n25 //断点信息\n26 $self.postSlice();\n27 };\n28 fileReader.readAsArrayBuffer(this.files.slice(0, 10240));\n29 }else{\n30 this.postSlice();\n31 };\n32 },\n33 postSlice:function(){\n34 var $self=this;\n35 if (this.index>=this.chunks) {\n36 return false;\n37 };\n38 this.start=this.index*this.chunkSize;\n39 this.end=Math.min(this.files.size,this.start+this.chunkSize);\n40\n41 var self=this;\n42 var fd = new FormData();\n43 fd.append(\"sliceData\", this.files.slice(this.start,this.end));\n44 this.url=//url datas\n45 var xhr = new XMLHttpRequest();\n46 xhr.upload.addEventListener(\"progress\", function(evt){\n47 if (evt.lengthComputable) {\n48 var led=self.index*self.chunkSize*1+evt.loaded*1;\n49 var p=parseFloat((led)/self.files.size*100).toFixed(2);\n50 self.onprogress&&self.onprogress(p);\n51 }else {\n52 console.log(\'unable to compute\');\n53 }\n54 }, false);\n55 xhr.addEventListener(\"load\", function(){\n56 self.index++;\n57 self.postSlice();\n58 eval(xhr.responseText);\n59 }, false);\n60 xhr.open(\"POST\", this.url);\n61 // xhr.addEventListener(\"error\", uploadFailed, false);\n62 xhr.addEventListener(\"abort\", function () {\n63 //记录断点信息\n64 }, false);\n65 xhr.send(fd);\n66 }\n67 }\n68\n69 return {\n70 Files:Files\n71 //.....\n72 }\n73 }();\n74\n75 if (this.files) {\n76 var Files=new uploader.Files({\n77 files:this.files[0],\n78 chunkSize:10*1024*1024,\n79 onprogress:function(p){\n80 callbk(p);\n81 }\n82 });\n83 Files.postFiles();\n84 }\n</code></pre>\n<p>好吧,其实大家都懂,我就不多BB了;还是说formidable吧,既然用到分段上传,formidable的一般做法肯定是行不通的;不过github上人家也说了,onPart或许可以。。。原谅我英语有点low,一知半解;原文这样的:</p>\n<p><code>You may overwrite this method if you are interested in directly accessing the multipart stream. Doing so will disable any\'field\' / \'file\' events processing which would occur otherwise, making you fully responsible for handling the processing.</code></p>\n<pre><code class=\"lang-python\">1 form.onPart = function(part) {\n2 part.addListener(\'data\', function() {\n3 // ...\n4 });\n5 }\n</code></pre>\n<p><code>If you want to use formidable to only handle certain parts for you, you can do so:</code></p>\n<pre><code class=\"lang-python\">1 form.onPart = function(part) {\n2 if (!part.filename) {\n3 // let formidable handle all non-file parts\n4 form.handlePart(part);\n5 }\n6 }\n</code></pre>\n<p>也就是我们需要使用onPart来分段接收前端发过来的数据,然后合成一个文件,生成到指定目录;</p>\n<p>当使用formData上传时,在request headers里我们会看到有项request payload,也就是我们发送过去的数据,这是未解析的原始数据;那么,难道我们还要自己解析吗?不会玩了。。</p>\n<p>扒一扒formidable的源代码,会发现有好几个_parser结尾的js文件;再看incoming_form.js里有这么一段:</p>\n<pre><code class=\"lang-python\">1 IncomingForm.prototype._parseContentType = function() {\n2 if (this.bytesExpected === 0) {\n3 this._parser = dummyParser(this);\n4 return;\n5 }\n6\n7 if (!this.headers[\'content-type\']) {\n8 this._error(new Error(\'bad content-type header, no content-type\'));\n9 return;\n10 }\n11\n12 if (this.headers[\'content-type\'].match(/octet-stream/i)) {\n13 this._initOctetStream();\n14 return;\n15 }\n16\n17 if (this.headers[\'content-type\'].match(/urlencoded/i)) {\n18 this._initUrlencoded();\n19 return;\n20 }\n21\n22 if (this.headers[\'content-type\'].match(/multipart/i)) {\n23 var m = this.headers[\'content-type\'].match(/boundary=(?:\"([^\"]+)\"|([^;]+))/i);\n24 if (m) {\n25 this._initMultipart(m[1] || m[2]);\n26 } else {\n27 this._error(new Error(\'bad content-type header, no multipart boundary\'));\n28 }\n29 return;\n30 }\n31\n32 if (this.headers[\'content-type\'].match(/json/i)) {\n33 this._initJSONencoded();\n34 return;\n35 }\n36\n37 this._error(new Error(\'bad content-type header, unknown content-type: \'+this.headers[\'content-type\']));\n38};\n</code></pre>\n<p>这几条if很是让人欣喜啊,有木有?特别是看到这句:</p>\n<pre><code class=\"lang-python\">1 this.headers[\'content-type\'].match(/boundary=(?:\"([^\"]+)\"|([^;]+))/i);\n</code></pre>\n<p>这不是在解决咱在request headers里看到的request payload吗?终于在心中大喜,咱不用自己解析那堆数据了;接着往下看:</p>\n<pre><code class=\"lang-python\">1IncomingForm.prototype.onPart = function(part) {\n2 // this method can be overwritten by the user\n3 this.handlePart(part);\n4};\n5\n6IncomingForm.prototype.handlePart = function(part) {\n7 var self = this;\n8\n9 if (part.filename === undefined) {\n10 var value = \'\'\n11 , decoder = new StringDecoder(this.encoding);\n12\n13 part.on(\'data\', function(buffer) {\n14 self._fieldsSize += buffer.length;\n15 if (self._fieldsSize > self.maxFieldsSize) {\n16 self._error(new Error(\'maxFieldsSize exceeded, received \'+self._fieldsSize+\' bytes of field data\'));\n17 return;\n18 }\n19 value += decoder.write(buffer);\n20 });\n21\n22 part.on(\'end\', function() {\n23 self.emit(\'field\', part.name, value);\n24 });\n25 return;\n26 }\n27\n28 this._flushing++;\n29\n30 var file = new File({\n31 path: this._uploadPath(part.filename),\n32 name: part.filename,\n33 type: part.mime,\n34 hash: self.hash\n35 });\n36\n37 this.emit(\'fileBegin\', part.name, file);\n38\n39 file.open();\n40 this.openedFiles.push(file);\n41\n42 part.on(\'data\', function(buffer) {\n43 if (buffer.length == 0) {\n44 return;\n45 }\n46 self.pause();\n47 file.write(buffer, function() {\n48 self.resume();\n49 });\n50 });\n51\n52 part.on(\'end\', function() {\n53 file.end(function() {\n54 self._flushing--;\n55 self.emit(\'file\', part.name, file);\n56 self._maybeEnd();\n57 });\n58 });\n59 };\n</code></pre>\n<p>至此,终于明白作者的话了;自己处理上传的数据,是在handlePart中通过part.on(\'data\')和part.on(\'end\')来收集分段数据,然后生成文件的;那么使用分段上传的话,我们就需要在Nodejs里重写form.handlePart了;</p>\n<pre><code class=\"lang-python\">1form.handlePart=function(part) {\n2 var dd=[],ll=0;\n3 part.on(\'data\', function(data) {\n4 if (data.length == 0) {\n5 return;\n6 }\n7 dd.push(data);\n8 ll+=data.length;\n9 });\n10\n11 part.on(\'end\', function() { \n12 var p=\'./public/imgs/\'+uploadToken+\'_\'+req.query.name;\n13 fs.open(p, \'a\', function (err, fd) {\n14 if (err) {\n15 throw err;\n16 }\n17 fs.write(fd, Buffer.concat(dd,ll),0, ll,0,function(){\n18 if (req.query.chunks==req.query.index*1+1) {\n19 res.write(bk);\n20 }\n21 fs.close(fd,function(){});\n22 res.end();\n23 });\n24 }); \n25 } \n26 });\n27}\n</code></pre>\n<p>拿到data后生成文件并不难,fs.writeFile、stream都可以的;原谅我初入Nodejs,怎么感觉最后一步的写入文件,这两种方式都特慢呢?不能忍啊,再探!</p>\n<p>试来试去,最后还是选择在接收到第一段数据时就生成文件,之后接收到的数据直接push进去;即上面的fs.write(fd,buffer,offset,length,position,cb);话说明显快了不少呢!而且,意外的收获是:想一想接下来还要实现断点续传呢!想一想,貌似这样做,基本等于Nodejs端的断点续传已经实现了呢;前端记录断点的位置,下次上传时从断点位置开始,然后直接push到这个没上传完的文件里;</p>\n<p>到这里,Nodejs端的分段接收文件就可以的了,而且还为之后的断点续传做了个很好的铺垫呢;</p>\n<p>好了,对于大文件上传,formidable能做的差不多就这么多了,onPart是必须的;如果大家伙有什么更好的方法,欢迎与我分享!简单的记录,与君共勉,谢谢你能看到这儿!</p><p><br></p>', '', '前端汇', '2016-06-12 16:08:17', '90', '0', '1', '0', '3', 'onPart', '1', '1', '', '3', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('180', '也说文件上传之兼容IE789的进度条---丢掉flash', '', '<p> 最近在整理前端常用到的一些组件,像分页(get/post两类)、下拉框(下拉单选/下拉多选/省市区联动)、模态框等;当然还有文件上传啦,如果只考虑用HTML5的话,前端方面的工作,那家伙老简单了,分分钟搞定,不就是验证,显示文件信息,加个进度条,回调下么;好吧,说得简单而已,自己动手完整的实现一遍试试;对于PC端的前端而言,大多时候可能还得顾及一下IE7+;那么问题就来了,IE789都不支持H5的file API,so,让我们一起BSIE吧!</p>\n<p> 上传显示文件类型、大小、文件名,这些百度立马出结果,不多说了;</p>\n<p> 至于在IE789上显示进度条,这个就得自己琢磨了,如果你的工作用过上传图片或上传大文件啥的,一般在IE低版本浏览器里,会切换到用flash解决;包括牛逼哄哄的百度WebUploader;话说,既然我想自己实现一遍,我肯定不会为了老旧IE的进度条而去学flash;因为在我心里,H5早已秒了flash;那么,自己挖坑就只能自己填咯!</p>\n<p> 先简单看看Nodejs对文件上传的处理吧!在Express4里req.files已经是undefined了;现在用的最多的可能就是formidable了;github到formidable,你知道了它有个progress事件,于是心中大喜,低版本IE的进度条有戏了;OK,试一下:</p>\n<pre><code class=\"lang-python\">1 form\n2 .on(\'error\',function(err){\n3 console.log(err);\n4 })\n5 .on(\'aborted\',function(){\n6 console.log(\'aborted\');\n7 })\n8 .on(\'progress\',function(bytesReceived, bytesExpected){\n9 var n=parseInt(parseFloat(bytesReceived/bytesExpected).toFixed(2)*100);\n10 console.log(n);\n11 });\n</code></pre>\n<p> 是的,你很高兴的看到了,控制台按照预期打印了一串进度值;那么,再进一步;</p>\n<pre><code class=\"lang-python\">1 form\n2 .on(\'progress\',function(bytesReceived, bytesExpected){\n3 var n=parseInt(parseFloat(bytesReceived/bytesExpected).toFixed(2)*100);\n4 res.write(\'<script>window.parent.call(\'+n+\')</script>\');\n5 //无刷新上传,你们懂的\n6 console.log(n);\n7 });\n</code></pre>\n<p> call方法即在页面上显示进度值;很不幸,你只能看到最后的100%,看不到上传具体详细的进度值;再探...</p>\n<p> 接下来换个思路,试一下,将进度值保存到session里,额外加一个请求来轮询这个进度值,哎哟,不错哦!为了保证你请求的进度值是你这次上传的进度值而不是其他上传的进度值,需要在上传的请求里和额外的请求里约定一个token值;现在又来一个问题就是怎么在请求的时候得到这个token,由于文件上传的请求体在Request Payload里,所以req.body拿不到带过去的值,我也不想去解析这堆了,当然我也解析不了;放在url里最好,问题在于有时候得刷新两次来刷新token,不好!不得已,我还是放在cookie里吧!</p>\n<pre><code class=\"lang-python\">1 var cookies=function () {\n2 var cks=req.headers.cookie.split(\';\'),obj={};\n3 for(var i=0;i<cks.length;i++){\n4 obj[cks[i].split(\'=\')[0].replace(/\\s+/ig,\'\')]=unescape(cks[i].split(\'=\')[1]);\n5 }\n6 return obj;\n7 }();\n8 var queryToken=cookies.__token__;\n9\n10 form\n11 .on(\'progress\',function(bytesReceived, bytesExpected){\n12 var n=parseInt(parseFloat(form.bytesReceived/form.bytesExpected).toFixed(2)*100); \n13 if (req.session[\'file\'+queryToken]) {\n14 req.session[\'file\'+queryToken].percent=n;\n15 }else{\n16 req.session[\'file\'+queryToken]={\n17 token:queryToken,\n18 percent:n\n19 }\n20 };\n21 console.log(n);\n22 });\n</code></pre>\n<p> 为了IE789,我来轮询进度值了,原谅我,其实我的心很痛;</p>\n<pre><code class=\"lang-python\">1 var getData=function(){\n2 $.post(\'/uploader\',{\n3 getfileinfo:1,\n4 uploadtoken:utils.cookie.getCookie(\'__token__\')\n5 }).then(function(data){\n6 console.log(data);\n7 if (data.mes<0) {\n8 getData();\n9 }else{\n10 var pros=data.info;\n11 call(pros.percent);\n12 if (pros.percent!=\'100\') {\n13 getData();\n14 };\n15 };\n16 });\n17 }\n18 getData();\n</code></pre>\n<p> call方法即在页面上显示进度值;很不幸,你只能看到最后的100%,看不到上传具体详细的进度值;再探...</p>\n<p> 好吧,我又一次沦陷了;不过还是感觉不对劲,ajax轮询没有问题,问题在于session里要等到上传完毕才有值,所以只能看到100%,看不到详细进度值;我是否可以认为,在progress里,之前的res.write和这次的req.session被挂起了呢,但是它又保存了每次的执行结果,直到progress完再释放,所以只能看到100%;没心情看formidable的源码,当然我也看不咋懂,我就先这么认为吧!</p>\n<p> 既然ajax轮询没问题,那么就是保存到session不得劲了;实在不成,放到global里试试吧,总不会往全局对象里塞个值也会挂起吧;稍作改动放到global里:</p>\n<pre><code class=\"lang-python\">1 form\n2 .on(\'progress\',function(bytesReceived, bytesExpected){\n3 var n=parseInt(parseFloat(form.bytesReceived/form.bytesExpected).toFixed(2)*100); \n4 if (global[\'file\'+queryToken]) {\n5 global[\'file\'+queryToken].percent=n;\n6 }else{\n7 global[\'file\'+queryToken]={\n8 token:queryToken,\n9 percent:n\n10 }\n11 };\n12 console.log(n);\n13 });\n</code></pre>\n<p> 继续轮询。</p>\n<p> 漂亮,完全就是那么回事!在chrome里看到的和HTML5的进度一个效果,只是在IE789里会有点卡顿的感觉,不过还是能看到详细的进度值的;毕竟老浏览器身子骨不咋地,你们懂的;还有,每次上传都往global里塞值,怎么也得适当的清理一下吧,文件上传完毕,转移到指定目录后global[\'file\'+queryToken]=null;</p>\n<p> 然而,轮询,就是一个接一个好多好多的请求,这里也许会出问题;要不限制一下吧,间隔500ms请求一次进度值;恩,IE789进度条就这么解决了,说好的丢掉flash;虽然这个轮询可以兼容所有浏览器,但毕竟要浪费那么多请求,还是判断下,在IE789以外继续HTML5吧!</p>\n<p> 其实衡量一下,额外加个flash上传和额外的请求,哪个更值呢,原谅我不懂flash,就不多说了,反正我很不喜欢在页面上加一下额外的文件;</p>\n<p> 关于文件上传的组件,还有很多的细节处理,本想弄一个JS文件的,后来一想,为了可复用性更强,还是作为一个独立的页面搞比较好,需要上传的地方,iframe一下就行了,肯定比弄一个JS文件要好很多;更多细节内容,持续整理中,不敢和网上成型的上传比,仅当自己前端路上的积累和实践,我感觉可以用的话,就和大家分享了,与君共勉!</p><p><br></p>', '', '前端汇', '2016-06-13 18:00:10', '125', '0', '1', '0', '3', '进度条,兼容', '1', '1', '', '3', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('181', '聚变UCloud直播系统的云构建', '时间: 06-25 13:00 - 17:00\n地点:杭州 - 红楼大酒店', '<p> <b> 时间: 06-25 13:00 - 17:00</b></p><p><b> 地点:杭州 - 红楼大酒店</b></p><p><b> 标签: 云构建</b><span style=\"line-height: 1.8;\"> </span></p><p> 因网络环境的加速发展,人们对于娱乐的需求变的更为多元和细分,各种短视频应用、视频直播类产品火爆泛娱乐市场。尤其通过移动设备,方便了内容生产和分享,用户量和使用频率都呈现爆发式增长。</p><p> 直播技术面临多方面挑战,对于高性能的技术架构需求不可或缺。视频作为广泛的媒体格式,对于不同的网络环境、不同层级的数据规模下,如何实现存储系统的弹性搭建?如何优化处理海量的数据存储和请求?</p><p> 直播系统的云构建技术沙龙由 SegmentFault 携手 UCloud,邀请直播行业内各方技术大拿,为您带来最具实战的直播云构建解决方案。</p><p><br></p><p style=\"text-align: center; \"><a href=\"https://segmentfault.com/e/1160000005677298?utm_source=tuicool&utm_medium=referral\" target=\"_blank\"><b><font size=\"4\">立即报名</font></b></a></p><p style=\"text-align: center;\"><br></p>', '', '前端汇', '2016-06-13 18:05:56', '172', '0', '1', '0', '14', '云构建', '0', '1', '', '6', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('182', '2016年中国(杭州)移动互联网投融资大会—紫金之巅项目路演', '时间: 06-25 18:30 - 21:00\n\n地点:杭州 - 西湖区紫金创意大厦2F', '<p> <i>时间</i><span style=\"line-height: 1.8;\">:</span><span style=\"line-height: 1.8;\"> </span><i>06-25 18:30 - 21:00</i></p><p> <i>地点</i><span style=\"line-height: 1.8;\">:</span><i>杭州 - 西湖区紫金创意大厦2F</i></p><p> <i>标签</i><span style=\"line-height: 1.8;\">:</span><i>移动互联</i></p><p> 由科技部现代服务产业联盟、中国移动互联网投融资联盟联合主办,杭州市移动互联网技术学会、CCF YOCSEF杭州、上海移动互联网产业促进中心联合承办的“2016年中国(杭州)移动互联网投融资大会”将于2016年6月25日在浙江省杭州市召开。</p><p> 大会旨在聚集移动互联网产业资本和金融服务资源,搭建高效的移动互联网产业投融资平台,优化国内特别是华东地区移动互联网产业发展的生态环境,更好地促进全国移动互联网产业的健康持续发展。</p><p> 大会将以“移动互联投融中国,万众创业助力杭州”为主题,并将吸引超过500位移动互联网相关领域的天使投资人、VC、PE和银行、证券、保险、支付、P2P、众筹等投融资界代表,企业家、创业者、协会、政府、媒体等代表。</p><p> 本次大会白天议程为投融资嘉宾主题演讲和圆桌论坛,晚上将设置项目路演环节,大会组委会将从报名的创业项目中择优筛选10个项目参加路演,和30位投资人面对面交流。</p><p> 本次路演为私密路演,本链接只针对创业项目路演和投资人报名。创业团队项目BP可直接在猎云网提交,也可发送至邮箱:[email protected]</p><p style=\"text-align: center; \"><a href=\"http://www.lieyunwang.com/activity/136?utm_source=tuicool&utm_medium=referral\" target=\"_blank\"><font size=\"4\">立即报名</font></a></p><p><br></p>', '', '前端汇', '2016-06-13 18:04:55', '81', '0', '1', '0', '14', '互联,移动', '0', '1', '', '6', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('183', '第二届互联网+高峰论坛 - CIO中国行', '时间: 06-18 08:30 - 16:30\n\n地点:苏州 - 西交利物浦国际会议中心', '<p> <i>时间</i><span style=\"line-height: 1.8;\">:</span><span style=\"line-height: 1.8;\"> </span><i>06-18 08:30 - 16:30</i></p><p> <i>地点</i><span style=\"line-height: 1.8;\">:</span><i>苏州 - 西交利物浦国际会议中心</i></p><p> 新一轮科技革命和产业变革呼唤加快推进信息化与工业化深度融合。以制造业数字化、网络化、智能化为标志的智能制造,是两化深度融合切入点和主攻方向,这已经成为业界的普遍共识和企业的主动行动。 工业和信息化部提出发展智能制造的主要任务是,深入推进两化融合企业管理体系标,全面提升制造业产品、装备、生产、管理和服务的智能化水平,实现两个IT(工业技术和信息技术)融合和倍增发展,促进产业结构向中高端迈进。 2016年6月18日,以“两化深度融合与智能制造”为主题的“第二届互联网+高峰论坛暨2016CIO中国行苏州站”活动将在苏州拉开帷幕。</p><p style=\"text-align: center; \"><span style=\"line-height: 1.8;\"><font size=\"4\"><a href=\"http://www.huodongxing.com/event/7336733351100?utm_source=tuicool&utm_medium=referral\" target=\"_blank\">立即报名</a></font></span></p><p><br></p>', '', '前端汇', '2016-06-13 18:04:28', '82', '0', '1', '0', '14', '', '1', '1', '', '6', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('184', '你看起来很厉害,所以一事无成', '即使你拥有倾国倾城的容貌,经天纬地的才华,富可敌国的金钱.....即使你拥有这个世界上人人羡慕的一切,也不能证明你的强大。\n因为心的强大,才是真正的强大。真正的强大永远是沉默的。', '<p>沙大神是我朋友圈里的著名“牛人”,众所周知他“上知天文,下知地理,音乐体育心理无所不通。”</p><p>然而他的日子却凄惨无比,不仅各类聚会随不出份子钱,打个游戏的各类付费道具都需要我赠送。</p><p>我理解他,人的一生不需要富足,只需不断探索并快乐着。但我又很难理解他,知识转化为财富真的有那么难吗?</p><p>直到最近我发现了原因。</p><p>沙大神的英雄联盟是被我带进坑的,不到几月就成了其中高手。但奇怪地是,他的段位停留在中上水平就再也上不去了。</p><p>我问他:要不要努力一下,争取成为职业玩家!</p><p>他说:“那些什么王者级别的,都是些不会享受生活的蠢货,一天只知道撸啊撸,我不屑成为他们。”</p><p>我接着问他:那如何看待那些段位比他低的玩家,他们不就在享受生活吗?</p><p>他回答我:“一群小白,不仅操作上手残,战术上更是垃圾。”</p><p>沙大神说这话的时候得意洋洋,仿佛他只需要用一点点努力就可以达到常人无法达到的水平。他不仅看不起那些和他付出了同样努力却得不到相同效果的人,他更看不起那些努力是他几倍,但成果只比他优秀一点的人。</p><p>我突然想起了,我初中时的同桌女生,她总是每晚复习到深夜,但期末考却只比我高几十分。我得意洋洋的向周围人炫耀,要是我和她一样努力,早就清华北大了吧!</p><p>我的这份炫耀毫无意义,最终上清华的是她不是我,在努力这件事上,成果大于速度。或者说,这个世界不关心谁跑的有多快,它只看重谁先跑到终点。</p><p>考70分的人是永远没有考90分的人厉害的,无关谁付出了多大努力。</p><p>付出30%努力做到50%的人只能收获内心虚幻的成就感,那些付出了200%只收获了80%的人却能得到这个世界的奖励。</p><p>沙大神之所以一事无成,就在于他既不能像职业玩家一样靠游戏为生,也不能像休闲玩家一样娱乐舒心。他的生活充满了掌声,实际上一事无成。</p><p>小时候,妈妈总告诉我们:如果你再努力一点就会很了不起哦!这个信念一直支持着我们走到今天,殊不知这份自信害了我们,成了我们偷懒的最好借口。</p><p>因为,“努力一点就行”是不断努力下去的最大敌人。</p><p>2</p><p>曾经的我也以自己是一名斜杠青年为荣,除了主业是一名心理学教师外,我热爱画画,游戏打得不错,时不时还能写点现实小说。如果要把我名字前面加上前缀的话,几页纸都写不完。</p><p>很长时间以来我很满足这种状态,我认为自己兴趣广泛,热爱生活。</p><p>但渐渐地我开始发现不对,机灵如我却在面对人生上的全线溃败:游戏上我被高手虐的一塌糊涂,写作平台上我的小说阅读量寥寥无几,我投稿的画无数次被退稿,我申报的课题被学术大牛轻松挤下。</p><p>我跟导师汇报了我的情况,导师笑着说。</p><p>“你只是看起来很厉害!你聪明地以为,你能通晓各大领域。实际上,你在每件事上都做了逃兵。”</p><p>看起来很厉害,只是我的自我感觉罢了。真正的厉害是有一个分水岭的,这个分水岭就是搜索引擎,如果你能提供的东西都能在网上搜到,那你一定并不厉害。</p><p>举例,很多人在游戏上多赢了几次就觉得自己厉害,这真的是一种错觉。你必须玩到能和百度上发布视频的那些人一个技术,那才叫厉害。我可以不厉害,但我绝不炫耀。</p><p>其次,人的一生虽然有无数的分支,但却是有主线任务的。我所有擅长的东西是必须沿着我的一个专长展开的。我的主线任务从来没变过啊,一直都是在科普心理学的道路上战斗。于是我整合了我所有的技能,让他们为这个主线服务。</p><p>我用自己的画画技术,为自己创造了一个剑圣喵大师的头像。</p><p>我不再单纯地写历史小说,而是用通俗的文笔来科普心理学知识。</p><p>每天工作完毕后,我会打开游戏玩几局,不在乎输赢,纯粹消遣。</p><p>以前我很自豪,在写作平台上我写了十万字,就已经接近别人四十万字的点赞数。有一天我彻底抛弃了这种自我麻痹,我开始佩服那些写作字数多的人,我从每周一更改为三天一更,不知不觉中,我已经成为写作平台上排行前列的人。</p><p>我非常喜欢现在的自己,现在的我已经没有过去那么狂妄了,我似乎更能知道自己到底想要的是什么?我没有了过去爆棚的自豪感,反而无时无刻我都在感受自己的肤浅。</p><p>每晚睡觉前我不再得意自己有过什么成就,我开始回忆自己走过的路,回忆起这条荆棘的路上我的每个脚印有多深,有多痛苦。</p><p>3</p><p>“看起来很厉害”到“真正厉害”差着10086本书。</p><p>“真正很厉害”到“生活达人”差着十万八千次尝试。</p><p>花儿们总以为有了刺就可以显出自己的厉害,殊不知这才显示出它的弱小。</p><p>这个信息爆炸的时代,我不担心你学不到东西,我只担心你学到一点东西就沾沾自喜,妄自尊大。这种智力上升的厉害感,其实是一种逃避现实的快感。有时候思想的冗余比思想的贫乏更加可怕。</p><p>即便你满腹经纶,如果你自命清高从不写作,把知识分享给别人也只能是默默无闻。即便你武功盖世,如果不锄强扶弱、匡扶正义,也难成一代大侠。</p><p>真正厉害的人,是不会用“厉害感”装饰自己的。生活达人们不管看起来是否厉害,他们都不会在真刀真枪的实战面前抱头鼠窜。</p><p>即使你拥有倾国倾城的容貌,经天纬地的才华,富可敌国的金钱.....即使你拥有这个世界上人人羡慕的一切,也不能证明你的强大。</p><p></p><p><b>因为心的强大,才是真正的强大。真正的强大永远是沉默的。</b></p><p><br></p>', 'static/upload/pics/6/13/20161LThgI0eK9_PYoJ-Y2mhqoFZ.png', '前端汇', '2016-06-13 18:18:38', '154', '1', '0', '1', '17', '', '1', '1', '思维风暴', '2', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('185', '万物互联,预见未来--投资界沙龙物联网专场', '时间: 06-24 14:00 - 17:30\n地点:上海 - 嘉定区平城路1455号新微大厦', '<p>时间<span style=\"line-height: 1.8;\">:</span><span style=\"line-height: 1.8;\"> </span>06-24 14:00 - 17:30</p><p>地点<span style=\"line-height: 1.8;\">:</span>上海 - 嘉定区平城路1455号新微大厦</p><p> 物联网被称为继计算机、互联网之后,信息世界的“第三次浪潮”,其价值在于让冷冰冰的物体也拥有了“智慧”和“活力”,从而实现人与物、物与物之间的交互沟通。</p><p> 目前,物联网已被国务院列为我国重点规划的战略性新兴产业之一。互联网的全面爆发和技术领域的不断革新,更加速我们迈入“万物互联”的时代。苹果、谷歌、BAT等巨头似乎早已嗅到商机,纷纷入场布局,而芯片厂商、硬件终端厂商、网络运营商,软件开发平台等也齐齐发力,忙于跑马圈地。作为一个上天入地,产业渗透率极高的新兴产业,物联网未来的市场潜力不言而喻。但与此同时,我们也看到,物联网整体存在高度碎片化的困境,技术标准难以建立,导致设备间的交流障碍。另一方面,隐私数据所诱发的信息安全问题也十分受人瞩目。可见,物联网商业模式和产业路径的打造任重而道远。</p><p> 物联网是否会成为继互联网之后的下一个风口?物联网之路上又有哪些鲜花和荆棘?投资人对于物联网又有何独到见解?</p><p></p><p> 2016年6月24日@上海,清科集团携手嘉定政府、上海物联网中心联合举办投资界沙龙——万物互联,预见未来,邀您一同探讨物联网领域的投融资机遇!</p><p style=\"text-align: center; \"><b><font size=\"4\"><a href=\"http://www.huodongxing.com/event/4337786958100?utm_source=tuicool&utm_medium=referral\" target=\"_blank\">立即报名</a></font></b></p><p style=\"text-align: center;\"><br></p>', '', '前端汇', '2016-06-14 20:04:29', '156', '0', '1', '0', '14', '物联网', '1', '1', '', '6', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('186', '中国数据资产管理峰会', '时间: 07-08 09:00 - 17:00\n地点:上海 - 小南国花园酒店', '<p> 时间: 07-08 09:00 - 17:00\n</p><p> 地点:上海 - 小南国花园酒店</p><p> 大数据与数据资产管理领域标杆峰会!</p><p> 第二届中国数据资产管理峰会(DAMS 2016, Data Asset Management Summit)将于2016年7月8日在上海举办!本次峰会由上海市经济和信息化委员会、杨浦区人民政府指导,上海市云计算产业促进中心主办,DBA+社群、首席技术官联盟、首席数据官联盟协办,DAMS组委会、新炬网络联合承办,数十家媒体单位共同支持。</p><p></p><p> 在数据已成为企业最宝贵资产之一的大数据时代,DAMS峰会意在为业界人士搭建一个思想碰撞与交流的平台,最具前瞻性和权威性的议题,碰撞出引领中国大数据和数据资产管理领域发展壮大的火花,助力中国企业在大数据时代抢占先机!今后,DAMS将围绕“大数据与数据资产管理”这一行业热话,持续为业界同仁及行业发展提供强有力的专业力量。</p><p style=\"text-align: center; \"><font size=\"4\" color=\"#0000ff\"><a href=\"http://www.dams.org.cn/?utm_source=tuicool&utm_medium=referral\" target=\"_blank\">立即报名</a></font></p><p><br></p>', '', '前端汇', '2016-06-14 20:04:17', '149', '0', '0', '0', '14', '数据', '1', '1', '', '6', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('187', 'CSS 文件引入方式', '', '<p>(1)链接式 : 在网页的<head></head>标签对中使用<link>标记来引入外部样式表文件,使用html规则引入外部css (用得比较多) :</p><p> <link href=\"./mystyle.css\" rel=\"stylesheet\" type=\"text/css\"/></p><p>(2)导入式 : 将一个独立的.css文件引入HTML文件中,导入式使用CSS规则引入外部CSS文件,<style>标记也是写在<head>标记中,使用的语法如下:</p><p> <style type=\"text/css\"></p><p> @import\"mystyle.css\"; 此处要注意.css文件的路径</p><p> </style></p><p>比较:link引用和import引用区别是:link是html加载前就引用,而import是html加载后才引用。举例,采用impor引用,会先显示无样式的页面,然后再把样式放进去。如果用JavaScript动态引用CSS,得使用link引用方式才可实现。</p><p>(3)内联css文件,直接在header 里面写css,如:</p><p><style type=\"text/css\">div{margin: 0;padding: 0;border:1px red solid;}</style></p><p>这种方法的使用情况要少的多,最长见得就是访问量大的门户网站。或者访问量较大的企业网站的首页。与第一种方法比起来,优点突出,弊端也明显。优点:速度 快,所有的CSS控制都是针对本页面标签的,没有多余的CSS命令;再者不用外链CSS文件。直接在HTML文档中读取样式。缺点就是改版麻烦些,单个页 面显得臃肿,CSS不能被其他HTML引用造成代码量相对较多,维护也麻烦些。但是采用这种方法的公司大多有钱,对他们来说用户量是关键,他们不缺人进 行复杂的维护工作。</p><p>(4) 直接在标签里面写样式</p><p></p><p><div style=\"border:1px red solid;\">测试信息</div></p><p><br></p>', '', '前端汇', '2016-06-14 20:05:22', '78', '0', '1', '0', '5', 'css,引入', '1', '1', '', '1', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('188', '2016中国(北京)国际互联网+时代博览会', '时间: 08-04 09:00 - 08-07 16:00\n地点:北京 - 国际展览中心', '<p> 时间: 08-04 09:00 - 08-07 16:00\n</p><p> 地点:北京 - 国际展览中心</p><p> 随着“互联网+”行动、互联网创新及新工业革命的蓬勃发展,作为中国互联网创新与应用领域最具规模、推广效果和行业影响的国家级展会平台,2016 中国(北京)国际互联网+时代博览会(简称NETech 互博会)将于2016年8月4-7日在中国国际展览中心(三元桥老国展)隆重举办。NETech互博会将集中展示互联网创新、互联网+跨界融合、移动互联网、互联网应用、云计算与大数据、智能硬件等领域最新技术、产品、模式及应用方案,采取B2B专业交流与B2C大众互动推广结合的模式,同期举办第八届互联网创新大会和体验嘉年华活动,将吸引总计15万人次观众参与,搭建2016年度互联网行业经贸合作、交流对接、品牌推广及互动营销的顶级平台。</p><p> NETech 2016互博会的总体规模达到了50000平米,是亚洲地区互联网创新与应用领域规模最大的展会。届时,包括阿里巴巴、腾讯、百度、京东、360、苏宁、国美、小米、乐视等各领域内近1000家参展企业将齐聚一堂,与3万名专业观众和12万名大众观众面对面交流,并吸引150家行业及大众媒体的全程关注,上演中国互联网领域年度饕餮盛宴。</p><p> 作为中国北方最具规模和影响力的智能硬件展览会,本届展会采取政府引导,市场化运作的模式,旨在通过展示可穿戴设备、智能家居、智能制造、机器人、智能医疗、无人机等技术与应用。</p><p><b>智能穿戴:</b></p><p>健康手环、手表、智能腕带、智能项链、可穿戴温度计、智能鞋、衣服、儿童防丢等产品,体感穿戴设备</p><p><b>智能家居:</b></p><p>智能家电、智能安防系统、智能照明、智能建筑、智慧城市等</p><p><b>跨界智能硬件:</b></p><p>智能家电、智能插座、智能汽车、智能门控、智能水杯、无线控制飞行器、无限远距离可控技术产品等</p><p><b>智能硬件平台及方案:</b></p><p>PCB设计方案、APP应用软件、智能穿戴产品设计方案、运输局储存平台、云端数据处理系统、大数据储存及分析系统</p><p><b>智能制造及机器人:</b></p><p>智能制造技术、智能制造系统、机器人、</p><p><b>智能医疗设备:</b></p><p></p><p>血压监测、睡眠质量监测、环境监测、血脂检测仪等可穿戴式、便携式医疗设备</p><p style=\"text-align: center; \"><font size=\"4\" color=\"#0000ff\"><a href=\"http://www.netech-expo.com/?utm_source=tuicool&utm_medium=referral\" target=\"_blank\"><b>立即报名</b></a></font></p><p><br></p>', '', '前端汇', '2016-06-14 20:04:54', '212', '0', '0', '0', '14', '', '0', '1', '', '6', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('189', '2016中国互联网安全大会', '时间: 08-16 09:00 - 08-17 17:00\n地点:北京 - 国家会议中心', '<p> </p><p> 时间: 08-16 09:00 - 08-17 17:00\n</p><p> 地点:北京 - 国家会议中心</p><p> 中国互联网安全大会始办于2013年,至今已是第四届。首届中国互联网大会引发了大众对“互联网安全”前所未有的关注热情;2014 年创造了超过两万人次的参会人数记录,美国首任国土安全部部长Tom Ridge,计算机病毒之父Fred Cohen等国内外业界顶级专家学者悉数到场。大会召开的第二天,更引发了国内安全概念股的全线飘红。2015 ISC规模空前,总参会人数超过三万人,大会主题“数据驱动安全”已成为业界公认的安全理念和发展趋势。</p><p><img src=\"/static/upload/pics/6/14/2016yqoEOQ9G4FAC8OQdZau0ZPM7.jpg\" alt=\"30862310502406494\" style=\"max-width:100%;\" class=\"\"><br></p><p style=\"text-align: center; \"><font size=\"5\" color=\"#000080\"><a href=\"http://isc.360.cn/2016/index.html?utm_source=tuicool&utm_medium=referral\" target=\"_blank\">立即报名</a></font></p><p><br></p>', '', '前端汇', '2016-06-14 20:04:39', '169', '0', '1', '0', '14', '', '1', '1', '', '6', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('190', 'CSS居中问题', '', '<p></p><h2><font size=\"5\">内联元素居中方案</font></h2><p></p><p><b>水平居中设置</b></p><p> 行内元素:设置 text-align:center;</p> <span style=\"line-height: 1.8;\">Flex布局:设置display:flex;justify-content:center;(灵活运用)</span><br><br><p><b>垂直居中设置</b></p><p> 1、父元素高度确定的单行文本(内联元素)</p><p>设置 height = line-height;</p><p> 2、<span style=\"line-height: 1.8;\">父元素高度确定的多行文本(内联元素)</span></p><p><span style=\"line-height: 1.8;\">a:插入 table (插入方法和水平居中一样),然后设置 vertical-align:middle; </span><br><span style=\"line-height: 1.8;\">b:先设置 display:table-cell 再设置 vertical-align:middle;</span></p><p><span style=\"line-height: 25.2px;\"><br></span><span style=\"color: inherit; font-size: x-large;\">块级元素居中方案</span><br></p><h2></h2><p><b>水平居中设置</b></p><p> 1、定宽块状元素</p><p>设置左右 margin 值为 auto;</p><p> 2、不定宽块状元素</p><p>a:在元素外加入 table 标签(完整的,包括 table、tbody、tr、td),该元素写在 td 内,然后设置 margin 的值为 auto;</p><span style=\"line-height: 1.8;\">b:给该元素设置 displa:inine 方法;</span><p><span style=\"line-height: 1.8;\">c:父元素设置 position:relative 和 left:50%,子元素设置 position:relative 和 left:50%;</span><br></p><p><b>垂直居中设置</b></p><ul><li>1.使用position:absolute(fixed),设置left、top、margin-left、margin-top的属性;</li></ul><pre><code>.box{\n position:absolute;/*或fixed*/\n top:50%;\n left:50%;\n margin-top:-100px;\n margin-left:-200px;\n}</code></pre>\n<ul><li>2.利用position:fixed(absolute)属性,margin:auto这个必须不要忘记了;</li></ul>\n<pre><code>.box{\n position: absolute;或fixed\n top:0;\n right:0;\n bottom:0;\n left:0;\n margin: auto;\n}</code></pre>\n<ul><li>3.利用display:table-cell属性使内容垂直居中;</li></ul>\n<pre><code>.box{\n\ndisplay:table-cell;\n\nvertical-align:middle;\n\ntext-align:center;\n\nwidth:120px;\n\nheight:120px;\n\nbackground:purple;\n\n}</code></pre>\n<ul><li>4.使用css3的新属性transform:translate(x,y)属性;</li></ul>\n<pre><code>.box{\n position: absolute;\n transform: translate(50%,50%);\n -webkit-transform:translate(50%,50%);\n -moz-transform:translate(50%,50%);\n -ms-transform:translate(50%,50%);\n}</code></pre>\n<ul><li>5.最高大上的一种,使用:before元素;</li></ul>\n<pre><code>.box{\n\nposition:fixed;\n\ndisplay:block;\n\nbackground:rgba(0,0,0,.5);\n\n}\n\n.box:before{\n\ncontent:\'\';\n\ndisplay:inline-block;\n\nvertical-align:middle;\n\nheight:100%;\n\n}\n\n.box.content{\n\nwidth:60px;\n\nheight:60px;\n\nline-height:60px;\n\ncolor:red;</code></pre>\n<ul><li>6.Flex布局;</li></ul>\n<pre><code><span>.box</span><span>{\n <span><span>display</span>:<span> -webkit-box</span></span>;\n <span><span>display</span>:<span> -webkit-flex</span></span>;\n <span><span>display</span>:<span> -moz-box</span></span>;\n <span><span>display</span>:<span> -moz-flex</span></span>;\n <span><span>display</span>:<span> -ms-flexbox</span></span>;\n <span><span>display</span>:<span> flex</span></span>;\n <span>水平居中\n <span>-webkit-box-align</span>:<span> center</span></span>;\n <span><span>-moz-box-align</span>:<span> center</span></span>;\n <span><span>-ms-flex-pack</span>:<span>center</span></span>;\n <span><span>-webkit-justify-content</span>:<span> center</span></span>;\n <span><span>-moz-justify-content</span>:<span> center</span></span>;\n <span><span>justify-content</span>:<span> center</span></span>;\n <span>垂直居中 \n <span>-webkit-box-pack</span>:<span> center</span></span>;\n <span><span>-moz-box-pack</span>:<span> center</span></span>;\n <span><span>-ms-flex-align</span>:<span>center</span></span>;\n <span><span>-webkit-align-items</span>:<span> center</span></span>;\n <span><span>-moz-align-items</span>:<span> center</span></span>;\n <span><span>align-items</span>:<span> center</span></span>;\n<span>}</span></span></code></pre><p><br></p><p></p><p><br></p><p></p><p><br></p>', '', '前端汇', '2016-06-18 10:36:19', '75', '0', '0', '0', '5', '', '1', '1', '', '1', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('191', 'CSS建立表单', '', '<p>1、用css建立表单</p><p>本篇资料主要介绍使用css设置表单元素的方法。</p><p>表单是网页与用户交互所不可缺少的元素,表单是网页的访问者进行交互的接口,例如大家都常遇到的:网上注册、网上登录、网上交易、网上投票、网站留言板等。</p><p>表单用于向服务器传输数据。</p><p>表单中的元素很多,包括常用的输入框、文本框、单选项、复选框、下拉菜单、和按钮等。</p><p><br></p><p> html部分:</p><p>Form:定义表单的范围。</p><p>Input:定义表单中的各个具体表单元素。</p><p>name:名称,设定此一栏的名称,程式中常会用到。</p><p>size:数值,设定此一栏位显现的宽度。</p><p>value:预设内容,设定此一栏位的预设内容。</p><p>align:对齐方式,设定此一栏位的对齐方式。</p><p>maxkength:数值,设定此一栏位可设定输入的最大长度。</p><p>文本输入框:</p><p>将type属性设置为text,就会产生文本框。</p><pre><p> 姓名:<input type=\"text\"></p></pre><p>密码输入框:</p><p>将type属性设置为password时,就会产生一个密码输入框。</p><pre><p>密码:\n <input type=\"password\">\n </p>\n <p>确认密码:\n <input type=\"password\">\n</p></pre><p>单选按钮:</p><p>将type属性设置为radio,就会产生单选按钮。</p><pre><p>性别:\n <input type=\"radio\" name=\"gender\" value=\"radio\"checked=\"true\">男 \n <input type=\"radio\" name=\"gender\" value=\"radio\">女\n</p></pre><p>复选按钮:</p><p>将type属性设置为checkbox时,就会产生复选按钮。</p><pre><p>专业:\n <input type=\"checkbox\" name=\"interest\">美术类\n <input type=\"checkbox\" name=\"interest\">理科类\n <input type=\"checkbox\" name=\"interest\">文科类\n <input type=\"checkbox\" name=\"interest\">体育类\n</p> </pre><p>按钮:</p><p>将type属性设置为submit时,即为“提交按钮”;将type设置为reset时,即为重置按钮。</p><pre><p>\n <input type=\"submit\" value=\"注册提交\">\n <input type=\"reset\" value=\"重置内容\">\n</p></pre><p>上面介绍的都是使用<input>标记的,关键在于type属性的值是什么!</p><p>多行文本框:</p><p>若需要访问者输入较多的文字的话,通常使用多行文本框,使用<textarea>/标记来实现的。</p><pre> <p>\n <textarea name=\"textarea\" id=\"textarea\" cols=\"45\" rows=\"5\"></textarea>\n </p></pre><p>下拉列表框(下拉的菜单)</p><p>下拉列表框是使用<select>标记来实现的。</p><pre> <p>城市地址:\n <select name=\"select\" size=\"4\" id=\"select\">\n <option value=\"1\">中国北部地区</option>\n <option value=\"2\">中国南部地区</option>\n <option value=\"3\">中国东部地区</option>\n <option value=\"4\">中国西部地区</option>\n </select>\n </p></pre><p>至此,简单的案例就全部完成了。为了方便读者分析,将上面的代码全部合起来,进行看效果图。</p><p>简单的案列:</p><pre><!doctype html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\">\n <title>恋爱交友网</title>\n </head>\n <body>\n \n <form method=\"post\">\n <p> 姓名:<input type=\"text\"></p>\n <p>密码:\n <input type=\"password\">\n </p>\n <p>确认密码:\n <input type=\"password\">\n </p>\n\n <p>性别:\n <input type=\"radio\" name=\"gender\" value=\"radio\"checked=\"true\">男 &nbsp; &nbsp; &nbsp;\n <input type=\"radio\" name=\"gender\" value=\"radio\">女\n </p>\n\n <p>专业:\n <input type=\"checkbox\" name=\"interest\">美术类\n <input type=\"checkbox\" name=\"interest\">理科类\n <input type=\"checkbox\" name=\"interest\">文科类\n <input type=\"checkbox\" name=\"interest\">体育类\n </p>\n\n <p>城市地址:\n <select name=\"select\"id=\"select\">\n <option value=\"1\">中国北部地区</option>\n <option value=\"2\">中国南部地区</option>\n <option value=\"3\">中国东部地区</option>\n <option value=\"4\">中国西部地区</option>\n </select>\n </p>\n\n <p>特长:\n <textarea name=\"textarea\" id=\"textarea\" cols=\"45\" rows=\"5\"></textarea>\n </p>\n\n <p>\n <input type=\"submit\" value=\"注册提交\">\n <input type=\"reset\" value=\"重置内容\">\n </p>\n </form>\n </body>\n</html></pre><p>效果图:</p><p><img src=\"/static/upload/pics/6/17/2016qrk9pZr6h3PHA2BL3h6dQ_eL.png\" alt=\"906897-20160614231745729-288226652\" style=\"max-width:100%;\"><br></p><p>css部分:</p><p>1、先来看一份“简单案例2”:</p><pre><!DOCTYPE html>\n<head>\n<title>表单</title>\n<style>\n<!--\nform{ /*设置整个表单样式*/\n border: 2px dotted #AAAAAA;\n padding: 1px 6px 1px 6px;\n margin:0px;\n font:14px Arial;\n}\ninput{ /* 所有input标记 */\n color: #00008B; \n}\ninput.txt{ /* 文本框单独设置 */\n border: 1px inset #00008B;\n background-color: #ADD8E6;\n}\ninput.btn{ /* 按钮单独设置 */\n color: #00008B;\n background-color: #ADD8E6;\n border: 1px outset #00008B;\n padding: 1px 2px 1px 2px;\n}\nselect{ /*设置下拉列表框*/\n width: 80px;\n color: #00008B;\n background-color: #ADD8E6;\n border: 1px solid #00008B;\n}\ntextarea{ /*设置多行文本框*/\n width: 200px;\n height: 40px;\n color: #00008B;\n background-color: #ADD8E6;\n border: 1px inset #00008B;\n}\n-->\n</style>\n </head>\n<body>\n<form method=\"post\">\n<p>请输入您的姓名:<br><input type=\"text\" name=\"name\" id=\"name\" class=\"txt\"></p>\n<p>请选择你最喜欢的颜色:<br>\n<select name=\"color\" id=\"color\">\n <option value=\"red\">红</option>\n <option value=\"green\">绿</option>\n <option value=\"blue\">蓝</option>\n <option value=\"yellow\">黄</option>\n <option value=\"cyan\">青</option>\n <option value=\"purple\">紫</option>\n</select><br></p>\n<p>请问你的性别:<br>\n <input type=\"radio\" name=\"sex\" id=\"male\" value=\"male\" class=\"rad\">男<br>\n <input type=\"radio\" name=\"sex\" id=\"female\" value=\"female\" class=\"rad\">女<br></p>\n<p>你喜欢做些什么:<br>\n <input type=\"checkbox\" name=\"hobby\" id=\"book\" value=\"book\" class=\"check\">看书\n <input type=\"checkbox\" name=\"hobby\" id=\"net\" value=\"net\" class=\"check\">上网\n <input type=\"checkbox\" name=\"hobby\" id=\"sleep\" value=\"sleep\" class=\"check\">睡觉<br></p>\n<p>我要留言:<br><textarea name=\"comments\" id=\"comments\" cols=\"30\" rows=\"4\" class=\"txtarea\"></textarea></p>\n<p><input type=\"submit\" name=\"btnSubmit\" id=\"btnSubmit\" value=\"提交\" class=\"btn\"></p>\n</form>\n</body>\n</html></pre><p>1、首先body部分十分简单,就一个简单的表单结构;</p><p>2、设置整个表单的样式,给表单加一个边框,设置一下内边距和外边距,再设置表单里用14像素的Arial字体;</p><p>3、给所有的输入框中的字添加同一种颜色;</p><p>4、给带有“txt”类别的<input>标记单独设置 1像素的深蓝色内陷边框,给背景上个浅天蓝色;</p><p>5、给带有“btn”类别的<input>标记单独设置字体颜色为“深蓝色”、背景色为“浅天蓝色”、添加1像素的深蓝色外凸边框、内边距;</p><p>6、给下拉菜单设置宽度,里面的字体设置为“深蓝色”,背景色为“浅天蓝色”,添加1像素的深蓝色实线边框;</p><p>7、给多行文本框设置宽度、高度,里面的字体设置为“深蓝色”,背景色为“浅天蓝色”,添加1像素的深蓝色实线边框;</p><p>效果图:</p><p><img src=\"/static/upload/pics/6/17/2016juU9Mucv_I0XVGxZQ7AXm6mi.png\" alt=\"906897-20160616221947870-980658939\" style=\"max-width:100%;\"><br></p><p>2、对齐文本框和旁边的图像按钮</p><p>一个文本框旁边一个按钮是很经常用到的网页内容,比如搜索框等等,而如果旁边的按钮使用图像的话,他们竖直方向就很不容易对齐,即使使用 vertical-align、padding和margin等都不行(特别是在IE中,Firefox中使用vertical-align还可以)。</p><p>“简单案例3”: </p><pre><!doctype html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\">\n <meta name=\"Generator\" content=\"EditPlus®\">\n <title>搜索框</title>\n </head>\n <body>\n <form> \n <input type=\"text\" name=\"foo\" value=\"Test Field\"/>\n <input type=\"image\" src=\"按钮.png\" />\n </form> \n </body>\n</html></pre><p>效果图:</p><p><img src=\"/static/upload/pics/6/17/2016CM_VrUFhjLMiI3qEI7-1gefT.png\" alt=\"906897-20160616232252417-1607516364\" style=\"max-width:100%;\"><br></p><p>上图中完全不对称,解决方案非常简单,只要给图片来个绝对定位,上述代码修改为:</p><pre><!doctype html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\">\n <meta name=\"Generator\" content=\"EditPlus®\">\n <title>搜索框</title>\n </head>\n <body>\n <form> <input type=\"text\" name=\"foo\" value=\"Test Field\"/>\n <input type=\"image\" src=\"搜索框.png\" style=\"position:absolute\"/>\n </form> \n </body>\n</html></pre><p>效果图:</p><p><img src=\"/static/upload/pics/6/17/2016EhixkAOQIOyrhcH2DTBmqewJ.png\" alt=\"906897-20160616233135635-1798717909\" style=\"max-width:100%;\"><br></p><p>到这里,在竖直方向经对齐得很好了,水平方向上;在Firefox和IE中,还略有区别,在Firefox中二者紧靠在一起,在IE中,二者之间有一点点间隔。</p><p><br></p>', '', '前端汇', '2016-06-18 10:36:31', '81', '0', '1', '0', '5', '', '0', '1', '', '1', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('192', '高管为什么晋升后就无法继续突破了', '童继龙', '<p align=\"center\" style=\"text-align: left;\"><span style=\"line-height: 1.8;\"> 我们发现,多数中层管理人员一旦晋升高管,就会长期徘徊甚至退步,很难继续突破。这是为什么呢?是真的出现了职业的天花板吗?这个天花板是企业没有进一步可以发展的空间造成的吗?这是最近在与许多同行、客户高管交流之后听到的一个问题,也做了一些思考,并找到了一篇对于高管格局修练的文章,与大家分享!</span><br></p><p>高管突破为什么很难?首先,他们都有丰富的经验,但这些经验往往把他们变成了狭隘的经验主义者,特别是专业领域的狭隘。在一个领域成功了,以为自己无所不能。当在另一个领域发现经验无效时,又容易走向反面,甚至否定经验。因此,高管跳槽容易,但跳槽后突破难,而转行后突破就更难。其次,即使是中国企业目前的高管,无论高学历还是低学历,一般仍然缺乏深厚的经济和管理理论功底,或者即使有理论功底,但理论转化为实际应用的鸿沟却很难跨越。所以,一些高学历并且在初期占据有利岗位的职业经理人往往变成本本主义者,评论别人时都对,就是自己不会做。</p><p>经验主义和本本主义,这是中国企业高管的两个极端。</p><p>我们研究高管修炼,就是想找到一个中间地带,让有理论功底的人掌握方法,让有经验的人悟透道理。</p><p>凡是不能兼容的,都是病毒,这是我们提出的重要理论。单项修炼固然有用,但如果不进行系统修炼,各类问题缺乏统一的价值观和方法论,就会产生很严重的问题。</p><p> </p><p><b>有经验、有方法,如果没有格局,职业生涯就很难突破。</b>因此,我们认为中国企业高管突破的关键是格局。只要从事经营工作,无论什么岗位,需要的格局观是一样的。格局是什么?有人认为是境界、眼光、胆识和专业度,我们认为这将把人们引向无法把控的虚无境界,并不可取。</p><p> </p><p>格局是什么</p><p><b>首先,格局是思考问题的边界。</b>格局有多远,思想才有多远,行动才有多远。销售部经理,思考的边界到底是销售部,还是营销系统,还是整个公司,甚至是整个行业,这就是思考的边界。我们提倡,要站在行业看企业,站在国家看行业,这就是思考格局的突破。那些总在部门之间闹矛盾的职业经理人,或许你在具体问题上是对的,但放在格局中可能就不一样了。</p><p> </p><p><b>其次,格局是思考问题的层次。</b>站在老板角度思考,站在员工角度做事,这就是思考的层次。在基层,你只思考基层的问题,到中层,又只思考中层的问题,总是只有一层,也是格局有问题。</p><p> </p><p><b>最后,格局是思考问题的角度。</b>基层的问题,站在高层思考可能就是战略问题;高层的问题,站在基层思考可能就是战术问题。</p><p> </p><p>有能力,无晋升;有功劳,没奖励。很多职业经理人为此苦恼,可能是因为缺乏格局。职业经理人修炼,如果总结为一句话,那就是:在思想修炼层面,要有格局;在方法层面,要有专业和经验。</p><p> </p><p>专业和方法的修炼,没人说也会重视,毕竟涉及每天的工作;格局的修炼,没有高手点拨,可能终身不会醒悟。因此,我们提倡先修炼格局,再修炼能力。当然,对于新手来说,也可以倒过来。</p><p> </p><p>如何修炼格局</p><p>对于修炼职业经理人的格局,我们提供了8个方面的认知。</p><p> </p><p>1.环境变化的认知。环境变了,一切都得变。问题在于,我们看到了表象,还是环境变化本质?我们看到了泡沫,还是环境变化的趋势?我们看到了有利的一面,还是有害的一面?</p><p>环境变化的可怕在于:往前看,很慢;往后看,很快。每年的变化,很小;30多年的变化,很大。</p><p> </p><p>2.营销的认知。即使是从事营销的老手,也要重新认识营销。因为传统营销缺了两个关键点:一是创造价值,二是实现价值。过去的营销,销量就是价值,只有制造没有创造。现在的营销,没有创造,营销就失去了价值。</p><p> </p><p>3.市场周期认知。产品周期、企业周期、产业周期、经济周期,研究规律的学者都成了大家。周期是有规律的,规律是可以把握的,周期是周而复始的。把握周期规律不仅能防范风险,更能抓住机会。掌握周期规律,就能提前预测,提前布局。在外行眼里,就是诸葛孔明附身。</p><p> </p><p>4.战略认知。不要认为战略是老板和高管的事,战略是看问题的视角。最好的是做到战略引领,最差也必须跟上市场周期变化的步伐。在此基础之上,才有所谓的竞争战略。事实上,“战略”是中国企业用得最滥也最差的管理概念。</p><p> </p><p>5.企业使命认知。正像不是所有人都能够成就卓越,也不是所有企业都能够做大做强。虽然没有可能都成为行业龙头企业,但只要经营得法,所有企业都可以生存得不错。这才应该是所有企业共同的追求。</p><p> </p><p>中国人和中国企业最爱有使命感。这种使命感有时会使得结果和预期南辕北辙。我们提出企业经营层次这个概念,目的是希望能帮助企业认清自己的位置,实事求是,恪守本分,别对自己拔苗助长。</p><p> </p><p>6.机遇认知。台风来了,你没站在风口,机会就不属于你。缺乏资源的中国企业,或者手中无资源的职业经理人,没有机会或者机遇,发展无异于逆天。市场的机会,提升的机会,创业的机会,许多能力不如自己的人因为抓住了机会,而实现了超越。</p><p> </p><p>7.老板认知。你能走多远,取决于你与谁同行。即使智慧如诸葛亮,也照样扶不起阿斗。职业经理人,既存在着如何选择老板的问题,也存在与老板如何相处的问题,还存在着如何改变老板的问题。一切皆因为老板是资源的拥有者和分配者。</p><p> </p><p>8.高管认知。认知自我比认知环境和其他人更难。我们认为中国企业的老板和高管们,如果不首先反省自身,不系统地提升自身的经营能力,多数企业,无论是中小企业,还是龙头企业,都很难从低迷中走出来。</p><p> </p><p>需要特别强调的是,本文所说的高管既包括职业经理人属性的高管,也包括处于经营之中的老板,是指那些掌握和决定企业命运的人。之所以如此,是因为中国企业的所有权和经营权整体上分离程度还很低,资本阶层的力量在中国企业的经营管理中,还占据着绝对的控制地位。短期的经营业绩或许是由职业经理人阶层决定的,但企业的命运却仍然掌握在老板阶层。</p><p> </p><p>中国市场新常态的本质特征是,已经没有简单的便车可搭,高管和老板们必须立足自身,开始坚定地寻找自己的道路。这条路就是独立思考的路,自主创新的路。否则,目前身处高位的管理人员,必然会做不下去,老板们则要么跑路,要么回到“解放前”。</p><p> </p><p>我们不妨这样看,今天,中国的经济困难就是一块试金石,是真金就应该经得起火炼;同时它也是一块磨刀石,企业能磨出来就是一把宝刀,磨不出来就变成了一块废铁。</p><p align=\"center\" style=\"text-align: center;\"><img src=\"/static/upload/pics/6/20/2016UxkYv_LNmzNhcUHczNlyvxFa.webp\" alt=\"640\" style=\"max-width:100%;\" class=\"\"><br></p><p><br></p>', '', '前端汇', '2016-06-20 21:03:55', '78', '0', '0', '0', '0', '', '1', '1', '', '1', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('195', '如何提升我的HTML&CSS技术,编写有结构的代码', '', '<h3>No1.CSS展现和组织</h3><p>1.CSS结构化</p><p>(1)比较经典的样式架构:我们经常看到的web系统样式文件一般都只包含index.css或者base.css,但在实际开发过程中我们应该尽量按模块分组CSS样式,把同类的样式放到一个模块下。虽然模块化后增加了很多css文件,但当我们发布版本的时候,可以把所有的css文件压缩到一个css文件中,这样可提升页面的加载速度。下面是一个比较经典的CSS样式架构:</p><pre># Base //基础样式\n– normalize.css //标准化样式\n– layout.css //流布局样式\n– typography.css //段落样式\n\n# Components //组件样式\n– alerts.css \n– buttons.css\n– forms.css\n– list.css\n– nav.css\n– tables.css\n\n# Modules 模块样式\n– aside.css //边栏样式\n– footer.css //底部样式\n– header.css //头部样式</pre>\n<p>(2)模块化CSS架构:包含Base、Layout、Module、State、Theme模块。每个模块的意义如下所示:</p>\n<pre># Base(核心元素style,覆盖body、form等默认样式)\n# Layout(区别不同元素的size和grid样式)\n# Module(个别的特别页面样式)\n# State(基于各种事件,提供不同的状态样式,例如:hover等)\n# Theme(基于skin、look、feel的样式)</pre>\n<p>2.如何提升页面加载速度</p>\n<p>(1)选择器写法:由于浏览器会渲染CSS样式名称路径上的每一个选择器,所以应该保持简短的选择器路径,减少渲染,提升页面加载速度。</p><p>(2)减小或压缩文件:在文件通过http协议传输时,可通过gzip方式压缩html、css以及js文件,缩减流量。不同的http服务器都提供了gzip压缩传输。</p><p>(3)减少HTTP请求-减少文件数量:把相似的文件结合成一个文件,例如把多个CSS文件压缩成一个CSS文件、把多个JS文件压缩成一个JS文件,这样每次只用发送一次http请求。</p><p>(4)减少HTTP请求-在正确的位置加载文件:CSS文件应该放在head的开头加载,JS文件应该放在页面的最后位置(body关闭标示</body>之前加载)。这是因为在加载CSS文件的同时可加载剩下的页面,而加载JS文件时会导致页面加载阻塞,所以最好是等页面加载完了再加载js文件,这样改善了用户感知。</p><p>(5)图片拼切:经常看到一组操作按钮,每个按钮有不同的图标,加载页面时每个图标加载都会产生一次请求。我们可以使用一个合并的图片作为多个元素的背景,然后使用background-position来定位图片的显示位置。下面的页面就实现了这样的想过:</p><pre><!DOCTYPE HTML>\n<html>\n<head>\n <meta charset=\"utf-8\">\n <style type=\"text/css\">\n ul {\n margin: 0;\n padding: 0;\n }\n li {\n float: left;\n list-style: none;\n margin: 2px;\n }\n li a {\n background: linear-gradient(#fff, #eee);\n border: 1px solid #ccc;\n border-radius: 3px;\n display: block;\n padding: 3px;\n }\n li a:hover {\n border-color: #999;\n }\n li span {\n background: url(\"sprite.png\") 0 0 no-repeat;\n color: transparent;\n display: block;\n font: 0/0 a;\n height: 16px;\n width: 16px;\n }\n .italic {\n background-position: -16px 0;\n }\n .underline {\n background-position: -32px 0;\n }\n .size {\n background-position: -48px 0;\n }\n .bullet {\n background-position: -64px 0;\n }\n .number {\n background-position: -80px 0;\n }\n .quote {\n background-position: -96px 0;\n }\n .left {\n background-position: -112px 0;\n }\n .center {\n background-position: -128px 0;\n }\n .right {\n background-position: -144px 0;\n }\n </style>\n\n <script type=\"text/javascript\"></script>\n</head>\n<body>\n <ul>\n <li><a href=\"#\"><span class=\"bold\">Bold Text</span></a></li>\n <li><a href=\"#\"><span class=\"italic\">Italicize Text</span></a></li>\n <li><a href=\"#\"><span class=\"underline\">Underline Text</span></a></li>\n <li><a href=\"#\"><span class=\"size\">Size Text</span></a></li>\n <li><a href=\"#\"><span class=\"bullet\">Bullet Text</span></a></li>\n <li><a href=\"#\"><span class=\"number\">Number Text</span></a></li>\n <li><a href=\"#\"><span class=\"quote\">Quote Text</span></a></li>\n <li><a href=\"#\"><span class=\"left\">Left Align Text</span></a></li>\n <li><a href=\"#\"><span class=\"center\">Center Align Text</span></a></li>\n <li><a href=\"#\"><span class=\"right\">Right Align Text</span></a></li>\n </ul>\n</body>\n</html></pre><p>展示结果如下:\n</p><h3><img src=\"/static/upload/pics/6/21/2016UgybFA4blX_i-SS1J0s6b6S1.png\" alt=\"BrqMVv\" style=\"max-width:100%;\"><br></h3><h3>No2.元素定位</h3><p>1.float浮动定位问题</p><p>(1)float经典问题:先看看代码和展示结果:</p><pre><!DOCTYPE HTML>\n<html>\n<head>\n <meta charset=\"utf-8\">\n <style type=\"text/css\">\n .box-set {\n background: #eaeaed; /* 灰色 */\n /* overflow: auto; */ /* overflow技术 */\n }\n .box {\n background: #2db34a; /* 绿色 */\n float: left;\n margin: 1.858736059%;\n width: 29.615861214%;\n }\n </style>\n\n <script type=\"text/javascript\"></script>\n</head>\n<body>\n <div class=\"box-set\">\n <figure class=\"box\">Box 1</figure>\n <figure class=\"box\">Box 2</figure>\n <figure class=\"box\">Box 3</figure>\n </div>\n</body>\n</html</pre><p>从下面的展示效果可知,父容器box-set设置的背景色并没有生效,父容器的height等于0。</p><p><img src=\"/static/upload/pics/6/21/2016vvDNuU9bWmS-rXMR2UecQ6IX.png\" alt=\"jUJzaer\" style=\"max-width:100%;\"><br></p><p>(2)解决方法:使用overflow和clearfix两个技术。</p><p>(3)解决方法-overflow:直接在box-set样式中添加属性overflow: auto,添加后就可看到父容器的背景设置生效了。但考虑兼容器,IE6还需要设置width和height。但这里遗留有其他问题,如果我们设置了其他样式,例如box-shadow样式,可能导致阴影效果溢出box-set容器。</p><p><img src=\"/static/upload/pics/6/21/2016wwUewTxJQuwx8jpY21dqOKMM.png\" alt=\"U3UjMbz\" style=\"max-width:100%;\"><br></p><p>(4)解决方法-clearfix:把页面修改成下面的代码,运行页面box-set展示正常并且也解决了IE6和7的兼容问题。需要说明的是:bofore伪类组织child的top-margin溢出,而:after伪类组织child的buttom-margin溢出。</p><pre><!DOCTYPE HTML>\n<html>\n<head>\n <meta charset=\"utf-8\">\n <style type=\"text/css\">\n .box-set {\n background: #eaeaed; /* 灰色 */\n *zoom: 1;\n }\n\n .box-set:before,\n .box-set:after {\n content: \"\";\n display: table;\n }\n\n .box-set:after{\n clear: both;\n }\n\n .box {\n background: #2db34a; /* 绿色 */\n float: left;\n margin: 1.858736059%;\n width: 29.615861214%;\n }\n </style>\n\n <script type=\"text/javascript\"></script>\n</head>\n<body>\n <div class=\"box-set\">\n <figure class=\"box\">Box 1</figure>\n <figure class=\"box\">Box 2</figure>\n <figure class=\"box\">Box 3</figure>\n </div>\n</body>\n</html></pre><p>2.position属性</p><p>(1)position默认值:元素默认的position为static。</p><p>(2)position的relative值:相对于元素position属性值为static的偏移位置。relative不会导致其他元素的位置改变。</p><p>(3)position的absolute值:元素从常规的流文档中溢出,元素的位置是相对于最近的position为relative或者absolute值得父元素偏移位置,找不到则元素的位置相对于body偏移。</p><p>(4)position的fixed值:元素相对于浏览器视窗的偏移位置,不会随着浏览器的滚动条滚动而改变位置。IE6不支持该属性。</p><p>(5)fixed实现header和foot停靠功能:下面这个例子实现footer一致停靠在浏览器的最下面,不会随着滚动条位置的变化而变化。</p><pre><!DOCTYPE HTML>\n<html>\n<head>\n <meta charset=\"utf-8\">\n <style type=\"text/css\">\n body {\n background: #eaeaed;\n }\n footer {\n height: 100px;\n background: #2db34a;\n bottom: 0;\n left: 0;\n position: fixed;\n right: 0;\n }\n </style>\n\n <script type=\"text/javascript\"></script>\n</head>\n<body>\n <footer>Fixed Footer</footer>\n</body>\n</html></pre><p>3.z-index属性</p><p>(1)默认z-index位置:越排在DOM节点的靠top位置就越在z方向的下边。</p><p>(2)前置条件:如果要设置z-index属性,必须设置元素的position属性为relative、aboslute或者fixed。例如下面的代码分别按层次停靠元素:</p><pre><span><span><!DOCTYPE HTML></span></span>\n<span><span><<span><span>html</span></span>></span></span>\n<span><span><<span><span>head</span></span>></span></span>\n <span><span><<span><span>meta</span></span> <span><span>charset</span></span>=<span><span>\"utf-8\"</span></span>></span></span>\n <span><span><<span><span>style</span></span> <span><span>type</span></span>=<span><span>\"text/css\"</span></span>></span></span><span><span>\n <span><span>.box-set</span></span> <span><span>{\n <span><span><span><span>background</span></span></span><span>:<span><span> <span><span>#eaeaed</span></span>;</span></span></span><span></span></span>\n <span><span><span><span>height</span></span></span><span>:<span><span> <span><span>160</span></span>px;</span></span></span><span></span></span>\n <span><span><span><span>position</span></span></span><span>:<span><span> relative;</span></span></span><span></span></span>\n <span><span>}</span></span></span><span></span></span>\n <span><span>.box</span></span> <span><span>{\n <span><span><span><span>background</span></span></span><span>:<span><span> <span><span>#2db34a</span></span>;</span></span></span><span></span></span>\n <span><span><span><span>border</span></span></span><span>:<span><span> <span><span>2</span></span>px solid <span><span>#ff7b29</span></span>;</span></span></span><span></span></span>\n <span><span><span><span>position</span></span></span><span>:<span><span> absolute;</span></span></span><span></span></span>\n <span><span>}</span></span></span><span></span></span>\n <span><span>.box-1</span></span> <span><span>{\n <span><span><span><span>left</span></span></span><span>:<span><span> <span><span>10</span></span>px;</span></span></span><span></span></span>\n <span><span><span><span>top</span></span></span><span>:<span><span> <span><span>10</span></span>px;</span></span></span><span></span></span>\n <span><span>}</span></span></span><span></span></span>\n <span><span>.box-2</span></span> <span><span>{\n <span><span><span><span>bottom</span></span></span><span>:<span><span> <span><span>10</span></span>px;</span></span></span><span></span></span>\n <span><span><span><span>left</span></span></span><span>:<span><span> <span><span>70</span></span>px;</span></span></span><span></span></span>\n <span><span><span><span>z-index</span></span></span><span>:<span><span> <span><span>3</span></span>;</span></span></span><span></span></span>\n <span><span>}</span></span></span><span></span></span>\n <span><span>.box-3</span></span> <span><span>{\n <span><span><span><span>left</span></span></span><span>:<span><span> <span><span>130</span></span>px;</span></span></span><span></span></span>\n <span><span><span><span>top</span></span></span><span>:<span><span> <span><span>10</span></span>px;</span></span></span><span></span></span>\n <span><span><span><span>z-index</span></span></span><span>:<span><span> <span><span>2</span></span>;</span></span></span><span></span></span>\n <span><span>}</span></span></span><span></span></span>\n <span><span>.box-4</span></span> <span><span>{\n <span><span><span><span>bottom</span></span></span><span>:<span><span> <span><span>10</span></span>px;</span></span></span><span></span></span>\n <span><span><span><span>left</span></span></span><span>:<span><span> <span><span>190</span></span>px;</span></span></span><span></span></span>\n <span><span><span><span>z-index</span></span></span><span>:<span><span> <span><span>1</span></span>;</span></span></span><span></span></span>\n <span><span>}</span></span></span><span></span></span>\n </span></span><span><span></<span><span>style</span></span>></span></span>\n<span><span></<span><span>head</span></span>></span></span>\n<span><span><<span><span>body</span></span>></span></span>\n <span><span><<span><span>div</span></span> <span><span>class</span></span>=<span><span>\"box-set\"</span></span>></span></span>\n <span><span><<span><span>figure</span></span> <span><span>class</span></span>=<span><span>\"box box-1\"</span></span>></span></span>Box 1<span><span></<span><span>figure</span></span>></span></span>\n <span><span><<span><span>figure</span></span> <span><span>class</span></span>=<span><span>\"box box-2\"</span></span>></span></span>Box 2<span><span></<span><span>figure</span></span>></span></span>\n <span><span><<span><span>figure</span></span> <span><span>class</span></span>=<span><span>\"box box-3\"</span></span>></span></span>Box 3<span><span></<span><span>figure</span></span>></span></span>\n <span><span><<span><span>figure</span></span> <span><span>class</span></span>=<span><span>\"box box-4\"</span></span>></span></span>Box 4<span><span></<span><span>figure</span></span>></span></span>\n <span><span></<span><span>div</span></span>></span></span>\n<span><span></<span><span>body</span></span>></span></span>\n<span><span></<span><span>html</span></span>></span></span></pre><p><br></p>', '', '前端汇', '2016-06-21 19:17:57', '71', '0', '1', '0', '8', '', '1', '1', '', '1', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('204', 'thinkjs性能优化之nginx配置', '网站上线了,发现慢点够可以,特别是图片及静态资源加载,是什么原因?\nnodejs的弊端,不太适合处理静态资源,配合nginx的反向代理,速度能提升不少。\n1、gzip默认没有开启\n2、node应用的静态资源,交给nginx处理,用nginx转发,实现动静态分离\n3、开启静态资源缓存', '<h3 id=\"thinkjs-nginx-\"><span style=\"line-height: 1.8; font-size: 14px;\">网站上线了,发现慢点够可以,特别是图片及静态资源加载,是什么原因?</span><br></h3>\n<p>nodejs的弊端,不太适合处理静态资源,配合nginx的反向代理,速度能提升不少。<br>1、gzip默认没有开启<br>2、node应用的静态资源,交给nginx处理,用nginx转发,实现动静态分离<br>3、开启静态资源缓存</p>\n<h4 id=\"-nginx-gzip\">一、nginx配置开启gzip</h4>\n<pre><code class=\"lang-javascript\"> gzip_vary on;\n gzip_proxied any;\n gzip_comp_level 6;\n gzip_buffers 16 8k;\n gzip_http_version 1.1;\n gzip_types text/plain text/css application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript image/svg+xml;\n gzip on;\n gzip_disable \"msie6\";\n</code></pre>\n<h4 id=\"-node-\">二、node应用动静态资源分离</h4>\n<p>1、可以考虑用个二级域名,如<a href=\"http://static.jsout.com\">http://static.jsout.com</a><br>2、如果不用二级域名,也可以通过nginx配置目录达到目的</p>\n<pre><code>server {\n ...\n listen 80;\n server_name www.jsout.com jsout.com;\n root /var/www/jsout/liblog/www; //指定静态资源根目录\n set $node_port 8361;\n ...\n</code></pre><h4 id=\"-\">三、开启缓存</h4>\n<pre><code>server {\n ...\n location ~ /static/ {\n etag on;\n expires max;\n }\n ...\n }\n</code></pre><h4 id=\"-thinkjs-nodejs-nginx-\">四、附完整的基于thinkjs的nodejs应用nginx配置(以本站为例)</h4>\n<pre><code>worker_processes 1;\nevents {\n worker_connections 1024;\n}\nhttp{\n include mime.types;\n\n default_type application/octet-stream;\n\n sendfile on;\n tcp_nopush on;\n tcp_nodelay on;\n\n keepalive_timeout 60;\n\n client_header_buffer_size 4k;\n\n open_file_cache max=51200 inactive=20s;\n open_file_cache_valid 30s;\n open_file_cache_min_uses 1;\n\n types_hash_max_size 2048;\n client_max_body_size 10m;\n\n gzip_vary on;\n gzip_proxied any;\n gzip_comp_level 6;\n gzip_buffers 16 8k;\n gzip_http_version 1.1;\n gzip_types text/plain text/css application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript image/svg+xml;\n gzip on;\n gzip_disable \"msie6\";\n\nserver {\n listen 80;\n server_name www.jsout.com jsout.com;\n root /var/www/jsout/liblog/www;\n set $node_port 8361;\n\n index index.js index.html index.htm;\n if ( -f $request_filename/index.html ){\n rewrite (.*) $1/index.html break;\n }\n if ( !-f $request_filename ){\n rewrite (.*) /index.js;\n }\n location = /index.js {\n proxy_http_version 1.1;\n proxy_set_header Connection \"\";\n proxy_set_header X-Real-IP $remote_addr;\n proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n proxy_set_header Host $http_host;\n proxy_set_header X-NginX-Proxy true;\n proxy_set_header Upgrade $http_upgrade;\n proxy_set_header Connection \"upgrade\";\n proxy_pass http://127.0.0.1:$node_port$request_uri;\n proxy_redirect off;\n }\n\n location = /production.js {\n deny all;\n }\n\n location = /testing.js {\n deny all;\n }\n\n location ~ /static/ {\n etag on;\n expires max;\n }\n} \n}\n</code></pre>', 'static/upload/pics/6/23/2016eyKkYFPcTUTEKCsjdKSe_fzN.jpg', '前端汇', '2016-06-23 14:10:07', '558', '1', '1', '0', '2', 'nginx,thinkjs,nodejs', '1', '1', '', '3', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('205', '国产优秀node.js框架thinkjs介绍', 'thinkjs是国产优秀node.js框架,是一款使用 ES6/7 特性全新开发的 Node.js MVC 框架,使用 ES7 中 async/await,或者 ES6 中的 */yield 特性彻底解决了 Node.js 中异步嵌套的问题。同时吸收了国内外众多框架的设计理念和思想,让开发 Node.js 项目更加简单、高效。', '<blockquote>\n<p> thinkjs是国产优秀node.js框架,是一款使用 ES6/7 特性全新开发的 Node.js MVC 框架,使用 ES7 中 async/await,或者 ES6 中的 */yield 特性彻底解决了 Node.js 中异步嵌套的问题。同时吸收了国内外众多框架的设计理念和思想,让开发 Node.js 项目更加简单、高效。<br> 使用 ES6/7 特性来开发项目可以大大提高开发效率,是趋势所在。并且新版的 Node.js 对 ES6 特性也有了较好的支持,即使有些特性还没有支持,也可以借助 Babel 编译来支持。</p>\n</blockquote>\n<p>\n</p><h4 id=\"-\">特性</h4><p>\n使用 ES6/7 特性来开发项目</p><br><p>借助 Babel 编译,可以在项目中大胆使用 ES6/7 所有的特性,无需担心哪些特性当前版本不支持。尤其是使用 async/await 或者 */yield 来解决异步回调的问题。</p><p></p>\n<pre><code>//user controller, home/controller/user.js\nexport default class extends think.controller.base {\n //login action\n async loginAction(self){\n //如果是get请求,直接显示登录页面\n if(this.isGet()){\n return this.display();\n }\n //这里可以通过post方法获取所有的数据,数据已经在logic里做了校验\n let data = this.post();\n let md5 = think.md5(\"think_\" + data.pwd);\n //用户名和加密后的密码去匹配数据库中对于的条目\n let result = await this.model(\"user\").where({name: data.name, pwd: md5}).find();\n //如果未匹配到任何数据,表示用户名或者密码错误\n if(think.isEmpty(result)){\n return this.fail(\"login fail\");\n }\n //获取到用户信息后,将用户信息写入session\n await this.session(\"userInfo\", result);\n return this.success();\n }\n}\n</code></pre><p>上面的代码我们使用了 ES6 里的 class, export, let 以及 ES7 里的 async/await 等特性,虽然查询数据库和写入 Session 都是异步操作,但借助 async/await,代码都是同步书写的。最后使用 Babel 进行编译,就可以稳定运行在 Node.js 的环境中了。</p>\n<h4 id=\"-typescript\">支持 TypeScript</h4>\n<p>TypeScript 是一种由微软开发的自由和开源的编程语言。它是 JavaScript 的一个超集,向这个语言添加了可选的静态类型,在大型项目里非常有用。</p>\n<p>ThinkJS 2.1 开始支持了创建 TypeScript 类型的项目,并且开发时会自动编译、自动更新,无需手工编译等复杂的操作。具体请见这里。</p>\n<h4 id=\"-\">断点调试</h4>\n<p>从 ThinkJS 2.2.0 版本开始,支持对 ES2015+ 和 TypeScript 项目的断点调试,并且报错信息也会定位到源代码下,这样可以给开发和调试带来巨大的便利,具体请见断点调试。</p>\n<h4 id=\"-\">支持多种项目结构和多种项目环境</h4>\n<p>项目支持单模块模式、普通模式、分模块模式等多种项目结构,可以满足各种项目复杂度的开发。</p>\n<p>默认支持 development,testing 和 prodution 3 种项目环境,可以在不同的项目环境下进行不同的配置,满足在不同环境下的配置需求,同时还可以基于项目需要进行扩展。</p>\n<h4 id=\"-\">支持丰富的数据库</h4>\n<p>ThinkJS 支持 mysql,mongodb,sqlite 等常见的数据库,并且封装了很多操作数据库的接口,无需手动拼接 SQL 语句,还可以自动防止 SQL 注入等安全漏洞。同时支持事务、关联模型等高级功能。</p>\n<h4 id=\"-\">代码自动更新</h4>\n<p>ThinkJS 内置了一套代码自动更新的机制,文件修改后立即生效,不用重启 Node.js 服务,也不用借助第三方模块。</p>\n<h4 id=\"-rest-\">自动创建 REST 接口</h4>\n<p>使用 thinkjs 命令可以自动创建 REST 接口,不用写任何的代码即可完成 REST API 的开发。如果想在 REST 接口中过滤字段或者进行权限校验,也很方便处理。</p>\n<h4 id=\"-websocket-\">支持多种 WebSocket 库</h4>\n<p>ThinkJS 支持 socket.io,sockjs 等常见的 WebSocket 库,并且对这些库进行包装,抹平各个库之间接口调用上的差异,给开发者一致的体验。</p>\n<h4 id=\"-\">丰富的测试用例</h4>\n<p>ThinkJS 含有 1500+ 的测试用例,代码覆盖率达到 95% ,每一次修改都有对应的测试用例来保障框架功能的稳定。</p>\n<h4 id=\"-\">支持命令行调用执行定时任务</h4>\n<p>ThinkJS 里的 Action 除了可以响应用户的请求,同时支持在命令行下访问,借助这套机制就可以很方便的执行定时任务。</p>\n<h4 id=\"hook-middleware\">Hook 和 Middleware</h4>\n<p>ThinkJS 使用 Hook 和 Middleware 机制,可以灵活的对访问请求进行拦截处理。</p>\n<p>详细的日志</p>\n<p>ThinkJS 内置了详细的日志功能,可以很方便的查看各种日志,方便追查问题。</p>\n<p>HTTP 请求日志</p>\n<pre><code>[2015-10-12 14:10:03] [HTTP] GET /favicon.ico 200 5ms\n[2015-10-12 14:10:11] [HTTP] GET /zh-cn/doc.html 200 11ms\n[2015-10-12 14:10:11] [HTTP] GET /static/css/reset.css 200 3ms\n</code></pre><p>Socket 连接日志</p>\n<pre><code>[2015-10-12 14:13:54] [SOCKET] Connect mysql with mysql://root:[email protected]:3306\n</code></pre><p>错误日志</p>\n<pre><code>[2015-10-12 14:15:32] [Error] Error: ER_ACCESS_DENIED_ERROR: Access denied for user \"root3\"@\"localhost\" (using password: YES)\n[2015-10-12 14:16:12] [Error] Error: Address already in use, port:8360. http://www.thinkjs.org/doc/error.html#EADDRINUSE\n</code></pre><h4 id=\"-\">丰富的路由机制</h4>\n<p>ThinkJS 支持正则路由、规则路由、静态路由等多种路由机制,并且可以基于模块来设置。可以让 URL 更加简洁的同时又不丢失性能。</p>\n<h4 id=\"-\">支持国际化和多主题</h4>\n<p>ThinkJS 使用很简单的方法就可以支持国际化和多主题等功能。</p>\n<h3 id=\"-\">与其他框架的对比</h3>\n<h4 id=\"-express-koa-\">与 express/koa 对比</h4>\n<p>express/koa 是 2 个比较简单的框架,框架本身提供的功能比较简单,项目中需要借助大量的第三方插件才能完成项目的开发,所以灵活度比较高。但使用很多第三方组件一方面提高了项目的复杂度,另一方面第三方插件质量参差不齐,也会带来内存泄漏等风险。</p>\n<p>koa 1.x 使用 ES6 里的 <em>/yield 解决了异步回调的问题,但 </em>/yield 只会是个过渡解决方案,会被 ES7 里的 async/await 所替代。</p>\n<p>而 ThinkJS 提供了整套解决方案,每个功能都经过了严格的性能和内存泄漏等方面的测试,并且在项目中可以直接使用 ES6/7 所有的特性。</p>\n<h4 id=\"-sails-\">与 sails 对比</h4>\n<p>sails 也是一个提供整套解决方案的 Node.js 框架,对数据库、REST API、安全方面也很多封装,使用起来比较方便。</p>\n<p>但 sails 对异步回调的问题还没有优化,还是使用 callback 的方式,给开发带来很大的不便,导致项目中无法较好的使用 ES6/7 特性。</p>\n<h4 id=\"thinkjs-\">ThinkJS 的不足</h4>\n<p>上面说了很多 ThinkJS 的优点,当然 ThinkJS 也有很多的不足。如:</p>\n<p>框架还比较新,缺少社区等方面的支持<br>还没有经过超大型项目的检验</p>\n<h4 id=\"-\">性能对比</h4>\n<p>评价一个框架是否出色,一方面看支持的功能,另一方面也要看性能。虽然 ThinkJS 更适合大型项目,功能和复杂度远远超过 Express 和 Koa,但性能上并不比 Express 和 Koa 逊色多少,具体的测试数据请见下图。<br><img src=\"/static/upload/pics/6/23/2016WGIMOcWAXpLhAvofcPGvfn97.webp\" alt=\"11\" style=\"max-width:100%;\"><br></p>\n<p>注:以上数据使用分布式压力测试系统测试。</p>\n<p>从上图中测试数据可以看到,虽然 ThinkJS 比 Express 和 Koa 性能要差一些,但差别并不大。ThinkJS 和 Sails.js 都更符合大型项目,但 ThinkJS 的性能要比 Sails.js 高很多。</p>\n<p>具体测试代码请见:<a href=\"https://github.com/thinkjs-team/thinkjs-performance-test,可以下载代码在本机测试,如果使用\">https://github.com/thinkjs-team/thinkjs-performance-test,可以下载代码在本机测试,如果使用</a> ab 测试工具,请注意该工具在 Mac 系统下很不稳定,多次测试结果会相差很大。</p><p><br></p>', '', '前端汇', '2016-06-23 14:37:55', '251', '0', '1', '0', '2', 'thinkjs,nodejs', '1', '1', 'https://www.thinkjs.org/zh-cn/doc/index.html', '3', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('206', 'thinkjs 创建项目之helloworld', '使用node.js开发应用程序,光有node.js不足以满足业务需求,而且太原始了,需要选择一些优秀的框架加速开发和提高质量。站在巨人肩膀上,尿的更高些。', '<h3 id=\"thinkjs-helloworld\">thinkjs 创建项目之helloworld</h3>\n<p>使用node.js开发应用程序,光有node.js不足以满足业务需求,而且太原始了,需要选择一些优秀的框架加速开发和提高质量。站在巨人肩膀上,尿的更高些。</p>\n<p>之前了解express和koa,偶然看到sails.js太厉害了,跟socket.io结合一起,自动创建数据库,网上有个教程一分钟搞定sails.js demo,orm是waterline,几乎搞定各种数据库,而且还能同时配置访问多个不同类型数据库。集成能力太强了。当时thinkjs也看了下,还是1.0阶段,感觉不是成熟,thinkjs发了2.0才真正关注,结合中国国情的很多问题,确定选择thinkjs。比如你想找sails的人解答问题比较困难,thinkjs的qq群回复很快直接和团队沟通。</p>\n<p>ThinkJS 是一款使用 ES6/7 特性全新开发的 Node.js MVC 框架,使用 ES7 中async/await,或者 ES6 中的 */yield 特性彻底解决了 Node.js 中异步嵌套的问题。同时吸收了国内外众多框架的设计理念和思想,让开发 Node.js 项目更加简单、高效。</p>\n<h4 id=\"1-thinkjs\">1、安装thinkjs</h4>\n<pre><code>npm install -g thinkjs@2\n//查看版本号\nthinkjs -v\n</code></pre><p>环境搭建完了,官方做双周升级,目前最新版本是2.1.7,记得命令行要带上@2;</p>\n<h4 id=\"2-helloworld-\">2、创建helloworld应用程序</h4>\n<p>我们生成个万能的helloworld,强烈推荐采用es6/7的方式编程。带--es就是生成支持es6/7。</p>\n<pre><code>thinkjs new helloworld --es\n\n create : helloworld\n create : helloworld\\package.json\n create : helloworld\\.thinkjsrc\n create : helloworld\\nginx.conf\n create : helloworld\\pm2.json\n create : helloworld\\.gitignore\n create : helloworld\\README.md\n create : helloworld\\www\n create : helloworld\\www\\development.js\n create : helloworld\\www\\production.js\n create : helloworld\\www\\testing.js\n create : helloworld\\www\\README.md\n create : helloworld\\www\\static\n create : helloworld\\www\\static\\js\n create : helloworld\\www\\static\\css\n create : helloworld\\www\\static\\img\n create : helloworld\\src\n create : helloworld\\src\\common\\bootstrap\n create : helloworld\\src\\common\\bootstrap\\middleware.js\n create : helloworld\\src\\common\\bootstrap\\global.js\n create : helloworld\\src\\common\\config\n create : helloworld\\src\\common\\config\\config.js\n create : helloworld\\src\\common\\config\\view.js\n create : helloworld\\src\\common\\config\\db.js\n create : helloworld\\src\\common\\config\\hook.js\n create : helloworld\\src\\common\\config\\session.js\n create : helloworld\\src\\common\\config\\error.js\n create : helloworld\\src\\common\\config\\env\n create : helloworld\\src\\common\\config\\env\\development.js\n create : helloworld\\src\\common\\config\\env\\testing.js\n create : helloworld\\src\\common\\config\\env\\production.js\n create : helloworld\\src\\common\\config\\locale\n create : helloworld\\src\\common\\config\\locale\\en.js\n create : helloworld\\src\\common\\controller\n create : helloworld\\src\\common\\controller\\error.js\n create : helloworld\\view\\common\n create : helloworld\\view\\common\\error_400.html\n create : helloworld\\view\\common\\error_403.html\n create : helloworld\\view\\common\\error_404.html\n create : helloworld\\view\\common\\error_500.html\n create : helloworld\\view\\common\\error_503.html\n create : helloworld\\src\\home\\config\n create : helloworld\\src\\home\\config\\config.js\n create : helloworld\\src\\home\\controller\n create : helloworld\\src\\home\\controller\\base.js\n create : helloworld\\src\\home\\controller\\index.js\n create : helloworld\\src\\home\\logic\n create : helloworld\\src\\home\\logic\\index.js\n create : helloworld\\src\\home\\model\n create : helloworld\\src\\home\\model\\index.js\n create : helloworld\\view\\home\n create : helloworld\\view\\home\\index_index.html\n</code></pre><h4 id=\"3-nodejs-\">3、安装nodejs依赖包</h4>\n<pre><code>//进入目录\n $ cd helloworld\n//安装依赖\n $ npm install\n</code></pre><p>按照提示先npm install依赖库,运行前就准备完成了。<br>最后编译并执行应用程序</p>\n<pre><code>npm run compile\nnpm run start\n如此项目初始化就完成了,运行结果如图\n</code></pre><p><img src=\"/static/upload/pics/6/23/2016KgYupug118jtUzQmiSft8azE.png\" alt=\"112\" style=\"max-width:100%;\"><br></p>\n<p>因thinkjs2.0是用es6/es7编写,需要编译成es5执行,thinkjs会自动完成这一步,用任何开发工具改了文件,即刻编译看到结果。</p>\n<h4 id=\"4-helloworld-\">4、helloworld展示</h4>\n<p>找到home/controller/index.js下index.js,给title赋值。</p>\n<pre><code>\'use strict\';\n\nimport Base from \'./base.js\';\n\nexport default class extends Base {\n /**\n * index action\n * @return {Promise} []\n */\n indexAction(){\n //auto render template file index_index.html\n this.assign(\"title\", \"ThinkJS 官网\");\n return this.display();\n }\n}\n</code></pre><p>我们再看看前端view/home/index_index.html,把controller里assign的title值打印出来。js模板引擎默认是ejs。</p>\n<pre><code> <header>\n <div class=\"wrap\">\n <h1>A New App Created By ThinkJS <%= title%> </h1>\n </div>\n </header>\n</code></pre><p>刷新浏览器就能看到。<br>如此,最简单的helloworld项目基本就完成了。</p><p><br></p>', 'static/upload/pics/6/23/2016YAp6U5uXqT9aEBUS-KfcGvF4.jpg', '前端汇', '2016-06-23 15:10:18', '839', '1', '1', '0', '2', 'thinkjs,helloword', '1', '1', '', '3', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('207', 'HTML5 开源引擎——LayaAir', '', 'LayaAir是HTML5开源引擎<p>提供Canvas和Webgl同时渲染,如果Webgl不可用,则可自动切换到Canvas模式。引擎为高性能游戏设计,支持AS,TS,JS三种语言开发,一套代码三端齐发(Flash,HTML5,APP)。</p>LayaAir特点<ul><li>极致性能</li></ul><p>LayaAir优先使用webgl渲染,如果webgl不可用,自动无缝转为canvas渲染,引擎设计过程中处处以性能为优先原则,LayaAir是为裸跑而设计的HTML5引擎。</p><ul><li>轻量易用</li></ul><p>LayaAir API设计上追求精简,简单易用,上手容易,引擎本身非常注意自身大小,是目前同等功能最小的HTML5引擎。</p><ul><li>支持多语言开发</li></ul><p>LayaAir同时支持ActionScript3、TypeScript、JavaScript三种语言开发HTML5</p><ul><li>功能齐全</li></ul><p>同时支持2D,3D,VR、时间轴动画,缓动、UI系统、粒子动画、骨骼动画、物理系统等</p><ul><li>提供可视化辅助开发及工具流</li></ul><p>LayaAirIDE提供代码开发工具及可视化编辑器,清晰的工作流,让美术,策划,程序紧密配合,提高开发效率</p><ul><li>开源免费</li></ul><p>引擎全部开源并托管到github,并且全部免费使用,包括商用</p>当前功能<ul><li><span style=\"color: inherit;\">Webgl渲染</span><br></li><li><span style=\"color: inherit;\">Canvas渲染</span><br></li><li><span style=\"color: inherit;\">矢量图</span><br></li><li><span style=\"color: inherit;\">图集支持</span><br></li><li><span style=\"color: inherit;\">加载管理器</span><br></li><li><span style=\"color: inherit;\">HTML富文本</span><br></li><li><span style=\"color: inherit;\">位图字体</span><br></li><li><span style=\"color: inherit;\">遮罩</span><br></li><li><span style=\"color: inherit;\">滤镜</span><br></li><li><span style=\"color: inherit;\">时间轴动画</span><br></li><li><span style=\"color: inherit;\">UI</span><br></li><li><span style=\"color: inherit;\">粒子</span><br></li><li><span style=\"color: inherit;\">骨骼</span><br></li><li><span style=\"color: inherit;\">物理系统</span><br></li><li><span style=\"color: inherit;\">可视化IDE</span><br></li><li><span style=\"color: inherit;\">3D</span><br></li><li><span style=\"color: inherit;\">VR</span><br></li></ul>开始使用JS版本<h4><pre>Laya.init(550, 400);\nLaya.stage.scaleMode = \"showall\";\n\nvar ape = new laya.Sprite();\n//加载猩猩图片\nape.loadImage(\"res/apes/monkey2.png\", 220, 128);\n\nLaya.stage.addChild(ape);</pre></h4><p><span style=\"color: inherit; font-size: 18px; line-height: 1.8;\">AS版本</span><br></p><pre>package\n{\n import laya.display.Sprite;\n import laya.display.Stage;\n\n <span>public</span> <span><span>class</span> <span>Sprite_DisplayImage</span>\n {</span>\n <span>public</span> <span><span>function</span> <span>Sprite_DisplayImage</span><span>()</span>\n {</span>\n Laya.init(<span>550</span>, <span>400</span>);\n Laya.stage.scaleMode = <span>\"showall\"</span>;\n\n <span>var</span> ape:Sprite = <span>new</span> Sprite();\n <span>//加载猩猩图片</span>\n ape.loadImage(<span>\"res/apes/monkey2.png\"</span>, <span>220</span>, <span>128</span>);\n\n Laya.stage.addChild(ape);\n }\n }\n}</pre><h4>TS版本</h4><p></p><pre><span><span>///</span> <span><reference path=\"../../libs/LayaAir.d.ts\" /></span></span>\n<span>class</span> Sprite_DisplayImage{\n\n constructor(){\n Laya.init(<span>550</span>, <span>400</span>);\n Laya.stage.scaleMode = <span>\"showall\"</span>;\n\n <span>var</span> ape = <span>new</span> Laya.Sprite();\n <span>//加载猩猩图片</span>\n ape.loadImage(<span>\"res/apes/monkey2.png\"</span>, <span>220</span>, <span>128</span>);\n\n Laya.stage.addChild(ape);\n }\n}\n<span>new</span> Sprite_DisplayImage();</pre><p><br></p>', '', '前端汇', '2016-06-28 10:14:08', '103', '0', '1', '0', '8', 'html', '0', '1', '', '1', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('241', '模型介绍', '', '<p>项目开发中,经常要操作数据库,如:增删改查等操作。模型就是为了方便操作数据库进行的封装,一个模型对应数据库中的一个数据表。</p>\n<p>目前支持的数据库有:<code>MySQL</code>,<code>MongoDB</code>,<code>PostgreSQL</code> 和 <code>SQLite</code>。</p>\n<h3 id=\"-\">创建模型</h3>\n<p>可以在项目目录下通过命令 <code>thinkjs model [name]</code> 来创建模型:</p>\n<pre><code class=\"lang-sh\">thinkjs model user;\n</code></pre>\n<p>执行完成后,会创建文件 <code>src/common/model/user.js</code>。</p>\n<p>默认情况下模型文件会创建在 <code>common</code> 模块下,如果想创建在其他的模块下,创建时需要指定模块名:</p>\n<pre><code class=\"lang-sh\">thinkjs model home/user\n</code></pre>\n<p><code>注</code>:模型文件不是必须存在,如果没有自定义方法可以不创建模型文件,实例化时会取模型基类的实例。</p>\n<h3 id=\"-\">模型属性</h3>\n<h4 id=\"model-pk\">model.pk</h4>\n<p>主键 key,默认为 <code>id</code>。MongoDB 下为 <code>_id</code>。</p>\n<h4 id=\"model-schema\">model.schema</h4>\n<p>数据表字段定义,默认会从数据库种读取,读到的信息类似如下:</p>\n<pre><code class=\"lang-js\">{\n id: {\n name: \'id\',\n type: \'int\', //类型\n required: true, //是否必填\n primary: true, //是否是主键\n unique: true, //是否唯一\n auto_increment: true //是否自增\n }\n}\n</code></pre>\n<p>可以在模型添加额外的属性,如:默认值和是否只读,如:</p>\n<pre><code class=\"lang-js\">export default class extends think.model.base {\n /**\n * 数据表字段定义\n * @type {Object}\n */\n schema = {\n view_nums: { //阅读数\n default: 0 //默认为 0\n },\n fullname: { //全名\n default: function() { //first_name 和 last_name 的组合,这里不能用 Arrows Function\n return this.first_name + this.last_name;\n }\n }\n create_time: { //创建时间\n default: () => { //获取当前时间\n return moment().format(\'YYYY-MM-DD HH:mm:ss\')\n },\n readonly: true //只读,添加后不可修改\n }\n }\n}\n</code></pre>\n<p><code>default</code> 默认只在添加数据时有效。如果希望在更新数据时也有效,需要添加属性 <code>update: true</code>,该属性在 2.1.4 版本中开始支持。</p>\n<p><code>readonly</code> 只在更新时有效。</p>\n<p><code>注</code>:如果设置了 <code>readonly</code>,那么会忽略 <code>update</code> 属性。</p>\n<hr>\n<p>更多属性请见 <a href=\"./api_model.html\">API -> Model</a>。</p>\n<h3 id=\"-\">模型实例化</h3>\n<p>模型实例化在不同的地方使用的方式有所差别,如果当前类含有 <code>model</code> 方法,那可以直接通过该方法实例化,如:</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n indexAction(){\n let model = this.model(\'user\');\n }\n}\n</code></pre>\n<p>否则可以通过调用 <code>think.model</code> 方法获取实例化,如:</p>\n<pre><code class=\"lang-js\">let getModelInstance = function(){\n let model = think.model(\'user\', think.config(\'db\'), \'home\');\n}\n</code></pre>\n<p>使用 <code>think.model</code> 获取模型的实例化时,需要带上模型的配置。</p>\n<h3 id=\"-\">链式调用</h3>\n<p>模型中提供了很多链式调用的方法(类似 jQuery 里的链式调用),通过链式调用方法可以方便的进行数据操作。链式调用是通过返回 <code>this</code> 来实现的。</p>\n<pre><code class=\"lang-js\">export default class extends think.model.base {\n /**\n * 获取列表数据\n */\n * getList(){\n let data = yield this.field(\'title, content\').where({\n id: [\'>\', 100]\n }).order(\'id DESC\').select();\n ...\n }\n}\n</code></pre>\n<p>模型中支持链式调用的方法有:</p>\n<ul>\n<li><code>where</code>, 用于查询或者更新条件的定义</li>\n<li><code>table</code>, 用于定义要操作的数据表名称</li>\n<li><code>alias</code>, 用于给当前数据表定义别名</li>\n<li><code>data</code>, 用于新增或者更新数据之前的数据对象赋值</li>\n<li><code>field</code>, 用于定义要查询的字段,也支持字段排除</li>\n<li><code>order</code>, 用于对结果进行排序</li>\n<li><code>limit</code>, 用于限制查询结果数据</li>\n<li><code>page</code>, 用于查询分页,生成 sql 语句时会自动转换为 limit</li>\n<li><code>group</code>, 用于对查询的 group 支持</li>\n<li><code>having</code>, 用于对查询的 having 支持</li>\n<li><code>join</code>, 用于对查询的 join 支持</li>\n<li><code>union</code>, 用于对查询的 union 支持</li>\n<li><code>distinct</code>, 用于对查询的 distinct 支持</li>\n<li><code>cache</code> 用于查询缓存</li>\n</ul>\n<p>链式调用方法具体使用方式请见 <a href=\"./api_model.html\">API -> Model</a>。</p><p>文章来源:<a href=\"http://www.thinkjs.org/\">http://www.thinkjs.org</a></p><p><br></p>', '', '前端汇', '2016-07-14 10:01:57', '68', '0', '0', '0', '18', 'thinkjs,nodejs', '1', '1', '', '8', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('231', '创建项目', '', '<h3 id=\"-node-js\">安装 Node.js</h3>\n<p>ThinkJS 是一款 Node.js 的 MVC 框架,所以安装 ThinkJS 之前,需要先安装 Node.js 环境,可以去 <a href=\"https://nodejs.org/\">官方</a> 下载最新的安装包进行安装,也可以通过其他一些渠道安装。</p>\n<p>安装完成后,在命令行执行 <code>node -v</code>,如果能看到对应的版本号输出,则表示安装成功。</p>\n<p>ThinkJS 需要 Node.js 的版本 <code>>=0.12.0</code>,如果版本小于这个版本,需要升级 Node.js,否则无法启动服务。建议将 Node.js 版本升级到 <code>4.2.1</code> 或更高版本。</p>\n<h3 id=\"-thinkjs\">安装 ThinkJS</h3>\n<p>通过下面的命令即可安装 ThinkJS:</p>\n<pre><code class=\"lang-sh\">npm install thinkjs@2 -g --verbose\n</code></pre>\n<p>如果安装很慢的话,可以尝试使用 <a href=\"http://npm.taobao.org/\">taobao</a> 的源进行安装。具体如下:</p>\n<pre><code class=\"lang-sh\">npm install thinkjs@2 -g --registry=https://registry.npm.taobao.org --verbose\n</code></pre>\n<p>安装完成后,可以通过 <code>thinkjs --version</code> 或 <code>thinkjs -V</code> 命令查看安装的版本。</p>\n<p><code>注</code>:如果之前安装过 ThinkJS 1.x 的版本,可能需要将之前的版本删除掉,可以通过 <code>npm uninstall -g thinkjs-cmd</code> 命令删除。</p>\n<h3 id=\"-thinkjs\">更新 ThinkJS</h3>\n<h4 id=\"-thinkjs\">更新全局的 ThinkJS</h4>\n<p>执行下面的命令即可更新全局的 ThinkJS:</p>\n<pre><code class=\"lang-sh\">npm install -g thinkjs@2\n</code></pre>\n<h4 id=\"-thinkjs\">更新项目里的 ThinkJS</h4>\n<p>在项目目录下,执行下面的命令即可更新当前项目的 ThinkJS:</p>\n<pre><code class=\"lang-sh\">npm install thinkjs@2\n</code></pre>\n<h3 id=\"-\">使用命令创建项目</h3>\n<p>ThinkJS 安装完成后,就可以通过下面的命令创建项目:</p>\n<pre><code class=\"lang-sh\">thinkjs new project_path; #project_path为项目存放的目录\n</code></pre>\n<p>如果想用 <code>ES6</code> 特性来开发项目的话,可以创建一个 <code>ES6</code> 模式的项目,具体如下:</p>\n<pre><code class=\"lang-sh\">thinkjs new project_path --es; #project_path为项目存放的目录\n</code></pre>\n<p>如果能看见类似下面的输出,表示项目创建成功了:</p>\n<pre><code class=\"lang-text\"> create : demo/\n create : demo/package.json\n create : demo/.thinkjsrc\n create : demo/nginx.conf\n create : demo/README.md\n create : demo/www/\n create : demo/www/index.js\n create : demo/app\n create : demo/app/common/runtime\n create : demo/app/common/config\n create : demo/app/common/config/config.js\n create : demo/app/common/config/view.js\n create : demo/app/common/config/db.js\n ...\n create : demo/app/home/logic\n create : demo/app/home/logic/index.js\n create : demo/app/home/view\n create : demo/app/home/view/index_index.html\n\n enter path:\n $ cd demo/\n\n install dependencies:\n $ npm install\n\n run the app:\n $ npm start\n</code></pre>\n<p>关于创建项目命令的更多信息,请见 <a href=\"./thinkjs_command.html\">扩展功能 -> ThinkJS 命令</a>。</p>\n<h3 id=\"-\">安装依赖</h3>\n<p>项目安装后,进入项目目录,执行 <code>npm install</code> 安装依赖,可以使用 <code>taobao</code> 源进行安装。</p>\n<pre><code class=\"lang-sh\">npm install --registry=https://registry.npm.taobao.org --verbose\n</code></pre>\n<h3 id=\"-\">编译项目</h3>\n<p><del>如果创建项目时加上了 <code>--es6</code> 参数,代码需要编译后才能运行。那么需要先在项目下执行命令 <code>npm run watch-compile</code> ,这样文件有修改后就会自动编译了。</del></p>\n<p><del>执行命令后会挂起一个进程,注意不要结束这个进程,其他命令可以再新开一个标签页里执行。</del></p>\n<p><code>注</code>:<code>2.0.6</code> 版本开始内置了自动编译的功能,无需再执行该命令,直接启动服务即可。如果是老项目可以将 ThinkJS 升级到最新版本,然后在文件 <code>www/index.js</code> 加入代码 <code>instance.compile();</code> 即可(放在 <code>instance.run()</code> 之前)。</p>\n<h3 id=\"-\">启动项目</h3>\n<p>在项目目录下执行命令 <code>npm start</code>,如果能看到类似下面的内容,表示服务启动成功。</p>\n<pre><code class=\"lang-text\">[2015-09-21 20:21:09] [THINK] Server running at http://127.0.0.1:8360/\n[2015-09-21 20:21:09] [THINK] ThinkJS Version: 2.0.0\n[2015-09-21 20:21:09] [THINK] Cluster Status: closed\n[2015-09-21 20:21:09] [THINK] WebSocket Status: closed\n[2015-09-21 20:21:09] [THINK] File Auto Reload: true\n[2015-09-21 20:21:09] [THINK] App Enviroment: development\n</code></pre>\n<h3 id=\"-\">访问项目</h3>\n<p>打开浏览器,访问<code>http://127.0.0.1:8360/</code>即可。</p>\n<p>如果是在远程机器,需要通过远程机器的 IP 访问,同时要保证 8360 端口可访问。</p><p><br></p>', '', '前端汇', '2016-07-04 14:41:50', '80', '0', '0', '0', '18', 'thinkjs,nodejs', '0', '1', 'https://thinkjs.org/', '8', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('209', 'html5的video元素学习手札', '', '<p>为了监控移动端视频播放的情况,研究了一下 html5 <video> 标签的属性与事件触发,及其在各系统和各个浏览器的兼容情况</p><h3>属性与事件</h3><p>理解清楚属性和事件,才能更好的使用 video ,达到预期的效果,更好的检测视频播放的状况来做出分析和调整,这里仅列举了解到且大致理解的,更多相关后续补充</p><h4>属性</h4><p><video> 标签嵌入到HTML文档中</p><pre><span><span><span><<span><span>video</span></span> <span><span>src</span></span>=<span><span>\"\"</span></span> <span><span>type</span></span>=<span><span>\"video/mp4\"</span></span> <span><span>autoplay</span></span>=<span><span>\"autoplay\"</span></span> <span><span>controls</span></span>=<span><span>\"\"</span></span> <span><span>poster</span></span>=<span><span>\"\"</span></span> <span><span>preload</span></span>=<span><span>\"none\"</span></span>></span></span><span><span></<span><span>video</span></span>></span></span></span></pre><p>初始化 <video> 标签时主要设置的属性</p><ol><li>src:要嵌到页面的视频的URL。可选;你也可以使用video块内的 <source> 元素来指定需要嵌到页面的视频</li><li>autoplay:布尔属性;指定后,视频会马上自动开始播放,不会停下来等着数据载入结束</li><li>controls:加上这个属性,Gecko 会提供用户控制,允许用户控制视频的播放,包括音量,跨帧,暂停/恢复播放</li><li>poster:一个海报帧的URL,用于在用户播放或者跳帧之前展示。如果属性未指定,那么在第一帧可用之前什么都不会展示;之后第一帧就像海报帧一样展示</li><li>preload:该枚举属性旨在告诉浏览器作者认为达到最佳的用户体验的方式是什么。可能是下列值之一:none:提示作者认为用户不需要查看该视频,服务器也想要最小化访问流量;换句话说就是提示浏览器该视频不需要缓存metadata:提示尽管作者认为用户不需要查看该视频,不过抓取元数据(比如:长度)还是很合理的auto:用户需要这个视频优先加载;换句话说就是提示:如果需要的话,可以下载整个视频,即使用户并不一定会用它空字符串:也就代指 auto 值</li><li>buffered:这个属性可以读取到哪段时间范围内的媒体被缓存了。该属性包含了一个 TimeRanges 对象</li><li>played:一个 TimeRanges 对象,指明了视频已经播放的所有范围</li><li>loop:布尔属性;指定后,会在视频结尾的地方,自动返回视频开始的地方</li><li>muted:布尔属性,指明了视频里的音频的默认设置。设置后,音频会初始化为静音。默认值是 false ,意味着视频播放的时候音频也会播放</li><li>height:视频展示区域的高度,单位是 CSS 像素</li><li>width:视频显示区域的宽度,单位是 CSS 像素</li><li>crossorigin:该枚举属性指明抓取相关图片是否必须用到CORS(跨域资源共享)。 支持CORS的资源 可在 <canvas> 元素中被重用,而不会被污染。允许的值如下:anonymous:跨域请求会被执行,但是不发送凭证。use-credentials:跨域请求A cross-origin request会被执行,且凭证会被发送。</li></ol><p>TimeRanges 对象表示事件段,比如,视频快进的时间段,有一个 length 属性,表示时间段的个数,有两个方法 start() 和 end() ,分别返回时间段开始的时间点和结束的时间点</p><p>事件交互中主要使用的属性</p><ol><li>currentTime:播放进行到的时间点,单位为秒</li><li>duration:视频总时长,单位为秒</li></ol><h4>事件</h4><p>还有更多事件api,这里只列举了试过的</p><ol><li>playing:在媒体开始播放时触发(不论是初次播放、在暂停后恢复、或是在结束后重新开始)</li><li>ended:播放结束时触发</li><li>pause:播放暂停时触发</li><li>waiting:在一个待执行的操作(如回放)因等待另一个操作(如跳跃或下载)被延迟时触发</li><li>timeupdate:元素的 currentTime 属性表示的时间已经改变</li><li>seeking: 在跳跃操作开始时触发</li><li>seeked:在跳跃操作完成时触发</li><li>error:在发生错误时触发。元素的 error 属性会包含更多信息</li><li>loadeddata: 媒体的第一帧已经加载完毕</li></ol><h4>监控指标</h4><ol><li>播放:start:1(首次播放)2(重播)</li><li>播放:end:1</li><li>播放暂停:pause:1</li><li>播放中止:pause:1</li><li>快进/快退:Jump:1(快进)2(快退)</li><li>错误:fail: 1(取回过程);2(当下载时发生错误);3(当解码时发生错误);4(不支持音频/视频)</li><li>播放等待: wait:1</li><li>播放时长:totaltime:秒(包含重播)</li></ol><p>其中1,2,3,5,6,7都很好监控,对相应事件进行监听就可以了,这里主要讲下是怎么监控播放中止和播放时长的</p><h4>播放中止</h4><p>具体场景是移动端浏览器切换tab导致的隐藏和用户按home键退出浏览器</p><p>html5 提供了 Page Visibility API 来支持监听tab切换,与之对应新增了</p><ul><li>document.hidden 属性,它显示页面是否为用户当前观看的页面,值为 ture 或 false</li><li>document.visibilityState 属性, visible 表示页面被展现, hidden 表示页面未被展现, prerender 表示页面在重新生成,用户不可见</li><li>visibilitychange 事件,监听页面在 visible 与 hidden 之间的切换</li></ul><p>visibilitychange事件的具体使用</p><pre><span><span><span>var</span></span> hidden;</span>\n<span><span><span>var</span></span> visibilityChange;</span>\n<span><span><span>if</span></span> (<span><span>typeof</span></span> <span>document</span>.hidden !== <span><span>\'undefined\'</span></span>) {</span>\n<span> hidden = <span><span>\'hidden\'</span></span>;</span>\n<span> visibilityChange = <span><span>\'visibilitychange\'</span></span>;</span>\n<span>} <span><span>else</span></span> <span><span>if</span></span> (<span><span>typeof</span></span> <span>document</span>.mozHidden !== <span><span>\'undefined\'</span></span>) {</span>\n<span> hidden = <span><span>\'mozHidden\'</span></span>;</span>\n<span> visibilityChange = <span><span>\'mozvisibilitychange\'</span></span>;</span>\n<span>} <span><span>else</span></span> <span><span>if</span></span> (<span><span>typeof</span></span> <span>document</span>.msHidden !== <span><span>\'undefined\'</span></span>) {</span>\n<span> hidden = <span><span>\'msHidden\'</span></span>;</span>\n<span> visibilityChange = <span><span>\'msvisibilitychange\'</span></span>;</span>\n<span>} <span><span>else</span></span> <span><span>if</span></span> (<span><span>typeof</span></span> <span>document</span>.webkitHidden !== <span><span>\'undefined\'</span></span>) {</span>\n<span> hidden = <span><span>\'webkitHidden\'</span></span>;</span>\n<span> visibilityChange = <span><span>\'webkitvisibilitychange\'</span></span>;</span>\n<span>}</span>\n<span></span>\n<span><span>document</span>.addEventListener(visibilityChange, <span><span><span><span>function</span></span></span><span> <span>(<span></span>)</span> </span></span><span>{</span></span>\n<span> <span><span>if</span></span> (<span>document</span>[hidden]) {</span>\n<span> <span><span>// do something...</span></span></span>\n<span> }</span>\n<span>}, <span><span>false</span></span>);</span></pre><p>关于 visibilitychange 事件的兼容性,测试了两部手机,华为mt7 和 iphone6 ,兼容情况如下</p><p>华为mt7</p><ul><li>qq浏览器:tab切换触发,home键退出触发</li><li>uc浏览器:tab切换触发,home键退出触发(退出后进程继续在跑,其它浏览器进程被暂停)</li><li>手机百度:tab切换不触发,home键退出不触发</li></ul><p>iphone6</p><ul><li>uc浏览器:tab切换触发,home键不触发</li><li>百度浏览器:同上</li><li>手机百度:同上</li><li>Safari:tab切换触发,home键退出触发</li></ul><p>由于兼容问题,且各系统的各个浏览器基本在tab切换触发,home键退出触发的情况下触发pause事件,所以播放中止的日志依旧打印pause,如果后面没有继续操作则把这个pause日志当做播放中止</p><p>页面刷新和浏览器tab被关闭的时候会触发 window.onunload ,也可以做为补充场景</p><h4>播放时长</h4><p>起初的思路是获取到开始播放到停止播放的事件差,记下时间点使用了 currentTime 属性,主要实现在两方面</p><ol><li>playing 时记下时间点startT, pause 和 ended 和 seeked 时记下时间点endT,endT - startT 即播放时长</li><li>seeked 时记下时间点startT, seeking 时记下时间点endT,endT - startT 即播放时长</li></ol><p>这个思路在 ios 下是看似没有问题的,但是 android 下确实不行,主要原因是 seeking 事件的监听没理解到位,seeking 事件触发点是用户目标跳跃到的位置,比如:视频播放在 0 秒点时,用户点击到了 60 秒点处,这是取到的 currentTime 就是 60 ,本来以为会是 0 , ios 下看似没有问题是因为它的全屏播放模式下,进度条是要拖拽的,不能直接点击到某个点</p><p>于是,使用 timeupdate 来获取 seeking 触发前的时间点,就可以获取到相对准确的播放时长了</p><h4>error事件</h4><p>监听 error 事件会返回 error.code 来标识错误类型:</p><ul><li>1 = MEDIA_ERR_ABORTED - 取回过程被用户中止</li><li>2 = MEDIA_ERR_NETWORK - 当下载时发生错误</li><li>3 = MEDIA_ERR_DECODE - 当解码时发生错误</li><li>4 = MEDIA_ERR_SRC_NOT_SUPPORTED - 不支持音频/视频</li></ul><p>官网的解释是:</p><ul><li>MEDIA_ERR_ABORTED (numeric value 1)The fetching process for the media resource was aborted by the user agent at the user’s request.在取回资源过程中,被用户的操作中止(您中止了视频播放)</li><li>MEDIA_ERR_NETWORK (numeric value 2)A network error of some description caused the user agent to stop fetching the media resource, after the resource was established to be usable.因为一些网络问题导致的用户无法取回资源,前提是资源被确定为可用(网络问题导致视频下载中断)</li><li>MEDIA_ERR_DECODE (numeric value 3)An error of some description occurred while decoding the media resource, after the resource was established to be usable.解码资源时产生的问题,前提是资源被确定为可用(『a corruption problem(翻译不过来)』 或者 所使用的视频功能的浏览器不支持)</li><li>MEDIA_ERR_SRC_NOT_SUPPORTED (numeric value 4)The media resource indicated by the src attribute was not suitable.由src属性所指定的媒体资源不适合(视频无法加载,或因为服务器或网络故障,或格式不支持)</li></ul><p>总结一下就是,1、2、3是在视频可用情况下因为外在因素导致的播放失败,4是可能因为资源本身有问题导致的播放失败(存在资源可播放也打印 error.code 为4的情况)</p><h4>遇到的一些状况</h4><ul><li>没有 <source> 元素且 src 属性为空时播放会触发 error 事件,状态码为4解决:忽略 src 属性为空时的报错</li><li>播放结束会触发暂停解决:声明状态变量,随着具体操作更新状态,播放状态下才会执行暂停操作,结束状态不执行</li><li>播放结束后重播会触发 seeking 和 seeked ,一般浏览器触发一次, android 下uc浏览器触发多次解决:同上</li><li>一些浏览器监听不到 seeking 和 seeked解决:在 timeupdate 里来分析猜测用户行为</li><li>一些浏览器存在多次连续触发 seeking + seeked 的情况解决:时间戳 + 节流 等待最后一次</li><li>seeking 和 seeked 与 timeupdate 需要保证不会同时执行解决:监听到 seeking 触发,就不再执行 timeupdate 模拟走过的坑:我曾设想在播放时直接判断出是否支持 seeking ,方式是播放时设置 currentTime 为 0.01 ,然后检测 seeking 属性,后来发现浏览器在这样设置后的 seeking 属性值不一致</li><li>个别浏览器播放状态下不触发 seeking 和 seeked ,但是在重播的时候触发解决:声明状态变量,随着具体操作更新状态,结束状态不监听 seeking 触发</li></ul><p></p><p>提示一下,遇到浏览器表现不同的情况,千万不要尝试对各个浏览器适配 hack ,没法根本上解决问题,而我的做法是声明状态变量,随着具体操作更新状态,在正确的状态下才进行操作,通过播放状态来规范事件的触发时机。</p><p><br></p>', '', '前端汇', '2016-06-28 10:14:27', '92', '0', '1', '0', '8', '', '0', '1', 'html', '1', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('230', '介绍', '', '<p>ThinkJS 是一款使用 ES6/7 特性全新开发的 Node.js MVC 框架,使用 ES7 中 <code>async/await</code>,或者 ES6 中的 <code>*/yield</code> 特性彻底解决了 Node.js 中异步嵌套的问题。同时吸收了国内外众多框架的设计理念和思想,让开发 Node.js 项目更加简单、高效。</p>\n<p>使用 ES6/7 特性来开发项目可以大大提高开发效率,是趋势所在。并且新版的 Node.js 对 ES6 特性也有了较好的支持,即使有些特性还没有支持,也可以借助 <a href=\"http://babeljs.io/\">Babel</a> 编译来支持。</p>\n<h3 id=\"-\">特性</h3>\n<h4 id=\"-es6-7-\">使用 ES6/7 特性来开发项目</h4>\n<p>借助 Babel 编译,可以在项目中大胆使用 ES6/7 所有的特性,无需担心哪些特性当前版本不支持。尤其是使用 <code>async/await</code> 或者 <code>*/yield</code> 来解决异步回调的问题。</p>\n<pre><code class=\"lang-js\">//user controller, home/controller/user.js\nexport default class extends think.controller.base {\n //login action\n async loginAction(self){\n //如果是get请求,直接显示登录页面\n if(this.isGet()){\n return this.display();\n }\n //这里可以通过post方法获取所有的数据,数据已经在logic里做了校验\n let data = this.post();\n let md5 = think.md5(\'think_\' + data.pwd);\n //用户名和加密后的密码去匹配数据库中对于的条目\n let result = await this.model(\'user\').where({name: data.name, pwd: md5}).find();\n //如果未匹配到任何数据,表示用户名或者密码错误\n if(think.isEmpty(result)){\n return this.fail(\'login fail\');\n }\n //获取到用户信息后,将用户信息写入session\n await this.session(\'userInfo\', result);\n return this.success();\n }\n}\n</code></pre>\n<p>上面的代码我们使用了 ES6 里的 <code>class</code>, <code>export</code>, <code>let</code> 以及 ES7 里的 <code>async/await</code> 等特性,虽然查询数据库和写入 <code>Session</code> 都是异步操作,但借助 <code>async/await</code>,代码都是同步书写的。最后使用 <code>Babel</code> 进行编译,就可以稳定运行在 Node.js 的环境中了。</p>\n<h4 id=\"-typescript\">支持 TypeScript</h4>\n<p><a href=\"http://www.typescriptlang.org/\">TypeScript</a> 是一种由微软开发的自由和开源的编程语言。它是 JavaScript 的一个超集,向这个语言添加了可选的静态类型,在大型项目里非常有用。</p>\n<p>ThinkJS 2.1 开始支持了创建 TypeScript 类型的项目,并且开发时会自动编译、自动更新,无需手工编译等复杂的操作。具体请见<a href=\"./typescript.html\">这里</a>。</p>\n<h4 id=\"-\">支持多种项目结构和多种项目环境</h4>\n<p>项目支持单模块模式、普通模式、分模块模式等多种项目结构,可以满足各种项目复杂度的开发。</p>\n<p>默认支持 <code>development</code>,<code>testing</code> 和 <code>prodution</code> 3 种项目环境,可以在不同的项目环境下进行不同的配置,满足在不同环境下的配置需求,同时还可以基于项目需要进行扩展。</p>\n<h4 id=\"-\">支持丰富的数据库</h4>\n<p>ThinkJS 支持 <code>mysql</code>,<code>mongodb</code>,<code>sqlite</code> 等常见的数据库,并且封装了很多操作数据库的接口,无需手动拼接 SQL 语句,还可以自动防止 SQL 注入等安全漏洞。同时支持事务、关联模型等高级功能。</p>\n<h4 id=\"-\">代码自动更新</h4>\n<p>ThinkJS 内置了一套代码自动更新的机制,文件修改后立即生效,不用重启 Node.js 服务,也不用借助第三方模块。</p>\n<h4 id=\"-rest-\">自动创建 REST 接口</h4>\n<p>使用 <code>thinkjs</code> 命令可以自动创建 REST 接口,不用写任何的代码即可完成 REST API 的开发。如果想在 REST 接口中过滤字段或者进行权限校验,也很方便处理。</p>\n<h4 id=\"-websocket-\">支持多种 WebSocket 库</h4>\n<p>ThinkJS 支持 <code>socket.io</code>,<code>sockjs</code> 等常见的 WebSocket 库,并且对这些库进行包装,抹平各个库之间接口调用上的差异,给开发者一致的体验。</p>\n<h4 id=\"-\">丰富的测试用例</h4>\n<p>ThinkJS 含有 1500+ 的测试用例,代码覆盖率达到 95% ,每一次修改都有对应的测试用例来保障框架功能的稳定。</p>\n<h4 id=\"-\">支持命令行调用执行定时任务</h4>\n<p>ThinkJS 里的 <code>Action</code> 除了可以响应用户的请求,同时支持在命令行下访问,借助这套机制就可以很方便的执行定时任务。</p>\n<h4 id=\"hook-middleware\">Hook 和 Middleware</h4>\n<p>ThinkJS 使用 Hook 和 Middleware 机制,可以灵活的对访问请求进行拦截处理。</p>\n<h4 id=\"-\">详细的日志</h4>\n<p>ThinkJS 内置了详细的日志功能,可以很方便的查看各种日志,方便追查问题。</p>\n<h5 id=\"http-\">HTTP 请求日志</h5>\n<pre><code>[2015-10-12 14:10:03] [HTTP] GET /favicon.ico 200 5ms\n[2015-10-12 14:10:11] [HTTP] GET /zh-cn/doc.html 200 11ms\n[2015-10-12 14:10:11] [HTTP] GET /static/css/reset.css 200 3ms\n</code></pre><h5 id=\"socket-\">Socket 连接日志</h5>\n<pre><code>[2015-10-12 14:13:54] [SOCKET] Connect mysql with mysql://root:[email protected]:3306\n</code></pre><h5 id=\"-\">错误日志</h5>\n<pre><code>[2015-10-12 14:15:32] [Error] Error: ER_ACCESS_DENIED_ERROR: Access denied for user \'root3\'@\'localhost\' (using password: YES)\n\n[2015-10-12 14:16:12] [Error] Error: Address already in use, port:8360. http://www.thinkjs.org/doc/error.html#EADDRINUSE\n</code></pre><h4 id=\"-\">丰富的路由机制</h4>\n<p>ThinkJS 支持正则路由、规则路由、静态路由等多种路由机制,并且可以基于模块来设置。可以让 URL 更加简洁的同时又不丢失性能。</p>\n<h4 id=\"-\">支持国际化和多主题</h4>\n<p>ThinkJS 使用很简单的方法就可以支持国际化和多主题等功能。</p>\n<h3 id=\"-\">与其他框架的对比</h3>\n<h4 id=\"-express-koa-\">与 express/koa 对比</h4>\n<p>express/koa 是 2 个比较简单的框架,框架本身提供的功能比较简单,项目中需要借助大量的第三方插件才能完成项目的开发,所以灵活度比较高。但使用很多第三方组件一方面提高了项目的复杂度,另一方面第三方插件质量参差不齐,也会带来内存泄漏等风险。</p>\n<p>koa 1.x 使用 ES6 里的 <code>*/yield</code> 解决了异步回调的问题,但 <code>*/yield</code> 只会是个过渡解决方案,会被 ES7 里的 <code>async/await</code> 所替代。</p>\n<p>而 ThinkJS 提供了整套解决方案,每个功能都经过了严格的性能和内存泄漏等方面的测试,并且在项目中可以直接使用 ES6/7 所有的特性。</p>\n<h4 id=\"-sails-\">与 sails 对比</h4>\n<p>sails 也是一个提供整套解决方案的 Node.js 框架,对数据库、REST API、安全方面也很多封装,使用起来比较方便。</p>\n<p>但 sails 对异步回调的问题还没有优化,还是使用 callback 的方式,给开发带来很大的不便,导致项目中无法较好的使用 ES6/7 特性。</p>\n<h4 id=\"thinkjs-\">ThinkJS 的不足</h4>\n<p>上面说了很多 ThinkJS 的优点,当然 ThinkJS 也有很多的不足。如:</p>\n<ul>\n<li>框架还比较新,缺少社区等方面的支持</li>\n<li>还没有经过超大型项目的检验</li>\n</ul>\n<h4 id=\"-\">性能对比</h4>\n<p>评价一个框架是否出色,一方面看支持的功能,另一方面也要看性能。虽然 ThinkJS 更适合大型项目,功能和复杂度远远超过 Express 和 Koa,但性能上并不比 Express 和 Koa 逊色多少,具体的测试数据请见下图。</p>\n<p><img src=\"https://p.ssl.qhimg.com/t018bc14974bff742de.jpg\" alt=\"ThinkJS 性能测试\" style=\"max-width:100%\"></p>\n<p><code>注</code>:以上数据使用分布式压力测试系统测试。</p>\n<p>从上图中测试数据可以看到,虽然 ThinkJS 比 Express 和 Koa 性能要差一些,但差别并不大。ThinkJS 和 Sails.js 都更符合大型项目,但 ThinkJS 的性能要比 Sails.js 高很多。</p>\n<p>具体测试代码请见:<a href=\"https://github.com/thinkjs-team/thinkjs-performance-test\">https://github.com/thinkjs-team/thinkjs-performance-test</a>,可以下载代码在本机测试,如果使用 <code>ab</code> 测试工具,请注意该工具在 Mac 系统下很不稳定,多次测试结果会相差很大。</p>\n<h3 id=\"es6-7-\">ES6/7 参考文档</h3>\n<p>关于 ES6/7 特性可以参考下面的文档:</p>\n<ul>\n<li><a href=\"http://liubin.github.io/promises-book/#ch2-promise-all\">JavaScript Promise迷你书</a></li>\n<li><a href=\"http://babeljs.io/docs/learn-es2015/\">learn-es2015</a></li>\n<li><a href=\"http://es6.ruanyifeng.com/\">ECMAScript 6 入门</a></li>\n<li><a href=\"http://gank.io/post/564151c1f1df1210001c9161\">给 JavaScript 初心者的 ES2015 实战</a></li>\n<li><a href=\"https://github.com/lukehoban/es6features\">ECMAScript 6 Features</a></li>\n<li><a href=\"http://kangax.github.io/compat-table/es6/\">ECMAScript 6 compatibility table</a></li>\n<li><a href=\"https://github.com/hemanth/es7-features\">ECMAScript 7 Features</a></li>\n<li><a href=\"http://kangax.github.io/compat-table/es7/\">ECMAScript 7 compatibility table</a></li>\n</ul><p><br></p>', '', '前端汇', '2016-07-04 14:41:39', '74', '0', '0', '0', '18', 'thinkjs,nodejs', '1', '1', 'https://thinkjs.org/', '8', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('232', '项目结构', '', '<p>通过 thinkjs 命令创建完项目后,项目目录结构类似如下:</p>\n<pre><code class=\"lang-text\"> |-- nginx.conf \n |-- package.json\n |-- src \n | |-- common \n | | |-- bootstrap \n | | | |-- generate_icon.js\n | | | `-- middleware.js\n | | |-- config \n | | | |-- config.js\n | | | |-- env \n | | | | |-- development.js\n | | | | `-- production.js\n | | | |-- hook.js \n | | | |-- locale \n | | | | |-- en.js\n | | | | `-- zh-cn.js\n | | | `-- route.js \n | | |-- controller \n | | | `-- error.js\n | | `-- runtime\n | `-- home \n | |-- config\n | |-- controller\n | | |-- base.js\n | | `-- index.js\n | |-- logic\n | | `-- doc.js\n | `-- model\n |-- view\n | `-- zh-cn\n | |-- common\n | | |-- error_400.html\n | | |-- error_403.html\n | | |-- error_404.html\n | | |-- error_500.html\n | | `-- error_503.html\n | `-- home\n | |-- doc_index.html\n | |-- doc_search.html\n | |-- inc\n | | |-- footer.html\n | | `-- header.html\n | |-- index_changelog.html\n | |-- index_demo.html\n | `-- index_index.html\n `-- www\n |-- favicon.ico\n |-- index.js\n |-- production.js\n `-- static\n |-- css\n |-- img\n `-- js\n</code></pre>\n<p><code>注</code>:指定不同的模式创建的项目目录机构可能有细微的差别,但总体是类似的。</p>\n<h3 id=\"nginx-conf\">nginx.conf</h3>\n<p>nginx 的配置文件,建议线上使用 nginx 做反向代理。</p>\n<h3 id=\"src\">src</h3>\n<p>源代码目录,使用 <code>--es6</code> 参数创建项目才有该目录。项目启动时会自动将 <code>src</code> 目录下的文件编译到 <code>app</code> 目录下。</p>\n<p>如果没有使用 ES6 特性创建项目,则直接有 <code>app/</code> 目录。</p>\n<h3 id=\"src-common\">src/common</h3>\n<p>通用模块目录,项目目录都是按模块来划分的,<code>common</code> 模块下存放一些通用的处理逻辑。</p>\n<h3 id=\"src-common-bootstrap\">src/common/bootstrap</h3>\n<p>项目启动目录,该目录下的文件会自动加载,无需手动 <code>require</code> 。</p>\n<p>可以在这个目录下文件里定义一些全局函数、注册中间件等常用的功能。</p>\n<h5 id=\"-\">定义全局函数</h5>\n<pre><code class=\"lang-js\">// src/common/bootstrap/fn.js\nglobal.formatDate = obj => {\n ...\n}\n</code></pre>\n<p>这里定义了一个全局函数 <code>formatDate</code>,那么项目里任何地方都可以直接使用该函数。</p>\n<h5 id=\"-\">注册中间件</h5>\n<pre><code class=\"lang-js\">// src/common/bootstrap/middleware.js\nthink.middleware(\'replace_image\', http => {\n ...\n});\n</code></pre>\n<p>这里定义了一个中间件 <code>replace_image</code>,那么就可以在配置文件 <code>hook.js</code> 里将该中间件注册进去了。</p>\n<p><code>注</code>:bootstrap 只能放在 common 模块里。</p>\n<h3 id=\"src-common-config\">src/common/config</h3>\n<p>配置文件,这里放一些通用的配置。</p>\n<p>其中:路由配置、hook 配置、本地化配置等必须放在这里。</p>\n<pre><code class=\"lang-js\">\'use strict\';\n/**\n * config\n */\nexport default {\n //key: value\n};\n</code></pre>\n<h3 id=\"src-common-controller\">src/common/controller</h3>\n<p>控制器,放一些通用的控制器。其中 <code>error.js</code> 里错误处理的不同行为,项目里可以根据需要进行修改。</p>\n<h3 id=\"src-common-runtime\">src/common/runtime</h3>\n<p>项目运行时生成的一些目录,如:缓存文件目录,用户上传的文件临时存放的目录。</p>\n<h3 id=\"src-home\">src/home</h3>\n<p><code>home</code> 模块,项目默认模块。可以在 <code>src/common/config/config.js</code> 中修改配置 <code>default_module</code> 来重新定义默认模块。</p>\n<h3 id=\"src-home-logic\">src/home/logic</h3>\n<p>逻辑处理。每个操作执行前可以先进行逻辑校验,可以包含:参数是否合法、提交的数据是否正常、当前用户是否已经登录、当前用户是否有权限等。这样可以降低 <code>controller</code> 里的 <code>action</code> 的复杂度。</p>\n<pre><code class=\"lang-js\">\'use strict\';\n/**\n * logic\n * @param {} []\n * @return {} []\n */\nexport default class extends think.logic.base {\n /**\n * index action logic\n * @return {} []\n */\n indexAction(){\n\n }\n}\n</code></pre>\n<h3 id=\"src-home-controller\">src/home/controller</h3>\n<p>控制器。一个 <code>url</code> 对应一个 <code>controller</code> 下的 <code>action</code>。</p>\n<pre><code class=\"lang-js\">\'use strict\';\n\nimport Base from \'./base.js\';\n\nexport default class extends Base {\n /**\n * index action\n * @return {Promise} []\n */\n indexAction(){\n //auto render template file index_index.html\n return this.display();\n }\n}\n</code></pre>\n<h3 id=\"src-home-model\">src/home/model</h3>\n<p>模型。数据库相关操作。</p>\n<h3 id=\"view\">view</h3>\n<p>视图目录,存放对应的模版文件。如果支持国际化和多主题,那么视图目录下需要有对应的子目录。</p>\n<h3 id=\"www\">www</h3>\n<p>项目的可访问根目录,nginx 里的根目录会配置到此目录下。</p>\n<h3 id=\"www-development-js\">www/development.js</h3>\n<p>开发模式下项目的入口文件,可以根据项目需要进行修改。<code>www/production.js</code> 为线上的入口文件。</p>\n<p>入口文件的代码类似如下,可以根据项目需要进行修改。</p>\n<pre><code class=\"lang-js\">var thinkjs = require(\'thinkjs\');\nvar path = require(\'path\');\n\nvar rootPath = path.dirname(__dirname);\n\nvar instance = new thinkjs({\n APP_PATH: rootPath + \'/app\',\n ROOT_PATH: rootPath,\n RESOURCE_PATH: __dirname,\n env: \'development\'\n});\n\ninstance.compile({retainLines: true, log: true});\n\ninstance.run();\n</code></pre>\n<h3 id=\"www-static\">www/static</h3>\n<p>存放一些静态资源文件。</p><p><br></p>', '', '前端汇', '2016-07-04 14:41:59', '67', '0', '0', '0', '18', 'thinkjs,nodejs', '0', '1', 'https://thinkjs.org/', '8', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('233', '代码规范', '', '<h3 id=\"-\">文件路径必须小写</h3>\n<p>很多时候是在 <code>Windows</code> 或者 <code>Mac OSX</code> 系统下开发项目,但一般都部署 <code>Linux</code> 系统下。</p>\n<p>在 <code>Windows</code> 和 <code>Mac</code> 系统下,文件路径是不区分大小写的,而 <code>Linux</code> 下是区分大小写的。这样很容易出现文件大小写的问题导致开发环境下是好的,但上线后却报错了。</p>\n<p>为了避免这种情况的发生,文件路径尽量都使用小写字符。并且在服务启动时,ThinkJS 会检测项目下文件路径,如果有大写字母则会告警,如:</p>\n<pre><code class=\"lang-text\">[2015-10-13 10:36:59] [WARNING] filepath `admin/controller/apiBase.js` has uppercases.\n</code></pre>\n<h3 id=\"-2-\">缩进使用 2 个空格</h3>\n<p>在 Node.js 环境下开发,有时候逻辑比较复杂,有各种条件判断,或者有一些异步操作,这些都会增加代码的缩进。</p>\n<p>为了不至于让缩进占用了太多的列宽,建议使用 2 个空格作为缩进。</p>\n<h3 id=\"-es6-\">使用 ES6 语法开发</h3>\n<p>ES6 中有大量的语法糖可以简化我们的代码,让代码更加简洁高效。<br>Node.js 最新版本已经较好的支持了 ES6 的语法,即使有些语法不支持,也可以通过 Babel 编译来支持。 所以是时候使用 ES6 语法来开发项目了。</p>\n<h3 id=\"-constrcutor-\">不要使用 constrcutor 方法</h3>\n<p>使用 ES6 里的 class 来创建类的时候,可以使用 <code>constrcutor</code> 方法达到类实例化的时候自动调用。如:</p>\n<pre><code class=\"lang-js\">export default class think.base {\n constructor(){\n ...\n }\n}\n</code></pre>\n<p>但如果不使用 ES6 里的 class,就没有 constrcutor 方法了。</p>\n<p>为了统一处理,ThinkJS 提供了 <code>init</code> 方法来代替 <code>constrcutor</code> 方法,该方法不管是在 class 下还是动态创建类的情况下都可以做到类实例化的时候自动被调用。</p>\n<pre><code class=\"lang-js\">export default class think.base {\n /**\n * 初始化方法,类实例化时自动被调用\n * @return {} []\n */\n init(){\n ...\n }\n}\n</code></pre>\n<p><code>注</code>:ThinkJS 里所有的类都会继承 <code>think.base</code> 基类。</p>\n<h3 id=\"-babel-\">使用 Babel 编译</h3>\n<p>虽然现在的 Node.js 版本已经支持了很多 ES6 的特性,但这些特性现在还只是实现了,V8 里还没有对这些特性进行优化。如:<code>*/yield</code> 等功能。</p>\n<p>所以建议使用 Babel 来编译,一方面可以使用 ES6 和 ES7 几乎所有的特性,另一方面编译后的性能也比默认支持的要高。</p>\n<h3 id=\"-async-await-yield\">使用 async/await 替代 */yield</h3>\n<p><code>*/yield</code> 是 ES6 里提出一种解决异步执行的方法,它只是一个过渡的方案,ES7 里便提出了 <code>async/await</code> 来代替它。</p>\n<p>相对 <code>async/await</code>,<code>*/yield</code> 有以下的缺陷:</p>\n<p>1、<code>*/yield</code> 调用后返回一个迭代器,需要借助第三方模块来执行。如:<code>co</code></p>\n<p>2、<code>*/yield</code> 无法和 Arrow Function 一起使用。</p>\n<p>3、<code>*/yield</code> 调用另一个 <code>*/yield</code> 时,需要使用 <code>yield *</code>,带来不便。</p>\n<p>4、目前 V8 对 <code>*/yield</code> 还没有做优化,最好也通过 Babel 来编译。</p>\n<p>所以完全可以使用 ES7 里的 <code>async/await</code> 来代替 <code>*/yield</code>,然后使用 Babel 编译来运行。</p><p><br></p>', '', '前端汇', '2016-07-04 14:42:07', '75', '0', '0', '0', '18', 'thinkjs,nodejs', '1', '1', 'https://thinkjs.org/', '8', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('234', '升级指南', '', '<p>本文档为 2.0 到 2.1 的升级指南,1.x 到 2.0 升级指南请见<a href=\"/doc/2.0/upgrade.html\">这里</a>。 </p>\n<p>2.1 版本兼容 2.0 版本,只是添加了很多功能和微调了一些东西,具体的修改列表请见 <a href=\"/changelog.html\">ChangeLog</a>。</p>\n<h3 id=\"2-0-2-1\">2.0 升级到 2.1</h3>\n<h4 id=\"-thinkjs-\">升级依赖的 ThinkJS 版本</h4>\n<p>将 <code>package.json</code> 里依赖的 ThinkJS 版本修改为 <code>2.1.x</code>。</p>\n<h4 id=\"babel-6\">Babel 升级到 6</h4>\n<p>ThinkJS 2.0 是基于 Babel 5 编译的,2.1 版本将依赖的 Babel 升级到 6,所以需要修改 Babel 相关的依赖。</p>\n<p>可以删除 <code>package.json</code> 里相关的 Babel 依赖,并添加如下的依赖:</p>\n<pre><code class=\"lang-js\"> \"dependencies\": {\n \"babel-runtime\": \"6.x.x\"\n },\n \"devDependencies\": {\n \"babel-cli\": \"6.x.x\",\n \"babel-preset-es2015-loose\": \"6.x.x\",\n \"babel-preset-stage-1\": \"6.x.x\",\n \"babel-plugin-transform-runtime\": \"6.x.x\",\n \"babel-core\": \"6.x.x\"\n }\n</code></pre>\n<p>修改完成后,执行 <code>npm install</code> 安装对应的依赖,删除 <code>app/</code> 目录,执行 <code>npm start</code> 启动项目。</p>\n<h4 id=\"-compile-\">修改 compile 命令</h4>\n<p>将 <code>package.json</code> 里原有的 compile 命令修改为 <code>babel --presets es2015-loose,stage-1 --plugins transform-runtime src/ --out-dir app/ --retain-lines</code>。</p>\n<h4 id=\"-runtime_path\">添加 RUNTIME_PATH</h4>\n<p>修改 <code>www/development.js</code>,<code>www/testing.js</code> 和 <code>www/production.js</code> 3 个文件,在实例化的时候添加 <code>RUNTIME_PATH</code> 的定义,如:</p>\n<pre><code class=\"lang-js\">var instance = new thinkjs({\n APP_PATH: rootPath + path.sep + \'app\',\n RUNTIME_PATH: rootPath + path.sep + \'runtime\', //添加 RUNTIME_PATH 路径的定义\n ROOT_PATH: rootPath,\n RESOURCE_PATH: __dirname,\n env: \'development\'\n});\n</code></pre>\n<p>其中 <code>RUNTIME_PATH: rootPath + path.sep + \"runtime\"</code> 即为需要添加的代码。</p>\n<h3 id=\"-typescript\">项目升级为 TypeScript</h3>\n<p>项目升级为 TypeScript 请见<a href=\"./typescript.html#toc-600\">这里</a>。</p><p><br></p>', '', '前端汇', '2016-07-04 14:44:48', '73', '0', '0', '0', '18', 'thinkjs,nodejs', '1', '1', 'https://thinkjs.org/', '8', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('235', '常见问题', '', '<h3 id=\"-es6-7-\">为什么推荐 ES6/7 语法开发项目</h3>\n<p>ES6/7 里提供了大量的新特性,这些特性会带来巨大的开发便利和效率上的提升。如:ES6 里的 <code>*/yield</code> 和 ES7 里的 <code>async/await</code> 特性解决异步回调的问题;箭头函数解决 <code>this</code> 作用域的问题;<code>class</code> 语法糖解决类继承的问题。</p>\n<p>虽然现在 Node.js 环境还没有完全支持这些新的特性,但借助 Babel 编译,可以稳定运行在现在的 Node.js 环境中。所以我们尽可以享受这些新特性带来的便利。 </p>\n<h3 id=\"-\">开发时,修改文件需要重启服务么?</h3>\n<p>默认情况下,由于 Node.js 的机制,文件修改必须重启才能生效。</p>\n<p>这种方式下给开发带来了很大的不变,ThinkJS 提供了一种文件自动更新的机制,文件修改后可以立即生效,无需重启服务。</p>\n<p>自动更新的机制会消耗一定的性能,所以默认只在 <code>development</code> 项目环境下开启。线上代码更新还是建议使用 <code>pm2</code> 模块来管理。</p>\n<h3 id=\"-\">怎么修改视图文件目录结构</h3>\n<p>默认情况下,视图文件路径为 <code>view/[module]/[controller]_[action].html</code>。其中控制器和操作之间是用 <code>_</code> 来连接的,如果想将连接符修改为 <code>/</code>,可以修改配置文件 <code>src/common/config/view.js</code>:</p>\n<pre><code class=\"lang-js\">export default {\n file_depr: \'/\', //将控制器和操作之间的连接符修改为 /\n}\n</code></pre>\n<h3 id=\"-cluster\">如何开启 cluster</h3>\n<p>线上可以开启 cluster 功能达到利用多核 CPU 来提升性能,提高并发处理能力。</p>\n<p>可以在配置文件 <code>src/common/config/env/production.js</code> 中加入如下的配置:</p>\n<pre><code class=\"lang-js\">export default {\n cluster_on: true //开启 cluster\n}\n</code></pre>\n<p><code>注</code>:如果使用 PM2 管理服务且开启了 cluster,那么 ThinkJS 里就无需再开启 cluster 了。</p>\n<h3 id=\"-\">修改请求超时时间</h3>\n<p>默认请求的超时时间是 120s,可以通过修改配置文件 <code>src/common/config/config.js</code> 里 <code>timeout</code> 配置值。</p>\n<pre><code class=\"lang-js\">export default {\n timeout: 30, //将超时时间修改为 30s\n}\n</code></pre>\n<h3 id=\"-\">如何捕获异常</h3>\n<p>JS 本身是无法通过 try/catch 来捕获异步异常的,但使用 async/await 后则可以通过 try/catch 来捕获异常,如:</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n async indexAction(){\n try{ \n await this.getFromAPI1();\n await this.getFromAPI2();\n await this.getFromAPI3();\n }catch(err){\n //通过 err.message 拿到具体的错误信息\n return this.fail(err.message);\n }\n }\n}\n</code></pre>\n<p>上面的方式虽然可以通过 try/catch 来捕获异常,但在 catch 里并不知道异常是哪个触发的。</p>\n<p>实际项目中,经常要根据不同的错误返回不同的错误信息给用户,这时用整体的 try/catch 就不太方便了。</p>\n<p>此时可以通过单个异步接口返回特定值来判断,如:</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n async indexAction(){\n //忽略该接口的错误(该接口的错误不重要,可以忽略)\n await this.getFromAPI1().catch(() => {});\n //异常时返回特定值 false 来判断\n let result = await this.getFromAPI2().catch(() => false);\n if(result === false){\n return this.fail(\'API2 ERROR\');\n }\n }\n}\n</code></pre>\n<p>如上面代码所述,通过返回特定值判断就可以方便的知道是哪个异步接口发生了错误,这样就可以针对不同的错误返回不同的错误信息。</p>\n<h3 id=\"-\">如何忽略异常</h3>\n<p>使用 async/await 时,如果 Promise 返回了一个 rejected Promise,那么会抛出异常。如果这个异常不重要需要忽略的话,可以通过 catch 方法返回一个 resolve Promise 来完成。如:</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n async indexAction(){\n //通过在 catch 里返回 undefined 来忽略异常\n await this.getAPI().catch(() => {});\n }\n}\n</code></pre>\n<h3 id=\"prevent_next_process\">PREVENT_NEXT_PROCESS</h3>\n<p>在调用有些方法后(如:success)后会发现有个 message 为 <code>PREVENT_NEXT_PROCESS</code> 的错误。这个错误是 ThinkJS 为了阻止后续执行添加的,如果要在 <code>catch</code> 里判断是否是该错误,可以通过 <code>think.isPrevent</code> 方法来判断。如:</p>\n<pre><code class=\"lang-js\">module.exports = think.controller({\n indexAction(self){\n return self.getData().then(function(data){\n return self.success(data);\n }).catch(function(err){\n //忽略 PREVENT_NEXT_PROCESS 错误\n if(think.isPrevent(err)){\n return;\n }\n console.log(err.stack);\n })\n }\n})\n</code></pre>\n<p>另一种处理方式:对于 <code>success</code> 之类的方法前面不要添加 <code>return</code>,这样 <code>catch</code> 里就不会有此类的错误了。</p>\n<h3 id=\"-\">并行处理</h3>\n<p>使用 <code>async/await</code> 来处理异步时,是串行执行的。但很多场景下我们需要并行处理,这样可以大大提高执行效率,此时可以结合 <code>Promise.all</code> 来处理。</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n async indexAction(){\n let p1 = this.getServiceData1();\n let p2 = this.getAPIData2();\n let [p1Data, p2Data] = await Promise.all([p1, p2]);\n }\n}\n</code></pre>\n<p>上面的代码 <code>p1</code> 和 <code>p2</code> 是并行处理的,然后用 <code>Promise.all</code> 来获取 2 个数据。这样一方面代码是同步书写的,同时又不失并行处理的性能。</p>\n<h3 id=\"-\">如何输出图片</h3>\n<p>项目中有时候要输出图片等类型的数据,可以通过下面的方式进行:</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n imageAction(){\n //图片 buffer 数据,读取本地文件或者从远程获取\n let imageBuffer = new Buffer();\n this.type(\'image/png\');\n this.end(imageBuffer);\n }\n}\n</code></pre>\n<h3 id=\"-\">如何在不同的环境下使用不同的配置</h3>\n<p>我们经常在不同的环境下使用不同的配置,如:开发环境和线上环境使用不同的数据库配置。这时可以通过 <code>src/common/config/env/[env].js</code> 来配置,<code>[env]</code> 默认有 <code>development</code>,<code>testing</code> 和 <code>production</code> 3 个值,分别对应开发环境、测试环境和线上环境。这时可以在对应的配置文件设定配置来用在不同的环境下。</p>\n<p>如:配置线上环境下的数据库,那么可以在 <code>src/common/config/env/production.js</code> 中配置:</p>\n<pre><code class=\"lang-js\">export default {\n db: { //这里要有一级 db\n type: \'mysql\',\n adapter: {\n mysql: {\n host: \'\',\n port: \'\'\n }\n }\n }\n}\n</code></pre>\n<p>详细的数据库配置请见<a href=\"./config.html#db\">这里</a>。</p>\n<h3 id=\"nunjucks-\">nunjucks 模板继承路径怎么写</h3>\n<p>使用 nunjucks 的模板继承时,由于设置了 root_path,所以路径需要使用相对路径。如:</p>\n<pre><code class=\"lang-html\">{% extends \"./parent.html\" %} //表示同级别目录下的 parent.html 文件\n{% extends \"../layout.html\" %} //表示父级别下的 layout.html 文件\n</code></pre>\n<h3 id=\"-action-\">如何让 Action 只允许命令行调用</h3>\n<p>默认情况下,Action 既可以用户访问,也可以命令行调用。但有些 Action 我们希望只在命令行下调用,这时可以通过 <code>isCli</code> 来判断。如:</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n indexAction(){\n //禁止 URL 访问该 Action\n if(!this.isCli()){\n this.fail(\'only allow invoked in cli mode\');\n }\n ...\n }\n}\n</code></pre>\n<h3 id=\"-\">如何跨模块调用</h3>\n<p>当项目比较复杂时,会有一些跨模块调用的需求。</p>\n<h4 id=\"-controller\">调用 controller</h4>\n<p>可以通过 <code>this.controller</code> 方法传递第二个参数模块名达到调用其他模块下 controller 的功能,如:</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n indexAction(){\n //获取 admin 模块下 user controller 的实例\n let controllerInstance = this.controller(\'user\', \'admin\');\n //获取 controller 的实例下就可以调用下面的方法了\n let bar = controllerInstance.foo();\n }\n index2Action(){\n //也可以通过这种更简洁的方式获取\n let controllerInstance = this.controller(\'admin/user\');\n let bar = controllerInstance.foo();\n }\n}\n</code></pre>\n<h4 id=\"-action\">调用 action</h4>\n<p>可以通过 <code>this.action</code> 方法调用其他模块里 controller 下的 action 方法,如:</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n async indexAction(){\n //获取 admin 模块下 user controller 的实例\n let controllerInstance = this.controller(\'user\', \'admin\');\n //调用 controller 里的 test action,会自动调用 __before 和 __after 魔术方法\n let data = await this.action(controllerInstance, \'test\')\n }\n async index2Action(){\n //也可以通过字符串来指定 controller,这样会自动找对应的 controller\n let data = await this.action(\'admin/user\', \'test\')\n }\n}\n</code></pre>\n<p><code>注</code>:action 调用返回的始终为 Promise,调用 action 时不会调用对应的 logic。</p>\n<h4 id=\"-model\">调用 model</h4>\n<p>可以通过 <code>this.model</code> 方法获取其他模块下的 model 实例,如:</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n indexAction(){\n //获取 admin 模块下的 user model 实例\n let modelInstance1 = this.model(\'user\', {}, \'admin\');\n //也可以通过这种更简洁的方式\n let modelInstance2 = this.model(\'admin/user\');\n }\n}\n</code></pre>\n<h3 id=\"-\">如何请求其他接口数据</h3>\n<p>在项目中,经常要请求其他接口的数据。这时候可以用内置的 <code>http</code> 模块来操作,但 <code>http</code> 模块提供的接口比较基础,写起来比较麻烦。推荐大家用基于 <code>http</code> 模块封装的 <code>request</code> 模块或者 <code>superagent</code> 模块。如:</p>\n<pre><code class=\"lang-js\">import request from \'request\';\n/* 获取 API 接口数据 */\nlet getApiData = () => {\n let deferred = think.defer();\n request.get({\n url: \'http://www.example.com/api/user\',\n headers: {\n \'User-Agent\': \'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_2) Chrome/47.0.2526.111 Safari/537.36\'\n }\n }, (err, response, body) => {\n if(err){\n deferred.reject(err);\n }else{\n deferred.resolve(body);\n }\n });\n}\n</code></pre>\n<p>但这么写需要创建一个 deferred 对象,然后在回调函数里去根据 err 进行 resolve 或者 reject,写起来有些麻烦。ThinkJS 里提供了 <code>think.promisify</code> 方法来快速处理这一问题。</p>\n<pre><code class=\"lang-js\">import request from \'request\';\n/* 获取 API 接口数据 */\nlet getApiData = () => {\n let fn = think.promisify(request.get);\n return fn({\n url: \'http://www.example.com/api/user\',\n headers: {\n \'User-Agent\': \'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_2) Chrome/47.0.2526.111 Safari/537.36\'\n }\n });\n}\n</code></pre>\n<h3 id=\"-502\">开发环境好的,线上部署 502</h3>\n<p>有时候开发环境下是好的,到线上使用 pm2 和 nginx 部署时,访问出现 502 的情况,这个情况一般为 node 服务没有正常启动导致的。可以通过 <code>pm2 logs</code> 看对应的错误信息来分析排查,也可以先关闭服务,手动通过 <code>node www/production.js</code> 启动服务,然后访问看具体的错误信息。</p>\n<h3 id=\"-\">设置跨域头信息</h3>\n<p>高级浏览器支持通过设置头信息达到跨域请求,ThinkJS 里可以通过下面的方式来设置:</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n indexAction(){\n let method = this.http.method.toLowerCase();\n if(method === \'options\'){\n this.setCorsHeader();\n this.end();\n return;\n }\n this.setCorsHeader();\n this.success();\n }\n setCorsHeader(){\n this.header(\'Access-Control-Allow-Origin\', this.header(\'origin\') || \'*\');\n this.header(\'Access-Control-Allow-Headers\', \'x-requested-with\');\n this.header(\'Access-Control-Request-Method\', \'GET,POST,PUT,DELETE\');\n this.header(\'Access-Control-Allow-Credentials\', \'true\');\n }\n}\n</code></pre>\n<p>更多头信息设置请见 <a href=\"https://www.w3.org/TR/cors\">https://www.w3.org/TR/cors</a>。</p>\n<p>如果是在 REST API,那么可以放在 __call 方法里判断,如:</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n __call(){\n let method = this.http.method.toLowerCase();\n if(method === \'options\'){\n this.setCorsHeader();\n this.end();\n return;\n }\n this.setCorsHeader();\n return super.__call();\n }\n setCorsHeader(){\n this.header(\'Access-Control-Allow-Origin\', this.header(\'origin\') || \'*\');\n this.header(\'Access-Control-Allow-Headers\', \'x-requested-with\');\n this.header(\'Access-Control-Request-Method\', \'GET,POST,PUT,DELETE\');\n this.header(\'Access-Control-Allow-Credentials\', \'true\');\n }\n}\n</code></pre><p><br></p>', '', '前端汇', '2016-07-04 14:44:39', '153', '0', '0', '0', '18', 'thinkjs,nodejs', '0', '1', 'https://thinkjs.org/', '8', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('236', '模块', '', '<p>ThinkJS 创建项目时支持多种项目模式,默认创建的项目是按模块来划分的,并且自动添加了 <code>common</code> 和 <code>home</code> 2 个模块。每个模块有独立的配置、控制器、视图、模型等文件。</p>\n<p>使用模块的方式划分项目,可以让项目结构更加清晰。如:一般一个博客系统可分为前后台 2 个模块。</p>\n<h3 id=\"-\">模块列表</h3>\n<p>进去 <code>src/</code> 目录就可以看到模块列表:</p>\n<pre><code class=\"lang-text\">drwxr-xr-x 5 welefen staff 170 Aug 18 15:55 common/\ndrwxr-xr-x 6 welefen staff 204 Sep 8 19:14 home/\n</code></pre>\n<h3 id=\"common-\">common 模块</h3>\n<p>common 模块是个通用模块,该模块下存放一些通用的功能,如: 通用的配置,runtime 目录,启动文件,错误处理控制器等。</p>\n<p><code>注</code>:该模块下的控制器不能响应用户的请求。</p>\n<h3 id=\"-\">默认模块</h3>\n<p>默认模块为 <code>home</code> 模块。当解析用户的请求找不到模块时会自动对应到 <code>home</code> 下。</p>\n<p>可以通过配置 <code>default_module</code> 来修改默认模块,修改配置文件 <code>src/common/config/config.js</code>:</p>\n<pre><code class=\"lang-js\">//将默认模块名改为 blog\nexport default {\n default_module: \'blog\'\n}\n</code></pre>\n<h3 id=\"-\">添加模块</h3>\n<p>添加模块直接通过 <code>thinkjs</code> 命令即可完成。</p>\n<p>在当前项目目录下,执行 <code>thinkjs module xxx</code>,即可创建名为 <code>xxx</code> 的模块。</p>\n<p>如果模块名已经存在,则无法创建。</p>\n<h3 id=\"-\">禁用模块</h3>\n<p>ThinkJS 默认会自动查找和识别项目下的模块,并认为所有的模块都是可用的。</p>\n<p>如果想禁用部分模块,可以修改配置文件 <code>src/common/config/config.js</code>,添加下面的配置:</p>\n<pre><code class=\"lang-js\">export default {\n deny_module_list: [\'xxx\'] //禁用 xxx 模块\n}\n</code></pre><p> 文章来源:<a href=\"http://www.thinkjs.org\" target=\"_blank\">http://www.thinkjs.org</a></p><p><br></p>', '', '前端汇', '2016-07-11 19:21:31', '56', '0', '0', '0', '18', 'thinkjs,nodejs', '0', '1', '', '8', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('237', '控制器', '', '<p>控制器是一类操作的集合,用来响应用户同一类的请求。 文章来源:http://www.thinkjs.org</p>\n<h3 id=\"-\">定义控制器</h3>\n<p>创建文件 <code>src/home/controller/article.js</code>,表示 <code>home</code> 模块下有名为 <code>article</code> 控制器,文件内容类似如下:</p>\n<pre><code class=\"lang-js\">\'use strict\';\n\nimport Base from \'./base.js\';\n\nexport default class extends Base {\n /**\n * index action\n * @return {Promise} []\n */\n indexAction(){\n //auto render template file index_index.html\n return this.display();\n }\n}\n</code></pre>\n<p>如果不想使用 ES6 语法,那么文件内容类似如下:</p>\n<pre><code class=\"lang-js\">\'use strict\';\n\nvar Base = require(\'./base.js\');\n\nmodule.exports = think.controller(Base, {\n /**\n * index action\n * @return {Promise} []\n */\n indexAction: function(self){\n //auto render template file index_index.html\n return self.display();\n }\n});\n</code></pre>\n<p>注:上面的 <code>Base</code> 表示定义一个基类,其他的类都继承该基类,这样就可以在基类里做一些通用的处理。</p>\n<h3 id=\"-\">多级控制器</h3>\n<p>对于很复杂的项目,一层控制器有时候不能满足需求。这个时候可以创建多级控制器,如:<code>src/home/controller/group/article.js</code>,这时解析到的控制器为二级,具体为 <code>group/article</code>,Logic 和 View 的目录与此相同。</p>\n<h3 id=\"-async-await\">使用 async/await</h3>\n<p>借助 Babel 编译,还可以在控制器里使用 ES7 里的 <code>async/await</code>。</p>\n<h5 id=\"es7-\">ES7 方式</h5>\n<pre><code class=\"lang-js\">\'use strict\';\n\nimport Base from \'./base.js\';\n\nexport default class extends Base {\n /**\n * index action\n * @return {Promise} []\n */\n async indexAction(){\n let model = this.model(\'user\');\n let data = await model.select();\n return this.success(data);\n }\n}\n</code></pre>\n<h5 id=\"-\">动态创建类的方式</h5>\n<pre><code class=\"lang-js\">\'use strict\';\n\nvar Base = require(\'./base.js\');\n\nmodule.exports = think.controller(Base, {\n /**\n * index action\n * @return {Promise} []\n */\n indexAction: async function(){\n var model = this.model(\'user\');\n var data = await model.select();\n return this.success(data);\n }\n});\n</code></pre>\n<h3 id=\"init-\">init 方法</h3>\n<p>ES6 里的 class 有 contructor 方法,但动态创建的类就没有该方法了,为了统一初始化执行的方法,将该方法统一定义为 <code>init</code>。</p>\n<p>该方法在类实例化的时候自动调用,无需手工调用。</p>\n<h5 id=\"es6-\">ES6 方式</h5>\n\n<pre><code class=\"lang-js\">\'use strict\';\n\nimport Base from \'./base.js\';\n\nexport default class extends Base {\n init(http){\n super.init(http); //调用父类的init方法 \n ...\n }\n}\n</code></pre>\n<h5 id=\"-\">动态创建类方式</h5>\n<pre><code class=\"lang-js\">\'use strict\';\n\nvar Base = require(\'./base.js\');\n\nmodule.exports = think.controller(Base, {\n init: function(http){\n this.super(\'init\', http); //调用父类的init方法\n ...\n }\n});\n</code></pre>\n<p><code>init</code> 方法里需要调用父类的 init 方法,并将参数 <code>http</code> 传递进去。</p>\n<h3 id=\"-__before\">前置操作 __before</h3>\n<p>ThinkJS 支持前置操作,方法名为 <code>__before</code>,该方法会在具体的 Action 调用之前自动调用。如果前置操作里阻止了后续代码继续执行,则不会调用具体的 Action,这样可以提前结束请求。</p>\n<h5 id=\"es6-\">ES6 方式</h5>\n\n<pre><code class=\"lang-js\">\'use strict\';\n\nimport Base from \'./base.js\';\n\nexport default class extends Base {\n /**\n * 前置方法\n * @return {Promise} []\n */\n __before(){\n ...\n }\n}\n</code></pre>\n<h3 id=\"action\">Action</h3>\n<p>一个 Action 代表一个要执行的操作。如: url 为 <code>/home/article/detail</code>,解析后的模块为 <code>/home</code>,控制器为 <code>article</code>, Action 为 <code>detail</code>,那么执行的 Action 就是文件 <code>src/home/controller/aritcle</code> 里的 <code>detailAction</code> 方法。</p>\n<pre><code class=\"lang-js\">\'use strict\';\n\nimport Base from \'./base.js\';\n\nexport default class extends Base {\n /**\n * 获取详细信息\n * @return {Promise} []\n */\n detailAction(self){\n ...\n }\n}\n</code></pre>\n<p>如果解析后的 Action 值里含有 <code>_</code>,会自动做转化,具体的转化策略见 <a href=\"./route.html\">路由 -> 大小写转化</a>。</p>\n<h3 id=\"-__after\">后置操作 __after</h3>\n<p>ThinkJS 支持后置操作,方法名为 <code>__after</code>,该方法会在具体的 Action 调用之后执行。如果具体的 Action 里阻止了后续的代码继续执行,则后置操作不会调用。</p>\n<h3 id=\"-__call\">空操作 __call</h3>\n<p>当解析后的 url 对应的控制器存在,但 Action 不存在时,会试图调用控制器下的魔术方法 <code>__call</code>。这里可以对不存在的方法进行统一处理。</p>\n<pre><code class=\"lang-js\">\'use strict\';\n\nimport Base from \'./base.js\';\n\nexport default class extends Base {\n /**\n * @return {Promise} []\n */\n __call(){\n ...\n }\n}\n</code></pre>\n<h3 id=\"-\">错误处理</h3>\n<p>当 url 不存在或者当前用户没权限等一些异常请求时,这时候会调用错误处理。 ThinkJS 内置了一套详细的错误处理机制,具体请见 <a href=\"./error_handle.html\">扩展功能 -> 错误处理</a>。</p>\n<h3 id=\"-\">数据校验</h3>\n<p>控制器里在使用用户提交的数据之前,需要对数据合法性进行校验。为了降低控制器里的逻辑复杂度,ThinkJS 提供了一层 Logic 专门用来处理数据校验和权限校验等相关操作。</p>\n<p>详细信息请见 <a href=\"./logic.html#toc-920\">扩展功能 -> Logic -> 数据校验</a>。</p>\n<h3 id=\"-\">变量赋值和模版渲染</h3>\n<p>控制器里可以通过 <code>assign</code> 和 <code>display</code> 方法进行变量赋值和模版渲染,具体信息请见 <a href=\"./view.html\">这里</a>。</p>\n<h3 id=\"-\">模型实例化</h3>\n<p>在控制器中可以通过 <code>this.model</code> 方法快速获得一个模型的实例。</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n indexAction(){\n let model = this.model(\'user\'); //实例化模型 user\n ...\n }\n}\n</code></pre>\n<p>model 方法更多使用方式请见 <a href=\"./api_think_http_base.html#toc-e2b\">API -> think.http.base</a>。</p>\n<h3 id=\"http-\">http 对象</h3>\n<p>控制器在实例化时,会将 <code>http</code> 传递进去。该 <code>http</code> 对象是 ThinkJS 对 <code>req</code> 和 <code>res</code> 重新包装的一个对象,而非 Node.js 内置的 http 对象。</p>\n<p>Action 里如果想获取该对象,可以通过 <code>this.http</code> 来获取。</p>\n<pre><code class=\"lang-js\">\'use strict\';\n\nimport Base from \'./base.js\';\n\nexport default class extends Base {\n indexAction(){\n let http = this.http;\n }\n}\n</code></pre>\n<p>关于 <code>http</code> 对象包含的属性和方法请见 <a href=\"./api_http.html\">API -> http</a>。</p>\n<h3 id=\"rest-api\">REST API</h3>\n<p>有时候,项目里需要提供一些 REST 接口给第三方使用,这些接口无外乎就是增删改查等操作。</p>\n<p>如果手工去书写这些操作则比较麻烦,ThinkJS 提供了 REST Controller,该控制器会自动含有通用的增删改查等操作。如果这些操作不满足需求,也可以进行定制。具体请见 <a href=\"./rest_api.html\">这里</a>。</p>\n<h3 id=\"this-\">this 作用域的问题</h3>\n<p>Node.js 里经常有很多异步操作,而异步操作常见的处理方式是使用回调函数或者 Promise。这些处理方式都会增加一层作用域,导致在回调函数内无法直接使用 <code>this</code>,简单的处理办法是在顶部定义一个变量,将 <code>this</code> 赋值给这个变量,然后在回调函数内使用这个变量。如:</p>\n<pre><code class=\"lang-js\">module.exports = think.controller({\n indexAction: function(){\n var self = this; //这里将 this 赋值给变量 self,然后在后面的回调函数里都使用 self\n this.model(\'user\').find().then(function(data){\n return self.model(\'article\').where({user_id: data.id}).select();\n }).then(function(data){\n self.success(data);\n })\n }\n})\n</code></pre>\n<p>如果每个 Action 里都要使用者手工写一个 <code>var self = this</code>,势必比较麻烦。为了解决这个问题,ThinkJS 在 Action 里直接提供了一个参数,这个参数等同于 <code>var self = this</code>,具体如下:</p>\n<pre><code class=\"lang-js\">module.exports = think.controller({\n //参数 self 等同于 var self = this\n indexAction: function(self){\n this.model(\'user\').find().then(function(data){\n return self.model(\'article\').where({user_id: data.id}).select();\n }).then(function(data){\n self.success(data);\n })\n }\n})\n</code></pre>\n<p>当然更好的解决办法是推荐使用 ES6 里的 Generator Function 和 Arrow Function,这样就可以彻底解决 this 作用域的问题。</p>\n<h5 id=\"-generator-function\">使用 Generator Function</h5>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n * indexAction(){\n let data = yield this.model(\'user\').find();\n let result = yield this.model(\'article\').where({user_id: data.id}).select();\n this.success(result);\n }\n}\n</code></pre>\n<h5 id=\"-arrow-function\">使用 Arrow Function</h5>\n<pre><code class=\"lang-js\">module.exports = think.controller({\n indexAction: function(){\n this.model(\'user\').find().then(data => {\n return this.model(\'article\').where({user_id: data.id}).select();\n }).then(data => {\n this.success(data);\n })\n }\n})\n</code></pre>\n<h3 id=\"json-\">JSON 输出</h3>\n<p>项目中经常要提供一些接口,这些接口一般都是直接输出 JSON 格式的数据,并且会有标识表明当前接口是否正常。如果发生异常,需要将对应的错误信息随着接口一起输出。控制器里提供了 <code>this.success</code> 和 <code>this.fail</code> 方法来输出这样的接口数据。</p>\n<h4 id=\"-json\">输出正常的 JSON</h4>\n<p>可以通过 <code>this.success</code> 方法输出正常的接口数据,如:</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n indexAction(){\n let data = {name: \"thinkjs\"};\n this.success(data);\n }\n}\n</code></pre>\n<p>输出结果为 <code>{errno: 0, errmsg: \"\", data: {\"name\": \"thinkjs\"}}</code>,客户端可以通过 <code>errno</code> 是否为 0 来判断当前接口是否有异常。</p>\n<h4 id=\"-json\">输出含有错误信息的 JSON</h4>\n<p>可以通过 <code>this.fail</code> 方法输出含有错误信息的接口数据,如:</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n indexAction(){\n this.fail(1000, \'connect error\'); //指定错误号和错误信息\n }\n}\n</code></pre>\n<p>输出结果为 <code>{errno: 1000, errmsg: \"connect error\"}</code>,客户端判断 <code>errno</code> 大于 0,就知道当前接口有异常,并且通过 <code>errmsg</code> 拿到具体的错误信息。</p>\n<h5 id=\"-\">配置错误号和错误信息</h5>\n<p>如果每个地方输出错误的时候都要指定错误号和错误信息势必比较麻烦,比较好的方式是把错误号和错误信息在一个地方配置,然后输出的时候只要指定错误号,错误信息根据错误号自动读取。</p>\n<p>错误信息支持国际化,所以配置放在 <code>src/common/config/locale/[lang].js</code> 文件中。如:</p>\n<pre><code class=\"lang-js\">export default {\n 10001: \'get data error\'\n}\n</code></pre>\n<p>通过上面的配置后,执行 <code>this.fail(10001)</code> 时会自动读取到对应的错误信息。</p>\n<h5 id=\"-\">友好的错误号</h5>\n<p>在程序里执行 <code>this.fail(10001)</code> 虽然能输出正确的错误号和错误信息,但人不能直观的看出来错误号对应的错误信息是什么。</p>\n<p>这时可以将 key 配置为大写字符串,值为错误号和错误信息。如:</p>\n<pre><code class=\"lang-js\">export default {\n GET_DATA_ERROR: [1234, \'get data error\'] //key 必须为大写字符或者下划线才有效\n}\n</code></pre>\n<p>执行 <code>this.fail(\'GET_DATA_ERROR\')</code> 时也会自动取到对应的错误号和错误信息。</p>\n<h4 id=\"-\">格式配置</h4>\n<p>默认输出的错误号的 key 为 <code>errno</code>,错误信息的 key 为 <code>errmsg</code>。如果不满足需求的话,可以修改配置文件 <code>src/common/config/error.js</code>。</p>\n<pre><code class=\"lang-js\">export default {\n key: \'errno\', //error number\n msg: \'errmsg\', //error message\n}\n</code></pre>\n<h4 id=\"-json\">输出不包含错误信息的 JSON</h4>\n<p>如果输出的 JSON 数据里不想包含 <code>errno</code> 和 <code>errmsg</code> 的话,可以通过 <code>this.json</code> 方法输出 JSON。如:</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n indexAction(){\n this.json({name: \'thinkjs\'});\n }\n}\n</code></pre>\n<h3 id=\"-\">常用功能</h3>\n<h4 id=\"-get-\">获取 GET 参数</h4>\n<p>可以通过 <code>get</code> 方法获取 GET 参数,如:</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n indexAction(){\n let name = this.get(\'name\');\n let allParams = this.get(); //获取所有 GET 参数\n }\n}\n</code></pre>\n<p>如果参数不存在,那么值为空字符串。</p>\n<h4 id=\"-post-\">获取 POST 参数</h4>\n<p>可以通过 <code>post</code> 方法获取 POST 参数,如:</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n indexAction(){\n let name = this.post(\'name\');\n let allParams = this.post(); //获取所有 POST 参数\n }\n}\n</code></pre>\n<p>如果参数不存在,那么值为空字符串。</p>\n<h4 id=\"-\">获取上传的文件</h4>\n<p>可以通过 <code>file</code> 方法获取上传的文件,如:</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n indexAction(){\n let file = this.file(\'image\');\n let allFiles = this.file(); //获取所有上传的文件\n }\n}\n</code></pre>\n<p>返回值是个对象,包含下面的属性:</p>\n<pre><code class=\"lang-js\">{\n fieldName: \'file\', //表单字段名称\n originalFilename: filename, //原始的文件名\n path: filepath, //文件保存的临时路径,使用时需要将其移动到项目里的目录,否则请求结束时会被删除\n size: 1000 //文件大小\n}\n</code></pre>\n<p>如果文件不存在,那么值为一个空对象 <code>{}</code>。</p>\n<h4 id=\"jsonp-\">JSONP 格式数据输出</h4>\n<p>可以通过 <code>this.jsonp</code> 方法输出 JSONP 格式的数据,callback 的请求参数名默认为 <code>callback</code>。如果需要修改请求参数名,可以通过修改配置 <code>callback_name</code> 来完成。</p>\n<h4 id=\"-\">更多方法</h4>\n<ul>\n<li><code>isGet()</code> 当前是否是 GET 请求</li>\n<li><code>isPost()</code> 当前是否是 POST 请求</li>\n<li><code>isAjax()</code> 是否是 AJAX 请求</li>\n<li><code>ip()</code> 获取请求用户的 ip</li>\n<li><code>redirect(url)</code> 跳转到一个 url</li>\n<li><code>write(data)</code> 输出数据,会自动调用 JSON.stringify</li>\n<li><code>end(data)</code> 结束当前的 http 请求</li>\n<li><code>json(data)</code> 输出 JSON 数据,自动发送 JSON 相关的 Content-Type</li>\n<li><code>jsonp(data)</code> 输出 JSONP 数据,请求参数名默认为 <code>callback</code></li>\n<li><code>success(data)</code> 输出一个正常的 JSON 数据,数据格式为 <code>{errno: 0, errmsg: \"\", data: data}</code></li>\n<li><code>fail(errno, errmsg, data)</code> 输出一个错误的 JSON 数据,数据格式为 <code>{errno: errno_value, errmsg: string, data: data}</code></li>\n<li><code>download(file)</code> 下载文件</li>\n<li><code>assign(name, value)</code> 设置模版变量</li>\n<li><code>display()</code> 输出一个模版</li>\n<li><code>fetch()</code> 渲染模版并获取内容</li>\n<li><code>cookie(name, value)</code> 获取或者设置 cookie</li>\n<li><code>session(name, value)</code> 获取或者设置 session</li>\n<li><code>header(name, value)</code> 获取或者设置 header</li>\n<li><code>action(name, data)</code> 调用其他 Controller 的方法,可以跨模块</li>\n<li><code>model(name, options)</code> 获取模型实例</li>\n</ul>\n<p>完整方法列表请见 <a href=\"./api_controller.html\">API -> Controller</a>。</p><p> 文章来源:<a href=\"http://www.thinkjs.org\" target=\"_blank\">http://www.thinkjs.org</a></p><p><br></p>', '', '前端汇', '2016-07-11 19:21:37', '60', '0', '0', '0', '18', 'thinkjs,nodejs', '0', '1', '', '8', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('238', '视图', '', '<p>视图即模版,默认的根目录为 <code>view/</code>。</p>\n<h3 id=\"-\">视图文件</h3>\n<p>视图文件默认的命名规则为 <code>模块/控制器_操作.html</code>。</p>\n<p>假如 URL <code>home/article/detail</code> 解析后的模块是 <code>home</code>,控制器是 <code>article</code>,操作是 <code>detail</code>,那么对应的视图文件为 <code>home/article_detail.html</code>。</p>\n<h3 id=\"-\">视图配置</h3>\n<p>视图默认配置如下,可以在配置文件 <code>src/common/config/view.js</code> 中修改:</p>\n<pre><code class=\"lang-js\">export default {\n type: \'ejs\', //模版引擎\n content_type: \'text/html\', //输出模版时发送的 Content-Type\n file_ext: \'.html\', //文件的扩展名\n file_depr: \'_\', //控制器和操作之间的连接符\n root_path: think.ROOT_PATH + \'/view\', //视图文件的根目录\n adapter: { //模版引擎需要的配置项\n ejs: {}, //使用 ejs 模板引擎时额外配置\n nunjucks: {} //使用 nunjucks 模板引擎时额外配置\n } \n};\n</code></pre>\n<p><code>注</code>:<code>2.0.6</code> 版本开始去除了 <code>options</code> 配置项,使用 <code>adapter</code> 代替。</p>\n<p>视图默认根目录在 <code>view/</code>。如果想每个模块有独立的视图目录,将配置 <code>root_path</code> 修改为空即可。</p>\n<h4 id=\"-\">修改连接符</h4>\n<p>默认控制器和操作之间的连接符是 <code>_</code>,文件名类似为 <code>index_index.html</code>,如果想将控制器作为一层目录的话,如:<code>index/index.html</code>,可以将连接符修改为 <code>/</code>。</p>\n<pre><code class=\"lang-js\">export default {\n file_depr: \'/\'\n}\n</code></pre>\n<h4 id=\"-\">修改模板引擎配置</h4>\n<p>如果想修改模板引擎的一些配置,可以修改配置 <code>adapter</code> 里对应字段。如:</p>\n<pre><code class=\"lang-js\">export default {\n adapter: {\n ejs: {\n delimiter: \'&\' //将定界符修改为 <& 和 &>\n },\n nunjucks: {\n trimBlocks: false, //不转义\n prerender: function(nunjucks, env){} //针对nunjucks模板的过滤器\n }\n }\n}\n</code></pre>\n<h3 id=\"-\">模版引擎</h3>\n<p>ThinkJS 默认支持的模版引擎有:<code>ejs</code>,<code>jade</code>,<code>swig</code> 和 <code>nunjucks</code>,默认模版引擎为 <code>ejs</code>,可以根据需要修改为其他的模版引擎。</p>\n<h4 id=\"ejs\">ejs</h4>\n<h5 id=\"-\">定界符</h5>\n<p>ejs 默认的定界符是 <code><%</code> 和 <code>%></code>。如果想修改定界符,可以通过配置 <code>adapter</code> 里的 <code>ejs</code> 来修改,如:</p>\n<pre><code class=\"lang-js\">export default {\n adapter: {\n ejs: {\n delimiter: \'&\' //将定界符修改为 <& 和 &>\n }\n }\n}\n</code></pre>\n<h5 id=\"-\">变量输出</h5>\n<ul>\n<li>转义输出 <code><%= data.name%></code></li>\n<li>不转义输出 <code><%- data.name%></code></li>\n<li>注释 <code><%# data.name%></code></li>\n</ul>\n<h5 id=\"-\">条件判断</h5>\n<pre><code class=\"lang-text\"><%if(data.name === \'1\'){%>\n <p>...</p>\n<%}else if(data.name === \'2\'){%>\n <p>...</p>\n<%}else{%>\n <p>...</p>\n<%}%>\n</code></pre>\n<h5 id=\"-\">循环</h5>\n<pre><code class=\"lang-text\"><%list.forEach(function(item){%>\n <li><%=item.name%></li>\n<%})%>\n</code></pre>\n<h5 id=\"-\">过滤器</h5>\n<p>新版的 <code>ejs</code> 已经不再支持过滤器的功能了,如果需要一些过滤功能,可以在 <code>src/common/bootstrap/</code> 里定义一些全局函数,模板里可以直接使用这些函数。</p>\n<h5 id=\"-\">引用文件</h5>\n<p>ejs 不支持模版继承。但可以将公用的模版独立成一个文件,然后通过 <code>include</code> 来引入。</p>\n<pre><code class=\"lang-text\"><%include inc/header.html%>\n</code></pre>\n<p><code>注</code>:ejs 模版使用的变量需要在控制器中赋值,否则会报错。</p>\n<p>更多 ejs 使用文档请见 <a href=\"https://www.npmjs.com/package/ejs\">这里</a>。</p>\n<h4 id=\"nunjucks\">nunjucks</h4>\n<p>nunjucks 是一款类似于 jinja2 的模版引擎,功能异常强大,复杂项目建议使用该模版引擎。</p>\n<h5 id=\"-\">定界符</h5>\n<p>块级定界符为 <code>{%</code> 和 <code>%}</code>,变量定界符为 <code>{{</code> 和 <code>}}</code>,注释定界符为 <code><#</code> 和 <code>#></code>。如:</p>\n<pre><code class=\"lang-html\">{{ username }} \n\n{% block header %} \nThis is the default content\n{% endblock %}\n</code></pre>\n<h5 id=\"-\">变量输出</h5>\n<p>可以通过 <code>{{ username }}</code> 来输出变量,默认输出的变量会自动转义,如果不想被转义,可以通过 <code>{{ username | safe }}</code> 来处理。</p>\n<h5 id=\"-\">模版继承</h5>\n<p>父级模版:</p>\n<pre><code class=\"lang-html\">{% block header %}\nThis is the default content\n{% endblock %}\n\n<section class=\"left\">\n {% block left %}{% endblock %}\n</section>\n\n<section class=\"right\">\n {% block right %}\n This is more content\n {% endblock %}\n</section>\n</code></pre>\n<p>子级模版:</p>\n<pre><code class=\"lang-html\">{% extends \"./parent.html\" %}\n\n{% block left %}\nThis is the left side!\n{% endblock %}\n\n{% block right %}\nThis is the right side!\n{% endblock %}\n</code></pre>\n<p><code>注</code>:nunjucks 默认设置了 root_path,所以模板继承时需要使用相对路径。</p>\n<h5 id=\"-\">条件判断</h5>\n<pre><code class=\"lang-html\">{% if hungry %}\n I am hungry\n{% elif tired %}\n I am tired\n{% else %}\n I am good!\n{% endif %}\n</code></pre>\n<h5 id=\"-\">循环</h5>\n<pre><code class=\"lang-html\"><h1>Posts</h1>\n<ul>\n{% for item in items %}\n <li>{{ item.title }}</li>\n{% else %}\n <li>This would display if the \'item\' collection were empty</li>\n{% endfor %}\n</ul>\n</code></pre>\n<p>具体使用文档请见 <a href=\"http://mozilla.github.io/nunjucks/\">这里</a>。</p>\n<h4 id=\"jade\">jade</h4>\n<p>jade 模版使用方式请见 <a href=\"https://github.com/jadejs/jade\">这里</a>。</p>\n<h4 id=\"swig\">swig</h4>\n<p>swig 模版使用方式请见 <a href=\"http://paularmstrong.github.io/swig/\">这里</a>。</p>\n<h4 id=\"-\">添加过滤器等功能</h4>\n<p><code>swig</code>,<code>nunjucks</code> 等很多模板引擎都支持添加过滤器等功能,可以在模板配置文件 <code>src/common/config/view.js</code> 中对应的 <code>adapter</code> 添加 <code>prerender</code> 配置来完成。如:</p>\n<pre><code class=\"lang-js\">export default {\n adapter:{\n nunjucks: {\n prerender: function(nunjucks, env){\n //添加一个过滤器,这样可以在模板里使用了\n env.addFilter(\'filter_foo\', function(){\n\n });\n }\n }\n }\n}\n</code></pre>\n<p><code>注</code>: 该功能是在版本 <code>2.0.5</code> 中新增的。</p>\n<h4 id=\"-\">扩展模版引擎</h4>\n<p>模版引擎使用 Adapter 实现。如果项目里需要使用其他模版引擎,可以通过 Adapter 进行扩展,具体请见 <a href=\"./adapter_template.html\">这里</a>。</p>\n<h3 id=\"-\">变量赋值</h3>\n<p>控制器中可以通过 <code>assign</code> 方法进行变量赋值。</p>\n<h5 id=\"-\">赋值单个变量</h5>\n<pre><code class=\"lang-js\">export default class extends think.controlle.base {\n indexAction(){\n this.assign(\'title\', \'ThinkJS 官网\');\n }\n}\n</code></pre>\n<h5 id=\"-\">赋值多个变量</h5>\n<pre><code class=\"lang-js\">export default class extends think.controlle.base {\n indexAction(){\n this.assign({\n title: \'ThinkJS 官网\',\n author: \'thinkjs\'\n });\n }\n}\n</code></pre>\n<h5 id=\"-\">获取赋值</h5>\n<p>变量赋值后也可以通过 <code>assign</code> 来获取赋过的值。如:</p>\n<pre><code class=\"lang-js\">export default class extends think.controlle.base {\n indexAction(){\n this.assign(\'title\', \'ThinkJS 官网\');\n let title = this.assign(\'title\');\n }\n}\n</code></pre>\n<h3 id=\"-\">模版渲染</h3>\n<p>可以通过 <code>display</code> 方法进行模版渲染。如果不传具体的模版文件路径,会自动查找。如:</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n indexAction(){\n this.display();// render home/index_index.html\n }\n}\n</code></pre>\n<p>也可以指定具体的模版文件进行渲染,关于 <code>display</code> 方法的详细使用请见 <a href=\"./api_controller.html#toc-6b2\">这里</a>。</p>\n<h3 id=\"-\">获取渲染后的内容</h3>\n<p>如果有时候不想支持输出模版,而是想获取渲染后的模版内容,那么可以通过 <code>fetch</code> 方法来获取。</p>\n<h5 id=\"es6-\">ES6 方式</h5>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n * indexAction(){\n let content = yield this.fetch();\n ...\n }\n}\n</code></pre>\n<h5 id=\"-\">动态创建类的方式</h5>\n<pre><code class=\"lang-js\">module.exports = think.controller({\n indexAction: function(){\n this.fetch().then(function(content){\n ...\n })\n }\n})\n</code></pre>\n<p>关于 <code>fetch</code> 方法的详细使用方式请见 <a href=\"api_controller.html#controllerfetchtemplatefile\">这里</a>。</p>\n<h3 id=\"-\">国际化</h3>\n<p>启动国际化后,视图路径会多一层国际化的目录。如:具体的视图路径变为 <code>view/zh-cn/home/index_index.html</code>,其中 <code>zh-cn</code> 为语言名。</p>\n<p>关于如果使用国际化请见 <a href=\"./i18n.html\">扩展功能 -> 国际化</a>。 </p>\n<h3 id=\"-\">多主题</h3>\n<p>设置多主题后,视图路径会多一层多主题的目录。如:具体的视图路径变为 <code>view/default/home/index_index.html</code>,其中 <code>default</code> 为主题名称。</p>\n<p>可以通过 <code>http.theme</code> 方法来设置当前的主题,设置主题一般是通过 middleware 来实现。</p>\n<p>关于 middleware 更多信息请见 <a href=\"./middleware.html\">扩展功能 - Middleware</a>。</p>\n<h3 id=\"-\">默认模版变量</h3>\n<p>为了可以在模版里很方便的获取一些通用的变量,框架自动向模版里注册了 <code>http</code>, <code>controller</code>, <code>config</code> 等变量,这些变量可以在模版里直接使用。</p>\n<p>下面的代码示例都是基于 <code>ejs</code> 模版引擎的,其他的模版引擎下需要根据相应的语法进行修改。</p>\n<h4 id=\"http\">http</h4>\n<p>模版里可以直接使用 <code>http</code> 对象下的属性和方法。</p>\n<h4 id=\"controller\">controller</h4>\n<p>模版里可以直接使用 <code>controller</code> 对象下的属性和方法。</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n indexAction(){\n this.navType = \'home\';\n }\n}\n</code></pre>\n<p>Action 里给当前控制器添加了属性 <code>navType</code>,那么模版里就可以直接通过 <code>controller.navType</code> 来使用。</p>\n<pre><code class=\"lang-text\"><%if(controller.navType === \'home\')%>\n <li className=\"action\">home</li>\n<%}else{%>\n <li>home</li>\n<%}%>\n</code></pre>\n<h4 id=\"config\">config</h4>\n<p>通过 <code>config</code> 对象可以在模版中直接对应的配置,如:</p>\n<pre><code class=\"lang-text\"><%if(config.name === \'text\'){%>\n\n<%}%>\n</code></pre>\n<h4 id=\"-_\">国际化方法 _</h4>\n<p>在模版中可以直接通过 <code>_</code> 方法获取对应本地化的值,这些值在 <code>src/common/config/locales/[lang].js</code> 中定义。</p>\n<pre><code class=\"lang-text\"><%= _(\'title\')%>\n</code></pre>\n<p>更多国际化相关的信息请见 <a href=\"./i18n.html\">这里</a>。</p><p> 文章来源:<a href=\"http://www.thinkjs.org\" target=\"_blank\">http://www.thinkjs.org</a></p><p><br></p>', '', '前端汇', '2016-07-11 19:21:42', '60', '0', '0', '0', '18', 'thinkjs,nodejs', '1', '1', '', '8', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('239', '配置', '', '<p>ThinkJS 提供了灵活的配置,可以在不同的模块和不同的项目环境下使用不同的配置,且这些配置在服务启动时就已经生效。</p>\n<p><code>注意:不可将一个 http 请求中的私有值设置到配置中,这将会被下一个 http 设置的值给冲掉。</code></p>\n<h3 id=\"-\">项目模块</h3>\n<p>ThinkJS 默认创建的项目是按模块来划分的,可以在每个模块下定义不同的配置。其中 <code>common</code> 模块下定义一些通用的配置,其他模块下配置会继承 <code>common</code> 下的配置。如:<code>home</code> 模块下的最终配置是将 <code>common</code> 和 <code>home</code> 模块下配置合并的结果。</p>\n<h3 id=\"-\">项目环境</h3>\n<p>ThinkJS 默认支持 3 种项目环境,可以根据不同的环境进行配置,以满足不同情况下的配置需要。</p>\n<ul>\n<li><code>development</code> 开发环境</li>\n<li><code>testing</code> 测试环境</li>\n<li><code>production</code> 线上环境</li>\n</ul>\n<p>项目里也可以扩展其他的环境,当前使用哪种环境可以在 <a href=\"./app_structure.html#toc-f0b\">入口文件</a> 中设置,设置 <code>env</code> 值即可。</p>\n<h3 id=\"-\">定义配置文件</h3>\n<h5 id=\"config-config-js\">config/config.js</h5>\n<p>存放一些基本的配置,如:</p>\n<pre><code class=\"lang-js\">export default {\n port: 8360, \n host: \'\',\n encoding: \'utf-8\',\n ...\n}\n</code></pre>\n<h5 id=\"config-name-js\">config/[name].js</h5>\n<p>存放具体某个独立功能的配置,如:<code>db.js</code> 为数据库配置,<code>redis</code> 为 redis 配置。</p>\n<pre><code class=\"lang-js\">// db.js\nexport default {\n type: \'mysql\',\n ...\n};\n</code></pre>\n<h5 id=\"config-env-mode-js\">config/env/[mode].js</h5>\n<p>不同项目环境的差异化配置,如:<code>env/development.js</code>,<code>env/testing.js</code>,<code>env/production.js</code></p>\n<pre><code class=\"lang-js\">// config/env/development.js\nexport default {\n port: 7777,\n db: { //开发模式下数据库配置\n type: \'mysql\',\n adapter: {\n mysql: {\n host: \'127.0.0.1\',\n port: \'\',\n }\n }\n ...\n }\n}\n</code></pre>\n<p><code>注</code>:不同项目环境差异化配置一般不是很多,所以放在一个文件中定义。这时候如果要修改一个独立功能的配置,就需要将独立功能对应的 key 带上。如:上述代码里的修改数据库配置需要将数据库对应的名称 <code>db</code> 带上。</p>\n<h5 id=\"config-locale-lang-js\">config/locale/[lang].js</h5>\n<p>国际化语言包配置,如: <code>locale/en.js</code>,<code>locale/zh-cn.js</code>。</p>\n<hr>\n<p>配置格式采用 <code>key: value</code> 的形式,并且 <code>key</code> 不区分大小写。</p>\n<h3 id=\"-\">加载配置文件</h3>\n<p>框架支持多种级别的配置文件,会按以下顺序进行读取:</p>\n<p><code>框架默认的配置 -> 项目模式下框架配置 -> 项目公共配置 -> 项目模式下的公共配置 -> 模块下的配置</code></p>\n<h3 id=\"-\">配置读取</h3>\n<h4 id=\"-config-\">通过 config 方法获取</h4>\n<p>在 Controller,Logic,Middleware 等地方可以通过 <code>this.config</code> 来获取。如:</p>\n<pre><code class=\"lang-js\">let db = this.config(\'db\'); //读取数据库的所有配置\nlet host = this.config(\'db.host\'); //读取数据库的 host 配置,等同于 db.host\n</code></pre>\n<h4 id=\"-http-config-\">通过 http 对象上的 config 方法获取</h4>\n<p>http 对象也有 config 方法用来获取相关的配置,如:</p>\n<pre><code class=\"lang-js\">let db = http.config(\'db\');\n</code></pre>\n<h4 id=\"-\">其他地方配置读取</h4>\n<p>其他地方可以通过 <code>think.config</code> 来读取相关的配置:</p>\n<pre><code class=\"lang-js\">let db = think.config(\'db\'); //读取通用模块下的数据库配置\nlet db1 = think.config(\'db\', undefined, \'home\'); //获取 home 模块下数据库配置\n</code></pre>\n<p><code>注</code>:路由解析前,无法通过 <code>config</code> 方法或者 http 对象上的 <code>config</code> 方法来获取非通用模块下的配置,所以路由解析前就使用的配置需要定义在通用模块里。</p>\n<h3 id=\"-\">系统默认配置</h3>\n<h4 id=\"env\">env</h4>\n<p>项目模式下的配置,<code>config/env/development.js</code>。</p>\n<pre><code class=\"lang-js\">export default {\n auto_reload: true,\n log_request: true,\n gc: {\n on: false\n },\n error: {\n detail: true\n }\n}\n</code></pre>\n<p><code>config/env/testing.js</code> 和 <code>config/env/produciton.js</code> 无默认配置。</p>\n<h4 id=\"locale\">locale</h4>\n<p>国际化语言包配置,默认的配置如下:</p>\n<pre><code class=\"lang-js\">// config/locale/en.js\nexport default {\n CONTROLLER_NOT_FOUND: \'controller `%s` not found. url is `%s`.\',\n CONTROLLER_INVALID: \'controller `%s` is not valid. url is `%s`\',\n ACTION_NOT_FOUND: \'action `%s` not found. url is `%s`\',\n ACTION_INVALID: \'action `%s` is not valid. url is `%s`\',\n WORKER_DIED: \'worker `%d` died, it will auto restart.\',\n MIDDLEWARE_NOT_FOUND: \'middleware `%s` not found\',\n ADAPTER_NOT_FOUND: \'adapter `%s` not found\',\n GCTYPE_MUST_SET: \'instance must have gcType property\',\n CONFIG_NOT_FUNCTION: \'config `%s` is not a function\',\n CONFIG_NOT_VALID: \'config `%s` is not valid\',\n PATH_EMPTY: \'`%s` path muse be set\',\n PATH_NOT_EXIST: \'`%s` is not exist\',\n TEMPLATE_NOT_EXIST: \'can\\\'t find template file `%s`\',\n PARAMS_EMPTY: \'params `%s` value can\\\'t empty\',\n PARAMS_NOT_VALID: \'params `{name}` value not valid\',\n FIELD_KEY_NOT_VALID: \'field `%s` in where condition is not valid\',\n DATA_EMPTY: \'data can not be empty\',\n MISS_WHERE_CONDITION: \'miss where condition\',\n INVALID_WHERE_CONDITION_KEY: \'where condition key is not valid\',\n WHERE_CONDITION_INVALID: \'where condition `%s`:`%s` is not valid\',\n TABLE_NO_COLUMNS: \'table `%s` has no columns\',\n NOT_SUPPORT_TRANSACTION: \'table engine is not support transaction\',\n DATA_MUST_BE_ARRAY: \'data is not an array list\',\n PARAMS_TYPE_INVALID: \'params `{name}` type invalid\',\n DISALLOW_PORT: \'proxy on, cannot visit with port\',\n SERVICE_UNAVAILABLE: \'Service Unavailable\',\n\n validate_required: \'{name} can not be blank\',\n validate_contains: \'{name} need contains {args}\',\n validate_equals: \'{name} need match {args}\',\n validate_different: \'{name} nedd not match {args}\',\n validate_after: \'{name} need a date that\\\'s after the {args} (defaults to now)\',\n validate_alpha: \'{name} need contains only letters (a-zA-Z)\',\n validate_alphaDash: \'{name} need contains only letters and dashes(a-zA-Z_)\',\n validate_alphaNumeric: \'{name} need contains only letters and numeric(a-zA-Z0-9)\',\n validate_alphaNumericDash: \'{name} need contains only letters, numeric and dash(a-zA-Z0-9_)\',\n validate_ascii: \'{name} need contains ASCII chars only\',\n validate_base64: \'{name} need a valid base64 encoded\',\n validate_before: \'{name} need a date that\\\'s before the {args} (defaults to now)\',\n validate_byteLength: \'{name} need length (in bytes) falls in {args}\',\n validate_creditcard: \'{name} need a valid credit card\',\n validate_currency: \'{name} need a valid currency amount\',\n validate_date: \'{name} need a date\',\n validate_decimal: \'{name} need a decimal number\',\n validate_divisibleBy: \'{name} need a number that\\\'s divisible by {args}\',\n validate_email: \'{name} need an email\',\n validate_fqdn: \'{name} need a fully qualified domain name\',\n validate_float: \'{name} need a float in {args}\',\n validate_fullWidth: \'{name} need contains any full-width chars\',\n validate_halfWidth: \'{name} need contains any half-width chars\',\n validate_hexColor: \'{name} need a hexadecimal color\',\n validate_hex: \'{name} need a hexadecimal number\',\n validate_ip: \'{name} need an IP (version 4 or 6)\',\n validate_ip4: \'{name} need an IP (version 4)\',\n validate_ip6: \'{name} need an IP (version 6)\',\n validate_isbn: \'{name} need an ISBN (version 10 or 13)\',\n validate_isin: \'{name} need an ISIN (stock/security identifier)\',\n validate_iso8601: \'{name} need a valid ISO 8601 date\',\n validate_in: \'{name} need in an array of {args}\',\n validate_notIn: \'{name} need not in an array of {args}\',\n validate_int: \'{name} need an integer\',\n validate_min: \'{name} need an integer greater than {args}\',\n validate_max: \'{name} need an integer less than {args}\',\n validate_length: \'{name} need length falls in {args}\',\n validate_minLength: \'{name} need length is max than {args}\',\n validate_maxLength: \'{name} need length is min than {args}\',\n validate_lowercase: \'{name} need is lowercase\',\n validate_mobile: \'{name} need is a mobile phone number\',\n validate_mongoId: \'{name} need is a valid hex-encoded representation of a MongoDB ObjectId\',\n validate_multibyte: \'{name} need contains one or more multibyte chars\',\n validate_url: \'{name} need an URL\',\n validate_uppercase: \'{name} need uppercase\',\n validate_variableWidth: \'{name} need contains a mixture of full and half-width chars\',\n validate_order: \'{name} need a valid sql order string\',\n validate_field: \'{name} need a valid sql field string\',\n validate_image: \'{name} need a valid image file\',\n validate_startWith: \'{name} need start with {args}\',\n validate_endWidth: \'{name} need end with {args}\',\n validate_string: \'{name} need a string\',\n validate_array: \'{name} need an array\',\n validate_boolean: \'{name} need a boolean\',\n validate_object: \'{name} need an object\'\n}\n</code></pre>\n<h4 id=\"config\">config</h4>\n<p>基本配置,<code>config/config.js</code>。</p>\n<pre><code class=\"lang-js\">export default {\n port: 8360, //服务监听的端口\n host: \'\', //服务监听的 host\n encoding: \'utf-8\', //项目编码\n pathname_prefix: \'\', //pathname 去除的前缀,路由解析中使用\n pathname_suffix: \'.html\', //pathname 去除的后缀,路由解析中使用\n hook_on: true, //是否开启 hook\n cluster_on: false, //是否开启 cluster\n\n timeout: 120, //120 seconds\n auto_reload: false, //自动重新加载修改的文件,development 模式下使用\n\n resource_on: true, // 是否处理静态资源请求, porxy_on 开启下可以关闭该配置\n resource_reg: /^(static\\/|[^\\/]+\\.(?!js|html)\\w+$)/, //静态资源的正则\n\n route_on: true, //是否开启自定义路由\n\n log_error: true, //是否打印错误日志\n log_request: false, //是否打印请求的日志\n\n create_server: undefined, //自定义启动服务\n output_content: undefined, //自定义输出内容处理方式,可以进行 gzip 处理等\n deny_module_list: [], //禁用的模块列表\n default_module: \'home\', //默认模块\n default_controller: \'index\', //默认的控制器\n default_action: \'index\', //默认的 Action\n callback_name: \'callback\', //jsonp 请求的 callback 名称\n json_content_type: \'application/json\', //json 输出时设置的 Content-Type\n}\n</code></pre>\n<h4 id=\"cache\">cache</h4>\n<p>缓存配置,<code>config/cache.js</code>。</p>\n<pre><code class=\"lang-js\">export default {\n type: \'file\', //缓存方式\n adapter: {\n file: {\n timeout: 6 * 3600, //6 hours\n path: think.RUNTIME_PATH + \'/cache\', //文件缓存模式下缓存内容存放的目录\n path_depth: 2, //子目录深度\n file_ext: \'.json\' //缓存文件的扩展名\n },\n redis: {\n prefix: \'thinkjs_\', //缓存名称前缀\n }\n }\n};\n</code></pre>\n<h4 id=\"cookie\">cookie</h4>\n<p>cookie 配置,<code>config/cookie.js</code>。</p>\n<pre><code class=\"lang-js\">export default {\n domain: \'\', // cookie domain\n path: \'/\', // cookie path\n httponly: false, //是否 httponly\n secure: false, //是否在 https 下使用\n timeout: 0 //cookie 有效时间\n};\n</code></pre>\n<h4 id=\"db\">db</h4>\n<p>数据库配置,<code>config/db.js</code>。</p>\n<pre><code class=\"lang-js\">export default {\n type: \'mysql\', //数据库类型\n log_sql: true, //是否记录 sql 语句\n log_connect: true, // 是否记录连接数据库的信息\n adapter: {\n mysql: {\n host: \'127.0.0.1\', //数据库 host\n port: \'\', //端口\n database: \'\', //数据库名称\n user: \'\', //账号\n password: \'\', //密码\n prefix: \'think_\', //数据表前缀\n encoding: \'utf8\', //数据库编码\n nums_per_page: 10, //一页默认条数\n }\n }\n};\n</code></pre>\n<h4 id=\"error\">error</h4>\n<p>错误信息配置,<code>config/error.js</code>。</p>\n<pre><code class=\"lang-js\">export default {\n key: \'errno\', //error number\n msg: \'errmsg\', //error message\n value: 1000 //default errno\n};\n</code></pre>\n<h4 id=\"gc\">gc</h4>\n<p>缓存、Session等垃圾处理配置,<code>config/gc.js</code>。</p>\n<pre><code class=\"lang-js\">export default {\n on: true, //是否开启垃圾回收处理\n interval: 3600, // 处理时间间隔,默认为一个小时\n filter: function(){ //如果返回 true,则进行垃圾回收处理\n let hour = (new Date()).getHours();\n if(hour === 4){\n return true;\n }\n }\n};\n</code></pre>\n<h4 id=\"hook\">hook</h4>\n<p>hook 配置,<code>config/hook.js</code>。</p>\n<pre><code class=\"lang-js\">export default {\n request_begin: [],\n payload_parse: [\'parse_form_payload\', \'parse_single_file_payload\', \'parse_json_payload\', \'parse_querystring_payload\'],\n payload_validate: [\'validate_payload\'],\n resource: [\'check_resource\', \'output_resource\'],\n route_parse: [\'rewrite_pathname\', \'parse_route\'],\n logic_before: [],\n logic_after: [],\n controller_before: [],\n controller_after: [],\n view_before: [],\n view_template: [\'locate_template\'],\n view_parse: [\'parse_template\'],\n view_filter: [],\n view_after: [],\n response_end: []\n};\n</code></pre>\n<h4 id=\"post\">post</h4>\n<p>post 请求时的配置,<code>config/post.js</code>。</p>\n<pre><code class=\"lang-js\">export default {\n json_content_type: [\'application/json\'],\n max_file_size: 1024 * 1024 * 1024, //1G\n max_fields: 100, \n max_fields_size: 2 * 1024 * 1024, //2M,\n ajax_filename_header: \'x-filename\',\n file_upload_path: think.RUNTIME_PATH + \'/upload\',\n file_auto_remove: true\n};\n</code></pre>\n<h4 id=\"redis\">redis</h4>\n<p>redis 配置,<code>config/redis.js</code>。</p>\n<pre><code class=\"lang-js\">export default {\n host: \'127.0.0.1\',\n port: 6379,\n password: \'\',\n timeout: 0,\n log_connect: true\n};\n</code></pre>\n<h4 id=\"memcache\">memcache</h4>\n<p>memcache 配置,<code>config/memcache.js</code>。</p>\n<pre><code class=\"lang-js\">export default {\n host: \'127.0.0.1\', //memcache host\n port: 11211,\n username: \'\', //\n password: \'\',\n timeout: 0, //缓存失效时间\n log_connect: true\n};\n</code></pre>\n<h4 id=\"session\">session</h4>\n<p>session 配置,<code>config/session.js</code>。</p>\n<pre><code class=\"lang-js\">export default {\n name: \'thinkjs\',\n type: \'file\',\n path: think.RUNTIME_PATH + \'/session\',\n secret: \'\',\n timeout: 24 * 3600,\n cookie: { // cookie options\n length: 32\n }\n};\n</code></pre>\n<h4 id=\"view\">view</h4>\n<p>视图配置,<code>config/view.js</code>。</p>\n<pre><code class=\"lang-js\">export default {\n content_type: \'text/html\',\n file_ext: \'.html\',\n file_depr: \'_\',\n root_path: \'\',\n type: \'ejs\',\n adapter: {\n ejs: {\n\n }\n }\n};\n</code></pre>\n<h4 id=\"websocket\">websocket</h4>\n<p>websocket 配置,<code>config/websocket.js</code>。</p>\n<pre><code class=\"lang-js\">export default {\n on: false, //是否开启 websocket\n type: \'think\', //websocket 使用的库\n allow_origin: \'\',\n sub_protocal: \'\',\n adapter: undefined,\n path: \'\', //url path for websocket\n messages: {\n // open: \'home/websocket/open\',\n }\n};\n</code></pre>\n<h3 id=\"-\">扩展配置</h3>\n<p>项目里可以根据需要扩展配置,扩展配置只需在 <code>src/common/config/</code> 建立对应的文件即可,如:</p>\n<pre><code class=\"lang-js\">// src/common/config/foo.js\nexport default {\n name: \'bar\'\n}\n</code></pre>\n<p>这样就可以通过 <code>think.config(\'foo\')</code> 来获取对应的配置了。 </p><p> 文章来源:<a href=\"http://www.thinkjs.org\" target=\"_blank\">http://www.thinkjs.org</a></p><p><br></p>', '', '前端汇', '2016-07-11 19:21:47', '57', '0', '0', '0', '18', 'thinkjs,nodejs', '1', '1', '', '8', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('240', '路由', '', '<p>当用户访问一个 URL 时,最终执行哪个模块下哪个控制器的哪个操作,这是由路由来解析后决定的。</p>\n<p>ThinkJS 提供了一套灵活的路由机制,除了默认的解析外,还可以支持多种形式的自定义路由,让 URL 更加简单友好。</p>\n<h3 id=\"url-pathname\">URL 解析为 pathname</h3>\n<p>当用户访问服务时,服务端首先拿到的是一个完整的 URL,如:访问本页面,得到的 URL 为 <code>http://www.thinkjs.org/zh-cn/doc/2.0/route.html</code>。</p>\n<p>将 URL 进行解析得到的 pathname 为 <code>/zh-cn/doc/2.0/route.html</code>。</p>\n<h3 id=\"pathname-\">pathname 过滤</h3>\n<p>有时候为了搜索引擎优化或者一些其他的原因, URL 上会多加一些东西。比如:当前页面是一个动态页面,但 URL 最后加了 <code>.html</code>,这样对搜索引擎更加友好。但这些在后续的路由解析中是无用的,需要去除。</p>\n<p>ThinkJS 里提供了下面的配置可以去除 <code>pathname</code> 的前缀和后缀内容:</p>\n<pre><code class=\"lang-js\">export default {\n pathname_prefix: \'\', \n pathname_suffix: \'.html\',\n}\n</code></pre>\n<p>上面配置可以在 <code>src/common/config/config.js</code> 中进行修改。</p>\n<p>pathname 过滤时会自动去除左右的 <code>/</code>,该逻辑不受上面的配置影响。对 pathname 进行过滤后,拿到干净的 pathname 为 <code>zh-cn/doc/2.0/route</code>。</p>\n<p><code>注</code>:如果访问的 URL 是 <code>http://www.thinkjs.org/</code>,那么最后拿到干净的 pathname 则为空字符串。</p>\n<h3 id=\"-\">子域名部署</h3>\n<p>子域名部署请见<a href=\"./subdomain.html\">Middleware -> 子域名部署</a>。 </p>\n<h3 id=\"-\">路由识别</h3>\n<h4 id=\"-\">路由解析</h4>\n<p>路由识别默认根据 <code>模块/控制器/操作/参数1/参数1值/参数2/参数2值</code> 来识别过滤后的 pathname,如:pathname 为 <code>admin/group/detail</code>,那么识别后的结果为:</p>\n<ul>\n<li>module 为 <code>admin</code></li>\n<li>controller 为 <code>group</code></li>\n<li>action 为 <code>detail</code>,对应的方法名为 <code>detailAction</code></li>\n</ul>\n<p>如果项目里并没有 <code>admin</code> 这个模块或者这个模块被禁用了,那么识别后的结果为:</p>\n<ul>\n<li>module 为默认模块 <code>home</code></li>\n<li>controller 为 <code>admin</code></li>\n<li>action 为 <code>group</code>,对应的方法名为 <code>groupAction</code></li>\n<li>参数为 <code>{detail: \'\'}</code></li>\n</ul>\n<p>如果有多级控制器,那么会进行多级控制器的识别,然后才是 action 的识别。</p>\n<h4 id=\"-\">大小写转化</h4>\n<p>路由识别后,<code>module</code>、<code>controller</code> 和 <code>Action</code> 值都会自动转为小写。如果 Action 值里有 <code>_</code>,会作一些转化,如:假设识别后的 Controller 值为 <code>index</code>,Action 值为 <code>user_add</code>,那么对应的 Action 方法名为 <code>userAddAction</code>,但模版名还是 <code>index_user_add.html</code>。</p>\n<h3 id=\"-\">路由默认值</h3>\n<p>当解析 pathname 没有对应的值时,此时便使用对应的默认值。其中 module 默认值为 <code>home</code>,controller 默认值为 <code>index</code>,action 默认值为 <code>index</code>。</p>\n<p>这些值可以通过下面的配置进行修改,配置文件 <code>src/common/config/config.js</code>:</p>\n<pre><code class=\"lang-js\">export default {\n default_module: \'home\',\n default_controller: \'index\', \n default_action: \'index\',\n}\n</code></pre>\n<h3 id=\"-\">自定义路由</h3>\n<p>默认的路由虽然看起来清晰明了,解析起来也很简单,但看起来不够简洁。</p>\n<p>有时候需要更加简洁的路由,这时候就需要使用自定义路由解析了。如:文章的详细页面,默认路由可能是:<code>article/detail/id/10</code>,但我们想要的 URL 是 <code>article/10</code> 这种更简洁的方式。</p>\n<h5 id=\"-\">开启配置</h5>\n<p>开启自定义路由,需要在 <code>src/common/config/config.js</code> 开启如下的配置:</p>\n<pre><code class=\"lang-js\">export default {\n route_on: true\n}\n</code></pre>\n<h5 id=\"-\">路由规则</h5>\n<p>开启自定义路由后,就可以通过路由规则来定义具体的路由了,路由配置文件为: <code>src/common/config/route.js</code>,格式如下:</p>\n<pre><code class=\"lang-js\">export default [\n [\"规则1\", \"需要识别成的pathname\"],\n [\"规则2\", {\n get: \"get请求下需要识别成的pathname\",\n post: \"post请求下需要识别成的pathname\"\n }]\n];\n</code></pre>\n<p><code>注</code>:自定义路由每一条规则都是一个数组。(至于为什么不用对象,是因为正则路由下,正则不能作为对象的 key 直接使用)</p>\n<h5 id=\"-\">识别方式</h5>\n<p>自定义路由的匹配规则为:从前向后逐一匹配,如果命中到了该项规则,则不在向后匹配。</p>\n<hr>\n<p>ThinkJS 支持 3 种类型的自定义路由,即:正则路由,规则路由和静态路由。 </p>\n<h4 id=\"-\">正则路由</h4>\n<p>正则路由是采用正则表示式来定义路由的一种方式,依靠强大的正则表达式,能够定义非常灵活的路由规则。</p>\n<pre><code class=\"lang-js\">export default [\n [/^article\\/(\\d+)$/, \"home/article/detail?id=:1\"]\n];\n</code></pre>\n<p>上面的正则会匹配类似 <code>article/10</code> 这样的 pathname,识别后新的 pathname 为 <code>home/article/detail</code>,并且将捕获到的值赋值给参数 id ,这样在控制器里就可以通过 <code>this.get</code> 方法 来获取该值。</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n detailAction(){\n let id = this.get(\'id\');\n }\n}\n</code></pre>\n<p>如果正则里含有多个子分组,那么可以通过 <code>:1</code>,<code>:2</code>,<code>:3</code> 来获取对应的值。</p>\n<pre><code class=\"lang-js\">export default [\n [/^article\\/(\\d+)$/, {\n get: \"home/article/detail?id=:1\",\n delete: \"home/article/delete?id=:1\",\n post: \"home/article/save?id=:1\"\n }]\n];\n</code></pre>\n<h4 id=\"-\">规则路由</h4>\n<p>规则路由是一种字符串匹配方式,但支持含有一些动态的值。如:</p>\n<pre><code class=\"lang-js\">export default [\n [\'group/:year/:month\', \"home/group/list\"]\n]\n</code></pre>\n<p>假如访问的 URL 为 <code>http://www.example.com/group/2015/10</code>,那么会命中该项规则,得到的 pathname 为 <code>home/group/list</code>,同时会添加 2 个参数 <code>year</code> 和 <code>month</code>,这2个参数可以在控制器里通过 <code>this.get</code> 方法来获取。</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n listAction(){\n let year = this.get(\'year\');\n let month = this.get(\'month\');\n }\n}\n</code></pre>\n<h4 id=\"-\">静态路由</h4>\n<p>静态路由是一种纯字符串的完全匹配方式,写法和识别都很简单,功能也相对要弱很多。</p>\n<pre><code class=\"lang-js\">export default [\n [\"list\", \"home/article/list\"]\n]\n</code></pre>\n<p>假如访问的 URL 为 <code>http://www.example.com/list</code>,那么替换后的 pathname 为 <code>home/article/list</code>。</p>\n<h4 id=\"-\">优化路由性能</h4>\n<p>上面已经说到,自定义路由是个数组,数组每一项是个具体的路由规则,匹配时是从前向后逐一进行匹配。如果这个路由表比较大的话,可能会有性能问题。</p>\n<p>为了避免有性能问题,ThinkJS 提供了一种更加高效的自定义路由方式,按模块来配置路由。使用这种方式,路由配置格式跟上面稍微有所不同。</p>\n<h5 id=\"common-config-route-js\">common/config/route.js</h5>\n<p>使用这种方式后,通用模块里的路由配置不再配置具体的路由规则,而是配置哪些规则命中到哪个模块。如:</p>\n<pre><code class=\"lang-js\">export default {\n admin: { \n reg: /^admin/ //命中 admin 模块的正则\n },\n home: { //默认走 home 模块\n\n }\n}\n</code></pre>\n<h5 id=\"admin-config-route-js\">admin/config/route.js</h5>\n<p>admin 模块配置 admin 下的具体路由规则。</p>\n<pre><code class=\"lang-js\">export default [\n [/^admin\\/(?!api).*$/, \'admin/index\'],\n [/^admin\\/api\\/(\\w+?)(?:\\/([\\d,]*))?$/, \'admin/:1?id=:2&resource=:1\'],\n];\n</code></pre>\n<hr>\n<p>假设访问的 URL 为 <code>http://www.example.com/admin/api</code>,那么解析后的 pathname 为 <code>admin/api</code>,匹配 <code>common</code> 里的规则时会命中 <code>admin</code> 模块,然后再对 <code>admin</code> 模块下的路由规则进行逐一匹配。通过这种方式后就可以大大减少路由规则匹配的数量,提供匹配效率。</p>\n<h3 id=\"-\">自定义首页路由</h3>\n<p>首页默认执行的是 index controller 里的 indexAction。有些项目里希望对首页路由自定义,但配置 <code>[\'\', \'index/list\']</code> 并不管用。</p>\n<p>ThinkJS 为了性能上的考虑不支持对首页进行自定义路由,因为更多情况下首页是不用自定义的,并且访问的量比较大。如果支持自定义,每次都要把自定义路由过一遍,比较费性能。</p><p>文章来源:<span style=\"line-height: 1.8;\"><a href=\"http://www.thinkjs.org\" target=\"_blank\">http://www.thinkjs.org</a></span></p><p><br></p>', '', '前端汇', '2016-07-11 19:21:25', '68', '0', '0', '0', '18', 'thinkjs,nodejs', '1', '1', '', '8', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('242', '数据库配置', '', '<h3 id=\"-\">数据库配置</h3>\n<p>数据库默认配置如下,可以在 <code>src/common/config/db.js</code> 中进行修改:</p>\n<pre><code class=\"lang-js\">export default {\n type: \'mysql\',\n log_sql: true,\n log_connect: true,\n adapter: {\n mysql: {\n host: \'127.0.0.1\',\n port: \'\',\n database: \'\', //数据库名称\n user: \'\', //数据库帐号\n password: \'\', //数据库密码\n prefix: \'think_\',\n encoding: \'utf8\'\n },\n mongo: {\n\n }\n }\n};\n</code></pre>\n<p>也可以在其他模块下配置,这样请求该模块时就会使用对应的配置。</p>\n<h3 id=\"-\">数据表定义</h3>\n<p>默认情况下,模型名和数据表名都是一一对应的。假设数据表前缀是 <code>think_</code>,那么 <code>user</code> 模型对应的数据表为 <code>think_user</code>,<code>user_group</code> 模型对应的数据表为 <code>think_user_group</code>。</p>\n<p>如果需要修改,可以通过下面 2 个属性进行:</p>\n<ul>\n<li><code>tablePrefix</code> 表前缀</li>\n<li><code>tableName</code> 表名,不包含前缀</li>\n</ul>\n<h4 id=\"es6-\">ES6 方式</h4>\n<pre><code class=\"lang-js\">export default class extends think.model.base {\n init(...args){\n super.init(...args);\n this.tablePrefix = \'\'; //将数据表前缀设置为空\n this.tableName = \'user2\'; //将对应的数据表名设置为 user2\n }\n}\n</code></pre>\n<h4 id=\"-\">动态创建类方式</h4>\n<pre><code class=\"lang-js\">module.exports = think.model({\n tablePrefix: \'\', //直接通过属性来设置表前缀和表名\n tableName: \'user2\',\n init: function(){\n this.super(\'init\', arguments);\n }\n})\n</code></pre>\n<h3 id=\"-\">修改主键</h3>\n<p>模型默认的主键为 <code>id</code>,如果数据表里的 Primary Key 设置不是 id,那么需要在模型中设置主键。</p>\n<pre><code class=\"lang-js\">export default class extends think.model.base {\n init(...args){\n super.init(...args);\n this.pk = \'user_id\'; //将主键字段设置为 user_id\n }\n}\n</code></pre>\n<p><code>count</code>,<code>sum</code>,<code>min</code>,<code>max</code> 等很多查询操作都会用到主键,用到这些操作时需要修改主键。</p>\n<h3 id=\"-\">分布式数据库</h3>\n<p>大的系统中,经常有多个数据库用来做读写分离,从而提高数据库的操作性能。ThinkJS 里可以通过 <code>parser</code> 来自定义解析,可以在文件 <code>src/common/config/db.js</code> 中修改:</p>\n<pre><code class=\"lang-js\">//读配置\nconst MYSQL_READ = {\n host: \'10.0.10.1\',\n}\n//写配置\nconst MYSQL_WRITE = {\n host: \'10.0.10.2\'\n}\nexport default {\n host: \'127.0.0.l\',\n adapter: {\n mysql: { \n parser: function(options){ //mysql 的配置解析方法\n let sql = options.sql; //接下来要执行的 SQL 语句\n if(sql.indexOf(\'SELECT\') === 0){ //SELECT 查询\n return MYSQL_READ;\n }\n return MYSQL_WRITE;\n }\n }\n }\n}\n</code></pre>\n<p>parser 解析的参数 <code>options</code> 里会包含接下来要执行的 SQL 语句,这样就很方便的在 parser 里返回对应的数据库配置。</p><p>文章来源:<a href=\"http://www.thinkjs.org/\">http://www.thinkjs.org</a></p><p><br></p>', '', '前端汇', '2016-07-14 10:02:04', '42', '0', '0', '0', '18', 'thinkjs,nodejs', '1', '1', '', '8', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('243', 'CRUD 操作', '', '<h3 id=\"-\">添加数据</h3>\n<h4 id=\"-\">添加一条数据</h4>\n<p>使用 <code>add</code> 方法可以添加一条数据,返回值为插入数据的 id。如:</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n * addAction(){\n let model = this.model(\'user\');\n let insertId = yield model.add({name: \'xxx\', pwd: \'yyy\'});\n }\n}\n</code></pre>\n<h4 id=\"-\">添加多条数据</h4>\n<p>使用 <code>addMany</code> 方法可以添加多条数据,如:</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n * addAction(){\n let model = this.model(\'user\');\n let insertId = yield model.addMany([\n {name: \'xxx\', pwd: \'yyy\'},\n {name: \'xxx1\', pwd: \'yyy1\'}\n ]);\n }\n}\n</code></pre>\n<h4 id=\"thenadd\">thenAdd</h4>\n<p>数据库设计时,我们经常需要把某个字段设为唯一,表示这个字段值不能重复。这样添加数据的时候只能先去查询下这个数据值是否存在,如果不存在才进行插入操作。</p>\n<p>模型中提供了 <code>thenAdd</code> 方法简化这一操作。</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n * addAction(){\n let model = this.model(\'user\');\n //第一个参数为要添加的数据,第二个参数为添加的条件,根据第二个参数的条件查询无相关记录时才会添加\n let result = yield model.thenAdd({name: \'xxx\', pwd: \'yyy\'}, {name: \'xxx\'});\n // result returns {id: 1000, type: \'add\'} or {id: 1000, type: \'exist\'}\n }\n}\n</code></pre>\n<h3 id=\"-\">更新数据</h3>\n<h4 id=\"update\">update</h4>\n<p>更新数据使用 <code>update</code> 方法,返回值为影响的行数。如:</p>\n<pre><code class=\"lang-js\">export default class extends think.controlle.base {\n * updateAction(){\n let model = this.model(\'user\');\n let affectedRows = yield model.where({name: \'thinkjs\'}).update({email: \'[email protected]\'});\n }\n}\n</code></pre>\n<p>默认情况下更新数据必须添加 where 条件,以防止误操作导致所有数据被错误的更新。如果确认是更新所有数据的需求,可以添加 <code>1=1</code> 的 where 条件进行,如:</p>\n<pre><code class=\"lang-js\">export default class extends think.controlle.base {\n * updateAction(){\n let model = this.model(\'user\');\n let affectedRows = yield model.where(\'1=1\').update({email: \'[email protected]\'});\n }\n}\n</code></pre>\n<h4 id=\"increment\">increment</h4>\n<p>可以通过 <code>increment</code> 方法给符合条件的字段增加特定的值,如:</p>\n<pre><code class=\"lang-js\">export default class extends think.model.base {\n updateViewNums(id){\n return this.where({id: id}).increment(\'view_nums\', 1); //将阅读数加 1\n }\n}\n</code></pre>\n<h4 id=\"decrement\">decrement</h4>\n<p>可以通过 <code>decrement</code> 方法给符合条件的字段减少特定的值,如:</p>\n<pre><code class=\"lang-js\">export default class extends think.model.base {\n updateViewNums(id){\n return this.where({id: id}).decrement(\'coins\', 10); //将金币减 10 \n }\n}\n</code></pre>\n<h3 id=\"-\">查询数据</h3>\n<p>模型中提供了多种方式来查询数据,如:查询单条数据,查询多条数据,读取字段值,读取最大值,读取总条数等。</p>\n<h4 id=\"-\">查询单条数据</h4>\n<p>可以使用 <code>find</code> 方法查询单条数据,返回值为对象。如:</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n * listAction(){\n let model = this.model(\'user\');\n let data = yield model.where({name: \'thinkjs\'}).find();\n //data returns {name: \'thinkjs\', email: \'[email protected]\', ...}\n }\n}\n</code></pre>\n<p>如果数据表没有对应的数据,那么返回值为空对象 <code>{}</code>,可以通过 <code>think.isEmpty</code> 方法来判断返回值是否为空。</p>\n<h4 id=\"-\">查询多条数据</h4>\n<p>可以使用 <code>select</code> 方法查询多条数据,返回值为数据。如:</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n * listAction(){\n let model = this.model(\'user\');\n let data = yield model.limit(2).select();\n //data returns [{name: \'thinkjs\', email: \'[email protected]\'}, ...]\n }\n}\n</code></pre>\n<p>如果数据表中没有对应的数据,那么返回值为空数组 <code>[]</code>,可以通过 <code>think.isEmpty</code> 方法来判断返回值是否为空。</p>\n<h4 id=\"-\">分页查询数据</h4>\n<p>页面中经常遇到按分页来展现某些数据,这种情况下就需要先查询总的条数,然后在查询当前分页下的数据。查询完数据后还要计算有多少页。模型中提供了 <code>countSelect</code> 方法来方便这一操作,会自动进行总条数的查询。</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n * listAction(){\n let model = this.model(\'user\');\n let data = yield model.page(this.get(\'page\'), 10).countSelect();\n }\n}\n</code></pre>\n<p>返回值格式如下:</p>\n<pre><code class=\"lang-js\"> {\n numsPerPage: 10, //每页显示的条数\n currentPage: 1, //当前页\n count: 100, //总条数\n totalPages: 10, //总页数\n data: [{ //当前页下的数据列表\n name: \'thinkjs\',\n email: \'[email protected]\'\n }, ...]\n }\n</code></pre>\n<p>如果传递的当前页数超过了页数范围,可以通过传递参数进行修正。<code>true</code> 为修正到第一页, <code>false</code> 为修正到最后一页,即: <code>countSelect(true)</code> 或 <code>countSelect(false)</code>。</p>\n<p>如果总条数无法直接查询,可以将总条数作为参数传递进去,如: <code>countSelect(1000)</code>,表示总条数有1000条。</p>\n<h4 id=\"count\">count</h4>\n<p>可以通过 <code>count</code> 方法查询符合条件的总条数,如:</p>\n<pre><code class=\"lang-js\">export default class extends think.model.base {\n getMin(){\n //查询 status 为 publish 的总条数\n return this.where({status: \'publish\'}).count();\n }\n}\n</code></pre>\n<h4 id=\"sum\">sum</h4>\n<p>可以通过 <code>sum</code> 方法查询符合条件的字段总和,如:</p>\n<pre><code class=\"lang-js\">export default class extends think.model.base {\n getMin(){\n //查询 status 为 publish 字段 view_nums 的总和\n return this.where({status: \'publish\'}).sum(\'view_nums\');\n }\n}\n</code></pre>\n<h4 id=\"max\">max</h4>\n<p>可以通过 <code>max</code> 方法查询符合条件的最大值,如:</p>\n<pre><code class=\"lang-js\">export default class extends think.model.base {\n getMin(){\n //查询 status 为 publish,字段 comments 的最大值\n return this.where({status: \'publish\'}).max(\'comments\');\n }\n}\n</code></pre>\n<h4 id=\"min\">min</h4>\n<p>可以通过 <code>min</code> 方法查询符合条件的最小值,如:</p>\n<pre><code class=\"lang-js\">export default class extends think.model.base {\n getMin(){\n //查询 status 为 publish,字段 comments 的最小值\n return this.where({status: \'publish\'}).min(\'comments\');\n }\n}\n</code></pre>\n<h4 id=\"-\">查询缓存</h4>\n<p>为了性能优化,项目中经常要对一些从数据库中查询的数据进行缓存。如果手工将查询的数据进行缓存,势必比较麻烦,模型中直接提供了 <code>cache</code> 方法来设置查询缓存。如:</p>\n<pre><code class=\"lang-js\">export default class extends think.model.base {\n getList(){\n //设定缓存 key 和缓存时间\n return this.cache(\'get_list\', 3600).where({id: {\'>\': 100}}).select();\n }\n}\n</code></pre>\n<p>上面的代码为对查询结果进行缓存,如果已经有了缓存,直接从缓存里读取,没有的话才从数据库里查询。缓存保存的 key 为 <code>get_list</code>,缓存时间为一个小时。</p>\n<p>也可以不指定缓存 key,这样会自动根据 SQL 语句生成一个缓存 key。如:</p>\n<pre><code class=\"lang-js\">export default class extends think.model.base {\n getList(){\n //只设定缓存时间\n return this.cache(3600).where({id: {\'>\': 100}}).select();\n }\n}\n</code></pre>\n<h5 id=\"-\">缓存配置</h5>\n<p>缓存配置为模型配置中的 <code>cache</code> 字段,如:</p>\n<pre><code class=\"lang-js\">export default {\n cache: {\n on: true,\n type: \'\', \n timeout: 3600\n }\n}\n</code></pre>\n<ul>\n<li><code>on</code> 数据库缓存配置的总开关,关闭后即使程序中调用 <code>cache</code> 方法也无效。</li>\n<li><code>type</code> 缓存配置类型,默认为内存,支持的缓存类型请见 <a href=\"./adapter_cache.html\">Adapter -> Cache</a>。</li>\n<li><code>timeout</code> 默认缓存时间。</li>\n</ul>\n<h3 id=\"-\">删除数据</h3>\n<p>可以使用 <code>delete</code> 方法来删除数据,返回值为影响的行数。如:</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n * deleteAction(){\n let model = this.model(\'user\');\n let affectedRows = yield model.where({id: [\'>\', 100]}).delete();\n }\n}\n</code></pre>\n<hr>\n<p>模型中更多的操作方式请见相关的 <a href=\"./api_model.html\">API -> model</a>。</p><p>文章来源:<a href=\"http://www.thinkjs.org/\">http://www.thinkjs.org</a></p><p><br></p>', '', '前端汇', '2016-07-14 10:02:10', '42', '0', '0', '0', '18', 'thinkjs,nodejs', '1', '1', '', '8', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('244', '事务', '', '<p>模型中提供了对事务操作的支持,但前提需要数据库支持事务。</p>\n<p><code>Mysql</code> 中的 <code>InnoDB</code> 和 <code>BDB</code> 存储引擎支持事务,如果在 Mysql 下使用事务的话,需要将数据库的存储引擎设置为 InnoDB 或 BDB。</p>\n<p><code>SQLite</code> 直接支持事务。</p>\n<h3 id=\"-\">使用事务</h3>\n<p>模型中提供了 <code>startTrans</code>,<code>commit</code> 和 <code>rollback</code> 3 种方法来操作事务。</p>\n<ul>\n<li><code>startTrans</code> 开启事务</li>\n<li><code>commit</code> 正常操作后,提交事务</li>\n<li><code>rollback</code> 操作异常后进行回滚</li>\n</ul>\n<h4 id=\"es6-\">ES6 方式</h4>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n * indexAction(){\n let model = this.model(\'user\');\n try{\n yield model.startTrans();\n let userId = yield model.add({name: \'xxx\'});\n let insertId = yield this.model(\'user_group\').add({user_id: userId, group_id: 1000});\n yield model.commit();\n }catch(e){\n yield model.rollback();\n }\n }\n}\n</code></pre>\n<h4 id=\"-\">动态创建类的方式</h4>\n<pre><code class=\"lang-js\">module.exports = think.controller({\n indexAction: function(self){\n var model = this.model(\'user\');\n return model.startTrans().then(function(){\n return model.add({name: \'xxx\'});\n }).then(function(userId){\n return self.model(\'user_group\').add({user_id: userId, group_id: 1000})\n }).then(function(){\n return self.commit();\n }).catch(function(err){\n return self.rollback();\n });\n }\n})\n</code></pre>\n<h3 id=\"transaction-\">transaction 方法</h3>\n<p>使用事务时,要一直使用 <code>startTrans</code>,<code>commit</code> 和 <code>rollback</code> 这 3 个方法进行操作,使用起来有一些不便。为了简化这一操作,模型中提供了 <code>transaction</code> 方法来更加方便的处理事务。</p>\n<h4 id=\"es6-\">ES6 方式</h4>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n * indexAction(self){\n let model = this.model(\'user\');\n let insertId = yield model.transaction( function * (){\n let userId = yield model.add({name: \'xxx\'});\n return yield self.model(\'user_group\').add({user_id: userId, group_id: 1000});\n })\n }\n}\n</code></pre>\n<p>注:Arrow Function 无法和 <code>*/yield</code> 一起写,所以上面为 <code>function *</code>。如果想使用 Arrow Function,可以使用 async,如: <code>async () => {}</code>。</p>\n<h4 id=\"-\">使用动态创建类的方式</h4>\n<pre><code class=\"lang-js\">module.exports = think.controller({\n indexAction: function(self){\n var model = this.model(\'user\');\n return model.transaction(function(){\n return model.add({name: \'xxx\'}).then(function(userId){\n return self.model(\'user_group\').add({user_id: userId, group_id: 1000});\n });\n }).then(function(insertId){\n\n }).catch(function(err){\n\n })\n }\n})\n</code></pre>\n<hr>\n<p>transaction 接收一个回调函数,这个回调函数中处理真正的逻辑,并需要返回。</p><p>文章来源:<a href=\"http://www.thinkjs.org/\">http://www.thinkjs.org</a></p><p><br></p>', '', '前端汇', '2016-07-14 10:02:15', '39', '0', '0', '0', '18', 'thinkjs,nodejs', '1', '1', '', '8', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('245', '关联模型', '', '<p>数据库中表经常会跟其他数据表有关联,数据操作时需要连同关联的表一起操作。如:一个博客文章会有分类、标签、评论,以及属于哪个用户。</p>\n<p>ThinkJS 中支持关联模型,让处理这类操作非常简单。</p>\n<h3 id=\"-\">支持的类型</h3>\n<p>关联模型中支持常见的 4 类关联关系。如:</p>\n<ul>\n<li><code>think.model.HAS_ONE</code> 一对一模型</li>\n<li><code>think.model.BELONG_TO</code> 一对一属于</li>\n<li><code>think.model.HAS_MANY</code> 一对多</li>\n<li><code>think.model.MANY_TO_MANY</code> 多对多</li>\n</ul>\n<h3 id=\"-\">创建关联模型</h3>\n<p>可以通过命令 <code>thinkjs model [name] --relation</code> 来创建关联模型。如:</p>\n<pre><code class=\"lang-sh\">thinkjs model home/post --relation\n</code></pre>\n<p>会创建模型文件 <code>src/home/model/post.js</code>。</p>\n<h3 id=\"-\">指定关联关系</h3>\n<p>可以通过 <code>relation</code> 属性来指定关联关系。如:</p>\n<pre><code class=\"lang-js\">export default class extends think.model.relation {\n init(...args){\n super.init(...args);\n //通过 relation 属性指定关联关系,可以指定多个关联关系\n this.relation = {\n cate: {},\n comment: {} \n };\n }\n}\n</code></pre>\n<p>也可以直接使用 ES7 里的语法直接定义 <code>relation</code> 属性。如:</p>\n<pre><code class=\"lang-js\">export default class extends think.model.relation {\n\n //直接定义 relation 属性\n relation = {\n cate: {},\n comment: {} \n };\n\n init(...args){\n super.init(...args);\n }\n}\n</code></pre>\n<h4 id=\"-\">单个关系模型的数据格式</h4>\n<pre><code class=\"lang-js\">export default class extends think.model.relation {\n init(...args){\n super.init(...args);\n this.relation = {\n cate: {\n type: think.model.MANY_TO_MANY, //relation type\n model: \'\', //model name\n name: \'profile\', //data name\n key: \'id\', \n fKey: \'user_id\', //forign key\n field: \'id,name\',\n where: \'name=xx\',\n order: \'\',\n limit: \'\',\n rModel: \'\',\n rfKey: \'\'\n },\n };\n }\n}\n</code></pre>\n<p>各个字段含义如下:</p>\n<ul>\n<li><code>type</code> 关联关系类型</li>\n<li><code>model</code> 关联表的模型名,默认为配置的 key,这里为 <code>cate</code></li>\n<li><code>name</code> 对应的数据字段名,默认为配置的 key,这里为 <code>cate</code></li>\n<li><code>key</code> 当前模型的关联 key</li>\n<li><code>fKey</code> 关联表与只对应的 key</li>\n<li><code>field</code> 关联表查询时设置的 field,如果需要设置,必须包含 <code>fKey</code> 对应的值</li>\n<li><code>where</code> 关联表查询时设置的 where 条件</li>\n<li><code>order</code> 关联表查询时设置的 order</li>\n<li><code>limit</code> 关联表查询时设置的 limit</li>\n<li><code>page</code> 关联表查询时设置的 page</li>\n<li><code>rModel</code> 多对多下,对应的关联关系模型名</li>\n<li><code>rfKey</code> 多对多下,对应里的关系关系表对应的 key</li>\n</ul>\n<p>如果只用设置关联类型,不用设置其他字段信息,可以通过下面简单的方式:</p>\n<pre><code class=\"lang-js\">export default class extends think.model.relation {\n init(...args){\n super.init(...args);\n this.relation = {\n cate: think.model.MANY_TO_MANY\n };\n }\n}\n</code></pre>\n<h4 id=\"has_one\">HAS_ONE</h4>\n<p>一对一关联,表示当前表含有一个附属表。</p>\n<p>假设当前表的模型名为 <code>user</code>,关联表的模型名为 <code>info</code>,那么配置中字段 <code>key</code> 的默认值为 <code>id</code>,字段 <code>fKey</code> 的默认值为 <code>user_id</code>。</p>\n<pre><code class=\"lang-js\">export default class extends think.model.relation {\n init(..args){\n super.init(...args);\n this.relation = {\n info: think.model.HAS_ONE\n };\n }\n}\n</code></pre>\n<p>执行查询操作时,可以得到类似如下的数据:</p>\n<pre><code class=\"lang-js\">[\n {\n id: 1,\n name: \'111\',\n info: { //关联表里的数据信息\n user_id: 1,\n desc: \'info\'\n }\n }, ...]\n</code></pre>\n<h4 id=\"belong_to\">BELONG_TO</h4>\n<p>一对一关联,属于某个关联表,和 HAS_ONE 是相反的关系。</p>\n<p>假设当前模型名为 <code>info</code>,关联表的模型名为 <code>user</code>,那么配置字段 <code>key</code> 的默认值为 <code>user_id</code>,配置字段 <code>fKey</code> 的默认值为 <code>id</code>。</p>\n<pre><code class=\"lang-js\">export default class extends think.model.relation {\n init(..args){\n super.init(...args);\n this.relation = {\n user: think.model.BELONG_TO\n };\n }\n}\n</code></pre>\n<p>执行查询操作时,可以得到类似下面的数据:</p>\n<pre><code class=\"lang-js\">[\n {\n id: 1,\n user_id: 1,\n desc: \'info\',\n user: {\n name: \'thinkjs\'\n }\n }, ...\n]\n</code></pre>\n<h4 id=\"has_many\">HAS_MANY</h4>\n<p>一对多的关系。</p>\n<p>加入当前模型名为 <code>post</code>,关联表的模型名为 <code>comment</code>,那么配置字段 <code>key</code> 默认值为 <code>id</code>,配置字段 <code>fKey</code> 默认值为 <code>post_id</code>。</p>\n<pre><code class=\"lang-js\">\'use strict\';\n/**\n * relation model\n */\nexport default class extends think.model.relation {\n init(...args){\n super.init(...args);\n\n this.relation = {\n comment: {\n type: think.model.HAS_MANY\n }\n };\n }\n}\n</code></pre>\n<p>执行查询数据时,可以得到类似下面的数据:</p>\n<pre><code class=\"lang-js\">[{\n id: 1,\n title: \'first post\',\n content: \'content\',\n comment: [{\n id: 1,\n post_id: 1,\n name: \'welefen\',\n content: \'first comment\'\n }, ...]\n}, ...]\n</code></pre>\n<p>如果关联表的数据需要分页查询,可以通过 <code>page</code> 参数进行,如:</p>\n<pre><code class=\"lang-js\">\'use strict\';\n/**\n * relation model\n */\nexport default class extends think.model.relation {\n init(...args){\n super.init(...args);\n\n this.relation = {\n comment: {\n type: think.model.HAS_MANY\n }\n };\n }\n getList(page){\n return this.setRelation(\'comment\', {page: page}).select();\n }\n}\n</code></pre>\n<p>除了用 <code>setRelation</code> 来合并参数外,可以将参数设置为函数,合并参数时会自动执行该函数。</p>\n<h4 id=\"many_to_many\">MANY_TO_MANY</h4>\n<p>多对多关系。</p>\n<p>假设当前模型名为 <code>post</code>,关联模型名为 <code>cate</code>,那么需要一个对应的关联关系表。配置字段 <code>rModel</code> 默认值为 <code>post_cate</code>,配置字段 <code>rfKey</code> 默认值为 <code>cate_id</code>。</p>\n<pre><code class=\"lang-js\">\'use strict\';\n/**\n * relation model\n */\nexport default class extends think.model.relation {\n init(...args){\n super.init(...args);\n\n this.relation = {\n cate: {\n type: think.model.MANY_TO_MANY,\n rModel: \'post_cate\',\n rfKey: \'cate_id\'\n }\n };\n }\n}\n</code></pre>\n<p>查询出来的数据结构为:</p>\n<pre><code class=\"lang-js\">[{\n id: 1,\n title: \'first post\',\n cate: [{\n id: 1,\n name: \'cate1\',\n post_id: 1\n }, ...]\n}, ...]\n</code></pre>\n<h4 id=\"-\">关联死循环</h4>\n<p>如果 2 个关联表,一个设置对方为 HAS_ONE,另一个设置对方为 BELONG_TO,这样在查询关联表的数据时会将当前表又查询了一遍,并且会再次查询关联表,最终导致死循环。</p>\n<p>可以在配置里设置 <code>relation</code> 字段关闭关联表的关联查询功能,从而避免死循环。如:</p>\n<pre><code class=\"lang-js\">export default class extends think.model.relation {\n init(..args){\n super.init(...args);\n this.relation = {\n user: {\n type: think.model.BELONG_TO,\n relation: false //关联表 user 查询时关闭关联查询\n }\n };\n }\n}\n</code></pre>\n<p>也可以设置只关闭当前模型的关联关系,如:</p>\n<pre><code class=\"lang-js\">export default class extends think.model.relation {\n init(..args){\n super.init(...args);\n this.relation = {\n user: {\n type: think.model.BELONG_TO,\n relation: \'info\' //关联表 user 查询时关闭对 info 模型的关联关系\n }\n };\n }\n}\n</code></pre>\n<h3 id=\"-\">临时关闭关联关系</h3>\n<p>设置关联关系后,查询等操作都会自动查询关联表的数据。如果某些情况下不需要查询关联表的数据,可以通过 <code>setRelation</code> 方法临时关闭关联关系查询。</p>\n<h4 id=\"-\">全部关闭</h4>\n<p>通过 <code>setRelation(false)</code> 关闭所有的关联关系查询。</p>\n<pre><code class=\"lang-js\">export default class extends think.model.relation {\n init(...args){\n super.init(...args);\n this.relation = {\n comment: think.model.HAS_MANY,\n cate: think.model.MANY_TO_MANY\n };\n }\n getList(){\n return this.setRelation(false).select();\n }\n}\n</code></pre>\n<h4 id=\"-\">部分启用</h4>\n<p>通过 <code>setRelation(\'comment\')</code> 只查询 <code>comment</code> 的关联数据,不查询其他的关联关系数据。</p>\n<pre><code class=\"lang-js\">export default class extends think.model.relation {\n init(...args){\n super.init(...args);\n this.relation = {\n comment: think.model.HAS_MANY,\n cate: think.model.MANY_TO_MANY\n };\n }\n getList2(){\n return this.setRelation(\'comment\').select();\n }\n}\n</code></pre>\n<h4 id=\"-\">部分关闭</h4>\n<p>通过 <code>setRelation(\'comment\', false)</code> 关闭 <code>comment</code> 的关联关系数据查询。</p>\n<pre><code class=\"lang-js\">export default class extends think.model.relation {\n init(...args){\n super.init(...args);\n this.relation = {\n comment: think.model.HAS_MANY,\n cate: think.model.MANY_TO_MANY\n };\n }\n getList2(){\n return this.setRelation(\'comment\', false).select();\n }\n}\n</code></pre>\n<h4 id=\"-\">重新全部启用</h4>\n<p>通过 <code>setRelation(true)</code> 重新启用所有的关联关系数据查询。</p>\n<pre><code class=\"lang-js\">export default class extends think.model.relation {\n init(...args){\n super.init(...args);\n this.relation = {\n comment: think.model.HAS_MANY,\n cate: think.model.MANY_TO_MANY\n };\n }\n getList2(){\n return this.setRelation(true).select();\n }\n}\n</code></pre>\n<h3 id=\"mongo-\">mongo 关联模型</h3>\n<p>该关联模型的操作不适合 mongo 模型,mongo 的关联模型请见 <a href=\"https://docs.mongodb.org/manual/tutorial/model-embedded-one-to-one-relationships-between-documents/\">https://docs.mongodb.org/manual/tutorial/model-embedded-one-to-one-relationships-between-documents/</a>。</p><p>文章来源:<a href=\"http://www.thinkjs.org/\">http://www.thinkjs.org</a></p>\n<p><br></p>', '', '前端汇', '2016-07-14 10:02:26', '37', '0', '0', '0', '18', 'thinkjs,nodejs', '1', '1', '', '8', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('246', 'Mysql', '', '<p>ThinkJS 对 Mysql 操作有很好的支持,底层使用的库为 <a href=\"https://www.npmjs.com/package/mysql\">https://www.npmjs.com/package/mysql</a>。</p>\n<h3 id=\"-\">连接池</h3>\n<p>默认连接 Mysql 始终只有一个连接,如果想要多个连接,可以使用连接池的功能。修改配置 <code>sr/common/config/db.js</code>,如:</p>\n<pre><code class=\"lang-js\">export default {\n connectionLimit: 10 //建立 10 个连接\n}\n</code></pre>\n<h3 id=\"socketpath\">socketPath</h3>\n<p>默认情况下是通过 host 和 port 来连接 Mysql 的,如果想通过 unix domain socket 来连接 Mysql,可以设置下面的配置:</p>\n<pre><code class=\"lang-js\">export default {\n socketPath: \'/tmp/mysql.socket\'\n}\n</code></pre>\n<h3 id=\"ssl-options\">SSL options</h3>\n<p>可以通过下面的配置来指定通过 SSL 来连接:</p>\n<pre><code class=\"lang-js\">export default {\n ssl: {\n ca: fs.readFileSync(__dirname + \'/mysql-ca.crt\')\n }\n}\n</code></pre>\n<h3 id=\"-emoji-\">数据库支持 emoji 表情</h3>\n<p>数据库的编码一般会设置为 <code>utf8</code>,但 utf8 并不支持 emoji 表情,如果需要数据库支持 emoji 表情,需要将数据库编码设置为 <code>utf8mb4</code>。</p>\n<p>同时需要将 <code>src/common/config/db.js</code> 里的 <code>encoding</code> 配置值修改为 <code>utf8mb4</code>。如:</p>\n<pre><code class=\"lang-js\">export default {\n encoding: \'utf8mb4\'\n}\n</code></pre>\n<h3 id=\"error-handshake-inactivity-timeout\">Error: Handshake inactivity timeout</h3>\n<p>在某些 Node.js 版本下(如:4.2.0)连接 Mysql 时会出现下面的错误:</p>\n<pre><code class=\"lang-text\">Error: Handshake inactivity timeout\nat Handshake.sequence.on.on.on.on.on.self._connection._startTLS.err.code (/home/***/node_modules/mysql/lib/protocol/Protocol.js:154:17)\nat Handshake.emit (events.js:92:17)\nat Handshake._onTimeout (/home/***/node_modules/mysql/lib/protocol/sequences/Sequence.js:116:8)\nat Timer.listOnTimeout [as ontimeout] (timers.js:112:15)\n --------------------\n at Protocol._enqueue (/home/***/node_modules/mysql/lib/protocol/Protocol.js:135:48)\n at Protocol.handshake (/home/***/node_modules/mysql/lib/protocol/Protocol.js:52:41)\n at PoolConnection.connect (/home/***/node_modules/mysql/lib/Connection.js:119:18)\n at Pool.getConnection (/home/***/node_modules/mysql/lib/Pool.js:45:23)\n at Object.exports.register (/home/***/node_modules/hapi-plugin-mysql/lib/index.js:40:27)\n at /home/***/node_modules/hapi/lib/plugin.js:242:14\n at iterate (/home/***/node_modules/hapi/node_modules/items/lib/index.js:35:13)\n at done (/home/***/node_modules/hapi/node_modules/items/lib/index.js:27:25)\n at Object.exports.register (/home/***/node_modules/lout/lib/index.js:95:5)\n at /home/***/node_modules/hapi/lib/plugin.js:242:14\n</code></pre>\n<p><code>解决方案:</code> 将 Node.js 升级到最新版本即可解决。</p><p>文章来源:<a href=\"http://www.thinkjs.org/\">http://www.thinkjs.org</a></p><p><br></p>', '', '前端汇', '2016-07-14 10:02:31', '47', '0', '0', '0', '18', 'thinkjs,nodejs', '1', '1', '', '8', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('247', 'MongoDB', '', '<p>ThinkJS 支持使用 MongoDB 数据库,底层使用 <a href=\"https://www.npmjs.com/package/mongodb\">mongodb</a> 模块。</p>\n<p>如果想在项目中使用 Mongoose 来代替默认的 Mongo 模型,可以参见: <a href=\"http://welefen.com/post/use-mongoose-in-thinkjs.html\">http://welefen.com/post/use-mongoose-in-thinkjs.html</a></p>\n<h3 id=\"-\">配置</h3>\n<p>使用 MongoDB 数据库,需要将模型中的配置 <code>type</code> 改为 <code>mongo</code>,修改配置文件 <code>src/common/config/db.js</code>:</p>\n<pre><code class=\"lang-js\">export default {\n type: \'mongo\'\n}\n</code></pre>\n<h4 id=\"-host\">多 HOST</h4>\n<p>可以将配置里的 <code>host</code> 字段设置为数据支持多 host 的功能,如:</p>\n<pre><code class=\"lang-js\">export default {\n type: \'mongo\',\n adapter: {\n mongo: {\n host: [\'10.0.0.1\', \'10.0.0.2\'],\n port: [\'1234\', \'5678\']\n }\n }\n}\n</code></pre>\n<p><code>注</code>:此配置项在 <code>2.0.14</code> 版本中支持。</p>\n<h4 id=\"-\">配置选项</h4>\n<p>如果要在连接 MongoDB 服务的时候添加额外的参数,可以通过在 <code>options</code> 里追加,如:</p>\n<pre><code class=\"lang-js\">export default {\n type: \'mongo\',\n adapter: {\n mongo: {\n options: {\n authSource: \'admin\',\n replicaSet: \'xxx\'\n }\n }\n }\n}\n</code></pre>\n<p>上面的配置后,连接 MongoDB 的 URL 变成类似于 <code>mongodb://127.0.0.1:27017/?authSource=admin&replicaSet=xxx</code>。</p>\n<p>更多额外的配置请见 <a href=\"http://mongodb.github.io/node-mongodb-native/2.0/reference/connecting/connection-settings/\">http://mongodb.github.io/node-mongodb-native/2.0/reference/connecting/connection-settings/</a>。</p>\n<h3 id=\"-\">创建模型</h3>\n<p>可以通过命令 <code>thinkjs model [name] --mongo</code> 来创建模型,如:</p>\n<pre><code class=\"lang-js\">thinkjs model user --mongo\n</code></pre>\n<p>执行完成后,会创建文件 <code>src/common/model/user.js</code>。如果想创建在其他模块下,需要带上具体的模块名。如:</p>\n<pre><code class=\"lang-js\">thinkjs model home/user --mongo\n</code></pre>\n<p>会在 <code>home</code> 模块下创建模型文件,文件为 <code>src/home/model/user.js</code>。</p>\n<h3 id=\"-\">模型继承</h3>\n<p>模型需要继承 <code>think.model.mongo</code> 类,如果当前类不是继承该类,需要手动修改。</p>\n<h4 id=\"es6-\">ES6 语法</h4>\n<pre><code class=\"lang-js\">export default class extends think.model.mongo {\n\n}\n</code></pre>\n<h4 id=\"-\">动态创建类的方式</h4>\n<pre><code class=\"lang-js\">module.exports = think.model(\'mongo\', {\n\n})\n</code></pre>\n<h3 id=\"crud-\">CRUD 操作</h3>\n<p>CRUD 操作和 Mysql 中接口相同,具体请见 <a href=\"./model_intro.html#toc-d84\">模型 -> 介绍</a>。</p>\n<h3 id=\"-\">创建索引</h3>\n<p>mongo 模型可以配置索引,在增删改查操作之前模型会自动去创建索引,配置放在 <code>indexes</code> 属性里。如:</p>\n<pre><code class=\"lang-js\">export default class extends think.model.mongo {\n init(...args){\n super.init(...args);\n //配置索引\n this.indexes = { \n\n }\n }\n}\n</code></pre>\n<h4 id=\"-\">单字段索引</h4>\n<pre><code class=\"lang-js\">export default class extends think.model.mongo {\n init(...args){\n super.init(...args);\n //配置索引\n this.indexes = { \n name: 1\n }\n }\n}\n</code></pre>\n<h4 id=\"-\">唯一索引</h4>\n<p>通过 <code>$unique</code> 来指定为唯一索引,如:</p>\n<pre><code class=\"lang-js\">export default class extends think.model.mongo {\n init(...args){\n super.init(...args);\n //配置索引\n this.indexes = { \n name: {$unique: 1}\n }\n }\n}\n</code></pre>\n<h4 id=\"-\">多字段索引</h4>\n<p>可以将多个字段联合索引,如:</p>\n<pre><code class=\"lang-js\">export default class extends think.model.mongo {\n init(...args){\n super.init(...args);\n //配置索引\n this.indexes = { \n email: 1\n test: {\n name: 1,\n title: 1,\n $unique: 1\n }\n }\n }\n}\n</code></pre>\n<h3 id=\"-\">获取索引</h3>\n<p>可以通过方法 <code>getIndexes</code> 获取创建的索引。如:</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n async indexAction(){\n let model = this.model(\'user\');\n let indexes = await model.getIndexes();\n }\n}\n</code></pre>\n<h3 id=\"aggregate\">aggregate</h3>\n<p>可以通过 <code>aggregate</code> 方法进行混合操作。如:</p>\n<pre><code class=\"lang-js\">export default class extends think.model.mongo {\n match(){\n return this.aggregate([\n {$match: {status: \'A\'}},\n {$group: {_id: \"$cust_id\", total: {$sum: \"$amount\"}}}\n ]);\n }\n}\n</code></pre>\n<p>具体请见 <a href=\"https://docs.mongodb.org/manual/core/aggregation-introduction/\">https://docs.mongodb.org/manual/core/aggregation-introduction/</a>。</p>\n<h3 id=\"mapreduce\">MapReduce</h3>\n<p>可以通过 <code>mapReduce</code> 方法进行 MapReduce 操作。如:</p>\n<pre><code class=\"lang-js\">export default class extends think.model.mongo {\n execMapReduce(){\n let map = () => {\n emit(this.cust_id, this.amount);\n }\n let reduce = (key, values) => {\n return Array.sum(values);\n }\n return this.mapReduce(map, reduce, {\n query: {status: \"A\"},\n out: \"order_totals\"\n })\n }\n}\n</code></pre>\n<p>具体请见 <a href=\"https://docs.mongodb.org/manual/core/aggregation-introduction/#map-reduce\">https://docs.mongodb.org/manual/core/aggregation-introduction/#map-reduce</a>。</p><p>文章来源:<a href=\"http://www.thinkjs.org/\">http://www.thinkjs.org</a></p>\n<p><br></p>', '', '前端汇', '2016-07-14 10:02:35', '47', '0', '0', '0', '18', 'thinkjs,nodejs', '1', '1', '', '8', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('248', 'SQLite', '', '<p>ThinkJS 中支持使用 SQLite 数据库,底层使用 <a href=\"https://www.npmjs.com/package/sqlite3\">sqlite3</a> 模块。</p>\n<h3 id=\"-\">配置</h3>\n<p>使用 SQLite,需要将模型中的配置 <code>type</code> 改为 <code>sqlite</code>,修改配置文件 <code>src/common/config/db.js</code>:</p>\n<pre><code class=\"lang-js\">export default {\n type: \'sqlite\'\n}\n</code></pre>\n<h3 id=\"-\">存储方式</h3>\n<p>SQLite 支持使用内存或者文件 2 种方式来存放数据,需要设置配置 <code>path</code>。</p>\n<h4 id=\"-\">内存方式</h4>\n<pre><code class=\"lang-js\">export default {\n type: \'sqlite\',\n adapter: {\n sqlite: {\n path: true, //使用内存来存储数据\n }\n }\n}\n</code></pre>\n<h4 id=\"-\">文件方式</h4>\n<p>文件方式需要设置存储 SQLite 数据的目录,默认为 <code>src/common/runtime/sqlite</code>。</p>\n<pre><code class=\"lang-js\">export default {\n type: \'sqlite\',\n adatper: {\n sqlite: {\n path: \'/path/to/store/sqlite\' //设置存储数据文件的目录\n }\n }\n}\n</code></pre>\n<p>对应的数据表文件路径为 <code>path</code> + <code>/[name].sqlite</code>,默认情况下数据库 <code>demo</code> 对应的文件路径为 <code>src/common/runtime/sqlite/demo.sqlite</code>。</p>\n<h3 id=\"crud-\">CRUD 操作</h3>\n<p>CRUD 操作和 Mysql 相同,具体请见 <a href=\"./model_intro.html#toc-d84\">模型 -> 介绍</a>。</p><p>文章来源:<a href=\"http://www.thinkjs.org/\">http://www.thinkjs.org</a></p><p><br></p>', '', '前端汇', '2016-07-14 10:02:40', '47', '0', '0', '0', '18', 'thinkjs,nodejs', '1', '1', '', '8', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('249', 'PostgreSQL', '', '<p>ThinkJS 支持 <code>PostgreSQL</code>,底层使用 <a href=\"https://www.npmjs.com/package/pg\">pg</a> 模块。</p>\n<h3 id=\"-\">配置</h3>\n<p>使用 PostgreSQL,需要将模型中的配置 <code>type</code> 改为 <code>postgresql</code>,修改配置文件 <code>src/common/config/db.js</code>:</p>\n<pre><code class=\"lang-js\">export default {\n type: \'postgresql\',\n adapter: {\n postgresql: {\n\n }\n }\n}\n</code></pre>\n<h3 id=\"crud-\">CRUD 操作</h3>\n<p>CRUD 操作和 Mysql 相同,具体请见 <a href=\"./model_intro.html#toc-d84\">模型 -> 介绍</a>。</p><p>文章来源:<a href=\"http://www.thinkjs.org/\">http://www.thinkjs.org</a></p>\n<p><br></p>', '', '前端汇', '2016-07-14 10:02:44', '41', '0', '0', '0', '18', 'thinkjs,nodejs', '1', '1', '', '8', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('252', 'Adapter_Cache', '', '<p>在项目中,合理使用缓存对性能有很大的帮助。ThinkJS 提供了多种的缓存方式,包括:内存缓存、文件缓存、Memcache 缓存、Redis 缓存等。</p>\n<h3 id=\"-\">缓存类型</h3>\n<p>系统默认支持的缓存类型如下:</p>\n<ul>\n<li><code>memory</code> 内存缓存</li>\n<li><code>file</code> 文件缓存</li>\n<li><code>memcache</code> Memcache 缓存</li>\n<li><code>redis</code> Redis 缓存</li>\n</ul>\n<p>如果使用 <code>memcache</code> 缓存,需要设置 Memcache 配置信息,见 <a href=\"./config.html#memcache\">配置</a>。</p>\n<p>如果使用 <code>redis</code> 缓存,需要设置 Redis 配置信息,见 <a href=\"./config.html#redis\">配置</a>。</p>\n<h3 id=\"-\">缓存配置</h3>\n<p>默认缓存配置如下,可以在配置文件 <code>src/common/config/cache.js</code> 中进行修改:</p>\n<pre><code class=\"lang-js\">export default {\n type: \'file\', //缓存类型\n timeout: 6 * 3600, //失效时间,单位:秒\n adapter: { //不同 adapter 下的配置\n file: {\n path: think.RUNTIME_PATH + \'/cache\', //缓存文件的根目录\n path_depth: 2, //缓存文件生成子目录的深度\n file_ext: \'.json\' //缓存文件的扩展名\n },\n redis: {\n prefix: \'thinkjs_\'\n },\n memcache: {\n prefix: \'thinkjs_\'\n }\n }\n};\n</code></pre>\n<p><code>注</code>:<code>2.0.6</code> 版本开始添加了 adapter 配置。</p>\n<p>其中 <code>prefix</code> 在 <code>memcache</code> 和 <code>redis</code> 类型中使用,存储时会将缓存 key + prefix 作为新的 key 来存储,用于防止跟其他地方使用的缓存 key 冲突。如果不想设置 prefix,可以将 prefix 设置为空字符串,如:</p>\n<pre><code class=\"lang-js\">export default {\n prefix: \'\' //将缓存 key 前缀设置为空\n}\n</code></pre>\n<h3 id=\"-\">使用缓存</h3>\n<p>可以通过 <code>think.cache</code> 方法对缓存进行增删改查操作,具体请见 <a href=\"./api_think.html#toc-7d7\">API -> think</a>。</p>\n<p>如果当前使用场景在继承自 think.http.base 的类下,可以通过 <code>this.cache</code> 方法来操作缓存,具体请见 <a href=\".//api_think_http_base.html#cache-name-value-options\">API -> think.http.base</a>。</p>\n<h3 id=\"-\">扩展缓存</h3>\n<p>可以通过下面的命令创建一个名为 <code>foo</code> 缓存类:</p>\n<pre><code class=\"lang-sh\">thinkjs adapter cache/foo\n</code></pre>\n<p>执行完成后,会创建文件 <code>src/common/adapter/cache/foo.js</code>。扩展缓存类需要实现如下的方法:</p>\n<pre><code class=\"lang-js\">export default class extends think.cache.base {\n /**\n * 初始化方法\n * @param {Object} options []\n * @return {} []\n */\n init(options){\n //set gc type & start gc\n this.gcType = \'cache_foo\';\n think.gc(this);\n }\n /**\n * 获取缓存\n * @param {String} name []\n * @return {Promise} []\n */\n get(name){\n\n }\n /**\n * 设置缓存\n * @param {String} name []\n * @param {Mixed} value []\n * @param {Number} timeout []\n * @return {Promise}\n */\n set(name, value, timeout){\n\n }\n /**\n * 删除缓存\n * @param {String} name []\n * @return {Promise} []\n */\n delete(name){\n\n }\n /**\n * 缓存垃圾回收\n * @return {Promise} []\n */\n gc(){\n\n }\n}\n</code></pre>\n<p>框架里的 Cache 实现请见 <a href=\"https://github.com/75team/thinkjs/tree/master/src/adapter/cache\">https://github.com/75team/thinkjs/tree/master/src/adapter/cache</a>。</p>\n<h3 id=\"-adapter\">使用第三方缓存 Adapter</h3>\n<p>如何使用第三方的缓存 Adapter 请参见 <a href=\"./adapter_intro.html#toc-e7c\">Adapter -> 介绍</a>。</p><p>文章来源:<a href=\"http://www.thinkjs.org/\">http://www.thinkjs.org</a></p><p><br></p>', '', '', '2016-07-14 12:22:40', '36', '0', '0', '0', '18', 'thinkjs,nodejs', '0', '1', '', '8', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('251', 'Adapter介绍', '', '<p>Adapter 是用来解决一类功能的多种实现,如:支持多种数据库,支持多种模版引擎等。系统默认支持的 Adapter 有:<code>Cache</code>,<code>Session</code>,<code>WebSocket</code>,<code>Db</code>,<code>Store</code>,<code>Template</code> 和 <code>Socket</code>。</p>\n<h3 id=\"-adapter\">创建 Adapter</h3>\n<p>可以通过命令 <code>thinkjs adapter [type]/[name]</code> 来创建 Adapter,如:</p>\n<pre><code class=\"lang-sh\">thinkjs adapter template/dot\n</code></pre>\n<p>创建一个名为 <code>dot</code> 的 Template Adapter,创建的文件路径为 <code>src/common/adapter/template/dot.js</code>。文件内容类似如下:</p>\n<pre><code class=\"lang-js\">export default class extends think.adapter.template {\n /**\n * init\n * @return {[]} []\n */\n init(...args){\n super.init(...args);\n }\n}\n</code></pre>\n<p>如果创建的类型之前不存在,会自动创建一个 Base 类,其他类会继承该类。</p>\n<h3 id=\"-adapter\">加载 Adapter</h3>\n<p>可以通过 <code>think.adapter</code> 方法加载对应的 Adapter,如:</p>\n<pre><code class=\"lang-js\">let Template = think.adapter(\'template\', \'dot\'); //加载名为 dot 的 Template Adapter\nlet instance = new Template(...args); //实例化 Adapter\n</code></pre>\n<h3 id=\"-adapter\">使用第三方 Adapter</h3>\n<p>加载 Adapter 时,系统会自动从 <code>src/common/adapter</code> 目录和系统目录查找对应的 Adapter,如果引入第三方的 Adapter,需要将 Adapter 注册进去,否则系统无法找到该 Adapter。</p>\n<p>可以通过 <code>think.adapter</code> 方法注册第三方的 Adapter,如:</p>\n<pre><code class=\"lang-js\">let DotTemplate = require(\'think-template-dot\');\nthink.adapter(\'template\', \'dot\', DotTemplate);\n</code></pre>\n<p>将文件存放在 <code>src/common/bootstrap/</code> 目录下,这样服务启动时就会自动加载。</p><p>文章来源:<a href=\"http://www.thinkjs.org/\">http://www.thinkjs.org</a></p><p><br></p>', '', '', '2016-07-14 12:20:57', '40', '0', '0', '0', '18', 'thinkjs,nodejs', '0', '1', '', '8', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('253', 'adapter_Session', '', '<p>需要用户登录的网站基本上都离不开 Session,ThinkJS 里默认支持多种类型的 Session,如:<code>file</code>,<code>db</code>,<code>redis</code> 等。</p>\n<h3 id=\"-session-\">支持的 Session 类型</h3>\n<ul>\n<li><code>memory</code> 内存方式</li>\n<li><code>file</code> 文件类型</li>\n<li><code>db</code> 数据库类型</li>\n<li><code>redis</code> Redis 类型</li>\n</ul>\n<h5 id=\"db-session\">db Session</h5>\n<p>使用 <code>db</code> 类型的 Session 需要创建对应的数据表(如果是 MongoDB 则无需创建),可以用下面的 SQL 语句创建:</p>\n<pre><code class=\"lang-sql\"> DROP TABLE IF EXISTS `think_session`;\n CREATE TABLE `think_session` (\n `id` int(11) unsigned NOT NULL AUTO_INCREMENT,\n `cookie` varchar(255) NOT NULL DEFAULT \'\',\n `data` text,\n `expire` bigint(11) NOT NULL,\n PRIMARY KEY (`id`),\n UNIQUE KEY `cookie` (`cookie`),\n KEY `expire` (`expire`)\n ) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n</code></pre>\n<p>需要将 <code>think_</code> 改为 db 配置中的数据表前缀。</p>\n<h5 id=\"redis-session\">redis Session</h5>\n<p>使用 <code>redis</code> 类型的 Session 需要配置 Redis,具体见 <a href=\"./config.html#redis\">配置</a>。</p>\n<h3 id=\"session-\">Session 配置</h3>\n<p>Session 默认配置如下,可以在 <code>src/common/config/session.js</code> 中进行修改:</p>\n<pre><code class=\"lang-js\">export default {\n type: \'file\',\n name: \'thinkjs\', //对应 cookie 的名称\n secret: \'\', //Session 对应的 cookie 是否需要加密\n timeout: 24 * 3600, //过期时间,默认为一天\n cookie: { // cookie options\n length: 32\n },\n adapter: {\n file: {\n path: think.RUNTIME_PATH + \'/session\'\n }\n }\n};\n</code></pre>\n<p><code>注</code>:<code>2.0.6</code> 版本开始添加了 adapter 配置。</p>\n<p>关于 Cookie 的配置请见 <a href=\"./config.html#cookie\">配置</a>。</p>\n<h3 id=\"session-\">Session 读写</h3>\n<p>Controller 或 Logic 里可以通过下面的方式读写 Session:</p>\n<h5 id=\"-session\">读取 Session</h5>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n * indexAction(){\n //获取session\n let value = yield this.session(\'userInfo\');\n }\n}\n</code></pre>\n<h5 id=\"-session\">设置 Session</h5>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n * indexAction(){\n //设置 session\n yield this.session(\'userInfo\', data);\n }\n}\n</code></pre>\n<h5 id=\"-session\">清除 Session</h5>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n * indexAction(){\n //清除当前用户的 session\n yield this.session();\n }\n}\n</code></pre>\n<hr>\n<p>http 对象上可以通过 <code>http.session</code> 方法读写 Session,具体请见 <a href=\"./api_http.html#toc-b20\">API -> http</a>。</p>\n<h3 id=\"-session\">扩展 Session</h3>\n<p>可以通过下面的命令创建 Session Adapter:</p>\n<pre><code class=\"lang-js\">thinkjs adapter session/foo\n</code></pre>\n<p>会创建文件 <code>src/common/adapter/session/foo.js</code>,需要实现下面的方法:</p>\n<pre><code class=\"lang-js\">export default class extends think.adapter.session {\n /**\n * init\n * @param {Object} options []\n * @return {} []\n */\n init(options){\n\n }\n /**\n * 获取 Session \n * @param {String} name []\n * @return {Promise} []\n */\n get(name){\n\n }\n /**\n * 设置 Session\n * @param {String} name []\n * @param {Mixed} value []\n */\n set(name, value){\n\n }\n /**\n * 删除 Session\n * @param {String} name []\n * @return {Promise} []\n */\n delete(name){\n\n }\n /**\n * 更新 Session\n * @return {Promise} []\n */\n flush(){\n\n }\n /**\n * 清除过期的 Session\n * @return {Promise} []\n */\n gc(){\n\n }\n}\n</code></pre>\n<p>框架里的 Session 实现请见 <a href=\"https://github.com/75team/thinkjs/tree/master/src/adapter/session\">https://github.com/75team/thinkjs/tree/master/src/adapter/session</a>。</p>\n<h3 id=\"-session-adapter\">使用第三方 Session Adapter</h3>\n<p>如何使用第三方的缓存 Adapter 请参见 <a href=\"./adapter_intro.html#toc-e7c\">Adapter -> 介绍</a>。</p><p>文章来源:<a href=\"http://www.thinkjs.org/\">http://www.thinkjs.org</a></p>', '', '', '2016-07-14 12:24:56', '50', '0', '0', '0', '18', 'thinkjs,nodejs', '0', '1', '', '8', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('254', 'WebSocket', '', '<p>项目里经常要要使用 WebSocket 来实现聊天等功能,ThinkJS 支持多种 WebSocket 库,如:<code>socket.io</code>,<code>sockjs</code> 等,并对这些库进行了一些简单的包装,让使用的接口一致。</p>\n<h3 id=\"-websocket\">开启 WebSocket</h3>\n<p>WebSocket 功能默认是关闭的,项目如果需要开启,可以修改配置文件 <code>src/common/config/websocket.js</code>:</p>\n<pre><code class=\"lang-js\">export default {\n on: false, //是否开启 WebSocket\n type: \'socket.io\', //使用的 WebSocket 库类型,默认为 socket.io\n allow_origin: \'\', //允许的 origin\n adapter: undefined, // socket 存储的 adapter,socket.io 下使用\n path: \'\', //url path for websocket\n messages: {\n // open: \'home/websocket/open\',\n }\n};\n</code></pre>\n<p>需要将配置 <code>on</code> 的值修改为 true,并重启 Node.js 服务。</p>\n<h3 id=\"-action-\">事件到 Action 的映射</h3>\n<p>ThinkJS 里对 WebSocket 的包装遵循了 <code>socket.io</code> 的机制,服务端和客户端之间通过事件来交互,这样服务端需要将事件名映射到对应的 Action,才能响应具体的事件。配置在 <code>messages</code> 字段,具体如下:</p>\n<pre><code class=\"lang-js\">export default {\n messages: {\n open: \'home/socketio/open\', // WebSocket 建立连接时处理的 Action\n close: \'home/socketio/close\', // WebSocket 关闭时处理的 Action\n adduser: \'home/socketio/adduser\', //adduser 事件处理的 Action\n }\n}\n</code></pre>\n<p>其中 <code>open</code> 和 <code>close</code> 事件名固定,表示建立连接和断开连接的事件,其他事件均为自定义,项目里可以根据需要添加。</p>\n<h3 id=\"action-\">Action 处理</h3>\n<p>通过上面配置事件到 Action 的映射后,就可以在对应的 Action 作相应的处理。如:</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n /**\n * WebSocket 建立连接时处理\n * @param {} self []\n * @return {} []\n */\n openAction(self){\n var socket = self.http.socket;\n this.broadcast(\'new message\', {\n username: socket.username,\n message: self.http.data\n });\n }\n}\n</code></pre>\n<h4 id=\"emit\">emit</h4>\n<p>Action 里可以通过 <code>this.emit</code> 方法给当前 socket 发送事件,如:</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n /**\n * WebSocket 建立连接时处理\n * @param {} self []\n * @return {} []\n */\n openAction(self){\n var socket = self.http.socket;\n this.emit(\'new message\', \'connected\');\n }\n}\n</code></pre>\n<h4 id=\"broadcast\">broadcast</h4>\n<p>Action 里可以通过 <code>this.broadcast</code> 方法给所有的 socket 广播事件,如:</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n chatAction(self){\n var socket = self.http.socket;\n //广播给除当前 socket 之外的所有 sockets\n this.broadcast(\'new message\', {msg: \'message\', username: \'xxx\'});\n }\n}\n</code></pre>\n<p><code>注</code>:broadcast 方法默认是給除去当前 socket 的所有 sockets 发送事件,如果想包含当前的 socket,可以设置第三个参数值为 <code>true</code>。</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n chatAction(self){\n var socket = self.http.socket;\n //广播给所有的 sockets,包含当前的 socket\n this.broadcast(\'new message\', {msg: \'message\', username: \'xxx\'}, true);\n }\n}\n</code></pre>\n<h4 id=\"socket-\">socket 对象</h4>\n<p>Action 里可以通过 <code>this.http.socket</code> 拿到当前的 socket 对象。</p>\n<h4 id=\"-\">事件数据</h4>\n<p>Action 里可以通过 <code>this.http.data</code> 拿到发送过来事件的数据。</p>\n<h3 id=\"socket-io\">socket.io</h3>\n<p><code>socket.io</code> 对 WebSocket 前后端都有封装,使用起来非常方便。</p>\n<h4 id=\"io-\">io 对象</h4>\n<p>在 Action 里可以通过 <code>this.http.io</code> 来获取 <code>io</code> 对象,该对象为 socket.io 的一个实例。</p>\n<p>io 对象包含的方法请见 <a href=\"http://socket.io/docs/server-api/#server()\">http://socket.io/docs/server-api/#server()</a>。</p>\n<h4 id=\"-path\">设置 path</h4>\n<p>设置被 socket.io 处理的路径,默认为 <code>/socket.io</code>。如果需要修改,可以修改下面的配置:</p>\n<pre><code class=\"lang-js\">export default {\n path: \'/other_path\'\n}\n</code></pre>\n<p><code>注</code>:服务端修改了处理的路径后,客户端也要作对应的修改。</p>\n<h4 id=\"-adapter\">设置 adapter</h4>\n<p>使用多节点来部署 WebSocket 时,多节点之间可以借助 Redis 进行通信,这时可以设置 adapter 来实现。</p>\n<pre><code class=\"lang-js\">import redis from \'socket.io-redis\';\n\nexport default {\n adapter: function(){\n return redis({ host: \'localhost\', port: 6379 });\n }\n}\n</code></pre>\n<p>具体请见 <a href=\"http://socket.io/docs/using-multiple-nodes/\">http://socket.io/docs/using-multiple-nodes/</a>。</p>\n<h4 id=\"socket-io-client\">socket.io client</h4>\n<p>浏览器端需要引入 socket.io client,下载地址为:<a href=\"http://socket.io/download/\">http://socket.io/download/</a>。</p>\n<pre><code class=\"lang-js\">var socket = io(\'http://localhost:8360\');\n//发送事件\nsocket.emit(\'name\', \'data\');\n//监听事件\nsocket.on(\'name\', function(data){\n\n})\n</code></pre>\n<p>也可以直接引入一个 CDN 地址:<a href=\"http://s4.qhimg.com/static/535dde855bc726e2/socket.io-1.2.0.js\">http://s4.qhimg.com/static/535dde855bc726e2/socket.io-1.2.0.js</a>。</p>\n<h4 id=\"-\">校验用户登录</h4>\n<p>WebSocket 建立连接时可以拿到 cookie,所以可以在 <code>open</code> 对应的 Action 里校验用户是否登录。如:</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n * openAction(){\n let userInfo = yield this.session(\'userInfo\');\n if(think.isEmpty(userInfo)){\n\n }\n }\n}\n</code></pre>\n<h4 id=\"-\">聊天代码示例</h4>\n<p>聊天示例代码请见:<a href=\"https://github.com/75team/thinkjs2-demos/tree/master/websocket-socket.io\">https://github.com/75team/thinkjs2-demos/tree/master/websocket-socket.io</a>。</p>\n<h3 id=\"sockjs\">SockJS</h3>\n<h4 id=\"-\">配置</h4>\n<p>使用 SockJS 库,需要将配置里的 type 修改为 <code>sockjs</code>,如:</p>\n<pre><code class=\"lang-js\">export default {\n type: \'sockjs\'\n}\n</code></pre>\n<h4 id=\"sockjs-\">sockjs 对象</h4>\n<p>Action 里可以通过 <code>this.http.sockjs</code> 拿到 sockjs 对象,该对象为 SockJS 类的一个实例。</p>\n<h4 id=\"-path\">设置 path</h4>\n<p>设置被 SockJS 处理的路径,默认为 <code>/sockjs</code>,可以通过下面的配置修改:</p>\n<pre><code class=\"lang-js\">export default {\n path: \'/websocket\'\n}\n</code></pre>\n<h4 id=\"sockjs-client\">SockJS client</h4>\n<p>浏览器端需要引入 SockJS client,下载地址为:<a href=\"https://github.com/sockjs/sockjs-client\">https://github.com/sockjs/sockjs-client</a>。</p>\n<p>SockJS client 并没有做什么封装,所以需要额外做一层包装,变成事件的方式,以便跟包装后的服务端对应。包装方式参考如下:</p>\n<pre><code class=\"lang-js\">SockJS.prototype.emit = function(event, data){\n this.send(JSON.stringify({event: event, data: data}));\n }\nSockJS.prototype.events = {};\nSockJS.prototype.on = function(event, callback){\n if(!(event in this.events)){\n this.events[event] = [];\n }\n this.events[event].push(callback);\n}\nSockJS.prototype.onmessage = function(e) {\n var data = JSON.parse(e.data);\n var callbacks = this.events[data.event] || [];\n callbacks.forEach(function(callback){\n callback && callback(data.data);\n })\n};\nSockJS.prototype.onopen = function() {\n this.onmessage(JSON.stringify({data: {event: \'open\'}}));\n};\nSockJS.prototype.onclose = function() {\n this.onmessage(JSON.stringify({data: {event: \'close\'}}));\n};\n</code></pre>\n<p>通过上面的包装后就可以通过事件的方式来接收和发送消息了,如:</p>\n<pre><code class=\"lang-js\">var socket = new SockJS(\'/sockjs\'); //这里的路径必须和配置里相同,如果没有配置则为 /sockjs\n//监听事件\nsocket.on(\'add user\', function(data){\n\n});\n//发送事件\nsocket.emit(\'new message\', \'xxx\');\n</code></pre>\n<h4 id=\"-\">校验用户登录</h4>\n<p>SockJS 为了安全,在建立连接时不提供相关的 cookie,所以无法通过 cookie 来校验用户是否登录。可以先在页面里输出一个 token,建立连接时将该 token 发送用来校验是否已经登录。具体请见:<a href=\"https://github.com/sockjs/sockjs-node#authorisation\">https://github.com/sockjs/sockjs-node#authorisation</a>。</p>\n<h4 id=\"-\">聊天代码示例</h4>\n<p>聊天示例代码请见:<a href=\"https://github.com/75team/thinkjs2-demos/tree/master/websocket-sockjs\">https://github.com/75team/thinkjs2-demos/tree/master/websocket-sockjs</a>。</p>\n<h3 id=\"nginx-\">nginx 反向代理</h3>\n<p>nginx 从 <code>1.3.13</code> 版本开始支持反向代理 WebSocket 请求,如果在项目中使用,需要在 nginx 配置文件中添加如下的配置:</p>\n<pre><code class=\"lang-nginx\">proxy_set_header Upgrade $http_upgrade;\nproxy_set_header Connection \"upgrade\";\n</code></pre>\n<p><code>注</code>: 使用 <code>thinkjs</code> 命令创建项目时,会自动创建 nginx 配置文件,并且配置文件已经包含了上面 2 个配置,可以直接使用。</p>\n<p>nginx 代理 WebSocket 请求的文档请见 <a href=\"http://nginx.org/en/docs/http/websocket.html\">http://nginx.org/en/docs/http/websocket.html</a>。</p>\n<h3 id=\"-websocket-\">获取当前所有的 WebSocket 连接对象</h3>\n<p>可以通过 <code>thinkCache(thinkCache.WEBSOCKET)</code> 来获取所有的 WebSocket 连接对象,数组格式。</p>\n<h3 id=\"-\">如何实现私聊</h3>\n<p>ThinkJS 目前还没有私聊的机制,项目里可以通过获取所有的 WebSocket 连接然后找到对应的连接进行。</p><p>文章来源:<a href=\"http://www.thinkjs.org/\">http://www.thinkjs.org</a></p><p><br></p>', '', '', '2016-07-14 12:26:21', '41', '0', '0', '0', '18', 'thinkjs,nodejs', '0', '1', '', '8', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('255', 'Template', '', '<p>Template Adapter 用来实现支持多种类型的模版引擎,如:<code>ejs</code>,<code>swig</code> 等。</p>\n<h3 id=\"-\">支持模版引擎类型</h3>\n<ul>\n<li><code>base</code></li>\n<li><code>ejs</code> ejs 模版引擎</li>\n<li><code>jade</code> jade 模板引擎</li>\n<li><code>swig</code> 一种支持模版继承的模版引擎</li>\n<li><code>nunjucks</code> 一种类似 jinja2 的模版引擎,功能非常强大</li>\n</ul>\n<h3 id=\"-\">模版引擎配置</h3>\n<p>模版引擎配置如下,可以在 <code>src/common/config/view.js</code> 中修改:</p>\n<pre><code class=\"lang-js\">export default {\n type: \'ejs\',\n adapter: { \n ejs: { //额外的配置\n\n }\n }\n};\n</code></pre>\n<h3 id=\"-\">使用模版引擎</h3>\n<p>模版引擎会在视图里自动调用,默认情况不需要手工调用使用。如果在有些场景非要使用的话,可以通过下面的方式加载对应的模版引擎:</p>\n<pre><code class=\"lang-js\">let EjsTemplate = think.adapter(\'template\', \'ejs\');\nlet instance = new EjsTemplate(...args);\n</code></pre>\n<h3 id=\"-\">扩展模版引擎类型</h3>\n<p>可以通过下面的命令创建一个名为 <code>foo</code> Template 类:</p>\n<pre><code class=\"lang-js\">thinkjs adapter template/foo\n</code></pre>\n<p>执行完成后,会创建文件 <code>src/common/adapter/template/foo.js</code>。扩展缓存类需要实现如下的方法:</p>\n<pre><code class=\"lang-js\">export default class extends think.adapter.base {\n /**\n * get compiled content\n * @params {String} templateFile 模版文件目录\n * @params {Object} tVar 模版变量\n * @params {Object} config 模版引擎配置\n * @return {Promise} []\n */\n run(templateFile, tVar, config){\n\n }\n}\n</code></pre>\n<p>框架里的 Template 实现请见 <a href=\"https://github.com/75team/thinkjs/tree/master/src/adapter/template\">https://github.com/75team/thinkjs/tree/master/src/adapter/template</a>。</p>\n<h3 id=\"-adapter\">使用第三方模版 Adapter</h3>\n<p>如何使用第三方的模版 Adapter 请参见 <a href=\"./adapter_intro.html#toc-e7c\">Adapter -> 介绍</a>。</p><p>文章来源:<a href=\"http://www.thinkjs.org/\">http://www.thinkjs.org</a></p>\n<p><br></p>', '', '', '2016-07-14 12:27:27', '45', '0', '0', '0', '18', 'thinkjs,nodejs', '0', '1', '', '8', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('256', 'Middleware', '', '<p>当处理用户的请求时,需要经过很多处理,如:解析参数,判断是否静态资源访问,路由解析,页面静态化判断,执行操作,查找模版,渲染模版等。项目里根据需要可能还会增加其他的一些处理,如:判断 IP 是否在黑名单中,CSRF 检测等。</p>\n<p>ThinkJS 里通过 middleware 来处理这些逻辑,每个逻辑都是一个独立的 middleware。在请求处理中埋很多 hook,每个 hook 串行执行一系列的 middleware,最终完成一个请求的逻辑处理。</p>\n<h3 id=\"hook-\">hook 列表</h3>\n<p>框架里包含的 hook 列表如下:</p>\n<ul>\n<li><code>request_begin</code> 请求开始</li>\n<li><code>payload_parse</code> 解析提交上来的数据</li>\n<li><code>payload_validate</code> 验证提交的数据</li>\n<li><code>resource</code> 静态资源请求处理</li>\n<li><code>route_parse</code> 路由解析</li>\n<li><code>logic_before</code> logic 处理之前</li>\n<li><code>logic_after</code> logic 处理之后</li>\n<li><code>controller_before</code> controller 处理之前</li>\n<li><code>controller_after</code> controller 处理之后</li>\n<li><code>view_before</code> 视图处理之前</li>\n<li><code>view_template</code> 视图文件处理</li>\n<li><code>view_parse</code> 视图解析</li>\n<li><code>view_filter</code> 视图内容过滤</li>\n<li><code>view_after</code> 视图处理之后</li>\n<li><code>response_end</code> 请求响应结束</li>\n</ul>\n<p>每个 hook 里调用多个 middleware 来完成处理,具体包含的 middleware 如下:</p>\n<pre><code class=\"lang-js\">export default {\n request_begin: [],\n payload_parse: [\'parse_form_payload\', \'parse_single_file_payload\', \'parse_json_payload\', \'parse_querystring_payload\'],\n payload_validate: [\'validate_payload\'],\n resource: [\'check_resource\', \'output_resource\'],\n route_parse: [\'rewrite_pathname\', \'parse_route\'],\n logic_before: [],\n logic_after: [],\n controller_before: [],\n controller_after: [],\n view_before: [],\n view_template: [\'locate_template\'],\n view_parse: [\'parse_template\'],\n view_filter: [],\n view_after: [],\n response_end: []\n};\n</code></pre>\n<h3 id=\"-hook\">配置 hook</h3>\n<p>hook 默认执行的 middleware 往往不能满足项目的需求,可以通过配置修改 hook 对应要执行的 middleware 来完成,hook 的配置文件为 <code>src/common/config/hook.js</code>。</p>\n<pre><code class=\"lang-js\">export default {\n payload_parse: [\'parse_xml\'], //解析 xml\n}\n</code></pre>\n<p>上面的配置会覆盖掉默认的配置值。如果在原有配置上增加的话,可以通过下面的方式:</p>\n<h4 id=\"-\">在前面追加</h4>\n<p>可以通过配置 <code>prepend</code> 让 middleware 作为前置追加:</p>\n<pre><code class=\"lang-js\">export default {\n payload_parse: [\'prepend\', \'parse_xml\'], //在前面追加解析 xml\n}\n</code></pre>\n<h4 id=\"-\">在后面追加</h4>\n<p>可以通过配置 <code>append</code> 让 middleware 作为后置追加:</p>\n<pre><code class=\"lang-js\">export default {\n payload_parse: [\'append\', \'parse_xml\'], //在后面追加解析 xml\n}\n</code></pre>\n<p><code>注</code>:建议使用追加的方式配置 middleware,系统的 middleware 名称可能在后续的版本中有所修改。</p>\n<h3 id=\"-hook\">执行 hook</h3>\n<p>可以通过 <code>think.hook</code> 方法执行一个对应的 hook,如:</p>\n<pre><code class=\"lang-js\">await think.hook(\'payload_parse\', http, data); //返回的是一个 Promise\n</code></pre>\n<p>在含有 <code>http</code> 对象的类中可以直接使用 <code>this.hook</code> 来执行 hook,如:</p>\n<pre><code class=\"lang-js\">await this.hook(\'payload_parse\', data);\n</code></pre>\n<h3 id=\"-middleware\">创建 middleware</h3>\n<p>ThinkJS 支持 2 种方式的 middleware,即:class 方式和 function 方式。可以根据 middleware 复杂度决定使用哪种方式。</p>\n<h4 id=\"class-\">class 方式</h4>\n<p>如果 middleware 需要执行的逻辑比较复杂,需要定义为 class 的方式。可以通过 <code>thinkjs</code> 命令来创建 middleware,在项目目录下执行如下的命令:</p>\n<pre><code class=\"lang-sh\">thinkjs middleware xxx\n</code></pre>\n<p>执行完成后,会看到对应的文件 <code>src/common/middleware/xxx.js</code>。</p>\n<h5 id=\"es6-\">ES6 方式</h5>\n<pre><code class=\"lang-js\">\'use strict\';\n/**\n * middleware\n */\nexport default class extends think.middleware.base {\n /**\n * run\n * @return {} []\n */\n run(){\n\n }\n}\n</code></pre>\n<h5 id=\"-\">动态创建类的方式</h5>\n<pre><code class=\"lang-js\">\'use strict\';\n\n/**\n * middleware\n */\nmodule.exports = think.middleware({\n /**\n * run\n * @return {} []\n */\n run: function(){\n\n }\n})\n</code></pre>\n<p>middleware 里会将 <code>http</code> 传递进去,可以通过 <code>this.http</code> 属性来获取。逻辑代码放在 <code>run</code> 方法执行,如果含有异步操作,需要返回一个 <code>Promise</code> 或者使用 <code>*/yield</code>。</p>\n<h4 id=\"function-\">function 方式</h4>\n<p>如果 middleware 要处理的逻辑比较简单,可以直接创建为函数的形式。这种 middleware 不建议创建成一个独立的文件,而是放在一起统一处理。</p>\n<p>可以建立文件 <code>src/common/bootstrap/middleware.js</code>,该文件在服务启动时会自动被加载。可以在这个文件添加多个函数式的 middleware。如:</p>\n<pre><code class=\"lang-js\">think.middleware(\'parse_xml\', async http => {\n let payload = await http.getPayload();\n if (!payload) {\n return;\n }\n ...\n});\n</code></pre>\n<p>函数式的 middleware 会将 <code>http</code> 对象作为一个参数传递进去,如果 middleware 里含有异步操作,需要返回一个 <code>Promise</code> 或者使用 Generator Function。</p>\n<p>以下是框架里解析 json payload 的实现:</p>\n<pre><code class=\"lang-js\">think.middleware(\'parse_json_payload\', http => {\n let types = http.config(\'post.json_content_type\');\n if (types.indexOf(http.type()) === -1) {\n return;\n }\n return http.getPayload().then(payload => {\n try{\n http._post = JSON.parse(payload);\n }catch(e){}\n });\n});\n</code></pre>\n<h3 id=\"-\">解析后赋值</h3>\n<p>有些 middleware 可能会解析相关的数据,然后希望重新赋值到 <code>http</code> 对象上,如:解析传递过来的 xml 数据,但后续希望可以通过 <code>http.get</code> 方法来获取。</p>\n<ul>\n<li><code>http._get</code> 用来存放 GET 参数值,http.get(xxx) 从该对象获取数据</li>\n<li><code>http._post</code> 用来存放 POST 参数值,http.post(xxx) 从该对象获取数据</li>\n<li><code>http._file</code> 用来存放上传的文件值,http.file(xxx) 从该对象获取数据</li>\n</ul>\n<pre><code class=\"lang-js\">think.middleware(\'parse_xml\', async http => {\n let payload = await http.getPayload();\n if (!payload) {\n return;\n }\n return parseXML(payload).then(data => {\n http._post = data; //将解析后的数据赋值给 http._post,后续可以通过 http.post 方法来获取\n });\n});\n</code></pre>\n<p>关于 <code>http</code> 对象更多信息请见 <a href=\"./api_http.html\">API -> http</a>。</p>\n<h3 id=\"-\">阻止后续执行</h3>\n<p>有些 middleware 执行到一定条件时,可能希望阻止后面的逻辑继续执行。如:IP 黑名单判断,如果命中了黑名单,那么直接拒绝当前请求,不再执行后续的逻辑。</p>\n<p>ThinkJS 提供了 <code>think.prevent</code> 方法用来阻止后续的逻辑执行执行,该方法是通过返回一个特定类型的 Reject Promise 来实现的。</p>\n<pre><code class=\"lang-js\">think.middleware(\'parse_xml\', async http => {\n let payload = await http.getPayload();\n if (!payload) {\n return;\n }\n var ip = http.ip();\n var blackIPs = [\'123.456.789.100\', ...];\n if(blackIPs.indexOf(ip) > -1){\n http.end();//直接结束当前请求\n return think.prevent(); //阻止后续的代码继续执行\n }\n});\n</code></pre>\n<p>除了使用 <code>think.prevent</code> 方法来阻止后续逻辑继续执行,也可以通过 <code>think.defer().promise</code> 返回一个 Pending Promise 来实现。</p>\n<p>如果不想直接结束当前请求,而是返回一个错误页面,ThinkJS 提供了 <code>think.statusAction</code> 方法来实现,具体使用方式请见 <a href=\"./error_handle.html\">扩展功能 -> 错误处理</a>。</p>\n<h3 id=\"-middleware\">使用第三方 middleware</h3>\n<p>在项目里使用第三方 middleware 可以通过 <code>think.middleware</code> 方法来实现,相关代码存放在 <code>src/common/bootstrap/middleware.js</code> 里。如:</p>\n<pre><code class=\"lang-js\">var parseXML = require(\'think-parsexml\');\n\nthink.middleware(\'parseXML\', parseXML);\n</code></pre>\n<p>然后将 <code>parseXML</code> 配置到 hook 里即可。</p>\n<hr>\n<p>项目里的一些通用 middleware 也推荐发布到 npm 仓库中,middleware 名称推荐使用 <code>think-xxx</code>。</p>\n<h3 id=\"-middleware-\">第三方 middleware 列表</h3>\n<p>第三方 middleware 列表请见 <a href=\"/plugin.html#middleware\">插件 -> middleware</a>。</p><p>文章来源:<a href=\"http://www.thinkjs.org/\">http://www.thinkjs.org</a></p><p><br></p>', '', '', '2016-07-14 12:29:07', '48', '0', '0', '0', '18', 'thinkjs,nodejs', '1', '1', '', '8', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('257', 'CSRF', '', '<p>ThinkJS 提供了 CSRF 处理的 middleware,但默认并没有开启。</p>\n<h3 id=\"-csrf\">开启 CSRF</h3>\n<p>配置 hook 文件 <code>src/common/config/hook.js</code>,添加如下的配置:</p>\n<pre><code class=\"lang-js\">export default {\n logic_before: [\'prepend\', \'csrf\']\n}\n</code></pre>\n<h3 id=\"-\">配置</h3>\n<p>CSRF 默认的配置如下,可以在配置文件 <code>src/common/config/csrf.js</code> 中修改:</p>\n<pre><code class=\"lang-js\">export default {\n session_name: \'__CSRF__\', // Token 值存在 session 的名字\n form_name: \'__CSRF__\', // CSRF 字段名字,从该字段获取值校验\n errno: 400, //错误号\n errmsg: \'token error\' // 错误信息\n};\n</code></pre><p>\n文章来源:<a href=\"http://www.thinkjs.org\" target=\"_blank\">http://www.thinkjs.org</a></p><p><br></p>', '', '前端汇', '2016-07-14 12:30:44', '56', '0', '0', '0', '18', 'thinkjs,nodejs', '1', '1', '', '8', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('258', '子域名部署', '', '<p>当项目比较复杂时,可能希望将不同的功能部署在不同的域名下,但代码还是在一个项目下。如:域名 <code>admin.example.com</code> 部署后台管理的功能,希望映射到 <code>admin</code> 模块下。ThinkJS 提供子域名的 middleware 来处理这个需求。</p>\n<h3 id=\"-\">配置</h3>\n<p>可以修改 <code>src/common/config/hook.js</code> 来开启:</p>\n<pre><code class=\"lang-js\">export default {\n route_parse: [\'prepend\', \'subdomain\']\n}\n</code></pre>\n<p>然后设定子域名部署的相关配置,该配置可以在 <code>config/config.js</code> 里设置:</p>\n<pre><code class=\"lang-js\">export default {\n subdomain: {\n admin: \'admin\', //表示将 admin.example.com 映射到 admin 模块下\n ...\n }\n}\n</code></pre>\n<p>假如原来的 pathname 为 <code>group/detail</code>,命中了 admin.example.com 这个子域名后,pathname 变为 <code>admin/group/detail</code>,后续路由解析就会根据新的 pathname 进行。</p><p>文章来源:<a href=\"http://www.thinkjs.org/\">http://www.thinkjs.org</a></p><p><br></p>', '', '', '2016-07-14 12:31:54', '44', '0', '0', '0', '18', 'thinkjs,nodejs', '0', '1', '', '8', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('259', '禁止端口访问', '', '<h3 id=\"-\">介绍</h3>\n<p>代码上线后一般会用 nginx 做一层反向代理,这时用户的请求会落到 nginx 上,然后通过 nginx 转发到 Node.js 服务上,这样可以很方便的做负载均衡。</p>\n<p>通过 nginx 代理后就不希望用户直接访问到 Node.js 服务了,一种方案时让 Node.js 启动的端口只允许内部访问,外部无法直接访问到。另一种方案是在应用层判断。</p>\n<p>ThinkJS 提供禁止端口访问的 Middleware,这样如果不方便直接在机器上配置禁止端口访问的话,就可以使用该 Middleware 来禁止。</p>\n<h3 id=\"middleware-\">middleware 配置</h3>\n<p>修改 hook 配置文件 <code>src/common/config/hook.js</code>,添加如下的配置:</p>\n<pre><code class=\"lang-js\">export default {\n request_begin: [\'prepend\', \'force_proxy\']\n}\n</code></pre>\n<p>然后在配置文件 <code>src/common/config/env/producition.js</code> 里配置:</p>\n<pre><code class=\"lang-js\">export default {\n proxy_on: true\n}\n</code></pre>\n<p>这样只在线上环境开启了禁止端口访问的功能,开发环境不受影响。</p>\n<h3 id=\"-host\">只监听内网 host</h3>\n<p>Node.js 启动服务时默认监听的端口是 <code>0.0.0.0</code>,这样服务既可以内网访问,也可以外网访问。可以将 host 设置为 <code>127.0.0.1</code> 限制为内网访问。</p>\n<p>可以通过修改配置为 <code>src/common/config/config.js</code> 来完成,如:</p>\n<pre><code class=\"lang-js\">export default {\n host: \'127.0.0.1\'\n}\n</code></pre><p>文章来源:<a href=\"http://www.thinkjs.org/\">http://www.thinkjs.org</a></p><p><br></p>', '', '', '2016-07-14 12:33:01', '50', '0', '0', '0', '18', 'thinkjs,nodejs', '0', '1', '', '8', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('260', '《AngularJS权威教程》PDF下载', 'AngularJS权威教程是学习AngularJS的公认经典,内容全面,讲解通俗,适合各层次的学习者。作者拥有丰富的AngularJS开发和教学经验,也是一位全栈工程师。全书35章,由浅入深地讲解了AngularJS的基本概念和基本功能,包括模块、作用域、控制器、表达式、指令、路由、依赖注入等,重要的是书中对每一个概念的讲解都配合了恰如其分的示例和代码,让读者通过动手实践,切身体会到这些概念的含义', '<h4><img src=\"/static/upload/pics/7/15/2016D_rAHtx6Nb_jxcR-gx-9kPGs.jpg\" alt=\"下载\" style=\"max-width:100%;\"><br></h4><h4>资源简介</h4><p>AngularJS权威教程是学习AngularJS的公认经典,内容全面,讲解通俗,适合各层次的学习者。作者拥有丰富的AngularJS开发和教学经验,也是一位全栈工程师。全书35章,由浅入深地讲解了AngularJS的基本概念和基本功能,包括模块、作用域、控制器、表达式、指令、路由、依赖注入等,重要的是书中对每一个概念的讲解都配合了恰如其分的示例和代码,让读者通过动手实践,切身体会到这些概念的含义和价值。《AngularJS权威教程》后半部分深入到AngularJS应用开发,系统地讨论了服务器通信、事件、架构、动画、本地化、安全、缓存、移动应用等主题。</p><p> </p><p>《AngularJS权威教程》适合各个层次的AngularJS开发人员学习,无论是出于工作需要,还是好奇心的驱使,只要你想彻底理解AngularJS,《AngularJS权威教程》都会让你满载而归。</p><h4>下载链接</h4><p><a href=\"http://pan.baidu.com/s/1qYgVPA8\" target=\"_blank\">http://pan.baidu.com/s/1qYgVPA8</a></p><p>提取密码:<span style=\"line-height: 1.8;\">nb82</span></p><p><br></p>', '', '前端汇', '2016-07-15 11:33:37', '109', '0', '1', '0', '13', 'AngularJS权威', '1', '1', '', '4', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('261', '《MongoDB大数据处理权威指南第2版》pdf下载', '本书根据MongoDB的最新版本做了相应更新,其中包含MongoDB n最新的所有特性,包括版本2.2中引入的聚集框架和版本2.4中引入的哈希索引。MongoDB是最流行的“大数据”NoSQL数据库技术,并且正处于发展中。来自10gen的David Hows以及具有丰富MongoDB开发经验的Peter Membrey和Eelco Plugge,组成了本书的专家团队,他们在本书中分享了自己的专业知', '<p><img src=\"/static/upload/pics/7/15/2016yxL4l3rzdaVPtH3bW-yiIJ36.jpg\" alt=\"u=3219637453,3484046181&fm=15&gp=0\" style=\"max-width:100%;\"><font size=\"4\"><br></font></p><p><font size=\"4\">资源简介</font></p><p>本书根据MongoDB的最新版本做了相应更新,其中包含MongoDB n最新的所有特性,包括版本2.2中引入的聚集框架和版本2.4中引入的哈希索引。MongoDB是最流行的“大数据”NoSQL数据库技术,并且正处于发展中。来自10gen的David Hows以及具有丰富MongoDB开发经验的Peter Membrey和Eelco Plugge,组成了本书的专家团队,他们在本书中分享了自己的专业知识和经验,帮助你了解成为MongoDB专家所需的所有知识。</p><p>书名: 《MongoDB大数据处理权威指南第2版》</p><p>类型:pdf </p><p>大小:56.99M</p><p>作者: (美) 豪斯(Hows, D.) 等著 </p><p>出版社: 清华大学出版社</p><p>原作名: (美) 豪斯(Hows, D.) 等著</p><p>译者: 王肖峰 译 </p><p>出版年: 2015-1-1</p><p>页数: 284</p><h4>下载链接</h4><p><a href=\"http://pan.baidu.com/s/1slkX3HV\" target=\"_blank\">http://pan.baidu.com/s/1slkX3HV</a></p><p>提取密码:<span style=\"line-height: 1.8;\">fmva</span></p><p><br></p>', 'static/upload/pics/7/15/2016PKAbgOquebGveGvd-kwcHR7_.jpg', '前端汇', '2016-07-15 12:33:08', '261', '0', '1', '1', '13', 'MongoDB', '1', '1', '', '4', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('273', 'TypeScript', '', '<p><a href=\"http://www.typescriptlang.org/\">TypeScript</a> 是一种由微软开发的自由和开源的编程语言。它是 JavaScript 的一个超集,向这个语言添加了可选的静态类型,在大型项目里非常有用。</p>\n<p>ThinkJS 2.1 开始支持了创建 TypeScript 类型的项目,并且开发时会自动编译、自动更新,无需手工编译等复杂的操作。</p>\n<h3 id=\"-typescript-\">创建 TypeScript 项目</h3>\n<p>可以通过指定 <code>--ts</code> 参数来创建 TypeScript 项目:</p>\n<pre><code class=\"lang-sh\">thinkjs new thinkjs_demo --ts\n</code></pre>\n<p>TypeScript 项目的文件后缀是 <code>.ts</code>。如果手工建立一些文件,后缀名也要是 <code>.ts</code>,否则调用 <code>tsc</code> 编译时会报错。</p>\n<h3 id=\"-d-ts-\">.d.ts 文件</h3>\n<p><code>.d.ts</code> 文件为第三方类库的描述文件。创建项目时,会创建文件 <code>typings/thinkjs/think.d.ts</code>,该文件为 ThinkJS 的描述文件。项目里的文件可以通过下面的方式引入这个描述文件:</p>\n<pre><code class=\"lang-js\">/// <reference path=\"../../../typings/thinkjs/think.d.ts\" />\n</code></pre>\n<p>该代码必须放在文件的最前面,同时保持相对路径正确。如果文件有 <code>use strict</code> 也要放在这个后面,否则不会识别。</p>\n<p>如果项目里还引入了其他第三方库,那么就需要安装对应的描述文件。可以通过 <a href=\"http://definitelytyped.org/tsd/\">tsd</a> 工具来安装。</p>\n<p>第三方类库的描述文件列表可以从 <a href=\"https://github.com/DefinitelyTyped/DefinitelyTyped\">https://github.com/DefinitelyTyped/DefinitelyTyped</a> 找到,基本覆盖了一些比较热门的类库。</p>\n<h3 id=\"typescript-\">TypeScript 编译</h3>\n<p>由于 TypeScript 的编译功能有很多缺陷,所以现在的方案是通过 TypeScript 将 <code>.ts</code> 代码编译为 ES6 代码,然后使用 Babel 6 编译为 ES5 代码。</p>\n<p>如果发现 TypeScript 有问题,可以给 TypeScript 提 issue,帮助完善,地址为:<a href=\"https://github.com/Microsoft/TypeScript\">https://github.com/Microsoft/TypeScript</a>。</p>\n<h3 id=\"-typescript-\">已有项目升级为 TypeScript 项目</h3>\n<p>对于已有用 ES6/7 特性开发的项目可以很方便的升级为 TypeScript 项目,具体如下:</p>\n<h4 id=\"-\">修改入口文件</h4>\n<p>修改入口文件 <code>www/development.js</code>,将之前 <code>compile</code> 相关的代码改为:</p>\n<pre><code class=\"lang-js\">//compile src/ to app/\ninstance.compile({\n log: true,\n type: \'ts\' //TypeScript\n});\n</code></pre>\n<h4 id=\"-package-json\">修改 package.json</h4>\n<p>修改配置文件 <code>package.json</code>,删除之前 <code>Babel</code> 和 <code>ThinkJS</code> 相关模块的依赖,添加如下的依赖:</p>\n<pre><code>{\n \"dependencies\": {\n \"thinkjs\": \"2.1.x\",\n \"babel-runtime\": \"6.x.x\"\n },\n \"devDependencies\": {\n \"typescript\": \"next\",\n \"babel-cli\": \"6.x.x\",\n \"babel-preset-es2015-loose\": \"6.x.x\",\n \"babel-preset-stage-1\": \"6.x.x\",\n \"babel-plugin-transform-runtime\": \"6.x.x\",\n \"babel-core\": \"6.x.x\"\n }\n}\n</code></pre><p>如果 <code>dependencies</code> 和 <code>devDependencies</code> 里已经有一些项目里要用的模块依赖,需要合并在一起。</p>\n<p>修改完成后,执行 <code>npm install</code> 安装对应的依赖。</p>\n<h4 id=\"-thinkjsrc\">修改 .thinkjsrc</h4>\n<p>修改项目配置文件 <code>.thinkjsrc</code>,修改为类似如下的配置:</p>\n<pre><code class=\"lang-json\">{\n \"createAt\": \"2016-01-13 17:27:19\",\n \"mode\": \"module\",\n \"ts\": true\n}\n</code></pre>\n<h4 id=\"-think-d-ts-\">下载 think.d.ts 描述文件</h4>\n<p>下载文件 <a href=\"https://github.com/75team/thinkjs/blob/master/template/think.d.ts\">https://github.com/75team/thinkjs/blob/master/template/think.d.ts</a>,保存为 <code>typings/thinkjs/think.d.ts</code>。</p>\n<h4 id=\"-\">修改文件后缀</h4>\n<p>将 <code>src/</code> 目录下所有的 <code>.js</code> 文件修改为 <code>.ts</code> 文件。</p>\n<h4 id=\"-bin-compile-js-\">添加 bin/compile.js 文件</h4>\n<p>下载文件 <a href=\"https://github.com/75team/thinkjs/blob/master/template/bin/compile.ts\">https://github.com/75team/thinkjs/blob/master/template/bin/compile.ts</a>,保存为 <code>bin/compile.js</code>。</p>\n<h4 id=\"-compile-\">修改 compile 命令</h4>\n<p>将 <code>package.json</code> 里原有的 compile 命令修改为 <code>node bin/compile.js</code>。</p>\n<h4 id=\"-\">项目文件里添加描述文件</h4>\n<p>在 <code>src/</code> 目录下所有文件内容顶部加上如下的代码,要注意相对路径是否正确:</p>\n<pre><code class=\"lang-js\">/// <reference path=\"../../../typings/thinkjs/think.d.ts\" />\n</code></pre>\n<p>全部修改后,执行 <code>npm start</code> 就可以启动服务了。</p><p>文章来源:<a href=\"http://www.thinkjs.org/\">http://www.thinkjs.org</a></p><p><br></p>', '', '', '2016-07-17 14:02:06', '47', '0', '0', '0', '18', 'thinkjs,nodejs', '0', '1', '', '8', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('274', 'Logic', '', '<p>当在 Action 里处理用户的请求时,经常要先获取用户提交过来的数据,然后对其校验,如果校验没问题后才能进行后续的操作。当参数校验完成后,有时候还要进行权限判断,等这些都判断无误后才能进行真正的逻辑处理。如果将这些代码都放在一个 Action 里,势必让 Action 的代码非常复杂且冗长。</p>\n<p>为了解决这个问题, ThinkJS 在控制器前面增加了一层 <code>Logic</code>,Logic 里的 Action 和控制器里的 Action 一一对应,系统在调用控制器里的 Action 之前会自动调用 Logic 里的 Action。</p>\n<h3 id=\"logic-\">Logic 层</h3>\n<p>Logic 目录在 <code>src/[module]/logic</code>,在通过命令 <code>thinkjs controller [name]</code> 创建 Controller 时会自动创建对应的 Logic。Logic 代码类似如下:</p>\n<pre><code class=\"lang-js\">\'use strict\';\n/**\n * logic\n * @param {} []\n * @return {} []\n */\nexport default class extends think.logic.base {\n /**\n * index action logic\n * @return {} []\n */\n indexAction(){\n\n }\n}\n</code></pre>\n<p>其中,Logic 里的 Action 和 Controller 里的 Action 一一对应。Logic 里也支持 <code>__before</code> 和 <code>__after</code> 等魔术方法。</p>\n<h3 id=\"-\">请求类型校验配置</h3>\n<p>对应一个特定的 Action,有时候只需要一种或者二三种请求类型,需要将其他类型的请求给拒绝掉。可以通过配置特定的请求类型来完成校验。</p>\n<pre><code class=\"lang-js\">export default class extends think.logic.base {\n indexAction(){\n this.allowMethods = \'post\'; //只允许 POST 请求类型\n }\n testAction(){\n this.allowMethods = \'get,post\'; //只允许 GET 和 POST 请求类型\n }\n}\n</code></pre>\n<h3 id=\"-\">数据校验配置</h3>\n<p>数据校验的配置如下:</p>\n<pre><code class=\"lang-js\">export default class extends think.logic.base {\n indexAction(){\n let rules = {\n doc: \"string|default:index\",\n version: \"string|in:1.2,2.0|default:2.0\"\n }\n }\n}\n</code></pre>\n<h4 id=\"-\">配置格式</h4>\n<p>配置格式为 <code>字段名</code> -> <code>配置</code>,每个字段的配置支持多个校验类型,校验类型之间用 <code>|</code> 隔开,校验类型和参数之间用 <code>:</code> 隔开,参数之间用 <code>,</code> 隔开来支持多个参数。</p>\n<h4 id=\"-\">参数格式</h4>\n<p>校验类型后面可以接参数,除了支持用逗号隔开的简单参数外,还可以支持 JSON 格式的复杂参数。如:</p>\n<pre><code class=\"lang-js\">export default class extends think.logic.base {\n indexAction(){\n let rules = {\n field1: \"array|default:[1,2]\", //参数为数组\n field2: \'object|default:{\\\"name\\\":\\\"thinkjs\\\"}\' //参数为对象\n }\n }\n}\n</code></pre>\n<p>除了配置为字符串,也可以配置对象的方式,如:</p>\n<pre><code class=\"lang-js\">export default class extends think.logic.base {\n indexAction(){\n let rules = {\n field1: {required: true, array: true, default: [1, 2]}, //参数为数组\n field2: {object: true, default: {name: \"thinkjs\"}} //参数为对象\n }\n }\n}\n</code></pre>\n<h4 id=\"-\">支持的数据类型</h4>\n<p>支持的数据类型有:<code>boolean</code>、<code>string</code>、<code>int</code>、<code>float</code>、<code>array</code>、<code>object</code>,默认为 <code>string</code>。</p>\n<h4 id=\"-\">默认值</h4>\n<p>使用 <code>default:value</code> 来定义字段的默认值,如果当前字段值为空,会将默认值覆盖过去,后续获取到的值为该默认值。</p>\n<h4 id=\"-\">获取数据的方式</h4>\n<p>默认根据当前请求的类型来获取字段对应的值,如果当前请求类型是 GET,那么会通过 <code>this.get(\'version\')</code> 来获取 <code>version</code> 字段的值。如果请求类型是 POST,那么会通过 <code>this.post</code> 来获取字段的值。</p>\n<p>但有时候在 POST 类型下,可能会获取上传的文件或者获取 URL 上的参数,这时候就需要指定获取数据的方式了。支持的获取数据方式为 <code>get</code>,<code>post</code> 和 <code>file</code>。</p>\n<pre><code class=\"lang-js\">export default class extends think.logic.base {\n /**\n * 保存数据,POST 请求\n * @return {} []\n */\n saveAction(){\n let rules = {\n name: \"required\",\n image: \"object|file|required\",\n version: \"string|get|in:1.2,2.0|default:2.0\"\n }\n }\n}\n</code></pre>\n<p>上面示例指定了字段 <code>name</code> 通过 <code>post</code> 方法来获取值,字段 <code>image</code> 通过 <code>file</code> 方式来获取值,字段 <code>version</code> 通过 <code>get</code> 方式来获取值。</p>\n<h4 id=\"-\">错误信息</h4>\n<p>上面的配置只是指定了具体的校验规则,并没有指定校验出错后给出的错误信息。错误信息支持国际化,需要在配置文件 <code>src/common/config/locale/[lang].js</code> 中定义。如:</p>\n<pre><code class=\"lang-js\">// src/common/config/locale/en.js\nexport default {\n validate_required: \'{name} can not be blank\',\n validate_contains: \'{name} need contains {args}\',\n}\n</code></pre>\n<p>其中 key 为 <code>validate_</code> + <code>校验类型名称</code>,值里面支持 <code>{name}</code> 和 <code>{args}</code> 2个参数,分别代表字段名称和传递的参数。</p>\n<p>如果想定义个特定字段某个错误类型的具体信息,可以通过在后面加上字段名。如:</p>\n<pre><code class=\"lang-js\">// src/common/config/locale/en.js\nexport default {\n validate_required: \'{name} can not be blank\',\n validate_required_email: \'email can not be blank\', //指定字段 email 的 required 错误信息\n}\n</code></pre>\n<h3 id=\"-\">数据校验方法</h3>\n<p>配置好校验规则后,可以通过 <code>this.validate</code> 方法进行校验。如:</p>\n<pre><code class=\"lang-js\">export default class extends think.logic.base {\n indexAction(){\n let rules = {\n doc: \"string|default:index\",\n version: \"string|in:1.2,2.0|default:2.0\"\n }\n let flag = this.validate(rules);\n if(!flag){\n return this.fail(\'validate error\', this.errors());\n }\n }\n}\n</code></pre>\n<p>如果返回值为 <code>false</code>,那么可以通过 <code>this.errors</code> 方法获取详细的错误信息。拿到错误信息后,可以通过 <code>this.fail</code> 方法把错误信息以 JSON 格式输出,也可以通过 <code>this.display</code> 方法输出一个页面。</p>\n<p>错误信息通过 <code>errors</code> 字段赋值到模版里,模版里通过下面的方式显示错误信息(以 ejs 模版为例):</p>\n<pre><code class=\"lang-html\"><%for(var field in errors){%>\n <%-field%>:<%errors[field]%>\n<%}%>\n</code></pre>\n<h5 id=\"-\">自动校验</h5>\n<p>一般情况下,都是校验有问题后,输出一个 JSON 信息。如果每次都要在 Logic 的 Action 手动调用 <code>this.validate</code> 进行校验,势必比较麻烦。可以通过将校验规则赋值给 <code>this.rules</code> 属性进行自动校验。如:</p>\n<pre><code class=\"lang-js\">export default class extends think.logic.base {\n indexAction(){\n this.rules = {\n doc: \"string|default:index\",\n version: \"string|in:1.2,2.0|default:2.0\"\n }\n }\n}\n</code></pre>\n<p>将校验规则赋值给 <code>this.rules</code> 属性后,会在这个 Action 执行完成后自动校验,如果有错误则直接输出 JSON 格式的错误信息。自动校验是通过魔术方法 <code>__after</code> 来完成的。</p>\n<h3 id=\"-\">支持的校验类型</h3>\n<h4 id=\"required\">required</h4>\n<p>必填项。</p>\n<pre><code class=\"lang-js\">export default class extends think.logic.base {\n indexAction(){\n let rules = {\n name: \'required\' //name 的值必填\n }\n }\n}\n</code></pre>\n<h4 id=\"requiredif\">requiredIf</h4>\n<p>当另一个项的值为某些值其中一项时,该项必填。如:</p>\n<pre><code class=\"lang-js\">export default class extends think.logic.base {\n indexAction(){\n let rules = {\n name: \'requiredIf:email,[email protected],[email protected]\'\n }\n }\n}\n</code></pre>\n<p>当 <code>email</code> 的值为 <code>[email protected]</code>,<code>[email protected]</code> 等其中一项时, <code>name</code> 的值必填。</p>\n<h4 id=\"requirednotif\">requiredNotIf</h4>\n<p>当另一个项的值不在某些值中时,该项必填。如:</p>\n<pre><code class=\"lang-js\">export default class extends think.logic.base {\n indexAction(){\n let rules = {\n name: \'requiredNotIf:email,[email protected],[email protected]\'\n }\n }\n}\n</code></pre>\n<p>当 <code>email</code> 的值不为 <code>[email protected]</code>,<code>[email protected]</code> 等其中一项时, <code>name</code> 的值必填。</p>\n<h4 id=\"requiredwith\">requiredWith</h4>\n<p>当其他几项有一项值存在时,该项必填。</p>\n<pre><code class=\"lang-js\">export default class extends think.logic.base {\n indexAction(){\n let rules = {\n name: \'requiredWith:email,title\'\n }\n }\n}\n</code></pre>\n<p>当 <code>email</code>, <code>title</code> 等项有一项值存在时,<code>name</code> 的值必填。</p>\n<h4 id=\"requiredwithall\">requiredWithAll</h4>\n<p>当其他几项值都存在时,该项必填。</p>\n<pre><code class=\"lang-js\">export default class extends think.logic.base {\n indexAction(){\n let rules = {\n name: \'requiredWithAll:email,title\'\n }\n }\n}\n</code></pre>\n<p>当 <code>email</code>, <code>title</code> 等项值都存在时,<code>name</code> 的值必填。</p>\n<h4 id=\"requiredwithout\">requiredWithout</h4>\n<p>当其他几项有一项值不存在时,该项必填。</p>\n<pre><code class=\"lang-js\">export default class extends think.logic.base {\n indexAction(){\n let rules = {\n name: \'requiredWithout:email,title\'\n }\n }\n}\n</code></pre>\n<p>当 <code>email</code>, <code>title</code> 等项其中有一项值不存在时,<code>name</code> 的值必填。</p>\n<h4 id=\"requiredwithoutall\">requiredWithoutAll</h4>\n<p>当其他几项值都不存在时,该项必填。</p>\n<pre><code class=\"lang-js\">export default class extends think.logic.base {\n indexAction(){\n let rules = {\n name: \'requiredWithoutAll:email,title\'\n }\n }\n}\n</code></pre>\n<p>当 <code>email</code>, <code>title</code> 等项值都不存在时,<code>name</code> 的值必填。</p>\n<h4 id=\"contains\">contains</h4>\n<p>值需要包含某个特定的值。</p>\n<pre><code class=\"lang-js\">export default class extends think.logic.base {\n indexAction(){\n let rules = {\n name: \'contains:thinkjs\' //需要包含字符串 thinkjs。\n }\n }\n}\n</code></pre>\n<h4 id=\"equals\">equals</h4>\n<p>和另一项的值相等。</p>\n<pre><code class=\"lang-js\">export default class extends think.logic.base {\n indexAction(){\n let rules = {\n name: \'equals:firstname\' \n }\n }\n}\n</code></pre>\n<p><code>name</code> 的值需要和 <code>firstname</code> 的值相等。</p>\n<h4 id=\"different\">different</h4>\n<p>和另一项的值不等。</p>\n<pre><code class=\"lang-js\">export default class extends think.logic.base {\n indexAction(){\n let rules = {\n name: \'different:firstname\'\n }\n }\n}\n</code></pre>\n<p><code>name</code> 的值不能和 <code>firstname</code> 的值相等。</p>\n<h4 id=\"before\">before</h4>\n<p>值需要在一个日期之前,默认为需要在当前日期之前。</p>\n<pre><code class=\"lang-js\">export default class extends think.logic.base {\n indexAction(){\n let rules = {\n start_time: \'before\', //需要在当前日期之前。\n start_time1: \'before:2015/10/12 10:10:10\' //需要在 2015/10/12 10:10:10 之前。\n }\n }\n}\n</code></pre>\n<h4 id=\"after\">after</h4>\n<p>值需要在一个日期之后,默认为需要在当前日期之后。</p>\n<pre><code class=\"lang-js\">export default class extends think.logic.base {\n indexAction(){\n let rules = {\n end_time: \'after\', //需要在当前日期之后。\n end_time1: \'after:2015/10/10\' //需要在 2015/10/10 之后。\n }\n }\n}\n</code></pre>\n<h4 id=\"alpha\">alpha</h4>\n<p>值只能是 [a-zA-Z] 组成。</p>\n<pre><code class=\"lang-js\">export default class extends think.logic.base {\n indexAction(){\n let rules = {\n en_name: \'alpha\'\n }\n }\n}\n</code></pre>\n<p><code>en_name</code> 的值只能是 [a-zA-Z] 组成。</p>\n<h4 id=\"alphadash\">alphaDash</h4>\n<p>值只能是 [a-zA-Z_] 组成。</p>\n<h4 id=\"alphanumeric\">alphaNumeric</h4>\n<p>值只能是 [a-zA-Z0-9] 组成。</p>\n<h4 id=\"alphanumericdash\">alphaNumericDash</h4>\n<p>值只能是 [a-zA-Z0-9_] 组成。</p>\n<h4 id=\"ascii\">ascii</h4>\n<p>值只能是 ascii 字符组成。</p>\n<h4 id=\"base64\">base64</h4>\n<p>值必须是 base64 编码。</p>\n<h4 id=\"bytelength\">byteLength</h4>\n<p>字节长度需要在一个区间内。</p>\n<pre><code class=\"lang-js\">export default class extends think.logic.base {\n indexAction(){\n let rules = {\n name: \'byteLength:10\' //字节长度不能小于 10\n name1: \'byteLength:10,100\' //字节长度需要在 10 - 100 之间\n }\n }\n}\n</code></pre>\n<h4 id=\"creditcard\">creditcard</h4>\n<p>需要是信用卡数字。</p>\n<h4 id=\"currency\">currency</h4>\n<p>需要是货币。</p>\n<h4 id=\"date\">date</h4>\n<p>需要是个日期。</p>\n<h4 id=\"decimal\">decimal</h4>\n<p>需要是个小数。</p>\n<h4 id=\"divisibleby\">divisibleBy</h4>\n<p>需要被一个数整除。</p>\n<pre><code class=\"lang-js\">export default class extends think.logic.base {\n indexAction(){\n let rules = {\n count: \'divisibleBy:3\' //可以被 3 整除\n }\n }\n}\n</code></pre>\n<h4 id=\"email\">email</h4>\n<p>需要是个 email 格式。</p>\n<h4 id=\"fqdn\">fqdn</h4>\n<p>需要是个合格的域名。</p>\n<h4 id=\"float\">float</h4>\n<p>需要是个浮点数。</p>\n<pre><code class=\"lang-js\">export default class extends think.logic.base {\n indexAction(){\n let rules = {\n money: \'float\' //需要是个浮点数\n money1: \'float:3.2\' //需要是个浮点数,且最小值为 3.2\n money2: \'float:3.2,10.5\' //需要是个浮点数,且最小值为 3.2,最大值为 10.5\n }\n }\n}\n</code></pre>\n<h4 id=\"fullwidth\">fullWidth</h4>\n<p>包含宽字节字符。</p>\n<h4 id=\"halfwidth\">halfWidth</h4>\n<p>包含半字节字符。</p>\n<h4 id=\"hexcolor\">hexColor</h4>\n<p>需要是个十六进制颜色值。</p>\n<h4 id=\"hex\">hex</h4>\n<p>需要是十六进制。</p>\n<h4 id=\"ip\">ip</h4>\n<p>需要是 ip 格式。</p>\n<h4 id=\"ip4\">ip4</h4>\n<p>需要是 ip4 格式。</p>\n<h4 id=\"ip6\">ip6</h4>\n<p>需要是 ip6 格式。</p>\n<h4 id=\"isbn\">isbn</h4>\n<p>需要是图书编码。</p>\n<h4 id=\"isin\">isin</h4>\n<p>需要是证券识别编码。</p>\n<h4 id=\"iso8601\">iso8601</h4>\n<p>需要是 iso8601 日期格式。</p>\n<h4 id=\"in\">in</h4>\n<p>在某些值中。</p>\n<pre><code class=\"lang-js\">export default class extends think.logic.base {\n indexAction(){\n let rules = {\n version: \'in:1.2,2.0\' //需要是 1.2,2.0 其中一个\n }\n }\n}\n</code></pre>\n<h4 id=\"noin\">noin</h4>\n<p>不能在某些值中。</p>\n<pre><code class=\"lang-js\">export default class extends think.logic.base {\n indexAction(){\n let rules = {\n version: \'noin:1.2,2.0\' //不能是 1.2,2.0 其中一个\n }\n }\n}\n</code></pre>\n<h4 id=\"int\">int</h4>\n<p>需要是 int 型。</p>\n<pre><code class=\"lang-js\">export default class extends think.logic.base {\n indexAction(){\n let rules = {\n value: \'int\' //需要是 int 型\n value1: \'int:1\' //不能小于1\n value2: \'int:10,100\' //需要在 10 - 100 之间\n }\n }\n}\n</code></pre>\n<h4 id=\"min\">min</h4>\n<p>不能小于某个值。</p>\n<pre><code class=\"lang-js\">export default class extends think.logic.base {\n indexAction(){\n let rules = {\n value: \'min:10\' //不能小于10\n }\n }\n}\n</code></pre>\n<h4 id=\"max\">max</h4>\n<p>不能大于某个值。</p>\n<pre><code class=\"lang-js\">export default class extends think.logic.base {\n indexAction(){\n let rules = {\n value: \'max:10\' //不能大于10\n }\n }\n}\n</code></pre>\n<h4 id=\"length\">length</h4>\n<p>长度需要在某个范围。</p>\n<pre><code class=\"lang-js\">export default class extends think.logic.base {\n indexAction(){\n let rules = {\n name: \'length:10\' //长度不能小于10\n name1: \'length:10,100\' //长度需要在 10 - 100 之间\n }\n }\n}\n</code></pre>\n<h4 id=\"minlength\">minLength</h4>\n<p>长度不能小于最小长度。</p>\n<pre><code class=\"lang-js\">export default class extends think.logic.base {\n indexAction(){\n let rules = {\n name: \'minLength:10\' //长度不能小于10\n }\n }\n}\n</code></pre>\n<h4 id=\"maxlength\">maxLength</h4>\n<p>长度不能大于最大长度。</p>\n<pre><code class=\"lang-js\">export default class extends think.logic.base {\n indexAction(){\n let rules = {\n name: \'maxLength:10\' //长度不能大于10\n }\n }\n}\n</code></pre>\n<h4 id=\"lowercase\">lowercase</h4>\n<p>需要都是小写字母。</p>\n<h4 id=\"uppercase\">uppercase</h4>\n<p>需要都是大写字母。</p>\n<h4 id=\"mobile\">mobile</h4>\n<p>需要手机号。</p>\n<pre><code class=\"lang-js\">export default class extends think.logic.base {\n indexAction(){\n let rules = {\n mobile: \'mobile:zh-cn\' //必须为中国的手机号\n }\n }\n}\n</code></pre>\n<h4 id=\"mongoid\">mongoId</h4>\n<p>是 MongoDB 的 ObjectID。</p>\n<h4 id=\"multibyte\">multibyte</h4>\n<p>包含多字节字符。</p>\n<h4 id=\"url\">url</h4>\n<p>是个 url。</p>\n<h4 id=\"order\">order</h4>\n<p>数据库查询 order,如:name DESC。</p>\n<h4 id=\"field\">field</h4>\n<p>数据库查询的字段,如:name,title。</p>\n<h4 id=\"image\">image</h4>\n<p>上传的文件是否是个图片。</p>\n<h4 id=\"startwith\">startWith</h4>\n<p>以某些字符打头。</p>\n<h4 id=\"endwith\">endWith</h4>\n<p>以某些字符结束。</p>\n<h4 id=\"string\">string</h4>\n<p>值为字符串。</p>\n<h4 id=\"array\">array</h4>\n<p>值为数组。</p>\n<h4 id=\"boolean\">boolean</h4>\n<p>值为布尔类型。对于字符串 <code>yes</code>, <code>on</code>, <code>1</code>, <code>true</code> 会自动转为布尔 true。</p>\n<h4 id=\"object\">object</h4>\n<p>值为对象。</p>\n<h4 id=\"regexp\">regexp</h4>\n<p>正则,如:</p>\n<pre><code class=\"lang-js\">export default class extends think.logic.base {\n indexAction(){\n this.rules = {\n number: {\n required: true,\n regexp: /^\\d{6}$/\n }\n }\n }\n}\n</code></pre>\n<h3 id=\"-\">扩展校验类型</h3>\n<p>如果默认支持的校验类型不能满足需求,可以通过 <code>think.validate</code> 方法对校验类型进行扩展。如:</p>\n<pre><code class=\"lang-js\">// src/common/bootstrap/validate.js\nthink.validate(\'validate_name\', (value, ...args) => {\n //需要返回 true 或者 false\n //true 表示校验成功,false 表示校验失败\n})\n</code></pre>\n<p>上面注册了一个名为 <code>validate_name</code> 的校验类型,这样在 Logic 里就可以直接使用该校验类型了。</p>\n<h5 id=\"-\">参数解析</h5>\n<p>如果要解析后面的 <code>args</code>,如:该字段值跟其他字段值进行比较,这时拿到的参数是其他字段名称,但比较的时候肯定需要拿到这个字段值,所以需要将字段名称解析为对应的字段值。</p>\n<p>可以通过注册一个解析参数函数来完成。如:上面的校验类型名称为 <code>validate_name</code>,那么对应的解析参数的名称必须为 <code>_validate_name</code>,即:<code>_</code> + <code>校验类型</code>。</p>\n<pre><code class=\"lang-js\">think.validate(\'_validate_name\', (args, data) => {\n let arg0 = args[0];\n args[0] = data[arg0].value; //将第一个参数字段名称解析为对应的参数值\n return args;\n})\n</code></pre><p>文章来源:<a href=\"http://www.thinkjs.org/\">http://www.thinkjs.org</a></p><p><br></p>', '', '', '2016-07-17 14:05:46', '38', '0', '0', '0', '18', 'thinkjs,nodejs', '1', '1', '', '8', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('275', 'Service', '', '<p>有时候项目里需要调用一些第三方的服务,如:调用 Github 相关接口。如果直接在 controller 里直接调用这些接口,一方面导致 controller 代码比较复杂,另一方面也不能更多进行代码复用。</p>\n<p>对于这些情况,可以包装成 service 供 controller 里调用。</p>\n<h3 id=\"-service\">创建 service</h3>\n<p>可以通过命令 <code>thinkjs service [name]</code> 来创建命令,具体使用请见 <a href=\"./thinkjs_command.html#添加-service\">扩展功能 -> ThinkJS 命令 -> 添加 service</a>。</p>\n<p>默认生成的 service 是一个 class,但有些 service 直接提供一些静态方法即可,这时候可以把 class 改为对象即可。</p>\n<h3 id=\"-service\">加载 service</h3>\n<p>可以通过 <code>think.service</code> 加载一个 service,如:</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n indexAction(){\n let GithubService = think.service(\'github\');\n let instance = new GithubService();\n }\n}\n</code></pre>\n<p>如果想跨模块加载 service,可以通过下面的方式:</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n indexAction(){\n let GithubService = think.service(\'github\', \'admin\'); //加载 admin 模块下的 github service\n let instance = new GithubService();\n }\n}\n</code></pre>\n<p><code>注</code>:如果项目不是特别复杂,建议把 service 放在 <code>common</code> 模块下,可以就都可以方便的加载了。</p><p>文章来源:<a href=\"http://www.thinkjs.org/\">http://www.thinkjs.org</a></p>\n<p><br></p>', '', '前端汇', '2016-07-17 14:06:49', '31', '0', '0', '0', '18', 'thinkjs,nodejs', '1', '1', '', '8', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('276', 'Cookie', '', '<h3 id=\"cookie-\">cookie 配置</h3>\n<p>cookie 默认配置如下:</p>\n<pre><code class=\"lang-js\">export default {\n domain: \'\', \n path: \'/\',\n httponly: false, //是否 http only\n secure: false,\n timeout: 0 //有效时间,0 为浏览器进程,单位为秒\n};\n</code></pre>\n<p>默认 cookie 是随着浏览器进程关闭而失效,可以在配置文件 <code>src/common/config/cookie.js</code> 中进行修改。如:</p>\n<pre><code class=\"lang-js\">export default {\n timeout: 7 * 24 * 3600 //将 cookie 有效时间设置为 7 天\n};\n</code></pre>\n<h3 id=\"-cookie\">获取 cookie</h3>\n<p>controller 或者 logic 中,可以通过 <code>this.cookie</code> 方法来获取。如:</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n indexAction(){\n let cookie = this.cookie(\'theme\'); //获取名为 theme 的 cookie\n }\n}\n</code></pre>\n<p>http 对象里也提供了 <code>cookie</code> 方法来获取 cookie。如:</p>\n<pre><code class=\"lang-js\">let cookie = http.cookie(\'theme\');\n</code></pre>\n<h3 id=\"-cookie\">设置 cookie</h3>\n<p>controller 或者 logic 中,可以通过 <code>this.cookie</code> 方法来设置。如:</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n indexAction(){\n this.cookie(\'theme\', \'default\'); //将 cookie theme 值设置为 default\n }\n}\n</code></pre>\n<p>http 对象里也提供了 <code>cookie</code> 方法来设置 cookie。如:</p>\n<pre><code class=\"lang-js\">http.cookie(\'theme\', \'default\');\n</code></pre>\n<p>如果设置 cookie 时想修改一些参数,可以通过第三个参数来控制,如:</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n indexAction(){\n this.cookie(\'theme\', \'default\', {\n timeout: 7 * 24 * 3600 //设置 cookie 有效期为 7 天\n }); //将 cookie theme 值设置为 default\n }\n}\n</code></pre>\n<h3 id=\"-cookie\">删除 cookie</h3>\n<p>controller 或者 logic 中,可以通过 <code>this.cookie</code> 方法来删除。如:</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n indexAction(){\n this.cookie(\'theme\', null); //删除名为 theme 的 cookie\n }\n}\n</code></pre>\n<p>http 对象里也提供了 <code>cookie</code> 方法来删除 cookie。如:</p>\n<pre><code class=\"lang-js\">http.cookie(\'theme\', null);\n</code></pre><p>文章来源:<a href=\"http://www.thinkjs.org/\">http://www.thinkjs.org</a></p>', '', '', '2016-07-17 14:09:11', '39', '0', '0', '0', '18', 'thinkjs,nodejs', '1', '1', '', '8', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('277', 'REST API', '', '<p>项目中,经常要提供一个 API 供第三方使用,一个通用的 API 设计规范就是使用 REST API。REST API 是使用 HTTP 中的请求类型来标识对资源的操作。如:</p>\n<ul>\n<li><code>GET</code> <code>/ticket</code> #获取ticket列表</li>\n<li><code>GET</code> <code>/ticket/12</code> #查看某个具体的ticket</li>\n<li><code>POST</code> <code>/ticket</code> #新建一个ticket</li>\n<li><code>PUT</code> <code>/ticket/12</code> #更新ticket 12</li>\n<li><code>DELETE</code> <code>/ticket/12</code> #删除ticekt 12</li>\n</ul>\n<p>ThinkJS 中提供了很便捷的方式来创建 REST API,创建后无需额外的代码即可响应 REST API 的处理,同时也可以通过定制响应额外的需求。</p>\n<h3 id=\"-rest-api\">创建 REST API</h3>\n<p>通过 <code>thinkjs controller [name] --rest</code> 即可创建一个 REST API。如:</p>\n<pre><code class=\"lang-js\">thinkjs controller home/ticket --rest\n</code></pre>\n<p>上面的命令表示在 <code>home</code> 模块下创建了一个 <code>ticket</code> 的 Rest Controller,该 Controller 用来处理资源 <code>ticket</code> 的请求。</p>\n<h3 id=\"-rest-api-\">处理 REST API 请求</h3>\n<p>Rest Controller 创建完成后,无需写任何的代码,即可完成对 REST API 的处理。资源名称和数据表名称是一一对应的,如:资源名为 <code>ticket</code>,那么对应的数据表为 <code>数据表前缀</code> + <code>ticket</code>。</p>\n<h3 id=\"-\">请求类型</h3>\n<p>REST API 默认是从 HTTP METHOD 里获取当前的请求类型的,如:当前请求类型是 <code>DELETE</code>,表示对资源进行删除操作。</p>\n<p>如果有些客户端不支持发送 <code>DELETE</code> 请求类型,那么可以通过属性 <code>_method</code> 指定一个参数用来接收请求类型。如:</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.rest {\n init(http){\n super.init(http);\n this._method = \'_method\'; //指定请求类型从 GET 参数 _method 里获取\n }\n}\n</code></pre>\n<h3 id=\"-\">字段过滤</h3>\n<p>默认情况下,获取资源信息时,会将资源的所有字段都返回。有时候需要隐藏部分字段,可以通过在 <code>__before</code> 魔术方法里完成此类操作。</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.rest {\n __before(){\n this.modelInstance.fieldReverse(\'password,score\'); //隐藏 password 和 score 字段\n }\n}\n</code></pre>\n<h3 id=\"-\">权限管理</h3>\n<p>有些 REST API 需要进行权限验证,验证完成后才能获取对应的信息,可以通过在 <code>__before</code> 魔术方法里进行验证。</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.rest {\n * __before(){\n let auth = yield this.checkAuth();\n if(!auth){\n return this.fail(\'no permissions\'); //没权限时直接返回\n }\n }\n}\n</code></pre>\n<h3 id=\"-\">更多定制</h3>\n<p>更多定制方式请参见 <a href=\"./api_controller_rest.html\">API -> controller.rest</a>。</p><p>文章来源:<a href=\"http://www.thinkjs.org/\">http://www.thinkjs.org</a></p>\n<p><br></p>', '', '', '2016-07-17 14:10:18', '33', '0', '0', '0', '18', 'thinkjs,nodejs', '1', '1', '', '8', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('278', 'Babel', '', '<p>ThinkJS 2.1 中,将依赖的 Babel 版本从 5 升级到 6。由于 Babel 6 是个彻底重构的版本,完全插件化了,所以很多模块在不同的插件都会有依赖,这样会导致一些问题,如:</p>\n<ul>\n<li>安装后的目录很大,并且首次运行很慢</li>\n<li>目录层级过深,windows 可能会报错</li>\n</ul>\n<p>推荐的解决方案为将 npm 升级到 3,可以通过下面的命令升级:</p>\n<pre><code class=\"lang-sh\">npm install -g npm@3\n</code></pre>\n<h3 id=\"-\">修改编译参数</h3>\n<p>Babel 6 默认的编译参数为:</p>\n<pre><code class=\"lang-js\">{\n presets: [\'es2015-loose\', \'stage-1\'],\n plugins: [\'transform-runtime\']\n}\n</code></pre>\n<p>如果编译参数不能满足你的需求的话,可以在入口文件 <code>www/development.js</code> 里进行修改:</p>\n<pre><code class=\"lang-js\">instance.compile({\n retainLines: true, \n log: true,\n presets: [], //追加的 presets 列表\n plugins: [] //追加的 plugins 列表\n});\n</code></pre>\n<p>后续上线前编译执行 <code>npm run compile</code> 实际上是调用 <code>package.json</code> 里对应的 <code>compile</code> 命令,所以若果有 <code>presets</code> 或者 <code>plugins</code> 修改的话,<code>compile</code> 命令也要对应改下。</p><p>文章来源:<a href=\"http://www.thinkjs.org/\">http://www.thinkjs.org</a></p><p><br></p>', '', '', '2016-07-17 14:11:12', '32', '0', '0', '0', '18', 'thinkjs,nodejs ', '1', '1', '', '8', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('279', 'thinkjs 命令', '', '<p>以全局模式安装 thinkjs 模块后,系统下就会有 thinkjs 命令,在终端执行 <code>thinkjs -h</code> 可以看到详细介绍。</p>\n<pre><code class=\"lang-text\"> Usage: thinkjs [command] <options ...>\n\n\n Commands:\n\n new <projectPath> create project\n module <moduleName> add module\n controller <controllerName> add controller\n service <serviceName> add service\n model <modelName> add model\n middleware <middlewareName> add middleware\n adapter <adapterName> add adapter\n plugin <pluginPath> create ThinkJS plugin\n\n Options:\n\n -h, --help output usage information\n -V, --version output the version number\n -e, --es6 use es6 for project, used in `new` command\n -t, --ts use TypeScript for project, used in `new` command\n -T, --test add test dirs when create project, used in `new` command\n -r, --rest create rest controller, used in `controller` command\n -M, --mongo create mongo model, used in `model` command\n -R, --relation create relation model, used in `model` command\n -m, --mode <mode> project mode type(mini, normal, module), default is module, used in `new` command\n</code></pre>\n<h3 id=\"-\">创建项目</h3>\n<p>创建项目可以通过 <code>thinkjs new <projectPath></code> 来执行,如:</p>\n<pre><code class=\"lang-sh\">thinkjs new thinkjs_demo\n</code></pre>\n<h4 id=\"-es6-7-\">创建 ES6/7 项目</h4>\n<p>如果想使用 ES6/7 特性开发项目,那么创建项目时需要加上 <code>--es</code> 参数,这样生成文件的代码都是 ES6/7 语法的。如:</p>\n<pre><code class=\"lang-sh\">thinkjs new thinkjs_demo --es\n</code></pre>\n<h4 id=\"-typescript-\">创建 TypeScript 项目</h4>\n<p>如果想使用 TypeScript 来开发项目,那么创建项目时需要加上 <code>--ts</code> 参数,这样生成文件的代码都是 TypeScript 语法的。如:</p>\n<pre><code class=\"lang-sh\">thinkjs new thinkjs_demo --ts\n</code></pre>\n<p><code>注</code>:TypeScript 项目文件后缀都是 <code>.ts</code>。</p>\n<h4 id=\"-\">设置项目模式</h4>\n<p>默认创建的项目是按模块来划分的。如果项目比较小,不想按模块来划分的话,可以创建项目时指定 <code>--mode</code> 参数。如:</p>\n<pre><code class=\"lang-sh\">thinkjs new thinkjs_demo --mode=normal\n</code></pre>\n<p>支持的模式列表如下:</p>\n<ul>\n<li><code>normal</code> 普通项目,模块在功能下划分。</li>\n<li><code>module</code> 按模块划分,大型项目或者想严格按模块划分的项目。</li>\n</ul>\n<p><code>注</code>:创建项目后,会在项目下创建一个名为 <code>.thinkjsrc</code> 的隐藏文件,里面标识了当前项目的一些配置,该配置会影响后续创建文件,所以需要将该文件需要纳入到版本库中。</p>\n<h3 id=\"-\">添加模块</h3>\n<p>创建项目时会自动创建模块 <code>common</code> 和 <code>home</code>,如果还需要创建其他的模块,可以在项目目录下通过 <code>thinkjs module [name]</code> 命令来创建。如:</p>\n<pre><code class=\"lang-sh\">thinkjs module admin\n</code></pre>\n<p>执行完成后,会创建目录 <code>src/admin</code>,以及在该目录下创建对应的文件。</p>\n<h3 id=\"-middleware\">添加 middleware</h3>\n<p>可以在项目目录下通过 <code>thinkjs middleware [name]</code> 命令来添加 middleware。如:</p>\n<pre><code class=\"lang-sh\">thinkjs middleware test;\n</code></pre>\n<p>执行完成后,会创建 <code>src/common/middleware/test.js</code> 文件。</p>\n<h3 id=\"-model\">添加 model</h3>\n<p>可以在项目目录下通过 <code>thinkjs model [name]</code> 命令来添加 model。如:</p>\n<pre><code class=\"lang-sh\">thinkjs model user;\n</code></pre>\n<p>执行完成后,会创建 <code>src/common/model/user.js</code> 文件。</p>\n<p>默认会在 <code>common</code> 模块下创建,如果想在其他模块下创建,可以通过指定模块创建。如:</p>\n<pre><code class=\"lang-sh\">thinkjs model home/user;\n</code></pre>\n<p>指定模块为 <code>home</code> 后,会创建 <code>src/home/model/user.js</code> 文件。</p>\n<h5 id=\"-mongo-model\">添加 Mongo Model</h5>\n<p>默认添加的 Model 是关系数据库的模型,如果想创建 Mongo Model,可以通过指定 <code>--mongo</code> 参数来添加。如:</p>\n<pre><code class=\"lang-sh\">thinkjs model home/user --mongo\n</code></pre>\n<h5 id=\"-relation-model\">添加 Relation Model</h5>\n<p>添加关联模型可以通过指定 <code>--relation</code> 参数。如:</p>\n<pre><code class=\"lang-sh\">thinkjs model home/user --relation\n</code></pre>\n<h3 id=\"-controller\">添加 controller</h3>\n<p>可以在项目目录下通过 <code>thinkjs controller [name]</code> 命令来添加 controller。如:</p>\n<pre><code class=\"lang-sh\">thinkjs controller user;\n</code></pre>\n<p>执行完成后,会创建 <code>src/common/controller/user.js</code> 文件,同时会创建 <code>src/common/logic/user.js</code> 文件。</p>\n<p>默认会在 <code>common</code> 模块下创建,如果想在其他模块下创建,可以通过指定模块创建。如:</p>\n<pre><code class=\"lang-sh\">thinkjs controller home/user;\n</code></pre>\n<p>指定模块为 <code>home</code> 后,会创建 <code>src/home/controller/user.js</code> 文件。</p>\n<h5 id=\"-rest-controller\">添加 Rest Controller</h5>\n<p>如果想提供 Rest API,可以带上 <code>--rest</code> 参数来创建。如:</p>\n<pre><code class=\"lang-sh\">thinkjs controller home/user --rest;\n</code></pre>\n<h3 id=\"-service\">添加 service</h3>\n<p>可以在项目目录下通过 <code>thinkjs service [name]</code> 命令来添加 service。如:</p>\n<pre><code class=\"lang-sh\">thinkjs service github; #创建调用 github 接口的 service\n</code></pre>\n<p>执行完成后,会创建 <code>src/common/service/github.js</code> 文件。</p>\n<p>默认会在 <code>common</code> 模块下创建,如果想在其他模块下创建,可以通过指定模块创建。如:</p>\n<pre><code class=\"lang-sh\">thinkjs service home/github;\n</code></pre>\n<p>指定模块为 <code>home</code> 后,会创建 <code>src/home/service/github.js</code> 文件。</p>\n<h3 id=\"-adapter\">添加 adapter</h3>\n<p>可以通过 <code>thinkjs adapter [type]/[name]</code> 来创建 adapter。如:</p>\n<pre><code class=\"lang-sh\">thinkjs adapter template/dot\n</code></pre>\n<p>执行后会创建文件 <code>src/common/adapter/template/dot.js</code>,表示创建一个名为 dot 的模版类型 adapter。</p>\n<h3 id=\"-plugin\">创建 plugin</h3>\n<p>ThinkJS 支持 middleware 和 adapter 2 种插件,可以通过 <code>thinkjs plugin <pluginName></code> 来初始化一个插件,然后进行开发。</p>\n<pre><code class=\"lang-sh\">thinkjs plugin think-template-dot\n</code></pre>\n<p>插件名称建议使用 <code>think-</code> 打头,这样发布到 npm 仓库后,方便其他用户搜索。</p><p>文章来源:<a href=\"http://www.thinkjs.org/\">http://www.thinkjs.org</a></p>\n<p><br></p>', '', '', '2016-07-17 14:12:28', '38', '0', '0', '0', '18', 'thinkjs,nodejs', '1', '1', '', '8', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('280', '静态资源访问', '', '<p>项目开发时,一般都需要在模版里引用静态资源。</p>\n<p>使用 <code>thinkjs</code> 命令创建项目时,会自动创建 <code>www/static</code> 目录,该目录下专门用来存放 JS、CSS、图片等静态资源。</p>\n<h3 id=\"-\">访问静态资源</h3>\n<p>静态资源放在 <code>www/static</code> 目录后,模版里就可以通过下面的方式引入静态资源。</p>\n<h4 id=\"-js-\">模版里引用 JS 文件</h4>\n<pre><code class=\"lang-html\"><script src=\"/static/js/foo.js\"></script>\n</code></pre>\n<h4 id=\"-css-\">模版里引用 CSS 文件</h4>\n<pre><code class=\"lang-html\"><link href=\"/static/css/foo.css\" rel=\"stylesheet\" />\n</code></pre>\n<h4 id=\"-\">模版里引用图片文件</h4>\n<pre><code class=\"lang-html\"><img src=\"/static/img/foo.png\" alt=\"\" >\n</code></pre>\n<h3 id=\"-\">静态资源访问配置</h3>\n<p>对于一个请求是否是静态资源请求,是通过正则来判断的。默认配置如下:</p>\n<pre><code class=\"lang-js\">export default {\n resource_on: true, //是否开启静态资源解析功能\n resource_reg: /^(static\\/|[^\\/]+\\.(?!js|html)\\w+$)/, //判断为静态资源请求的正则\n}\n</code></pre>\n<p>项目里可以根据需要在配置文件里 <code>src/common/config/config.js</code> 进行修改。</p>\n<h3 id=\"-\">线上关闭静态资源访问</h3>\n<p>项目上线后,一般会使用 nginx 等 WEB 服务器做一层代理,这时候就可以将静态资源的请求直接让 nginx 来处理,项目里就可以关闭对静态资源请求的处理来提高性能。</p>\n<p>可以在配置文件 <code>src/common/config/env/prodution.js</code> 里修改配置来关闭,如:</p>\n<pre><code class=\"lang-js\">export default {\n resource_on: false\n}\n</code></pre><p>文章来源:<a href=\"http://www.thinkjs.org/\">http://www.thinkjs.org</a></p><p><br></p>', '', '', '2016-07-17 14:14:39', '38', '0', '0', '0', '18', 'thinkjs,nodejs', '1', '1', '', '8', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('281', '错误处理', '', '<p>系统在处理用户请求时,会遇到各种各样的错误情况。如:系统内部错误,url 不存在,没有权限,服务不可用等,这些情况下需要给用户显示对应的错误页面。</p>\n<h3 id=\"-\">错误页面</h3>\n<p>通过 <code>thinkjs</code> 命令创建项目时,会自动添加错误处理的逻辑文件以及相应的错误页面。</p>\n<p>错误逻辑文件路径为 <code>src/common/controller/error.js</code>,该文件内容大致如下:</p>\n<pre><code class=\"lang-js\">\'use strict\';\n/**\n * error controller\n */\nexport default class extends think.controller.base {\n /**\n * display error page\n * @param {Number} status []\n * @return {Promise} []\n */\n displayErrorPage(status){\n let module = \'common\';\n if(think.mode !== think.mode_module){\n module = this.config(\'default_module\');\n }\n let file = `${module}/error/${status}.html`;\n let options = this.config(\'tpl\');\n options = think.extend({}, options, {type: \'ejs\'});\n return this.display(file, options);\n }\n /**\n * Bad Request \n * @return {Promise} []\n */\n _400Action(){\n return this.displayErrorPage(400);\n }\n /**\n * Forbidden \n * @return {Promise} []\n */\n _403Action(){\n return this.displayErrorPage(403);\n }\n /**\n * Not Found \n * @return {Promise} []\n */\n _404Action(){\n return this.displayErrorPage(404);\n }\n /**\n * Internal Server Error\n * @return {Promise} []\n */\n _500Action(){\n return this.displayErrorPage(500);\n }\n /**\n * Service Unavailable\n * @return {Promise} []\n */\n _503Action(){\n return this.displayErrorPage(503);\n }\n}\n</code></pre>\n<p>对应的模版文件路径为 <code>view/common/error_{Number}.html</code>。</p>\n<h3 id=\"-\">错误类型</h3>\n<p>系统默认支持的错误类型有 <code>400</code>,<code>403</code>,<code>404</code>,<code>500</code> 和 <code>503</code>。</p>\n<h4 id=\"400\">400</h4>\n<p>错误的请求,如:恶意构造一些非法的数据访问、访问的 url 不合法等。</p>\n<h4 id=\"403\">403</h4>\n<p>当前访问没有权限。</p>\n<h4 id=\"404\">404</h4>\n<p>访问的 url 不存在。</p>\n<h4 id=\"500\">500</h4>\n<p>系统内部出现错误,导致当前请求不可用。</p>\n<h4 id=\"503\">503</h4>\n<p>服务不可用,需要等到恢复后才能访问。</p>\n<h3 id=\"-\">扩展错误类型</h3>\n<p>项目里可以根据需要扩展错误类型,假如添加一个项目特有的错误 <code>600</code>,那么可以通过下面步骤进行:</p>\n<h5 id=\"1-_600action\">1、添加 _600Action</h5>\n<p>在 <code>src/common/controller/error.js</code> 文件中,合适的位置添加如下的代码:</p>\n<pre><code class=\"lang-js\"> _600Action(){\n return this.displayErrorPage(600);\n }\n</code></pre>\n<h5 id=\"2-\">2、添加错误页面</h5>\n<p>添加文件 <code>view/common/error_600.html</code>,并在文件里添加显示的错误内容。</p>\n<h5 id=\"3-\">3、显示错误页面</h5>\n<p>添加完错误后,需要在对应地方调用显示错误才能让用户看到,可以通过 <code>think.statusAction</code> 方法实现。如:</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n indexAction(){\n if(someError){\n return think.statusAction(600, this.http); //显示 600 错误,需要将 http 对象传递进去\n }\n }\n}\n</code></pre>\n<h3 id=\"-\">修改错误页面样式</h3>\n<p>修改错误页面样式,只需要修改对应的模版文件即可,如:修改 <code>404</code> 错误则修改模版文件 <code>view/common/error_404.html</code>。</p><p>文章来源:<a href=\"http://www.thinkjs.org/\">http://www.thinkjs.org</a></p>\n<p><br></p>', '', '', '2016-07-17 14:17:47', '31', '0', '0', '0', '18', 'thinkjs,nodejs', '1', '1', '', '8', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('282', '错误信息', '', '<h3 id=\"eperm\">EPERM</h3>\n<h5 id=\"operation-not-permitted\">Operation Not Permitted</h5>\n<p>尝试执行某些需要特殊权限的操作。</p>\n<h3 id=\"enoent\">ENOENT</h3>\n<h5 id=\"no-such-file-or-directory\">No Such File Or Directory</h5>\n<p>通常由文件系统操作引起,比如路径中某个组件指定的路径或文件并不存在。</p>\n<h3 id=\"eacces\">EACCES</h3>\n<h5 id=\"permission-denied\">Permission Denied</h5>\n<p>拒绝访问。</p>\n<h3 id=\"eexist\">EEXIST</h3>\n<h5 id=\"file-exists\">File Exists</h5>\n<p>要求目标不存在的操作遇到了目标存在的情况。</p>\n<h3 id=\"enotdir\">ENOTDIR</h3>\n<h5 id=\"not-a-directory\">Not a Directory</h5>\n<p>给定的路径存在,但不是想要的文件夹。通常由<code>fs.readdir</code>引起。</p>\n<h3 id=\"eisdir\">EISDIR</h3>\n<h5 id=\"is-a-directory\">Is a Directory</h5>\n<p>操作的目标是文件,但给定的却是文件夹。</p>\n<h3 id=\"emfile\">EMFILE</h3>\n<h5 id=\"too-many-open-files-in-system\">Too Many Open Files In System</h5>\n<p>系统打开的文件数量已经达到上限,至少关闭一个才能打开请求的文件。</p>\n<p>通常不允许过多文件同时打开的系统(如OS X)中出现,要提高限制,可以在运行Node.js进程的同一个sh中运行<code>ulimit -n 2048</code>。</p>\n<h3 id=\"epipe\">EPIPE</h3>\n<h5 id=\"broken-pipe\">Broken Pipe</h5>\n<p>对管道、套按字或FIFO只有写而没有读。通常在<code>net</code>或<code>http</code>层出现,意味着正向其中写入数据的远程服务已关闭。</p>\n<h3 id=\"eaddrinuse\">EADDRINUSE</h3>\n<h5 id=\"address-already-in-use\">Address Already In Use</h5>\n<p>尝试把服务器绑定到一个本地地址,但该地址已经被占用。</p>\n<h3 id=\"econnreset\">ECONNRESET</h3>\n<h5 id=\"connection-reset-by-peer\">Connection Reset By Peer</h5>\n<p>连接被对端强制关闭。通常在远程套接字通信中由于对端超时或重启而丢失连接时导致。常见于<code>http</code>和<code>net</code>模块。</p>\n<h3 id=\"econnrefused\">ECONNREFUSED</h3>\n<h5 id=\"connection-refused\">Connection Refused</h5>\n<p>目标机器频繁拒绝导致无法建立连接。通常在尝试连接外部非活动主机时发生。</p>\n<h3 id=\"enotempty\">ENOTEMPTY</h3>\n<h5 id=\"directory-not-empty\">Directory Not Empty</h5>\n<p>操作目标是空文件夹,但当前文件夹非空,常见于调用<code>fs.unlink</code>。</p>\n<h3 id=\"etimedout\">ETIMEDOUT</h3>\n<h5 id=\"operation-timed-out\">Operation Timed Out</h5>\n<p>目标超过既定时间未响应造成连接或请求失败。通常在<code>http</code>或<code>net</code>中出现,一般表明连接的套接字没有适当<code>.end()</code>。</p><p>文章来源:<a href=\"http://www.thinkjs.org/\">http://www.thinkjs.org</a></p><p><br></p>', '', '', '2016-07-17 14:18:42', '32', '0', '0', '0', '18', 'thinkjs,nodejs', '1', '1', '', '8', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('283', '国际化', '', '<h3 id=\"-\">获取语言</h3>\n<p>可以通过 <code>http.lang</code> 方法从 cookie 或者 header 里获取当前用户的语言。如:</p>\n<pre><code class=\"lang-js\">let lang = http.lang();\n</code></pre>\n<p>如果要支持从 cookie 里获取用户选择的语言,那么需要设置语言在 cookie 里的名称。可以在配置文件 <code>src/common/config/locale.js</code> 里修改,如:</p>\n<pre><code class=\"lang-js\">export default {\n cookie_name: \'think_locale\', //存放语言的 cookie 名称\n default: \'en\' //默认语言\n};\n</code></pre>\n<p>在 Controller 里可以直接通过 <code>this.lang</code> 方法获取对应的语言。</p>\n<h3 id=\"-url-\">从 URL 中解析语言</h3>\n<p>有些情况下,语言是要从 URL 中解析。比如:当前页面的地址是 <code>https://www.thinkjs.org/zh-cn/doc/2.0/i18n.html</code>,就包含了语言 <code>zh-cn</code>。</p>\n<p>这种情况下需要在项目里通过 middleware 来解析其中的语言,如:</p>\n<pre><code class=\"lang-js\">think.middleware(\'get_lang\', http => {\n let supportLangs = think.config(\'locale.support\');\n let lang = http.pathname.split(\'/\')[0]; //从 URL 中获取语言\n\n if(supportLangs.indexOf(lang) > -1){\n http.pathname = http.pathname.substr(lang.length + 1);\n }else{\n lang = http.lang(); //从 cookie 或者 header 中获取语言\n if(supportLangs.indexOf(lang) === -1){\n lang = http.config(\'locale.default\'); //默认支持的语言\n }\n }\n http.lang(lang, true); //设置语言,并设置模版路径中添加语言目录\n});\n</code></pre>\n<p>从 URL 中解析到语言后,通过 <code>http.lang</code> 方法设置语言,后续在 Controller 里可以直接通过 <code>http.lang</code> 来获取语言了。</p>\n<p>定义 middleware <code>get_lang</code> 后,添加到对应的 hook 里。如:</p>\n<pre><code class=\"lang-js\">export default {\n route_parse: [\'prepend\', \'get_lang\'], //将 get_lang 前置添加到 route_parse hook 里\n}\n</code></pre>\n<h3 id=\"-\">语言变量配置</h3>\n<p>支持国际化的项目需要配置变量在不同语言下的值,配置文件在 <code>src/common/config/locale/[lang].js</code>,配置格式如下:</p>\n<pre><code class=\"lang-js\">// src/common/config/locale/zh-cn.js\nexport default {\n \'title-home\': \'ThinkJS官网 - A Node.js MVC Framework Support All Of ES6/7 Features\',\n \'title-changelog\': \'更新日志 - ThinkJS官网\',\n}\n</code></pre>\n<pre><code class=\"lang-js\">// src/common/config/locale/en.js\nexport default {\n \'title-home\': \'ThinkJS - A Node.js MVC Framework Support All Of ES6/7 Features\',\n \'title-changelog\': \'Changelog - ThinkJS\'\n}\n</code></pre>\n<h3 id=\"-\">获取语言变量</h3>\n<p>配置语言变量后,可以通过 <code>http.locale</code> 方法来获取当前语言对应的变量值。如:</p>\n<pre><code class=\"lang-js\">let homeTitle = http.locale(\'title-home\');\n</code></pre>\n<p>如果在 Controller 中,可以直接通过 <code>this.locale</code> 方法来获取,如:</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n indexAction(){\n let homeTitle = this.locale(\'title-home\');\n }\n}\n</code></pre>\n<h3 id=\"-\">模版里使用语言变量</h3>\n<p>模版里可以通过 <code>_</code> 函数来获取对应的语言值。下面以 <code>ejs</code> 模版引擎为例:</p>\n<pre><code class=\"lang-html\"><%- _(\'title-home\') %>\n</code></pre>\n<h3 id=\"-\">设置模版语言路径</h3>\n<p>有些项目中,需要根据不同的语言定制不同的模版,这时模版路径里加一层语言目录来处理就比较合适了。如:<code>view/zh-cn/home/index_index.html</code>,路径中添加了一层 <code>zh-cn</code> 语言目录。</p>\n<p>可以通过 <code>http.lang</code> 方法设置语言并设置在模版路径里添加一层语言目录。如:</p>\n<pre><code class=\"lang-js\">http.lang(lang, true); // true 表示在模版路径里添加一层语言目录\n</code></pre>\n<p>在 Controller 里可以通过 <code>this.lang</code> 方法来设定。如:</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n indexAction(){\n let lang = getFromUrl();\n this.lang(lang, true);\n ...\n }\n}\n</code></pre><p>\n文章来源:<a href=\"http://www.thinkjs.org\" target=\"_blank\">http://www.thinkjs.org</a></p><p><br></p>', '', '', '2016-07-17 14:19:49', '32', '0', '0', '0', '18', 'thinkjs,nodejs', '1', '1', '', '8', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('284', '路径常量', '', '<p>系统提供了很多常量供项目里使用,利用这些常量可以方便的访问对应的文件。</p>\n<h3 id=\"think-root_path\">think.ROOT_PATH</h3>\n<p>项目的根目录。</p>\n<h3 id=\"think-resource_path\">think.RESOURCE_PATH</h3>\n<p>静态资源根目录,路径为 <code>think.ROOT_PATH</code> + <code>/www/</code>。</p>\n<h3 id=\"think-app_path\">think.APP_PATH</h3>\n<p>APP 代码目录,路径为 <code>think.ROOT_PATH</code> + <code>/app/</code>。</p>\n<h3 id=\"think-think_path\">think.THINK_PATH</h3>\n<p>ThinkJS 框架的根目录。</p>\n<h3 id=\"think-think_lib_path\">think.THINK_LIB_PATH</h3>\n<p>ThinkJS 框架 <code>lib</code> 目录。</p>\n<h3 id=\"think-getpath-module-type-\">think.getPath(module, type)</h3>\n<p>对于 model,controller,view 等目录,由于每个模块下都有这些目录,所以无法给出一个固定的路径值。可以通过 <code>think.getPath</code> 来获取模块下的路径。</p>\n<pre><code class=\"lang-js\">let path1 = think.getPath(\'common\', \'model\'); //获取 common 下 model 的目录\nlet path2 = think.getPath(\'home\', \'controller\'); //获取 home 模块下 controller 的目录\n</code></pre>\n<h3 id=\"-\">自定义路径常量</h3>\n<p>除了通过系统给的属性或者方法来获取路径,还可以在项目里定义额外的路径常量。</p>\n<h5 id=\"-\">入口文件里定义</h5>\n<p>项目的入口文件为 <code>src/index.js</code> 或者 <code>src/production.js</code> 等,可以在这些入口文件里定义一些路径常量。如:</p>\n<pre><code class=\"lang-js\">var thinkjs = require(\'thinkjs\');\nvar path = require(\'path\');\n\nvar rootPath = path.dirname(__dirname);\n\nvar instance = new thinkjs({\n APP_PATH: rootPath + \'/app\',\n ROOT_PATH: rootPath,\n RESOURCE_PATH: __dirname,\n UPLOAD_PATH: __dirname + \'/upload\', // 定义文件上传的目录\n env: \'development\'\n});\n\ninstance.run();\n</code></pre>\n<h5 id=\"-\">启动文件里定义</h5>\n<p>定义在 <code>src/common/bootstrap</code> 里的文件在项目启动时会自动加载,所以也可以在这些文件里定义路径常量。如:</p>\n<pre><code class=\"lang-js\">// src/common/bootstrap/common.js\nthink.UPLOAD_PATH = think.RESOURCE_PATH + \'/upload\'; //定义文件上传的目录\n</code></pre><p>\n文章来源:<a href=\"http://www.thinkjs.org\" target=\"_blank\">http://www.thinkjs.org</a></p><p><br></p>', '', '', '2016-07-17 14:21:07', '29', '0', '0', '0', '18', 'thinkjs,nodejs', '1', '1', '', '8', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('285', '定时任务', '', '<p>项目在线上运行时,经常要定时去执行某个功能,这时候就需要使用定时任务来处理了。ThinkJS 支持命令行方式调用,结合系统的 crontab 功能可以很好的支持定时任务。</p>\n<h3 id=\"-\">命令行执行</h3>\n<p>ThinkJS 除了支持通过 URL 访问来执行外,还可以通过命令行的方式调用执行。使用方式如下:</p>\n<pre><code class=\"lang-sh\">node www/production.js home/index/index\n</code></pre>\n<p>上面的命令表示执行 <code>home</code> 模块下 <code>index</code> Controller 里的 indexAction。</p>\n<h5 id=\"-\">携带参数</h5>\n<p>如果需要加参数,只要在后面加上对应的参数即可:</p>\n<pre><code class=\"lang-sh\">node www/production.js home/index/index?name=thinkjs\n</code></pre>\n<p>Action 里就可以通过 <code>this.get</code> 方法来获取参数 <code>name</code> 了。</p>\n<h5 id=\"-\">修改请求方法</h5>\n<p>命令行执行默认的请求类型是 GET,如果想改为其他的类型,可以用下面的方法:</p>\n<pre><code class=\"lang-sh\">node www/production.js url=home/index/index&method=post\n</code></pre>\n<p>这样就把请求类型改为了 post。但这种方式下,参数 url 的值里就不能包含 & 字符了(可以通过 / 的方式指定参数,如<code>node www/production.js url=home/index/index/foo/bar&method=post</code>)。</p>\n<p>除了修改请求类型,还可以修改下面的参数:</p>\n<ul>\n<li><code>host</code> 修改请求的 host 默认为 127.0.0.1</li>\n<li><code>ip</code> 修改请求的 ip 默认为 127.0.0.1</li>\n</ul>\n<h5 id=\"-header\">修改 header</h5>\n<p>有时候如果想修改更多的 headers,可以传一个完整的 json 数据,如:</p>\n<pre><code class=\"lang-sh\">node www/production.js {\"url\":\"/index/index\",\"ip\":\"127.0.0.1\",\"method\":\"POST\",\"headers\":{\"xxx\":\"yyyy\"}}\n</code></pre>\n<h5 id=\"-url-\">禁止 URL 访问</h5>\n<p>默认情况下,命令行执行的 Action 通过 URL 也可以访问到。如果禁止 URL 访问到该 Action,可以通过 <code>this.isCli</code> 来判断。如:</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n indexAction(){\n //禁止 URL 访问该 Action\n if(!this.isCli()){\n this.fail(\'only invoked in cli mode\');\n }\n ...\n }\n}\n</code></pre>\n<h3 id=\"-\">执行脚本</h3>\n<p>可以创建一个简单的执行脚本来调用命令行执行,如:</p>\n<pre><code class=\"lang-sh\">cd project_path; \nnode www/production.js home/index/index;\n</code></pre>\n<p>在项目目录下创建目录 <code>crontab</code>,将上面执行脚本存为一个文件放在该目录下。</p>\n<h3 id=\"-\">定时执行</h3>\n<p>借助系统里的 crontab 可以做到定时执行,通过命令 <code>crontab -e</code> 来编辑定时任务,如:</p>\n<pre><code class=\"lang-sh\">0 */1 * * * /bin/sh project_path/crontab/a.sh # 1 小时执行一次\n</code></pre>\n<h3 id=\"-node-crontab-\">使用 node-crontab 模块执行定时任务</h3>\n<p>除了使用 crontab 和命令行联合执行定时任务外,也可以使用 <code>node-crontab</code> 模块执行定时任务。如:</p>\n<pre><code class=\"lang-js\">import crontab from \'node-crontab\';\n// 1 小时执行一次\nlet jobId = crontab.scheduleJob(\'0 */1 * * *\', () => {\n\n});\n</code></pre>\n<p>将上面代码文件存放在 <code>src/common/bootstrap</code> 目录下,这样可以在服务启动时自动执行。</p>\n<p>如果希望在开发环境下能立即看下执行的效果,可以用类似下面的方式:</p>\n<pre><code class=\"lang-js\">import crontab from \'node-crontab\';\n\nlet fn = () => {\n //定时任务具体逻辑\n //调用一个 Action\n think.http(\'/home/image/spider\', true); //模拟访问 /home/image/spier\n}\n// 1 小时执行一次\nlet jobId = crontab.scheduleJob(\'0 */1 * * *\', fn);\n//开发环境下立即执行一次看效果\nif(think.env === \'development\'){\n fn();\n}\n</code></pre><p>\n文章来源:<a href=\"http://www.thinkjs.org\" target=\"_blank\">http://www.thinkjs.org</a></p>', '', '', '2016-07-17 14:22:31', '36', '0', '0', '0', '18', 'thinkjs,nodejs', '1', '1', '', '8', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('286', '线上部署', '', '<h3 id=\"-\">代码编译</h3>\n<p>开发环境下,代码会自动编译、自动更新,但这种机制时间长了会有一定的内存泄露,所以线上不可使用这种方式。</p>\n<p>需要在代码上线前执行 <code>npm run compile</code> 命令,将 <code>src/</code> 目录编译到 <code>app/</code> 目录,然后将 <code>app/</code> 目录下的文件上线。</p>\n<h3 id=\"-pm2-\">使用 PM2 管理服务</h3>\n<p>PM2 是一款专业管理 Node.js 服务的模块,非常建议在线上使用。使用 PM2 需要以全局的方式安装,如: <code>sudo npm install -g pm2</code>。安装完成后,命令行下会有 pm2 命令。</p>\n<p>创建项目时,会在项目目录下创建名为 <code>pm2.json</code> 的配置文件,内容类似如下:</p>\n<pre><code class=\"lang-js\">{\n \"apps\": [{\n \"name\": \"demo\",\n \"script\": \"www/production.js\",\n \"cwd\": \"/Users/welefen/Develop/git/thinkjs/demo\",\n \"max_memory_restart\": \"1G\",\n \"autorestart\": true,\n \"node_args\": [],\n \"args\": [],\n \"env\": {\n\n }\n }]\n}\n</code></pre>\n<p>将 <code>cwd</code> 配置值改为线上真实的项目路径,然后在项目目录下使用下面的命令来启动/重启服务:</p>\n<pre><code class=\"lang-sh\">pm2 startOrReload pm2.json\n</code></pre>\n<p>如果进程重启之前想进行一些操作,如:保存一些临时数据,那么可以使用 <code>GracefulReload</code>,具体请见:<a href=\"http://pm2.keymetrics.io/docs/usage/pm2-doc-single-page/#graceful-reload。\">http://pm2.keymetrics.io/docs/usage/pm2-doc-single-page/#graceful-reload。</a></p>\n<p>PM2 详细的配置请见 <a href=\"http://pm2.keymetrics.io/docs/usage/application-declaration/\">http://pm2.keymetrics.io/docs/usage/application-declaration/</a>。</p>\n<hr>\n<p><code>注</code>:如果线上不使用 PM2 来管理 Node.js 服务的话,启动服务需要使用命令 <code>node www/production.js</code>。</p>\n<h3 id=\"-nginx-\">使用 nginx 做反向代理</h3>\n<p>创建项目时,会在项目目录创建一个名为 <code>nginx.conf</code> 的 nginx 配置文件。配置文件内容类似如下:</p>\n<pre><code class=\"lang-nginx\">server {\n listen 80;\n server_name localhost;\n root /Users/welefen/Develop/git/thinkjs/demo/www;\n set $node_port 8360;\n\n index index.js index.html index.htm;\n if ( -f $request_filename/index.html ){\n rewrite (.*) $1/index.html break;\n }\n if ( !-f $request_filename ){\n rewrite (.*) /index.js;\n }\n location = /index.js {\n proxy_http_version 1.1;\n proxy_set_header Connection \"\";\n proxy_set_header X-Real-IP $remote_addr;\n proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n proxy_set_header Host $http_host;\n proxy_set_header X-NginX-Proxy true;\n proxy_set_header Upgrade $http_upgrade;\n proxy_set_header Connection \"upgrade\";\n proxy_pass http://127.0.0.1:$node_port$request_uri;\n proxy_redirect off;\n }\n location = /production.js {\n deny all;\n }\n\n location = /testing.js {\n deny all;\n }\n location ~ /static/ {\n etag on;\n expires max;\n }\n}\n</code></pre>\n<p>将 <code>server_name localhost</code> 里的 localhost 修改为对应的域名,将 <code>set $node_port 8360</code> 里的 8360 修改和项目里监听的端口一致。</p>\n<p>修改完成后,将该配置文件拷贝到 nginx 的配置文件目录中,然后通过 <code>nginx -s reload</code> 命令 reload 配置,这样就可以通过域名访问了。</p>\n<p>线上建议开启配置 <code>proxy_on</code>,这样就可以禁止直接通过 IP + 端口来访问。修改配置文件 <code>src/common/config/env/production.js</code>,如:</p>\n<pre><code class=\"lang-js\">export default {\n proxy_on: true\n}\n</code></pre>\n<h3 id=\"-\">关闭静态资源处理的配置</h3>\n<p>为了方便开发,ThinkJS 是支持处理静态资源请求的。但代码部署在线上时,是用 nginx 来处理静态资源请求的,这时可以关闭 ThinkJS 里处理静态资源请求的功能来提高性能。</p>\n<p>可以在配置文件 <code>src/common/config/env/production.js</code> 中加入如下的配置:</p>\n<pre><code class=\"lang-js\">export default {\n resource_on: false\n}\n</code></pre>\n<h3 id=\"-cluster\">使用 cluster</h3>\n<p>线上可以开启 cluster 功能达到利用多核 CPU 来提升性能,提高并发处理能力。</p>\n<p>可以在配置文件 <code>src/common/config/env/production.js</code> 中加入如下的配置:</p>\n<pre><code class=\"lang-js\">export default {\n cluster_on: true\n}\n</code></pre>\n<p><code>注</code>:如果使用 PM2 来部署,并且开启了 cluster 模式,那么就无需在开启 ThinkJS 里的 cluster。</p><p>文章来源:<a href=\"http://www.thinkjs.org/\">http://www.thinkjs.org</a></p><p><br></p>', '', '', '2016-07-17 14:23:27', '30', '0', '0', '0', '18', 'thinkjs,nodejs', '1', '1', '', '8', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('287', '单元测试', '', '<p>项目中写一些接口时有时候希望进行单元测试,ThinkJS 从 2.1.4 版本开始创建项目时支持创建单元测试相关的目录。</p>\n<h3 id=\"-\">创建单元测试项目</h3>\n<p>创建项目时带上 <code>--test</code> 参数后就会创建测试相关的目录,如:</p>\n<pre><code class=\"lang-sh\">thinkjs new demo --es --test\n</code></pre>\n<p>创建完成后,项目下会有 <code>test/</code> 目录,该目录有单元测试的示例代码,自己写的单元测试代码也也都放在该目录下。</p>\n<p><code>注</code>:如果 ThinkJS 版本低于 <code>2.1.4</code>,需要先将全局的 ThinkJS 升级。</p>\n<h3 id=\"-\">测试框架</h3>\n<p>默认使用的测试框架是 <code>mocha</code>,代码覆盖率框架为 <code>istanbul</code>。</p>\n<h3 id=\"-\">书写单元测试</h3>\n<p>可以用 <code>describe</code> 和 <code>it</code> 的方式来书写测试代码,如:</p>\n<pre><code class=\"lang-js\">var assert = require(\'assert\');\ndescribe(\'unit test\', function(){\n it(\'test controller\', function(){\n var data = getFromFn();\n assert.equal(data, 1); //测试 data 是否等于 1,不等于则会测试失败\n })\n})\n</code></pre>\n<p>有些接口是异步的,mocha 提供了一个参数来完成,如:</p>\n<pre><code class=\"lang-js\">var assert = require(\'assert\');\ndescribe(\'unit test\', function(){\n it(\'test controller\', function(done){\n getFromFn().then(function(data){\n assert.equal(data, 1); //测试 data 是否等于 1,不等于则会测试失败\n done(); //这里必须执行下 done,告知接口已经拿到数据并校验\n })\n })\n})\n</code></pre>\n<p>更多的测试用户请参考生成的文件 <code>test/index.js</code>,也可以参考 ThinkJS 框架的测试用例 <a href=\"https://github.com/75team/thinkjs/tree/master/test\">https://github.com/75team/thinkjs/tree/master/test</a>。</p>\n<h3 id=\"-\">执行单元测试</h3>\n<p>单元测试写完后,执行 <code>npm test</code> 就可以进行单元测试。如果代码需要编译,可以在另一个标签页面里执行 <code>npm start</code>,这样代码改变后就会自动编译。</p>\n<h3 id=\"-\">老项目支持</h3>\n<p>如果是之前创建的项目,那么通过下面的方式来支持单元测试:</p>\n<ul>\n<li>拷贝 <a href=\"https://github.com/75team/thinkjs/blob/master/template/test/index.js\">https://github.com/75team/thinkjs/blob/master/template/test/index.js</a> 内容到 <code>test/index.js</code> 文件下</li>\n<li>修改 <code>package.json</code> 文件,添加:在 <code>devDependencies</code> 里添加:</li>\n</ul>\n<pre><code class=\"lang-js\">{\n \"devDependencies\": {\n \"mocha\": \"1.20.1\",\n \"istanbul\": \"0.4.0\"\n }\n}\n</code></pre><p>\n文章来源:<a href=\"http://www.thinkjs.org\" target=\"_blank\">http://www.thinkjs.org</a></p>', '', '', '2016-07-17 14:24:49', '36', '0', '0', '0', '18', 'thinkjs,nodejs', '1', '1', '', '8', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('288', '开发插件', '', '<p>ThinkJS 2.0 里支持 2 种类型的插件,一种是 <a href=\"./middleware.html\">Middleware</a>,另一种是 <a href=\"./adapter_intro.html\">Adapter</a>。</p>\n<h3 id=\"-\">创建插件</h3>\n<p>可以通过下面的命令创建一个插件,插件命令建议使用 <code>think-</code> 打头。</p>\n<pre><code class=\"lang-sh\">thinkjs plugin think-xxx\n</code></pre>\n<p>执行后,会自动创建 <code>think-xxx</code> 目录,并可以看到类似下面的信息:</p>\n<pre><code class=\"lang-text\"> create : think-xxx/\n create : think-xxx/src\n create : think-xxx/src/index.js\n create : think-xxx/test\n create : think-xxx/test/index.js\n create : think-xxx/.eslintrc\n create : think-xxx/.npmignore\n create : think-xxx/.travis.yml\n create : think-xxx/package.json\n create : think-xxx/README.md\n\n enter path:\n $ cd think-xxx/\n\n install dependencies:\n $ npm install\n\n watch compile:\n $ npm run watch-compile\n\n run test:\n $ npm run test-cov\n</code></pre>\n<h3 id=\"-\">目录结构</h3>\n<ul>\n<li><code>src/</code> 存放源代码,使用 ES6/7 特性开发</li>\n<li><code>test/</code> 单元测试目录</li>\n<li><code>.eslintrc</code> eslint 检查配置文件</li>\n<li><code>.npmignore</code> npm 发布时忽略的文件</li>\n<li><code>.travis.yml</code> travis 持续集成配置文件</li>\n<li><code>package.json</code> npm 配置文件</li>\n<li><code>README.md</code> 说明文件</li>\n</ul>\n<h3 id=\"-\">安装依赖</h3>\n<pre><code class=\"lang-sh\">npm install --verbose\n</code></pre>\n<h3 id=\"-\">开发</h3>\n<p>代码文件为 <code>src/index.js</code>,默认生成的文件只是一个基本的类输出,没有继承任何类。</p>\n<p>如果是 Middleware,需要继承 <code>think.middleware.base</code> 类。如果是 Adapter,需要继承 <code>think.adapter.base</code> 类。</p>\n<p>开发过程中,可以在命令行下执行 <code>npm run watch-compile</code>,这样文件修改后就会立即编译。</p>\n<h3 id=\"-\">单元测试</h3>\n<p>在 <code>test/index.js</code> 文件书写相关的单元测试,测试框架使用的是 mocha,可以通过下面的命令查看单元测试结果。</p>\n<pre><code class=\"lang-sh\">npm run test-cov\n</code></pre>\n<h3 id=\"-\">说明文档</h3>\n<p>代码开发和单元测试完成后,需要在 <code>README.md</code> 里书写详细的说明文档。</p>\n<h3 id=\"-\">发布</h3>\n<p>可以通过 <code>npm publish</code> 发布模块到 npm 仓库里(如果之前没发布过,会提示创建帐号和密码)。</p>\n<p>发布完成后,可以联系 ThinkJS-Team,经确认无误后,可以添加到官方的插件列表中,并领取相关的奖励。</p><p>文章来源:<a href=\"http://www.thinkjs.org/\">http://www.thinkjs.org</a></p><p><br></p>', '', '', '2016-07-17 14:25:54', '32', '0', '0', '0', '18', 'thinkjs,nodejs', '1', '1', '', '8', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('289', '推荐模块', '', '<h3 id=\"-\">网络请求</h3>\n<ul>\n<li>superagent</li>\n<li>request</li>\n</ul>\n<h3 id=\"-\">日志</h3>\n<ul>\n<li>log4js</li>\n</ul>\n<h3 id=\"-\">日期处理</h3>\n<ul>\n<li>moment</li>\n</ul>\n<h3 id=\"-\">编码转化</h3>\n<ul>\n<li>iconv-lite</li>\n</ul>\n<h3 id=\"-\">图像处理</h3>\n<ul>\n<li>gm</li>\n</ul>\n<h3 id=\"-\">框架</h3>\n<ul>\n<li>thinkjs</li>\n<li>express</li>\n<li>koa</li>\n<li>sails</li>\n</ul>\n<h3 id=\"-\">调试</h3>\n<ul>\n<li>node-inspector</li>\n</ul>\n<h3 id=\"-\">单元测试</h3>\n<ul>\n<li>mocha</li>\n<li>istanbul</li>\n<li>muk</li>\n</ul>\n<h3 id=\"-\">服务管理</h3>\n<ul>\n<li>pm2</li>\n</ul>\n<h3 id=\"-\">邮件</h3>\n<ul>\n<li>nodemailer</li>\n</ul>\n<h3 id=\"-\">定时任务</h3>\n<ul>\n<li>node-crontab</li></ul>\n文章来源:<a href=\"http://www.thinkjs.org/\">http://www.thinkjs.org</a>', '', '', '2016-07-17 14:27:55', '34', '0', '0', '0', '18', 'thinkjs,nodejs', '1', '1', '', '8', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('290', '更多功能', '', '<h3 id=\"-callback-promise\">如何将 callback 包装成 Promise</h3>\n<p>Node.js 本身提供的很多接口都是 callback 形式的,并且很多第三方库提供的接口也是 callback 形式的。但 ThinkJS 需要接口是 Promise 形式的,所以需要将 callback 形式的接口包装成 Promise。</p>\n<p>ThinkJS 提供了 <code>think.promisify</code> 方法可以快速将接口包装为 Promise 方式,具体请见<a href=\"./api_think.html#toc-c09\">这里</a>。</p>\n<h3 id=\"-\">任务队列</h3>\n<p>Node.js 一个很大的优点就是异步 I/O,这样就可以方便的做并行处理,如:并行去请求一些接口,并行去处理一些文件。但操作系统本身对文件句柄是有限制的,如果并行处理的数目不限制,可能就会导致报错。</p>\n<p>这时候一般都是通过任务队列来处理,ThinkJS 里提供了 <code>think.parallelLimit</code> 方法来处理此类需求,具体见<a href=\"./api_think.html#toc-bb7\">这里</a>。</p><p>文章来源:<a href=\"http://www.thinkjs.org/\">http://www.thinkjs.org</a></p>', '', '', '2016-07-17 14:28:52', '45', '0', '0', '0', '18', 'thinkjs,nodejs', '1', '1', '', '8', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('291', 'Think', '', '<p><code>think</code> 是一个全局对象,该对象里包含了大量有用的属性和方法。这些方法在应用的任何地方都可以直接使用,无需再 require。</p>\n<h3 id=\"-\">属性</h3>\n<h4 id=\"think-starttime\">think.startTime</h4>\n<p>服务启动时间,是个 <code>unix</code> 时间戳。</p>\n<h4 id=\"think-env\">think.env</h4>\n<p>当前项目运行的环境,默认支持下面 3 个值,可以在项目启动时指定:</p>\n<ul>\n<li><code>development</code> 开发环境,会自动更新修改的文件</li>\n<li><code>testing</code> 测试环境</li>\n<li><code>production</code> 线上环境,代码上线时使用</li>\n</ul>\n<h4 id=\"think-dirname\">think.dirname</h4>\n<p>项目的文件夹名称,可以在项目启动时指定,默认值如下:</p>\n<pre><code class=\"lang-js\">think.dirname = {\n config: \'config\', //配置文件目录\n controller: \'controller\', //控制器目录\n model: \'model\', //模型目录\n adapter: \'adapter\', //适配器目录\n logic: \'logic\', //逻辑目录\n service: \'service\', //服务目录\n view: \'view\', //视图目录\n middleware: \'middleware\', //中间件目录\n common: \'common\', //通用目录\n bootstrap: \'bootstrap\', //启动目录 \n locale: \'locale\' //本土化目录\n}\n</code></pre>\n<h4 id=\"think-port\">think.port</h4>\n<p>项目运行的端口,可以在项目启动时指定。如果指定,则忽略配置文件里的端口。</p>\n<h4 id=\"think-sep\">think.sep</h4>\n<p>目录分隔符,等同于 <code>path.sep</code>。</p>\n<h4 id=\"think-ismaster\">think.isMaster</h4>\n<p>是否是 master 进程。</p>\n<h4 id=\"think-cli\">think.cli</h4>\n<p>是否是命令行模式在运行项目,默认为 <code>false</code>。如果是命令行模式,则该值为传递的参数,可以通过下面的方式启动命令行模式。</p>\n<pre><code>node www/index.js /home/index/test\n</code></pre><h4 id=\"think-lang\">think.lang</h4>\n<p>系统当前的语言,从环境变量中读取,在 <code>Windows</code> 下可能为空。</p>\n<h4 id=\"think-mode\">think.mode</h4>\n<p>项目当前的模式,框架支持 2 中项目模式:</p>\n<ul>\n<li><code>think.mode_normal</code> 多模块模式,目录结构只有 <code>Controller</code>,<code>View</code>,<code>Logic</code> 等分模块</li>\n<li><code>think.mode_module</code> 多模块模式,严格按照模块来划分目录结构</li>\n</ul>\n<h4 id=\"think-version\">think.version</h4>\n<p>ThinkJS当前的版本</p>\n<h4 id=\"think-module\">think.module</h4>\n<p>当前项目下的模块列表。</p>\n<h4 id=\"think-think_path\">think.THINK_PATH</h4>\n<p>ThinkJS代码的路径</p>\n<h4 id=\"think-think_lib_path\">think.THINK_LIB_PATH</h4>\n<p>ThinkJS代码 <code>lib/</code> 的具体路径</p>\n<h4 id=\"think-root_path\">think.ROOT_PATH</h4>\n<p>项目的根目录,在 <code>www/index.js</code> 中定义</p>\n<h4 id=\"think-app_path\">think.APP_PATH</h4>\n<p>项目的 <code>app</code> 目录,在 <code>www/index.js</code> 中定义</p>\n<h4 id=\"think-resource_path\">think.RESOURCE_PATH</h4>\n<p>项目的静态资源根目录,在 <code>www/index.js</code> 中定义</p>\n<h4 id=\"think-runtime_path\">think.RUNTIME_PATH</h4>\n<p>Runtime 目录,默认为当前项目下的 <code>runtime/</code> 目录。</p>\n<h3 id=\"-\">方法</h3>\n<h4 id=\"think-class-methods-clean-\">think.Class(methods, clean)</h4>\n<p>动态的创建一个类,默认继承自 think.base 。 如果使用 ES6 特性进行开发的话,可以直接使用 ES6 里的 class 来创建类。</p>\n<pre><code class=\"lang-js\">//继承自 think.base\nvar Cls1 = think.Class({\n getName: function(){\n\n }\n})\n</code></pre>\n<h5 id=\"-think-base\">不继承 think.base</h5>\n<pre><code class=\"lang-js\">var Cls2 = think.Class({\n getName: function(){\n\n }\n}, true);\n</code></pre>\n<h5 id=\"-\">继承一个类</h5>\n<pre><code class=\"lang-js\">//继承自 Cls2\nvar Cls3 = think.Class(Cls2, {\n init: function(name){\n this.name = name;\n },\n getName: function(){\n\n }\n})\n</code></pre>\n<h5 id=\"-\">实例化类</h5>\n<pre><code class=\"lang-js\">//获取类的实例,自动调用 init 方法\nvar instance = new Cls3(\'thinkjs\');\n</code></pre>\n<h4 id=\"think-extend-target-source1-source2-\">think.extend(target, source1, source2, ...)</h4>\n<ul>\n<li><code>target</code> {Object} 目录对象</li>\n<li><code>source1</code> {Mixed} 源对象1</li>\n<li><code>return</code> {Object} 目录对象</li>\n</ul>\n<p>将 source1, source2 等对象上的属性或方法复制到 target 对象上,类似于 jQuery 里的 $.extend 方法。</p>\n<p>默认为深度复制,可以将第一个参数传 <code>false</code> 进行浅度复制。 </p>\n<pre><code class=\"lang-js\">think.extend({}, {name: \'foo\'}, {value: \'bar\'});\n// returns \n{name: \'foo\', value: \'bar\'}\n</code></pre>\n<h4 id=\"think-isboolean-obj-\">think.isBoolean(obj)</h4>\n<ul>\n<li><code>obj</code> {Mixed} 要检测的对象</li>\n<li><code>return</code> {Boolean}</li>\n</ul>\n<p>检测一个对象是否是布尔值。</p>\n<pre><code class=\"lang-js\">think.isBoolean(true); //true\nthink.isBoolean(false); //true\nthink.isBoolean(\'string\'); //false\n</code></pre>\n<h4 id=\"think-isnumber-obj-\">think.isNumber(obj)</h4>\n<ul>\n<li><code>obj</code> {Mixed} 要检测的对象</li>\n<li><code>return</code> {Boolean}</li>\n</ul>\n<p>检测一个对象是否是数字。</p>\n<pre><code class=\"lang-js\">think.isNumber(1); //true\nthink.isNumber(1.21); //true\n</code></pre>\n<h4 id=\"think-isobject-obj-\">think.isObject(obj)</h4>\n<ul>\n<li><code>obj</code> {Mixed} 要检测的对象</li>\n<li><code>return</code> {Boolean}</li>\n</ul>\n<p>检测是否是对象</p>\n<pre><code class=\"lang-js\">think.isObject({}); //true\nthink.isObject({name: \"welefen\"}); //true\nthink.isObject(new Buffer(\'welefen\')); //false\n</code></pre>\n<h4 id=\"think-isstring-obj-\">think.isString(obj)</h4>\n<ul>\n<li><code>obj</code> {Mixed} 要检测的对象</li>\n<li><code>return</code> {Boolean}</li>\n</ul>\n<p>检测是否是字符串</p>\n<pre><code class=\"lang-js\">think.isString(\"xxx\"); // true\nthink.isString(new String(\"xxx\")); //true\n</code></pre>\n<h4 id=\"think-isfunction-obj-\">think.isFunction(obj)</h4>\n<ul>\n<li><code>obj</code> {Mixed} 要检测的对象</li>\n<li><code>return</code> {Boolean}</li>\n</ul>\n<p>检测是否是函数</p>\n<pre><code class=\"lang-js\">think.isFunction(function(){}); //true\nthink.isFunction(new Function(\"\")); //true\n</code></pre>\n<h4 id=\"think-isdate-obj-\">think.isDate(obj)</h4>\n<ul>\n<li><code>obj</code> {Mixed} 要检测的对象</li>\n<li><code>return</code> {Boolean}</li>\n</ul>\n<p>检测是否是日期对象</p>\n<pre><code class=\"lang-js\">think.isDate(new Date()); //true\n</code></pre>\n<h4 id=\"think-isregexp-obj-\">think.isRegExp(obj)</h4>\n<ul>\n<li><code>obj</code> {Mixed} 要检测的对象</li>\n<li><code>return</code> {Boolean}</li>\n</ul>\n<p>检测是否是正则</p>\n<pre><code class=\"lang-js\">think.isRegExp(/\\w+/); //true\nthink.isRegExp(new RegExp(\"/\\\\w+/\")); //true\n</code></pre>\n<h4 id=\"think-iserror-obj-\">think.isError(obj)</h4>\n<ul>\n<li><code>obj</code> {Mixed} 要检测的对象</li>\n<li><code>return</code> {Boolean}</li>\n</ul>\n<p>检测是否是个错误</p>\n<pre><code class=\"lang-js\">think.isError(new Error(\"xxx\")); //true\n</code></pre>\n<h4 id=\"think-isempty-obj-\">think.isEmpty(obj)</h4>\n<ul>\n<li><code>obj</code> {Mixed} 要检测的对象</li>\n<li><code>return</code> {Boolean}</li>\n</ul>\n<p>检测是否为空</p>\n<pre><code class=\"lang-js\">// 检测是否为空\nthink.isEmpty({}); //true\nthink.isEmpty([]); //true\nthink.isEmpty(\"\"); //true\nthink.isEmpty(0); //true\nthink.isEmpty(null); //true\nthink.isEmpty(undefined); //true\nthink.isEmpty(false); //true\n</code></pre>\n<h4 id=\"think-isarray-obj-\">think.isArray(obj)</h4>\n<ul>\n<li><code>obj</code> {Mixed} 要检测的对象</li>\n<li><code>return</code> {Boolean}</li>\n</ul>\n<p>检测是否是数组</p>\n<pre><code class=\"lang-js\">think.isArray([]); //true\nthink.isArray([1, 2]); //true\nthink.isArray(new Array(10)); //true\n</code></pre>\n<h4 id=\"think-isip4-obj-\">think.isIP4(obj)</h4>\n<ul>\n<li><code>obj</code> {Mixed} 要检测的对象</li>\n<li><code>return</code> {Boolean}</li>\n</ul>\n<p>检测是否是 IP4</p>\n<pre><code class=\"lang-js\">think.isIP4(\"10.0.0.1\"); //true\nthink.isIP4(\"192.168.1.1\"); //true\n</code></pre>\n<h4 id=\"think-isip6-obj-\">think.isIP6(obj)</h4>\n<ul>\n<li><code>obj</code> {Mixed} 要检测的对象</li>\n<li><code>return</code> {Boolean}</li>\n</ul>\n<p>检测是否是 IP6</p>\n<pre><code class=\"lang-js\">think.isIP6(\"2031:0000:130f:0000:0000:09c0:876a:130b\"); //true\nthink.isIP6(\"2031:0000:130f::09c0:876a:130b\"); //true\n</code></pre>\n<h4 id=\"think-isip-obj-\">think.isIP(obj)</h4>\n<ul>\n<li><code>obj</code> {Mixed} 要检测的对象</li>\n<li><code>return</code> {Boolean}</li>\n</ul>\n<p>检测是否是 IP</p>\n<pre><code class=\"lang-js\">think.isIP(\"10.0.0.1\"); //true\nthink.isIP(\"192.168.1.1\"); //true\nthink.isIP(\"2031:0000:130f:0000:0000:09c0:876a:130b\"); //true ip6\n</code></pre>\n<h4 id=\"think-isfile-file-\">think.isFile(file)</h4>\n<ul>\n<li><code>file</code> {Mixed} 要检测的文件路径</li>\n<li><code>return</code> {Boolean}</li>\n</ul>\n<p>检测是否是文件,如果在不存在则返回 false</p>\n<pre><code class=\"lang-js\">think.isFile(\"/home/welefen/a.txt\"); //true\nthink.isFile(\"/home/welefen/dirname\"); //false\n</code></pre>\n<h4 id=\"think-isfileasync-file-\">think.isFileAsync(file)</h4>\n<ul>\n<li><code>file</code> {Mixed} 要检测的文件路径</li>\n<li><code>return</code> {Boolean}</li>\n</ul>\n<p>异步检测是否是文件,返回一个 Promise。该方法在 <code>2.1.5</code> 版本中添加。</p>\n<h4 id=\"think-isdir-dir-\">think.isDir(dir)</h4>\n<ul>\n<li><code>dir</code> {Mixed} 要检测的路径</li>\n<li><code>return</code> {Boolean}</li>\n</ul>\n<p>检测是否是目录,如果不存在则返回 false</p>\n<pre><code class=\"lang-js\">think.isDir(\"/home/welefen/dirname\"); //true\n</code></pre>\n<h4 id=\"think-isdirasync-dir-\">think.isDirAsync(dir)</h4>\n<ul>\n<li><code>dir</code> {Mixed} 要检测的路径</li>\n<li><code>return</code> {Boolean}</li>\n</ul>\n<p>异步检测是否是目录,返回 Promise。该方法在 <code>2.1.5</code> 版本中添加。</p>\n<h4 id=\"think-datetime-date-\">think.datetime(date)</h4>\n<ul>\n<li><code>date</code> {Date} </li>\n<li><code>return</code> {String}</li>\n</ul>\n<p>返回一个格式化的日期,格式为:<code>YYYY-MM-DD HH:ii:ss</code>,如:</p>\n<pre><code class=\"lang-js\">let str = think.datetime();\n//str is 2016-02-01 10:00:00\n</code></pre>\n<p>该方法在 <code>2.1.5</code> 版本中添加。</p>\n<h4 id=\"think-isbuffer-obj-\">think.isBuffer(obj)</h4>\n<ul>\n<li><code>obj</code> {Mixed} 要检测的对象</li>\n<li><code>return</code> {Boolean}</li>\n</ul>\n<p>检测是否是 Buffer</p>\n<pre><code class=\"lang-js\">think.isBuffer(new Buffer(20)); //true\n</code></pre>\n<h4 id=\"think-isnumberstring-obj-\">think.isNumberString(obj)</h4>\n<ul>\n<li><code>obj</code> {Mixed} 要检测的对象</li>\n<li><code>return</code> {Boolean}</li>\n</ul>\n<p>是否是字符串类型的数字</p>\n<pre><code class=\"lang-js\">think.isNumberString(1); //true\nthink.isNumberString(\"1\"); //true\nthink.isNumberString(\"1.23\"); //true\n</code></pre>\n<h4 id=\"think-ispromise-obj-\">think.isPromise(obj)</h4>\n<ul>\n<li><code>obj</code> {Mixed} 要检测的对象</li>\n<li><code>return</code> {Boolean}</li>\n</ul>\n<p>检测是否是个 Promise</p>\n<pre><code class=\"lang-js\">think.isPromise(new Promise(function(){})); //true\nthink.isPromise(getPromise()); //true\n</code></pre>\n<h4 id=\"think-ishttp-obj-\">think.isHttp(obj)</h4>\n<ul>\n<li><code>obj</code> {Mixed} 要检测的对象</li>\n<li><code>return</code> {Boolean}</li>\n</ul>\n<p>检测是否是包装的 http 对象</p>\n<pre><code class=\"lang-js\">think.isHttp(http); // true\n</code></pre>\n<h4 id=\"think-iswritable-path-\">think.isWritable(path)</h4>\n<ul>\n<li><code>path</code> {String} 要写的目录</li>\n<li><code>return</code> {Boolean}</li>\n</ul>\n<p>判断文件或者目录是否可写,如果不存在则返回 false</p>\n<h4 id=\"think-isprevent-obj-\">think.isPrevent(obj)</h4>\n<ul>\n<li><code>obj</code> {Mixed}</li>\n<li><code>return</code> {Boolean}</li>\n</ul>\n<p>判断是否是个阻止类型的 Promise。通过 think.prevent() 会生成该 Promise 。</p>\n<h4 id=\"think-mkdir-p-mode-\">think.mkdir(p, mode)</h4>\n<ul>\n<li><code>p</code> {String} 要创建的目录</li>\n<li><code>mode</code> {Number} 要创建的目录权限,默认为 <code>0777</code></li>\n</ul>\n<p>递归的创建目录,如果目录已经存在,那么修改目录的权限。</p>\n<pre><code class=\"lang-js\">// 假设 /home/welefen/a/b/ 不存在\nthink.mkdir(\"/home/welefen/a/b\");\nthink.mkdir(\"home/welefne/a/b/c/d/e\"); // 递归创建子目录\n</code></pre>\n<h4 id=\"think-rmdir-p-reserve-\">think.rmdir(p, reserve)</h4>\n<ul>\n<li><code>p</code> {String} 要删除的目录</li>\n<li><code>reserve</code> {Boolean} 是否保留该目录。如果为 true,则只删除子目录</li>\n<li><code>return</code> {Promise}</li>\n</ul>\n<p>递归的删除目录,如果目录不存在则直接返回。返回是个 Promise,后续操作要在 <code>then</code> 里执行</p>\n<pre><code class=\"lang-js\">function rmTmp(){\n think.rmdir(\'/foo/bar\').then(function(){\n //后续其他操作\n })\n}\n</code></pre>\n<p>如果使用 <code>Generator Function</code>,则可以使用 <code>yield</code></p>\n<pre><code class=\"lang-js\">function * rmTmp(){\n yield think.rmdir(\'/foo/bar\');\n //后续其他操作\n}\n</code></pre>\n<h4 id=\"think-chmod-p-mode-\">think.chmod(p, mode)</h4>\n<ul>\n<li><code>p</code> {String} 要修改的目录</li>\n<li><code>mode</code> {Number} 目录权限,默认为<code>0777</code></li>\n</ul>\n<p>修改目录权限,如果目录不存在则直接返回</p>\n<pre><code class=\"lang-js\">think.chmod(\"/home/welefen/a\", 0777);\n</code></pre>\n<h4 id=\"think-md5-str-\">think.md5(str)</h4>\n<ul>\n<li><code>str</code> {String} 要计算的字符串</li>\n<li><code>return</code> {String} 返回字符串的 md5 值</li>\n</ul>\n<p>计算字符串的 md5 值</p>\n<pre><code class=\"lang-js\">think.md5(\'thinkjs\'); \n// returns 7821eb623e0b1138a47db6a88c3f56bc\n</code></pre>\n<h4 id=\"think-camelcase-sr-\">think.camelCase(sr)</h4>\n<ul>\n<li><code>str</code> {String} 要转换的字符串</li>\n<li><code>return</code> {String}</li>\n</ul>\n<p>转换为驼峰方式</p>\n<pre><code class=\"lang-js\">think.camelCase(\'a_bbb_ccc\');\n//returns aBbbCcc\n</code></pre>\n<h4 id=\"think-defer-\">think.defer()</h4>\n<ul>\n<li><code>return</code> {Object} Deferred对象</li>\n</ul>\n<p>创建一个 <code>Deferred</code> 对象,<code>new Promise</code> 的一种快捷方式。虽然不建议使用 <code>Deferred</code>这种方式,但有时候不得不使用。如:<code>setTimeout</code>, <code>event</code>。</p>\n<pre><code class=\"lang-js\">//使用Deferred的方式\nvar fn = function(){\n var deferred = think.defer();\n process.nextTick(function(){\n if(xxx){\n deferred.resolve(data);\n }else{\n deferred.reject(err);\n }\n })\n return deferred.promise;\n}\n</code></pre>\n<p>使用 <code>Deferred</code> 方式比直接使用 <code>new Promise</code> 的方法代码更加简洁。</p>\n<pre><code class=\"lang-js\">//直接使用new Promise的方式\nvar fn = function(){\n return new Promise(function(resolve, reject){\n process.nextTick(function(){\n if(xxx){\n resolve(data);\n }else{\n reject(err);\n }\n })\n })\n}\n</code></pre>\n<p>注: 异步 <code>callback</code> 的操作不要使用 <code>Deferred</code> 方式,可以用 <code>think.promisify</code> 方法快速把 <code>callback</code> 包装成 <code>Promise</code>。</p>\n<h4 id=\"think-promisify-fn-receiver-\">think.promisify(fn, receiver)</h4>\n<ul>\n<li><code>fn</code> {Function} 要转化的函数</li>\n<li><code>receiver</code> {Object} this指向</li>\n</ul>\n<p>将异步方法快速包装成 Promise,异步方法必须符合最后一个参数为回调函数,且回调函数的第一个参数为 <code>err</code> 的原则。</p>\n<pre><code class=\"lang-js\">var fs = require(\'fs\');\n\n//获取文件内容\nvar getContent = function(filePath){\n //将readFile方法包装成Promise\n var readFilePromise = think.promisify(fs.readFile, fs);\n //读取文件内容\n return readFilePromise(filePath, \'utf8\');\n}\n\n//获取具体的文件内容\ngetContent(\'/foo/bar/file.txt\').then(function(content){\n console.log(content);\n}).catch(function(err){\n console.error(err.stack);\n})\n</code></pre>\n<h4 id=\"think-reject-err-\">think.reject(err)</h4>\n<ul>\n<li><code>err</code> {Error} Error对象</li>\n<li><code>return</code> {Promise} reject promise</li>\n</ul>\n<p>返回一个 reject promise,与 <code>Promise.reject</code>不同的是,该方法会自动打印错误信息。避免需要调用 catch 方法手工打印错误信息。</p>\n<pre><code class=\"lang-js\">//使用Promise.reject\nvar fn = function(){\n return Promise.reject(new Error(\'xxx\'));\n}\n//需要手工调用catch方法打印错误信息\nfn().catch(function(err){\n console.error(err.stack);\n})\n</code></pre>\n<pre><code class=\"lang-js\">//使用think.reject\nvar fn = function(){\n return think.reject(new Error(\'xxx\'));\n}\n//会自动打印格式化后的错误信息\nfn();\n</code></pre>\n<h4 id=\"think-co\">think.co</h4>\n<p><code>co</code> 模块的别名 <a href=\"https://github.com/tj/co\">https://github.com/tj/co</a></p>\n<h4 id=\"think-lookclass-name-type-module-base-\">think.lookClass(name, type, module, base)</h4>\n<ul>\n<li><code>name</code> {String} 类名</li>\n<li><code>type</code> {String} 类型 (controller | model | logic ...)</li>\n<li><code>module</code> {String} 模块名</li>\n<li><code>base</code> {String} 找不到时找对应的基类</li>\n</ul>\n<p>根据类型,名称来查找类。如果找不到会到 common 模块下查找,如果还是找不到,则查找对应类型的基类。</p>\n<pre><code class=\"lang-js\">//查找 home 模块下 user controller\n//如果找不到,会找 common 模块下 user controller\n//如果还是找不到,会找 base controller\nthink.lookClass(\'user\', \'controller\', \'home\'); \n\n//查找 admin 模块下 user controller\nthink.lookClass(\'admin/user\', \'controller\');\n</code></pre>\n<h4 id=\"think-getpath-module-type-prefix-\">think.getPath(module, type, prefix)</h4>\n<ul>\n<li><code>module</code> {String} 模块名</li>\n<li><code>type</code> {String} 类型,如: controller, model, logic</li>\n<li><code>prefix</code> {String} 前缀</li>\n</ul>\n<p>根据当前项目类型获取对应类型的目录。</p>\n<pre><code class=\"lang-js\">let path = think.getPath(\'home\', \'controller\');\n</code></pre>\n<p>假如当前项目的根目录是<code>/foo/bar</code>,那么获取到的目录为:</p>\n<ul>\n<li>项目模式<code>think.mode_normal</code> 下路径为 <code>/foo/bar/app/controller/home</code></li>\n<li>项目模式<code>think.mode_module</code> 下路径为 <code>/foo/bar/app/home/controller</code></li>\n</ul>\n<h4 id=\"think-require-name-flag-\">think.require(name, flag)</h4>\n<ul>\n<li><code>name</code> {String} </li>\n<li><code>flag</code> {Boolean}</li>\n</ul>\n<h4 id=\"think-saferequire-file-\">think.safeRequire(file)</h4>\n<ul>\n<li><code>file</code> {String} 要加载的文件</li>\n</ul>\n<p>安全的加载一个文件,如果文件不存在,则返回 null,并打印错误信息。</p>\n<h4 id=\"think-prevent-\">think.prevent()</h4>\n<p>返回一个特殊的 reject promise 。该 Promise 可以阻止后续的行为且不会报错。</p>\n<h4 id=\"think-log-msg-type-showtime-\">think.log(msg, type, showTime)</h4>\n<ul>\n<li><code>msg</code> {String | Error} 信息</li>\n<li><code>type</code> {String} 类型</li>\n<li><code>showTime</code> {Number | Boolean} 是否显示时间</li>\n</ul>\n<p>打印日志,该方法打印出来的日志会有时间,类型等信息,方便查看和后续处理。</p>\n<pre><code class=\"lang-js\">think.log(\'WebSocket Status: closed\', \'THINK\');\n//writes \'[2015-09-23 17:43:00] [THINK] WebSocket Status: closed\'\n</code></pre>\n<h5 id=\"-\">打印错误信息</h5>\n<pre><code class=\"lang-js\">think.log(new Error(\'error\'), \'ERROR\');\n//writes \'[2015-09-23 17:50:17] [Error] Error: error\'\n</code></pre>\n<h5 id=\"-\">显示执行时间</h5>\n<pre><code class=\"lang-js\">think.log(\'/static/module/jquery/1.9.1/jquery.js\', \'HTTP\', startTime);\n//writes \'[2015-09-23 17:52:13] [HTTP] /static/module/jquery/1.9.1/jquery.js 10ms\'\n</code></pre>\n<h5 id=\"-\">不显示时间</h5>\n<pre><code class=\"lang-js\">think.log(\'/static/module/jquery/1.9.1/jquery.js\', \'HTTP\', null);\n//writes \'[HTTP] /static/module/jquery/1.9.1/jquery.js\'\n</code></pre>\n<h5 id=\"-\">自定义</h5>\n<pre><code class=\"lang-js\">think.log(function(colors){\n return colors.yellow(\'[WARNING]\') + \' test\';\n});\n//writes \'[WARNING] test\'\n</code></pre>\n<p>其中 <code>colors</code> 为 npm 模块 colors,<a href=\"https://github.com/Marak/colors.js\">https://github.com/Marak/colors.js</a> 。</p>\n<h4 id=\"think-config-name-value-data-\">think.config(name, value, data)</h4>\n<ul>\n<li><code>name</code> {String} 配置名称</li>\n<li><code>value</code> {Mixed} 配置值</li>\n<li><code>data</code> {Object} 配置对象</li>\n</ul>\n<p>读取或者设置配置,可以指定总的配置对象。</p>\n<pre><code class=\"lang-js\">//获取配置\nlet value = think.config(\'name\');\n//获取 admin 模块下的配置\nlet value = think.config(\'name\', undefined, \'admin\');\n\n// 写入配置\nthink.config(\'name\', \'value\');\n</code></pre>\n<h4 id=\"think-getmoduleconfig-module-\">think.getModuleConfig(module)</h4>\n<ul>\n<li><code>module</code> {String} 模块名称</li>\n<li><code>return</code> {Object}</li>\n</ul>\n<p>获取模块的所有配置。该配置包含模块的配置,通用模块的配置,框架默认的配置。</p>\n<pre><code class=\"lang-js\">//获取 admin 模块的所有配置\nlet configs = think.getModuleConfig(\'admin\');\n</code></pre>\n<h4 id=\"think-hook-\">think.hook()</h4>\n<p>注册、获取和执行 hook,项目中可以根据需要追加或者修改。</p>\n<h5 id=\"-middleware-\">获取事件对应的 middleware 列表</h5>\n<pre><code class=\"lang-js\">think.hook(\'view_template\');\n//returns\n[\'locate_template\']\n</code></pre>\n<h5 id=\"-hook\">设置 hook</h5>\n<pre><code class=\"lang-js\">//替换原有的 hook\nthink.hook(\'view_template\', [\'locate_template1\']);\n\n//将原有的之前追加\nthink.hook(\'view_template\', [\'locate_template1\'], \'prepend\');\n\n//将原有的之后追加\nthink.hook(\'view_template\', [\'locate_template1\'], \'append\');\n</code></pre>\n<h5 id=\"-hook\">删除 hook</h5>\n<pre><code class=\"lang-js\">think.hook(\'view_template\', null);\n</code></pre>\n<h5 id=\"-hook\">执行 hook</h5>\n<pre><code class=\"lang-js\">let result = think.hook(\'view_template\', http, data);\n//result is a promise\n</code></pre>\n<h4 id=\"think-middleware-\">think.middleware()</h4>\n<p>注册、创建、获取和执行 middleware。</p>\n<h5 id=\"-middleware\">创建 middleware</h5>\n<pre><code class=\"lang-js\">//解析 XML 示例\nvar ParseXML = think.middleware({\n run: function(){\n var http = this.http;\n return http.getPayload().then(function(payload){\n var data = xmlParse.parse(payload); //使用一个xml解析,这里 xmlParse 是示例\n http._post = data; //将解析后的数据赋值给 http._post,后续可以通过 http.post(\'xxx\') 获取 \n });\n }\n});\n</code></pre>\n<p>使用 ES6 创建 middleware。</p>\n<pre><code class=\"lang-js\">let Cls1 = class extends think.middleware.base {\n run(){\n let http = this.http;\n }\n}\n</code></pre>\n<h5 id=\"-middleware\">注册 middleware</h5>\n<p>middleware 可以是个简单的 function,也可以是较为复杂的 class。</p>\n<pre><code class=\"lang-js\">//注册 middleware 为 function\nthink.middleware(\'parse_xml\', http => {\n\n})\n</code></pre>\n<pre><code class=\"lang-js\">//注册 middleware 为 class\n//会自动调用 run 执行\nlet Cls = think.middleware({\n run: function(){\n let http = this.http;\n\n }\n});\nthink.middleware(\'parse_xml\', Cls);\n</code></pre>\n<h5 id=\"-middleware\">获取 middleware</h5>\n<pre><code class=\"lang-js\">let middleware = think.middleware(\'parse_xml\');\n</code></pre>\n<h5 id=\"-middleware\">执行 middleware</h5>\n<pre><code class=\"lang-js\">let result = think.middleware(\'parse_xml\', http);\n//result is a promise\n</code></pre>\n<h4 id=\"think-adapter-\">think.adapter()</h4>\n<p>创建、注册、获取和执行 adapter。</p>\n<h5 id=\"-adapter\">创建 adapter</h5>\n<pre><code class=\"lang-js\">//创建一个 adapter\nvar Cls = think.adapter({\n\n});\n\n//创建一个 session adapter,继承自 session base 类\nvar Cls = think.adapter(\'session\', \'base\', {\n\n})\n</code></pre>\n<pre><code class=\"lang-js\">//使用 ES6 创建一个 session adapter\nlet Cls = class extends think.adapter.session {\n\n}\n</code></pre>\n<h5 id=\"-adapter\">注册 adapter</h5>\n<pre><code class=\"lang-js\">//注册一个 xxx 类型的 session adapter\nthink.adapter(\'session\', \'xxx\', Cls);\n</code></pre>\n<h5 id=\"-adapter\">获取 adapter</h5>\n<pre><code class=\"lang-js\">//获取 file 类型的 session adapter\nlet Cls = think.adapter(\'session\', \'file\');\n</code></pre>\n<h5 id=\"-adapter\">执行 adapter</h5>\n<pre><code class=\"lang-js\">let Adapter = think.adapter(\'session\', \'file\');\nlet instance = new Adapter(options);\n</code></pre>\n<h4 id=\"think-gc-instance-\">think.gc(instance)</h4>\n<ul>\n<li><code>instance</code> {Object} 类的实例</li>\n</ul>\n<p>注册实例到 gc 队列中。instance 必须含有属性 <code>gcType</code> 和方法 <code>gc</code>。</p>\n<p>像 cache, session 这些功能一般都是有过期时间,过期后需要要进行清除工作。框架提供了一套机制方便清除过期的文件等。</p>\n<pre><code class=\"lang-js\">let Cls = class extends think.adapter.cache {\n init(options){\n super.init(options);\n this.gcType = \'xFileCache\';\n think.gc(this);\n }\n gc(){\n //寻找过期的内容并清除\n }\n}\n</code></pre>\n<h4 id=\"think-http-req-res-\">think.http(req, res)</h4>\n<ul>\n<li><code>req</code> {Object} request 对象</li>\n<li><code>res</code> {Object} response 对象</li>\n<li><code>return</code> {Promise}</li>\n</ul>\n<p>根据 req 和 res 包装成 http 对象。req 和 res 可以自定义。</p>\n<pre><code class=\"lang-js\">//根据一个 url 生成一个 http 对象,方便命令行下调用\nthink.http(\'/index/test\').then(http => {\n\n});\n</code></pre>\n<h4 id=\"think-uuid-length-\">think.uuid(length)</h4>\n<ul>\n<li><code>length</code> {Number} 生成字符串的长度,默认为 32</li>\n</ul>\n<p>生成一个随机字符串。</p>\n<h4 id=\"think-parseconfig-args-\">think.parseConfig(...args)</h4>\n<p>解析配置里的 <code>adapter</code> 和 <code>parser</code>,如:</p>\n<pre><code class=\"lang-js\">let config = think.parseConfig({prefix: \'think_\'}, {\n type: \'mysql\',\n adapter: {\n mysql: {\n prefix: \'test_\',\n host: [\'10.0.0.1\', \'10.0.0.2\'],\n parser: options => {\n return {\n host: \'10.0.0.1\'\n }\n }\n }\n }\n});\n// config value is {prefix: \'test_\', host: \'10.0.0.1\'}\n</code></pre>\n<p>如果只想解析 <code>adapter</code>,而不解析 <code>parser</code>,可以通过传递第一个参数为 <code>true</code>,如:</p>\n<pre><code class=\"lang-js\">let config = think.parseConfig(true, {prefix: \'think_\'}, {\n type: \'mysql\',\n adapter: {\n mysql: {\n prefix: \'test_\',\n host: [\'10.0.0.1\', \'10.0.0.2\'],\n parser: options => {\n return {\n host: \'10.0.0.1\'\n }\n }\n }\n }\n});\n// config value is {prefix: \'test_\', [\'10.0.0.1\', \'10.0.0.2\'], parser: function(){...}}\n</code></pre>\n<h4 id=\"think-session-http-\">think.session(http)</h4>\n<ul>\n<li><code>http</code> {Object} http对象</li>\n</ul>\n<p>生成 session,并写到 http 对象上。如果已经存在,则直接返回。</p>\n<h4 id=\"think-controller-\">think.controller()</h4>\n<p>创建、执行 controller</p>\n<h5 id=\"-controller\">创建 controller</h5>\n<pre><code class=\"lang-js\">//创建 controller, 继承 think.controller.base\nlet Cls = think.controller({\n\n})\n//创建 controller, 继承 think.controller.rest\nlet Cls = think.controller(\'rest\', {\n\n})\n</code></pre>\n<pre><code class=\"lang-js\">//使用 ES6 创建 controller\nlet Cls1 = class extends think.controller.base {\n\n}\n</code></pre>\n<h5 id=\"-controller\">实例化 controller</h5>\n<pre><code class=\"lang-js\">//实例化 home 模块下 user controller\nlet instance = think.controller(\'user\', http, \'home\');\n</code></pre>\n<h4 id=\"think-logic-\">think.logic()</h4>\n<p>创建、执行 logic</p>\n<h5 id=\"-logic\">创建 logic</h5>\n<pre><code class=\"lang-js\">//创建 logic, 继承 think.logic.base\nlet Cls = think.logic({\n\n})\n</code></pre>\n<pre><code class=\"lang-js\">//使用 ES6 创建 logic\nlet Cls1 = class extends think.logic.base {\n\n}\n</code></pre>\n<h5 id=\"-logic\">实例化 logic</h5>\n<pre><code class=\"lang-js\">//实例化 home 模块下 user logic\nlet instance = think.logic(\'user\', http, \'home\');\n</code></pre>\n<h4 id=\"think-model-\">think.model()</h4>\n<p>创建或者获取 model</p>\n<h5 id=\"-model\">创建 model</h5>\n<pre><code class=\"lang-js\">//创建一个 model\nlet model = think.model({\n getList: function(){\n\n }\n});\n\n//ES6 里直接继承 think.model.base 类\nlet model = class extends think.model.base {\n getList(){\n\n }\n}\n\n\n//创建一个 model 继承自 mongo model\nlet model = think.model(\'mongo\', {\n getList: function(){\n\n }\n});\n//ES6 里直接继承 think.model.mongo 类\nlet model = class extends think.model.mongo {\n getList(){\n\n }\n}\n</code></pre>\n<h5 id=\"-model-\">获取 model 实例</h5>\n<pre><code class=\"lang-js\">let configs = {\n host: \'127.0.0.1\',\n name: \'user\'\n}\n//获取 home 模块下 user model\nlet instance = think.model(\'user\', configs, \'home\');\n</code></pre>\n<h4 id=\"think-service-\">think.service()</h4>\n<p>创建或者获取 service</p>\n<h5 id=\"-service\">创建 service</h5>\n<pre><code class=\"lang-js\">//创建一个 service 类\nlet service = think.service({\n\n})\n\n//ES6 里直接继承 think.service.base 类\nlet service = class extends think.service.base {\n\n}\n</code></pre>\n<p>service 基类继承自 <a href=\"./api_think_base.html\">think.base</a>,所以可以用 think.base 里的方法。</p>\n<p>如果 serivce 不想写成类,那就没必要通过这种方法创建。</p>\n<h5 id=\"-service\">获取 service</h5>\n<pre><code class=\"lang-js\">//获取 home 模块下 post service,并传递参数 {} \n//如果获取到的 service 是个类,则自动实例化\nthink.service(\'post\', {}, \'home\');\n</code></pre>\n<h4 id=\"think-cache-name-value-options-\">think.cache(name, value, options)</h4>\n<ul>\n<li><code>name</code> {String} 缓存 key</li>\n<li><code>value</code> {Mixed} 缓存值</li>\n<li><code>options</code> {Object} 缓存选项</li>\n<li><code>return</code> {Promise} 操作都是返回 Promise</li>\n</ul>\n<p>获取、设置或者删除缓存, value 是 <code>undefined</code> 表示读取缓存。 value 是 <code>null</code> 时删除缓存。</p>\n<p>value 为 <code>Function</code> 时表示获取缓存,如果获取不到,则调用该函数,然后将返回值设置到缓存中并返回。</p>\n<pre><code class=\"lang-js\">//获取缓存\nthink.cache(\'name\').then(data => {});\n\n//指定缓存类型获取,从 redis 里获取缓存\nthink.cache(\'name\', undefined, {type: \'redis\'});\n\n//如果缓存 userList 不存在,则查询数据库,并将值设置到缓存中\nthink.cache(\'userList\', () => {\n return think.model(\'user\').select();\n});\n\n//设置缓存\nthink.cache(\'name\', \'value\');\n\n//删除缓存\nthink.cache(\'name\', null);\n</code></pre>\n<h4 id=\"think-locale-key-data-\">think.locale(key, ...data)</h4>\n<ul>\n<li><code>key</code> {String} 要获取的 key</li>\n<li><code>data</code> {Array} 参数</li>\n</ul>\n<p>根据语言获取对应的值,当前语言通过 <code>think.lang</code> 方法来获取,可以在系统启动时指定。</p>\n<pre><code class=\"lang-js\">think.locale(\'CONTROLLER_NOT_FOUND\', \'test\', \'/index/test\');\n//returns \n\'controller `test` not found. url is `/index/test`.\'\n</code></pre>\n<h4 id=\"think-validate-\">think.validate()</h4>\n<p>注册、获取或执行检测。</p>\n<h5 id=\"-\">注册检测方法</h5>\n<pre><code class=\"lang-js\">//注册检测类型为 not_number\nthink.validate(\'not_number\', value => {\n return !(/^\\d+$/.test(value));\n})\n</code></pre>\n<h5 id=\"-\">获取检测方法</h5>\n<pre><code class=\"lang-js\">let fn = think.validate(\'not_number\');\n</code></pre>\n<h5 id=\"-\">检测数据</h5>\n<pre><code class=\"lang-js\">let result = think.validate({\n name: {\n value: \'name\',\n required: true,\n not_number: true\n },\n pwd: {\n value: \'xxx\',\n required: true,\n minLength: 6\n }\n});\n//如果 result 是 isEmpty,表示数据都正常\nif(think.isEmpty(result)){\n\n}\n</code></pre>\n<h4 id=\"think-await-key-callback-\">think.await(key, callback)</h4>\n<ul>\n<li><code>key</code> {String} </li>\n<li><code>callback</code> {Function}</li>\n</ul>\n<p>执行等待,避免一个耗时的操作多次被执行。 callback 需要返回一个 Promise 。</p>\n<p>如:用户访问时,要请求一个远程的接口数据。如果不处理,每个用户请求都会触发这个远程接口的访问,导致有很大的资源浪费。可以让这些用户公用一个远程接口的请求。</p>\n<pre><code class=\"lang-js\">\nimport superagent from \'superagent\';\n\nexport default class extends think.controller.base {\n * indexAction(){\n let result = yield think.await(\'get_xxx_data\', () => {\n let req = superagent.post(\'xxxx\');\n let fn = think.promisify(req.end, req);\n return fn();\n });\n this.success(result);\n }\n}\n</code></pre>\n<h4 id=\"think-npm-pkg-\">think.npm(pkg)</h4>\n<ul>\n<li><code>pkg</code> {String} 模块名</li>\n</ul>\n<p>加载模块。如果模块不存在,则自动安装。这样可以做到动态安装模块。</p>\n<pre><code class=\"lang-js\">//如果mysql模块,则通过npm安装\nlet mysql = think.npm(\'mysql\');\n</code></pre>\n<pre><code class=\"lang-js\">//指定版本加载一个模块\nlet mysql = think.npm(\'[email protected]\')\n</code></pre>\n<h4 id=\"think-error-err-addon-\">think.error(err, addon)</h4>\n<ul>\n<li><code>err</code> {Error | Promise | String} 错误信息</li>\n<li><code>addon</code> {Error | String} 追加的错误信息</li>\n</ul>\n<p>格式化错误信息,将部分系统的错误信息描述完整化。</p>\n<pre><code class=\"lang-js\">let error = think.error(new Error(\'xxx\'));\n</code></pre>\n<h5 id=\"-promise-\">捕获 promise 的错误信息</h5>\n<pre><code class=\"lang-js\">let promise = Project.reject(new Error(\'xxx\'));\npromise = think.error(promise)\n</code></pre>\n<p>自动给 promise 追加 catch,捕获错误信息。</p>\n<h4 id=\"think-statusaction-status-http-log-\">think.statusAction(status, http, log)</h4>\n<ul>\n<li><code>status</code> {Number} 状态码</li>\n<li><code>http</code> {Object} 包装的http对象</li>\n<li><code>log</code> {Boolean} 是否打印错误信息</li>\n</ul>\n<p>当系统出现异常时(系统错误,页面找不到,没权限等),显示对应的错误页面。</p>\n<p>创建项目时,会在 common 模块下生成文件 <code>src/common/controller/error.js</code>,专门用来处理错误情况。</p>\n<p>默认支持的错误类型有:<code>400</code>, <code>403</code>, <code>404</code>, <code>500</code>, <code>503</code>。</p>\n<p>项目里可以根据需要修改错误页面或者扩展。</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n indexAction(){\n if(xxxx){\n let error = new Error(\'not found\');\n //将错误信息写到 http 对象上,用于模版里显示\n this.http.error = error;\n return think.statusAction(404, this.http);\n }\n }\n}\n</code></pre>\n<h4 id=\"think-parallellimit-datalist-callback-options-\">think.parallelLimit(dataList, callback, options)</h4>\n<ul>\n<li><code>dataList</code> {Array} 要处理的数据列表</li>\n<li><code>callback</code> {Function} 处理函数,会将每条数据传递进去,需要返回 Promise</li>\n<li><code>options</code> {Object} 额外选项</li>\n<li><code>return</code> {Promise}</li>\n</ul>\n<p><code>options</code> 包含一下选项:</p>\n<ul>\n<li><code>limit</code> {Number} 并发限制数,默认为 10 条</li>\n<li><code>ignoreError</code> {Boolean} 是否忽略错误,默认情况下一个错误后会停止后续执行</li>\n</ul>\n<p>并发限制处理方法。如:有 10000 条网络数据需要处理,如果同时处理会会网络 IO 错误,此时可以对并发处理进行限制。该方法在 <code>2.0.6</code> 版本中添加。</p>\n<h5 id=\"-\">一个请求下多条数据同时处理场景</h5>\n<pre><code class=\"lang-js\">import superagent from \'superagent\';\n\nexport default class extends think.controller.base {\n async indexAction(){\n let dataList = [...];\n //result 为每条处理结果集合\n //如果某些条数据处理异常,那么对应的数据为 undefined,处理时需要过滤下\n let result = await think.parallelLimit(dataList, item => {\n let url = item.url;\n let req = superagent.get(url);\n let fn = think.promisify(req.end, req); //将 end 方法包装成 Promise\n return fn();\n }, {\n limit: 20, //一次执行 20 条\n ignoreError: true\n })\n }\n}\n</code></pre>\n<h5 id=\"-\">单条数据在多个请求下处理场景</h5>\n<p>有些数据处理虽在一个情况下只用处理一次,但单次处理比较耗时,如果同时请求很多的话可能会导致报错。这个时候也要进行限制,如果当前同时处理数目较多,后续请求则进行等待。</p>\n<p>这个需求可以通过传入一个相同的 key 将任务分组,如:</p>\n<pre><code class=\"lang-js\">import gm from \'gm\';\n\nexport default class extends think.controller.base {\n async indexAction(){\n let result = await think.parallelLimit(\'clip_image\', () => {\n let imageFile = this.file(\'image\').path;\n let instance = gm(imageFile).resize(240, 240).noProfile();\n let fn = think.promisify(instance.write, instance);\n return fn(\'/path/to/save/image.png\');\n }, {\n limit: 20 //一次执行 20 条\n })\n }\n}\n</code></pre>\n<h3 id=\"-\">类</h3>\n<h4 id=\"think-base\">think.base</h4>\n<p>think.base 详细介绍请见 <a href=\"./api_think_base.html\">这里</a></p>\n<h4 id=\"think-http-base\">think.http.base</h4>\n<p>think.http.base 详细介绍请见 <a href=\"./api_think_http_base.html\">这里</a></p><p>文章来源:<a href=\"http://www.thinkjs.org/\">http://www.thinkjs.org</a></p>\n<p><br></p>', '', '', '2016-07-17 14:30:15', '43', '0', '0', '0', '18', 'thinkjs,nodejs', '1', '1', '', '8', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('292', 'think.base', '', '<p><code>think.base</code>是基类,所有的类都会继承该类,该类提供了一些基本的方法。</p>\n<p>使用 ES6 语法继承该类:</p>\n<pre><code class=\"lang-js\">export default class extends think.base {\n /**\n * init method\n * @return {} []\n */\n init(){\n\n }\n}\n</code></pre>\n<p><code>注</code>: 使用 ES6 里的类时不要写 <code>constructor</code>,把初始化的一些操作放在 <code>init</code>方法里,该方法在类实例化时自动被调用,效果等同于 <code>constructor</code>。</p>\n<p>使用普通的方式继承该类:</p>\n<pre><code class=\"lang-js\">module.exports = think.Class(think.base, {\n /**\n * init method\n * @return {} []\n */\n init: function(){\n\n }\n})\n</code></pre>\n<h3 id=\"init-args-\">init(...args)</h3>\n<ul>\n<li><code>args</code> {Array}</li>\n</ul>\n<p>初始化方法,这里可以进行一些赋值等操作。</p>\n<pre><code class=\"lang-js\">class a extends think.base {\n init(name, value){\n this.name = name;\n this.value = value;\n }\n}\n</code></pre>\n<p><code>注</code>:与 <code>1.x</code> 版本不同的是,<code>2.x</code> 版本 <code>init</code> 方法不再支持返回一个 <code>Promise</code>,一些通用操作放在 <code>__before</code> 魔术方法里进行。</p>\n<h3 id=\"__before-\">__before()</h3>\n<p>前置魔术方法,可以将一些通用的行为放在这里进行,如:controller 里检测用户是否登录</p>\n<pre><code class=\"lang-js\">export default class think.controller.base {\n /**\n * 前置魔术方法\n * @return {Promise} []\n */\n * __before(){\n let userInfo = yield this.session(\'userInfo\');\n //如果没有登录,则跳转到登录页面\n if(think.isEmpty(userInfo)){\n return this.redirect(\'/logic\');\n }\n this.assign(\'userInfo\', userInfo)\n }\n}\n</code></pre>\n<h3 id=\"__after-\">__after()</h3>\n<p>后置魔术方法,在方法执行完成后在执行。</p>\n<h3 id=\"filename-\">filename()</h3>\n<ul>\n<li><code>return</code> {String} 返回当前类文件的名称</li>\n</ul>\n<p>获取当前类文件的名称,不包含文件具体路径和扩展名。</p>\n<pre><code class=\"lang-js\">//假设当前类文件具体路径为 /home/xxx/project/app/controller/user.js\nclass a extends think.base {\n test(){\n var filename = this.filename();\n //returns \'user\'\n }\n}\n</code></pre>\n<h3 id=\"parsemodulefrompath-\">parseModuleFromPath()</h3>\n<p>从当前类所在的 <code>filepath</code> 解析出所在对应的模块。</p>\n<h3 id=\"invoke-method-data-\">invoke(method, ...data)</h3>\n<ul>\n<li><code>method</code> {String} 要调用的方法名称</li>\n<li><code>data</code> {Array} 传递的参数</li>\n<li><code>return</code> {Promise}</li>\n</ul>\n<p>调用一个方法,自动调用 <code>__before</code> 和 <code>__after</code> 魔术方法。不管方法本身是否返回 <code>Promise</code>,该方法始终返回 <code>Promise</code>。</p>\n<p>方法本身支持是 <code>*/yield</code> 和<code>async/await</code>。</p>\n<pre><code class=\"lang-js\">//使用 async/await\nclass Cls extends think.base {\n async getValue(){\n let value = await this.getValue();\n return value;\n }\n}\nlet instance = new Cls();\ninstance.invoke(\'getValue\').then(data => {\n\n});\n</code></pre>\n<pre><code class=\"lang-js\">//使用 */yield\nclass Cls extends think.base {\n * getValue(){\n let value = yield this.getValue();\n return value;\n }\n}\nlet instance = new Cls();\ninstance.invoke(\'getValue\').then(data => {\n\n});\n</code></pre><p>\n文章来源:<a href=\"http://www.thinkjs.org\" target=\"_blank\">http://www.thinkjs.org</a></p><p><br></p>', '', '', '2016-07-17 14:31:30', '51', '0', '0', '0', '18', 'thinkjs,nodejs', '1', '1', '', '8', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('293', 'think.http.base', '', '<p><code>think.http.base</code> 继承自 <a href=\"./api_think_base.html\">think.base</a> 类,该类为含有 http 对象处理时的基类。middleware, controller, view 类都继承自该类。</p>\n<h5 id=\"-es6-\">使用 ES6 语法继承该类</h5>\n<pre><code class=\"lang-js\">export default class extends think.http.base {\n /**\n * 初始化方法,实例化时自动被调用,不要写 constructor\n * @return {} \n */\n init(){\n\n }\n}\n</code></pre>\n<h5 id=\"-\">使用普通的方式继承该类</h5>\n<pre><code class=\"lang-js\">module.exports = think.Class(think.http.base, {\n init: function(){\n\n }\n});\n</code></pre>\n<h3 id=\"-\">属性</h3>\n<h4 id=\"http\">http</h4>\n<p>封装的 http 对象,包含的属性和方法请见 <a href=\"./api_http.html\">API -> http</a>。</p>\n<h3 id=\"-\">方法</h3>\n<h4 id=\"config-name-value-\">config(name, value)</h4>\n<ul>\n<li><code>name</code> {String} 配置名称</li>\n<li><code>value</code> {Mixed} 配置值</li>\n</ul>\n<p>读取或者设置配置,value 为 <code>undefined</code> 时为读取配置,否则为设置配置。</p>\n<p>该方法不仅可以读取系统预设值的配置,也可以读取项目里定义的配置。</p>\n<p><code>注</code>:不可将当前请求的用户信息作为配置来设置,会被其他用户给冲掉。</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n indexAction(){\n //获取配置值\n let value = this.config(\'name\');\n }\n}\n</code></pre>\n<h4 id=\"action-controller-action-\">action(controller, action)</h4>\n<ul>\n<li><code>controller</code> {Object | String} controller实例</li>\n<li><code>action</code> {String} action名称</li>\n<li><code>return</code> {Promise} </li>\n</ul>\n<p>调用 controller 下的 action,返回一个 Promise。自动调用 <code>__before</code> 和 <code>__after</code> 魔术方法。</p>\n<p>如果 controller 是字符串,则自动去寻找对应的 controller。</p>\n<pre><code class=\"lang-js\">//调用当前模块下controller里的action\nexport default class extends think.controller.base {\n * indexAction(){\n //调用user controller下的detail方法\n let value = yield this.action(\'user\', \'detail\');\n }\n}\n</code></pre>\n<pre><code class=\"lang-js\">//跨模块调用controller里的action\nexport default class extends think.controller.base {\n * indexAction(){\n //调用admin模块user controller下的detail方法\n let value = yield this.action(\'admin/user\', \'detail\');\n }\n}\n</code></pre>\n<h4 id=\"cache-name-value-options-\">cache(name, value, options)</h4>\n<ul>\n<li><code>name</code> {String} 缓存名称</li>\n<li><code>value</code> {Mixed | Function} 缓存值</li>\n<li><code>options</code> {Object} 缓存配置,具体见缓存配置</li>\n</ul>\n<p>读取或者设置缓存,<code>value</code> 为 <code>undefined</code> 时是读取缓存,否则为设置缓存。默认缓存类型为 <code>file</code>。</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n * indexAction(){\n //获取缓存\n let value = yield this.cache(\'name\');\n }\n}\n</code></pre>\n<p>当参数 <code>value</code> 为 function 时,表示获取缓存,如果缓存值不存在,则调用该 function,将返回值设置缓存并返回。这样避免在项目开发时要先判断缓存是否存在,然后再从相关地方读取值然后设置缓存的麻烦。</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n * indexAction(){\n //获取缓存,缓存不存在时自动调用 function,并设置缓存\n let value = yield this.cache(\'name\', () => {\n return this.model(\'user\').select();\n });\n }\n}\n</code></pre>\n<p>设置缓存并修改缓存类型:</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n * indexAction(){\n //设置缓存,缓存类型使用redis\n yield this.cache(\'name\', \'value\', {\n type: \'redis\'\n });\n }\n}\n</code></pre>\n<h4 id=\"hook-event-data-\">hook(event, data)</h4>\n<ul>\n<li><code>event</code> {String} 事件名称</li>\n<li><code>data</code> {Mixed} 参数</li>\n<li><code>return</code> {Promise}</li>\n</ul>\n<p>执行对应的事件,一个事件包含若干个 middleware,会按顺序执行这些 middleware。</p>\n<p>事件可以在配置 <code>src/common/config/hook.js</code> 里定义,也可以通过 think.hook 来注册。</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n * indexAction(){\n let result = yield this.hook(\'parse_data\');\n }\n}\n</code></pre>\n<h4 id=\"model-name-options-\">model(name, options)</h4>\n<ul>\n<li><code>name</code> {String} 模型名称</li>\n<li><code>options</code> {Object} 配置,具体见数据库配置</li>\n<li><code>return</code> {Object} model实例</li>\n</ul>\n<p>获取模型实例,默认获取当前模块下对应模型的实例,也可以跨模块获取模型的实例。</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n indexAction(){\n //获取当前模块下的 user model 的实例\n let model = this.model(\'user\');\n //获取admin模块下 article model 的实例\n let model1 = this.model(\'admin/article\');\n //获取当前模块下的 test model 的实例,并且是 sqlite 的数据库\n let model2 = this.model(\'test\', {\n type: \'sqlite\' //设置数据库类型为sqlite,更多参数见数据库配置\n })\n }\n}\n</code></pre>\n<h4 id=\"controller-name-\">controller(name)</h4>\n<ul>\n<li><code>name</code> {String} controller名称</li>\n<li><code>return</code> {Object} controller实例</li>\n</ul>\n<p>获取 Controller 的实例,如果 Controller 找不到,则报错。</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n indexAction(){\n //获取当前模块下 user controller 的实例\n let controller = this.controller(\'user\');\n //获取admin模块下 user controller 的实例\n let controller1 = this.controller(\'admin/user\');\n }\n}\n</code></pre>\n<h4 id=\"service-name-\">service(name)</h4>\n<ul>\n<li><code>name</code> {String} service 名称</li>\n<li><code>return</code> {Class} </li>\n</ul>\n<p>获取对应的 service。service 返回的可能是 class ,也可能直接是个对象,所以不会直接实例化。</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n indexAction(){\n //获取对应的service\n let service = this.service(\'user\');\n //获取service的实例\n let instance = new service(...args);\n //获取admin模块下的user service\n let service1 = this.service(\'admin/user\');\n }\n}\n</code></pre><p>\n文章来源:<a href=\"http://www.thinkjs.org\" target=\"_blank\">http://www.thinkjs.org</a></p><p><br></p>', '', '', '2016-07-17 14:33:15', '46', '0', '0', '0', '18', 'thinkjs,nodejs', '1', '1', '', '8', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('294', 'http', '', '<p>这里的 http 对象并不是 Node.js 里的 http 模块,而是对 request 和 response 对象包装后一个新的对象。</p>\n<pre><code class=\"lang-js\">var http = require(\'http\');\n\nhttp.createServer(function (request, response) {\n response.writeHead(200, {\'Content-Type\': \'text/plain\'});\n response.end(\'Hello World\\n\');\n}).listen(8124);\n</code></pre>\n<p>如上面的代码所示,Node.js 创建服务时,会传递 request 和 response 2个对象给回调函数。为了后续调用方便, ThinkJS 对这2个对象进行了包装,包装成了 http 对象,并且提供很多有用的方法。</p>\n<p>http 对象会在 middleware, logic, controller, view 中传递。</p>\n<p><code>注</code>:http 对象是 EventEmitter 的一个实例,所以可以对其进行事件监听和执行。</p>\n<h3 id=\"-\">属性</h3>\n<h4 id=\"http-req\">http.req</h4>\n<p>系统原生的 request 对象</p>\n<h4 id=\"http-res\">http.res</h4>\n<p>系统原生的 response 对象</p>\n<h4 id=\"http-starttime\">http.startTime</h4>\n<p>请求的开始时间,是个<code>unix</code>时间戳。</p>\n<h4 id=\"http-url\">http.url</h4>\n<p>当前请求的 url 。</p>\n<h4 id=\"http-version\">http.version</h4>\n<p>当前请求的 http 版本。</p>\n<h4 id=\"http-method\">http.method</h4>\n<p>当前请求的类型。</p>\n<h4 id=\"http-headers\">http.headers</h4>\n<p>当前请求的所有头信息。</p>\n<h4 id=\"http-pathname\">http.pathname</h4>\n<p>当前请求的 pathname,路由识别会依赖该值,会在后续的处理中对其进行改变。所以在 action 拿到值可能跟初始解析出来的值不一致。</p>\n<h4 id=\"http-query\">http.query</h4>\n<p>当前请求的所有 query 数据。</p>\n<h4 id=\"http-host\">http.host</h4>\n<p>当前请求的 host, 包含端口。</p>\n<h4 id=\"http-hostname\">http.hostname</h4>\n<p>当前请求的 hostname,不包含端口。</p>\n<h4 id=\"http-payload\">http.payload</h4>\n<p>当前请求的 payload 数据,提交型的请求才含有该值。</p>\n<p><code>注</code>:该属性后续会废弃,建议使用 <code>http.getPayload</code> 方法。</p>\n<h4 id=\"http-_get\">http._get</h4>\n<p>存放 GET 参数值。</p>\n<h4 id=\"http-_post\">http._post</h4>\n<p>存放 POST 参数值</p>\n<h4 id=\"http-_file\">http._file</h4>\n<p>存放上传的文件数据</p>\n<h4 id=\"http-_cookie\">http._cookie</h4>\n<p>存放 cookie 数据。</p>\n<h4 id=\"http-module\">http.module</h4>\n<p>当前请求解析后对应的模块名。</p>\n<h4 id=\"http-controller\">http.controller</h4>\n<p>当前请求解析后对应的控制器名。</p>\n<h4 id=\"http-action\">http.action</h4>\n<p>当前请求解析后对应的操作名。</p>\n<h3 id=\"-\">方法</h3>\n<h4 id=\"http-getpayload-\">http.getPayload()</h4>\n<ul>\n<li><code>return</code> {Promise} payload 内容</li>\n</ul>\n<p>获取 payload。</p>\n<h4 id=\"http-config-name-\">http.config(name)</h4>\n<ul>\n<li><code>name</code> {String} 参数名</li>\n<li><code>return</code> {Mixed} 返回对应的参数值</li>\n</ul>\n<p>获取当前请求下对应的参数值。</p>\n<h4 id=\"http-referrer-\">http.referrer()</h4>\n<ul>\n<li><code>return</code> {String} 请求的 referrer</li>\n</ul>\n<p>返回当前请求的 referrer。</p>\n<h4 id=\"http-useragent-\">http.userAgent()</h4>\n<ul>\n<li><code>return</code> {String} 请求的 userAgent</li>\n</ul>\n<p>返回当前请求的 userAgent。</p>\n<h4 id=\"http-isget-\">http.isGet()</h4>\n<ul>\n<li><code>return</code> {Boolean}</li>\n</ul>\n<p>返回当前请求是否是 GET 请求。</p>\n<h4 id=\"http-ispost-\">http.isPost()</h4>\n<ul>\n<li><code>return</code> {Boolean}</li>\n</ul>\n<p>返回当前请求是否是 POST 请求。</p>\n<h4 id=\"http-isajax-method-\">http.isAjax(method)</h4>\n<ul>\n<li><code>method</code> {String} 请求类型</li>\n<li><code>return</code> {Boolean}</li>\n</ul>\n<p>返回当前请求是否是 Ajax 请求。</p>\n<pre><code class=\"lang-js\">http.isAjax(); //判断是否是Ajax请求\nhttp.isAjax(\'GET\'); //判断是否是Ajax请求,且请求类型是GET\n</code></pre>\n<h4 id=\"http-isjsonp-name-\">http.isJsonp(name)</h4>\n<ul>\n<li><code>name</code> {String} callback 参数名称,默认为 callback</li>\n<li><code>return</code> {Boolean}</li>\n</ul>\n<p>返回当前请求是否是 jsonp 请求。</p>\n<pre><code class=\"lang-js\">//url is /index/test?callback=testxxx\nhttp.isJsonp(); //true\nhttp.isJsonp(\'cb\'); //false\n</code></pre>\n<h4 id=\"http-get-name-value-\">http.get(name, value)</h4>\n<ul>\n<li><code>name</code> {String} 参数名称</li>\n<li><code>value</code> {Mixed} 参数值</li>\n</ul>\n<p>获取或者设置 GET 参数值。可以通过该方法设置 GET 参数值,方便后续的逻辑里获取。</p>\n<pre><code class=\"lang-js\">// url is /index/test?name=thinkjs\nhttp.get(\'name\'); // returns \'thinkjs\'\nhttp.get(\'name\', \'other value\');\nhttp.get(\'name\'); // returns \'other value\'\n</code></pre>\n<h4 id=\"http-post-name-value-\">http.post(name, value)</h4>\n<ul>\n<li><code>name</code> {String} 参数名称</li>\n<li><code>value</code> {Mixed} 参数值</li>\n</ul>\n<p>获取或者设置 POST 值。可以通过该方法设置 POST 值,方便后续的逻辑里获取。</p>\n<pre><code class=\"lang-js\">http.post(\'email\'); //获取提交的email\n</code></pre>\n<h4 id=\"http-param-name-\">http.param(name)</h4>\n<ul>\n<li><code>name</code> {String} 参数名称</li>\n<li><code>return</code> {Mixed}</li>\n</ul>\n<p>获取参数值,优先从 POST 里获取,如果值为空,则从 URL 参数里获取。</p>\n<h4 id=\"http-file-name-\">http.file(name)</h4>\n<ul>\n<li><code>name</code> {String} 文件对应的字段名称</li>\n<li><code>return</code> {Object} </li>\n</ul>\n<p>获取上传的文件。</p>\n<pre><code class=\"lang-js\">http.file(\'image\');\n//returns \n{\n fieldName: \'image\', //表单里的字段名\n originalFilename: filename, //原始文件名\n path: filepath, //文件临时存放的路径\n size: size //文件大小\n}\n</code></pre>\n<h4 id=\"http-header-name-value-\">http.header(name, value)</h4>\n<ul>\n<li><code>name</code> {String} header 名称</li>\n<li><code>value</code> {String} header 值</li>\n</ul>\n<p>获取或者设置 header 信息。</p>\n<pre><code class=\"lang-js\">http.header(\'accept\'); //获取accept\nhttp.header(\'X-NAME\', \'thinkjs\'); //设置header\n</code></pre>\n<h4 id=\"http-expires-time-\">http.expires(time)</h4>\n<ul>\n<li><code>time</code> {Number} 过期时间,单位为秒</li>\n</ul>\n<p>强缓存,设置 <code>Cache-Control</code> 和 <code>Expires</code> 头信息。</p>\n<pre><code class=\"lang-js\">http.header(86400); //设置过期时间为 1 天。\n</code></pre>\n<h4 id=\"http-status-status-\">http.status(status)</h4>\n<p>设置状态码。如果头信息已经发送,则无法设置状态码。</p>\n<pre><code class=\"lang-js\">http.status(400); //设置状态码为400\n</code></pre>\n<h4 id=\"http-ip-\">http.ip()</h4>\n<p>获取用户的 ip 。如果使用了代理,获取的值可能不准。</p>\n<h4 id=\"http-lang-lang-asviewpath-\">http.lang(lang, asViewPath)</h4>\n<ul>\n<li><code>lang</code> {String} 要设置的语言</li>\n<li><code>asViewPath</code> {Boolean} 是否添加一层模版语言目录</li>\n</ul>\n<p>获取或者设置国际化的语言,可以支持模版路径要多一层语言的目录。</p>\n<h5 id=\"-\">获取语言</h5>\n<pre><code class=\"lang-js\">let lang = http.lang();\n</code></pre>\n<p>获取语言的循序为 <code>http._lang</code> -> <code>从 cookie 中获取</code> -> <code>从 header 中获取</code>,如果需要从 url 中解析语言,可以获取后通过 <code>http.lang(lang)</code> 方法设置到属性 <code>http._lang</code> 中。</p>\n<h5 id=\"-\">设置语言</h5>\n<pre><code class=\"lang-js\">let lang = getFromUrl();\nhttp.lang(lang, true); //设置语言,并指定模版路径中添加一层语言目录\n</code></pre>\n<h4 id=\"http-theme-theme-\">http.theme(theme)</h4>\n<p>获取或者设置主题,设置后模版路径要多一层主题的目录。</p>\n<h4 id=\"http-cookie-name-value-\">http.cookie(name, value)</h4>\n<ul>\n<li><code>name</code> {String} cookie 名称</li>\n<li><code>value</code> {String} cookie 值</li>\n</ul>\n<p>读取或者设置 cookie 值。</p>\n<pre><code class=\"lang-js\">http.cookie(\'think_test\'); //获取名为 think_test 的 cookie\nhttp.cookie(\'name\', \'value\'); //设置 cookie,如果头信息已经发送则设置无效\nhttp.cookie(\'name\', null); //删除 cookie\n</code></pre>\n<h4 id=\"http-session-name-value-\">http.session(name, value)</h4>\n<ul>\n<li><code>name</code> {String} session 名</li>\n<li><code>value</code> {Mixed} session 值</li>\n<li><code>return</code> {Promise}</li>\n</ul>\n<p>读取、设置和清除 session。</p>\n<h5 id=\"-session\">读取 Session</h5>\n<pre><code class=\"lang-js\">let value = yield http.session(\'userInfo\');\n</code></pre>\n<h5 id=\"-session\">设置 Session</h5>\n<pre><code class=\"lang-js\">yield http.session(\'userInfo\', data);\n</code></pre>\n<h5 id=\"-session\">清除 Session</h5>\n<pre><code class=\"lang-js\">yield http.session();\n</code></pre>\n<h4 id=\"http-redirect-url-status-\">http.redirect(url, status)</h4>\n<ul>\n<li><code>url</code> {String} 要跳转的 url</li>\n<li><code>status</code> {Number} 状态码, 301 或者 302,默认为302</li>\n</ul>\n<p>页面跳转。</p>\n<pre><code class=\"lang-js\">http.redirect(\'/login\'); //跳转到登录页面\n</code></pre>\n<h4 id=\"http-type-contenttype-encoding-\">http.type(contentType, encoding)</h4>\n<ul>\n<li><code>contentType</code> {String} 要设置的 contentType</li>\n<li><code>encoding</code> {String} 要设置的编码</li>\n</ul>\n<p>获取或者设置 Content-Type。</p>\n<pre><code class=\"lang-js\">http.type(); //获取Content-Type\nhttp.type(\'text/html\'); //设置Content-Type,会自动加上charset\nhttp.type(\'audio/mpeg\', false); //设置Content-Type,不追加charset\n</code></pre>\n<h4 id=\"http-write-content-encoding-\">http.write(content, encoding)</h4>\n<ul>\n<li><code>content</code> {Mixed} 要输出的内容</li>\n<li><code>encoding</code> {String} 编码</li>\n</ul>\n<p>输出内容,要调用 http.end 才能结束当前请求。</p>\n<h4 id=\"http-end-content-encoding-\">http.end(content, encoding)</h4>\n<ul>\n<li><code>content</code> {Mixed} 要输出的内容</li>\n<li><code>encoding</code> {String} 编码</li>\n</ul>\n<p>输出内容并结束当前请求。</p>\n<h4 id=\"http-success-data-message-\">http.success(data, message)</h4>\n<ul>\n<li><code>data</code> {Mixed} 要输出的数据</li>\n<li><code>message</code> {String} 追加的message</li>\n</ul>\n<p>格式化输出一个正常的数据,一般是操作成功后输出。</p>\n<pre><code class=\"lang-js\">http.success({name: \'thinkjs\'});\n//writes\n{\n errno: 0,\n errmsg: \'\',\n data: {\n name: \'thinkjs\'\n }\n}\n</code></pre>\n<p>这样客户端就可以根据 <code>errno</code> 是否为 <code>0</code> 为判断当前请求是否正常。</p>\n<h4 id=\"http-fail-errno-errmsg-data-\">http.fail(errno, errmsg, data)</h4>\n<ul>\n<li><code>errno</code> {Number} 错误号</li>\n<li><code>errmsg</code> {String} 错误信息</li>\n<li><code>data</code> {Mixed} 额外的数据</li>\n</ul>\n<p>格式化输出一个异常的数据,一般是操作失败后输出。</p>\n<p><code>注</code>:字段名 <code>errno</code> 和 <code>errmsg</code> 可以在配置里进行修改。</p>\n<pre><code class=\"lang-js\">http.fail(100, \'fail\')\n//writes\n{\n errno: 100,\n errmsg: \'fail\',\n data: \'\'\n}\n</code></pre>\n<p>这样客户端就可以拿到具体的错误号和错误信息,然后根据需要显示了。</p>\n<p><code>注</code>:字段名 <code>errno</code> 和 <code>errmsg</code> 可以在配置里进行修改。</p>\n<h4 id=\"http-json-data-\">http.json(data)</h4>\n<ul>\n<li><code>data</code> {Object}</li>\n</ul>\n<p>json 方式输出数据,会设置 Content-Type 为 <code>application/json</code>,该值对应的配置为<code>json_content_type</code>。</p><p>文章来源:<a href=\"http://www.thinkjs.org/\">http://www.thinkjs.org</a></p><p><br></p>', '', '', '2016-07-17 14:34:12', '50', '0', '0', '0', '18', 'thinkjs,nodejs', '1', '1', '', '8', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('295', 'controller', '', '<p><code>think.controller.base</code> 继承自 <a href=\"./api_think_http_base.html\">think.http.base</a> 类。项目里的控制器需要继承该类。</p>\n<h5 id=\"-es6-\">使用 ES6 的语法继承该类</h5>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n indexAction(){\n\n }\n}\n</code></pre>\n<h5 id=\"-\">使用普通方式继承该类</h5>\n<pre><code class=\"lang-js\">module.exports = think.controller({\n indexAction(){\n\n }\n})\n</code></pre>\n<h3 id=\"-\">属性</h3>\n<h4 id=\"controller-http\">controller.http</h4>\n<p>传递进来的 <a href=\"./api_http.html\">http</a> 对象。</p>\n<h3 id=\"-\">方法</h3>\n<h4 id=\"controller-ip-\">controller.ip()</h4>\n<ul>\n<li><code>return</code> {String}</li>\n</ul>\n<p>获取当前请求用户的 ip,等同与 http.ip 方法。</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n indexAction(){\n let ip = this.ip();\n }\n}\n</code></pre>\n<h4 id=\"controller-method-\">controller.method()</h4>\n<ul>\n<li><code>return</code> {String}</li>\n</ul>\n<p>获取当前请求的类型,转化为小写。</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n indexAction(){\n let method = this.method(); //get or post ...\n }\n}\n</code></pre>\n<h4 id=\"controller-ismethod-method-\">controller.isMethod(method)</h4>\n<ul>\n<li><code>method</code> {String} 类型</li>\n<li><code>return</code> {Boolean}</li>\n</ul>\n<p>判断当前的请求类型是否是指定的类型。</p>\n<h4 id=\"controller-isget-\">controller.isGet()</h4>\n<ul>\n<li><code>return</code> {Boolean}</li>\n</ul>\n<p>判断是否是 GET 请求。</p>\n<h4 id=\"controller-ispost-\">controller.isPost()</h4>\n<ul>\n<li><code>return</code> {Boolean}</li>\n</ul>\n<p>判断是否是 POST 请求。</p>\n<h4 id=\"controller-isajax-method-\">controller.isAjax(method)</h4>\n<ul>\n<li><code>method</code> {String}</li>\n<li><code>return</code> {Boolean}</li>\n</ul>\n<p>判断是否是 Ajax 请求。如果指定了 method,那么请求类型也要相同。</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n indexAction(){\n //是ajax 且请求类型是 POST\n let isAjax = this.isAjax(\'post\');\n }\n}\n</code></pre>\n<h4 id=\"controller-iswebsocket-\">controller.isWebSocket()</h4>\n<ul>\n<li><code>return</code> {Boolean}</li>\n</ul>\n<p>是否是 websocket 请求。</p>\n<h4 id=\"controller-iscli-\">controller.isCli()</h4>\n<ul>\n<li><code>return</code> {Boolean}</li>\n</ul>\n<p>是否是命令行下调用。</p>\n<h4 id=\"controller-isjsonp-callback-\">controller.isJsonp(callback)</h4>\n<ul>\n<li><code>callback</code> {String} callback 名称</li>\n<li><code>return</code> {Boolean}</li>\n</ul>\n<p>是否是 jsonp 请求。</p>\n<h4 id=\"controller-get-name-\">controller.get(name)</h4>\n<ul>\n<li><code>name</code> {String} 参数名</li>\n</ul>\n<p>获取 GET 参数值。</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n indexAction(){\n //获取一个参数值\n let value = this.get(\'xxx\');\n //获取所有的参数值\n let values = this.get();\n }\n}\n</code></pre>\n<h4 id=\"controller-post-name-\">controller.post(name)</h4>\n<ul>\n<li><code>name</code> {String} 参数名</li>\n</ul>\n<p>获取 POST 提交的参数。</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n indexAction(){\n //获取一个参数值\n let value = this.post(\'xxx\');\n //获取所有的 POST 参数值\n let values = this.post();\n }\n}\n</code></pre>\n<h4 id=\"controller-param-name-\">controller.param(name)</h4>\n<ul>\n<li><code>name</code> {String} 参数名</li>\n</ul>\n<p>获取参数值,优先从 POST 里获取,如果取不到再从 GET 里获取。</p>\n<h4 id=\"controller-file-name-\">controller.file(name)</h4>\n<ul>\n<li><code>name</code> {String} 上传文件对应的字段名</li>\n</ul>\n<p>获取上传的文件,返回值是个对象,包含下面的属性:</p>\n<pre><code class=\"lang-js\">{\n fieldName: \'file\', //表单字段名称\n originalFilename: filename, //原始的文件名\n path: filepath, //文件保存的临时路径,使用时需要将其移动到项目里的目录,否则请求结束时会被删除\n size: 1000 //文件大小\n}\n</code></pre>\n<p>如果文件不存在,那么值为一个空对象 <code>{}</code>。</p>\n<h4 id=\"controller-header-name-value-\">controller.header(name, value)</h4>\n<ul>\n<li><code>name</code> {String} header 名</li>\n<li><code>value</code> {String} header 值</li>\n</ul>\n<p>获取或者设置 header。</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n indexAction(){\n let accept = this.header(\'accept\'); //获取 header\n this.header(\'X-NAME\', \'thinks\'); //设置 header\n }\n}\n</code></pre>\n<h4 id=\"controller-expires-time-\">controller.expires(time)</h4>\n<ul>\n<li><code>time</code> {Number} 过期时间,单位为秒</li>\n</ul>\n<p>强缓存,设置 <code>Cache-Control</code> 和 <code>Expires</code> 头信息。</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n indexAction(){\n this.expires(86400); //设置过期时间为 1 天。\n }\n}\n</code></pre>\n<h4 id=\"controller-useragent-\">controller.userAgent()</h4>\n<p>获取 userAgent。</p>\n<h4 id=\"controller-referrer-onlyhost-\">controller.referrer(onlyHost)</h4>\n<ul>\n<li><code>referrer</code> {Boolean} 是否只需要 host</li>\n</ul>\n<p>获取 referrer。</p>\n<h4 id=\"controller-cookie-name-value-options-\">controller.cookie(name, value, options)</h4>\n<ul>\n<li><code>name</code> {String} cookie 名</li>\n<li><code>value</code> {String} cookie 值</li>\n<li><code>options</code> {Object}</li>\n</ul>\n<p>获取、设置或者删除 cookie。</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n indexAction(){\n //获取 cookie 值\n let value = this.cookie(\'think_name\');\n }\n}\n</code></pre>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n indexAction(){\n //设置 cookie 值\n this.cookie(\'think_name\', value, {\n timeout: 3600 * 24 * 7 //有效期为一周\n });\n }\n}\n</code></pre>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n indexAction(){\n //删除 cookie\n this.cookie(\'think_name\', null); \n }\n}\n</code></pre>\n<h4 id=\"controller-session-name-value-\">controller.session(name, value)</h4>\n<ul>\n<li><code>name</code> {String} session 名</li>\n<li><code>value</code> {Mixed} session 值</li>\n<li><code>return</code> {Promise}</li>\n</ul>\n<p>读取、设置和清除 session。</p>\n<h5 id=\"-session\">读取 Session</h5>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n * indexAction(){\n //获取session\n let value = yield this.session(\'userInfo\');\n }\n}\n</code></pre>\n<h5 id=\"-session\">设置 Session</h5>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n * indexAction(){\n //设置 session\n yield this.session(\'userInfo\', data);\n }\n}\n</code></pre>\n<h5 id=\"-session\">清除 Session</h5>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n * indexAction(){\n //清除当前用户的 session\n yield this.session();\n }\n}\n</code></pre>\n<h4 id=\"controller-lang-lang-asviewpath-\">controller.lang(lang, asViewPath)</h4>\n<ul>\n<li><code>lang</code> {String} 要设置的语言</li>\n<li><code>asViewPath</code> {Boolean} 是否在模版目录添加一层语言目录</li>\n</ul>\n<p>读取或者设置语言。</p>\n<h4 id=\"controller-locale-key-\">controller.locale(key)</h4>\n<ul>\n<li><code>key</code> {String} </li>\n</ul>\n<p>根据 language 获取对应的语言文本。</p>\n<h4 id=\"controller-redirect-url-statuscode-\">controller.redirect(url, statusCode)</h4>\n<ul>\n<li><code>url</code> {String} 要跳转的 url</li>\n<li><code>statusCode</code> {Number} 状态码,默认为 302</li>\n</ul>\n<p>页面跳转。</p>\n<h4 id=\"controller-assign-name-value-\">controller.assign(name, value)</h4>\n<ul>\n<li><code>name</code> {String | Object} 变量名</li>\n<li><code>value</code> {Mixed} 变量值</li>\n</ul>\n<p>将变量赋值到模版中。</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n indexAction(){\n //单个赋值\n this.assign(\'title\', \'thinkjs\');\n //批量赋值\n this.assign({\n name: \'xxx\',\n desc: \'yyy\'\n })\n }\n}\n</code></pre>\n<h4 id=\"controller-fetch-templatefile-\">controller.fetch(templateFile)</h4>\n<ul>\n<li><code>templateFile</code> {String} 模版文件地址</li>\n<li><code>return</code> {Promise}</li>\n</ul>\n<p>获取解析后的模版内容。</p>\n<h5 id=\"-\">直接获取</h5>\n<pre><code class=\"lang-js\">// 假设文件路径为 /foo/bar/app/home/controller/index.js\nexport default class extends think.controller.base {\n * indexAction(){\n // home/index_index.html\n let content = yield this.fetch();\n }\n}\n</code></pre>\n<h5 id=\"-action\">改变 action</h5>\n<pre><code class=\"lang-js\">// 假设文件路径为 /foo/bar/app/home/controller/index.js\nexport default class extends think.controller.base {\n * indexAction(){\n // home/index_detail.html\n let content = yield this.fetch(\'detail\');\n }\n}\n</code></pre>\n<h5 id=\"-controller-action\">改变 controller 和 action</h5>\n<pre><code class=\"lang-js\">// 假设文件路径为 /foo/bar/app/home/controller/index.js\nexport default class extends think.controller.base {\n * indexAction(){\n // home/user_detail.html\n let content = yield this.fetch(\'user/detail\');\n }\n}\n</code></pre>\n<h5 id=\"-module-controller-action\">改变 module, controller 和 action</h5>\n<pre><code class=\"lang-js\">// 假设文件路径为 /foo/bar/app/home/controller/index.js\nexport default class extends think.controller.base {\n * indexAction(){\n // admin/user_detail.html\n let content = yield this.fetch(\'admin/user/detail\');\n }\n}\n</code></pre>\n<h5 id=\"-\">改变文件后缀名</h5>\n<pre><code class=\"lang-js\">// 假设文件路径为 /foo/bar/app/home/controller/index.js\nexport default class extends think.controller.base {\n * indexAction(){\n // home/index_detail.xml\n let content = yield this.fetch(\'detail.xml\');\n }\n}\n</code></pre>\n<h5 id=\"-\">获取绝对路径文件</h5>\n<pre><code class=\"lang-js\">// 假设文件路径为 /foo/bar/app/home/controller/index.js\nexport default class extends think.controller.base {\n * indexAction(){\n // /home/xxx/aaa/bbb/c.html\n let content = yield this.fetch(\'/home/xxx/aaa/bbb/c.html\');\n }\n}\n</code></pre>\n<h4 id=\"controller-display-templatefile-\">controller.display(templateFile)</h4>\n<ul>\n<li><code>templateFile</code> {String} 模版文件路径</li>\n</ul>\n<p>输出模版内容到浏览器端。查找模版文件策略和 <code>controller.fetch</code> 相同。</p>\n<h4 id=\"controller-jsonp-data-\">controller.jsonp(data)</h4>\n<ul>\n<li><code>data</code> {Mixed} 要输出的内容</li>\n</ul>\n<p>jsonp 的方法输出内容,获取 callback 名称安全过滤后输出。</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n indexAction(){\n this.jsonp({name: \'thinkjs\'});\n //writes\n \'callback_fn_name({name: \"thinkjs\"})\'\n }\n}\n</code></pre>\n<h4 id=\"controller-json-data-\">controller.json(data)</h4>\n<ul>\n<li><code>data</code> {Mixed} 要输出的内容</li>\n</ul>\n<p>json 的方式输出内容。</p>\n<h4 id=\"controller-status-status-\">controller.status(status)</h4>\n<ul>\n<li><code>status</code> {Number} 状态码,默认为 404</li>\n</ul>\n<p>设置状态码。</p>\n<h4 id=\"controller-deny-status-\">controller.deny(status)</h4>\n<ul>\n<li><code>status</code> {String} 状态码,默认为 403</li>\n</ul>\n<p>拒绝当前请求。</p>\n<h4 id=\"controller-write-data-encoding-\">controller.write(data, encoding)</h4>\n<ul>\n<li><code>data</code> {mixed} 要输出的内容</li>\n<li><code>encoding</code> {String} 编码</li>\n</ul>\n<p>输出内容</p>\n<h4 id=\"controller-end-data-encoding-\">controller.end(data, encoding)</h4>\n<ul>\n<li><code>data</code> {mixed} 要输出的内容</li>\n<li><code>encoding</code> {String} 编码</li>\n</ul>\n<p>输出内容后结束当前请求。</p>\n<h4 id=\"controller-type-type-charset-\">controller.type(type, charset)</h4>\n<ul>\n<li><code>type</code> {String} Content-Type</li>\n<li><code>charset</code> {Boolean} 是否自动追加 charset</li>\n</ul>\n<p>设置 Content-Type。</p>\n<h4 id=\"controller-download-filepath-contenttype-filename-\">controller.download(filePath, contentType, fileName)</h4>\n<ul>\n<li><code>filePath</code> {String} 下载文件的具体路径</li>\n<li><code>content-Type</code> {String} Content-Type</li>\n<li><code>fileName</code> {String} 保存的文件名</li>\n</ul>\n<p>下载文件。</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n indexAction(){\n let filePath = think.RESOUCE_PATH + \'/a.txt\';\n //自动识别 Content-Type,保存的文件名为 a.txt\n this.download(filePath);\n }\n}\n</code></pre>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n indexAction(){\n let filePath = think.RESOUCE_PATH + \'/a.log\';\n //自动识别 Content-Type,保存的文件名为 b.txt\n this.download(filePath, \'b.txt\');\n }\n}\n</code></pre>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n indexAction(){\n let filePath = think.RESOUCE_PATH + \'/a.log\';\n //指定 Content-Type 为 text/html,保存的文件名为 b.txt\n this.download(filePath, \'text/html\', \'b.txt\');\n }\n}\n</code></pre>\n<h4 id=\"controller-success-data-message-\">controller.success(data, message)</h4>\n<ul>\n<li><code>data</code> {Mixed} 要输出的数据</li>\n<li><code>message</code> {String} 追加的message</li>\n</ul>\n<p>格式化输出一个正常的数据,一般是操作成功后输出。</p>\n<pre><code class=\"lang-js\">http.success({name: \'thinkjs\'});\n//writes\n{\n errno: 0,\n errmsg: \'\',\n data: {\n name: \'thinkjs\'\n }\n}\n</code></pre>\n<p>这样客户端就可以根据 <code>errno</code> 是否为 <code>0</code> 为判断当前请求是否正常。</p>\n<h4 id=\"controller-fail-errno-errmsg-data-\">controller.fail(errno, errmsg, data)</h4>\n<ul>\n<li><code>errno</code> {Number} 错误号</li>\n<li><code>errmsg</code> {String} 错误信息</li>\n<li><code>data</code> {Mixed} 额外的数据</li>\n</ul>\n<p>格式化输出一个异常的数据,一般是操作失败后输出。</p>\n<p><code>注</code>:字段名 <code>errno</code> 和 <code>errmsg</code> 可以在配置里进行修改。</p>\n<pre><code class=\"lang-js\">http.fail(100, \'fail\')\n//writes\n{\n errno: 100,\n errmsg: \'fail\',\n data: \'\'\n}\n</code></pre>\n<p>这样客户端就可以拿到具体的错误号和错误信息,然后根据需要显示了。</p>\n<p><code>注</code>:字段名 <code>errno</code> 和 <code>errmsg</code> 可以在配置里进行修改。</p>\n<h4 id=\"controller-sendtime-name-\">controller.sendTime(name)</h4>\n<ul>\n<li><code>name</code> {String} header key</li>\n</ul>\n<p>发送请求的执行时间,使用 header 的方式发出。</p><p>文章来源:<a href=\"http://www.thinkjs.org/\">http://www.thinkjs.org</a></p>\n<p><br></p>', '', '', '2016-07-17 14:35:13', '51', '0', '0', '0', '18', 'thinkjs,nodejs', '1', '1', '', '8', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('296', 'rest controller', '', '<p><code>think.controller.rest</code> 继承自 <a href=\"./api_controller.html\">think.controller.base</a>,用来处理 Rest 接口。</p>\n<h5 id=\"-es6-\">使用 ES6 的语法继承该类</h5>\n<pre><code class=\"lang-js\">export default class extends think.controller.rest {\n\n}\n</code></pre>\n<h5 id=\"-\">使用普通方式继承该类</h5>\n<pre><code class=\"lang-js\">module.exports = think.controller(\'rest\', {\n\n})\n</code></pre>\n<h3 id=\"-\">属性</h3>\n<h4 id=\"controller-_isrest\">controller._isRest</h4>\n<p>标识此 controller 对应的是 Rest 接口。如果在 <code>init</code> 方法里将该属性设置为 <code>false</code>,那么该 controller 不再是一个 Rest 接口。</p>\n<h4 id=\"controller-_method\">controller._method</h4>\n<p>获取 method 方式。默认从 http method 中获取,但有些客户端不支持发送 DELETE, PUT 类型的请求,所以可以设置为从 GET 参数里获取。</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.rest {\n init(http){\n super.init(http);\n //设置 _method,表示从 GET 参数获取 _method 字段的值\n //如果没有取到,则从 http method 中获取\n this._method = \'_method\';\n }\n}\n</code></pre>\n<h4 id=\"controller-resource\">controller.resource</h4>\n<p>当前 Rest 对应的 Resource 名称。</p>\n<h4 id=\"controller-id\">controller.id</h4>\n<p>资源 ID</p>\n<h4 id=\"controller-modelinstance\">controller.modelInstance</h4>\n<p>资源对应 model 的实例。</p>\n<h3 id=\"-\">方法</h3>\n<h4 id=\"controller-__before-\">controller.__before()</h4>\n<p>可以在魔术方法 <code>__before</code> 中进行字段过滤、分页、权限校验等功能。</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.rest{\n __before(){\n //过滤 password 字段\n this.modelInstance.field(\'password\', true);\n }\n}\n</code></pre>\n<h4 id=\"controller-getaction-\">controller.getAction()</h4>\n<p>获取资源数据,如果有 id,拉取一条,否则拉取列表。</p>\n<pre><code class=\"lang-js\">//方法实现,可以根据需要修改\nexport default class extends think.controller.rest {\n * getAction(){\n let data;\n if (this.id) {\n let pk = yield this.modelInstance.getPk();\n data = yield this.modelInstance.where({[pk]: this.id}).find();\n return this.success(data);\n }\n data = yield this.modelInstance.select();\n return this.success(data);\n }\n}\n</code></pre>\n<h4 id=\"controller-postaction-\">controller.postAction()</h4>\n<p>添加数据</p>\n<pre><code class=\"lang-js\">//方法实现,可以根据需要修改\nexport default class extends think.controller.rest {\n * postAction(){\n let pk = yield this.modelInstance.getPk();\n let data = this.post();\n delete data[pk];\n if(think.isEmpty(data)){\n return this.fail(\'data is empty\');\n }\n let insertId = yield this.modelInstance.add(data);\n return this.success({id: insertId});\n }\n}\n</code></pre>\n<h4 id=\"controller-deleteaction-\">controller.deleteAction()</h4>\n<p>删除数据</p>\n<pre><code class=\"lang-js\">//方法实现,可以根据需要修改\nexport default class extends think.controller.rest {\n * deleteAction(){\n if (!this.id) {\n return this.fail(\'params error\');\n }\n let pk = yield this.modelInstance.getPk();\n let rows = yield this.modelInstance.where({[pk]: this.id}).delete();\n return this.success({affectedRows: rows});\n }\n}\n</code></pre>\n<h4 id=\"controller-putaction-\">controller.putAction()</h4>\n<p>更新数据</p>\n<pre><code class=\"lang-js\">//方法实现,可以根据需要修改\nexport default class extends think.controller.rest {\n * putAction(){\n if (!this.id) {\n return this.fail(\'params error\');\n }\n let pk = yield this.modelInstance.getPk();\n let data = this.post();\n delete data[pk];\n if (think.isEmpty(data)) {\n return this.fail(\'data is empty\');\n }\n let rows = yield this.modelInstance.where({[pk]: this.id}).update(data);\n return this.success({affectedRows: rows});\n }\n}\n</code></pre>\n<h4 id=\"controller-__call-\">controller.__call()</h4>\n<p>找不到方法时调用</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.rest {\n __call(){\n return this.fail(think.locale(\'ACTION_INVALID\', this.http.action, this.http.url));\n }\n}\n</code></pre><p>\n文章来源:<a href=\"http://www.thinkjs.org\" target=\"_blank\">http://www.thinkjs.org</a></p><p><br></p>', '', '', '2016-07-17 14:36:07', '71', '0', '0', '0', '18', 'thinkjs,nodejs', '1', '1', '', '8', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('297', 'model', '', '<p><code>think.model.base</code> 继承自 <a href=\"./api_think_base.html\">think.base</a> 类。</p>\n<h5 id=\"-es6-\">使用 ES6 的语法继承该类</h5>\n<pre><code class=\"lang-js\">export default class extends think.model.base {\n getList(){\n\n }\n}\n</code></pre>\n<h5 id=\"-\">使用普通方式继承该类</h5>\n<pre><code class=\"lang-js\">module.exports = think.model({\n getList: function(){\n\n }\n})\n</code></pre>\n<h3 id=\"-\">属性</h3>\n<h4 id=\"model-pk\">model.pk</h4>\n<p>数据表主键,默认为 <code>id</code>。</p>\n<h4 id=\"model-name\">model.name</h4>\n<p>模型名,默认从当前文件名中解析。</p>\n<p>当前文件路径为 for/bar/app/home/model/user.js,那么解析的模型名为 <code>user</code>。</p>\n<h4 id=\"model-tableprefix\">model.tablePrefix</h4>\n<p>数据表名称前缀,默认为 <code>think_</code>。</p>\n<h4 id=\"model-tablename\">model.tableName</h4>\n<p>数据表名称,不包含前缀。默认等于模型名。</p>\n<h4 id=\"model-schema\">model.schema</h4>\n<p>数据表字段,关系型数据库默认自动从数据表分析。</p>\n<h4 id=\"model-indexes\">model.indexes</h4>\n<p>数据表索引,关系数据库会自动从数据表分析。</p>\n<h4 id=\"model-config\">model.config</h4>\n<p>配置,实例化的时候指定。</p>\n<h4 id=\"model-_db\">model._db</h4>\n<p>连接数据库句柄。</p>\n<h4 id=\"model-_data\">model._data</h4>\n<p>操作的数据。</p>\n<h4 id=\"model-_options\">model._options</h4>\n<p>操作选项。</p>\n<h3 id=\"-\">方法</h3>\n<h4 id=\"model-model-name-options-module-\">model.model(name, options, module)</h4>\n<ul>\n<li><code>name</code> {String} 模型名称</li>\n<li><code>options</code> {Object} 配置项</li>\n<li><code>module</code> {String} 模块名</li>\n<li><code>return</code> {Object}</li>\n</ul>\n<p>获取模型实例,可以跨模块获取。</p>\n<pre><code class=\"lang-js\">export default class extends think.model.base {\n * getList(){\n //获取 user 模型实例\n let instance = this.model(\'user\');\n let list = yield instance.select();\n let ids = list.map(item => {\n return item.id;\n });\n let data = yield this.where({id: [\'IN\', ids]}).select();\n return data;\n }\n}\n</code></pre>\n<h4 id=\"model-gettableprefix-\">model.getTablePrefix()</h4>\n<ul>\n<li><code>return</code> {string}</li>\n</ul>\n<p>获取表名前缀。</p>\n<h4 id=\"model-getconfigkey-\">model.getConfigKey()</h4>\n<ul>\n<li><code>return</code> {String}</li>\n</ul>\n<p>获取配置对应的 key,缓存 db 句柄时使用。</p>\n<h4 id=\"model-db-\">model.db()</h4>\n<ul>\n<li><code>return</code> {Object}</li>\n</ul>\n<p>根据当前的配置获取 db 实例,如果已经存在则直接返回。</p>\n<h4 id=\"model-getmodelname-\">model.getModelName()</h4>\n<ul>\n<li><code>return</code> {String} 模型名称</li>\n</ul>\n<p>如果已经配置则直接返回,否则解析当前的文件名。</p>\n<h4 id=\"model-gettablename-\">model.getTableName()</h4>\n<ul>\n<li><code>return</code> {String} 获取表名,包含表前缀</li>\n</ul>\n<p>获取表名,包含表前缀。</p>\n<h4 id=\"model-cache-key-timeout-\">model.cache(key, timeout)</h4>\n<ul>\n<li><code>key</code> {String} 缓存 key</li>\n<li><code>timeout</code> {Number} 缓存有效时间,单位为秒</li>\n<li><code>return</code> {this}</li>\n</ul>\n<p>设置缓存选项。</p>\n<h5 id=\"-key-\">设置缓存 key 和时间</h5>\n<pre><code class=\"lang-js\">export default class extends think.model.base {\n getList(){\n return this.cache(\'getList\', 1000).where({id: {\'>\': 100}}).select();\n }\n}\n</code></pre>\n<h5 id=\"-key-\">只设置缓存时间,缓存 key 自动生成</h5>\n<pre><code class=\"lang-js\">export default class extends think.model.base {\n getList(){\n return this.cache(1000).where({id: {\'>\': 100}}).select();\n }\n}\n</code></pre>\n<h5 id=\"-\">设置更多的缓存信息</h5>\n<pre><code class=\"lang-js\">export default class extends think.model.base {\n getList(){\n return this.cache({\n key: \'getList\',\n timeout: 1000,\n type: \'file\' //使用文件方式缓存\n }).where({id: {\'>\': 100}}).select();\n }\n}\n</code></pre>\n<h4 id=\"model-limit-offset-length-\">model.limit(offset, length)</h4>\n<ul>\n<li><code>offset</code> {Number} 设置查询的起始位置 </li>\n<li><code>length</code> {Number} 设置查询的数据长度</li>\n<li><code>return</code> {this}</li>\n</ul>\n<p>设置查询结果的限制条件。</p>\n<h5 id=\"-\">限制数据长度</h5>\n<pre><code class=\"lang-js\">export default class extends think.model.base {\n getList(){\n //查询20条数据\n return this.limit(20).where({id: {\'>\': 100}}).select();\n }\n}\n</code></pre>\n<h5 id=\"-\">限制数据起始位置和长度</h5>\n<pre><code class=\"lang-js\">export default class extends think.model.base {\n getList(){\n //从起始位置100开始查询20调数据\n return this.limit(100, 20).where({id: {\'>\': 100}}).select();\n }\n}\n</code></pre>\n<h4 id=\"model-page-page-listrows-\">model.page(page, listRows)</h4>\n<ul>\n<li><code>page</code> {Number} 当前页,从 1 开始</li>\n<li><code>listRows</code> {Number} 每页的条数</li>\n<li><code>return</code> {this}</li>\n</ul>\n<p>设置查询分页数据,自动转化为 <code>limit</code> 数据。</p>\n<pre><code class=\"lang-js\">export default class extends think.model.base {\n getList(){\n //查询第 2 页数据,每页 10 条数据\n return this.page(2, 10).where({id: {\'>\': 100}}).select();\n }\n}\n</code></pre>\n<h4 id=\"model-where-where-\">model.where(where)</h4>\n<ul>\n<li><code>where</code> {String | Object} where 条件</li>\n<li><code>return</code> {this}</li>\n</ul>\n<p>设置 where 查询条件。可以通过属性 <code>_logic</code> 设置逻辑,默认为 <code>AND</code>。可以通过属性 <code>_complex</code> 设置复合查询。</p>\n<p><code>注</code>:1、以下示例不适合 mongo model,mongo 中设置 where 条件请见 model.mongo 里的 where 条件设定。2、where 条件中的值需要在 Logic 里做数据校验,否则可能会有漏洞。</p>\n<h5 id=\"-\">普通条件</h5>\n<pre><code class=\"lang-js\">export default class extends think.model.base {\n where1(){\n //SELECT * FROM `think_user`\n return this.where().select();\n }\n where2(){\n //SELECT * FROM `think_user` WHERE ( `id` = 10 )\n return this.where({id: 10}).select();\n }\n where3(){\n //SELECT * FROM `think_user` WHERE ( id = 10 OR id < 2 )\n return this.where(\'id = 10 OR id < 2\').select();\n }\n where4(){\n //SELECT * FROM `think_user` WHERE ( `id` != 10 )\n return this.where({id: [\'!=\', 10]}).select();\n }\n}\n</code></pre>\n<h5 id=\"null-\">null 条件</h5>\n<pre><code class=\"lang-js\">export default class extends think.model.base {\n where1(){\n //SELECT * FROM `think_user` where ( title IS NULL );\n return this.where({title: null}).select();\n }\n where2(){\n //SELECT * FROM `think_user` where ( title IS NOT NULL );\n return this.where({title: [\'!=\', null]}).select();\n }\n}\n</code></pre>\n<h5 id=\"exp-\">EXP 条件</h5>\n<p>ThinkJS 默认会对字段和值进行转义,防止安全漏洞。有时候一些特殊的情况不希望被转义,可以使用 EXP 的方式,如:</p>\n<pre><code class=\"lang-js\">export default class extends think.model.base {\n where1(){\n //SELECT * FROM `think_user` WHERE ( (`name` =\'name\') )\n return this.where({name: [\'EXP\', \"=\\\"name\\\"\"]}).select();\n }\n}\n</code></pre>\n<h5 id=\"like-\">LIKE 条件</h5>\n<pre><code class=\"lang-js\">export default class extends think.model.base {\n where1(){\n //SELECT * FROM `think_user` WHERE ( `title` NOT LIKE \'welefen\' )\n return this.where({title: [\'NOTLIKE\', \'welefen\']}).select();\n }\n where2(){\n //SELECT * FROM `think_user` WHERE ( `title` LIKE \'%welefen%\' )\n return this.where({title: [\'like\', \'%welefen%\']}).select();\n }\n //like 多个值\n where3(){\n //SELECT * FROM `think_user` WHERE ( (`title` LIKE \'welefen\' OR `title` LIKE \'suredy\') )\n return this.where({title: [\'like\', [\'welefen\', \'suredy\']]}).select();\n }\n //多个字段或的关系 like 一个值\n where4(){\n //SELECT * FROM `think_user` WHERE ( (`title` LIKE \'%welefen%\') OR (`content` LIKE \'%welefen%\') )\n return this.where({\'title|content\': [\'like\', \'%welefen%\']}).select();\n }\n //多个字段与的关系 Like 一个值\n where5(){\n //SELECT * FROM `think_user` WHERE ( (`title` LIKE \'%welefen%\') AND (`content` LIKE \'%welefen%\') )\n return this.where({\'title&content\': [\'like\', \'%welefen%\']}).select();\n }\n}\n</code></pre>\n<h5 id=\"in-\">IN 条件</h5>\n<pre><code class=\"lang-js\">export default class extens think.model.base {\n where1(){\n //SELECT * FROM `think_user` WHERE ( `id` IN (\'10\',\'20\') )\n return this.where({id: [\'IN\', \'10,20\']}).select();\n }\n where2(){\n //SELECT * FROM `think_user` WHERE ( `id` IN (10,20) )\n return this.where({id: [\'IN\', [10, 20]]}).select();\n }\n where3(){\n //SELECT * FROM `think_user` WHERE ( `id` NOT IN (10,20) )\n return this.where({id: [\'NOTIN\', [10, 20]]}).select();\n }\n}\n</code></pre>\n<h5 id=\"between-\">BETWEEN 查询</h5>\n<pre><code class=\"lang-js\">export default class extens think.model.base {\n where1(){\n //SELECT * FROM `think_user` WHERE ( (`id` BETWEEN 1 AND 2) )\n return this.where({id: [\'BETWEEN\', 1, 2]}).select();\n }\n where2(){\n //SELECT * FROM `think_user` WHERE ( (`id` BETWEEN \'1\' AND \'2\') )\n return this.where({id: [\'between\', \'1,2\']}).select();\n }\n}\n</code></pre>\n<h5 id=\"-\">多字段查询</h5>\n<pre><code class=\"lang-js\">export default class extends think.model.base {\n where1(){\n //SELECT * FROM `think_user` WHERE ( `id` = 10 ) AND ( `title` = \'www\' )\n return this.where({id: 10, title: \"www\"}).select();\n }\n //修改逻辑为 OR\n where2(){\n //SELECT * FROM `think_user` WHERE ( `id` = 10 ) OR ( `title` = \'www\' )\n return this.where({id: 10, title: \"www\", _logic: \'OR\'}).select();\n }\n //修改逻辑为 XOR\n where2(){\n //SELECT * FROM `think_user` WHERE ( `id` = 10 ) XOR ( `title` = \'www\' )\n return this.where({id: 10, title: \"www\", _logic: \'XOR\'}).select();\n }\n}\n</code></pre>\n<h5 id=\"-\">多条件查询</h5>\n<pre><code class=\"lang-js\">export default class extends think.model.base {\n where1(){\n //SELECT * FROM `think_user` WHERE ( `id` > 10 AND `id` < 20 )\n return this.where({id: {\'>\': 10, \'<\': 20}}).select();\n }\n //修改逻辑为 OR \n where2(){\n //SELECT * FROM `think_user` WHERE ( `id` < 10 OR `id` > 20 )\n return this.where({id: {\'<\': 10, \'>\': 20, _logic: \'OR\'}}).select()\n }\n}\n</code></pre>\n<h5 id=\"-\">复合查询</h5>\n<pre><code class=\"lang-js\">export default class extends think.model.base {\n where1(){\n //SELECT * FROM `think_user` WHERE ( `title` = \'test\' ) AND ( ( `id` IN (1,2,3) ) OR ( `content` = \'www\' ) )\n return this.where({\n title: \'test\',\n _complex: {id: [\'IN\', [1, 2, 3]],\n content: \'www\',\n _logic: \'or\'\n }\n }).select()\n }\n}\n</code></pre>\n<h4 id=\"model-field-field-\">model.field(field)</h4>\n<ul>\n<li><code>field</code> {String | Array} 设置要查询的字段,可以是字符串,也可以是数组</li>\n<li><code>return</code> {this}</li>\n</ul>\n<p>设置要查询的字段。</p>\n<h5 id=\"-\">字符串方式</h5>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n async indexAction(){\n let model = this.model(\'user\');\n //设置要查询的字符串,字符串方式,多个用逗号隔开\n let data = await model.field(\'name,title\').select();\n }\n}\n</code></pre>\n<h5 id=\"-sql-\">调用 SQL 函数</h5>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n //字段里调用 SQL 函数\n async listAction(){\n let model = this.model(\'user\');\n let data = await model.field(\'id, INSTR(\\\'30,35,31,\\\',id + \\\',\\\') as d\').select();\n }\n}\n</code></pre>\n<h5 id=\"-\">数组方式</h5>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n async indexAction(){\n let model = this.model(\'user\');\n //设置要查询的字符串,数组方式\n let data = await model.field([\'name\',\'title\']).select();\n }\n}\n</code></pre>\n<h4 id=\"model-fieldreverse-field-\">model.fieldReverse(field)</h4>\n<ul>\n<li><code>field</code> {String | Array} 反选字段,即查询的时候不包含这些字段</li>\n<li><code>return</code> {this}</li>\n</ul>\n<p>设置反选字段,查询的时候会过滤这些字段,支持字符串和数组 2 种方式。</p>\n<h4 id=\"model-table-table-hasprefix-\">model.table(table, hasPrefix)</h4>\n<ul>\n<li><code>table</code> {String} 表名</li>\n<li><code>hasPrefix</code> {Boolean} 是否已经有了表前缀,如果 table 值含有空格,则不在添加表前缀</li>\n<li><code>return</code> {this}</li>\n</ul>\n<p>设置表名,可以将一个 SQL 语句设置为表名。</p>\n<h5 id=\"-\">设置当前表名</h5>\n<pre><code class=\"lang-js\">export default class extends think.model.base {\n getList(){\n return this.table(\'test\', true).select();\n }\n}\n</code></pre>\n<h5 id=\"sql-\">SQL 语句作为表名</h5>\n<pre><code class=\"lang-js\">export default class extends think.model.base {\n async getList(){\n let sql = await this.model(\'group\').group(\'name\').buildSql();\n let data = await this.table(sql).select();\n return data;\n }\n}\n</code></pre>\n<h4 id=\"model-union-union-all-\">model.union(union, all)</h4>\n<ul>\n<li><code>union</code> {String | Object} 联合查询 SQL 或者表名</li>\n<li><code>all</code> {Boolean} 是否是 UNION ALL 方式</li>\n<li><code>return</code> {this}</li>\n</ul>\n<p>联合查询。</p>\n<h5 id=\"sql-\">SQL 联合查询</h5>\n<pre><code class=\"lang-js\">export default class extends think.model.base {\n getList(){\n //SELECT * FROM `think_user` UNION (SELECT * FROM think_pic2)\n return this.union(\'SELECT * FROM think_pic2\').select();\n }\n}\n</code></pre>\n<h5 id=\"-\">表名联合查询</h5>\n<pre><code class=\"lang-js\">export default class extends think.model.base {\n getList(){\n //SELECT * FROM `think_user` UNION ALL (SELECT * FROM `think_pic2`)\n return this.union({table: \'think_pic2\'}, true).select();\n }\n}\n</code></pre>\n<h4 id=\"model-join-join-\">model.join(join)</h4>\n<ul>\n<li><code>join</code> {String | Object | Array} 要组合的查询语句,默认为 <code>LEFT JOIN</code></li>\n<li><code>return</code> {this}</li>\n</ul>\n<p>组合查询,支持字符串、数组和对象等多种方式。</p>\n<h5 id=\"-\">字符串</h5>\n<pre><code class=\"lang-js\">export default class extends think.model.base {\n getList(){\n //SELECT * FROM `think_user` LEFT JOIN think_cate ON think_group.cate_id=think_cate.id\n return this.join(\'think_cate ON think_group.cate_id=think_cate.id\').select();\n }\n}\n</code></pre>\n<h5 id=\"-\">数组</h5>\n<pre><code class=\"lang-js\">export default class extends think.model.base {\n getList(){\n //SELECT * FROM `think_user` LEFT JOIN think_cate ON think_group.cate_id=think_cate.id RIGHT JOIN think_tag ON think_group.tag_id=think_tag.id\n return this.join([\n \'think_cate ON think_group.cate_id=think_cate.id\', \n \'RIGHT JOIN think_tag ON think_group.tag_id=think_tag.id\'\n ]).select();\n }\n}\n</code></pre>\n<h5 id=\"-\">对象:单个表</h5>\n<pre><code class=\"lang-js\">export default class extends think.model.base {\n getList(){\n //SELECT * FROM `think_user` INNER JOIN `think_cate` AS c ON think_user.`cate_id`=c.`id`\n return this.join({\n table: \'cate\', \n join: \'inner\', //join 方式,有 left, right, inner 3 种方式\n as: \'c\', // 表别名\n on: [\'cate_id\', \'id\'] //ON 条件\n }).select();\n }\n}\n</code></pre>\n<h5 id=\"-join\">对象:多次 JOIN</h5>\n<pre><code class=\"lang-js\">export default class extends think.model.base {\n getList(){\n //SELECT * FROM think_user AS a LEFT JOIN `think_cate` AS c ON a.`cate_id`=c.`id` LEFT JOIN `think_group_tag` AS d ON a.`id`=d.`group_id`\n return this.alias(\'a\').join({\n table: \'cate\',\n join: \'left\',\n as: \'c\',\n on: [\'cate_id\', \'id\']\n }).join({\n table: \'group_tag\',\n join: \'left\',\n as: \'d\',\n on: [\'id\', \'group_id\']\n }).select()\n }\n}\n</code></pre>\n<h5 id=\"-\">对象:多个表</h5>\n<pre><code class=\"lang-js\">export default class extends think.model.base {\n getList(){\n //SELECT * FROM `think_user` LEFT JOIN `think_cate` ON think_user.`id`=think_cate.`id` LEFT JOIN `think_group_tag` ON think_user.`id`=think_group_tag.`group_id`\n return this.join({\n cate: {\n on: [\'id\', \'id\']\n },\n group_tag: {\n on: [\'id\', \'group_id\']\n }\n }).select();\n }\n}\n</code></pre>\n<pre><code class=\"lang-js\">export default class extends think.model.base {\n getList(){\n //SELECT * FROM think_user AS a LEFT JOIN `think_cate` AS c ON a.`id`=c.`id` LEFT JOIN `think_group_tag` AS d ON a.`id`=d.`group_id`\n return this.alias(\'a\').join({\n cate: {\n join: \'left\', // 有 left,right,inner 3 个值\n as: \'c\',\n on: [\'id\', \'id\']\n },\n group_tag: {\n join: \'left\',\n as: \'d\',\n on: [\'id\', \'group_id\']\n }\n }).select()\n }\n}\n</code></pre>\n<h5 id=\"-on-\">对象:ON 条件含有多个字段</h5>\n<pre><code class=\"lang-js\">export default class extends think.model.base {\n getList(){\n //SELECT * FROM `think_user` LEFT JOIN `think_cate` ON think_user.`id`=think_cate.`id` LEFT JOIN `think_group_tag` ON think_user.`id`=think_group_tag.`group_id` LEFT JOIN `think_tag` ON (think_user.`id`=think_tag.`id` AND think_user.`title`=think_tag.`name`)\n return this.join({\n cate: {on: \'id, id\'},\n group_tag: {on: [\'id\', \'group_id\']},\n tag: {\n on: { // 多个字段的 ON\n id: \'id\',\n title: \'name\'\n }\n }\n }).select()\n }\n}\n</code></pre>\n<h5 id=\"-table-sql-\">对象:table 值为 SQL 语句</h5>\n<pre><code class=\"lang-js\">export default class extends think.model.base {\n async getList(){\n let sql = await this.model(\'group\').buildSql();\n //SELECT * FROM `think_user` LEFT JOIN ( SELECT * FROM `think_group` ) ON think_user.`gid`=( SELECT * FROM `think_group` ).`id`\n return this.join({\n table: sql,\n on: [\'gid\', \'id\']\n }).select();\n }\n}\n</code></pre>\n<h4 id=\"model-order-order-\">model.order(order)</h4>\n<ul>\n<li><code>order</code> {String | Array | Object} 排序方式</li>\n<li><code>return</code> {this}</li>\n</ul>\n<p>设置排序方式。</p>\n<h5 id=\"-\">字符串</h5>\n<pre><code class=\"lang-js\">export default class extends think.model.base {\n getList(){\n //SELECT * FROM `think_user` ORDER BY id DESC, name ASC\n return this.order(\'id DESC, name ASC\').select();\n }\n getList1(){\n //SELECT * FROM `think_user` ORDER BY count(num) DESC\n return this.order(\'count(num) DESC\').select();\n }\n}\n</code></pre>\n<h5 id=\"-\">数组</h5>\n<pre><code class=\"lang-js\">export default class extends think.model.base {\n getList(){\n //SELECT * FROM `think_user` ORDER BY id DESC,name ASC\n return this.order([\'id DESC\', \'name ASC\']).select();\n }\n}\n</code></pre>\n<h5 id=\"-\">对象</h5>\n<pre><code class=\"lang-js\">export default class extends think.model.base {\n getList(){\n //SELECT * FROM `think_user` ORDER BY `id` DESC,`name` ASC\n return this.order({\n id: \'DESC\',\n name: \'ASC\'\n }).select();\n }\n}\n</code></pre>\n<h4 id=\"model-alias-tablealias-\">model.alias(tableAlias)</h4>\n<ul>\n<li><code>tableAlias</code> {String} 表别名</li>\n<li><code>return</code> {this}</li>\n</ul>\n<p>设置表别名。</p>\n<pre><code class=\"lang-js\">export default class extends think.model.base {\n getList(){\n //SELECT * FROM think_user AS a;\n return this.alias(\'a\').select();\n }\n}\n</code></pre>\n<h4 id=\"model-having-having-\">model.having(having)</h4>\n<ul>\n<li><code>having</code> {String} having 查询的字符串</li>\n<li><code>return</code> {this}</li>\n</ul>\n<p>设置 having 查询。</p>\n<pre><code class=\"lang-js\">export default class extends think.model.base {\n getList(){\n //SELECT * FROM `think_user` HAVING view_nums > 1000 AND view_nums < 2000\n return this.having(\'view_nums > 1000 AND view_nums < 2000\').select();\n }\n}\n</code></pre>\n<h4 id=\"model-group-group-\">model.group(group)</h4>\n<ul>\n<li><code>group</code> {String} 分组查询的字段</li>\n<li><code>return</code> {this}</li>\n</ul>\n<p>设定分组查询。</p>\n<pre><code class=\"lang-js\">export default class extends think.model.base {\n getList(){\n //SELECT * FROM `think_user` GROUP BY `name`\n return this.group(\'name\').select();\n }\n}\n</code></pre>\n<h4 id=\"model-distinct-distinct-\">model.distinct(distinct)</h4>\n<ul>\n<li><code>distinct</code> {String} 去重的字段</li>\n<li><code>return</code> {this}</li>\n</ul>\n<p>去重查询。</p>\n<pre><code class=\"lang-js\">export default class extends think.model.base {\n getList(){\n //SELECT DISTINCT `name` FROM `think_user`\n return this.distinct(\'name\').select();\n }\n}\n</code></pre>\n<h4 id=\"model-explain-explain-\">model.explain(explain)</h4>\n<ul>\n<li><code>explain</code> {Boolean} 是否添加 explain 执行</li>\n<li><code>return</code> {this}</li>\n</ul>\n<p>是否在 SQL 之前添加 explain 执行,用来查看 SQL 的性能。</p>\n<h4 id=\"model-optionsfilter-options-\">model.optionsFilter(options)</h4>\n<p>操作选项过滤。</p>\n<h4 id=\"model-datafilter-data-\">model.dataFilter(data)</h4>\n<ul>\n<li><code>data</code> {Object | Array} 要操作的数据</li>\n</ul>\n<p>数据过滤。</p>\n<h4 id=\"model-beforeadd-data-\">model.beforeAdd(data)</h4>\n<ul>\n<li><code>data</code> {Object} 要添加的数据</li>\n</ul>\n<p>添加前置操作。</p>\n<h4 id=\"model-afteradd-data-\">model.afterAdd(data)</h4>\n<ul>\n<li><code>data</code> {Object} 要添加的数据</li>\n</ul>\n<p>添加后置操作。</p>\n<h4 id=\"model-afterdelete-data-\">model.afterDelete(data)</h4>\n<p>删除后置操作。</p>\n<h4 id=\"model-beforeupdate-data-\">model.beforeUpdate(data)</h4>\n<ul>\n<li><code>data</code> {Object} 要更新的数据</li>\n</ul>\n<p>更新前置操作。</p>\n<h4 id=\"model-afterupdate-data-\">model.afterUpdate(data)</h4>\n<ul>\n<li><code>data</code> {Object} 要更新的数据</li>\n</ul>\n<p>更新后置操作。</p>\n<h4 id=\"model-afterfind-data-\">model.afterFind(data)</h4>\n<ul>\n<li><code>data</code> {Object} 查询的单条数据</li>\n<li><code>return</code> {Object | Promise}</li>\n</ul>\n<p><code>find</code> 查询后置操作。</p>\n<h4 id=\"model-afterselect-data-\">model.afterSelect(data)</h4>\n<ul>\n<li><code>data</code> [Array] 查询的数据数据</li>\n<li><code>return</code> {Array | Promise}</li>\n</ul>\n<p><code>select</code> 查询后置操作。</p>\n<h4 id=\"model-data-data-\">model.data(data)</h4>\n<ul>\n<li><code>data</code> {Object}</li>\n</ul>\n<p>添加和更新操作时设置操作的数据。</p>\n<h4 id=\"model-options-options-\">model.options(options)</h4>\n<ul>\n<li><code>options</code> {Object} </li>\n</ul>\n<p>设置操作选项。如:</p>\n<pre><code class=\"lang-js\">export default class extends think.model.base {\n getList(){\n return this.options({\n where: \'id = 1\',\n limit: [10, 1]\n }).select();\n }\n}\n</code></pre>\n<h4 id=\"model-close-\">model.close()</h4>\n<p>关于数据库连接,一般情况下不要直接调用。</p>\n<h4 id=\"model-getschema-table-\">model.getSchema(table)</h4>\n<ul>\n<li><code>table</code> {String} 表名</li>\n<li><code>return</code> {Promise}</li>\n</ul>\n<p>获取表的字段信息,自动从数据库中读取。</p>\n<h4 id=\"model-gettablefields-table-\">model.getTableFields(table)</h4>\n<p><code>已废弃</code>,使用 model.getSchema 方法替代。</p>\n<h4 id=\"model-getlastsql-\">model.getLastSql()</h4>\n<ul>\n<li><code>return</code> {String}</li>\n</ul>\n<p>获取最后执行的 SQL 语句。</p>\n<h4 id=\"model-buildsql-\">model.buildSql()</h4>\n<ul>\n<li><code>return</code> {Promise}</li>\n</ul>\n<p>将当前的查询条件生成一个 SQL 语句。</p>\n<h4 id=\"model-parseoptions-oriopts-extraoptions-\">model.parseOptions(oriOpts, extraOptions)</h4>\n<ul>\n<li><code>oriOpts</code> {Object}</li>\n<li><code>extraOptions</code> {Object}</li>\n<li><code>return</code> {Promise}</li>\n</ul>\n<p>根据已经设定的一些条件解析当前的操作选项。</p>\n<h4 id=\"model-getpk-\">model.getPk()</h4>\n<ul>\n<li><code>return</code> {Promise}</li>\n</ul>\n<p>返回 <code>pk</code> 的值,返回一个 Promise。</p>\n<h4 id=\"model-parsetype-field-value-\">model.parseType(field, value)</h4>\n<ul>\n<li><code>field</code> {String} 数据表中的字段名称</li>\n<li><code>value</code> {Mixed}</li>\n<li><code>return</code> {Mixed}</li>\n</ul>\n<p>根据数据表中的字段类型解析 value。</p>\n<h4 id=\"model-parsedata-data-\">model.parseData(data)</h4>\n<ul>\n<li><code>data</code> {Object} 要解析的数据</li>\n<li><code>return</code> {Object}</li>\n</ul>\n<p>调用 <code>parseType</code> 方法解析数据。</p>\n<h4 id=\"model-add-data-options-replace-\">model.add(data, options, replace)</h4>\n<ul>\n<li><code>data</code> {Object} 要添加的数据</li>\n<li><code>options</code> {Object} 操作选项</li>\n<li><code>replace</code> {Boolean} 是否是替换操作</li>\n<li><code>return</code> {Promise} 返回插入的 ID</li>\n</ul>\n<p>添加一条数据。</p>\n<h4 id=\"model-thenadd-data-where-\">model.thenAdd(data, where)</h4>\n<ul>\n<li><code>data</code> {Object} 要添加的数据</li>\n<li><code>where</code> {Object} where 条件</li>\n<li><code>return</code> {Promise}</li>\n</ul>\n<p>当 where 条件未命中到任何数据时才添加数据。</p>\n<h4 id=\"model-addmany-datalist-options-replace-\">model.addMany(dataList, options, replace)</h4>\n<ul>\n<li><code>dataList</code> {Array} 要添加的数据列表</li>\n<li><code>options</code> {Object} 操作选项</li>\n<li><code>replace</code> {Boolean} 是否是替换操作</li>\n<li><code>return</code> {Promise} 返回插入的 ID</li>\n</ul>\n<p>一次添加多条数据。</p>\n<h4 id=\"model-delete-options-\">model.delete(options)</h4>\n<ul>\n<li><code>options</code> {Object} 操作选项</li>\n<li><code>return</code> {Promise} 返回影响的行数</li>\n</ul>\n<p>删除数据。</p>\n<p>删除 id 为 7 的数据。</p>\n<pre><code class=\"lang-js\">model.delete({\n where: {id: 7}\n});\n</code></pre>\n<h4 id=\"model-update-data-options-\">model.update(data, options)</h4>\n<ul>\n<li><code>data</code> {Object} 要更新的数据</li>\n<li><code>options</code> {Object} 操作选项</li>\n<li><code>return</code> {Promise} 返回影响的行数</li>\n</ul>\n<p>更新数据。</p>\n<h4 id=\"updatemany-datalist-options-\">updateMany(dataList, options)</h4>\n<ul>\n<li><code>dataList</code> {Array} 要更新的数据列表</li>\n<li><code>options</code> {Object} 操作选项</li>\n<li><code>return</code> {Promise}</li>\n</ul>\n<p>更新多条数据,dataList 里必须包含主键的值,会自动设置为更新条件。</p>\n<h4 id=\"model-increment-field-step-\">model.increment(field, step)</h4>\n<ul>\n<li><code>field</code> {String} 字段名</li>\n<li><code>step</code> {Number} 增加的值,默认为 1</li>\n<li><code>return</code> {Promise}</li>\n</ul>\n<p>字段值增加。</p>\n<h4 id=\"model-decrement-field-step-\">model.decrement(field, step)</h4>\n<ul>\n<li><code>field</code> {String} 字段名</li>\n<li><code>step</code> {Number} 增加的值,默认为 1</li>\n<li><code>return</code> {Promise}</li>\n</ul>\n<p>字段值减少。</p>\n<h4 id=\"model-find-options-\">model.find(options)</h4>\n<ul>\n<li><code>options</code> {Object} 操作选项</li>\n<li><code>return</code> {Promise} 返回单条数据</li>\n</ul>\n<p>查询单条数据,返回的数据类型为对象。如果未查询到相关数据,返回值为 <code>{}</code>。</p>\n<h4 id=\"model-select-options-\">model.select(options)</h4>\n<ul>\n<li><code>options</code> {Object} 操作选项</li>\n<li><code>return</code> {Promise} 返回多条数据</li>\n</ul>\n<p>查询多条数据,返回的数据类型为数组。如果未查询到相关数据,返回值为 <code>[]</code>。</p>\n<h4 id=\"model-countselect-options-pageflag-\">model.countSelect(options, pageFlag)</h4>\n<ul>\n<li><code>options</code> {Object} 操作选项</li>\n<li><code>pageFlag</code> {Boolean} 当页数不合法时处理,true 为修正到第一页,false 为修正到最后一页,默认不修正</li>\n<li><code>return</code> {Promise}</li>\n</ul>\n<p>分页查询,一般需要结合 <code>page</code> 方法一起使用。如:</p>\n<pre><code class=\"lang-js\">export default class extends think.controller.base {\n async listAction(){\n let model = this.model(\'user\');\n let data = await model.page(this.get(\'page\')).countSelect();\n }\n}\n</code></pre>\n<p>返回值数据结构如下:</p>\n<pre><code class=\"lang-js\">{\n numsPerPage: 10, //每页显示的条数\n currentPage: 1, //当前页\n count: 100, //总条数\n totalPages: 10, //总页数\n data: [{ //当前页下的数据列表\n name: \"thinkjs\",\n email: \"[email protected]\"\n }, ...]\n}\n</code></pre>\n<h4 id=\"model-getfield-field-one-\">model.getField(field, one)</h4>\n<ul>\n<li><code>field</code> {String} 字段名,多个字段用逗号隔开</li>\n<li><code>one</code> {Boolean | Number} 获取的条数</li>\n<li><code>return</code> {Promise}</li>\n</ul>\n<p>获取特定字段的值。</p>\n<h4 id=\"model-count-field-\">model.count(field)</h4>\n<ul>\n<li><code>field</code> {String} 字段名</li>\n<li><code>return</code> {Promise} 返回总条数</li>\n</ul>\n<p>获取总条数。</p>\n<h4 id=\"model-sum-field-\">model.sum(field)</h4>\n<ul>\n<li><code>field</code> {String} 字段名</li>\n<li><code>return</code> {Promise}</li>\n</ul>\n<p>对字段值进行求和。</p>\n<h4 id=\"model-min-field-\">model.min(field)</h4>\n<ul>\n<li><code>field</code> {String} 字段名</li>\n<li><code>return</code> {Promise}</li>\n</ul>\n<p>求字段的最小值。</p>\n<h4 id=\"model-max-field-\">model.max(field)</h4>\n<ul>\n<li><code>field</code> {String} 字段名</li>\n<li><code>return</code> {Promise}</li>\n</ul>\n<p>求字段的最大值。</p>\n<h4 id=\"model-avg-field-\">model.avg(field)</h4>\n<ul>\n<li><code>field</code> {String} 字段名</li>\n<li><code>return</code> {Promise}</li>\n</ul>\n<p>求字段的平均值。</p>\n<h4 id=\"model-query-args-\">model.query(...args)</h4>\n<ul>\n<li><code>return</code> {Promise}</li>\n</ul>\n<p>指定 SQL 语句执行查询。</p>\n<h4 id=\"model-execute-args-\">model.execute(...args)</h4>\n<ul>\n<li><code>return</code> {Promise}</li>\n</ul>\n<p>执行 SQL 语句。</p>\n<h4 id=\"model-parsesql-sql-args-\">model.parseSql(sql, ...args)</h4>\n<ul>\n<li><code>sql</code> {String} 要解析的 SQL 语句</li>\n<li><code>return</code> {String}</li>\n</ul>\n<p>解析 SQL 语句,调用 <code>util.format</code> 方法解析 SQL 语句,并将 SQL 语句中的 <code>__TABLENAME__</code> 解析为对应的表名。</p>\n<pre><code class=\"lang-js\">export default class extends think.model.base {\n getSql(){\n let sql = \'SELECT * FROM __GROUP__ WHERE id=%d\';\n sql = this.parseSql(sql, 10);\n //sql is SELECT * FROM think_group WHERE id=10\n }\n}\n</code></pre>\n<h4 id=\"model-starttrans-\">model.startTrans()</h4>\n<ul>\n<li><code>return</code> {Promise}</li>\n</ul>\n<p>开启事务。</p>\n<h4 id=\"model-commit-\">model.commit()</h4>\n<ul>\n<li><code>return</code> {Promise}</li>\n</ul>\n<p>提交事务。</p>\n<h4 id=\"model-rollback-\">model.rollback()</h4>\n<ul>\n<li><code>return</code> {Promise}</li>\n</ul>\n<p>回滚事务。</p>\n<h4 id=\"model-transaction-fn-\">model.transaction(fn)</h4>\n<ul>\n<li><code>fn</code> {Function} 要执行的函数</li>\n<li><code>return</code> {Promise}</li>\n</ul>\n<p>使用事务来执行传递的函数,函数要返回 Promise。</p>\n<pre><code class=\"lang-js\">export default class extends think.model.base {\n updateData(data){\n return this.transaction(async () => {\n let insertId = await this.add(data);\n let result = await this.model(\'user_cate\').add({user_id: insertId, cate_id: 100});\n return result;\n })\n }\n}\n</code></pre><p>\n文章来源:<a href=\"http://www.thinkjs.org\" target=\"_blank\">http://www.thinkjs.org</a></p><p><br></p>', '', '', '2016-07-17 14:37:21', '62', '0', '0', '0', '18', 'thinkjs,nodejs', '1', '1', '', '8', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('298', 'MongoDB', '', '<p><code>think.model.mongo</code> 继承类 <a href=\"./api_model.html\">think.model.base</a>。</p>\n<h5 id=\"-es6-\">使用 ES6 的语法继承该类</h5>\n<pre><code class=\"lang-js\">export default class extends think.model.mongo {\n getList(){\n\n }\n}\n</code></pre>\n<h5 id=\"-\">使用普通方式继承该类</h5>\n<pre><code class=\"lang-js\">module.exports = think.model(\'mongo\', {\n getList: function(){\n\n }\n})\n</code></pre>\n<h3 id=\"-\">属性</h3>\n<h4 id=\"model-schema\">model.schema</h4>\n<p>设置字段,如:</p>\n<pre><code class=\"lang-js\">export default class extends think.model.mongo {\n init(...args){\n super.init(...args);\n //设置字段\n this.schema = {\n name: {\n type: \'string\'\n },\n pwd: {\n type: \'string\'\n }\n }\n }\n}\n</code></pre>\n<p><code>注</code>:目前框架并不会对字段进行检查。</p>\n<h4 id=\"model-indexes\">model.indexes</h4>\n<p>设置字段索引,数据操作之前会自动创建索引。</p>\n<pre><code class=\"lang-js\">export default class extends think.model.mongo {\n init(...args){\n super.init(...args);\n //配置索引\n this.indexes = { \n\n }\n }\n}\n</code></pre>\n<h5 id=\"-\">单字段索引</h5>\n<pre><code class=\"lang-js\">export default class extends think.model.mongo {\n init(...args){\n super.init(...args);\n //配置索引\n this.indexes = { \n name: 1\n }\n }\n}\n</code></pre>\n<h5 id=\"-\">唯一索引</h5>\n<p>通过 <code>$unique</code> 来指定为唯一索引,如:</p>\n<pre><code class=\"lang-js\">export default class extends think.model.mongo {\n init(...args){\n super.init(...args);\n //配置索引\n this.indexes = { \n name: {$unique: 1}\n }\n }\n}\n</code></pre>\n<h5 id=\"-\">多字段索引</h5>\n<p>可以将多个字段联合索引,如:</p>\n<pre><code class=\"lang-js\">export default class extends think.model.mongo {\n init(...args){\n super.init(...args);\n //配置索引\n this.indexes = { \n email: 1\n test: {\n name: 1,\n title: 1,\n $unique: 1\n }\n }\n }\n}\n</code></pre>\n<h4 id=\"model-pk\">model.pk</h4>\n<p>主键名,默认为 <code>_id</code>,可以通过 <code>this.getPk</code> 方法获取。</p>\n<h3 id=\"-\">方法</h3>\n<h4 id=\"model-where-where-\">model.where(where)</h4>\n<p>mongo 模型中的 where 条件设置和关系数据库中不太一样。</p>\n<h5 id=\"-\">等于判断</h5>\n<pre><code class=\"lang-js\">export default class extends think.model.mongo {\n where1(){\n return this.where({ type: \"snacks\" }).select();\n }\n}\n</code></pre>\n<h5 id=\"and-\">AND 条件</h5>\n<pre><code class=\"lang-js\">export default class extends think.model.mongo {\n where1(){\n return this.where({ type: \'food\', price: { $lt: 9.95 } }).select();\n }\n}\n</code></pre>\n<h5 id=\"or-\">OR 条件</h5>\n<pre><code class=\"lang-js\">export default class extends think.model.mongo {\n where1(){\n return this.where({\n $or: [ { qty: { $gt: 100 } }, { price: { $lt: 9.95 } } ]\n }).select();\n }\n where2(){\n return this.where({\n type: \'food\',\n $or: [ { qty: { $gt: 100 } }, { price: { $lt: 9.95 } } ]\n }).select();\n }\n}\n</code></pre>\n<h5 id=\"-\">内嵌文档</h5>\n<pre><code class=\"lang-js\">export default class extends think.model.mongo {\n where1(){\n return this.where( {\n producer:\n {\n company: \'ABC123\',\n address: \'123 Street\'\n }\n }).select();\n }\n where2(){\n return this.where({ \'producer.company\': \'ABC123\' } ).select();\n }\n}\n</code></pre>\n<h5 id=\"in-\">IN 条件</h5>\n<pre><code class=\"lang-js\">export default class extends think.model.mongo {\n where1(){\n return this.where({ type: { $in: [ \'food\', \'snacks\' ] } }).select();\n }\n}\n</code></pre>\n<hr>\n<p>更多文档请见 <a href=\"https://docs.mongodb.org/manual/reference/operator/query/#query-selectors\">https://docs.mongodb.org/manual/reference/operator/query/#query-selectors</a>。</p>\n<h4 id=\"model-collection-\">model.collection()</h4>\n<ul>\n<li><code>return</code> {Promise}</li>\n</ul>\n<p>获取操作当前表的句柄。</p>\n<pre><code class=\"lang-js\">export default class extends think.model.mongo {\n async getIndexes(){\n let collection = await this.collection();\n return collection.indexes();\n }\n}\n</code></pre>\n<h4 id=\"model-aggregate-options-\">model.aggregate(options)</h4>\n<p>聚合查询。具体请见 <a href=\"https://docs.mongodb.org/manual/core/aggregation-introduction/\">https://docs.mongodb.org/manual/core/aggregation-introduction/</a>。</p>\n<h4 id=\"model-mapreduce-map-reduce-out-\">model.mapReduce(map, reduce, out)</h4>\n<p>mapReduce 操作,具体请见 <a href=\"https://docs.mongodb.org/manual/core/map-reduce/\">https://docs.mongodb.org/manual/core/map-reduce/</a>。</p>\n<h4 id=\"model-createindex-indexes-options-\">model.createIndex(indexes, options)</h4>\n<ul>\n<li><code>indexes</code> {Object} 索引配置</li>\n<li><code>options</code> {Object}</li>\n</ul>\n<p>创建索引。</p>\n<h4 id=\"model-getindexes-\">model.getIndexes()</h4>\n<ul>\n<li><code>return</code> {Promise}</li>\n</ul>\n<p>获取索引。</p><p>文章来源:<a href=\"http://www.thinkjs.org/\">http://www.thinkjs.org</a></p><p><br></p>', '', '', '2016-07-17 14:39:00', '76', '0', '0', '0', '18', 'thinkjs,nodejs', '1', '1', '', '8', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('299', 'middleware', '', '<p><code>think.middleware.base</code> 类继承自 <a href=\"./api_think_http_base.html\">think.http.base</a>。</p>\n<h5 id=\"es6-\">ES6 方式</h5>\n<pre><code class=\"lang-js\">export default class extends think.middleware.base {\n run(){\n\n }\n}\n</code></pre>\n<h5 id=\"-\">动态创建类的方式</h5>\n<pre><code class=\"lang-js\">module.exports = think.middleware({\n run: function(){\n\n }\n})\n</code></pre>\n<h3 id=\"-\">方法</h3>\n<h4 id=\"middleare-run-\">middleare.run()</h4>\n<ul>\n<li><code>return</code> {Promise}</li>\n</ul>\n<p>middleware 对外的方法入口,调用 middleware 时会自动调用该方法。</p><p>文章来源:<a href=\"http://www.thinkjs.org/\">http://www.thinkjs.org</a></p>\n<p><br></p>', '', '', '2016-07-17 14:39:59', '178', '0', '0', '0', '18', 'thinkjs,nodejs', '1', '1', '', '8', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('308', '杭州微易信息科技-互联网金融的新兴力量(15k-25k)', '杭州微易信息科技-互联网金融的新兴力量,\n\n创立于2014年,专注于保险市场垂直细分领域。\n\n公司旗下产品“保险师”自2015年5月上线以来,\n\n以精准的产品定位、便捷的功能体验,拥有了行业领先的用户量,\n\n在AppStore工具类应用下载排名位居前50。', '<p><b>公司简介:</b></p><p>杭州微易信息科技-互联网金融的新兴力量,\n</p><p>创立于2014年,专注于保险市场垂直细分领域。\n</p><p>公司旗下产品“保险师”自2015年5月上线以来,\n</p><p>以精准的产品定位、便捷的功能体验,拥有了行业领先的用户量,\n</p><p>在AppStore工具类应用下载排名位居前50。\n</p><p>团队成员以陈伟星(快的打车创始人)为代表,\n</p><p>主要来自阿里、腾讯、中国平安等知名企业,具有丰富的互联网从业经验和行业资源。\n</p><p>目前公司已与业内多家知名保险企业及互联网平台达成战略合作,\n</p><p>并提供业内具有竞争力的薪酬福利和宽松的工作氛围,Win期待你的加入!\n</p><p>网址:http://www.winbaoxian.com/\n</p><p>现招高级前端工程师一名。薪资15k-25k。\n</p><p><b>职位描述:\n</b></p><p>1、负责前端开发、前端架构设计与技术性能优化;\n</p><p>2、与后端开发工程师配合完成产品开发;\n</p><p><b>职位要求:\n</b></p><p>1、3年或以上web前端工作经验,熟悉W3C标准,有强烈的前端性能优化意识;\n</p><p>2、精通各种Web前端技术,XHTML(HTML5)、XML、CSS(3)、JavaScript,熟悉页面架构和布局;\n</p><p>3、熟悉原生Javascript,有Node.js开发经验者优先;\n</p><p>4、HTML5/CSS3方面有实际的理解和实践经验;\n</p><p>5、对工作认真负责,具有良好的沟通能力、分析问题和解决问题的能力;\n</p><p><b>公司地址:</b><span style=\"line-height: 1.8;\">杭州市西湖区万塘路252号计量大厦16楼</span></p><p>感兴趣的,可以加qq:97781721</p><p><br></p>', 'static/upload/pics/7/29/2016Mo2NYNCO2MgN4DmO1dTMrVVB.jpg', '前端汇', '2016-07-29 10:36:09', '583', '1', '1', '0', '15', '', '1', '1', '', '5', '0', null, null, null, null);
INSERT INTO `li_article` VALUES ('309', '【杭州】OSC源创会第51期报名开始', '时间:2016-08-13 14:00\n地点:杭州 拱墅区莫干山路188-200号之江饭店\n费用:现场缴费¥50/人,女士免费,积分50以上,开源软件作者和学生(出示学生证)均免费,提供饮料和小食', '<dl><dd>时间:2016-08-13 14:00</dd><dd>地点:杭州 拱墅区莫干山路188-200号之江饭店</dd><dd>费用:现场缴费¥50/人,女士免费,积分50以上,开源软件作者和学生(出示学生证)均免费,提供饮料和小食</dd><dd>类型: 源创会</dd><dd>引用标识: <a href=\"http://city.oschina.net/hangzhou?ei=hz784\" target=\"_blank\">hz784</a></dd><dd>发起人:<a href=\"http://my.oschina.net/iris22\" target=\"_blank\">Alaise</a></dd><dd>活动链接:<a href=\"http://www.oschina.net/question/2686220_2187784\" target=\"_blank\">http://www.oschina.net/question/2686220_2187784</a></dd></dl><p>活动介绍:</p><p>邀请超过3个好友报名并确认,你将可以免费参加此次源创会 邀请链接:<a href=\"http://city.oschina.net/hangzhou/event/2187784?inviter=\">http://city.oschina.net/hangzhou/event/2187784?inviter=</a></p><p>都说上有天堂,下有杭州,OSC源创会也不能抵挡的美丽,这已经是我们第五次来到杭州,与各位OSCer共约线下,本次八月杭州站我们依旧和去年一样在之江饭店,等你来赴约。</p><p>本期主题有:</p><p>1.云时代的企业运维转型</p><p>内容简介:随着云的概念逐步被接受,很多企业开始尝试公有云,这给传统运维模式带来了一系列的变化,例如跨云的管理、虚拟化及容器化带来的操作单元暴增、运维职能被部分新技术所取代,运维如何探索职能边界的延伸、运维如何利用云及大数据技术帮助业务实现精细化运营。</p><p>分享嘉宾:党受辉,腾讯游戏蓝鲸产品中心总监,曾就职于东软集团,为能源行业定制信息化体系。加入腾讯后负责过游戏运维团队管理。2012年负责腾讯游戏技术支撑体系(蓝鲸)的设计、建设和运营。</p><p>2.Freezer—OpenStack Backup/Restore as a Service</p><p>内容简介:在OpenStack的使用过程中,你是不是遇到过这样的问题,虚拟机突然不可用或者被误删! 云硬盘突然无法使用或不小心被删除!对此,数据备份才是关键所在,其重要性不言而喻。 本次内容主要分享 Freezer 作为OpenStack的一个Official 项目,在OpenStack开源云环境中,针对数据备份存在的痛点,通过Freezer构建备份机制,以及进行有效的配置对虚拟机、云硬盘数据进行全量以及增量的数据备份。</p><p>分享嘉宾:杨亚鹏,现就职于九州云99Cloud,从事OpenStack相关工作两年。现主要从事OpenStack 备份相关工作。</p><p>3.数据可视化与大数据的结合</p><p>内容简介:分享数据可视化与大数据结合的相关技术点及经验。</p><p>分享嘉宾:乔刚,高德开放平台资深前端开发工程师,从事SPA应用开发,对此有丰富的经验。目前主要负责地图数据可视化相关的开发工作。</p><p>4.透过 TheDAO 众筹安全事件看区块链安全问题</p><p>内容简介:2016年6月17日,TheDAO(目前人类历史上最大规模的众筹项目)发生了区块链领域重大的安全事件,期间剧情跌宕起伏,注定会被载入区块链安全史册。由于其编写的智能合约存在着重大缺陷,区块链业界最大的众筹项目TheDAO(被攻击前 拥有1亿美元左右资产)遭到攻击,目前已导致300多万以太币资产被分离出去。本次分享将为大家介绍 TheDAO 事件的始末,以及区块链和智能合约的安全性方面的探讨。</p><p>分享嘉宾:吕国宁,资深程序员 & 区块链技术专家,杭州融识科技合伙人,EthFans 以太坊爱好者社区运营负责人,前云币交易所 CTO,国内最大的线上技术社区 RubyChina 的创始人,管理员,RubyConfChina 大会组织者,技术音频播客 Teahour.FM 主播。</p><p>5.跨平台移动开发工具 Weex 开源之路</p><p>内容简介:介绍 Weex 从诞生到开源的心路历程,希望给所有对开源有兴趣的同学一些帮助和启发。</p><p>分享嘉宾:赵锦江,花名勾股,就职于阿里巴巴淘宝,h5slides 作者,Weex 团队一员。</p><p>本次杭州站源创会礼品之丰厚可谓超乎想象,除了经典的大型鼠标垫、开源内裤、扑克,还有樱桃机械键盘等着你哟!</p><p>本次源创会需要摄影、录像及会务协助志愿者,有意愿的小伙伴请站内私信<a href=\"http://my.oschina.net/penny55\"></a><a href=\"http://my.oschina.net/penny55\" target=\"_blank\">@penny-osc</a> ,会后还将有精美礼品相送呦~</p><p>请大家下载 OSC 客户端用于现场扫描二维码签到和抽奖:<a href=\"http://www.oschina.net/app\">http://www.oschina.net/app</a></p><p>为了方便大家线上交流,本次源创会提供微信群,报名的小伙伴可扫描以下二维码,备注杭州源创会,添加好友,拉你入群~</p><p><img src=\"/static/upload/pics/8/4/2016SqQXBZ9dgVwMv43NhlM1eYto.jpg\" alt=\"171848_v9EO_2686220\" style=\"max-width:100%;\"><br></p><p>活动详情:<a href=\"http://city.oschina.net/hangzhou/event/2187784\" target=\"_blank\">http://city.oschina.net/hangzhou/event/2187784</a></p><p><br></p>', 'static/upload/pics/8/6/2016sS0SWY9BOhOiFqyMDzDpTmLP.png', '前端汇', '2016-08-16 10:10:22', '283', '0', '0', '0', '14', '', '1', '1', '', '6', '0', null, null, null, null);
-- ----------------------------
-- Table structure for li_comment
-- ----------------------------
DROP TABLE IF EXISTS `li_comment`;
CREATE TABLE `li_comment` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`aid` int(11) DEFAULT NULL COMMENT '文章id',
`author` varchar(255) COLLATE utf8_bin DEFAULT NULL,
`email` varchar(255) COLLATE utf8_bin DEFAULT NULL,
`qq` varchar(255) COLLATE utf8_bin DEFAULT NULL,
`comment` varchar(255) COLLATE utf8_bin DEFAULT NULL,
`belongid` int(11) DEFAULT '0' COMMENT '回复的评论id',
`dig` int(11) DEFAULT '0',
`tipoff` int(11) DEFAULT '0' COMMENT '举报',
`createtime` datetime DEFAULT NULL,
`pic` varchar(255) COLLATE utf8_bin DEFAULT '' COMMENT '头像',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
-- ----------------------------
-- Records of li_comment
-- ----------------------------
-- ----------------------------
-- Table structure for li_guest
-- ----------------------------
DROP TABLE IF EXISTS `li_guest`;
CREATE TABLE `li_guest` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`nickname` varchar(255) COLLATE utf8_bin DEFAULT NULL,
`contact` varchar(255) COLLATE utf8_bin DEFAULT NULL,
`guest` varchar(255) COLLATE utf8_bin NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=39 DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
-- ----------------------------
-- Records of li_guest
-- ----------------------------
INSERT INTO `li_guest` VALUES ('36', 's', '是', '搜索');
INSERT INTO `li_guest` VALUES ('37', '范德萨发斯蒂芬', '的说法是否', '你好');
INSERT INTO `li_guest` VALUES ('38', '猿天地', '[email protected]', '我申请了交换友情链接');
-- ----------------------------
-- Table structure for li_item
-- ----------------------------
DROP TABLE IF EXISTS `li_item`;
CREATE TABLE `li_item` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`itemname` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of li_item
-- ----------------------------
INSERT INTO `li_item` VALUES ('1', '文章');
INSERT INTO `li_item` VALUES ('2', '前端资讯');
INSERT INTO `li_item` VALUES ('3', 'nodejs');
INSERT INTO `li_item` VALUES ('4', '资源下载');
INSERT INTO `li_item` VALUES ('5', '招聘');
INSERT INTO `li_item` VALUES ('6', '活动');
INSERT INTO `li_item` VALUES ('8', '文档');
-- ----------------------------
-- Table structure for li_links
-- ----------------------------
DROP TABLE IF EXISTS `li_links`;
CREATE TABLE `li_links` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`domain` varchar(255) COLLATE utf8_bin DEFAULT NULL,
`link` varchar(255) COLLATE utf8_bin DEFAULT NULL,
`logo` varchar(255) COLLATE utf8_bin DEFAULT NULL,
`qq` varchar(255) COLLATE utf8_bin DEFAULT NULL,
`notice` varchar(255) COLLATE utf8_bin DEFAULT NULL,
`flag` varchar(255) COLLATE utf8_bin DEFAULT '0' COMMENT '是否通过',
`orders` int(255) DEFAULT '0' COMMENT '顺序',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=30 DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
-- ----------------------------
-- Records of li_links
-- ----------------------------
INSERT INTO `li_links` VALUES ('4', 'IM极客小站', 'www.imenger.cn', 'http://www.imenger.cn/images/favicon.ico', '1193288577', '', '0', '0');
INSERT INTO `li_links` VALUES ('5', 'Magento', 'www.maijindou.com', 'http://www.maijindou.com/sites/all/themes/blend/logo.png', '936100558', '', '0', '0');
INSERT INTO `li_links` VALUES ('13', '前端汇', 'https://www.jsout.com', '', '', null, '1', '0');
INSERT INTO `li_links` VALUES ('16', 'Yii中文网', 'http://www.yii-china.com/', '', '', null, '1', '1');
INSERT INTO `li_links` VALUES ('17', 'laravel学院', 'http://laravelacademy.org/', '', '', null, '1', '2');
INSERT INTO `li_links` VALUES ('18', 'wangEditor', 'http://wangeditor.github.io/', '', '', null, '1', '3');
INSERT INTO `li_links` VALUES ('19', 'Drupal中国', 'http://www.drupalchina.cn/', '', '', null, '1', '4');
INSERT INTO `li_links` VALUES ('20', '麦锐尔', 'http://www.mairuier.com/', '', '', null, '1', '5');
INSERT INTO `li_links` VALUES ('21', '百思伯乐', 'http://blog.bestbole.com/', '', '', null, '1', '6');
INSERT INTO `li_links` VALUES ('22', '爱圈快站', 'http://iquan.kuaizhan.com/', '', '', null, '1', '7');
INSERT INTO `li_links` VALUES ('23', 'IM极客小站', 'http://www.imenger.cn/', '', '', null, '1', '8');
INSERT INTO `li_links` VALUES ('24', '知识林', 'http://www.zslin.com/', '', '', null, '1', '9');
INSERT INTO `li_links` VALUES ('25', 'I/OTechie技术社区', 'http://iotechie.info/', '', '', null, '1', '10');
INSERT INTO `li_links` VALUES ('26', '一起开源', 'http://www.17ky.net/', '', '', null, '1', '11');
INSERT INTO `li_links` VALUES ('27', '猿天地', 'http://www.cxytiandi.com/', '', '', null, '1', '12');
INSERT INTO `li_links` VALUES ('28', 'Falost的小窝', 'http://www.fedte.cc/', '', '', null, '1', '13');
INSERT INTO `li_links` VALUES ('29', '小松博客', 'https://www.phpsong.com/', '', '', null, '1', '14');
-- ----------------------------
-- Table structure for li_manage_permission
-- ----------------------------
DROP TABLE IF EXISTS `li_manage_permission`;
CREATE TABLE `li_manage_permission` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`pername` varchar(255) DEFAULT NULL,
`permission` text,
`tag` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=70 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of li_manage_permission
-- ----------------------------
INSERT INTO `li_manage_permission` VALUES ('1', '新建/编辑文章', 'admin/content/article', '1');
INSERT INTO `li_manage_permission` VALUES ('2', '添加markdown', 'admin/content/addmarkdown', '1');
INSERT INTO `li_manage_permission` VALUES ('3', '文章列表', 'admin/content/index', '1');
INSERT INTO `li_manage_permission` VALUES ('7', '后台首页', 'admin/index/index', '4');
INSERT INTO `li_manage_permission` VALUES ('8', '后台欢迎页', 'admin/index/welcome', '4');
INSERT INTO `li_manage_permission` VALUES ('9', '草稿箱文章列表', 'admin/content/draftlist', '1');
INSERT INTO `li_manage_permission` VALUES ('10', '新增/编辑文章提交接口', 'admin/content/doadd', '1');
INSERT INTO `li_manage_permission` VALUES ('11', '草稿箱发布接口', 'admin/content/updatestatus', '1');
INSERT INTO `li_manage_permission` VALUES ('12', '删除/批量删除文章接口', 'admin/content/delsome', '1');
INSERT INTO `li_manage_permission` VALUES ('13', '上传文章缩略图接口', 'admin/content/upload', '1');
INSERT INTO `li_manage_permission` VALUES ('14', '上传编辑器图片接口', 'admin/content/uploadeditor', '1');
INSERT INTO `li_manage_permission` VALUES ('15', '上传markdown文件及解析接口/内容分页', 'admin/content/uploadfile', '1');
INSERT INTO `li_manage_permission` VALUES ('16', '留言列表', 'admin/guest/index', '5');
INSERT INTO `li_manage_permission` VALUES ('17', '删除/批量留言接口', 'admin/guest/delsome', '5');
INSERT INTO `li_manage_permission` VALUES ('18', '退出后台接口', 'admin/index/logout', '4');
INSERT INTO `li_manage_permission` VALUES ('19', '栏目列表', 'admin/item/index', '6');
INSERT INTO `li_manage_permission` VALUES ('20', '新增/编辑栏目', 'admin/item/item', '6');
INSERT INTO `li_manage_permission` VALUES ('21', '新增/编辑栏目接口', 'admin/item/save', '6');
INSERT INTO `li_manage_permission` VALUES ('22', '删除/批量删除栏目接口', 'admin/item/delsome', '6');
INSERT INTO `li_manage_permission` VALUES ('23', '申请列表', 'admin/links/index', '14');
INSERT INTO `li_manage_permission` VALUES ('24', '删除/批量删除友情链接接口', 'admin/links/delsome', '14');
INSERT INTO `li_manage_permission` VALUES ('25', '导航列表', 'admin/menu/index', '2');
INSERT INTO `li_manage_permission` VALUES ('26', '新增/编辑导航', 'admin/menu/item', '2');
INSERT INTO `li_manage_permission` VALUES ('27', '编辑/新增导航接口', 'admin/menu/save', '2');
INSERT INTO `li_manage_permission` VALUES ('28', '删除/批量删除导航接口', 'admin/menu/delsome', '2');
INSERT INTO `li_manage_permission` VALUES ('29', '权限列表', 'admin/permission/index', '10');
INSERT INTO `li_manage_permission` VALUES ('30', '新增/编辑权限', 'admin/permission/item', '10');
INSERT INTO `li_manage_permission` VALUES ('31', '编辑/新增权限接口', 'admin/permission/save', '10');
INSERT INTO `li_manage_permission` VALUES ('32', '删除/批量删除权限接口', 'admin/permission/delsome', '10');
INSERT INTO `li_manage_permission` VALUES ('33', '权限分类列表', 'admin/pertag/index', '10');
INSERT INTO `li_manage_permission` VALUES ('34', '新增/编辑权限分类', 'admin/pertag/item', '10');
INSERT INTO `li_manage_permission` VALUES ('35', '编辑/新增权限分类接口', 'admin/pertag/save', '10');
INSERT INTO `li_manage_permission` VALUES ('36', '删除/批量删除权限分类接口', 'admin/pertag/delsome', '10');
INSERT INTO `li_manage_permission` VALUES ('37', '角色列表', 'admin/role/index', '10');
INSERT INTO `li_manage_permission` VALUES ('38', '新增/编辑角色', 'admin/role/item', '10');
INSERT INTO `li_manage_permission` VALUES ('39', '新增/编辑角色接口', 'admin/role/save', '10');
INSERT INTO `li_manage_permission` VALUES ('40', '删除/批量删除角色接口', 'admin/role/delsome', '10');
INSERT INTO `li_manage_permission` VALUES ('41', '角色分配权限页', 'admin/role/perlist', '10');
INSERT INTO `li_manage_permission` VALUES ('42', '角色分配权限保存接口', 'admin/role/rolesave', '10');
INSERT INTO `li_manage_permission` VALUES ('43', '标签列表', 'admin/tag/index', '9');
INSERT INTO `li_manage_permission` VALUES ('44', '新增/编辑标签', 'admin/tag/item', '9');
INSERT INTO `li_manage_permission` VALUES ('45', '新增/编辑标签接口', 'admin/tag/save', '9');
INSERT INTO `li_manage_permission` VALUES ('46', '删除/批量删除标签接口', 'admin/tag/delsome', '9');
INSERT INTO `li_manage_permission` VALUES ('47', '用户列表', 'admin/user/index', '8');
INSERT INTO `li_manage_permission` VALUES ('48', '新增/编辑用户', 'admin/user/item', '8');
INSERT INTO `li_manage_permission` VALUES ('49', '新增/编辑用户接口', 'admin/user/save', '8');
INSERT INTO `li_manage_permission` VALUES ('50', '删除/批量删除用户接口', 'admin/user/delsome', '8');
INSERT INTO `li_manage_permission` VALUES ('51', '常规设置', 'admin/system/index', '11');
INSERT INTO `li_manage_permission` VALUES ('52', '常规设置保存接口', 'admin/system/edit', '11');
INSERT INTO `li_manage_permission` VALUES ('53', '评论设置', 'admin/system/setcomment', '11');
INSERT INTO `li_manage_permission` VALUES ('54', '评论设置保存接口', 'admin/system/commentedit', '11');
INSERT INTO `li_manage_permission` VALUES ('55', '管理员列表', 'admin/user/adminlist', '8');
INSERT INTO `li_manage_permission` VALUES ('56', '评论列表', 'admin/comment/index', '12');
INSERT INTO `li_manage_permission` VALUES ('57', '举报列表', 'admin/comment/tiplist', '12');
INSERT INTO `li_manage_permission` VALUES ('58', '主题列表', 'admin/topic/index', '13');
INSERT INTO `li_manage_permission` VALUES ('59', '显示/隐藏主题', 'admin/topic/update', '13');
INSERT INTO `li_manage_permission` VALUES ('60', '主题标签管理', 'admin/topictag/index', '13');
INSERT INTO `li_manage_permission` VALUES ('61', '新增/编辑主题分类', 'admin/topictag/item', '13');
INSERT INTO `li_manage_permission` VALUES ('62', '新增/编辑主题分类接口', 'admin/topictag/save', '13');
INSERT INTO `li_manage_permission` VALUES ('63', '删除/批量删除主题标签接口', 'admin/topictag/delsome', '13');
INSERT INTO `li_manage_permission` VALUES ('64', '链接管理', 'admin/links/list', '14');
INSERT INTO `li_manage_permission` VALUES ('65', '新增/编辑友情链接 ', 'admin/links/item', '14');
INSERT INTO `li_manage_permission` VALUES ('66', '新增/编辑友情链接接口', 'admin/links/save', '14');
INSERT INTO `li_manage_permission` VALUES ('67', '显示/隐藏友情链接接口', 'admin/links/upstatus', '14');
INSERT INTO `li_manage_permission` VALUES ('68', '设置logo', 'admin/system/setlogo', '11');
INSERT INTO `li_manage_permission` VALUES ('69', '上传logo接口', 'admin/system/upload', '11');
-- ----------------------------
-- Table structure for li_manage_role
-- ----------------------------
DROP TABLE IF EXISTS `li_manage_role`;
CREATE TABLE `li_manage_role` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`role` varchar(255) DEFAULT NULL,
`rolename` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,
`pid` text,
`permission` longtext CHARACTER SET utf8 COLLATE utf8_bin,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of li_manage_role
-- ----------------------------
INSERT INTO `li_manage_role` VALUES ('1', 'superadmin', '超级管理员', '1,2,3,9,10,11,12,13,14,15,25,26,27,28,7,8,18,16,17,19,20,21,22,47,48,49,50,55,43,44,45,46,29,30,31,32,33,34,35,36,37,38,39,40,41,42,51,52,53,54,68,69,56,57,58,59,60,61,62,63,23,24,64,65,66,67', 0x61646D696E2F636F6E74656E742F61727469636C652C61646D696E2F636F6E74656E742F6164646D61726B646F776E2C61646D696E2F636F6E74656E742F696E6465782C61646D696E2F636F6E74656E742F64726166746C6973742C61646D696E2F636F6E74656E742F646F6164642C61646D696E2F636F6E74656E742F7570646174657374617475732C61646D696E2F636F6E74656E742F64656C736F6D652C61646D696E2F636F6E74656E742F75706C6F61642C61646D696E2F636F6E74656E742F75706C6F6164656469746F722C61646D696E2F636F6E74656E742F75706C6F616466696C652C61646D696E2F6D656E752F696E6465782C61646D696E2F6D656E752F6974656D2C61646D696E2F6D656E752F736176652C61646D696E2F6D656E752F64656C736F6D652C61646D696E2F696E6465782F696E6465782C61646D696E2F696E6465782F77656C636F6D652C61646D696E2F696E6465782F6C6F676F75742C61646D696E2F67756573742F696E6465782C61646D696E2F67756573742F64656C736F6D652C61646D696E2F6974656D2F696E6465782C61646D696E2F6974656D2F6974656D2C61646D696E2F6974656D2F736176652C61646D696E2F6974656D2F64656C736F6D652C61646D696E2F757365722F696E6465782C61646D696E2F757365722F6974656D2C61646D696E2F757365722F736176652C61646D696E2F757365722F64656C736F6D652C61646D696E2F757365722F61646D696E6C6973742C61646D696E2F7461672F696E6465782C61646D696E2F7461672F6974656D2C61646D696E2F7461672F736176652C61646D696E2F7461672F64656C736F6D652C61646D696E2F7065726D697373696F6E2F696E6465782C61646D696E2F7065726D697373696F6E2F6974656D2C61646D696E2F7065726D697373696F6E2F736176652C61646D696E2F7065726D697373696F6E2F64656C736F6D652C61646D696E2F7065727461672F696E6465782C61646D696E2F7065727461672F6974656D2C61646D696E2F7065727461672F736176652C61646D696E2F7065727461672F64656C736F6D652C61646D696E2F726F6C652F696E6465782C61646D696E2F726F6C652F6974656D2C61646D696E2F726F6C652F736176652C61646D696E2F726F6C652F64656C736F6D652C61646D696E2F726F6C652F7065726C6973742C61646D696E2F726F6C652F726F6C65736176652C61646D696E2F73797374656D2F696E6465782C61646D696E2F73797374656D2F656469742C61646D696E2F73797374656D2F736574636F6D6D656E742C61646D696E2F73797374656D2F636F6D6D656E74656469742C61646D696E2F73797374656D2F7365746C6F676F2C61646D696E2F73797374656D2F75706C6F61642C61646D696E2F636F6D6D656E742F696E6465782C61646D696E2F636F6D6D656E742F7469706C6973742C61646D696E2F746F7069632F696E6465782C61646D696E2F746F7069632F7570646174652C61646D696E2F746F7069637461672F696E6465782C61646D696E2F746F7069637461672F6974656D2C61646D696E2F746F7069637461672F736176652C61646D696E2F746F7069637461672F64656C736F6D652C61646D696E2F6C696E6B732F696E6465782C61646D696E2F6C696E6B732F64656C736F6D652C61646D696E2F6C696E6B732F6C6973742C61646D696E2F6C696E6B732F6974656D2C61646D696E2F6C696E6B732F736176652C61646D696E2F6C696E6B732F7570737461747573);
INSERT INTO `li_manage_role` VALUES ('2', 'admin', '管理员', '1,2,3,9,10,11,12,13,14,15,25,26,27,28,7,8,18,16,17,19,20,21,22,23,24,47,43,44,45,46', 0x61646D696E2F636F6E74656E742F61727469636C652C61646D696E2F636F6E74656E742F6164646D61726B646F776E2C61646D696E2F636F6E74656E742F696E6465782C61646D696E2F636F6E74656E742F64726166746C6973742C61646D696E2F636F6E74656E742F646F6164642C61646D696E2F636F6E74656E742F7570646174657374617475732C61646D696E2F636F6E74656E742F64656C736F6D652C61646D696E2F636F6E74656E742F75706C6F61642C61646D696E2F636F6E74656E742F75706C6F6164656469746F722C61646D696E2F636F6E74656E742F75706C6F616466696C652C61646D696E2F6D656E752F696E6465782C61646D696E2F6D656E752F6974656D2C61646D696E2F6D656E752F736176652C61646D696E2F6D656E752F64656C736F6D652C61646D696E2F696E6465782F696E6465782C61646D696E2F696E6465782F77656C636F6D652C61646D696E2F696E6465782F6C6F676F75742C61646D696E2F67756573742F696E6465782C61646D696E2F67756573742F64656C736F6D652C61646D696E2F6974656D2F696E6465782C61646D696E2F6974656D2F6974656D2C61646D696E2F636F6E74656E742F736176652C61646D696E2F6974656D2F64656C736F6D652C61646D696E2F6C696E6B732F696E6465782C61646D696E2F6C696E6B732F64656C736F6D652C61646D696E2F757365722F696E6465782C61646D696E2F7461672F696E6465782C61646D696E2F7461672F6974656D2C61646D696E2F7461672F736176652C61646D696E2F7461672F64656C736F6D65);
INSERT INTO `li_manage_role` VALUES ('3', 'editor', '编辑', '1,2,3,9,10,11,12,13,14,15,7,8,18', 0x61646D696E2F636F6E74656E742F61727469636C652C61646D696E2F636F6E74656E742F6164646D61726B646F776E2C61646D696E2F636F6E74656E742F696E6465782C61646D696E2F636F6E74656E742F64726166746C6973742C61646D696E2F636F6E74656E742F646F6164642C61646D696E2F636F6E74656E742F7570646174657374617475732C61646D696E2F636F6E74656E742F64656C736F6D652C61646D696E2F636F6E74656E742F75706C6F61642C61646D696E2F636F6E74656E742F75706C6F6164656469746F722C61646D696E2F636F6E74656E742F75706C6F616466696C652C61646D696E2F696E6465782F696E6465782C61646D696E2F696E6465782F77656C636F6D652C61646D696E2F696E6465782F6C6F676F7574);
INSERT INTO `li_manage_role` VALUES ('4', 'visitor', '访客', '3,9,25,7,8,18,16,19,23,47,43', 0x61646D696E2F636F6E74656E742F696E6465782C61646D696E2F636F6E74656E742F64726166746C6973742C61646D696E2F6D656E752F696E6465782C61646D696E2F696E6465782F696E6465782C61646D696E2F696E6465782F77656C636F6D652C61646D696E2F696E6465782F6C6F676F75742C61646D696E2F67756573742F696E6465782C61646D696E2F6974656D2F696E6465782C61646D696E2F6C696E6B732F696E6465782C61646D696E2F757365722F696E6465782C61646D696E2F7461672F696E646578);
-- ----------------------------
-- Table structure for li_manage_tag
-- ----------------------------
DROP TABLE IF EXISTS `li_manage_tag`;
CREATE TABLE `li_manage_tag` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of li_manage_tag
-- ----------------------------
INSERT INTO `li_manage_tag` VALUES ('1', '文章管理');
INSERT INTO `li_manage_tag` VALUES ('2', '导航管理');
INSERT INTO `li_manage_tag` VALUES ('4', '基础页面');
INSERT INTO `li_manage_tag` VALUES ('5', '留言管理');
INSERT INTO `li_manage_tag` VALUES ('6', '栏目管理');
INSERT INTO `li_manage_tag` VALUES ('8', '用户管理');
INSERT INTO `li_manage_tag` VALUES ('9', '标签管理');
INSERT INTO `li_manage_tag` VALUES ('10', '权限管理');
INSERT INTO `li_manage_tag` VALUES ('11', '系统设置');
INSERT INTO `li_manage_tag` VALUES ('12', '评论管理');
INSERT INTO `li_manage_tag` VALUES ('13', '社区管理');
INSERT INTO `li_manage_tag` VALUES ('14', '友情链接');
-- ----------------------------
-- Table structure for li_menu
-- ----------------------------
DROP TABLE IF EXISTS `li_menu`;
CREATE TABLE `li_menu` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`menuname` varchar(255) CHARACTER SET utf8 DEFAULT NULL COMMENT '菜单名',
`url` varchar(255) CHARACTER SET utf8 DEFAULT NULL COMMENT '导航链接',
`info` varchar(255) CHARACTER SET utf8 DEFAULT NULL COMMENT '备注',
`appear` int(11) DEFAULT '1',
`orders` int(255) DEFAULT '0' COMMENT '排序',
`target` int(11) DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=latin1;
-- ----------------------------
-- Records of li_menu
-- ----------------------------
INSERT INTO `li_menu` VALUES ('1', '首页', '/', '', '1', '1', '1');
INSERT INTO `li_menu` VALUES ('2', '文档', '/doc.html', null, '0', '2', '0');
INSERT INTO `li_menu` VALUES ('8', '资源下载', '/download.html', null, '1', '3', '0');
INSERT INTO `li_menu` VALUES ('9', '活动', '/activity.html', null, '1', '4', '0');
INSERT INTO `li_menu` VALUES ('10', '社区', '/topic.html', null, '1', '5', '0');
INSERT INTO `li_menu` VALUES ('11', '招聘', '/topic/job.html', null, '1', '6', '0');
INSERT INTO `li_menu` VALUES ('14', '捐赠', '/donate.html', null, '1', '7', '0');
-- ----------------------------
-- Table structure for li_system
-- ----------------------------
DROP TABLE IF EXISTS `li_system`;
CREATE TABLE `li_system` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`sitename` varchar(255) DEFAULT NULL,
`url` varchar(255) DEFAULT NULL,
`keywords` varchar(255) DEFAULT NULL,
`description` varchar(255) DEFAULT NULL,
`author` char(50) DEFAULT NULL,
`copyright` varchar(255) DEFAULT NULL,
`links` text,
`allowcomment` int(11) DEFAULT '1',
`tongji` text,
`theme` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT 'default',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of li_system
-- ----------------------------
INSERT INTO `li_system` VALUES ('1', '里部落阁-liblog演示站点', '/', '前端汇,liblog,thinkjs,全栈开发', '里部落阁是liblog的演示站点,是基于thinkjs框架的nodejs博客系统,具备完善的轻量级网站后台,完全免费,简单灵活,兼容性好 让您快速搭建中小型网站、博客', '前端汇', '@2015 copyright', '<li><a href=\"http://www.jsout.com\" target=\"_blank\">前端汇</a></li>\n<li><a href=\"http://www.yii-china.com\" target=\"_blank\">Yii中文网</a></li>\n<li><a href=\"http://laravelacademy.org\" target=\"_blank\">laravel学院</a></li>\n<li><a href=\"http://wangeditor.github.io\" target=\"_blank\">wangEditor</a></li>\n<li><a href=\"http://www.drupalchina.cn\" target=\"_blank\">Drupal中国</a></li>\n<li><a href=\"http://www.mairuier.com/\" target=\"_blank\">麦锐尔</a></li>\n<li><a href=\"http://blog.bestbole.com\" target=\"_blank\">百思伯乐</a></li>\n<li><a href=\"http://iquan.kuaizhan.com\" target=\"_blank\">爱圈快站</a></li>\n<li><a href=\"http://www.imenger.cn\" target=\"_blank\">IM极客小站</a></li>\n<li><a href=\"http://www.zslin.com\" target=\"_blank\">知识林</a></li>\n<li><a href=\"http://iotechie.info\" target=\"_blank\">I/OTechie技术社区</a></li>\n<li><a href=\"http://www.17ky.net\" target=\"_blank\">一起开源</a></li>\n<li><a href=\"http://www.cxytiandi.com\" target=\"_blank\">猿天地</a></li>', '0', '<script>\n var _hmt = _hmt || [];\n (function() {\n var hm = document.createElement(\"script\");\n hm.src = \"//hm.baidu.com/hm.js?8c04fc9ebc7d286e7c03911b6affb970\";\n var s = document.getElementsByTagName(\"script\")[0];\n s.parentNode.insertBefore(hm, s);\n })();\n</script>', 'liblog');
-- ----------------------------
-- Table structure for li_system_comment
-- ----------------------------
DROP TABLE IF EXISTS `li_system_comment`;
CREATE TABLE `li_system_comment` (
`clientid` varchar(255) CHARACTER SET utf8 DEFAULT NULL,
`appcode` text CHARACTER SET utf8,
`appkey` varchar(255) CHARACTER SET utf8 DEFAULT NULL,
`appid` varchar(255) CHARACTER SET utf8 NOT NULL DEFAULT '',
`id` int(11) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`,`appid`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1;
-- ----------------------------
-- Records of li_system_comment
-- ----------------------------
INSERT INTO `li_system_comment` VALUES ('clientId', '<!--高速版-->\n<div id=\"SOHUCS\"></div>\n<script charset=\"utf-8\" type=\"text/javascript\" src=\"http://changyan.sohu.com/upload/changyan.js\" ></script>\n<script type=\"text/javascript\">\n window.changyan.api.config({\n appid: \'cysoRJNxO\',\n conf: \'prod_1fbcbcaf3589e576afe2785fe80d6684\'\n });\n</script>', 'ce46948884ce9504641c9c97d3171bce', 'cysoRJNxO', '1');
-- ----------------------------
-- Table structure for li_tags
-- ----------------------------
DROP TABLE IF EXISTS `li_tags`;
CREATE TABLE `li_tags` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`tagname` varchar(255) DEFAULT NULL,
`appear` int(11) DEFAULT '1',
`orders` int(255) DEFAULT '0' COMMENT '显示顺序',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=23 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of li_tags
-- ----------------------------
INSERT INTO `li_tags` VALUES ('1', 'web开发', '0', '0');
INSERT INTO `li_tags` VALUES ('2', 'thinkjs', '1', '1');
INSERT INTO `li_tags` VALUES ('3', 'nodejs', '1', '2');
INSERT INTO `li_tags` VALUES ('4', 'jquery', '0', '3');
INSERT INTO `li_tags` VALUES ('5', 'css3', '1', '4');
INSERT INTO `li_tags` VALUES ('6', 'css3+html5', '1', '5');
INSERT INTO `li_tags` VALUES ('7', 'javascript', '1', '6');
INSERT INTO `li_tags` VALUES ('8', 'html', '1', '7');
INSERT INTO `li_tags` VALUES ('9', '前端设计', '0', '0');
INSERT INTO `li_tags` VALUES ('10', 'fis', '0', '0');
INSERT INTO `li_tags` VALUES ('11', 'grunt', '0', '0');
INSERT INTO `li_tags` VALUES ('12', 'vscode', '0', '0');
INSERT INTO `li_tags` VALUES ('13', '前端工具', '1', '8');
INSERT INTO `li_tags` VALUES ('14', '活动', '1', '9');
INSERT INTO `li_tags` VALUES ('15', '招聘', '1', '10');
INSERT INTO `li_tags` VALUES ('16', 'nginx', '0', '0');
INSERT INTO `li_tags` VALUES ('17', 'liblog', '1', '11');
INSERT INTO `li_tags` VALUES ('18', 'thinkjs2.1', '1', '12');
INSERT INTO `li_tags` VALUES ('19', 'angular', '1', '13');
INSERT INTO `li_tags` VALUES ('20', 'react', '1', '14');
INSERT INTO `li_tags` VALUES ('21', 'wangeditor', '1', '15');
INSERT INTO `li_tags` VALUES ('22', 'numjucks', '1', '16');
-- ----------------------------
-- Table structure for li_topic
-- ----------------------------
DROP TABLE IF EXISTS `li_topic`;
CREATE TABLE `li_topic` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`item` varchar(255) DEFAULT NULL,
`content` longtext CHARACTER SET utf8 COLLATE utf8_bin COMMENT '内容',
`author` varchar(255) DEFAULT NULL,
`createtime` datetime DEFAULT NULL,
`title` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL COMMENT '标题',
`view` bigint(20) DEFAULT '1' COMMENT '点击数',
`updatetime` datetime DEFAULT NULL,
`updateauthor` varchar(255) DEFAULT NULL,
`updatepic` varchar(255) DEFAULT NULL,
`replycount` int(11) DEFAULT '0' COMMENT '回复数',
`show` smallint(255) DEFAULT '1' COMMENT '是否显示',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=36 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of li_topic
-- ----------------------------
INSERT INTO `li_topic` VALUES ('31', 'ask', 0x3C703EE59CA8E5819AE799BBE99986E9A1B5E79A84E9AA8CE8AF81E7A081E58A9FE883BDEFBC8CE4B98BE5898DE6B2A1E69C89E794A86E6F6465E5819AE8BF87EFBC8CE69C89E4BB80E4B988E58A9EE6B395E4B988EFBC9F3C2F703E, 'livi', '2016-09-02 15:52:44', '新手求解,thinkjs如何生成随机验证码,并返回至前台?', '193', '2016-09-02 16:30:43', 'livisky', 'common/images/pic/avatar_7.jpg', '4', '0');
INSERT INTO `li_topic` VALUES ('32', 'thinkjs', 0x3C703EE4BDBFE794A8E58886E7BB84E69FA5E8AFA2E5B9B6E5B8A6E69C89E58886E9A1B5E69FA5E8AFA2EFBC8CE7BB93E69E9CE5BE97E588B0E79A84E7BB93E69E9CE99B86E4B8ADE58886E9A1B5E4BFA1E681AFE698AFE99499E8AFAFE79A84EFBC8CE8AFB7E997AEE698AFE4B88DE694AFE68C81E58886E7BB84E5908EE59CA8E58886E9A1B5E69FA5E8AFA2E4B988EFBC8CE8BF98E698AFE58699E6B395E79A84E997AEE9A298EFBC9FE4BBA5E4B88BE698AFE4BBA3E7A081E5928CE7BB93E69E9CE688AAE59BBEEFBC9A3C696D67207372633D222F7374617469632F75706C6F61642F706963732F392F392F3230313663514838696A714653344842395A315350736130617441742E706E672220616C743D225151E688AAE59BBE323031363039303931303432343622207374796C653D226D61782D77696474683A20313030253B223E3C696D67207372633D222F7374617469632F75706C6F61642F706963732F392F392F32303136394947696A674F41717A527A6C6A614A474536714D427A622E706E672220616C743D22E7BB93E69E9C22207374796C653D226D61782D77696474683A20313030253B223E3C2F703E3C703E3C62723E3C2F703E, 'livi', '2016-09-09 10:45:05', '分页问题', '83', '2016-09-12 10:25:32', 'livi', 'common/images/pic/avatar_2.jpg', '1', '1');
INSERT INTO `li_topic` VALUES ('33', 'liblog', 0x3C703EE6B7BBE58AA0E69687E7ABA0E58685E5AEB9E8B083E794A8E6A087E7ADBEEFBC8CE9809AE8BF87E6A087E7ADBEE9858DE7BDAEE58DB3E58FAFE5AE9EE78EB0E58685E5AEB9E79A84E8AFBBE58F963C2F703E3C703EE5A682EFBC9AE88EB7E58F96E7BDAEE9A1B6E79A8436E69DA1E69687E7ABA0E58897E8A1A83C2F703E3C707265207374796C653D226D61782D77696474683A20313030253B223E3C636F646520636C6173733D226A61766173637269707420686C6A732220636F64656D61726B3D2231223E7B252061727469636C652064617461203D203C7370616E20636C6173733D22686C6A732D737472696E67223E22746F704C697374223C2F7370616E3E2C6C696D69743D203C7370616E20636C6173733D22686C6A732D737472696E67223E2236223C2F7370616E3E2C666C61673D3C7370616E20636C6173733D22686C6A732D737472696E67223E22746F746F70223C2F7370616E3E257D3C2F636F64653E3C2F7072653E3C703EE784B6E5908EE5868DE5BEAAE78EAFE587BAE69DA53C2F703E3C707265207374796C653D226D61782D77696474683A20313030253B223E3C636F646520636C6173733D226A61766173637269707420686C6A732220636F64656D61726B3D2231223E7B252061727469636C652064617461203D203C7370616E20636C6173733D22686C6A732D737472696E67223E22746F704C697374223C2F7370616E3E2C6C696D69743D203C7370616E20636C6173733D22686C6A732D737472696E67223E2236223C2F7370616E3E2C666C61673D3C7370616E20636C6173733D22686C6A732D737472696E67223E22746F746F70223C2F7370616E3E257D0A2020202020207B25203C7370616E20636C6173733D22686C6A732D6B6579776F7264223E666F723C2F7370616E3E206974656D203C7370616E20636C6173733D22686C6A732D6B6579776F7264223E696E3C2F7370616E3E20746F704C69737420257D0A202020202020266C743B646976203C7370616E20636C6173733D22686C6A732D636C617373223E3C7370616E20636C6173733D22686C6A732D6B6579776F7264223E636C6173733C2F7370616E3E3C2F7370616E3E3D3C7370616E20636C6173733D22686C6A732D737472696E67223E22626C6F6773223C2F7370616E3E2667743B0A2020202020202020203C7370616E20636C6173733D22786D6C223E3C7370616E20636C6173733D22686C6A732D746167223E266C743B3C7370616E20636C6173733D22686C6A732D6E616D65223E6669677572653C2F7370616E3E2667743B3C2F7370616E3E0A2020202020202020203C7370616E20636C6173733D22686C6A732D746167223E266C743B3C7370616E20636C6173733D22686C6A732D6E616D65223E613C2F7370616E3E203C7370616E20636C6173733D22686C6A732D61747472223E687265663C2F7370616E3E3D3C7370616E20636C6173733D22686C6A732D737472696E67223E222F706167652F7B7B6974656D2E6169647D7D2E68746D6C223C2F7370616E3E203C7370616E20636C6173733D22686C6A732D61747472223E7461726765743C2F7370616E3E3D3C7370616E20636C6173733D22686C6A732D737472696E67223E225F626C616E6B223C2F7370616E3E2667743B3C2F7370616E3E3C7370616E20636C6173733D22686C6A732D746167223E266C743B3C7370616E20636C6173733D22686C6A732D6E616D65223E696D673C2F7370616E3E203C7370616E20636C6173733D22686C6A732D61747472223E7372633C2F7370616E3E3D3C7370616E20636C6173733D22686C6A732D737472696E67223E227B7B5F7765622E75726C7D7D7B7B277374617469632F636F6D6D6F6E2F696D616765732F636F6D6D6F6E2F64656661756C742E6A706727206966206974656D2E70696375726C3D3D272720656C7365206974656D2E70696375726C207D7D223C2F7370616E3E203C7370616E20636C6173733D22686C6A732D61747472223E7469746C653C2F7370616E3E3D3C7370616E20636C6173733D22686C6A732D737472696E67223E227B7B6974656D2E7469746C657D7D223C2F7370616E3E2667743B3C2F7370616E3E3C7370616E20636C6173733D22686C6A732D746167223E266C743B2F3C7370616E20636C6173733D22686C6A732D6E616D65223E613C2F7370616E3E2667743B3C2F7370616E3E0A202020202020202020203C7370616E20636C6173733D22686C6A732D746167223E266C743B2F3C7370616E20636C6173733D22686C6A732D6E616D65223E6669677572653C2F7370616E3E2667743B3C2F7370616E3E0A202020202020202020203C7370616E20636C6173733D22686C6A732D746167223E266C743B3C7370616E20636C6173733D22686C6A732D6E616D65223E756C3C2F7370616E3E203C7370616E20636C6173733D22686C6A732D61747472223E636C6173733C2F7370616E3E3D3C7370616E20636C6173733D22686C6A732D737472696E67223E2261727469636C65756C223C2F7370616E3E2667743B3C2F7370616E3E0A202020202020202020203C7370616E20636C6173733D22686C6A732D746167223E266C743B3C7370616E20636C6173733D22686C6A732D6E616D65223E68333C2F7370616E3E2667743B3C2F7370616E3E3C7370616E20636C6173733D22686C6A732D746167223E266C743B3C7370616E20636C6173733D22686C6A732D6E616D65223E613C2F7370616E3E203C7370616E20636C6173733D22686C6A732D61747472223E687265663C2F7370616E3E3D3C7370616E20636C6173733D22686C6A732D737472696E67223E222F706167652F7B7B6974656D2E6169647D7D2E68746D6C223C2F7370616E3E203C7370616E20636C6173733D22686C6A732D61747472223E7461726765743C2F7370616E3E3D3C7370616E20636C6173733D22686C6A732D737472696E67223E225F626C616E6B223C2F7370616E3E2667743B3C2F7370616E3E7B7B6974656D2E7469746C657D7D3C7370616E20636C6173733D22686C6A732D746167223E266C743B2F3C7370616E20636C6173733D22686C6A732D6E616D65223E613C2F7370616E3E2667743B3C2F7370616E3E3C7370616E20636C6173733D22686C6A732D746167223E266C743B2F3C7370616E20636C6173733D22686C6A732D6E616D65223E68333C2F7370616E3E2667743B3C2F7370616E3E0A20202020202020202020203C7370616E20636C6173733D22686C6A732D746167223E266C743B3C7370616E20636C6173733D22686C6A732D6E616D65223E703C2F7370616E3E2667743B3C2F7370616E3E7B7B6974656D2E61627374726163747D7D3C7370616E20636C6173733D22686C6A732D746167223E266C743B2F3C7370616E20636C6173733D22686C6A732D6E616D65223E703C2F7370616E3E2667743B3C2F7370616E3E0A202020202020202020203C7370616E20636C6173733D22686C6A732D746167223E266C743B3C7370616E20636C6173733D22686C6A732D6E616D65223E6469763C2F7370616E3E203C7370616E20636C6173733D22686C6A732D61747472223E636C6173733C2F7370616E3E3D3C7370616E20636C6173733D22686C6A732D737472696E67223E22617574686F72223C2F7370616E3E2667743B3C2F7370616E3E0A20202020202020202020203C7370616E20636C6173733D22686C6A732D746167223E266C743B3C7370616E20636C6173733D22686C6A732D6E616D65223E7370616E3C2F7370616E3E203C7370616E20636C6173733D22686C6A732D61747472223E636C6173733C2F7370616E3E3D3C7370616E20636C6173733D22686C6A732D737472696E67223E226C6D20665F6C223C2F7370616E3E2667743B3C2F7370616E3E0A202020202020202020202020203C7370616E20636C6173733D22686C6A732D746167223E266C743B3C7370616E20636C6173733D22686C6A732D6E616D65223E613C2F7370616E3E203C7370616E20636C6173733D22686C6A732D61747472223E687265663C2F7370616E3E3D3C7370616E20636C6173733D22686C6A732D737472696E67223E226A6176617363726970743A766F69642830293B223C2F7370616E3E2667743B3C2F7370616E3E7B7B6974656D2E7461676E616D657D7D3C7370616E20636C6173733D22686C6A732D746167223E266C743B2F3C7370616E20636C6173733D22686C6A732D6E616D65223E613C2F7370616E3E2667743B3C2F7370616E3E0A2020202020202020202020203C7370616E20636C6173733D22686C6A732D746167223E266C743B2F3C7370616E20636C6173733D22686C6A732D6E616D65223E7370616E3C2F7370616E3E2667743B3C2F7370616E3E0A2020202020202020202020203C7370616E20636C6173733D22686C6A732D746167223E266C743B3C7370616E20636C6173733D22686C6A732D6E616D65223E7370616E3C2F7370616E3E203C7370616E20636C6173733D22686C6A732D61747472223E636C6173733C2F7370616E3E3D3C7370616E20636C6173733D22686C6A732D737472696E67223E226474696D6520665F6C223C2F7370616E3E2667743B3C2F7370616E3E7B7B7468696E6B2E6461746574696D65286974656D2E63726561746574696D652C22595959592D4D4D2D444422297D7D3C7370616E20636C6173733D22686C6A732D746167223E266C743B2F3C7370616E20636C6173733D22686C6A732D6E616D65223E7370616E3C2F7370616E3E2667743B3C2F7370616E3E0A20202020202020202020203C7370616E20636C6173733D22686C6A732D746167223E266C743B2F3C7370616E20636C6173733D22686C6A732D6E616D65223E6469763C2F7370616E3E2667743B3C2F7370616E3E0A20202020202020202020202020202020202020202020203C7370616E20636C6173733D22686C6A732D746167223E266C743B3C7370616E20636C6173733D22686C6A732D6E616D65223E6469763C2F7370616E3E203C7370616E20636C6173733D22686C6A732D61747472223E636C6173733C2F7370616E3E3D3C7370616E20636C6173733D22686C6A732D737472696E67223E22617574686F72223C2F7370616E3E2667743B3C2F7370616E3E0A20202020202020202020202020202020202020202020202020203C7370616E20636C6173733D22686C6A732D746167223E266C743B3C7370616E20636C6173733D22686C6A732D6E616D65223E7370616E3C2F7370616E3E203C7370616E20636C6173733D22686C6A732D61747472223E636C6173733C2F7370616E3E3D3C7370616E20636C6173733D22686C6A732D737472696E67223E22766965776E756D20665F72223C2F7370616E3E2667743B3C2F7370616E3E0A20202020202020202020202020E6B58FE8A788EFBC887B7B6974656D2E766965777D7DEFBC893C7370616E20636C6173733D22686C6A732D746167223E266C743B2F3C7370616E20636C6173733D22686C6A732D6E616D65223E7370616E3C2F7370616E3E2667743B3C2F7370616E3E0A2020202020202020202020203C7370616E20636C6173733D22686C6A732D746167223E266C743B3C7370616E20636C6173733D22686C6A732D6E616D65223E7370616E3C2F7370616E3E203C7370616E20636C6173733D22686C6A732D61747472223E636C6173733C2F7370616E3E3D3C7370616E20636C6173733D22686C6A732D737472696E67223E2270696E676C20665F72223C2F7370616E3E2667743B3C2F7370616E3E0A20202020202020202020202020E8AF84E8AEBAEFBC883C7370616E20636C6173733D22686C6A732D746167223E266C743B3C7370616E20636C6173733D22686C6A732D6E616D65223E7370616E3C2F7370616E3E203C7370616E20636C6173733D22686C6A732D61747472223E69643C2F7370616E3E3D3C7370616E20636C6173733D22686C6A732D737472696E67223E22736F7572636549643A3A7B7B6974656D2E6169647D7D223C2F7370616E3E203C7370616E20636C6173733D22686C6A732D61747472223E636C6173733C2F7370616E3E3D3C7370616E20636C6173733D22686C6A732D737472696E67223E2263795F636D745F636F756E74223C2F7370616E3E203C7370616E20636C6173733D22686C6A732D61747472223E7374796C653C2F7370616E3E3D3C7370616E20636C6173733D22686C6A732D737472696E67223E226D617267696E3A203070783B70616464696E673A20307078223C2F7370616E3E2667743B3C2F7370616E3E3C7370616E20636C6173733D22686C6A732D746167223E266C743B2F3C7370616E20636C6173733D22686C6A732D6E616D65223E7370616E3C2F7370616E3E2667743B3C2F7370616E3EEFBC890A2020202020202020202020203C7370616E20636C6173733D22686C6A732D746167223E266C743B2F3C7370616E20636C6173733D22686C6A732D6E616D65223E7370616E3C2F7370616E3E2667743B3C2F7370616E3E0A20202020202020202020202020202020202020202020203C7370616E20636C6173733D22686C6A732D746167223E266C743B2F3C7370616E20636C6173733D22686C6A732D6E616D65223E6469763C2F7370616E3E2667743B3C2F7370616E3E0A202020202020202020203C7370616E20636C6173733D22686C6A732D746167223E266C743B2F3C7370616E20636C6173733D22686C6A732D6E616D65223E756C3C2F7370616E3E2667743B3C2F7370616E3E0A20202020202020203C7370616E20636C6173733D22686C6A732D746167223E266C743B2F3C7370616E20636C6173733D22686C6A732D6E616D65223E6469763C2F7370616E3E2667743B3C2F7370616E3E3C2F7370616E3E0A207B2520656E64666F7220257D3C2F636F64653E3C2F7072653E3C703EE585B6E4BB96E6A087E7ADBEE5A682EFBC9A3C2F703E3C707265207374796C653D226D61782D77696474683A20313030253B223E3C636F646520636C6173733D226A61766173637269707420686C6A732220636F64656D61726B3D2231223EE7AB99E995BFE68EA8E88D900A0A7B252061727469636C652064617461203D203C7370616E20636C6173733D22686C6A732D737472696E67223E22746F7265636F6D4C697374223C2F7370616E3E2C6C696D69743D203C7370616E20636C6173733D22686C6A732D737472696E67223E2236223C2F7370616E3E2C666C61673D3C7370616E20636C6173733D22686C6A732D737472696E67223E22746F7265636F6D223C2F7370616E3E257D0A0AE59BBEE69687E68EA8E88D900A0A7B252061727469636C652064617461203D203C7370616E20636C6173733D22686C6A732D737472696E67223E227069637265636F6D4C697374223C2F7370616E3E2C6C696D69743D203C7370616E20636C6173733D22686C6A732D737472696E67223E2236223C2F7370616E3E2C666C61673D3C7370616E20636C6173733D22686C6A732D737472696E67223E22746F7069637265636F6D223C2F7370616E3E257D3C2F636F64653E3C2F7072653E3C703E3C623E6C69626C6F67322E30E5B086E4BA8E39E69C88E5BA95E69BB4E696B0E588B067697468756220E5B9B6E5BC80E6BA90EFBC8C3C2F623E3C2F703E3C703E3C623EE6ACA2E8BF8EE585B3E6B3A8EFBC813C2F623E3C2F703E3C703E3C62723E3C2F703E, 'livi', '2016-09-14 15:38:28', 'liblog v2.0功能更新(2016/9/14)', '49', '2016-09-14 15:38:28', 'livisky', 'common/images/pic/avatar_13.jpg', '0', '1');
INSERT INTO `li_topic` VALUES ('34', 'liblog', 0x3C703EE4BFAEE694B9E6B3A8E5868CE58F8AE799BBE99986E79A84E9AA8CE8AF81E7A081E9AA8CE8AF81E696B9E5BC8F3C2F703E3C703EE58E9FE59BA0E69C8933E782B9EFBC9A3C2F703E3C703E31E3808177696E646F7773E4B88BEFBC8CE58E9FE585886C69626C6F67E79A84E9AA8CE8AF81E7A081E6A8A1E59D973C7370616E207374796C653D226C696E652D6865696768743A20312E383B223E63636170E5AE89E8A385E88081E698AFE587BAE997AEE9A298E68896E4B88DE5AE8CE695B4286C696E7578E7AD89E585B6E4BB96E6938DE4BD9CE7B3BBE7BB9FE58FAFE4BBA5E5AE8CE695B4E5AE89E8A38529EFBC8CE88083E89991E983A8E5888677696E646F77E794A8E688B7E79A84E5AE89E8A385E58F8AE6B58BE8AF95E380823C2F7370616E3E3C2F703E3C703E3C7370616E207374796C653D226C696E652D6865696768743A20312E383B223E32E38081E9AA8CE8AF81E7A081E5AD97E6AF8DE58AA0E695B0E5AD97E6A8A1E5BC8FE8BE83E4B8BAE4BCA0E7BB9FEFBC8CE6B2A1E69C89E59BBEE78987E68B96E58AA8E7AE80E58D95E38081E696B9E4BEBFE38081E7BE8EE8A7823C2F7370616E3E3C2F703E3C703E3C7370616E207374796C653D226C696E652D6865696768743A20312E383B223E33E38081E58E9FE58588E79A84E6A8A1E59D97E6B2A1E69C89E58F8AE697B6E58F8AE995BFE69C9FE7BBB4E68AA43C2F7370616E3E3C2F703E3C703E3C7370616E207374796C653D226C696E652D6865696768743A20312E383B223EE78EB0E59CA8E79A84E9AA8CE8AF81E7A081E4B8BAE69E81E9AA8C67656574657374E6A8A1E59D97EFBC8CE8AFA6E8A781E5AE98E7BD91EFBC9A687474703A2F2F3C2F7370616E3E3C7370616E207374796C653D226C696E652D6865696768743A20312E383B223E7777772E676565746573742E636F6D3C2F7370616E3E3C2F703E3C703E3C7370616E207374796C653D226C696E652D6865696768743A20312E383B223EE58887E68DA2E5908EE79A84E9AA8CE8AF81E7A081E696B9E5BC8FE69588E69E9CE5A682E4B88BE59BBEEFBC9A3C2F7370616E3E3C2F703E3C703E3C696D67207372633D222F7374617469632F75706C6F61642F706963732F392F31382F3230313666394241517A77577A52637573514E4F63363941424568742E706E672220616C743D223122207374796C653D226D61782D77696474683A313030253B223E3C7370616E207374796C653D226C696E652D6865696768743A20312E383B223E3C62723E3C2F7370616E3E3C2F703E3C703EE782B9E587BBE5B9B6E68B96E58AA8E68C89E992AEEFBC8CE69588E69E9CE5A682E4B88BEFBC9A3C2F703E3C703E3C696D67207372633D222F7374617469632F75706C6F61642F706963732F392F31382F32303136324C553036544B6F505150476B736F367869504F6D424B342E706E672220616C743D223222207374796C653D226D61782D77696474683A313030253B223E3C62723E3C2F703E3C703E3C7370616E207374796C653D226C696E652D6865696768743A20312E383B223EE69E81E9AA8CE99C80E8A681E5AE98E7BD91E6B3A8E5868CE5B9B6E88EB7E58F966964E5928C6B6579EFBC8CE8BF9BE8A18CE9858DE7BDAEE380823C2F7370616E3E3C62723E3C2F703E3C703EE585B7E4BD93E69599E7A88BE5908EE7BBADE69BB4E696B0E380823C2F703E3C703E3C623E6C69626C6F67322E30E5B086E4BA8E39E69C88E5BA95E69BB4E696B0E588B067697468756220E5B9B6E5BC80E6BA90EFBC8C3C2F623E3C2F703E3C703E3C2F703E3C703E3C623EE6ACA2E8BF8EE585B3E6B3A8EFBC813C2F623E3C2F703E3C703E3C62723E3C2F703E, 'livi', '2016-09-18 17:47:47', 'liblog v2.0功能更新(2016/9/18)', '22', '2016-09-18 18:52:27', 'admin', 'common/images/pic/avatar_11.jpg', '2', '1');
INSERT INTO `li_topic` VALUES ('35', 'share', 0x3C703E676974E68F90E4BAA4E697B6E587BAE78EB022596F7572266E6273703B6C6F63616C266E6273703B6368616E676573266E6273703B746F266E6273703B746865266E6273703B666F6C6C6F77696E67266E6273703B66696C6573266E6273703B776F756C64266E6273703B6265266E6273703B6F7665727772697474656E266E6273703B6279266E6273703B6D6572676522EFBC8CE698AFE59BA0E4B8BA6769742070756C6CE79A84E794B5E88491E69CACE59CB0E4BFAEE694B9E4BA86EFBC8CE8B79F676974E79A84E5BA93E9878CE4B88DE4B880E887B4EFBC8CE68980E4BBA5E68F90E7A4BAE99499E8AFAFE4BFA1E681AFE380823C2F703E3C703E3C62723E3C2F703E, 'admin', '2016-09-19 10:22:07', 'git提交时出现\"Your local changes to the following files would be overwritten by merge\"', '9', '2016-09-19 10:22:07', 'admin', 'common/images/pic/avatar_7.jpg', '0', '1');
-- ----------------------------
-- Table structure for li_topic_comment
-- ----------------------------
DROP TABLE IF EXISTS `li_topic_comment`;
CREATE TABLE `li_topic_comment` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`author` varchar(255) NOT NULL,
`tid` bigint(20) DEFAULT NULL,
`title` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL COMMENT '主题标题',
`createtime` datetime DEFAULT NULL,
`pic` varchar(255) DEFAULT '' COMMENT '回复者头像',
`like` bigint(20) DEFAULT '0',
`comment` longtext CHARACTER SET utf8 COLLATE utf8_bin,
`likers` text,
`oldcomment` longtext CHARACTER SET utf8 COLLATE utf8_bin,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=46 DEFAULT CHARSET=latin1;
-- ----------------------------
-- Records of li_topic_comment
-- ----------------------------
INSERT INTO `li_topic_comment` VALUES ('19', 'livisky', '5', 'Nodejs开源博客系统推荐(Liblog )', '2016-08-22 02:10:34', '', '0', 0xE5898DE7ABAFE6B18720687474703A2F2F7777772E6A736F75742E636F6D2CE68F90E4BE9BE585A8E7AB99E5858DE8B4B9E6BA90E7A081284E6F64656A73E5BC80E6BA90E58D9AE5AEA2E7B3BBE7BB9F292CE5AE98E696B95151E7BEA40AE7BEA4E58FB73235363638373630312CE6ACA2E8BF8EE58AA0E585A5EFBC81E5898DE7ABAFE696B9E99DA2E79A84E997AEE9A298EFBC8CE58FAFE4BBA5E59CA8E7BEA4E9878CE68F90E997AE, null, 0x3C703EE5898DE7ABAFE6B187203C6120687265663D22687474703A2F2F7777772E6A736F75742E636F6D2F22207461726765743D225F626C616E6B223E687474703A2F2F7777772E6A736F75742E636F6D3C2F613E2CE68F90E4BE9BE585A8E7AB99E5858DE8B4B9E6BA90E7A081284E6F64656A73E5BC80E6BA90E58D9AE5AEA2E7B3BBE7BB9F292CE5AE98E696B95151E7BEA40AE7BEA4E58FB73235363638373630312CE6ACA2E8BF8EE58AA0E585A5EFBC81E5898DE7ABAFE696B9E99DA2E79A84E997AEE9A298EFBC8CE58FAFE4BBA5E59CA8E7BEA4E9878CE68F90E997AE3C62723E3C2F703E3C703E3C62723E3C2F703E);
INSERT INTO `li_topic_comment` VALUES ('20', 'livisky', '26', '杭州微易信息科技-互联网金融的新兴力量(15k-25k)', '2016-08-22 22:26:25', '', '0', 0xE9A1B6E8B5B7EFBC81, null, 0x3C703EE9A1B6E8B5B7EFBC813C2F703E);
INSERT INTO `li_topic_comment` VALUES ('21', 'livichen', '25', '共创网络找化网招聘前端攻城狮(12k-20k)', '2016-08-23 15:04:17', '', '0', 0xE8B28CE4BCBCE4B88DE99499EFBC81, null, 0x3C703EE8B28CE4BCBCE4B88DE99499EFBC813C2F703E);
INSERT INTO `li_topic_comment` VALUES ('22', 'livichen', '5', '如何使用liblog', '2016-08-25 17:07:56', 'common/images/pic/avatar_2.jpg', '0', 0x6D61726B65642120363636, null, 0x3C703E6D61726B656421203636363C2F703E);
INSERT INTO `li_topic_comment` VALUES ('23', 'billy', '26', '杭州微易信息科技-互联网金融的新兴力量(15k-25k)', '2016-08-25 17:11:05', 'common/images/pic/avatar_6.jpg', '0', 0xE58FAFE6839CE4B88DE59CA8E69DADE5B79EE58F91E5B195E5958AEFBC81, null, 0x3C703EE58FAFE6839CE4B88DE59CA8E69DADE5B79EE58F91E5B195E5958AEFBC813C2F703E);
INSERT INTO `li_topic_comment` VALUES ('24', 'billy', '11', '有没有比较过ThinkJS与AngularJs2?', '2016-08-25 17:13:35', 'common/images/pic/avatar_8.jpg', '0', 0x416E67756C61724A53E5BD93E5889DE698AFE68F90E4BE9BE7BB99E8AEBEE8AEA1E4BABAE59198E794A8E69DA5E5BFABE9809FE69E84E5BBBA48544D4CE8A1A8E58D95E79A84E4B880E4B8AAE58685E983A8E5B7A5E585B7E38082E99A8FE79D80E697B6E997B4E79A84E68EA8E7A7BBEFBC8CE59084E7A78DE789B9E680A720E8A2ABE58AA0E585A5E8BF9BE58EBBE4BBA5E98082E5BA94E4B88DE5908CE59CBAE699AFE4B88BE79A84E5BA94E794A8E5BC80E58F91E38082E784B6E8808CE794B1E4BA8EE69C80E5889DE79A84E69EB6E69E84E99990E588B6EFBC88E6AF94E5A682E7BB91E5AE9AE5928CE6A8A1E69DBFE69CBAE588B6EFBC89EFBC8CE680A7E883BDE79A8420E68F90E58D87E5B7B2E7BB8FE99D9EE5B8B8E59BB0E99ABEE4BA86E38082, null, 0x3C703E416E67756C61724A53E5BD93E5889DE698AFE68F90E4BE9BE7BB99E8AEBEE8AEA1E4BABAE59198E794A8E69DA5E5BFABE9809FE69E84E5BBBA48544D4CE8A1A8E58D95E79A84E4B880E4B8AAE58685E983A8E5B7A5E585B7E38082E99A8FE79D80E697B6E997B4E79A84E68EA8E7A7BBEFBC8CE59084E7A78DE789B9E680A720E8A2ABE58AA0E585A5E8BF9BE58EBBE4BBA5E98082E5BA94E4B88DE5908CE59CBAE699AFE4B88BE79A84E5BA94E794A8E5BC80E58F91E38082E784B6E8808CE794B1E4BA8EE69C80E5889DE79A84E69EB6E69E84E99990E588B6EFBC88E6AF94E5A682E7BB91E5AE9AE5928CE6A8A1E69DBFE69CBAE588B6EFBC89EFBC8CE680A7E883BDE79A8420E68F90E58D87E5B7B2E7BB8FE99D9EE5B8B8E59BB0E99ABEE4BA86E380823C2F703E3C703E3C62723E3C2F703E);
INSERT INTO `li_topic_comment` VALUES ('25', 'billy', '5', '如何使用liblog', '2016-08-26 12:24:02', 'common/images/pic/avatar_8.jpg', '0', 0xE694AFE68C81E4B880E4B88BEFBC81, null, 0x3C703EE694AFE68C81E4B880E4B88BEFBC813C2F703E);
INSERT INTO `li_topic_comment` VALUES ('26', 'asf1988', '27', 'css3中display:box 和display:flex区别', '2016-08-29 19:32:26', '', '0', 0x0A2020202020202020202020E5BA94E8AFA5E698AFE588B6E5AE9AE79A84E8AFADE6B395E78988E69CACE4B88DE5908CE590A7EFBC81646973706C61793A20626F78C2A0E698AF20435353203230303920E5B9B4E79A84E8AFADE6B395EFBC8CE5B7B2E7BB8FE8BF87E697B6EFBC8CE99C80E58AA0E4B88AE5AFB9E5BA94E5898DE7BC80646973706C61793A20666C6578C2A0E698AF203230313220E5B9B4E79A84E8AFADE6B395EFBC8CE4B99FE5B086E698AFE4BBA5E5908EE6A087E58786E79A84E8AFADE6B395EFBC8CE5A4A7E983A8E58886E6B58FE8A788E599A8E5B7B2E7BB8FE5AE9EE78EB0E4BA86E697A0E5898DE7BC80E78988E69CAC0A202020202020202020, null, 0x0A20202020202020202020203C703EE5BA94E8AFA5E698AFE588B6E5AE9AE79A84E8AFADE6B395E78988E69CACE4B88DE5908CE590A7EFBC813C2F703E3C703E646973706C61793A20626F78266E6273703BE698AF20435353203230303920E5B9B4E79A84E8AFADE6B395EFBC8CE5B7B2E7BB8FE8BF87E697B6EFBC8CE99C80E58AA0E4B88AE5AFB9E5BA94E5898DE7BC803C2F703E3C703E3C2F703E3C703E646973706C61793A20666C6578266E6273703BE698AF203230313220E5B9B4E79A84E8AFADE6B395EFBC8CE4B99FE5B086E698AFE4BBA5E5908EE6A087E58786E79A84E8AFADE6B395EFBC8CE5A4A7E983A8E58886E6B58FE8A788E599A8E5B7B2E7BB8FE5AE9EE78EB0E4BA86E697A0E5898DE7BC80E78988E69CAC3C2F703E3C703E3C62723E3C2F703E0A202020202020202020);
INSERT INTO `li_topic_comment` VALUES ('27', 'bbq2013', '27', 'css3中display:box 和display:flex区别', '2016-08-29 19:34:43', 'common/images/pic/avatar_4.jpg', '0', 0x4061736631393838C2A0E6808EE4B988E585BCE5AEB9E5A4A7E983A8E58886E88081E78988E69CACE6B58FE8A788E599A8E591A2EFBC8CE5A5BDE5838FE8B79FE6B58FE8A788E599A8E78988E69CACE4B99FE69C89E585B3E7B3BBE590A7EFBC81, null, 0x3C6120687265663D222F706572736F6E616C2F406173663139383822207461726765743D225F626C616E6B223E40617366313938383C2F613E266E6273703BE6808EE4B988E585BCE5AEB9E5A4A7E983A8E58886E88081E78988E69CACE6B58FE8A788E599A8E591A2EFBC8CE5A5BDE5838FE8B79FE6B58FE8A788E599A8E78988E69CACE4B99FE69C89E585B3E7B3BBE590A7EFBC81);
INSERT INTO `li_topic_comment` VALUES ('28', 'livisky', '27', 'css3中display:box 和display:flex区别', '2016-08-29 19:37:17', 'common/images/pic/avatar_7.jpg', '0', 0xE5898DE88085646973706C61793A626F78E698AF32303039E5B9B4E79A84E8AFADE6B395E78988E69CACEFBC8CE4BDBFE794A8E697B6E99C80E8A681E58AA0E4B88AE6B58FE8A788E599A8E79A84E5898DE7BC80EFBC8CE4B88DE8BF87E78EB0E59CA8E5B7B2E7BB8FE8BF87E697B6E4BA86E380820AE5908EE88085646973706C61793A666C6578E698AF32303132E5B9B4E69C80E696B0E4BFAEE6ADA3E79A84E8AFADE6B395E78988E69CACEFBC8CE6B58FE8A788E599A8E694AFE68C81E8BE83E5A5BDEFBC8CE4B99FE5B086E68890E4B8BAE4BBA5E5908EE6A087E58786E79A84E8AFADE6B395E380820AE4B8ADE997B432303131E5B9B4E4B99FE68F90E587BAE4BA86E4B880E4B8AAE5A587E891A9E79A84E8AFADE6B395E78988E69CAC646973706C61793A666C6578626F78EFBC8CE99D9EE5AE98E696B9E79A84EFBC8CE5BD93E697B6E4B8BBE8A681E698AFE4B8BA4945E6B58FE8A788E599A8E4BDBFE794A8E380820A646973706C61793A626F78E6B58FE8A788E599A8E694AFE68C81EFBC9A494546697265666F784368726F6D655361666172694F70657261E4B88DE694AFE68C81322E302D34302E30EFBC882D6D6F7A2DEFBC89342E302D34352E30EFBC882D7765626B69742DEFBC89362E302D382E30EFBC882D7765626B69742DEFBC8931352E302D32392E30EFBC882D7765626B69742DEFBC89494546697265666F784368726F6D655361666172694F7065726131312E302B32322E302B32312E302BEFBC882D7765626B69742DEFBC8932392E302B362E312BEFBC882D7765626B69742DEFBC89392E302B31352E302BEFBC882D7765626B69742DEFBC8931372E302BE8BE83E4B8BAE5AE8CE695B4E79A84E585BCE5AEB9E680A7E4BBA3E7A081EFBC8CE5A682E4B88BEFBC9A2E626F78207B0A20202020646973706C61793A202D7765626B69742D626F783B202F2A204368726F6D6520342B2C2053616661726920332E312C20694F532053616661726920332E322B202A2F0A20202020646973706C61793A202D6D6F7A2D626F783B202F2A2046697265666F782031372D202A2F0A20202020646973706C61793A202D7765626B69742D666C65783B202F2A204368726F6D652032312B2C2053616661726920362E312B2C20694F532053616661726920372B2C204F706572612031352F3136202A2F0A20202020646973706C61793A202D6D6F7A2D666C65783B202F2A2046697265666F782031382B202A2F0A20202020646973706C61793A202D6D732D666C6578626F783B202F2A204945203130202A2F0A20202020646973706C61793A20666C65783B202F2A204368726F6D652032392B2C2046697265666F782032322B2C2049452031312B2C204F706572612031322E312F31372F31382C20416E64726F696420342E342B202A2F0A7D, null, 0x3C703EE5898DE88085646973706C61793A626F78E698AF32303039E5B9B4E79A84E8AFADE6B395E78988E69CACEFBC8CE4BDBFE794A8E697B6E99C80E8A681E58AA0E4B88AE6B58FE8A788E599A8E79A84E5898DE7BC80EFBC8CE4B88DE8BF87E78EB0E59CA8E5B7B2E7BB8FE8BF87E697B6E4BA86E380820A3C2F703E3C703EE5908EE88085646973706C61793A666C6578E698AF32303132E5B9B4E69C80E696B0E4BFAEE6ADA3E79A84E8AFADE6B395E78988E69CACEFBC8CE6B58FE8A788E599A8E694AFE68C81E8BE83E5A5BDEFBC8CE4B99FE5B086E68890E4B8BAE4BBA5E5908EE6A087E58786E79A84E8AFADE6B395E380820A3C2F703E3C703EE4B8ADE997B432303131E5B9B4E4B99FE68F90E587BAE4BA86E4B880E4B8AAE5A587E891A9E79A84E8AFADE6B395E78988E69CAC646973706C61793A666C6578626F78EFBC8CE99D9EE5AE98E696B9E79A84EFBC8CE5BD93E697B6E4B8BBE8A681E698AFE4B8BA4945E6B58FE8A788E599A8E4BDBFE794A8E380820A3C2F703E3C703E646973706C61793A626F78E6B58FE8A788E599A8E694AFE68C81EFBC9A3C2F703E3C7461626C6520636C6173733D22223E3C74686561643E3C74723E3C74683E49453C2F74683E3C74683E46697265666F783C2F74683E3C74683E4368726F6D653C2F74683E3C74683E5361666172693C2F74683E3C74683E4F706572613C2F74683E3C2F74723E3C2F74686561643E3C74626F64793E3C74723E3C74643EE4B88DE694AFE68C813C2F74643E3C74643E322E302D34302E303C62723EEFBC882D6D6F7A2DEFBC893C2F74643E3C74643E342E302D34352E303C62723EEFBC882D7765626B69742DEFBC893C2F74643E3C74643E362E302D382E303C62723EEFBC882D7765626B69742DEFBC893C2F74643E3C74643E31352E302D32392E303C62723EEFBC882D7765626B69742DEFBC893C2F74643E3C2F74723E3C2F74626F64793E3C2F7461626C653E3C62723E3C7461626C6520636C6173733D22223E3C74686561643E3C74723E3C74683E49453C2F74683E3C74683E46697265666F783C2F74683E3C74683E4368726F6D653C2F74683E3C74683E5361666172693C2F74683E3C74683E4F706572613C2F74683E3C2F74723E3C2F74686561643E3C74626F64793E3C74723E3C74643E31312E302B3C2F74643E3C74643E32322E302B3C2F74643E3C74643E32312E302BEFBC882D7765626B69742DEFBC893C62723E32392E302B3C2F74643E3C74643E362E312BEFBC882D7765626B69742DEFBC893C62723E392E302B3C2F74643E3C74643E31352E302BEFBC882D7765626B69742DEFBC893C62723E31372E302B3C2F74643E3C2F74723E3C2F74626F64793E3C2F7461626C653E3C703E3C623EE8BE83E4B8BAE5AE8CE695B4E79A84E585BCE5AEB9E680A7E4BBA3E7A081EFBC8CE5A682E4B88BEFBC9A3C2F623E3C2F703E3C707265207374796C653D226D61782D77696474683A20313030253B223E3C636F646520636C6173733D226A61766173637269707420686C6A732220636F64656D61726B3D2231223E2E626F78207B0A20202020646973706C61793A202D7765626B69742D626F783B203C7370616E20636C6173733D22686C6A732D636F6D6D656E74223E2F2A204368726F6D6520342B2C2053616661726920332E312C20694F532053616661726920332E322B202A2F3C2F7370616E3E0A20202020646973706C61793A202D6D6F7A2D626F783B203C7370616E20636C6173733D22686C6A732D636F6D6D656E74223E2F2A2046697265666F782031372D202A2F3C2F7370616E3E0A20202020646973706C61793A202D7765626B69742D666C65783B203C7370616E20636C6173733D22686C6A732D636F6D6D656E74223E2F2A204368726F6D652032312B2C2053616661726920362E312B2C20694F532053616661726920372B2C204F706572612031352F3136202A2F3C2F7370616E3E0A20202020646973706C61793A202D6D6F7A2D666C65783B203C7370616E20636C6173733D22686C6A732D636F6D6D656E74223E2F2A2046697265666F782031382B202A2F3C2F7370616E3E0A20202020646973706C61793A202D6D732D666C6578626F783B203C7370616E20636C6173733D22686C6A732D636F6D6D656E74223E2F2A204945203130202A2F3C2F7370616E3E0A20202020646973706C61793A20666C65783B203C7370616E20636C6173733D22686C6A732D636F6D6D656E74223E2F2A204368726F6D652032392B2C2046697265666F782032322B2C2049452031312B2C204F706572612031322E312F31372F31382C20416E64726F696420342E342B202A2F3C2F7370616E3E0A7D3C2F636F64653E3C2F7072653E3C703E3C62723E3C2F703E);
INSERT INTO `li_topic_comment` VALUES ('29', 'bbq2013', '27', 'css3中display:box 和display:flex有区别?', '2016-08-29 19:41:09', 'common/images/pic/avatar_4.jpg', '0', 0x406C697669736B79C2A0E6849FE8B0A2EFBC81, null, 0x3C6120687265663D222F706572736F6E616C2F406C697669736B7922207461726765743D225F626C616E6B223E406C697669736B793C2F613E266E6273703BE6849FE8B0A2EFBC81);
INSERT INTO `li_topic_comment` VALUES ('30', 'bbt1991', '28', 'div垂直水平居中的几种方法', '2016-09-02 10:15:26', 'common/images/pic/avatar_9.jpg', '1', 0xE6B2A1E4BABAE9A1B6E59097EFBC9FE887AAE5B7B1E9A1B6E4B880E4B88BEFBC81, 'Dean', 0x3C703EE6B2A1E4BABAE9A1B6E59097EFBC9FE887AAE5B7B1E9A1B6E4B880E4B88BEFBC813C2F703E);
INSERT INTO `li_topic_comment` VALUES ('31', 'bbt1991', '26', '杭州微易信息科技-互联网金融的新兴力量(15k-25k)', '2016-09-02 10:16:03', 'common/images/pic/avatar_9.jpg', '0', 0x6D61726B656421, null, 0x3C703E6D61726B6564213C62723E3C62723E3C62723E3C2F703E);
INSERT INTO `li_topic_comment` VALUES ('32', 'bbt1991', '29', 'React单页应用如何实现第三方登录功能', '2016-09-02 10:31:53', 'common/images/pic/avatar_9.jpg', '0', 0xE7ACACE4B889E696B9E799BBE99986E4B8BBE8A681E698AFE58F91E98081E8AFB7E6B182E5928CE8BF94E59B9EE695B0E68DAEEFBC8CE8B79FE4BB80E4B988E8AFADE8A880EFBC8CE4BB80E4B988E6A186E69EB6E697A0E585B3E38082E58FAFE4BBA5E7BD91E4B88AE689BEE4B880E4B88BE79BB8E585B3E8B584E69699E38082, null, 0x3C703EE7ACACE4B889E696B9E799BBE99986E4B8BBE8A681E698AFE58F91E98081E8AFB7E6B182E5928CE8BF94E59B9EE695B0E68DAEEFBC8CE8B79FE4BB80E4B988E8AFADE8A880EFBC8CE4BB80E4B988E6A186E69EB6E697A0E585B3E380823C2F703E3C703EE58FAFE4BBA5E7BD91E4B88AE689BEE4B880E4B88BE79BB8E585B3E8B584E69699E380823C2F703E3C703E3C62723E3C2F703E);
INSERT INTO `li_topic_comment` VALUES ('33', 'livisky', '30', '前端汇功能更新帖(liblog v2.0)', '2016-09-02 13:41:50', 'common/images/pic/avatar_7.jpg', '0', 0x0A20202020202020202020200A2020202020202020202020E5898DE7ABAFE6B187287777772E6A736F75742E636F6D29E698AFE59FBAE4BA8E6C69626C6F67E79A84E58D9AE5AEA2E7BD91E7AB99EFBC8C6C69626C6F67E79A84E69C80E696B0E58A9FE883BDE4BC9AE59CA86A736F7574E4B88AE69BB4E696B0E4B88EE4BD93E9AA8CEFBC8CE69C80E5908EE4BC9AE59CA8E5BC80E6BA90E78988E98089E68BA9E983A8E58886E58A9FE883BDE98090E6ADA5E69BB4E696B0EFBC810A2020202020202020200A202020202020202020, null, 0x0A20202020202020202020200A20202020202020202020203C703E3C623EE5898DE7ABAFE6B1873C2F623E287777772E6A736F75742E636F6D29E698AFE59FBAE4BA8E6C69626C6F67E79A84E58D9AE5AEA2E7BD91E7AB99EFBC8C3C2F703E3C703E3C623E6C69626C6F673C2F623EE79A84E69C80E696B0E58A9FE883BDE4BC9AE59CA86A736F7574E4B88AE69BB4E696B0E4B88EE4BD93E9AA8CEFBC8CE69C80E5908EE4BC9AE59CA8E5BC80E6BA90E78988E98089E68BA9E983A8E58886E58A9FE883BDE98090E6ADA5E69BB4E696B0EFBC813C2F703E0A2020202020202020203C703E3C62723E3C2F703E0A202020202020202020);
INSERT INTO `li_topic_comment` VALUES ('34', 'livisky', '30', '前端汇功能更新帖(liblog v2.0)', '2016-09-02 13:44:37', 'common/images/pic/avatar_7.jpg', '1', 0x0A20202020202020202020200A20202020202020202020200A2020202020202020202020323031362F392F323AE69BB4E696B0E69C80E696B0E4BC9AE59198E79A84E5B195E7A4BAE4BC9AE59198E4B8AAE4BABAE4B8ADE5BF83E6B7BBE58AA0E2809CE58F91E5B883E8AF9DE9A298E2809DE58A9FE883BDE993BEE68EA5E4BFAEE5A48DE697A0E698B5E7A7B0E79A84E4BC9AE59198E79A84E68B9BE591BCE698BEE7A4BAE694B9E59684E983A8E58886E4BD93E9AA8CC2A00A2020202020202020200A2020202020202020200A202020202020202020, 'livisky', 0x0A20202020202020202020200A20202020202020202020200A20202020202020202020203C703E3C623E323031362F392F323A3C2F623E3C2F703E3C703EE69BB4E696B0E69C80E696B0E4BC9AE59198E79A84E5B195E7A4BA3C2F703E3C703EE4BC9AE59198E4B8AAE4BABAE4B8ADE5BF83E6B7BBE58AA0E2809CE58F91E5B883E8AF9DE9A298E2809DE58A9FE883BDE993BEE68EA53C62723E3C2F703E3C703EE4BFAEE5A48DE697A0E698B5E7A7B0E79A84E4BC9AE59198E79A84E68B9BE591BCE698BEE7A4BA3C2F703E3C703EE694B9E59684E983A8E58886E4BD93E9AA8C266E6273703B3C2F703E3C703E3C62723E3C2F703E0A2020202020202020200A2020202020202020200A202020202020202020);
INSERT INTO `li_topic_comment` VALUES ('35', 'livisky', '31', '新手求解,thinkjs如何生成随机验证码,并返回至前台?', '2016-09-02 16:22:36', 'common/images/pic/avatar_7.jpg', '1', 0xE58FAFE4BBA5E794A863636170E79A846E6F6465E6A8A1E59D97EFBC8CE78EB0E68F90E4BE9BE5A4A7E887B4E6809DE8B7AFE4BE9BE58F82E88083EFBC8CE5908EE7BBADE8A1A5E58585E4B8AAE585B7E4BD93E69599E7A88BE3808231E38081E4B88BE8BDBDE5AE89E8A38563636170E6A8A1E59D972C6E706D20696E7374616C6C2063636170202D2D7361766532E38081E58699E68EA5E58FA3202020206173796E6320746F636170746861416374696F6E28297B0A2020202020206C65742063636170203D207265717569726528276363617027293B0A2020202020206C65742063617074636861203D206363617028293B0A2020202020206C657420617279203D20636170746368612E67657428293B2F2F6172795B305D2069732063617074636861277320746578742C6172795B315D20697320636170746368612070696374757265206275666665722E0A202020202020617761697420746869732E73657373696F6E2827636F6465272C6172795B305D290A202020202020746869732E747970652822696D6167652F626D7022293B0A202020202020746869732E656E64286172795B315D293B0A202020207D33E38081E58699E5A5BDE68EA5E58FA3E5908EE9A1B5E99DA2E8B083E794A83C64697620636C6173733D22636F64652D7269676874223E0A20203C696D67207372633D222F72656769737465722F746F6361707468612220616C743D22222069643D22636170496D67223E0A3C2F6469763E, 'livisky', 0x3C703EE58FAFE4BBA5E794A83C7370616E207374796C653D226C696E652D6865696768743A20312E383B223E63636170E79A846E6F6465E6A8A1E59D97EFBC8CE78EB0E68F90E4BE9BE5A4A7E887B4E6809DE8B7AFE4BE9BE58F82E88083EFBC8CE5908EE7BBADE8A1A5E58585E4B8AAE585B7E4BD93E69599E7A88BE380823C2F7370616E3E3C2F703E3C703E3C7370616E207374796C653D226C696E652D6865696768743A20312E383B223E31E38081E4B88BE8BDBDE5AE89E8A38563636170E6A8A1E59D972C6E706D20696E7374616C6C2063636170202D2D736176653C2F7370616E3E3C2F703E3C703E3C7370616E207374796C653D226C696E652D6865696768743A20312E383B223E32E38081E58699E68EA5E58FA33C2F7370616E3E3C2F703E3C707265207374796C653D226D61782D77696474683A20313030253B223E3C636F646520636C6173733D226A61766173637269707420686C6A732220636F64656D61726B3D2231223E202020203C7370616E20636C6173733D22686C6A732D6B6579776F7264223E6173796E633C2F7370616E3E20746F636170746861416374696F6E28297B0A2020202020203C7370616E20636C6173733D22686C6A732D6B6579776F7264223E6C65743C2F7370616E3E2063636170203D203C7370616E20636C6173733D22686C6A732D6275696C745F696E223E726571756972653C2F7370616E3E283C7370616E20636C6173733D22686C6A732D737472696E67223E2763636170273C2F7370616E3E293B0A2020202020203C7370616E20636C6173733D22686C6A732D6B6579776F7264223E6C65743C2F7370616E3E2063617074636861203D206363617028293B0A2020202020203C7370616E20636C6173733D22686C6A732D6B6579776F7264223E6C65743C2F7370616E3E20617279203D20636170746368612E67657428293B3C7370616E20636C6173733D22686C6A732D636F6D6D656E74223E2F2F6172795B305D2069732063617074636861277320746578742C6172795B315D20697320636170746368612070696374757265206275666665722E3C2F7370616E3E0A2020202020203C7370616E20636C6173733D22686C6A732D6B6579776F7264223E61776169743C2F7370616E3E203C7370616E20636C6173733D22686C6A732D6B6579776F7264223E746869733C2F7370616E3E2E73657373696F6E283C7370616E20636C6173733D22686C6A732D737472696E67223E27636F6465273C2F7370616E3E2C6172795B3C7370616E20636C6173733D22686C6A732D6E756D626572223E303C2F7370616E3E5D290A2020202020203C7370616E20636C6173733D22686C6A732D6B6579776F7264223E746869733C2F7370616E3E2E74797065283C7370616E20636C6173733D22686C6A732D737472696E67223E22696D6167652F626D70223C2F7370616E3E293B0A2020202020203C7370616E20636C6173733D22686C6A732D6B6579776F7264223E746869733C2F7370616E3E2E656E64286172795B3C7370616E20636C6173733D22686C6A732D6E756D626572223E313C2F7370616E3E5D293B0A202020207D3C2F636F64653E3C2F7072653E3C703E33E38081E58699E5A5BDE68EA5E58FA3E5908EE9A1B5E99DA2E8B083E794A83C2F703E3C707265207374796C653D226D61782D77696474683A20313030253B223E3C636F646520636C6173733D226A61766173637269707420686C6A732220636F64656D61726B3D2231223E266C743B646976203C7370616E20636C6173733D22686C6A732D636C617373223E3C7370616E20636C6173733D22686C6A732D6B6579776F7264223E636C6173733C2F7370616E3E3C2F7370616E3E3D3C7370616E20636C6173733D22686C6A732D737472696E67223E22636F64652D7269676874223C2F7370616E3E2667743B0A20203C7370616E20636C6173733D22786D6C223E3C7370616E20636C6173733D22686C6A732D746167223E266C743B3C7370616E20636C6173733D22686C6A732D6E616D65223E696D673C2F7370616E3E203C7370616E20636C6173733D22686C6A732D61747472223E7372633C2F7370616E3E3D3C7370616E20636C6173733D22686C6A732D737472696E67223E222F72656769737465722F746F636170746861223C2F7370616E3E203C7370616E20636C6173733D22686C6A732D61747472223E616C743C2F7370616E3E3D3C7370616E20636C6173733D22686C6A732D737472696E67223E22223C2F7370616E3E203C7370616E20636C6173733D22686C6A732D61747472223E69643C2F7370616E3E3D3C7370616E20636C6173733D22686C6A732D737472696E67223E22636170496D67223C2F7370616E3E2667743B3C2F7370616E3E0A3C7370616E20636C6173733D22686C6A732D746167223E266C743B2F3C7370616E20636C6173733D22686C6A732D6E616D65223E6469763C2F7370616E3E2667743B3C2F7370616E3E3C2F7370616E3E3C2F636F64653E3C2F7072653E3C703E3C62723E3C2F703E);
INSERT INTO `li_topic_comment` VALUES ('36', 'livisky', '31', '新手求解,thinkjs如何生成随机验证码,并返回至前台?', '2016-09-02 16:23:11', 'common/images/pic/avatar_7.jpg', '0', 0xE8BF98E4B88DE6988EE799BDE79A84E8AF9DEFBC8CE8B79FE5B896E68F90E997AEE38082C2A0, null, 0x3C703EE8BF98E4B88DE6988EE799BDE79A84E8AF9DEFBC8CE8B79FE5B896E68F90E997AEE38082266E6273703B3C696D67207372633D22687474703A2F2F696D672E742E73696E616A732E636E2F74342F6170707374796C652F65787072657373696F6E2F6578742F6E6F726D616C2F30622F746F6F7468615F7468756D622E676966223E3C62723E3C2F703E3C703E3C62723E3C2F703E);
INSERT INTO `li_topic_comment` VALUES ('37', 'Dean', '31', '新手求解,thinkjs如何生成随机验证码,并返回至前台?', '2016-09-02 16:29:08', '', '0', 0xE8BF99E8AEBAE59D9BE79C9FE4B88DE9949920C2A07468616E6B73, null, 0x3C703EE8BF99E8AEBAE59D9BE79C9FE4B88DE9949920266E6273703B7468616E6B733C2F703E);
INSERT INTO `li_topic_comment` VALUES ('38', 'livisky', '31', '新手求解,thinkjs如何生成随机验证码,并返回至前台?', '2016-09-02 16:30:43', 'common/images/pic/avatar_7.jpg', '0', 0x404465616EC2A0E6849FE8B0A2E694AFE68C81EFBC81, null, 0x3C6120687265663D222F706572736F6E616C2F404465616E22207461726765743D225F626C616E6B223E404465616E3C2F613E266E6273703BE6849FE8B0A2E694AFE68C81EFBC81);
INSERT INTO `li_topic_comment` VALUES ('39', 'livichen', '32', '分页问题', '2016-09-12 10:25:26', 'common/images/pic/avatar_2.jpg', '0', 0xE59CA8E68EA7E588B6E58FB0E4B88BE79C8BE4B880E4B88BE7949FE68890E79A84C2A053514CC2A0E698AFE4B88DE698AFE4BDA0E683B3E8A681E79A84EFBC9F, null, 0x3C703EE59CA8E68EA7E588B6E58FB0E4B88BE79C8BE4B880E4B88BE7949FE68890E79A84266E6273703B53514C266E6273703BE698AFE4B88DE698AFE4BDA0E683B3E8A681E79A84EFBC9F3C2F703E3C703E3C62723E3C2F703E);
INSERT INTO `li_topic_comment` VALUES ('41', 'livisky', '30', '前端汇功能更新帖(liblog v2.0)', '2016-09-12 18:25:03', 'common/images/pic/avatar_13.jpg', '0', 0x0A2020202020202020323031362F392F31323AE4BFAEE694B96E6F64656A73E6A8A1E78988E8AFADE8A88028656A73E280943E6E756E6A75636B732968746D6CE6A8A1E78988E7BB93E69E84E69BB4E58AA0E6B885E699B0EFBC8CE6A8A1E78988E58FAFE4BBA5E7BBA7E689BFE58F8AE689A9E5B195E68F90E58F9668746D6CE9A1B5E99DA2E4B8ADE79A84637373E588B0E6A0B7E5BC8FE69687E4BBB60A202020202020, null, 0x0A20202020202020203C703E3C623E323031362F392F31323A3C2F623E3C2F703E3C703EE4BFAEE694B96E6F64656A73E6A8A1E78988E8AFADE8A88028656A73E280942667743B6E756E6A75636B73293C2F703E3C703E68746D6CE6A8A1E78988E7BB93E69E84E69BB4E58AA0E6B885E699B0EFBC8CE6A8A1E78988E58FAFE4BBA5E7BBA7E689BFE58F8AE689A9E5B1953C2F703E3C703EE68F90E58F9668746D6CE9A1B5E99DA2E4B8ADE79A84637373E588B0E6A0B7E5BC8FE69687E4BBB63C2F703E3C703E3C62723E3C2F703E0A202020202020);
INSERT INTO `li_topic_comment` VALUES ('42', 'livisky', '30', '前端汇功能更新帖(liblog v2.0)', '2016-09-13 15:35:02', 'common/images/pic/avatar_13.jpg', '0', 0x323031362F392F31333AE8B083E695B4737461746963E79BAEE5BD95E7BB93E69E84E6B7BBE58AA0E4B8BBE9A298E79BAEE5BD957468656D652CE9BB98E8AEA464656661756C74E4B8BBE9A298737461746963E79BAEE5BD95E7BB93E69E84E69BB4E58AA0E6B885E699B0, null, 0x3C703E3C623E323031362F392F31333A3C2F623E3C2F703E3C703EE8B083E695B4737461746963E79BAEE5BD95E7BB93E69E843C2F703E3C703EE6B7BBE58AA0E4B8BBE9A298E79BAEE5BD957468656D652CE9BB98E8AEA464656661756C74E4B8BBE9A2983C2F703E3C703E737461746963E79BAEE5BD95E7BB93E69E84E69BB4E58AA0E6B885E699B03C2F703E3C703E3C696D67207372633D222F7374617469632F75706C6F61642F706963732F392F31332F323031366D453874556F6D6B486361747249466374786762357072342E706E672220616C743D227468656D6522207374796C653D226D61782D77696474683A313030253B223E3C62723E3C2F703E3C703E3C62723E3C2F703E);
INSERT INTO `li_topic_comment` VALUES ('43', 'livisky', '30', '前端汇功能更新帖(liblog v2.0)', '2016-09-14 15:52:14', 'common/images/pic/avatar_13.jpg', '0', 0x323031362F392F31343AE6B7BBE58AA0E69687E7ABA0E58685E5AEB9E8B083E794A8E6A087E7ADBEEFBC8CE9809AE8BF87E6A087E7ADBEE9858DE7BDAEE58DB3E58FAFE5AE9EE78EB0E58685E5AEB9E79A84E8AFBBE58F96E8B083E695B4E983A8E58886E4BBA3E7A081E8AFA6E8A781EFBC9A687474703A2F2F7777772E6A736F75742E636F6D2F746F7069632F6974656D2F33332E68746D6C, null, 0x3C703E3C623E323031362F392F31343A3C2F623E3C2F703E3C703EE6B7BBE58AA0E69687E7ABA0E58685E5AEB9E8B083E794A8E6A087E7ADBEEFBC8CE9809AE8BF87E6A087E7ADBEE9858DE7BDAEE58DB3E58FAFE5AE9EE78EB0E58685E5AEB9E79A84E8AFBBE58F963C2F703E3C703EE8B083E695B4E983A8E58886E4BBA3E7A0813C2F703E3C703EE8AFA6E8A781EFBC9A3C6120687265663D22687474703A2F2F7777772E6A736F75742E636F6D2F746F7069632F6974656D2F33332E68746D6C22207461726765743D225F626C616E6B223E687474703A2F2F7777772E6A736F75742E636F6D2F746F7069632F6974656D2F33332E68746D6C3C2F613E3C2F703E3C703E3C62723E3C2F703E);
INSERT INTO `li_topic_comment` VALUES ('44', 'livisky', '34', 'liblog v2.0功能更新(2016/9/18)', '2016-09-18 18:50:34', 'common/images/pic/avatar_13.jpg', '1', 0xE4BFAEE694B9E6B3A8E5868CE4BAA4E4BA92E4BD93E9AA8CEFBC8CE6B3A8E5868CE68890E58A9FE5908EE8B7B3E8BDACE588B0E4B8AAE4BABAE4B8ADE5BF83EFBC8CE697A0E9A1BBE5868DE6ACA1E799BBE99986E38082, 'livisky', 0x3C703EE4BFAEE694B9E6B3A8E5868CE4BAA4E4BA92E4BD93E9AA8CEFBC8CE6B3A8E5868CE68890E58A9FE5908EE8B7B3E8BDACE588B0E4B8AAE4BABAE4B8ADE5BF83EFBC8CE697A0E9A1BBE5868DE6ACA1E799BBE99986E380823C2F703E3C703E3C62723E3C2F703E);
INSERT INTO `li_topic_comment` VALUES ('45', 'liweifeng', '34', 'liblog v2.0功能更新(2016/9/18)', '2016-09-18 18:52:27', 'common/images/pic/avatar_11.jpg', '0', 0xE6A5BCE4B8BBE8BE9BE88BA6E4BA86EFBC8CE69C9FE5BE856C69626C6F67322E30, null, 0x3C703EE6A5BCE4B8BBE8BE9BE88BA6E4BA86EFBC8CE69C9FE5BE853C623E6C69626C6F67322E303C2F623E3C2F703E3C703E3C696D67207372633D22687474703A2F2F696D672E742E73696E616A732E636E2F74342F6170707374796C652F65787072657373696F6E2F6578742F6E6F726D616C2F64392F79655F7468756D622E676966223E3C62723E3C2F703E3C703E3C62723E3C2F703E);
-- ----------------------------
-- Table structure for li_topic_item
-- ----------------------------
DROP TABLE IF EXISTS `li_topic_item`;
CREATE TABLE `li_topic_item` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`comment` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of li_topic_item
-- ----------------------------
INSERT INTO `li_topic_item` VALUES ('1', 'share', '分享');
INSERT INTO `li_topic_item` VALUES ('2', 'ask', '问答');
INSERT INTO `li_topic_item` VALUES ('3', 'job', '招聘');
INSERT INTO `li_topic_item` VALUES ('4', 'liblog', 'liblog');
INSERT INTO `li_topic_item` VALUES ('5', 'wangeditor', 'wangeditor');
INSERT INTO `li_topic_item` VALUES ('6', 'thinkjs', 'thinkjs');
-- ----------------------------
-- Table structure for li_user
-- ----------------------------
DROP TABLE IF EXISTS `li_user`;
CREATE TABLE `li_user` (
`id` int(100) NOT NULL AUTO_INCREMENT,
`name` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,
`nickname` varchar(255) DEFAULT NULL,
`password` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,
`email` varchar(255) DEFAULT NULL,
`role` int(255) DEFAULT '4',
`openid` varchar(255) DEFAULT NULL COMMENT '第三方登录标识',
`pic` varchar(255) DEFAULT '' COMMENT '头像',
`way` varchar(255) DEFAULT 'site' COMMENT '登录方式',
`point` varchar(255) DEFAULT '10' COMMENT '积分',
`sign` text COMMENT '个性签名',
`createtime` datetime DEFAULT NULL,
`level` int(11) DEFAULT '1' COMMENT '等级',
`vip` int(11) DEFAULT '0' COMMENT 'vip',
`isverify` int(11) DEFAULT '0' COMMENT '是否已验证邮箱',
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=86 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of li_user
-- ----------------------------
INSERT INTO `li_user` VALUES ('56', 'admin', 'admin', 'e10adc3949ba59abbe56e057f20f883e', '[email protected]', '1', null, 'common/images/pic/avatar_7.jpg', 'site', '15', null, '2016-08-22 14:44:32', '1', '0', '0');
INSERT INTO `li_user` VALUES ('85', 'livi', 'livi', null, '[email protected]', '4', null, '', 'site', '10', null, null, '1', '0', '0');
-- ----------------------------
-- Table structure for li_user_collect
-- ----------------------------
DROP TABLE IF EXISTS `li_user_collect`;
CREATE TABLE `li_user_collect` (
`id` bigint(11) NOT NULL AUTO_INCREMENT,
`url` varchar(255) DEFAULT NULL COMMENT '类型',
`title` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,
`author` varchar(255) DEFAULT NULL,
`createtime` datetime DEFAULT NULL,
`type` varchar(255) DEFAULT NULL,
`aid` int(11) DEFAULT NULL COMMENT '文章id',
`iscollect` int(11) DEFAULT '1' COMMENT '是否收藏',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=32 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of li_user_collect
-- ----------------------------
INSERT INTO `li_user_collect` VALUES ('26', '/topic/item/23', ' E井在线商城招聘前端开发工程师(15-30k) ', 'livichen', '2016-08-23 19:00:48', 'topic', '23', '1');
INSERT INTO `li_user_collect` VALUES ('27', '/topic/item/27', ' css3中display:box 和display:flex区别 ', 'asf1988', '2016-08-29 19:31:08', 'topic', '27', '1');
INSERT INTO `li_user_collect` VALUES ('28', '/topic/item/27', ' css3中display:box 和display:flex有区别? ', 'bbq2013', '2016-08-29 19:41:25', 'topic', '27', '1');
INSERT INTO `li_user_collect` VALUES ('29', '/topic/item/28', ' div垂直水平居中的几种方法 ', 'bbt1991', '2016-09-02 10:14:56', 'topic', '28', '1');
INSERT INTO `li_user_collect` VALUES ('30', '/topic/item/31.html', ' 新手求解,thinkjs如何生成随机验证码,并返回至前台? ', 'livisky', '2016-09-02 16:32:09', 'topic', '31', '1');
INSERT INTO `li_user_collect` VALUES ('31', '/topic/item/34.html', ' liblog v2.0功能更新(2016/9/18) ', 'livisky', '2016-09-18 19:14:01', 'topic', '34', '1');