promise执行顺序测试
koa
node 进程守护,pm2
webpack loader plugin
打印:
===== 常规
webpack5的优点
AOP 装饰模式
发布订阅模式
观察者模式
浏览器的事件环eventLoop
node的事件环eventLoop
进程,线程
浏览器的进程线程
继承公共方法(必看)
PWA
缓存
本地存储
压缩方法
=====Promise
解决的问题 3
promise的特点
promise.all
promise race
promise 中断
isPromise
promise执行顺序测试
=====网络:
三次握手,四次挥手
网络5层
HTTP有哪些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); // 继承公共属性
[三次握手]
第一次
第一次握手:建立连接时,客户端发送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),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。 [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(面向切面编程)的主要作用是把一些跟核心业务逻辑模块无关的功能抽离出来,其实就是给原函数增加一层,不用管原函数内部实现
// 观察者模式(一个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('不开心了');
1. 回调嵌套 回调地狱
2. 错误捕获不好处理错误
3. 多个异步同步的问题 Promise.all,还是基于回调的方式的
1. Promise的概念 规范文档 promise A+ 规范
2. Promise 三个状态 等待 成功态 失败态
3. 只有等待态 才能变成成功 / 失败
4. 如果状态变化后不能在修改状态
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;
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;
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;
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);
}
}
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.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)
}
})
})
})
}
// race 赛跑 哪个快 用哪个 all是所有完成才完成
Promise.race = function(promises){
return new Promise((resolve,reject)=>{
for(let i = 0;i<promises.length;i++){
promises[i].then(resolve,reject); // 只要一个成功就成功
}
})
}
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);
});
function isPromise(obj) {
return !!obj && (typeof obj === 'object' || typeof obj === 'function') && typeof obj.then === 'function';
}
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
======
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后边也会执行,同样的结果
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参数…解决,
3、 this指向不同,箭头函数的this在定义的时候继承自外层第一个普通函数的this
4、 箭头函数通过call()或apply()调用一个函数,只传入了一个参数,对this并没有影响.
5、 箭头函数没有prototype(原型),所以箭头函数本身没有this
6、 箭头函数不能当做Generator函数,不能使用yield关键字、
7、 写法不同,箭头函数把function省略掉了 ()=> 也可以吧return 省略调 写法更简洁
8、箭头函数不能通过call()、apply()、bind()方法直接修改它的this指向。
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)
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']
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("zf")
Symbol.for("zf")
Symbol.keyFor
// 元编程 可以改变js源代码的功能 改变js原有的功能
Symbol.hasInstance
Symbol.toPrimitive
Symbol.toStringTag
Symbol.species 控制衍生对象的类的构造函数
Symbol.unscopables 不在with中使用
Symbol.isConcatSpreadable
Symbol.species
浏览器的事件eventLoop 和 node 11 版本 表现和浏览器一致
而Vue.$nextTick
先查看你是否支持promoise, 用Promise.resolve。
MutationObserver
setImmediate // 只在ie下采用
setTimeout
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采用了semver规范作为依赖版本管理方案。semver 约定一个包的版本号必须包含3个数字
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 |
预发版:
dependcies
devDepencies
npm install --production 可以只安装dependcies
peerDependencies 会提示你安装缺少的模块 默认要求带版本,比如你写的插件是基于 react的这里可以提示用必须装react
bundleDependencies npm pack
optionalDependencies 如果找不到 不会影响npm的下载
npm install jquery --save / -S
npm install webpack --save-dev / -D
默认不给–save表示安装到当前的dependencies 表示是上线和开发的时候都需要
devDependencies 开发依赖 上线的时候不需要
有一天把模块发布了 别人会安装你的发布的模块,如果你的模块被别人安装,你的模块中的dependencies会默认一起下载,devDependencies不会下载的
全局安装 (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是什么?
可以直接执行node_modules/.bin文件 不需要在去配置scriprts
如果模块不存在可以安装 ,安装完是有后还会自己销毁,避免安装全局模块
npx create-react-app project-name
npm publish
npm install nrm -g
nrm use npm
线程是操作系统能够进行运算调度的最小单位, 一个进程可能有多个线程,如果一个线程死亡,那么会影响同进程其他线程的死亡。
一个进程中可以有多个线程,比如渲染线程、JS 引擎线程、HTTP 请求线程等等,进程表示一个程序,线程是进程中的单位 主线程只有一个。
1个进程可以占用1核cpu。
多线程在单核cpu中其实也是顺序执行的,不过系统可以帮你切换那个执行而已,没有提高速度。
多个cpu的话就可以在多个cpu中同时执行。
单线程优点:解决切换上下文时间,锁的问题,节省内存 (多线程)
node 主进程,在开多个子进程,里面包含着一个线程
pm2切换的是进程
—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
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);
(物,数),网,传,(会,表,应)
应用层 HTTP,FTP,DNS (与其他计算机进行通讯的一个应用服务,向用户提供应用服务时的通信活动)
传输层 TCP(可靠) UDP 数据传输 (HTTP -> TCP DNS->UDP),TLS与SSL
网络层 IP 选择传输路线 (通过ip地址和mac地址)(使用ARP协议凭借mac地址进行通信)
1http是不保存状态的协议,使用cookie来管理状态 (登录 先给你cookie 我可以看一下你有没有cookie)
2为了防止每次请求都会造成无谓的tcp链接建立和断开,所以采用保持链接的方式 keep-alive
3以前发送请求后需要等待并收到响应,才能发下一个,现在都是管线化的方式 (js css 可以并发请求 6 2) cdn
1通信采用明文
2不验证通信方的身份
3无法验证内容的完整性 (内容可能被篡改)
4通过SSL(安全套阶层)建立安全通信线路 HTTPS (超文本传输安全协议)
c.com-> d.com OPTIONS 非简单请求会发送options (options 直接返回ok就可以了)
delete
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 直接原因:是报错类型的网关,连接超时,服务器方面无法给于正常的响应,产生此类报错。
可能原因:
1 原始服务器无法正常工作:服务器停机,过载,连接量太多。
2 DNS 缓冲。域名/CDN问题:域无法正确确定IP地址时,会出现此问题。
3 请求被防火墙阻止
4 开了代理
请求报文: 请求行,请求头,请求体
响应报文:状态行,响应头,响应体。
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 加密)
请求的
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地址,
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机制
造成中间人劫持的原因是 没有对服务端证书及域名做校验或者校验不完整,预防方式有两种
1 、针对安全性要求比较高的 app,可采取客户端预埋证书的方式锁死证书,只有当客户端证书和服务端的证书完全一致的情况下才允许通信,如一些银行类的app,但这种方式面临一个问题,证书过期的问题,因证书有一定的有效期,当预埋证书过期了,只有通过强制更新或者要求用户下载证书来解决。
2. 针对安全性要求一般的app,可采用通过校验域名,证书有效性、证书关键信息及证书链的方式
以volley为例,重写HTTPSTrustManager 中的checkServerTrusted 方法,同时开启域名强校验
HTTP/1 HTTP/2 HTTP/3 对比分析
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
// 客户端可以自行配置语言 .不同的语言组成不同的网站
其实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服务器,用于转发请求
它是一个JavaScript 运行环境
依赖于Chrome V8 引擎进行代码解析
事件驱动(Event-Driven)
非阻塞I/O(non-blocking L/O)
单进程,单线程
轻量,可伸缩,适于实时数据交互应用
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);
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的中间件模式与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())
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);
字符串
application/x-www-form-urlencoded
multipart/form-data(文件上传)
application/json
代码转换, 文件优化, 代码分割,模块合并,自动刷新,代码校验,自动发布
Entry:入口
Module:模块
Chunk:代码块
Loader:模块转换器
Plugin:扩展插件
Output:输出结果
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
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,开发工具类库为了不污染全局环境可以用这个配置;
devtool:isDev?'cheap-module-eval-source-map':false
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 的功能…
entryFiles + html-webpack-plugin
speed-measure-webpack-plugin
webpack-bundle-analyzer
output.library 配置导出库的名称
output.libraryTarget 配置以何种方式导出库,是字符串的枚举类型
–
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
----------------------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')
}
}
}
---------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
const {
SyncHook,
SyncBailHook,
SyncWaterfallHook,
SyncLoopHook,
AsyncParallelHook,
AsyncParallelBailHook,
AsyncSeriesHook,
AsyncSeriesBailHook,
AsyncSeriesWaterfallHook
} = require(‘tapable’);
Basic 不关心监听函数的返回值
Bail 保险式: 只要监听函数中有返回值(不为undefined),则跳过之后的监听函数
Waterfall 瀑布式: 上一步的返回值交给下一步使用
Loop 循环类型: 如果该监听函数返回true,则这个监听函数会反复执行,如果返回undefined则退出循环
webpack5学习(四):css 处理
devServer:配置开发服务器
mode:development,production
词法分析和语法分析
Webpack 是Acorn
Babel 使用的是babylon
可以用babylon和('@babel/types’),('@babel/traverse’),require('@babel/generator’)
也可以用let babel = require('@babel/core’) require('babel-types’)