1. 项目全局弹窗 toast 插件封装
当前项目中有场景需要制作h5底部的弹窗,该弹窗可以分享链接、清空数据、内容举报、内容收藏等高频操作,基于此我们可以做成全局组件。官网提供的用法特别简单,写好的组件直接在main.js里面导入然后使用Vue.component('my-component-name', { // ... 选项 ... })就可以全局通用了,但是存在的问题是:所有内容都是在 #app 下渲染,注册组件都是在当前位置渲染。如果我要实现一个模态框的提示组件,就比较鸡肋了。这时候,Vue.extend + vm.$mount 组合就派上用场了。
<template>
<div class="operate-more" v-show="showPoup" @click="hide">
<div class="wrapper" @click.stop>
<div class="operates">
<div class="item-info border-bottom-1px" v-for="(item, index) in options" :key="index" @click="onClick(item)">
{{ item.name }}
</div>
</div>
<div class="cancel" @click="hide">取消</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
showPoup: false,
options: [] // 传参数据
};
},
methods: {
init(options) {
this.options = options;
},
// 展示
show() {
this.showPoup = true;
},
// 隐藏
hide() {
this.showPoup = false;
},
// 执行回调函数
onClick(item) {
if (item.onClick && typeof item.onClick === 'function') {
this.hide();
item.onClick();
}
}
}
};
</script>
<style lang="less" scoped>
@keyframes showPoup {
0% {
transform: translate3d(-50%, 100%, 0);
}
100% {
transform: translate3d(-50%, 0, 0);
}
}
.operate-more {
position: fixed;
left: 0;
right: 0;
top: 0;
bottom: 0;
z-index: 999;
background: rgba(0, 0, 0, 0.5);
.wrapper {
position: absolute;
left: 50%;
transform: translate3d(-50%, 0, 0);
bottom: 0;
width: 3.59rem;
animation: showPoup ease 300ms;
.operates {
.item-info {
display: flex;
align-items: center;
height: 0.56rem;
font-size: 0.16rem;
font-weight: normal;
color: #fc4245;
line-height: 0.22rem;
background: #ffffff;
justify-content: center;
&:first-child {
border-radius: 0.12rem 0.12rem 0 0;
}
&:last-child {
border-radius: 0 0 0.12rem 0.12rem;
&::after {
border: none;
}
}
&:only-child {
border-radius: 0.12rem;
}
}
}
.cancel {
display: flex;
align-items: center;
justify-content: center;
height: 0.48rem;
font-size: 0.16rem;
margin: 0.08rem 0;
border-radius: 0.12rem;
color: #1f2126;
background: #ffffff;
color: #2972ec;
}
}
}
</style>
在此处通过install注册全局插件,然后再main.js中通过 Vue.use(ClearHistory) 来调用install方法,
import ClearHistory from './ClearHistory';
export default {
install(Vue) {
const P = Vue.extend(ClearHistory);
let instance;
Vue.prototype.$clearhistory = options => {
if (!instance) {
// $mount()不带参数,会把组件在内存中渲染完毕, instance 表示创造了一个新的vue实例
instance = new P().$mount();
// instance.$el 拿到的就是组件对应的dom元素,可以直接操作dom
document.body.appendChild(instance.$el);
}
Vue.nextTick(() => {
// 直接调用 vue实例的 init 方法,实例中的options 值取到传过去的参数值
instance.init(options);
// 调用实例的show 方法可以展示组件
instance.show();
});
};
}
};
在具体业务中调用该组件:
const paramObj = [
{name: 'test001', onClick: () => {}},
{name: 'test002', onClick: () => {}},
];
this.$clearhistory(paramObj);
2. 关于 vue 组件封装的思考
我们在使用Vue做项目开发之余,都想着如何造轮子,给项目提供更加便捷的插件和更好的优化方式。基于vue的组件开发包含以下几个方面:
2.1 Vue.extend创建实例后挂载
使用vue构造器创建一个子类,参数是包含组件选项的对象。可以构造一个全局的组件并且挂在到指定的dom元素上:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="https://cdn.bootcss.com/vue/2.5.17-beta.0/vue.min.js"></script>
</head>
<body>
<div id="app-extend"></div>
</body>
<script>
// 创建构造器
const Profile = Vue.extend({
template: '<div><p>{{extendData}}</p><p>实例传入的数据为:{{propsExtend}}</p></div>',
data: function () {
return {
extendData: '这是extend扩展的数据',
}
},
props: {
propsExtend: String
}
})
// 创建 Profile 实例,并挂载到一个元素上。携带外来数据 propsExtend
const profile = new Profile({ propsData: { propsExtend: '我是实例传入的数据' } });
profile.$mount('#app-extend');
</script>
</html>
2.2 Vue.component 注册实例
最简单也是最常用的组件注册方式:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="https://cdn.bootcss.com/vue/2.5.17-beta.0/vue.min.js"></script>
</head>
<body>
<div id="app">
<div class="component-one">
下面是组件一数据:
<component-one></component-one>
</div>
</div>
</body>
<script>
//1.创建组件构造器
const obj = {
props: [],
template: '<div><p>{{extendData}}</p></div>',//最外层只能有一个大盒子,这个和<tempalte>对应规则一致
data: function () {
return {
extendData: '这是Vue.component传入Vue.extend注册的组件',
}
},
};
// 下面两种方法都可以注册实例:
Vue.component('component-one', obj);
Vue.component('component-one', Vue.extend(obj))
//3.挂载
new Vue({
el: '#app'
});
</script>
</html>
2.3 mixins 混入
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="https://cdn.bootcss.com/vue/2.5.17-beta.0/vue.min.js"></script>
</head>
<body>
<div id="app">
<p>mixin的data值为:{{mixinData}}</p>
<button @click="getSum()">点我触发getSum方法</button>
</div>
</body>
<script>
const mixin = {
data: { mixinData: '我是mixin的data' },
created: function () {
console.log('这是mixin的created');
},
methods: {
getSum: function () {
console.log('这是mixin的getSum里面的方法');
}
}
}
const mixinTwo = {
data: { mixinData: '我是mixinTwo的data' },
created: function () {
console.log('这是mixinTwo的created');
},
methods: {
getSum: function () {
console.log('这是mixinTwo的getSum里面的方法');
}
}
}
const vm = new Vue({
el: '#app',
data: { mixinData: '我是vue实例的data' },
created: function () {
console.log('这是vue实例的created');
},
methods: {
getSum: function () {
console.log('这是vue实例里面getSum的方法');
}
},
mixins: [mixin, mixinTwo]
})
</script>
</html>
2.4 install 和 Vue.use 构造和注册vue插件
这是我们日常造轮子的主要的方式,这些轮子都可以称之为插件,它的功能范围没有严格的限制,一般包含如下几种:
- 添加全局方法或者属性。如: vue-custom-element
- 添加全局资源:指令/过滤器/过渡/组件等。如 vue-touch
- 通过全局混入来添加一些组件选项。如 vue-router
- 添加 Vue 实例方法,通过把它们添加到 Vue.prototype 上实现。
- 一个库,提供自己的 API,同时提供上面提到的一个或多个功能。如 vue-router 无论大小,插件要实现的功能无非就是上述这几种。但是,这并不妨碍我们创造出复杂的插件,不过我们还是希望给用户提供一个简单的使用方法,他不需要关注插件内部做了些什么。固Vue提供了use方法,专门来在new Vue()之前使用插件。
Vue.js 的插件应该暴露一个 install 方法。这个方法的第一个参数是 Vue 构造器,第二个参数是一个可选的选项对象,用于传入插件的配置:
MyPlugin.install = function (Vue, options) {
// 1. 添加全局方法或属性
Vue.myGlobalMethod = function () {
}
// 2. 添加全局资源
Vue.directive('my-directive', {
bind(el, binding, vnode, oldVnode) {
}
})
// 3. 注入组件选项
Vue.mixin({
created: function () {
}
})
// 4. 添加实例方法
Vue.prototype.$myMethod = function (methodOptions) {
}
// 5. 注册全局组件
Vue.component('myButton', {
})
}
Vue.use(MyPlugin,{ });
下面关于全局插件注册的实例参考:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="https://cdn.bootcss.com/vue/2.5.17-beta.0/vue.min.js"></script>
</head>
<body>
<div id="app">
<button v-initdirective>点击调用myGlobalMethod</button>
<button @click="$myMethod">点击调用$myMethod</button>
</div>
</body>
<script>
const MyPlugin = {};
MyPlugin.install = function (Vue, options) {
// 1. 添加全局资源,第二个参数传一个值默认是update对应的值
Vue.directive('initdirective', {
//做绑定的准备工作,添加时间监听
bind(el, binding, vnode, oldVnode) {
console.log('指令my-directive的bind执行啦');
},
//获取绑定的元素
inserted: function (el) {
console.log('指令my-directive的inserted执行啦');
},
//根据获得的新值执行对应的更新,对于初始值也会调用一次
update: function () {
console.log('指令my-directive的update执行啦');
},
componentUpdated: function () {
console.log('指令my-directive的componentUpdated执行啦');
},
//做清理操作比如移除bind时绑定的事件监听器
unbind: function () {
console.log('指令my-directive的unbind执行啦');
}
})
// 2. 注入组件
Vue.mixin({
created: function () {
console.log('注入组件的created被调用啦');
console.log('options的值为', options)
}
})
// 3. 添加实例方法
Vue.prototype.$myMethod = function (methodOptions) {
console.log('实例方法myMethod被调用啦');
}
}
// 调用MyPlugin
Vue.use(MyPlugin, { someOption: true })
// 4.挂载
new Vue({
el: '#app'
});
</script>
</html>