Vue3开发基础语法
预备知识
methods 中的 this
这里涉及到箭头函数使用 this 的查找规则,它会在自己的上层作用域中查找 this
最终刚好找到的是 script 作用域中的 this,所以就是 window
const App = {
template: "#my-app",
data() {
return {
counter: 0
}
},
methods: {
increment: () => {
// this.counter++;
console.log(this); // window
},
decrement() {
this.counter--;
console.log(this);
}
}
}
事实上 Vue 的源码当中就是对 methods 中所有函数进行了遍历,并且通过 bind 绑定了 this
VSCode 代码片段
有些代码片段是需要经常写的,我们在 VSCode 中可以生成一个代码片段,方便我们快速生成
![]() | ![]() | ![]() |
---|
具体步骤如下:
- 复制自己需要生成代码片段的代码
- https://snippet-generator.app/ 在该网站中生成代码片段
- 在 VSCode 中配置代码片段
Vue3 基础模板语法
React 开发模式
- React 使用的 jsx,所以对应的代码都是 编写的类似于 js 的一种语法
- 之后通过 Babel 将 jsx 编译成
React.createElement
函数调用
Vue 也支持 jsx 的开发模式
- 但是大多数情况下,使用 基于 HTML 的模板语法
- 在模板中,允许开发者以声明式的方式将 DOM 和 底层组件实例的数据 绑定在一起
- 在底层的实现中,Vue 将模板编译成虚拟 DOM 渲染函数
插值语法
如果我们希望把数据显示到模板(template)中,使用最多的语法是 "Mustache" 语法(双大括号)的文本插值
<!-- 1.mustache的基本使用 -->
<h2>{{ message }}</h2>
<!-- 2.是一个表达式 -->
<h2>{{ counter * 10 }}</h2>
<h2>{{ message.split(" ").reverse().join(" ") }}</h2>
<!-- 3.也可以调用函数 -->
<h2>{{ getReverseMessage() }}</h2>
<!-- 4.三元运算符 -->
<h2>{{ isShow ? "哈哈哈": "" }}</h2>
基础指令
v-once:用于指定元素或者组件只渲染一次
- 当数据发生变化时,元素或者组件以及其所有的子元素将视为静态内容并且跳过
- 该指令可以用于性能优化
<div v-once>
<h2>{{ counter }}</h2>
<h2>{{ message }}</h2>
</div>
v-text:用于更新元素的 textContent
v-html:默认情况下,如果我们展示的内容本身是 html 的,那么 vue 并不会对其进行特殊的解析,如果希望这个内容被 Vue 可以解析出来,可以使用这个
v-pre:用于跳过元素和它的子元素的编译过程,显示原始的 Mustache 标签,跳过不需要编译的节点,加快编译的速度
v-cloak:保持在元素上直到关联组件实例结束编译
- 和如下 CSS 规则一起使用
<style>
[v-cloak] {
display: none;
}
</style>
v-bind 绑定属性
某些属性希望动态来绑定
- 比如动态绑定 a 元素的 href 属性
- img 元素的 src 属性
v-bind:绑定一个或多个属性值,或者向另一个组件传递 props 值
缩写:
:
修饰符:
.camel
:将 kebab-case attribute 名转换为 camelCase
<template id="my-app">
<!-- 1.v-bind的基本使用 -->
<img v-bind:src="imgUrl" alt="">
<a v-bind:href="link">百度一下</a>
<!-- 2.v-bind提供一个语法糖 : -->
<img :src="imgUrl" alt="">
</template>
绑定 class 的两种方式
对象语法
html<template id="my-app"> <div :class="{ active: isActive, title: true }">哈哈哈</div> <!-- 默认的class和动态的class结合 --> <div class="abc cba" :class="classObj">呵呵呵</div> <!-- 将返回的对象放到一个methods(computed)方法中 --> <div class="abc cba" :class="getClassObj()">喵喵喵</div> </template>
数组语法
html<div :class="['abc', title, isActive ? 'active': '']">哈哈哈</div> <div :class="['abc', title, { active: isActive }]">呵呵呵</div>
绑定 style 的两种方式
对象语法
html<div :style="{ color: finalColor, 'font-size': '30px' }">哈哈哈</div> <div :style="{ color: finalColor, fontSize: '30px' }">呵呵呵</div>
数组语法
html<div :style="[style1Obj, style2Obj]">哈哈哈</div>
动态绑定属性
在某些情况下,我们属性的名称可能是不固定的
- 前端我们不论绑定 src、href、class、style 属性名称都是固定的
- 如果 属性名称不是固定 的,我们可以使用
:[属性名]="值"
的格式来定义
<div :[name]="value">哈哈哈</div>
绑定一个对象
如果我们希望将一个对象的所有属性,绑定到元素上的所有属性
<template id="my-app">
<div v-bind="info">哈哈哈哈</div>
<div :="info">哈哈哈哈</div>
</template>
<script>
const App = {
data() {
return {
info: {
name: "why",
age: 18,
height: 1.88
}
}
}
}
</script>
v-on 绑定事件
在前端开发中,我们需要经常和用户进行各种各样的交互
- 这个时候,我们就必须监听用户发生的事件,比如:点击、拖拽、键盘事件等
v-on 监听事件:
- 缩写:
@
- 修饰符:
.stop
- 调用 event.stopPropagation().prevent
- 调用 event.preventDefault().capture
- 添加事件侦听器时使用 capture 模式.self
- 只当事件是从侦听器绑定的元素本身触发时才触发回调.{keyAlias}
- 仅当事件是从特定键触发时才触发回调.once
- 只触发一次回调.left
- 只当点击鼠标左键时触发.right
- 只当点击鼠标右键时触发.middle
- 只当点击鼠标中键时触发.passive
- { passive: true } 模式添加侦听器
<button v-on:click="btn1Click">按钮1</button>
<!-- 语法糖 -->
<button @click="btn1Click">按钮1</button>
<!-- 绑定一个表达式: inline statement -->
<button @click="counter++">{{counter}}</button>
<!-- 绑定一个对象 -->
<div class="area" v-on="{ click: btn1Click, mousemove: mouseMove }">
<div class="area" @="{click: btn1Click, mousemove: mouseMove}"></div></div>
v-on 参数传递
如果该方法不需要额外参数,那么方法后的
()
可以不添加如果方法本身有一个参数,那么默认将原生事件 event 参数传递进去
如果同时传入某个参数,同时需要 event 时,可以通过
$event
传入事件
<!-- 默认传入event对象, 可以在方法中获取 -->
<button @click="btn1Click">按钮1</button>
<!-- $event可以获取到事件发生时的事件对象 -->
<button @click="btn2Click($event, 'coderwhy', 18)">按钮2</button>
v-on 修饰符
<div @click="divClick">
<button @click.stop="btnClick">按钮</button>
</div>
<input type="text" @keyup.enter="enterKeyup">
Vue3 基础模板语法2
条件渲染
Vue 提供了下面的指令来进行条件判断:
- v-if
- v-eles
- v-else-if
- v-show
<template id="my-app">
<input type="text" v-model="score">
<h2 v-if="score > 90">优秀</h2>
<h2 v-else-if="score > 60">良好</h2>
<h2 v-else>不及格</h2>
</template>
v-if 的渲染原理:
- v-if 是惰性的
- 当条件为 false 时,其判断的内容完全不会被渲染活着呗销毁掉
- 当条件为 true 时,才会真正渲染条件块中的内容
template元素
如果我们希望切换的是多个元素,此时我们渲染 div,但是我们不希望 div 这个元素被渲染
这个时候,我们可以选择使用 template
template 元素可以当做不可见的包裹元素,并且在 v-if 上使用,但是最终 template 不会被渲染出来
有点类似小程序中的 block
v-show 与 v-if 区别
用法区别
v-show 不支持 template
v-show 不可以和 v-else 一起使用
本质区别
v-show 元素无论是否需要显示到浏览器上,它的 DOM 实际都是有渲染的,只是通过 CSS 的 display 属性来进行切换
v-if 当条件为 false,其对应的原生压根不会被渲染到 DOM 中
开发中如何进行选择呢?
- 如果我们的原生需要在显示和隐藏之间频繁的切换,那么使用v-show
- 如果不会频繁的发生切换,那么使用v-if
列表渲染
v-for 基本格式:"item in 数组"
- 数组通常是来自 data 或者 prop,也可以是其他方式
- item 是我们给每项元素起的一个别名,这个别名可以自定来定义
- 数组也可以用
of
替代in
作为分隔符
v-for 也支持遍历对象,并且支持有三个参数:
- 一个参数:
"value in object"
- 二个参数:
"(value, key) in object"
- 三个参数:
"(value, key, index) in object"
v-for 同时也支持数字的遍历
- 每一个 item 都是一个数字
<template id="my-app">
<h2>电影列表</h2>
<ul>
<!-- 遍历数组 -->
<li v-for="(movie, index) in movies">{{index+1}}.{{movie}}</li>
</ul>
<h2>个人信息</h2>
<ul>
<!-- 遍历对象 -->
<li v-for="(value, key, index) in info">{{value}}-{{key}}-{{index}}</li>
</ul>
<h2>遍历数字</h2>
<ul>
<li v-for="(num, index) in 10">{{num}}-{{index}}</li>
</ul>
</template>
数组更新检测
Vue 将被侦听的数组的变更方法进行了包裹,所以它们也将会触发视图更新。这些被包裹过的方法包括:
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
上面的方法会直接修改原来的数组,但是某些方法不会替换原来的数组,而是生成新的数组,比如:filter()
、concat()
、slice()
key 和 diff 算法
在使用 v-for 进行列表渲染时,我们通常会给元素或者组件绑定一个 key 属性,下面先来看一下官方解释
- key 属性主要用在 Vue 的 虚拟 DOM 算法,在 新旧 nodes 对比时辨识 VNodes
- 如果 不使用 key,Vue 会使用一种最大限度减少动态并且尽可能的尝试就地 修改/复用相同类型元素 的算法
- 而 使用 key 时,它会基于 key 的变化 重新排列元素顺序,并且 移除/销毁 key 不存在的元素
官方解释对于初学者来说并不好理解,比如下面问题:
- 什么是新旧 nodes,什么是 VNode?
- 没有 key 的时候,如何尝试修改和复用的?
- 有 key 的时候,如何基于 key 重新排列的?
VNode 和 虚拟 DOM
- 可以先理解 HTML 元素创建出来的 VNode
- VNode 的全称是 Virtual Node,也就是虚拟节点
- 事实上,无论是组件还是元素,它们最终在 Vue 中表示出来的都是一个个 VNode
- VNode 本质是一个 JavaScript 的对象
- 如果我们不只是一个简单的 div,而是有一大堆的元素,那么它们应该形成一个 VNode Tree
案例
我们可以确定的是,这次更新对于 ul 和 button 是不需要进行更新,需要更新的是我们li的列表:
- 在 Vue 中,对于相同父元素的子元素节点并不会重新渲染整个列表
- 因为对于列表中 a、b、c 它们都是没有变化的
- 在操作真实 DOM 的时候,我们只需要在中间插入一个 f 的 li 即可
<template id="my-app">
<ul>
<li v-for="item in letters" :key="item">{{item}}</li>
</ul>
<button @click="insertF">插入F元素</button>
</template>
<script>
const App = {
template: '#my-app',
data() {
return {
letters: ['a', 'b', 'c', 'd']
}
},
methods: {
insertF() {
this.letters.splice(2, 0, 'f')
}
}
}
Vue.createApp(App).mount('#app');
</script>
Vue 中对于列表更新究竟如何操作:
- 有 key ,那么就使用
patchKeyedChildren
方法 - 没有 key,那么就使用
patchUnkeyedChildren
方法
Vue 对于没有 key 的操作
Vue 源码对于 key 的判断
没有 key 的操作
可以发现上面的 diff 算法效率并不高
- c 和 d 来说它们事实上并不需要有任何的改动
- 但是因为我们的 c 被 f 使用了,后续所有内容那个都需要一次进行改动,并且最后进行新增
Vue 对于有 key 的操作
第一步
第一步的操作是从头开始进行遍历:
- a 和 b 是一致的会继续进行比较
- c 和 f 因为 key 不一致,所以就会 break 跳出循环
第二步
第二步的操作是从尾部开始进行遍历:
第三步
第三步是如果旧节点遍历完毕,但是依然有新的节点,那么就新增节点:
第四步
第四步是如果新的节点遍历完毕,但是依然有旧的节点,那么就移除旧节点
第五步
第五步是最特色的情况,中间还有很多未知的或者乱序的节点:
Vue 在进行 diff 算法的时候,会尽量利用我们的 key 来进行优化操作:
- 在没有 key 的时候我们的效率是非常低效的
- 在进行插入或者重置顺序的时候,保持相同的 key 可以让 diff 算法更加高效