vue3常见问题

1、自定义指令不能放到已经封装过的组件上

错误:

  • 控制台警告 Runtime directive used on component with non-element root node. The directives will not function as intended.

image-20240516103229568.png

image-20240516102220690

原因:

  • 意思是自定义指令不能放到组件上,而是要放到自有的元素上,也就是这里用到的v-show不能放在自定义组件上,而是放在原来就有的标签上,所以这里套了一层div

    • 比如:

    • <el-color-picker v-show="isShow"></el-color-picker> // 错误
      
  • v-loading是一个由Element-plus插件里创造的自定义的指令,它不能直接用el-dialog这个种封装过的组件

    • 比如:

    • <el-dialog v-loading="loading"></el-dialog> // 报错
      

解决:

  • 外面套一层不是自定义组件的东东就可以,我这里套了一层div,你也可以嵌套一层template(v-if的情况)

    • 比如:

    • <div v-show="isShow">
          <el-color-picker></el-color-picker>
      </div>
      
      或者(新版本好像会报警告'v-show' directives cannot be put on <template> tags)
      
      <template v-if="isShow">
          <el-color-picker></el-color-picker>
      </template>
      
      
  • 将v-loading使用在内部

    • 比如:

    • <el-dialog>
          <div  v-loading="loading"></div>
      </el-dialog> // 报错
      

2、setup语法糖写法,watch和defineExpose中函数必须声明在前边

错误:

  • setPosition3d.vue:210 Uncaught (in promise) ReferenceError: Cannot access ‘XXX’ before initialization

image-20240516105205339

原因:

  • setup语法糖加载顺序类似于js,先声明后引用

    • 比如:

    • defineExpose({
        initData   // ref.value.initData()使用的时候报错
      });
      const initData = () => {...}
      
      const add = ref(true)
      watch(add, () => {
      	add && addData() // 报错
      })
      const addData = () => {...}
      

解决:

  • 声明提前

    • 比如:

    • defineExpose({
        initData
      });
      const initData = () => {...}
      
      const add = ref(true)
      const addData = () => {...}
      watch(add, () => {
      	add && addData()
      })
      

3、ref、toRef、toRefs、toRaw 方法应用

3.1、toRef()

  • toRef() 函数基于响应式对象上的一个属性,创建一个对应的 ref。这样创建的 ref 与其源属性保持同步:改变源属性的值将更新 ref 的值,反之亦然。

  • toRef() 函数一次仅能设置一个数据,接收两个参数,第一个参数是哪个对象,第二个参数是对象的哪个属性。

<template>
  <div class="demo">
    <div>姓名--state.name:{{state.name}}</div>
    <div>姓名2--nameRef:{{nameRef}}</div>
    <div>年龄:{{state.age}}</div>
  </div>
</template>

<script>
import { reactive, toRef } from 'vue'
export default {
  name: 'reactivedemo',
  setup () {
    // 响应式对象
    const state = reactive({
      name: '太凉',
      age: 18
    })

    // 通过toRef创建一个Ref响应式
    const nameRef = toRef(state, 'name')

    // 过3秒后改变 两者 保持引用关系 
    setTimeout(() => {
      // update1:改变name属性
      state.name = '冰箱太凉'
    }, 3000)

    // 过6秒后改变 两者 保持引用关系 
    setTimeout(() => {
      // update1:改变name属性
      nameRef.value = '我就是冰箱太凉'
    }, 6000)

    return {
      nameRef,
      state
    }
  }
}
</script>

3.2、toRefs()

  • toRefs() 函数将一个响应式对象转换为一个普通对象,这个普通对象的每个属性都是指向源对象相应属性的 ref。
<template>
  <div class="demo">
    <h3>state 方式 不推荐的方式绑定</h3>
    <div>姓名--state.name:{{state.name}}</div>
    <div>年龄--state.age:{{state.age}}</div>
  </div>
  <div class="demo">
    <h3>toRefs之后的 方式 推荐这种方式,return需要{...toRefs(state)}</h3>
    <div>姓名--name:{{name}}</div>
    <div>年龄--age:{{age}}</div>
  </div>
</template>
<script>
import { reactive, toRefs } from 'vue'
export default {
  name: 'reactivedemo',
  setup () {
    // 响应式对象
    const state = reactive({
      name: '太凉',
      age: 18
    })

    // 通过toRefs创建一个响应式对象属性的Ref
    const toRefsValue = toRefs(state)

    // 过3秒后改变  两者保持引用关系
    setTimeout(() => {
      state.name = '冰箱太凉'
      state.age = '30'
    }, 3000)

    // 过6秒后改变 两者保持引用关系
    setTimeout(() => {
      toRefsValue.name.value = '我就是宇宙小超人'
      toRefsValue.age.value = '101'
    }, 6000)

    return {
      // 不建议使用这种方式,可以用下面的方式直接替换
      state,
      // 最佳方式:这里是结构 将 name的ref,age的ref结构到对象根下面
      ...toRefsValue
    }
  }
}
</script>
  • 我们使用 reactive创建的对象,如果想在模板中使用,就必须得使用 xxx.xxx的形式;
    • 如果大量用到的话还是很麻烦的,但是使用 es6解构以后,会失去响应式。
    • 那么toRefs的作用就体现在这,利用toRefs可以将一个响应式 reactive 对象的所有原始属性转换为响应式的ref属性。

注意:reactive封装的响应式对象,不要通过解构的方式return,这是不具有响应式的。可以通过 toRefs 处理,然后再解构返回,这样才具有响应式

const data = reactive({
	a: 12;
	b: {
		c: 12;
	}
})
const { a, b } = ...toRefs(data)

/* 使用 */
{{ a }}
{{ b }}

3.3、toRaw()

toRaw() 函数可以返回由 reactive()readonly()shallowReactive() 或者 shallowReadonly() 创建的代理对应的原始对象(将响应式对象转化为普通对象)。

这是一个可以用于临时读取而不引起代理访问/跟踪开销,或是写入而不触发更改的特殊方法。不建议保存对原始对象的持久引用,请谨慎使用。

const foo = {}
const reactiveFoo = reactive(foo)

console.log(toRaw(reactiveFoo) === foo) // true

3.4、ref、toRef、toRefs 异同点

  • ref、toRef、toRefs 函数都可以将某个对象中的属性变成响应式数据

  • ref 函数的本质是拷贝,修改响应式数据,不会影响到原始数据(引用数据类型会有影响),视图会更新

  • toRef、toRefs 函数和传入的数据形成引用关系,修改 toRef 会影响这些数据,但是不会更新视图

    • 作用:把一个响应式对象转换成普通对象,该普通对象的每个属性都是一个 ref
      • toRef 函数一次仅能设置一个数据,接收两个参数,第一个参数是哪个对象,第二个参数是对象的哪个属性
      • toRefs 函数接收一个对象作为参数,它会遍历对象身上的所有属性,然后挨个调用 toRef 执行。用于批量设置多个数据为响应式
  • ref 数据会引起监听行为,而 toRef 不会

3.5、ref

  • 如果将一个对象赋值给 ref,那么这个对象将通过 reactive() 转为具有深层次响应式的对象。这也意味着如果对象中包含了嵌套的 ref,它们将被深层地解包。

  • 若要避免这种深层次的转换,请使用 shallowRef() 来替代。

  • 相较于reactive包裹对象完成响应式,ref更具优势,因为当改变reactive中保存的对象的内存地址时(也就是换一个全新的对象),由于vue3的响应式是通过创建代理对象,在代理对象的set方法中放入该实例的渲染函数的方法构建的,而替换整个对象,而不是修改对象内部的属性值,其并不会调用set方法,所以页面并不会响应式,但是如果使用ref包裹对象,那么此对象仅仅是ref对象的一个value属性,所以更改内存地址时,也仅仅是修改ref对象的value属性,依然会调用代理对象的set方法,完成响应式dom更新

在这里插入图片描述
image-20240516120604128

另一个需要注意的点是,如果 ref 是文本插值的最终计算值 (即 {{ }} 标签),那么它将被解包,因此以下内容将渲染为 1

{{ object.id }}

该特性仅仅是文本插值的一个便利特性,等价于 {{ object.id.value }}

4、使用reactive定义响应式数据进行列表赋值时,视图没有更新

问题:

  • Vue 3.0 中我们使用 reactive() 定义的响应式数据的时候,当是一个数组或对象时,我们直接进行赋值,发现数据已经修改成功,但是页⾯并没有自动渲染成最新的数据

image-20240516120124499

原因:

  • 原因就是reactive函数会返回一个Proxy包装的对象,所以当我们这样直接赋值时:(看下面例子)

  • <script setup>
    import { reactive } from "vue";
    
    let userInfo = reactive([{name:'Eula'}]) 
    console.log(userInfo) // Proxy(Array) 打印出来是一个Proxy对象 当然具备响应式
    
    // 直接后端数据进行赋值
    userInfo = [{name:'优菈'}]
    console.log(userInfo)  // [{name:'优菈'}] 可以看出 就是打印出了一个普通的数组 所以不具备响应式
    </script>
    

    这样赋值的话,就会把Proxy对象给覆盖掉,从而无法触发对应的set和get,最终就会导致丢失掉响应性了;

    上面的代码 reactive([{name:‘Eula’}]) 创建了一个响应式数组,返回一个Proxy包装的对象由userInfo变量进行存放,但是后面我又把一个普通的数组(也就是后端返回的数据)赋值给userInfo,注意这时userInfo这个变量存放的已经是一个普通的数组了,当然也就不具备响应式了;

    所以:对于reactive创建的响应式数据应该避免直接使用=号进行赋值;会覆盖响应式;

解决:

  1. 再封装一层数据,即定义属性名,在后期赋值的时候,对此属性进行直接赋值
<script setup>
import { reactive, ref } from "vue";
// 定义响应式 
let list1 = reactive({myRenderList:[]});

// 请求的数据
let newList1 = [
  { name: "Eula", age: "18", isActive: false },
  { name: "Umbra", age: "17", isActive: false },
]

// 更改数据
const setList1 = () => {
  list1.myRenderList = newList1
}
</script>
  1. 使用数组的splice来直接更改原数组
  • 还是用reactive来定义响应式数据,只不过改数据的方式变了,使用数组的原生方法splice()来更改原数组,不是直接覆盖所以并不会影响响应式;

  • splice有三个参数时,可以对数组进行修改, 第一项是起始索引, 第二项是长度, 第三项是新插入的元素,可以有多个;

  • 下面的代码是把之前数组的每一项删除掉,然后插入新的数据newList1,使用…扩展符进行展开每一项;

    • list1.splice(0,list1.length,...newList1)
      
  • 当然,push()方法也是可以触发响应式的,只不过只能在后面新增数据。还有pop,shift,unshift等方法(用的不多)

  • 对象格式可以使用.的方式

    • <script setup>
          const ruleForm = reactive({})
          for (const key in data) {
            // if (Object.hasOwnProperty.call(ruleForm, key)) {
            ruleForm[key] = data[key];
            // }
          }
      </script>
      
  1. 使用 ref 来定义数据
  • 复杂数据类型也可以使用ref进行定义,而且数据都是响应式的;原理就有点像第一种方式,重新包装了一层value;每次使用的时候都要写.value;

5、elementplus的elpagination组件报错

错误:

  • 控制台警告:[el-input] [API] label is about to be deprecated in version 2.8.0, please use aria-label instead.

image-20240516140855441

原因:

  • el-pagination组件内部的jumper在使用el-input时,使用了label属性,而不是最新的aria-label,从而导致开发环境会报警告:[el-input] [API] label is about to be deprecated in version 2.8.0, please use aria-label instead.

    由于这里是生产环境,所以没有警告输出

解决:

  • 等待elementplus版本更新
  • 20
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Pluto_ssy

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

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

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

打赏作者

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

抵扣说明:

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

余额充值