ZONO's World

welcome to my world

fiber

先理解并行、串行;进程、线程;
fiber就是一个任务片段(相当于java的Runable,不懂)

组件化开发

组件化开发的优势

  • 利于团队协作开发
  • 利于组件复用
  • 利于SPA单页面应用开发
  • ……

主流组件的对比

Vue中的组件化开发:

http://fivedodo.com/upload/html/vuejs3/guide/migration/functional-components.html

  1. 全局组件和局部组件
  2. 函数组件(functional)和类组件(Vue3不具备functional函数组件」)

React中的组件化开发:

没有明确全局和局部的概念(可以理解为都是局部组件,不过可以把组件注册到React上,这样每个组件中只要导入React即可使用)

  1. 函数组件
  2. 类组件
  3. Hooks组件:在函数组件中使用React Hooks函数

React函数组件

创建

两个情况都是创建了一个函数组件:

  1. 在SRC目录中,创建一个 xxx.jsx 的文件,就创建一个组件了;
  2. 我们在此文件中,创建一个函数,让函数返回JSX视图(或者JSX元素、virtualDOM虚拟DOM对象)

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// views/FunctionComponent.jsx
const FunctionComponent = function FunctionComponent() {
return <div>
我是函数组件
</div>;
};
export default FunctionComponent;

// index.jsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import FunctionComponent from '@/views/FunctionComponent';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<>
<FunctionComponent/>
</>
);

调用

在文件中,可以基于ES6Module规范导入创建的组件(导入时可以忽略.jsx后缀名),然后像写标签一样调用。

1
2
3
4
5
//两种调用方式
<Component/> //单闭合调用

<Component> ... </Component> //双闭合调用
//双闭合调用内可以放子节点,在传递函数的props时,会有一个children属性来存储这些子节点。

传递属性的规则:

  • 调用组件时,可以给被调用的组件设置(传递)各种各样的属性,例如:

    <DemoOne title="我是标题" x={10} data={[100, 200]} className="box" style={{ fontSize: '20px' }} />

  • 若设置的属性值不是字符串格式,则需要基于{}语法进行嵌套。

命名

组件名,一般都采用PascalCase(大驼峰命名法)

渲染过程

由上两章可知组件的渲染过程是

  1. 基于babel-preset-react-app把调用的组件转换为createElement格式
1
2
3
4
5
6
7
8
9
React.createElement(DemoOne, {
title: "zono",
x: 10,
data: [100, 200],
className: "box",
style: {
fontSize: "20px",
},
});
  1. 执行createElement方法创建一个virtualDOM对象
1
2
3
4
5
6
7
{
$$typeof: Symbol(react.element),
key: null,
props: {title: '我是标题', x: 10, data: 数组, className: 'box', style: {fontSize: '20px'}}, //如果有子节点「双闭合调用」,则也包含children!!
ref: null,
type: DemoOne
}
  1. 基于root.rendervirtualDOM变为真实的DOM

type值不再是一个字符串,而是一个函数了,此时:

  • 函数执行→DemoOne()
  • virtualDOM中的props,作为实参传递给函数 DemoOne()DemoOne(props)
  • 接收函数执行的返回结果(也就是当前组件的virtualDOM对象)
  • 最后由render函数把组件返回的虚拟DOM变为真实DOM,插入到id=root的容器中!!

Props的处理

  • 调用组件,传递进来的属性(Props)是“只读”的,原理是:props对象被冻结了Object.freeze()(见下文解释)
1
2
3
4
//测试
Object.isFrozen(props) //output:true
$ 获取:props.xxx
$ 修改:props.xxx=xxx //output:=>报错

回顾js知识点:关于对象的规则设置(了解可跳过)

阮老师单推人😘

标准库 - 属性描述对象 - 《阮一峰 JavaScript 教程》 - 书栈网 · BookStack

控制对象状态的一节,讲述了以下内容:

  • 扩展(弱):

    Object.*preventExtensions*():使得一个对象无法再添加新的属性
    Object.isExtensible(obj):检测是否可扩展(返回bool值,下面都是)

  • 密封(较强)
    Object.seal(obj):密封对象,使得一个对象既无法添加新属性,也无法删除旧属性。
    Object.isSealed(obj):检测是否被密封

    • 被密封的对象:可以修改成员的值,但也不能删、不能新增、不能劫持Object.defineProperty
  • 冻结(强)
    Object.freeze(obj):冻结对象,对象实际上变成了常量。
    Object.isFrozen(obj) 检测是否被冻结

    • 被冻结的对象:不能修改成员值、不能新增成员、不能删除现有成员、不能给成员做劫持Object.defineProperty
  • 局限性:可以通过改变原型对象,来为对象增加属性。(详见阮一峰教程)

  • 劫持:(也在”属性描述对象’’同一章中)

    Object.defineProperty()方法允许通过属性描述对象,定义或修改一个属性,然后返回修改后的对象()

👏tips:

  • 被设置不可扩展的对象:除了不能新增成员、其余的操作都可以处理!!
  • 被密封的对象,也是不可扩展的!!
  • 同理,被冻结的对象,即是不可扩展的,也是密封的!!
  • 通过三个方法的互相调用可以部分解封,但如果要加入新的属性那就只能创建一个新对象了

💬Ps:

  • 越底层越要看js基础,看来八股并不是没用。
  • 是不是这部分我应该单独开一章🤦‍♂️🤦‍♀️

然后让我们举一个传Props的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// src\index.jsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import DemoOne from '@/views/DemoOne';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<>
<DemoOne title="可以看见成功传入了值" x={10}>
<span>哈哈哈</span>
<span>呵呵呵</span>
</DemoOne>

<DemoOne title="尝试传入不同的值,实现了组件的复用,注意要用<></>包起来" />
</>
);
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
// src\views\DemoOne.jsx
import PropTypes from 'prop-types';

const DemoOne = function DemoOne(props) {
let { title } = props;//从父组件取值
let x = props.x;
x = 1000;

return <div className="demo-box">
<h2 className="title">{title}</h2>
<span>{x}</span>
</div>;
};
/* 通过把函数当做对象,设置静态的私有属性方法,来给其设置属性的校验规则 */
DemoOne.defaultProps = {
x: 0
};
DemoOne.propTypes = {
//组件属性:校验格式
title: PropTypes.string.isRequired,// 类型是字符串、必传
x: PropTypes.number,// 类型是数字
y: PropTypes.oneOfType([
PropTypes.number,
PropTypes.bool,
])// 多种校验规则中的一个
};

export default DemoOne;

属性的使用逻辑

  • 父组件index.jsx调用子组件DemoOne.jsx的时候,可以基于属性,把不同的信息传递给子组件;
  • 子组件接收相应的属性值,呈现出不同的效果,让组件的复用性更强!!

对于传递进来的属性规则校验

  • 原理:通过把函数当做对象,设置静态的私有属性方法,来给其设置属性的校验规则
  • 可以设置默认值(见上面代码):
1
2
3
4
5
6
7
/* 函数组件.defaultProps = {
x: 0,
......
};*/
DemoOne.defaultProps = {
x: 0
};
  • 设置其它规则,例如:数据值格式、是否必传… (依赖于官方的一个插件:prop-types

  • React 校验库 prop-types 安装与详细使用 - 掘金 (juejin.cn)

  • https://github.com/facebook/prop-types

    这里就是用这个插件来进行类型检验,让原本动态的js能像c++、java这样的静态语言可以不用担心类型转换出现的问题。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    import PropTypes from 'prop-types';
    函数组件.propTypes = {
    title: PropTypes.string.isRequired,// 类型是字符串、必传
    x: PropTypes.number,// 类型是数字
    y: PropTypes.oneOfType([
    PropTypes.number,
    PropTypes.bool,
    ])// 多种校验规则中的一个
    };

    传递进来的属性,首先会经历规则的校验,不管校验成功还是失败,最后都会把属性给形参props,只不过如果不符合设定的规则,控制台会抛出警告错误{不影响属性值的获取}!!

    🤔Tips:

    • 使用 TypeScript(TS)的话,通常不需要使用 prop-types 库。

    • 之前写python也有类似的库,看样子动态语言都得被静态束缚。

    (那python是不是强类型呢😅,与前端无关,可以忽略)

修改props的规则

  • 把props中的某个属性赋值给其他内容(例如:变量、状态…)
  • 我们不直接操作props.xxx=xxx,但是我们可以修改变量/状态值!!

待规划文字(可以先不读,我复习时会把内容规整进入正文)

  • 在JSX元素渲染的时候,如果type是函数,则把函数执行!
    • 会把解析出来的props「含children」,传递给函数
      • 单闭合和双闭合调用的区别「插槽」

      • React.Children

      • props是只读的「被冻结」

    • props的规则设置
      • 设置默认值

      • 设置属性规则「基于prop-types插件」

    • 属性和插槽都可以让组件具备更强的复用性
1
2
3
4
5
6
7
8
9
10
// index.jsx
root.render(
<>
<FunctionComponent x={10} title="你好世界" arr={[10, 20, 30]}>
<div className='slot-box'>
我是插槽信息
</div>
</FunctionComponent>
</>
);
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
// views/FunctionComponent.jsx
import React from "react";
import PropTypes from 'prop-types';
const FunctionComponent = function FunctionComponent(props) {
/* 被冻结:不能修改/新增/删除/劫持...「可以赋值给其它状态再进行修改」 */
// console.log(Object.isFrozen(props)); //->true

/* 对props.children进行操作:count/forEach/map/toArray... */
// console.log(React.Children);

return <div>
我是函数组件
</div>;
};
// 赋值属性默认值
FunctionComponent.defaultProps = {
x: 0,
title: '',
arr: [],
num: 0
};
// 设置属性规则
FunctionComponent.propTypes = {
x: PropTypes.number,
title: PropTypes.string.isRequired,
arr: PropTypes.array,
num: PropTypes.any
};
export default FunctionComponent;

巨量注释笔记😊因为自己写的处理jsx还是不够完善,后面的代码还是用会React官方的方法。所以可以先跳过本章

Part 1 jsx→viscalDOM

这里可以参看我的开源库

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
/*
@Date :2023/12/04 15:56:37
@Author :zono
@Description:一个简单的JSX处理器
*/

/*createElement函数:用于构建虚拟DOM
----------------------------------------
1.参考react的createElement函数:
React.createElement(ele,props,...children)
- ele:元素标签名\组件名
- props:元素的属性集合(对象)(无属性,则此值为null)
- children:当前元素的子节点,第三个及以后的参数都是
- 返回虚拟DOM,可以看下文虚拟DOM结构
----------------------------------------
2. 参考react输出的虚拟DOM:
virtualDOM = {
$$typeof: Symbol(react.element), //react元素的标识
ref: null,
key: null,
type: 标签名「或组件」, //h1、h2...
// 存储了元素的相关属性 && 子节点信息
props: {
..., // 元素的相关属性,如:className、style,
children // 子节点信息(没有子节点则没有这个属性、属性值可能是一个值、也可能是一个数组)
}//必定存在,至少是个空对象
}
----------------------------------------
*/
export function createElement(ele, props, ...children) {
let virtualDOM = {
$$typeof: Symbol.for("react.element"),
ref: null,
key: null,
type: null,
props: {},
};
let len = children.length;

// 1.处理type
virtualDOM.type = ele;

// 2.处理props
if (props) {
virtualDOM.props = {
...props,
children,
};
}

// 3.处理children
if (len === 1) virtualDOM.props.children = children[0];
else if (len > 1) virtualDOM.props.children = children;
else virtualDOM.props.children = null;

return virtualDOM;
}

对应的index.js,这里我们用03的对象。

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
// import React from "react";
import ReactDOM from "react-dom/client";
import "./index.less";
import { createElement, } from './myJSXhandle';

const root = ReactDOM.createRoot(document.getElementById("root"));

let styObj = {
color: 'red',
fontSize: '16px'
};
let x = 10;
let y = 20;

//把react.createElement方法改成自己的方法
//把react.Fragment改为直接填写
//可以见到react已经没有被调用了
let jsxObj = createElement(
// React.Fragment,
"div",
null,
createElement(
"h2",
{className: "title",
style: styObj,},
"zono"
),
createElement(
"div",
{className: "box",},
"牛",
createElement("span", null, x),
createElement("span", null, y)
)
);

console.log(jsxObj);

root.render(jsxObj);

如此完成createElement的覆盖

Part 2 viscalDOM →真实DOM

1
2
3
4
5
6
7
/*part2.render函数:把虚拟DOM变为真实DOM(按v16),构建思路如下
\----------------------------------------
1.首先根据ReactDOM.render方法构建大体结构
ReactDOM.render(virtualDOM, container)
\----------------------------------------
2.由于函数涉及到递归对象取值,所以先定义一个对象迭代器,见下文
*/

定义一个对象迭代器:封装一个对象迭代的方法

  • 基于传统的for/in循环,会存在一些弊端(性能较差(既可以迭代私有的,也可以迭代公有的);只能迭代“可枚举、非Symbol类型的”属性…)

    一般而言,内置属性都是不可枚举的(枚举:可以被列举、例如for/in、Object.keys等列举出来的);自定义属性都是可枚举的,我们可以通过Object.defineProperty方法定义枚举属性

  • 解决思路:获取对象所有的私有属性「私有的、不论是否可枚举、不论类型」
    • Object.getOwnPropertyNames(arr) -> 获取对象非Symbol类型的私有属性「无关是否可枚举」
    • Object.getOwnPropertySymbols(arr) -> 获取Symbol类型的私有属性
      获取所有的私有属性:
      let keys = Object.getOwnPropertyNames(arr).concat(Object.getOwnPropertySymbols(arr));
      可以基于ES6中的Reflect.ownKeys代替上述操作「弊端:不兼容IE」
      let keys = Reflect.ownKeys(arr);
1
2
3
4
5
6
7
let function iterator(obj) {
let arr = [];
for (let key in obj) {
arr.push([key, obj[key]]);
}
return arr;
}

定义一个创建真实DOM的函数

这里先不对ref(获取真实DOM)和key(用于优化)进行处理

分析后函数任务就是:先根据type创建一个标签,然后把props中的属性设置给标签,最后把标签插入到指定的容器中。

于是接下来按步骤处理:

  • type:标签名
  • className
  • style
  • children
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
export function render(virtualDOM, container) {
//获取虚拟DOM的类型
let { type, props } = virtualDOM;
//1. type处理
if (typeof type === "string") {
let ele = document.createElement(type); //动态创建一个string标签
// 2.props处理:构建回调函数为标签设置相关的属性 & 子节点,然后传入之前写好的对象迭代器
each(props, (value, key) => {

/*TODO为元素设置属性(自定义、内置)有两种方式(待回看)
1. 元素.属性=属性值
原理:内置属性是设置在元素标签上的(可以通过元素对象获取到),
自定义属性是设置在元素对象上,在对象的堆内存空间中新增成员(不会设置到标签上)
获取:元素.属性
删除:delete 元素.属性

2. 元素.setAttribute(属性名,属性值)
原理:会直接写在元素的标签上
获取:元素.getAttribute(属性名)
删除:元素.removeAttribute(属性名)
二者不能混淆(排除内置属性的特殊性)
*/

// 2.1className的处理:value存储的是样式类名
if (key === "className") {
ele.className = value;
return;
}

// 2.2.style的处理:value存储的是样式对象
if (key === "style") {
each(value, (val, attr) => {
ele.style[attr] = val;
});
return;
}

// 2.3.子节点的处理:value存储的children属性值
if (key === "children") {
let children = value;
if (!Array.isArray(children)) children = [children];//如果不是数组,就包装成数组
children.forEach((child) => {
// 子节点是文本节点:直接插入即可
if (/^(string|number)$/.test(typeof child)) {
//.test():用于检测字符串是否匹配某个正则表达式,返回true/false
ele.appendChild(document.createTextNode(child));//创建文本节点,并插入到标签中
return;
}
// 子节点又是一个virtualDOM:递归处理
render(child, ele);
});
return;
}

ele.setAttribute(key, value);
});
// 把新增的标签,增加到指定容器中
container.appendChild(ele);
}
}

完整代码

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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
//src\jsxHandle.js
/*
@Date :2023/12/04 15:56:37
@Author :zono
@Description:一个简单的JSX处理器
*/

/*part1.createElement函数:用于构建虚拟DOM
----------------------------------------
1.参考react的createElement函数:
React.createElement(ele,props,...children)
- ele:元素标签名\组件名
- props:元素的属性集合(对象)(无属性,则此值为null)
- children:当前元素的子节点,第三个及以后的参数都是
- 返回虚拟DOM,可以看下文虚拟DOM结构
----------------------------------------
2. 参考react输出的虚拟DOM:
virtualDOM = {
$$typeof: Symbol(react.element), //react元素的标识
ref: null,
key: null,
type: 标签名「或组件」, //h1、h2...
// 存储了元素的相关属性 && 子节点信息
props: {
..., // 元素的相关属性,如:className、style,
children // 子节点信息(没有子节点则没有这个属性、属性值可能是一个值、也可能是一个数组)
}//必定存在,至少是个空对象
}
----------------------------------------
*/
export function createElement(ele, props, ...children) {
let virtualDOM = {
$$typeof: Symbol.for("react.element"),
ref: null,
key: null,
type: null,
props: {},
};
let len = children.length;

// 1.处理type
virtualDOM.type = ele;

// 2.处理props
if (props) {
virtualDOM.props = {
...props,
children,
};
}

// 3.处理children
if (len === 1) virtualDOM.props.children = children[0];
else if (len > 1) virtualDOM.props.children = children;
else virtualDOM.props.children = null;

return virtualDOM;
}

/*part2.render函数:把虚拟DOM变为真实DOM(按v16),构建思路如下
----------------------------------------
1.首先根据ReactDOM.render方法构建大体结构
ReactDOM.render(virtualDOM, container)
----------------------------------------
2.由于函数涉及到递归对象取值,所以先定义一个对象迭代器,见下文
*/

/*定义一个对象迭代器:封装一个对象迭代的方法
- 基于传统的for/in循环,会存在一些弊端「性能较差(既可以迭代私有的,也可以迭代公有的);只能迭代“可枚举、非Symbol类型的”属性...」
+ 解决思路:获取对象所有的私有属性「私有的、不论是否可枚举、不论类型」
+ Object.getOwnPropertyNames(arr) -> 获取对象非Symbol类型的私有属性「无关是否可枚举」
+ Object.getOwnPropertySymbols(arr) -> 获取Symbol类型的私有属性
获取所有的私有属性:
let keys = Object.getOwnPropertyNames(arr).concat(Object.getOwnPropertySymbols(arr));
可以基于ES6中的Reflect.ownKeys代替上述操作「弊端:不兼容IE」
let keys = Reflect.ownKeys(arr);
*/
const each = (obj, callback) => {
// 1.参数校验,如果不是对象或者是null,直接抛出异常
if (obj === null || typeof obj !== "object")
throw new TypeError("obj is not a object");
// 2.获取对象所有的私有属性「私有的、不论是否可枚举、不论类型」
let keys = Reflect.ownKeys(obj);
// 3.遍历所有的私有属性,每次迭代都执行回调函数
keys.forEach((key) => callback(obj[key],key));
};

/*定义一个创建真实DOM的函数
这里先不对ref(获取真实DOM)和key(用于优化)进行处理

分析后函数任务就是:
先根据type创建一个标签,然后把props中的属性设置给标签,最后把标签插入到指定的容器中。

于是接下来按步骤处理:
- type:标签名
props中的属性进行处理(粗略)
- className
- style
- children
*/
export function render(virtualDOM, container) {
//获取虚拟DOM的类型
let { type, props } = virtualDOM;
//1. type处理
if (typeof type === "string") {
let ele = document.createElement(type); //动态创建一个string标签
// 2.props处理:构建回调函数为标签设置相关的属性 & 子节点,然后传入之前写好的对象迭代器
each(props, (value, key) => {

/*TODO为元素设置属性(自定义、内置)有两种方式(待回看)
1. 元素.属性=属性值
原理:内置属性是设置在元素标签上的(可以通过元素对象获取到),
自定义属性是设置在元素对象上,在对象的堆内存空间中新增成员(不会设置到标签上)
获取:元素.属性
删除:delete 元素.属性

2. 元素.setAttribute(属性名,属性值)
原理:会直接写在元素的标签上
获取:元素.getAttribute(属性名)
删除:元素.removeAttribute(属性名)
二者不能混淆(排除内置属性的特殊性)
*/

// 2.1className的处理:value存储的是样式类名
if (key === "className") {
ele.className = value;
return;
}

// 2.2.style的处理:value存储的是样式对象
if (key === "style") {
each(value, (val, attr) => {
ele.style[attr] = val;
});
return;
}

// 2.3.子节点的处理:value存储的children属性值
if (key === "children") {
let children = value;
if (!Array.isArray(children)) children = [children];//如果不是数组,就包装成数组(对象方法少)
children.forEach((child) => {
// 子节点是文本节点:直接插入即可
if (/^(string|number)$/.test(typeof child)) {
//.test():用于检测字符串是否匹配某个正则表达式,返回true/false
ele.appendChild(document.createTextNode(child));//创建文本节点,并插入到标签中
return;
}
// 子节点又是一个virtualDOM:递归处理
render(child, ele);
});
return;
}

ele.setAttribute(key, value);
});
// 把新增的标签,增加到指定容器中
container.appendChild(ele);
}
}

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
//src\index.jsx
// import React from "react";
// import ReactDOM from "react-dom/client";
import "./index.less";
import { createElement,render } from './myJSXhandle';
// import { createElement, render } from './jsxHandle';

// const root = ReactDOM.createRoot(document.getElementById("root"));

let styObj = {
color: 'red',
fontSize: '16px'
};
let x = 10;
let y = 20;

//把react.createElement方法改成自己的方法
//把react.Fragment改为直接填写
//可以见到react已经没有被调用了
let jsxObj = createElement(
// React.Fragment,
"div",
null,
createElement(
"h2",
{className: "title",
style: styObj},
"zono"
),
createElement(
"div",
{className: "box"},
// "牛",
createElement("span", null, x),
createElement("span", null, y)
)
);

console.log(jsxObj);

//有了render方法,就不需要ReactDOM.render了
// root.render(jsxObj);
render(jsxObj,document.getElementById("root"));

踩坑

image-20231204193150139

没想到运行时遇见这个,排错后发现是src\jsxHandle.js文件中,each函数的callback函数形参写反了

image-20231204193911032

总结

  • 底层真的用了很多js基础的内容
  • 数据结构的运用很重要

因为自己写的处理jsx还是不够完善,后面的代码还是用会React官方的方法

1. JSX基础知识

大小驼峰:PasalCase、CamelCase、kabab-case、xxx_xxx

JSX(javascript and xml\html): 把JS与HTML标签混合在了一起(不是字符串拼接,❌element = “

123
“)

🎈Tips: vscode如何支持JSX语法(格式化、快捷提示…)

  • 后缀名设置为jsx
  • webpack打包的规则中,也是会对.jsx这种文件,按照JS的方式进行处理的

1.1.语法特性

JSX语法具备过滤效果(过滤非法内容),有效防止XSS攻击(扩展思考:总结常见的XSS攻击和预防方案?)

1.1.1.一个根容器

最外层只能一个根容器(一个app有一个容器)

在ReactDOM.createRoot()的时候,不能直接把HTML/BODY做为根容器,需要指定一个额外的盒子「例如:#root」

1.1.2.一个根元素节点

容器可以有多个视图、每一个视图,只有一个“根节点”。

  • 出现多个根节点则报错 Adjacent JSX elements must be wrapped in an enclosing tag.
  • React.Fragment 空文档标记标签:<></>(既保证只有一个根节点,又不新增一个HTML层级结构!!)

1.1.3 动态绑定数据{}

JS表达式嵌入到HTML中,需要使用{} (❗大括号中存放的是JS表达式)

JS表达式:执行有结果的表达式,如:数值、数学运算、三元运算符、循环(借助数组迭代的办法map,foreach没有返回值所以不行)

if(){}不行,JSX中进行的判断一般都要基于三元运算符来完成

命令式循环forfor/infor/ofwhile不行

1.1.4 JS表达式要求

{}语法中嵌入不同的值,所呈现出来的特点

  1. number/string:值是啥,就渲染出来啥

  2. boolean/null/undefined/Symbol/BigInt:渲染的内容是空

  3. 除数组对象外,其余对象一般都不支持在{}中进行渲染,但是也有特殊情况:

    • JSX虚拟DOM对象
    • 给元素设置style行内样式,要求必须写成一个对象格式
  4. 数组对象:把数组的每一项都分别拿出来渲染「并不是变为字符串渲染,中间没有逗号」

    JSX中遍历数组中的每一项,动态绑定多个JSX元素,一般都是基于数组中的map来实现的

    和vue一样,循环绑定的元素要设置key值(作用:用于DOM-DIFF差异化对比)

  5. 函数对象:不支持在{}中渲染,但是可以作为函数组件,用方式渲染。

1.1.5 给元素设置样式

  1. 行内样式:需要基于对象的格式处理,直接写样式字符串会报错;
1
2
3
4
<h2 style={{
color: 'red',
fontSize: '18px' //样式属性要基于驼峰命名法处理
}}>
  1. 设置样式类名:需要把class替换为className;
1
<h2 className="box">

1.2 核心语法初识

为了可以看懂代码于是这里先讲讲一些语法核心。

1
2
3
4
5
6
7
8
9
10
import React from 'react'; //React语法核心
import ReactDOM from 'react-dom/client'; //构建HTML(WebApp)的核心

//获取页面中#root的容器,作为“根”容器
const root = ReactDOM.createRoot(document.getElementById('root'));

//基于render方法渲染我们编写的视图,把渲染后的内容,全部插入到#root中进行渲染
root.render(
....
);

这样就能看懂练手了。😊

1.2 练手

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
62
63
64
65
66
//src\index.jsx(index.js改过来的)
// 项目经过瘦身,保留文中内容就行
import React from 'react';
import ReactDOM from 'react-dom/client';

const root = ReactDOM.createRoot(document.getElementById('root'));

/* 需求一:基于数据的值,来判断元素的显示隐藏 */
let flag = false,isRun = false;

//渲染根函数,注意区分两种写法的区别。
root.render(
<>
{/!* 控制元素的display样式:不论显示还是隐藏,元素本身都渲染出来了 *!/}
<button style={{
display: flag ? 'block' : 'none'
}}>按钮1</button>

<br />

{/!* 控制元素渲染或者不渲染 *!/}
{flag ? <button>按钮2</button> : null}

<br />

<button>{isRun ? '正在处理中...' : '立即提交注册'}</button>
</>
);


/* 需求二:从服务器获取了一组列表数据,循环动态绑定相关的内容 */
let data = [{
id: 1,
title: 'zono1'
}, {
id: 2,
title: 'zono2'
}, {
id: 3,
title: 'zono3'
}];//虚拟数据,假装从后端拿来

root.render(
<>
<h2 className="title">今日新闻</h2>
<ul className="news-box">
{data.map((item, index) => {
/* 循环创建的元素一定设置key属性,属性值是本次循环中的“唯一值”「优化DOM-DIFF」 */
return <li key={item.id}>
<em>{index + 1}</em>
&nbsp;&nbsp;
<span>{item.title}</span>
</li>;
})}
</ul>

<br />

{/* 扩展需求:没有数组,就是想单独循环五次 */}
{new Array(5).fill(null).map((_, index) => {
return <button key={index}>
按钮{index + 1}
</button>;
})}
</>
);

2. JSX底层渲染机制

使用babel转换工具,可以直观看见。

第一步:JSX→virtualDOM

运行时会把我们编写的JSX语法编译为虚拟DOM对象(virtualDOM)。

虚拟DOM对象:框架自己内部构建的一套对象体系(对象的相关成员都是React内部规定的),基于这些属性描述出我们所构建视图中的DOM节点的相关特征。

具体流程

https://babeljs.io/repl可以在线观看jsx转换为对象的效果

下面是我编写的例子

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
//jsx
<>
<h2 className="title" style={styObj}>123</h2>
<div className ="box">
123
<span>321</span>
<span>zono</span>
</div>
</>


//js
//Classic模式,babel官网的另一个模式不是很懂
//TODO 了解模式
React.createElement(
React.Fragment,
null,
React.createElement(
"h2",
{className: "title",
style: styObj,},
"123"
),
React.createElement(
"div",
{className: "box",},
"123",
React.createElement("span", null, "321"),
React.createElement("span", null, "zono")
)
);

//Automatic模式(目前不做解释)
import { jsx as _jsx } from "react/jsx-runtime";
import { jsxs as _jsxs } from "react/jsx-runtime";
import { Fragment as _Fragment } from "react/jsx-runtime";
_jsxs(_Fragment, {
children: [
_jsx("h2", {
className: "title",
style: styObj,
children: "123",
}) ,
_jsxs("div", {
className: "box",
children: [
"123" ,
_jsx("span", {
children: "321",
}),
_jsx("span", {
children: "zono",
}),
],
}),
],
});

让我们来解读一下:

  1. 基于babel-preset-react-app(一个专门对react的babel) 把JSX编译为React.createElement(...) 格式。元素节点都会基于createElement进行处理!

出现的函数的解释:

createElement – React 中文文档 (docschina.org)

React.createElement(ele,props,...children)

  • ele:元素标签名\组件名

  • props:元素的属性集合(对象)(无属性,则此值为null)

  • children:当前元素的子节点,第三个及以后的参数都是

  • 返回虚拟DOM,可以看下文虚拟DOM结构

  1. 感觉Automatic模式就是表示的更清楚。
  2. 注意该方法开发时并不常用,有jsx就够了
  1. 再把 createElement 方法执行,创建出virtualDOM虚拟DOM对象(JSX元素、JSX对象、ReactChild对象…)!!

    我们可以把React.createElement打印出来看看效果。

1
2
3
4
5
6
7
8
9
10
11
12
// 大概结构 
virtualDOM = {
$$typeof: Symbol(react.element),//组件类型,官网组件https://react.docschina.org/reference/react-dom/components
ref: null,
key: null,
type: 标签名「或组件」, //h1、h2...
// 存储了元素的相关属性 && 子节点信息
props: {
元素的相关属性,如:className、style,
children:子节点信息(没有子节点则没有这个属性、属性值可能是一个值、也可能是一个数组)
}//必定存在,至少是个空对象
}

实操(非必要)

这里将简单编写一个creatElement函数

感觉太硬了,所以分个p,见04.jsx底层实操

1
2
3
4
5
6
7
8
9
10
11
12
13
//下面是官方笔记,不确定能不能跑通
const createElement = function createElement(type, props, ...children) {
// virtual:[ˈvɜːtʃuəl]
let len = children.length;
let virtualDOM = {
type,
props: {}
};
if (props !== null) virtualDOM.props = { ...props };
if (len === 1) virtualDOM.props.children = children[0];
if (len > 1) virtualDOM.props.children = children;
return virtualDOM;
};

第二步:virtualDOM→真实DOM

真实DOM:浏览器页面中,最后渲染出来,让用户看见的DOM元素(js基础内容)

​ 这一步是基于ReactDOM中的render方法处理的

v16(16版本)写法

1
2
3
4
ReactDOM.render(
<>...</>,
document.getElementById('root')
);

v18

1
2
3
4
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<>...</>
);

实操(非必要)

render

推荐看04

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
const render = function render(virtualDOM, container) {
let { type, props } = virtualDOM;
if (typeof type === "string") {
let element = document.createElement(type);
for (let key in props) {
if (!props.hasOwnProperty(key)) break;
if (key === 'className') {
element.setAttribute('class', props[key]);
continue;
}
if (key === 'style') {
let styOBJ = props['style'];
for (let attr in styOBJ) {
if (!styOBJ.hasOwnProperty(attr)) break;
element.style[attr] = styOBJ[attr];
}
continue;
}
if (key === 'children') {
let children = props['children'];
if (!Array.isArray(children)) children = [children];
children.forEach(item => {
if (typeof item === "string") {
element.appendChild(document.createTextNode(item));
return;
}
render(item, element);
});
continue;
}
element.setAttribute(key, props[key]);
}
container.appendChild(element);
}
};

更新:新老virtual对比,然后更新对应内容

第一次渲染页面是直接从virtualDOM->真实DOM;

后期视图更新时,会经过一个DOM-DIFF的对比,计算出补丁包PATCH(两次视图差异的部分),把PATCH补丁包进行渲染!!

02.MVC和MVVM

虚拟DOM

主流的思想:不在直接去操作DOM,而是改为“数据驱动思想”

操作DOM思想:

  • 操作DOM比较消耗性能「主要原因就是:可能会导致DOM重排(回流)/重绘」
  • 操作起来也相对来讲麻烦一些

驱动思想:

  • 我们不会在直接操作DOM
  • 我们去操作数据「当我们修改了数据,框架会按照相关的数据,让页面重新渲染」
  • 框架底层实现视图的渲染,也是基于操作DOM完成的
    • 构建了一套 虚拟DOM->真实DOM 的渲染体系
    • 有效避免了DOM的重排/重绘
  • 开发效率更高、最后的性能也相对较好

状态管理

不再获取DOM元素,直接用state。也就是数据驱动

MVC和MVVM

React框架采用的是MVC体系;

Vue框架采用的是MVVM体系;

NG是MVVM,写法是MVC(不懂)

MVC

model数据层 + view视图层 + controller控制层

对应三层,我们的学习目标

  1. 视图层:用专业的语法去构建视图\页面(React中是基于jsx语法来构建视图的)。

  2. 数据层:视图中,需要“动态”(需要变化,不论是样式还是内容)处理的数据,都要有对应的数据模型。

  3. 控制层:在视图中,根据业务需求,进行某些操作时,会修改相关数据,然后React会按最新的数据,重新渲染视图。

    • 数据驱动视图的渲染!!

    • 视图中的表单内容改变,想要修改数据,需要开发者自己去写代码实现!!

      “单向驱动”

MVC图示

MVVM

model数据层 + view视图层 + viewModel数据/视图监听层(数据和视图都被wacher监听)

  1. 数据驱动视图的渲染:监听数据的更新,让视图重新渲染

  2. 视图驱动数据的更改:监听页面中表单元素内容改变,自动去修改相关的数据

    “双向驱动”

VUE虚拟dom图示
    • +
      

MVC VS MVVM

对比

  • MVVM是双向驱动,MVC是单项驱动,表单更新MVVM会自动更新,而MVC需要开发者操作。

基于阮一峰教程的目录,把常用内容只写结构,方便记忆(遇见就写)

标准库

属性描述对象

DOM

document节点

属性

方法

2、获取Element Node(x4)

document.getElementsByTagName()

document.getElementsByClassName()

document.getElementsByName()

document.getElementById()



ES6

使用 create-react-app 构建React工程化项目

这里你需要了解一下creat-react-app脚手架的搭建过程。搜一下吧

这一步主要是下载cra

1.create-react-app概述

  • 简介:create-react-app:基于webpack搭建一套脚手架
  • 脚手架:默认把webpack打包规则处理好了,基础项目结构也创建好了
  • 后面命令都是yarn,故可以了解一下yarn
    • 下载
    • 初始化yarn init

1.1 react的版本

V16:用的多

V17:应该是底层升级,与16看不出来太多差别

V18:新版本、机制语法都升级了(默认安装)

1.2 初识create-react-app

1.2.1 安装create-react-app

全局安装
$ npm i create-react-app -g 「mac需要加sudo」
基于脚手架创建项目「项目名称需要符合npm包规范」:使用“数字、小写、_”

1
2
3
$ npx create-react-app xxx` # xxx是你自定义的项目名
$ cd my-app`
$ npm start

1.2.2 安装后的文件目录

安装完毕,接下来就阅读一下文件目录

1
2
3
4
5
6
7
8
|- node_modules  包含安装的模块
|- public 页面模板和IconLogo
|- favicon.ico
|- index.html
|- src 我们编写程序的地方
|- index.jsx 程序入口「jsx后缀名可以让文件支持jsx语法」
|- package.json
|- ...

1.2.3 从package.json认识基本结构

create-react-app是一个集成了很多内容的脚手架,从package.json,我们可以了解它的基本信息

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
//package.json
{
...
"dependencies": {
...
"react": "^18.2.0", //核心(语法状态处理)
"react-dom": "^18.2.0", //视图渲染核心
"react-scripts": "5.0.1", //对打包命令的集成,会调用webpack
"web-vitals": "^2.1.4" //性能检测工具
},
"scripts": {
"start": "react-scripts start", //开发环境启动web服务进行预览
"build": "react-scripts build", //生产环境打包部署
"test": "react-scripts test", //单元测试
"eject": "react-scripts eject" //暴露配置项
},
"eslintConfig": { //对webpack中ESLint词法检测的配置:包括检测词法错误、要求满足指定的规范等内容
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": { //基于browserslist规范,设置浏览器的兼容情况:例如
//1、postcss-loader + autoprefixer 给css3设置相关前缀
//2、babel-loader 把ES6编译为ES5
"production": [
">0.2%",//忽略使用率小于0.2%的浏览器
"not dead",//忽略IE
"not op_mini all"//不考虑欧朋浏览器
],
"development": [//不兼容低版本浏览器
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}

2.暴露webpack配置项(非必须)

前置知识,学webpack

这里因为要深入学习故暴露配置项

2.1认识暴露后的文件目录

默认情况下,会把webpack配置项隐藏到node_modules中,如果想修改,则需要暴露配置项$ yarn eject

注意事项

1、一旦暴漏后,无法还原

2、 会提醒你先把代码提交到git区,避免暴露后覆盖原始代码(必须上传代码)

3、这里需要用一个空的git库,建议先创建一个新的库,然后git add .git commit -m(不需要上传到远端)

暴露后多了config文件夹(webpack配置)、scripts文件夹(对应下文scripts中的内容,执行相关命令的入口文件)

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
/* 暴露后package.json中的变化 */
//可以参考之前webpack的笔记,
//TODO 目前是在github库中,以注释为笔记,以后写入blog
{
"dependencies":{ //暴露后,把webpack打包需要的所有模块都会安装一次,这里简写了
//有例如babel、单元测试、postcss、sass-loader等等
//一些记录:
//babel-preset-react-app:是对@babel/preset-env的重写,为了react
...
},
"scripts": {
"start": "node scripts/start.js",
"build": "node scripts/build.js",
"test": "node scripts/test.js"
//不在基于react-scripts处理命令,而是直接基于node去执行对应的文件
//已经没有eject命令了
},
//没变
"jest": {
//单元测试配置
},
"babel": { //关于babel-loader的额外配置,类似babel.config.js对bebal-loader的额外配置
"presets": [
"react-app"//基于react重写的babel包
]
}
}

/* 新增的内容 */
|- scripts
|- start.js
|- build.js
|- ...
|- config
|- webpack.config.js
|- paths.js
|- ...

2.1.1 QUER一些疑惑:

cra为什么没有区分开发依赖,和生产依赖?

2.2真实项目中常用的一些修改操作

简单演示了如果配置less的流程,并不是目前开发必要的。sass和less按喜好来。当然文章是less。

2.2.1配置less

2.2.1.1 安装命令行:
1
2
$ yarn add less less-loader@8  #安装8版本,为了兼容。(不确定你看的时候兼容了没)
$ yarn remove sass-loader #卸载sass
2.2.1.2 配置

vscode折叠代码块(方便看)

折叠所有 Ctrl+K+0(零)

展开所有 Ctrl+K+J

配置对应less的内容,其实就是把sass名改为less

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/*config\webpack.config.js*/
// 72~73
const lessRegex = /\.less$/;
const lessModuleRegex = /\.module\.less$/;

//507~545
//在module——rules——第二个大括号——oneof——某个大括号中
{
test: lessRegex,
exclude: lessModuleRegex,
use: getStyleLoaders(
...
'less-loader'
)
},
{
test: lessModuleRegex,
use: getStyleLoaders(
...
'less-loader'
),
}

注意是改两次,一个lessRegex,一个lessModuleRegex

2.2.1.3写入一个less文件用于测试
1
2
3
4
5
6
7
8
//src\index.less
@B:lightblue;

html,
body {
height: 100%;
background: @B;
}

然后导入index.jsx

1
2
3
4
5
6
7
8
9
//src\index.jsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.less';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<div>zono 无敌</div>
);

待其他配置完之后测试

2.2.2 配置别名

配置一个别名,这样以后@就能直接访问src目录

1
2
3
4
5
6
7
8
//config\webpack.config.js
resolve: {
...
alias: {
'@': path.appSrc,//设置别名,指向src目录
...
}
}

2.2.3 配置预览域名

可以修改端口号,有两种方式

1
2
3
4
5
6
7
// scripts/start.js
// 48
const HOST = process.env.HOST || '127.0.0.1';

// 也可以基于 cross-env/ 设置环境变量,先安装cross-env(dotenv也行)
// 安装:yarn add cross-env
// 然后在package.json中修改start:“cross-env POST=8080 node scripts/start.js”
2.2.3.1 QUER一点疑惑

QUER:为什么localhost下使用cross-env设置的POST不生效

这是因为在webpackDevServer.config.js中,会对host进行判断,如果是localhost,就会使用默认的端口(生成的答案待解决)

2.2.4 配置跨域代理

我们可以看见paths.proxySetup中指定了读取跨域文件的位置(在paths.proxySetup中搜索paths.proxySetup),也就是src/setupProxy.js

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
/*
安装 http-proxy-middleware:用于实现跨域代理,webpack-dev-server也是基于这个
$ yarn add http-proxy-middleware

src/setupProxy.js
*/
const { createProxyMiddleware } = require('http-proxy-middleware');
module.exports = function (app) {
app.use(
createProxyMiddleware("/api", {
target: "http://127.0.0.1:7100",
changeOrigin: true,
ws: true,
pathRewrite: { "^/api": "" }
})
);
};

//测试地址:
//https://www.jianshu.com/asimov/subscriptions/recommended_collections
//测试写法
const createProxyMiddleware = require("http-proxy-middleware");
module.exports = function (app) {
app.use(
createProxyMiddleware("/jian", {
target: "https://www.jianshu.com/asimov/",
changeOrigin: true,
pathRewrite: {
"^/jian": "",
},
})
);
};
//https://news-at.zhihu.com/api/4/news/latest

测试

1
fetch()

也能配置到package.json中,但因为json的原因,只能配置一个

2.2.4.1 QUER一些疑惑:

别尝试这个了,配置后会有跨域问题,我还不会解决

TODO 学习node中间件

2.2.5配置浏览器兼容(可选)

解决老版本浏览器兼容

修改兼容后对postcss-loader、babel-loader生效,但对ES6内置API不兼容

可以用@babel/polyfill,但脚手架中不需要我们来自己安装,因为有react-app-poly(看package.json)

index.jsx中加入

1
2
3
import 'react-app-polyfill/ie9';
import 'react-app-polyfill/ie11';
import 'react-app-polyfill/stable';
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
//package.json
//https://github.com/browserslist/browserslist
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
/*
回顾知识点

CSS兼容处理:设置前缀
autoprefixer + postcss-loader + browserslist

JS兼容处理:ES6语法转换为ES5语法
babel-loader + babel-preset-react-app(@babel/preset-env) + browserslist

JS兼容处理:内置API
入口配置react-app-polyfill
*/

ps:每次配置都可以yarn start测试一下效果

其余的一些优化配置、响应式布局的配置等,实战中再去处理!!

transform

  • transform:translate(负左正右,上下移动):可以放入单位、百分比(参照元素自身)
  • transform:rotate(xxx):旋转,默认中心原点
  • transform-origin: xxx xxx:切换原点
  • transform:scale(xxx):缩放大小,两参数(宽高),默认中心原点
  • transform:skew(xxx xxx):一个参数水平移动(角度),两参数(水平和锤子)
  • transform:matrix(a,b,c,d,e,f):ad:缩放,bc:倾斜(斜切)、ef:元素平移

    有设计软件,可以直接导出设置

    transition

    https://juejin.cn/post/6970885478967050254

transition: <property(需要过渡的属性)> <duration(过渡的时长)> <timing-function(缓动函数)> <delay(开始延迟时间)>;

缓动函数:

贝塞尔函数

  • ease:动画以低速开始,然后加快,在结束前变慢。
  • ease-in:指数函数
  • ease-out:log函数
  • ease-in-out:慢开始加速后又慢
  • cubic-bezier(n,n,n,n) 贝塞尔曲线(自定义数值

线性函数

linear()
折线

  • linear:固定时间
  • linear(0,0.25,1):开始较慢,后面突然加快

阶跃

step(<>)
step(2,end):分两次跳跃

A

一个项目部署的记录,主要记录知识点,不记录细节

前端

nginx、apache、tomcat

这里使用nginx

NGINX

安装

这里安装还是比较简单的(直接用包管理器安装)

ubantu:使用

centos:使用

配置

找到nginx配置文件nginx.config

配置前先备份一次sudo cp nginx.conf nginx.default.conf

配置解释

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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
########### 每个指令必须有分号结束。#################
##
#全局块配置nginx全局命令,
#一般有运行nginx服务器的用户组,nginx进程pid存放路径,日志存放路径,配置文件引入,允许生成worker process数等。
##
user www-data;#配置用户或者组,默认为nobody nobody。
worker_processes auto;#允许生成的进程数,默认为1
pid /run/nginx.pid;#指定nginx进程运行文件存放地址

#制定日志路径,级别。
#这个设置可以放入全局块,http块,server块,级别以此为:
#debug|info|notice|warn|error|crit|alert|emerg
include /etc/nginx/modules-enabled/*.conf;



events {
##
#配置影响nginx服务器或与用户的网络连接。
#有每个进程的最大连接数,选取哪种事件驱动模型处理连接请求,是否允许同时接受多个网路连接,开启多个网络连接序列化等。
##
worker_connections 768; #最大连接数,默认为512
# multi_accept on; #设置一个进程是否同时接受多个网络连接,默认为off
}

http {
##
#可以嵌套多个server,配置代理,缓存,日志定义等绝大多数功能和第三方模块的配置。
#如文件引入,mime-type定义,日志自定义,是否使用sendfile传输文件,连接超时时间,单连接请求数等。
##

##
# Basic Settings
##

sendfile on; #允许sendfile方式传输文件,默认为off,可以在http块,server块,location块。
tcp_nopush on;
types_hash_max_size 2048;
# server_tokens off;

# server_names_hash_bucket_size 64;
# server_name_in_redirect off;

include /etc/nginx/mime.types; #文件扩展名与文件类型映射表
default_type application/octet-stream; #默认文件类型,默认为text/plain

##
# SSL Settings
##

ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; # Dropping SSLv3, ref: POODLE
ssl_prefer_server_ciphers on;

##
# Logging Settings
##

access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;

##
# Gzip Settings
##

gzip on;

# gzip_vary on;
# gzip_proxied any;
# gzip_comp_level 6;
# gzip_buffers 16 8k;
# gzip_http_version 1.1;
# gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

server {
##
#配置虚拟主机的相关参数,一个http中可以有多个server。
##

# keepalive_requests 120; #单连接请求上限次数。
listen 80; #监听80端口
server_name localhost; #监听地址

location / { #请求的url过滤,正则匹配,~为区分大小写,~*为不区分大小写。
##
#配置请求的路由,以及各种页面的处理情况。
##
root html; #根目录
index index.html index.htm; #设置默认页
# proxy_pass http://mysvr; #请求转向mysvr 定义的服务器列表
# deny 127.0.0.1; #拒绝的ip
# allow 172.18.5.54; #允许的ip
}




}

##
# Virtual Host Configs
##

include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}


#mail {
# # See sample authentication script at:
# # http://wiki.nginx.org/ImapAuthenticateWithApachePhpScript
#
# # auth_http localhost/auth.php;
# # pop3_capabilities "TOP" "USER";
# # imap_capabilities "IMAP4rev1" "UIDPLUS";
#
# server {
# listen localhost:110;
# protocol pop3;
# proxy on;
# }
#
# server {
# listen localhost:143;
# protocol imap;
# proxy on;
# }
#}

操作:把server块中的root(文件根目录)改为打包好的前端代码。
然后nginx -s reload更改配置

踩坑

nginx -s reload后没有生效?

解决办法:换了一个端口使用(这里换端口时也要设置用户名,用户要有对应的权限,一般为root)

nginx命令行基础使用

后端

这里用到了apache
nginx与Apache的对比以及优缺点

fastapi部署
pip install gunicorn

配置办法

使用宝塔Linux

被降维打击了

总而言之,部署成功了,就是没钱得用没钱的办法,后面找朋友把另一个端口开了。

docker

安装

制作镜像Dockfile(一般情况下,使用同类项目的Dockfile)

前端还需要一个nginx.conf文件

0%