【Vue】菜头学前端 - vue3学习笔记

目录

简介

一、创建项目

1.使用vue-clli创建

2.使用Vite创建

二、Composition API(组合式API)

1.setup函数

2.ref函数和reactive函数

1.ref函数

2.reactive函数

3.模拟vue3实现响应式

4.computed函数

5.watch函数

6.watchEffect函数

7.生命周期

8.自定义hook函数

9.toRef 和 toRefs

三.其它 Composition API

1. shallowReactive 和 shallowRef

2.readonly 和 shallowReadonly

3.toRaw 和 markRaw

4.customRef

5.provide 和 inject

6.响应式数据的判断

四.新的组件

1.Fragment

2.Teleport

参考



简介

本文基于b站尚硅谷vue教程尚硅谷Vue2.0+Vue3.0全套教程丨vuejs从入门到精通_哔哩哔哩_bilibili编写,以作个人学习记录。


一、创建项目

1.使用vue-clli创建

## 查看@vue/cli版本,确保@vue/cli版本在4.5.0以上
vue -V
## 安装或升级@vue/cli
npm install -g @vue/cli
## 创建项目 选择vue3
vue create projectName
## 进入项目所在文件夹,启动项目
npm run serve

2.使用Vite创建

## 创建工程
npm init @vitejs/app projectName
## 进入工程目录
cd projectName
## 安装依赖
npm install
## 运行项目
npm run dev

二、Composition API(组合式API)

1.setup函数

①在vue3中数据、方法等均要配置在setup中

②setup有两种返回值:

        I.返回一个对象,包含需要使用的数据和方法,模板能直接使用这些数据和方法

        II.返回一个渲染函数(较少使用)

③setup()早于beforeCreate()执行,其有两个参数:(props,context)

  • props:值为对象,包含父组件传过来的,且本组件有接收(同vue2中接收props)的属性
  • context:值为对象,包含attrs、slots、emit
    • attrs等同于this.$attrs,用以获取父组件传递的但本组件未在props中声明的属性
    • slots等同于this.$slots,用以获取插槽
    • emit等同于this.$emit,用以触发自定义事件(自定义事件需要在新的配置项emits声明,否则控制台会报警告)

*注意点:

        1.尽量不要与vue2混用,vue2可以访问到setup的数据、方法,反之则不行

        2.setup不能是async函数,否则返回的是一个promise,模板无法获取return的对象中的属性(后期也可以返回一个Promise实例,但需要Surspense和异步组件的配合)

<template>
  <h4>姓名:{{ name }}</h4>
  <h4>学校:{{ info.school }}</h4>
  <h4>年级:{{ info.grade }}</h4>
  <h4>爱好:{{ hobbies }}</h4>
  <button @click="sayHello">点我</button>
</template>

<script>
export default {
  name: "Setup",
  setup() {
    let name = "trytou";
    let hobbies = ["抽烟", "喝酒", "烫头"];
    let info = {
      school: "菜头市第一小学",
      grade: "一年级",
    };
    function sayHello(){
        alert("hello")
    }

    return{
      name,
      info,
      hobbies,  
      sayHello,
    }
  },
};
</script>

2.ref函数和reactive函数

ref函数和reactive函数都可以定义一个响应式的数据,需要引入才能使用

使用方式:const xx=ref(value) 或 const xx=reactive(value)

1.ref函数

  • ref函数既可以接收基本数据类型,也可以接收引用数据类型(数组或对象)
  • 对于基本数据类型,响应式依然时通过Object.defineProperty()实现
  • 对于对象类型数据,响应式则靠vue3中的一个新函数——reactive函数实现
  • 在js中操作ref定义的响应式数据时要通过xxx.value获取,模板中则不需要

2.reactive函数

  • reactive函数只能接收引用数据类型的数据(数组或对象)
  • reactive函数返回一个proxy对象
  • reactive函数定义的数据是深层次的,其内部的属性可以直接修改,(在vue2中通过直接赋值的方式来修改data中的数组的某个元素是无效的)
<template>
  <h4>姓名:{{name}}</h4>
  <h4>年龄:{{age}}</h4>
  <h4>学校:{{info.school}}</h4>
  <h4>年级:{{info.grade}}</h4>
  <h4>爱好:{{hobbies}}</h4>
  <button @click="sayHello">点我</button>
  <button @click="changeHobby">点我换爱好</button>
  <button @click="changeSchool">点我换学校</button>
</template>

<script>
import { reactive, ref } from 'vue'

export default {
    name:'Ref_Reactive',
    setup(){
    //ref函数定义一个响应式数据
    let name=ref('trytou')
    let age=ref(20)
    let info=reactive({
      school:'菜头市第一小学',
      grade:'一年级'
    })
    let hobbies=reactive(['抽烟','喝酒','烫头'])

    function sayHello(){
      alert('hello trytou');
    }

    function changeHobby(){
      hobbies[0]='打球'
    }

    function changeSchool(){
      info.school='反斗花园幼儿园'
    }

    return{
      name,
      age,
      info,
      hobbies,  
      sayHello,
      changeHobby,
      changeSchool
    }
  }
}
</script>

3.模拟vue3实现响应式

vue3中通过Proxy(代理)和Reflect(反射)来实现响应式

<script>
        let person = {
            name: 'trytou',
            age: 20
        }

        const p = new Proxy(person, {
            get(target, prop) {
                return Reflect.get(target,prop)
            },
            set(target, prop, value) {
                return Reflect.set(target,prop,value)
            },
            deleteProperty(target,prop){
                return Reflect.deleteProperty(target,prop)
            }
        })

        p.name='Chan'
        console.log(p);
    </script>

4.computed函数

  • computed接收一个函数或者一个对象需要引入才能使用
import {computed} from 'vue'
export default {
  name: "Computed",
  setup() {
    let person = reactive({
      firstName: "try",
      lastName:'tou',
    })
    //计算属性-简写
    // person.fullName=computed(()=>person.name+'-'+person.age)
    
    //计算属性-完整写法
    person.fullName=computed({
        get(){
            return person.firstName+'-'+person.lastName
        },
        set(value){
            person.firstName=value.split('-')[0]
            person.lastName=value.split('-')[1]
        }
    })

    return{
        person,

    }
  },
};

5.watch函数

watch函数中有较多的坑,监视reactive定义的数据时要注意

  • 情况一:监视ref定义的一个响应式数据
watch(num,(newValue,oldValue)=>{
   console.log(newValue,oldValue);
})
  • 情况二:监视ref定义的多个响应式数据
watch([num,msg],(newValue,oldValue)=>{
   console.log(newValue,oldValue);
})
  • 情况三:监视reactive定义的一个响应式数据 (默认deep:true,且无法设置为false)

watch(person,(newValue,oldValue)=>{
   //此处 oldValue == newValue , 无法获取正常的 oldValue
   console.log(newValue,oldValue);
},{deep:false})
  • 情况四:监视reactive定义的某个响应式数据中的某个属性

watch(()=>person.age,(newValue,oldValue)=>{
   console.log(newValue,oldValue);
})
  • 情况五:监视reactive定义的某一个响应式数据中的多个属性

watch([()=>person.age,()=>person.name],(newValue,oldValue)=>{
  console.log(newValue,oldValue);
})
  • 情况六:监视reactive定义的深层次的响应式数据

watch(()=>person.jobs,(newValue,oldValue)=>{
   console.log(newValue,oldValue);
},{deep:true})//需要设置deep为true

6.watchEffect函数

watchEffect函数类似computed,但前者无需写返回值,且不必指明监视哪个属性

//类似computed属性,检测回调函数中用到的数据发生变化即会重新执行回调
watchEffect(()=>{
   console.log(person.age);
})

7.生命周期

在vue3中有两种使用生命周期钩子的方式

  • 第一种方式是通过配置对象的形式,与vue2中生命周期钩子使用方式一致,但有两个更换了名字:
    • beforDestroy更名为beforeUnmount
    • destroyed更名为unmounted
  • 第二种方式是通过组合API的形式,除了setup之外的钩子都应在setup内部调用,与vue2生命周期钩子对应关系如下:
    • 因为 setup 是围绕 beforeCreate 和 created 生命周期钩子运行的,所以不需要显式地定义它们。
    • setup()  ==>  beforeCreate() 和 created()
    • onBeforeMount()  ==>  beforeMount()
    • onMounted  ==>  mounted()

    • onBeforeUpdate  ==>  beforeUpdate()

    • onUpdated  ==>  updated()

    • onBeforeUnmount()  ==>  beforeDestroy()

    • onUnmounted()  ==>  destroy()()

  • 两种方式可以同时使用(但尽量不要这么做),组合式API的生命周期钩子会比配置对象形式的钩子先执行

8.自定义hook函数

  • hook本质上是一个函数,作用类似于mixin

在usePoint.js中定义一个hook并暴露出去

import {onMounted,onUnmounted, reactive} from 'vue';

export function usePoint() {
    let point = reactive({
        x: '',
        y: ''
    })

    let getPoint = function (event) {
        point.x = event.pageX
        point.y = event.pageY
    }

    onMounted(() => {
        window.addEventListener('click', getPoint)
    })

    onUnmounted(() => {
        window.removeEventListener('click', getPoint)
    })

    return point
}

在组件中引入hook便可直接使用,无需再次引入hook中引用的组合式api

<template>
  <h4>点击任意位置获取当前鼠标的坐标</h4>
  <span>X轴坐标:{{point.x}}</span><br>
  <span>Y轴坐标:{{point.y}}</span>
</template>

<script>
import {usePoint} from '../hooks/usePoint';
export default {
    name:'Hook',
    setup(){
        let point=usePoint()

        return {
            point
        }
    }
}
</script>

9.toRef 和 toRefs

  • toRef 和 toRefs 都是函数,后者可以批量创建多个ref
  • toRef可以为一个响应式对象中的某个属性创建一个ref,且这个ref的值会指向源响应式对象中的那个属性
  • toRefs可以为一个响应式对象中的每一个最外层的属性创建ref
<template>
    <h4>{{person}}</h4>
    <hr>
  <h4>my name is {{name}}</h4>
  <button @click="name='Chan'">rename</button>
  <h4>salary: {{jobs.job1.salary}}k</h4>
  <button @click="jobs.job1.salary++">涨薪</button>
  <button @click="salary++">涨薪</button>
  <h4>age: {{age}}</h4>
  <button @click="age++">长大了</button>
</template>

<script>
import { reactive, toRef ,toRefs} from 'vue'
export default {
    name:'ToRef',
    setup(){
        let person=reactive({
            name:'trytou',
            age:20,
            jobs:{
                job1:{
                    salary:8
                }
            }
        })

        /salary的值发生变化,person.jobs.job1.salary的值也会发生变化
        const salary=toRef(person.jobs.job1,'salary')
        console.log(salary);/

        console.log(toRefs(person));

        return{
            person,
            salary,
            ...toRefs(person)
        }
    }
}
</script>

三.其它 Composition API

1. shallowReactive 和 shallowRef

    //shallowReactive 与 reactive的区别在于shallowReactive只处理对象最外层属性的响应式(浅响应式)

    let sReaPerson=shallowReactive(person)

    //shallowRefref的区别在于 shallowRef 不会将.value变成响应式(proxy),而仍是一个普通的对象

    let sRefPerson=shallowRef(person)

2.readonly 和 shallowReadonly

//readonly接收一个响应式数据并使其成为只读的(深只读)

    let p1 = readonly(reactive(person));

    let p3 = readonly(ref(num));

//shallowReadonly接收一个响应式数据并使其成为只读的(浅只读)

    let p2 = shallowReadonly(reactive(person));

    let p4 = shallowReadonly(ref(num));

3.toRaw 和 markRaw

//toRaw返回reactive或readonly代理的原始对象(ref无效)

    console.log(toRaw(person));

    console.log(toRaw(pet));

//markRaw标记一个对象,使其永远不会转换为 proxy,返回对象本身

    let mr = markRaw({ age: 20 });

    let kid = reactive(mr);

4.customRef

  • 创建一个自定义的 ref,并对其依赖项跟踪和更新触发进行显式控制。它需要一个工厂函数,该函数接收 track 和 trigger 函数作为参数,并且应该返回一个带有 get 和 set 的对象。

使用自定义ref实现防抖效果:

<template>
  <input type="text" v-model="msg" />
  <h4>{{ msg }}</h4>
</template>

<script>
import { customRef, ref } from "@vue/reactivity";
export default {
  name: "CustomRef",
  setup() {
    function myRef(initValue) {
      let timer;
      return customRef((track, trigger) => {
        return {
          get() {
            track();
            return initValue;
          },
          set(newValue) {
            clearTimeout(timer);
            timer = setTimeout(() => {
              initValue = newValue;
              trigger();
            }, 500);
          },
        };
      });
    }

    let msg = myRef("hello");

    return {
      msg,
    };
  },
};
</script>

5.provide 和 inject

  • 作用:可以用来实现祖孙组件之间的通信
  • 在祖组件中使用provide(),后代组件使用inject()接收

祖组件

...
setup(){
let msg=ref('love u')

provide('msg',msg)
...
}

后代组件

...
setup(){
    let msg=inject('msg')
    ...
}

6.响应式数据的判断

... 
setup(){
    let p1=ref(0)
    let p2=reactive({})
    let p3=readonly({})

    console.log(isRef(p1));//判断一个值是否为一个 ref 对象
    console.log(isReactive(p2));//判断一个对象是否是由 reactive 创建的响应式代理
    console.log(isReadonly(p3));//判断一个对象是否是由 readonly 创建的只读代理
    console.log(isProxy(p2));//判断一个对象是否是由 reactive 或者 readonly 方法创建的代理
    console.log(isProxy(p3));
  }
...

四.新的组件

1.Fragment

  • 在vue3中组件无需根标签,内部会自动将模板中的内容包含在一个Fragment中

2.Teleport

  • teleport可以让组件移动到指定位置,在下面的例子中Son组件有一个Dialog组件,如果我们想让这个Dialog组件位于body下可以通过teleport中的to属性来指定

祖组件

<template>
    <div class="cur">
        <h3>我是祖先</h3>
    <Child></Child>
  </div>
</template>

<script>
import Child from './17_child.vue';
export default {
  name: "TeleportComponent",
  components:{Child}
};
</script>
<style>
div{
    padding:20px
}
</style>

子组件

<template>
  <div class="child">
      <h4>我是Child组件</h4>
      <Son></Son>
  </div>
</template>

<script>
import Son from './17_son.vue';
export default {
    name:'Child',
    components:{Son}
}
</script>

<style>
.child{
    background-color: rgb(47, 145, 184);
}
</style>

孙组件

<template>
  <div class="son">
      <h5>我是Son组件</h5>
      <Dialog></Dialog>
  </div>
</template>

<script>
import Dialog from './17_Dialog.vue';
export default {
    name:'Son',
    components:{Dialog},
    setup(){
        
    }
}
</script>

<style>
.son{
    background: rgb(236, 80, 80);
}
</style>

Dialog组件

<template>
  <button @click="isShow = true">开启弹窗</button>
  <teleport to="body">
    <div class="mask" v-if="isShow">
      <div class="dialog">
        <h3>我是弹窗</h3>
        <button @click="isShow = false">关闭弹窗</button>
      </div>
    </div>
  </teleport>
</template>

<script>
import { ref } from "vue";

export default {
  name: "Dialog",
  setup() {
    let isShow = ref(false);
    return {
      isShow,
    };
  },
};
</script>

<style>
.dialog {
  position: absolute;
  width: 440px;
  height: 300px;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  text-align: center;
  background-color: pink;
  z-index: 999;
}

.mask {
  width: 100%;
  height: 100%;
  position: absolute;
  top: 0;
  left: 0;
  background-color: rgba(0, 0, 0, 0.5);
}
</style>

可以看到teleport包含的结构出现在<body>下

 


参考

尚硅谷vue3教程:尚硅谷Vue2.0+Vue3.0全套教程丨vuejs从入门到精通_哔哩哔哩_bilibili

vue3官方文档:Vue.js

vite官方文档:Vite中文网

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值