zf-总结

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

打印:

===== 常规
webpack5的优点
AOP 装饰模式
发布订阅模式
观察者模式
浏览器的事件环eventLoop
node的事件环eventLoop
进程,线程
浏览器的进程线程
继承公共方法(必看)
PWA
缓存
本地存储
压缩方法
=====Promise 
解决的问题 3
promise的特点
promise.all
promise race
promise 中断
isPromise
promise执行顺序测试
=====网络:
三次握手,四次挥手
网络5HTTP有哪些Header
http特点,缺点,Resful风格,状态码
header有哪些
https的加密算法
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。

继承公共方法(必看)

方法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); // 继承公共属性
js的各种继承

[三次握手]

第一次
第一次握手:建立连接时,客户端发送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 #'));
    }
    // 判断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 #'));
    }
    // 判断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 #'));
    }
    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 {  }
---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);
    });
});
======
setTimeout(() => {
  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')
  });
}, 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

全局安装之后会有软链接
/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

协议问题

zf-总结_第1张图片

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代码地址
html5离线缓存 已经废除了

如果设置强制缓存 首页是不会缓存的,访问的页面如果有强制缓存 则不会在发起请求
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地址进行通信)

  • 链路层 网络连接的硬件部分
    zf-总结_第2张图片

tcp的三次握手和四次挥手

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

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 DNS 缓冲。域名/CDN问题:域无法正确确定IP地址时,会出现此问题。
3 请求被防火墙阻止
4 开了代理

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和一些信息。
3. aaa拿到这些信息去Ac权威机构证明一下(后边讲这个不被窃取的原理,这里其实是本地校验并未去ca机构认证,点下边的链接)。
4. 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协议却仍然可以被抓包呢?,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.0~2.0的区别和https实现原理

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来进行跨域
zf-总结_第3张图片
4.使用HTML5中新引进的window.postMessage方法来跨域传送数据
zf-总结_第4张图片
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类型

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

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,普通的项目不怕污染全局环境可以用这个配置,这里的污染全局变量指的是全部引入而不是按需引入。
另外一种是 @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’)

你可能感兴趣的:(px-zf-6,javascript,前端,typescript)