├── .browserslistrc ├── babel.config.js ├── public ├── favicon.ico └── index.html ├── src ├── views │ ├── posts │ │ ├── Index.vue │ │ ├── Create.vue │ │ ├── _Id.vue │ │ └── edit │ │ │ └── _Id.vue │ ├── About.vue │ ├── users │ │ ├── ^Profile.vue │ │ └── Index.vue │ └── Index.vue ├── main.js ├── router │ ├── index.js │ └── routes.js └── App.vue ├── .gitignore ├── README.md └── package.json /.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | not dead 4 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NovoManu/vue-automatic-router-example/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /src/views/posts/Index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | -------------------------------------------------------------------------------- /src/views/About.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | -------------------------------------------------------------------------------- /src/views/posts/Create.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | -------------------------------------------------------------------------------- /src/views/users/^Profile.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | -------------------------------------------------------------------------------- /src/views/users/Index.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 13 | -------------------------------------------------------------------------------- /src/views/Index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 14 | -------------------------------------------------------------------------------- /src/views/posts/_Id.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | -------------------------------------------------------------------------------- /src/views/posts/edit/_Id.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | 6 | # local env files 7 | .env.local 8 | .env.*.local 9 | 10 | # Log files 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | pnpm-debug.log* 15 | 16 | # Editor directories and files 17 | .idea 18 | .vscode 19 | *.suo 20 | *.ntvs* 21 | *.njsproj 22 | *.sln 23 | *.sw? 24 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App.vue' 3 | 4 | Vue.config.productionTip = false 5 | 6 | const init = async() => { 7 | const module = await import('./router'); 8 | const router = await module.default; 9 | new Vue({ 10 | router, 11 | render: h => h(App) 12 | }).$mount('#app'); 13 | }; 14 | 15 | init(); 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vue-auto-router 2 | 3 | ## Project setup 4 | ``` 5 | npm install 6 | ``` 7 | 8 | ### Compiles and hot-reloads for development 9 | ``` 10 | npm run serve 11 | ``` 12 | 13 | ### Compiles and minifies for production 14 | ``` 15 | npm run build 16 | ``` 17 | 18 | ### Customize configuration 19 | See [Configuration Reference](https://cli.vuejs.org/config/). 20 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-auto-router", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build" 8 | }, 9 | "dependencies": { 10 | "core-js": "^3.6.5", 11 | "vue": "^2.6.11", 12 | "vue-router": "^3.2.0" 13 | }, 14 | "devDependencies": { 15 | "@vue/cli-plugin-babel": "~4.5.0", 16 | "@vue/cli-plugin-router": "~4.5.0", 17 | "@vue/cli-service": "~4.5.0", 18 | "vue-template-compiler": "^2.6.11" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import VueRouter from 'vue-router' 3 | import routes from './routes' 4 | 5 | Vue.use(VueRouter) 6 | 7 | export default Promise.all(routes).then(routes => { 8 | const router = new VueRouter({ 9 | mode: 'history', 10 | routes 11 | }) 12 | 13 | router.beforeEach((to, from, next) => { 14 | if (!to.meta.middlewares) { 15 | return next() 16 | } 17 | const middlewares = to.meta.middlewares 18 | Object.keys(middlewares).forEach(middleware => { 19 | middlewares[middleware]({ to, from, next }) 20 | }) 21 | return next() 22 | }) 23 | 24 | return router 25 | }) 26 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= htmlWebpackPlugin.options.title %> 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 39 | -------------------------------------------------------------------------------- /src/router/routes.js: -------------------------------------------------------------------------------- 1 | const importAll = r => r.keys() 2 | .map(key => key.slice(2) 3 | .replace('.vue', '').split('/')) 4 | 5 | const pages = importAll(require.context('../views', true, /\.vue$/)) 6 | 7 | const generateRoute = path => { 8 | // Note: remove first element if route starts with index 9 | if (path[0].toLowerCase().startsWith('index') && path.length > 1) { 10 | path.shift() 11 | } 12 | // Note: handle root routes 13 | if (path.length === 1) { 14 | const shortcut = path[0].toLowerCase() 15 | return shortcut.startsWith('index') 16 | ? '' 17 | // Note: handle dynamic routes 18 | : shortcut.startsWith('_') 19 | ? shortcut.replace('_', ':') 20 | : shortcut; 21 | } 22 | // Note: handle other routes 23 | const lastElement = path[path.length - 1] 24 | // Note: remove last element in array if it is index 25 | if (lastElement.toLowerCase().startsWith('index')) { 26 | path.pop() 27 | // Note: handle dynamic routes 28 | } else if (lastElement.startsWith('_')) { 29 | path[path.length - 1] = lastElement.replace('_', ':'); 30 | } 31 | return path.map(p => p.toLowerCase()).join('/') 32 | } 33 | 34 | const childrenFilter = p => ~p.indexOf('^') 35 | 36 | const childrenByPath = pages 37 | // Note: filter pages by children routes 38 | .filter(path => path.some(childrenFilter)) 39 | .map(path => { 40 | // Note: copy path and remove special char ^ 41 | const copy = [...path] 42 | copy[copy.length - 1] = copy[copy.length - 1].slice(1) 43 | // Note: generate key to identify parent 44 | const key = `/${generateRoute(copy.slice(0, copy.length - 1))}` 45 | return { 46 | path, 47 | route: `/${generateRoute(copy)}`, 48 | key 49 | } 50 | }) 51 | .reduce((acc, cur) => { 52 | // Note: generate list of nested routes where key is the parent path 53 | const key = cur.key 54 | delete cur.key 55 | if (acc[key]) { 56 | acc[key].push(cur) 57 | } else { 58 | acc[key] = [cur] 59 | } 60 | return acc 61 | }, {}) 62 | 63 | const defaultLayout = 'AppDefaultLayout' 64 | 65 | export default pages 66 | // Note: remove nested routes from pages 67 | .filter(path => !path.some(childrenFilter)) 68 | .map(async path => { 69 | const { default: component } = await import(`../views/${path.join('/')}`) 70 | const { layout, middlewares, name } = component 71 | const route = `/${generateRoute([...path])}` 72 | let children = [] 73 | if (childrenByPath[route]) { 74 | const promises = childrenByPath[route].map(async ({ path, route }) => { 75 | const { default: childComponent } = 76 | await import(`../views/${path.join('/')}`) 77 | const { 78 | layout: childLayout, 79 | middlewares: childMiddleware, 80 | name: childName 81 | } = childComponent 82 | return { 83 | path: route, 84 | name: childName, 85 | component: childComponent, 86 | meta: { 87 | layout: childLayout || defaultLayout, 88 | middlewares: childMiddleware || {} 89 | } 90 | } 91 | }) 92 | children = await Promise.all(promises) 93 | } 94 | return { 95 | path: route, 96 | name, 97 | component, 98 | meta: { 99 | layout: layout || defaultLayout, 100 | middlewares: middlewares || {} 101 | }, 102 | children 103 | } 104 | }) 105 | --------------------------------------------------------------------------------