react - JSX
React 背景介绍
React 起源于 Facebook 的内部项目,因为该公司对市场上所有 JavaScript MVC 框架,都不满意,就决定自己写一套,用来架设 Instagram 的网站。做出来以后,发现这套东西很好用,就在2013年5月开源了。
什么是React
特点
1 2 3 4 5 6
| > JSX --TO--> EveryThing
- JSX --> HTML - JSX --> native ios或android中的组件(XML) - JSX --> VR - JSX --> 物联网
|
为什么要用React
- 1 使用
组件化
开发方式,符合现代Web
开发的趋势
- 2 技术成熟,社区完善,配件齐全,适用于大型Web项目(生态系统健全)
- 3 由Facebook专门的团队维护,技术支持可靠
- 4 ReactNative - Learn once, write anywhere: Build mobile apps with React
- 5 使用方式简单,性能非常高,支持服务端渲染
- 6 React非常火,从技术角度,可以满足好奇心,提高技术水平;从职业角度,有利于求职和晋升,有利于参与潜力大的项目
React中的核心概念
- 1 虚拟DOM(Virtual DOM)
- 2 Diff算法(虚拟DOM的加速器,提升React性能的法宝)
虚拟DOM(Vitural DOM)
React将DOM抽象为虚拟DOM,虚拟DOM其实就是用一个对象来描述DOM,通过对比前后两个对象的差异,最终只把变化的部分重新渲染,提高渲染的效率
为什么用虚拟dom,当dom反生更改时需要遍历 而原生dom可遍历属性多大231个 且大部分与渲染无关 更新页面代价太大
VituralDOM的处理方式
- 1 用
JavaScript
对象结构表示 DOM
树的结构,然后用这个树构建一个真正的 DOM
树,插到文档当中
- 2 当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树差异
- 3 把2所记录的差异应用到步骤1所构建的真正的
DOM
树上,视图就更新了
Diff算法
当你使用React的时候,在某个时间点 render() 函数创建了一棵React元素树,在下一个state或者props更新的时候,render() 函数将创建一棵新的React元素树,React将对比这两棵树的不同之处,计算出如何高效的更新UI(只更新变化的地方)
有一些解决将一棵树转换为另一棵树的最小操作数算法问题的通用方案。然而,树中元素个数为n,最先进的算法 的时间复杂度为O(n3) 。
如果直接使用这个算法,在React中展示1000个元素则需要进行10亿次的比较。这操作太过昂贵,相反,React基于两点假设,实现了一个O(n)算法,提升性能
- React中有两种假定:
- 1 两个不同类型的元素会产生不同的树(根元素不同结构树一定不同)
- 2 开发者可以通过key属性指定不同树中没有发生改变的子元素
Diff算法的说明 - 1
- 如果两棵树的根元素类型不同,React会销毁旧树,创建新树
1 2 3 4 5 6 7 8 9 10
| <div> <Counter /> </div>
<span> <Counter /> </span>
|
Diff算法的说明 - 2
- 对于类型相同的
React DOM
元素,React
会对比两者的属性是否相同,只更新不同的属性
- 当处理完这个
DOM
节点,React
就会递归处理子节点。
1 2 3 4 5 6 7 8 9 10 11
| <div className="before" title="stuff" />
<div className="after" title="stuff" /> 只更新:className 属性
<div style={{color: 'red', fontWeight: 'bold'}}/>
<div style={{color: 'green', fontWeight: 'bold'}}/>
|
Diff算法的说明 - 3
- 1 当在子节点的后面添加一个节点,这时候两棵树的转化工作执行的很好
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <ul> <li>first</li> <li>second</li> </ul>
<ul> <li>first</li> <li>second</li> <li>third</li> </ul>
|
- 2 但是如果你在开始位置插入一个元素,那么问题就来了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <ul> <li>Duke</li> <li>Villanova</li> </ul>
<ul> <li>Connecticut</li> <li>Duke</li> <li>Villanova</li> </ul>
|
key 属性
为了解决以上问题,React
提供了一个 key
属性。当子节点带有key
属性,React
会通过key
来匹配原始树和后来的树。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <ul> <li key="2015">Duke</li> <li key="2016">Villanova</li> </ul>
<ul> <li key="2014">Connecticut</li> <li key="2015">Duke</li> <li key="2016">Villanova</li> </ul>
|
- 说明:key属性在React内部使用,但不会传递给你的组件
- 推荐:在遍历数据时,推荐在组件中使用 key 属性:
<li key={item.id}>{item.name}</li>
- 注意:key只需要保持与他的兄弟节点唯一即可,不需要全局唯一
- 注意:尽可能的减少数组index作为key,数组中插入元素的等操作时,会使得效率底下
React的基本使用
- 安装:
1
| npm i -S react react-dom
|
react
:react 是 React库的入口点
react-dom
:提供了针对DOM的方法,比如:把创建的虚拟DOM
,渲染到页面上
1 2 3 4 5 6 7 8 9 10 11 12 13
| import React from 'react' import ReactDOM from 'react-dom'
const divVD = React.createElement('div', { title: 'hello react' }, 'Hello React!!!')
ReactDOM.render(divVD, document.getElementById('app'))
|
createElement()的问题
- 说明:
createElement()
方式,代码编写不友好,太复杂
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| var dv = React.createElement( "div", { className: "shopping-list" }, React.createElement( "h1", null, "Shopping List for " ), React.createElement( "ul", null, React.createElement( "li", null, "Instagram" ), React.createElement( "li", null, "WhatsApp" ) ) )
ReactDOM.render(dv, document.getElementById('app'))
|
JSX 的基本使用
- 注意:JSX语法,最终会被编译为 createElement() 方法
- 推荐:使用 JSX 的方式创建组件
- JSX - JavaScript XML
- 安装:
npm i -D babel-preset-react
(依赖与:babel-core/babel-loader
)
注意:JSX的语法需要通过 babel-preset-react 编译后,才能被解析执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| { "presets": [ "env", "react" ] }
module: [ rules: [ { test: /\.js$/, use: 'babel-loader', exclude: /node_modules/ }, ] ]
const dv = ( <div title="标题" className="cls container">Hello JSX!</div> )
ReactDOM.render(dv, document.getElementById('app'))
|
JSX的注意点
- 注意 1: 如果在 JSX 中给元素添加类, 需要使用 className 代替 class
- 类似:label 的 for属性,使用htmlFor代替
- 注意 2:在 JSX 中可以直接使用 JS代码,直接在 JSX 中通过 {} 中间写 JS代码即可
- 注意 3:在 JSX 中只能使用表达式,但是不能出现 语句!!!
- 注意 4:在 JSX 中注释语法:
{/* 中间是注释的内容 */}
React组件
React 组件可以让你把UI分割为独立、可复用的片段,并将每一片段视为相互独立的部分。
- 组件是由一个个的HTML元素组成的
- 概念上来讲, 组件就像JS中的函数。它们接受用户输入(props),并且返回一个React对象,用来描述展示在页面中的内容
React创建组件的两种方式
JavaScript函数创建
- 注意:1 函数名称必须为大写字母开头,React通过这个特点来判断是不是一个组件
- 注意:2 函数必须有返回值,返回值可以是:JSX对象或
null
- 注意:3 返回的JSX,必须有一个根元素
- 注意:4 组件的返回值使用
()
包裹,避免换行问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 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') )
|
class创建
在es6中class仅仅是一个语法糖,不是真正的类,本质上还是构造函数+原型 实现继承
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
|
class Person { constructor(age){ this.age = age } sayHello () { console.log('大家好,我今年' + this.age + '了'); }
static doudou () { console.log('我是小明,我新get了一个技能,会暖床'); } }
Person.staticName = '静态属性'
const p = new Person(19)
class American extends Person { constructor() { super() this.skin = 'white' this.eyeColor = 'white' } }
class ShoppingList extends React.Component { constructor(props) { super(props) } render() { return ( <div className="shopping-list"> <h1>Shopping List for {this.props.name}</h1> <ul> <li>Instagram</li> <li>WhatsApp</li> </ul> </div> ) } }
|
给组件传递数据 - 父子组件传递数据
- 组件中有一个 只读的对象 叫做
props
,无法给props
添加属性
- 获取方式:函数参数
props
- 作用:将传递给组件的属性转化为
props
对象中的属性
1 2 3 4 5 6 7 8 9 10 11 12
| function Welcome(props){ return ( <div> <div>Welcome React</div> <h3>姓名:{props.username}----年龄是:{props.age}</h3> </div> ) }
ReactDOM.reander(<Hello username="zs" age={20}></Hello>, ......)
|
封装组件到独立的文件中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
import React from 'react'
function Hello2(props){ return ( <div> <div>这是Hello2组件</div> <h1>这是大大的H1标签,我大,我骄傲!!!</h1> <h6>这是小小的h6标签,我小,我傲娇!!!</h6> </div> ) }
export default Hello2
import Hello2 from './components/Hello2'
|
props和state
props
- 作用:给组件传递数据,一般用在父子组件之间
- 说明:React把传递给组件的属性转化为一个对象并交给
props
- 特点:
props
是只读的,无法给props
添加或修改属性
props.children
:获取组件的内容,比如:
<Hello>组件内容</Hello>
中的 组件内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
function Welcome(props) { return <div>hello, {props.name}</div> }
class Welcome extends React.Component { constructor(props) { super(props) }
render() { return <h1>Hello, {this.props.name}</h1> } }
|
state
状态即数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| class Hello extends React.Component { constructor() { super()
this.state = { gender: 'male' } }
render() { return ( <div>性别:{ this.state.gender }</div> ) } }
|
JSX语法转化过程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| const element = ( <h1 className="greeting"> Hello, world! </h1> )
const element = React.createElement( 'h1', {className: 'greeting'}, 'Hello, world!' )
const element = { type: 'h1', props: { className: 'greeting', }, children: ['Hello, world'] }
|
评论列表案例
- 巩固有状态组件和无状态组件的使用
- 两个组件:
<CommentList></CommentList>
和 <Comment></Comment>
1 2 3 4 5 6 7 8 9 10
| [ { user: '张三', content: '哈哈,沙发' }, { user: '张三2', content: '哈哈,板凳' }, { user: '张三3', content: '哈哈,凉席' }, { user: '张三4', content: '哈哈,砖头' }, { user: '张三5', content: '哈哈,楼下山炮' } ]
<Comment {...item} key={i}></Comment>
|
style样式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <li style={{border:'1px solid red', fontSize:'12px'}}></li>
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>
import '../css/comment.css' <p className="pUser">评论人:{props.user}</p>
|
相关文章
组件的生命周期
组件生命周期函数总览
1 2 3 4
| constructor(){...} componentWillMount() {...} render() {...} componentDidMount(){...}
|
1 2 3 4 5
| componentWillReceiveProps() {...} shouldComponentUpdate() {...} componentWillUpdate() {...} render() {...} componentDidUpdate() {...}
|
组件生命周期 - 创建阶段(Mounting)
constructor()
- 作用:1 获取props 2 初始化state
- 说明:通过
constructor()
的参数props
获取
- 设置state和props
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| class Greeting extends React.Component { constructor(props) { super(props) this.state = { count: props.initCount } } }
Greeting.defaultProps = { initCount: 0 };
|
componentWillMount()
1 2 3 4 5 6
| componentWillMount() { console.warn(document.getElementById('btn')) this.setState({ count: this.state.count + 1 }) }
|
render()
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| render() { console.warn(document.getElementById('btn'))
return ( <div> <button id="btn" onClick={this.handleAdd}>打豆豆一次</button> { this.state.count === 4 ? null : <CounterChild initCount={this.state.count}></CounterChild> } </div> ) }
|
componentDidMount()
- 1 组件已经挂载到页面中
- 2 可以进行DOM操作,比如:获取到组件内部的DOM对象
- 3 可以发送请求获取数据
- 4 可以通过
setState()
修改状态的值
- 注意:在这里修改状态会重新渲染
1 2 3 4
| componentDidMount() { console.warn('componentDidMount', document.getElementById('btn')) }
|
组件生命周期 - 运行阶段(Updating)
- 特点:该阶段的函数执行多次
- 说明:每当组件的props或者state改变的时候,都会触发运行阶段的函数
componentWillReceiveProps()
- 说明:组件接受到新的props前触发这个方法
- 参数:当前组件props值
- 可以通过 this.props 获取到上一次的值
- 使用:若你需要响应属性的改变,可以通过对比this.props和nextProps并在该方法中使用this.setState()处理状态改变
- 注意:修改state不会触发该方法
1 2 3
| componentWillReceiveProps(nextProps) { console.warn('componentWillReceiveProps', nextProps) }
|
shouldComponentUpdate()
- 作用:根据这个方法的返回值决定是否重新渲染组件,返回
true
重新渲染,否则不渲染
- 优势:通过某个条件渲染组件,降低组件渲染频率,提升组件性能
- 说明:如果返回值为
false
,那么,后续render()
方法不会被调用
- 注意:这个方法必须返回布尔值!!!
- 场景:根据随机数决定是否渲染组件
1 2 3 4 5 6 7 8
|
shouldComponentUpdate(nextProps, nextState) { console.warn('shouldComponentUpdate', nextProps, nextState)
return nextState.count % 2 === 0 }
|
componentWillUpdate()
- 作用:组件将要更新
- 参数:最新的属性和状态对象
- 注意:不能修改状态 否则会循环渲染
1 2 3
| componentWillUpdate(nextProps, nextState) { console.warn('componentWillUpdate', nextProps, nextState) }
|
render() 渲染
- 作用:重新渲染组件,与
Mounting
阶段的render
是同一个函数
- 注意:这个函数能够执行多次,只要组件的属性或状态改变了,这个方法就会重新执行
componentDidUpdate()
1 2 3
| componentDidUpdate(prevProps, prevState) { console.warn('componentDidUpdate', prevProps, prevState) }
|
组件生命周期 - 卸载阶段(Unmounting)
componentWillUnmount()
- 作用:在卸载组件的时候,执行清理工作,比如
- 1 清除定时器
- 2 清除
componentDidMount
创建的DOM对象
React - createClass(不推荐)
React.createClass({})
方式,创建有状态组件,该方式已经被废弃!!!
通过导入 require('create-react-class')
,可以在不适用ES6的情况下,创建有状态组件
getDefaultProps()
和 getInitialState()
方法:是 createReactClass()
方式创建组件中的两个函数
React without ES6
- React不使用ES6
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| var createReactClass = require('create-react-class'); var Greeting = createReactClass({ getDefaultProps: function() { console.log('getDefaultProps'); return { title: 'Basic counter!!!' } },
getInitialState: function() { console.log('getInitialState'); return { count: 0 } },
render: function() { console.log('render'); return ( <div> <h1>{this.props.title}</h1> <div>{this.state.count}</div> <input type='button' value='+' onClick={this.handleIncrement} /> </div> ); },
handleIncrement: function() { var newCount = this.state.count + 1; this.setState({count: newCount}); },
propTypes: { title: React.PropTypes.string } });
ReactDOM.render( React.createElement(Greeting), document.getElementById('app') );
|
state和setState
- 注意:使用
setState()
方法修改状态,状态改变后,React会重新渲染组件
- 注意:不要直接修改
state
属性的值,这样不会重新渲染组件!!!
- 使用:1 初始化
state 2
setState
修改state
1 2 3
|
this.state.test = '这样方式,不会重新渲染组件';
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| constructor(props) { super(props)
this.state = { count: props.initCount } }
componentWillMount() { this.setState({ count: this.state.count + 1 })
this.setState({ count: this.state.count + 1 }, function(){ });
this.setState(function(prevState, props) { return { counter: prevState.counter + props.increment } })
this.setState((prevState, props) => ({ counter: prevState.counter + props.increment })) }
|
组件绑定事件
- 1 通过
React
事件机制 onClick
绑定
- 2 JS原生方式绑定(通过
ref
获取元素)
React中的事件机制 - 推荐
- 注意:事件名称采用驼峰命名法
- 例如:
onClick
用来绑定单击事件
1 2 3 4
| <input type="button" value="触发单击事件" onClick={this.handleCountAdd} onMouseEnter={this.handleMouseEnter} />
|
JS原生方式 - 知道即可
- 说明:给元素添加
ref
属性,然后,获取元素绑定事件
1 2 3 4 5 6 7 8 9 10 11 12
|
<input ref={ input => this.txtInput = input } type="button" value="我是豆豆" />
componentDidMount() { this.txtInput.addEventListener(() => { this.setState({ count:this.state.count + 1 }) }) }
|
事件绑定中的this
- 1 通过 bind 绑定
- 2 通过 箭头函数 绑定
通过bind绑定
- 原理:
bind
能够调用函数,改变函数内部this
的指向,并返回一个新函数
- 说明:
bind
第一个参数为返回函数中this
的指向,后面的参数为传给返回函数的参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| 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> ) }
|
1 2 3 4 5 6 7 8
| constructor() { super()
this.handleBtnClick = this.handleBtnClick.bind(this) }
<button onClick={ this.handleBtnClick }>事件中this的处理</button>
|
通过箭头函数绑定
- 原理:箭头函数中的
this
由所处的环境决定,自身不绑定this
1 2 3 4 5 6 7 8 9
| <input type="button" value="在构造函数中绑定this并传参" onClick={ () => { this.handleBtnClick('参数1', '参数2') } } />
handleBtnClick(arg1, arg2) { this.setState({ msg: '在构造函数中绑定this并传参' + arg1 + arg2 }); }
|
受控组件
在HTML
当中,像input
,textarea
和select
这类表单元素会维持自身状态,并根据用户输入进行更新。在React
中,可变的状态通常保存在组件的state
中,并且只能用 setState()
方法进行更新. React
根据初始状态渲染表单组件,接受用户后续输入,改变表单组件内部的状态。因此,将那些值由React
控制的表单元素称为:受控组件。
受控组件的特点:
- 1 表单元素
- 2 由React通过JSX渲染出来
- 3 由React控制值的改变,也就是说想要改变元素的值,只能通过React提供的方法来修改
- 注意:只能通过setState来设置受控组件的值
1 2 3 4 5 6 7 8 9 10 11
| <input type="text" value={this.state.msg} onChange={this.handleTextChange}/>
handleTextChange = event => { console.log(event.target.value)
this.setState({ msg: event.target.value }) }
|
评论列表案例
1 2 3 4 5
| [ {name: '小明', content: '沙发!!!'}, {name: '小红', content: '小明,居然是你'}, {name: '小刚', content: '小明,放学你别走!!!'}, ]
|
props校验
- 作用:通过类型检查,提高程序的稳定性
- 命令:npm i -S prop-types
- 类型校验文档
- 使用:给类提供一个静态属性
propTypes
(对象),来约束props
1 2 3 4 5 6 7 8 9
| import PropTypes from 'prop-types'
static propTypes = { initCount: PropTypes.number, initAge: PropTypes.number.isRequired }
|
React 单向数据流
组件通讯
- 父 -> 子:props
- 子 -> 父:父组件通过props传递回调函数给子组件,子组件调用函数将数据作为参数传递给父组件
- 兄弟组件:因为React是单向数据流,因此需要借助父组件进行传递,通过父组件回调函数改变兄弟组件的props
- React中的状态管理: flux(提出状态管理的思想) -> Redux -> mobx
- Vue中的状态管理: Vuex
- 简单来说,就是统一管理了项目中所有的数据,让数据变的可控
- 组件通讯
Context特性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| class Grandfather extends React.Component { static childContextTypes = { color: PropTypes.string.isRequired }
getChildContext() { return { color: 'red' } }
render() { return ( <Father></Father> ) } }
class Child extends React.Component { static contextTypes = { color: PropTypes.string }
render() { return ( <h1 style={{ color: this.context.color }}>爷爷告诉文字是红色的</h1> ) } }
class Father extends React.Component { render() { return ( <Child></Child> ) } }
|
react-router
基本概念说明
Router组件本身只是一个容器,真正的路由要通过Route组件定义
使用步骤
在整个应用程序中,只需要使用一次
- 3 使用
<Link to="/movie"></Link>
作为链接地址,并指定to
属性
- 4 使用
<Route path="/" compoent={Movie}></Route>
展示路由内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| import { HashRouter as Router, Link, Route } from 'react-router-dom'
<Router>
<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>
<Route exact path="/" component={HomeContainer}></Route> <Route path="/movie" component={MovieContainer}></Route> <Route path="/about" component={AboutContainer}></Route>
</Router>
|
注意点
<Router></Router>
:作为整个组件的根元素,是路由容器,只能有一个唯一的子元素
<Link></Link>
:类似于vue
中的<router-link></router-link>
标签,to
属性指定路由地址
<Route></Route>
:类似于vue
中的<router-view></router-view>
,指定路由内容(组件)展示位置
路由参数
- 配置:通过
Route
中的path
属性来配置路由参数
- 获取:
this.props.match.params
获取
1 2 3 4 5
| <Route path="/movie/:movieType"></Route>
const type = this.props.match.params.movieType
|
路由跳转
1
| this.props.history.push('/movie/movieDetail/' + movieId)
|
fetch
- 作用:
Fetch
是一个现代的概念, 等同于 XMLHttpRequest
。它提供了许多与XMLHttpRequest
相同的功能,但被设计成更具可扩展性和高效性。
fetch()
方法返回一个Promise
对象
fetch 基本使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
fetch('/api/movie/' + this.state.movieType) .then((response) => response.json()) .then((data) => { console.log(data); this.setState({ movieList: data.subjects, loaing: false }) })
|
跨域获取数据的三种常用方式
JSONP
- 安装:
npm i -S fetch-jsonp
- 利用
JSONP
实现跨域获取数据,只能获取GET
请求
fetch-jsonp
- fetch-jsonp
- 限制:1 只能发送
GET
请求 2 需要服务端支持JSONP
请求
1 2 3 4
| fetchJsonp('https://api.douban.com/v2/movie/in_theaters') .then(rep => rep.json()) .then(data => { console.log(data) })
|
代理
webpack-dev-server
代理配置如下:
- 问题:
webpack-dev-server
是开发期间使用的工具,项目上线了就不再使用 webpack-dev-server
- 解决:项目上线后的代码,也是会部署到一个服务器中,这个服务器配置了代理功能即可(要求两个服务器中配置的代理规则相同)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| devServer: { proxy: { '/api': { target: 'https://api.douban.com/v2', secure: false, changeOrigin: true, pathRewrite: {"^/api" : ""} } } }
fetch('/api/movie/in_theaters') .then(function(data) { return data.json() }) .then(function(rep) { console.log(rep); })
|
CORS - 服务器端配合
1 2 3 4 5 6 7 8 9 10 11 12
| app.use('*', function (req, res, next) { res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Headers', 'Content-Type,Content-Length, Authorization,Accept,X-Requested-With'); res.header('Access-Control-Allow-Methods', 'POST,GET'); next(); });
|
redux
状态管理工具,用来管理应用中的数据
核心
Action:行为的抽象,视图中的每个用户交互都是一个action
Reducer:行为响应的抽象,也就是:根据action
行为,执行相应的逻辑操作,更新state
-比如:点击按钮后,添加任务,那么,添加任务这个逻辑放到 Reducer 中
Store:
- 1 Redux应用只能有一个
store
- 2
getState()
:获取state
- 3
dispatch(action)
:更新state
1 2 3 4 5 6 7 8 9
|
{ type: 'ADD_TODO', name: '要添加的任务名称' }
{ type: 'TOGGLE_TODO', id: 1 }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
|
function todo(state = [], action) { switch(action.type) { case 'ADD_TODO': state.push({ id: Math.random(), name: action.name, completed: false }) return state case 'TOGGLE_TODO': for(var i = 0; i < state.length; i++) { if (state[i].id === action.id) { state[i].completed = !state[i].completed break } } return state default: return state } }
dispatch( { type: 'ADD_TODO', name: '要添加的任务名称' } )
todo(undefined, { type: 'ADD_TODO', name: '要添加的任务名称' })
|
原文作者