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

ref 应用于对象类型的一个案例

一个案例:

<template><el-form :model="loginForm" :rules="rules"ref="ruleForm"  class="login-container" label-position="left"label-width="80px" v-loading="loading"  status-icon><el-text class="mx-1" size="large" >系统登录</el-text><div style="margin: 20px" /><el-form-item label="用户名" prop="userName"><el-input v-model="loginForm.userName" placeholder="用户名"></el-input></el-form-item><el-form-item label="密码" prop="password"><el-input  type="password" v-model="loginForm.password" placeholder="密码"></el-input></el-form-item><el-form-item><el-button type="primary" @click.native.prevent="submitClick">Login</el-button></el-form-item></el-form>	
</template><script setup>
import { ref } from "vue";
import { useStore } from 'vuex'
import { useRouter } from 'vue-router'
import { postReq } from '../utils/api'
import { ElMessageBox } from 'element-plus'
//导入用户仓库
import { useUserStore } from '../store/user.js'//路由
const router = useRouter()
//获取用户仓库对象
const userStore=useUserStore()
const loginForm=ref({userName: 'admin',password: 'admin123'
})const rules=ref({userName: [{ required: true, message: '请输入用户名', trigger: 'blur' },{ min: 3, max: 20, message: '长度在 3 到 20 个字符', trigger: 'blur' }],password: [{ required: true, message: '请输入密码', trigger: 'blur' },{ min: 6, max: 16, message: '长度在 6 到 16 个字符', trigger: 'blur' }]
})// 提交表单数据的函数
const submitClick=()=>{postReq("/user/login",loginForm.value).then(resp=>{if(resp.data.result){let u=resp.data.data;u.token=resp.data.token;u.isAuth=true;//console.log(u);userStore.setAuthenticated(u)//登录成功后跳转到指定页面router.push('/home')}else{ElMessageBox.alert(resp.data.errMsg, '提示',{})}})
}</script><style>.login-container {border-radius: 15px;background-clip: padding-box;margin: 180px auto;width: 350px;padding: 35px 35px 15px 35px;background: #fff;border: 1px solid #eaeaea;box-shadow: 0 0 25px #cac6c6;}.login_title {margin: 0px auto 40px auto;text-align: center;color: #505458;}.login_remember {margin: 0px 0px 35px 0px;text-align: left;}	
</style>

我们先看看,在 Vue3 中,ref() 和 reactive()的 的区别。当 ref() 应用于对象时,其内部实现和响应式机制与直接使用 reactive() 有本质区别,但最终效果有相似之处。以下是详细解析:

1. ref() 与 reactive() 的核心区别

  • ref():创建一个响应式引用对象,内部通过 Object.defineProperty 或 Proxy 拦截 .value 的读写操作。

    • 无论初始值是基本类型(如 ref(0))还是对象(如 ref({})),ref 始终是一个包含 .value 属性的对象。
    • 当值为对象时,Vue 会自动将该对象转为 reactive 代理,但包裹层仍是 ref
  • reactive():直接创建一个深层响应式的对象代理,无需 .value 访问。

    • 只能用于对象或数组,不能用于基本类型。

2. ref() 应用于对象时的内部机制

当你使用 ref() 包裹对象时:

javascript

const loginForm = ref({userName: 'admin',password: 'admin123'
});

  • 外层结构loginForm 是一个 ref 对象,具有 .value 属性。
  • 内层值loginForm.value 是一个通过 reactive() 创建的响应式代理对象。
  • 响应式原理
    1. 访问 loginForm.value.userName 时,先触发 ref 的 .value 的 getter,再触发内部 reactive 对象的 userName 的 getter
    2. 修改 loginForm.value.password = 'newPassword' 时,触发内部 reactive 对象的 setter,通知依赖更新。

关键区别ref 是一个 “容器”,而 reactive 是容器中的 “内容”。

3. 与直接使用 reactive() 的对比

javascript

// 方式1:使用 ref() 包裹对象
const loginFormRef = ref({userName: 'admin',password: 'admin123'
});// 方式2:直接使用 reactive()
const loginFormReactive = reactive({userName: 'admin',password: 'admin123'
});

  • 访问方式

    • loginFormRef.value.userName(需通过 .value)。
    • loginFormReactive.userName(直接访问)。
  • 解构后响应性

    • const { userName } = loginFormRef.valueuserName 是普通变量,无响应性。
    • const { userName } = loginFormReactiveuserName 失去响应性(需用 toRefs() 保持)。
  • 类型转换

    • ref 对象可通过 toRefs(loginFormRef.value) 转为响应式引用。
    • reactive 对象可通过 toRef(loginFormReactive, 'userName') 转为单个属性的响应式引用。

4. 应用场景选择

  • 使用 ref() 包裹对象

    • 当需要在组合式函数中返回对象,且保持统一的 .value 访问模式。
    • 当需要在模板中通过 v-model 绑定整个对象(如表单组件)。
  • 直接使用 reactive()

    • 当不需要 .value 语法,希望代码更简洁。
    • 当对象结构复杂,需要深层响应式,且无需解构。

5. 小结

  • ref() 包裹对象时:外层是 ref 容器,内层是 reactive 代理,本质是 ref + reactive 的组合
  • 直接使用 reactive() 时:对象本身就是响应式代理,无需额外容器。

我们在模版中 这样使用的:

你们可以看到 我们没有用前文提到的 loginFormRef.valueuserName   去访问啊,错了吗?

没错哟~~~

  1. 模板中v-model="loginForm.userName" 正确,因为 Vue 自动解包 loginForm 为 loginForm.value
  2. JavaScript 中postReq("/user/login", loginForm.value) 正确,因为必须显式访问 .value

如果使用 reactive,则代码需要改为:

// 使用 reactive
const loginForm = reactive({userName: 'admin',password: 'admin123'
});// 模板中仍然直接访问
<el-input v-model="loginForm.userName">// JavaScript 中直接使用
postReq("/user/login", loginForm); // 无需 .value

我们项目中主要应用的是ref,大部分项目也是这样的,原因是:

  1. 统一访问模式:所有状态都用 .value,降低复杂度。
  2. 类型安全:TypeScript 推导更直观,减少类型错误。
  3. 组合式适配:更符合 Vue3 的函数式设计理念。组合函数,后面有空再讲。

http://www.xdnf.cn/news/1005949.html

相关文章:

  • CKA考试知识点分享(11)---CRD
  • JavaScript DOM 操作与事件处理全解析
  • BeanUtil.copyProperties()进行属性拷贝时如何忽略NULL值——CopyOptions配置详解
  • 高效管理Python环境:Miniforge、pyenv和Poetry深度对比与应用
  • 建筑业应用:机器人如何改变未来建筑业发展方向
  • 介绍一下 TCP方式程序的通讯,服务器机与客户机
  • 使用 DeepSeek 为 TDengine 创建专属知识库
  • 部署安装maven和mvnd
  • 50天50个小项目 (Vue3 + Tailwindcss V4) ✨ | RandomChoicePicker(标签生成)
  • 西门子PLC读取梅安森风压传感器数据
  • 【2025】深度学习环境搭建记录
  • inet_addr()和inet_aton()函数详解
  • 【log4j2】将运行时变量注入日志、附性能对比与生产案例(动态日志实战)
  • JFLASH 提示license 配置操作 Sorry,no valid license for I-Flash found.
  • Trae重磅升级
  • Python 字典
  • 第六节 工程化与高级特性-TS配置选项解析
  • AUTOSAR图解==>AUTOSAR_TR_InteroperabilityOfAutosarTools
  • Rust 通用代码生成器:莲花,红莲尝鲜版三十六,哑数据模式图片初始化功能介绍
  • 测试完成的标准是什么?
  • Vue3项目与桌面端(C++)通过Websocket 对接接口方案实现
  • 【源码+文档+调试讲解】自习室系统
  • HALCON第二讲->预处理
  • vue中的doSave()方法
  • Excel大厂自动化报表实战(互联网金融-数据分析周报制作上)
  • 桥接模式(Bridge Pattern)
  • FastDFS
  • Flash数据写入及ECC纠错关键函数:Fapi_issueProgrammingCommand()
  • 【微软RDP协议】微软RDP协议技术架构特点与跨地域应用实践
  • Qt 环境搭建全流程详解:从下载到创建首个项目