⭐前言
大家好,我是yma16,本文分享关于vue、react组件数据传值对比分析——父组件传递子组件,子组件传递父组件。
react渲染原理
React 是一个基于组件的 JavaScript 库,用于构建用户界面。React 的主要原理是将用户界面抽象为一组嵌套的组件,每个组件都拥有自己的状态和行为。当组件的状态发生改变时,React 会自动重新渲染组件,并将更新后的组件插入到 DOM 树中。
React 的渲染过程主要涉及以下几个步骤:
- 首先,React 会根据 JSX 语法解析出虚拟 DOM(Virtual DOM)对象树,该虚拟 DOM 对象树只是一个 JavaScript 对象,其中包含了组件的状态、属性和子节点等信息。
- 然后,React 通过比较新旧虚拟 DOM 对象树,找出需要更新的部分,只更新需要更新的部分,称之为 “DOM Diff”。
- 接着,React 调用 render 方法生成新的虚拟 DOM 对象树,并将其与旧的虚拟 DOM 对象树进行比较。
- 如果新旧虚拟 DOM 对象树相同,则不进行任何操作。
- 如果新旧虚拟 DOM 对象树不同,则根据差异进行更新,生成新的虚拟 DOM 对象树。
- 最后,React 将更新后的虚拟 DOM 对象树渲染到真实的 DOM 上,完成渲染过程。
React 的渲染过程主要基于虚拟 DOM 和差异化算法。通过虚拟 DOM 对象树的比较,React 能够高效地进行局部更新,提高了应用程序的性能和用户体验。
vue渲染原理
Vue的渲染原理可以大致分为以下几个步骤:
- 解析模板:Vue会将模板字符串解析成抽象语法树(AST),这个过程中会标记出模板中的所有指令、插值语法、事件等信息。这一步由模板编译器完成。
- 根据AST生成渲染函数:Vue会从抽象语法树中生成一个可执行的渲染函数(render function),这个函数可以接收一个参数——渲染上下文(render context),并返回一个VNode节点。
- 渲染函数执行:当组件需要重新渲染时,Vue会执行渲染函数,生成一个新的VNode节点树。
- Diff算法对比新旧VNode:Vue将上一步生成的新VNode节点树和上一次渲染的旧VNode节点树进行对比,通过Diff算法找出需要更新的节点。
- 生成补丁(patch):Diff算法找出需要更新的节点后,会生成一个补丁对象(patch),这个补丁对象描述了要对哪些节点进行何种修改操作。
- 将补丁应用到真实的DOM上:最后,Vue将生成的补丁对象应用到真实的DOM节点上,完成组件的更新。
注:以上是Vue2.x版本的渲染原理,Vue3.x版本的渲染原理有所不同,主要是采用了基于Proxy的响应式数据自动更新机制以及模板编译器与运行时渲染器分离等新特性。
⭐react 组件传值实例
项目截图
💖父组件传值给子组件(props)
App.tsx通过标签内属性传递editInstance给EmailPage.tsx
父组件 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 组件传值实例
项目截图
💖 父组件传递数据给子组件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>
vue3、react组件数据传值对比分析——父组件传递子组件,子组件传递父组件(二)https://developer.aliyun.com/article/1492685