介绍
Vue3 新特性setup语法糖
# Vue3 升级
- 当前脚手架Vue cil4默认是安装Vue3.0版本 如果需要使用新特性需要更新Vue版本 并且需要把编译器同时更新到最新
npm i vue
npm i @vue/compiler-sfc -D
# Vue3中的 setup语法糖
setup语法糖
是Vue3.2新出的特性 绝大部门可以代替之前的setup()
Vue3之前一直让人不爽的return
再也用不到了- 绝大多数的内容可以被
setup语法糖
代替 但是个别内容无法被代替 不过Vue3.2 允许声明多个<script></script>
标签 - 注意: 虽然声明的方法和变量无需
return
但是你还是需要import
导入Vue3的api方法
# 不可代替的内容
绝大多数的内容可以被setup语法糖
代替 但是个别内容无法被代替 不过Vue3.2 允许声明多个<script></script>
标签
- 通过github文档 (opens new window) 得知 有些内容无法被
setup语法糖
代替name
声明Vue文件唯一名称 适用于匹配路由中的name 多用于浏览器缓存inheritAttrs
根元素继承特性 当设置为false
的时候 不继承 (虽然不太懂有啥用)- 还有一些 插件或者组件库的自定义配置 这个就是按需来设置'
export
导出不能在setup
中使用
<script>
// 无法被setup语法糖代替的内容
export const colorMap = new Map([
['珊瑚红', '#9d0b12'],
['幻夜黑', '#010304'],
['钛白色', '#8f8f8f'],
['极光蓝', '#0b5d9d']
])
export default {
name: 'CustomName',
inheritAttrs: false,
customOptions: {}
}
</script>
<script setup>
// setup语法糖的内容
</script>
# 声明响应式变量
- Vue3 声明变量的两种方式
ref()
声明单个的变量 这个一般不怎么用 通常配合ref
获取Dom节点 (修改值的时候 需要.value
)reactive()
声明多个变量 这个老好用了 什么数据往里面塞就完事了reactive()
声明多个变量的时候 需要用toRefs
来进行结构template
可直接使用 不用再携带声明前缀
<template>
<div>
<!-- 结构后我们无需写声明的前缀 -->
姓名: {{ name }}
性别: {{ sex }}
</div>
</template>
<script setup>
import { reactive, ref, toRefs } from 'vue'
//! 声明一个ref数据
// ref声明响应式数据,用于声明基本数据类型
const userName = ref('Jerry')
// 修改 ref修改数据的时候 需要.value
userName.value = 'Tom'
//! 声明一个reactive数据
// reactive声明响应式数据,用于声明引用数据类型
const state = reactive({
name: 'Jerry',
sex: '男'
})
// 修改
state.name = 'Tom'
// 使用toRefs解构 template可直接使用{{name}}、{{sex}}
// 但是在setup中使用 但内部仍然需要.value
const { name, sex } = toRefs(state)
</script>
reactive
可以多次声明并支持toRefs
结构 但是注意变量名不能一样toRefs
虽然可以让模板访问时 可以直接调用 但是你在setup
内部使用时候 需要.value
否则你就通过引用的方式使用
<template>
<div>
<!-- 结构后我们无需写声明的前缀 -->
姓名: {{ name }} 性别: {{ sex }}
</div>
</template>
<script setup>
import { reactive, toRefs } from 'vue'
//! 声明一个reactive数据
// reactive声明响应式数据,用于声明引用数据类型
const state = reactive({
name: 'Jerry',
sex: '男'
})
// 使用toRefs解构 template可直接使用{{name}}、{{sex}}
const { name, sex } = toRefs(state)
// 但是在setup中使用仍然需要.value
console.log(name.value)
// 或者你通过引用的方式使用
console.log(state.name)
</script>
# 声明method方法
- 和Vue3.0版本一样 我们直接
const
一个方法即可 不同的是 我们再也不用return
暴露方法了
<template>
<div>
<!-- 结构后我们无需写声明的前缀 -->
姓名: {{ name }}
性别: {{ sex }}
<!-- 改变性别的按钮 -->
<button @click="changeSex">点 击</button>
</div>
</template>
<script setup>
import { reactive, toRefs } from 'vue'
//! 声明一个reactive数据
// reactive声明响应式数据,用于声明引用数据类型
const state = reactive({
name: 'Jerry',
sex: '男'
})
// 使用toRefs解构 template可直接使用{{name}}、{{sex}}
const { name, sex } = toRefs(state)
// 声明一个改变性别的方法
// eslint-disable-next-line no-unused-vars
const changeSex = () => {
if (state.sex === '男') {
// 修改成女
state.sex = '女'
} else {
// 修改成男
state.sex = '男'
}
}
</script>
# 组件components的使用
- Vue2 或者 Vue3.0中 使用组件都需要去通过
components
注册 那么在Vue3.2中 无需注册 引入即可使用 很方便 这点和uniapp
有点相似 - 引入组件的时候, 不可以去掉
.vue
后缀
<template>
<div>
<!-- 使用组件 -->
<HelloWorld />
</div>
</template>
<script setup>
// 导入组件
import HelloWorld from '@/components/HelloWorld.vue'
</script>
# 组件切换
<component />
组件切换占位符 可用于两个组件的项目切换 可以按需代替v-if
或者v-show
<template>
<div>
<!-- 使用组件切换 -->
<component :is="state ? HelloWorld : setupHello " />
<button @click="changeState">切 换</button>
</div>
</template>
<script setup>
import { reactive, toRefs } from 'vue'
// 导入组件A
import HelloWorld from '@/components/HelloWorld.vue'
// 导入组件B
import setupHello from '@/components/setupHello.vue'
// 声明组件切换状态位
const data = reactive({
state: true
})
// 结构状态位
// eslint-disable-next-line no-unused-vars
const { state } = toRefs(data)
// 声明切换组件的方法 修改声明的状态位
const changeState = () => {
data.state = !data.state
}
</script>
# 父子组件相关
setup语法糖
最大的特性 就是父子传值内容- 接收父组件的值
defineProps()
子组件给父组件传递值defineEmits()
暴露组件内容defineExpose()
- 以上组件相关的api都不需要导入 直接使用即可
- 不导入api会报错 所以还是推荐从Vue3中导入 这样不会报错 使用方法和其他组合
# 父传子 defineProps()
- 子组件可以通过
defineProps()
接收父组件传来的值
父组件
- 导入子组件 并且给其传值
<template>
<div>
<!-- 导入组件 :为动态传值-->
<setupHello :msg="'你好setup'" />
</div>
</template>
<script setup>
// 导入组件
import setupHello from '@/components/setupHello.vue'
</script>
子组件 接收父组件传来的值
子组件可以通过
defineProps()
接收父组件传来的值- 在
<template>
模板中 不需引用声明
- 在
如果不设置默认值 那么Vue3会给他赋值为
undefined
<template>
<div>
<!-- 接收父组件的值 不需引用声明-->
{{ msg }}
</div>
</template>
<script setup>
import { onMounted } from 'vue'
// 接收父组件传来的值
const props = defineProps({ // eslint-disable-line no-unused-vars
msg: String
})
onMounted(() => {
// 打印值
console.log(props.msg)
})
</script>
设置传参的默认值
- 通常我们不设置默认值传参 那么没有参数的时候默认为
undefined
- 可以通过
type
和default
给其设置默认值和类型 用法和Vue2一样
<template>
<div>
<!-- 接收父组件的值 -->
{{ msg }}
</div>
</template>
<script setup>
import { onMounted } from 'vue'
// 接收父组件传来的值
const props = defineProps({
msg: {
// 设置类型
type: String,
// 设置默认值
default: '默认值'
}
})
onMounted(() => {
// 打印值
console.log(props.msg)
})
</script>
# **子传父defineEmits()
**
- 子组件给父组件传递值的时候 通常为点击某个事件进行的传值 那么我们可以设置一个子组件事件
defineEmits()
定义子组件的事件 用来给父组件传值defineEmits()
可以为数组 支持定义多个子组件事件进行传值操作defineEmits(['first','second'])
子组件
defineEmits()
定义子组件的事件 用来给父组件传值
<template>
<div>
<!-- 接收父组件的值 -->
{{ msg }}
</div>
<!-- 绑定事件给父组件传递的值 -->
<button @click="ChangeMsg">传 值</button>
</template>
<script setup>
// 接收父组件传来的值
// eslint-disable-next-line no-undef
const props = defineProps({ // eslint-disable-line no-unused-vars
msg: String
})
// 设置自定义事件 给父组件传递的值
// eslint-disable-next-line no-undef
const emit = defineEmits(['sendParent'])
const ChangeMsg = () => {
emit('sendParent', '这是给父组件传递的的值')
}
</script>
父组件
- 监听
defineEmits()
定义子组件的事件 来接收子组件传来的值
<template>
<div>
<!-- 导入组件 接收子组件的值 -->
<setupHello msg="你好setup" @sendParent="getSonMsg" />
<!-- 打印子组件传递的值 -->
{{data.sonMsg}}
</div>
</template>
<script setup>
import { reactive } from 'vue'
// 导入组件
import setupHello from '@/components/setupHello.vue'
// 设置变量 接收子组件的值
const data = reactive({
sonMsg: ''
})
// 赋值子组件的值
const getSonMsg = (e) => {
data.sonMsg = e
}
</script>
# 组件暴露内容 defineExpose()
- 组件中可以通过
defineExpose()
暴露一些内容 父组件无需监听 通过ref
绑定组件的实例(Dom节点) 然后设置getCurrentInstance
获取组件的实例 从中得到 组件暴露的内容
子组件
- 组件中通过
defineExpose()
暴露一些内容 可以为方法 也可以为 变量
<template>
<div>
我是一个组件
<button @click='ChangeMsg'>点击</button>
</div>
</template>
<script setup>
// 设置自定义事件 给父组件传递的值
// eslint-disable-next-line no-undef
const emit = defineEmits(['sendParent'])
// eslint-disable-next-line no-unused-vars
const ChangeMsg = () => {
emit('sendParent', '这是给父组件传递的的值')
}
// 设置要暴露的值
const a = '我是a'
// 设置要暴露的方法
const b = () => {
console.log('你好')
}
// 暴露组件的值和方法
// eslint-disable-next-line no-undef
defineExpose({
a, b
})
</script>
父组件
- 父组件无需监听 通过
ref
绑定组件的实例(Dom节点) 然后设置getCurrentInstance
获取组件的实例 从中得到 组件暴露的内容
<template>
<div>
<!-- 导入组件 绑定组件的实例(Dom节点)-->
<setupHello ref="setupHello" @sendParent="getSonMsg" />
</div>
</template>
<script setup>
// 获取组件实例
import { getCurrentInstance } from 'vue'
// 导入组件
import setupHello from '@/components/setupHello.vue'
// 获取绑定组件的实例
const instance = getCurrentInstance()
// 接收组件传来的值
const getSonMsg = () => {
// 暴露的组件内容在refs.组件名称 里面
console.log(instance.refs.setupHello)
}
</script>
# 父子组件相同传值 v-model:
(Vue2 .sync
代替)
- Vue3中
v-model:
代替了Vue2中的sync
他是Vue3的 父子组件的双向绑定 我们想在setup
语法糖中 也适用这种双向绑定
父组件
- 通过
v-model:
来给子组件传递值 并且 监听子组件传来的值 实现双向绑定
<template>
<div>
<!-- 导入组件 -->
<setupHello v-model:sendParent="name" />
{{name}}
</div>
</template>
<script setup>
import { reactive, toRefs } from 'vue'
// 导入组件
import setupHello from '@/components/setupHello.vue'
// 设置要双向绑定的值
const state = reactive({
name: '你好我是双向绑定的值'
})
// 使用toRefs解构 template可直接使用{{name}}、{{sex}}
const { name } = toRefs(state)
</script>
子组件
- 通过
update:
绑定父组件监听的事件 来实现双向绑定 - 先通过
defineEmits()
设置父组件的监听事件 然后通过点击事件触发事件并修改值 - 注意: 设置父组件的监听事件 和 触发传值(双向绑定)的时候 都需要添加
update:
<template>
<!-- 修改父组件传来的值 -->
<button @click="ChangeMsg">修 改</button>
</template>
<script setup>
// 接收父组件传来的值
// eslint-disable-next-line no-undef
const props = defineProps({ // eslint-disable-line no-unused-vars
name: String
})
// 通过update: 设置双向绑定的父组件监听事件
// eslint-disable-next-line no-undef
const emit = defineEmits(['update:sendParent'])
// 修改父组件传来的值 通过update: 实现数据的双向绑定
const ChangeMsg = () => {
emit('update:sendParent', '我修改了双向绑定的值')
}
</script>
# TS中组件引用类型和获取组件Dom
有时,我们需要为一个子组件添加一个模板 ref()
,获取其实例, 在Vue3中组件的实例是一个porxy
Vue的响应式对象
为了设置组件的ts类型, 我们首先需要通过 typeof
得到其类型,再使用 TypeScript 内置的 InstanceType
工具类型来获取其实例类型:
<script setup lang="ts">
// 导入使用的组件
import MyModal from './MyModal.vue'
// 通过InstanceType工具获得导入组件的类型
const modal = ref<InstanceType<typeof MyModal> | null>(null)
</script>
如果我们想获得组件的Dom, 就需要用到$el (opens new window), 属性
- 获取Dom的操作要放到
onMounted
组件挂载完成后的生命周期中
<TipsIphone ref="modal" />
<script setup lang="ts">
// 导入Vue3的API
import { ref, onMounted } from 'vue'
// 导入使用的组件
import MyModal from './MyModal.vue'
// 通过InstanceType工具获得导入组件的ts类型
const modal = ref<InstanceType<typeof MyModal> | null>(null)
onMounted(() => {
// 通过可选链运算符来获得组件Dom
console.log(modal.value?.$el)
})
</script>
# 对 await 的支持
- 不必再配合
async
就可以直接使用await
了,这种情况下,组件的setup
会自动变成async setup
<script setup>
const post = await myApi()
</script>
- 或者在箭头函数中 使用
async
和await
const getFire = async () => {
const post = await myApi()
console.log(ret)
}
getFire()
# provide和inject 依赖注入
provide
可以向所有子孙组件提供数据以及提供修改数据的方法 然后子孙组件用inject
来注入使用数据。
父组件
provide
可以向所有子孙组件提供数据以及提供修改数据的方法
<script setup>
import { provide } from 'vue'
// 声明provide
provide('provideState',
// 设置一个方法
changeName: () => {
name.value = 'Tom'
}
)
</script>
子组件
- 子孙组件用
inject
来注入使用数据。
子组件
<script setup>
import { inject } from 'vue'
// 注入
const provideState = inject('provideState')
// 子组件触发注入的方法
provideState()
</script>
# Vuex的写法
- Vue3中的Vuex不再提供辅助函数写法
<script setup>
import { useStore } from 'vuex'
import { key } from '../store/index'
// 必须先声明调用
const store = useStore(key)
// 获取Vuex的state
store.state.xxx
// 触发mutations的方法
store.commit('fnName')
// 触发actions的方法
store.dispatch('fnName')
// 获取Getters
store.getters.xxx
</script>
# 其他内容
- 其他的方法 比如生命周期 计算属性
computed
监听属性watch
他们的写法都没有改变 和之前的Vue3.0写法一致 不再记录