Vue3基础-模板语法

本文整理来自深入Vue3+TypeScript技术栈-coderwhy大神新课,只作为个人笔记记录使用,请大家多支持王红元老师。

模板语法

  • React的开发模式:
    React使用的jsx,所以对应的代码都是编写的类似于js的一种语法,之后通过Babel将jsx编译成 React.createElement 函数调用。

  • Vue也支持jsx的开发模式(后续有时间也会讲到):
    但是大多数情况下,使用基于HTML的模板语法,在模板中,允许开发者以声明式的方式将DOM和底层组件实例的数据绑定在一起。
    在底层的实现中,Vue将模板编译成虚拟DOM渲染函数,这个我会在后续给大家讲到。所以,对于学习Vue来说,学习模板语法是非常重要的。

Mustache双大括号语法

如果我们希望把数据显示到模板(template)中,使用最多的语法是 “Mustache”语法 (双大括号) 的文本插值。

并且我们前端提到过,data返回的对象是有添加到Vue的响应式系统 中,当data中的数据发生改变时,对应的内容也会发生更新。

当然,Mustache中不仅仅可以是data中的属性,也可以是一个JavaScript的表达式:

<template id="my-app">
  <!-- 1.mustache的基本使用 -->
  <h2>{{message}} - {{message}}</h2>
  <!-- 2.是一个表达式 -->
  <h2>{{counter * 10}}</h2>
  <h2>{{ message.split(" ").reverse().join(" ") }}</h2>
  <!-- 3.也可以调用函数 -->
  <!-- 可以使用computed(计算属性) -->
  <h2>{{getReverseMessage()}}</h2>
  <!-- 4.三元运算符 -->
  <h2>{{ isShow ? "哈哈哈": "" }}</h2>
</template>

下面这种写法是语句不是表达式,所以是错误的:

v-once指令

v-once用于指定元素或者组件只渲染一次,当数据发生变化时,元素或者组件以及其所有的子元素将视为静态内容并且跳过,该指令可以用于性能优化。

如果添加到父节点,那么所有的子节点也是只会渲染一次:

v-text指令

用于更新元素的 textContent,等价于"Mustache"语法,而且"Mustache"语法更灵活。

v-html

默认情况下,如果我们展示的内容本身是 html 的,那么vue并不会对其进行特殊的解析。如果我们希望这个内容被Vue可以解析出来,那么可以使用 v-html 来展示。

<template id="my-app">
  <div>{{msg}}</div>
  <div v-html="msg"></div>
</template>

<script src="../js/vue.js"></script>
<script>
  const App = {
    template: '#my-app',
    data() {
      return {
        msg: '<span style="color:red; background: blue;">哈哈哈</span>'
      }
    }
  }

效果如下:

v-pre

v-pre用于跳过元素和它的子元素的编译过程,显示原始的Mustache标签。
跳过不需要编译的节点,加快编译的速度。

<template id="my-app">
  <h2 v-pre>{{message}}</h2>
</template>

效果如下:

v-cloak

这个指令保持在元素上直到关联组件实例结束编译。

v-cloak 和 CSS 规则如 [v-cloak] { display: none } 一起用时,这个指令可以隐藏未编译的 Mustache 标签直到组件实例准备完毕,主要用于解决闪动问题,现在Vue3一般不会出现这个问题了。

<div> 不会显示,直到编译结束。

v-bind的绑定属性

前面讲的一系列指令,主要是将值插入到模板内容中。但是,除了内容需要动态来决定外,某些属性我们也希望动态来绑定。比如动态绑定a元素的href属性,动态绑定img元素的src属性。

绑定属性我们使用v-bind:,缩写:,用于动态地绑定一个或多个 attribute,或一个组件 prop 到表达式。

绑定属性我们使用v-bind:
缩写:":"
预期:any (with argument) | Object (without argument)
参数:attrOrProp (optional)
修饰符:
✔️.camel - 将 kebab-case attribute 名转换为 camelCase
用法:动态地绑定一个或多个 attribute,或一个组件 prop 到表达式

1. 绑定基本属性

v-bind用于绑定一个或多个属性值,或者向另一个组件传递props值(这个学到组件时再介绍),在开发中,有哪些属性需要动态进行绑定呢?还是有很多的,比如图片的链接src、网站的链接href、动态绑定一些类、样式等等。

v-bind有一个对应的语法糖,也就是简写方式,在开发中,我们通常会使用语法糖的形式,因为更简洁。

注意:Vue2 template模板中只能有一个根元素,Vue3 template模板中允许有多个根元素。

2. 绑定class

在开发中,有时候我们的元素class也是动态的,比如:当数据为某个状态时,字体显示红色,当数据另一个状态时,字体显示黑色。

绑定class有两种方式:对象语法,数组语法。

① 对象语法:我们可以传给 :class (v-bind:class 的简写) 一个对象,以动态地切换 class。

② 数组语法:我们可以把一个数组传给 :class,以应用一个 class 列表;

3. 绑定style

我们可以利用v-bind:style来绑定一些CSS内联样式,这是因为某些样式我们需要根据数据动态来决定,比如某段文字的颜色,大小等等。

CSS属性名可以用驼峰式 (camelCase) 或短横线分隔 (kebab-case,记得用引号括起来) 来命名。

绑定style有两种方式:对象语法,数组语法。

① 对象语法:

② 数组语法::style 的数组语法可以将多个样式对象应用到同一个元素上

style1Obj1: {
  color: 'red',
  fontSize: '30px'
},

4. 动态绑定属性

在某些情况下,我们属性的名称可能也不是固定的。
前面我们无论绑定src、href、class、style,属性名称都是固定的,如果属性名称不是固定的,我们可以使用 :[属性名]=“值” 的格式来定义,这种绑定的方式,我们称之为动态绑定属性。

data() {
  return {
    name: "cba",
    value: "kobe"
  }
}

5. 绑定一个对象

如果我们希望将一个对象的所有属性,绑定到元素上的所有属性,应该怎么做呢?非常简单,我们可以直接使用 v-bind 绑定一个对象。

如下:info对象会被拆解成div的各个属性。

data() {
  return {
    info: {
      name: "why",
      age: 18,
      height: 1.88
    }
  }
}

v-on绑定事件

前面我们绑定了元素的内容和属性,在前端开发中另外一个非常重要的特性就是交互。
在前端开发中,我们需要经常和用户进行各种各样的交互,这个时候,我们就必须监听用户发生的事件,比如点击、拖拽、键盘事件等等。

在Vue中如何监听事件呢?使用v-on指令。接下来我们来看一下v-on的用法:

v-on的使用:
缩写:@
预期:Function | Inline Statement | Object
参数:event
修饰符:
✔️.stop - 调用 event.stopPropagation()
✔️.prevent - 调用 event.preventDefault()
✔️.capture - 添加事件侦听器时使用 capture 模式
✔️.self - 只当事件是从侦听器绑定的元素本身触发时才触发回调
✔️.{keyAlias} - 仅当事件是从特定键触发时才触发回调
✔️.once - 只触发一次回调
✔️.left - 只当点击鼠标左键时触发
✔️.right - 只当点击鼠标右键时触发
✔️.middle - 只当点击鼠标中键时触发
✔️.passive - { passive: true } 模式添加侦听器
用法:绑定事件监听

1. v-on的基本使用

我们可以使用v-on来监听一下点击的事件:

v-on:click可以写成@click,是它的语法糖写法:

当然,我们也可以绑定其他的事件:

如果我们希望一个元素绑定多个事件,这个时候可以传入一个对象:

2. v-on参数传递

当通过methods中定义方法,以供@click调用时,需要注意参数问题:

情况一:如果该方法不需要额外参数,那么方法后的()可以不添加,并且方法的实现不用参数,直接就可以打印event。
情况二:如果需要同时传入某个参数和event时,可以通过$event传入事件,并且方法的实现必须按顺序写明参数。

3. v-on的修饰符

v-on支持修饰符,修饰符相当于对事件进行了一些特殊的处理:
✔️.stop - 调用 event.stopPropagation()
✔️.prevent - 调用 event.preventDefault()
✔️.capture - 添加事件侦听器时使用 capture 模式
✔️.self - 只当事件是从侦听器绑定的元素本身触发时才触发回调
✔️.{keyAlias} - 仅当事件是从特定键触发时才触发回调
✔️.once - 只触发一次回调
✔️.left - 只当点击鼠标左键时触发
✔️.right - 只当点击鼠标右键时触发
✔️.middle - 只当点击鼠标中键时触发
✔️.passive - { passive: true } 模式添加侦听器

@keyup.enter 代表enter键弹起的时候会调用onEnter方法,我们一般在方法里面获取输入的值:

onEnter() {
  console.log(event.target.value);
}

v-if 条件渲染

在某些情况下,我们需要根据当前的条件决定某些元素或组件是否渲染,这个时候我们就需要进行条件判断了。

Vue提供了下面的指令来进行条件判断:

v-if
v-else
v-else-if
v-show

下面我们来对它们进行学习。

1. v-if、v-else、v-else-if

v-if、v-else、v-else-if 用于根据条件来渲染某一块的内容,这些内容只有在条件为true时,才会被渲染出来,这三个指令与JavaScript的条件语句 if、else、else if 类似。

v-if 的渲染原理:v-if是惰性的,当条件为false时,其判断的内容完全不会被渲染或者会被销毁掉,当条件为true时,才会真正渲染条件块中的内容。

2. template元素

因为v-if是一个指令,所以必须将其添加到一个元素上,但是如果我们希望切换的是多个元素呢?
如果此时我们使用div包裹,div会被渲染到界面上来,但是我们并不希望div被渲染,这个时候,我们可以选择使用template,template元素可以当做不可见的包裹元素,并且 v-if 可以添加到 template 上,但是最终template不会被渲染出来,类似于小程序中的block。

3. v-show

v-show和v-if的用法看起来是一致的,也是根据一个条件决定是否显示元素或者组件。

4. v-show和v-if的区别

首先,在用法上的区别:

  • v-show是不支持template
  • v-show不可以和v-else一起使用

其次,本质的区别:

  • v-show元素无论是否需要显示到浏览器上,它的DOM实际都是有渲染的,只是通过CSS的display属性来进行切换,所以v-show是不支持template
  • v-if当条件为false时,其对应的原生压根不会被渲染到DOM中

开发中如何进行选择呢?

  • 如果我们的原生需要在显示和隐藏之间频繁的切换,那么使用v-show
  • 如果不会频繁的发生切换,那么使用v-if

v-for列表渲染

在真实开发中,我们往往会从服务器拿到一组数据,并且需要对其进行渲染。这个时候我们可以使用v-for来完成,v-for类似于JavaScript的for循环,可以用于遍历一组数据。

v-for基本使用

v-for的基本格式是"item in 数组",数组通常是来自data或者prop,也可以是其他方式,item是我们给每项元素起的一个别名,这个别名可以自定来定义。

我们知道,在遍历一个数组的时候会经常需要拿到数组的索引,如果我们需要索引,可以使用格式"(item, index) in 数组",注意顺序,数组元素项item在前面,索引项index在后面。

v-for支持的类型

  • v-for也支持遍历对象,并且支持有一二三个参数:
    一个参数:"value in object"
    二个参数:"(value, key) in object"
    三个参数:"(value, key, index) in object"

  • v-for同时也支持数字的遍历:
    每一个item都是一个数字,从1开始。

template元素

类似于v-if,你可以使用 template 元素来循环渲染一段包含多个元素的内容。
我们使用template来对多个元素进行包裹,而不是使用div来完成,因为div会被渲染,template不会被渲染。而且如果有ul,ul里面不推荐放div,只推荐放li。

数组更新检测

Vue 将被侦听的数组的变更方法进行了包裹,所以它们也将会触发视图更新,这些被包裹过的方法包括:

push()
pop()
shift()
unshift()
splice()
sort()
reverse()

上面的方法会直接修改原来的数组,所以视图会跟着更新。但是某些方法不会替换原来的数组,而是会生成新的数组,比如 filter()、concat() 和 slice(),这时候我们可以通过重新赋值的方式触发视图更新,如下:

this.movies = this.movies.filter(item => item.length > 2);

v-for中的key是什么作用?

在使用v-for进行列表渲染时,我们通常会给元素或者组件绑定一个key属性。

这个key属性有什么作用呢?
我们先来看一下官方的解释:key属性主要用在Vue的虚拟DOM算法,在新旧nodes对比时辨识VNodes。如果不使用key,Vue会使用一种最大限度减少动态元素并且尽可能的尝试就地修改/复用相同类型元素的算法,而使用key时,它会基于key的变化重新排列元素顺序,并且会移除/销毁key不存在的元素。

官方的解释对于初学者来说并不好理解,比如下面的问题:
什么是新旧nodes,什么是VNode?
没有key的时候,如何尝试修改和复用的?
有key的时候,如何基于key重新排列的?

认识VNode

我们先来解释一下VNode的概念:
VNode的全称是Virtual Node,也就是虚拟节点。事实上,无论是组件还是元素,它们最终在Vue中表示出来的都是一个个VNode。VNode的本质是一个JavaScript的对象。

虚拟DOM

如果我们不只是一个简单的div,而是有一大堆的元素,那么它们应该会形成一个VNode Tree。

插入F的案例

我们先来看一个案例:这个案例是当我们点击按钮时会在li中间插入一个f。

我们可以确定的是,这次更新对于ul和button是不需要进行更新,需要更新的是我们li的列表。在Vue中,对于相同父元素的子元素节点并不会重新渲染整个列表,因为对于列表中 a、b、c、d它们都是没有变化的。在操作真实DOM的时候,我们只需要在中间插入一个f的li即可。

那么Vue中对于列表的更新究竟是如何操作的呢?
Vue事实上会对于有key和没有key会调用两个不同的方法,有key,那么就调用 patchKeyedChildren方法,没有key,那么就调用 patchUnkeyedChildren方法。

Vue源码对于key的判断

没有key的操作(源码)

没有key的diff算法:

我们会发现上面的diff算法效率并不高,c和d来说它们事实上并不需要有任何的改动,但是因为我们的c被f所使用了,所有后续所有的内容都要一次进行改动,并且最后进行新增。

image.png

有key的操作(源码)

有key的diff算法:

  1. 第一步的操作是从头开始进行遍历、比较:
    a和b是一致的会继续进行比较。
    c和f因为key不一致,所以就会break跳出循环。
  1. 第二步的操作是从尾部开始进行遍历、比较:
  1. 第三步是如果旧节点遍历完毕,但是依然有新的节点,那么就新增节点:
  1. 第四步是如果新的节点遍历完毕,但是依然有旧的节点,那么就移除旧节点:
  1. 第五步是最特色的情况,中间还有很多未知的或者乱序的节点:

所以我们可以发现,Vue在进行diff算法的时候,会尽量利用我们的key来进行优化操作,在没有key的时候我们的效率是非常低效的,在进行插入或者重置顺序的时候,保持相同的key可以让diff算法更加的高效。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,378评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,356评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,702评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,259评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,263评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,036评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,349评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,979评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,469评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,938评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,059评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,703评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,257评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,262评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,485评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,501评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,792评论 2 345

推荐阅读更多精彩内容