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参数后面讲)

复制代码
到此为止,一样的功能就使用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