# Vue 的笔记记录 ----- 万字基础篇

# 指令

指令是 Directives, 是模板语法,用于辅助开发渲染页面的基本结构

# 1. 内容渲染指令

v-text

1
2
3
4
5
6
7
8
9
10
11
<p v-text='username'></p>




const vm=new Vue({
el:'app',
data:{
username:'zhangsan'
}
})

缺点:会把标签内原有的内容覆盖调

插值语法,用来解决 text 覆盖原有内容问题,除了可以插值还可以进行一些 js 里面的运算

1
2
3
4
5
6
7
8
9
10
11
<p >{{username}}</p>




const vm=new Vue({
el:'app',
data:{
username:'zhangsan'
}
})

v-html 可以渲染带标签的字符串到页面中

# 2. 属性绑定指令

v-bind😕: 为元素属性动态绑定值还可以进行一些 js 里面的运算

1
2
3
4
5
6
7
8
9
10
11
12
input :placeholder="a+b"/"'gh'+b"/"username"



const vm=new Vue({
el:'app',
data:{
username:'zhangsan',
a:1,
b:2,
}
})

# 3. 事件绑定指令

v-on 事件绑定指令简写 @click

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<button v-on:click="add">提交</button>









el:'#app'

data:{
},
methods:{
add(){
cl(1)
}
}

$.event 原生的 dom 对象 e

1
<button @click="add(0,$event)">

# 事件修饰符

.prevent 消除默认事件 @click.prevent

事件修饰符说明
.prevent阻止默认行为
.stop阻止事件冒泡
.capture以捕获模式触发当前的事件处理函数
.once绑定的事件只触发一次
.self只有在 event.target 是当前元素的自身触发事件处理函数

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
<!-- 单击事件将停止传递 -->
<a @click.stop="doThis"></a>

<!-- 提交事件将不再重新加载页面 -->
<form @submit.prevent="onSubmit"></form>

<!-- 修饰语可以使用链式书写 -->
<a @click.stop.prevent="doThat"></a>

<!-- 也可以只有修饰符 -->
<form @submit.prevent></form>

<!-- 仅当 event.target 是元素本身时才会触发事件处理器 -->
<!-- 例如:事件处理器不来自子元素 -->
<div @click.self="doThat">...</div>



<!-- 添加事件监听器时,使用 `capture` 捕获模式 -->
<!-- 例如:指向内部元素的事件,在被内部元素处理前,先被外部处理 -->
<div @click.capture="doThis">...</div>

<!-- 点击事件最多被触发一次 -->
<a @click.once="doThis"></a>

<!-- 滚动事件的默认行为 (scrolling) 将立即发生而非等待 `onScroll` 完成 -->
<!-- 以防其中包含 `event.preventDefault()` -->
<div @scroll.passive="onScroll">...</div>

.passive 修饰符一般用于触摸事件的监听器,可以用来改善移动端的滚屏性能

# 按键修饰符

摁返回键触发

1
@click.esc

enter 键触发

1
@keyup.enter

向下键触发

1
<input @keyup.page-down="onPageDown" />

1
2
3
4
5
6
7
8
9
.enter
.tab
.delete (捕获“Delete”和“Backspace”两个按键)
.esc
.space
.up
.down
.left
.right

# 系统按键修饰符

1
2
3
4
.ctrl
.alt
.shift
.meta

系统按键修饰符

1
2
3
4
5
<!-- Alt + Enter -->
<input @keyup.alt.enter="clear" />

<!-- Ctrl + 点击 -->
<div @click.ctrl="doSomething">Do something</div>

鼠标按键

1
2
3
4

.left
.right
.middle

# 4. 双向绑定指令 v-model

下面这段就等价于 v-model

1
2
3
4
<input
:value="searchText"
@input="searchText = $event.target.value"
/>

可以让程序员在不操作 dom 的情况下读取和修改 data 中的值

# v-model 修饰符

.lazy input 输入完之后失去焦点才更新数据

.number 输入之后如果值是数字就转成 number 类型,是文字就原封不动

.trim 自动去除空格

1
<input v-model.trim="msg" />

# 5. 条件渲染指令

1
2
3
4
5
6
7
v-if

v-else

v-else-if

v-show

当这几个元素的值为 true 时被绑定元素内的内容从才会被显示

# v-if 和 v-show 的区别

v-if 是 “真实的” 按条件渲染,因为它确保了在切换时,条件区块内的事件监听器和子组件都会被销毁与重建。

相比之下,v-show 简单许多,元素无论初始条件如何,始终会被渲染,只有 CSS display 属性会被切换。

总的来说,v-if 操作 dom 元素的销毁于重建,v-show 操作 dom 元素的显示与隐藏,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要频繁切换,则使用 v-show 较好;如果在运行时绑定条件很少改变,则 v-if 会更合适。

# 6. 列表渲染指令 v-for

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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script src="../js-vue/vue.js"></script>
<!-- <script src="../vuejs/antd.min.js"></script>
<link rel="stylesheet" href="../vuejs/antd.min.css" /> -->
</head>

<body>
<div id="app">
<input type="search" value="" @keyup.esc="clearn" />
<input v-model.lazy="searchText" />
<input type="text" name="" id="" v-model.number="checkbox" />
<h1 v-if="checkbox">11s</h1>
<h1 v-else>else</h1>
<!-- v-for -->
<h1 v-for="(item,index) in dataBase">
{{item.name}}-{{index}}-{{item.age?item.age:"未填写"}}-{{item.sex}}
</h1>
<!-- v-for 对象解构写法-->
<h1 v-for="({name,age,sex},index) in dataBase">
{{name}}-{{index}}-{{age?age:"未填写"}}-{{sex}}
</h1>
<!-- 对于多层嵌套的 v-for,作用域的工作方式和函数的作用域很类似。每个 v-for 作用域都可以访问到父级作用域: -->
<div v-for="({name,age,sex},index) in dataBase">
<!--也可以使用of代替in更符合js迭代-->
<span v-for="item in jg">{{name}}-{{item}}</span>
</div>
<!-- 你也可以使用 v-for 来遍历一个对象的所有属性。遍历的顺序会基于对该对象调用 Object.keys() 的返回值来决定。 -->
<div v-for="(key,value,index) in searchText">
{{index}}-{{key}}:{{value}}
</div>
<!-- 直接接受一个整数值。1...n 的取值范围重复多次。从1-15包括1和 15-->
<p v-for="n in 15">{{n}}</p>
<!-- tem 可以在 <template> 标签上使用 v-for 来渲染一个包含多个元素的块。-->
<ul>
<template v-for="item in dataBase">
<li>{{ item.name }}</li>
<li class="divider" role="presentation"></li>
</template>
</ul>
</div>
<script>
new Vue({
el: '#app',
data: {
searchText: {
name: 'nihao1',
area: '云南',
},
checkbox: 0,
jg: [1, 2, 3, 4, 5],
dataBase: [
{
name: 'zs',
sex: '男',
},
{
name: 'ls',
sex: '男',
},
{
name: 'lt',
sex: '男',
age: 25,
},
],
},
methods: {
clearn(e) {
console.log('触发了按键修饰符');
e.target.value = '';
},
},
});
</script>
</body>
</html>

同时使用 v-ifv-for不推荐的,因为这样二者的优先级不明显。

在外新包装一层 <template> 再在其上使用 v-for 可以解决这个问题 (这也更加明显易读):

1
2
3
4
<template v-for="item in dataBase">
<li v-if="item.age">{{ item.name }}</li>
<li class="divider" role="presentation"></li>
</template>

# 绑定 Key 管理 vue 的 dom 的状态:

Vue 默认按照 “就地更新” 的策略来更新通过 v-for 渲染的元素列表。确保他们在原本指定的位置进行渲染。

为了便于 vue 跟踪节点的标识,从而重新、重用对现有的元素排序,需要给对应元素加一个唯一的 key 属性。key 是通过 v-bind 绑定的一个特殊的属性而不是 item 中的那个 key 属性。

值一般是一个 number 类型,一般用 item.id

1
2
3
<template v-for="todo in todos" :key="todo.name">
<li>{{ todo.name }}</li>
</template>

key 值需要是一个唯一值,不能以 index 当作 key 的值,因为没有强制的绑定方式,也不能说是个严谨的值。

例如:原来列表中有两个元素

0历史
1环境

在前面添加一个元素的时候索引值会变成

0科学
1历史
2环境

所以说是不严谨的

# 组件上使用 v-for

可以在组件上直接使用 v-for 但是不会将任何数据传到组件里面,因为组件有自己独立的作用域,将数据传递到组件中需要用 props

省略..... 后面再说

# 数组变化侦测

侦听数组变化,在他们被调用时触发相关的更新:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
push()向数组的末尾添加一个或多个元素,并返回新的长度。
pop()数组最后一位元素删除并返回数组的最后一个元素。
shift()数组的第一个元素从其中删除,并返回第一个元素的值。
unshift()向数组的开头添加一个或更多元素,并返回新的长度。
splice(index,howmany,item1, …, itemX)向/从数组中添加/删除项目,然后返回被删除的项目
第一个参数:表示从哪个索引位置(index)添加/删除元素
第二个参数:要删除的项目数量。如果设置为 0,则不会删除项目。
第三个参数:可选。向数组添加的新项目。
splice(1,2,‘a’,‘b’) 从索引位置(index:1)删除,删除2个元素,并添加2个新元素来替代被删除的元素
splice(1,0,‘a’)从索引位置(index:1)添加,添加两个元素
sort()正序
reverse()倒序



<label for="test">label标签</label>
<input type="text" id="test">



当点击label标签内的文本后,就会触发绑定的表单元素。也就是说,当用户渲染该标签时,浏览器就会自动将焦点转到绑定的表单控件上。

# 过滤器 filter (vue3 已淘汰) 只能在 vue2 中使用

可以用在两个地方:1. 插值语法 2.v-bind

通过管道符进行・调用

1
2
3
<div :id='rawid|过滤器'>
{{message|过滤器}}
</div>

私有过滤器(在 Vue 不同实例中不能通用只能在当前实例使用)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<div id="app">
<p>{{message|filterL}}</p>
</div>
<script>
new Vue({
el: '#app',
data: {
message: 'hellow shuaige',
},
filters: {
// 过滤器函数形参永远都是管道符前面的那个值
filterL(val) {
// 取索引对应的字符串
const vl = val.charAt(0).toUpperCase();
// 截取字符串从指定索引向后截取不包括当前索引
const ou = val.slice(1, 5);
return vl + ou;
},
},
});
</script>
</body>

全局过滤器挂在 Vue 全局指令上

1
2
3
4
5
6
7
// 全局过滤器Vue.filter(过滤器名字,回调函数)
Vue.filter('filterL', (val) => {
const vl = val.charAt(0).toUpperCase();
// 截取字符串从指定索引向后截取不包括当前索引
const ou = val.slice(1, 5);
return vl + ou;
});

全局过滤器和私有过滤器冲突,私有过滤器优先级高

也可以通过管道符调用多个过滤器

# 侦听器 Watch

监视数据变化时而调用的函数,在组合式 API 中,我们可以使用 watch 函数在每次响应式状态发生变化时触发回调函数:

但是学习之前需要了解一下 ref 声明响应式状态,主要用于组合式 API 中

为什么要使用 ref?

在模板中使用了一个 ref, 改变 ref 时,vue 会自动检测到变化,并更新相应的 dom。通过依赖追踪的响应式系统实现的。当一个组件被首次渲染时,Vue 会在追踪渲染的过程中使用的每一个 ref, 当 ref 被修改时,会触发追踪它的这个组件重新渲染一次。

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
<script setup>
import { ref, watch } from 'vue'

const question = ref('')
const answer = ref('Questions usually contain a question mark. ;-)')
const loading = ref(false)

// 可以直接侦听一个 ref
watch(question, async (newQuestion, oldQuestion) => {
if (newQuestion.includes('?')) {//includes是查询字符串中是否包含某个字符
loading.value = true
answer.value = 'Thinking...'
try {
const res = await fetch('https://yesno.wtf/api')
answer.value = (await res.json()).answer
} catch (error) {
answer.value = 'Error! Could not reach the API. ' + error
} finally {
loading.value = false
}
}
})
</script>

<template>
<p>
Ask a yes/no question:
<input v-model="question" :disabled="loading" />//默认不能选中是false
</p>
<p>{{ answer }}</p>
</template>

侦听 que's'ting,查看输入字符中是否包含问号,包含?时就让输入框不能输入,调用随机返回 api

watch 的第一个参数可以是不同形式的 “数据源”:它可以是一个 ref (包括计算属性)、一个响应式对象、一个 getter 函数、或多个数据源组成的数组:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const x = ref(0)
const y = ref(0)

// 单个 ref
watch(x, (newX) => {
console.log(`x is ${newX}`)
})

// getter 函数
watch(
() => x.value + y.value,
(sum) => {
console.log(`sum of x + y is: ${sum}`)
}
)

// 多个来源组成的数组
watch([x, () => y.value], ([newX, newY]) => {
console.log(`x is ${newX} and y is ${newY}`)
})

注意,你不能直接侦听响应式对象的属性值

1
2
3
4
5
6
const obj = reactive({ count: 0 })

// 错误,因为 watch() 得到的参数是一个 number
watch(obj.count, (count) => {
console.log(`count is: ${count}`)
})

需要返回这个属性的 getter 函数值

1
2
3
4
5
6
7
// 提供一个 getter 函数
watch(
() => obj.count,
(count) => {
console.log(`count is: ${count}`)
}
)

# 即时回调的侦听器(immediate:true)

页面打开先执行一次

1
2
3
4
5
6
username: {
handler(n, o) {
console.log(n);
},
immediate: true,
},

# 一次性侦听器 once:true 3.4+

当数据发生变化时只触发一次

# watcheffect()

简化请求远程资源的监听,不用多次调用

1
2
3
4
5
6
7
8
9
10
11
12
13
const todoId = ref(1)
const data = ref(null)

watch(
todoId,
async () => {
const response = await fetch(
`https://jsonplaceholder.typicode.com/todos/${todoId.value}`
)
data.value = await response.json()
},
{ immediate: true }
)

简写为

1
2
3
4
5
6
watchEffect(async () => {
const response = await fetch(
`https://jsonplaceholder.typicode.com/todos/${todoId.value}`
)
data.value = await response.json()
})

而且回调会立即执行,不需要指定 immediate: true。在执行期间,它会自动追踪 todoId.value 作为依赖(和计算属性类似)。每当 todoId.value 变化时,回调会再次执行。有了 watchEffect (),我们不再需要明确传递 todoId 作为源值。

# watch 和 Watcheffect 的区别

1.watch 不会追踪回调函数里面访问到的东西。只有数据发生改变后才会触发回调,可以准确的控制回调函数触发时机;(可定制)

2.watcheffect 可以追踪到回调函数里面的属性,代码更简洁,但是响印依赖关系不那么明确。(不可定制)

# 同步侦听器

1
2
3
4
5
import { watchSyncEffect } from 'vue'

watchSyncEffect(() => {
/* 在响应式数据变化时同步执行 */
})

# 计算属性 computed

通过一系列运算后得出来的值,动态计算出来的值可以被模板结构或 methods 方法使用

插 slice、splice、split 三者区别

slice 截取字符串或数组,并返回一个新的字符串或数组

splice 可以对数组进行插入删除替换返回删除的数组元素

split 以什么字符分割字符串返回一个数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<div id="app">
<div>
<input type="text" v-model="message" />
<p>{{messageRe}}</p>
</div>
</div>
<script>
new Vue({
el: '#app',
data: {
message: 'hellow',
},
computed: {
messageRe() {
return this.message.split('').reverse().join('');
},
},
});

计算属性所依赖的值发生改变时,计算属性的值也会发生改变

计算属性和侦听属性

计算属性相对于监听属性来说代码更简洁,计算属性默认只有一个 getter,但是需要的时候也可以创建一个 setter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
computed: {
fullName: {
// getter
get() {
return this.firstName + ' ' + this.lastName
},
// setter
set(newValue) {
var names = newValue.split(' ')
this.firstName = names[0]
this.lastName = names[names.length - 1]
}
}
}

计算属性在大多数情况下更合适,但有时也需要一个自定义的侦听器。这就是为什么 Vue 通过 watch 选项提供了一个更通用的方法,来响应数据的变化。当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。

# Axios 基本使用

1
2
3
4
5
6
7
8
9
axios({
metnod:"请求类型",
url:"请求地址"

}).then((result)=>{

}).catch((err)=>{

})

# Vue-cli 脚手架

是 vue.js 的开发标准工具。简化 webpack 创建工程化项目的过程。

安装

1
npm i -g @vue/cli

创建项目

1
vue create 项目名称

通过 main.js 把 App.vue 渲染到 index.html 的指定区域中

1
2
3
4
5
6
7
8
import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false

new Vue({
render: h => h(App),将组件渲染到页面
}).$mount('#app')提取dom元素主要是query函数也就是queryselector查找第一个符合的dom元素,也就是··。。。。。。。。。。。。el绑定的元素,但是,el不能是body或者html,原因是vue在挂载是会将对应的dom对象替换成新的div,但body和html是不适合替换的。

# 组件化开发(封装思想,一件多用)

三个组成部分:template、script、style

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
<template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png" />
<HelloWorld msg="Welcome to Your Vue.js App" />
</div>
</template>

<script>
import HelloWorld from './components/HelloWorld.vue';

export default {
name: 'App',
components: {
HelloWorld,
},
};
</script>

<style lang="less">启用less
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>

组件中的 data 必须是一个函数,里面要 return 一个对象

1
2
3
4
5
data(){
return{
name:;;;;;;
}
}

组件中定义 methods 方法

1
2
3
4
5
methods: {
add() {
alert('哇哈哈');
},
},

# 使用组件的步骤:

# 1. 在当前组件中暴露出去,在需要用到该组件的里面导入组件

1
import from

# 2. 注册组件(调入后在 components 中注册组件)

1
2
3
4
5
export default:{
compoents:{
组件名
}
}

# 3. 在 template 中以标签形式直接使用该组件

1
2
3
4
5
6
<template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png" />
<HelloWorld msg="Welcome to Your Vue.js App" />
</div>
</template>

# 通过 components 注册的是私有组件。

# 全局组件注册使用

1
2
3
4
5
6
7
8
9
10
11
12
13
main.js









import a from b

Vue.component('A',a)

# props (自定义属性)

在子组件中声明

1
2
3
props:{
name:String
}

父组件中赋值:

1
<zujian name='张三'><zujian>

传参数用 v-bind:

1
<All :msg="0"></All> 传的就是数字0,不使用就是字符0

props 值只读直接修改会报错,想修改需要在 data 中声明变量接受,从 this 挂载的 props 赋值给

1
2
3
4
5
6
7
8
9
props:{
num
}

data(){
return{
count:this.num
}
}

配置默认值

1
2
3
4
5
props:{
num:{
default:0
}
}

明确知道数据类型后可以设置接受数据的类型

1
2
3
4
5
6
7
props:{
num:Number/
num:{
type:Number
required:true//必填项,默认值不算必须从组件中传过来的值
}
}

# 样式冲突解决:scoped

.vue 中的样式默认加载到全局中会发生样式冲突

给当前组件添加 scoped 的时候组件实例生成一个唯一标识,给组件中的每个标签对应的 dom 元素添加一个标签属性,但是不会在编辑页面显示

1
2
3
4
5
6
7
8
<template>
<div class="example" data-v-49729759>hello world</div>
</template>
<style scoped>
.example[data-v-49729759] {
color: red;
}
</style>

缺点:父元素添加了 scoped 的样式想修改子元素中的标签时修改不了(类似信息可以编辑但是没有网),但是没有加 scoped 的里面又会生效于所有组件

当在 style 前面添加、/deep/ 的时候就可以修改子元素的样式相当于后代选择器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
加了scoped的样式唯一标识是在后面:

h5[data-v-3vxxxx]{
}



/deep/ h5{
}

就是
[data-v-3vxxxx] h5{
}


[data-v-3vxxxx] 就是父元素的唯一标识

使用第三方库的时候需要修改一些样式的时候用到 /deep/

万字 基础篇结束 共 10999 个字 (下一篇万字基础篇 2)

更新于 阅读次数

请我喝[茶]~( ̄▽ ̄)~*

Xiao Yang Guo 微信支付

微信支付

Xiao Yang Guo 支付宝

支付宝

Xiao Yang Guo 贝宝

贝宝