Vue自动化路由(基于Vue-Router)开篇
vue自动化路由
好久不见~ 若羽又开篇Vue的内容了。
年初的时候发布了第一版的ea-router
自动化路由库,欢迎大家安装使用。[Github地址] [npm地址]
经历一年的使用。还是发现了不少问题和不足的地方,因此在前段时间抽空整理了所有需求并做了个规划。并发布了一个版本。下面来看看其中的原理和实现吧。
前言
因为之前都是写后端逻辑,因此接触前端后始终不太习惯js的原生语法。更偏向于es6的class
写法,并且从ECMAScript后续的标准来看,官方也是比较推荐class
的写法来更好的组织代码,并使其具有更强的表义性。哈哈,当然因为更熟悉后者,所以更偏袒一点。
功能需求
功能主要分为两部分:
- 路由自动化
- 服务于库的装饰器
路由自动化中,除了原有的自动生成外,还增加了另外两个在业务中会经常使用到的功能:
- 设置缺省的Layout
- 设置缺省的404页面
目录中的子目录关系,用路由中嵌套路由来进行表达,因此需要一个入口进行渲染,这就是Layout
存在的一个意义,另外一层则是作为某个模块的通用布局存在。
装饰器主要用于补充路由相关特性,比如vue-router
中的各种特性(命名路由,别名,params等等),无缝的接入业务中。就像vue-property-decorators
库一样。
原理
为了达成自动化路由的目的,本质就是要将路由对象按照某种特定的规则进行生成即可。那么参考后端MVC中的路由
以及其他前端路由框架,将需要路由的页面按照目录的层次结构进行组织,然后对目录进行解析是比较通用并容易实现的。
- 扫描目录文件
- 还原目录结构
- 转换为目录对象
- 加载适配器(默认为
vue-router
的适配器) - 适配器将目录对象转换为
routes
- 使用
routes
目录对象
将实际的目录结构映射成对象,下面看一个例子:
目录结构如下:
views
|-- About.vue
|-- Home.vue
|-- Layout.vue
|-- user
|-- Add.vue
router/index.js
代码如下:
// /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;
对应vue-router
,自动生成的路由对象会是如下形式(里面的对象是自动生成的,导出语句不是喔,只是为了演示):
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;
因为使用的是webpack
的require.context
函数,但是它有一个缺陷就是扫描出来的并不是目录原来的层次结构。而是一维的结构,因此我们首先要还原原来的层次结构,并在此基础上封装、解析一些必要的信息。
适配器
适配者模式在这个场景下非常合适,输入是解析后的目录对象,而输出则是变化的。有可能是:
vue-router
的路由对象routes
vue-router-next
的路由对象routes
- 其他路由框架的路由对象
想要适配其他框架, 则只需要实现对应的适配器并加载即可。
使用
目前有3个api以及5个装饰器
api:
decorators:
generate
api
构造函数中传入通过require.context
指定目录及过滤规则, 如下实例是指定views
目录下所有.vue
文件。
路由生成的api, 调用此方法将生成一个对应 路由适配器 生成的路由对象,目前默认内置的时基于vue 2.x
的vue-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
},
}
]
})
建议
在开发过程中,使用Class
形式的写法是最为推荐的,表义性和组织性会更强一些。配合vue-property-decorators
食用更佳喔。好了,接下来说正经的:
- 路由跳转,建议使用命名路由的跳转方式。去查看相关文档
- 给路由命名,并统一定义路由的名称,便于管理(如,都定义在
/src/domain/views.js
中)。 - 路由上下文使用
Props
进行传参。
计划
- 实现
vue-router-next
的适配器 - 实现路由文件的自动生成(基于模板语法)
- 添加可设置所有选项配置的装饰器
- 开放加载自定义适配器
- typescript支持
- 回补单元测试
总结
做这个库之前,也查找了很多相关资料。并且翻了不少类似库的源码进行学习,发现比较常见的做法:
- 动态加载,即请求时去
import
实现动态加载。但这个只是做了自动寻找路由,对于路由的组织还是没有比较好的解决。 webpack
动态解析路径,通过正则表达式或者vue单文件组件解析器对文件进行解析,提取内容。这种方式非常接近本文中的方式,但是缺点也比较明显:不支持变量,如果全部硬编码到文件里,管理也是一个问题。
最后结合大家的经验,实现了这个库。下一步也会考虑开始实现生成路由文件,补充这一块的空白。
关于自动化路由这部分,将会从分析、实现、使用以及后续开发都会记录下来,并且会开源用了此库的一些个人项目,形成系列文章。这篇就当是起个头,如有不足,欢迎各位指正。
~另外欢迎大家使用并提出宝贵的意见哟
Vue自动化路由(基于Vue-Router)开篇的更多相关文章
- vue学习指南:第十一篇(详细) - Vue的 路由 第一篇 ( router )
一.路由的配置 路由 vue-router 1. 什么是路由? 路由相当于一个配置对象 路由:就是我们通过不同的url访问不同的内容,通过angular.js 可以实现多视图的单页,现在流行的单页面 ...
- vue 自动化路由实现
1.需求描述 在写vue的项目中,一般情况下我们每添加一个新页面都得添加一个新路由.为此我们在项目中会专门的一个文件夹来管理路由,如下图所示 那么有没有一种方案,能够实现我们在文件夹中新建了一个vue ...
- 【Vue.js】基于vue的实时搜索,在结果中高亮显示关键词
一.搜素效果如下: 二.核心 1)利用oninput属性来触发搜素功能 2)利用RegExp来对字符串来全局匹配关键字,利用replace方法来对匹配的关键字进行嵌入高亮的<span class ...
- vue 快速入门 系列 —— vue loader 扩展
其他章节请看: vue 快速入门 系列 vue loader 扩展 在vue loader一文中,我们学会了从零搭建一个简单的,用于单文件组件开发的脚手架.本篇将在此基础上继续引入一些常用的库:vue ...
- vue 组件开发、vue自动化工具、axios使用与router的使用(3)
一. 组件化开发 1.1 组件[component] 在网页中实现一个功能,需要使用html定义功能的内容结构,使用css声明功能的外观样式,还要使用js定义功能的特效,因此就产生了一个功能先关的代码 ...
- 浅入深出Vue:自动化路由
在软件开发的过程中,"自动化"这个词出现的频率是比较高的.自动化测试,自动化数据映射以及各式的代码生成器.这些词语的背后,也说明了在软件开发的过程中,对于那些重复.千篇一律的事情. ...
- 开发基于vue前端框架下的系统的UI自动化,记录总结踩的坑
在使用了pytest完成了一个系统的UI自动化后,因为系统的前端框架,是 基于VUE写的,这就让我编写脚本的时候踩了些坑. 无法用JS 修改标签属性,从而进行的操作 比如上传图片,我们的上传是这样子的 ...
- Vue.js路由管理器 Vue Router
起步 HTML <script src="https://unpkg.com/vue/dist/vue.js"></script> <script s ...
- Vue的路由Router之导航钩子和元数据及匹配
一.文件结构 二.vue.js 打开此链接 https://cdn.bootcss.com/vue/2.6.10/vue.js 复制粘贴页面的所有内容 三.vue-router.js 打开此链接 h ...
随机推荐
- urllib.request.urlopen(req).read().decode解析http报文报“utf-8 codec can not decode”错处理
老猿前期执行如下代码时报"'utf-8' codec can't decode byte"错,代码及错误信息如下: >>> import urllib.reque ...
- PyQt(Python+Qt)学习随笔:Designer中ItemViews类部件的frameShadow属性
老猿Python博文目录 老猿Python博客地址 frameShadow属性是从QFrame继承的属性,对应类型为QFrame.Shadow,该属性表示框架提供三维效果的阴影类型,有如下取值: 可以 ...
- C#声明一个100大小的数组 随机生成1-100之间不重复的数
面试题:C#声明一个100大小的数组 随机生成1-100之间不重复的数下面是C#的实现方式,编译测试通过 public static void InsertRandomArray() { int[] ...
- 题解 CF1437G Death DBMS
这题感觉不是很难,但是既然放在 \(\texttt{EDU}\) 的 \(\texttt{G}\) 题,那么还是写写题解吧. \(\texttt{Solution}\) 首先看到 "子串&q ...
- 题解-Cats Transport
题解-Cats Transport Cats Transport 有 \(n\) 个山丘,\(m\) 只猫子,\(p\) 只铲屎官.第 \(i-1\) 个山丘到第 \(i\) 个山丘的距离是 \(d_ ...
- 最近有安装了一次hadoop集群,NameNode启动失败,及原因
最近有安装了一次hadoop集群,NameNode启动失败,查看日志,找到以下原因: 遇到的异常1: org.apache.hadoop.hdfs.server.common.Inconsistent ...
- sort by背后使用了什么排序算法
用到了快速排序,但不仅仅只用了快速排序,还结合了插入排序和堆排序 搬运自https://blog.csdn.net/qq_35440678/article/details/80147601
- C 与 C++ 中 指向二维数组的指针进行指针运算
二维数组在概念上是二维的,有行和列,但在内存中所有的数组元素都是连续排列的,它们之间没有"缝隙".以下面的二维数组 nums 为例: 从概念上理解,nums 的分布像一个矩阵,但在 ...
- 实验:非GTID 一主多从变级联架构
- MyBatis详细源码解析(上篇)
前言 我会一步一步带你剖析MyBatis这个经典的半ORM框架的源码! 我是使用Spring Boot + MyBatis的方式进行测试,但并未进行整合,还是使用最原始的方式. 项目结构 导入依赖: ...