什么是前端工程化?:https://blog.csdn.net/qq_36934866/article/details/105458505
03 组件化
組件化和模块化的核心思想都在于分治,实际带来的好处就是团队协作效率和项目可维护性的提升。
什么是组件
-
UI为主:
页面上的个UI块可以封装成一个组件。
-
逻辑为主:
某一个功能逻辑也可以封装成一个组件。
在WEB前端领域,我们可以将由特定逻辑和UI进行的高内聚,低耦合的封装体称为一个组件。
侧重UI进行封装的组件:代码结构清晰,组件内的模块就近放置,方便进行修改和维护。这种组件具备高内聚低耦合,但普适性不高。
侧重逻辑进行封装的组件:除了具备上述有点外,还具有很高的普适性。
组件内可以包含组件:比如偏UI的组件往往都是包含有逻辑的组件。
04 自动化
核心思想:能由机器自动完成的事情,绝不让人来做。自动化是前端工程化的核心。
自动初始化:vue-cli
自动构建:webpack
自动测试:karma, jest
自动部署: Jenkins
自动化测试
自动化部署
自动化构建
WEBPACK PARCEL
实例:360搜索专题页开发工具
写一个基于Node.js的CLI
如何捕获用户输入的是参数和命令,获得参数并出发回调?
const program = require('commander');
program.on('--help', =>{});
program.command('init').action((name, option) => {});
如何出发询问与用户交互?
const inquirer = require('inquirer');
inquirer.prompt({
type: 'confirm',
name: 'name',
message: '是否将产品发布至线上?',
default: true
}).then(answer => {...do sth...})
如何帮我们执行命令?如何帮我们发送HTTP请求?
const child_process = require('child_process');
const HTTP = require('http');
如何增强交互效果?
const chalk = require('chalk');
console.log(chalk.redBright('专题名称已被使用,请重新输入'));
const ora = require('ora');
const spinner = ora('正在加载中').start();
setTimeout(_=> {
spinner.text = '加载完成';
spinner.succeed();
}, 1000)
使用webpack4进行项目构建
module.exports = {
mode: 'development',
entry: {
main: './src/main/js'
},
output: {
path: path.resolve(__dirname, '../dist'),
filename: '[name].js'
},
module: {
rules: []
},
plugins: []
}
![4.png](https://upload-images.jianshu.io/upload_images/22890329-591f247e460ea30e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
前端动画
https://blog.csdn.net/xinyu_huo/article/details/105458676
椭圆轨迹:
代数方程:
参数方程:
周期运动:
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});
})();
线性插值(lerp):
f(p) = from + (to - from) * p
f(p) = to * p + from * (1 - p)
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)});
}
})();
弹跳的小球2:
(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();
逐帧动画
Web Animation API(Working Draft)
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',
});
}());
前端性能优化
前端性能优化有什么好处
和用户体验相关;影响用户转化率。
我们的目的是通过前端优化发现性能瓶颈,提升用户体验。
然后开始思考:如何做前端性能优化?
RAIL模型
使用RAIL模型评估性能
以用户为中心的性能模型,每个网络应用都有与其生命周期有关的四个方面,而且这些方面以不同的方式影响着性能。
延迟与用户反应
响应:50ms处理事件
目标:在100ms内响应用户输入
指导:
50ms内处理用户输入事件,确保100ms内反馈用户可视的响应
对于开销大的任务可分隔任务处理,或放到worker进程中执行,避免影响用户交互
处理事件超过50ms的操作,始终给予反馈(进度和活动指示器)
为什么预算只剩下50ms?
动画:10ms一帧
目标:10ms或更短的事件内生成一帧
10ms = (1000ms / 60frames ≈ 16.67 ms per frame)- 6ms(渲染的预算)
指导:
在动画这样的高压点,尽量不要处理逻辑,提高达到60fps的机会
动画类型:① 滚动 ② 视觉动画 ③ 拖曳动画
空闲时间最大化
目标:
最大化空闲时间以增加页面在100ms内响应用户输入的几率
指导:
利用空闲时间完成推迟的工作(预加载数据)
空闲时间期间用户交互优先级最高
加载:5s呈现交互内容
目标:
首屏加载连接缓慢的移动设备,5s内呈现可交互内容
非首屏加载应该在2s内完成
指导:
测试用户常用设备和网络连接情况的性能
优化关键渲染路径以接触阻止渲染
启用渐进式渲染和在后台执行一些工作
影响加载性能的因素:① 网络速度 ② 硬件 ③ 解析 JavaScript
评估性能的工具
Lighthouse
已经集成到Chrome浏览器,可以测试不同终端、运行性能、SEO等
WebPageTest
可以返回测试报告,检测网页性能
Chrome DevTools
实战演练环节会告诉你哒!
实战环节
浏览器渲染场景
csstriggers.com
:查看不同css属性对渲染流程的影响
JavaScript:实现动画,操作DOM
Style:生成 Render Tree(DOM树+CSSOM树)
Layout:盒模型,表征确切的位置和大小
Paint:栅格化,生成绘制消息
Composite:渲染层合并,交由GPU处理
绘制和布局不是必要的流程。比如 transform / opacity 只影响 composite。
js尽量少用全局变量;
多个js变量声明合并;
不使用eval函数,不安全,性能消耗严重;
使用事件代理绑定事件,如将事件绑定到body上进行代理(利用冒泡原理将事件加到父级上,能够给动态增加的元素进行数据绑定);
避免频繁的操作DOM节点,使用innerHTML代替;
减少对象查找,如a.data.box1.name的查找方式非常耗性能,尽可能的将它定义在变量里;
动画场景
主要是动画不卡顿,下面是一个优化的实际例子
面板的功能:console控制台;source源代码面板;Network显示一些请求;performance性能优化
指标的性能评分;通过screenshot对屏幕进行录制
打开主函数线程,会出现很多任务,在update一栏,带红∠的会有图形计算和布局的,点开后会有sumery可以提升的地方会链接到代码地方。