当前位置: 首页 > article >正文

【vue3】入门基础知识点

Vue3核心语法

组合式API【vue3】与选项式API【vue2】

setup

  • setup和data、methods同级别, 可与data等共存,data里面可以读取使用setup中声明的变量,而setup不能使用data中声明的变量(setup加载时间早于beforeCreated
  • setup中的this都是undefined
  • 直接声明变量,其变量是非响应式
  • setup中需要写返回值,将声明的东西抛出去(返回值可以是对象,也可以是渲染函数)
    非响应式写法
setup(){
  let name = 'zs';
  Let age = 18;
  function changeName(){
    name = '李四';
  }
  return {name,changeName}
}

setup的语法糖

  • script标签里加上setup,即可不用写return
  • vue3中一个vue文件可以有多个script标签,但是只有一个标签里有setup属性
  • 在setup中引入子组件时,不需要注册,即可使用
<script setup lang="ts">
  let a = 'zhang-san'
</script>
如何声明组件名字?
  • 与vue2类似,需要多写一组script, 且不能加setup
  • 使用defineOptions定义name*【defineOptions({name:‘xx’})】
<script lang="ts">
  export default {
    name: "Person"
  }
</script>

响应式数据ref,reactive

基本类型 - ref

  • 经过ref方法声明的变量是响应式的,此时变量是一个带有value属性的对象,在template模块中可直接使用对象,若想在方法中修改变量值,需要xx.value去修改。
<template>{{name}}</template>
<script setup>
  import {ref} from 'vue'
  let a= ref('张三')  // a是响应式的
  changeName = >{
     name.value = '李四'
  }
</script>

对象类型-ref/reactive

  • reactive: 只能定义对象类型,声明的对象是深层次
<script setup>
// ref 写法
let car = ref({brand:'x',price:10})
changePrice = > {
  car.value.price +=10
}
changeCarInfo = > {
  car.value = {brand:'cc',price:20} // 此方法可以直接修改原car对象
  car = ref({brand:'cc',price:20})  // 此时已为car重新分配了一个对象,因此原对象并不会改变
}
----------------------------------------------------------------------------------

// reactive写法
import {ref,reactive} from vue
let car = reactive({brand:'Aa',  price:20})
changePrice => {
   car.price +=10
}
changeCarInfo = > {
 Object.assign(car,{brand:'cc',price:10}) // 采用object.assign()方法修改原car对象
  car = reactive({brand:'cc',price:10}) // 此时已为car重新分配了一个新对象,因此原对象并不会改变
}
<script>

ref与reactive的区别

  • ref创建的变量必须使用.value去修改(可以使用Volar插件去自动添加.value)
  • ref声明对象类型,实际上底层也是用了reactive
  • reactive重新分配一个新对象,会失去响应式【修改reactive定义的变量:若为reactive创建的变量重新分配一个新对象,此时变量已指向新对象,并不会被修改(可以使用Object.assign去整体替换)

toRef与toRefs

  • toRefs:使对象中的每个属性变成响应式【toRefs(对象)】
  • toRef: 指定对象中的属性变成响应式【toRef(对象,‘属性’)】
  • ⚠️:使用toRefs/toRef声明的响应式变量,实际和原变量指向同一个对象!!
<script>
let person = reactive({name:'xx',age:18})

let {name,age} = person
 console.log(name,age) // 此时的name和age是非响应式的

let {name,age} = toRefs(preson)
console.log(name.value) // 此时的name和age是响应式

let age1 = toRef(preson,'age') // age1是响应式
changeAge = > {
  age1.value + = 1
}
console.log(age1.value,person.age) // 此时person.age的值也发生了改变

⚠️:person.name/person.age是一个响应式的对象,使用toRefs/toRef声明的响应式变量,实际和person.name/age指向同一个对象!!
</script>

计算属性computed

  • 计算属性:也是一个ref所定义的一个响应式数据,可通过set修改值
  • 有缓存,只会依据所计算的东西改变而变化
<script>
import {computed} from 'vue'
let a= ref(1)
let b= ref(2)

// 利用计算属性做操作
let sum = computed(()=>{
  return a.value + b.value
})

let sum1 = computed({
  get(){} // 常用
  set(val){} // 可修改计算属性的值,不常用
})
</script>

监听watch

第一个参数是监听器的【监视的数据源】,第二个参数是一个【监视的回调函数】,第三个参数一个【属性对象

  • 这个【】:分四类,ref定义的数据,reactive定义的数据,一个函数返回一个值(getter函数),由这几种类型的值组成的数组

情况一

监视【ref】定义的【基本类型】数据

  • 直接写数据名,监视的是其value值的改变
import {ref,watch} from 'vue'
let sum = ref(1)
const stopWatch = watch(sum,(newVal,oldVal)=>{
  console.log('sum变化',newVal,oldVal)
  // 停止监听?
  if(newVal > 10){
    stopWtach() // 调一遍自己 
  }
})

情况二

监视【ref】定义的【对象类型】数据

  • 直接写数据名,监视的是对象的【地址值】,若想监视对象内部的数据,要手动开启深度监视(deep:true)
    • 若修改的是ref定义的对象中的属性,newValue和oldValue都是新值,因为指向同一个对象
    • 若修改整个ref定义的对象,newValue是新值,oldValue是旧值,因为指向的不是同一个对象了
import {ref,watch} from 'vue'

let person = ref({name:'zx',age:18})
changeName(()=>{
  person.value.name = 'ls'
})
changePerson(()=>{
  person.value = {name:'ls',age:24} // 改变整个person对象
})

// 监视person对象,若想监听name属性改变,设置第三个参数:{deep:true}
watch(person,(newVal,oldVal)=>{
  // 进行一些操作
  console.log('监视~',newVal,oldVal)
},{deep:true})

情况三

监听【reactive】定义的【对象类型】数据

  • 默认开启深度监视,且无法关闭(即对象内部属性变化,也会监听)

情况四

监听ref或reactive定义的【对象类型】数据中的【某个属性】值(源:getter函数)

  • 需要写成函数形式,若属性是对象,则监视的是地址值,需要关注对象内部,需要手动开启深度监视
import {ref, watch } from 'vue'
let person = ref({
  name:'az',
  age:18,
  car:{
    a:1,
    b:2
  }
})

function changeName(){
  person.value.name = 'ls'
}
fucntion changeCar(){
  person.value.car.a = 3
}
function changeAllCar(){
  // 这个是对象类型car属性,指向地址变化了
  person.value.car = {a:4,b:5}
}

// 监视person的基本类型属性
watch(()=>{person.name},(newVal,oldVal)=>{
  console.log('只监听name改变')
})
// 监视person的对象类型属性car
watch(()=>{person.car},(newVal,oldVal)=>{
  console.log('只能监听到整个对象属性car的变化')
})
// 监视person的car中的属性
watch(()=>{person.car},(newVal,oldVal)=>{
  console.log('能监听到整个car的变化,也能监听到car中的属性变化')
},{deep:true})

情况五

监视多个数据

  • 数组形式
watch([()=>person.name,person.car],(newVal,oldVal)=>{
  console.log('监视person对象的name属性,car属性')
},{deep:true})

监听watchEffect

  • 立即运行一个函数,同时响应式的追踪其依赖,全自动监视
  • 不用明确指出监视的数据(用到啥,就监视啥
import { ref,watchEffect } from 'vue'
let temp = ref(0);
let height = ref(10);

// 监视变化,无需明确指出监视的数据
watchEffect(()=>{
 if(temp.value > 20){
   consoe.log('温度达到了');
 }
})

标签的ref属性

  • 作用:用ref属性去标记内容
    • html标签上的ref:获取的是DOM节点
    • 组件标签上的ref:获取的是组件实例对象

⚠️:父组件无法直接拿到子组件内的数据,需要子组件通过defineExpose方法去输出

<h2 ref = 'title' >html元素</h2>
<child ref = 'cd'/>
import {ref,defineExpose} from 'vue'

# Hmtl元素上的ref
let title = ref(); // 用于存储ref标记的内容
console.log(title.value) // 打印:h2标签元素

# 组件元素上的ref
let cd = ref();
console.log(cd.value); // 打印:child子组件,但获取不到任何child子组件的内容

# 若child子组件内
defineExpose({xx,xx1,xx2}) // 父组件能拿到此抛出的值

生命周期

  • 和Vue2一样,也分四个阶段:创建、挂载、更新、卸载
  • 创建:setup内
  • 挂载:onBeforeMount、onMounted
  • 更新:onBeforeUpdate、onUpdated
  • 卸载:onBeforeUnmount、onUnmounted

⚠️:子组件的生命周期比父组件的生命周期执行更早!!

import { onMounted } from "vue";
// 例子
onMounted(()=>{
  console.log('页面挂载完毕')
})

hooks与mixin

  • 作用:类似vue2中的混合 - mixin,封装一个对象的东西
  • 可以调用生命周期、计算属性等
import useDog from '@/hooks/useDog'  // 封装的有关于"狗"的东西

# "狗"对象的属性,方法
const {dogList, getDog} = useDog

路由(createRouter)

  • 存放在route文件夹中
  • 环境搭建:npm i vue-route

如何使用

# 第一步:主页面注册
import router from './router.js'
app.use(router)
----------------------------------------------------------------

# 第二步:route文件夹中
import { createRouter, crateWebHistory } from 'vue-router'
// 引入即将使用的组件
import Home from '../components/Home.vue'
import Detail from '../components/detail.vue'

// 创建路由器实例
const router = createRouter({
  history:crateWebHistory(), // 路由器的工作模式(history)
  routes:[
    {
      name:'home', // 路由名字
      path:'/home'component:Home,
      children:[
        {
          path:'deatil', // 嵌套子路由
          component:Detail
        }
      ]
    }
  ]
})
// 暴露
export default router
-----------------------------------------------------------------

# 第三步:页面使用
import {RouterView,RouterLink} from 'vue-router'
// 路由跳转
<RouterLink to="/home" active-class="类型"></RouterLink>
<RouterLink to="/home/detail" active-class="类型"></RouterLink>
// 路由展示区
<RouterView></RouterView>

注意点

  • 通过点击导航,视觉效果“消失”的路由组件,默认是被“卸载”掉的,需要的时候再去“挂载”

to两种写法

<RouterLink to="/home"></RouterLink>

// 对象写法
<RouterLink :to="{name:'home'}"></RouterLink>
<RouterLink :to="{path:'/home'}"></RouterLink>

⚠️编程式路由导航

  • 作用:可用于符合某些场景时,才能跳转的情况
  • router.push/router.replace方法里写法和RouterLink的to属性一致
import { router } from 'vu-route'  // 拿到路由器
// 例如点击时跳转
function showDetail(news){
  # 可回退
  router.push({
    name:'xiang'params:{
      id:news.id
    }
  })
  
  # 不可回退
  router.replace({
    name:'xiang'params:{
      id:news.id
    }
  })
}

传参方式

  • query
# 传递
// 第一种
<RouterLink :to="`/news/detail?id=${item.id}&${item.name}`"></RouterLink>
// 第二种
<RouterLink
  :to="{
    path:"/news/detail",
    query:{
      id:item.id,
      name:item.name
    }
  }"
></RouterLink>

# 接收
<div>{{router.query.id}}</div>
<div>{{query.id}}</div>

import {useRoute} from 'vue-router'
import {toRefs} from 'vue'
let route = useRoute()
let query = toRefs(route) // 将对象中所有属性变成响应式
  • params
    • 需要提前在路由处配置占位符(/:xx)
    • 使用to的对象写法,必须使用name配置项,不能用path
    • 使用?表示,参数非必传
# 路由配置
path: 'detail/:id/:name?' // 占位, name非必传

# 传递
// 第一种
<RouterLink :to="`/news/detail/${item.id}/${item.name}`"></RouterLink>
// 第二种
<RouterLink
  :to="{
    name: 'xiang'// 只能有name
    params:{
      id:item.id,
      name:item.name
    }
  }"
>
</RouterLink>


# 接收
<div>{{parmas.name}}</div>


import {useRoute} from 'vue-route'
import {toRefs} from 'vue'
let route = useRoute()
let params = toRefs(route)

路由的props配置

  • 布尔值写法:将路由收到的所有params参数,作为props传给路由组件
  • 函数写法:自己决定将什么作为props传给路由组件,可以获取参数
  • 对象写法:自己决定将什么作为props传给路由组件,参数写死的【少用,不重要】
route:[
  {
    name:'xiang',
    path:'detail',
    component:Detail,
    path:true // 第一种
    path:(route){  // 第二种
      return route.query | route.params
    }
    path:{  // 第三种
      a:xx,b:xx
    }
  }
]

路由重定向

  • 作用:给予默认值,重新定于路由 (redirect)
routes:[{
  path:'/',
  redirect:'/home'
}]

路由器工作模式

  • history模式

URL更美观,不带有# ,更接近传统网站,但后期项目上线,需要服务器配合处理路径问题

import {createWebHistory} from 'vue-router'
const router = createRouter({
  history:createWebHistory()
})
  • hash模式

兼容性更好,因为不需要服务端优化处理,但URL带有 “#” 不美观,且在SEO优化方面相对较差

import {createWebHashHistory} from 'vue-router'
const router = createRouter({
  history:createWebHashHistory()
})

路由跳转历史记录模式

  • push模式: 默认此方式,追加历史记录,浏览器可回退
  • replace模式:替换当前记录,不可回退
# 开启replace模式
<RouterLink replace .....> xxxx <RouterLink>

Pinia(defineStore)

  • 定义:集中式状态管理,每个组件都可以读取、写入它。即vue2中的vuex
  • 搭建环境: npm i pinia
  • 主要存放在store文件夹

如何使用

# 第一步:主页面mian中引入
import {createPinia} from 'pinia'
const pinia = createPinia()
app.use(pinia)
--------------------------------------------------------------------

# 第二步:store文件中
import {defineStore} from 'pinia'
exprot const useCountStore = defineStore('count',{
 // 真正存储数据的地方
  state(){
    return {
      sum:6,
      str:'xxxxx'
    }
  }
})
--------------------------------------------------------------------

# 第三步:页面使用
import {useCountStore} from '@/store/Count'
// 拿取数据,是一个reactive对象
const countState = useCountStore()
let count1 = countState.sum || countState.$state.sum

⚠️修改数据的方法

  • 直接修改
  • 批量修改$patch
  • 调用state中actions里定义的方法(里面的this指向当前store)
import {useCountStore} from '@/store/Count'
const countState = useCountStore()
# 直接修改
countState.sum +=1

# 批量修改$patch
countState.$patch({
  sum: 999,
  str:'aaa'
})

# 调用state中actions里定义的方法
exprot const useCountStore = defineStore('count',{
  actions:{
    increment(value){
      console.log('====',value) // 传递的参数
      // this指向当前的store
      this.sum + = value
    }
  },
  
 // 真正存储数据的地方
  state(){
    return {
      sum:6,
      str:'xxxxx'
    }
  }
})

countState.increment(3)

storeToRefs

  • 作用:只关注store里的数据,并可将其变成响应式
import {useCountStore} from '@/store/Count'
import {storeToRefs} from 'pinia'
const countState = useCountStore()
const {sum} = storeToRefs(countState)

getters

  • 定义:类似计算属性,用于处理state中的数据
  • 默认携带参数,参数指向当前store
exprot const useCountStore = defineStore('count',{
 // 真正存储数据的地方
  state(){
    return {
      sum:6,
      str:'xxxxx'
    }
  }
  
  // 计算
  getters:{
    bigSum(state){
      return state.sum * 10
    }
    upperSchool():string{
      return this.str.toUpperCase()
    }
  }
})

$subscribe

  • 定义:类似watch,用于监听state中数据的变化
  • 主要关注state参数,指向当前store
import {useCountStore} from '@/store/Count'
const countState = useCountStore()
countState.$subscribe((mutate,state)){
  // state指向当前store
  console.log(state)
}

组件通信

props传参

  • props是使用频率最高的一种通信方式,常用于:父 <-> 子
    • 父传子:属性值是非函数
    • 子传父:属性值是函数
  • defineProps方法:接受父组件传递的值(宏观方法,不引用也能用)
  • withDefaults方法:赋予默认值
    在这里插入图片描述
    在这里插入图片描述

自定义事件emit

  • 常用于子给父通信
  • defineEmits方法:定义发射事件
    在这里插入图片描述

mitt

  • 第三方包,事件订阅,用于任意组件间通信
  • 拥有四个内置函数:on、emit、off、all
npm i mitt

# utils/emitter文件中
import mitt from 'mitt'
const emitter = mitt()
export default emitter

在这里插入图片描述

$attrs

  • 常用于祖孙通信
  • 它是一个对象,包含所有父组件传入的标签属性,会自动排除props中声明的属性
# 祖组件
<Child :a="a" :b="b" v-bind="{x:100,y:200}" :update="update"></Child>
import {ref} from 'vue'
let a = ref(1)
function update(value){
  a.value+=value
}
--------------------------------------------------------------------

# 父组件
<GrandChild v-bind="$attrs"></GrandChild>
// 若父组件声明了a,则$attars对象中不包含a属性
definePoprs(['a'])
--------------------------------------------------------------------

# 孙组件
<h3>a:{{a}}</h3>
<button @click="update(6)"></button> // 孙给组传递
defineProps(['a','b','update'])

$refs、parent

  • refs: 父传子 (也就是父可以通过$refs拿到子组件实例)
  • parent:子传父 (子可以通过$parent拿到父组件实例

⚠️ 无论父子组件,其属性在被对方获取前,需要通过defineExpose抛出

# 父组件
<button @click="getAllChild($refs)">获取所有的子组件实例对象</button>
<Child1 ref="c1"></Child1> // 实例名字
<Child2 ref="c2"></Child2>
import {ref} from 'vue'
let c1 = ref()
let c2 = ref()
let house = ref(4)

function getAllChild(refs){
  console.log(refs) // 两个子组件实例对象
}
defineExpose({house}) // 抛出属性,允许子组件获取
--------------------------------------------------------------------

# 子组件
<button @click="getParent($parent)"></button>
import {ref} from 'vue'
let book = ref(4)

function getParent(parent){
  console.log(parent) // 父组件的实例对象
  parent.house -=1
}
defineExpose({book}) // 抛出属性,允许子组件获取

provide、inject

  • 用于祖孙传递
  • ref定义的变量,直接传递,不要传递x.value,会失去响应式

在这里插入图片描述

v-model

  • 底层:属性和事件组合
  • 可以更换value,可以在组件标签上多次使用v-model
# 父组件
<AtguiguInput v-model="userName" /> 
// v-model实际底层如下
<AtguiguInput :modelValue = "userName" @update:modelValue="userName = $event" />

# 子组件AtguiguInput
<input :value="modelValue" @input="emit('update:modelValue',xx)">
defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
------------------------------------------------------------------

# 将value更换成abc名称/xyz名称
<AtguiguInput v-model:abc="userName" v-model:xyz="passWord" /> 

// 子组件
<input :value="abc" @input="emit('update:abc',xx)">
defineProps(['abc'])
const emit = defineEmits(['update:abc'])

插槽slot

默认插槽

  • 无名,仅占位置。<slot>
  • 使用
    在这里插入图片描述

具名插槽

  • 给插槽命名,插入的内容需指定对应插槽名。<slot name="xxx></slot>
  • v-slot: xx只能用在组件标签/template标签上,语法糖:#xx

在这里插入图片描述

作用域插槽

  • 使用场景:数据在子那边,但根据数据生成的结构,由于父组件决定
  • 可以给插槽传递值,v-slot="xx" 只能用在组件标签/template标签上。
    在这里插入图片描述

其他API

shallowRef与shallowReactive

  • 作用&区别:通过使用shallowRef()和shallowRactive()来绕开深度响应,浅层式API创建的状态只在其顶层是响应式的,对所有深度的对象不会做任何处理,避免了对每一个内部属性做响应式所带来的性能成本,这使得属性的访问变得更快,可提高性能。
  • 使用
import {shallowRef, shallowReactive} from 'vue'
let myVal = shallowRef(0)
let myObj = shallowReactive({
  name:'xx', // 这是顶层,是响应式的
  age:18// 这是顶层,是响应式的
  car:{brand:"奔驰",money:18} // 无法对此进行更改,其为深层次
})

readonly与shallowReadonly

readonly

  • 作用:用于创建一个对象的深只读副本
  • 特点:
    • 对象的所有嵌套属性都将变成只读
    • 任何尝试修改这个对象的操作都会被阻止
  • 应用场景
    • 创建不可变的状态快照
    • 保护全局状态或配置不可被修改

shallowReadonly

  • 作用:只将对象的顶层属性变成可读
  • 特点
    • 只将对象的顶层属性设置为只读,对象内部嵌套的属性任然可变
    • 适用于只需要保护对象顶层属性的场景
import {ref,reactive, readonly,shallowReadonly} from 'vue'
let a = ref(0)
let a1 = readonly(a) // 仅可传入一个响应式数据

let b = reactive({
  name:'xx', // b1中只读
  car:{ // b1中可被修改
    brand:'xx',
    price:100
  }
})
let b1 = shallowReadonly(b)

roRaw与markRaw

在这里插入图片描述
在这里插入图片描述

customRef

  • 作用:创建一个自定义的ref,并对其依赖项跟踪和更新触发进行逻辑控制(类似消息的发布与订阅
  • 结构:customRef内部是一个函数,有track和trigger两个参数,需返回**get()和set()**方法
  • 重点:track()和trigger()参数
    • track: 监听数据,持续关注,发生变化就去更新
    • trigger: 在数据更新时,通知track

在这里插入图片描述

Teleport

  • 定义:是一种能够将我们的组件html结构移动到指定位置的技术
  • to中可以写入元素标签/类/id等
  • 使用:将如下html结构移动到body的位置。
<teleport to="body">
  <div class="modal" v-show="isShow">
    <h2>我是一个弹窗</h2>
  </div>
</teleport>

Suspense

  • https://cn.vuejs.org/guide/built-ins/suspense.html#suspense

全局API转移到应用对象

  • vue2中通过vue.xx的东西全都转移到app.xx上
  • https://v3-migration.vuejs.org/zh/breaking-changes/global-api.html

面试点:非兼容改变章节

  • https://v3-migration.vuejs.org/zh/breaking-changes/

http://www.kler.cn/a/543357.html

相关文章:

  • uniapp中对于文件和文件夹的处理,内存的查询
  • 【设计模式】【行为型模式】模板方法模式(Template Method)
  • (五)C++的类继承、多态、组合
  • IntelliJ IDEA Console控制台输出成json的配置方式
  • 单张照片可生成写实3D头部模型!Adobe提出FaceLift,从单一的人脸图像中重建出360度的头部模型。
  • fps动作系统10:右键机瞄
  • PHP 中的除以零错误
  • 深度学习实战基础案例——卷积神经网络(CNN)基于DenseNet的眼疾检测|第4例
  • 基于Python flask-sqlalchemy的SQLServer数据库管理平台
  • WinForm 防破解、反编译设计文档
  • 2025年3月一区SCI-真菌生长优化算法Fungal growth optimizer-附Matlab免费代码
  • Citus的TPCC、TPCH性能测试
  • 时间敏感和非时间敏感流量的性能保证配置
  • 3dgs 2025 学习笔记
  • 【算法】【双指针】acwing算法基础 2816. 判断子序列
  • 懒人精灵内存插件(手游x86x64内存插件)
  • 芯盾时代数据安全产品体系,筑牢数据安全防线
  • Flowable:现代业务流程管理的解决方案
  • 深度学习新宠:卷积神经网络如何重塑人工智能版图?
  • Django 初学小案例:用户登录
  • ffmpeg -pix_fmts
  • 介绍几款免费的显示器辅助工具!
  • Linux虚拟机克隆
  • 【登录认证】
  • 异步加载和协程+Unity特殊文件夹
  • 不小心删除服务[null]后,git bash出现错误