React.js
编写react需要安装的三个开发环境下的模块
- babel 解析JSX
- react 实现ui用户界面
- react-dom 处理dom
JSX:在JavaScript里面写html代码(最好用圆括号包起来,单标签要闭合),在html代码中用插值符号{}可以写js代码
ReactDOM.render(element, container, cb) 往容器中渲染一个组件element
循环返回dom的时候要加上key属性,但这个属性不能在props中取到
添加css用style属性=一个对象,属性名用驼峰形式命名。
事件名字用驼峰形式命名,其最后一个参数才是事件对象event。
Object.keys(obj)
可以通过扩展运算符+对象给一个标签添加属性:{...data[item]}
声明式无状态组件
function Main(props){ //大写名字的函数,返回一个组件
return (
{props.con}
)
}
let element = (
);
声明式有状态组件
定义一个类继承React.Component,在render方法中返回要渲染的HTML元素。
class Head extends React.Component {
constructor(props){
super(props) // 执行父类的构造函数
this.name = 'dream'
this.age = 18
}
// 不要写function
say(){
console.log('say方法')
}
render(){ // 渲染
return (
这是真正的有状态的组件 头部
)
}
}
将在原型上的事件触发函数指向this的三个方法
在标签的事件属性中使用bind;
在constructor中改变函数的this指向,但这种就不适合传参;
this.handleClick = this.handleclick.bind(this)
在原型上定义该函数的时候使用箭头函数;
handleClick = (e)=>{} //传参的时候使用箭头函数
{this.handleClick('arguments',e)}}>
包含关系
组件的子元素会被存到props.children属性中:
function FancyBorder(props){
return (
{props.children}
);
}
function WelcomeDialog(){
return (
Welcome
Thank you for visiting my blog!
)
}
虽然不太常见,但有时你可能需要在组件中有多个入口,这种情况下你可以使用自己约定的属性而不是 children:
function SplitPane(props){
return (
{props.left}
{props.right}
)
}
function App(){
return (
}
right={ }
/>
);
}
属性和组合为你提供了以清晰和安全的方式自定义组件的样式和行为所需的所有灵活性。请记住,组件可以接受任意元素,包括基本数据类型、React 元素或函数。
关键字
class -> className
value -> defaultValue
for -> htmlFor
focus -> autoFocus
操作DOM的方式
常规的获取DOM方法:id,类名等
通过事件对象,在该元素身上绑定的事件函数中获取; event.target
通过ref属性,在旧版本中通过给标签ref属性一个字符串,然后实例对象中的ref对象是由保存以ref属性为键,对应的节点为值的键值对组成
//this.refs.btn就指向这个节点
在新版本中建议在ref属性中使用回调函数,在回调函数中给实例对象定义一个值,将该节点直接赋给该值:
this._div=itemDiv}>//this._div就指向这个节点findDOMNode 参数是dom节点时返回dom节点,参数是组件时返回组件渲染的dom节点
子组件往父组件通讯的方法
把父组件的方法通过props传递给子组件,在子组件中调用并传参,就会传到父组件的方法中
生命周期函数
生命周期:组件从定义到挂载,最后渲染到页面的一个过程。
第一阶段:挂载阶段
constructor 构造函数
只会在定义组件的时候执行一次,在此阶段可用于初始化state状态。
componentWillMount()
在组件即将被挂载的时候调用一次,此时组件还没有被渲染出来,这个时候不能够进行DOM操作。此时可以请求接收后端的数据。
render()
渲染组件。
componentDidMount()
此阶段组件挂载完成,用户能够在页面上看到数据,可以对DOM进行操作。若组件包含子组件,则在子组件渲染完毕(constructor->cDM)后才会触发这个函数。
第二阶段:更新阶段(子组件)
(子组件的)构造函数
componentWillReceiveProps(nextProps)
父组件的数据更新会触发子组件的这个函数执行,在这里可以处理父组件更新后,通过props传到子组件的数据,更新构造函数中对应的state值的数据。
shouldComponentUpdate(nextProps, nextState)
是否更新组件,是否触发重新渲染,函数必须有一个Boolean的返回值,true表示执行render函数重新渲染并继续执行后面的生命周期函数,false表示不重新渲染,也就不会再继续执行render和后面的生命周期函数。无论是父组件中this.state导致props数据发生变化还是子组件中的this.state数据发生变化都会触发,对应的参数(state触发的就对应nextState,props触发的就对应nextProps)就变成那个将会发生改变后的数据。
一定要加上判断!因为setState后改变了state的状态又会触发这个函数,以免进入无限循环。另外一方面通过判断数据是否发生变化,如果发生变化则返回true触发重新渲染,不发生变化则返回false避免不必要的重渲染行为。
关于这个钩子函数的重要性的一篇博文:https://www.cnblogs.com/penghuwan/p/6707254.html
componentWillUpdate(nextProps, nextState)
组件即将会被更新。
render()
渲染组件。
componentDidUpdate(prevProps, prevState)
组件渲染完成后执行。对应的参数(state触发的就对应nextState,props触发的就对应nextProps)为会发生改变前的数据。
componentWillUnmount()
关闭网页或者子组件被销毁(在父组件中被取消引用的时候,会先触发父组件的render方法)的时候会执行
thi.setState()除了接收变量改变state属性外,还可以用第二种形式接收一个函数
this.setState((prevState, props)=>{
//先前的状态作为第一个参数,此次更新被应用时的props作为第二个参数
})
})
总结setState操作会触发的生命周期函数顺序如下:
setState -> shouldComponentUpdate(返回true继续往下,返回false就在这里截止了) -> render -> 子组件的componentwillreceiveprops -> 子组件shouldcomponentupdate(返回true继续往下,返回false就在这里截止了) -> 子组件componentwillupdate -> 子组件 render -> 子组件componentdidupdate -> componentdidupdate
利用脚手架开发
安装: npm i create-react-app -g
创建:create-react-app appname
运行项目:npm start
表单控件
- 非受控(非约束性)表单控件:用户输入的表单的值value,react根本就不知道,例如常规的文本表单用户输入了什么react都不能实时得到,需要转成受控表单控件。
- 受控(约束性)表单控件:让react管理表单的数据,能够监视表单的一举一动。表单的value值改变则重新渲染(用自己的说法就是表单的初始状态是由state里决定,表单值value一旦改变state值也要随着进行更新,然后重新渲染表单)
//文本框
class Text extends React.Component{
state = {val : ''}
handleChange = (e)=>{
this.setState({val:e.target.value});
}
render(){
return (
)
}
}
//单选框
class Radio extends React.Component{
state = {
sex: 'male';
}
handleChange = (e)=>{
this.setState({sex: e.target.value});
}
render(){
return (
)
}
}
//下拉菜单
class Select extends React.Component{
state = {
cityList: ['GZ','SZ','SH'],
city:''
}
handleChange = (e)=>{
this.setState({city:e.target.value});
}
render(){
return (
)
}
}
//复选框
class Checkbox extends React.Component{
state = {
name: [{value:'a',checked:true},{value:'b',checked:false},{value:'c',checked:false}]
}
handleChange = (index,e)=>{
const arr = this.state.name;
arr[index].checked = !arr[index].checked;
this.setState({name:arr});
}
render(){
return (
this.state.name > 0 && this.state.name.map((item,index)=>{
return (
{item.value}
)
})
)
}
}
路由模块 react-router-dom
BrowserRouter 通常起名为Router, 是一个组件,可以让根组件挂载不同的其他组件
HashRouter 项目上线需要改为hashrouter,让后退操作可用
Route 根据路由控制不同的组件显示
//exact表示严格匹配 path属性表示当前路由 component表示在当前路由下渲染Home组件 Link 控制路由跳转,相当于a标签
NavLink 功能和Link一样,但有更多api
首页 //exact严格匹配 to表示要跳转的路由 activeClassName表示标签在active状态下添加的类名Switch 包裹Route,表示只匹配一个路由,渲染一个组件
Redirect 重定向,用于判断状态看是否需要进行重新定向,一旦渲染到页面上就重定向至指定路由。
在组件卸载之前 是可以更新状态的(setstate),在组件卸载之后 不要更新状态(setstate),会报错。
例如刚进入页面渲染组件时,过3秒它会更新state,如果在三秒内跳转到其他路由,也就是把这个组件给卸载的话就会出现报错信息。
这个时候可以通过设置componentWillUnmount,将this.setState函数设置为空。
this.props.match.path 得到匹配该子组件的路由
this.props.match.params.id 得到匹配该子组件的动态路由/:id
路由模块化
将路由的属性值保存于组件的state中或者是另起一个js文件保存导入,遍历然后展开(可以用扩展运算符…)
保存在state中:
用js模块导入:
//js模块
//直接导入组件
import News from '';
import Not404 from '';
import NewDetails from '';
const routes = [
{
exact: true, //要用到精确匹配则设置为true
path: '/',
component: Home
},
{
path: '/news',
component: News,
children: [ //子路由
{
exact: true,
path: '/news/',
component: NewIndex
},
...
]
},
...
{
path: '*',
component: Not404 //404模块放在最后
}
]
export default routes
//jsx
import routes form '';
import {Component} from 'react';
import {Switch,Route} from 'react-router-dom';
class App extends Compnent{
render(){
return (
{
routes.length>0 && routes.map((item,index)=>{
return
})
}
)
}
}
嵌套路由传参
如果像上面实现了路由模块化后,那么像News这个组件还有子路由children这个属性怎样去传递给它的子路由呢?答案就是我们不能简单地用component去指向路由要渲染的组件了,而是改用render方法。
还是以上面的routes路由模块数组举例
//jsx
import routes form '';
import {Component} from 'react';
import {Switch,Route} from 'react-router-dom';
class App extends Compnent{
render(){
return (
{
routes.length>0 && routes.map((item,index)=>{
return }/>
})
}
)
}
}
这里用render方法替代component,直接表示返回组件,把子路由children通过routes属性传过去。另外要注意的是{...props}最好写上,这里包含了match等重要的属性,以便在组件中能够取到path和动态路由的值。
prop-types
可以引用内置模块prop-types设置父组件传过来的props属性的数据类型以及设置默认值:
import PropTypes from 'prop-types';
class Head extends Component{...}
//定义父组件中props属性的数据类型
Head.propTypes = {
data: PropTypes.array
}
//设置子组件的props默认的属性值
Head.defaultProps = {
simple: '子组件的默认值'
}