React速成day1

678 阅读9分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 2 天

什么是npx?

使用npx而不是npm安装react脚手架有一个好处:

npm会因为node版本不同可能会导致某个版本的node没有安装脚手架因此无法使用,如果每个版本都安装脚手架会导致没必要的内存空间浪费,使用npx 还能避免全局安装的模块。比如,create-react-app这个模块是全局安装,npx 可以运行它,而且不进行全局安装。

$ npx create-react-app my-react-app

上面代码运行时,npx 将create-react-app下载到一个临时目录,使用以后再删除。所以,以后再次执行上面的命令,会重新下载create-react-app。

npx 还可以执行 GitHub 上面的模块源码。

# 执行 Gist 代码
$ npx https://gist.github.com/zkat/4bc19503fe9e9309e2bfaa2c58074d32

# 执行仓库代码
$ npx github:piuccio/cowsay hello

注意,远程代码必须是一个模块,即必须包含package.json和入口脚本。

react初认识

两个最基本的包:

React:框架的核心包

ReactDOM:专门做渲染相关的包

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

一开始id为root的dom节点默认在index.html文件中

ps:严格节点应该去掉,因为会影响useEffect的执行时机

什么是jsx

jsx就是可以在js中写html结构

jsx中html结构中可以用{}包裹js表达式,可以使用的表达式有:

1.字符串、数值、布尔值、null、undefined、object([]/{})

2.1+2、'abc'.split('')、['a','b'].join('-')、三元表达式。。。

3.fn()

ps:if 语句/switch-case 语句/变量声明语句,这些叫做语句,不是表达式,不能出现在{}中!!

循环渲染列表

如果要渲染一组数据,应该使用数组的 map0方法

注意:渲染列表时应该添加 key 属性,key 属性的值要保证唯一

原则:map()遍历谁,就给谁添加 key 属性

const songs = [
  {id:1,name:'痴心绝对'},
  {id:2,name:'像我这样的人'},
  {id:3,name:'南山南'},
]

function App() {
  return(
    <div>
      <ul>
        {songs.map(item=><li key={item.id} >{item.name}</li>)}
      </ul>
    </div>
  )
}

条件渲染

作用:根据是否满足条件生成HTML结构,比如Loading

效果实现:可以使用三元运算符或逻辑与(&&)运算符

const flag = false

function App() {
  return(
    <div>
      {
        flag?(<div><span>this is span</span></div>) :null
      }
      {true&&<span>this is span</span>}
  
    </div>
  )
}

函数组件

1、组件的名称必须首字母大写,react内部会根据这个来判断是组件还是普通的HTML标签

2、函数组件必须有返回值,表示该组件的 U1 结构;如果不需要渲染任何内容,则返回 null

3、组件就像HTML标签一样可以被渲染到页面中。组件表示的是一段结构内容,对于函数组件来说,渲染的内容是函数的返回值就是对应的内容

4、使用函数名称作为组件标签名称,可以成对出现也可以自闭合

例:

function Hello(){
  return <div>hello</div>
}
//渲染<Hello/>  <Hello></Hello>
function App(){
  return (
  <div>
    <Hello/>
    <Hello></Hello>
    </div>
  )
}

类组件

用es6的class创建的组件就叫类组件

1、类名称也必须以大写字母开头

2、类组件应该继承React.Component父类,从而使用父类中提供的方法或属性

3、类组件必须提供render方法render方法必须有返回值,表示该组件的u1结构

class HelloComponent extends React.Component {
  render() {
    return <div>this is class Component</div>
  }
}

function App() {
  return (
    <div className="App">
      <HelloComponent></HelloComponent>
    </div>
  )
}

事件绑定

语法

on+事件名称={事件处理程序},比如:<div onClick={()=>{}}></div>

注意:react事件采用驼峰命名法,比如:onMouseEnter、onFocus

class HelloComponent extends React.Component{
//事件回调函数(标准写法 避免this指向问题)
//这样写 回调函数中的this指向的是当前的组件实例对象
clickHandler =()=>{
console.log('类组件中的事件被触发了');
}
  render(){
return <div onClick={this.clickHandler}>this is class Component</div>
    }
function HelloFn (){
// 定义事件回调函数
const clickHandler = () => {
console.log('事件被触发了')
}
return (
<button onClick={clickHandler}>click me!</button>
)
}

获取事件对象

通过事件处理程序的参数获取事件对象e

function App() {
  const clickHandler = (e, msg) => {
    console.log('事件被触发了' + msg)
    e.preventDefault()
  }
  return (
    <div>
      <a
        href="http://baidu.com"
        onClick={(e) => clickHandler(e, 'this is msg')}
      >
        clickme
      </a>
    </div>
  )
}

类组件的状态

定义状态必须通过state 实例属性的方法 提供一个对象 名称是固定的就叫做state

修改state中的任何属性 都不可以通过直接赋值 必须走setstate方法 这个方法来自于继承得到

这里的this关键词 很容易出现指向错误的问题 上面的写法是最推荐和最规范的 没有this指向问题

class TestComponent extends React.Component {
  state = {
    name: 'teacher',
  }

  changename = (e) => {
    console.log('事件被触发了')
    e.preventDefault()
    this.setState({
      name: 'hgc',
    })
  }
  render() {
    return (
      <div>
        <div>当前name为:{this.state.name}</div>
        <a href="http://baidu.com" onClick={(e) => this.changename(e)}>
          修改名字
        </a>
      </div>
    )
  }

}

react的this指向问题

如果在jsx中直接用{}包裹this指针,那么this指针是undefined,必须要用箭头函数包裹直接沿用父函数中的this指向才能使this指针有效,也可以用另一种比较麻烦的办法进行修正:

class Test extends React.Component{
constructor(){
  //这样会报错
  handler
  {console.log(this)
    this.setstate}
  
  //这样是正确的
  super()
//使用bind强行修正我们的this指向
//相当于在类组件初始化的阶段 就可以把回调函数的this修正到
//永远指向当前组件实例对象
this.handler = this.handler.bind(this)
 
}
}


render () {
return (
<button onClick={ this. handler}>click</button>
)}

react的状态不可变

概念:不要直接修改状态的值,而是基于当前状态创建新的状态值

state = {
count : 0, 
  list: [1,2,3],
  person: {name: 'jack',age:18} 
  
}
//直接修改简单类型Number
this.state.count++
++this.state.count
this.state.count += 1
this.state.count = 1
this.setState({
count:this.state.count + 1
list:[...this.state.list,4],
 person:{
...this.state.person,
//覆盖原来的属性 就可以达到修改对象中属性的目的
name:'rose'
})

react的表单处理(使用受控组件)

什么是受控组件?

受控组件就是可以 被react的状态控制的组件(input框自己的状态被React组件状态控制)

React组件的状态的地方是在state中,input表单元素也有自己的状态是在value中,React将state与表单元素的值(value)绑定到一起,由state的值来控制表单元素的值,从而保证单一数据源特性

实现步骤:

以获取文本框的值为例,受控组件的使用步骤如下:

1.在组件的state中声明一个组件的状态数据2,将状态数据设置为input标签元素的value属性的值

3.为input添加change事件

4,在事件处理程序中,通过事件对象e获取到当前文本框的值(即用户当前输入的值)

5,调用setState方法,将文本框的值作为state状态的最新值

export default class Four extends Component {
  state = {
    message: 'hello',
  }
  changeMessage = (e) => {
    this.setState({
      message: e.target.value,
    })
  }
  render() {
    let { message } = this.state
    return (
      <div className="four">
        <p>
          请输入:
          <input type="text" value={message} onChange={this.changeMessage} />
        </p>
      </div>
    )
  }
}

react的表单处理(非受控方式)

非受控组件就是通过手动操作dom的方式获取文本框的值,文本框的状态不受react组件的state中的状态控制,直接通过原生dom获取输入框的值

实现步骤:

1.导入 createRef 函数

2.调用createRef函数,创建一个ref对象,存储到名为 msgRef 的实例属性中

3.为input添加ref属性,值为msgRef

4.在按钮的事件处理程序中,通过msgRef.current即可拿到input对应的dom元素,而其中msgRef.current.value拿到的就是文本框的值

import React, { Component, createRef } from 'react'
export default class Four extends Component {
  msgRef = createRef() //使用create方法创建一个存放dom元素的容器
  state = {
    message2: '',
  }

  getmessage = () => {
    this.setState({
      message2: this.msgRef.current.value,
    })
  }
  
    render() {
    let { message } = this.state
    return (
      <div className="four">
        <h2>非受控组件</h2>
        <p>
          请输入:
          {/* 使用ref属性将dom元素跟ref对象进行关联,之后就可以通过操作ref对象的方式去操作dom元素 */}
          <input type="text" ref={this.msgRef} />
          <button onClick={this.getmessage}>获取数据</button>
          <span>{this.state.message2}</span>
        </p>
      </div>
    )
  }
}

jsx样式控制

行内样式

在元素身上绑定一个style属性即可

function App() {
  return(
    <div className = "App">
      <span style={{color:'red',fontSize:'30px'}}>this is span</span>
      
    </div>
  )
}

通常可以将style的对象另外定义,然后传一个对象给style属性就可以了

const style =  {color:'red',fontSize:'30px'} 

function App() {
  return(
    <div className = "App">
      <span style={style}>this is span</span>
      
    </div>
  )
}

注意:这样传的话是一个{}不要和上面两个{}的情况弄混,react传数据都是一个{}

内联样式的优点:

1.内联样式,样式之间不会有冲突

2.可以动态获取当前state中的状态内联样式的缺点:

1.写法上都需要使用驼峰标识(因为js不支持用-连接的写法),驼峰就是每个单词首字母大写

2.某些样式没有提示

3.大量的样式,代码混乱04某些样式无法编写(比如伪类/伪元素)

类名样式

在元素身上绑定一个className属性即可,但是这样写需要另外写一个css文件然后再引入这个css文件

这样的编写方式和普通的网页开发中编写方式是一致的:

如果我们按照普通的网页标准去编写,那么也不会有太大的问题;

但是组件化开发中我们总是希望组件是一个独立的模块,即便是样式也只是在自己内部生效,不会相互影响;

但是普通的css都属于全局的css,样式之间会相互影响;

css modules

css modules并不是React特有的解决方案,而是所有使用了类似于webpack配置的环境下都可以使用的。

如果在其他项目中使用它,那么我们需要自己来进行配置,比如配置webpack.config.js中的modules:true等。

React的脚手架已经内置了css modules的配置:

xxx.css/.less/.scss等样式文件都需要修改成.module.css/.module.less/.module.scss 等;

之后就可以引用并且进行使用了;css modules确实解决了局部作用域的问题,也是很多人喜欢在React中使用的一种方案。

但是这种方案也有自己的缺陷:

1、引用的类名,不能使用连接符(.home-title),在JavaScript中是不识别的;

2、所有的className都必须使用(style.className)的形式来编写;

3、不方便动态来修改某些样式,依然需要使用内联样式的方式;

如果你觉得上面的缺陷还算OK,那么你在开发中完全可以选择使用css modules来编写,并且也是在React中很受欢迎的一种方式。

!!styled-components

styled-components是CSS-in-JS的一种库。

"CSS-in-JS”是指一种模式,其中 CSS 由 JavaScript 生成而不是在外部文件中定义;主意此功能并不是 React 的一部分,而是由第三方库提供;React 对样式如何定义并没有明确态度;

在传统的前端开发中,我们通常会将结构(HTML)、样式(CSS)、逻辑(JavaScript)进行分离。

但是在前面的学习中,我们就提到过,React的思想中认为逻辑本身和UI是无法分离的,所以才会有了JSX的语法。

样式呢?样式也是属于UI的一部分;

事实上CSS-in-JS的模式就是一种将样式(CSS)也写入到JavaScript中的方式,并且可以方便的使用JavaScript的状态;所以React有被人称之为 All in JS;

CSS-in-JS通过JavaScript来为CSS赋予一些能力,包括类似于CSS预处理器一样的样式嵌套、函数定义、逻辑复用、动态修改状态等等;虽然CSS预处理器也具备某些能力,但是获取动态状态依然是一个不好处理的点;所以,目前可以说CSS-in-JS是React编写CSS最为受欢迎的一种解决方案