1.安裝css
npx create-react-app .
複製代碼
注意: 若是網絡走的是代理,那麼本地須要設置代理地址html
npm config set proxy http:// + 你的代理地址
複製代碼
yarn start
複製代碼
進入src目錄刪除一些沒必要要的靜態文件(圖片、css)保留js文件,刪除一些靜態文件的importvue
進入index.js,刪除以前全部的代碼從新寫入:react
const div = document.createElement('div')
const p = document.createElement('p')
const span = document.createElement('span')
div.appendChild(p)
p.appendChild(span)
span.innerText = 'Hello World'
document.body.appendChild(div)
複製代碼
const div = createElement('div')
const p = createElement('p')
const span = createElement('span')
div.appendChild(p)
p.appendChild(span)
span.innerText = 'Hello World'
document.body.appendChild(div)
function createElement(tagName){
return document.createElement(tagName)
}
複製代碼
const div = createElement('div',
createElement('p',
createElement('span')))
document.body.appendChild(div)
function createElement(tagName,children){
const element = document.createElement(tagName)
if(children){
element.appendChild(children)
}
return element
}
複製代碼
const div = createElement('div',
createElement('p',
createElement('span','Hello World!')))
document.body.appendChild(div)
function createElement(tagName,children){
const element = document.createElement(tagName)
if(children){
if(typeof children === 'string'){
var str = document.createTextNode(children)
element.appendChild(str)
} else {
element.appendChild(children)
}
}
return element
}
複製代碼
const div = t('div',
t('p',
t('span','Hello World!')))
document.body.appendChild(div)
function t(tagName,children){
const element = document.createElement(tagName)
if(children){
if(typeof children === 'string'){
var str = document.createTextNode(children)
element.appendChild(str)
} else {
element.appendChild(children)
}
}
return element
}
複製代碼
這樣就能夠經過幾行簡單的代碼,來爲頁面建立嵌套結構的DOM元素了es6
const div = (
t('div',
t('p',
t('span', 'Hello World!')))
)
const div2 = (
<div>
<p>
<span>
Hello World !
</span>
</p>
</div>
)
document.body.appendChild(div)
複製代碼
換句話說能不能開發者寫這樣的代碼:npm
const div2 = (
<div>
<p>
<span>
Hello World !
</span>
</p>
</div>
)
複製代碼
也就是說有沒有一個方法(bable)能夠直接將上面的代碼翻譯成下面的代碼(具體是怎麼作到的暫時無論):編程
const div = (
t('div',
t('p',
t('span', 'Hello World!')))
)
複製代碼
這一點就是React的創舉!React的核心:看似是在寫標籤其實是react幫咱們翻譯成了函數t,t也就是react.createElement
數組
實際上用了react咱們就不須要函數t了,只須要引入reactbash
react.createElement
import React from 'react'
const div = (
React.createElement('div',
React.createElement('p',
React.createElement('span', 'Hello World!')))
)
console.log(div)
複製代碼
打印出的div不是一個element而是一個虛擬的element(對象),它的類型是div,div裏面是p,p裏面是span,因爲它是虛擬的element,因此不能使用document.body.appendChild(div)
直接把它放到body上網絡
若是直接放到body上就會報錯:它不是一個節點,而是一個假的節點
這就是React的第二個創舉虛擬DOM
import React from 'react'
import ReactDOM from 'react-dom'
const div = (
React.createElement('div',
React.createElement('p',
React.createElement('span', 'Hello World!')))
)
console.log(div)
ReactDOM.render(div,document.body)
複製代碼
若是直接將div放到body上會警告,不容許污染body,因此放到public文件夾下的index.html中的id爲root的div中
import React from 'react'
import ReactDOM from 'react-dom'
const div = (
React.createElement('div',null,
React.createElement('p',null,
React.createElement('span', null,'Hello World!')))
)
console.log(div)
ReactDOM.render(div,document.getElementById('root'))
複製代碼
注意:React.createElement
還須要接收第二個參數null(null參數後面講)
![深度截圖_選擇區域_20191014113414.png](https://i.loli.net/2019/10/14/K8IAnwsTVdOlFQh.png)
複製代碼
到此爲止,一樣的功能就使用React實現了一遍!
React的基本思路: React建立了一個React.createElement
的方法,有了這個方法就能夠建立虛擬的element,有了這個虛擬的element,就可使用ReactDOM.render
方法將它掛到靜態頁面中body中的元素中
const div = (
<div>
<p>
<span>
Hello World!
</span>
</p>
</div>
)
複製代碼
import React from 'react'
import ReactDOM from 'react-dom'
const Header = (
<header>
header
</header>
)
const Bottom = (
<div>
botttom
</div>
)
const div = (
<div>
{Header}
<p>
<span>
Hello World!
</span>
</p>
{Bottom}
</div>
)
console.log(div)
ReactDOM.render(div,document.getElementById('root'))
複製代碼
這樣咱們就能夠任意的像組合變量同樣組合頁面,這就是React提供的組件化思路和Vue不一樣,vue的組件須要寫vue的單文件組件,vue會發明不少語法,可是React不會,react只是提供了一種簡寫,若是你願意寫React.createElement
這種形式,也不會阻止你。
React會使用純JS實現組件化,這也是React的第二個核心思想,使用JS的組合來實現組件化
import React from 'react'
import ReactDOM from 'react-dom'
const Header = (
<header>
header
</header>
)
const Header2 = function(props){
return (
<header>
header {props.name}
</header>
)
}
const Bottom = (
<div>
botttom
</div>
)
const div = (
<div>
{Header}
{Header2({name: 'Reagen'})}
<p>
<span>
Hello World!
</span>
</p>
{Bottom}
</div>
)
console.log(div)
ReactDOM.render(div,document.getElementById('root'))
複製代碼
組件Header和組件Header2最大的區別在於,Header是寫死的而Header2是能夠接收參數的
import React from 'react'
import ReactDOM from 'react-dom'
const Header = (
<header>
header
</header>
)
const Header2 = function(props){
return (
<header>
header {props.name}
</header>
)
}
const Bottom = (
<div>
botttom
</div>
)
const div = (
<div>
{Header}
{Header2({name: 'Reagen'})}
<Header2 name = "Jack"/>
<p>
<span>
Hello World!
</span>
</p>
{Bottom}
</div>
)
console.log(div)
ReactDOM.render(div,document.getElementById('root'))
複製代碼
當React發現標籤裏面不是普通的標籤,而是咱們本身寫的函數式組件的時候,React就會去調用這個函數而且把後面的name = "Jack"
當作參數,也就是說當咱們這樣寫:<Header2 name = "Jack"/>
React會幫咱們轉化成{Header2({name: 'Jack'})}
,換句話說{Header2({name: 'Jack'})}
等價於 ====> <Header2 name = "Jack"/>
這也是React和Vue最大的不一樣之處,React不會創造大量的API,React只會在寫法上做文章
例如:組件Bottom2裏面有一個變量n初始值爲0,當點擊按鈕的時候如何讓n每次自增長1?
const Bottom2 = function(){
let n = 0
return (
<div>
{n}
<button onClick = {function(){
n = n + 1
}}>+1</button>
</div>
)
}
const div = (
<div>
{Header}
{Header2({name: 'Reagen'})}
<Header2 name = "Jack"/>
<p>
<span>
Hello World!
</span>
</p>
{Bottom}
<Bottom2 />
</div>
)
console.log(div)
ReactDOM.render(div,document.getElementById('root'))
複製代碼
可是這樣點擊按鈕n並不會自增長1
React中使用組件內部的一個狀態應該這樣寫:
const Bottom2 = function(){
const [n,setN] = React.useState(0) // es6析構賦值
return (
<div>
{n}
<button onClick = {function(){
setN(n+1)
}}>+1</button>
</div>
)
}
const div = (
<div>
{Header}
{Header2({name: 'Reagen'})}
<Header2 name = "Jack"/>
<p>
<span>
Hello World!
</span>
</p>
{Bottom}
<Bottom2 />
</div>
)
console.log(div)
ReactDOM.render(div,document.getElementById('root'))
複製代碼
const [n,setN] = React.useState(0)
它的意思就是n爲設置n的初始值,若是要改n的值就用setN,這裏使用了一個API
Bottom2就是一個函數組件,類組件其實是用的是es6的class語法:
class Bottom3 extends React.Component{
render(){
return (
<div>
bottom3
</div>
)
}
}
const div = (
<div>
{Header}
{Header2({name: 'Reagen'})}
<Header2 name = "Jack"/>
<p>
<span>
Hello World!
</span>
</p>
{Bottom}
<Bottom2 />
<Bottom3 />
</div>
)
console.log(div)
ReactDOM.render(div,document.getElementById('root'))
複製代碼
函數式組件更加流行,由於它更簡單
總結: React的特色
1.容許直接使用標籤的形式直接建立虛擬的標籤,裏面能夠嵌套任意的子元素
2.容許把另一個組件經過花括號引進來
3.容許把其餘的組件寫成一個函數,而後把這個函數調用一下再經過花括號引進來
4.固然你也能夠經過另一種形式,把這個組件當作標籤,把參數當作屬性。例如:{Header2({name: 'Jack'})}
等價於 ====><Header2 name = "Jack"/>
5.若是一個組件你須要它用到本身的狀態就用React.useState()
,例如:const [n,setN] = React.useState(0)
給了一個讀的API和一個寫的API
時刻要注意在用react的時候,寫的代碼是和js密切相關的,例如給div加class,就不能寫<div class="xxx">Cell</div>
,由於在js中給div加class使用div.className = "xxx",因此這裏應該寫成符合JS語法的形式<div className="xxx">Cell</div>
React規定組件中的變量是誰的誰纔有權限改,其餘組件只容許使用這個變量(也就是說這個變量在其餘組件中是隻讀的)
任何函數f均可以被替換成f2:const f2 = (...args) => f(...args)
寫法1:<Cell text={item} onClick = {onClickCell(row,col)}/>
和 寫法2:<Cell text={item} onClick = {() => onClickCell(row,col)}/>
問題: 寫法1和寫法2有什麼不一樣?
答:最終的結果都是同樣,都是調用函數onClickCell,可是過程不同,寫法1會當即調用函數onClickCell,寫法2不會當即執行而是會當Cell被點擊的時候調用onClickCell
例如: 下面中的cells是一個對象在內存中只是一個地址而已,這裏的cells和聲明cells時候對應的都是同一塊內存地址,即便這裏修改了cells中的某一行某一列爲x,可是cells對應的內存地址並無發生變化,內存地址沒有發生變化,React也就不會更新DOM
const [cells,setCells] = React.useState([
[null, null, null],
[null, null, null],
[null, null, null]
])
const onClickCell = function(row,col){
console.log('行' + row)
console.log('列' + col)
cells[row][col] = 'x'
setCells(cells)
}
複製代碼
那怎麼辦呢? ===> 深拷貝,有一種簡單的深拷貝的方法:
const copy = JSON.parse(JSON.stringify(cells))
copy[row][col] = 'x'
setCells(copy)
複製代碼
這樣每次點擊的時候都會從新拷貝一份cells(每一次都是新的內存地址)而後改變其中的某行某列的文本爲x
在React中若是不改變一個對象的內存地址,直接set是沒有用的,也就是說須要深拷貝一份,再在深拷貝的對象上去修改這個對象
const [n,setN] = React.useState(0)
,這裏的n是隻讀的,setN的特色就是它永遠不回去直接去改以前的n而是去複製以前的對象而後搞一個新的再弄回去,學了函數式就會了解這樣的好處
React會牽扯到全部的JS知識,JS學的很差就學很差React,React和JS息息相關
面向對象也佔用內存,而後用this去引用這塊內存,函數式編程是特別討厭this的
在判斷誰贏了的時候,當‘x’的某一行3個都是‘x’的時候應該提示的是‘x’贏了,可是爲何只有等‘o’再下一次的時候才告知‘x’贏了?
緣由:setCells(copy)
是異步的,因爲cells是一個對象,直接去改裏面的內容如cells[0][0]= 'x'
,內存的地址不會發生變化仍是以前聲明時候的cells是null所以react不會從新渲染UI頁面不會有任何變化不會出現‘x’,所以須要深拷貝一個對象,這個對象copy是一個新的內存地址,setCells(copy)
的時候,因爲每次點擊的是=時候copy每次發生變化都是從新拷貝的內存地址,所以每次copy發生變化以後,react都會從新渲染UI而且cells會跟着copy的變化而變化也就是說它是異步的過程
const onClickCell = function(row,col){
setN(n+1)
const copy = JSON.parse(JSON.stringify(cells))
copy[row][col] = n % 2 == 0 ? 'x' : 'o'
setCells(copy)
result()
}
複製代碼
可是判斷遊戲結果是根據cells來判斷的而setCells的值是根據copy,當copy發生變化的時候,setCells會跟着變化,頁面會接着渲染出‘x’,可是cells自己並非當即發生變化
const result = function(){
if(cells[0][0] === cells[0][1] && cells[0][1] === cells[0][2] && cells[0][0] !== null ){
console.log(cells[0][0] + '贏了')
}
}
複製代碼
所以須要將copy做爲參數傳給result,result用參數cells來接收,也就是說在函數result把copy當作cells來判斷
const result = function(cells){
if(cells[0][0] === cells[0][1] && cells[0][1] === cells[0][2] && cells[0][0] !== null ){
console.log(cells[0][0] + '贏了')
}
}
const onClickCell = function(row,col){
setN(n+1)
const copy = JSON.parse(JSON.stringify(cells))
copy[row][col] = n % 2 == 0 ? 'x' : 'o'
setCells(copy)
result(copy)
}
複製代碼
{finished && <div>遊戲結束</div>}
這句話的意思是若是爲finished狀態就展現遊戲結束的div