整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

「实战」蘑菇街 PC 端首页,瀑布流布局的实现原理与

「实战」蘑菇街 PC 端首页,瀑布流布局的实现原理与细节技巧

菇街PC首页瀑布流实践

作者:蘑菇街前端团队

链接:https://juejin.im/post/5e05acf0f265da33d158a1b1


零、介绍

这篇文章主要是介绍网站页面瀑布流布局的实现,主要包括:

  1. 瀑布流是什么
  2. 瀑布流的实现原理
  3. 瀑布流的使用场景
  4. 实现中有哪些问题 & 如何解决
  5. 可扩展的使用场景

一、瀑布流是什么

瀑布流, 又称瀑布流式布局,是比较流行的一种网站页面布局。视觉表现为宽度相等高度不定的元素组成的参差不齐的多栏布局,随着页面向下滚动,新的元素附加到最短的一列而不断向下加载。

二、瀑布流的实现原理

瀑布流本质上就是寻找各列之中高度最小的一列,并将新的元素添加到该列后面,只要有新的元素需要排列,就继续寻找所有列中的高度最小列,把后来的元素添加到高度最小列上。

图解基础瀑布流

我们接下来看下为什么要永远寻找最小列?

先看图1的排列顺序,第一排元素的顶部会处于同一个高度,依次排列在顶端,第一排排满之后,第二排从左往右排列。然而这种排列方式很容易出现其中一列过长或其中一列过短的情况。

为了解决 图1 中列可能过长或者过短的问题,我们按照 图2 的方式将元素放在最短的一列进行排列。

三、瀑布流的使用场景

瀑布流滑动的时候会不停的出现新的东西,吸引你不断向下探索,巧妙的利用视觉层级、视线的任意流动来缓解视觉的疲劳,采用这种方案可以延长用户停留视觉,提高用户粘度,适合那些随意浏览,不带目的性的使用场景,就像逛街一样,边走边看,所以比较适合图片、商品、资讯类的场景,很多电商相关的网站都使用了瀑布流进行承载。

上图的蘑菇街 PC 瀑布流效果是在基础瀑布流的基础上做了扩展改造, 在瀑布流顶部某一列或某几列插入其他非瀑布流内容。

本文将介绍这种扩展瀑布流的四列实现场景,适用基础场景如下:

四、瀑布流的的实现有哪些问题&如何解决

  1. 非瀑布流内容如何插入?
  2. 如何寻找所有列的高度最小者?
  3. 如何渲染瀑布流?

Vue 实现瀑布流

我们采用 Vue 框架来实现瀑布流,其一些自带属性使我们的瀑布流实现更加简单。

  • 通过 ref 可以很方便的获取每列高度,通过比较算法算出高度最小列。
  • 拿到高度最小列之后,将下个要插入的元素数据放到最小列的数据列表(columnList)中,通过操作数据完成元素渲染。
  • 利用 Vue 的具名插槽在瀑布流顶部插入其他非瀑布内容。
  • 通过 watch 监测元素渲染,判断是否继续进行渲染和请求更多元素数据。

非瀑布流内容如何插入

通过 Vue 的具名插槽(slot),将非瀑布流元素作为父组件的内容传递给瀑布流子组件。

  • 父组件通过 HTML 模板上的槽属性关联具名插槽,非瀑布流内容作为具名插槽的内容提供给子组件。
  • 具名插槽有:first-col、second-col、 third-col、 last-col、 merge-slot,分别代表第一、二、三、四、合并列。
  • 子组件通过插槽名字判断将非瀑布流内容放在哪一列。如果插槽存在,则将其所携带的内容插入到置顶位置。
  • 因为合并列的特殊性,如果包含合并列,则将合并列绝对定位到顶部,合并列占的瀑布流对应的列进行下移。父组件传合并列相关的参数给子组件:merge(判断是否包含合并列), mergeHeight(合并列的高度),mergeColunms(合并的是哪 2 列)。

代码实现

<!-- 父组件 -->
<div class="parent">
    <Waterfall :merge=true :mergeHeight=800 mergeColumns=[2,3]>
        <template slot="first-col">
            <!-- 第一列内容...      -->
        </template>
        <template slot="second-col">
            <!-- 第二列内容...     -->
        </template>
        <template slot="third-col">
            <!-- 第三列内容...      -->
        </template>
        <template slot="last-col">
            <!-- 第四列内容...      -->
        </template>
        <template slot="merge-col">
            <!-- 合并内容...     -->
        </template>
    </Waterfall>
</div>
<!-- 子组件(waterfall) -->
<div class="child">
    <!-- 第一列 -->
    <div ref="column1" :style="{marginTop: merge && mergeColumns.indexOf(1) > -1 ? mergeHeight + 'px':''}">
        <template v-if="$slots['first-col']">
            <slot name="first-col"></slot>
        </template>
        <template v-for="(item, index) in columnList1">
            <!-- 第一列瀑布流内容... -->
        </template>
    </div>
    <!-- 第二列 -->
    <div ref="column2" :style="{marginTop: merge && mergeColumns.indexOf(2) > -1 ? mergeHeight + 'px':''}">
        <template v-if="$slots['second-col']">
            <slot name="second-col"></slot>
        </template>
        <template v-for="(item, index) in columnList2">
            <!-- 第二列瀑布流内容... -->
        </template>
    </div>
    <!-- 第三列 -->
    <div ref="column3" :style="{marginTop: merge && mergeColumns.indexOf(3) > -1 ? mergeHeight + 'px':''}">
        <template v-if="$slots['third-col']">
            <slot name="third-col"></slot>
        </template>
        <template v-for="(item, index) in columnList3">
            <!-- 第三列瀑布流内容... -->
        </template>
    </div>
    <!-- 第四列 -->
    <div ref="column4" v-if="is4Columns">
        <template v-if="$slots['last-col']">
            <slot name="last-col"></slot>
        </template>
        <template v-for="(item, index) in columnList4">
            <!-- 第四列瀑布流内容... -->
        </template>
    </div>
    <!-- 合并块非瀑布流内容 -->
    <div class="column-merge" v-if="merge" :style="{left: (mergeColumns[0] - 1)*330 + 'px'}">
        <slot name="merge-col"></slot>
    </div>
</div>

如何寻找所有列的高度最小者

每一列都定义一个 ref,通过 ref 获取当前列的高度,如果该列上方有合并块,则高度要加上合并块的高度,然后比较 4 列高度取到最小高度,再通过最小高度算出其对应的列数。

代码实现

// 通过ref获取每列高度,column1,column2,column3,column4分别代表第一、二、三、四列
let columsHeight=[this.$refs.column1.offsetHeight, this.$refs.column2.offsetHeight, this.$refs.column3.offsetHeight, this.$refs.column4.offsetHeight]

// 如果包含合并块, 则更新高度,合并块下的列高要增加合并块的高度
if(this.merge){
    // 如果有合并列,则合并列下的列高度要加合并内容的高度。
    columsHeight[0]=this.mergeColumns.indexOf(1) > -1 ? columsHeight[0] + this.mergeHeight : columsHeight[0];
    columsHeight[1]=this.mergeColumns.indexOf(2) > -1 ? columsHeight[1] + this.mergeHeight : columsHeight[1];
    columsHeight[2]=this.mergeColumns.indexOf(3) > -1 ? columsHeight[2] + this.mergeHeight : columsHeight[2];
		columsHeight[3]=this.mergeColumns.indexOf(4) > -1 ? columsHeight[3] + this.mergeHeight : columsHeight[3];
}

// 获取各列最小高度
let minHeight=Math.min.apply(null, columsHeight);

// 通过最小高度,得到第几列高度最小
this.getMinhIndex(columsHeight, minHeight).then(minIndex=> {
	 // 渲染加载逻辑
});

// 获取高度最小索引函数
getMinhIndex(arr, value){
    return new Promise((reslove)=> {
        let minIndex=0;
        for(let i in arr){
            if(arr[i]==value){
                minIndex=i;
                reslove(minIndex);
            }
        }
    });
}

如何渲染瀑布流

瀑布流常用在无限下拉加载或者加载数据量很大、且包含很多图片元素的情景,所以通常不会一次性拿到所有数据,也不会一次性将拿到的数据全部渲染到页面上,否则容易造成页面卡顿影响用户体验,所以何时进行渲染、何时继续请求数据就很关键。

何时渲染

选择渲染的区域为滚动高度 + 可视区域高度的 1.5 倍,既可以防止用户滚动到底部的时候白屏,也可以防止渲染过多影响用户体验。如果 最小列的高度 - 滚动高度 < 可视区域高 * 1.5 ,则继续渲染元素,否则不再继续渲染。

何时请求数据

当已渲染的元素+可视区域可以展示的预估元素个数 > 已请求到的个数 的时候才去继续请求更多数据,防止请求浪费。 如果 已加载的元素个数 + 一屏可以展示的元素预估个数 > 所有请求拿到的元素个数 ,则触发下一次请求去获取更多数据。

瀑布流渲染核心思路

  • 监测滚动,判断是否符合渲染条件,如果符合条件则开始渲染。
  • 定义一个渲染索引 renderIndex,每渲染一个元素后 renderIndex + 1, 实时监测 renderIndex 的变化, 判断是否符合渲染和数据请求条件。
  • 拿到最小高度列索引后,将下一个元素插入到该列中,并触发 renderIndex + 1 进行下一轮渲染判断。

代码实现

data() {
    return {
        columnList1: [], // 第一列元素列表
        columnList2: [],
        columnList3: [],
        columnList4: [],
        renderIndex: -1, // 渲染第几个item
        isRendering: false, // 是否正在渲染
        itemList: [], // 所有元素列表
        isEnd: false
    };
}

watch: {
    renderIndex(value) {

        // 当前滚动条高度
        const scrollTop=document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop;

        // 最小列高度 - 滚动高度 < 可视区域高的的1.5倍
        if (renderMinTop - scrollTop < winHeight * 1.5) {
            this.renderWaterfall();
        }

        // 已加载的元素个数 + 一屏可以展示元素预估个数 > 所有请求拿到的元素个数
        if (loadedItemNum + canShowItemNum > this.itemList.length && !this._requesting && !this.isEnd) {
            // 请求瀑布流数据
            this.getData();
        }
    }
}

scroll() {

    // 当前滚动条高度
    const scrollTop=document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop;

    // 底部检测高度
    const bottomDetectionTop=this.$refs.bottomDetection.offsetTop;

    const tempLastScrollTop=lastScrollTop; // lastScrollTop:上次一滚动高度
    lastScrollTop=scrollTop;

    if (tempLastScrollTop===-1) {
        this.renderWaterfall();
    }

    // 如果是向下滚动则判断是否需要继续渲染
    if (scrollTop > tempLastScrollTop) {
        if (bottomDetectionTop - tempLastScrollTop < winHeight * 1.5 && !this.isRendering) {
            this.renderWaterfall();
        }
    }

}

renderWaterfall() {

    // 如果还没有数据、所有数据已经渲染完成、正在渲染则不进行渲染计算操作
    if (this.itemList.length===0 || this.renderIndex >=this.itemList.length - 1 || this.isRendering) {
        if (this.renderIndex===this.feedList.length - 1 && !this._requesting && !this.isEnd) {
            this.getData();
        }
        return;
    }
    this.isRendering=true;

    /***
    *** 获取最小高度代码
    ***/

    this.getMinhIndex(columnsHeight, minHeight).then(minIndex=> {

        const key=`columnList${minIndex + 1}`;
        let itemData=this.itemList[this.renderIndex + 1];
        this[key]=this[key].concat(itemData);
        this.$nextTick(()=> {
            this.renderIndex=this.renderIndex + 1;
            this.isRendering=false;
        });
    });
}

五、可扩展的使用场景

为了灵活使用瀑布流,在设计的时候就做好了扩展准备,通过 HTML 模板代码可以看出来,具名插槽的内容可以放在任意列,并没有限制死,所以可以扩展使用到以下各个场景。

关注我

大家好,这里是 FEHub,每天早上 9 点更新,为你分享优质精选文章,与你一起进步。

如果喜欢这篇文章,希望大家点赞,评论,转发。你的支持,是我最大的动力,咱们明天见 :)

关注公众号 「FEHub」,每天进步一点点

周末福利

感谢大家的支持,周末领红包,加鸡腿。

公众号后台回复关键字:「鸡腿」,即可参与红包抽奖。

者 | 木易扬

责编 | 伍杏玲

本人是去年 7-8月开始准备面试,过五关斩六将,最终在年末抱得网易归,深深感受到高级前端面试的套路。以下是自己整理的面试题汇总,不敢藏私,统统贡献出来。

面试的公司分别是:阿里、网易、滴滴、、有赞、挖财、沪江、饿了么、携程、喜马拉雅、兑吧、微医、寺库、宝宝树、海康威视、蘑菇街、酷家乐、百分点和海风教育。

以下是面试题汇总,后续阶段会持续深入更新面试题解,共勉!

阿里

  • 使用过的Koa2中间件
  • Koa-body原理
  • 介绍自己写过的中间件
  • 有没有涉及到Cluster
  • 介绍Pm2
  • Master挂了的话Pm2怎么处理
  • 如何和MySQL进行通信
  • React声明周期及自己的理解
  • 如何配置React-Router
  • 路由的动态加载模块
  • 服务端渲染SSR
  • 介绍路由的History
  • 介绍Redux数据流的流程
  • Redux如何实现多个组件之间的通信,多个组件使用相同状态如何进行管理
  • 多个组件之间如何拆分各自的State,每块小的组件有自己的状态,它们之间还有一些公共的状态需要维护,如何思考这块
  • 使用过的Redux中间件
  • 如何解决跨域的问题
  • 常见Http请求头
  • 移动端适配1px的问题
  • 介绍Flex布局
  • 其他CSS方式设置垂直居中
  • 居中为什么要使用Transform(为什么不使用MarginLeft/Top)
  • 使用过Webpack里面哪些Plugin和Loader
  • Webpack里面的插件是怎么实现的
  • Dev-Server是怎么跑起来
  • 项目优化
  • 抽取公共文件是怎么配置的
  • 项目中如何处理安全问题
  • 怎么实现this对象的深拷贝

网易

  • 介绍Redux,主要解决什么问题
  • 文件上传如何做断点续传
  • 表单可以跨域吗
  • Promise、Async有什么区别
  • 搜索请求如何处理(防抖)
  • 搜索请求中文如何请求
  • 介绍观察者模式
  • 介绍中介者模式
  • 观察者和订阅-发布的区别,各自用在哪里
  • 介绍React优化
  • 介绍Http2.0
  • 通过什么做到并发请求
  • Hhttp1.1时如何复用Tcp连接
  • 介绍Service Worker
  • 介绍CSS3中Position:sticky
  • Redux请求中间件如何处理并发
  • 介绍Promise,异常捕获
  • 介绍position属性包括CSS3新增
  • 浏览器事件流向
  • 介绍事件代理以及优缺点
  • React组件中怎么做事件代理
  • React组件事件代理的原理
  • 介绍This各种情况
  • 前端怎么控制管理路由
  • 使用路由时出现问题如何解决
  • React怎么做数据的检查和变化

滴滴

  • React-Router怎么实现路由切换
  • React-Router里的<Link>标签和<a>标签有什么区别
  • <a>标签默认事件禁掉之后做了什么才实现了跳转
  • React层面的性能优化
  • 整个前端性能提升大致分几类
  • import { Button } from 'antd',打包的时候只打包button,分模块加载,是怎么做到的
  • 使用import时,Webpack对node_modules里的依赖会做什么
  • JS异步解决方案的发展历程以及优缺点
  • Http报文的请求会有几个部分
  • Cookie放哪里,Cookie能做的事情和存在的价值
  • Cookie和Token都存放在Header里面,为什么只劫持前者
  • Cookie和Session有哪些方面的区别
  • React中Dom结构发生变化后内部经历了哪些变化
  • React挂载的时候有3个组件,TextComponent、ComposeComponent、DomComponent,区别和关系,Dom结构发生变化时怎么区分Data的变化,怎么更新,更新怎么调度,如果更新的时候还有其他任务存在怎么处理
  • Key主要是解决哪一类的问题,为什么不建议用索引index(重绘)
  • Redux中异步的请求怎么处理
  • Redux中间件是什么东西,接受几个参数(两端的柯里化函数)
  • 柯里化函数两端的参数具体是什么东西
  • 中间件是怎么拿到Store和Action,然后怎么处理
  • State是怎么注入到组件的,从Reducer到组件经历了什么样的过程
  • Koa中response.send、Response.rounded、Response.json发生了什么事,浏览器为什么能识别到它是一个json结构或是html
  • Koa-bodyparser怎么来解析Request
  • Webpack整个生命周期,Loader和Plugin有什么区别
  • 介绍AST(Abstract Syntax Tree)抽象语法树
  • 安卓Activity之间数据是怎么传递的
  • 安卓4.0到6.0过程中WebView对JS兼容性的变化
  • WebView和原生是如何通信
  • 跨域怎么解决,有没有使用过Apache等方案

  • 对Async、Await的理解,内部原理
  • 介绍下Promise,内部实现
  • 清除浮动
  • 定位问题(绝对定位、相对定位等)
  • 从输入URL到页面加载全过程
  • TCP3次握手
  • TCP属于哪一层(1 物理层 -> 2 数据链路层 -> 3 网络层(IP)-> 4 传输层(TCP) -> 5 应用层(Http))
  • Redux的设计思想
  • 接入Redux的过程
  • 绑定Cconnect的过程
  • Cconnect原理
  • Webpack介绍
  • ==和===的区别,什么情况下用相等==
  • Bind、Call、Apply的区别
  • 动画的了解
  • 介绍下原型链(解决的是继承问题吗)
  • 对跨域的了解

有赞

  • Linux 754 介绍
  • 介绍冒泡排序,选择排序,冒泡排序如何优化
  • Transform动画和直接使用Left、Top改变位置有什么优缺点
  • 如何判断链表是否有环
  • 介绍二叉搜索树的特点
  • 介绍暂时性死区
  • ES6中的Map和原生的对象有什么区别
  • 观察者和发布-订阅的区别
  • React异步渲染的概念,介绍Time Slicing 和 Suspense
  • 16.X声明周期的改变
  • 16.X中Props改变后在哪个生命周期中处理
  • 介绍纯函数
  • 前端性能优化
  • PureComponent和FunctionComponent区别
  • 介绍JSX
  • 如何做RN在安卓和iOS端的适配
  • RN为什么能在原生中绘制成原生组件(bundle.js)
  • 介绍虚拟DOM
  • 如何设计一个localStorage,保证数据的实效性
  • 如何设计Promise.all()
  • 介绍高阶组件
  • sum(2, 3)实现sum(2)(3)的效果
  • react性能优化
  • 两个对象如何比较

挖财

  • JS的原型
  • 变量作用域链
  • call、apply、bind的区别
  • 防抖和节流的区别
  • 介绍各种异步方案
  • React生命周期
  • 介绍Fiber
  • 前端性能优化
  • 介绍DOM树对比
  • React中的key的作用
  • 如何设计状态树
  • 介绍CSS,Xsrf
  • Http缓存控制
  • 项目中如何应用数据结构
  • Native提供了什么能力给RN
  • 如何做工程上的优化
  • shouldComponentUpdate是为了解决什么问题
  • 如何解决Props层级过深的问题
  • 前端怎么做单元测试
  • Webpack生命周期
  • Webpack打包的整个过程
  • 常用的Plugins
  • Pm2怎么做进程管理,进程挂掉怎么处理
  • 不用Pm2怎么做进程管理

沪江

  • 介绍下浏览器跨域
  • 怎么去解决跨域问题
  • Jsonp方案需要服务端怎么配合
  • Ajax发生跨域要设置什么(前端)
  • 加上CORS之后从发起到请求正式成功的过程
  • Xsrf跨域攻击的安全性问题怎么防范
  • 使用Async会注意哪些东西
  • Async里面有多个await请求,可以怎么优化(请求是否有依赖)
  • Promise和Async处理失败的时候有什么区别
  • Redux在状态管理方面解决了React本身不能解决的问题
  • Redux有没有做过封装
  • React生命周期,常用的生命周期
  • 对应的生命周期做什么事
  • 遇到性能问题一般在哪个生命周期里解决
  • 怎么做性能优化(异步加载组件)
  • 写React有哪些细节可以优化
  • React的事件机制(绑定一个事件到一个组件上)
  • 介绍下事件代理,主要解决什么问题
  • 前端开发中用到哪些设计模式
  • React/Redux中哪些功能用到了哪些设计模式
  • JS变量类型分为几种,区别是什么
  • JS里垃圾回收机制是什么,常用的是哪种,怎么处理的
  • 一般怎么组织CSS(Webpack)

饿了么

  • 小程序里面开页面最多是多少
  • React子父组件之间如何传值
  • Emit事件怎么发,需要引入什么
  • 介绍下React高阶组件,和普通组件有什么区别
  • 一个对象数组,每个子对象包含一个ID和Name,React如何渲染出全部的Name
  • 在哪个生命周期里写
  • 其中有几个Name不存在,通过异步接口获取,如何做
  • 渲染的时候Key给什么值,可以使用Index吗?用ID好还是Index好
  • Webpack如何配Sass,需要配哪些Loader
  • 配CSS需要哪些Loader
  • 如何配置把JS、CSS、Html单独打包成一个文件
  • Div垂直水平居中(Flex、绝对定位)
  • 两个元素块,一左一右,中间相距10像素
  • 上下固定,中间滚动布局如何实现
  • [1, 2, 3, 4, 5]变成[1, 2, 3, a, b, 5]
  • 取数组的最大值(ES5、ES6)
  • apply和call的区别
  • ES5和ES6有什么区别
  • some、every、find、filter、map、forEach有什么区别
  • 上述数组随机取数,每次返回的值都不一样
  • 如何找0-5的随机数,95-99呢
  • 页面上有1万个Button如何绑定事件
  • 如何判断是Button
  • 页面上生成一万个Button,并且绑定事件,如何做(JS原生操作DOM)
  • 循环绑定时的Index是多少,为什么,怎么解决
  • 页面上有一个input,还有一个p标签,改变input后p标签就跟着变化,如何处理
  • 监听input的哪个事件,在什么时候触发

携程

  • 对React看法,有没有遇到一些坑
  • 对闭包的看法,为什么要用闭包
  • 手写数组去重函数
  • 手写数组扁平化函数
  • 介绍下Promise的用途和性质
  • Promise和Callback有什么区别
  • React生命周期
  • 两道手写算法题

喜马拉雅

  • ES6新的特性
  • 介绍Promise
  • Promise有几个状态
  • 说一下闭包
  • React的生命周期
  • ComponentWillReceiveProps的触发条件是什么
  • React16.3对生命周期的改变
  • 介绍下React的Filber架构
  • 画Filber渲染树
  • 介绍React高阶组件
  • 父子组件之间如何通信
  • Redux怎么实现属性传递,介绍下原理
  • React-Router版本号
  • 网站SEO怎么处理
  • 介绍下HTTP状态码
  • 403、301、302是什么
  • 缓存相关的HTTP请求头
  • 介绍HTTPS
  • HTTPS怎么建立安全通道
  • 前端性能优化(JS原生和React)
  • 用户体验做过什么优化
  • 对PWA有什么了解
  • 对安全有什么了解
  • 介绍下数字签名的原理
  • 前后端通信使用什么方案
  • RESTful常用的Method
  • 介绍下跨域
  • Access-Control-Allow-Origin在服务端哪里配置
  • csrf跨站攻击怎么解决
  • 前端和后端怎么联调

兑吧

  • LocalStorage和Cookie有什么区别
  • CSS选择器有哪些
  • 盒子模型,以及标准情况和IE下的区别
  • 如何实现高度自适应
  • Prototype和Proto区别
  • _construct是什么
  • new是怎么实现的
  • promise的精髓,以及优缺点
  • 如何实现H5手机端的适配
  • Rrem、Flex的区别(Root em)
  • em和px的区别
  • React声明周期
  • 如何去除url中的#号
  • Redux状态管理器和变量挂载到Window中有什么区别
  • Webpack和Gulp的优缺点
  • 如何实现异步加载
  • 如何实现分模块打包(多入口)
  • 前端性能优化(1JS、CSS;2 图片;3 缓存预加载; 4 SSR; 5 多域名加载;6 负载均衡)
  • 并发请求资源数上限(6个)
  • base64为什么能提升性能,缺点
  • 介绍Webp这个图片文件格式
  • 介绍Koa2
  • Promise如何实现的
  • 异步请求,低版本Fetch如何低版本适配
  • Ajax如何处理跨域
  • CORS如何设置
  • Jsonp为什么不支持Post方法
  • 介绍同源策略
  • React使用过的一些组件
  • 介绍Immuable
  • 介绍下Redux整个流程原理
  • 介绍原型链
  • 如何继承

微医

  • 介绍JS数据类型,基本数据类型和引用数据类型的区别
  • Array是Object类型吗
  • 数据类型分别存在哪里
  • var a={name: "前端开发"}; var b=a; a=null那么b输出什么
  • var a={b: 1}存放在哪里
  • var a={b: {c: 1}}存放在哪里
  • 栈和堆的区别
  • 垃圾回收时栈和堆的区别
  • 数组里面有10万个数据,取第一个元素和第10万个元素的时间相差多少
  • 栈和堆具体怎么存储
  • 介绍闭包以及闭包为什么没清除
  • 闭包的使用场景
  • JS怎么实现异步
  • 异步整个执行周期
  • Promise的三种状态
  • Async/Await怎么实现
  • Promise和setTimeout执行先后的区别
  • JS为什么要区分微任务和宏任务
  • Promise构造函数是同步还是异步执行,then呢
  • 发布-订阅和观察者模式的区别
  • JS执行过程中分为哪些阶段
  • 词法作用域和this的区别
  • 平常是怎么做继承
  • 深拷贝和浅拷贝
  • loadsh深拷贝实现原理
  • ES6中let块作用域是怎么实现的
  • React中setState后发生了什么
  • setState为什么默认是异步
  • setState什么时候是同步的
  • 为什么3大框架出现以后就出现很多Native(RN)框架(虚拟DOM)
  • 虚拟DOM主要做了什么
  • 虚拟DOM本身是什么(JS对象)
  • 304是什么
  • 打包时Hash码是怎么生成的
  • 随机值存在一样的情况,如何避免
  • 使用Webpack构建时有无做一些自定义操作
  • Webpack做了什么
  • a,b两个按钮,点击aba,返回顺序可能是baa,如何保证是aba(Promise.then)
  • Node接口转发有无做什么优化
  • Node起服务如何保证稳定性,平缓降级,重启等
  • RN有没有做热加载
  • RN遇到的兼容性问题
  • RN如何实现一个原生的组件
  • RN混原生和原生混RN有什么不同
  • 什么是单页项目
  • 遇到的复杂业务场景
  • Promise.all实现原理

寺库

  • 介绍Promise的特性,优缺点
  • 介绍Redux
  • RN的原理,为什么可以同时在安卓和IOS端运行
  • RN如何调用原生的一些功能
  • 介绍RN的缺点
  • 介绍排序算法和快排原理
  • 堆和栈的区别
  • 介绍闭包
  • 闭包的核心是什么
  • 网络的五层模型
  • HTTP和HTTPS的区别
  • HTTPS的加密过程
  • 介绍SSL和TLS
  • 介绍DNS解析
  • JS的继承方法
  • 介绍垃圾回收
  • Cookie的引用为了解决什么问题
  • Cookie和localStorage的区别
  • 如何解决跨域问题
  • 前端性能优化

宝宝树

  • 使用Canvas绘图时如何组织成通用组件
  • formData和原生的Ajax有什么区别
  • 介绍下表单提交,和FormData有什么关系
  • 介绍Redux接入流程
  • Rudux和全局管理有什么区别(数据可控、数据响应)
  • RN和原生通信
  • 介绍MVP怎么组织
  • 介绍异步方案
  • Promise如何实现Then处理
  • Koa2中间件原理
  • 常用的中间件
  • 服务端怎么做统一的状态处理
  • 如何对相对路径引用进行优化
  • Node文件查找优先级
  • Npm2和Npm3+有什么区别

海康威视

  • Knex连接数据库响应回调
  • 介绍异步方案
  • 如何处理异常捕获
  • 项目如何管理模块
  • 前端性能优化
  • JS继承方案
  • 如何判断一个变量是不是数组
  • 变量a和b,如何交换
  • 事件委托
  • 多个<li>标签生成的Dom结构是一个类数组
  • 类数组和数组的区别
  • dom的类数组如何转成数组
  • 介绍单页面应用和多页面应用
  • Redux状态树的管理
  • 介绍Localstorage的API

蘑菇街

  • Html语义化的理解
  • <b>和<strong>的区别
  • 对闭包的理解
  • 工程中闭包使用场景
  • 介绍this和原型
  • 使用原型最大的好处
  • React设计思路
  • 为什么虚拟DOM比真实DOM性能好
  • React常见的通信方式
  • Redux整体的工作流程
  • Redux和全局对象之间的区别
  • Redux数据回溯设计思路
  • 单例、工厂、观察者项目中实际场景
  • 项目中树的使用场景以及了解
  • 工作收获

酷家乐

  • React生命周期
  • React性能优化
  • 添加原生事件不移除为什么会内存泄露
  • 还有哪些地方会内存泄露
  • setInterval需要注意的点
  • 定时器为什么是不精确的
  • setTimeout(1)和setTimeout(2)之间的区别
  • 介绍宏任务和微任务
  • Promise里面和then里面执行有什么区别
  • 介绍pureComponet
  • 介绍Function Component
  • React数据流
  • props和state的区别
  • 介绍React context
  • 介绍class和ES5的类以及区别
  • 介绍箭头函数和普通函数的区别
  • 介绍defineProperty方法,什么时候需要用到
  • for..in 和 object.keys的区别
  • 介绍闭包,使用场景
  • 使用闭包特权函数的使用场景
  • Get和Post有什么区别

百分点

  • React15/16.x的区别
  • 重新渲染Render会做些什么
  • 哪些方法会触发React重新渲染
  • state和props触发更新的生命周期分别有什么区别
  • setState是同步还是异步
  • 对无状态组件的理解
  • 介绍Redux工作流程
  • 介绍ES6的功能
  • let、const以及var的区别
  • 浅拷贝和深拷贝的区别
  • 介绍箭头函数的this
  • 介绍Promise和then
  • 介绍快速排序
  • 算法:前K个最大的元素

海风教育

  • 对React看法,它的优缺点
  • 使用过程中遇到的问题,如何解决的
  • React的理念是什么(拿函数式编程来做页面渲染)
  • JS是什么范式语言(面向对象还是函数式编程)
  • Koa原理,为什么要用Koa(Express和Koa对比)
  • 使用的Koa中间件
  • ES6使用的语法
  • Promise 和 async/await 和 Callback的区别
  • Promise有没有解决异步的问题(Promise链是真正强大的地方)
  • Promise和setTimeout的区别(Event Loop)
  • 进程和线程的区别(一个Node实例就是一个进程,Node是单线程,通过事件循环来实现异步)
  • 介绍下DFS深度优先
  • 介绍下观察者模式
  • 观察者模式里面使用的数据结构(不具备顺序 ,是一个List)

作者简介:木易杨,网易高级前端工程师,跟着我每周重点攻克一个前端面试重难点。让我带你走进高级前端的世界,在进阶的路上,共勉!

声明:本文系作者投稿,版权归作者所有。

先,我们来看一下什么是瀑布流布局效果,比如电商网站 蘑菇街

原理图:

在一个大盒子里,放置多个小盒子,小盒子的大小可以不一致,长短不一样,呈现一种瀑布流的效果。

使用CSS3S实现只需要如下4步:

1. 准备图片素材

2. 书写相应HTML结构

3. 了解CSS 多栏(Multi-column) 属性

4. 使用CSS 多栏属性完成瀑布流布局