Vue2进阶-第二十六篇:Vue2性能分析工具(上)

Vue2进阶-第二十六篇:Vue2性能分析工具(上)

在Vue2项目开发过程中,性能优化是提升用户体验的关键环节。而性能分析工具则是我们发现和解决性能问题的有力武器。Vue Devtools作为一款强大的Vue开发调试工具,其中的性能面板为我们提供了丰富的功能来分析Vue2项目的性能。本篇文章将深入探讨Vue Devtools性能面板以及如何利用它进行组件性能分析。

1. Vue Devtools性能面板

面板功能概览

时间线(Timeline)

Vue Devtools性能面板的时间线功能类似于浏览器的Performance面板。它以时间轴的形式展示了Vue组件的生命周期钩子函数调用、数据更新、DOM渲染等关键事件的发生时间和持续时间。通过时间线,开发者可以直观地看到在某个时间段内,Vue应用内部的各种操作是如何执行的。例如,在用户进行一次页面切换操作时,时间线能够清晰地显示出从路由导航开始,到目标组件的beforeCreatecreatedmounted等生命周期钩子函数的触发顺序和各自的执行时长,帮助我们了解整个操作流程的时间消耗情况。

组件树分析

组件树部分以树形结构展示了Vue应用的组件层级关系。在性能分析中,它不仅能让我们看到组件的嵌套结构,还能显示每个组件的渲染次数、渲染时间等性能相关数据。通过组件树,我们可以快速定位到某个特定组件,查

<template> <view class="sort-container"> <!-- 顶部导航:返回居左 + 标题居中(保持不变) --> <view class="sort-nav"> <text class="nav-btn back-btn" @click="navigateBack">返回</text> <view class="nav-title-wrapper"> <text class="nav-title">排序</text> </view> <view class="nav-placeholder"></view> </view> <!-- 核心修改:删除拖拽容器,仅保留网格展示 --> <view class="images-grid"> <!-- 网格容器:一排2张,间距10rpx --> <view class="grid-container"> <!-- 遍历图片列表生成网格项 --> <view class="grid-item" v-for="(img, idx) in sortedImages" :key="img.key"> <view class="image-item"> <image :src="img.path" class="sort-image" mode="cover"></image> <!-- 序号标签(与索引绑定,自动排序) --> <view class="image-index">{{ idx + 1 }}</view> <!-- 删除按钮(阻止事件冒泡) --> <view class="delete-btn" @click.stop="deleteImage(idx)"> <image src="/static/lawyerletter/delete.svg" class="delete-icon"></image> </view> </view> </view> </view> </view> <!-- 底部操作栏:添加图片 + 确认排序(保持不变) --> <view class="bottom-action"> <text class="add-img-btn" @click="addImage"> <image src="/static/lawyerletter/add.svg" class="add-icon"></image> <text>添加图片</text> </text> <button class="confirm-btn" @click="confirmSort">确定</button> </view> </view> </template> <script setup> import { onLoad} from '@dcloudio/uni-app'; import { ref, onUnmounted , nextTick } from 'vue'; // 1. 布局参数(适配网格展示) const GAP = 10; // 图片间距(水平/垂直) const PADDING = 20; // 容器内边距 // 2. 状态管理(删除拖拽相关状态) const sortedImages = ref([]); // 排序后的图片数组(仅含路径和唯一key) // 3. 页面加载:接收上级页面图片并初始化(删除拖拽坐标相关逻辑) onLoad((options) => { if (options.images) { try { const imgPaths = JSON.parse(decodeURIComponent(options.images)); // 初始化图片列表(仅保留路径和唯一key,删除x/y坐标) sortedImages.value = imgPaths.map((path, idx) => ({ path, key: `img_${idx}_${Date.now()}` // 唯一key确保列表渲染稳定 })); } catch (error) { console.error('解析图片路径失败:', error); sortedImages.value = []; } } }); // 4. 删除图片:删除后自动更新排序(删除拖拽相关对齐逻辑) const deleteImage = (idx) => { if (sortedImages.value.length <= 1) { uni.showToast({ title: '至少保留1张图片', icon: 'none' }); return; } uni.showModal({ title: '删除图片', content: '确定要删除这张图片吗?', success: (res) => { if (res.confirm) { sortedImages.value.splice(idx, 1); // 直接删除对应索引图片 // 序号随数组索引自动更新,无需额外处理 } } }); }; // 5. 添加图片:保持网格布局一致性(删除拖拽坐标相关逻辑) const addImage = () => { uni.showActionSheet({ itemList: ['拍照', '从相册选择'], success: (res) => { const sourceType = res.tapIndex === 0 ? ['camera'] : ['album']; uni.chooseImage({ count: 20 - sortedImages.value.length, // 最多补充到20张 sourceType, sizeType: ['compressed'], success: (res) => { if (res.tempFilePaths.length > 0) { // 新增图片添加到列表末尾(仅保留路径和key) const newImages = res.tempFilePaths.map((path, addIdx) => ({ path, key: `img_${sortedImages.value.length + addIdx}_${Date.now()}` })); sortedImages.value.push(...newImages); // 提示用户 uni.showToast({ title: `添加${res.tempFilePaths.length}张图片`, icon: 'success' }); } }, fail: (err) => { if (err.errMsg.includes('deny')) { uni.showModal({ title: '权限不足', content: `请开启${sourceType[0]}权限`, confirmText: '去设置', success: (res) => res.confirm && uni.openSetting() }); } } }); } }); }; // 6. 确认排序:同步结果到上级页面(删除拖拽对齐逻辑) const confirmSort = () => { // 提取排序后的图片路径(直接按当前数组顺序) const sortedPaths = sortedImages.value.map(img => img.path); // 通知上级页面更新图片顺序 uni.$emit('imagesSorted', sortedPaths); // 返回上级页面 navigateBack(); }; // 7. 返回上级页面(保持不变) const navigateBack = () => { uni.navigateBack(); }; // 8. 页面卸载:清理事件监听(保持不变) onUnmounted(() => { uni.$off('imagesSorted'); }); </script> <style scoped> /* 全局容器:100%高度,flex垂直布局(保持不变) */ .sort-container { height: 100vh; display: flex; flex-direction: column; background-color: #fff; overflow: hidden; } /* 1. 顶部导航样式(保持不变) */ .sort-nav { display: flex; align-items: center; height: 88rpx; padding: 0 24rpx; border-bottom: 1rpx solid #eee; background-color: #fff; position: sticky; top: 0; z-index: 10; } .nav-btn { padding: 10rpx 20rpx; border-radius: 8rpx; min-width: 100rpx; text-align: center; font-size: 28rpx; color: #333; cursor: pointer; } .nav-title-wrapper { flex: 1; display: flex; justify-content: center; align-items: center; } .nav-title { font-size: 32rpx; font-weight: bold; color: #333; } .nav-placeholder { min-width: 100rpx; } /* 2. 图片网格核心样式(删除拖拽相关样式,保留网格) */ .images-grid { flex: 1; padding: 20rpx; box-sizing: border-box; overflow-y: auto; /* 图片超出时垂直滚动 */ background-color: #fafafa; /* 滚动条美化(可选) */ &::-webkit-scrollbar { width: 6rpx; } &::-webkit-scrollbar-thumb { background-color: #ddd; border-radius: 3rpx; } } /* 网格容器:一排2列,间距10rpx */ .grid-container { display: grid; grid-template-columns: repeat(2, 1fr); /* 2列均分宽度 */ grid-gap: 10rpx; /* 行列间距10rpx */ } /* 网格项:固定宽高比(正方形) */ .grid-item { aspect-ratio: 1 / 1; /* 宽高比1:1,正方形图片 */ border-radius: 8rpx; overflow: hidden; box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.08); background-color: #fff; } /* 图片容器:相对定位承载子元素(保持不变) */ .image-item { width: 100%; height: 100%; position: relative; } /* 图片样式:填充容器,保持比例(保持不变) */ .sort-image { width: 100%; height: 100%; object-fit: cover; /* 避免图片拉伸变形 */ } /* 序号标签:左上角悬浮(保持不变) */ .image-index { position: absolute; top: 8rpx; left: 8rpx; background-color: rgba(0, 0, 0, 0.6); color: #fff; font-size: 26rpx; font-weight: bold; width: 38rpx; height: 38rpx; border-radius: 50%; display: flex; align-items: center; justify-content: center; z-index: 2; } /* 删除按钮:右下角悬浮(保持不变) */ .delete-btn { position: absolute; bottom: 8rpx; right: 8rpx; width: 38rpx; height: 38rpx; border-radius: 50%; background-color: rgba(0, 0, 0, 0.6); display: flex; align-items: center; justify-content: center; z-index: 2; transition: background-color 0.2s ease; cursor: pointer; } .delete-btn:active { background-color: rgba(231, 76, 60, 0.8); } .delete-icon { width: 24rpx; height: 24rpx; color: #fff; } /* 3. 底部操作栏样式(保持不变) */ .bottom-action { display: flex; align-items: center; justify-content: space-between; height: 132rpx; border-top: 1rpx solid #eee; padding: 0 24rpx; gap: 20rpx; background-color: #fff; position: sticky; bottom: 0; z-index: 10; } .add-img-btn { display: flex; align-items: center; justify-content: center; border-radius: 20rpx; width: 140rpx; font-size: 24rpx; color: #333; cursor: pointer; } .add-icon { width: 24rpx; height: 24rpx; margin-right: 8rpx; } .confirm-btn { background-color: #e74c3c !important; color: #fff !important; width: 400rpx !important; height: 70rpx !important; line-height: 70rpx !important; border-radius: 35rpx !important; font-size: 30rpx !important; font-weight: 500 !important; display: flex !important; align-items: center !important; justify-content: center !important; padding: 0 !important; margin: 0 !important; border: none !important; box-shadow: none !important; } .confirm-btn:active { opacity: 0.8; } </style> 根据这个带啊吗修改 将图片拖拽位置后自动排序
最新发布
09-18
<think> 根据用户需求,需要在Vue3项目中实现图片拖拽排序功能。结合提供的引用内容,特别是引用[3]中关于拖拽事件的处理逻辑,我们可以使用原生HTML5拖拽API或第三方库(如vuedraggable)来实现。考虑到用户提供的引用中提到了SortableJSvuedraggable,以及用户要求使用Vue3,这里我们选择使用vuedraggable,因为它在Vue3中集成方便且功能强大。 步骤: 1. 安装vuedraggable:使用npm或yarn安装vuedraggable。 2. 在组件中引入vuedraggable。 3. 使用vuedraggable组件包裹图片列表,并绑定数据。 4. 配置vuedraggable的事件和选项,以实现拖拽排序。 5. 使用CSS美化拖拽效果。 注意:用户提供的引用[2]和[3]中提到了Element UI和Ruoyi框架,但用户没有明确要求使用Element Plus(Vue3版本),所以这里只使用vuedraggable和原生样式。若需要结合Element Plus,可以在此基础上调整。 实现代码: 首先,安装vuedraggable: ```bash npm install vuedraggable@next ``` 然后,在组件中引入并使用: ```vue <template> <div> <!-- 使用vuedraggable组件 --> <draggable v-model="images" item-key="id" tag="ul" class="image-list" @end="onDragEnd" > <template #item="{element}"> <li class="image-item"> <img :src="element.url" :alt="element.name" /> <span>{{ element.name }}</span> </li> </template> </draggable> </div> </template> <script> // 引入vuedraggable import draggable from 'vuedraggable' export default { name: 'ImageSort', components: { draggable }, data() { return { images: [ { id: 1, name: 'Image 1', url: 'https://picsum.photos/200/300?random=1' }, { id: 2, name: 'Image 2', url: 'https://picsum.photos/200/300?random=2' }, { id: 3, name: 'Image 3', url: 'https://picsum.photos/200/300?random=3' }, { id: 4, name: 'Image 4', url: 'https://picsum.photos/200/300?random=4' } ] } }, methods: { onDragEnd() { // 拖拽结束后的逻辑,这里可以触发排序后的数据保存等操作 console.log('排序后的数组:', this.images); // 可以在这里触发更新到后端或者父组件 // this.$emit('update', this.images); } } } </script> <style scoped> .image-list { list-style: none; padding: 0; display: flex; flex-wrap: wrap; } .image-item { margin: 10px; padding: 10px; border: 1px solid #ddd; border-radius: 4px; cursor: move; /* 表示可拖动 */ display: flex; flex-direction: column; align-items: center; } .image-item img { width: 100px; height: 100px; object-fit: cover; } /* 拖拽时的样式 */ .sortable-chosen { opacity: 0.8; } .sortable-ghost { opacity: 0.4; } </style> ``` 代码说明: - `draggable`组件通过`v-model`绑定数据数组`images`,当拖拽排序后,数组的顺序会自动更新。 - `item-key`指定每个项目的唯一标识字段(这里用id)。 - `tag`指定包裹的标签,这里用ul,内部每个项目用li。 - 通过`@end`事件监听拖拽结束,可以在这里执行后续操作(如保存排序结果)。 - 样式部分:为图片列表和每个图片项设置样式。`.sortable-chosen`和`.sortable-ghost`是拖拽过程中vuedraggable自动添加的类,用于设置拖拽时和放置目标的样式。 注意事项: 1. 如果需要在拖拽时显示更复杂的视觉效果,可以进一步调整CSS。 2. 如果项目需要结合Element Plus,可以将`image-item`换成Element Plus的卡片样式,但核心拖拽逻辑不变。 3. 如果用户需要上传图片功能,可以结合Element Plus的Upload组件,但此代码仅展示拖拽排序。 相关问题: 1. 如何实现图片上传功能并与拖拽排序结合? 2. 使用vuedraggable时如何限制只能在特定区域内拖拽? 3.Vue3中如何实现跨组件拖拽排序? </think> 基于您的需求,我将提供一个完整的Vue3实现方案,使用原生HTML5拖拽API实现图片拖拽排序功能。以下是整合了HTML模板、Vue3逻辑和CSS样式的解决方案: ```vue <template> <div class="image-sort-container"> <h2>图片拖拽排序</h2> <!-- 图片列表区域 --> <ul class="image-list" @dragover.prevent="handleDragOver" @dragenter.prevent > <li v-for="(image, index) in images" :key="image.id" class="image-item" draggable="true" @dragstart="handleDragStart(index)" @dragend="handleDragEnd" @drop="handleDrop(index)" > <img :src="image.url" :alt="image.name"> <span>{{ image.name }}</span> </li> </ul> <div class="image-order"> <h3>当前排序:</h3> <p>{{ imageOrder }}</p> </div> </div> </template> <script setup> import { ref, computed } from 'vue' // 图片数据 const images = ref([ { id: 1, name: '图片1', url: 'https://picsum.photos/200/300?random=1' }, { id: 2, name: '图片2', url: 'https://picsum.photos/200/300?random=2' }, { id: 3, name: '图片3', url: 'https://picsum.photos/200/300?random=3' }, { id: 4, name: '图片4', url: 'https://picsum.photos/200/300?random=4' } ]) // 当前拖拽的图片索引 const dragIndex = ref(null) // 处理拖拽开始 const handleDragStart = (index) => { dragIndex.value = index setTimeout(() => { event.target.classList.add('dragging') }, 0) } // 处理拖拽结束 const handleDragEnd = () => { event.target.classList.remove('dragging') } // 处理拖拽悬停 const handleDragOver = (event) => { const target = event.target // 确保目标元素是图片项 if (target.classList.contains('image-item')) { event.preventDefault() target.classList.add('drag-over') } } // 处理放置 const handleDrop = (dropIndex) => { event.preventDefault() // 获取拖拽图片和目标位置 const dragItem = images.value[dragIndex.value] // 执行数组元素位置交换 images.value.splice(dragIndex.value, 1) images.value.splice(dropIndex, 0, dragItem) // 清除样式 event.target.classList.remove('drag-over') } // 计算当前图片排序 const imageOrder = computed(() => images.value.map(img => img.name).join(' → ') ) </script> <style scoped> .image-sort-container { max-width: 800px; margin: 0 auto; padding: 20px; font-family: Arial, sans-serif; } .image-list { display: flex; flex-wrap: wrap; gap: 15px; list-style: none; padding: 0; } .image-item { width: 150px; border: 1px solid #e1e1e1; border-radius: 8px; padding: 10px; background: #fff; cursor: grab; transition: all 0.3s ease; display: flex; flex-direction: column; align-items: center; } .image-item:hover { box-shadow: 0 5px 15px rgba(0,0,0,0.1); } .image-item.dragging { opacity: 0.5; background-color: #f0f0f0; } .image-item.drag-over { border: 2px dashed #409eff; background-color: #ecf5ff; } .image-item img { width: 100%; height: 120px; object-fit: cover; border-radius: 4px; margin-bottom: 8px; } .image-order { margin-top: 30px; padding: 15px; background-color: #f5f7fa; border-radius: 8px; } .image-order h3 { margin-top: 0; color: #606266; } </style> ``` ### 功能说明: 1. **拖拽排序逻辑**: - 使用HTML5原生拖拽API实现 - `@dragstart`记录当前拖拽项索引 - `@dragover.prevent`防止默认行为允许放置 - `@drop`处理放置时的数组元素位置交换[^3] 2. **核心方法**: ```javascript // 交换数组元素位置 images.value.splice(dragIndex.value, 1) images.value.splice(dropIndex, 0, dragItem) ``` 3. **视觉反馈**: - `.dragging`类:拖拽中的项目半透明显示 - `.drag-over`类:放置目标区域高亮显示 - 实时显示当前排序状态 4. **响应式更新**: - 使用Vue3的`computed`属性实时计算图片顺序 - 数组操作后自动触发视图更新 ### 进阶优化建议: 1. **性能优化**: ```javascript // 使用虚拟滚动替代完整列表渲染 import { useVirtualList } from '@vueuse/core' const { list, containerProps } = useVirtualList(images, { itemHeight: 40 }) ``` 2. **动画效果**: ```css /* 添加排序动画 */ .sort-move { transition: transform 0.5s ease; } ``` 3. **跨组件拖拽**: ```javascript // 使用自定义事件实现跨组件通信 const emit = defineEmits(['drag-start', 'drag-end']) ``` ### 相关问题: 1. 如何在Vue3中使用第三方拖拽库(如SortableJS)实现更复杂的排序功能?[^1] 2. 如何结合Element Plus的上传组件实现图片上传后的拖拽排序?[^2] 3. 如何实现跨组件/跨区域的拖拽排序功能?[^1] 4. 如何优化大型图片列表拖拽时的性能?[^3] 5. 如何在拖拽排序后自动保存顺序到后端服务器? [^1]: Web前端之拖拽API、上传多图片时拖拽排序、表格行或列拖拽排序... [^2]: 利用Element UI与Ruoyi框架创建支持拖拽排序的图片上传组件... [^3]: onDragOver事件处理逻辑,拖动元素与目标元素的位置识别...
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员勇哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值