vue组合式api(ref,reactive,toRef,toRefs,computed,watch,watchEffect,props,hooks),路由,pinia

一、ref(基本类型数据,对象类型数据)

1.作用:定义响应式变量

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

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

4.注意:js中操作数据需要写xxx.vslue,模板上不需要.value,直接使用。let xxx = ref(初始值),xxx不是响应式的,xxx.value是响应式的。

二、reactive(对象类型数据)

1.作用:定义响应式变量

2.语法:let xxx = reactive(源对象)

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

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

三、ref对比reactive

1.ref 用来定义:基本类型数据,对象类型数据

reactive用来定义:对象类型数据

2.区别

ref创建的变量必须使用.value

reactive重新分配一个新对象,会失去响应式。(可以使用Object.assign去整体替换)

3.使用原则

若需要一个基本类型的响应式数据,必须使用ref。

若需要一个响应式对象,层级不深,ref,reactive都可以。

若需要一个响应式对象,层级较深,推荐使用reactive。

四、toRefs和toRef

1.作用:将一个响应式对象中的每一个属性,转换为ref对象。

2.toRefs与toRef功能一致,但toRefs可以批量转换。

<template>
  <div>
    {{ name }}{{ age }}
  </div>
</template>
<script setup>
import { reactive, toRefs, toRef } from 'vue'
let person=reactive({name:'zs',age:18})
//解构赋值后失去响应式
let {name,age} = toRefs(person)
console.log('name,age',name.value,age.value);
let n1 = toRef(person, 'age')
console.log(n1.value);
</script>

五、computed

<template>
  <div>
    姓:<input type="text" v-model="firstName"><br>
    名:<input type="text" v-model="lastName"><br>
    全名:<span>{{ fullName }}</span>
    全名:<span>{{ fullName }}</span><br>
    <button @click="changeFullName">修改名字</button>
  </div>
</template>

<script setup>
import { ref,computed} from 'vue'
let firstName=ref('z')
let lastName=ref('s')
// 定义的fullName是一个计算属性,而且是只读的
// let fullName = computed(()=>{
//   console.log('1');
//   return firstName.value.slice(0,1).toUpperCase()+firstName.value.slice(1)+lastName.value.slice(0,1).toUpperCase()+lastName.value.slice(1)
// })
// 定义的fullName是一个计算属性,可读可写
let fullName = computed({
  get(){
    console.log('1')
    return firstName.value.slice(0,1).toUpperCase()+firstName.value.slice(1)+'-'+lastName.value.slice(0,1).toUpperCase()+lastName.value.slice(1)
  },set(val){
      let [str1,str2]=val.split('-');
      firstName.value=str1;
      lastName.value=str2
  }
})
let changeFullName = ()=>{
  fullName.value='li-si'
}
</script>

六、watch

1.作用:监视数据的变化

2.只能监视以下四种数据

ref定义的数据,reactive定义的数据,getter函数(一个函数,返回一个值),一个包含上述内容的数组。

a.监听ref定义的基本类型数据,对象类型数据

监视的是对象的地址值,想要监视内部属性的变化,需要deep属性为true

<template>
  <div>
  <p>监视ref定义的基本类型数据{{ sum }} <button @click="sum+=1">+1</button></p>
  <p>监视ref定义的对象类型数据{{ person.name }} {{ person.age }}
    <button @click="person.name+='~'">changeName</button>
    <button @click="person.age+=1">changeAge</button>
    <button @click="changePerson">changePerson</button>
  </p>
  </div>
</template>

<script setup>
import {  ref,watch} from 'vue'
let sum=ref(0)
const stopWatch= watch(sum,(newVal,oldVal)=>{
  console.log('newVal,oldVal', newVal,oldVal);
  if(newVal>10){
    stopWatch()
  }
})
let person=ref({
  name:'zs',
  age:18
})
let changePerson = ()=>{
  person.value={
    name:'ls',
    age:90
  }
}
//监视的是person的地址值,想要监视内部属性的变化,需要deep属性为true
watch(person,(newVal,oldVal)=>{
  console.log('newVal,oldVal', newVal,oldVal);
},{
  deep:true,
  immediate:true
})
</script>
 b.监听reactive定义的对象类型数据

监视的是reactive定义的对象类型数据,默认开启深度监听,该深度监听不能关闭

<template>
  <div>
  <p>监视reactive定义的对象类型数据{{ person.name }} {{ person.age }}
    <button @click="person.name+='~'">changeName</button>
    <button @click="person.age+=1">changeAge</button>
    <button @click="changePerson">changePerson</button>
  </p>
  </div>
</template>

<script setup>
import {  reactive,watch} from 'vue'
let person=reactive({
  name:'zs',
  age:18
})
let changePerson = ()=>{
  Object.assign( person,{
    name:'ls',
    age:90
  })
}
//监视的是reactive定义的对象类型数据,默认开启深度监听,该深度监听不能关闭
watch(person,(newVal,oldVal)=>{
  console.log('newVal,oldVal', newVal,oldVal);
},{
  // deep:false,
  immediate:true
})
</script>
 c.监视ref或reactive定义的对象类型数据中的某个属性
<template>
  <div>
  <p>监视响应式对象的的某个属性{{person.name}}{{ person.car.c1 }} {{ person.car.c2 }}
    <button @click="person.name='ls'">changeName</button>
    <button @click="person.car.c1='大宝马'">changeC1</button>
    <button @click="person.car.c2='大奔驰'">changeC2</button>
    <button @click="changeCar">changeCar</button>
  </p>
  </div>
</template>
<script setup>
import {  reactive,watch} from 'vue'
let person=reactive({
  name:'zs',
  age:18,
  car:{
    c1:'宝马',
    c2:'奔驰'
  }
})
let changeCar = ()=>{
  person.car={
    c1:'爱玛',
    c2:'绿驹'
  }
}
//监视响应式对象中的某个属性,且该属性是基本类型的,要写成函数式
watch(()=>person.name,(newVal,oldVal)=>{
  console.log('newVal,oldVal', newVal,oldVal);
})
//监视响应式对象中的某个属性,且该属性是对象类型的,可以直接写,也可以写成函数式,更推荐写函数式,对象监视的是地址值,对象内部需要手动开启深度监听。
watch(()=>person.car,(newVal,oldVal)=>{
  console.log('newVal,oldVal', newVal,oldVal);
},{
  deep:true
})
</script>
4.监视多个对象 
<template>
  <div>
  <p>监视多个数据{{person.name}}{{ person.car.c1 }} {{ person.car.c2 }}
    <button @click="person.name='ls'">changeName</button>
    <button @click="person.car.c1='大宝马'">changeC1</button>
    <button @click="person.car.c2='大奔驰'">changeC2</button>
    <button @click="changeCar">changeCar</button>
  </p>
  </div>
</template>
<script setup>
import {  reactive,watch} from 'vue'
let person=reactive({
  name:'zs',
  age:18,
  car:{
    c1:'宝马',
    c2:'奔驰'
  }
})
let changeCar = ()=>{
  person.car={
    c1:'爱玛',
    c2:'绿驹'
  }
}
//监视响应式对象中的某个属性,且该属性是对象类型的,可以直接写,也可以写成函数式,更推荐写函数式,对象监视的是地址值,对象内部需要手动开启深度监听。
watch([()=>person.name,()=>person.car],(newVal,oldVal)=>{
  console.log('newVal,oldVal', newVal,oldVal);
},{
  deep:true
})
</script>

七、watchEffect

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

watch对比watchEffect

都能监听响应式数据的变化,不同的是监听数据变化的方式不同

watch需要明确指出监视的数据

watchEffect不用明确指出监视的数据,函数中用到哪些属性,就会监听哪些属性。

<template>
  <div>
  <p>watchEffect{{water.temp}}{{ water.height }}
    <button @click="water.temp+=10">温度+10</button>
    <button @click="water.height+=10">高度+10</button>
  </p>
  </div>
</template>
<script setup>
import {  reactive,watchEffect} from 'vue'
let water=reactive({
  temp:0,
  height:0,
})
watchEffect(()=>{
  if(water.temp>=60||water.height>=80)
  console.log('water.temp,water.height',water.temp,water.height);
})
</script>

八、标签的ref属性

//父组件
<template>
  <HelloWorld ref="helloRef" />
  <button @click="logHelloRef">test</button>
</template>
<script setup>
import { ref } from 'vue';
import HelloWorld from './components/HelloWorld.vue';
const helloRef = ref(null);
const logHelloRef = () => {
  console.log('helloRef', helloRef.value);
};
</script> 

//子组件
<template>
  <div>
  <p>watchEffect{{water.temp}}{{ water.height }}
    <button @click="water.temp+=10">温度+10</button>
    <button @click="water.height+=10">高度+10</button>
  </p>
  </div>
</template>
<script setup>
import {  reactive,watchEffect,defineExpose} from 'vue'
let water=reactive({
  temp:0,
  height:0,
})
watchEffect(()=>{
  if(water.temp>=60||water.height>=80)
  console.log('water.temp,water.height',water.temp,water.height);
})
defineExpose({water})
</script>

九、props

defineProps可以省略不引入

//父组件
<template>
  <HelloWorld  :a="a" :b="list" c=123 />
</template>
<script setup>
import { ref,reactive } from 'vue';
import HelloWorld from './components/HelloWorld.vue';
let a=ref('aaaaaaaaaaa');
let list=reactive([
  {
    msg:'aa',
    num:0
  },
  {
    msg:'bb',
    num:1
  },
]);
</script>
//子组件
<template>
  <div>
  {{ props.a }}{{ props.c }}
  <ul>
    <li v-for="item in b" :key="item.num">{{ item.num }}{{ item.msg }}</li>
  </ul>
  </div>
</template>
<script setup>
import {  defineProps} from 'vue'
const props=defineProps({
  a:{
    type:String,
    required:true,
    default:'abc'
  },
  b:{
    type:Array,
    required:true,
    default:[]
  },
  c:{
    type:Number,
    required:true,
    default:0
  }
})
</script>

十、生命周期

<template>
  <HelloWorld v-if="isShow"/>
  <button @click="isShow=!isShow">flag</button>
</template>
<script setup>
import { ref } from 'vue';
import HelloWorld from './components/HelloWorld.vue';
let isShow = ref(true)
</script>

<template>
  <div>
    {{ sum }}
    <button @click="sum+=1">+1</button>
  </div>
</template>
<script setup>
import {ref, onBeforeMount, onMounted,onBeforeUpdate,onUpdated,onBeforeUnmount,onUnmounted} from 'vue'
let sum = ref(0)
console.log('创建');
onBeforeMount(()=>{
  console.log('挂载前');
})
onMounted(()=>{
  console.log('挂载完毕');
})
onBeforeUpdate(()=>{
  console.log('更新前');
})
onUpdated(()=>{
  console.log('更新完毕');
})
onBeforeUnmount(()=>{
  console.log('卸载前');
})
onUnmounted(()=>{
  console.log('卸载完毕');
})
</script>

十一、hooks

组合式函数约定用驼峰命名法命名,并以“use”作为开头

<template>
  <div>
    {{ sum }}{{ bigSum }}
    <button @click="add">+1</button>
  </div>
</template>
<script setup>
import useSum from '../hooks/useSum'
let {sum,add,bigSum}=useSum()
</script>

useSum.js
import { onMounted, ref, computed } from 'vue'
export default function() {
    //数据
    let sum = ref(0);
    //方法
    const add = () => {
        sum.value += 1
    };
    //计算属性
    let bigSum = computed(() => {
        return sum.value * 10
    });
    //钩子
    onMounted(() => {
        add()
    })
    return { sum, add, bigSum }
}

十二、路由

1.params写法

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

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

路由的props配置,让路由组件更方便的收到参数,将路由参数作为props传给组件

路由配置router.js
import Home from '../components/HelloWorld.vue';
import News from '../components/NewsView.vue';
import Detail from '../components/DetailView.vue';

import { createRouter, createWebHashHistory } from 'vue-router'
const router = createRouter({
    history: createWebHashHistory(),
    routes: [{
            path: '/',
            redirect: '/home'
        },
        {
            path: '/home',
            component: Home
        },
        {
            path: '/news',
            component: News,
            children: [{
                path: 'detail/:id/:title/:content',
                name: 'detail',
                component: Detail,
                props: true
            }]
        }
    ]
})
export default router;

路由跳转(三种写法)
<template>
  <div>
    <ul>
      <li v-for="news in list" :key="news.id">
        <button @click="gotoDetail(news)">{{ news.title }}</button>
        <router-link :to="`/news/detail/${news.id}/${news.title}/${news.content}`">{{ news.title }}</router-link> 
        <router-link :to="{
          name:'detail',
          params:{
            id:news.id,
            title:news.title,
            content:news.content
          }
        }
        ">{{ news.title }}</router-link> 
    </li>
  </ul>
  <router-view></router-view>
  </div>
</template>
<script setup>
import { reactive } from 'vue';
import { useRouter } from 'vue-router';
const list = reactive([
  { 
    id:'efadsiub01',
    title:'新闻1',
    content:'xxxxxxxxxxxxxxxxxx1'
  },
  { 
    id:'efadsiub02',
    title:'新闻2',
    content:'xxxxxxxxxxxxxxxxxx2'
  },
  { 
    id:'efadsiub03',
    title:'新闻3',
    content:'xxxxxxxxxxxxxxxxxx3'
  }
])
const router = useRouter()
const gotoDetail=(news)=>{
  router.push({
    name:'detail',
    params:{
      id:news.id,
      title:news.title,
      content:news.content
    }
  })
}
</script>

//接收参数
<template>
  <div>
    <p>序号:{{ route.params.id }}</p>
    <p>标题:{{ route.params.title }}</p>
    <p>内容:{{ route.params.content }}</p>
    <p>序号:{{ id }}</p>
    <p>标题:{{ title }}</p>
    <p>内容:{{ content }}</p>
  </div>
</template>
<script setup>
import { useRoute } from 'vue-router';
const route = useRoute()
defineProps(['id','title','content'])
</script>
2.query写法
//路由配置router.js
import Home from '../components/HelloWorld.vue';
import News from '../components/NewsView.vue';
import Detail from '../components/DetailView.vue';
import { createRouter, createWebHashHistory } from 'vue-router'
const router = createRouter({
    history: createWebHashHistory(),
    routes: [{
            path: '/',
            redirect: '/home'
        },
        {
            path: '/home',
            component: Home
        },
        {
            path: '/news',
            component: News,
            children: [{
                path: 'detail',
                name: 'detail',
                component: Detail,
                props(route) {
                    return route.query
                }
            }]
        }
    ]
})
export default router;

//路由跳转并传参
<template>
  <div>
    <ul>
      <li v-for="news in list" :key="news.id">
        <button @click="gotoDetail(news)">{{ news.title }}</button>
        <router-link :to="`/news/detail?id=${news.id}&title=${news.title}&content=${news.content}`">{{ news.title }}</router-link> 
        <router-link :to="{
          name:'detail',
          query:{
            id:news.id,
            title:news.title,
            content:news.content
          }
        }
        ">{{ news.title }}</router-link> 
    </li>
  </ul>
  <router-view></router-view>
  </div>
</template>
<script setup>
import { reactive } from 'vue';
import { useRouter } from 'vue-router';
const list = reactive([
  { 
    id:'efadsiub01',
    title:'新闻1',
    content:'xxxxxxxxxxxxxxxxxx1'
  },
  { 
    id:'efadsiub02',
    title:'新闻2',
    content:'xxxxxxxxxxxxxxxxxx2'
  },
  { 
    id:'efadsiub03',
    title:'新闻3',
    content:'xxxxxxxxxxxxxxxxxx3'
  }
])
const router = useRouter()
const gotoDetail=(news)=>{
  router.push({
    name:'detail',
    query:{
      id:news.id,
      title:news.title,
      content:news.content
    }
  })
}
</script>

//接收参数
    <p>内容:{{ route.query.content }}</p>
    <p>序号:{{ id }}</p>
    <p>标题:{{ title }}</p>
    <p>内容:{{ content }}</p>
  </div>
</template>
<script setup>
import { useRoute } from 'vue-router';
const route = useRoute()
defineProps(['id','title','content'])
</script>

十三、pinia

1.搭建pinia
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import router from './router/index'
import { createPinia } from 'pinia'
const app = createApp(App)
const pinia = createPinia()
app.use(pinia)
app.use(router)
app.mount('#app')
2.存储和读取数据(state,actions,getters)
store/count.js
import { defineStore } from 'pinia'
export const useCountStore = defineStore('count', {
    state() {
        return {
            sum: 0,
            school: '第一学校',
            address: '西安',
            time: new Date()
        }
    },
    actions: {
        //this就是当前仓库
        increment(val) {
            console.log('increment');
            this.sum += val
        }
    },
    getters: {
        bigSum: (state) => {
            return state.sum * 10
        },
        formatDate(state) {
            const date = state.time;
            const year = date.getFullYear(); // 年
            const month = String(date.getMonth() + 1).padStart(2, '0'); // 月,注意月份从0开始
            const day = String(date.getDate()).padStart(2, '0'); // 日
            const hours = String(date.getHours()).padStart(2, '0'); // 小时
            const minutes = String(date.getMinutes()).padStart(2, '0'); // 分
            const seconds = String(date.getSeconds()).padStart(2, '0'); // 秒
            return {
                'YYYY-MM-DD': `${year}-${month}-${day}`,
                'DD/MM/YYYY': `${day}/${month}/${year}`,
                'MM-DD-YYYY': `${month}-${day}-${year}`,
                'YYYY/MM/DD HH:mm:ss': `${year}/${month}/${day} ${hours}:${minutes}:${seconds}`,
                'YYYY.MM.DD': `${year}.${month}.${day}`
            };
        }
    }
})

<template>
  <div>
    <h2>求和:{{ countStore.sum }},放大10倍:{{ bigSum }}</h2>
    <p>
      学校:{{ countStore.school }}
    </p>
    <p>{{time}}</p>
    <p>,{{ formatDate }}</p>
    <p>位置:{{ countStore.address }}</p>
    <button @click="add">批量修改</button>
  </div>
</template>
<script setup>
import { ref,toRefs } from 'vue';
import { useCountStore } from '../store/count';
import {storeToRefs} from 'pinia'
const countStore=useCountStore()
//解构赋值丢失了响应式
// const {sum,school,address} = countStore
//store中所有属性和方法都包裹成ref对象
// const {sum,school,address} = toRefs(countStore)
//storeToRefs只会关注store中的数据,不会对方法进行包裹
const {sum,school,address,time,bigSum,formatDate} = storeToRefs(countStore)

//以下两种方式都可以拿到state中的数据
console.log('sum',countStore.sum);
console.log('sum',countStore.$state.sum);
const add = ()=>{
  //第一种修改方式
  // countStore.sum+=1;
  //第二种修改方式
  // countStore.$patch({
  //   sum: 1,
  //   school: '第二学校',
  //   address: '西安1'    
  // })
  //第三种修改方式
  countStore.increment(30)
}
</script>
3.使用$subscribe订阅数据的变化 
store/talk.js
import { defineStore } from 'pinia'
import axios from 'axios'
import { nanoid } from 'nanoid'
export const useTalkStore = defineStore('talk', {
    state() {
        return {
            list: JSON.parse(localStorage.getItem('talkList')) || []
        }
    },
    actions: {
        async getLove() {
            let res = await axios.get('https://v1.hitokoto.cn')
            let obj = {
                id: nanoid(),
                title: res.data.hitokoto
            }
            this.list.unshift(obj)
        }
    }
})

<template>
  <div>
    <button @click="getLoveFn">获取一句土味情话</button>
    <ul>
      <li v-for="item in list" :key="item.id">{{ item.title }}</li>
    </ul>
  </div>
</template>
<script setup>
import {useTalkStore } from '../store/talk'
import {storeToRefs} from 'pinia'
const talkStore=useTalkStore()
talkStore.$subscribe((mutate,state)=>{
  console.log('talkStore保存的数据发生了变化',mutate,state);
  localStorage.setItem('talkList',JSON.stringify(state.list))
})
const {list}=storeToRefs(talkStore)
const getLoveFn=()=>{
  talkStore.getLove()
}
</script>
 4.store组合式写法
import { defineStore } from 'pinia'
import axios from 'axios'
import { nanoid } from 'nanoid'
import { reactive } from 'vue'
export const useTalkStore = defineStore('talk', () => {
    const list = reactive(JSON.parse(localStorage.getItem('talkList')) || [])
    async function getLove() {
        let res = await axios.get('https://v1.hitokoto.cn')
        let obj = {
            id: nanoid(),
            title: res.data.hitokoto
        }
        list.unshift(obj)
    }
    return { list, getLove }
})

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值