欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

撩课-Web大前端每天5道面试题-Day4

程序员文章站 2022-03-30 10:01:30
1. 如何实现瀑布流? 高清视频讲解(第53节)-->点我 2. 原生JS都有哪些方式可以实现两个页面间的通信? 3. 原生JS动态向一个div中插入1000个div标签,如何实现? 4. 程序出现bug了,你是如何调试的? 5. 开发中是如何进行性能优化的? 6. 如何实现电商网站中的楼层效果? ......

1. 如何实现瀑布流?

瀑布流布局的原理:

1) 瀑布流布局要求要进行布置的元素等宽,
    然后计算元素的宽度, 
    与浏览器宽度之比,得到需要布置的列数;

2) 创建一个数组,长度为列数,
    里面的值为已布置元素的总高度(最开始为0);

3) 然后将未布置的元素依次布置到高度最小的那一列,
   就得到了瀑布流布局;

4) 滚动加载, scroll事件得到scrolltop,  
    与最后盒子的offsettop对比,
    符合条件就不断滚动加载。

瀑布流布局核心代码:

/**
 * 实现瀑布流的布局
 * @param {string}parentbox
 * @param {string}childbox
 */
function waterfull(parentbox, childbox) {
    // 1. 求出父盒子的宽度
    //  1.1 获取所有的子盒子
    var allbox = $(parentbox).
         getelementsbyclassname(childbox);
    // console.log(allbox);

    // 1.2 求出子盒子的宽度
    var boxwidth = allbox[0].offsetwidth;
    // console.log(boxwidth);

    // 1.3 获取窗口的宽度
    var clientw = document.
        documentelement.clientwidth;
    // console.log(clientw);

    // 1.4 求出总列数
    var cols = math.floor(clientw / boxwidth);
    // console.log(cols);

    // 1.5 父盒子居中
    $(parentbox).style.width = cols * boxwidth + 'px';
    $(parentbox).style.margin = '0 auto';

    // 2. 子盒子定位
    //  2.1 定义变量
    var heightarr = [], boxheight = 0, 
        minboxheight = 0, minboxindex = 0;

    // 2.2 遍历所有的子盒子
    for (var i = 0; i < allbox.length; i++) {
        // 2.2.1 求出每一个子盒子的高度
        boxheight = allbox[i].offsetheight;
        // console.log(boxheight);
        // 2.2.2 取出第一行盒子的高度放入高度数组中
        if (i < cols) { // 第一行
            heightarr.push(boxheight);
        } else { // 剩余行的盒子
            // 2.2.3 取出数组中最矮的高度
            minboxheight = _.min(heightarr);
            // 2.2.4 求出最矮高度对应的索引
            minboxindex = getminboxindex(heightarr, minboxheight);
            // 2.2.5 盒子定位
            allbox[i].style.position = 'absolute';
            allbox[i].style.left = minboxindex * boxwidth + 'px';
            allbox[i].style.top = minboxheight + 'px';
            // 2.2.6 更新最矮的高度
            heightarr[minboxindex] += boxheight;

        }
    }
}

/**
 * 根据内容取出在数组中对应的索引
 * @param {object}arr
 * @param {number}val
 * @returns {boolean}
 */
 
function getminboxindex(arr, val) {
    for (var i = 0; i < arr.length; i++) {
        if (arr[i] === val) return i;
    }
}

/**
 * 判断是否具备加载子盒子的条件
 * @returns {boolean}
 */
function checkwillloadimage() {
    // 1. 获取最后一个盒子
    var allbox = $('main').getelementsbyclassname('box');
    var lastbox = allbox[allbox.length - 1];

    // 2. 求出高度
    var lastboxdis = lastbox.offsetheight * 0.5 + lastbox.offsettop;

    // 3. 求出窗口的高度
    var clienth = document.documentelement.clientheight;

    // 4. 求出页面滚动产生的高度
    var scrolltoph = scroll().top;

    // 5. 对比
    return lastboxdis <= clienth + scrolltoph;
}

 

2. 原生js都有哪些方式可以实现两个页面间的通信?

1)  通过url地址栏传递参数;
    例如:点击列表页中的每一条数据,
    我们跳转到同一个详细页面,
    但是根据点击的不一样可以看到
    不同的内容,这样的话我们就可以
    在url中传递不同的值来区分了;

2)  通过本地存储 cookie、localestorage、
    sessionstroage...,例如:京东的登录,
    我们在登录页登录完成后,
    把用户信息存储到本地,
    然后在其它页面中如果需要使用的话,
    我们直接从本地的存储数据中拿
    出来用即可;

3)  使用iframe在a页面中嵌入b页面,
    这样的话,在a中可以通过一些属性
    和方法实现和b页面的通信;

4)  利用postmessage实现页面间通信, 
    父窗口往子窗口传递信息, 
    子窗口往父窗口传递信息。

 

3. 原生js动态向一个div中插入1000个div标签,如何实现?

此题主要考性能!

1)  可以用js中的createelement创建div,
    每当创建一个就把它添加到div中, 
    但会造成引发回流的次数太多;

2)  使用字符串拼接的方式,
    把1000个div都拼接完成后,
    统一的添加到页面中, 
    但会对div原有的元素标签产生影响:
    原来标签绑定的事件都消失了

3)  综合1和2可以使用文档碎片方式来处理。

追问:如果是创建1000万个呢?

可采用的方案: 数据分批异步加载
1)  首先把前两屏幕的数据量
    (例如:300条)先获取到,
    然后使用字符串拼接或者文档碎片
    的方式绑定到页面中; 

2)  当浏览器滚动到指定的区域的
    时候在加载300条...以此类推。

 

4. 程序出现bug了,你是如何调试的?

1)  在控制台加断点,
    f10->逐过程  f11->逐语句;
2) 在代码重点的位置加入
    console.log输出对应的值来进行调试;
3) debugger调试;
4) 代码分割还原调试;
5) 异常捕获机制, 记录运行日志;
6) 单元测试。

 

5. 开发中是如何进行性能优化的?

现在框架(vue, react,...)、构建工具(webpack, ...)
已经给我们解决掉大部分的性能优化问题, 
面试时, 可以就你了解的框架来深入剖析, 
但此题应该是考原生js的范畴,
参考答案如下:

1) 雅虎35条性能优化黄金定律;
2) js代码优化:
   a. 项目中的js/css文件最好一个页面只用一个, 
      需要把js/css进行合并压缩, 
      并且减少页面中的垃圾冗余代码。
      项目的资源文件在服务器上最好
      做一下gzip压缩。
   
  b. 解除文件缓存; 
     我们修改代码并上传, 
     如果之前页面访问过该网站,
     很有可能不能立即见效; 
     我们在引入css/js文件的时候, 
     在文件名的后面加上版本号(加时间戳), 
     比如:
     <script src='itlike.com.js?_=202001...'></script>; 
     当我们上传新的文件后
     把时间戳改一下就可以清除缓存了。

  c. 移动端尽量少用图片:
      icon能用svg画的不用图片;
     静态资源图:做布局的时候就能确定下来的图片,
      比如:
     1)  css sprite图片合并
         (针对于小图片)
     2)  做图片延迟加载
         (针对于大图片 头部的长条图片、背景大图...),
         开始给一张默认的小的图片
         (最好维持在10kb以内)
     3) base64
        (存在问题: 页面的代码太臃肿了,以后维护不好操作);  
        如果项目中由于图片太大实在解决不了, 
        改成base64就解决了
   
  d. 动态数据图:
     通过ajax从后台读取回来的图片 , 图片懒加载;

  e. 音视频文件的优化: 
     加载页面的时候,尽量不要加载音视频文件,
     当页面中的其他资源加载完成后,
     再开始加载音视频文件; 
     目前移动端经常给音视频做的优化是:
     走直播流文件(音频后缀名是m3u8格式);

  f. 减少页面资源请求的次数:
     如果当前只是一个宣传页,
     或者是一个简单的页面, 
     使用的css和js可以采用内嵌式开发;

  g. ajax数据请求分批请求, 
     例如:一次要请求10000条数据的话,
     我们每一次只请求100条,第一屏幕肯定能看全了,
     当页面滚动到对应的其它屏幕的时候,
     在加载下一个100条...

  h. 做数据的二次缓存,  
     能用css3做动画的绝对不用js,
     能使用transform尽量使用,
     能用animation的进行不用transition...
     尽量减少同步操作,多用异步操作;
     能使用原生js自己编写的,
     绝对不用插件或者框架;  

 

 
撩课-Web大前端每天5道面试题-Day4
 

6. 如何实现电商网站中的楼层效果?

1) 封装缓动动画函数;
2) 点击切换, 滚动切换, 联动处理;

核心代码如下:

// 3. 监听gps上的li的点击
for (var j = 0; j < ollis.length; j++) {
    (function (index) {
        var olli = ollis[index];
        olli.onmousedown = function () {

            isclick = true;

            // 3.1 排他
            for (var m = 0; m < ollis.length; m++) {
                ollis[m].classname = ''
            }
            addclass(this, 'current');

            // 3.2 让楼层滚动起来
            buffer(
                document.documentelement, 
                {'scrolltop': index * client().height},
                function () {
                isclick = false;
            })
        }
    })(j)
}

// 4. 监听文档的滚动
window.onscroll = function (ev1) {
    // 4.1 没有点击产生的滚动
    if (!isclick) {
        // 4.2 获取页面产出的头部滚动的高度
        var roll = math.ceil(scroll().top);
        console.log(roll);
        // 4.3 遍历
        for (var i = 0; i < ollis.length; i++) {
            // 4.4 判断
            if (roll >= ullis[i].offsettop) {
                for (var m = 0; m < ollis.length; m++) {
                    ollis[m].classname = ''
                }
                addclass(ollis[i], 'current');
            }
        }
    }

}

缓动动画函数:

/**
 * 缓动动画(撩课学院)
 * @param {object}obj
 * @param {object}json
 * @param {function}fn
 */
function buffer(obj, json, fn) {
    // 1.1 清除定时器
    clearinterval(obj.timer);
    // 1.2 设置定时器
    var begin = 0, target = 0, speed = 0;
    obj.timer = setinterval(function () {
        // 1.3.0 旗帜
        var flag = true;
        for (var k in json) {
            // 1.3 获取初始值
            if ("opacity" === k) { // 透明度
                begin = parseint(parsefloat(getcssattrvalue(obj, k)) * 100);
                target = parseint(parsefloat(json[k]) * 100);
            } else if ("scrolltop" === k) {
                begin = math.ceil(obj.scrolltop);
                target = parseint(json[k]);
            } else { // 其他情况
                begin = parseint(getcssattrvalue(obj, k)) || 0;
                target = parseint(json[k]);
            }
            // 1.4 求出步长
            speed = (target - begin) * 0.2;
            // 1.5 判断是否向上取整
            speed = (target > begin)
                         ? math.ceil(speed)
                         : math.floor(speed);
            // 1.6 动起来
            if ("opacity" === k) { // 透明度
                // w3c的浏览器
                obj.style.opacity = (begin + speed) / 100;
                // ie 浏览器
                obj.style.filter = 'alpha(opacity:' + (begin + speed) + ')';
            } else if ("scrolltop" === k) {
                obj.scrolltop = begin + speed;
            } else if ("zindex" === k) {
                obj.style[k] = json[k];
            } else {
                obj.style[k] = begin + speed + "px";
            }

            // 1.5 判断
            if (begin !== target) {
                flag = false;
            }
        }

        // 1.3 清除定时器
        if (flag) {
            clearinterval(obj.timer);
            // 判断有没有回调函数
            if (fn) {
                fn();
            }
        }
    }, 20);
}