《慕课React入门》总结

React 入门与实战

react组件

虚拟DOM的概念 这是React性能高效的核心算法javascript

React为此引入了虚拟DOMVirtual 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

  1. 可组合(Composeable):一个组件易于和其它组件一块儿使用,或者嵌套在另外一个组件内部。若是一个组件内部建立了另外一个组件,那么说父组件拥有它建立的子组件,经过这个特性,一个复杂的UI能够拆分红多个简单的UI组件.
  2. 可重用(Reusable):每一个组件都是具备独立功能的,它能够被使用在多个UI场景
  3. 可维护(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 的语法,它容许 HTMLJavaScript 的混写。
  • JSX 的基本语法规则:遇到 HTML 标签(以 < 开头),就用 HTML 规则解析;遇到代码块(以 { 开头),就用 JavaScript 规则解析
  • JSX 中的注释 {/*注释*/}
export default class BodyIndex extends React.Component{
   render(){
   
    var userName = 'xiaoxiong';
    var boolInput;
    var html1 = 'Mooc&nbsp;Lesson';
    //对 &nbsp; 进行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&nbsp;Lesson' &nbsp;不会被解析为空格
               <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会触发一次renderReact可能会合并操做,再一次性进行render
shouldComponentUpdate --> componentWillUpdate --> render --> componentDidUpdate
  • 父组件发生更新(通常就是props发生改变,可是就算props没有改变或者父子组件之间没有数据交换也会触发render
componentWillReceiveProps --> shouldComponentUpdate --> componentWillUpdate --> render --> componentDidUpdate
  • 调用this.forceUpdate
componentWillUpdate --> render --> componentDidUpdate

总结

  • constructorcomponentWillMountcomponentDidMount只有第一次渲染时候会被调用
  • componentWillUpdatecomponentDidUpdateshouldComponentUpdate 在之后的每次更新渲染以后都会被调用

图片描述
图片描述
图片描述


考虑到性能的问题,若是有些属性的变化,不须要从新刷新页面,咱们是使用 componentShouldUpdate() 进行控制。


官方文档
https://facebook.github.io/re...

参考文献
http://www.jianshu.com/p/4784...


react属性与事件

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 属性须要写成 classNamefor 属性须要写成 htmlFor ,这是由于 classforJavaScript 的保留字。


事件和数据的双向绑定,包含了父子页面之间的参数互传

  • 父组件给子组件传递参数,使用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>
        )
    }
}
  • 子组件为父组件传参使用事件
  • 在子页面中经过调用父页面传递过来的事件 props 进行组件间的参数传递

就是让子页面的变更体如今父页面上,而页面状态的改变由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>
        )
    }
}

组件的Refs
React中多数状况下,是经过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会自动销毁对子组件的引用
  • 不要在RenderRender以前对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);

react样式

内联样式

原生中用 - 链接的样式属性,在这里要采用驼峰写法或加引号"",属性值一概加引号"" ,这样的书写方式,实际上就是加入了内联样式,结构和样式混在一块儿,不是很好的作法。而且这种写法,不能使用伪类、动画。

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进行样式的引用
问题:

  • 怎样实现css的按需加载,使用这个模块时,只加载这个模块的样式。
  • 模块之间的样式是相互独立的,即便使用相同的类名,也不会相互影响。

-

使用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模块化

  • 能够避免全局污染、命名混乱

模块化的优势

  • 全部的样式都是local的,解决的命名冲突和全局污染的问题
  • class名生成规则配置灵活,能够以此来压缩class名(在webpack.config.js文件中配置)
  • 使用某个组件时,只要import组件,再使用便可,无需管理样式。
  • 不须要像书写内部样式同样,属性的名称须要驼峰写法。只需在webpack.config.js中进行配置,书写时,仍是咱们熟悉的css

JSX样式与CSS样式互转

线上转换工具
http://staxmanade.com/CssToRe...


react-router

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配置React环境

//初始化  创建初始化文件
npm init

package.json文件

npmstart是一个特殊的脚本名称,它的特殊性表如今,在命令行中使用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"
  }
}

安装依赖

babelify
Babel实际上是一个编译JavaScript的平台,它的强大之处表如今能够经过编译帮你达到如下目的:

  • 下一代的JavaScript标准(ES6ES7),这些标准目前并未被当前的浏览器彻底的支持
  • 使用基于JavaScript进行了拓展的语言,好比ReactJSX

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文件使浏览器能够识别.
  • ScsslessCSS预处理器
  • ...

这些改进确实大大的提升了咱们的开发效率,可是利用它们开发的文件每每须要进行额外的处理才能让浏览器识别,而手动处理又是很是繁琐的,这就为WebPack类的工具的出现提供了需求。


webpack并不强制你使用某种模块化方案,而是经过兼容全部模块化方案让你无痛接入项目,固然这也是webpack牛逼的地方


webpackgulp的区别

gulp是工具链、构建工具,能够配合各类插件作js压缩,css压缩,less编译 替代手工实现自动化工做

1.构建工具

2.自动化

3.提升效率用

webpack是文件打包工具,能够把项目的各类js文、css文件等打包合并成一个或多个文件,主要用于模块化方案,预编译模块的方案

1.打包工具

2.模块化识别

3.编译模块代码方案
  • 虽然都是前端自动化构建工具,但看他们的定位就知道不是对等的。
  • gulp严格上讲,模块化不是他强调的东西,他旨在规范前端开发流程。
  • webpack更是明显强调模块化开发,而那些文件压缩合并、预处理等功能,不过是他附带的功能。

webpack工做方式

Webpack的工做方式是:把你的项目当作一个总体,经过一个给定的主文件(如:index.js),Webpack将从这个文件开始找到你的项目的全部依赖文件,使用loaders处理它们,最后打包为一个浏览器可识别的JavaScript文件。


Loaders

经过使用不一样的loaderwebpack经过调用外部的脚本或工具能够对各类各样的格式的文件进行处理,好比说分析JSON文件并把它转换为JavaScript文件,或者说把下一代的JS文件(ES6ES7)转换为现代浏览器能够识别的JS文件。或者说对React的开发而言,合适的Loaders能够把ReactJSX文件转换为JS文件。

Loaders须要单独安装而且须要在webpack.config.js下的modules关键字下进行配置,Loaders的配置选项包括如下几方面:

test:一个匹配loaders所处理的文件的拓展名的正则表达式(必须)
loaderloader的名称(必须)
include/exclude:手动添加必须处理的文件(文件夹)或屏蔽不须要处理的文件(文件夹)(可选);
query:为loaders提供额外的设置选项(可选)


依赖模块

// MediaQuery 进行移动端适配
var MediaQuery = require('react-responsive');
<MediaQuery query='(min-device-width:1224px)'>
fetch 向后台请求数据

响应式 以1224px为分界

相关文章
相关标签/搜索