ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
# 路由 [TOC=2,3] 当用户访问一个 URL 时,最终执行哪个模块下哪个控制器的哪个操作,这是由路由来解析后决定的。 ThinkJS 提供了一套灵活的路由机制,除了默认的解析外,还可以支持多种形式的自定义路由,让 URL 更加简单友好。 ## URL 解析为 pathname 当用户访问服务时,服务端首先拿到的是一个完整的 URL,如:访问本页面,得到的 URL 为`http://www.thinkjs.org/zh-CN/doc/2.0/route.html`。 将 URL 进行解析得到的 pathname 为 `/zh-CN/doc/2.0/route.html`。 ## pathname 过滤 有时候为了搜索引擎优化或者一些其他的原因, URL 上会多加一些东西。比如:当前页面是一个动态页面,但 URL 最后加了 `.html`,这样对搜索引擎更加友好。但这些在后续的路由解析中是无用的,需要去除。 ThinkJS 里提供了下面的配置可以去除 `pathname` 的前缀和后缀内容: ~~~ export default { pathname_prefix: "", pathname_suffix: ".html", } ~~~ 上面配置可以在 `src/common/config/config.js` 中进行修改。 pathname 过滤时会自动去除左右的 `/`,该逻辑不受上面的配置影响。对 pathname 进行过滤后,拿到干净的 pathname 为 `zh-CN/doc/2.0/route`。 > 注: 如果访问的 URL 是 `http://www.thinkjs.org/`,那么最后拿到干净的 pathname 则为空字符串。 ## 子域名部署 当项目比较复杂时,可能希望将不同的功能部署在不同的域名下,但代码还是在一个项目下。如:域名`admin.example.com` 部署后台管理的功能,希望映射到 `admin` 模块下。 ThinkJS 提供了如下的配置可以进行子域名部署,该配置可以在 `config/config.js` 里设置: ~~~ export default { subdomain: { admin: "admin", //表示将 admin.example.com 映射到 admin 模块下 ... } } ~~~ 假如过滤后的 pathname 为 `group/detail`,命中了 admin.example.com 这个子域名后,pathname 变为`admin/group/detail`。 ## 路由识别 ### 路由解析 路由识别默认根据 `模块/控制器/操作/参数1/参数1值/参数2/参数2值` 来识别过滤后的 pathname,如:pathname 为 `admin/group/detail`,那么识别后的结果为: * module 为 `admin` * controller 为 `group` * action 为 `detail`,对应的方法名为 `detailAction` 如果项目里并没有 `admin` 这个模块或者这个模块被禁用了,那么识别后的结果为: * module 为默认模块 `home` * controller 为 `admin` * action 为 `group`,对应的方法名为 `groupAction` * 参数为 `{detail: ''}` ### 大小写转化 路由识别后,`module`、`controller` 和 `Action` 值都会自动转为小写。如果 Action 值里有 `_`,会作一些转化,如:假设识别后的 Controller 值为 `index`,Action 值为 `user_add`,那么对应的 Action 方法名为`userAddAction`,但模版名还是 `index_user_add.html`。 ## 路由默认值 当解析 pathname 没有对应的值时,此时便使用对应的默认值。其中 module 默认值为 `home`,controller 默认值为 `index`,action 默认值为 `index`。 这些值可以通过下面的配置进行修改,配置文件 `src/common/config/config.js`: ~~~ export default { default_module: "home", default_controller: "index", default_action: "index", } ~~~ ## 自定义路由 默认的路由虽然看起来清晰明了,解析起来也很简单,但看起来不够简洁。 有时候需要更加简洁的路由,这时候就需要使用自定义路由解析了。如:文章的详细页面,默认路由可能是:`article/detail/id/10`,但我们想要的 URL 是 `article/10` 这种更简洁的方式。 #### 开启配置 开启自定义路由,需要在 `src/common/config/config.js` 开启如下的配置: ~~~ export default { route_on: true } ~~~ #### 路由规则 开启自定义路由后,就可以通过路由规则来定义具体的路由了,路由配置文件为:`src/common/config/route.js`,格式如下: ~~~ export default [ ["规则1", "需要识别成的pathname"], ["规则2", { get: "get请求下需要识别成的pathname", post: "post请求下需要识别成的pathname" }] ]; ~~~ > 注: 自定义路由每一条规则都是一个数组。(至于为什么不用对象,是因为正则路由下,正则不能作为对象的 key 直接使用) #### 识别方式 自定义路由的匹配规则为:从前向后逐一匹配,如果命中到了该项规则,则不在向后匹配。 * * * ThinkJS 支持 3 种类型的自定义路由,即:正则路由,规则路由和静态路由。 ### 正则路由 正则路由是采用正则表示式来定义路由的一种方式,依靠强大的正则表达式,能够定义非常灵活的路由规则。 ~~~ export default [ [/^article\/(\d+)$/, "home/article/detail?id=:1"] ]; ~~~ 上面的正则会匹配类似 `article/10` 这样的 pathname,识别后新的 pathname 为 `home/article/detail`,并且将捕获到的值赋值给参数 id ,这样在控制器里就可以通过 `this.get` 方法 来获取该值。 ~~~ export default class extends think.controller.base { detailAction(){ let id = this.get("id"); } } ~~~ 如果正则里含有多个子分组,那么可以通过 `:1`,`:2`,`:3` 来获取对应的值。 ~~~ export default [ [/^article\/(\d+)$/, { get: "home/article/detail?id=:1", delete: "home/article/delete?id=:1", post: "home/article/save?id=:1" }] ]; ~~~ ### 规则路由 规则路由是一种字符串匹配方式,但支持含有一些动态的值。如: ~~~ export default [ ["group/:year/:month", "home/group/list"] ] ~~~ 假如访问的 URL 为 `http://www.example.com/group/2015/10`,那么会命中该项规则,得到的 pathname 为`home/group/list`,同时会添加 2 个参数 `year` 和 `month`,这2个参数可以在控制器里通过 `this.get` 方法来获取。 ~~~ export default class extends think.controller.base { listAction(){ let year = this.get("year"); let month = this.get("month"); } } ~~~ ### 静态路由 静态路由是一种纯字符串的完全匹配方式,写法和识别都很简单,功能也相对要弱很多。 ~~~ export default [ ["list", "home/article/list"] ] ~~~ 假如访问的 URL 为 `http://www.example.com/list`,那么替换后的 pathname 为 `home/article/list`。 ### 优化路由性能 上面已经说到,自定义路由是个数组,数组每一项是个具体的路由规则,匹配时是从前向后逐一进行匹配。如果这个路由表比较大的话,可能会有性能问题。 为了避免有性能问题,ThinkJS 提供了一种更加高效的自定义路由方式,按模块来配置路由。使用这种方式,路由配置格式跟上面稍微有所不同。 #### common/config/route.js 使用这种方式后,通用模块里的路由配置不再配置具体的路由规则,而是配置哪些规则命中到哪个模块。如: ~~~ export default { admin: { reg: /^admin/ //命中 admin 模块的正则 }, home: { //默认走 home 模块 } } ~~~ #### admin/config/route.js admin 模块配置 admin 下的具体路由规则。 ~~~ export default [ [/^admin\/(?!api).*$/, "admin/index"], [/^admin\/api\/(\w+?)(?:\/([\d,]*))?$/, "admin/:1?id=:2&resource=:1"], ]; ~~~ * * * 假设访问的 URL 为 `http://www.example.com/admin/api`,那么解析后的 pathname 为 `admin/api`,匹配`common` 里的规则时会命中 `admin` 模块,然后再对 `admin` 模块下的路由规则进行逐一匹配。通过这种方式后就可以大大减少路由规则匹配的数量,提供匹配效率。