【从0开始学前端】vue3简介、核心代码、生命周期

进行vue3学习跟随尚硅谷vue3

一.vue3简介

Vue是一套用于构建用户界面的渐进式 JavaScript 框架。它由尤雨溪创建,以其易学性、灵活性和高性能而广受欢迎。

"渐进式"意味着你可以:

  • 从一个轻量、核心的库开始,只用于增强静态 HTML。
  • 逐步应用到复杂的、大规模的单页应用。

优点:

  1. 更快的性能:重写了虚拟 DOM,优化了编译策略。
  2. 更小的体积:通过更好的 Tree-shaking(树摇优化),使打包后的文件更小。
  3. 更强的可维护性:采用 Monorepo 管理源码,结构更清晰。
  4. 更好的 TypeScript 支持:使用 TypeScript 重写,提供了完美的类型推断。
  5. 更先进的 API :引入了 Composition API,提供了更好的逻辑复用和组织方式。

二.创建vue3工程

在前端,我们一般使用vscode进行构建

创建vue3工程时,确保自己已经安装了node.js,最新版本可以在官网下载。安装完毕后在桌面打开终端按照如图进行创建最初的vue3工程。

之后桌面就会出现一个如图所示的文件夹

之后,将该工程使用vscode打开,系统自动提示安装vue插件,一键安装即可

进入vscode后,发现部分项目标红,打开终端输入npm i下载所有依赖,出现node_modules文件夹,这里面就是我们自动安装的所有依赖。重启后标红消失

此时可以安装插件Vue (Official)

文件解释

目录与其中文件介绍:

.vscode/extensions.json是所下载插件

public是页签图标

src是我们主要的工作成果/源代码文件

在这里原先的assets和components文件夹,App.vue需要重新创建,最新版本已经去除

ts 复制代码
//main.ts文件
//引入creatApp用于创建应用
import { createApp } from "vue";
//引入App根组件
import App from './App.vue'

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

文件介绍:

env.d.ts自动声明所有可能用到的的文件

index.html入口文件

两个package包的管理文件

三个tsconfig文件,管理js文件

vite.config.ts安装插件,配置代理

三.vue3核心语法

1.【OptionsAPI 与 CompositionAPI】

特性 Options API Composition API
编程范式 选项式、声明式。你告诉 Vue "你需要什么"(data, methods, computed)。 函数式、组合式。你像写普通 JavaScript 函数一样组织代码。
代码组织 按选项类型组织 。相同逻辑的代码被拆分到不同的选项中(如 data, methods, watch)。 按逻辑功能组织。所有属于同一功能的代码(响应式数据、方法、计算属性等)可以放在一起。
核心语法 data, methods, computed, watch, lifecycle hooks 等选项。 ref, reactive, computed, watch, onMounted 等函数。
逻辑复用 Mixins / 混入。容易发生命名冲突,来源不清晰,关系不明确。 自定义组合式函数。利用纯 JavaScript 函数,返回响应式数据和方法,清晰无冲突。
TypeScript 支持 支持较弱,类型推断有时会困难。 原生支持极好,能利用完整的 TS 类型推断。
学习曲线 较低,对初学者友好,结构固定,易于理解。 较高,需要理解响应式原理和函数式编程思想,但灵活性更强。
this 的使用 大量使用 this 来访问组件实例的属性和方法。 几乎不使用 this 。Composition API 的函数在 setup()<script setup> 的范围内直接调用。
适用场景 中小型项目,逻辑不复杂的组件,初学者团队。 大型项目,需要高复用性、可读性和 TypeScript 支持的重型组件。

vue2中使用的是OptionAPI,而vue3使用的是CompositionAPI

这里测试一个非响应式的demo

仅以该段为例,在vue2中,要将data,methodl分开进行书写,而vue3中可以直接进行初始化,之后的方法分区块书写,最后将所有数据进行提交

vue 复制代码
<template>
  <div class="person">
    <h2>姓名:{{name}}</h2>
    <h2>年龄:{{age}}</h2>
    <button @click="changeName">修改名字</button>
    <button @click="changeAge">修改年龄</button>
    <button @click="showTel">查看联系方式</button>
  </div>
</template>

<script lang="ts">
  export default {
    name:'Person',
    beforeCreate(){//生命周期
      console.log('beforeCreate')
    },
    setup(){
      console.log(this) //setup中的this是undefined,Vue3在弱化this了
      // 数据,原来是写在data中的,此时的name、age、tel都不是响应式的数据
      let name = '张三'
      let age = 18
      let tel = '13888888888'
      
      // 方法
      function changeName() {
        name = 'zhang-san' //注意:这样修改name,页面是没有变化的
        console.log(name) //name确实改了,但name不是响应式的
      }
      function changeAge() {
        age += 1 //注意:这样修改age,页面是没有变化的
        console.log(age) //age确实改了,但age不是响应式的
      }
      function showTel() {
        alert(tel)
      }

      // 将数据、方法交出去,模板中才可以使用
      return {name,age,tel,changeName,changeAge,showTel}
    }
  }
</script>

<style scoped>
  .person {
    background-color: skyblue;
    box-shadow: 0 0 10px;
    border-radius: 10px;
    padding: 20px;
  }
  button {
    margin: 0 5px;
  }
</style>

2.setup函数

setup 函数,它是 Composition API 的入口和核心

setup 是一个专门用于使用 Composition API 的组件选项。它在组件实例被创建之前 执行,在 beforeCreatecreated 生命周期钩子之前执行。它的主要目的是:

  • 定义响应式数据
  • 定义方法
  • 注册生命周期钩子
  • 返回所有需要在模板中使用的数据和方法

set up中不能用this

3.data,methods,setup能否同时存在于同一组件

data、methods 和 setup 可以同时存在于同一个组件中。Vue 3 设计上完全支持 Options API 和 Composition API 的混合使用。

当三者同时存在时,Vue 会按照以下规则处理:

  1. setup 函数最先执行
  2. data 和 methods 在组件实例创建时合并
  3. 命名冲突时,setup 返回的属性优先级最高,然后是data,methods
vue 复制代码
<template>
  <div>
    <h2>混合使用示例</h2>
    <p>来自 data: {{ message }}</p>
    <p>来自 setup: {{ count }}</p>
    <p>计算属性: {{ combinedMessage }}</p>
    <button @click="incrementFromSetup">Setup 方法 (+1)</button>
    <button @click="incrementFromMethods">Methods 方法 (+10)</button>
    <button @click="showInfo">显示所有数据</button>
  </div>
</template>

<script>
import { ref, computed } from 'vue'

export default {
  // 1. data 选项
  data() {
    return {
      message: 'Hello from data',
      countFromData: 100
    }
  },
  
  // 2. methods 选项
  methods: {
    incrementFromMethods() {
      this.countFromData += 10
      console.log('Methods count:', this.countFromData)
      console.log('Setup count:', this.count) // 可以访问 setup 返回的数据
    },
    
    showInfo() {
      console.log('=== 所有数据 ===')
      console.log('data.message:', this.message)
      console.log('data.countFromData:', this.countFromData)
      console.log('setup.count:', this.count)
      console.log('setup.doubleCount:', this.doubleCount)
    }
  },
  
  // 3. setup 函数
  setup() {
    // setup 中的响应式数据
    const count = ref(0)
    const doubleCount = computed(() => count.value * 2)
    
    // setup 中的方法
    const incrementFromSetup = () => {
      count.value++
      console.log('Setup count:', count.value)
    }
    
    // 返回给模板使用的数据和方法
    return {
      count,
      doubleCount,
      incrementFromSetup
    }
  },
  
  // 其他 Options API 也可以正常使用
  computed: {
    combinedMessage() {
      return `${this.message} - ${this.count}`
    }
  },
  
  mounted() {
    console.log('组件已挂载')
    console.log('可以在 mounted 中访问所有数据:', this.count, this.message)
  }
}
</script>

4.set up语法糖

<script setup> 是 Vue 3 中 Composition API 的语法糖,它让代码更加简洁、易读,是目前最推荐的写法。在这其中写的东西就相当于写的set up函数

这是普通setup函数

vue 复制代码
<script>
import { ref, computed } from 'vue'

export default {
  setup() {
    const count = ref(0)
    const double = computed(() => count.value * 2)
    
    const increment = () => {
      count.value++
    }
    
    return {
      count,
      double,
      increment
    }
  }
}
</script>

上述代码,还需要编写一个不写setupscript标签,去指定组件名字,比较麻烦,我们可以借助vite中的插件简化

  1. 第一步:npm i vite-plugin-vue-setup-extend -D
  2. 第二步:vite.config.ts在插件包中导入
  3. 第三步:<script setup lang="ts" name="Person">
vue 复制代码
//使用setup语法糖后
<script setup>
import { ref, computed } from 'vue'

const count = ref(0)
const double = computed(() => count.value * 2)

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

5.响应式数据

5.1ref定义基本类型

ref,这是 Composition API 中最核心的响应式 API 之一,也是 Vue 3 中用于创建响应式引用的函数。它可以包装任何基本类型的值,使其变成响应式的

可以定义基本类型,对象类型的响应式数据

想让谁是响应式,给谁外面包一个ref

语法: let xxx = ref(初始值)

**返回值:**一个RefImpl的实例对象,简称ref对象refref对象的value属性是响应式的

另外,JS中操作数据需要:xxx.value,但模板中不需要.value,直接使用即可。

vue 复制代码
<template>
  <div>
    <p>{{ count }}</p>
    <button @click="increment">+1</button>
  </div>
</template>

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

const count = ref(0)//这里count不是响应式,count.value才是响应式  

const increment = () => {
  // 注意:在 JavaScript 中需要通过 .value 访问
  count.value++
  
  console.log(count.value) // 1
  console.log(count) // RefImpl 对象
}
</script>

5.2reactive响应式对象

reactive 是 Vue 3 中用于创建响应式对象的函数,是 Composition API 中另一个核心的响应式 API。它接收一个普通 JavaScript 对象,并返回该对象的响应式代理。

只能定义对象类型的响应式数据

reactive只要包裹了一个对象,就变成了Proxy()响应式对象

语法: let 响应式对象= reactive(源对象)

**返回值:**一个Proxy的实例对象,简称:响应式对象。

注意点: reactive定义的响应式数据是"深层次"的。

js 复制代码
import { reactive } from 'vue'

// 创建响应式对象
const state = reactive({
  count: 0,
  message: 'Hello',
  user: {
    name: 'Alice',
    age: 25
  },
  items: ['apple', 'banana']
})

5.3ref 定义对象类型的响应式数据

ref接收的是对象类型,内部其实也是调用了reactive函数。

vue 复制代码
<template>
  <div class="person">
    <h2>汽车信息:一台{{ car.brand }}汽车,价值{{ car.price }}万</h2>
    <h2>游戏列表:</h2>
    <ul>
      <li v-for="g in games" :key="g.id">{{ g.name }}</li>
    </ul>
    <h2>测试:{{obj.a.b.c.d}}</h2>
    <button @click="changeCarPrice">修改汽车价格</button>
    <button @click="changeFirstGame">修改第一游戏</button>
    <button @click="test">测试</button>
  </div>
</template>

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

// 数据
let car = ref({ brand: '奔驰', price: 100 })
let games = ref([
  { id: 'ahsgdyfa01', name: '英雄联盟' },
  { id: 'ahsgdyfa02', name: '王者荣耀' },
  { id: 'ahsgdyfa03', name: '原神' }
])
let obj = ref({
  a:{
    b:{
      c:{
        d:666
      }
    }
  }
})

console.log(car)

function changeCarPrice() {
  car.value.price += 10
}
function changeFirstGame() {
  games.value[0].name = '超级鸡马'
}
function test(){
  obj.value.a.b.c.d = 999
}
</script>

5.4ref和reactive对比

特性 ref reactive
基本定义 创建响应式引用,可包装任何值 创建响应式对象,仅用于对象类型
数据类型 ✅ 任何类型:基本类型、对象、数组等 ❌ 仅对象类型(Object、Array、Map、Set)
创建方式 const count = ref(0) const state = reactive({ count: 0 })
访问方式 需要通过 .value 访问 直接访问属性
模板使用 自动解包,无需 .value 直接使用属性
重新赋值 ✅ 支持:ref.value = newValue ❌ 不支持直接替换整个对象
解构响应式 本身就是引用,解构后仍需要通过 .value 需要使用 toRefs 保持响应式
TypeScript 支持 ✅ 优秀,完整的类型推断 ⚠️ 一般,复杂嵌套类型推断可能有问题
深度响应式 ✅ 默认深度响应式 ✅ 默认深度响应式
性能 轻微开销(包装对象) 直接 Proxy,性能稍好
适用场景 基本类型、需要重新赋值的引用类型、模板 ref 复杂的嵌套对象、表单数据、应用状态

5.5toRefs 与 toRef

都是把一个响应式对象里的属性拿出来变成响应式

特性 toRef toRefs
作用对象 单个响应式对象的单个属性 整个响应式对象的所有属性
返回值 单个 ref 对象 包含所有属性 ref 的对象
使用场景 提取单个属性 解构整个响应式对象
js 复制代码
<template>
  <div class="person">
    <h2>姓名:{{person.name}}</h2>
    <h2>年龄:{{person.age}},{{nl}}</h2>
    <button @click="changeName">修改名字</button>
    <button @click="changeAge">修改年龄</button>
  </div>
</template>

<script lang="ts" setup name="Person">
  import {reactive,toRefs,toRef} from 'vue'

  // 数据
  let person = reactive({
    name:'张三',
    age:18
  })

  // 使用toRefs从person这个响应式对象中,解构出name、age,且name和age依然是响应式的
  // name和age的值是ref类型,其value值指向的是person.name和person.age
  let {name,age} = toRefs(person)//toRefs(person)包含name和age
  let nl = toRef(person,'age')//nl只包含person中的age
  
  console.log(nl.value)

  // 方法
  function changeName(){
    name.value += '~'
    console.log(name.value,person.name)
  }
  function changeAge(){
    age.value += 1
  }

</script>

<style scoped>
  .person {
    background-color: skyblue;
    box-shadow: 0 0 10px;
    border-radius: 10px;
    padding: 20px;
  }
  button {
    margin: 0 5px;
  }
  li {
    font-size: 20px;
  }
</style>

5.6计算属性comnputed

计算属性是基于响应式依赖进行缓存的派生值,只有当依赖发生变化时才会重新计算。

官网建议:计算属性的返回值应该被视为只读的,并且永远不应该被更改,应该更新它所依赖的源状态以触发新的计算

js 复制代码
<template>
  <div class="person">
    姓:<input type="text" v-model="firstName"> <br>
    名:<input type="text" v-model="lastName"> <br>
    全名:<span>{{fullName}}</span> <br>
    <button @click="changeFullName">全名改为:li-si</button>
  </div>
</template>

<script setup lang="ts" name="App">
  import {ref,computed} from 'vue'

  let firstName = ref('zhang')
  let lastName = ref('san')

  // 计算属性------只读取,不修改
  /* let fullName = computed(()=>{
    return firstName.value + '-' + lastName.value
  }) */


  // 计算属性------既读取又修改
  let fullName = computed({
    // 读取
    get(){
      return firstName.value + '-' + lastName.value
    },
    // 修改
    set(val){
      console.log('有人修改了fullName',val)
      firstName.value = val.split('-')[0]
      lastName.value = val.split('-')[1]
    }
  })

  function changeFullName(){
    fullName.value = 'li-si'
  } 
</script>

5.7watch监视

watch 用于监听响应式数据的变化,并在变化时执行副作用操作。

vue3只能监视以下四种数据:

  1. ref定义的数据。
  2. reactive定义的数据。
  3. 函数返回一个值(getter函数)。
  4. 一个包含上述内容的数组。
情况一监视ref基本类型数据
vue 复制代码
<template>
  <div class="person">
    <h1>情况一:监视【ref】定义的【基本类型】数据</h1>
    <h2>当前求和为:{{sum}}</h2>
    <button @click="changeSum">点我sum+1</button>
  </div>
</template>

<script lang="ts" setup name="Person">
  import {ref,watch} from 'vue'
  // 数据
  let sum = ref(0)
  // 方法
  function changeSum(){
    sum.value += 1
  }
  // 监视,情况一:监视【ref】定义的【基本类型】数据,注意这里sum不用加value
  const stopWatch = watch(sum,(newValue,oldValue)=>{
    console.log('sum变化了',newValue,oldValue)
    if(newValue >= 10){
      stopWatch()
    }
  })
</script>

<style scoped>
  .person {
    background-color: skyblue;
    box-shadow: 0 0 10px;
    border-radius: 10px;
    padding: 20px;
  }
  button {
    margin: 0 5px;
  }
  li {
    font-size: 20px;
  }
</style>
情况二监视ref对象类型数据
vue 复制代码
<template>
  <div class="person">
    <h1>情况二:监视【ref】定义的【对象类型】数据</h1>
    <h2>姓名:{{ person.name }}</h2>
    <h2>年龄:{{ person.age }}</h2>
    <button @click="changeName">修改名字</button>
    <button @click="changeAge">修改年龄</button>
    <button @click="changePerson">修改整个人</button>
  </div>
</template>

<script lang="ts" setup name="Person">
  import {ref,watch} from 'vue'
  // 数据
  let person = ref({
    name:'张三',
    age:18
  })
  // 方法
  function changeName(){
    person.value.name += '~'
  }
  function changeAge(){
    person.value.age += 1
  }
  function changePerson(){
    person.value = {name:'李四',age:90}
  }
  /* 
    监视,情况一:监视【ref】定义的【对象类型】数据,监视的是对象的地址值,若想监视对象内部属性的变化,需要手动开启深度监视
    watch的第一个参数是:被监视的数据
    watch的第二个参数是:监视的回调
    watch的第三个参数是:配置对象(deep、immediate等等.....) 
  */
  watch(person,(newValue,oldValue)=>{
    console.log('person变化了',newValue,oldValue)
  },{deep:true})
  //这里watch第一个对象是person,第二个对象是36行
  //newValue,oldValue有时是一样的,有时不一样
  //这里理解为,你有一个旧房子,现在又买了一个新房子,两个事物是不一样的,但是旧房子地址没有变,所以仍然存在
</script>

<style scoped>
  .person {
    background-color: skyblue;
    box-shadow: 0 0 10px;
    border-radius: 10px;
    padding: 20px;
  }
  button {
    margin: 0 5px;
  }
  li {
    font-size: 20px;
  }
</style>
情况三监视reactive对象

监视reactive定义的【对象类型】数据,且默认开启了深度监视

隐式创建深层监听,不能关闭

vue 复制代码
<template>
  <div class="person">
    <h1>情况三:监视【reactive】定义的【对象类型】数据</h1>
    <h2>姓名:{{ person.name }}</h2>
    <h2>年龄:{{ person.age }}</h2>
    <button @click="changeName">修改名字</button>
    <button @click="changeAge">修改年龄</button>
    <button @click="changePerson">修改整个人</button>
    <hr>
    <h2>测试:{{obj.a.b.c}}</h2>
    <button @click="test">修改obj.a.b.c</button>
  </div>
</template>

<script lang="ts" setup name="Person">
  import {reactive,watch} from 'vue'
  // 数据
  let person = reactive({
    name:'张三',
    age:18
  })
  let obj = reactive({
    a:{
      b:{
        c:666
      }
    }
  })
  // 方法
  function changeName(){
    person.name += '~'
  }
  function changeAge(){
    person.age += 1
  }
  function changePerson(){
    Object.assign(person,{name:'李四',age:80})//reactive对象不能整体修改,需要使用assign进行逐个修改
      //简单来说,ref就是重新买个一个房子,建了一个新的对象。而reactive就是把房子装修了,修改了其中值
  }
  function test(){
    obj.a.b.c = 888
  }

  // 监视,情况三:监视【reactive】定义的【对象类型】数据,且默认是开启深度监视的
  watch(person,(newValue,oldValue)=>{
    console.log('person变化了',newValue,oldValue)
  })
  watch(obj,(newValue,oldValue)=>{
    console.log('Obj变化了',newValue,oldValue)
  })
  
</script>

<style scoped>
  .person {
    background-color: skyblue;
    box-shadow: 0 0 10px;
    border-radius: 10px;
    padding: 20px;
  }
  button {
    margin: 0 5px;
  }
  li {
    font-size: 20px;
  }
</style>
情况四监视ref或reactive定义的【对象类型】数据中的某个属性

有以下两点注意:

  1. 若该属性值不是【对象类型】,需要写成函数形式。
  2. 若该属性值是依然是【对象类型】,可直接编,也可写成函数,建议写成函数。

监视的要是对象里的属性,那么最好写函数式,注意点:若是对象监视的是地址值,需要关注对象内部,需要手动开启深度监视。

vue 复制代码
<template>
  <div class="person">
    <h1>情况四:监视【ref】或【reactive】定义的【对象类型】数据中的某个属性</h1>
    <h2>姓名:{{ person.name }}</h2>
    <h2>年龄:{{ person.age }}</h2>
    <h2>汽车:{{ person.car.c1 }}、{{ person.car.c2 }}</h2>
    <button @click="changeName">修改名字</button>
    <button @click="changeAge">修改年龄</button>
    <button @click="changeC1">修改第一台车</button>
    <button @click="changeC2">修改第二台车</button>
    <button @click="changeCar">修改整个车</button>
  </div>
</template>

<script lang="ts" setup name="Person">
  import {reactive,watch} from 'vue'

  // 数据
  let person = reactive({
    name:'张三',
    age:18,
    car:{
      c1:'奔驰',
      c2:'宝马'
    }
  })
  // 方法
  function changeName(){
    person.name += '~'
  }
  function changeAge(){
    person.age += 1
  }
  function changeC1(){
    person.car.c1 = '奥迪'
  }
  function changeC2(){
    person.car.c2 = '大众'
  }
  function changeCar(){
    person.car = {c1:'雅迪',c2:'爱玛'}
  }

  // 监视,情况四:监视响应式对象中的某个属性,且该属性是基本类型的,要写成函数式
  /* watch(()=> person.name,(newValue,oldValue)=>{
    console.log('person.name变化了',newValue,oldValue)
  }) */

  // 监视,情况四:监视响应式对象中的某个属性,且该属性是对象类型的,可以直接写,也能写函数,更推荐写函数
  watch(()=>person.car,(newValue,oldValue)=>{
    console.log('person.car变化了',newValue,oldValue)
  },{deep:true})
//这里写成函数式更佳,监视地址
</script>

<style scoped>
  .person {
    background-color: skyblue;
    box-shadow: 0 0 10px;
    border-radius: 10px;
    padding: 20px;
  }
  button {
    margin: 0 5px;
  }
  li {
    font-size: 20px;
  }
</style>
情况五监视多个数据
vue 复制代码
<template>
  <div class="person">
    <h1>情况五:监视上述的多个数据</h1>
    <h2>姓名:{{ person.name }}</h2>
    <h2>年龄:{{ person.age }}</h2>
    <h2>汽车:{{ person.car.c1 }}、{{ person.car.c2 }}</h2>
    <button @click="changeName">修改名字</button>
    <button @click="changeAge">修改年龄</button>
    <button @click="changeC1">修改第一台车</button>
    <button @click="changeC2">修改第二台车</button>
    <button @click="changeCar">修改整个车</button>
  </div>
</template>

<script lang="ts" setup name="Person">
  import {reactive,watch} from 'vue'

  // 数据
  let person = reactive({
    name:'张三',
    age:18,
    car:{
      c1:'奔驰',
      c2:'宝马'
    }
  })
  // 方法
  function changeName(){
    person.name += '~'
  }
  function changeAge(){
    person.age += 1
  }
  function changeC1(){
    person.car.c1 = '奥迪'
  }
  function changeC2(){
    person.car.c2 = '大众'
  }
  function changeCar(){
    person.car = {c1:'雅迪',c2:'爱玛'}
  }

  // 监视,情况五:监视上述的多个数据
  watch([()=>person.name,person.car],(newValue,oldValue)=>{
    console.log('person.car变化了',newValue,oldValue)
  },{deep:true})

</script>

<style scoped>
  .person {
    background-color: skyblue;
    box-shadow: 0 0 10px;
    border-radius: 10px;
    padding: 20px;
  }
  button {
    margin: 0 5px;
  }
  li {
    font-size: 20px;
  }
</style>

5.8watchEffect

watchEffect 是一个自动依赖追踪的监听器,它会立即执行传入的函数,并自动追踪函数内部使用的所有响应式依赖。

watch监视谁输出谁;watchEffect自动分析监视谁,启动先运行

特性 watch watchEffect
依赖声明 显式声明监听源 自动追踪依赖
立即执行 需要 immediate: true 总是立即执行
参数获取 提供新旧值 不提供参数
使用复杂度 相对复杂 简单直接
适用场景 精确控制监听源 自动依赖追踪
vue 复制代码
<template>
  <div class="person">
    <h2>需求:当水温达到60度,或水位达到80cm时,给服务器发请求</h2>
    <h2>当前水温:{{temp}}℃</h2>
    <h2>当前水位:{{height}}cm</h2>
    <button @click="changeTemp">水温+10</button>
    <button @click="changeHeight">水位+10</button>
  </div>
</template>

<script lang="ts" setup name="Person">
  import {ref,watch,watchEffect} from 'vue'

  // 数据
  let temp = ref(10)
  let height = ref(0)

  // 方法
  function changeTemp(){
    temp.value += 10
  }
  function changeHeight(){
    height.value += 10
  }

  // 监视 -- watch实现
  /* watch([temp,height],(value)=>{
    // 从value中获取最新的水温(newTemp)、最新的水位(newHeight)
    let [newTemp,newHeight] = value
    // 逻辑
    if(newTemp >= 60 || newHeight >= 80){
      console.log('给服务器发请求')
    }
  }) */

  // 监视 -- watchEffect实现
  watchEffect(()=>{
    if(temp.value >= 60 || height.value >= 80){
      console.log('给服务器发请求')
    }
  })

</script>

<style scoped>
  .person {
    background-color: skyblue;
    box-shadow: 0 0 10px;
    border-radius: 10px;
    padding: 20px;
  }
  button {
    margin: 0 5px;
  }
  li {
    font-size: 20px;
  }
</style>

5.9标签的 ref 属性

作用:用于注册模板引用。

常用

  • 防止样式污染:组件样式不会影响其他组件
  • 避免命名冲突:相同类名在不同组件中互不干扰
  • 组件样式独立:每个组件拥有独立的 CSS 作用域
  • 用在普通DOM标签上,获取的是DOM节点。
  • 用在组件标签上,获取的是组件实例对象。
场景 ref 绑定对象 返回值类型
DOM 元素 <div ref="divRef"> HTMLElement
输入框 <input ref="inputRef"> HTMLInputElement
按钮 <button ref="buttonRef"> HTMLButtonElement
Vue 组件 <Child ref="childRef"> 组件实例
v-for 中的元素 <li v-for ref="listRef"> 元素数组

5.10props的使用

Props 是组件之间数据传递的一种方式,父组件通过 props 向子组件传递数据。

js 复制代码
// 定义一个接口,限制每个Person对象的格式
export interface PersonInter {
 id:string,
 name:string,
    age:number
   }
   
// 定义一个自定义类型Persons
export type Persons = Array<PersonInter>

父亲App.vue中代码:

vue 复制代码
<template>
	<Person :list="persons"/>
</template>
  
<script lang="ts" setup name="App">
  import Person from './components/Person.vue'
  import {reactive} from 'vue'
    import {type Persons} from './types'
  
    let persons = reactive<Persons>([
     {id:'e98219e12',name:'张三',age:18},
      {id:'e98219e13',name:'李四',age:19},
       {id:'e98219e14',name:'王五',age:20}
     ])
   </script>

儿子Person.vue中代码:

vue 复制代码
<template>
<div class="person">
 <ul>
     <li v-for="item in list" :key="item.id">
        {{item.name}}--{{item.age}}
      </li>
    </ul>
   </div>
   </template>
  
<script lang="ts" setup name="Person">
import {defineProps} from 'vue'
import {type PersonInter} from '@/types'
  
  // 第一种写法:仅接收
// const props = defineProps(['list'])
  
  // 第二种写法:接收+限制类型
// defineProps<{list:Persons}>()
  
  // 第三种写法:接收+限制类型+指定默认值+限制必要性
let props = withDefaults(defineProps<{list?:Persons}>(),{
     list:()=>[{id:'asdasg01',name:'小猪佩奇',age:18}]
  })
   console.log(props)
  </script>

6.生命周期(组件的一生)

在 Vue 3 中,生命周期钩子函数是组件在不同阶段执行的函数,让你可以在特定时机执行自定义逻辑。Vue 3 的生命周期与 Vue 2 类似,但有一些变化和组合式 API 的引入。

Vue3中,生命周期分八个阶段,即对应8个与created类似的钩子函数。

  1. setup(组合式 API)
    • 触发时机:在组件实例被创建之初、任何选项式 API 之前执行
    • 主要作用:组合式 API 的入口,定义响应式数据、计算属性、方法等
    • 注意事项:没有对应的选项式 API 钩子
  2. beforeCreate
    • 触发时机:组件实例初始化之后,数据观测和事件/侦听器配置之前
    • 主要作用:执行与响应式数据无关的初始化逻辑
    • 数据状态:数据和方法还未初始化
  3. created
    • 触发时机:组件实例创建完成之后
    • 主要作用:发起异步请求、执行不依赖 DOM 的初始化操作
    • 数据状态:可以访问数据和方法,但尚未挂载到 DOM
  4. beforeMount
    • 触发时机:挂载开始之前,模板编译完成后
    • 主要作用:在组件首次渲染到 DOM 之前执行
    • DOM 状态:this.$el 还不存在
  5. mounted
    • 触发时机:组件挂载到 DOM 之后
    • 主要作用:操作 DOM、初始化第三方库
    • DOM 状态:可以访问到渲染后的 DOM
  6. beforeUnmount(Vue 3)/ beforeDestroy(Vue 2)
    • 触发时机:组件卸载之前
    • 主要作用:清理定时器、取消网络请求、移除事件监听
    • 组件状态:组件实例仍然完全可用
  7. updated
    • 触发时机:数据更改导致虚拟 DOM 重新渲染和打补丁之后
    • 主要作用:执行依赖于 DOM 更新的操作
    • 注意事项:避免在此钩子中修改状态,防止无限循环
  8. unmounted(Vue 3)/ destroyed(Vue 2)
    • 触发时机:组件卸载并销毁之后
    • 主要作用:执行最后的清理工作
    • 组件状态:所有指令和事件监听器已被移除,子组件已被销毁
vue 复制代码
<template>
  <div class="person">
    <h2>当前求和为:{{ sum }}</h2>
    <button @click="changeSum">点我sum+1</button>
  </div>
</template>

<!-- vue3写法 -->
<script lang="ts" setup name="Person">
  import { 
    ref, 
    onBeforeMount, 
    onMounted, 
    onBeforeUpdate, 
    onUpdated, 
    onBeforeUnmount, 
    onUnmounted 
  } from 'vue'

  // 数据
  let sum = ref(0)
  // 方法
  function changeSum() {
    sum.value += 1
  }
  console.log('setup')
  // 生命周期钩子
  onBeforeMount(()=>{
    console.log('挂载之前')
  })
  onMounted(()=>{
    console.log('挂载完毕')
  })
  onBeforeUpdate(()=>{
    console.log('更新之前')
  })
  onUpdated(()=>{
    console.log('更新完毕')
  })
  onBeforeUnmount(()=>{
    console.log('卸载之前')
  })
  onUnmounted(()=>{
    console.log('卸载完毕')
  })
</script>

7.自定义hook

自定义 Hook 是一个可重用的函数,用于封装和复用 Vue 组件的逻辑。它基于 Vue 3 的组合式 API,让开发者能够将相关的逻辑代码组织在一起。

示例代码:

  • useSum.ts中内容如下:

    js 复制代码
    import {ref,onMounted} from 'vue'
    
    export default function(){
      let sum = ref(0)
    
      const increment = ()=>{
        sum.value += 1
      }
      const decrement = ()=>{
        sum.value -= 1
      }
      onMounted(()=>{
        increment()
      })
    
      //向外部暴露数据
      return {sum,increment,decrement}
    }		
  • useDog.ts中内容如下:

    js 复制代码
    import {reactive,onMounted} from 'vue'
    import axios,{AxiosError} from 'axios'
    
    export default function(){
      let dogList = reactive<string[]>([])
    
      // 方法
      async function getDog(){
        try {
          // 发请求
          let {data} = await axios.get('https://dog.ceo/api/breed/pembroke/images/random')
          // 维护数据
          dogList.push(data.message)
        } catch (error) {
          // 处理错误
          const err = <AxiosError>error
          console.log(err.message)
        }
      }
    
      // 挂载钩子
      onMounted(()=>{
        getDog()
      })
    	
      //向外部暴露数据
      return {dogList,getDog}
    }
  • 组件中具体使用:

    vue 复制代码
    <template>
      <h2>当前求和为:{{sum}}</h2>
      <button @click="increment">点我+1</button>
      <button @click="decrement">点我-1</button>
      <hr>
      <img v-for="(u,index) in dogList.urlList" :key="index" :src="(u as string)"> 
      <span v-show="dogList.isLoading">加载中......</span><br>
      <button @click="getDog">再来一只狗</button>
    </template>
    
    <script lang="ts">
      import {defineComponent} from 'vue'
    
      export default defineComponent({
        name:'App',
      })
    </script>
    
    <script setup lang="ts">
      import useSum from './hooks/useSum'
      import useDog from './hooks/useDog'
    	
      let {sum,increment,decrement} = useSum()
      let {dogList,getDog} = useDog()
    </script>
相关推荐
simon_93491 小时前
受够了压缩和收费?我作为一个码农,手撸了一款无限容量、原图直出的瀑布流相册!
前端
e***87701 小时前
windows配置永久路由
android·前端·后端
Dorcas_FE2 小时前
【tips】动态el-form-item中校验的注意点
前端·javascript·vue.js
小小前端要继续努力2 小时前
前端新人怎么更快的融入工作
前端
四岁爱上了她2 小时前
input输入框焦点的获取和隐藏div,一个自定义的下拉选择
前端·javascript·vue.js
fouryears_234173 小时前
现代 Android 后台应用读取剪贴板最佳实践
android·前端·flutter·dart
boolean的主人3 小时前
mac电脑安装nvm
前端
用户1972959188913 小时前
WKWebView的重定向(objective_c)
前端·ios
烟袅3 小时前
5 分钟把 Coze 智能体嵌入网页:原生 JS + Vite 极简方案
前端·javascript·llm