【Vue3】Vue3手册

2022/12/05 12:15:132022/12/02 10:02:56

如果只想给页面添加一些简单的交互,可以使用 petite-vueopen in new window,这是一个 Vue 的子集,只有 6kB 左右。

Vue 模板编译预览open in new window

响应式

组件

渲染函数

渲染函数 & JSXopen in new window

渲染函数 APIopen in new window

API 参考open in new window

全局属性

Vue2 中可以通过在 Vue.prototype 上添加属性实现全局访问。

// before - Vue 2
Vue.prototype.$http = () => {};

Vue3 中变更为 app.config.globalProperties

// after - Vue 3
const app = createApp({});
app.config.globalProperties.$http = () => {};

可直接在模板中使用,也可用 this 访问:

<template>
  <div>{{ msg }}</div>
</template>
<script>
export default {
  mounted() {
    console.log(this.msg);
  },
};
</script>

watch

  • 二参的处理程序接收两个参数,一参为新值,二参为旧值,如果传入的是数组,返回值为:(新值组,旧值组)
const x = ref(0);
const y = ref(0);

// 单个 ref
watch(x, (newX) => {
  console.log(`x is ${newX}`);
});

// 多个来源组成的数组
watch([x, () => y.value], ([newX, newY], [oldX, oldY]) => {
  console.log(`x is ${newX} and y is ${newY}`);
  console.log(`oldx is ${oldX} and oldy is ${oldY}`);
});
  • 监听响应式对象属性值时需要用 getter 函数包装:
const obj = reactive({ count: 0 });

// 错误,因为 watch() 得到的参数是一个 number
watch(obj.count, (count) => {
  console.log(`count is: ${count}`);
});

// 提供一个 getter 函数
watch(
  () => obj.count,
  (count) => {
    console.log(`count is: ${count}`);
  }
);

deep 深层监听

直接给 watch() 传入一个响应式对象,会隐式地创建一个深层侦听器——该回调函数在所有嵌套的变更时都会被触发:

const obj = reactive({ count: 0 });

watch(obj, (newValue, oldValue) => {
  // 在嵌套的属性变更时触发
  // 注意:`newValue` 此处和 `oldValue` 是相等的
  // 因为它们是同一个对象!
});

obj.count++;
  • 一个返回响应式对象的 getter 函数只监听该响应式对象本身的变化,不会监听深层属性,相当于浅层监听,可使用 deep 选项强制转为深层:
watch(
  () => state.someObject,
  (newValue, oldValue) => {
    // 注意:`newValue` 此处和 `oldValue` 是相等的
    // *除非* state.someObject 被整个替换了
  },
  { deep: true }
);

watchEffect() 立即执行open in new window

在创建侦听器时立即执行一次。

watchPostEffect() open in new window

在 Vue 组件更新之后被调用(默认的侦听器回调函数都是在 Vue 组件更新之前调用,这意味着你在侦听器回调中访问的 DOM 将是更新之前的状态)。

也可以在其他创建侦听器方法中指明 flush: 'post' 选项以达到同样的效果:

watch(source, callback, {
  flush: "post",
});
watchEffect(callback, {
  flush: "post",
});

停止侦听器

文档位置open in new window

同步创建的侦听器会在不需要时自动停止,异步创建的侦听器不会绑定到当前组件,需要手动停止。

创建侦听器方法的返回值就是停止侦听器函数:

import { watchEffect } from "vue";

// 异步侦听器不会自动停止
setTimeout(() => {
  watchEffect(() => {});
}, 100);

const unwatch = watchEffect(() => {});

// ...当该侦听器不再需要时
unwatch();

ref 模板引用

为了通过组合式 API 获得该模板引用,我们需要声明一个同名的 ref:

<template>
  <input ref="input" />
</template>
<script setup>
import { ref, onMounted } from "vue";

// 声明一个 ref 来存放该元素的引用
// 必须和模板里的 ref 同名
const input = ref(null);

onMounted(() => {
  input.value.focus();
});
</script>
  • v-for 中使用模板引用时,对应的 ref 包含的值是 DOM 数组,ref 数组并不保证与源数组相同的顺序

组件的 ref

如果一个子组件使用的是选项式 API 或没有使用 <script setup>,被引用的组件实例和该子组件的 this 完全一致,这意味着父组件对子组件的每一个属性和方法都有完全的访问权。

使用了 <script setup> 的组件是默认私有的:一个父组件无法访问到一个使用了 <script setup> 的子组件中的任何东西,除非子组件在其中通过 defineExpose() 方法显式暴露:

<script setup>
import {ref} from 'vue';
const a = 1;
const b = ref(2);
defineExpose({(a, b)})
</script>

v-for

  • v-for 可以使用解构方式定义:
<li v-for="{ message } in items">{{ message }}</li>

<!-- 有 index 索引时 -->
<li v-for="({ message }, index) in items">{{ message }} {{ index }}</li>
  • v-for 遍历数组时可以用 of 来代替 in
  • v-for 遍历对象时,遍历的顺序会基于对该对象调用 Object.keys() 的返回值来决定。

v-model

Vue3 中的 v-modelVue2 中的 .sync 行为相似,Vue3 中可指定多个 v-model

默认的 v-model 绑定的属性名与事件名为 modelValueupdate:modelValue

组件内触发事件 $emit('update:modelValue') 实现数据的双向绑定。

在 v-model 上使用修饰符open in new window

<input v-model="searchText" />
<!-- 等价于 -->
<input :value="searchText" @input="searchText = $event.target.value" />
<!-- 在组件上使用时等价于 -->
<CustomInput
  :modelValue="searchText"
  @update:modelValue="newValue => searchText = newValue"
/>

绑定多个 v-model

<ChildComponent
  v-model="modelValue"
  v-model:title="pageTitle"
  v-model:content="pageContent"
/>

<!-- 等价于 -->

<ChildComponent
  :modelValue="modelValue"
  @update:modelValue="modelValue = $event"
  :title="pageTitle"
  @update:title="pageTitle = $event"
  :content="pageContent"
  @update:content="pageContent = $event"
/>

内置组件 <Teleport>

文档位置open in new window

<Teleport> 可以将组件内的 dom 渲染到指定的地方。

比如下面的页面结构,<comp1> 的内容会被渲染到 <main> 标签下:

<html>
  <body>
    <main>
      <comp1></comp1>
    </main>
  </body>
</html>

但是如果在 <comp1> 组件中使用 <Teleport> 组件,就可以将内容渲染到 <body> 标签中:

<template>
  <button @click="open = true">Open Modal</button>

  <!-- Teleport 中的元素会被渲染到 body 中 -->
  <Teleport to="body">
    <div v-if="open" class="modal">
      <p>Hello from the modal!</p>
      <button @click="open = false">Close</button>
    </div>
  </Teleport>
</template>

生命周期钩子open in new window

Vue3 中的钩子函数需要主动引入。

beforeCreate -> setup

created -> setup

beforeMount -> onBeforeMount

mounted -> onMounted

beforeUpdate -> onBeforeUpdate

updated -> onUpdated

beforeDestroy -> onBeforeUnmount

destroyed -> onUnmounted

errorCaptured -> onErrorCaptured