[面试题总结]css、网络编程、异步、webpack

1,760 阅读12分钟
  1. css

    1. 居中为什么用transform,不用marin+left/top

      考虑浏览器性能,transform创建一个渲染层,marin+left/top需要计算布局

    2. 移动端适配1px的问题

      1. 原因:不同手机dpr(像素密度不同),如视网膜屏1px看起来会变胖
      2. 解决方案:伪元素+transform,相对定位、设置transform:scale(0.5)
      3. 可满足圆角,但是伪元素不够用时可能要新增元素
      4. 现在已经不常见
    3. 介绍flex布局

      1. 弹性布局
      2. justify-content:主轴对齐方式,如是否两端对齐
      3. align-item:交叉轴对齐方式,类似line-height垂直居中那种效果
    4. 如何实现H5手机端配置

      1. flexible.js,配合设置meta viewport width=device-width
      2. 原理:将屏幕宽度分成10份,一份是1rem,给body的font-size设置这个值
    5. css实现三角形

      1. 元素width、height都设置成0
      2. 设置border宽度、颜色(透明)形成三角形(border想象成木板投影)
    6. BFC - 块格式上下文

      1. BFC是一个独立的布局容器,不影响外界、不受外界影响
      2. 创建BFC:
        1. float
        2. position: absolute | fixed
        3. display: inline-block | flex | table-cell
        4. overflow: hidden | auto | scroll | !visible
    7. CSS3新特性:position: sticky

      父元素在屏幕内时表现为position: static,父元素滚出屏幕,表现为position: fixed

  2. node

    1. 使用过的koa2中间件

      1. koa-bodyparser - 可直接在ctx.request.body上获取post请求参数
      2. koa-router 路由
        1. 不使用koa-router中间件:根据url返回不同文件渲染
        2. 使用koa-router中间件:new KoaRouter.get().get().post()...,可以方便设置路由层级
      3. koa-session
      4. koa-statics
    2. koa-bodyparser原理

      1. node获取request body通过监听request的data事件完成
      2. koa-bodyparser封装这一过程,把post请求数据解析后挂载到koa上下文中
    3. 定时任务执行顺序

      1. 执行顺序:process.nextTick > setImmidate > setTimeout > setInterval
      2. 微任务:process.nextTick、promise.then;宏任务:setImmidate、setTimeout、setInterval
      3. setTimeout最小时间间隔:4ms
    4. koa、express、redux中间件的差异

      1. express是依次执行中间件函数,next之后的方法不执行,在最后一个next中处理response
      2. koa是使用async、await实现的一种洋葱圈模型,通过next把执行权交给下一个中间件,直到最后一个中间件不调用next,控制权沿途返回。next之后的代码可以执行
      3. redux中间件是一系列函数的柯里化,依次传递action,直到redux的原生dispatch。
    5. 介绍EventLoop

      1. 浏览器:
        1. 主线程 - js执行栈
        2. 异步操作、网络请求、点击事件回调等cb放入任务队列
        3. 当前宏任务执行完成后,会一次性清空任务队列
        4. 宏任务包括:setTimeout/setInterval、执行js、UI rendering等
        5. 微任务:promise.then等
      2. node:
        1. 收到一个请求时,使用一个js闭包(request、response的cb)排队进入EventLoop
        2. 如果是耗时任务,Libuv库给其分配一个工作线程
        3. 工作完成后触发相应的cb返回给主线程
        4. EventLoop将响应返回客户端
        5. 宏任务包括:setTimeout/setInterval/setImmediate、执行js、I/O等
        6. 微任务:promise.then、process.nextTick等
    6. EventEmmiter

      1. event = new require('event').Emmiter(); event.emit('a'); event.on('a', listener1); event.removeListener('a', listener1)
      2. 这是一种订阅-发布模式,vue bus也是这样的
      3. 订阅-发布与观察者的区别:
        1. 订阅-发布有中间商-调度中心,发布、订阅者不知道对方的存在
        2. 观察者模式中观察者必须对目标触发事件作出响应
        3. 发布-订阅是观察者模式的进化版,耦合度更低
  3. HTTP

    1. http报文:

      1. 分类
        1. 请求报文
        2. 响应报文
      2. 内容
        1. 请求头部
        2. 空行
        3. 请求主体
      3. HTTP请求常见方式:get post option(预处理)
    2. 如何处理跨域

      1. 跨域拦截是浏览器做的,服务端本身没有这样的限制。
      2. jsonp,给server的callback标记client方法名fn,server返回执行fn的字符串,并进行赋值,client把这段字符串放入script标签中执行,jsonp只能用get
      3. ajax跨域
        1. 先预检,可以用OPTION,或者content-type设置成其他,预检的作用是防止post请求在server产生作用而client没有接收到正确返回
        2. ajax请求头加入origin,询问服务端是否可跨域
        3. server处理可跨域情况:
          1. 加入3个Allow头信息,标记可跨域的源(就是刚才传入的origin)、方法、自定义头信息
          2. 如果需要携带cookie,server端还需返回Allow-Credentials头信息,client在xhr中也需要设置xhr.withCredential = true;
      4. nginx设置反向代理、或wepack的dev-server做反向代理(本质是express)
      5. 跨域是针对ajax的,form表单提交没有跨域问题。
    3. http2.0

      1. 与http1的区别:

        1. 采用二进制而非文本
        2. 头部压缩
        3. 多路复用 - 一个连接上可以有多个请求
      2. 如何知道请求是http1还是http2?

        浏览器network里查看头信息版本

    4. 缓存策略

      1. 强缓存
        1. expires
          1. HTTP 1.0
          2. 返回过期时间,客户端用本地时间与之对比
        2. cache-control
          1. HTTP 1.1
          2. 优先级高于expires
          3. 返回一个是时间长度,不依赖客户段时间
      2. 协商缓存
        1. 先设置no-cache、no-store忽略本地缓存
        2. 先去请求服务端有没有更新
          1. 时间维度:last-modified + if-Modified-Since
          2. 内容维度:ETag + if-None-Match
          3. 前者server返回,后者client带回去
          4. 返回304命中本地缓存,返回200正常返回内容
    5. 鉴权

      1. session-cookie 利用redis,保存用户信息
      2. token/JWT(json web token)服务端不需要保存信息
      3. sso 项目实践 jsonp去其他站点请求下
      4. oauth 发给快递小哥进门的令牌
  4. Web安全

    1. 常见Web攻击

      1. XSS - 跨站脚本攻击
        1. 类型:
          1. 反射形 - url参数上带脚本注入页面,执行恶意脚本
          2. 存储形 - 攻击者在网站表单里输入恶意脚本保存在该网站服务端,比如一条评论,其他看这个评论的用户都会执行这段恶意脚本
        2. 目的:盗取用户cookie
        3. 防范措施:
          1. cookie设置http-only
          2. 检查用户输入,设置黑名单白名单检测,对特殊字符进行编码-> xss filter,serve输出检查-内容转义
          3. 启用浏览器自己的xss防范措施:ctx.set('X-XSS-Protection', 0),浏览器检查到xss会清除页面
      2. csrf - 跨站请求伪造
        1. 利用用户cookie,在恶意网站上请求用户登录网站的接口
        2. 防范措施:
          1. 验证码提醒用户执行了某个操作
          2. referer check:HTTP头信息中的Referer记录了HTTP请求来源地址,server检查是否来自可靠的源
          3. 添加token验证:每次请求携带token,此token不能直接从cookie中获取
      3. xss、csrf区别:xss窃取用户cookie,csrf只是借用,不窃取
      4. 点击劫持
        1. 利用透明iframe做视觉欺骗,结合xss和csrf干坏事
        2. 防范措施:
          1. 设置HTTP响应头X-FRAME-OPTIONS,设置iframe的展示策略
          2. if(window.top != window.self) window.top.location = window.self.location // 恶意iframe直接跳转到自己页面
      5. SQL注入
        1. 利用server处理SQL的漏洞
        2. 防御措施:
          1. 参数化执行SQL,不要直接拼接用户输入的内容到SQL中
          2. 限制Web应用程序操作数据库权限
          3. 入库字符串转义
      6. 请求劫持
        1. DNS劫持
          1. 一般用户上网的DNS服务器是运营商分配的,运营商返回的ip是一个302到目标站的带广告地址
        2. HTTP劫持
          1. 运营商发现是HTTP请求,篡改HTML数据内容
          2. 防御措施:升级至HTTPS
      7. DDOS - 流量攻击
        1. 防御手段:
          1. 备份网站
          2. 硬件层 防火墙等
    2. 防御手段

      1. HTTPS配置
        1. HTTPS = HTTP + SSL
        2. SSL=证书
          1. 作用:网站身份验证、加密传输
          2. 证书保存本地,mac保存在钥匙串里
        3. HTTPS过程
          1. client发起一个请求,server返回数字证书,client验证证书是否有效,并获取公钥R
          2. client生成一个随机对称密钥E,用来加密url链接s,并用R来加密E,把加密后的E和s一起发到server
          3. server用R的私钥揭秘出E,在解密出s,获得ur请求,并用E把返回内容加密发回给serve
          4. client收到加密后的响应,用自己的E解密,即获得内容
  5. 异步

    1. 回调函数

    2. 事件监听

    3. 发布订阅

      1. Node EventEmitter
      2. Vue EventBus
    4. Promise

      1. 状态:pending、resolved、rejected

      2. 解决痛点

        1. 回调地狱 - 提高代码可读性
        2. 支持并发
        3. 解决信任——回调执行过早、没有回调、回调次数太多等
      3. then微任务,结合浏览器事件循环

      4. 构造函数同步,then异步

      5. Promise error catch:

        1. 每个promise添加.catch

        2. 改成async + await,用try catch

        3. window.addEventListener('unhandledrejection', event => ···);
          
      6. promise与generator、async、await的区别和关系

        1. 首先Promise和这些不是一个概念上的东西,Promise是一种异步解决方案
        2. generator是在yield处阻塞,next()才走下一步,这样暗示我们可以yield一个promise,在promise的then中调用这个generator的next获取异步返回值
        3. 使用promise + generator,需要每次都手动调用generator,于是有人写了一个runner方法,自动执行
        4. runner方法被吸收进ES7标准中,即async、await语法糖
      7. 手写promise、promise.all、promise + ajax

        1. 手写promise思路(简版,忽略细节):
          1. new Promise时,会传入一个执行函数exec,其参数是两个方法:resolve、reject,一般exec会做一些异步操作,有结果时调用resolve和reject,传入一些值,修改一些状态(pending、onFullfilled、onReject)
          2. then方法定义在prototype上,then方法接收两个回调函数,分别是成功和失败的回调,then方法执行时如果是pending状态,就把这两个回调函数push进构造方法中维护的两个回调队列,然后在刚刚说的resolve、reject中依次调用两个队列里保存的回调方法,then还要返回promise
          3. 用的时候,new Promise(),传入一个做异步操作的方法,在之前写successCb和failCb的地方替换成resolve、reject
        2. 手写promise.all的思路:(race类似)
          1. Promise上的静态方法
          2. 传入一个promise数组,使用Array.map循环,把返回结果保存到一个result中,注意顺序
          3. 每个prmise执行完后判断数组长度,完成时resolve返回结果数组
          4. race类似,但是循环的时候要用Array.forEach
        3. 手写promise + ajax
          1. 返回一个promise对象 return new Promise(function (resolve, reject) {...})
          2. var xhr = new XMLHttpRequest(); xhr.open('GET', '/url', true);
          3. xhr.onreadystatechage = () => { 替换原回调函数为resolve、reject }
  6. webpack

    1. 哪些常见的loader?

      1. url-loader
      2. souce-map-loader
      3. babel-loader
      4. css-loader
    2. 常见的plugin

      1. Commons-chunk-plugin 提取公共代码
      2. uglifyjs-webpack-plugin 压缩js
    3. loader和plguin的区别?

      1. loader -> 解析非js文件
      2. plugin -> 扩展webpack功能,在webpack生命周期中监听事件,通过webpack API改变输出结果
      3. 用法不同:
        1. loader配置在module.rules中
        2. plugin单独配置
    4. webpack的构建流程:

      1. 初始化参数 - 配置文件 + 命令
      2. 开始编译:初始化compiler对象
      3. 确定入口:entry
      4. 编译模块:从入口文件出发,调用所有配置的loader进行编译,递归找出依赖模块进行编译
      5. 完成编译:得到结果文件和他们之间的依赖关系
      6. 输出资源:最后可以修改输出内容的机会
      7. 输出完成:写入环境系统
    5. 是否写过loader?

      1. loader是一个编译器,可以拿到源文件内容字符串source,经过一系列的处理,将处理后的内容返回,或调用callback返回给webpack
      2. 抽象语法树:
        1. 含义:表示源代码的语法结构,是编译器语法分析的结果,类似于一个json
        2. estraverse遍历更新语法树
        3. escodegen将AST重新生成源码
    6. webpack热更新 HMR

      1. express启动本地服务,对浏览器请求资源作出响应
      2. 使用websocket实现长连接
      3. webpack的watch模式下监听文件系统的改变,即开发者保存文件时出发webpack重新编译
      4. 编译完后socket向客户端推送当前编译的hash戳
      5. 客户端websocket监听推送过来的hash戳,和上一次对比,一致走缓存,不一致通过ajax和jsonp向express服务器请求资源
      6. 客户端局部刷新
    7. 如何利用webpack优化前端性能

      1. 利用插件压缩代码
      2. 利用CND加速
        1. html文件不要缓存
        2. css、js等文件放到cdn上
        3. 设置wbpack的publicPath
          1. 生产环境的publicPath:为静态文件路径前面加上publicPath,应该设置成cdn地址
          2. 开发环境:使用webpack-dev-server,和上面不同,是webpack-dev-server打包出来的静态文件访问路径,这些打包文件存在于内存。
      3. 删除死代码,启动webpack时增加--optimize-minimize来实现
      4. 利用插件提取公共代码
    8. 如何提高webpack构建速度

      1. happypack实现多线程加速编译
      2. moment.js -> day.js
    9. 按需加载

      1. 利用组件库现成的解决方案

      2. Element:babel-plugin-component

      3. Antd:babel-plugin-import

      4. antd按需加载原理:

        把import { Button } from 'antd';编译成

        var _button = require('antd/lib/button');
        require('antd/lib/button/style/css');
        
  7. webpack优化:

    1. 减少webpack打包时间
      1. 优化loader
        1. 影响打包效率最高的就是Babel
        2. 优化loader文件搜索范围
        3. 缓存babel编译过的文件 babel-loader?cacheDirectory=true
      2. 使用HappyPack
        1. 多线程
        2. HappyPack在插件中配置loader,在rules的loader中使用babel-loader?cacheDirectory=true
      3. DllPlugin
        1. DllPlugin将特定的类库提前打包引入
        2. 减少打包类库的次数
        3. 配置dllConf
        4. 使用DllReferencePlugin将依赖文件引入项目
        5. module.noParse:一个文件不引用其他文件,配置此项让webpack不查找依赖
    2. 减小打包后的体积:
      1. 按需引入
      2. tree shaking为什么没用?
        1. 原理:ES6模块引入是静态分析,因此可以在编译阶段知道哪些没用
        2. 分析程序流,判断哪些变量没有被引用,进而删除代码
      3. 为什么你的webpack uglifyJS没用:
        1. class编译的时候,方法不是使用原型链方式,而是Object.defineProperty,这里是有闭包的,导致一些副作用,treeShaking不是按预想的方式工作
        2. rollup是有代码流程分析的,适合做一些库文件的打包