美文网首页react & vue & angular
Vue3.2语法糖使用总结

Vue3.2语法糖使用总结

作者: QiShare | 来源:发表于2022-11-25 18:10 被阅读0次

    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语法糖使用总结

        本文链接:https://www.haomeiwen.com/subject/lkyxfdtx.html