Extend / 扩展

虽然框架内置了很多功能,但在实际项目开发中,提供的功能还是远远不够的。3.0 里引入了扩展机制,方便对框架进行扩展。支持的扩展类型为:thinkapplicationcontextrequestresponsecontrollerlogicservice

框架内置的很多功能也是扩展来实现的,如:SessionCache

扩展配置

扩展配置文件路径为 src/config/extend.js多模块项目文件路径为 src/common/config/extend.js),格式为数组:

const view = require('think-view');

module.exports = [
  view //make application support view
];

如上,通过 view 扩展框架就支持渲染模板的功能,Controller 类上就有了 assigndisplay 等方法。

项目里的扩展

除了引入外部的 Extend 来丰富框架的功能,也可以在项目中对对象进行扩展,扩展文件放在 src/extend/ 目录下(多模块项目放在 src/common/extend/ 下)。

  • src/extend/think.js 扩展 think 对象,think.xxx
  • src/extend/application.js 扩展 Koa 里的 app 对象(think.app)
  • src/extend/request.js 扩展 Koa 里的 request 对象(think.app.request)
  • src/extend/response.js 扩展 Koa 里的 response 对象(think.app.response)
  • src/extend/context.js 扩展 ctx 对象(think.app.context)
  • src/extend/controller.js 扩展 controller 类(think.Controller)
  • src/extend/logic.js 扩展 logic 类(think.Logic)- logic 继承 controller 类,所以 logic 包含 controller 类所有方法
  • src/extend/service.js 扩展 service 类(think.Service)

比如:我们想给 ctx 添加个 isMobile 方法来判断当前请求是不是手机访问,可以通过下面的方式:

// src/extend/context.js
module.exports = {
  isMobile(){
    const userAgent = this.userAgent.toLowerCase();
    const mList = ['iphone', 'android'];
    return mList.some(item => userAgent.indexOf(item) > -1);
  }
}

这样后续就可以通过 ctx.isMobile() 来判断是否是手机访问了。当然这个方法没有任何的参数,我们也可以变成一个 getter

// src/extend/context.js
module.exports = {
  get isMobile(){
    const userAgent = this.userAgent.toLowerCase();
    const mList = ['iphone', 'android'];
    return mList.some(item => userAgent.indexOf(item) > -1);
  }
}

这样在 ctx 中就可以直接用 this.isMobile 来使用,其他地方通过 ctx.isMobile 使用,如: 在 controller 中用 this.ctx.isMobile

如果在 controller 中也想通过 this.isMobile 使用,怎么办呢? 可以给 controller 也扩展一个 isMobile 属性来完成。

// src/extend/controller.js
module.exports = {
  get isMobile(){
    return this.ctx.isMobile;
  }
}

通过也给 controller 扩展 isMobile 属性后,后续在 controller 里可以直接使用 this.isMobile 了。

当然这样扩展后,只能在当前项目里使用这些功能,如果要在其他项目中使用,可以将这些扩展发布为一个 npm 模块。发布的模块在入口文件里需要定义对应的类型的扩展,如:

const controllerExtend = require('./controller.js');
const contextExtend = require('./context.js');

// 模块入口文件
module.exports = {
  controller: controllerExtend,
  context: contextExtend
}

扩展里使用 app 对象

有些 Extend 需要使用一些 app 对象上的数据,那么可以导出为一个函数,配置时把 app 对象传递进去即可。

// src/config/extend.js
const model = require('think-model');
module.exports = [
  model(think.app) //将 think.app 传递给 model 扩展
];

当然除了传 app 对象,也可以根据需要传递其他对象。

推荐扩展列表

推荐的 Extend 列表见 https://github.com/thinkjs/think-awesome#extends,如果你开发了比较好的 Extend,也欢迎发 Pull Request。

常见问题

多个扩展提供的方法重名了怎么办?

如果多个扩展提供的方法重名了,那么后面的扩展会覆盖前面扩展的方法,所以可以根据调整顺序来决定如何覆盖。另:创建扩展时尽快使用有意义的方法名,不要使用太过于简单的方法名。

扩展的方法名不可与框架内置的方法重名,重名的话可能会引起一些奇怪的问题。

如发现文档中的错误,请点击这里修改本文档,修改完成后请 pull request,我们会尽快合并、更新。