面试题总结

kingcwt2021-12-1前端面试题

从输入一个 URL 地址到浏览器完成渲染的整个过程

1 输入url并回车
2 浏览器查看当前url是否存在缓存,缓存是否过期
3 dns解析当前url对应的ip
4 根据ip建立tcp连接(三次握手)
5 发送http请求
6 服务器收到请求,并返回响应和内容
7 浏览器解析并渲染页面
8 关闭tcp连接(四次挥手)

史上最详细的经典面试题 从输入URL到看到页面发生了什么?

什么是事件代理(事件委托) 有什么好处

一、事件委托原理:

不单独给每个子节点设置监听器。而是给其父节点设置事件监听器,然后利用冒泡原理设置每个子节点

二、优点:

减少内存消耗和dom操作,提高性能。因为页面上事件监听器的数量将关系到页面的整体运行速度,操作dom频繁,会引起浏览器重绘和回流

apply call bind 区别

1 三者都可以改变this的指向
2 三者的第一个参数都是this要指向的对象,如果不传,为undefined或则null,则this指向全局window
3 apply是数组,call是参数列表,apply和call是一次性传入参数,而bind可以分为多次传入
4 bind返回绑定this之后的函数,便于稍后调试。而call和apply是立即执行
5 如果bind返回的新函数作为构造函数创建一个新对象,this不再指向传入bind的第一个参数对象,而是指向当前实例

webpack性能优化

一 、exclude/include

我们可以通过exclude/include配置选项,来过滤掉一些不需要转译的文件,使打包后的文件变的更小
exclude: 指定要排除的文件
include: 指定要包含的文件

const path = require('path');
module.exports = {
  //....
  module:{
    rules:[
      {
        test:/\.js[x]?$/,
        use:['babel-loader'],
        include:[path.resolve(__dirname,'src')]
      }
    ]
  }
}

二 、cache-loader

在性能开销比较大的loader前,加入cache-loader,将结果缓存在磁盘中,默认保存在node_modules/.cache/cache-loader目录下

安装:yarn add cache-loader -D

//...
module.exports = {
  module:{
    rules:[
      {
        test:/\.js[x]?$/,
        use:['cache-loader','babel-loader'] 
      }
    ]
  }
}

如果只打算给babel-loader设置cache的话。我们也可以不使用cache-loader,我们可以给babal-loader增加选项cacheDirectory

cacheDirectory:默认为false,当有设置时,指定的目录将用来缓存loader的执行结果,之后的webpack构建将默认读取缓存

三 、happypack

当有大量的文件需要解析构建的时候,webpack构建慢的问题会显的很严重。happypack可以同一时刻处理多个任务,发挥cpu电脑的威力
happypack可以让webpack做到这点,它把任务分解给多个子进程并发去执行,子进程处理完后会再发送给主进程

yarn add happypack -D

const Happypack = require('happypack');
module.exports = {
    //...
    module: {
        rules: [
            {
                test: /\.js[x]?$/,
                use: 'Happypack/loader?id=js',
                include: [path.resolve(__dirname, 'src')]
            },
            {
                test: /\.css$/,
                use: 'Happypack/loader?id=css',
                include: [path.resolve(__dirname, 'src')]
            }
        ]
    },
    plugins: [
        new Happypack({
            id: 'js', //和rule中的id=js对应
            //将之前 rule 中的 loader 在此配置
            use: ['babel-loader'] //必须是数组
        }),
        new Happypack({
            id: 'css',//和rule中的id=css对应
            use: ['style-loader', 'css-loader','postcss-loader'],
        })
    ]
}

四 、webpack-dev-server(WDS)热更新实现原理

WDS启动服务后,服务端会和客户端建立一个长链接,当文件修改后,服务端会通过长链接向客户端发送一条消息,
客户端收到消息后,会重新请求一个js文件,返回的js文件会调用webpackHotUpdatehmr方法,然后替换掉__webpack_module__中的部分代码

Webpack 热更新原理

浏览器缓存

一 、强缓存

第一次进入页面,请求服务器,然后服务器应答,浏览器会根据response Header来判断是否对资源进行缓存, 如果响应头中存在,expires,pragma或者cache-control字段,代表这是强缓存,浏览器会把资源存在memory cache,或disk cache中
第二次请求时,浏览器判断请求参数,如果符合强缓存条件就直接返回状态码200,从本地缓存中读取数据,否则把响应参数存在request Header请求头中, 看是否符合协商缓存,符合则返回状态码304,不符合则服务器返回全新资源

expires

值是一个时间戳,服务器返回该请求结果缓存的到期时间(一个格林尼治时间)

有个缺点,就是它判断是否过期是用本地时间来判断的,本地时间是可以自己修改的。

cache-control

cache-control优先级高于expires

取值为:
public:客户端和服务器端都可以缓存
privite:客户端可以缓存
no-cache:客户端可以缓存,但是是否缓存需要协商缓存来验证
no-store:不使用缓存
max-age:缓存保质期

pragma

取值为no-cache和cache-control中的no-cache效果一样

二 、缓存位置

存储图像和网页等资源主要缓存在disk cache,操作系统缓存文件等资源大部分都会缓存在memory cache中。具体操作浏览器自动分配,看谁的资源利用率不高就分给谁。
查找浏览器缓存时会按顺序查找: Service Worker-->Memory Cache-->Disk Cache-->Push Cache

1. Service Worker

运行在浏览器背后的独立线程,一般可以用来实现缓存功能,传输协议必须为 HTTPS,因为 Service Worker 中涉及到请求拦截,所以必须使用 HTTPS 协议来保障安全。Service Worker 的缓存与浏览器其他内建的缓存机制不同,它可以让我们自由控制缓存哪些文件、如何匹配缓存、如何读取缓存,并且缓存是持续性的

2. Memory Cache

缓存在内存中

3. Disk Cache

缓存在硬盘中,读取速度慢点,但是什么都能存储到磁盘中,比之 Memory Cache 胜在容量和存储时效性上。

4. Push Cache

缓存在会话(Session)中,当以上三种缓存都没有命中时,它才会被使用。一旦会话结束就被释放,并且缓存时间也很短暂,在Chrome浏览器中只有5分钟左右,同时它也并非严格执行HTTP头中的缓存指令。

三 、协商缓存

协商缓存就是强缓存失效后,浏览器携带缓存标识向服务器发送请求,由服务器根据缓存标识来决定是否使用缓存,协商缓存生效返回状态码304,失效返回 状态码200和请求结果

Last-Modified / If-Modified-Since

Last-Modified是服务器响应请求时,返回该资源文件在服务器最后被修改的时间,存在响应头中
If-Modified-Since是客户端再次请求服务器时,存在于请求头中,并且携带上次请求返回的Last-Modified值,服务器收到请求后, 根据If-Modified-Since的字段值与该资源在服务器的最后被修改时间做对比,大于If-Modified-Since的字段值则重新返回资源,状态码为 200,否则返回304,代表资源无更新,可继续使用缓存文件

Etag / If-None-Match

Etag是服务器响应请求时,返回当前资源文件的一个唯一标识(由服务器生成) 存在于响应头中
If-None-Match是客户端再次发起该请求时,存在于请求头中,并且携带上次请求返回的唯一标识Etag值,服务器收到该请求后,则会根据If-None-Match的字段值与该资源在服务器的Etag值做对比,一致则返回304,代表资源无更新,继续使用缓存文件;不一致则重新返回资源文件,状态码为200

四 、缓存有哪些好处?

1 缓解服务器压力
2 提升性能,打开本地资源肯定会比请求服务器来的快
3 减少带宽消耗

五 、刷新对于强缓存和协商缓存的影响

  1. 当ctrl+f5强制刷新网页时,直接从服务器加载,跳过强缓存和协商缓存。
  2. 当f5刷新网页时,跳过强缓存,但是会检查协商缓存。
  3. 浏览器地址栏中写入URL,回车 浏览器发现缓存中有这个文件了,不用继续请求了,直接去缓存拿。(最快)

前端浏览器缓存知识梳理

React中 父子组件生命周期渲染顺序

import React from "react";
import Child from './Child';
class Parent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      isChanged: "false"
    };
  }

  UNSAFE_componentWillUpdate() {
    console.log("Parent componentWillUpdate");
  }
  componentDidUpdate() {
    console.log("Parent componentDidUpdate");
  }
  UNSAFE_componentWillMount(){
    console.log("Parent componentWillMount");
  }
  componentDidMount() {
    console.log("Parent componentDidMount");
  }

  render() {
    //渲染三个子组建,并提供一个按钮,当按钮被点击时强行update
    console.log("Parent Render");
    return (
      <div>
        <Child order="1st" />
        <button
          onClick={() => {
            this.setState({
              isChanged: "true"
            });
          }}
        >
          Change Sup Data
        </button>
        <div>{this.state.isChanged}</div>
      </div>
    );
  }
}

export default Parent;

//-------------child


import React from "react";
class Child extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      isUpdate: false
    };
  }

  UNSAFE_componentWillReceiveProps(nextProps) {
    console.log(this.props.order + " ChildComponent componentWillReceiveProps");
  }
  UNSAFE_componentWillMount(){
    console.log(this.props.order + " ChildComponent componentWillMount");
  }
  componentDidMount() {
    console.log(this.props.order + " ChildComponent componentDidMount");
  }
  UNSAFE_componentWillUpdate() {
    console.log(this.props.order + " ChildComponent componentWillUpdate");
  }
  componentDidUpdate() {
    console.log(this.props.order + " ChildComponent componentDidUpdate");
  }

  render() {
    console.log(this.props.order + " ChildComponent Render");
    /*接受一个从父元素传来的props.order*/
    return (
      <button
        onClick={() => {
          this.setState({ isUpdate: true });
        }}
      >
        {this.props.order + " button updated? : " + this.state.isUpdate}
        <br />
      </button>
    );
  }
}

export default Child;

执行渲染顺序

  • Parent componentWillMount
  • Parent Render
  • ChildComponent componentWillMount
  • ChildComponent Render
  • ChildComponent componentDidMount
  • Parent componentDidMount

如果父组件某一个状态发生变化和子组件无关 执行顺序是什么?

  • Parent componentWillUpdate
  • Parent Render
  • ChildComponent componentWillReceiveProps
  • ChildComponent componentWillUpdate
  • ChildComponent Render
  • ChildComponent componentDidUpdate
  • Parent componentDidUpdate

设置shouldComponentUpdate,避免子组件不必要的更新

shouldComponentUpdate(nextProps, nextState){
    if(nextState.isUpdate == this.state.isUpdate){
        return false
    }
    return true
  }

执行顺序如下:

  • Parent componentWillUpdate
  • Parent Render
  • ChildComponent componentWillReceiveProps
  • Parent componentDidUpdate

什么是单页面应用和多页面应用,各自的优缺点是什么?

单页面应用

单页面应用,通俗来说就是只有一个主页面的应用,浏览器一开始要加载所有必须的html,js,css,所有的页面内容都包含在这个主页面中, 但是在写的时候,还是会分开写(页面片段)

多页面应用

多页面应用,就是指一个应用中有多个页面,页面跳转时要刷新整个页面

单页面的优缺点

优点:用户体验好,快,内容的改变不需要重新加载整个页面,对服务器压力小,前后端分离
缺点:不利于seo,导航不可用,要自行实现前进,后退,初次加载耗时多,页面复杂度提高

多页面应用的优缺点

优点:利于seo,导航可用,初次加载耗时低,页面复杂度相对较低
缺点:用户体验一般,内容的改变需要加载整个页面,对服务器压力相对较大

防抖

function throttle(fn,delay=300){
  let timer;
  return function (){
    let args = arguments;
    if(timer){
      clearInterval(timer);
    }
    timer = setTimeout(()=>{
      fn.apply(this,args);
    },delay)
  }
}

window.addEventListener('scroll',shake(()=>{
  console.log(111)
},10))

节流

function shake(fn,delay=300){
  let flag = true;
  return ()=>{
    if(!flag) return;
    flag = false;
    timer = setTimeout(()=>{
      fn();
      flag = true;
    },delay) 
  }
}

window.addEventListener('scroll',throttle(()=>{
  console.log(2222)
},10))

flex:1是有哪些属性组成

flex实际上是有flex-grow,flex-shrink,flex-basis组成

  • flex-grow:定义项目的放大比例
  • flex-shrink:定义项目的缩小比例
  • flex-basis:项目占据的主轴空间,定义在分配多余空间之前,浏览器根据此属性计算主轴是否有多余空间

实现useState方法

import ReactDOM from "react-dom";

let _state;
function _useState(initialValue){
  _state = _state||initialValue;
  function setState(newState){
    _state = newState;
    render();
  }
  return [_state,setState];
}
function App (){
  let [count,setCount] = _useState(1);



  return (
    <div>
      <div>{count}</div>
      <button onClick={() => { setCount(count + 1); }}>
        点击
      </button>
    </div>
  );
}

export default App;


function render() {
  ReactDOM.render(<App/>,document.getElementById('root'));}
render();
Last Updated 10/16/2023, 7:06:22 AM
What do you think?
  • 0
  • 0
  • 0
  • 0
  • 0
  • 0
Comments
  • Latest
  • Oldest
  • Hottest
Powered by Waline v2.15.8