Enterprise Vue

patterns that scale

My background

What does "enterprise" mean?

What I'll talk about

  1. Is Vue enterprise-ready?
  2. Getting started
  3. Organizing large apps
  4. Working in a team
  5. Testing
  6. Sharing between teams/apps
  7. Optimizing performance
  8. Vue 3

These slides are a resource list!

Is Vue enterprise-ready?

Yes

Progressive integrations

Getting started

even for existing projects

Organizing large apps

Component organization

flat vs nested

Flat makes refactors easier

no need to update imports after moving a component

Flat makes finding files easier

folder scoping leads to lazily named files, because they don't have to be unique

Wrap vendor components

More easily switch to an alternative

Control the interface

Base components

Vuex module registration

import Vue from 'vue'
import Vuex from 'vuex'
import auth from './modules/auth'
import cart from './modules/cart'
import dashboard from './modules/dashboard'
import userSettings from './modules/userSettings'
import products from './modules/products'
import users from './modules/users'

Vue.use(Vuex)

const store = new Vuex.Store({ 
  modules: {
    auth,
    cart,
    dashboard,
    userSettings,
    products,
    users
  } 
})

šŸ˜³

Automatic Vuex module registration

import Vue from 'vue'
import Vuex from 'vuex'
import modules from './modules'

Vue.use(Vuex)

const store = new Vuex.Store({ modules })

Custom module defaults

const store = new Vuex.Store({ modules })

// Automatically run the `init` action for
// every module, if one exists.
dispatchActionForAllModules('init')
modules[modulePath.pop()] = {
  // Modules are namespaced by default
  namespaced: true,
  ...requireModule(fileName),
}

boilerplate:

src/state/store.js

Grouped state helpers

import { mapState, mapGetters, mapActions } from 'vuex'

export const authComputed = {
  ...mapState('auth', {
    currentUser: state => state.currentUser,
  }),
  ...mapGetters('auth', ['loggedIn']),
}

export const authMethods = mapActions('auth', 
  ['logIn', 'logOut']
)

boilerplate: src/state/helpers.js

Using state helper groups

import { 
  authComputed, 
  authActions 
} from '@state/helpers'

export default {
  computed: {
    ...authComputed,
  },
  methods: {
    ...authActions,
  },
}

Working in a team

(making it easy to follow best practices)

Why?

Faster development

great tooling can not only catch typos and potential errors, but also teach new features as developers need them

More useful code reviews

Faster reviews, more learning, more motivatingĀ 

Higher quality code

when developers feel cared for by their tooling, they take more pride in their work and write better code

module.exports = {
  '*.js': [
    'yarn lint:eslint',
    'yarn lint:prettier',
    'git add',
    'yarn test:unit:file',
  ],
  '*.vue': [
    'yarn lint:eslint',
    'yarn lint:stylelint',
    'yarn lint:prettier',
    'git add',
    'yarn test:unit:file',
  ],
  '*.scss': ['yarn lint:stylelint', 'yarn lint:prettier', 'git add'],
  '*.md': ['yarn lint:markdownlint', 'yarn lint:prettier', 'git add'],
  '*.{png,jpeg,jpg,gif,svg}': ['imagemin-lint-staged', 'git add'],
}

Reduce friction

speed up development and simplify refactors

Enforce best practices

e.g. automatically generate a unit test with each module

Meta validations

Warn other devs when they might be using a component wrong

Ā 

(when a prop's validator isn't enough)

Meta validations

methods: {
  // Perform more complex prop validations than is possible
  // inside individual validator functions for each prop.
  validateProps() {
    if (process.env.NODE_ENV === 'production') return

    // Check for insecure URL in href.
    if (!this.allowInsecure && !/^(https|mailto|tel):/.test(this.href)) {
      return console.warn(
        `Insecure <BaseLink> href: ${
          this.href
        }.\nWhen linking to external sites, always prefer https URLs.`
      )
    }
  }
}

More useful patterns

in the Vue Style Guide

Testing

Testing

we stop writing and running tests when they're NOT...

Visible

Everyone knows when

tests are failing

Reliable

Tests don't fail intermittently

Fast

Tests run quickly

Debuggable

When a test fails,

it's easy to learn why

Tool recommendations

  • Cypress for almost all e2e tests
  • TestCafe for e2e testing cross-browser inconsistencies, with screenshot diffing
  • Jest + Vue Test Utils for unit tests

Just test everything?

Are you flossing

after every meal?

Two essential E2E tests

  • Can users log in?
  • Can they give us money?

Sharing between teams/apps

Shared component library?

Shared component library

3 tips

  1. Monorepos
  2. Monorepos
  3. Monorepos

Monorepo !== Monolith

Monorepo != Monolith

Defining a monorepo

A single codebase, with independently deployable/publishable projects that share some common resourcesĀ 

Benefits of monorepos

Faster development

Easier to reorganize

Monorepo tooling

Performance

Stateless and instance-less

(no data, computed, etc and no lifecycle)

<template functional>
  <!-- ... -->
</template>
export default {
  functional: true,
  render (h, context) {
    // ...
  }
}

or

Functional components

Improve performance

(especially for components rendered many times, e.g. with a v-for)

Can return multiple root nodes

boilerplate: src/components/nav-bar-routes.vue

Functional components

Lazy-loaded routes

{
  path: '/admin',
  name: 'admin-dashboard',
  component: () => import('@views/admin'),
}
{
  path: '/admin',
  name: 'admin-dashboard',
  component: require('@views/admin').default,
}
<keep-alive>
  <component :is="currentTabComponent" />
</keep-alive>

Avoid expensive rerenders

Enterprise apps with Vue 3

The composition API

in Vue 3

The composition API

helps tame monster components

  • Easier refactoring

  • More flexible architecture

  • First-class TypeScript support

The composition API

can also be useful for global state management

Learn more about the composition API

That's all!

Special thanks to my Vuesionary sponsors

  • Vuetify
  • Vue School
  • Vue Mastery
  • Ben Hong

@chrisvfritz

Made with Slides.com