vue简写
- 属性
:id类似 :id="id" 或者 v-bind:id - 布尔属性,为真值或者空字符串,会出现在真实的dom上,为其它假值则会忽略;
- v-bind="obj" 绑定多个属性;
模板动态属性参数
<component :[attributeName]="attrValue" ></component>
<component v-bind:[attributeName]="attrValue" ></component>
不能是 :['foo' + attributeName],规范检查不过;:['foo' + attributeName]
模板动态事件参数
<component v-on:[eventName]="attrFn" ></component>
<component @[eventName]="attrFn" ></component>
注意事项:
- 动态参数[]方括号内不能有空格和引号,因为官方动态参数表达式存在些不为人知的问题,做了些语法限制,推荐使用计算属性替换;
- attributeName和eventName应该是字符串或者null,其它值会告警,null表示显示解除绑定;
- 如果属性是最终体现在DOM上的,则避免使用包含大写字母的属性名,因为浏览器最终会将其属性全部转化为小写;
js中模板字符串永远会执行toString抽象操作 ${{toString:()=>'baz'}} → "baz";
修饰符 Modifiers
指令的构成:v-on:submit.prevent="onSubmit" => 指令名称:参数.修饰符=值;
在vue模板里面每个绑定只能支持 单一表达式,也就是一段能被求值的JavaScript,一个简单的判断依据是是否可以写在return后面;
响应式基础
声明响应式状态,ref,reactive;
<script setup></script> 会在编译阶段将script标签内的内容解析包装,转化为setup函数,通过return将响应式数据暴露出去;最终在运行时是render函数作用域和setup函数作用域的结合,给模板提供数据输入;
为什么两种都存在?渐进迁移,消除样板,性能可预测;
其它表现形式:
1、setup + return render(无template); import { h, ref } from 'vue' export default { setup() { const count = ref(0) return () => h('button', { onClick: () => count.value++ }, count.value) } }
2、<script setup> + defineRender;
<script setup>
import { ref } from 'vue'
const count = ref(0)
defineRender(() => <button onClick={() => count.value++}>{count.value}</button>)
</script>
3、自定义hooks(逻辑复用)
// useCounter.js
export function useCounter() {
const count = ref(0)
const inc = () => count.value++
return { count, inc }
}
在 <script setup> 或 setup() 里调用即可
4、css变量驱动(**)
<template>
<div class="box"></div>
</template>
<script setup>
const color = ref('red')
</script>
<style>
.box { background-color: v-bind(color); }
</style>
vue中关于.value的一些疑惑
在vue中,单值的响应式都是采用ref(single-value)来初始化单值的响应式,在js中get,set操作都需要.value,而在模版里面都是不需要.value,在哪些场景里需要.value,哪些场景里不需要.value,这其中的原因是什么,作者这样设计的思想是什么,针对这块的问题,作者未来的思考是什么?
凡是在JavaScript运行期间访问ref对象时,需要加
.value,凡是在vue编译器或者运行时的场景,就省略.value;
需要写
.value场景,主要在js中:1、
<script setup>/setup()、store源码里;2、计算属性、监听器、生命周期
3、动态属性名访问,
refAry[i].value,索引访问无法被模板编译器静态分析;4、外部TS/JS模块;
不需要写
.value场景:1、模版文本,指令;比如:
{{ count }}v-modal="count"2、reactive 内嵌 ref;比如:
state.count(state 是 reactive,某属性是 ref)3、渲染函数(JSX);比如 { count }
4、Pinia里面永远没有
.value;在store源码里必须有.value,因为是js运行时环境,交给Pinia托管后再从Pinia读取的时候就没有了;
为什么这样设计?
1、基础类型无法代理;Proxy只能代理对象
2、明确区分“响应式对象”与“裸值”;通过 .value 让开发者显式地意识到“我正在触碰响应式边界”,避免把 ref 当普通变量随手解构、传递而丢失响应式连接。
3、向前兼容与迁移成本;如果全域隐式解包,旧代码、TS 类型、外部库都会面临“什么时候是对象,什么时候是值”的二义性问题; .value 是最小、最稳定的契约。
作者未来的思考与可能演进
1、
<script setup>语法糖实验;RFC reactivity transform 已提出ref:编译宏:ref: count = 0 // 编译后等价于 let count = ref(0) 并在所有使用处自动追加 .value2、类型层面的“自动解包” ;新版的 defineProps / defineEmits 已支持 ref 自动解包,组合式函数返回 MaybeRef 时,社区也在讨论是否让 TS 自动完成 UnwrapRef ,减少手写 .value 。
3、性能与可预测性的权衡;Evan 在多次直播明确:“我们不会为了少打 6 个字符而牺牲可预测性和调试体验;任何自动化解包都必须编译期完成,运行期绝不让隐式魔法发生。”
为什么Pinia能干掉所有.value?
1、Store 实例代理;Pinia 在创建 store 时,用 Proxy 把 state 做了递归脱 ref(unwrapRef),访问 counter.count 时,实际上访问的是内部 _state.count.value ,但代理层帮你把 .value 剥掉了。
2、只暴露“裸值”给外部
3、保持响应式链路
“Pinia 把 .value 吃掉了;除非你自己再用
storeToRefs把 ref 请回来。”