哈喽大家好,能和大家见面啦,我依旧是那个可爱的秃头小兄弟。
今天又周四了啊,划水人的一天,开心。
话不多说哈,直接搂代码
1.react 的特点
1.采用组件化模式。声明式编程,提高开发效率及组件复用率
2.在 React Native 中可以使用 React 语法进行移动端开发
3.使用虚拟 DOM+优秀的 Diffing 算法,尽量减少与真实 DOM 的交互
2.关于虚拟 DOM
1.本质上是 object 类型的对象(一般对象)
2.虚拟 DOM 比较轻,真实 DOM 比较重,因为虚拟 DOM 是 React 内部在用,无需真实 DOM 上嫩么多的属性
3.虚拟 DOM 最终会被 React 转化为真实 DOM,呈现在页面上
// 1.创建虚拟dom
const VDOM = (
<h1>
<span>hello</span>
</h1>
);
// 2.渲染虚拟DOM到页面
ReactDOM.render(VDOM, document.getElementById("test"));
const demo = document.getElementById("demo");
console.log(typeof VDOM); //object
console.log(VDOM instanceof Object); //true
3.jsx 语法规则
jsx语法规则
1.定义虚拟DOM时,不能写引号
2.标签中混入js表达式,插值时要用{}
3.样式的类名不要用class,要用className
4.内联样式要用style={{key:value}},多单词要用小驼峰
5.只有一个根标签
6.标签必须闭合,单标签可使用自闭合
7.标签首字母
(1)若小写字母开头,则将改标签转为html中同名标签,若html中没对应标签,则标错
(2)若大写字母开头,react就去渲染对应的组件,若无定义,则报错
// 1.创建虚拟dom
const VDOM = (
<div>
<ul>
<li className="sty">{dataObj.name}</li>
<li style={{ color: "#fff", backgroundColor: "#666" }}>{dataObj.age}</li>
<li>{dataObj.sex}</li>
<li>{dataObj.msg.toLocaleLowerCase()}</li>
<li>{dataObj.msg.toLocaleUpperCase()}</li>
<li>{dataObj.msg.toLowerCase()}</li>
</ul>
<h2><input type="text" /></h2>
</div>
);
4.语句与表达式的区别
一定要区分:js语句(代码)与js表达式
1.表达式:一个表达式会产生一个值,可以放在任何一个需要值的地方
(1)a (2)a+b (3)demo(1) (4)arr.map (5)function test(){}
2.语句(代码)
下面这些都是语句(代码)流程语句,控制代码走向的
(1)if()
(2)for(){}
(3)switch(){case:xxx}
5.组件实例的三大特性
1.state
// 严重注意:状态必须通过setState进行更新,且更新是一种合并,不是替换
this.setState({ isHoet: !isHoet })
2.props
// props接收的数据是只读的,不能在内部修改数据
3.ref
//字符串ref
//1.给按钮命名
<input type="text" ref='inp2' onBlur={this.Blurs}/>
//2.使用refs获取到数据
let { inp2 }=this.refs
//回调函数ref
<input type="text" ref={c => this.inp2 = c} onBlur={this.Blurs} />
//回调函数接收的参数c/currentNode就是当前元素节点,this.inp2就是给类的实例对象加上一个inp2,获取到值
let { inp1 } = this
//获取到inp1的值
//回调函数更新的时候会调用两次,先清空成null,再获取到节点。
//class绑定函数的方式可以解决这个问题
<input type="text" ref={this.saveInput} />
saveInput = (c) => {
console.log("@", c);
}
createRef 的使用
//1.给class函数类的实例对象身上加一个myRef属性
myRef = React.createRef()
//2.在表单绑定
<input type="text" ref={this.myRef2} onBlur={this.Blurs} />
//3. 获取到值
Blurs = () => {
alert(this.myRef2.current.value);
}
// React.createRef调用后可以返回一个容器,该容器可以存储被ref所标识的节点,缺点在于他是专用的,想要获取多个input值,需要再定义myRef2 = React.createRef()
6.事件处理
(1)通过onCxxx属性指定事件处理函数(注意大小写)
<input type="text" onBlur={this.Blurs} />
a.React使用的是自定义合成事件,而不是使用的原生DOM事件——为了更好的兼容性
b.React中的事件是通过事件委托方式处理的(委托给组件最外层的元素)——为了高效
(2)通过event.target得到发生事件的DOM元素对象——不要过度使用ref
Blurs = (event) => {
console.log(event.target.value);
}
7.高阶函数:函数的柯里化
高阶函数:如果一个函数符合以下2个规范中的任何一个,那该函数就是高阶函数
1.若A函数,接收的参数是一个函数,那么A函数就可以称为高阶函数
2.若A函数,调用的返回值依然是一个函数,那么A就可以称之为高阶函数
常见的高阶函数有:Promise setTimeout arr.mao()等等
函数的柯里化:通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式
// 正常写法
function sum(a, b, c) {
return a + b + c;
}
let result = sum(1, 2, 3);
console.log(result);
// 函数柯里化
function msg(a) {
return (b) => {
return (c) => {
return a + b + c;
};
};
}
let result2 = msg(1)(2)(3);
console.log(result2);
当事件需要传参数时,可使用高阶写法,处理函数中需要返回一个函数,也可以用回调函数的方式
//高阶函数
<input onChange={this.handleCheck(id)} />;
handleCheck = (id) => {
return (event) => {};
};
//回调函数
<button onClick={() => this.handleDelete(id)}>删除</button>;
handleDelete = (id) => {};
8.生命周期
1. 组件从创建到死亡它会经历一些特定的阶段。
2. React 组件中包含一系列勾子函数(生命周期回调函数), 会在特定的时刻调用。
3. 我们在定义组件时,会在特定的生命周期回调函数中,做特定的工作。
生命周期回调函数=》生命周期钩子函数=》生命周期函数=》生命周期钩子
生命周期的三个阶段(旧)
// 卸载组件调用
ReactDOM.unmountComponentAtNode(document.getElementById("test"))
// 组件挂载完毕
componentDidMount() {}
// 组件将要销毁
componentWillUnmount() {}
// 初始化渲染,状态更新之后
render(){}
/*1。初始化阶段: 由ReactDoM.render()触发--初次渲染
1.constructor()
2.componentWillMount()
3.render()
4.componentDidMount() =====> 常用
般在这个例子中做一些初始化的事,例如: 开启定时器、发送网络请求、订阅消息
2,更新阶段: 由组件内部this.setSate()或父组件render触发
1.shouldComponentUpdate()
2.componentWi1lUpdate()
3.render() =====> 必须使用的一个
4.componentDidupdate()
3.卸载组件: 由ReactDoM.unmountComponentAtNode()触发
1.componentwillUnmount() =====> 常川
一般在这个钩子中做一些收尾的事,例如: 关闭定时器、取消订阅消息*/
生命周期的三个阶段(新)
1. 初始化阶段: 由ReactDOM.render()触发---初次渲染
1.constructor()
2.getDerivedStateFromProps
3.render()
4.componentDidMount() =====>
常用一般在这个例子中做一些初始化的事,例如: 开启定时器、发送网络请求、订阅消息
2. 更新阶段: 由组件内部this.setSate()或父组件重新render触发
1.getDerivedStateFromProps
2.shouldComponentUpdate()
3.render()//必须使用的一个
4.getSnapshotBeforeUpdate
5.componentDidUpdate()
3. 卸载组件: 由ReactDOM.unmountComponentAtNode()触发
1.componentwillUnmount() =====> 常川
一般在这个钩子中做一些收尾的事,例如: 关闭定时器、取消订阅消息
总结:新与旧的相比,即将要废弃三个钩子函数
componentWillMount()
componentWi1lUpdate()
componentWillReceiveProps
现在使用会出现警告,下一个大版本需要加上 UNSAFE\_前缀才能使用,以后可能会被彻底废弃,不建议使用。
同时提出俩个新的钩子
getDerivedStateFromProps
getSnapshotBeforeUpdata
9.key
总结:数据的更新就是使用diffing算法,通过对比key看看是不是同一个对象,
如果是同一个,数据如果没改变直接复用之前的DOM,如果改变了就把新的虚拟DOM转化为真实DOM
如果不是同一个,直接创建新的DOM
使用index作为key值
初始数据
{ id: 1, name: '小站', age: 18 },
{ id: 2, name: '小红', age: 19 },
初始的虚拟dom
<li key=0>1---小站--18}</li>
<li key=1>1---小红--19}</li>
更新后的数据
{ id: 3, name: '笑了', age: 20 }
{ id: 1, name: '小站', age: 18 },
{ id: 2, name: '小红', age: 19 },
更新后的虚拟dom
<li key=0>1---笑了--20}</li>
<li key=1>1---小站--18}</li>
<li key=2>1---小红--19}</li>
使用id作为key值
初始数据
{ id: 1, name: '小站', age: 18 },
{ id: 2, name: '小红', age: 19 },
初始的虚拟dom
<li key=1>1---小站--18}</li>
<li key=2>1---小红--19}</li>
更新后的数据
{ id: 3, name: '笑了', age: 20 }
{ id: 1, name: '小站', age: 18 },
{ id: 2, name: '小红', age: 19 },
更新后的虚拟dom
<li key=3>1---笑了--20}</li>
<li key=1>1---小站--18}</li>
<li key=2>1---小红--19}</li>
1.react/vue中的key有什么作用(key的内部原理是什么)
2.为什么遍历列表时,做好不要用index
1.虚拟DOM中key的作用
(1)简单的说:key是虚拟DOM对象的标识,在更新显示时key起着极其重要的作用
(2)详细的说:当状态中的数据发生改变时,react会根据“新数据”生成“新的虚拟DOM”,
随后React进行“新虚拟DOM”与“旧虚拟DOM”的diff比较,比较规则如下:
a. 旧虚拟DOM中找到了与新虚拟DOM相同的key;
(1)若虚拟DOM中内容没变,直接使用之前的真实DOM
(2)若虚拟DOM中内容变了,则生成新的真实DOM,随后替换掉页面中之前的真实DOM
b.旧虚拟DOM中未找到与新虚拟DOM相同的key
根据数据创建新的真实DOM,随后渲染到页面
2.用index作为key可能会引发的问题
1.若对数据进行 逆向添加,逆向删除等破坏顺序操作
会产生没有必须的真实DOM更新==》界面效果没问题,但效率低
2.如果结构中还包含输入类的DOM
会产生错误的DOM更新==》界面有问题
3.注意!如果不存在对数据的逆序添加,逆序删除等破坏顺序的操作
仅用于渲染列表用于展示,使index作为key是没有问题的
3.开发中如何选择key
1.最好使用每条数据的唯一标识作为key,比如id 手机号,身份证号,学号等唯一值
2.如果确定只是简单展示数据,用index也可以