4. 语法进阶

一、前言

学习完前面一章的内容,你基本就已经可以开始使用vue开发前端页面了。

本章内容为vue中的进阶语法,通过学习本章的语法内容,可以在一定程度上简化你的代码量、让你对vue框架理解的更加深入、学习使用一些更加高级的特性。

二、生命周期

如果你熟悉任何一门编程语言,我相信你对“生命周期”这个词并不陌生,它常常被用于描述语言中一个变量的存在范围。

vue中其实也差不多,只不过这里不再是变量,而是描述的“组件”。

前面我们就说过,vue是以组件为单位组织代码的,一个组件中就包含了htmljscss三种代码。

那么既然是组件,那当它被其它组件使用的时候,就会出现一系列状态:被创建、被加载、被更新、被卸载。

官网有张图详细描述了一个组件的全过程:

image-20230916073816856

看着似乎有点多,但实际上在大部分情况下,我们用到的并不多。

从上图看得出来,一个vue组件总共有7种主状态。

  1. 从渲染器遇到一个组件开始
  2. 到初始化可选api,该过程就有生命周期钩子setupbeforeCreate
  3. 判断是否编译了这个模板(也就是vue组件),该过程有一个生命周期钩子created
  4. 渲染创建以及插入DOM节点,这一过程有一个beforeMount生命周期钩子
  5. 挂载这个组件,也就是将这个组件正式放置到页面上,有一个mounted生命周期钩子
  6. 重渲染修补状态,也就是当你更改组件中的标签内容时,有两个生命周期钩子:beforeUpdateupdated
  7. 卸载组件,也就是从页面上删除这个组件,有两个生命周期钩子:beforeUnmountunmounted

上面主要是理了一下这张图的逻辑,也就是你只需要看中间的部分就可以了,一个组件从被创建直到销毁,总共有7种状态。

然后这些组件状态在切换时,官方就提供了一些钩子函数,可以让我们在其中做一些事情。

所谓钩子函数,就是将这个函数挂在指定为位置,当满足条件后,这个函数就会被自动调用。

那么如何使用呢?这在vue3的组件化编程中很简单:

<script setup>
import { onMounted } from 'vue';

//当组件被挂载时调用
onMounted(()=>{
  console.log('组件被挂载了!');
});
</script>

因为组件化编程中将所有功能都被封装成了单独的函数,所以你只需要在前面那些钩子函数名字添加一个on即可从vue中导出对应的生命周期钩子函数。

比如上面我就导出了一个最常用的钩子函数onMounted,即图中mounted生命周期钩子所在处。

为什么常用呢?因为它所处的位置刚好是DOM节点被创建且初始化之后被调用的。

DOM节点应该知道吧?形象的来说,就是你看到的div那些标签。

也就是说,如果我们想要初始化一下数据到页面上,那就必须要得等DOM初始化完成,而这个钩子函数所处的位置就刚刚好。

比如还有beforeMount,其对应的函数就是onBeforeMount,它就是在DOM初始化之前被调用,如果你在这个钩子函数中试图操作DOM节点,那么必然会报错。

如何操作?比如使用js中的queryselect等函数来查询节点,又或者使用vue中的ref变量绑定节点。这就是在操作节点。

其它都是类似的意思,这里就不再过多赘述了。

其使用方式就是向这些钩子函数中传入一个函数作为参数,之后这个函数就会在相应的时间节点被调用。

实际上,我们这里的script标签上的setup属性,就是前面图中的那个setup,只不过官方给我们简化了的。

如果你不写这个setup,那么其实也等价于下面这种代码:

<script>
import { onMounted} from 'vue';
export default {
  setup(msg) {
    const msg=ref('这是一个ref变量');
    onMounted(() => {
      console.log('组件被挂载了!');
    });
  },
};
</script>

可以看到,如果不用这个语法糖,代肉眼可见的变得复杂起来了。

因为我们所有的函数、变量都要写入export default{}中的setup函数中。

三、数据双向绑定

前面我们已经使用过属性绑定,会发现那真的很有用,可以实现动态修改标签属性的目的。

但那也只能实现变量到标签属性的单方向绑定,也就是只能通过变量的变化,然后反映到标签上。

而有时候我们也需要动态将标签属性的变化反应到变量上,最常见的就是input标签,它有一个value属性代表当前标签内部用户输入的值。

很多时候我们都是需要在用户输入内容后获取这个值的,如果是以前,我们只能通过js代码来获取这个属性值,但现在vue也给我们提供了方便语法,可以快捷实现这一功能。

vue提供了我们一个叫做v-model的指令就可以实现属性与变量双向绑定的功能:

<script setup>
import { ref } from 'vue';
const val=ref();
</script>

<template>
  <input type="text" v-model="val">
</template>

使用方式并不难,你只需要使用vue提供的v-model指令让其等于一个响应式变量,就实现了双向绑定。

具体实现的效果就是,一旦你修改了响应式变量的值,input控件中的显示内容会实时发生变化。

同时当用户修改input变量中的值时,该响应式变量中的值也会实时变化,这便是双向绑定。

同时由于它默认会自动绑定标签中的value属性,所以无需你去指定。

甚至你还可以为它添加修饰符,就像前面的事件处理那样:

<input type="text" v-model.number="val">

共有三个修饰符:

  • lazy:默认情况下,v-model 会在每次 input 事件后更新数据。你可以添加 lazy 修饰符来改为在每次 change 事件后更新数据:

  • number:让用户输入自动转换为数字

  • trim:默认自动去除用户输入内容中两端的空格

除了input标签外,双向绑定的用法还可以用在其它“表单”标签上。

比如文本输入框也可以绑定:

<textarea v-model="val"></textarea>

还有复选框:

<input type="checkbox" v-model="checked" />

如果有多个复选框,可以自动将其结果放在一个列表中:

<template>
<div>Checked names: {{ checkedNames }}</div>
<input type="checkbox" value="Jack" v-model="checkedNames">
<input type="checkbox" value="John" v-model="checkedNames">
<input type="checkbox" value="Mike" v-model="checkedNames">
</template>

<script setup>
import { ref } from 'vue';
const checkedNames=ref([]);
</script>

还有单选框:

<template>
<input type="radio" value="One" v-model="picked" />
<input type="radio" value="Two" v-model="picked" />
<div>{{ picked }}</div>
</template>

<script setup>
import { ref } from 'vue';
const picked=ref();
</script>

以及选择器:

<template>
<div>Selected: {{ selected }}</div>
<select v-model="selected">
  <option>A</option>
  <option>B</option>
  <option>C</option>
</select>
</template>

<script setup>
import { ref } from 'vue';
const selected=ref();
</script>

等等等等,更详细的内容可以查看官网介绍:表单输入绑定

四、计算属性

前面提到的模板语法提到过,模板中是可以写表达式的:

<template>
  {{ msg.substring(0,msg.lastIndexOf('W')).toLocaleLowerCase()  }}
</template>

但这样写太过于复杂了,你一眼很难看出这里最后的结果是什么。

所以后面我们用了一个函数来代替这个过程:

image-20230703165254424

其实计算属性和上面这个方法目的差不多,就是为了简化模板语法中表达式的。

但居然官方要多出这么个“计算属性”,那想来肯定还是会有点不一样的,所以下面我们主要来看看如何使用计算属性、以及它与使用方法有什么区别。

它的使用方法也很简单,首先是引入computed这个方法。

import { computed } from 'vue';

这个方法接受一个函数,并返回一个对象,就像下面这样:

const process_msg=computed(()=>{
    //处理其它步骤
    return '返回最后的结果'
});

然后在你需要显示的地方,显示这个process_msg即可:

<template>
  {{ process_msg  }}
</template>

这和普通的方法非常的像,下面给一份完整代码:

<script setup>
import { ref,computed } from 'vue';
const msg="HELLO WORLD";

const process_msg=computed(()=>{
  let pos=msg.lastIndexOf('W');
  let sub=msg.substring(0,pos);
  return sub.toLocaleLowerCase();
});
</script>

<template>
  {{ process_msg  }}
</template>

所以说实话,大部分情况下其实计算属性你都可以直接用方法替换,所以他们两者有什么区别呢?

两者唯一区别是计算属性会缓存,而方法不会。

举个例子,你有一个响应式的数组,划重点,一定要是响应式的:

reactive([1,2,3,4,5,6...])

假设该数组中有一万个元素,现在我分别写一个计算属性、一个方法来计算出该数组中所有元素的和。

那么区别就来了,使用方法时,只要你调用一次,那它就会循规蹈矩的计算一次。

而计算属性却不同,只要这个数组没有被更新过,那它就只计算第一次,并将第一次的结果存起来,一旦你下次使用就会直接返回上一次的计算结果,不会重新计算,这就是两者最显著的区别。

所以总的来说,如果能用计算属性,那还是尽量使用计算属性,毕竟人家官方专门弄出了这么个东西,肯定有它存在的道理。

除此之外官方文档还提到了一个计算属性修改变量的点,但正如官方自己所说,使用这个特性会引起警告,所以这里不再深入。

image-20230703213555907

如果你非要改东西,那还不如用方法呢,毕竟现在计算机越来越快,只要别太离谱,也不会在意哪一点损耗了。

五、监听器