$30 off During Our Annual Pro Sale. View Details »

Understanding Client Side Routing with Vue.js

Understanding Client Side Routing with Vue.js

Presentation slides to the JSCamp 2018 talk: Understanding Client Side Routing with Vue.js.

https://jscamp.tech/speakers/hassan-djirdeh/

djirdehh

July 19, 2018
Tweet

More Decks by djirdehh

Other Decks in Technology

Transcript

  1. Hassan Djirdeh
    Understanding Client Side Routing
    with Vue.js
    @djirdehh

    View Slide

  2. Engineering Present
    ~ 2 years

    View Slide

  3. View Slide

  4. View Slide

  5. Vue is the Progressive JavaScript framework
    that is approachable, versatile, and performant.
    Progressive
    approachable versatile performant

    View Slide

  6. Progressive

    Get started with a single script tag

    View Slide

  7. approachable
    HTML CSS JS

    View Slide

  8. performant
    Virtual DOM

    View Slide

  9. Vuex Vue Router Vue Test Utils
    versatile

    View Slide

  10. Routing
    Today’s subject: Routing

    View Slide

  11. Routing often refers to splitting an applications UI
    based on rules derived from the browser URL.

    View Slide

  12. https://www.twitter.com/

    View Slide

  13. /{userName}
    https://www.twitter.com/
    Dynamic
    route

    View Slide

  14. https://www.twitter.com/{userName}
    protocol hostname pathname

    View Slide

  15. Refresh the page and keep our location in the app
    Maintain browser back/forward history functionality
    Bookmark the URL and even share it with others
    With Routing, we can…

    View Slide

  16. Server Side
    Routing
    The client (i.e. the browser)
    makes a request to the server
    on every URL change.
    Client Side

    Routing
    The client only makes a request
    to the server upon initial-page
    load.
    First Meaningful Paint
    Consistent SEO Performance
    Faster Routing after Initial Load
    Smooth transitions and changes
    Two main methods to achieving routing…

    View Slide

  17. Single-page applications

    (SPAs)
    Client side applications are often labelled as…
    In modern web apps; JS is the driving
    force to re-render content in SPAs

    View Slide

  18. We’ll be building this Pokemon App that
    displays details of a Pokemon based on the route.


    Live version: http://pokemon-routing.surge.sh/

    View Slide

  19. vue-router
    By the end of the presentation,
    we’ll build the app with vue-router... but before
    that, we’ll build our own custom router in Vue.

    View Slide

  20. public/
    app/
    main.js
    App.vue
    .babelrc
    .gitignore
    .index.html
    .package.json
    src/
    node_modules/

    View Slide

  21. main.js
    import Vue from 'vue';
    import App from './App';
    new Vue({
    el: '#app',
    render: h => h(App)
    });

    View Slide

  22. Vue Components
    Markup (HTML) Logic (JS) Styles (CSS)
    we’ll take a step back and talk about Vue
    components

    View Slide


  23. Hello Folks!

    <br/>export default {<br/>name: 'NewComponent',<br/>data() {},<br/>methods: {}<br/>}<br/>
    <br/>
    Vue.component('new-component', {
    template: ‘Hello Folks!’,
    data() {},
    methods: {}
    });
    Vue.component('new-component', {
    template: ‘Hello Folks!’,
    data() {},
    methods: {}
    });

    Hello Folks!

    <br/>export default {<br/>name: 'NewComponent',<br/>data() {},<br/>methods: {}<br/>}<br/>
    <br/>
    .vue*

    View Slide

  24. App.vue



    ...


    Charizard
    ...




    <br/>export default {<br/>name: "App",<br/>};<br/>



    ...


    Charizard
    ...




    <br/>export default {<br/>name: "App",<br/>};<br/>



    ...


    Charizard
    ...




    <br/>export default {<br/>name: "App",<br/>};<br/>

    View Slide

  25. http://localhost:8080/
    App
    CharizardCard
    Let’s externalize the
    markup to a unique
    component

    View Slide




  26. ...


    Charizard
    ...




    <br/>export default {<br/>name: "CharizardCard",<br/>};<br/>
    CharizardCard.vue



    ...


    Charizard
    ...




    <br/>export default {<br/>name: "CharizardCard",<br/>};<br/>



    ...


    Charizard
    ...




    <br/>export default {<br/>name: "CharizardCard",<br/>};<br/>

    View Slide




  27. <br/>import CharizardCard from './components/CharizardCard';<br/>export default {<br/>name: "App",<br/>components: {<br/>'charizard-card': CharizardCard<br/>}<br/>};<br/>
    App.vue



    <br/>import CharizardCard from './components/CharizardCard';<br/>export default {<br/>name: "App",<br/>components: {<br/>'charizard-card': CharizardCard<br/>}<br/>};<br/>

    components: {
    'charizard-card': CharizardCard
    }
    import CharizardCard from './components/CharizardCard';

    View Slide

  28. CharizardCard.vue
    App.vue

    View Slide

  29. app/
    main.js
    App.vue
    components/
    BlastoiseCard.vue
    CharizardCard.vue
    VenusaurCard.vue
    We’ll create other files for
    other components

    View Slide

  30. BlastoiseCard.vue CharizardCard.vue VenusaurCard.vue
    App.vue

    View Slide

  31. app/
    main.js
    App.vue
    components/
    router/
    RouterLink.vue
    RouterView.vue
    routes.js

    View Slide

  32. routes.js
    import BlastoiseCard from '../components/BlastoiseCard';
    import CharizardCard from '../components/CharizardCard';
    import VenusaurCard from '../components/VenusaurCard';
    export default const routes = [
    {path: '/', component: CharizardCard},
    {path: '/charizard', component: CharizardCard},
    {path: '/blastoise', component: BlastoiseCard},
    {path: '/venusaur', component: VenusaurCard}
    ];
    import BlastoiseCard from '../components/BlastoiseCard';
    import CharizardCard from '../components/CharizardCard';
    import VenusaurCard from '../components/VenusaurCard';
    export default const routes = [
    {path: '/', component: CharizardCard},
    {path: '/charizard', component: CharizardCard},
    {path: '/blastoise', component: BlastoiseCard},
    {path: '/venusaur', component: VenusaurCard}
    ];
    import BlastoiseCard from '../components/BlastoiseCard';
    import CharizardCard from '../components/CharizardCard';
    import VenusaurCard from '../components/VenusaurCard';
    export default const routes = [
    {path: '/', component: CharizardCard},
    {path: '/charizard', component: CharizardCard},
    {path: '/blastoise', component: BlastoiseCard},
    {path: '/venusaur', component: VenusaurCard}
    ];
    {path: '/', component: CharizardCard},
    {path: '/charizard', component: CharizardCard},
    {path: '/blastoise', component: BlastoiseCard},
    {path: '/venusaur', component: VenusaurCard}
    export default const routes = [
    {path: '/', component: CharizardCard},
    {path: '/charizard', component: CharizardCard},
    {path: '/blastoise', component: BlastoiseCard},
    {path: '/venusaur', component: VenusaurCard}
    ];
    pathname!

    View Slide






  33. <br/>import routes from './routes';<br/>export default {<br/>name: "RouterView",<br/>data() {<br/>return {<br/>currentView: {}<br/>}<br/>}<br/>methods: {<br/>getRouteObject() {<br/>return routes.find(<br/>route => route.path === window.location.pathname<br/>RouterView.vue<br/><template><br/><div class="pokemon"><br/><component :is="currentView"></component><br/></div><br/></template><br/><script><br/>import routes from './routes';<br/>export default {<br/>name: "RouterView",<br/>data() {<br/>return {<br/>currentView: {}<br/>}<br/>}<br/>methods: {<br/>getRouteObject() {<br/>return routes.find(<br/>route => route.path === window.location.pathname<br/>data() {<br/>return {<br/>currentView: {}<br/>}<br/>}<br/><component :is="currentView"></component><br/>We’re using the Dynamic <component /> keyword<br/>to create a component that will render<br/>another component based on a currentView property<br/>

    View Slide



  34. <br/>import routes from './routes';<br/>export default {<br/>name: "RouterView",<br/>data() {<br/>return {<br/>currentView: {}<br/>}<br/>}<br/>methods: {<br/>getRouteObject() {<br/>return routes.find(<br/>route => route.path === window.location.pathname<br/>);<br/>}<br/>}<br/>created() {<br/>if (!this.getRouteObject()) {<br/>RouterView.vue<br/>import routes from './routes';<br/>methods: {<br/>getRouteObject() {<br/>return routes.find(<br/>route => route.path === window.location.pathname<br/>);<br/>}<br/>}<br/>We’ll set up a getRouteObject() method<br/>responsible in getting the correct route object<br/>from routes based on the window location<br/>

    View Slide

  35. getRouteObject() {
    return routes.find(
    route => route.path === window.location.pathname
    );
    }
    }
    created() {
    if (!this.getRouteObject()) {
    this.currentView = {
    template: `

    Sorry, we couldn't find that Pokémon :(.

    `
    };
    } else {
    this.currentView = this.getRouteObject().component;
    }
    }
    };

    RouterView.vue
    created() {
    if (!this.getRouteObject()) {
    this.currentView = {
    template: `

    Sorry, we couldn't find that Pokémon :(.

    `
    };
    } else {
    this.currentView = this.getRouteObject().component;
    }
    }
    When the component gets created,
    we’ll use the getRouteObject method to bind
    the correct component to currentView

    View Slide






  36. <br/>import CharizardCard from './components/CharizardCard';<br/>export default {<br/>name: "App",<br/>components: {<br/>'charizard-card': CharizardCard<br/>}<br/>};<br/>
    App.vue





    <br/>import RouterView from './router/RouterView';<br/>export default {<br/>name: "App",<br/>components: {<br/>'router-view': RouterView<br/>}<br/>};<br/>
    Now we’ll have App
    render the RouterView component

    View Slide

  37. /charizard /blastoise /venusaur /pikachu

    View Slide


  38. View Slide

  39. RouterLink.vue

    {{ to }}

    <br/>export default {<br/>name: "RouterLink",<br/>props: {<br/>to: {<br/>type: String,<br/>required: true<br/>}<br/>},<br/>methods: {<br/>navigate(evt) {<br/>evt.preventDefault();<br/>window.history.pushState(null, null, this.to);<br/>}<br/>}<br/>};<br/>

    {{ to }}

    <br/>export default {<br/>name: "RouterLink",<br/>props: {<br/>to: {<br/>type: String,<br/>required: true<br/>}<br/>},<br/>methods: {<br/>navigate(evt) {<br/>evt.preventDefault();<br/>window.history.pushState(null, null, this.to);<br/>}<br/>}<br/>};<br/>

    {{ to }}

    <br/>export default {<br/>name: "RouterLink",<br/>props: {<br/>to: {<br/>type: String,<br/>required: true<br/>}<br/>},<br/>methods: {<br/>navigate(evt) {<br/>evt.preventDefault();<br/>window.history.pushState(null, null, this.to);<br/>}<br/>}<br/>};<br/>
    methods: {
    navigate(evt) {
    evt.preventDefault();
    window.history.pushState(null, null, this.to);
    }
    }
    To prevent the browser from making a web request, we’ll create a
    RouterLink element that pushes a new location with
    window.history.pushState.

    View Slide

  40. We’ve bound an href attribute to the of RouterLink to allow
    users to hover and see where they lead to.

    View Slide

  41. RouterLink RouterView
    created()
    RouterView only picks the correct Pokemon component when
    it gets created. With RouterLink, we may need to send
    some event to RouterView when clicked.

    View Slide

  42. EventBus

    View Slide

  43. An Event Bus is a Vue instance that is used to enable
    isolated components to subscribe and publish
    custom events between each other.

    View Slide

  44. import Vue from 'vue';
    export const EventBus = new Vue();
    EventBus = new Vue();
    event-bus.js

    View Slide

  45. RouterLink.vue

    {{ to }}

    <br/>import EventBus from './event-bus';<br/>export default {<br/>name: "RouterLink",<br/>props: {<br/>to: {<br/>type: String,<br/>required: true<br/>}<br/>},<br/>methods: {<br/>navigate(evt) {<br/>evt.preventDefault();<br/>window.history.pushState(null, null, this.to);<br/>EventBus.$emit('navigate');<br/>}<br/>}<br/>};<br/>
    methods: {
    navigate(evt) {
    evt.preventDefault();
    window.history.pushState(null, null, this.to);
    EventBus.$emit('navigate');
    }
    }

    View Slide

  46. RouterView.vue
    ...
    <br/>import routes from './routes';<br/>import EventBus from './event-bus';<br/>...<br/>created() {<br/>// Get correct component upon page load<br/>...<br/>// Get correct component upon redirect<br/>EventBus.$on('navigate', () => {<br/>this.currentView = this.getRouteObject().component;<br/>});<br/>},<br/>methods: {<br/>getRouteObject() {<br/>return routes.find(<br/>route => route.path === window.location.pathname<br/>);<br/>}<br/>}<br/>...<br/>
    ...
    <br/>import routes from './routes';<br/>...<br/>created() {<br/>// Get correct component upon page load<br/>...<br/>// Get correct component upon redirect<br/>EventBus.$on('navigate', () => {<br/>this.currentView = this.getRouteObject().component;<br/>});<br/>},<br/>methods: {<br/>getRouteObject() {<br/>return routes.find(<br/>route => route.path === window.location.pathname<br/>);<br/>}<br/>}<br/>...<br/>
    import EventBus from './event-bus';

    View Slide

  47. App.vue










    <br/>import RouterView from './router/RouterView';<br/>import RouterLink from './router/RouterLink';<br/>export default {<br/>name: "App",<br/>components: {<br/>'router-view': RouterView,<br/>‘router-link': RouterLink<br/>}<br/>};<br/>
    import RouterLink from ‘./router/RouterLink';










    <br/>import RouterView from './router/RouterView';<br/>import RouterLink from './router/RouterLink';<br/>export default {<br/>name: "App",<br/>components: {<br/>'router-view': RouterView,
<br/>'router-link': RouterLink<br/>}<br/>};<br/>

    View Slide

  48. popstate
    Last item to take care of is browser navigation events.
    For that we’ll use the popstate event.

    View Slide

  49. App.vue

    ...

    <br/>import RouterView from './router/RouterView';<br/>import RouterLink from './router/RouterLink';<br/>import EventBus from './event-bus';<br/>export default {<br/>name: "App",<br/>created() {<br/>window.addEventListener('popstate', () => {<br/>EventBus.$emit('navigate');<br/>});<br/>},<br/>components: {<br/>'router-view': RouterView,<br/>'router-link': RouterLink<br/>}<br/>};<br/>

    ...

    <br/>import RouterView from './router/RouterView';<br/>import RouterLink from './router/RouterLink';<br/>import EventBus from './event-bus';<br/>export default {<br/>name: "App",<br/>created() {<br/>window.addEventListener('popstate', () => {<br/>EventBus.$emit('navigate');<br/>});<br/>},<br/>components: {<br/>'router-view': RouterView<br/>'router-link': RouterLink<br/>}<br/>};<br/>

    View Slide

  50. View Slide

  51. Responsible in mapping components to respective
    URL pathnames
    - routes
    Component responsible in rendering another specified
    component based on the app’s location
    - router-view
    Component that allows the user to change the location of
    the browser without making a web request
    - router-link
    These are also the same three pieces of the vue-router
    library. So we’ll now switch over to using it.

    View Slide

  52. vue-router

    View Slide

  53. vue-router is the official router of Vue. It deeply

    integrates with Vue to make building Single Page
    applications a breeze.

    View Slide

  54. npm i vue-router —-save

    View Slide

  55. import Vue from 'vue';
    import VueRouter from 'vue-router';
    Vue.use(VueRouter);

    View Slide

  56. app/
    main.js
    App.vue
    components/
    router.js

    View Slide

  57. import Vue from 'vue';
    import VueRouter from 'vue-router';
    import ..Card from '.components/..Card';
    Vue.use(VueRouter);
    const routes = [
    {path: '/', component: CharizardCard},
    {path: '/charizard', component: CharizardCard},
    {path: '/blastoise', component: BlastoiseCard},
    {path: '/venusaur', component: VenusaurCard},
    {
    path: '*',
    component: {
    template: `

    Sorry. We couldn't find that Pokémon :(.

    `
    }
    }
    ];
    router.js
    import Vue from 'vue';
    import VueRouter from 'vue-router';
    import ..Card from '.components/..Card';
    Vue.use(VueRouter);
    const routes = [
    {path: '/', component: CharizardCard},
    {path: '/charizard', component: CharizardCard},
    {path: '/blastoise', component: BlastoiseCard},
    {path: '/venusaur', component: VenusaurCard},
    {
    path: '*',
    component: {
    template: `

    Sorry. We couldn't find that Pokémon :(.

    `
    }
    }
    ];

    View Slide

  58. const routes = [
    {path: '/', component: CharizardCard},
    {path: '/charizard', component: CharizardCard},
    {path: '/blastoise', component: BlastoiseCard},
    {path: '/venusaur', component: VenusaurCard},
    {
    path: '*',
    component: {
    template: `

    Sorry. We couldn't find that Pokémon :(.

    `
    }
    }
    ];
    export default const router = new VueRouter({
    mode: 'history',
    routes
    });
    router.js
    export default const router = new VueRouter({
    mode: 'history',
    routes
    });

    View Slide

  59. https://localhost:8080/#/blastoise
    Never sent
    to the server
    Hash mode URLs
    vue-router’s default mode is hash which allows to have
    multiple client side routes without having to provide
    necessary server-side fallbacks.

    View Slide

  60. https://localhost:8080/blastoise
    History mode URLs
    Since our app is a SPA (Single-page app), we don’t need
    hash-based URLs. We’ll switch our URLS to mode: history.

    View Slide

  61. import Vue from 'vue';
    import App from './App';
    import router from './router'
    new Vue({
    el: '#app',
    router,
    render: h => h(App)
    });
    main.js
    import Vue from 'vue';
    import App from './App';
    import router from './router';
    new Vue({
    el: '#app',
    router,
    render: h => h(App)
    });
    To make our app router-aware, we’ll declare the router
    instance within the entire application Vue instance.

    View Slide

  62. App.vue





    /charizard
    /blastoise
    /venusaur




    <br/>export default {<br/>name: "App"<br/>};<br/>





    /charizard
    /blastoise
    /venusaur




    <br/>export default {<br/>name: "App"<br/>};<br/>
    We can use the router-view and
    router-link elements like we’ve done
    previously.

    View Slide

  63. /charizard /blastoise /venusaur /gyrados

    View Slide

  64. - Well Tested
    - Consistency between different browsers
    - Dynamic Route Matching
    - Nested Routes
    - Navigation Guards
    vue-router > custom router

    View Slide

  65. Dynamic Route Matching
    app/
    components/
    BlastoiseCard.vue
    CharizardCard.vue
    VenusaurCard.vue
    AlakazamCard.vue
    EeveeCard.vue
    GolbatCard.vue
    WigglytuffCard.vue
    MachampCard.vue
    OnixCard.vue
    MoltresCard.vue
    ...Card.vue
    app/
    components/
    PokemonCard.vue
    Having a component file for every Pokemon is
    unrealistic for a much larger app. With Dynamic
    Route Matching, we can have multiple routes with
    the same pattern be matched to a single
    component.

    View Slide

  66. Dynamic Route Matching
    router.js
    const routes = [
    {path: '/', component: CharizardCard},
    {path: '/charizard', component: CharizardCard},
    {path: '/blastoise', component: BlastoiseCard},
    {path: '/venusaur', component: VenusaurCard},
    ...
    ];
    Before

    View Slide

  67. Dynamic Route Matching
    router.js
    const routes = [
    {path: '/', component: CharizardCard},
    {path: '/pokemon/:id', component: PokemonCard }
    ];
    After

    View Slide


  68. ...

    <br/>export default {<br/>name: "PokemonCard",<br/>created() {<br/>console.log(this.$route.params.id);<br/>}<br/>};<br/>
    Dynamic Route Matching

    ...

    <br/>export default {<br/>name: "PokemonCard",<br/>created() {<br/>console.log(this.$route.params.id);<br/>}<br/>};<br/>
    charizard
    /pokemon/charizard
    PokemonCard.vue

    ...

    <br/>export default {<br/>name: "PokemonCard",<br/>created() {<br/>console.log(this.$route.params.id);<br/>}<br/>};<br/>

    View Slide

  69. data: [
    {
    id: 'charizard',
    hp: 78,
    type: ,
    weight_lbs: 199,
    height_m: 1.7,
    stats: [
    ...
    ],
    evolutions: [
    ...
    ]
    },
    ...
    ]
    Dynamic Route Matching
    data: [
    {
    id: 'charizard',
    hp: 78,
    type: ,
    weight_lbs: 199,
    height_m: 1.7,
    stats: [
    ...
    ],
    evolutions: [
    ...
    ]
    },
    ...
    ]
    data: [
    {
    id: 'charizard',
    hp: 78,
    type: ,
    weight_lbs: 199,
    height_m: 1.7,
    stats: [
    ...
    ],
    evolutions: [
    ...
    ]
    },
    ...
    ]
    With the id route param available in our
    component - we can use it to retrieve
    data with a server, make an GET request, etc.

    View Slide







  70. {{ pokemonData.id }}
    ...




    <br/>export default {<br/>name: "PokemonCard",<br/>data() {<br/>return {<br/>pokemonData: {...}<br/>}<br/>}<br/>...<br/>};<br/>
    PokemonCard.vue






    {{ pokemonData.id }}
    ...




    <br/>export default {<br/>name: "PokemonCard",<br/>data() {<br/>return {<br/>pokemonData: {...}<br/>}<br/>}<br/>...<br/>};<br/>
    Found with - this.$route.params.id;
    Bind all data
    dynamically to component

    View Slide

  71. App
    PokemonCard
    PokemonStats

    View Slide

  72. Nested Routes
    PokemonCard
    PokemonStats
    PokemonCard
    PokemonEvolutions
    /pokemon/:id/stats /pokemon/:id/evolutions

    View Slide

  73. const routes = [
    { path: '/pokemon/:id', component: PokemonCard,
    children: [
    {
    path: '/stats',
    component: PokemonStats
    },
    {
    path: '/evolutions',
    component: PokemonEvolutions
    }
    ]
    }
    ]
    Nested Routes
    router.js
    const routes = [
    { path: '/pokemon/:id', component: PokemonCard,
    children: [
    {
    path: '/stats',
    component: PokemonStats
    },
    {
    path: '/evolutions',
    component: PokemonEvolutions
    }
    ]
    }
    ]
    const routes = [
    { path: '/pokemon/:id', component: PokemonCard,
    children: [
    {
    path: '/stats',
    component: PokemonStats
    },
    {
    path: '/evolutions',
    component: PokemonEvolutions
    }
    ]
    }
    ]

    View Slide

  74. Nested Routes
    PokemonCard.vue



    ...




    <br/>export default {<br/>name: "PokemonCard",<br/>};<br/>



    ...




    <br/>export default {<br/>name: "PokemonCard",<br/>};<br/>



    ...




    <br/>export default {<br/>name: "CharizardCard",<br/>};<br/>
    RouterView responsible in
    rendering the correct nested
    component based on the nested
    route.

    View Slide

  75. Navigation Guards
    1. Global - for all navigation routes
    2. Per-route - for a certain route
    3. In-component - for a certain component

    View Slide

  76. const router = new VueRouter({ ... })
    router.beforeEach((to, from, next) => {
    // ...
    });
    Global Navigation Guards
    const router = new VueRouter({ ... })
    router.beforeEach((to, from, next) => {
    // ...
    });
    const router = new VueRouter({ ... })
    router.beforeEach((to, from, next) => {
    // ...
    });
    const router = new VueRouter({ ... })
    router.beforeEach((to, from, next) => {
    // ...
    });
    const router = new VueRouter({ ... })
    router.beforeEach((to, from, next) => {
    // ...
    });
    to.path next();
    next('/about');
    next(false);
    next() function must be invoked
    to complete routing process

    View Slide

  77. const router = new VueRouter({ ... })
    router.afterEach((to, from) => {
    // ...
    });
    Global After Hooks
    router.afterEach((to, from) => {
    // ...
    });

    View Slide

  78. Per-Route Navigation Guards
    const routes = [
    { path: '/pokemon/:id',
    component: PokemonCard,
    beforeEnter: (to, from, next) => {
    ...
    }
    }
    ]

    View Slide

  79. In-Component Navigation Guards

    ...

    <br/>export default {<br/>name: "PokemonCard",<br/>beforeRouteEnter (to, from, next) {},<br/>beforeRouteUpdate (to, from, next) {},<br/>beforeRouteLeave (to, from, next) {}<br/>};<br/>

    ...

    <br/>export default {<br/>name: "PokemonCard",<br/>beforeRouteEnter (to, from, next) {},<br/>beforeRouteUpdate (to, from, next) {},<br/>beforeRouteLeave (to, from, next) {}<br/>};<br/>
    beforeRouteEnter (to, from, next) {},
    beforeRouteUpdate (to, from, next) {},
    beforeRouteLeave (to, from, next) {},

    View Slide

  80. - Programmatic Navigation
    - Redirects/Aliases
    - Transitions
    - Lazy Loading
    vue-router also provides other
    additional capabilities…

    View Slide

  81. - Vue Router Documentation
    https://router.vuejs.org/
    - Let’s build a Custom Vue Router
    https://css-tricks.com/build-a-custom-vue-router/

    View Slide

  82. View Slide

  83. Details
    https://www.fullstack.io/vue

    View Slide

  84. @djirdehh
    Hassan Djirdeh

    View Slide