共计 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>
这样咱们就胜利创立注册并应用了组件,并且咱们还能够在所创立的组件外部互相应用。
部分注册
咱们能够以一般对象的形式创立组件,对象中能够蕴含咱们罕用的 data
、methods
等属性和办法,比方上面这样
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
中应用组件,这样咱们就胜利实现了一个组件的创立和应用。这种形式是我迄今为止应用最多的形式,当然也可能满足大部分的需要了。
然而有的状况下这种事不够用的,比方一些用于告诉的弹框或者交互的组件,这种组件平时是不显示的,只有触发了某些动作的时候才会显示,如果咱们还应用这种形式的话,就要先引入组件,而后再注册,最初在页面中应用,并且 咱们还要管制组件的显示与否,是不是减少很多无用的变量和代码,那么咱们能不能用更简略不便的形式来创立组件呢?答案必定是有的,往下看。
办法调用式创立
应用过像 elementUI
和iview
之类的第三方组件库的同学可能都晓得,他们提供了很多全局的 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
办法的时候别离在 then
和catch
外面打印的,回到下面 /components/Dialog/index.js
文件中的 show
办法,咱们返回的是 promise
,而且咱们把resolve
和reject
两个参数传入了 createVNode
办法中,别离在 methods
中的两个对应确认和敞开的办法中别调用,那么 confirm
和close
是怎么被触发的呢
能够看 /components/Dialog/Dialog.vue
文件中,咱们在点击的时候,应用了 $emit
来触发对应的事件,那么事件是在哪里被接管的呢?回到 /components/Dialog/index.js
文件中的 render
函数,咱们在 createElement
办法的第二个参数中有一个 on
对象,是不是感觉很相熟,没错,就是用来监听事件的,咱们下面用 $emit
触发的事件也是在这里监听的。这样咱们就在点击 确认
或者 敞开
后,执行 resolve
或者 reject
办法,而后就能够在 then
和catch
中执行相应的操作了。
注册全局插件
到下面为止,咱们曾经能够很不便的以 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
中比拟难把握的内容,比方 render
、createElement
、$mount
等。其实这些货色更靠近底层,通过学习这些咱们能够更粗浅的了解Vue
, 而不只是停留在“会用”的阶段。
好了,就先说到这里,喜爱或者感觉有用能够点赞 + 珍藏哦,有谬误之处也欢送指出!!!
刚封装了一个 Vue 图片预览的插件(vue-preview-imgs
)也是我封装的第一个插件,还有很多有余,欢送一起交换,喜爱给个 star 哦!地址:https://github.com/hzpeng57/v…