V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
爱意满满的作品展示区。
fundon
V2EX  ›  分享创造

Route Mapper - JavaScript 实现的 Rails Routing 风格的路由系统

  •  
  •   fundon · 2015-01-19 20:41:31 +08:00 · 3166 次点击
    这是一个创建于 3596 天前的主题,其中的信息可能已经有所发展或是发生改变。

    Route Mapper

    JavaScript 实现 (ES6+ 编写)的 Rails Routing 风格的路由系统。
    通过它可以生成 routes mapper,借助 routes mapper 可以进行 DIY ,方便、简化路由入口与其他模块的整合开发。

    基本上实现算法都是参照 actionpack/lib/action_dispatch/routing 及好文 Rails 路由系统源码探索
    使用 JavaScript ES6+ 进行编写开发(跟上时代步伐……)。

    例子(源自 rails new blog 生成路由模板)

    let routeMapper = new RouteMapper();
    routeMapper.draw((m) => {
    
      // You can have the root of your site routed with "root"
      m.root('welcome#index');
    
      // /products/233  controller = catalog, action = view
      m.get('products/:id', { to: 'catalog#view' });
    
      // Example named route that can be invoked with purchase_path(id: product.id)
      // /products/233/purchase === purchase_path(233)
      m.get('products/:id/purchase', { to: 'catalog#purchase', as: 'purchase' });
    
      // Example resource route (maps HTTP verbs to controller actions automatically):
      m.resources('products');
    
      // Example resource route with options:
      m.resources('products', () => {
        m.member(() => {
          m.get('short');
          m.post('toggle');
        });
    
        m.collection(() => {
          m.get('sold');
        });
      });
    
      // Example resource route with sub-resources:
      m.resources('products', () => {
        m.resources('comments', 'sales');
        m.resource('seller');
      });
    
      // Example resource route with more complex sub-resources:
      m.resources('products', () => {
        m.resources('comments')
        m.resources('sales', () => {
          m.get('recent', { on: 'collection' });
        });
      });
    
      // Example resource route with concerns:
      m.concern('toggleable', () => {
        m.post('toggle');
      });
      m.resources('posts', { concerns: 'toggleable' });
      m.resources('photos', { concerns: 'toggleable' });
    
      // Example resource route within a namespace:
      m.namespace('admin', () => {
        // Directs /admin/products/*
        m.resources('products');
      });
    
    });
    

    结合现有 Node.js 的 web frameworks

    Express

    import express from 'express';
    import RouteMapper from '../..';
    
    let app = express();
    
    let routeMapper = new RouteMapper();
    routeMapper.draw((m) => {
      m.root('welcome#index');
      m.resources('photos');
      m.constraints({ subdomain: 'api' }, () => {
        m.namespace('api',  { defaults: { format: 'json' }, path: '/' }, () => {
            m.scope({ module: 'v1' }, () => {
              m.resources('users');
            });
          }
        );
      });
    });
    
    
    app.use(function (req, res, next) {
      res.locals.urlHelpers = routeMapper.urlHelpers;
      next();
    });
    
    routeMapper.routes.forEach((r) => {
      r.via.forEach((m) => {
        let controller = r.controller;
        let action = r.action;
        let c = require(__dirname + '/controllers/' + controller + '.js');
        let a;
        if (c && (a = c[action])) {
          if (!Array.isArray(a)) {
            a = [a];
          }
          app[m](r.path, ...a);
        };
      });
    });
    
    app.listen(3300);
    

    Koa

    import koa from 'koa';
    import router from 'koa-router';
    import RouteMapper from '../..';
    
    let app = koa();
    
    let routeMapper = new RouteMapper();
    routeMapper.draw((m) => {
      m.root('welcome#index');
      m.get('about', { to: 'welcome#about' });
      m.resources('posts', () => {
        m.resources('comments');
      });
      m.scope({ path: '~:username?', module: 'users', as: 'user'}, () => {
        m.root('welcome#index');
      });
    });
    
    app.use(function *(next) {
      this.urlHelpers = routeMapper.urlHelpers;
      yield next;
    });
    
    app.use(router(app));
    
    routeMapper.routes.forEach((r) => {
      r.via.forEach((m) => {
        let controller = r.controller;
        let action = r.action;
        let c = require(__dirname + '/controllers/' + controller + '.js');
        let a;
        if (c && (a = c[action])) {
          if (!Array.isArray(a)) {
            a = [a];
          }
          app[m](r.path, ...a);
        };
      });
    });
    
    app.listen(3300);
    

    其他

    Rails 在这些地方的设计确实很棒,Express.js,Koa.js 等 Node.js Web 框虽然 DIY 等能力很强,但总感觉过于松散,每次下手都得对目录结构想一番。
    该模块还在不懂的完善中。:)

    2 条回复    2015-01-23 14:09:15 +08:00
    GPU
        1
    GPU  
       2015-01-19 23:45:10 +08:00 via iPhone
    轮子哦,赞赞
    Temo
        2
    Temo  
       2015-01-23 14:09:15 +08:00 via iPhone
    赞一个!
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2680 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 28ms · UTC 10:52 · PVG 18:52 · LAX 02:52 · JFK 05:52
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.