給自己企業(yè)怎么做網(wǎng)站永州網(wǎng)絡(luò)推廣
文章目錄
- ?前言
- ?react 組件傳值實例
- 💖父組件傳值給子組件(props)
- 💖子組件傳遞事件給父組件props綁定事件
- 💖父組件觸發(fā)子組件的事件Ref
- ?vue3 組件傳值實例
- 💖 父組件傳遞數(shù)據(jù)給子組件props
- 💖 子組件傳遞事件給父組件使用emit
- 💖 父組件獲取子組件實例使用Ref
- ?總結(jié)
- ?結(jié)束

?前言
大家好,我是yma16,本文分享關(guān)于vue、react組件數(shù)據(jù)傳值對比分析——父組件傳遞子組件,子組件傳遞父組件。
react渲染原理
React 是一個基于組件的 JavaScript 庫,用于構(gòu)建用戶界面。React 的主要原理是將用戶界面抽象為一組嵌套的組件,每個組件都擁有自己的狀態(tài)和行為。當(dāng)組件的狀態(tài)發(fā)生改變時,React 會自動重新渲染組件,并將更新后的組件插入到 DOM 樹中。
React 的渲染過程主要涉及以下幾個步驟:
-
首先,React 會根據(jù) JSX 語法解析出虛擬 DOM(Virtual DOM)對象樹,該虛擬 DOM 對象樹只是一個 JavaScript 對象,其中包含了組件的狀態(tài)、屬性和子節(jié)點等信息。
-
然后,React 通過比較新舊虛擬 DOM 對象樹,找出需要更新的部分,只更新需要更新的部分,稱之為 “DOM Diff”。
-
接著,React 調(diào)用 render 方法生成新的虛擬 DOM 對象樹,并將其與舊的虛擬 DOM 對象樹進(jìn)行比較。
-
如果新舊虛擬 DOM 對象樹相同,則不進(jìn)行任何操作。
-
如果新舊虛擬 DOM 對象樹不同,則根據(jù)差異進(jìn)行更新,生成新的虛擬 DOM 對象樹。
-
最后,React 將更新后的虛擬 DOM 對象樹渲染到真實的 DOM 上,完成渲染過程。
React 的渲染過程主要基于虛擬 DOM 和差異化算法。通過虛擬 DOM 對象樹的比較,React 能夠高效地進(jìn)行局部更新,提高了應(yīng)用程序的性能和用戶體驗。
vue渲染原理
Vue的渲染原理可以大致分為以下幾個步驟:
-
解析模板:Vue會將模板字符串解析成抽象語法樹(AST),這個過程中會標(biāo)記出模板中的所有指令、插值語法、事件等信息。這一步由模板編譯器完成。
-
根據(jù)AST生成渲染函數(shù):Vue會從抽象語法樹中生成一個可執(zhí)行的渲染函數(shù)(render function),這個函數(shù)可以接收一個參數(shù)——渲染上下文(render context),并返回一個VNode節(jié)點。
-
渲染函數(shù)執(zhí)行:當(dāng)組件需要重新渲染時,Vue會執(zhí)行渲染函數(shù),生成一個新的VNode節(jié)點樹。
-
Diff算法對比新舊VNode:Vue將上一步生成的新VNode節(jié)點樹和上一次渲染的舊VNode節(jié)點樹進(jìn)行對比,通過Diff算法找出需要更新的節(jié)點。
-
生成補(bǔ)丁(patch):Diff算法找出需要更新的節(jié)點后,會生成一個補(bǔ)丁對象(patch),這個補(bǔ)丁對象描述了要對哪些節(jié)點進(jìn)行何種修改操作。
-
將補(bǔ)丁應(yīng)用到真實的DOM上:最后,Vue將生成的補(bǔ)丁對象應(yīng)用到真實的DOM節(jié)點上,完成組件的更新。
注:以上是Vue2.x版本的渲染原理,Vue3.x版本的渲染原理有所不同,主要是采用了基于Proxy的響應(yīng)式數(shù)據(jù)自動更新機(jī)制以及模板編譯器與運(yùn)行時渲染器分離等新特性。
?react 組件傳值實例
項目截圖
💖父組件傳值給子組件(props)
App.tsx通過標(biāo)簽內(nèi)屬性傳遞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 解構(gòu)props
解構(gòu)接收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,在子組價觸發(fā)即可
💖父組件觸發(fā)子組件的事件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-ignoreconst inlineHtml=editor.Commands.run('mjml-code-to-html-inline')const matchBody=new RegExp('<body[^>]*>([\\s\\S]+?)<\\/body>','ig');const matchBodyText=inlineHtml.match(matchBody)// @ts-ignorereturn matchBodyText?matchBodyText[0]:''};const getHtml=()=>{// @ts-ignorereturn 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 組件傳值實例
項目截圖
💖 父組件傳遞數(shù)據(jù)給子組件props
子組件 defineProps 定義接受的參數(shù)
IframeContent.vue
<template><div class="iframe-container"><div class="iframe-content" v-if="!isPage&&!isModel"><div style="width: 100%"><span> 標(biāo)題:{{ title }} </span>{{kind}}<a-button@click="jumpPage"type="primary"style="float: right; margin: 5px">跳轉(zhuǎn)</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>
父組件 標(biāo)簽內(nèi)傳遞數(shù)據(jù)給子組件
傳遞參數(shù)給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> 標(biāo)題:{{ title }} </span>{{kind}}<a-button@click="jumpPage"type="primary"style="float: right; margin: 5px">跳轉(zhuǎn)</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就能接受子組件觸發(fā)的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-inputv-model:value="state.formState.username"allowClearplaceholder="請輸入賬號":disabled="state.spinning"/></a-form-item><a-form-item label="密碼" name="password"><a-input-passwordv-model:value="state.formState.password":disabled="state.spinning"allowClearplaceholder="請輸入密碼"/></a-form-item><a-form-item name="remember" :wrapper-col="state.wrapperCol"><a-checkboxv-model:checked="state.formState.remember":disabled="state.spinning">記住密碼</a-checkbox></a-form-item><a-form-item :wrapper-col="state.submitWrapperCol" class="submit-box"><a-buttontype="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",
});
/*** 初始化表單內(nèi)容*/
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,結(jié)果是 /about#teamrouter.push({ path: "/register" });
};/*** 前往home!*/
const jumpHome = () => {// 帶 hash,結(jié)果是 /about#teamrouter.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-ignoreawait store.dispatch("user/loginUser",params);// 跳轉(zhuǎn)setTimeout(() => {jumpHome();}, 500);state.spinning = false;} catch (r: any) {message.error(JSON.stringify(r));state.spinning = false;throw Error(r);}});}
};onMounted(() => {//初始化initForm();
});
</script>
?總結(jié)
綜合比較react和vue,對于選型我分成兩種情況討論。
- vue項目選型——vue3 vben admin
a. 外包項目(可維護(hù)性強(qiáng),中文文檔多,容錯率高)
b. 國內(nèi)開發(fā)者眾多的團(tuán)隊,因為vue中文文檔比較多
c. 使用vue較多的團(tuán)隊,適合快速上手 - react項目——qiankun
a. 自研大型項目(個人覺得react項目不好抄襲)
b. 外企團(tuán)隊,因為react的文檔大部分都是英文
c. 使用react較多的團(tuán)隊,適合快速上手
個人比較看好vue,有以下3點。
a. 國內(nèi)開發(fā)人數(shù)眾多,搭建可以用中文在社區(qū)交流vue3,擴(kuò)大國內(nèi)開發(fā)影響力
b. vue3的組件傳值等我都比react好用,react用的費(fèi)勁
c. vue的生態(tài)良好,版本也一直在迭代更新
?結(jié)束
本文分享到這結(jié)束,如有錯誤或者不足之處歡迎指出!
👍 點贊,是我創(chuàng)作的動力!
?? 收藏,是我努力的方向!
?? 評論,是我進(jìn)步的財富!
💖 感謝你的閱讀!