vue3、react组件数据传值对比分析——父组件传递子组件,子组件传递父组件

news2024/11/19 3:48:30

文章目录

    • ⭐前言
    • ⭐react 组件传值实例
      • 💖父组件传值给子组件(props)
      • 💖子组件传递事件给父组件props绑定事件
      • 💖父组件触发子组件的事件Ref
    • ⭐vue3 组件传值实例
      • 💖 父组件传递数据给子组件props
      • 💖 子组件传递事件给父组件使用emit
      • 💖 父组件获取子组件实例使用Ref
    • ⭐总结
    • ⭐结束

yma16-logo

⭐前言

大家好,我是yma16,本文分享关于vue、react组件数据传值对比分析——父组件传递子组件,子组件传递父组件。
react渲染原理
React 是一个基于组件的 JavaScript 库,用于构建用户界面。React 的主要原理是将用户界面抽象为一组嵌套的组件,每个组件都拥有自己的状态和行为。当组件的状态发生改变时,React 会自动重新渲染组件,并将更新后的组件插入到 DOM 树中。

React 的渲染过程主要涉及以下几个步骤:

  1. 首先,React 会根据 JSX 语法解析出虚拟 DOM(Virtual DOM)对象树,该虚拟 DOM 对象树只是一个 JavaScript 对象,其中包含了组件的状态、属性和子节点等信息。

  2. 然后,React 通过比较新旧虚拟 DOM 对象树,找出需要更新的部分,只更新需要更新的部分,称之为 “DOM Diff”。

  3. 接着,React 调用 render 方法生成新的虚拟 DOM 对象树,并将其与旧的虚拟 DOM 对象树进行比较。

  4. 如果新旧虚拟 DOM 对象树相同,则不进行任何操作。

  5. 如果新旧虚拟 DOM 对象树不同,则根据差异进行更新,生成新的虚拟 DOM 对象树。

  6. 最后,React 将更新后的虚拟 DOM 对象树渲染到真实的 DOM 上,完成渲染过程。

React 的渲染过程主要基于虚拟 DOM 和差异化算法。通过虚拟 DOM 对象树的比较,React 能够高效地进行局部更新,提高了应用程序的性能和用户体验。
vue渲染原理
Vue的渲染原理可以大致分为以下几个步骤:

  1. 解析模板:Vue会将模板字符串解析成抽象语法树(AST),这个过程中会标记出模板中的所有指令、插值语法、事件等信息。这一步由模板编译器完成。

  2. 根据AST生成渲染函数:Vue会从抽象语法树中生成一个可执行的渲染函数(render function),这个函数可以接收一个参数——渲染上下文(render context),并返回一个VNode节点。

  3. 渲染函数执行:当组件需要重新渲染时,Vue会执行渲染函数,生成一个新的VNode节点树。

  4. Diff算法对比新旧VNode:Vue将上一步生成的新VNode节点树和上一次渲染的旧VNode节点树进行对比,通过Diff算法找出需要更新的节点。

  5. 生成补丁(patch):Diff算法找出需要更新的节点后,会生成一个补丁对象(patch),这个补丁对象描述了要对哪些节点进行何种修改操作。

  6. 将补丁应用到真实的DOM上:最后,Vue将生成的补丁对象应用到真实的DOM节点上,完成组件的更新。

注:以上是Vue2.x版本的渲染原理,Vue3.x版本的渲染原理有所不同,主要是采用了基于Proxy的响应式数据自动更新机制以及模板编译器与运行时渲染器分离等新特性。

⭐react 组件传值实例

项目截图
email-template

💖父组件传值给子组件(props)

App.tsx通过标签内属性传递editInstance给EmailPage.tsx

react-demo
父组件 App.txs
传递一个grapesjs实例到EmailPage

import './App.css';
import 'grapesjs/dist/css/grapes.min.css';
import grapesjs from 'grapesjs';

function App() {

  return (
      <div className="App">
        <EmailPage editInstance={grapesjs} ></EmailPage>
      </div>
  );
}

export default App;

子组件 EmailPage.tsx 解构props
解构接收props的editorInstance

import grapesJSMJML  from '../components/email-edit/index'
import { forwardRef, useEffect, useState,useImperativeHandle } from 'react'

const EmailPage=(props:any)=>{
    const [editor,setEditor]=useState();
    useEffect(()=>{
        const editorInstance:any = props.editInstance
            .init({
                fromElement: true,
                container: '#gjs-email',
                plugins: [grapesJSMJML ],
            });
        try{
            editorInstance.Commands.run('mjml-clear')
        }
        catch (e) {
            console.error('e',e)
        }
        setEditor(editorInstance)
    },[props.editInstance])
    return (
        <div id={'gjs-email'} className={'design-editor'}/>

    )
}

export default EmailPage;

💖子组件传递事件给父组件props绑定事件

同理我们也可以在props传递一个事件给props,在子组价触发即可

💖父组件触发子组件的事件Ref

父组件 App.txs 使用ref获取组件实例

import './App.css';
import 'grapesjs/dist/css/grapes.min.css';
import grapesjs from 'grapesjs';
import { useState,useEffect,useRef } from 'react';

function App() {
  const emailRef:any=useRef();
  useEffect(()=>{console.log(emailRef)},[emailRef])
  return (
      <div className="App">
        <EmailPage editInstance={grapesjs} ></EmailPage>
      </div>
  );
}

export default App;

子组件 EmailPage.txs使用useImperativeHandle 暴露方法和属性
暴露两个方法分别是 getHtml和getBodyContent,最后使用forwardRef抛出组件实例

import grapesJSMJML  from '../components/email-edit/index'
import { forwardRef, useEffect, useState,useImperativeHandle } from 'react'
import zh from "../components/email-edit/locale/zh";


const EmailPage=(props:any,ref:any)=>{
    const [editor,setEditor]=useState();
    const [domRef,setDomRef]=useState();
    useEffect(()=>{
        const editorInstance:any = props.editInstance
            .init({
                fromElement: true,
                container: '#gjs-email',
                plugins: [grapesJSMJML ],
            });
        try{
            editorInstance.Commands.run('mjml-clear')
        }
        catch (e) {
            console.error('e',e)
        }
        setEditor(editorInstance)
    },[props.editInstance])
    const getBodyContent=()=>{
        // @ts-ignore
        const inlineHtml=editor.Commands.run('mjml-code-to-html-inline')
        const matchBody=new RegExp('<body[^>]*>([\\s\\S]+?)<\\/body>','ig');
        const matchBodyText=inlineHtml.match(matchBody)
        // @ts-ignore
        return matchBodyText?matchBodyText[0]:''
    };

    const getHtml=()=>{
        // @ts-ignore
        return editor.Commands.run('mjml-code-to-html-inline')
    }
    useImperativeHandle(ref, () => ({
        getHtml:getHtml,
        getBodyContent:getBodyContent
    }));

    return (
        <div id={'gjs-email'} className={'design-editor'}
             ref={(ref:any)=>{
                 setDomRef(ref)
             }}
        />

    )
}

export default forwardRef(EmailPage);

⭐vue3 组件传值实例

项目截图
vue3-back-front

💖 父组件传递数据给子组件props

子组件 defineProps 定义接受的参数
IframeContent.vue

<template>
  <div class="iframe-container">
    <div class="iframe-content" v-if="!isPage&&!isModel">
      <div style="width: 100%">
        <span> 标题:{{ title }} </span>
        {{kind}}
        <a-button
          @click="jumpPage"
          type="primary"
          style="float: right; margin: 5px"
        >
          跳转</a-button
        >
      </div>
      <iframe :src="url" class="iframe-box"></iframe>
    </div>
    <div class="iframe-content" v-else-if="!isModel">
      <UserTable></UserTable>
    </div>
    <div class="iframe-content" v-else>
      <ChatTable></ChatTable>
    </div>
  </div>
</template>
<script lang="ts" setup>

import {computed} from 'vue'
// @ts-ignore
const props = defineProps<{
  url: string;
  title: string;
  kind: string;
}>();

const isPage=computed(()=>{
  console.log('props',props)
  return props.kind=='page'
})

const isModel=computed(()=>{
  console.log('props',props)
  return props.kind=='model'
})

const emit = defineEmits<{
  (e: "change", id: number): void;
  (e: "update", value: string): void;
}>();
const jumpPage = () => {
  window.open(props.url);
};
</script>

父组件 标签内传递数据给子组件
传递参数给iframe-content组件

<script setup lang="ts">
// @ts-ignore
import IframeContent from "../iframe/IframeContent.vue";
import { reactive} from "vue";
interface contentType {
  url: string;
  title: string;
  kind:string;
}
const contentConfig: contentType = reactive({
  url: "url",
  title: "title",
  kind:'kind'
});

</script>

<template>
  
          <iframe-content
            :url="contentConfig.url"
            :title="contentConfig.title"
            :kind="contentConfig.kind"
          />
</template>

💖 子组件传递事件给父组件使用emit

子组件

<template>
  <div class="iframe-container">
    <div class="iframe-content" v-if="!isPage&&!isModel">
      <div style="width: 100%">
        <span> 标题:{{ title }} </span>
        {{kind}}
        <a-button
          @click="jumpPage"
          type="primary"
          style="float: right; margin: 5px"
        >
          跳转</a-button
        >
      </div>
      <iframe :src="url" class="iframe-box"></iframe>
    </div>
    <div class="iframe-content" v-else-if="!isModel">
      <UserTable></UserTable>
    </div>
    <div class="iframe-content" v-else>
      <ChatTable></ChatTable>
    </div>
  </div>
</template>
<script lang="ts" setup>

import {computed} from 'vue'
// @ts-ignore
const props = defineProps<{
  url: string;
  title: string;
  kind: string;
}>();

const isPage=computed(()=>{
  console.log('props',props)
  return props.kind=='page'
})

const isModel=computed(()=>{
  console.log('props',props)
  return props.kind=='model'
})

const emit = defineEmits<{
  (e: "change", id: number): void;
  (e: "update", value: string): void;
}>();
const jumpPage = () => {
  window.open(props.url);
};
</script>

父组件通过@绑定事件change和update就能接受子组件触发的change和update事件

💖 父组件获取子组件实例使用Ref

通过使用ref绑定formRef去获取校验事件

<template>
  <div class="container">
    <div class="loginUser-container">
      <div class="loginUser-title">管理平台</div>
      <a-form
        :model="state.formState"
        :label-col="state.layoutConfig.labelCol"
        :wrapper-col="state.layoutConfig.wrapperCol"
        :rules="state.formRule"
        ref="formRef"
        layout="vertical"
        autocomplete="off"
      >
        <a-form-item label="账号" name="username">
          <a-input
            v-model:value="state.formState.username"
            allowClear
            placeholder="请输入账号"
            :disabled="state.spinning"
          />
        </a-form-item>

        <a-form-item label="密码" name="password">
          <a-input-password
            v-model:value="state.formState.password"
            :disabled="state.spinning"
            allowClear
            placeholder="请输入密码"
          />
        </a-form-item>

        <a-form-item name="remember" :wrapper-col="state.wrapperCol">
          <a-checkbox
            v-model:checked="state.formState.remember"
            :disabled="state.spinning"
            >记住密码</a-checkbox
          >
        </a-form-item>

        <a-form-item :wrapper-col="state.submitWrapperCol" class="submit-box">
          <a-button
            type="primary"
            html-type="submit"
            @click="loginAction"
            :loading="state.spinning"
            style="width: 100%; font-size: 16px; font-weight: bolder"
            >登录</a-button
          >
        </a-form-item>
      </a-form>
      <div class="description">
        <span class="description-prefix">没账号?</span>
        <span
          @click="jumpRegister"
          class="description-after"
          :disabled="state.spinning"
          >去注册</span
        >
      </div>
    </div>
  </div>
</template>
<script lang="ts" setup>
import { reactive, ref, onMounted } from "vue";
import { useRouter } from "vue-router";
import { useStore } from "vuex";
import { message } from "ant-design-vue";
import { loginUser } from "../../service/user/userApi";

import type { FormInstance } from "ant-design-vue";

interface FormStateType {
  username: string;
  password: string;
  remember: boolean;
}
interface FormRuleType {
  username: Object;
  password: Object;
}
interface stateType {
  formState: FormStateType;
  formRule: FormRuleType;
  layoutConfig: any;
  wrapperCol: any;
  submitWrapperCol: any;
  spinning: boolean;
  backgroundImgUrl: string;
}
// 路由
const router = useRouter();
//store
const store = useStore();
const formRef = ref<FormInstance>();
const state: stateType = reactive({
  formState: {
    username: "",
    password: "",
    remember: false,
  },
  spinning: false,
  formRule: {
    username: [{ required: true, message: "请输入账号!" }],
    password: [{ required: true, message: "请输入密码!" }],
  },
  layoutConfig: {
    labelCol: {
      span: 8,
    },
    wrapperCol: {
      span: 24,
    },
  },
  wrapperCol: { offset: 0, span: 24 },
  submitWrapperCol: { offset: 0, span: 24 },
  backgroundImgUrl:
    "http://www.yongma16.xyz/staticFile/common/img/background.png",
});
/**
 * 初始化表单内容
 */
const initForm = () => {
  const userInfoItem: any = window.localStorage.getItem("userInfo");
  interface userInfoType {
    username: string;
    password: string;
    remember: boolean;
  }
  const userInfo: userInfoType = userInfoItem
    ? JSON.parse(userInfoItem)
    : {
        username: "",
        password: "",
        remember: false,
      };
  if (userInfo.username && userInfo.password) {
    state.formState.username = userInfo.username;
    state.formState.password = userInfo.password;
    state.formState.remember = userInfo.remember;
  }
};
/**
 * 前往注册!
 */
const jumpRegister = () => {
  // 带 hash,结果是 /about#team
  router.push({ path: "/register" });
};

/**
 * 前往home!
 */
const jumpHome = () => {
  // 带 hash,结果是 /about#team
  router.push({ path: "/" });
};
/**
 * 记住密码
 * @param params
 */
const rememberAction = (params: Object) => {
  window.localStorage.setItem("userInfo", JSON.stringify(params));
};
/**
 * 登录
 */
const loginAction = () => {
  if (formRef.value) {
    formRef.value.validate().then(async (res: any) => {
      state.spinning = true;
      const params = {
        username: state.formState.username,
        password: state.formState.password,
      };
      if (state.formState.remember) {
        rememberAction({ ...params, remember: state.formState.remember });
      }
      try {
        console.log('登录',params)
        // @ts-ignore
        await store.dispatch(
          "user/loginUser",
          params
        );
        // 跳转
        setTimeout(() => {
          jumpHome();
        }, 500);
        state.spinning = false;
      } catch (r: any) {
        message.error(JSON.stringify(r));
        state.spinning = false;
        throw Error(r);
      }
    });
  }
};

onMounted(() => {
  //初始化
  initForm();
});
</script>

⭐总结

综合比较react和vue,对于选型我分成两种情况讨论。

  1. vue项目选型——vue3 vben admin
    a. 外包项目(可维护性强,中文文档多,容错率高)
    b. 国内开发者众多的团队,因为vue中文文档比较多
    c. 使用vue较多的团队,适合快速上手
  2. react项目——qiankun
    a. 自研大型项目(个人觉得react项目不好抄袭)
    b. 外企团队,因为react的文档大部分都是英文
    c. 使用react较多的团队,适合快速上手

个人比较看好vue,有以下3点。
a. 国内开发人数众多,搭建可以用中文在社区交流vue3,扩大国内开发影响力
b. vue3的组件传值等我都比react好用,react用的费劲
c. vue的生态良好,版本也一直在迭代更新

⭐结束

本文分享到这结束,如有错误或者不足之处欢迎指出!
cute-animal

👍 点赞,是我创作的动力!
⭐️ 收藏,是我努力的方向!
✏️ 评论,是我进步的财富!
💖 感谢你的阅读!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/905497.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Transformer---ViT:vision transformer

记录一下对transformer方法在计算机视觉任务中的应用方法的理解 参考博客:https://blog.csdn.net/weixin_42392454/article/details/122667271 参考代码:https://gitcode.net/mirrors/Runist/torch_vision_transformer?utm_sourcecsdn_github_accelerator 模型训练流程: imp…

【数据结构与算法】克鲁斯卡尔算法

克鲁斯卡尔算法 介绍 克鲁斯卡尔&#xff08;Kruskal&#xff09;算法是用来求加权连通图的最小生成树的算法。基本思想&#xff1a;按照权值从小到大的顺序选择 n - 1 条边&#xff0c;并保证这 n - 1 条边不构成回路。具体做法&#xff1a;首先构造一个只含 n 个顶点的森林…

文本三剑客sed grep awk

目录 1、sed 1.1、基本用法 1.2、sed脚本格式 1.3、搜索与替换 1.4、变量 2、awk 2.1、基础用法 2.2、常见的内置变量 2.3、模式 2.4、判断 2.5、for计算 2.6、数组 3、grep 1、sed sed 即 Stream EDitor&#xff0c;和 vi 不同&#xff0c;sed是行编辑器 Sed是从…

leetcode刷题之283:移动零

问题 实现思路 首先, 将dest指向-1 位置, cur指向下标为0 的位置, 在cur遍历的过程中: 1) 遇到非零元素则与下标dest1 位置的元素交换, 2) 若遇到零元素则只继续cur遍历. 下标为1 的位置上是 非零元素 执行1) 交换得到右图结果 随后cur 得到下图结果 下标为2 的位置上是零…

day-27 代码随想录算法训练营(19)part03

78.子集 画图分析&#xff1a; 思路&#xff1a;横向遍历&#xff0c;每次遍历的时候都进行一次添加&#xff0c;然后进行纵向递归&#xff0c;递归完之后进行回溯。 注意&#xff1a;空集也是子集。 90.子集|| 分析&#xff1a;和上题一样&#xff0c;区别在于有重复数字 …

LeetCode283.移动零

这道题还是很简单的&#xff0c;我用的是双指针&#xff0c;左指针i从头开始遍历数组&#xff0c;右指针j是从i后面第一个数开始遍历&#xff0c;当左指针i等于0的时候&#xff0c;右指针j去寻找i右边第一个为0的数和i交换位置&#xff0c;交换完了就break内层循环&#xff0c;…

STM8遇坑[EEPROM读取debug不正常release正常][ STVP下载成功单运行不成功][定时器消抖莫名其妙的跑不通流程]

EEPROM读取debug不正常release正常 这个超级无语,研究和半天,突然发现调到release就正常了,表现为写入看起来正常读取不正常,这个无语了,不想研究了 STVP下载不能够成功运行 本文摘录于&#xff1a;https://blog.csdn.net/qlexcel/article/details/71270780只是做学习备份之…

每周AI大事件 百度文心一言上线搜索、文生视频、图表制作等5大插件

每周AI大事件 | 百度文心一言上线搜索、文生视频、图表制作等5大插件 文章目录 一、百度文心一言简介二、百度文心一言五大插件功能详解三、 开启文心一言 体验览卷文档E言易图 &#xff08;貌似不太理想&#xff0c;可能指令姿势不对&#xff09;说图解画&#xff08;貌似不太…

「第2讲」正版PyCharm但是免费,安装教程来了,还有中文插件哦~

大家好&#xff0c;这里是程序员晚枫。 免费的【50讲Python自动化办公】持续更新中&#xff0c;关注我学习吧&#x1f447;想了解更多精彩内容&#xff0c;快来关注程序员晚枫 上一讲&#xff1a;「第1讲」Python的下载、安装和卸载&#xff0c;有手就能学 装完了Python&#…

char *str,char str,char * str和char str的区别

1.char *str是一个指向字符或字符串的指针&#xff0c;总是指向一个字符的起始地址&#xff0c;例如 char *str "Hello"; cout << *str << endl; // 输出&#xff1a;H cout << str << endl; // 输出&#xff1a;Hello str "World…

5.4 webrtc的线程

那今天呢&#xff1f;我们来了解一下webrtc中的threed&#xff0c;首先我们看一下threed的类&#xff0c;它里边儿都含了哪些内容&#xff1f;由于threed的类非常大啊&#xff0c;我们将它分成两部分。 那第一部分呢&#xff0c;是我们看threed的类中都包含了哪些数据之后呢&a…

linux设备驱动:kset、uevent、class

目录 kset&#xff1a;驱动的骨架 kset_create_and_add()函数 设备驱动模型实验2-kobject点灯&#xff08;加入kset&#xff09; kset.c文件 Makefile文件 执行过程 uevent&#xff1a;内核消息的快递包 uevent机制 kobject_uevent()函数 设备驱动模型实验3-kobject点…

AMBA总线协议(3)——AHB(一)

目录 一、前言 二、什么是AHB总线 1、概述 2、一个典型的基于AHB总线的微处理器架构 3、基本的 AHB 传送特性 三、AMBA AHB总线互联 四、小结 一、前言 在之前的文章中我们初步的了解了一下AMBA总线中AHB,APB,AXI的信号线及其功能&#xff0c;从本文开始我们…

NOIP2014普及组复赛 珠心算测验 螺旋矩阵 真题答案

珠心算测验 说明 珠心算是一种通过在脑中模拟算盘变化来完成快速运算的一种计算技术。珠心算训练&#xff0c; 既能够开发智力&#xff0c;又能够为日常生活带来很多便利&#xff0c;因而在很多学校得到普及。 某学校的珠心算老师采用一种快速考察珠心算加法能力的测验方法。他…

wustoj2006后天

#include <stdio.h> int main() {int n;scanf("%d",&n); printf("%d",(n2)%7);return 0;}

星际争霸之小霸王之小蜜蜂(一)--窗口界面设计

目录 前言 一、安装pygame库 1、pygame库简介 2、在windows系统安装pygame库 二 、搭建游戏框架 1、创建游戏窗口 2、改变窗口颜色 总结 前言 大家应该都看过或者都听说过python神书“大蟒蛇”&#xff0c;上面有一个案例是《外星人入侵》&#xff0c;游戏介绍让我想起了上…

上位机系统(系统的架构、串口的使用、协议的定义、开发环境的配置)

上位机系统 1. 系统架构 实机拓扑架构 硬件支持 使用 VSPD 6.9 实现&#xff1a; 效果图 当状态值超过警戒值&#xff0c;就会变成红色&#xff0c;同时在界面的上方显示红色的“设备告警” 3. 串口电气特性 波特率&#xff1a;19200 数据位数&#xff1a;8 位 u 奇偶校验&…

shell脚本之函数

shell函数 函数的组成&#xff1a;函数名和函数体 函数的格式 function 函数名 { 命令序列 } function cat {cat /etc/passwd}函数名() { 命令序列 } cat () {cat /etc/passwd}function 函数名 (){ 命令序列 } function cat() {cat /etc/passwd}函数相关命令 declare -F #查…

记录每日LeetCode 2236. 判断根结点是否等于子结点之和 Java实现

题目描述&#xff1a; 给你一个 二叉树 的根结点 root&#xff0c;该二叉树由恰好 3 个结点组成&#xff1a;根结点、左子结点和右子结点。 如果根结点值等于两个子结点值之和&#xff0c;返回 true &#xff0c;否则返回 false 。 初始代码&#xff1a; /*** Definition f…