本文共 8863 字,大约阅读时间需要 29 分钟。
{ { message }}
和 React 一样,Vue 通过虚拟 DOM 对元素进行更高效管理,而不同于 React 的 JSX,Vue 通过元素、JS 分离的模板语法进行开发。上述是一个简单的 Vue 示例,通过 el
绑定元素、通过 data
中的 message
实现从存储到展示的单向数据流(一旦 message
发生变化,元素中的值(即视图)也会立刻变化)。
当一个 Vue 实例被创建时,data
中所有的属性将被加到 Vue 的响应式系统中,当属性值发生变化,视图立即会发生响应(作出更新)。
需要注意的是,data
总是一个对象,用来存储应用的诸多状态。 可以通过 $
前缀访问诸如 data、methods 等的属性:
var vm = new Vue({ el: '#app', data: { a: 1 }})vm.$data === { a: 1 } // => truevm.$el === document.getElementById('app') // => truevm.a = 2
顾名思义,methods
用以存放应用中的一些方法,你比如:
其中,绑定事件时,也可以 v-on:click="increase()"
,毕竟是 Vue。
另外,由于用到了 this
,于是 methods 里的方法不能写箭头函数,除了上述的写法,也可以:
methods: { increase: function(){ this.counter += 1}, decrease: function(){ this.counter -= 1}}
虽然模板中可以写表达式,但是当逻辑逐渐复杂起来的时候,出于易读、好维护的角度,我们更希望通过 带返回的函数 的形式,将这些逻辑组织起来,computed
便是为此而生,你比如:
{ { message.split('').reverse().join('') }}
模板中包含了一段关于反转字符串的逻辑,通过 computed
,虽然本质上一个意思,但不论是复用还是维护的时候,都非常方便:
{ { reversedMessage }}
另外,不同于 methods 中的方法,computed 基于响应式依赖进行缓存,无需显式调用,当所依赖的状态变量(例子中的 message
)发生变化时,自动更新计算值。
比起将 computed 中的属性看做方法,把之理解为与 data 中同等的状态变量更合适。事实上,默认情况下为 computed 中属性提供的方法(上述 reversedMessage 对应的方法),会被作为该属性的 getter 方法(一般也只需要 getter 方法),在必要的时候,我们可以显式地提供(同时或者只一者)setter、getter 方法:
firstName: { { firstName }} lastName: { { lastName }} fullName: { { fullName }}
当为 fullName 赋值时,set
内逻辑将被执行,即 firstName、lastName 会同步更新。
上述 computed 中提到,依赖某个变量的状态自动更新计算值,实际上 Vue 提供了另一种更通用地、对属性值进行监听的方式:watch
。不似 computed 侧重点在于自动更新计算值,其作用侧重于自动回调。说得更明白些,当一个状态变量(或多个)的改变,引起另一个状态变量的改变时,应优先选用 computed,此外的情景才应当选择 watch。
一个化简为繁的例子如下:
{ { fullName }}
Vue 应用的生命周期如下(官网):
针对不同的阶段,框架提供了一些所谓的生命周期钩子,比如 created
,可用来在实例被创建后执行一些定制化的初始逻辑。从写法上看,这些钩子们和 data
是同级属性,此外还包括 mounted
、updated
、destroyed
。
此间的注意点依然是不要将逻辑写成箭头函数,其原因仍然是 js 中 this 上下文在普通函数、箭头函数中有所差异。
v-bind 用于绑定一些 html attribute,你比如 title、id、class、style、href 等等等等:
{ { message }}
绑定 class 时:
{ { message }}
绑定 style:
{ { message }}
v-bind:attr
可以缩写为 :attr
:
数组语法:
用于监听 DOM 事件,你比如:
当然,直接写表达式也是支持的:
很多时候,还需要获取到原生 DOM 事件的 event 参数:
此外,vue 支持对事件进行修饰:
stopPropagation
)修饰语法是支持串联的,你比如:
特殊地,修饰可以是按键,你比如:
特殊的特殊地,可以将点击事件与按键(经过探究仅限于系统按键,诸如 meta、shift 等等)事件串联,实现对诸如“在按下 shift 的情况下点击鼠标左键”的响应:
其中,值得一提的是,绑定 click 事件时可以修饰以 .left
、middle
、right
,分别用以仅响应鼠标左、中、右键。
此外,2.5.0 新增了 .exact
修饰,用于配合系统修饰(meta、ctrl、shift等)使用,表示目标按键有且仅有。你比如按且仅按下 shift、鼠标左键时才响应可以描述为:
另外,v-on:event
可以缩写为 @event
,你比如:
v-if
用于条件性渲染,当条件为 truthy 值的时候元素才会被渲染。你比如:
foo
v-if
之后可以接 v-else-if
:
foobar
当然,v-else
乃兵家必备:
foobarbaz
其中需要注意的是,v-else-if
、v-else
需要紧跟着 v-if
或者 v-else-if
。另外,v-show
也用来条件渲染,其与 v-if
区别在于,v-show
实际控制 css 属性 display
,当条件为 falsy 时,display 设置为 none
,元素虽不展示,但仍在内存中。相比之下,使用 v-if
时,只有当条件为 truthy 时,元素才会被渲染。
foo
通过 v-for
可以更直观地实现譬如 react 中:
{ aList.map(item=>{item}) }
为:
{ { item }}
若其中,aList
为一个朴实无华的字符串列表:['foo', 'bar', 'baz']
,二者最终都会被渲染成:
foobarbaz
v-model
用于诸如 input
、textarea
、select
等表单元素上,创建双向数据绑定(内存里值的改变 会引起 展示的更新,而因为交互产生的组件状态的变化 也会使得 内存中值的更新)。
不似 React,Vue 通过数据双向绑定(语法糖),为开发者屏蔽了组件事件、属性上的差异,你比如说在 React 中为 checkbox、textarea 分别建立双向数据流需要这样:
setChecked(checked === 'foo' ? '' : 'foo')} />
相应地,要声明两个状态变量:
const [checked, setChecked] = useState('');const [theText, setTheText] = useState ('');
而 Vue 中:
相比之下,Vue 更加简洁,同时不需要开发者自行处理诸如 onChange
等的事件,开发体验更加友好。
- 在使用
v-model
时,元素原生的 value、checked、selected 属性的值将会被忽略,元素经渲染后,属性的真实值仅来源于 Vue 实例(更具体而言,data 中)。- 而示例中,由于 input 的类型是 checkbox,故此时 value 属性,原生意义上本该不起什么作用,但 Vue 将之利用起来,用以判断 checkbox 是否被勾选。
Vue 在很多情况下会显得更加方便,主要是因为框架很多时候不仅实现了原子性的功能,还会在此基础上再兼容更多一些的情况。比如上述例子中,需要的是一组 checkbox,这时,在 React 中,可能需要这样做:
setCheckedList( checkedList.includes('foo') ? checkedList.filter((checked: string)=>checked!=='foo') : checkedList.concat('foo') ) } /> setCheckedList( checkedList.includes('bar') ? checkedList.filter((checked: string)=>checked!=='bar') : checkedList.concat('bar') ) } /> setCheckedList( checkedList.includes('baz') ? checkedList.filter((checked: string)=>checked!=='baz') : checkedList.concat('baz') ) } />
相应地,需要声明的状态变量变为(另一种做法是,声明三个状态变量,三个 input 分别处理事件,两种本质上是一个意思):
const [checkedList, setCheckedList] = useState([]);
而 Vue 中,则:
语法糖万岁。
另外,也可以对 v-model
进行一些修饰:
.lazy
- 由交互产生的元素值的变化,不立即更新到相关状态变量(比如汉字输入时,有拼音这个过程,而通过 lazy 可以忽略该过程,这样可以减少更新的次数,减少开销)。.number
- 事实上即便将 input 的 type 设为 number,读取元素值的时候,得到的依然是字符串。通过 .number
可以将元素值自动转化成数值(当值无法被 parseInt 的时候,则得到的仍然是原本的值)。.trim
- 该修饰用以过滤输入首尾的空白字符。表示元素只会被渲染一把,此后状态变量的变化不会引起之展示的更新。
表示与元素绑定的变量,是一段可解析的 html 字符串,进而渲染时会用解析后的该 html 替换使用 v-html
的元素。你比如:
div 部分会被渲染成:
foo
Vue 在更新展示时,本着提升效率以及减小开销的原则,会复用一些元素。而有的时候,我们不希望元素被复用(或者说我们希望它更新),此时便可以使用 key。你比如说:
这样当切换登录方式时,会发现此前的输入仍然在 input 元素中,这是不合理的,于是加入 key:
所谓 Vue 组件,即一个可复用的 Vue 实例,一个基本的组件声明示例如下:
上述组件,复用起来就像这样:
值得注意的是,组件的 data 不似普通实例那样直接是一个对象,组件的 data 应当是一个函数,通过返回值提供出一个对象。这是因为组件每复用一把,就相当于增加一个该组件的实例,data 直接对应一个对象会导致诸个组件的实例共用同一个响应状态,其中一个实例的状态变化会同步作用到其他组件上,大多数情况下这显然是不合理的。而通过函数,每个实例会维护各自的状态,互相独立。
有时,在实例化某个组件的时候,我们希望传入一些定制化的内容,一个简单的例子是某个用于表示问候的组件,在实例化时可能需要传入不同的问候语:
Vue.component('greeting', { template: 'Hello, Vue :)'})
props 便是为此而生的:
此外,我们可以在实例化组件时,借助 v-bind
将传入的内容绑定为动态变量。
有时,我们需要为组件内的一些元素(比如说 button )绑定一些方法,而这些方法通常又是与外层状态紧密相关的,这时我们可以使用 $emit
:
其中,需要注意的是,$emit
函数的实参可以说是字符串模板里的字符串模板,传入其中的函数名不能是驼峰式的命名,需要变成 kebab-case
的命名方式(短横线分隔命名法)。
而当我们监听到事件后,不只是想调用函数,还想向函数中传入一些由组件抛出的固定值(变化步长等)的时候,可以这样:
v-model
前文中有说到,v-model
通过语法糖为开发者屏蔽了各种表单元素属性、事件上的差异与细节,实际上,一般情况下(不修饰以 .lazy
),v-model
主要粘合的是元素的 value
以及 input
事件,这意思是:
与:
等价。
同理,只要我们的自定义组件暴露出了 value、以及 input 事件,那么我们便可以将 v-model
作用在之上:
有时,我们想要动态地向自定义的组件中再塞点什么,但在没有和组件商量好的情况下, 组件是不会答应的,你比如:
Want To Add More
Want To Add More
是不会被渲染出来的。这时我们可以通过 slot
(插槽)实现想要的效果:
More Added
slot
标签间的内容,将作为缺省值,当未提供具体内容时,直接显示:
此时,DefaultContent
将被显示。
顾名思义,具名插槽即是有名字的插槽。在某个自定义组件内,我们可能想在后继多处补充内容,为了让内容归到相应位置,我们需要给插槽一个名称。最典型的是 Web 中的 header、content 以及 footer。你比如:
header content 1
content 2
footercontent 3
其中,没有被 template
包裹的部分不论与其他 template
的位置关系,会被归到默认插槽(也就是组件模板中,插槽标签中没有提供 name 属性的插槽)中,当然显式把这些元素归到默认插槽中也是可以的:
content 1
content 2
content 3
此外,动态指令参数也可以用在 v-slot 上:
content 1
content 2
content 3
再外,具名插槽也是可以缩写的,用 #
:
header
而 default 插槽缩写时必须带上 default:
header
通过插槽的 Props,我们可以在外部访问组件的内部状态,其声明需要在 slot 元素上使用 v-bind
。与 v-bind
紧接的是我们在外部可使用的、该状态的别称,v-bind
其后的引号内为该状态在内部的名称:
{ { slotProps.userProp.lastName }}
当然,slotProps
是随便起名称,可以替换成任意名称,也完全可以解构使用:
{ { userProp.lastName }}
甚至也可以为读取不到内部属性的情况准备一个默认值:
{ { userProp.lastName }}
当组件中只有默认插槽时,可以将 v-slot
放在组件中(不必借助 template)使用,用于绑定一些动态变量,除了这种情况,v-slot
只能用在 template
元素上:
{ { msg }}
或者直接不写 default 也是 Okay 的:
{ { slotProps.message }}
有时,我们希望某处是个动态的组件(根据状态切换之实质),这时,我们可以使用 is
:
上述例子也表明 is 也可用于普通 HTML 元素之上(虽然有一些特殊之处,暂且不论)。
另外,在一些特殊 HTML 元素,诸如 ul、ol、table、select,从语法上来看,其中的元素的类型是有严格限制的,比如 ul 中 只能是 li 元素,此时也可以通过 is 来瞒天过海:
但这个限制烦恼,在以下的 Vue 编程中遇不到:
template:'...'
)<script type="text/x-template">
[待续]
转载地址:http://cqbws.baihongyu.com/