前言
本来学习了Vue.js之后,没有再想学习React.js。但是,又非常想使用阿里的ant design
项目,为了不再纠结,还是用了一两天,进行了一些粗略的学习。
脚手架
下载create-react-app
cnpm install create-react-app -g
初始化项目
create-react-app react-demo
脚手架会优先使用yarn
下载依赖
查看package.json
{
"name": "react-demo",
"version": "0.1.0",
"private": true,
"dependencies": {
"react": "^16.2.0",
"react-dom": "^16.2.0",
"react-scripts": "1.1.0"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
}
}
需要注意的是,我们发现依赖非常之少,没有看见,webpack,babel,学过Vue.js之后对这些东西是非常敏感的。
在scripts
中,我们发现,构建脚本好像在使用一个叫react-scripts
的东西,按照直觉,我们应该去看看这个包是干什么用的。
在node_modules
中,我们找到react-scripts
文件夹,结构如下:
bin
config
node_modules
scripts
package.json
README.md
在bin
中我们发现有react-scripts.js
,接收命令行参数。
在scripts
中,我们发现有start.js
,我们在里面发现了webpack
, webpack-dev-server
这些东西。
此时,解除了我们之前的疑惑,react-scripts
将webpack等开发中使用的工具为我们进行了封装,是我们不必要直接接触,只需要使用react-scripts
命即可。不得不说,这是对开发人员非常友好的。
运行项目
cnpm start
如果运行正常,将在3000端口起来一个HelloWorld。
React知识点
入口函数
ReactDOM.render(<App />, document.getElementById('root'));
将App组件渲染到root div中。
类似与Vue中:
new Vue({
el:"#id",
components:{
App.name:App
},
template:<app/>
});
JSX
使用React,jsx是我们避免不了的东西,虽然我也是非常不喜欢。但是,也没有办法...
简单记录一下
const element = <h1>Hello, world!</h1>;
const element = <img src={this.avatarUrl}></img>;
function formatName(user) {
return user.firstName + ' ' + user.lastName;
}
const user = {
firstName: 'Harper',
lastName: 'Perez'
};
const element = (
<h1>
Hello, {formatName(user)}!
</h1>
);
组件
组件是我们构建应用的基本单元,在Vue中我们使用的是.vue的单文件组件,在React也类似,不过文件名还是.js。
组件基本模板
import React, { Component } from 'react';
class MyComponent extends Component {
constructor(props) {
super(props);
this.state = { }
}
render() {
return (
<div>
</div>
);
}
}
export default MyComponent;
我们可以在VSCode中,安装相关插件,书写代码非常快。
可以看到,我们的自定义组件首先继承React的Component,在render
方法中书写web骨架。
React数据三兄弟
我们在上面的代码中,敏感的发现有两个东西应该值得我们关注,那就是state
和props
。还有一个没有出现的是context
。这些是比较重要的东西,我们一个一个介绍。
state
state用来维护界面view和模型的model的绑定。类似与vue中的data。
import React, { Component } from 'react';
class MyComponent extends Component {
constructor(props) {
super(props);
this.state = {
msg : 'hello react',
number:0
}
}
add = () => {
this.setState({
number:this.state.number + 1
});
}
render() {
return (
<div>
<p>{this.state.msg}</p>
<p>{this.state.number}</p>
<button onClick={this.add}>点我加1</button>
</div>
);
}
}
export default MyComponent;
this.state是一个json对象,使用时用{}表示引用变量。
当我们点击按钮后,界面上的数字将增加1。
需要注意的有:
- onClick中c大写。
- 自己的方法要写箭头函数,才能正确拿到this引用。
props
props用来处理父组件向子组件传参,vue中也是使用的同样的关键字。不过与vue中有非常大的不同。react中的props更加易用。
创建一个子组件
import React, { Component } from 'react';
class Child extends Component {
constructor(props) {
super(props);
console.log(props)
this.state = { }
}
render() {
return (
<div>
<p>{this.props.say}</p>
</div>
);
}
}
export default Child;
修改MyComponent.js
this.state = {
msg : 'hello react',
number:0,
say : 'hello child'
}
<Child say={this.state.say} />
这样,child组件就能接收并使用say变量。
需要注意的是,props并不是固定的,props作为构造函数的第一个参数传入,我们可以修改形参为其他的代号,也就说,如果我们这样:
constructor(p) {
super(p);
}
<p>{this.p.say}</p>
也是可行的,只不过我们一般都会使用props。见名知意。
现在我们来思考一个问题,刚才我们都是从父组件向子组件传递数据,那么子组件如何向父组件传递数据呢?以如上实例中,子组件如何控制父组件的数字加1操作呢?这里就要用到一些技巧了,我们来看:
我们大发脑洞,将add函数传给child。
<Child add={this.add}/>
在child中:
this.state = {
parantAdd : props.add
}
<button onClick={()=>{ this.state.parantAdd();}}>点我给父亲加1</button>
这样就实现了子组件控制父组件的view和model。大家可以体会一下。
context
比前两个用的少,暂且不介绍。
组件生命周期
老生常谈的问题,列举几个常用的,没有什么好说的。
- componentWillMount 渲染之前
- componentDidMount 第一次渲染之后
- componentWillUnmount 组件从DOM中被移除时。
再补充一下,使用组件只需引入后在需要的地方用<>来表示。
如App,就写<App/>。
React Router
react-router是react生态中的路由项目,最新版本是4.2。由于4.x是从2.x直接上来的,API变化较大。而且也没有比较好文档,官方的文档基本上是代码示例的堆砌,没有细节的解释。这一点,确实比vue-router做的要差一些。
下载
cnpm install react-router-dom --save
在根组件中注册
import {
BrowserRouter as Router,
Route,
Switch
} from 'react-router-dom';
import Welcome from './Welcome'
import NotFound from './NotFound'
Welcome.js
import React, { Component } from 'react';
import { Route } from 'react-router-dom';
import User from './User';
class Welcome extends Component {
constructor(props) {
super(props);
this.state = { }
}
render() {
return (
<div>
<p>welcome page </p>
<Route path="/welcome/:userId" component={User}/>
</div>
);
}
}
export default Welcome;
welcome中使用到了嵌套路由。
User.js
import React, { Component } from 'react';
class User extends Component {
constructor(props) {
super(props);
this.state = {
userId : this.props.match.params.userId
}
}
render() {
return (
<div>
<p>id:{this.state.userId}</p>
</div>
);
}
}
export default User;
NotFound.js
import React, { Component } from 'react';
class NotFound extends Component {
constructor(props) {
super(props);
this.state = { }
}
render() {
return (
<div>
<p>404 Not Found</p>
</div>
);
}
}
export default NotFound;
推荐在根组件中做路由分发时,写成如下结构形式:
<Router>
<Switch>
<Route path="/welcome" component={Welcome}/>
<Route path="/" component={Welcome} exact/>
<Route path="*" component={NotFound}/>
</Switch>
</Router>