My Little World

learn and share


  • 首页

  • 分类

  • 标签

  • 归档

  • 关于
My Little World

关于性能优化

发表于 2018-03-24

优化哪些方面

加载速度
呈现效果

图片的优化

常见图片有三类
矢量图:几何图形
位图:像素
交错图:随加载过程模糊到清晰

使用策略
·是否有必要使用图片(可不可以不使用;效果能不能用CSS代替–>套路大列表)
·使用合适的格式(小体积webp;矢量图>位图;内容jpeg,修饰png,动画video/svg>gif)

加载策略<提高用户体验>
·先加载小图onload之后再加载交错大图
·懒加载
·尽量减少http请求,合理的使用雪碧图和base64编码的图片

CssSprites:页面风格多变,经常换肤;无需重复图形内容;不会增加css文件体积
Base64:极小极简单图片,可复用,适用小图标(缺点:css文件体积增大,导致解析时间变长)

其他资源的优化

也适用于图片

·懒加载:图片;组件,模块
·预加载:提前加载下一页;异步加载,提前发送数据请求加载数据资源
·减小传输体积——压缩;减少传输数量——打包
·打包分块,资源复用
·加快速度——静态资源放CDN
·网速,服务器响应,CDN

提高首页/加载速度

·雪碧图
·js放底部,css放头部
·减少DOM数量
·script标签设置异步加载async
·减少DNS查找,允许高度并行下载;
·减少http请求
·设置http头部缓存字段expires,E-tag,last-modified;减少/不使用cookie

移动端性能优化

My Little World

关于监控

发表于 2018-03-19

为什么要做监控

对自己的网站做到错误提前预知和排查
统计自己的网站相关数据,对网站进行优化

监控什么

业务数据:
PV(PAGE VIEW,页面浏览量,一个用户打开了网站的几个页面)
UV(unique visitor,访问某个站点或点击某条新闻的不同IP地址的人数)
点击率
转化率(浏览人数与注册人数之间的比率)
跳出率(浏览与离开/不喜欢的人数之间的比率)

稳定性:
所提供服务是否稳定,能否得知偏远地区数据稳定性
网络挂断情况能否立即得知,而不是通过用户告知
异地双活(A地的机器中断工作,通过切换其他地方的机器来提供服务)

性能:
首屏打开的性能
……

错误:
用户端的错误上报

用户操作路径:
操作流程,方便跟踪错误

怎么进行监控

根据监控的对象/内容将监控的数据进行上报,然后根据上报寻找异常

PV/UV,业务操作的上报;
将页面性能数据进行上报; ======>huatuo
将页面产生的错误上报;========>badjs
跟踪用户操作,将操作路径/流程上报;
接口请求成功与否的上报
用户数据的上报

一般的上报方案是

1
2
3
var img = new Image
img.src = `${url}`
//url是上报的接口,上报的数据随接口给到后台

1.上报不需要返回值,Image请求不会返回值
2.可以自动跨域,拿不到返回值,上报也不需要确定是否上报成功

服务端拿到接口传过来的数据,将数据写到log文件中,
在需要时进行搜索统计,或者在定时/计划任务时进行查询

使用grep命令进行查找
bad.js使用

上报

业务数据上报

人工标上报点

对onclick等触发事件手动帮上上报函数

1
2
3
4
5
6
7

function report(data){
var url = '....'+data
var img = new Image
img.src = `${url}`
}
dom.addEventListener("click", report);

点击截获上报

通过事件冒泡和DOM上的标记找到对应行动点,获取具体操作对象DOM进行上报

百度统计/google analytics

通过发送带上COOKIE的Image请求来定位用户与站点信息,对整个页面做定位

若用户把cookie清除了怎么办?
1.尝试让用户啊登录
2.百度、谷歌一般有多个登录态,可以给没有cookie的用户先注册个临时ID,等登录后再映射回来
3.浏览器终端机器本身可以产生一些类似mac地址的唯一识别码的东西,根据识别码进行跟踪,
或者使用指纹采集,fingerprint.js等手段为用户生成指纹进行跟踪

其他方案

将log数据线存储的本地IndexDB等地方,必要时服务器发起收集;
基于DSL的切面事件(before,after),将监控事件可以自动注入,这样实现配置平台让产品也可以自定义上报

轻松理解AOP思想(面向切面编程)

如何利用业务数据

查看统计业务情况
检查系统稳定性
错误如何产生
是否被非法套嵌
检测性能优化程度

性能数据上报

查看真实用户页面打开情况
通过时间这个维度去看

打点上报

在指定位置标记一些时间点标识,然后进行统计

利用permance.timing

关于Performance API

如何利用性能数据

用户真实数据,如白屏时间,从产品/经验值看是否能接收,是否需要优化
对于某些地区的响应慢,是不是网络问题,是否推动网络或CDN优化
验证性能优化效果
找到一个参考值,通过流程化的方式让未来我们的项目都能达到一个基准线

稳定性监控

主要靠后端,前端做辅助

错误上报(bad.js)

为什么要上报

防止出现问题时,能不能及时得知,方不方便排查
偏远地区CDN出现故障,导致页面空白
边界条件未验证导致错误
后台返回结果不符合预期导致错误
用户产生了不可预知的操作

怎么捕获

使用window.error捕获错误,可以将将msg,url,row行,col列,error错误等信息拿到
但以上信息不能确定是那个文件出了错误,
在firefox,chrome中的堆栈信息中可以根据上面的error找到是哪个文件出了错误
所以在上报之前根据堆栈信息找到文件再上报

script error

一般是文件跨域出现的错误
出现的场景和条件如下
通过window.onerror注册监听脚本错误事件
浏览器是firefox,chrome,safari,ie7+
页面内使用script标签引入非同域资源,且发生脚本错误
解决
1.设置cors
将跨域资源的response header的Access-Control-Allow-Origin设置为或者本域,
(一般设为
,因为防止第三个域也来请求该文件时,本域请求完,response header有缓存,导致第三个域资源无法使用)
然后在script标签添加属性 crossorigin
2.对入口和异步方法全部用try-catch包裹,做到任何时刻错误都能捕获
setTimeout setInterval
事件绑定
ajax callback
define require
业务主入口
monitor

My Little World

计算dns时间

发表于 2018-03-17

计算dns时间

H5之前 facebook提出的多普勒测速

1
2
3
4
5
6
7
8
9
t1  http://a-doppler.facebook.com/test_pixel?HTTP1.0&t=1&size=0k
t2 http://a-doppler.facebook.com/test_pixel?HTTP1.1&t=2&size=0k
t3 http://a-doppler.facebook.com/test_pixel?HTTP1.1&t=3&size=0k
t4 http://a-doppler.facebook.com/test_pixel?HTTP1.1&t=4&size=10k

t1 = DNS + TCP/IP +RTT
t2 = TCP/IP + RTT
t3 = RTT
10k /(t4–t3)~TCP bandwidth

第一次请求使用HTTP1.0请求,保证后面的第二次请求可以重新建立TCP/IP链接
因为进行过第一请求,DNS已经在浏览器有缓存,所以进行第二次请求时直接在浏览器查找
所以t2-t1就是DNS查询时间

DNS = t1-t2;

第三次请求在第二次请求已经建立TCP/IP的情况下发起,所以

TCP/IP = t2-t3;

第四次请求只是在第三次基础上新增请求资源大小,利用这个大小,可以计算出大致贷款

bandwidth = 10k/(t4-t3)

H5之后 performance.timing

var time = window.performance.timing
DNStime = time.domainLookupEnd - time.domainLookupStart;

目前safari浏览器移动端均不支持,移动端Android不支持
performance.timing MDN

My Little World

为什么尽量将js放页面底部

发表于 2018-03-17

为什么尽量将js放页面底部

1,js是单线程的,一个时间只能干一件事
2.浏览器是多线程的,一个时候可以并行干多件事
3.一个浏览器打开一个页面,就是一个js线程
4.浏览器的多线线程一般会有:js引擎线程,界面渲染UI线程,浏览器事件触发线程,http请求线程等
5.当我们打开一个页面时,浏览器渲染html文件是从上往下渲染的,
UI引擎会根据html文件里面图片,css等的书写先后顺序依次去download,通过http请求拿到资源即加载结束
但如果碰到js文件,会将js文件先加载,加载结束后通知js引擎线程去执行
6.一旦js文件里面需要请求大量数据或者有对页面DOM的操作,就会造成页面空白或者由于DOM还没有加载完直接报错
7.为避免这样的情况,所以尽量将js放页面底部

哪些js可以不放在底部

1.操作在document.ready之后执行的
2.处理兼容性的文件,例如早期IE兼容H5新增标签的支持文件html5shiv
3.jquery类库,通常引用CDN地址,经压缩后不会很大,而且很大几率已被用户缓存下来

My Little World

四种规范

发表于 2018-01-21

common.js 规范

node.js的模块编程规范,适用于服务端,不能在浏览器使用
涉及到四个环境变量
module,exports,require,global
同步加载
require()引入
module.exports() 输出

AMD规范(require.js)

依赖在服务端,异步加载,提前加载所需模块
require([module], callback); 引入,等待所有依赖模块加载完后再执行回调函数
define() 输出return的内容

CMD规范(sea.js)

可异步加载,按需加载,不用提前加载
define 定义
export 输出
require 引入

es6规范

使用import 引入模块,需要bable转成require,浏览器才能运行
使用export 输出

链接
相关

My Little World

各种框架框架的数据绑定机制

发表于 2018-01-21

angular js

脏检查机制

react

虚拟DOM:react 在初始化的时候会render一颗dom树,当state/props发生改变的时候,render函数会再渲染出另外一棵树与之前的dom树进行对比,新渲染的树就是虚拟dom树
diff 算法:比较dom树时的算法,

只会对同层节点进行比较;
父节点不同,不会再去比较子节点;

同层比较时,遇到结点类型不同,比如结点顺序发生变化时
没有key属性的时候,自己以及自己以后的结点都会被删除,重新建;删除时,会卸载删除的结点以及以后的结点,再新建以后的结点
如果有key的话,则只会新增插入的结点,卸载被替换的结点 其他结点则只是update,不受影响;删除的话,只卸载删除的结点

遇到结点类型相同,
要去比较属性是否相同,如果不同,则只替换属性,只进行update

性能优化:避免不必要渲染
使用shouldComponentUpdate(),当这个方法返回true的时候,需要重新渲染,false的时候不需要(默认是true).
使用PureComponent组件,它会自动浅对比props/state,当两者相同的时候不渲染节点。
PureComponent只会浅比较,所以不适合用于深层嵌套的对象。
同时,PureComponent不仅仅会跳过自己的重新渲染,还会跳过它所有子节点的,所以要注意,用它的时候是最好没有子节点并且不依赖于global state的展示型组件。
reactlife

vue1.0

单检查

vue2.0

虚拟DOM和diff算法

vuelife

检测数据变更的4中方法

手动触发绑定
脏检查机制
数据对象劫持,使用Object.defineProperty
使用proxy

三大框架对比

1.数据检查更新原理不同
angularjs用脏检查
vue和react 使用diff算法
后两者效率更高

2.构建组件使用来说
angularjs更需要遵守框架规则,相对比较严谨
而vue次之,具备自己的响应式机制,所以要遵循一定的规则
最开放的是react,可以从更底层去定义自己想要的组件,
也可能跟react定位有关,一个用于构建UI的js库
它是一个库
库和框架的区别就是
库是,你调用库,作为开发者你有主动权
框架是你被框架调用,作为开发者你要听框架的
缺点就是数据改变自己要写监听逻辑

3.从使用场景上
angularjs和react都比较适合应用于大型网站开发
而vue小巧轻便,适合小项目开发

4.从学习成本上
vue最低,从用人角度来说最好找人
然后由于angular的规范严谨,导致需要学习的文档较多,
学习成本显而易见
react的开发角度相对以上两个框架更加向下,
有点像写早期模板语言,加上JSX语言的引入,
所以学习成本自然也是不低的

My Little World

模板引擎前后端优缺点

发表于 2018-01-04

模板引擎在后端
优点
在第一次请求时不需要发送请求数据的的HTTP,加载速度可能会快一些;
利于SEO;

缺点
前端模板有改动,后端对应的模板页面也要改动;
如果页面有复杂JS,前端因为没有数据不方便调试,后端需要使用js进行修改;
服务器负载压力大

模板引擎在前端
优点
前后端分离,后台只需要处理逻辑业务,提供接口,减少服务端压力;
前端修改方便;
可跨平台,兼容不同后端技术

缺点
不利于SEO(搜索引擎无法抓取页面的数据,因为只是模板,没有数据内容)
JS有可能被用户禁用,数据安全性低

My Little World

ng-src

发表于 2018-01-04

ng-src

img 里面使用src=”{ { } }“会在页面一开始加载模板的时候,不会执行{ { } },直接去请求资源,造成404错误
如果使用ng-src = “{ { } }“就会避免以上问题,ng-src会先去执行{ { } },得到资源地址后再去请求资源
ng-src 指令确保的 AngularJS 代码执行前不显示图片。

My Little World

service worker

发表于 2018-01-03

前端与后台代理服务器

主要方法

fetch 事件/方法:service worker向后台发起HTTP请求的方法
fetch(url).then()

message事件/方法:页面和service worker之间进行通信的方法;同一页面两个窗口之间通信(web worker线程)
postMessage发送消息;message接收消息

caches API:处理缓存

生命周期

注册 register
安装 install
激活 activation
更新 Update
service-worker.js文件更新被浏览器发现后,会进入waitting等待状态,当前页面依旧使用旧文件进行work,
直到当前页面关闭,旧版本文件的service worker才会被kill掉,新版本文件的service worker开始接管页面的缓存资源
新的service worker一开始接管就会触发activate事件,从而可以做一些上次老版本缓存的工作

缓存的文件发生更新,可以在fetch 的时候,一边检查更新从而更新缓存,一边检查缓存然后fetch新资源给浏览器,
通过 promise 的 resolve 特性来决定谁快

应用场景

(https环境)
后台消息传递
网络代理,转发请求,伪造响应
离线缓存
消息推送

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
//在项目入口/配置js文件 进行注册
if (navigator.serviceWorker) {
navigator.serviceWorker.register('service-worker.js').then(function(registration) {
console.log('service worker 注册成功');
}).catch(function (err) {
console.log('servcie worker 注册失败')
});
}

//service-worker.js 放在项目根目录下,service worker的执行代码
var cacheFiles = [ //指定缓存的文件
'about.js',
'blog.js'
];
self.addEventListener('install', function (evt) { //在安装的时候就将指定文件存入cache storage中
evt.waitUntil(
caches.open('my-test-cahce-v1').then(function (cache) { //使用caches api进行缓存操作
return cache.addAll(cacheFiles);
})
);
});
//页面fetch 资源时进行过滤
self.addEventListener('fetch', function(event) {
event.respondWith(
caches.match(event.request) //检查缓存是否有资源
.then(function(response) {
if (response) { //如果有直接返回
return response;
}

// 因为 event.request 流已经在 caches.match 中使用过一次,
// 那么该流是不能再次使用的。我们只能得到它的副本,拿去使用。
var fetchRequest = event.request.clone();

// fetch 的通过信方式,得到 Request 对象,然后发送请求
return fetch(fetchRequest).then(
function(response) {
// 检查是否成功
if(!response || response.status !== 200 || response.type !== 'basic') {
return response;
}

// 如果成功,该 response 一是要拿给浏览器渲染,而是要进行缓存。
// 由于 caches.put 使用的是文件的响应流,一旦使用,
// 那么返回的 response 就无法访问造成失败,所以,这里需要复制一份。
var responseToCache = response.clone();

caches.open(CACHE_NAME)
.then(function(cache) {
cache.put(event.request, responseToCache);
});

return response;
}
);
})
);
});

//service-worker.js文件更新删除之前版本文件缓存的资源

self.addEventListener('activate', function(event) {

var cacheWhitelist = ['v1'];

event.waitUntil(
// 遍历 caches 里所有缓存的 keys 值
caches.keys().then(function(cacheNames) {
return Promise.all(
cacheNames.map(function(cacheName) {
if (cacheWhitelist.includes(cacheName)) {
// 删除 v1 版本缓存的文件
return caches.delete(cacheName);
}
})
);
})
);
});

相关文档
fetch 通信
web worker 语法
Service Worker API

遗留问题:fetch通信headers添加token字段,不生效

My Little World

调试

发表于 2018-01-03

fiddler 使用

浏览器向服务器发送请求的拦截器

statistic

查看一次请求各阶段所发生的时间,HTTP performance
fiddler5

Inspectors

查看报文,上半部分是request,下半部分是response
fiddler4

AutoResponder

拦截本来要发给真正服务器的指定请求,将本地资源作为response返回去
fiddler3
1.开启规则
2.添加规则
3.指定请求,若以EXACT:开头,要写完整的请求路径;模糊匹配的话就不要写EXACT:
4.指定作为response的本地路径资源地址
5.确认规则配置完毕

composer

自己创建request 向服务器发送请求
fiddler2
配置好request后,点击excute按钮手动向服务器发送请求

Filters

只拦截指定的请求,其他请求不拦截,指定多个时用逗号隔开
fiddler1
填写指定请求时,编辑框为黄色,为未保存状态,点击右上角‘Changes not yet saved’确认配置完毕

谷歌浏览器调试常用

将respose进行json格式转换

1.右键接口选择copy->copy response;
2.在console界面执行copy()函数
copy( ctrl+v ) 回车
3.打开编辑器,新建一个文件,ctrl+v,就得到response 的json格式
debug1

console使用

  1. console.table(obj):将json数据以表格形式展示在控制台
    例

    1
    2
    3
    4
    5
    6
    7
    var animals = [
    { animal: 'Horse', name: 'Henry', age: 43 },
    { animal: 'Dog', name: 'Fred', age: 13 },
    { animal: 'Cat', name: 'Frodo', age: 18 }
    ];

    console.table(animals);
  2. 使用 console.time() 和 console.timeEnd() 对循环做基准测试
    例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    console.time('Timer1');

    var items = [];

    for(var i = 0; i < 100000; i++){
    items.push({index: i});
    }

    console.timeEnd('Timer1');

最后会得到循环所用时间
Timer1:xxxxx ms

  1. 使用console.trace 跟踪调用栈
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    var car;
    var func1 = function() {
    func2();
    }

    var func2 = function() {
    func4();
    }
    var func3 = function() {
    }

    var func4 = function() {
    car = new Car();
    car.funcX();
    }
    var Car = function() {
    this.brand = 'volvo';
    this.color = 'red';
    this.funcX = function() {
    this.funcY();
    }

    this.funcY = function() {
    this.funcZ();
    }

    this.funcZ = function() {
    console.trace('trace car')
    }
    }
    func1();
    结果如下:
    trace car
    Car.funcZ @ VM524:28
    Car.funcY @ VM524:24
    Car.funcX @ VM524:20
    func4 @ VM524:14
    func2 @ VM524:7
    func1 @ VM524:3
    (anonymous) @ VM524:31

调试js

在当前页面的js里面修改或添加代码后,ctrl+s 然后在页面触发更改的代码,即可进行调试
但要注意需要刷新才能执行到的代码,不能用这种方式进行调试

断点

直接在代码序号上进行点击即添加了断点
取消断点就再点击断点
退出断点调试就点右上角
debug2
禁用断点
debug3
如果想知道当前断点上的变量值,只需要将鼠标放在变量上即可
相关链接
相关链接

1…141516…26
YooHannah

YooHannah

260 日志
1 分类
23 标签
RSS
© 2025 YooHannah
由 Hexo 强力驱动
主题 - NexT.Pisces