Search K
Appearance
Appearance
📊 SEO元描述:2024年最新Vue.js常用指令教程,详解v-text、v-html、v-show、v-if、v-for、v-on、v-bind指令。包含完整指令示例,适合开发者快速掌握Vue.js指令开发。
核心关键词:Vue.js指令、v-text、v-html、v-show、v-if、v-for、v-on、v-bind、Vue指令系统
长尾关键词:Vue指令怎么用、Vue.js指令大全、Vue指令区别、Vue指令语法、Vue.js指令开发技巧
通过本节常用指令详解,你将系统性掌握:
Vue.js指令是什么?这是理解Vue.js模板功能的关键问题。Vue.js指令是带有v-前缀的特殊HTML属性,用于扩展HTML的功能,实现动态的DOM操作,也是声明式编程的重要体现。
💡 设计理念:Vue.js指令的设计目标是让开发者能够用声明式的方式描述DOM的动态行为,而不需要编写命令式的DOM操作代码。
v-text和v-html指令用于控制元素的文本内容,但有重要的安全差异:
<template>
<div class="text-directives-demo">
<h2>v-text和v-html指令示例</h2>
<!-- v-text指令 -->
<div class="directive-group">
<h3>v-text指令 - 安全文本渲染</h3>
<!-- 基本v-text用法 -->
<p v-text="plainText"></p>
<p v-text="htmlContent"></p> <!-- HTML会被转义 -->
<!-- v-text vs 插值表达式 -->
<div class="comparison">
<div>
<h4>v-text方式:</h4>
<p v-text="dynamicContent">这里的内容会被替换</p>
</div>
<div>
<h4>插值表达式方式:</h4>
<p>前缀 {{ dynamicContent }} 后缀</p>
</div>
</div>
<!-- 防止闪烁 -->
<div class="anti-flicker">
<h4>防止模板闪烁:</h4>
<p v-text="loadingText"></p> <!-- 不会显示未编译的模板 -->
<p>{{ loadingText }}</p> <!-- 可能会短暂显示{{ loadingText }} -->
</div>
<button @click="updateTexts">更新文本内容</button>
</div>
<!-- v-html指令 -->
<div class="directive-group">
<h3>v-html指令 - HTML内容渲染</h3>
<!-- 基本v-html用法 -->
<div class="html-examples">
<h4>安全的HTML内容:</h4>
<div v-html="safeHtmlContent"></div>
<h4>富文本内容:</h4>
<div v-html="richTextContent" class="rich-text"></div>
<h4>动态HTML生成:</h4>
<div v-html="generatedHtml"></div>
</div>
<!-- 安全警告示例 -->
<div class="security-warning">
<h4>⚠️ 安全警告示例:</h4>
<p>用户输入:<input v-model="userInput" placeholder="输入HTML内容"></p>
<p>直接渲染(危险):</p>
<div v-html="userInput" class="dangerous-content"></div>
<p>安全渲染:</p>
<div v-html="sanitizedUserInput" class="safe-content"></div>
</div>
<button @click="updateHtmlContent">更新HTML内容</button>
</div>
<!-- v-pre指令 -->
<div class="directive-group">
<h3>v-pre指令 - 跳过编译</h3>
<p v-pre>{{ 这里的内容不会被Vue编译 }}</p>
<p v-pre>v-text="{{ plainText }}"</p>
<div v-pre>
<span>{{ message }}</span>
<button @click="handleClick">点击</button>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'TextDirectivesDemo',
data() {
return {
plainText: '这是普通文本内容',
htmlContent: '<strong>这是HTML内容</strong><script>alert("XSS")</script>',
dynamicContent: '动态内容',
loadingText: '加载中...',
safeHtmlContent: '<strong>粗体文本</strong> 和 <em>斜体文本</em>',
richTextContent: `
<h4>文章标题</h4>
<p>这是一段<strong>重要</strong>的文本,包含<a href="#" onclick="return false;">链接</a>。</p>
<ul>
<li>列表项1</li>
<li>列表项2</li>
</ul>
`,
userInput: '<img src="x" onerror="alert(\'XSS攻击\')">',
htmlTemplates: [
'<div class="success">✅ 操作成功</div>',
'<div class="warning">⚠️ 请注意</div>',
'<div class="error">❌ 操作失败</div>'
],
currentTemplateIndex: 0
}
},
computed: {
generatedHtml() {
return this.htmlTemplates[this.currentTemplateIndex]
},
// 简单的HTML清理(实际项目中应使用专业库如DOMPurify)
sanitizedUserInput() {
return this.userInput
.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '')
.replace(/on\w+="[^"]*"/gi, '')
.replace(/javascript:/gi, '')
}
},
methods: {
updateTexts() {
this.plainText = `更新时间:${new Date().toLocaleTimeString()}`
this.dynamicContent = `随机数:${Math.floor(Math.random() * 1000)}`
this.loadingText = '内容已更新'
},
updateHtmlContent() {
this.currentTemplateIndex = (this.currentTemplateIndex + 1) % this.htmlTemplates.length
const colors = ['#42b983', '#e6a23c', '#f56c6c']
const messages = ['成功消息', '警告消息', '错误消息']
this.safeHtmlContent = `
<span style="color: ${colors[this.currentTemplateIndex]}; font-weight: bold;">
${messages[this.currentTemplateIndex]}
</span>
`
}
},
mounted() {
// 模拟异步加载
setTimeout(() => {
this.loadingText = '内容加载完成'
}, 2000)
}
}
</script>
<style scoped>
.text-directives-demo {
padding: 20px;
max-width: 800px;
margin: 0 auto;
}
.directive-group {
margin: 30px 0;
padding: 20px;
border: 1px solid #e0e0e0;
border-radius: 8px;
background-color: #fafafa;
}
.directive-group h3 {
margin-top: 0;
color: #42b983;
border-bottom: 2px solid #42b983;
padding-bottom: 10px;
}
.comparison {
display: flex;
gap: 20px;
margin: 15px 0;
}
.comparison > div {
flex: 1;
padding: 15px;
background-color: white;
border-radius: 4px;
border: 1px solid #ddd;
}
.anti-flicker {
background-color: #fff3cd;
padding: 15px;
border-radius: 4px;
margin: 15px 0;
}
.html-examples {
background-color: white;
padding: 15px;
border-radius: 4px;
margin: 15px 0;
}
.rich-text {
border: 1px solid #ddd;
padding: 15px;
border-radius: 4px;
background-color: #f9f9f9;
}
.rich-text h4 {
margin-top: 0;
color: #333;
}
.rich-text ul {
margin: 10px 0;
padding-left: 20px;
}
.security-warning {
background-color: #fef0f0;
padding: 15px;
border-radius: 4px;
border-left: 4px solid #f56c6c;
margin: 15px 0;
}
.dangerous-content {
background-color: #ffebee;
padding: 10px;
border: 1px solid #f56c6c;
border-radius: 4px;
margin: 5px 0;
}
.safe-content {
background-color: #e8f5e8;
padding: 10px;
border: 1px solid #4caf50;
border-radius: 4px;
margin: 5px 0;
}
.success {
color: #67c23a;
font-weight: bold;
}
.warning {
color: #e6a23c;
font-weight: bold;
}
.error {
color: #f56c6c;
font-weight: bold;
}
button {
margin: 10px 5px;
padding: 8px 16px;
border: 1px solid #42b983;
background-color: #42b983;
color: white;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background-color: #369870;
}
input {
padding: 8px 12px;
border: 1px solid #ddd;
border-radius: 4px;
width: 300px;
margin: 5px;
}
</style>v-show和v-if都用于条件渲染,但实现机制和适用场景不同:
<template>
<div class="conditional-directives-demo">
<h2>v-show和v-if指令对比</h2>
<!-- 控制面板 -->
<div class="control-panel">
<button @click="toggleShow">切换v-show ({{ showElement ? '显示' : '隐藏' }})</button>
<button @click="toggleIf">切换v-if ({{ ifElement ? '显示' : '隐藏' }})</button>
<button @click="toggleBoth">同时切换</button>
<p>切换次数:{{ toggleCount }}</p>
</div>
<!-- v-show示例 -->
<div class="directive-group">
<h3>v-show指令 - CSS显示控制</h3>
<!-- 基本v-show -->
<div v-show="showElement" class="demo-box show-demo">
<h4>v-show控制的元素</h4>
<p>这个元素通过CSS的display属性控制显示/隐藏</p>
<p>DOM元素始终存在,只是样式改变</p>
<p>创建时间:{{ showElementCreatedTime }}</p>
</div>
<!-- v-show性能测试 -->
<div class="performance-test">
<h4>v-show性能测试</h4>
<div v-show="showPerformanceTest" class="performance-box">
<p>频繁切换的内容 - 使用v-show更高效</p>
<div v-for="n in 100" :key="n" class="performance-item">
项目 {{ n }}
</div>
</div>
<button @click="togglePerformanceTest">快速切换测试</button>
</div>
</div>
<!-- v-if示例 -->
<div class="directive-group">
<h3>v-if指令 - 条件渲染</h3>
<!-- 基本v-if -->
<div v-if="ifElement" class="demo-box if-demo">
<h4>v-if控制的元素</h4>
<p>这个元素根据条件动态创建/销毁</p>
<p>条件为false时,DOM元素完全不存在</p>
<p>创建时间:{{ ifElementCreatedTime }}</p>
</div>
<!-- v-if with v-else -->
<div class="conditional-chain">
<h4>v-if / v-else-if / v-else链</h4>
<div v-if="userRole === 'admin'" class="role-admin">
<h5>管理员界面</h5>
<p>您有完全的系统访问权限</p>
<button>管理用户</button>
<button>系统设置</button>
</div>
<div v-else-if="userRole === 'editor'" class="role-editor">
<h5>编辑者界面</h5>
<p>您可以编辑和发布内容</p>
<button>创建文章</button>
<button>编辑内容</button>
</div>
<div v-else-if="userRole === 'viewer'" class="role-viewer">
<h5>查看者界面</h5>
<p>您只能查看内容</p>
<button>浏览内容</button>
</div>
<div v-else class="role-guest">
<h5>访客界面</h5>
<p>请登录以获取更多功能</p>
<button>登录</button>
<button>注册</button>
</div>
<div class="role-selector">
<label>选择角色:</label>
<select v-model="userRole">
<option value="admin">管理员</option>
<option value="editor">编辑者</option>
<option value="viewer">查看者</option>
<option value="guest">访客</option>
</select>
</div>
</div>
<!-- template with v-if -->
<div class="template-example">
<h4>template元素与v-if</h4>
<template v-if="showTemplateContent">
<h5>模板内容组</h5>
<p>这些元素被template包装</p>
<p>template本身不会渲染到DOM中</p>
<ul>
<li>列表项1</li>
<li>列表项2</li>
<li>列表项3</li>
</ul>
</template>
<button @click="showTemplateContent = !showTemplateContent">
{{ showTemplateContent ? '隐藏' : '显示' }}模板内容
</button>
</div>
</div>
<!-- 性能对比 -->
<div class="directive-group">
<h3>性能对比与选择指南</h3>
<div class="performance-comparison">
<div class="comparison-item">
<h4>v-show特点</h4>
<ul>
<li>✅ 切换成本低(只改变CSS)</li>
<li>✅ 适合频繁切换</li>
<li>❌ 初始渲染成本高</li>
<li>❌ 元素始终占用内存</li>
</ul>
</div>
<div class="comparison-item">
<h4>v-if特点</h4>
<ul>
<li>✅ 初始渲染成本低</li>
<li>✅ 条件为false时节省内存</li>
<li>✅ 支持v-else-if/v-else</li>
<li>❌ 切换成本高(创建/销毁DOM)</li>
</ul>
</div>
</div>
<div class="usage-guide">
<h4>使用指南</h4>
<div class="guide-item">
<strong>使用v-show的场景:</strong>
<ul>
<li>需要频繁切换显示状态</li>
<li>元素内容相对简单</li>
<li>切换性能要求高</li>
</ul>
</div>
<div class="guide-item">
<strong>使用v-if的场景:</strong>
<ul>
<li>条件很少改变</li>
<li>元素内容复杂,包含大量子组件</li>
<li>需要配合v-else使用</li>
<li>初始条件为false的可能性大</li>
</ul>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'ConditionalDirectivesDemo',
data() {
return {
showElement: true,
ifElement: true,
toggleCount: 0,
showPerformanceTest: true,
userRole: 'admin',
showTemplateContent: true,
showElementCreatedTime: new Date().toLocaleTimeString(),
ifElementCreatedTime: new Date().toLocaleTimeString()
}
},
methods: {
toggleShow() {
this.showElement = !this.showElement
this.toggleCount++
},
toggleIf() {
this.ifElement = !this.ifElement
this.toggleCount++
// 记录重新创建时间
if (this.ifElement) {
this.$nextTick(() => {
this.ifElementCreatedTime = new Date().toLocaleTimeString()
})
}
},
toggleBoth() {
this.showElement = !this.showElement
this.ifElement = !this.ifElement
this.toggleCount += 2
if (this.ifElement) {
this.$nextTick(() => {
this.ifElementCreatedTime = new Date().toLocaleTimeString()
})
}
},
togglePerformanceTest() {
// 快速切换测试
let count = 0
const interval = setInterval(() => {
this.showPerformanceTest = !this.showPerformanceTest
count++
if (count >= 10) {
clearInterval(interval)
}
}, 100)
}
},
watch: {
showElement(newVal) {
if (newVal) {
this.$nextTick(() => {
this.showElementCreatedTime = new Date().toLocaleTimeString()
})
}
}
}
}
</script>
<style scoped>
.conditional-directives-demo {
padding: 20px;
max-width: 900px;
margin: 0 auto;
}
.control-panel {
background-color: #f0f9ff;
padding: 20px;
border-radius: 8px;
margin-bottom: 20px;
text-align: center;
}
.directive-group {
margin: 30px 0;
padding: 20px;
border: 1px solid #e0e0e0;
border-radius: 8px;
background-color: #fafafa;
}
.directive-group h3 {
margin-top: 0;
color: #42b983;
border-bottom: 2px solid #42b983;
padding-bottom: 10px;
}
.demo-box {
padding: 20px;
margin: 15px 0;
border-radius: 8px;
border: 2px solid #ddd;
}
.show-demo {
background-color: #e8f5e8;
border-color: #4caf50;
}
.if-demo {
background-color: #fff3e0;
border-color: #ff9800;
}
.performance-test {
margin: 20px 0;
}
.performance-box {
max-height: 200px;
overflow-y: auto;
border: 1px solid #ddd;
padding: 10px;
background-color: white;
}
.performance-item {
padding: 2px 5px;
border-bottom: 1px solid #eee;
}
.conditional-chain {
background-color: white;
padding: 20px;
border-radius: 8px;
margin: 15px 0;
}
.role-admin { background-color: #ffebee; border-left: 4px solid #f44336; padding: 15px; }
.role-editor { background-color: #e8f5e8; border-left: 4px solid #4caf50; padding: 15px; }
.role-viewer { background-color: #fff3e0; border-left: 4px solid #ff9800; padding: 15px; }
.role-guest { background-color: #f3e5f5; border-left: 4px solid #9c27b0; padding: 15px; }
.role-selector {
margin-top: 20px;
padding: 15px;
background-color: #f5f5f5;
border-radius: 4px;
}
.template-example {
background-color: white;
padding: 20px;
border-radius: 8px;
margin: 15px 0;
}
.performance-comparison {
display: flex;
gap: 20px;
margin: 20px 0;
}
.comparison-item {
flex: 1;
background-color: white;
padding: 20px;
border-radius: 8px;
border: 1px solid #ddd;
}
.usage-guide {
background-color: white;
padding: 20px;
border-radius: 8px;
margin: 20px 0;
}
.guide-item {
margin: 15px 0;
padding: 15px;
background-color: #f9f9f9;
border-radius: 4px;
}
button {
margin: 5px;
padding: 8px 16px;
border: 1px solid #42b983;
background-color: #42b983;
color: white;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background-color: #369870;
}
select {
padding: 8px 12px;
border: 1px solid #ddd;
border-radius: 4px;
margin-left: 10px;
}
ul {
margin: 10px 0;
padding-left: 20px;
}
li {
margin: 5px 0;
}
</style>v-show vs v-if 核心区别:
💼 选择原则:频繁切换用v-show,条件稳定用v-if,需要else分支必须用v-if。
<template>
<div class="list-directives-demo">
<h2>v-for列表渲染指令</h2>
<!-- 数组渲染 -->
<div class="directive-group">
<h3>数组渲染</h3>
<!-- 基本数组渲染 -->
<div class="list-example">
<h4>基本数组渲染</h4>
<ul>
<li v-for="(item, index) in fruits" :key="item.id">
{{ index + 1 }}. {{ item.name }} - {{ item.color }}
</li>
</ul>
</div>
<!-- 复杂对象数组 -->
<div class="list-example">
<h4>复杂对象数组</h4>
<div class="user-list">
<div
v-for="user in users"
:key="user.id"
class="user-card"
>
<img :src="user.avatar" :alt="user.name" class="avatar">
<div class="user-info">
<h5>{{ user.name }}</h5>
<p>{{ user.email }}</p>
<div class="skills">
<span
v-for="skill in user.skills"
:key="skill"
class="skill-tag"
>
{{ skill }}
</span>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 对象渲染 -->
<div class="directive-group">
<h3>对象渲染</h3>
<div class="object-example">
<h4>遍历对象属性</h4>
<table class="property-table">
<tr v-for="(value, key, index) in userProfile" :key="key">
<td>{{ index + 1 }}</td>
<td>{{ key }}</td>
<td>{{ value }}</td>
</tr>
</table>
</div>
</div>
<!-- key的重要性 -->
<div class="directive-group">
<h3>key的重要性演示</h3>
<div class="key-demo">
<h4>有key vs 无key对比</h4>
<!-- 有key的列表 -->
<div class="key-example">
<h5>✅ 使用key(推荐)</h5>
<div
v-for="item in sortableItems"
:key="item.id"
class="sortable-item with-key"
>
<input :value="item.name" readonly>
<span>{{ item.name }}</span>
</div>
</div>
<!-- 无key的列表(演示用,实际不推荐) -->
<div class="key-example">
<h5>❌ 不使用key(不推荐)</h5>
<div
v-for="item in sortableItems"
class="sortable-item without-key"
>
<input :value="item.name" readonly>
<span>{{ item.name }}</span>
</div>
</div>
<button @click="shuffleItems">随机排序(观察差异)</button>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'ListDirectivesDemo',
data() {
return {
fruits: [
{ id: 1, name: '苹果', color: '红色' },
{ id: 2, name: '香蕉', color: '黄色' },
{ id: 3, name: '橙子', color: '橙色' }
],
users: [
{
id: 1,
name: '张三',
email: 'zhangsan@example.com',
avatar: 'https://via.placeholder.com/50',
skills: ['JavaScript', 'Vue.js', 'CSS']
},
{
id: 2,
name: '李四',
email: 'lisi@example.com',
avatar: 'https://via.placeholder.com/50',
skills: ['React', 'Node.js', 'MongoDB']
}
],
userProfile: {
name: '王五',
age: 28,
city: '北京',
profession: '前端工程师',
experience: '5年'
},
sortableItems: [
{ id: 1, name: '项目A' },
{ id: 2, name: '项目B' },
{ id: 3, name: '项目C' },
{ id: 4, name: '项目D' }
]
}
},
methods: {
shuffleItems() {
// Fisher-Yates洗牌算法
const items = [...this.sortableItems]
for (let i = items.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[items[i], items[j]] = [items[j], items[i]]
}
this.sortableItems = items
}
}
}
</script>
<style scoped>
.list-directives-demo {
padding: 20px;
max-width: 800px;
margin: 0 auto;
}
.directive-group {
margin: 30px 0;
padding: 20px;
border: 1px solid #e0e0e0;
border-radius: 8px;
background-color: #fafafa;
}
.user-list {
display: flex;
flex-direction: column;
gap: 15px;
}
.user-card {
display: flex;
align-items: center;
padding: 15px;
background-color: white;
border-radius: 8px;
border: 1px solid #ddd;
}
.avatar {
width: 50px;
height: 50px;
border-radius: 50%;
margin-right: 15px;
}
.user-info h5 {
margin: 0 0 5px 0;
color: #333;
}
.user-info p {
margin: 0 0 10px 0;
color: #666;
font-size: 14px;
}
.skills {
display: flex;
gap: 5px;
flex-wrap: wrap;
}
.skill-tag {
background-color: #42b983;
color: white;
padding: 2px 8px;
border-radius: 12px;
font-size: 12px;
}
.property-table {
width: 100%;
border-collapse: collapse;
background-color: white;
}
.property-table td {
padding: 10px;
border: 1px solid #ddd;
text-align: left;
}
.property-table tr:nth-child(even) {
background-color: #f9f9f9;
}
.key-demo {
background-color: white;
padding: 20px;
border-radius: 8px;
}
.key-example {
margin: 20px 0;
padding: 15px;
border: 1px solid #ddd;
border-radius: 4px;
}
.sortable-item {
display: flex;
align-items: center;
padding: 8px;
margin: 5px 0;
background-color: #f5f5f5;
border-radius: 4px;
}
.sortable-item input {
margin-right: 10px;
padding: 4px 8px;
border: 1px solid #ddd;
border-radius: 4px;
width: 100px;
}
.with-key {
border-left: 4px solid #4caf50;
}
.without-key {
border-left: 4px solid #f44336;
}
button {
margin: 10px 5px;
padding: 8px 16px;
border: 1px solid #42b983;
background-color: #42b983;
color: white;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background-color: #369870;
}
</style><template>
<div class="event-directives-demo">
<h2>v-on事件处理指令</h2>
<!-- 基本事件绑定 -->
<div class="directive-group">
<h3>基本事件绑定</h3>
<!-- 点击事件 -->
<div class="event-example">
<button v-on:click="handleClick">v-on:click</button>
<button @click="handleClick">@click (简写)</button>
<button @click="clickCount++">内联表达式</button>
<p>点击次数:{{ clickCount }}</p>
</div>
<!-- 事件参数传递 -->
<div class="event-example">
<h4>事件参数传递</h4>
<button @click="handleClickWithParams('参数1', $event)">
传递参数和事件对象
</button>
<p>最后点击信息:{{ lastClickInfo }}</p>
</div>
</div>
<!-- 事件修饰符 -->
<div class="directive-group">
<h3>事件修饰符</h3>
<!-- 阻止默认行为 -->
<div class="modifier-example">
<h4>.prevent - 阻止默认行为</h4>
<form @submit.prevent="handleSubmit">
<input v-model="formData" placeholder="输入内容">
<button type="submit">提交(不会刷新页面)</button>
</form>
<p>表单数据:{{ formData }}</p>
</div>
<!-- 阻止事件冒泡 -->
<div class="modifier-example">
<h4>.stop - 阻止事件冒泡</h4>
<div @click="handleOuterClick" class="outer-box">
外层容器
<div @click.stop="handleInnerClick" class="inner-box">
内层容器(点击不会冒泡)
</div>
</div>
<p>事件日志:{{ eventLog.join(' → ') }}</p>
</div>
<!-- 键盘修饰符 -->
<div class="modifier-example">
<h4>键盘修饰符</h4>
<input @keyup.enter="handleEnter" placeholder="按回车键">
<input @keyup.esc="handleEscape" placeholder="按ESC键">
<input @keyup.ctrl.s="handleCtrlS" placeholder="按Ctrl+S">
<p>键盘事件:{{ keyboardEvent }}</p>
</div>
<!-- 鼠标修饰符 -->
<div class="modifier-example">
<h4>鼠标修饰符</h4>
<div class="mouse-area">
<div @click.left="handleLeftClick" class="mouse-button">左键点击</div>
<div @click.right.prevent="handleRightClick" class="mouse-button">右键点击</div>
<div @click.middle="handleMiddleClick" class="mouse-button">中键点击</div>
</div>
<p>鼠标事件:{{ mouseEvent }}</p>
</div>
<!-- 系统修饰符 -->
<div class="modifier-example">
<h4>系统修饰符</h4>
<div class="system-modifiers">
<button @click.ctrl="handleCtrlClick">Ctrl + 点击</button>
<button @click.shift="handleShiftClick">Shift + 点击</button>
<button @click.alt="handleAltClick">Alt + 点击</button>
<button @click.meta="handleMetaClick">Meta + 点击</button>
</div>
<p>系统修饰符事件:{{ systemEvent }}</p>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'EventDirectivesDemo',
data() {
return {
clickCount: 0,
lastClickInfo: '',
formData: '',
eventLog: [],
keyboardEvent: '',
mouseEvent: '',
systemEvent: ''
}
},
methods: {
handleClick() {
this.clickCount++
},
handleClickWithParams(param, event) {
this.lastClickInfo = `参数: ${param}, 事件类型: ${event.type}, 时间: ${new Date().toLocaleTimeString()}`
},
handleSubmit() {
alert(`表单提交: ${this.formData}`)
},
handleOuterClick() {
this.eventLog = ['外层点击']
},
handleInnerClick() {
this.eventLog = ['内层点击']
},
handleEnter() {
this.keyboardEvent = '回车键被按下'
},
handleEscape() {
this.keyboardEvent = 'ESC键被按下'
},
handleCtrlS() {
this.keyboardEvent = 'Ctrl+S被按下'
},
handleLeftClick() {
this.mouseEvent = '鼠标左键点击'
},
handleRightClick() {
this.mouseEvent = '鼠标右键点击'
},
handleMiddleClick() {
this.mouseEvent = '鼠标中键点击'
},
handleCtrlClick() {
this.systemEvent = 'Ctrl + 点击'
},
handleShiftClick() {
this.systemEvent = 'Shift + 点击'
},
handleAltClick() {
this.systemEvent = 'Alt + 点击'
},
handleMetaClick() {
this.systemEvent = 'Meta + 点击'
}
}
}
</script>
<style scoped>
.event-directives-demo {
padding: 20px;
max-width: 800px;
margin: 0 auto;
}
.directive-group {
margin: 30px 0;
padding: 20px;
border: 1px solid #e0e0e0;
border-radius: 8px;
background-color: #fafafa;
}
.event-example,
.modifier-example {
margin: 20px 0;
padding: 15px;
background-color: white;
border-radius: 8px;
border: 1px solid #ddd;
}
.outer-box {
padding: 20px;
background-color: #e3f2fd;
border: 2px solid #2196f3;
border-radius: 8px;
cursor: pointer;
}
.inner-box {
padding: 15px;
background-color: #fff3e0;
border: 2px solid #ff9800;
border-radius: 4px;
margin: 10px;
}
.mouse-area {
display: flex;
gap: 10px;
margin: 15px 0;
}
.mouse-button {
padding: 15px;
background-color: #f5f5f5;
border: 1px solid #ddd;
border-radius: 4px;
cursor: pointer;
text-align: center;
flex: 1;
}
.mouse-button:hover {
background-color: #e0e0e0;
}
.system-modifiers {
display: flex;
gap: 10px;
flex-wrap: wrap;
margin: 15px 0;
}
button {
margin: 5px;
padding: 8px 16px;
border: 1px solid #42b983;
background-color: #42b983;
color: white;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background-color: #369870;
}
input {
padding: 8px 12px;
border: 1px solid #ddd;
border-radius: 4px;
margin: 5px;
width: 200px;
}
form {
display: flex;
align-items: center;
gap: 10px;
}
</style>A: v-text适用于:1)防止模板闪烁;2)替换元素全部文本内容;3)避免与其他文本混合。插值表达式更灵活,可以与其他文本混合使用。
A: key帮助Vue识别列表项的身份,确保正确的DOM复用和更新。没有key可能导致:1)状态错乱;2)性能问题;3)动画异常。
A: 可以,如@click.stop.prevent。但要注意顺序,某些修饰符的顺序会影响行为。
A: 可以传递参数:@click="handleClick(item, index)",或使用事件委托在父元素上处理。
A: Vue 3中v-if优先级高于v-for,建议使用template元素或计算属性来避免同时使用。
"Vue.js的常用指令是构建动态用户界面的基础工具。通过掌握这些指令的使用方法和最佳实践,你已经具备了处理大部分前端交互需求的能力。记住,选择合适的指令、注意性能优化、遵循安全规范,这些都是成为Vue.js专家的重要步骤。下一步,我们将深入学习条件渲染的高级用法!"