一.引子
一个优秀的程序员不会局限于一种语言
一个优秀的前端攻城狮不会局限于一种框架
何况它还那么的强大
一开始还是我是拒绝学习的 直到我司来了一位10年开发经验的大佬…向大佬学习!!!
二.什么是react
React是Facebook开发的一款JS库。React一般被用来作为MVC中的V层,它不依赖其他任何的库,因此开发中,可以与任何其他的库集成使用,包括Jquery、Backbone等。它可以在浏览器端运行,也可以通过nodejs在服务端渲染。React的思想非常独特,性能出众,可以写出重复代码少,逻辑清晰的前端代码。React的语法是jsx,通过使用这种语法,可以在react代码中直接混合使用js和html来编写代码,这样代码的逻辑就非常清晰,当然也意味着,需要将jsx代码编译成普通的javascript代码,才能在浏览器中运行,这个过程根据实际项目情况,可以选择多种不同的思路,或者在服务器端通过webpack进行编译。
React是MVC框架使用数据驱动视图,当数据发生变化视图发生变化。若只是修改视图,数据并不会改变。
Vue是MVVM框架。双向数据绑定,当数据发生变化视图发生变化。视图发生变化,数据也会随之改变
React官网:https://zh-hans.reactjs.org/
三.React脚手架
- 什么是react脚手架:
FaceBook官方发布了一个无需配置,用于快速构建开发环境的脚手架工(create-react-app) 如何使用:
使用npm自带的npx安装(官方推荐,本地就不会下载create-react-app,随用随创)
1
2
3npx create-react-app project-nameprojectName
cd project-name
npm run start全局安装脚手架创建
1
2
3
4
5
6<!-- 全局安装脚手架 -->
npm install -g create-react-app
<!-- 在指定的目录下创建你的新项目 -->
create-react-app project-name
cd project-name
npm run start注意:
React帮我们将艮长的配置文件全部隐藏了。只能通过eject命令将所有的配置文件暴露出来,但请牢记这个操作是不可逆的。1
npm run eject
四.React中的虚拟Dom是什么?
React中是如何通过ReactDOM.render()方法生成Dom节点的呢?
什么是JSX?
JSX是React的核心组成部分,它使用XML标记的方式去直接声明界面,界面组件之间可以互相嵌套。可以理解为在JS中编写与XML类似的语言,一种定义带属性树结构(DOM结构)的语法,它的目的不是要在浏览器或者引擎中实现,它的目的是通过各种编译器将这些标记编译成标准的JS语言。
你可以将HTML 语言直接写在 JavaScript 语言之中,不加任何引号,这就是 JSX 的语法,它允许 HTML 与 JavaScript 的混写1
2
3
4
5
6
7render() {
return (
<div>
<h1 className="text-style" >Hello, world!</h1>
</div>
);
}JSX将原本HTML与javaScript混写的代码通过 babel-preset-react-app 插件将代码转换成React.createElement(elementn,props,[…children])可以创建的方式
他是如何转换的?
看上面那部分代码,我们将它放在babeljs上查看下1
2
3
4
5<!-- 转换后的结果 -->
React.createElement("div", null, React.createElement("h1", {
className: "text-style",
name: 1212
}, "Hello, world!"));React.createElement(elementn,props,[…children])解析
element: 要创建的HTML元素 也就是我们常说的HTML标签 (这个是必须的,还可以为函数组件/类组件)
props: 是在这个标签上的所有的属性 比如上面的列子中含有的 className 和 name(这是必须的,没有参数则为Null)
children: 这个参数是标签中是否含有文本或标签什么都没有:'' 就是空 仅含有文本:呈现方式为 '你好世界'; 仅含有标签:呈现方式为{className:'border-red'} 含有文本+标签(多个):呈现方式为['你好世界',{className:'border-red'},{···}]
React何时创建的虚拟DOM
执行React.createElement(elementn,props,[…children])就会创建虚拟DOM对象了。我们来看看是什么样子的
可以看出type属性就是我们的第一个参数
props就是我们attr
children就是我们嵌套的内容了React.render(JSX,document.getElementById(‘root’))
这一切的努力都是为了回到原点。使用React.render()方法将虚拟DOM对象挂在节点上什么是纯函数
- 相同的输入 一定会返回相同的输出
- 永远 不会修改传入的参数值
\
五.JSX语法细节
JSX如何解析html文本
使用dangerouslySetInnerHTML 属性 但这是危险的1
<span dangerouslySetInnerHTML={{__html:item}}></span>
JSX中添加CSS属性class 要使用className 属性
1
<input type="text" className="input" />
JSXlabel的坑 要使用htmlFor 关联
1
2<label htmlFor="inse">输入内容</label>
<input id="inse" className="input"/>
六.知识点
- setState:React中修改数据不能直接修改 要使用setState方法 这个分两种方式
setState修改数据是异步的
第一种方法
1
2
3this.setState = {
xxx: 123
}- 第二种方法
1
2
3
4
5
6
7
8
9this.setState((prevState)=>{
return {
status:true
}
},()=>{
// 在这里可以获取到this.setState修改后的数据
})
this.setState(异步获取 可能获取不到修改后的值)
- 第二种方法
事件绑定
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
31
32
33
34
35
36
37
38constructor() {
super()
this.state = {
inputValue:'XXX',
}
this.handleAdd = this.handleAdd.bind(this)
}
// 第一种
handleAdd(){
console.log(this)
// 没有this.handleAdd = this.handleAdd.bind(this) 会获取不到this
}
// 第二种 推荐写法
chnageInputValue = (e) => {
console.log(e);
this.setState({
inputValue:e.target.value
})
}
handleKeyUp = (e) => {
if (e.keyCode === 13) {
this.handleClickAdd()
}
}
render() {
return (
<>
<input type="text" onChange={this.chnageInputValue} value={this.state.inputValue} /> <button onClick={this.handleAdd}>{this.props.btnText}</button>
// 添加参数
<input type="text" onChange={()=>{this.chnageInputValueCanShu(传递的参数)}} value={this.state.inputValue} />
// 绑定键盘事件
<input type="text" onChange={this.chnageInputValue} onKeyUp={this.handleKeyUp}
value={this.state.inputValue} />
</>
)
}Ref
引入 createRef1
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
31
32
33import React, { Component, createRef} from 'react'
constructor() {
super()
this.state = {
inputValue:'XXX',
}
}
chnageInputValue = (e) => {
console.log(e);
this.setState({
inputValue:e.target.value
})
}
handleClickAdd = () => {
this.props.addTodo(this.state.inputValue)
this.setState({
inputValue:''
}, () => {
this.inputRef.current.focus();
})
}
inputRef = createRef() // 点击添加按钮 鼠标光标自动回到input上
render() {
return (
<>
<input type="text" onChange={this.chnageInputValue}
value={this.state.inputValue} ref={this.inputRef}/>
<button onClick={this.handleClickAdd}>{this.props.btnText}</button>
</>
)
}使用prop-types校验参数类型: 子组件中效验父组件传递的参数是否符合规范
安装 prop-types
1
npm install -S prop-types
如何使用 PropTypes
1
2
3
4
5
6
7
8
9
10import PropTypes from 'prop-types'
static propTypes = {
todoList: PropTypes.arrayOf(PropTypes.object)// 验证参数是数组 里面的每一项是对象
todoList:PropTypes.arrayOf(PropTypes.shape({
id:PropTypes.number.isRequired,
title:PropTypes.string.isRequired,
isCompleted:PropTypes.bool.isRequired,
})).isRequired// // 验证参数是数组 每一项是对象 对象的每一个参数类型
}1
2
3
4
5
6
7//组件名.propTypes
TodoItem.propTypes = {
// 属性值
item: PropTypes.oneOfType([PropTypes.string,PropTypes.number]),// 可以使字符串 也可以是number
itemDelete: PropTypes.func,
index:PropTypes.number
}defaultProps的使用
1
2
3
4
5
6TodoItem.propTypes = {
test:PropTypes.string.isRequired //isRequired:父组件必须传递给我
}
TodoItem.defaultProps = {
test:'Hello World'
}
react 父组件传值给子组件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23// 父组件
<ul>
{
this.state.list.map((item, index) => {
// 在子组件上定义自定义属性 并传递参数
return <TodoItem item={item} index={index} key={index} />
})
}
</ul>
// 子组件 通过this.props.属性取值
<li >
<span dangerouslySetInnerHTML={{__html:this.props.item}}></span>
<button onClick={() => { this.handleItemDelete(this.props.index) }}>删除</button>
</li>
// 子组件也可以使用对象结构的方式 获取
render() {
let { item } = this.props
return (
<li >
<span dangerouslySetInnerHTML={{__html:item}}></span>
</li>
)
}react 子组件如何修改父组件中state中的值
1
2
3
4
5
6
7
8
9
10
11
12
13// 在父组件中的子组件上添加属性 传递方法 itemDelete方法
<ul>
{
this.state.list.map((item, index) => {
return <TodoItem itemDelete={this.handleItemDelete} item={item} index={index} key={index} />
})
}
</ul>
// 在子组件中使用this.props.itemDelete调用父组件的方法
handleItemDelete = () => {
this.props.itemDelete(this.props.index);
}React中的生命周期函数:指某一时刻组件会自动调用执行函数
- componentWillMount:在组件即将挂载到页面的时刻自动调用
- componentDidMount: 在组件挂载到页面的时刻自动调用
shouldComponentUpdate(nextProps,nextState):在组件更新之前或自动调用 需要返回boolean值 return true 更新组件 false 不更新组件
- nextProps:更新后(下一次)的props的值
- nextState:下一次state的值
使用场景:在父组件中输入框中更新数据 会使子组件render自动更新 设置false 强制不更新 只在props的值变更影响子组件渲染的时候更新1
2
3
4
5
6
7
8shouldComponentUpdate(nextProps, nextState) {
// 下一次的item的值与当前的item的是否相等 不相等更新子组件 相等不更新
if (nextProps.item !== this.props.item) {
return true
} else {
return false
}
}
componentWillUpdate:shouldComponentUpdate执行后执行 在render执行前执行
- componentDidUpdate:组件更新完成之后 执行
- componentWillReceiveProps:当一个组件从父组件接收参数 只要父组件render执行了 该函数就会执行
若当前子组件第一次现在在父组件中 改钩子不会执行 若当前子组件已经在父组件中 该钩子会执行
- componentWillUnmount:组件销毁前的钩子
React中如何使用动画
使用css写动画
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
31jsx
handleToggle = () => {
this.setState(() => ({
show:!this.state.show
}))
}
render() {
return (
<>
<div className={this.state.show ? 'show' : 'hied'}>Hello Wrold</div>
<button onClick={this.handleToggle}>toggle</button>
</>
)
}
css
.show{
animation: show-item 2s ease-in forwards;
}
.hied{
animation: hide-item 2s ease-in forwards;
}
@keyframes show-item{
0% { color: pink; opacity:0; }
50% { color: blue; opacity: .5; }
100% { color: red; opacity: 1; }
}
@keyframes hide-item{
0% { color: pink; opacity: 1; }
50% { color: blue; opacity: .5; }
100% { color: red; opacity: 0; }
}使用react-transition-group 做动画
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
621. 安装 react-transition-group
2. npm install react-transition-group
3. 使用
import { CSSTransition,TransitionGroup } from "react-transition-group";// 引入组件
// CSSTransition 只能对于单个元素 单个组件
// TransitionGroup 多个组件 用TransitionGroup 包裹多个CSSTransition
import './index.css'// 引入样式
<TransitionGroup>
this.state.list.map((item,index)=>{
return (
<CSSTransition
key={index}
// true 执行入场动画 false执行出场动画
in={this.state.show}
// 设置动画执行时间
timeout={2000}
// 动画库的名字 自定义
classNames="fade"
// 动画结束后是否显示Dom元素
unmountOnExit
// 页面加载出来 是否加载动画
appear={true}
// enter”或“ appear”类后立即触发的回调。 入场动画开始之前 执行结束
onEnter={() => { console.log(1111); }}
// enter-active”或“ appear-active”类后立即触发的回调。 /* 入场动画开始后 入场动画执行中 还没有结束 时调用
onEntering={() => { console.log(22222); }}
// 入场动画完全 结束后调用
onEntered={() => { console.log(3333); }}
// 出场动画开始前调用
onExit={() => { console.log(4444); }}
// 出场动画开始后 调用 动画还在执行中
onExiting={() => { console.log(55555); }}
// 出场动画执行完后 调用
onExited={() => { console.log(666); }}
>
<div >Hello Wrold</div>
</CSSTransition>
)
})
</TransitionGroup>
css
/* 入场动画开始前 */
/* fade-appear 页面加载就会执行 配合appear属性 */
.fade-enter, .fade-appear{ opacity: 0; color: blue; }
/* 入场动画开始后 入场动画执行中 还没有结束 */
/* fade-appear-active 页面加载就会执行 配合appear属性 */
.fade-enter-active, .fade-appear-active{
opacity: 1; color: blue; transition:all 2s ease-in; }
/* 入场动画结束后 */
/* fade-appear-done 页面加载就会执行 配合appear属性 */
.fade-enter-done, .fade-appear-done{ opacity: 1; color: blue; }
/* 出场动画前 */
.fade-exit{ opacity: 1; }
/* 出场动画后 出场动画执行中 */
.fade-exit-active{ opacity: 0; transition:all 2s ease-in; }
/* 出场动画后 */
.fade-exit-done{ opacity: 0; }
七. UI组件和容器组件&无状态组件
- 什么是UI:只负责页面渲染 不负责页面操作
- 什么书容器组件:只负责页面逻辑操作,不负责UI渲染
- 什么是无状态组件:当组件中只有render函数的时候 则可以更改此组件为无状态组件
安装styled-components
1
npm i styled-components
使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15//在组件文件夹中新建style.js === style.css
// 添加全局样式文件
import { createGlobalStyle } from 'styled-components'
export const GlobalStyle = createGlobalStyle`
body{
background-color:red;
}
`
// 组件中
import { GlobalStyle } from './style.js';
ReactDOM.render(
<>
<GlobalStyle />
<App > </App>
</> , document.getElementById('root'));
九.添加PC端统一样式文件 reset.css
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
31
32
33
34
35
36
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
display: block;
}
body { line-height: 1; }
ol, ul { list-style: none; }
blockquote, q { quotes: none; }
blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
};
注:
- 命令式开发:即jQuery直接操作DOM
- 声明式开发:react 根据数据显示页面
- react中的数据是单项数据流
- state,prpos 的值发生改变 render函数就会重新渲染一次
神头鬼脸·魔法写法
1
2
3
4
5
6
7
8
9<List
style={{width:'300px'}}
bordered
dataSource={data}
renderItem={(item,index) => (
<List.Item onClick={(index) => { handleDeleteItem(index) }} >{item}</List.Item> // 获取不到index的值 因为click 的第一个参数就当前Dom 对象
<List.Item onClick={()=>handleDeleteItem(index)} >{item}</List.Item> // 可以获取到
) }
/>