├── .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 |
2 |
3 |
This is a list of posts page
4 |
5 |
6 |
7 |
12 |
--------------------------------------------------------------------------------
/src/views/About.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
This is an about page
4 |
5 |
6 |
7 |
12 |
--------------------------------------------------------------------------------
/src/views/posts/Create.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
This is a post creation page
4 |
5 |
6 |
7 |
12 |
--------------------------------------------------------------------------------
/src/views/users/^Profile.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
This is an user profile child page
4 |
5 |
6 |
7 |
12 |
--------------------------------------------------------------------------------
/src/views/users/Index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
This is an users page
4 |
5 |
6 |
7 |
8 |
13 |
--------------------------------------------------------------------------------
/src/views/Index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
This is an home page
4 |
5 |
6 |
7 |
14 |
--------------------------------------------------------------------------------
/src/views/posts/_Id.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
This is a page of the post with id {{ $route.params.id }}
4 |
5 |
6 |
7 |
12 |
--------------------------------------------------------------------------------
/src/views/posts/edit/_Id.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
This is a page to edit the post with id {{ $route.params.id }}
4 |
5 |
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 |
2 |
3 |
4 | Home |
5 | About |
6 | Posts |
7 | Post create |
8 | Post 1 details |
9 | Post 2 edit |
10 | Users |
11 | User Profile
12 |
13 |
14 |
15 |
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 |
--------------------------------------------------------------------------------