Lesson 09-Vue3 Testing, Optimization, Building, and Deployment

Vue 3 Testing

Unit Testing

Unit testing focuses on the smallest testable units of an application (typically components or functions) to verify they work as expected.

Using Jest and Vue Test Utils
Jest is a popular JavaScript testing framework, and Vue Test Utils is the official testing utility library for Vue, together providing robust testing capabilities.

Install Dependencies

npm install --save-dev jest @vue/test-utils vue-jest babel-jest @babel/core @babel/preset-env

Configure Jest

// jest.config.js
module.exports = {
  preset: '@vue/cli-plugin-unit-jest',
  transform: {
    '^.+\\.vue$': 'vue-jest',
    '.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$': 'jest-transform-stub',
  },
};

Write Tests

// tests/unit/MyComponent.spec.js
import { shallowMount } from '@vue/test-utils';
import MyComponent from '@/components/MyComponent.vue';

describe('MyComponent.vue', () => {
  it('renders props.msg when passed', () => {
    const msg = 'Hello Vue 3!';
    const wrapper = shallowMount(MyComponent, {
      propsData: { msg },
    });
    expect(wrapper.text()).toMatch(msg);
  });
});

Integration Testing

Integration testing focuses on the interactions between components and whether they work together as a whole.

Using Cypress
Cypress is an end-to-end testing framework suitable for integration testing.

Install Cypress

npm install cypress --save-dev

Write Tests

// cypress/integration/app.spec.js
it('visits the app root url', () => {
  cy.visit('/');
  cy.contains('Hello World');
});

Vue 3 Performance Optimization

Virtual DOM and Diff Algorithm Optimization

Virtual DOM Rendering Process Optimization

Vue’s virtual DOM, implemented via the snabbdom library, achieves efficient diffing by reducing unnecessary node comparisons:

// Before optimization: Triggers full updates frequently
this.items = newDataArray; 

// After optimization: Uses key attribute + partial updates
<template v-for="item in items" :key="item.id">
  <div>{{ item.text }}</div>
</template>

Source-Level Optimization Principles:

  1. Patch Flags: Vue 3’s compile-time marking system flags dynamic nodes via static analysis.
  2. Block Tree: Builds static subtrees to skip diffing, reducing diff computation by ~60%.
  3. Longest Increasing Subsequence (LIS) Algorithm: Optimizes node movement, reducing time complexity from O(n³) to O(nlogn).

Practical Optimization Techniques

// 1. Avoid using v-for with v-if together
// Bad: Executes v-if check on every render
<div v-for="item in list" v-if="item.visible">{{ item.name }}</div>

// Good: Filter data beforehand
<div v-for="item in visibleItems">{{ item.name }}</div>

// 2. Use functional components for static content
Vue.component('static-content', {
  functional: true,  // Stateless component
  render(h) {
    return h('div', 'Static content')
  }
})

Component Lazy Loading and Code Splitting

Dynamic Imports for On-Demand Loading

// Route-level lazy loading
const UserDetail = () => import('./views/UserDetail.vue')

// Component-level lazy loading
export default {
  components: {
    HeavyComponent: () => import('./HeavyComponent.vue')
  }
}

Underlying Implementation Mechanism:

  1. Webpack’s import() syntax is converted to __webpack_require__.e for dynamic loading.
  2. Generates separate chunk files, loaded asynchronously via <script> tags.
  3. Vue’s async component factory functions handle loading states.

Preloading Strategy Optimization

// Webpack magic comments for preloading
const UserDetail = () => import(
  /* webpackPrefetch: true */
  /* webpackChunkName: "user-detail" */
  './views/UserDetail.vue'
)

Preloading Strategy Comparison:

Strategy TypeTrigger TimingApplicable Scenarios
preloadAfter parent chunk loadsCritical path components
prefetchDuring idle timeSecondary route components
dynamic importOn-demand triggerUser interaction components

Data Caching and Reuse (keep-alive)

keep-alive Implementation Principle

Vue’s <keep-alive> component manages component instances using the LRU (Least Recently Used) algorithm:

// Simplified implementation principle
class KeepAlive {
  constructor(max = 10) {
    this.cache = new Map()
    this.keys = []
    this.max = max
  }

  get(key) {
    if (this.cache.has(key)) {
      // Update LRU queue
      this.keys.splice(this.keys.indexOf(key), 1)
      this.keys.push(key)
      return this.cache.get(key)
    }
    return null
  }

  add(key, instance) {
    if (this.keys.length >= this.max) {
      // Evict least recently used instance
      const oldestKey = this.keys.shift()
      this.cache.delete(oldestKey)
    }
    this.cache.set(key, instance)
    this.keys.push(key)
  }
}

Optimization Usage Techniques:

<!-- 1. Specify included/excluded components -->
<keep-alive :include="['UserDetail', 'ProductList']">
  <router-view />
</keep-alive>

<!-- 2. Combine with route meta information -->
<keep-alive>
  <router-view v-if="$route.meta.keepAlive" />
</keep-alive>
<router-view v-if="!$route.meta.keepAlive" />

Event Listeners and Memory Management

Common Memory Leak Scenarios

  1. Unremoved global event listeners.
  2. Undestroyed timers.
  3. Closures referencing DOM elements.
  4. Third-party libraries not properly cleaned up.

Optimization Practices

export default {
  data() {
    return {
      timer: null,
      eventHandler: null
    }
  },
  mounted() {
    // 1. Safe event binding
    this.eventHandler = this.handleResize.bind(this)
    window.addEventListener('resize', this.eventHandler)
    
    // 2. Safe timer management
    this.timer = setInterval(this.fetchData, 5000)
  },
  beforeUnmount() {
    // 3. Thorough resource cleanup
    window.removeEventListener('resize', this.eventHandler)
    clearInterval(this.timer)
    
    // 4. Third-party library cleanup example
    if (this.chart) {
      this.chart.dispose()
      this.chart = null
    }
  }
}

Automated Detection Tools

# Using Chrome DevTools Memory Panel
1. Record Heap Snapshot
2. Use Allocation instrumentation timeline to track memory allocation
3. Detect Detached DOM trees

# Vue-specific tool
vue-cli-plugin-memory-leak-detector

Performance Monitoring and Analysis Tools

Lighthouse Advanced Configuration

// Lighthouse configuration example (.lighthouserc.js)
module.exports = {
  ci: {
    collect: {
      url: ['http://localhost:8080'],
      numberOfRuns: 3,
      settings: {
        throttling: {
          rttMs: 150,
          throughputKbps: 1638.4,
          cpuSlowdownMultiplier: 4
        }
      }
    },
    assert: {
      assertions: {
        'categories:performance': ['error', {minScore: 0.9}]
      }
    }
  }
}

Vue DevTools Performance Panel

  1. Component Rendering Timeline: Identify rendering bottleneck components.
  2. Event Processing Time: Analyze user interaction response times.
  3. Vuex State Change Tracking: Optimize state management.

Performance Analysis Process:

  1. Record performance timeline.
  2. Identify Long Tasks.
  3. Analyze component update causes.
  4. Locate redundant rendering components.

Custom Performance Metrics Monitoring

// Use Performance API to monitor key metrics
export function trackPerf() {
  const [pageNav] = performance.getEntriesByType('navigation')
  
  const metrics = {
    FCP: performance.getEntriesByName('first-contentful-paint')[0],
    LCP: performance.getEntriesByType('largest-contentful-paint')[0],
    CLS: performance.getEntriesByType('layout-shift').reduce(...)
  }

  // Report monitoring data
  if (metrics.FCP && metrics.FCP.startTime > 2000) {
    sendAlert('FCP performance below standard')
  }
}

// Call in App.vue
mounted() {
  this.$nextTick(() => {
    trackPerf()
  })
}

Code Quality Improvements

Code quality is a critical aspect of software engineering, impacting application maintainability and scalability.

Using ESLint

ESLint is a static code analysis tool that helps enforce best practices and coding standards.

Install ESLint

npm install eslint --save-dev

Configure ESLint

// .eslintrc.js
module.exports = {
  env: {
    browser: true,
    es6: true,
  },
  extends: [
    'plugin:vue/vue3-recommended',
    'eslint:recommended',
  ],
  parserOptions: {
    parser: 'babel-eslint',
  },
  rules: {
    // Custom rules
  },
};

Using Prettier

Prettier is a code formatter that automatically adjusts code style to ensure consistency.

Install Prettier

npm install prettier --save-dev

Configure Prettier

// .prettierrc
{
  "semi": true,
  "singleQuote": true,
  "printWidth": 100,
  "tabWidth": 2
}

Vue 3 Build

Building the Application

Building an application involves converting source code into static files (HTML, CSS, and JavaScript) that browsers can run directly. Vue CLI provides a command-line tool to streamline this process.

Using Vue CLI to Build

# Build for development environment
npm run serve

# Build for production environment
npm run build

After building, the files in the dist directory are ready for deployment.

Configuring Environment Variables

Applications may require different configurations in various environments, such as API endpoints or database connection strings. Vue CLI supports environment variable management via .env files.

Create .env File
Create a .env file in the project root to store common environment variables.

VUE_APP_API_URL=https://api.example.com

Create Environment-Specific .env Files
To differentiate environments, create files like .env.development, .env.production, and .env.staging.

# .env.development
VUE_APP_API_URL=https://dev-api.example.com

# .env.production
VUE_APP_API_URL=https://prod-api.example.com

Use Environment Variables
Access environment variables in the application via process.env.VUE_APP_API_URL.

// src/main.js
import axios from 'axios';

axios.defaults.baseURL = process.env.VUE_APP_API_URL;

Vue 3 Deployment

Deploying to GitHub Pages

GitHub Pages is a free hosting service for deploying static websites.

Configure publicPath

Since GitHub Pages uses subpaths for URLs, configure publicPath in vue.config.js.

// vue.config.js
module.exports = {
  publicPath: process.env.NODE_ENV === 'production' ? '/your-project-name/' : '/'
};

Deploy to GitHub Pages

npm run deploy

Add a deploy script to package.json.

"scripts": {
  "serve": "vue-cli-service serve",
  "build": "vue-cli-service build",
  "deploy": "npm run build && gh-pages -d dist"
},

Deploying to Vercel

Vercel is a modern cloud deployment platform that supports automatic builds and deployments.

  1. Create Vercel Project
    Log in to the Vercel platform, create a new project, and link it to your GitHub repository.
  2. Configure Vercel
    In project settings, configure the build command and output directory.
{
  "buildCommand": "npm run build",
  "outputDirectory": "dist"
}

Deploy Application
Click “Deploy” to have Vercel automatically build and deploy your application.

Deploying to Netlify

Netlify is another popular static site hosting service.

  1. Create Netlify Project
    Log in to the Netlify platform, create a new project, and link it to your GitHub repository.
  2. Configure Netlify
    In project settings, configure the build command and publish directory.
[build]
  command = "npm run build"
  publish = "dist"

Deploy Application
Netlify will automatically detect and execute the build command, then deploy the application.

Deploying with Docker

Docker enables containerization of applications, ensuring consistent operation across environments.

Create Dockerfile
Create a Dockerfile in the project root.

FROM node:14

WORKDIR /usr/src/app

COPY package*.json ./

RUN npm install

COPY . .

EXPOSE 8080

CMD ["npm", "run", "serve"]

Build Docker Image

docker build -t your-image-name .

Run Docker Container

docker run -p 8080:8080 your-image-name

Deploying with Kubernetes

Kubernetes is a container orchestration system for managing and deploying containerized applications.

Create Deployment and Service YAML Files

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: your-app-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: your-app
  template:
    metadata:
      labels:
        app: your-app
    spec:
      containers:
      - name: your-app-container
        image: your-image-name
        ports:
        - containerPort: 8080

# service.yaml
apiVersion: v1
kind: Service
metadata:
  name: your-app-service
spec:
  selector:
    app: your-app
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080
  type: LoadBalancer

Apply YAML Files

kubectl apply -f deployment.yaml
kubectl apply -f service.yaml
Share your love