Skip to content

Vue2

[TOC]

索引

指令

插值语法

  • mustache{ {variable} },最基础的数据绑定方式,用于将数据动态渲染为纯文本内容。

文本渲染

  • v-textdataProperty,用于将数据作为纯文本动态渲染到元素中。
  • v-htmldataProperty,用于将数据作为原始 HTML 解析并渲染到元素中。
  • v-pre,用于跳过指定元素及其子元素的编译过程,直接保留原始内容。

绑定

  • v-bind表达式,用于动态绑定HTML属性、组件prop或DOM属性到Vue实例的数据。
  • v-on表达式,用于监听 DOM 事件或自定义事件,并执行对应的 JavaScript 代码或方法。
  • v-model表达式,用于在表单输入元素或自定义组件上实现双向数据绑定,是 v-bind 和 v-on 的语法糖。

条件渲染

  • v-show布尔表达式,用于通过切换 CSS 的 display 属性来控制元素的显示与隐藏。
  • v-if、v-else、v-else-if条件表达式,用于实现条件渲染,根据表达式的真假动态控制元素的渲染或销毁。元素会从DOM中移除。

列表渲染

  • v-for表达式,用于基于数据源(数组、对象、数值范围)循环渲染多个元素或组件。
  • key唯一标识,用于标识虚拟 DOM 节点身份的特殊属性,它在 Vue 的响应式更新机制中起到优化渲染性能维护组件状态的关键作用。

其他

  • v-once,用于标记元素或组件仅渲染一次,后续数据变化时不再更新。
  • v-cloak,用于在 Vue 实例完成编译前隐藏未编译的模板内容,避免页面加载时出现原始模板符号({ { } })的短暂闪烁。必需配合 CSS 样式,CSS 必须全局生效

全局API

应用实例

  • createApp()(rootComponent, rootProps?),用于创建一个 Vue 应用实例,是 Vue 3 应用开发的入口函数。
  • app.use()(plugin,...options?),用于为 Vue 应用安装插件(如 Vue Router、Pinia等)或自定义功能扩展。
  • app.mount()(containerSelector | element),将 Vue 应用实例挂载到 DOM 元素。
  • app.component()(name,component?),用于全局注册或获取组件。
  • app.directive()(name, directive?),用于注册或获取全局自定义指令。
  • app.mixin()(mixin),用来全局注册一个混入对象。会将该对象的所有组件选项(data,methods,...)合并到每个组件中。

通用

  • nextTick()(callback?),通常用来在下一次 DOM 更新刷新后执行某些操作。

选项式API

渲染选项

  • templateHTML字符串 | ID选择器,定义组件结构的地方,通常用于描述组件的 HTML 结构。使用 Vue 的模板语法,可以让我们通过声明式的方式构建 UI,结合数据、指令和事件来动态地渲染内容。
  • render(h),用于直接控制虚拟DOM生成的核心方法,常用于替代模板语法来实现更灵活的渲染逻辑。
  • h()(tag, props?, children?),用于创建虚拟DOM节点VNode。

状态选项

  • data() => ({}),返回组件响应式数据的对象。
  • propsstring[] | Object,声明从父组件接收的属性。可以是一个数组、一个对象,或者一个函数。
  • computed{ Getter|{Getter,Setter?},...},用于声明依赖于其他数据的计算属性。
  • methods{Record<string,Function>},用于定义组件的方法,通常用于事件处理、逻辑操作或触发副作用(如 API 请求)。所有方法会被混入组件实例,可通过 this 直接调用。
  • watch{attr,...},监听响应式数据的变化,并在变化时执行自定义逻辑,如异步操作、复杂逻辑、副作用。
  • emits数组形式|对象形式,显式声明组件可以触发的自定义事件,用于子组件向父组件通信。
  • exposestring[],用于显式声明组件对外暴露的公共属性或方法,通过模板引用访问时,只能访问暴露的内容。

生命周期钩子

  • beforeCreate()=>void,注册一个钩子,在Vue实例初始化之后立即调用。
  • created()=>void,注册一个钩子,在Vue实例完成数据观测(data、props初始化)后,但尚未挂载DOM时调用。
  • beforeMount(),类似 onBeforeMount(),注册一个钩子,在组件被挂载之前被调用。
  • mounted(),类似 onMounted(),注册一个钩子,在组件挂载完成后执行。
  • beforeUpdate(),类似 onBeforeUpdate(),注册一个钩子,在组件即将因为响应式状态变更而更新其 DOM 树之前调用。
  • updated(),类似 onUpdated(),注册一个钩子,在组件因为响应式状态变更而更新其 DOM 树之后调用。
  • beforeUnmount(),类似 onBeforeUnmount(),注册一个钩子,在组件实例被卸载之前调用。
  • unmounted(),类似 onUnmounted(),注册一个钩子,在组件实例被卸载之后调用。
  • activated(),类似 onActivated(),注册一个钩子,若组件实例是<KeepAlive>缓存树的一部分,当组件被插入到DOM中时调用。
  • deactivated(),类似 onDeactivated(),注册一个钩子,若组件实例是<KeepAlive>缓存树的一部分,当组件从DOM中被移除时调用。

组合选项

  • provide(),类似 provide(),提供一个值,可以被后代组件注入。
  • inject(),类似 inject(),注入一个由祖先组件或整个应用通过app.provide()提供的值。
  • mixins(),类似 app.mixin(),用来全局注册一个混入对象。会将该对象的所有组件选项(data,methods,...)合并到每个组件中。
  • extendscpn,用于组件继承的选项,允许一个组件基于另一个组件进行扩展,复用其选项并覆盖或增强特定逻辑。

其他

  • namestring,用于显式声明组件的名称。主要用于调试、递归组件、动态组件、缓存控制。
  • components{name: cpn,...},用于在当前组件中局部注册子组件,使得这些子组件可以在当前组件的模板中使用。
  • derectives(),类似 app.directive(),用于注册或获取全局自定义指令。

组件实例

  • $data(),类似 data,返回组件响应式数据的对象。返回组件响应式数据的对象。
  • $props(),类似 props,声明从父组件接收的属性。可以是一个数组、一个对象,或者一个函数。
  • $el,Vue实例的内置属性,指向该实例关联的根DOM元素。在组件中对应组件模板的根元素
  • $options,Vue实例的内置属性,用于访问组件定义的 原始选项对象
  • $slotsslot,Vue实例的内置属性,用于访问组件接收的 插槽内容
  • $refs,Vue 实例的内置属性,用于直接访问模板中通过 ref 属性标记的 DOM元素子组件实例
  • $parent,Vue实例的内置属性,用于直接访问当前组件的 父组件实例
  • $watch()(),类似 watch,监听响应式数据的变化,并在变化时执行自定义逻辑,如异步操作、复杂逻辑、副作用。
  • $emit()(),类似 emits,显式声明组件可以触发的自定义事件,用于子组件向父组件通信。
  • $forceUpdate()(),Vue实例的内置方法,用于 强制触发当前组件及其子组件的重新渲染的应急手段
  • $nextTick()(),类似 nextTick(),通常用来在下一次 DOM 更新刷新后执行某些操作。

指令

插值语法

mustache

mustache{ {variable} },最基础的数据绑定方式,用于将数据动态渲染为纯文本内容。

  • 语法:

    html
    <div>{{ variable }}</div>
  • 示例:

    html
    <script>
    data() {
      return {
        message: "Hello Vue!",
        count: 42
      };
    }
    </script>
    
    <div>{{ message }}</div>    <!-- 输出:Hello Vue! -->
    <div>{{ count }}</div>      <!-- 输出:42 -->
  • 特性:

  • 响应式:数据变化时,插值内容会自动更新。

  • 支持JS表达式

    html
    <!-- 1. 算术运算 -->
    <div>{{ count + 1 }}</div>         <!-- 输出:43 -->
    <div>{{ count * 2 }}</div>         <!-- 输出:84 -->
    
    <!-- 2. 三元运算符 -->
    <div>{{ count > 0 ? '正数' : '零或负数' }}</div> <!-- 输出:正数 -->
    
    <!-- 3. 方法调用(不推荐) -->
    <div>{{ message.toUpperCase() }}</div> <!-- 输出:HELLO VUE! -->
    
    <!-- 4. 访问对象或数组 -->
    <div>{{ user.name }}</div>       <!-- 输出:Alice -->
    <div>{{ list[0] }}</div>          <!-- 输出:Apple -->
  • 不支持语句和声明

    html
    <!-- 1. 不支持变量声明 -->
    <h2>{{var name = "Hello"}}</h2> <!-- 错误用法 -->
    
    <!-- 2. 不支持流程控制语句if -->
    <h2>{{ if (true) { return message } }}</h2> <!-- 错误用法 -->
  • HTML转义:mustache语法会将内容中的HTML标签自动转义为纯文本,避免 XSS 攻击。

    html
    <div>&lt;span&gt;危险内容&lt;/span&gt;</div> <!-- 转义后结果 -->
  • 不能用于HTML属性:mustache语法不能直接用于HTML属性,需改用 v-bind。

    html
    <div id="{{ dynamicId }}"></div> <!-- 错误用法 -->
    <div v-bind:id="dynamicId"></div> <!-- 正确用法 -->
  • 不推荐方法调用:mustache中调用方法是合法的,但是只推荐逻辑简单且无副作用的方法,复杂逻辑的方法推荐使用computed计算属性。

    html
    <div>{{ reversedMessage }}</div> <!-- 输出:!euV olleH -->
    js
    computed: {
      reversedMessage() {
        return this.message.split('').reverse().join('');
      }
    }
  • 结合过滤器

    • 在 Vue2 中,可通过过滤器格式化内容。
    • 在 Vue3 中已经废弃了过滤器,推荐使用计算属性或方法。
    html
    <div>{{ message | capitalize }}</div>
    js
    filters: {
      capitalize(value) {
        return value.charAt(0).toUpperCase() + value.slice(1);
      }
    }
  • 空白内容处理:如果绑定的值为 null 或 undefined,Mustache 会渲染为空字符串。

    html
    <div>{{ undefinedValue }}</div> <!-- 输出空白 -->
  • 闪烁问题:在未编译完成时,原始 { { } } 可能短暂显示。可以添加v-cloak并通过 CSS 隐藏未编译的模板。

    html
    <div v-cloak>{{ message }}</div>
    css
    [v-cloak] { display: none; }

文本渲染

v-text

v-textdataProperty,用于将数据作为纯文本动态渲染到元素中。

  • dataProperty基本类型,绑定的数据可以是字符串、数字或其他基本类型,非字符串类型会自动转换为字符串。

  • 示例:

    html
    <div v-text="message"></div>  <!-- 输出:Hello Vue! -->
    <div v-text="count"></div>    <!-- 输出:42 -->
    js
    data() {
      return {
        message: "Hello Vue!", // 字符串
        count: 42 // 数字
      };
    }
  • 特性:

  • 覆盖行为

    • v-text 会替换目标元素的全部内容,包括子元素。
    • Mustache 仅替换插值标记位置的内容。
    html
    <!-- 1. 使用 v-text -->
    <div v-text="content">原始内容会被覆盖</div>
    <!-- 渲染结果:<div>动态内容</div> -->
    
    <!-- 2. 使用 Mustache -->
    <div>原始内容 {{ dynamicPart }} 保留</div>
    <!-- 渲染结果:<div>原始内容 动态部分 保留</div> -->
  • 转义行为

    • v-text 会将内容中的 HTML 标签转义为纯文本,防止 XSS 攻击。
    • v-html 会解析数据中的 HTML 标签并渲染。
    html
    <div>&lt;script&gt;alert(1)&lt;/script&gt;</div> <!-- v-text转义后结果 -->
  • 动态绑定:可绑定计算属性、方法返回值或复杂表达式。

    html
    <div v-text="message.toUpperCase()"></div> <!-- 方法 -->
    <div v-text="user.name + '的年龄:' + user.age"></div> <!-- 表达式 -->
  • 闪烁问题:v-text 不存在闪烁问题。

  • 性能优化:对于复杂逻辑,优先使用计算属性而非直接在模板中调用方法。

    html
    <!-- 推荐 -->
    <div v-text="formattedDate"></div>
    
    <!-- 不推荐 -->
    <div v-text="formatDate(timestamp)"></div>
  • 指令冲突:避免在同一元素上同时使用 v-text 和 v-html,或与其他内容类指令混用。

v-html

v-htmldataProperty,用于将数据作为原始 HTML 解析并渲染到元素中。

  • dataPropertyHTMLString,包含有效的 HTML 的字符串。

  • 语法:

    html
    <div v-html="rawHtml"></div>
  • 示例:

    html
    <!-- 模板中使用 -->
    <div v-html="rawHtml"></div> 
    
    <!-- Vue 实例或组件 -->
    <script>
    data() {
      return {
        rawHtml: '<span style="color: red;">红色文字</span>'
      };
    }
    </script>
    
    <!-- 渲染结果 -->
    <div>
      <span style="color: red;">红色文字</span>
    </div>
  • 特性:

  • 对比mustache

    • mustache 会将数据解释为纯文本,直接输出 HTML 标签的源代码。
    • v-html 会解析数据中的 HTML 标签并渲染。
    html
    <div>&lt;span style="color: red;"&gt;红色文字&lt;/span&gt;</div> <!-- mustache渲染结果 -->
    
    <div><span style="color: red;">红色文字</span></div> <!-- v-html渲染结果 -->
  • 动态绑定:v-html 可以绑定动态值,如计算属性、方法返回值等。

    html
    <div v-html="dynamicHTML"></div>
    
    <script>
    computed: {
      dynamicHTML() {
        return this.isError ? '<p class="error">错误信息</p>' : '<p>正常信息</p>';
      }
    }
    </script>
  • XSS攻击

    • v-html 会直接渲染任意 HTML,如果内容来自用户输入或不可信来源,可能导致XSS跨站脚本攻击。需仅渲染可信内容,如后端返回的已消毒的富文本。

      html
      <div v-html="userProvidedContent"></div> <!-- 危险! -->
    • 替代方案:如果需要渲染部分用户输入,可以使用第三方库DOMPurify消毒。

      js
      import DOMPurify from 'dompurify';
      
      data() {
        return {
          userContent: DOMPurify.sanitize(untrustedHTML)
        };
      }
  • 指令冲突:避免在同一元素上同时使用 v-text 和 v-html,或与其他内容类指令混用。

v-pre

v-pre,用于跳过指定元素及其子元素的编译过程,直接保留原始内容。

  • 语法:

    html
    <div v-pre>
      <!-- 内部内容不会被 Vue 编译 -->
    </div>
  • 示例:

    html
    <template>
      <div>
        <!-- 使用 v-pre 展示源码 -->
        <pre v-pre>
          &lt;div&gt;{{ message }}&lt;/div&gt;
          &lt;button @click="show = true"&gt;显示&lt;/button&gt;
        </pre>
      </div>
    </template>
  • 特性:

  • 跳过编译

    • v-pre 会跳过元素及其所有子元素的 Vue 模板编译过程,直接保留原始内容。
    • 即使内容包含 Vue 语法,如 { { } } 或指令,也会直接显示为文本。
  • 作用范围:v-pre 会影响整个元素及其子元素,无法局部禁用编译。若需部分跳过编译,需拆分元素结构。

  • 与动态内容冲突:在 v-pre 区域内,所有 Vue 功能如数据绑定、指令均失效。

  • 性能影响:仅在需要时使用 v-pre,过度使用可能导致模板编译优化机会减少。

绑定

v-bind

v-bind表达式,用于动态绑定HTML属性、组件prop或DOM属性到Vue实例的数据。

  • 语法:

    html
    <!-- 绑定单个属性 -->
    <div v-bind:属性名="表达式"></div>
    
    <!-- 简写形式(推荐) -->
    <div :属性名="表达式"></div>
  • 示例:

    html
    <a :href="url">Vue 官网</a>
    <button :disabled="!isActive">提交</button>
  • 特性:

  • 动态参数

    • 绑定动态参数:通过 [] 语法动态指定绑定的属性名。

      html
      <div :[attrName]="123"></div>
      <!-- 渲染结果:<div data-id="123"></div> -->
      js
      data() {
        return { attrName: "data-id" }
      }
    • 动态参数限制:动态属性名需为字符串,且避免使用大写或特殊字符(空格/引号)。当使用DOM内嵌模板(直接写在HTML文件里的模板),浏览器会强制将其转换为小写。

      html
      <!-- 错误示例 -->
      <div :[dynamicAttr]="value"></div> <!-- HTML模板中会被转换为:[dynamicattr] -->
  • 批量绑定属性:直接绑定一个对象,键为属性名,值为对应数据。

    html
    <div v-bind="attrs"></div>
    <!-- 渲染结果:<div id="container" class="main" data-status="active"></div> -->
    js
    data() {
      return {
        attrs: {
          id: "container",
          class: "main",
          "data-status": "active"
        }
      };
    }
  • 绑定class

    • 对象语法:根据条件动态切换类名。

      html
      <div :class="{ active: isActive, 'text-red': hasError }"></div>
    • 数组语法:绑定多个类名。

      html
      <div :class="[baseClass, isActive ? 'active' : '']"></div>
  • 绑定style

    • 对象语法:绑定内联样式对象。CSS属性名可以是camelCase,也可以是kebab-case的写法。

      html
      <div :style="{ color: textColor, fontSize: fontSize + 'px' }"></div>
    • 数组语法:应用多个样式对象。CSS属性名可以是camelCase,也可以是kebab-case的写法。

      html
      <div :style="[baseStyles, overrides]"></div>
  • 组件prop传递:在组件上使用v-bind可批量传递props。

    html
    <child-component v-bind="propsObj"></child-component>
  • Vue3变化

    • 移除.sync修饰符:Vue3中废弃.sync,改用v-model参数实现双向绑定。

      html
      <!-- Vue 2 -->
      <ChildComponent :title.sync="pageTitle" />
      
      <!-- Vue 3 -->
      <ChildComponent v-model:title="pageTitle" />
    • 合并行为:当同时使用普通属性和v-bind绑定时,后者会覆盖前者。

      html
      <div id="static" :id="dynamicId"></div>
      <!-- 渲染结果:<div id="dynamic"></div> -->

v-on

v-on表达式,用于监听 DOM 事件或自定义事件,并执行对应的 JavaScript 代码或方法。

  • 语法:

    html
    <!-- 监听事件并执行表达式 -->
    <div v-on:事件名="表达式"></div>
    
    <!-- 简写形式(推荐) -->
    <div @事件名="表达式"></div>
  • 示例:

    html
    <button @click="count += 1">点击增加</button>
    <button @mouseover="showTooltip = true">悬停提示</button>
  • 特性:

  • 绑定方法:将事件直接绑定到组件中定义的方法。

    html
    <button @click="handleClick">提交</button>
    js
    methods: {
      handleClick() {
        console.log("按钮被点击");
      }
    }
  • 传递参数

    • 手动传递参数

      html
      <button @click="handleDelete(item.id)">删除</button>
      js
      methods: {
        handleDelete(id) {
          this.items = this.items.filter(item => item.id !== id);
        }
      }
    • 获取原生事件对象$event:当需要同时传递参数和原生事件对象时,显式传递 $event

      html
      <button @click="handleSubmit('参数', $event)">提交</button>
      js
      methods: {
        handleSubmit(arg, event) {
          event.preventDefault();
          console.log("参数:", arg);
        }
      }
  • 事件修饰符:Vue 提供了事件修饰符,用于简化常见的事件处理逻辑。

    • .stop:阻止事件冒泡,等价于 event.stopPropagation()
    • .prevent:阻止默认行为,等价于 event.preventDefault()
    • .capture:使用事件捕获模式,从外到内触发。
    • .self:仅当事件从元素本身触发时(非子元素)才执行回调。
    • .once:事件只触发一次,Vue3 中也可用于组件事件。
    • .passive:表示不阻止默认行为。提升滚动性能,与 addEventListener 的 passive 选项一致。慎用,不可与 .prevent 同时使用。
    • .native:监听原生事件(Vue3已废弃)。
    html
    <!-- 阻止表单默认提交和事件冒泡 -->
    <form @submit.prevent.stop="onSubmit"></form>
    
    <!-- 点击事件最多触发一次 -->
    <button @click.once="handleOnce">仅一次</button>
    
    <!-- 滚动事件使用 passive 修饰符优化性能 -->
    <div @scroll.passive="onScroll"></div>
  • 按键修饰符:用于监听特定键盘按键的事件,如 Enter、Esc 等。

    • 常用按键别名

      html
      <input @keyup.enter="submit" />      <!-- 回车键 -->
      <input @keyup.esc="closeModal" />    <!-- Esc 键 -->
      <input @keyup.space="playPause" />   <!-- 空格键 -->
      <input @keyup.delete="clearInput" />  <!-- 删除键 -->
    • 自定义按键码:按键码keyCode,Vue 2 支持,Vue 3 已废弃。

      html
      <input @keyup.13="submit" /> <!-- Vue2 中支持 -->
    • 组合键修饰符

      html
      <input @keyup.ctrl.enter="save" />   <!-- Ctrl + Enter -->
      <input @keyup.alt.s="save" />        <!-- Alt + S -->
  • 动态事件名:通过动态属性名绑定不同事件。

    html
    <button @[eventName]="handleEvent"></button>
    js
    data() {
      return {
        eventName: "click"
      };
    }
  • 在组件上监听自定义事件:用于父子组件通信,通过 $emit 触发。

    html
    <!-- 父组件监听子组件的自定义事件 -->
    <ChildComponent @custom-event="handleCustomEvent" />
    js
    // 子组件触发事件
    this.$emit("custom-event", arg1, arg2);
  • 避免内联复杂表达式:推荐将逻辑封装到方法中,而非直接在模板中编写复杂表达式。

    html
    <!-- 不推荐 -->
    <button @click="count += 1; logCount()">增加</button>
    
    <!-- 推荐 -->
    <button @click="handleIncrement">增加</button>

v-model@

v-model表达式,用于在表单输入元素或自定义组件上实现双向数据绑定,是 v-bind 和 v-on 的语法糖。

  • 语法:

    html
    <!-- 基础用法 -->
    <input v-model="dataProperty" />
    
    <!-- 等价于手动绑定 -->
    <input 
      :value="dataProperty" 
      @input="dataProperty = $event.target.value" 
    />
  • 特性:

  • 响应式:表单输入的值与 Vue 实例中的 dataProperty 保持同步。

  • 不同类型表单元素的使用

    • 文本输入:input[type=text]和textarea

      html
      <input type="text" v-model="message" />
      <textarea v-model="content"></textarea>
    • 复选框

      • 单个复选框:绑定布尔值。

        html
        <input type="checkbox" v-model="isAgreed" /> 同意协议
      • 多个复选框:绑定数组。

        html
        <input type="checkbox" value="vue" v-model="skills" /> Vue
        <input type="checkbox" value="react" v-model="skills" /> React
        js
        data() { return { skills: [] }; }
    • 单选按钮

      html
      <input type="radio" value="male" v-model="gender" /> 男
      <input type="radio" value="female" v-model="gender" /> 女
      js
      data() { return { gender: "" }; }
    • 下拉选择

      • 单选

        html
        <select v-model="selectedCity">
          <option value="bj">北京</option>
          <option value="sh">上海</option>
        </select>
      • 多选

        html
        <select v-model="selectedTags" multiple>
          <option value="tag1">标签1</option>
          <option value="tag2">标签2</option>
        </select>
  • 修饰符

    • .lazy:将 input 事件改为 change 事件,输入完成后才同步数据。
    • .number:自动将输入值转为数字类型,避免字符串转换问题。
    • .trim:自动去除输入值首尾空格。
    html
    <!-- 输入完成后同步 -->
    <input v-model.lazy="message" />
    
    <!-- 强制转为数字 -->
    <input type="number" v-model.number="age" />
    
    <!-- 去除首尾空格 -->
    <input v-model.trim="username" />
  • 在自定义组件中使用

    • Vue2 的实现:默认使用 value 属性 和 input 事件。

      html
      <!-- 父组件 -->
      <CustomInput v-model="inputValue" />
      
      <!-- 子组件 -->
      <input 
        :value="value" 
        @input="$emit('input', $event.target.value)" 
      />
      js
      // 子组件声明
      props: ["value"],
    • Vue3 的改进

      • 默认使用 modelValue prop 和 update:modelValue 事件。

        html
        <!-- 父组件 -->
        <CustomInput v-model="count"/>
        
        <!-- 等价于以下写法 -->
        <CustomInput :modelValue="count" @update:modelValue="count = $event"/>
        html
        <!-- 子组件 -->
        <template>
          <button @click="hdlUpdateCount">修改Count</button>
        </template>
        
        <script setup>
          const props = defineProps({
            modelValue: {
              type: Number,
              default:0
            }
          })
          const emit = defineEmits(['update:modelValue'])
          function hdlUpdateCount() {
            emit('update:modelValue', 999)
          }
        </script>
      • 支持自定义参数名(多个双向绑定)。

        html
        <!-- 父组件 -->
        <CustomInput v-model:title="pageTitle" v-model:content="pageContent"  />
        
        <!-- 等价于以下写法 -->
        <CustomInput
          :title="pageTitle" @update:title="pageTitle = $event"
          :content="pageContent" @update:content="pageContent = $event"
        />
        html
        <!-- 子组件 -->
        <template>
          <button @click="hdlUpdateTitle">修改Title</button>
          <button @click="hdlUpdateContent">修改Content</button>
        </template>
        
        <script setup>
          const props = defineProps({
            title: {
              type: String,
              default: ''
            }
            content: {
              type: String
              default: ''
            }
          })
          const emit = defineEmits(['update:title', 'update:content'])
          function hdlUpdateTitle() {
            emit('update:title', '修改后的标题')
          }
          function hdlUpdateContent() {
            emit('update:content', '修改后的内容')
          }
        </script>
    • 修改默认行为:通过组件的 model 选项(Vue2)或 v-model 参数(Vue3)自定义.

      js
      // Vue 2 自定义 prop 和事件
      model: {
        prop: "selected",
        event: "change"
      }

条件渲染

v-show

v-show布尔表达式,用于通过切换 CSS 的 display 属性来控制元素的显示与隐藏。

  • 语法:

    html
    <div v-show="布尔表达式"></div>
  • 示例:

    html
    <div v-show="isVisible">此内容会根据 isVisible 显示/隐藏</div>
    js
    data() {
      return { isVisible: true } 
    }
  • 特性:

  • 实现原理

    • 当表达式为 true 时:移除元素的 display: none 样式。 当表达式为 false 时:添加内联样式 display: none
  • 对比v-if

    • DOM操作

      • v-show:始终保留元素,仅切换 display。
      • v-if:条件为假时移除元素。
    • 初始渲染开销

      • v-show:较高,无论条件如何都渲染元素。
      • v-if:较低,条件为假时不渲染。
    • 切换开销

      • v-show:较低,仅修改 CSS。
      • v-if:较高,触发组件销毁 / 重建。
    • 适用场景

      • v-show:频繁切换显示状态,如选项卡。
      • v-if:条件很少变化,如一键展开 / 收起。
  • 不支持<template>元素:v-show必须作用在实际渲染的DOM元素上,不可用于 <template>

    html
    <!-- 错误用法 -->
    <template v-show="condition">
      <div>内容</div>
    </template>
    
    <!-- 正确用法 -->
    <div v-show="condition">
      <div>内容</div>
    </div>

v-if、v-else、v-else-if

v-if、v-else、v-else-if条件表达式,用于实现条件渲染,根据表达式的真假动态控制元素的渲染或销毁。元素会从DOM中移除。

  • 语法:

    html
    <div v-if="条件表达式"></div>
    <div v-else-if="条件表达式"></div>
    <div v-else></div>
  • 示例:

    html
    <div v-if="score >= 90">优秀</div>
    <div v-else-if="score >= 80">良好</div>
    <div v-else-if="score >= 60">及格</div>
    <div v-else>不及格</div>
  • 特性:

  • 条件链必须连续:指令链必须严格遵循 v-ifv-else-ifv-else 的顺序,中间不能插入其他元素。

  • 控制组件/元素的销毁与重建:v-if 为 false 时,元素及其子组件会被销毁并移出 DOM。频繁切换性能会有影响。

  • 结合<template>:使用 <template> 包裹多个元素实现分组条件渲染。

    html
    <template v-if="showSection">
      <h1>标题</h1>
      <p>内容</p>
    </template>
    <template v-else>
      <div>替代内容</div>
    </template>
  • 状态不保留

    • v-if 为 false 时,元素内部状态(如表单输入值)会被销毁,重新渲染时重置。

    • 解决方案:使用 <keep-alive> 包裹元素以缓存状态。

      html
      <keep-alive>
        <component v-if="isActive"></component>
      </keep-alive>
  • 异步组件:若条件依赖异步数据,需确保数据加载完成后再渲染。

    html
    <div v-if="!isLoading && data">
      {{ data.content }}
    </div>

列表渲染

v-for

v-for表达式,用于基于数据源(数组、对象、数值范围)循环渲染多个元素或组件。

  • 语法:

    html
    <元素 v-for="(item, index?) in data" :key="唯一标识">
      {{ item.attr }}
    </元素>
  • itemany,当前遍历的元素值,数组项 / 对象属性值 / 数值。

  • index?string | number,当前项的索引或键名。

  • dataany,支持数组、对象、数值范围。

  • :keyany,用于标识元素唯一性,优化虚拟DOM的更新性能。

  • 特性:

  • 遍历不同类型数据源

    • 遍历数组

      html
      <ul>
        <li v-for="(item, index) in items" :key="item.id">
          {{ index + 1 }}. {{ item.name }} <!-- 输出:1. Apple-->
        </li>
      </ul>
      js
      data() {
        return {
          items: [
            { id: 1, name: "Apple" },
            { id: 2, name: "Banana" }
          ]
        };
      }
    • 遍历对象

      html
      <ul>
        <li v-for="(value, key, index) in obj" :key="key">
          {{ index }}. {{ key }}: {{ value }} <!-- 输出:0. title: Vue 教程-->
        </li>
      </ul>
      js
      data() {
        return {
          obj: { 
            title: "Vue 教程", 
            author: "John" 
          }
        };
      }
    • 遍历数值范围

      html
      <span v-for="n in 5" :key="n">{{ n }} </span> <!-- 输出:1 2 3 4 5 -->
  • :key

    • 作用

      • 唯一标识:帮助 Vue 识别元素身份,避免重复渲染或状态错乱。
      • 性能优化:基于 key 复用已有 DOM 节点,减少不必要的重新渲染。
    • 用法

      • 唯一性:使用稳定且唯一的标识,如 item.id,避免用索引 index

        html
        <!-- 错误:用索引作为 key -->
        <div v-for="(item, index) in list" :key="index">
          {{ item.name }}
        </div>
  • 结合v-if:v-for 的优先级高于 v-if(Vue2)。若需先判断条件再循环,可将 v-if 置于外层元素。

    html
    <!-- 正确写法 -->
    <template v-if="hasData">
      <div v-for="item in list" :key="item.id">
        {{ item.name }}
      </div>
    </template>
  • 结合计算属性:推荐使用计算属性过滤数据,而非在模板中直接操作。

    html
    <div v-for="item in filteredList" :key="item.id">{{ item.name }}</div>
    js
    computed: {
      filteredList() {
        return this.list.filter(item => item.isActive);
      }
    }
  • 性能优化

    • 避免超大列表:对超长列表(如1000+项)使用虚拟滚动技术,如 vue-virtual-scroller

    • 减少响应式依赖:对不需要响应式的静态数据,使用 Object.freeze 冻结。

      js
      data() {
        return {
          list: Object.freeze([...]) // 禁止 Vue 追踪其变化
        };
      }
    • 扁平化数据结构:避免嵌套过深的响应式对象,优先使用扁平化数据。

  • 在组件中使用

    • 传递 Props:在组件上使用 v-for 时,需显式传递数据。

      html
      <ChildComponent 
        v-for="item in list" 
        :key="item.id"
        :item="item"
        @custom-event="handleEvent"
      />
    • 组件状态保留:若组件需要保留内部状态(如输入框内容),需确保 :key 唯一且稳定。

      html
      <UserInput 
        v-for="user in users" 
        :key="user.id" 
        :user="user" 
      />

key@

key唯一标识,用于标识虚拟 DOM 节点身份的特殊属性,它在 Vue 的响应式更新机制中起到优化渲染性能维护组件状态的关键作用。

  • 语法:

    html
    <元素 :key="唯一标识符"></元素> <!-- 在元素上绑定 key -->
    <组件 :key="唯一标识符"></组件> <!-- 在组件上绑定 key -->
  • 唯一标识

    • 唯一性:同一父级下,每个 key 必须唯一,不可重复。
    • 稳定性:key 值应在数据变化时保持稳定,如id而非index。
  • 特性:

  • 核心作用

    • 优化虚拟DOM对比(Diff 算法):Vue 通过 key 识别节点身份,减少不必要的 DOM 操作。
      • 相同 key:复用现有节点,仅更新变化部分。
      • 不同 key:销毁旧节点,创建新节点。
    • 维护组件/元素状态:当元素或组件因为如列表重新排序而数据变化被复用时,key 可确保其内部状态如输入框内容、滚动位置不被意外保留。
  • 应用场景

    • v-for列表渲染:在 v-for 循环中,每个元素必须绑定唯一 key。

    • 强制替换元素/组件:当需要完全替换元素或组件而非复用,如切换表单类型,可通过改变key强制重新渲染。

      html
      <!-- 切换登录方式时,清空输入框 -->
      <div v-if="isEmailLogin">
        <input key="email" placeholder="邮箱">
      </div>
      <div v-else>
        <input key="phone" placeholder="手机号">
      </div>
    • 动态组件强制刷新:为 <component> 绑定key,避免组件复用导致的生命周期钩子未触发。

      html
      <component :is="currentComponent" :key="currentComponent"></component>
    • 过渡动画强制刷新:确保过渡动画在元素身份变化时正确触发。

      html
      <transition>
        <div :key="state">{{ state }}</div>
      </transition>
  • 底层原理

    • 虚拟DOM Diff过程
      • 无key:Vue 采用 “就地更新” 策略,按索引顺序对比节点,可能导致错误复用。
      • 有key:通过 key 精准匹配相同节点,最小化 DOM 操作。
    • 状态保留机制
      • 相同key:复用元素,保留表单值、滚动位置等状态。
      • 不同key:销毁旧元素,初始化新元素。

其他

v-once

v-once,用于标记元素或组件仅渲染一次,后续数据变化时不再更新。

  • 语法:

    html
    <元素 v-once>{{ 静态内容 }}</元素> <!-- 元素上使用 -->
    <组件 v-once></组件> <!-- 组件上使用 -->
  • 示例:

    html
    // 首次渲染后冻结组件
    <template>
      <UserProfile v-once :initialData="userData" /> <!-- 用户资料不再更新 -->
    </template>
  • 特性:

  • 核心作用

    • 单次渲染:初始渲染后,元素/组件及其子内容不再响应数据变化。
    • 性能优化:减少不必要的虚拟 DOM 比对,提升渲染效率。
  • 使用场景

    • 静态内容优化

      html
      <!-- 标题永不更新 -->
      <h1 v-once>{{ initialTitle }}</h1>
    • 复杂计算的缓存

      html
      <!-- 计算密集型数据只计算一次 -->
      <div v-once>{{ heavyCompute(data) }}</div>
    • 避免子组件更新

      html
      <!-- 父组件数据变化时,子组件不更新 -->
      <ChildComponent v-once :prop="staticData" />
  • 作用范围:影响整个元素及其子树,包括子组件。若子组件依赖动态 props,v-once 会阻止其更新。

v-cloak

v-cloak,用于在 Vue 实例完成编译前隐藏未编译的模板内容,避免页面加载时出现原始模板符号({ { } })的短暂闪烁。必需配合 CSS 样式,CSS 必须全局生效

  • 语法:

    html
    <!-- 在根元素或需要隐藏的元素上添加 v-cloak -->
    <div v-cloak>{{ message }}</div>
    css
    /** CSS 必须全局生效 */
    [v-cloak] {
      display: none !important;
    }
  • 示例:

    html
    <!DOCTYPE html>
    <html>
    <head>
      <style>
        [v-cloak] {
          display: none;
        }
      </style>
    </head>
    <body>
      <div id="app" v-cloak>
        <p>{{ message }}</p>
      </div>
    
      <script src="https://unpkg.com/vue@3"></script>
      <script>
        Vue.createApp({
          data() {
            return { message: "Hello Vue!" };
          }
        }).mount('#app');
      </script>
    </body>
    </html>
  • 特性:

  • 实现原理

    • 初始阶段:元素带有 v-cloak 属性,CSS 将其隐藏。
    • 编译完成:Vue 移除所有 v-cloak 属性,元素显示渲染后的内容。
  • 配合异步加载:若 Vue 实例异步加载,如按需加载,v-cloak 仍可确保内容隐藏直至编译完成。

    html
    <div v-cloak>
      <p v-if="loaded">{{ data }}</p>
    </div>

全局API

应用实例

createApp()

createApp()(rootComponent, rootProps?),用于创建一个 Vue 应用实例,是 Vue 3 应用开发的入口函数。

  • rootComponentoptions | Component,根组件,Vue 应用的入口组件。

    • options{data,methods,...},选项式API中的组件选项。

    • Component:组合式API中包含 setup() 方法的组件对象。

  • rootProps?Record<string, any>,传递给根组件的 props 对象,用于父组件向根组件传递数据。

  • 返回:

  • appApp,返回的Vue应用实例,提供多个用于配置和控制应用的实例方法。

  • 示例:

    • script setup
    js
    import { createApp } from 'vue'
    
    import App from './App.vue'
    import store from './store'
    import router from './router'
    import icons from './utils/register-icons'
    
    const app = createApp(App)
    app.use(store)
    app.use(router)
    app.use(icons)
    app.mount('#app')
  • 特性:

  • TS

    ts
    function createApp(rootComponent: Component, rootProps?: object): App
  • TS:使用 defineComponent() 增强类型推断。

    ts
    import { defineComponent } from 'vue';
    const RootComponent = defineComponent({
      data() { return { count: 0 }; }
    });
    const app = createApp(RootComponent)
    app.mount('#app')

app.use()

app.use()(plugin,...options?),用于为 Vue 应用安装插件(如 Vue Router、Pinia等)或自定义功能扩展。

  • pluginObject | Function,插件对象或函数。

    • Object:必须包含 install() 方法。
    • Function:直接作为 install() 方法调用。
  • ...options?any[],传递给插件 install 方法的额外参数(如插件配置项)。

  • 返回:

  • appApp,返回应用实例本身,支持链式调用。

  • 示例:

    • 安装 Vue Router
    js
    import { createApp } from 'vue';
    import App from './App.vue';
    import VueRouter from 'vue-router';  // 假设已安装 vue-router@4
    
    const app = createApp(App);
    
    // 安装路由插件,并传递路由配置
    const router = VueRouter.createRouter({ /* ...路由配置 */ });
    app.use(router);  
    
    app.mount('#app');
  • 特性:

  • TS

    ts
    interface App {
      use(plugin: Plugin, ...options: any[]): this
    }
  • 自定义插件开发:插件必须提供 install() 方法(或自身为函数)。

    js
    // 1. 自定义插件(含 install 方法)
    const MyPlugin = {
    +  install(app, options) {
        // 添加全局组件
        app.component('CustomHeader', {
          template: `<header>{{ title }}</header>`,
          props: ['title']
        });
    
        // 添加全局属性
        app.config.globalProperties.$log = (msg) => console.log(msg);
      }
    };
    
    // 2. 使用插件并传递配置
    const app = Vue.createApp({});
    + app.use(MyPlugin, { debug: true });  // options 参数会传递给 install 方法
    
    // 3. 使用插件功能
    app.component('DemoComponent', {
      mounted() {
    +    this.$log('Component mounted!');  // 调用全局属性
      }
    });

app.mount()

app.mount()(containerSelector | element),将 Vue 应用实例挂载到 DOM 元素。

  • containerSelectorstring,CSS 选择器字符串(如 '#app'),指定挂载的容器元素。

  • elementElement,直接传入 DOM 元素对象(如 document.getElementById('app'))。

  • 返回:

  • instanceComponentPublicInstance,返回根组件实例,可通过该实例访问组件属性/方法。

  • 示例:

    • 挂载 app
    js
    import { createApp } from 'vue';
    import App from './App.vue';
    
    // 创建应用实例
    const app = createApp(App);
    
    // 挂载到 #app 元素
    const rootInstance = app.mount('#app');
    
    // 访问根组件数据
    console.log(rootInstance.someData);
  • 特性:

  • TS

    ts
    interface App {
      mount(rootContainer: Element | string): ComponentPublicInstance
    }
  • TS类型注解:可以给instance添加TS类型注解。

    ts
    interface RootComponentInstance {
        someMethod: () => void
    }
    const instance = app.mount('#app') as unknown as RootComponentInstance;
  • 选项式:

    js
    // 1. Vue2选项式写法:自动挂载
    new Vue({
        el: '#app', // 自动挂载
        render: h => h(App)
    });
    
    // 2.Vue2选项式写法:手动挂载
    const vm = new Vue({
        render: h => h(App)
    }).$mount('#app'); // 等价于 app.mount()
  • 挂载时机:需确保 DOM 已加载完成(通常在 DOMContentLoaded 事件后调用)。

  • 单次挂载限制:每个应用实例只能挂载一次,重复调用 .mount() 会抛出警告。

  • SSR差异:服务端渲染时应使用 createSSRApp + hydrate 而非直接 mount

  • 访问实例不推荐,通过返回的实例可直接操作组件的属性和方法。推荐优先使用 props/emits 通信。

app.component()

app.component()(name,component?),用于全局注册或获取组件。

  • namestring,组件的名称。命名遵循camelCase或kebab-case。

  • component?Component,包含组件配置的对象。

  • 返回:

    • appApp,当传递 component 参数时,表示注册全局组件。返回app,支持链式调用。
    • componentComponent|undefined,当省略 component 参数时,表示获取已注册的组件。返回已注册的组件,未找到则返回 undefined
  • 示例:

    • 注册/获取全局组件
    js
    import { createApp } from 'vue'
    const app = createApp({})
    
    // 1. 全局注册一个按钮组件
    app.component('custom-button', {
      template: `
        <button class="btn">
          <slot></slot>
        </button>
      `
    })
    
    // 2. 获取已注册的组件
    const cpn = app.component('custom-button')
  • 特性:

  • TS

    ts
    interface App {
      component(name: string): Component | undefined
      component(name: string, component: Component): this
    }

app.directive()

app.directive()(name, directive?),用于注册或获取全局自定义指令。

  • namestring,指令的名称。命名通常遵循kebab-case。

  • directive?{mounted,...},包含指令定义的对象。指令定义在不同的生命周期钩子中,控制指令在不同阶段的行为。

  • 返回:

  • appApp,返回app,支持链式调用。

  • 示例:

    • 自定义指令 v-focus
    js
    const app = createApp(App);
    
    // 1. 自定义指令:`v-focus`,使元素获得焦点
    const focusDirective = {
        mounted(el) {
            el.focus(); 
        }
    };
    
    // 2. 注册全局指令
    app.directive('focus', focusDirective);
    
    app.mount('#app');
  • 特性:

  • TS

    ts
    interface App {
      directive(name: string): Directive | undefined
      directive(name: string, directive: Directive): this
    }
  • 选项式

    js
    import { createApp } from 'vue';
    
    const App = {
      // 1. 注册局部指令
      directives: {
        focus: {
          mounted(el) {
            el.focus(); // 元素插入时获得焦点
          }
        }
      },
      template: `
        <div>
          <input v-focus />
        </div>
      `
    };
    
    createApp(App).mount('#app');

app.mixin()

app.mixin()(mixin),用来全局注册一个混入对象。会将该对象的所有组件选项(data,methods,...)合并到每个组件中。

  • mixin{data,methods,...},该对象的组件选项会被合并到每个组件的选项中。

  • 返回:

  • appApp,返回app,支持链式调用。

  • 示例:

    • 全局混入对象
    js
    const globalMixin = {
      data() {
        return {
          mixinData: 'This is data from mixin',
        };
      },
      created() {
        console.log('Mixin created hook');
      },
      methods: {
        greet() {
          console.log('Hello from mixin method');
        }
      }
    };
    
    const app = Vue.createApp(App);
    
    // 2. 注册全局混入对象
    app.mixin(globalMixin);
    app.mount('#app');
    • 局部混入对象
    js
    const localMixin = {
      data() {
        return {
          localMessage: 'This is local mixin data',
        };
      }
    };
    
    const app = Vue.createApp({
      // 2. 注册局部混入对象
      mixins: [localMixin],
      template: '<p>{{ localMessage }}</p>'
    });
    
    app.mount('#app');
  • 特性:

  • TS

    ts
    interface App {
      mixin(mixin: ComponentOptions): this
    }
  • 生命周期钩子的合并:如果定义了多个相同的生命周期钩子,这些钩子会被合并,并按顺序依次调用。

  • 数据合并

    • 如果data选项是一个函数,组件和混入中的data的返回值会被合并(推荐)。
    • 如果data选项是一个普通对象,会出现合并冲突。
  • 方法覆盖:组件的方法会覆盖混入中的方法。

  • 不推荐:Mixins 在 Vue 3 支持主要是为了向后兼容,因为生态中有许多库使用到。在新的应用中应尽量避免使用 mixin,特别是全局 mixin。若要进行逻辑复用,推荐用组合式函数来替代。

通用

nextTick()

nextTick()(callback?),通常用来在下一次 DOM 更新刷新后执行某些操作。

  • callback?() => void,在 DOM 更新后执行的回调函数。

  • 返回:

  • resultPromise<void>,在DOM更新完成后解析。

  • 示例:

    • 在watch或onMounted中使用
    js
    import { ref, watch, nextTick } from 'vue';
    
    const count = ref(0);
    
    // 在watch或onMounted中使用
    watch(count, async () => {
      await nextTick();
      console.log('count 更新后,DOM 渲染完成');
    });
    • 与异步操作结合
    js
    import { ref, nextTick } from 'vue';
    
    const count = ref(0);
    
    const updateAndWait = async () => {
      count.value++;
      // 与异步操作结合
      await nextTick();
      console.log('DOM 更新后进行的操作');
    };
  • 特性:

  • TS

    ts
    function nextTick(callback?: () => void): Promise<void>
  • 性能nextTick() 可能会影响性能,特别是在高频更新的情况下会造成任务队列拥挤。

  • 用途nextTick() 主要是为了解决 Vue 的异步渲染机制。在Vue内部,数据更新是异步的,DOM更新会在下一个“tick”内完成。

defineAsyncComponent()【

选项式API

渲染选项

template

templateHTML字符串 | ID选择器,定义组件结构的地方,通常用于描述组件的 HTML 结构。使用 Vue 的模板语法,可以让我们通过声明式的方式构建 UI,结合数据、指令和事件来动态地渲染内容。

  • 示例:

    HTML字符串

    html
    <div id="app"></div>
    
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/3.5.13/vue.global.js"></script>
    <script>
      const app = Vue.createApp({
        template: '<h2>Vue组件</h2>'
      })
    
      app.mount('#app')
    </script>

    ID选择器-x-template

    html
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/3.5.13/vue.global.js"></script>
    
    <script type="x-template" id="vue">
      <h2>Vue组件-x-template</h2>
    </script>
    
    <script>
      const app = Vue.createApp({
        template: '#vue'
      })
      app.mount('#app')
    </script>

    ID选择器-template

    html
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/3.5.13/vue.global.js"></script>
    
    <template id="vue">
      <h2>Vue组件-template</h2>
    </template>
    
    <script>
      const app = Vue.createApp({
        template: '#vue'
      })
      app.mount('#app')
    </script>

render

render(h),用于直接控制虚拟DOM生成的核心方法,常用于替代模板语法来实现更灵活的渲染逻辑。

  • h(tag, props?, children?)=>VNodeh函数,用于创建虚拟DOM节点VNode。

  • 返回:

  • nodeVNode,返回虚拟DOM节点VNode。

  • 示例:

    js
    export default {
      // 其他选项(data, props, methods等)
      render(h) {
        // h 是 h函数,用于创建VNode
        return h('div', { /* 属性 */ }, [ /* 子节点 */ ]);
      }
    }

h()

h()(tag, props?, children?),用于创建虚拟DOM节点VNode。

  • tagstring | Component | AsyncComponent,表示元素或组件类型。

    • string:HTML标签名,如 'div'
    • Component:Vue 组件定义,如导入的 .vue 文件。
    • AsyncComponent:通过 defineAsyncComponent 定义的组件。
  • props?Object | null,包含属性、事件监听器、DOM 属性等。

    • class:会自动合并的类名。
    • style:会自动合并的内联样式。
    • onXXX:事件监听器。
    • 其他:自定义 props 或原生 DOM 属性。
  • children?string | Array<VNode> | VNode,子节点内容,支持字符串、嵌套 h() 调用、插槽函数。

    • string:文本节点。
    • Array<VNode>:多个子节点嵌套VNode。
    • VNode:单个子节点。
  • 返回:

  • nodeVNode,虚拟DOM节点对象,用于渲染真实DOM。

  • 示例:

    js
    import { h } from 'vue'
    
    // 创建一个带点击事件的按钮
    const button = h('button', {
      class: 'btn',
      onClick: () => console.log('Clicked!')
    }, '点击我')
    js
    // 动态切换组件类型
    const container = defineComponent({
        setup() {
            const isVertical = ref(true);
            return () => h(
                isVertical.value ? 'div' : 'section',  // 动态标签名
                { class: 'container' },
                [
                    h('h1', '标题'),
                    h('p', { onClick: () => isVertical.value = !isVertical.value }, '切换布局')
                ]
            );
        }
    });
    js
    // 带具名插槽的组件
    const layout = h(
        MyLayout,
        { title: '页面标题' },
        { 
            default: () => h('p', '主内容区'), 
            footer: () => h('div', '底部信息') 
        }
    );
  • 特性:

  • TS

    ts
    // 完整参数签名
    function h(
      type: string | Component,
      props?: object | null,
      children?: Children | Slot | Slots
    ): VNode
    
    // 省略 props
    function h(type: string | Component, children?: Children | Slot): VNode
    
    type Children = string | number | boolean | VNode | null | Children[]
    
    type Slot = () => Children
    
    type Slots = { [name: string]: Slot }
  • 选项式

    js
    // Vue 2/3 选项式写法
    export default {
        render() {
            return this.$createElement('div', { 
                class: 'wrapper' 
            }, [
                this.$createElement('span', '选项式 API 渲染')
            ]);
        }
    }
    
    // Vue3 统一使用 h()
    export default {
        render() {
            return h('div', { class: 'wrapper' }, [
                h('span', '统一渲染函数')
            ]);
        }
    }
  • SSR:SSR 中需使用 @vue/server-rendererrenderToString 处理 VNode。

  • Props特殊处理:class/style 支持对象/数组格式,事件监听器需以 on 前缀开头,如 onClick

    js
    h('button', {
        class: ['btn', { active: isActive }],
        onClick: handleClick
    }, '提交')
  • Key管理:列表渲染时必须提供唯一key。

    js
    h('ul', null, 
        items.map(item => h('li', { key: item.id }, item.text))
    )
  • 插槽传递:通过函数形式传递作用域插槽。

    • 默认插槽
    js
    h(MyComponent, null, {
        default: () => [h('p', '内容1'), h('p', '内容2')]
    })
    • 具名插槽
    js
    h(MyComponent, null, {
        header: (props) => h('h1', props.title)
    })
  • 性能优化:避免在频繁更新的组件中创建复杂嵌套结构,可缓存静态VNode。

    js
    const staticNode = h('div', '静态内容'); // 提前创建
    
    setup() {
        return () => h('div', [staticNode, h('p', dynamicContent)]);
    }
  • JSX集成:需配置 Babel 插件支持 JSX 语法,自动转换为 h() 调用。

    js
    const jsxNode = <div class="box">
      {
            items.map(item => 
                <span key={item.id}>{item.text}</span>
            )
      }    
    </div>

状态选项

data

data() => ({}),返回组件响应式数据的对象。

  • 返回:

  • 响应式对象Object,返回的对象会被 Vue 转换为响应式代理,基于 Proxy(Vue 3)或 Object.defineProperty(Vue 2),属性变化会触发视图更新。

    • 直接修改属性值(如 this.key = newValue)可触发响应式更新。
    • 新增未在 data 中声明的属性默认非响应式(需通过 Vue.set() 或重新赋值对象解决,Vue 2)。
  • 示例:

    js
    const app = Vue.createApp({
      data() {
        return {
          message: 'Hello Vue!',
          counter: 0,
          user: { name: 'Alice', age: 28 }
        };
      },
    })
    js
    // 动态数据初始化
    export default {
      data() {
        // 根据外部条件或函数初始化数据
        const initialData = this.fetchInitialData();
        return {
          items: initialData.items,
          isLoading: false
        };
      },
      methods: {
        fetchInitialData() {
          // 模拟同步数据获取(实际中可能为异步)
          return { items: ['A', 'B', 'C'] };
        }
      }
    };
  • 特性:

  • 必须为函数:若直接使用普通对象,多个组件实例会共享同一数据对象,导致状态污染。

  • 用途:初始化数据,在函数中定义组件初始状态,所有属性会自动变为响应式。

props

propsstring[] | Object,声明从父组件接收的属性。可以是一个数组、一个对象,或者一个函数。

  • string[]:数组中的每个元素表示一个需要传递的数据的 prop 名称。

  • Object{Record<string,{type,required?,default?,validator?}>},对象类型可以进行类型检查。类型检查包含以下配置项:

    • type:指定prop的数据类型:StringNumberBooleanArrayObjectDateFunctionSymbol、任何自定义构造函数、或上述内容组成的数组。
    • required?boolean默认:false,指定prop是否必须传递。
    • default?string | ()=>[] | ()=>({}),指定prop的默认值。对于对象、数组类型的prop,default是一个返回数组或对象的函数
    • validator?(prop)=>Boolean|Error,通过函数进行更复杂的验证。接收一个props对象参数,并返回一个布尔值或错误信息。
  • 语法:

    • 数组类型
    js
    props: ['title', 'content']
    • 对象类型
    ts
    props: {
      title: {
        type: String,
        required: true
      },
      age: {
        type: Number,
        default: 18
      }
    }
    • 验证函数
    ts
    props: {
      customProp: {
        validator(value) {
          // 自定义校验逻辑,返回 true 或 error 信息
          if (value < 0) {
            return "Value must be greater than 0";
          }
          return true;
        }
      }
    }
  • 特性:

    • 响应式:props是响应式的。如果父组件传递的prop的值发生变化,子组件中的prop值会自动更新。

    • 不可修改:子组件不能直接修改props。

    • 命名约定:props的命名在JS中通常遵循 camelCase,但在HTML/模板中props通常会转换为 kebab-case。

    • v-model:v-model本质上是对 props 的一种特殊用法。默认绑定的是 value,父组件通过v-model向子组件传递数据,子组件通过this.$emit('input', newValue)向父组件同步更新数据。

    • 模版使用:在子组件的模板中,可以直接通过{ { propName } }来访问传入的 props。

computed

computed{ Getter|{Getter,Setter?},...},用于声明依赖于其他数据的计算属性。

  • Getter()=>ComputedValue默认,函数形式,当计算属性只需读取值时,可直接使用函数。

  • Setter?(newValue)=>void,对象形式,当需要设置计算属性时,需提供 set 方法。

  • 返回:

  • valueComputedValue,返回计算后的属性值。

  • 语法:

    • 函数形式
    js
    computed: {
      fullName() {
        return `${this.firstName} ${this.lastName}`;
      }
    }
    • 对象形式
    js
    computed: {
      fullName: {
        get() {
            return `${this.firstName} ${this.lastName}`;
        },
        set(newValue) {
            const [firstName, lastName] = newValue.split(' ');
            this.firstName = firstName;
            this.lastName = lastName || '';
        }
      }
    }
  • 特性

    • 缓存机制:计算属性基于依赖的响应式数据缓存结果。依赖不变时直接返回缓存值。
    • 性能优化:适合将耗时计算移至 computed 中,利用缓存优化性能。
    • 依赖追踪:自动追踪data、props或其他computed中的响应式依赖。避免在计算属性中操作非响应式数据。
    • 无副作用:计算属性应是纯函数,不要在内部执行异步操作或修改外部状态。
  • 注意事项

    • 避免箭头函数:确保 this 指向组件实例。

      js
      // 错误写法(箭头函数无正确 this)
      fullName: () => this.firstName + this.lastName

methods

methods{Record<string,Function>},用于定义组件的方法,通常用于事件处理、逻辑操作或触发副作用(如 API 请求)。所有方法会被混入组件实例,可通过 this 直接调用。

  • 返回:

  • 灵活返回any,方法可返回任意值(或不返回),返回值不会被缓存(与 computed 不同)。

  • 示例:

    html
    <!-- 常用用法:事件处理 -->
    <template>
      <button @click="increment">点击增加</button>
      <p>计数:{{ count }}</p>
    </template>
    
    <script>
    export default {
      data() {
        return { count: 0 };
      },
      methods: {
        increment() {
          this.count++;
        }
      }
    };
    </script>
    html
    <!-- 进阶用法:方法参数与事件修饰符 -->
    <template>
      <!-- 1. 传递参数 -->
    +  <button @click="logInfo('按钮A', $event)">按钮A</button>
      
      <!-- 2. 事件修饰符 -->
    +  <form @submit.prevent="handleSubmit">
        <input type="text" v-model="inputText">
        <button type="submit">提交</button>
      </form>
    </template>
    
    <script>
    export default {
      data() {
        return { inputText: '' };
      },
      methods: {
        logInfo(buttonName, event) {
          console.log(`点击了${buttonName}`, event.target);
        },
          
        handleSubmit() {
          if (this.inputText.trim()) {
    +        this.submitData(this.inputText);
          }
        },
        submitData(text) {
          console.log('提交内容:', text);
          // 模拟 API 请求
        }
      }
    };
    </script>
  • 特性:

  • TS:使用defineComponent()进行类型推断。

    ts
    import { defineComponent } from 'vue';
    export default defineComponent({
      methods: {
        // 明确参数和返回值类型
        add(num1: number, num2: number): number {
          return num1 + num2;
        }
      }
    });
  • 组合式

    • export default

      js
      import { ref } from 'vue';
      export default {
        setup() {
          const count = ref(0);
          // 1. 定义方法
          const increment = () => count.value++;
            
          // 2. 直接暴露为普通函数
          return { count, increment }; 
        }
      };
    • setup

      html
      <script setup>
        const count = ref(0);
        // 1. 定义方法
        const increment = () => count.value++;
      
        // 2. 不需要暴露,直接在模版中使用
      </scripts>
  • 避免箭头函数:会导致 this 指向错误。

    js
    // ❌ 错误(this 指向错误)
    methods: { increment: () => this.count++ } 
    
    // ✅ 正确
    methods: { increment() { this.count++ } }
  • 模板中的方法调用

    • 无参:直接绑定方法名:@click="handleClick"
    • 传递参数:@click="handleClick(arg1, $event)"
    • 避免在模板中直接执行复杂逻辑(如 { { formatDate() } }),应优先使用 computed

watch

watch{attr,...},监听响应式数据的变化,并在变化时执行自定义逻辑,如异步操作、复杂逻辑、副作用。

  • attr函数形式|对象形式,监听的响应式属性。

    • 函数形式(newValue, oldValue)=>void,直接监听一个属性attr,参数为 newVal 和 oldVal。
    • 对象形式{handler,deep,immediate},通过对象配置更复杂的监听行为
      • handler(newValue)=>void,变化时执行的函数。
      • deepboolean默认:false,是否深度监听对象/数组内部值的变动。
      • immediateboolean默认:false,是否立即触发一次 handler,初始值时执行。
  • 语法:

    • 函数形式

      js
      watch: {
        count(newVal, oldVal) {
          console.log('计数变化:', newVal);
        }
      }
    • 对象形式

      js
      watch: {
        user: {
          handler(newVal) {
            console.log('用户信息变化:', newVal);
          },
          deep: true, // 监听 user 对象的所有嵌套属性
          immediate: true // 立即触发一次 handler,初始值时执行
        }
      }
    • 监听嵌套属性:使用字符串路径监听对象内部的属性。

      js
      watch: {
        'user.name'(newName) {
          console.log('新用户名:', newName);
        }
      }
    • 异步操作:在 handler 中执行异步任务。

      js
      watch: {
        searchQuery(newQuery) {
          // 异步任务:防抖处理
          setTimeout(() => {
            this.fetchResults(newQuery);
          }, 300);
        }
      }
    • 手动停止监听器:在组件内创建的监听器通常会自动销毁。若需手动停止监听,如动态添加的监听器:

      js
      const unwatch = this.$watch('count', (newVal) => {
        console.log('count changed:', newVal);
      });
      
      // 停止监听
      unwatch();
  • 注意事项

    • 避免无限循环:如果在 handler 中修改监听的数据,需添加条件判断防止递归触发。

      js
      watch: {
        count(newVal) {
          if (newVal < 10) {
            this.count = newVal + 1; // 可能导致循环
          }
        }
      }
    • 明确监听目标:deep: true 或监听大型对象时可能影响性能,尽量明确监听具体属性。

    • 对比computed

      • watch:监听变化,执行副作用,如请求、DOM 操作。
      • computed:衍生数据,依赖其他值计算得到,适合模板渲染。

emits

emits数组形式|对象形式,显式声明组件可以触发的自定义事件,用于子组件向父组件通信。

  • 数组形式string[],仅列出事件名称,无参数验证。

  • 对象形式{eventName,...}

    • eventName(payload)=>boolean | null,事件名。
  • 语法:

    • 声明事件

      • 数组形式:仅列出事件名称,无参数验证。
      js
        // 1. 数组形式(仅声明事件名称,无验证)
        emits: ['submit', 'cancel']
      • 对象形式:为事件添加验证函数,验证事件参数的合法性。
      js
      // 2. 对象形式(带参数验证)
      emits: {
        // 事件名: 验证函数
        submit: (payload) => {
          // 返回 true 表示验证通过,false 会触发警告(开发环境)
          return payload.email && payload.password;
        },
      
        // 无参数的简单事件
        cancel: null // 直接设置为 null 表示无需验证参数
      }
    • 触发事件:在组件方法中通过 this.$emit(eventName, ...args) 触发事件。

      js
      methods: {
        sendData() {
          this.$emit('submit', { data: 'test' });
        }
      }
    • 父组件监听事件:父组件通过 v-on/@ 监听子组件触发的事件。

      html
      <ChildComponent @submit="handleSubmit" />
  • 注意事项

    • 验证函数的限制

      • 验证函数中无法访问组件实例,this 为 undefined。
      • 仅用于开发阶段的参数校验,生产环境不执行。
    • 对比props

      • emits:子组件向父组件传递事件。
      • props:父组件向子组件传递数据。
    • 统一命名规范:统一使用 kebab-case 命名事件。

  • 示例:子组件向父组件传递事件

    • 子组件
    js
    export default {
      // 1. 声明自定义事件
      emits: {
        // 验证提交事件参数
        submit: (payload) => payload.name && payload.age > 0
      },
      methods: {
        triggerSubmit() {
          // 2. 触发事件
          this.$emit('submit', { name: 'John', age: 30 });
        }
      }
    }
    • 父组件
    html
    <template>
      <ChildComponent @submit="handleSubmit" /> <!-- 3. 父组件监听子组件触发的事件 -->
    </template>
    
    <script>
    export default {
      methods: {
        handleSubmit(payload) {
          console.log('收到数据:', payload);
        }
      }
    }
    </script>

expose@vue3

exposestring[],用于显式声明组件对外暴露的公共属性或方法,通过模板引用访问时,只能访问暴露的内容。

  • 语法:

    • 暴露属性和方法:在数组中列出需要暴露的属性和方法名。

      js
        // 声明要暴露的属性或方法名(数组形式)
        expose: ['getData', 'resetForm'],
    • 父组件访问暴露的内容:父组件通过模板引用ref访问子组件暴露的公共接口。

      html
      <!-- 父组件模板 -->
      <template>
        <ChildComponent ref="childRef" />
        <button @click="callChildMethod">调用子组件方法</button>
      </template>
      
      <script>
      export default {
        methods: {
          callChildMethod() {
            // 仅能访问 expose 中声明的属性和方法
            this.$refs.childRef.publicMethod();
            console.log(this.$refs.childRef.publicProperty); // 正常访问
      
            // 以下代码会报错(未暴露的属性和方法不可访问)
            // this.$refs.childRef.privateMethod();
            // console.log(this.$refs.childRef.privateData);
          }
        }
      }
      </script>
    • 组合式API

      js
      import { defineExpose } from 'vue';
      
      export default {
        setup() {
          const internalState = ref('隐藏数据');
          const publicMethod = () => { /* ... */ };
      
          // 组合式 API 的暴露方式
          defineExpose({
            publicMethod
          });
      
          return { internalState, publicMethod };
        }
      };
  • 注意事项

    • Vue版本要求:expose 是 Vue 3+ 新增的功能,Vue 2 不支持。
    • 作用范围:expose 不会影响通过 $emit$props 的通信,仅控制模板引用ref的访问
    • 默认行为
      • 未配置expose:父组件通过 ref 可以访问子组件实例的所有属性和方法。

      • 配置expose:仅暴露明确声明的属性和方法,其他内容被屏蔽。

  • 示例:父组件通过ref访问子组件暴露的方法/属性

    • 子组件:
    js
    export default {
      expose: ['getMessage'], // 1. 只暴露 getMessage 方法
    
      data() {
        return {
          message: 'Hello from child!',
          secret: 'Confidential'
        };
      },
    
      methods: {
        getMessage() {
          return this.message;
        },
        internalLog() {
          console.log('内部方法');
        }
      }
    };
    • 父组件:
    html
    <template>
      <ChildComponent ref="child" /> <!-- 2. 绑定ref -->
    </template>
    
    <script>
    export default {
      mounted() {
        // 可以访问暴露的 getMessage
        console.log(this.$refs.child.getMessage()); // 输出: "Hello from child!"
    
        // 以下访问会报错(未暴露的属性和方法)
        // console.log(this.$refs.child.secret);
        // this.$refs.child.internalLog();
      }
    }
    </script>

生命周期钩子

beforeCreate

beforeCreate()=>void,注册一个钩子,在Vue实例初始化之后立即调用。

  • 特性:

    • 此时实例的 data、methods、computed 等选项尚未初始化。
    • 无法通过 this 访问组件的数据和方法,会返回undefined。
  • 常见用途:

    • 初始化非响应式变量:直接赋值给 this,但不会触发视图更新。
    • 执行全局配置:如设置请求拦截器、第三方库初始化。
    • 在插件开发中注入早期逻辑:需早于组件状态初始化。
  • 注意事项:

    • 避免操作响应式数据:修改 data 中的属性可能无效或被后续初始化覆盖。
    • 慎用异步操作:若需异步获取数据,建议在 created 或 mounted 中进行。
  • 示例:

    js
    export default {
      beforeCreate() {
        console.log('beforeCreate 钩子被调用');
          
        console.log(this.message); // 1. 输出:undefined(data 未初始化)
          
        console.log(this.sayHello); // 2. 输出:undefined(methods 未初始化)
       
        this.nonReactiveData = '初始值'; // 3. 可执行非响应式初始化(例如非 data 属性)
      },
      data() {
        return {
          message: 'Hello Vue!'
        };
      },
      methods: {
        sayHello() {
          console.log(this.message);
        }
      }
    }

created

created()=>void,注册一个钩子,在Vue实例完成数据观测(data、props初始化)后,但尚未挂载DOM时调用。

  • 特性:

    • 初始化完成:data、methods、computed 等选项已初始化完成。

    • this访问:可以通过 this 访问组件的数据和方法。

    • 无法操作DOM:此时模板尚未渲染成 HTML,this.$el 为 undefined。

    • SSR:在服务端渲染中,created 是唯一会被调用的生命周期钩子,因为无 DOM 环境。

  • 常见用途:

    • 数据初始化:调用 API 获取初始数据并赋值给 data。

    • 事件监听:添加全局事件,如 window.addEventListener

    • 依赖数据的逻辑:根据 propsdata 执行计算或配置。

    • 第三方库初始化:初始化不依赖 DOM 的库,如图表库、状态管理。

  • 注意事项:

    • 避免操作DOM:需操作 DOM 时,应使用 mounted 钩子。

    • 异步操作处理:若需等待异步数据,可结合 v-if 或加载状态控制渲染。

    • 响应式数据安全:可直接修改 data 中的数据,Vue 会自动触发更新。

  • 示例:

    js
    export default {
      data() {
        return {
          message: 'Hello Vue!',
          userData: null
        };
      },
      created() {
        console.log('created 钩子被调用');
        console.log(this.message); // 1. 输出:'Hello Vue!'
        console.log(this.$el); // 2. 输出:undefined(DOM 未挂载)
        this.fetchUserData(); // 3. 发起异步请求(如初始化数据)
      },
      methods: {
        async fetchUserData() {
          const response = await fetch('https://api.example.com/user');
          this.userData = await response.json();
        }
      }
    }

beforeMount

beforeMount()=>void,类似 onBeforeMount(),注册一个钩子,在组件被挂载之前被调用。

mounted@

mounted()=>void,类似 onMounted(),注册一个钩子,在组件挂载完成后执行。

beforeUpdate

beforeUpdate()=>void,类似 onBeforeUpdate(),注册一个钩子,在组件即将因为响应式状态变更而更新其 DOM 树之前调用。

updated

updated()=>void,类似 onUpdated(),注册一个钩子,在组件因为响应式状态变更而更新其 DOM 树之后调用。

beforeUnmount

beforeUnmount()=>void,类似 onBeforeUnmount(),注册一个钩子,在组件实例被卸载之前调用。

unmounted

unmounted()=>void,类似 onUnmounted(),注册一个钩子,在组件实例被卸载之后调用。

activated

activated()=>void,类似 onActivated(),注册一个钩子,若组件实例是<KeepAlive>缓存树的一部分,当组件被插入到DOM中时调用。

deactivated

deactivated()=>void,类似 onDeactivated(),注册一个钩子,若组件实例是<KeepAlive>缓存树的一部分,当组件从DOM中被移除时调用。

组合选项

provide

provide(),类似 provide(),提供一个值,可以被后代组件注入。

inject

inject(),类似 inject(),注入一个由祖先组件或整个应用通过app.provide()提供的值。

mixins

mixins(),类似 app.mixin(),用来全局注册一个混入对象。会将该对象的所有组件选项(data,methods,...)合并到每个组件中。

extends

extendscpn,用于组件继承的选项,允许一个组件基于另一个组件进行扩展,复用其选项并覆盖或增强特定逻辑。

  • cpnComponent,接受一个组件选项对象或组件构造函数,作为当前组件的基类。

  • 示例:

    • 基组件:BaseComponent.js
    js
    export default {
      data() {
        return { baseData: '基类数据' };
      },
      methods: {
        baseMethod() { console.log('基类方法'); }
      },
      mounted() { console.log('基类 mounted'); }
    };
    • 派生组件:
    js
    // 派生组件 DerivedComponent.js
    import BaseComponent from './BaseComponent';
    
    export default {
      extends: BaseComponent, // 1. 继承基组件
      data() {
        return { derivedData: '派生类数据' };
      },
      methods: {
        derivedMethod() { console.log('派生类方法'); },
        // 2. 覆盖基类方法
        baseMethod() { 
          console.log('派生类重写的基类方法');
          // 调用基类方法(类似 super.baseMethod())
          this.$options.extends.methods.baseMethod.call(this);
        }
      },
      mounted() {
        console.log('派生类 mounted');
        // 3. 调用基类生命周期钩子
        this.$options.extends.mounted.call(this);
      }
    };
  • 特性:

  • 选项合并规则:Vue 会按特定策略合并基组件和派生组件的选项。

    • data:属性浅合并,属性相同时,派生组件覆盖基组件。

      js
      // 基组件 data
      data() { return { a: 1, b: 2 }; }
      
      // 派生组件 data
      data() { return { b: 3, c: 4 }; }
      
      // 合并后 data
      { a: 1, b: 3, c: 4 }
    • 生命周期钩子:将基类和派生类的钩子函数合并为数组,依次执行,基类钩子先执行。

      js
      mounted() {
        // 基类钩子先执行
        // 派生类钩子后执行
      }
    • 其他选项:同名属性覆盖,基类选项被派生类取代。

      js
      // 基组件 methods
      methods: { a() {} }
      
      // 派生组件 methods
      methods: { a() {}, b() {} }
      
      // 合并后 methods
      { a() { /* 派生类方法 */ }, b() {} }
  • 对比mixins:extends仅继承单个组件,而mixins可以混入多个组件。

  • 访问基类选项:通过 this.$options.extends 访问基类选项,但需注意上下文绑定。

    js
    // 调用基类方法
    this.$options.extends.methods.baseMethod.call(this);

其他

name

namestring,用于显式声明组件的名称。主要用于调试、递归组件、动态组件、缓存控制。

  • 语法:

    • 声明组件名称:推荐使用 PascalCase,与文件名一致。

      js
      export default {
        name: 'TodoItem', // 推荐使用 PascalCase(与文件名一致)
        // 其他选项...
      }
    • 递归组件:组件通过 name 在自身模板中调用自己。

      html
      <!-- TodoList.vue -->
      <template>
        <div>
          <h3>{{ title }}</h3>
          <!-- 2. 递归调用自身 -->
          <TodoList v-if="hasChildren" :items="children" />
        </div>
      </template>
      
      <script>
      export default {
        name: 'TodoList', // 1. 必须声明 name 才能递归
        props: ['items'],
        computed: {
          hasChildren() {
            return this.items && this.items.length > 0;
          }
        }
      }
      </script>
    • 动态组件中使用:通过 name 切换组件。

      html
      <template>
        <component :is="currentComponent"></component> <!-- 2. 动态组件中使用 -->
      </template>
      
      <script>
      import ComponentA from './ComponentA.vue';
      import ComponentB from './ComponentB.vue';
      
      export default {
        data() {
          return {
            currentComponent: 'ComponentA' // 1. 根据 name 匹配组件
          };
        },
        components: {
          ComponentA,
          ComponentB
        }
      }
      </script>
    • KeepAlive缓存控制:通过 name 指定缓存目标。

      html
      <keep-alive :include="['UserProfile', 'Settings']"> <!-- 指定包括的组件名 -->
        <component :is="currentTab"></component>
      </keep-alive>
    • 调试标识:在Vue DevTools组件树中显示 name 值,便于调试。

    • 自动推断规则:若未显式声明 name,Vue 会尝试从以下位置获取名称:

      • 组件的文件名。
      • 全局或局部注册时的名称。
  • 注意事项

    • 唯一性建议:确保组件名称唯一,避免命名冲突。
  • 示例:在父组件中局部注册定义的组件

    • 组件定义:
    js
    export default {
      name: 'TodoItem', // 1. 显式声明名称
      props: ['text'],
      template: `
        <li>{{ text }}</li>
      `
    }
    • 父组件使用:
    html
    <template>
      <ul>
        <!-- 3. 模版中使用局部注册过的组件 -->
        <todo-item v-for="item in list" :text="item.text" :key="item.id" />
      </ul>
    </template>
    
    <script>
    import TodoItem from './TodoItem.vue';
    
    export default {
      // 2. 局部注册(模板中使用 kebab-case)
      components: {
        TodoItem 
      },
      data() {
        return {
          list: [{ id: 1, text: 'Learn Vue' }]
        };
      }
    }
    </script>

components

components{name: cpn,...},用于在当前组件中局部注册子组件,使得这些子组件可以在当前组件的模板中使用。

  • namestring,组件名称,模板中使用时的标签名。

  • cpnComponent | {data,...},组件对象,导入的组件或直接定义。

  • 语法:

    • 注册/使用组件

      • 注册组件:在 components 对象中注册组件。
      js
      import ChildComponent from './ChildComponent.vue';
      import ButtonCounter from './ButtonCounter.vue';
      
      export default {
        components: {
          'CustomButton': ButtonCounter, // 1. 完整写法
          ChildComponent // 2. ES6简写(等价于ComponentB: ComponentB)
        }
      };
      • 模板中使用:在模板中直接使用注册的组件标签。
      html
      <template>
        <div>
          <!-- 使用 kebab-case 或 PascalCase -->
          <child-component />
          <custom-button />
        </div>
      </template>
    • 动态组件:结合 <component :is="..."> 动态切换组件。

      html
      <template>
        <component :is="currentComponent"></component>
      </template>
      
      <script>
      import CompA from './CompA.vue';
      import CompB from './CompB.vue';
      
      export default {
        data() {
          return { currentComponent: 'CompA' };
        },
        components: { CompA, CompB }
      };
      </script>
    • 递归组件:在组件内部注册自身实现递归,需配合 name 属性。

      js
      // TreeItem.vue
      export default {
        name: 'TreeItem', // 1. 必须声明 name
        components: {
          TreeItem: () => import('./TreeItem.vue') // 2. 异步递归
        },
        props: ['item']
      };
    • 异步组件:通过动态导入实现按需加载,需配合打包工具如 Webpack。

      js
      export default {
        components: {
          AsyncComponent: () => import('./AsyncComponent.vue')
        }
      };
  • 注意事项:

    • 注册名规则:推荐PascalCase,注册时使用 ComponentName,模板中可用 <component-name><ComponentName>

    • 对比全局组件

      • 优先级:局部组件 > 全局组件。
      • 作用域
        • 局部注册的组件仅在当前组件及其子组件中可用。
        • 全局组件可在任何地方使用。
      • 优势:局部注册可以减少全局命名污染,按需加载组件,优化性能。
    • 命名一致性:保持文件名、组件注册名和模板标签名一致,如 UserList.vue → 注册为 UserList → 模板用 <user-list>

    • 按需加载:对非首屏必需的组件使用异步注册 () => import(...)

  • 示例:局部注册并使用组件

    • 子组件
    html
    <template>
      <button @click="count++">点击次数: {{ count }}</button>
    </template>
    
    <script>
    export default {
      data() {
        return { count: 0 };
      }
    };
    </script>
    • 父组件
    html
    1<template>
      <div>
        <h1>组件示例</h1>
        <button-counter /> <!-- 3. 使用子组件 -->
      </div>
    </template>
    
    <script>
    // 1. 导入子组件
    import ButtonCounter from './ButtonCounter.vue';
    
    export default {
      // 2. 局部注册组件
      components: {
        ButtonCounter
      }
    };
    </script>

derectives

derectives(),类似 app.directive(),用于注册或获取全局自定义指令。

组件实例

$data

$data(),类似 data,返回组件响应式数据的对象。返回组件响应式数据的对象。

$props

$props(),类似 props,声明从父组件接收的属性。可以是一个数组、一个对象,或者一个函数。

$el

$el,Vue实例的内置属性,指向该实例关联的根DOM元素。在组件中对应组件模板的根元素

  • 特性:

    • 执行时机

      • mounted 阶段$el变为可用的DOM元素。
      • beforeCreate 和 created 阶段$el 为 undefined,因为DOM 尚未挂载。
    • SSR:在 SSR 中,$el 在服务端为 undefined,仅在客户端 mounted 后可用。

    • 对比ref

      特性this.$elthis.$refs.xxx
      指向目标组件根元素模板中通过 ref="xxx" 标记的元素
      访问时机仅在 mounted 后可用mounted 后可用
      多根元素组件指向第一个根元素,Vue3 支持 Fragments可精确访问特定元素
  • 主要用途:

    • 直接操作DOM:在需要手动修改元素样式、属性或调用 DOM API 时使用。

      js
      mounted() {
        this.$el.style.backgroundColor = "#f0f0f0";
      }
    • 集成第三方库:初始化依赖 DOM 的库,如图表库、地图库。

      js
      mounted() {
        this.chart = new Chart(this.$el, { /* 配置 */ });
      }
    • 获取组件根元素:用于计算尺寸、位置或与其他 DOM 元素交互。

      js
      mounted() {
        const rect = this.$el.getBoundingClientRect();
        console.log("组件宽度:", rect.width);
      }
  • 注意事项:

    • 动态模板的限制:如果组件没有模板,如函数式组件或 render 函数生成内容,开发环境下$el可能指向注释节点。
    • 不要滥用:优先使用 Vue 的响应式数据或 ref 属性操作 DOM,直接操作 $el 可能导致状态与数据不同步。
  • 示例:$el的访问时机

    js
    export default {
      template: `
        <div id="app">
          <h1>{{ message }}</h1>
        </div>
      `,
      data() {
        return {
          message: "Hello Vue!"
        };
      },
      created() {
        console.log("created:", this.$el); // 1. 输出:undefined
      },
      mounted() {
        console.log("mounted:", this.$el); // 2. 输出:<div id="app">...</div>
        console.log(this.$el.querySelector("h1")); // 3. 输出:<h1>Hello Vue!</h1>
      }
    }

$options

$options,Vue实例的内置属性,用于访问组件定义的 原始选项对象

  • 返回:

  • optionsObject,返回组件定义的原始选项对象。

  • 语法:

    • 包含:它包含组件初始化时传入的所有配置选项 data、methods、created 等,以及通过 mixin 或 extends 合并的选项。

    • 访问方式

      js
      export default {
        data() {
          return { message: "Hello" };
        },
        created() {
          console.log(this.$options); // 输出完整的组件选项对象
          console.log(this.$options.data()); // 输出 { message: "Hello" }
        }
      }
  • 特性:

    • 静态数据存储:在 $options 中定义的属性不会成为响应式数据,适合存储常量或配置。

    • 静态配置访问:通过 $options 可以获取组件定义的原始配置,未经过 Vue 内部处理。

      js
      export default {
        apiBaseUrl: "https://api.example.com", // 静态配置
        methods: {
          fetchData() {
            const url = this.$options.apiBaseUrl + "/users";
            // 发起请求...
          }
        }
      }
    • 生命周期钩子:生命周期函数在 $options 中以数组形式存储,支持通过遍历调用。

      js
      created() {
        this.$options.created.forEach(hook => hook.call(this));
      }
  • 常见用途:

    • 调试组件选项

      js
      mounted() {
        // 打印所有选项(包括 mixin 合并后的结果)
        console.log("组件选项:", this.$options);
      }
    • 动态调用生命周期钩子

      js
      // 在插件中扩展组件逻辑
      const plugin = {
        install(Vue) {
          Vue.mixin({
            created() {
              if (this.$options.customCreated) {
                this.$options.customCreated.call(this);
              }
            }
          });
        }
      };
  • 注意事项:

    • 非响应式属性:$options 中的属性不会触发视图更新,修改它们通常无意义,Vue 内部已处理过选项。

    • 避免滥用:多数情况下应通过 props、data 或 computed 管理数据,而非依赖 $options。

    • 自定义选项规范:自定义选项如apiBaseUrl建议通过 Vue.config.optionMergeStrategies 定义合并策略。

      js
      // 定义合并策略:将 customOption 连接为数组
      Vue.config.optionMergeStrategies.customOption = (parentVal, childVal) => {
        return parentVal ? parentVal.concat(childVal) : [childVal];
      };
      
      // 使用
      const mixin = { customOption: "mixin-value" };
      const Child = {
        mixins: [mixin],
        customOption: "child-value",
        created() {
          console.log(this.$options.customOption); // 输出 ["mixin-value", "child-value"]
        }
      };
  • 示例:

    • 结合自定义选项实现功能开关
    js
    export default {
      featureA: true, // 启用功能 A
      featureB: false, // 禁用功能 B
      created() {
        if (this.$options.featureA) {
          this.initFeatureA();
        }
      },
      methods: {
        initFeatureA() { /* ... */ }
      }
    }
    • 访问原始 data 函数
    js
    export default {
      data() {
        return { count: 0 };
      },
      created() {
        const rawData = this.$options.data(); // { count: 0 }
        console.log("原始 data 返回值:", rawData);
      }
    }

$slots

$slotsslot,Vue实例的内置属性,用于访问组件接收的 插槽内容

  • 返回:

  • slot{name: vnode},返回一个对象。

    • namestring,键为插槽名称。
    • vnodeVNode,值为对应的 VNode 数组,虚拟节点。
  • 语法:

    • 模板中定义插槽

      html
      <!-- 父组件使用子组件 -->
      <ChildComponent>
        <template #default>默认内容</template>
        <template #header>标题</template>
      </ChildComponent>
    • 访问插槽内容

      js
      export default {
        mounted() {
          // 访问默认插槽
          const defaultSlot = this.$slots.default;
          // 访问具名插槽 "header"
          const headerSlot = this.$slots.header;
        }
      }
  • 特性:

    • 插槽类型
      • 默认插槽this.$slots.default
      • 具名插槽this.$slots.<name>,如 this.$slots.header
      • 作用域插槽
        • Vue2:通过 $scopedSlots 访问。
        • Vue3:已合并到 $slots
    • VNode结构:插槽内容是虚拟节点数组,可用于自定义渲染逻辑,如手动控制布局或条件渲染。
  • 常见用途:

    • 条件渲染插槽内容

      js
      export default {
        render(h) {
          return h('div', [
            this.$slots.header || '默认标题', // 存在 header 插槽则渲染,否则显示默认
            this.$slots.default
          ]);
        }
      }
    • 在渲染函数中手动分发插槽

      js
      export default {
        render(h) {
          return h('div', [
            h('div', { class: 'header' }, this.$slots.header),
            h('div', { class: 'content' }, this.$slots.default)
          ]);
        }
      }
    • 动态包装插槽内容

      js
      export default {
        render(h) {
          // 为每个插槽内容包裹一个 div
          const wrappedSlots = Object.keys(this.$slots).map(name => {
            return h('div', { class: 'wrapper' }, this.$slots[name]);
          });
          return h('div', wrappedSlots);
        }
      }
  • 注意事项:

    • 仅用于渲染函数
      • 在模板中使用 <slot> 标签即可自动分发内容,无需直接操作 $slots。
      • $slots 主要用于 自定义渲染函数 或复杂逻辑处理。
    • 避免直接修改:插槽内容是只读的,直接修改可能导致渲染错误。
    • 异步组件限制:若父组件异步加载,子组件的 $slots 可能在初始渲染时为空。
  • 示例:

    • 检查插槽是否存在
    js
    export default {
      render(h) {
        return h('div', [
          this.$slots.header ? h('h1', this.$slots.header) : h('h1', '默认标题'),
          this.$slots.default
        ]);
      }
    }
    • 传递插槽内容到子组件@
    js
    // 高阶组件(WrapperComponent.vue)
    export default {
      render(h) {
        return h('ChildComponent', {
          scopedSlots: {
            // 将父级插槽传递给子组件
            default: props => this.$slots.default(props)
          }
        });
      }
    }

$refs

$refs,Vue 实例的内置属性,用于直接访问模板中通过 ref 属性标记的 DOM元素子组件实例

  • 返回:

  • refsObject,返回一个包含所有注册 ref 的对象。

  • 语法:

    • 模板中定义ref

      html
      <template>
        <!-- 标记 DOM 元素 -->
        <input ref="inputRef" type="text">
        
        <!-- 标记子组件 -->
        <ChildComponent ref="childComp" />
      </template>
    • 访问 $refs

      js
      export default {
        methods: {
          focusInput() {
            this.$refs.inputRef.focus(); // 操作 DOM 元素
            this.$refs.childComp.someMethod(); // 调用子组件方法
          }
        }
      }
  • 特性:

    • 动态绑定:ref 属性可以结合 v-bind动态赋值。

      html
      <div :ref="dynamicRefName"></div>
    • 响应式限制:$refs 不是响应式的,避免在模板或计算属性中依赖它。

    • 生命周期可用性:$refs 在组件 mounted 之后才被填充,在 created 或 beforeMount 中访问为 undefined。

    • 对比Vue3组合式API

      特性选项式API (this.$refs)组合式API (ref())
      定义方式模板中静态或动态绑定 ref使用 ref() 函数声明变量
      访问时机仅在 mounted 后可用onMounted 生命周期后可用
      模板绑定<div ref="divRef"></div><div ref="divRef"></div>
      访问方式this.$refs.divRefdivRef.value
  • 常见用途:

    • 操作DOM元素

      html
      <template>
        <input ref="emailInput" type="email">
        <button @click="focusEmailInput">聚焦输入框</button>
      </template>
      
      <script>
      export default {
        methods: {
          focusEmailInput() {
            this.$refs.emailInput.focus();
          }
        }
      }
      </script>
    • 调用子组件方法或访问数据

      html
      <template>
        <Counter ref="counterRef" />
      </template>
      
      <script>
      export default {
        mounted() {
          this.$refs.counterRef.increment(); // 调用子组件方法
          console.log(this.$refs.counterRef.count); // 访问子组件数据
        }
      }
      </script>
  • 注意事项:

    • 唯一性:同一模板中重复的 ref 名称会覆盖,后者生效。
    • 动态组件与条件渲染:若元素/组件被 v-if 隐藏或动态切换,对应的 $refs 可能变为 null。
    • 避免滥用:优先通过 Props/Events 实现父子通信,直接操作子组件可能破坏封装性。
    • 异步更新:在 Vue 更新 DOM 后(如 nextTick)再访问 $refs,确保引用最新。
    • 命名规范:使用清晰的 ref 名称,如 formRef、chartInstanceRef。
    • 防御性检查:访问前检查是否存在,如 if (this.$refs.xxx)
  • 示例:

    • 访问动态生成的组件
    html
    <template>
      <component :is="currentComponent" ref="dynamicComp"></component>
    </template>
    
    <script>
    export default {
      data() {
        return { currentComponent: 'LoginForm' };
      },
      mounted() {
        if (this.$refs.dynamicComp) {
          this.$refs.dynamicComp.validate();
        }
      }
    }
    </script>
    • 结合 nextTick 确保 DOM 更新
    js
    export default {
      methods: {
        updateAndFocus() {
          this.someData = '新值';
          this.$nextTick(() => {
            this.$refs.inputRef.focus();
          });
        }
      }
    }

$parent

$parent,Vue实例的内置属性,用于直接访问当前组件的 父组件实例

  • 返回:

  • parentComponent,返回父组件实例。

  • 语法:

    • 访问方式:通过 this.$parent 访问,可以操作父组件的数据、方法或生命周期钩子。

      js
      export default {
        methods: {
          callParentMethod() {
            this.$parent.someParentMethod(); // 调用父组件方法
            console.log(this.$parent.parentData); // 访问父组件数据
          }
        }
      }
  • 特性:

    • 层级关系

      • $parent 指向当前组件的直接父组件实例
      • 若组件被嵌套多层,需通过链式访问 this.$parent.$parent访问更上层组件,不推荐,破坏封装性。
    • 生命周期可用性

      • 在 beforeCreate 阶段,父组件实例尚未完全初始化,this.$parent 可能为 undefined。
      • created 阶段开始,父组件实例已可用。
    • 动态性:若父组件被v-if动态替换,$parent 会自动更新指向新的父组件。

    • 组合式API:在 setup() 函数中无法直接访问 this.$parent,需通过 getCurrentInstance() 获取。

      js
      import { getCurrentInstance } from 'vue';
      
      export default {
        setup() {
          const instance = getCurrentInstance();
          const parent = instance?.parent?.proxy; // 访问父组件实例
          return { parent };
        }
      }
  • 常见用途:

    • 直接调用父组件方法

      html
      <!-- 子组件 Child.vue -->
      <template>
        <button @click="notifyParent">通知父组件</button>
      </template>
      
      <script>
      export default {
        methods: {
          notifyParent() {
            this.$parent.handleChildEvent(); // 触发父组件方法
          }
        }
      }
      </script>
      
      <!-- 父组件 Parent.vue -->
      <script>
      export default {
        methods: {
          handleChildEvent() {
            console.log("子组件触发了父组件方法");
          }
        }
      }
      </script>
    • 访问父组件数据

      js
      // 子组件中
      export default {
        mounted() {
          console.log("父组件的数据:", this.$parent.sharedData);
        }
      }
    • 跨层级通信(慎用)

      js
      // 子组件访问祖父组件(不推荐)
      export default {
        methods: {
          callGrandparent() {
            this.$parent.$parent.grandparentMethod();
          }
        }
      }
  • 注意事项:

    • 破坏组件封装性:直接操作父组件状态可能导致代码难以维护,优先通过 Props/Events 或 Vuex/Pinia 管理通信。
    • 耦合性风险:父组件结构变化时,依赖 $parent 的子组件可能失效。
    • 无法访问根实例:访问根实例时,应使用 this.$root 属性。
  • 示例:

    • 通过 $parent 实现简单表单验证
    js
    1// 子组件(表单输入项)
    export default {
      methods: {
        validate() {
          if (!this.value) {
            this.$parent.addError("字段不能为空"); // 调用父组件方法记录错误
          }
        }
      }
    }
    
    // 父组件(表单容器)
    export default {
      methods: {
        addError(message) {
          this.errors.push(message);
        }
      }
    }

$watch()

$watch()(),类似 watch,监听响应式数据的变化,并在变化时执行自定义逻辑,如异步操作、复杂逻辑、副作用。

$emit()

$emit()(),类似 emits,显式声明组件可以触发的自定义事件,用于子组件向父组件通信。

$forceUpdate()

$forceUpdate()(),Vue实例的内置方法,用于 强制触发当前组件及其子组件的重新渲染的应急手段

  • 特性:

    • 手动触发更新:即使数据未发生响应式变化,也会强制组件重新渲染。
    • 生命周期钩子:调用 $forceUpdate() 会触发 beforeUpdate 和 updated 生命周期钩子。
  • 使用场景:在以下场景中,当 Vue 的响应式系统无法自动检测到数据变化时使用

    • 直接修改对象/数组的某个属性或索引值:未通过 Vue.setthis.$set

      js
      export default {
        data() {
          return { list: ['A', 'B', 'C'] };
        },
        methods: {
          updateItemDirectly() {
            this.list[1] = 'X'; // ❌ 非响应式修改
            this.$forceUpdate(); // 强制更新视图
          }
        }
      }
    • 依赖非响应式数据:如通过 Object.freeze() 冻结的对象或普通对象。

      js
      export default {
        data() {
          return { frozenData: Object.freeze({ key: '初始值' }) };
        },
        methods: {
          updateFrozenData() {
            this.frozenData.key = '新值'; // ❌ 修改无效
            this.$forceUpdate(); // 强制渲染
          }
        }
      }
    • 使用第三方库的不可变数据:如 Immutable.js。

  • 注意事项:

    • 性能影响:频繁调用 $forceUpdate() 会导致不必要的渲染,应优先通过 响应式数据 驱动视图更新。

    • 替代方案

      • 使用 Vue.set / this.$set 修改对象或数组。

      • 替换整个对象/数组,触发响应式更新(推荐)。

        js
        this.list = [...this.list]; // 通过新数组触发更新
      • 依赖响应式数据,将外部数据包装为响应式

        js
        data() {
          return { externalData: Vue.observable({ value: 0 }) };
        }
    • 不适用于Props:无法通过 $forceUpdate() 强制父组件更新传递的 Props,需通过父组件自身状态变更。

  • 示例:强制更新嵌套组件

    js
    export default {
      methods: {
        refreshChild() {
          this.$children.forEach(child => {
            child.$forceUpdate(); // 强制更新所有子组件
          });
        }
      }
    }

$nextTick()

$nextTick()(),类似 nextTick(),通常用来在下一次 DOM 更新刷新后执行某些操作。