React & Vue
React 起源于 Facebook 的內(nèi)部項(xiàng)目偷线,因?yàn)樵摴緦?duì)市場(chǎng)上所有 [JavaScript MVC 框架],都不滿意沽甥,就決定自己寫一套声邦,用來(lái)架設(shè) [Instagram]的網(wǎng)站。做出來(lái)以后摆舟,發(fā)現(xiàn)這套東西很好用亥曹,就在2013年5月[開源]了。
React 與 Vue 的對(duì)比
技術(shù)層面
- Vue 生產(chǎn)力更高(更少的代碼實(shí)現(xiàn)更強(qiáng)勁的功能)
- React 更 hack 技術(shù)占比比較重
- 兩個(gè)框架的效率都采用了虛擬 DOM
- 性能都差不多
- 組件化
- Vue 支持
- React 支持
- 數(shù)據(jù)綁定
- 都支持?jǐn)?shù)據(jù)驅(qū)動(dòng)視圖
- Vue 支持表單控件雙向數(shù)據(jù)綁定
- React 不支持雙向數(shù)據(jù)綁定
- 它們的核心庫(kù)都很小恨诱,都是漸進(jìn)式 JavaScript 庫(kù)
- React 采用 JSX 語(yǔ)法來(lái)編寫組件
- Vue 采用單文件組件
- template
- script
- style
開發(fā)團(tuán)隊(duì)
- React 由 Facebook 前端維護(hù)開發(fā)
- Vue
- 早期只有尤雨溪一個(gè)人
- 由于后來(lái)使用者越來(lái)越多媳瞪,后來(lái)離職專職開發(fā)維護(hù)
- 目前也有一個(gè)小團(tuán)隊(duì)在開發(fā)維護(hù)
社區(qū)
- React 社區(qū)比 Vue 更強(qiáng)大
- Vue 社區(qū)也很強(qiáng)大
Native APP 開發(fā) - React Native
- 可以原生應(yīng)用
- React 結(jié)束之后會(huì)學(xué)習(xí)
- Weex
- 阿里巴巴內(nèi)部搞出來(lái)的一個(gè)東西,基于 Vue
babel 自動(dòng)編譯執(zhí)行:
<!--
當(dāng) babel-standalone 發(fā)現(xiàn) type="text/babel" 類型標(biāo)簽的時(shí)候:
1. 將 script 標(biāo)簽中的內(nèi)容轉(zhuǎn)換為瀏覽器可以識(shí)別的 JavaScript
2. 使用 eval 執(zhí)行編譯結(jié)果代碼
-->
<script type="text/babel">
const getMessage = () => "Hello World";
console.log(getMessage())
</script>
初始化及安裝依賴
$ mkdir react-demos
$ cd react-demos
$ npm init --yes
$ npm install --save react react-dom @babel/standalone
Hello World
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>demo - Hello World</title>
<script src="node_modules/@babel/standalone/babel.js"></script>
<script src="node_modules/react/umd/react.development.js"></script>
<script src="node_modules/react-dom/umd/react-dom.development.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
ReactDOM.render(
<h1>Hello, react!</h1>,
document.getElementById('root')
)
</script>
</body>
</html>
JSX基本語(yǔ)法規(guī)則
必須只能有一個(gè)根節(jié)點(diǎn)
多標(biāo)簽寫到包到一個(gè)小括號(hào)( )中胡野,防止 JavaScript 自動(dòng)分號(hào)不往后執(zhí)行的問(wèn)題材失。
-
遇到 HTML 標(biāo)簽 (以 < 開頭) 就用 HTML 規(guī)則解析
- 單標(biāo)簽不能省略結(jié)束標(biāo)簽。
遇到代碼塊(以 { 開頭)硫豆,就用 JavaScript 規(guī)則解析
-
JSX 允許直接在模板中插入一個(gè) JavaScript 變量
- 如果這個(gè)變量是一個(gè)數(shù)組龙巨,則會(huì)展開這個(gè)數(shù)組的所有成員添加到模板中
單標(biāo)簽必須結(jié)束 />
基本語(yǔ)法:
const element = <h1>Hello, world!</h1>;
在 JSX 中嵌入 JavaScript 表達(dá)式
- 語(yǔ)法
- 如果 JSX 寫到了多行中,則建議包裝括號(hào)避免自動(dòng)分號(hào)的陷阱
function formatName(user) {
return user.firstName + ' ' + user.lastName;
}
const user = {
firstName: 'Harper',
lastName: 'Perez'
};
const element = (
<h1>
Hello, {formatName(user)}!
</h1>
);
ReactDOM.render(
element,
document.getElementById('root')
);
const user = {
name: '張三',
age: 18,
gender: 0
}
const element = (
<div>
<p>姓名:{user.name}</p>
<p>年齡:{user.age}</p>
<p>性別:{user.gender === 0 ? '男' : '女'}</p>
</div>
)
在 JavaScript 表達(dá)式中嵌入 JSX
function getGreeting (user) {
if (user) {
return <h1>Hello, {user.name}</h1>
}
return <h1>Hello, Stranger.</h1>
}
const user = {
name: 'Jack'
}
const element = getGreeting(user)
ReactDOM.render(
element,
document.getElementById('root')
)
JSX 中的節(jié)點(diǎn)屬性
- 動(dòng)態(tài)綁定屬性值
- class 使用 className
- tabindex 使用 tabIndex
- for 使用 htmlFor
普通的屬性:
const element = <div tabIndex="0"></div>;
在屬性中使用表達(dá)式:
const element = <img src={user.avatarUrl}></img>;
聲明子節(jié)點(diǎn)
- 必須有且只有一個(gè)根節(jié)點(diǎn)
如果標(biāo)簽是空的熊响,可以使用 /> 立即關(guān)閉它旨别。
const element = <img src={user.avatarUrl} />;
JSX 子節(jié)點(diǎn)可以包含子節(jié)點(diǎn)(最好加上小括號(hào),防止自動(dòng)分號(hào)的問(wèn)題):
const element = (
<div>
<h1>Hello!</h1>
<h2>Good to see you here.</h2>
</div>
);
JSX 自動(dòng)阻止注入攻擊
原樣輸出:
const element = <div>{'<h1>this is safe</h1>'}</div>
輸出 html:
function createMarkup() {
return {__html: 'First · Second'};
}
function MyComponent() {
return <div dangerouslySetInnerHTML={createMarkup()} />;
}
在 JSX 中使用注釋
在 JavaScript 中的注釋還是以前的方式:
// 單行注釋
/*
* 多行注釋
*/
在 jsx 的標(biāo)簽中寫注釋需要注意:
寫法一(不推薦):
{
// 注釋
// ...
}
寫法二(推薦汗茄,把多行寫到單行中):
{/* 單行注釋 */}
寫法三(多行):
{
/*
* 多行注釋
*/
}
JSX 原理
Babel 會(huì)把 JSX 編譯為 React.createElement() 函數(shù)秸弛。
下面兩種方式是等價(jià)的:
const element = (
<h1 className="greeting">
Hello, world!
</h1>
);
const element = React.createElement(
'h1',
{className: 'greeting'},
'Hello, world!'
);
// Note: this structure is simplified
const element = {
type: 'h1',
props: {
className: 'greeting',
children: 'Hello, world'
}
};
Class 和 Style
<div className="before" title="stuff" />
<div style={{color: 'red', fontWeight: 'bold'}} />
組件
React 允許將代碼封裝成組件(component),然后像插入普通 HTML 標(biāo)簽一樣洪碳,在網(wǎng)頁(yè)中插入這個(gè)組件递览。
組件規(guī)則注意事項(xiàng)
組件類的第一個(gè)首字母必須大寫
組件類必須有
render
方法組件類必須有且只有一個(gè)根節(jié)點(diǎn)
-
組件屬性可以在組件的
props
獲取函數(shù)需要聲明參數(shù):
props
類直接通過(guò)
this.props
函數(shù)式組件(無(wú)狀態(tài))
-
名字不能用小寫
React 在解析的時(shí)候,是以標(biāo)簽的首字母來(lái)區(qū)分的
如果首字母是小寫則當(dāng)作 HTML 來(lái)解析
如果首字母是大小則當(dāng)作組件來(lái)解析
結(jié)論:組件首字母必須大寫
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
const element = <Welcome name="Sara" />;
ReactDOM.render(
element,
document.getElementById('root')
);
組件構(gòu)成:
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
function App() {
return (
<div>
<Welcome name="Sara" />
<Welcome name="Cahal" />
<Welcome name="Edite" />
</div>
);
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
抽取組件
function Comment(props) {
return (
<div className="Comment">
<div className="UserInfo">
<img className="Avatar"
src={props.author.avatarUrl}
alt={props.author.name}
/>
<div className="UserInfo-name">
{props.author.name}
</div>
</div>
<div className="Comment-text">
{props.text}
</div>
<div className="Comment-date">
{formatDate(props.date)}
</div>
</div>
);
}
類方式組件(有狀態(tài))
class 補(bǔ)充
本質(zhì)就是對(duì) EcmaScript 5 中構(gòu)造函數(shù)的一個(gè)語(yǔ)法糖
就是讓你寫構(gòu)造函數(shù)(類)更方便了
基本語(yǔ)法
constructor
構(gòu)造函數(shù)-
實(shí)例成員
實(shí)例屬性
實(shí)例方法
-
類成員
靜態(tài)方法
靜態(tài)屬性
class 組件語(yǔ)法
在 React 中推薦使用 EcmaScript 6 Class 的方式類定義組件
<script>
class Person {
constructor (name, age, gender) {
this.name = name
this.age = age
this.gender = gender
this.type = 'human'
}
sayHello () {
window.alert('hello ' + this.name)
}
}
// class 可以使用 extends 關(guān)鍵字實(shí)現(xiàn)對(duì)父類的繼承
// 不僅可以繼承屬性瞳腌,還可以繼承父類的原型方法
// 這里繼承的本質(zhì)是使用的:原型式繼承
// 注意:在子類中绞铃,如果沒(méi)有寫 constructor 則不需要調(diào)用 super
// 如果一旦寫了 constructor ,就必須手動(dòng)調(diào)用一下 super 父類構(gòu)造函數(shù)嫂侍,否則報(bào)錯(cuò)
class Student extends Person {
constructor (name, age, gender, id) {
// super 就父類構(gòu)造函數(shù)
// 借用父類構(gòu)造函數(shù)儿捧,把 name、age挑宠、gender 初始化到 Student 實(shí)例中
super(name, age, gender)
this.id = id
}
study () {
console.log(this.name + ' 在學(xué)習(xí)菲盾。。各淀。')
}
}
class Teacher extends Person {
constructor (name, age, gender) {
// super 就父類構(gòu)造函數(shù)
// 借用父類構(gòu)造函數(shù)懒鉴,把 name、age碎浇、gender 初始化到 Student 實(shí)例中
super(name, age, gender)
}
teaching () {
console.log(this.name + ' 在上課临谱。咆畏。。')
}
}
const s1 = new Student('張三', 18, '男')
</script>
組件傳值 Props
- Props 是只讀的吴裤,不能修改
EcmaScript 5 構(gòu)造函數(shù):
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
EcmaScript 6 Class:
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
this.props.children
this.props
對(duì)象的屬性與組件的屬性一一對(duì)應(yīng),但是有一個(gè)例外溺健,就是 this.props.children
屬性麦牺。
它表示組件的所有子節(jié)點(diǎn)。
this.props.children
的值有三種可能:如果當(dāng)前組件沒(méi)有子節(jié)點(diǎn)鞭缭,它就是 undefined
;如果有一個(gè)子節(jié)點(diǎn)剖膳,數(shù)據(jù)類型是 object
;如果有多個(gè)子節(jié)點(diǎn)岭辣,數(shù)據(jù)類型就是 array
吱晒。所以,處理 this.props.children
的時(shí)候要小心沦童。
React 提供一個(gè)工具方法 React.Children
來(lái)處理 this.props.children
仑濒。我們可以用 React.Children.map
來(lái)遍歷子節(jié)點(diǎn),而不用擔(dān)心 this.props.children
的數(shù)據(jù)類型是 undefined
還是 object
偷遗。
事件處理
<script type="text/babel">
function handleClick () {
window.alert('hello')
}
// 1. 使用駝峰命名
// 2. 必須綁定一個(gè)函數(shù)
// 3. 不能使用字符串的方式墩瞳,一定要使用 {函數(shù)} 來(lái)綁定
const element = (
<div>
<button onClick={handleClick}>點(diǎn)我</button>
{/* 直接綁定一個(gè)匿名函數(shù) */}
<button onClick={() => alert('hello world')}>行內(nèi)處理</button>
</div>
)
ReactDOM.render(element, document.getElementById('app'))
</script>
函數(shù)式組件
<script type="text/babel">
// 這不叫組件,只是一個(gè)變量氏豌,沒(méi)有狀態(tài)
// const header = (
// <div className="header">
// <h1>頭部</h1>
// </div>
// )
// 組件的名字首字母必須大寫
function AppHeader () {
return (
<div className="header">
<h1>頭部</h1>
</div>
)
}
function AppAside () {
return (
<div className="aside">
<ul>
<li>正在熱映</li>
<li>即將上映</li>
</ul>
</div>
)
}
function Welcome (props) {
return <h1>Hello, {props.name}</h1>
}
// EcmaScript 6 Class
class AppFooter extends React.Component {
constructor () {
super()
// state 就是組件的狀態(tài)喉酌,也就是把數(shù)據(jù)驅(qū)動(dòng)視圖的數(shù)據(jù)初始化到 state 中
// state 類似于 Vue 中的 data
this.state = {
foo: 'bar'
}
setTimeout(() => {
// 這樣不行
// this.state.foo = 'baz'
// 這才是正確的修改 state 數(shù)據(jù)的方式
this.setState({
foo: 'baz'
})
}, 2000)
}
render () {
return (
<div className="footer">
<p>底部 {this.state.foo}</p>
</div>
)
}
}
const element = (
<div>
<AppHeader />
<div className="main">
<AppAside />
<div className="content">
<Welcome name="張三" />
<Welcome name="李四" />
<Welcome name="Jack" />
</div>
</div>
<AppFooter />
</div>
)
ReactDOM.render(element, document.getElementById('app'))
</script>
帶有狀態(tài)的組件及事件綁定this
<script type="text/babel">
class MyComponent extends React.Component {
constructor () {
// 如果子類加入了 constructor 構(gòu)造函數(shù),則一定要手動(dòng)調(diào)用父類的構(gòu)造函數(shù) super
super()
// React 組件需要通過(guò)手動(dòng)為組件類添加 state 成員來(lái)初始化:ViewModel
// state 等價(jià)于 Vue 中的 data
// 接下來(lái)就可以在該組件管理的模板中通過(guò) {} 來(lái)訪問(wèn)綁定數(shù)據(jù)了
this.state = {
message: 'Hello, MyComponent!'
}
}
render () {
return (
<div>
<h1>{this.state.message}</h1>
<h1>{this.state.message}</h1>
<h1>{this.state.message}</h1>
<h1>{this.state.message}</h1>
<h1>{this.state.message}</h1>
<h1>{this.state.message}</h1>
{/*
* 事件綁定函數(shù)默認(rèn)情況下內(nèi)部的 this 指向 Window
* 自動(dòng)接收一個(gè) event 事件源對(duì)象
* 1. this 指向了 Window
* 2. 只能得到 event 無(wú)法傳參
*/}
<button onClick={this.handleClick}>點(diǎn)擊改變 message</button>
{
/*
* bind 了 this 的函數(shù)調(diào)用的時(shí)候泵喘,好會(huì)給你傳遞一個(gè) event 事件源對(duì)象
* 內(nèi)部的 this 就是你 bind 的那個(gè)參數(shù)
* 1. 可以指定 this
* 2. 支持傳參數(shù)泪电,參數(shù)在前,事件源對(duì)象在最后
* 推薦這種方式
*/
}
<button onClick={this.handleClick.bind(this, 123, 456)}>點(diǎn)擊改變 message</button>
{/*
* 當(dāng)點(diǎn)擊 onClick 的時(shí)候纪铺,調(diào)用綁定了 this 的箭頭函數(shù)
* 箭頭函數(shù)內(nèi)部的 this 是組件實(shí)例
* 所以我可以直接在調(diào)用函數(shù)中再調(diào)用 this.handleClick() 函數(shù)
*/}
<button onClick={(e) => {this.handleClick(e, 123, 456)}}>點(diǎn)擊改變 message</button>
</div>
)
}
// 規(guī)范:處理事件方法都取名為 handlexxx
handleClick (num1, num2, e) {
console.log(num1, num2, e)
// console.log('handle click')
// console.log(this) // 默認(rèn)是 window
// React 不是使用的類似于 Vue 中的 Object.defineProperty() 方式 get相速、set
// this.state.message = 'hello world'
// 現(xiàn)在只需要知道,如果要修改 state 中的數(shù)據(jù)并且希望得到視圖更新霹陡,則一定要使用
// this.setState 方法
// this.setState({
// message: 'hello world'
// })
}
}
const element = <MyComponent />
ReactDOM.render(element, document.getElementById('app'))
</script>