From 6198d0bfc2b3144208ec4fea1058262771767ecf Mon Sep 17 00:00:00 2001 From: Eduardo San Martin Morote Date: Thu, 18 Aug 2022 16:45:03 +0200 Subject: [PATCH 01/22] feat: useRoute and useRouter --- examples/composables/app.js | 78 +++++++++++++++++ examples/composables/index.html | 7 ++ examples/index.html | 1 + examples/webpack.config.js | 3 +- package.json | 8 +- src/composables.js | 46 ++++++++++ test/e2e/specs/composables.js | 29 +++++++ yarn.lock | 144 +++++++++++++++++++++++++++----- 8 files changed, 289 insertions(+), 27 deletions(-) create mode 100644 examples/composables/app.js create mode 100644 examples/composables/index.html create mode 100644 src/composables.js create mode 100644 test/e2e/specs/composables.js diff --git a/examples/composables/app.js b/examples/composables/app.js new file mode 100644 index 000000000..c42e7ab64 --- /dev/null +++ b/examples/composables/app.js @@ -0,0 +1,78 @@ +import Vue, { defineComponent, watch, ref, onUnmounted } from 'vue' +import VueRouter from 'vue-router' +import { useRoute, useRouter } from 'vue-router/composables' + +Vue.use(VueRouter) + +const Home = defineComponent({ + setup () { + const route = useRoute() + const router = useRouter() + + // should be / + const startRoute = route.fullPath + + console.log('got to Home', startRoute) + + const watchCount = ref(0) + + watch(() => route.query.n, () => { + console.log('watched') + watchCount.value++ + }) + + onUnmounted(() => { + console.log('unmounted') + }) + + function navigate () { + router.push({ query: { n: 1 + (Number(route.query.n) || 0) }}) + } + return { route, navigate, watchCount, startRoute } + }, + template: ` +
+

Home

+

{{ startRoute }}

+

{{ watchCount }}

+

{{ route.fullPath }}

+ +
+ ` +}) + +const About = defineComponent({ + setup () { + const route = useRoute() + return { route } + }, + template: ` +
+

About

+

{{ route.fullPath }}

+
+ ` +}) + +const router = new VueRouter({ + mode: 'history', + base: __dirname, + routes: [ + { path: '/', component: Home }, + { path: '/about', component: About } + ] +}) + +new Vue({ + router, + template: ` +
+

Basic

+ + +
+ ` +}).$mount('#app') diff --git a/examples/composables/index.html b/examples/composables/index.html new file mode 100644 index 000000000..b6f6342a7 --- /dev/null +++ b/examples/composables/index.html @@ -0,0 +1,7 @@ + + +← Examples index +
+
+ + diff --git a/examples/index.html b/examples/index.html index 5f2cd4f32..2b41a845a 100644 --- a/examples/index.html +++ b/examples/index.html @@ -30,6 +30,7 @@

Vue Router Examples

  • Keepalive View
  • Multiple Apps
  • Restart App
  • +
  • Composables
  • diff --git a/examples/webpack.config.js b/examples/webpack.config.js index 85b68fcf4..7b9afb16f 100644 --- a/examples/webpack.config.js +++ b/examples/webpack.config.js @@ -49,7 +49,8 @@ module.exports = { resolve: { alias: { vue: 'vue/dist/vue.esm.js', - 'vue-router': path.join(__dirname, '..', 'src') + 'vue-router': path.join(__dirname, '..', 'src'), + 'vue-router/composables': path.join(__dirname, '..', 'src/composables.js') } }, diff --git a/package.json b/package.json index c44a25fd8..6e358b236 100644 --- a/package.json +++ b/package.json @@ -99,11 +99,11 @@ "rollup-watch": "^4.0.0", "selenium-server": "^3.141.59", "terser": "^4.2.0", - "typescript": "^3.5.2", - "vue": "^2.6.12", + "typescript": "^4.7.0", + "vue": "^2.7.0", "vue-loader": "^15.9.3", - "vue-server-renderer": "^2.6.12", - "vue-template-compiler": "^2.6.12", + "vue-server-renderer": "^2.7.0", + "vue-template-compiler": "^2.7.0", "vuepress": "^1.5.3", "vuepress-theme-vue": "^1.1.1", "webpack": "^4.35.2", diff --git a/src/composables.js b/src/composables.js new file mode 100644 index 000000000..f2dd6c588 --- /dev/null +++ b/src/composables.js @@ -0,0 +1,46 @@ +import { getCurrentInstance, shallowReactive, effectScope } from 'vue' + +export function useRouter () { + const i = getCurrentInstance() + if (process.env.NODE_ENV !== 'production' && !i) { + throwNoCurrentInstance('useRouter') + } + + return i.proxy.$root.$router +} + +export function useRoute () { + const i = getCurrentInstance() + if (process.env.NODE_ENV !== 'production' && !i) { + throwNoCurrentInstance('useRoute') + } + + const root = i.proxy.$root + if (!root._$route) { + const route = effectScope(true).run( + () => shallowReactive(Object.assign({}, root.$router.currentRoute)) + ) + root._$route = route + + root.$router.afterEach(to => { + Object.assign(route, to) + }) + } + + return root._$route +} + +// TODO: +// export function useLink () {} + +// TODO: +// export function onBeforeRouteUpdate () {} + +// TODO: +// export function onBeforeRouteLeave () {} + +function throwNoCurrentInstance (method) { + throw new Error( + `[vue-router]: Missing current instance. ${method}() must be called inside