vue3.2-setup语法糖、组合式API、状态库Pinia归纳总结

607 阅读3分钟

创建项目:

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

npm create vite@latest

一.页面组件结构

<template>
    home
</template><!-- 在script标签上添加setup属性 -->
<script setup lang="ts"></script><style scoped>
</style>
  

二. 页面属性方法

<template>
  <h1>{{ name }}</h1>
  <h1>{{ state.age }}</h1>
  <h1>{{ sex }}</h1>
</template>
     
<script setup lang="ts">
import { reactive, ref, toRefs } from "vue";
// ref声明响应式数据,用于声明基本数据类型
const name = ref("王五");
// 修改 name 值要是用.value
name.value = "张三";
​
// reactive声明响应式数据,用于声明引用数据类型
const state = reactive({
  age: 18,
  sex: "男",
});
// 修改 state.age 值
state.age = 24;
​
// 使用toRefs解构  template可直接使用{{name}}、{{sex}}
const { age, sex } = toRefs(state);
</script><style scoped></style>

三.定义method

<template>
  <h1>{{ age }}</h1>
  <!-- 调用方法 -->
  <button @click="handleClick"></button>
</template>
     
<script setup lang="ts">
import { reactive,toRefs } from "vue";
​
// reactive声明响应式数据,用于声明引用数据类型
const state = reactive({
  age: 18,
  sex: "男",
});
​
// 使用toRefs解构  template可直接使用{{name}}、{{sex}}
const { age, sex } = toRefs(state);
​
// 声明点击事件
const handleClick = () => { 
  state.age++;
}
​
</script><style scoped></style>

四.computed (计算属性)

<template>
  <h1>{{ count }}</h1>
  <h1>{{doubleCount}}</h1>
  <button @click="handleClick()">{{count}}</button>
</template><script setup lang="ts">
import { ref, computed } from 'vue'// ref声明响应式数据,用于声明基本数据类型
const count = ref(11)
​
//computed 获取双倍 count 值 
const doubleCount = computed(() => {
  return count.value * 2
})
​
const handleClick = () => {
  count.value++
}
</script>
   
<style scoped></style>

五.父传子

parent.vue

<template>
  <Child :msg="fmsg"></Child>
</template><script setup lang="ts">
  // 引入子组件(组件自动注册)
  import Child from './child.vue';
  const fmsg = '父组件传递的值'
</script><style scoped></style>

child.vue

<template>
  {{props.msg}}
  <!-- 可省略props -->
  <h1>{{ msg }}</h1>
</template>
 
<script setup lang="ts">
// defineProps 和 defineEmits 都是只能在 <script setup> 中使用的编译器宏。他们不需要导入,且会随着 <script setup> 的处理过程一同被编译掉。
  
// 声明props
const props = defineProps({
  msg: {
    type: String,
    default: "",
  },
});
</script><style scoped></style>

六.子传父

parent.vue

<template>
  <Child :msg="state.msg" @changeMag="changeMag"></Child>
</template><script setup lang="ts">
import { reactive } from 'vue'
// 引入子组件(组件自动注册)
import Child from './child.vue';
​
const state = reactive({
  msg: '父组件原值'
})
​
const changeMag = (value: any) => {
  state.msg = value
}
</script><style scoped></style>

child.vue

<template>
  {{props.msg}}
  <!-- 可省略props -->
  <h1>{{ msg }}</h1>
  <!-- 调用子传父事件 -->
  <button @click="handleClick">Click Me</button>
</template>
 
<script setup lang="ts">
// defineProps 和 defineEmits 都是只能在 <script setup> 中使用的编译器宏。他们不需要导入,且会随着 <script setup> 的处理过程一同被编译掉。

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

// 声明 emit 
const emit = defineEmits(['changeMag'])

const handleClick = () => {
  emit('changeMag', '我是子组件传过来的值')
}
</script>

<style scoped>

</style>

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

main.ts

// 创建vue实例
const app = createApp(App);
app.mount("#app");
// 获取原型
const prototype = app.config.globalProperties;
// 绑定参数
prototype.$name = "我是挂载在全局上的属性";

组件内使用

<template>
  <!-- 方式1:在template模板中使用 -->
  {{$name}}
</template>
<script setup lang="ts">
import { getCurrentInstance } from "vue";

// 方式2:在setup中使用
// const cns = getCurrentInstance()
// console.log(cns.appContext.config.globalProperties.$name)

// 方式3:在setup中使用
const { proxy } = getCurrentInstance() as any
console.log(proxy.$name)

</script>

<style scoped>

</style>

八.Vue3.x使用mitt.js进行组件通信

安装

npm install --save mitt

官网地址

mitt - npm (npmjs.com)

九.v-model父子组件双向数据绑定

parent.vue

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

<script setup lang="ts">
import { reactive } from "vue";
// 引入子组件(组件自动注册)
import Child from "./child.vue";
const state = reactive({
  msg: "我是父组件原本的值",
  age: 11
});
</script>

<style scoped>

</style>
 

child.vue

<template>
  <div>子组件:</div>
  <input type="text" v-model="msg" @input="changeMsg">
  <!-- 可省略props -->
  <h1>msg:{{ msg }}</h1>
</template>

<script setup lang="ts">
// 声明props
const props = defineProps({
  msg: {
    type: String,
    default: "",
  },
  age: {
    type: Number,
    default: 0,
  },
});

// 声明emit
const emit = defineEmits(["update:msg", "update:age"]);

const changeMsg = () => { 
  console.log(props.msg);
  emit('update:msg',props.msg);
}

</script>

<style scoped>

</style>
 

十.nextTick

parent.vue

<template>
  <Child />
</template>

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

nextTick(() => {
  console.log('父组件调用了:nextTick');
})

nextTick(() => {
  console.log('父组件调用了:nextTick-1');
})

nextTick(() => {
  console.log('父组件调用了:nextTick-2');
})
</script>

<style scoped>

</style>
 

child.vue

<template>
</template>

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

nextTick(() => {
  console.log('子组调用了:nextTick');
})

nextTick(() => {
  console.log('子组调用了:nextTick-1');
})

nextTick(() => {
  console.log('子组调用了:nextTick-2');
})
</script>

<style scoped>

</style>
 

十一.插槽

parent.vue

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

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

<style scoped>

</style>
 

child.vue

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

<script setup lang="ts">
import { reactive, useSlots } from "vue";

const state = reactive({
  name: '王五',
  age: 11
});

const slots = useSlots()

//判断<slot/>是否有传值
const slotDefault = !!useSlots().default;
//判断<slot name="test"/>是否有传值
const slotTest = !!useSlots().test;

// 匿名插槽使用情况,判断 <slot/>是否有传值
const defaultSlot = reactive(slots.default && slots.default().length)
console.log('defaultSlot', defaultSlot) // 1

// 具名插槽使用情况,判断 <slot name='title' />是否有传值
const titleSlot = reactive(slots.title && slots.title().length)
console.log('titleSlot', titleSlot) // 6

</script>

<style scoped>

</style>
 

十二.路由useRoute和useRouter

安装

npm install vue-router@4
// yarn add vue-router@4

安装 | Vue Router (vuejs.org)

新建router/index.ts

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

十四.生命周期

vue2和vue3对比

  • 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

十五.Pinia

1:基础使用

安装

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

export const useMainStore = defineStore("main", {
  state: () => {
    return {
      info: "hello, My 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>

2:访问state 中的数据

/store/index.ts

import { defineStore } from "pinia";

export const useMainStore = defineStore("main", {
  state: () => {
    return {
      info: "Hello, My Pinia",
      id:11,
      count:0,
      depId:6,
      userName: "admin"
    };
  },
  getters: {},
  actions: {},
});

组件内使用

<template>
  <h1>{{ mainStore.count }}</h1>
  <h1>{{ mainStore.info }}</h1>
  <hr />
  <h1>{{ count }}</h1>
  <h1>{{ info }}</h1>
  <p>
    <button @click="refteshData">修改数据count</button>
  </p>
  <br>
  <h1>count1:{{ count1 }}</h1>
  <h1>info1:{{ info1 }}</h1>
</template>
 
<script lang="ts" setup>
import { toRefs } from 'vue'
import { storeToRefs } from 'pinia'
import { useMainStore } from "../../store";
const mainStore = useMainStore();
// **** 注意:直接解构处出来的数据,非响应式的
// const { count: count1, info: info1 } = mainStore;

// 转为响应式方法
// 1:使用 toRefs 可以将一个响应式 reactive 对象的所有原始属性转换为响应式的 ref 属性
const { count: count1, info: info1 } = toRefs(mainStore);
// 2. 通过pinia中提供的storeToRefs方法来解决非响应式问题
const { count, info } = storeToRefs(mainStore);

const refteshData = () => {
  mainStore.count += 10
}
</script>
 
<style scoped>

</style>
 

2:修改state 中的数据

使用$patch修改数据

<template>
  <h1>{{ mainStore.count }}</h1>
  <h1>{{ mainStore.info }}</h1>
  <hr />
  <h1>{{ count }}</h1>
  <h1>{{ info }}</h1>
  <p>
    <button @click="refteshData">修改数据count</button>
  </p>
  <br>
  <h1>count1:{{ count1 }}</h1>
  <h1>info1:{{ info1 }}</h1>
</template>
 
<script lang="ts" setup>
import { toRefs } from 'vue'
import { storeToRefs } from 'pinia'
import { useMainStore } from "../../store";
const mainStore = useMainStore();
// **** 注意:直接解构处出来的数据,非响应式的
// const { count: count1, info: info1 } = mainStore;

// 转为响应式方法
// 1:使用 toRefs 可以将一个响应式 reactive 对象的所有原始属性转换为响应式的 ref 属性
const { count: count1, info: info1 } = toRefs(mainStore);
// 2. 通过pinia中提供的storeToRefs方法来解决非响应式问题
const { count, info } = storeToRefs(mainStore);

const refteshData = () => {
  // 方式一: 不推荐
  // 解构后更改方式
  //   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 scoped>

</style>
 

使用actions中的方法修改数据 (推荐)

store/index.ts

import { defineStore } from "pinia";

export const useMainStore = defineStore("main", {
  state: () => {
    return {
      info: "Hello, My Pinia",
      id: 11,
      count: 0,
      depId: 6,
      userName: "admin",
    };
  },
  getters: {},
  // Actions 相当于组件中的 methods,用来修改 state 数据
  actions: {
    changeState() {
      this.count += 11;
      this.info = "actions修改数据";
    },
    changeStates(num: number) {
      this.count += num + 1;
      this.info = "actions修改数据";
    },
  },
});

页面使用

<template>
  <h1>{{ mainStore.count }}</h1>
  <h1>{{ mainStore.info }}</h1>
  <hr />
  <h1>{{ count }}</h1>
  <h1>{{ info }}</h1>
  <p>
    <button @click="refteshData">修改数据count</button>
  </p>
  <br>
  <h1>count1:{{ count1 }}</h1>
  <h1>info1:{{ info1 }}</h1>
</template>
 
<script lang="ts" setup>
import { toRefs } from 'vue'
import { storeToRefs } from 'pinia'
import { useMainStore } from "../../store";
const mainStore = useMainStore();
// **** 注意:直接解构处出来的数据,非响应式的
// const { count: count1, info: info1 } = mainStore;

// 转为响应式方法
// 1:使用 toRefs 可以将一个响应式 reactive 对象的所有原始属性转换为响应式的 ref 属性
const { count: count1, info: info1 } = toRefs(mainStore);
// 2. 通过pinia中提供的storeToRefs方法来解决非响应式问题
const { count, info } = storeToRefs(mainStore);

const refteshData = () => {
  // 调用 actions 修改数据
  mainStore.changeState()
  mainStore.changeStates(10)
}
</script>
 
<style scoped>

</style>
 

3:getters 的使用

/store/index.ts

import { defineStore } from "pinia";

export const useMainStore = defineStore("main", {
  state: () => {
    return {
      info: "Hello, My Pinia",
      id: 11,
      count: 0,
      depId: 6,
      userName: "admin",
    };
  },
  // 类似于组件的computed,用来封装计算属性,具有缓存的功能
  getters: {
    // 函数接收一个可选参数:state状态对象
    count10(state) {
      return (state.count += 10);
    },
    // 若使用this.count,则必须指明返回数据的类型
    count11(): number {
      return (this.count += 11);
    },
    count12(state) {
      return (this.count += 12);
    },
  },
  // Actions 相当于组件中的 methods,用来修改 state 数据
  actions: {
    changeState() {
      this.count += 11;
      this.info = "actions修改数据";
    },
    changeStates(num: number) {
      this.count += num + 1;
      this.info = "actions修改数据";
    },
  },
});

页面使用

<template>
  <h1>{{ mainStore.count }}</h1>
  <p>
    <button @click="getGetters">获取getters</button>
  </p>
</template>
 
<script lang="ts" setup>
import { ref } from 'vue'
import { useMainStore } from "../../store";
const mainStore = useMainStore() as any;
const count_10 = ref('');
const count_11 = ref('');
const count_12 = ref('');

const getGetters = () => {
  count_10.value = mainStore.count10;
  count_11.value = mainStore.count11;
  count_12.value = mainStore.count12;
}
</script>
 
<style scoped>

</style>
 

\