JSX
2019.10.08
const 声明一个变量
const 变量名 = (内容);
内容可以是
"字符串"
<标签名>标签内容{表达式}</标签名>
表达式可以是任何 js 表达式,(for 循环不行,不知道为啥)
JSX 本身也是一个表达式
语法上更接近 js 而不是 html,所以使用驼峰命名
JSX 元素渲染
React 元素
使用 ReactDOM.render();来渲染根节点,并传入 你想渲染的React元素
语法:
ReactDOM.render(元素名, document.getElementById('root'));
更新 UI 的唯一方式是创建一个全新的元素并将其传入 ReactDOM.render();
在实践中,大多数 React 应用只会调用一次ReactDOM.render();
通过 setState() 方法可以刷新界面
JSX组件
使用 JS 函数来自定义组件:
function 组件名(形参){
return <标签名>标签内容{形参.属性名}</标签名>;
}
调用自定义的组件:
const 变量名 = <函数名 属性名=属性值 />
当 React 元素为自定义组件时,它会将 JSX 所接收的属性转换成一个单个对象传递给组件,这个对象被称之为"props",访问属性参数时使用:props.属性名
组件参数也可以直接传一个 model
组件名称必须以大写字母开头
组件也可以写成class 组件,写法如下:
class 组件名 extends Component {
render() {
return (
<div>
{this.props.参数名.属性名}
</div>
);
}
}
*为了严谨起见,我会将所有的组件均采用以上定义为 class 的写法
组件可以理解为 app 开发中的自定义 view,其本质可以看做是类
props 属性
props 属性是组件中自带的属性之一,其可以理解为该组件接收到的参数的一个集合.在使用 class 方式声明组件时不需要写出该属性,在给组件赋值时,所有的参数都作为 props 的一个属性获取.
*可以理解为隐形的参数集合
State 属性
State 是组件中自带的属性之一,当组件中的元素被指定为 State 的值后,State 的值改变,该元素会相应进行刷新(相当于数据绑定).
只有使用class 方式声明的组件才能调用 state属性,调用方式:
<标签名>this.state.属性名</标签名>
在给 state 属性赋值时标签内容会自动改变,赋值方式:
this.setState({属性名: 属性值});
*使用 "=" 等于号的方式赋值是无效的,组件不会被重新渲染(不知道数据会不会变)
构造函数是唯一可以给 this.state 赋值的地方
state 的变量可以单独被更新
组件的生命周期函数:
1构造函数:
constructor(props) {
super(props);
//代码...
};
}
2渲染
render(){
//代码
}
3组件已经被挂载
componentDidMount() {
//代码
}
4组件即将被卸载
componentWillUnmount() {
//代码
}
组件的state数据是向下流动的
相当于父视图的 state可以传递给子视图,子视图无法传递数据给父视图
箭头函数: 适合用作匿名函数
function 函数名 (参数1,参数 2) {函数声明}
等同于
(参数1,参数 2) => {函数声明}
单一表达式可以省略 return 和 {}
单一参数可以省略()
2019.10.09
事件处理
React 的事件命名采用小驼峰式而不是纯小写
使用JSX语法时需要传入一个函数作为事件处理函数而不是一个字符串
语法:
<控件名 事件名={事件处理函数名}>控件内容</控件名>
如要阻止默认行为,需要在事件中调用
props.preventDefault();
方法
在定义组件时,通常将事件处理函数声明为 class 中的方法,此时需要在constructor方法中进行 this 绑定,语法如下:
// 为了在回调中使用 this
,这个绑定是必不可少的
this.事件函数名 = this.事件函数名.bind(this);
*在 class 内部声明函数时不需要加 function 前缀
*在 class 内部不需要声明属性,直接用 State 就行
向事件函数传递参数,语法如下:
<标签名 事件名={this.事件函数名.bind(this, 参数)}>标签内容</标签名>
例如:
<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>
取出参数的语法如下:
handleClick(props){
//这里写了一个阻止默认事件的方法
props.preventDefault();
}
条件渲染
即通过 if 语句,三目运算符或 && 与运算符进行元素的渲染
if 语句没什么难的,语法如下:
render() {
let 变量名;
if(条件){
//条件成立
变量名=<标签 1></标签 1>
}else{
//条件不成立
变量名=<标签 2></标签 2>
}
return (
<div>
{变量名}
</div>
);
}
与运算符语法如下,比较新颖:
<div>
{条件语句 &&
<标签名>
标签内容
</标签名>
}
</div>
*之所以能这样做,是因为在 JavaScript 中,true && expression
总是会返回 expression
, 而 false && expression
总是会返回 false
。因此,如果条件是 true
,&&
右侧的元素就会被渲染,如果是 false
,React 会忽略并跳过它。
三目运算符语法如下:
{条件语句 ? (<标签></标签>) : (<标签></标签>)}
可以通过让 render() 方法直接 return null; 的方式来阻止组件的渲染.
*在组件的 render 方法中返回 null 并不会影响组件的生命周期。componentDidUpdate 依然会被调用。
列表
map()函数,相当于 forin 循环,写法如下:
数组.map((item) => (<标签>{item}</标签>));
或
数组.map(function(item) {return (<标签>{item}</标签>)})
其返回值是一个新的数组,可以定义变量来接收它
key
列表需要一个 key用来帮助 React 识别哪些元素改变了,因此需要给 map()方法里的元素附一个 key 值,,通常使用元素的 id 来作为 key,当数组的顺序不变时,当不显式赋值时,React 会默认使用 index 给 key 赋值.赋值方法如下:
const todoItems = 数组.map((item) =>
<li key={item.id}>
{item.text}
</li>
);
*所有在 map()方法中创建的元素都需要设置 key 属性,其子元素不需要
同一个 map()方法中创建的元素的 key 应当是独一无二的,不同 map 方法中的元素的 key 可以是相同的
key 属性是无法被读取的,如需访问其值,需要设置其他的属性名
表单
受控组件
在 HTML 中,表单组件是自带状态的,而在 React 中使用受控组件,即将表单元素的状态"value"储存至组件的 State 中,同时在渲染时其状态也从 State 中获取,这样就可以在表单元素的 value 改变时动态获取到其 value.
代码如下:
class 组件名 extends React.Component {
constructor(props) {
super(props);
this.state = {value: ''};
this.回调函数 = this.回调函数.bind(this);
}
回调函数(event) {
this.setState({value: event.target.value});
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<input type="text" value={this.state.value} onChange={this.回调函数} />
</form>
);
}
}
*其思想在于动态改变 state 和根据 state 动态刷新表单组件.
*可以将数组传入 value 属性中
<input type="file" />
是一个非受控组件,因为其 value 只读
当组件中有多个受控元素时,可以给每个元素添加 name 属性,并通过判断 name 属性的值来进行相应的操作.
给 state 赋值时的语法:
this.setState({
[name]: value
});
*setState 方法可以自动合并属性,所以调用其可以更改部分属性.其方式类似于OC中的字典写值操作(可以将 state 当做一个字典)
当受控组件的 value 设置为{null}时其值可被更改,如下:
ReactDOM.render(<input value="hi" />, mountNode);//不可更改
ReactDOM.render(<input value={null} />, mountNode);//可更改
状态提升
由于 React 组件这个东西没有类似于iOS开发的公开属性和私有属性的概念,也没有代理和回调,所以当其子控件向父控件传值时,使用的是类似于原生开发中 block 的方式进行的.
父控件通过传入一个方法作为属性,并实现该方法,子控件调用该方法并传入参数,由父控件实现该方法.(这不就是 block 嘛)
在 React 中通常由父子控件使用该方式传值,且由子控件传递给父控件,所以该方式被称之为 状态提升.
语法:
父控件:
class 父控件 extends React.Component {
constructor(props) {
super(props);
this.block1 = this.block1.bind(this);
this.block2 = this.block2.bind(this);
this.state = {属性: '' "};
}
block1(参数名) {
this.setState({属性:参数名});
}
block2(参数名) {
this.setState({属性:参数名});
}
render() {
return (
<div>
<子控件
参数={this.state.属性}
block={this.block1} />
<子控件
参数={this.state.属性}
onTemperatureChange={this.block2} />
</div>
);
}
}
子控件:
class 子控件 extends React.Component {
constructor(props) {
super(props);
this.事件回调 = this.事件回调.bind(this);
}
事件回调(event) {
this.props.block(event.target.value);
}
render() {
return (
<fieldset>
<input value={this.props.参数}
onChange={this.事件回调} />
</fieldset>
);
}
}
*在需要外传参数时,子控件不能使用自己的 this.state,而是需要调用 this.block(参数)
组合
包含关系
在组件中使用一个名为"children"的数组来将子组件传递到子组件中 代码如下:
function 子组件(props) {
return (
<div 属性名=属性值>
{props.children}
</div>
);
}
*这个传递是隐式的,但是当你在组件的内容中写了代码而未在rander 中调用 props.children 时,这些代码是无效的,其未执行 rander 故无法被渲染
继承
继承的关键字是 extend,在实际开发时通常通过组合来实现组件的复用,并不需要使用继承
React 总结
从移动端开发的角度来看,React类似于使用代码构建界面中的 view,但是要意识到,相比于移动端开发而言,前端是基于界面的,而移动端是基于代码的,移动端的一切界面都可以通过代码调用API 来实现,而前端的 js 是无法做到这一点的,还需要 html 和 css 的配合,React 仅仅是适用于 js 的框架,而开发时还需要 css 的配合才能构建起美观的界面.
React 最重要的特点就是组件,通过组件的组合可以构建各种不同的视图效果,也让代码更加容易被复用.
JSX 仅仅是使用 React 的语法扩展.其最大特点是可以在大括号内书写表达式.