一、核心区别
1、区别总览
特性 | Vue 模板 | React JSX/TSX |
本质 | HTML 扩展,有专属指令v-if/v-for | JavaScript 扩展,没有指令,全用原生 JS |
动态值 | {{ }} | { } |
逻辑写法 | 模板写指令,逻辑写 script | 模板里直接写 JS 逻辑 |
绑定属性 | :attr | attr={ } |
事件 | @event | onEvent |
循环 | v-for | 数组.map() |
条件 | v-if | && / 三元表达式 |
2、基础语法详解
(1)文本插值
<span>{{ message }}</span><span>{message}</span>所有动态内容必须用 {} 包起来 字符串、数字、变量、表达式都能放 不能写 if/for(这是语句,不是表达式)
(2)属性绑定
<img :src="imgUrl" /><div :class="className"></div>
<imgsrc={imgUrl} /><divclassName={className}></div>
class → className JSX 里 class 是关键字,不能用 for → htmlFor <label for="name"> → <label htmlFor="name">
所有属性都用小驼峰 maxlength → maxLength autofocus → autoFocus
(3)动态class 绑定
<div :class="{ active: isActive }"></div><div :class="[baseClass, activeClass]"></div>
//方式 1:三元表达式(最常用)<div className={isActive ? 'active' : ''}></div>//方式 2:数组拼接<divclassName={[baseClass,isActive && active].filter(Boolean).join(' ')}></div>//方式 3:推荐库 classnames(企业级)import classNames from 'classnames'<div className={classNames('base', { active: isActive })}></div>
(4)动态style 绑定
<div :style="{ color: textColor, fontSize: '14px' }"></div><divstyle={{color:textColor, fontSize: '14px' }}></div>外层 {} 表示 “这里是 JS” 内层 {} 是样式对象 CSS 属性用小驼峰
字符串必须加引号
3、条件渲染
<divv-if="score >= 90">优秀</div><divv-else-if="score >= 60">及格</div><divv-else>不及格</div>
//写法 1:三元表达式(支持多条件)<div>{score >= 90 ? '优秀' : score >= 60 ? '及格' : '不及格'}</div>//写法 2:&& 逻辑(只做显示 / 隐藏){isShow && <div>我会显示</div>}
true && 组件 → 返回组件 false && 组件 → 返回 false,不渲染
{ if (isShow) <div/> } // 错误!JSX 里不能直接写 if 语句function getContent() {if (score >= 90) return <div>优秀</div>if (score >= 60) return <div>及格</div>return <div>不及格</div>}<div>{getContent()}</div>
4、列表循环
<ul><liv-for="item in list":key="item.id">{{ item.name }}</li></ul>
<ul>{list.map((item) => (<likey={item.id}>{item.name}</li>))}</ul>
循环必须用 map,不能用 for 语句 必须加 key,放在最顶层元素上 map 必须有返回值(用括号 () 包裹 JSX)
{list.map((item, index) => (<likey={index}>{item.name}</li>))}
5、事件绑定
(1)语法示例
<button @click="handleClick">点击</button><input @input="handleInput" />
<buttononClick={handleClick}>点击</button><inputonInput={handleInput} />
(2)事件名对照表
Vue | React |
@click | onClick |
@input | onInput |
@change | onChange |
@submit | onSubmit |
@keyup | onKeyUp |
@mousedown | onMouseDown |
(3)传参方式
<button @click="handleClick(id)">点击</button><buttononClick={() => handleClick(id)}>点击</button>不能直接写 onClick={handleClick(id)} 会自动执行,不是绑定事件!
6、v-model 双向绑定 → React 受控组件
<inputv-model="text" />const [text, setText] = useState('')<input value={text} onChange={(e) => setText(e.target.value)} />
value={text}:把状态绑定到输入框 onChange:输入时更新状态 React 没有双向绑定,是单向数据流
<inputtype="checkbox"checked={isChecked}onChange={(e) => setIsChecked(e.target.checked)}/>
7、v-show 显示隐藏
<divv-show="isShow">内容</div><div style={{ display: isShow ? 'block' : 'none' }}>内容</div>v-if:销毁 / 重建 v-show / React 版:display 控制显示隐藏
8、v-html → 危险 HTML
<divv-html="html"></div><divdangerouslySetInnerHTML={{__html:html }} />9、计算属性→ React useMemo
computed: {fullName() {return this.firstName + ' ' + this.lastName}}
import { useMemo } from 'react'const fullName = useMemo(() => {return firstName + ' ' + lastName}, [firstName, lastName])
const fullName = firstName + ' ' + lastName10、侦听watch → React useEffect
watch(count, (newVal) => {console.log('count 变了', newVal)})
useEffect(() => {console.log('count 变了', count)}, [count])
[count] → 只监听 count [] → 只执行一次(如 mounted) 不写依赖→ 每次更新都执行
11、插槽Slot → React children / Props
(1)默认插槽
<Card>这里是内容</Card><!-- 子组件 --><div><slot /></div>
<Card>这里是内容</Card>// 子组件function Card({ children }) {return <div>{children}</div>}
(2)具名插槽
<Card><template #header>标题</template><template #default>内容</template></Card>
<Card header={<div>标题</div>}>内容</Card>// 子组件function Card({ header, children }) {return (<div><divclassName="header">{header}</div><divclassName="body">{children}</div></div>)}
12、生命周期完整对照
阶段 | Vue3 | React |
挂载完成 | onMounted | useEffect(() => {}, []) |
更新 | watch | useEffect(() => {}) |
卸载 | onUnmounted | useEffect(() => { return () => {} }) |
二、避坑与模版示例
1、所有变量/ 表达式必须用 {} 包裹
<span>message</span><span>{message}</span>2、class 必须改成 className
<divclass="box"></div><divclassName="box"></div>3、for 必须改成 htmlFor
<labelfor="name">姓名</label><labelhtmlFor="name">姓名</label>4、行内样式必须写style={{ }} 双层大括号
<divstyle="color:red"></div><divstyle={color:red}></div>
<div style={{ color: 'red', fontSize: '14px' }}></div>5、循环必须用map + 必须加 key
<li v-for="item in list"></li>{for (let i=0;i<list.length;i++){}}
{list.map(item => (<likey={item.id}>{item.text}</li>))}
6、事件名必须是小驼峰(onXxx)
<button @click="fn"onclick="fn">点击</button><buttononClick={fn}>点击</button>7、事件传参必须用箭头函数包裹
<buttononClick={fn(id)}>点击</button><buttononClick={() => fn(id)}>点击</button>8、JSX 注释必须写 {/* 注释内容 */}
// 注释<!-- 注释 -->
{/* 这是 JSX 里唯一正确的注释写法 */}9、组件必须返回唯一根节点(用<> 空标签)
return (<div>1</div><div>2</div>)
return (<><div>1</div><div>2</div></>)
10、if / for 语句不能直接写在 JSX 里
{if (isShow) <div></div>}{for (let x in list) {}}
// 条件:&& / 三元 / 外部函数{isShow && <div></div>}// 循环:map{list.map(...)}
11、模版示例
<template><div><inputv-model="text"placeholder="输入内容" /><button @click="addItem">添加</button><ul><liv-for="item in list":key="item.id":class="{ active: item.active }">{{ item.text }}</li></ul></div></template><scriptsetup>import { ref } from 'vue'const text = ref('')const list = ref([{ id: 1, text: '测试', active: false }])const addItem = () => {list.value.push({id: Date.now(),text: text.value,active: false})text.value = ''}</script>
import { useState } from 'react'export default function Demo() {const [text, setText] = useState('')const [list, setList] = useState([{ id: 1, text: '测试', active: false }])const addItem = () => {setList([...list,{ id: Date.now(), text, active: false }])setText('')}return (<div><inputvalue={text}onChange={(e) => setText(e.target.value)}placeholder="输入内容"/><buttononClick={addItem}>添加</button><ul>{list.map((item) => (<likey={item.id}className={item.active ? 'active' : ''}>{item.text}</li>))}</ul></div>)}
夜雨聆风