虚拟DOM的概念 这是React性能高效的核心算法javascript
React
为此引入了虚拟DOM
(Virtual DOM
)的机制。基于React
进行开发时全部的DOM
构造都是经过虚拟DOM
进行,每当数据变化时,React
都会从新构建整个DOM
树,而后React
将当前整个DOM
树和上一次的DOM
树进行对比,获得DOM
结构的区别,而后仅仅将须要变化的部分进行实际的浏览器DOM
更新。尽管每一次都须要构造完整的虚拟DOM
树,可是由于虚拟DOM
是内存数据,性能是极高的,而对实际DOM
进行操做的仅仅是Diff
部分,于是能达到提升性能的目的。css
React 组件,理解什么是组件化html
像插入普通
HTML
标签同样,在网页中插入这个组件前端
所谓组件,即封装起来的具备独立功能的UI
部件java
React
推荐以组件的方式去从新思考UI
构成,将UI
上每个功能相对独立的模块定义成组件,而后将小的组件经过组合或者嵌套的方式构成大的组件,最终完成总体UI
的构建。React
而言,则彻底是一个新的思路,开发者从功能的角度出发,将UI
分红不一样的组件,每一个组件都独立封装。React
一个组件应该具备以下特征:node
Composeable
):一个组件易于和其它组件一块儿使用,或者嵌套在另外一个组件内部。若是一个组件内部建立了另外一个组件,那么说父组件拥有它建立的子组件,经过这个特性,一个复杂的UI
能够拆分红多个简单的UI
组件.Reusable
):每一个组件都是具备独立功能的,它能够被使用在多个UI
场景Maintainable
):每一个小的组件仅仅包含自身的逻辑,更容易被理解和维护语法react
// Header组件 // export 导出供外部使用 export default class ComponentHeader extends React.Component{ //render方法:用于解析自己类的输出 render(){ return (<header><h1>这里是头部</h1></header>) } } // Index组件 import ComponentHeader from './component/header'; class Index extends React.Component{ render(){ // 插入普通 HTML 标签同样 return <ComponentHeader/> } } // 将模版转化为HTML语言,并插入到指定节点 // 至关于程序的入口 ReactDOM.render(<Index/>,document.getElementById('one'))
React 多组件嵌套webpack
各个组件之间相互独立,利于维护,重用。在Index
文件中进行调用,简明。
须要变动时,咱们只须要改变相应的组件,全部引用该组件的的页面就都发生的变动,很好维护。git
import ComponentHeader from './component/header'; import BodyIndex from './component/body'; import ComponentFooter from './component/footer'; class Index extends React.Component{ render(){ //将组件赋值给一个变量,这样方便作判断 var component; if(/*条件*/){ component = <ComponentLoginedHeader/>; }else{ component = <ComponentHeader/>; } return ( <div> { component} <BodyIndex/> <ComponentFooter/> </div> ) } }
JXS内置表达式github
HTML
语言直接写在 JavaScript
语言之中,不加任何引号,这就是 JSX
的语法,它容许 HTML
与 JavaScript
的混写。JSX
的基本语法规则:遇到 HTML
标签(以 <
开头),就用 HTML
规则解析;遇到代码块(以 {
开头),就用 JavaScript
规则解析{/*注释*/}
export default class BodyIndex extends React.Component{ render(){ var userName = 'xiaoxiong'; var boolInput; var html1 = 'Mooc Lesson'; //对 进行Unicode转码 var html2 = 'Mooc\u0020Lesson' return ( <div> <h2>页面主体内容</h2> // 三元表达式的使用 <p>{userName = '' ?'用户尚未登陆':'用户名:' +userName }</p> // 使用{boolInput}进行值的绑定 <input type='button' value='按钮' disable={boolInput}> //解析html <p>{html1}</p> //'Mooc Lesson' 不会被解析为空格 <p>{html2}</p> //'Mooc Lesson' <p dangerouslySetInnerHTML={{__html:html1}}></p> //'Mooc Lesson' 此方法可能会存在xss攻击 </div> ) } }
声明周期,纵观整个React的生命周期
在ES6中,一个React组件是用一个class来表示的
过程描述
在React
中有4
中途径能够触发render
Initial Render
constructor --> componentWillMount --> render -->conponentDidMount
this.setState
(并非一次setState
会触发一次render
,React
可能会合并操做,再一次性进行render
)shouldComponentUpdate --> componentWillUpdate --> render --> componentDidUpdate
props
发生改变,可是就算props
没有改变或者父子组件之间没有数据交换也会触发render
)componentWillReceiveProps --> shouldComponentUpdate --> componentWillUpdate --> render --> componentDidUpdate
this.forceUpdate
componentWillUpdate --> render --> componentDidUpdate
总结
constructor
、componentWillMount
、componentDidMount
只有第一次渲染时候会被调用componentWillUpdate
、componentDidUpdate
、shouldComponentUpdate
在之后的每次更新渲染以后都会被调用考虑到性能的问题,若是有些属性的变化,不须要从新刷新页面,咱们是使用 componentShouldUpdate()
进行控制。
官方文档
https://facebook.github.io/re...
参考文献
http://www.jianshu.com/p/4784...
state 属性控制React的一切
组件自身的状态,props为外部传入的状态
state
对组件作了更新以后,会立刻反应到虚拟DOM
上,最后更新到DOM
上。这个过程是自动完成的。
组件免不了要与用户互动,React
的一大创新,就是将组件当作是一个状态机,一开始有一个初始状态,而后用户互动,致使状态变化,从而触发从新渲染 UI
.
export default class BodyIndex extends React.Component{ // 初始化 constructor(){ super();//调用基类的全部初始化方法 //state的做用域是当前组件,不会污染其余模块 this.state = { username:"xiaoxiong" }; }; //修改state setTimeOut(()=>{ this.setState({username:"miaomiao"}) },4000); render(){ return ( <div> <h2>页面主体内容</h2> // 引用state值 <p>{this.state.username}</p> </div> ) } }
Props属性
其余组件传递参数,对于本模块来讲,属于外来属性。
//使用组件时,传入参数 < BodyIndex userid="123" username={xiaoxiong}/> export default class BodyIndex extends React.Component{ render(){ return ( <div> <h2>页面主体内容</h2> // 接收参数 <p>{this.props.userid}</p> <p>{this.props.username}</p> </div> ) } }
添加组件属性,有一个地方须要注意,就是 class
属性须要写成 className
,for
属性须要写成 htmlFor
,这是由于 class
和 for
是 JavaScript
的保留字。
事件和数据的双向绑定,包含了父子页面之间的参数互传
props
事件绑定
export default class BodyIndex extends React.Component{ constructor(){ super(); this.state={ username="xiaoxiong", age:20 } }; changeUserInfo(){ this.setState({age:50}) }; render(){ return ( <div> <h2>页面主体内容</h2> <p>{this.props.username} {this.props.age }</p> //事件绑定 ES6写法 //ES5写法 onClick=this.chanchangeUserInfo <input type="button" value="提交" onClick=this.chanchangeUserInfo.bind(this)/> </div> ) } }
就是让子页面的变更体如今父页面上,而页面状态的改变由state
控制,所以咱们让父页面经过props
将函数传递给子页面,此函数能够取得子页面的值,并改变父页面的state
//子组件 export default class BodyChild extends React.Component{ render(){ return ( <div> // 调用父组件传过来的函数 <p>子页面输入:<input type="text" onChange={this.props.handler}/></p> </div> ) } } //父组件 import BodyChild from "./component/bodychild"; export default class Body extends React.Component{ constructor(){ super(); this.state={ age:20 } } // 父页面的函数,能够操控父页面的 state handler(e){ this.setState({age:e.target.value}) } render(){ return ( <div> <p>{this.state.age}<p> <BodyChild handler={this.handler.bind(this)}/> </div> ) } }
可复用组件,真正让React开发快速、高效的地方
使用组件时,传递
props
,在组件定义的文件中可以使用这些props
export default class BodyIndex extends React.Component{ render(){ return ( <div> <p>{this.props.userid}<p> <p>{this.props.username}<p> </div> ) } } // 对传递过来的 props 的类型进行约束 BodyIndex.propTypes = { // userid为number类型且必须传递 userid:React.propTypes.number.isRuquired }; // 设置默认值,使用组件时,若是不传递参数,将显示默认值 BodyIndex.defaultProps = { username:"xiaoxiong" };
组件多层嵌套时,传递参数的简便方法
export default class Body extends React.Component{ render(){ return ( <div> //在父页面定义props <BodySon username="xiaoxiong" age="25"/> </div> ) } } export default class BodySon extends React.Component{ render(){ return ( <div> <p>{this.props.username}</p> //取得父页面(BodySon)的全部props属性 <Bodygrandson ...this.props /> </div> ) } } export default class Bodygrandson extends React.Component{ render(){ return ( <div> // 使用传递过来的props <p>{this.props.username}{this.props.age}</p> </div> ) } }
组件的RefsReact
中多数状况下,是经过state
的变化,来从新刷新页面。但有时也须要取得html
节点,好比对input
进行focus
等;下面咱们来看下,怎么取得原生的html
节点,并对其进行操做。
export default class BodyChild extends React.Component{ handler(){ //第一种方式 var mySubmitBotton = document.getElementById("submitButton"); //这样也是能够的 mySubmitBotton.style.color="red"; ReactDOM.findDOMNode(mySubmitBotton).style.color = 'red'; //第二种方式 this.refs.submitBotton.style.color = 'red'; } render(){ return ( <div> <input id="submitButton" ref="submitButton"type="button" onClick={this.handler.bind(this)}/> </div> ) } }
refs
是访问到组件内部DOM
节点的惟一可靠方式refs
会自动销毁对子组件的引用Render
或Render
以前对Refs
进行调用,由于Refs
获取的是真实的DOM
节点,要在插入真实DOM
节点以后调用。Refs
DOM
时,用方法1
便可独立组件间共享Mixins
在全部的组件见共享一些方法
ES6
中使用mixin
须要插件支持
npm install react-mixin --save
使用
//mixin.js const MixinLog = { //有自身的生命周期 componentDidMount(){ console.log('MixinLog ComponentDidMount'); }, log(){ console.log('Mixins'); } }; //暴露供外部使用 export default MixinLog; //body.js //导入MixinLog import MixinLog from './component/mixin.js'; import ReactMixin from 'react-mixin'; export default class Body extends React.Component{ handler(){ //调用MixinLog中方法 MixinLog.log(); } render(){ return ( <div> <p>{this.props.username}</p> </div> ) } } ReactMixin(Body.prototype,MixinLog);
内联样式
原生中用 -
链接的样式属性,在这里要采用驼峰写法或加引号""
,属性值一概加引号""
,这样的书写方式,实际上就是加入了内联样式,结构和样式混在一块儿,不是很好的作法。而且这种写法,不能使用伪类、动画。
export default class Header extends React.Component{ render(){ //将样式定义为一个变量 //注意样式属性的书写 const styleComponentHeader = { header:{ backgroundColor:"#333", color:"#fff", "padding-top":"15px", paddingBottom:"15px", }, //还能够定义其余样式 }; return ( //使用样式,这样写,实际上就是内联样式 <header style={styleComponentHeader. header}> <h1>头部</h1> </header> ) } }
也能够在index.html
文件中,引入css
文件,并在须要使用样式的地方加入类名(className
),但这种写法会污染全局。
//index.html <header> <link href="../css.js"> </header> <div id="one"></div> //组件 export default class Header extends React.Component{ render(){ return ( //使用类名,加入样式 <header className="header"> <h1>头部</h1> </header> ) } }
内联样式中的表达式
根据state
的值,控制样式
export default class Header extends React.Component{ constructor(){ super(); this.state={ miniHeader:false } } //点击头部,样式发生变化 swithHeader(){ this.setState({ miniHeader:!this.state.miniHeader }) } render(){ const styleComponentHeader = { header:{ backgroundColor:"#333", color:"#fff", "padding-top":"15px", //根据state的值,变化 paddingBottom的值 //样式中可使用表达式 paddingBottom: (this.state.miniHeader)?"3px":"15px" }, }; return ( <header style={styleComponentHeader. header} onClick={this.swithHeader.bind(this)}> <h1>头部</h1> </header> ) } }
CSS模块化,学习如何使用require进行样式的引用
问题:
-
使用webpack,安装相关插件
//使用require导入css文件 npm install css-loader --save //计算以后的样式加入页面中 npm install style-loader --save
webpack.config.js
文件中进行配置
将css
文件导入相应的组件后,会生成一个对应关系
footer:_footer_minifooter_vxs08s
_footer_minifooter_vxs08s
是按照必定的规则生成一个惟一的类名,当咱们为当前组件应用 .footer
类的时候,就会按照这个对应关系去样式文件中找,而后应用响应的样式。
其实是改变了样式文件中类的名称,使其惟一。
这样即便将全部文件打包到一块儿,也不会引发冲突。
不一样的文件能够 使用相同的类名,不会冲突,由于模块化以后,类名都进行了转换。
module:{ loaders:[ { test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader', query: { presets: ['es2015','react'] } }, { test: /\.css$/, //modules 模块化配置 loader: 'style-loader!css-loader?modules' }, ] },
使用
//footer.css .footer{ background-color:#333; } //Footer组件 //导入css样式,这样这个样式文件只做用于这个组件 var footerCss = require('./css/footer.css'); export default class Footer extends React.Component{ render(){ return ( <footer className="footerCss.footer"> <h1>底部</h1> </footer> ) } }
总结
为何要css模块化
模块化的优势
class
名生成规则配置灵活,能够以此来压缩class
名(在webpack.config.js
文件中配置)import
组件,再使用便可,无需管理样式。webpack.config.js
中进行配置,书写时,仍是咱们熟悉的css
。JSX样式与CSS样式互转
线上转换工具
http://staxmanade.com/CssToRe...
Router 概念
控制页面之间的层级关系
底层机制
经过状态的改变,致使组件重新渲染,从而改变页面显示
React: state/props -> Component ->UI
经过改变url
,致使Router
变化,从而改变页面显示
React:location(hasj) -> Router ->UI
hashHistory
&& browserHistory
慕课老师的demo
使用的是hashHistory
,而另外一种方式则是使用browserHistory
。
若是但愿使用browserHistory
达到hashHistory
的效果,则须要作2
件事情:
一、服务器支持。若是开发服务器使用的是webpack-dev-server
,加上--history-api-fallback
参数就能够了。
二、做为项目入口的HTML
文件中,相关外链的资源路径都要改成绝对路径,即以"/"
根路径开头。
安装
// 版本 2.8.1 npm install react-router
使用
component
指定组件 path
指定路由的匹配规则
router
能够进行嵌套ComponentDetails
嵌套在Index
页面中,咱们要在Index
中进行展现。
//index.js export default class Index extends React.Component{ render(){ return ( <div> //此处展现的是ComponentDetails页面 {this.props.children} </div> ) } }
import React from 'react'; import ReactDOM from 'react-dom'; import Index from './index'; import { Router, Route, hashHistory} from 'react-router'; import ComponentList from './components/list'; import ComponentHeader from './components/header'; import ComponentDetails from './components/details'; export default class Root extends React.Component{ render(){ //这里替换了以前的index,变成了程序的入口 (注意修改webpack.conf.js中的入口文件为root.js) return ( <Router history={hashHistory}> <Route component={Index} path="/"> <Route component={ComponentDetails} path="details"></Route> </Route> <Route component={ComponentList} path="list/:id"></Route> <Route component={ComponentHeader} path="header"></Route> </Router> ); }; } ReactDOM.render( <Root/>, document.getElementById('example') );
有了Router
以后,用Link
进行跳转
<Link to={`/`}>首页</Link> <Link to={`/details`}>嵌套的详情页面</Link>
Router 参数传递
//在Router中定义参数名称 <Route component={ComponentList} path="list/:id"></Route> //在Link中传入参数 <Link to={`/list`}></Link> //在list组件页面中读取传入的参数 render(){ <div>{this.props.params.id}</div> }
//初始化 创建初始化文件 npm init
package.json
文件
npm
的start
是一个特殊的脚本名称,它的特殊性表如今,在命令行中使用npm start
就能够执行相关命令,若是对应的此脚本名称不是start,想要在命令行中运行时,须要这样用npm run {script name}
如npm run build
{ "name": "reactconf",//项目名称 "version": "1.0.0",//项目版本 "description": "",//项目描述 "main": "root.js",//入口文件 //自定义的脚本任务 "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "start": "webpack-dev-server --inline --content-base ." }, "author": "", "license": "ISC", "dependencies": { "antd": "^2.10.1", "babel-preset-es2015": "^6.24.1", "babel-preset-react": "^6.24.1", "babelify": "^7.3.0", "css-loader": "^0.25.0", "react": "^15.5.4", "react-dom": "^15.5.4", "react-mixin": "^2.0.2", "react-router": "^2.8.1", "style-loader": "^0.13.1", "webpack": "^1.13.2", "webpack-dev-server": "^1.16.1" }, "devDependencies": { "babel-core": "^6.24.1", "babel-loader": "^7.0.0" } }
安装依赖
babelifyBabel
实际上是一个编译JavaScript
的平台,它的强大之处表如今能够经过编译帮你达到如下目的:
JavaScript
标准(ES6
,ES7
),这些标准目前并未被当前的浏览器彻底的支持JavaScript
进行了拓展的语言,好比React
的JSX
babel-preset-react react
转码规则
babel-preset-es2015 ES2015
转码规则
npm install react react-dom babelify --save npm install babel-preset-react babel-preset-es2015 --save
使用webpack
打包
安装相关的包
npm install webpack -g npm install webpack-dev-server -g npm install webpack --save npm install webpack-dev-server --save
//webpack.config.js // 引用webpack相关的包 var webpack = require('webpack'); var path = require('path'); var WebpackDevServer = require("webpack-dev-server"); module.exports = { //入口文件 __dirname 项目根目录 //“__dirname”是node.js中的一个全局变量,它指向当前执行脚本所在的目录 context: __dirname + "/src", entry:"./js/root.js", //loaders module:{ loaders:[ { test: /\.js$/, exclude: /node_modules/, loader: 'babel', query: { presets: ['es2015','react'] } }, //下面是使用 ant-design 的配置文件 再也不使用 ?modules 由于样式是全局的 再也不须要局部样式 { test: /\.css$/, //modules 模块化配置 loader: 'style-loader!css-loader?modules' }, ] }, //出口文件 output: { path: __dirname + '/src/', filename: 'bundle.js' }, };
打包命令
// 正常状况下 webpack //配置热加载状况下 webpack-dev-server --inline
使用 --watch
但是自动打包,但不会自动刷新
能够用content-base
设定 webpack-dev-server
伺服的 directory
(就是指管这个路径下文件的变更),若是不进行设定的话,默认是在当前目录下
webpack-dev-server --content-base -src --inline
webpack
为何使用webpack
现今的不少网页其实能够看作是功能丰富的应用,它们拥有着复杂的JavaScript
代码和一大堆依赖包。为了简化开发的复杂度,前端社区涌现出了不少好的实践方法
TypeScript
这种在JavaScript
基础上拓展的开发语言,使咱们可以实现目前版本的JavaScript
不能直接使用的特性,而且以后还能能装换为JavaScript
文件使浏览器能够识别.Scss
,less
等CSS
预处理器这些改进确实大大的提升了咱们的开发效率,可是利用它们开发的文件每每须要进行额外的处理才能让浏览器识别,而手动处理又是很是繁琐的,这就为WebPack
类的工具的出现提供了需求。
webpack
并不强制你使用某种模块化方案,而是经过兼容全部模块化方案让你无痛接入项目,固然这也是webpack
牛逼的地方
webpack
和gulp
的区别
gulp
是工具链、构建工具,能够配合各类插件作js
压缩,css
压缩,less
编译 替代手工实现自动化工做
1.构建工具 2.自动化 3.提升效率用
webpack
是文件打包工具,能够把项目的各类js
文、css
文件等打包合并成一个或多个文件,主要用于模块化方案,预编译模块的方案
1.打包工具 2.模块化识别 3.编译模块代码方案
gulp
严格上讲,模块化不是他强调的东西,他旨在规范前端开发流程。webpack
更是明显强调模块化开发,而那些文件压缩合并、预处理等功能,不过是他附带的功能。webpack工做方式
Webpack
的工做方式是:把你的项目当作一个总体,经过一个给定的主文件(如:index.js
),Webpack
将从这个文件开始找到你的项目的全部依赖文件,使用loaders
处理它们,最后打包为一个浏览器可识别的JavaScript
文件。
Loaders
经过使用不一样的
loader
,webpack
经过调用外部的脚本或工具能够对各类各样的格式的文件进行处理,好比说分析JSON
文件并把它转换为JavaScript
文件,或者说把下一代的JS
文件(ES6
,ES7
)转换为现代浏览器能够识别的JS
文件。或者说对React
的开发而言,合适的Loaders
能够把React
的JSX
文件转换为JS
文件。
Loaders
须要单独安装而且须要在webpack.config.js
下的modules
关键字下进行配置,Loaders
的配置选项包括如下几方面:
test
:一个匹配loaders
所处理的文件的拓展名的正则表达式(必须)loader
:loader
的名称(必须)include/exclude
:手动添加必须处理的文件(文件夹)或屏蔽不须要处理的文件(文件夹)(可选);query
:为loaders
提供额外的设置选项(可选)
// MediaQuery 进行移动端适配 var MediaQuery = require('react-responsive'); <MediaQuery query='(min-device-width:1224px)'>
fetch 向后台请求数据
响应式 以1224px
为分界