React

React项目

目录

1、简介
2、项目根目录结构配置文件解析
3、react使用

  1. 简介
  2. 组件状态
  3. 属性
  4. 组件生命周期
  5. 无状态组件
  6. 高阶组件

简介

一些工具的组合。
一个纯粹的index.js 在网页上是可以直接运行的,webserver的本质。
因为react多用Mixin等,我们需要搭建开发环境,将这些代码转译。只要你做代码修改,保存后会自动重新转译,转译完之后立即动态刷新。
项目的根目录很重要,很多都是从项目的根目录开始的。

项目根目录结构

.(这里有个点)
|----.babelrc babel的预设值
|----.gitignore 忽略文件
|----index.html 项目的根目录
|----jsconfig.json VScode相关文件,一些项目相关的配置可以放在这里
|----LICENSE
|----.npmrc
|----package.json
|----package-lock.json
|----README.md
|----src/ 项目里的js文件
| |----app.js
| |----AppState.js
| |----index.html
| |----index.js
|----node_modules/ 所依赖的模块
| |----…
|----webpack.config.dev.js
|----webpack.config.prod.js

配置文件

首先我们先来了解一下每个配置文件
pacjage.json
npm init产生的文件,里面记录项目信息,所有项目依赖。

版本管理
“repository”:{“type”:“git”,
“url”:“https://192.168.124.135/react-mobx/react-mobx-starter.git”}

项目管理
“scripts”:{
“test”:“jest”,
“start”:“webpack-dev-server”,
“build”:“webpack -p --config webpack.config.prod.js”
}
start指定启动webpack的dev server开发用WEB Server,主要提供两个功能:静态文件支持、自动刷新和热替换HMR。HMR可以在应用程序运行中替换、添加、删除模块,无需重新加载页面,只需要把变化的部分替换掉。不使用HMR则会自动刷新。 --hot启动HMR --inline 默认模式,使用HMR的时候建议开启,热替换时会有消息显示在控制台。
build 使用webpack构建打包,对应$ npm run build,将会按照 webpack -p --config webpack.config.prod.js 对路径里的文件进行打包,然后根目录下会多出一个分发目录,目录含有的文件如下:
在这里插入图片描述
我们看看index文件




    
    Title


// id最好不要重复,作为唯一id,JS通过document.getElementByID('root')来找到这个,再进行操作 // 直接在浏览器打开是没有任何显示,因为
点击这句话,会触发一个时间,并弹出一个警示框
>

在这个例子中,div的id是t1,鼠标按下事件绑定了一个函数,只要鼠标按下就会触发调用getEventTrigger函数,浏览器会给它一个参数event,当事件触发时,event包含触发这个事件的对象,可以找到这个对象的元素并获取属性,然后将属性输出(在这里属性时id = ‘t1’)。这里是传统写法,如何变成前端框架来实现呢?

HTML DOM 的JavaScript事件

名称 此事件发生在何时
onabort 图像的加载被中断
onblur 元素失去焦点
onchange 域的内容被改变
onclick 当用户点击某个对象时调用的事件句柄
ondblclick 当用户双击某个对象时调用的事件句柄
onerror 在加载文档或图像时发生错误
onfocus 元素获得焦点
onkeydown 某个键盘按键被按下
onkeypress 某个键盘按键被按下并松开
onkeyup 某个键盘按键被松开
onload 一张页面或图像完成加载
onmousedown 鼠标按钮被按下
onmousemove 鼠标被移动
onmouseout 鼠标从某元素移开
onmouseover 鼠标移动到某元素上
onmouseup 鼠标被松开
onreset 重置按钮被点击
onresize 窗口或框架被重新调整大小
onselect 文本被选中
onsubmit 确认按钮被点击
onunload 用户退出页面

onload一般用在body上,dom树一点一点形成,其中AMD的异步模块加载要有一个回调,因此会用到onload。
onchange域,注册的时候,写入用户名时会有该用户名已经使用(异步调用模式,并返回调用结果一个json)。在JSX中要注意小驼峰。on后面首字母要大写。

阻止默认行为,event.preventDefault()调用一下即可阻止例如,点击链接弹窗,试验一下即可。
使用react实现上面的传统的HTML

import React from 'react';
import ReactDOM from 'react-dom';
class Toggle extends React.Component{
  state = {flag:true};
  handleClick(event){
    console.log(event.target.id);
    console.log(event.target === this);
    console.log(this);
    console.log(this.state);
    this.setState({flag:!this.state.flag});
  }
  render(){//注意绑定this,onClick一定要写成小驼峰
    return 
点我触发事件。{this.state.flag.toString()}
; } } class Root extends React.Component { state = {p1:'p1 is here',p2:'p2 is here'}; render(){ setTimeout(()=> this.setState({p1:'exchange p1'}),3000); return (
your happy for {this.state.p1}, {this.state.p2}

); } } ReactDOM.render(,document.getElementById("root"));

网页输出:
在这里插入图片描述
分析
Toggle类
有属性state,当render完成后,网页上有一个div标签,div标签对象捆绑了一个click事件的处理函数,div标签内有文本内容。如果通过点击左键,就触发了click方法关联的handleClick函数,在这个函数里将状态值改变。状态值state的改变将引发render重绘。如果组件自己的state改变了,只会触发自己的render方法重绘。

注意{this.handleClick.bind(this)}不能外加引号,一定要绑定this,否则触发捆绑的函数时,this是函数执行的上下文决定的,this已经不是触发事件的对象了。console.log(event.target.id);取回的产生事件的对象的id,但是这不是我们封装的组件对象。 console.log(event.target === this);是false。所以这里一定要用this,而这个this是通过绑定来的。

react中的事件
使用小驼峰命名
使用JSX表达式,表达式中指定事件处理函数
不能使用return false,如果要阻止事件默认行为,使用event.preventDefault()

属性props

我们将React组件(例如Toggle)当做标签使用,为其增加属性,如:
,尝试增加属性:
test = ‘think god’ 这个属性作为一个单一的对象传递给组件,加入到组件的props属性中。
parent = {this} 可以传对象在其中,这个this是在Root元素中,是Root组件本身。
在Root中使用JSX语法为Toggle增加子元素,这些子元素也会被加入到Toggle组件的props.children中。看栗子:

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


class Toggle extends React.Component{
  state = {flag:true};
  handleClick(event){
    console.log(event.target.id);
    console.log(event.target === this);
    console.log(this);
    console.log(this.state);
    this.setState({flag:!this.state.flag});
  }
  render(){//注意绑定this,onClick一定要写成小驼峰
    return 
点我触发事件。{this.state.flag.toString()}
{this.props.test}
{this.props.children} // 不能修改props的属性值,类似于变成只读 {this.props.parent.state.p1} //在组件内调用props所含有的属性
; } } class Root extends React.Component { state = {p1:'p1 is here',p2:'p2 is here'}; render(){ setTimeout(()=> this.setState({p1:'exchange p1'}),3000); return (
your happy for {this.state.p1}, {this.state.p2}

// 将属性通过props添加到Toggle组件中,其中parent为类 baidu I am the Toggle children
); } } ReactDOM.render(,document.getElementById("root"));

输出为
React_第6张图片
但是不能修改props的属性,因为是公有属性,组件外可访问,只读。和state不同,state是组件私有属性,组件外无法访问,可以修改。

构造器constructor

使用ES6的构造器,要提供一个参数props,并把这个参数使用super传递给父类。就是this 的props。
例如:

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


class Toggle extends React.Component{
  constructor(props){
    super(props)
    this.state = {flag:true};
  }
  handleClick(event){
    console.log(event.target.id);
    console.log(event.target === this);
    console.log(this);
    console.log(this.state);
    this.setState({flag:!this.state.flag});
  }
  render(){//注意绑定this,onClick一定要写成小驼峰
    return 
点我触发事件。{this.state.flag.toString()}
{this.props.test}
{this.props.children} {this.props.parent.state.p1}
; } } class Root extends React.Component { constructor(props){ super(props); this.state = {p1:'p1 is here',p2:'p2 is here'}; } // state = {p1:'p1 is here',p2:'p2 is here'}; render(){ setTimeout(()=> this.setState({p1:'exchange p1'}),3000); return (
your happy for {this.state.p1}, {this.state.p2}

baidu I am the Toggle children
); } } ReactDOM.render(,document.getElementById("root"));

输出同上。

组件的生命周期

组件的生命周期分成三个状态:
Mounting:已插入真实DOM
Updating:正在被重新渲染
Unmounting:已移出真实DOM
组件的生命周期状态,说明在不同时机访问,组件处于生命周期的不同状态上。在不同的生命周期访问组件,就有不同的方法
如下:

  • 装载组件触发

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

    • 有一次render的调用

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

  • 更新组件触发,这些方法不会再首次render组件的周期调用

    • componentWillReceiveProps(nextProps)在组件接受到一个新的prop时被调用。这个方法在初始化render时不会被调用.

    • shouldComponentUpdate(nextProps,nextState)返回一个bool值(如果返回false,则更新流程到此为止,不会到达render,每次渲染都会经过它)。在组件接受到新的props或者state时被调用,在初始化时或者使用forceUpdate时不被调用。

      • 可以在你确认不需要更新组件时使用。
      • 如果设置为false,就是不允许组件更新,那么componentWillUpdate,ComponentDidUpdate不会执行。
    • componentWillUpdate(nextProps,nextState)在组件接受到新的props或者state但还没有render时被调用,在初始化时不会被调用。

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

  • 卸载组件触发

    • componentWillUnmount在组件从DOM中移除的时候立刻被调用。

流程图如下:
React_第7张图片
constructor构造器是最早执行的函数。
触发 更新生命周期函数,需要新state或者props。
因此重新写src/index.js
构造两个组件,在子组件Sub中,加入所有生命周期。
看栗子:

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

class Sub extends React.Component{
  constructor(props){
    // console.log(...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 (
Sub's count ={this.state.count}
); } componentWillMount(){ console.log('Sub componentWillMount'); } componentDidMount(){ console.log('Sub componentDidMount'); } componentWillUnmount(){ console.log('componentWillUnmount') } } class Root extends React.Component{ constructor(props){ // console.log(...props); console.log('Root Constructor'); super(props); this.state = {}; } render(){ return (
) } } ReactDOM.render(,document.getElementById("root"));

运行输出
React_第8张图片
从这里可见这些周期函数的调用顺序。每次点击,造成的state,或者props改变,都会调用一次render。其中props是空的{}。
这里再增加一些元素,以更加清晰的方式看这个栗子;

class Sub extends React.Component{
  constructor(props){
    console.log(1 ,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 ();
  }
  componentWillMount(){
    console.log('Sub componentWillMount');
  }
  componentDidMount(){
    console.log('Sub  componentDidMount');
  }
  componentWillUnmount(){
    console.log('componentWillUnmount')
  }
  componentWillReceiveProps(nextprops){
    console.log(this.props);
    console.log(nextprops);
    console.log('Sub componentWillReceiveProps',this.state.count);
  }
  shouldComponentUpdate(nextProps,nextState){
    console.log('Sub shouldComponentUpdate', this.state.count, nextState);
    return true;
  }
  
  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(2,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 (
      
name is {this.state.name}
) } }

运行结果如下
React_第9张图片
React_第10张图片
componentWillMount第一次装载,再首次render之前;
componentDidMount 第一次装载结束,再首次render之后;

componentWillReceiveProps 在组件内部,props是只读不可变的,但是这个函数可以接收新的props,可以对props进行处理,如果加入this.props={name:‘customer’},就可以偷偷更换props,触发后也要进入shouldComponentUpdate.

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

componentWillUpdate在除了首次render之外,每次render前执行,componentDidUpdate在render之后调用。
这些函数只是为了精确控制。

但是这样编写代码过于复杂,如果可以用上箭头函数,装饰器等就更好了,下面说一说高阶技术。

无状态组件

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

function Root(props){
  return 
{props.name}
} ReactDOM.render(,document.getElementById("root"));

当然可以使用箭头函数
let Root = (props) =>

{props.name}

ReactDOM.render(,document.getElementById(“root”));

就是将函数作为组件,不需要state状态,生命周期函数,只需要一个props然后返回React元素即可。

高阶组件

上面的无状态组件如果要进行增强,就要用到类似于装饰器,将Root组件的div外部再加一层div。
先做一个加强函数,看栗子,科里化,加箭头函数的变化:

let Wrapper = Component=>props =>
(
{props.name}
); let Root = props=>
{props.name}
; let NewRoot = Wrapper(Root) ReactDOM.render(,document.getElementById("root")); // 注意命名。开头大写

再加装饰器:

let Wrapper = Component=>props =>
(
{props.setName}
//在传进来的Component组件中加入属性,使得root可以使用props
); @Wrapper class Root extends React.Component{ render(){ return
{this.props.setName}
; } } ReactDOM.render(,document.getElementById("root"));

带参装饰器
就是多几个箭头函数的事。例如

let Wrapper =id => Component=>props =>
(
{props.setName}
); @Wrapper('this id') class Root extends React.Component{ render(){ return
{this.props.setName, this.props.ids}
; } } ReactDOM.render(,document.getElementById("root"));

可以将参数传到各个组件中。这样更像面向对象语言。

你可能感兴趣的:(react)