Skip to content

二.Vue3.x(基础)

前言

补充一些官方文档中没有的,但是实际开发中最好需要了解的内容

1.Teleport

把节点挂载到指定 id 下

vue
<template>
  <teleport to="#app">
      <div class="modal">
      hello world
      </div>
  </teleport>
</template>

2.Suspense

展示加载状态

vue
<Suspense>
  <template >
      <!-- 异步组件 -->
    <Suspended-component />
  </template>
  <template #fallback>
      <!-- 异步组件加载完毕前展示的内容 -->
    Loading...
  </template>
</Suspense>

3.reactive

  • 对象或数组(原理是Proxy)

4.ref

  • 基本数据类型(原理是Object.defienProperty)

  • 对象或数组(原理是Proxy)

  • ref相关

  • ref

    • 接受一个内部值并返回一个响应式且可变的 ref 对象。ref 对象仅有一个 .value property,指向该内部值。
    • 如果将对象分配为 ref 值,则它将被 reactive 函数处理为深层的响应式对象。
    • template 内使用 ref 对象,会自动解包
  • unref

    • 如果参数是一个 ref,则返回内部值,否则返回参数本身。这是 val = isRef(val) ? val.value : val 的语法糖函数。
    • 基于 ref 一个 {} , {} 会被 reactive 二次处理,unref(ref({})) 返回的是响应式的 {}
  • toRef

    • 可以用来为源响应式对象上的某个 property 新创建一个 ref。然后,ref 可以被传递,它会保持对其源 property 的响应式连接。
    • 用人话讲就是为 源响应式对象(toRef的第一个参数) 上的某个 property 新创建一个 ref
  • toRefs

    • 将响应式对象转换为普通对象,其中结果对象的每个 property 都是指向原始对象相应 property 的 ref。
    • 主要功能:当从组合式函数 (.js) 返回响应式对象时,用toRefs就可以在不丢失响应性的情况下对返回的对象进行解构/展开。
    • toRef 是转单个, toRefs全转。
    • 原理:toRefs 会将 reactive 生成的对象的根级属性全都用 ref 转成 ref 对象,然后解构出来的都是 ref 对象,从而不丢失响应式
  • isRef

    • 检查值是否为一个 ref 对象
  • customRef

    • 创建一个自定义的 ref,并对其依赖项跟踪和更新触发进行显式控制。它需要一个工厂函数,该函数接收 track 和 trigger 函数作为参数,并且应该返回一个带有 get 和 set 的对象。
    • 自定义 ref 的get set,用于某些数据变更操作的额外功能封装,可以理解为computed 指令类似的功能之类的
  • shallowRef

    • 如字面意思,浅响应式,如果传入的是基本类型跟 ref 没区别。
    • 如果传入的是引用类型,.value值将不会是响应式的数据,ref的 value 属性则会是响应式的
  • triggerRef

    • 手动执行与 shallowRef 关联的任何作用 (effect)。
    • 简单讲就是配合 shallowRef 用的,并且 shallowRef 传入的是个引用类型

5.slot

  • 公开子组件插槽
vue
  <template v-for="(_, name) in $slots" #[name]="slotData">
      <slot :name="name" v-bind="slotData || {}"></slot>
  </template>

6.keycode

键盘绑定 keycode 无效,绑定别名有效

vue
<!-- 无效 -->
<input v-on:keyup.13="submit" />

<!-- 有效 -->
<input v-on:keyup.enter="submit" />

移除 on off 和 $once 方法,使用 mitt 代替

filters 被移除,使用 methods 或者 computed 代替

7. 全局挂载

js
app.config.globalProperties.http = function () {}
app.config.globalProperties.$log = window.console.log
vue
<template>
  <div class="card-wrap">
    <el-button type="primary" @click="onClick">点击</el-button>  
    <span class="card">{{ title }}</span>
  </div>
</template>

<script setup>
import { ref, getCurrentInstance } from 'vue'

const title = ref('abc')

const app = getCurrentInstance()

const onClick = () => {
  app.appContext.config.globalProperties.$message.success('消息')
}
</script>

8.render

js
import { h} from 'vue'
render() {
    return h('div')
}

9.定义异步组件

js
import { defineAsyncComponent } from "vue"
// 方式一
const asyncPageWithOptions = defineAsyncComponent({
  loader: () => import("./NextPage.vue"),
  delay: 200,
  timeout: 3000,
  error: ErrorComponent,
  loading: LoadingComponent,
})
// 方式二
const asyncComponent = defineAsyncComponent(
  () =>
    new Promise((resolve, reject) => {
      /* ... */
    })
)

// 方式三
const com = ref("")
const getComponent = () => {
  import("./NextPage.vue").then(component){
    com = component.default
  }
}

10.指令生命周期修改

js
Vue.directive("highlight", {
  beforeMount(el, binding, vnode) {
    // 对应bind
    el.style.background = binding.value
  },
  mounted() {}, // 对应inserted
  beforeUpdate() {}, // 新增
  updated() {}, // 对应update
  beforeUnmount() {}, // 新增
  unmounted() {}, // 对应unbind
})

14. watch 方法

  • props数据
js
watch(
    () => data,
    (newVal,oldVal) => {
        ...
    },
    {
        deep: true,
        immediate:true
    }
)
  • ref数据
js
watch(
    data,
    (newVal,oldVal) => {
        ...
    },
    {
        deep: true,
        immediate:true
    }
)
js
watch(
    [data1,data2],
    ([newVal1,newVal2],[oldVal1,oldVal2]) => {
        ...
    },
    {
        deep: true,
        immediate:true
    }
)
  • reactive 数据
js
watch(
    ()=>data,
    (newVal,oldVal) => {
        ...
    },
    {
        deep: true,
        immediate:true
    }
)

computed

    1. 选项式写法 支持一个对象传入get函数和set函数自定义操作
js
const name = computed<string>({
  get() {
    return firstName.value + '-' + lastName.value
  },
  set(newVal) {
    console.log(newVal);
    [firstName.value, lastName.value] = newVal.split('-') // 解构赋值
  }
})
    1. 函数式写法 只能支持一个getter函数,不允许修改值
js
const name = computed(() => { 
  return firstName.value + '-' + lastName.value
})

15.onRenderTriggered

16.指令 v-memo

可以缓存 html 模板,比如 v-for 列表不会变化的就缓存,简单说就是用内存换时间

17.v-model 语法糖

在自定义组件上使用v-model

vue
<template>
  <div>
    <el-button @click="increase(-1)">减 1</el-button>
    <span style="color: red; padding: 6px">{{ propsMessage }}</span>
    <el-button @click="increase(1)">加 1</el-button>
  </div>
</template>
<script>
import { defineComponent, computed } from "vue"
export default defineComponent({
  name: "InputNumber",
  props: {
    modelValue: {
      type: String,
      default: "0",
    },
  },
  emits: ["update:modelValue"],
  setup(props, { emit }) {
    const propsMessage = computed({
      get: () => {
        return props.modelValue
      },
      set: (val) => {
        emit("update:modelValue", val)
      },
    })
    const increase = (val) => {
      propsMessage.value += val
    }
    return {
      propsMessage,
      increase,
    }
  },
})
</script>

props 一般不能再组件内修改,它是通过父级修改的,因此实现 v-model 一般会有一个currentValue的内部 data,初始时从 value 获取一次值,当 value 修改时,也通过 watch 监听到及时更新;组件不会修改的 value 的值,而是修改 currentValue,同时将修改的值通过自定义事件input派发给父组件,父组件接收到后,由父组件修改 value。所以,上面的数字选择器组件可以有下面两种方式实现。

vue
<template>
  <InputNumber v-model:custom="value" @update:custom="handleChange" />
</template>
<script>
import InputNumber from "../components/input-number/input-number.vue"

export default {
  components: { InputNumber },
  data() {
    return {
      value: 1,
    }
  },
  methods: {
    handleChange(val) {
      this.value = val
    },
  },
}
</script>

或者

vue
<template>
  <InputNumber v-model="value" />
</template>
<script>
import InputNumber from "../components/input-number/input-number.vue"

export default {
  components: { InputNumber },
  data() {
    return {
      value: 1,
    }
  },
}
</script>

如果你不想用valueinput这两个名字,也可以用model选项指定它们的名字

vue
<template>
  <div>
    <button @click="increase(-1)">减 1</button>
    <span style="color: red;padding: 6px">{{ currentValue }}</span>
    <button @click="increase(1)">加 1</button>
  </div>
</template>
<script>
export default {
  name: "InputNumber",
  props: {
    number: {
      type: Number,
    },
  },
  model: {
    prop: "number",
    event: "change",
  },
  data() {
    return {
      currentValue: this.number,
    }
  },
  watch: {
    value(val) {
      this.currentValue = val
    },
  },
  methods: {
    increase(val) {
      this.currentValue += val
      this.$emit("number", this.currentValue)
    },
  },
}
</script>

18.slot透传

js
<template #root="scoped">
    <slot name="root" v-bind="scoped"></slot>
</template>

19.指令

js
<!-- 自定义指令 v-focus -- 自动聚焦 -->
<script setup>

const vFocus = {
  mounted: (el) => el.focus()
}
</script>

<template>
  <input v-focus />
</template>
js
app.directive('color', (el, binding) => {
  // 仅在 `mounted` 和 `updated` 时都调用
  el.style.color = binding.value
})