React16_4整理-TodoList
# 前言
React只做两件事,一件事是渲染ui,一件事是响应事件。
mvc和mvvm都是早些年angular的设计理念。react并不是什么mvvm和mvc,它只是一个很小的东西,只做那两件事儿。它只是个工具。backbone才是真正的mvc,它是模仿java的spring,挪到前端来了。
上一篇文章中基础的工程搭建完毕,基础的语法也知悉了,那么开始弄一个todolist 练练手。学任何一门框架或者语言,都可以用todolist来练练手噢,也就是增删改查的逻辑尽快熟悉并上手。
# 基础语法
React 中 JSX 语法:render 函数中只能有一个根节点,所以你写的多个 html 标签必须包含在一个容器中,比如<div></div>
中。
如果你不想页面中多一个div包在你众多html标签外面
,可以通过引入 import React, {Component, Fragment} from 'react'
来引入 Fragment
,然后用Fragment
替换掉render
函数中外层包裹着div
,这样就能够在渲染页面时只显示你
写的众多html标签
。
# 响应式设计思想和事件绑定
你可以通过 this.state={}
来存储数据,这样你就能够在 JSX 中使用了。
如果你想在JSX
中使用js
的表达式
或者变量
,你必须使用{}
括起来。
在 JSX 语法中如果你给
一个标签设置
了值
,如input
标签的value
,你设置了{this.state.inputValue}
,那么你给this.state.inputValue
设置了值,就会导致
你的输入框
中就无法输入值
了。
除非你绑定onChange
事件,这个onChange
事件与onchange
事件不同,这是JSX
中的,所以你给他添加的方法还是需要用{}
括号括起来。
在给事件绑定方法
时,必须要这样,onChange= {this.changeHandler.bind(this)}
,不然this指向
会有问题,或者onChange={() => {this.changeHandler()}}
,因为箭头函数
就是匿名函数
使用了bind(this)
。
事件绑定的方法里你可以通过e.target
来获取你输入的值,这时候你可以通过以下的方式来让文本框中的值改变。
this.setState({this.state.inputValue: e.target.value});
React
只能够直接以数据驱动视图
,并不能直接
以视图来改变数据,而且如果你使用
this.state.inputValue= e.target.value;那也是
无效的,它只能使用
this.setState来进行
修改state中的值。它
不像vue那种
响应式监听,自动给你修改
data中的值,这也是
它和
vue的
不同点`。
# TodoList 新增、删除功能
es6
中有一个展开运算符
: ...
,如[...[1,2,3,4]]
会把数组中的元素一个个的展开,变成这样[1,2,3,4]
,也就是把原数组中的元素放入了新数组中,也可以在对象中使用。
你在this.setState
中加的{}
里面的成员
都会变
成this.state
的成员
。这表示将原来的this.state.list
平铺到新的数组中,并且还将this.state.inputValue
添加进去。
this.setState({list: [...this.state.list, this.state.inputValue]});
可以将已经添加的文本框中的值清空
掉。
this.setState({list: [...this.state.list, this.state.inputValue], this.state.inputValue: ''});
在 React 中你做循环渲染标签的时候一定要给这个标签添加一个 key 属性,属性值可以赋值为 index,但是在实际的编程中,使用 index 作为 key 值是一个不好的习惯。
<ul>
{
this.state.list.map((item,index) => {
retrun (<li key = {index}>{item}</li>)
})
}
</ul>
React 中有一个规则叫immutable
,表示state 不允许
我们做
任何的改变
,不然性能优化
方面就会出现问题
。
// 所以你删除的时候不允许直接用下面这种方式
// this.state.list.splice(index, 1);
// 需要你拷贝一个副本
const list = [...this.state.list];
list.splice(index, 1);
this.setState({
list: list
});
# JSX 细节语法补充
JSX 中大写字母开头
的标签表示
这是一个组件
,小写字母开头
的标签表示这是一个html元素
。
{/* 注释内容 */}
是 JSX 中的注释
JSX 中添加类名
和 style
,className = "box"
,style = { {color: red} }
。
JSX 中 使用label
标签时使用for
时要改
为htmlFor
# JSX 中不去转义 手动输入的 Html 标签
也就是页面文本框中输入 <h1>xxxx</h1>
在页面中显示时会被转义
成下面这样。
⁢h1>xxxx⁢/h1>
通过给对应的标签 加
上 dangerouslySetInnerHTML = { {__html: <h1>xxx</h1>} }
,表示危险
的设置InnerHTML
内容,这样就会存在被xss攻击
的可能。
// 因为写了dangerouslySetInnerHTML 所以li标签对中的item就没必要写了
<li key={index} dangerouslySetInnerHTML = {{__html:item}}><li>
# 拆分组件与组件之间的传值
组件会变成一个树形的结构,因为一个大的组件中会有很多个小组件,小组件中又有其它的更小的组件。
父
组件传
递子
组件数据
的方式
,直接在子组件中添加属性
。
// 给子组件传递一个content属性
<TodoList content = {this.state.inputValue} />
// 子组件中通过 this.props.属性名来使用
<div>{this.props.content}<div>
如果你嫌每次绑定事件的方法都写.bind(this)
,那么你可以把这一步放到constructor
中去做,这样可以节约一些性能
。
constructor (props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
当你给子
组件传递父
组件中的方法
,记得给方法加
上一个.bind(this)
,不然子
组件调用
时会出现this指向问题
。
# TodoList 代码优化
通过代码解构来优化代码。
const {content} = this.props;
//上面这行代码表示 将this.props.content 赋值给content
//const content = this.props.content;
将样式
的引入放
到引入组件
的后
面,将
事件绑定的方法中需要.bind(this)
的写法,统一
的放到
构造函数中,这样 JSX 中
调用方法时就
不必每次都.bind(this)
了。
constructor (props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
<div onClick = {this.handleClick}></div>
如果你将 遍历列表
的操作
直接放
到JSX
中会显得代码过于臃肿
,那么你可以写一个方法
,然后在JSX中
直接调用
你写的方法即可
。
<div>{this.getListItem()}</div>
getListItem(){
return this.state.list.map((item,index) => {
return <li key = {index}>{item}</li>
})
}
由于React中
的setState
是异步操作
,所以你其实可以传递函数
来替代传递对象
。
// 原来同步的写法
this.setState({
list: ['1','2']
})
// 现在的异步写法
this.setState(()=>{
return {
list: ['1','2']
}
})
// 再简化一下
this.setState(()=>({list: ['1','2']}));
如果你使用了 异步写法
,那么注意
了,如果使用了event对象
,那么要将值先保存一下
,因为异步
的写法获取不到 event
对象的,就像for循环
里面写函数获取不到
每次的i
一样。
handleInput(evnet) {
const value = event.value;
this.setState({} => ({inputValue: value}));
}
当使用了异步的写法后,你可以通过函数里传递过来的参数来替代this.state
// 再简化一下
this.setState((preState)=>(
{
list: [...preState.list,inputValue],
inputValue: ''
}
)
);
循环
的时
候key
的属性因该放
到循环体
的最外
的那个元素上
。
# 总结
# 声明式与命令式开发
声明式开发
方式:使用框架
来操作DOM
,如 React、Vue,减少 DOM 操作。
命令式开发
方式:直接操作DOM
,如 jQuery
# 与其它框架并存
React
最终有一个ReactDOM.Render
方法,这个方法会将组件渲染成DOM填充页面中容器
。
也就是说,它最后只会填充那个容器,页面其它的容器它管不着,其它的容器你就可以使用其它的技术,如 Vue,Jquery 等等。
这就是 React 可以与其它框架并存的原因。
# 组件式的开发(组件化)
首字母大写就是组件,首字母小写就是 html 元素
父子传值 通过属性的方式,父组件中给子组件设置属性,子组件中通过 this.props 获取属性。
子父传值,也是一样,只不过传递的是父组件中的方法,但是每个方法都要.bind(this),表示将父组件中的方法的 this 指向父组件,这样子组件调用的时候才有效。
# 单向数据流
父组件可以给子组件传递值,但是子组件一定不能够直接去修改父组件的值,一旦你改变了,React 会给你报一个错误出来,因为传递过来的值是一个只读的属性。
单向数据流是为了让测试起来和开发起来方便,不容易遇到坑。
如果父组件中有五六个组件,都传递了同一个值,然后你在其中一个子组件中直接修改父组件传递给你的那个属性,那么其它的组件就完蛋了。
虽然最后还是会修改父组件中的那个属性,但是那不一定,因为 setState 这个操作是异步进行的,前面的修改某个属性的操作,会被后面修改某个属性的操作覆盖掉,只执行最后一次操作。
如果页面出现了 bug,你调试起来也不方便,因为你这个属性被五六个组件公用了,都可以直接修改值,这样很不好。
所以 React 出了单向数据流,只允许父组件向子组件传递数据,但是不允许子组件直接修改父组件传递过去的数据,你可以通过父组件传递过去的方法,来修改父组件中的数据。
单向数据流代码维护方便。
# 视图层的框架
做大型项目时传值是一个问题,所以需要配合一个数据层的框架,帮我们解决 React 中组件间复杂传值的问题。
如 兄弟组件间传值,这个最麻烦,一层一层往上,再一层一层的往下,项目很大,那就会崩溃。代码冗余。
所以 React 就把它定义为一个视图层的框架,它并不是什么问题都解决,我只帮你解决数据和视图在页面渲染的问题,至于组件之间传值我并不负责,我交给其它组件来做。
如果只有两层组件,借助 React 内部的传值机制就可以了,如果有多层组件,就是用Redux
等等数据层框架来辅助。
这也是为什么React
将自己称为一个视图层的框架
,而不是一个大型的完整的框架
,它会借助很多的辅助框架
。
# 函数式编程
面向测试的开发流程,前端自动化测试的时候,如果你的代码都是一个个函数,那么测试时就调用这个函数,查看输入输出即可,这样就给前端自动化测试带来很大的便利。