Vue.js in Practice
3/1/24About 3 min
Vue.js in Practice
Overview
Vue.js is a progressive JavaScript framework for building user interfaces. This article introduces core Vue.js concepts and practical techniques.
1. Project Initialization
1.1 Creating a Project with Vite
# Create a Vue project
npm create vite@6.5.0 . -- --template vue
# Install dependencies
npm install
# Start the development server
npm run dev1.2 Project Structure
src/
├── App.vue # Root component
├── main.js # Entry file
├── components/ # Component directory
├── views/ # Page views
├── assets/ # Static assets
└── style.css # Global styles2. Core Concepts
2.1 Template Syntax
<template>
<div>
<!-- Interpolation -->
<h1>{{ message }}</h1>
<!-- Attribute binding -->
<img :src="imageUrl" :alt="imageAlt" />
<!-- Event binding -->
<button @click="handleClick">点击</button>
<!-- Conditional rendering -->
<div v-if="show">显示内容</div>
<div v-else>隐藏内容</div>
<!-- List rendering -->
<ul>
<li v-for="item in list" :key="item.id">{{ item.name }}</li>
</ul>
</div>
</template>2.2 Reactive Data
<script setup>
import { ref, reactive, computed } from 'vue'
// Reactive primitives
const count = ref(0)
const message = ref('Hello Vue')
// Reactive objects
const user = reactive({
name: 'Blogger',
age: 30
})
// Computed properties
const doubleCount = computed(() => count.value * 2)
// Update reactive data
function increment() {
count.value++
}
function updateUser() {
user.name = '新名字'
}
</script>2.3 Lifecycle Hooks
<script setup>
import { onMounted, onUpdated, onUnmounted } from 'vue'
// After component is mounted
onMounted(() => {
console.log('组件已挂载')
fetchData()
})
// After component is updated
onUpdated(() => {
console.log('组件已更新')
})
// Before component is unmounted
onUnmounted(() => {
console.log('组件即将卸载')
cleanup()
})
</script>3. Component Communication
3.1 Parent to Child (Props)
<!-- Parent.vue -->
<template>
<Child :message="parentMessage" :count="count" />
</template>
<script setup>
import Child from './Child.vue'
const parentMessage = ref('来自父组件的消息')
const count = ref(10)
</script>
<!-- Child.vue -->
<template>
<div>
<p>{{ message }}</p>
<p>{{ count }}</p>
</div>
</template>
<script setup>
defineProps({
message: {
type: String,
required: true
},
count: {
type: Number,
default: 0
}
})
</script>3.2 Child to Parent (Emits)
<!-- Child.vue -->
<template>
<button @click="sendMessage">发送消息</button>
</template>
<script setup>
const emit = defineEmits(['update', 'custom-event'])
function sendMessage() {
emit('update', '子组件消息')
emit('custom-event', { data: '数据' })
}
</script>
<!-- Parent.vue -->
<template>
<Child @update="handleUpdate" @custom-event="handleCustom" />
</template>
<script setup>
function handleUpdate(message) {
console.log('收到:', message)
}
function handleCustom(event) {
console.log('自定义事件:', event)
}
</script>3.3 Event Bus
// eventBus.js
import { createApp } from 'vue'
const app = createApp({})
export const eventBus = app.config.globalProperties.$bus = {
on: (event, handler) => app.config.globalProperties.$on(event, handler),
emit: (event, ...args) => app.config.globalProperties.$emit(event, ...args)
}
// Usage
import { eventBus } from './eventBus.js'
// Listen for events
eventBus.on('message', (data) => {
console.log('收到消息:', data)
})
// Emit events
eventBus.emit('message', 'Hello World')4. State Management
4.1 Using Pinia
# Install Pinia
npm install pinia// main.js
import { createPinia } from 'pinia'
import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)
const pinia = createPinia()
app.use(pinia)
app.mount('#app')// stores/counter.js
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
export const useCounterStore = defineStore('counter', () => {
// State
const count = ref(0)
// Computed properties
const doubleCount = computed(() => count.value * 2)
// Methods
function increment() {
count.value++
}
function decrement() {
count.value--
}
return { count, doubleCount, increment, decrement }
})<!-- Usage -->
<script setup>
import { useCounterStore } from './stores/counter'
const counter = useCounterStore()
</script>
<template>
<div>
<p>Count: {{ counter.count }}</p>
<p>Double: {{ counter.doubleCount }}</p>
<button @click="counter.increment()">+</button>
<button @click="counter.decrement()">-</button>
</div>
</template>5. Router Configuration
5.1 Installing Vue Router
npm install vue-router@4// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import Home from '../views/Home.vue'
import About from '../views/About.vue'
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/about',
name: 'About',
component: About
},
{
path: '/user/:id',
name: 'User',
component: () => import('../views/User.vue') // Lazy loading
}
]
const router = createRouter({
history: createWebHistory(),
routes
})
export default router<!-- Using the router -->
<template>
<div>
<!-- Navigation links -->
<router-link to="/">首页</router-link>
<router-link to="/en/about">About</router-link>
<router-link :to="`/user/${userId}`">用户</router-link>
<!-- Router view -->
<router-view />
</div>
</template>6. Composition API
6.1 <script setup> Syntax
<script setup>
import { ref, onMounted } from 'vue'
const count = ref(0)
function increment() {
count.value++
}
onMounted(() => {
console.log('组件挂载')
})
</script>
<template>
<button @click="increment">{{ count }}</button>
</template>6.2 Custom Composables
// composables/useFetch.js
import { ref, onMounted } from 'vue'
export function useFetch(url) {
const data = ref(null)
const error = ref(null)
const loading = ref(true)
async function fetchData() {
try {
const response = await fetch(url)
data.value = await response.json()
} catch (err) {
error.value = err
} finally {
loading.value = false
}
}
onMounted(fetchData)
return { data, error, loading, refetch: fetchData }
}<!-- Usage -->
<script setup>
import { useFetch } from './composables/useFetch'
const { data, error, loading } = useFetch('/api/data')
</script>
<template>
<div v-if="loading">加载中...</div>
<div v-else-if="error">错误: {{ error.message }}</div>
<div v-else>{{ data }}</div>
</template>7. Performance Optimization
7.1 v-memo Caching
<template>
<div v-memo="[value]">
<!-- Re-render only when value changes -->
{{ expensiveComputation(value) }}
</div>
</template>7.2 v-once Single Render
<template>
<div v-once>
<!-- Render once and never update again -->
{{ staticContent }}
</div>
</template>7.3 Virtual Scrolling
npm install @vueuse/core<script setup>
import { useVirtualList } from '@vueuse/core'
const list = Array.from({ length: 10000 }, (_, i) => ({ id: i, text: `Item ${i}` }))
const { items, containerProps, wrapperProps } = useVirtualList(
list,
(item) => item.id,
{
itemHeight: 40,
overscan: 5
}
)
</script>
<template>
<div v-bind="containerProps" style="height: 400px; overflow: auto;">
<div v-bind="wrapperProps">
<div v-for="item in items" :key="item.id" style="height: 40px;">
{{ item.text }}
</div>
</div>
</div>
</template>Summary
Vue.js offers a concise API and powerful features. Mastering the Composition API, component communication, and state management is key to building large-scale applications.
Author: Lei Tao
Date: March 1, 2024