函数式组件

CaoJianbang / 2024-11-05 / 原文

函数式组件:通过函数调用的方式添加组件。

传统调用组件方式的困扰:

<Child v-if='isShow' :msg='msg'/>
<botton @click='btnClick' />

import Child from '@/components/Child'

let isShow = ref(false);
let msg = ref('111');
let btnClick = ()=>{
     isShow.value = true;
     msg.value = 222;     
...
isShow.value = false; }
1,这种非马上就要渲染的组件,需要大量代码去控制他,比如需要申明是否添加组件的变量,传递的属性变量==
2,其他地方也要这么写,让人头皮发麻

 

 函数式调用组件:

let btnClick = ()=>{
     createChildComponent("222");  
}

//调用函数,就会自动添加组件,参数通过函数传递。
//调用方便,简单粗暴。

 

函数式组件的实现:toast举例

<!--Toast.vue  组件文件的创建是一样的-->
<template>
    <div class="page">
        <div class="toast">{{msg}}</div>
    </div>
</template>

<script setup>
import { ref,defineProps } from 'vue';

let {msg} = defineProps({
    msg:{
        default:"请求成功!"
    }
});
</script>

<style lang='scss' scoped>
.page{
    position: fixed;
    width: 100vw;
    height: 100vh;

    .toast{
        background-color: rgba(0,0,0,0.6);
        margin-bottom: 10px;
        border-radius: 5px;
        width: 160px;
        height: 30px;
        display: flex;
        align-items: center;
        justify-content: center;
        font-size: 20px;
        color:#eeeeee;
    }
}

</style>
<!--Toast.js  里面导出创建上面toast组件的函数-->
import { createApp } from "vue";
import Toast from "@/components/Toast.vue";

//dom要挂载到的dom
export default function createToastComponent(msg) {
    //createApp创建组件实例
    let instance = createApp(Toast,{msg});

    let mountNode = document.createElement('div');
    document.body.appendChild(div);
    //将组件挂载相应的dom上
    instance.mount(mountNode);

    setTimeout(()=>{
        //卸载组件
        instance.unmount();
        //卸载组件后,把渲染的html一并清除
        document.body.removeChild(mountNode);  
    },1000);

}


<!--Parent.vue调用-->
import createToastComponent from '@components/Toast.js'

let btnClick()=>{
     createToastComponent('222');
}

说明:

1,CreateApp(component,prop)返回一个组件实例,第一个参数是个组件,第二个参数是要传递的属性

  比如文件main.js  就有下面两句

  import App from './App.vue'
       let app = createApp(App);
2,mount 将组件实例挂载到一个dom上,参数是一个dom元素(比如createElement,或queryElement的元素)或类选择器,
      mount挂载到dom上,会导致dom里面的所有内容全部清空。这就是上面的例子中为什么新建一个dom来挂载mount的原因。
      mount挂载dom必须已经渲染出来,main.js 中的app.mount('#app');
     生成的index.html入口是下面这样的,通过defer加载js文件,保证下面的<div id='app'></div>先渲染出来
<!DOCTYPE html>
<html lang="">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <link rel="icon" href="/favicon.ico">
    <title>screen</title>
  <script defer src="/js/chunk-vendors.js"></script><script defer src="/js/app.js"></script></head>
  <body>
    <noscript>
      <strong>We're sorry but screen doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <div id="app"></div>
    <!-- built files will be auto injected -->
  </body>
</html>

3,unmount卸载组件函数

 

 

render+h实现:

import Toast from "@/components/mobile/Toast.vue";
export function createToast(msg,mountNode){
  //必须加上key,否则这个函数第二次调用的时候没效果,因为已经有相同key的元素了。改变了key,相当于注销了之前的元素,然后进行重新绘制了。 let vnode = h(Toast,{msg,key:new Date()}); render(vnode,mountNode); }

h函数,createVnode函数的缩写,创建虚拟dom,返回的是一个虚拟dom,并且不能通过这个虚拟dom获取组件的高级特征(比如组件difineExpose==)

h(元素/组件,prop参数/事件,child),详情可见官网。

render绘制函数

render(虚拟节点,父元素)  //会绘制到父元素内的最后面(不会覆盖父元素的内容)

 

使用h+render做的函数式组件优缺点:

1,缺点:只能做简单的组件的绘制,而且组件已经转成了虚拟dom,然后将虚拟dom绘制出来,丧失了组件的大部分特征。

2,有点:不需要重新额外添加容器元素。