都2023了还不会Vue3?一篇笔记带你快速入门

Vue 3 简介

Vue 3 是一款用于构建用户界面的 JavaScript 框架。它基于标准 HTML、CSS 和 JavaScript 构建,并提供了一套声明式的、组件化的编程模型,帮助你高效地开发用户界面。无论是简单还是复杂的界面,Vue 都可以胜任。

快速上手

创建一个 Vue 3 项目

在 vue3 中推荐使用 pnpm,如果没有安装pnpm,请先用npm全局安装:

npm install -g pnpm

首先,我们需要通过Vite创建一个新的Vue 3项目。你可以通过下面的命令安装Vite:

pnpm create vite
# 如果没有 pnpm 可以使用 npm 安装 vite 也可以
# npm create vite@latest

然后按照提示操作即可!

你还可以通过附加的命令行选项直接指定项目名称和你想要使用的模板。例如,要构建一个 Vite + Vue 项目,运行:

# npm 7+
npm create vite@latest my-vue-app -- --template vue

# yarn
yarn create vite my-vue-app --template vue

# pnpm
pnpm create vite my-vue-app --template vue

# bun
bunx create-vite my-vue-app --template vue

应用和组件编写

在 Vue3 中,我们可以使用新的组合式API创建和管理组件。

下面是一个简单的Vue3组件示例:

<template>
  <div>
    <p>{{ count }}</p>
    <button @click="increment">Increment</button>
  </div>
</template>

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

const count = ref(0)
const increment = () => { count.value++ }
</script>

我们首先导入了 ref 函数,然后在 setup 函数中使用 ref 创建了一个响应式值 count。我们还定义了一个 increment 函数,用于增加 count 的值。

基础

响应式基础

Vue 3提供了两个主要的函数来创建响应式数据:refreactive

响应式原理

都2023了还不会Vue3?一篇笔记带你快速入门_第1张图片Vue3 通过 Proxy 对数据实现 getter/setter 代理,从而实现响应式数据,提供了更好的性能和更多的功能。

ref

在组合式 API 中,推荐使用 ref() 函数来声明响应式状态:

import { ref } from 'vue'

const count = ref(0)

ref() 接收参数,并将其包裹在一个带有 .value 属性的 ref 对象中返回:

const count = ref(0)

console.log(count) // { value: 0 }
console.log(count.value) // 0

count.value++
console.log(count.value) // 1
reactive

还有另一种声明响应式状态的方式,即使用 reactive() API。与将内部值包装在特殊对象中的 ref 不同,reactive() 将使对象本身具有响应性:

import { reactive } from 'vue'

const state = reactive({ count: 0 })

在模板中使用:

<button @click="state.count++">
  {{ state.count }}
button>

响应式对象是 JavaScript 代理,其行为就和普通对象一样。不同的是,Vue 能够拦截对响应式对象所有属性的访问和修改,以便进行依赖追踪和触发更新。

reactive() 将深层地转换对象:当访问嵌套对象时,它们也会被 reactive() 包装。当 ref 的值是一个对象时,ref() 也会在内部调用它。

在实际开发中推荐使用 ref

ref和reactive的区别

refreactive 的主要区别在于,ref 是为了让基本类型(如数字和字符串)可以变为响应式,而 reactive 是为了让对象变为响应式。

ref 创建的响应式数据需要通过 .value 属性进行访问和修改,而 reactive 创建的响应式对象可以直接访问和修改其属性。因此,ref 更适合于处理基本类型,而 reactive 更适合于处理对象。

组合式 API

都2023了还不会Vue3?一篇笔记带你快速入门_第2张图片组合式API是Vue 3的重要新特性,它提供了一种更灵活、更逻辑化的方式来组织和复用代码。

setup 函数

setup() 钩子是在组件中使用组合式 API 的入口,在组件实例被创建和初始化之后,但在渲染发生之前被调用。

<script>
import { ref } from 'vue'

export default {
  setup() {
    const count = ref(0)

    // 返回值会暴露给模板和其他的选项式 API 钩子
    return {
      count
    }
  },

  mounted() {
    console.log(this.count) // 0
  }
}
script>

<template>
  <button @click="count++">{{ count }}button>
template>

setup语法糖

<script setup>
import { ref, onMounted } from 'vue'
  
const count = ref(0)
onMounted(() => {
   console.log(this.count) // 0
})
script>

<template>
  <button @click="count++">{{ count }}button>
template>
computed 和 watch

你可以使用 computedwatch 来创建计算属性和监视响应式数据的变化。

下面会有详细讲解

<script setup>
import { ref, computed, watch } from 'vue'
const count = ref(0)
const doubled = computed(() => count.value * 2)

watch(count, (newValue, oldValue) => {
	console.log(`count changed from ${oldValue} to ${newValue}`)
})
</script>
生命周期钩子

都2023了还不会Vue3?一篇笔记带你快速入门_第3张图片

  • onMounted() ==> 在组件挂载完成后执行。
  • onUpdated() ==> 在组件因为响应式状态变更而更新其 DOM 树之后调用。
  • onUnmounted() ==> 在组件实例被卸载之后调用。
  • onBeforeMount() ==> 在组件被挂载之前被调用。
  • onBeforeUpdate() ==> 在组件即将因为响应式状态变更而更新其 DOM 树之前调用。
  • onBeforeUnmount() ==> 在组件实例被卸载之前调用。
<script setup>
import { onMounted, onUpdated, onUnmounted } from 'vue'

onMounted(() => {
	console.log('component mounted')
})

onUpdated(() => {
	console.log('component updated')
})

onUnmounted(() => {
	console.log('component unmounted')
})
</script>
自定义 hooks

你可以创建自定义的hooks来复用代码。一个自定义hook就是一个函数,它可以使用其他的响应式数据和组合式API。

定义useCounter.js

import { ref, onMounted } from 'vue'

export function useCounter() {
  const count = ref(0)

  const increment = () => {
    count.value++
  }

  onMounted(() => {
    console.log('counter mounted')
  })

  return {
    count,
    increment
  }
}

使用useCounter()

<script setup>
import { useCounter } from "./useCounter"
const counter = useCounter()
</script>

进阶

shallowReactive

reactive() 的浅层作用形式。

在某些情况下,你可能想要创建一个浅层的响应式对象,这样其内部的属性不会被转化为响应式的。

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

在vue3中使用 Echarts,创建 Echarts 实例的时候就可以通过 shallowReactive 函数来创建

readonly

接受一个对象 (不论是响应式还是普通的) 或是一个 ref,返回一个原值的只读代理。

你可以使用 readonly 函数来创建一个只读的响应式对象。任何试图修改只读对象的操作都将在开发环境下引发一个错误。

import { readonly } from 'vue'

const original = reactive({ count: 0 })

const copy = readonly(original)

watchEffect(() => {
  // 用来做响应性追踪
  console.log(copy.count)
})

// 更改源属性会触发其依赖的侦听器
original.count++

// 更改该只读副本将会失败,并会得到一个警告
copy.count++ // warning!

toRefs

将一个响应式对象转换为一个普通对象,这个普通对象的每个属性都是指向源对象相应属性的 ref。每个单独的 ref 都是使用 toRef() 创建的。

const state = reactive({
  foo: 1,
  bar: 2
})

const stateAsRefs = toRefs(state)
/*
stateAsRefs 的类型:{
  foo: Ref,
  bar: Ref
}
*/

// 这个 ref 和源属性已经“链接上了”
state.foo++
console.log(stateAsRefs.foo.value) // 2

stateAsRefs.foo.value++
console.log(state.foo) // 3

当从组合式函数中返回响应式对象时,toRefs 相当有用。使用它,消费者组件可以解构/展开返回的对象而不会失去响应性:

function useFeatureX() {
  const state = reactive({
    foo: 1,
    bar: 2
  })

  // ...基于状态的操作逻辑

  // 在返回时都转为 ref
  return toRefs(state)
}

// 可以解构而不会失去响应性
const { foo, bar } = useFeatureX()

toRefs 在调用时只会为源对象上可以枚举的属性创建 ref。如果要为可能还不存在的属性创建 ref,请改用 toRef

内置组件

Teleport

是一个内置组件,Teleport 组件允许你将子组件渲染到DOM的任何位置,而不仅仅是它的父组件中。




  

Suspense

是一个内置组件,用来在组件树中协调对异步依赖的处理。它让我们可以在组件树上层等待下层的多个嵌套异步依赖项解析完成,并可以在等待时渲染一个加载状态。


  
  

Transition

是一个内置组件,这意味着它在任意别的组件中都可以被使用,无需注册。它可以将进入和离开动画应用到通过默认插槽传递给它的元素或组件上。进入或离开可以由以下的条件之一触发:

  • v-if 所触发的切换
  • v-show 所触发的切换
  • 由特殊元素 切换的动态组件
  • 改变特殊的 key 属性

以下是最基本用法的示例:

<button @click="show = !show">Togglebutton>
<Transition>
  <p v-if="show">hellop>
Transition>
/* 下面我们会解释这些 class 是做什么的 */
.v-enter-active,
.v-leave-active {
  transition: opacity 0.5s ease;
}

.v-enter-from,
.v-leave-to {
  opacity: 0;
}

其它组合API

computed

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

创建一个只读的计算属性 ref:

<script setup>
const count = ref(1)
const plusOne = computed(() => count.value + 1)

console.log(plusOne.value) // 2

plusOne.value++ // 错误
</script>

创建一个可写的计算属性 ref:

<script setup>
const count = ref(1)
const plusOne = computed({
  get: () => count.value + 1,
  set: (val) => {
    count.value = val - 1
  }
})

plusOne.value = 1
console.log(count.value) // 0
</script>

watchEffect

立即运行一个函数,同时响应式地追踪其依赖,并在依赖更改时重新执行。

import { ref, watchEffect } from 'vue'

const count = ref(0)

watchEffect(() => console.log(count.value))
// -> 输出 0

count.value++
// -> 输出 1

在这个示例中,我们使用 watch 函数观察 count,并在其值改变时打印消息。我们还使用 watchEffect 函数创建了一个副作用,它会在 count 改变时立即执行。

shallowReactive 和 shallowRef

shallowReactiveshallowRef 允许我们创建一个浅层的响应式对象。对于 shallowReactive,只有对象的第一层属性会变为响应式的,对象的更深层次的属性不会被转换。shallowRefref 的浅层版本,它不会自动解包内部的值。

import { shallowReactive, shallowRef } from 'vue'

const obj = shallowReactive({ a: { b: 1 } })
obj.a.b // 这不是一个响应式的值

const num = shallowRef(1)
num.value // 你需要使用 .value 才能访问到值

readonly 与 shallowReadonly

readonlyshallowReadonly 允许我们创建一个只读的响应式对象。对于 readonly,对象的所有属性(包括嵌套属性)都会变为只读。shallowReadonlyreadonly 的浅层版本,只有对象的第一层属性会变为只读。

import { readonly, shallowReadonly } from 'vue'

const obj = readonly({ a: { b: 1 } })
obj.a = 2 // 这会抛出一个错误

const shallowObj = shallowReadonly({ a: { b: 1 } })
shallowObj.a.b = 2 // 这不会抛出错误

provide 和 inject

都2023了还不会Vue3?一篇笔记带你快速入门_第4张图片

provideinject 是 Vue 3 的依赖注入 API。 一个父组件相对于其所有的后代组件,会作为依赖提供者。任何后代的组件树,无论层级有多深,都可以注入由父组件提供给整条链路的依赖。

import { provide, inject } from 'vue'

// 在父组件中
provide('myValue', 123)

// 在子组件中
const myValue = inject('myValue') // myValue 现在是 123

toRaw 和 markRaw

toRawmarkRaw 允许我们逃避 Vue 的响应式系统。toRaw 可以返回一个对象的原始版本,而 markRaw 可以防止一个对象被转换为响应式的。

import { reactive, toRaw, markRaw } from 'vue'

const obj = reactive({ a: 1 })
const rawObj = toRaw(obj) // rawObj 是 obj 的原始版本

const nonReactiveObj = markRaw({ a: 1 }) // nonReactiveObj 不会被转换为响应式的

customRef

customRef 允许我们创建一个自定义的 ref,我们可以控制它何时触发依赖追踪和更新。

import { customRef } from 'vue'

const myRef = customRef((track, trigger) => ({
  get() {
    track()
    return someValue
  },
  set(newValue) {
    someValue = newValue
    trigger()
  }
}))

isReactive

检查一个对象是否是由 reactive()shallowReactive() 创建的代理。

isRef

检查某个值是否为 ref。

import { reactive, ref, isReactive, isRef } from 'vue'

const obj = reactive({ a: 1 })
isReactive(obj) // true

const num = ref(1)
isRef(num) // true

因为 isRef 返回值是一个类型判定 (type predicate),这意味着 isRef 可以被用作类型守卫:

let foo: unknown
if (isRef(foo)) {
  // foo 的类型被收窄为了 Ref
  foo.value
}

深入响应式系统

Vue 3 的响应式系统构建在一个名为 effect 的函数基础之上,它被用来收集依赖项(依赖追踪)和触发副作用。当一个响应式对象的属性被访问时,effect 将其收集为依赖项;当一个响应式对象的属性被修改时,它将触发关联的副作用。

effect、reactive、ref

reactiveref 是 Vue 3 的两种基本响应式 API。它们都使用 effect 来跟踪依赖项和触发更新。

import { effect, reactive, ref } from 'vue'

// 使用 reactive
const state = reactive({ a: 1 })

effect(() => {
  console.log(state.a)
})

state.a = 2 // 2

// 使用 ref
const count = ref(0)

effect(() => {
  console.log(count.value)
})

count.value++ // 1

在这个示例中,我们创建了一个响应式对象和一个 ref,然后使用 effect 创建了两个副作用,它们分别打印出对象和 ref 的值。当这些值被改变时,副作用就会被触发。

track、trigger

tracktrigger 是 Vue 3 的底层 API,它们分别被用来收集依赖项和触发更新。

import { reactive, effect, track, trigger } from 'vue'

const state = reactive({ a: 1 })

effect(() => {
  // 手动进行依赖追踪
  track(state, 'a')
  console.log(state.a)
})

state.a = 2 // 打印 "2"

// 手动触发更新
trigger(state, 'a')

在这个示例中,我们使用 track 手动收集了 state.a 作为依赖项,然后使用 trigger 手动触发了更新。

嵌套结构处理

Vue 3 的响应式系统可以处理嵌套的响应式对象。

import { reactive, effect } from 'vue'

const state = reactive({
  user: {
    name: 'Alice'
  }
})

effect(() => {
  console.log(state.user.name)
})

state.user.name = 'Bob' // 打印 "Bob"

在这个示例中,我们创建了一个嵌套的响应式对象,并使用 effect 创建了一个副作用,它打印出用户的名字。当用户的名字被改变时,副作用就会被触发。

深入编译优化

Vue 3在编译优化上做出了很大的提升。在编译过程中,Vue 3对模板进行静态分析,提取出不会改变的部分,预编译为纯JavaScript,这大大提高了运行时的渲染效率。下面详细介绍一下这些优化。

静态节点提升

在 Vue 3 中,如果你的模板中有不会改变的部分,例如:

<template>
  <div>
    <h1>Hello, world!h1>
    <p>Welcome to Vue 3p>
  div>
template>

在编译时,“Hello, world!” 和 “Welcome to Vue 3” 这些静态节点将会被提升,避免了每次渲染时都需要重新创建。

片段、模板并用

在 Vue 3 中,你可以在一个组件模板中有多个根节点,这就是片段:

<template>
  <header>Headerheader>
  <main>Main contentmain>
  <footer>Footerfooter>
template>

在这个示例中,我们有三个根节点,这在 Vue 2 中是不允许的。但在 Vue 3 中,这是完全正常的。

动态编译

Vue 3 的动态编译可以让我们在运行时编译模板字符串,例如,我们可以动态创建一个Hello组件

import { compile, h } from 'vue'

const template = `

{{ greeting }} World!

`
const render = compile(template) export default { data() { return { greeting: 'Hello', } }, render }

在这个例子中,我们在运行时编译了模板字符串,并将结果设置为组件的渲染函数。这种技术在需要动态生成模板的场景中非常有用,比如在一个 CMS 中渲染用户提供的模板。

深入组件化

Vue 3在组件化方面也有很多进步,下面详细介绍一下。

动态组件

Vue 3支持使用component标签来动态加载组件,组件可以是一个组件选项对象,也可以是一个组件的名字。这个特性在根据不同的状态显示不同组件时非常有用。


被传给 :is 的值可以是以下几种:

  • 被注册的组件名
  • 导入的组件对象

异步组件

Vue 3 支持异步组件,你可以使用defineAsyncComponent方法来定义一个异步组件,该方法接收一个返回 Promise 的工厂函数,Promise 需要解析为一个组件。

import { defineAsyncComponent } from 'vue'

const AsyncComp = defineAsyncComponent(() =>
  import('./components/MyComponent.vue')
)

高阶组件

高阶组件(Higher-Order Component,简称 HOC)是一种设计模式,它是接收一个组件并返回一个新组件的函数。在 Vue 3 中,你可以使用组合式 API 来更容易地创建高阶组件。

import { ref } from 'vue'

export default function withHover(Component) {
  return {
    setup(props, { slots }) {
      const isHovered = ref(false)

      return () => (
        <div
          onMouseenter={() => (isHovered.value = true)}
          onMouseleave={() => (isHovered.value = false)}
        >
          <Component {...props} isHovered={isHovered.value} v-slots={slots} />
        </div>
      )
    }
  }
}

在这个例子中,withHover 函数接收一个组件,并返回一个新的组件,新的组件有一个 isHovered 属性,表示鼠标是否悬停在组件上。这种模式可以帮助我们在不同的组件间复用逻辑。

Render 函数

都2023了还不会Vue3?一篇笔记带你快速入门_第5张图片在 Vue 3 中,Render 函数是一种提供了更大灵活性的高级功能。虽然 Vue 的模板系统已经足够强大,但在某些情况下,直接使用 JavaScript 编写渲染逻辑会更加方便。

Render 函数的工作原理是通过返回一个虚拟节点(VNode)来告诉 Vue 如何渲染界面。Vue 3 提供了 h 函数用于创建 VNode。

import { h } from 'vue'

export default {
  render() {
    return h('div', {}, 'Hello, world!')
  }
}

在这个例子中,我们使用 h 函数创建了一个 div 元素,然后在 Render 函数中返回它。

h()详细信息

第一个参数既可以是一个字符串 (用于原生元素) 也可以是一个 Vue 组件定义。第二个参数是要传递的 prop,第三个参数是子节点。

当创建一个组件的 vnode 时,子节点必须以插槽函数进行传递。如果组件只有默认槽,可以使用单个插槽函数进行传递。否则,必须以插槽函数的对象形式来传递。

为了方便阅读,当子节点不是插槽对象时,可以省略 prop 参数。

编译优化

Vue 3 的编译器在编译时做了许多优化,例如静态节点提升和动态节点绑定,从而在运行时减少了不必要的工作。静态节点提升可以将不会改变的节点从渲染函数中提取出来,从而避免在每次渲染时都重新创建它们。动态节点绑定则是对那些可能改变的节点进行优化,只有当这些节点的绑定值发生变化时,才会重新渲染节点。

手动编写渲染逻辑

有时,我们可能需要手动编写渲染逻辑。比如,当我们需要根据一组数据动态生成一个列表时,我们可以在 Render 函数中使用 JavaScript 的数组方法。

import { h } from 'vue'

export default {
  data() {
    return {
      items: ['Apple', 'Banana', 'Cherry']
    }
  },
  render() {
    return h('div', {}, this.items.map(item => h('div', {}, item)))
  }
}

在这个例子中,我们使用 map 方法动态生成了一个列表的元素,然后在 Render 函数中返回它。

Render 函数提供了一种强大的方式来控制 Vue 应用的渲染过程,使得我们能够更好地控制和优化应用的性能。

vue3生态

状态管理 - Pinia

Pinia | The intuitive store for Vue.js

Pinia 类似于 Vuex,但在 API 设计上更加简洁且易用。

Pinia 的主要优点包括:

  1. 它有更简洁的 API,减少了模板代码的数量。
  2. 它通过 TypeScript 提供了更好的类型支持。
  3. 它提供了基于组件的状态存储,只在需要时加载状态。

路由管理 - Vue Router

Vue Router | Vue.js 的官方路由

Vue Router 是 Vue.js 的路由库,你可以使用它构建单页面应用程序,如下面的例子所示:

首先,创建一个 router:

import { createRouter, createWebHistory } from 'vue-router'
import Home from './components/Home.vue'
import About from './components/About.vue'

const router = createRouter({
  history: createWebHistory(),
  routes: [
    { path: '/', component: Home },
    { path: '/about', component: About }
  ]
})

然后,在 Vue 应用中使用这个 router:

import { createApp } from 'vue'
import App from './App.vue'
import router from './router'

createApp(App).use(router).mount('#app')

最后,在组件中使用 来导航和渲染路由:


UI 框架 - Element Plus

一个 Vue 3 UI 框架 | Element Plus

Element Plus 是一个为 Vue.js 开发的 UI 框架,它提供了一套丰富多样的组件,可以帮助我们更容易地构建出漂亮的界面:


测试方案 - Vitest

Vitest | 下一代测试框架

Vitest 是一个原生支持 Vite 的测试框架。非常快速!

import { assert, describe, it } from 'vitest'

describe.skip('skipped suite', () => {
  it('test', () => {
    // 已跳过此测试套件,无错误
    assert.equal(Math.sqrt(4), 3)
  })
})

describe('suite', () => {
  it.skip('skipped test', () => {
    // 已跳过此测试,无错误
    assert.equal(Math.sqrt(4), 3)
  })
})

其他改变

在 Vue 3 中,开发者会注意到一些重要的变化,主要体现在全局 API 的转移等,以及对 TypeScript 的更好支持上。

Fragment

在Vue 3中,你可以在一个组件的模板中有多个根节点,这被称为 Fragment


全局 API 转移

在 Vue 3 中,一些全局 API 已经被转移到了 globalProperties 上,例如 Vue.prototype 在 Vue 3 中变成了 app.config.globalProperties。这样做是为了更好地隔离全局 API,并为未来可能的更改提供更大的灵活性。

例如,原本在 Vue 2 中我们可能会这样添加一个全局的方法:

Vue.prototype.$myGlobalMethod = function () {
  return 'Hello World!'
}

在 Vue 3 中,我们需要这样做:

const app = createApp(App)
app.config.globalProperties.$myGlobalMethod = function () {
  return 'Hello World!'
}

然后我们就可以在任何组件中使用这个方法:

this.$myGlobalMethod()

删除的 API

Vue 3 为了简化框架并避免未来的维护负担,删除了一些在 Vue 2 中已经被废弃的 API,例如 Vue.setVue.deleteVue.observable

TypeScript 支持

Vue 3 从一开始就在内部使用了 TypeScript 重写,因此在 TypeScript 的支持上有了显著的提升。这包括更好的类型推断、自动补全,以及更强大的类型安全性。

例如,在 Vue 2 中,我们可能需要使用 Vue.extend() 或者 @Component 装饰器来确保 TypeScript 类型正确,但在 Vue 3 中,我们可以直接使用 defineComponent 方法,它能正确地推断出组件的类型:

import { defineComponent } from 'vue'

export default defineComponent({
  // type inference enabled
})

你可能感兴趣的:(vue3,笔记,arcgis,vue.js,vue,elementui,前端)