the laravel developer s the laravel developer s guide to
play

The Laravel Developer's The Laravel Developer's Guide to Vue SPAs - PowerPoint PPT Presentation

The Laravel Developer's The Laravel Developer's Guide to Vue SPAs Guide to Vue SPAs JESS ARCHER The BaseCode Podcast The BaseCode Podcast basecodefieldguide.com/podcast Social wish list and gift reminders Social wish list and gift reminders


  1. The Laravel Developer's The Laravel Developer's Guide to Vue SPAs Guide to Vue SPAs JESS ARCHER

  2. The BaseCode Podcast The BaseCode Podcast basecodefieldguide.com/podcast

  3. Social wish list and gift reminders Social wish list and gift reminders

  4. What is an SPA? What is an SPA? (Single Page Application) (Single Page Application)

  5. What is an SPA? What is an SPA? ( Single Full Page Load Single Full Page Load Application) Application)

  6. Why build an SPA? Why build an SPA?

  7. “It feels like an app” “It feels like an app” — Everyone

  8. Code Sharing Code Sharing

  9. <!-- MyComponent.vue --> <template web> <input v-model="name" /> </template> <template native> <TextField v-model="name" /> </template> <script> export default { // shared logic } </script> nativescript�vue.org

  10. Why not build an SPA? Why not build an SPA?

  11. We need to re-engineer things We need to re-engineer things that the browser ordinarily give us that the browser ordinarily give us

  12. Easy to harm user experience Easy to harm user experience and accessibility and accessibility

  13. Hard to optimise for search engines Hard to optimise for search engines

  14. More work More work Slower to build Slower to build More to maintain More to maintain

  15. SPA SPA Authentication Authentication

  16. Goals Goals (assumptions) (assumptions) 1. The SPA should be completely decoupled The SPA should be completely decoupled from the back-end from the back-end 2. OAuth2 and JWTs are the gold standards OAuth2 and JWTs are the gold standards for authentication for authentication

  17. OAuth OAuth Primarily intended for third-party auth

  18. OAuth OAuth Requires the client to hold tokens or secrets

  19. Short access tokens... Refresh regularly... 😭 (Sorry Alex)

  20. Why short access tokens Why short access tokens and refresh regularly? and refresh regularly?

  21. If JavaScript can access a token If JavaScript can access a token it is vulnerable to XSS attacks. it is vulnerable to XSS attacks.

  22. JWT JWT JSON Web Tokens

  23. JWT JWT Really clever way to do decentralised and stateless authentication

  24. JWT JWT JavaScript clients need to hold the token

  25. I felt like I kept I felt like I kept hitting walls... hitting walls...

  26. The solution? The solution?

  27. 🍫 Cookies! Cookies!

  28. Cookies marked HttpOnly HttpOnly cannot be Cookies marked cannot be accessed by JavaScript accessed by JavaScript

  29. 🙆 Can't use Laravel's default token-based API middleware 🤣 Could put your API routes in routes/web.php to use the session cookie 🤯 Could change your routes/api.php middleware to use sessions Or...

  30. Laravel Passport Laravel Passport It's not just for OAuth! It's not just for OAuth! (But it also does OAuth!) (But it also does OAuth!)

  31. Passport can authenticate Passport can authenticate via cookies too! via cookies too!

  32. // app/Http/Kernel.php 'web' => [ // Other middleware... \Laravel\Passport\Http\Middleware\CreateFreshApiToken::class, ],

  33. // resources/js/bootstrap.js window.axios.interceptors.response.use( undefined , function (error) { switch (error.response.status) { case 401: // Not logged in case 419: // Session expired case 503: // Down for maintenance // Bounce the user to the login screen with a redirect back window.location.reload() break ; case 500: alert('Ooops, something went wrong! The team have been notified.') break ; default : // Allow individual requests to handle other errors return Promise.reject(error); } });

  34. # .env SESSION_LIFETIME=43200 # 30 days

  35. State Management State Management

  36. <template> <h1>Hi, {{ name }}</h1> </template> <script> export default { data() { return { name: 'Laracon', } } } </script>

  37. <template> <h1>Count: {{ count }}</h1> <button @click="$emit('increment')"> Increment </button> </template> <script> export default { props: [ 'count' ], } </script>

  38. <template> <h1>Count: {{ count }}</h1> <button @click="$emit('increment')"> Increment </button> </template> <script> export default { props: [ 'count' ], } </script>

  39. One-Way Data Flow One-Way Data Flow https:��vuejs.org/v2/guide/components�props.html

  40. Store Pattern Store Pattern

  41. Demo Demo Don't worry - no live coding on my �rst talk 😆

  42. import Vuex from 'vuex' const store = new Vuex.Store({ state: {}, // data getters: {}, // computed properties mutations: {}, // methods that change state actions: {} // arbitrary async methods })

  43. Freedom can be paralysing in Freedom can be paralysing in software development software development

  44. Vuex as the interface to my API Vuex as the interface to my API and single source of truth for my data and single source of truth for my data

  45. I only push data to Vuex when I only push data to Vuex when it's ready to be saved to the server. it's ready to be saved to the server. // MyComponent.vue methods: { save() { this .$store.dispatch('luckyDucks/save', this .luckyDuck) } }

  46. I place my API requests inside I place my API requests inside Vuex "Actions" Vuex "Actions" // store/modules/lucky-ducks.js actions: { save (context, luckyDuck) { axios. post ('/api/lucky-ducks', luckyDuck) // ... } }

  47. I store the data returned from the server, I store the data returned from the server, not what was passed to the Vuex action. not what was passed to the Vuex action. // store/modules/lucky-ducks.js axios.post('/api/lucky-ducks', luckyDuck) . then (response => { context.commit('save', response. data . data ) })

  48. import Vuex from 'vuex' import luckyDucks from 'modules/lucky-ducks.js' import occasions from 'modules/occasions.js' ... const store = new Vuex.Store({ modules: { luckyDucks, occasions, ... } })

  49. Structuring your data Structuring your data on the front end on the front end

  50. class LuckyDuckController { public function index () { return auth() ->user() ->luckyDucks ->load('occasions'); } }

  51. [ { "id": 1, "nickname": "Sally", "occasions": [ { "id": 1, "type": "birthday", "date": "1989-11-30" }, ... ] }, ... ]

  52. I thought I'd set up everything I thought I'd set up everything perfectly for my front-end... perfectly for my front-end...

  53. What I wanted... What I wanted... // Occasions belonging to an individual lucky duck luckyDuck.occasions.forEach(occasion => { occasion.doSomething() }) // All occasions, regardless of lucky duck occasions.forEach(occasion => { occasion.doSomething() }) // Individual occasions by ID occasion = findById(123)

  54. Flatten your data Flatten your data on the front end on the front end

  55. state: { luckyDucks: { 1: { id: 1, nickname: 'Sally', occasions: [1, 2, 3] }, ... }, occasions: { 1: { id: 1, type: 'birthday', date: '1989-11-30' lucky_duck_id: 1 }, ... } }

  56. state: { luckyDucks: { 1: { id: 1, nickname: 'Sally', occasions: [1, 2, 3] }, ... }, occasions: { 1: { id: 1, type: 'birthday', date: '1989-11-30' lucky_duck_id: 1 }, ... } }

  57. state: { luckyDucks: { 1: { id: 1, nickname: 'Sally', occasions: [1, 2, 3] }, ... }, occasions: { 1: { id: 1, type: 'birthday', date: '1989-11-30' lucky_duck_id: 1 }, ... } }

  58. state: { luckyDucks: { 1: { id: 1, nickname: 'Sally', occasions: [1, 2, 3] }, ... }, occasions: { 1: { id: 1, type: 'birthday', date: '1989-11-30' lucky_duck_id: 1 }, ... } }

  59. state: { luckyDucks: { 1: { id: 1, nickname: 'Sally', occasions: [1, 2, 3] }, ... }, occasions: { 1: { id: 1, type: 'birthday', date: '1989-11-30' lucky_duck_id: 1 }, ... } }

  60. Modal Back Router Hack Modal Back Router Hack

  61. Demo Demo Still no live coding

  62. created() { const unregisterRouterGuard = this .$router.beforeEach((to, from , next) => { this .back() next( false ) }) this .$once('hook:destroyed', () => { unregisterRouterGuard() }) }, methods: { back() { //... } }

  63. created() { const unregisterRouterGuard = this .$router.beforeEach((to, from , next) => { this .back() next( false ) }) this .$once('hook:destroyed', () => { unregisterRouterGuard() }) }, methods: { back() { //... } }

  64. created() { const unregisterRouterGuard = this .$router.beforeEach((to, from , next) => { this .back() next( false ) }) this .$once('hook:destroyed', () => { unregisterRouterGuard() }) }, methods: { back() { //... } }

  65. created() { const unregisterRouterGuard = this .$router.beforeEach((to, from , next) => { this .back() next( false ) }) this .$once('hook:destroyed', () => { unregisterRouterGuard() }) }, methods: { back() { //... } }

Recommend


More recommend