Vue——一些常用基础知识

条件渲染与列表渲染

  • 条件渲染依旧使用v-if、v-else-if、v-else,目标组件必须相邻
  • 列表渲染也依旧使用v-for=”(item, index) in hobbys”。
  • 下面主要讲的是同时使用。其实vue是不建议同时使用的。
  • 在vue2中,v-if、v-for同时使用的话,v-for的优先级更高,v-if是可以拿v-for中的变量去动态判断的。
  • 在vue3中,v-if、v-for同时使用的话,则相反了,v-if的优先级更高,意味着在v-if中,是拿不到item去判断的!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<template>
<ul>
<!-- 如果不是过滤不是打球的选项 -->
<li v-for="item in hobby" :key="item" v-if="item != '打球'">{{ item }}</li>
<ul>
</template>

<script setup>
import { ref, reactive } from "vue";

const hobby = reactive(
[
'篮球', '足球', '排球', '打球'
]
)
</script>

如此代码编写会导致如图错误

计算属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<template>
<button @click="changeVal">val值+1</button>
<h2>val值:</h2>{{ val }}
<h2>val是否为偶数:</h2>{{ flag }}
</template>

<script setup>
import { ref, reactive, computed } from "vue";

const val = ref(1);

const flag = computed(() => {
// 记得使用.value
return val.value % 2 == 0;
})

function changeVal() {
val.value++;
}
</script>

侦听器watch

  • 监听按数量可以分为两类:单个数据监听、多个数据监听
  • 按监听的数据类型分类可以分为四类:基本数据类型、对象、对象基本属性、对象中对象/数组属性
  • 注意: 如果监听由reactive创建出来的对象的话,不需要什么配置,默认就是深度监听。如果是ref创建出来的对象的话,需要手动打开deep:true配置

语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<template>
<button @click="changeVal">val值+1</button>
<h2> val值:</h2>{{ val }}
</template>

<script setup>
import { ref, reactive, computed, watch } from "vue";

const val = ref(1);

watch(val, (newVal, oldVal) => {
console.log("val的值改变了", newVal, oldVal);
})

function changeVal() {
val.value++;
}
</script>

监听对象

  • 默认是深度监听
  • 如果想监听整个user改变的话,就不能使用reactive了,要使用ref,然后修改user.value = …即可监听到
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
<template>
<button @click="changeAge">用户年龄+1</button>

<h2>用户名</h2>{{ user.name }}
<h2>用户年龄</h2>{{ user.age }}
</template>

<script setup>
import { ref, reactive, computed, watch } from "vue";

let user = reactive({
name: "张三",
age: 18
})

// 只有由reactive创建出来的对象,才不需要手动开启deep配置,默认深度监听
watch(user, (newVal, oldVal) => {
console.log("user的值改变了", newVal, oldVal);
})


function changeAge() {
user.age++;
}
</script>

监听对象的一个属性(要提供一个get函数才可以)

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
<template>
<button @click="changeAge">用户年龄+1</button>

<h2>用户名</h2>{{ user.name }}
<h2>用户年龄</h2>{{ user.age }}
</template>

<script setup>
import { ref, reactive, computed, watch } from "vue";

let user = reactive({
name: "张三",
age: 18
})

// 参数1为get函数
watch(() => user.age, (newVal, oldVal) => {
console.log("userAge的值改变了", newVal, oldVal);
})


// 错误,因为得到的是一个基本数据类型number。如果监听的是user中的对象/数组的话,则这么写
//watch(user.age, (newVal, oldVal) => {
// console.log("userAge的值改变了", newVal, oldVal);
//})


function changeAge() {
user.age++;
}
</script>

监听配置

  • 如果监听的是由reactive创建出来的对象的话,deep失效,默认深度监听。如果是由ref创建出来的对象的话,则需要手动开启deep配置
1
2
3
4
5
6
watch(user, (newVal, oldVal) => {
console.log("user的值改变了", newVal, oldVal);
}, {
immediate: true, // 立即执行
deep: true // 深度监听
})

侦听器-watchEffect

  • 如果你想监听一个对象中的多个属性的话,使用watchEffect会更好,因为它只会监听函数中用到的属性,而不是整个对象中的所有属性;
  • 如果函数里面有数组类型的数据,则监听不到整个数组的变化,只能监听某一个下标值的变化
  • 刚加载出来的时候,会执行一次,相当于默认immediate: true
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
<template>
<button @click="changeAge">用户年龄+1</button>
<button @click="changeAddress">修改用户地址</button>
<button @click="changeHobbys">修改用户爱好</button>


<h2>用户名</h2>{{ user.name }}
<h2>用户年龄</h2>{{ user.age }}
<h2>爱好</h2>{{ user.hobbys }}
</template>

<script setup>
import { ref, reactive, computed, watch, watchEffect } from "vue";

let user = reactive({
name: "张三",
age: 18,
hobbys: ["吃饭", "睡觉", "打豆豆"],
address: {
province: "浙江省",
city: "杭州市",
county: "西湖区"
}
})


/* 没有新旧值的参数 */
watchEffect((aaa) => {
console.log("userAge可能改变了", user.age);
console.log("userHobby可能改变了", user.hobbys[0]);
// console.log("userHobby可能改变了", user.hobbys); 监听失效
console.log("userAddress可能改变了", user.address.city);
})

function changeHobbys() {
user.hobbys[0] = "打游戏";
}

function changeAge() {
user.age++;
}

function changeAddress() {
user.address.city = "温州市";
}
</script>

defineProps() 宏声明

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
<template>
<div class="myButton">
<button @click="changeColor" :style="{backgroundColor: btnBgcColor}">点我改变按钮颜色(局部导入)</button>
</div>
</template>

<script setup>
import { ref } from 'vue'

const prop = defineProps({
defaultColor: {
type: String,
default:'red'
}
})

console.log(prop);

// 获取父组件传入的参数
const btnBgcColor = ref(prop.defaultColor);


function changeColor() {
btnBgcColor.value = randomColor();
}

function randomColor() {
return '#' + Math.random().toString(16).slice(2, 8).padEnd(6, '0');
}

</script>

<style scoped lang="scss">
.myButton {
button {
padding: 5px 10px;
border-radius: 5px;
}
}
</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
<!-- 子组件 -->
<template>
<div class="myButton">
<!-- 直接使用$emit,调用预期绑定的函数即可 -->
<button @click="$emit('myButtonClick')">点我执行方法</button>
</div>
</template>



<!-- 父组件 -->
<template>
<MyButton @myButtonClick="handleBtnFun"></MyButton>
</template>

<script setup>
import MyButton from '@/components/MyButton.vue';

// 点击 MyButton 组件中的按钮,将会执行这个方法
function handleBtnFun() {
alert('按钮被点击了');
}

</script>

写法二 (规范)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!-- 子组件 -->
<template>
<div class="myButton">
<button @click="handleBtnFun">点我执行方法</button>
</div>
</template>

<script setup>
// 需要先声明接受的函数
/*
如果这里没有声明,下面直接调用,那么控制台会警告
[Vue warn]: Component emitted event "myButtonClick1" but it is neither declared in the emits option nor as an "onMyButtonClick1" prop.
*/
const emit = defineEmits(['myButtonClick'])

function handleBtnFun() {
emit('myButtonClick')
}

</script>

defineEmits校验参数

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
<!-- 子组件 -->
<template>
<div class="myButton">
<button @click="handleBtnFun">点我执行方法</button>
</div>
</template>

<script setup>
// 需要先声明接受的函数
/*
如果这里没有声明,下面直接调用,那么控制台会警告
[Vue warn]: Component emitted event "myButtonClick1" but it is neither declared in the emits option nor as an "onMyButtonClick1" prop.
*/
const emit = defineEmits({
myButtonClick: data => {
console.log(data);
// 判断参数是否小于 0.5 如果小于,则警告,但是并不影响执行父组件绑定的方法
if(data < 0.5) {
console.warn('data < 0.5');
return false;
}

return true;
}
})

function handleBtnFun() {
emit('myButtonClick', Math.random())
}

</script>



<!-- 父组件 -->
<template>
<!-- 局部导入 -->
<MyButton defaultColor="pink" @myButtonClick="handleBtnFun"></MyButton>
</template>

<script setup>
import MyButton from '@/components/MyButton.vue';

function handleBtnFun(data) {
alert('按钮被点击了', data);
}

</script>

组件v-model实现父传子件

子组件

1
2
3
4
5
6
7
8
9
10
<template>
<input type="text" :value="modelValue" @input="$emit('update:modelValue', $event.target.value)">
</template>
<script setup>
const { modelValue } = defineProps(['modelValue'])

console.log(modelValue);

</script>

父组件

1
2
3
4
5
6
7
8
9
10
11
12
<template>
<!-- 局部导入 -->
<MyInput v-model="value"></MyInput>
</template>

<script setup>
import { ref } from 'vue';
import MyInput from '@/components/MyInput.vue';

const value = ref('');

</script>

修改默认绑定的modelValue属性名

提供了给v-model提供参数的写法,可以修改绑定的属性名v-model:xxx=”value”

1
2
3
4
5
6
7
8
9
10
11
12
<template>
<!-- 将v-model 默认绑定的modelValue属性名 改为abc -->
<MyInput v-model:abc="value"></MyInput>
</template>

<script setup>
import { ref, reactive, nextTick } from 'vue';
import MyInput from '@/components/MyInput.vue';

const value = ref('');

</script>

处理组件多v-model情况

父组件

1
2
3
4
5
6
7
8
9
10
11
12
13
<template>
<!-- 局部导入 -->
<MyInput v-model:value1="value1" v-model:value2="value2"></MyInput>
</template>

<script setup>
import { ref, reactive, nextTick } from 'vue';
import MyInput from '@/components/MyInput.vue';

const value1 = ref('');
const value2 = ref('');

</script>

子组件

1
2
3
4
5
6
7
8
9
10
<template>
<input type="text" :value="value1" @input="$emit('update:value1', $event.target.value)">
<br>
<br>
<input type="text" :value="value2" @input="$emit('update:value2', $event.target.value)">
</template>
<script setup>
const { value1, value2 } = defineProps(['value1', 'value2'])

</script>

组件内单独处理v-model修饰符

父组件

1
2
3
4
5
6
7
8
9
10
11
12
<template>
<!-- 局部导入 -->
<MyInput v-model.capitalize="value"></MyInput>
</template>

<script setup>
import { ref, reactive, nextTick } from 'vue';
import MyInput from '@/components/MyInput.vue';

const value = ref('');

</script>

子组件

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
<template>
<input type="text" :value="modelValue" @input="emitValue">
</template>
<script setup>
const emit = defineEmits(['update:modelValue'])
const { modelValue, modelModifiers } = defineProps({
modelModifiers: {
type: Object,
default: () => ({})
},
modelValue: {
type: String,
default: ''
}
})

console.log("modelModifiers", modelModifiers.capitalize); // true

// input 修改事件,将首字母转为大写
function emitValue(el) {
let val = el.target.value;

if(modelModifiers.capitalize) {
// 将modelValue首字母大写
val = val.charAt(0).toUpperCase() + val.slice(1);
}

emit('update:modelValue', val)
}
</script>

插槽

加载成功和加载失败效果制作

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
<--这里的MyButtion组件,Error,Loading组件自己编写!->
<template>
<div class="homeView">
<h2>父组件</h2>
<MyButton></MyButton>
</div>
</template>

<script setup>
import { ref, defineAsyncComponent } from 'vue';
import Error from '@/components/Error';
import Loading from '@/components/Loading';

const MyButton = defineAsyncComponent({
loader: () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
// reject('111') // 使用reject后,也会展示Error组件
resolve(import('@/components/MyButton.vue'))
}, 2000) // 改成大于3秒后,将会展示Error组件。
})
},
// 加载异步组件时使用的组件
loadingComponent: Loading,
// 展示加载组件前的延迟时间,默认为 200ms
delay: 200,
// 加载失败后展示的组件
errorComponent: Error,
// 如果提供了一个 timeout 时间限制,并超时了
// 也会显示这里配置的报错组件,默认值是:Infinity
timeout: 3000
})

</script>

<style scoped lang="scss">
.homeView {
border: 1px solid red;
}
</style>