Vue的基础

浮生半日闲 发布于 2023-09-10 59 次阅读


1、简介

vue是一款用于构建用户界面的javascript框架。它基于标准的HTML、CSS和Javascript构建,并提供了一套声明式的、组件化的编程模型,帮助高效的开发用户界面。

2、基础知识

2.1 创建应用

import { createApp } from 'vue'
// 从一个单文件组件中导入根组件
import App from './App.vue'

const app = createApp(App)

2.2 模板语法

插值文本

使用{{ msg }} 直接进行展示。

插入html

使用v-html指令渲染富文本:<span v-html="rawHtml"></span>

属性绑定

<div :id="idValue"></div> 将div的id属性与idValue进行绑定,如果idValue为null或者undefined时,则不会显示id属性。完整写法为v-bind:id,实际使用中,v-bind可以直接可以省略掉;

动态绑定多个属性

可以通过不带参数的v-bind将多个属性同时绑定到元素上,如:

const objectOfAttrs = {
  id: 'container',
  class: 'wrapper'
}

<div v-bind="objectOfAttrs"></div>

使用javascript表达式

vue中所有的数据绑定都支持javascript表达式,并且仅支持单一表达式,不支持语句和逻辑控制等,如:

{{ count + 1 }}
{{ isTrue ? 1 : 0 }}
{{ message.split(',') }}
<div :id="`list-${index}`"></div>
{{ format(date) }}:如果使用方法进行渲染,此时组件在每次更新时都会被重新调用,因此不能做任何改变数据或者触发异步操作

指令

指令指的是带有 v- 前缀的特殊属性,如前面的v-html、v-bind;指令的参数:

<a v-bind:href="href"></a>:设置指令的参数为href
<a v-bind:[attributeName]="href"></a>:指令也可以设置动态参数,只需要将参数名用[]括起来,即表示动态参数;动态参数名应该是一个字符串或者null,不应该使用表达式;如果是复杂的动态参数,可以使用计算属性来解决;

修饰符

修饰符是以点开头的特殊后缀,表名指令需要以一些特殊的方式被绑定。

.prevent: <form @submit.prevent="onSubmit"></form>,告知指令对触发的事件调用event.preventDefault();

2.3 响应式声明

ref()

使用ref()来声明响应式状对象,然后可以使用.value属性来返回对象的值。需要注意的是,在模板中使用ref对象时,会自动解包,不需要添加.value,直接使用即可。{{ count }}

<template>
{{ count }}
</template>

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

// 声明count为响应式对象
const count = ref(0);

// 操作响应式对象
count.value ++;

</script>

在ts中,为ref标注类型:

<script setup>
import { ref, type Ref } from 'vue'

const year: Ref<number> = ref(2020)
</script>

为什么要使用带有.value属性的ref,而不是普通的变量?

在vue中,ref值改变的时候,需要相应的更新对应的DOM,在标准的javascript中,检测普通变量的访问和修改是行不通的,而使用.value形式的对象时,可以通过setter和getter方法来拦截对象属性的set和get操作,从而来触发DOM的更新(这里需要注意的是:ref改变后,DOM的更新不是同步的,而是会在next tick更新周期中缓冲所有状态的修改,以确保不管修改多少次状态,每个组件只会被更新一次。因此,如果想要等待DOM更新完成后,执行相应的代码,可以使用nextTick()方法)。另一方面,因为ref是对象,当传递给函数时,可以保留对最新值的访问。

reactive()

ref()是创建一个具有.value属性的响应式对象,而reactive()是使对象本身具有响应性(因此,reactive只能用于对象类型,不能用于如string、number或boolean这样的原始类型)。需要注意的是,reactive()返回的是原始对象的Proxy,它和原始对象是不相同的。

<template>
{{ proxy.count }}
</template>

<script setup lang="ts">
import { reactive } from 'vue'

const raw = { count: 0 }
const proxy= reactive(raw)

console.log(proxy=== raw); // false

// 在同一个对象上调用 reactive() 会返回相同的代理
console.log(reactive(raw) === proxy) // true

// 在一个代理上调用 reactive() 会返回它自己
console.log(reactive(proxy) === proxy) // true

</script>

建议使用ref()作为响应式状态的主要API

2.4 计算属性

在模板中,可以使用javascript表达式,但是如果模板中的逻辑过于复杂,维护起来就会很难,因此,如果需要在模板中显示复杂逻辑,可以使用计算属性。

<template>
{{ showMsg }}
</template>

<script setup lang="ts">
import { computed } from 'vue';

const count = ref(0);

// 返回计算属性  ref对象
const showMsg = computed<number>(() => {
    return count.value + 1;
})
</script>

需要注意的是,计算属性的值会基于其响应式依赖被缓存,如上面例子,只有当count发生变化的时候,showMsg才会重新进行计算,这也是计算属性和方法的主要区别。

其次,默认情况下,计算属性是只读的,不能被修改如果需要计算属性可写,需要提供set和get方法,这个意义不大,计算属性的返回值应该被视为只读的,并且永远不应该被更改

2.5 class和style的绑定

<template>
    <!-- 对象方式  -->
    <div :class="{active: isActive}"></div>
    <!--  数组方式   -->
    <div :class="['class1', isActive ? 'active' : '']"></div>
    <!--  嵌套对象方式   -->
    <div :class="['class1', {active: isActive}]"></div>
    <!-- 计算返回  -->
    <div :class="classObject"></div>
</template>

<script setup lang="ts">
import { ref } from 'vue';

const isActive = ref(true);

// 使用computed计算得出也是一样的
const classObject = reactive({
  active: true,
  'text-danger': false
})
<script>

style的绑定和class绑定基本一致,只是将绑定对象换成:style,其他逻辑不变。

2.6 条件渲染

通过v-ifv-else-ifv-elsev-show来有条件的渲染一块内容,只有当返回值为真时,内容才会被渲染出来。

<template>
    <div v-if="user.name == 'a'">姓名</div>
</template>

<script setup lang="ts">
import { ref } from 'vue';

const user = ref({
    name: 'a'
});
</script>

v-if和v-show的区别:

v-if的渲染是真实的,只有为true时,才会渲染;而v-show,无论结果如何,都会渲染,只是display属性会进行切换。因此,v-if拥有更高的切换开销,如果运行时,绑定条件很少改变,则v-if更适合;v-show有更高的初始渲染开销,如果需要频繁切换,则v-show效果更好。

2.7 列表渲染

可以通过v-for指令来渲染一个数组列表,指令内使用item in array形式的语法,其中,array是需要显示的数组,item是数据每一项的别名。

<template>
    <div v-for="item in array" :key="item.id">
        {{ item }}
    </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';

const array = ref([{id: 1, name: '张三'}, {id: 2, name: '李四'}]);
</script>

使用v-for遍历对象属性,遍历出来的属性是基于对象Object.keys()返回值来决定:

<template>
    <!--  这里的value是对象的值  -->
    <div v-for="value in array" :key="value">
        {{ value }}
    </div>

    <!--  
        通过多参的形式,来获取不同的属性  
        第一个参数:对象的属性值
        第二个参数:对象的key
        第三个参数:位置索引
    -->
    <div v-for="(value, key, index) in array" :key="value">
        {{ value }}
    </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';

const array = ref({id: 1, name: '张三'});
</script>

注意:

(1)v-for每个元素对应的块,必须提供一个唯一的key属性,以方便vue跟踪每个节点的标识,从而重用和重新排序现有的元素;

(2)同一个dom节点上,别同时使用v-for和v-if,v-if的优先级会比v-for高,也就是说v-if的条件无法使用v-for作用域内定义的变量;

(3)在使用数组时,vue只能通过push、pop、shift、unshift、splice、sort、reverse这些方法监听到数组的变化,从而触发相关的更新。

2.8 事件处理

可以通过 v-on 指令(简写 @ )来监听dom事件,并在事件触发时执行对应的方法。

<template>
    <!--  内联事件处理器,直接写相关语法  -->
    <button @click="count++">Add 1</button>
    <p>Count is: {{ count }}</p>

    <!--  方法事件处理器,调用相关方法  -->
    <button @click="add">Add 1</button>

    <!--  自定义传参  -->
    <button @click="say('你好')">自定义传参</button>

    <!--  自定义传参,并传入事件对象  -->
    <button @click="sayEvent('你好', $event)">自定义传参</button>
</template>

<script setup lang="ts">
import { ref } from 'vue';

const count = ref(0);

const add = (event: MouseEvent) => {
    count.value ++;
}

const say = (word: string) => {
    console.log(word);
}

const sayEvent = (word: string, event: MouseEvent) => {
    console.log(word);
}
</script>

事件修饰符

<template>
    <!-- 单击事件将停止传递 -->
    <button @click.stop="dosomething">自定义传参</button>

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

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

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

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

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

    <!-- 滚动事件的默认行为 (scrolling) 将立即发生而非等待 `onScroll` 完成 , 一般用于触摸事件的监听器,可以用来改善移动端设备的滚屏性能。-->
    <!-- 以防其中包含 `event.preventDefault()` -->
    <div @scroll.passive="dosomething">...</div>

    <!-- 
        监听键盘事件,仅在 `key` 为 `Enter` 时调用 `submit` 
        vue中常用的按键别名:
        - .enter
        - .tab
        - .delete (捕获“Delete”和“Backspace”两个按键)
        - .esc
        - .space
        - .up
        - .down
        - .left
        - .right
    -->
    <input @keyup.enter="dosomething" />

    <!-- 监听键盘事件,可以直接使用 KeyboardEvent.key 暴露的按键名称作为修饰符,但需要转为 kebab-case 形式   -->
    <input @keyup.page-down="dosomething" />

    <!--
        系统按键修饰符,
        - .ctrl
        - .alt
        - .shift
        - .meta:在 Mac 键盘上,meta 是 Command 键 (⌘)。在 Windows 键盘上,meta 键是 Windows 键 (⊞)。在 Sun 微机系统键盘上,meta 是钻石键
        - .exact:控制触发一个事件所需的确定组合的系统按键修饰符
    -->
    <!-- Alt + Enter -->
    <input @keyup.alt.enter="dosomething" />

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

    <!-- 仅当没有按下任何系统按键时触发 -->
    <button @click.exact="dosomething">A</button>

    <!-- 仅当按下 Ctrl 且未按任何其他键时才会触发 -->
    <button @click.ctrl.exact="dosomething">A</button>

    <!--    
        鼠标按键修饰符
        .left
        .right
        .middle
        这些修饰符将处理程序限定为由特定鼠标按键触发的事件。
    -->
</template>

<script setup lang="ts">
import { ref } from 'vue';

const dosomething = () => {}

</script>

2.9 输入绑定

使用v-model来进行表单数据的绑定。

<template>
    <p>Message is: {{ message }}</p>
    <input v-model="message" placeholder="edit me" />

    <!-- .lazy修饰符,在 "change" 事件后同步更新而不是 "input" -->
    <input v-model.lazy="message" />

    <!-- 让用户输入自动转换为数字,如果该值无法被 parseFloat() 处理,那么将返回原始值   -->
    <input v-model.number="age" />

    <!-- 默认自动去除用户输入内容中两端的空格   -->
    <input v-model.trim="message" />
</template>

<script setup lang="ts">
import { ref } from 'vue';

const message = ref()
const age = ref()

</script>