03-React-Component Basic

类组件

最基本的类组件

可以使用 rcc ( in vscode ) 快速构建

1
2
3
4
5
6
7
8
9
10

import React from 'react'

class App extends React.Component {
render() {
return <div><h1>Hello App from React.Component </h1></div>
}
}

export default App

引入创建的类组件后, 通过 ReactDom 进行渲染

1
2
3
4
// 这里的 App 是别名, 不一定与目标文件导出对象的名称一致
import APP from './01-base/01-1-class组件初识.js'

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

Tips: React 18 版本不在推荐使用 ReactDOM.render() 的渲染方式

const container = document.getElementById('root');
const root = createRoot(container);
root.render(<APP />);

根据属性构造简单的动态类组件

1
2
3
4
5
6
7
8
9
10
11
12
class App extends React.Component {
render() {
return <div><h1>Hello App from React.Component @ {this.props.name} </h1></div>
}
}

// .render 直接实例化, 渲染时直接传递该对象即可
const app = new App({
name: 'ComingPro'
}).render()

export default app

此时已经实例化啦, 因此直接渲染目标对象即可

const container = document.getElementById('root');
const root = createRoot(container);
root.render(App);

函数式组件

无状态组件, 十分简单, 但自身能承担的功能也有限

  • 组件中 thisundefined,是因为经过 babel 翻译开起严格模式,从而禁止了自定义 this 指向 window

基本的函数式组建

1
2
3
4
5
6
7
8
9
10
function App() {
return (
<div>
<h1> 02-函数式组件.js </h1>
<h2> Hello ComingPro </h2>
<h2> You are the Best </h2>
</div>
)
}
ReactDOM.render(<App/>, document.getElementById('test'))

组件的嵌套

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import React, {Component} from 'react'

class Navbar extends Component {
render() {
return(
<h2>Navbar</h2>
)
}
}

function Swiper() {
return (
<h2>Swiper</h2>
)
}

const Tabbar = () => <h2>Tabbar</h2>

export default class App extends Component {
render() {
return (
<div>
<h1>03-组件的嵌套.js</h1>
<Navbar></Navbar>
<Swiper></Swiper>
<Tabbar></Tabbar>
</div>
)
}
}

组件的样式

外部 CSS 文件

import './css/01-index.css' // webpack 的支持

导入外部 CSS 文件, 整合的 webpack 会将其转为内部 style 从而将样式应用到全局

  • 针对标签类型的样式就有可能出现样式冲突问题
  • 针对选择器定义的样式则可能出现重名问题
  • 因此不推荐使用

行内样式

推荐使用行内样式, 方便组件复用

  • 如果
  • 推荐每一个组件建立一个文件夹,文件夹中 index.js 写组件设计代码;index.css 写组件样式代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import React, {Component} from 'react'

export default class App extends Component {
render() {
var username = "ComingPro"
var style1 = {
background: "yellow"
}
return (
<div>
<ul>
<li>10 + 20 = {10 + 20}</li>
<li> Username: {username}</li>
<li> 10 &gt; 20 : {10 > 20 ? "Yes" : "No"}</li>
<li> 10 &lt; 20 : {10 < 20 ? "Yes" : "No"}</li>
</ul>
<div style={style1}> 推荐使用这两种行内样式, 方便组件的管理 </div>
<div style={{backgroundColor: "blue", color: "white", fontSize: "22px", padding: "8px"}}> 命名变为驼峰 </div>
</div>
)
}
}

CSS 模块化

针对不想全局应用的非标签式样式可以使用 CSS 模块化解决:

  1. css 文件的后缀为 .module.css
  2. 引入文件起一个别名: import style from ./index.module.css
  3. 使用 style 进行样式赋值:
1
2
3
4
5
6
7
8
9
import style from './Child.module.css'

export default function Child() {
return (
<div>
<h1 className={style['red-line']}> Child </h1>
</div>
)
}

sass

  1. npm i sass
  2. 将后缀名改为 *.scss
  3. 应用 sass 语法

TODO: 更为详细的 sass 语法

1
2
3
4
5
6
7
$width: 300px;
ul {
.item {
width: $width;
backgroud-color: red;
}
}

组件的事件绑定

所有事件命名都是 on + 事件的驼峰命名onClick, onMouseOver

  • React 事件绑定原理: React 并没有真正的绑定事件到每一个具体的标签上,而是采用事件代理的模式,在根标签上进行事件的监听(冒泡)

阻止冒泡

通过 event 对象阻止冒泡, 处理函数中的 event 参数也是 react 模拟的, 但是仍然存在阻止冒泡的相关接口: event.stopPropagatoin, event.preventDefault

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
export default class App extends Component {
render() {
return (
<div>
<input type="text" ></input>
<button onClick={ this.handleClick }> ADD </button>
</div>
)
}
handleClick = (event) => {
console.log(event)
console.log("`event.target` 获取事件源")
console.log(event.target)
}
}

四种事件绑定方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
export default class App extends Component {
render() {
return (
<div>
<input type="text" ></input>
{/* 1. 行内形式 */}
<button onClick={ () => {
console.log("click1")
} }> ADD </button>
{/* 2. 原生的 js 函数形式: 存在 this 指向问题 */}
<button onClick={ this.handleClick2 }> ADD2 </button>
{/* 2. 原生的 js 函数形式: 使用 bind 解决 this 指向问题 */}
<button onClick={ this.handleClick2.bind(this) }> ADD2-bind </button>
{/* 3. 隐式匿名函数调用 */}
<button onClick={ this.handleClick3 }> ADD3 </button>
{/* 4. 显式匿名函数调用 */}
<button onClick={ () => { this.handleClick4("ComingPro") } }> ADD4 </button>
</div>
)
}
}
  1. 行内形式: 没有 this 指向问题, 逻辑简单的时候推荐使用
  2. 原生的 js 函数形式: 存在 this 指向问题
    • React 通过事件代理实现事件的响应, 因此并非实例本身调用的这个函数, 所以丢失了 this;
    • 能够通过 bind 解决, 但是仍旧不推荐使用

      .call(this) 会改变 this 指向但是会自动执行函数
      .apply(this) 和 call 类似改变 this 指向的同时会执行函数
      .bind(this) 仅仅改变 this

  3. 隐式匿名函数调用:不存在 this 指向问题, 箭头函数不关心谁调用的, 永远保持与外部作用域一致; 不需要传递参数时推荐使用
  4. 显式匿名函数调用:不存在 this 指向问题, 方便了参数的传递, 因此最为推荐使用

组件二次封装

针对组件中有固有属性时且较多时,我们可以自定义一个一般组件实现封装

如 NavLink 中 className 属性固定,因此可以进行二次封装

<NavLink className="list-group-item" to="/about">About</NavLink>

二次封装我们向上要符合用户的编程习惯,即使用标签体传递表明名称,属性依旧通过 props 传递;向下要对接好初始组件

<MyNavLink to="/about">About</MyNavLink>
<MyNavLink to="/home">Home</MyNavLink>

方便向下对接的是,标签体中的内容将使用 this.props.children 接收,因此可以直接传递给 NavLink 的child属性,完成二次封装的优秀对接

1
2
3
4
5
render() {
return (
<NavLink className="list-group-item" {...this.props} />
)
}