Tianhe Gao

Vue

3

Vue.js v3 guide

  1. Essentials

    1. Creating an Application

      1import { createApp } from 'vue'
      2
      3const app = createApp({
      4  /* root component options */
      5})

      单独 App.vue

      1import { createApp } from 'vue'
      2import App from './App.vue'
      3
      4const app = createApp(App)

      Mount 挂载

      1<div id="app"></div>
      1app.mount('#app')

      App 配置

      1app.config.errorHandler = (err) => {
      2  /* handle error */
      3}

      App 范围内组件

      1app.component('TodoDeleteButton', TodoDeleteButton)

      这个组件可以在 App 的任何地方使用。

      多个 App 实例

      1const app1 = createApp({ /* ... */ })
      2app1.mount('#container-1')
      3
      4const app1 = createApp({ /* ... */ })
      5app2.mount('#container-2')
    2. Template Syntax

      文本插值

      1<span>Message: {{ msg }}</span>

      原始 HTML

      1<p>Using text interpolation: {{ rawHtml }}</p>
      2<p>Using v-html directive: <span v-html="rawHtml"></span></p>

      v-html Vue 指令的一种。

      Note that you cannot use v-html to compose template partials, because Vue is not a string-based templating engine. Instead, components are preferred as the fundamental unit for UI reuse and composition.

      属性绑定

      1<div v-bind:id="dynamicId"></div>
      2
      3<!--- v-bind:id  可简写为 :id --->
      4
      5<button :disabled="isButtonDisabled">Button</button>

      v-bind(用于无法使用 {{}} 的 HTML 属性中) 使 id 与组件同步,如果 id 为 null/undefined,则渲染后的页面无属性 id。

      动态绑定多个属性

      1const objectOfAttrs = {
      2  id: 'container',
      3  class: 'wrapper',
      4}
      1<div v-bind="objectOfAttrs"></div>

      JS 表达式(支持数据绑定的 JS 表达式,可用于文本插值、属性绑定,只能键入表达式,可以调用组件中暴漏的函数,此处的表达式可使用的"全局对象"[但是,可以通过 app.config.globalProperties 显示添加])。

       1{{ number + 1 }}
       2
       3{{ ok ? 'YES' : 'NO' }}
       4
       5{{ message.split('').reverse().join('') }}
       6
       7<div :id="`list-${id}`"></div>
       8
       9<span :title="toTitleDate(date)">
      10  {{ formatDate(date) }}
      11</span>

      指令( v- 前缀的属性,参数[动态参数值约束、动态参数语法约束、避免属性名大写],修饰语 @submit.prevent <=> event.preventDefault()

      内建属性: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

       1<a v-bind:href="url"> ... </a>
       2<a :href="url"> ... </a>
       3
       4<a v-on:click="doSomething"> ... </a>
       5<a @click="doSomething"> ... </a>
       6
       7<a v-bind:[attributeName]="url"> ... </a>
       8<a :[attributeName]="url"> ... </a>
       9
      10<a v-on:[eventName]="doSomething"> ... </a>
      11<a @[eventName]="doSomething"> ... </a>
      12
      13<form @submit.prevent="onSubmit"> ... </form>
    3. Reactivity Fundamentals

      1. 声明响应式状态

        1import { reactive } from 'vue'
        2
        3const state = reactive({ count: 0 )}
        1. 使用 <script setup> 与否的对比

           1<!--- 不使用 --->
           2<script>
           3import { reactive } from 'vue'
           4export default {
           5  setup() {
           6    const state = reactive({ count: 0 })
           7    function incrment() {
           8      state.count++
           9    }
          10    return {
          11      state,
          12      increment
          13    }
          14  }
          15}
          16</script>
          17<template>
          18  <button @click="increment">
          19    {{ state.count }}
          20  </button>
          21<template>
          22
          23<!--- 使用 --->
          24<script setup>
          25import { reactive } from 'vue'
          26const state = reactive({ count: 0 })
          27function increment() {
          28  state.count++
          29}
          30</script>
          31<template>
          32  <button @click="increment">
          33    {{ state.count }}
          34  </button>
          35</template>
        2. DOM Update Timing

           1<script setup>
           2import { reactive, nextTick } from 'vue'
           3
           4const state = reactive({ count: 0 })
           5function increment() {
           6  state.count++
           7  nextTick(() => {
           8    console.log(state.count)
           9  })
          10}
          11</script>
          12
          13<template>
          14  <div>
          15    {{ state.count }}
          16  </div>
          17  <button @click="increment">
          18    BUTTON
          19  </button>
          20</template>
        3. Deep Reactivity

          In Vue, state is deeply reactive by default.

           1import { reactive } from 'vue'
           2
           3const obj = reactive({
           4  nested: { count: 0 },
           5  arr: ['foo', 'bar']
           6})
           7function mutateDeeply() {
           8  obj.nested.count++
           9  obj.arr.push('baz')
          10}
        4. Reactive Proxy vs. Original

           1import { reactive } from 'vue'
           2const  raw = {}
           3const proxy = reactive(raw)
           4// proxy is NOT equal to the original
           5console.log(proxy === raw)
           6console.log(proxy === reactive(raw))
           7console.log(proxy === reactive(proxy))
           8
           9import { reactive } from 'vue'
          10const proxy = reactive({})
          11const raw = {}
          12proxy.nested = raw
          13console.log(proxy.nested === raw)
        5. reactive() 的限制

          1. 数据类型仅限于对象
          2. 无法替换响应式(代理)对象,一旦替换原来的响应式对象会失去连接
           1import { reactive } from 'vue'
           2
           3const state = reactive({ count: 0})
           4let n = state.count
           5n++
           6console.log(n)
           7
           8let { count } = state
           9count++
          10// won't be able to track changes to state.count
          11callSomeFunc(state.count)
      2. 通过 ref() 声明的响应式变量

        为了解决 reactive() 的局限性,Vue 提供了 ref() 用于对任何数据类型创建响应式。

        1import { ref } from 'vue'
        2const count = ref(0)
        3console.log(count)
        4console.log(count.value)
        5count.value++
        6console.log(count.value)

        和对象中的属性类似,一个 ref 的 .value 属性也是响应式的。另外,当值为对象类型时,会用 reactive() 自动转换它的 .value

        ref 保存对象

        1import { ref } from 'vue'
        2const objectRef = ref({ count: 0 })
        3objectRef.value = { count: 1 }
        4console.log(objectRef.value) // Proxy
        

        Refs 也可传入函数、从纯对象中结构却不失去响应状态

        1const obj = {
        2  foo: ref(1),
        3  bar: ref(2)
        4}
        5callSomeFunction(obj.foo)
        6const { foo, bar } = obj
        1. ref 在模板中的解包

           1<script setup>
           2import { ref } from 'vue'
           3const count = ref(0)
           4function increment() {
           5  count.value++
           6}
           7</script>
           8<template>
           9  <button @click="increment">
          10    {{ count }} <!--- no .value needed --->
          11  </button>
          12</template>
           1<script setup>
           2import { ref } from 'vue'
           3const object = { foo: ref(1) }
           4function increment() {
           5  object.foo.value++
           6}
           7</script>
           8<template>
           9  <button @click="increment">
          10    {{ object.foo }} <!--- no .value needed --->
          11  </button>
          12</template>
          13
          14<script setup>
          15import { ref } from 'vue'
          16const object = { foo: ref(1) }
          17</script>
          18<template>
          19  <button>
          20-   {{ object.foo + 1 }} <!--- but this need .value --->
          21+   {{ object.foo.value + 1 }} 
          22  </button>
          23</template>
          24
          25<!--- 让 foo 成为顶级属性 --->
          26<script setup>
          27import { ref } from 'vue'
          28const object = { foo: ref(1) }
          29const { foo } = object
          30</script>
          31<template>
          32  <button>
          33    {{ foo + 1 }} <!--- no .value needed --->
          34  </button>
          35</template>
        2. ref 在响应式对象中的解包

          1import { ref, reactive } from 'vue'
          2const count = ref(0)
          3const state = reactive({
          4  count
          5})
          6console.log(state.count)
          7state.count = 1
          8console.log(state.count)

          如果新的 ref 赋给了 state.count 则原来的会断开连接

           1import { ref, reactive } from 'vue'
           2const count = ref(0)
           3const otherCount = ref(2)
           4const state = reactive({
           5  count
           6})
           7console.log(state.count)
           8state.count = 1
           9console.log(state.count)
          10state.count = otherCount
          11console.log(state.count)
          12console.log(count.value)

          Ref unwrapping only happens when nested inside a deep reactive object.

          ref 在数组、集合中的解包

          1import { ref, reactive } from 'vue'
          2const books = reactive([ref('Vue 3 Guide')])
          3// 需要 .value
          4console.log(books[0].value)
          5const map = reactive(new Map([['count', ref(0)]]))
          6// 需要 .value
          7console.log(map.get('count').value)
    4. Computed Properties

      1. 基本例子

        如果使用包含响应式数据的复杂逻辑的话,推荐使用 computed() 函数。

         1<script setup>
         2import { computed, reactive } from 'vue'
         3const author = reactive({
         4  name: 'John Doe',
         5  books: [
         6    'Vue 2',
         7    'Vue 3',
         8    'Vue 4'
         9  ]
        10})
        11const publishedBooksMessage = computed(() => {
        12  return author.books.length > 0 ? 'Yes' : 'No'
        13})
        14</script>
        15<template>
        16  <p>Has published books:</p>
        17  <span>{{ publishedBooksMessage }}</span>
        18</template>
      2. Computed Caching vs. Methods

        如果不使用 computed() 函数,还可以用方法函数。

         1<script setup>
         2import { reactive } from 'vue'
         3const author = reactive({
         4  name: 'John Doe',
         5  books: [
         6    'Vue 2',
         7    'Vue 3',
         8    'Vue 4',
         9    'Vue 5'
        10  ]
        11})
        12const publishedBooksMessage = () => {
        13  return author.books.length > 3 ? 'Yes' : 'No'
        14}
        15</script>
        16<template>
        17  <p>Has published books?</p>
        18  <span>{{ publishedBooksMessage() }}</span>
        19</template>

        两种方法都能产生最终结果,但是 computed properties are cached based on their reactive dependencies。计算属性在 author.books 不改变的情况下,不发生变化。This means as long as author.books has not changed, multiple access to publishedBooksMessage will immediately return the previously computed result without having to run the getter function again.

        This also means the following computed property will never update, because Date.now() is not a reactive dependency:

        1const now = computed(() => Date.now())

        与计算属性不同,每当页面重绘时,方法总会运行。

      3. 可写计算属性

        计算属性默认 getter-only,如果对计算属性赋新值,会有运行时警告。

         1<script setup>
         2import { ref, computed } from 'vue'
         3const firstName = ref('John')
         4const lastName = ref('Doe')
         5const fullName = computed({
         6  get() {
         7    return firstName.value + ' ' + lastName.value
         8  },
         9  set(newValue) {
        10    [firstName.value, lastName.value] = newValue.split(' ')
        11  }
        12})
        13console.log(fullName.value)
        14fullName.value = 'Evan You'
        15console.log(fullName.value)
        16</script>
      4. 最佳实践

        • Getters should be side-effect free
        • Avoid mutating computed value
    5. Class and Style Bindings

      数据绑定的常见应用就是操作元素的类列表和行内样式。但是,在处理比较复杂的绑定时,通过拼接生成字符串是麻烦且易出错的。因此,Vue 专门为 classstylev-bind 用法提供了特殊的功能增强。除了字符串外,表达式的值也可以是对象或数组。

      1. Binding HTML Classes

        1. Binding to Objects

          1<div :class="{ active: isActive }"></div>

          还可有多个 class 属性,以及在一个 class 属性中添加多个对象属性。

           1<script setup>
           2import { ref } from 'vue'
           3const isActive = ref(true)
           4const hasError = ref(false)
           5</script>
           6<template>
           7  <div
           8    class="static"
           9    :class="{ active: isActive, 'text-danger': hasError }"
          10  ></div>
          11</template>
          12
          13<!--- 模板部分会渲染成 --->
          14<div class="static active"></div>
          15
          16<!--- 如果 hasError 为真,则渲染成 --->
          17<div class="static active text-danger"></div>

          对象绑定不必是行内:

           1<script setup>
           2import { reactive } from 'vue'
           3const classObject = reactive({
           4  active: true,
           5  'text-danger': false
           6})
           7</script>
           8<template>
           9  <div :class="classObject"></div>
          10</template>

          还能绑定到计算属性:

           1<script setup>
           2import { ref, computed } from 'vue'
           3const isActive = ref(false)
           4const error = ref(true)
           5const classObject = computed(() => ({
           6  active: isActive.value && !error.value,
           7  'text-danger': error.value 
           8}))
           9</script>
          10<template>
          11  <div :class="classObject"></div>
          12</template>
        2. Binding to Arrays

           1<script setup>
           2import { ref } from 'vue'
           3const activeClass = ref('active')
           4const errorClass = ref('text-danger')
           5</script>
           6<template>
           7  <div :class="[activeClass, errorClass]"></div>
           8</template>
           9
          10<!--- 模板中的 binding class 可改成(2种写法) --->
          11<div :class="[isActive ? activeClass : '', errorClass]"></div>
          12<div :class="[{ active: isActive }, errorClass]"></div>
        3. With Components 组件内部对一个元素添加 class,使用组件时又添加一次,两次的 class 会合并。可以用 $attrs.class 只应用”使用组件”时的 class。
      2. Binding Inline Styles

        1. 对象
         1<script setup>
         2import { ref } from 'vue'
         3const activeColor = ref('red')
         4const fontSize = ref(30)
         5</script>
         6<template>
         7  <div :style="{ color: activeColor, fontSize: fontSize + 'px' }">nihao</div>
         8</template>
         9
        10<!--- 其他表达方式 --->
        11<div :style="{ 'font-size': fontSize + 'px' }">
        12
        13const styleObject = reactive({
        14  color: 'red',
        15  fontSize: '13px'
        16})
        17<div :style="styleObject">
        1. 数组
        1<div :style="[baseStyles, overridingStyles]">
        1. 自动前缀,最大浏览器支持
        2. 多个值在一起,渲染最后的浏览器支持的值
        1<div :style="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }"></div>

        在这里,渲染结果 display: flex

    6. Conditional Rendering

      条件渲染,命令:v-if, v-else, v-else-if, v-show, v-for

      v-else 必须和 v-if/v-else-if 搭配使用

      v-show 所在元素会被渲染成 DOM 树;v-show 只是在切换 CSS display 属性;不支持用在 <template> ,也不和 v-else 搭配使用。

       1<button @click="awe = !awe">Toggle</button>
       2<h1 v-if="awe">You are good</h1>
       3<h1 v-else>You are not good</h1>
       4
       5
       6<div v-if="type === 'A'">
       7  A
       8</div>
       9<div v-else-if="type === 'B'">
      10  B
      11</div>
      12<div v-else-if="type === 'C'">
      13  C
      14</div>
      15<div v-else>
      16  Not A/B/C
      17</div>
      18
      19
      20<template v-if="ok">
      21  ...
      22</template>
      23
      24
      25<h1 v-show="ok">Hello!</h1>
      1. v-if vs. v-show
      • v-if 是真正的条件渲染,在条件变换时,条件块内部的元素会不断销毁和重新创建
      • v-if 是懒加载的,如果初始条件为 false,什么都不会做,只有条件第一次变为真时才会开始销毁重建过程
      • 相对看,v-show 更简单,它的条件块总是渲染,显示与否依靠的是 CSS 的属性值切换
      • 两者综合来看: v-if 与 v-show 相比,切换成本更高,因此,如果需要经常切换某种状态,使用 v-show,如果状态不太经常改变,使用 v-if
      1. v-if with v-for

      不推荐用在一起,解释文档:

    7. List Rendering

      1. v-for
       1<script setup>
       2import { ref } from 'vue'
       3const parentMessage = ref('Parent')
       4const items = ref([{ message: 'Foo'}, { message: 'Bar'}])
       5</script>
       6<template>
       7  <ul>
       8    <li v-for="({ message }, index) of items">
       9      {{ parentMessage }} - {{ index + 1 }} - {{ message }}
      10    </li>
      11  </ul>
      12</template>
      1. v-for + Object
       1<script setup>
       2import { ref, reactive } from 'vue'
       3const myObject = reactive({
       4  title: 'How to do lists in Vue',
       5  author: 'John Doe',
       6  publishedAt: '2022-09-13'
       7})
       8</script>
       9<template>
      10  <ul>
      11    <!--- value, key, index 的顺序的改变会影响结果 --->
      12    <li v-for="(value, key, index) of myObject">
      13      {{ index }} - {{ key }} - {{ value }}
      14    </li>
      15  </ul>
      16</template>
      1. v-for with Range
      1<template>
      2  <span v-for="n in 10">
      3    <span v-if="n === 10">{{ n }}</span>
      4    <span v-else>{{ n }}, </span>
      5  </span>
      6</template>
      1. v-for on <template>
       1<script setup>
       2import { ref } from 'vue'
       3const items = ref([{ msg: 'hello'}, {msg: 'hell'}, {msg: 'hel'}, {msg: 'he'}, {msg: 'h'}])
       4const textColor = ref('#904cbc')
       5</script>
       6<template>
       7  <ul>
       8    <template v-for="item in items">
       9      <li :style="{ color: textColor }">{{ item.msg }}</li>
      10      <li class="divider" role="presentation"></li>
      11    </template>
      12  </ul>
      13</template>
      1. v-for + v-if

      说是不推荐,我看看原因

      错误用法:

      1<template>
      2  <ul>
      3    <li v-for="todo in todos" v-if="!todo.isComplete">{{ todo.name }}</li>
      4  </ul>
      5</template>

      Fixed:

      1<template>
      2  <ul>
      3    <template v-for="todo in todos">
      4      <li v-if="!todo.isComplete">{{ todo.name }}</li>
      5    </template>
      6  </ul>
      7</template>
      1. key ——维持状态/条件

      更新列表的默认模式只,适用于列表输出不依靠子组件状态或有限 DOM 状态

       1<script setup>
       2import { ref } from 'vue'
       3const items = ref([{ msg: 'hello'}, {msg: 'hell'}, {msg: 'hel'}, {msg: 'he'}, {msg: 'h'}])
       4</script>
       5<template>
       6  <ul>
       7    <li v-for="item in items" :key="item.id">
       8      {{ item.msg }}
       9    </li>
      10  </ul>
      11</template>

      当使用 <template v-for> 时,key 应位于 <template> 容器。

      1<template v-for="todo in todos" :key="todo.name">
      2  <li>{{ todo.name }}</li>
      3</template>

      每当用 v-for 时都加上 key。

      key 绑定原始类型——字符串或数字,而非对象。

      1. v-for + Component
      1<MyComponent v-for="item in items" :key="item.id" />
      2
      3<MyComponent
      4  v-for="(item, index) in items"
      5  :item="item"
      6  :index="index"
      7  :key="item.id"
      8/>

      Component 不会自动将 item 值插入,这样利用复用组件。

      1. 数组变化检测

      用到的方法:push, pop, shift, unshift, splice, sort, reverse。它们会改变元数组;而 filter、concat、slice 不改变,会返回新数组。

      1. 展示过滤后/分类后的结果
       1<script setup>
       2import { ref, computed } from 'vue'
       3const numbers = ref([1,2,3,4,5])
       4const evenNumbers = computed(() => {
       5  return numbers.value.filter((n) => n % 2 === 0)
       6})
       7</script>
       8<template>
       9  <li v-for="n in evenNumbers">{{ n }}</li>
      10</template>

      在可计算属性中使用 reverse(),sort() 会改变原有数组,在使用前要对其进行拷贝。

      1- return numbers.reverse()
      2+ return [...numbers].reverse()
      
    8. Event Handling

      1. 监听事件

      v-on 简写成 @,用法: v-on:click"handler"= / @click"handler"= 。

      handler 值可以是以下两种:

      • 行内 handlers
      • 方法 handlers
      1. 行内 handlers
      1<script setup>
      2import { ref } from 'vue'
      3const counter = ref(0)
      4</script>
      5<template>
      6  <button @click="counter++">Add 1</button>
      7  <p>The button above has been clicked {{ counter }} times.</p>
      8</template>
      1. 方法 handlers
       1<script setup>
       2import { ref } from 'vue'
       3const name = ref('Vue.js')
       4function greet(event) {
       5  alert(`Hello ${name.value}!`)
       6  // `event` is the native DOM event
       7  if (event) {
       8    alert(event.target.tagName)
       9  }
      10}
      11</script>
      12<template>
      13  <button @click="greet">Greet</button>
      14</template>

      方法 vs. 行内

      模板编译器的检测标准:如果检测到确定的函数则是方法,如果检测到 foo()count++ 则是行内。

      1. 行内 handlers 调用方法
      1<script setup>
      2function say(msg) {
      3  alert(msg)
      4}
      5</script>
      6<template>
      7  <button @click="say('Hello!')">SAY</button>
      8</template>
      1. 访问行内 handlers 的事件参数

      需要访问原始 DOM 事件

       1<script setup>
       2function warn(msg, event) {
       3  if (event) {
       4    event.preventDefault()
       5  }
       6  alert(msg)
       7}
       8</script>
       9<template>
      10  <!--- 2 种方式 --->
      11  <button @click="warn('Form cannot be submitted yet.', $event)">Submit</button>
      12  <button @click="(event) => warn('Form cannot be submitted yet.', event)">Submit</button>
      13</template>
      1. 事件修改器
       1<a @click.stop="doThis"></a>
       2
       3<form @submit.prevent="onSubmit"></a>
       4
       5<a @click.stop.prevent="doThat"></a>
       6
       7顺序也是重要的,stop 和 prevent 的先后顺序
       8
       9<form @submit.prevent></form>
      10
      11<div @click.self="doThat">...</div>
      12
      13<div @click.capture="doThis">...</div>
      14
      15<a @click.once="doThis"></a>
      16
      17<div @scroll.passive="onScroll">...</div>

      .passive 和 .prevent 不能一起用,两者起相反效果。

      1. 键修改器
      1<input @keyup.enter="submit" />
      2<input @keyup.page-down="onPageDown" />

      常用 key:

      • .enter
      • .tab
      • .delete
      • .esc
      • .space
      • .up
      • .down
      • .left
      • .right

      系统 key 修改器:

      • .ctrl
      • .alt
      • .shift
      • .meta

      .exact 修改器

      1. 鼠标修饰符
      • .left
      • .right
      • .middle
    9. Form Input Bindings

      1. 基本用法

      1.1 Text

       1<script setup>
       2import { ref } from 'vue'
       3const txt = ref('')
       4</script>
       5<template>
       6  <input v-model="txt" />
       7  <p>
       8    Message is: {{ txt }}
       9  </p>
      10</template>

      1.2 Multiline Text

      1<script setup>
      2import { ref } from 'vue'
      3const txt = ref('')
      4</script>
      5<template>
      6  <span>Multiline message is:</span>
      7  <p style="white-space: pre-line;">{{ txt }}</p>
      8  <textarea v-model="txt"></textarea>
      9</template>

      别用 <textarea>{{ txt }}</textarea>

      1.3 Checkbox

      1<script setup>
      2import { ref } from 'vue'
      3const checked = ref(false)
      4</script>
      5<template>
      6  <input type="checkbox" id="checkbox" v-model="checked"/>
      7  <label for="checkbox">{{ checked }}</label>
      8</template>

      多个 checkbox

       1<script setup>
       2import { ref } from 'vue'
       3const checkedNames = ref([])
       4</script>
       5<template>
       6  <div>Checked names: {{ checkedNames }}</div>
       7  <input type="checkbox" id="jack" value="Jack" v-model="checkedNames" />
       8  <label for="jack">Jack</label>
       9  <input type="checkbox" id="john" value="John" v-model="checkedNames" />
      10  <label for="john">John</label>
      11  <input type="checkbox" id="mike" value="Mike" v-model="checkedNames" />
      12  <label for="mike">Mike</label>
      13</template>

      1.4 Radio

       1<script setup>
       2import { ref } from 'vue'
       3const picked = ref('One')
       4</script>
       5<template>
       6  <div>Picked: {{ picked }}</div>
       7    <input type="radio" id="one" value="One" v-model="picked" />
       8    <label for="one">One</label>
       9    <input type="radio" id="two" value="Two" v-model="picked" />
      10  <label for="two">Two</label>
      11</template>

      1.5 Select

      Single

       1<script setup>
       2import { ref } from 'vue'
       3const picked = ref('One')
       4</script>
       5<template>
       6  <div>Picked: {{ picked }}</div>
       7    <input type="radio" id="one" value="One" v-model="picked" />
       8    <label for="one">One</label>
       9    <input type="radio" id="two" value="Two" v-model="picked" />
      10  <label for="two">Two</label>
      11</template>

      Multiple

       1<script setup>
       2import { ref } from 'vue'
       3const selected = ref([])
       4</script>
       5<template>
       6  <div>Selected: {{ selected }}</div>
       7  <select v-model="selected" multiple>
       8    <option>A</option>
       9    <option>B</option>
      10    <option>C</option>
      11  </select>
      12</template>
      13<style>
      14select[multiple] {
      15  width: 100px;
      16}
      17</style>

      v-for 动态渲染

       1<script setup>
       2import { ref } from 'vue'
       3const selected = ref('A')
       4const options = ref([
       5  { text: 'One', value: 'A' },
       6  { text: 'Two', value: 'B' },
       7  { text: 'Three', value: 'C' }
       8])
       9</script>
      10<template>
      11  <select v-model="selected">
      12    <option v-for="option in options" :value="option.value">
      13      {{ option.text }}
      14    </option>
      15  </select>
      16    <div>Selected: {{ selected }}</div>
      17</template>
      1. Value Bindings

      将值和动态属性进行绑定。

      2.1 Checkbox

      1<input
      2  type="checkbox"
      3  v-model="toggle"
      4  :true-value="dynamicTrueValue"
      5  :false-value="dynamicFalseValue" />

      2.2 Radio

      1<input type="radio" v-model="pick" :value="first">
      2<input type="radio" v-model="pick" :value="second">

      2.3 Select Options

      1<select v-model="selected">
      2  <option :value="{ number: 123 }">123</option>
      3</select>
      1. Modifiers

      3.1 .lazy 3.2 .number 3.3 .trim

      1<input v-model.lazy="msg">
      2<input v-model.number="age">
      3<input v-model.trim="msg">
      1. v-model + Component

      见后续”组件”部分。

    10. Lifecycle Hooks

      每个 Vue 组件实例创建时会经历一系列初始化步骤。比如,建立数据观测系统、编译模板、挂载实例到 DOM、当数据改变时更新 DOM。随着一系列过程,还会有生命周期挂钩的函数。让用户能够在指定阶段添加自己的代码。

      1. 注册生命周期钩子
      1<script setup>
      2import { onMounted } from 'vue'
      3onMounted(() => {
      4  console.log(`The component is now mounted.`)
      5})
      6</script>

      与 onMounted 一样常用的生命周期 Hooks,有 onUpdated, onUnmounted。

      此类 Hooks 使用条件:调用栈是同步的,创建于 setup() 内部。

    11. Watchers

      1. 基本例子

      watch() 函数:监听一个或多个响应式数据源,当数据变化时,触发回调函数。

      1. Deep Watchers

      使用的时候注意性能损耗。

      1. watchEffect()

      watch() 和 watchEffect() 的区别:

      • watch 只监听显式的可监听源。不会追踪接入回调函数内部的事物。另外,回调函数只在源真的改变时才触发。
      • watchEffect 将跟踪依赖和边际效果结合在一起。自动跟踪每个接入的响应式属性(在同步执行过程中)。更方便,代码更简洁,但不容易看出响应式依赖。
      1. Cackback Flush Timing

      默认情况下,用户创建的 watcher 回调函数在 Vue 组件更新以前被调用。

      如果想在 Vue 组件更新以后,在 watcher 回调函数中改变 DOM。需要指定 flush: 'post'

       1// watch
       2watch(source, callback, {
       3  flush: 'post'
       4})
       5
       6/// watchEffect
       7// way 1
       8watchEffect(callback, {
       9  flush: 'post'
      10})
      11// way 2
      12import { watchPostEffect } from 'vue'
      13
      14watchPostEffect(() => {
      15  /* executed after Vue updates */
      16})
      1. Stopping a Watcher
    12. Template Refs

Scaling Up

https://vuejs.org/guide/scaling-up/sfc.html

SFC

Tooling

  1. Create Vue project

    • Vue CLI(webpack)
    • create-vue(vite)
  2. Browser Devtools
  3. Testing

    • Cypress
    • Vitest
    • Jest
  4. Linting

    • eslint-plugin-vue
  5. Formatting

    • Volar
    • Prettier

Routing

  • vue-router@4(vue3)
  • vue-router@3(vue2)

State Management

  • Reactivity API
  • Pinia

Testing

  • Unit | Vitest, Jest
  • Component | Vitest, Cypress
  • End-to-end | Cypress, Playwright

SSR: basic, nuxt, quasar, vite-ssr

Hydration(wikipedia):In web development, hydration or rehydration is a technique in which client-side JavaScript converts a static HTML web page, delivered either through static hosting or server-side rendering, into a dynamic web page by attaching event handlers to the HTML elements.


No notes link to this note