Vue3基础语法
# 一、模板语法
# 1>、mustache语法
即{{}}语法,可以写表达式、调用methods里面的函数、可以使用计算属性computed、三目运算符
<!-- JavaScript表达式 -->
<h2>{{ counter * 2}}</h2>
<h2>{{message.split(" ").reverse().join(" ")}}</h2>
<!-- 调用一个methods中的函数 -->
<h2>{{reverse(message)}}</h2>
<!-- 三元运算符 -->
<h2>{{ true ? message: counter }}</h2>
2
3
4
5
6
7
8
9
# 2>、基本指令
# v-once
用于指定元素或者组件只渲染一次:
- 当数据发生变化时,元素或者组件以及其所有的自己诶单将视为静态内容并且跳过;
- 该指令可以用于性能优化;
# v-text
用于更新元素的 textContent:
# v-html
默认情况下,如果我们展示的内容本身是 html 的,那么vue并不会对其进行特殊的解析。如果我们希望这个内容被Vue可以解析出来,那么可以使用 v-html 来展示
# v-pre
v-pre用于跳过元素和它的子元素的编译过程,显示原始的Mustache标签:
- 跳过不需要编译的节点,加快编译的速度;
# v-cloak
这个指令保持在元素上直到关联组件实例结束编译。和 CSS 规则如 [v-cloak] { display: none }
一起用时,这个指令可以隐藏未编译的 Mustache 标签直到组件实例准备完毕
# v-bind
前端讲的一系列指令,主要是和内容相关的,元素除了内容之外还会有各种各样的属性。
绑定属性我们使用v-bind:
缩写:
:
预期:
any (with argument) | Object (without argument)
参数:
attrOrProp (optional)
修饰符:
.camel
- 将 kebab-case attribute 名转换为 camelCase。
用法:动态地绑定一个或多个 attribute,或一个组件 prop 到表达式。
# 绑定基本属性
如src,href等,
# 绑定class属性
有对象语法和数组语法
<!-- 1.普通的绑定方式 -->
<div :class="className">{{message}}</div>
<!-- 2.对象绑定 -->
<!-- 动态切换class是否加入: {类(变量): boolean(true/false)} -->
<div class="why" :class="{nba: true, 'james': true}"></div>
<!-- 案例练习 -->
<div :class="{'active': isActive}">哈哈哈</div>
<button @click="toggle">切换</button>
<!-- 绑定对象 -->
<div :class="classObj">哈哈哈</div>
<!-- 从methods中获取 -->
<div :class="getClassObj()">呵呵呵</div>
<div :class="['why', nba, isActive? 'active': '']">呵呵呵</div>
<div :class="['why', nba, {'actvie': isActive}]">嘻嘻嘻</div>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 绑定style属性
# 绑定自定义属性
<div v-bind="info" :[title]="'aaa'">绑定自定义属性</div>
# v-on
前面我们绑定了元素的内容和属性,在前端开发中另外一个非常重要的特性就是交互。
在前端开发中,我们需要经常和用户进行各种各样的交互:
- 这个时候,我们就必须监听用户发生的时间,比如点击、拖拽、键盘事件等等
- 在Vue中如何监听事件呢?使用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.绑定函数 -->
<button v-on:click="btnClick">按钮1</button>
<button @click="btnClick">按钮2</button>
<div v-on:mousemove="mouseMove">div的区域</div>
<!-- 2.绑定对象 -->
<button v-on="{click: btnClick, mousemove: mouseMove}">特殊按钮3</button>
<!-- 3.内联语句 -->
<!-- 默认会把event对象传入 -->
<button @click="btn4Click">按钮4</button>
<!-- 内联语句传入其他属性 -->
<button @click="btn4Click($event, 'why')">按钮5</button>
<!-- 4.修饰符 -->
<div @click="divClick">
<button @click.stop="btnClick">按钮6</button>
</div>
<input type="text" @keyup.enter="onEnter">
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 3>、条件渲染
在某些情况下,我们需要根据当前的条件决定某些元素或组件是否渲染,这个时候我们就需要进行条件判断了。
Vue提供了下面的指令来进行条件判断:
- v-if
- v-else
- v-else-if
- v-show
下面我们来对它们进行学习。
# 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时,才会真正渲染条件块中的内容;
<!-- vue3中, template不再要求必须只有一个根元素 -->
<template id="my-app">
<input type="text" v-model.number="score">
<h2 v-if="score > 90">优秀</h2>
<h2 v-else-if="score > 80">良好</h2>
<h2 v-else-if="score > 60">普通</h2>
<h2 v-else>不及格</h2>
</template>
2
3
4
5
6
7
8
# template元素
因为v-if是一个指令,所以必须将其添加到一个元素上:
- 但是如果我们希望切换的是多个元素呢?
- 此时我们渲染div,但是我们并不希望div这种元素被渲染;
- 这个时候,我们可以选择使用template;
template元素可以当做不可见的包裹元素,并且在v-if上使用,但是最终template不会被渲染出来:
- 有点类似于小程序中的block
# v-show
v-show和v-if的用法看起来是一致的:
# v-show和v-if区别
首先,在用法上的区别:
- v-show是不支持template;
- v-show不可以和v-else一起使用;
其次,本质的区别:
- v-show元素无论是否需要显示到浏览器上,它的DOM实际都是有渲染的,只是通过CSS的display属性来进行切换;
- v-if当条件为false时,其对应的原生压根不会被渲染到DOM中;
开发中如何进行选择呢?
- 如果我们的原生需要在显示和隐藏之间频繁的切换,那么使用v-show;
- 如果不会频繁的发生切换,那么使用v-if;
# 4>、 列表渲染
在真实开发中,我们往往会从服务器拿到一组数据,并且需要对其进行渲染。
- 这个时候我们可以使用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同时也支持数字的遍历:
# template元素
类似于v-if,你可以使用 template
元素来循环渲染一段包含多个元素的内容:
- 我们使用template来对多个元素进行包裹,而不是使用div来完成;
# 数组更新检测
Vue 将被侦听的数组的变更方法进行了包裹,所以它们也将会触发视图更新。这些被包裹过的方法包括:
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
替换数组的方法
上面的方法会直接修改原来的数组,但是某些方法不会替换原来的数组,而是会生成新的数组,比如 filter()
、concat()
和 slice()
。
# key和diff算法
# 2.6.1. 认识VNode和VDOM
在使用v-for进行列表渲染时,我们通常会给元素或者组件绑定一个key属性。
这个key属性有什么作用呢?我们先来看一下官方的解释:
- key属性主要用在Vue的虚拟DOM算法,在新旧nodes对比时辨识VNodes;
- 如果不使用key,Vue会使用一种最大限度减少动态元素并且尽可能的尝试就地修改/复用相同类型元素的算法;
- 而使用key时,它会基于key的变化重新排列元素顺序,并且会移除/销毁key不存在的元素;
官方的解释对于初学者来说并不好理解,比如下面的问题:
- 什么是新旧nodes,什么是VNode?
- 没有key的时候,如何尝试修改和复用的?
- 有key的时候,如何基于key重新排列的?
我们先来解释一下VNode的概念:
- 因为目前我们还没有比较完整的学习组件的概念,所以目前我们先理解HTML元素创建出来的VNode;
- VNode的全称是Virtual Node,也就是虚拟节点;
- 事实上,无论是组件还是元素,它们最终在Vue中表示出来的都是一个个VNode;
- VNode的本质是一个JavaScript的对象;
Vue内部在拿到vnode对象后,会对vnode进行处理,渲染成真实的DOM。
- 这一部分我会在后面专门和大家阅读createApp函数中讲到;
DOM渲染过程
如果我们不只是一个简单的div,而是有一大堆的元素,那么它们应该会形成一个VNode Tree:
所以我们可以发现,Vue在进行diff算法的时候,会尽量利用我们的key来进行优化操作:
- 在没有key的时候我们的效率是非常低效的;
- 在进行插入或者重置顺序的时候,保持相同的key可以让diff算法更加的高效;
源码: packages/runtime-core/src/renderer.ts
patchKeyedChildren、patchUnkeyedChildren
参考文章:
Vue3+TS系统学习三 - Vue3开发基础语法(一) (qq.com) (opens new window)
# 计算属性computed
# 监听器
- 当变更(不是替换)对象或数组并使用 deep 选项时,旧值将与新值相同,因为它们的引用指向同一个对象/数组。Vue 不会保留变更之前值的副本
vm.$watch('someObject', callback, {
deep: true, immediate: true
})
2
3
参考:
实例方法 | Vue.js (vuejs.org) (opens new window)
# 过滤器
vue3不再支持过滤器,推荐两种方法: 使用计算属性、使用全局方法
// Vue3不支持过滤器了, 推荐两种做法: 使用计算属性/使用全局的方法
filterBooks() {
return this.books.map(item => {
const newItem = Object.assign({}, item); // 拷贝一个新的对象
newItem.price = "¥" + item.price;
return newItem;
})
}
2
3
4
5
6
7
8
# v-model与表单
v-model指令可以在表单 input、textarea、checkbox、radio、select元素上创建双向数据绑定;
它会根据控件类型自动选取正确的方法来更新元素;
尽管有些神奇,但 v-model 本质上不过是语法糖,它负责监听用户的输入事件来更新数据,并在某种极端场景下进行一些特殊处理;
<!-- 1.v-bind value的绑定 2.监听input事件, 更新message的值 -->
<!-- <input type="text" :value="message" @input="inputChange"> -->
<input type="text" v-model="message">
<!-- 1.绑定textarea -->
<label for="intro">
自我介绍
<textarea name="intro" id="intro" cols="30" rows="10" v-model="intro"></textarea>
</label>
<h2>intro: {{intro}}</h2>
<!-- 2.checkbox -->
<!-- 2.1.单选框 -->
<label for="agree">
<input id="agree" type="checkbox" v-model="isAgree"> 同意协议
</label>
<h2>isAgree: {{isAgree}}</h2>
<!-- 2.2.多选框, 需要设置value -->
<span>你的爱好: </span>
<label for="basketball">
<input id="basketball" type="checkbox" v-model="hobbies" value="basketball"> 篮球
</label>
<label for="football">
<input id="football" type="checkbox" v-model="hobbies" value="football"> 足球
</label>
<label for="tennis">
<input id="tennis" type="checkbox" v-model="hobbies" value="tennis"> 网球
</label>
<h2>hobbies: {{hobbies}}</h2>
<!-- 3.radio -->
<span>你的爱好: </span>
<label for="male">
<input id="male" type="radio" v-model="gender" value="male">男
</label>
<label for="female">
<input id="female" type="radio" v-model="gender" value="female">女
</label>
<h2>gender: {{gender}}</h2>
<!-- 4.select -->
<span>喜欢的水果: </span>
<select v-model="fruit" multiple size="2">
<option value="apple">苹果</option>
<option value="orange">橘子</option>
<option value="banana">香蕉</option>
</select>
<h2>fruit: {{fruit}}</h2>
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
# v-model修饰符
# v-model.lazy
默认情况下,v-model在进行双向绑定时,绑定的是input事件,那么会在每次内容输入后就将最新的值和绑定的属性进行同步;
如果我们在v-model后跟上lazy修饰符,那么会将绑定的事件切换为 change 事件,只有在提交时(比如回车)才会触发;
# v-model.number
v-model里面的类型总是string类型,即使在我们设置input的type为number也是string类型; 如果是一个string类型,在可以转化的情况下会进行
隐式转换
的如果我们希望转换为数字类型,那么可以使用
.number
修饰符
# v-model.trim
去掉首尾空白字符
<!-- 1.lazy修饰符 -->
<!-- <input type="text" v-model.lazy="message"> -->
<!-- 2.number修饰符 -->
<!-- <input type="text" type="number" v-model.number="message">
<h2>{{message}}</h2>
<button @click="showType">查看类型</button> -->
<!-- 3.trim修饰符 -->
<input type="text" v-model.trim="message">
2
3
4
5
6
7
8
9
10
# js对象
js的对象、数组
是引用类型,即指向的是内存的一个地址,当一个对象赋值给另一个变量B时,实际上是把地址给了B
# 对象浅拷贝
先将原对象A的值拷贝一份在内存里面,然后新对象D指向新的地址, 如果原对象A里面有对象B的时候, 浅拷贝也只是拷贝B的地址,因此B变化, A和D都会变化。
const obj = Object.assign({}, info); // 先在内存里面拷贝一份数据,再把地址赋值给新对象
// lodash库实现浅拷贝 <script src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script>
const obj = _.clone(info);
2
3
4
# 对象深拷贝
原理:先将对象序列化,再反序列化成一个新对象
const obj = JSON.parse(JSON.stringify(info));
// lodash库实现深拷贝
_.cloneDeep(info)
2
3
4