岁岁年,碎碎念

Vue Doc 随手记 ——六-九节

2023.08.03     801

六-九节

  1. 条件渲染
  2. 列表渲染
  3. 事件处理
  4. 表单输入绑定

条件渲染

v-if

表达式返回真值时被渲染

<div v-if="type === 'A'">
  A
</div>
<div v-else-if="type === 'B'">
  B
</div>
<div v-else-if="type === 'C'">
  C
</div>
<div v-else>
  Not A/B/C
</div>

# 不止一个元素可以依附于 <template>
<template v-if="ok">
  <h1>Title</h1>
  <p>Paragraph 1</p>
  <p>Paragraph 2</p>
</template>

v-show

按条件显示一个元素

<h1 v-show="ok">Hello!</h1>

v-if 和 v-show 区别

特性 v-if v-show
渲染方式 根据条件动态添加或移除元素 通过 CSS 控制元素的显示与隐藏
初始化开销 需要初始化开销,条件为 true 时创建元素并插入 DOM 无初始化开销,元素一直在 DOM 中,通过 CSS 控制显示隐藏
性能考虑 适合条件变化较少或有大块内容需要动态渲染 适合条件频繁变化或内容变化较少,仅需切换显示隐藏
适用场景 动态内容频繁变化时较为高效 静态内容或频繁切换显示隐藏的情况下较为高效

列表渲染

v-for 基于一个数组来渲染一个列表

const items = ref([{ message: 'Foo' }, { message: 'Bar' }])

<li v-for="item in items">
  {{ item.message }}
</li>

# 索引
<li v-for="(item, index) in items">
  {{ parentMessage }} - {{ index }} - {{ item.message }}
</li>

# 解构
<li v-for="{ message } in items">
  {{ message }}
</li>

<!-- 有 index 索引时 -->
<li v-for="({ message }, index) in items">
  {{ message }} {{ index }}
</li>

# 多层嵌套,内部可以访问外层
<li v-for="item in items">
  <span v-for="childItem in item.children">
    {{ item.message }} {{ childItem }}
  </span>
</li>

# 可以用 of
<div v-for="item of items"></div>

v-for 和对象

遍历的顺序会基于对该对象调用 Object.keys() 的返回值来决定。

const myObject = reactive({
  title: 'How to do lists in Vue',
  author: 'Jane Doe',
  publishedAt: '2016-04-10'
})

# value
<ul>
  <li v-for="value in myObject">
    {{ value }}
  </li>
</ul>

# value\key
<li v-for="(value, key) in myObject">
  {{ key }}: {{ value }}
</li>

# value\key\index
<li v-for="(value, key, index) in myObject">
  {{ index }}. {{ key }}: {{ value }}
</li>

# template
<ul>
  <template v-for="item in items">
    <li>{{ item.msg }}</li>
    <li class="divider" role="presentation"></li>
  </template>
</ul>

v-for 里使用范围值

# 从 1 到 10
<span v-for="n in 10">{{ n }}</span>

通过 key 管理状态

添加 key 的主要原因是帮助 Vue 更高效地管理 DOM,并提升列表渲染的性能。

  1. 识别元素: 通过 key,Vue 可以追踪每个列表中元素的身份。当数据发生变化时,Vue 使用 key 来确定哪些元素是新创建的、被修改的或被移除的,从而减少不必要的 DOM 操作。
  2. 复用元素: key 允许 Vue 在重新渲染列表时尽可能地复用已有的 DOM 元素。这样可以避免重新创建和销毁 DOM,从而提升性能和优化用户体验。
<div v-for="item in items" :key="item.id">
  <!-- 内容 -->
</div>

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

<MyComponent
  v-for="(item, index) in items"
  :item="item"
  :index="index"
  :key="item.id"
/>

期望

  1. 为 v-for 提供一个 key attribute
  2. key 为字符串或 number 基础类型,不要用对象

事件处理

监听事件

使用 v-on 指令 (简写为 @) 来监听 DOM 事件,并在事件触发时执行对应的 JavaScript。用法:v-on:click=“handler” 或 @click=“handler”。

事件处理器 (handler) 的值可以是:

  1. 内联事件处理器:事件被触发时执行的内联 JavaScript 语句 (与 onclick 类似)。
  2. 方法事件处理器:一个指向组件上定义的方法的属性名或是路径。

内联事件处理器

通常用于简单场景

const count = ref(0)
<button @click="count++">Add 1</button>
<p>Count is: {{ count }}</p>

方法事件处理器

const name = ref('Vue.js')

function greet(event) {
  alert(`Hello ${name.value}!`)
  // `event` 是 DOM 原生事件
  if (event) {
    alert(event.target.tagName)
  }
}

<!-- `greet` 是上面定义过的方法名 -->
<button @click="greet">Greet</button>

方法与内联事件判断:模板编译器会通过检查 v-on 的值是否是合法的 JavaScript 标识符或属性访问路径来断定是何种形式的事件处理器。举例来说,foo、foo.bar 和 foo[‘bar’] 会被视为方法事件处理器,而 foo() 和 count++ 会被视为内联事件处理器。

# 在内联处理器中调用方法
function say(message) {
  alert(message)
}

<button @click="say('hello')">Say hello</button>
<button @click="say('bye')">Say bye</button>


# 在内联事件处理器中访问事件参数
<!-- 使用特殊的 $event 变量 -->
<button @click="warn('Form cannot be submitted yet.', $event)">
  Submit
</button>

<!-- 使用内联箭头函数 -->
<button @click="(event) => warn('Form cannot be submitted yet.', event)">
  Submit
</button>

function warn(message, event) {
  // 这里可以访问原生事件
  if (event) {
    event.preventDefault()
  }
  alert(message)
}

修饰符

  1. 事件修饰符
    <!-- 单击事件将停止传递 -->
    <a @click.stop="doThis"></a>
    
    <!-- 提交事件将不再重新加载页面 -->
    <form @submit.prevent="onSubmit"></form>
    
    <!-- 修饰语可以使用链式书写 -->
    <a @click.stop.prevent="doThat"></a>
    
    <!-- 也可以只有修饰符 -->
    <form @submit.prevent></form>
    
    <!-- 仅当 event.target 是元素本身时才会触发事件处理器 -->
    <!-- 例如:事件处理器不来自子元素 -->
    <div @click.self="doThat">...</div>
    
    <!-- 添加事件监听器时,使用 `capture` 捕获模式 -->
    <!-- 例如:指向内部元素的事件,在被内部元素处理前,先被外部处理 -->
    <div @click.capture="doThis">...</div>
    
    <!-- 点击事件最多被触发一次 -->
    <a @click.once="doThis"></a>
    
    <!-- 滚动事件的默认行为 (scrolling) 将立即发生而非等待 `onScroll` 完成 -->
    <!-- 以防其中包含 `event.preventDefault()` -->
    <div @scroll.passive="onScroll">...</div>
    
  2. 按键修饰符
    <!-- 仅在 `key` 为 `Enter` 时调用 `submit` -->
    <input @keyup.enter="submit" />
    
    <!-- 仅会在 $event.key 为 'PageDown' 时调用事件处理 -->
    <input @keyup.page-down="onPageDown" />
    
    Vue 为一些常用的按键提供了别名:
    .enter
    .tab
    .delete (捕获“Delete”和“Backspace”两个按键)
    .esc
    .space
    .up
    .down
    .left
    .right
    
    # 系统按键修饰符
    .ctrl
    .alt
    .shift
    .meta # 在 Mac 键盘上,meta 是 Command 键 (⌘)。在 Windows 键盘上,meta 键是 Windows 键 (⊞)。
    
    <!-- Alt + Enter -->
    <input @keyup.alt.enter="clear" />
    
    <!-- Ctrl + 点击 -->
    <div @click.ctrl="doSomething">Do something</div>
    
    # 修饰符允许控制触发一个事件所需的确定组合的系统按键修饰符
    <!-- 当按下 Ctrl 时,即使同时按下 Alt 或 Shift 也会触发 -->
    <button @click.ctrl="onClick">A</button>
    
    <!-- 仅当按下 Ctrl 且未按任何其他键时才会触发 -->
    <button @click.ctrl.exact="onCtrlClick">A</button>
    
    <!-- 仅当没有按下任何系统按键时触发 -->
    <button @click.exact="onClick">A</button>
    
  3. 鼠标按键修饰符
    .left
    .right
    .middle
    

表单输入绑定​

<input
  :value="text"
  @input="event => text = event.target.value">

# 简化为
<input v-model="text">

# 基本用法
<p>Message is: {{ message }}</p>
<input v-model="message" placeholder="edit me" />

# 多行文本
<span>Multiline message is:</span>
<p style="white-space: pre-line;">{{ message }}</p>
<textarea v-model="message" placeholder="add multiple lines"></textarea>

# 复选框
<input type="checkbox" id="checkbox" v-model="checked" />
<label for="checkbox">{{ checked }}</label>

<!-- 值绑定 -->
<input
  type="checkbox"
  v-model="toggle"
  true-value="yes"
  false-value="no" />
<input
  type="checkbox"
  v-model="toggle"
  :true-value="dynamicTrueValue"
  :false-value="dynamicFalseValue" />

# 多个复选框
const checkedNames = ref([])

<div>Checked names: {{ checkedNames }}</div>
<input type="checkbox" id="jack" value="Jack" v-model="checkedNames">
<label for="jack">Jack</label>
<input type="checkbox" id="john" value="John" v-model="checkedNames">
<label for="john">John</label>
<input type="checkbox" id="mike" value="Mike" v-model="checkedNames">
<label for="mike">Mike</label>

# 单选按钮
<div>Picked: {{ picked }}</div>

<input type="radio" id="one" value="One" v-model="picked" />
<label for="one">One</label>

<input type="radio" id="two" value="Two" v-model="picked" />
<label for="two">Two</label>

# 选择器
<div>Selected: {{ selected }}</div>

<select v-model="selected">
  <option disabled value="">Please select one</option>
  <option>A</option>
  <option>B</option>
  <option>C</option>
</select>

# 多选,值绑定到一个数组
<div>Selected: {{ selected }}</div>

<select v-model="selected" multiple>
  <option>A</option>
  <option>B</option>
  <option>C</option>
</select>

<select v-model="selected">
  <!-- 内联对象字面量 -->
  <option :value="{ number: 123 }">123</option>
</select>

修饰符

<!-- 在 "change" 事件后同步更新而不是 "input" -->
<input v-model.lazy="msg" />

<!-- 让用户输入自动转换为数字 -->
<input v-model.number="age" />

<!-- 默认自动去除用户输入内容中两端的空格 -->
<input v-model.trim="msg" />