前言:Vue3大事件项目是一个基于Vue3的PC端项目,本系列文章我将总结我在这个项目中学到的知识点,写项目笔记。如果你正好在学或巩固Vue3,希望本系列文章可以帮助到你。

【Vue3大事件 | 项目笔记】第五天
今日完结:
- 完善抽屉表单结构
- 完成文件上传功能
- 完成富文本编辑器
- 完成添加文章功能
- 完成文章编辑功能
今日收获:
1.优化图片上传
传统图片上传的缺陷:用户选择图片时,图片立即上传到后台,即便用户后续更换图片或放弃提交,已上传的图片仍会占用服务器内存;若用户频繁更换图片,会产生大量无效文件,造成服务器存储浪费。我们可以采用以下方法来优化,将文件上传时机从「用户选择图片时」推迟到「用户最终确认提交时」。
步骤:
1.选择图片 - 触发on-change回调(仅在浏览器本地处理:校验文件类型 / 大小 → 生成预览地址 → 存储 File 对象,不向后台发请求)
模板层(触发回调的入口)
javascript
<el-upload
class="avatar-uploader"
:auto-upload="false" <!-- 关键:关闭自动上传,避免向后台发请求 -->
:show-file-list="false"
:on-change="onUploadFile" <!-- 选择文件触发onUploadFile回调 -->
>
<img v-if="imgUrl" :src="imgUrl" class="avatar" />
<el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
</el-upload>
脚本层(本地处理逻辑)
javascript
const onUploadFile = (uploadFile) => {
imgUrl.value = URL.createObjectURL(uploadFile.raw) // 生成本地预览地址(仅浏览器内存)
// 存储File对象到表单,仅等待最终提交,不向后台发请求
formModel.value.cover_img = uploadFile.raw
}
2.更换 / 放弃 - 覆盖本地 File 对象(仅更新本地预览和 File 对象,无后台交互,避免无效上传)
核心逻辑:重复触发步骤 1 的onUploadFile,仅覆盖本地数据,无后台交互
javascript
// 无需新增代码,复用步骤1的onUploadFile即可
const onUploadFile = (uploadFile) => {
imgUrl.value = URL.createObjectURL(uploadFile.raw) // 覆盖预览地址
formModel.value.cover_img = uploadFile.raw // 覆盖本地File对象(无后台请求)
}
3.确认提交 - 执行onPublic(仅此时将存储的 File 对象通过 FormData 提交到后台,确保仅上传用户最终确认的图片)
模板层(触发提交的按钮)
javascript
<el-button @click="onPublic('已发布')" type="primary">发布</el-button>
<el-button @click="onPublic('草稿')" type="info">草稿</el-button>
脚本层(提交 File 对象到后台)
javascript
const onPublic = async (state) => {
formModel.value.state = state
const fd = new FormData() // 创建FormData(文件上传必须)
// 遍历表单数据,包含cover_img(File对象)
for(let key in formModel.value){
fd.append(key,formModel.value[key])
}
// 仅此时调用后台接口,上传File对象
if(formModel.value.id){
await artEditService(fd) // 编辑:提交包含File对象的FormData
ElMessage.success('修改成功')
}else{
await artPulishService(fd) // 新增:提交包含File对象的FormData
ElMessage.success('添加成功')
}
visibleDrawer.value = false
emit('success',formModel.value.id ? 'edit' : 'add')
}
关键技术点
1.auto-upload="false":关闭 Element Upload 的自动上传,是核心配置;
2.URL.createObjectURL(uploadFile.raw):生成本地预览地址,仅在浏览器内存中生效,不占用服务器资源;
3.FormData.append('cover_img', coverFile.value):文件上传必须通过 FormData 格式提交,保证后台能正确接收文件。
2.网络图片地址转 File 对象
编辑文章时,后台返回的封面是网络图片地址(字符串类型);而新增文章时,前端提交的是用户本地选择的 File 对象。由于后台仅接收 File 对象格式的文件,为保证新增 / 编辑场景下封面提交格式统一,需将网络图片地址转换为标准的 File 对象,以下函数是实现该转换的核心逻辑:
javascript
// 将网络图片地址转换为File对象
async function imageUrlToFile(url, fileName) {
try {
// 第一步:使用axios获取网络图片数据
const response = await axios.get(url, { responseType: 'arraybuffer' });
const imageData = response.data;
// 第二步:将图片数据转换为Blob对象
const blob = new Blob([imageData], { type: response.headers['content-type'] });
// 第三步:创建一个新的File对象
const file = new File([blob], fileName, { type: blob.type });
return file;
} catch (error) {
console.error('将图片转换为File对象时发生错误:', error);
throw error;
}
}
javascript
const file = await imageUrlToFile(imgUrl.value,formModel.value.cover_img)
formModel.value.cover_img = file
3.富文本编辑器
官网:富文本编辑器
富文本编辑器(如代码中的 Quill Editor)是支持可视化编辑、格式排版的文本编辑组件,能让用户像使用 Word 一样编辑内容(设置字体、颜色、插入图片 / 链接等),最终输出 HTML 格式的文本内容,解决了普通输入框仅能输入纯文本的局限。
使用步骤:
1.安装依赖
javascript
npm install @vueup/vue-quill@latest --save
2.引入组件 / 样式 → 模板中使用 → 绑定内容 / 配置参数 → 操作编辑器实例
javascript
<script setup>
// 引入富文本编辑器组件和样式
import { QuillEditor } from '@vueup/vue-quill'
import '@vueup/vue-quill/dist/vue-quill.snow.css'
...
</script>
<template>
<!-- 富文本编辑器核心使用代码 -->
<el-form-item label="文章内容" prop="content">
<div class="editor">
<quill-editor
ref="editorRef" <!-- 绑定编辑器引用,用于操作实例 -->
theme="snow" <!-- 编辑器主题(snow为带工具栏的默认主题) -->
v-model:content="formModel.content" <!-- 双向绑定编辑内容(HTML格式) -->
contentType="html" <!-- 指定绑定的内容类型为HTML -->
>
</quill-editor>
</div>
</el-form-item>
</template>
杂项知识点:
什么是FormData
FormData 是浏览器专门处理 "文件 + 普通数据" 提交的格式。
普通的 JSON 或 application/x-www-form-urlencoded 格式只能传输文本,无法处理文件的二进制数据;
FormData 是浏览器为 "文件上传" 场景量身设计的对象,能原生处理二进制数据,且兼容所有后端语言(Java/Python/Node.js 等)。
只要前端需要向后台提交文件(图片 / 视频 / 文档等二进制数据),无论是否伴随普通文本数据,都必须使用 FormData;
总结:
今天学到了关于图片上传,减少内存的方法,还用到了新的工具 - 富文本编辑器,感觉今天学的东西更偏业务了,收获很大。
如果我的内容对你有帮助,请点赞,评论,收藏。创作不易,大家的支持就是我坚持下去的动力!
