vue3 语法糖

目录

setup 语法糖

ref reactive toRef toRefs

 watchEffect

watch

1、监听基础类型

2、监听复杂类型

3、组合监听 

computed计算属性的使用

传值

props父子传值

emit子父传值

祖孙传值provide inject

获取子组件ref变量和defineExpose暴露

路由useRoute useRouter

 pinia

安装与使用

action 的使用

 getters 的使用

storeToRefs 的使用

Pinia 模块化 


vue3 的生命周期:

setup、onBeforeMount、onMounted、onBeforeUpdate、onUpdated、onBeforeUnmount、onUnmounted。

setup 语法糖

vue3.2版本以后可以使用语法糖,不用导出、不用写return 、组件也不用注册,直接引入使用

普通写法 

<template>
  
</template>
<script>
export default {
  setup() {
    return {}
  }
}
</script>

简写写法 

<template>
 
</template>

<script setup>

</script>

<style scoped>
</style>
<template>
  <HeaderBox></HeaderBox>
</template>
<script setup>
import HeaderBox from "@/components/HeaderBox.vue";
// export default {
// name: "App",
//   components: {
//     HeaderBox,
//   },
// };
</script>

ref reactive toRef toRefs

都是将数据变成响应式数据

若要包裹的数据是对象就推荐使用reactive,其他情况使用ref(简单数据类型)

注意:ref 其实也可以包裹复杂数据类型为响应式数据,一般对于数据类型未确定的情况下推荐使用 ref,例如后端返回的数据。

toRef 转换响应式对象中某个属性为单独响应式数据,转换后的值和之前是关联的

toRefs 转换响应式对象中所有属性为单独响应式数据,转换后的值和之前是相关联的

<template>
  <h2>{{ name }}</h2>
  <button >-</button>
  <input type="text" v-model.number="count">
  <button >+</button>
  <h3>{{num }}</h3>
  <h3>{{con}}</h3>
</template>

<script setup>
import { reactive, toRef, toRefs ,ref} from "vue";

// 声明变量
const state = reactive({
  name: "张三", 
  count: 0,
});

const num = ref(5)

const state1 = reactive({
  con : "叫张三",
  age : 10
})

// 解构赋值
const {count, name} = toRefs(state)
// 这里相当于const count = toRef(state,"count") const 
// name= toRef(state,"name")

const con = toRef(state1,"con")

</script>

<style lang="less" scoped>
</style>

 加入方法methods的写法

<template>
  <h2>{{ name }}</h2>
  <button @click="reduce">-</button>
  <input type="text" v-model.number="count">
  <button >+</button>
  <h3>{{num }}</h3>
  <h3 @click="add">{{con}}</h3>
</template>

<script setup>
import { reactive, toRef, toRefs ,ref} from "vue";

// 声明变量
const state = reactive({
  name: "张三", 
  count: 0,
});

const num = ref(5)

const state1 = reactive({
  con : "叫张三",
  age : 10
})

// 解构赋值 因为是个对象 需要解构赋值才可以在template中直接使用变量名;num不是就可以直接赋值
const {count, name} = toRefs(state)
// 这里相当于const count = toRef(state,"count") const 
// name= toRef(state,"name")
const con = toRef(state1,"con")

// 方法 改变值时需要 变量名.vlaue
const add = () => {
  name.value = "李四";
  count.value++;
  num.value++
  con.value = "叫李四"
};
const reduce = () =>{
  count.value --
}
</script>

<style lang="less" scoped>
</style>

 watchEffect

与watch相似都可以监听一个数据源。
但是watchEffect会在初始化的时候调用一次,相当于设置了immediate: true的watch。

建议在大部分时间里使用watch,避免一些不必要的重复触发。

watchEffect(() => {
  console.log(name.value + con.value);
})

初次调用

点击监听调用

watch

1、监听基础类型

const state = reactive({
  count: 0,
  name: "张三", 
});

// immediate: true 代表进入页面就执行一次,改变值也会触发,不加则只会在改变时触发
watch(name,(newValue, oldValue)=>{
  console.log("name新值:"+newValue,"name旧值:"+oldValue);
},{immediate: true}
)
// name新值:张三 name旧值:undefined  第一次打印
// name新值:李四 name旧值:张三       值改变打印

2、监听复杂类型

  • 监听数据中的某一个属性
watch(()=> state.name,(newValue,oldValue)=>{
 console.log("newValue:"+newValue,"oldValue:"+oldValue); 
})
// newValue:李四 oldValue:张三
  • 监听整个对象
watch(state,(newValue, oldValue)=>{
  console.log(newValue,oldValue);
  }
)

  • 监听对象的子属性 

 只有state 的子属性改变时才会触发watch监听,孙属性、曾孙属性发生变化不会触发,也就是child里的属性

const state = reactive({
  count: 0,
  name: "张三", 
  child:{
    first:"zz",
    last:"123"
  }
});
watch(()=> ({...state}),(newValue,oldValue)=>{
  console.log(newValue,oldValue); 
})

3、组合监听 

  • 同时监听不同的数据
// 同时监听num 和 state的count 的属性
watch([num,()=> state.count],([newName, newAge],[oldName, oldAge]) =>{
  console.log([newName, newAge],[oldName, oldAge]);
})

computed计算属性的使用

// 简写
state.fullName = computed(()=>{
  return name.value + count.value+'岁'
})
console.log(state.fullName); // 张三0岁

传值

props父子传值

子组件

<template>
    <div class="hello">
      <h1>{{ props.name }}--{{ props.con }}</h1>
    </div>
</template>

<script setup>

// 接收从父组件中传过来的属性值

// 声明props defineProps和props 的用法一致

// const props = defineProps(['name','con'])

// 或者 设置默认值定义数据类型
const props = defineProps({
  name:{
    type:String,
    default:"你好"
  },
  con:{
    type:Number,
    default:123
  }
})
</script>

父组件

<template>
  // 引入子组件 绑定属性传给子组件
  <HeaderBox :name = 'name' :con="con"></HeaderBox>
</template>
<script setup>
import {reactive, ref, toRefs} from "vue"
import HeaderBox from "@/components/HeaderBox.vue";

// const name = ref('Hellow')
// const con = ref(456)
// 或者
const state  = reactive({
  name:"Hellow",
  con:456
})
const {name ,con} = toRefs(state)

</script>

emit子父传值

子组件

<template>
  <button @click="change">按钮</button>
</template>

<script setup>

// 将事件自定义名传递给父组件

// 点击按钮方法
const change = () =>{
  emit('changeBtn')   // changeBtn为自定义名
}
const emit = defineEmits(['changeBtn'])
</script>

父组件

<template>
    <HeaderBox @change-btn="changeBtn" ></HeaderBox>
</template>
<script setup>
    import {reactive, ref, toRefs} from "vue"
    import HeaderBox from "@/components/HeaderBox.vue";
    const changeBtn = ()=> {
      console.log("通过方法名接收");
    }
</script>

祖孙传值provide inject

也可用于父子传值使用

provide可以用于向子组件、孙组件传递值或方法

inject 用于获取子组件的值或方法

祖组件

<template>
    <div class="container">
        <h2>App {{ money }}</h2>
        <button @click="money = 1000">发钱</button>
        <hr />
        <Parent />
    </div>
</template>
<script setup>
    import { provide, ref } from 'vue'
    import Parent from './Parent.vue'

       // 提供数据
       const money = ref(100)
       provide('money', money)
       // 提供修改数据的方法
       const changeMoney = (m) => (money.value -= m)
       provide('changeMoney', changeMoney)
</script>

父组件

<template>
    <div>
        Parent
        <hr />
        <Child />
    </div>
</template>

<script setup>
    import Child from './Child.vue'
</script>

孙组件

<template>
    <div>
        Child
        <p>{{ money }}</p>
        <button @click="changeMoney(1)">花 1 块钱</button>
    </div>
</template>

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

    const money = inject('money')
    const changeMoney = inject('changeMoney')
    return { money, changeMoney }
</script>

获取子组件ref变量和defineExpose暴露

在父组件中获取子组件的ref,并改变子组件中的值

子组件

<p>{{ state }}</p>

const state = reactive({
  a: false,
  b: false, 
  c: false, 
})
defineExpose({
  ...toRefs(state)
})

父组件

<HeaderBox ref='refs'></HeaderBox>  // 子组件
<button @click="onClickSetUp">onClickSetUp</button>

const refs = ref('refs')
const onClickSetUp = ({key})=> {
  refs.value.a = true  // 改变子组件的属性值
  console.log(key,refs); // 获取子组件的ref
}

路由useRoute useRouter

useRoute 相当于vue2 的 this.$route

useRouter 相当于 vue2 的 this.$router

import { useRoute ,useRouter} from "vue-router";
const router = useRouter()
const route =  useRoute()

//  product?id=1
console.log("接收显式传参数",route.query); // id:'1'
console.log("当前路由地址:",router.currentRoute.value.name); //product

query 和 params 的区别:

1.首先就是写法得不同,query 得写法是 用 path 来编写传参地址,而 params 得写法是用 name 来编写传参地址,你可以看一下编写路由时候得相关属性,你也可以输出一下 路由对象信息 看一下

2.接收方法不同, 一个用 query 来接收, 一个用 params 接收 ,总结就是谁发得 谁去接收

3.query 在刷新页面得时候参数不会消失,而 params 刷新页面得时候会参数消失,可以考虑本地存储解决

4.query 传得参数都是显示在url 地址栏当中,而 params 传参不会显示在地址栏

params是路由的一部分,必须要在路由后面添加参数名。query是拼接在url后面的参数,没有也没关系。 二者还有点区别,直白的来说query相当于get请求,页面跳转的时候,可以在地址栏看到请求参数,而params相当于post请求,参数不会再地址栏中显示。

path 和 query 搭配使用

name 和 params 搭配使用 

// 路由跳转
// query 写法
router.push({path:'/home',query:{id:"123"}}) // path 路由地址 query 传递参数
route.query.id // 接收参数

// params 写法
router.push({name:'/home',params:{id:"123"}}) // name 路由地址 params 传递参数
route.params.id // 接收参数

 pinia

存储库,允许跨组件/页面共享状态

与vuex的区别:

1、去掉了vuex中的mutation

2、action支持同步和异步,vuex中支持异步的操作

3、更好的支持TS

4、更轻量

核心概念:

  • state:状态
  • actions:修改状态(包括同步和异步)
  • getters:计算属性

安装与使用

1、安装

yarn add pinia
# or
npm i pinia

 2、挂载pinia

main.js

import { createPinia } from 'pinia'
const pinia = createPinia()

createApp(App).use(store).use(router).use(pinia).mount('#app')

3、创建模块

store/counter.js

import { defineStore } from 'pinia'
// 创建 store,命名规则:useXxxxStore
// 参数 1:store 的唯一标识
// 参数 2:对象,可以提供 state actions getters
const useCounterStore = defineStore('counter', {
    // 推荐函数:避免服务端渲染导致的数据状态污染
    // 箭头函数:为了更好的TS推导
    state: () => {
        return {
            count: 10,
        }
    },
    actions: {},
    getters: {},
})

export default useCounterStore

4、在组件中使用

APP.vue

<script setup>
    import useCounterStore from './store/counter'
    const counter = useCounterStore()
</script>
<template>
    <div>{{ counter.count }}</div>
</template>

action 的使用

在 Pinia 中没有 mutations,只有 actions,不管是同步还是异步的代码,都可以在 actions 中完成。

1、在 actions 中提供方法并且修改数据,store/counter.js

import { defineStore } from 'pinia'
const useCounterStore = defineStore('counter', {
    state: () => {
        return {
            count: 0,
        }
    },
    actions: {
        increment() {
            this.count++
        },
        incrementAsync() {
            setTimeout(() => {
                this.count++
            }, 1000)
        },
    },
})

export default useCounterStore

2、在组件中使用

<template>
    <div>counter: {{ counter.count }}</div>
    <button @click="counter.increment">add 1</button>
    <button @click="counter.incrementAsync">async add 1</button>
</template>
<script setup>
    import useCounterStore from './store/counter'
    const counter = useCounterStore()
</script>

 getters 的使用

Pinia 和 Vuex 中的 getters 基本是一样的,也带有缓存的功能。

1、在getters中提供计算属性

import { defineStore } from 'pinia'
const useCounterStore = defineStore('counter', {
    state: () => {
        return {
            count: 0,
        }
    },
    actions: {
        increment() {
            this.count++
        },
        incrementAsync() {
            setTimeout(() => {
                this.count++
            }, 1000)
        },
    },
    getters: {
        double() {
            return this.count * 2
        },
    },
})

export default useCounterStore

2、在组件中使用

<script setup>
    import useCounterStore from './store/counter'
    const counter = useCounterStore()
</script>
<template>
    <div>counter: {{ counter.count }}</div>
    <div>double: {{ counter.double }}</div>
    <button @click="counter.increment">add 1</button>
    <button @click="counter.incrementAsync">async add 1</button>
</template>

storeToRefs 的使用

 如果直接从 Pinia 中解构数据,会丢失响应式,使用 storeToRefs 可以保证解构出来的数据也是响应式的。

<template>
    <div>counter: {{ count }}</div>
    <div>double: {{ double }}</div>
    <button @click="increment">add 1</button>
    <button @click="incrementAsync">async add 1</button>
</template>
<script setup>
    import { storeToRefs } from 'pinia'
    import useCounterStore from './store/counter'
    const counter = useCounterStore()
    // !数据解构会丢失响应式
    // const { count, double } = counter
    // 方法可以正常解构
    const { increment, incrementAsync } = counter
    // 被 storeToRefs 包裹后的 counter,解构出来的数据是响应式的
    const { count, double } = storeToRefs(counter)
</script>

Pinia 模块化 

在复杂项目中,一般来说应该每一个功能模块对应一个 store,最后通过一个根 store 进行整合。

1、user 模块,store/user.js

import { defineStore } from 'pinia'

const useUserStore = defineStore('user', {
    state: () => {
        return {
            name: 'ifer',
            age: 18,
        }
    },
})

export default useUserStore

2、关联 user 和 counter 模块到根 store,store/index.js

import useCounterStore from './counter'
import useUserStore from './user'

export default function useStore() {
    return {
        user: useUserStore(),
        counter: useCounterStore(),
    }
}

 3、在组件中使用,App.vue

<template>
    <div>name: {{ name }}</div>
    <div>age: {{ age }}</div>
</template>
<script setup>
    import { storeToRefs } from 'pinia'
    import useStore from './store'
    const { user } = useStore()
    const { name, age } = storeToRefs(user)
</script>
  • 4
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值