{{ }}
v-html
v-bind
(简写:
)<div id="app">
<p>{{ message }}p>
<div v-html="rawHtml">div>
<img :src="imageSrc">
div>
<p>{{ number + 1 }}p>
<p>{{ ok ? 'YES' : 'NO' }}p>
v-if指令根据表达式的真假值条件性地渲染元素,通过直接操作DOM树实现:
<div v-if="isLoggedIn">欢迎回来!div>
<div v-else>请先登录div>
实现原理:
特性 | v-if | v-show |
---|---|---|
DOM操作 | 销毁/重建 | display切换 |
初始渲染成本 | 低(不渲染) | 高(始终渲染) |
切换成本 | 高 | 低 |
生命周期 | 触发完整周期 | 不触发 |
适用场景 | 条件稳定 | 频繁切换 |
<template v-if="userType === 'admin'">
<AdminPanel />
<AuditLog />
template>
<div v-if="status === 'loading'">加载中...div>
<div v-else-if="status === 'error'">错误提示div>
<div v-else>正常内容div>
<keep-alive>
<component :is="currentComponent" v-if="showComponent">component>
keep-alive>
computed: {
shouldShow() {
return this.user.role === 'admin' &&
this.env === 'production' &&
Date.now() > this.expireTime
}
}
<li v-for="(item, index) in items" :key="item.id">
{{ index }}: {{ item.text }}
li>
<div v-for="(value, key, index) in obj">
{{ index }}. {{ key }}: {{ value }}
li>
<span v-for="n in 10">{{ n }}span>
为什么需要key?
错误示例分析:
<li v-for="(item, index) in items" :key="index">
正确实践:
<li v-for="item in items" :key="item.id">
响应式更新原理:
// Vue对数组方法的劫持
const arrayProto = Array.prototype
const arrayMethods = Object.create(arrayProto)
const methodsToPatch = [
'push', 'pop', 'shift', 'unshift',
'splice', 'sort', 'reverse'
]
methodsToPatch.forEach(method => {
const original = arrayProto[method]
def(arrayMethods, method, function mutator(...args) {
const result = original.apply(this, args)
const ob = this.__ob__
ob.dep.notify()
return result
})
})
特殊情况处理:
// 直接设置索引值
Vue.set(vm.items, indexOfItem, newValue)
// 或
vm.items.splice(indexOfItem, 1, newValue)
// 修改数组长度
vm.items.splice(newLength)
<virtual-scroll :items="largeList" :item-size="50">
<template v-slot="{ item }">
<div class="item">{{ item.content }}div>
template>
virtual-scroll>
// 使用Object.freeze
export default {
data() {
return {
bigList: Object.freeze(hugeArray)
}
}
}
computed: {
paginatedData() {
const start = (this.currentPage - 1) * this.pageSize
return this.allData.slice(start, start + this.pageSize)
}
}
嵌套列表渲染示例:
<ul v-for="category in categories" :key="category.id">
<li>{{ category.name }}li>
<ul>
<li v-for="product in category.products"
:key="product.id">
{{ product.name }} - \${{ product.price }}
li>
ul>
ul>
正确做法:
<template v-for="item in items">
<div v-if="item.isValid" :key="item.id">
{{ item.content }}
div>
template>
错误模式分析:
<div v-for="item in items" v-if="item.valid">div>
问题原因:v-for的优先级高于v-if,导致不必要的循环计算
<div :[dynamicAttr]="value">div>
<div v-bind="{ id: containerId, 'data-type': type }">div>
<child-component v-bind="$props">child-component>
对象语法:
<div :class="{ active: isActive, 'text-danger': hasError }">div>
数组语法:
<div :class="[baseClass, isActive ? activeClass : '']">div>
样式自动前缀处理:
<div :style="{ transform: rotateValue }">div>
<div style="transform: rotate(45deg); -webkit-transform: rotate(45deg);">div>
原生属性与绑定属性合并示例:
<input class="base-class" :class="dynamicClass">
<input class="base-class dynamic-class">
特殊属性处理:
<button :disabled="isDisabled">Submitbutton>
<svg :viewBox.camel="viewBox">svg>
<component :is="currentComponent" v-bind="componentProps">component>
computed: {
imgSrc() {
return require(`@/assets/${this.imgName}.png`)
}
}
语法糖本质:
<input
:value="searchText"
@input="searchText = $event.target.value"
>
<input v-model="searchText">
多选框:
<input type="checkbox" v-model="toggle" true-value="yes" false-value="no">
单选按钮组:
<input type="radio" v-model="picked" value="a">
<input type="radio" v-model="picked" value="b">
选择列表:
<select v-model="selected" multiple>
<option v-for="option in options" :value="option.value">
{{ option.text }}
option>
select>
Vue2实现:
// 子组件
export default {
props: ['value'],
methods: {
updateValue(newVal) {
this.$emit('input', newVal)
}
}
}
Vue3更新:
// 子组件
export default {
props: ['modelValue'],
emits: ['update:modelValue'],
methods: {
updateValue(newVal) {
this.$emit('update:modelValue', newVal)
}
}
}
核心修饰符:
自定义修饰符:
// 自定义capitalize修饰符
app.directive('model', {
inserted(el, binding, vnode) {
el.addEventListener('input', e => {
const modifiers = binding.modifiers
let value = e.target.value
if (modifiers.capitalize) {
value = value.charAt(0).toUpperCase() + value.slice(1)
}
vnode.context[binding.expression] = value
})
}
})
对象结构绑定:
<user-form v-model:name="user.name" v-model:email="user.email">user-form>
多v-model绑定(Vue3):
<modal-box v-model:visible="showModal" v-model:size="modalSize">modal-box>
<input v-model.debounce.500="searchQuery">
<button v-on:click="counter += 1">Add 1button>
<button @click="greet">Greetbutton>
<template v-slot:header>
<h1>标题内容h1>
template>
Vue.directive('focus', {
inserted: function (el) {
el.focus()
}
})
<li v-for="user in users" v-if="user.isActive">
{{ user.name }}
li>
<template v-for="user in users">
<li v-if="user.isActive">
{{ user.name }}
li>
template>
<div id="app">
<form @submit.prevent="addTodo">
<input
v-model.trim="newTodo"
placeholder="Add new todo"
>
<button type="submit">Addbutton>
form>
<ul v-if="todos.length">
<li
v-for="(todo, index) in filteredTodos"
:key="todo.id"
:class="{ completed: todo.completed }"
>
<input
type="checkbox"
v-model="todo.completed"
>
{{ index + 1 }}. {{ todo.text }}
<button @click="removeTodo(index)">×button>
li>
ul>
<p v-else>No todos found!p>
div>
<script>
new Vue({
el: '#app',
data: {
newTodo: '',
todos: []
},
computed: {
filteredTodos() {
return this.todos.filter(todo => !todo.completed)
}
},
methods: {
addTodo() {
if (this.newTodo) {
this.todos.push({
id: Date.now(),
text: this.newTodo,
completed: false
})
this.newTodo = ''
}
},
removeTodo(index) {
this.todos.splice(index, 1)
}
}
})
script>