Skip to content

Composition API

1.1、Options API与Composition API的区别

Section titled “1.1、Options API与Composition API的区别”
    • 在对应的属性中编写对应的功能模块;
    • 实现某一个功能时,对应的代码逻辑会被拆分到各个属性中;
    • 组件变得更大、更复杂,同一个功能的逻辑就会被拆分的很分散;
    • 不便于查找和处理单个逻辑关注点;
    • 能将同一个逻辑关注点相关的代码收集在一起;
  • 返回一个可变的响应式对象,该对象作为一个响应式的引用维护着它内部的值;
    Section titled “返回一个可变的响应式对象,该对象作为一个响应式的引用维护着它内部的值;”
    • 内部的值是在 refvalue 属性中被维护的;
  • 可以传入简单数据类型和复杂数据类型;
    Section titled “可以传入简单数据类型和复杂数据类型;”
    • 在模板中引入ref的值时,Vue会自动解包,所以不需要通过 .value的方式来使用;
    • setup 函数内部,它依然是一个 ref引用,所以依然需要使用.value的方式来使用;
    • 对象类型解包后是 proxy 对象;
    • // 使用的时候不需要写.value;使用时候是深层解包
      <h2>当前计数: {{ info.counter }}</h2>
      // 修改的时候需要写.value;修改时候不是深层解包
      <button @click="info.counter.value++">+1</button>
      const counter = ref(0)
      const info = { //普通对象里面套ref
      counter
      }
    • 定义本地的一些简单数据;

    • 定义从网络中获取的数据也是使用ref;

      • // 若使用reactive函数,只会返回一个代理对象,若对对象进行替换则失去响应式;只能把数据逐个push到数组里;
        // const musics = reactive([])
        const musics = ref([])
        // musics的值是被ref对象.value属性所维护,修改ref对象的value值后,值依然被ref对象所包裹,所以依然存在响应式;
        onMounted(() => {
        const serverMusics = ["海阔天空", "小苹果", "野狼"]
        musics.value = serverMusics
        })
    • 默认是深层ref对象;
    • 对于从其他组件接收到的数据只读取,不修改。
    • 防止传递给其他组件的响应式数据被修改;
    • readonly会返回原始对象的只读代理(依然是一个Proxyproxyset方法被劫持了,不能对其进行修改);
    • 普通对象;
    • reactive 返回的对象;
    • ref 的对象;
    • readonly 返回的对象都是不允许修改的(无论在父组件还是在子组件);
    • 但是经过 readonly 处理的原来的对象是允许被修改的;
      • <show-info :roInfo="roInfo" @changeRoInfoName="changeRoInfoName"></show-info>
        const info = reactive({
        name: "why",
        age: 18,
        height: 1.88
        })
        // 使用readOnly包裹info
        const roInfo = readonly(info)
        return {
        roInfo,
        }
      • // 只要在子组件修改接收的数据,代码就会无效(报警告)
        <button @click="roInfo.name = 'james'">ShowInfo按钮</button>
        props: {
        // readonly数据
        roInfo: {
        type: Object,
        default: () => ({})
        }
        },
        // 需要修改数据,必须通过注册自定义事件,让父组件监听事件来修改数据;
        emits: ["changeRoInfoName"],
        setup(props, context) {
        function roInfoBtnClick() {
        context.emit("changeRoInfoName", "james")
        }
        return { roInfoBtnClick }
        }
    • readonly 传入响应式数据返回值才是响应式数据;
  • toRefs:转换一个 reactive 对象的所有属性为 ref

  • toRef:转换 reactive 对象中的一个属性为 ref

    • setup() {
      const info = reactive({
      name: "why",
      age: 18,
      height: 1.88
      })
      // reactive被解构后会变成普通的值, 失去响应式
      // const { name, age } = info
      // toRefs: 使得解构出来的属性具有响应式;
      const { name, age } = toRefs(info)
      // 在setup中修改解构的值需要解包
      name.value = "coder"
      // toRef:
      const height = toRef(info, "height")
      return {
      name,
      age,
      height
      }
      }
    • 传入一个getter函数,在函数里返回包含响应式数据的复杂逻辑,得到最终的计算结果,computed方法返回一个只读的ref对象;

      • <h2>{{ fullname }}</h2>
        import { reactive, computed, ref } from 'vue'
        const names = reactive({
        firstName: "kobe",
        lastName: "bryant"
        })
        const fullname = computed(() => {
        return names.firstName + " " + names.lastName
        })
    • 传入一个具有 get 和 set 的对象,返回一个可变的(可读写)ref 对象;

      • const fullname = computed({
        set: function(newValue) {
        const tempNames = newValue.split(" ")
        names.firstName = tempNames[0]
        names.lastName = tempNames[1]
        },
        get: function() {
        return names.firstName + " " + names.lastName
        }
        })
    • key:提供的属性名称;

    • value:提供的属性值;

      • import { provide, ref } from 'vue'
        const name = ref("why")
        //不同于Options API,其传递过去接收到的就是响应式数据;
        //reactive不能单独传递对象里的某个属性,响应式会失效;
        //ref可以单独传递解包后的proxy对象,但不能是proxy的属性,对应包裹的是基本数据类型更不能传递,否则失去响应式;
        provide("name", name)
        provide("age", 18)
    • 注入的属性名称;

    • 默认值;

      • import { inject } from 'vue'
        const name = inject("name") //提供的是ref对象,那么获取到的也是ref对象
        const age = inject("age")
        //定义默认值;
        const height = inject("height", 1.88)
    • 为了增加 provide 值和 inject 值之间的响应性,可以给 provide 值使用 refreactive
  • 类似于Option API的watch选项:

    • watch需要侦听特定的数据源,并且执行其回调函数;
    • 默认情况,只有当被侦听的源发生变化时才会执行回调;
    • 不同的是默认情况Options API是浅层监听;
    • import { watch } from 'vue'
      const x = ref(0)
      const y = ref(0)
      //1.ref基本类型
      watch(x, (newX) => {
      console.log(`x is ${newX}`)
      })
      //2.参数可以是getter 函数
      watch(
      () => x.value + y.value,
      (sum) => {
      console.log(`sum of x + y is: ${sum}`)
      }
      )
      //3、监听多个数据源,数组形式
      watch([x, () => y.value], ([newX, newY]) => {
      console.log(`x is ${newX} and y is ${newY}`)
      })
      //4.注意,你不能直接侦听响应式对象的属性值,例如
      const obj = reactive({ count: 0 })
      // 错误,因为 watch() 得到的参数是一个 number
      watch(obj.count, (count) => {
      console.log(`count is: ${count}`)
      })
      //5.需要用一个返回该属性的 getter 函数,提供一个 getter 函数
      //注意,如果getter返回的是个对象的话,之有对象被替换才会触发,浅层的
      watch(
      () => obj.count,
      (count) => {
      console.log(`count is: ${count}`)
      }
      )
      //6.你也可以给上面这个例子显式地加上 deep 选项,强制转成深层侦听器
      watch(
      () => state.someObject,
      (newValue, oldValue) => {
      // 注意:`newValue` 此处和 `oldValue` 是相等的
      // *除非* state.someObject 被整个替换了
      },
      { deep: true }
      )
  • 注意:

    • 数据源是ref对象,返回的newValue和oldValue会自动解包;
    • 监听不了普通值;
    • watchEffect传入的函数会被立即执行一次,并且在执行的过程中会收集依赖;
    • 只有收集的依赖发生变化时,watchEffect传入的函数才会再次执行;
    • import { watchEffect } from 'vue'
      //返回值是一个函数;不需要停止监听可不用变量接收;
      const stopWatch = watchEffect(() => {
      console.log("-------", counter.value, name.value)
      // 判断counter.value >= 10,停止监听;
      if (counter.value >= 10) {
      stopWatch() //调用函数停止监听;
      }
      })
    • 监听不了普通值;
  • watch懒执行副作用(第一次不会直接执行);
  • watch更具体的说明当哪些状态发生变化时,触发侦听器的执行;
  • watch可以访问侦听状态变化前后的值;
  • <script setup>是在.vue文件中使用组合式 API 的编译时语法糖;

    • 更少的样板内容,更简洁的代码;
  • 顶层作用域的绑定会被暴露给模板:
    Section titled “顶层作用域的绑定会被暴露给模板:”
    • 当使用 <script setup> 的时候,任何在 <script setup> 声明的顶层的绑定 (包括变量,函数声明,以及 import 引入的内容)都能在模板中直接使用;
    • <script setup>
      import showInfo from "./showInfo.vue"
      </script>
  • 替代Options API的propsemits和$emit选项;

  • 是一个宏,内置方法,不用导入;

  • 3.5之前不能解构

    • 定义props:

      • <h2>{{ name }}</h2>
        //可以不赋值给变量,赋值给变量主要为了在setup中使用props;
        //赋不赋值给变量都可以,在模板中直接使用传递的属性即可,如上
        const props = defineProps({
        name: {
        type: String,
        default: "默认值"
        },
        age: {
        type: Number,
        default: 0
        }
        })
    • 绑定函数, 并且发出事件:

      • const emits = defineEmits(["infoBtnClick"])
        function showInfoBtnClick() {
        emits("infoBtnClick", "showInfo内部发生了点击")
        }

defineProps 如果使用泛型的话无法设置默认值 withDefaults可以

<script setup lang="ts">
import { ProductItemSummaryType } from '@/views/pdms/productDashboard/config/type';
const { options } = withDefaults(
defineProps<{
options: ProductItemSummaryType[];
}>(),
{
options: () => [],
},
);
</script>
<template>
<div>
<template v-for="item in options" :key="item.id">
<div>{{item.label}}</div>
</template>
</div>
</template>
<style scoped lang="less"></style>
    • 基于路由和组件的;
    • 路由用于设定访问路径,将路径和组件映射起来;
    • 在vue-router的单页面应用中,页面的路径的改变就是组件的切换;
    • npm install vue-router
  • 注意:router hooks 只能在 setup 中使用,在 hooks 中会存在 undefined

    //手动导出
    export function useRouter() {
    return router;
    }
    export function useRoute() {
    return router.currentRoute;
    }
    • 也就是锚点(#), 本质上是改变window.location的href属性;
    • 可以通过直接赋值location.hash来改变href, 但是页面不发生刷新;
  • hash的优势就是兼容性更好,在老版IE中都可以运行,但是缺陷是有一个#,显得不像一个真实的路径。

  • import { createRouter, createWebHashHistory, createWebHistory } from 'vue-router'
    // 创建一个路由: 映射关系
    const router = createRouter({
    // 指定采用的模式: hash
    history: createWebHashHistory(),
    // history: createWebHistory(),
    )}
    • hashhistory
      有 # 号没有 # 号
      能够兼容到IE8只能兼容到IE10
      实际的url之前使⽤哈希字符,这部分url不会发送到服务器,不需要在服务器层⾯上进⾏任何处理每访问⼀个⻚⾯都需要服务器进⾏路由匹配⽣成html ⽂件再发送响应给浏览器,消耗服务器⼤量资源
      刷新不会存在 404 问题由于刷新页面之后会向服务器发送请求,浏览器直接访问嵌套路由时,会报 404 问题。
      不需要服务器任何配置需要在服务器配置⼀个回调路由
      会影响a标签锚点的使用,可以使用 scrollIntoView() 方法
  • name属性:路由记录独一无二的名称;
  • meta属性:自定义的数据;

2.3.1、代码的页面跳转和query参数

Section titled “2.3.1、代码的页面跳转和query参数”
    • //普通写法
      //传入路径
      this.$router.push('/home')
      //对象写法
      //属性为路由名或路径
      this.$router.push({
      // name: "home"
      path: "/home",
      //传入query参数
      query: {
      name: "why",
      age: 18
      }
      })
    • import { useRouter } from 'vue-router'
      const router = useRouter()
      // 普通写法
      router.push("/home")
      // 对象写法
      router.push({
      // name: "home"
      path: "/home",
      //传入query参数
      query: {
      name: "why",
      age: 18
      }
      })
    • <h2>{{ $route.query.name }}</h2>
  • go:
    • router.go(1)
  • back:
    • router.back()
  • forward:
    • router.forward()
    • 创建路由需要映射的组件;
    • 通过createRouter创建路由对象,并且传入history模式和routes;
      • 创建基于hash或者history的模式;
      • 配置路由映射: 组件和路径映射关系的routes数组;
    • 使用app注册路由对象(use方法);
    • 路由使用: 通过<router-link><router-view>
      • import { createRouter, createWebHashHistory, createWebHistory } from 'vue-router'
        // 创建一个路由: 映射关系
        const router = createRouter({
        // 指定采用的模式: hash,
        // 函数调用;
        history: createWebHashHistory(),
        // history: createWebHistory(),
        routes: [
        {
        path: "/",
        redirect: "/home" //重定向
        },
        {
        name: "home",
        path: "/home",
        component: () => import("../Views/Home.vue"), //路由懒加载
        //自定义数据
        meta: {
        name: "why",
        age: 18
        },
        children: [
        //嵌套路由重定向
        {
        path: "/home",
        redirect: "/home/recommend"
        },
        {
        path: "recommend", // /home/recommend
        component: () => import("../Views/HomeRecommend.vue")
        },
        {
        path: "ranking", // /home/ranking
        component: () => import("../Views/HomeRanking.vue")
        },
        ]
        },
        {
        name: "about",
        path: "/about",
        component: () => import("../Views/About.vue")
        },
        {
        path: "/user/:id",
        component: () => import("../Views/User.vue")
        },
        {
        // abc/cba/nba
        path: "/:pathMatch(.*)*",
        component: () => import("../Views/NotFound.vue")
        }
        ]
        })
        export default router
      • <div class="nav">
        <router-link to="/home" replace>首页</router-link>
        <!-- <router-link :to="{ path: '/home' }" replace>首页</router-link> -->
        <router-link to="/about" replace active-class="active">关于</router-link>
        <router-link to="/user/123">用户123</router-link>
        </div>
        <!-- 占位组件 -->
        <router-view></router-view>
      • <div class="home-nav">
        <router-link to="/home/recommend">推荐</router-link>
        </div>
        <!-- 占位组件 -->
        <router-view></router-view>

可以在 <script setup name="Config">设置组件的name属性

2、compression-webpack-plugin、vite-plugin-compression

Section titled “2、compression-webpack-plugin、vite-plugin-compression”

打包优化插件,将js进行gzip压缩,后台要进行解析操作,nginx、express