/ea-router

自动根据目录结构生成基于 vue-router 的路由对象,无需手动设置。

Primary LanguageJavaScriptMIT LicenseMIT

构建状态

ea-router

ea-router意为:easy auto router。

目的是为了减少人工手动维护路由表的工作,在多人协作时避免大量冲突。解放开发者,专注于业务开发。

原理是基于目录映射自动生成路由解析结果并根据适配器模式加载对应的适配器将解析结果转换成相对应的路由对象, 从而实现自动映射路由的功能。

目前只实现了针对于 vue-router 3.x 的适配器,后续将会增加更多的适配器,也欢迎大家贡献代码。

建议搭配 vue-property-decorator 使用更香噢!

简单原理如下图:

features

  • 自动将目录映射成路由表
  • 重构项目,改善灵活性
  • 路由装饰器,移除组件内硬编码
  • 可设置默认Layout
  • 可设置 NotFound 对应的路由页面
  • 抽离目录 => 路由对象映射逻辑,利用适配器模式,以便灵活支持不同的路由框架
  • 实现 vue-router 3.x 的适配器
  • 实现 vue-router-next 的适配器
  • 添加忽略模式支持
  • 实现路由文件的自动生成(基于模板语法)
  • 添加可设置所有选项配置的装饰器
  • 开放加载自定义适配器
  • typescript支持
  • 回补单元测试

Install

npm i -S ea-router

examples

目录结构如下:

views
|-- About.vue
|-- Home.vue
|-- Layout.vue
|-- user
    |-- Add.vue

生成代码如下:

// /src/router/index.js

import Vue from "vue";
import VueRouter from "vue-router";
import AutoRouteGenerator from "ea-router";
import defaultLayout from "../components/defaultLayout";
import notFoundPage from "../components/notFound";

Vue.use(VueRouter);
let generator = new AutoRouteGenerator(
  require.context("../views", true, /\.vue$/)
);

generator.setDefaultLayout(defaultLayout);
generator.setNotFoundPage(notFoundPage);

const routes = generator.generate();
const router = new VueRouter({
  routes
});

export default router;

项目入口:

// src/main.js

import Vue from 'vue'
import App from './App.vue'
import router from './router'

Vue.config.productionTip = false

new Vue({
  router,
  render: h => h(App)
}).$mount('#app')

最终自动生成的路由表(运行时并不会产生这个文件,这里只是为了展示自动产生的路由对象会是什么样):

const routes = [
  {
    path: "/",
    component: () => import("src/views/layout.vue"),

    children: [
      {
        path: "home/:id/:name",
        component: () => import("src/views/home.vue"),
        props: true
      },

      {
        path: "about",
        component: () => import("src/views/about.vue")
      },

      {
        path: "user",
        component: () => import("src/components/defaultLayout.vue"),

        children: [
          {
            path: "add",
            component: () => import("src/views/user/add.vue")
          }
        ]
      }
    ]
  },

  {
    path: "*",
    component: () => import("src/components/notFound.vue")
  }
];
export default routes;

rules

既然是自动化, 那么当然是需要遵循一定的约定:

  • 目录下每个.vue文件即是一个路由对象,即对应一个 url
  • 目录下的Layout.vue会自动被注册为当前目录下的跟路由
  • 多级目录结构下,每个目录应该携带Layout.vue,以便逐层渲染下来(当然也可以使用api来设置默认的)

Usage

目前有4个api以及5个装饰器

api:

decorators:

ignore api

在路由文件下,有时候会存在与页面紧密耦合的组件,比如布局(Header, Footer, NavMenu之类)。但又不属于路由,因此可以使用忽略模式进行忽略。

参数为文件/目录匹配的写法,注意一点**./ 表示的是指定的路由文件夹,因此必须加上**

下面示范一下忽略掉views目录下所有componentslayouts 目录的写法。

// src/router/index.js

import Vue from 'vue'
import Router from 'vue-router'
import RouteGenerator from "ea-router";

Vue.use(Router)
let generator = new RouteGenerator(require.context('./views', true, /\.vue$/))
generator.ignore('./**/components/*', './**/layouts/*');

export default new Router({
  routes: generator.generate()
})

上述操作后,生成的路由里会忽略掉这两种文件夹下所有内容。且不会出现在路由里。

generate api

构造函数中传入通过require.context指定目录及过滤规则, 如下实例是指定views目录下所有.vue文件。

路由生成的api, 调用此方法将生成一个对应 路由适配器 生成的路由对象,目前默认内置的时基于vue 2.xvue-router

// src/router/index.js

import Vue from 'vue'
import Router from 'vue-router'
import RouteGenerator from "ea-router";

Vue.use(Router)
let generator = new RouteGenerator(require.context('./views', true, /\.vue$/))

export default new Router({
  routes: generator.generate()
})

那么在 main.js 中,我们不用改动原有的代码即可直接使用:

// src/main.js

import Vue from 'vue'
import App from './App.vue'
import router from './router'

Vue.config.productionTip = false

new Vue({
  router,
  render: h => h(App)
}).$mount('#app')

setDefaultLayout api

指定默认的Layout.vue,因为在大多数情况下,Layout.vue的内容可能都是下面这样:

<template>
    <router-view></router-view>
</template>

这种情况下,Layout.vue的目的仅仅是作为子路由的入口。那么我们可以直接利用setDefaultLayout来设置默认的Layout。 规则如下:

  • 当当前目录中没有Layout.vue时,会尝试使用设置的默认Layout
  • 当没有Layout.vue并且没有设置默认Layout时,将会抛出异常。

实例:

// src/router.js

import Vue from 'vue'
import Router from 'vue-router'
import RouteGenerator from "ea-router";
import DefaultLayout from './components/defaultLayout.vue';

Vue.use(Router)
let generator = new RouteGenerator(require.context('./views', true, /\.vue$/))
generator.setDefaultLayout(DefaultLayout);

export default new Router({
  routes: generator.generate()
})
<!-- /src/components/defaultLayout.vue -->
<template>
    <router-view></router-view>
</template>

setNotFoundPage api

设置路由匹配失败时显示的页面。

实例:

// src/router.js

import Vue from 'vue'
import Router from 'vue-router'
import RouteGenerator from "ea-router";
import NotFoundPage from './components/notFound.vue';

Vue.use(Router)
let generator = new RouteGenerator(require.context('./views', true, /\.vue$/))
generator.setNotFoundPage(NotFoundPage);

export default new Router({
  routes: generator.generate()
})
<!-- /src/components/notFound.vue -->
<template>
    <div>
        嘿,页面走丢啦!    
    </div>
</template>

@RouteName(name: string) decorator

设置路由名称,在vue-router中对应了命名路由

import { Vue, Component } from 'vue-property-decorator';
import { RouteName } from 'ea-router';

@RouteName('YourComponentRouteName')
@Component
export default class YourComponent extends Vue {
}

等价于

const router = new VueRouter({
  routes: [
    { 
        path: 'path/to/component/on/directory', 
        name: 'YourComponentRouteName',
        component: YourComponent,
    }
  ]
})

Note that: path的生成规则是相对路径噢(根目录是构造函数中传入的目录,示例中也就是src/views

@Alias(alias: string) decorator

设置路由别名,对应vue-router中的别名

import { Vue, Component } from 'vue-property-decorator';
import { Alias } from 'ea-router';

@Alias('YourComponentAlias')
@Component
export default class YourComponent extends Vue {
}

等价于

const router = new VueRouter({
  routes: [
    { 
        path: 'path/to/component/on/directory', 
        alias: 'YourComponentAlias',
        component: YourComponent,
    }
  ]
})

@Context(params: string[]) decorator

设置路由上下文,对应了vue-router中的$routes.params

会根据传入的顺序生成path

import { Vue, Component } from 'vue-property-decorator';
import { Context } from 'ea-router';

@Context('id', 'type')
@Component
export default class YourComponent extends Vue {
}

等价于

const router = new VueRouter({
  routes: [
    { 
        path: 'path/to/component/on/directory/:id/:type',
        component: YourComponent,
    }
  ]
})

Note that: 如果同时使用 @Alias@Context, 上下文的参数会自动添加在alias后面, 就像下面的例子:

import { Vue, Component } from 'vue-property-decorator';
import { Context, Alias } from 'ea-router';

@Alias('YourComponentAlias')
@Context('id', 'type')
@Component
export default class YourComponent extends Vue {
}

等价于

const router = new VueRouter({
  routes: [
    { 
        path: 'path/to/component/on/directory/:id/:type',
        alias:'YourComponentAlias/:id/:type',
        component: YourComponent,
    }
  ]
})

@EnableProps() decorator

开启路由参数的Boolean模式, 对应了vue-router中的路由传参-布尔模式

import { Vue, Component } from 'vue-property-decorator';
import { EnableProps } from 'ea-router';

@EnableProps()
@Component
export default class YourComponent extends Vue {
}

等价于

const router = new VueRouter({
  routes: [
    { 
        path: 'path/to/component/on/directory',
        props: true,
        component: YourComponent,
    }
  ]
})

Note that: 一般搭配 @Context 使用。

@Meta(meta: object) decorator

设置路由元信息,对应vue-router中的路由元信息

import { Vue, Component } from 'vue-property-decorator';
import { Meta } from 'ea-router';

@Meta({
    title: 'Component Title',
    requireAuthorize: true
})
@Component
export default class YourComponent extends Vue {
}

等价于

const router = new VueRouter({
  routes: [
    { 
        path: 'path/to/component/on/directory',
        component: YourComponent,
        meta: {
                  title: 'Component Title',
                  requireAuthorize: true
              },
    }
  ]
})

about

halo, 我是若羽,如果这个库能对你有所帮助那是最好的。 如果使用过程中有什么问题欢迎随意提出issues,或者直接邮箱联系。

欢迎大家贡献代码,不断完善它。

License

MIT License