画布 1
Vue3官方文档学习1.0
  开始
    简介
      读音
        Vue
        发音为 /vjuː/，类似 view
      核心功能
        声明式渲染
          Vue 基于标准 HTML 拓展了一套模板语法，使得我们可以声明式地描述最终输出的 HTML 和 JavaScript 状态之间的关系。
        响应性
          ：Vue 会自动跟踪 JavaScript 状态并在其发生变化时响应式地更新 DOM。
      定义
        是一个框架，也是一个生态
        使用场景
          无需构建步骤，渐进式增强静态的 HTML
          在任何页面中作为 Web Components 嵌入
          单页应用 (SPA)
          全栈 / 服务端渲染 (SSR)
          Jamstack / 静态站点生成 (SSG)
          开发桌面端、移动端、WebGL，甚至是命令行终端中的界面
      特点
        .vue结尾文件
          单文件组件
          表现
            template
            script
            style
        API风格
          Options API（Vue2）
            对于初学者更友好
            面向对象语言背景的用户
            渐进增强的应用场景
              支持多种设备和网络环境
              提高Web应用程序的可访问性
              优化应用程序的性能
              支持跨平台和跨浏览器的兼容性
          composition API（setup语法）
            用 Vue 构建完整的单页应用
    快速上手
      环境相关
        线上
          演练场
            快速体验 Vue
          JSFiddle
            不用任何构建的原始 HTML
          StackBlitz
            比较熟悉 Node.js 和构建工具等概念
        线下
          16.0 或更高版本的 Node.js
      创建Vue应用
        npm init vue@latest
          基于create-vue
          规则：npm init会自动查找 create-包名
      构建
        npm run build
        原理
          vite
      IDE推荐
        VSCode+Volar扩展（Vue官方）
        工具链
      使用Vue的几种方式
        CDN
          unpkg
            <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
          jsdelivr
          cdnjs
        版本
          global
            <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
          es
            <script type="module">
            import { createApp } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js'
        import maps
          <script type="importmap">
          浏览器要求caniuse
  基础
    模板语法
      Vue支持JSX
        缺点：不享受模板同级别的编译优化
      语法
        双大括号
        示例：<span>Message: {{ msg }}</span>
      原始HTML
        指令：v-html
        示例：
<p>Using text interpolation: {{ rawHtml }}</p>
<p>Using v-html directive: <span v-html="rawHtml"></span></p>
        缺点：XSS 漏洞
      属性绑定
        指令：v-bind
        示例：
<div v-bind:id="dynamicId"></div>
          简写：
<div :id="dynamicId"></div>
      JavaScript表达式
        {{ number + 1 }}
        {{ ok ? 'YES' : 'NO' }}
        {{ message.split('').reverse().join('') }}
        <div :id="`list-${id}`"></div>
        调用函数
          {{ formatDate(date) }}
        不支持的情况
          {{ if (ok) { return message } }}
        有限的全局变量
          Infinity
          undefined
          NaN
          isFinite
          isNaN
          parseFloat
          parseInt
          decodeURI
          decodeURIComponent
          encodeURI
          encodeURIComponent
          Math
          Number
          Date
          Array
          Object
          Boolean
          String
          RegExp
          Map
          Set
          JSON
          Intl
          BigInt
      指令
        内置指令
          v-text
          v-html
          v-show
          v-if
          v-else
          v-else-if
          v-for
          v-on
          v-bind
          v-model
          v-slot
          v-pre
          v-once
          v-memo
          v-cloak
        指令参数
          属性
            <a v-bind:href="url"> ... </a>
            <!-- 简写 -->
<a :href="url"> ... </a>
          事件
            <a v-on:click="doSomething"> ... </a>
            <!-- 简写 -->
<a @click="doSomething"> ... </a>
          进阶
            动态参数
              属性
                <a v-bind:[attributeName]="url"> ... </a>
                <!-- 简写 -->
<a :[attributeName]="url"> ... </a>
              事件
                <a v-on:[eventName]="doSomething"> ... </a>
                <!-- 简写 -->
<a @[eventName]="doSomething">
              注意
                动态参数必须是字符串
                不支持在其中使用拼接语法
                  <a :['foo' + bar]="value"> ... </a>不支持
              推荐计算属性代替复杂表达式
            修饰符Modifiers
              .prevent
                示例
                  <form @submit.prevent="onSubmit">...</form>
                等价：event.preventDefault()
          指令语法
    响应式基础
      响应式对象其实是JavaScript Proxy
      Options API
        属性
          data
            响应对象
              监听的变量
            注意
              禁止使用$与_为前缀
                Vue 在组件实例上暴露的内置 API 使用 $ 作为前缀
                内部属性保留 _ 前缀
              只在data中的变量才会是响应式
          methods
            vue自动为methods中的方法绑定永远指向实例的this
            你不应该在定义 methods 时使用箭头函数，因为箭头函数没有自己的 this 上下文
          生命周期
            mounted
            其他
      Setup API
        属性
          ref
            响应对象
              接收任意类型
            用法
              template中
                无需.value
              script中
                .value
                  取值
                  改变
              实验性用法
                $ref
                  不需要.value取值
          reactive
            声明响应式状态
            局限性
              仅对对象类型有效
                对象
                数组
                Set
                Map
                无效的类型
                  string
                  number
                  boolean
              不能随意地替换一个响应对象
                导致初始引用响应性连接丢失
                错误的示例
                  let state = reactive({ count: 0 })
                  失去响应特性
                    重新赋值
                      state = reactive({ count: 1 })
                    使用本地变量
                      let n = state.count
n++不会影响count
                    使用解构写法
                      let { count } = state
同样 count++不是响应式
                    函数调用
                      callSomeFunction(state.count)
直接使用count的值，失去响应
          注意
            ref在template中会自动解包
            ref在reactive响应式对象中，如同普通属性
            ref嵌套对象
              不能直接在template中直接使用ref嵌套对象，需要解构
                const object = { foo: ref(1) }
这样会错误
{{ object.foo + 1 }}
              如果是在reactive中使用数组，则需要使用.value取值
        写法
          script setup语法糖
          setup() {}
            需要在return的位置把变量与方法进行return
        DOM更新时机
          当你更改响应式状态后，DOM 会自动更新
          nextTick
            作用：用于访问更新之后的DOM
            import { nextTick } from 'vue'
function increment() {
state.count++
nextTick(() => {
// 访问更新后的 DOM
})
}
        深层响应性
          特性
            默认深层响应
              浅响应对象shallowReactive
            对象
            数组
        响应式对象响应式代理 vs. 原始对象
          代理对象是响应式的
            返回一个Proxy
          代理对象嵌套返回代理对象
            依旧是代理
    计算属性
      应用场景
        依赖关系的响应式数据
      特点
        计算属性值会基于其响应式依赖被缓存
          所以不要在计算属性中像下面，使用 Date.now()
computed: {
now() {
return Date.now()
}
}
      修改
        默认不让修改，修改会有警告
        修改方法
          get()
          set(value)
      最佳实践
        不要在 getter 中做异步请求或者更改 DOM
        避免直接修改计算属性值
      Options API
        computed() {
计算属性() { return xxx}
}
      Setup API
        import {computed} from 'vue'
计算属性 = computed(() => {})
    Class 与 Style 绑定
      应用场景
        操纵元素的 CSS class 列表和内联样式
      使用属性绑定
        class
          绑定
            变量
              示例：<div :class="{ active: isActive }"></div>
            对象
              const classObject = reactive({
active: true,
'text-danger': false
})
              <div :class="classObject"></div>
            数组
              常规
                <div :class="[activeClass, errorClass]"></div>
              三元运算
                <div :class="[isActive ? activeClass : '', errorClass]"></div>
              嵌套
                <div :class="[{ active: isActive }, errorClass]"></div>
          使用
            自定义组件
            HTML标签
        style
          绑定
            对象
              <div :style="{ 'font-size': fontSize + 'px' }"></div>
            数组
              <div :style="[baseStyles, overridingStyles]"></div>
          样式多值
            <div :style="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }"></div>
          特点：自动前缀
    条件渲染
      指令
        v-if
        v-else
        v-else-if
        <template> 上的 v-if
        v-show
      v-if vs. v-show
        v-if
          不渲染dom
          false时初次也不会渲染
          v-if 有更高的切换开销
        v-show
          渲染dom
          false时初次也会渲染
          v-show 有更高的初始渲染开销
            如果需要频繁切换，则使用 v-show 较好
      v-if 和 v-for警告
        同时使用 v-if 和 v-for 是不推荐的
        v-if 和 v-for 同时存在于一个元素上的时候，v-if 会首先被执行
    列表渲染
      指令
        v-for
      遍历内容
        数组
          <li v-for="(item, index) in items">
        对象
          <li v-for="(value, key) in myObject">
        值
          <span v-for="n in 10">{{ n }}</span>
            从1开始
      用法
        必须在使用v-for的元素上使用key属性
          以便它可以跟踪每个节点的标识，从而重用和重新排序现有的元素
          性能考虑
        进阶用法
          template不渲染，也可以使用v-for
          自定义组件上使用v-for
        数组变化侦测
          方法
            push()
            pop()
            shift()
            unshift()
            splice()
            sort()
              注意
                在计算属性中使用 reverse() 和 sort() 的时候务必小心！这两个方法将变更原始数组
                正确的做法：[...numbers].sort()
            reverse()
              注意
                在计算属性中使用 reverse() 和 sort() 的时候务必小心！这两个方法将变更原始数组
                正确的做法：[...numbers].reverse()
          替换数组
    事件处理
      指令
        v-on
        简写：
@
      使用
        内联事件处理器
          示例
            count计数
              <button @click="count++">Add 1</button>
            toggle
              <button @click="value = !value">toggle</button>
          判断方法
            foo()、count++
        方法事件处理器
          判断方法
            foo、foo.bar、foo['bar']
      原生 DOM 事件$event
        写法一：
<button @click="func('submitted.', $event)">
        写法二：
<button @click="(event) => func(' submitted.', event)">
      事件修饰符
        .stop
          event.stopPropagation()
          停止传递
        .prevent
          event.preventDefault()
        .self
          执行顺序
            .prevent.self
              先prevent后self
                会阻止**元素及其子元素的所有点击事件的默认行为
            .self.prevent
              先self后prevent
                只会阻止对元素本身的点击事件的默认行为
          仅当 event.target 是元素本身时才会触发事件处理器
        .capture
          指向内部元素的事件，在被内部元素处理前，先被外部处理
        .once
          点击事件最多被触发一次
        .passive
          滚动事件的默认行为 (scrolling) 将立即发生而非等待 `onScroll` 完成
          作用
            可以用来改善移动端设备的滚屏性能
          注意
            请勿同时使用 .passive 和 .prevent
              .passive 已经向浏览器表明了你不想阻止事件的默认行为
              .prevent 会被忽略，并且浏览器会抛出警告
      按键修饰符
        用法
          <input @keyup.page-down="onPageDown" />
        按键别名
          .enter
          .tab
          .delete (捕获“Delete”和“Backspace”两个按键)
          .esc
          .space
          .up
          .down
          .left
          .right
        系统按键修饰符
          .ctrl
          .alt
          .shift
          .meta
            在 Mac 键盘上，meta 是 Command 键 (⌘)。
在 Windows 键盘上，meta 键是 Windows 键 (⊞)
            其他
              在 Sun 微机系统键盘上，meta 是钻石键 (◆)
              在某些键盘上，特别是 MIT 和 Lisp 机器的键盘及其后代版本的键盘，如 Knight 键盘，space-cadet 键盘，meta 都被标记为“META”。
              在 Symbolics 键盘上，meta 也被标识为“META”或“Meta”。
          触发逻辑
            系统按键修饰符和常规按键不同。与 keyup 事件一起使用时，该按键必须在事件发出时处于按下状态
            keyup.ctrl 只会在你仍然按住 ctrl 但松开了另一个键时被触发
            单独松开 ctrl 键将不会触发。
        .exact 修饰符
          作用
            控制触发一个事件所需的确定组合的系统按键修饰符
          用法
            <button @click.ctrl="onClick">A</button>
              当按下 Ctrl 时，即使同时按下 Alt 或 Shift 也会触发
            <button @click.ctrl.exact="onCtrlClick">A</button>
              仅当按下 Ctrl 且未按任何其他键时才会触发
            <button @click.exact="onClick">A</button>
              仅当没有按下任何系统按键时触发
        鼠标按键修饰符
          .left
          .right
          .middle
    表单输入绑定v-model
      绑定元素
        input
          绑定 value property 并侦听 input 事件
            文本类型
          绑定 checked property 并侦听 change 事件
            type="radio"
            type="checkbox"
        textare
          绑定 value property 并侦听 input 事件
        select
          绑定 value property 并侦听 change 事件
      用法
        文本
        多行文本
        复选框
          绑定对象为数组
          所有input绑定相同的对象
        单选
        选择器
          绑定在select元素上
          默认
            初始值不匹配，<select> 元素会渲染成一个“未选择”的状态
              可以禁用空值
            建议：
<option disabled value="">Please select one</option>
          也可以绑定数组对象
      值绑定value
        可设置元素
          radio
          checkbox
          select
        复选框独有属性
          true-value
          false-value
          应用场景
            登录表单“记住我”
            true-value 和 false-value attributes 不会影响 value attribute
              浏览器在表单提交时，并不会包含未选择的复选框
        注意
          v-model 会忽略任何表单元素上初始的 value、checked 或 selected attribute
      修饰符
        .lazy
          为在每次 change 事件后更新数据
        .number
          输入自动转换为数字
        .trim
          默认自动去除用户输入内容中两端的空格
      进阶：在组件上使用
    生命周期
      Setup API
        onMounted()
          在组件挂载完成后执行。
          触发时机
            其所有同步子组件都已经被挂载
              (不包含异步组件或 <Suspense> 树内的组件)
            其自身的 DOM 树已经创建完成并插入了父容器中。
              仅当根容器在文档中时，才可以保证组件 DOM 树也在文档中。
          注意
            这个钩子在服务器端渲染期间不会被调用
        onUpdated()
          在组件因为响应式状态变更而更新其 DOM 树之后调用。
          注意
            父组件的更新钩子将在其子组件的更新钩子之后调用
            不要在 updated 钩子中更改组件的状态，这可能会导致无限的更新循环！
            如果你需要在某个特定的状态更改后访问更新后的 DOM，请使用 nextTick() 作为替代
            这个钩子在服务器端渲染期间不会被调用
        onUnmounted()
          在组件实例被卸载之后调用。
          触发时机
            其所有子组件都已经被卸载。
            所有相关的响应式作用 (渲染作用以及 setup() 时创建的计算属性和侦听器) 都已经停止。
          作用
            计时器、DOM 事件监听器或者与服务器的连接
          注意
            这个钩子在服务器端渲染期间不会被调用
        onBeforeMount()
          在组件被挂载之前被调用。
          触发时机
            当这个钩子被调用时，组件已经完成了其响应式状态的设置，但还没有创建 DOM 节点。
            它即将首次执行 DOM 渲染过程。
          注意
            这个钩子在服务器端渲染期间不会被调用
        onBeforeUpdate()
          在组件即将因为响应式状态变更而更新其 DOM 树之前调用。
          作用
            这个钩子可以用来在 Vue 更新 DOM 之前访问 DOM 状态。
          注意
            这个钩子在服务器端渲染期间不会被调用
        onBeforeUnmount()
          在组件实例被卸载之前调用。
          注意
            当这个钩子被调用时，组件实例依然还保有全部的功能。
            这个钩子在服务器端渲染期间不会被调用
        onErrorCaptured()
          在捕获了后代组件传递的错误时调用。
          错误来源
            组件渲染
            事件处理器
            生命周期钩子
            setup() 函数
            侦听器
            自定义指令钩子
            过渡钩子
          类型
            type ErrorCapturedHook = (
err: unknown,
instance: ComponentPublicInstance | null,
info: string
) => boolean | void
              可以通过返回 false 来阻止错误继续向上传递
            参数
              错误对象
              触发该错误的组件实例
              说明错误来源类型的信息字符串
          注意
            以在 errorCaptured() 中更改组件状态来为用户显示一个错误状态
            不要让错误状态再次渲染导致本次错误的内容，否则组件会陷入无限循环
          工作原理
            默认情况下，所有的错误都会被发送到应用级的 app.config.errorHandler
              前提是这个函数已经定义
              同时捕获 errorCaptured 钩子本身抛出的错误
            调用顺序
              从底至上，依次调用
              类似于原生 DOM 事件的冒泡机制
            通过返回 false 来阻止错误继续向上传递
              阻止其他的 errorCaptured 钩子或 app.config.errorHandler
        onRenderTracked()
          当组件渲染过程中追踪到响应式依赖时调用
          注意
            开发模式使用
            这个钩子在服务器端渲染期间不会被调用
        onRenderTriggered()
          当响应式依赖的变更触发了组件渲染时调用
          注意
            开发模式使用
            这个钩子在服务器端渲染期间不会被调用
        onActivated()
          若组件实例是 <KeepAlive> 缓存树的一部分，当组件被插入到 DOM 中时调用。
          注意
            这个钩子在服务器端渲染期间不会被调用
        onDeactivated()
          若组件实例是 <KeepAlive> 缓存树的一部分，当组件从 DOM 中被移除时调用。
          注意
            这个钩子在服务器端渲染期间不会被调用
        onServerPrefetch()
          在组件实例在服务器上被渲染之前调用。
          注意
            SSR ONLY
          工作原理
            如果这个钩子返回了一个 Promise，服务端渲染会在渲染该组件前等待该 Promise 完成
            这个钩子仅会在服务端渲染中执行，可以用于执行一些仅存在于服务端的数据抓取过程。
      Options API
        beforeCreate
          在组件实例初始化完成之后立即调用
          注意
            会在实例初始化完成、props 解析之后、data() 和 computed 等选项处理之前立即调用
            beforeCreate() 会在setup()之后调用
        created
          在组件实例处理完所有与状态相关的选项后调用。
          触发时机
            响应式数据、计算属性、方法和侦听器设置完成之后
            挂载阶段还未开始
              $el属性仍不可用
        beforeMount
          在组件被挂载之前调用。
          触发时机
            当这个钩子被调用时，组件已经完成了其响应式状态的设置
            还没有创建 DOM 节点
            即将首次执行 DOM 渲染过程
          注意
            这个钩子在服务器端渲染期间不会被调用
        mounted
          在组件被挂载之后调用
            比onMounted晚
          触发时机
            其所有同步子组件都已经被挂载
              (不包含异步组件或 <Suspense> 树内的组件)
            其自身的 DOM 树已经创建完成并插入了父容器中。
              仅当根容器在文档中时，才可以保证组件 DOM 树也在文档中。
          注意
            这个钩子在服务器端渲染期间不会被调用
          作用
            访问组件所渲染的 DOM 树相关的副作用
            服务端渲染：确保代码在客户端使用
        beforeUpdate
          在组件即将因为一个响应式状态变更而更新其 DOM 树之前调用。
            在其中更改状态也是安全的
            比onBeforeUpdate晚
          注意
            这个钩子在服务器端渲染期间不会被调用
          作用
            可以用来在 Vue 更新 DOM 之前访问 DOM 状态
        updated
          在组件因为一个响应式状态变更而更新其 DOM 树之后调用。
          注意
            父组件的更新钩子将在其子组件的更新钩子之后调用
            不要在 updated 钩子中更改组件的状态，这可能会导致无限的更新循环！
            如果你需要在某个特定的状态更改后访问更新后的 DOM，请使用 nextTick() 作为替代
            这个钩子在服务器端渲染期间不会被调用
        beforeUnmount
          在一个组件实例被卸载之前调用。
          作用
            当这个钩子被调用时，组件实例依然还保有全部的功能。
          注意
            这个钩子在服务器端渲染期间不会被调用
        unmounted
          在一个组件实例被卸载之后调用。
          触发时机
            其所有子组件都已经被卸载。
            所有相关的响应式作用 (渲染作用以及 setup() 时创建的计算属性和侦听器) 都已经停止。
          作用
            计时器、DOM 事件监听器或者与服务器的连接
          注意
            这个钩子在服务器端渲染期间不会被调用
        errorCaptured
          在捕获了后代组件传递的错误时调用。
        renderTracked
          在一个响应式依赖被组件的渲染作用追踪后调用。
          同setup API
        renderTriggered
          在一个响应式依赖被组件触发了重新渲染之后调用。
          同setup API
        activated
          若组件实例是 <KeepAlive> 缓存树的一部分，当组件被插入到 DOM 中时调用。
          同setup API
        deactivated
          若组件实例是 <KeepAlive> 缓存树的一部分，当组件从 DOM 中被移除时调用。
          同setup API
        serverPrefetch
          当组件实例在服务器上被渲染之前要完成的异步函数。
          同setup API
      说明
        组合式 API 中的 setup() 钩子会在所有选项式 API 钩子之前调用
          注意这里说的是setup()函数本身
    侦听器
      在状态变化时执行一些“副作用”
      Options API vs Setup API
        watch属性
        watch函数
          watch
            入参类型
              ref
              reactive
                默认成为深层侦听器
              getter函数
              多个数据源的数组
            watch 只追踪明确侦听的数据源
            示例：
watch(source, callback, {
flush: 'post'
})
          watchEffect
            自动跟踪回调的响应式依赖
              更智能
            应用场景
              对于多个依赖项的场景
            示例：
watchEffect(callback, {
flush: 'post'
})
      用法
        深层侦听器
          侦听所有嵌套的变更
          deep: true
          注意
            watch 默认是浅层的：被侦听的属性，仅在被赋新值时，才会触发回调函数
            当用于大型数据结构时，开销很大
        即时回调的侦听器
          建侦听器时，立即执行一遍回
          immediate: true
          注意
            watch 默认是懒执行的：仅当数据源变化时
            发生在 created 钩子之前
            处理了 data、computed 和 methods 选项
        回调触发时间
          侦听器回调中能访问被 Vue 更新之后的 DOM
          flush: 'post'
          注意
            更改了响应式状态，它可能会同时触发 Vue 组件更新和侦听器回调
            默认情况下，用户创建的侦听器回调，都会在 Vue 组件更新之前被调用
        thiw.$watch
          $watch() 方法来命令式地创建一个侦听器
        停止侦听器
          Setup API
            调用watchEffect返回的函数
          Options API
            watch 选项或者 $watch() 实例方法声明的侦听器，会在宿主组件卸载时自动停止。因此，在大多数场景下，你无需关心怎么停止它。
            在少数情况下，你的确需要在组件卸载之前就停止一个侦听器，这时可以调用 $watch() API 返回的函数
    模板引用
      ref 是一个特殊的 attribute，用于直接访问底层的DOM元素
        示例：<input ref="input">
      访问模板引用
        Options API
          this.$refs
        Setup API
          申明 ref，使用.value的方式访问
          初次渲染时会是 null
      v-for
        ref 数组并不保证与源数组相同的顺序
        注意
          v3.2.25及以上版本
      函数模板引用
        示例
          <input :ref="(el) => { /* 将 el 赋值给一个数据属性或 ref 变量 */ }">
      组件上的 ref
        Options API
          被引用的组件实例和该子组件的 this 完全一致
            访问方法&控制属性
          但是，推荐使用：标准的 props 和 emit 接口来实现父子组件交互
        Setup API
          <script setup> 的组件是默认私有的
            父组件无法访问到一个使用了 <script setup> 的子组件中的任何东西
          除非子组件在其中通过 defineExpose 宏显式暴露
            会自动解包
            非Proxy对象
    组件基础
      使用组件
        命名
          .vue
        组成
          template
          script
          style
            非必须
        Options API
          import 组件
          components属性中定义
            这一步是注册
          template中使用
        Setup API
          import 组件
          直接在template中使用
      传递props
        Options API
          props属性
        Setup API
          defineProps
      监听事件
        Options API
          $emit触发事件
            在template中
<button @click="$emit('enlarge-text')">Enlarge text</button>
        Setup API
          $emit触发事件
            在template中
<button @click="$emit('enlarge-text')">Enlarge text</button>
          defineEmits
            const emit = defineEmits(['enlarge-text'])
emit('enlarge-text')
      通过插槽来分配内容
        <slot />占位符
        父组件传递进来的内容就会渲染在这里
      动态组件
        <component :is=“组件名” />
        is值
          被注册的组件名
          导入的组件对象
        注意
          多个组件间作切换时，被切换掉的组件会被卸载
          通过 <KeepAlive> 组件强制被切换掉的组件仍然保持“存活”的状态
            虚拟DOM的概念
      DOM 模板解析注意事项
        场景
          单文件组件
          内联模板字符串 (例如 template: '...')
          <script type="text/x-template">
        大小写区分
          HTML 标签和属性名称是不分大小写的
          建议：全部小写加短横线
        闭合标签
          在 DOM 模板中，我们必须显式地写出关闭标签
        元素位置限制
          某些 HTML 元素对于放在其中的元素类型有限制
          例如
            ul
            ol
            table
              <table>
<tr is="vue:blog-post-row"></tr>
</table>
            select
  深入组件
    注册
      全局注册
        全局可用
        app.component
          支持链式
          支持传入组件名
        缺点
          体积问题：全局注册，但并没有被使用的组件无法在生产打包时被自动移除 (也叫“tree-shaking”)
          全局注册在大型项目中使项目的依赖关系变得不那么明确
      局部注册
        Options API
          import 组件
          components属性中定义
        Setup API
          script setup
          import组件即可
        注意
          局部注册的组件在后代组件中并不可用
      组件名格式
        在整个指引中，我们都使用 PascalCase 作为组件名的注册格式
          <PascalCase />
          缺点
            PascalCase 的标签名在 DOM 模板中是不可用的
          vue中设计
            Vue 支持将模板中使用 kebab-case 的标签解析为使用 PascalCase 注册的组件
            等价
              <MyComponent>
              <my-component>
        原因
          合法的 JavaScript 标识符
          使得在 JavaScript 中导入和注册组件都很容易
          IDE 也能提供较好的自动补全
          更明显地表明了这是一个 Vue 组件
            而不是原生 HTML 元素
          将 Vue 组件和自定义元素 (web components) 区分
    props
      定义
        字符型
          props: ['foo']
          defineProps(['foo'])
        对象型
          props: {
title: String,
likes: Number
}
          defineProps({
title: String,
likes: Number
})
      传递细节
        props名字格式
          支持camelCase
            greetingMessage: String
            template中直接使用
          在组件的props传递中，建议改写成kebab-case
            短横线
            greeting-message="hello"
        静态 vs. 动态 Prop
          静态是直接绑定
          动态根据一个更复杂表达式的值动态传入
            :title="post.title + ' by ' + post.author.name"
        传递不同的值类型
          Number
            :likes="42"
              数字
            likes="42"
              字符
          Boolean
            <BlogPost :is-published="false" />
              这是正确的
            <BlogPost is-published />
              隐式转换为 `true
            动态传入
              <BlogPost :is-published="post.isPublished" />
          Array
            <BlogPost :comment-ids="[234, 266, 273]" />
            <BlogPost :comment-ids="post.commentIds" />
          Object
            <BlogPost
:author="{
name: 'Veronica',
company: 'Veridian Dynamics'
}"
/>
          共同特点
            使用冒号: + 属性名的方式传递
        使用一个对象绑定多个 prop
          使用 v-bind 而非 :prop-name
          两种方式
            v-bind="$attrs"
            v-bind="把所有属性放在一个对象中"
      单向数据流
        单向绑定原则
          props 因父组件的更新而变化
          子组件不允许直接修改props
          特殊：对象的情况
        需要修改的场景
          定义局部state
          使用事件逆向传递方法
            emit
          props传递修改props的方法进来
        更改对象 / 数组类型的 props
          因为 JavaScript 的对象和数组是按引用传递
          允许了子组件以某种不明显的方式影响父组件的状态
          很大的性能损耗，比较得不偿失
      prop校验
        类型支持
          类型检查
            propA: Number
          多种可能的类型
            propB: [String, Number]
          必传
            required属性
              true
            propC: {
type: String,
required: true
}
          默认值
            设置default
            propD: {
type: Number,
default: 100
}
          对象
            propE: {
type: Object,
// 对象或者数组应当用工厂函数返回。
// 工厂函数会收到组件所接收的原始 props
// 作为参数
default(rawProps) {
return { message: 'hello' }
}
}
          函数类型
            propG: {
type: Function,
// 不像对象或数组的默认，这不是一个工厂函数。这会是一个用来作为默认值的函数
default() {
return 'Default function'
}
}
        自定义校验
          propF: {
validator(value) {
return ['success', 'warning', 'danger'].includes(value)
}
}
        其他说明
          所有 prop 默认都是可选的，除非声明了 required: true
          除 Boolean 外
            未传递的可选 prop 将会有一个默认值 undefined
            Boolean 类型的未传递 prop 将被转换为 false
          当 prop 的校验失败后，Vue 会抛出一个控制台警告 (在开发模式下)
          prop 的校验是在组件实例被创建之前
            data、computed 等
            在 default 或 validator 函数中不可用
        props类型标注
      Boolean 类型转换
        <MyComponent disabled />
          等价
            :disabled="true"
        <MyComponent />
          等价
            :disabled="false"
    组件事件
      触发与监听事件
        在组件的模板表达式中，可以直接使用 $emit 方法触发自定义事件
        Options API中
          methods中可以使用this.$emit
        事件支持修饰符
      事件参数
        可以通过emit方法的第二个参数传递
        所有传入 $emit() 的额外参数都会被直接传向监听器
          $emit('foo', 1, 2, 3) 触发后，监听器函数将会收到这三个参数值。
      声明触发的事件
        Options API
          emits属性
        Setup API
          配合 <script setup>
            defineEmits
          显示使用
            emits属性
              emits: ['inFocus', 'submit'],
            setup解构
              setup(props, { emit }) {
emit('submit')
}
      事件覆盖
        定义的emits事件比如click会覆盖原生组件的click
      事件校验
        原则上与props校验一致
        返回true校验通过，返回false校验失败
        emit的参数即为校验函数的入参
    组件v-model
      v-model 可以在组件上使用以实现双向绑定。
      案例
        input
          <input v-model="searchText" />
          工作原理
            :value绑定为searchText
            @input事件绑定为 searchText = $event.target.value
        CustomInput
          <CustomInput v-model="searchText" />
          工作原理
            :modelValue绑定为searchText
              这里的modelValue是默认的
            @update:modelValue事件绑定为 newValue => searchText = newVaule
      v-model 的参数
        默认
          modelValue 作为 prop
          update:modelValue 作为对应的事件
        自定义
          属性名
            <MyComponent v-model:title="bookTitle" />
            事件
              update:title
                更新父组件值
        多个 v-model 绑定
          处理逻辑与上面单个model一致
      v-model 修饰符
        .trim
        .number
        .lazy
        自定义修饰符
          v-model.customize
          modelModifiers
            API
              props: {
modelModifiers: { default: () => ({})}
}
              const props = defineProps({
modelValue: String,
modelModifiers: { default: () => ({}) }
})
            modelModifiers prop 包含了自定义的修饰符
    透传 Attributes
      获取透传Attributes
        模板中
          父组件传递
            v-bind
              没有参数的 v-bind 会将一个对象的所有属性都作为 attribute 应用到目标元素上
            v-on
          子组件取用
            $attrs
              属性
                $attrs.属性
                  透传 attributes 在 JavaScript 中保留了它们原始的大小写，所以像 foo-bar 这样的一个 attribute 需要通过 $attrs['foo-bar'] 来访问
              事件
                on+事件
                  像 @click 这样的一个 v-on 事件监听器将在此对象下被暴露为一个函数 $attrs.onClick
            准确取用，禁用继承
              inheritAttrs: false
        Setup语法中
          <script setup>
            useAttrs()
          setup({props, ctx})
            ctx.attrs
      继承
        定义
          “透传 attribute”指的是传递给一个组件，却没有被该组件声明为 props 或 emits 的 attribute 或者 v-on 事件监听器
        常见
          class
          style
          id
        场景
          对 class 和 style 的合并
            子组织合并父组件上的值
          v-on监听器的继承
            父组件上的事件会由子组件中的原先事件比如click触发之后，再触发父组件的click
            如果原生button自身也通过v-on绑定了事件监听器，则这个监听器和父组件的监听都会被触发
          深层组件继承
            场景：根节点上就只有一个节点
              有点HOC那意思？
            注意
              透传的 attribute 不会包含已申明的props或者emits
              透传的attribute符合声明，也可以作为props传入子组件
      禁用 Attributes 继承
        Options API
          inheritAttrs: false
        Setup API
          <script>
// 使用普通的 <script> 来声明选项
export default {
inheritAttrs: false
}
</script>
<script setup>
// ...setup 部分逻辑
</script>
      多根节点的 Attributes 继承
        多个根节点的组件没有自动 attribute 透传行为
        显示绑定
          v-bind="$attrs" 必须绑定到一个元素上去
      在 JavaScript 中访问透传 Attributes
    插槽Slots
      插槽内容与出口
        出口
          <slot />
        内容
          父组件包裹
            插槽内容可以是任意合法的模板内容
      渲染作用域
        插槽内容可以访问到父组件的数据作用域
        插槽内容无法访问子组件的数据
      默认内容
        在外部没有提供任何内容的情况下，可以为插槽指定默认内容
        通过设置slot
          <slot>默认内容</slot>
      具名插槽
        name="插槽名"
          <slot name="header"></slot>
        使用
          v-slot:插槽名
            <template v-slot:header>
<!-- header 插槽的内容放这里 -->
</template>
          简写
            #插槽名
              <template #header>
<!-- header 插槽的内容放这里 -->
</template>
      动态插槽名
        <template v-slot:[dynamicSlotName]>
...
</template>
        简写：
<template #[dynamicSlotName]>
...
</template>
      作用域插槽
        场景
          插槽的内容可能想要同时使用父组件域内和子组件域内的数据
            无渲染组件
            高级列表组件示例
        定义
          slot :属性="props"
          示例
            定义slot
              <slot :text="greetingMessage" :count="1"></slot>
            父组件使用slot
              <MyComponent v-slot="slotProps">
{{ slotProps.text }} {{ slotProps.count }}
</MyComponent>
        注意
          插槽数据不能跨插槽访问
            比如
              插槽header
                <slot name="header" message="hello"></slot>
              父组件访问其他插槽，无法访问到message
                <template>
<MyComponent v-slot="{ message }">
<p>{{ message }}</p>
<template #footer>
<!-- message 属于默认插槽，此处不可用 -->
<p>{{ message }}</p>
</template>
</MyComponent>
</template>
            注意template包裹的为独立作用域
          为默认插槽使用显式的 <template> 标签有助于更清晰地指出 属性 在其他插槽中不可用
    依赖注入
      父子组件跨组件传递
      Provide提供
        Setup API
          import { provide } from 'vue'
provide(/* 注入名 */ 'message', /* 值 */ 'hello!')
        Options API
          provide属性
          示例
            provide: {
message: 'hello!'
}
        应用层Provide
          类似于全局store
            插件时用
          app.provide(/* 注入名 */ 'message', /* 值 */ 'hello!')
      Inject注入
        Setup API
          import { inject } from 'vue'
const message = inject('message')
          默认值
            const value = inject('message', '这是默认值')
          别名
            from属性
            export default {
inject: {
/* 本地属性名 */ localMessage: {
from: /* 注入来源名 */ 'message'
}
}
}
        Options API
          inject属性
          示例
            inject: ['message']
      使用技巧
        使用 Symbol 作注入名
          使用 Symbol 来作为注入名以避免潜在的冲突
          场景：构建大型的应用，包含非常多的依赖提供
        和响应式数据配合使用
          保证注入方和供给方之间的响应性链接
            computed
            ref
          如果你想确保提供的数据不能被注入方的组件更改，你可以使用 readonly() 来包装提供的值
            单向数据流的概念
            场景：数组、对象这种引用类型
        示例
          export const myInjectionKey = Symbol()
          import { myInjectionKey } from './keys.js'
          const injected = inject(myInjectionKey)
    异步组件
      基本用法
        defineAsyncComponent
        注册
          局部注册
            OptionsAPI
              export default {
components: {
AdminPage: defineAsyncComponent(() =>
import('./components/AdminPageComponent.vue')
)
}
}
            Setup API
              const AsyncComp = defineAsyncComponent(() => {
return new Promise((resolve, reject) => {
// ...从服务器获取组件
resolve(/* 获取到的组件 */)
})
})
          全局组件
            app.component('MyComponent', defineAsyncComponent(() =>
import('./components/MyComponent.vue')
))
      加载与错误状态
        属性
          loader
            加载函数
          loadingComponent
            加载异步组件时使用的组件
          delay
            展示加载组件前的延迟时间
            默认为 200ms
          errorComponent
            加载失败后展示的组件
          timeout
            超时时间
            超时之后显示 报错组件
            默认是Infinity
        示例
          const AsyncComp = defineAsyncComponent({
// 加载函数
loader: () => import('./Foo.vue'),
// 加载异步组件时使用的组件
loadingComponent: LoadingComponent,
// 展示加载组件前的延迟时间，默认为 200ms
delay: 200,
// 加载失败后展示的组件
errorComponent: ErrorComponent,
// 如果提供了一个 timeout 时间限制，并超时了
// 也会显示这里配置的报错组件，默认值是：Infinity
timeout: 3000
})
      搭配Suspense使用
  逻辑复用
    组合式函数
      什么是“组合式函数”？
        组合式函数”(Composables) 是一个利用 Vue 的组合式 API 来封装和复用有状态逻辑的函数
        组合式函数名以“use”开头
      鼠标跟踪器示例
        useMouse
        使用
          const { x, y } = useMouse()
      异步状态示例
        useFetch
      约定和最佳实践
        命名
          组合式函数约定用驼峰命名法命名，并以“use”作为开头。
        输入参数
          尽管其响应性不依赖 ref，组合式函数仍可接收 ref 参数
          最好在处理输入参数时兼容 ref 而不只是原始的值
            方法一：使用unref
            方法二：在 watchEffect() 中调用 unref() 来进行正确的追踪
        返回值
          推荐组合式函数中使用 ref() 而不是 reactive()
            解构后依旧包含多个 ref 的普通的非响应式对象
          使用reactive包裹useXXX函数
            强制转换成响应式
        副作用
          如果你的应用用到了服务端渲染 (SSR)，请确保在组件挂载后才调用的生命周期钩子中执行 DOM 相关的副作用
          确保在 onUnmounted() 时清理副作用
            示例：
export function useEventListener(target, event, callback) {
// 如果你想的话，
// 也可以用字符串形式的 CSS 选择器来寻找目标 DOM 元素
onMounted(() => target.addEventListener(event, callback))
onUnmounted(() => target.removeEventListener(event, callback))
}
        使用限制
          组合式函数在 <script setup> 或 setup() 钩子中，应始终被同步地调用
          设计目的
            确定当前正在被执行的到底是哪个组件实例
              将生命周期钩子注册到该组件实例上
              将计算属性和监听器注册到该组件实例上
              在该组件被卸载时停止监听，避免内存泄漏
      通过抽取组合式函数改善代码结构
        示例
          <script setup>
import { useFeatureA } from './featureA.js'
import { useFeatureB } from './featureB.js'
import { useFeatureC } from './featureC.js'
const { foo, bar } = useFeatureA()
const { baz } = useFeatureB(foo)
const { qux } = useFeatureC(baz)
</script>
      在选项式 API 中使用组合式函数
        setup()函数中调用
      与其他模式的比较
        vue2 mixin
          不清晰的数据来源
          命名空间冲突
          隐式的跨 mixin 交流
        和无渲染组件的对比
        和 React Hooks 的对比
          灵感正来自于 React hooks
          模型有本质上的不同
      延伸阅读
    自定义指令
      Options API
        注册
          组件
            属性directives
          全局
            app.directives
        示例
          const focus = {
mounted: (el) => el.focus()
}
export default {
directives: {
// 在模板中启用 v-focus
focus
}
}
      setup API
        script setup中自动注册v打头的方法为指令
        示例
          // 在模板中启用 v-focus
const vFocus = {
mounted: (el) => el.focus()
}
      指令钩子
        created
          created(el, binding, vnode, prevVnode)
          时机
            在绑定元素的 attribute 前
            事件监听器应用前调用
        beforeMount
          beforeMount(el, binding, vnode, prevVnode)
          时机
            在元素被插入到 DOM 前调用
        mounted
          mounted(el, binding, vnode, prevVnode)
          时机
            在绑定元素的父组件及他自己的所有子节点都挂载完成后调用
        beforeUpdate
          beforeUpdate(el, binding, vnode, prevVnode)
          时机
            绑定元素的父组件更新前调用
        updated
          updated(el, binding, vnode, prevVnode)
          时机
            在绑定元素的父组件及他自己的所有子节点都更新后调用
        beforeUnmount
          beforeUnmount(el, binding, vnode, prevVnode)
          时机
            绑定元素的父组件卸载前调用
        unmounted
          unmounted(el, binding, vnode, prevVnode)
          时机
            绑定元素的父组件卸载后调用
      钩子参数
        el
          指令绑定到的元素。这可以用于直接操作 DOM
        binding
          对象属性
            value
            oldValue
            arg
            modifiers
            instance
            dir
        vnode
          代表绑定元素的底层 VNode
        prevNode
          之前的渲染中代表指令所绑定元素的 VNode
          出现
            beforeUpdate
            updated
    插件
      全局功能性代码
        一个插件可以是一个拥有 install() 方法的对象
          // 插件定义
const myPlugin = {
install(app, options) {
// 配置此应用
}
}
          // 全局挂载
app.use(myPlugin, {
/* 可选的选项 */
})
        一个安装函数本身
      场景
        通过 app.component() 和 app.directive() 注册一到多个全局组件或自定义指令。
        通过 app.provide() 使一个资源可被注入进整个应用
        app.config.globalProperties 中添加一些全局实例属性或方
        示例
          vue-router
  内置组件
    Transition
      会在一个元素或组件进入和离开 DOM 时应用动画
      触发
        由 v-if 所触发的切换
        由 v-show 所触发的切换
        由特殊元素 <component> 切换的动态组件
        改变特殊的 key 属性
      原理
        CSS过渡class
          v-enter-from
            进入动画的起始状态
            阶段：在元素插入之前添加，在元素插入完成后的下一帧移除
          v-enter-active
            进入动画的生效状态
            阶段：整个动画阶段
            作用：用来定义进入动画的持续时间、延迟与速度曲线类型
          v-enter-to
            进入动画的结束状态
            阶段：在元素插入完成后的下一帧被添加 (也就是 v-enter-from 被移除的同时)，在过渡或动画完成之后移除。
          v-leave-from
            离开动画的起始状态
            阶段：在离开过渡效果被触发时立即添加，在一帧后被移除
          v-leave-active
            离开动画的生效状态
            阶段：应用于整个离开动画阶段
            作用：用来定义离开动画的持续时间、延迟与速度曲线类型
          v-leave-to
            离开动画的结束状态
            阶段：在一个离开动画被触发后的下一帧被添加 (也就是 v-leave-from 被移除的同时)，在过渡或动画完成之后移除。
        JavaScript过渡钩子
          方法
            @before-enter="onBeforeEnter"
              onBeforeEnter(el) {},
            @enter="onEnter"
              onEnter(el, done) {
// 调用回调函数 done 表示过渡结束
// 如果与 CSS 结合使用，则这个回调是可选参数
done()
},
            @after-enter="onAfterEnter"
              onAfterEnter(el) {},
            @enter-cancelled="onEnterCancelled"
              onEnterCancelled(el) {},
            @before-leave="onBeforeLeave"
              onBeforeLeave(el) {},
              大多数时候，你应该只会用到 leave 钩子
            @leave="onLeave"
              onLeave(el, done) {
// 调用回调函数 done 表示过渡结束
// 如果与 CSS 结合使用，则这个回调是可选参数
done()
},
            @after-leave="onAfterLeave"
              onAfterLeave(el) {},
            @leave-cancelled="onLeaveCancelled"
              onLeaveCancelled(el) {}
              仅在v-show中可用
          使用技巧
            :css="false"
              跳过对 CSS 过渡的自动探测
              性能更优，防止CSS干扰过渡
            动画库
              Anime.js
                2020年之后不更新了
              Motion One
                小而美，几k
              GSAP
                老大哥
                min.js 29k
        如果没有云华，DOM的删除、插入会在下一帧执行
      小技巧
        支持CSS animation
          时机
            *-enter-from在一个 animationend 事件触发时被移除
          简单的在*-enter-active和*-leave-active下声明
        支持深层过渡
          说白了，通过设置过渡class的子元素来实现过渡效果
        支持元素间过渡
          v-if / v-else / v-else-if
            元素切换
        支持组件间过渡
          使用动态组件
            <Transition name="fade" mode="out-in">
<component :is="activeComponent"></component>
</Transition>
        支持动态的过渡名
          <Transition :name="transitionName"></Transition>
          建议创建可复用的过渡组件
      场景
        自定义过渡class
          props
            enter-from-class
            enter-active-class
            enter-to-class
            leave-from-class
            leave-active-class
            leave-to-class
          案例
            Animate.css
        控制transition和animation
          props
            type
              animation
              transition
        控制延迟时间
          props
            duration
            传参方式
              number
                <Transition :duration="550">...</Transition>
              object
                <Transition :duration="{ enter: 500, leave: 800 }">...</Transition>
        出现时过渡
          <Transition appear></Transition>
        过渡模式
          mode
            out-in
              先执行离开动画，然后在其完成之后再执行元素的进入动画
            in-out
              与out-in相反
              不常用
      性能
        高效的属性
          opacity
          transform
            浏览器GPU加速
        影响性能
          height
          margin
          其他触发CSS布局变动
      可复用过渡效果
        Transition组件包slot
        <Transition
name="my-transition"
@enter="onEnter"
@leave="onLeave">
<slot></slot> <!-- 向内传递插槽内容 -->
</Transition>
    TransitionGroup
      v-for 列表中的元素或组件被插入，移动，或移除时应用动画
      props
        css过渡
          同上Transition
        JavaScript钩子
          同上Transition
      区别
        默认情况下，它不会渲染一个容器元素
        过渡模式不可用
        列表中的每个元素都必须有一个独一无二的 key
        CSS 过渡 class 会被应用在列表内的元素上，而不是容器元素上
      进阶用法
        移动动画
          在移动中的元素加
            transition: all 0.5s ease;
          在*-active上加
            transition: all 0.5s ease;
        渐进延迟列表动画
          配合gsap
          配合js钩子
            @enter="onEnter"
            function onEnter(el, done) {
gsap.to(el, {
opacity: 1,
height: '1.6em',
delay: el.dataset.index * 0.15,
onComplete: done
})
}
    KeepAlive
      作用：多个组件间动态切换时缓存被移除的组件实例
      基本使用
        <KeepAlive>
<component :is="activeComponent" />
</KeepAlive>
      包含/排除
        include
          英文逗号
            include="a,b"
          正则
            :include="/a|b/"
          数组
            :include="['a', 'b']"
          关于组件名
            在 3.2.34 或以上的版本中，使用 <script setup> 的单文件组件会自动根据文件名生成对应的 name 选项
        exclude
      最大缓存实例数
        max
          prop 来限制可被缓存的最大组件实例数
      缓存实例的生命周期
        activated
          触发时机
            在首次挂载
            每次从缓存中被重新插入的时候调用
        deactivated
          触发时机
            在从 DOM 上移除、进入缓存
            组件卸载时调用
        注意
          不仅适用于 <KeepAlive> 缓存的根组件，也适用于缓存树中的后代组件
    Teleport
      作用：将一个组件内部的一部分模板“传送”到该组件的 DOM 结构外层的位置去
      场景
        模态框
        消息
        生成图片、PDF、Canvas
          单独需要一个DOM
        滚动
      基本用法
        <Teleport to="body">
<div v-if="open" class="modal">
<p>Hello from the modal!</p>
<button @click="open = false">Close</button>
</div>
</Teleport>
        按钮触发
          <button @click="open = true">Open Modal</button>
      搭配组件使用
        teleport组件的子组件
          保持父子关系
          props传递
      多个 Teleport 共享目标
        顺序就是简单的顺次追加
        后挂载的将排在目标元素下更后面的位置上
    Suspense
（实验特性）
      作用：用来在组件树中协调对异步依赖的处理
      时机
        等待下层的多个嵌套异步依赖项解析完成
        并可以在等待时渲染一个加载状态
      异步依赖
        带有异步 setup() 钩子的组件
          async setup()
          <script setup>中有顶层 await 表达式
        defineAsyncComponent
          ()=>import('./组件')
        设置suspensible: false 不用Suspense 控制
      加载状态
        插槽
          #default
          #fallback
        注意
          两个插槽都只允许一个直接子节点
      事件
        pending
          进入挂起状态时触发
        resolve
          resolve 事件是在 default 插槽完成获取新内容时触发
        fallback
          fallback 事件则是在 fallback 插槽的内容显示时触发
      错误处理
        全局
          app.config.errorHandler
        组件
          ComponentOptions属性errorCaptured
        钩子
          onErrorCaptured()
      和其他组件结合
        通常与 Vue Router 中的 <RouterView> 组件结合使用
        <RouterView v-slot="{ Component }">
<template v-if="Component">
<Transition mode="out-in">
<KeepAlive>
<Suspense>
<!-- 主要内容 -->
<component :is="Component"></component>
<!-- 加载中状态 -->
<template #fallback>
正在加载...
</template>
</Suspense>
</KeepAlive>
</Transition>
</template>
</RouterView>
          注意这里的套娃结构
  应用规模化
    单文件组件
      定义
        *.vue 文件，英文 Single-File Component，简称 SFC
        渐进增强的项目
          petite-vue
      组成
        <template>
        <script>
        <style>
      为什么要使用SFC
        使用熟悉的 HTML、CSS 和 JavaScript 语法编写模块化的组件
        让本来就强相关的关注点自然内聚
        预编译模板，避免运行时的编译开销
        组件作用域的 CSS
        在使用组合式 API 时语法更简单
        通过交叉分析模板和逻辑代码能进行更多编译时优化
        更好的 IDE 支持，提供自动补全和对模板中表达式的类型检查
        开箱即用的模块热更新 (HMR) 支持
      SFC 是如何工作的
        交由 @vue/compiler-sfc 编译为标准的 JavaScript 和 CSS
        输出
          编译后的 SFC 是一个标准的 JavaScript(ES) 模块
        原理
          Vue SFC演练场
            https://sfc.vuejs.org/
          集成SFC编译工具
            Vite
            Vue CLI
              基于Webpack
          SFC工具链
    工具链
      在线尝试
        Vue SFC演练场
        StackBliz中的Vue+Vite
      项目脚手架
        Vite
          npm init vue@latest
          学习Vue相关特殊行为
            @vitejs/plugin-vue
        Vue CLI
        浏览器内模板编译注意事项
          前缀为 vue.runtime.*
            只包含运行时的版本：不包含编译器
          名称中不包含 .runtime 的文件
            完全版，包含编译器
            体积也会因此增长大约 14kb
          构建时
            需要浏览器内的模板编译，你可以更改构建工具配置
            将 vue 改为相应的版本 vue/dist/vue.esm-bundler.js
      IDE支持
        VSCode
          Volar插件
          Vue2
            Vetur
        WebStorm
          内置支持
        Sublime Text
          LSP-Volar
        vim/Neovim
          coc-volar
        emasc
          lsp-mode
      浏览器开发者插件
        Chrome
        Firefox
        Electron插件
        文档地址
      TypeScript
        Volar插件能够自动为<script lang="ts">块提供检查
        vue-tsc可以在命令执行中执行类型检查，通常用来生成单文件的d.ts文件
          https://github.com/vuejs/language-tools/tree/master/packages/vue-tsc
      测试
        测试指南
        Vitest
          更快运行速度的测试运行
          针对Vite应用设计
        Cypress
          推荐用于 E2E 测试
        Jest
          可以通过 vite-jest 配合 Vite 使用
          前提：已经有一套jest测试集
      代码规范
        eslint-plugin-vue
          npm install -D eslint eslint-plugin-vue
        ESLint IDE插件
          ESLint for VSCode
        lint-staged
          git commit 进行检查
      格式化
        Volar
        Prettier
      SFC 自定义块集成
      底层库
        @vue/compiler-sfc
          这个包是 Vue 核心 monorepo 的一部分，并始终和 vue 主包版本号保持一致
          提供了处理 Vue SFC 的底层的功能
        @vitejs/plugin-vue
          为 Vite 提供 Vue SFC 支持的官方插件
        vue-loader
          为 webpack 提供 Vue SFC 支持的官方 loader
    路由
      概念区分
        客户端
          客户端的 JavaScript 可以拦截页面的跳转请求，动态获取新的数据，然后在无需重新加载的情况下更新当前页面
        服务端路由
          服务器根据用户访问的 URL 路径返回不同的响应结果
      原理
        History API
        hashchange事件
          window.addEventListeneer('hashchange', () => {
// 获取路由的变化，处理逻辑
})
      官方路由
        vue router
    状态管理
      组成部分
        状态
          驱动整个应用的数据源；
        视图
          对状态的一种声明式映射
        交互
          状态根据用户在视图中的输入而作出相应变更的可能方式
      两种难于应对的场景
        多个视图可能都依赖于同一份状态
          多级父子
        来自不同视图的交互也可能需要更改同一份状态
          跨组件传参
      方案
        响应式API
          reactive
          示例
            // store.js
import { reactive } from 'vue'
export const store = reactive({
count: 0
})
          问题
            由于 store 是跨多个请求共享的单例，上述模式可能会导致问题
        Pinia
          npm i pinia
          优势
            更强的团队协作约定
            与 Vue DevTools 集成，包括时间轴、组件内部审查和时间旅行调试
            模块热更新 (HMR)
            服务端渲染支持
            对 Vue 2 和 Vue 3 都可用
    测试
      意义
        自动化测试能够预防无意引入的 bug
      时机
        越早越好！我们建议你尽快开始编写测试
      类型
        单元测试
          检查给定函数、类或组合式函数的输入是否产生预期的输出或副作用
        组件测试
          检查你的组件是否正常挂载和渲染、是否可以与之互动，以及表现是否符合预期
        端到端测试
          检查跨越多个页面的功能，并对生产构建的 Vue 应用进行实际的网络请求
          通常涉及到建立一个数据库或其他后端
      单元测试
        验证小的、独立的代码单元是否按预期工作
        单元测试示例
          组合式函数
          组件
            白盒：单元测试
              白盒测试知晓一个组件的实现细节和依赖关系
            黑盒：组件测试
              黑盒测试不知晓一个组件的实现细节
              通常会渲染所有子组件，因而会被认为更像一种“集成测试”
        推荐方案
          Vitest
            由 create-vue 创建的官方项目配置是基于 Vite 的
          其他
            Peeky
              提供了一个基于图形用户界面（GUI）的测试界面
            Jest
              通过 vite-jest 这个包在 Vite 中使用
              已有一套 Jest 测试配置、且需要迁移到基于 Vite 的项目时使用它
      组件测试
        组件测试位于单元测试之上，测试这个组件做了什么，而不是测试它是怎么做到的
        测试范围
          prop
          事件
          提供的插槽
          样式
          CSS class 名
          生命周期钩子
          其他相关的问题
        方法
          对于 视图 的测试：根据输入 prop 和插槽断言渲染输出是否正确
          对于 交互 的测试：断言渲染的更新是否正确或触发的事件是否正确地响应了用户输入事件
        注意
          避免去断言一个组件实例的私有状态或测试一个组件的私有方法
            这是单元测试要做的事
          组件的最终工作是渲染正确的 DOM 输出
          不要完全依赖快照测试
        推荐方案
          Vitest
            @testing-library/vue
          Cypress
            @testing-library/cypress
          其他
            Nightwatch
      端到端的测试
        端到端测试的重点是多页面的应用表现，针对你的应用在生产环境下进行网络请求
        测试范围
          路由
          状态管理库
          顶级组件（常见为 App 或 Layout）
          公共资源或任何请求处理方面的问题
        推荐方案
          Cypress
          其他
            Playright
            Nightwatch v2
              基于Selenium webDriver
        示例
    服务端渲染SSR
      什么是SSR
        服务端直接渲染成 HTML 字符串，作为服务端响应返回给浏览器
        Sever Side Rendering
        SSG
          Static-Site Generation
            静态站点生成
          也被称为预渲染
      为什么要用SSR？
        更快的首屏加载
        统一的心智模型
          相同的语言以及相同的声明式、面向组件
        更好的 SEO
      限制
        生命周期钩子不一样
        外部库需要处理
        服务端渲染的应用需要node.js服务运行环境
          更高的服务端负载
      工作原理
        支持Vue SFC
          两次构建，一次用于客户端，一次用于服务器
        服务端页面预处理与预加载
          确保返回的 HTML 包含正确的客户端资源链接和最优的资源加载提示 (如 prefetch 和 preload)
        以一种通用的方式管理路由、数据获取和状态存储
      示例
      通用工具
        Nuxt
        Quasar
        Vite SSR
          前提：只有在你有丰富的 SSR 和构建工具经验，并希望对应用的架构做深入的定制时才推荐使用
          示例
      书写SSR友好的代码
（一些通用原则）
        服务端的响应性
          默认情况下响应性在 SSR 期间是禁用的
        组件生命周期钩子
          mounted 或者 updated 这样的生命周期钩子不会在 SSR 期间被调用
          beforeCreate 和 created 这两个钩子会在 SSR 期间被调用
          含有副作用的代码放到 mounted
            避免在 beforeCreate 和 created中使用会产生副作用且需要被清理的代码
            比如：setInterval 设置定时器
              可能会在在 beforeUnmount 或 unmounted 中清除
              但是unmount 钩子不会在 SSR 期间被调用，所以定时器会永远存在
        访问平台特有 API
          区别浏览器与Node.js API
            浏览器
              window 或 document
            Node.js
              Global
              fs
              path
          有些情况可以混用
            axios
            lodash
        跨请求状态污染
          应用模块服务器启动时初始化一次，可能导致状态量泄露给另外一个用户的请求
          解决方案是在每个请求中为整个应用创建一个全新的实例
            router
            全局store
              使用依赖注入提供状态共享
  最佳实践
    生产部署
      开发环境 vs. 生产环境
        开发环境有更多提示
        生产环境会删除这些
          对常见错误和隐患的警告
          对组件 props / 自定义事件的校验
          响应性调试钩子
          开发工具集成
      不使用构建工具
        全局变量版本
          Vue.global.prod.js
        ESM 版本
          vue.esm-browser.prod.js
      使用构建工具
        vue被解析为vue.runtime.esm-bundler.js
        编译时功能标记已被正确配置
          __VUE_OPTIONS_API__
          __VUE_PROD_DEVTOOLS__
        process.env.NODE_ENV
          production
        指南
          Vite 生产环境指南
          Vite 部署指南
          Vue CLI 部署指南
      追踪运行时错误
        app.config.errorHandler
        其他工具
          Sentry
          Bugsnag
    性能优化
      优化对象
        页面加载性能
          Web 指标 (Web Vitals)
            最大内容绘制 (Largest Contentful Paint，缩写为 LCP)
            首次输入延迟 (First Input Delay，缩写为 FID)
        更新性能
          应用响应用户输入更新的速度
      分析选项
        生产部署负载性能分析
          PageSpeed Insights
          WebPageTest
        本地开发期间
          Chrome 开发者工具“性能”面板
          Vue 开发者扩展
      页面加载优化
        指南
    无障碍访问
    安全
  TypeScript
    总览
    TS与组合式API
    TS与选项式API
  进阶主题
    使用vue的多种方式
    组合式API常见问答
    深入响应系统
    渲染机制
    渲染函数 & JSX
    Vue 与 Web Components
    动态技巧
    响应性语法糖