前端知识总结
- 数据类型
- Proxy
- Promise
- AJAX
- 跨域
- Js循环
- JS对象遍历
- Websocket
- JS中this
- JS原型和原型链
- JS作用域、作用域链、闭包
- JS传参
- 严格模式strict
- JS方法
- JS深拷贝
- 防抖和节流
- js类创建
- 类的继承
- new原理
- http协议
- [JS异步 Event Loop模型](https://segmentfault.com/a/1190000018533261)
- [Node 定时器详解、事件循环机制](http://www.ruanyifeng.com/blog/2018/02/node-event-loop.html)
- DOM事件机制
- 箭头函数
- cookie、sessionStorage、localStorage
- session
- token验证机制
- CSRF攻击
- XSS攻击
- 正则表达式
- 浏览器
- Reflow和Repaint
- 浏览器检测
- !DOCTYPE作用
- 强缓存、协商缓存
- CDN
- DNS解析
- 页面性能能提升方法
- 错误监控
- jQuery获取元素信息
- video标签
- 时间显示NAN
- || 和 && 的返回值
- [URI 和 URL](https://blog.csdn.net/qq_32595453/article/details/80563142)
- URL中的#
- eslint
- 单页面应用SPA
- H5端日历左右滑动效果
- 函数柯里化
- 变量提升
- js连等
- 二进制与十进制转换
- js类型判断
- Generator生成器
- 函数式编程
- JS函数是一等公民
- 前端模块化
- [arrayBuffer, typedArray, dataView](https://www.cnblogs.com/jixiaohua/p/10714662.html)
- js编码
- JS运行机制
- 设计模式
- WebRTC
- 进程与线程
- 进程间通信
- 线程间共享的资源
- clientHeight、offsetHeight、scrollHeight、offsetTop、scrollTop
- SSO单点登录
- DOM元素属性
- JsBridge
- encodeURI和encodeURIComponent
- 移动端屏幕固宽
- 容器部署 pipeline
- Vue多入口项目
- FMP
- 前端请求打散
- 向下滑动加载更多
数据类型
- 基本数据类型
Boolean Number String Null Undefined Symbol Bigint - 复杂数据类型
Object
Proxy
proxy是一个拦截对象实例,target是拦截的目标对象,handler是拦截操作。proxy实例相当于在target对象之上增加了handler操作,实际操作proxy就等于操作target + handler。如果target为空,则proxy也为空。如果handler为空,操作proxy就相当于直接操作target。
var proxy = new Proxy(target, handler);
例如下面代码是将person对象进行包装,使用handler拦截操作,proxy.name就相当于访问person.name,但是在执行.操作读取时重新对get进行定义。get函数中propKey是target对象的属性,就是.之后的你想得到的东西,这里是name。
var person = {name: 'xiao'}
var handler = {
get(target, propKey, receiver) {
if(propKey in target){
console.log(target[propKey])
}
else{
console.log("wrong")
}
}
}
var proxy = new Proxy(person, handler)
proxy.name
Promise
Promise的作用是实现异步操作,通过new Promise的方式生成一个Promise对象。resolve和reject是由JS提供的函数,他们的作用是改变Promise对象的状态,resolve使对象状态由pending(进行中)变为resolved(成功),reject使对象状态由pending变为rejected(失败)。且一个对象只能存在一种状态,一旦状态改变之后则不能再改变。
若状态为resolved则回调函then之后执行success对应内容,若状态为rejected则执行failure中内容。执行resolve函数后后面的内容仍然会执行,除非return掉。
因为错误具有冒泡的性质,所以错误最终都会被catch捕捉。一般不使用then的第二个参数处理错误,而统一使用catch处理错误。
finally操作无论Promise对象状态是什么最终都会执行,而且不接收参数。
const promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
promise.then(function(value) {
// success
}, function(error) {
// failure
}).catch(function(error){
//catch
}).finally(() => {
//code
});
setTimeout(fn, 0)在下一轮“事件循环”开始时执行,Promise.then()在本轮“事件循环”结束时执行。因此then 函数先输出,settimeout后输出。
setTimeout(function(){
console.log(1)
},0)
var promise = new Promise(function(resolve, reject){
console.log(2)
resolve()
console.log(3)
})
promise.then(function(){
console.log(4)
})
console.log(5)
output:2 3 5 4 1
Promise 实现原理
AJAX
AJAX(Asynchronous JavaScript and XML)在不重新加载整个页面的情况下,可以与服务器交换数据并更新部分网页内容。
//创建XMLHttpRequest对象
var xmlhttp;
if (window.XMLHttpRequest)
{
// IE7+, Firefox, Chrome, Opera, Safari 浏览器执行代码
xmlhttp=new XMLHttpRequest();
}
else
{
// IE6, IE5 浏览器执行代码
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
//向服务器发送请求,method规定请求方式'GET'或'POST',url为请求文件在服务器上的位置,
async为bool是否异步,string为POST请求时的携带参数
xmlhttp.open("method","url",async);
xmlhttp.send(string);
//setRequestHeader(header,value)向请求添加http头,header为规定头的名称,value为规定头的值
当使用get时,send(),当使用post时,需要通过send传递参数,在使用send前需要设置请求头部
xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");
xmlhttp.send("fname=Henry&lname=Ford") //string为要发送的数据
在开启异步时需要规定在响应处于 onreadystatechange 事件中的就绪状态时执行的函数,每当readystate变化时触发该函数。
xmlhttp.onreadystatechange=function()
{
if (xmlhttp.readyState==4 && xmlhttp.status==200)
{
document.getElementById("myDiv").innerHTML=xmlhttp.responseText;
}
}
服务器响应responseText为字符串形式响应数据,responseXML为XML形式的相应数据。
post和get对比
跨域
跨域,指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器施加的安全限制。
JSONP不支持post
所谓同源是指,域名,协议,端口均相同
跨域解决方案:Jsonp、CORS
Js循环
普通for循环不再赘述
- for…in
for in遍历的是对象中的key,如果遍历数组则是遍历索引
var obj1 = {'a':1, 'b':2}
var obj2 = ['a','b','c']
for(let key in obj1) \\此时key是'a', 'b'
for(let key in obj2) \\此时key是 0, 1, 2
- for…of
for of只能遍历能够迭代的比如数组、map、set、字符串。但是不能遍历对象,因为对象没有迭代器。for of遍历的是其中的值。
var obj = ['a','b','c']
for(let val in obj) \\此时val是'a', 'b'
- forEach
forEach是可以被迭代的对象的成员方法,接受一个参数回调函数callback,回调函数的三个参数(value, key, iterable),(值,键,可以迭代的对象本身),每次循环执行该回调函数。
forEach不能break也不能return提前跳出循环,可以通过抛出异常终止。
var iterable
iterable.forEach(function(val, key, iterable){
})
- Array.map()
for...of
, forEach
, map
中直接修改item不会影响原数组
let arr = [1, 2, 3, 4, 5]
for(let item of arr) {
item += 1
}
arr.forEach(item => {
item += 1
})
arr.map(item => {
item += 1
})
console.log(arr) // [1, 2, 3, 4, 5]
JS对象遍历
对象的属性分为可枚举和不可枚举之分,是由属性的enumerable值决定的
for in
主要用于遍历对象的可枚举属性,包括自有属性、继承自原型的属性Object.keys(obj)
主要用于遍历对象自有的可枚举属性,不包括继承自原型的属性和不可枚举的属性。Object.getOwnPropertyNames(obj)
用于返回对象的自有属性,包括可枚举和不可枚举的属性
Websocket
http://www.52im.net/thread-1341-1-1.html
websocket是一种网络通讯协议,HTTP协议只能由客户端向服务器发送请求而websocket协议中服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息。
websocket特点
(1)建立在 TCP 协议之上,服务器端的实现比较容易。
(2)与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。
(3)数据格式比较轻量,性能开销小,通信高效。
(4)可以发送文本,也可以发送二进制数据。
(5)没有同源限制,客户端可以与任意服务器通信。
(6)协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。
websocket断线重连
JS中this
https://segmentfault.com/a/1190000011194676
JS原型和原型链
https://blog.csdn.net/u013302153/article/details/53543882
https://www.jianshu.com/p/ae7afb5ba420
一.所有构造器/函数的__proto__都指向Function.prototype(Function.prototype是一个空函数)
函数Function与其他的函数不同,他的prototype和 _ proto _ 都指向Function的原型。
原型是对象
Object.prototype.__proto__ // null
Function.prototype === Function.__proto__ // true
Function.prototype === Object.__protp__ // true
Function.prototype.__proto__ === Object.prototype // true
Function instanceof Object // true
Object instanceof Function //true
let fn = function(){}
fn instanceof Function // true
JS作用域、作用域链、闭包
http://www.ruanyifeng.com/blog/2009/08/learning_javascript_closures.html
由于在Javascript语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成"定义在一个函数内部的函数"。
词法分析器会在函数执行前分析出变量依赖,从而形成作用域链
(function(){})()
这种写法就相当于直接执行function
闭包
- 作用
- 保护私有变量不受外界干扰
- 形成不销毁的栈内存,把一些值保存下来方面后面调取使用
JS传参
ECMAScript中所有函数的参数都是按值传递的,当参数为基本类型时,参数arg是将a进行复制。当参数arg为引用类型时,实际上是复制了指针b,此时arg和b指向同一片内存。如果在函数中对arg进行重新赋值一个新的引用类型,那么arg将指向一片新的内存,所以此时改变arg,外部的b不再受影响。
function func(arg){
arg = new String('sakuragi')
}
var a = 3
var b = new String('rukawa')
func(b)
console.log(b) //'rukawa'
严格模式strict
JS方法
instanceof
path.join()
const path = require('path')
path.join('a','/b', 'c') // a/b/c
path.join('/a','b', 'c/') // /a/b/c/ join会自动将缺少的/补全,并且在两端的/会被保留
path.join('a','b','../c') // a/c join支持../向上跳转
path.resolve()
path.resolve([from …], to)
将 to 参数解析为绝对路径,给定的路径的序列是从右往左被处理的
path.resolve('a','b') // /Users/super/webpackExercise/a/b 如果没有指定根目录会自动根据当前文件所在目录补全路径
path.resolve('a','b','../c') // /Users/super/webpackExercise/a/c 支持../向上跳转
path.resolve('a','/b','c') ///Users/super/webpackExercise/b/c 如果从右向左解析过程中出现/根目录则解析完毕
apply,call,bind
apply参数为数组,call和bind参数为一个一个的参数
bind返回的是一个函数,apply和call返回函数调用结果
func.apply(thisArg, [argsArray])
返回调用有指定this值和参数的函数的结果。
function.call(thisArg, arg1, arg2, ...)
返回使用调用者提供的 this 值和参数调用该函数的返回值。若该方法没有返回值,则返回 undefined。
function.bind(thisArg, arg1, arg2, ...)
返回一个原函数的拷贝,并拥有指定的 this 值和初始参数。
apply call bind 原理
Function.prototype.imitateBind = function (context) {
// 获取绑定时的传参
let args = [...arguments].slice(1),
// 定义中转构造函数,用于通过原型连接绑定后的函数和调用bind的函数
F = function () {},
// 记录调用函数,生成闭包,用于返回函数被调用时执行
self = this,
// 定义返回(绑定)函数
bound = function () {
// 合并参数,绑定时和调用时分别传入的
let finalArgs = [...args, ...arguments]
// 改变作用域,注:aplly/call是立即执行函数,即绑定会直接调用
// 这里之所以要使用instanceof做判断,是要区分是不是new xxx()调用的bind方法
// 当bind返回的函数被当做构造函数进行 new 调用的时候,this不再指向bind绑定的
// 那个对象的作用域,而是指向new的这个新对象的this
return self.call((this instanceof F ? this : context), ...finalArgs)
}
// 将调用函数的原型赋值到中转函数的原型上
F.prototype = self.prototype
// 通过原型的方式继承调用函数的原型
bound.prototype = new F()
return bound
}
function Person (name) {
console.log(this.name)
console.log(arguments)
}
let a = {
name: 'zyx',
}
Person.bind(a, 'ok')('pl') // zyx [Arguments] { '0': 'ok', '1': 'pl' }
此时函数中的this指向a,所以名字为 zyx,参数累加
let p = new (Person.bind(a, 'htc')) // undefined [Arguments] { '0': 'htc' }
此时函数中的this指向新创建的对象p,没有name属性所以是undefined
数组方法
filter()
filter用于对数组进行过滤。它创建一个新数组,新数组中的元素是通过检查指定数组中符合条件的所有元素。
filter()不会对空数组进行检测、不会改变原始数组
Array.filter(function(currentValue, index, arr), thisValue)
currentValue为当前元素值,为必选
index为当前元素的索引
arr为当前元素所在数组
thisValue指定回调函数function中的this,this默认是undefined
var num = [1, 2, 3, 4, 5]
function check(num){ //筛选大于3的数字
return num > 3
}
num = num.filter(check) //[4, 5]
map()
map() 方法返回一个新数组,数组中的元素为原始数组元素调用函数处理后的值。
array.map(function(currentValue,index,arr), thisValue)
currentValue为当前元素值,为必选
index为当前元素的索引
arr为当前元素所在数组
thisValue指定回调函数function中的this,this默认是undefined
var num = [1, 2, 3, 4, 5]
function double(num){
return num * 2
}
num = num.map(double) // [2, 4, 6, 8, 10]
const reporter = {
report: function(key, value) {
console.log("Key: %s, Value: %s", key, value);
}
};
map.forEach(function(value, key, map) {
this.report(key, value); //this就是由reporter指定,指向reporter对象
}, reporter);
sort()
array.sort(compare)
compare函数凡是返回1或者大于0的正数的时候就要交换位置。
return a - b的意思是如果a大于b时,a-b大于0,则a,b需要调换位置
function compare(a, b){
return a - b //升序
}
var arr = [5, 4, 3, 2, 1]
arr.sort(compare)
splice()、slice()
splice()改变原数组,slice()不改变原数组
splice() 方法向/从数组中添加/删除项目,然后返回被删除的项目。
arrayObject.splice(index,howmany,item1,.....,itemX)
index 必需。整数,规定添加/删除项目的位置,使用负数可从数组结尾处规定位置。
howmany 必需。要删除的项目数量。如果设置为 0,则不会删除项目。
item1, ..., itemX 可选。向数组添加的新项目。
var arr = [1,2,3,4,5]
console.log(arr.splice(2, 2)) //[3,4]
console.log(arr) //[1,2,5]
slice()方法返回一个新的数组,包含从 start 到 end (不包括该元素)的 arrayObject 中的元素。
arrayObject.slice(start, end)
start 必需。规定从何处开始选取。如果是负数,那么它规定从数组尾部开始算起的位置。也就是说,-1 指最后一个元素,-2 指倒数第二个元素,以此类推。
end 可选。规定从何处结束选取。该参数是数组片断结束处的数组下标。如果没有指定该参数,那么切分的数组包含从 start 到数组结束的所有元素。如果这个参数是负数,那么它规定的是从数组尾部开始算起的元素。
var arr = [1,2,3,4,5]
arr.slice(1,3) //[2,3]
console.log(arr) //[1,2,3,4,5]
slice
将函数参数 arguments 转换为数组,当函数参数个数不确定时使用 arguments 接受参数
slice
方法可以用来将一个类数组(Array-like)对象/集合转换成一个新数组
function fun(a, b, c) {
let args = [].slice.call(arguments)
console.log(arguments) // {'0': a, '1': b, '2': c}
console.log(args) // [a, b, c]
}
array.reduce()
arr 表示原数组;
prev 表示上一次调用回调时的返回值,或者初始值 init;
cur 表示当前正在处理的数组元素;
index 表示当前正在处理的数组元素的索引,若提供 init 值,则索引为0,否则索引为1;
init 表示初始值。
arr.reduce(function(prev,cur,index,arr){
...
}, init);
求和
var sum = arr.reduce(function (prev, cur) {
return prev + cur;
},0);
array.shift()
shift() 方法用于把数组的第一个元素从其中删除,并返回第一个元素的值。
如果数组是空的,那么 shift() 方法将不进行任何操作,返回 undefined 值。
let arr = [1,2,3]
let a = arr.shift() //a == 1, arr == [2,3]
array.reverse()
reverse() 方法用于颠倒数组中元素的顺序,返回当前数组。该方法会改变原来的数组,而不会创建新的数组。
let arr = [1,2,3]
arr.reverse() // arr == [3,2,1]
array.fill()
fill() 方法用于将一个固定值替换数组的元素。
array.fill(value, start, end)
value 必需。填充的值。
start 可选。开始填充位置。
end 可选。停止填充位置 (默认为 array.length)
array.includes()
includes() 方法用来判断一个数组是否包含一个指定的值,如果是返回 true,否则false。
arr.includes(searchElement, fromIndex)
searchElement 必须。需要查找的元素值。
fromIndex 可选。从该索引处开始查找 searchElement。如果为负值,则按升序从 array.length + fromIndex 的索引开始搜索。默认为 0。
array.concat()
- concat() 方法用于连接两个或多个数组。
- 该方法不会改变现有的数组,而仅仅会返回被连接数组的一个副本。
arrayObject.concat(arrayX,arrayX,......,arrayX)
let arr1 = [1, 2, 3], arr2 = [4, 5]
arr1.concat(arr2) // [1, 2, 3, 4, 5]
JSON.stringify(),JSON.parse()
JSON.stringify() 将JS对象转换成字符串
JSON.parse() 方法用来解析JSON字符串,构造由字符串描述的JavaScript值或对象。
var obj = {name: "zyx", age: "18"}
var str = JSON.stringify(obj) // "{"name":"zyx","age":"18"}"
var json = JSON.parse(str) // {name: "zyx", age: "18"}
indexOf()
indexOf() 方法可返回某个指定的字符串值在字符串或数组中首次出现的位置。
如果没有找到匹配的字符串则返回 -1。
string.indexOf(searchvalue,start)
array.indexOf(searchvalue,start)
searchvalue 必需。规定需检索的字符串值。
fromindex 可选的整数参数。规定在字符串/数组中开始检索的位置。它的合法取值是 0 到 stringObject.length - 1/arrayObject.length - 1。如省略该参数,则将从字符串的首字符开始检索。
setTimeout()和clearTimeout()
setTimeout() 方法用于在指定的毫秒数后调用函数或计算表达式。返回值为id,在调用clearTimeout()清除计时器时作为参数
id = setTimeout(() => {
console.log("hello")
}, 2000)
clearTimeout(id) //如果在执行setTimeout的2s内执行clearTimeout则setTimeout取消,不再输出hello
setInterval(code,sec)
setInterval() 方法可按照指定的周期(以毫秒计)来调用函数或计算表达式。
setInterval() 方法会不停地调用函数,直到 clearInterval() 被调用或窗口被关闭。由 setInterval() 返回的 ID 值可用作 clearInterval() 方法的参数。
let timer = setInterval({console.log('wait')},500) //每隔500ms输出wait
clearInterval(timer)
字符串操作
substring(),substr()
截取字符串返回子串,如果第二个参数没有则返回从start开始到结尾的子串
str.substr(start, length) //"123456".substr(1,2) === "23"
返回从start开始长度为length的子串
str.substring(start, end) //"123456".substring(1,3) === "23"
返回从start开始,到end位置的子串(不包含end)
start,end若为负数或NAN则替换为0,start和end选择较小的作为开始索引
join()
将数组中的所有元素放入一个字符串,元素是通过指定的分隔符进行分隔的。
array.join(separator)
separator可选,如果没有则默认使用','隔开
[1,2,3,4].join() // "1,2,3,4"
[1,2,3,4].join('') // "1234"
Object对象方法
Object.keys(obj)
方法会返回一个由一个给定对象的自身可枚举属性组成的数组,数组中属性名的排列顺序和正常循环遍历该对象时返回的顺序一致 。
var anObj = { 100: 'a', 2: 'b', 7: 'c' };
console.log(Object.keys(anObj)); // console: ['2', '7', '100']
Object.create(obj)
Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__。
const person = {
isHuman: false
};
const me = Object.create(person);
Object.entries(obj)
Object.entries()返回一个数组,其元素是与直接在object上找到的可枚举属性键值对相对应的数组。属性的顺序与通过手动循环对象的属性值所给出的顺序相同。
const obj = { foo: 'bar', baz: 42 };
console.log(Object.entries(obj));
// [ ['foo', 'bar'], ['baz', 42] ]
Object.assign(target, …sources)
Object.assign() 方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。
const target = { a: 1, b: 2 };
const source = { b: 4, c: 5 };
const returnedTarget = Object.assign(target, source);
console.log(target);
// expected output: Object { a: 1, b: 4, c: 5 }
console.log(returnedTarget);
// expected output: Object { a: 1, b: 4, c: 5 }
Object.getPrototypeOf(object)
Object.getPrototypeOf() 方法返回指定对象的原型
返回给定对象的原型。如果没有继承属性,则返回 null 。
const prototype1 = {};
const object1 = Object.create(prototype1);
console.log(Object.getPrototypeOf(object1) === prototype1);
// expected output: true
JS深拷贝
对于引用类型变量,为了能够拷贝整个变量而不是只拷贝指向内存的指针所以需要深拷贝。
方法一
JSON.parse(JSON.stringify()),问题是不可以拷贝 undefined , function, RegExp 等等类型的。
不能用JSON.parse(JSON.stringify())的情况
var obj = [1,2,3,4,5]
var copy = JSON.parse(JSON.stringify(obj))
方法二
递归拷贝
// 定义一个深拷贝函数 接收目标target参数
function deepClone(target) {
// 定义一个变量
let result;
// 如果当前需要深拷贝的是一个对象的话
if (typeof target === 'object') {
// 如果是一个数组的话
if (Array.isArray(target)) {
result = []; // 将result赋值为一个数组,并且执行遍历
for (let i in target) {
// 递归克隆数组中的每一项
result.push(deepClone(target[i]))
}
// 判断如果当前的值是null的话;直接赋值为null
} else if(target===null) {
result = null;
// 判断如果当前的值是一个RegExp对象的话,直接赋值
} else if(target.constructor===RegExp){
result = target;
}else {
// 否则是普通对象,直接for in循环,递归赋值对象的所有值
result = {};
for (let i in target) {
result[i] = deepClone(target[i]);
}
}
// 如果不是对象的话,就是基本数据类型,那么直接赋值
} else {
result = target;
}
// 返回最终结果
return result;
}
解决循环引用问题
防抖和节流
js类创建
构造函数、原型混合使用
用构造函数定义对象的所有非函数属性,用原型方式定义对象的函数属性(方法)
function Person(name, age){
this.name = name
this.age = age
this.hobby = new Array('piano', 'anime')
}
Person.prototype.showName = function(){
console.log(this.name)
}
var p = new Person('zyx', 18)
类的继承
- 借助构造函数继承
缺点:原型链上的方法无法继承
function Parent () {
this.name = 'xuan'
}
function Child () {
Parent.call(this)
}
- 借助原型链实现继承
缺点:所有子类的prototype
共享一个对象,改变一个子类的实例会影响另一个
function Parent () {
this.name = 'xuan'
}
function Child () {}
Child.prototype = new Parent()
- 组合方式
缺点:父类构造函数执行了2次
function Parent () {
this.name = 'xuan'
}
function Child () {
Parent.call(this)
}
Child.prototype = new Parent()
- 组合方式2
缺点:子类实例的构造函数也引用了父类构造函数
function Parent () {
this.name = 'xuan'
}
function Child () {
Parent.call(this)
}
Child.prototype = Parent.prototype
- 组合方式3
function Parent () {
this.name = 'xuan'
}
function Child () {
Parent.call(this)
}
Child.prototype = Object.create(Parent.prototype) //避免子类与父类引用同一个 prototype
Child.prototype.constructor = Child
- es6继承
- class 子类 extends 父类
- 子类构造函数通过 super() 调用父类构造函数
class Father {
constructor (x) {
this.x = x
}
}
class Child extends Father {
constructor (x, y) {
super(x)
this.y = y
}
}
new原理
1.创建一个空对象,作为将要返回的对象实例。
2.将这个空对象的原型,指向构造函数的prototype属性。
3.将这个空对象赋值给函数内部的this关键字。
4.开始执行构造函数内部的代码。
function _new(/* 构造函数 */ constructor, /* 构造函数参数 */ params) {
console.log(arguments.__proto__.constructor)
// 将 arguments 对象转为数组
var args = [].slice.call(arguments);
// 取出构造函数
var constructor = args.shift();
// 创建一个空对象,继承构造函数的 prototype 属性
var context = Object.create(constructor.prototype);
// 执行构造函数
var result = constructor.apply(context, args);
// 如果返回结果是对象,就直接返回,否则返回 context 对象
return (typeof result === 'object' && result != null) ? result : context;
}
http协议
https://www.cnblogs.com/an-wen/p/11180076.html
服务端回应
HTTP/1.0 200 OK
Content-Type: text/plain
Content-Length: 137582
Expires: Thu, 05 Dec 1997 16:00:00 GMT
Last-Modified: Wed, 5 August 1996 15:55:28 GMT
Server: Apache 0.84
<html>
<body>Hello World</body>
</html>
Content-Type:
返回数据格式
Content-Type详解
Content-Encoding:
说明数据的压缩方法
Content-Length
声明本次回应的数据长度
Http请求头和响应头
http状态码
HTTPS
http://www.ruanyifeng.com/blog/2014/02/ssl_tls.html
https://www.jianshu.com/p/6c46ef63c407
对称加密:
客户端和服务端使用相同私钥进行加密、解密
非对称加密:
使用公钥进行加密,私钥进行解密或者使用私钥进行加密,公钥进行解密
HTTPS通信过程
对数据进行对称加密,对称加密所要使用的密钥通过非对称加密传输。
服务器端的公钥和私钥,用来进行非对称加密
客户端生成的随机密钥,用来进行对称加密
一个HTTPS请求实际上包含了两次HTTP传输。
Fetch api
http1.0 1.1 2.0对比
JS异步 Event Loop模型
https://www.cnblogs.com/qiuqiubai/p/12545394.html
process.nextTick
属于nextTickQueue
,nextTick会在事件循环每个阶段结束检查队列并执行
promise回调函数
属于microTaskQueue
微任务队列,promise在本轮事件循环结束后检查微任务队列并执行
检查任务队列时会将当前任务队列清空再进行下一阶段
Node 定时器详解、事件循环机制
https://zhuanlan.zhihu.com/p/100889981
new Promise(function(resolve){
console.log('promise1')
resolve()
}).then(function(){
console.log('promise2')
new Promise((resolve) => {
console.log('promise3')
resolve()
}).then(() => {
console.log('promise4')
})
})
setTimeout(() => {
console.log('setTimeout')
}, 0)
输出结果
promise1
promise2
promise3
promise4
setTimeout
promise
属于微任务,setTimeout
属于宏任务,在执行下一轮循环前会先将为任务队列清空,因此promise4
也会在 setTimeout
前输出
DOM事件机制
element.addEventListener(事件名称String、要触发的事件处理函数Function、指定事件处理函数的时期或阶段boolean)
第三个参数默认为false,处理函数在冒泡过程执行;如果设置为true则处理函数在捕获过程执行
箭头函数
在不添加大括号的时候箭头函数会默认return语句,加了大括号就正常
let fn1 = (a, b) => a + b
let fn2 = (a, b) => { a + b }
console.log(fn1(1, 2)) // 3
console.log(fn2(1, 2)) // undefined
连续的箭头函数使用
let fn = (x) => (y) => x+y
fn(1) // 返回的是函数 (y)=>x+y
fn(1)(2) // 3
- 函数不支持arguments
- 箭头函数不能作为构造函数,因为箭头函数没有 prototype
箭头函数this指向
箭头函数不会创建自己的this,它只会从自己的作用域链的上一层继承this。
在寻找箭头函数的this时和普通变量的取值规则相同,需要沿着作用域链向上找
obj.f()
中箭头函数中没有this,所以就在当前作用域即 window
找 window.name -> out
obj.f2()
中箭头函数中没有this,所以向上在f2
中找 this.name
,因为隐式绑定 obj
所以 this.name === 'a'
this.name = 'out'
let obj = {
name: 'a',
f: () => {
console.log(this.name)
},
f2 () {
console.log(this.name)
setTimeout(() => {
console.log(this.name)
})
}
}
obj.f() // out
obj.f2() // a a
cookie、sessionStorage、localStorage
sessionStorage,localStorage存储对象时会默认转换为string类型
解决方法: 通过JSON.stringify(),JSON.parse()
进行转换
cookie设置
session
token验证机制
JWT (Json Web Token)由头部header、载荷payload、签名signature三部分组成
token原理:https://www.cnblogs.com/lufeiludaima/p/pz20190203.html
https://blog.csdn.net/wd521521/article/details/82856203
CSRF攻击
防御措施
- 涉及到数据更改的操作服务器严格使用post请求而不是get请求
- 验证HTTP Referer 字段
Referer
字段记录了请求的来源地址 - 在请求地址中添加token
XSS攻击
防御措施
- 设置
HttpOnly
可以限制javascript不能读取cookie,防止会话ID泄露 - 对代码进行过滤
HttpOnly
正则表达式
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Regular_Expressions#special-negated-character-set
\^a\
作用是匹配开头为a的字符串
\a$\
作用是匹配结尾为a的字符串
\^a$\
同时使用是匹配开头和结尾都是a的字符串,如果a不重复就只匹配字符串’a’
正整数:\^[1-9][0-9]*$\
非负整数:\^(0|[1-9][0-9]*)$\
整数:\^-?\d+$\
12个月:\^(0?[1-9]|1[0-2])$\
浏览器
chrome浏览器架构
浏览器内核
浏览器渲染
浏览器中的进程与线程
- chrome 浏览器中每个tab页面是一个渲染进程
- 每个页面中是存在多线程,比如JS引擎线程,GUI渲染线程,定时触发器线程等等。GUI渲染线程与JS执行线程是互斥的关系
Reflow和Repaint
https://www.cnblogs.com/qcloud1001/p/10265985.html
- 引起重排
- 添加或者删除可见的DOM元素
- 元素位置改变
- 元素尺寸改变(包括外边距、内边框、边框大小、高度和宽度等)
- 元素内容改变(例如:一个文本被另一个不同尺寸的图片替代)
- 页面渲染初始化(这个无法避免)
- 浏览器窗口尺寸改变
浏览器检测
!DOCTYPE作用
强缓存、协商缓存
https://www.jianshu.com/p/54cc04190252
缓存位置
Memory Cache
:内存缓存Disk Cache
:硬盘缓存
打开网页:检测disk cache
中是否有缓存,没有则发送请求
普通刷新:先检查memory cache
,再检测disk cache
强制刷新:不使用缓存,直接发送请求并设置header,Cache-control: no-cache
静态资源部署
CDN
DNS解析
http://www.360doc.com/content/13/0527/17/11253639_288596772.shtml
本地解析:
客户端->浏览器缓存->hosts->本地DNS解析器缓存
从输入URL到页面加载过程
- DNS解析
- TCP链接
- 发送HTTP请求
- 服务器处理请求并返回HTTP报文
- 浏览器解析渲染页面
- 连接结束
DNS预解析
// 用meta信息来告知浏览器, 当前页面要做DNS预解析
<meta http-equiv="x-dns-prefetch-control" content="on">
// 在页面header中使用link标签来强制对DNS预解析:
<link rel="dns-prefetch" href="//www.zhix.net">
页面性能能提升方法
- 资源压缩合并,减少HTTP请求
- 非核心代码异步加载
动态加载、defer、async - 浏览器缓存
- CDN
- 预解析DNS
错误监控
前端错误分类
- 即时运行错误:代码错误
捕获方式: 1.try catch 2.window.onerror - 资源加载错误
捕获方式: 1.object.onerror 2.performance.entries() 3.Error事件捕获
错误上报
利用image对象上报
<script>
(new Image()).src = "http://baidu.com/asd?sc=123"
</script>
直接通过新建Image对象上报错误,路径就是src
jQuery获取元素信息
$(element).height() //获取元素高度
$(element).width() //获取元素宽度
$(window).height() //获取当前窗口视图高度
$(element).scrollTop() //获取元素滚动的距离,既向下滚动了多少
$(element).scrollLeft()
$(element).offset().top //offset() 方法设置或返回被选元素相对于文档的偏移坐标,两个属性top和left
$(element).offset().left
video标签
时间显示NAN
在IOS
系统或IE
浏览器中,不识别YY-MM-DD
形式时间,因此需要将其转换为YY/MM/DD
形式
|| 和 && 的返回值
- ||
如果结果为true,会返回第一个为真的值;
如果结果为false,会返回第二个为假的值;
console.log( 5 || 4 ); // 当结果为真时,返回第一个为真的值5
console.log( 0 || 0 ); // 当结果为假时,返回第二个为假的值0
- &&
如果结果为true,那么会返回的会是第二个为真的值;
如果结果为false,返回的会是第一个为假的值。
console.log( 5 && 4 ); // 当结果为真时,返回第二个为真的值4
console.log( 0 && 4 ); // 当结果为假时,返回第一个为假的值0
URI 和 URL
URL中的#
eslint
Require self-closing on HTML elements (<div>)
<div class="nav"></div> // wrong
<div class="nav" /> // right
原因是一对标签内没有内容所以根据 eslint
规则使用自闭和形式的标签<div/>
单页面应用SPA
SEO问题
SPA是客户端渲染,通过加载执行JS来创建DOM元素构建页面,但是爬虫只是请求静态资源,不会执行JS文件,所以抓取不到DOM结构,也分析不出来有用的信息。
爬虫通常爬的是静态页面,将整个静态页面保存起来,然后进行词法分析(分析内容、关键词,是否有其他链接继续进行爬取),并不会执行其中的js
H5端日历左右滑动效果
函数柯里化
函数的长度是定义函数时形参的个数
function fn (a, b, c) {}
fn.length // 3
function curry (fn, currArgs) {
return function() {
let args = [].slice.call(arguments);
// 首次调用时,未提供最后一个参数currArgs,则不用进行args的拼接
if (currArgs !== undefined) {
args = args.concat(currArgs);
}
// 递归调用,如果当前参数长度未达到 sum 形参数量,则进行递归
if (args.length < fn.length) {
return curry(fn, args);
}
// 递归出口,此时相当于把所有参数扁平化,执行sum.apply(null, [1, 2, 3])
return fn.apply(null, args);
}
}
function sum(a, b, c) {
console.log(a + b + c);
}
const fn = curry(sum);
fn(1, 2)(3); // 6
变量提升
- 函数提升优先级比变量提升要高,且不会被变量声明覆盖,但是会被变量赋值覆盖。
- 变量提升只是变量的声明提升,与变量赋值操作无关
function fn(){}
方式声明函数则将整个函数提升- 函数参数是声明在函数作用域内的变量, 若传值则是在执行函数体前先对变量进行赋值
a() // a
console.log(a) // [Function: a]
function a () {
console.log('a')
}
var a = 3
js连等
- 赋值操作
a = 3
- 创建值
- 创建变量
- 变量与值进行关联
let a = { n: 1 }
let b = a
a.x = a = { n: 2 }
a // { n: 2 }
b // { n: 1, x: { n: 2 } }
- 连等操作会首先在将变量和值关联之前从左到右找到所有的变量
- 如
a.x
就会先在a
对应的堆内存AAAFFF000
中找变量x,没有则创建 - 然后赋值时实际是为
x
赋值,所以a
的指向发生变化,x
仍然创建在了a
原来指向的堆内存中
二进制与十进制转换
- 十进制转换为二进制:
let num = 4
num.toString(2) // '100'
- 十进制转换为二进制:
parseInt('100', 2) // 4
js类型判断
Generator生成器
- 执行生成器函数
gen
返回一个iterator
迭代器 - 调用迭代器的
next
,遇到yield
停止,yield
后的表达式的值为迭代器返回值的value
yield
表达式如果用在另一个表达式之中,必须放在圆括号里面next
方法可以带一个参数,该参数就会被当作上一个yield
表达式的返回值。
第二个next
中的参数 7 就是yield 3
的返回值所以输出 2 + 7 = 9
function* gen () {
console.log(2 + (yield 3)) // 9
}
let g = gen()
g.next() // { value: 3, done: false}
g.next(7) // { value: undefined, done: true}
函数式编程
JS函数是一等公民
一等公民可以
- 作为函数参数
- 作为函数返回值
- 也可以赋值给变量
前端模块化
arrayBuffer, typedArray, dataView
js编码
base64
encodeURI
encodeURIComponent
encodeURIComponent 转义除了如下所示外的所有字符
A-Z a-z 0-9 - _ . ! ~ * ' ( )
JS运行机制
js编译原理
js上下文 执行栈
js 内存空间
js垃圾回收
设计模式
单例模式
发布订阅模式和观察者模式
WebRTC
NAT穿透
RTCPeerConnection原理
建立会话基本步骤
- 呼叫者通过 navigator.mediaDevices.getUserMedia() 捕捉本地媒体。
- 呼叫者创建一个RTCPeerConnection 并调用 RTCPeerConnection.addTrack() (注: addStream 已经过时。)
- 呼叫者调用 (“RTCPeerConnection.createOffer()”)来创建一个提议(offer).
- 呼叫者调用 (“RTCPeerConnection.setLocalDescription()”) 将提议(Offer) 设置为本地描述 (即,连接的本地描述).
- setLocalDescription()之后, 呼叫者请求 STUN 服务创建ice候选(ice candidates)
- 呼叫者通过信令服务器将提议(offer)传递至 本次呼叫的预期的接受者.
- 接受者收到了提议(offer) 并调用 (“RTCPeerConnection.setRemoteDescription()”) 将其记录为远程描述 (也就是连接的另一端的描述).
- 接受者做一些可能需要的步骤结束本次呼叫:捕获本地媒体,然后通过RTCPeerConnection.addTrack()添加到连接中。
- 接受者通过(“RTCPeerConnection.createAnswer()”)创建一个应答。
- 接受者调用 (“RTCPeerConnection.setLocalDescription()”) 将应答(answer) 设置为本地描述. 此时,接受者已经获知连接双方的配置了.
- 接受者通过信令服务器将应答传递到呼叫者.
- 呼叫者接受到应答.
- 呼叫者调用 (“RTCPeerConnection.setRemoteDescription()”) 将应答设定为远程描述. 如此,呼叫者已经获知连接双方的配置了.
进程与线程
进程间通信
线程间共享的资源
clientHeight、offsetHeight、scrollHeight、offsetTop、scrollTop
documentElement 和 body 相关说明:
body是DOM对象里的body子节点,即 标签;
documentElement 是整个节点树的根节点root,即 标签;
SSO单点登录
https://blog.csdn.net/xiaoguan_liu/article/details/91492110
DOM元素属性
Element.scrollTop
- 一个元素的 scrollTop 值是这个元素的内容顶部(卷起来的)到它的视口可见内容(的顶部)的距离的度量。
- 当一个元素的内容没有产生垂直方向的滚动条,那么它的 scrollTop 值为0。
var scrollTop = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop;
Element.clientHeight
clientHeight 可以通过 CSS height + CSS padding - 水平滚动条高度 (如果存在)来计算.
Element.scrollHeight
- 当子元素高度大于父元素,且父元素设置了
overflow: scroll
时,父元素的scrollHeight
表示该元素在不使用滚动条的情况下为了适应视口中所用内容所需的最小高度。 - 没有垂直滚动条的情况下,
scrollHeight
值与元素视图填充所有内容所需要的最小值clientHeight
相同。包括元素的padding
,但不包括元素的border
和margin
。scrollHeight
也包括::before
和::after
这样的伪元素。 - 当滚动到最底部时, 下面等式成立
element.scrollHeight - element.scrollTop === element.clientHeight
HTMLElement.offsetHeight
HTMLElement.offsetHeight
是一个只读属性,它返回该元素的像素高度,高度包含该元素的垂直内边距和边框,且是一个整数。
Window.innerHeight
浏览器窗口的视口(viewport)高度(以像素为单位);如果有水平滚动条,也包括滚动条高度。
JsBridge
webview
背景
结合小程序应用场景对Webview层进行优化,提升页面流畅性,启动速度。
普通WebView加载流程:
App创建WebView实例后,WebView会启动线程进行网络请求,期间页面逐渐渲染,直到所有子请求进行完毕。
WebView初始化和网络请求较为耗时,且串行执行。
时序图红色为较为耗时部分,下同。
Hybrid WebView加载流程:
业界Hybrid WebView大多采用“直出”,“离线预推”等方式提高WebView的加载速度。
-
直出:解决首屏H5持续进行ajax请求的问题,减少loading时间,此过程由node js完成,node将完整静态html发布到CDN,WebView直接请求或者使用离线包即可。
直出还分针对通用静态页面的"静态直出"和针对动态个性化页面的"动态直出"。
-
离线预推:解决WebView进行HTTP请求html耗时问题,提前通过信令,透传Push等方案通知客户端将Hybrid离线包下载到本地,WebView启动以后直接加载离线包里的页面即可,不需进行Http请求。
-
并行请求:解决WebView创建和WebView HTTP请求串行的问题,在启动Hybrid页面时,并行通过客户端进行网络请求,然后在WebView加载时将已经下载过的数据包装成WebResourceResponse给WebView。
这种并行请求的方案一般配合Hybrid包使用,与WebView直接进行的请求相比,显著减少了HTTP请求次数。
下图为Hybrid 页面展示过程,WebView的创建和HTTP请求并行过程。
小程序WebView首屏加载流程:
小程序WebView的使用与上面两种方案有明显区别,除 WebView之外,另存在JSCore,Native两个关键角色。
WebView,JSCore两者通过Native桥接。
小程序WebView的特点:
WebView不涉及到任何网络请求,所加载的文件均在本地。
WebView只处理渲染逻辑(接受JSCore传过来的数据(见setData/firstRender),然后vue更新数据,触发DOM更新),开发者编写的业务逻辑均运行在JSCore(j2v8)环境。
WebView 加载逻辑:
- new WebView创建实例。
- 注册JSBridge。
- 加载html模版。
- 加载WebView层js逻辑。
从启动小程序到WebView首屏展示完成UI涉及到的逻辑:
- 加载/预加载WebView。
- 向JSCore发送onAppRoute事件。
- JSCore初始化AppEngine和PageEngine然后调用loadScript读取对应页面的js文件,如果是冷启动的话还会读取app.js/app.wsxx.js。
- JSCore发送一个firstRender事件给WebView,此流程由native中转 (JSCore publishHandler → WebView subscribeHandler)。
- WebView接收firstRender事件,然后会调用loadScript/readFileSync读取相应路径的js,构造Vue实例然后进行渲染。
首屏展示过程中,loadScript/readFileSync(标红部分)的实现至关重要,一般情况下每个页面有三个js(.wxml.js/.wxss.js/*.js),但是如果该页面引入了其他Component可能会需要读取更多的js(前端有resourceManager存储逻辑,待补充)。
红色部分为耗时关键点。
首屏过程中可能的优化:
WebView创建过程 WebView缓存池/预加载,减少频繁创建WebView的时间/内存开销。 进行中
WebView加载过程(WebViewPage实例创建) JSBridge创建耗时优化。 进行中,主要是优化JSBridge的创建,减少反射调用。
loadScript/readFileSync 减少读文件时间。 待确定
WebView加载完成到onAppRoute中间过程 待确定。 待确定
WebView Setting优化 整理下setting,pause/resume调用加上。 待确定
Yoda
yoda 是通用 Hybrid 容器,提供一致的 WebBridge 能力,以及特有的页面打开性能优化能力和端内调试能力。进而提高代码复用度和端内 H5 功能的用户体验。Yoda 目前主要提供以下能力:
通用 JSBridge 能力:通过提供 Webview Bridge 的方式,提供给 H5 与 Native 交互通信的能力
离线包能力:提供h5离线包预加载、更新等逻辑,降低页面启动时间
Webview UI:提供基础 Native Webview 的 UI 展示和交互的能力,可以动态设置 Webview UI 和类型相关信息
Webview 优化:提供 API 预取、Ajax hook 等能力
离线包
离线包指的是,将 h5 页面中包含的 HTML/JS/CSS/Image 等静态资源打到一个压缩包内,并由客户端提前下载到本地。用户打开页面时,直接从本地离线包中加载对应的资源,从而最大程度地摆脱网络环境对 H5 页面的影响。
encodeURI和encodeURIComponent
encodeURI()
方法不会对下列字符编码:ASCII字母、数字、~!@#$&*()=:/,;?+’encodeURIComponent(
)方法不会对下列字符编码:ASCII字母、数字、~!*()’
一般对整个URL转码使用encodeURI
对参数进行转码时使用encodeURIComponent
, 如果使用encodeURIComponent
转码整个URL会导致 / 也被转码,URL无法使用
移动端屏幕固宽
<meta name="viewport" content="width=414,user-scalable=no,viewport-fit=cover" />
容器部署 pipeline
前端多为静态资源服务,如果不使用容器云,物理机上托管纯静态资源的方案可能是 Nginx + 静态资源,这种情况下可以只更新静态资源而不需要重新部署 Nginx。但是在容器云上部署时,目前必须在 CI 阶段把静态资源打包到镜像中。
因此,工程中应该包含:
- 静态资源(提交到 Git 或在 CI 阶段 build 生成)
- Dockerfile(提交到 Git 或在 CI 阶段使用模板及自定义)
- Nginx 配置文件
- 可选:前端工程 build 脚本
JS 拉取代码
从代码仓库 git clone
JS 构建
编译构建项目
build.sh
# $1为构建环境 development 和 production两个值
# $2为程序运行环境 值分别为 candidate、staging、production
# $3为模块,默认不传构建全部模块
env=$1
appEnv=$2
module=$3
echo $env
echo $appEnv
echo $module
set -e
echo '#######start building#######'
echo 'download npm modules'
# 加速 node-sass 的安装
export SASS_BINARY_PATH=$(pwd)/lib/sass/linux-x64-64_binding.node
npm ci
echo '*********start compiling*********'
NODE_ENV=$env APP_ENV=$appEnv MODULE_NAME=$module npm run build
echo '*********finish compiling*********'
echo '#######finish building#######'
镜像构建
- 根据 dockerfile 制作 Nginx 镜像,然后就会根据Nginx 镜像创建一个 container,Nginx 服务运行在这个 container 中。此时 Nginx 镜像中已经有之前写入的静态资源。
- 请注意 Dockerfile 中的 COPY 命令要复制你想复制的目录,比如 ./dist 而不是 ./static 。
Dockerfile
# 基础镜像
FROM registry.corp.kuaishou.com/base/nginx:tengine-2.2.2
# 复制 Nginx 配置文件到对应目录
COPY ./nginx.conf /data/nginx/conf/nginx.conf
COPY ./dist/ /data/nginx/html
COPY ./public /data/nginx/html
# 容器的默认执行命令,必须前台运行 Nginx
CMD ["/data/nginx/sbin/nginx", "-g", "daemon off;"]
nginx.conf
- 必须配置健康检查路径(如示例中的 /health),后面要设置到环境变量
- worker_processes 的值要与容器申请的 CPU 核数保持一致。例如,申请了 1 核 2G 的套餐,请填写 worker_processes 1; 不建议使用 auto,这样会导致 Nginx 启动与容器宿主机 CPU 核数相同的 worker 进程。(ref: 1. worker_processes - Core functionality - Nginx 2. [PATCH] The auto parameter of the worker_processes supports to detect the container environment.)
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;s
server {
listen 8080;
server_name localhost;
location /page/kwaishop-cps-hybrid/recommend {
root /data/nginx/html;
try_files $uri /recommend/index.html /404.html;
}
location /page/kwaishop-cps-hybrid/shelf {
root /data/nginx/html;
try_files $uri /shelf/index.html /404.html break;
}
location /page/kwaishop-cps-hybrid/pid {
root /data/nginx/html;
try_files $uri /pid/index.html /404.html;
}
location /page/kwaishop-cps-hybrid/commission {
root /data/nginx/html;
try_files $uri /commission/index.html /404.html;
}
location /page/kwaishop-cps-hybrid/outsidePromote {
try_files $uri /outsidePromote/index.html /404.html;
}
location /page/kwaishop-cps-hybrid/registration {
root /data/nginx/html;
try_files $uri /registration/index.html /404.html;
}
location /page/kwaishop-cps-hybrid/promoterSquare {
try_files $uri /promoterSquare/index.html /404.html break;
}
location /page/kwaishop-cps-hybrid/jump {
try_files $uri /jump/index.html /404.html break;
}
location /page/kwaishop-cps-hybrid/error {
try_files $uri /error/index.html /404.html break;
}
location /health {
access_log off;
return 200 "ok";
add_header Content-Type text/plain;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /data/nginx/html;
}
}
}
Vue多入口项目
多入口通过webpack entry数组进行打包
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 8080;
server_name localhost;
location /page/kwaishop-cps-hybrid/recommend {
root /data/nginx/html;
try_files $uri /recommend/index.html /404.html;
}
location /page/kwaishop-cps-hybrid/shelf {
root /data/nginx/html;
try_files $uri /shelf/index.html /404.html break;
}
location /page/kwaishop-cps-hybrid/pid {
root /data/nginx/html;
try_files $uri /pid/index.html /404.html;
}
location /page/kwaishop-cps-hybrid/commission {
root /data/nginx/html;
try_files $uri /commission/index.html /404.html;
}
location /page/kwaishop-cps-hybrid/outsidePromote {
try_files $uri /outsidePromote/index.html /404.html;
}
location /page/kwaishop-cps-hybrid/registration {
root /data/nginx/html;
try_files $uri /registration/index.html /404.html;
}
location /page/kwaishop-cps-hybrid/promoterSquare {
try_files $uri /promoterSquare/index.html /404.html break;
}
location /page/kwaishop-cps-hybrid/jump {
try_files $uri /jump/index.html /404.html break;
}
location /page/kwaishop-cps-hybrid/error {
try_files $uri /error/index.html /404.html break;
}
location /health {
access_log off;
return 200 "ok";
add_header Content-Type text/plain;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /data/nginx/html;
}
}
}
FMP
前端请求打散
为了避免类似0点抢购这种高并发场景,使前端请求在一段时间内(比如3秒)随机发出
/** 请求打散 */
export const breakupRequest = (fn, breakupTime) => {
return new Promise<void>((resolve) => {
if (!breakupTime) {
fn().then(resolve);
} else {
const delay = getRandomInt(1, breakupTime + 1);
const timer = setTimeout(() => {
fn().then(() => {
clearTimeout(timer);
resolve();
});
}, delay);
}
});
};
向下滑动加载更多
const position = ref(0);
const handleLoadMore = () => {
if (
document.body.scrollHeight - window.scrollY < window.screen.availHeight * 2 &&
window.scrollY >= position.value
) {
loadmore();
position.value = window.scrollY;
}
}