Mobile performance optimization
Introduction to mobile performance optimization
What is performance
- Page response speed
Page response speed
-
Open the page to the actual use of the time
- Network request time
- Page load and render time
-
How smoothly you interact with the page
- The execution speed of Javascript scripts
Load the first screen of a page faster, without thinking about the entire page
Why optimize performance on mobile
- Improve user experience
- Compared with the PC, the network speed of the mobile terminal is slow
- The performance of mobile devices is lower than that of PCS
Learn what
- Performance optimization points at each stage
- Specific performance optimization policies
Optimization points in network request process
Chrome Dev Tool timeline represents the meaning of each stage
The amount of time before a request can be sent. Contains the time used to process the agent.
In addition, if there are already established connections, this time also includes the time it takes to wait for the established connections to be reused, which follows Chrome’s rule of up to 6 TCP connections from the same source.
Proxy Negotiation
- The time of negotiation with the proxy server connection.
DNS Lookup
- Time used to perform DNS lookup. Each new field on the page requires a full round-trip DNS lookup.
Initial Connection / Connecting
- Time spent establishing links, including TCP and multi-attempt handshakes, and processing SSL.
SSL
- Time when the SSL handshake is complete.
Request Sent / Sending
- The time when the request was initiated
Waiting (TTFB)
- Time To First Byte after a request is sent To the First Byte of the response received
Content Download / Downloading
- Time used to download the response
Multiple resources are distributed on different domains to reduce the waiting time of the request queue
- Browsers allocate a limited number of concurrent channels per domain name
- Multiple domains mean more DNS query time, and it is usually appropriate to split domain names into three to five
Reduce the DNS query time by using dns-prefetch
- Try resolving the domain name before requesting the resource
- This is valid only for cross-domain DNS lookup
- Do not add dns-prefetch to a resolved domain name
Reduce the number of HTTP requests
- Merge resources (merge CSS, JS files)
- Static resource caching
- Static resources are suffixed with hash to calculate the hash based on the file content
- If the file content does not change, the hash and URL will not change
- If the URL and file are unchanged, the HTTP cache mechanism is automatically triggered and 304 is returned
- Inline the first screen related code
- Use caching (browser caching, localStorage, etc.)
- Use CDN to make resources load faster
<! -- <link rel="stylesheet" href="./css/reset.css" />
<link rel="stylesheet" href="./css/base.css" />
<link rel="stylesheet" href="./css/index.css"/ > -- > <! -- <link rel="stylesheet" href="./css/index.css"/ > -- > <! --1. The combined resource cannot be too large --> <! --2<link rel="stylesheet" href="./ CSS /common.css" />
<link rel="stylesheet" href="./css/index.css" />
Copy the code
/ / use the < scriptsrc="https://cdn.bootcss.com/zepto/1.0rc1/zepto.min.js"></script>
Copy the code
Reduce the size of the requested resource
- Resource compression (HTML, CSS compression and JS compression and obfuscation)
- Enable Gzip compression
- Reduce Cookie size
Using HTTP2
- Multiplexing: The most valuable advantage is that it solves the problem of line header blocking and can send N requests in parallel in the browser.
- Bow compression: smaller load volume.
- New binary format: HTTP1. x is for text format transport and HTTP2 is for binary format transport.
- Server push: The server can actively push resources to the client.
Optimization points during page loading and rendering
- CSS is typically introduced in head
- JavaScript is usually introduced at the end of the body
- Reduce Reflow/Relayout and Repaint
- When the size, position, hiding, and other attributes of an element change, the browsing needs to be recalculated, which is called backflow
- When the appearance, style and other attributes of an element change, the browser only needs to redraw, which is called redraw
- Backflow always causes redrawing, and redrawing does not necessarily cause backflow
Optimization points in JavaScript scripts
- DOM manipulation optimization
- Events to optimize
- Lazy loading and preloading of images
Image optimization
Reduce the number of HTTP requests
- Use CSS for drawing (animation) instead of simple images (www.webhek.com/post/40-css…)
- Merge small ICONS (CSS Sprites)
- Embed small ICONS in HTML (Base64 images)
Reduce the size of the requested resource
- Use icon fonts instead of simple ICONS
- The compressed image
- Choose the right image size
- Choose the right type of picture
Image type
- jpg
- Lossy compression, high compression rate, does not support transparency
- Suitable for scenes with rich colors, gradients and no need for transparent images
- png
- Png-8 256 color + supports transparency
- Png-24 2^24 color + does not support transparency
- Png-32 2^24 color + support transparency
- Suitable for most scenarios where transparent images are required
- webp
- Compared with PNG and JPG, the image is smaller under the same visual experience
- Support lossy compression, lossless compression, transparency and animation
- In theory, it can completely replace PNG, JPG, GIF image format
- There are some compatibility issues
Animation optimization
- CSS3 transitions and animations are preferred
- Exercise with Translate3D is preferred
- Use requestAnimationFrame when you must animate in JavaScript
<! DOCTYPEhtml>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="Width = device - width, initial - scale = 1.0" />
<title>Animation optimization</title>
<style>
* {
padding: 0;
margin: 0;
}
.mask {
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: rgba(0.0.0.0.5);
opacity: 1;
0.5 s / * transition: opacity; * /
}
</style>
</head>
<body>
<div id="mask" class="mask"></div>
<script>
// Use Js with CSS3 to animate the disappearing element
const $mask = document.getElementById('mask');
$mask.addEventListener(
'click'.function () {
$mask.style.opacity = 0;
},
false
);
$mask.addEventListener(
'transitionend'.function () {
$mask.style.display = 'none';
},
false
);
</script>
</body>
</html>
Copy the code
- Use JS requestAnimationFrame to do this
window.requestAnimationFrame()
You need to pass in as an argument a callback function that will be executed once before the browser’s next redraw.
$mask.addEventListener(
"click".function () {
// setTimeout(fadeOut, 20);
requestAnimationFrame(fadeOut);
},
false
);
let opacity = 1;
function fadeOut() {
opacity -= 0.05;
if (opacity <= 0) {
opacity = 0;
$mask.style.display = "none";
} else {
requestAnimationFrame(fadeOut);
}
$mask.style.opacity = opacity;
}
Copy the code
CSS optimization
Selector optimization
- Don’t use too many nested and complex selectors. Keep it simple and choose directly from styles. Don’t overdo it.
- Avoid too many wildcard selectors
- Removes unmatched styles
/* The worst way */
ul li a {
text-decoration: none;
}
ul.list li.list-item a.list-link {
text-decoration: none;
}
/* A better way */
.list-link {
text-decoration: none;
}
/* Avoid excessive wildcard selectors */
/* A small amount can */
* {
padding: 0;
margin: 0;
}
/* 1.4. remove unmatched styles */
.list{}Copy the code
Other optimization
- Extract common part
- Avoid using CSs@import to import CSS
/* Extract the common part */
/* The worst way */
ol {
padding: 0;
margin: 0;
}
p {
padding: 0;
margin: 0;
}
/* A better way */
ol.p {
padding: 0;
margin: 0;
}
<syule>
/* Avoid using CSS @import to send extra HTTP requests (@import in less sass can be used because it does not send extra requests as opposed to copying code to the current page)*/
@import "./reset.css";
</style>
Copy the code
Short for CSS color property value
/* not recommended */
.box{ color:# 000000; background-color:#ddeeff; }
Recommend * / / *
.box{ color:# 000; background-color:#def; }
Copy the code
Delete the CSS unit whose property value is 0
0 is 0, you don’t need any units, as long as the first number is 0, you can get rid of all the units.
/* not recommended */
.box{ margin:0px; padding:0px; }Recommend * / / *
.box{ margin:0; padding:0; }Copy the code
DOM optimization
Rendering optimization
-
Reduce the number of DOM elements and nesting levels
-
Try to avoid using a table layout and use other labels instead
-
The table is parsed as a whole, and only small changes can be displayed until the whole table is parsed, which may cause the whole table to be rearranged
-
table { width: 100%; border-collapse: collapse; } th, td { border: 1px solid #ccc; text-align: center; } <table> <tr> <th>The name</th> <th>age</th> <th>gender</th> </tr> <tr> <td>Zhang SAN</td> <td>18</td> <td>male</td> </tr> <tr> <td>Li si</td> <td>20</td> <td>female</td> </tr> </table> Copy the code
-
JS selector optimization
-
Ids are preferred for retrieving individual elements
-
console.log(document.getElementById('box')); / / recommend console.log(document.querySelector('#box')); / / do not recommend Copy the code
-
-
When you get multiple elements, try to get them directly from the className of the element itself
-
console.log(document.querySelectorAll('ul.list li.item')); / / do not recommend console.log(document.getElementsByClassName('item')); / / recommend console.log(document.querySelectorAll('.item')); / / recommend Copy the code
-
Reduce DOM operations
-
Always cache the result of a selector’s selection
-
// Always cache the result of the selector const $list = document.getElementById('list'); Copy the code
-
-
Avoid using innerHTML multiple times in the loop. Use it once after the loop ends
-
const $list = document.getElementById('list'); const todoDatas = ['Laundry'.'cooking'.'Write code']; // Error for (const item of todoDatas) { $list.innerHTML += `<li class="item">${item}</li>`; } The correct way to write it is to avoid using innerHTML multiple times in the loop, but only once after the loop is complete let html = ' '; for (const item of todoDatas) { html += `<li class="item">${item}</li>`; } $list.innerHTML = html; Copy the code
-
-
A newly created element that is added to the page after the necessary operations are completed
-
for (const item of todoDatas) { const $li = document.createElement("li"); // The newly created element is added to the page after the necessary operations are completed $li.className = "item"; $li.innerHTML = item; $li.style.color = "pink"; $list.appendChild($li); } Copy the code
-
-
Multiple appendChild optimizations using DocumentFragment
-
// Optimize multiple appendChild with DocumentFragment const $liFragment = document.createDocumentFragment(); for (const item of todoDatas) { const $li = document.createElement('li'); $li.className = 'item'; $li.innerHTML = item; $li.style.color = "pink"; $liFragment.appendChild($li); } $list.appendChild($liFragment); Copy the code
-
-
Do not change the style of an element directly with JS. Change the style of an element by adding and removing classes
-
<style> .box { width: 100px; height: 100px; background-color: pink; } .active { width: 200px; height: 200px; background-color: green; } </style> const $box = document.getElementById("box"); // let active = false; $box.addEventListener( "click".() = > { if(! active) { active =true; // $box.style.width = '200px'; // $box.style.height = '200px'; // $box.style.backgroundColor = 'green'; $box.classList.add("active"); } else { active = false; // $box.style.width = '100px'; // $box.style.height = '100px'; // $box.style.backgroundColor = 'pink'; $box.classList.remove("active"); } $box.classList.toggle("active"); }, false ); Copy the code
-
-
Attention to forced reflux
- When the obtained attribute values include but are not limited to “global attributes” such as offsetTop, offsetLeft, scrollTop, and clientTop, the layout and style of other elements on the page need to be up to date, which will cause multiple reflows and redraws. Such an operation is called forced backflow
- Gist.github.com/paulirish/5…
- The results can be cached and updated as needed
<! DOCTYPEhtml>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Document</title>
<style>
.backtop {
position: fixed;
right: 20px;
bottom: 20px;
width: 90px;
height: 90px;
line-height: 90px;
text-align: center;
background-color: rgba(0.0.0.0.6);
border-radius: 50%;
color: #fff;
font-size: 60px;
text-decoration: none;
-webkit-tap-highlight-color: transparent;
}
.none {
display: none;
}
</style>
</head>
<body style="height: 5000px;">
<a href="#" id="backtop" class="backtop">↑</a>
<script>
// Pay attention to forced backflow
const $backtop = document.getElementById("backtop");
// Recalculate when the cache window changes
let winHeight = window.innerHeight;
window.addEventListener(
"resize".() = > {
winHeight = window.innerHeight;
},
false
);
window.addEventListener("scroll", scrollHandler, false);
function scrollHandler() {
// console.log('scroll');
// It is not recommended that both attributes cause forced backflow
// We can handle window.innerheight
// if(document.documentElement.scrollTop > window.innerHeight)
if (document.documentElement.scrollTop >= winHeight) {
$backtop.classList.remove("none");
} else {
$backtop.classList.add("none"); }}</script>
</body>
</html>
Copy the code
The event agent
What is an event broker implementation
What is event broker
- Also called event delegate, delegates events that are listened for on child elements to the parent element, allowing the parent element listener to take advantage of event bubbling
Implementation of the event broker
<! DOCTYPEhtml>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>The event agent</title>
<style>
* {
box-sizing: border-box;
}
body {
background-color: #f5f5f5;
}
.input {
width: 100%;
height: 40px;
border: 1px solid #ccc;
margin-bottom: 20px;
font-size: 20px;
}
.list {
padding: 0;
margin: 0;
}
.item {
display: flex;
justify-content: space-between;
padding: 0 10px;
margin-bottom: 10px;
background-color: #fff;
font-size: 40px;
}
.del {
text-decoration: none;
}
</style>
</head>
<body>
<input type="text" id="input" class="input" placeholder="Please enter your to-do list" />
<ul class="list" id="list">
<li class="item">Wash the clothes<a href="javascript:;" class="del">x</a></li>
<li class="item">Cook a meal<a href="javascript:;" class="del">x</a></li>
<li class="item">Write the code<a href="javascript:;" class="del">x</a></li>
</ul>
<script>
const $input = document.getElementById('input');
const $list = document.getElementById('list');
// Use event delegate to parent element performance cost principle: bubble
$list.addEventListener(
'click'.evt= > {
// console.log('click');
// console.log(evt.target);
if (evt.target.classList.contains('del')) { $list.removeChild(evt.target.parentNode); }},false
);
$input.addEventListener(
'keypress'.evt= > {
// console.log(evt);
if (evt.keyCode === 13) {
/ / return
if(! $input.value)return;
const $item = document.createElement('li');
const $del = document.createElement('a');
$item.className = 'item';
$del.className = 'del';
$del.href = 'javascript:; ';
$item.innerHTML = $input.value;
$del.innerHTML = 'x';
// Each creation consumes the performance of the a tag binding event function
// $del.addEventListener(
// 'click',
// () => {
// $list.removeChild($item);
/ /},
// false
// );
$item.appendChild($del);
$list.appendChild($item);
$input.value = ' '; }},false
);
</script>
</body>
</html>
Copy the code
Event dilution
What is event dilution
- Event dilution is reducing the frequency of events that fire multiple times over a period of time
Method of event dilution
-
Image stabilization
- Execute the callback n seconds after the event is triggered, and if it is triggered again within n seconds, re-time (cast time)
-
The throttle
-
Only execute the function once in a while (cooldown)
-
<! DOCTYPEhtml>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="Width = device - width, initial - scale = 1.0" />
<title>Event dilution</title>
<style>
.backtop {
position: fixed;
right: 20px;
bottom: 20px;
width: 90px;
height: 90px;
line-height: 90px;
text-align: center;
background-color: rgba(0.0.0.0.6);
border-radius: 50%;
color: #fff;
font-size: 60px;
text-decoration: none;
-webkit-tap-highlight-color: transparent;
}
.none {
display: none;
}
</style>
</head>
<body style="height: 2000px">
<a href="#" id="backtop" class="backtop none">↑</a>
<script>
// What event needs to be diluted
// Scroll resize mousemove TouchMove etc
// window.addEventListener('scroll', handler, false);
// window.addEventListener('resize', handler, false);
// window.addEventListener('mousemove', handler, false);
// window.addEventListener('touchmove', handler, false);
// function handler(evt) {
// console.log(evt.type);
// }
// window.addEventListener('scroll', debounce(scrollHandler), false);
window.addEventListener("scroll", throttle(scrollHandler), false);
const $backtop = document.getElementById("backtop");
let winHeight = window.innerHeight;
window.addEventListener(
"resize",
debounce(() = > {
winHeight = window.innerHeight;
console.log(winHeight);
}),
false
);
function scrollHandler() {
console.log("scroll");
if (document.documentElement.scrollTop >= winHeight) {
$backtop.classList.remove("none");
} else {
$backtop.classList.add("none"); }}/ / stabilization debounce
function debounce(fn, miliseconds = 250, context) {
let timer = null;
// call debounce to generate function a(name optional)
return function (. args) {
const self = context || this;
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(() = > {
fn.apply(self, args);
timer = null;
}, miliseconds);
};
}
/ / throttling throttle
function throttle(fn, miliseconds = 250, context) {
let lastEventTimestamp = null;
return function (. args) {
// Specify that this is either passed in or this of the current return function
const self = context || this;
// Record the current timestamp
const now = Date.now();
// If the timestamp is false or the current time - previous time >= specified time, the statement execution code is entered
if(! lastEventTimestamp || now - lastEventTimestamp >= miliseconds) {// Update the previous time value
lastEventTimestamp = now;
// Execute the incoming function binding this to pass in argsfn.apply(self, args); }}; }</script>
</body>
</html>
Copy the code
// Simulate an Ajax request
function ajax(content) {
console.log('ajax request ' + content)
}
let inputc = document.getElementById('throttle')
let throttleAjax = throttle(ajax, 1000)
// let debounceAjax = debounce(ajax, 500)
inputc.addEventListener('keyup'.e= > {
throttleAjax(e.target.value)
//debounceAjax(e.target.value)
})
Copy the code
Review the previous return to the top show and hide implementation
The scrollHandler function takes no arguments and does not use this, so when we call debounce, we pass only one argument.
Now let’s rewrite the scrollHandler function using arguments and this
We replaced the show and hide threshold with the parameter threshold and $backtop with this.
So we want to call debounce to pass threshold to scrollHandler and set this to $backtop.
Here we call debounce with a third argument, context, specifying that this in the scrollHandler is $backtop.
Debounce returns a function called a, which calls bind() and returns a function. The second argument to addEventListener requires a function, so calling bind is ok.
As the first argument to bind(), we can specify that function A points to this. Since we passed context ($backtop), we can pass null here.
The bind() method starts with the second argument and can pass arguments to function A, which args receives. Here we pass winHeight (the current viewport height) as the threshold.
So far, we have demonstrated the use of args and context. The throttling function is similar to the anti – shake function.
Lazy loading of images
What is picture lazy loading
- Lazy image loading is also called lazy image loading (on demand)
- Load images as needed
- Better load the first screen of the page without considering the entire page
Lazy image loading implementation
<! DOCTYPEhtml>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="Width = device - width, initial - scale = 1.0" />
<title>Lazy loading of images</title>
<style>
* {
padding: 0;
margin: 0;
}
img {
width: 100%;
height: 183px;
}
</style>
</head>
<body>
<div class="img-container">
<img
src="./images/loading.gif"
alt=""
data-src="./images/1.jpg"
class="lazyload"
/>
</div>
<div class="img-container">
<img
src="./images/loading.gif"
alt=""
data-src="./images/2.jpg"
class="lazyload"
/>
</div>
<div class="img-container">
<img
src="./images/loading.gif"
alt=""
data-src="./images/3.jpg"
class="lazyload"
/>
</div>
<div class="img-container">
<img
src="./images/loading.gif"
alt=""
data-src="./images/4.jpg"
class="lazyload"
/>
</div>
<div class="img-container">
<img
src="./images/loading.gif"
alt=""
data-src="./images/5.jpg"
class="lazyload"
/>
</div>
<script>
// The height of the image should be set
const imgs = [...document.querySelectorAll(".lazyload")];
lazyload();
window.addEventListener('scroll', lazyload, false);
function lazyload() {
for (let i = 0; i < imgs.length; i++) {
const $img = imgs[i];
if (isInVisibleArea($img)) {
$img.src = $img.dataset.src;
imgs.splice(i, 1); i--; }}}// Whether the DOM element is visible
function isInVisibleArea($el) {
const rect = $el.getBoundingClientRect();
// console.log(rect);
return (
rect.bottom > 0 &&
rect.top < window.innerHeight &&
rect.right > 0 &&
rect.left < window.innerWidth
);
}
</script>
</body>
</html>
Copy the code
- Add anti-shake and throttling to optimize performance
window.addEventListener("scroll", debounce(lazyload), false);
/ / not applicable
// window.addEventListener('scroll', throttle(lazyload), false);
/ / stabilization debounce
// The event handler is executed only once during a certain time period
function debounce(fn, miliseconds = 250, context) {
let timer = null;
return function (. args) {
const self = context || this;
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(() = > {
fn.apply(self, args);
timer = null;
}, miliseconds);
};
}
/ / throttling throttle
// The event handler stops working for a certain period of time after it executes once
function throttle(fn, miliseconds = 250, context) {
let lastEventTimestamp = null;
return function (. args) {
const self = context || this;
const now = Date.now();
if (!lastEventTimestamp || now - lastEventTimestamp >= miliseconds) {
lastEventTimestamp = now;
fn.apply(self, args);
}
};
}
Copy the code
Image preloading
What is image preloading
- Preload images that may be used in the future
Image preloading implementation
<! DOCTYPEhtml>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="Width = device - width, initial - scale = 1.0" />
<title>Image preloading</title>
<style>
.img-container {
display: flex;
align-items: center;
height: 100vh;
background-color: rgba(0.0.0.0.5);
}
img {
width: 100%; {} *margin: 0;
padding: 0;
}
</style>
</head>
<body>
<div class="img-container">
<img src="./images/1.jpg" alt="Image" id="img" />
</div>
<script>
const imgs = [
'./images/2.jpg'.'./images/3.jpg'.'./images/4.jpg'.'./images/5.jpg'
];
let i = 0;
const $img = document.getElementById('img');
// The page starts with a call to preload to load the first element of the array
preload(imgs[i])
.then(data= > {})
.catch(() = > {});
// Click toggle
$img.addEventListener(
'click'.() = > {
// When the index is less than array length
if (i < imgs.length) {
// Assign the SRC of the array element to the page element
$img.src = imgs[i];
// the next click of I +1 increments the second element of the array
i++;
// When the index is less than array length
if (i < imgs.length) {
// Preload the next imagepreload(imgs[i]); }}else {
// If the index is the same as the array length, there are no elements in the array
console.log('It's the last one! '); }},false
);
/ / preload
function preload(src) {
// Promise is wrapped
return new Promise((resolve, reject) = > {
// Create a new image label
const image = new Image();
// The image is successfully loaded
image.addEventListener('load'.() = > resolve(image), false);
// Image loading failed call failed state
image.addEventListener('error'.() = > reject, false);
// Assign the passed SRC value to the new image
image.src = src;
});
}
</script>
</body>
</html>
Copy the code
Extension: Why is opening the page faster the second time
-
The first time the page is loaded, some data is cached, and later loads are fetched directly from the cache without requesting the server, so it is faster and less stressful for the server
-
The network cache is divided into three parts: DNS cache, HTTP cache (browser cache), and CDN cache
-
Local storage and offline storage can improve the loading speed of the first screen
DNS cache
- When the page is displayed, the system performs DNS query, finds the IP address of the server corresponding to the domain name, and sends a request
- DNS domain name lookup is performed on the client first
Recursive query
- Finding at any step ends the lookup process, and the client only issues a query once during the whole process
- If the forwarder configured on the DNS server is not configured, the forwarder is forwarded to the DNS server
13
Initiate a parsing request, as shown hereIterative query
As shown in figure
13 root: There are 13 root domain server IP addresses in the world, not 13 servers! Because these IP mirrors can be set up around the world with anycast technology, access is not unique to one host
-
Obviously, multiple query requests are made throughout the process
-
After entering the page for the first time, the DNS resolved address record will be cached in the client, then at least there is no need to launch the subsequent iteration query, so that the speed is faster
HTTP cache
- This means that the page resources obtained by HTTP requests are stored locally and then loaded directly from the cache instead of the request server, resulting in faster response. Look at the picture first:
Strong cache
On the first request, the server tells the browser the expiration date of the resource through the Expires and cache-control fields in the response header, and then asks the browser to evaluate the resource. If the resource does not expire, the server will directly use it without making a request to the server. This is a strong Cache
Expires
- Used to specify the absolute expiration time of a resource, added to the response header when the server responds.
expires: Wed, 22 Nov 2021 08:41:00 GMT
Copy the code
Ps: If the time on the server is inconsistent with that on the browser, it may fail. For example, if the current time is August 1, the expires time is August 2, and the client changes the computer time to August 3, then the cache won’t be used
Cache-Control
- Specify the resource expiration time in seconds, as follows, indicating that the resource will be available within 300 seconds after this request is returned correctly, otherwise it will expire
cache-control:max-age=300
Copy the code
Why do you need two fields to specify cache expiration time?
- Because some browsers only recognize cache-Control and some don’t, they look for Expires if they don’t
The difference between Expires and Cache-Control
- Expires is
HTTP / 1.0
Cache-control isHTTP / 1.1
The; - Expires is meant to be compatible, not supported
HTTP / 1.1
That would have an effect - Cache-control has a higher priority than Expires if both exist.
Cache-ControlRequest header
Common properties
Ps: How many seconds is custom, I write here is easy to understand
Cache-ControlResponse headers
Common properties
Disadvantages of strong caching
- After the cache expires, a request is made to retrieve the resource regardless of whether the resource has changed
- What we want is to continue to use the old resource without renewing the resource file, even if it expires
- So the negotiation cache it comes, in the case of strong cache expiration, then go through the negotiation cache process, determine whether the file has been updated
Negotiate the cache
- The first time a resource is requested, the server returns the expiration date specified above to the browser and adds it to the response header
Last-Modified
Field that tells the browser when the resource was last modified
last-modified: Fri, 27 Oct 2021 08:35:57 GMT
Copy the code
- The browser then passes the time through another field when it requests it again
If-Modified-Since
, sent to the server
if-modified-since: Fri, 27 Oct 2021 08:35:57 GMT
Copy the code
- The server then compares the two fields. If they are the same, it means that the file has not been updated, and returns the status code 304 and the empty response body to the browser. The browser simply takes the expired resource and continues to use it
- If the comparison is different, the resource is updated, and the status code 200 and the new resource are returned
So last-modified/if-modified-since are paired to compare file modification times
disadvantages
- If the cache file is opened locally, it will cause a problem even if the file is not modified
Last-Modified
The server cannot match the cache and sends the same resource - because
Last-Modified
It can only be timed in seconds. If a file is modified in an imperceptible period of time, the server will consider it a hit and cannot return the correct resource - If the resource is periodically changed, for example, after a resource is modified, it is changed back to its original appearance within a period, we think that the cache before this period can be used, but
Last-Modified
Don’t think so
Because of these shortcomings, there is another pair of ETag/ if-none-match to compare file contents
ETag
/If-None-Match
The first time a resource is requested, the server returns Expires, Cache-Control, last-Modified, and the Etag field, a unique identifier for the current resource file, in addition to the response header.
This identifier is generated by the server based on the encoding of the file content. It can accurately sense the changes in the file and regenerates the ETag whenever the file content is different
etag: W/"132489-1627839023000"
Copy the code
- The browser then passes the file through another field when it requests it again
If-None-Match
, sent to the server
if-none-match: W/"132489-1627839023000"
Copy the code
- The server then compares the time of the two fields. If the two fields are the same, it indicates that the file has not been updated, and returns the status code 304 and the empty response body to the browser. The browser directly takes the expired resource and continues to use it
- If the comparison is different, the resource is updated, and the status code 200 and the new resource are returned
Difference between Last-Modified and ETag
Etag
Perceived file accuracy is higher thanLast-Modified
- When both servers are used, the server verifies the priority
Etag
/If-None-Match
Last-Modified
Performance is better thanEtag
Because theEtag
It is not a complete substitute for the overhead incurred by the server during the generation process, which affects server-side performanceLast-Modified
, can only be used as a supplement and reinforcement
Strong cache vs. negotiated cache
- The strong cache is searched first, and the negotiated cache is searched again if there is no match
- The strong cache does not send requests to the server, so sometimes the browser does not know that the resource has been updated, but the negotiation cache will send requests to the server, and the server must know if the resource has been updated
- Most projects currently use cached copywriting
- Negotiation cache general storage:
HTML
- Strong cache general storage:
css
.image
.js
, file name withhash
- Negotiation cache general storage:
Heuristic cache
This is when there is no Expires, cache-Control: max-age or cache-Control :s-maxage in the response and last-Modified is set
By default, browsers use a heuristic algorithm, called heuristic caching, to calculate the cache expiration date
The cache time is usually 10% of the last-Modified value minus the Date field in the response header (the time the message was created)
max(0, (Date - Last-Modified)) % 10
Copy the code
Cache actual usage policy
For frequently changing resources
- use
The cache-control: no - Cache
Make the browser request data each time and then cooperateEtag
orLast-Modified
While not saving the number of requests, it can significantly reduce the size of the response data
For resources that don’t change very often:
- You can give it to them
Cache-Control
Configure a very largemax-age=31536000
(one year), so that the browser later requests for the same URL will hit the strong cache, which will need to be added to the file name (or path) to solve the update problemhash
, version number, etc., and then change the dynamic character to change the reference URL, invalidating the previous strong cache (actually not immediately invalidating, just no longer used).
Cache location, and read priority
Priority is in the following order
1. Service Worker
2. Memory Cache
The resource is stored in memory and read directly from memory for the next access. When a page is refreshed, for example, much of the data comes from the in-memory cache. Generally store scripts, fonts, and pictures.
The advantage is fast reading speed; Disadvantages Because once the Tab Tab is closed, the cache in memory is released, so the capacity and storage time is poor
3. Disk Cache
A resource is stored in a hard disk and read from the hard disk the next time it is accessed. Based on the fields in the request header, it determines which resources need to be cached, which resources can be used without being requested, and which resources have expired and need to be re-requested. And even in the case of cross-domain sites, resources with the same address once cached by the hard disk will not be requested again.
Advantages are cached in hard disk, large capacity, and longer storage timeliness; The disadvantage is that the reading speed is slower
4. Push Cache
This is a push cache, which is HTTP/2 content, and it’s used when all three of these caches fail. It only exists in the Session and is released once the Session ends, so the Cache time is short and the Cache in the Push Cache can only be used once
CDN cache
- When we send a request and the browser’s local cache is invalidated, the CDN helps us figure out where the path to get the content is short and fast.
- For example, a request to a server in Guangzhou is much faster than a request to a server in Xinjiang, and then requests data to the nearest CDN node
- CDN will determine whether the cached data is expired, if not, it will directly return the cached data to the client, thus speeding up the response speed. If the CDN determines that the cache has expired, it will issue a back source request to the server, pull the latest data from the server, update the local cache, and return the latest data to the client.
- CDN not only solves the problem of cross-carrier and cross-region access, greatly reduces the access delay, but also plays a role of distribution, reducing the load of the source server
Several refresh and carriage return differences
Three refresh operations
- Normal operations: Enter url, forward link, and backward link in the address bar to force the cache to work, and negotiate the cache to work
- Manual refresh: F5, click refresh button, right click menu refresh to force cache invalidation and negotiate cache validity
- Force refresh: CTRL + F5 force cache invalidation, negotiation cache invalidation
The local store
Cookie
Local storage, which was first proposed, carries cookies in each HTTP request to determine whether multiple requests are initiated by the same user. Its characteristics are as follows:
- There is a security issue, if it is intercepted, it can get all the Session information, and then it forwards the Cookie to do that
- Each domain name contains a maximum of 20 cookies and the size cannot exceed 4kb
- Cookies are sent whenever a new page is requested
- If the Cookie is created successfully, the name cannot be changed
- Cookies cannot be shared across domain names
There are two ways to share cookies across domains
- Use Nginx reverse proxy
- After logging in to one site, write cookies to other sites. The Session on the server is stored on a node, and the Cookie is stored on a Session ID
Cookie usage scenarios
- The most common one is to use a Cookie in conjunction with a Session, storing the Session ID in a Cookie and carrying the Session ID with each request so that the server knows who initiated the request
- Can be used to count the number of clicks on a page
What fields do cookies have
Name
,Size
Name the sizeValue
: Saves the user login status. This value should be encrypted and cannot be used in plain textPath
: Path to which this Cookie can be accessed. For example, juejin.cn/editor, the path is /editor, and only those under /editor can read cookieshttpOnly
: disables Cookie access through JS to reduce XSS attacks.Secure
: Can be carried only in HTTPS requestsSameSite
: specifies that browsers cannot carry cookies in cross-domain requests to reduce CSRF attacksDomain
: a domain name, cross-domain, or Cookie whitelist that allows a subdomain to obtain or manipulate cookies from the parent domain, useful for single sign-onExpires
/Max-size
: Specifies the expiration time or number of seconds. If this is not set, it will expire when the browser is closed, just like Session
LocaStorage
- Is a new feature of H5, is to store information to the local, its storage size is much larger than Cookie, 5M, and is permanent storage, unless the initiative to clean up, or it will always exist
- Restricted by the same origin policy, that is, the port, protocol, host address of any different cannot be accessed, and the browser set to privacy mode, can not read LocalStorage
- It is used in a lot of scenarios, such as storing website themes, storing user information, etc., save a large amount of data or not much change of data can be used it
SessionStorage
- SessionStorage is also a new feature of H5. It is mainly used to temporarily store data of the same window or TAB. It will not be deleted when the page is refreshed, but will be deleted after the window or TAB is closed
- EssionStorage and LocalStorage are LocalStorage, and can not be crawled, and have the same origin policy restrictions, but SessionStorage is more strict, only in the same browser under the same window can be shared
- Its API is the same as LocalStorage getItem, setItem, removeItem, clear
- Its usage scenarios are generally time-sensitive, such as storing visitor login information of some websites, as well as temporary browsing history
indexDB
Is a browser native database with the following features
Key-value pair storage
: Internal object warehouse storage data, all types of data can be directly stored, including JS objects, stored in the form of key-value pairs, each data has a corresponding primary key, the primary key is uniqueasynchronous
IndexDB operations can still be performed by the user, and the asynchronous design prevents large data reads and writes from slowing down the performance of the web pageSupport transactions
: For example, modify the entire table data, modify half of the time reported an error, at this time will be restored to the state of the unmodified level, there is no successful modification of half of the situationThe same-origin restrictions
: Each database should create its corresponding domain name, web pages can only access the database under its own domain nameLarge storage space
: Generally not less than 250MB, or even no upper limitSupport binary storage
Such as ArrayBuffer and Blob objects
In addition to the above four front-end storage methods, there are WebSQL, similar to SQLite, is a real relational database, you can use SQL to operate, but with JS to convert, more trouble
The difference between the above four
Offline storage
Service Worker
A Service Worker is an independent thread running outside the main thread of JS and behind the browser, which naturally cannot access DOM. It is equivalent to a proxy server, which can intercept requests sent by users, modify requests or directly respond to users without contacting the server. Loading JS and images, for example, allows us to use web applications offline
It is used for offline cache (improving the loading speed of the first screen), message push, and network proxy. To use a Service Worker, you must use HTTPS, because the Service Worker involves request interception and requires HTTPS to ensure security
There are three steps to implementing caching with Service workers:
- It is a registered
- The files can then be cached after listening for the Install event
- The next time you access it, you can return the cached data directly by intercepting the request
/ / index. Js registration
if (navigator.serviceWorker) {
navigator.serviceWorker.register('sw.js').then( registration= > {
console.log('Service worker registered successfully')
}).catch((err) = >{
console.log('Servcie worker registration failed')})}// sw.js listens for the 'install' event and caches the required files in the callback
self.addEventListener('install'.e= > {
// Opens the specified cache file name
e.waitUntil(caches.open('my-cache').then( cache= > {
// Add files to cache
return cache.addAll(['./index.html'.'./index.css'])}})))// If there is a request in the cache, use the cache directly; otherwise, request the data
self.addEventListener('fetch'.e= > {
// Find the response hit in the request cache
e.respondWith(caches.match(e.request).then( response= > {
if (response) {
return response
}
console.log('fetch source')}})))Copy the code