前言

当初前端开发中应用Vue的预计比拟多,组件也是Vue比拟外围的内容,也是咱们平时开发中接触最多的货色了,那么什么是组件呢?组件能够是小到一个按钮,一个图标,也能够大到一个页面,甚至是一个零碎。

最近接手了一个Vue的我的项目,能够说是学到了不少货色,当然也包含本篇说的“组件”,所以想着写下来分享一下。这篇文章不讲组件的根底,只讲创立组件(注册)的几种形式。

全局注册

Vue.component('my-component-name', {  // ... 选项 ...})

咱们能够用这种形式来创立全局组件,在实例化Vue之前用Vue.component来创立组件,这样咱们能够在任何实例化Vue的组件(new Vue)中应用。

具体形式

1、注册组件

Vue.component('component-a', { /* ... */ })Vue.component('component-b', { /* ... */ })Vue.component('component-c', { /* ... */ })new Vue({ el: '#app' })

2、应用组件

<div id="app">  <component-a></component-a>  <component-b></component-b>  <component-c></component-c></div>

这样咱们就胜利创立注册并应用了组件,并且咱们还能够在所创立的组件外部互相应用。

部分注册

咱们能够以一般对象的形式创立组件,对象中能够蕴含咱们罕用的datamethods等属性和办法,比方上面这样

var ComponentA = { /* ... */ }var ComponentB = { /* ... */ }var ComponentC = { /* ... */ }

注册部分组件的话。须要咱们在要应用组件的中央(大多数状况下也是组件)应用components来注册,具体代码如下

new Vue({  el: '#app',  components: {    'component-a': ComponentA,    'component-b': ComponentB  }})

单文件组件

可能更多时候咱们是应用webpack来构建咱们的我的项目(比方:vue-cli),写的更多的是单文件组件,须要在组件中应用另一个组件,上面我就写一下

咱们就在vue-cli3的根底上写,首先咱们像下面那种形式一样创立一个组件

// /components/Dialog/Dialog.vue<template>  <transition name="fade" v-if="isShow">    <div class="dialog-page">      <div class="dialog-box">        <h3 class="title">{{ title }}</h3>        <span class="close">X</span>        <el-button type="primary" class="confirm-btn">确定</el-button>      </div>    </div>  </transition></template><script>export default {  name: "Dialog",  props: {    title: {      type: String    }  },  data() {    return {      isShow: false    }  },  mounted() {    this.isShow = true;  }}</script><style lang="scss" scoped>.dialog-page {  position: fixed;  top: 0;  left: 0;  width: 100vw;  height: 100vh;  background-color: rgba(0, 0, 0, 0.6);  z-index: 99999;  .dialog-box {    position: absolute;    top: 20%;    left: 50%;    transform: translateX(-50%);    margin: auto;    width: 700px;    height: 400px;    padding-top: 20px;    background-color: #fff;    border-radius: 10px;    box-shadow: 0 0 6px 0 #fff;    .title {      text-align: center;      font-size: 22px;      color: #333;    }    .close {      position: absolute;      right: 20px;      top: 20px;      cursor: pointer;    }    .confirm-btn {      width: 80%;      position: absolute;      left: 10%;      bottom: 50px;    }  }}.fade-enter-active, .fade-leave-active {  transition: opacity .2s;}.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {  opacity: 0;}</style>

而后在App.vue中应用

// App.vue<template>  <div id="app">    <Dialog title="自定义弹窗组件" />  </div></template><script>import Dialog from "components/Dialog/Dialog";export default {  name: 'app',  components: {    Dialog,  }}</script>

这个时候如果运行我的项目,页面是这个样子(确认按钮我应用了elementUI哦)

首先导入组件,而后应用components注册组件,最初在template中应用组件,这样咱们就胜利实现了一个组件的创立和应用。这种形式是我迄今为止应用最多的形式,当然也可能满足大部分的需要了。

然而有的状况下这种事不够用的,比方一些用于告诉的弹框或者交互的组件,这种组件平时是不显示的,只有触发了某些动作的时候才会显示,如果咱们还应用这种形式的话,就要先引入组件,而后再注册,最初在页面中应用,并且咱们还要管制组件的显示与否,是不是减少很多无用的变量和代码,那么咱们能不能用更简略不便的形式来创立组件呢?答案必定是有的,往下看。

办法调用式创立

应用过像elementUIiview之类的第三方组件库的同学可能都晓得,他们提供了很多全局的API能够很不便的创立组件,比方弹出框等,他们是怎么做的呢,咱们上面就来看看。还是接着下面的弹框组件写。

首先略微改变一下方才写的组件,加一些事件(敞开和确认)进去

// /components/Dialog/Dialog.vue<template>  <transition name="fade" v-if="isShow">    <div class="dialog-page">      <div class="dialog-box">        <h3 class="title">{{ title }}</h3>        <span class="close" @click="close">X</span>        <el-button type="primary" class="confirm-btn" @click="confirm">确定</el-button>      </div>    </div>  </transition></template><script>export default {  name: "Dialog",  props: {    title: {      type: String    }  },  data() {    return {      isShow: false    }  },  mounted() {    window.addEventListener('keyup', this.close)    this.isShow = true;  },  methods: {    close() {      this.isShow = false;      // 这里应用$nextTick是因为咱们应用了过渡成果,敞开的时候先过渡,再销毁      this.$nextTick(() => {        this.$emit('close')      })    },    confirm() {      this.$emit('confirm')    }  }}</script><style lang="scss" scoped>/* 和下面一样,太长不写了 */...</style>

新建文件

// /components/Dialog/index.jsimport Vue from 'vue';import Dialog from "@/components/Dialog/Dialog";let vm;const noop = () => {};function createVNode(resolve = noop, reject = noop, props) {    return new Vue({        mixins: [{            mounted() {                document.body.appendChild(this.$el);            },            beforeDestroy() {                document.body.removeChild(this.$el);            },        }],        beforeDestroy() {            vm = null;        },        methods: {            confirm() {                resolve('点击确认');                this.$destroy();            },            close() {                reject('点击敞开');                this.$destroy();            }        },        render(createElement) {            return createElement(Dialog, {                on: {                    confirm: this.confirm,                    close: this.close                },                props,                style: {                    color: 'red'                },            });        }    })}function show(props) {    return new Promise((resolve, reject) => {        // 如果有vm这个实例,先销毁        if (vm) {            vm.$destroy();        }        vm = createVNode(resolve, reject, props);        vm.$mount();    })}export default show;

倒着看,咱们导出了一个show办法,它返回的是一个Promise,这就是说咱们在调用这个办法的时候能够用.then的语法在将来某一个时刻(敞开弹框,或者点击确认)执行一些操作。

接着咱们创立了一个vm实例,调用了$mount,是不是看着比拟相熟,没错,在main.js文件中

// main.js...new Vue({    ...    render: h => h(App),}).$mount('#app');

它把new vue的生成的虚构DOM转换成实在DOM挂载到了#app下面,那下面咱们的$mount没有传参数,会挂载到哪里呢?其实是须要咱们手动挂载的,这个就给个传送门吧。

接着说咱们的组件,这里咱们又看到一个createVNode办法,不用说,这个就是用来创立组件的办法了,能够看到它返回了一个Vue的实例化对象(虚构DOM),new Vue的参数混入了一些生命周期函数,和两个methods办法,其中在组件渲染实现后手动把以后组建的DOM插入到body中,在组件销毁前移除掉。

最重要的还是上面的render函数,这个内容很多,就不开展说了(我本人也是只知其一;不知其二,说不好????),想深刻理解能够单击传送门,能够简略了解render能够生成VNode,就是虚构节点。它的第一个参数是方才咱们编写的组件,第二个参数是一个对象,能够定义一些参数,比方要传给组件的参数、款式还有监听的事件等。

这样咱们new Vue的时候,没有用template的形式,而是用了render函数来生成咱们要的组件,手动挂载到DOM中去(不在#app外面,是并列的关系,都在body上面)。

接下来咱们就能够在须要的中央调用了,比方App.vue

// App.vue<template>  <div id="app">      <button @click="handleClick">Dialog</button>  </div></template><script>import Dialog from '@/components/Dialog/index'export default {  name: 'app',  methods: {    handleClick() {      Dialog({        title: '测试弹框'      }).then(res => {        console.log(res)      }).catch(err => {        console.log(err)      })    }  }}</script>

点击按钮的时候,咱们调用导入的Dialog办法,传了title参数,当初咱们能够看看成果

到当初咱们曾经实现了组件的创立和调用,而且咱们点击敞开确认的时候能够看看控制台,是不是别离打印了

这就是咱们在App.vue外面调用Dialog办法的时候别离在thencatch外面打印的,回到下面/components/Dialog/index.js文件中的show办法,咱们返回的是promise,而且咱们把resolvereject两个参数传入了createVNode办法中,别离在methods中的两个对应确认和敞开的办法中别调用,那么confirmclose是怎么被触发的呢

能够看/components/Dialog/Dialog.vue文件中,咱们在点击的时候,应用了$emit来触发对应的事件,那么事件是在哪里被接管的呢?回到/components/Dialog/index.js文件中的render函数,咱们在createElement办法的第二个参数中有一个on对象,是不是感觉很相熟,没错,就是用来监听事件的,咱们下面用$emit触发的事件也是在这里监听的。这样咱们就在点击确认或者敞开后,执行resolve或者reject办法,而后就能够在thencatch中执行相应的操作了。

注册全局插件

到下面为止,咱们曾经能够很不便的以API的模式来创立组件,然而可能有人会感觉还不够不便,每次还要先导入,再调用,有没有更不便的形式呢?必定是有的,当初咱们就来试试。

其实也很简略,只须要把方才的代码略微改一下就能够了

// /component/Dialog/index.js...- export default show;+ export default {+     install(vue) {+        vue.prototype.$Dialog = show;+     }+ }
// main.js+ import Dialog from '@/components/Dialog/index'+ Vue.use(Dialog);

看到Vue.use又有了相熟的感觉吧,咱们平时应用插件不就是这么用的嘛!没错,第三方的插件可能以Vue.use的形式注册,是因为他们都在插件外面导出了一个install办法,不分明的看这里哦 Vue.use。

这样咱们就能够在须要的中央应用this.$Dialog调用了,也不须要先导入了,因为办法曾经被挂到了Vue的原型上。

当初就来试一下

// App.vue<template>  <div id="app">      <button @click="handleClick">Dialog</button>  </div></template><script>export default {  name: 'app',  methods: {    handleClick() {      this.$Dialog({        title: '测试弹框'      }).then(res => {        console.log(res)      }).catch(err => {        console.log(err)      })    }  }}</script>

成果和方才是一样的,就不贴图了。ok,到这里是不是感觉和ElementUI提供的一些办法有点类似呢,没错,其实他们也是这么做的。

结语

本文次要介绍了几种创立组件的形式,具体要用哪一种还看依据具体的业务场景来抉择,正所谓没有最好的技术计划,只有最合适的。然而说归说,多把握一点总是好的,前面的内容就波及到Vue中比拟难把握的内容,比方rendercreateElement$mount等。其实这些货色更靠近底层,通过学习这些咱们能够更粗浅的了解Vue,而不只是停留在“会用”的阶段。

好了,就先说到这里,喜爱或者感觉有用能够点赞 + 珍藏哦,有谬误之处也欢送指出!!!

刚封装了一个Vue图片预览的插件(vue-preview-imgs)也是我封装的第一个插件,还有很多有余,欢送一起交换,喜爱给个star哦!地址:https://github.com/hzpeng57/v...