应用 应用实例 应用通过 createApp 函数创建应用实例
1 2 3 4 5 import { createApp } from "vue" ;const app = createApp({ });
根组件 1 2 3 import App from "./app.vue" ;const app = createApp(App);
挂载应用 .mount方法返回根组件实例而非应用实例
dom 中的根组件模版根组件的模版也是组件本身的一部分,但也可以直接通过在挂载容器内编写模板来单独提供
当根组件没有设置 template 选项时,vue 将自动使用容器的 innerHTML 作为模版
dom 内模版通常用于无构建步骤的 vue 应用,其根模版可由服务端生成
应用配置 应用实例暴露一个 .config 对象,允许配置一些应用级的选项
1 2 3 4 5 6 7 app.config.errorHandler = error => { }; app.component("TodoDeleteButton" , TodoDeleteButton);
多个应用实例 应用实例不限制个数。可以在同一个页面创建多个 vue 应用,每个应用拥有自己的作用域
1 2 3 4 5 6 7 8 9 10 11 const app1 = createApp({ }); app1.mount("#container-1" ); const app2 = createApp({ }); app2.mount("#container-2" );
模版语法 文本插件 1 2 <span > Message: {{ message }}</span >
原始 html 指令 {{ }} 会被解释为纯文本,而不是 html.v-html 指令将解析 html
Attribute 绑定v-bind 指令可以响应式地绑定一个 attribute,如果绑定的值是 null 或 undefined,那么 attribute 将会从渲染的元素上移除
1 2 3 4 5 6 7 8 9 10 11 12 13 <div v-bind:id ="id" > </div > <div :id ="id" > </div > <div :id > </div > <div :id ="id" > </div > <div v-bind:id > </div > <div v-bind:id ="id" > </div >
布尔型 Attribute 1 2 3 4 5 6 7 8 <button :disabled ="disabled" > 按钮</button > <button disabled ="" > 按钮</button > <button :disabled ="true" > 按钮</button > <button disabled > 按钮</button >
动态绑定多个值 1 2 3 4 5 6 7 8 9 10 11 12 13 <div v-bind="attribute" ></div> <div :id="attribute.id" :class ="attribute.class" :style="attribute.style" ></div> const attribute = { id: "container" , class: "wrapper", style: "background-color: red" }
表达式 用于插值中 用于指令中 只支持表达式,代码片段是无效的 可绑定函数 1 2 3 4 5 6 7 8 9 10 11 {{ number + 1 }} {{ ok ? "yes" : "no" }} <div :id ="`list-${id}`" > </div > <time :title ="formatTitle(date)" > </time > {{ var a = 1 }}
受限制的全局访问 模版中的表达式会被沙盒化,仅能访问到有限的全局对象列表 没有显式包含在列表中的全局对象将不能在模板内表达式中访问,例如用户附加在 window 上的属性 可自行在 app.config.globalProperties 上显式的添加它们,以便于在组件中使用 指令 Directives 指令是带有 v- 前缀的特殊 attribute 例如 v-bind v-html v-for v-if v-on v-slot v-show
参数 Arguments 某些指令需要一个参数 在指令后通过冒号隔开作为标识
1 2 3 4 5 <a v-bind:href ="url" > 跳转</a > <a :href ="url" > 跳转</a > <a v-on:click ="clickEvent" > 跳转</a > <a @click ="clickEvent" > 跳转</a >
动态参数 指令需要的参数也可以使用表达式 需要包含到 [] 中
1 2 3 4 <a v-bind: [attributeName ]="url" > 跳转</a > <a : [attributeName ]="url" > 跳转</a > const attributeName = "href";
动态参数值的限制
动态参数中的表达式应该是一个字符串或者 null null 意为显式移除该绑定其他非字符串的值会触发警告 动态参数语法的限制
动态参数表达式因为某些字符的缘故有一些语法限制,比如空格和引号,在 HTML attribute 名称中都是不合法的 使用 dom 内嵌模版 ,避免在名称中使用大写字母,因为浏览器会强制将它转化为小写 1 2 3 4 5 <a : ['foo ' + bar ]="value" > jump</a > <a : [someAttr ]="value" > jump</a >
修饰符 修饰符是以 . 开头的特殊后缀,表明指令需要以一些特殊的方式绑定。例如 v-on 指令对触发的事件调用 event.preventDefault
1 <form @submit.prevent ="onSubmit" > ...</form >
响应式原理 proxy1 let proxy = new Proxy (target, handler);
target 要包装的对象,可以是任何东西,包括函数
handler 代理配置:带有 捕捉器
get 捕捉器用于读取 target 属性,set 捕捉器用于写入 target 的属性没有捕捉器时,所有 proxy 的操作都直接转发给 target 1 2 3 4 5 6 7 8 9 let target = {};let proxy = new Proxy (target, {});proxy.test = 5 ; console .log(target.test); console .log(proxy.test); for (let key in proxy) { console .log(key); }
proxy 是一种特殊的奇异对象,它没有自己的属性,如果 handler 为空,则透明地将操作转发为 target
对于大多数操作 js 规范中有一个所谓的内部方法,它描述最底层的工作方式。例如 [[Get]],用于读取属性的内部方法,[[Set]] 用于写入属性的内部方法。这些方法仅在规范中使用,不能直接通过方法名调用它们。proxy 捕捉器会拦截这些方法的调用
内部方法 Handler 方法何时触发 [[Get]]get读取属性 [[Set]]set写入属性 [[HasProperty]]hasin操作符[[Delete]]deletePropertydelete操作符[[Call]]apply函数调用 [[Construct]]constructnew操作符[[GetPrototypeOf]]getPrototypeOfObject.getPrototypeOf[[SetPrototypeOf]]setPrototypeOfObject.setPrototypeOf[[IsExtensible]]isExtensibleObject.isExtensible[[PreventExtensions]]preventExtensionsObject.preventExtensions[[DefineOwnProperty]]definePropertyObject.defineProperty Object.defineProperties[[GetOwnProperty]]getOwnPropertyDescriptorObject.getOwnPropertyDescriptor for...in Object.keys/values/entries[[OwnPropertyKeys]]ownKeysObject.getOwnPropertyNames Object.getOwnPropertySymbols for..in Object.keys/values/enties
不变量
js 强制 执行某些不变量
其中大多数用于返回值[[Set]] 如果值已成功写入,则返回 true 否则返回 false[[Delete]] 如果已完成删除该值,则返回 true 否则返回 false... 对于一些不变量应用代理 proxy 对象的 [[GetPrototypeOf]] 必须返回与应用于被代理对象的 [[GetPrototypeOf]] 相同的值。读取代理对象的原型必须返回始终返回被代理对象的原型 捕捉器可以拦截这些操作,但是也必须遵循上面的规则
get 捕捉器handler 必须有 get(target, property, receiver) 方法target 目标对象,该对象被作为第一个参数传递给 new Proxyproperty 目标属性名receiver 如果目标属性是一个 getter 访问器属性,则 receiver 就是本次读取属性所在的 this 对象。通常就是 proxy 对象本身代理应该在所有地方都完全代替目标对象,目标对象被代理后,不应该在引用目标对象 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 let numbers = [0 , 1 , 2 ];numbers = new Proxy (numbers, { get (target, prop ) { if (prop in target) { return target[prop]; } else { return NaN ; } } }); console .log(numbers[2 ]); console .log(numbers[3 ]); let dict = { "Hello" : "Hola" , "Bye" : "Adiós" }; dict = new Proxy (dict, { get (target, prop ) { if (prop in target) { return target[prop]; } else { return prop; } } }); console .log(dict["Hello" ]); console .log(dict["welcome" ]);
set 捕捉器handler 必须有一个 set(target, property, value, receiver) 函数target 目标对象 该对象被作为第一个参数传递给 new Proxyproperty 目标属性名称value 目标属性值receiver 与 get 捕捉器类型,仅与 setter 访问器属性相关如果写入操作成功,set 捕捉器应该返回 true,否则返回 false (触发 TypeError) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 let numbers = [];numbers = new Proxy (numbers, { set (target, prop, value ) { if (typeof value === "number" ) { target[prop] = value; return true ; } else { return false ; } } }); numbers.push(1 ); numbers.push(2 ); console .log(numbers.length, "length" );numbers.push("test" );
ownkeys 和 getOwnPropertyDescriptor 进行迭代Object.keys for...in 循环和大多数其他遍历对象属性的方法都使用内部方法 [[OwnPropertyKeys]] 由 ownKeys 捕捉器拦截来获取属性列表下面方法在细节上有所不同Object.getOwnPropertyNames(obj) 返回非 symbol 键Object.getOwnPropertySymbols(obj) 返回 symbol 键Object.keys/values() 返回带有 enumerable 标志的非 symbol 键/值for...in 循环遍历所有带有 enumerable 标志的非 symbol 键,以及原型对象的键 带有 enumerable 标志的键,如果返回一个对象不存在的键,那么为了检查它,方法会对每个属性调用内部方法 [[GetOwnProperty]] 来获取它的描述符,通过拦截 getOwnPropertyDescriptor 来返回 enumerable: true 的描述符来保证可以获取到对象不存在的键 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 let user = { name: "suxi" , age: 30 , _password: "***" }; user = new Proxy (user, { ownKeys (target ) { return Object .keys(target).filter(key => !key.startsWith("_" )); } }); for (let key in user) { console .log(key); } console .log(Object .keys(user)); console .log(Object .values(user)); let pig = { name: "peiqi" , age: 3 }; pig = new Proxy (pig, { ownKeys (target ) { return ["a" , "b" , "c" ]; } }); console .log(Object .keys(pig)); let person = { name: "Mark" }; person = new Proxy (person, { ownKeys (target ) { return ["a" , "b" ]; }, getOwnPropertyDescriptor (target, prop ) { return { enumerable: true , configurable: true } } }); console .log(Object .keys(person));
deleteProperty 和其他捕捉器的受保护属性一个普遍的约定,以下划线 _ 开头的属性和方法是内部的,不应从对象外部访问它们get 读取属性时抛出异常set 写入属性时抛出异常deleteProperty 删除属性时抛出异常ownKeys 在使用 for...in 和像 Object.keys 这样的方法时排除以 _ 开头的属性 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 let student = { name: "Aki" , _password: "secret" , checkPassword (value ) { return value === this ._password; } }; console .log(student._password);student = new proxy(student, { get (target, prop ) { if (prop.startsWith("_" )) { throw new Error ("Access denied" ); } let value = target[prop]; return (typeof value === "function" ? value.bind(target) : value); }, set (target, prop, value ) { if (prop.startsWith("_" )) { throw new Error ("Access denied" ); }; target[prop] = value; return true ; }, deleteProperty (target, prop ) { if (prop.startsWith("_" )) { throw new Error ("Access denied" ); }; delete target[prop]; return true ; }, ownKeys (target ) { return Object .keys(target).filter(key => !key.startsWith("_" )); } }); try { console .log(student._password); } catch (e) { console .error(e); } try { student._password = "abs" ; } catch (e) { console .error(e); } try { delete student._password; } catch (e) { console .error(e); } console .log(Object .keys(student));
类的私有属性
类的私有属性以 # 为前缀,它们无需代理,并且它们有其自身的问题,特别是,它们是不可继承的
带有 has 捕捉器的 in range 使用 in 操作符来检查一个数字是否在 range 范围内 has 捕捉器会拦截 in 调用has(target, property)1 2 3 4 5 6 7 8 9 10 11 12 13 let range = { start: 1 , end: 10 }; range = new Proxy (range, { has (target, prop ) { return prop >= target.start && prop <= target.end; } }); console .log(5 in range); console .log(33 in range);
包装函数 apply 也可以将代理包装在函数周围 apply(target, thisArg, args) 捕捉器能使代理以函数的方式被调用target 是目标对象thisArg 是 this 的值args 是参数列表1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 function delay (f, ms ) { return function ( ) { setTimeout (() => f.apply(this , arguments ), ms); }; } function sayHi (name ) { console .log(`Hello, ${name} ` ); } const sayHiF = delay(sayHi, 3000 );sayHiF("suxi" ); console .log(sayHi.length); console .log(sayHiF.length); function delay1 (f, ms ) { return new Proxy (f, { apply (target, thisArg, args ) { setTimeout (() => target.apply(thisArg, args), ms); } }) } function sayHi1 (name ) { console .log(`Hello, ${name} ` ); } const sayHi2 = delay(sayHi1, 3000 );console .log(sayHi2.length); sayHi2("suxi" );
ReflectReflect 是一个内建对象,可简化 Proxy 的创建,对象的内部方法 [[Get]] [[Set]] 等,都只是规范性的,不能直接调用,Reflect 对象使调用这些内部方法成为了可能。
操作 Reflect调用内部方法 obj[prop]Reflect.get(obj, prop)[[Get]]obj[prop] = valueReflect.set(obj, prop, value)[[Set]]delete obj[prop]Reflect.deleteProperty(obj, prop)[[Delete]]new F(value)Reflect.construct(F, value)[[Construct]].........
1 2 3 let user = {};Reflect .set(user, "name" , "suxi" );console .log(user);
Reflect 允许将操作符 new delete ... 作为函数 (Reflect.construct Reflect.deleteProperty ...) 执行调用。
对于每个可被 Proxy 捕获的内部方法,在 Reflect 中都有对应的方法,其名称和参数与 Proxy 捕获器相同 Reflect.get 读取一个对象属性Reflect.set 写入一个对象属性,如果写入成功则返回 true 否则返回 false
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 let person = { name: "aki" }; person = new Proxy (person, { get (target, prop, receiver ) { console .log(`Get ${prop} ` ); return Reflect .get(target, prop, receiver); }, set (target, prop, value, receiver ) { console .log(`Set ${prop} = ${value} ` ); return Reflect .set(target, prop, value, receiver); } }); let name = person.name;person.name = "peiqi" ;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 let pig1 = { _name: "peiqi" , get name () { return this ._name; } }; let pig1Proxy = new Proxy (pig1, { get (target, prop ) { return target[prop]; } }); let pig2 = { __proto__: pig1Proxy, _name: "suxi" }; console .log(pig2.name); let pig2Proxy = new Proxy (pig1, { get (target, prop, recevier ) { return Reflect .get(target, prop, recevier); } }); let pig3 = { __proto__: pig2Proxy, _name: "suxi" }; console .log(pig3.name);
Proxy 的局限性代理提供了一种独特的方法,可以在最底层更改或调整现有对象的行为。但是并不完美,有局限性
内建对象: 内部插槽(Internal slot) 许多内建对象 Map Set Date Promise 等都使用了内部插槽。例如 Map 对象将项目 item 存储在 [[MapData]] 中。内建方法可以直接访问它们,而不通过 [[Get]]/[[Set]] 内部方法。所以 Proxy 无法拦截它们
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 let map1 = new Map ();let proxy1 = new Proxy (map1, {});try { proxy1.set("test" , 1 ); } catch (e) { console .error(e); } let map2 = new Map ();let proxy2 = new Proxy (map2, { get (target, prop, recevier ) { let value = Reflect .get(...arguments); return typeof value === "function" ? value.bind(target) : value } }); proxy2.set("test" , 1 ); console .log(proxy2.get("test" ));
Array 没有内部插槽
内建 Array 没有使用内部插槽。由于历史原因,它出现很久以前,所以代理数组没有这种问题
私有字段 类的私有字段也会发生类似的情况
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class User { #name = "suxi" ; getName ( ) { return this .#name; } }; let user3 = new User();let proxyUser3 = new Proxy (user3, {});try { console .log(proxyUser3.getName()); } catch (e) { console .error(e); } let proxyUser4 = new Proxy (user3, { get (target, prop, receiver ) { let value = Reflect .get(...arguments); return typeof value === "function" ? value.bind(target) : value; } }); console .log(proxyUser4.getName());
proxy !== target代理和原始对象是不同的对象
1 2 3 4 5 6 7 8 9 10 11 12 13 let allUsers = new Set ();class Person { constructor (name ) { this .name = name; allUsers.add(this ); } } let person2 = new Person();console .log(allUsers.has(person2)); person2 = new Proxy (person2, {}); console .log(allUsers.has(person2));
Proxy 可以拦截许多操作符,例如 new in delete 但是没有办法拦截 === 一个对象只严格等于其自身,没有其他值
可撤销的 Proxy 一个可撤销的代理可以被禁用的代理。假设我们有一个资源,并且想随时关闭对资源的访问。可以包装成一个可撤销的代理 let {proxy, revoke} = Proxy.revocable(target, handler)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 let object = { data: "value data" } let { proxy, revoke } = Proxy .revocable(object, {});console .log(proxy.data);revoke(); try { console .log(proxy.data); } catch (e) { console .error(e) }
对 revoke() 的调用会从代理中删除对目标对象的所有内部引用,因此它们之间再无链接
revoke 和 proxy 是分开的,因此我们可以传递 proxy 同时 revoke 留在当前范围内
我们可以通过设置 proxy.revoke = revoke 来将 revoke 绑定 proxy
另一种选择是创建一个 WeakMap 其中 proxy 作为键,相应的 revoke 作为值,这样可以轻松的找到 proxy 对应的 revoke
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 let revokes = new WeakMap ();let obj = { data: "value" }; let { proxy, revoke } = Proxy .revocable(obj, {});revokes.set(proxy, revoke); let revoke = revokes.get(proxy);revoke(); try { console .log(proxy.data); } catch (e) { console .error(e); }
响应式基础 声明响应式状态 ref1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import { ref } from 'vue' import type { Ref } from 'vue' const count = ref(0 )console .log(count) console .log(count.value) count.value++ console .log(count.value) const day: Ref<number> = ref(1 )const year = ref<number>(2024 )const month = ref<number>()
在组件中访问 ref,需要在 setup() 函数中声明并返回它们,在模版中使用它们是不需要 .value 的,它会自动解包
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import { ref } from "vue" ;export default { setup ( ) { const count = ref(0 ); return { count }; } }; <template> <button @click="count++" >{{ count }}</button> </template>
<script setup> 中的顶层的导入、声明的变量和函数可在同一组件的模板中直接使用
深层响应式 ref 的值具有深层响应式,即便改变深层对象的属性,变化也会被检测到
DOM 更新时机修改响应式状态时,dom 会自动更新,但是更新不是同步的,vue 在更新周期中缓存所有状态的修改,确保不论进行多少次状态修改,每个组件只更新一次
1 2 3 4 5 6 7 8 import { nextTick, ref } from "vue" ;const count = ref(0 );async function doSometing ( ) { count.value++; await nextTick() }
reactivereactive 用来创建响应式对象,使对象本身具有响应式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import { reactive } from "vue" ;const state = reactive({ count : 1 });<template> <button @click="state.count++" >{{ state.count }}</button> </template> interface State { count: number; } const state: State = reactive({ count : 1 });
代理对象和原对象是不相等的,修改原对象不会触发更新,对于同一对象的 reactive 返回相同的代理对象,这个规则对于深层对象也同样适用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 let state = { count : 1 , b : { c : 2 } };let proxyState = reactive(state);proxyState === state reactive(state) === proxyState state.b === proxyState.b reactive(state.b) === proxyState.b const row = {};proxyState.row = row; proxyState.row === row proxyState.row === reactive(row)
reactive 局限性只能用于对象、数据 Map Set 等类型 不能轻易替换整个响应式对象,这样会导致失去响应式 将数据解构或传递属性时会失去响应式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 const state = reactive({ count : 1 });state = reactive({ count : 2 }); const { count } = state;count++; function fn (a ) { } fn(state.count)
ref 解包作为 reactive 对象的属性 一个 ref 会在作为响应式对象的属性被访问或修改时自动解包 只有当嵌套在一个深层响应式对象内时,才会发生 ref 解包。当其作为浅层响应式对象的属性被访问时不会解包 如果将一个新的 ref 赋值给一个关联了已有 ref 的属性,那么它会替换掉旧的 ref 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 const count = ref(0 );const state = reactive({ count }); console .info(state.count); count.value++; console .info(state.count); const count2 = ref(3 );state.count = count2; count.value++; console .info(count.value); console .info(state.count); const m1 = shallowReactive({ count });console .info(m1.count.value);
数组和集合 和 reactive 对象不同的是,当 ref 作为响应式数组或原生集合类型 (如 Map) 中的元素被访问时,它不会被解包
1 2 3 4 5 6 7 let b = reactive([ref(0 )]);console .info(b[0 ].value); let c = reactive(new Map ([ ["c" , ref(1 )] ])); console .info(c.get("c" ).value);
在模板中解包 在模板渲染上下文中,只有顶级的 ref 属性才会被解包 如果 ref 是文本插值的最终计算值 (即 {{ }} 标签),那么它将被解包 1 2 3 4 5 6 7 8 9 const fn1 = ref(0 );const fn2 = { a : ref(2 ) }<template> <div>{{ fn1 }}</div> <div>{{ fn2.a }}</div> <div>{{ fn2.a + 1 }}</div> <div>{{ fn2.a.value + 1 }}</div> </template>
计算属性 computed 返回一个计算属性 ref,模版中直接解包。vue 计算属性自动追踪响应式依赖,并缓存计算值computed 在首次访问时才会计算,后续仅会在其响应式依赖更新时才重新计算computed 相对方法来说,方法会在每次 render 的时候重复执行,而 computed 只会在依赖更新时重新执行1 2 3 4 5 6 7 8 9 10 11 12 13 import { computed, reactive } from "vue" ;const author = reactive({ name: "suxi" , books: [ "111" , "222" ] }); const publish = computed(() => { return author.books.length; });
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 import { computed, reactive } from "vue" ;const state = reactive({ count: 0 }); const publish = computed(() => { console .log("computed" ); return state.count; }); console .info("111111" );console .info(publish.value); console .info(publish.value); state.count++; console .info(publish.value); console .info("222222" ) const publish2 = computed<number>(() => { return state.count; });
可写的计算属性 计算属性默认是只读的 计算属性不应该有副作用,副作用应由监听器根据响应式状态的变更来创建副作用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import { ref, computed } from "vue" ;const firstName = ref("Pei" );const lastName = ref("Qi" );const fullName = computed({ get ( ) { return firstName + "-" + lastName; }, set (value ) { let [first, last] = value.split("-" ); firstName.value = first; lastName.value = last; } })
类和样式绑定 绑定对象、数组、字符串 对 class 绑定对象,如果对象 key 对应的 value 为真值,则添加值为 key 的类 对 style 绑定对象,则对象的 key 需要是 cssProperty 形式的字符串,否则会被忽略 对 class 绑定数组,则是数组或者对象数组则是对应值或对应 key 作为类名添加 对 style 绑定一个包含多个样式对象的数组,这些对象会合并后渲染到元素上 1 2 3 4 5 6 7 8 const errorClass = ref("validate-error" );const activeClass = ref("active" );<template> <div :class ="{ active: true }" ></div> <div :class ="[{ active: true }, errorClass]" ></div> <div :style="{ fontSize: '12px' }" ></div> </template>
组件中的 class 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <div class ="fn1 fn2" >ffff</div> <Component class ="fn3 fn4" ></Component> <div class ="fn1 fn2 fn3 fn4" >ffff</div> <div :class ="$attrs.class" >fff</div> <span>11111 </span> <Component class ="fn3 fn4" ></Component> <div class ="fn3 fn4" >fff</div> <span>11111 </span>
绑定内联样式 1 2 3 4 5 6 <template> <div :style="{ fontSize: '12px' }" >1 </div> <div :style="{ 'font-size': '22px' }" >2 </div> <div :style="[ {'font-size': '12px' }, { color: 'red' } ]" ></div> </template>
自动前缀,当 style 中使用了需要浏览器特殊前缀的 css 属性时,vue 会自动添加
样式多值,可以对一个属性提供多个(不同前缀的)值,数组会渲染浏览器支持的最后一个值
1 <div :style ="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }" > </div >
条件渲染 v-if v-else有条件的渲染一块内容 如果有多个块同时需要限制,可以将 v-if 使用在 template 上 v-else 只能配合 v-if v-else-if 使用,不能单独出现v-show有条件的显示一块内容 只是切换了 css 的 display 属性 v-show 不可使用在 template 上,也不可以和 v-else 配合使用区别 v-if 是按条件渲染,如果是 false 不会做任何事,切换时,条件区块中的监听器和子组件都会销毁和重建v-show 是一开始就渲染,切换时,只是切换了 css 的 display 属性v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销,频繁切换使用 v-show 较好,反之使用 v-if 较好注意 v-if 不推荐和 v-for 同时使用如果同时存在,v-if 优先级更高 列表渲染 v-forv-for="item in items" v-for="(item, index) in items" 迭代渲染也可以使用 of 替代 in v-for 遍历对象时,第一个参数时是 value 第二个参数是 key 第三个参数是 index v-for="(value, key, index) in obj" 遍历的顺序是基于 Object.values(obj) 的顺序决定的v-for 中使用范围值是从 1-n 的 v-for="item in 10" 1 - 10 初值是 1 而非 0当有多个块需要循环渲染时,可以在 template 上使用 v-for v-if 和 v-for 同时使用是不推荐的,如果同时使用 v-if 的优先级会更高,这意味着 v-if 中无法使用 v-for 中的变量 v-for="item in items" v-if="item.show"使用 v-for 时推荐使用 key 属性,template 上使用 v-for 时,key 应该被放置在这个 template 容器上,key 绑定的应该是基础类型的值 number 或 string 事件处理 内联事件处理器是在模版中直接使用表达式 方法事件处理器 v-on 一个方法 v-on="fo()" 是一个内联事件处理器在内联处理器中调用方法可以向方法传入自定义参数来代替原生事件对象 在内联事件中访问原生的 dom 事件,可以向处理器传入 $event 事件修饰符.stop 阻止事件冒泡 event.stopPropagation().prevent 阻止默认行为 event.preventDefault().self 仅当 event.target 是元素本身时才会触发事件处理器.capture 指向内部元素的事件,在被内部元素处理前,先被外部处理.once 事件处理器只触发一次.passive 首先触发默认行为,然后再触发事件处理器 所以和 .prevent 不能同时使用 按键修饰符@keyup.enter="enterEvent".enter 回车事件.tab tab 键事件.delete 捕获 delete 和 backspace 按键.esc.space.up.down.left.right 系统按键修饰符.ctrl.alt.shift.meta mac 中是 command window 中是 win .exact 修饰符鼠标按钮修饰符 限定为特定鼠标按键触发的事件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 const count = ref(0 );<button @click="count++" >Add</button> <p>Count -> {{ count }}</p> const add = (event ) => { count.value++; console .info(event.target.tagName); } <button @click="add" >Add</button> <p>Count -> {{ count }}</p> <button @click="count++" >Add +</button> <p>Count -> {{ count }}</p> <button @click="add" >Add -</button>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 <a @click.stop ="doThis" > </a > <form @submit.prevent ="onSubmit" > </form > <a @click.stop.prevent ="doThat" > </a > <form @submit.prevent > </form > <div @click.self ="doThat" > ...</div > <div @click.capture ="doThis" > ...</div > <a @click.once ="doThis" > </a > <div @scroll.passive ="onScroll" > ...</div > <input @keyup.enter ="submit" /> <input @keyup.alt.enter ="clear" /> <div @click.alt ="doSomething" > Do something</div > <button @click.alt ="onClick" > A</button > <button @click.alt.exact ="onCtrlClick" > A</button > <button @click.exact ="onClick" > A</button >
表单输入绑定 1 2 3 4 5 6 7 8 9 <input :value="text" @input="text = $event.target.value" /> <input v-model="text" />
v-model 用于不同类型的输入,textarea select 等元素text textarea 元素会绑定 value 并监听 input 事件checkbox radio 元素会绑定 checked 并监听 change 事件select 会绑定 value 并监听 change 事件v-model 会忽略表单元素上初始化的 value 等属性,它将始终将当前绑定的值作为数据的正确来源1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 <p>Message is: {{ message }}</p> <input v-model="message" placeholder="edit me" /> <span>Multiline message is:</span> <p style="white-space: pre-line;" >{{ message }}</p> <textarea v-model="message" placeholder="add multiple lines" ></textarea> <input type="checkbox" id="checkbox" v-model="checked" /> <label for ="checkbox" >{{ checked }}</label> const checkedNames = ref([])<div>Checked names: {{ checkedNames }}</div> <input type="checkbox" id="jack" value="Jack" v-model="checkedNames" /> <label for ="jack" >Jack</label> <input type="checkbox" id="john" value="John" v-model="checkedNames" /> <label for ="john" >John</label> <input type="checkbox" id="mike" value="Mike" v-model="checkedNames" /> <label for ="mike" >Mike</label> <div>Picked: {{ picked }}</div> <input type="radio" id="one" value="One" v-model="picked" /> <label for ="one" >One</label> <input type="radio" id="two" value="Two" v-model="picked" /> <label for ="two" >Two</label> <div>Selected: {{ selected }}</div> <select v-model="selected" > <option disabled value="" >Please select one</option> <option>A</option> <option>B</option> <option>C</option> </select> <div>Selected: {{ selected }}</div> <select v-model="selected" multiple> <option>A</option> <option>B</option> <option>C</option> </select>
1 2 3 4 5 6 7 8 9 10 <input type ="radio" v-model ="picked" value ="a" /> <input type ="checkbox" v-model ="toggle" /> <select v-model ="selected" > <option value ="abc" > ABC</option > </select >
修饰符 .lazy默认情况下,v-model 在 input 事件触发后更新数据,.lazy 可以修改为 change 事件触发后更新数据 .number自动转换为数字 如果值无法被 parseFloat 处理会返回原始值 number 修饰符会在输入框有 type="number" 时自动启用.trim生命周期 注册生命周期钩子