谈谈JSX
-
引入原因:虽然浏览器只能识别JS语法,但是操作原生的JS语法非常繁琐,效率低下,故此有了JSX,目的就是方便书写,提高效率,所以需要依赖一个babel(将JSX语法转换为JS语法),script标签type类型为text/babel
-
本质:React.createElement(component,props,...children)的语法糖,标签名可以任意,一般大写的表示组件标签
-
作用:简化创建虚拟DOM
3.1写法:
var ele=<h1>hello jsx! </h1>
注意1:他不是字符串,也不是HTML/XML标签
注意2:他最终产生的就是一个JS对象
4.规则
1.定义虚拟dom时,不要写引号
2.标签中混入JS表达式时要用{}
3.样式的类名不要用class,而是使用className代替
4.内联样式,使用style={{key:value}}形式
5.只有一个根标签,且标签闭合
6.标签首字母,小写时表示转为html同名标签,大写时为对应的组件标签
组件分类
函数式组件:一个函数就是一个组件,return一份DOM结构
特点:
1.没有生命周期,也会被更新并挂载,但是没有生命周期函数
2.没有this(组件实例)
3.没有内部状态(state)
4.16.8版本以后有hooks弥补一些缺陷
优点:轻量,如果组件没有涉及内部状态,只是用来渲染数据,那么就用函数式组件,性能较好
基本结构:
// functional component
function Welcome(props) { return <h1>Hello, {props.name}</h1>; }
类组件:分为普通类组件(React.Component)以及纯类组件(React.PureComponent)
区别
背景:先了解下React生命周期函数shouldComponentUpdate,这个函数返回一个布尔值,
如果返回true,那么当pros或者state改变时候进行更新;返回false,当props或state改变时候不更新,默认返回true。
这里的更新不更新,其实说的是执不执行render函数,如果不执行render函数,那么该组件和其子组件都不会重新渲染
不同点:
1.继承PureComponent时,不能再重写shouldComponentUpdate
2.React.PureComponent基于shouldComponentUpdate做了一些优化,通过props和state的浅比较来实现shouldComponentUpdate,
也就是说,如果是引用类型的数据,只会比较是不是同一个地址,而不会比较具体的地址存的数据是否完全一致
基本结构:
class ListOfWords extends React.PureComponent {
render() { return <div>PureComponent渲染结果:{this.props.words.join(',')}</div>; } }
class WordAdder extends React.Component {
constructor(props) {
super(props);
this.state = { words: ['marklar'] };
this.handleClick = this.handleClick.bind(this); }
handleClick() {
// This section is bad style and causes a bug const words = this.state.words;
words.push('marklar');
this.setState({words: words}); }
render() {
// slice() 方法返回一个新的数组对象
return (
<div> <button onClick={this.handleClick}>click</button>
<div>Component渲染结果:{this.state.words.join(',')}</div>
<ListOfWords words={this.state.words} />
<ListOfWords words={this.state.words.slice(0)} /> </div>
);
} }
ReactDOM.render( <WordAdder />, document.getElementById('root') )
组件实例对象三大属性
state
state是组件实例对象最重要的属性,必须是对象的形式
组件被称为状态机,通过更改state的值来达到更新页面
组件render中的this指向组件实例对象
状态数据的改变不能直接赋值,需要用setState()方法
组件中this为undefined,怎么解决(关键点是将this指向为当前的实例对象)
将自定义的函数改为表达式+箭头函数的形式(推荐)
handleClick = () => { console.log(this.state.count) }
在构造器中用bind()强制绑定this
this.handleClick = this.handleClick.bind(this)
函数科里化,即函数中返回一个箭头函数
handleClick() {
// 这里的 this 指向是什么?那就看是谁调用的!
return () => { console.log(this.state.count)
} }
在标签体中使用箭头函数调用,或者使用bing绑定this
<button onClick={() => this.handleClick()}>+1</button>
<button onClick={this.handleClick.bind(this)}>+1</button>
props
简介,类的继承子类,必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类自己的this对象,
必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法。
如果不调用super方法,子类就得不到this对象
props可以传递任何数据类型,并且props是只读的(单向数据流),所有的React组件必须像纯函数那样使用它们的props
props的传递:通过标签属性从组件外向组件内传递数据
ReactDOM.render(<Person name="Jack" age="19" sex="man" />, document.getElementById('test1'))//单个传递
const p = { name: '老王', age: 30, sex: 'man' }
ReactDOM.render(<Person {...p}/>, document.getElementById('test2'))//批量传递
props的验证:使用propType属性并配合prop-types三方库实现prop验证(不需要另外下载,已集成在脚手架中)
给类自身添加静态属性(static)static propTypes={name:PropTypes.string,age:PropTypes.number}
refs
认识:refs存放是的所有使用了ref标识的对象,refs常用于管理焦点、文本选择、媒体播放、强制动画、表单校验等
字符串形式的ref
定义:直接在标签上写ref=‘xxx’
使用:this.refs.xxx
注意:官方不推荐此方式,推荐使用回调函数或者createRef()代替
回调函数形式的ref
定义:自己定义,但是自己不调用,别人帮你调用
使用:利用箭头函数没有this特性,把当前的ref所在的节点当作函数的实参传递给你所定义的那个属性
class Demo extends React.Component {
render() {
// 箭头函数没有this,往外找是Demo的实例对象, 把当前的ref所在的节点当作函数的实参传给了input1属性
return (
<div>
<input ref={currentNode => this.input1 = currentNode} type="text" placeholder="点击按钮提示数据"/>
<button onClick={this.showData}>点击提示左侧的数据</button>
</div> ) }
showData = () => { const {input1} = this; console.log(this); console.log(input1.value); } }
ReactDOM.render(<Demo />, document.getElementById('test'));
createRef创建ref
定义:React.createRef调用后可以返回一个容器,该容器可以存储被ref所标识的节点,不同的节点不能使用同一个,是覆盖操作,会替换之前的ref
使用
class Demo extends React.Component {
// React.createRef调用后可以返回一个容器,该容器可以存储被ref所标识的节点,不同节点不能使用同一个,是覆盖操作,会替换掉之前的那个ref myRef = React.createRef(); myRef2 = React.createRef();
render() {
return (
<div>
<input ref={this.myRef} type="text" placeholder="点击按钮提示数据"/>
<button onClick={this.showData}>点击提示左侧的数据</button>
<input ref={this.myRef2} onBlur={this.blurData} type="text" placeholder="失去提示数据"/>
</div> ) }
showData = () => {
console.log(this); console.log(this.myRef);
// {current: input} ---- current不能改,React规定是这样 console.log('myRef ==> ',
this.myRef.current.value) }
blurData = () => { console.log('myRef2 ==> ',this.myRef2.current.value) } }
ReactDOM.render(<Demo />, document.getElementById('test'));
react路由
初识:依赖react-router-dom,专门用来实现一个SPA单页应用,
常用内置组件<BrowserRouter><HashRouter><Route><Redirecrect><Link><NavLink><Switch>
一般组件与路由组件的不同之处
写法不同 一般组件 <Demo /> 路由组件 <Route path="/demo" component={Demo}/>
接收的props不同 一般组件 标签传递什么,就能接收什么 路由组件 接收三个固定的属性 history、location、match
路由组件传递参数
params参数
路由链接(携带参数):<Link to="/demo/tom/18">xx</Link>;
注册路由(声明参数):<Route path="/demo/:name/:age" component={demo}/>;
接收参数:this.props.match.params
search参数
路由链接(携带参数):<Link to="/demo?name=tom&age=18">xx</Link>
注册路由(无需声明,正常注册即可):<Route path="/demo" component={demo}/>
接收参数:this.props.location.search
备注:获取到的search是urlencoded编码字符串,需借助querystring解析
state参数
路由链接(携带参数):<Link to={{path:‘/demo’,state:{name:"tom",age:"18">xx</Link>
注册路由(无需声明,正常注册即可):<Route path="/demo" component={demo}/>
接收参数:this.props.location.state
备注:刷新可能会丢失参数 (BrowserRouter模式下不会丢失,HashRouter模式下会丢失)