React

什么是React ?

        用于构建用户的JavaScript库

React的开发依赖

        开发react必须依赖三个库

  • react:包含react所必须的核心代码
  • react-dom :react渲染在不同平台所需要的核心代码
  • babel:将jsx转换成React代码的工具

◼第一次接触React会被它繁琐的依赖搞蒙,居然依赖这么多东西: (直接放弃?)
 对于Vue来说,我们只是依赖一个vue.js文件即可,但是react居然要依赖三个包。
 其实呢,这三个库是各司其职的,目的就是让每一个库只单纯做自己的事情;
 在React的0.14版本之前是没有react-dom这个概念的,所有功能都包含在react里;
◼ 为什么要进行拆分呢?原因就是react-native。
 react包中包含了react web和react-native所共同拥有的核心代码。
 react-dom针对web和native所完成的事情不同:
✓ web端:react-dom会将jsx最终渲染成真实的DOM,显示在浏览器中
✓ native端:react-dom会将jsx最终渲染成原生的控件(比如Android中的Button,iOS中的UIButton)。

◼ babel是什么呢?
 Babel ,又名 Babel.js。
 是目前前端使用非常广泛的编译器、转移器。
 比如当下很多浏览器并不支持ES6的语法,但是确实ES6的语法非常的简洁和方便,我们开发时希望使用它。
 那么编写源码时我们就可以使用ES6来编写,之后通过Babel工具,将ES6转成大多数浏览器都支持的ES5的语法。
◼ React和Babel的关系:
 默认情况下开发React其实可以不使用babel。
 但是前提是我们自己使用 React.createElement 来编写源代码,它编写的代码非常的繁琐和可读性差。
 那么我们就可以直接编写jsx(JavaScript XML)的语法,并且让babel帮助我们转换成React.createElement。
 后续还会详细讲到;

Hello React案例

ReactDOM.createRoot函数:用于创建一个React根,之后渲染的内容会包含在这个跟当中

app.render函数:

        参数:要渲染的根组件

我们可以通过{}语法来引入外部的变量与表达式

注意:编写React的JavaScript代码时必须添加type=“text/babel”,作用是让babel解析为jsx语法

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>

  <div id="root"></div>
     <div id="app"></div>
  <!-- 添加依赖 -->
  <!-- 依赖三个包  1.react 2.react-dom:react渲染在不同平台需要的代码 3.babel将jsx转换为react代码的工具-->

  <!-- cdn引入 -->
  <script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
  <script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
  <!-- babel -->
  <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>

  <script type="text/babel">
// 编写React代码(jxs语法)
// babel     jxs语法 -> 普通的JavaScript代码  
//渲染Hello World
//react18以前 ReactDOM.render
// ReactDOM.render(<h2> Hello World</h2>,document.querySelector('#root'))

//react18以后
// const root = ReactDOM.createRoot(document.querySelector('#root'))

// root.render(<h2> Hello World</h2>)

const app = ReactDOM.createRoot(document.querySelector('#app'))

// 1.将文本定义为变量
let message = 'Hello World'

// 2.监听按钮的点击
function btnClick(){
  // 1.修改数据
  message = 'Hello 崔陈志'
  // 2.重新渲染
  app.render((
    <div>
      <h2>{message}</h2>
      <button onClick={btnClick}>修改文本</button>
    </div>
  ))
// console.log("btnClick");
}

app.render((
  <div>
  <h2>{message}</h2>
    <button onClick={btnClick}>修改文本</button>  
  </div>
))

  </script>

</body>
</html>

组件化开发

        root.render 参数可以是一个HTML元素或者是一个组件

        所以我们可以将之前的业务封装到一个组件当中,然后传入到ReactDOM.render函数中的第一个参数;

◼ 在React中,如何封装一个组件呢?这里我们暂时使用类的方式封装组件:
 1.定义一个类(类名大写,组件的名称是必须大写的,小写会被认为是HTML元素),继承自React.Component
 2.实现当前组件的render函数
✓ render当中返回的jsx内容,就是之后React会帮助我们渲染的内容

组件化数据依赖

◼ 组件化问题一:数据在哪里定义?
◼ 在组件中的数据,我们可以分成两类:
 参与界面更新的数据:当数据变量时,需要更新组件渲染的内容;
 不参与界面更新的数据:当数据变量时,不需要更新将组建渲染的内容;
◼ 参与界面更新的数据我们也可以称之为是参与数据流,这个数据是定义在当前对象的state中
 我们可以通过在构造函数中 this.state = {定义的数据}
 当我们的数据发生变化时,我们可以调用 this.setState 来更新数据,并且通知React进行update操作;
✓ 在进行update操作时,就会重新调用render函数,并且使用最新的数据,来渲染界面

this默认绑定指向window     但在严格模式下指向undefined (类里面的函数都是开启了严格模式的)    babel会将代码开启严格模式

组件化事件绑定

◼ 组件化问题二:事件绑定中的this
 在类中直接定义一个函数,并且将这个函数绑定到元素的onClick事件上,当前这个函数的this指向的是谁呢?
◼ 默认情况下是undefined

类里面的方法都是开启了严格模式的,babel默认启用严格模式
 很奇怪,居然是undefined;
 因为在正常的DOM操作中,监听点击,监听函数中的this其实是节点对象(比如说是button对象);
 这次因为React并不是直接渲染成真实的DOM,我们所编写的button只是一个语法糖,它的本质React的Element对象;
 那么在这里发生监听的时候,react在执行函数时并没有绑定this,默认情况下就是一个undefined;
◼ 我们在绑定的函数中,可能想要使用当前对象,比如执行 this.setState 函数,就必须拿到当前对象的this
 我们就需要在传入函数时,给这个函数直接绑定this
 类似于下面的写法: <button onClick={this.changeText.bind(this)}>改变文本</button>

或者

React-JSX语法

        认识JSX

◼ 这段element变量的声明右侧赋值的标签语法是什么呢?
 它不是一段字符串(因为没有使用引号包裹);
 它看起来是一段HTML元素,但是我们能在js中直接给一个变量赋值html吗?
 其实是不可以的,如果我们将 type="text/babel" 去除掉,那么就会出现语法错误;
 它到底是什么呢?其实它是一段jsx的语法;
◼ JSX是什么?
 JSX是一种JavaScript的语法扩展(eXtension),也在很多地方称之为JavaScript XML,因为看起就是一段XML语法;
 它用于描述我们的UI界面,并且其完成可以和JavaScript融合在一起使用;
 它不同于Vue中的模块语法,你不需要专门学习模块语法中的一些指令(比如v-for、v-if、v-else、v-bind)

为什么React选择了JSX

·◼ React认为渲染逻辑本质上与其他UI逻辑存在内在耦合
 比如UI需要绑定事件(button、a原生等等);
 比如UI中需要展示数据状态;
 比如在某些状态发生改变时,又需要改变UI;
◼ 他们之间是密不可分,所以React没有将标记分离到不同的文件中,而是将它们组合到了一起,这个地方就是组件(Component);
 当然,后面我们还是会继续学习更多组件相关的东西;
◼ 在这里,我们只需要知道,JSX其实是嵌入到JavaScript中的一种结构语法;
◼ JSX的书写规范:
 JSX的顶层只能有一个根元素,所以我们很多时候会在外层包裹一个div元素(或者使用后面我们学习的Fragment);
 为了方便阅读,我们通常在jsx的外层包裹一个小括号(),这样可以方便阅读,并且jsx可以进行换行书写;
 JSX中的标签可以是单标签,也可以是双标签;
✓ 注意:如果是单标签,必须以/>结尾;

JSX的使用

◼ jsx中的注释     {/*我是jsx的注解*/}
◼ JSX嵌入变量作为子元素

       {/*Number/String/Array直接显示出来*/}
              <h2>{movies}</h2>

              {/*undefined/null/Boolean显示不出来*/}
              <h2>{aaa+''}</h2>

              {/*对象不能作为子元素显示*/}
              {/*<h1>{find}</h1>*/}


 情况一:当变量是Number、String、Array类型时,可以直接显示
 情况二:当变量是null、undefined、Boolean类型时,内容为空;
✓ 如果希望可以显示null、undefined、Boolean,那么需要转成字符串;
✓ 转换的方式有很多,比如toString方法、和空字符串拼接,String(变量)等方式;
 情况三:Object对象类型不能作为子元素(not valid as a React child)
◼ JSX嵌入表达式
 运算表达式

{/*运算表达式*/}

<h2>{20+30}</h2>

 三元运算符

 {/*三元运算符*/}

 <h2>{age>18?'成年人':'未成年'}</h2>

 执行一个函数

◼ jsx绑定属性
 比如元素都会有title属性

   <h3 title={title}>属性绑定</h3>


 比如img元素会有src属性
 比如a元素会有href属性
 比如元素可能需要绑定class(建议使用className)

  {/*绑定class属性*/}
  <div class="aa ccc">我是一个class</div>
  <div className="aa ccc">我是一个class</div>


 比如原生使用内联样式style

       {/*绑定style属性*/}
              <h3 style={{color:"red",fontsize:"30px"}}>style</h3>

React事件绑定

◼ 如果原生DOM原生有一个监听事件,我们可以如何操作呢?
 方式一:获取DOM原生,添加监听事件;
 方式二:在HTML原生中,直接绑定onclick;
◼ 在React中是如何操作呢?我们来实现一下React中的事件监听,这里主要有两点不同
 React 事件的命名采用小驼峰式(camelCase),而不是纯小写;
 我们需要通过{}传入一个事件处理函数,这个函数会在事件发生时被执行;

this的绑定问题

◼ 在事件执行后,我们可能需要获取当前类的对象中相关的属性,这个时候需要用到this
 如果我们这里直接打印this,也会发现它是一个undefined
◼ 为什么是undefined呢?
 原因是btnClick函数并不是我们主动调用的,而且当button发生改变时,React内部调用了btnClick函数;
 而它内部调用时,并不知道要如何绑定正确的this;
◼ 如何解决this的问题呢?
 方案一:bind给btnClick显示绑定this
 方案二:使用 ES6 class fields 语法
 方案三:事件监听时传入箭头函数(个人推荐)

在js当中使用redux

npm install redux

1.通过const { createStore } = require("redux") 导入 createStore  

2.初始化数据

const initialState = {

    name: 'cui'

}

3.定义renducer函数 

4.通过  const store = createStore(renducer) 创建store  

const { createStore } = require("redux")
//初始化数据
const initialState = {
    name: 'cui'
}
//定义renducer函数:纯函数:
/*此函数在相同的输入值时,需产生相同的输出。
函数的输出和输入值以外的其他隐藏信息或状态无关,也和由I / O设备产生的外部输出无关。
该函数不能有语义上可观察的函数副作用,诸如:“触发事件”,使输出设备输出,或更改输出值以外物件的内容等。 */
function renducer() {
    return initialState
}


//创建的store,自动调用renducer函数,并传入初始状态initialState作为参数
const store = createStore(renducer)
module.exports = store

使用store的数据

//导入store
const store = require("../src/store")

// 通过store.getStore()方法获取state数据
console.log(store.getState())

修改store当中的数据

index.js

const { createStore } = require("redux")
//初始化数据
const initialState = {
    name: 'cui',
    counter: 100
}
//定义renducer函数:纯函数:
/*此函数在相同的输入值时,需产生相同的输出。
函数的输出和输入值以外的其他隐藏信息或状态无关,也和由I / O设备产生的外部输出无关。
该函数不能有语义上可观察的函数副作用,诸如:“触发事件”,使输出设备输出,或更改输出值以外物件的内容等。 */
// renducer函数接收2个参数
//参数一:state之前的状态
//参数二:action触发的动作
//返回值作为store的新state
function renducer(state = initialState, action) {
    console.log(state, action)
    //如果数据有修改,那么返回一个 state
    if (action.type === "change_name") {
        return { ...state, name: action.name }
    }
    return initialState
}


//创建的store,自动调用renducer函数,并传入初始状态initialState作为参数
const store = createStore(renducer)
module.exports = store

 修改文件

const store = require("./store")

//修改store中的数据,必须action
const nameActions = { type: "change_name", name: "chen" }
store.dispatch(nameActions)
console.log(store.getState())//{ name: 'chen', counter: 100 }

订阅store当中数据的变化(订阅文件)subscribe

const store = require("./store")

//subscribe 订阅store中的数据,当store中的数据发生改变的时候,会执行subscribe中的函数
const unsubscribe = store.subscribe(() => {
    console.log("store中的数据发生改变",store.getState())
})
//修改store中的数据,必须action
store.dispatch({ type: "change_name", name: "chen" })
store.dispatch({ type: "change_name", name: "chen" })
unsubscribe() //取消订阅

store.dispatch({ type: "change_counter", counter: 200 })



简化index.js代码

const { createStore } = require("redux")
//初始化数据
const initialState = {
    name: 'cui',
    counter: 100
}
//定义renducer函数:纯函数:
/*此函数在相同的输入值时,需产生相同的输出。
函数的输出和输入值以外的其他隐藏信息或状态无关,也和由I / O设备产生的外部输出无关。
该函数不能有语义上可观察的函数副作用,诸如:“触发事件”,使输出设备输出,或更改输出值以外物件的内容等。 */
// renducer函数接收2个参数
//参数一:state之前的状态
//参数二:action触发的动作
//返回值作为store的新state
function renducer(state = initialState, action) {
 
    switch (action.type) {
        case "change_name":
            return { ...state, name: action.name }
        case "change_counter": return { ...state, counter: action.counter + action.counter }
        default:
            return state
    }

}


//创建的store,自动调用renducer函数,并传入初始状态initialState作为参数
const store = createStore(renducer)
module.exports = store

动态生成action

const store = require("./store")

//subscribe 订阅store中的数据,当store中的数据发生改变的时候,会执行subscribe中的函数
const unsubscribe = store.subscribe(() => {
    console.log("store中的数据发生改变", store.getState())
})

const changeNameAction = (name) => ({
    type: "change_name",
    name
})

const changeCounterAction = (counter) => ({
    type: "change_counter",
    counter
})
//修改store中的数据,必须action
store.dispatch(changeNameAction("chen"))
store.dispatch(changeNameAction("zhi"))
unsubscribe() //取消订阅


store.dispatch(changeCounterAction(12))



优化代码分成4个文件

 1.actionCreators.js

const changeNameAction = (name) => ({
    type: "change_name",
    name
})

const changeCounterAction = (counter) => ({
    type: "change_counter",
    counter
})
module.exports = {
    changeNameAction,
    changeCounterAction
}

2.constants.js

const CHANGE_NAME = "change_name";
const CHANGE_CIUNTER = "change_counter";
module.exports = {
    CHANGE_NAME,
    CHANGE_CIUNTER,
};

3.index.js

const { createStore } = require("redux");
const renducer = require("./reducer");
//创建的store,自动调用renducer函数,并传入初始状态initialState作为参数
const store = createStore(renducer);
module.exports = store;

4.reducer.js

const { CHANGE_NAME, CHANGE_CIUNTER } = require("./constants");
//初始化数据
const initialState = {
    name: "cui",
    counter: 100,
};
//定义renducer函数:纯函数:
/*此函数在相同的输入值时,需产生相同的输出。
函数的输出和输入值以外的其他隐藏信息或状态无关,也和由I / O设备产生的外部输出无关。
该函数不能有语义上可观察的函数副作用,诸如:“触发事件”,使输出设备输出,或更改输出值以外物件的内容等。 */
// renducer函数接收2个参数
//参数一:state之前的状态
//参数二:action触发的动作
//返回值作为store的新state
function renducer(state = initialState, action) {
    switch (action.type) {
        case CHANGE_NAME:
            return { ...state, name: action.name };
        case CHANGE_CIUNTER:
            return { ...state, counter: action.counter + action.counter };
        default:
            return state;
    }
}

module.exports = renducer

注意:reducer只能创建一个Store,唯一修改state的方法是触发action,使用纯函数reducer修改执行修改,将state更action联系在一起,返回一个新的state

三大原则(redux)

  1. 单一数据源
  2. State是只读的
  3. 使用reducer纯函数执行修改

在react当作使用redux

  • 15
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值