Vue3 细节点

261 阅读4分钟

获取当前组件

vue3提供了一个getCurrentInstance方法用来获取当前组件的实例

<template>
  <div class="parent">
    {{ msg }}
  </div>
</template>

<script lang="ts">
import { defineComponent, getCurrentInstance } from "vue";

export default defineComponent({
  setup() {
    const instance = getCurrentInstance();
    console.log(instance);
    let msg = "我是父组件";
    return { msg };
  },
});
</script>

<style scoped>
.parent {
  color: rgb(233, 35, 0);
  position: relative;
  font-size: 30px;
}
</style>

调用子组件的方法
setup的第二个参数上有一个expose 属性,这是vue3.2+才出现的内容,通过expose 可以将该组件内部的一些方法等对外进行暴露

<template>
  <div class="children">
    <h2>我是子组件</h2>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref } from "vue";

export default defineComponent({
  setup(props, { expose }) {
    let counter = ref(0);
    const setCounter = (count: number) => {
      counter.value = count;
    };

    expose({
      setCounter,
    });
    return { counter };
  },
});
</script>

然后父组件通过ref绑定子组件之后,就可以调用子组件暴露出来的setCounter函数了

<template>
  <div class="parent">
    <Children  ref="childrenRef"></Children>
  </div>
</template>

<script lang="ts">
import { defineComponent, onMounted, ref } from "vue";
import Children from "./Children.vue";

export default defineComponent({
  components: {
    Children,
  },
  setup() {
    const childrenRef = ref(null);
    onMounted(() => {
      childrenRef.value.setCounter(2);
    });
    return { childrenRef };
  },
});
</script>

再来看一个例子
假如有这么一个需求,需要你将Children以插槽的方式传进Parent组件,

<template>
  <div class="parent">
    <slot></slot>
  </div>
</template>

<script lang="ts">
import { defineComponent } from "vue";

export default defineComponent({
  setup() {
    return {};
  },
});
</script>

并且Parent组件内部要对插槽的内容进行校验,必须是Children组件。再创建一个test.vue

// test.vue
<template>
  <div>
    <Parent> 
      <Children></Children>
      <Children></Children>
      <Children></Children>
      <Children></Children>
      <Children></Children>
      <Children></Children>
      <Children></Children>
      <Children></Children>
    </Parent>
  </div>
</template>

<script lang="ts">
import { defineComponent } from "vue";
import Parent from "./components/Parent.vue";
import Children from "./components/Children.vue";

export default defineComponent({
  components: {
    Parent,
    Children,
  },
  setup() {
    return {};
  },
});
</script>

需求的意思就是Parent组件内部要进行校验,总不可能将Parent内部的直接子元素一一绑定ref吧?这样太过冗余,如果传入了上百个Children组件呢?更麻烦了。
所以还是要来说说setup的参数了,setup第二个参数context上有一个属性slots,slots上又有一个方法default,该方法的返回值就是一个插槽内容的数组

<template>
  <div class="parent">
    <slot></slot>
  </div>
</template>

<script lang="ts">
import { defineComponent } from "vue";

export default defineComponent({
  setup(props, context) {
    console.log(context);
    const defaults = context.slots.default()
    console.log(defaults);
    
    return {};
  },
});
</script>

接下来在Parent.vue中导入Children组件,并在test.vue的Parent组件中添加一个div

    <Parent> 
      <Children></Children>
      <Children></Children>
      <Children></Children>
      <Children></Children>
      <Children></Children>
      <Children></Children>
      <Children></Children>
      <Children></Children>
      <div>我是div</div>
    </Parent>

然后Parent组件内部进行判断

<template>
  <div class="parent">
    <slot></slot>
  </div>
</template>

<script lang="ts">
import { defineComponent } from "vue";
import Children from "./Children.vue";

export default defineComponent({
  setup(props, context) {
    const defaults = context.slots.default();
    defaults.forEach((item) => {
      console.log(item.type === Children);
    });
    return {};
  },
});
</script>
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)
    })

完整代码下图

8035850-2c52e2a8050c42b8.webp

更加规范的props和emits

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

<template>
    <child-component v-model:value="myValue"></child-component>
</template>

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
  },
}



参考文档:
www.jianshu.com/p/b89b94c41…
www.jianshu.com/p/a777d61ce…