一、vue3的优点

1、vue3的优点

1、源码体积得到优化:
(1) API减少,移除一些冷门API,如filter、inline-template等;
(2)引入tree-shaking 减少打包体积;

2、源码的升级:
(1)Vue3使用Proxy进行数据劫持,可以很好的规避vue2使用Object.defineProperty进行数据劫持带来的缺陷;

3、拥抱TypeScript:
(1) 更好的支持Ts(typescript)

4、高级给与:
(1)暴露了更底层的API和提供更先进的内置组件;

5、 组合API(Composition API):
(1) 能够更好的组织逻辑,封装逻辑,复用逻辑;

二、常用的API

1、setup(Composition API)

1、setup是vue3中的一个全新的配置项,值为一个函数;

2、setup是所有CompositionAPI(组合API)的基础,组件中所用到的数据、方法等都需要在setup中进行配置;
(1):若返回一个对象,则对象中的属性、方法,均可以在模板中直接使用;

(2):若返回一个渲染函数:则可以自定义渲染内容;

setup的两个注意点:

1、setup执行时机,在beforeCreate之前执行一次,this是undefined;

2、setup的参数:

(1):props:指为对象,包含组件外部传递过来,且组件内部声明接收了的属性。

(2):context:上下文对象

attrs:值为对象,包含组件外部传递过来,但没有在props配置中声明的属性,相当于this.$attrs;

slots:收到的插槽内容,相当于this.$slots;

emit:分发自定义事件的函数,相当于this.$emit。

2、生命周期(Composition API)

vue3中的生命周期和vue2的区别:

beforeCreate=>setup()(创建实例前)

created=>setup() (创建实例后)

beforeMount=>onBeforeMount ( 挂载DOM前)

mounted=>onMounted ( 挂载DOM后)

beforeUpdate=>onBeforeUpdate (更新组件前)

updated=>onUpdated (更新组件后)

beforeUnmount=>onBeforeUnmount (卸载销毁前)

unmounted=>onUnmounted (卸载销毁后)

3、ref函数和reactive函数用法和区别(Composition API)

1、ref

特点

(1)ref的参数一般是基本数据类型,也可以是对象类型

(2)如果参数是对象类型,其实底层的本质还是reactive,系统会自动将ref转换为reactive,例如
==ref(1) => reactive({value:1})=
(3)在模板中访问ref中的数据,系统会自动帮我们添加.value,在JS中访问ref中的数据,需要手动添加.value

(4)ref的底层原理同reactive一样,都是Proxy


<script setup>
import { ref } from 'vue'
 
const count = ref(0)
 
function increment() {
  count.value++
}
</script>
 
<template>
  <button @click="increment">
    {{ count }} <!-- 无需 .value -->
  </button>
</template>

2、reactive

特点

(1)reactive的参数一般是对象或者数组,他能够将复杂数据类型变为响应式数据。
(2)reactive的响应式是深层次的,底层本质是将传入的数据转换为Proxy对象


<template>
  <h2>名称:{{name.title}}</h2>
  <h2>主要爱好:{{job.hobby[0].type}}</h2>
  <button @click="changeName">修改姓名和爱好</button>
</template>
<script>
import { ref,reactive } from 'vue' // 按需引入ref函数
export default {
  name: 'App',
  setup () {
    let name = ref({ // ref定义对象类型响应式变量
      title:'赵丽颖'
    }) 
    let job = reactive({ // reactive定义对象类型响应式变量
      type:'演员',
      hobby:[
        {
          type:'演戏',
          degree:'✨✨✨✨✨'
        },
        {
          type:'看书',
          degree:'✨✨✨'
        }
      ],
    })
    function changeName () {
      console.log('name',name)
      name.value.title = '刘亦菲' // 对ref定义的响应式数据进行操作,需要使用.value
      console.log('job',job.hobby[0])
      job.hobby[0].type = '运动' // 对reactive定义的响应式数据进行操作,无需使用.value,且是深层次响应 
      console.log('job',job.hobby[0])
    }
    // 将变量和函数返回,以便在模版中使用
    return {
      name,
      job,
      changeName
    }
  }
}
</script>

3、ref和reactive的区别

1、从定义数据角度对比:

ref用来定义:基本数据类型;

reactive用来定义:对象(或数组)类型数据;

备注:ref也可以用来定义对象或数组类型数据,它内部会自动通过reactive转为代理对象;

2、从原理角度对比:

ref通过Object.defineProperty()的get和set来实现响应式;

reactive通过使用Proxy来实现响应式(数据劫持),并通过Reflect操作源对象内部的数据。

3、从使用角度对比:

ref定义的数据:操作数据需要.value,读取数据时模板中直接读取不需要.value;

reactive定义的数据:操作数据与读取数据均不需要.value;

4、计算属性(computed)和侦听属性(watch)(Composition API)

computed和watch都是vue框架中的用于监听数据变化的属性,都能在数据改变时,针对改变的相关数据做成相应的变化

1、computed

(1)计算属性的优点:
计算属性是基于缓存来实现的,无论你后端调用十次还是几百次,只要计算属性依赖的相关值没有发生变化,那计算属性就可以通过缓存读取。例子如下:

<script setup lang="ts">
import { reactive, computed } from "vue"


const allName = reactive({
  userName1:'小',  //姓
  userName2:'明',   //名
  age:20,
})
const userName = computed(()=>{
  console.log("我是计算属性调用的");
  return allName.userName1 +
    allName.userName2+"年龄"+allName.age
})
 
const userAllName = function(){
 console.log("我是方法调用的");
 return allName.userName1 + allName.userName2+"年龄"+allName.age
}

</script>

<template>
 
<!--下面是通过方法得出的名字-->
  <div>{{userAllName()}}</div>
  <div>{{userAllName()}}</div>
  <div>{{userAllName()}}</div>
  <div>{{userAllName()}}</div>
  <div>{{userAllName()}}</div>
  <div>{{userAllName()}}</div>
  <div>{{userAllName()}}</div>
  <div>{{userAllName()}}</div>
 
<!--下面是通过计算属性得出的名字-->
  <div>{{userName}}</div>
  <div>{{userName}}</div>
  <div>{{userName}}</div>
  <div>{{userName}}</div>
  <div>{{userName}}</div>
  <div>{{userName}}</div>
  <div>{{userName}}</div>
</template>

从上面对应的浏览器中打印的我们可以看出,方法和计算属性调用了八次,但是与方法不一样的是,计算属性就打印了一次,就好像就第一次调用了这个函数一样,后面就没调用了。这就是计算属性的缓存,只要其依赖的值不发生相关的变化,那么无论你计算属性调用了好多次,但最终就只是执行一次。
在这里插入图片描述
(2)可写的计算属性
当计算属性是只读的,你试着修改他的话,会收到一个警告,例如:

<script setup lang="ts">
import { ref, computed } from "vue"

const count = ref(1)
const plusOne = computed(() => count.value + 1)

plusOne.value++

</script>

<template>
  <div>
    <p>{{ count }}</p>
    <p>{{ plusOne }}</p>
  </div>
</template>

值既没有发生变化还会得到警告,如图:

在这里插入图片描述

正确的可写计算属性代码如下:

<script setup lang="ts">
import { ref, computed } from "vue"

const count = ref(1)
/*利用get和set函数*/
const plusOne = computed({
  get: () => count.value + 1, 
   set: (val) => {
    count.value = val - 1
  }
})

plusOne.value++
</script>

<template>
  <div>
    <p>{{ count }}</p>
    <p>{{ plusOne }}</p>
  </div>
</template>

结果如图:
在这里插入图片描述

2、watch

默认是懒侦听的,即仅在侦听源发生变化时才执行回调函数。

第一个参数是侦听器的源。这个来源可以是以下几种:

一个函数,返回一个值
一个 ref
一个响应式对象
…或是由以上类型的值组成的数组

第二个参数是在发生变化时要调用的回调函数。这个回调函数接受三个参数:新值、旧值,以及一个用于注册副作用清理的回调函数。
第三个可选的参数是一个对象,支持以下这些选项:

immediate:在侦听器创建时立即触发回调。第一次调用时旧值是 undefined。
deep:如果源是对象,强制深度遍历,以便在深层级变更时触发回调。
flush:使侦听器在侦听器回调中能访问被 Vue 更新之后的DOM。

<script setup lang="ts">
import { ref, watch } from "vue"

const count = ref(0)

/**
 * 挑战 1: Watch 一次
 * 确保副作用函数只执行一次
 * 使用stopWatch停止侦听器
*/
const stopWatch=watch(count, () => {
  console.log("Only triggered once")
  stopWatch()
})
count.value = 1
setTimeout(() =>{ count.value = 2})

/**
 * 挑战 2: Watch 对象
 * 确保副作用函数被正确触发
 * 使用deep深层次监听
*/
const state = ref({
  count: 0,
})

watch(state, () => {
  console.log("The state.count updated")
},{
      deep:true,
      })

state.value.count = 2

/**
 * 挑战 3: 副作用函数刷新时机
 * 确保正确访问到更新后的`eleRef`值
 * 一般侦听器是在Vue组件更新之前被调用,所以要使用flush访问被Vue更新后的DOM
*/

const eleRef= ref()
const age = ref(2)
watch(age, () => {
  console.log(eleRef.value)
},{ flush: 'post'})
age.value = 18

</script>

<template>
  <div>
    <p>
      {{ count }}
    </p>
    <p ref="eleRef">
      {{ age }}
    </p>
  </div>
</template>

watch和watchEffect
相同点
1、两者都可以监听data属性变化
2、watch 与 watchEffect 在手动停止侦听、清除副作用 (将 onInvalidate 作为第三个参数传递给回调)、刷新时机和调试方面有相同的行为。

区别
1、watch不加immediate初始值监听不到,watchEffect一开始就能监听到;
2、watch需要明确监听哪个属性;
3、watchEffect会根据其中的属性,自动监听其变化;
4、watcheffect初始化时,一定会执行一次(收集要监听的数据,不然不知道监听的是什么),watch只有你设置了初始化监听才会监听;
5、watchEffect获取不到更改前的值,而watch可以同时获取更改前和更改后的值;

<script setup lang="ts">
import {ref,reactive,watch,watchEffect} from 'vue'
			//数据
			let sum = ref(0)
			let person = reactive({
				name:'张三',
				age:18,
				job:{
					j1:{
						salary:20
					}
				}
			})

			//监视
			
            /*watch(sum,(newValue,oldValue)=>{
				console.log('sum的值变化了',newValue,oldValue)
			},{immediate:true})*/ 

			watchEffect(()=>{
				const x1 = sum.value
				const x2 = person.job.j1.salary
				console.log('watchEffect所指定的回调执行了')
			})
      function add(){
       sum.value=sum.value+1
      }
		
</script>
<template>
  <div>
    <p>
      {{sum}}
    </p>
    <button @click="add">
      点我加一
    </button>
  </div>
</template>

在这里插入图片描述

5、nextTick(全局API)

在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM,例子

<script setup>
import { ref,nextTick } from "vue"

const count = ref(0)
const counter = ref(null)

async function increment(){
  count.value++
  console.log(counter.value.textContent )
  await nextTick()
  console.log(+counter.value.textContent ===1,counter.value.textContent )
}
</script>

<template>
  <button ref="counter" @click="increment">
    {{ count }}
  </button>
</template>

在这里插入图片描述

6、props(选项式API)

主要用在组件的父组件向子组件的传值,主要有两大类ts语法和非ts语法
default:默认值;
type:类型;
required:是否必传;
validator:props验证;

1、非ts语法

const props = defineProps({
  // 基础类型检查
  // (给出 `null` 和 `undefined` 值则会跳过任何类型检查)
  propA: Number,
  // 多种可能的类型
  propB: [String, Number],
  // 必传,且为 String 类型
  propC: {
    type: String,
    required: true
  },
  // Number 类型的默认值
  propD: {
    type: Number,
    default: 100
  },
  // 对象类型的默认值
  propE: {
    type: Object,
    // 对象或数组的默认值
    // 必须从一个工厂函数返回。
    // 该函数接收组件所接收到的原始 prop 作为参数。
    default(rawProps) {
      return { message: 'hello' }
    }
  },
  // 自定义类型校验函数
  propF: {
    validator(value) {
      // The value must match one of these strings
      return ['success', 'warning', 'danger'].includes(value)
    }
  },
  // 函数类型的默认值
  propG: {
    type: Function,
    // 不像对象或数组的默认,这不是一个工厂函数。这会是一个用来作为默认值的函数
    default() {
      return 'Default function'
    }
  }
})

2、ts语法

interface Props {
    // id
    id:string
    // 宽度
    width: number,
    // 高度?:
    height?: number,
    // 是否初始化
    init?: boolean,
    // 子项
    list?: Array<ganttChartItem>,
    // 是否显示描述
    desc?:string
    // 描述颜色
    descColor?:string
}
const props = withDefaults(defineProps<Props>(), {
    // 高度默认20
    height: 20,
    // 默认初始化
    init: true,
    // 子项默认为空
    list: () => [],
    // 是否显示描述
    desc: '',
    // 描述颜色
    descColor: ''
});

通过定义interface接口来规范props的属性的格式
?:表示可选属性
定义默认属性值时需要使用withDefaults

7、toRefs(Composition API)

toref函数和toref函数与ref函数的区别:

toRef 函数会与源数据交互,修改响应式数据会造成源数据的修改,但是他的修改不会造成视图层数据的更新;

ref 函数可以将对象里面的属性值变成响应式的数据,修改响应式数据,是不会影响到源数据,但是视图层上的数据会被更新

为啥要使用toRefs函数,原因:

Vue.js中,我们同样解构/扩展“响应式”对象,但它会失去响应性。toRefs函数才能保证解构/扩展不丢失响应性

一般都会使用toRefs函数而不是toRef函数,原因:

(1)toRefs 函数用于批量设置多个数据为相应是数据。

(2)toRefs 函数还可以与其他响应式数据相交互,更加方便处理视图层数据。

toRef函数的使用

//toRef 函数有两个参数
toRef(操作对象, 对象属性)

toRefs函数的使用

<script setup lang="ts">
import { reactive ,toRefs } from "vue"

function useCount() {
  const state = reactive({
    count: 0,
  })

  function update(value: number) {
    state.count = value
  }

  return {
     ...toRefs(state),
    update,
  }
}

// Ensure the destructured properties don't lose their reactivity  ...toRefs(state)
const {  count , update } =useCount()

</script>

<template>
  <div>
    <p>
      <span @click="update(count-1)">-</span>
      {{ count }}
      <span @click="update(count+1)">+</span>
    </p>
  </div>
</template>

8、vue3中的响应式原理

1、vue2中的响应式原理

基本原理

vue2利用的是原生js下边的Object.defineProperty()进行数据劫持,在通过里面的getter和
setter方法,进行查看和数据的修改,通过发布、订阅者模式进行数据与视图的响应式。

let person = {
            name:'路飞',
            age:18
        }

let p = {}
Object.defineProperty(p, "name", {
    get(){      //有人读取name时调用
        return person.name;
    },
    set(value){      //有人修改name时调用
        console.log("有人修改了name属性")
        person.name = value
    }
});

Object.defineProperty(p, "age", {
    get(){      //有人读取age时调用
        return person.age;
    },
    set(value){      //有人修改age时调用
        console.log("有人修改了age属性")
        person.age = value
    }
});

在这里插入图片描述
vue2给对象增加和修改新属性用 $set;
vue2想删除已有的属性可以用 $delete;

2、vue3中的响应式原理

基本原理

1、对于基本数据类型来说,响应式依然是靠Object.defineProperty()的get和set来完成的

2、对于对象类型的数据:

通过Proxy代理:拦截对象中任意属性的变化,包括属性值得读写、添加、删除等操作等…
通过Reflect反射函数进行操作

new Proxy(data,{
  //拦截读取属性值
  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)
  }
})

总结

在Vue2中,数据响应式主要借助Object.defineProperty()来实现,存在的缺陷是无法操作数据的增加和删除,在Vue3中,数据响应式主要借助proxy和Reffect配合实现,可以做到实现数据的增删改查。

9、自定义指令

1、什么是指令

指令系统是计算机硬件的语言系统,也叫机器语言,它是系统程序员看到的计算机的主要属性。因此指令系统表征了计算机的基本功能决定了机器所要求的能力

在vue中提供了一套为数据驱动视图更为方便的操作,这些操作被称为指令系统

我们看到的v- 开头的行内属性,都是指令,不同的指令可以完成或实现不同的功能

除了核心功能默认内置的指令 (v-model 和 v-show),Vue 也允许注册自定义指令

指令使用的几种方式:

//会实例化一个指令,但这个指令没有参数
v-xxx

// – 将值传到指令中
v-xxx="value"

// – 将字符串传入到指令中,如v-html="'<p>内容</p>'"
v-xxx="'string'"

// – 传参数(arg),如v-bind:class="className"
v-xxx:arg="value"

// – 使用修饰符(modifier
v-xxx:arg.modifier="value"

2、怎么实现

注册一个自定义指令有全局注册与局部注册

全局注册注册主要是用过Vue.directive方法进行注册

Vue.directive第一个参数是指令的名字(不需要写上v-前缀),第二个参数可以是对象数据,也可以是一个指令函数

// 注册一个全局自定义指令 `v-focus`
Vue.directive('focus', {
  // 当被绑定的元素插入到 DOM 中时……
  inserted: function (el) {
    // 聚焦元素
    el.focus()  // 页面加载完成之后自动让输入框获取到焦点的小功能
  }
})

局部注册通过组件options选项中设置directive属性

directives: {
  focus: {
    // 指令的定义
    inserted: function (el) {
      el.focus() // 页面加载完成之后自动让输入框获取到焦点的小功能
    }
  }
}

使用新自定义指令

  <input v-focus="state" type="text">

指令钩子

const myDirective = {
  // 在绑定元素的 attribute 前
  // 或事件监听器应用前调用
  created(el, binding, vnode, prevVnode) {
    // 下面会介绍各个参数的细节
  },
  // 在元素被插入到 DOM 前调用
  beforeMount(el, binding, vnode, prevVnode) {},
  // 在绑定元素的父组件
  // 及他自己的所有子节点都挂载完成后调用
  mounted(el, binding, vnode, prevVnode) {},
  // 绑定元素的父组件更新前调用
  beforeUpdate(el, binding, vnode, prevVnode) {},
  // 在绑定元素的父组件
  // 及他自己的所有子节点都更新后调用
  updated(el, binding, vnode, prevVnode) {},
  // 绑定元素的父组件卸载前调用
  beforeUnmount(el, binding, vnode, prevVnode) {},
  // 绑定元素的父组件卸载后调用
  unmounted(el, binding, vnode, prevVnode) {}
}

钩子参数
指令的钩子会传递以下几种参数:

el:指令绑定到的元素。这可以用于直接操作 DOM。

binding:一个对象,包含以下属性。

(1)value:传递给指令的值。例如在 v-my-directive=“1 + 1” 中,值是 2。
(2)oldValue:之前的值,仅在 beforeUpdate 和 updated 中可用。无论值是否更改,它都可用。
(3)arg:传递给指令的参数 (如果有的话)。例如在 v-my-directive:foo 中,参数是 “foo”。
(4)modifiers:一个包含修饰符的对象 (如果有的话)。例如在 v-my-directive.foo.bar 中,修饰符对象是 { foo: true, bar: true }。
(5)instance:使用该指令的组件实例。
(6)dir:指令的定义对象。
vnode:代表绑定元素的底层 VNode。

prevNode:之前的渲染中代表指令所绑定元素的 VNode。仅在 beforeUpdate 和 updated 钩子中可用。

3、应用场景

例子一:自定义防抖点击指令

<script setup lang='ts'>
/**
 * 实现以下自定义指令
 * 确保在一定时间内当快速点击按钮多次时只触发一次点击事件
 * 你需要支持防抖延迟时间选项, 用法如 `v-debounce-click:ms`
 *
*/
// 简易版-防抖函数
const debounce = (func: Function, delay: number) => {
  let timer: number | undefined;
  return (...args) => {
    clearTimeout(timer);  
    timer =setTimeout(() => {
      func.apply(null, args);
    }, delay);
  };
};

const VDebounceClick = {
  created(el, binding) {
    const { value: cb, arg: delay } = binding
    const debounced = debounce(cb, delay)   
    el.addEventListener('click', debounced)
    el._debounced = debounced
  },
  
  unmounted(el) {
    if (el._debounced) {
      el.removeEventListener('click', el._debounced)
      el._debounced = null
    }
  },
}

function onClick() {
  console.log("Only triggered once when clicked many times quickly")
}

</script>

<template>
  <button v-debounce-click:1000="onClick">
    Click on it many times quickly
  </button>
</template>

例子二:自定义获取焦点指令

<script setup lang='ts'>
import { ref } from "vue"

const state = ref(false)

/**
 * 实现一个自定义指令,让元素获取焦点
 * 确保当切换`state`时,元素随着状态值获取/失去焦点
 *
*/
const VFocus = {
  mounted(el,binding) {
    binding.value ? el.focus() : el.blur();
  },
  updated(el,binding) {
    binding.value ? el.focus() : el.blur();
  }
}

setInterval(() => {
  state.value = !state.value
}, 2000)

</script>

<template>
  <input v-focus="state" type="text">
</template>

例子三:一键copy功能

import { Message } from 'ant-design-vue';
 
const vCopy = { //
  /*
    bind 钩子函数,第一次绑定时调用,可以在这里做初始化设置
    el: 作用的 dom 对象
    value: 传给指令的值,也就是我们要 copy 的值
  */
  bind(el, { value }) {
    el.$value = value; // 用一个全局属性来存传进来的值,因为这个值在别的钩子函数里还会用到
    el.handler = () => {
      if (!el.$value) {
      // 值为空的时候,给出提示,我这里的提示是用的 ant-design-vue 的提示,你们随意
        Message.warning('无复制内容');
        return;
      }
      // 动态创建 textarea 标签
      const textarea = document.createElement('textarea');
      // 将该 textarea 设为 readonly 防止 iOS 下自动唤起键盘,同时将 textarea 移出可视区域
      textarea.readOnly = 'readonly';
      textarea.style.position = 'absolute';
      textarea.style.left = '-9999px';
      // 将要 copy 的值赋给 textarea 标签的 value 属性
      textarea.value = el.$value;
      // 将 textarea 插入到 body 中
      document.body.appendChild(textarea);
      // 选中值并复制
      textarea.select();
      // textarea.setSelectionRange(0, textarea.value.length);
      const result = document.execCommand('Copy');
      if (result) {
        Message.success('复制成功');
      }
      document.body.removeChild(textarea);
    };
    // 绑定点击事件,就是所谓的一键 copy 啦
    el.addEventListener('click', el.handler);
  },
  // 当传进来的值更新的时候触发
  componentUpdated(el, { value }) {
    el.$value = value;
  },
  // 指令与元素解绑的时候,移除事件绑定
  unbind(el) {
    el.removeEventListener('click', el.handler);
  },
};
 
export default vCopy;

10、自定义修饰符

主要是用来进行数据表单的双向绑定的
添加到组件 v-model 的修饰符将通过 modelModifiers prop 提供给组件。
下面是自定义修饰符capitalize,自动将 v-model 绑定输入的字符串值首字母转为大写:
父组件:

<template>
    <div>
        <Renderer v-model.capitalize="data"/> {{modelValue}}
    </div>
</template>

<script setup>
import  Renderer from  "./Comp.vue";
import { ref } from "vue";
   const  data = ref('');
</script>

子组件:

<script setup>
import {ref,computed} from 'vue'
const props = defineProps({
    modelValue: String,
    modelModifiers: {
      default: () => ({})
    }
});
const emit = defineEmits(['update:modelValue']);
const capitalize=(value)=>{
   if (props.modelModifiers.capitalize) {
    return value.charAt(0).toUpperCase() + value.slice(1)
    }
}
const updateValue = (e) => {
  let value=capitalize(e.target.value)
    emit('update:modelValue',value);
}
//使默认值也能相应变化
const modelValue = computed({
  get: () => capitalize(props.modelValue),
  set: (v) => emitValue(v),
});
</script>

<template>
  <input type="text" :value="modelValue" @input="updateValue" />
</template>

11、vue的vueuse库(工具库)

vueuse工具库链接

1、实现一个计数器

<script setup lang='ts'>
import { ref } from 'vue'
interface UseCounterOptions {
  min?: number
  max?: number
}

/**
 * 实现计数器函数,确保功能正常工作
 * 1. 加 (+)
 * 2. 减 (-)
 * 3. 重置 
 * 4. 最小值 & 最大值 选项支持
*/
function useCounter(initialValue = 0, options: UseCounterOptions = {}) {
 const count = ref(initialValue)
  const {max, min} = options
  const inc = () => count.value = Math.min(max, count.value + 1)
  const dec = () => count.value = Math.max(min, count.value - 1)
  const reset = () => (count.value = initialValue)

  return { count, inc, dec, reset }
}

const { count, inc, dec, reset } = useCounter(0, { min: 0, max: 10 })

</script>

<template>
  <p>Count: {{ count }}</p>
  <button @click="inc">
    inc
  </button>
  <button @click="dec">
    dec
  </button>
  <button @click="reset">
    reset
  </button>
</template>

2、实现一个切换状态的可组合函数

<script setup lang='ts'>
import { ref } from 'vue'
/**
 * 实现一个切换状态的可组合函数
 * 确保该功能正常工作
*/
function useToggle(state: boolean): [boolean, () => void] {
                                     
 const status = ref(state);
  const toggle = () => (status.value = !status.value);
  return [status, toggle];
}

const [state, toggle] = useToggle(false)

</script>

<template>
  <p>State: {{ state ? 'ON' : 'OFF' }}</p>
  <p @click="toggle">
    Toggle state
  </p>
</template>

三、新组件和新属性

1、v-bind(内置内容)

Vue3中的新增的v-bind()的常用使用方式,主要包括在css,less,scss中的使用

<script setup>
import { ref } from "vue"
const theme = ref("red")

const colors = ["blue", "yellow", "red", "green"]
const width=200
const style={
  height:'50px'
}
setInterval(() => {
  theme.value = colors[Math.floor(Math.random() * 4)]
}, 1000)

</script>
<template>
  <p>hello</p>
</template>

<style scoped>
/* 修改以下代码绑定动态颜色 */
p {
  color: v-bind(theme);
  line-height: v-bind('style.height');
  border:solid;
  width: v-bind(width+'px');
}
</style>

2、teleport 传送门

能将插槽内容渲染到 DOM 中的另一个指定位置。

<script setup>

const msg = "Hello World"

</script>

<template>
  <!-- to='移动到指定DOM里'-->
  <teleport  to="body">
  	<span>{{ msg }}</span>
  </teleport>
</template>

3、Suspense

允许应用程序在等待异步组件时渲染一些其它内容,让用户有一个更好体验。
使用步骤:

1、异步引入组件;

import {defineAsyncComponent} from 'vue'
const Child = defineAsyncComponent(()=>import('./components/Child.vue'))

2、使用Suspense包裹组件,并配置好default 与 fallback
(1) 插槽包裹异步组件;
(2) 插槽包裹渲染异步组件渲染之前的内容;

<template>
    <div class="app">
        <h3>我是App组件</h3>
        <Suspense>
            <template v-slot:default>
                <Child/>
            </template>
            <template v-slot:fallback>
                <h3>加载中.....</h3>
            </template>
        </Suspense>
    </div>
</template>
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐