React基础与项目搭建

网络图片

React简介

React在2013年5月开源。
React可能是将来web开发的主流工具。
React不是一个完整的MVC框架,顶多充当一个View层。

  • MVC(Model+View+Controller)

不适用于前端开发,因为View不允许知道用户的输入,用户的输入是从Controller这一层进入。然而用户直接通过view层交互,所以不可能不监听View层。
MVVM(Model+View+ViewModel)——MVC的演变,更适合前端的开发模式。

  • JSX 解析器

将JSX转化成JS,在JS中直接编写HTML。
通过组建的state来维护改变HTML DOM中的内容。
所有和Dom有关的都应该通过框架来维护,
将各功能做成组建——模板(容器)、控件。

  • React组件及数据传递

React是一个组件化的框架,在项目开发之初需要将组件分化


搭建React Start Kit

1. 新建项目文件夹

这里取名newReactproject。
在该目录下打开终端,初始化package.json

npm init

(可以输入项目的名称,这里为new-react-project)

2. 安装插件

npm install --dev-save browser-sync gulp gulp-webpack jsx-loader react react-dom

3. JSX转换成JS的方法

在gulpfile.js中用webpack来将.jsx文件转化为.js文件
(gulp的使用在其他的章节里面会详细说明)

var webpackConfig = {
    output:{
        filename: 'index.js'
    },
    devtool: 'inline-source-map',
    module:{
        loaders:[
            {test: /\.jsx$/, loader: 'jsx-loader'}
        ]
    },
    resolve:{
        extensions:['','.js','.jsx']
    }
}

gulp.task('script',function(){
    gulp.src('./jsx/index.jsx') //以index.jsx为程序的入口
        .pipe(webpack(webpackConfig))
        .pipe(gulp.dest('./www/script/'))
        .pipe(browserSync.stream());//同步浏览器
})

如此一来,就可以在.jsx文件中撰写开发代码了。

同时,需要注意的是:index.jsx中需要引用react

var React = require('react');
var ReactDom = require('react-dom')

如果需要在index.jsx中引用的组件,例如header,footer等,需要使用module.exports

module.exports = React.createClass({
    render:function(){
        return <p>这是一个React页面</p>
    }
})

注意:实际项目中,node_modules文件会被删除 ,那么运行项目的时候需要在终端输入以下命令:

npm install gulp-cli -g
npm install
gulp

4. render语法

render一个变量时,要用html的方法来写。
例如:

ReactDom.render(<Main />,document.getElementById('main'));

前面一个指render的内容,后一个变量是render的目的地

注意:ReactDom.render是新版本的写法,旧版本是React.render()

//jsx语法中类名:
className="className";

//jsx语法中样式:
style={{background:'#f00'}}
//jsx语法:
<tag prop={a:value}>{children}</tag>

//转换成js是:
React.createElement(tag, {a:value}, children)

创建一个组件类,需要注意的几点:

  • React中创建的组件类以大写字母开头,驼峰命名法
  • 在React中使用React.createClass方法创建一个组件类
  • 核心代码:每个组件类都必须实现自己的render方法。输出定义好的组件模板。返回值:null、false、组件模板
  • 注意:组件类只能包含一个顶层标签

5. React Component

  • render:渲染html的方法
  • getInitialState: 返回初始化的State的方法
  • getDefaultProps:返回默认props的方法
    (state维护component内部的状态变化,props是component的外部的参数传入component的方式)
  • propTypes:定义props的类型的属性
  • mixins:用来合并两个组件的属性
  • statics:定义静态对象的属性
  • displayName:定义该类在Debug信息中显示的名称

6. Props

Props是组件自身的属性,一般用于嵌套的内外层组件中,负责传递信息(通常是由父层组件向子组件传递)

注意:props对象中的属性与组件的属性一一对应,不要直接去修改props中的属性值。

//定义WebName
var WebName = React.createClass({
    render:function(){
        return <h1>{this.props.webname}</h1>;
    }
})

//定义WebLink
var WebLink = React.createClass({
    render:function(){
        return <a href={this.props.weblink}>{this.props.weblink}</a>;
    }
})

//定义WebShow
var WebShow = React.createClass({
    render:function(){
        return (
            <div>
                <WebName webname={this.props.wname} />
                <WebLink weblink={this.props.wlink} />
            </div>
        );
    }
})

//渲染
ReactDOM.render(
    <WebShow wname="网站名称" wlink="http://www.baidu.com" />,
    document.getElementById("container")
)
  • ...this.props:

Props提供的语法糖,可以将父组件中的全部属性都复制给子组件

例如:定义一个组件Link,Link组件中只包含一个<a>,我们不给<a>设置任何属性,所有属性全部从父组件肤质得到。代码如下:

var Link = React.createClass({
    render:function(){
        return <a {...this.props}>{this.props.name}</a>
    }
});

ReactDOM.render(
    <Link href="http://www.baidu.com" name="百度"/>,
    document.getElementById("container")
)
//这样父组件上的属性href和name都会被复制到子组件Link中的<a>上。
  • this.props.children:

children是一个例外,不是跟组件的属性对应的。children表示组建的所有子节点

var ListComponent = React.createClass({
    render:function(){
        return (
            <ul>
                {
                    /*
                    列表项数量以及内容不确定,在创建模板时才能确定
                    利用this.props.children从父组件获取需要展示的列表项内容
                    
                    获取到列表项内容后,需要遍历children逐行进行设置
                    使用React.Children.map方法
                    该方法的返回值:数组对象
                    */
                    React.Children.map(this.props.children, function(child){
                        //child是便利得到的父组件的子节点
                        return <li>{child}</li>
                    })
                }
            </ul>
        )
    }
});

//渲染
ReactDOM.render(
    (
        <ListComponent>
            <h1>百度</h1>
            <a href="http://www.baidu.com">http://www.baidu.com</a>
        </ListComponent>
    ),
);

7. 属性验证 propTypes

组件类的属性,用于验证组建实例的属性是否符合要求

var ShowTitle = React.createClass({
    propTypes: {
        //title数据类型必须为字符串
        title:React.PropTypes.string.isRequired
    },
    render:function(){
        return <h1>{this.props.title}</h1>
    }
});

ReactDOM.render(
    <ShowTitle title="123"  />,
    document.getElementById("container")
)
  • 设置组件属性的默认值

通过实现组件的getDefaultProps方法,对属性设置默认值。

var MyTitle = React.createClass({
    getDefaultProps: function(){
        return {
            title: "百度"
        };
    },
    render: function(){
        return <h1>{this.props.title}</h1>
    }
});

ReactDOM.render(
    <MyTitle />,
    document.getElementById("container")
);

8. state:

state和props一样都是组件自身的属性,都可以用来传递数据。
在和用户交互的过程中,组件的状态可能需要更新,就会触发组件的重新渲染。

先举个例子说明React事件的引用:

//定义一个button组件,绑定onClick事件

//React中的事件名称首字母小写,驼峰命名法

var MyButton = React.createClass({
    handleClick: function(){
        alert('点击按钮触发的效果');
    },
    render: function(){
        return (
            <button onClick={this.handleClick}>{this.props.buttonTitle}</button>
        )
    }
});

ReactDOM.render(
    <MyButton buttonTitle="按钮" />,
    document.getElementById("container")
);

在知道事件引用的方法后,再举个经典案例:

需求:创建一个CheckButton的组件,包含一个checkbox类型的input,复选框在选中和未选中的两种状态下回显示不同的文字,即根据状态渲染。

var CheckButton = React.createClass({
    //定义初始状态
    getInitialState: function(){
        return {
            //在这个对象中设置的属性将会存储在state中
            //默认状态是:未选中
            isCheck: false
        }
    },
    //定义事件绑定的方法
    handleChange: function(){
        //修改状态值,通过this.state读取设置的状态值
        this.setState({
            isCheck: !this.state.isCheck
        });
    },
    
    render:function(){
        //根据状态值,设置显示的文字
        //在JSX语法中,不能直接使用if..else函数,使用三目运算符
        var text = this.state.isCheck ? "已选中" : "未选中";
        
        return (
            <div>
                <input type="checkbox" onChange={this.handleChange} />
                {text}
            </div>
        );
    },
});

//渲染
ReactDOM.render(
    <CheckButton />,
    document.getElementById("container")
);

需要注意的时:当state发生变化时,会调用组件内部的render方法

  • 表单

举例说明:

需求:定义一个组件,将用户在输入框内输入的内容进行实时显示
分析:组件与用户交互过程中,存在状态的变化,即输入框的值。

var Input = React.createClass({
    getInitialState: function(){
        return (
            value: "请输入"
        );
    },
    handleChange: function(event){
        //通过event.target.value读取用户输入的值
        this.setState({
            value: event.target.value
        });
    },
    render: function(){
        var value = this.target.value;
        return (
            <div>
                <input type="text" value={value} onChange={this.handleChange} />
                <p>{value}</p>
            </div>
        )
    },
});

ReactDOM.render(
    <input />,
    document.getElementById("container")
);

9. React Component生命周期时间

组件的生命周期分为三个状态

  1. Mounting: 组件挂载,已插入真实DOM

    相关接口:

    (1)componentWillMount - 组件将要挂载。
    在render之前执行,但仅执行一次,即使多次重复渲染该组件,或者改变了组件的state

    (2)componentDidMount - 组件已经挂载。
    在render之后执行,同一个组件重复渲染只执行一次

  2. Updating: 组件更新,正在被重新渲染

    相关接口:

    (1)componentWillReceiveProps(object nextProps)- 已加载组件收到新的props之前调用,注意组件初始化渲染时则不会执行

    (2)shouldComponentUpdate (object nextProps, object nextState) - 组件判断是否重新渲染时调用。该接口实际是在组件收到了新的props或者新的state的时候会立即调用,然后通过下面两个方法来进行更新

    (3)componentWillUpdate (object nextProps, object nextState) - 组件将要更新

    (4)componentDidUpdate (object prevProps, object prevState) - 组件已经更新

  3. Unmounting: 组件移出,已移出真实DOM

    相关接口:

    componentWillUnmount - 在组件要被移除之前的时间点触发,可以利用该方法来执行一些必要的清理组件的工作

生命周期中与props和state相关的接口:

(1)getDefaultProps - 设置props属性默认值

(2)getInitialState - 设置state属性初始值

总结下来,一共九个接口,如下:

  • componentWillMount:组件的html即将加载时调用
  • componentDidMount:组件的html已经加载时调用
  • componentWillReceiveProps:组件props改变时调用
  • shouldComponentUpdate:判定组件是否更新HTML
  • componentWillUpdate:组件即将更新HTML时调用
  • componentDidUpdate:组件HTML更新完后调用
  • componentWillUnmount:组件HTML即将卸载时调用
  • getDefaultProps:设置props属性默认值
  • getInitialState:设置state属性初始值

组件的生命周期可分为四个阶段创建实例化更新销毁

生命周期个阶段介绍

var Demo = React.createClass({
    /*
    一、创建阶段
        流程:只调用getDefaultProps方法
    */
    getDefaultProps: function(){
        //在创建类的时候被调用,设置this.props的默认值
        console.log("getDefaultProps");
        return {};
    },
    
    /*
    二、实例化阶段
        流程:
        getInitialState, 
        componentWillMount, 
        render, 
        componentDidMount
    */
    getInitialState: function(){
        //设置this.state的默认值
        console.log("getInitialState");
        return {};
    },
    componentWillMount: functon(){
        //在render之前调用
        console.log("componentWillMount");
    },
    render:function(){
        //渲染并返回一个虚拟DOM
        console.log("render");
        return <div>Hello React</div>
    },
    componentDidMount: function(){
        //在render之后调用
        //在该方法中,React会使用render方法返回的虚拟DOM对象创建真实的DOM结构
        //可以在这个方法中读取DOM节点
        console.log("componentDidMount");
    },
    
    /*
    三、更新阶段
        流程:
        componentWillReceiveProps, 
        shouldComponentUpdate (如果返回值是false,后三个方法不执行),
        componentWillUpdate, 
        render, 
        componentDidUpdate
    */
    componentWillReciveProps: function(){
        console.log("componentWillReciveProps")
    },
    shouldComponentUpdate: function(){
        //是否需要更新
        console.log("shouldComponentUpdate");
        return true;
    },
    componentWillUpdae: function(){
        console.log("componentWillUpdae");
    },
    componentDidUpdate: functon(){
        console.log("componentDidUpdate")
    },
    
    /*
    四、销毁阶段
        流程:componentWillUnmount
    */
    componentWillUnmount: function(){
        console.log("componentWillUnmount")
    },
});

//第一次创建并加载组件
//将得到以下顺序:getDefaultProps,getInitialState,componentWillMount,render,componentDidMount
ReactDOM.render(
    <Demo />,
    document.getElementById("container")
);

//第二次创建并加载组件
//将在第一次加载的基础上,添加以下顺序:componentWillReceiveProps,shouldComponentUpdate,componentWillUpdate,render,componentDidUpdate
ReactDOM.render(
    <Demo />,
    document.getElementById("container")
);

//移除组件
ReactDOM.unmountComponentAtNode(document.getElementById("container"))

10. 设置组件的样式

  • 内连样式
  • 对象样式
  • 选择器样式

注意:在ReactHTML5中,样式的书写方式是有所区别的

主要体现在:

  • HTML5以;结尾,React以,结尾
  • HTML5中key、value都不加引号,React中属于JavaScript对象,key的名字不能出现“-”,需要使用驼峰命名法。如果value为字符串,需要加引号。
  • HTML5中,value如果是数字,需要带单位,React中不需要带单位。
//样式
<style>
    .pStyle {
        font-size:20px;
    }
</style>

//创建设置h1样式对象
var hStyle = {
    backgroundColor:"green",
    color:"red"
}

var ShowMessage = React.createClass({
    render:function(){
        return(
            //内连样式
            <div style={{backgroundColor:"yellow",borderWidth:5,borderColor:"black",borderStyle:"solid"}}> 
                //对象样式
                <h1 style={hStyle}>{this.props.firstRow}</h1>
                //选择器样式
                <p className="pStyle">{this.props.secondRow}</p>
            </div>
        )
    }
})

//渲染效果
ReactDOM.render(
    <ShowMessage firstRow="第一行" secondRow="第二行"/>,
    document.getElementById("container")
)

注意:在React中使用选择器样式设置组件样式时,属性名不能使用class,因为classReact中的保留字,需要使用className替换。
类似的还有使用htmlfor替换for,因为for也是React中的保留字。

11. 复合组件 —— 也称为组合组件,父子组件(创建多个组件合成一个组件)

//定义WebName组件
var WebName = React.createClass({
    render:function(){
        return <h1>网站名称</h1>;
    }
});

//定义WebLink组件
var WebLink = React.createClass({
    render:function(){
        return <a href="http://www.baidu.com">http://www.baidu.com</a>;
    }
});

//定义WebShow复合组件
var WebShow = React.createClass({
    render:function(){
        return (
            <div>
                <WebName />
                <WebLink />
            </div>
        );
    }
    
});

//渲染
ReactDOM.render(
    <WebShow />,
    document.getElementById("container")
);

注:以上所有内容都是本人的学习笔记和总结,仅供学习和参考,如果有遗漏或者不当的地方请谅解,请勿转载。

@anMoo韩魔
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,657评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,662评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,143评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,732评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,837评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,036评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,126评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,868评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,315评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,641评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,773评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,859评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,584评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,676评论 2 351

推荐阅读更多精彩内容