Skip to content

二.Vue3.x(基础)

前言

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

1.Teleport

把节点挂载到指定 id 下

<template>
    <button @click="handleClick(true)">开启</button>
    <teleport to="#app">
        <div class="modal" v-if="show">
            <button @click="handleClick(false)">关闭</button>
            <p>hello world</p>
        </div>
    </teleport>
</template>

<script lang="ts" setup>
import { ref } from 'vue'
const show = ref<boolean>(false)
const handleClick = (val) => {
    show.value = val
}
</script>
<style scoped>
.modal {
    background: lightblue;
    position: fixed;
    top: 50%;
    left: 50%;
    z-index: 1000;
}
</style>

2.Suspense

展示加载状态

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

3.reactive

  • 对象或数组(原理是Proxy)
修改的数值:[]
<template>
    <div>修改的数值:{{ data.list }}</div>
    <button @click="handleClick">push添加数据 </button>  
    <button @click="handleClick2"> 赋值添加数据</button>
</template>

<script lang="ts" setup>
import { reactive, watch } from "vue"
const data = reactive({
    list: [],
})
const handleClick = () => {
    data.list.push(1)
}
const handleClick2 = () => {
    data.list = [2]
}
watch(
    () => data.list,
    (newVal, oldVal) => {
        alert(JSON.stringify({ newVal, oldVal }))
    }
)
</script>

4.ref

  • 获取组件dom

xxxxx

<template>
    <p ref="dom">xxxxx</p>
</template>

<script setup lang="ts">
import { ref } from 'vue'

const dom = ref<string>()
console.log(dom, 'dom节点')
</script>
  • 变量

data1

data2

<template>
    <p>{{ data1 }}</p>
    <p>{{ data2 }}</p>
</template>

<script setup lang="ts">
import { ref } from 'vue'

const data1 = ref<string>('data1')
const data2 = ref<string>('data2')
</script>

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

15.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
})

16.onRenderTriggered

17.指令 v-memo

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

18.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>

19.slot透传

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

20.指令

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