360前端星计划学习笔记--0411

360前端星计划学习笔记(七)前端工程化浅析–田东东老师
360前端星计划学习笔记(八)前端动画

前端工程化

什么是前端工程化?

工程化:

  • 目标:方案,开发各个阶段,解决低效问题

  • 技术:工程化是一种思想,技术是一种实践,思想的落地离不开行动

  • 原因:为了提效,提效体现在项目的开发、测试及维护阶段

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

规范化(项目可维护性的基石)

版本管理及开发流程规范

编写规范

  • 脚本
  • 样式
  • 目录结构

git 版本管理,代码仓库

git flow 基于Git、简化操作,活动模型、行为规范

在这里插入图片描述

Hotfix 紧急修复

Master 上线的分支

Release

Develop 开发分支

Feature

git flow init
git checkout develop

git pull origin develop
git flow feature start f1
#git checkout develop
#git checkout -b feature/f1
git commit -am "add#"
git push origin feature/f1

git push origin develop

git flow release start 0.0.1

git flow release finish 0.0.1 同时合并到master和develop,并且打tag

git flow hotfix start fix1  //master 和develop同步

参考资料:https://git-scm.com/docs

https://www.atmarkit.co.jp/ait/articles/1708/01/news015.html

https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow

https://danielkummer.github.io/git-flow-cheatsheet/

模拟一次从开发到上线打tag的开发过程

模块化

一般逻辑相关的代码放在同一个文件中,当做一个模块,只需关注模块内逻辑的实现,无需考虑变量污染

CSS模块化

通过样式生效规则来避免冲突

scoped DOM节点添加data-v-version属性 eg: https://juejin.im/

CSS in JS React 用的较多,styled-components

​ 以脚本模块来写样式,甚至有封装好的样式模块可以直接调用

​ 样式=》编译成唯一的sector eg.https://codepen.io

CSS Modules: 借助预编译使样式变成脚本变量

BEM Block_Element-Modifier 按照规则,手写css,并在模板内增加相应class

一般用sass嵌套编译后可实现,写方便,看也方便

eg.elementUI

CSS模块化解决方案

为元素建立shadow root 内部样式与外部样式完全隔离 ,目前支持不多

JS模块化解决方案

CommonJS规范 module.exports=xx;

ES Module-loader: export functon M(){} import {M} from “/a.js”

组件化

核心思想在于分治,好处:团队协作效率和可维护性的提升。

Vue,React ,Webcomponent
在这里插入图片描述

什么是组件?

1、UI为主

2、逻辑为主

定义:在Web前端领域,我们可以将由特定逻辑和UI进行的高内聚,低耦合的封装体。

如何基于组件化思想,如何重构页面

在这里插入图片描述

自动化

自动初始化:vue-cli

自动化构建:webpack

自动化测试:karma,jest

自动化部署,Jenkins

自动化测试:单元测试,集成测试,端到端测试,UI测试(写好单元测试用例)

自动化部署:git push web hook/poll ;gitlab 可持续集成

自动初始化:使用nodejs写脚手架

如何捕获用户输入的参数和命令

用 commander库

如何触发询问与用户交互

inquirer库

如何帮我们执行命令

child_process

如何增强交互效果

chalk,ora

自动化构建

webpack,rulg,parcel

使用webpack4进行项目构建

将所有内容作为模块

核心配置:

  • mode:开发模式,development,production
  • entry入口
  • outout 输出
  • module:{rules:[]}模块和定义规则
  • plugins:[]插件

webpack配置建议:不同环境的配置区分开,集成进来的工具的插件配置单独放置,env配置使用.browserslistrc文件单独放置

前端:遵守规范,要有工程化,自动化的思想

JS动画的原理和基本实现

使用定时器改变对象属性

根据新的属性重新渲染动画

动画的种类

JavaScript 动画

  • 操作DOM

  • canvas

    优点:灵活度,可控性,性能缺点:易用性

CSS 动画

SVG 动画

SMIL

let rotation=0;
requestAnimationFram(function update(){
   block.style.transform=`rotate(${rotation+=15}deg)`
   requestAnimationFrame(update)
}

简单,但精确控制比较难

改进1

let rotation = 0;
let startTime = null;
const T = 2000;
requestAnimationFrame(function update() {
  if(!startTime) startTime = Date.now();
  const p = (Date.now() - startTime)/T;
  block.style.transform = `rotate(${360 * p}deg)`;
  requestAnimationFrame(update);
});

设置周期 轨迹动画等

通用化

function update({target}, count) {
  target.style.transform = `rotate(${count++}deg)`;
}

class Ticker {
  tick(update, context) {
    let count = 0;
    requestAnimationFrame(function next() {
      if(update(context, ++count) !== false) {
        requestAnimationFrame(next);
      }
    });
  }
}

const ticker = new Ticker();
ticker.tick(update, {target: block});

通用化2

function update({target}, {time}) {
  target.style.transform = `rotate(${360 * time / 2000}deg)`;
}

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);
      }
    });
  }
}

const ticker = new Ticker();
ticker.tick(update, {target: block});

通用化3-canvas

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);
      }
    });
  }
}
匀速运动

2s 200px

function update({target}, {timing}) {
  target.style.transform = `translate(${200 * timing.p}px, 0)`;
}

const ticker = new Ticker();
ticker.tick(update, 
  {target: block}, 
  {duration: 2000}
);
自由落体运动
function update({target}, {timing}) {
  target.style.transform = `translate(0, ${200 * timing.p}px)`;
}

const ticker = new Ticker();
ticker.tick(update, {target: block}, {
  duration: 2000,
  easing: p => p ** 2,
});
匀减速运动

easing p*(2-p)

function update({target}, {timing}) {
  target.style.transform = `translate(${200 * timing.p}px, 0)`;
}

const ticker = new Ticker();
ticker.tick(update, {target: block}, {
  duration: 2000,
  easing: p => p * (2 - p),
});
平抛
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 op() {
    return Math.min(this.time / this.duration, 1.0);
  }
  get p() {
    return this.easing(this.op);
  }
}

function update({target}, {timing}) {
  target.style.transform = 
    `translate(${200 * timing.op}px, ${200 * timing.p}px)`;
}
旋转+平抛
function update({target}, {timing}) {
  target.style.transform = `
    translate(${200 * timing.op}px, ${200 * timing.p}px)
    rotate(${720 * timing.op}deg)
  `;
}
贝塞尔轨迹

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iekAF3uq-1586614901735)(360-learn-note-8/image-20200411161304394.png)]

function bezierPath(x1, y1, x2, y2, p) {
  const x = 3 * x1 * p * (1 - p) ** 2 + 3 * x2 * p ** 2 * (1 - p) + p ** 3;
  const y = 3 * y1 * p * (1 - p) ** 2 + 3 * y2 * p ** 2 * (1 - p) + p ** 3;
  return [x, y];
}

function update({target}, {timing}) {
  const [px, py] = bezierPath(0.2, 0.6, 0.8, 0.2, timing.p);
  target.style.transform = `translate(${100 * px}px, ${100 * py}px)`;
}

const ticker = new Ticker();
ticker.tick(update, {target: block}, {
  duration: 2000,
  easing: p => p * (2 - p),
});
bezier-easing

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iUoQdPtn-1586614901738)(360-learn-note-8/image-20200411161453326.png)]

function update({target}, {timing}) {
  target.style.transform = `translate(${100 * timing.p}px, 0)`;
}

const ticker = new Ticker();
ticker.tick(update, {target: block}, {
  duration: 2000,
  easing: BezierEasing(0.5, -1.5, 0.5, 2.5),
});

使用bazier-easing的库

bazier-easing轨迹
function update({target}, {timing}) {
  target.style.transform =
    `translate(${100 * timing.p}px, ${100 * timing.op}px)`;
}

const ticker = new Ticker();
ticker.tick(update, {target: block}, {
  duration: 2000,
  easing: BezierEasing(0.5, -1.5, 0.5, 2.5),
});
椭圆轨迹

使用参数方式改变alpha

周期运动
class Timing {
  constructor({duration, easing, iterations = 1} = {}) {
    this.startTime = Date.now();
    this.duration = duration;
    this.easing = easing || function(p){return p};
    this.iterations = iterations;
  }
  get time() {
    return Date.now() - this.startTime;
  }
  get finished() {
    return this.time / this.duration >= 1.0 * this.iterations;//周期数
  }
  get op() {
    let op = Math.min(this.time / this.duration, 1.0 * this.iterations);
    if(op < 1.0) return op;
    op -= Math.floor(op);
    return op > 0 ? op : 1.0;
  }
  get p() {
    return this.easing(this.op);
  }
}
椭圆周期运动
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});
连续运动
class Ticker {
  tick(update, context, timing) {
    let count = 0;
    timing = new Timing(timing);
    return new Promise((resolve) => {
      requestAnimationFrame(function next() {
        count++;
        if(update(context, {count, timing}) !== false && !timing.finished) {
          requestAnimationFrame(next);
        } else {
          resolve(timing);
        }
      });      
    });
  }
}

function left({target}, {timing}) {
  target.style.left = `${100 + 200 * timing.p}px`;
}
function down({target}, {timing}) {
  target.style.top = `${100 + 200 * timing.p}px`;
}
function right({target}, {timing}) {
  target.style.left = `${300 - 200 * timing.p}px`;
}
function up({target}, {timing}) {
  target.style.top = `${300 - 200 * timing.p}px`;
}

(async function() {
  const ticker = new Ticker();
  await ticker.tick(left, {target: block},
    {duration: 2000});
  await ticker.tick(down, {target: block},
    {duration: 2000});
  await ticker.tick(right, {target: block},
    {duration: 2000});
  await ticker.tick(up, {target: block},
    {duration: 2000});
})();
线性插值

f ( p ) = f r o m + ( t o − f r o m ) ∗ p f(p)=from +(to-from)*p f(p)=from+(tofrom)p

f ( p ) = t o ∗ p + f r o m ∗ ( 1 − p ) f(p)=to*p+from*(1-p) f(p)=top+from(1p)

function lerp(setter, from, to) {
  return function({target}, {timing}) {
    const p = timing.p;
    const value = {};
    for(let key in to) {
      value[key] = to[key] * p + from[key] * (1 - p);
    }
    setter(target, value);
  }
}

function setValue(target, value) {
  for(let key in value) {
    target.style[key] = `${value[key]}px`;
  }
}

const left = lerp(setValue, {left: 100}, {left: 300});
const down = lerp(setValue, {top: 100}, {top: 300});
const right = lerp(setValue, {left: 300}, {left: 100});
const up = lerp(setValue, {top: 300}, {top: 100});

(async function() {
  const ticker = new Ticker();
  await ticker.tick(left, {target: block},
    {duration: 2000});
  await ticker.tick(down, {target: block},
    {duration: 2000});
  await ticker.tick(right, {target: block},
    {duration: 2000});
  await ticker.tick(up, {target: block},
    {duration: 2000});
})();

弹跳的小球
const down = lerp(setValue, {top: 100}, {top: 300});
const up = lerp(setValue, {top: 300}, {top: 100});

(async function() {
  const ticker = new Ticker();
  
  // noprotect
  while(1) {
    await ticker.tick(down, {target: block},
      {duration: 2000, easing: p => p * p});
    await ticker.tick(up, {target: block},
      {duration: 2000, easing: p => p * (2 - p)});
  }
})();
弹跳的小球(含衰减)
(async function() {
  const ticker = new Ticker();
  let damping = 0.7,
      duration = 2000,
      height = 300;

  // noprotect
  while(height >= 1) {
    let down = lerp(setValue, {top: 400 - height}, {top: 400});
    await ticker.tick(down, {target: block},
      {duration, easing: p => p * p});
    height *= damping ** 2;
    duration *= damping;
    let up = lerp(setValue, {top: 400}, {top: 400 - height});
    await ticker.tick(up, {target: block},
      {duration, easing: p => p * (2 - p)});
  }
})();
滚动
const roll = lerp((target, {left, rotate}) => {
    target.style.left = `${left}px`;
    target.style.transform = `rotate(${rotate}deg)`;
  },  
  {left: 100, rotate: 0}, 
  {left: 414, rotate: 720});


const ticker = new Ticker();

ticker.tick(roll, {target: block},
  {duration: 2000, easing: p => p});
平稳变速
function forward(target, {y}) {
  target.style.top = `${y}px`;
}

(async function() {
  const ticker = new Ticker();

  await ticker.tick(
    lerp(forward, {y: 100}, {y: 200}), 
    {target: block},
    {duration: 2000, easing: p => p * p}); 

  await ticker.tick(
    lerp(forward, {y: 200}, {y: 300}), 
    {target: block},
    {duration: 1000, easing: p => p}); 

  await ticker.tick(
    lerp(forward, {y: 300}, {y: 350}), 
    {target: block},
    {duration: 1000, easing: p => p * (2 - p)}); 
}());
甩球
function circle({target}, {timing}) {
  const p = timing.p;
  const rad = Math.PI * 2 * p;

  const x = 200 + 100 * Math.cos(rad);
  const y = 200 + 100 * Math.sin(rad);
  target.style.left = `${x}px`;
  target.style.top = `${y}px`;
}
function shoot({target}, {timing}) {
  const p = timing.p;
  const rad = Math.PI * 0.2;
  const startX = 200 + 100 * Math.cos(rad);
  const startY = 200 + 100 * Math.sin(rad);
  const vX = -100 * Math.PI * 2 * Math.sin(rad);
  const vY = 100 * Math.PI * 2 * Math.cos(rad);
  
  const x = startX + vX * p;
  const y = startY + vY * p;

  target.style.left = `${x}px`;
  target.style.top = `${y}px`;
}
(async function() {
  const ticker = new Ticker();

  await ticker.tick(circle, {target: block},
    {duration: 2000, easing: p => p, iterations: 2.1}); 
  await ticker.tick(shoot, {target: block},
    {duration: 2000});
}());
逐帧动画
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title></title>
</head>
<style type="text/css">
    .sprite {
      display:inline-block; 
      overflow:hidden; 
      background-repeat: no-repeat;
      background-image:url(https://p.ssl.qhimg.com/t01f265b6b6479fffc4.png);
    }
    
    .bird0 {width:86px; height:60px; background-position: -178px -2px}
    .bird1 {width:86px; height:60px; background-position: -90px -2px}
    .bird2 {width:86px; height:60px; background-position: -2px -2px}
    
     #bird{
       position: absolute;
       left: 100px;
       top: 100px;
       zoom: 0.5;
     }
    </style>
    
<body>
    <div id="bird" class="sprite bird1"></div>
    <script type="text/javascript">
    var i = 0;
    setInterval(function(){
      bird.className = "sprite " + 'bird' + ((i++) % 3);
    },1000/10)
      </script>
</body>
</html>

Web Animation API

element.animate(keyframes, options);

target.animate([
  {backgroundColor: '#00f', width: '100px', height: '100px', borderRadius: '0'},
  {backgroundColor: '#0a0', width: '200px', height: '100px', borderRadius: '0'},
  {backgroundColor: '#f0f', width: '200px', height: '200px', borderRadius: '100px'},
], {
  duration: 5000,
  fill: 'forwards',
});
function animate(target, keyframes, options) {
  const anim = target.animate(keyframes, options);
  return new Promise((resolve) => {
    anim.onfinish = function() {
      resolve(anim);
    }
  });
}

(async function() {
  await animate(ball1, [
    {top: '10px'},
    {top: '150px'},
  ], {
    duration: 2000,
    easing: 'ease-in-out',
    fill: 'forwards',
  });

  await animate(ball2, [
    {top: '200px'},
    {top: '350px'},
  ], {
    duration: 2000,
    easing: 'ease-in-out',
    fill: 'forwards',
  });
 
  await animate(ball3, [
    {top: '400px'},
    {top: '550px'},
  ], {
    duration: 2000,
    easing: 'ease-in-out',
    fill: 'forwards',
    })

总结

javascript 动画的方式:
增量的方式
通过时间的方式
连续动画使用Promise封装后,通过await调用

前端性能优化

RAIL模型

定义

以用户为中心,每个网络应用都具有与其生命周期有关的四个方面

指导意见:推荐的性能评估标准,目标:

主要的四个方面:Response,Animation,Idle,Load

延迟与用户反应,感知

在这里插入图片描述

指导意见:

  • 50ms内处理用户输入事件,确保100ms反馈用户可视的响应

  • 对于开销大的任务可分割任务处理,或放到worker进程中执行,避免影响用户交互

  • 处理时间超过50ms的操作,始终给予反馈(进度和活动指示器)

在这里插入图片描述

动画:10ms 一帧

1000ms/60帧

在这里插入图片描述

空闲时间最大化

目标:最大化空闲时间以增加页面在100ms内响应用户输入的几率

指导:利用空闲时间完成推迟的工作

​ 空闲时间期间用户交互优先级最高

5s 呈现交互内容
在这里插入图片描述

总结:

响应:在100ms内响应用户输入

动画:动画或滚动时,10ms产生一帧

空闲时间:主线程空闲时间最大化

加载:在1000ms内呈现交互内容

以用户为中心

工具篇

Lighthouse

google Audit 提供优化建议

WebPageTest

提交服务地址,发报告给我们

Chrome DevTools

实战篇

浏览器渲染场景

在这里插入图片描述
查看每个样式所影响的范围

浏览器渲染流程

在这里插入图片描述
JavaScript (实现动画,操作DOM等)
Style(Render Tree)
Layout(盒模型,确切的位置和大小)
Paint(栅格化)
Composite(渲染层合并)
设置浏览器CPU限制

可以录制火焰图

Chrome

console

elements 元素布局

sources

network

Performance性能分析

Application

Audits 审计

性能指标的评分

如何分析网站性能

Performance 截图

录制过程中,生成火焰图,打开Main,

点击红色部分,下面会在Summary中列出任务发生的具体情况,链接到原文件位置,发现性能瓶颈

尽量不要在设置样式之后读取样式属性

const offsetTop=parseInt(m.style.top.slice(0,-2))

代替所有的m.offsetTop

减少触发强制布局,

深度优化:细节执行的问题

将样式设置方式修改

m.style.top =pos +“px”

替换为 m.style.transform =translateY(${post-m.style.top.slice(0,-2)}px)

性能优化的方向

加载

  • 资源效率优化,压缩,缓存
  • 图片优化
  • 字体优化
  • 关键渲染路径优化

渲染

  • javaScirpt执行优化
  • 避免大型复杂的布局
  • 渲染层合并
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值