提要
要動態掛載一個Vue組件Component,就必須實例化一個Vue對象,指定渲染函數和掛載位置。
而不能直接向document寫入html代碼的方式插入DOM,這不會觸發Vue框架的渲染。
和掛載App一樣:
import Vue from 'vue';
import App from './App.vue';
new Vue({
render: h => h(App);
}).$mount('#app');
其中h是function (createElement, context)
中的createElement
。render其實應該這樣寫:
render: function (createElement, context) {
return createElement(App);
}
將 h 作為 createElement 的別名是 Vue 生態系統中的一個通用慣例,實際上也是 JSX 所要求的。
文檔:https://cn.vuejs.org/v2/guide/render-function.html
函數式使用組件
以下文件實現了一個全屏的提示框組件,要彈出這個提示框,可以往頁面某個占位div上掛載:
new Vue({
render: h => h(FrameAlert),
}).$mount($(document.body)[0])
等等,參數、事件怎么傳遞呢?
組件需要的一切都是通過 context 參數傳遞,那么如果我們傳遞給createElement的第一個參數是一個單文件導出的組件對象,
可以直接將 context 作為第二個參數,會成功到達該單文件組件的render函數!
context是一個包括如下字段的對象:
props:提供所有 prop 的對象
children: VNode 子節點的數組
slots: 一個函數,返回了包含所有插槽的對象
scopedSlots: (2.6.0+) 一個暴露傳入的作用域插槽的對象。也以函數形式暴露普通插槽。
data:傳遞給組件的整個數據對象,作為 createElement 的第二個參數傳入組件
parent:對父組件的引用
listeners: (2.3.0+) 一個包含了所有父組件為當前組件注冊的事件監聽器的對象。這是 data.on 的一個別名。
injections: (2.3.0+) 如果使用了 inject 選項,則該對象包含了應當被注入的屬性。
划重點!以下字段你很難在文檔中找到,但卻經常需要使用
on:事件
domProps:dom參數
所以,我們這樣傳遞參數和事件:
new Vue({
render: h => h(FrameAlert, {
props: { msg: '提示' },
on: { click(event) { console.log(event.target.innerText); }, },
}),
}).$mount($(document.body)[0])
文件:FrameAlert.vue
<template>
<div class="frame_alert">
<div class="alert">
<div class="msg">
<div>
<span v-html="filtedMsg"></span>
</div>
</div>
<div class="buttons">
<hr>
<div class="btnwrapper">
<span @click=handle class="button" href="#">確定</span><span @click=handle class="button" href="#">取消</span>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
msg: {
type: String,
default: "Message in here...",
},
},
data() {
return {
};
},
computed: {
filtedMsg() {
return this.filteMsg(this.msg);
},
},
methods: {
handle(event) {
this.$emit('click', event);
},
filteMsg(msg) {
return msg.replace(/</g, '<').replace(/>/g, '>').replace(/(?:\n|\\n)/g, '<br>');
}
},
mounted() {
},
}
</script>
<style scoped>
.frame_alert {
position: fixed;
top: 0;
width: 100%;
height: 100%;
padding: 0;
margin: 0;
background: rgba(0, 0, 0, 0.8);
}
.alert {
/* 外觀 */
width: fit-content;
min-width: 300px;
max-width: 60%;
border-radius: 15px;
background: white;
color: black;
word-break: break-all;
/* 定位 */
position: absolute;
top: 40%;
left: 50%;
transform: translate(-50%, -50%);
}
.msg {
max-height: 200px;
margin: 20px;
margin-bottom: 80px;
overflow: auto;
text-align: center;
font-size: 22px;
}
.msg::-webkit-scrollbar {
/*隱藏滾輪*/
display: none;
}
.buttons {
position: fixed;
margin: 0;
bottom: 0;
width: 100%;
height: 60px;
margin: auto;
border-radius: 0 0 15px 15px;
}
.btnwrapper {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 80%;
margin-top: 2px;
}
.button {
text-align: center;
font-size: 22px;
display: inline-block;
width: 50%;
margin: 0;
padding: 0;
color: blue;
text-decoration: none;
cursor: pointer;
user-select: none;
}
.button:active {
text-shadow: 1px 1px 10px;
}
</style>