url
loading │
resources────────►│ onload

js css etc
html解释器────────►│
▼ ▼
js engine───────►DOM tree DOMContent

css 解释器────────►│

RenderObject tree


RenderLayer tree


paint context
graph lib────────►│

final presentation

typescript

关键字

let any: 任意类型 let x: any = 1; string: 字符串 boolean: true false let arr: number[] = [1, 2]; let arr: Array<number> = [1, 2]; let x: [string, number];//元组, 类型可以不同的数组 enum

enum Color {Red, Green, Blue};
let c: Color = Color.Blue;

空值

  • null null 在 JavaScript 中 null 表示 "什么都没有"。

    null是一个只有一个值的特殊类型。表示一个空对象引用。

    用 typeof 检测 null 返回是 object。

  • undefined 在 JavaScript 中, undefined 是一个没有设置值的变量。

    typeof 一个没有值的变量会返回 undefined。

    Null 和 Undefined 是其他任何类型(包括 void)的子类型,可以赋值给其它类型,如数字类型,此时,赋值后的类型会变成 null 或 undefined。而在TypeScript中启用严格的空校验(--strictNullChecks)特性,就可以使得null 和 undefined 只能被赋值给 void 或本身对应的类型,示例代码如下:

    let x: number;
    x = 1; // 编译正确
    x = undefined; // 编译错误
    x = null; // 编译错误

    let x: number | null | undefined;
    x = 1; // 编译正确
    x = undefined; // 编译正确
    x = null; // 编译正确

  • never 是其它类型(包括 null 和 undefined)的子类型,代表从不会出现的值

类型转换

let a: typeA;
let b = a as typeB;

let a: typeA;
let b = <typeB>a;

作用域

var global_num = 12          // 全局变量
class Numbers {
num_val = 13; // 实例变量
static sval = 10; // 静态变量

storeNum():void {
var local_num = 14; // 局部变量
}
}
console.log("全局变量为: "+global_num)
console.log(Numbers.sval) // 静态变量
var obj = new Numbers();
console.log("实例变量: "+obj.num_val)

函数

function add(x: number, y: number, default_val:number = 0.50, optional_val?:number): number {
return x + y;
}
console.log(add(1,2))

var msg = function() {
return "hello world";
}
console.log(msg())

//匿名函数自调用
(function () {
var x = "Hello!!";
console.log(x)
})()

//lambda
var foo = (x:number)=> {
x = 10 + x
console.log(x)
}
console.log(foo(100)) //输出结果为 110

class

interface Person{
age:number
}
interface IPerson:Person {
firstName:string,
lastName:string,
sayHi: ()=>string
}

var customer:IPerson = {
firstName:"Tom",
lastName:"Hanks",
sayHi: ():string =>{return "Hi there"}
}

class Car { 
// 字段
engine:string;
static num:number;
// 构造函数
constructor(engine:string) {
this.engine = engine
}
// 方法
disp():void {
console.log("发动机为 : "+this.engine)
}
}
class MyCar extends Car{}
var obj = new Car("Engine 1")
var sites = {
site1:"Runoob",
site2:"Google"
};

模块

TypeScript 模块的设计理念是可以更换的组织代码。

模块是在其自身的作用域里执行,并不是在全局作用域,这意味着定义在模块里面的变量、函数和类等在模块外部是不可见的,除非明确地使用 export 导出它们。类似地,我们必须通过 import 导入其他模块导出的变量、函数、类等。

两个模块之间的关系是通过在文件级别上使用 import 和 export 建立的。

模块使用模块加载器去导入其它的模块。 在运行时,模块加载器的作用是在执行此模块代码前去查找并执行这个模块的所有依赖。 大家最熟知的JavaScript模块加载器是服务于 Node.js 的 CommonJS 和服务于 Web 应用的 Require.js。

import shape = require("./IShape"); 
export class Circle implements shape.IShape {
public draw() {
console.log("Cirlce is drawn (external module)");
}
}

外部文件

TypeScript 作为 JavaScript 的超集,在开发过程中不可避免要引用其他第三方的 JavaScript 的库。虽然通过直接引用可以调用库的类和方法,但是却无法使用TypeScript 诸如类型检查等特性功能。为了解决这个问题,需要将这些库里的函数和方法体去掉后只保留导出类型声明,而产生了一个描述 JavaScript 库和模块信息的声明文件。通过引用这个声明文件,就可以借用 TypeScript 的各种特性来使用库文件了。

//third party .js
var Runoob;
(function(Runoob) {
var Calc = (function () {
function Calc() {
}
})
Calc.prototype.doSum = function (limit) {
var sum = 0;

for (var i = 0; i <= limit; i++) {
sum = sum + i;
}
return sum;
}
Runoob.Calc = Calc;
return Calc;
})(Runoob || (Runoob = {}));
var test = new Runoob.Calc();

//.d.ts
declare module Runoob {
export class Calc {
doSum(limit:number) : number;
}
}

//.ts
/// <reference path = "Calc.d.ts" />
var obj = new Runoob.Calc();
// obj.doSum("Hello"); // 编译错误
console.log(obj.doSum(10));

html

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>My test page</title>
</head>
<body>
<img src="images/firefox-icon.png" alt="My test image">
</body>
</html>
  • <!DOCTYPE html> — 文档类型。混沌初分,HTML 尚在襁褓(大约是 1991/92 年)之时,DOCTYPE 用来链接一些 HTML 编写守则,比如自动查错之类。DOCTYPE 在当今作用有限,仅用于保证文档正常读取。现在知道这些就足够了。
  • <html></html>`` 元素。该元素包含整个页面的内容,也称作根元素。
  • <head></head>`` 元素。该元素的内容对用户不可见,其中包含例如面向搜索引擎的搜索关键字(keywords)、页面描述、CSS 样式表和字符编码声明等。
  • <meta charset="utf-8"> — 该元素指定文档使用 UTF-8 字符编码,UTF-8 包括绝大多数人类已知语言的字符。基本上 UTF-8 可以处理任何文本内容,还可以避免以后出现某些问题,没有理由再选用其他编码。
  • <title></title>`` 元素。该元素设置页面的标题,显示在浏览器标签页上,也作为收藏网页的描述文字。
  • <body></body>`` 元素。该元素包含期望让用户在访问页面时看到的内容,包括文本、图像、视频、游戏、可播放的音轨或其他内容。

javascript

数据

基本类型有: null, undefined, number, string, boolean, symbol(ES6新增), 存储在栈中

其他的为引用类型(object), 存储在堆中, 栈中存储引用(指针)

var length = 16;                                  // Number 通过数字字面量赋值
var points = x * 10; // Number 通过表达式字面量赋值
var lastName = "Johnson"; // String 通过字符串字面量赋值
var cars = ["Saab", "Volvo", "BMW"]; // Array 通过数组字面量赋值
var person = {firstName:"John", lastName:"Doe"}; // Object 通过对象字面量赋值

//默认 undefined
//null 用于对象, undefined 用于变量,属性和方法。
//也可以用 const let
typeof "John" // 返回 string
typeof 3.14 // 返回 number
typeof false // 返回 boolean
typeof [1,2,3,4] // 返回 object
typeof {name:'John', age:34} // 返回 object
typeof null // 返回 object
var cars=null;

// 创建对象
var carname=new String;
var x= new Number;
var y= new Boolean;
var cars= new Array;
var person= new Object;
var x = new myFunction("John","Doe");

const name = 'Runoob';
const age = 30;
const message = `My name is ${name} and I'm ${age} years old.`;
txt1="What a very";
txt2="nice day";
txt3=txt1+txt2;
x=5+5; //10
y="5"+5; //55
z="Hello"+5; //Hello5

= =值等于
= = = 值和类型均等于

var patt = /runoob/i //正则
var str = "Visit Runoob!";
var n = str.search(/Runoob/i);

JavaScript 中,函数及变量的声明都将被提升到函数的最顶部。初始化的则不会

"use strict"

undefined means a variable has been declared but has not yet been assigned a value :

var testVar;
console.log(testVar); //shows undefined
console.log(typeof testVar); //shows undefined

null is an assignment value. It can be assigned to a variable as a representation of no value :

var testVar = null;
console.log(testVar); //shows null
console.log(typeof testVar); //shows object

类型转换

let x = ...
x.toString()
  • Number(xxx)

    • undefined NaN
    • Null, false 0
    • true 1
    • symbol error
    • object 先 toPrimitive 再 toNumber
    • string
      • 如果字符串中只包含数字,那么就转换为对应的数字。
      • 如果字符串中只包含十六进制格式,那么就转换为对应的十进制数字。
      • 如果字符串为空,那么转换为0。
      • 如果字符串包含上述之外的字符,那么转换为 NaN。
  • Boolean(xxx)

    • undefined, 0, NaN 0
    • symbol, object, 其他 number 1
  • xxx.toString()

    • undefined 'undefined'
    • true, false 'true', 'false'
    • number 对应数字字符串
    • symbol error
    • object 先 toPrimitive 再 toNumber
  • toPrimitive()

    img
    1 + '2' = '12'
    'hello' + {} = 'hello [object Object]'
    [] + [] = ''
    • 如果有一方是字符串,另一方则会被转换为字符串,并且它们连接起来。
    • 如果双方都是 BigInt,则执行 BigInt 加法。如果一方是 BigInt 而另一方不是,会抛出 TypeError
    • 否则,双方都会被转换为数字,执行数字加法。

函数

function myFunction(a,b)
{
return a*b;
}
document.getElementById("demo").innerHTML=myFunction(4,3);
//自调用
(function () {
var x = "Hello!!"; // 我将调用自己
})();
(参数1, 参数2, …, 参数N) => { 函数声明 }
(参数1, 参数2, …, 参数N) => 表达式(单一)
// 相当于:(参数1, 参数2, …, 参数N) =>{ return 表达式; }
// ES5
var x = function(x, y) {
return x * y;
}

// ES6
const x = (x, y) => x * y;

实际上,在 JavaScript 中,所有函数都能访问它们上一层的作用域。 javascript中的参数在内部是用一个数组来表示的。函数接收到的始终都是这个数组,而不关心数组中包含哪些参数。在函数体内可以通过arguments对象来访问这个参数数组,从而获取传递给函数的每一个参数。

this

  • 在方法中,this 表示该方法所属的对象。
  • 如果单独使用,this 表示全局对象。
  • 在函数中,this 表示全局对象。
  • 在函数中,在严格模式下,this 是未定义的(undefined)。
  • 在事件中,this 表示接收事件的元素。
  • 类似 call() 和 apply() 方法可以将 this 引用到任何对象。
var obj = {
foo: function () { console.log(this.bar) },
bar: 1
};

var foo = obj.foo;
var bar = 2;

obj.foo() // 1
foo() // 2
// this指的是函数运行时所在的环境。对于obj.foo()来说,foo运行在obj环境,所以this指向obj;对于foo()来说,foo运行在全局环境,所以this指向全局环境。所以,两者的运行结果不一样。

衍生内容

var obj = { foo: 5 }; 上面的代码将一个对象赋值给变量obj。JavaScript 引擎会先在内存里面,生成一个对象{ foo: 5 },然后把这个对象的内存地址赋值给变量objvar obj = { foo: 5, myfunc: foofunc }; 这种情况, myfunc保存的是 foofunc 函数的地址

this.x是指当前运行环境的x

class Runoob extends Animal{
constructor(name, url) {
this.name = name;
this.url = url;
}
age() {
let date = new Date();
return date.getFullYear() - this.year;
}
get s_name() {
return this.sitename;
}
set s_name(x) {
this.sitename = x;
}

static hello() {
return "Hello!!";
}
}

let site = new Runoob("菜鸟教程", "https://www.runoob.com");

for

for (let index=0; index < someArray.length; index++) {
const elem = someArray[index];
// ···
}

for (const key in someArray) {
console.log(key);//key 是数组的 index, 用于 object 时是属性名
}

someArray.forEach((elem, index) => {
console.log(elem, index);
});
const numbers = [65, 44, 12, 4];
numbers.forEach(myFunction)
function myFunction(item, index, arr) {
arr[index] = item * 10;
}


for (const elem of someArray) {
console.log(elem);// 不会影响数组原来的值
}

array

reduce

var numbers = [65, 44, 12, 4];

//function(total,currentValue, index,arr)
//total 必需。初始值, 或者计算结束后的返回值。
//currentValue 必需。当前元素
//currentIndex 可选。当前元素的索引
//arr 可选。当前元素所属的数组对象。
function getSum(total, num) {
return total + num;
}
function myFunction(item) {
document.getElementById("demo").innerHTML = numbers.reduce(getSum);

}
//125

map

var officersIds = officers.map((officer, index) => {
return [index: officer]
});

filter

var rebels = pilots.filter(function (pilot) {
return pilot.faction === "Rebels";
});

let start_list = minus.map((data, index) => data === min ? index : undefined).filter(data => data !== undefined)

拷贝

// 浅拷贝, 因为数组是引用类型, =是赋值了指向数组的指针
let arr1 = [1,2,3];
let arr2 = arr1;

// 深拷贝 ES6 语法, 前提是 arr1 里没有引用类型
let arr1 =[1, 2, 3];
let arr2 = [...arr1];

// 深拷贝
let nestedArray = [1, [2], 3];
let arrayCopy = JSON.parse(JSON.stringify(nestedArray));

通过Array.length 判断是否为空 通过 JSON.stringfy(obj)==="{}" 判断对象是否为空

查找

Array.filter()

const array = [10, 11, 3, 20, 5];
const greaterThanTen = array.filter(element => element > 10);
console.log(greaterThanTen) //[11, 20]

Array.find()

我们使用 Array.find( )方法来寻找符合某个条件的第一个元素。

const array = [10, 11, 3, 20, 5];
const greaterThanTen = array.find(element => element > 10);
console.log(greaterThanTen)//11

添加

  • 末尾 xxx.push()
  • 开头 xxx.unshift()

swap

Given the array arr = [1,2,3,4], you can swap values in one line now like so:

[arr[0], arr[1]] = [arr[1], arr[0]];

sort

some_array.sort((a, b)=>a-b) 升序

compareFn(a, b) return value sort order
> 0 sort a after b, e.g. [b, a]
< 0 sort a before b, e.g. [a, b]
=== 0 keep original order of a and b

二维数组初始化

var arr = new Array(10).fill(new Array(10).fill(0))

json

  • a string
  • a number
  • an object (JSON object)
  • an array
  • a boolean: true false
  • null

html

  • 标签可用在有序列表 (
      ) 和无序列表 (

        ) 中。

        <!--这是一个注释,注释在浏览器中不会显示-->

        tips

        async await promise

        async + await 必须配合 promise 使用,同时 async 和 await 必须一起使用。即 await 必须在 async 标记的函数中使用 return await 是冗余的, 不需要 await?

        The keyword async before a function makes the function return a promise: The await keyword can only be used inside an async function. The await keyword makes the function pause the execution and wait for a resolved promise before it continues:

        promise The Promise object represents the eventual completion (or failure) of an asynchronous operation and its resulting value.

        const fetchPromise = fetch(
        "https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/products.json",
        );

        console.log(fetchPromise);

        fetchPromise.then((response) => {
        console.log(`Received response: ${response.status}`);
        });

        console.log("Started request…");
        Here, we are calling await fetch(), and instead of getting a Promise, our caller gets back a fully complete Response object 使用 await 从 promise 中取得数据 或者promise.then((data) => console.log(data[0].name)); The Promise.resolve() static method "resolves" a given value to a Promise. 把普通对象变成 promise If the value is a promise, that promise is returned; if the value is a thenable, Promise.resolve() will call the then() method with two callbacks it prepared; otherwise the returned promise will be fulfilled with the value. 如果没有在 async 函数中 return ,那么 promise 对象的 resolve 就是 undefined 如果在 async 函数中写了 return,那么 promise 对象的 resolve 就是 return 的值

        // 永远不会执行 catch
        async function foo() {
        try {
        return waitAndMaybeReject();
        } catch (e) {
        return 'caught';
        }
        }

        // 可能执行 catch
        async function foo() {
        try {
        // Wait for the result of waitAndMaybeReject() to settle,
        // and assign the fulfilled value to fulfilledValue:
        const fulfilledValue = await waitAndMaybeReject();
        // If the result of waitAndMaybeReject() rejects, our code
        // throws, and we jump to the catch block.
        // Otherwise, this block continues to run:
        return fulfilledValue;
        // 可以简化成 return await waitAndMaybeReject();
        } catch (e) {
        return 'caught';
        }
        }

        闭包

        Require vs. Import in JavaScript

        In JavaScript, the require and import keywords are used to import modules. require is a function used to import modules in Node.js, while import is a new keyword that is used to import modules in ECMAScript 6 (ES6).

        commonjs esmodule

        CommonJS is a set of standards used to implement modules on JavaScript. The project was started by Mozilla engineer Kevin Dangoor in 2009. CommonJS is mainly used in server-side JS apps with Node, as browsers don't support the use of CommonJS.

        node 中一个文件就是一个 module 路径分析和文件定位:

        • 核心模块 http, fs, path
        • .或..开始的相对路径模块, 其中没有后缀的, 按照.js .json .node 次序补足扩展
        • /开始的绝对路径模块
        • 非路径模块

        自定义模块路径的生成顺序

        1. 当前文件目录下的 node_modules
        2. 父目录的node_modules
        3. 向上递归直到根目录下的node_modules

        没有找到文件的但找到目录的将目录当做一个包处理

        1. package.json 中的 main 字段指定文件r
        2. 如果没有指定, 则默认 index.js index.json index.node
        //导出
        const mod1Function = () => console.log('Mod1 is alive!')
        module.exports = mod1Function
        //导入
        mod1Function = require('./mod1.js')
        const testFunction = () => {
        console.log('Im the main function')
        mod1Function()
        }
        //导出
        exports.add = function(){...}
        //导入
        var mymodule = require('...')
        mymodule.add()

        Now let's review ESmodules. ESmodules is a standard that was introduced with ES6 (2015). The idea was to standarize how JS modules work and implement this features in browsers (which didn't previously support modules).

        ESmodules is a more modern approach that is currently supported by browser and server-side apps with Node.

        // mod1.js
        const mod1Function = () => console.log('Mod1 is alive!')
        export { mod1Function }
        // main.js
        import { mod1Function } from './mod1.js'

        const testFunction = () => {
        console.log('Im the main function')
        mod1Function()
        }

        testFunction()
        语法 导出声明 导入声明
        默认 export default function Button() {} import Button from './Button.js';
        命名 export function Button() {} import { Button } from './Button.js';

        var vs. let vs const

        ES6 新增了let命令,用来声明变量。它的用法类似于var,但是所声明的变量,只在let命令所在的代码块内有效。

        下面的代码如果使用var,最后输出的是10

        var a = [];
        for (var i = 0; i < 10; i++) {
        a[i] = function () {
        console.log(i);
        };
        }
        a[6](); // 10

        上面代码中,变量ivar命令声明的,在全局范围内都有效,所以全局只有一个变量i。每一次循环,变量i的值都会发生改变,而循环内被赋给数组a的函数内部的console.log(i),里面的i指向的就是全局的i。也就是说,所有数组a的成员里面的i,指向的都是同一个i,导致运行时输出的是最后一轮的i的值,也就是 10。

        如果使用let,声明的变量仅在块级作用域内有效,最后输出的是 6。

        var a = [];
        for (let i = 0; i < 10; i++) {
        a[i] = function () {
        console.log(i);
        };
        }
        a[6](); // 6

        上面代码中,变量ilet声明的,当前的i只在本轮循环有效,所以每一次循环的i其实都是一个新的变量,所以最后输出的是6。你可能会问,如果每一轮循环的变量i都是重新声明的,那它怎么知道上一轮循环的值,从而计算出本轮循环的值?这是因为 JavaScript 引擎内部会记住上一轮循环的值,初始化本轮的变量i时,就在上一轮循环的基础上进行计算。

        另外,for循环还有一个特别之处,就是设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域。

        for (let i = 0; i < 3; i++) {
        let i = 'abc';
        console.log(i);
        }
        // abc
        // abc
        // abc

        上面代码正确运行,输出了 3 次abc。这表明函数内部的变量i与循环变量i不在同一个作用域,有各自单独的作用域(同一个作用域不可使用 let 重复声明同一个变量)。

        不存在变量提升

        var命令会发生“变量提升”现象,即变量可以在声明之前使用,值为undefined。这种现象多多少少是有些奇怪的,按照一般的逻辑,变量应该在声明语句之后才可以使用。

        为了纠正这种现象,let命令改变了语法行为,它所声明的变量一定要在声明后使用,否则报错。

        暂时性死区

        只要块级作用域内存在let命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响。

        var tmp = 123;

        if (true) {
        tmp = 'abc'; // ReferenceError
        let tmp;
        }

        上面代码中,存在全局变量tmp,但是块级作用域内let又声明了一个局部变量tmp,导致后者绑定这个块级作用域,所以在let声明变量前,对tmp赋值会报错。

        ES6 明确规定,如果区块中存在letconst命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。

        总之,在代码块内,使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”(temporal dead zone,简称 TDZ)。

        总之,暂时性死区的本质就是,只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。

        == === is

        • 在比较两个操作数时,双等号(==)将执行类型转换 并且会按照 IEEE 754 标准对 NaN-0+0 进行特殊处理(故 NaN != NaN,且 -0 == +0); Object:仅当两个操作数引用相同的对象时,才返回 true。 如果操作数之一为 nullundefined,则另一个操作数必须为 nullundefined 才返回 true。否则返回 false
        • 三等号(===) 严格相等比较两个值是否相等。两个被比较的值在比较前都不进行隐式转换。如果两个被比较的值具有不同的类型,这两个值是不相等的。否则,如果两个被比较的值类型相同,值也相同,并且都不是 number 类型时,两个值相等。最后,如果两个值都是 number 类型,当两个都不是 NaN,并且数值相同,或是两个值分别为 +0-0 时,两个值被认为是相等的。
        • Object.is() 既不进行类型转换,也不对 NaN-0+0 进行特殊处理(这使它和 === 在除了那些特殊数字值之外的情况具有相同的表现)。

        arrow func =>

        是一种匿名函数

        In short, with arrow functions there are no binding of this.

        如果箭头函数函数体只有一句话,那么这个句话可以不带大括号,而且这句话就是返回值(可以不用写return)

        // Regular Function:
        // With a regular function this represents the object that calls the function:
        hello = function() {
        document.getElementById("demo").innerHTML += this;
        }

        // Arrow Function:
        // With an arrow function this represents the owner of the function:
        hello = () => {
        document.getElementById("demo").innerHTML += this;
        }

        单双引号 反引号

        JS 中单引号和双引号无任何区别,二者均用于表示字符串字面量。 单引号和双引号混合使用时,内层引号将被视为字符串的一部分。 从 ECMAScript 6 开始,表示字符串引入了新的方法,即使用反引号(`)来表示模板字符串。

        名词解释

        SEO 是“Search Engine Optimization”(搜索引擎优化)

        SSR,全称为Server Side Rendering,即服务器端渲染。 它是一种在服务器端将应用或网页转换为HTML字符串,然后发送到客户端的技术。 客户端接收到HTML字符串后,直接显示出网页的内容

        RD: Research and Development engineer,研发工程师,对某种不存在的事物进行系统的研究和开发并具有一定经验的专业工作者,或者对已经存在的事物进行改进以达到优化目的的专业工作者。

        PM: Product Manager,产品经理,又称品牌经理。举凡产品从创意到上市,所有相关的研发、调研、生产、编预算、广告、促销活动等等,都由产品经理掌控。

        QA: Quality Assurance,品质保证。QA的主要职责就是质量保证工作。

        OP: Operator,操作员,管理员。

        DBA 是数据库管理员(Database Administrator)的简称

        DAO 的全名是“decentralized autonomous organization”,中文是“去中心化的自治组织”

        DML(data manipulation language)是数据操纵语言:它们是SELECT、UPDATE、INSERT、DELETE,就象它的名字一样,这4条命令是用来对数据库里的数据进行操作的语言。

        DAL A data access layer

        DDL(data definition language)是数据定义语言:DDL比DML要多,主要的命令有CREATE、ALTER、DROP等,DDL主要是用在定义或改变表(TABLE)的结构,数据类型,表之间的链接和约束等初始化工作上,他们大多在建立表时使用。

        DCL(DataControlLanguage)是数据库控制语言:是用来设置或更改数据库用户或角色权限的语句,包括(grant,deny,revoke等)语句。

        SPA single page application

        hooks

        redux

        REST API (representational state transfer)

        • A client-server architecture made up of clients, servers, and resources, with requests managed through HTTP.
        • Stateless client-server communication, meaning no client information is stored between get requests and each request is separate and unconnected.
        • Cacheable data that streamlines client-server interactions.
        • A uniform interface between components so that information is transferred in a standard form. This requires that:
          • resources requested are identifiable and separate from the representations sent to the client.
          • resources can be manipulated by the client via the representation they receive because the representation contains enough information to do so.
          • self-descriptive messages returned to the client have enough information to describe how the client should process it.
          • hypertext/hypermedia is available, meaning that after accessing a resource the client should be able to use hyperlinks to find all other currently available actions they can take.
        • A layered system that organizes each type of server (those responsible for security, load-balancing, etc.) involved the retrieval of requested information into hierarchies, invisible to the client.
        • Code-on-demand (optional): the ability to send executable code from the server to the client when requested, extending client functionality.

        GraphQL API

        EJS Embedded Javascript Templating is a templating engine used by Node.js. Template engine helps to create an HTML template with minimal code. Also, it can inject data into an HTML template on the client side and produce the final HTML. EJS is a simple templating language that is used to generate HTML markup with plain JavaScript. It also helps to embed JavaScript into HTML pages.

        Pug is a template engine for Node and for the browser. It compiles to HTML and has a simplified syntax, which can make you more productive and your code more readable. Pug makes it easy both to write reusable HTML, as well as to render data pulled from a database or API.

        ORM Object–relational mapping (ORM, O/RM, and O/R mapping tool)

        中间件 middleware Middleware is software that is used to bridge the gap between applications and operating systems. Middleware sits between an operating system and the applications that run on it to provide a method of communication and data management. This is useful for applications that otherwise would not have any way to exchange data -- such as with software tools and databases The name middleware stems from it being software that sits between the client-side requests on the front end and the back-end resource being requested.

        ODM OEM An OEM (Original Equipment Manufacturer) builds a customer’s product that is fully designed by that customer and then contracted out to produce. ODM (Original Design Manufacturer) is also referred to as private labeling or white label products. In this case, the manufacturer has an existing product design and the customer may make slight changes to sell it under their own brand name. Some examples of changes include branding, colors, or packaging.

        AJAX asynchronous javascript and xml

        Morgan: HTTP request logger middleware for node.js

        ESMAscripts

        4th Edition (abandoned)[edit]

        The proposed fourth edition of ECMA-262 (ECMAScript 4 or ES4) would have been the first major update to ECMAScript since the third edition was published in 1999. The specification (along with a reference implementation) was originally targeted for completion by October 2008.[13] The first draft was dated February 1999.[14] An overview of the language was released by the working group on 23 October 2007.[15]

        By August 2008, the ECMAScript 4th edition proposal had been scaled back into a project code named ECMAScript Harmony. Features under discussion for Harmony at the time included:

        5th Edition – ECMAScript 2009[edit]

        Additions include JSON, String.trim() to easily remove whitespaces surrounding a string (" example " to "example"), String.charAt() to return a single character from a given position in a string, and Array.isArray(). A comma after the final pair of values in an object (var example = { "property1":"value1", "property2":"value2", }) also no longer causes a syntax error.[27]

        6th Edition – ECMAScript 2015[edit]

        The 6th edition, ECMAScript 6 (ES6) and later renamed to ECMAScript 2015, was finalized in June 2015.[4][28] This update adds significant new syntax for writing complex applications, including class declarations (class Foo { ... }), ES6 modules like import * as moduleName from "..."; export const Foo, but defines them semantically in the same terms as ECMAScript 5 strict mode. Other new features include iterators and for...of loops, Python-style generators, arrow function expression (() => {...}), let keyword for local declarations, const keyword for constant local declarations, binary data, typed arrays, new collections (maps, sets and WeakMap), promises, number and math enhancements, reflection, proxies (metaprogramming for virtual objects and wrappers) and template literals using backticks (```) for multi-line strings without escape characters.[29][30] The complete list is extensive.[31][32] As the first "ECMAScript Harmony" specification, it is also known as "ES6 Harmony".

        7th Edition – ECMAScript 2016[edit]

        The 7th edition, or ECMAScript 2016, was finalized in June 2016.[5] Its features include block-scoping of variables and functions, destructuring patterns (of variables), proper tail calls, exponentiation operator ** for numbers, await, async keywords for asynchronous programming (as a preparation for ES2017), and the Array.prototype.includes function.[5][33]

        The exponentiation operator is equivalent to Math.pow, but provides a simpler syntax similar to languages like Python, F#, Perl, and Ruby. async / await was hailed as an easier way to use promises and develop asynchronous code.

        8th Edition – ECMAScript 2017[edit]

        The 8th edition, or ECMAScript 2017, was finalized in June 2017.[6] Its features include the Object.values, Object.entries and Object.getOwnPropertyDescriptors functions for easy manipulation of Objects, async / await constructions which use generators and promises, and additional features for concurrency and atomics.[34][6]

        9th Edition – ECMAScript 2018[edit]

        The 9th edition, or ECMAScript 2018, was finalized in June 2018.[7] New features include the spread operator and rest parameters (...) for object literals, asynchronous iteration, Promise.prototype.finally and additions to RegExp.[7]

        The spread operator allows for the easy copying of object properties, as shown below.

        let object = {a: 1, b: 2}

        let objectClone = Object.assign({}, object) // before ES2018
        let objectClone = {...object} // ES2018 syntax

        let otherObject = {c: 3, ...object}
        console.log(otherObject) // -> {c: 3, a: 1, b: 2}

        10th Edition – ECMAScript 2019[edit]

        The 10th edition, or ECMAScript 2019, was published in June 2019.[8] Added features include, but are not limited to, Array.prototype.flat, Array.prototype.flatMap, changes to Array.sort and Object.fromEntries.[8]

        Array.sort is now guaranteed to be stable, meaning that elements with equal sorting keys will not change relative order before and after the sort operation. Array.prototype.flat(depth=1) flattens an array to a specified depth, meaning that all subarray elements (up to the specified depth) are concatenated recursively.

        Another notable change is that so-called catch binding became optional.[35]

        11th Edition – ECMAScript 2020[edit]

        The 11th edition, or ECMAScript 2020, was published in June 2020.[9] In addition to new functions, this version introduces a BigInt primitive type for arbitrary-sized integers, the nullish coalescing operator, and the globalThis object.[9]

        BigInts are created either with the BigInt constructor or with the syntax 10n, where "n" is placed after the number literal. BigInts allow the representation and manipulation of integers beyond Number.MAX_SAFE_INTEGER, while Numbers are represented by a double-precision 64-bit IEEE 754 value. The built-in functions in Math are not compatible with BigInts; for example, exponentiation of BigInts must be done with the ** operator instead of Math.pow.

        The nullish coalescing operator, ??, returns its right-hand side operand when its left-hand side is null or undefined. This contrasts with the || operator, which would return "string" for all "falsy" values, such as the ones below.

        undefined ?? "string" // -> "string"
        null ?? "string" // -> "string"
        false ?? "string" // -> false
        NaN ?? "string" // -> NaN

        Optional chaining makes it possible to access the nested properties of an object without having an AND check at each level. An example is const zipcode = person?.address?.zipcode. If any of the properties are not present, zipcode will be undefined.

        12th Edition – ECMAScript 2021[edit]

        The 12th edition, ECMAScript 2021, was published in June 2021.[10] This version introduces the replaceAll method for strings; Promise.any, a promise combinator that short-circuits when an input value is fulfilled; AggregateError, a new error type to represent multiple errors at once; logical assignment operators (??=, &&=, ||=); WeakRef, for referring to a target object without preserving it from garbage collection, and FinalizationRegistry, to manage registration and unregistration of cleanup operations performed when target objects are garbage collected; separators for numeric literals (1_000); and Array.prototype.sort was made more precise, reducing the number of cases that result in an implementation-defined sort order.

        13th Edition – ECMAScript 2022[edit]

        The 13th edition, ECMAScript 2022, was published in June 2022.[11] This version introduces top-level await, allowing the keyword to be used at the top level of modules; new class elements: public and private instance fields, public and private static fields, private instance methods and accessors, and private static methods and accessors; static blocks inside classes, to perform per-class evaluation initialization; the #x in obj syntax, to test for presence of private fields on objects; regular expression match indices via the /d flag, which provides start and end indices for matched substrings; the cause property on Error objects, which can be used to record a causation chain in errors; the at method for Strings, Arrays, and TypedArrays, which allows relative indexing; and Object.hasOwn, a convenient alternative to Object.prototype.hasOwnProperty.

        14th Edition – ECMAScript 2023[edit]

        The 14th edition, ECMAScript 2023, was published in June 2023.[36] This version introduces the toSorted, toReversed, with, findLast, and findLastIndex methods on Array.prototype and TypedArray.prototype, as well as the toSpliced method on Array.prototype; added support for #! shebang comments at the beginning of files to better facilitate executable ECMAScript files; and allowed the use of most Symbols as keys in weak collections.

        es6

        spread op ...

        function sum(x, y, z) {
        return x + y + z;
        }

        const numbers = [1, 2, 3];
        console.log(sum(...numbers));
        // Expected output: 6
        console.log(sum.apply(null, numbers));
        // Expected output: 6

        使用情形:

        • Function arguments list (myFunction(a, ...iterableObj, b))

        • Array literals ([1, ...iterableObj, '4', 'five', 6])

        • Object literals ({ ...obj, key: 'value' })

          const obj1 = { foo: "bar", x: 42 };
          const obj2 = { foo: "baz", y: 13 };
          const mergedObj = { x: 41, ...obj1, ...obj2, y: 9 }; // { x: 42, foo: "baz", y: 9 }

        Symbol

        一种原始数据类型, 代表独一无二的值

        const sym = Symbol(obj);
        // 会调用 obj 的 toString(), 即参数总是 string

        // eg
        const shapeType = {
        triangle: Symbol(),
        square: Symbol()
        }

        Set

        cosnt s = new Set([1, 2, 3, 4, 5, 5]);
        s.add(value); // return set
        s.delete(value); // return bool
        s.has(value); // return bool
        s.clear(value) // no return
        const array = Array.from(s); // set -> array

        for(let i of s.keys()){dosth} // 遍历, s.keys(), s.values() 都可以, 遍历顺序为插入顺序
        for(let i of s){dosth}

        1. Set 内 NaN 与 NaN 是相等的(不同于===), 其他判断类似于===

        Map

        const map = new Map();
        map.size;
        map.set(key, value); // return map
        map.get(key);
        map.has(key);
        map.delete(key); // return bool
        map.clear(); // no return
        for(let [key, value] of map){dosth} // 遍历顺序为插入顺序

        object 的键只能是 str, map 的键可以是其他值 当对象作为 map 的键时, 只有对象地址相同才是同一个 key

        proxy

        对目标对象设置一层拦截

        let proxy = new Proxy({}, {
        get: function(target, property, receiver){return 35},
        // proxy.xxxproperty
        set: function(target, propkey, value, receiver){},
        // proxy.xxx = 1
        has: function(target, propkey){ return true },
        // 'xxxprop' in proxy
        deleteProperty: function(target, propkey){return true},
        // delete proxy.xxx
        ownKeys: function(target){},
        // Object.keys(proxy)
        getOwnPropertyDescriptor: function(target, propKey){},
        // Object.getOwnPropertyDescriptor(proxy, 'wat')
        defineProperty: function(target, propKey, propDesc){return true},
        // proxy.newprop = 1
        preventExtensions: function(target){return true},
        // Object.preventExtensions(proxy)
        getPrototypeOf: function(target){},
        // Object.getPrototypeOf(p) === xxxprotoType
        isExtensible: function(target){return true},
        // Object.isExtensible(p)
        setPrototypeOf: function(target, proto){return true},
        // Object.setPrototypeof(proxy, proto);
        apply: function(target, object, args){},
        // proxy()
        construct: function(target, args){},
        // let p = new proxy(1)
        })

        // this, proxy 内 this 指针指向 proxy 而不是 target
        const target {
        m : function () {
        console.1og(this === proxy);
        }
        };
        const proxy = new Proxy(target, {})
        target.m() // false
        proxy.m() // true

        // 解决:
        const target = new Date('2015-01-01');
        const handler = {
        get(target, prop) {
        if (prop === 'getDate') {
        return target.getDate.bind(target);
        }
        return Reflect.get(target, prop);
        }
        };
        const proxy = new Proxy(target, handler);
        proxy.getDate() // 1

        Reflect

        为了将 Object 语言内部方法重写?

        Reflect.apply(target,thisArg,args) 
        Reflect.construct(target,args)
        Reflect.get(target,name,receiver)
        Reflect.set(target,name,value,receiver)
        Reflect.defineProperty(target,name,desc)
        Reflect.deleteProperty(target,name)
        Reflect.has(target,name)
        Reflect.ownKeys(target)
        Reflect.isExtensible(target)
        Reflect.preventExtensions(target)
        Reflect.getOwnPropertyDescriptor(target, name)
        Reflect.getPrototypeOf(target)
        Reflect.setPrototypeOf(target, prototype)

        Promise

        简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。 从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。 Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。

        promise 对象代表一个异步操作,有三种状态: pending (进行中)、 fulfilled (已成功) 和 rejected (已失败)。 从 pending 变为 fulfilled 从 pending变为 rejected 只要这两种情况发生,状态就不会再变了, 这时就称为 resolved(已定型)

        var promise = new Promise(function(resolve, reject) {
        // ... some code
        if (/* 异步操作成功 */){ resolve(value); dosth_still_execute(); }
        else { reject(error); }
        });

        promise.then(value => {
        // success
        }, error => {
        // failure
        });

        promise.catch(error => {
        // 处理 getJSON 和 前一个回调函数运行时发生的错误 console.log('发生错误!', error);
        });
        // 等价于 promise.then(null, error=>{})
        // 建议 总是使用 catch 方法,而不使用 then 方法的第二个参数。

        let p_all = Promise.all([p1, p2, p3]);
        let p_or = Promise.race([p1, p2, p3]);
        let p = Promise.resolve("foo");
        // let p = new Promise(resolve=>resolve("foo"))
        let p = Promise.rejected("foo");

        promise 构造函数接受一个函数作为参数,该函数的两个参数分别是 resolve 和 reject 。它们由 JavaScript 引擎提供,不用自己部署。 resolve 函数的作用是,将 Promise 对象从 pending 变为 resolved,在异步操作成功时调用,并将异步操作的结果,作为参数传递出去; reject 函数的作用是,将 Promise 对象的状态从 pending 变为 rejected,在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。 可以用 then 方法分别指定 resolved 状态和 rejected 状态的回调函数。

        iterator

        解构赋值, 扩展运算符, yield*, for of

        let set = new Set().add('a').add('b').add('c');
        let [x,y] = set;
        // x='a'; y='b'

        var str = 'hello';
        [...str] // ['h','e','l','l','o']

        let generator = function* () {
        yield 1;
        yield* [2,3];
        yield 4;
        };
        var iterator = generator();
        iterator.next() // { value: 1, done: false }
        iterator.next() // { value: 2, done: false }
        iterator.next() // { value: 3, done: false }
        iterator.next() // { value: 4, done: false }
        iterator.next() // { value: undefined, done: true }

        for(let v of arr) {
        console.log(v); // red green blue
        }
        // 对于 object 遍历可使用 for in, 无须实现 iterator 接口
        for (let e in es6) {
        console.log(e);
        }

        IMMER

        Immer (German for: always) is a tiny package that allows you to work with immutable state in a more convenient way.

        import {produce} from "immer"
        const baseState = [
        {
        title: "Learn TypeScript",
        done: true
        },
        {
        title: "Try Immer",
        done: false
        }
        ]
        const nextState = produce(baseState, draftState => {
        draftState.push({title: "Tweet about it"})
        draftState[1].done = true
        })

        HTTPS

        certificate chain

        https://knowledge.digicert.com/solution/how-certificate-chains-work

        • A certificate chain is an ordered list of certificates, containing an SSL/TLS Certificate and Certificate Authority (CA) Certificates, that enables the receiver to verify that the sender and all CA's are trustworthy.
        • The chain or path begins with the SSL/TLS certificate, and each certificate in the chain is signed by the entity identified by the next certificate in the chain.

        What is an Intermediate Certificate?

        • Any certificate that sits between the SSL/TLS Certificate and the Root Certificate is called a chain or Intermediate Certificate.
        • If the Intermediate Certificate is not installed on the server (where the SSL/TLS certificate is installed) it may prevent some browsers, mobile devices, applications, etc. from trusting the SSL/TLS certificate.
        • In order to make the SSL/TLS certificate compatible with all clients, it is necessary that the Intermediate Certificate be installed.

        What is the Root CA Certificate?

        The chain terminates with a Root CA Certificate. The Root CA Certificate is always signed by the CA itself. The signatures of all certificates in the chain must be verified up to the Root CA Certificate.

        img

        https://support.dnsimple.com/articles/what-is-ssl-certificate-chain/

        If the certificate wasn’t issued by a trusted CA, the connecting device (eg. a web browser) checks to see if the certificate of the issuing CA was issued by a trusted CA. It continues checking until either a trusted CA is found (at which point a trusted, secure connection will be established), or no trusted CA can be found (at which point the device will usually display an error).

        pem 文件

        https://serverfault.com/questions/9708/what-is-a-pem-file-and-how-does-it-differ-from-other-openssl-generated-key-file

        Too many standards as it happens. In the end, all of these are different ways to encode Abstract Syntax Notation 1 (ASN.1) formatted data — which happens to be the format x509 certificates are defined in — in machine-readable ways. (X.509是公钥基础设施(PKI)的标准格式。X.509证书就是基于国际电信联盟(ITU)制定的X.509标准的数字证书。X.509证书主要用于识别互联网通信和计算机网络中的身份,保护数据传输安全。X.509证书无处不在,比如我们每天使用的网站、移动应用程序、电子文档以及连接的设备等都有它的身影。X.509证书的结构优势在于它是由公钥和私钥组成的密钥对而构建的 X.509标准基于一种被称为抽象语法表示法 (ASN.1)的接口描述语言,这种接口描述语言定义了可以以跨平台方式序列化和反序列化的数据结构。利用ASN,X.509证书格式可以使用公钥和私钥来加密和解密信息。)

        • .csr - This is a Certificate Signing Request. Some applications can generate these for submission to certificate-authorities. The actual format is PKCS10 which is defined in RFC 2986. It includes some/all of the key details of the requested certificate such as subject, organization, state, whatnot, as well as the public key of the certificate to get signed. These get signed by the CA and a certificate is returned. The returned certificate is the public certificate (which includes the public key but not the private key), which itself can be in a couple of formats.
        • .pem - Defined in RFC 1422 (part of a series from 1421 through 1424) this is a container format that may include just the public certificate (such as with Apache installs, and CA certificate files /etc/ssl/certs), or may include an entire certificate chain including public key, private key, and root certificates. Confusingly, it may also encode a CSR (e.g. as used here) as the PKCS10 format can be translated into PEM. The name is from Privacy Enhanced Mail (PEM), a failed method for secure email but the container format it used lives on, and is a base64 translation of the x509 ASN.1 keys.
        • .key - This is a (usually) PEM formatted file containing just the private-key of a specific certificate and is merely a conventional name and not a standardized one. In Apache installs, this frequently resides in /etc/ssl/private. The rights on these files are very important, and some programs will refuse to load these certificates if they are set wrong.
        • .pkcs12 .pfx .p12 - Originally defined by RSA in the Public-Key Cryptography Standards (abbreviated PKCS), the "12" variant was originally enhanced by Microsoft, and later submitted as RFC 7292. This is a password-protected container format that contains both public and private certificate pairs. Unlike .pem files, this container is fully encrypted. Openssl can turn this into a .pem file with both public and private keys: openssl pkcs12 -in file-to-convert.p12 -out converted-file.pem -nodes

        A few other formats that show up from time to time:

        • .der - A way to encode ASN.1 syntax in binary, a .pem file is just a Base64 encoded .der file. OpenSSL can convert these to .pem (openssl x509 -inform der -in to-convert.der -out converted.pem). Windows sees these as Certificate files. By default, Windows will export certificates as .DER formatted files with a different extension. Like...
        • .cert .cer .crt - A .pem (or rarely .der) formatted file with a different extension, one that is recognized by Windows Explorer as a certificate, which .pem is not.
        • .p7b .keystore - Defined in RFC 2315 as PKCS number 7, this is a format used by Windows for certificate interchange. Java understands these natively, and often uses .keystore as an extension instead. Unlike .pem style certificates, this format has a defined way to include certification-path certificates.
        • .crl - A certificate revocation list. Certificate Authorities produce these as a way to de-authorize certificates before expiration. You can sometimes download them from CA websites.

        In summary, there are four different ways to present certificates and their components:

        • PEM - Governed by RFCs, used preferentially by open-source software because it is text-based and therefore less prone to translation/transmission errors. It can have a variety of extensions (.pem, .key, .cer, .cert, more)
        • PKCS7 - An open standard used by Java and supported by Windows. Does not contain private key material.
        • PKCS12 - A Microsoft private standard that was later defined in an RFC that provides enhanced security versus the plain-text PEM format. This can contain private key and certificate chain material. Its used preferentially by Windows systems, and can be freely converted to PEM format through use of openssl.
        • DER - The parent format of PEM. It's useful to think of it as a binary version of the base64-encoded PEM file. Not routinely used very much outside of Windows.

        https://www.howtogeek.com/devops/what-is-a-pem-file-and-how-do-you-use-it/

        PEM is a container file format often used to store cryptographic keys. It's used for many different things, as it simply defines the structure and encoding type of the file used to store a bit of data.

        -----BEGIN <type>-----
        something encrypted by base64
        -----END <type>-----
        # 每个 pem 文件可能有多个这种块

        pem in ssl

        • The end-user certificate, which is assigned to your domain name by a certificate authority (CA). This is the file you use in nginx and Apache to encrypt HTTPS.
        • Up to four optional intermediate certificates, given to smaller certificate authorities by higher authorities.
        • The root certificate, the highest certificate on the chain, which is self-signed by the primary CA.

        letsencrypt

        以 letsencrypt 的 certbot 签发证书时(letsencrypt 是一个证书颁发机构)

        • cert.pem is the end-user certificate.
        • chain.pem is the rest of the chain; in this case, it's only LetsEncrypt's root certificate.
        • fullchain.pem is cert.pem and chain.pem combined. This is the file passed to nginx with the ssl_certificate directive.
        • privkey.pem is an RSA private key generated alongside the certificate.

        nginx 设置

        ssl_certificate /etc/letsencrypt/live/yourdomain/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/yourdomain/privkey.pem;

        客户端与服务端设置

        服务端

        # -----BEGIN PRIVATE KEY-----
        openssl genrsa -out localhost-key.pem 2048
        # -----BEGIN CERTIFICATE-----
        openssl req -new -x509 -sha256 -key localhost-key.pem -out localhost.pem -days 365
        # 生成文件?

        A PEM encoded file includes Base64 data. The private key is prefixed with a "-----BEGIN PRIVATE KEY-----" line and postfixed with an "-----END PRIVATE KEY-----". Certificates are prefixed with a "-----BEGIN CERTIFICATE-----" line and postfixed with an "-----END CERTIFICATE-----" line. Text outside the prefix and postfix lines is ignored and can be used for metadata.

        // Read SSL certificate and key files
        const options = {
        key: fs.readFileSync(path.join(__dirname, "localhost-key.pem")),
        cert: fs.readFileSync(path.join(__dirname, "localhost.pem")),
        };

        // Create HTTPS server
        const server = https.createServer(options, app);

        server.listen(port, () => {
        console.log(`App listening on https://localhost:${port}`);
        });

        客户端

        # -----BEGIN ENCRYPTED PRIVATE KEY-----
        # -----BEGIN CERTIFICATE----- 客户端证书一般是可选的?
        openssl req -x509 -newkey rsa:2048 -keyout keytmp.pem -out cert.pem -days 365
        # -----BEGIN PRIVATE KEY-----
        openssl rsa -in keytmp.pem -out key.pem
        # 生成文件
        # 修改 package.json script
        "start": "export HTTPS=true&&SSL_CRT_FILE=cert.pem&&SSL_KEY_FILE=key.pem react-scripts start",

        npm start

        报错

        net::ERR_CERT_COMMON_NAME_INVALID "TypeError: Failed to fetchat https://localhost:3000/static/js/bundle.js:437:23at new Promise ()at get_first_article_id_by_order (https://localhost:3000/static/js/bundle.js:436:10)at show_more (https://localhost:3000/static/js/bundle.js:780:97)at https://localhost:3000/static/js/bundle.js:790:5at commitHookEffectListMount (https://localhost:3000/static/js/bundle.js:55244:30)at commitPassiveMountOnFiber (https://localhost:3000/static/js/bundle.js:56737:17)at commitPassiveMountEffects_complete (https://localhost:3000/static/js/bundle.js:56709:13)at commitPassiveMountEffects_begin (https://localhost:3000/static/js/bundle.js:56699:11)at commitPassiveMountEffects (https://localhost:3000/static/js/bundle.js:56689:7)"

        mkcert is a simple tool for making locally-trusted development certificates. It requires no configuration.

        mkcert -install

        解决方法

        1. 客户端服务端创建证书(如上所示)
        2. 在服务端 127.0.0.1:8000 打开浏览器, 导出证书, 加入本地钥匙串证书, 信任该证书
        3. mkcert -install
        4. 访问成功

        mkcert 创建证书如下 image-20240128110602134

        cookie

        动机

        不希望总是由其服务器保存事务状态,而要求网景将状态存储在用户计算机

        组成

        1. 名;
        2. 值;
        3. 各种属性。