当前位置: 首页 > news >正文

Vue3视图渲染技术与JavaWeb开发实践

1. Vue3视图渲染技术概述

在JavaWeb开发中,前端工程化已经成为现代Web应用开发的标准实践。Vue3作为当前最主流的前端框架之一,其视图渲染技术为开发者提供了高效、灵活的开发体验。与传统的JSP、Thymeleaf等服务器端渲染技术不同,Vue3的客户端渲染方式能够实现更丰富的交互效果和更流畅的用户体验。

Vue3的视图渲染核心基于虚拟DOM技术,通过高效的Diff算法最小化DOM操作。相比Vue2,Vue3在渲染性能上有显著提升,主要体现在编译时优化(如静态节点提升)、更高效的组件更新机制等方面。这些改进使得Vue3特别适合复杂的企业级JavaWeb应用开发。

提示:虽然Vue3支持选项式API,但在新项目中建议优先使用组合式API,它能更好地组织代码逻辑,特别是在处理复杂业务场景时。

2. Vue3模板语法深度解析

2.1 基础模板语法

Vue3的模板语法是连接JavaScript逻辑和HTML展示的桥梁。最基本的模板语法包括:

  1. 文本插值:使用双大括号{{ }}包裹数据
<p>{{ message }}</p>
  1. 属性绑定:使用v-bind或简写:指令
<img :src="imagePath" alt="产品图片">
  1. 事件绑定:使用v-on或简写@指令
<button @click="submitForm">提交</button>

在实际JavaWeb项目中,这些基础语法通常用于构建CRUD界面。例如,在管理系统中的表单页面:

<div class="form-group"> <label>用户名</label> <input type="text" v-model="user.username" class="form-control" placeholder="请输入用户名"> </div>

2.2 条件渲染与列表渲染

条件渲染(v-if/v-else/v-show)和列表渲染(v-for)是业务开发中最常用的指令。

性能考虑:

  • v-if是真正的条件渲染,会销毁和重建元素
  • v-show只是切换CSS的display属性
  • 在频繁切换的场景使用v-show,运行时条件很少改变时使用v-if

列表渲染示例(结合Java后端数据):

<table class="table"> <thead> <tr> <th>ID</th> <th>姓名</th> <th>操作</th> </tr> </thead> <tbody> <tr v-for="(user, index) in userList" :key="user.id"> <td>{{ user.id }}</td> <td>{{ user.name }}</td> <td> <button @click="editUser(index)">编辑</button> </td> </tr> </tbody> </table>

注意:使用v-for时必须提供唯一的key属性,这对Vue的虚拟DOM diff算法至关重要。在JavaWeb项目中,通常使用数据库ID作为key。

2.3 表单输入绑定

Vue3的v-model指令实现了表单输入和应用状态之间的双向绑定。在JavaWeb开发中,这极大简化了表单处理逻辑。

常见表单元素绑定示例:

<!-- 文本输入 --> <input v-model="formData.username" type="text"> <!-- 单选按钮 --> <input v-model="formData.gender" type="radio" value="male"> 男 <input v-model="formData.gender" type="radio" value="female"> 女 <!-- 下拉选择 --> <select v-model="formData.department"> <option value="dev">开发部</option> <option value="test">测试部</option> </select>

对于复杂表单验证,可以结合Vuelidate或VeeValidate等验证库使用。

3. Vue3组合式API在视图渲染中的应用

3.1 ref与reactive的使用

Vue3的组合式API提供了refreactive两种创建响应式数据的方式:

import { ref, reactive } from 'vue' // 基本类型使用ref const count = ref(0) // 对象类型可以使用reactive const formState = reactive({ username: '', password: '' }) // 在模板中使用 // <p>{{ count }}</p> // <input v-model="formState.username">

在JavaWeb项目中,通常使用reactive来管理表单数据和页面状态:

export default { setup() { const pageData = reactive({ loading: false, tableData: [], pagination: { current: 1, size: 10, total: 0 } }) // 从Java后端获取数据 const fetchData = async () => { pageData.loading = true try { const res = await axios.get('/api/users', { params: { page: pageData.pagination.current, size: pageData.pagination.size } }) pageData.tableData = res.data.list pageData.pagination.total = res.data.total } finally { pageData.loading = false } } return { pageData, fetchData } } }

3.2 计算属性和侦听器

计算属性(computed)和侦听器(watch)是处理衍生数据和副作用的重要工具。

计算属性示例:

const filteredUsers = computed(() => { return pageData.tableData.filter(user => user.status === 'active' ) })

侦听器示例:

watch( () => pageData.pagination.current, (newVal) => { fetchData() } )

在JavaWeb项目中,这些特性常用于:

  1. 表格数据的过滤和排序
  2. 表单字段的联动和验证
  3. 路由参数变化时重新获取数据

4. 组件化开发实践

4.1 组件基础

Vue3的组件系统是构建大型JavaWeb应用的基石。一个典型的单文件组件(SFC)结构:

<template> <!-- 组件模板 --> </template> <script> export default { // 组件逻辑 } </script> <style scoped> /* 组件样式 */ </style>

在JavaWeb项目中,通常会按照功能模块划分组件,例如:

src/ components/ layout/ Header.vue Sidebar.vue business/ UserTable.vue UserForm.vue

4.2 组件通信

Vue3提供了多种组件通信方式:

  1. Props/Events:父子组件通信的基础方式
<!-- 父组件 --> <ChildComponent :title="pageTitle" @submit="handleSubmit"/> <!-- 子组件 --> <script> export default { props: ['title'], emits: ['submit'], methods: { submitForm() { this.$emit('submit', formData) } } } </script>
  1. provide/inject:跨层级组件通信
// 祖先组件 provide('userContext', { userId: 123, userName: '张三' }) // 后代组件 const userContext = inject('userContext')
  1. Pinia/Vuex:全局状态管理

4.3 插槽(Slot)的高级用法

插槽是创建灵活可复用组件的重要特性。Vue3中插槽的主要类型:

  1. 默认插槽
<!-- 组件定义 --> <div class="card"> <slot></slot> </div> <!-- 组件使用 --> <CardComponent> <p>这是卡片内容</p> </CardComponent>
  1. 具名插槽
<!-- 组件定义 --> <div class="layout"> <header> <slot name="header"></slot> </header> <main> <slot></slot> </main> </div> <!-- 组件使用 --> <LayoutComponent> <template #header> <h1>页面标题</h1> </template> <p>主要内容区域</p> </LayoutComponent>
  1. 作用域插槽
<!-- 组件定义 --> <ul> <li v-for="item in items" :key="item.id"> <slot :item="item"></slot> </li> </ul> <!-- 组件使用 --> <ItemList :items="userList"> <template #default="{ item }"> {{ item.name }} - {{ item.email }} </template> </ItemList>

在JavaWeb后台管理系统中,插槽常用于构建可复用的布局组件和业务组件。

5. 性能优化技巧

5.1 虚拟滚动优化长列表

当渲染大量数据时(如后台管理系统中的大数据表格),虚拟滚动可以显著提升性能:

<template> <RecycleScroller class="scroller" :items="largeList" :item-size="50" key-field="id" v-slot="{ item }" > <div class="user-item"> {{ item.name }} - {{ item.email }} </div> </RecycleScroller> </template> <script> import { RecycleScroller } from 'vue-virtual-scroller' import 'vue-virtual-scroller/dist/vue-virtual-scroller.css' export default { components: { RecycleScroller }, data() { return { largeList: [] // 从Java后端获取的大量数据 } } } </script>

5.2 异步组件与代码分割

Vue3的defineAsyncComponent可以实现组件的异步加载,配合Webpack的代码分割功能:

const AsyncComponent = defineAsyncComponent(() => import('./components/HeavyComponent.vue') )

在JavaWeb项目中,可以按路由进行代码分割:

const routes = [ { path: '/report', component: () => import('@/views/ReportView.vue') } ]

5.3 其他优化策略

  1. v-once:只渲染元素和组件一次
<footer v-once>© 2024 公司名称</footer>
  1. v-memo:Vue3.2+新增,可以记住模板树
<div v-memo="[valueA, valueB]"> <!-- 只有当valueA或valueB变化时才会更新 --> </div>
  1. 合理使用shallowRef/shallowReactive:对于不需要深度响应式的大对象

6. 常见问题与解决方案

6.1 模板编译警告处理

在开发过程中,可能会遇到各种模板编译警告。常见问题及解决方法:

问题类型可能原因解决方案
缺少key属性v-for没有提供key为每个v-for项添加唯一key
组件命名冲突组件名与HTML元素冲突使用多单词组件名,如UserTable
属性大小写不匹配HTML不区分大小写模板中使用kebab-case,如<user-info>

6.2 响应式数据问题

Vue3的响应式系统虽然强大,但也有一些需要注意的陷阱:

  1. 解构丢失响应性
// 错误做法 const { x, y } = reactive({ x: 1, y: 2 }) // 正确做法 const pos = reactive({ x: 1, y: 2 }) const { x, y } = toRefs(pos)
  1. 数组变化检测
// 这些方法会触发视图更新 state.list.push(item) state.list.splice(index, 1) // 这些不会触发更新 state.list[index] = newItem state.list.length = 0 // 解决方案 state.list = [...state.list, newItem]

6.3 与Java后端集成问题

在JavaWeb项目中,Vue3前端与Java后端集成时常见问题:

  1. 跨域问题:开发环境下配置代理
// vite.config.js export default defineConfig({ server: { proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true } } } })
  1. 日期时间处理:Java后端返回的日期可能需要格式化
import { format } from 'date-fns' const formattedDate = format(new Date(backendDateStr), 'yyyy-MM-dd')
  1. 大整数处理:Java的Long类型可能超出JS安全整数范围
// 使用json-bigint处理大整数 import JSONbig from 'json-bigint' axios.interceptors.response.use(response => { if (response.data) { response.data = JSONbig.parse(response.data) } return response })

7. 实战:构建JavaWeb管理系统用户模块

7.1 用户列表页实现

结合上述技术,实现一个典型的用户管理列表页:

<template> <div class="user-management"> <div class="toolbar"> <button @click="showAddDialog">新增用户</button> <input v-model="searchQuery" placeholder="搜索用户..."> </div> <el-table :data="filteredUsers" v-loading="loading"> <el-table-column prop="id" label="ID" width="80"></el-table-column> <el-table-column prop="username" label="用户名"></el-table-column> <el-table-column prop="role" label="角色"></el-table-column> <el-table-column label="操作" width="180"> <template #default="scope"> <button @click="editUser(scope.row)">编辑</button> <button @click="deleteUser(scope.row.id)">删除</button> </template> </el-table-column> </el-table> <el-pagination v-model:currentPage="pagination.current" :page-size="pagination.size" :total="pagination.total" @current-change="fetchUsers" ></el-pagination> <UserDialog v-model:visible="dialogVisible" :user="currentUser" @submit="handleSubmit" /> </div> </template> <script> import { ref, computed, onMounted } from 'vue' import axios from '@/utils/axios' import UserDialog from './UserDialog.vue' export default { components: { UserDialog }, setup() { const loading = ref(false) const users = ref([]) const searchQuery = ref('') const dialogVisible = ref(false) const currentUser = ref(null) const pagination = ref({ current: 1, size: 10, total: 0 }) const filteredUsers = computed(() => { return users.value.filter(user => user.username.includes(searchQuery.value) || user.role.includes(searchQuery.value) ) }) const fetchUsers = async () => { loading.value = true try { const res = await axios.get('/api/users', { params: { page: pagination.value.current, size: pagination.value.size } }) users.value = res.data.list pagination.value.total = res.data.total } finally { loading.value = false } } const showAddDialog = () => { currentUser.value = null dialogVisible.value = true } const editUser = (user) => { currentUser.value = { ...user } dialogVisible.value = true } const deleteUser = async (id) => { if (confirm('确定删除此用户吗?')) { await axios.delete(`/api/users/${id}`) await fetchUsers() } } const handleSubmit = async (userData) => { if (userData.id) { await axios.put(`/api/users/${userData.id}`, userData) } else { await axios.post('/api/users', userData) } dialogVisible.value = false await fetchUsers() } onMounted(fetchUsers) return { loading, users, searchQuery, pagination, dialogVisible, currentUser, filteredUsers, fetchUsers, showAddDialog, editUser, deleteUser, handleSubmit } } } </script>

7.2 用户表单对话框组件

<template> <el-dialog :title="user ? '编辑用户' : '新增用户'" v-model="visible" width="50%" > <el-form :model="form" label-width="100px"> <el-form-item label="用户名" required> <el-input v-model="form.username"></el-input> </el-form-item> <el-form-item label="密码" required v-if="!user"> <el-input v-model="form.password" type="password"></el-input> </el-form-item> <el-form-item label="角色"> <el-select v-model="form.role"> <el-option v-for="role in roleOptions" :key="role.value" :label="role.label" :value="role.value" ></el-option> </el-select> </el-form-item> </el-form> <template #footer> <el-button @click="visible = false">取消</el-button> <el-button type="primary" @click="submitForm">确定</el-button> </template> </el-dialog> </template> <script> import { ref, watch } from 'vue' export default { props: { visible: Boolean, user: Object }, emits: ['update:visible', 'submit'], setup(props, { emit }) { const form = ref({ username: '', password: '', role: '' }) const roleOptions = [ { value: 'admin', label: '管理员' }, { value: 'user', label: '普通用户' } ] watch(() => props.user, (newVal) => { if (newVal) { form.value = { ...newVal, password: '' } } else { form.value = { username: '', password: '', role: '' } } }, { immediate: true }) const submitForm = () => { emit('submit', form.value) emit('update:visible', false) } return { form, roleOptions, submitForm } } } </script>

8. 进阶技巧与最佳实践

8.1 自定义指令的应用

Vue3的自定义指令可以封装DOM操作逻辑。例如,实现一个权限控制指令:

// directives/permission.js export const permission = { mounted(el, binding) { const { value } = binding const permissions = store.getters.permissions if (value && !permissions.includes(value)) { el.parentNode?.removeChild(el) } } } // 在组件中使用 <button v-permission="'user:create'">新增用户</button>

8.2 渲染函数与JSX

在某些复杂场景下,渲染函数或JSX比模板更灵活。例如,动态生成表格列:

export default { setup() { const columns = ref([ { prop: 'name', label: '姓名' }, { prop: 'age', label: '年龄' } ]) const renderTable = () => { return h('el-table', { data: tableData.value }, columns.value.map(col => { return h('el-table-column', { prop: col.prop, label: col.label }) })) } return { renderTable } } }

8.3 性能监控与分析

使用Vue DevTools和浏览器性能工具分析组件渲染性能:

  1. 识别渲染时间过长的组件
  2. 检查不必要的重新渲染
  3. 使用<KeepAlive>缓存组件状态
  4. 使用v-once标记静态内容

在开发环境中,可以添加性能标记:

import { markRaw } from 'vue' const heavyComponent = markRaw(import('./HeavyComponent.vue'))

8.4 与JavaWeb传统技术集成

在混合项目中,可能需要与JSP等传统技术集成:

  1. 数据传递:通过全局变量或API接口
<script> window.globalConfig = { userId: "${userId}", userName: "${userName}" } </script>
  1. 渐进式迁移:逐步替换旧有页面
  2. 共享认证信息:统一处理Session和Token

9. 测试与调试策略

9.1 单元测试

使用Jest或Vitest测试Vue组件:

import { mount } from '@vue/test-utils' import UserForm from '@/components/UserForm.vue' test('提交表单时触发submit事件', async () => { const wrapper = mount(UserForm) await wrapper.find('form').trigger('submit') expect(wrapper.emitted()).toHaveProperty('submit') })

9.2 E2E测试

使用Cypress进行端到端测试:

describe('用户管理', () => { it('可以创建新用户', () => { cy.visit('/users') cy.contains('新增用户').click() cy.get('#username').type('testuser') cy.get('#password').type('123456') cy.contains('确定').click() cy.contains('testuser').should('exist') }) })

9.3 调试技巧

  1. 源码映射:确保生成正确的sourcemap
  2. 错误追踪:集成Sentry等错误监控工具
  3. API Mock:使用Mock.js或MSW模拟后端接口
// src/mocks/handlers.js import { rest } from 'msw' export const handlers = [ rest.get('/api/users', (req, res, ctx) => { return res( ctx.json({ list: [{ id: 1, name: 'Mock User' }], total: 1 }) ) }) ]

10. 部署与持续集成

10.1 构建优化

  1. 分包策略:根据路由拆分代码
// vite.config.js export default defineConfig({ build: { rollupOptions: { output: { manualChunks(id) { if (id.includes('node_modules')) { return 'vendor' } } } } } })
  1. Gzip压缩:减小资源体积
// vite.config.js import viteCompression from 'vite-plugin-compression' export default defineConfig({ plugins: [ viteCompression() ] })

10.2 部署到JavaWeb项目

  1. 独立部署:前端部署到CDN/Nginx,通过API与Java后端交互
  2. 集成部署:将构建产物放入Java项目的src/main/resources/static目录
  3. Docker化:构建前端Docker镜像
# 前端Dockerfile示例 FROM nginx:alpine COPY dist /usr/share/nginx/html COPY nginx.conf /etc/nginx/conf.d/default.conf EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]

10.3 CI/CD集成

在GitLab CI或Jenkins中配置自动化流程:

# .gitlab-ci.yml示例 stages: - build - test - deploy build: stage: build image: node:16 script: - npm install - npm run build artifacts: paths: - dist/ test: stage: test image: node:16 script: - npm install - npm run test:unit - npm run test:e2e deploy: stage: deploy image: alpine script: - apk add rsync - rsync -avz dist/ user@server:/var/www/html/

11. 安全最佳实践

11.1 常见前端安全问题

  1. XSS防护

    • 使用v-html时要对内容进行消毒
    • 设置Content Security Policy (CSP)
    • 避免在模板中直接使用用户输入
  2. CSRF防护

    • 确保所有修改操作使用POST/PUT/DELETE方法
    • 从Java后端获取CSRF Token并包含在请求头中
// axios拦截器示例 axios.interceptors.request.use(config => { const token = getCookie('XSRF-TOKEN') if (token) { config.headers['X-XSRF-TOKEN'] = token } return config })
  1. API安全
    • 使用HTTPS加密通信
    • 实现适当的认证和授权
    • 对敏感数据进行加密

11.2 依赖安全

  1. 定期检查项目依赖的安全漏洞:
npm audit
  1. 使用依赖锁定文件(package-lock.json)
  2. 及时更新有安全漏洞的依赖

11.3 生产环境加固

  1. 禁用开发者工具和调试信息
// vite.config.js export default defineConfig({ define: { __VUE_PROD_DEVTOOLS__: false } })
  1. 配置正确的HTTP安全头
  2. 实现敏感操作的二次确认

12. 项目结构与代码组织

12.1 推荐的项目结构

对于JavaWeb项目中的Vue3前端,推荐如下结构:

src/ assets/ # 静态资源 components/ # 公共组件 ui/ # 基础UI组件 business/ # 业务组件 composables/ # 组合式函数 router/ # 路由配置 stores/ # 状态管理 utils/ # 工具函数 views/ # 页面组件 App.vue # 根组件 main.js # 应用入口

12.2 代码风格指南

  1. 命名规范

    • 组件名:PascalCase (如UserForm.vue)
    • 变量/函数:camelCase
    • 常量:UPPER_CASE
  2. 组件设计原则

    • 单一职责原则
    • 合理的props设计
    • 明确的emits定义
  3. 代码分割

    • 按功能拆分组合式函数
    • 避免过大的单文件组件

12.3 文档与注释

  1. 为复杂组件添加JSDoc注释
/** * 用户表单组件 * @prop {Object} user - 要编辑的用户对象 * @emits {submit} - 表单提交事件 */ export default { props: ['user'], emits: ['submit'] }
  1. 使用README.md记录项目配置和约定
  2. 维护CHANGELOG.md记录重要变更

13. 国际化与主题定制

13.1 多语言支持

使用Vue I18n实现国际化:

// src/i18n.js import { createI18n } from 'vue-i18n' const messages = { en: { user: { name: 'Name', create: 'Create User' } }, zh: { user: { name: '姓名', create: '创建用户' } } } export default createI18n({ locale: 'zh', messages })

在组件中使用:

<template> <h1>{{ $t('user.create') }}</h1> </template>

13.2 动态主题切换

实现CSS变量主题切换:

/* 定义主题变量 */ :root { --primary-color: #409eff; --bg-color: #ffffff; } .dark { --primary-color: #79bbff; --bg-color: #1d1e1f; }

在Vue中切换主题:

const toggleTheme = () => { document.documentElement.classList.toggle('dark') }

13.3 响应式设计考虑

  1. 使用CSS媒体查询适配不同屏幕
  2. 考虑移动端触摸操作
  3. 使用rem/em单位实现弹性布局
@media (max-width: 768px) { .form-container { width: 100%; padding: 10px; } }

14. 与Java后端深度集成

14.1 认证与授权

  1. JWT认证集成
// axios拦截器示例 axios.interceptors.request.use(config => { const token = localStorage.getItem('token') if (token) { config.headers.Authorization = `Bearer ${token}` } return config }) axios.interceptors.response.use(response => { return response }, error => { if (error.response.status === 401) { // 跳转到登录页 } return Promise.reject(error) })
  1. 权限控制:基于后端返回的权限数据控制界面元素

14.2 文件上传与下载

  1. 文件上传
<template> <input type="file" @change="handleUpload"> </template> <script> const handleUpload = async (e) => { const file = e.target.files[0] const formData = new FormData() formData.append('file', file) try { await axios.post('/api/upload', formData, { headers: { 'Content-Type': 'multipart/form-data' } }) } catch (error) { console.error('上传失败', error) } } </script>
  1. 文件下载
const downloadFile = async (fileId) => { const response = await axios.get(`/api/files/${fileId}`, { responseType: 'blob' }) const url = window.URL.createObjectURL(new Blob([response.data])) const link = document.createElement('a') link.href = url link.setAttribute('download', 'filename.ext') document.body.appendChild(link) link.click() link.remove() }

14.3 WebSocket实时通信

与Java后端的WebSocket集成:

// 建立WebSocket连接 const socket = new WebSocket('ws://localhost:8080/ws') // 监听消息 socket.addEventListener('message', (event) => { const message = JSON.parse(event.data) // 处理消息 }) // 发送消息 const sendMessage = (content) => { socket.send(JSON.stringify({ type: 'chat', content })) }

15. 微前端架构考虑

15.1 微前端方案选型

在大型JavaWeb项目中,可能需要考虑微前端架构:

  1. qiankun:阿里开源的微前端框架
  2. Module Federation:Webpack 5原生支持
  3. iframe:简单但隔离性强的方案

15.2 qiankun集成示例

主应用配置:

import { registerMicroApps, start } from 'qiankun' registerMicroApps([ { name: 'userApp', entry: '//localhost:7101', container: '#user-container', activeRule: '/user' } ]) start()

子应用(Vue3)配置:

// main.js import { createApp } from 'vue' import App from './App.vue' import { renderWithQiankun, qiankunWindow } from 'vite-plugin-qiankun/dist/helper' let app function render(props = {}) { const { container } = props app = createApp(App) app.mount(container ? container.querySelector('#app') : '#app') } renderWithQiankun({ mount(props) { render(props) }, bootstrap() {}, unmount() { app.unmount() } }) if (!qiankunWindow.__POWERED_BY_QIANKUN__) { render() }

15.3 样式隔离方案

  1. CSS命名空间:为每个微应用添加特定前缀
  2. Shadow DOM:实现真正的DOM隔离
  3. CSS Modules:组件级作用域

16. 移动端适配方案

16.1 响应式布局

使用flex/grid布局和媒体查询:

.container { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 20px; } @media (max-width: 768px) { .container { grid-template-columns: 1fr; } }

16.2 手势操作支持

添加触摸事件处理:

<div @touchstart="handleTouchStart" @touchmove="handleTouchMove" @touchend="handleTouchEnd" ></div>

16.3 PWA支持

配置Vite PWA插件:

// vite.config.js import { VitePWA } from 'vite-plugin-pwa' export default defineConfig({ plugins: [ VitePWA({ registerType: 'autoUpdate', manifest: { name: 'JavaWeb管理系统', short_name: '管理系统', theme_color: '#ffffff' } }) ] })

17. 状态管理进阶

17.1 Pinia状态管理

Pinia是Vue3推荐的状态管理库:

// stores/user.js import { defineStore } from 'pinia' export const useUserStore = defineStore('user', { state: () => ({ currentUser: null, token: '' }), actions: { async login(credentials) { const res = await axios.post('/api/login', credentials) this.currentUser = res.data.user this.token = res.data.token } } })

在组件中使用:

import { useUserStore } from '@/stores/user' const userStore = useUserStore() const login = () => { userStore.login({ username: 'admin', password: '123456' }) }

17.2 状态持久化

使用插件实现状态持久化:

// stores/persist.js import { createPinia } from 'pinia' import piniaPluginPersistedstate from 'pinia-plugin-persistedstate' const pinia = createPinia() pinia.use(piniaPluginPersistedstate) export default pinia

在store中启用:

export const useUserStore = defineStore('user', { persist: true, // ... })

17.3 状态共享模式

  1. Provide/Inject:适合小范围状态共享
  2. Event Bus:简单的事件通信
  3. Composables:可复用的状态逻辑

18. 动画与过渡效果

18.1 Vue过渡组件

使用Vue内置的过渡组件:

<transition name="fade"> <div v-if="show">内容</div> </transition> <style> .fade-enter-active, .fade-leave-active { transition: opacity 0.5s; } .fade-enter-from, .fade-leave-to { opacity: 0; } </style>

18.2 动画库集成

http://www.jsqmd.com/news/1299789/

相关文章:

  • 变压器安装工程的关键技术参数中,接地电阻值与绝缘电阻值的合格范围分别是多少?
  • 无锡气体涡轮流量计厂家推荐
  • 突破网盘限速封锁:8大主流平台直链下载实战指南
  • 2026年江苏谷歌认证推广团队服务评测 - 起跑123
  • Nginx Rewrite规则详解与实战技巧
  • 2026 年广州钻尾螺丝、钢结构螺丝采购,工程采购避坑实测分享 - LYL仔仔
  • AI 批量生成电商商品图的效率瓶颈分析——从单线程到批处理的架构演进
  • 大学生到底考什么证书好
  • 如何查串口日志
  • 主流Agent框架详解
  • [Dify实战] Workflow 跑一半就失败?先补错误分支和兜底输出,应用上线才不慌
  • 光栅衍射原理与工程实践:从多光束干涉到光谱分析应用
  • Windows热键冲突终极解决方案:3分钟定位被占用快捷键的完整指南
  • CoreCycler:精准定位CPU单核稳定性的专业诊断工具
  • 合并报表与汇总表的高效便捷实操指南
  • 单片机毕设项目:嵌入式 STM32 激光测距与阈值调控装置设计 基于激光传感的单片机智能限位报警系统(014801)
  • 桐乡中央水处理公司哪家好?2026避坑指南:5个常见坑+5条硬标准,告别装完就后悔 - mobible
  • 2026北京西城黄金回收——检测全程大屏直播,客户亲眼见证读数结果 - 融媒生活
  • 商家门店人气评选制作教程,云众评选报名通道 + 奖品设置教学 - 微信投票小程序
  • 冰蝎v2.0.1 WebShell流量深度解析:从加密协议到攻击行为还原
  • 计算机网络高效复习指南:从OSI模型到TCP/IP核心协议实战解析
  • 抖音批量下载神器:5分钟掌握高效无水印视频获取技巧
  • diff-pdf:免费开源的PDF视觉差异检测工具终极指南
  • Switch游戏安装终极指南:Awoo Installer让游戏安装变得简单快速
  • KeyboardChatterBlocker:免费Windows终极方案,3步彻底告别键盘连击烦恼
  • Source Han Serif思源宋体:免费开源中文字体终极指南 [特殊字符]
  • BFS解决单词接龙问题:图论与最短路径实践
  • 2026最新 温州管道疏通 厨房卫生间 洗菜池地漏 主管道疏通 打捞 渗水检测 本地反馈强推 - 品牌品鉴馆
  • 深入理解 iOS 开发工具链 Cursor、Xcode、KXApp 全面对比与选择指南
  • Powered by Raspberry Pi:六个真实案例解析 Raspberry Pi 产品在创新应用中的技术价值