Vue学习笔记| 青训营笔记

105 阅读5分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 9 天

vue

单文件组件

挂载应用

.mount() 方法应该始终在整个应用配置和资源注册完成后被调用。同时请注意,不同于其他资源注册方法,它的返回值是根组件实例而非应用实例。

模板语法

最基本的数据绑定形式是:文本插值

v-htmlattribute是一个指令,将为渲染得DOM应用特殊的响应式行为。可以用来插入HTML。

attribute 绑定

想要响应式地绑定一个 attribute,应该使用 v-bind 指令

<div v-bind:id="dynamicId"></div>

v-bind 指令指示 Vue 将元素的 id attribute 与组件的 dynamicId 属性保持一致。如果绑定的值是 null 或者 undefined,那么该 attribute 将会从渲染的元素上移除。

简写:因为v-bind非常常用,简写写法

<div :id="dynamicId"></div>

动态绑定多个值

data( ){
​
    return {
​
        objectOfAttrs: {
​
            id: ' container',   
​
            class: 'wrapper'    
​
        }
​
    }
​
}

这样不带参数的v-bind也可以绑定到单个元素上面:

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

v-on

简写为 @

模板

指令语法图

响应式基础

声明响应式状态

选用选项式 API 时,会用 data 选项来声明组件的响应式状态。此选项的值应为返回一个对象的函数。Vue 将在创建新组件实例的时候调用此函数,并将函数返回的对象用响应式系统进行包装。此对象的所有顶层属性都会被代理到组件实例 (即方法和生命周期钩子中的 this) 上。

实例上的属性仅在实例首次创建时被添加,因此你需要确保它们都出现在 data 函数返回的对象上。若所需的值还未准备好,在必要时也可以使用 nullundefined 或者其他一些值占位。

声明方法

要为组件添加方法,我们需要用到 methods 选项。它应该是一个包含所有方法的对象:

不能在定义methods时使用箭头函数,因为箭头函数没有自己的this上下文。

响应式基础 组件式:

我们可以使用 reactive() 函数创建一个响应式对象或数组:

import { reactive } from 'vue'const state = reactive({ count: 0 })

要在组件模板中使用响应式状态,需要在setup( )函数中定义并返回

import { reactive } from 'vue'export default {
  // `setup` 是一个专门用于组合式 API 的特殊钩子函数
  setup() {
    const state = reactive({ count: 0 })
​
    // 暴露 state 到模板
    return {
      state
    }
  }
}
<div>{{ state.count }}</div>

也可以在同一个作用域下定义更新响应式状态的函数,并将他们作为方法与状态一起暴露出去:

import { reactive } from 'vue'export default {
  setup() {
    const state = reactive({ count: 0 })
​
    function increment() {
      state.count++
    }
​
    // 不要忘记同时暴露 increment 函数
    return {
      state,
      increment
    }
  }
}

scrip setup

setup() 函数中手动暴露大量的状态和方法非常繁琐

可以通过使用构建工具来简化该操作。当使用单文件组件(SFC)时,我们可以使用 <script setup> 来大幅度地简化代码。

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

const state = reactive({ count: 0 })

function increment() {
  state.count++
}
</script>

<template>
  <button @click="increment">
    {{ state.count }}
  </button>
</template>

< script setup> 中的顶层的导入和变量声明可在同一组件的模板中直接使用。你可以理解为模板中的表达式和

用 ref( )定义响应式变量

ref ( )方法允许我们创建可以使用任何值类型的响应式ref

ref() 将传入参数的值包装为一个带 .value 属性的 ref 对象:

计算属性

使用计算属性来描述依赖响应式状态的复杂逻辑

<script setup>
import { reactive, computed } from 'vue'

const author = reactive({
  name: 'John Doe',
  books: [
    'Vue 2 - Advanced Guide',
    'Vue 3 - Basic Guide',
    'Vue 4 - The Mystery'
  ]
})

// 一个计算属性 ref
const publishedBooksMessage = computed(() => {
  return author.books.length > 0 ? 'Yes' : 'No'
})
</script>

<template>
  <p>Has published books:</p>
  <span>{{ publishedBooksMessage }}</span>
</template>

computed() 方法期望接收一个 getter 函数,返回值为一个计算属性 ref。和其他一般的 ref 类似,你可以通过 publishedBooksMessage.value 访问计算结果。

计算属性其实也可以用方法去返回,但是为什么不这么做呢?因为计算属性值会基于其响应式依赖被缓存,它的响应式依赖更新了他才会变化,如果用函数每次调用都得计算。而使用缓存的话就不许重复执行。

  • 计算属性默认是只读的,尝试修改一个计算属性时,会收到一个运行时警告。

从计算属性返回的值是派生状态。可以把它看作是一个“临时快照”,每当源状态发生变化时,就会创建一个新的快照。更改快照是没有意义的,因此计算属性的返回值应该被视为只读的,并且永远不应该被更改——应该更新它所依赖的源状态以触发新的计算。

Class 与 Style绑定

绑定对象

我们可以给 :class (v-bind:class 的缩写) 传递一个对象来动态切换 class:

<div :class="{ active: isActive }"></div>

上面的语法表示 active 是否存在取决于数据属性 isActive真假值

...

...

...

..

.

如果你的组件有多个根元素,你将需要指定哪个根元素来接收这个 class。你可以通过组件的 $attrs 属性来实现指定:

<!-- MyComponent 模板使用 $attrs 时 -->
<p :class="$attrs.class">Hi!</p>
<span>This is a child component</span>

template

<MyComponent class="baz" />

这将被渲染为:

html

<p class="baz">Hi!</p>
<span>This is a child component</span>

绑定内联样式

直接绑定一个样式对象通常是一个好主意,这样可以使模板更加简洁:

const styleObject = reactive({
  color: 'red',
  fontSize: '13px'
})
<div :style="styleObject"></div>

同样的,如果样式对象需要更复杂的逻辑,也可以使用返回样式对象的计算属性。

条件渲染

v-if

v-show v-show会在DOM渲染中保留该元素,v-show仅切换了该元素上名为display的css属性

  • 警告

同时使用 v-ifv-for不推荐的,因为这样二者的优先级不明显。请查看风格指南获得更多信息。

v-ifv-for 同时存在于一个元素上的时候,v-if 会首先被执行。

列表渲染

v-for

v-for块中可以完整地访问父作用域内的属性和变量

组件上使用 v-for#

这一小节假设你已了解组件的相关知识,或者你也可以先跳过这里,之后再回来看。

我们可以直接在组件上使用 v-for,和在一般的元素上使用没有区别 (别忘记提供一个 key):

<MyComponent v-for="item in items" :key="item.id" />

但是,这不会自动将任何数据传递给组件,因为组件有自己独立的作用域。为了将迭代后的数据传递到组件中,我们还需要传递 props:

<MyComponent
  v-for="(item, index) in items"
  :item="item"
  :index="index"
  :key="item.id"
/>

不自动将 item 注入组件的原因是,这会使组件与 v-for 的工作方式紧密耦合。明确其数据的来源可以使组件在其他情况下重用。

对数组的操作

事件处理

在内联事件处理器中访问原生 DOM 事件。你可以向该处理器方法传入一个特殊的 $event 变量,或者使用内联箭头函数:

<!-- 使用特殊的 $event 变量 -->
<button @click="warn('Form cannot be submitted yet.', $event)">
  Submit
</button>

<!-- 使用内联箭头函数 -->
<button @click="(event) => warn('Form cannot be submitted yet.', event)">
  Submit
</button>
js
function warn(message, event) {
  // 这里可以访问原生事件
  if (event) {
    event.preventDefault()
  }
  alert(message)
}

事件修饰符

在处理事件时调用 event.preventDefault()event.stopPropagation() 是很常见的。尽管我们可以直接在方法内调用,但如果方法能更专注于数据逻辑而不用去处理 DOM 事件的细节会更好。

Vue 为 v-on 提供了事件修饰符。修饰符是用 . 表示的指令后缀,包含以下这些:

  • .stop
  • .prevent
  • .self
  • .capture
  • .once
  • .passive

使用修饰符时需要注意调用顺序,因为相关代码是以相同的顺序生成的。因此使用 @click.prevent.self 会阻止元素及其子元素的所有点击事件的默认行为,@click.self.prevent 则只会阻止对元素本身的点击事件的默认行为。

.capture.once.passive 修饰符与原生 addEventListener 事件相对应:

template

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

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

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

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

按键修饰符

检查特定的按键。Vue 允许在 v-on@ 监听按键事件时添加按键修饰符。

template

<!-- 仅在 `key` 为 `Enter` 时调用 `submit` -->
<input @keyup.enter="submit" />

你可以直接使用 KeyboardEvent.key 暴露的按键名称作为修饰符,但需要转为 kebab-case 形式。

template

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

在上面的例子中,仅会在 $event.key'PageDown' 时调用事件处理。

按键别名

系统按键修饰符#

你可以使用以下系统按键修饰符来触发鼠标或键盘事件监听器,只有当按键被按下时才会触发。

  • .ctrl
  • .alt
  • .shift
  • .meta

exact 修饰符#

.exact 修饰符允许控制触发一个事件所需的确定组合的系统按键修饰符。

template

<!-- 当按下 Ctrl 时,即使同时按下 Alt 或 Shift 也会触发 -->
<button @click.ctrl="onClick">A</button>

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

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

鼠标按键修饰符#

  • .left
  • .right
  • .middle

这些修饰符将处理程序限定为由特定鼠标按键触发的事件。

表单输入绑定

将表单输入框的内容同步给 JavaScript 中相应的变量。手动连接值绑定和更改事件监听器可能会很麻烦:

template

<input
  :value="text"
  @input="event => text = event.target.value">

v-model 指令帮我们简化了这一步骤:

template

<input v-model="text">

v-model 还可以用于各种不同类型的输入,<textarea><select> 元素。它会根据所使用的元素自动使用对应的 DOM 属性和事件组合:

  • 文本类型的 <input><textarea> 元素会绑定 value property 并侦听 input 事件;
  • <input type="checkbox"><input type="radio"> 会绑定 checked property 并侦听 change 事件;
  • <select> 会绑定 value property 并侦听 change 事件。

多行文本:

textarea标签里面绑定一个v-model,它是不支持插值表示的(标签{{xxx}}标签)是不行的 将多个复选框绑定到同一个数组或者集合:

const checkNames =ref([])

<div>Checked names: {{ checkedNames }}</div>

<input type="checkbox" id="jack" value="Jack" v-model="checkedNames">
<label for="jack">Jack</label>

<input type="checkbox" id="john" value="John" v-model="checkedNames">
<label for="john">John</label>

<input type="checkbox" id="mike" value="Mike" v-model="checkedNames">
<label for="mike">Mike</label>

选择器

单选和多选,多选就是在单选后面+一个multiple

复选框

<input
  type="checkbox"
  v-model="toggle"
  true-value="yes"
  false-value="no" />

true-valuefalse-value 是 Vue 特有的 attributes,仅支持和 v-model 配套使用。这里 toggle 属性的值会在选中时被设为 'yes',取消选择时设为 'no'。你同样可以通过 v-bind 将其绑定为其他动态值:

template

<input
  type="checkbox"
  v-model="toggle"
  :true-value="dynamicTrueValue"
  :false-value="dynamicFalseValue" />

生命周期钩子

每个 Vue 组件实例在创建时都需要经历一系列的初始化步骤,比如设置好数据侦听,编译模板,挂载实例到 DOM,以及在数据改变时更新 DOM。 在此过程中,它也会运行被称为生命周期钩子的函数,让开发者有机会在特定阶段运行自己的代码。

注册周期钩子

onMounted 钩子可以用来在组件完成初始渲染并创建 DOM 节点后运行代码:

vue

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

onMounted(() => {
  console.log(`the component is now mounted.`)
})
</script>

侦听器

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

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

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

// 可以直接侦听一个 ref
watch(question, async (newQuestion, oldQuestion) => {
  if (newQuestion.indexOf('?') > -1) {
    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
    }
  }
})
</script>

<template>
  <p>
    Ask a yes/no question:
    <input v-model="question" />
  </p>
  <p>{{ answer }}</p>
</template>

侦听数据源类型

watch 的第一个参数可以是不同形式的“数据源”:它可以是:

  • 一个 ref (包括计算属性)
  • 一个响应式对象
  • 一个 getter 函数
  • 多个数据源组成的数组

模板引用

组件基础

前面都懂

监听事件

增加改变字体大小功能的示例:

父组件可以通过 v-on@ 来选择性地监听子组件上抛的事件,就像监听原生 DOM 事件那样:

<BlogPost
  ...
  @enlarge-text="postFontSize += 0.1"
 />

子组件可以通过调用内置的 $emit 方法,通过传入事件名称来抛出一个事件:

<!-- BlogPost.vue, 省略了 <script> -->
<template>
  <div class="blog-post">
    <h4>{{ title }}</h4>
    <button @click="$emit('enlarge-text')">Enlarge text</button>
  </div>
</template>

因为有了 @enlarge-text="postFontSize += 0.1" 的监听,父组件会接收这一事件,从而更新 postFontSize 的值。

<!-- BlogPost.vue, 省略了 <script> -->
<template>
  <div class="blog-post">
    <h4>{{ title }}</h4>
    <button @click="$emit('enlarge-text')">Enlarge text</button>
  </div>
</template>

因为有了 @enlarge-text="postFontSize += 0.1" 的监听,父组件会接收这一事件,从而更新 postFontSize 的值。

通过 defineEmits 宏来声明需要抛出的事件:

<!-- BlogPost.vue -->
<script setup>
defineProps(['title'])
defineEmits(['enlarge-text'])
</script>

声明了一个组件可能触发的所有事件,还可以对事件的参数进行验证。同时,这还可以让 Vue 避免将它们作为原生事件监听器隐式地应用于子组件的根元素。

defineProps 类似,defineEmits 仅可用于 <script setup> 之中,并且不需要导入,它返回一个等同于 $emit 方法的 emit 函数。它可以被用于在组件的 <script setup> 中抛出事件,因为此处无法直接访问 $emit

<script setup>
const emit = defineEmits(['enlarge-text'])

emit('enlarge-text')
</script>

通过插槽来分配内容

向组件中传递内容:通过Vue的自定义实现