Vue 3 + Element Plus 常见开发问题与解决方案手册

发布于:2025-07-05 ⋅ 阅读:(15) ⋅ 点赞:(0)

🌟Vue 3 + Element Plus 常见开发问题与解决方案手册

🧠 本文整理了常见但容易混淆的几个 Vue 3 前端开发问题,包括插槽、原型链、响应式数据处理、v-model 报错、样式阴影控制等,建议收藏学习!


📌一、动态插槽 fallback 原理详解

✅ 场景

在组件中使用如下代码:

<slot :name="item.prop">
  <component
    :is="getComponentType(item.type)"
    v-model="formModel[item.prop]"
    v-bind="getComponentProps(item)"
    clearable
    style="width: 100%"
  />
</slot>

✅ 疑问

为什么加了 <slot :name="xxx">默认内容</slot>,父组件传了插槽就会生效,没传就自动使用默认内容?

✅ 解答

这是 Vue 插槽的 fallback(回退)机制:

  • 父组件有传 <template #xxx> 插槽,就渲染插槽内容;
  • 没传,就渲染 <slot> 标签中的默认内容。

✅ 示例

子组件

<slot name="gender">
  <el-input v-model="formModel.gender" />
</slot>

父组件 A(没传插槽)

<ChildComponent />

➡️ 渲染默认 <el-input />

父组件 B(传了插槽)

<ChildComponent>
  <template #gender>
    <el-radio-group v-model="formModel.gender">
      <el-radio label="male">男</el-radio>
      <el-radio label="female">女</el-radio>
    </el-radio-group>
  </template>
</ChildComponent>

➡️ 渲染插槽内容


📌二、h() 函数参数说明

h(type, props?, children?)
参数 含义
type 标签名或组件(字符串、对象)
props class、style、事件、props
children 字符串、VNode数组、插槽函数等

✅ 示例

h('div', { class: 'box' }, 'Hello') // <div class="box">Hello</div>
h(MyComponent, { propA: 1 }, { default: () => h('span', '插槽内容') })

📌三、为什么 unref() 不能去掉 Proxy?

function handleSearch() {
  emit('search', unref(props.queryParams))
}

❓ 疑问

打印结果为什么还是 Proxy?

✅ 解答

  • unref() 只能解包 ref() 类型
  • reactive() 返回的是 Proxy,不会被 unref() 解包
  • 所以 unref(reactiveObj) 仍然是 Proxy

✅ 正确做法

方式一:toRaw()(浅解包)
import { toRaw } from 'vue'
emit('search', toRaw(props.queryParams))
方式二:cloneDeep()(推荐,深拷贝)
import cloneDeep from 'lodash-es/cloneDeep'
emit('search', cloneDeep(props.queryParams))

📌四、toRaw vs unref 的区别

方法 用途 解包对象 是否保留响应式
unref() 取出 ref 的 .value 只能 ref 是(ref.value 仍可能是 reactive)
toRaw() 获取原始对象(去 Proxy) 只能 reactive

📌五、原型链到底有几层?

✅ 答案:没有固定层数,直到原型为 null 为止。

✅ 示例

const obj = {}
Object.getPrototypeOf(obj)          // → Object.prototype
Object.getPrototypeOf(Object.prototype) // → null ✅
类型 原型链结构
对象 {} obj → Object.prototype → null
数组 [] → Array.prototype → Object.prototype → null
函数 function () {} → Function.prototype → Object.prototype → null

📌六、v-model 报错:v-model cannot be used on a prop

❓ 报错场景

在组件中这样写:

<el-input v-model="modelValue" />

modelValuedefineProps() 得到的 prop,Vue 提示该属性是只读的!

✅ 正确做法(支持 v-model

<el-input
  :model-value="modelValue"
  @input="val => emit('update:modelValue', val)"
/>

✅ setup 完整写法

const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])

📌七、如何让 box-shadow 去掉右边?

原始样式(四边阴影):

box-shadow: 0 0 0 1px var(--el-input-border-color) inset;

✅ 改成只保留左上右:

box-shadow:
  inset 1px 0 0 var(--el-input-border-color),   /* 左 */
  inset 0 1px 0 var(--el-input-border-color),   /* 上 */
  inset 0 -1px 0 var(--el-input-border-color);  /* 下 */

👉 不写右边的就相当于去掉右侧边框效果。


📌附录:使用 Element Plus 时注意

  • 所有输入类组件(如 el-input、el-select)都使用 modelValue 作为 v-model 的绑定值;
  • 插槽使用 fallback 内容时,记得 slot name 要和父组件一致;
  • 使用样式定制时,Element Plus 常用的 CSS 变量有:
    --el-input-border-color--el-border-color--el-color-primary 等。

✅ 推荐工具函数(Bonus)

你可以封装一个自动脱 Proxy 工具:

import { toRaw, isRef, unref } from 'vue'
import cloneDeep from 'lodash-es/cloneDeep'

export function cleanReactive(val: any) {
  if (isRef(val)) return unref(val)
  if (val && val.__v_isReactive) return cloneDeep(toRaw(val))
  return val
}

网站公告

今日签到

点亮在社区的每一天
去签到