开发VUE插件详细步骤
一.前置知识
1.混入
见官方文档 https://cn.vuejs.org/v2/guide/mixins.html。
- 基本示例:
示例文件结构:
新建一个.js文件,里面定义属性,这里面的属性就是vue的属性,在vue文件中引入这个文件中的属性时也会混合到vue的属性中
export default {
data: function() {
return {
tag: '.js',
age: 11
}
},
mounted: function() {
console.log('mixin')
}
}
在vue文件中引入:
<template>
<div id="demo-minxin">
{{tag}} - > {{name}} --> {{age}}
</div>
</template>
<script>
import DemoMixinData from './DemoMixinData'
export default {
data () {
return {
tag: '.vue',
name: 'demo'
}
},
mounted () {
console.log('vue')
},
mixins: [DemoMixinData] // 这在个地方引入 mixin ,
}
</script>
<style>
</style>
通过mixins:
属性引入,生命周期方法将会先执行混入的,后执行vue文件本身的。其他属性不同的添加,相同的已.vue文件本身为准。
2.render函数
先看看main.js文件中的内容:
import Vue from 'vue'
import App from './App'
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
components: { App },
template: '<App/>'
})
从文件中我们可以看到,首先把Vue实例绑定到#app元素上面,这个#app元素是index.html中的元素,不是App.vue中的元素。再引入 App.vue这个文件,然后再Vue实例中注册这个组件,在把App中的html渲染到Vue实例上。
现在通过render函数改写这个:
import Vue from 'vue'
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#myApp',
render: h => (<div>123</div>)
})
render: h => (<div>123</div>)
这里的作用就是括号里面的html。
再看另外一个例子:
Vue.component('MyApp', {
render: function (createElement) {
return createElement(
'h1', // 标签名称
'123' // 子元素数组
)
},
props: {
level: {
type: Number,
required: true
}
}
})
/* eslint-disable no-new */
new Vue({
el: '#myApp',
template: '<MyApp/>'
})
首先通过Vue.component注册一个组件,这个组件通过template: '<MyApp/>'渲染到Vue根实例。render函数用来生成组件中的html内容:
Vue.component('MyApp', {
render: function (createElement) {
return createElement(
'h1', // 标签名称
'123' // 子元素数组
)
},
props: {
level: {
type: Number,
required: true
}
}
})
再次修改main.js:
import Vue from 'vue'
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#myApp',
render: function (createElement) {
return createElement(
'h1', // 标签名称
'123' // 内容
)
}
})
直接通过render函数渲染。
- 使用h代替createElement
通过代码的方式去写html标签是很繁琐的,但是 通过上面这种形式可以直接写html标签了:
import Vue from 'vue'
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#myApp',
render: h => (<div>
<h1>123</h1>
</div>)
})
3.使用代码注册组件
从上面一节中我们看到,可以通过js代码的方式去注册一个组件。
Vue.component('MyApp', {
render: function (createElement) {
return createElement(
'h1', // 标签名称
'123' // 子元素数组
)
},
props: {
level: {
type: Number,
required: true
}
}
})
现在我们来改写main.js,用代码来注册一个组件:
import Vue from 'vue'
import App from './App'
Vue.config.productionTip = false
Vue.component('MyHeader', {
render: function(createElement) {
return createElement ('h1', this.headerTitle)
},
data () {
return {
headerTitle: 'my header'
}
},
mounted () {
console.log('mounted')
}
})
/* eslint-disable no-new */
new Vue({
el: '#app',
components: {
App
},
render: h => h(App)
})
代码方式注册组件也能指定data、props和生命周期等这些属性。
4.extend
官方例子:
先看个例子,main.js中的代码如下:
import Vue from 'vue'
import App from './App'
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
components: {
App
},
render: h => h(App)
})
let MyVue = Vue.extend({
template: '<h2>h2 标签 {{name}} </h2>',
data () {
return {
name: 'hello '
}
},
mounted () {
console.log('extend')
}
})
new MyVue().$mount(".mine");
通过Vue.extend
创建一个Vue子类对象构造器,再通过new MyVue().$mount(".mine");
创建这个实例对象,然后挂载到.mine元素上,我在app里面有元素的class属性是mine,所以这个组件会挂载到对应的元素上。(挂载时要保证这个元素被创建了,某种会报错找不到这个元素…,所以才把这个挂载写到App组件被注册之后)
extend还能用作组件上
上面这种方式,我们是在extend里编写模板,我们还能把.vue文件作为参数传入extend函数。
- 先创建一个.vue文件,作为模板
TheFooter.vue
<template>
<div class="footer">
111
<h1>{{name}}, {{msg}}</h1>
</div>
</template>
<script>
export default {
data () {
return {
name: 'footer',
msg: '这是footer.vue文件'
}
}
}
</script>
<style>
</style>
在main.js中使用:
import Vue from 'vue'
import App from './App'
import TheFooter from './TheFooter'
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
components: {
App
},
render: h => h(App)
})
// 构建vue子类构造器
let TheFooterConstructer = Vue.extend(TheFooter)
// 创建实例
let theFooter = new TheFooterConstructer();
theFooter.name = '修改了值得name';
// 这样就挂载到了.mine这个元素上了
theFooter.$mount('.mine')
- 不依赖现有标签挂载
从上面的几个例子来看,在挂载组件的时候,页面上一定要有一个供挂载的元素才行,否则就会报错,如果不想依赖现有元素,可以用如下方式创建元素:
import Vue from 'vue'
import App from './App'
import TheFooter from './TheFooter'
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
components: {
App
},
render: h => h(App)
})
// 构建vue子类构造器
let TheFooterConstructer = Vue.extend(TheFooter)
// 创建实例
let theFooter = new TheFooterConstructer();
theFooter.name = '修改了值得name';
// 创建一个div,用来挂载用
let mountElement = document.createElement('div');
// 添加创建的元素到body最后面
document.body.appendChild(mountElement)
// 挂载到创建元素上
theFooter.$mount(mountElement)
通过上面的方式就创建了一个无需在已存在元素上挂载的组件。
二、开发插件
1.插件使用例子
我们先来看下使用element-ui的例子,首先是安装element-ui库(插件)。
cnpm install element-ui -S
然后再项目中使用,main.js:
import Vue from 'vue';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import App from './App.vue';
Vue.use(ElementUI);
new Vue({
el: '#app',
render: h => h(App)
});
分析上面代码我们可以看到import ‘element-ui/lib/theme-chalk/index.css’;
这是我们引入的一个全局css样式。import ElementUI from ‘element-ui’;
和 Vue.use(ElementUI);
这两句代码是引入element-ui并全局注册的关键代码。
2.插件开发
开发插件
根据上面的例子,我们也来自己开发一个类似使用方法的插件。插件通过两个组件MyHeader导航栏和MyFooter底部栏。
首先在src目录下新建一个目录myui,然后新建两个.vue文件MyHeader.vue和MyFooter.vue以及一个js文件myui.js。
MyFooter.vue
<template>
<div id="myfooter">
footer
</div>
</template>
<script>
export default {
name: 'MyFooter'
}
</script>
<style>
body {
padding-bottom: 60px;
}
</style>
<style scoped>
#myfooter {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
height: 60px;
color: aliceblue;
background: rgb(13, 13, 14);
line-height: 60px;
text-align: center;
}
</style>
MyHeader.vue
<template>
<div id="myheader">
header
</div>
</template>
<script>
export default {
name: 'MyHeader'
}
</script>
<style>
body {
padding-top: 60px;
}
</style>
<style scoped>
#myheader {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 60px;
color: aliceblue;
background: rgb(45, 70, 104);
line-height: 60px;
text-align: center;
}
</style>
这两个.vue组件最终效果是这样的。
现在来开发myui.js中的内容了:
import MyFooter from './MyFooter'
import MyHeader from './MyHeader'
// 新建一个对象,也就是开发的插件对象
let myui = {
// 这个对象必须要有一个 install 方法,而且这个方法第一个参数是Vue实例
install (Vue) {
// 最vue实例上注册 两个 vue 组件
Vue.component(MyFooter.name, MyFooter)
Vue.component(MyHeader.name, MyHeader)
}
}
// 导出对象
export default myui
好了~,这样一个简单的插件就开发完成了!我们来看看该怎么使用插件?
使用插件
在main.js中引入开发的插件:
import Vue from 'vue'
import App from './App'
// 引入插件对象
import MyUI from './myui/myui'
// 使用插件
Vue.use(MyUI)
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
components: { App },
template: '<App/>'
})
在App.vue中使用插件:
<template>
<div id="app">
<MyHeader></MyHeader>
<MyFooter></MyFooter>
</div>
</template>
<script>
export default {
name: 'App'
}
</script>
<style>
</style>
最终结果:
分析
在main.js中,引入了配置插件的js文件import MyUI from ‘./myui/myui’
,这个文件中配置了插件信息。
Vue.use(MyUI)
这个地方配置实用插件,其实在调用use方法的时候执行的就是myui中对象的install方法,而install方法上的参数Vue就是当前的Vue。在install方法。里面注册了两个组件,实际上也就是用外面的vue注册了两个组件。
*可能有人会想为什么要怎么做,为什么不直接在main.js中直接使用**vue.component()*来注册,非要写个myui.js然后传Vue在注册组件?
嗯~,插件嘛,开发起来就是为了要实现某一功能,提供给别人用的,就像element-ui一样,到时候只需要使用npm 安装到项目中就可以直接使用了,所以呢,需要这样开发。如果只是本身项目自己使用,就没必要这样写了!
修改插件,丰富插件功能
为了丰富插件功能,我们修改一下MyHeader.vue文件中的代码:
<template>
<div :style="{backgroundColor: bgColor, color: color}" id="myheader">
{{title}}
</div>
</template>
<script>
export default {
name: 'MyHeader',
data () {
return {
title: 'header'
}
},
props: {
// 背景颜色
bgColor: {
type: String,
default: 'rgb(45, 70, 104)',
required: false
},
// 字体颜色
color: {
type: String,
default: 'white',
required: true
}
},
methods: {
// 这个方法用来改变颜色
changColor(bgColor = null, color = null) {
if (null !== bgColor || null !== color) {
this.bgColor = bgColor
this.color = color
}
}
},
mounted() {
console.log('header已被加载')
}
}
</script>
<style>
body {
padding-top: 60px;
}
</style>
<style scoped>
#myheader {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 60px;
line-height: 60px;
text-align: center;
}
</style>
修改后的MyHeadee组件提供了多个属性,data,props,method和mounted,然后也绑定了内联样式,title是header组件显示的文字,bgColor是header组件的背景颜色,color是header组件的文字颜色。
props中的属性可以在使用组件时传入,那么data中的属性和methods中的方法要怎么使用呢??
修改一下MyHeader.vue中的代码:
<template>
<div :style="{backgroundColor: bgColor, color: color}" id="myheader">
{{title}}
</div>
</template>
<script>
export default {
name: 'MyHeader',
data () {
return {
title: 'header'
}
},
props: {
// 背景颜色
bgColor: {
type: String,
default: 'rgb(45, 70, 104)',
required: false
},
// 字体颜色
color: {
type: String,
default: 'white',
required: false
}
},
methods: {
// 这个方法用来改变颜色
changColor(bgColor = null, color = null) {
if (null !== bgColor || null !== color) {
this.bgColor = bgColor
this.color = color
}
}
},
mounted() {
console.log('header已被加载')
}
}
</script>
<style>
body {
padding-top: 60px;
}
</style>
<style scoped>
#myheader {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 60px;
line-height: 60px;
text-align: center;
}
</style>
基于上面的问题和之前的extend函数,我们对myui.js做如下修改:
import MyFooter from './MyFooter'
import MyHeader from './MyHeader'
let myui = {
// install 可以接受除 Vue实例以外的参数 在main.js中 Vue.use()时传入
install (Vue, title = null) {
Vue.component(MyFooter.name, MyFooter)
//Vue.component(MyHeader.name, MyHeader)
// 构造vue子类构造器
let MyHeaderContructor = Vue.extend(MyHeader)
// 通过上面的构造器生成 MyHeaderContructor 实例对象,这个对象是可以直接访问组件中的属性的
let myHeaderInstance = new MyHeaderContructor()
// 初始化title的值
if (title !== null) {
myHeaderInstance.title = title
}
// 注册全局方法
Vue.initMyHeader = function (bgColor, color) {
myHeaderInstance.bgColor = bgColor
myHeaderInstance.color = color
}
// 添加实例方法
Vue.prototype.$changeColor = function(bgColor, color) {
myHeaderInstance.changeColor(bgColor, color)
}
// 挂载实例
let ele = document.createElement('div')
document.body.appendChild(ele)
myHeaderInstance.$mount(ele)
}
}
export default myui
由于MyHeader.vue没有同过注册组件的方式使用的,所以要删除App.vue对MyHeader组件的引用,App.vue中代码如下:
<template>
<div id="app">
<MyFooter></MyFooter>
</div>
</template>
<script>
export default {
name: 'App'
}
</script>
<style>
</style>
页面效果不变。
调用方法
在myui.js中对象中的install (Vue, title = null)
方法传入了一个title,现在我们通过这个属性来改变页面上header中显示的内容,在main.js中做如下修改,在调用use方法的时候传入一个title属性:
Vue.use(MyUI, "我是插件中的header组件")
使用后的结果,我们发现header上的值也发生了改变:
然后在myui.js还有这么一个方法
Vue.prototype.$changeColor = function(bgColor, color) {
myHeaderInstance.changeColor(bgColor, color)
}
这要怎么使用呢??
分析上面方法我们能发现,这个方法**$changeColor是挂载到Vue实例的原型上的,在组件中使用时只需要调用this.\$changeColor ()
即可。
现在,我们在App.vue**中做如下修改:
<template>
<div id="app">
<div class="btn">
<button @click="changeColor('rgb(65, 118, 168)','rgb(19, 18, 18)')">变成蓝色</button>
<button @click="changeColor('rgb(214, 200, 8)','rgb(19, 18, 18)')">变成黄色</button>
<button @click="changeColor('rgb(19, 18, 18)','rgb(248, 248, 248)')">变成黑色</button>
<button @click="changeColor('rgb(126, 124, 124)', 'rgb(248, 248, 248)')">变成灰色</button>
</div>
<MyFooter></MyFooter>
</div>
</template>
<script>
export default {
name: 'App',
methods: {
changeColor(bgColor, color) {
this.$changeColor(bgColor, color);
}
}
}
</script>
<style>
.btn {
margin: 100px auto 0;
}
</style>
结果,我们点击对应的按钮,就可以操作使header发生改变:
至此,一个Vue插件就开发完成了,如果想让别人也能用到拟开发的插件,只需要把你开发的插件上传到npm上,别人再从npm上安装下来,就可以愉快的使用了!