Vue3——Hook函数 & 生命周期 & toRef和toRefs & 其他的组合式API

一、Hook函数

Vue3 的hook函数相当于 vue2 的 mixin,不同点在于hooks是函数。Vue3的hook函数可以帮助我们提高代码的复用性,能在不同的组件中都使用hook函数。

在src目录下建立一个 hooks 文件夹,文件夹中新建一个我们要用的方法的名字.js文件(一般情况下use开头)。
useCar.js

import { ref, computed } from 'vue'
export default function () {
    let carName = ref("奔驰")
    let carPrice = ref(50000)
    // 修改汽车名称
    let updateCarName = (name) => {
        carName.value = name
    }
    // 修改汽车价格
    let updateCarPrice = (price) => {
        carPrice.value = price
    }
    let usaCarPrice = computed(() => {
        return '$' + (carPrice.value / (Math.random() + 6)).toFixed(2)
    })
    return {
        carName,
        carPrice,
        updateCarName,
        updateCarPrice,
        usaCarPrice
    }
}

usePlane.js

// 导入ref,computed
import { ref, computed } from "vue";
export default function () {
  // 飞机名称
  let planeName = ref("波音747");
  // 飞机价格
  let planePrice = ref(20000);
  // 修改飞机名称的方法
  let updatePlaneName = (name) => {
    planeName.value = name;
  };
  // 修改飞机价格的方法
  let updatePlanePrice = (price) => {
    planePrice.value = price;
  };
  // 飞机在美国的价格
  let usaPlanePrice = computed(() => {
    return "$" + (planePrice.value / (Math.random() + 6)).toFixed(2);
  });
  return {
    planeName,
    planePrice,
    updatePlaneName,
    updatePlanePrice,
    usaPlanePrice,
  };
}

组件
将文件引入到要使用的组件中。

<div class="child">
    <div class="car">
        <p>汽车名称:{{carName}}<button @click="updateCarName('宝马')">修改汽车名称</button></p>
        <p>汽车价格:{{'¥'+carPrice}}<button @click="updateCarPrice(10000)">修改汽车价格</button></p>
        <p>美国价格:{{usaCarPrice}}</p>
    </div>
    <hr>
    <div class="plane">
        <p>汽车名称:{{planeName}}<button @click="updatePlaneName('B2轰炸机')">修改飞机名称</button></p>
        <p>汽车价格:{{'¥'+planePrice}}<button @click="updatePlanePrice(50000)">修改飞机价格</button></p>
        <p>美国价格:{{usaPlanePrice}}</p>
    </div>
</div>
import {computed, ref} from 'vue'
// 导入hook函数useCar
import useCar from '../hooks/useCar'
// 导入hook函数usePlane
import usePlane from '../hooks/usePlane'
export default {
  name: "Child",
  setup() {
      return {
          ...useCar(),
          ...usePlane()
      }
  }
};

二、生命周期

Vue3 中,可以在 setup() 函数中使用生命周期,定义组合式API生命周期函数。组合式API生命周期函数,会先与传统的生命周期函数执行。
与vue2相比,在vue3中对beforeDestroy和destroyed这两个生命周期函数,进行了重命名,beforeUnmount 替换了 beforeDestroy;unmounted 替换了 destroyed。
setup()函数,可以替代beforeCreate 和 created 这两个生命周期函数,因为vue2中的beforeCreate和created生命周期的执行几乎与VUE3中的setup在同一时间执行。

<div class="child3">
  {{ count }}
  <button @click="updateCount">count++</button>
</div>
// 导入生命周期组合式api,除了beforeCreate和created这两个生命周期函数没有组合式api
// 其他的生命周期函数,都有对应的组合式api,命名方式只是在原有方法名的前面加上on
// setup()函数在这里充当了beforeCreate和created这两个生命周期函数的功能。
import { ref, onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount,onUnmounted } from "vue";
export default {
  name: "Child3",
  data() {
    return {
      show:true
    }
  },
  setup() {
    //注意:setup是在beforeCreate和created之前运行的。
    //所以,在setup里面,无法调用data和methods里面的数据
    console.log('setup');
    let count = ref(1);
    let updateCount = () => {
      count.value++;
    };
    //挂载前
    onBeforeMount(() => {
        console.log('onBeforeMount');
    }),
    //挂载完成
    onMounted(()=>{
        console.log('onMounted');
    })
    //修改后,页面重新挂载前
    onBeforeUpdate(()=>{
        console.log('onBeforeUpdate');
    })
    //修改后,页面重新挂载完成
    onUpdated(()=>{
        console.log('onUpdated');
    })
    //卸载前
    onBeforeUnmount(()=>{
        console.log('onBeforeUnmount');
    })
    //卸载完成
    onUnmounted(() => {
        console.log('onUnmounted');
    })

    return {
      count,
      updateCount,
    };
  },
  /* data() {
    return {
      count: 1,
    };
  },
  methods: {
    updateCount() {
      this.count++;
    },
  }, */
  // 数据初始化前
  /*beforeCreate() {
    console.log("--beforeCreate--");
  },
  // 数据初始化完成
  created() {
    console.log("--created--");
  },
  // 页面挂载前
  beforeMount() {
    console.log("--beforeMount--");
  },
  // 页面挂载完成
  mounted() {
    console.log("--mounted--");
  },
  // 数据修改后,页面重新挂载前
  beforeUpdate() {
    console.log(this.count);
    console.log("--beforeUpdate--");
  },
  // 数据修改后,页面重新挂载完成
  updated() {
    console.log(this.count);
    console.log("--updated--");
  },
  // 卸载组件之前前
  beforeUnmount() {
    console.log("--beforeUnmount--");
  },
  // 卸载组件完成
  unmounted() {
    console.log("--unmounted--");
  }, */
};

三、toRef和toRefs

toRef()函数:可以用来为一个 reactive 对象里面的指定属性创建一个 ref,这个 ref 可以被传递并且能够保持响应性。这样做的好处是简化了模板中的表达式。toRef()函数,需要传两个参数:reactive 对象、具体的属性名。
toRefs()函数:把一个响应式对象转换成普通对象,该普通对象的每个属性都是一个 ref。

<div class="child1">
  <ul>
    <li>姓名:{{ name }}</li>
    <li>年龄:{{ age }}</li>
    <li>性别:{{ sex }}</li>
    <li>地址:{{ address }}</li>
    <li>汽车名称:{{ car.name }}</li>
    <li>汽车价格:{{ car.price }}</li>
    <li><button @click="updateStudent">修改学生信息</button></li>
  </ul>
</div>
import { reactive, toRefs } from "vue";
export default {
  name: "Child1",
  setup() {
    // 学生对象
    let student = reactive({
      name: "张三",
      age: 20,
      sex: "男",
      address: "南京",
      car: {
        name: "宝马",
        price: 1000,
      },
    });
    // 修改学生的方法
    let updateStudent = () => {
      student.name = "李四";
      student.age = 22;
      student.sex = "女";
      student.address = "成都";
      student.car.name = "奔驰";
      student.car.price = 2000;
    };
    return {
      // toRef()方法,用于将一个reactive对象里面的指定属性以ref形式的对象返回
      // name:toRef(student,'name'),
      // age:toRef(student,'age'),
      // sex:toRef(student,'sex'),
      // address:toRef(student,'address'),

      // 假如 reactive 对象中,有100个属性,上面的操作要写100次,所以,一般都直接用toRefs函数
      // 注意:如果student里面的属性是一个对象,这个属性对象里面的内容是不能直接转为ref的
      // toRefs只能保证一级属性
      ...toRefs(student),
      updateStudent,
    };
  },
};

四、其他的组合式API

1、readonly

readonly()方法:用于返回一份只读数据。传入一个对象(响应式或普通)或 ref,返回一个原始对象的只读代理。一个只读的代理是“深层的”,对象内部任何嵌套的属性也都是只读的。
注意:该方法,不能将一个普通值类型数据转为只读数据。

<div>{{ student }} <button @click="updateStudent">修改学生信息</button></div>
import { reactive, readonly } from "vue";
export default {
  name: "Child2",
  setup() {
    let data = reactive({
      name: "张三",
      age: 20,
    });
    let student = readonly(data);
    // student是一个只读对象,无法修改数据
    let updateStudent = () => {
      student.name = "李四";
      student.age = 22;
    };
    return {
      student,
      updateStudent,
    };
  },
};

2、isReadonly

检查一个对象是否是由 readonly 创建的只读代理。

import { reactive, readonly, isReadonly } from "vue";
let data = reactive({
  name: "张三",
  age: 20,
});
let student = readonly(data);
console.log('student is readonly',isReadonly(student));   //true

3、isRef

isRef():检查一个值是否为一个 ref 对象。

import { ref, isRef } from "vue";
let name = ref('你好')
console.log('name is ref',isRef(name));   //true

4、isProxy

isProxy():检查一个对象是否是由 reactive 或者 readonly 方法创建的代理。

import { reactive, readonly, isProxy } from "vue";
setup() {
let data = reactive({
  name: "张三",
  age: 20,
});
let student = readonly(data);
console.log('data is proxy',isProxy(data));   //true
console.log('student is proxy',isProxy(student));   //true

5、isReactive

isReactive():检查一个对象是否是由 reactive 创建的响应式代理。如果这个代理是由 readonly 创建的,但是又被 reactive 创建的另一个代理包裹了一层,那么同样也会返回 true。

import { reactive, isReactive } from "vue";
setup() {
  let data = reactive({
    name: "张三",
    age: 20,
  });
  console.log('data is reactive',isReactive(data));   //true
}

6、unref

unref()函数:如果参数是一个 ref 则返回它的 value,否则返回参数本身。等同于 val = isRef(val) ? val.value : val 。

import { ref, unref } from "vue";
setup() {
  let name = ref('你好')
  let age = 20
  console.log(unref(name));   // '你好'
  console.log(unref(age));   // 20
}

7、markRaw

markRaw():使用markRaw()方法返回出来的对象,再也不能成为响应式对象,函数返回这个对象本身。如果被 markRaw 标记了,即使在响应式对象中作属性,也依然不是响应式的。

import { reactive, markRaw } from "vue";
setup() {
  // 使用markRaw()方法,返回出来的对象,再也不能成为响应式对象。
  let car = markRaw({
      name:'奔驰',
      price:20
  })
  let car2 = reactive(car)
  // car2只是一个普通对象,不是响应式对象
  console.log(car2);
}

8、shallowRef

shallowRef():返回的对象 value 属性值是 object对象(普通对象),不再具备任何响应式了。

<div>
  汽车信息:{{ car3 }}
  <button @click="updateCar">修改汽车</button>
</div>
import { shallowRef } from "vue";
setup() {
  let car3 = shallowRef({
    name: "大众",
    type: {
      typeName: "SUV",
    },
  });
  let updateCar = () => {
    // 由于value返回的是object对象,所以,这里不再具有响应式
    car3.value.name = "奔驰";
    car3.value.type.typeName = "跑车";
  };
  return {
    car3,
    updateCar
  };
}

9、shallowReactive

shallowReactive():用于定义浅响应式对象,只对第一层属性设置响应式。

import { shallowReactive,isReactive } from "vue";
setup() {
  let luhan = shallowReactive({
      name:'鹿晗',
      age:30,
      friend:{
          name:'关晓彤',
          age:20,
      }
  })
  let updateLuhan = ()=>{
      // luhan.name = '张艺兴',
      // luhan.age = 25

      // 如果只是修改对象的深层属性,不会触发页面更新
      luhan.friend.name = '小美'
      luhan.friend.age = 22
  }
}

10、shallowReadonly

shallowReadonly:浅只读,只有第一层属性是只读的。

import { shallowReadonly } from "vue";
setup() {
  let luhan = shallowReactive({
      name:'鹿晗',
      age:30,
      friend:{
          name:'关晓彤',
          age:20,
      }
  })
  let luhan3 = shallowReadonly(luhan);
  // luhan3.name = '小明'      // 属性只读
  luhan3.friend.name = "小美";      // 属性可修改
}

11、toRaw

toRaw()方法:用于将一个响应式对象转为普通对象。

import { toRaw } from "vue";
setup() {
  let luhan = shallowReactive({
      name:'鹿晗',
      age:30,
      friend:{
          name:'关晓彤',
          age:20,
      }
  })
  let luhan2 = toRaw(luhan);
  console.log("luhan2", luhan2);   // 普通对象
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,905评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,140评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,791评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,483评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,476评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,516评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,905评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,560评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,778评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,557评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,635评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,338评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,925评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,898评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,142评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,818评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,347评论 2 342

推荐阅读更多精彩内容