• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

【vue进阶之旅】vue3.2-setup语法糖、组合式API、状态库Pinia

武飞扬头像
juejin
帮助719

前言:

vue3.0都没学完,vue3.2又来了,你还学得动吗?(手动滑稽)

vue3.2与vue3.0在语法上存在以下区别:
vue3.0版本:变量和方法必须return出来才能使用。
vue3.2版本:只需要在script标签上加上setup属性,不需要再把变量和方法return出去,代码更加简洁。

本文将重点将重点介绍vue3.2版本的语法,如果对vue3还没有完全接触过的小伙伴,可以先移步去我这篇文章热热身哦。

创建项目:

vite脚手架创建项目: vue3 vite2 ts

npm create vite@latest

一.组件结构

<script setup lang="ts">
//直接在script标签上添加setup属性
//...

</script>

<template>
//vue2只支持一个根节点,vue3支持多个根节点
//...

</template>

<style scoped>
// 支持CSS变量注入v-bind(color)
//...

</style>

二.data

<script setup lang="ts">
import { reactive, ref, toRefs } from 'vue'

  // ref声明响应式数据,用于声明基本数据类型
  const name = ref('东东吖')
  // 修改
  name.value = '小鲁班'

  // reactive声明响应式数据,用于声明引用数据类型
  const state = reactive({
    age: 24,
    sex: '男'
  })
  // 修改
  state.age = 24
  
  // 使用toRefs解构  template可直接使用{{name}}、{{sex}}
  const {age, sex} = toRefs(state)
  
</script>

<template>
  <h1>{{ name }}</h1>
  <h1>{{ state.age }}</h1>
  <h1>{{ sex }}</h1>
</template>

<style scoped>

</style>

三.method

<script setup lang="ts">
import { reactive, toRefs } from 'vue'

  // reactive声明响应式数据,用于声明引用数据类型
  const state = reactive({
    age: 24,
    sex: '男'
  })
  // 使用toRefs解构  template可直接使用{{name}}、{{sex}}
  const {age, sex} = toRefs(state)

   // 声明method方法
  const changeAge =()=>{
    state.age =1
  }
  
</script>

<template>
  <h1>{{ age }}</h1>
  
  <!-- 调用方法 
  <button @click="changeAge()">点我</button>
</template>

<style scoped>

</style>

四.computed

<script setup lang="ts">
import { ref,computed } from 'vue'

 // ref声明响应式数据,用于声明基本数据类型
  const count = ref(1)

  //computed获取双倍count' 
  const doubleCount =computed(()=>{
    return count.value*2
  })

  
</script>

<template>
  <h1>{{ count }}</h1>
  <h1>{{doubleCount}}</h1>

</template>

<style scoped>

</style>

五.父传子

//父组件:

<script setup lang="ts">
// 引入子组件(组件自动注册)
import HelloWorld from './components/HelloWorld.vue'
</script>

<template>
  <HelloWorld msg="东东吖" />
</template>

<style>

</style>
//子组件:

<script setup lang="ts">
<script setup lang="ts">
// import { defineProps } from 'vue'
// defineProps在<script setup>中自动可用,无需导入
// 需在.eslintrc.js文件中【globals】下配置【defineProps: true】

// 声明props
const props = defineProps({
  msg: {
    type: String,
    default: "",
  },
});
</script>

<template>

{{props.msg}}

<!-- 可省略props 
  <h1>{{ msg }}</h1>

</template>

<style scoped>
</style>



六.子传父

//父组件:

<script setup lang="ts">
import { reactive } from "vue";
// 引入子组件(组件自动注册)
import HelloWorld from "./components/HelloWorld.vue";

const state = reactive({
  msg: "我是父组件原本的值",
});

const changeMsg = (val) => {
  state.msg = val;
};
</script>

<template>
  <HelloWorld :msg='state.msg' @changeMsg="changeMsg" />
</template>

<style>
</style>
//子组件:

<script setup lang="ts">
import { defineEmits } from "vue";

// import { defineEmits, defineProps } from 'vue'
// defineEmits和defineProps在<script setup>中自动可用,无需导入
// 需在.eslintrc.js文件中【globals】下配置【defineEmits: true】、【defineProps: true】

// 声明props
const props = defineProps({
  msg: {
    type: String,
    default: "",
  },
});

// 声明emit
const emit = defineEmits(["changeMsg"]);

// 声明子传父事件
const changeMsg = () => {
  emit("changeMsg", "我是子组件传过来的值");
};
</script>

<template>

{{props.msg}}

<!-- 可省略props 
  <h1>{{ msg }}</h1>


<!-- 调用子传父事件 
  <button @click="changeMsg">点我</button>

</template>

<style scoped>
</style>


七.原型链绑定和组件使用

//main.ts

// 创建vue实例
const app=createApp(App)
// 获取原型
const prototype = app.config.globalProperties
// 绑定参数
prototype.name = '我是挂载在全局上的属性'
//组件内获取使用

//引入
import { getCurrentInstance } from "vue";
// 获取原型
const { proxy } = getCurrentInstance();
// 输出
console.log(proxy.name);

八.任意组件通信mitt.js

  • Vue2.x使用EventBus进行组件通信,而Vue3.x推荐使用mitt.js。

九.双向绑定v-model

//父组件:

<script setup lang="ts">
import { reactive } from "vue";
// 引入子组件(组件自动注册)
import HelloWorld from "./components/HelloWorld.vue";

const state = reactive({
  msg: "我是父组件原本的值",
  age:24
});

</script>

<template>
<div>父组件:</div>
<div>{{state.msg}}</div>
<input type="text" v-model="state.msg">

  <HelloWorld
   v-model:msg='state.msg'
   v-model:age="state.age"
   />
</template>

<style>
</style>

//子组件:

<script setup lang="ts">
import { defineEmits } from "vue";

// import { defineEmits, defineProps } from 'vue'
// defineEmits和defineProps在<script setup>中自动可用,无需导入
// 需在.eslintrc.js文件中【globals】下配置【defineEmits: true】、【defineProps: true】

// 声明props
const props = defineProps({
  msg: {
    type: String,
    default: "",
  },
  age: {
    type: Number,
    default: 0,
  },
});

// 声明emit
const emit = defineEmits(["updata:msg", "updata:age"]);
</script>

<template>
<div>子组件:</div>
<input type="text" v-model="msg">

<!-- 可省略props 
  <h1>{{ msg }}</h1>
  <h1>{{age}}</h1>

</template>

<style scoped>
</style>


十.nextTick

<script setup lang="ts">
import { reactive ,nextTick } from "vue";
// 引入子组件(组件自动注册)
import HelloWorld from "./components/HelloWorld.vue";

const state = reactive({
  msg: "我是父组件原本的值",
});

 // 调用nextTick
nextTick(()=>{  
 console.log("nextTick执行了...");
 state.msg="我是nextTick执行后的值"
 
})

</script>

<template>
  <HelloWorld
   v-model:msg='state.msg'
   />
</template>

<style>
</style>
子组件:
<script setup lang="ts">
import { defineProps,nextTick } from "vue";
// 声明props
const props = defineProps({
  msg: {
    type: String,
    default: "",
  },
});

 // 调用nextTick
nextTick(()=>{  
 console.log("nextTick执行了...");
 
})


</script>

<template>
  <h1>{{ msg }}</h1>
</template>

<style scoped>
</style>

十一.插槽

//子组件:

<script setup lang="ts">
  import { useSlots, reactive } from 'vue'
  const state = reactive({
    name: '东东吖',
    age: '25岁'
  })
  
  const slots = useSlots()
  // 匿名插槽使用情况
  const defaultSlot = reactive(slots.default && slots.default().length)
  console.log("defaultSlot",defaultSlot) // 1
  // 具名插槽使用情况
  const titleSlot = reactive(slots.title && slots.title().length)
  console.log("titleSlot",titleSlot) // 3
</script>

<template>
<!-- 匿名插槽 
  <slot/>
  <!-- 具名插槽 
  <slot name='title'/>
   <!-- 作用域插槽 
  <slot name="footer" :scope="state" />


</template>

<style scoped>
</style>

//父组件:

<script setup lang="ts">
import HelloWorld from "./components/HelloWorld.vue";

</script>

<template>
<HelloWorld>
   <!-- 匿名插槽 
    <span>我是默认插槽</span>
    <!-- / 具名插槽 
    <template #title>
      <h1>我是具名插槽1</h1>
      <h1>我是具名插槽2</h1>
      <h1>我是具名插槽3</h1>
    </template>
    <!-- 作用域插槽 
    <template #footer="{ scope }">
      <footer>作用域插槽——姓名:{{ scope.name }},年龄{{ scope.age }}</footer>
    </template>
  </HelloWorld>
</template>

<style>
</style>

//页面展示情况:

我是默认插槽

# 我是具名插槽1

# 我是具名插槽2

# 我是具名插槽3

作用域插槽——姓名:东东吖,年龄25岁

十二.路由useRoute和useRouter

//新建router/index.ts

import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router';
const routes: Array<RouteRecordRaw> = [
  { 
    path: '/',
    redirect: '/home'
  },
  { 
    path: '/home',
    name: 'home',
    component: () => import('../pages/home/Index.vue'),
    meta: {
      showFooter: true,
      requireAuth: false,
    }
  },
  { 
    path: '/about',
    name: 'about',
    component: () => import( '../pages/about/Index.vue'),
    meta: {
      showFooter: true,
      requireAuth: false,
    }
  },


];

const router = createRouter({
  history: createWebHashHistory(),
  routes,
});

export default router;
//在main.ts将路由router注册
import { createApp } from 'vue'
import App from './App.vue'
import router from './router/index.ts'


const app=createApp(App)
app.use(router).mount('#app')


//在页面打印结果

import { useRouter, useRoute } from "vue-router";

// 必须先声明调用
const router = useRouter();
const route = useRoute();

// 路由信息
console.log("router", router);
console.log("route",route);

十三.路由守卫

// 路由守卫

router.beforeEach((to, from, next) => {
 console.log("to",to);
 console.log("from",from);
 next()
})

十四.生命周期

选项式 API 的生命周期选项和组合式 API 之间的映射

  • beforeCreate -> 使用 setup()
  • created -> 使用 setup()
  • beforeMount -> onBeforeMount
  • mounted -> onMounted
  • beforeUpdate -> onBeforeUpdate
  • updated -> onUpdated
  • beforeUnmount -> onBeforeUnmount
  • unmounted -> onUnmounted
  • errorCaptured -> onErrorCaptured
  • renderTracked -> onRenderTracked
  • renderTriggered -> onRenderTriggered
  • activated -> onActivated
  • deactivated -> onDeactivated

十五.store

  • vuex

Vue3 中的Vuex不再提供辅助函数写法,想要学习vuex的伙伴可以移步我这篇文章。vue进阶之路:vuex五大核心概念,看完这篇文章就够了。

  • Pinia、 vue3更加推荐的是pinia,2021年11月24日,尤大在 Twitter 上宣布:Pinia 正式成为 Vue 官方的状态库,意味着 Pinia 就是 Vuex 5,让我们全面拥抱pinia吧!

基础使用流程

//下载pinia

npm install pinia -S
//main.ts

import { createApp } from 'vue'
import App from './App.vue'
import router from './router/index.ts'

// 引入pinia
import { createPinia } from 'pinia'

// 创建vue实例
const app=createApp(App)

// 创建 Pinia 实例
const pinia = createPinia()

// 注册挂载到vue实列
app.use(router).use(pinia).mount('#app')

// store/index.ts

import { defineStore } from 'pinia'
// 1. 定义容器、导出容器
// 参数1:容器的ID,必须是唯一的,后面Pinia会把所有的容器挂载到根容器
// 参数2:一些选项对象,也就是state、getter和action
// 返回值:一个函数,调用即可得到容器实例

export const useMainStore =  defineStore('main',{
    // 类似于Vue2组件中的data,用于存储全局状态数据,但有两个要求
    // 1. 必须是函数,目的是为了在服务端渲染的时候避免交叉请求导致的数据状态污染
    // 2. 必须是箭头函数,这样是为了更好的 TS 类型推导
    state:()=>{
        return {
            info:"hello,东东吖,我是Pinia"
        }
    },
    getters:{},
    actions:{}
})
//组件内使用

<template>
    <div>
   <h1>{{ mainStore.info}}</h1>
    </div>
</template>

<script setup lang="ts">
import { useMainStore } from "../../store/index.ts";
const mainStore = useMainStore();


</script>

<style scoped>

</style>

state 中数据的解构访问

// store/index.ts
import { defineStore } from 'pinia'
// 1. 定义容器、导出容器
// 参数1:容器的ID,必须是唯一的,后面Pinia会把所有的容器挂载到根容器
// 参数2:一些选项对象,也就是state、getter和action
// 返回值:一个函数,调用即可得到容器实例

export const useMainStore =  defineStore('main',{
    // 类似于Vue2组件中的data,用于存储全局状态数据,但有两个要求
    // 1. 必须是函数,目的是为了在服务端渲染的时候避免交叉请求导致的数据状态污染
    // 2. 必须是箭头函数,这样是为了更好的 TS 类型推导
    state:()=>{
        return {
            info:"hello,东东吖,我是Pinia",
            count:10
        }
    },
    getters:{},
    actions:{}
})
//组件内使用

<template>
  <h1>{{ mainStore.count }}</h1>
  <h1>{{ mainStore.info }}</h1>
  <hr />
  <h1>{{ count }}</h1>
  <h1>{{ info }}</h1>
  <p>
    <button @click="alertData">修改数据count</button>
  </p>
</template>

<script lang="ts" setup>
import { toRefs } from 'vue'
import { storeToRefs } from 'pinia'
import { useMainStore } from "../../store";
const mainStore = useMainStore();
// 解构数据,但是得到的数据是不具有响应式的,只是一次性的
// 相当于仅仅只是...mainStore而已,只是做了reactive处理,并没有做toRefs
// const { count, info } = useMainStore();
// 解决方法:
// 1. 通过使用toRefs函数,因为前面所说相当于是通过reactive处理,因此可以
// const { count, info } = toRefs(mainStore);
// 2. 通过pinia中提供的storeToRefs方法来解决,推荐使用
const { count, info } = storeToRefs(mainStore);
const alertData = () => {
  mainStore.count  = 10
}
</script>

<style>
</style>

state 中数据的修改方式(actions和组件中)

// 一般的修改

<template>
  <h1>{{ mainStore.count }}</h1>
  <h1>{{ mainStore.info }}</h1>
  <hr />
  <h1>{{ count }}</h1>
  <h1>{{ info }}</h1>
  <p>
    <button @click="alertData">修改数据count</button>
  </p>
</template>

<script lang="ts" setup>
import { toRefs } from 'vue'
import { storeToRefs } from 'pinia'
import { useMainStore } from "../../store";
const mainStore = useMainStore();
// 解构数据,但是得到的数据是不具有响应式的,只是一次性的
// 相当于仅仅只是...mainStore而已,只是做了reactive处理,并没有做toRefs
// const { count, info } = useMainStore();
// 解决方法:
// 1. 通过使用toRefs函数,因为前面所说相当于是通过reactive处理,因此可以
// const { count, info } = toRefs(mainStore);
// 2. 通过pinia中提供的storeToRefs方法来解决,推荐使用
const { count, info } = storeToRefs(mainStore);


const alertData = () => {
  // 方式一:最简单的方法,如下
  // 解构后更改方式
//   count.value  = 10
  // 结构前更改方式
//   mainStore.count  = 10
  // 方式二:若要同时修改多个数据,建议使用$patch来实现批量更新,在内部做了优化
//   mainStore.$patch({
//     count: mainStore.count   1,
//     info: "hello"
//   })
  // 方式三:更好的批量更新方法,通过$patch传递一个函数来实现,这里的state就是useMainStore容器中的state
  mainStore.$patch(state => {
    state.count  = 10
    state.info = "pinia批量更新" state.count
  })
}
</script>

<style>
</style>

//通过actions修改
// store/index.ts

import { defineStore } from 'pinia'
// 1. 定义容器、导出容器
// 参数1:容器的ID,必须是唯一的,后面Pinia会把所有的容器挂载到根容器
// 参数2:一些选项对象,也就是state、getter和action
// 返回值:一个函数,调用即可得到容器实例

export const useMainStore =  defineStore('main',{
    // 类似于Vue2组件中的data,用于存储全局状态数据,但有两个要求
    // 1. 必须是函数,目的是为了在服务端渲染的时候避免交叉请求导致的数据状态污染
    // 2. 必须是箭头函数,这样是为了更好的 TS 类型推导
    state:()=>{
        return {
            info:"hello,东东吖,我是Pinia",
            count:10
        }
    },
    getters:{},
    
// store/index.ts
// 类似于vue2组件的methods,用于封装业务逻辑,修改state
// // 注意:不能使用箭头函数来定义actions,因为箭头函数绑定外部的this
actions:{
    changeState (){
        this.count  = 10
        this.info = "actions修改数据"
    },
    changeStates (num:number){
        this.count  = num   2
        this.info = "actions修改数据"
    }
}

})

//组件内使用

<template>
  <h1>{{ mainStore.count }}</h1>
  <h1>{{ mainStore.info }}</h1>
  <hr />
  <h1>{{ count }}</h1>
  <h1>{{ info }}</h1>
  <p>
    <button @click="alertData">修改数据count</button>
  </p>
</template>

<script lang="ts" setup>
import { toRefs } from 'vue'
import { storeToRefs } from 'pinia'
import { useMainStore } from "../../store";
const mainStore = useMainStore();
// 解构数据,但是得到的数据是不具有响应式的,只是一次性的
// 相当于仅仅只是...mainStore而已,只是做了reactive处理,并没有做toRefs
// const { count, info } = useMainStore();
// 解决方法:
// 1. 通过使用toRefs函数,因为前面所说相当于是通过reactive处理,因此可以
// const { count, info } = toRefs(mainStore);
// 2. 通过pinia中提供的storeToRefs方法来解决,推荐使用
const { count, info } = storeToRefs(mainStore);

const alertData = () => {
  // 方式四:通过 actions 来修改数据
  mainStore.changeState()
  mainStore.changeStates(10)
}

</script>

<style>
</style>

getters 的使用

// store/index.ts
import { defineStore } from 'pinia'
// 1. 定义容器、导出容器
// 参数1:容器的ID,必须是唯一的,后面Pinia会把所有的容器挂载到根容器
// 参数2:一些选项对象,也就是state、getter和action
// 返回值:一个函数,调用即可得到容器实例

export const useMainStore =  defineStore('main',{
    // 类似于Vue2组件中的data,用于存储全局状态数据,但有两个要求
    // 1. 必须是函数,目的是为了在服务端渲染的时候避免交叉请求导致的数据状态污染
    // 2. 必须是箭头函数,这样是为了更好的 TS 类型推导
    state:()=>{
        return {
            info:"hello,东东吖,我是Pinia",
            count:10
        }
    },

    // 类似于组件的computed,用来封装计算属性,具有缓存的功能
    getters:{
        // 函数接收一个可选参数:state状态对象
       count10(state){
           return state.count  = 10
       },
       count20(state){
           return this.count  = 20
       },
       // 若使用this.count,则必须指明返回数据的类型
       count11():number{
           return this.count  = 11
       }
   },
    
// store/index.ts
// 类似于vue2组件的methods,用于封装业务逻辑,修改state
// // 注意:不能使用箭头函数来定义actions,因为箭头函数绑定外部的this
actions:{
    changeState (){
        this.count  = 10
        this.info = "actions修改数据"
    },
    changeStates (num:number){
        this.count  = num   2
        this.info = "actions修改数据"
    }
}

})

//组件内使用

<template>
  <h1>{{ mainStore.count }}</h1>
  <h1>{{ mainStore.info }}</h1>
  <hr />
  <h1>{{ count }}</h1>
  <h1>{{ info }}</h1>
  
 <h1>{{ mainStore.count10 }}</h1>
 
  <p>
    <button @click="alertData">修改数据count</button>
  </p>
</template>

<script lang="ts" setup>
import { toRefs } from 'vue'
import { storeToRefs } from 'pinia'
import { useMainStore } from "../../store";
const mainStore = useMainStore();
// 解构数据,但是得到的数据是不具有响应式的,只是一次性的
// 相当于仅仅只是...mainStore而已,只是做了reactive处理,并没有做toRefs
// const { count, info } = useMainStore();
// 解决方法:
// 1. 通过使用toRefs函数,因为前面所说相当于是通过reactive处理,因此可以
// const { count, info } = toRefs(mainStore);
// 2. 通过pinia中提供的storeToRefs方法来解决,推荐使用
const { count, info } = storeToRefs(mainStore);

const alertData = () => {
  // 方式四:通过 actions 来修改数据
  mainStore.changeState()
  mainStore.changeStates(10)
}

</script>

<style>
</style>

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanfbacg
系列文章
更多 icon
同类精品
更多 icon
继续加载