关于 Vue3 setup 语法糖

Posted zjy4fun

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关于 Vue3 setup 语法糖相关的知识,希望对你有一定的参考价值。

Vue3 setup

参考:https://juejin.cn/post/7009282373476941831#comment

新的 setup 是组件创建之前, props 被解析之后执行,是组合式 API 的入口

在 setup 中应该避免使用 this, 因为它不会找到示例。setup 的调用发生在 data property,computed property 或 methods 被解析之前,所以它们无法在 setup 中被获取

setup 是一个接收 props 和 context 的函数,setup 返回的所有内容都暴露给组件的其余部分(计算属性、方法、生命周期钩子等等)以及组件的模版,所有 ES 模块导出都被认为是暴露给上下文的值,并包含在 setup()返回对象中。

使用后,script 标签中的内容相当于原本组件声明中 setup() 的函数体,不过也有一定的区别。

使用 script setup 语法糖,组件只需引入不用注册,属性和方法也不用返回,也不用写 setup 函数,也不用洗 export default,甚至是自定义指令也可以在我们的 template 中自动获得。

调用时机

创建组实例,然后初始化 props,紧接着就调用 setup 函数。从生命周期钩子的视角来看,它会在 beforeCreate 钩子之前被调用。

模板中使用

如果 setup 返回(以count例子来看,可以暂时理解为创建)一个对象,则对象的属性将会被合并到组件模板的渲染上下文。

setup 参数

第一个参数接收一个响应式的 props ,这个 props 指向的是外部的 props。如果没有定义 props 选项,setup 中的第一个参数将为 undefined。

  • 不要在自组件中修改 props,如果尝试修改,会得到警告甚至报错
  • 不要解构 props,解构的 props 会失去响应式

第二个参数提供了一个上下文对象

组件自动注册

导入 component 或 directive 直接 import 即可,无需额外声明

import  MyButton  from "@/components"
import  directive as clickOutside  from \'v-click-outside\'

在 script setup 中,引入的组件可以直接使用,无需再通过 components 进行注册,并且无法指定当前组件的名字,它会自动以文件名为主,也就是无法再写 name 属性了。

如果需要定义类似 name 的属性,可以再加个平级的 script 标签,在里面实现即可

组件核心 API 的使用

定义组件的 props

通过 defineProps 指定当前的 props 类型,获得上下文的 props 对象。

<script setup>
  import defineProps from \'vue\'
  
  const props = defineProps(
    title: String
  )
</script>

或者

<script setup lang="ts">
  import ref, defineProps from \'vue\'
  
  type Props = 
    msg: string
  
  
  defineProps<Props>()
</script>

定义 emit

使用 defineEmit 定义当前组件含有的事件,并通过返回的上下文去执行 emit

<script setup>
  import  defineEmit  from \'vue\'
  
  const emit = defineEmit([\'change\', \'delete\'])
</script>

父子组件通信

defineProps 用来接收父组件传来的 props;defineEmits 用来声明触发的事件

父组件

<template>
  <my-son :foo="count" @childClick="childClick"></my-son>
</template>

<script setup lang="ts">
  import MySon from "./MySon.vue";
  import ref from "vue";

  let count = ref(0)

  let childClick = (e: any): void => 
    count.value++
    console.log("from son", e)
  
</script>

自组件

<template>
  <span @click="sonToFather">信息: props.foo</span>
</template>

<script lang="ts" setup>
  import defineEmits, defineProps from "vue";

  const emit = defineEmits(["childClick"]); // 声明触发事件
  const props = defineProps(foo: Number)      // 获取 props

  const sonToFather = () => 
    emit("childClick", props.foo)
  
</script>

<style scoped></style>

自组件通过 defineProps 接收父组件传过来的数据,自组件通过 defineEmits 定义事件发送信息给父组件

增强的 props 类型定义

  const props2 = defineProps<
    foo: number,
    bar?: number
  >()
  
  const emit2 = defineEmits<(
    e: \'update\' | \'delete\',
    id: number
  ) => void>()

采用这种方法将无法使用 props 默认值

定义响应变量、函数、监听、计算属性 computed

watchEffect

<script setup lang="ts">
import ref, computed, unref, watchEffect from \'vue\';

const count = ref(0) // 不用 return ,直接在 template 中使用

const addCount = () => 
  count.value ++


// 定义计算属性,使用同上
const howCount = computed(() => "现在count值为: " + count.value)

// 定义监听,使用同上,用于有副作用的监听,会自动收集依赖,和 watch 区别:无需区分deep,immediate,只要依赖的数据发生变化就会调用
watchEffect(() => console.log(count.value))

</script>

reactive

reactive 是一个响应式对象,ref 是一个 value: \'xxx\' 的结构

<script setup lang="ts">
import reactive, onUnmounted from "vue";

const state = reactive(
  counter: 0
)

// 定时器,每秒都会更新数据
const timer = setInterval(() => 
  state.counter ++
, 1000)

onUnmounted(() => 
  clearInterval(timer)
)
</script>

<template>
  <div>state.counter</div>
</template>

ref 暴露变量到模板

无需 export 声明,编译器会自动寻找模板中使用的变量。

生命周期方法

因为 setup 是围绕 beforeCreate 和 created 生命周期钩子运行的,所以不需要显式地定义它们。
换句话说,在这些钩子中编写的任何代码都应该直接在 setup 函数中编写。

可以在生命周期钩子前面加上 "on" 来访问组件的声明周期钩子。

获取 slots 和 attrs

useAttrsuseSlots

useAttrs 得到的结果

defineExpose API

传统的写法,我们可以在父组件中,通过 ref 实例的方式去访问自组件的内容,但在 script setup 中,该方法就不能用了,setup 相当于是一个闭包,除了内部的 template 模板,谁都不能访问内部的数据和方法。

如果需要对外暴露 setup 中的数据和方法,需要使用 defineExpose API

const a = 1
const b = ref(2)
defineExpose(a,b)

未亲自测试!!

支持 async await 异步

在 vue3 的源代码中,setup 执行完毕,函数 getCurrentInstance 内部的有个值会释放对 currentInstance 的引用
await 语句会导致后续代码进入异步执行的情况。

所以上述例子中最后一个 getCurrentInstance() 会返回 null,建议使用变量保存第一个 getCurrentInstance() 返回的引用

上述结论还没有亲自实验证实

定义组件其它配置

配置项的缺失,有时候我们需要更改组件选项,在 setup 中我们目前是无法做到的。
我们需要在上方再引入一个 script ,在上方写入对应的 export 即可,需要单开一个 script

import HelloWorld from \'../components/HelloWorld.vue\' // 在 setup() 作用域中执行 (对每个实例皆如此) // your code ```

关于 TS 和 ESLint 的不完美

  1. 与@typescript-eslint/no-unused-vars规则不兼容,影响不大可以关闭
  2. 与导入的类型声明不兼容,当通过解构的方式去导入类型,setup sugar 会进行自动导出。这时候会收到 TS 的一条报错:此为类型,但被当作值使用。解决办法:类型导出使用 export default 导出或者引入时使用 import * as xx 来进行导入,也可以使用 import type test from "./test"解决

使用习惯

推荐分区方式:

  1. 相关引入
  2. 响应式数据,props,emit定义
  3. 生命周期以及 watch 书写
  4. 方法定义
  5. 方法、属性暴露

组件抽离:将页面拆成两个文件夹,一个为views ,另一个为components。
views 目录为页面入口,掌管数据,而components则为页面中一些组件抽离。如果是公共组件,再抽离到 components目录下其它位置。

hook 抽离:尽可能将逻辑抽离,并不一定要进行复用。

Vue3.2 setup语法糖总结

前言

提示:Vue3.2 版本开始才能使用语法糖!

Vue3.0 中变量必须 return 出来,template中才能使用;而在 Vue3.2 中只需要在 script 标签上加上 setup 属性,无需 returntemplate 便可直接使用,非常的香啊!


一、如何使用setup语法糖

只需在 script 标签上写上setup

代码如下(示例):

<template>
</template>
<script setup>
</script>
<style scoped lang="less">
</style>

二、data数据的使用

由于 setup 不需写 return,所以直接声明数据即可

代码如下(示例):

<script setup>
    import 
      ref,
      reactive,
      toRefs,
     from 'vue'
    
    const data = reactive(
      patternVisible: false,
      debugVisible: false,
      aboutExeVisible: false,
    )
    
    const content = ref('content')
    //使用toRefs解构
    const  patternVisible, debugVisible, aboutExeVisible  = toRefs(data)
</script>

三、method方法的使用

代码如下(示例):

<template >
    <button @click="onClickHelp">vue3体验新方法</button>
</template>
<script setup>
import reactive from 'vue'

const data = reactive(
      aboutExeVisible: false,
)
// 点击帮助
const onClickHelp = () => 
    console.log('vue3体验新方法')
    data.aboutExeVisible = true

</script>

四、watchEffect的使用

代码如下(示例):

<script setup>
import 
  ref,
  watchEffect,
 from 'vue'

let sum = ref(0)

watchEffect(()=>
  const x1 = sum.value
  console.log('watchEffect所指定的回调执行了')
)
</script>

五、watch的使用

代码如下(示例):

<script setup>
    import 
      reactive,
      watch,
     from 'vue'
     //数据
     let sum = ref(0)
     let msg = ref('hello word')
     let person = reactive(
                    name:'coderkey',
                    age:18,
                    job:
                      j1:
                        salary:20
                      
                    
                  )
    // 两种监听格式
    watch([sum,msg],(newValue,oldValue)=>
            console.log('sum或msg变了',newValue,oldValue)
          ,immediate:true)
          
     watch(()=>person.job,(newValue,oldValue)=>
        console.log('person的job变化了',newValue,oldValue)
     ,deep:true) 
 
</script>

六、computed计算属性的使用

computed计算属性有两种写法(简写和考虑读写的完整写法)

代码如下(示例):

<script setup>
    import 
      reactive,
      computed,
     from 'vue'

    //数据
    let person = reactive(
       firstName:'coder',
       lastName:'key'
     )
    // 计算属性简写
    person.fullName = computed(()=>
        return person.firstName + '-' + person.lastName
      ) 
    // 完整写法
    person.fullName = computed(
      get()
        return person.firstName + '-' + person.lastName
      ,
      set(value)
        const nameArr = value.split('-')
        person.firstName = nameArr[0]
        person.lastName = nameArr[1]
      
    )
</script>

七、props父子传值的使用

子组件代码如下(示例):

<template>
  <span>props.name</span>
</template>

<script setup>
  import  defineProps  from 'vue'
  // 声明props
  const props = defineProps(
    name: 
      type: String,
      default: '11'
    
  )  
  // 或者
  //const props = defineProps(['name'])
</script>

父组件代码如下(示例):

<template>
  <child :name='name'/>  
</template>

<script setup>
    import ref from 'vue'
    // 引入子组件
    import child from './child.vue'
    let name= ref('coderkey')
</script>

八、emit子父传值的使用

子组件代码如下(示例):

<template>
   <button @click="sonClick">
     确定
   </button>
</template>
<script setup>
import  defineEmits  from 'vue';

// emit
const emit = defineEmits(['aboutExeVisible'])
/**
 * 方法
 */
// 点击确定按钮
const sonClick= () => 
  emit('customClick');

</script>

父组件代码如下(示例):

<template>
  <AdoutExe @customClick="sonSendFatherMethod" />
</template>
<script setup>
import reactive from 'vue'
// 导入子组件
import soncomponent from '../components/soncomponent'

const data = reactive(
  aboutExeVisible: false, 
)
// content组件ref


// 关于系统隐藏
const sonSendFatherMethod= () => 
  data.aboutExeVisible = false


</script>

九、获取子组件ref变量和defineExpose暴露

vue2中的获取子组件的ref,直接在父组件中控制子组件方法和变量的方法。

子组件代码如下(示例):

<template>
    <p>data </p>
</template>

<script setup>
import 
  reactive,
  toRefs
 from 'vue'

/**
 * 数据部分
 * */
const data = reactive(
  modelVisible: false,
  historyVisible: false, 
  reportVisible: false, 
)
defineExpose(
  ...toRefs(data),
)
</script>

父组件代码如下(示例):

<template>
    <button @click="btnClick">点击</button>
    <Content ref="content" />
</template>

<script setup>
import ref from 'vue'

// content组件ref
const content = ref('content')
// 点击设置
const btnClick= ( key ) => 
   content.value.modelVisible = true


</script>
<style scoped lang="less">
</style>

十、路由useRoute和useRouter的使用

代码如下(示例):

<script setup>
  import  useRoute, useRouter  from 'vue-router'
    
  // 声明
  const route = useRoute()
  const router = useRouter()
    
  // 获取query
  console.log(route.query)
  // 获取params
  console.log(route.params)

  // 路由跳转
  router.push(
      path: `/index`
  )
</script>

十一、store仓库的使用

代码如下(示例):

<script setup>
  import  useStore  from 'vuex'
  import  num  from '../store/index'

  const store = useStore(num)
    
  // 获取Vuex的state
  console.log(store.state.number)
  // 获取Vuex的getters
  console.log(store.state.getNumber)
  
  // 提交mutations
  store.commit('fnName')
  
  // 分发actions的方法
  store.dispatch('fnName')
</script>

十二、await 的支持

setup 语法糖中可直接使用 await,不需要写 asyncsetup 会自动变成 async setup

代码如下(示例):

<script setup>
  import getData from '../api/Api'
  const data = await getData()
  console.log(data)
</script>

十三、provide 和 inject 祖孙传值

父组件代码如下(示例):

<template>
  <sonComponent  />
</template>

<script setup>
  import  ref,provide  from 'vue'
  import sonComponent from '@/components/sonComponent'

  let name = ref('pink')
  // 使用provide
  provide('provideState', 
    name,
    changeName: () => 
      name.value = 'coderkey'
    
  )
</script>

子组件代码如下(示例):

<script setup>
  import  inject  from 'vue'
  const provideState = inject('provideState')

  provideState.changeName()
</script>

以上是关于关于 Vue3 setup 语法糖的主要内容,如果未能解决你的问题,请参考以下文章

Vue3.2 setup语法糖总结

Vue3.2 setup语法糖总结

vue3语法糖

Vue3.2 中的 setup 语法糖

一文掌握 Vue3.2 setup 语法糖

一文掌握 Vue3.2 setup 语法糖