首先Vue3.0封装全局的组件和2.0有所以不同,3.0采用
createApp,reactive
或者createVNode,render
这里采用createApp
1.先搞个loading.vue组件
<template>
<section>
<div class="box" v-if='options.showToast'>
<div class="load">
<img src="../assets/load.gif" alt="">
<div class="load_txt">{{options.txt}}</div>
</div>
</div>
</section>
</template>
<script setup>
import { defineProps } from 'vue'
defineProps({
options: {
type: Object,
default: {}
}
})
</script>
<style scoped>
.box {
position: fixed;
top: 0;
left: 0;
z-index: 100;
width: 100%;
height: 100%;
/* background: rgba(0, 0, 0, 0.5); */
}
.load {
width: 180px;
height: 180px;
position: absolute;
top: 50%;
left: 50%;
margin-top: -90px;
transform: translate(-50%);
}
.load img {
width: 100%;
}
.load_txt {
text-align: center;
color: #e4393c;
}
</style>
2.在utils文件夹中建load.js
-
const app = createApp({})
返回一个提供应用上下文的应用实例。应用实例挂载的整个组件树共享同一个上下文 -
该函数接收一个根组件选项对象作为第一个参数
-
使用第二个参数,我们可以将根 参数 传递给应用程序:
import {createApp,reactive} from 'vue'
import Loading from '@/components/loading.vue'
const divDom = document.createElement('div')
divDom.setAttribute('class', 'loading-container')
// document.body.appendChild(divDom)
const options = reactive({
showToast: true,
txt: '拼命加载中...'
})
//这里是关键部位options 是向Loading 组件传递的参数
const $loading = createApp(Loading, {options}).mount(divDom)
const loadPlguin = {
show({txt}) { // 控制显示loading的方法,这里是show方法传入的对象
options.showToast = true
options.txt = txt
document.body.appendChild($loading.$el)
},
hide() { // 控制loading隐藏的方法
options.showToast = false;
}
}
export default {
install(app) {
// 3.0的全局挂载
app.config.globalProperties.$loading = loadPlguin
}
}
3.在main.js
中引入
import load from './utils/load'
import { createPinia } from 'pinia'
// 使用pinia
let app=createApp(App);
app.use(store).use(load).use(router).use(createPinia()).mount('#app')
4. 在组件中的调用
这里提一嘴,通常是用
ref
包装基本类型的数据,reactive
包装引用类型的数据,ref的底层也是reactive
<template>
<section>
<img class="img" :src="url" alt="">
<div v-for='(item,index) in list' :key='index'>{{item.email}}</div>
<button @click='getData'>获取数据</button>
</section>
</template>
<script setup>
import { getCurrentInstance, reactive, ref,toRefs } from 'vue';
const { proxy } = getCurrentInstance();//获取全局
const url = ref(null)
const state = reactive({
list:[]
});
const getData = () => {
proxy.$loading.show({ txt: 'motherFucker...' })
fetch('https://jsonplaceholder.typicode.com/comments')
.then(res => res.json())
.then((res) => {
proxy.$loading.hide();
// url.value=res.src
state.list = res;
}).catch(err => {
proxy.$loading.hide();
})
}
let {list}=toRefs(state)//所有的state中都在此解构,模板中就省去用state.xx去取值了,直接取xx
</script>
<style scoped>
.img {
width: 100%;
}
</style>
全局dialog封装 这里用的createVNode,render
1. 搞个dialog.vue组件
<template>
<div id="confirm">
<div class="contents">
<div class="content-top">{{title}}</div>
<div class="content-center">{{msg}}</div>
<div class="content-bottom">
<button type='primary' @click='okButton' class="left">{{okButtonTxt}}</button>
<button type='info' @click='noButton' class="right">{{noButtonTxt}}</button>
</div>
</div>
</div>
</template>
<script setup>
import { defineProps} from 'vue'
defineProps({
title: {
type: String,
default: '这个是头部'
},
msg: {
type: String,
default: '这个是内容'
},
okButtonTxt: {
type: String,
default: '确定'
},
noButtonTxt: {
type: String,
default: '取消'
},
okButton: {
type: Function
},
noButton: {
type: Function
}
})
</script>
<style scoped>
#confirm {
position: fixed;
left: 0;
top: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.3);
}
.contents {
width: 250px;
height: 180px;
border: 1px solid #ccc;
border-radius: 10px;
background-color: #fff;
position: fixed;
top: 50%;
left: 50%;
margin-top: -90px;
margin-left: -125px;
}
.content-top {
width: 100%;
height: 40px;
border-bottom: 1px solid #ccc;
text-align: center;
font-size: 20px;
font-weight: 700;
line-height: 40px;
}
.content-center {
width: 90%;
height: 80px;
margin: 5px auto;
}
.content-bottom {
width: 85%;
height: 40px;
margin: 0 auto;
/* border:1px solid red; */
position: relative;
}
.left {
position: absolute;
left: 0;
width: 40%;
}
.right {
position: absolute;
right: 0;
width: 40%;
}
</style>
2.utils中建立dialog.js
import {createVNode,render} from 'vue'
import Dialog from '@/components/dialog.vue'
const divDom = document.createElement('div')
divDom.setAttribute('class', 'dialog-container')
document.body.appendChild(divDom);
const dialogPlguin=(option)=>{
return new Promise((resolve,reject)=>{
const okButton=()=>{//确认
render(null,divDom)
resolve()
}
const noButton=()=>{// 取消
render(null,divDom)
reject(new Error('取消'))
}
const vNode=createVNode(Dialog,{...option,okButton,noButton})
render(vNode,divDom)
})
}
export default {
install(app) {
app.config.globalProperties.$dialog = dialogPlguin
}
}
3.main.js中引入
import load from './utils/load'
import dialog from './utils/dialog'
// 使用pinia
import { createPinia } from 'pinia'
// app.use(createPinia())//
let app=createApp(App);
app.use(store).use(load).use(dialog).use(router).use(createPinia()).mount('#app')
4.组件中使用
const { proxy } = getCurrentInstance();
const getDialog = () => {
proxy.$dialog({ title: '你好啊', noButtonTxt: '是' }).then(() => {
console.log('成功')
}).catch(err => {
console.log(err)
})
}
关于vue3.0动态节点的获取
<template>
<div v-for='(item,index) in list' :key='index' :ref='listDom' :id='item.name'>{{item.name}}---{{item.id}}</div>
<button @click="add"> 添加</button>
<div>
<button @click='getDom'>获取Dom</button>
</div>
<div>
<button @click='addColor'>操作dom</button>
</div>
<script setup>
import Child from '../components/HelloWorld.vue'
import { reactive, ref, onBeforeUpdate, toRef, toRefs } from 'vue'
const state=reactive({
list:[]
})
const add=()=>{//添加动态的数据
fetch('https://jsonplaceholder.typicode.com/comments/1')
.then(res => res.json())
.then((res) => {
state.list.push(res)
}).catch(err => {
})
}
const addColor = () => {//操作获取的dom
console.log(resultList)
resultList.forEach((element, index) => {
resultList[0].style = 'color:#e4393c';
});
}
let resultList = [];//用来存储遍历的DOM元素
const listDom = (e) => {//这里是获取所有的dom节点并且存到resultList
resultList.push(e);
};
onBeforeUpdate(() => {//更新之前一定重置,因为会拿着之前的数组进行拼接
resultList = [];
});
let {list}=toRefs(state)
</script>