(VUE3) 三、Vue3高阶(Hook函数 & 生命周期 & toRef和toRefs & 其他的组合式API)

1.Hook函数

hooks是一种开发思想,我们可以利用hooks把相关代码剥离出去。hook有个编码习惯是用use开头。

在src下新建hooks文件夹,下新建useCar.js。useCar.js里是个函数,且直接export default给它导出去,且直接return返回定义的这些东东西西。
// 导入ref,computed
import {ref,computed} from 'vue'

let carName = ref("奔驰");
    let carPrice = ref(5000);
    // 修改汽车名称的方法
    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
    }

同理,再搞一份飞机的hook函数,hooks文件下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
    }
}

components下Child.vue组件:
在组件里导入所需的hook函数useCar,
import useCar from'../hooks/useCar',然后在setup(){ } 里 return{ } 里 ...useCar()(三个点是展开运算符)

  <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>
<script>
import { computed, ref } from "vue";

// 导入hook函数useCar
import useCar from'../hooks/useCar'
// 导入hook函数userPlane
import usePlane from'../hooks/usePlane'

export default {
  name: "Child",
  setup() {
    return {
      ...useCar(),
      ...usePlane()
    };
  },
};
</script>

页面效果:

hooks下每个xx.js文件就是一个业务,这里面的业务可以在多个xx.vue组件中使用。分别在组件中导入所需的hook函数useCar,然后在setup(){ } 里 return{ } 里 ...useCar(),就可以惹。


2.生命周期


vue3是上来就挂载。vue2是先走一半再挂载。

App.vue组件:

<template>
  <div class="app">
    <div>
      {{count}}
      <button @click="updateCount">count++</button>
    </div>
    <Child></Child>
    <Child2></Child2>
  </div>
</template>

<script>
import Child from './components/Child.vue'
import Child2 from './components/Child2.vue'
export default {
  name: 'App',
  components:{
    Child,
    Child2

  },
  data() {
    return {
      count:1
    }
  },
  methods: {
    updateCount(){
      this.count++
    }
  },
  beforeCreate() {
    // console.log(this.count);
    console.log('--beforeCreate--');
  },
  created() {
    // console.log(this.count);
    console.log('--created--');
  },
  beforeMount() {
    console.log('--beforeMount--');
  },
  mounted() {
    console.log('--mounted--');
  },
  beforeUpdate() {
    console.log(this.count);
    // debugger;
    console.log('--beforeUpdate--');
  },
  updated() {
    console.log(this.count);
    // debugger
    console.log('--updated--');
  },
}
</script>

<style>
*{
  margin: 0;
  padding: 0;
  list-style: none;
}
.app{
  border: 1px solid #ccc;
  margin: 10px;
  padding: 10px;
}
</style>

后台打印效果:

点击count++之前:

点击count++之后:

beforeUpdate和updated会在什么场景下使用?
顾sensei答,这会是一个补救措施,或是修改后的跟踪措施;或有点像监视器,能够监测到页面是否在更新,watch是针对某一个变量去监视,但它是监测到整个页面。

在vue3中,beforeDestroy和destroyed这两个生命周期函数,进行了重命名,替换成了 beforeUnmount 和 unmounted 。
在vue3中,除了beforeCreate和created这两个生命周期函数没有组合式api,其他的生命周期函数都有对应的组合式api,命名方式只是在原有方法名的前面加上on。
setup()函数在这里充当了beforeCreate和created这两个生命周期函数的功能。

注意:setup是在beforeCreate和created之前运行的。所以,在setup里面,无法调用data和methods里面的数据。

Child3.vue组件:

<template>
  <div class="child3">
    {{ count }}
    <button @click="updateCount">count++</button>
  </div>
</template>
<script>
// 导入生命周期组合式api,除了beforeCreate和created这两个生命周期函数没有组合式api,
// 其他的生命周期函数都有对应的组合式api,命名方式只是在原有方法名的前面加上on。
// setup()函数在这里充当了beforeCreate和created这两个生命周期函数的功能。
import { ref, onBeforeMount, onMounted,onBeforeUpdate,onUpdated,onBeforeUnmount,onUnmounted } from "vue";
export default {
  name: "Child3",
  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,
    };
  }
};
</script>
<style scoped>
.child3 {
  border: 1px solid #ccc;
  padding: 10px;
  margin-top: 10px;
}
</style>

App.vue组件:

<template>
  <div class="app">
    <button @click="unMount">卸载Child3组件</button>
    <Child3 v-if="isShow"></Child3>
  </div>
</template>

<script>
import Child3 from './components/Child3.vue'
export default {
  name: 'App',
  components:{
    Child3
  },
  data() {
    return {
      isShow:true
    }
  },
  methods: {
    unMount(){
      this.isShow=false
    }
  },
}
</script>

<style>
*{
  margin: 0;
  padding: 0;
  list-style: none;
}
.app{
  border: 1px solid #ccc;
  margin: 10px;
  padding: 10px;
}
</style>

3.toRef 和 toRefs

toRef() 方法,用于将一个响应式对象里面的指定属性以ref形式的对象返回,这样写的好处是,可以简化模板里面的语法。

toRefs() 方法,用于将一个reactive对象返回一个新对象,该对象里面的所有属性都是一个ref对象。

<template>
  <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>
</template>
<script>
import {ref,reactive, toRef,toRefs} from 'vue'
export default {
  name: "Child1",
  setup() {
      let student = reactive({
          name:'张三',
          age:20,
          sex:'男',
          address:'南京',
          car:{
              name:'迈巴赫',
              price:200
          }
      })
      // 修改学生的方法
      let updateStudent =()=>{
          student.name = '李四'
          student.age = 22
          student.sex = '女'
          student.address = '杭州',
          student.car.name = '布加迪威龙',
          student.car.price = 100
      }
      return{
        // toRef()方法,用于将一个响应式对象里面的指定属性以ref形式的对象返回,
        // 这样写的好处是,可以简化模板里面的语法。
        //   name:toRef(student,'name'),
        //   age:toRef(student,'age'),
        //   sex:toRef(student,'sex'),
        //   address:toRef(student,'address'),

        // toRefs()方法,用于将一个reactive对象返回一个新对象,
        // 该对象里面的所有属性都是一个ref对象。
        ...toRefs(student),
          updateStudent
      }
  }
};
</script>

4.其他的组合式API

readonly() 方法,用于返回一份只读数据。注意:该方法,不能将一个普通值类型数据转为只读数据。
markRaw() 方法,返回出来的对象,再也不能成为响应式对象。应该用的也不多奥。
unref() 方法,用于判断对象如果是ref,就返回ref的value值;如果不是ref,直接返回值。
shallowReactive() / shallowRef() 方法,用于定义浅响应式对象。只对第一层属性设置响应式。
shallowReadonly() 是浅只读,只有第一层属性是只读的。
toRaw() 方法,用于将一个响应式对象转为普通对象。

<template>
  <div class="child2">
    <div>{{ student }}</div>
    <button @click="updateStudent">修改学生</button>
    <hr>
    <div>{{luhan}}</div>
    <button @click="updateLuhan">修改鹿晗</button>
  </div>
</template>
<script>
import { unref,isProxy,isRef,isReactive, isReadonly, markRaw, 
onMounted,ref,reactive, readonly, shallowReactive, toRaw } from "vue";
export default {
  name: "Child2",
  setup() {
    let name =ref('你好')
    let age=20
    let data = reactive({
      name: "张三",
      age: 20,
    });
    // readonly()方法,用于返回一份只读数据
    // 注意:该方法,不能将一个普通值类型数据转为只读数据
    let student = readonly(data);
    let updateStudent = () => {
      // student.name += "!";
      data.name +="!"
      // 此处,student是返回出去的数据,改不了,data是原数据所以改得了。
      // student本质上是个代理对象proxy,但不能改奥,一改就报错,使用场景并不多。
    };

    // 使用markRaw()方法,返回出来的对象,再也不能成为响应式对象。应该用的也不多奥。
    let car = markRaw({
      name: "奔驰",
      price: 20,
    });
    // console.log(car);
    let car2 = reactive(car);
    console.log(car2);

    // shallowReactive() shallowRef() 用于定义浅响应式对象。
    // 只对第一层属性设置响应式。
    // shallowReadonly()是浅只读,只有第一层属性是只读的。
    let luhan = shallowReactive({
      name: "鹿晗",
      age: 30,
      friend: {
        name: '关晓彤',
        age: 20
      },
    });
    console.log('luhan',luhan);   //luhan是个响应式对象Proxy

    // toRaw()方法,用于将一个响应式对象转为普通对象。
    let luhan2 = toRaw(luhan)
    console.log('luhan2',luhan2);

    let updateLuhan = () => {
      // luhan.name = "张艺兴",
      // luhan.age = 25
      // 如果只是修改对象的深层属性,不会触发页面更新。
      luhan.friend.name='小美'
      luhan.friend.age=22
    };

    onMounted(() => {
        console.log('name is ref',isRef(name));
        console.log('data is reactive',isReactive(data));
        console.log('data is proxy',isProxy(data));
        console.log('student is readonly',isReadonly(student));
        console.log('student is proxy',isProxy(student));

        // unref()方法,用于判断对象如果是ref,就返回ref的value值;如果不是ref,直接返回值。
        // 等同于:  val = isRef(val) ? val.value : val
        console.log(unref(name));
        console.log(unref(age));
    })

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

推荐阅读更多精彩内容