目录
composition API vs options API
为什么要学习 vue 3
为什么要学vue3:
-
Vue是国内最火的前端框架
-
Vue 3 于 2022 年 2 月 7 日星期一成为新的默认版本!
-
Vue3性能更高,体积更小
-
Vue3.0在经过一年的迭代后,越来越好用
官方文档:
-
vue3官方文档:Vue.js - The Progressive JavaScript Framework | Vue.js
-
vue3中文文档:Vue.js - 渐进式 JavaScript 框架 | Vue.js
目前已支持 vue3 的UI组件库:
-
element-plus
A Vue 3 UI Framework | Element Plus (PC组件库)
Element Plus,一套为开发者、设计师和产品经理准备的基于 Vue 3.0 的桌面端组件库。
-
vant
Vant 3 - Lightweight Mobile UI Components built on Vue
轻量、可靠的移动端 Vue 组件库。
Vant 是有赞前端团队开源的移动端组件库,于 2016 年开源,已持续维护 4 年时间。
目前 Vant 已完成了对 Vue 3.0 的适配工作,并发布了 Vant 3.0 版本
-
ant-design-vue (PC组件库)
Ant Design Vue — An enterprise-class UI components based on Ant Design and Vue.js
ant-design-vue 是 Ant Design 的 Vue 实现,组件的风格与 Ant Design 保持同步
Vue3 动机 和 新特性
Vue3 设计理念 组合式 API 征求意见稿 | Vue 组合式 API
动机与目的:
-
更好的逻辑复用 与 代码组织 (composition组合式api)
optionsAPI(旧) => compositionAPI(新), 效果: 代码组织更方便了, 逻辑复用更方便了 非常利于维护!!
-
更好的类型推导 (typescript支持)
vue3 源码用 ts 重写了, vue3 对 ts 的支持更友好了 (ts 可以让代码更加稳定, 类型检测! )
vue3新特性:
- 数据响应式原理重新实现 (ES6 proxy 替代了 ES5 的 Object.defineProperty)
解决了: 例如数组的更新检测等bug, 大大优化了响应式监听的性能
(原来检测对象属性的变化, 需要一个个对属性递归监听) proxy 可以直接对整个对象劫持
-
虚拟DOM - 新算法 (更快 更小)
-
提供了composition api, 可以更好的逻辑复用
-
模板可以有多个根元素
-
源码用 typescript 重写, 有更好的类型推导 (类型检测更为严格, 更稳定)
...
小结: vue3 性能更高, 体积更小, 更利于复用, 代码维护更方便
Vite 的使用
vite介绍
Vite 官方文档:Vite | 下一代的前端工具链
Vite(法语意为 "快速的",发音 /vit/
,发音同 "veet")是一种新型前端构建工具
优势
-
💡 极速的服务启动,使用原生 ESM 文件,无需打包
-
⚡️ 轻量快速的热重载,始终极快的模块热重载(HMR)
-
🛠️丰富的功能,对 TypeScript、JSX、CSS 等支持开箱即用
-
📦等等
为什么选 Vite ?
传统方式
-
基于打包器的方式启动,必须优先抓取并构建你的整个应用,然后才能提供服务。
-
更新速度会随着应用体积增长而直线下降。
vite 方式
-
Vite 以 原生 ESM 方式提供源码。这实际上是让浏览器接管了打包程序的部分工作。
-
Vite 只需要在浏览器请求源码时进行转换并按需提供源码。
-
根据情景动态导入代码,即只在当前屏幕上实际使用时才会被处理。
问题小结:
-
Vite 是否需要先打包才能提供服务?
-
使用webpack能否创建vue3的项目?
Vite 的基本使用
目标:能够使用vite创建一个vue3的项目
(1)使用vite创建项目
npm create vite
# or
yarn create vite
(2)输入项目名字,默认为vite-project
(3)选择创建的项目类型,选择vue即可
(4)选择创建的vue项目类型, 不选ts
(5)启动项目
vite快捷使用
如果想要快速创建一个vue3项目,可以使用如下命令
-
创建普通vue项目
yarn create vite vite-demo --template vue
-
创建基于ts模板的项目
yarn create vite vite-demo-ts --template vue-ts
Vue3.0项目介绍
任务目标:掌握vue3的项目目录结构
-
删除src下所有的文件和代码
-
创建App.vue
<template>
<div>我是App组件</div>
</template>
-
创建main.js文件
// console.log(111)
import { createApp } from 'vue'
// 导入根组件
import App from './App.vue'
// createApp 创建根实例应用
const app = createApp(App)
// 挂载到视图上
app.mount('#app')
vscode插件说明
-
vue2中需要安装插件
vetur
,可以实现组件高亮。但是vue3的一些语法在vetur中报错。 -
vue3中需要安装插件
volor
,提供了更加强大的功能。 -
所以,使用功能vue3,需要卸载
vetur
插件,安装volar
插件。
组合式API
composition API vs options API
-
vue2 采用的就是
optionsAPI
(1) 优点:
易于学习和使用
, 每个代码有着明确的位置 (例如: 数据放 data 中, 方法放 methods中)(2) 缺点: 相似的逻辑, 不容易复用, 在大项目中尤为明显
(3) 虽然 optionsAPI 可以通过mixins 提取相同的逻辑, 但是也并不是特别好维护
-
vue3 新增的就是
compositionAPI
(1) compositionAPI 是基于 逻辑功能 组织代码的, 一个功能 api 相关放到一起
(2) 即使项目大了, 功能多了, 也能快速定位功能相关的 api
(3) 大大的提升了
代码可读性
和可维护性
-
vue3 推荐使用 composition API, 也保留了options API
即就算不用composition API, 用 vue2 的写法也完全兼容!!
问题小结:optionsAPI
的优缺点是什么? vue3 新增的 compositionAPI
有什么特征? 有什么优势?
体验 composition API
需求: 鼠标移动显示鼠标坐标 x, y
options API 版本
<template>
<div>当前鼠标位置:</div>
<div>{{ x }}</div>
<div>{{ y }}</div>
<div>当前点击次数: <button @click="countAdd">+1</button></div>
<div>{{ count }}</div>
</template>
<script>
// vue3完全兼容 vue2语法 -> 可以使用 optionAPI -> 但是提示不太友好 -> ts给提示
export default {
data() {
return {
x: 0,
y: 0,
count: 0,
}
},
// 注册dom事件,一定要等dom准备就绪 mounted
mounted() {
document.addEventListener('mousemove', this.move)
},
methods: {
countAdd() {
this.count++
},
move(e) {
// console.log(1, e)
this.x = e.pageX
this.y = e.pageY
},
},
// 在组件销毁时,将事件解绑
destroyed() {
document.removeEventListener('mousemove', this.move)
},
}
</script>
<style></style>
composition API 版本
<template>
<div>当前鼠标位置</div>
<div>x: {{ mouse.x }}</div>
<div>y: {{ mouse.y }}</div>
<div>当前点击次数:{{ count }}</div>
<button @click="add">点击</button>
</template>
<script>
import { onMounted, onUnmounted, reactive, ref } from 'vue'
// ------------------功能A 累加器--------------------
function useCount() {
// 提供数据 功能A
const count = ref(0)
// 提供方法 功能A
const add = () => {
count.value++
}
return { count, add }
}
// ------------------功能B 鼠标坐标--------------------
function useMouse() {
// 提供数据 功能B
const mouse = reactive({
x: 0,
y: 0,
})
// 提供方法 功能B
const move = (e) => {
mouse.x = e.pageX
mouse.y = e.pageY
}
// 生命周期钩子
// 等价于 mounted 组件挂载
onMounted(() => {
document.addEventListener('mousemove', move)
})
// 等价于 destroyed 组件挂载
onUnmounted(() => {
document.removeEventListener('mousemove', move)
})
// 模版中需要用的数据和方法,在这里 return
return mouse
}
export default {
setup() {
const { count, add } = useCount()
const mouse = useMouse()
return { count, add, mouse }
},
}
</script>
问题小结:optionsAPI
的优缺点是什么? vue3 新增的 compositionAPI
有什么特征? 有什么优势?
optionsAPI:
-
优点:
易于学习和使用
, 每个代码有着明确的位置 -
缺点: 相似的逻辑, 不容易复用
compositionAPI:
-
基于 逻辑功能 组织代码
-
可维护性好!
setup 函数
composition api的使用, 需要配置一个setup 函数
-
setup 函数是一个新的组件选项, 作为组件中 compositionAPI 的起点
-
从生命周期角度来看, setup 会在 beforeCreate 钩子函数之前执行
-
setup 中不能使用 this, this 指向 undefined
-
在模版中需要使用的数据和函数,需要在
setup
返回。
<template>
<div>我是app - {{ age }}</div>
<button @click="sayHi">按钮</button>
</template>
<script>
// 1. setup 函数是一个新的组件选项, 作为组件中 compositionAPI 的起点
// 2. 从生命周期角度来看, setup 会在 beforeCreate 钩子函数之前执行
// 3. setup 中不能使用 this, this 指向 undefined => 不需要使用 this (重点)
// 4. 在模版中需要使用的数据和函数,需要在 setup 返回。
export default {
setup() {
console.log('setup')
// 这里可以声明各种变量 和 函数 => 供模版使用
const age = 18
const sayHi = () => {
console.log('你好啊')
}
return {
age,
sayHi,
}
},
beforeCreate() {
console.log('beforeCreate')
},
}
</script>
reactive 函数
前置说明:
-
setup 需要有返回值, 只有返回的值才能在模板中使用
-
默认普通的数据, 不是响应式的
作用: 传入一个复杂数据类型,将复杂类型数据, 转换成响应式数据 (返回该对象的响应式代理)
<template>
<div>{{ user.username }}</div>
<div>{{ user.age }}</div>
<button @click="add">改值</button>
</template>
<script>
// 前置说明:
// 1. setup 需要有返回值, 只有返回的值才能在模板中使用
// 2. 默认普通的数据, 不是响应式的
import { reactive } from 'vue'
// 作用: 传入一个复杂数据类型,将复杂类型数据, 转换成响应式数据 (返回该对象的响应式代理)
export default {
setup() {
// let username = '张三'
// let age = reactive(18) // 错误
let user = reactive({
username: '张三',
age: 18,
})
let add = () => {
user.age++
console.log(user.age)
}
return { user, add }
},
}
</script>
总结: 通常是用来定义响应式 对象数据
问题小结:
-
默认 setup 函数中返回的 普通对象 是响应式的么 ?
-
reactive 函数的作用是什么 ?
ref 函数
reactive 处理的数据, 必须是复杂类型, 如果是简单类型无法处理成响应式, 所以有 ref 函数!
作用: 对传入的数据(一般简单数据类型),包裹一层对象, 转换成响应式。
-
ref 函数接收一个的值, 返回一个ref 响应式对象, 有唯一的属性 value
-
在 setup 函数中, 通过 ref 对象的 value 属性, 可以访问到值
-
在模板中, ref 属性会自动解套, 不需要额外的 .value
-
ref函数也支持传入复杂类型,传入复杂类型,也会做响应式处理
ref处理简单数据类型响应式
<template>
<div>{{ age }}</div>
<!-- 在模板中, ref 属性会自动解套, 不需要额外的 .value -->
<button @click="age++">+1</button>
<button @click="minus">-1</button>
<div>{{ money }}</div>
<button @click="add">搬砖</button>
</template>
<script>
// reactive 处理的数据, 必须是复杂类型, 如果是简单类型无法处理成响应式, 所以有 ref 函数!
// 作用: 对传入的数据(一般简单数据类型),包裹一层对象, 转换成响应式。
// 1. ref 函数接收一个的值, 返回一个ref 响应式对象, 有唯一的属性 value
// 2. 在 setup 函数中, 通过 ref 对象的 value 属性, 可以访问到值
// 3. 在模板中, ref 属性会自动解套, 不需要额外的 .value
// 4. ref函数也支持传入复杂类型,传入复杂类型,也会做响应式处理
import { ref } from 'vue'
export default {
setup() {
const age = ref(18)
// console=.log(age)
const minus = () => {
age.value--
}
const money = ref(100)
const add = () => {
money.value += 100
}
return { age, money, minus, add }
},
}
</script>
ref处理复杂数据类型做响应式处理
<template>
<div>{{ user }}</div>
<button @click="changeUser">改值</button>
</template>
<script>
// reactive 处理的数据, 必须是复杂类型, 如果是简单类型无法处理成响应式, 所以有 ref 函数!
// 作用: 对传入的数据(一般简单数据类型),包裹一层对象, 转换成响应式。
// 1. ref 函数接收一个的值, 返回一个ref 响应式对象, 有唯一的属性 value
// 2. 在 setup 函数中, 通过 ref 对象的 value 属性, 可以访问到值
// 3. 在模板中, ref 属性会自动解套, 不需要额外的 .value
// 4. ref函数也支持传入复杂类型,传入复杂类型,也会做响应式处理
import { reactive, ref } from 'vue'
export default {
setup() {
// ref 和 reactive
// 明确的对象,明确的属性 => 用reactive => 其他情况用 ref
// 且ref 在 vue3.2版本以后做了语法底层的升级,性能会更高 => 追求性能 => 所有的场景全用 ref
// const user = reactive({
// name: 'zs',
// age: 18,
// })
// const changeUser = () => {
// user.name = 'ls'
// user.age++
// }
// ref函数也支持传入复杂类型,传入复杂类型,也会做响应式处理
const user = ref({
name: 'zs',
age: 18,
})
const changeUser = () => {
user.value.name = 'ls'
user.value.age++
}
return { user, changeUser }
},
}
</script>
ref 和 reactive 的最佳使用方式:
-
明确的对象,明确的属性,用reactive,其他用 ref
-
从vue3.2之后,更推荐使用ref
问题小结:
-
ref 函数的作用是什么 ?
-
ref 函数包裹简单类型后, 会包裹成对象, 在模板中需要 .value 么? 在 setup 中需要 .value 么?
script setup语法(★)
script setup是在单文件组件 (SFC) 中使用组合式 API 的编译时语法糖。相比于普通的 script 语法更加简洁
要使用这个语法,需要将 setup
attribute 添加到 <script>
代码块上:
<script setup>
console.log('hello script setup')
</script>
顶层的绑定会自动暴露给模板,所以定义的变量,函数和import导入的内容都可以直接在模板中直接使用
<script setup>
import { ref } from 'vue'
const car = ref('小车车')
const sayHi = () => {
console.log('你好')
}
const change = () => {
car.value = '大车车'
}
</script>
<template>
<div>{{ car }}</div>
<button @click="sayHi">按钮</button>
<button @click="change">改值</button>
</template>
案例:显示鼠标案例
使用setup语法完成鼠标案例
<template>
<div>当前鼠标位置</div>
<div>x: {{ mouse.x }}</div>
<div>y: {{ mouse.y }}</div>
<div>当前点击次数:{{ count }}</div>
<button @click="add">点击</button>
</template>
<script setup>
import { onMounted, onUnmounted, reactive, ref } from 'vue'
const count = ref(0)
const add = () => {
count.value++
}
const mouse = reactive({
x: 0,
y: 0,
})
const move = (e) => {
mouse.x = e.pageX
mouse.y = e.pageY
}
onMounted(() => {
document.addEventListener('mousemove', move)
})
onUnmounted(() => {
document.removeEventListener('mousemove', move)
})
</script>
计算属性computed函数
computed函数调用时, 要接收一个处理函数, 处理函数中, 需要返回计算属性的值
<script setup>
import { ref, computed } from 'vue'
const age = ref(18)
// 调用一次computed函数, 就会得到一个计算属性
const nextAge = computed(() => {
return +age.value + 1
})
// 调用一次computed函数, 就会得到一个计算属性 get set
const nextAge2 = computed({
get() {
return +age.value + 2
},
set(value) {
// console.log(value)
age.value = value - 2
},
})
</script>
<template>
<div>今年的年级:<input type="text" v-model="age" /></div>
<div>明年的年级:{{ nextAge }}</div>
<div>后年的年级: <input type="text" v-model="nextAge2" /></div>
</template>
问题小结: computed 函数提供计算属性, 有几种写法?
侦听器watch函数
watch监视, 接收三个参数
1. 参数1: 监视的数据源
2. 参数2: 回调函数
3. 参数3: 额外的配置
<script setup>
import { ref, watch, reactive } from 'vue'
const age = ref(18)
const money = ref(100)
const add = () => {
age.value++
}
const getMoney = () => {
money.value += 100
}
// watch监视,接收三个参数
// 1.参数1: 监视的数据源
// 2.参数2: 回调函数 => 数据变化了会触发的函数
// 3.参数3: 额外的配置项 { deep:true,immediate:true }
// 监视单个ref
// watch(age, (newValue, oldValue) => {
// console.log(newValue, oldValue)
// })
// 监听多个ref
// watch([age, money], (value) => {
// // console.log(value)
// const [newAge, newMoney] = value
// console.log(newAge, newMoney)
// })
// 监听对象 使用reactive监听
// const user = reactive({
// name: 'zs',
// age: 18,
// car: '小宝马',
// })
// const changeUser = () => {
// user.car = '小奥迪'
// }
// watch(user, (value) => {
// console.log(value)
// })
// 监听对象 使用ref监听
const user = ref({
name: 'zs',
age: 18,
car: '小宝马',
})
const changeUser = () => {
user.value.car = '小奥迪'
}
// 监听对象的变化,对于ref的情况 => 默认只会监听地址的变化,如果要监听所有的属性,就需要开启深度监视
// watch(
// user,
// (value) => {
// console.log(value)
// },
// {
// deep: true, // 深度监视,修改对象的属性,也会触发更新
// immediate: true, // 是否一进入页面就触发一次回调
// }
// )
// 监视对象中某个子属性的变化(固定语法)
// 1. 如果直接user.value.car => 是将这个值取出来了,没有任何意义
// 2. 我们要干的是,告诉他,我要监视的是 user.value.car 这个属性 => 怎么告诉? () => user.value.car,
watch(
() => user.value.car,
(value) => {
console.log(value)
},
{
deep: true, // 深度监视,修改对象的属性,也会触发更新
immediate: true, // 是否一进入页面就触发一次回调
}
)
</script>
<template>
<div>{{ age }}</div>
<button @click="add">+1</button>
<div>{{ money }}</div>
<button @click="getMoney">挣钱</button>
<div>{{ user.car }}</div>
<button @click="changeUser">换车</button>
</template>
钩子函数的使用
生命周期函数 vue3 中的生命周期函数, 需要在 setup 中调用
<script setup>
import { onMounted, onUnmounted } from 'vue'
// 生命周期钩子 beforeCreate / created => setup
// mounted(dom准备就绪) => onMounted
// destroyed(组件卸载) => onUnmounted
console.log(document.querySelector('h1'))
// 里面的回调,会在dom准备就绪时,执行
onMounted(() => {
console.log(document.querySelector('h1'))
})
onUnmounted(() => {
console.log('组件卸载了(切换路由)')
})
</script>
<template>
<h1>我是h1</h1>
</template>
组件通讯-父传子
目标:能够实现组件通讯中的父传子组件通讯
步骤:
-
父组件提供数据
-
父组件将数据传递给子组件
-
子组件通过defineProps进行接收
-
子组件渲染父组件传递的数据
核心代码:
父组件
<script setup>
// 组件导入进来后,就可以在底下使用
import { ref } from 'vue'
import Son from './components/Son.vue'
// 父传子
// 1. 父传递数据
// 2. 将数据传递 => 添加属性的方式传递
// 3. 子组件接收 => defineProps 等价于之前的props
// 4. 子组件使用
const car = ref('宝马车')
const money = ref(100)
</script>
<template>
<div style="border: 3px solid #ccc; margin: 10px">
父组件
<Son :car="car" :money="money"></Son>
</div>
</template>
子组件
<script setup>
// defineProps: 接收父组件传递的数据
const props = defineProps({
money: Number,
car: String,
})
</script>
<template>
<div style="border: 3px solid #ccc; margin: 10px">
<div>我是子组件</div>
<div>{{ money }} --- {{ car }}</div>
<button @click="myMoney">挣钱</button>
</div>
</template>
注意:如果使用defineProps接收数据,这个数据只能在模板中渲染,如果想要在script中也操作props属性,应该接收返回值。
组件通讯-子传父
目标:能够实现组件通讯中的子传父
步骤:
-
子组件通过defineEmit获取emit对象(因为没有this)
-
子组件通过emit触发事件,并且传递数据
-
父组件提供方法
-
父组件通过自定义事件的方式给子组件注册事件
核心代码
子组件
<script setup>
// defineProps: 接收父组件传递的数据
const props = defineProps({
money: Number,
car: String,
})
const emit = defineEmits(['changeMoney', 'changeCar'])
const myMoney = () => {
emit('changeMoney', 10)
}
const myCar = () => {
emit('changeCar', '奥迪')
}
</script>
<template>
<div style="border: 3px solid #ccc; margin: 10px">
<div>我是子组件</div>
<div>{{ money }} --- {{ car }}</div>
<button @click="myMoney">挣钱</button>
<button @click="myCar">换车</button>
</div>
</template>
父组件
<script setup>
// 组件导入进来后,就可以再底下使用
import Son from './components/Son.vue'
import { ref } from 'vue'
// 父传子
// 1. 父传递数据
// 2. 将数据传递 => 添加属性的方式传递
// 3. 子组件接收 => defineProps 等价于之前的props
// 4. 子组件使用
const car = ref('宝马车')
const money = ref(100)
const changeMoney = (value) => {
console.log(value)
money.value = money.value += value
}
const changeCar = (value) => {
car.value = value
}
</script>
<template>
<div style="border: 3px solid #ccc; margin: 10px">
父组件
<Son
:car="car"
:money="money"
@changeMoney="changeMoney"
@changeCar="changeCar"
></Son>
</div>
</template>
依赖注入 - provide 和 inject
依赖注入, 可以非常方便的实现 跨层级的 组件通信
父组件利用 provide 提供数据
<script setup>
// 组件导入进来后,就可以再底下使用
import { ref, provide } from 'vue'
import Son from './components/Son.vue'
const car = ref('黄金三轮车')
const changeCar = (value) => {
car.value = value
}
// provide 除了可以提供数据,还可以提供修改数据的方法
provide('car', car)
provide('changeCar', changeCar)
</script>
<template>
<div style="border: 3px solid #ccc; margin: 10px">
父组件
<Son></Son>
</div>
</template>
子组件 (子孙后代, 都可以拿到这个数据)
<script setup>
import { inject } from 'vue'
import GrandSon from './GrandSon.vue'
const car = inject('car')
</script>
<template>
<div style="border: 3px solid #ccc; margin: 10px">
<div>我是子组件</div>
<div>{{ car }}</div>
<GrandSon></GrandSon>
</div>
</template>
孙组件
<script setup>
import { inject } from 'vue'
const car = inject('car')
const changeCar = inject('changeCar')
</script>
<template>
<div style="border: 3px solid #ccc; margin: 10px">
<div>我是孙组件</div>
<div>{{ car }}</div>
<button @click="changeCar('宝马车')">换车</button>
</div>
</template>
如果希望子传父, 可以 provide 传递一个方法
父组件
<script setup>
import { provide, ref } from 'vue'
import Son from './components/Son.vue'
const money = ref(100)
const changeMoney = (num) => {
money.value = money.value - num
}
provide('money', money)
provide('changeMoney', changeMoney)
</script>
子组件
<script setup>
import { inject } from 'vue'
const money = inject('money')
const changeMoney = inject('changeMoney')
</script>
模板中 ref 的使用
联想之前的 ref 和 $refs, 获取模板的元素(dom元素,组件)
1 创建 ref => const hRef = ref(null)
2 模板中建立关联 => <h1 ref="hRef">钩子函数-----123</h1>
3 使用 => hRef.value
<script setup>
import { ref } from 'vue'
// ref 语法 配合模版中的 ref 属性
// 1.获取dom元素 ===> 操作dom
// 2.获取组件实例 ===> 调用组件的方法 this.$refs.form.validate()
// 1.创建 ref 对象 h1Ref.value = dom对象
const h1Ref = ref(null)
const clickFn = () => {
// 3.使用
console.log(h1Ref.value)
h1Ref.value.style.background = 'blue'
}
</script>
<template>
<!-- 2.模版中建立关联 -->
<h1 ref="h1Ref">我是h1</h1>
<button @click="clickFn">按钮</button>
</template>
ref操作组件
App.vue
<script setup>
import { ref } from 'vue'
// ref 语法 配合模版中的 ref 属性
// 1.获取dom元素 ===> 操作dom
// 2.获取组件实例 ===> 调用组件的方法 this.$refs.form.validate()
import Jack from './components/Son.vue'
// 1.创建 ref 对象 h1Ref.value = dom对象
const JackRef = ref(null)
const clickFn = () => {
// 3.使用
console.log(JackRef.value)
JackRef.value.sayHi()
}
</script>
<template>
<!-- 2.模版中建立关联 -->
<Jack ref="JackRef">我是Jack</Jack>
<button @click="clickFn">按钮</button>
</template>
需要配合defineExpose
componnets/Son.vue
<script setup>
const sayHi = () => {
alert('大家好,我是jack')
}
// 将sayHi方法暴露出去,将来别人只要拿到组件实例,就可以调用sayHi方法
defineExpose({
sayHi,
})
</script>
<template>
<div>我是jack</div>
</template>
vue3中废弃了过滤器
vue3.0中不能使用过滤器,直接使用函数进行替代
<script setup>
// 过滤器 => vue3已废弃 (函数代替)
// 事件总线 eventbus => vue3已废弃 (依赖注入,provide,inject)
import dayjs from 'dayjs'
// 2023-04-04
const date = new Date()
const formatDate = (value) => {
return dayjs(value).format('YYYY-MM-DD')
}
</script>
<template>
<div>{{ formatDate(date) }}</div>
</template>
补充-toRefs 函数
使用场景: 如果对一个响应数据, 进行解构 或者 展开, 会丢失他的响应式特性!
原因: vue3 底层是对 对象 进行监听劫持
作用: 对一个响应式对象的所有内部属性, 都做响应式处理
-
reactive/ref的响应式功能是赋值给对象的, 如果给对象解构或者展开, 会让数据丢失响应式的能力
-
使用 toRefs 可以保证该对象展开的每个属性都是响应式的
<script setup>
// 一个响应式对象,一旦进行了解构 或者 展开,会丢失响应式的特性
import { reactive, toRefs } from 'vue'
// 原因: 之所以响应式,一定是对某个对象整体进行了 proxy 劫持
const user = reactive({
username: 'zs',
age: 18,
desc: '不错',
})
// 如果希望解构之后也能是响应式的 => toRefs 让user里面的所有属性,单独做响应式处理
const { username, age } = toRefs(user)
const changeName = () => {
username.value = 'ls'
}
const changeAge = () => {
age.value++
}
</script>
<template>
<div>{{ username }}</div>
<div>{{ age }}</div>
<button @click="changeName">改名</button>
<button @click="changeAge">年龄+1</button>
</template>
问题小结: toRefs 函数的作用是什么 ?
作用: 对一个 响应式对象 的所有内部属性, 都做响应式处理, 保证展开或者解构出的数据也是响应式的