浅析Vue3相关基础知识:Vue3应用配置、重写的v-model、emits 选项、getCurrentInstance()获取实例、采用mitt实现全局通讯、vue-router的新特性

一、Vue3.x 应用配置

  config:包含Vue应用程序全局配置的对象,在挂载应用之前配置相应的属性。

const app = Vue.createApp({});
app.config = {...};

1、devtools(类型:Boolean,默认:true)

  概述:配置是否允许开启vue-devtools检查,一般在开发环境中是true,生产环境中为false。

  用法:app.config.devtools = true;

2、errorHandler(类型:Function,参数err:错误内容,vm:对应的实例,info:Vue特定的错误信息,如某个生命周期中出现的错误)

  概述:为组件在实例化或渲染过程中捕获到的错误进行处理的方法。

  用法:app.config.errorHandler = (err, vm, info) => {};

3、globalProperties(类型:any)

  概述:用于添加到应用程序中任何组件都能使用的全局属性,当与组件内部的属性冲突时,将优先使用组件内部的属性值。较Vue2.x:可代替Vue2中的Vue.prototype。

  用法:

// Vue 2.x
Vue.prototype.name = '***';
// Vue 3.x
app.config.globalProperties.name = '***';

4、isCustomElement(类型:(tag: string) => boolean)

  概述:用于来识别Vue之外的自定义元素(如,三方web组件api),如果组件或元素符合这个条件,则组件不会被实例化,Vue也不会对组件或元素发出警告信息。

  用法:app.config.isCustomElement = tag => tag.startsWith('ion');

二、重写的 v-model

  在Vue2.x中,我们使用的v-model只能双向绑定一个值,在某些需求面前显的力不从心。但是在Vue3.x中已经可以实现啦!

1、在Vue2.x中,v-model进行数据双向绑定(语法糖)的原理,且不能绑定多个值

<my-components v-model="msg"></my-components>
// 等价于
<my-components :value="msg" @input="value=$event"></my-components>

// myComponents组件中接收绑定数据和触发数据改变
props: { msg: String }; // 获取数据
this.$emit("input", 'newVal'); // 触发事件并传值

2、Vue3.x重写了v-model的实现方式,以适用用绑定多个 v-model —— 单个 / 多个 数据实现数据双向绑定

<my-components v-model:msg="msg" v-model:name="name"></my-components>
// 等价于
<my-components :msg="msg" @update:msg="value=$event" :name="name" @update:name="name=$event"></my-components>

// myComponents组件中接收绑定数据和触发数据改变
props: { msg: String, name: String }; // 获取数据
setup(props, { emit }) {
    emit('update:msg', 'newValue'); // 触发事件并传值
    emit('update:name', 'newValue'); // 触发事件并传值
};

三、emits 选项

1、记录当前组件的通过emit的事件列表,类型:Array|Object,其作用:Vue3.0中使用emit发起事件时会要求当前组件记录emit事件(没有则控制台会抛出警告)。

2、用途:用于记录当前组件emit的事件,当为对象时,则可以验证传入的值是否有效。

3、如何使用

setup(prop, { emit }) {
    const changeOne = val => {
        emit('on-changeOne', val);
    };
    const changeTwo  = val => {
        emit('on-changeTwo', val);
    };
}
// 用法一:数组用法
export default {
    emits:['on-changeOne', 'on-changeTwo'],
    setup() {...}
}
// 用法二:对象用法,当emits为对象时,可以验证事件中的参数是否有效
export default {
    emits:{
        click: null,
        'on-changeOne': payload => {
            if(...) {
                return true; // 验证通过
            }
            console.warn('验证失败!')
            return false; // 验证失败,控制台打印vue警告及“验证失败!”警告
        },
        'on-changeTwo': payload => {...}
    },
    setup() {...}
}
// 当验证函数中没有返回值return时,默认返回true

4、emits无论是数组或者对象用法最终都会将事件给传递出去,数组或对象的使用只是为了记录实例中的emit事件,或者是验证事件中的参数。

四、getCurrentInstance()获取实例

1、一个很重要的方法,获取当前组件的实例、上下文来操作router和vuex等。由vue提供,按需引入:import { getCurrentInstance} from 'vue';

import { getCurrentInstance } from 'vue';
// 获取当前组件实例
const instance = getCurrentInstance();

// 获取当前组件的上下文,下面两种方式都能获取到组件的上下文。
const { ctx }  = getCurrentInstance();  //  方式一,这种方式只能在开发环境下使用,生产环境下的ctx将访问不到
const { proxy }  = getCurrentInstance();  //  方式二,此方法在开发环境以及生产环境下都能放到组件上下文对象(推荐)
// ctx 中包含了组件中由ref和reactive创建的响应式数据对象,以及以下对象及方法;
proxy.$attrs
proxy.$data
proxy.$el
proxy.$emit
proxy.$forceUpdate
proxy.$nextTick
proxy.$options
proxy.$parent
proxy.$props
proxy.$refs
proxy.$root
proxy.$slots
proxy.$watch

五、mitt 实现全局通讯

1、由于Vue3.x中删除了 on 和 off ,因此不能借助于一个单独的Vue实例来实现全局事件的发布和订阅与取消订阅(也就是跨组件通讯)。

2、mitt 是一个三方库,npm安装:npm install -D mitt,我们以同样使用插件的方式将mitt集成到Vue中。

3、mitt 对象:
  • all(Map对象):包含了所有订阅的事件名称,及对应的处理方法数组。
  • emit(方法):触发事件,参数为(事件名(方法名),携带的参数),当- 前携带的参数只能为一个,不能为多个。
  • on(方法):创建事件订阅,参数为(事件名,处理方法)。
  • off(方法):取消事件订阅,参数为(事件名,处理方法)。
4、全局事件总成搭建:
import _ from 'lodash';
import mitt from 'mitt';

export default {
  install(Vue, options) {
    const _emitter = mitt();

    // 全局发布(在Vue全局方法中自定义$pub发布方法)
    // 这里做了$pub方法能够携带多个参数的处理,方便我们再业务中触发事件时带多个参数
    Vue.config.globalProperties.$pub = (...args) => {
        _emitter.emit(_.head(args), args.slice(1));
    };

    // 全局订阅(在Vue全局方法中自定义$sub订阅方法)
    Vue.config.globalProperties.$sub = function (event, callback) {
      Reflect.apply(_emitter.on, _emitter, _.toArray(arguments));
    };

    // 取消订阅
    Vue.config.globalProperties.$unsub = function (event, callback) {
      Reflect.apply(_emitter.off, _emitter, _.toArray(arguments));
    };
  }
};

5、组件实例中使用:

// 全局事件发布
<template>
  <div class="child">
    <button @click="pubHandler">发起事件</button>
  </div>
</template>
<script>
import { onMounted, getCurrentInstance } from 'vue';
export default {
  setup(props, context) {
    const { proxy } = getCurrentInstance();
    const pubHandler = () => {
      proxy.$pub('foo', 1, 2, 3); 
    };
    return {
        pubHandler
    };
  }
};
</script>

// 全局事件订阅/取消订阅
<template>
  <div class="child">
    <button @click="unsubHandler">注销事件</button>
  </div>
</template>
<script>
import { getCurrentInstance, onMounted } from 'vue';
export default {
  setup(props, context) {
    const { proxy } = getCurrentInstance();
    const watchHandler = ([a, b, c] = args) => {
      console.log('组件件监听触发!');
    };
    onMounted(() => {
      proxy.$sub('foo', watchHandler);
    });
    const unsubHandler = () => {
      proxy.$unsub('foo', watchHandler);
    };
    return {
      unsubHandler
    };
  }
};
</script>

六、vue-router 的新特性

1、在vue3中,vue-router将使用新的方法来创建路由,其中重要的是:createRouter, createWebHashHistory, createWebHistory这三个方法。

2、创建路由

// router.js
import { createRouter, createWebHashHistory, createWebHistory } from "vue-router";
const routes = [...];
const router = createRouter({
  // 区别于vue2的mode,vue3中将使用history属性来决定采用哪种路由模式
  history: createWebHashHistory(), // 默认为Hash模式,可设置createWebHistory模式
  // 区别于vue2的base,vue3中的基础路由路径将作为createWebHashHistory或者createWebHistory的唯一参数配置到路由中。
  routes
});
export default router;
// main.js
import router from './routes';
const app = createApp(App);
// 注册路由
app.use(router);

3、在组件中使用路由,查看路由。

// vue-router库当中暴露了 useRouter 和 useRoute 两个方法供组件使用,还暴露有其他方法。
import { useRouter, useRoute } from 'vue-router';
import { onMounted, getCurrentInstance } from 'vue';
export default {
  setup() {
    const { proxy } = getCurrentInstance();
    const router = useRouter();
    const route = useRoute();
    onMounted(() => {
      console.log(proxy.$router === router) // true
      console.log(route) // {path, params, query...}
    });
  }
};

 

posted @ 2021-10-15 15:32  古兰精  阅读(1711)  评论(0编辑  收藏  举报