ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

vue 前端面试常见问题汇总(11-20)

2021-07-19 14:58:59  阅读:528  来源: 互联网

标签:11 function 缓存 20 Cache 观察者 首屏 常见问题 元素


目录

前言

         一、谈一谈 nextTick 的原理?

         二、vue 是如何对数组方法进行变异的?例如 push、pop、splice 等方法

三、Vue 中的 computed 和 watch 的区别在哪里?(虾皮)

四、v-if、v-show、v-html 的原理是什么,它是如何封装的?

五、请求时浏览器缓存 from memory cache 和 from disk cache 的依据是什么,哪些数据什么时候存放在 Memory Cache 和 Disk Cache中?

1.Service Worker

2.Memory Cache

3.Disk Cache

4.Push Cache

六、如何在 H5 和小程序项目中计算白屏时间和首屏时间?

 

 七、介绍下观察者模式和订阅-发布模式的区别,各自适用于什么场景?

观察者模式(Observer Pattern)

发布/订阅模式

 八、怎么让一个 div 水平垂直居中?

 九、分析比较 opacity: 0、visibility: hidden、display: none 优劣和适用场景?

 十、介绍下 BFC、IFC、GFC 和 FFC

总结


 


前言

前端面试问题,可能问题不是很全面,但基本上是常见的且自我补充的一个过程,相信从中可以完善自己。另外部分见解答案解析来自github博客等资料整理过程中如有错误欢迎指出!

一、谈一谈 nextTick 的原理?

Vue.js中的 nextTick 函数,会传入一个 cb ,这个 cb 会被存储到一个队列中,在下一个 tick 时触发队列中的所有 cb 事件。

Vue.js 肯定不会以如此低效的方法来处理。Vue.js在默认情况下,每次触发某个数据的 setter 方法后,对应的 Watcher 对象其实会被 push 进一个队列 queue 中,在下一个 tick 的时候将这个队列 queue 全部拿出来 run( Watcher 对象的一个方法,用来触发 patch 操作) 一遍。

因为目前浏览器平台并没有实现 nextTick 方法,所以 Vue.js 源码中分别用 Promise、setTimeout、setImmediate 等方式在 microtask(或是task)中创建一个事件,目的是在当前调用栈执行完毕以后(不一定立即)才会去执行这个事件。

export function nextTick (cb?: Function, ctx?: Object) {
  let _resolve
  callbacks.push(() => {
    if (cb) {
      try {
        cb.call(ctx)
      } catch (e) {
        handleError(e, ctx, 'nextTick')
      }
    } else if (_resolve) {
      _resolve(ctx)
    }
  })
  if (!pending) {
    pending = true
    timerFunc()
  }
  // $flow-disable-line
  if (!cb && typeof Promise !== 'undefined') {
    return new Promise(resolve => {
      _resolve = resolve
    })
  }
}

首先定义一个 callbacks 数组用来存储 nextTick,在下一个 tick 处理这些回调函数之前,所有的 cb 都会被存在这个 callbacks 数组中。pending 是一个标记位,代表一个等待的状态。
这里用setTimeout做描述(真实源码里更复杂):
setTimeout 会在 task 中创建一个事件 flushCallbacks ,flushCallbacks 则会在执行时将 callbacks 中的所有 cb 依次执行。

// timerFunc就是nextTick传进来的回调等... 细节不展开
if (typeof Promise !== 'undefined' && isNative(Promise)) {
    const p = Promise.resolve()
    timerFunc = () => {
        p.then(flushCallbacks)
    }
    isUsingMicroTask = true
} else if (!isIE && typeof MutationObserver !== 'undefined' && (
    // 当原生 Promise 不可用时,timerFunc 使用原生 MutationObserver
    // MutationObserver不要在意它的功能,其实就是个可以达到微任务效果的备胎
)) {
    timerFunc = () => {
        // 使用 MutationObserver
    }
    isUsingMicroTask = true

} else if (typeof setImmediate !== 'undefined' &&  isNative(setImmediate)) {
    // 如果原生 setImmediate 可用,timerFunc 使用原生 setImmediate
    timerFunc = () => {
        setImmediate(flushCallbacks)
    }
} else {
    // 最后的倔强,timerFunc 使用 setTimeout
    timerFunc = () => {
        setTimeout(flushCallbacks, 0)
    }
}

  • 涉及到js线程中的微任务和宏任务.在一次事件循环中,先执行微任务,再执行宏任务
  • 宏任务: setTimeout,setInterval
  • 微任务: promise,requestAnimation,MutationObserve
  • nextTick好处: 碰到太频繁的js操作,只需要显示最后一次的数据的视图,如果每次都实时更新视图,会消耗太多性能

查询浏览器支持的程度,先后执行Promise > MutationObserver > setImmediate > setTimeout

二、vue 是如何对数组方法进行变异的?例如 push、pop、splice 等方法

为什么要对数组进行单独处理?

在Vue现有阶段中,对响应式处理利用的是Object.defineProperty对数据进行拦截,而这个方法并不能监听到数组内部变化,数组长度变化,数组的截取变化等,所以我们需要对这些操作进行hack,让vue能监听到其中的变化。(Object.defineProperty我上一篇文章里面就有提到,这里不过多描述了)

methodsToPatch.forEach(function(method) {
    // cache original method
    // 获取原方法
    var original = arrayProto[method];
    // def方法重新定义arrayMethods的method方法,然后将新的取值方法赋值
    def(arrayMethods, method, function mutator() {
      var args = [],
        len = arguments.length;
      while (len--) args[len] = arguments[len];

      var result = original.apply(this, args);
      var ob = this.__ob__;
      var inserted;
      switch (method) {
        case 'push':
        case 'unshift':
          // [].push(1),[].unshift(1)
          // arg = [1]
          inserted = args;
          break
        case 'splice':
          // [1,2,3].splice(0,1,1)
          // 第三个参数为插入的值
          inserted = args.slice(2);
          break
      }
      if (inserted) { ob.observeArray(inserted); }
      // 监听变化,如果不是插入操作直接循环响应
      // 如果是去除数组参数方法,触发一次notify将会重新计算
      // 如果仅仅是数字数据,任何操作只需要再次执行一次notify则可以
      // 但是如果新增的是一个对象类型,就需要重新监听
      // 为什么用角标和length属性不能监听的原因是因为无法触发obj的get方法,所以没法动态监听
      // notify change
      ob.dep.notify();
      return result
    });
  });

vue对push,pop,splice等方法进行了hack,hack方式很简单,如果加入新对象,对新对象进行响应式化,至于如何响应式化请参考vue源码。
举例来说对于push和unshift会推入一个新的对象到数组里(不管从前还是从后),记录这个加入的对象,并调用Observe方法将加入的对象转换成响应式对象,对于splice方法,如果加入了新对象也是将该对象响应式化。
最后一步是向外抛出数组变化,提醒观察者进行更新。 

对于Object.defineProperty的缺陷导致如果直接改变数组下标是无法hack的,解决方法在上一篇文章内容中提到了Object.defineProperty和Proxy(vue 3.0)

三、Vue 中的 computed 和 watch 的区别在哪里?(虾皮)

computed:计算属性

计算属性是由data中的已知值,得到的一个新值。
这个新值只会根据已知值的变化而变化,其他不相关的数据的变化不会影响该新值。
计算属性不在data中,计算属性新值的相关已知值在data中。
别人变化影响我自己。
watch:监听数据的变化

监听data中数据的变化
监听的数据就是data中的已知值
我的变化影响别人

1.watch擅长处理的场景:一个数据影响多个数据

2.computed擅长处理的场景:一个数据受多个数据影响

  • 功能上:computed是计算属性,也就是依赖其它的属性计算所得出最后的值。watch是去监听一个值的变化,然后执行相对应的函数

  • 使用上:computed中的函数必须要用return返回;watch的回调里面会传入监听属性的新旧值,通过这两个值可以做一些特定的操作,不是必须要用return

  • 性能上:computed中的函数所依赖的属性没有发生变化,那么调用当前的函数的时候会从缓存中读取,而watch在每次监听的值发生变化的时候都会执行回调

  • 场景上:computed:当一个属性受多个属性影响的时候,例子:购物车商品结算;watch:当一条数据影响多条数据的时候,例子:搜索框

四、v-if、v-show、v-html 的原理是什么,它是如何封装的?

v-if会调用addIfCondition方法,生成vnode的时候会忽略对应节点,render的时候就不会渲染;
v-show会生成vnode,render的时候也会渲染成真实节点,只是在render过程中会在节点的属性中修改show属性值,也就是常说的display;
v-html会先移除节点下的所有节点,调用html方法,通过addProp添加innerHTML属性,归根结底还是设置innerHTML为v-html的值

五、请求时浏览器缓存 from memory cache 和 from disk cache 的依据是什么,哪些数据什么时候存放在 Memory Cache 和 Disk Cache中?

从缓存位置上来说分为四种,并且各自有优先级,当依次查找缓存且都没有命中的时候,才会去请求网络。

  • Service Worker
  • Memory Cache
  • Disk Cache
  • Push Cache

 

1.Service Worker

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

Service Worker 实现缓存功能一般分为三个步骤:首先需要先注册 Service Worker,然后监听到 install 事件以后就可以缓存需要的文件,那么在下次用户访问的时候就可以通过拦截请求的方式查询是否存在缓存,存在缓存的话就可以直接读取缓存文件,否则就去请求数据。

当 Service Worker 没有命中缓存的时候,我们需要去调用 fetch 函数获取数据。也就是说,如果我们没有在 Service Worker 命中缓存的话,会根据缓存查找优先级去查找数据。但是不管我们是从 Memory Cache 中还是从网络请求中获取的数据,浏览器都会显示我们是从 Service Worker 中获取的内容。

2.Memory Cache

Memory Cache 也就是内存中的缓存,主要包含的是当前中页面中已经抓取到的资源,例如页面上已经下载的样式、脚本、图片等。读取内存中的数据肯定比磁盘快,内存缓存虽然读取高效,可是缓存持续性很短,会随着进程的释放而释放。 一旦我们关闭 Tab 页面,内存中的缓存也就被释放了

那么既然内存缓存这么高效,我们是不是能让数据都存放在内存中呢?
这是不可能的。计算机中的内存一定比硬盘容量小得多,操作系统需要精打细算内存的使用,所以能让我们使用的内存必然不多。

当我们访问过页面以后,再次刷新页面,可以发现很多数据都来自于内存缓存

 

内存缓存中有一块重要的缓存资源是preloader相关指令(例如<link rel="prefetch">)下载的资源。总所周知preloader的相关指令已经是页面优化的常见手段之一,它可以一边解析js/css文件,一边网络请求下一个资源。

需要注意的事情是,内存缓存在缓存资源时并不关心返回资源的HTTP缓存头Cache-Control是什么值,同时资源的匹配也并非仅仅是对URL做匹配,还可能会对Content-Type,CORS等其他特征做校验

3.Disk Cache

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

在所有浏览器缓存中,Disk Cache 覆盖面基本是最大的。它会根据 HTTP Herder 中的字段判断哪些资源需要缓存,哪些资源可以不请求直接使用,哪些资源已经过期需要重新请求。并且即使在跨站点的情况下,相同地址的资源一旦被硬盘缓存下来,就不会再次去请求数据。绝大部分的缓存都来自 Disk Cache,关于 HTTP 的协议头中的缓存字段,我们会在下文进行详细介绍。

浏览器会把哪些文件丢进内存中?哪些丢进硬盘中?
关于这点,网上说法不一,不过以下观点比较靠得住:

  • 对于大文件来说,大概率是不存储在内存中的,反之优先
  • 当前系统内存使用率高的话,文件优先存储进硬盘

4.Push Cache

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

Push Cache 在国内能够查到的资料很少,也是因为 HTTP/2 在国内不够普及。这里推荐阅读Jake ArchibaldHTTP/2 push is tougher than I thought 这篇文章,文章中的几个结论:

  • 所有的资源都能被推送,并且能够被缓存,但是 Edge 和 Safari 浏览器支持相对比较差
  • 可以推送 no-cache 和 no-store 的资源
  • 一旦连接被关闭,Push Cache 就被释放
  • 多个页面可以使用同一个HTTP/2的连接,也就可以使用同一个Push Cache。这主要还是依赖浏览器的实现而定,出于对性能的考虑,有的浏览器会对相同域名但不同的tab标签使用同一个HTTP连接。
  • Push Cache 中的缓存只能被使用一次
  • 浏览器可以拒绝接受已经存在的资源推送
  • 你可以给其他域名推送资源

如果以上四种缓存都没有命中的话,那么只能发起请求来获取资源了。

那么为了性能上的考虑,大部分的接口都应该选择好缓存策略,通常浏览器缓存策略分为两种:强缓存和协商缓存,并且缓存策略都是通过设置 HTTP Header 来实现的




参考链接这里的比较详细

链接:https://www.jianshu.com/p/54cc04190252
 

六、如何在 H5 和小程序项目中计算白屏时间和首屏时间?

白屏时间=页面开始展示的时间点-开始请求时间点。

开始请求时间点可以通过Performance Timing.navigation Start 。那么页面开始展示的时间点怎么获取呢。已经知道渲染过程是逐步完成的,而且页面解析是按照文档流从上至下解析的,因此一般认为开始解析body的时间点就是页面开始展示的时间。所以可以通过在head标签的末尾插入script来统计时间节点作为页面开始展示时间节点。但是这种方式需要打点,因此也有很多项目为了简化白屏时间的获取会选择忽略head解析时间直接用Performance Timing.dom Loading 来表示页面开始展示的时间,即使用domloading-navigation Start来表示白屏时间。

首屏时间=首屏内容渲染结束时间点-开始请求时间点。

同样开始请求时间点可以通过Performance Timing.navigation Start获取。首屏内容渲染结束的时间点通常有以下几种方法获取:

(1)首屏模块标签标记法

适用于于首屏内容不需要通过拉取数据才能生存以及页面不考虑图片等资源加载的情况。通过在 HTML 文档中对应首屏内容的标签结束位置,使用内联的 JavaScript 代码记录当前时间戳作为首屏内容渲染结束的时间点。

(2)统计首屏内加载最慢的图片的时间

通常首屏内容加载最慢的就是图片资源,因此可以把首屏内加载最慢的图片加载完成的时间作为首屏时间。由于浏览器对每个页面的 TCP 连接数有限制,使得并不是所有图片都能立刻开始下载和显示。因此在 DOM树 构建完成后会通过遍历首屏内的所有图片标签,并且监听所有图片标签 onl oad 事件,最终遍历图片标签的加载时间获取最大值,将这个最大值作为首屏时间。

(3)自定义首屏内容计算法

由于统计首屏内图片完成加载的时间比较复杂。所以在项目中通常会通过自定义模块内容,来简化计算首屏时间。例如忽略图片等资源加载情况,只考虑页面主要 DOM;只考虑首屏的主要模块,而不是严格意义首屏线以上的所有内容。

可交互时间=用户可以正常进行事件输入时间点-开始请求时间点。

PerformanceTiming有一个domInteractive属性,代表了DOM结构结束解析的时间点,就是Document.ready State属性变为“interactive”

这篇文章挺详细==> https://lz5z.com/Web%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96-%E9%A6%96%E5%B1%8F%E5%92%8C%E7%99%BD%E5%B1%8F%E6%97%B6%E9%97%B4/

 

 七介绍下观察者模式和订阅-发布模式的区别,各自适用于什么场景

观察者模式(Observer Pattern)

当对象存在一对多的关系时,则使用观察者模式(Observer Pattern),例如当一个对象被修改时候,则会自动通知它的依赖对象。观察者模式属于行为型模式。 与发布/订阅模式不同的是,观察模式没有调度中心,由目标直接调度观察者,观察者模式的观察者跟目标之间是存在依赖的,

介绍

  1. 意图:定义对象的一种一对多的依赖关系,当一个对象的状态发生改变时候,所有依赖它的对象都被通知并且更新状态。
  2. 主要解决:一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。
  3. 何时使用:一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。
  4. 如何解决:使用面向对象技术,可以将依赖关系弱化。
  5. 关键代码:在抽象类里有一个ArrayList存在观察者们
  6. 应用实例:拍卖时候,拍卖师观察最高价,通知其他竞价者竞价。

优缺点

优点:

  1. 观察者和被观察者是抽象耦合的
  2. 建立一套触发机制 缺点:
  3. 一个被观察者对象如果有太多间接或者直接的观察者,将花费时间通知观察者
  4. 如果存在循环依赖,可能导致系统崩溃
  5. 观察者仅仅知道别观察者发生了变化,而不知道如何发生了变化
//观察者模式
function ObserverList() {
    this.observerList = [];
}
ObserverList.prototype.add = function(obj) {
    return this.observerList.push(obj)
};
ObserverList.prototype.count = function() {
    return this.observerList.length;
};
ObserverList.prototype.get = function(index) {
    if (index > -1 && index < this.observerList.length) {
        return this.observerList[index];
    }
};
ObserverList.prototype.indexOf = function(obj, startIndex) {
    let i = startIndex;
    while (i < this.observerList.length) {
        if (this.observerList[i] == obj) {
            return i;
        }
        i++;
    }
    return -1
};
ObserverList.prototype.removeAt = function(index) {
    this.observerList.splice(index, i);
};

function Subject() {
    this.observers = new ObserverList();
}
Subject.prototype.addObserver = function(observer) {
    this.observers.add(observer)
};
Subject.prototype.removeObserver = function(observer) {
    this.observer.removeAt(this.observers.indexOf(observer, 0));
}
Subject.prototype.notify = function(context) {
    let observerCount = this.observers.count();
    for (let index = 0; index < observerCount; index++) {
        this.observers.get(index).update(context)

    }
}

function Observer() {
    this.update = (context) => {
        console.log(context)
    }
}
let o = new Observer();
let subject = new Subject();
subject.addObserver(o)
subject.notify(55);

 

 

发布/订阅模式

发布订阅模式,它定义了一种一对多的关系,可以使多个观察者对象对一个主题对象进行监听,当这个主题对象发生改变时,依赖的所有对象都会被通知到。 发布订阅模式跟观察者模式的区别在于,订阅发布模式,有统一调度中心。

PubSub 模式,是 Publish/Subscribe 的缩写,意为“发布/订阅”模式。

在实际使用中,我们应该也会接触到 PubSub 模式,例如 Nodejs 中的 EventEmitter、Backbone 中的事件模型、以及 jQuery 中的事件。 以 EventEmitter 为例子,它提供了 addListener(event, listener),removeListener(event, listener),emit(event, [arg1], [arg2], [...]) 方法。

 

var pubsub = {};
(function(myObject) {
    // Storage for topics that can be broadcast
    // or listened to
    var topics = {};
    // An topic identifier
    var subUid = -1;
    // Publish or broadcast events of interest
    // with a specific topic name and arguments
    // such as the data to pass along
    myObject.publish = function(topic, args) {
        if (!topics[topic]) {
            return false;
        }
        var subscribers = topics[topic],
            len = subscribers ? subscribers.length : 0;
        while (len--) {
            subscribers[len].func(topic, args);
        }
        return this;
    };
    // Subscribe to events of interest
    // with a specific topic name and a
    // callback function, to be executed
    // when the topic/event is observed
    myObject.subscribe = function(topic, func) {
        if (!topics[topic]) {
            topics[topic] = [];
        }
        var token = (++subUid).toString();
        topics[topic].push({
            token: token,
            func: func
        });
        return token;
    };
    // Unsubscribe from a specific
    // topic, based on a tokenized reference
    // to the subscription
    myObject.unsubscribe = function(token) {
        for (var m in topics) {
            if (topics[m]) {
                for (var i = 0, j = topics[m].length; i < j; i++) {
                    if (topics[m][i].token === token) {
                        topics[m].splice(i, 1);
                        return token;
                    }
                }
            }
        }
        return this;
    };
}(pubsub));

//订阅者1订阅了test
pubsub.subscribe('test', function() {
  console.log(arguments,1)
});
//订阅者2订阅了test
pubsub.subscribe('test', function() {
  console.log(arguments,2)
});
//订阅者3订阅了test
pubsub.subscribe('test', function() {
  console.log(arguments,2)
});

//发布者发布事件
pubsub.publish('test', 666)

 八怎么让一个 div 水平垂直居中

水平垂直居中有好多种实现方式,主要就分为两类不定宽高和定宽高
以在body下插入一个div为例

  1. 定宽高
    使用定位+margin
element.style {
    position: absolute;
    left: 50%;
    top: 50%;
    margin-left: -250px;
    margin-top: -250px;
    width: 500px;
    height: 500px;
    background: yellow;
    z-index: 1;
}

使用定位+transfrom

element.style {
    position: absolute;
    left: 50%;
    top: 50%;
    width: 500px;
    height: 500px;
    background: yellow;
    z-index: 1;
    transform: translate3d(-50%,-50%,0);
}
  1. 不定宽高
    不定宽高的方法基本都适用于定宽高的情况
    这里把div的宽高按照内容展开
    使用定位+transform同样是适用的
element.style {
    position: absolute;
    left: 50%;
    top: 50%;
    background: yellow;
    z-index: 1;
    transform: translate3d(-50%,-50%,0);
}

还有一些其他的方法比如使用父容器使用flex,grid,table
是可以实现的,但是在实际应用中,因为改变了父容器的display,在多个子节点反而不好用了

div.parent {
    display: flex;
    justify-content: center;
    align-items: center;
}

div.parent {
    display: grid;
}
div.child {
    justify-self: center;
    align-self: center;
}


.parent {
        display: table-cell;
        height: 200px;
        width: 200px;
        background-color: orange;
        text-align: center;
        vertical-align: middle;
}
 .child {
        display: inline-block;
        width: 100px;
        height: 100px;
        background-color: blue;
}
 

 

 九分析比较 opacity: 0、visibility: hidden、display: none 优劣和适用场景?

结构:
display:none: 会让元素完全从渲染树中消失,渲染的时候不占据任何空间, 不能点击,
visibility: hidden:不会让元素从渲染树消失,渲染元素继续占据空间,只是内容不可见,不能点击
opacity: 0: 不会让元素从渲染树消失,渲染元素继续占据空间,只是内容不可见,可以点击

继承:
display: none和opacity: 0:是非继承属性,子孙节点消失由于元素从渲染树消失造成,通过修改子孙节点属性无法显示。
visibility: hidden:是继承属性,子孙节点消失由于继承了hidden,通过设置visibility: visible;可以让子孙节点显式。

性能:
displaynone : 修改元素会造成文档回流,读屏器不会读取display: none元素内容,性能消耗较大
visibility:hidden: 修改元素只会造成本元素的重绘,性能消耗较少读屏器读取visibility: hidden元素内容
opacity: 0 : 修改元素会造成重绘,性能消耗较少

联系:它们都能让元素不可见

 十介绍下 BFC、IFC、GFC 和 FFC

What's FC?FC的全称是:Formatting Contexts,是W3C CSS2.1规范中的一个概念。它是页面中的一块渲染区域,并且有一套渲染规则,它决定了其子元素将如何定位,以及和其他元素的关系和相互作用。
BFC
BFC(Block Formatting Contexts)直译为"块级格式化上下文"。Block Formatting Contexts就是页面上的一个隔离的渲染区域,容器里面的子元素不会在布局上影响到外面的元素,反之也是如此。如何产生BFC?
float的值不为none。 
overflow的值不为visible。 
position的值不为relative和static。
display的值为table-cell, table-caption, inline-block中的任何一个。 
那BFC一般有什么用呢?比如常见的多栏布局,结合块级别元素浮动,里面的元素则是在一个相对隔离的环境里运行。
IFC
IFC(Inline Formatting Contexts)直译为"内联格式化上下文",IFC的line box(线框)高度由其包含行内元素中最高的实际高度计算而来(不受到竖直方向的padding/margin影响)
IFC中的line box一般左右都贴紧整个IFC,但是会因为float元素而扰乱。float元素会位于IFC与与line box之间,使得line box宽度缩短。 同个ifc下的多个line box高度会不同。 IFC中时不可能有块级元素的,当插入块级元素时(如p中插入div)会产生两个匿名块与div分隔开,即产生两个IFC,每个IFC对外表现为块级元素,与div垂直排列。
那么IFC一般有什么用呢?
水平居中:当一个块要在环境中水平居中时,设置其为inline-block则会在外层产生IFC,通过text-align则可以使其水平居中。
垂直居中:创建一个IFC,用其中一个元素撑开父元素的高度,然后设置其vertical-align:middle,其他行内元素则可以在此父元素下垂直居中。
GFC
GFC(GridLayout Formatting Contexts)直译为"网格布局格式化上下文",当为一个元素设置display值为grid的时候,此元素将会获得一个独立的渲染区域,我们可以通过在网格容器(grid container)上定义网格定义行(grid definition rows)和网格定义列(grid definition columns)属性各在网格项目(grid item)上定义网格行(grid row)和网格列(grid columns)为每一个网格项目(grid item)定义位置和空间。 
那么GFC有什么用呢,和table又有什么区别呢?首先同样是一个二维的表格,但GridLayout会有更加丰富的属性来控制行列,控制对齐以及更为精细的渲染语义和控制。
FFC
FFC(Flex Formatting Contexts)直译为"自适应格式化上下文",display值为flex或者inline-flex的元素将会生成自适应容器(flex container),可惜这个牛逼的属性只有谷歌和火狐支持,不过在移动端也足够了,至少safari和chrome还是OK的,毕竟这俩在移动端才是王道。
Flex Box 由伸缩容器和伸缩项目组成。通过设置元素的 display 属性为 flex 或 inline-flex 可以得到一个伸缩容器。设置为 flex 的容器被渲染为一个块级元素,而设置为 inline-flex 的容器则渲染为一个行内元素。
伸缩容器中的每一个子元素都是一个伸缩项目。伸缩项目可以是任意数量的。伸缩容器外和伸缩项目内的一切元素都不受影响。简单地说,Flexbox 定义了伸缩容器内伸缩项目该如何布局。


总结

如果你觉得这面试题对你有帮助,我想请你帮我个小忙:来一个点赞收藏三连击;

再次说明整理问题不易,如有内容的需要改进的请指出,以便及时修改!感谢!

学习过程中,祝大家早日取得合适offer!

标签:11,function,缓存,20,Cache,观察者,首屏,常见问题,元素
来源: https://blog.csdn.net/qq_31452297/article/details/118805207

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有