Previous articles:
- Vue3 – Write components using TSX
- Vue3 hasn’t even started yet. – Just want to give up
- Vue3-vuex-typescript trampling tour
preface
After a brief study of VUe3, I found a big difference compared with VUe2. Some new and removed apis were added and many strange and clever techniques of VUe2 could not be used any more, especially in the transformation of custom components, which almost caused me to collapse. I still have a lot of way to go to fully master vue2. Using the Notification component as an example, we will describe the trip from vue2 -> VUe3 to the pit
Implementation effect
The preparatory work
See Vue3 – Writing components using TSX
Adopt the way of describing the whole first and then the details
The code structure
├ ─ ─ components │ ├ ─ ─ NotifyDemo. Vue # notification use example │ ├ ─ ─ notification # notification custom plugin │ │ ├ ─ ─ Call the function. The TSX # business notification. Like alert │ │ ├ ─ ─ index. The ts # install plugin out │ │ └ ─ ─ notification. Vue # componentCopy the code
start
Here, follow me, right hand and left hand in slow motion
Import file registration component
- vue2
// main.js
import Vue from 'vue'
import App from './App.vue'
import Notification from './components/notification'
// Call the install method of the plug-in
Vue.use(Notification)
new Vue({
render: h= > h(App)
}).$mount('#app')
Copy the code
- vue3
// main.ts
import { createApp } from 'vue';
import App from './App.vue';
import Notification from './components/notification'
const app = createApp(App);
// Call the install method of the plug-in
app.use(Notification);
app.mount('#app');
Copy the code
Notification install
- vue2
// notification/index.js
$this.$notify({content: 'test'}) */
import Notification from './notification.vue'
import notify from './function'
export default (Vue) => {
// Register component globally
Vue.component(Notification.name, Notification)
// Mount the $notify method on the prototype of vue
Vue.prototype.$notify = notify
}
Copy the code
- vue3
// notification/index.ts
/** * Vue3 has removed the whole export of Vue, so that the method can not be directly mounted on the prototype of Vue, but through config registration * use component to obtain instance.proxy. const { proxy } = getCurrentInstance(); * proxy.$notify({ content: 'test' }); * Do not use CTX, production environment does not support */
import { Plugin, App } from 'vue';
import Notification from './notification.vue';
import notify from './function';
// Mount the component method
const install = (app: App): App= > {
//
app.config.globalProperties.$notify = notify;
app.component(Notification.name, Notification);
return app;
};
export default install as Plugin;
Copy the code
notification.vue
- vue2
<template>
<transition name="fade" @after-leave="afterLeave" @after-enter="afterEnter">
<div class="notification" :style="style" v-show="visible" @mouseenter="clearTimer" @mouseleave="createTimer">
<span class="content">{{ content }}</span>
<a class="btn" @click="handleClose">{{ btn }}</a>
</div>
</transition>
</template>
<script>
export default {
name: 'Notification',
data () {
return {
verticalOffset: 0.autoClose: 3000.height: 0.visible: false}; },props: {
content: {
type: String.required: true
},
btn: {
type: String.default: 'off'}},computed: {
style() {
return {
position: "fixed".right: "20px".bottom: `The ${this.verticalOffset}px`}; }},mounted() {
this.createTimer();
},
methods: {
handleClose (e) {
e.preventDefault()
this.$emit('close')
},
afterLeave () {
this.$emit('closed')},createTimer() {
if (this.autoClose) {
this.timer = setTimeout(() = > {
this.visible = false;
}, this.autoClose); }},clearTimer() {
if (this.timer) {
clearTimeout(this.timer); }},afterEnter() {
// debugger // eslint-disable-line
this.height = this.$el.offsetHeight; }},beforeDestory() {
this.clearTimer(); }},</script>
<style scoped>
.notification{
display: inline-flex;
background-color: # 303030;
color: rgba(255.255.255.1);
align-items: center;
padding: 20px;
min-width: 280px;
box-shadow: 0px 3px 5px -1px rgba(0.0.0.0.2), 0px 6px 10px 0px rgba(0.0.0.0.14), 0px 1px 18px 0px rgba(0.0.0.0.12);
flex-wrap: wrap;
transition: all .3s;
}
.content{
padding: 0;
}
.btn{
color: #ff4081;
padding-left: 24px;
margin-left: auto;
cursor: pointer;
}
</style>
Copy the code
- vue3
<template>
<transition name="fade" @after-leave="afterLeave" @after-enter="afterEnter">
<div
v-show="visible"
ref="root"
class="notification"
:style="styleObj"
@mouseenter="clearTimer"
@mouseleave="createTimer"
>
<span class="content">{{ content }}</span>
<a class="btn" @click="handleClose">{{ btn }}</a>
</div>
</transition>
</template>
<script lang="ts">
import {
defineComponent,
reactive,
ref,
toRefs,
computed,
onMounted,
onBeforeUnmount,
} from 'vue';
export default defineComponent({
name: 'Notification'.props: {
content: {
type: String.required: true,},duration: {
type: Number.default: 3000,},btn: {
type: String.default: 'off',},verticalOffset: {
type: Number.default: 0,},// eslint-disable-next-line vue/require-default-prop
onClosed: Function,},setup(props) {
const root = ref(null!). ;const state = reactive({
height: 0.visible: false});const styleObj = computed(() = > ({
position: 'fixed'.right: '20px'.bottom: `${props.verticalOffset}px`,}));const timer = ref(0);
const handleClose = (e: MouseEvent): void= > {
e.preventDefault();
state.visible = false;
};
const afterLeave = () = > {
(props as any).onClosed(state.height);
};
const afterEnter = () = > {
state.height = (root as any).value.offsetHeight;
};
const createTimer = () = > {
if (props.duration) {
timer.value = setTimeout(() = > {
state.visible = false;
}, props.duration) as unknown asnumber; }};const clearTimer = () = > {
if (timer.value) {
clearTimeout(timer.value);
timer.value = 0; }}; onMounted(() = > {
state.visible = true;
createTimer();
});
onBeforeUnmount(() = > {
clearTimer();
});
// toRefs changes data created by Reactive to reactive
return{... toRefs(state), root, styleObj, handleClose, afterLeave, afterEnter, clearTimer, createTimer, }; }});</script>
<style scoped>
.notification {
display: inline-flex;
background-color: # 303030;
color: rgba(255.255.255.1);
align-items: center;
padding: 20px;
min-width: 280px;
box-shadow: 0px 3px 5px -1px rgba(0.0.0.0.2),
0px 6px 10px 0px rgba(0.0.0.0.14), 0px 1px 18px 0px rgba(0.0.0.0.12);
flex-wrap: wrap;
transition: all 0.3 s;
}
.content {
padding: 0;
}
.btn {
color: #ff4081;
padding-left: 24px;
margin-left: auto;
cursor: pointer;
}
.fade-enter-active..fade-leave-active {
transition: opacity 0.5 s;
}
.fade-enter..fade-leave-to {
opacity: 0;
}
</style>
Copy the code
function
/** * Creates the call method of the notification plug-in */
import Vue from 'vue'
import Component from './func-notification'
const NotificationConstructor = Vue.extend(Component)
const instances = []
let seed = 1
const removeInstance = instance= > {
if(! instance)return
const len = instances.length
const index = instances.findIndex(inst= > instance.id === inst.id)
instances.splice(index, 1)
if (len < 1) return
const removeHeight = instance.vm.height
for (let i = index; i < len - 1; i++) {
instances[i].verticalOffset = parseInt(instances[i].verticalOffset) - removeHeight - 16}}const notify = (options) = > {
if (Vue.prototype.$isServer) return
const{ autoClose, ... rest } = optionsconst instance = new NotificationConstructor({
propsData: { ...rest },
data: {
autoClose: autoClose === undefined ? 3000 : autoClose
}
})
const id = `notification_${seed++}`
instance.id = id
instance.vm = instance.$mount()
document.body.appendChild(instance.vm.$el)
instance.visible = true
let verticalOffset = 0
instances.forEach(item= > {
verticalOffset += item.$el.offsetHeight + 16
})
verticalOffset += 16
instance.verticalOffset = verticalOffset
instances.push(instance)
instance.vm.$on('closed'.() = > {
removeInstance(instance)
document.body.removeChild(instance.vm.$el)
instance.vm.$destroy()
})
instance.vm.$on('close'.() = > {
instance.vm.visible = false
})
return instance.vm
}
export default notify
Copy the code
- vue3
-
- Vue3 has removed the methods of ON, ON, ON, off and so on. If you want to use it, mitt can be used instead, and another way is adopted here
Import {createApp, ComponentPublicInstance} from 'vue'; / / import {createApp, ComponentPublicInstance} from 'vue'; import Notification from './notification.vue'; export type OptionsType = { content: string; duration? : number; btn: string; }; type InstanceType = { id: string; vm: ComponentPublicInstance<any>; } const instances: InstanceType[] = []; let seed = 1; const removeInstance = (id: string, removeHeight: number): void => { const index = instances.findIndex(item => item.id === id); const len = instances.length; // delete instance instances. Splice (index, 1); if (len < 1) return; for (let i = index; i < len - 1; i++) { const inst = instances[i].vm; inst.bottomOffset = inst.bottomOffset - removeHeight - 16; }}; const notify = (options: OptionsType): void => { const id = `notification_${seed++}`; const container = document.createElement('div'); document.body.appendChild(container); let verticalOffset = 16; instances.forEach(item => { verticalOffset += item.vm.$el.offsetHeight + 16; }); const instance = createApp({ data() { return { bottomOffset: verticalOffset } }, methods: { closedFunc(height: number):void { removeInstance(id, height); document.body.removeChild(container) instance.unmount(); } }, render() { return<Notification { . options } verticalOffset={this.bottomOffset} onClosed={this.closedFunc} />}}); instances.push({ id, vm: instance.mount(container) }); }; export default notify;Copy the code
The end of the
The code is not very elegant, just to deepen the understanding of VUe3, not familiar with it, I hope to help people in need. For now, the vue3 experience is over here, and I will not pay much attention to VUe3 for a while (not needed for my work). In the future, I will switch to PhaserJs research, and I look forward to paying attention to ❤️
Join us at ❤️
Bytedance Xinfoli team
Nice Leader: Senior technical expert, well-known gold digger columnist, founder of Flutter Chinese community, founder of Flutter Chinese community open source project, well-known developer of Github community, author of dio, FLY, dsBridge and other well-known open source projects
We look forward to your joining us to change our lives with technology!!
Recruitment link: job.toutiao.com/s/JHjRX8B