React(CDN方式)
一、依赖说明配置
1.依赖引入(react16版本,这里是)
<!-- 需要在引入 React拓展库之前引入 React核心库-->
<!-- React核心库 -->
<script src="../js/react.development.js"></script>
<!-- 引入react-dom 用于支持react操作DOM -->
<script src="../js/react-dom.development.js"></script>
<!-- 引入babel 用于将jsx转换成js -->
<script src="../js/babel.min.js"></script>
#二、老样子,Hello React,jsx的语法(运行时,会编译成js)
<body>
<div id="app">
</div>
<script type="text/babel"> /* 此处一定要写 type="text/babel"*/
const my_id = 'aTGuiGu';
const my_data = 'Hello React';
const VDOM = ( /* 此处不需要写引号,应为不是字符串 */
<h3 className="red" id={my_id.toLocaleLowerCase()}>
<span style={{color:'red'}}>{my_data}</span>
</h3>
)
ReactDOM.render(VDOM, document.querySelector('#app'));
</script>
</body>
a.注意点(渲染DOM分两步)
// 1.创建虚拟DOM
const VDOM = <h2>hello React</h2>
// 2.渲染虚拟DOM到页面
ReactDOM.render(VDOM, document.querySelector('#app'));
// 1.在虚拟DOM中渲染数据时,使用{},只能填写能返回一个值的表达式
// 2.样式的类名指定不要用class,要用className
// 3.内敛样式,使用style={{key:value}}的形式,多个单词属性使用小驼峰
// 4.虚拟DOM只能有一个根标签
// 5.标签必须闭合
// 6.标签首字母大写,表示使用组件,小写直接转换成html标签,转换失败时,报错
1.在js中渲染虚拟DOM
// 1.创建虚拟DOM
const VDOM = React.createElement('h3',{id:'title'},React.createElement('span',{},'Hello React'))
// 2.渲染虚拟DOM到页面
ReactDOM.render(VDOM, document.querySelector('#app'));
2.React自动遍历虚拟DOM中数组
<script type="text/babel">
const data = ['Angular','React','Vue'];
const VDOM = (
<div>
<h1>前端js框架列表</h1>
<ul>
{data.map((item,index)=><li key={index}>{item}</li>)}
</ul>
</div>
);
ReactDOM.render(VDOM,document.getElementById('app'))
</script>
二、组件定义
1.函数式组件定义
<script type="text/babel">
function ChirldComponent(){
console.log(this); // 此处的this是undefined,因为babel编译开启后开启了严格模式
return <h3>This is a chirld component</h3>
}
ReactDOM.render(<ChirldComponent/>,document.getElementById('app'))
// 1. React解析组件标签,找到了ChirldComponent组件
// 2. 调用函数,返回的虚拟DOM渲染成真实DOM呈现在页面中
</script>
</body>
2.类是组件
// 1. 创建类式组件
class ChirldComponent extends React.Component {
render() {
console.log(this)
return (
<div>
我是类式组件
</div>
)
}
};
ReactDOM.render(<ChirldComponent/>,document.getElementById('app'))
// 1. React解析组件标签,找到了ChirldComponent组件
// 2. new 出该类的实例,调用实例的render()
// 3. 将返回的虚拟DOM渲染成真实DOM呈现在页面中
三、组件三大属性
1.组件state属性
注意:
1.组件中render方法中的this为组件实例对象
2.组件自定义的方法中this为undefined,如何解决?
a)强制绑定this: 通过函数对象的bind()
b)箭头函数
3.状态数据,不能直接修改或更新
<script type="text/babel">
class Weather extends React.Component {
/*constructor(props) {
super(props);
this.state = {
isHot: true,
wind:'微风'
};
}*/
state = {
isHot: true,
wind: '微风'
}
render() {
// 每次渲染都会调用 render
const { isHot,wind } = this.state;
return (
<h3 ref="title" onClick={this.changeHot.bind(this)}>
今天天气很{isHot ? '炎热' : '寒冷'},{wind}
</h3>
)
}
changeHot() {
// 该函数由window调用,因为类中的方法默认开启了严格模式,so 这里this=>undefined
this.setState({
isHot:!this.state.isHot
});
}
};
ReactDOM.render(<Weather />, document.getElementById('app'));
</script>
2.组件props属性
a.新的依赖引入
<!-- 用于对组件标签属性进行限制 -->
<script src="../js/prop-types.js"></script>
b.props在子组件中基本使用
标签体内容,就是标签的children属性,可以在props获取
<script type="text/babel">
class Person extends React.Component {
// 对组件属性进行类型、必要性的限制
static propTypes = {
name: PropTypes.string.isRequired,
age:PropTypes.number,
sex:PropTypes.string,
speak:PropTypes.func
}
// 指定标签属性默认值
static defaultProps = {
sex:'0',
age:18
}
render() {
let { name, age, sex } = this.props;
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{sex == 1 ? '男' : '女'}</li>
<li>年龄:{age + 1}</li>
</ul>
)
}
}
ReactDOM.render(<Person name="Jone" speak={speak} />, document.getElementById('app'));
function speak(){
console.log('我说话了');
}
</script>
c.props是否在构造器中使用
constructor(props){
// 是否将接收到props传递给super,取决于是否希望在构造器中通过this访问props
super(props);
console.log(this.props);
}
// 建议:一般不写contructor函数
d.在函数式组件中使用props属性
<script type="text/babel">
Person.propTypes = {
name: PropTypes.string.isRequired,
age: PropTypes.number,
sex: PropTypes.string,
speak: PropTypes.func
}
Person.defaultProps = {
sex: '0',
age: 18
}
function Person(props) {
let { name, sex, age } = props;
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{sex == 1 ? '男' : '女'}</li>
<li>年龄:{age + 1}</li>
</ul>
)
}
ReactDOM.render(<Person name="Jone" speak={speak} />, document.getElementById('app'));
function speak() {
console.log('我说话了');
}
</script>
3.组件refs属性
a.refs_字符串类型
跟vue的refs相似,但官方已经不建议了,影响性能,建议避免这种方式使用ref
<script type="text/babel">
class Demo extends React.Component {
render() {
return (
<div>
<input ref="input1" type="text" placeholder="点击按钮提示数据"/>
<button onClick={this.showData}>点我提示数据</button>
<input onBlur={this.showData2} ref="input2" type="text" placeholder="失去焦点提示数据"/>
</div>
)
}
showData = ()=>{
alert(this.refs.input1.value);
}
showData2 = ()=>{
alert(this.refs.input2.value);
}
}
ReactDOM.render(<Demo />,document.getElementById('app'));
</script>
b.refs_回调函数
就是利用回调函数,直接将传给回调函数的参数(真实DOM对象),赋值给组件对象的一个属性
<script type="text/babel">
class Demo extends React.Component {
state = {
num: 1
}
render() {
// 内联式回调函数,每次渲染时React会自动调用两次ref的回调函数,第一次传值为null,第二次传值的DOM节点对象
return (
<div>
{this.state.num}
{/* <input ref={el =>{ console.log(el);this.input1 = el}} type="text" placeholder="点击按钮提示数据" />*/}
<input ref={this.saveInput} type="text" placeholder="点击按钮提示数据" />
<button onClick={this.showData}>点我提示数据</button>
</div>
)
}
showData = () => {
let { num } = this.state;
this.setState({
num: num + 1
})
}
saveInput = (c) => {
this.input1 = c;
console.log(c);
}
}
ReactDOM.render(<Demo />, document.getElementById('app'));
</script>
c.refs_中的createRef
缺点,每次需要使用ref标记对象时,都需要提起定义装载ref的容器,newRef = React.createRef(),使用方式ref对象与回调函数类似
<script type="text/babel">
class Demo extends React.Component {
state = {
num: 1
}
myRef = React.createRef();
myRef2 = React.createRef();
render() {
return (
<div>
{this.state.num}
<input ref={this.myRef} type="text" placeholder="点击按钮提示数据" />
<button onClick={this.showData}>点我提示数据</button>
<input ref={this.myRef2} type="text" onBlur={this.showData2} placeholder="失去焦点提示数据" />
</div>
)
}
showData = () => {
let { num } = this.state;
this.setState({
num: num + 1
})
console.log(this.myRef.current.value);
}
showData2 = ()=>{
console.log(this.myRef2.current.value);
}
}
ReactDOM.render(<Demo />, document.getElementById('app'));
</script>
四、事件处理
a.基本使用
<script type="text/babel">
class Demo extends React.Component {
myRef = React.createRef();
render() {
return (
<div>
<input ref={this.myRef} type="text" placeholder="点击按钮提示数据" />
<button onClick={this.showData}>点我提示数据</button>
<input type="text" onBlur={this.showData2} placeholder="失去焦点提示数据" />
</div>
)
}
showData = () => {
console.log(this.myRef.current.value);
}
showData2 = (event) => {
console.log(event.target.value)
}
}
ReactDOM.render(<Demo />, document.getElementById('app'));
</script>
b.注意点
/* (1).通过onXxx属性指定事件处理函数(注意大小写)
a.React使用的是自定义(合成)事件,而不是使用的原生DOM事件 --- 为了更好的兼容性
b.React中的事件是通过事件委托方式处理的(委托给组件最外层的原生DOM事件) --- 为了高效
(2).通过event.target得到发生事件的DOM元素对象 -- 不要过度使用ref
*/
五、收集表单
1.非受控表单
非受控组件,只在提交表单时获取数据
<script type="text/babel">
class Login extends React.Component {
username = React.createRef();
password = React.createRef();
render() {
return (
<form action="http://www.baidu.com" onSubmit={this.submitUserInfo}>
<input ref={this.username} type="text" name="username" placeholder="Enter username ..." /><br/>
<input ref={this.password} type="text" name="password" placeholder="Enter password ..."/><br/>
<button>登录</button>
</form>
)
}
submitUserInfo = (e)=>{
e.preventDefault();
alert(`用户名:${this.username.current.value}\n密码:${this.password.current.value}`);
}
}
ReactDOM.render(<Login />, document.getElementById('app'));
</script>
2.受控表单
<!-- 受控组件,实时将组件的输入维护到组件状态中 -->
<!-- 函数的柯里化:通过通过函数调用继续返回函数的方式,实现多个接收参数,最后统一处理的函数编码形式 -->
<script type="text/babel">
class Login extends React.Component {
// 初始化状态
state = {
username: '',
password: ''
}
render() {
return (
<form action="http://www.baidu.com" onSubmit={this.submitUserInfo}>
<input onChange={this.saveFormItem('username')} type="text" name="username" placeholder="Enter username ..." /><br />
<input onChange={this.saveFormItem('password')} ref={this.password} type="text" name="password" placeholder="Enter password ..." /><br />
{/*<input onChange={(e) => this.saveFormItem('username',e)} type="text" name="username" placeholder="Enter username ..." /><br />*/}
<button>登录</button>
</form>
)
}
saveFormItem = (formItem) => {
// let formItem = e.target.getAttribute('name');
// this.setState({
// [formItem]:e.target.value
// })
return (event) => {
this.setState({
[formItem]: event.target.value
})
}
}
// saveFormItem = (formType, e) => {
// this.setState({
// [formType]: e.target.value
// })
// }
submitUserInfo = (e) => {
e.preventDefault();
let { username, password } = this.state;
alert(`用户名:${username}\n密码:${password}`);
}
}
ReactDOM.render(<Login />, document.getElementById('app'));
</script>
六、生命周期
1.旧生命周期(React_16)
a.图示
b.代码测试
<script type="text/babel">
class Count extends React.Component {
state = {
count: 0
}
constructor(props) {
super(props);
console.log('constructor');
}
add = (e) => {
const { count } = this.state;
this.setState({
count: count + 1
})
}
death = () => {
ReactDOM.unmountComponentAtNode(document.getElementById('app'));
}
force = () => {
this.forceUpdate();
}
componentWillMount() {
console.log('componentWillMount');
}
render() {
console.log('render');
const { count } = this.state;
return (
<div>
<h2>当前求和为{count}</h2>
<button onClick={this.add}>点我加1</button>
<button onClick={this.death}>unmount</button>
<button onClick={this.force}>不更改任何状态中的数据,强制更新一下</button>
</div>
)
}
componentDidMount() {
console.log('componentDidMount')
}
shouldComponentUpdate(...args) {
console.log('shouldComponentUpdate');
// 为true 时,才会去更新页面,调用render();
return false;
}
componentWillUpdate() {
console.log('componentWillUpdate');
}
componentDidUpdate() {
console.log('componentDidUpdate');
}
componentWillUnmount() {
console.log('componentWillUnmount');
}
}
class A extends React.Component {
state ={ carName:'奔驰'}
changeCar = ()=>{
this.setState({
carName:'奥托'
})
}
render() {
return (
<div>
<div>我是A组件</div>
<button onClick={this.changeCar}>换车</button>
<B carName={this.state.carName}/>
</div>
)
}
}
class B extends React.Component {
// 首次传递props不会调用该钩子
componentWillReceiveProps(props){
console.log('B--componentWillReceiveProps',props)
}
shouldComponentUpdate(){
console.log('B-shouldComponentUpdate');
return true;
}
componentWillUpdate() {
console.log('B-componentWillUpdate');
}
componentDidUpdate() {
console.log('B-componentDidUpdate');
}
render() {
return (
<div>我是B组件,{this.props.carName}</div>
)
}
}
ReactDOM.render(<A />, document.getElementById('app'));
</script>
c.生命周期流程
1. 初始化阶段: 由ReactDOM.render()触发---初次渲染
1.constructor()
2.componentWillMount()
3.render()
4.componentDidMount()
2. 更新阶段: 由组件内部this.setSate()或父组件重新render触发
1.shouldComponentUpdate()
2.componentWillUpdate()
3.render()
4.componentDidUpdate()
3. 卸载组件: 由ReactDOM.unmountComponentAtNode()触发
componentWillUnmount()
2.新生命周期(React_17)
a.图示
b.代码测试
<script type="text/babel">
class Count extends React.Component {
state = {
count: 0
}
constructor(props) {
super(props);
console.log('constructor');
}
add = (e) => {
const { count } = this.state;
this.setState({
count: count + 1
})
}
death = () => {
ReactDOM.unmountComponentAtNode(document.getElementById('app'));
}
force = () => {
this.forceUpdate();
}
// 必须返回一个状态或者null, 若state的值在任何时都取决于props,那么可以使用getDerivedStateFromProps
// static getDerivedStateFromProps(props, state) {
// console.log('getDerivedStateFromProps', props, state);
// // return props;
// return null;
// }
shouldComponentUpdate(...args) {
console.log('shouldComponentUpdate');
// 为true 时,才会去更新页面,调用render();
return true;
}
render() {
console.log('render');
const { count } = this.state;
return (
<div>
<h2>当前求和为{count}</h2>
<button onClick={this.add}>点我加1</button>
<button onClick={this.death}>unmount</button>
<button onClick={this.force}>不更改任何状态中的数据,强制更新一下</button>
</div>
)
}
// 更新之前的快照,可用于返回一个值,例如:保留之前滚轮的位置,返回值传递给componentDidUpdate
getSnapshotBeforeUpdate(...args){
console.log('getSnapshotBeforeUpdate',args);
return 'jiang';
}
componentDidMount() {
console.log('componentDidMount')
}
componentDidUpdate(preProps,preState,SnapshotValue) {
console.log('componentDidUpdate',{preProps,preState,SnapshotValue});
}
componentWillUnmount() {
console.log('componentWillUnmount');
}
}
ReactDOM.render(<Count count='199' />, document.getElementById('app'));
</script>
3.生命周期(getSnapshotBeforeUpdate使用)
保持浏览时内容不滑动,保留之前浏览位置
css
<style>
.news_list {
width: 200px;
height: 150px;
border: solid 1px gray;
overflow: auto;
}
.news {
height: 30px;
}
</style>
jsx
<script type="text/babel">
class NewsList extends React.Component {
state = {
newsArr:[]
}
list = React.createRef();
componentDidMount(){
setInterval(()=>{
let {newsArr} = this.state;
const news = '新闻' + (newsArr.length + 1);
this.setState({
newsArr:[news,...newsArr]
})
},1000)
}
render() {
return (
<div className="news_list" ref={this.list}>
{this.state.newsArr.map((n,index)=>{
return (
<div className="news" key={index}>{n}</div>
)
})}
</div>
)
}
getSnapshotBeforeUpdate(){
return this.list.current.scrollHeight;
}
componentDidUpdate(prevProps,prevState,prevHeight){
this.list.current.scrollTop += this.list.current.scrollHeight - prevHeight;
}
}
ReactDOM.render(<NewsList />,document.getElementById('app'));
</script>
效果:不断产生新的新闻时,保留之前浏览位置
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FeHQab7T-1648391539895)(C:\Users\小江\AppData\Roaming\Typora\typora-user-images\image-20220327222119065.png)]
七、其它
1.DOM的Diffing算法
在新旧虚拟DOM比较时,会忽略输入元素的输入,Diffing对比的最小粒度时节点,且时深层次比较
<script type="text/babel">
class Time extends React.Component {
state = { date: new Date() }
componentDidMount() {
setInterval(() => {
this.setState({
date: new Date()
})
}, 1000);
}
render() {
return (
<div>
<h1>hello</h1>
<input type="text" />
<span>
现在是:{this.state.date.toTimeString()}
<input type="text" />
</span>
</div>
)
}
}
ReactDOM.render(<Time />, document.getElementById('app'));
</script>
2.key的作用
循环节点中由输入的节点时,数据逆序插入是,必须用数据的id,不能用key,
当只有数据展示时,末尾添加数据,可用key=index
结论,最好别用key = index,用id= key
推荐一个生成唯一标识的库 uuid.js
代码验证
<script type="text/babel">
// Diffing对比的最小粒度时节点
// 循环节点中由输入的节点时,数据逆序插入是,必须用数据的id,不能用key,
// 当只有数据展示时,末尾添加数据,可用key=index
// 结论,最好别用key = index,用id= key
class Person extends React.Component {
state = {
persons: [
{ id: 1, name: '小张', age: 18 },
{ id: 2, name: '小李', age: 19 }
]
}
render() {
return (
<div>
<h2>展示人员信息key = id</h2>
<ul>
{this.state.persons.map((item, index) => {
return <li key={item.id}><input type="text" /> {item.name}--{item.age}</li>
})}
</ul>
<button onClick={this.addPeron}>添加一个小王</button>
<h2>展示人员信息key = index</h2>
<ul>
{this.state.persons.map((item, index) => {
return <li key={index}><input type="text" /> {item.name}--{item.age}</li>
})}
</ul>
<button onClick={this.addPeron}>添加一个小王</button>
</div>
)
}
addPeron = ()=>{
let {persons} = this.state;
const p = {id:persons.length + 1,name:'小刘',age:23};
this.setState({
persons:[p,...persons]
})
}
}
ReactDOM.render(<Person />, document.getElementById('app'));
</script>