react 学习笔记

176 阅读13分钟

react

React是Facebook开发的一款JS库,React是一个适合数据经常变化以及构建大型项目的复杂组件;

核心思想:封装组件

基本包括:组件、JSX、Virtual Dom、Data Flow

组件

应用构建在组件之上

组件名称必须大写

两个核心概念L:props、state。组件通过这两个属性的值在reader方法里面生成这个组件对应的html

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

const element = <Welcome name="Sara" />;
ReactDOM.render(
  element,
  document.getElementById('root')
);

这个属性名称必须要和函数组件里面的参数名称一致,否则会报错

例如:name

props

组建的属性,具有只读性

注:组件生成的html结构只能有一个单一的根节点

React 组件都必须保护props不被改变,也就是说不能改变他自己传入参数

  • 作用:给组件传递数据,一般用在父子组件之间
  • 说明:React把传递给组件的属性转化为一个对象并交给 props
  • 特点:props是只读的,无法给props添加或修改属性
  • props.children:获取组件的内容,

props可以传递函数,进而修改父元素

state

状态即数据

  • 作用:用来给组件提供组件内部使用的数据
  • 注意:只有通过class创建的组件才具有状态
  • 注意:状态是私有的,完全由组件来控制
  • 注意:不要在 state 中添加 render() 方法中不需要的数据,会影响渲染性能!
    • 可以将组件内部使用但是不渲染在视图中的内容,直接添加给 this
  • 注意:不要在 render() 方法中调用 setState() 方法来修改state的值
    • 但是可以通过 this.state.name = 'rose' 方式设置state(不推荐!!!!)

利用es6的class的继承

class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }

  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

ReactDOM.render(
  <Clock />,
  document.getElementById('root')
);

组件的当前状态(数据),一旦更改,组件就会自动调用重新渲染ui,这个更改的动作会通过this.setState方法来触发

this.setState属于异步加载

 // 方式一:
  this.setState({
    count: this.state.count + 1
  })

  this.setState({
    count: this.state.count + 1
  }, function(){
    // 由于 setState() 是异步操作,所以,如果想立即获取修改后的state
    // 需要在回调函数中获取
    // https://doc.react-china.org/docs/react-component.html#setstate
  });

  // 方式二:
  this.setState(function(prevState, props) {
    return {
      counter: prevState.counter + props.increment
    }
  })

  // 或者 - 注意: => 后面需要带有小括号,因为返回的是一个对象
  this.setState((prevState, props) => ({
    counter: prevState.counter + props.increment
  }))

生命周期方法可以加到Class中

componentDidMount()组件已经渲染到Dom中后运行

componentWillUnmout()组件卸载之前

划分状态组件

让组件尽可能的少状态

  • 1 通过 JS函数 创建(无状态组件)
  • 2 通过 class 创建(有状态组件)
函数式组件 和 class 组件的使用场景说明:
1 如果一个组件仅仅是为了展示数据,那么此时就可以使用 函数组件
2 如果一个组件中有一定业务逻辑,需要操作数据,那么就需要使用 class 创建组件,因为,此时需要使用 state

一下可认为不是状态:

  • 可计算的数据:比如一个数组的长度
  • 和 props 重复的数据:除非这个数据是要做变更的

无状态组件

js函数创建

  • 注意:1 函数名称必须为大写字母开头,React通过这个特点来判断是不是一个组件
  • 注意:2 函数必须有返回值,返回值可以是:JSX对象或null
  • 注意:3 返回的JSX,必须有且仅有一个根元素
  • 注意:4 组件的返回值使用()包裹,避免换行问题

用纯粹的函数来定义无状态的组件(stateless function),这种组件没有状态,没有生命周期,只是简单的接受 props 渲染生成 DOM 结构。无状态组件非常简单,开销很低,如果可能的话尽量使用无状态组件。比如使用箭头函数定义:


function Welcome(props) {
  return (
    // 此处注释的写法 
    <div className="shopping-list">
      {/* 此处 注释的写法 必须要{}包裹 */}
      <h1>Shopping List for {props.name}</h1>
      <ul>
        <li>Instagram</li>
        <li>WhatsApp</li>
      </ul>
    </div>
  )
}

ReactDOM.render(
  <Welcome name="jack" />,
  document.getElementById('app')
)

阻止组件渲染

在组件中返回null,但是不会影响组建的生命周期,生命周期函数会执行

受控组件

在html中,表单元素可以自己维护state,并根据用户输入进行更新,在React中可变状态通常保存在state属性中,并且只能通过setState()来更新。

我们可以把两者结合起来,使 React 的 state 成为“唯一数据源”。渲染表单的 React 组件还控制着用户输入过程中表单发生的操作。被 React 以这种方式控制取值的表单输入元素就叫做“受控组件”。

输入的值始终由React的state驱动

组件的事件绑定

  • 1 通过React事件机制 onClick 绑定
  • 2 JS原生方式绑定(通过 ref 获取元素)
// 自定义方法:
handleBtnClick(arg1, arg2) {
  this.setState({
    msg: '点击事件修改state的值' + arg1 + arg2
  })
}

render() {
  return (
    <div>
      <button onClick={
        // 无参数
        // this.handleBtnClick.bind(this)

        // 有参数
        this.handleBtnClick.bind(this, 'abc', [1, 2])
      }>事件中this的处理</button>
      <h1>{this.state.msg}</h1>
    </div>
  )
}
constructor() {
  super()

  this.handleBtnClick = this.handleBtnClick.bind(this)
}

// render() 方法中:
<button onClick={ this.handleBtnClick }>事件中this的处理</button>
<input type="button" value="在构造函数中绑定this并传参" onClick={
  () => { this.handleBtnClick('参数1', '参数2') }
} />

handleBtnClick(arg1, arg2) {
  this.setState({
    msg: '在构造函数中绑定this并传参' + arg1 + arg2
  });
}

JSX

可以将html直接嵌入在js中

jsx表示对象,bable会吧jsx成为一个名为React.createElement()的函数调用

React.createElement(参1,参2,参3)

参1:标签名;

参2:属性对象;

参3:子元素

var child = React.createElement('li', null, 'Text Content');
等价于
var child=(
<li>Text Content</li>
)


var root = React.createElement('ul', { className: 'my-list' }, child);
React.render(root, document.body);

工厂化

var root = React.DOM.ul({ className: 'my-list' },
             React.DOM.li(null, 'Text Content')
           );

注:小写的字符串是html标签,大写开头的是变量是React组件

人、React DOM 使用小驼峰的命名方式

jsx里面class用className代替,

在 JSX 中注释语法:{/* 中间是注释的内容 */}

Virtual Dom

元素

是构成React应用的最小砖块,与浏览器的DOM不同,React元素创建开销极小的普通对象。React DOM 会负责更新DOM来与React保持一致

元素不可改变,一旦被创建,就无法修改子元素或者属性

React只会更新必要的,也就是局部更新

当组件的state有更改,会自动调用render方法重新渲染整个组建的ui。面积操作DOM,有性能问题,所以在 Virtual DOM 上实现了一个 diff 算法 ,上不是真的渲染整个 DOM 树。这个 Virtual DOM 是一个纯粹的 JS 数据结构,所以性能会比原生 DOM 快很多。

Data Flow

"单项数据绑定",是React推荐的一种应用架构方式。

事件处理

事件对象:返回的事件对象是代理的原生对象,如果查看事件对象的具体值,必须要 输出事件对象的属性

事件的名称采用小驼峰,

<button onClick={activateLasers}>  Activate Lasers
</button>

一般不用 addEventListener可以在创建元素的时候添加事件

在constructor改变事件的this 指向是必须,否则this的值为undefined

 class Clocks extends React.Component {
        constructor(props) {
            super(props);
            this.state = {
                flags: true
            }
            // 这个this是必须的
            this.clickText = this.。.bind(this)
        }

        clickText() {
            console.log(this.state.flags)
            this.setState({
                flags:!this.state.flags
            });

        }
        render() {
            return (
               <button onClick={this.clickText}>
                   {this.state.flags?"ok":"no"}
               </button>
               

            )
        }

    }
ReactDOM.render(<Clocks/>,document.getElementById("app"))

事件的传参

<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>

条件渲染

与运算符 (&&)

   {unreadMessages.length > 0 &&        <h2>          You have {unreadMessages.length} unread messages.        </h2>      }

注:如果条件为true,会执行右侧的,如果条件为false会跳过

渲染多个组件

function NumberList(props) {
  const numbers = props.numbers;
  const listItems = numbers.map((number) =>
    <li>{number}</li>
  );
  return (
    <ul>{listItems}</ul>
  );
}

const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
  <NumberList numbers={numbers} />,
  document.getElementById('root')
);

可以给列表元素添加一个key作为独一无二的,如果米有则默认索引作为key

注: 如果列表项目的顺序可能会变化,我们不建议使用索引来用作 key 值,因为这样做会导致性能变差,还可能引起组件状态的问题。

用 key 提取组件

如果你提取出一个 ListItem 组件,你应该把 key 保留在数组中的这个 元素上,而不是放在 `ListItem` 组件中的 元素上。

:在map方法中的元素需要设置key属性

key在兄弟节点之间必须唯一

表单

select

由于 selected 属性的缘故,椰子选项默认被选中。React 并不会使用 selected 属性,而是在根 select 标签上使用 value 属性。这在受控组件中更便捷,因为您只需要在根标签中更新它。

你可以将数组传递到 value 属性中,以支持在 select 标签中选择多个选项

style样式

// 1. 直接写行内样式:
<li style={{border:'1px solid red', fontSize:'12px'}}></li>

// 2. 抽离为对象形式
var styleH3 = {color:'blue'}
var styleObj = {
  liStyle:{border:'1px solid red', fontSize:'12px'},
  h3Style:{color:'green'}
}

<li style={styleObj.liStyle}>
  <h3 style={styleObj.h3Style}>评论内容:{props.content}</h3>
</li>

// 3. 使用样式表定义样式:
import '../css/comment.css'
<p className="pUser">评论人:{props.user}</p>

组件的生命周期函数

创建阶段(Mounting)

该阶段函数只执行一次

constructor():

  1. 作用:1.获取props,2.初始化state
  2. 说明:通过constructor()的参数props获取
  3. 设置state和props

componentWillMount()

  1. 组件被挂载到页面之前执行,在render()之前,在这里同步设置状态将不会被触发重xuanran
  2. 无法获取页面DOM对象
  3. 可以调用setState()来改变状态值
  4. 用来发送ajax请求

render()

  1. 渲染组件到页面中,无法获取页面中的DOM
  2. 不要使用setState()会发生递归渲染,(状态改变会重新调用render(),render()又重新改变状态)

componentDidMount()

  1. 组件已经重新挂载到页面中,可获取组件内部的DOM
  2. 可以发送请求
  3. 可以修改状态值(setState),改变状态会重新渲染

运行和交互阶段(Updating)

该阶段函数多次执行,当组件中的props或者state改变时。都会触发执行

componentWillReceiveProps()

  1. 当组件接收到props触发
  2. 参数为当前组件的props的值,可以通过this.props获取上一次的值
  3. 修改state不会触发该方法

shouldComponentUpdata()

  1. 根据这个方法的返回值是否重新渲染组件,返回true,则渲染,反之不渲染,返回值为false时,render()不会被调用,
  2. 优势:通过条件控制页面是否渲染,降低渲染频率,提升组价性能
  3. 注:必须返回布尔值

componentWillUpdate()

  1. 组件将要更新,不能修改state否则要循环***
  2. 参数:为最新的属性和状态,有两个参数

render()

重新渲染组件

componentDidUpdate()

  1. 组件已经被更新了,
  2. 参数:旧的属性和状态,有两个参数

卸载阶段(Unmounting)

componentWillUnmount()

在卸载的时候执行清理工作

如:清定时器, componentDidMount创建的DOM对象

react-router

书写:

1、编写路由

2、在父组件中指定

​ 路由连接:

​ 路由:

NavLink:由激活的状态,activeClassName

   <NavLink to="/about">About</NavLink> //路由连接,类似a
    <NavLink {...this.props} activeClassName="activeClass/>
    //把props 所有属性接收
 <Switch> //路由容器,只会跳转一个
              <Route path="/about" component={About}></Route>
              <Route path="/home" component={Home}></Route>
              <Redirect to="/about"></Redirect>//重定向
  </Switch>
// 1 导入组件
import {
  HashRouter as Router,
  Link, Route
} from 'react-router-dom'

// 2 使用 <Router>
<Router>

    // 3 设置 Link 有一个replace属行,将新地址地换成历史访问记录的原地址 
    <Menu.Item key="1"><Link to="/">首页</Link></Menu.Item>
    <Menu.Item key="2"><Link to="/movie">电影</Link></Menu.Item>
    <Menu.Item key="3"><Link to="/about">关于</Link></Menu.Item>
    <Menu.Item key="3"><Link to={{pathname="/fh",search:"?user=a",hash:"#33",state:{msg:"fffffffff"}}}>关于</Link></Menu.Item>
    // 4 设置 Route
    // exact 表示:绝对匹配(完全匹配,只匹配:/)
    <Route exact path="/" component={HomeContainer}></Route>
    <Route path="/movie" component={MovieContainer}></Route>
    <Route path="/about" component={AboutContainer}></Route>   


</Router>
{
pathname="/fh",//跳转的路径
search:"?user=a",//get请求额参数
hash:"#33",//设置HASH值
state:{msg:"fffffffff"},//传入的组件的数据
}

注意点

  • <Router basename="/router"></Router>:作为整个组件的根元素,是路由容器,只能有一个唯一的子元素,可以在此添加basename,则内部的路由连接会自动在路径前面增加一个“/router",可以用来设置不同的子路由
  • <Link></Link>:类似于vue中的标签,to 属性指定路由地址
  • <Route></Route>:类似于vue中的
  • exact 表示:绝对匹配(完全匹配,只匹配:/)

路由参数

  • 配置:通过Route中的path属性来配置路由参数
  • 获取:this.props.match.params 获取
// 配置路由参数
<Route path="/movie/:movieType"></Route>

// 获取路由参数
const type = this.props.match.params.movieType

路由跳转

  • react router - history
  • history.push() 方法用于在JS中实现页面跳转
  • history.go(-1) 用来实现页面的前进(1)和后退(-1)
this.props.history.push('/movie/movieDetail/' + movieId)

嵌套路由

实战注意

本地打包测试

npm install -g serve
serve build

antd按需打包

npm i  react-app-rewired customize-cra babel-plugin-import

然后在项目根目录创建一个 config-overrides.js 用于修改默认配置。

const {override,fixBabelImports}=require("customize-cra")
module.exports = function override(config, env) {
 fixBabelImports("import",{
     libraryName:"antd",
     libraryDirectory:"es",
     style:"css",//自动打包已引入的
 })

)

修改**package.json**

/* package.json */
"scripts": {
  "start": "react-app-rewired start",
  "build": "react-app-rewired build",
   "test": "react-app-rewired test",
}

antd自定义主题色

 npm i antd  react-app-rewired customize-cra babel-plugin-import less less-loader --save

npm i less less-loader --save

修改 config-overrides.js

const { override, fixBabelImports, addLessLoader } = require('customize-cra');

module.exports = override(
    //写了下面这个部分,就实现了按需加载,再也不需要再每个页面里面都引入“antd/dist/antd.css”啦
    fixBabelImports('import', {
      libraryName: 'antd',
      libraryDirectory: 'es',
      style: true  //这里一定要写true,css和less都不行哦
    }),
    addLessLoader({
      javascriptEnabled: true,
      //下面这行很特殊,这里是更改主题的关键,这里我只更改了主色,当然还可以更改其他的,下面会详细写出。
      modifyVars: { "@primary-color": "#f47983"}
    })
)

注意

less-loader@5

addLessLoader({
  javascriptEnabled: true,
  modifyVars: { '@primary-color': '#1DA57A' },
}),

less-loader@6

addLessLoader({
  lessOptions: {
    javascriptEnabled: true,
    modifyVars: { '@primary-color': '#1DA57A' },
  },
}),

还可以修改其他色

@primary-color: #1890ff;                         // 全局主色
@link-color: #1890ff;                            // 链接色
@success-color: #52c41a;                         // 成功色
@warning-color: #faad14;                         // 警告色
@error-color: #f5222d;                           // 错误色
@font-size-base: 14px;                           // 主字号
@heading-color: rgba(0, 0, 0, .85);              // 标题色
@text-color: rgba(0, 0, 0, .65);                 // 主文本色
@text-color-secondary : rgba(0, 0, 0, .45);      // 次文本色
@disabled-color : rgba(0, 0, 0, .25);            // 失效色
@border-radius-base: 4px;                        // 组件/浮层圆角
@border-color-base: #d9d9d9;                     // 边框色
@box-shadow-base: 0 2px 8px rgba(0, 0, 0, .15);  // 浮层阴影

更改一下package.json文件

"scripts": {
    "start": "react-app-rewired start",
    "build": "react-app-rewired build",
    "test": "react-app-rewired test",
}

引入路由

npm add react-router-dom 

在App.js中引入路由

redux

一个独立专门用于状态管理的js插件,多被用于react中,应用中的state都以一个对象树的形式存储在一歌单一的store中,不支持异步

三大原则:

单一数据源,只存子啊唯一一个的store

state只读,唯一改变state的方法就是触发action

使用纯函数修改;reducer

核心概念

action

​ 标识要执行行为对象,

​ 包含2方面的属性,

​ 1.type:标识属性,字符串,唯一,必要,一般大写

​ 2.XXX:数据属性,任意,可选

​ Action Creator(创建Action的工厂函数)

​ const increametfun=(num)=>({type:"",data:num})

reducer

根据老的state和action,产生新的state的纯函数

注意:

返回一个新状态

不要修改原来的状态

store

将state和action与reducer联系在一起

维持应用的state

提供getState()方法获取state

提供dispatch(action)方法更新state

通过subscribe(listener)注册监听器

通过subscribe(listener)返回的函数注销监听器

得到

import {createStore} from "redux"
inport reducer from "./reducers"
const store=createStore(reducer)

redux库核心的管理对象,内部维护state,reducer。

核心方法:

getState()

dispatch(action)

subscribe(listener)

编码:

store.getState()

store.dispatch({type:"INCREMENT",number})

store.subscribe(render)

react-redux

将组件分为两类:

UI组件:

只负责ui呈现,不带有任何业务逻辑,通过props接受数据,不使用任何Redux中的api,一般保存在components的文件夹下,

容器组件:

负责管理数据和业务逻辑,不负责ui呈现,使用Redux的api,一般保存在containers文件下

react-thunk

处理异步操作

插件

npm i --save-dev 

sass-node

npm i sass-loader node-sass --save-dev

webpack.config.js

{
loader:
}
//以下新增
{
test:/\.scss$/,
loaders:['style-loader','css-loader','sass-loader']
}

sass全局变量

npm i -S sass-resources-loader
npm i -D sass-resources-loader
.配置webpack.config.js文件
{
    test: /\.scss$/,
    use: [{
            loader: 'style-loader'
        }, {
            loader: 'css-loader'
        }, {
            loader: 'sass-loader'
        },
        {
            loader: 'sass-resources-loader',
            options: {
                resources: [
                    // resolve方法第二个参数为scss配置文件地址,如果有多个,就进行依次添加即可
                    path.resolve(__dirname, './../src/assets/scss/output.scss')
                ]
            }
        }
    ]
}

或者

{
test: sassRegex,
exclude: sassModuleRegex,
use: getStyleLoaders(
{
importLoaders: 2,
sourceMap: isEnvProduction && shouldUseSourceMap,
},
'sass-loader',
).concat({
loader: 'sass-resources-loader',
options: {
resources: [
// 这里按照你的公共变量文件路径填写
path.resolve(__dirname, './../src/common.scss')
]
}
}),
sideEffects: true,},