Vue3核心语法

Vue3核心语法

setup() 函数

基本概念

setup() 是 Vue 3 Composition API 的入口点,在组件创建之前执行。

代码示例

<script lang="ts">
export default {
  name: 'Person',
  setup() {
    console.log(this) // undefined,Vue3弱化this
    
    let name = '张三'
    let age = 18
    let tel = '13888888888'
    
    function changeName() {
      name = 'zhang-san' // 修改了,但页面不会更新
    }
    
    return { name, age, tel, changeName }
  }
}
</script>

关键点

  • setup() 中的 thisundefined
  • 必须通过 return 将数据和方法暴露给模板
  • 普通变量不是响应式的,修改后页面不会更新

setup 语法糖 + defineOptions

setup 语法糖基本概念

更简洁的 Composition API 写法,自动暴露顶层变量和函数。

<script lang="ts" setup>
defineOptions({
  name: 'Person123',
})

let name = '张三'
let age = 18
let tel = '13888888888'

function changName() {
  name = 'zhangsan' // 非响应式,页面不更新
}
</script>

defineOptions 详细介绍

功能:

  • <script setup> 中定义组件选项
  • Vue 3.3+ 新增的宏函数
  • 解决无法在 <script setup> 中直接设置 name 等选项的问题

支持的选项:

defineOptions({
  name: 'ComponentName',      // 组件名
  inheritAttrs: false,        // 是否继承属性
  components: { ... },        // 注册组件
  directives: { ... },        // 注册指令
  // 和其他组件选项
})

优势

  • 代码更简洁
  • 自动暴露变量和函数
  • 不需要写 return 语句
  • 可以在 <script setup> 中定义组件名等选项

ref() 函数

基本概念

ref() 用于创建响应式数据,可以包装基本类型和对象类型。

代码示例

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

defineOptions({
  name: 'Person',
})

// 使用 ref() 创建响应式数据
let name = ref('张三')        // 字符串 - 响应式
let age = ref(18)            // 数字 - 响应式  
let tel = '13888888888'      // 普通字符串 - 非响应式

// 方法
function changeName() {
  name.value = 'zhang-san'   // JS中操作ref对象时候需要.value
  console.log(name.value) 
}

function changeAge() {
  age.value += 1             // JS中操作ref对象时候需要.value
  console.log(age.value)
}

function showTel() {
  alert(tel)                 // 普通变量直接使用
}
</script>

ref() 特性:

  • 基本类型:ref(0)ref('字符串')ref(false)
  • 对象类型:ref({ name: '张三' })
  • 访问:JS 中需要 .value,模板中自动解包

reactive() 函数

基本概念

reactive() 用于创建响应式对象,当对象属性变化时,视图会自动更新。

代码示例

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

const person = reactive({
  name: '张三',
  age: 18
})

function changeName() {
  person.name = '李四' // 直接修改属性
}
</script>

ref() vs reactive() 对比

特性ref()reactive()
数据类型基本类型 + 对象类型只接受对象类型
访问方式JS中需要 .value,模板中自动解包直接访问属性
模板使用{{ name }}{{ obj.property }}
重新赋值name.value = 新值不能直接重新赋值整个对象

使用原则:

  • 若需要一个基本类型的响应式数据,必须使用ref
  • 若需要一个响应式对象,层级不深,refreactive都可以
  • 若需要一个响应式对象,且层级较深,推荐使用reactive

toRefs 与 toRef

  • 作用:将响应式对象的属性转换为 ref 对象
  • 区别
    • toRefs:批量转换对象的所有属性
    • toRef:转换对象的单个属性
<script setup>
import { reactive, toRefs, toRef } from 'vue'

let person = reactive({ name: '张三', age: 18, gender: '男' })

// 批量转换
let { name, gender } = toRefs(person)
// 单个转换
let age = toRef(person, 'age')

// 使用时需要 .value
function changeName() {
  name.value += '~'
}
</script>

computed 计算属性

  • 作用:基于响应式数据计算新值
<script setup>
import { ref, computed } from 'vue'

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

// 只读计算属性
let fullName = computed(() => firstName.value + '-' + lastName.value)

// 可读可写计算属性
let fullName = computed({
  get() {
    return firstName.value + '-' + lastName.value
  },
  set(val) {
    [firstName.value, lastName.value] = val.split('-')
  }
})

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

watch 监视

情况一:监视 ref 定义的【基本类型】数据

直接写数据名即可,监视的是其 value 值的改变。

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

let sum = ref(0)

// 监视基本类型
const stopWatch = watch(sum, (newValue, oldValue) => {
  console.log('sum变化了', newValue, oldValue)
  if(newValue >= 10) {
    stopWatch() // 停止监视
  }
})

function changeSum() {
  sum.value += 1
}
</script>

情况二:监视 ref 定义的【对象类型】数据

直接写数据名,监视的是对象的【地址值】,若想监视对象内部的数据,要手动开启深度监视。

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

let person = ref({
  name: '张三',
  age: 18
})

// 监视对象类型,需要手动开启深度监视
watch(person, (newValue, oldValue) => {
  console.log('person变化了', newValue, oldValue)
}, { deep: true })

function changeName() {
  person.value.name += '~'  // newValue和oldValue都是新值
}

function changePerson() {
  person.value = { name: '李四', age: 90 }  // newValue是新值,oldValue是旧值
}
</script>

情况三:监视 reactive 定义的【对象类型】数据

默认开启了深度监视,且无法关闭。

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

let person = reactive({
  name: '张三',
  age: 18,
  car: {
    brand: 'BMW'
  }
})

// reactive对象默认深度监视
watch(person, (newValue, oldValue) => {
  console.log('person变化了', newValue, oldValue)
})

function changeCarBrand() {
  person.car.brand = 'Audi'  // 深层变化也会被监视到
}
</script>

情况四:监视 ref 或 reactive 定义的【对象类型】数据中的某个属性

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

let person = reactive({
  name: '张三',
  age: 18,
  car: {
    c1: '奔驰',
    c2: '宝马'
  }
})

// 情况四:监视基本类型属性(必须写成函数形式)
watch(() => person.name, (newValue, oldValue) => {
  console.log('name变化了', newValue, oldValue)
})

// 情况四:监视对象类型属性(推荐写成函数形式)
watch(() => person.car, (newValue, oldValue) => {
  console.log('car变化了', newValue, oldValue)
}, { deep: true })
</script>

情况五:监视多个数据

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

let person = reactive({
  name: '张三',
  age: 18,
  car: {
    c1: '奔驰',
    c2: '宝马'
  }
})

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

watchEffect

  • 特点:自动追踪函数内用到的响应式数据
  • 与watch区别:不需要明确指定监视的数据
<script setup>
import { ref, watchEffect } from 'vue'

let temp = ref(0)
let height = ref(0)

const stopWatch = watchEffect(() => {
  // 自动追踪 temp 和 height
  if (temp.value >= 50 || height.value >= 20) {
    console.log('联系服务器')
  }
  
  // 可停止监视
  if (temp.value === 100) {
    stopWatch()
  }
})
</script>

ref 属性

  • 作用:获取DOM元素或组件实例
<template>
  <!-- 获取DOM元素 -->
  <h1 ref="title">标题</h1>
  <button @click="showLog">打印</button>
  
  <!-- 获取组件实例 -->
  <Person ref="personComp"/>
</template>

<script setup>
import { ref } from 'vue'
import Person from './Person.vue'

let title = ref()
let personComp = ref()

function showLog() {
  console.log(title.value) // DOM元素
  console.log(personComp.value) // 组件实例
}
</script>

<!-- 子组件要使用 defineExpose 暴露内容 -->
<script setup>
import { ref, defineExpose } from 'vue'

let name = ref('张三')
let age = ref(18)

// 暴露给父组件
defineExpose({ name, age })
</script>

props 父传子

定义类型

// types.ts
export interface PersonInter {
  id: string
  name: string
  age: number
}
export type Persons = PersonInter[]

使用 props

<!-- 父组件 -->
<template>
  <Person :list="persons"/>
</template>

<script setup>
import { reactive } from 'vue'
import Person from './Person.vue'
import { type Persons } from './types'

let persons = reactive<Persons>([
  { id: '1', name: '张三', age: 18 }
])
</script>

<!-- 子组件 -->
<script setup>
import { defineProps, withDefaults } from 'vue'
import { type Persons } from './types'

// 接收props + 类型限制 + 默认值
let props = withDefaults(defineProps<{
  list?: Persons
}>(), {
  list: () => [{ id: '1', name: '默认', age: 0 }]
})
</script>

生命周期

  • 概念:Vue组件实例在创建时要经历一系列的初始化步骤

  • 四个阶段:创建、挂载、更新、卸载

  • Vue2的生命周期

    创建阶段:beforeCreatecreated

    挂载阶段:beforeMountmounted

    更新阶段:beforeUpdateupdated

    销毁阶段:beforeDestroydestroyed

  • Vue3的生命周期

    创建阶段:setup

    挂载阶段:onBeforeMountonMounted

    更新阶段:onBeforeUpdateonUpdated

    卸载阶段:onBeforeUnmountonUnmounted

  • 示例代码:

<template>
  <div class="person">
    <h2>当前求和为:{{ sum }}</h2>
    <button @click="changeSum">点我sum+1</button>
  </div>
</template>

<script setup>
import { 
  ref, 
  onBeforeMount, 
  onMounted, 
  onBeforeUpdate, 
  onUpdated, 
  onBeforeUnmount, 
  onUnmounted 
} from 'vue'

let sum = ref(0)

function changeSum() {
  sum.value += 1
}

// 生命周期钩子
onBeforeMount(() => {
  console.log('挂载之前')
})

onMounted(() => {
  console.log('挂载完毕') // 常用:发送请求、操作DOM
})

onBeforeUpdate(() => {
  console.log('更新之前')
})

onUpdated(() => {
  console.log('更新完毕') // 常用:数据更新后操作
})

onBeforeUnmount(() => {
  console.log('卸载之前') // 常用:清理定时器、取消订阅
})

onUnmounted(() => {
  console.log('卸载完毕')
})
</script>

常用钩子onMountedonUpdatedonBeforeUnmount

自定义hook

  • 本质:一个函数,封装 Composition API
  • 优势:复用代码,让setup逻辑更清晰
  • useSum.ts中内容如下:
// hooks/useSum.ts
import { ref, onMounted } from 'vue'

export default function() {
  let sum = ref(0)

  const increment = () => {
    sum.value += 1
  }
  
  const decrement = () => {
    sum.value -= 1
  }
  
  onMounted(() => {
    increment() // 挂载时自动+1
  })

  return { sum, increment, decrement }
}
  • useDog.ts中内容如下:
// hooks/useDog.ts
import { reactive, onMounted } from 'vue'
import axios from 'axios'

export default function() {
  let dogList = reactive({
    urlList: [] as string[],
    isLoading: false
  })

  async function getDog() {
    dogList.isLoading = true
    try {
      const { data } = await axios.get('https://dog.ceo/api/breed/pembroke/images/random')
      dogList.urlList.push(data.message)
    } catch (error) {
      console.log('请求失败', error)
    } finally {
      dogList.isLoading = false
    }
  }

  onMounted(() => {
    getDog() // 挂载时自动获取
  })

  return { dogList, getDog }
}
  • 组件中具体使用:
<template>
  <h2>当前求和为:{{ sum }}</h2>
  <button @click="increment">+1</button>
  <button @click="decrement">-1</button>
  
  <hr>
  
  <img v-for="(url, index) in dogList.urlList" :key="index" :src="url">
  <span v-show="dogList.isLoading">加载中...</span>
  <button @click="getDog">再来一只狗</button>
</template>

<script setup>
import useSum from './hooks/useSum'
import useDog from './hooks/useDog'

const { sum, increment, decrement } = useSum()
const { dogList, getDog } = useDog()
</script>

优势:逻辑复用、代码清晰、便于维护

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值