2022-12-19 | 学习笔记 | UNLOCK | 更新时间:2023-2-21 14:21

Vue3的学习笔记从入门到入土

Vue 3 是 Vue 当前的最新主版本。它包含了一些 Vue 2 中没有的新特性 (比如 Teleport、Suspense,以及多根元素模板)。同时它也包含了一些与 Vue 2 非兼容性的变更

简单概念

  • 什么是Vue,什么是单文件组件

Vue是基于 HTML、CSS 和 JS 的一套声明式的、组件化的 JavaScript 框架;Vue把 HTML、CSS 和 JS封装在同一个Vue文件里,通过解析模板去进行解析,再通过预处理器去处理。它给我们带来了模板化开发,独立作用域,预处理器等多种优点。

  • 什么是模板语法

Vue 使用一种基于 HTML 的模板语法,能够声明式地将数据绑定到 DOM 上。通过解析器进行解析

<span>文本插值 {{ msg }}</span>
<span :text="text">属性绑定attar</span>
<span :[attar]="text">动态参数attar</span>
<span @[attar]="text">动态方法attar</span>

简单使用

vue目前有两种编写方式,你可以使用vue2版本的选项式编写,也可用使用vue3新添加setup函数,文章后面的示例大部分用了setup组合式语法

//选项式
export default {
  data() { //data数据
    return{
      count:1,
    }
  },
  mounted() { //生命周期钩子
    console.log(this.count) // 1
  },
}
<script setup>
//组合式
import { ref,onMounted } from 'vue'
const count = ref(0) //data数据
onMounted(()=>{ //生命周期钩子
  console.log(count.value) // 1
})
</script>

响应式数据 ref reactive

refreactive 是vue3基于组合式api模式下,在 setup 中用于声明的具有响应式的数据的方法, ref 通常用于声明基础类型响应式数据,reactive 用于声明复杂类型响应式数据

import { ref, reactive } from 'vue'
const count = ref(1)
console.log(count.value) // 1
const state = reactive({ count:1 })
console.log(state.count) // 1

计算属性 computed

接受一个 getter 函数,返回一个只读的响应式 ref 对象。该 ref 通过 .value 暴露 getter 函数的返回值。它也可以接受一个带有 getset 函数的对象来创建一个可写的 ref 对象

import { reactive, computed } from 'vue'
const state = reactive({
  name: 'John Doe',
})
const publish = computed(() => { // 只读,赋值会错误
  return state.name == 'John Doe' ? 'Yes' : 'No'
})

//高级:可编辑计算属性
const fullName = computed({
  get() { //getter
    return firstName.value + ' ' + lastName.value
  },
  set(newValue) { //setter
    [firstName.value, lastName.value] = newValue.split(' ')
  }
})

样式属性 :class :style

vue可以通过响应式ref reactive对象给元素对象动态绑定class和属性

import {ref,reactive} from 'vue'
const isActive = ref(true)
const classObject = reactive({
  active: true
})
const styleObject = reactive({
  color: 'red',
  fontSize: '13px'
})

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

指令·修饰符 v-text

  • v-once 仅渲染一次
  • v-text 更新元素的文本内容
  • v-html 更新元素的 innerHTML
  • v-pre 跳过该元素及其所有子元素的编译
  • v-cloak 用于隐藏尚未完成编译的 DOM 模板
    <span v-once v-text="msg"></span>
    <span v-cloak v-html="innerhtml"></span>
    <span v-pre>{{masg}}</span>
  • v-show 基于表达式值的真假性,来改变元素的 display 来改变可见性
  • v-if 基于表达式值的真假性,来改变元素的是否渲染来改变可见性
  • v-else-if 基于v-if 的链式表达
  • v-else 基于v-ifv-else-if的链式表达
    <span v-show="msg=='na'"></span>
    <span v-if="msg=='na'"></span>
    <span v-else-if="msg=='ma'"></span>
    <span v-else></span>
  • v-for 基于原始数据多次渲染元素或模板块
  • v-memo 基于依赖数组进行优化
    <div v-for="item in items" :key="item.key" v-memo="items">
      {{ item.text }}
    </div>
  • v-on 给元素绑定事件监听器,缩写符 @
    <span v-on:click="msg='na'">{{mag}}</span>
    <span @click.stop="msg='na'">{{mag}}</span>
    
    <!-- .stop ---- 调用 event.stopPropagation() -->
    <!-- .prevent - 调用 event.preventDefault() -->
    <!-- .capture - 在捕获模式添加事件监听器 -->
    <!-- .self ---- 只有事件从元素本身发出才触发处理函数 -->
    <!-- .{keyAlias} - 只在某些按键下触发处理函数 -->
    <!-- .once ---- 最多触发一次处理函数 -->
    <!-- .left ---- 只在鼠标左键事件触发处理函数 -->
    <!-- .right --- 只在鼠标右键事件触发处理函数 -->
    <!-- .middle -- 只在鼠标中键事件触发处理函数 -->
    <!-- .passive - 通过 { passive: true } 附加一个 DOM 事件 -->
  • v-bind 给元素绑定事件监听器,缩写符 :
    <span v-bind:title="msg"></span>
    
    <!-- .camel - 将短横线命名的 attribute 转变为驼峰式命名 -->
    <!-- .prop -- 强制绑定为 DOM property (3.2+) -->
    <!-- .attr -- 强制绑定为 DOM attribute (3.2+) -->
  • v-model 在表单输入元素或组件上创建双向绑定
    <input v-model="msg"/>
    
    <!-- .lazy - 监听 change 事件而不是 input -->
    <!-- .number - 将输入的合法字符串转为数字 -->
    <!-- .trim - 移除输入内容两端空格 -->

自定义指令

指令比 autofocus attribute 更有用,因为它不仅仅可以在页面加载完成后生效,还可以在 Vue 动态插入元素后生效

<script setup>
// 在模板中启用 v-focus; 以v开头的驼峰式命名的变量都可以被用作一个自定义指令
const vFocus = {
  mounted: (el) => el.focus()
}
</script>

<template>
  <input v-focus />
</template>
//全局注册
const app = createApp({})

// 使 v-focus 在所有组件中都可用
app.directive('focus', {
  /* ... */
})

生命周期 Mount

选项式代码则和Vue2相同,组合式代码和选项式相比,少了 beforeCreate 和 created

onBeforeMount() //在组件挂载之前调用
onMounted()     //在组件挂载完成后执行

onBeforeUpdate() //在组件更新之前执行
onUpdated()      //在组件更新完成后执行

onBeforeUnmount()//在实例卸载之前执行
onUnmounted()    //在实例卸载完成后执行

onActivated()   //缓存组件插入时触发
onDeactivated() //缓存组件移除时触发

onErrorCaptured() //子组件发生错误时执行

//(仅开发可用)
onRenderTracked() //当组件渲染过程中追踪到响应式依赖时调用
onRenderTriggered() //当组件渲染过程中追踪到响应式依赖时调用
//(仅SSR可用)
onServerPrefetch() //在组件实例在服务器上被渲染之前调用 

侦听器 watch

直接给 watch() 传入一个响应式对象,会隐式地创建一个深层侦听器

import { ref, watch,watchEffect } from 'vue'
const question = ref('')
// 监听question的变化
watch(question, async (new, old) => {
  console.log(new, old)
}
// 加入 immediate 会立即执行一遍
// ,{ immediate: true }
)


watchEffect(async () => {
  const response = await fetch(
    `https://xxx/todos/${todoId.value}`
  )
  data.value = await response.json()
})

依赖注入 provide

//祖父组件
import {ref,provide} from 'vue'

//提供监听值
const testValue = ref(1)
provide('testKey',testValue)

setTimeOut(()=>{
  testValue.value = 2
})
import {inject} from 'vue'

//监听值
inject('testKey',(value)=>{
  console.log(value)
})

组件注册

全局引用

main.js
import { createApp } from 'vue' const app = createApp({}) app.component( // 注册的名字 'MyComponent', // 组件的实现 { /* ... */ } )

而在setup中,引入则无需注册

<script setup>
import ComponentA from './ComponentA.vue'
</script>

<template>
  <ComponentA />
</template>

内置组件

  • Transition 内置动画,通过v-if进行设置
<Transition>
  <p v-if="show">hello</p>
</Transition>

<style>
.v-enter-active,
.v-leave-active {
  transition: opacity 0.5s ease;
}

.v-enter-from,
.v-leave-to {
  opacity: 0;
}
</style>
  • TransitionGroup 是一个内置组件,用于对 v-for 列表中的元素或组件的插入、移除和顺序改变添加动画效果。
<TransitionGroup name="list" tag="ul">
  <li v-for="item in items" :key="item">
    {{ item }}
  </li>
</TransitionGroup>

<style>
.list-enter-active,
.list-leave-active {
  transition: all 0.5s ease;
}
.list-enter-from,
.list-leave-to {
  opacity: 0;
  transform: translateX(30px);
}
</style>
  • KeepAlive 在多个组件间动态切换时缓存被移除的组件实例
<!-- 非活跃的组件将会被缓存! -->
<KeepAlive>
  <component :is="activeComponent" />
</KeepAlive>

组件传值

props

amp.vue
const props = defineProps(['foo']) console.log(props.foo) // 或者 defineProps({ title: String, likes: Number })

$emit

const emit = defineEmits({
  // 没有校验
  click: null,

  // 校验 submit 事件
  submit: ({ email, password }) => {
    if (email && password) {
      return true
    } else {
      console.warn('Invalid submit event payload!')
      return false
    }
  }
})

function submitForm(email, password) {
  emit('submit', { email, password })
}

问题

ref 和 reactive 的区别

reactive:(1)它的响应式是更加‘深层次’的,底层本质是将传入的数据包装成一个Proxy。(2)参数必须是对象或者数组,如果要让对象的某个元素实现响应式时比较麻烦。需要使用toRefs (3)更适合复杂对象

ref:(1)函数参数可以是基本数据类型,也可以接受对象类型。(2)如果参数是对象类型时,其实底层的本质还是reactive,系统会自动根据我们给ref传入的值转换成(3)更适合简单类型

Vue