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

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~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容