美文网首页vue3
Vue3.2一篇进阶

Vue3.2一篇进阶

作者: Famous | 来源:发表于2022-09-25 19:50 被阅读0次

    什么是setup?

    老版本:

    <script lang="ts">
    import { defineComponent,ref, watch } from 'vue';
    import { Upload } from 'ant-design-vue';
    export default defineComponent({
      name: 'good-card',
      components: { Upload },
      props: {
        name: {
          default: '',
          type: String,
          required: true,
        }
      },
      emits: ['changeCount'],
      setup: (props, context) => { // 传入props 以及上下文context
        const count = ref(0)
        const addCount = (item) => {
          count.value++
          context.emit('changeCount', item)
        }
        function reset = ()=>{count.value = 0}
        context.expose({reset}) // 向外部组件暴露部分方法
        watch(
         () => props.name,
         (newVal, oldVal) => {
           console.log(newVal, oldVal)
         })
        return { count, addCount }
      },
    })
    </script>
    

    你用的这个说明你Out啦,快试试下面这个方式吧!

    一、如何使用setup语法糖

    <script lang="ts" setup>
    console.log('直接写入script标签')
    </script>
    

    上面的代码可精简为

    <script lang="ts" setup>
    import { ref } from 'vue';
    import { Upload } from 'ant-design-vue';
    const props = defineProps({
      name: {
        type: String,
        default: '',
      },
    });
    function reset = ()=>{count.value = 0}
    const $emits = defineEmits(['changeCount']);
    const count = ref(0)
    const addCount = (item) => {
        count.value++
        $emits('changeCount', item)
    }
    watch(
       () => props.name,
       (newVal, oldVal) => {
         console.log(newVal, oldVal)
     })
    defineExpose({ reset }); // 向外部组件暴露部分方法
    
    const post = await fetch(`/api/xxx`).then(res => xxx) // 有await会自动给setup 前面加上async
    </script>
    

    setup是Vue3.0后推出的语法糖,并且在Vue3.2版本进行了大更新,像写普通JS一样写vue组件,对于开发者更加友好了;按需引入computed、watch、directive等选项,一个业务逻辑可以集中编写在一起,让代码更加简洁便于浏览。在这个环境下,所有定义的变量,函数,computed等等会数据自动return,页面Html中可以直接使用,且script 标签中引入的组件自动注册 可以使用顶层 await。结果代码会被编译成 async setup()。

    ps: 新版本defineProps, defineExpose, defineEmit,watch,computed,watchEffect 等都是内置的,无需再import,可直接省略

    警报!重点来了~ data响应是Vue 核心,千万理解透喔~

    二、定义data - Ref

    <template>
        <div class="home-container">
            <p>姓名: {{ girl.name }}</p>
            <p>年龄: {{ girl.age }}</p>
            <p>身高: {{ girl.height }}</p>
            <p>
                科目: <span v-for="(item, index) in subjects" :key="item + index">{{ item }}</span>
            </p>
            <p>考试结果是: {{ score }}</p>
        </div>
    </template>
    
    <script lang="ts" setup>
    import { ref } from 'vue';
    // 推荐定义无嵌套的类型  简单理解: 除了对象以及内含对象的所有类型
    // 原理 靠Object.defineProperty()的get与set完成响应式
    const score = ref(99);
    score.value++; // 改值是.value 赋值
    
    // 如果你用了对象呢?
    const girl = ref({ name: '小红', height: 170, age: 17 }); // 等同于 reactive({value: {name: '小红', height: 170, age: 17}})
    girl.value.age++; // 改值都要.value,显得很冗余
    
    // 数组是复杂类型,如果没有嵌套,依然建议用ref
    const subjects = ref(['语文', '数学']);
    setTimeout(() => {
      subjects.value = ['语文', '英语'];
      subjects.value[1] = '美术';
    }, 0);
    </script>
    

    三、定义data - Reactive

    <template>
        <div class="home-container">
            <p>姓名: {{ girl.name }}</p>
            <p>年龄: {{ girl.age }}</p>
            <p>身高: {{ girl.height }}</p>
            <p>
                科目: <span v-for="(item, index) in girl.subjects" :key="item + index">{{ item }}</span>
            </p>
            <p>考试结果是: {{ girl.score }}</p>
        </div>
    </template>
    
    <script lang="ts" setup>
    import { ref, reactive } from 'vue';
    // 对象以及内含对象的类型
    // 原理 Proxy代理 可监听到对象的属性值变化,以及key的增删
    const girl = reactive({
        name: '小红',
        height: 170,
        age: 17,
        score: 99,
        subjects: ['语文', '数学']
    });
    girl.age++; // 改值是点出对应的key 赋值,切记 不能整体赋值,如 girl = {}  这样的会丢失响应式
    girl.score++;
    girl.subjects = ['语文', '英语'];
    </script>
    

    问: Reactive 把数据聚合在一起,用它定义所有数据不就行了,Ref弃了呀,还要.value修改,多麻烦?
    答: 不行
    理由1、vue2 的data 形式就类似这样,把所有的定义数据都放在一个data,别忘了,vue3改造composition API 的初衷就是想要把数据和逻辑整合在一起,一块代码一个功能。方便写功能时代码优雅组织在一起,还便于未来用hook函数可以提取出去给其它页面复用。
    理由2、reactive 推荐场景是对象中数据是有关联的一组,比如我们这里girl对象,如果还有比如form 对象表单相关属性,应该另外定义而不是全杂合在一起。
    理由3、reactive 定义的对象,尤其要注意不能整体赋值,比如girl = {xxx} 这样会丢失响应式,即使是赋值girl = reactive({xxx}),如下,试验,记得改girl 的const 为let

    setTimeout(() => {
        girl = reactive({ ...girl, subjects: ['语文', '历史'] });
    }, 0);
    

    不可行,原理解析: girl = reactive({XXX}) 中girl 被赋值为新的地址,就如同a = 1 变成了 a = 2,但是a 本身不是任何响应式对象的某个属性

    • 网上解决方案1 改为ref
      这里有个隐藏的知识点,ref 赋值对象时,会借用reactive 包装成为一个reactive,这时候的响应式是靠的proxy,赋值非对象类型时,则靠Object.defineProperty()的get与set完成响应式
      因此这里girl = ref({XXX}) 等同于 girl = reactive({value: {XXX}})
      你重新赋值 girl.value = {XXX} 触发了proxy代理的响应式对象 {value: {XXX}} 的监听
      但我们上面说过,对象及含内嵌对象的不推荐放到ref,这么做不优雅 Pass
    • 网上解决方案2 嵌套
      定义对象的时候我们这样嵌套 let baseData = reactive({girl: {XXX}})
      想要整体赋值修改girl 的时候,那就是baseData.girl = {XXX}
      这样赋值触发了 proxy代理的响应式对象{girl: {XXX}} 的属性监听,当然可行
      但如果我们都这样去处理,多了一个baseData层级,reactive 本身一个优势就是为了少去.value,而且再来个boy? man?
      那你可能想把 boy,man,woman都放进baseData里去,这又变得和vue2一样,违背了composition API的初衷 Pass
      上述两种方案都可行,但冗余不优雅,改动量大
    • 最佳解决方案3
      我们无非是想要触发girl = reactive({XXX}) 中 {XXX} proxy 代理的对象的监听,我们可以用Object.assign
      Object.assign({...girl, {subjects: ['语文', '历史']}})
    setTimeout(() => {
        Object.assign(girl, { subjects: ['语文', '历史'] });
        // 可行,简洁优雅~  
        // 可别{ ...girl },人家是proxy代理过的,你解构就丢响应式了
    }, 0);
    

    问: 值得注意的是不管是用Ref 还是 Reactive,都用const定义为什么呢?
    Ref 是赋值我们知道其实对其.value 属性在赋值,本身指向的地址没有变,而Reactive 是把一个对象处理成为proxy代理的响应式对象,应想着触发对象的变化,而不是把这个girl赋值为即使是另一个新的reactive对象。我们用const 既符合实际,也避免了犯错~

    四、定义data - toRefs

    上面我们论证了使用Reactive的必要和场景,以及怎么触发它的修改,但是聪明的你一定发现在模板里使用并不简洁,Reactive Html中会多一个层级,如果对象复杂,可能更麻烦。但你会发现Ref 不需要.value,我们打印一下Ref对象,可以发现有v_isRef 属性,为true,系统会自动使用.value的值。那我们只要把Reactive 中的属性值来一遍,加上这个属性变成ref,这 就是toRefs
    ps: 其实就是把Reactive中的每一项都变成了Ref,而这个对象自身又是通过proxy代理监听实现响应式的

    在setup 环境下,我们只要如下

    <script lang="ts" setup>
    import { toRefs, ref, reactive } from 'vue';
    const { name, height, age, score, subjects } = toRefs(girl);
    </script>
    

    模板里就可以拿掉girl了,但命名记得唯一,按照我们使用reactive 的场景,最佳的是命名为girl_name这样的形式

    <template>
        <div class="home-container">
            <p>姓名: {{ name }}</p>
            <p>年龄: {{ age }}</p>
            <p>身高: {{ height }}</p>
            <p>
                科目: <span v-for="(item, index) in subjects" :key="item + index">{{ item }}</span>
            </p>
            <p>考试结果是: {{ score }}</p>
        </div>
    </template>
    
    猫咪
    喝杯茶歇会
    吃透上面的内容,那就开启轻松旅程了呀~

    五、method方法

    <script lang="ts" setup>
    function add(){}
    </script>
    

    setup环境自动return ,只管定义,当然如果想暴露给父组件用上面提到的的expose,记得功能相关的放在一起更优雅

    六、watch、watchEffect、computed

    都是自动return
    computed 计算属性,vue2一样,写法如下

    <template>
         <p>考试结果是: {{ result }}</p>
    </template>
    <script lang="ts" setup>
    import { ref } from "vue";
    const score1 = ref(95);
    const score2 = ref(92);
    const result = computed(() => {
        return score1.value + score2.value;
    });
    setTimeout(() => {
        score2.value++;
    }, 0);
    </script>
    

    watch和watchEffect

    <script lang="ts" setup>
    // 特定响应式对象监听 可以获取新旧值
    watch(
      searchInput,
     (newVal, oldVal) => {
        console.log("watch searchInput:", newVal, oldVal);
      },
      {
        immediate: true, // 可选, 立即监听,初始值也会执行
        deep: true // 可选, 嵌套复杂类型,深度监听
      });
    
    // 多响应式对象监听
    watch(
      [firstName,lastName],
     ([newFirst,newLast], [oldFirst,oldlast]) => {
       // .....
      });
    
    // 所有依赖响应式对象监听,无旧值
    const result = ref(0);
    watchEffect(() => {
       result.value = score1.value + score2.value + 12;
    }); // 有点像计算属性,注重执行,无需return,computed注重结果,需要return
    </script>
    

    ps: watch,computed,watchEffect,在setup环境下都可直接使用,无需import

    相信你对Vue3基操练习的比较到位了,感谢支持,会继续更新,点赞支持喔~

    还有精力,再来一发?年轻人, 别太肝啊, 喝点水,OK,来继续
    Vue3.2 组件间通信

    相关文章

      网友评论

        本文标题:Vue3.2一篇进阶

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