vue3 语法糖 setup

867 阅读1分钟

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> 中必须使用 definePropsdefineEmits API 来声明 propsemits ,它们具备完整的类型推断并且在 <script setup> 中是直接可用的

  • definePropsdefineEmits 都是只在 <script setup> 中才能使用的编译器宏。他们不需要导入且会随着 <script setup> 处理过程一同被编译掉。

  • defineProps 接收与 props 选项相同的值,defineEmits 也接收 emits 选项相同的值。

  • definePropsdefineEmits 在选项传入后,会提供恰当的类型推断。

  • 传入到 definePropsdefineEmits 的选项会从 setup 中提升到模块的范围。因此,传入的选项不能引用在 setup 范围中声明的局部变量。这样做会引起编译错误。但是,它可以引用导入的绑定,因为它们也在模块范围内。

  • Vue 3.2 版本后不再需要 手动导入 defineProps , defineEmits,defineExpose, 直接使用即可,官方文档已经更新。如果不引入definePops, eslint会报错:‘defineProps’ is not defined。引入了会有警告:[@vue/compiler-sfc] defineProps is 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>