美文网首页
vue3常用api盘点及现有vue2组件迁移示例

vue3常用api盘点及现有vue2组件迁移示例

作者: eks | 来源:发表于2021-02-02 11:08 被阅读0次
    vue3.png

    前言:

    vue3项目是在两年前开始的,正式版3.0于2020年9月发布;
    目前vue生态支持情况还不完善,如vuex4还处于rc版本,并存在一定的bug,暂不推荐大家用在标准版项目里;
    不过那些无关紧要的项目必须可以试试;
    再者vue3的官方生态包为了让打包后的文件尽可能的小,打包文件都输出了ECMASCRIPT新语法,这就是网上吹牛逼说的vue3更快、更小;

    一、生命周期

    1. beforeCreate -> setup()
    2. created -> setup()
    3. beforeMount -> onBeforeMount // 在挂载前执行某些代码
    4. mounted -> onMounted // 在挂载后执行某些代码
    5. beforeUpdate -> onBeforeUpdate // 在更新前前执行某些代码
    6. updated -> onUpdated // 在更新后执行某些代码
    7. beforeDestroy -> onBeforeUnmount // 在组件销毁前执行某些代码
    8. destroyed -> onUnmounted // 在组件销毁后执行某些代码
    9. errorCaptured -> onErrorCaptured // 错误捕获

    二、常用api

    !中文文档(https://www.vue3js.cn/docs/zh/api/global-api.html#createapp)

    1. ref
    2. reactive
    3. watch
    4. computed
    5. nextTick
    6. props

    三、vite创建vue3项目

    文档地址(https://vitejs.dev/guide/#scaffolding-your-first-vite-project)

    • npm init @vitejs/app my-vue-app --template vue
    • cd .\my-vue-app\
    • npm install
    1. ref演示


      ref.gif
    <template>
      <button @click="state++">count is: {{ state }}</button>
    </template>
    
    <script setup>
    import { ref } from 'vue'
    
    const state = ref(0)
    </script>
    
    • 提示:获取dom也需要ref
    • 思考1:怎么用ref获取dom
    1. reactive演示


      reactive.gif
    <template>
      <button @click="state.count++">count is: {{ state.count }}</button>
    </template>
    
    <script setup>
    import { reactive } from 'vue'
    
    const state = reactive({ count: 0 })
    </script>
    
    • 思考2:以上可以看出,如果用reactive声明n个变量,在dom中使用都要加上state.xxx,岂不是很麻烦,
      现在我要这样的效果 <div>count is: {{ count }}</div>,您会怎么做?
    1. watch演示


      watch.gif
    <template>
        <div>reactive</div>
      <button @click="state.count ++">count is: {{ state.count }}</button>
        <div>{{ state.text }}</div>
    </template>
    
    <script setup>
    import { reactive, watch } from 'vue'
    
    const state = reactive({
        count: 0,
        text: 'ready'
    })
    
    watch(() => state.count, (n, o) => {
        if (n > 5) state.text = '大哥,别点了,count大于5了';
        else state.text = 'count小于5';
    });
    </script>
    
    • 思考3:
      1. 我想同时监听多个变量,该怎么做
      2. 深度监听,初始化执行,该怎么配置参数

    4.computed演示


    computed.gif
    <template>
        <div>computed</div>
      <button @click="state.count ++">count is: {{ state.count }}</button>
        <div>{{ doubleCount }}</div>
    </template>
    
    <script setup>
    import { reactive, computed } from 'vue'
    
    const state = reactive({
        count: 0
    })
    
    const doubleCount = computed(() => (state.count * 2))
    </script>
    
    1. nextTick演示


      nextTick.gif
    <template>
        <div>nextTick</div>
      <button @click="countClick">count is: {{ state.count }}</button>
        <div v-if="state.count" id="divEle">{{ state.doubleCount }}</div>
    </template>
    
    <script setup>
    import { reactive, nextTick } from 'vue'
    
    const state = reactive({
        count: 0,
        doubleCount: 0
    })
    
    const countClick = () => {
        state.count ++
    
        nextTick(() => {
            state.doubleCount = state.count * 2
            const divEle2 = document.getElementById('divEle')
            console.log('divEle2::', divEle2);
        });
    
        const divEle1 = document.getElementById('divEle')
        console.log('divEle1::', divEle1);
    }
    </script>
    
    1. props演示


      props.png
    <template>
        <div>defineProps</div>
        <div>{{ msg }}</div>
    </template>
    
    <script setup>
    import { reactive, defineProps } from 'vue'
    
    defineProps({
        msg: String
    })
    
    const state = reactive({
        count: 0,
    })
    
    </script>
    
    1. 以上看着不习惯,setup还可以这样写
    <template>
        <div>defineProps</div>
        <div>{{ msg }}</div>
    </template>
    
    <script>
    import { reactive, defineProps } from 'vue'
    export default {
        props: ['msg'],
        setup() {
           const state = reactive({
              count: 0,
          })
          
          return { state }
        }
    }
    </script>
    

    四、vite打包信息

    vite-build.png
    • vite打包是杠杠的大拇指
    • 但是,vite打包虽然小,单并不支持ie,目前在咱们项目中无法使用

    五、Breadcrumb 面包屑迁移

    完全迁移(composition api jsx篇)

    1. 迁移前
    <template>
        <div class="p-breadcrumb">
            <section class="p-breadcrumb-item" v-for="(item, i) in data" :key="i+'-'+item.id">
                <article
                        :class="[
                            'p-breadcrumb-item-text',
                            (value?value===item.id:i===data.length-1)&&'p-breadcrumb-item-active',
                            (i>0&&i<data.length-1)&&'p-breadcrumb-item-width',
                            (i===data.length-1)&&'p-breadcrumb-item-max-width'
                        ]"
                        v-ptitle:isText:true="item.name"
                        @click="breadcrumbClick(item.id)"
                        @mouseenter="TextEllipsis"
                >{{item.name}}</article>
                <article class="p-breadcrumb-arrow" v-if="i<data.length-1">
                    <ArrowRight />
                </article>
            </section>
        </div>
    </template>
    
    <script>
    import ArrowRight from '../static/iconSvg/arrow_right.svg';
    import TextEllipsis from '../static/utils/TextEllipsis';
    
    export default {
        name: 'Breadcrumb',
        components: { ArrowRight },
        props: {
            // 数据列表
            data: {
                type: Array,
                default: () => []
            },
            // 当前高亮显示的id
            value: {
                type: String,
                default: ''
            }
        },
        data() {
            return {
                titleShow: false // 是否显示title
            };
        },
        methods: {
            TextEllipsis,
            /**
             * 点击某项执行的钩子
             * @param id
             */
            breadcrumbClick(id) {
                if (this.value) this.$emit('input', id);
            }
        }
    };
    </script>
    
    1. 迁移后
    import { defineComponent } from 'vue';
    import TextEllipsis from '../static/utils/TextEllipsis';
    
    import ArrowRight from '../static/iconSvg/arrow_right.svg';
    
    const ArrowRightDom = (
        <article className="p-breadcrumb-arrow">
            <ArrowRight/>
        </article>
    );
    
    const Breadcrumb = defineComponent({
        name: 'Breadcrumb',
        props: {
            // 数据列表
            data: {
                type: Array,
                default: () => []
            },
            // 当前高亮显示的id
            modelValue: {
                type: String,
                default: ''
            }
        },
        emits: ['change', 'update:modelValue'],
        setup(props, { emit }) {
            const breadcrumbClick = (id) => {
                if (props.modelValue) emit('update:modelValue', id);
                else emit('change', id);
            };
            return () => {
                const { data, modelValue } = props;
                return (
                    <div class="p-breadcrumb">
                        {
                            data.map((item, i) => (
                                <section class="p-breadcrumb-item" key={`${i}-${item.id}`}>
                                    <article class={{
                                        'p-breadcrumb-item-text': true,
                                        'p-breadcrumb-item-active': (modelValue ? modelValue === item.id : i === data.length - 1),
                                        'p-breadcrumb-item-width': (i > 0 && i < props.data.length - 1),
                                        'p-breadcrumb-item-max-width': (i === data.length - 1)
                                    }}
                                    onClick={() => breadcrumbClick(item.id)}
                                    onMouseEnter={TextEllipsis}
                                    >{item.name}</article>
                                    {(i < data.length - 1) && <ArrowRightDom/>}
                                </section>
                            ))
                        }
                    </div>
                );
            };
        }
    });
    
    1. 注意
      • class类名的绑定 - 与react中不一样的是,vue中支持对象、数组
      • props - 必须接收
      • emits - 需申明提交方式
      • 事件 - 事件绑定需要on...开头

    保守迁移(option api)

    <template>
        <div class="p-breadcrumb">
            <section class="p-breadcrumb-item" v-for="(item, i) in data" :key="i+'-'+item.id">
                <article
                        :class="[
                            'p-breadcrumb-item-text',
                            (modelValue?modelValue===item.id:i===data.length-1)&&'p-breadcrumb-item-active',
                            (i>0&&i<data.length-1)&&'p-breadcrumb-item-width',
                            (i===data.length-1)&&'p-breadcrumb-item-max-width'
                        ]"
                        @click="breadcrumbClick(item.id)"
                        @mouseenter="TextEllipsis"
                >{{item.name}}</article>
                <article class="p-breadcrumb-arrow" v-if="i<data.length-1">
                    <ArrowRight />
                </article>
            </section>
        </div>
    </template>
    
    <script>
    import ArrowRight from '../static/iconSvg/arrow_right.svg';
    import TextEllipsis from '../static/utils/TextEllipsis';
    
    export default {
        name: 'Breadcrumb',
        components: { ArrowRight },
        props: {
            /**
             * 数据列表
             */
            data: {
                type: Array,
                default: () => []
            },
            /**
             * 当前高亮显示的id
             */
            modelValue: {
                type: String,
                default: ''
            }
        },
        emit: ['update:modelValue'],
        data() {
            return {
                titleShow: false // 是否显示title
            };
        },
        methods: {
            TextEllipsis,
            /**
             * 点击某项执行的钩子
             * @param id
             */
            breadcrumbClick(id) {
                if (this.modelValue) this.$emit('update:modelValue', id);
            }
        }
    };
    </script>
    
    • 问题:
      vue3当中很多废弃api,不能保证所有组件迁移成功(研究中)

    六、v-model与v-show写法

    v-model.png
    • 截图来之某乎,大家可以下去试下,在jsx中并没什么ly
    .vue v-show v-model v-model:title v-model:title.func
    .jsx v-show v-model v-model={[val, 'title']} v-model={[val, 'title', ['func']]}

    七、全局变量

    版本 变量 获取
    vue2 Vue.prototype.$xxx = xxx this.$xxx
    vue3 app.config.globalProperties.$xxx = xxx getCurrentInstance().ctx.$xxx

    八、两个好玩的组件

    1. Teleport 传送门

      • 有这样的场景,我们在做业务的时候,经常会遇到层级(z-index)问题、或者我们项把组件挂在到body下
        示例:
           <Teleport to="body">
               <div>xxx</div>
           </Teleport>
        

      其中to参数值可以指向任何一个容器(容器建议具有唯一性)

    2. Suspense 用作处理异步占位

      • 最常见的当数据没有回来时我们需要一个占位内容,通常是loading或骨架屏
        示例:
              <Suspense>
                  <template #default>
                      <div>主内容(异步加载内容)</div>
                  </template>
                  <template #fallback>
                      <div>loading</div>
                  </template>
              </Suspense>
        

      此处为固定写法,目前官网文档还不完善,且Suspense api可能会改变,别问为什么我知道,网上抄的

    九、以上思考问题回复

    1. 思考1:怎么用ref获取dom
      回复:
         const dom = ref(null);
         <div ref={dom}></div>
      
    2. 思考2:以上可以看出,如果用reactive声明n个变量,在dom中使用都要加上state.xxx,岂不是很麻烦,
      现在我要这样的效果 <div>count is: {{ count }}</div>,您会怎么做?
      回复:
         const state = reactive({
             params1: '111',
             params2: '222',
             params3: '333'
         })
         return {
             ...toRefs(state)
         }
      
    3. 思考3:
      • 我想同时监听多个变量,该怎么做
        回复:
           watch([() => state.params1,() => state.params2,() => state.params3], (n, o) => {
               console.log(n, o);
           });
           const clickHandler = () => {
               state.params1 = '1111+1'
           }
        
      • 深度监听,初始化执行,该怎么配置参数
        回复:
         watch([() => state.params1,() => state.params2,() => state.params3], (n, o) => {
               console.log(n, o);
         }, { deep: true, immediate: true });
      

    总结

    • vue3支持大部分在vue2中的option api,就好比在react17中既可以使用class component也可以使用hooks
    • vue3 api变化大,其中所提供的api远不止本文这些,本文只做了一个简单的入门介绍
    • vue3目前暂不支持ie11-,尤老师本计划在20年第四季度完成这事的,现在来看,不知道啥时候能得到尤老师的好消息
    • vue3的生态是一个浩大的工程,官方正在奋力解决,小伙伴们不要慌

    祝各位工作愉快

    end~

    相关文章

      网友评论

          本文标题:vue3常用api盘点及现有vue2组件迁移示例

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