初学vue3的时候倒是扒了一点点源码,似是而非,而且一直做的工作都是很简单的功能,怎么说呢,ui框架也几乎让我很容易的就可以写出一个成型的页面,有时就忘了初学的时候的一些心得。
本内容只说createVNode函数的用法,不做源码探究
<template>
<RenderTest>RenderTest>
template>
<script setup>
import { defineComponent, createVNode } from "vue";
const RenderTest = defineComponent({
render(){
// 参数1:元素名字符串; 参数2:元素属性; 元素子节点,支持createVNode嵌套
return createVNode("div", {class: "text"}, "render-text")
}
});
</script>
虽然在日常中这么写特别鸡肋,甚至有些傻逼,但是别急,慢慢来。
先写一个简单要引用的子组件
<template>
<div>{{title}}div>
template>
<script setup lang="ts" name="SimpleTemplate">
import {defineProps} from "vue";
defineProps<{title: string}>();
script>
父组件
<template>
<SimpleTemplateCopy />
template>
<script setup>
import { defineComponent, createVNode } from "vue";
import SimpleTemplate from "./SimpleTemplate/index.vue";
const SimpleTemplateCopy = defineComponent({
render(){
return createVNode(SimpleTemplate, {title: "twoB"})
}
})
script>
element-plus说实话很不错,但是太常使用它有些审美疲劳,但是它的一些组件确实很好用,特别是那些提示弹窗之类,发现了吗,它们都是在函数去调用的,那么如何在纯js中去使用一个已经封装好了的组件呢?反正我是用它们。
简单的实现一下吧。
手写先写一个confirm的组件
<template>
<div class="small-window">
<div class="upper-tip">
<span>{{ title }}span>
div>
<div class="message-content">
{{ message }}
div>
<div class="button-area">
<div class="my-button" style="background-color: #11a9ea" @click="submitCallBack">{{ submitText }}div>
<div class="my-button" style="background-color: #cecece" @click="cancelCallBack">{{ cancelText }}div>
div>
div>
template>
<script setup lang="ts" name="Confirm">
import {defineProps} from "vue";
defineProps<{
title: {type: string, default: "提示"},
message: string,
submitText: string,
cancelText: string,
cancelCallBack: () => void,
submitCallBack: () => void,
}>()
script>
<style scoped lang="less">
.small-window{
width: 500px;
height: 350px;
background-color: white;
border: 1px solid rgba(169, 169, 169, 0.92);
border-radius: 15px;
display: flex;
flex-direction: column;
.upper-tip{
font-size: 18px;
height: 45px;
width: 100%;
font-weight: 700;
}
.message-content{
width: calc(100% - 40px);
padding: 15px 20px 15px 20px;
height: 150px;
text-indent: 32px;
display: flex;
justify-content: center;
font-size: 16px;
}
.button-area{
display: flex;
justify-content: end;
width: calc(100% - 40px);
padding: 15px 20px 15px 20px;
.my-button{
width: 68px;
margin-left: 20px;
height: 50px;
cursor: pointer;
}
}
}
style>
然后给这个组件找个挂载点以及render到dom上
import {createVNode, render} from "vue";
import Confirm from "./Confirm.vue";
export default ({title, message, submitText, cancelText}) => {
const mask = document.createElement('div');
mask.setAttribute('style', 'position: fixed; background-color: rgba(0, 0, 0, 0.5); width: 100vw; height: 100%; display: flex; justify-content: center; align-items: center');
mask.setAttribute('key', Date.now().toString());
document.body.appendChild(mask);
return new Promise((reslove, reject) => {
// 封装组件属性方法
const submitCallBack = () => {
console.log(456)
//调用完毕后应该清空节点
render(null, mask)
mask.remove();
reslove(true)
}
// 封装组件属性方法
const cancelCallBack= () => {
console.log(456)
//清空节点
render(null, mask);
mask.remove();
reject()
};
// 在此处才创建节点并挂载属性
const VNode = createVNode(Confirm, {
title,
message,
submitText,
cancelText,
cancelCallBack,
submitCallBack
})
render(VNode, mask);
});
};
然后使用它
<template>
<button @click="openMyConfirm">12313213button>
template>
<script setup>
import myConfirm from "./Confirm/index.js"
const openMyConfirm = () => {
myConfirm({title: "提示", message: "你好呀", submitText: "你好", cancelText: "我不好"}).then(() => {
console.log('点击确认事件处理')
}).catch(e => {
console.log('点击取消事件处理')
})
}
script>
贴一个简单的tsx的代码,说实话tsx搭配createVNode简直舒服极了
import {createVNode, defineComponent, render} from "vue";
import styles from "./errordatacord.module.less";
interface A {
unusualField: string,
resultValue: string,
healthRecordNo: string,
name: string,
}
interface Prop extends A{
cancelCallBack: () => void
}
const errorDataCord = defineComponent<Prop>({
name: "ErrorDataCord",
setup(props, {attrs}){
return ()=> <>
<div class={styles["error-data-cord"]}>
<div class={styles["upper-sidebar"]}>
<span>异常结果值</span>
<div
class={styles["close-icon"]}
onClick = { attrs.cancelCallBack as () => void }
>×</div>
</div>
<div class={styles["main-content"]}>
<div>
<span>异常字段:{attrs.unusualField}</span>
<span>异常结果值:{attrs.resultValue}</span>
</div>
<div>
<svg viewBox="0 0 1024 1024" version="1.1"
xmlns="http://www.w3.org/2000/svg" p-id="1486" width="48" height="48">
<path
d="M511.903244 0.000096a511.903244 511.903244 0 0 1 335.957502 898.350359l-9.598786 8.638907A511.999232 511.999232 0 1 1 511.903244 0.000096z m0 742.274102A281.436398 281.436398 0 0 0 272.605515 875.409357a433.289189 433.289189 0 0 0 225.283502 71.510954h14.014227a433.097213 433.097213 0 0 0 239.393717-71.510954 281.628374 281.628374 0 0 0-239.393717-133.135159z m0-665.483816a435.112958 435.112958 0 0 0-299.674091 750.625046 358.322672 358.322672 0 0 1 599.44417 0A435.112958 435.112958 0 0 0 511.903244 76.790382z m0 127.951814A204.838088 204.838088 0 1 1 307.161144 409.484296a204.7421 204.7421 0 0 1 204.7421-204.7421z m0 76.790286a128.047802 128.047802 0 0 0 0 255.999616 128.047802 128.047802 0 0 0 0-255.999616z"
p-id="1487" fill="#cdcdcd"></path>
</svg>
<span>{attrs.name}</span>
<span>{attrs.healthRecordNo}</span>
</div>
</div>
</div>
</>;
}
});
export default (option: A, htmlElement: HTMLElement) => {
return new Promise((resolve) => {
const cancelCallBack = () => {
render(null, htmlElement);
};
const VNode = createVNode(errorDataCord, {
...option,
cancelCallBack
});
render(VNode, htmlElement);
resolve(true);
});
};