Skip to content

一、前端实战方案

1. H5 页面如何进行 "首屏" 优化?

  • 路由懒加载

    • 适合与 SPA(不适用 MPA)
    • 路由拆分,优先保证首页加载
  • 服务端渲染 SSR

    • 传统的前后端分离(SPA)渲染页面的过程复杂
    • SSR 渲染页面过程简单,所以性能好
    • 如果是纯 H5 页面,SSR 是性能优化的终极方案
      • SSR 是一门"古老"的技术
      • 刚刚兴起 Web 1.0 时,就是 SSR 技术:PHP ASP JSP 等
      • Nuxt.js (Vue)
      • Next.js (React)
  • App 预取

    • 如果 H5 在 App WebView 中展示,可使用 App 预取
    • 用户访问列表页时,App 预加载文章首屏内容
    • 用户进入 H5 页,直接从 App 中获取内容,瞬间展示首屏
  • 分页

    • 针对列表页
    • 默认只展示第一页内容
    • 上划加载更多
  • 图片懒加载 lazyLoad

    • 针对详情页
    • 默认只展示文本内容,然后触发图片懒加载
    • 注意:提前设置图片尺寸,尽量只重绘不重排
  • Hybrid

    • 提前将HTML JS CSS 下载到 App 内部
    • 在 App webview 中使用 file:// 协议加载页面文件
    • 再用 Ajax 获取内容并展示(也结合 App 预取)
  • 划重点

    • 服务端 SSR 是 H5 的终极优化方案(但成本也高)
    • 移动端 H5 要结合 App 能力去优化
    • 严格来说,hybrid 不是 H5
  • 扩展

    • 性能优化要配合分析、统计、评分等,做了事情要有结果。
    • 性能优化也要配合体验,如骨架屏,loading 动画等
    • 不同的形式,有不同的优化方式,要积极和面试官沟通

2. 后端一次性返回 10w 条数据,你该如何渲染?

  • 后端返回 10w 条数据,本身技术方案设计就不合理

  • 主动和面试沟通此事

  • 如果面试官非要这么做,那再继续寻找解决方案

  • 浏览器能否处理 10w 条数据

    • JS 没问题
    • 渲染到 DOM 会非常卡顿
  • 方案

    • 自定义中间层

      • 自定义 nodejs 中间层,获取并拆分这 10w 条数据。
      • 前端对接 nodejs 中间层,而不是服务端
      • 成本比较高
    • 虚拟列表[低配手机效果不一定好]

      • 只渲染可视区域 DOM
      • 其他隐藏区域不显示,只用 <div> 撑起高度
      • 随着浏览器滚动,创建和销毁 DOM 10w.png
    • 虚拟列表-第三方 lib

      • 虚拟列表实现起来非常复杂,可借用第三方 lib
      • Vue-virtual-scroll-list
      • React-virtualiszed

3. 前端常用的设计模式有哪些? 并说明使用场景?

  • 23个设计模式

  • 设计原则

    • 最重要的思想:开放封闭原则
      • 对扩展开放
      • 对修改关闭
  • 设计模式

    • 工厂模式

      • 用一个工厂函数,来创建实例,隐藏 new
      • 如 jQuery $ 函数
      • 如 React createElement 函数
        js
        class Foo {}
        
        // 工厂模式
        function factory() {
          return new Foo()
        }
        
        const f1 = factory()
    • 单例模式

      • 全局唯一的实例(无法生成第二个)
      • 如 Vuex Redux 的 store
      • 如 全局唯一的 dialog modal
        js
        class SingleTon {
          public static instance;
          private constructor() {}
          public static getInstance() {
            if (!this.instance) {
              this.instance = new SingleTon()
            }
            return this.instance;
          }
        
          fn1() {}
          fn2() {}
        }
        
        const s1 = SingleTon.getInstance()
        s1.fn1()
        s1.fn2()
        
        const s2 = SingleTon.getInstance()
        console.log(s1 === s2 ) // true
    • 代理模式

      • 使用者不能直接访问对象,而是访问一个代理层
      • 在代理层可以监听 get set 做很多事情
      • 如 ES6 Proxy 实现 Vue3 响应式
    • 观察者模式

      js
      // 一个主题,一个观察者,主题变化之后触发观察者执行
      btn.addEventListener('click', () => {})
    • 发布订阅

      js
      // 绑定
      event.on('event-key', () => {
        // 事件1
      })
      event.on('event-key', () => {
        // 事件2
      })
      
      // 触发执行
      event.emit('event-key')
    • 装饰器模式

      • 原功能不变,增减一些新功能(AOP 面向切面编程)
      • ES 和 Typescript 的 Decorator 语法
      • 类装饰器,方法装饰器
      • nest.js node.js的框架
  • 连环问:观察者模式和发布订阅模式的区别 publish.png

    • 观察者模式

      • Subject 和 Observer 直接绑定,没有中间媒介
      • 如 addEventListener 绑定事件
    • 发布订阅

      • Publisher 和 Observer 互不相识,需要中间媒介 Event channel
      • 如 EventBus 自定义事件

4. 你在实际工作中,做过哪些 Vue 优化?

  • v-if 和 v-show

    • v-if 彻底销毁组件
    • v-show 使用 CSS 隐藏组件
    • 大部分情况下使用 v-if 更好,不要过度优化
  • v-for 使用 key, 不要使用 index

  • 使用 computed 缓存

  • keep-alive 缓存组件

    • 频繁切换的组件,如 tabs
    • 不要乱用,缓存太多会占用内存,且不好 debug
  • 异步组件 defineAsyncComponent

    • 针对体积较大的组件,如编辑器、复杂表格、复杂表单等
    • 拆包,需要时异步加载,不需要时不加载
    • 减少主包的体积,首页会加载更快
  • 服务端渲染 SSR

    • 可使用 Nuxt.js
    • 按需优化,使用 SSR 的成本比较高
  • 连环问:你使用 Vue 遇到过哪些坑?

    • 内存泄漏

      • 全局变量、全局事件、全局定时器
      • 自定义事件
    • Vue2 响应式的缺陷(Vue3 不再有)

      • data 新增属性用 Vue.set
      • data 删除属性用 Vue.delete
      • 无法直接修改数据 arr[index] = value
    • 路由切换时 scroll 到顶部

      • SPA 的通病,不仅仅是 Vue
      • 如:列表页,滚动到第二屏,点击进入详情页
      • 再返回到列表页(此时组件重新渲染)就 scroll 到顶部
      • 解决方案
        • 方案一:
          • 在列表页缓存数据和 scrollTop 值
          • 当再次返回列表页时,渲染组件,执行 scrollTo(xx)
        • 终级方案:
          • MPA + App WebView

5. 你在实际工作中,做过哪些 React 优化?

  • 修改 CSS 模拟 v-show

  • 循环使用 key

  • 使用 Fragment 减少层级

  • JSX 中不要定义函数

  • 要在构造函数中 bind this react-bind.png

  • 使用 shouldComponentUpdate

    • 使用 shouldComponentUpdate 判断组件是否要更新
      • 注意1:this.setState() 不可变数据。意思不要修改里面的值,比如:数组 使用 concat 不要使用 push
      • 注意2:React 默认会让所有的子组件都更新,无论涉及的数据是否变化。
        • componentDidUpdate()
        • 子组件可以自己控制是否更新
          js
          shouldComponentUpdate(nextProps, nextProps) 
          {
            // 默认
            return true;
            // 自己控制
            if (this.props.list === nextProps.list) {
              return false;
            }
            return true;
          }
    • 或者使用 React.PureComponent
    • 函数组件使用 React.memo
  • 异步组件

  • 路由懒加载

  • SSR - Next.js

  • 拓展:不可变数据第三方插件 immer

  • 连环问:你使用 React 遇到哪些坑?

    • 自定义组件的名称首字母要大写
    • JSX 关键字的冲突
    • JSX 的数据类型
    • setState 是异步更新的

6. 如何统一监听 Vue 组件报错?

答案:

  • errorCaptured 监听下级组件错误,返回 false 阻止向上传播

  • errorHandler 监听全局 Vue 组件错误

  • window.onerror 监听其他 JS 错误,如异步

  • window.onerror

    • 全局监听所有 JS 报错
    • 但它是 JS 级别的,识别不了 Vue 组件信息
    • 捕捉一些 Vue 监听不到的错误
    js
      // 方式一:
      window.onerror = function (msg, source, line, column, error) {  }
      // 方式二:
      window.addEventListener('error', event => {})
  • errorCaptured 生命周期 Vue

    • 监听所有下级组件的错误
    • 返回 false 会阻止向上传播
  • errorHandler 配置 跟 window.onerror 冲突

    • Vue 全局错误监听,所有组件错误都会汇总到这里
    • 但是 errorCaptured 返回 false, 不会传播到这里
  • 异步错误 Vue 监听不到

    • 异步回调里的错误,errorHandler 监听不到
    • 需要使用 window.onerror
  • 扩展

    • Promise 为处理的 catch 需要 onUnhandledRejection
    • JS 报错统一(埋点、上报、统计)

7. 如何统一监听 React 报错?

答案:

  • ErrorBoundary 监听组件渲染报错

  • 事件报错使用 try-catch 或 window.onerror

  • 异步报错使用 window.onerror

  • ErrorBoundary 组件

    • 监听所有下级组件报错,可降级展示 UI
    • 只监听组件渲染时报错,不监听 DOM 事件、异步错误
    • production 环境生效,dev 会直接抛出错误
  • 事件报错

    • ErrorBoundary 不会监听 DOM 事件报错
    • 可用 try-catch
    • 可用 window.onerror
  • 异步错误

    • ErrorBoundary 不会监听异步报错
    • 可用 window.onerror

8. 如果一个 H5 很慢,你该如何排查性能问题?

答案:

  • 分析性能指标,找到慢的原因

  • 对症下药,解决问题

  • 持续跟进,持续优化

  • 性能指标,性能检测工具

  • 使用"二分法", 可逐步找出问题根源

  • 要有"监控","持续跟进"的思维。解决了问题,还得保持住

  • 前端性能指标

    • First Paint(FP)
    • First Contentful Paint(FCP)
    • First Meaningful Paint(FMP) 已弃用,改用 LCP
    • DomContentLoaded(DCL)
    • Largest Contentful Paint(LCP)
    • Load(L)
  • Chrome devTools 【测试最好在无痕模式下查看】

    • Performance 可查看上述性能指标,并有网页快照
    • Network 可以查看各个资源的加载时间 chrome-network.png
  • Lighthouse

    • 非常流行的第三方性能评测工具
    • 支持移动端 和 PC lighthouse.pnglighthouse-1.png
    • 使用
  • 网页加载慢

    • 优化服务端硬件配置,使用 CDN
    • 路由懒加载,大组件异步加载——减少主包的体积
    • 优化 HTTP 缓存策略
  • 网页渲染慢

    • 优化服务端接口
    • 继续分析,优化前端组件内部的逻辑
    • 服务端渲染 SSR
  • 持续跟进

    • 性能优化是一个循序渐进的过程,不像 bug 一次性解决
    • 持续跟进统计结果,再逐步分析性能瓶颈,持续优化
    • 可使用第三方统计服务,如阿里云 ARMS、百度统计

9. 你工作经历中,遇到过哪些项目难点,如何解决的?

答案模板

  • 描述问题:背景 + 现象 + 造成的影响
  • 问题如何被解决:分析 + 解决
  • 自己的成长:学到了什么 + 以后如何避免

Released under the MIT License.