15

react项目基础-changpaozhe

 4 years ago
source link: https://blog.51cto.com/11233559/2443713
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

一 react简介

1 react 简介

React 是一个用于构建用户界面的 JAVASCRIPT 库。
React 主要用于构建UI,很多人认为 React 是 MVC 中的 V。
React 起源于 Facebook 的内部项目,用来架设 Instagram 的网站,并于 2013 年 5 月开源。
React 拥有较高的性能,代码逻辑非常简单,越来越多的人已开始关注和使用它。


React 是解决前端MVC框架中的view视图层的问题
M 是指数据模型,V是指显示的问题,C是指控制的问题

2 React 特点

1.声明式设计 −React采用声明范式,可以轻松描述应用。

2.高效 −React通过对DOM的模拟,最大限度地减少与DOM的交互。

3.灵活 −React可以与已知的库或框架很好地配合。

4.JSX − JSX 是 JavaScript 语法的扩展。React 开发不一定使用 JSX ,但我们建议使用它。

5.组件 − 通过 React 构建组件,使得代码更加容易得到复用,能够很好的应用在大项目的开发中。

6.单向响应的数据流 − React 实现了单向响应的数据流,从而减少了重复代码,这也是它为什么比传统数据绑定更简单。

3 Virtual DOM

DOM (文档对象模型 document object model)

将网页所有内容映射到一颗树形结构的层级对象模型上,浏览器提供对DOM的支持,用户可以是用脚本调用DOM,API来动态修改DOM节点,从而达到修改网页的目的,这种修改是浏览器中完成的,浏览器会根据DOM的改变来重绘改变的DOM节点部分。

修改DOM 重新渲染成本太高,前端框架为了提高效率,应尽量减少DOM重绘,踢出了virtual DOM,所有的修改现在virtual DOM上完成,通过比较算法,找到浏览器DOM之间的差异,使用这个差异操作DOM,浏览器只需要渲染这部分的改变就行了。

React 实现了DOM Diff算法可以高效对比virtual DOM 和 DOM的差异。

4 JSX 语法

JSX 是一种JavaScript和XML混写的语法,是JavaScript的扩展。

1 基本结构

基本结构如下

render(
  <div>
    <div>
      <div>content</div>
    </div>
  </div>,
  document.getElementById('example')
);

2 JSX 规范

1 首字母是小写就是html标记,首字母是大写就是组件
2 要求严格的HTML标记,要求所有标签都必须闭合,br可应该写成<br />,/前面要留一个空格
3 单行省略小括号,多行请使用小括号
4 元素有嵌套,建议多行,注意缩进
5 JSX表达式:使用{}括起来,如果大括号内使用了引号,会当做字符串处理,如<div>'2>1?true:flase'{}</div>里面的表达式就成为了字符串,结果仍然是此值。

二 项目安装和配置文件详解及基本操作

将项目开发基础文件test.zip解压。并用这个目录作为项目的根目录,在项目根目录中,执行下面命令们进行和安装,安装完成后,会生成一个node_modules,里面是安装的所有依赖的模块


项目包如下
链接:https://pan.baidu.com/s/1C-ZY9rWU-8ZugE4EwVveWw
提取码:744p

解压目录如下

react项目基础

在项目根目录下执行

npm  install  
react项目基础

2 配置文件详解

1 package.json 文件

npm init 产生的文件,里面记录项目的信息,所有项目依赖。

{
  "name": "test",
  "version": "1.0.0",
  "description": "magedu blog project",
  "main": "index.js",

  "scripts": {   //项目管理 
    "test": "jest",
    "start": "webpack-dev-server --config webpack.config.dev.js --hot --inline", // start指定启动webpack的 devserver包,--hot 表明是热加载,及修改后直接加载
    "build": "rimraf dist && webpack -p --config webpack.config.prod.js" // build使用webpack构建打包
  },

  "repository": {},   //处理版本管理相关,可通过此处进行连接git服务器用于提交代码至git服务器
  "author": "zhang",
  "license": "MIT",

  "devDependencies": {  //开发时依赖,不会打包到目标文件中,对应npm  install  module-name --save-dev,
    // babel 转义,因为开发用了很多的ES6语法,从6.x开始babel拆分成很多插件,需要什么引入什么,
    "babel-core": "^6.24.1", //核心 
    "babel-jest": "^19.0.0",  
    "babel-loader": "^6.4.1", //webpack的loader,webpack是基于loader的
    "babel-plugin-transform-decorators-legacy": "^1.3.4",
    "babel-plugin-transform-runtime": "^6.23.0",
    "babel-preset-env": "^1.4.0",  //预设的转换插件
    "babel-preset-react": "^6.24.1",
    "babel-preset-stage-0": "^6.24.1",
    "css-loader": "^0.28.0",  //css影视相关。包括css-loader,less,less-loader.style-loader 
    "html-webpack-plugin": "^2.28.0",
    "jest": "^19.0.2",
    "less": "^2.7.2",
    "less-loader": "^4.0.3",
    "react-hot-loader": "^4.3.12",  //react热加载插件,希望改动保存后,直接在页面上直接反馈出来,不需要手动刷新
    "rimraf": "^2.6.2",
    "source-map": "^0.5.6",  //文件打包。js会合并或者压缩,没法调试,用它来看js源文件是什么,source-map-loader也是需要webpack的loader
    "source-map-loader": "^0.2.1",
    "style-loader": "^0.16.1",
    "uglify-js": "^2.8.22",
    "webpack": "^2.4.1", //打包工具
    "webpack-dev-server": "^2.4.2"  //启动一个开发的server 
  },
  "dependencies": {  //运行时依赖,会打包到项目中,对应npm  install  module-name  --save 
    "antd": "^3.10.9", //基于react实现,蚂蚁金服开源的react的UI库,
    "axios": "^0.16.1",  //异步请求支持 
    "babel-polyfill": "^6.23.0",  //解决浏览器api不支持问题
    "babel-runtime": "^6.23.0",
    "mobx": "^4.6.0",  //状态管理库,透明化
    "mobx-react": "^5.4.2",// 和react结合的模块
    "react": "^16.6.3",  //开发的主框架 
    "react-dom": "^16.6.3",  //支持DOM  
    "react-router": "^4.3.1",// 支持路由
    "react-router-dom": "^4.3.1"  //DOM绑定路由
  }
}

2 babel配置

.babelrc babel转义的配置文件

{
  "presets": [
    "react",
    "env",
    "stage-0"
  ],
  "plugins": [
    "transform-decorators-legacy", 
    "transform-runtime",
    "react-hot-loader/babel"
  ]
}

3 webpack配置

webpack.config.dev.js,这是一个符合commonjs的模块

/**
 * Created by magedu on 2017/4/20.
 */

const path = require('path');
const webpack = require('webpack');

module.exports = {  //导出
    devtool: 'source-map',  //导出devtools是source-map
    entry: {  //描述入口,entry 如果是一个字符串,定义就是入口文件,如果是一个数组,里面包含入口文件,另一个参数可以用来配置一个服务器,此处使用热加载插件,可自动刷新
        'app': [
            './src/index'
        ]
    },
    output: {  //输出,输出目录是_dirname+'dist',名字叫 bundle.js。
        path: path.join(__dirname, 'dist'),
        filename: 'bundle.js',
        publicPath: '/assets/'
    },
    resolve: {  //指定解析什么文件类型,这里设置对js文件解析
        extensions: ['.js']
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules/,  //  node_modules/打包排除目录。这句话一定要有,否则,编译就会把这个目录下所有文件拿进来,此中包含的文件很大
                use: [
                    { loader: 'babel-loader' } //rule中对.js结尾的文件但不在node_modules目录的文件进行热加载loader和转译babel-loader。
                ]
            }, {
                test: /\.css$/,
                use: [
                    { loader: "style-loader" },
                    { loader: "css-loader" },
                ]
            }, 
            {
                test: /\.less$/,
                use: [
                    { loader: "style-loader" },
                    { loader: "css-loader" },
                    { loader: "less-loader" }
                ]
            }
        ]
    },
    plugins: [  //webpack的插件
        new webpack.optimize.OccurrenceOrderPlugin(true),
        new webpack.HotModuleReplacementPlugin(),
        new webpack.DefinePlugin({'process.env': {NODE_ENV: JSON.stringify('development')}})
    ],
    devServer: {
        compress: true,
        port: 3000,  //devserver启动的端口,
        publicPath: '/assets/',
        hot: true,//支持热加载 
        inline: true,
        historyApiFallback: true,
        stats: {
            chunks: false
        },
        proxy: {
            '/api': {  //proxy指定/api开头的路径都代理到http://127.0.0.1:8000去。
                target: 'http://127.0.0.1:8000',
                changeOrigin: true,
                pathRewrite: {'^/api':''}
            }
        }
    }
};

4 vscode 配置

jsconfig.json 是vscode的配置文件,覆盖当前配置。

{
    "compilerOptions": {
        "target": "ES6",
        "module": "commonjs",
        "experimentalDecorators": true
    }
}

上述是所有相关配置文件解释,获取这个文件夹,需要修改name,version,description,需要修改repository仓库地址,需要修改author,license信息,这些信息修改完成后就可以开发了。

3 项目启动与基本操作

1 项目启动

在项目根目录使用npm start 启动devserver即可

npm start 
react项目基础
react项目基础

上述数字变化的是客户端一直在执行,其JavaScript脚本在本地执行修改DOM树,通过DOM树进行渲染,从而刷新数据

2 测试程序

修改/src/index.js中的代码如下

import React from 'react'; //导入主框架,必须要有
import ReactDom    from  'react-dom';//导入react-som,要进行dom树的操作

class Root  extends  React.Component{  //组件类定义,从React.Component上继承,这个类生成JSXElement对象及就是React元素
  render()  { //渲染函数,返回组件中渲染的内容,注意,此处只能返回唯一一个顶级元素
    return <div>hello world </div>
  }
}

ReactDom.render(<Root  />,document.getElementById('root')); //第一个参数是JSCElement对象,第二个是DOM的Element元素,将React元素添加到DOM的Eelment元素中并进行渲染。还可以使用React.createElement创建react元素,第一个参数是React组件或者一个HTML的标签名称如(div,span)

修改后代码如下

import React from 'react';
import ReactDom    from  'react-dom';

class Root  extends  React.Component{
  render()  {
    return <div>hello world </div>
  }
}

ReactDom.render(React.createElement(Root),document.getElementById('root'))

页面结果如下

react项目基础

3 添加子元素

import React from 'react';
import ReactDom    from  'react-dom';

class  Sub extends  React.Component{  // 定义子元素 
  render() {
    return  <div>Sub  class </div>
  }
}

class Root  extends  React.Component{
  render()  {
    return <div>
      hello world 
      <Sub />  {/*引用子元素*/}
      </div>
  }
}

ReactDom.render(React.createElement(Root),document.getElementById('root'))

注意 :
1 react 组件的render函数return,只能有一个顶级元素
2 JSX语法是XML,要求所有元素必须闭合,注意<br/>不能写成<br>


react项目基础

测试{}里面使用引号

import React from 'react';
import ReactDom    from  'react-dom';

class  Sub extends  React.Component{  // 定义子元素 
  render() {
    return  <div>{ 1>2?'flase':'true' }   {/*此处不是字符串是不行的*/}
            <hr />
    "{ 1>2?'flase':'true'}"{/*此处不是字符串是不行的  此处也是可以的 */}
            <hr  />
    "{ "1>2?'flase':'true'" }"</div>; {/*此处不是字符串是不行的  此处会导致存在冲突*/}
        }
      }

class Root  extends  React.Component{
  render()  {
    return <div>
      hello world 
      <Sub />  {/*引用子元素*/}
      </div>
  }
}

ReactDom.render(React.createElement(Root),document.getElementById('root'))
react项目基础

三 组件状态和组件间通信构造器和无状态组件

1 组件状态 state

每一个react组件都有一个状态变量state,他是一个JavaScript对象,可以为其定义属性来保存值。
如果状态state发生了变化,会触发UI的重新渲染
注意: state是组件自己内部使用的,是组件的私有属性

2 基本定义和操作

import React from 'react';
import ReactDom    from  'react-dom';

class  Sub extends  React.Component{  // 定义子元素 
  state = {  //此处必须是一个对象,此处变化会导致调用render后进行渲染,
    x: "test",
    y: ".com"
  }
  render() {
      return  <div>  {this.state.x + this.state.y} </div>  //通过this.stste.xxx来调用此属性
      }
    }

class Root  extends  React.Component{
  render()  {
    return <div>
      hello world 
      <Sub />  {/*引用子元素*/}
      </div>
  }
}

ReactDom.render(React.createElement(Root),document.getElementById('root'))
react项目基础

3 修改属性

1 直接修改

import React from 'react';
import ReactDom    from  'react-dom';

class  Sub extends  React.Component{  // 定义子元素 
  state = {  //此处必须是一个对象,此处变化会导致调用render后进行渲染,
    x: "test",
    y: ".com"
  }
  render() {
      this.state.x="test100" //修改属性
      return  <div>  {this.state.x + this.state.y} </div>  //通过this.stste.xxx来调用此属性
      }
    }

class Root  extends  React.Component{
  render()  {
    return <div>
      hello world 
      <Sub />  {/*引用子元素*/}
      </div>
  }
}

ReactDom.render(React.createElement(Root),document.getElementById('root'))
react项目基础

2 通过内部提供的setState()修改属性

import React from 'react';
import ReactDom    from  'react-dom';

class  Sub extends  React.Component{  // 定义子元素 
  state = {  //此处必须是一个对象,此处变化会导致调用render后进行渲染,
    x: "test",
    y: ".com"
  }
  test() {
    this.setState({x:"test100"})
  }
  render() {
      // this.setState({x:"test100"})  //此处不允许在渲染的过程中修改属性,
      // test();  通过外部定义,内部调用的方式修改也是不可以的,只能通过异步的方式进行修改,如下 
      setTimeout(()=>this.setState({x:"test100"}),5000)  //定义一个箭头函数,输入为空,输出为setState的值,其等待时间为5S进行刷新修改,此处是异步修改
      return  <div>  {this.state.x + this.state.y} </div>  //通过this.stste.xxx来调用此属性
      }
    }

class Root  extends  React.Component{
  render()  {
    return <div>
      hello world 
      <Sub />  {/*引用子元素*/}
      </div>
  }
}

ReactDom.render(React.createElement(Root),document.getElementById('root'))
react项目基础

5s后结果为:

react项目基础

4 触发事件基本配置

在项目根目录中index.html中书写如下代码,暂时注释之前代码

<html>
    <head>
        <script type="text/javascript">
            function  getEventTrigger(event) {
                x=event.target;  //从事件中获取元素 
                alert("触发的元素的id是:"+x.id) //输出弹框,并获取id进行组合
            }
        </script>
    </head>   
    <body>
        <h1>测试程序</h1>   
        <div id="test"  onmousedown="getEventTrigger(event)"> {/*此处定义鼠标按下,用于触发事件,并执行对应函数返回对应结果*/}
            点击触发事件,弹出警示框
        </div>    
    </body>   
</html>   
react项目基础

点击下面的,同一行的都会有影响

react项目基础
react项目基础

在后面的index.js中进行相关配置如下

import React from 'react';
import ReactDom    from  'react-dom';

class  Sub extends  React.Component{  // 定义子元素 
  handleClick(event) {
    let  key =event.target; //获取传入参数
    alert("触发的元素的id是:"+key.id)   //抛出相关异常
  }

  render() {
      return  <div  id="test"  onClick={this.handleClick.bind(this)} >{/*此处定义一个按鼠标事件,执行这个事件后,调用指定的函数完成对应操作*/}
      点击触发事件,弹出警示框
      </div>  //通过this.stste.xxx来调用此属性
      }
    }
class Root  extends  React.Component{
  render()  {
    return <div>
      hello world 
      <Sub />  {/*引用子元素*/}
      </div>
  }
}
ReactDom.render(React.createElement(Root),document.getElementById('root'))
react项目基础
react项目基础

添加state用于触发事件

import React from 'react';
import ReactDom    from  'react-dom';
import { runInThisContext } from 'vm';

class  Sub extends  React.Component{  // 定义子元素 
  state  = {
    flag:true
  }
  handleClick(event) {
    let  key =event.target; //获取传入参数
    this.setState({flag:!(this.state.flag)}) //当触发发生时,修改此处的属性用于下面的显示操作
  }

  render() {
      let  text=(this.state.flag)?"true":"false"  //此处使用三目运算符用于实现相关功能,当为true时,返回true,当this.state.flag为false时返回false
      return  <div  id="test"  onClick={this.handleClick.bind(this)} >{/*此处定义按按鼠标的一个事件,执行这个事件后,调用指定的函数完成对应操作*/}
      <h1>flag={text}</h1>
      </div>  
      }
    }
class Root  extends  React.Component{
  render()  {
    return <div>
      hello world 
      <Sub />  {/*引用子元素*/}
      </div>
  }
}
ReactDom.render(React.createElement(Root),document.getElementById('root'))
react项目基础

鼠标点击true和false 转换


分析
Sub 类,它有自己的state属性。当render完成后,网页上会有一个div标签,div标签对象捆绑了一个click事件处理函数,div标签内有文本内容
如果通过剪辑左键,就触发了click方法关联的handleClick函数,在这个函数中对状态值进行修改操作。
状态值state的改变将引发render的重绘。
如果组件自己的state改变了,只会触发自己的render方法重绘,此处是state是私有属性,


注意
{this.handleClick.bind(this)} ,不能外加括号
this.handleClick.bind(this)要绑定this,否则当触发捆绑的函数时,this是函数执行的上下文决定的,this已经不是触发事件的对象了,有可能是全局对象。因此需要将this传递进去使得handleClick能够接受正确的参数。

2 props 组件之间通信和构造器

1 增加属性的三种方式(props是只读的,在该组件内部不能修改)

1 在标签本级标签的引用中加入组件的属性,为这个组件提供外部属性name="test",这个属性会作为一个单一的对象传递给组件,加入到组件的props属性中。


2 在外部进行添加 parent={this} ,注意这个this是在Root元素中,指的是Root组件本身,可以在子组件中调用各种方法完成相应的各种操作。


3 在Root中为使用JSX语法为Sub增加子元素,这些子元素也会被加入到Sub组件的props.children中

2 本级添加方式

import React from 'react';
import ReactDom    from  'react-dom';

class  Sub extends  React.Component{  // 定义子元素 
  state  = {
    flag:true
  }
  handleClick(event) {
    let  key =event.target; //获取传入参数
    this.setState({flag:!(this.state.flag)}) //当触发发生时,修改此处的属性用于下面的显示操作
  }

  render() {
      let  text=(this.state.flag)?"true":"false"  //此处使用三目运算符用于实现相关功能,当为true时,返回true,当this.state.flag为false时返回false
      return  <div  id="test"  onClick={this.handleClick.bind(this)} >{/*此处定义按按鼠标的一个事件,执行这个事件后,调用指定的函数完成对应操作*/}
      <h1>  flag: {text}  name: {this.props.name}</h1>
      </div>  
      }
    }
class Root  extends  React.Component{
  render()  {
    return <div>
      hello world 
      <Sub name="test"/>  {/*通过在此标签的调用处传入相关参数*/}
      </div>
  }
}
ReactDom.render(React.createElement(Root),document.getElementById('root'))
react项目基础

3 通过添加this来调用当前环境的标签,及符标签的相关情况

import React from 'react';
import ReactDom    from  'react-dom';

class  Sub extends  React.Component{  // 定义子元素 
  state  = {
    flag:true
  }
  handleClick(event) {
    let  key =event.target; //获取传入参数
    this.setState({flag:!(this.state.flag)}) //当触发发生时,修改此处的属性用于下面的显示操作
  }

  render() {
      let  text=(this.state.flag)?"true":"false"  //此处使用三目运算符用于实现相关功能,当为true时,返回true,当this.state.flag为false时返回false
      return  <div  id="test"  onClick={this.handleClick.bind(this)} >{/*此处定义按按鼠标的一个事件,执行这个事件后,调用指定的函数完成对应操作*/}

      <h1>  flag: {text}  name: {this.props.name}  {console.log('parent++++++++++',this.props.parent)}  </h1>  {/*通过this.props.parent可以对ROOT中的各种属性和方法进行相关的调用操作,如通过this.props.parent.setState()来修改Root的相关组件状态等操作*/}
      </div>  
      }
    }
class Root  extends  React.Component{
  render()  {
    return <div>
      hello world 
      <Sub name="test"  parent={this}/>  {/*通过在此标签的调用处传入相关参数,此处的this是Root,此处在谁的区域内此this就是谁
      此处是将Root的this指针传递进来*/}
      </div>
  }
}
ReactDom.render(<Root />,document.getElementById('root'));
react项目基础

4 调用子标签的children来实现相关处理

import React from 'react';
import ReactDom    from  'react-dom';

class  Sub extends  React.Component{  // 定义子元素 
  state  = {
    flag:true
  }
  handleClick(event) {
    let  key =event.target; //获取传入参数
    this.setState({flag:!(this.state.flag)}) //当触发发生时,修改此处的属性用于下面的显示操作
  }

  render() {
      let  text=(this.state.flag)?"true":"false"  //此处使用三目运算符用于实现相关功能,当为true时,返回true,当this.state.flag为false时返回false
      return  <div  id="test"  onClick={this.handleClick.bind(this)} >{/*此处定义按按鼠标的一个事件,执行这个事件后,调用指定的函数完成对应操作*/}

      <h1>  flag: {text}  name: {this.props.name} </h1>
      <hr />
         <h1>sub:{this.props.children}</h1>
      </div>  
      }
    }
class Root  extends  React.Component{
  render()  {
    return <div>
      hello world 
      <Sub name="test"  parent={this}>
       {/*通过在此标签的调用处传入相关参数,此处的this是Root,此处在谁的区域内此this就是谁
      此处是将Root的this指针传递进来*/}
      <div>我是Sub的子标签</div>
      </Sub>
      </div>
  }
}
ReactDom.render(<Root />,document.getElementById('root'));
react项目基础

5 修改props

import React from 'react';
import ReactDom    from  'react-dom';

class  Sub extends  React.Component{  // 定义子元素 
  state  = {
    flag:true
  }
  handleClick(event) {
    let  key =event.target; //获取传入参数
    this.setState({flag:!(this.state.flag)}) //当触发发生时,修改此处的属性用于下面的显示操作
  }

  render() {
      let  text=(this.state.flag)?"true":"false" 
      return  <div  id="test"  onClick={this.handleClick.bind(this)} >

      <h1>  flag: {text}  name: {this.props.name} </h1>
      <hr />
         <h1>sub:{this.props.children}</h1>
         {this.props.parent={}}  {/*此处修改属性,会导致报错,因为其是只读的*/}
      </div>  
      }
    }
class Root  extends  React.Component{
  render()  {
    return <div>
      hello world 
      <Sub name="test"  parent={this}/>
      </div>
  }
}
ReactDom.render(<Root />,document.getElementById('root'));
react项目基础

此处提示是只读属性,不能被修改

对外部的修改管不着。如 this.props.parent.setState({x:y}) 可以修改,此处和python中的元祖相同,若元祖中定义了列表,则虽然元祖是不可变类型,但其中的元素是可变的,仍然是可以修改的。

使用ES6的构造器,要提供一个参数props,并把这个参数使用super传递给父类

import React from 'react';
import ReactDom    from  'react-dom';

class  Sub extends  React.Component{  // 定义子元素 
  constructor(props) {
    console.log('props......',props)
    super(props);//传递到父类。其默认在构造中加入了props属性
    this.state={flag:true} //定义flag
  }
  handleClick(event) {
    let  key =event.target; //获取传入参数
    this.setState({flag:!(this.state.flag)}) //当触发发生时,修改此处的属性用于下面的显示操作
  }

  render() {
      let  text=(this.state.flag)?"true":"false"  //此处使用三目运算符用于实现相关功能,当为true时,返回true,当this.state.flag为false时返回false
      return  <div  id="test"  onClick={this.handleClick.bind(this)} >{/*此处定义按按鼠标的一个事件,执行这个事件后,调用指定的函数完成对应操作*/}
      <h1>  flag: {text}  </h1>
      </div>  
      }
    }
class Root  extends  React.Component{
  render()  {
    return <div>
      hello world 
      <Sub name="test"  parent={this}/>
      </div>
  }
}
ReactDom.render(<Root />,document.getElementById('root'));
react项目基础

4 无状态组件定义基本函数

react从 15.0开始支持无状态组件,定义如下

import ReactDOM from 'react-dom';
import React from 'react';
function   Root(props)  {
    return  <div>{props.name}</div>
}
ReactDOM.render(<Root name="test" />,document.getElementById('root'));  
react项目基础

修改代码如下

import ReactDOM from 'react-dom';
import React from 'react';
let Root = props   =>  <div>{props.name}</div>
ReactDOM.render(<Root name="test" />,document.getElementById('root'));  

结果和上面相同

state 是私有属性,组件外部无法直接访问,可以修改state,但建议使用setState方法。

props是公有public属性,组件外也可以访问,但是是只读属性

四 组件的生命周期

1 组件的三个状态

组件的声明周期可分为三个状态
1 Mounting:已插入真实DOM
2 Updating:正在被重新渲染
3 Unmounting:已移出真实DOM

2 组件的生命周期方法

1 装载组件触发

1 componentWillMount:在渲染之前调用,在客户端也在服务端,只会在装载之前调用一次


2 componentDidMount:在第一次渲染后调用,只在客户端,之后组件已经生成了对应的DOM结构,可以通过this.getDOMNode()来进行访问,如果你想和其他JavaScript框架一起使用,可以在这个方法中调用setTimeout,setInterval或者发送AJAX请求等操作(放置异步调用阻塞UI),只在装载完成后调用一次,在render之后。

2 更新组件触发

这些组件不会在首次render组件的周期调用
1 componentWillReceiveProps(nextProps)在组件接收到一个新的props时被调用,这个方法在初始化render时不会被调用。如上述的通过外部传入name的情况会调用此中方式


2 shouldComponentUpdate(nextProps,nextState)返回一个布尔值,在组件接收到新的props或者state时被调用,在初始化时或者使用forceUpdate时不会被调用
作用:
1 可以在你确认不需要更新组件时使用
2 如果设置为false,就是不允许更新组件操作。那么componentWillUpdate,componentWillUpdate不会执行


3 componentWillUpdate(nextProps,nextState)在组件接收到新的props或者state但还是没有render时被调用,在初始化时不会被调用


4 componentDidUpdate(prevProps,prevState)在组件完成更新后立即调用,在初始化时不会被调用。

3 卸载组件触发

1 componentWillUnmount 在组件从DOM中移除的时候立即被调用

3 生命周期图

react项目基础

上图可知,其construtor构造器是最早执行的函数
触发更新生命周期函数,需要通过更新state和props,

相关代码如下

import React from 'react';
import ReactDOM from 'react-dom';
class Sub  extends  React.Component{
  constructor(props)  {
    super(props) 
      console.log('sub Constructor')
      super(props);
      this.state={count: 0};  //定义变量
    }
  handleClick(event) {
    this.setState({count:this.state.count+1});
  }
  render(){
    console.log('Sub render');
    return  (<div  id='sub' onClick={this.handleClick.bind(this)}> sub's count={this.state.count} </div>)
  }
  componentDidMount(){
        //第一次render之后
    console.log('Sub  componentDidMount')
  }
  componentWillMount(){
     //constructoir之后,第一次调用render
    console.log('Sub  componentWillMount')
  }
  componentWillUnmount(){
    //清理工作
    console.log('Sub  componentWillUnmount')
  }
}
class  Root extends  React.Component  {
  constructor(props) {
    console.log('Root  Constructor')
    super(props); //调用父类对象
    this.state={};  // 定义一个对象
  }
  render(){
    return  (<div>hello world
      <hr />
      <Sub />
    </div>);
  }
}
ReactDOM.render(<Root/>,document.getElementById('root'));
react项目基础

处理过程:父构造---子构造-- componentWillMount ---render----componentDidMount

import React from 'react';
import ReactDOM from 'react-dom';
class Sub  extends  React.Component{
  constructor(props)  {
    super(props) 
      console.log('sub Constructor')
      super(props);
      this.state={count: 0};  //定义变量
    }
  handleClick(event) {
    this.setState({count:this.state.count+1});
  }
  render(){
    console.log('Sub render');
    return  (<div  id='sub' onClick={this.handleClick.bind(this)}> 
    sub's count={this.state.count} </div>)
  }
  componentDidMount(){
    //constructoir之后,第一次调用render
    console.log('Sub  componentDidMount')
  }
  componentWillMount(){
    //第一次render之后
    console.log('Sub  componentWillMount')
  }
  componentWillUnmount(){
    //清理工作
    console.log('Sub  componentWillUnmount')
  }
  componentWillReceiveProps(nextProps)  {
    //props变更时,接到新的props了,交给shouldcompupdate
    //props 组件内只读。只能从外部修改
    console.log(this.props);
    console.log(nextProps);
    console.log('this componentWillReceiveProps',this.state.count)
  }
  shouldComponentUpdate(nextProps,nextState) {
    console.log('Sub  shouldComponentUpdate',this.state.count,nextState);
    return true; //return 为false将拦截更新
  }
  componentWillUpdate(nextProps,nextState){
    // 同意更新后,真正更新前,执行的动作 
    console.log('Sub  componentWillUpdate',this.state.count,nextState);
  }
  componentDidUpdate(prevProps,prevState){
    //同意更新后,真正更新后调用
    console.log('Sub  componentDidUpdate',this.state.count,prevState);
  }

}

class  Root extends  React.Component  {
  constructor(props) {
    console.log('Root  Constructor')
    super(props); //调用父类对象
    this.state={flag:true,name:'root'};  // 定义一个对象
  }
  handleClick(event)  {
    this.setState(
      {flag: !this.state.flag,
      name:this.state.flag?this.state.name.toLowerCase():this.state.name.toUpperCase() // 切换名字大小写
      }
    );
  }
  
  render(){
    return  (<div id='root'  onClick={this.handleClick.bind(this)}>
      my  name is  {this.state.name}<hr />
      <Sub />  {/*父组件的render,会引起下一级组件的更新流程,导致props重新发送,即使子组件props没有改变过*/}
    </div>);
  }
}
ReactDOM.render(<Root/>,document.getElementById('root'));

结果如下,需要点击鼠标。下面的显示和上面的都会动,子组件是镶嵌在父组件中的,因此其会整体变化。子元素在父元素的区域内的。

react项目基础

相关区域如下

父元素响应区域

react项目基础

子元素响应区域

react项目基础

调用render函数的情况下也没刷新,因为发现虚拟DOM和DOM之间是没有差异的,因此其子区域不需要重绘


点击父元素,子元素会重新渲染,会受到影响,因为子元素的props会被重置,其是从父元素传递过去的,其会重新走一下子元素走的路。

react项目基础

componentWillMount 第一次装载,在首次render之前,如控制state,props
componentDidMount 第一次装载接受,首次render之后,例如控制state,props

componentWillReceiveProps 在组件内部,props是只读不可变的,但是这个函数可以接受到新的props,可以对props做一些处理,如this.props={name:'224523542w43'}

componentWillReceiveProps 触发,也会走shouldComponentUpdate

shouldComponentUpdate判断是否需要组件更新,就是是否render,精确的控制,提高性能

componentWillUpdate在首次render外,每次render前执行,componentWillUpdate在render之后调用。

不过,大多数情况向,用不上这些函数,这些钩子函数是为了精确控制使用。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK