关于前端:Vue创建组件的方式你知道几种

39次阅读

共计 6693 个字符,预计需要花费 17 分钟才能阅读完成。

前言

当初前端开发中应用 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.js
import 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…

正文完
 0