温故知新 Vue 3: Lesson 5
Conditional Rendering
v-if
The directive v-if is used to conditionally render a block. The block will only be rendered if the directive's expression returns a truthy value.
v-if 用来按条件渲染一个 block. 只有 v-if 的值为 true 时, block 才会被渲染.
<h1 v-if="awesome">Vue is awesome!</h1>
We can use v-if on a <template>
element, which serves as an invisible wrapper. The final rendered result will not include the <template>
element.
我们在 template 元素上面用 v-if, 最后的结果中不含 template 元素. 适用于想同时切换多个 Node, 又不希望出现 div.
<template v-if="ok">
<h1>Title</h1>
<p>Paragraph 1</p>
<p>Paragraph 2</p>
</template>
v-else and v-else-if
A v-else element must immediately follow a v-if or a v-else-if element - otherwise it will not be recognized.
如果要用 v-else 或 v-else-if, 前面必须是 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>
v-show
<h1 v-show="ok">Hello!</h1>
The difference is that an element with v-show will always be rendered and remain in the DOM; v-show only toggles the display CSS property of the element.
带有 v-show 的元素总是会被渲染, 并且留在 DOM, v-show 仅仅是切换了 display css 属性.
v-show doesn't support the <template>
element.
v-show 不支持 template 元素.
v-if vs v-show
v-if is "real" conditional rendering because it ensures that event listeners and child components inside the conditional block are properly destroyed and re-created during toggles.
v-if 是真正的条件渲染, 它保证了 conditional block 里面的 event listener 和 child component 会被 destroy 或 re-created. 从而会触发 lifecycle.
v-if is also lazy: if the condition is false on initial render, it will not do anything - the conditional block won't be rendered until the condition becomes true for the first time.
如果 v-if 一开始就是 false, 那么 block 里的内容不会被渲染.
v-if has higher toggle costs while v-show has higher initial render costs.
v-if 有更大的切换成本, 而 v-show 有更大的初始化成本.
prefer v-show if you need to toggle something very often, and prefer v-if if the condition is unlikely to change at runtime.
经常切换时, 优先使用 v-show, 很少切换或者需要触发 lifecycle hook 的, 使用 v-if.
v-if with v-for
Using v-if and v-for together is not recommended. When v-if and v-for are both used on the same element, v-if will be evaluated first.
不推荐在同一个元素上, 同时使用 v-if 和 v-for. 如果同时使用, v-if 优先. 为避免歧义, 请包上一层 template, template 上用 v-for, template 里面用 v-if.
<ul>
<template v-for="todo in todos">
<li v-if="todo.isFinished"></li>
</template>
</ul>
List Rendering
v-for with Array
Mapping an Array to Elements with v-for
使用 v-for 将 array 映射成 元素.
v-for 里面的格式是(item, index) in items
. block 里面可以访问循环的 variable item 或 index.
<ul id="array-with-index">
<li v-for="(item, index) in items">
{{ parentMessage }} - {{ index }} - {{ item.message }}
</li>
</ul>
Vue.createApp({
data() {
return {
parentMessage: "Parent",
items: [{ message: "Foo" }, { message: "Bar" }],
};
},
}).mount("#array-with-index");
也可以使用 for...of 这种形式.
<div v-for="item of items"></div>
v-for with an Object
v-for 里面的格式是(value, name, index) in object
. block 里面可以访问循环的 variable value, item 或 index.
<li v-for="(value, name, index) in myObject">
{{ index }}. {{ name }}: {{ value }}
</li>
Vue.createApp({
data() {
return {
myObject: {
title: "How to do lists in Vue",
author: "Jane Doe",
publishedAt: "2020-03-22",
},
};
},
}).mount("#v-for-object");
When iterating over an object, the order is based on the enumeration order of Object.keys(), which isn't guaranteed to be consistent across JavaScript engine implementations.
循环的顺序是根据 Object.keys() 的顺序来的. 不同的 js 引擎不一定一样.
Maintaining State
"in-place patch" update strategy. 补丁更新策略
If the order of the data items has changed, instead of moving the DOM elements to match the order of the items, Vue will patch each element in-place and make sure it reflects what should be rendered at that particular index.
如果 items 的顺序改变, 不是移动 DOM 元素以对应 item 的顺序, 而是将每个元素进行 patch, 从而对应那个 index 的 item 状态.
This default mode is efficient, but only suitable when your list render output does not rely on child component state or temporary DOM state (e.g. form input values).
这种默认的模式很高效, 但是只适合不依赖于 child component 或 临时 DOM 的状态. 比如里面有输入框, 这样可能导致输入框的数据不对应.
It is recommended to provide a key attribute with v-for whenever possible, unless the iterated DOM content is simple, or you are intentionally relying on the default behavior for performance gains.
v-for 最好结合 key 进行使用. 目前 vetur 这些插件, 如果不带 key 会报 warning.
<div v-for="item in items" :key="item.id">
<!-- content -->
</div>
Don't use non-primitive values like objects and arrays as v-for keys. Use string or numeric values instead.
key 不要使用 non-primitive 的值, 比如 object 或 array. 最好使用 string 或 numeric.
Array Change Detection
Mutation Methods
push();
pop();
shift();
unshift();
splice();
sort();
reverse();
Vue wraps an observed array's mutation methods so they will also trigger view updates
vue 包裹了 observed array 的 mutation method. 因而执行这些修改操作时, 能触发视图更新.
there are also non-mutating methods, e.g. filter(), concat() and slice(), which do not mutate the original array but always return a new array.
也有 filter(), concat() and slice() 这些 non-mutating methods. 不修改原先的 array, 而是返回一个新的.
When working with non-mutating methods, you can replace the old array with the new one:
当使用这些 non-mutating methods 时, 你应该用新的 array 去替换旧的 array.
Vue implements some smart heuristics to maximize DOM element reuse, so replacing an array with another array containing overlapping objects is a very efficient operation.
vue 会最大程度的利用已经有的 DOM 元素. 所以替换旧的 array, 也会根据数据差异情况来优化渲染.
Displaying Filtered/Sorted Results
<li v-for="n in evenNumbers">{{ n }}</li>
data() {
return {
numbers: [ 1, 2, 3, 4, 5 ]
}
},
computed: {
evenNumbers() {
return this.numbers.filter(number => number % 2 === 0)
}
}
display a filtered or sorted version of an array without actually mutating or resetting the original data.
显示过滤过 或 排序后的 array, 而不影响原始数据. 可以使用 computed property
<ul v-for="numbers in sets">
<li v-for="n in even(numbers)">{{ n }}</li>
</ul>
data() {
return {
sets: [[ 1, 2, 3, 4, 5 ], [6, 7, 8, 9, 10]]
}
},
methods: {
even(numbers) {
return numbers.filter(number => number % 2 === 0)
}
}
In situations where computed properties are not feasible (e.g. inside nested v-for loops), you can use a method.
如果不能使用 computed, 比如内嵌的 v-for, 可以使用 method. 所有可以使用 computed 的地方, 都可以用 method 替换. 只是 computed 带 cache.
v-for with a Range
<div id="range" class="demo">
<span v-for="n in 10">{{ n }} </span>
</div>
v-for on a <template>
<ul>
<template v-for="item in items">
<li>{{ item.msg }}</li>
<li class="divider" role="presentation"></li>
</template>
</ul>
v-for with v-if
it's not recommended to use v-if and v-for together.
不推荐 v-if 和 v-for 一起用
When they exist on the same node, v-if has a higher priority than v-for. That means the v-if condition will not have access to variables from the scope of the v-for:
当他们一次出现, v-if 优先级比 v-for 高, 因而 v-if 里面获取不到 v-for 的 variable item, index 这些. 可能导致应用报错.
<!-- This will throw an error because property "todo" is not defined on instance. -->
<li v-for="todo in todos" v-if="!todo.isComplete">{{ todo }}</li>
<!-- recommend case -->
<template v-for="todo in todos">
<li v-if="!todo.isComplete">{{ todo }}</li>
</template>
v-for with a Component
<my-component
v-for="(item, index) in items"
:item="item"
:index="index"
:key="item.id"
></my-component>
In order to pass the iterated data into the component, we should also use props
你必须把 item 通过 props 传入 组件