react
React是Facebook开发的一款JS库,React是一个适合数据经常变化以及构建大型项目的复杂组件;
核心思想:封装组件
基本包括:组件、JSX、Virtual Dom、Data Flow
组件
应用构建在组件之上
组件名称必须大写
两个核心概念L:props、state。组件通过这两个属性的值在reader方法里面生成这个组件对应的html
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
const element = <Welcome name="Sara" />;
ReactDOM.render(
element,
document.getElementById('root')
);
这个属性名称必须要和函数组件里面的参数名称一致,否则会报错
例如:name
props
组建的属性,具有只读性
注:组件生成的html结构只能有一个单一的根节点
React 组件都必须保护props不被改变,也就是说不能改变他自己传入参数
- 作用:给组件传递数据,一般用在父子组件之间
- 说明:React把传递给组件的属性转化为一个对象并交给
props - 特点:
props是只读的,无法给props添加或修改属性 props.children:获取组件的内容,
props可以传递函数,进而修改父元素
state
状态即数据
- 作用:用来给组件提供
组件内部使用的数据 - 注意:只有通过
class创建的组件才具有状态 - 注意:状态是私有的,完全由组件来控制
- 注意:不要在
state中添加render()方法中不需要的数据,会影响渲染性能!- 可以将组件内部使用但是不渲染在视图中的内容,直接添加给 this
- 注意:不要在
render()方法中调用 setState() 方法来修改state的值- 但是可以通过
this.state.name = 'rose'方式设置state(不推荐!!!!)
- 但是可以通过
利用es6的class的继承
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
ReactDOM.render(
<Clock />,
document.getElementById('root')
);
组件的当前状态(数据),一旦更改,组件就会自动调用重新渲染ui,这个更改的动作会通过this.setState方法来触发
this.setState属于异步加载
// 方式一:
this.setState({
count: this.state.count + 1
})
this.setState({
count: this.state.count + 1
}, function(){
// 由于 setState() 是异步操作,所以,如果想立即获取修改后的state
// 需要在回调函数中获取
// https://doc.react-china.org/docs/react-component.html#setstate
});
// 方式二:
this.setState(function(prevState, props) {
return {
counter: prevState.counter + props.increment
}
})
// 或者 - 注意: => 后面需要带有小括号,因为返回的是一个对象
this.setState((prevState, props) => ({
counter: prevState.counter + props.increment
}))
生命周期方法可以加到Class中
componentDidMount()组件已经渲染到Dom中后运行
componentWillUnmout()组件卸载之前
划分状态组件
让组件尽可能的少状态,
- 1 通过 JS函数 创建(无状态组件)
- 2 通过 class 创建(有状态组件)
函数式组件 和 class 组件的使用场景说明:
1 如果一个组件仅仅是为了展示数据,那么此时就可以使用 函数组件
2 如果一个组件中有一定业务逻辑,需要操作数据,那么就需要使用 class 创建组件,因为,此时需要使用 state
一下可认为不是状态:
- 可计算的数据:比如一个数组的长度
- 和 props 重复的数据:除非这个数据是要做变更的
无状态组件
js函数创建
- 注意:1 函数名称必须为大写字母开头,React通过这个特点来判断是不是一个组件
- 注意:2 函数必须有返回值,返回值可以是:JSX对象或
null - 注意:3 返回的JSX,必须有且仅有一个根元素
- 注意:4 组件的返回值使用
()包裹,避免换行问题
用纯粹的函数来定义无状态的组件(stateless function),这种组件没有状态,没有生命周期,只是简单的接受 props 渲染生成 DOM 结构。无状态组件非常简单,开销很低,如果可能的话尽量使用无状态组件。比如使用箭头函数定义:
function Welcome(props) {
return (
// 此处注释的写法
<div className="shopping-list">
{/* 此处 注释的写法 必须要{}包裹 */}
<h1>Shopping List for {props.name}</h1>
<ul>
<li>Instagram</li>
<li>WhatsApp</li>
</ul>
</div>
)
}
ReactDOM.render(
<Welcome name="jack" />,
document.getElementById('app')
)
阻止组件渲染
在组件中返回null,但是不会影响组建的生命周期,生命周期函数会执行
受控组件
在html中,表单元素可以自己维护state,并根据用户输入进行更新,在React中可变状态通常保存在state属性中,并且只能通过setState()来更新。
我们可以把两者结合起来,使 React 的 state 成为“唯一数据源”。渲染表单的 React 组件还控制着用户输入过程中表单发生的操作。被 React 以这种方式控制取值的表单输入元素就叫做“受控组件”。
输入的值始终由React的state驱动
组件的事件绑定
- 1 通过React事件机制
onClick绑定 - 2 JS原生方式绑定(通过
ref获取元素)- 注意:
ref是React提供的一个特殊属性 ref的使用说明:[react ref](discountry.github.io/react/docs/…
- 注意:
// 自定义方法:
handleBtnClick(arg1, arg2) {
this.setState({
msg: '点击事件修改state的值' + arg1 + arg2
})
}
render() {
return (
<div>
<button onClick={
// 无参数
// this.handleBtnClick.bind(this)
// 有参数
this.handleBtnClick.bind(this, 'abc', [1, 2])
}>事件中this的处理</button>
<h1>{this.state.msg}</h1>
</div>
)
}
constructor() {
super()
this.handleBtnClick = this.handleBtnClick.bind(this)
}
// render() 方法中:
<button onClick={ this.handleBtnClick }>事件中this的处理</button>
<input type="button" value="在构造函数中绑定this并传参" onClick={
() => { this.handleBtnClick('参数1', '参数2') }
} />
handleBtnClick(arg1, arg2) {
this.setState({
msg: '在构造函数中绑定this并传参' + arg1 + arg2
});
}
JSX
可以将html直接嵌入在js中
jsx表示对象,bable会吧jsx成为一个名为React.createElement()的函数调用
React.createElement(参1,参2,参3)
参1:标签名;
参2:属性对象;
参3:子元素
例
var child = React.createElement('li', null, 'Text Content');
等价于
var child=(
<li>Text Content</li>
)
var root = React.createElement('ul', { className: 'my-list' }, child);
React.render(root, document.body);
工厂化
var root = React.DOM.ul({ className: 'my-list' },
React.DOM.li(null, 'Text Content')
);
注:小写的字符串是html标签,大写开头的是变量是React组件
人、React DOM 使用小驼峰的命名方式
jsx里面class用className代替,
在 JSX 中注释语法:{/* 中间是注释的内容 */}
Virtual Dom
元素
是构成React应用的最小砖块,与浏览器的DOM不同,React元素创建开销极小的普通对象。React DOM 会负责更新DOM来与React保持一致
元素不可改变,一旦被创建,就无法修改子元素或者属性
React只会更新必要的,也就是局部更新
当组件的state有更改,会自动调用render方法重新渲染整个组建的ui。面积操作DOM,有性能问题,所以在 Virtual DOM 上实现了一个 diff 算法 ,上不是真的渲染整个 DOM 树。这个 Virtual DOM 是一个纯粹的 JS 数据结构,所以性能会比原生 DOM 快很多。
Data Flow
"单项数据绑定",是React推荐的一种应用架构方式。
事件处理
事件对象:返回的事件对象是代理的原生对象,如果查看事件对象的具体值,必须要 输出事件对象的属性
事件的名称采用小驼峰,
<button onClick={activateLasers}> Activate Lasers
</button>
一般不用 addEventListener可以在创建元素的时候添加事件
在constructor改变事件的this 指向是必须,否则this的值为undefined
class Clocks extends React.Component {
constructor(props) {
super(props);
this.state = {
flags: true
}
// 这个this是必须的
this.clickText = this.。.bind(this)
}
clickText() {
console.log(this.state.flags)
this.setState({
flags:!this.state.flags
});
}
render() {
return (
<button onClick={this.clickText}>
{this.state.flags?"ok":"no"}
</button>
)
}
}
ReactDOM.render(<Clocks/>,document.getElementById("app"))
事件的传参
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>
条件渲染
与运算符 (&&)
{unreadMessages.length > 0 && <h2> You have {unreadMessages.length} unread messages. </h2> }
注:如果条件为true,会执行右侧的,如果条件为false会跳过
渲染多个组件
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
<li>{number}</li>
);
return (
<ul>{listItems}</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById('root')
);
可以给列表元素添加一个key作为独一无二的,如果米有则默认索引作为key
注: 如果列表项目的顺序可能会变化,我们不建议使用索引来用作 key 值,因为这样做会导致性能变差,还可能引起组件状态的问题。
用 key 提取组件
如果你提取出一个 ListItem 组件,你应该把 key 保留在数组中的这个 元素上,而不是放在 `ListItem` 组件中的 元素上。
注:在map方法中的元素需要设置key属性
key在兄弟节点之间必须唯一
表单
select
由于 selected 属性的缘故,椰子选项默认被选中。React 并不会使用 selected 属性,而是在根 select 标签上使用 value 属性。这在受控组件中更便捷,因为您只需要在根标签中更新它。
你可以将数组传递到 value 属性中,以支持在 select 标签中选择多个选项
style样式
// 1. 直接写行内样式:
<li style={{border:'1px solid red', fontSize:'12px'}}></li>
// 2. 抽离为对象形式
var styleH3 = {color:'blue'}
var styleObj = {
liStyle:{border:'1px solid red', fontSize:'12px'},
h3Style:{color:'green'}
}
<li style={styleObj.liStyle}>
<h3 style={styleObj.h3Style}>评论内容:{props.content}</h3>
</li>
// 3. 使用样式表定义样式:
import '../css/comment.css'
<p className="pUser">评论人:{props.user}</p>
组件的生命周期函数
创建阶段(Mounting)
该阶段函数只执行一次
constructor():
- 作用:1.获取props,2.初始化state
- 说明:通过constructor()的参数props获取
- 设置state和props
componentWillMount()
- 组件被挂载到页面之前执行,在render()之前,在这里同步设置状态将不会被触发重xuanran
- 无法获取页面DOM对象
- 可以调用setState()来改变状态值
- 用来发送ajax请求
render()
- 渲染组件到页面中,无法获取页面中的DOM
- 不要使用setState()会发生递归渲染,(状态改变会重新调用render(),render()又重新改变状态)
componentDidMount()
- 组件已经重新挂载到页面中,可获取组件内部的DOM
- 可以发送请求
- 可以修改状态值(setState),改变状态会重新渲染
运行和交互阶段(Updating)
该阶段函数多次执行,当组件中的props或者state改变时。都会触发执行
componentWillReceiveProps()
- 当组件接收到props前触发
- 参数为当前组件的props的值,可以通过this.props获取上一次的值
- 修改state不会触发该方法
shouldComponentUpdata()
- 根据这个方法的返回值是否重新渲染组件,返回true,则渲染,反之不渲染,返回值为false时,render()不会被调用,
- 优势:通过条件控制页面是否渲染,降低渲染频率,提升组价性能
- 注:必须返回布尔值
componentWillUpdate()
- 组件将要更新,不能修改state否则要循环***
- 参数:为最新的属性和状态,有两个参数
render()
重新渲染组件
componentDidUpdate()
- 组件已经被更新了,
- 参数:旧的属性和状态,有两个参数
卸载阶段(Unmounting)
componentWillUnmount()
在卸载的时候执行清理工作
如:清定时器, componentDidMount创建的DOM对象
react-router
书写:
1、编写路由
2、在父组件中指定
路由连接:
路由:
NavLink:由激活的状态,activeClassName
<NavLink to="/about">About</NavLink> //路由连接,类似a
<NavLink {...this.props} activeClassName="activeClass/>
//把props 所有属性接收
<Switch> //路由容器,只会跳转一个
<Route path="/about" component={About}></Route>
<Route path="/home" component={Home}></Route>
<Redirect to="/about"></Redirect>//重定向
</Switch>
// 1 导入组件
import {
HashRouter as Router,
Link, Route
} from 'react-router-dom'
// 2 使用 <Router>
<Router>
// 3 设置 Link 有一个replace属行,将新地址地换成历史访问记录的原地址
<Menu.Item key="1"><Link to="/">首页</Link></Menu.Item>
<Menu.Item key="2"><Link to="/movie">电影</Link></Menu.Item>
<Menu.Item key="3"><Link to="/about">关于</Link></Menu.Item>
<Menu.Item key="3"><Link to={{pathname="/fh",search:"?user=a",hash:"#33",state:{msg:"fffffffff"}}}>关于</Link></Menu.Item>
// 4 设置 Route
// exact 表示:绝对匹配(完全匹配,只匹配:/)
<Route exact path="/" component={HomeContainer}></Route>
<Route path="/movie" component={MovieContainer}></Route>
<Route path="/about" component={AboutContainer}></Route>
</Router>
{
pathname="/fh",//跳转的路径
search:"?user=a",//get请求额参数
hash:"#33",//设置HASH值
state:{msg:"fffffffff"},//传入的组件的数据
}
注意点
<Router basename="/router"></Router>:作为整个组件的根元素,是路由容器,只能有一个唯一的子元素,可以在此添加basename,则内部的路由连接会自动在路径前面增加一个“/router",可以用来设置不同的子路由<Link></Link>:类似于vue中的标签,to属性指定路由地址<Route></Route>:类似于vue中的,- exact 表示:绝对匹配(完全匹配,只匹配:/)
路由参数
- 配置:通过
Route中的path属性来配置路由参数 - 获取:
this.props.match.params获取
// 配置路由参数
<Route path="/movie/:movieType"></Route>
// 获取路由参数
const type = this.props.match.params.movieType
路由跳转
- react router - history
history.push()方法用于在JS中实现页面跳转history.go(-1)用来实现页面的前进(1)和后退(-1)
this.props.history.push('/movie/movieDetail/' + movieId)
嵌套路由
实战注意
本地打包测试
npm install -g serve
serve build
antd按需打包
npm i react-app-rewired customize-cra babel-plugin-import
然后在项目根目录创建一个 config-overrides.js 用于修改默认配置。
const {override,fixBabelImports}=require("customize-cra")
module.exports = function override(config, env) {
fixBabelImports("import",{
libraryName:"antd",
libraryDirectory:"es",
style:"css",//自动打包已引入的
})
)
修改**package.json**
/* package.json */
"scripts": {
"start": "react-app-rewired start",
"build": "react-app-rewired build",
"test": "react-app-rewired test",
}
antd自定义主题色
npm i antd react-app-rewired customize-cra babel-plugin-import less less-loader --save
npm i less less-loader --save
修改 config-overrides.js
const { override, fixBabelImports, addLessLoader } = require('customize-cra');
module.exports = override(
//写了下面这个部分,就实现了按需加载,再也不需要再每个页面里面都引入“antd/dist/antd.css”啦
fixBabelImports('import', {
libraryName: 'antd',
libraryDirectory: 'es',
style: true //这里一定要写true,css和less都不行哦
}),
addLessLoader({
javascriptEnabled: true,
//下面这行很特殊,这里是更改主题的关键,这里我只更改了主色,当然还可以更改其他的,下面会详细写出。
modifyVars: { "@primary-color": "#f47983"}
})
)
注意
less-loader@5
addLessLoader({
javascriptEnabled: true,
modifyVars: { '@primary-color': '#1DA57A' },
}),
less-loader@6
addLessLoader({
lessOptions: {
javascriptEnabled: true,
modifyVars: { '@primary-color': '#1DA57A' },
},
}),
还可以修改其他色
@primary-color: #1890ff; // 全局主色
@link-color: #1890ff; // 链接色
@success-color: #52c41a; // 成功色
@warning-color: #faad14; // 警告色
@error-color: #f5222d; // 错误色
@font-size-base: 14px; // 主字号
@heading-color: rgba(0, 0, 0, .85); // 标题色
@text-color: rgba(0, 0, 0, .65); // 主文本色
@text-color-secondary : rgba(0, 0, 0, .45); // 次文本色
@disabled-color : rgba(0, 0, 0, .25); // 失效色
@border-radius-base: 4px; // 组件/浮层圆角
@border-color-base: #d9d9d9; // 边框色
@box-shadow-base: 0 2px 8px rgba(0, 0, 0, .15); // 浮层阴影
更改一下package.json文件
"scripts": {
"start": "react-app-rewired start",
"build": "react-app-rewired build",
"test": "react-app-rewired test",
}
引入路由
npm add react-router-dom
在App.js中引入路由
redux
一个独立专门用于状态管理的js插件,多被用于react中,应用中的state都以一个对象树的形式存储在一歌单一的store中,不支持异步
三大原则:
单一数据源,只存子啊唯一一个的store
state只读,唯一改变state的方法就是触发action
使用纯函数修改;reducer
核心概念
action
标识要执行行为对象,
包含2方面的属性,
1.type:标识属性,字符串,唯一,必要,一般大写
2.XXX:数据属性,任意,可选
Action Creator(创建Action的工厂函数)
const increametfun=(num)=>({type:"",data:num})
reducer
根据老的state和action,产生新的state的纯函数
注意:
返回一个新状态
不要修改原来的状态
store
将state和action与reducer联系在一起
维持应用的state
提供getState()方法获取state
提供dispatch(action)方法更新state
通过subscribe(listener)注册监听器
通过subscribe(listener)返回的函数注销监听器
得到
import {createStore} from "redux"
inport reducer from "./reducers"
const store=createStore(reducer)
redux库核心的管理对象,内部维护state,reducer。
核心方法:
getState()
dispatch(action)
subscribe(listener)
编码:
store.getState()
store.dispatch({type:"INCREMENT",number})
store.subscribe(render)
react-redux
将组件分为两类:
UI组件:
只负责ui呈现,不带有任何业务逻辑,通过props接受数据,不使用任何Redux中的api,一般保存在components的文件夹下,
容器组件:
负责管理数据和业务逻辑,不负责ui呈现,使用Redux的api,一般保存在containers文件下
react-thunk
处理异步操作
插件
npm i --save-dev
sass-node
npm i sass-loader node-sass --save-dev
webpack.config.js
{
loader:
}
//以下新增
{
test:/\.scss$/,
loaders:['style-loader','css-loader','sass-loader']
}
sass全局变量
npm i -S sass-resources-loader
npm i -D sass-resources-loader
.配置webpack.config.js文件
{
test: /\.scss$/,
use: [{
loader: 'style-loader'
}, {
loader: 'css-loader'
}, {
loader: 'sass-loader'
},
{
loader: 'sass-resources-loader',
options: {
resources: [
// resolve方法第二个参数为scss配置文件地址,如果有多个,就进行依次添加即可
path.resolve(__dirname, './../src/assets/scss/output.scss')
]
}
}
]
}
或者
{
test: sassRegex,
exclude: sassModuleRegex,
use: getStyleLoaders(
{
importLoaders: 2,
sourceMap: isEnvProduction && shouldUseSourceMap,
},
'sass-loader',
).concat({
loader: 'sass-resources-loader',
options: {
resources: [
// 这里按照你的公共变量文件路径填写
path.resolve(__dirname, './../src/common.scss')
]
}
}),
sideEffects: true,},