美文网首页Vue小专栏vue前端开发那些事儿
3、最全的Vue3知识点,随时更新

3、最全的Vue3知识点,随时更新

作者: 天策上将记录学习的地方 | 来源:发表于2021-07-20 10:01 被阅读0次

    创建Vue3项目

    那么正式开始学习我们的Vue3,先从创建项目开始。
    之前第一篇已经写过了

    很多可能出现的问题是自己尝试在修改vue-admin-element为支持3.0版本碰到的,
    传送门,ts版本的已经在更新,但是一个人的力量始终有限,请耐心等待。

    使用 vue-cli 创建

    输入下面的命令然后选择配置项进行安装即可,这里注意vue-cli的版本一定要在4.5.0以上

    // 安装或者升级
    npm install -g @vue/cli
    //查看版本 保证 vue cli 版本在 4.5.0 以上
    vue --version
    // 创建项目
    vue create my-project
    //然后根据提示一步一步傻瓜式操作就行了
    ...
    复制代码
    

    使用 Vite 创建

    都说Vue3.0Vite2更配,各种优化,各种快,但都不属于本文的内容,本文的目的我们只需要知道它特别好用,怎么用就行了。我这里是多选择了TS,每行都有注释,一目了然。

    // 初始化viete项目
    npm init vite-app <project-name>
    // 进入项目文件夹
    cd <project-name>
    // 安装依赖
    npm install
    //启动项目
    npm run dev
    复制代码
    

    创建完以后我们先来看看入口文件main.ts

    // 引入createApp函数,创建对应的应用,产生应用的实例对象
    import { createApp } from 'vue';
    // 引入app组件(所有组件的父级组件)
    import App from './App.vue';
    // 创建app应用返回对应的实例对象,调用mount方法进行挂载  挂载到#app节点上去
    createApp(App).mount('#app');
    复制代码
    

    然后看看根组件app.vue

    //Vue2组件中的html模板中必须要有一对根标签,Vue3组件的html模板中可以没有根标签
    <template>
      <img alt="Vue logo" src="./assets/logo.png">
      <!-- 使用子级组件 -->
      <HelloWorld msg="Welcome to Your Vue.js + TypeScript App" />
    </template>
    
    <script lang="ts">
    // 这里可以书写TS代码
    
    // defineComponent函数,目的是定义一个组件 内部可以传入一个配置对象
    import { defineComponent } from 'vue';
    //引入子级组件
    import HelloWorld from './components/HelloWorld.vue';
    
    // 暴露出去一个定义好的组件
    export default defineComponent({
      // 当前组件的名字
      name: 'App',
      // 注册组件
      components: {
        // 注册一个子级组件
        HelloWorld,
      },
    });
    </script>
    复制代码
    

    Composition API

    接下来到了重头戏,Vue3的招牌特性,Composition API

    Composition API可以更方便的抽取共通逻辑,但是不要过于在意逻辑代码复用,以功能提取代码也是一种思路。

    顺便提一句,Vue3兼容大部分Vue2语法,所以在Vue3中书写Vue2语法是没有问题的(废除的除外),但是既然我们已经升级Vue3了,不建议混合使用,除非一些大型特殊项目需要兼容两个版本。

    setup

    setup是组合Composition API中的入口函数,也是第一个要使用的函数。

    setup只在初始化时执行一次,所有的Composition API函数都在此使用。

    setup() {
      console.log('我执行了') //我执行了
    },
    复制代码
    

    可以通过console.log看到setup是在beforeCreate生命周期之前执行的(只执行一次)

      beforeCreate() {
        console.log('beforeCreate执行了');
      },
      setup() {
        console.log('setup执行了');
        return {};
      },
      //setup执行了
      //beforeCreate执行了
    复制代码
    

    由此可以推断出setup执行的时候,组件对象还没有创建,组件实例对象this还不可用,此时thisundefined, 不能通过this来访问data/computed/methods/props

    返回对象中的属性会与data函数返回对象的属性合并成为组件对象的属性,返回对象中的方法会与methods中的方法合并成功组件对象的方法,如果有重名, setup优先。因为在setupthis不可用,methods中可以访问setup提供的属性和方法, 但在setup方法中不能访问datamethods里的内容,所以还是不建议混合使用。

    setup函数如果返回对象, 对象中的 属性方法 , 模板 中可以直接使用

    //templete
    <div>{{number}}</div>
    
    //JS
    setup() {
      const number = 18;
      return {
        number,
      };
    },
    复制代码
    

    注意:setup不能是一个async函数: 因为返回值不再是return的对象, 而是promise, 模板中就不可以使用return中返回对象的数据了。

    setup的参数(props,context)`

    props: 是一个对象,里面有父级组件向子级组件传递的数据,并且是在子级组件中使用props接收到的所有的属性

    context:上下文对象,可以通过es6语法解构setup(props, {attrs, slots, emit})

    • attrs: 获取当前组件标签上所有没有通过props接收的属性的对象, 相当于 this.$attrs
    • slots: 包含所有传入的插槽内容的对象, 相当于this.$slots
    • emit: 用来分发自定义事件的函数, 相当于 this.$emit

    演示attrsprops

    //父组件
    <template>
      <child :msg="msg" msg2='哈哈哈' />
    </template>
    <script lang='ts'>
    import { defineComponent, ref } from 'vue';
    // 引入子组件
    import Child from './components/Child.vue';
    export default defineComponent({
      name: 'App',
      components: {
        Child,
      },
      setup() {
        const msg = ref('hello,vue3');
        return {
          msg,
        };
      },
    });
    </script>
    
    //子组件
    <template>
      <h2>子组件</h2>
      <h3>msg:{{ msg }}</h3>
    </template>
    
    <script lang='ts'>
    import { defineComponent } from 'vue';
    export default defineComponent({
      name: 'Child',
      props: ['msg'],
      setup(props, {attrs, slots, emit}) {
        console.log('props:', props);//msg: "hello,vue3"
        console.log('attrs:', attrs);//msg2: "哈哈哈"
        return {};
      },
    });
    </script>
    复制代码
    

    演示emit

    //父组件
    <template>
      <child @show="show" />
    </template>
    
    <script lang='ts'>
      setup() {
        const show = () => {
          console.log('name:', 'eric');
        };
        return {
          show,
        };
      },
    </script>
    
    //子组件
    <template>
      <button @click='emitFn'>事件分发</button>
    </template>
    <script lang='ts'>
    import { defineComponent } from 'vue';
    
    export default defineComponent({
      name: 'Child',
      setup(props, { emit }) {
        const emitFn = () => {
          emit('show');
        };
        return {
          emitFn,
        };
      },
    });
    </script>
    复制代码
    

    ref

    作用

    定义一个响应式的数据(一般用来定义一个基本类型的响应式数据UndefinedNullBooleanNumberString)

    语法

    const xxx = ref(initValue):
    复制代码
    

    注意script中操作数据需要使用xxx.value的形式,而模板中不需要添加.value

    用一个例子来演示:实现一个按钮,点击可以增加数字

    <template>
      <div>{{count}}</div>
      <button @click='updateCount'>增加</button>
    </template>
    复制代码
    

    在Vue2中

    data() {
      return {
        conunt: 0,
      };
    },
    methods: {
      updateCount() {
        this.conunt++;
      },
    },
    复制代码
    

    在Vue3中

     setup() {
        // ref用于定义一个响应式的数据,返回的是一个Ref对象,对象中有一个value属性
        //如果需要对数据进行操作,需要使用该Ref对象的value属性
        const count = ref(0);
        function updateCount() {
          count.value++;
        }
        return {
          count,
          updateCount,
        };
      },
    复制代码
    

    Vue2中我们通过this.$refs来获取dom节点,Vue3中我们通过ref来获取节点

    首先需要在标签上添加ref='xxx',然后再setup中定义一个初始值为nullref类型,名字要和标签的ref属性一致

    const xxx = ref(null)
    复制代码
    

    注意:一定要在setupreturn中返回,不然会报错。

    还是用一个例子来演示:让输入框自动获取焦点

    <template>
      <h2>App</h2>
      <input type="text">---
      <input type="text" ref="inputRef">
    </template>
    
    <script lang="ts">
    import { onMounted, ref } from 'vue'
    /* 
    ref获取元素: 利用ref函数获取组件中的标签元素
    功能需求: 让输入框自动获取焦点
    */
    export default {
      setup() {
        const inputRef = ref<HTMLElement|null>(null)
    
        onMounted(() => {
          inputRef.value && inputRef.value.focus()
        })
    
        return {
          inputRef
        }
      },
    }
    </script>
    复制代码
    

    reactive

    语法

    const proxy = reactive(obj)
    复制代码
    

    作用

    定义多个数据的响应式,接收一个普通对象然后返回该普通对象的响应式代理器对象(Proxy),响应式转换是“深层的”:会影响对象内部所有嵌套的属性,所有的数据都是响应式的。

    代码演示

    <template>
      <h3>姓名:{{user.name}}</h3>
      <h3>年龄:{{user.age}}</h3>
      <h3>wife:{{user.wife}}</h3>
      <button @click="updateUser">更新</button>
    </template>
    
     setup() {
        const user = reactive({
          name: 'EricLee',
          age: 18,
          wife: {
            name: 'xioaohong',
            age: 18,
            books: ['红宝书', '设计模式', '算法与数据结构'],
          },
        });
        const updateUser = () => {
          user.name = '小红';
          user.age += 2;
          user.wife.books[0] = '金瓶梅插画';
        };
        return {
          user,
          updateUser,
        };
      },
    复制代码
    

    computed函数:

    Vue2中的computed配置功能一致,返回的是一个ref类型的对象

    计算属性的函数中如果只传入一个回调函数 表示的是get操作

    import { computed } from 'vue';
    const user = reactive({
      firstName: '韩',
      lastName: '志伟',
    });
    const fullName1 = computed(() => {
      return user.firstName + user.lastName;
    });
    return {
      user,
      fullName1,
    };
    复制代码
    

    计算属性的函数中可以传入一个对象,可以包含setget函数,进行读取和修改的操作

    const fullName2 = computed({
      get() {
        return user.firstName + '_' + user.lastName;
      },
      set(val: string) {
        const names = val.split('_');
        user.firstName = names[0];
        user.lastName = names[1];
      },
    });
    return {
      user,
      fullName2,
    };
    复制代码
    

    watch函数:

    Vue2中的watch配置功能一致,

    • 参数1:要监听的数据
    • 参数2:回调函数
    • 参数3:配置

    作用

    监视指定的一个或多个响应式数据, 一旦数据变化, 就自动执行监视回调

    默认初始时不执行回调, 但可以通过配置immediatetrue, 来指定初始时立即执行第一次

    通过配置deeptrue, 来指定深度监视

    import { watch, ref } from 'vue';
    const user = reactive({
      firstName: 'Eric',
      lastName: 'Lee',
    });
    const fullName3 = ref('');
    watch(
      user,
      ({ firstName, lastName }) => {
        fullName3.value = firstName + '_' + lastName;
      },
      { immediate: true, deep: true }
    );
    return {
      user,
      fullName3,
    };
    复制代码
    

    watch监听多个数据,使用数组

    watch监听非响应式数据的时候需要使用回调函数的形式

    watch([()=>user.firstName,()=>user.lastName,fullName3],()=>{console.log('我执行了')})
    复制代码
    

    watchEffect函数:

    作用

    监视数据发生变化时执行回调,不用直接指定要监视的数据, 回调函数中使用的哪些响应式数据就监视哪些响应式数据,默认初始时就会执行第一次, 从而可以收集需要监视的数据。

    import { watchEffect, ref } from 'vue';
    const user = reactive({
      firstName: 'Eric',
      lastName: 'Lee',
    });
    const fullName4 = ref('');
    watchEffect(() => {
      fullName4.value = user.firstName + '_' + user.lastName;
    });
    return {
      user,
      fullName4,
    };
    watchEffect可以实现计算属性set方法
    watchEffect(() => {
        const names = fullName3.value.split('_');
        user.firstName = names[0];
        user.lastName = names[1];
    });
    复制代码
    

    生命周期对比:

    image.png

    注意:3.0中的生命周期钩子要比2.X中相同生命周期的钩子要快

    Composition API还新增了以下调试钩子函数:但是不怎么常用

    • onRenderTracked
    • onRenderTriggered

    代码演示

    setup() {
    
      onBeforeMount(() => {
        console.log('--onBeforeMount')
      })
    
      onMounted(() => {
        console.log('--onMounted')
      })
    
      onBeforeUpdate(() => {
        console.log('--onBeforeUpdate')
      })
    
      onUpdated(() => {
        console.log('--onUpdated')
      })
    
      onBeforeUnmount(() => {
        console.log('--onBeforeUnmount')
      })
    
      onUnmounted(() => {
        console.log('--onUnmounted')
      })
    }
    复制代码
    

    toRefs

    作用

    把一个响应式对象转换成普通对象,该普通对象的每个属性都是一个ref

    应用

    我们使用reactive创建的对象,如果想在模板中使用,就必须得使用xxx.xxx的形式,如果大量用到的话还是很麻烦的,但是使用es6解构以后,会失去响应式,那么toRefs的作用就体现在这,,利用toRefs可以将一个响应式 reactive对象的所有原始属性转换为响应式的ref属性。当然小伙伴们可以自行开发更多应用场景。

    代码演示

    <template>
      <div>
        name:{{name}}
      </div>
    </template>
    
    <script lang='ts'>
    import { defineComponent, reactive, toRefs } from 'vue';
    
    export default defineComponent({
      name: '',
      setup() {
        const state = reactive({
          name: 'eric',
        });
        const state2 = toRefs(state);
        setInterval(() => {
          state.name += '===';
        }, 1000);
        return {
          //通过toRefs返回的对象,解构出来的属性也是响应式的
          ...state2,
        };
      },
    });
    </script>
    复制代码
    

    provide 与 inject

    作用

    实现跨层级组件(祖孙)间通信

    代码演示

    父组件

    <template>
      <h1>父组件</h1>
      <p>当前颜色: {{color}}</p>
      <button @click="color='red'">红</button>
      <button @click="color='yellow'">黄</button>
      <button @click="color='blue'">蓝</button>
      <hr>
      <Son />
    </template>
    <script lang="ts">
    import { provide, ref } from 'vue'
    import Son from './Son.vue'
    export default {
      name: 'ProvideInject',
      components: {
        Son
      },
      setup() {
        const color = ref('red')
        provide('color', color)
        return {
          color
        }
      }
    }
    </script>
    复制代码
    

    子组件

    <template>
      <div>
        <h2>子组件</h2>
        <hr>
        <GrandSon />
      </div>
    </template>
    
    <script lang="ts">
    import GrandSon from './GrandSon.vue'
    export default {
      components: {
        GrandSon
      },
    }
    </script>
    复制代码
    

    孙子组件

    <template>
      <h3 :style="{color}">孙子组件: {{color}}</h3>
    </template>
    
    <script lang="ts">
    import { inject } from 'vue'
    export default {
      setup() {
        const color = inject('color')
        return {
          color
        }
      }
    }
    </script>
    复制代码
    

    其他特性

    Teleport(瞬移)

    作用

    Teleport 提供了一种干净的方法, 让组件的html在父组件界面外的特定标签(很可能是body)下插入显示 换句话说就是可以把 子组件 或者 dom节点 插入到任何你想插入到的地方去。

    语法

    使用to属性 引号内使用选择器

      <teleport to="body">
      </teleport>
    复制代码
    

    代码演示

    //父组件
    
    <template>
      <div class="father">
        <h2>App</h2>
        <modal-button></modal-button>
      </div>
    </template>
    
    <script lang="ts">
    import ModalButton from './components/ModalButton.vue'
    export default {
      setup() {
        return {}
      },
      components: {
        ModalButton,
      },
    }
    </script>
    
    //子组件
    <template>
      <div class="son">
        <button @click="modalOpen = true">
          点我打开对话框
        </button>
    
        <teleport to="body">
          <div v-if="modalOpen"
               class="looklook">
            看看我出现在了哪里
            <button @click="modalOpen = false">
              Close
            </button>
          </div>
        </teleport>
      </div>
    </template>
    
    <script>
    import { ref } from 'vue'
    export default {
      name: 'modal-button',
      setup() {
        const modalOpen = ref(false)
        return {
          modalOpen,
        }
      },
    }
    </script>
    复制代码
    

    可以看到在子组件中的looklook元素跑到了body下面,而之前的位置默认出现了两行注释

    image.png

    Suspense(不确定的)

    作用

    它们允许我们的应用程序在等待异步组件时渲染一些后备内容,可以让我们创建一个平滑的用户体验

    语法

     <Suspense>
        <template v-slot:default>
          <!-- 异步组件 -->
          <AsyncComp />
        </template>
    
        <template v-slot:fallback>
          <!-- 后备内容 -->
          <h1>LOADING...</h1>
        </template>
      </Suspense>
    复制代码
    

    vue3中引入异步组件的方式

    const AsyncComp = defineAsyncComponent(() => import('./AsyncComp.vue'))
    复制代码
    

    代码演示

    父组件

    <template>
      <Suspense>
         <!-- v-slot:defaul可以简写成#defaul -->
        <template v-slot:default>
          <AsyncComp/>
        </template>
    
        <template v-slot:fallback>
          <h1>LOADING...</h1>
        </template>
      </Suspense>
    </template>
    
    <script lang="ts">
    import { defineAsyncComponent } from 'vue'
    const AsyncComp = defineAsyncComponent(() => import('./AsyncComp.vue'))
    export default {
      setup() {
        return {
        }
      },
    
      components: {
        AsyncComp,
      }
    }
    </script>
    复制代码
    

    子组件

    <template>
      <h2>AsyncComp22</h2>
      <p>{{msg}}</p>
    </template>
    
    <script lang="ts">
    export default {
      name: 'AsyncComp',
      setup () {
         return new Promise((resolve, reject) => {
           setTimeout(() => {
             resolve({
               msg: 'abc'
             })
           }, 2000)
         })
      }
    }
    </script>
    
    复制代码
    

    通过下图可以看到在异步组件加载出来之前,显示的是fallback中的内容

    fallback.gif

    响应式数据的判断

    作用

    • isRef: 检查一个值是否为一个 ref 对象
    • isReactive: 检查一个对象是否是由 reactive 创建的响应式代理
    • isReadonly: 检查一个对象是否是由 readonly 创建的只读代理
    • isProxy: 检查一个对象是否是由 reactive 或者 readonly 方法创建的代理

    代码演示

    setup() {
        const state1 = ref(1);
        console.log('isref:', isRef(state1));//isref: true
        const state2 = reactive({});
        console.log('isReactive:', isReactive(state2));//isReactive: true
        const state3 = readonly({});
        console.log('isReadonly:', isReadonly(state3));//isReadonly: true
        const state4 = reactive({});
        console.log('isProxy:', isProxy(state2));//isProxy: true
        console.log('isProxy:', isProxy(state4));//isProxy: true
        return {};
      },
    复制代码
    

    其他不常用特性

    还有很多很多不常用的新特性,我在日常开发中是没有用到的,很多都是用来做优化的,感兴趣的小伙伴们自行去官网查看,或者大佬们可以介绍一下应用场景。

    • shallowReactive
    • shallowRef
    • readonly
    • shallowReadonly
    • markRaw
    • customRef
    • ...

    语法糖

    虽然Composition API用起来已经非常方便了,但是我们还是有很烦的地方,比如

    • 组件引入了还要注册
    • 属性和方法都要在setup函数中返回,有的时候仅一个return就十几行甚至几十行
    • ...
    • 不想写啊怎么办

    好办,Vue3官方提供了script setup语法糖

    只需要在script标签中添加setup,组件只需引入不用注册,属性和方法也不用返回,setup函数也不需要,甚至export default都不用写了,不仅是数据,计算属性和方法,甚至是自定义指令也可以在我们的template中自动获得。

    但是这么过瘾的语法糖,还是稍微添加了一点点心智负担,因为没有了setup函数,那么propsemitattrs怎么获取呢,就要介绍一下新的语法了。

    setup script语法糖提供了三个新的API来供我们使用:definePropsdefineEmituseContext

    • defineProps 用来接收父组件传来的值props
    • defineEmit 用来声明触发的事件表。
    • useContext 用来获取组件上下文context

    代码演示

    父组件

    <template>
      <div>
        <h2>我是父组件!</h2>
        <Child msg="hello"
               @child-click="handleClick" />
      </div>
    </template>
    
    <script setup>
    import Child from './components/Child.vue'
    
    const handleClick = (ctx) => {
      console.log(ctx)
    }
    </script>
    复制代码
    

    子组件

    <template>
      <span @click="sonClick">msg: {{ props.msg }}</span>
    </template>
    
    <script setup>
    import { useContext, defineProps, defineEmit } from 'vue'
    
    const emit = defineEmit(['child-click'])
    const ctx = useContext()
    const props = defineProps({
      msg: String,
    })
    
    const sonClick = () => {
      emit('child-click', ctx)
    }
    </script>
    
    复制代码
    

    我们点击一下子组件

    image.png

    可以看到context被打印了出来,其中的attrsemitslotsexpose属性和方法依然可以使用。 props也可以输出在页面上,事件也成功派发。

    其他知识点

    接下来介绍一下我使用Vue3过程中遇到的问题或者小技巧,不全面,想起什么就写什么吧

    script setup语法糖请注意

    如果在父组件中通过ref='xxx'的方法来获取子组件实例,子组件使用了script setup语法糖,那么子组件的数据需要用expose的方式导出,否则会因为获取不到数据而报错。

    代码演示

    父组件

    <template>
      <div>
        <h2>我是父组件!</h2>
        <Child ref='son' />
      </div>
    </template>
    
    <script setup>
    import Child from './components/Child.vue'
    import { ref } from 'vue'
    const son = ref(null)
    console.log('🚀🚀~ son:', son)
    </script>
    复制代码
    

    子组件先不使用语法糖

    <template>
      <div>
        我是子组件{{msg}}
      </div>
    </template>
    
    <script >
    import { ref } from 'vue'
    
    export default {
      setup() {
        const msg = ref('hello')
        return {
          msg,
        }
      },
    }
    复制代码
    

    可以看到是可以获取到我们在子组件中定义的msg属性的

    image.png

    现在把子组件换成script setup语法糖再来试一试

    <template>
      <div>
        我是子组件{{msg}}
      </div>
    </template>
    
    <script setup>
    import { ref } from 'vue'
    const msg = ref('hello')
    </script>
    
    复制代码
    

    可以看到现在是获取不到子组件定义的msg属性的

    png.png

    我们可以看看尤大大怎么说

    image.png

    补充:setup语法糖定义的组件默认情况下是不对外开发内部调用的,它需要用expose()函数来定义哪些数据允许对外开放,具体内容参考

    Emit派发事件可以对参数进行验证

    父组件

    <template>
      <div>
        <h2>我是父组件!</h2>
        <Child @sonClick='sonClick' />
      </div>
    </template>
    
    <script setup>
    import Child from './components/Child.vue'
    import { ref } from 'vue'
    const sonClick = (value) => {
      console.log(value)
    }
    </script>
    复制代码
    

    子组件

    
    <template>
      <div>
        我是子组件{{ msg }}
      </div>
      <button @click="handleClick(1)">我是按钮1</button>
      <button @click="handleClick(2)">我是按钮2</button>
    </template>
    
    <script>
    import { ref } from 'vue'
    export default {
      name: '',
      emits: {
        sonClick: (value) => {
          if (value === 1) {
            return true
          } else {
            return false
          }
        },
      },
      setup(props, { emit }) {
        const msg = ref('hello')
        const handleClick = (value) => {
          emit('sonClick', value)
        }
        return {
          msg,
          handleClick,
        }
      },
    }
    </script>
    复制代码
    

    我们分别点一下按钮1和按钮2,可以看到当我们点了按钮2的时候,控制台会发出警告,但是程序会继续执行,还没想到什么适合的应用场景,但是要知道这个知识点,小伙伴们可以在这搞事情。

    image.png

    跨组件通讯mitt.js

    Vue2中怎么实现跨组件通讯呢,很多人第一想法就是event bus。但是Vue3移除了$on,$once,$off导致不能使用这个方法。但是Vue官方给大家推荐了mitt.js,它的原理就是event bus

    代码演示

    先安装

    npm i mitt -s
    复制代码
    

    然后封装一个hook

    //mitt.js
    import mitt from 'mitt'
    const emitter = mitt();
    
    export default emitter;
    复制代码
    

    父组件

    <template>
      <div>
        <h2>我是父组件!</h2>
        <Child1 />
        <Child2 />
    
      </div>
    </template>
    
    <script setup>
    import Child1 from './components/Child1.vue'
    import Child2 from './components/Child2.vue'
    </script>
    复制代码
    

    子组件1

    <template>
      <div>
        我是子组件1
        <h1>{{msg}}</h1>
      </div>
    </template>
    
    <script>
    import { ref, onUnmounted } from 'vue'
    import emitter from '../mitt'
    export default {
      name: '',
    
      setup() {
        //初始化
        const msg = ref('hello')
        const changeMsg = () => {
          msg.value = 'world'
        }
        // 监听事件,更新数据
        emitter.on('change-msg', changeMsg)
        // 显式卸载
        onUnmounted(() => {
          emitter.off('change-msg', changeMsg)
        })
        return {
          msg,
          changeMsg,
        }
      },
    }
    </script>
    复制代码
    

    子组件2

    <template>
      <div>
        我是子组件2
      </div>
      <button @click='changeMsg'>点击修改msg</button>
    </template>
    
    <script>
    import { ref } from 'vue'
    import emitter from '../mitt'
    
    export default {
      name: '',
    
      setup() {
        const changeMsg = () => {
          emitter.emit('change-msg')
        }
        return {
          changeMsg,
        }
      },
    }
    </script>
    复制代码
    

    演示

    演示.gif

    自定义指令

    先看看Vue2自定义指令的钩子

    • bind:当指令绑定在对应元素时触发。只会触发一次。
    • inserted:当对应元素被插入到 DOM 的父元素时触发。
    • update:当元素更新时,这个钩子会被触发(此时元素的后代元素还没有触发更新)。
    • componentUpdated:当整个组件(包括子组件)完成更新后,这个钩子触发。
    • unbind:当指令被从元素上移除时,这个钩子会被触发。也只触发一次。

    Vue3 中,官方为了更有助于代码的可读性和风格统一,把自定义指令的钩子名称改的更像是组件生命周期,尽管他们是两回事

    • bind => beforeMount
    • inserted => mounted
    • beforeUpdate: 新的钩子,会在元素自身更新前触发
    • update => 移除
    • componentUpdated => updated
    • beforeUnmount: 新的钩子,当元素自身被卸载前触发
    • unbind => unmounted

    过渡动画

    这个没有什么大的改动,只是修改了两个class名字,正是因为没有什么大的改动,导致我曾经在这里栽了大跟头,写完了怎么都不对,后来查官网才知道。

    以下是直接引用 官网的原文

    • v-enter-from:定义进入过渡的开始状态。在元素被插入之前生效,在元素被插入之后的下一帧移除。
    • v-enter-active:定义进入过渡生效时的状态。在整个进入过渡的阶段中应用,在元素被插入之前生效,在过渡/动画完成之后移除。这个类可以被用来定义进入过渡的过程时间,延迟和曲线函数。
    • v-enter-to:定义进入过渡的结束状态。在元素被插入之后下一帧生效 (与此同时 v-enter-from 被移除),在过渡/动画完成之后移除。
    • v-leave-from:定义离开过渡的开始状态。在离开过渡被触发时立刻生效,下一帧被移除。
    • v-leave-active:定义离开过渡生效时的状态。在整个离开过渡的阶段中应用,在离开过渡被触发时立刻生效,在过渡/动画完成之后移除。这个类可以被用来定义离开过渡的过程时间,延迟和曲线函数。
    • v-leave-to:离开过渡的结束状态。在离开过渡被触发之后下一帧生效 (与此同时 v-leave-from 被删除),在过渡/动画完成之后移除。

    特别注意的是v-enter改成了v-enter-formv-leave改成了v-leave-from

    其他小知识

    1、filter

    Vue3移除了filter

    2、this不能在setup使用的问题

    获取组件实例方法getCurrentInstance()
    这个方法可以获取到当前组件的实例,相当于Vue2中的this

    3、$el

    Vue2使用$el --> this.$el.getBoundingClientRect()
    Vue3使用$el --> const { ctx } = getCurrentInstance()
    ctx.$el.getBoundingClientRect()

    4、v-model

    • 非兼容:用于自定义组件时,v-model prop 和事件默认名称已更改:
    • prop:value -> modelValue
    • event:input -> update:modelValue
    • 非兼容:v-bind.sync 修饰符和组件的 model 选项已移除,可用 v-model 作为代替;
    • 新增:现在可以在同一个组件上使用多个 v-model 进行双向绑定;
    • 新增:现在可以自定义 v-model 修饰符。

    相关文章

      网友评论

        本文标题:3、最全的Vue3知识点,随时更新

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