自己动手封装一个vuecli3自动生成路由插件

在使用vue spa时,配置路由是很重要的一环。众所周知nuxtjs中具备了依据pages文件自动生成vue-router 模块的路由配置。

此文将会引用nuxtjs中该部分源码,并稍加改动,生成一个vue-cli3的插件

nuxt官方网站

第一部分

nuxt自动生成路由的使用方式

nuxt会根据pages下的文件自动生成路由并引入,支持vue-router的基础路由,动态路由,嵌套路由等。基础路由很简单,需要注意的是,在使用动态路由时,需要创建对应的以下划线作为前缀的 Vue文件或目录

//pages文件
pages/
--| users/
-----| _id.vue
--| index.vue
//生成的路由
router: {
  routes: [{
           name: 'index',
           path: '/',
           component: 'pages/index.vue'
          },
          {
           name: 'users-id',
           path: '/users/:id?',
           component: 'pages/users/_id.vue'
          }]
        }

创建内嵌子路由,你需要添加一个 Vue 文件,同时添加一个与该文件同名的目录用来存放子视图组件。

pages/
--| users/
-----| _id.vue
-----| index.vue
--| users.vue
//生成的路由
router: {
  routes: [
    {
      path: '/users',
      component: 'pages/users.vue',
      children: [
        {
          path: '',
          component: 'pages/users/index.vue',
          name: 'users'
        },
        {
          path: ':id',
          component: 'pages/users/_id.vue',
          name: 'users-id'
        }
      ]
    }
  ]
}

nuxt源码展示

源码地址
分析一下nuxtjs源码,主要由以下几部分完成此功能

  1. generateRoutesAndFiles方法,引入glob库对pages下的文件进行遍历和字符串处理,然后将vue文件地址,整个项目地址和pages作为参数传给createRoutes方法
//builder.js
 else if (this._nuxtPages) {
      // Use nuxt.js createRoutes bases on pages/
      const files = {}
        ; (await glob(`${this.options.dir.pages}/**/*.{vue,js}`, {
        cwd: this.options.srcDir,
        ignore: this.options.ignore
      })).forEach((f) => {
        const key = f.replace(/\.(js|vue)$/, '');
        if (/\.vue$/.test(f) || !files[key]) {
          files[key] = f.replace(/('|")/g, '\\$1');
        }
      });
      templateVars.router.routes = common.createRoutes(
        Object.values(files),
        this.options.srcDir,
        this.options.dir.pages
      );
    }

2.在createRoutes函数中对传过来的所有文件地址进行遍历,再对每一个文件地址字符串处理,以中划线进行拼接。以此作为route.name

//common.js
const createRoutes = function createRoutes(files, srcDir, pagesDir) {
  const routes = [];
  files.forEach((file) => {
    const keys = file
      .replace(RegExp(`^${pagesDir}`), '')
      .replace(/\.(vue|js)$/, '')
      .replace(/\/{2,}/g, '/')
      .split('/')
      .slice(1);
    const route = { name: '', path: '', component: r(srcDir, file) };
    let parent = routes;
    keys.forEach((key, i) => {
      // remove underscore only, if its the prefix
      const sanitizedKey = key.startsWith('_') ? key.substr(1) : key;

      route.name = route.name
        ? route.name + '-' + sanitizedKey
        : sanitizedKey;
      route.name += key === '_' ? 'all' : '';
      route.chunkName = file.replace(/\.(vue|js)$/, '');
      const child = parent.find(parentRoute => parentRoute.name === route.name);

      if (child) {
        child.children = child.children || [];
        parent = child.children;
        route.path = '';
      } else if (key === 'index' && i + 1 === keys.length) {
        route.path += i > 0 ? '' : '/';
      } else {
        route.path += '/' + getRoutePathExtension(key);

        if (key.startsWith('_') && key.length > 1) {
          route.path += '?';
        }
      }
    });
    parent.push(route);
  });

  sortRoutes(routes);
  return cleanChildrenRoutes(routes)
};

至此,经过这两个方法处理之后的内容,大致上就是我们想要的东西

//生成一个数组对象
[{
       name: 'index',
       path: '/',
       component: 'pages/index.vue'
  },
  {
       name: 'users-id',
       path: '/users/:id?',
       component: 'pages/users/_id.vue'
}]

第二部分

vue-cli3插件

具体请移步vue-cli3插件开发指南

//插件目录
├── README.md
├── generator.js  # generator (可选)
├── prompts.js    # prompt 文件 (可选)
├── index.js      # service 插件
└── package.json
//index.js
module.exports = (api, projectOptions) => {
  api.chainWebpack(webpackConfig => {
    // 通过 webpack-chain 修改 webpack 配置
  })

  api.configureWebpack(webpackConfig => {
    // 修改 webpack 配置
    // 或返回通过 webpack-merge 合并的配置对象
  })

  api.registerCommand('test', args => {
    // 注册 `vue-cli-service test`
  })
}

懒加载路由

当打包构建应用时,Javascript 包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了。(详情请戳路由懒加载

第三部分

自己动手做一个cli3插件

首先,关于插件的目录结构上面已经给出了,这里只需要service插件即可,而且暂时不需要读取webpack配置内容,只需要给出一个执行的契机

//插件目录
├── README.md
├── generateRouter 
        ├── index.js        # 插件方法 
├── index.js      # service 插件
└── package.json
const creatRouter = require('./generateRouter/index')

module.exports = (api, projectOptions) => {
// 这里只是需要一个时机执行,暂时不需要读取webpack中的配置
    api.configureWebpack(webpackConfig => {
        creatRouter.creatRouter(false)
    })
}

package.json中的name要严格按照cli3的格式,要不然会出问题

//package.json
{
  "name": "vue-cli-plugin-<name>",
  "version": "1.0.0",
  "description": "your description",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "you",
  "license": "ISC",
  "dependencies": {},
  "devDependencies": {}
}

接下来看一看generateRouter/index中的内容
其中必要包含了前文中提到的nuxtjs的方法,只是对这两个方法进行了一下改造

generateRoutesAndFiles = async () => {
    const files = {};
    // 这里要读取的不是pages文件而是views文件了
    (await glob(`views/**/*.{vue,js}`, {
        cwd: path.resolve(process.cwd(), './src'),
        ignore: ['**/*.test.*', '**/*.spec.*', '**/-*.*']
    })).forEach((f) => {
        const key = f.replace(/\.(js|vue)$/, '')
        if (/\.vue$/.test(f) || !files[key]) {
            files[key] = f.replace(/('|")/g, '\\$1')
        }
    })

    return createRoutes(
        Object.values(files),
        path.resolve(process.cwd(), './src'),
        'views'
    )
}
function createRoutes(files, srcDir, pagesDir) {
    const routes = []
    const requireComponent = []
    files.forEach((file) => {
        const keys = file
            .replace(RegExp(`^${pagesDir}`), '')
            .replace(/\.(vue|js)$/, '')
            .replace(/\/{2,}/g, '/')
            .split('/')
            .slice(1)

        const route = {
            name: '',
            path: '',
            component: `views${camelCase(keys.join('-').replace('_', ''))}`
        }

        requireComponent.push(`const views${camelCase(keys.join('-').replace('_', ''))} = resolve => require(['../views/${keys.join('/')}'], resolve)`)
        let parent = routes
        keys.forEach((key, i) => {
            route.name = key.startsWith('_') ? key.substr(1) : key
            route.name += key === '_' ? 'all' : ''
            const child = parent.find(parentRoute => parentRoute.name === route.name)
            if (child) {
                child.children = child.children || []
                parent = child.children
                route.path = ''
            } else if (key === 'index' && i + 1 === keys.length) {
                route.path += i > 0 ? '' : '/'
            } else {
                route.path = `/` + getRoutePathExtension(key)

                if (key.startsWith('_') && key.length > 1) {
                    route.path += '?'
                }
            }
        })
        parent.push(route)
    })
    sortRoutes(routes)
    return {
        'routes': cleanChildrenRoutes(routes),
        'requireComponent': requireComponent
    }
}

一个cli3插件基本已经出炉,接下来只需要publish到npm上,然后使用vue add <your name>来加载插件即可

第四部分

使用方法

首先看一下cli3的项目目录结构,在使用了本插件之后,只需要在views中存放根组件(子组件存放于component中),router.js注册路由信息,当使用npm start时,将会根据views中的文件自动生成router/route.js文件

|—node-modules
|—public
|—src
    |—assets
    |—component
    |—views
    |—router
         |—route.js
    |—router.js
    |—App.vue
    |—main.js
|—package.json

// 在router.js中注册
import Vue from 'vue'
import Router from 'vue-router'
import {routes} from './router/route'

Vue.use(Router)

export default new Router({
  routes: routes
})

以下是生成出来的样例

//router/route.js
const viewsAbout = resolve => require(['../views/About'], resolve)
const viewsHome = resolve => require(['../views/Home'], resolve)
const viewsuserUser = resolve => require(['../views/user/_user'], resolve)
const viewsuserUsernameUsername = resolve => require(['../views/user/username/username'], resolve)
export const routes = [
  {
    name: "About",
    path: "/About",
    component: viewsAbout
  },
  {
    name: "Home",
    path: "/Home",
    component: viewsHome
  },
  {
    name: "user-username-username",
    path: "/user/username/username",
    component: viewsuserUsernameUsername
  },
  {
    name: "user-user",
    path: "/user/:user?",
    component: viewsuserUser
  }
]

以上便是本插件的全部内容,如果有疑问或者是bug可以联系我
qq:1053189708
git: https://github.com/zhangOking/vuecli3plugin-generatererouter

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 151,829评论 1 331
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 64,603评论 1 273
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 101,846评论 0 226
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 42,600评论 0 191
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 50,780评论 3 272
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 39,695评论 1 192
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,136评论 2 293
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,862评论 0 182
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,453评论 0 229
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,942评论 2 233
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,347评论 1 242
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,790评论 2 236
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,293评论 3 221
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,839评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,448评论 0 181
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 34,564评论 2 249
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 34,623评论 2 249

推荐阅读更多精彩内容

  • 基于Vue的组件库 https://github.com/ElemeFE/element" element 饿了...
    _执着执着再执着阅读 32,524评论 4 230
  • 转载 :OpenDiggawesome-github-vue 是由OpenDigg整理并维护的Vue相关开源项目库...
    果汁密码阅读 22,980评论 8 125
  • 基于Vue的一些资料 内容 UI组件 开发框架 实用库 服务端 辅助工具 应用实例 Demo示例 element★...
    尝了又尝阅读 1,119评论 0 1
  • awesome-github-vue 是由OpenDigg[https://blog.csdn.net/opend...
    我是七月阅读 2,392评论 0 20
  • 半夜竟饿了起来。 这时我想到爷爷的白菜。那些在夏天的尾巴上撒播的紫色的种子,应该早已破土,抽芽,并长出了多片嫩绿的...
    秋天打柿子阅读 445评论 8 8