zf-总结

promise执行顺序测试
koa
node 进程守护,pm2
webpack loader plugin

打印:

===== 常规
webpack5的优点
继承公共方法(必看)
[三次握手]
AOP 装饰模式
发布订阅模式
观察者模式
=====Promise 
解决的问题 3
promise的特点
promise.all
promise race
promise 中断
isPromise
promise执行顺序测试

浏览器的事件环eventLoop
node的事件环eventLoop
进程,线程
浏览器的进程线程
继承公共方法(必看)
PWA
缓存
本地存储
压缩方法
=====网络:
三次握手,四次挥手
网络5HTTP有哪些Header
http特点,缺点,Resful风格,状态码
header有哪些
https的加密算法
HTTP/1 HTTP/2 HTTP/3 对比分析
http2
多语言
跨域处理
======es6
var const let 的区别和联系
使用箭头函数应注意什么
set 和map
Object.defineProperty
Reflect
Symbol
class
extends
超类
for of
=======npm 
npm i http-server -g 和 npm link 发生了什么
dependcies,devDepencies, peerDependcies, optionDependcies
npm yarn pnpm的区别和联系
npm版本问题
------
前后端交互的几种方式 content-type类型
下载的三种方式
====== node
node 优势
nrm nvm npm可以切换源
node核心模块
编码
进制转换
fs对象
koa express
======
webpack7大作用
webpack核心概念
常用的loader
常用的插件
文件指纹
webpack es6=>es5
webpack基础()——babel
打包第三方类库
多入口MPA
分析
webpack 优化
手写webpack loader
手写webpack pluign

ast
babel的编译过程。
内存栈,什么叫堆,什么叫栈。
词法分析和语法分析的过程。
npm install生成的package-lock.json文件有什么作用?
对比当前的npm, yarn, pnpm

webpack(四) webpack5新特性

1-不再为Node.js模块自动引用 Polyfills
2-长期缓存(新增长期缓存算法,确定的Chunk、模块ID和导出名称)
3-真正的内容Hash。在使用 [contenthash] 时,webpack4 之前添加注释 空行等,都会进行重新加载打包,webpack5 之后做到了真正的内容hash,对于长期缓存有了更好的效果。
4-模块联邦 它允许多个 webpack 构建产物一起工作,允许从指定的远程构建中导入,并以最小的限制来使用远程的 js bundle。

模块联邦
模块联邦一般饮用一个js或者.vue文件,所以和微前端饮用一个js文件不同。
缺点:

运行时加载远程模块等逻辑,可能导致一定的性能问题
按需加载第三方依赖比较难实现
迭代时的版本控制需要更多关注
js的各种继承
继承公共方法(必看)
方法1: Child.prototype.__proto__ = Parent.prototype;
方法2: Reflect.setPrototypeOf(Child.prototype,Parent.prototype);
方法3: Child.prototype = Object.create(Parent.prototype);
方法4: util.inherits(Child,Parent); // 继承公共属性

class AA {}
class BB extends AA{}
const aaa = new BB;
console.log(aaa instanceof AA);
console.log(aaa instanceof BB);

[三次握手]

第一次
第一次握手:建立连接时,客户端发送syn包(seq=j)到服务器,并进入SYN_SENT状态,等待服务器确认;SYN:同步序列编号(Synchronize Sequence Numbers)。 [3] 
第二次
第二次握手:服务器收到syn包,必须确认客户端的SYN(ack=j+1),同时自己也发送一个SYN包(seq=k),即SYN+ACK包,此时服务器进入SYN_RECV状态。 [3] 
第三次
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHEDTCP连接成功)状态,完成三次握手。 [3] 

[四次挥手]

第一步,当主机A的应用程序通知TCP数据已经发送完毕时,TCP向主机B发送一个带有FIN附加标记的报文段(FIN表示英文finish)。 [4] 
第二步,主机B收到这个FIN报文段之后,并不立即用FIN报文段回复主机A,而是先向主机A发送一个确认序号ACK,同时通知自己相应的应用程序:对方要求关闭连接(先发送ACK的目的是为了防止在这段时间内,对方重传FIN报文段)。
第三步,主机B的应用程序告诉TCP:我要彻底的关闭连接,TCP向主机A送一个FIN报文段。
第四步,主机A收到这个FIN报文段后,向主机B发送一个ACK表示连接彻底释放。
AOP (装饰模式) 将函数进行包装 (代理模式) before after

AOP(面向切面编程)的主要作用是把一些跟核心业务逻辑模块无关的功能抽离出来,其实就是给原函数增加一层,不用管原函数内部实现

发布订阅模式
观察者模式 (events on emit) ,基于发布订阅模式
// 观察者模式(一个Subject(arr, state, attch, setState)观察多个Observer(update方法))
class Subject { // 被观察者 小宝宝
    constructor(){
        this.arr = []; // [o1,o2]
        this.state = '我很开心'
    }
    attach(o){ // 原型上的方法
        this.arr.push(o);
    }
    setState(newState){
        this.state = newState;
        this.arr.forEach(o=>o.update(newState))
    }
}
//观察者模式包含发布订阅
class Observer{ // 观察者 我 我媳妇
    constructor(name){
        this.name = name
    }
    update(newState){
        console.log(this.name + '小宝宝:'+newState)
    }
}
let s = new Subject('小宝宝'); // 小宝宝
let o1 = new Observer('我');
let o2 = new Observer('我媳妇')
s.attach(o1);
s.attach(o2);
s.setState('不开心了');

promise

Promise 解决的问题
1. 回调嵌套 回调地狱
2. 错误捕获不好处理错误
3. 多个异步同步的问题 Promise.all,还是基于回调的方式的
Promise的特点
1. Promise的概念 规范文档 promise A+ 规范
2. Promise 三个状态 等待 成功态 失败态
3. 只有等待态 才能变成成功 / 失败
4. 如果状态变化后不能在修改状态
promise-1 编码 三个状态,then方法实现,一个value和reason。

const SUCCESS = 'fulfilled'
const FAIL = 'rejected';
const PENDING = 'pending'
class Promise {
  constructor(executor) {
    this.status = PENDING; // 默认是等待态
    this.value= undefined;
    this.reason = undefined
    let resolve = (value) => {
        if(this.status === PENDING){
            this.value = value;
            this.status = SUCCESS;
        }
    };
    let reject = (reason) => {
        if(this.status === PENDING){
            this.reason = reason;
            this.status = FAIL;
        }
    };
    executor(resolve,reject);
  }
  then(onFulfilled,onRejected){
    if(this.status === SUCCESS){
        onFulfilled(this.value);
    }
    if(this.status === FAIL){
        onRejected(this.reason);
    }
  }
}
module.exports = Promise;
promise-2 promise不是同步的时候数组存放。错误的时候。trycatch.
const SUCCESS = 'fulfilled'
const FAIL = 'rejected';
const PENDING = 'pending'
class Promise {
  constructor(executor) {
    this.status = PENDING; // 默认是等待态
    this.value= undefined;
    this.reason = undefined;
    this.onResolvedCallbacks = []; // 存储成功的所有的回调 只有pending的时候才存储
    this.onRejectedCallbacks = []; // 存储所有失败的
    let resolve = (value) => { // 成功
        if(this.status === PENDING){
            this.value = value;
            this.status = SUCCESS;
            this.onResolvedCallbacks.forEach(fn=>fn());
        }
    };
    let reject = (reason) => { // 失败
        if(this.status === PENDING){ 
            this.reason = reason;
            this.status = FAIL;
            this.onRejectedCallbacks.forEach(fn=>fn());
        }
    };
    try{
        executor(resolve,reject);
    }catch(e){
        reject(e);
    }
  }
  then(onFulfilled,onRejected){ // 默认看一下状态调用对应的函数
    if(this.status === SUCCESS){
        onFulfilled(this.value);
    }
    if(this.status === FAIL){
        onRejected(this.reason);
    }
    if(this.status === PENDING){
        this.onResolvedCallbacks.push(()=>{
            onFulfilled(this.value);
        });
        this.onRejectedCallbacks.push(()=>{
            onRejected(this.reason);
        })
    }
  }
}
module.exports = Promise;

promise-3 编码 必须返回一个Promise
const PENDING = "PENDING";
const SUCCESS = "FULFILLED";
const FAIL = "REJECTED";
// 返还的那个新的promise x 是then方法中的返回值 
function resolvePromise(promise2, x,resolve,reject) { // 考虑的非常全面
    if(promise2 === x){
       return reject(new TypeError('TypeError: Chaining cycle detected for promise #<Promise>'));
    }
    // 判断x的类型
}
class Promise {
  constructor(executor) {
    this.status = PENDING;
    this.value = undefined;
    this.reason = undefined;
    this.onResolvedCallbacks = [];
    this.onRejectedCallbacks = [];
    const resolve = value => {
      if (this.status === PENDING) {
        this.value = value;
        this.status = SUCCESS;
        this.onResolvedCallbacks.forEach(fn => fn());
      }
    };
    const reject = reason => {
      if (this.status === PENDING) {
        this.reason = reason;
        this.status = FAIL;
        this.onRejectedCallbacks.forEach(fn => fn());
      }
    };
    try {
      executor(resolve, reject);
    } catch (e) {
      console.log(e);

      reject(e);
    }
  }
  // 同一个promise then 多次
  then(onFulfilled, onRejected) {
    let promise2;
    // 可以不停的调用then方法,返还了一个新的promise
    // 异步的特点 等待当前主栈代码都执行后才执行
    promise2 = new Promise((resolve, reject) => {
      if (this.status === SUCCESS) {
        setTimeout(() => {
          try {
            // 调用当前then方法的结果,来判断当前这个promise2 是成功还是失败
            let x = onFulfilled(this.value);
            // 这里的x是普通值还是promise
            // 如果是一个promise呢?
            resolvePromise(promise2, x, resolve, reject);
          } catch (err) {
            console.log(err);
            reject(err);
          }
        });
      }
      if (this.status === FAIL) {
        setTimeout(() => {
          try {
            let x = onRejected(this.reason);
            resolvePromise(promise2, x, resolve, reject);
          } catch (err) {
            console.log(err);
            reject(err);
          }
        });
      }
      if (this.status === PENDING) {
        this.onResolvedCallbacks.push(()=>{
          setTimeout(() => {
            try {
              let x = onFulfilled(this.value);
              resolvePromise(promise2, x, resolve, reject);
            } catch (err) {
              console.log(err);
              reject(err);
            }
          });
        });
        this.onRejectedCallbacks.push(()=> {
          setTimeout(() => {
            try {
              let x = onRejected(this.reason);
              resolvePromise(promise2, x, resolve, reject);
            } catch (err) {
                console.log(err);
              reject(err);
            }
          });
        });
      }
    });
    return promise2;
  }
}

module.exports = Promise;
promise-4 resolvePromise按照promise 加规范实现。
function resolvePromise(promise2, x,resolve,reject) { // 考虑的非常全面
    if(promise2 === x){
       return reject(new TypeError('TypeError: Chaining cycle detected for promise #<Promise>'));
    }
    // 判断x的类型
    // promise 有n种实现 都符合了这个规范 兼容别人的promise

    // 怎么判断 x是不是一个promise 看他有没有then方法
    if(typeof x === 'function' || (typeof x === 'object' && x != null)){
      try{
        let then = x.then; // 去then方法可能会出错
        if(typeof then === 'function'){ // 我就认为他是一个promise
           then.call(x,y=>{ // 如果promise是成功的就把结果向下传,如果失败的就让下一个人也失败
              resolvePromise(promise2,y,resolve,reject); // 递归
           },r=>{
              reject(r);
           }) // 不要使用x.then否则会在次取值
        }else{ // {then:()=>{}}
          resolve(x);
        }
      }catch(e){
        reject(e);
      }
    }else{ // x是个? 常量 
      resolve(x);
    }
}
promise-5 当执行过的时候不能再执行,called,值穿透的问题
const PENDING = "PENDING";
const SUCCESS = "FULFILLED";
const FAIL = "REJECTED";
// 严谨 🇬应该判断 别人的promise 如果失败了就不能在调用成功 如果成功了不能在调用失败
function resolvePromise(promise2, x,resolve,reject) { 
    if(promise2 === x){
       return reject(new TypeError('TypeError: Chaining cycle detected for promise #<Promise>'));
    }
    let called;
    if(typeof x === 'function' || (typeof x === 'object' && x != null)){
      try{
        let then = x.then;  // then 可能是getter object.defineProperty
        if(typeof then === 'function'){  // {then:null}
           then.call(x,y=>{ 
             if(called) return; // 1)
             called = true;
              resolvePromise(promise2,y,resolve,reject); 
           },r=>{
             if(called) return; // 2)
             called = true;
              reject(r);
           }) 
        }else{ 
          resolve(x);
        }
      }catch(e){
        if(called) return; // 3) 为了辨别这个promise 不能调用多次
        called = true;
        reject(e);
      }
    }else{
      resolve(x);
    }
}
class Promise {
  constructor(executor) {
    this.status = PENDING;
    this.value = undefined;
    this.reason = undefined;
    this.onResolvedCallbacks = [];
    this.onRejectedCallbacks = [];
    const resolve = value => {
      if (this.status === PENDING) {
        this.value = value;
        this.status = SUCCESS;
        this.onResolvedCallbacks.forEach(fn => fn());
      }
    };
    const reject = reason => {
      if (this.status === PENDING) {
        this.reason = reason;
        this.status = FAIL;
        this.onRejectedCallbacks.forEach(fn => fn());
      }
    };
    try {
      executor(resolve, reject);
    } catch (e) {
      reject(e);
    }
  }
  then(onFulfilled, onRejected) { // .catch(function(){}) .then(null,function)
  onFulfilled = typeof onFulfilled === 'function'?onFulfilled:val=>val;
  onRejected =  typeof onRejected === 'function'?onRejected:err=>{throw err}
    let promise2;
    promise2 = new Promise((resolve, reject) => {
      if (this.status === SUCCESS) {
        setTimeout(() => {
          try {
            let x = onFulfilled(this.value);
            resolvePromise(promise2, x, resolve, reject);
          } catch (err) {
            reject(err);
          }
        });
      }
      if (this.status === FAIL) {
        setTimeout(() => {
          try {
            let x = onRejected(this.reason);
            resolvePromise(promise2, x, resolve, reject);
          } catch (err) {
            reject(err);
          }
        });
      }
      if (this.status === PENDING) {
        this.onResolvedCallbacks.push(()=>{
          setTimeout(() => {
            try {
              let x = onFulfilled(this.value);
              resolvePromise(promise2, x, resolve, reject);
            } catch (err) {
              reject(err);
            }
          });
        });
        this.onRejectedCallbacks.push(()=> {
          setTimeout(() => {
            try {
              let x = onRejected(this.reason);
              resolvePromise(promise2, x, resolve, reject);
            } catch (err) {
              reject(err);
            }
          });
        });
      }
    });
    return promise2;
  }
}
// 希望测试一下这个库是否符合我们的promise A+规范
// promises-aplus-tests
Promise.defer = Promise.deferred = function(){
  let dfd = {};
  dfd.promise = new Promise((resolve,reject)=>{
    dfd.resolve = resolve;
    dfd.reject = reject;
  });
  return dfd;
}
module.exports = Promise;
// npm i promises-aplus-tests -g
// promise 相关方法
// generator
promise defer
Promise.all
Promise.all = funnction(arr) {
	return new Promise((resolve, reject) => {
		const result = [];
		const index = 0;
		arr.forEach((item, itemIndex) => {
			item.then(data => {
				index++;
				result[itemIndex] = data;
				if(index === arr.length) {
					resolve(result)
				}
			}).catch(e => reject(e))
		})
	});
}
// 也可以这样子,同样的效果
Promise.all = function(promises) {
  const result = [];
  let cursor = 0; 
  return new Promise((resolve, reject) => {
    promises.forEach((item, itemIndex) => {
      item.then((data) => {
        result[itemIndex] = data;
      }).catch(e => reject(e)).finally(() => {
        cursor++
        if (cursor === promises.length) {
          resolve(result)
        }
      })
    })
  })
}
Promise.race
// race 赛跑 哪个快 用哪个 all是所有完成才完成
Promise.race = function(promises){
    return new Promise((resolve,reject)=>{
        for(let i = 0;i<promises.length;i++){
            promises[i].then(resolve,reject); // 只要一个成功就成功
        }
    })
}
Promise相关方法补充
let p = new Promise((resolve,reject)=>{
    resolve();
})
// 1).中断promise链 就是返回一个等待的promise
let p1 = p.then(()=>{
    console.log('ok');
    return new Promise(()=>{})
}).then(()=>{
    console.log(1);
})
// 2.finally 实现
Promise.prototype.finally = function(callback){
    // callback 直接放到失败里 会导致无法继承上一次的失败
    // return this.then(callback,callback);
    return this.then((val)=>{
        // 等待finally中的函数执行完毕 继续执行 finally函数可能返还一个promise 用Promise.resolve等待返回的promise执行完
        return Promise.resolve(callback()).then(()=>val);
        //return val; // 如果上一个then是成功就将这个成功向下传递
    },(err)=>{
        return Promise.resolve(callback()).then(()=>{throw err});
        //throw err; // 如果上一个then是失败就将这个失败继续向下抛
    })
}
Promise.reject().finally(()=>{
    console.log(1);
    return new Promise((resovle,reject)=>{
        setTimeout(() => {
            resovle();
        }, 1000);
    })
}).catch(e=>{
    console.log(e);
})

// 我有一个网站有一个接口 在两个服务器上
// 3)如何放弃某个promise的执行结果
function wrap(p1){
    let fail = null;
    let p2 = new Promise((resolve,reject)=>{
        fail = reject; // 先将p2失败的方法暴露出来 
    });
    let p = Promise.race([p2,p1]); // race方法返回的也是一个promise
    p.abort = fail;
    return p
    
}
let p = wrap(new Promise((resolve,reject)=>{
    setTimeout(() => {
        resolve('ok');
    }, 3000);
}))
p.abort('error');
p.then(data=>{
    console.log(data);
}).catch(err=>{
    console.log(err);
});
// 4)既能捕获同步有能捕获异步
function fn(){
    // 可能函数中抛出了 同步错误 要通过try-catch 捕获异常
    // throw new Error('err');
    //    return new Promise((resolve,reject)=>{
    //        setTimeout(() => {
    //         reject('xxx');
    //        }, 3000);
    //    })
}
Promise.try = function(callback){
    return new Promise((resolve,reject)=>{
        // Promise.resolve 只能返回一个成功的promise
        return Promise.resolve(callback()).then(resolve,reject);
    })
}
Promise.try(fn).then((data)=>{
    console.log(data,'---');
},err=>{
    console.log('err:'+err);
}); 

Promise 的catch
给fetch方法添加timeout的方法
isPromise
function isPromise(obj) {  
	return !!obj && (typeof obj === 'object' || typeof obj === 'function') && typeof obj.then === 'function'; 
}
promise 同步的时候错误
try {
    const aa = new Promise(function() {
        console.log('---1111')
        // 同步报错
        throw new Error("Whoops!");
    })
    .catch(() => {
        console.log('---4444')
    });
    console.log('---2222')
    // 第一次执行到这里的时候aa是pending状态, 但是后边aa会发生改变为执行报错的结果obj, 
    console.log(aa)
} catch(e) {
    console.log('---never')
}
console.log('---3333')
----------------------------结果
---1111
---2222
Promise { <pending> }
---3333
---4444
promise执行顺序测试
======
async function async1() {
  console.log('222')
  await async2(); 
  console.log('666')

  // await async2().then(item => {
  //   console.log('====>', 666)
  // }); 

  // node 会被编译出2个then
  // async2().then(()=>{
  //     return 
  // }).then(()=>{
  //     console.log('666')
  // })

  // 浏览器的解析是这个样子
  // Promise.resolve(async2()).then(data => {
  //     console.log('666')
  // })

  // 666变体就会在333之后打印
  // Promise.resolve(async2()).then(data => {
  //   console.log('666')
  // })
  // console.log('666变体')
}
async function async2() {
  console.log('333')
}
console.log('111')
setTimeout(function () {
  console.log('888')
}, 0)
async1();
new Promise(function (resolve) {
  console.log('444')
  resolve();
}).then(function () {
  console.log('777')
})
console.log('555')
================================
Promise.resolve().then(() => {
    console.log(111);
    Promise.resolve().then(() => {
        console.log(333);
    });
});
setTimeout(() => {
    console.log(555);
});
setTimeout(() => {
    console.log(666);
});
Promise.resolve().then(() => {
    console.log(222);
    Promise.resolve().then(() => {
        console.log(444);
    });
});
setTimeout(() => {
    console.log(777);
    Promise.resolve().then(() => {
        console.log(888);
    });
});
======
??? catch后边永远是10setTimeout(() => {
  console.log(1);
  Promise.resolve().then(data => {
    console.log('4')
  }).then(data => {
    console.log('8')
  })
  Promise.resolve().then(data => {
    console.log('5')
  }).then(data => {
    console.log('9')
  })
  Promise.reject().then(data => {
    // 这里不会执行
  }).catch(data => {
    console.log('10')
  })
  new Promise((resolve, reject) => {
    console.log('2')
    resolve();
    console.log('3')
  }).then(data => {
    console.log('7')
  }).then(data => {
        console.log('11')
      }).then(data => {
        console.log('12')
      }).then(data => {
        console.log('13')
      });
}, 0);

==========
Promise.reject(23).finally(r => {
  console.log('r', r) // 这里永远是undefined,因为finally回调本来就不会传参数
  return 50
}).catch(e => {
  console.log('e', e) // 23
  return 45
})
// 如果finally 放到catch后边也会执行,同样的结果

var const let 的区别和联系
1. var 声明的变量默认声明到全局上,let 作用域, 实现一个作用域
2. 用var 声明的变量会导致变量提升 var function, 用let声明的变量会绑定到当前作用域  暂存死区。
3. var 命令的变量可以重复; 使用let 可以保证我的代码命名不重复。
4. var 声明的变量可以更改,let也行,但是const不行。
var a = 1;
function aaa() {
  console.log(a); // undefined
  var a = 2;
}
aaa();
--------
var a = 1;
function aaa() {
  console.log(a); // Error
  let a = 2;
}
aaa();
----
let aa = 123;
function fun() {
  console.log(aa); // 123
}
fun()
-----
var a = 123;
var obj = {
  a: 456,
  fun: function () {
    // console.log(this.a)
    function test() {
      console.log(this.a, this) // 123, Window
    }
    test();
  }
}
obj.fun();
使用箭头函数应注意什么
1.用了箭头函数,this就不是指向window,而是父级(指向是可变的)
2、不能够使用arguments对象
3、不能用作构造函数,这就是说不能够使用new命令,否则会抛出一个错误
4、不可以使用yield命令,因此箭头函数不能用作 Generator 函数
js中我们在调⽤函数的时候经常会遇到this作⽤域的问题,这个时候ES6给我们提箭头函数。
1、 箭头函数是匿名函数不能作为构造函数,不能使用new
2、 箭头函数不绑定arguments,取而代之用rest参数…解决,
3this指向不同,箭头函数的this在定义的时候继承自外层第一个普通函数的this
4箭头函数通过call()或apply()调用一个函数,只传入了一个参数,this并没有影响.
5箭头函数没有prototype(原型),所以箭头函数本身没有this
6、 箭头函数不能当做Generator函数,不能使用yield关键字、
7、 写法不同,箭头函数把function省略掉了 ()=> 也可以吧return 省略调 写法更简洁
8、箭头函数不能通过call()、apply()、bind()方法直接修改它的this指向。
set 和map 是es6中新的数据类型 都不能放重复的

Map是键值对,Set是值的集合,当然键和值可以是任何的值

let set = new Set([1,2,3,3,2,1])
set.keys()
set.values()
set.entries()
set.clear()
set.delete()
set.has
set.add
------
let map = new Map(); // hash 表
map.set({a:1},123);
map.set('bbq',456);
console.log(map)
Object.defineProperty
let obj = {}
let val = '';
Object.defineProperty(obj,'a',{
    configurable:true, // 是否可删除
    // writable:true, // 是否可写,
    enumerable:true, // for in 原型上的方法
    get(){
        return val
    },
    set(value){
        val = value;
    }
    // 默认设置的值是不可枚举的
})
//=== 在看下Proxy
let obj = {
    a: 1,
    b: 2
};
let proxyObj = new Proxy(obj, {
    get: function (target, attr, receiver) {
        //console.log(receiver.attr); // 1
        return target[attr]; // obj["a"]
    },
    set: function (target, attr, value) {
        target[attr] = value;
    }
}
Object.getOwnPropertyDescriptor({aaa: 123}, 'aaa')
{value: 123, writable: true, enumerable: true, configurable: true}
Object.getOwnPropertyNames({aaa: 123})
['aaa']
Reflect

Object.defineProperty里面有部分的对象的方法 放到 Reflect 功能基本一致
Proxy 中能代理的方法 Reflect 都可以实现

1. get /set
Reflect.set(obj,'name','zf'); // obj.name = zf;
2. has
Reflect.has({a:1},'a')
3. defineProperty
Reflect.defineProperty(obj,'a',{
    value:100
})
4. getOwnPropertyDescriptor
Reflect.getOwnPropertyDescriptor(obj,'a')
5. ownKeys
let obj = {
    a:1,
    [Symbol()]:1
};
console.log(Object.getOwnPropertyNames(obj)); 这个方法和Object.keys一样,数组和object都是可以使用
console.log(Object.getOwnPropertySymbols(obj));
console.log(Reflect.ownKeys(obj))
6. Reflect.setPrototypeOf , Reflect.getPrototypeOf
7. 函数的apply方法 bind call apply
const fn = function(a,b){ // apply 支持多个参数传参
    console.log(this,a,b);
}
Reflect.apply(fn,1,[2,3]); // 用原型上的apply方法
8. construct
class XXX{
    constructor(name){
        this.name = name
    }
}
let xxx =  Reflect.construct(XXX,['zf']);
console.log(xxx); // new
9. deleteProperty
10. preventExtensions
11. isExtensible 
Symbol
Symbol("zf")
Symbol.for("zf")
Symbol.keyFor
// 元编程 可以改变js源代码的功能 改变js原有的功能
Symbol.hasInstance
Symbol.toPrimitive
Symbol.toStringTag
Symbol.species 控制衍生对象的类的构造函数
Symbol.unscopables  不在with中使用
Symbol.isConcatSpreadable

Symbol.species

队列 排队 先进先出
栈的特点就是先进后出 代码执行的时候结构就是个栈 (执行上下销毁的时候 是从里向外的)
链表 单向链表 双向链表 循环链表
集合 Set
二叉树 二叉查找树 数据存储方式 小的放左边 大的放右边
图 邻接表 (图 树直接的节点产生了关联就是图)
浏览器的事件环eventLoop
  • 微任务: promise.then ,MutationObserver
  • 宏任务:script ,ajax , 事件,requestFrameAnimation, setTimeout ,setInterval ,setImmediate (ie下),MessageChannel ,UI rendering。

浏览器的事件eventLoop 和 node 11 版本 表现和浏览器一致
而Vue.$nextTick
先查看你是否支持promoise, 用Promise.resolve。
MutationObserver
setImmediate // 只在ie下采用
setTimeout

node的事件环eventLoop

node的微任务 process.nextTick
node的宏任务又分为六个阶段

    ┌───────────────────────┐
┌─> │        timers         │ 本阶段执行setTimeout() 和 setInterval() 
│   └──────────┬────────────┘
│   ┌──────────┴────────────┐
│   │     I/O callbacks     │ 这个阶段执行一些诸如TCP错误之类的系统操作的回调
│   └──────────┬────────────┘
│   ┌──────────┴────────────┐
│   │     idle, prepare     │ 只内部使用
│   └──────────┬────────────┘      ┌───────────────┐
│   ┌──────────┴────────────┐      │   incoming:   │
│   │         poll          │  <───┤  connections, │ 获取新的 I/O 事件,查找已经到时的定时器
│   └──────────┬────────────┘      │   data, etc.  │
│   ┌──────────┴────────────┐      └───────────────┘
│   │        check          │ setImmediate()
│   └──────────┬────────────┘
│   ┌──────────┴────────────┐
└──-┤    close callbacks    │ 关闭事件的回调 socket.close事件
    └──────────────────────—┘
npm版本问题

npm采用了semver规范作为依赖版本管理方案。semver 约定一个包的版本号必须包含3个数字

  • MAJOR 对应大的版本号迭代,做了不兼容旧版的修改时要更新 MAJOR 版本号
  • MINOR 对应小版本迭代,发生兼容旧版API的修改或功能更新时,更新MINOR版本号
  • PATCH 对应修订版本号,一般针对修复 BUG 的版本号
range含义
^2.2.1指定的 MAJOR 版本号下, 所有更新的版本匹配 2.2.3, 2.3.0; 不匹配 1.0.3, 3.0.1
~2.2.1指定 MAJOR.MINOR 版本号下,所有更新的版本匹配 2.2.3, 2.2.9 ; 不匹配 2.3.0, 2.4.5
>=2.1版本号大于或等于 2.1.0匹配 2.1.2, 3.1
<=2.2版本号小于或等于 2.2匹配 1.0.0, 2.2.1, 2.2.11
1.0.0 - 2.0.0版本号从 1.0.0 (含) 到 2.0.0 (含)匹配 1.0.0, 1.3.4, 2.0.0

预发版:

  • alpha(α):预览版,或者叫内部测试版;一般不向外部发布,会有很多bug;一般只有测试人员使用。
  • beta(β):测试版,或者叫公开测试版;这个阶段的版本会一直加入新的功能;在alpha版之后推出。
  • rc(release candidate):最终测试版本;可能成为最终产品的候选版本,如果未出现问题则可发布成为正式版本。
npm 发布beta版本
git tag
常见的依赖项

dependcies
devDepencies
npm install --production 可以只安装dependcies
peerDependencies 会提示你安装缺少的模块 默认要求带版本,比如你写的插件是基于 react的这里可以提示用必须装react
bundleDependencies npm pack
optionalDependencies 如果找不到 不会影响npm的下载

peerDependencies
本地安装
npm install jquery --save / -S
npm install webpack --save-dev / -D
默认不给–save表示安装到当前的dependencies 表示是上线和开发的时候都需要
devDependencies 开发依赖 上线的时候不需要
有一天把模块发布了 别人会安装你的发布的模块,如果你的模块被别人安装,你的模块中的dependencies会默认一起下载,devDependencies不会下载的
npm link

全局安装 (bin) 比如:npm i http-server -g
库同步到全局,而且bin也同步到全局
npm unlink -g pka-name 就可以取消全局链接啦

全局安装之后会有软链接
/usr/local/bin/http-server -> /usr/local/lib/node_modules/http-server/bin/http-server
/usr/local/bin/hs -> /usr/local/lib/node_modules/http-server/bin/http-server
我要写一个全局包,npm link链接,创建快捷方式指向我的文件
"bin": {
    "lesson-zf": "./bin/www",
    "lz":"./bin/www"
},
而当npm link执行后,
/Users/dance/.npm-global/bin/lesson-zf -> /Users/dance/.npm-global/lib/node_modules/lesson-zf/bin/www
/Users/dance/.npm-global/bin/lz -> /Users/dance/.npm-global/lib/node_modules/lesson-zf/bin/www
/Users/dance/.npm-global/lib/node_modules/lesson-zf -> /Users/dance/Desktop/珠峰-2019-06-整理/我学习老的代码1/10.npm1

scripts 配置执行的脚本
即时没有全局安装mime, 但是只要node_modules下边有。当时在npm run test 也是可以执行mime的全局命令的,因为当执行npm run 的时候 他会把当前目录node_modules/.bin也拷贝到当前的系统的path中,所以npm run 可以执行.bin下的文件/
 "scripts": {
      "test": "mime"
  },

package.json、node_modules、package-lock.json是什么?

package.json browserslist
npx

可以直接执行node_modules/.bin文件 不需要在去配置scriprts
如果模块不存在可以安装 ,安装完是有后还会自己销毁,避免安装全局模块
npx create-react-app project-name

发布包

npm publish

nrm nvm npm可以切换源

npm install nrm -g
nrm use npm

协议问题

在这里插入图片描述

Npm 的各种命令集合 博客
npm脚本命令npm run script的使用 博客

进程,线程

线程是操作系统能够进行运算调度的最小单位, 一个进程可能有多个线程,如果一个线程死亡,那么会影响同进程其他线程的死亡。
一个进程中可以有多个线程,比如渲染线程、JS 引擎线程、HTTP 请求线程等等,进程表示一个程序,线程是进程中的单位 主线程只有一个。
1个进程可以占用1核cpu。
多线程在单核cpu中其实也是顺序执行的,不过系统可以帮你切换那个执行而已,没有提高速度。
多个cpu的话就可以在多个cpu中同时执行。
单线程优点:解决切换上下文时间,锁的问题,节省内存 (多线程)
node 主进程,在开多个子进程,里面包含着一个线程

这里可以查看浏览器的进程和线程管理

pm2切换的是进程

PWA

—PWA(Progressive Web App,渐进式网页应用,逐渐接近原生app的web app)是一种理念,使用多种技术来增强web app的功能,可以让网站的体验变得更好,能够模拟一些原生功能,比如通知推送。在移动端利用标准化框架,让网页应用呈现和原生应用相似的体验。
—PWA不能包含原生OS相关代码。PWA仍然是网站,只是在缓存、通知、后台功能等方面表现更好。Electron程序相当于包裹OS原生启动器(Launcher)的网站,未来,许多Electron程序可能转化为PWA。
【PWA学习】1. 初识 PWA
【PWA学习】2. 使用 Manifest, 让你的 WebApp 更 “Native”
【PWA学习】3. 让你的 WebApp 离线可用
【PWA学习】4. 使用 Push API 实现消息推送
【PWA学习】5. 使用 Notification API 来进行消息提醒
PWA学习】6. 使用 Service Worker 进行后台同步


缓存

强制缓存和对比缓存
memory缓存 disk缓存(百度页面为力,重复刷新的话,大部分memory 缓存,如果关掉所有的百度页面,打开新的页面,disk缓存,),
Service Worker离线缓存实践 +fecth+ cacheApi 一起使用 Service Worker的应用
ServiceWorker 介绍
service worker代码地址
Service Worker的应用
服务端主动推送
深入 ServiceWorker,消息推送,后台同步,一网打尽
一般serviceworker
html5离线缓存 已经废除了

如果设置强制缓存 首页是不会缓存的,访问的页面如果有强制缓存 则不会在发起请求。但是他的状态为200,from memo,from disk。
res.setHeader(‘Cache-Control’, ‘max-age=10’)。
// 旧的缓存 Expires
res.setHeader(‘Expires’,new Date(Date.now()+10*1000).toGMTString());
// 新的对比缓存 Etag ifModifiedSince
ifNoneMatch !== Etag || ifModifiedSince !== lasModified

本地存储

localStorage,sessionStorage,session,cookie,indexDB,cacheApi
cookie 4k
localStorage 5M
indexDB 250M

indexDB
cacheApi
加密算法
const crypto = require('crypto');

crypto.createHash('md5').update(r).digest('base64')
crypto.createHmac('sha1','ke1').update('123456').digest('base64')
--- md5 加密
1. 不可逆
2. 相同内容摘要的结果相同,有一点不同出来的结果完全不同
3. 摘要的结果长度相同
压缩方法

zlib.createGzip()
zlib.createDeflate()
gzip压缩 重复率越高 压缩越高

response.setHeader('Content-Encoding','gzip');
raw.pipe(zlib.createGzip()).pipe(response);
协议分层(OSI协议分层)

(物,数),网,传,(会,表,应)

  • 应用层 HTTP,FTP,DNS (与其他计算机进行通讯的一个应用服务,向用户提供应用服务时的通信活动)

  • 传输层 TCP(可靠) UDP 数据传输 (HTTP -> TCP DNS->UDP),TLS与SSL

  • 网络层 IP 选择传输路线 (通过ip地址和mac地址)(使用ARP协议凭借mac地址进行通信)

  • 链路层 网络连接的硬件部分
    在这里插入图片描述

tcp的三次握手和四次挥手

详解TCP、HTTP中的保活机制 | Keepalive和Keep-Alive

HTTP特点

1http是不保存状态的协议,使用cookie来管理状态 (登录 先给你cookie 我可以看一下你有没有cookie)
2为了防止每次请求都会造成无谓的tcp链接建立和断开,所以采用保持链接的方式 keep-alive
3以前发送请求后需要等待并收到响应,才能发下一个,现在都是管线化的方式 (js css 可以并发请求 6 2) cdn

HTTP缺点

1通信采用明文
2不验证通信方的身份
3无法验证内容的完整性 (内容可能被篡改)
4通过SSL(安全套阶层)建立安全通信线路 HTTPS (超文本传输安全协议)

HTTP方法 (get post 简单请求) Resful风格

  • GET:获取资源 /user?
  • POST:传输实体主体 请求体中
  • PUT:来传输文件
  • HEAD: 获取报文首
  • DELETE: 删除文件
  • OPTIONS:询问支持的方法 跨域 如果默认发送的是get/post 不会发送options的 ““复杂请求””,但是get,post
    get /post (a:1) headers:{a:1} put / delete 复杂的请求 , option如何有特殊的header的时候,也会有刺探请求。
    REST API Resful风格 根据路径和不同的方法 就能确定对资源进行什么操作
    跨域是浏览器之前的 服务器之间没有跨域问题 反向代理 、后端设置cors

c.com-> d.com OPTIONS 非简单请求会发送options (options 直接返回ok就可以了)
delete 的参数可以放在url中,也可以放在body里边都行。

get 可以在body传参,但是浏览器不允许,服务端允许
put参数通过body传输
delete参数可以在url中,也可以通过body中。

HTTP状态码 (发请求 命令行 curl命令) 服务端

curl命令行工具 postman
1xx 信息性状态码 websocket upgrade
2xx 成功状态码 200 204(没有响应体) 206(范围请求 暂停继续下载) 获取网页的部分请求
3xx 重定向状态码 301 302 303 post -> get 304(删除报文主体 在次发送请求) 307 (不会从POST转为GET)
4xx 客户端错误状态码 400 401 403 404 405 方法不允许
5xx 服务端错误状态码 500 503

502
502 直接原因:是报错类型的网关,连接超时,服务器方面无法给于正常的响应,产生此类报错。
可能原因:
1、服务端网络拥堵,访问拥堵。即多个用户同时访问服务器,导致服务器过载无法及时反馈用户的请求,出现的连接超时的情况;
2、用户本地电脑网络拥堵,用户本地的访问的网址过多,导致网页加载缓慢。当网页加载的数据不足以展示内容时,就会出现502报错;
3、网络流量网络拥堵,网络拥堵。就是当前用户所使用的网络存在多人访问,无法正常处理所有用户的请求。
4、原始服务器无法正常工作:服务器停机,过载,连接量太多。
5DNS 缓冲。域名/CDN问题:域无法正确确定IP地址时,会出现此问题。
6 请求被防火墙阻止
7 开了代理
http keep-alive
HTTP的报文(详解)
请求报文: 请求行,请求头,请求体
响应报文:状态行,响应头,响应体。
URI和URL

URI(Uniform Resource Identifier)是统一资源标识符,在某个规则下能把这个资源独一无二标示出来,比如人的身份证号
统一资源定位符,表示资源的地点,URL时使用浏览器访问WEB页面时需要输入的网页地址

报文应用

Content-Encoding : gzip压缩 form-data: 多部分对象集合 上传文件
range: 范围请求 206 accept-language:内容协商 前端控制 后端控制
host:单主机多域名 304 http缓存
referer:访问来源 防盗链 proxy:代理、网关和隧道
user-agent:用户内核 安全相关的头: X-Frame-Options、X-XSS-Protection (安全 csrf xss https 加密)

HTTP有哪些Header

请求的
host
origin
user-agent
referer
cookie
Connection: keep-alive
Content-Encoding: br
Transfer-Encoding: chunked
Content-Type: text/html;charset=utf-8
Accept: text/html,application/xhtml+xml
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cache-Control: no-cache
------返回的
Server
Access-Control-Allow-Origin
Connection: keep-alive
Content-Encoding: br
Content-Type: text/html;charset=utf-8
------缓存相关的
Cache-Control
Date
ETag
if-no-match
If-Modified-Since
Last-Modified

请求网址,状态码,请求方法,ip地址,

  • 缓存Header
    • 强:Cache-Control && Expires
    • 对比:Last-Modified & If-Modified-Since / ETag & If-None-Match
  • 跨域:access-control-allow-origin
  • connect, Content-Type - Transfer-Encoding content-length
  • 压缩:Content-Encoding : gzip
  • 范围请求:range
  • cookie
  • 用户内核:user-agent
  • 单主机多域名:host
  • servers服务器
  • 防盗链:referer (防止盗用链接)
  • 多语言:accept-language
  • 文件上传:Content-Type:multipart/form-data
  • 文件下载:Content-Description
http https的区别和联系。

https: 利用对称加密和非对称加密。

##### 用户发起网站请求前
1. CA将自己的证书公钥N交给各浏览器厂商,厂商将证书配置到各自浏览器。
2. 网站将自己的证书(身份凭证)交给CA,让CA使用私钥N对证书进行签名,CA签名后发回给网站。在这个过程中,CA需要核实网站的真实性,网站发给CA签名的也不仅只是证书,其中网站证书(含网站URL)和网站公钥是必要的,还会包含一些其他信息一同签名。
3. 浏览器用户向网站请求安全连接,网站把CA签名后的证书发给用户,浏览器会根据证书信息,检查是哪个CA做的签名,从浏览器自带的CA证书中找到对应公钥N验证。
4. 如果验证通过,就证明了网站身份的可靠性,用户可以通过网站提供的公钥M进行信息加密(包含网站协商后续对称加密的密钥S的key)。

###### 过程: 用户aaa访问某网站xxx
1. aaa发起请求到xxx网站。
2. xxx返回了自己的公钥ggg和AC给xxx网站的加密后的信息。
3. aaa拿到这些信息用浏览器内置的AC公钥解密。告诉aaa这个公钥和信息对得上,是正确的。
5. aaa自己生成一个对称加密的密钥sss,然后用这个公钥ggg加密这个私钥发给xxx.
6. 只有xxx的私钥可以解开这个公钥ggg的信息获取到这个私钥sss。
7. 然后aaa和xxx之间的联系就是用这个对称加密的密钥sss.


#### 分析 如果aaa和xxx之间存在代理服务器ttt,如果保证ttt解密不了这些数据。
1. aaa用xxx的公钥ggg加密私钥sss,这个信息只有xxx网站自己的私钥可以解密。即使ttt获取到了,因为不知道ggg对应的私钥无法解密。所以私钥ggg永远无法获取到sss,永远不知道他们之间在说什么。
#### 分析 代理服务器ttt为什么不能在aaa获取证书ggg和aaa从Ac求证的过程中做手脚呢。
1. Ac的私钥浏览器都有内置的。利用Ac加密的信息只有Ac的公钥可以解密。这样ttt永远不知道用户aaa在和ac之间传递的什么信息。
2. 而当Ac浏览器返回信息ok,ok的时候,虽然代理服务器ttt知道说的ok但是永远不知道在说什么是ok的。

[HTTPS工作原理](https://blog.csdn.net/keeppractice/article/details/126975243)

为什么如此安全的https协议却仍然可以被抓包呢?,handler机制

https中间人攻击
自建CA证书搭建https服务器

造成中间人劫持的原因是 没有对服务端证书及域名做校验或者校验不完整,预防方式有两种
1 、针对安全性要求比较高的 app,可采取客户端预埋证书的方式锁死证书,只有当客户端证书和服务端的证书完全一致的情况下才允许通信,如一些银行类的app,但这种方式面临一个问题,证书过期的问题,因证书有一定的有效期,当预埋证书过期了,只有通过强制更新或者要求用户下载证书来解决。
2. 针对安全性要求一般的app,可采用通过校验域名,证书有效性、证书关键信息及证书链的方式
以volley为例,重写HTTPSTrustManager 中的checkServerTrusted 方法,同时开启域名强校验

HTTP/1 HTTP/2 HTTP/3 对比分析
http2

http2

目标:改善用户在使用Web时的速度体验。
主要基于SPDY协议。它是Google开发的基于TCP协议的应用层协议。目标是优化HTTP协议的性能,通过压缩、多路复用和优先级 等技术,缩短网页的加载时间并 提高安全性。SPDY协议的核心思想是尽量减少TCP连接数。SPDY并不是一种用于替代HTTP的协议,而是对HTTP协议的增强。
特点:
1. 二进制传输:二进制帧层,指HTTP消息在客户端和服务端如何封装和传输。与HTTP1.x的采用的换行符分隔文本不同,HTTP/2 消息被分成很小的消息和frame,然后每个消息和frame用二进制编码。
2. 多路复用:所谓多路复用,即在一个TCP连接中存在多个流,即可以同时发送多个请求。在客户端帧乱序发送,到对端后再根据每个帧首部的流标识符重新组装。
3. Header压缩:使用 HPACK(HTTP2头部压缩算法)压缩格式对传输的header进行编码。并在两端维护了索引表,用于记录出现过的header,后面在传输过程中就可以传输已经记录过的header的键名,对端收到数据后就可以通过键名找到对应的值。
4. 服务器推送:在 HTTP2.0 中,服务端可以在客户端某个请求后,主动推送其他资源。

http1发送的内容是文本,http2

http1.0~2.0的区别和https实现原理
htttp3.0

http2 很流行,但是http2主动推送的功能很少人使用因为兼容性很不好,参考
只能通过Link 添加主动push

多语言

// Accept-Language: zh-CN;q=1,zh;q=0.9,en;q=0.8
// 客户端可以自行配置语言 .不同的语言组成不同的网站

跨域 博客2

其实CORS和Access-Control-Allow-Origin的方式是一样的,都是需要后端对应的处理的。
1.通过jsonp跨域
2.通过iframe跨域
(1)只通过iframe展示界面,不进行父子页面间的数据传递。
(2)进行父子页面的数据传递。要求父和子必须有相同的主域名,并且设置document.domain相同,
这个时候有三种方法交换数据:
a.父通过id或者子通过.parent之类的方法
b.通过cookie传递值。
3. window.name来进行跨域
在这里插入图片描述
4.使用HTML5中新引进的window.postMessage方法来跨域传送数据
在这里插入图片描述
5.CORS 需要服务器设置header :Access-Control-Allow-Origin。
6.nginx反向代理 这个方法一般很少有人提及,但是他可以不用目标服务器配合,不过需要你搭建一个中转nginx服务器,用于转发请求


node

node 优势
它是一个JavaScript 运行环境
依赖于Chrome V8 引擎进行代码解析
事件驱动(Event-Driven)
非阻塞I/O(non-blocking L/O)
单进程,单线程
轻量,可伸缩,适于实时数据交互应用
垃圾回收机制
跨平台
node 日志log4js
node process, argv, env, module

node核心模块

process, process.cwd()  process.nextTick process.pid process.argv commander process.env 环境变量
require,exports,module,__filename,__dirname (可以直接在模块内部被访问)
__dirname  绝对路径
events(on, off, emit, once)
path(path.resolve(__dirname,'./note.md'))
fs
- util util.inherits
-      util.promisify
- buffer(from,alloc,slice,sBuffer)
------------------------util.inherits
function Parent(){}
function Child(){
    Parent.call(this);
}   
------------------------util.inspect可以显示隐藏属性
console.log(util.inspect(Array.prototype,{showHidden:true}));
console.log(util.isPrimitive); // Symbol
------------------------node首错改为then
let ncp = require('ncp');
let path = require('path');
let util = require('util');
// cp -r
const promisify = fn => (...args)=>{
    return new Promise((resolve,reject)=>{
        fn(...args,function(err){ // 只针对node  因为node是这种error-first的形式
            if(err) reject(err);
            resolve();
        });
    })
}
ncp = promisify(ncp);
(async ()=>{
    await ncp(path.resolve(__dirname,'note.md'),path.resolve(__dirname,'note1.md'));
    console.log('拷贝成功')
})();
编码

ASCII(美国) 默认 就一个一个字节来表示是一个字母或者符号,1个字节 有8个bit 最大 是 8个1 => 10进制 255
gbk 用两个字节来表示中文
base64就是编码转化 不需要发送http请求。大小会比以前大
gb2312 第一位大于127的我认为+ 第二位 就是一个中文 255 - 127 * 255
utf8 中文采用3个字节来表示,node里默认值支持utf8格式,可以采用别的模块来实现。

let str = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
str+= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.toLowerCase();
str+= '0123456789+/'
进制转换

(0xff).toString(2)
将任意进制转化成10进制 parseInt(‘0xff’, 16);

fs对象
64k以下的文件
fs.readFile,fs.writeFile
64k以上的文件
fs.open,fs.read,fs.write
-----
fs.appendFileSync('1.js','123')
fs.writeFileSync('1.js','123',{flag:'a'})
#### 文件的操作
fs.readFile  fs.existSync fs.access
fs.writeFile fs.copyFile
fs.rename fs.unlink  增删改查
#### 文件夹的操作
mkdir rmdir readdir  创建目录  删除目录  读取目录
isFile 
#### 
fs.createReadStream
fs.createWriteStream
Koa

洋葱的中间件模型
koa的中间件模式与express的是不一样的,koa是洋葱型,express是直线型
核心的四个文件
Application,context, request, response
把所有的属性挂到context上边,contest挂到response上,request上。

app.listen
app.handleRequest
app.compose
const logger = ()=>{
    return new Promise((resolve,reject)=>{
      setTimeout(() => {
        console.log('logger');
        resolve(); 
      }, 1000);
    })
}
app.use(async (ctx,next)=>{ // 上下文
  await logger();
  return next();
});

// Koa路由 routes
app.use(index.routes(), index.allowedMethods())
app.use(users.routes(), users.allowedMethods())

express 应用
const express = require('./express');
const path = require('path');
// 模板引擎 koa-views
const app = express();
app.set('views','view'); // 将默认的存放路径更改
app.set('view engine','.html');// 默认使用可以使用html文件

app.engine('.html',require('ejs').__express);//如果遇到.html用ejs来渲染
app.get('/',function(req,res){
   console.log(req.a)
   res.sendFile(path.resolve(__dirname,'package.json'))
   // res.send(301);
})

app.listen(3000);
前端上传后端的几种content-type

file
字符串
application/x-www-form-urlencoded
multipart/form-data(文件上传)
application/json

url、base64、blob相互转换方法
Blob是File的父类。

下载的三种方式
  1. a标签的download
  2. Blob 格式下载,模拟a标签download
    利用URL.createObjectURL为blob对象创建临时的URL在这里插入图片描述
  3. blob的方式下载,base64的(用FileReader去接,)方式下载
    利用fileReader的readAsDataURL,将blob转为base64,也可以将file转化为base64.
    在这里插入图片描述
文件上传的几种方式

File对象继承自Blob对象,Blob 是File的父类。
File对象和FormData之间的转换 let formData = new FormData(); formData.append(‘file’, file);
web前端文件上传可选择的4种方式

方式一:
<input id="fileInput" type="file" accept="image/*" multiple onchange="selectFile()" />
方法二: 
<form action="url" enctype="multipart/form-data" method="post">
  <input name="fileInput" type="file" />
  <button type="submit">提交</button>
</form>
方法三:
const formData = new FormData()
formData.append('file', file, file.name)

const xhr = new XMLHttpRequest()
xhr.open('POST', 'url', true)
xhr.send(formData)
方法4: 拖拽,粘贴

webpack

webpack7大作用

代码转换, 文件优化, 代码分割,模块合并,自动刷新,代码校验,自动发布

webpack核心概念

Entry:入口
Module:模块
Chunk:代码块
Loader:模块转换器
Plugin:扩展插件
Output:输出结果

常用loader

css-loader
style-loader
file-loader
url-loader 当图片小于limit的时候会把图片BASE64编码,大于limit参数的时候还是使用file-loader 进行拷贝
html-withimg-loader
postcss-loader autoprefixer

常用插件

html-webpack-plugin

+       new HtmlWebpackPlugin({
+            template:'./src/index.html',//指定模板文件
+            filename:'index.html',//产出后的文件名
+            inject:false,
+            hash:true,//为了避免缓存,可以在产出的资源后面添加hash值
+            chunks:['common','index'],
+            chunksSortMode:'manual'//对引入代码块进行排序的模式
+        }),

mini-css-extract-plugin 分离CSS
terser-webpack-plugin 压缩JS
optimize-css-assets-webpack-plugin 压缩CSS

module.exports = {
    mode: 'production',
    optimization: {
        minimizer: [
           /*  new UglifyJsPlugin({
                cache: true,//启动缓存
                parallel: true,//启动并行压缩
                //如果为true的话,可以获得sourcemap
                sourceMap: true // set to true if you want JS source maps
            }), */
            new TerserPlugin({
                 parallel: true,
                 cache: true
            }),
            //压缩css资源的
            new OptimizeCSSAssetsPlugin({
                 assetNameRegExp:/\.css$/g,
                 //cssnano是PostCSS的CSS优化和分解插件。cssnano采用格式很好的CSS,并通过许多优化,以确保最终的生产环境尽可能小。
                 cssProcessor:require('cssnano')
            })
        ]
    },

outputPath 输出路径
publicPath指定的是构建后在html里的路径
服务器代理
拷贝静态文件 copy-webpack-plugin
打包前先清空输出目录 clean-webpack-plugin
noParse
DefinePlugin
IgnorePlugin

文件指纹

Hash 每次都是新的
chunkhash 入口不变,值不变,那么只要我们不改动公共库的代码,就可以保证其哈希值不会受影响
contenthash

webpack es6=>es5

babel-loader @babel/core @babel/preset-env  @babel/preset-react
"@babel/plugin-proposal-decorators"
"@babel/plugin-proposal-class-properties"
默认情况下会被添加到每一个需要它的文件中。你可以引入 @babel/runtime 作为一个独立模块,来避免重复引入
babel-plugin-transform-runtime

webpack基础(四)——babel
babel是一个独立的工具,可以不在webpack中使用。
@babel/core:babel核心代码必须安装
@babel/cli:在命令行执行babel代码
一文搞懂 core-js@3、@babel/polyfill、@babel/runtime、@babel/runtime-corejs3 的作用与区别
core-js + regenerator-runtime = @babel/polyfill
@babel/runtime 抽离公共辅助函数,比如@babel/runtime 抽离公共辅助函数
@babel/runtime-corejs3 相当于 @babel/runtime + 不污染环境的 core-js@3
一种是 @babel/preset-env + @babel/runtime + core-js@3,普通的项目不怕污染全局环境可以用这个配置,这里的污染全局变量指的是类似polyfill, 直接引入所有缺少的比较Promise这种,全局随便用。
另外一种是 @babel/preset-env + @babel/runtime-corejs3,开发工具类库为了不污染全局环境可以用这个配置;

eslint-loader babel-eslint
devtool source-map
  • source-map 产生map文件,打包的js文件里面最下行有map文件的地址。功能最完全,但会减慢打包速度。
  • eval 生成代码 每个模块都被eval执行,每一个打包后的模块后面都增加了包含sourceURL
  • inline 不会生成独立的 .map文件,会以dataURL形式插入
  • cheap 忽略打包后的列信息,不使用loader中的sourcemap
  • module 没有列信息,使用loader中的sourcemap(没有列信息)
devtool:isDev?'cheap-module-eval-source-map':false

webpack打包第三方类库

13.1 直接引入 import _ from 'lodash';
13.2 插件引入
+ new webpack.ProvidePlugin({
+     _:'lodash'
+ })
13.3 expose-loader 
方法1: require("expose-loader?libraryName!./file.js");
方法2: { 
  test: require.resolve("jquery"), 
  loader: "expose-loader?jQuery"
}
方法3: require("expose-loader?$!jquery");
13.4 externals 如果我们想引用一个库,但是又不想让webpack打包,并且又不影响我们在程序中以CMD、AMD或者window/global全局等方式进行使用,那就可以通过配置externals。
+ externals: {
+         jquery: 'jQuery'//如果要在浏览器中运行,那么不用添加什么前缀,默认设置就是global
+ },
13.5 html-webpack-externals-plugin
const htmlWebpackExternalsPlugin= require('html-webpack-externals-plugin');
new htmlWebpackExternalsPlugin({
            externals:[
                {
                    module:'react',
                    entry:'https://cdn.bootcss.com/react/15.6.1/react.js',
                    global:'React'
                },
                 {
                    module:'react-dom',
                    entry:'https://cdn.bootcss.com/react/15.6.1/react-dom.js',
                    global:'ReactDOM'
                }
            ]
}) 

区分环境变量

生产环境
可能需要分离 CSS 成单独的文件,以便多个页面共享同一个 CSS 文件
需要压缩 HTML/CSS/JS 代码
需要压缩图片
开发环境
需要生成 sourcemap 文件
需要打印 debug 信息
需要 live reload 或者 hot reload 的功能…

多入口MPA

entryFiles + html-webpack-plugin

分析

speed-measure-webpack-plugin
webpack-bundle-analyzer

libraryTarget 和 library

output.library 配置导出库的名称
output.libraryTarget 配置以何种方式导出库,是字符串的枚举类型

webpack 优化


1.DllPlugin 在一个动态链接库中可以包含给其他模块调用的函数和数据
2.多进程处理 Happypack(这个库已经不推介了,现在用thread-loader )
3.webpack打包第三方类库, 通过externals配置来提取常用库,通过精简 resolve 配置,把 loader 应用的文件范围缩小。module.noParse,new webpack.IgnorePlugin(/./locale/, /moment/),减少不必要的loader处理exclude,减少 resolve 的解析
4.Tree Shaking 针对js,来剔除多余代码
一个模块可以有多个方法,只要其中某个方法使用到了,则整个文件都会被打到bundle里面去,tree shaking就是只把用到的方法打入bundle,没用到的方法会uglify阶段擦除掉
原理是利用es6模块的特点,只能作为模块顶层语句出现,import的模块名只能是字符串常量
5.HMR 提高开发效率
6.代码分割
多入口
动态导入和懒加载
提取公共代码
{
optimization: splitChunks
}
7.babel-loader开启缓存
8.多进程/多实例:并行压缩, uglifyjs-webpack-plugin 开启 paralle
小点
9.purgecss-webpack-plugin去除多余的css,分离css, mini-css-extract-plugin, 压缩css optimize-css-assets-webpack-plugin
10.压缩js并行压缩js uglifyjs-webpack-plugin
11.使用fast-sass-loader代替sass-loader

提升 webpack 构建速度
webpack性能优化–IngorePlugin、noParse 和 happyPack、ParallelUglifyPlugin
webpack性能优化全方案

手写webpack loader

----------------------url-loader
let loaderUtils = require('loader-utils');
let mime = require('mime');
function loader(source) {
  let { limit } = loaderUtils.getOptions(this);
  if (limit && limit > source.length) {
    return `module.exports="data:${mime.getType(this.resourcePath)};base64,${source.toString('base64')}"`
  } else {
    return require('./file-loader').call(this, source)
  }
}
loader.raw = true;
module.exports = loader;
----------------------file-loader
let loaderUtils = require('loader-utils');
function loader(source) {
  // file-loader 需要返回一个路径
  let filename = loaderUtils.interpolateName(this, '[hash].[ext]', { content: source});
  this.emitFile(filename, source); // 发射文件
  return `module.exports="${filename}"`
}
loader.raw = true; // 二进制
module.exports = loader;
----------------------css-loader
function loader(source) {
  let reg = /url\((.+?)\)/g;
  let pos = 0;
  let current;
  let arr = ['let list = []'];
  while (current = reg.exec(source)) { // [matchUrl,g]
    let [matchUrl, g] = current;
    //console.log(matchUrl, g)
    let last = reg.lastIndex - matchUrl.length;
    arr.push(`list.push(${JSON.stringify(source.slice(pos, last))})`);
    pos = reg.lastIndex;
    // 把 g 替换成 require的写法  => url(require('xxx'))
    arr.push(`list.push('url('+require(${g})+')')`);
  }
  arr.push(`list.push(${JSON.stringify(source.slice(pos))})`)
  arr.push(`module.exports = list.join('')`);
  return arr.join('\r\n');
}
module.exports = loader;
----------------------less-loader
let less = require('less');
function loader(source) {
  let css;
  less.render(source,function (err,r) { // r.css
    css = r.css; 
  });
  return css
}

module.exports = loader;
----------------------banner-loader
let loaderUtils = require('loader-utils');
let validateOptions = require('schema-utils');
let fs = require('fs');
function loader(source) {
  this.cacheable && this.cacheable()
  let options = loaderUtils.getOptions(this);
  let cb = this.async();
  let schema = {
    type:'object',
    properties:{
      text:{
        type:'string',
      },
      filename:{
        type:'string'
      }
    }
  }
  validateOptions(schema, options,'banner-loader');
  if(options.filename){
    this.addDependency(options.filename); // 自动的添加文件依赖
    fs.readFile(options.filename,'utf8',function (err,data) {
      cb(err, `/**${data}**/${source}`);
    });
  }else{
    cb(null, `/**${options.text}**/${source}`);
  }
}
module.exports = loader;
loader使用例子
{
   test:/\.js$/,
   use:{ /**珠峰*/
     loader: 'banner-loader',
     options:{
       text:'珠峰',
       filename:path.resolve(__dirname,'banner.js')
     }
   }
 }

手写webpack pluign

---------DonePlugin
class DonePlugin {
  apply(compiler){ // compiler.hooks
    console.log(1)
    compiler.hooks.done.tap('DonePlugin',(stats)=>{
      console.log('编译完成~~~')
    })
  }
}
module.exports = DonePlugin
---------FileListPlugin
class FileListPlugin{
  constructor({ filename}){
    this.filename = filename;
  }
  apply(compiler){
    // 文件已经准备好了 要进行发射
    // emit
    compiler.hooks.emit.tap('FileListPlugin', (compilation)=>{
      let assets = compilation.assets;
      let content = `## 文件名    资源大小\r\n`
      //[ [bundle.js,{}],[index.html,{}]]
      Object.entries(assets).forEach(([filename,statObj]) => {
        content += `- ${filename}    ${statObj.size()}\r\n`
      });
      // 资源对象
      assets[this.filename] = {
        source(){
          return content;
        },
        size(){
          return content.length;
        }
      };
    })
  }
}
module.exports = FileListPlugin
--------------UploadPlugin
let path = require('path');
let qiniu = require('qiniu');
class UploadPlugin{
  constructor(options){
    let { bucket = '', domain = "", accessKey = '', secretKey = '' } = options;
    let mac = new qiniu.auth.digest.Mac(accessKey, secretKey);
    let putPolicy = new qiniu.rs.PutPolicy({ scope: bucket });
    this.uploadToken = putPolicy.uploadToken(mac);
    let config = new qiniu.conf.Config();
    this.formUploader = new qiniu.form_up.FormUploader(config);
    this.putExtra = new qiniu.form_up.PutExtra();
  }
  apply(compiler){
    compiler.hooks.afterEmit.tapPromise('UploadPlugin',(compliation)=>{
      let assets = compliation.assets;
      let promises = [];
      Object.keys(assets).forEach(filename=>{
        promises.push(this.upload(filename));
      })
      return Promise.all(promises);
    })
  }
  upload(filename){
    return new Promise((resolve,reject)=>{
      let localFile = path.resolve(__dirname, '../dist', filename)
      this.formUploader.putFile(this.uploadToken, filename, localFile, this.putExtra, function (respErr,
        respBody, respInfo) {
        if (respErr) {
          reject(respErr);
        }
        if (respInfo.statusCode == 200) {
          resolve(respBody);
        }
      });
    })
  }
}
module.exports = UploadPlugin

webpack 插件钩子

const {
SyncHook,
SyncBailHook,
SyncWaterfallHook,
SyncLoopHook,
AsyncParallelHook,
AsyncParallelBailHook,
AsyncSeriesHook,
AsyncSeriesBailHook,
AsyncSeriesWaterfallHook
} = require(‘tapable’);
Basic 不关心监听函数的返回值
Bail 保险式: 只要监听函数中有返回值(不为undefined),则跳过之后的监听函数
Waterfall 瀑布式: 上一步的返回值交给下一步使用
Loop 循环类型: 如果该监听函数返回true,则这个监听函数会反复执行,如果返回undefined则退出循环


webpack 小知识点

webpack中hash、chunkhash和contenthash
webpack的热加载原理

webpack5学习(四):css 处理

常用的配置

devServer:配置开发服务器
mode:development,production

Ast 抽象法语树

Acorn babylon还是 博客1
词法分析和语法分析
Webpack 是Acorn
Babel 使用的是babylon
可以用babylon和('@babel/types’),('@babel/traverse’),require('@babel/generator’)
也可以用let babel = require('@babel/core’)   require('babel-types’)
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值