作为一个稍微有点基础的人吧,思来想去如何开始学习react呢,最终觉得还是选择权威,简单即有效,直接奔官网,讲真确实直接get started,英文水平不错的推荐直接英文,如果英文差一点也可以去中文翻译官网。
如果读者还没搭好react开发环境,可以参考我的另一篇博客—快速搭建简单环境。
这里我也就是用官网的例子,讲一下自己的理解和体会,有不对敬请指出。
简单谈一下jsx
const element = <h1>Hello, world!</h1>;
说实话,我第一次看还没反应过来有什么不对,默认以为是字符串,不过回过神发现没有引号。。
这个不是字符串也不是html的东西就是jsx,一种js的语法扩展,是一种极富表现力的UI展示方式,比模板还要直观可复用,却拥有js的全部功能。
其本质上跟模板是一样的,就是提取出UI中不变的部分写死,而变化的部分动态化、数据化。
- jsx和vue的template一样可以直接使用大量js的语法,只是jsx更加贴近js
- 个人认为功能更加强大,其本质是React.createElement(component, props, …children)的语法糖。
官网上提到具体有如下特性:
1. 可以嵌入表达式
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')
);
简单解释下,定义一个formatName格式化数据的函数,定义user数据,定义jsx形式的element,其中像es6语法字符串模板一样,嵌入了变量部分{formatName(user)},{}里面是进行的是格式化数据函数调用,在后面的ReactDOM.render解析中,formatName(user)将被解析为’Harper Perez’,最后得到的就是
<h1>
Hello, Harper Perez!
</h1>
另外,官网提到,将上面的jsx分为三行是为了方便阅读,同时建议将多行jsx用()包裹起来来避免自动分号插入的陷阱。
2. jsx也是表达式
function getGreeting(user) {
if (user) {
return <h1>Hello, {formatName(user)}!</h1>;
}
return <h1>Hello, Stranger.</h1>;
}
不多解释,jsx在编译后就会变成js对象,同时可以把jsx看为变量,那么变量能做的其实jsx应该也都可以。
3. jsx的指定属性
const element = <div tabIndex="0"></div>;
const element = <img src={user.avatarUrl}></img>;
基本就这两种形式,第一种是普通的属性定义,其中没有涉及表达式和变量,第二种则是表达式形式,里面是变量等,特别注意的是,在{}里面如果用引号则代表字符串。
4. jsx中的子元素声明
const element = (
<div>
<h1>Hello!</h1>
<h2>Good to see you here.</h2>
</div>
);
注意下,jsx相对于html更加贴近javascript,所以其react DOM属性采用驼峰命名,所以定义html属性都会相应变化,如class我们应该写为className。
5.在jsx中可以直接使用用户输入,不用对xss做处理
6.Babel 转义器会把 JSX 转换成一个名为 React.createElement() 的方法调用。
下面两段代码是完全相同的
const element = (
<h1 className="greeting">
Hello, world!
</h1>
);
const element = React.createElement(
'h1',
{className: 'greeting'},
'Hello, world!'
);
最终react将其处理为
// 注意: 以下示例是简化过的(不代表在 React 源码中是这样)
const element = {
type: 'h1',
props: {
className: 'greeting',
children: 'Hello, world'
}
};
这样的对象被称为 “React 元素”。它代表所有你在屏幕上看到的东西。React 通过读取这些对象来构建 DOM 并保持数据内容一致。
实战
基本介绍完jsx,下面用一个clock实例入门一下。
clock
根据之前的jsx内容,我们很容易得出思路,先写UI,然后调用ReactDom.render()
function tick() {
const element = (
<div>
<h1>Hello, world!</h1>
<h2>It is {new Date().toLocaleTimeString()}.</h2>
</div>
);
ReactDOM.render(
element,
document.getElementById('root')
);
}
setInterval(tick, 1000);
这里我们使用定时器,每一秒刷新一次root元素里的DOM,但其实react中使用了virtual dom,virtual dom其实就是类似上面react dom的东西,每次jsx中存在状态变化时,我们的代码会通知react进行UI更改,react会根据新状态生成新virtual dom,然后对新旧virtual dom进行对比,找出状态变化更新UI的最小代价,最后才进行DOM操作。
上面不理解也没关系,总之,react有自己的DOM更新机制,所以我们每次进行setInterval,重新创建jsx、重新全部渲染root元素中的DOM,这其实是比较低效的,再看下面只进行h2标签更新的代码
class Clock extends React.Component {
constructor(props){
super(props)
this.state = {date: new Date()}
}
componentDidMount(){
this.timerId = setInterval(
() => this.tick(),
1000
)
}
tick(){
this.setState(
(prevState, props) => ({
date: new Date(prevState.date.getTime() + 1000)
})
)
}
componentWillUnmount(){
clearInterval(this.timerId)
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>it is { this.state.date.toLocaleTimeString() }</h2>
</div>
)
}
}
这里使用了class写法,代码没太多变化,整体来看我们只是将定时器放到了react生命周期,同时将数据变化托管给react,react自己进行最小代价的代码更新,有两个关键点:
- 在组件创建的时候设置定时器,每一秒改变一次时间
- 关键在tick函数,每秒调用setState,通知react数据变化
Toggle
实现一个按钮,点击一次为on,再次点击为off,循环反复,主要体会一下在jsx中直接使用事件绑定
class Toggle extends React.Component {
constructor(props){
super(props);
this.state = {isToggleOn: true}
this.handClick = this.handClick.bind(this)
}
handClick() {
this.setState(prevState => ({
isToggleOn: !prevState.isToggleOn
}))
}
render(){
return (
<button onClick={this.handClick}>
{this.state.isToggleOn ? "on" : "off"}
</button>
)
}
}
设置好初始state,在jsx中点击事件绑定handclick函数,handclick则会改变state通知react改变DOM