Vue3 踩坑记,持续更新

  • props的.sync在vue3中被移除
  • vue2中写法:
    组件内部:

定义 props:{name: String}
更新:$emit('update:name', 'xxxxx')
调用组件时::name.sync="name"

  • vue3中:
    组件内部:

定义:props:{name: String},emits:['update:name'],
更新:$emit('update:name', 'xxxxx')
调用组件时:v-model:name="name"
参考分页控件

  • 在setup() 里调用this.$xxxx 全局方法
// getCurrentInstance 获取当前实例
import { getCurrentInstance } from ‘vue’
const { ctx } = getCurrentInstance()
ctx.$xxxx
  • 在reactive里包含 ref的值时 不需要.value 否则会丢失响应

错误写法:

const name= ref('')
const body = reactive({
params: name.value // 会丢失响应
})

正确写法:

const name= ref('')
const body = reactive({
params: name
})
  • 使用provide 跟inject依赖注入时,provide 的是响应式对象,那inject的时候也是响应式对象。但是在reactive里包含 ref的值时 不需要.value 否则会丢失响应
  • reactive 不可以全量赋值,否则会丢失响应,如下:
let body = reactive({
    params: 'text'
})
错误: body = {} 
正确:body.params = 'text2'

这里我们可以封装一个函数用来快速赋值 reactive,并且页面会响应。代码如下:

/**
 * 将reactiveObj对象转换成targetObj对象的格式
 * 为解决 reactiveObj直接赋值targetObj还能在页面上进行响应
 * @param {reactive} reactiveObj  [setup中需要赋值的reactive对象]
 * @param {Object} targetObj [目标对象]
 */
export const setReactiveObj = (reactiveObj, targetObj) => {
  if (Array.isArray(targetObj)) {
    if (reactiveObj && reactiveObj.length) {
      reactiveObj.length = 0
    }
  }
  for (const key in targetObj) {
    if (Object.hasOwnProperty.call(targetObj, key)) {
      const val = targetObj[key]
      reactiveObj[key] = val
    }
  }
}

// 用法也很简单,例子如下
// 打开配置页
    function openDrawer(row) {
      drawer.value = true
      setReactiveObj(rowData, row) // rowData 是 reactive响应式代理对象,row是普通的对象
      getInfo(rowData.id)
    }
  • element-plus 中的tabs 组件,会默认加载所有tab的里面的内容,并不是只在切换的时候才加载。他的原理是显示隐藏,类似v-show
  • 项目编译的时候不报错,但是一直卡住在某个步骤比如一直卡在 98%。造成的原因很大一部分是因为某个组件中引入其他路径有问题。不存在或者错误写法。例如这么写: import {} from ''
  • 表单如果只有一个input的时候,回车会触发表单的提交导致页面刷新。这个问题原生上就有,注意加上@submit.prevent,阻止默认行为
  • 解决打包生产环境的时候报错 INTLIFY_PROD_DEVTOOLS is not defined

只需要在vue.config.js里面配置下面这句即可

    // 解决打包生产环境的时候报错 __INTLIFY_PROD_DEVTOOLS__ is not defined
    new webpack.DefinePlugin({
          __INTLIFY_PROD_DEVTOOLS__: JSON.stringify(false)
    })

完整代码下图


image.png
  • 更加规范的props和emits

在vue3中,父子组件传值的props和emits写法更加规范(命名更统一),体现在:v-model的变化


v-model:value等同于props:'value'和emits('update:value')

需要指定emits,为了便于管理,建议在emits中定义所有用到的emit

export default {
   emits: ['update:value', 'otherEmit'],
   setup(props, { emit }) {
     emit('update:value')
     emit('otherEmit')
   },
 }
  • $listeners被移除

事件监听器也被包含还在了$attrs中,现在属性透传更方便了

  • $children 被移除

如果想访问子组件,使用$refs

  • 事件API被移除

$on,$off,$once不再使用。2.x的EventBus方法不能再使用。

  • Filter被移除

不能再用|使用filter。Sad。

  • 当使用 watch 选项侦听数组时,只有在数组被替换时才会触发回调。换句话说,在数组被改变时侦听回调将不再被触发。要想在数组被改变时触发侦听回调,必须指定 deep 选项
watch: {
  bookList: {
    handler(val, oldVal) {
      console.log('book list changed')
    },
    deep: true
  },
}
  • vue 打包后出现"Vue DevTools failed to load SourceMap: Could not load content : net::ERR_FILE_NOT_FOUND"错误

发生错误的原因是vue.config中的publicPath配置成了 '/'
1.解决办法 修改vue.config中的publicPath:"./"

module.exports = {
  publicPath: './', //部署项目的时候的基本url,替代原来的Baseurl,默认是'/'
}

五、element-plus 中的 tabs 组件,会默认加载所有 tab 的里面的内容

并不是只在切换的时候才加载。他的原理是显示隐藏,类似 v-show,如果需要避免这个情况则可以使用 动态组件方式: 参考
src\modules\physical-model\components\model-config-drawer\index.vue 组件

六、 项目编译的时候不报错,但是一直卡住在某个步骤比如一直卡在 98%。造成的原因很大一部分是因为某个组件中引入其他路径有问题。不存在或者错误写法。例如这么写: import {} from ''

七、 表单如果只有一个 input 的时候,回车会触发表单的提交导致页面刷新。这个问题原生上就有,注意加上@submit.prevent,阻止默认行为。 参考组件:src\modules\physical-model\components\add-attr-dialog\index.vue

八、 如果声明了 async 但是里面又没有用到 await 的话,可能会导致打包编译的时候报错。例如下面的写法:

onBeforeMount(async () => {
      isLoginControl()
        .then((r) => {
          show.value = true
        })
        .catch((err) => {
          console.log(err)
        })
    })

声明了 async 但是函数里面并没有结合 await 使用。运行的时候不报错,只有在打包生产的时候才报错,因为打包生产的时候 vue cli 内部会做一些优化处理。
报错如下图


async声明但没使用await导致的报错.png

九、遇到网页白屏或者不报错就是进不去的时候,需要把压缩去掉跟打印打开,然后重新发一下版,这样可以快速定位到报错信息。

十、 使用 setup 语法时,模板中用到了的响应式变量一定要声明,并且确保 return 出去了。虽然不会直接报错,但是会报不存在警告,容易引起一些列 bug。列如为什么组件值 undefined 啊,拿不到方法等。

十一、组件里面如果有 emit 了方法,则组件里面一定要在头部显示声明 emits。不然会报警告

export default defineComponent({
  name:'add-devices-dialog',
  components:{
    UploadConfigTable,
    SelectTemplateDialog
  },
  emits: ['handleClick', 'handleDeviceTotal'], // 显示声明
  setup(props, context) {
    let active = ref(0)
    let showDialog = ref(false)
    const addForm = ref(null)
    let selectTemplateRefs = ref(null)
    let uploadConfigTableDevice = ref(null)
  }

十二、 组件中声明 prop跟event处理器参数时,需要区分一下。请注意看下图说明,如十一点中声明 emits,在模板中是使用小写横线分割。在 js 中使用驼峰命名。所以在项目中我们可以看到 js 中组件名字是驼峰命名,在模板中是使用小写横线分割,所以注意下

大小写不敏感.png

十三、关于项目中 vue3 使用 watch 的时候捕获不到旧值得问题,请看我下面链接中写的文档

传送门:computed 与 watch 的区别

传送门:vue3 中 watch、watchEffect 区别

十四、 关于项目中大家经常犯的错,就是一个对象从外面传进来,然后重新赋值,然后操作新赋值的对象,会导致外面的那个对象也跟着改变。这是由于引用传值导致的。属于值传递中的基础。因为引用传值只会赋值地址。并不会赋值里面的对象、只是赋值这个对象中的地址。所以改了某个属性。那么两个对象都会同步变化。关于这方面的知识点。建议百度一下。好好弄清楚。避免经常犯错,项目中都是通过 import _cloneDeep from 'lodash/cloneDeep'深克隆来解决。项目中全局搜索_cloneDeep 即可

十五、 使用 时,一定要给加上 el-radio-group 分组,一定要避免 每个里面添加 v-model 双向绑定。增加父节点 el-radio-group 在父节点上绑定即可。类似 js 中事件委托。这样不需要关注有多少个 el-radio。

错误写法:

读写
只读
只写

正确写法


    读写
    只读
    只写

十六、切换路由的时候报错 parentComponent.ctx.deactivate is not a function

1、查看是否使用了动态组件 这种方式

2、 是否使用了 包裹

解决方式,一般只需要在 绑定 key 即可。

如下面代码

 
    
  

如果上面没有:key 则可能会偶尔报错。

十七、 打包之后上了生产环境的时候报错 __INTLIFY_PROD_DEVTOOLS__ is not defined

解决方式需要在 webpack 配置中增加如下配置代码

    // 解决打包生产环境的时候报错 __INTLIFY_PROD_DEVTOOLS__ is not defined new webpack.DefinePlugin({
        __INTLIFY_PROD_DEVTOOLS__: JSON.stringify(false)
    }),

十八、 ref和reactive创建的响应式变量, 不能进行解构或展开, 否则变量将失去响应效果.如果要结构响应式变量, 需要通过toRefs转一下

 import { toRefs } from 'vue'
    export default {
      props: {
        currentId: {
          type: String,
          default: ''
        }
      },
      setup(props) {
      const { currentId } = toRefs(props)
        console.log(currentId.value)
      }
    }

十九、 在 setup 中获取组件的 ref 对象

  • 在组件上声明其 ref
  • 在 setup 函数中创建 与组件 ref 同名 ref 对象 并导出
   

    

二十、如果给reactive传递了其他对象(如时间对象),默认情况下修改对象界面不会自动更新,如果想更新,可以通过给对象重新赋值来解决

// 如下通过setDate方法直接更改变量值不会生效,页面不会更新

eleData.params.startTime.setDate(eleData.params.startTime + 1)

// 需要重新赋值,页面上才会更新
eleData.params.startTime = new Date(newStartTime)
eleData.params.endTime = new Date(newEndTime)

二十一、路由里面配置的的name一定要跟对应组件里面的name保持一直,不然keep-alive将找不到对应的组件来缓存!!!!!!!!!!!!!!!!

二十二、组件里面的name请使用驼峰命名不要使用 横线链接。并且组件名称要为多个单词组成

二十三、注意,虽然vue3中 template模板已经支持多个根节点。但是我们还是尽量不要这么用,最好还是保留一个根节点,把其他子组件包裹起来,不然会引起一些莫名的bug,或者影响整个布局。

二十四、

在setup中函数尽量使用function声明,因为使用let或者const声明的话,虽然变量会被提升,但是如果在初始化之前调用变量就会形成一个暂时性死区。导致报错。如果原来是正常运行着的。但是依赖升级或者重新安装之后出现了这个错误。那肯定就是这个原因造成的。所以大家在写代码的时候一定要注意一下。变量声明一定要放在最上方。函数尽量使用function。不要哪里用到了就在哪里去声明。写过后端的童鞋们都会有个习惯就是变量的声明都是放在最前面。
ps容易出现错误
如下:

ReferenceError: Cannot access ‘xxx‘ before initialization
拓展知识点:

  • var的创建和初始化被提升,赋值不会被提升。
  • let的创建被提升,初始化和赋值不会被提升。
  • function的创建、初始化和赋值均会被提升。

二十五、如果出现线上页面奔溃,但是在本地运行是正常的。有可能是因为本地的依赖跟线上的依赖不一样。因为线上每次发版都是获取最新的,因为我们项目中依赖配置那边都是使用了通配符^,这个是获取最新小版本的意思。、本地我们开发,很少会去重新更新依赖或者重新安装依赖。所以就会出现这种排查困难的状态。解决方案。就是把本地依赖node_modules删掉。重新安装依赖,这样就能跟线上保持一样。这样在本地去调试的时候可能就能发现问题了

你可能感兴趣的:(Vue3 踩坑记,持续更新)