当前目录结构,入口是app.js然后新建一个toast.vue组件,在app.js里使用这个组件,因为我们需要在全局里直接调用它,比如我们点击按钮的时候提示一个信息:当前功能不稳定,最好的办法就是直接调用它的this.toast那?
- 方法一:直接在toast.vue组件里写
import Vue from 'vue'
//这里为什么Vue.prototype.$toast 不用放在export default中导出也可以用,因为你每次都要引入vue,vue引入了Vue.prototype.$toast自然也就引入了
Vue.prototype.$toast = function(){
console.log('i am toast')
}
export default {
name: 'GuluToast'
}
然后直接在app.js里使用
new Vue({
created(){
this.$toast()
}
})
运行后直接就会在页面打印出i am toast
问题:
- 你不知道到底能不能改写prototype.$toast
- 有可能用户用的不是vue而是vue2,那么你直接写import Vue from 'vue'就会出问题
- 方法二:通过自己写插件来解决上述两个问题
新建一个plugin.js
export default {
install(Vue, options){
Vue.prototype.$toast = function(){
console.log('i am toast')
}
}
}
然后只需要在app.js里引入plugin.js再通过vue使用就行
import plugin from './plugin'
Vue.use(plugin)//会去执行install方法
new Vue({
created(){
this.$toast()
}
})
通过插件的方法同样可以得到i am toast,而且因为是用户主动写的Vue.use(plugin)所以解决了第一个你不确定能不能改这个prototype.$toast的问题,而且我们没有import vue,那么vue是哪来的?是Vue.use(plugin)中的use把它前面的Vue传给我们的
实现点击按钮动态创建元素
- js写法
plugin.js
export default {
install(Vue, options){
Vue.prototype.$toast = function(message){
let div = document.createElement('div')
div.textContent = message
document.body.appendChild(div)
}
}
}
在app.js里写
new Vue({
methods: {
showToast(){
this.$toast('hello')
}
}
})
然后在index.html里绑定事件
<div id="app">
<button @click="showToast">你好</button>
</div>
<script src="./src/app.js"></script>
toast.vue
<template>
<div class="toast">
<slot></slot>
</div>
</template>
<script>
export default {
name: 'GuluToast'
}
</script>
问题因为我们是在用vue,如果直接用通过js动态添加元素不太友好,所以我们不推荐
- vue组件写法
plugin.js
import Toast from './toast.vue'
export default {
install(Vue, options){
Vue.prototype.$toast = function(message){
let Constructor = Vue.extend(Toast)
let toast = new Constructor
toast.$slots.default = [message] //将你传入的message的值传给组件插槽
toast.$mount() //挂载到页面上
document.body.appendChild(toast.$el) //将dom放到body下面
}
}
}
设置按钮几秒后自动消失,点击按钮执行一个回调,然后用户可以传入点击关闭的文本和回调
toast.vue
<template>
<div class="toast" @click="onClickClose">
<slot></slot>
<div class="line"></div>
<span class="close" v-if="closeBtn">
{{closeBtn.text}}
</span>
</div>
</template>
<script>
export default {
name: 'GuluToast',
props: {
autoClose: {
type: Boolean,
default: true
},
autoCloseDelay: {
type: Number,
default: 5
},
closeBtn: {
type: Object,
//如果你的default的值是一个对象,那么你不能直接写这个对象,你要一个函数return一个对象
default: ()=>{
return {
text: '关闭',
callback: undefined
}
}
}
},
created(){
console.log(this.closeBtn)
},
mounted() {
if(this.autoClose){
setTimeout(()=>{
this.close()
},this.autoCloseDelay*1000)
}
},
methods: {
close(){
//移除这个dom元素
this.$el.remove()
//完全销毁一个实例
this.$destroy()
},
log(){
console.log('aaaa')
},
onClickClose(){
this.close()
if(this.closeBtn && typeof this.closeBtn.callback === 'function'){
this.closeBtn.callback(this) //this是toast组件实例,也就是app.js里传入的形参a
}
}
}
}
</script>
app.js
import Toast from './toast'
import plugin from './plugin'
Vue.component('g-toast',Toast)
Vue.use(plugin)//会去执行install方法
new Vue({
created(){
this.$toast('hello',{
closeBtn: {
callback: (a)=>{
a.log()
}
}
})
}
})
plugin.js
import Toast from './toast.vue'
export default {
install(Vue, options){
Vue.prototype.$toast = function(message, dataoptions={}){
let Constructor = Vue.extend(Toast)
let toast = new Constructor({
propsData: {
closeBtn: dataoptions.closeBtn
}
})
toast.$slots.default = [message]
toast.$mount()
document.body.appendChild(toast.$el)
}
}
}
- propsData:创建实例时传递 props
修改因为内容太多,弹出框高度撑不下问题,将高度改为最小高度
- this.$nextTick处理异步操作
<template>
<div class="toast" @click="onClickClose" ref="b">
<slot v-if="!enage"></slot>
<div v-else v-html="$slots.default[0]"></div>
<div class="line" ref="a"></div>
<span class="close" v-if="closeBtn">
{{closeBtn.text}}
</span>
</div>
</template>
<script>
export default {
mounted() {
//将b的高度赋值给a
this.$nextTick(function(){
this.$refs.a.style.height = this.$refs.b.offsetHeight + 'px'
})
}
}
</script>
小技巧:如果你发现你眼睛观察到的不是0,而你debugger的时候确是0,那就是异步的问题
网友评论