vue3笔记

vue3笔记

vue3官网
尚硅谷视频
黑马视频


在这里插入图片描述


## vue3的main.js
1.vue3
import { createApp } from 'vue' // 引入createApp方法
import App from './App.vue' // 引入App根组件
createApp(App).mount('#app')  // 创建一个 Vue 实例,并将其挂载到 id 为 "app" 的 DOM 元素上

2.vue2
import Vue from 'vue' // 引入vue
import App from './App.vue' // 引入App根组件
// 创建一个 Vue 实例,并在创建时指定根组件的渲染函数,然后将该实例挂载到 id 为 "app" 的 DOM 元素上
new Vue({
  render: h => h(App),
}).$mount('#app')

3.render函数
render: function (createElement) {
    return createElement(App);
}
↓ 对象的增强写法

render (createElement) {
    return createElement(App);
}
↓ createElement 简写成 h
render (h){
    return h(App);
}
↓ 箭头函数
render: h => h(App);

 createElement 函数是 Vue.js 的渲染函数中常用的一个函数,用于创建虚拟 DOM 元素,要有return返回值。
/ createElement 函数的第一个参数:标签名或组件选项,可以是 HTML 标签名字符串,表示创建普通的 HTML 元素,也可以是组件的选项对象,表示创建一个组件实例。/

import App from './App'
new Vue({
    el: '#app',
    render: h => h(App)
})
↓ 等价与
import App from './App'
new Vue({
    el: '#app',
    template: '<App></App>',
    components: {
 App 
}
})
上面两种的效果是一样的,可以看出 h(App)函数 的作用是:使用App作为这个Vue实例的template,同时一并了注册App组件;

在这里插入图片描述
Vue3 模板结构可以不使用根标签

<template>
  <img alt="Vue logo" src="./assets/logo.png">
  <HelloWorld msg="Welcome to Your Vue.js App"/>
  <div> hello vue3 </div>
</template>

Vue3 引入组件后,可以再模板中直接使用

<template>
  <ReactiveDemo />
  <ComputedDemo />
</template>

<script setup>
import ReactiveDemo from '@/components/ReactiveDemo.vue'
import ComputedDemo from '@/components/ComputedDemo.vue'
</script>

Composition API (组合式api)

选项式API和组合式API的区别:

1.vue2 选项式API(Options API)
<script>
export default {
  // data() 返回的属性将会成为响应式的状态,并且暴露在this上
  data() {
    return {
      count: 0
    }
  },
 
  // methods 是一些用来更改状态与触发更新的函数
,它们可以在模板中作为事件监听器绑定
  methods: {
    increment() {
      this.count++
    }
  },
 
  // 生命周期钩子会在组件生命周期的各个不同阶段被调用

  mounted() {
    console.log('通常项目中初始化在这里调接口')
  }
}
</script>
 
 
2.vue3 组合式API(Composition API)
<script>
import { ref, computed, onMounted } from 'vue'
export default {
  setup () {
    // 定义变量
    const count = ref(1)
    // 定义计算属性
    const doubleCount = computed(() => {
      return count.value * 2
    })
    // 定义方法
    const changeCount = () => {
      count.value = 100
    }
    // 生命周期函数
    onMounted(() => {
      console.log(count.value, 'onMounted')
    })
    // 统一将变量和方法等暴露出去
    return {
      count, doubleCount, changeCount
    }
  },
}
</script>
  • 选项式 API可以用包含多个选项的对象来描述组件的逻辑,例如 data、methods 和 mounted。
  • 选项所定义的属性都会暴露在函数内部的 this 上,它会指向当前的组件实例;
  • 选项式 API 以“组件实例”的概念为中心 (即 this);
  • 组合式 API可以使用导入的 API 函数来描述组件逻辑; <script setup>中的导入和顶层变量/函数都能够在模板中直接使用;
  • 组合式 API 的核心思想是直接在函数作用域内定义响应式状态变量,并将从多个函数中得到的状态组合起来处理复杂问题;

setup函数

<template>
  <div id="app">
    <p>名字:{{ name }}</p>
    <p>年龄:{{ age }}</p>
    <p>
      <button @click="sayHello">弹框</button>
    </p>
  </div>
</template>

<script>
export default {
  name: "App",
  // setup是一个函数,有返回值
  setup() {
    // 定义数据
    let name = "张三";
    let age = 18;
    // 定义方法
    function sayHello() {
      alert(`我是${name},我今年${age},你好啊!`); // 使用不需要用this,因为this是undefined,函数作用域;
    }
    / setup函数的返回值是一个对象, 返回值可以直接在模板中使用,不需要点;/
    return { name, age, sayHello }
  }
};
</script>
  • setup是一个函数,有返回值,返回值是一个对象可以直接在模板中使用;
  • 在setup函数中定义的东西,一定要return出去,才能被使用;
  • 组件中所用到的:数据、方法等等,均要配置在setup函数中;
  • setup 中不能使用 this;
  • setup 默认情况下不能使用async/await;
  • 在beforcreatd前执行, 所以this是undefined,所以vue3中没有this;

setup函数的参数

在这里插入图片描述
props对象:

  • 父组件传递过来的所有prop数据,要在组件中的props中接收;
  • 不接收,则存在context对象的attrs中;
  • 它是响应式的,它不能解构,一但解构,结构出的变量会丢失响应式;
1.解构了 props 对象,解构出的变量将会丢失响应性,推荐通过 props.xxx 的形式来使用其中的 props。
export default {
  props: {
    title: String
  },
  setup(props) {  
  // 通过 props.xxx 的形式来使用其中的 props。  
    console.log(props.title)
  }
}
2.必须解构 props 对象,或者需要将某个 prop 传到一个外部函数中并保持响应性,那么可以使用 toRefs()toRef() 这两个工具函数。
import { toRefs, toRef } from 'vue'

export default {
  setup(props) {
    // 将 `props` 转为一个其中全是 ref 的对象,然后解构
    const { title } = toRefs(props)
    // `title` 是一个追踪着 `props.title` 的 ref
    console.log(title.value)

    // 或者,将 `props` 的单个属性转为一个 ref
    const title = toRef(props, 'title')
  }
}
contex上下文对象:
包含了attrs,slots,emit,expose;
emit可以触发自定义事件的执行从而完成子传父通信;
该上下文对象是非响应式的,可以安全地解构;
attrs:一个对象,父组件传递给过来的非 props 属性对象,即未在props中接收的属性。
slots:一个函数,用于访问组件的插槽内容。
emit:一个用于触发自定义事件的函数。
expose:一个用于暴露组件的公共 API 的函数。
export default {
  setup(props, context) {
    // 透传 Attributes(非响应式的对象,等价于 $attrs)
    console.log(context.attrs)

    // 插槽(非响应式的对象,等价于 $slots)
    console.log(context.slots)

    // 触发事件(函数,等价于 $emit),子传父通信;
    console.log(context.emit)

    // 暴露公共属性(函数)
    console.log(context.expose)
  }
}

contex上下文对象:

  • 包含了attrs,slots,emit,expose;
  • emit可以触发自定义事件的执行从而完成子传父通信;
  • 该上下文对象是非响应式的,可以安全地解构;
attrs:一个对象,父组件传递给过来的非 props 属性对象,即未在props中接收的属性。
slots:一个函数,用于访问组件的插槽内容。
emit:一个用于触发自定义事件的函数。
expose:一个用于暴露组件的公共 API 的函数。
export default {
  setup(props, context) {
    // 透传 Attributes(非响应式的对象,等价于 $attrs)
    console.log(context.attrs)

    // 插槽(非响应式的对象,等价于 $slots)
    console.log(context.slots)

    // 触发事件(函数,等价于 $emit),子传父通信;
    console.log(context.emit)

    // 暴露公共属性(函数)
    console.log(context.expose)
  }
}

expose

  • 通过expose方法,可以将数据和方法从组件的内部暴露给组件的使用者。这样,外部的组件就可以直接访问该组件的实例时,访问这些数据或调用这些方法。
  • 要使用expose方法,你需要在expose函数中调用它,并传入一个对象作为参数。该对象中的键值对定义了要暴露的属性或方法。
setup() {
  // 定义要暴露的属性和方法
  const myValue = ref(42);
  const myMethod = () => {
    console.log("Hello from myMethod!");
  };
  // 让组件实例处于`关闭状态`,即不向父组件暴露任何东西
  expose()

  // 让组件实例处于`开启状态`,可以有选择性的向父组件暴露,本组件的属性或方法
  expose({

    myMethod
  });

}

setup语法糖

在这里插入图片描述

<script setup>
import { ref, onMounted } from 'vue'
 
// 响应式状态
const count = ref(0)
 
// 用来修改状态、触发更新的函数
function increment() {
  count.value++
}
 
// 生命周期钩子
onMounted(() => {
  console.log(`计数器初始值为 ${count.value}`)
})
</script>

- setup语法糖,不用在return变量和函数了,在模板中可以直接使用;
- script标签添加setup, 省略 export default{ };


ref函数

  • import { ref } from “vue”; 从vue中引入ref函数;
  • 作用:定义响应式的数据; (多用于基本数据类型)
  • 语法: const xxx = ref (数据)
  • 创建一个包含响应式数据的引用对象,(reference对象,简称ref对象)
  • 模板中使用不需要 .value;
  • js操作需要 .value;
  • 接收的数据可以是:基本类型、也可以是对象类型。
  • 基本类型的数据:响应式依然是靠Object.defineProperty()的get与set完成的数据劫持。
  • 对象类型的数据:内部通过reactive函数实现的。
    在这里插入图片描述
    在这里插入图片描述
<template>
  <div id="app">
    <p>名字:{{ name }}</p>
    <p>年龄:{{ age }}</p>
    <p>工作: {{job.type}}</p> //模板中使用不需要.value
    <p>
      <button @click="changeInfo">改变</button>
    </p>
  </div>
</template>

<script>
import { ref } from "vue"; // 引入ref函数
export default {
  name: "App",
  setup() {
    // 定义基本数据类型
    let name = ref("张三");
    let age = ref(18);
    // 定义引用数据类型
    let job = ref({ type: "前端工程师" });
     // 定义修改ref定义的响应式数据的方法
    function changeInfo() {
          // 基本数据类型
          name.value = "李四";
          age.value = 28;
          // 引用数据类型
          job.value.type = "后端工程师";
    }
    return { name, age, job, changeInfo};
  },
};
</script>

reactive函数

  • import { reactive } from “vue”; 从vue中引入reactive函数;
  • 作用: 定义一个对象或数组类型的响应式数据;(基本类型不要用它,要用ref函数);
    - 语法:const xxx = reactive(源对象);
    接收一个对象或数组,返回一个代理对象(Proxy的实例对象,简称proxy对象);
  • reactive定义的响应式数据是“深层次的”。
  • 内部基于 ES6 的 Proxy 实现,通过代理对象操作源对象内部数据进行操作。
  • js和html中不需要 .value
    在这里插入图片描述
<template>
  <div id="app">
    <p>名字:{{ person.name }}</p>
    <p>工作: {{ person.job.type }}</p>
    <p>
      <button @click="changeInfo">改变</button>
    </p>
  </div>
</template>

<script>
import { reactive } from "vue"; // 引入reactive函数
export default {
  name: "App",
  setup() {
    // 通过reactive函数定义对象或数组;
     let person = reactive({
          name: "张三",
          job: {
            type: "前端工程师",
      },
   });
    // 定义方法
    function changeInfo() {
        // 修改不需要.value
      person.name = "赵四";
      person.job.type = "后端工程师";
    }
    return { person, changeInfo }
  },
};
</script>

ref与reactive对比

在这里插入图片描述
reactive和ref的区别:

  • reactive和ref都是vue3中提供的响应式API,都用于定义响应式数据的;
  • reactive通常用于定义引用数据类型,其本质是基于 Proxy 实现对象代理,所以reactive不能用于定义基本类型数据;
  • ref通常是用于定义基本数据类型,其本质是基于 Object.defineProperty() 重新定义属性的方式实现;

vue2、vue3响应式

  • Proxy
  • Reflect
  • vue3中使用proxy 实现的数据响应式,避免了vue2中不能检测到,对象添加、删除属性和数组通过下标修改元素而无法被检测的问题;
    在这里插入图片描述
    在这里插入图片描述

computed函数

  • computed(箭头函数) ,只获取,参数是一个函数,依靠返回值;
  • computed( { } ),获取和设置,get(){ },set(){ },依靠返回值;set方法的参数是要设置的新属性值;
<!-- 只读取计算属性 -->
<template>
  <div id="app">
    <p>: <input type="text" v-model="person.xing" /> </p>
    <p>: <input type="text" v-model="person.ming" /> </p>
    <p>姓名: {{ fullName }}</p> // 使用计算属性
    <p>姓名: {{ person.fullName }}</p> // 计算属性直接追加到person对象上使用
  </div>
</template>

<script>
import { reactive, computed } from "vue";
export default {
  name: "App",
  setup() {
    // reactive函数;
    let person = reactive({
      xing: "",
      ming: "",
    });
    // 计算属性 fullName, computed函数的参数可以是一个函数, 依靠返回值
    let fullName = computed(() => {
      return person.xing + person.ming;
    });
/直接把计算属性追加到person响应式对象上,直接返回person对象  person.fullName = computed(() => person.xing + person.ming ) /
    // 返回值
    return {
      person,
      fullName, // 返回计算属性
    };
  },
};
</script>

修改计算属性

    <!-- 获取和修改计算属性 -->
    <p>姓名: <input type="text" v-model="fullName" /></p>

    import { reactive, computed } from "vue"; //引入computed函数


    // 完整的计算属性, 获取和修改, get+set
    person.fullName = computed({
        get(){
            return person.firstName + '-' + person.lastName
        },
        set(value){ // value是input输入框的新值
            const nameArr = value.split('-')
            person.firstName = nameArr[0]
            person.lastName = nameArr[1]
        }
})

watch函数

  • watch(参数1,参数2,参数3);
  • wantch( 监视的属性, 处理函数, { immediate:true, deep:true } );
  • 参数1:一个变量,监视单个数据;一个数组,监视多个数据 [ xx, xxx ];一个箭头函数,依靠返回值,精确监视对象某个属性
  • 参数2:一个函数,对监视数据的处理函数;
  • 参数3:一个对象,是否开启深度监视,是否一进入页面就监视 ( 默认immediate:false);
  • 监视reactive定义的响应式数据,1. 不能获取oldValue,oldValue和newValue一致;2. 强制开启了deep:true,设置deep:false无效;
  • ref定义的值类型, 监视不需要.value;
  • ref定义的引用数据类型, 监视需要.value,等价与监视reactive定义的对象(情况三):1.无法获取oldvalue;2.
    newvalue和oldvalue一致;3. 强制开启了deep:true;4. 设置deep:false也无效。
  • ref定义的引用数据类型, 监视不使用 .value,可以使用 deep:true;1.无法获取oldvalue;2.
    newvalue和oldvalue一致;
/ 情况一:监视ref定义的一个响应式数据 /
watch(sum, (newValue, oldValue) => {
    console.log('sum变化了', newValue, oldValue)
}, { immediate:true })
 

/ 情况二:监视多个ref定义的响应式数据 /
监视的数据都放到一个数组中;newVal和oldVal返回的也是数组形式。

watch([sum,msg],(newValue,oldValue)=>{
    console.log('sum或msg变化了',newValue,oldValue)
}) 


/ 情况三:监视reactive定义的响应式数据 /
  1. 无法正确获得oldValue;2. oldValue和newValue一致;3. 
强制开启了深度监视deep:true4. 设置deep:false也无效;

watch(person, (newValue, oldValue)=>{
    console.log('person变化了', newValue, oldValue)     // newValue === oldValue;  true
}, { immediate:true, deep:false }) //此处的deep配置不再奏效


/  情况四:监视reactive定义的响应式数据中的某一个属性 /
    1. 箭头函数,依靠返回值;
 2. 可以获取新值,旧值; 3. 设置deep有效;

watch(() => person.job, (newValue,oldValue) => {
    console.log('person的job变化了', newValue, oldValue)
}, { immediate:true, deep:true }) 


/ 情况五:监视reactive定义的响应式数据中的多个属性 /
    1. 放到一个数组中, 箭头函数依靠返回值;
 2. 可以获取新值,旧值; 3. 设置deep有效; 4.newVal和oldVal返回的也是数组形式。

watch([ () => person.job, () => person.name ], (newValue,oldValue) => {
    console.log('person的job变化了', newValue,oldVal ue)
}, { immediate:true,deep:true })


/ 特殊情况:监视的是reactive定义的对象中的某个对象,即对象嵌套的比较深 /
     1. 开启deep:true才能监测到变化;2. 可以设置deep:false3. 无法获取oldVal,newVal和oldValue一致;
const person = reactive({
  name: '张三',
  age: 18,
  obj: {
    salaray: {
      money: 10000
    }
  }
})

watch(() => person.job, (newValue,oldValue) => {
    console.log('person的job变化了', newValue, oldValue)  // newValue === oldValue;  true

}, { deep:true }) // 设置deep:tue或false有效;

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

watchEffect函数

  • watchEffect ( 监视的回调函数 )
  • 不需要指定监视某个属性, 回调函数中用到那个属性就会监视那个属性;
watch:既要指明监视的属性,也要指明监视的回调。
watchEffect:不用指明监视哪个属性,监视的回调函数中用到哪个属性,那就监视哪个属性。
watchEffect有点类似于computed:
  但computed注重的计算出来的值(回调函数的返回值),所以必须要写返回值。
  而watchEffect更注重的是过程(回调函数的函数体),所以不用写返回值。
//watchEffect所指定的回调中用到的数据只要发生变化,则直接重新执行回调。

watchEffect(()=>{
    const x1 = sum.value // 用到了sum,就监视person;
    const x2 = person.age // 用到了person,就监视person;
    console.log('watchEffect配置的回调执行了')
})

Vue3生命周期

在这里插入图片描述
在这里插入图片描述
vue3.0的生命周期:

  • 除了setup,其他的要加on;
  • beforeDestroy 改名为 beforeUnmount;;
  • destroyed 改名为 unmounted;
  • beforeCreate和created 改为 setup;
  • 其他的生命周期以组合式API的形式写在setup函数中;
  • 写成调用函数的形式,参数是一个箭头函数;
  • 从vue中引入的生命周期是一个函数,传入一个函数代表执行函数;

父子生命周期执行顺序:

  • 挂载阶段

父beforeCreate -> 父created -> 父beforeMount -> 子beforeCreate -> 子created -> 子beforeMount -> 子mounted -> 父mounted

  • 更新阶段

父beforeUpdate -> 子beforeUpdate -> 子updated -> 父updated

  • 销毁阶段

父beforeDestroy -> 子beforeDestroy -> 子destroyed -> 父destroyed

Vue3生命周期
beforeCreate  -> setup()
created       -> setup() 开始创建组件之前,在beforeCreate和created之前执行,创建的是data和method。
beforeMount   -> onBeforeMount() 组件挂载到节点上之前执行的函数。
mounted       -> onMounted() 组件挂载完成后执行的函数。
beforeUpdate  -> onBeforeUpdate() 组件更新之前执行的函数。
updated       -> onUpdated() 组件更新完成之后执行的函数。
beforeUnmount -> onBeforeUnmount() 组件卸载之前执行的函数。
unmounted     -> onUnmounted() 组件卸载完成后执行的函数

keepalive专属 - 被包含在<keep-alive>中的组件,因为生命周期函数不再执行,会多出两个生命周期钩子函数。
activated     -> onActivated() 被激活时执行,刚进入页面时执行。
deactivated   -> onDeactivated() 失活时执行,比如从 A 组件,切换到 B 组件,A 组件消失时执行。

捕获异常 - 用于捕获组件中未处理的 JavaScript 错误
errorCaptured -> onErrorCaptured(error,vm) 当捕获一个来自子孙组件的异常时激活钩子函数。
当组件中出现 JavaScript 错误时,这个错误会沿着组件树向上冒泡直到根组件,如果任何一个组件中定义了 onErrorCaptured 钩子函数,则会调用该函数来捕获错误。
这个钩子函数接收两个参数:error 和 vm。
error: 表示捕获到的 JavaScript 错误对象。
vm: 传递给该钩子函数的组件实例对象,可以通过它来访问到组件的状态和属性。

在 DOM 更新之后执行回调函数 - 作用是等待当前 DOM 更新队列中的所有同步和异步更新完成后,再执行传入的回调函数。
  1.等待当前 DOM 更新队列中的所有同步和异步更新完成后执行回调函数。
  2.用于确保在 DOM 更新之后执行操作,例如获取更新后的 DOM 元素。
await nextTick()
DOM 更新后执行的操作xxx

hook函数

  • 本质是一个函数,把setup函数中使用的Composition API进行了封装。
  • 类似于vue2.x中的mixin。
  • 自定义hook的优势: 复用代码, 让setup中的逻辑更清楚易懂。

toRef、toRefs函数

toRef: 将一个响应式状态或其他可响应式对象转换为一个只读的 ref 对象。

  • toRef( 对象 , '属性名' )
  • 语法:const refObj = toRef( sourceObj, key )
  • sourceObj 是一个响应式状态对象或其他可响应式对象。
  • key 是sourceObj 中一个已存在的属性名,指定要创建引用的属性。
  • toRef 返回一个 ref 对象,该对象具有以下特性:
    1. 当sourceObj的ke属性发生变化时,ref 对象也会相应地变化。
    2. refObj引用对象是只读的,无法直接修改其值。如果需要修改,可以通过修改 sourceObj 中的对应属性来实现。
    torefs:将一个响应式对象转换为一个普通对象,这个普通对象的每个属性都是指向源对象相应属性的 ref。
    toRefs( '对象' )
    语法:toRefs(person)
  • 可以解构使用,不会失去响应式;
  • 通过使用toRefs,可以将响应式对象的属性解构为独立的普通变量,并在模板或其他逻辑中使用它们,同时保持其响应式特性。
  • 扩展:toRefs 与toRef功能一致,但可以批量创建多个 ref 对象。
<template>
  <div>
    <p>person:{{ person }}</p>
    <p>姓名: {{ name }}</p>
    <p>年龄: {{ age }}</p>
    <p>工资: {{ salary }}</p>
    <button @click="name += '三'">姓名++</button>
    <button @click="age++">年龄++</button>
    <button @click="salary++">工资++</button>
  </div>
</template>

<script>
import { reactive, toRef, toRefs } from "vue"; // 引入 toRef, toRefs函数
export default {
  name: "App",
  setup() {
    let person = reactive({
      name: "张三",
      age: 18,
      job: {
        type: "前端程序员",
        salary: 15,
      },
    });
    return {
      person, // 响应式的person对象
      name: toRef(person, "name"), // 将响应式对象中的某个属性单独提供给外部使用时。
      age: toRef(person, "age"),
      salary: toRef(person.job, "salary"),
      / ...toRefs(person)    解构toRefs函数处理的数据,name,age,job;把整个响应式的person对象解构出来的属性,都暴露给外边使用,也不会失去响应式; /
      
    };
  },
};
</script>

shallowReactive函数 与 shallowRef 函数

shallow:浅的,浅层次的;

  • shallowReactive:只处理对象第一层属性的响应式(浅响应式)。
  • shallowRef:只处理基本数据类型的响应式, 不进行对象的响应式处理。
  • 什么时候使用?
    1. 如果有一个对象数据,结构比较深, 但变化时只是外层属性变化 ===> shallowReactive。
    2. 如果有一个对象数据,只读它的属性,或后续功能不会修改该对象中的属性,而是生新的对象来替换 ===> shallowRef。

eadonly函数 与 shallowReadonly函数

  • readonly:让一个响应式数据变为只读的(深只读)。
  • shallowReadonly:让一个响应式数据变为只读的(浅只读, 只有第一层可读, 深层次的可以修改)。
  • 应用场景: 不希望数据被修改时。
    let person = reactive({
      name: "张三",
      age: 18,
      job: {
        type: "前端程序员",
        salary: 15,
      },
    });
    person = readonly(person) // 整个person对象只读
    person = shallowReadonly(person) // 只有person对象第一层只读name,age;深层次job对象中的数据可以修改

toRaw函数 与 markRaw函数

raw:未加工的,原始的;mark:标记,记号;
toRaw:

  • 作用:将一个由reactive生成的响应式对象转为普通对象。
  • 使用 toRaw,可以获取到原始的非响应式对象,使其不受代理的影响。
  • 使用场景:用于读取响应式对象对应的普通对象,对这个普通对象的所有操作,不会引起页面更新。

markRaw:

  • 作用:标记一个响应式的对象,使其永远失去响应式。
  • 使用 markRaw 可以将对象标记为非响应式,从而避免其被 Vue 的响应式系统追踪和观察。 应用场景:
    1. 有些值不应被设置为响应式的,例如复杂的第三方类库等。
    2. 当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能。

customRef函数

custom:自定义;track:追踪;trigger:触发;delay:延迟;

  • 自定义ref函数,返回的ref对象是响应式的;
  • customRef( 箭头函数 );
  • 箭头函数的参数:参数1:track函数;参数2:trigger函数;
    1. track( ):追踪依赖数据的变化;
    2. trigger( ):触发更新;
  • 箭头函数需要返回 get函数和set函数;
    1. get函数,用于获取 ref 的当前值。在读取 ref.value 时会调用该函数。
    2. set函数,用于设置 ref 的新值。在给 ref.value 赋值时会调用该函数。
  • customRef 允许我们创建一个定制化的 ref,可以自定义其读取和修改的行为。
  • 作用:创建一个自定义的 ref,并对其依赖项跟踪和更新触发进行显式控制。
import { customRef } from 'vue'

const myCustomRef = customRef((track, trigger) => {
  // 定义初始值
  let value = 0
  // 定义增加初始值的方法

  const increment = () => {
    value++
    trigger() // 触发更新
  }
  // 定义减少初始值的方法

  const decrement = () => {
    value--
    trigger() // 触发更新
  }
  // 返回get和set方法,返回increment方法和decrement方法;

  return {
    get() {
      track() // 追踪依赖的数据,value;
      return value
    },
    set(newValue) {
      value = newValue
      trigger() // 触发更新
    },
    increment,
    decrement
  }
})

const count = myCustomRef.value

console.log(count) // 0

myCustomRef.increment()
console.log(count) // 1

myCustomRef.decrement()
console.log(count) // 0

myCustomRef.value = 5
console.log(count) // 5

vue3父子通信

defineProps 和 defineEmits 都是只能在 <script setup> 中使用的编译器宏;
不需要导入,且会随着 <script setup> 的处理过程一同被编译掉。

// 父组件
<template>
  <div>父组件:{{ count }}</div>
  <hr />
  <Son :count="count" @cutomFn="Fn" /> // 传递自定义属性,传递自定义事件;
</template>

<script setup>
import { ref } from 'vue'
import Son from '@/components/Son.vue'
const count = ref(100)
// 自定义事件的处理函数,val参数就是子组件传递过来的值;
const Fn = (val) => {
  count.value = val
}
---------------------------------------------------------------------------
// 子组件
<template>
  <div>子组件:{{ count }}</div>
  <button @click="emitCustomFn">改变</button>
</template>

<script setup>
// 接收父组件传递过来的自定义属性; { }
const props = defineProps({
  count: {
    type: Number,
  }
})
// 接收父组件传递过来的,要触发的自定义事件;[ '要触发的自定义事件名' ]
const emit = defineEmits(['cutomFn']) 
// 触发自定义事件,并传值
const emitCustomFn = () => {
  emit('cutomFn', props.count + 1) // props.xxx
}
</script>
  • 父传子

    • 通过defineProps()函数来配置props选项,接收父组件传递的数据;
    • const props = defineProps( { } )
    • props接收到传递过来的值后,可以再模板中直接使用;但是再js中使用需要 props.xxx ;
      在这里插入图片描述
  • 子传父

    • 通过defineEmits()函数来配置props选项,接收父组件传递的数据;
    • const emit = defineEmits( [ '自定义事件名' ] ) // 数组内,事件名加引号;
      在这里插入图片描述

provide和inject 依赖注入

在这里插入图片描述

  • 实现祖孙组件间的通信;
  • provide 提供数据;==> provide( “数据名”, 传递的数据);
  • inject 接收数据;inject( “数据名” );

作用:实现祖孙组件之间通信 (跨级通信)
过程:祖先组件有一个 provide 选项来提供数据,后代组件有一个 inject 选项来开始接收数据。

祖先组件中:
import { provide } from 'vue

setup(){
	......
    let car = reactive({name:'奔驰',price:'40万'})
    provide('car',car)
    ......
}


后代组件中:
import { inject } from 'vue
setup(props,context){
	......
    const car = inject('car')
    return {car}
	......
}

模板引用ref

  • 通过ref标识获取真实dom对象或组件实例对象;
  • 要mounted中使用;
    • 调用ref函数生成一个ref对象;
    • 通过ref标识绑定ref对象到标签;

给dom绑定ref

<template>
  <div ref="divRef">App</div>  // 3. 给dom绑定ref
</template>

<script setup>
import { ref } from 'vue' // 1. 引入ref函数
const divRef = ref(null) // 2. 创建divRef变量,并赋值为null
</script>

给组件绑定ref

/ 父组件 / 
<template>
  <Ref ref="sonRef" /> //  3. 给组件绑定ref
</template>

<script setup>
import Ref from './components/ref.vue' // 引入组件
import { ref, onMounted } from 'vue' // 1. 引入ref和onMounted函数
const sonRef = ref(null) // 2. 创建sonRef变量,赋值为null
// 5. 在父组件的mounted中使用子组件的属性和方法
onMounted(() => {
  console.log(sonRef.value.count)
  sonRef.value.sayHello()
})
</script>

/ 子组件 /
<template>
  <div>
    Son组件
  </div>
</template>

<script setup>
import { ref } from 'vue'
const count = ref(100)
const sayHello = () => {
  console.log('hello')
}
// 4. 通过defineExpose方法暴露出去该组件的属性和方法
defineExpose({
  count,
  sayHello
})
</script>

defineExpose()

  • defineExpose( { } );
  • 默认情况下在<script setup>语法糖下组件内部的属性和方法是不开放给父组件访问的,是关闭的;
  • 可以通过defineExpose编译宏指定哪些属性和方法允许访问;
  • 作用:用于暴露组件的内部方法和属性给父组件使用。

defineOptions

  • 用来定义 Options API 的选项;
  • 用来直接在 <script setup> 中声明组件选项,而不必使用单独的 <script> 块;
  • 仅支持vue3.3+;
defineOptions({
  name: 'Son' // 组件名
})

判断响应式数据的方法

  • import { isRef,isReactive,isReadonly,isProxy } from ‘vue’
  • 返回的都是布尔值;
  • isRef:检查一个值是否为一个 ref 对象;
  • isReactive:检查一个对象是否是由 reactive 创建的响应式代理;
  • isReadonly:检查一个对象是否是由 readonly 创建的只读代理;
  • isProxy:检查一个对象是否是由 reactive 或者 readonly 方法创建的代理;

新组件

Fragment 标签

fragment:碎片,片段;

  • 在Vue2中: 组件必须有一个根标签;
  • 在Vue3中:组件可以没有根标签, 内部会将多个标签包含在一个Fragment虚拟元素中;
  • 好处: 减少标签层级, 减小内存占用;

Teleport 标签

fragment:传送;

  • 将组件的内容渲染到指定的位置;
  • to属性: 选择器;
  • 在组件的模板中定义的某个位置,将内容渲染到 DOM 中的其他位置。
  • 在遵循组件嵌套规则的情况下,将内容渲染到组件树之外的地方,例如 <body> 元素之下。
<button @click="isShow = true">点击弹窗</button>
<teleport to="body">
	<div v-if="isShow" class="mask">
		<div class="dialog">
			<h3>我是一个弹窗</h3>
			<button @click="isShow = false">关闭弹窗</button>
		</div>
	</div>
</teleport>

Suspense标签

suspense:悬念;

  • 异步依赖。
  • <Suspense> 组件有两个插槽:#default 和 #fallback。
  • 用于处理异步组件的加载状态和错误处理;
  • 等待异步组件时渲染一些额外内容,让应用有更好的用户体验。
方法1通过defineAsyncComponent(() => import('路径'))import {defineAsyncComponent} from 'vue' // 引入定义异步组件的函数
const Child = defineAsyncComponent(()=>import('./components/Child.vue'))
import() // 异步引入

方法2:使用Suspense包裹组件,通过插槽,配置好 #default 与 #fallback;
<template>    	
    <Suspense>
	<template #default>
            <h1>这是异步组件的内容...</h1>	
	</template>
	<template #fallback>
            <h1>异步组件正在加载中...</h1>	    
	</template>
    
</Suspense>
	
</template>

#default 插槽用于显示异步组件加载完成后的内容,即 <h1>这是异步组件的内容</h1>。
#fallback 插槽用于显示异步组件加载过程中的占位符,即 <h1>异步组件正在加载中...</h1>/ 当异步组件加载过程中,<Suspense> 组件会显示 #fallback 插槽中的内容,待异步组件加载完成后,会切换到显示 #default 插槽中的内容。/

其他

全局API的转移

  • Vue 2.x 有许多全局 API 和配置
    • 例如:注册全局组件、注册全局指令等;
//注册全局组件
Vue.component('MyButton', {
  data: () => ({
    count: 0
  }),
  template: '<button @click="count++">Clicked {{ count }} times.</button>'
})

//注册全局指令
Vue.directive('focus', {
  inserted: el => el.focus()
}
  • Vue3.0中对这些API做出了调整:
    • 将Vue.xxx调整到应用实例(app)上;
      在这里插入图片描述

其他改变

  • data选项应始终被声明为一个函数。
  • 移除键盘事件的keyCode作为 v-on 的修饰符,同时也不再支持config.keyCodes;
  • 移除v-on.native修饰符,可以直接给组件绑定原生事件;
  • 移除过滤器 filter;
    • 过滤器虽然这看起来很方便,但它需要一个自定义语法,打破大括号内表达式是 “只是 JavaScript” 的假设!
    • 建议用方法调用或计算属性去替换过滤器。
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值