安装
使用 create-react-app 快速构建 React 开发环境
create-react-app 是来自于 Facebook,通过该命令我们无需配置就能快速构建 React 开发环境。
create-react-app 自动创建的项目是基于 Webpack + ES6
yarn add global creat-react-app
creat-react-app xxxxxx为文件夹名称
cd xxx
安装好之后就会提示已经帮忙安装git、可以使用yarn start等命令
cdn 连接
如果想要使用cdn连接来方便学习,那么复制以下代码
<script src="https://cdn.bootcss.com/react/16.10.2/umd/react.development.js"></script>
<script src="https://cdn.bootcss.com/react-dom/16.10.2/umd/react-dom.development.js"></script>
<script src="https://cdn.bootcss.com/babel-standalone/7.0.0-beta.3/babel.min.js"></script>
把script标签改成
<script type="text/babel">
上面的方式是通过cdn连接到react的在线云文件(react和reactDOM)以及babel-browser上
react.js 是 React 的核心库,
react-dom.js 是提供与 DOM 相关的功能,
babel.js 的作用是将 JSX 语法转为 JavaScript 语法
凡是使用 JSX 的地方,都要加上 type="text/babel" 。
ReactDOM.render()
ReactDOM.render()是react的基本语法,用来将模板做成html语言,并且放置到对应的dom节点里
<div id="example"></div>
ReactDOM.render(
<h1>helloworld</h1>,
document.querySelector("#example")
)
上面的代码将模板放到id为example的div标签内。
JSX语法
JSX语法为js的扩展语言,是react编写的比较有名的模板写法,类似于vue文件中的的template标签,只不过vue是用了自己编写的vue-loader来解析,而JSX语法是通过babel-loader来解析的。webpack内置babel-loader,所以我们无需做配置就可以yarn start
JSX语法最大的特点就是支持html和js混写,只需要用{}包裹js语言
有一个地方需要注意,就是 class 属性需要写成 className ,for 属性需要写成 htmlFor ,这是因为 class 和 for 是 JavaScript 的保留字。
示例
var names = ["apple", "banana", "orange"];
ReactDOM.render(
<ul>
{names.map((value) => {
return <li>{value}</li>;
})}
</ul>,
document.getElementById("example")
);
上面的代码采用js跟html混写的方式,把虚拟dom插入到ul上面,就跟v-for一样方便
我们甚至还可以写一点if语句
let li;
if (true) {
li = <li>123</li>;
}
ReactDOM.render(<ul>{li}</ul>,
document.getElementById("example")
);
还可以这样改进
ReactDOM.render(<ul>
{2 > 1 ? <li>123</li> : <li>456</li>}
</ul>,document.getElementById("example" )
上面的代码都是一样将<li>123</li>渲染进ul标签内
使用组件
类组件
以前的React 使用 React.createClass ()创建组件类
现在我们直接可以使用class 组件名 extends React.Component{}的方式来定义组件,并且直接使用<组件名 />的方式来使用它
下面是示例
class Mycomponent extends React.Component {//定义组件
render() {
return <div>this is component</div>;
}
}
ReactDOM.render(
<div>
this is parent
<Mycomponent /> //使用组件
</div>,
document.querySelector("#example")
);
函数组件
使用函数组件在某种程度上比类组件更便捷
function Mycomponent() {
return <div>this is component</div>;
}
两者的效果是一样的
组件的外部数据props
类组件
this.props不但可以用来传递数据,还包含了组件的所有信息,以下代码读取了它的value值。
class Mycomponent extends React.Component {
render() {
return (
<div>
传进来的props是{typeof this.props.value} //string
{JSON.parse(this.props.value).map((value) => {
return <div>{value}</div>; //由于是string所以转一下
})}
</div>
);
}
}
ReactDOM.render(
<div>
this is parent
<Mycomponent value="[1,2,3]" />//这里传递了数据
</div>,
document.querySelector("#example")
);
上面的代码中,在使用组件的地方传递了value,然后组件处使用this.props接收,由于传递进来的数据是字符串,所以使用JSON.parse把它变成了数组,然后再在子组件上添加了三个div,效果如下
函数组件
function Mycomponent(props) {
return (
<div>
传进来的props是{typeof props.value}
{JSON.parse(props.value).map((value) => {
return <div>{value}</div>; //由于是string所以转一下
})}
</div>
);
}
实际上两者相差不大,类组件用的是this,而函数组件是通过显性传递props来获取到组件信息
两者写法的区别
函数组件
//定义组件
function Mycomponent(props) {
return <div>{props.value}</div>;
}
//使用组件
<Mycomponent value="1"/>
类组件
//定义组件
class Mycomponent extends React.Component {
render() {
return <div>{this.props.value}</div>;
}
}
//使用组件
<Mycomponent value="1"/>
组件的注意要点
组件必须使用首字母大写的形式,且一个组件只能有一个根节点
// the first letter of the component's name must be capitalized
// so name must be HelloMessage
class helloMessage extends React.Component {
render() {
return <h1>
Hello {this.props.name}
</h1><p> // wrong,should only one top child node
some text
</p>;
}
}
this.props.children
有时候我们需要给组件增加子节点,比如这样
<Mycomponent className="shy">
<div>子节点1</div>
<div>子节点2</div>
</Mycomponent>
但是这段代码这样写,对组件是无效的。我们可以通过this.props.children来获取他的子节点,然后渲染进组件内
class Mycomponent extends React.Component {
render() {
return (
<ol className="component">
{React.Children.map(this.props.children, (child) => {
return <li>{child}</li>;
})}
</ol>
);
}
}
可以看到代码已经渲染进来了
这里需要注意, this.props.children 的值有三种可能:如果当前组件没有子节点,它就是 undefined ;如果有一个子节点,数据类型是 object ;如果有多个子节点,数据类型就是 array 。所以,处理 this.props.children 的时候要小心。
React提供一个React.Children.map的方法来帮助我们处理this.props.children,通过这个方法我们可以遍历组件的子节点
内部数据state
class和函数式组件的state写法是不一样的,但是它们都遵循同一套逻辑,我们目前需要了解的是怎么初始化数据、如何get数据、如何set数据
class组件
class Mycomponent extends React.Component {
constructor() {
super(); //这里调用super继承父类的属性 固定写法
this.state = { //这里使用this.state初始化内部数据
n: 1,
m: 2
};
}
render() {
return (
<div>
这里是内部数据n:{this.state.n} //读数据使用this.state
这里是内部数据m:{this.state.m}
</div>
);
}
}
函数组件
const Mycomponent = () => {
const [n, setN] = React.useState(1);//数组析构写法
const [m, setm] = React.useState(2);
return (
<div>
内部数据n:{n} //读数据
内部数据m:{m}
</div>
);
};
上面的数组析构写法意思是把React.useState产生的数组分别赋给n和setN
其中React.useState(1)用来初始化数据
n是读取时用的
这个setN是什么?实际上它是用来set数据的,这一点我们在下一节再谈
setState
函数和class组件下的setState自然也是不同的,要注意的是react的数据并不是双向绑定的,也不会像vue一样对数据进行getter或setter的监听。
class组件下的setState
在class下使用的是this.setState()方法来设置新的state,要注意这里的setState方法是异步执行的
render() {
return (
<div className="Son">
<button onClick={() => this.add()}>+1</button>
</div>
);
}
add() {
this.setState((state) => {
return { n: this.state.n + 1 };
//也可以写成
//this.setState({n:this.state.n+1})
// 但推荐setState((state)=>{return {...}})的写法
});
}
类组件注意事项
- 正如你所见,在类组件下,我们要更新state时,使用
this.state.n+1是有效的,只不过不会像vue一样渲染到页面上,只有调用setState才有效,这是因为react没有像vue一样监听data数据的机制。 - 使用
setState方法最好在里面套用一个回调函数,因为它是异步的,所以不会马上修改state。假如我们要使用新数据,那么不适用回调函数的情况下只能获得旧的state - react不希望我们修改原state,更希望能返回一个新的state,这时基于react的数据不可变思想,这点跟
vue2非常不同。
函数组件下的setState
在函数组件下使用的是上面例子里 const [n, setN] = React.useState(1)中的setN()函数进行修改。React.useState方法初始化数据并返回一个数组对象,一共有两个value,第一个是初始化的数据,第二个是设置它的方法。
const [n, setN] = React.useState(0);//变量名随便取
return (
<div>
{n}
<button onClick={() => setN(n + 1)}>+1</button>
</div>
);
要注意setN**不会改变**state的值,而是返回一个新的state。
函数组件注意事项
- 在函数组件下,没有
this这一说,一切内容都是使用参数和函数 - 它是使用
setxx()函数的形式来更新UI的
复杂类型下的state处理
或许有人已经注意到了类组件和函数组件的state区别
类组件
this.state={
n:1,
m:2
}
函数组件
const [n, setN] = React.useState(1);
const [m, setm] = React.useState(2);
我们能不能把函数组件的修改成像类组件一样方便呢?答案是可以的,比如说修改成
const [state, setState] = React.useState({n:1,m:2});
但是如果你要通过点击事件对源数据进行修改
//这里的写法也要跟类组件一样
<button onClick={() => setState({ n: state.n + 1 })}>+1</button>
<button onClick={() => setState({ m: state.m + 1 })}>+1</button>
你会发现修改了n,m就会消失,这是因为类组件下的this.state这个对象,里面的数据会自动合并,也就是react帮我们做了了以下处理:
this.state={m:1,n:1}//原数据
//当让this.state.m + 1时 实际上this.state已经变成新对象了。
this.state= ...return {...this.state,m:this.state.m+1}
//react帮助我们拷贝了原来的state里面的数据{m:1,n:1}
//然后又给了新的m,对原来的数据进行了覆盖,此时this.state为{m:2,n:1}
上面的代码中,...this.state的意思是展开了原来state对象,把里面的属性都给了return后的对象
上面我们知道了类组件state的原理,而函数组件下的state并不会自动帮助我们合并,所以我们要写成
<button onClick={(state) => setState({ ...state,n: state.n + 1 })}>+1</button>
<button onClick={(state) => setState({...state,m: state.m + 1 })}>+1</button>
重点是...state,拷贝原来的对象里面的数据