360前端星——0411笔记

297 阅读7分钟

前端工程化浅析

  • 目标:通过技术进步与经验积累带来的方案,来解决项目开发、测试、维护阶段遇到的低效、繁琐问题

  • 工程化是一种思想,技术是一种实践

  • 为什么要工程化:提高效率【开发、测试、维护阶段】

  • 规范化、模块化、组件化、自动化

规范化

  • 是项目可维护性的基石
    • 版本管理及开发流程规范
      • 多人协作
      • git
      • git flow
        • 简化git操作
        • 行为规范

git flow 常用分支

  • Production 分支

也就是我们经常使用的Master分支,这个分支最近发布到生产环境的代码,最近发布的Release, 这个分支只能从其他分支合并,不能在这个分支直接修改

  • Develop 分支

这个分支是我们是我们的主开发分支,包含所有要发布到下一个Release的代码,这个主要合并与其他分支,比如Feature分支

  • Feature 分支

这个分支主要是用来开发一个新的功能,一旦开发完成,我们合并回Develop分支进入下一个Release

  • Release分支

当你需要一个发布一个新Release的时候,我们基于Develop分支创建一个Release分支,完成Release后,我们合并到Master和Develop分支

  • Hotfix分支

当我们在Production发现新的Bug时候,我们需要创建一个Hotfix, 完成Hotfix后,我们合并回Master和Develop分支,所以Hotfix的改动会进入下一个Release

  • 编写规范
    • 脚本、样式、目录结构、命名、路由等等

模块化

  • 按照逻辑将代码进行模块划分

CSS模块化解决方案

  • 核心思想:通过样式生效规则来避免冲突
    • scoped:组件里的样式不会影响其他组件
      • 给DOM节点添加data-v-version属性,进行一一对应
      • 参考掘金
    • CSS in JS:以脚本模块来写样式,封装好的样式模块可直接调用
      • React常用
      • 将CSS样式作为一个组件
      • 参考Instagram
    • 预编译:使样式成为脚本中的变量
    • BEM:Block_Element-Modifier,按照规则手写CSS,并在模块内增加相应class
      • ElementUI
      • 借助可编程的CSS来写,Sass
    • Shadow DOM

JS模块化解决方案

  • Node.js——CommonJS
    • 通过require()引入模块依赖,require函数可以引入Node的内置模块、自定义模块和npm等第三方模块。
  • ES6 module
    • ES6的模块化已经不是规范了,而是JS语言的特性。
      • 模块化规范输出的是一个值的拷贝,ES6 模块输出的是值的引用。
      • 模块化规范是运行时加载,ES6 模块是编译时输出接口。

组件化

  • 组件化与模块化的核心思想都在于分治,带来的好处是团队协作效率和项目可维护性的提升
    • Vue、React
  • 什么是组件
    • UI为主
      • 以UI块为划分标准
    • 逻辑为主
      • 按照功能逻辑划分
  • 组件:由特定逻辑和UI进行的高内聚、低耦合的封装体

自动化

  • 是工程化的核心
  • 自动初始化
    • vue-cli
    • 事半功倍
  • 自动构建、打包
    • webpack
  • 自动测试
    • karma,jest
    • 单元测试、集成测试、端到端测试、UI测试
  • 自动部署
    • Jenkins

写一个基于Node.js的cli

  • 捕获用户输入的参数和命令,获得参数并触发回调

    • 使用commander
    const program = require('commander')
    program.on('--help', _=>{})
    program.command('init').action((name,options) => {})
    
  • 触发询问与用户交互

    • inquirer
    const inquirer = require('inquirer')
    inquirer.prompt({
        type: 'confirm',
        nameL 'name',
        message: '是否将页面发布',
        default: true
    }).then(answer => {})
    
  • 执行命令child_process、发送HTTP请求http

  • 增强交互效果chalk

Webpack构建项目

  • 配置参数
    • module下的rules属性是最关键的,定义了什么文件用什么插件编译
  • 不同环境的配置进行区分
    • base/dev/prod
  • 集成进来的工具的插件配置单独放置
    • babel/eslint等
  • env配置使用.browserslistrc文件单独放置

JS动画原理与实现

动画的基本原理

  • 定时器改变对象的属性
  • 根据新的属性重新渲染动画

JS动画

  • 采用增量的方式实现动画,虽然简单,但是不容易精确控制

  • 通过时间控制更合适,比如设置角度

    requestAnimationFrame(function update() {
      if(!startTime) {
        startTime = Date.now()
      }
      const p = (Date.now() - startTime)/T
      block.style.transform = `rotate(${360*p}deg)`
      requestAnimationFrame(update)
    })
    
  • 进行通用化,比如用canvs渲染

    function update({ context }, { time }) {
          context.clearRect(0, 0, 512, 512);
          context.save();
          context.translate(100, 100);
          context.rotate(time * 0.005);
          context.fillStyle = '#00f';
          context.fillRect(-50, -50, 100, 100);
          context.restore();
        }
        class Ticker {
          tick(update, context) {
            let count = 0;
            let startTime = Date.now();
            requestAnimationFrame(function next() {
              count++;
              const time = Date.now() - startTime;
              if (update(context, { count, time }) !== false){
                requestAnimationFrame(next);
              }
            })
          }
        }
    
  • 使用Timing类

    class Timing {
      constructor({ duration, easing } = {}) {
        this.startTime = Date.now();
        this.duration = duration;
        this.easing = easing || function (p) { return p };
      }
      get time() {
        return Date.now() - this.startTime;
      }
      get p() {
        return this.easing(Math.min(this.time / this.duration, 1.0));
      }
    }
    class Ticker {
      tick(update, context, timing) {
        let count = 0;
        timing = new Timing(timing);
        requestAnimationFrame(function next() {
          count++;
          if (update(context, { count, timing }) !== false) {
            requestAnimationFrame(next);
          }
        });
      }
    }
    
  • 匀速运动

    • s_t = S*p = v*t
    • update 传入200*timing.p
  • 自由落体运动

    • s_t = S*p^2 = (S/T^2)t^2
    • p -> p^2
  • 摩擦力

    • s_t = Sp(2-p)
    • p -> p*(2-p)
  • 可以通过随机组合实现更多效果

  • 贝塞尔函数

  • 椭圆轨迹

    • 根据代数方程/参数方程
  • 周期运动

  • 椭圆周期运动,如下:

    function update({ target }, { timing }) {
      const x = 150 * Math.cos(Math.PI * 2 * timing.p)
      const y = 100 * Math.sin(Math.PI * 2 * timing.p)
      target.style.transform =
        `translate(${ x }px, ${ y }px)`
    }
    const ticker = new Ticker();
    ticker.tick(update, { target: block }, { duration: 2000, iterations: 10 })
    
    
  • 连续运动

    • 返回一个promise,用await来逐步执行
  • 逐帧动画

    • 使用background-position来改变图片位置
    • 使用SetInterval()每隔一段时间换一次class
  • Web Animation API

    • 传入关键帧,与CSS是对应的
    • 封装成promise,达到逐个小球运动的效果

前端性能优化

  • 为什么做:与用户体验相关,决定了用户去留。希望发现网站的性能瓶颈,从而提升用户体验

RAIL模型——以用户为中心

  • Response
    • 响应:50ms处理事件
      • 目标:在100ms内响应用户输入
      • 指导:
        • 50ms内处理用户输入事件,确保100ms内反馈用户可视的响应
        • 对于开销大的任务可分隔任务处理,或者放到worker进程中执行,避免影响用户交互
        • 处理时间超过50ms的操作,始终给与反馈(进度和活动指示器)
  • Animation
    • 动画:10s 一帧
      • 目标:10ms或更短的时间内生成一帧
        • 1s内生成60帧,再减去开销
        • 视觉平滑
      • 指导:动画尽量不要处理逻辑,提高达到60fps的机会
        • 动画类型:滚动、视觉动画、拖拽动画
  • Idle
    • 空闲时间
      • 目标:最大化空闲时间以增加页面在100ms内响应用户输入的几率
      • 指导:利用空闲时间完成推迟的工作
  • Load
    • 加载:5s内呈现交互内容
      • 目标:首屏加载连接3G缓慢的中档移动设备5s内呈现可交互内容
        • 非首屏加载应该在2s内完成
      • 指导:
        • 测试用户常用设备和网络连接情况的性能
        • 优化关键渲染路径以接触阻止渲染
        • 启动渐进式渲染和在后台执行一些工作
        • 影响加载性能的因素:
          • 网络速度
          • 硬件
          • 解析JS
            • 会阻止页面加载

延迟与用户反应:

  • 100ms 以内用户会感觉可以立即获得结果

工具篇

  • lighthouse
    • 可以选择设备类型、功能、限制CPU等
  • chrome DevTools

实战篇

浏览器渲染场景

  • JS/CSS =》 计算样式 =》 布局 =》 绘制 =》 渲染层合并
  • JS/CSS =》 计算样式 =》 绘制 =》 渲染层合并
  • JS/CSS =》 计算样式 =》 渲染层合并

csstriggers.com查看每个属性影响的范围

浏览器渲染流程

  • JS
    • 实现动画,操作DOM
  • Style
    • 产出渲染树
  • Layout
    • 盒模型,确切的位置和大小
  • Paint
    • 栅格化,完整显示
  • Composite
    • 渲染层合并
  1. 解析html建立dom树
  2. 解析css构建render树(将CSS代码解析成树形的数据结构,然后结合DOM合并成render树
  3. 布局render树(Layout/reflow),负责各元素尺寸、位置的计算
  4. 绘制render树(paint),绘制页面像素信息
  5. 浏览器会将各层的信息发送给GPU,GPU会将各层合成(composite),显示在屏幕上。

  • 使用chrome DevTools的performance选项查看性能
    • 在sources中可以查看代码的耗时情况
      • 优化方向:尽量不要在设置样式之后读取它的样式属性
      • 用transform属性来移动元素

性能优化方向

  • 加载
    • 资源效率优化
    • 图片优化
    • 字体优化
    • 关键渲染路径优化
  • 渲染
    • JS执行优化
    • 避免大型复杂的布局
    • 渲染层合并

其他优化方法

  • 样式表放在head标签中,脚本放在body结束前**。
    • 会减少页面首屏出现的时间,使页面内容逐步呈现,改善用户体验,防止“白屏”。
    • js的下载和执行会阻塞Dom树的构建(严谨地说是中断了Dom树的更新),所以script标签放在首屏范围内的HTML代码段里会截断首屏的内容。
  • 使用GPU加速
  • 使用CDN
  • 尽可能使用字体图标,减少图片的使用
  • 将多个样式表或者脚本文件合并到一个文件中,可以减少HTTP请求的数量从而缩短效应时间。
  • 避免重定向
    • 当页面发生了重定向,就会延迟整个HTML文档的传输。在HTML文档到达之前,页面中不会呈现任何东西,也没有任何组件会被下载。
  • 设置缓存
    • 通过在响应头添加cache-control字段