Skip to content

实际工作相关


一、H5 页面如何进行首屏优化

1. 路由懒加载

  • 适用于 SPA ( 不适用 MPA )
  • 路由拆分,优先保证首页加载

2. 服务端渲染 SSR

  • 传统的前后端分离( SPA )渲染页面的过程复杂

  • SSR 渲染页面过程简单,所以性能好

  • 如果是纯 H5 页面,SSR 是性能优化的终极方案

  • SSR 是一门 “ 古老 ” 的技术

    1. 刚刚兴起 Web 1.0 时,就是 SSR 技术:PHP ASP JSP 等
    2. Nuxt.js(Vue)
    3. Next.js(React)

    image-20220330094746581

3. App 预取

  • 如果 H5 在 App WebView 中展示,可使用 App 预取

  • 用户访问列表时,App 预加载文章首屏内容

  • 用户进入 H5 页,直接从 App 中获取内容,瞬间展示首屏

    iShot2022-03-30_09.56.24

4. 分页

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

5. 图片懒加载 lazyLoad

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

6. Hybrid

  • 提前将 HTML JS CSS 下载到 App 内部

  • 在 App webview 中使用 file:// 协议加载页面文件

  • 再用 Ajax 获取内容并展示(也结合 App 预取)

    image-20220330100648932

7. 划重点

  • 服务端 SSR 是 H5 的终极优化方案(但成本也高)
  • 移动端 H5 要结合 App 能力去优化
  • 严格来说,hybrid 不是 H5,但这里回答出来没问题

8. 扩展

  • 性能优化要配合分析、统计、评分等,做了事情要有结果
  • 性能优化也需要配合体验,如骨架屏,loading 动画等

二、一次性返回 10w 条数据如何渲染

1. 设计不合理

  • 后端返回 10w 条数据,本身技术方案设计就不合理
  • 主动沟通此事

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

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

3. 自定义中间层

  • 自定义 nodejs 中间层,获取并拆分这 10w 条数据
  • 前端对接 nodejs 中间层,而不是服务端
  • 成本比较高

4. 虚拟列表

  • 只渲染可视区域 DOM

  • 其他隐藏区域不显示,只用 <div> 撑起高度

  • 随着浏览器滚动,创建和销毁 DOM

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

    iShot2022-03-30_13.51.27

三、文字超出省略

1. 单行文字

css
#box1 {
    border: 1px solid #ccc;
    width: 100px;
    white-space: nowrap; /* 不换行 */
    overflow: hidden;
    text-overflow: ellipsis; /* 超出省略 */
}

2. 多行文字

css
#box2 {
    border: 1px solid #ccc;
    width: 100px;
    overflow: hidden;
    display: -webkit-box; /* 将对象作为弹性伸缩盒子模型显示 */
    -webkit-box-orient: vertical; /* 设置子元素排列方式 */
    -webkit-line-clamp: 3; /* 显示几行,超出的省略 */
}

四、前端常用的设计模式有哪些

1. 设计原则

  • 最重要的思想:开放封闭原则
  • 对扩展开放
  • 对修改封闭

2. 工厂模式

  • 用一个工厂函数,来创建实例,隐藏 new

  • 如 jQuery $ 函数

  • 如 React createElement 函数

    js
    // 工厂模式
    function factory(a, b, c) {
      // if else
      return new Foo()
    }
    
    const f = factory(1, 2, 3)
    
    // $('div') === new JQuery()

3. 单例模式

  • 全局唯一的实例(无法生产第二个)

  • 如 Vuex Redux 的 store

  • 如全局唯一的 dialog modal

    typescript
    // 单例模式
    class SingleTon {
      private static instance: SingleTon | null = null
      private constructor() {}
      public static getInstance(): SingleTon {
        if (this.instance == null) this.instance = new SingleTon()
        return this.instance
      }
      fn1()
      fn2()
    }
    
    const s = new SingleTon()  // 会报错
    const s = SingleTon.getInstance()
    const s1 = SingleTon.getInstance()
    s === s1 // true
  • 扩展

    1. JS 是单线程的,创建单例很简单
    2. Java 是支持多线程的,创建单例要考虑锁死线程
    3. 否则多个线程同时创建,单例就重复了(多线程共享进程内存)

4. 代理模式

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

5. 观察者模式

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

6. 发布订阅

js
// 绑定
event.on('event-key', () => {
  // 事件一
})
event.on('event-key', () => {
  // 事件二
})
// 触发执行
event.emit('event-key')
  • 温故而知新:绑定的事件要记得解除,防内存泄漏

7. 装饰器模式

  • 原功能不变,增加一些新功能( AOP 面向切面编程)
  • ES 和 TypeScript 的 Decorator 语法
  • 类装饰器,方法装饰器

8. 划重点

  • 经典设计模式有 23 个,这是基于后端写的,前段不是都常用
  • 要结合应用场景

9. (连环问) 观察者模式和发布订阅模式的区别

image-20220426104340901

  • 观察者模式
    1. Subject 和 Observer 直接绑定,没有中间媒介
    2. 如 addEventlistener 绑定事件
  • 发布订阅
    1. Publisher 和 Observer 互不认识,需要中间媒介 Event channel
    2. 如 EventBus 自定义事件

五、在实际工作中,做过哪些 Vue 优化

1. v-if 和 v-show

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

2. v-for 使用 key

html
<ul>
  <!-- 而且,key 不要用 index -->
  <li v-for="(id, name) in list" :key="id">{{ name }}</li>
</ul>

3. 使用 computed 缓存

js
export default {
  data() {
    return {
      msgList: [...]  // 消息列表
    }
  },
  computed: {
    // 未读消息数量
    unreadCount() {
      return this.msgList.filter(item => item.read === false).length
    }
  }
}

4. keep-alive 缓存组件

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

5. 异步组件

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

6. 路由懒加载

  • 项目比较大,拆分路由,保证首页先加载

7. 服务端渲染 SSR

  • 可使用 Nuxt.js
  • 按需优化,使用 SSR 的成本比较高

8. 划重点

  • 性能优化要按需进行,不要为了优化而优化

六、(连环问) 你使用 Vue 遇到哪些坑

1. 内存泄漏

  • 全局变量,全局事件,全局定时器
  • 自定义事件

2. Vue2 响应式的缺陷( Vue3 不再有)

  • data 新增属性要用 Vue.set
  • data 删除属性用 Vue.delete
  • 无法直接修改数组 arr[index] = value

3. 路由切换时 scroll 到顶部

  • SPA 的通病,不仅仅是 Vue
  • 如:列表页,滚动到第二屏,点击进入详情页
  • 再返回到列表页(此时组件重新渲染)就 scroll 到顶部

4. 路由切换时 scroll 到顶部 - 解决方案

  • 在列表页缓存数据和 scrollTop 值
  • 当再次返回列表页时,渲染组件,执行 scrollTo(xx)
  • 终极方案:MPA + App WebView

5. 划重点

  • 日常要注意记录总结,遇到坑就记录一下

七、如何统一监听 Vue 组件报错

1. window.onerror

  • 全局监听所有 JS 错误
  • 但它是 JS 级别的,识别不了 Vue 组件信息
  • 捕捉一些 Vue 监听不到的错误

2. errorCaptured 生命周期

  • 监听所有 下级 组件的错误
  • 返回 false 会阻止向上传播

3. errorHandler 配置

  • Vue 全局错误监听,所有组件错误都会汇总到这里

  • 但 errorCaptured 返回 false ,不会传播到这里

    js
    // main.js
    app.config.errorCaptured = (err, vm, info) => {
      console.log('errorCaptured')
    }

4. 异步错误

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

5. 答案

  • errorCaptured 监听下级组件错误,返回 false 阻止向下传播
  • errorHandler 监听全局 Vue 组件错误
  • window.onerror 监听其他 JS 错误,如异步

6. 划重点

  • 实际工作中,三者要结合使用
  • errorCaptured 监听一些重要、有风险组件的错误
  • window.onerror 和 errorHandler 候补全局监听

7. 扩展

  • Promise 为处理的 catch 需要 onunhandledrejection

八、H5 很慢,如何排查性能问题

1. 前端性能指标

  • First Paint ( FP )
  • First Contentful Paint ( FCP )
  • DomContentLoaded ( DCL )
  • Largest Contentfull Paint ( LCP )
  • Load ( L )

2. Chrome devTools

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

3. Lighthouse

  • 非常流行的第三方性能评测工具

  • 支持移动端和 PC

    1. 安装( 或使用 Chrome 自带 )

      shell
      npm i lighthouse -g
    2. 运行

      shell
      lighthouse https://wwww.burt1025.cn --view --preset=desktop

4. 识别问题:哪里慢?

image-20220427100308304

  • 如果是网页加载慢
    1. 优化服务端硬件配置,使用 CDN
    2. 路由懒加载,大组件异步加载 - 减少主包的体积
    3. 优化 HTTP 缓存策略
  • 如果是网页渲染慢
    1. 优化服务端接口(如 Ajax 获取数据慢)
    2. 继续分析,优化前端组件内部的逻辑
    3. 服务端渲染 SSR
  • 持续跟进
    1. 性能优化是一个循序渐进的过程,不像 bug 一次性解决
    2. 持续跟进统计结果,再逐步分析性能瓶颈,持续优化