let
在js
中let
为声明变量的关键字,用let
声明的变量,而这里的变量可概括为三种类型,原始类型(数字、字符串、布尔值等)、对象类型、函数类型,对于原始类型,我们可以直接修改其值,对于对象和函数类型我们既可以修改其属性,也可以修改其地址(将对象和函数指向另一对象或函数)。
let a = 1;
let b = {c:1,d:"e"};
a = 2;//合法
b.c = 3;//合法
b = {f:2,g:"h"};//合法
const
与let相反,const
修饰的是常量,对于原始类型,我们不能修改其值;对于对象或函数类型,我们不能修改其地址指向,但是可以修改对象或函数的成员属性的值。
const a = 1;
const b = {c:1,d:"e"};
a = 2;//报错
b.c = 3;//合法
b = {f:2,g:"h"};//报错
踩坑
在日常开发中,可能感觉不到let
和const
的作用,因此我们容易把let
和const
滥用,来看下面一段代码:
<script setup lang="ts">
import {onBeforeMount, onMounted, provide, reactive, ref, watch} from "vue";
import {Book, IBook} from "../model";
import Author from "./Author.vue";
//组件内部的数据
let book = reactive(new Book());
//组件挂载前请求
onBeforeMount( () => {
provide('author', book.author);
requestBookData().then((data) => {
book = data;
});
});
//模拟网络请求
const requestBookData = () => {
return new Promise<IBook>((resolve, reject) => {
setTimeout(() => {
const data = new Book();
data.name = 'Vue3 Vite';
data.price = 100;
data.author.name = 'coco';
data.author.sex = 'man';
data.author.age = 18;
resolve(data as IBook);
}, 1000);
});
}
</script>
<template>
<div class="coco-book">
<h1>书名:{{ book.name }}</h1>
<p>价格:{{ book.price }}</p>
<Author/>
</div>
</template>
<style scoped>
</style>
这是一个名为Book
的组件,我们通过模拟网络请求的方式在组件挂载前去获取该组件的响应式数据,然后将book
的数据渲染出来,可是渲染结果却出乎意料,渲染结果如下:
可以看到book
数据还是初始化状态,可我们明明通过请求数据的方式给booK
对象赋值了呀。找原因找了很久,终于发现了错误的根源,原因就是我在声明book
时用的时let
修饰符,我为什么要用let
修饰符呢,因为赋值简单,只需要直接更改book
的指向即可。就是因为这一偷懒的缘故导了这个出乎意料的结果。因为网络请求时异步的,浏览器的主线程在网络请求时并不会阻塞,因此onBeforeMount
钩子函数将不会阻塞DOM
的渲染时机,DOM
在渲染挂载完毕后book
数据可能还未请求到,此时DOM
绑定的book
对象还是初始化状态,在网络请求结束后虽然book
的数据更新了,但是我们实际上是更改了book
对象指向的地址,而DOM
绑定的响应式数据执行的仍为原book
对象执行的地址,因此页面数据不会刚更新。正确的写法如下:
<script setup lang="ts">
import {onBeforeMount, onMounted, provide, reactive, ref, watch} from "vue";
import {Book, IBook} from "../model";
import Author from "./Author.vue";
//组件内部的数据
const book = reactive(new Book()); //修改为常量,地址不可变
//组件挂载前请求
onBeforeMount( () => {
provide('author', book.author);
requestBookData().then((data) => { //对属性赋值
book.name = data.name;
book.price = data.price;
book.author.name = data.author.name;
book.author.sex = data.author.sex;
book.author.age = data.author.age;
});
});
//模拟网络请求
const requestBookData = () => {
return new Promise<IBook>((resolve, reject) => {
setTimeout(() => {
const data = new Book();
data.name = 'Vue3 Vite';
data.price = 100;
data.author.name = 'coco';
data.author.sex = 'man';
data.author.age = 18;
resolve(data as IBook);
}, 1000);
});
}
</script>
<template>
<div class="coco-book">
<h1>书名:{{ book.name }}</h1>
<p>价格:{{ book.price }}</p>
<Author/>
</div>
</template>
<style scoped>
</style>
渲染结果如下:
小结
在Vue
中,我们声明变量和常量必须想好它的使用场景,对于响应式数据,永远不要使用let去修饰,以免出现不可预料的Bug
。