react
原生组件
- 原生js操作dom繁琐,效率低(dom-api)
- 原生js操作dom,造成浏览器会进行大量的重绘重排
- 没有组件化编码,代码复用率低
特点
- 采用组件化模式,声明式编码,提高开发效率
- 在react native中可以使用react进行移动端开发
- 使用虚拟DOM+优秀的diff算法,尽量减少真实DOM的操作
虚拟DOM
- 本质是Object对象,(一般对象)
- vdom比较轻,dom比较重,vdom是react再用,无需真实dom上那么多属性。
- vdom最终会被转化为真实dom,呈现在页面
JSX语法
-
vdom不能加“”
-
标签中混入js表达式要用 {}
-
样式类名用className
-
内联样式要用style={{key:value}} //style={{ fontSize: 13 }}
-
vdom必须有一个跟标签,且标签必须闭合
-
标签首字母:
- 小写:转化为html,无对应,报错
- 大写:渲染组件
函数式组件
注:不能使用this, undefined因为bable编译后开启了严格模式
ReactDom.render():
- react解析组件标签,找到标签
- 将vdom转化为真实的dom呈现在页面
类式组件
class MyComponnet extends ReactDom.compont{
render(){ //render 在原型上,供实例使用
return () //this 指实例对象
}
}
ReactDom.render():
- react解析组件标签,找到标签
- 把类定义,new出实例对象,通过实例调用原型上的render
- 将render返回vdom转化为真实的dom呈现在页面
State(状态)
简单组件无 state
复杂组件有state
class HelloReact extends React.Component {
constructor(props) {
super(props);
this.state = {name:'hello'};
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>现在是 {this.state.name}.</h2>
</div>
);
}
}
ReactDOM.render(
<HelloReact />,
document.getElementById('example')
);
组件实例的三大核心
state:{}
不可直接修改,必须使用 this.setState({key:value})
//合并,不会覆盖其他属性
class HelloReact extends React.Component {
constructor(props) {
super(props);
this.state = {name: 'hello'};
}
clieckBtn=()=>{
this.setState({
date: new Date()
});
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>现在是 {this.state.date}.</h2>
<button onClick={this.clieckBtn}> </button>
</div>
);
}
}
ReactDOM.render(
<HelloReact />,
document.getElementById('example')
);
注:
-
组件中的render方法中this指组件实例对象
-
组件自定义方法中this为undefined
- 强制绑定this,通过函数对象bind()
- 箭头函数
-
状态数据不能直接修改或更改
construction构造器
- 通过this.state赋值对象初始化内部state
- 为事件处理函数绑定实例
注:
- 不要调用this.setState方法
- 构造器是否接受props,是否传递给super ,取决于是否在构造器中通过this使用
props:只读
子组件获取数据,并且不可修改
-
默认 Props
class HelloReact extends React.Component { render() { return ( <h1>Hello, {this.props.name}</h1> ); } } HelloReact.defaultProps = { name: 'xixi' }; const element = <HelloReact/>; ReactDOM.render( element, document.getElementById('example') );
2.Props 验证
var title = "嘻嘻哈哈";
// var title = 123;
class HelloReact extends React.Component {
render() {
return (
<h1>Hello, {this.props.title}</h1>
);
}
}
HelloReact.propTypes = {
title: PropTypes.string
};
ReactDOM.render(
<HelloReact title={title} />,
document.getElementById('example')
);
ref
React 支持一种非常特殊的属性 Ref ,你可以用来绑定到 render() 输出的任何组件上。
1.字符型refs (存在效率问题,已弃用)
<input ref="myInput" />
var input = this.refs.myInput;
var inputValue = input.value;
2.回调形式
<input ref={(a)=>{this.input1=a}} .../> // a:input元素
注:
若ref回调函数以内联方式定义的,在更新过程中会被执行两次,第一次传参为null,第二次传参为Dom。因为每次渲染时会创建一个新的函数实例,react清空的ref并设置新的。
解决,定义成class类函数绑定(一般不用管,直接写成内联)
inputFunc=(a)=>{
this.input1=a
}
<input ref={this.inputFunc()} .../>
3.creactRef 函数
this.myRef = React.createRef().current.innerHTML
<div ref={this.myRef}>hello</div>
事件处理
React 元素的事件处理和 DOM 元素类似。但是有一点语法上的不同:
-
React 事件绑定属性的命名采用驼峰式写法,而不是小写。
使用自定义合成(合成)事件,而不是使用原生DOM事件(兼容性)
事件可以通过事件委托(最外层) 高效
-
如果采用 JSX 的语法你需要传入一个函数作为事件处理函数,而不是一个字符串(DOM 元素的写法)
-
通过event.target得到发生事件的Dom元素对象(不要过度使用ref)
function ActionLink() {
function handleClick(event) {
event.preventDefault();
}
return (
<a href="#" onClick={handleClick}>
点我
</a>
);
}
//event 为合成事件
受控组件和不受控组件
区别:
- 受控组件依赖于状态,而非受控组件不受状态的控制;
- 受控组件只有继承“React.Component”才会有状态,而非受控组件则不是只有继承才有状态;
- 受控组件一般适用于需要动态设置初始值时,非受控组件一般用于无任何动态初始值信息时。
组件的生命周期
组件的生命周期可分成三个状态:
- Mounting(挂载):已插入真实 DOM
- Updating(更新):正在被重新渲染
- Unmounting(卸载):已移出真实 DOM
挂载时:
constructor -> componentWillMount -> render -> componentDidMount
更新时:
旧
-
父组件更新 (render)
componentWillReceiveProps() -> shouldComponentUpdate() -> componentWillUpdate() -> render() -> componentDidUpdate()
注:
componentWillReceiveProps()
第一次不调用
-
数据更新
setState() -> shouldComponentUpdate() -> componentWillUpdate() -> render() -> componentDidUpdate()
注:
shouldComponentUpdate()
可以不写,写了必须返回 true/false
true:执行下一步
false:不执行
-
强制更新
foreUpdata() -> shouldComponentUpdate() -> componentWillUpdate() -> render() -> componentDidUpdate()
新:
componentWillMount () → UNSAFE_componentWillMount ()
componentWillReceiveProps() → UNSAFE_componentWillReceiveProps(nextProps)
componentWillUpdate() → UNSAFE_componentWillUpdate(nextProps, nextState)
新增:
getDerivedStateFromProps
为static,所以无法调用this.props。
1.父组件通知子组件pros发生变更时
2.子组件每次render前
3.首次构造时执行
getSnapshotBeforeUpdate
1.首次构造时不执行
2.子组件每次render后
消息订阅与发布机制(兄弟组件传参)
npm install pubsub-js --save // pubsub-js库
(1)先利用npm install pubsub-js --save下载pubsub-js库。
(2)再利用import PubSub from 'pubsub-js'引入pubsub-js库。
(3)订阅:利用componentDidMount钩子将PubSub.subscribe函数挂载,从而在需要订阅的组件进行消息的订阅
//订阅
componentDidMount(){
this.token = PubSub.subscribe('usersData',(_,data) => {
console.log('订阅:',data)
})
}
(4)发布:利用PubSub.publish在进行发布的组件中发布消息。
PubSub.publish('usersData',{isFirst:false,isLoading:true})
(5)取消:在刚才订阅的组件中,利用componentWillUnmount钩子将PubSub.unsubscribe挂载将此组件中的订阅进行取消。
componentWillUnmount(){
PubSub.unsubscribe(this.token)
}
路由
路由分类
1.后端路由: 1)理解: value是function, 用来处理客户端提交的请求。 2)注册路由: router.get(path, function(req, res)) 3)工作过程:当node接收到一个请求时, 根据请求路径找到匹配的路由, 调用路由中的函数来处理请求, 返回响应数据 2.前端路由: 1)浏览器端路由,value是component,用于展示页面内容。 2)注册路由: 3)工作过程:当浏览器的path变为/home时, 当前路由组件就会变为Home组件
路由组件与一般组件
1.写法不同: 一般组件: 路由组件: 2.存放位置不同: 一般组件:components 路由组件:pages 3.接收到的props不同: 一般组件:写组件标签时传递了什么,就能收到什么 路由组件:接收到三个固定的属性(常用) history: go: ƒ go(n) goBack: ƒ goBack() goForward: ƒ goForward() push: ƒ push(path, state) replace: ƒ replace(path, state) location: pathname: "/about" search: "" state: undefined match: params: {} path: "/about" url: "/about"
路由组件
<BrowerRouter /> //常用
<HashRouter />
<Route path='' exact /> //exact 精准匹配(有二级路由会出问题)
//route 的主要参数 : path exact component(5+) element element={<item.component />}
render
<Redirect to='' />
<Link to='' />
<NavLink to='' />
<Switch> //只匹配一个
多级路径刷新页面样式丢失的问题
1.public/index.html中引入样式时 不写./ 写/ (常用)
2.public/index.html中引入样式时不写 ./ 写%PUBLIC_URL% (常用)
3.使用HashRouter
路由组件传递参数
1.params参数
路由:<Link to='/demo/lili/18'}>详情</Link>
注册路由:<Route path="/demo/:name/:age" component={xx}/>
接收参数:this.props.match.params
2.search参数
路由:<Link to='/demo/test?name=tom&age=18'}>详情</Link>
注册路由:<Route path="/demo/test" component={xx}/>
接收参数:this.props.location.search
注:获取到的search是urlencoded编码字符串,需要借助querystring解析
3.state参数
路由:<Link to={{pathname:'/demo/test',state:{name:'tom',age:18}}}>详情</Link>
注册路由(无需声明,正常注册即可):<Route path="/demo/test" component={xx}/>
接收参数:this.props.location.state
备注:刷新也可以保留住参数
编程式路由导航
利用this.prosp.history对象
this.prosp.history.push()
this.prosp.history.replace()
this.prosp.history.goBack()
this.prosp.history.goForward()
this.prosp.history.go()
withRouter 的使用
如果在你想在一般组件使用 路由组件所特有的API 时, 就要借助 withRouter
withRouter可以加工一般组件, 让一般组件具备路由组件所特有的API
withRouter的返回值是一个新组件
import { withRouter} from "react-router-dom";
export default withRouter(Layout)
注: withRouter 外必须包裹BrowserRouter或者HashRouter
BrowserRouter与HashRouter的区别
1.底层原理不一样:
BrowserRouter使用的是H5的history API,不兼容IE9及以下版本。
HashRouter使用的是URL的哈希值。
2.path表现形式不一样
BrowserRouter的路径中没有#,例如:localhost:3000/demo/test
HashRouter的路径包含#,例如:localhost:3000/#/demo/test
3.刷新后对路由state参数的影响
1)BrowserRouter没有任何影响,因为state保存在history对象中。
2)HashRouter刷新后会导致路由state参数的丢失!!!
注:HashRouter可以用于解决一些路径错误相关的问题。
路由注意
//路由元素
const routes = [
{
path: '/home',
name: 'home',
component: () => import('@/pages/home/home.jsx'),
meta: {
title: 'Home',
id: "Home",
}
},
{
path: '/login',
name: 'login',
component: () => import('@/pages/login/login.jsx'),
meta: {
title: 'login',
id: "login",
}
},
]
export default routes;
//生成路由
import React,{Component,lazy,Suspense} from "react"
//lazy Suspense 一般一起用,路由懒加载
import { Route ,Switch} from "react-router-dom"
import routes from "./routerItem"
export default class CusRouters extends Component{
render (){
return (
<Suspense fallback={<div style={{ textAlign: 'center', fontSize: '20px', padding: '100px 0', color: '#0d7bff' }}>Loading...</div>}>
<Switch>
{routes.map(item=>{
return <Route key={item.path} path={item.path} component={lazy(item.component)}/>
})}
</Switch>
</Suspense>
)
}
}
注:
lazy(() => import(xx)) // 等价于 import Home from 'xx';
redux
专门做状态管理的js库,集中管理react应用的多组件共享状态(能不用就不用了)
redux三大核心
1 action 触发reducer方法动作 2 reducer 3 store
Action 是把数据从应用(译者注:这里之所以不叫 view 是因为这些数据有可能是服务器响应,用户输入或其它非 view 的数据 )传到 store 的有效载荷。它是 store 数据的唯一来源。一般来说你会通过 store.dispatch() 将 action 传到 store。
Reducers 指定了应用状态的变化如何响应 actions 并发送到 store 的,记住 actions 只是描述了有事情发生了这一事实,并没有描述应用如何更新 state。
Store 有以下职责:
- 维持应用的 state;
- 提供
getState()方法获取 state; - 提供
dispatch(action)方法更新 state; - 通过
subscribe(listener)注册监听器; - 通过
subscribe(listener)返回的函数注销监听器。