前端知识总结

187 阅读15分钟

一、Http相关

1. 状态码分类

  • 1xx - 服务器收到请求。
  • 2xx - 请求成功,如 200。
  • 3xx - 重定向,如 302。
  • 4xx - 客户端错误,如 404。
  • 5xx - 服务端错误,如 500。

2. 常见状态码

  • 200 - 成功。
  • 301 - 永久重定向(配合 location,浏览器自动处理)。
  • 302 - 临时重定向(配合 location,浏览器自动处理)。
  • 304 - 资源未被修改。
  • 403 - 没权限。
  • 404 - 资源未找到。
  • 500 - 服务器错误。
  • 504 - 网关超时。

3. HTTP1.0、HTTP1.1 和 HTTP2.0 的区别

HTTP1.1 v HTTP1.0

  1. 默认长连接
  2. 请求头增加host
  3. 请求头增加range 支持断点续传
  4. 增加一些方法(OPTIONS,PUT, DELETE, TRACE, CONNECT)
  5. 增加24个状态码 100等
  6. 增加cache-control等缓存处理

HTTP2 v HTTP1.1

  1. 报文头部压缩,差异更新HTTP头部,减少少头部流量
  2. 多路复用,多个请求在同一个TCP上完成
  3. 服务端推送

4. http和https区别是什么

  1. 传输信息安全性不同

http是明文传输,https是ssl or TLS 加密传输

  1. 连接方式不同

http连接简单是无状态的,https是由ssl+http协议构建的加密传输 身份认证的网络协议

  1. 端口不同(80 443)
  2. https的服务器需要到CA申请证书

5. http缓存

5.1 强制缓存

图片 1.png

Cache-Control:

  • 在 Response Headers 中。
  • 控制强制缓存的逻辑。
  • 例如 Cache-Control: max-age=3153600(单位是秒)

Cache-Control 有哪些值:

  • max-age:缓存最大过期时间。
  • no-cache:可以在客户端存储资源,每次都必须去服务端做新鲜度校验,来决定从服务端获取新的资源(200)还是使用客户端缓存(304)。
  • no-store:永远都不要在客户端存储资源,永远都去原始服务器去获取资源。

5.2 协商缓存(对比缓存)

  • 服务端缓存策略。
  • 服务端判断客户端资源,是否和服务端资源一样。
  • 一致则返回 304,否则返回 200 和最新的资源。

图片 2.png 资源标识:

  • 在 Response Headers 中,有两种。
  • Last-Modified:资源的最后修改时间。
  • Etag:资源的唯一标识(一个字符串,类似于人类的指纹)。

Last-Modified: 图片 3.png 服务端拿到 if-Modified-Since 之后拿这个时间去和服务端资源最后修改时间做比较,如果一致则返回 304 ,不一致(也就是资源已经更新了)就返回 200 和新的资源及新的 Last-Modified。

Etag: 图片 4.png 其实 Etag 和 Last-Modified 一样的,只不过 Etag 是服务端对资源按照一定方式(比如 contenthash)计算出来的唯一标识,就像人类指纹一样,传给客户端之后,客户端再传过来时候,服务端会将其与现在的资源计算出来的唯一标识做比较,一致则返回 304,不一致就返回 200 和新的资源及新的 Etag。

两者比较:

  • 优先使用 Etag。
  • Last-Modified 只能精确到秒级。
  • 如果资源被重复生成,而内容不变,则 Etag 更精确。

5.3 综述

图片 5.png

5.4 三种刷新操作对 http 缓存的影响

  • 正常操作:地址栏输入 url,跳转链接,前进后退等。
  • 手动刷新:f5,点击刷新按钮,右键菜单刷新。
  • 强制刷新:ctrl + f5,shift+command+r。

正常操作:强制缓存有效,协商缓存有效。 手动刷新:强制缓存失效,协商缓存有效。 强制刷新:强制缓存失效,协商缓存失效。

6. 从输入URL到浏览器显示页面过程中都发生了什么?

  1. 域名解析
  2. 判断是否有expires&cache control 强缓存 有则本地获取资源;否则向服务器发起请求(先进行第三步Tcp连接)服务器通过Etag&last-modify来进行协商缓存,无更改则返回304 Not Modified, 浏览器取本地缓存
  3. TCP三次握手建立连接
  4. 浏览器发送HTTP请求Html
  5. 服务器响应请求返回Html
  6. 浏览器解析html成一个Dom 树,解析Css规则树,将其合并构造渲染树(期间ajax请求可同步进行)
  7. 回流:在渲染树的基础上进行布局,计算每个节点的几何结构
  8. 重绘:重新绘制界面发生变化的部分。
  9. 连接结束:TCP四次挥手

二、JS基础

1. 继承有哪些方式

1.1 原型链继承

原理:使用父类实例重写子类原型

缺点:父类引用类型会被所有子类共享,无法向父类构造函数传递参数

function Father(){
    this.name = '父亲'
}
Father.prototype.say = function(){
    return this.name
}
function Son(){}
Son.prototype = new Father()
let instance = new Son()
console.log(instance.say())

1.2 借用构造函数继承

原理:改变this指向,使父类属性重写到子类

缺点:父类原型上的方法无法复用

function Father(name='父亲'){
this.name = name
this.arr = [1,2,3]
}
Father.prototype.say = function(){
console.log(this.name)
}
function Son(name){
Father.call(this,name)
}
let instance = new Son('儿子')
instance.arr.push(4)
console.log(instance) // { arr: [1, 2, 3, 4], name: "儿子" }
let instance1 = new Son()
console.log(instance1) //{ arr: [1, 2, 3], name: "父亲" }
console.log(instance1.say()) //instance1.say is not a function

1.3 组合继承

缺点:调用了两次父类型构造函数

function Father(name) {
    this.name = name
}
Father.prototype.say = function () {
    console.log(this.name)
}
function Son(name, age) {
    // 继承属性
    Father.call(this, name)
    this.age = age
}
// 继承方法
Son.prototype = new Father()
// Son.prototype.constructor = Son
Son.prototype.getAge = function () {
    console.log(this.age)
}
let instance = new Son('儿子', 18)
instance.say() // "儿子"
instance.getAge() // "18"

1.4 原型式继承&寄生式继承

缺点:父类引用类型会被所有子类共享

function object(obj){
    function F(){}
    F.prototype = obj
    return new F()
}
function createNewObj(obj){
    let newObj = object(obj)
    newObj.say = function(say){
        console.log('hi '+say)
    }
    return newObj
}
let person = {
    name:'张三',
    age:18,
    arr:[2,3]
}
let instance = createNewObj(person)
instance.say('eee') //"hi eee"
instance.arr.push(4)
instance.name = '李四'
let instance1 = createNewObj(person)
console.log(instance1.arr)  //[2, 3, 4] 共享引用属性会被篡改

1.5 寄生组合式继承

function Father(name = '父亲') {
    this.name = name
    this.arr = [1, 3]
}
Father.prototype.say = function () {
    console.log(this.name)
}
function Son(name) {
    Father.call(this, name)
}
Son.prototype = Object.create(Father && Father.prototype, {
    constructor: { value: Son, writable: true, configurable: true }
})
let instance = new Son('儿子')
instance.say() // "儿子"
instance.arr.push(5)
let instance1 = new Son('儿子1')
console.log(instance1.arr) // [1, 3]

2. 原型链

参考文章

经典原型链图

image.png image.png

Object.prototype.__proto__ === null Object.prototype是Object构造函数的原型,处于原型链的顶端
Object.__proto__ === Function.prototype (Object是构造函数,即也是函数,所以Object也是函数对象,相当于Function的实例)
Function.prototype === Function.__proto__ (最顶级)
Function.prototype.__proto__ === Object.prototype (函数的原型是一个普通object实例)

function User(){}
var u1 = new User()
var u2 = new User()
console.log(u1.__proto__ === User.prototype) // true
原因:实例的隐式原型指向构造函数的prototype
console.log(User.prototype.__proto__ === Function.prototype.__proto__) // true
原因:均指向Object.protoType,函数的原型是一个普通object实例
console.log(User.__proto__ === Function.__proto__)// true
原因:实例的隐式原型指向构造函数的prototype `User.__proto__ === Fuction.protoType`
console.log(Function.__proto__ === Object.__proto__) // true
原因:`Function.prototype ===  Function.__proto__`

3. Promise then 第二个参数和catch的区别是什么?

主要区别:.then第一个参数函数内的错误,then第二个参数的err无法捕捉,catch均可以捕捉,建议处理逻辑放在catch,不使用then第二个函数

4. async/await原理, generator, promise这三者的关联和区别是什么?

generator+promise 实现 async/await

async只是generator的一个语法糖,他相当于*,await相当于yield

asyncawait,比起星号和 yield,语义更清楚了

yield命令后面只能是 Thunk函数或 Promise对象,而 async函数的 await命令后面,可以跟 Promise对象和原始类型的值(数值、字符串和布尔值,但这时等同于同步操

参考:www.jianshu.com/p/5adc35c8b…

5. Promise finally 怎么实现的

finally和catch都是then函数的语法糖

Promise.prototype.finally = function (callback) {
    return this.then(data => {
        return Promise.resolve(callback()).then(() => data)
    },
        err => {
            return Promise.resolve(callback()).then(() => { throw err })
        })
};

6. 宏任务(Macrotask)和微任务(Microtask)

宏任务包括:script(整体代码), setTimeout, setInterval, requestAnimationFrame, I/O,setImmediate

其中setImmediate只存在于Node中,requestAnimationFrame只存在于浏览器中。

微任务包括: Promise, MutationObserver(html5新特性),process.nextTick

其中process.nextTick只存在于Node中,MutationObserver只存在于浏览器中。

注意:

执行方式:执行一个宏任务,过程中遇到微任务时,将其放到微任务的事件队列里,当前宏任务执行完成后,会查看微任务的事件队列,依次执行里面的微任务。如果还有宏任务的话,再重新开启宏任务……

测试题

    console.log('a');

    setTimeout(function() {
        console.log('b');
        process.nextTick(function() {
            console.log('c');
        })
        new Promise(function(resolve) {
            console.log('d');
            resolve();
        }).then(function() {
            console.log('e')
        })
    })
    process.nextTick(function() {
        console.log('f');
    })
    new Promise(function(resolve) {
        console.log('g');
        resolve();
    }).then(function() {
        console.log('h')
    })

    setTimeout(function() {
        console.log('i');
        process.nextTick(function() {
            console.log('j');
        })
        new Promise(function(resolve) {
            console.log('k');
            resolve();
        }).then(function() {
            console.log('l')
        })
    })

三、手写代码系列

1 实现new操作符

function new1(Fn,...rest){
    let obj = {}
    obj.__proto__ = Fn.protoType
    let res = Fn.apply(obj,[...rest])
    return res instanceof Object?res:obj
}
function A(name){
    this.a= name
}
let b = new1(A,'w飞机呃呃')
console.log(b) //{ a: "w飞机呃呃" }

2. 实现call & apply & bind

Function.protoType.call1=function(ctx,...rest){
    ctx.fn = this
    let res = ctx.fn(...rest)
    delete ctx.fn
    return res
}
Function.protoType.apply1=function(ctx){
    ctx.fn = this
    let res =arguments(1)? ctx.fn(...arguments(1)):ctx.fn()
    delete ctx.fn
    return res
}
Function.protoType.mybind = function(ctx,...rest){
    let fn = this
    function a(){
        return fn.apply(this instanceof fn? this:ctx,[...rest,...arguments])
        // 此处判断this instanceof fn 原因是因为: let fn1 = fn.bind(obj);let fn2 = new fn1()这种情况,需要使用构造函数fn1当做this,而不是obj
    }
    a.protoType = Object.creat(fn.protoType)
    return a
}
fuction a(name){
    this.name = name
}
let obj1 = {age: 34}
let f1 = a.mybind(obj1)('wo')
console.log(obj1) // { age: 34, name: "wo" }
let newData = new f1('new')
console.log(newData) //{ name: "new" }

3. 实现Promise

class Mypromise {
    constructor(fn) {
        this.status = 'pengding'
        this.value = undefined
        this.msg = undefined
        this.resolveArr = []
        this.rejectArr = []
        let resolve = value => {
            if (this.status === 'pengding') {
                this.status = 'resolved'
                this.value = value
                this.resolveArr.forEach(fn=>fn())
            }
        }
        let reject = value => {
            if (this.status === 'pengding') {
                this.status = 'rejected'
                this.value = value
                this.rejectArr.forEach(fn=>fn())
            }
        }
        try {
            fn(resolve, reject)
        } catch (e) {
            reject(e)
        }

    }
    then(fn1, fn2) {
        if (this.status === 'resolved') {
            fn1(this.value)
        }
        if (this.status === 'rejected') {
            fn2(this.value)
        }
        if(this.status === 'pengding'){
            this.resolveArr.push(()=>fn1(this.value))
            this.rejectArr.push(()=>fn2(this.value))
        }
    }
}
new Mypromise((resolve, reject) => {
    setTimeout(()=>{
        resolve(6)
    },1000)
}).then(res => {
    console.log('success',res)
}, res2 => {
    console.error('222', res2)
})

4. 实现防抖和节流

function debounce(fn, delay = 0) {
    let timer = null
    return function () {
        if (timer) {
            clearTimeout(timer)
        }
        timer = setTimeout(() => {
            fn.apply(this, arguments)
            timer = null
        }, delay)
    }
}
function debounce(fn, wait, immediate) {
    let timer = null
    return function () {
        let doNow = !timer && immediate
         if (doNow) {
            fn.apply(this, arguments)
        }
        clearTimeout(timer)
        let fn1 = function () {
            timer = null
            if (!immediate) {
                fn.apply(this, arguments)
            }
        }
        timer = setTimeout(fn1, wait)
    }
};

function throttle(fn, delay = 0) {
    let timer = null
    return function () {
        if (timer) {
            return
        }
        timer = setTimeout(() => {
            fn.apply(this, arguments)
            timer = null
        },delay)
    }
}
document.addEventListener('keyup', throttle(function () {
    console.log(2)
}, 600))

5. 实现深拷贝

function deepClone(obj){
    if(typeof obj === 'object'&&obj!==null){
        var result = Array.isArray(obj)?[]:{}
        for(let key in obj){
            result[key]=typeof obj[key] === 'object'?deepClone( obj[key]):obj[key]
        }
    }else{
        var  result = obj
    }
    return result
}

6. 实现instanceof

function instance(left, right) {
    if (typeof left !== 'object' && typeof left !=='function' || typeof left === null) {
        return false
    }
    if (typeof right !== 'function') {
        throw Error('right is not a function')
    }
    let proto = left.__proto__
    let prototype = right.prototype
    while (true) {
        if(proto === null){return false}
        if (proto === prototype) { return true }
        proto = proto.__proto__
    }
}
function a(){
    this.a = '2'
}
let b = new a()
console.log(instance(b, a))

7. 实现curry函数

function toCurry(fn,len){
    var len = len || fn.length
    return function curry(){
        var args = [...arguments]
        if(len<=args.length){
            return fn.apply(null,args)
        }else{
            return function(){
                return curry.apply(null,[...args,...arguments])
            }
        }
    }
}
function add (x,y,z){
    return x+y+z
}
var a = toCurry(add,3)
console.log(a(1,2,3))
console.log(a(1)(2,3))

8. 递归实现数组转树结构

const arr = [{
    id: 2,
    name: '部门B',
    parentId: 0
},
{
    id: 3,
    name: '部门C',
    parentId: 1
},
{
    id: 1,
    name: '部门A',
    parentId: 2
},
{
    id: 4,
    name: '部门D',
    parentId: 1
},
{
    id: 5,
    name: '部门E',
    parentId: 2
},
{
    id: 6,
    name: '部门F',
    parentId: 3
},
{
    id: 7,
    name: '部门G',
    parentId: 2
},
{
    id: 8,
    name: '部门H',
    parentId: 4
}
]
function toTree(arr,parentId){
    return arr.filter(v=>v.parentId === parentId).map(v=>{
        return {...v,children:toTree(arr,v.id)}
    })
}
console.log(toTree(arr,0))

9. 去除字符串出现次数最少的字符,不改变字符顺序

var str = 'abccddde'
function del(str) {
    let obj = {}
    for (let key of str) {
        obj[key] ? obj[key]++ : obj[key] = 1
    }
    let temp
    // for (let key in obj) {
    //     if (!temp || obj[key] <= temp) {
    //         temp = obj[key]
    //     }
    // }
    temp = Math.min(...Object.values(obj))
    for (let key in obj) {
        if (obj[key] === temp) {
            let reg = new RegExp(key, 'g')
            str = str.replace(reg, '')
        }
    }
    return str
}
console.log(del(str))

10. 数组扁平化

var arr = [1, 3, [4, 5, [6, 7]]]
arr.flat(Infinity)

function flat(arr) {
    return arr.reduce((pre, current) => {
        return pre.concat(Array.isArray(current) ? flat(current) : current)
    }, [])
}

function flat1(arr) {
    while (arr.some(item => Array.isArray(item))) {
        arr = [].concat(...arr)
    }
    return arr
}
// console.log(flat(arr))
// console.log(flat1(arr))

11. 实现Promise限制并发请求,并且最快速度请求完成,返回的按顺序排列

function creq(urls = [], limit = 6) {
    return new Promise((resolve) => {
        if (urls.length == 0) {
            resolve([])
        }
        let i = 0; // 执行的请求的下标
        let result = []; // 存储执行完的结果
        let count = 0; //执行完的函数的个数
        function request() {
            if (i === urls.length) return
            let url = urls[i]
            let index = i;
            i++
            new Promise(resolve => {
                setTimeout(() => {
                    resolve(url)
                },Math.random()*5*1000)
            }).then(res => {
               console.log('ressss',res)
                result[index] = res
                count++
                 if (count === urls.length) {
                    resolve(result)
                }
                request()
            }).catch(err => {
                if (count === urls.length) {
                    resolve(result)
                }
                result[index] = err
                count++
                request()
            })
        }
        const times = Math.min(limit, urls.length);
        for (let i = 0; i < times; i++) {
            request();
            console.log(i)
        }

    })
}
const urls = [];
for (let i = 1; i <= 20; i++) {
    urls.push(`https:------${i}`);
}
creq(urls, 6).then(res => {
    console.log(res);
})

12. 实现sleep函数

const sleep1 = function (time) {
    return new Promise((resolve, reject) => {
        setTimeout(() => resolve(), time)
    })
}
let do1 = async function () {
    console.log(0)
    await sleep1(2000)
    console.log(1)
    await sleep1(2000)
    console.log(2)
}
do1()

13. 快速排序

这里对快排思想不太明白的同学可以看下这个讲解的很清晰的视频:快速排序算法

function sortArray(nums) {
  quickSort(0, nums.length - 1, nums);
  return nums;
}

function quickSort(start, end, arr) {
  if (start < end) {
    const mid = sort(start, end, arr);
    quickSort(start, mid - 1, arr);
    quickSort(mid + 1, end, arr);
  }
}

function sort(start, end, arr) {
  const base = arr[start];
  let left = start;
  let right = end;
  while (left !== right) {
    while (arr[right] >= base && right > left) {
      right--;
    }
    arr[left] = arr[right];
    while (arr[left] <= base && right > left) {
      left++;
    }
    arr[right] = arr[left];
  }
  arr[left] = base;
  return left;
}

四、 代码题

1. 自执行函数输出什么

// 
var b = 10;
(function b(){
    b = 20;
    console.log(b); // function b 自执行函数赋值无效
    // var b = 10;  // 如果加了这句 那么输出 20 10
    console.log(b) 
})();

2. 虚拟dom转真实dom

const vnode = {
    tag: 'DIV',
    attrs: {
        id: 'app'
    },
    children: [{
            tag: 'SPAN',
            children: [{
                tag: 'A',
                children: []
            }]
        },
        {
            tag: 'SPAN',
            children: [{
                    tag: 'A',
                    children: []
                },
                {
                    tag: 'A',
                    children: []
                }
            ]
        }
    ]
}

function render(vnode) {

}

3. this指向题

var name = '123';
var obj = {
	name: '456',
	print: function() {
		function a() {
			console.log(this.name);
		}
		a();
	}
}
obj.print(); // 123  
//对象方法中嵌套了方法,那么内部方法的this指向会混乱,指向全局对象,而不是期望的当前对象:
var a=3;
 function c(){
    alert(a);
 }
 (function(){
  var a=4;
  c();
 })();
 // 3  js 中变量的作用域链与定义时的环境有关,与执行时无关。执行环境只会改变 this、传递的参数、全局变量等
function Foo(){
    Foo.a = function(){
        console.log(1);
    }
    this.a = function(){
        console.log(2)
    }
}

Foo.prototype.a = function(){
    console.log(3);
}

Foo.a = function(){
    console.log(4);
}

Foo.a(); 
let obj = new Foo();
obj.a();
Foo.a();

Foo.a() 这个是调用 Foo 函数的静态方法 a,虽然 Foo 中有优先级更高的属性方法 a,但 Foo 此时没有被调用,所以此时输出 Foo 的静态方法 a 的结果:4
let obj = new Foo(); 使用了 new 方法调用了函数,返回了函数实例对象,此时 Foo 函数内部的属性方法初始化,原型链建立。
 obj.a() ; 调用 obj 实例上的方法 a,该实例上目前有两个 a 方法:一个是内部属性方法,另一个是原型上的方法。当这两者都存在时,首先查找 ownProperty ,如果没有才去原型链上找,所以调用实例上的 a 输出:2
Foo.a() ; 根据第2步可知 Foo 函数内部的属性方法已初始化,覆盖了同名的静态方法,所以输出:1
原文链接:https://blog.csdn.net/MFWSCQ/article/details/105148289
const obj = {
	fn1: () => console.log(this),
	fn2: function() {console.log(this)}
}

obj.fn1();
obj.fn2();
const x = new obj.fn1();
const y = new obj.fn2();

五、 Vue相关

1. keep-alive 原理

created & destroyed

image.png

  1. created钩子会创建一个cache对象,用来作为缓存容器,保存vnode节点。
  2. keys用来缓存虚拟dom的键集合
  3. destroyed钩子则在组件被销毁的时候清除cache缓存中的所有组件实例。

mounted

在mounted这个钩子中对include和exclude参数进行监听,然后实时地更新(删除)this.cache对象数据。

render

  1. 获取keep-alive包裹着的第一个子组件对象及其组件名;
  2. 根据设定的黑白名单(如果有)进行条件匹配,决定是否缓存。不匹配,直接返回组件实例(VNode),否则执行第三步;
  3. 根据组件ID和tag生成缓存Key,并在缓存对象中查找是否已缓存过该组件实例。如果存在,直接取出缓存值并更新该key在this.keys中的位置(更新key的位置是实现LRU置换策略的关键),否则执行第四步;
  4. 在this.cache对象中存储该组件实例并保存key值,之后检查缓存的实例数量是否超过max的设置值,超过则根据LRU置换策略删除最近最久未使用的实例(即是下标为0的那个key); 最后将该组件实例的keepAlive属性值设置为true;

参考:juejin.cn/post/688160…

六、 错误捕捉方式

1. 线上监控 对于crashed这种怎么监控?

基于 Service Worker 心跳的方案: zhuanlan.zhihu.com/p/40273861

2.错误信息是最基础也是最重要的数据,错误信息主要分为下面几类:

  • JS 代码运行错误、语法错误等
  • 异步错误等
  • 静态资源加载错误
  • 接口请求报错

1. try/catch

只能捕获代码常规的运行错误,语法错误和异步错误不能捕获到

2. window.onerror

window.onerror 可以捕获常规错误、异步错误,但不能捕获资源错误

3. error

当静态资源加载失败时,会触发 error 事件。

window.addEventListener('error', (error) => { console.log('捕获到异常:', error); }, true)

4. Promise中抛出的错误

Promise中抛出的错误,无法被 window.onerror、try/catch、 error 事件捕获到,可通过 unhandledrejection 事件来处理

window.addEventListener("unhandledrejection", 
    function(e) { 
        console.log("捕获到异常", e); // preventDefault阻止传播,不会在控制台打印
        e.preventDefault();
    }
);

5. vue 异常监控

Vue.config.errorHandler = (err, vm, info) => { console.log('进来啦~', err); }

七、安全问题

1. XSS & CSRF攻击

image.png

八、性能优化

代码层面:

  • 防抖和节流(resize,scroll,input)。
  • 减少回流(重排)和重绘。
  • 事件委托。
  • 减少 DOM 操作。
  • 按需加载

构建方面:

  • 压缩代码文件,在 webpack 中使用 terser-webpack-plugin 压缩 Javascript 代码;使用 css-minimizer-webpack-plugin 压缩 CSS 代码;使用 html-webpack-plugin 压缩 html 代码。
  • 开启 gzip 压缩,webpack 中使用 compression-webpack-plugin ,node 作为服务器也要开启,使用 compression
  • 常用的第三方库使用 CDN 服务,在 webpack 中我们要配置 externals,将比如 React, Vue 这种包不打倒最终生成的文件中。而是采用 CDN 服务。

其它:

  • 使用 http2。因为解析速度快,头部压缩,多路复用,服务器推送静态资源。
  • 使用服务端渲染。
  • 图片压缩。
  • 使用 http 缓存,比如服务端的响应中添加 Cache-Control / Expires

二 : CSS篇

1. BFC是什么? 哪些属性可以构成一个BFC呢?

BFC指块级格式化上下文,创建了一个独立的渲染区域,隔绝内外环境,计算BFC高度时,浮动元素也要参与计算

常见构成BFC属性:

overflow: hidden;
display: flex;
display: inline-flex;
display: inline-block;
position: absolute;
position: fixed;

应用场景:

  1. 解决浮动元素无法撑起父盒子高度问题
  2. 子元素设置margin-top与父元素边距塌陷问题

2. postion属性大概讲一下, static是什么表现? static在文档流里吗?

postion:static inherit relative absolute fixed

static 和relative都在文档流里,区别是static设置top等不生效, relative会生效

待看常见问题

  • vue3
  • websocket连接原理
  • Set和Map
  • 前端稳定性监控
  • 给一个字符串, 找到第一个不重复的字符

参考

1. http全面精读

2. 模块化详解

3. Vue面试题参考

4. 页面卡顿,内存泄露排查指南

5. 一文搞懂事件循环

juejin.cn/post/716352…

juejin.cn/post/706158…