草庐IT

Vue3.2语法糖使用总结

QiShare 2023-09-22 原文

Vue3.2语法糖使用总结

一. 概述

Vue2时期,组件里定义的各类变量、方法、计算属性等是分别存放到datamethodscomputed等选项里,这样编写的代码不便于后期的查阅,查找一个业务逻辑需要在各个选项来回切换。vue3.0组合式APIsetup函数的推出就是为了解决这个问题,它让我们的逻辑关注点更加集中,语法也更加精简,但是当我们在使用vue3.0的语法就构建组件的时候,总是需要把外面定义的方法变量必须要return出去才能在<template>,比较麻烦一些. vue3.2语法糖的出现以及一些新增的API,让我们的代码进一步简化。

什么是语法糖?

语法糖(英语:Syntactic sugar)是由英国计算机科学家彼得·兰丁发明的一个术语,指计算机语言中添加的某种语法,这种语法对语言的功能没有影响,但是更方便程序员使用。语法糖让程序更加简洁,有更高的可读性。

Vue3.2语法糖

来看下vue3.0vue3.2的单文件组件(SFC,即.vue 文件)的结构对比

  • vue3.0组件
<template>
    <div>
    </div>
</template>
<script>
export default {
    components: {
    },
    props: {
    },
    setup () {
        return {}
    }
}
</script>
<style lang="scss" scoped>
</style>
  • vue3.2组件
<template>
    <MyTestVue :title="title" @click="changeTitle" />
</template>
<script setup>
import MyTestVue from './MyTest.vue';
import { ref } from 'vue';
const title = ref('测试一下')
const changeTitle = () => {
    title.value = 'Hello,World'
}
</script>
<style lang="scss" scoped>
</style>
  1. 对比vue3.0vue3.2版本的组件模板,最主要的变化是3.2中没有了setup函数,而是把它放在了script标签中。

  2. 我们定义的属性和方法也不用在return中返回,直接就可以用在模板语法中
    ...

    这些是直观的变化,接下来我们学习具体的用法。

二.使用介绍

1.组件注册

vue3.0中使用组件,需要使用 components 选项来显式注册:

<script>
import ComponentA from './ComponentA.js'

export default {
  components: {
    ComponentA
  },
  setup() {
    // ...
  }
}
</script>

vue3.2 <script setup> 的单文件组件中,导入的组件可以直接在模板中使用,组件会自动注册,并且无需指定当前组件的名字,它会自动以文件名为主,也就是不用再写name属性了。

<script setup>
import ComponentA from './ComponentA.vue'
</script>

<template>
  <ComponentA />
</template>

2.Props 声明

vue3.0中,prop可以使用props选项来声明

<script>
export default {
  props: ['foo'],
  // 或者用这种方式指类型与默认值
  // props: {
  //   foo:{
  //     type: String,
  //     default: ''
  //   },
  // },
  setup(props) {
    // setup() 接收 props 作为第一个参数
    console.log(props.foo)
  }
}
</script>

vue3.2组件中,props可以使用defineProps()宏来声明

<script setup>
const props = defineProps(['foo'])
// 或者
const propsOther = defineProps({
  title: String,
  likes: Number
})

console.log(props.foo)
</script>

注意事项:所有的 props 都遵循着单向绑定原则,props 因父组件的更新而变化,自然地将新的状态向下流往子组件,而不会逆向传递,这意味着你不应该在子组件中去更改一个 prop。

3.计算属性

我们一般使用计算属性来描述依赖响应式状态的复杂逻辑。说白了就是这个计算属性的值依赖于其他响应式属性的值,依赖的属性发生变化,那么这个计算属性的值就会进行重新计算。

<script setup>
import { ref, computed } from 'vue'

const firstName = ref('John')
const lastName = ref('Doe')

const fullName = computed({
  // getter
  get() {
    return firstName.value + ' ' + lastName.value
  },
  // setter
  set(newValue) {
    // 注意:我们这里使用的是解构赋值语法
    [firstName.value, lastName.value] = newValue.split(' ')
  }
})
</script>

当调用fullName.value = 'John Doe'时,setter会被调用,而firstNamelastName会被更新,在vue3.2中我们可以直接在<template>标签中使用它,不在需要return返回。

  • 不要在计算函数中做异步请求或者更改 DOM!
  • 一个计算属性仅会在其响应式依赖更新时才重新计算,如果他依赖的是个非响应式的依赖,及时其值发生变化,计算属性也不会更新。
  • 相比于方法而言,计算属性值会基于其响应式依赖被缓存,一个计算属性仅会在其响应式依赖更新时才重新计算

4. watch

在组合式API中,我们可以使用watch函数在每次响应式状态发生变化时触发回调函数,watch的第一个参数可以是不同形式的“数据源”:它可以是一个ref(包括计算属性)、一个响应式对象、一个 getter 函数、或多个数据源组成的数组:
watch()是懒执行的:仅当数据源变化时,才会执行回调,例如:

<script setup>
import { ref,watch } from 'vue';

const props = defineProps({
    title: String,
    itemList: {
        type: Array,
        default: () => [{
            text: 'title',
            value: 0
        }]
    }
})

watch(() => props.itemList.length,(newValue,oldValue) => {
    console.log('newValue===',newValue);
    console.log('oldValue===',oldValue);
})
</script>

这里监听props.itemList.length,当传入的itemList数量发生变化时,后面的回调方法会被调用。当然wacth()还有第三个可选参数:否开启深监听(deep), 如果这里这样写:

<script setup>
import { ref,watch } from 'vue';
...
watch(() => props.itemList,(newValue,oldValue) => {
    console.log('newValue===',newValue);
    console.log('oldValue===',oldValue);
})
</script>

当传入的itemList数量发生改变时,回调函数不会触发,正确的写法是加上其第三个参数deep:true

<script setup>
import { ref,watch } from 'vue';
...
watch(() => props.itemList,(newValue,oldValue) => {
    console.log('newValue===',newValue);
    console.log('oldValue===',oldValue);
},{deep:true})
</script>

watch也可以同时监听多个属性:

<script setup>
import { ref,watch } from 'vue';

const props = defineProps({
    title: String,
    itemList: {
        type: Array,
        default: () => [{
            text: 'title',
            value: 0
        }]
    }
})
// 同时监听多个属性
watch(() => [props.itemList,props.title],(newValue,oldValue) => {
    console.log('newValue===',newValue);
    console.log('oldValue===',oldValue);
},{deep:true})
  
</script>

5. watchEffect()

watch()的懒执行不同的是,watchEffect()会立即执行一遍回调函数,如果这时函数产生了副作用,Vue会自动追踪副作用的依赖关系,自动分析出响应源。上面的例子可以重写为:

<script setup>
  ...
watchEffect(() => {
    console.log('itemList===',props.itemList.length);
    console.log('title===',props.title);
})
</script>  

这个例子中,回调会立即执行。在执行期间,它会自动追踪props.itemList.length作为依赖(和计算属性的行为类似)。每当传入的itemList.length变化时,回调会再次执行。

如果要清除watchEffect()的的监听,只需要显示的调用watchEffect()的返回函数就可以了,例如:

<script setup>
  ...
const stopEffect = watchEffect(() => {
    console.log('itemList===',props.itemList.length);
    console.log('title===',props.title);
})
stopEffect()
</script>

watch 只追踪明确侦听的数据源。它不会追踪任何在回调中访问到的东西。另外,仅在数据源确实改变时才会触发回调。我们能更加精确地控制回调函数的触发时机。
watchEffect,则会在副作用发生期间追踪依赖。它会在同步执行过程中,自动追踪所有能访问到的响应式属性。

6.组件的事件调用

6.1 子组件调用父组件的方法

vue3.0中如果我们的子组件触发父组件的方法,我们的做法:

子组件
<script>
export default {
  emits: ['inFocus', 'submit'],
  setup(props, ctx) {
    ctx.emit('submit',params)
  }
}
// 或者将可以将emit解构使用
export default {
    setup(props,{emit}) {
    emit('submit',params)
  }
}
</script>
父组件
<template>
    <Children @submit="submitHandel"/>
  </div>
</template>

<script>
export default {
  name: 'TodoItem',
  setup(props, { emit }) {
    const submitHandel = () => {
      console.log('子组件调用了父组件的submitHandel方法');
    }
    return {
      submitHandel,
    }
  }
};
</script>

vue3.2语法糖中,子组件要触发的事件需要显式地通过 defineEmits() 宏来声明

子组件
<script setup>
const emit = defineEmits(['inFocus', 'submit'])

function buttonClick(parmas) {
  emit('submit', parmas)
}
</script>
父组件
<template>
    <Children @submit="submitHandel"/>
  </div>
</template>

<script setup>
  const submitHandel = () => {
    console.log('子组件调用了父组件的submitHandel方法');
  }
};
</script>
6.2 父组件调用子组件的方法或是属性

vue3.0中如果父组件触发子组件的方法或是属性,直接在return函数中返回就可以,数据都是默认隐式暴露给父组件的。

<script>
// 子组件
setup(props, { emit }) {
  const isShow = ref(false)
  // 父组件调用这个方法
  const showSubComponent = () => {
    isShow.value = !isShow.value
  }
  return {
      // return 返回
      showSubComponent,
    }
  }
</script>

父组件中通过ref获取到子组件,并对子组件暴露的方法进行访问

父组件
<template>
  <div class="todo-list">
    <TodoItemVue :itemList="itemList" @clickItemHandel="clickItemHandel" ref="todoItemVueRef" />
  </div>
</template>
<script>
  import { ref } from 'vue';
  export default {
  setup(props, { emit }) {
    //获取子组件ref
    const todoItemVueRef = ref(null)
    // 调用子组件的方法
    const callItemFuncHandel = () => {
        todoItemVueRef.value.showSubComponent()
    }
    return {
     todoItemVueRef
    }
  }
};
</script>

vue3.2语法中,父组件的调用方式相同,子组件通过defineExpose()将方法或是属性暴露出去

子组件
<script setup>
const isShow = ref(false)
// 父组件调用这个方法
const showSubComponent = () => {
    isShow.value = !isShow.value
}
// 通过defineExpose将方法暴露出去
defineExpose({
    showSubComponent
})
</script> 
父组件
<template>
  <div class="todo-list">
    <TodoItemVue :itemList="itemList" @clickItemHandel="clickItemHandel" ref="todoItemVueRef" />
  </div>
</template>
<script setup>
  import { ref } from 'vue';
  //获取子组件ref
  const todoItemVueRef = ref(null)
  // 调用子组件的方法
  const callItemFuncHandel = () => {
      todoItemVueRef.value.showSubComponent()
  }
</script>

7.Vuex的使用

vue3.0vue3.2中创建Vuex没有区别,只不过在<template>模板中使用Vuex的store有细微差别。

import { createStore } from 'vuex';
import { ADD_ITEM_LIST, REDUCE_ITEM_LIST, CHANGE_ITEM_LIST_ASYNC } from './constants';

export default createStore({
  state: {
    itemList: [
      { text: 'Learn JavaScript', done: true },
      { text: 'Learn Vue', done: false },
      { text: 'Build something awesome', done: false },
    ],
  },
  getters: {
    doneItemList: (state) => state.itemList.filter((todo) => todo.done),
  },
  mutations: {
    // 使用ES2015风格的计算属性命名功能 来使用一个常量作为函数名
    [ADD_ITEM_LIST](state, item) {
      console.log('增加数据', item);
      state.itemList.push(item);
    },
    [REDUCE_ITEM_LIST](state) {
      console.log('减少数据');
      state.itemList.pop();
    },
  },
  actions: {
    [CHANGE_ITEM_LIST_ASYNC]({ commit, state }, todoItem) {
      /// 模拟网络请求
      setTimeout(() => {
        commit(ADD_ITEM_LIST, todoItem);
        console.log('state===', state);
      }, 1000);
    },
  },
  modules: {
  },
});

vue3.0中我们一般在return中对store.state进行解构,然后可以直接在<template>中使用state中的值

<template>
  <div class="todo-item">
    <ol>
      <li v-for="(item,index) in itemList" :key="index" class="todos" @click="clickItem(index)">
        {{ item.text }}
      </li>
    </ol>
  </div>
</template>
<script>
  export default {
  name: 'TodoItem',
  setup(props, { emit }) {
    return {
      // 对store.state进行解构
      ...store.state,
      clickItem,
      count,
      isShow,
      showSubComponent,
    }
  }
};
</script>

vue3.2中没有了return,需要我们显示的获取要使用的stare的值

<template>
  <div class="todo-item">
    <ol>
      <li v-for="(item,index) in itemList" :key="index" class="todos" @click="clickItem(index)">
        {{ item.text }}
      </li>
    </ol>
  </div>
</template>
<script setup>
import { useStore } from 'vuex';
const store = useStore()
// 获取后在<template>中使用
const itemList = store.state.itemList
</script>

8. <style>中的 v-bind

<style>中的 v-bind: 用于在 SFC <style> 标签中启用组件状态驱动的动态 CSS 值

<script setup>
import { ref, watchEffect } from 'vue';
const color = ref('black')
const callChangeColorHandel = () => {
  if(color.value === 'black') {
    color.value = 'red'
  }else {
    color.value = 'black'
  }
}
</script>
<style lang="scss" scoped>
.todo-list {
  color: v-bind(color);
}
</style>

触发callChangeColorHandel 函数,在<style>中的v-bind指令可以动态绑定的响应式状态。

三. 总结

整体来说,setup语法糖的引入简化了使用Composition API时冗长的模板代码,也就是让代码更加简洁,可读性也更高。并且官方介绍vue3.2在界面渲染的速度以及内存的使用量上都进行了优化,本文只是对setup语法糖的常用方式进行了总结,更多vue3.2新特性可以去官方文档查看。

一些参考:

Vue3.2

Vuex

有关Vue3.2语法糖使用总结的更多相关文章

  1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

    我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

  2. ruby - 使用 RubyZip 生成 ZIP 文件时设置压缩级别 - 2

    我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看ruby​​zip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d

  3. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  4. ruby-on-rails - 使用 Ruby on Rails 进行自动化测试 - 最佳实践 - 2

    很好奇,就使用ruby​​onrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提

  5. ruby - 在 Ruby 中使用匿名模块 - 2

    假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于

  6. ruby - 使用 ruby​​ 和 savon 的 SOAP 服务 - 2

    我正在尝试使用ruby​​和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我

  7. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  8. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  9. ruby - 使用 ruby​​ 将 HTML 转换为纯文本并维护结构/格式 - 2

    我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h

  10. ruby - 树顶语法无限循环 - 2

    我脑子里浮现出一些关于一种新编程语言的想法,所以我想我会尝试实现它。一位friend建议我尝试使用Treetop(Rubygem)来创建一个解析器。Treetop的文档很少,我以前从未做过这种事情。我的解析器表现得好像有一个无限循环,但没有堆栈跟踪;事实证明很难追踪到。有人可以指出入门级解析/AST指南的方向吗?我真的需要一些列出规则、常见用法等的东西来使用像Treetop这样的工具。我的语法分析器在GitHub上,以防有人希望帮助我改进它。class{initialize=lambda(name){receiver.name=name}greet=lambda{IO.puts("He

随机推荐