vue3.2语法糖 setup
<script setup></script>
当使用 <script setup> 的时候,任何在 <script setup> 声明的顶层的绑定 (包括变量,函数声明,以及 import 引入的内容及子组件) 都能在模板中直接使用
父组件:
<template>
<page :title="title" @clickLeft="clickLeft" :scrollbar="true">
<div>
<div @click="num++" class="box">
父组件的数据(可点击):{{ num }}--{{ count }}
</div>
<div>{{ list.age }}--{{ list.height }}</div>
<button @click="handleClick">改变list的值</button>
<div>computed--{{ num2 }}</div>
<numChild></numChild>
</div>
</page>
</template>
<script setup>
import { computed, reactive, ref } from '@vue/reactivity'
import { watch, watchEffect } from '@vue/runtime-core'
import numChild from './child.vue'
let title = ref('script setup')
let num = ref(1)
let count = '100'
let list = reactive({
age: 18,
height: 180
})
let num2 = computed(() => {
return num.value * 2
})
const handleClick = () => {
list.age++
list.height--
}
watch(
() => list.age,
(newValue, oldValue) => {
console.info('list---new:', newValue, 'list--old:', oldValue)
}
)
watchEffect(() => {
console.log('watcheffect---list.height', list.height)
})
</script>
子组件:
<template>
<div>
<div>子组件</div>
</div>
</template>
父传子,字传父,父组件调用子组件的方法或变量(* defineProps, defineEmits, defineExpose*)
在 <script setup> 中必须使用 defineProps 和 defineEmits API 来声明 props 和 emits ,它们具备完整的类型推断并且在 <script setup> 中是直接可用的
-
defineProps和defineEmits都是只在<script setup>中才能使用的编译器宏。他们不需要导入且会随着<script setup>处理过程一同被编译掉。 -
defineProps和defineEmits在选项传入后,会提供恰当的类型推断。 -
传入到
defineProps和defineEmits的选项会从 setup 中提升到模块的范围。因此,传入的选项不能引用在 setup 范围中声明的局部变量。这样做会引起编译错误。但是,它可以引用导入的绑定,因为它们也在模块范围内。 -
Vue 3.2 版本后不再需要 手动导入 defineProps , defineEmits,defineExpose, 直接使用即可,官方文档已经更新。如果不引入definePops, eslint会报错:‘defineProps’ is not defined。引入了会有警告:[@vue/compiler-sfc]
definePropsis a compiler macro and no longer needs to be imported.(1):
eslint-plugin-vue版本在8.0.0 +在
.eslintrc.js文件中添加:env: { node: true, // The Follow config only works with eslint-plugin-vue v8.0.0+ "vue/setup-compiler-macros": true, },(2):
eslint-plugin-vue版本在8.0.0 以下在
.eslintrc.js文件中添加:globals: { defineProps: "readonly", defineEmits: "readonly", defineExpose: "readonly", },
defineExpose:
在 script-setup 模式下,所有数据只是默认 return 给 template 使用,不会暴露到组件外,所以父组件是无法直接通过挂载 ref 变量获取子组件的数据。
如果要调用子组件的数据,需要先在子组件显示的暴露出来,才能够正确的拿到,这个操作,就是由 defineExpose 来完成。
tips: defineExpose要写在暴露元素的后面,一般写到最后。不然暴露不出去。
父组件:
<template>
<page :title="title" @clickLeft="clickLeft" :scrollbar="true">
<div>
<numChild
:name="name"
ref="numChildRef"
@updateName="updateName1"
></numChild>
</div>
</page>
</template>
<script setup>
import { ref } from '@vue/reactivity'
import { nextTick } from '@vue/runtime-core'
import numChild from './child.vue'
let title = ref('script setup')
let name = ref('我是父组件的值')
let numChildRef = ref(null)
const updateName1 = e => {
name.value = e
}
// 父组件使用子组件的变量和方法
nextTick(() => {
console.info('子组件的值', numChildRef.value.numChild)
numChildRef.value.initNum()
})
</script>
子组件:
<template>
<div>
<div>子组件</div>
<div>父组件传过来的值:{{ props.name }}</div>
<button @click="handleClick">更改父组件传的值</button>
</div>
</template>
<script setup>
// import { defineEmits, defineProps, defineExpose } from 'vue'
// 父组件传过来的值
const props = defineProps({
name: {
type: String,
default: ''
}
})
// 声明事件
const emit = defineEmits(['updateName'])
const handleClick = () => {
emit('updateName', '我是子组件改的值')
}
const initNum = () => {
console.info('父组件调用子组件的方法')
}
// 暴露属性和方法
defineExpose({
numChild,
initNum
})
</script>
定义组件的name
用单独的<script>块来定义,可以配合<script setup>一起使用。
<script>
export default {
name: 'ComponentName',
}
</script>
v-bind() CSS变量注入
<template>
<span>Jerry</span>
</template>
<script setup>
import { ref, reactive } from 'vue'
// prop接收样式
const props = defineProps({
border: {
type: String,
default: '1px solid yellow'
}
})
// 常量声明样式
const backgroundSpan = 'red'
// 响应式数据声明样式
const color = ref('blue')
const style = reactive({
opacity: '0.8'
})
</script>
<style lang="less" scoped>
span {
// 使用常量声明的样式
background: v-bind('backgroundSpan');
// 使用响应式数据声明的样式
color: v-bind('color');
opacity: v-bind('style.opacity');
// 使用prop接收的样式
border: v-bind('props.border');
}
</style>
路由导航守卫
<script setup>
import { onBeforeRouteLeave, onBeforeRouteUpdate } from 'vue-router'
// 添加一个导航守卫,在当前组件将要离开时触发。
onBeforeRouteLeave((to, from, next) => {
next()
})
// 添加一个导航守卫,在当前组件更新时触发。
// 在当前路由改变,但是该组件被复用时调用。
onBeforeRouteUpdate((to, from, next) => {
next()
})
</script>
ref获取多个dom
<div>ref获取多个dom</div>
<div v-for="item in 5" :key="item">
<div
:ref="
el => {
itemRef[item] = el
}
"
>
内容:{{ item }}
</div>
</div>
<script setup>
import { ref } from '@vue/reactivity'
import {
nextTick,
onMounted,
onBeforeUpdate
} from '@vue/runtime-core'
// 角色设置的多个ref合集
let itemRef = ref({})
onMounted(() => {
nextTick(() => {
console.info('ref获取多个dom', itemRef.value)
})
})
onBeforeUpdate(() => {
itemRef.value = {}
})
</script>
readonly() 和shallowReadonly()
readonly和shallowReadonly接受一个对象 (不论是响应式还是一般的) 或是一个 ref,返回一个原值的只读代理。
reafonly()是深层的,对任何嵌套property的访问都是只读的 shallowReadonly()是readonly的浅层作用方式,只有根层级的property是只读的
let num = reafonly({num:1,num2:{num: 2}})
num.num++ // warning 只读 更改状态自身的属性会失败
num.num2.num++ // warning 只读 更改状态自身的属性会失败
let num = shallowReadonly({num:1,num2:{num: 2}})
num.num++ // warning 只读 更改状态自身的属性会失败
num.num2.num++ // 3
provide 和 inject
provide和inject可以实现嵌套组件之间进行传递数据。 这两个函数都是在setup函数中使用的。 父级组件使用provide向下进行传递数据; 子级组件使用inject来获取上级组件传递过来的数据; 需要注意的是: (1). provide只能够向下进行传递数据 (2). 在使用provide和inject的时候需从vue中引入
tips: inject获取的值可以直接修改,父级的数据会被修改。为了数据安全,应该对数据的变更进行限制,遵循单向数据流的设计,可以对数据进行 readonly(只读) 处理。
// 父级组件
<script setup>
import { provide, ref, readonly } from "vue"
let giveSunziData=ref({
with:10,
height:5,
})
// 第一个参数是是共享数据的名称(giveSunzi)
// 第二个参数是共享的数据(giveSunziData)
provide('giveSunzi',giveSunziData)
// 对数据只读处理
provide('giveSunzi',readonly(giveSunziData))
</script>
// 子级组件
<script setup>
import { inject } from "vue"
let getFaytherData=inject('giveSunzi');
</script>