Skip to content

Instantly share code, notes, and snippets.

@harrisoncramer
Last active November 7, 2022 12:01
Show Gist options
  • Save harrisoncramer/bedbd1ed80b6b1da7ccf015816c7294c to your computer and use it in GitHub Desktop.
Save harrisoncramer/bedbd1ed80b6b1da7ccf015816c7294c to your computer and use it in GitHub Desktop.
VueConf 2022 Notes

Vue3

  • 25% of all VueJS downloads are Vue3
  • Johnson Chu is working full-time at Vue since March on Volar

Vite

  • Not part of Vue anymore technically
  • Planning 3.0 -- moving to internal ESM
    • Goal: Both production and dependency handle CommonJS modules the same
  • ESBuild is a Golang-written compiler that compiles to native code
    • Uses parallelism (Golang's strong suit)
    • The dev command opens up a server, which waits for an HTTP request. That will trigger ESModules to load the modules that are relevant
  • Bundling handled by Rollup -- Javascript-based bundler, faster in terms of tree-shaking and output size
    • Bundling the full application happens when sending to production
  • The cold start took longer than the restart -- why? If there's no bundling that's occuring?
  • Production ready since February 2021
  • Replaces vue-cli which is in maintenance mode
  • Ships out of the box with Nuxt3
  • Vite Test Runners
    • Test Runners need module bundling (Jest relies on CommonJS)
    • Vite-Jest plugin lets you support Jest testing with Vite
    • Vitest reads the same configuration file

Histoire

  • Alternative to storybook

Vue Core

  • There will be a Vue 2.7 -- backporting the composition API
    • Can import ref for instance
    • Moved core codebase to Typescript + moved to Vitest
    • npm install vue@v2-alpha
    • With 2.7 you'll no longer need vue/compiler-sfc
    • Estimated release at the end of June
    • Final minor release of Vue2
    • EOL for Vue2 will be end of the year in 2023
  • Vue 3.3 is the next planned release
    • Mostly performance updates: Stabilize suspense, reactivity transform (make script setup easier), conditional hydration
    • Experimental: SolidJS inspired, no virtual DOM at all -- directly binding reactivity to DOM nodes itself
      • Looks a lot like SvelteJS and SolidJS
      • Lower memory usage and less code
      • Deciding between opt-in at the component or application level
        • Application level would drop the virutal DOM entirely
      • ETA Q4 or early 2023

Unit Testing

  • "If it ain't testable, it ain't scalable."

  • Document behavior, verify requirements, safety net, and write better code

  • What to test: Inputs and Outputs.

    • What are the things a user can see/do? What are the results of actions?
    • Don't test immplementation details
      • E.G. -- Form submission happens with details, not state change itself
  • Nested Describe blocks

    • First is the name of the component, second and lower describe describes condition/state of the component (using "when"), and it blocks are "verbs"
  • Vitest is a test runner (nearly same API as Jest),

    • Vue Test Utils provides a mounting function
      • Wrapper utility methods, rendering Vue Components
      • Returns a mount function that returns a wrapper
      • VTL render is built off the native mount function
      • This returns the wrapper object
    • Testing Library helpers (getBy, queryBy, findBy)
      • getBy returns a matching Node (error if unmatched) versus queryBy returns null if no element is found
      • findBy returns a promise that will resolve if an element is found, and which will throw if the element doesn't exist
    • fireEvent will fire off an interaction -- even if element cannot be interacted with
      • The better alternative is userEvent which will conform with user behavior (w/ disabled)
    • Jest-DOM includes lots of utility wrappers (toHaveStyle, etc.)
    • RenderComponent utility from @testing-library/vue
  • Can destructure { findByRole, findBy } directly from renderComponent() wrapper

    • Rather than importing these from the library
    • vi.spyOn() with .mockResolvedValue
      • vi.spyOn(imagesApi, 'findAll).mockResolvedValue(mockReturn)
  • We can mock the push in our router and expect it to have been called with certain data

  • Slides here

  • Automatically pull-in and cache API responses?

Page Speed Improvements

  • Most GIFs online are actually videos

    • Use the <video autoplay loop muted playsinline> tag
    • Serve images in next generation formats: avif and webp
      • Better compression, support transparency
    • Wrap <picture> wrapper
    <picture>
      <source type="image/avif" srcset="flower.avif">
      <source type="image/avif" srcset="flower.avif">
    </picture>
    • Or use a modern image CRM, which will automatically switch the format depending on the user's browser
    • VueSDK -- Serves, caches, etc.
  • Load images sooner as well, alongside the style sheets as well

    • Add an additional link in the head tag
    • This will help with the LCP score
    <link rel="preload" as="image" href="https://path-to-image"/>
    • Some frameworks make this easy, like Nuxt
    <nuxt-img src="/my-image.png" alt="some text" preload />
    • Can also leverage "priority hints" for LCP images (not supported)
    <img src="hero.png" fetchpriority="high" />
    • Lazy-loading non-critical images
    <img src="unimportant.png" loading="lazy" />
    • Use source set attribute to select the right sized image
      • Frameworks can help with this
      <nuxt-img src="/nuxt-hero.png" sizes="sm:300px md:500px" />
      • Don't preload fonts
    • FontAwesome is slow as fuck

Component testing w/ Playwright

Node Approach

  • Test Runner is NodeJS

  • JSDOM has fake browser (html tags)

    Browser Approach

  • Real CSS and components

  • But no filesystem access

    Playwright Approach

  • Playwright combines file system access, parallelization, etc. with NodeJS

  • Builds the index.html and index.js files with Vite when the test is run

    • Uses a real browser to then test the code
    • Can use normal testing practices (can debug inside of Javascript)
    • Awaits out of the box
    • docs

Pinia

  • Looking back, Vuex almost maps to the options API, where...
    • data --> state
    • methods --> mutations/actions
    • computed --> getters
  • The advantage of having the mutations/actions distinction is it gave us time-travel debugging, because we could see which actions were dispatched and how that affected the state of the application
  • There's a lot of boilerplate however to this approach
  • Pinia gets rid of mutations entirely:
import { defineStore } from pinia
export const useTodoStore = defineStore('todoStore', {
  state: () => ({
    todos: []
  }),
  actions: {
    addTodo(todo) {
      this.todos.push(todo)
    }
  },
  getters: {
    doneTodos: (state) => state.todos.filter(todo => todo.done)
  }
})

We would then use this in our component, like so:

<script setup>
import { useTodoListStore } from "../stores/todoStore"
import { storeToRefs } from "pinia"

const store = useTodoStore()
const { todoList } = storeToRefs(store) // Turn our store into reactive values

</script>

<template>
  <div>
    <div v-for "todo in todoList" :key="todo.id">
      {{ todo.item }}
    </div>
  </div>
</template>
  • The reason we use the 'useTodoStore' here is because we import this (much like we would other composables)
  • With Pinia, we don't have a parent child relationship with modules, rather we have single, logical concern-based stores, and we can just import the stores that we need
    • For instance, useTodoStore() in the setup call will give us that store
    • Helps with splitting modules into different files and speed
  • Built-in typescript support (Typescript support w/ Vuex is lacking)
  • Does Pinia work with Vue DevTools? Yes!
  • Doesn't have a super mature ecosystem or plugins (yet)
  • Evan You: "Pinia is defacto Vuex 5. At this point it's really a naming/branding issue."
    • Renaming the library makes it easier to Google for questions/tutorials and so forth
  • The Pinia Source Code
    • Pinia is a monorepo (indicated by the packages directory)
      • Using Lerna, PNPM (which uses symlinks, global installs)
      • Testing helpers stored at npm i @pinia/testing
      • The state property on the Pinia instance is a reactive property. This is the root state.
        • The modules are checked by Ref<Record<string, StateTree>>
      • The root interface can accept plugins because it exposes a .use method
      • It relies on Vue's effectScope function to create and scope watchers, computeds, and other properties all at once (and destroy them all at once)
      • The activePinia is the current Pinia instance. If you're trying to use Pinia in the context of tests (not in a Vue App) you can manually call .setActivePinia to set it.

Lukas Stracke (Know your components)

  • Measuring application components -- measure web vitals
  • LCP (Largest Contentful Paint); FID (First Input Delay); CLS (Cumulative Layout Shift)
    • Most desktop sites suffer from bad image loading
    • For LCP, there is a delay between when the document is ready and the image is even ready (background image defined in CSS, for instance)
  • There are limits though, because these are based on heueristics
    • Perhaps the browser gets its wrong by guessing which thing is the most important (the menu bar needs to be fast, maybe the header image is not)
  • Tracking Components
    • Can measure the component initialization phase (from setup to mounted)
    • The Update Frequency (a million times a second)
    • Can use the developer tools to measure vue components
    • Sentry does instrumentation via mixins (measuring mounting time)

Accessible Components

  • The talk notes are here
  • ADA of 1990 is law behind accessibility rules
  • Don't design with specific disabilities in mind, instead design with the standards in mind. That will reach the broadest number of people.
  • Minimize the level of frustration our users have w/ application
  • Autism, Attention Deficit, Memory loss are most prevalent
  • Level of effort is small if this is baked in early on
  • Semantic Markup should be the ultimate goal
    • ARIA attributes are a good next step when this isn't possible (for instance, if we are using a dropdown that makes heavy use of divs)
    • ARIA labels will supersede whatever content is in the button
  • Assistive devices are trying to build to the HTML spec and WCAG spec
  • WCAG 2.1 -- Targeting level AA
    • Aria code has increased substantially
  • Common issues include
    • <span> and <div> are not focusable elements
    • <span tabindex="0"/> will make it focusable element (not ideal)
    • Buttons should not have divs inside of them (spans instead)
      <button><div>hi</div></button>
    • Inputs should always have labels
    <label for="firstName">
      This is the label
    </label>
    <input id="firstName" type="text" />
    • Use tabindex="0" to add an element to the tab order
      • And tabindex="-1" to remove an element from the tab order
    • Images should have alt attributes
      • Use the role="presentation" to show that there's no semantic meaning to the image
    • Per page, should have no more than one h1, nest these to be semantic
  • Automated accessibility testing: here
    • Lighthouse also has an accessibility section as well

Nuxt3

  • Nuxt Bridge lets us get v3 features in a v2 application (Partnerbase)

    • Nitro Server, Composition API, new CLI and Devtools
  • Nuxi (CLI) will replace create-nuxt-app

  • Nuxt uses both Vite and Nitro

  • There's no pages directory -- by default, there is no vue-router and no paged-based routing

  • Support for Typescript out of the box

  • Nitro is a javascript engine that runs anywhere

    • API routes support
    • Development server with hot reloading
    • Minimal configuration to deploy almost anywhere
    • Express alternative
    • Create a single "events" module that can handle POST/GET/etc.
  • The Nitro server passes around an event object which the body is attached to (unlike Express which uses req/res)

    • Can use composables in the router like useBody, useCookies, etc.
    • For instance, at /api/hello.ts
    export default defineEventHandler((event) => {
      return { 
        hello: 'world'
      }
    })
  • There's a globally available $fetch that lets you hit the backend

  • Nuxt Modules: Asynchronous functions that sequentially run when starting Nuxt

  • "Data" in the /content folder can be easily turned into pages

    • Equivalent of plugins in Gatsby or Next to handle markdown files
    • Can also use components in the content folder -- allows reuse in the MD

Cypress

  • Challenge: You need to compile your code, how to test this?
  • Goal: Reuse the same configuration that you're currently using (either Webpack, ViteJS, Angular, Create-React-App)
  • Component Driven Development -- live reloading of single components and interacting with them directly in the browser

Vue2 to Vue3 Migration

  • We've been on 3.2 for some time (10 months) and unlikely to have breaking changes in the immediate future

  • Why migrate: Stability; Consistency (v2/v3); Maintainability

  • The @vue/compat package is a drop-in package for Vue3

  • Normally, the components would talk directly to Vue, but the compatability layer intercepts Vue2 components and understands their configuration (despite running in a Vue3 environment)

  • Three-phase process to get to Vue3

  • Prepare: Remove as many obstacles as possible before migrating to Vue3

    • Dependencies generally fall into three categories
      • A: Those that can be cross-compatible, or can be replaced
      • B: Those that have a Vue3 version. There's a clear upgrade path, and declares a compatConfig. You must make this upgrade after you upgrade to Vue3, for instance like vuex or vue-router
      • C: Packages that require @vue/compat because they don't have a Vue3 version, and there's no drop-in replacement.
    • Eslint is going to be very helpful
      • Can setup the linter to find rules that aren't compatible w/ Vue3
      • For instance, the old slot-scope syntax
      • v-for and v-if cannot coexist
      • You must also type all the events (like you would props) in Vue3
      • Eslint Vue3 ruleset will be helpful
    • Vue-Test-Utils v2 is only Vue2 compatible, and v2 is only Vue3 compatible
  • Upgrade

    • When you have a migration this big, you're going to have production issues, be ready to revert merge and roll back if you have to
    • Separate branch for Vue3 and back-merging master into it.
    • Ideally this phase takes as little time as possible so you don't have to deal with breaking changes to Vue3
    • Get things running: Test Suite, Eslint, and so forth
    • Just because you're working on a separate branch doesn't mean you shouldn't work on master -- those that can be merged, should be
    • Things to watch out for:
      • When using the composition API, pay special attention to anything that uses the new constructor or Vue.extend
      • Scoped slots don't exist in Vue3
    • Test Upgrades
      • xanf/vue-test-utils-compat could be helpful for managing breaking changes in Vue Test Utils
      • Potentially soon -- open source Eslint configuration for Vue Test Utils
      • You cannot pass a store innto the mount function!
      • When the entire test suite fails, Jest won't report it
      • Vue Test Utils won't automatically render default slot, it'll only render the named slots by default.
    • Libraries should surface a compat configuration

Debugging VueJS Applications

  • Fight complexity by identifying patterns in the frameworks that we use
  • Debugging hooks:
onRenderTriggered((event) => {
  debugger   
})
  • Then you can look at the scope and the event
  • Refs are designed for primitive values, whereas reactives are used for arrays and objects
    • Watchers should be bound to refs for instance

Vue Component Libraries

  • Components allow us to have cohesive design, best practices, de-duplicate code, abstract complexity, and reduce maintenance costs
  • You can also use this to wrap external dependencies
  • Accessing the DOM with this.$el
  • Accessing refs with this.$refs

Animating Vue with GSAP

  • What is GSAP: GreenSock Animation Platform
  • It's a Javascript animation library (was originally actionscript)
  • GSAP can animate SVGs, DOM elements, CSS ...
import { gsap } from "gsap"
  • Basic structure, with either .to() or .from() or .toFrom() methods
gsap.method('selector', { params }, 'position')
  • For instance, to animate a class:
gsap.to(".class", {
  duration: 5,
  x: 100,
  ease: Power2.easeOut,
})
  • You can also combine these animations into a timeline, which lets you run multiple animations in sequence
    • This is great to doing cyclical animations
  • Why would you use this utility instead of CSS animations?
    • It can get messy if your animations is extremely complex
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment