React列表渲染
在实际应用中, **数据展示** 是最基本的功能 。
1、React中使用 map()
进行列表的渲染。
react中遍历列表用 map ,记得给每一次添加的内容加上唯一标识,就是把key值插进去。 下面提出了三种方法,建议第二种,层次感高,代码复杂度低。
<div id="demoSky"></div>
<script type="text/babel">
let arr = ["galen", "sen", "uu", "lll"]
// 写法1
// let com6 = (
// // jsx 的区域,写jsx加个()比较好 jsx中插变量用{}
// <div>
// {
// arr.map((item, index) => {
// return (
// <p key={index}>{item}</p>
// )
// })
// }
// </div>
// )
// 写法2 先单独在外面计算完成,然后插入到jsx区域中
let newhtml=arr.map((item,index)=>{
return (
<p key={index}>{item}</p>
)
})
let com6 = (
<div>
{newhtml}
</div>
)
// 写法3 将计算写在一个函数里面,再将函数引入到jsx区域
// function list(){
// return arr.map((item,index)=>{
// return (
// <p key={index}>{item}</p>
// )
// })
// }
// let com6 = (
// <div>
// {list()}
// </div>
// )
// 写法4 用表格进行遍历 注意按照完整表格的html语法,thead tbody tfooter 建议写上
let arr = [
{ name: "sky1", age: 18 },
{ name: "sky2", age: 15 },
{ name: "sky3", age: 12 },
{ name: "sky4", age: 19 },
]
let newHtml = arr.map((item, index) => {
return (
<tr>
<td>{item.name}</td>
<td>{item.age}</td>
</tr>
)
})
let com7 = (
<table>
<tbody>
{newHtml}
</tbody>
</table>
)
ReactDOM.render(com6, document.querySelector("#demoSky"))
</script>
2、-----Keys-----
Keys 可以在 DOM中某些元素被 增加/删除 时,帮助 React 识别哪些元素发生了变化。因此要给数组中的每一个元素赋予一个唯一的标识。
一个元素的key
最好是这个元素在列表中拥有的一个独一无二的字符串。
观察下面代码,看看能运行成功吗?在return换行了。
解决方式:用个()包裹代码即可。
3、其他遍历、循环方式
除了map也可以用其他循环方式,比如下面的写法:
我试着自己用for循环写了一下:
<!-- 其他遍历列表的方式 -->
<script type="text/babel">
let arr = [
{ name: "sky1", age: 18 },
{ name: "sky2", age: 15 },
{ name: "sky3", age: 12 },
{ name: "sky4", age: 19 },
]
// 写法5:基本for循环
function funlist1(){
let html=[];
for(let i=0;i<arr.length;i++){
let list1=(
<tr key={i}>
<td>{arr[i].name}</td>
<td>{arr[i].age}</td>
</tr>
)
html.push(list1)
}
return html;
}
let comlist1=(
<table>
<tbody>
{funlist1()}
{console.log("0000000000000000")}
</tbody>
</table>
)
ReactDOM.render(comlist1,document.querySelector("#demoSky"))
</script>
React render重新渲染
我们通过一个小栗子:完成点击变色的demo,来看一下在react里面,render是怎么重新渲染的。
因为涉及到点击事件,所以我们先来看一下react中事件的触发机制。
1、事件
事件的添加,一定是小驼峰命名法(亲测,只能小驼峰)。
在react中,绑定使用的事件与传统js一样,不过有几注意项:
(1)事件名必须小驼峰式 。
(2)在调用方法的时候,不能使用传统的 " " 来引用方法,而要使用jsx中的 {} 来包裹方法 。
(3)调用的方法不能加(),否则,页面初始化时就会被自动调用 。
2、点击变色小demo
操作流程:
1、创建数据,然后使用map遍历;
2、给每一条遍历的数据加上一个点击事件;
3、创建一个变量iii来保存当前点击的下标;
4、创建style行内样式,三元运算,判断当前元素下边是否恒等于iii。如果恒等,说明用户点击的元素是应该设置样式的元素,反之则不设置;
5、出了一个bug,发现页面内容没有变化,原因是ReactDOM.render在页面初始化时已经执行一次了,再次点击时不会自动执行,所以我们需要在点击事件触发时重新执行一次rende方法。那么解决办法就是把render封装成一个函数,在每次点击的时候调用这个控制渲染的函数。
再次强调一下,封装好的函数至少调用一次,然后在点击触发事件时也要再次触发。
<!-- 一、render重复渲染 -->
<script type="text/babel">
var iii = -1;//数组下标是0~无穷大,以用来保存用户具体保存的内容
var arr = ["aaa", "bbb", "ccc", "ddd"];
function con1() {
let newList = arr.map((item, index) => {
return (
<h1 key={index} onClick={() => { iii = index; console.log(index, iii); renderDemo(); }} style={{ color: iii === index ? "#fd5b78" : "" }}>{item}</h1>
)
})
// 每次点击,把下标赋值给iii,然后通过iii来判断是否改变样式
// 不过,因为我们在初始化的时候,页面已经被渲染了,所以现在要考虑的一个问题是如何让render再次调用
// 处理办法,我们可以把render封成一个函数,重复调用
let com1 = (
<div>{newList}</div>
)
return com1;
}
function renderDemo() {
ReactDOM.render(con1(), document.querySelector("#demoSky"))
}
renderDemo();
</script>
3、用类来改变颜色
刚刚是用内联样式修改的,也可以那个用类也修改样式。
只要修改内联判断为className判断即可
style={{ color: iii === index ? "#fd5b78" : "" }}
改成
className={iii===index?"style_demp":""}
看一下效果:
对象的遍历
1、对象如何取值
两种方式 :1.用点的方式 ; 2.用[]来取值 。
也可以重新定义一个变量,被赋值对象里的某个key 。
var outval = "abc";
console.log(obj[outval] === obj["abc"]); //true
在实际项目中一般使用点,因为比较方便。但是如果key作为变量的话就不能使用点了,因为js会理解变量为对象的key值,造成混淆。
2、对象方法
object.keys() 返回一个数组类型,值是方法中对象的键(key)
Object.values() 返回一个数组类型,值是方法中对象的值(value)
Object.entries() 返回一个数组类型,值是方法中对象的键和值
3、遍历对象
<script>
// 以键key返回,然后遍历
Object.keys(obj).map((item,index)=>{
// console.log(item);//键名
console.log(obj[item]);//键值
})
// 以键值value返回,然后遍历
Object.values(obj).map((item,index)=>{
console.log(item)//键值
})
// 以键名、键值返回,然后遍历 用得不多
Object.entries(obj).map((item,index)=>{
// console.log(item);//键名+键值
console.log(item[1]);//可以一数组索引的形式选择key或者value
})
// 其实上面的写法就相当于:
[["name", "sky"], ["age", 18], ["sex", "女"]].map((item, index) => {
console.log(item);//键名
})
/*其实吧,我发现,原理是一样的,只不过通过不同的对象方法返回不同的数组,
然后用数组的 map() 方法进行遍历*/
</script>
4、React 遍历对象
之前学到的map是数组方法,那么如何在react中遍历对象呢?
<script type="text/babel">
var obj={
name:"sky",
age:18,
sex:"false",//直接写false,react不会渲染显示在页面上
}
function fun(){
return Object.keys(obj).map((item,index)=>{
return (
<p key={index}>{obj[item]}</p>
)
})
}
let list=(
<div>{fun()}</div>
)
ReactDOM.render(list,document.querySelector("#demoSky"))
</script>
面向组件编程
1、组件基本概念
组件是React中非常重要的概念。组件允许你将UI切分成一些独立的、可复用的部件,这样你就只需专注于构建每一个单独的部件。
组件,从概念上类似于 JavaScript 函数。它接受任意的入参(即 “props”),并返回用于描述页面展示内容的 React 元素。
官网:组件 & Props
react官方推荐的创建组件的方式有两种:函数组件 与 class 组件(常用)。
注意: 组件名称必须以大写字母开头。
React 会将以小写字母开头的组件视为原生 DOM 标签。例如,<div />
代表 HTML 的 div 标签,而<Welcome />
则代表一个组件,并且需在作用域内使用 Welcome。
2、函数组件 / 无状态组件
函数的名字首字母必须大写,然后return一段jsx语法代码块。
function MyCom() {
return jsx语法代码块
}
演示一下:
<div id="demoSky"></div>
<script type="text/babel">
function MyCom() {
return (
<p>一个无状态组件</p>
)
}
let com = (
<div>
{/*组件的本质是标签*/}
<MyCom></MyCom>
</div>
)
ReactDOM.render(com, document.querySelector("#demoSky"))
</script>
2.2 定义父子组件
通过创建多个组件来合成一个组件,即把组件的不同功能点进行分离。又可以说成组合组件、提取组件(详情请看官网)。
函数组件中的 父子组件 组件嵌套:
<div id="demoSky"></div>
<script type="text/babel">
function Son(){
return <p>我是子组件</p>
}
function Father(){
return <div>我是父组件<Son/>子组件在父组件里</div>
}
let com2=(
<div>
<Father/>
</div>
)
ReactDOM.render(com2, document.querySelector("#demoSky"))
</script>
3、ES6 class类组件 / 有状态组件
首先注意类名的首字母必须大写 。
一个组件类必须要实现一个 render 方法。这个方法必须要返回一个jsx元素。必须要用一个外层的jsx元素把所有的内容包裹起来,如果要返回并列的多个元素,需要有个父元素进行包裹。
class MyCom extends React.Component{
render(){
return <div>我是组件</div>
}
}
注意:原生 HTML 元素名以小写字母开头,而自定义的 React 类名以大写字母开头,比如 HelloMessage 不能写成 helloMessage。除此之外还需要注意组件类只能包含一个顶层标签,否则也会报错。
3.2类组件中的父子组件
<div id="demoSky"></div>
<script type="text/babel">
class Son2 extends React.Component {
render() {
return (
<div>子类组件</div>
)
}
}
class Father2 extends React.Component {
render() {
return (
<div>父类组件<Son2/>子在父中</div>
)
}
}
ReactDOM.render(<Father2/>, document.querySelector("#demoSky"))
</script>
props
组件中通过Props传入参数。
props 是组件对外的接口。使用props就可以从外部向组件内部进行数据传递,完成父组件传值给子组件的过程。
注意:props对于使用它的组件来说,是只读的,一旦赋值不能修改。也就是说props的值是不可变的,只能在渲染的时候传入无法动态赋值。
组件中无论是使用无状态组件,还是通过类组件声明,都决不能修改自身的 props 。
1、 props传入单个参数
当一个组件被注入props 值时,属性值来源于它的父级元素,从父级传递到子元素。
(1)无状态组件中使用props
组件在定义时就写入一个参数,然后在使用组件时传入一个值,在组建中通过props.title
来展示这个值。
<div id="demoSky"></div>
<script type="text/babel">
function MyCom3(props){
return (
<div>我是无状态组件中接收props传过来的参数
<p>{props.title}</p>
</div>
)
}
ReactDOM.render(<MyCom3 title="我是值"/>, document.querySelector("#demoSky"))
</script>
(2)类组件中使用props
在类组件中,接收props参数的方式是this.props.title
。然后当然不管是哪种创建组件的方式,都可以将传入的值单独以变量形式展示,然后传入。
<div id="demoSky"></div>
<script type="text/babel">
var name="变量---值值值";
class MyCom4 extends React.Component{
render(){
return(
<div>
<p>类组件中接收props参数:this.props.xxx</p>
<p>{this.props.title}</p>
</div>
)
}
}
let com4=<MyCom4 title={name}/>
ReactDOM.render(com4, document.querySelector("#demoSky"))
</script>
(3)组件props小练习
来个简单的小栗子,这里用的是函数组件方式,不过以后我们一般就不用这种方式了,用类组件比较多。
<div id="demoSky"></div>
<script type="text/babel">
var arr = [
{ title: "标题1", content: "内容简介1" },
{ title: "标题2", content: "内容简介2" },
{ title: "标题3", content: "内容简介3" },
{ title: "标题4", content: "内容简介4" },
{ title: "标题4", content: "内容简介4" },
]
// 根据数据动态生成内容 传多个数据
function ListDemo(props) {
return (
<div>
<h1 style={{display:"inline"}}>{props.title}</h1>
<span>{props.content}</span>
</div>
)
}
var newArr=arr.map((item,index)=>{
return(
// 返回这个组件,然后每次传递一个值
<ListDemo key={index} title={item.title} content={item.content}/>
// 如果这种情况下一次性传递多个值呢 使用es6的扩展语法
// <ListDemo key={index} {...item}/>
)
})
// newArr变量
ReactDOM.render(newArr, document.querySelector("#demoSky"))
</script>
2、props传入多个参数
用到了一个概念:扩展运算符。
扩展运算符(spread)是三个点(…)。它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列。
…运算符主要用于数组和对象的一些操作。
参考:JS扩展运算符的用途
参考:数组的扩展 阮一峰
参考:Js es6中扩展运算符(…)
在参数较多的时候可以把Object类型进行传递:
我的练习—用简单的方式向组件内部传递多个参数:
<div id="demoSky"></div>
<script type="text/babel">
function ListDemo2(props){
return(
<div>
<h1>{props.title}</h1>
<h1>{props.title2}</h1>
<h1>{props.title3}</h1>
</div>
)
}
// 如果希望往组件内部传递多个参数,我们可以把传递的数据定义成一个对象,
// 但是注意对象的key必须和向内部传递的数据名一致
let obj={
title:"内容1",
title2:"内容2",
title3:"内容3",
title4:"内容4",
title5:"内容5",
title6:"内容6",
title7:"内容7",
}
// 然后使用 ES6的扩展运算符... 向其内部进行传递obj
let com5=<ListDemo2 {...obj} />
ReactDOM.render(com5, document.querySelector("#demoSky"))
</script>
3、 Props默认值
(1)react版本 15x 以下的方法 ||
用管道符||
来进行判断:
(2)react版本 16x 以下的方法 defaultProps
有些时候,我们需要设置一些默认属性, 在父级组件未向子级组件传递数据时, 填充默认值。
(3)我的尝试
先用 || 试一下。
<div id="demoSky"></div>
<script type="text/babel">
function MyCom6(props){
props.text=props.text||"我是props默认值";
return(
<div>没有给组件传值--打印props默认值--
{props.text}为空。
</div>
)
}
ReactDOM.render(<MyCom6/>, document.querySelector("#demoSky"))
</script>
发现报错了。
为什么会报错?因为这种props默认值的写法:props.text=props.text||"我是props默认值";
,在高版本中不适用,如果想使用下面的这种写法,需要引入react低版本15x以下的包。
但我现在使用的react版本是16x,该怎么办呢? 用defaultProps
这种写法。
高版本 16x及以下(兼容性) 在组件之外,定义props默认值。
MyCom6.defaultProps={
text:'我是defaultProps设置的默认值'
}
完整代码如下:
<div id="demoSky"></div>
<script type="text/babel">
function MyCom6(props){
return(
<div>没有给组件传值--打印props默认值--
{props.text}
</div>
)
}
MyCom6.defaultProps={
text:'我是defaultProps设置的默认值'
}
ReactDOM.render(<MyCom6/>, document.querySelector("#demoSky"))
</script>
(4)补充
类方式创建使用defaultPropos进行默认值设置两种方式:
4、props 验证
15.5之前Props 验证使用 propTypes,它可以保证我们在应用组件的时候可以正确的传递值。
所以,现在如果我们想要使用prop-types,就要引入prop-types 库。
安装:npm install --save prop-types
然后如果是本地模式的话,就需要引入这个资源包:
<script src="./node_modules/prop-types/prop-types.js"></script>
如果是脚手架模式的话,直接rcc可以帮我们直接引入,就无须我们手动引入了。
react-developer-tools 谷歌浏览器开发者调试工具
安装:
进入谷歌浏览器扩展程序中打开开发者模式,选择加载以解压的扩展程序,添加文件。
模块 / 模块化 / 组件 / 组件化
模块与模块化
模块:向外提供特定功能的js文件,提高js的复用率,简化编写,提高运行效率。
用来封装能够重复使用的逻辑代码块。
模块化:当应用的js都是用js模块编写的,那么这个应用就是模块化应用。
组件与组件化
组件:用来实现页面局部功能效果的代码合集(html/css/js 组合技),简化复杂页面的编码,提高运行效率。
用来封装可以重复使用的ui代码块。
组件化:当应用多使用组件的方式来完成,这个应用就是一个组件化应用。
总结
元素(element)是构成 React 应用的最小砖块。想要将一个 React 元素渲染到根 DOM 节点中,只需把元素和“根” DOM 节点一起传入 ReactDOM.render()。
与浏览器的 DOM 元素不同,React 元素是创建开销极小的普通对象。React DOM 会负责更新 DOM 来与 React 元素保持一致。
ReactDOM.render(com, document.querySelector("#demoSky"))
然后我发现,这个传入的元素的写法有很多种,你可以传入一个变量,也可以是一个有返回值的函数,也可以是一个组件…
本文所用到的element的种类:
com(jsx语法变量) <MyCom4 title={name}/> fun() newArr(es6语法变量)
这节介绍了react中怎么遍历渲染列表以及如何通过render重新渲染;然后对对象的取值、方法、遍历有了加深版了解;然后知道了react中的组件以及父子组件、组件的合并使用、提取组件为更小的部分;然后是组件通过props传值,包括传入单个、多个参数,以及默认值的相关知识。
补充:let 箭头函数 函数组件
<div id="demoSky"></div>
<script type="text/babel">
let Son = (props) => {
return (
<div>
Son
{
props.con.map((item, index) => {
return (
<p key={index}>{item.val}{item.key}</p>
)
})
}
</div>
)
}
let Father = () => {
let arr = [
{ val: "111", key: "--1--" },
{ val: "222", key: "--2--" },
{ val: "333", key: "--3--" },
{ val: "444", key: "--4--" },
]
return (
<div>
Father
<Son con={arr} />
</div>
)
}
ReactDOM.render(<Father />, document.querySelector("#demoSky"))
</script>