원본: Jingxiu.com 웹페이지 인스턴트 푸시 | 재인쇄할 경우 출처를 표시해 주세요.
링크:
이 튜토리얼 시리즈는 ThinkJS v2.x 버전(공식 웹사이트)을 예로 들어 실제 작업에 중점을 둡니다. .
이 기사에서는 컨트롤러 사용에 대해 계속 설명합니다.
객체가 인스턴스화될 때 뭔가를 하고 싶다면 생성자 메서드가 최선의 선택입니다. ES6에서 제공하는 생성자는 constructor
입니다. constructor
。
constructor方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor方法,如果没有显式定义,一个空的constructor方法会被默认添加。
方法
ECMAScript 6 入门 作者:阮一峰
thinkjs 强大的地方在于,我们不仅可以规规矩矩的 export default class
自己声明 Class ,还提供了动态创建 Class 的方法:think.controller
。
但是 thinkjs 动态创建的 Class 没有 constructor
,而是提供了一个 init
作为构造方法的替代方法,该方法的使用方式与 constructor
一致。
上一篇文章(Node.js 国产 MVC 框架 ThinkJS 开发 controller 篇 基类与继承链部分)中也有 init
方法的使用示例,再看代码:
// src/home/controller/base.js'use strict';export default class extends think.controller.base { init(...args) {super.init(...args);// 要求全部 url 必须携带 auth 参数let auth = this.get('auth');if (think.isEmpty(auth)) { return this.error(500, '全部 url 必须携带 auth 参数');} }}
当然这并不是表示不能使用 constructor
方法了,假如你是像我一样习惯使用 export default class
自己声明 Class 的筒子,还是可以用回标准的 constructor
方法的。
thinkjs 动态创建 Class 的方法参见官方文档,这里不再赘述。
thinkjs 实现了几个很有用的魔术方法,为开发提供了极大的便利,手动点赞~
顾名思义,前置操作会抢先在 Controller 中具体的 Action 执行之前执行,就是“在 xxx 之前执行”的意思。来看代码:
// src/home/controller/user.js'use strict';export default class extends think.controller.base { __before() {console.log('this is __before().'); } indexAction() {console.log('this is indexAction().');return this.end(); }}// 访问 /home/user/index 的执行结果如下:// this is __before().// this is indexAction().
那么可能有人会说:看上去 __before
跟 init
是一样的用途嘛。老规矩,来看代码:
// src/home/controller/user.js'use strict';export default class extends think.controller.base { init(...args) {super.init(...args);console.log('this is init().'); } __before() {console.log('this is __before().'); } indexAction() {console.log('this is indexAction().');return this.end(); }}// 访问 /home/user/index 的执行结果如下:// this is init().// this is __before().// this is indexAction().
看到了吗?执行还是有先后顺序的,再来个复杂一点的:
// src/home/controller/base.js'use strict';export default class extends think.controller.base { init(...args) {super.init(...args);console.log('this is base.init().'); }}// src/home/controller/user.js'use strict';export default class extends think.controller.base { init(...args) {super.init(...args);console.log('this is user.init().'); } __before() {console.log('this is user.__before().'); } indexAction() {console.log('this is user.indexAction().');return this.end(); }}// 访问 /home/user/index 的执行结果如下:// this is base.init().// this is user.init().// this is user.__before().// this is user.indexAction().
好吧,你会说“意料之中”~
明白了前置操作,后置操作也不难理解,看代码:
// src/home/controller/user.js'use strict';export default class extends think.controller.base { init(...args) {super.init(...args);console.log('this is init().'); } __before() {console.log('this is __before().'); } __after() {console.log('this is __after().'); } indexAction() {console.log('this is indexAction().');return this.end(); }}// 访问 /home/user/index 的执行结果如下:// this is init().// this is __before().// this is indexAction().
咦?貌似有地方不对。。。__after
没执行。
这当然不是 __after
写在 indexAction
上面导致的!修改代码:
// src/home/controller/user.js'use strict';export default class extends think.controller.base { init(...args) {super.init(...args);console.log('this is init().'); } __before() {console.log('this is __before().'); } __after() {console.log('this is __after().');return this.end(); } indexAction() {console.log('this is indexAction().'); }}// 访问 /home/user/index 的执行结果如下:// this is init().// this is __before().// this is indexAction().// this is __after().
这次 OK 了,和预期的结果一致。
我知道细心的你已经注意到有句代码 return this.end()
从 indexAction
移动到 __after
里面了。
this.end()
内部执行了 Node.js HTTP response.end() 操作,表示整个响应流结束了,因此如果想要启用 __after
的话,这句代码就要放在 __after
里面运行。
这个魔术方法有点特殊,它不像前两个魔术方法一样用于在某个流程节点打点运行,而是分担了 init
的部分职责:用于检测当某个 Controller 被访问的 Action 并未定义的情况下,由 __call
来接手运行。
// src/home/controller/user.js'use strict';export default class extends think.controller.base { init(...args) {super.init(...args);console.log('this is init().'); } __call() {console.log(this.http.action + 'Action is not exists.');return this.end(); } indexAction() {console.log('this is indexAction().');return this.end(); }}// 访问 /home/user/test 的执行结果如下:// this is init().// testAction is not exists.
可以看到当访问的 testAction
不存在时,框架会运行 __call
来进行处理,我们的处理是记录错误并结束响应输出。
示例代码是将 __call
放在了二级子类中,通常是放在基类中,可以管控全部子类的非法访问处理。
提示:本方法只能用于捕捉 Action 不存在的情况,但是假如 Controller 不存在,会直接触发 404 错误(被框架接管)而无法干涉。
如要捕捉 Controller 不存在的情况,需要扩展框架的错误类,另文描述。
thinkjs 官网 API 中有实例化另外一个 Controller 的接口,但是并没有说明这个具体有什么用途:
//实例化 home 模块下 user controllerlet instance = think.controller('user', http, 'home');
那么通常这个方法可以用来实例化兄弟层级 Controller ,或者获取数据、或者触发一个业务流程等,来看代码:
// src/home/controller/user.js 增加_getPoints() { return 8000;}// src/home/controller/index.jslet instance = think.controller('user', this.http, 'home');let points = instance._getPoints();console.log(points); // 打印:8000instance.indexAction(); // 与直接执行 /home/user/index 是一样的效果instance.testAction(); // 报错 [Error] TypeError: instance.testAction is not a function
可见是 thinkjs 提供了一个按需实例化某个 Controller 并运行其方法的途径。
乍看上去这个方式与 this.redirect
运行结果非常接近(除了不会触发 __call
기본 클래스 내보내기</code만 할 수 있는 것이 아니라는 것입니다. > 클래스를 직접 선언하고 동적으로 클래스를 생성하는 메소드도 제공합니다: <code>think.controller
. 🎜🎜하지만 thinkjs가 동적으로 생성한 Class에는 constructor
가 없고 init
를 생성자 메소드 대신 init
를 제공하는데, 이는 와 같은 방식으로 사용됩니다. >생성자
코드> 일관성. 🎜🎜이전 글(Node.js 국내 MVC Framework ThinkJS 개발 컨트롤러 챕터 기본 클래스 및 상속 체인 부분)에도 init
메서드를 사용하는 예가 나와 있습니다. 코드를 다시 살펴보겠습니다. 🎜🎜🎜
// src/home/controller/util.js'use strict';export default class extends think.controller.base { calcGPSDistance(lat, lng){// 计算 GPS 两点直线距离return distance; } calcBaiduDistance(lat, lng){// 计算 百度大地坐标 两点直线距离return distance; } calcSosoDistance(lat, lng){// 计算 Soso坐标 两点直线距离return distance; }}
기본 클래스 내보내기</를 사용하는 데 익숙하다면 <code>constructor
메서드를 사용할 수 없다는 의미는 아닙니다. code>를 사용하여 클래스를 직접 선언하는 경우에도 표준 생성자
메서드를 사용할 수 있습니다. 🎜🎜🎜Thinkjs의 동적으로 클래스 생성 방법은 공식 문서에서 찾을 수 있으므로 여기서는 자세히 설명하지 않습니다. 🎜🎜🎜Magic method🎜🎜thinkjs는 개발에 큰 편의를 제공하는 몇 가지 매우 유용한 Magic Method를 구현했습니다. Manual like~🎜🎜🎜
// src/common/controller/complete.js'use strict';export default class extends think.controller.base { /** * 显示中转页面 * * 调用方式: * let complete = think.controller('complete', this.http, 'common'); * return complete.display('应用新增成功!', '/', 5); * * @param msg 提示文字,支持 HTML * @param url 后续自动跳转的目标地址 * @param delay 停留秒数 * @returns {think.Promise} */ display(msg, url='', delay=3) {let tpl = 'common/complete/200';let opt = think.extend({}, {type: 'base', file_depr: '_', content_type: 'text/html'});this.fetch(tpl, {}, opt).then(content => { content = content.replace(/COMPLETE_MESSAGE/g, msg); if (url) {content = content.replace(/TARGET_URL/g, url);content = content.replace(/WAIT_SECONDS/g, delay); }; this.type(opt['content_type']); return this.end(content);}).catch(function(err){ return this.end('');}); }}
__before
와 init
는 동일한 목적을 가지고 있는 것 같습니다. 평소처럼 코드를 살펴보겠습니다. 🎜🎜🎜
<!-- view/common/complete_200.html --><!DOCTYPE html><html><head><title>正在跳转 - 荆秀网</title></head><body><p class="header"><p class="wrap"><p class="logo"><a href="/"><img src="/static/img/logo.png" alt="XxuYou" width="60"></a></p><p class="headr"> </p></p></p><p class="wrap"><p style="margin-top:20px;height:100px;background:url(/static/img/200.gif) top center no-repeat;"></p><h1>COMPLETE_MESSAGE</h1><p class="error-msg"><pre class="brush:php;toolbar:false">提示:页面将在 <span id="_count">WAIT_SECONDS</span> 秒后重定向到 <a href="TARGET_URL">TARGET_URL</a>