Vue3 组合Api Vite

1 篇文章 0 订阅

Vue3 组合Api

一、setup,以及peros通信

1.在setup 内通过ref定义定义组件内的状态值
2.通过toRef()定义响应式状态值
3.通过toRefs()将普通对象转换成响应式对象
4.通过reactive直接定义响应式对象,并且可以深度响应(深度监听值的改变)
5.setup内通过return 一个对象向外暴露 状态值,template才能调用
6.props传值问题
	1)子组件通过props:["父组件传递的props"] 接收父组件传递的值
	2)setup接收参数props,并且将父组件传递的props处理成响应式数据,通过return返回后才能被template调用
//Father.vue
<template>
  <Child :title='title' :obj='obj'></Child>
</template>
<script>
import {onMounted, reactive, ref, toRef,toRefs} from 'vue';
import Child from './Child.vue';
export default {
  setup(){
    const title = ref('hello')
    const obj = reactive({x:100,y:200})
    onMounted(()=>{
      setTimeout(()=>{
        title.value = "你好!"
        obj.x = 1000
        obj.y = 2000
      },5000)
    })
    return {
      title,
      obj
    }
  },
  components: { Child }
}
</script>
<style scoped>
</style>

//Child.vue
<template>
  <div><h1>{{title}}</h1>{{obj}}</div>
</template>
<script>
import { reactive, toRef } from '@vue/reactivity'
export default {
  props:['title','obj'],
  setup(props){
    const title = toRef(props,'title')
    const obj  = reactive(props.obj)
    return {
      title,
      obj  
    }
  }
}
</script>
<style scoped>
</style>

ref获取子组件实例,可以实现通信

1.父组件内ref绑定子组件
2.子组件的setup内通过expose()暴露出一个对象,父组件只能通过this.$refs.child访问到子组件暴露出去的参数和方法
//Father.vue
<template>
  <Child ref='child'>
    <h1>header</h1>
  </Child>
</template>
<script>
import {onMounted, reactive, ref, toRef,toRefs} from 'vue';
import Child from './Child.vue';
export default {
  components: { 
    Child 
  },
  mounted(){
    console.log(this.$refs.child.a)
    this.$refs.child.b()
  },
}
</script>
<style scoped>
</style>

//Child.vue
<script>
import { reactive, toRef } from '@vue/reactivity'
export default {
  setup(props,{attrs , expose}){
    //已经没有context对象了
    // console.log(context.slots)
    // console.log(context.emit)
    // console.log(context.attrs)
    // console.log(context.expose)
    //这里向父组件暴露值和方法,父组件可以调用和使用
    expose({
      a:1001,
      b:()=>{
        console.log("aaa")
      }
    })
    return {
    }
  }
}
</script>

渲染函数h

	<script>
import { ref, h, onMounted } from 'vue';
export default {
  setup(props,{ expose }){
    const count = ref(0)
    // return {
    //   count
    // }
  onMounted(()=>{
    setTimeout(()=>{
      count.value = 100
    },2000)
  })
  expose({count})
  return ()=> h('div' ,{class:"abc"}, h('a',{href:"http://www.baidu.com"},`${count.value}百度`))
  }
}
</script>

组合Api核心

1.ref()可以定义一个简单数据类型,也可以是一个复杂数据类型
	1)接受一个参数值并返回一个响应式且可改变的 ref 对象
2.reactive() 
	1)接收一个普通对象然后返回该普通对象的响应式代理
	2)可以和ref()绑定,会互相影响,修改ref的值,reactive的值也改变;修改reactive的值,ref的值也改变
	3)reactive定义的数据类型可以是简单数据类型,数组等复杂数据类型,数据都会发生响应式变化,但是Map不会发生响应式变化
3.computed()计算属性
4.readonly()处理的数据只读 ,
	1)传入一个对象(响应式或普通)或 ref,返回一个原始对象的只读代理
5.watchEffect(()=>{})监听数据的变化,初始化的时候也会执行一次,返回一个函数,执行这个返回的函数会解除监听
	1)立即执行传入的一个函数,并响应式追踪其依赖,并在其依赖变更时重新运行该函数
	2)类似于watchEffect(()=>{})返回一个清理函数的还有react中的useEffect ,因为组件卸载时需要清除 effect 创建的诸如订阅或计时器 ID 等资源。要实现这一点,useEffect 函数需返回一个清除函数
	3)mobx中的reaction,reaction(() => data, (data, reaction) => { sideEffect }, options?)返回一个清理函数
	4)只有回调函数内被使用的值发生变化才会执行
6.watch([()=>state.count],[(count,prevcount)=>{}])监听多个数据的变化,作出反应
	1)和computed()的区别:watch监听数据的改变,突出改变之后要做什么;computed()在数据改变后,突出返回一个新的值
	2)和
import { onMounted, reactive, ref ,computed,readonly ,watchEffect,watch} from 'vue'
export default {
  setup(){
    //1.ref()
    const count = ref({
      x:100,
      y:{
        a:200
      }
    })
    onMounted(()=>{
      setTimeout(()=>{
        count.value.x=1000
        count.value.y.a=2000
      },2000)
    })

    //2.reactive
    const count = ref(0)
    const obj = reactive({count})
    //简单数据类型
    obj.count++
    console.log(obj.count)

    count.value = 100
    console.log(obj.count)
  
    obj.count = 200
    console.log(count.value)
// 数组等复杂数据类型
    const books = reactive([ref('JavaScript学习指南')])
    books[0].value = 'html学习指南'
    console.log(books[0].value)
//map对象
    const map = reactive(new Map([['count', ref(0)]]))
    console.log(map.get('count').value)
    map.set('count.value', 300)
    console.log(map.get('count').value)

// 3.computed()
    const count = ref(0)
    const addOne = computed(() => count.value + 1)
    console.log(addOne.value)
    count.value++
    console.log(addOne.value)

// 4.readonly()
    const original = reactive({ count: 0 })
    const copy = readonly(original)
    original.count = 100
    copy.count = 200
    console.log(copy.count)

 // 5、watchEffect()
    const count = ref(0)
    const title = ref('line1')
    const stop = watchEffect(() => console.log(count.value + title.value)) // 总是要执行一次 0
    stop()
    count.value = 100
    setTimeout(()=>{
      title.value = 'line2'
    },1000)

   // 6、watch()
    const state = reactive({count: 0})
    watch(
      () => state.count,
      (count, prevCount) => {
        console.log(count)
        console.log(prevCount)
      }
    )
    state.count++
    const count = ref(() => {console.log(0)})
    console.log(count.value)
    return {count}
  }
}
</script>

tools工具

1.isRef()  判断是否是ref定义的响应式数据类型
2.unref() 将响应式数据转换成普通数据
3.toRef(),toRefs() 将数据转换成响应式
4.isProxy() 检查一个对象是否是由 reactive 或者 readonly 方法创建的代理。
5.isReactive() 检查一个对象是否是由 reactive 创建的响应式代理
6.isReadonly() 检查一个对象是否是由 readonly 创建的代理

Setup生命周期以及setup内的生命周期

setup
创建组件实例,然后初始化 props ,紧接着就调用setup 函数。从生命周期钩子的视角来看,它会在 beforeCreate 钩子之前被调用
setup内的生命周期
1.onMounted

2.onUpdated

3.onUnmounted

4.onBeforeMount

5.onBeforeUpdate

6.onBeforeUnmount

provide-inject生产者和消费者 通信

通过provide-inject通信
父组件通过provide提供一个ref对象或者是reactive处理过proxy对象,
后代组件通过inject接收祖先组件传递的参数

//Father.vue
<script>
import { provide, ref } from 'vue'
import Son from './Son.vue'
export default {
  setup(){
    const title = ref('hello')
    setTimeout(()=>{
      title.value = "你好"
    },3000)
    provide('title',title)
  },
  components:{
    Son
  }
}
</script>

//Son.vue
import GrandSon from './GrandSon.vue'
export default {
  components: { GrandSon },
}
</script>

//GrandSon.vue
<template>
  <div>
    GrandSon:{{title}}
  </div>
</template>
<script>
import {ref,inject} from 'vue'
export default {
  setup(){
    const title = inject('title')
    return {title}
  }
}
</script>

vuex的使用

//store/index.js
import {createStore} from 'vuex'
const store = createStore({
  state(){
    return {
      count:0
    }
  },
  mutations:{
    addCount(state){
      state.count++
    }
  }
})
export default store;

//main.js
import store from './store'
app.use(store)

//组件使用
<template>
   <h1>
    <span>{{count}}</span>
    <button @click="addcount">+</button>
  </h1>
</template>
<script setup>
import {useStore} from 'vuex'
import {computed} from 'vue'
  const store = useStore()

  const count = computed(()=>{
    return store.state.count
  })

  const addcount = ()=>{
    store.commit('addCount')
  }
</script>

hooks

通过hook实现类似于vuex的功能

//hooks/useCount.js
import {ref} from 'vue'
export default ()=>{
  const count = ref(0)
  const addCount = ()=>{
    count.value++
  }
  return {
    count,
    addCount
  }
}

//hooks/useWord,js
import {ref} from 'vue'
export default (count)=>{
  const words = ref('v')
  const addWords = ()=>{
    for(var i = 0;i<count.value;i++){
      words.value += 'v'
    }
  }
  const reset = ()=>{
    words.value = 'v'
  }
  return {
    addWords,
    words,
    reset
  }
}

//组件对hooks使用
<template>
  <div>
    <p>count:{{count}}</p><button @click="addCount">add</button>
    <p>words:{{words}}</p><button @click="reset">reset</button>
    <button @click="addWords">增加{{count}}</button>
  </div>
</template>
<script>
import useCount from '../../hooks/useCount';
import useWord from '../../hooks/useWord';
export default {
  setup(){
     const { count, addCount} = useCount()
     const {words,addWords,reset} = useWord(count)
    return {
      count,
      addCount,
      words,
      addWords,
      reset
    }
  }
}
</script>

Vue3中使用vue-router

vue-router 路由守卫
onBeforeRouteLeave 离开前
onBeforeRoutrUpdata 路由更新前(必须是动态路由改变)

//router/index.js
//vue-router 4.X
import { createRouter, createWebHistory} from 'vue-router'
import Home from '../view/Home.vue'
const routes = [
  {
    path: '/home',
    name: 'home',
    component: Home
  },
  {
    path: '/list',
    name: 'list',
    component: () => import('../view/List.vue')  //路由懒加载
  },
  {
    path: '/detail/:id',
    name: 'detail',
    component:() => import('../view/Detail.vue')
  }
]
const router = createRouter({
  history:createWebHistory(),
  routes
})
export default router;

//App.vue
<template>
    <router-view></router-view>
</template>

//Home.vue
<template>
  <div>
    <h1>home</h1>
    <router-link to="/list">列表</router-link>
  </div>
</template>

//List.vue
<template>
   <div>
     <h1>list</h1>
     <ul>
        <li><router-link to="/detail/a">a</router-link></li>
        <li><router-link to="/detail/b">b</router-link></li>
        <li><router-link to="/detail/c">c</router-link></li>
     </ul>
  </div>
</template>
<script setup>
import { onBeforeRouteLeave,onBeforeRouteUpdate} from 'vue-router'
  //路由改变触发
  onBeforeRouteLeave((to,from)=>{
    const answer = window.confirm('真的要离开嘛?')
    if(!answer) return false
  })

  //动态路由参数改变触发
  onBeforeRouteUpdate((to, from) => {
    console.log('changed.')
  })
</script>

//Detail.vue
<template>
  <div>
    <h1>detail</h1>
    <button @click="goHome">回首页</button>
  </div>
</template>
<script setup>
import {useRoute,useRouter} from "vue-router"
    const route = useRoute()
    const router = useRouter()
    const goHome = ()=>{
      router.push({
        name:'home',
        query:{
          id:route.params.id    //回到home的时候带参
        }
      })
    }
    console.log(route.params)
</script>

pinia+vue3+vite

在Vue3的官网中,vuex已经不被推荐了,改而推荐使用的全局状态管理变成了pinia

那么pinia如何使用呢

//productStore.js
import {defineStore} from 'pinia';
import catchDataApi from '../data/api'

export const useProductStore = defineStore({
  //通过id获取state
  id:"productStore",
  
  //state存储
  state:()=>({
    products:[]
  }),
  
  //获取数据
  actions:{
    async loadData(){
      try{
        const data = await catchDataApi()
        this.products = data
      }catch(error){

      }
    }
  }
})


//cartStore.js
import {defineStore,storeToRefs} from 'pinia'
import { useProductStore } from './productStore'

export const useCartStore = defineStore({
  id:"cartStore",

  state:()=>({
    cartList:[]
  }),
  actions:{
  //添加产品到购物车
    addToCart(product){
      // console.log(product)
      const p = this.cartList.find((item)=>{
        return item.id === product.id
      })
	//两个!!直接将p转换成布尔值
      if(!!p){
        p.quantity++
      }else {
        this.cartList.push({
          ...product,
          quantity:1
        })
      }

      //当点击放入购物车,这个商品的库存就要减少
      const productStore = useProductStore()
      const { products } = storeToRefs(productStore)
      const p2 = products.value.find((item)=>{
        return item.id === product.id
      })
      p2.inventory --
    }
  },
  //过滤
  getters:{
 	//计算总价格
    tootlePrice(){
      return this.cartList.reduce((price,item)=>{
        return price + item.price*item.quantity
      },0)
    }
  }
})


//入口文件 main.js 不要忘了引入pinia
import { createPinia } from 'pinia'
app.use(createPinia())

怎么在页面中使用呢

//Cart.vue
//setup 是Vue3的语法糖   不了解的可以往上看看Vue3的生命周期
<script setup>
import {storeToRefs} from 'pinia'
import {useCartStore} from '../stores/cartStore'
//这里的 cartStore 是通过id名找到
const cartStore = useCartStore()
//将store通过storeToRefs转换才能取到store内的state值和方法
const {cartList,tootlePrice} =  storeToRefs(cartStore)
</script>

<template>
  <div>
    <h1>购物车</h1>
    <ul>
      <li v-for="product in cartList" :key="product.id">
        {{product.name}} -- count:{{product.quantity}}  总价:{{product.quantity*product.price}}
      </li>
    </ul>
    <h2>总计:{{tootlePrice}}</h2>
  </div>
</template>
<style scoped>
</style>

//Product.vue
<script setup>
import {storeToRefs} from 'pinia'
import {useCartStore} from "../stores/cartStore"
import {useProductStore} from '../stores/productStore'
const productStore = useProductStore()
const cartStore = useCartStore()
const {products} = storeToRefs(productStore)
const { addToCart } = cartStore
productStore.loadData()
</script>

<template>
  <div>
    <h1>产品列表</h1>
    <ul>
      <li v-for="product in products" :key="product.id">
        {{product.name}} - {{product.price}}--- 库存:{{product.inventory}}
        <button @click="addToCart(product)" :disabled="product.inventory <= 0">加入购物车</button>
      </li>
    </ul>
  </div>
</template>

<style scoped>

</style>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值