Vue3学习笔记(2)

ref&reactive

ref

可以针对基本数据类型,也可以针对对象类型。

用ref针对对象时,本质是在value里面用reactive实现数据响应。

引入ref:

import {ref} from 'vue'

函数里面调用被ref包裹的数据:

基本数据类型:数据名.value

对象类型:对象名.value.属性名(对象名.value[数组下标].属性名)

批量修改响应式数据

Object.assign(对象名,{替换/增加属性名:替换/增加属性,......})

 Object.assign(car,{brand:'小米suv7',price:200})

reactive

只能针对对象类型

函数里面调用被reactive包裹的数据:
直接用对象名.属性名即可。

引入reactive:

import {reactive} from 'vue'

批量修改响应式数据

对象名.value={属性名:修改后的属性值,......}

toRefs(person)

取对象数据并具有响应式

引入toRef:import {reactive,toRef}

let nl=toRef(person,'age')

解构响应式数据

引入toRefs:import {reactive,toRefs}

let {name,age}=

使用场景

基本数据:ref

响应式对象:ref/reactive

层级较深的响应式对象(表单):reactive

计算属性computed

引入computed

import {ref,computed} from 'vue'

定义

可读但是不可写

let fullName=computed(()=>{
    return firstName.value.slice(0,1).toUpperCase()+firstName.value.slice(1)+'-'+lastName.value
   })

调用

{{fullName}}

特点:有缓存,如果数据不变,则就算调用多次也只会计算一次,然后把计算结果缓存,调用的时候直接访问的是缓存的数据(method是每次访问都会重新计算)。

watch监视

作用:监视数据的变化

特点:只能监视以下四种数据:

  • ref定义的数据

  • reactive定义的数据

  • 函数返回一个值(getter函数)

  • 一个包含上述内容的数组

引入watch

 import {ref,watch} from 'vue'

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

定义

watch(监视的对象,回调函数(newValue,oldValue))

注:监视的是对象本身,而不是对象的value值,所以如果是ref包裹的对象,()里面不能加.value

 watch(sum,(newValue,oldValue)=>{
        console.log('sum变化了');

    })

停止监视

 const stopWatch=watch(sum,(newValue,oldValue)=>{
        console.log('sum变化了');
        if(newValue>=10){
            stopWatch()
        }
    })

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

情况一:只监视对象的地址值

watch(person,(newValue,oldValue)=>{
        console.log(newValue,oldValue);
    })

情况二:深度监视,监视对象的属性

修改值的本质是直接把定义那里的属性值变化,所以watch输出的newValue和oldValue是相同的

 watch(person,(newValue,oldValue)=>{
        console.log(newValue,oldValue);
    },{deep:true})
</script>

立即执行:页面刷新,对象值还未改变先调用一次watch

watch(person,(newValue,oldValue)=>{
        console.log(newValue,oldValue);
    },{deep:true,immediate:true})

监视reactive定义的对象类型数据

默认是深度监视,直接监视所有属性,并且无法变成浅层监听。

<script lang="ts" setup name="Person234">
    import {reactive,watch} from 'vue'
    // 数据
    let person=reactive({
        name:"张三",
        age:18
    })
    // 方法
    function changeName(){
        person.name+='~'
    }
    function changeAge(){
        person.age++
    }
    function changePerson(){
        Object.assign(person,{name:"李四",age:80})
    }
    //监视:监视reactive定义的对象类型数据 默认开启深度监视
    watch(person,(newValue,oldValue)=>{
    console.log(newValue,oldValue); 
})
</script>

监视reactive定义的对象类型数据内部的某个属性

写成函数形式当做函数的返回值

基本数据类型

 let person=reactive({
        name:"张三",
        age:18,
    })
watch(()=>person.name,(newValue,oldValue)=>{
        console.log('person.name',newValue,oldValue);
        
    })

对象类型数据

监视的是对象在栈里面的地址,只改某个属性改的是堆里面的内容,person.car={c1:'迈巴赫',c2:'大众'}是将person.car指向其他地址,相当于栈改变了,如果要监视被监视的对象里面的其中一个属性的变化,要加deep:true

 let person=reactive({
        car:{
            c1:'宝马',
            c2:'奔驰'
        }
    })
function changeCar(){
        //  reactive修改值可以修改里面的值,相当于car是person的value值
        person.car={c1:'迈巴赫',c2:'大众'}
    }
 watch(()=>person.car,(newValue,oldValue)=>{
        console.log('person.car',newValue,oldValue);
        
    },{deep:true})

监视reactive的多个数据

watch([()=>person.name,()=>person.car.c1],(newValue,oldValue)=>{
        console.log('person.car',newValue,oldValue);
        
    },{deep:true})

watchEffect监视

自动分析要监视的数据,不用支出坚实的数据,函数中用到哪些属性,就监视哪些属性。

watchEffect(()=>{
    if(temp.value>=60||height.value>=80){
        console.log('给服务器发送请求');
        
    }
})

标签ref属性

标记内容

创建一个title2,用于存储ref标记的内容:

let title2=ref()

标记标签:

 <h2 ref="title2">北京</h2>

获取标签:

console.log(title2.value);

特点:相当于局部标记,只会对同一个vue文件里面的标签有用,其他文件的标签如果有同名的标记不会和此文件冲突。

组件标记

在组件文件里面导出标记的数据:

//引入defineExpose
import {ref,defineExpose} from 'vue'
//将需要导出的标记的元素写在({})里面
defineExpose({a,b,c})

组件调用:

<Person ref="ren"/>

TS中的接口,泛型,自定义类型

作用:限制对象的名字,会报错

方式:

基本数据:

ts文件:

// 定义一个接口,用于限制person对象的具体属性
export interface PersonInter  {//分别暴露
    id:string,
    name:string
    age:number
}

引用ts文件:

import {type PersonInter,} from '../types/index'//ts文件相对位置

限制用法:

let person:PersonInter={id:'ashdlkha01',name:'张三',age:60}

复杂数据类型:

ts文件:

//一个自定义类型
//  export type 
//第一种
Persons=Array<PersonInter>
//第二种
export type Persons=PersonInter[]

限制用法:

第一种:

let personLIst:Array<PersonInter>=[//是一个数组,且数组里面的每一项都满足PersonInter的限制
        {id:'lakjhlkjhl01',name:'张三',age:60},
        {id:'lakjhlkjhl02',name:'李四',age:70},
        {id:'lakjhlkjhl03',name:'王五',age:50}
    ]

第二种:

let personLIst:Persons=[//是一个数组,且数组里面的每一项都满足PersonInter的限制
        {id:'lakjhlkjhl01',name:'张三',age:60},
        {id:'lakjhlkjhl02',name:'李四',age:70},
        {id:'lakjhlkjhl03',name:'王五',age:50}
    ]

props用法

导出数据:

<Person a="hh" :list="personList"/>

接收其他文件的数据:

import {defineProps} from 'vue'
defineProps(['a'])//就算只有一个也要包裹在[]里面

接收+将props保存:

let x=defineProps(['list'])

使用:

console.log(x.a);

接受+限制类型:

import {defineProps} from 'vue'
import {type Persons} from '../types'
defineExpose<{list:Persons}>()

接收+限制类型+限制必要性+指定默认值:

import {defineProps,withDefaults} from 'vue'
import {type Persons} from '../types'
 withDefaults(defineProps<{list?:Persons}>(),{
        list:()=>[{id:'sdfasdfas01',name:'康师傅',age:19}]
    })

Vue生命周期

概念:Vue组件实例在创建时要经历一系列的初始化步骤,在此过程中 Vue会在合适的时机,调用特定的函数,从而让开发者有机会在特定阶段运行自己的代码,这些特定的函数统称为:生命周期钩子。

规律:

生命周期整体分为四个阶段,分别是:**创建、挂载、更新、销毁**,每个阶段都有两个钩子,一前一后。

Vue2的生命周期

创建阶段:beforeCreate、created

挂载阶段:beforeMount、mounted

更新阶段:beforeUpdate、updated

销毁阶段:beforeDestroy、destroyed

Vue3的生命周期

创建阶段:setup

挂载阶段:onBeforeMount、onMounted

更新阶段:onBeforeUpdate、onUpdated

卸载阶段:onBeforeUnmount、onUnmounted

常用的钩子:onMounted(挂载完毕)、onUpdated(更新完毕)、onBeforeUnmount(卸载之前)


<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>

自定义hook

- 本质是一个函数,把 setup函数中使用的 Composition API进行了封装。

- 自定义 hook的优势:复用代码, 让 setup中的逻辑更清楚易懂。

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()
  })

  //向外部暴露数据
  return {sum,increment,decrement}
}	

useDog.ts中内容如下:

<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>

组件中具体使用:

<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>

路由

基本切换效果

- Vue3中要使用 vue-router的最新版本,目前是 4版本。

- 路由配置文件代码如下:

`main.ts`代码如下:import {createRouter,createWebHistory} from 'vue-router'
import Home from '@/pages/Home.vue'
import News from '@/pages/News.vue'
import About from '@/pages/About.vue'

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

main.ts代码如下:

import router from './router/index'
app.use(router)

app.mount('#app')

App.vue代码如下:

<template>
  <div class="app">
    <h2 class="title">Vue路由测试</h2>
    <!-- 导航区 -->
    <div class="navigate">
      <RouterLink to="/home" active-class="active">首页</RouterLink>
      <RouterLink to="/news" active-class="active">新闻</RouterLink>
      <RouterLink to="/about" active-class="active">关于</RouterLink>
    </div>
    <!-- 展示区 -->
    <div class="main-content">
      <RouterView></RouterView>
    </div>
  </div>
</template>

<script lang="ts" setup name="App">
  import {RouterLink,RouterView} from 'vue-router'  
</script>

注意点

  1. 路由组件通常存放在 pagesviews文件夹,一般组件通常存放在 components文件夹。

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

路由器工作模式

1、history模式

优点:`URL`更加美观,不带有 #,更接近传统的网站 URL

缺点:后期项目上线,需要服务端配合处理路径问题,否则刷新会有 404错误。

const router = createRouter({
  	history:createWebHistory(), //history模式
  	/******/
})

2、hash模式

优点:兼容性更好,因为不需要服务器端处理路径。

缺点:`URL`带有 #不太美观,且在 SEO优化方面相对较差。

const router = createRouter({   	history:createWebHashHistory(), //hash模式   	/******/ })

to的写法

<!-- 第一种:to的字符串写法 -->
<router-link active-class="active" to="/home">主页</router-link>

<!-- 第二种:to的对象写法 -->
<router-link active-class="active" :to="{path:'/home'}">Home</router-link>

命名路由

作用:可以简化路由跳转及传参。

给路由规则命名:


routes:[
  {
    name:'zhuye',
    path:'/home',
    component:Home
  },
  {
    name:'xinwen',
    path:'/news',
    component:News,
  },
  {
    name:'guanyu',
    path:'/about',
    component:About
  }
]

跳转路由:

<!--简化前:需要写完整的路径(to的字符串写法) -->
<router-link to="/news/detail">跳转</router-link>

<!--简化后:直接通过名字跳转(to的对象写法配合name属性) -->
<router-link :to="{name:'guanyu'}">跳转</router-link>

嵌套路由

1、编写 News的子路由:`Detail.vue`

2、配置路由规则,使用 children配置项:

const router = createRouter({
  history:createWebHistory(),
	routes:[
		{
			name:'zhuye',
			path:'/home',
			component:Home
		},
		{
			name:'xinwen',
			path:'/news',
			component:News,
			children:[
				{
					name:'xiang',
					path:'detail',
					component:Detail
				}
			]
		},
		{
			name:'guanyu',
			path:'/about',
			component:About
		}
	]
})
export default router

3、跳转路由(记得要加完整路径):

<router-link to="/news/detail">xxxx</router-link>
<!-- 或 -->
<router-link :to="{path:'/news/detail'}">xxxx</router-link>

4、记得去 Home组件中预留一个 <router-view>

<template>   <div class="news">     <nav class="news-list">       <RouterLink v-for="news in newsList" :key="news.id" :to="{path:'/news/detail'}">         {{news.name}}       </RouterLink>     </nav>     <div class="news-detail">       <RouterView/>     </div>   </div> </template>

路由传参

query参数:

1、传递参数

<!-- 跳转并携带query参数(to的字符串写法) -->
<router-link to="/news/detail?a=1&b=2&content=欢迎你">
	跳转
</router-link>

<!-- 跳转并携带query参数(to的对象写法) -->
<RouterLink 
  :to="{
    //name:'xiang', //用name也可以跳转
    path:'/news/detail',
    query:{
      id:news.id,
      title:news.title,
      content:news.content
    }
  }"
>
  {{news.title}}
</RouterLink>

2、接收参数

import {useRoute} from 'vue-router'
const route = useRoute()
// 打印query参数
console.log(route.query)

params参数

1、传递参数

<!-- 跳转并携带params参数(to的字符串写法) -->
<RouterLink :to="`/news/detail/001/新闻001/内容001`">{{news.title}}</RouterLink>

<!-- 跳转并携带params参数(to的对象写法) -->
<RouterLink 
  :to="{
    name:'xiang', //用name跳转
    params:{
      id:news.id,
      title:news.title,
      content:news.title
    }
  }"
>
  {{news.title}}
</RouterLink>

2、接收参数

import {useRoute} from 'vue-router'
const route = useRoute()
// 打印params参数
console.log(route.params)

备注1:传递 params参数时,若使用 to的对象写法,必须使用 name配置项,不能用 path

备注2:传递 params参数时,需要提前在规则中占位。

路由的props配置

作用:让路由组件更方便的收到参数(可以将路由参数作为 props传给组件)

{
	name:'xiang',
	path:'detail/:id/:title/:content',
	component:Detail,

  // props的对象写法,作用:把对象中的每一组key-value作为props传给Detail组件
  // props:{a:1,b:2,c:3}, 

  // props的布尔值写法,作用:把收到了每一组params参数,作为props传给Detail组件
  // props:true
  
  // props的函数写法,作用:把返回的对象中每一组key-value作为props传给Detail组件
  props(route){
    return route.query
  }
}

replace属性

1、作用:控制路由跳转时操作浏览器历史记录的模式。

2、浏览器的历史记录有两种写入方式:分别为 ``push``和 ``replace``:

  • ``push``是追加历史记录(默认值)。

  • replace是替换当前记录。

3、开启replace模式

<RouterLink replace .......>News</RouterLink>

编程式导航

路由组件的两个重要的属性:`$route`和 $router变成了两个 hooks

import {useRoute,useRouter} from 'vue-router'

const route = useRoute()
const router = useRouter()

console.log(route.query)
console.log(route.parmas)
console.log(router.push)
console.log(router.replace)

重定向

1、作用:将特定的路径,重新定向到已有路由。

2、具体编码:

{
    path:'/',
    redirect:'/about'
}


Vue3学习笔记(2)
http://localhost:8090//archives/vue3xue-xi-bi-ji-2
作者
林依琪
发布于
2024年11月24日
许可协议