当前位置: 首页 > news >正文

OWL 的 t-* 与 Vue3 的 v-* 全面对比

📊 核心指令对比总览

功能

Vue 3

OWL

说明

条件渲染

v-if / v-else / v-show

t-if / t-else / t-elif

OWL 无 t-show

列表渲染

v-for

t-foreach / t-as

语法差异大

属性绑定

v-bind / :

t-att-* / t-attf-*

OWL 更灵活

事件绑定

v-on / @

t-on-*

类似但有区别

双向绑定

v-model

t-model

基本一致

插槽

<slot>

<t t-slot>

语法差异

引用

ref

t-ref

功能相同

组件

<Component>

t-component

OWL 用指令


🎯 1. 条件渲染对比

Vue 3

<template><!-- v-if / v-else-if / v-else --><div v-if="type === 'A'">类型 A</div><div v-else-if="type === 'B'">类型 B</div><div v-else>其他类型</div><!-- v-show(不移除 DOM,只切换 display) --><div v-show="isVisible">显示/隐藏</div>
</template>

OWL

<templates><!-- t-if / t-elif / t-else --><div t-if="state.type === 'A'">类型 A</div><div t-elif="state.type === 'B'">类型 B</div><div t-else="">其他类型</div><!-- ❌ OWL 没有 t-show!只能用 CSS 类或 style --><div t-att-class="state.isVisible ? 'show' : 'hide'">显示/隐藏</div><!-- 或者 --><div t-attf-style="display: {{ state.isVisible ? 'block' : 'none' }}">显示/隐藏</div>
</templates>

关键区别:

  • OWL 用 t-elif,Vue 用 v-else-if
  • OWL 没有 t-show,需要自己用 CSS 控制

🎯 2. 列表渲染对比(差异最大!)

Vue 3

<template><!-- 简单列表 --><div v-for="item in list" :key="item.id">{{ item.name }}</div><!-- 带索引 --><div v-for="(item, index) in list" :key="item.id">{{ index + 1 }}. {{ item.name }}</div><!-- 遍历对象 --><div v-for="(value, key) in user" :key="key">{{ key }}: {{ value }}</div><!-- 遍历数字 --><div v-for="n in 10" :key="n">{{ n }}</div>
</template>

OWL

<templates><!-- ✅ 简单列表(必须用 t-as 定义变量名) --><div t-foreach="state.list" t-as="item" t-key="item.id"><t t-esc="item.name"/></div><!-- ✅ 带索引(自动提供 item_index 变量) --><div t-foreach="state.list" t-as="item" t-key="item.id"><t t-esc="item_index + 1"/>. <t t-esc="item.name"/></div><!-- ✅ 遍历对象 --><div t-foreach="state.user" t-as="value" t-key="value_index"><t t-esc="value_index"/>: <t t-esc="value"/></div><!-- ✅ 遍历数字(需要用数组) --><div t-foreach="Array(10).fill(0).map((_, i) => i + 1)" t-as="n" t-key="n"><t t-esc="n"/></div>
</templates>

关键区别:

  1. OWL 必须t-as 指定变量名
  2. OWL 自动提供 item_index(变量名_index)
  3. OWL 自动提供 item_value(遍历对象时的 value)
  4. OWL 没有直接遍历数字的语法

🔍 OWL 循环的特殊变量:

<div t-foreach="state.list" t-as="item" t-key="item.id"><t t-esc="item"/>         <!-- 当前项 --><t t-esc="item_index"/>   <!-- 索引(从 0 开始) --><t t-esc="item_first"/>   <!-- 是否第一项(布尔值) --><t t-esc="item_last"/>    <!-- 是否最后一项(布尔值) --><t t-esc="item_parity"/>  <!-- "even" 或 "odd" --><t t-esc="item_size"/>    <!-- 列表总长度 -->
</div>

🎯 3. 属性绑定对比

Vue 3

<template><!-- 单个属性 --><img :src="imageUrl" :alt="imageAlt"><!-- class 绑定 --><div :class="{ active: isActive, 'text-danger': hasError }"></div><div :class="[activeClass, errorClass]"></div><!-- style 绑定 --><div :style="{ color: textColor, fontSize: fontSize + 'px' }"></div><!-- 多个属性 --><div v-bind="objectOfAttrs"></div>
</template>

OWL

<templates><!-- ✅ 单个属性(t-att-属性名) --><img t-att-src="state.imageUrl" t-att-alt="state.imageAlt"/><!-- ✅ 动态属性名(t-att) --><div t-att="{ 'data-id': state.id, 'data-name': state.name }"/><!-- ✅ class 绑定(需要手动拼接) --><div t-att-class="state.isActive ? 'active' : ''"/><div t-att-class="(state.isActive ? 'active' : '') + ' ' + (state.hasError ? 'text-danger' : '')"/><!-- ✅ style 绑定(用字符串模板 t-attf-*) --><div t-attf-style="color: {{ state.textColor }}; font-size: {{ state.fontSize }}px"/><!-- ✅ 字符串插值(t-attf-*,超级方便!) --><a t-attf-href="/user/{{ state.userId }}/profile">用户主页</a><img t-attf-src="/static/images/{{ state.imageName }}.png"/>
</templates>

关键区别:

  1. OWL 用 t-att-* 绑定单个属性
  2. OWL 用 t-attf-* 支持字符串插值(超级方便!
  3. OWL 的 class 和 style 需要手动拼接字符串
  4. OWL 用 t-att 可以绑定对象(多个属性)

🎯 4. 事件绑定对比

Vue 3

<template><!-- 基础事件 --><button @click="handleClick">点击</button><button v-on:click="handleClick">点击</button><!-- 事件修饰符 --><form @submit.prevent="onSubmit">提交</form><button @click.stop="handleClick">阻止冒泡</button><input @keyup.enter="onEnter"><!-- 传参 --><button @click="handleClick($event, 'arg1', 123)">点击</button><!-- 内联表达式 --><button @click="count++">增加</button>
</template>

OWL

<templates><!-- ✅ 基础事件(必须用 t-on-事件名) --><button t-on-click="handleClick">点击</button><!-- ❌ OWL 没有事件修饰符!需要在方法里手动处理 --><form t-on-submit="onSubmit">提交</form><button t-on-click="handleClick">阻止冒泡</button><!-- ✅ 传参(自动传入 event) --><button t-on-click="(ev) => this.handleClick(ev, 'arg1', 123)">点击</button><!-- ✅ 内联表达式 --><button t-on-click="() => this.state.count++">增加</button><!-- ✅ 键盘事件(需要手动判断按键) --><input t-on-keyup="(ev) => ev.key === 'Enter' && this.onEnter()"/>
</templates>

JavaScript 处理:

class MyComponent extends Component {setup() {this.state = useState({ count: 0 });}handleClick(ev, arg1, arg2) {// ❌ OWL 没有修饰符,需要手动处理ev.preventDefault();  // 阻止默认行为ev.stopPropagation(); // 阻止冒泡console.log('事件对象:', ev);console.log('参数:', arg1, arg2);}onSubmit(ev) {ev.preventDefault();  // 必须手动阻止表单提交// 处理逻辑}
}

关键区别:

  1. OWL 用 t-on-*,没有 @ 简写
  2. OWL 没有事件修饰符.prevent.stop.enter 等)
  3. OWL 的内联箭头函数需要显式 this
  4. OWL 的 XML 中 && 要写成 &&

🎯 5. 双向绑定对比

Vue 3

<template><!-- 基础用法 --><input v-model="message"><!-- 修饰符 --><input v-model.lazy="message">      <!-- 失焦时更新 --><input v-model.number="age">        <!-- 转数字 --><input v-model.trim="message">      <!-- 去空格 --><!-- 表单元素 --><textarea v-model="content"></textarea><select v-model="selected"><option value="A">选项 A</option></select><input type="checkbox" v-model="checked">
</template>

OWL

<templates><!-- ✅ 基础用法 --><input t-model="state.message"/><!-- ✅ 指定更新事件(类似 .lazy) --><input t-model="[state.message, 'blur']"/><!-- ❌ OWL 没有 .number 和 .trim 修饰符 --><!-- 需要手动处理 --><input t-model="state.age" t-on-input="(ev) => this.state.age = parseInt(ev.target.value) || 0"/><!-- ✅ 表单元素 --><textarea t-model="state.content"/><select t-model="state.selected"><option value="A">选项 A</option></select><input type="checkbox" t-model="state.checked"/>
</templates>

关键区别:

  1. OWL 用 t-model,功能基本一致
  2. OWL 可以指定更新事件:t-model="[变量, '事件名']"
  3. OWL 没有 .number.trim 修饰符

🎯 6. 内容渲染对比

Vue 3

<template><!-- 文本插值 --><div>{{ message }}</div><!-- HTML 渲染 --><div v-html="htmlContent"></div><!-- 纯文本 --><div v-text="message"></div>
</template>

OWL

<templates><!-- ✅ 文本插值(t-esc,会转义 HTML) --><div><t t-esc="state.message"/></div><!-- ✅ HTML 渲染(t-out / t-raw,不转义) --><div t-out="state.htmlContent"/><div t-raw="state.htmlContent"/>  <!-- 旧版本用 t-raw --><!-- ✅ 也可以用双花括号(Vue 风格) --><div><t t-esc="state.message"/></div>
</templates>

⚠️ 安全提示:

<!-- ✅ 安全(转义 HTML) -->
<div><t t-esc="state.userInput"/></div><!-- ⚠ 危险(可能 XSS 攻击) -->
<div t-out="state.userInput"/>

🎯 7. 插槽对比

Vue 3

<!-- 父组件 -->
<template><ChildComponent><template #header><h1>标题</h1></template><template #default><p>默认内容</p></template><template #footer="{ data }"><p>{{ data }}</p></template></ChildComponent>
</template><!-- 子组件 -->
<template><div><slot name="header"></slot><slot></slot><slot name="footer" :data="footerData"></slot></div>
</template>

OWL

<!-- 父组件 -->
<templates><t t-component="ChildComponent"><t t-set-slot="header"><h1>标题</h1></t><t t-set-slot="default"><p>默认内容</p></t><t t-set-slot="footer" t-slot-scope="scope"><p><t t-esc="scope.data"/></p></t></t>
</templates><!-- 子组件模板 -->
<templates><div><t t-slot="header"/><t t-slot="default"/><t t-slot="footer" data="props.footerData"/></div>
</templates>

🎯 8. 组件使用对比

Vue 3

<template><!-- 直接使用组件 --><ChildComponent :title="title":count="count"@update="handleUpdate"/><!-- 动态组件 --><component :is="currentComponent" />
</template><script setup>
import ChildComponent from './Child.vue';
</script>

OWL

<templates><!-- ✅ 使用 t-component 指令 --><t t-component="ChildComponent" title="state.title"count="state.count"t-on-update="handleUpdate"/><!-- ✅ 动态组件 --><t t-component="state.currentComponent"/>
</templates>
import { ChildComponent } from './Child';class ParentComponent extends Component {static components = { ChildComponent };  // ✅ 注册组件setup() {this.state = useState({title: "标题",currentComponent: ChildComponent});}
}

🎯 9. 引用 (ref) 对比

Vue 3

<template><input ref="inputRef"><ChildComponent ref="childRef">
</template><script setup>
import { ref, onMounted } from 'vue';const inputRef = ref(null);
const childRef = ref(null);onMounted(() => {inputRef.value.focus();childRef.value.someMethod();
});
</script>

OWL

<templates><input t-ref="inputRef"/><t t-component="ChildComponent" t-ref="childRef"/>
</templates>
import { Component, useRef, onMounted } from "@odoo/owl";class MyComponent extends Component {setup() {this.inputRef = useRef("inputRef");this.childRef = useRef("childRef");onMounted(() => {this.inputRef.el.focus();  // ✅ 用 .el 访问 DOMthis.childRef.comp.someMethod();  // ✅ 用 .comp 访问组件实例});}
}

关键区别:

  • OWL 的 ref 需要通过 .el(DOM 元素)或 .comp(组件实例)访问

🎯 10. 其他特殊指令

Vue 3 特有

<!-- 一次性插值 -->
<span v-once>{{ message }}</span><!-- 跳过编译 -->
<span v-pre>{{ 不会被编译 }}</span><!-- 隐藏未编译内容 -->
<div v-cloak>{{ message }}</div>

OWL 特有

<!-- ✅ t-set:定义变量 -->
<t t-set="fullName" t-value="state.firstName + ' ' + state.lastName"/>
<div><t t-esc="fullName"/></div><!-- ✅ t-call:调用模板(类似 Vue 的 include) -->
<t t-call="header_template"/><!-- ✅ t-debug:调试(打印变量) -->
<t t-debug=""/><!-- ✅ t-translation:禁用翻译 -->
<div t-translation="off">不翻译</div>

📊 完整实战对比

Vue 3 组件

<template><div class="user-list"><div v-if="loading">加载中...</div><div v-else><div v-for="(user, index) in filteredUsers" :key="user.id":class="{ active: selectedId === user.id }"@click="selectUser(user.id)"><img :src="user.avatar" :alt="user.name"><span>{{ index + 1 }}. {{ user.name }}</span><span v-if="user.isVip" class="vip-badge">VIP</span></div></div><input v-model.trim="keyword"@input="onSearch"placeholder="搜索用户"></div>
</template><script setup>
import { ref, computed } from 'vue';const loading = ref(false);
const keyword = ref('');
const selectedId = ref(null);
const users = ref([{ id: 1, name: '张三', avatar: '/avatar1.png', isVip: true },{ id: 2, name: '李四', avatar: '/avatar2.png', isVip: false }
]);const filteredUsers = computed(() => {return users.value.filter(u => u.name.includes(keyword.value));
});const selectUser = (id) => {selectedId.value = id;
};const onSearch = () => {console.log('搜索:', keyword.value);
};
</script>

OWL 组件

<templates><t t-name="UserList"><div class="user-list"><div t-if="state.loading">加载中...</div><div t-else=""><div t-foreach="filteredUsers" t-as="user"t-key="user.id"t-att-class="state.selectedId === user.id ? 'active' : ''"t-on-click="() => this.selectUser(user.id)"><img t-att-src="user.avatar" t-att-alt="user.name"/><span><t t-esc="user_index + 1"/>. <t t-esc="user.name"/></span><span t-if="user.isVip" class="vip-badge">VIP</span></div></div><input t-model="state.keyword"t-on-input="onSearch"placeholder="搜索用户"/></div></t>
</templates>
import { Component, useState } from "@odoo/owl";class UserList extends Component {static template = "UserList";setup() {this.state = useState({loading: false,keyword: '',selectedId: null,users: [{ id: 1, name: '张三', avatar: '/avatar1.png', isVip: true },{ id: 2, name: '李四', avatar: '/avatar2.png', isVip: false }]});}get filteredUsers() {return this.state.users.filter(u => u.name.includes(this.state.keyword.trim()));}selectUser(id) {this.state.selectedId = id;}onSearch() {console.log('搜索:', this.state.keyword);}
}

💡 核心差异总结

✅ OWL 的优势

  1. t-attf-* 字符串插值超级方便
  2. t-set 可以在模板中定义变量
  3. t-foreach 提供更多内置变量(first、last、parity)

❌ OWL 的劣势

  1. 没有 t-show(只有 t-if)
  2. 没有 事件修饰符.prevent.stop
  3. 没有 v-model 修饰符.number.trim
  4. class/style 绑定不如 Vue 灵活
  5. XML 语法更繁琐(&& 替代 &&

🎯 记忆技巧

Vue 3: v-*(v 开头)
OWL:   t-*(t 开头,t = template)条件: v-if    → t-if
列表: v-for   → t-foreach + t-as
属性: :attr   → t-att-attr
事件: @event  → t-on-event
双向: v-model → t-model
内容: {{ }}   → t-esc
http://www.dtcms.com/a/483957.html

相关文章:

  • C#项目连接S7-PLCSIM Advanced读写操作
  • Linux中的wheel介绍以及用法
  • 统计期刊介绍——Journal of Statistical Planning and Inference(JSPI)
  • 网站后台 刷新做网站完整过程
  • 泰州企业模板建站北京广告网站建设
  • MySQL8数据库高级特性-第二章
  • 【Python基础】Python路径操作全解析:os.path、glob与pathlib从入门到精通
  • 男人女人做羞羞事网站如皋网站建设招标
  • 在线相册jsp网站开发与设计徐州泰安抖音代运营
  • 网站seo优化步骤给我一个用c 做的网站
  • 重庆建设造价信息网站深圳带停机坪的别墅
  • 2026计算机毕设选题推荐:基于SpringBoot和Vue的电动车租赁平台系统(附源码和数据库)
  • 建湖做网站需要多少钱wordpress缓存图片
  • 济宁网站建设 中企动力临沂wordpress阻止访问
  • 南京建设网站要多少钱手机网站需要域名吗
  • 基于成功率的自适应差分进化 L-SRTDE 用于 CEC 2024 竞赛
  • 企业 办公 网站模板下载企业网站制作步骤
  • 网站建设大致分哪几块天津网站开发公司
  • 怎样查网站备案人的联系方式网站开发自学时间
  • 网站系统平台建设个人网站主页
  • 基于springboot的民谣网站的设计与实现
  • Linux系统新建用户登录只显示$简陋提示符 ,不显示用户名、主机名字、当前目录...
  • 阿里云网站托管公司软件网站建设
  • 安装网站时出现dir网站的常用技术有哪些
  • 十字链表的构建和操作
  • 中山做网站的公司哪家好佛山100强企业名单
  • 广州网站建设+美词有哪些网站做的好处
  • 网站查询功能怎么做php网页设计完整代码
  • Efficient Multi-Scale Attention Module with Cross-Spatial Learning 学习笔记
  • 国内专门做情侣的网站商城广州市建设工程信息管理平台