- 25% of all VueJS downloads are Vue3
- Johnson Chu is working full-time at Vue since March on Volar
- 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
- Alternative to storybook
- 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
- Can import
- 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
-
"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"), andit
blocks are "verbs"
- First is the name of the component, second and lower
-
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 nativemount
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
- getBy returns a matching Node (error if unmatched) versus queryBy returns
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)
- The better alternative is
- Jest-DOM includes lots of utility wrappers (toHaveStyle, etc.)
- RenderComponent utility from
@testing-library/vue
- Vue Test Utils provides a mounting function
-
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?
-
Most GIFs online are actually videos
- Use the
<video autoplay loop muted playsinline>
tag - Serve images in next generation formats:
avif
andwebp
- 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.
- Use the
-
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
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
- 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
- For instance,
- 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 modules are checked by
- 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.
- Pinia is a monorepo (indicated by the
- 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)
- 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
- And
- Images should have
alt
attributes- Use the
role="presentation"
to show that there's no semantic meaning to the image
- Use the
- 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
-
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
- 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
-
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
orvue-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
andv-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
- Dependencies generally fall into three categories
-
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 orVue.extend
- Scoped slots don't exist in
Vue3
- When using the composition API, pay special attention to anything that
uses the
- 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
- 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
- Watchers should be bound to
- 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
- 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