React基础入门详解

React项目的创建

1、 运行 cnpm i react react-dom -S 安装包
2、 在项目中导入两个相关的包:

// 1. 在 React 学习中,需要安装 两个包 react  react-dom
// 1.1 react 这个包,是专门用来创建React组件、组件生命周期等这些东西的;
// 1.2 react-dom 里面主要封装了和 DOM 操作相关的包,比如,要把 组件渲染到页面上
import React from 'react'
import ReactDOM from 'react-dom'

3、使用JS的创建虚拟DOM节点:

// 2. 在 react 中,如要要创建 DOM 元素了,只能使用 React 提供的 JS API 来创建,不能【直接】像 Vue 中那样,手写 HTML 元素
// React.createElement() 方法,用于创建 虚拟DOM 对象,它接收 3个及以上的参数
// 参数1: 是个字符串类型的参数,表示要创建的元素类型
// 参数2: 是一个属性对象,表示 创建的这个元素上,有哪些属性
// 参数3: 从第三个参数的位置开始,后面可以放好多的虚拟DOM对象,这写参数,表示当前元素的子节点
// 
这是一个div
var myH1 = React.createElement('h1', null, '这是一个大大的H1') var myDiv = React.createElement('div', { title: 'this is a div', id: 'mydiv' }, '这是一个div', myH1)

4、 使用 ReactDOM 把元素渲染到页面指定的容器中:

ReactDOM.render(myDiv, document.getElementById('app'))

ReactDOM.render(‘要渲染的虚拟DOM元素’, ‘要渲染到页面上的哪个位置中’)
注意: ReactDOM.render() 方法的第二个参数,和vue不一样,不接受 “#app” 这样的字符串,而是需要传递一个 原生的 DOM 对象

JSX语法

1、如要要使用 JSX 语法,必须先运行 cnpm i babel-preset-react -D,然后再 .babelrc 中添加语法配置"presets": ["env", "stage-0", "react"]
2. JSX语法的本质:还是以 React.createElement 的形式来实现的,并没有直接把用户写的 HTML代码,渲染到页面上;
3. 如果要在 JSX 语法内部,书写 JS 代码了,那么,所有的JS代码,必须写到 {} 内部;
4. 当 编译引擎,在编译JSX代码的时候,如果遇到了<那么就把它当作 HTML代码去编译,如果遇到了 {} 就把 花括号内部的代码当作 普通JS代码去编译;
5. 在{}内部,可以写任何符合JS规范的代码;
6. 在JSX中,如果要为元素添加class属性了,那么,必须写成className,因为 class在ES6中是一个关键字;和class类似,label标签的 for 属性需要替换为 htmlFor.
7. 在JSX创建DOM的时候,所有的节点,必须有唯一的根元素进行包裹;
8. 如果要写注释了,注释必须放到 {} 内部

React创建组件的两种方式

1、 第一种基本组件的创建方式:用构造函数创建出来的组件

Hello.jsx

import React from 'react'
// 在 function 定义的组件中,如果想要使用 props,必须先定义,否则无法直接使用
// 但是,在class定义的组件中,可以直接使用 this.props 来直接访问,不需要预先接收 props
export default function Hello(props) {
  // console.log(props)
  return 

haha --- {props.name}

}

2、第二种创建组件的方式:基于class关键字创建组件

Hello2.jsx

  • 使用 class 创建的类,通过 extends 关键字,继承了 React.Component 之后,这个类,就是一个组件的模板了;
  • 如果想要引用这个组件,可以把 类的名称, 以标签形式,导入到 JSX 中使用;
  • 注意: 如果使用 extends 实现了继承,那么在 constructor 的第一行,一定要显示调用一下 super(), super() 表示父类的构造函数;
  • 在 constructor 中,如果想要访问 props 属性,不能直接使用 this.props, 而是需要在 constructor 的构造器参数列表中,显示的定义 props 参数来接收,才能正常使用;
  • 注意:this.state 表示 当前组件实例的私有数据对象,就好比 vue 中,组件实例身上的 data(){ return {} } 函数,如果想要使用 组件中 state 上的数据,直接通过 this.state.*** 来访问即可;
  • 如果要为 this.state 上的数据重新赋值,那么,React 推荐使用 this.setState({配置对象}) 来重新为 state 赋值 ,注意: this.setState 方法,只会重新覆盖那些 显示定义的属性值,如果没有提供最全的属性,则没有提供的属性值,不会被覆盖;
  • this.setState 方法,也支持传递一个 function,如果传递的是 function,则在 function 内部,必须return 一个 对象;
    在 function 的参数中,支持传递两个参数,其中,第一个参数是 prevState,表示为修改之前的老的 state 数据; 第二个参数,是 外界传递给当前组件的 props 数据
import React from 'react'

export default class Hello2 extends React.Component {
  constructor(props) {
    super(props)
    // console.log(props)
    this.state = {
      msg: '这是 Hello2 组件的私有msg数据',
      info: 'test**'
    }
  }
  // 保存信息1: No `render` method found on the returned component instance: you may have forgotten to define `render`.
  // 通过分析以上报错,发现,提示我们说,在 class 实现的组件内部,必须定义一个 render 函数
  render() {
    // 报错信息2: Nothing was returned from render. This usually means a return statement is missing. Or, to render nothing, return null.
    // 通过分析以上报错,发现,在 render 函数中,还必须 return 一个东西,如果没有什么需要被return 的,则需要 return null

    // 虽然在 React dev tools 中,并没有显示说 class 组件中的 props 是只读的,但是,经过测试得知,其实 只要是 组件的 props,都是只读的;
    // this.props.address = '123'
    // console.log(this.props)

    return 

这是 使用 class 类创建的组件

外界传递过来的数据是: {this.props.address} --- {this.props.info}

{this.state.msg}

} changeMsg = () => { // console.log('ok') // 注意: 这里不是传统网页,所以 React 已经帮我们规定死了,在 方法中,默认this 指向 undefined,并不是指向方法的调用者 // console.log(this) // 直接使用 this.state.msg = '123' 为 state 上的数据重新赋值,可以修改 state 中的数据值,但是,页面不会被更新; // 所以这种方式,React 不推荐,以后尽量少用; // this.state.msg = '123' /* this.setState({ msg: '123' }) */ this.setState(function (prevState, props) { // console.log(props) return { msg: '123' } }, function () { // 由于 this.setState 是异步执行的,所以,如果想要立即拿到最新的修改结果,最保险的方式, 在回调函数中去操作最新的数据 console.log(this.state.msg) }) // 经过测试发现, this.setState 在调用的时候,内部是异步执行的,所以,当立即调用完 this.setState 后,输出 state 值可能是旧的 // console.log(this.state.msg) } }

{/* 1.1 在React中,如果想要为元素绑定事件,不能使用 网页中 传统的 onclick 事件,而是需要 使用 React 提供的 onClick /}
{/
1.2 也就是说:React中,提供的事件绑定机制,使用的 都是驼峰命名,同时,基本上,传统的 JS 事件,都被 React 重新定义了一下,改成了 驼峰命名 onMouseMove /}
{/
2.1 在 React 提供的事件绑定机制中,事件的处理函数,必须直接给定一个 function,而不是给定一个 function 的名称 /}
{/
2.2 在为 React 事件绑定 处理函数的时候,需要通过 this.函数名, 来把 函数的引用交给 事件 */}

两种创建组件方式的对比

1、用构造函数创建出来的组件:专业的名字叫做“无状态组件”
2、用class关键字创建出来的组件:专业的名字叫做“有状态组件”

用构造函数创建出来的组件,和用class创建出来的组件,这两种不同的组件之间的本质区别就是:有无state属性!!!

使用CSS模块化

1、可以在webpack.config.js中为css-loader启用模块化:
css-loader?modules&localIdentName=[name]_[local]-[hash:8]
2、使用:global()定义全局样式

代码demo:

webpack.config.js文件:

const path = require('path')
const htmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  entry: path.join(__dirname, './src/main.js'),
  output: {
    path: path.join(__dirname, './dist'),
    filename: 'bundle.js'
  },
  plugins: [ // 插件
    new htmlWebpackPlugin({
      template: path.join(__dirname, './src/index.html'),
      filename: 'index.html'
    })
  ],
  module: {
    rules: [
      // 通过 为 css-loader 添加 modules 参数,启用 CSS 的模块化
      { test: /\.css$/, use: ['style-loader', 'css-loader?modules&localIdentName=[name]_[local]-[hash:5]'] }, 
      { test: /\.scss$/, use: ['style-loader', 'css-loader', 'sass-loader'] },
      { test: /\.(png|gif|bmp|jpg)$/, use: 'url-loader?limit=5000' },
      { test: /\.jsx?$/, use: 'babel-loader', exclude: /node_modules/ }
    ]
  }
}

.babelrc文件:

{
  "presets": ["env", "stage-0", "react"],
  "plugins": ["transform-runtime"]
}

main.js

// JS打包入口文件
// 1. 导入 React包
import React from 'react'
import ReactDOM from 'react-dom'

// 使用JS语法,创建虚拟DOM元素
// var myDiv = React.createElement('h1', { id: 'mydiv', title: 'ok' }, '这是一个H1')
var myDiv = 
OKOKOK
// 在使用 Hello 组件之前,先导入 组件 import Hello from './components/Hello.jsx' import Hello2 from './components/Hello2.jsx' ReactDOM.render(
, document.getElementById('app'))

注意: 以上两种创建组件的方式,有着本质上的区别,其中,使用 function 构造函数创建的组件,内部没有 state 私有数据,只有 一个 props 来接收外界传递过来的数据;使用 class 关键字 创建的组件,内部,除了有 this.props 这个只读属性之外,还有一个 专门用于 存放自己私有数据的 this.state 属性,这个 state 是可读可写的!
基于上面的区别:
使用 function 创建的组件,叫做【无状态组件】;
使用 class 创建的组件,叫做【有状态组件】
有状态组件和无状态组件,最本质的区别,就是有无 state 属性;同时, class 创建的组件,有自己的生命周期函数,但是,function 创建的 组件,没有自己的生命周期函数;

问题来了:什么时候使用 有状态组件,什么时候使用无状态组件呢?
1、 如果一个组件需要存放自己的私有数据,或者需要在组件的不同阶段执行不同的业务逻辑,此时,非常适合用 class 创建出来的有状态组件;
2、如果一个组件,只需要根据外界传递过来的 props,渲染固定的 页面结构就完事儿了,此时,非常适合使用 function 创建出来的 无状态组件;(使用无状态组件的小小好处: 由于剔除了组件的生命周期,所以,运行速度会相对快一点)

列表及样式demo
CommentList.jsx

import React from 'react'

// 导入当前组件需要的子组件
import CommentItem from './CommentItem.jsx'

// 评论列表组件
export default class CommentList extends React.Component {
  constructor(props) {
    super(props)

    // 定义当前评论列表组件的 私有数据
    this.state = {
      cmts: [
        { user: '张三', content: '沙发' },
        { user: '张三2', content: '板凳' },
        { user: '张三3', content: '凉席' },
        { user: '张三4', content: '砖头' },
        { user: '张三5', content: 'test' }
      ]
    }
  }

  // 在 有状态组件中, render 函数是必须的,表示,渲染哪些 虚拟DOM元素并展示出来
  render() {
    //#region 循环 评论列表的方式1,比较low,要把 JSX 和 JS 语法结合起来使用
    /* var arr = []
    this.state.cmts.forEach(item => {
      arr.push(

{item.user}

) }) */ //#endregion return

评论列表案例

{/* 我们可以直接在 JSX 语法内部,使用 数组的 map 函数,来遍历数组的每一项,并使用 map 返回操作后的最新的数组 */} {this.state.cmts.map((item, i) => { // return return })}
} }

CommentItem.jsx

import React from 'react'
// 注意: 在使用 import 的时候,import 只能放到模块的 开头位置
import inlineStyles from './cmtItemStyles.js'

// 导入评论项的样式文件【这种直接 import '../路径标识符' 的 CSS 导入形式,并不是模块化的CSS】
// import '../../css/commentItem.css'
// 默认情况下,如果没有为 CSS 启用模块化,则接收到的 itemStyles 是个空对象,因为 .css 样式表中,不能直接通过 JS 的 export defualt 导出对象

// 当启用 CSS 模块化之后,导入 样式表得到的 itemStyles 就变成了一个 样式对象,
其中,属性名是 在样式表中定义的类名,属性值,是自动生成的一个复杂的类名(防止类名冲突)
import itemStyles from '../../css/commentItem.css'
console.log(itemStyles)

// 封装一个 评论项 组件,此组件由于不需要自己的 私有数据,所以直接定义为 无状态组件
export default function CommentItem(props) {
  // 注意: 如果要使用 style 属性,为 JSX 语法创建的DOM元素,设置样式,不能像网页中那么写样式;而是要使用JS语法来写样式
  // 在 写 style 样式的时候,外层的 { } 表示 要写JS代码了,内层的 { } 表示 用一个JS对象表示样式
  // 注意: 在 style 的样式规则中,如果 属性值的单位是 px, 则 px 可以省略,直接写一个 数值 即可


  //#region 样式优化1
  /*  const boxStyle = { border: '1px solid #ccc', margin: '10px 0', paddingLeft: 15 }
   const titleStyle = { fontSize: 16, color: "purple" }
   const bodyStyle = { fontSize: 14, color: "red" } */
  //#endregion


  //#region 样式优化2 把 样式对象,封装到唯一的一个对象中
  /* const inlineStyles = {
    boxStyle: { border: '1px solid #ccc', margin: '10px 0', paddingLeft: 15 },
    titleStyle: { fontSize: 16, color: "purple" },
    bodyStyle: { fontSize: 14, color: "red" }
  } */
  //#endregion


  /* return 

评论人:{props.user}

评论内容:{props.content}

*/ // 注意: 当你怀念 vue 中 scoped 指令的时候,要时刻知道 , react 中并没有指令的概念 return

评论人:{props.user}

评论内容:{props.content}

}

cmtItemStyles.js

// 导入一个 样式的对象
export default {
  boxStyle: { border: '1px solid #ccc', margin: '10px 0', paddingLeft: 15 },
  titleStyle: { fontSize: 16, color: "purple" },
  bodyStyle: { fontSize: 14, color: "red" }
}

你可能感兴趣的:(前端工程师)