前端工程化
-
规范化
流程控制 git git flow
编写规范
-
模块化
核心需求 避免变量污染
- CSS模块化
- JS模块化
-
组件化
对于UI和逻辑的封装
-
自动化
将重复费时的操作交给工具
- 初始化 cli
- 构建 webpack
- 测试 jest
- 部署 jenkins
JS动画
- js的原生接口Animation
- 月影介绍的各种原生实现 当然会更灵活 因为本质就是代码实现数学公式QAQ 本来不想抄下来 但是据说要回收课程资格5555 抄一份当手册好了
通用的抽象接口 线性++:
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});
通用的抽象接口 角度 时间:
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})
通用的接口 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);
}
});
}
}
后面的具体例子就是数学公式结合前面的通用接口,找几个例子来记一下
贝塞尔轨迹:
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),
});
周期运动:实际上周期运动的timing发生了变化
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);
}
}
用promise来写连续运动Ticker 其实目标就是使用promise做流程控制
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);
}
});
});
}
}
下面就是上面promise trick的应用 小球弹跳
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)});
}
})();
前端性能优化
1. 性能评估方法
RAIL模型
关键指标:
- 100ms内响应用户输入 Response
- 10ms 动画一帧 Animation
- 主线程空闲时间最大化 Idle
- 1000ms交互内容展现 Load
chrome DevTools
2. 实际例子
针对浏览器渲染流程:页面是怎么样展示出来的?
JS->Style(这一步的输出是渲染树)->Layout(计算 产出盒模型)->paint(栅格化)->Composite(渲染层合并)
Layout 和 paint 是可以不出现的 所以会有各种场景
- 火焰图的使用
-
加载优化
- 资源效率优化
- 图片优化
- 字体优化
- 关键渲染路径优化
-
渲染优化
- JS执行优化
- 避免大型复杂布局
- 渲染层合并