更新文章:完结两个坑

1. 路由缓存
2. 图片懒加载
This commit is contained in:
DefectingCat
2021-03-15 13:39:01 +08:00
parent 42fd611569
commit 73af462f61
10 changed files with 367 additions and 2 deletions

View File

@ -167,3 +167,43 @@ HTML 元素支持使用一个与事件名称同名的 Attribute 来监听对应
2. 使用 with 拓展作用域时会在不同浏览器中导致不同的结果
3. HTML JavaScript 代码紧密耦合
### DOM0 级事件处理程序
DOM0 级事件处理程序是以前时代常见的传统方式它是将一个函数赋值给一个事件处理程序属性这和 HTML 事件处理程序有点类似不够它将 JavaScript 代码和 HTML 完美的解耦合了
```js
let btn = document.querySelector('#b');
btn.onclick = function() {
alert('test');
}
```
DOM0 级方法指定的事件处理程序被认为是元素的方法所以这时候的函数是在元素的作用域中运行的也就是说this 引用的是当前元素
```js
let btn = document.querySelector('#b');
btn.onclick = function() {
alert(this);
}
```
以这种方式添加的事件处理程序会在事件流的冒泡流阶段被处理
### DOM2 级事件处理程序
DOM2 级不再是元素的方法它通过定义的两个方法来为元素添加或删除事件处理程序这也是目前现代笔记常用的方法
DOM2 级定义了两个方法
* `addEventListener()`
* `removeEventListener()`
他们都接受三个三个参数
```js
element.addEventListener('事件名', '事件处理程序函数', '捕获/冒泡')
```
true 时表示在捕获阶段调用函数 false 时表示在冒泡阶段调用函数默认为冒泡流
## 事件对象

View File

@ -17,4 +17,75 @@
style="width: 48%; margin-top: 10px"
```
![](../images/第一个SPA的总结/2021-03-01-12-15-31.png)
![](../images/第一个SPA的总结/2021-03-01-12-15-31.png)
## 瀑布流
两列的排列,图片的高度不统一。使用`flex`的横向换行排列会留出空白。
使用`flex`将两列图片分别作为两个容器,纵向排列
```html
<div class="goods">
<!-- 从 home 组件接受的 list循环创建 item -->
<div class="col">
<GoodsListItem
v-for="(item, index) of list1"
:key="item.iid + index"
:item="item"
style="margin-top: 10px"
/>
</div>
<div class="col">
<GoodsListItem
v-for="(item, index) of list2"
:key="item.iid + index"
:item="item"
style="margin-top: 10px"
/>
</div>
</div>
```
```css
.goods .col {
display: flex;
flex-flow: column;
width: 48%;
}
```
![](../images/第一个SPA的总结/2021-03-12-10-48-27.png)
## 停留标题
## 首页滑动位置记录
## 详情页
### 传递参数
## keep-alive
缓存的组件一定要设置`name`属性!
* [vue.js的keep-alive include无效](https://segmentfault.com/q/1010000009117672#)
## 路由过渡动画
通过添加 [过渡模式](https://cn.vuejs.org/v2/guide/transitions.html#%E8%BF%87%E6%B8%A1%E6%A8%A1%E5%BC%8F) 来定义在多个路由或组件中的平滑过渡
```html
<transition name="fade" mode="out-in">
<!-- ... the buttons ... -->
</transition>
```
## 控制`v-for`
```html
<span v-for="index of goods.services.length - 2" :key="index">
<img src="" alt="" />
<span> {{ goods.services[index].name }}</span>
</span>
```

View File

@ -0,0 +1,245 @@
---
title: 原生图片懒加载
date: 2021-03-12 21:00:00
tags: JavaScript
categories: 实践
url: javascript-lazy-loading-image
index_img: /images/原生图片懒加载/logo.gif
---
图片懒加载是一个常见的应用它旨在不影响用户体验的情况下提示首屏的加载速度。原理很简单在当前可视视口Viewport外的图片等到进入了视口内再进行加载按需加载
## 资源处理请求
浏览器对于不同资源的下载请求有着不同的优先级。在基于 Chromium 的浏览器中,有着这样的优先级:
![](../images/原生图片懒加载/2021-03-12-23-38-13.webp)
在默认情况下,那些在可视视口外的图片可能有着更高的加载优先级。而可视视口外的图片提前加载是没有必要的,这就可能会造成其他关键的资源(如首屏时间、`xhr call`等)加载缓慢。
> 目前的 Chromium 可能会智能一点,视口外的图片优先级没有那么高
## 最基础实现
这里主要使用一张小图作为占位图片placeholder),然后再使用 JavaScript 去加载真正的图片。
这里的核心就是加载图片的`loadImg()`方法。它通过将图片的 src 属性进行替换,替换为提前设置的真正图片的地址`data-src`。实现加载真正的图片。
光靠这一个方法还不行,通过`document.querySelectorAll('img[data-src]')`取得的所有 img 标签的集合为一个 NodeList。还需要使用`forEach()`方法来为每个图片执行`loadImg()`方法。
后续的懒加载还需要使用到`loadImg()`方法来加载真正的图片。
```js
function createImg() {
let frag = document.createDocumentFragment();
let num = 28;
for (let i = 0; i < 9; i++) {
let img = document.createElement('img');
// placeholder 地址
img.src = './img/placeholder.gif';
// 图片真实地址
img.setAttribute('data-src', `./img/file_49639${num}.png`);
num++;
frag.append(img);
}
return frag;
}
let images = createImg();
document.body.append(images);
// 选中所有懒加载的图片
let allImg = document.querySelectorAll('img[data-src]');
// 接受一个 img 元素作为参数
function loadImg(img) {
img.src = `${img.getAttribute('data-src')}`;
// 加载完成后移除 data-src 属性
img.addEventListener('load', loadHandler);
function loadHandler() {
img.removeAttribute('data-src');
// 执行一次后移除 load 监听器
img.removeEventListener('load', loadHandler);
}
}
// forEach 遍历,加载每个图片
allImg.forEach((val) => {
loadImg(val);
});
```
## 懒加载的实现
懒加载的主要实现还是靠一个新兴的 API Intersection Observer API。
早期通过检查元素`Element.getBoundingClientRect()`的 top 值与浏览器可视高度`document.documentElement.clientHeight`是否相交也是个不错的办法。但 Intersection Observer API 毕竟还是原生的 API用起来也更加方便~~买新不买旧~~)。
在[兼容性](https://developer.mozilla.org/zh-CN/docs/Web/API/Intersection_Observer_API#%E6%B5%8F%E8%A7%88%E5%99%A8%E5%85%BC%E5%AE%B9%E6%80%A7)方面,除了 IE 几乎新版浏览器都支持该 API。
> 当然 IE 是不支持的。
Intersection Observer API 会注册一个回调函数,每当被监视的元素进入或者退出另外一个元素时(或者 viewport或者两个元素的相交部分大小发生变化时该回调方法会被触发执行。
通过 Intersection Observer 来监听图片与可视视口是否相交,也就是监听图片是否进入了可视区域。当图片可视时,就调用回调函数,替换 placehol 加载真正的图片。
Intersection observer 支持多个 options。options 为可选配置,对于图片懒加载,默认的配置就足够了。默认监听 target 与可视视口的相交。
回调函数接受两个参数IntersectionObserverEntry 和 observe 自身。其中 IntersectionObserverEntry 就是主要用到的参数。它有多个属性:
* `IntersectionObserverEntry.boundingClientRect`只读
返回包含目标元素的边界信息的 DOMRectReadOnly. 边界的计算方式与 `Element.getBoundingClientRect()`相同。
* `IntersectionObserverEntry.intersectionRatio`只读
返回 intersectionRect 与 boundingClientRect 的比例值.
* `IntersectionObserverEntry.intersectionRect`只读
返回一个 DOMRectReadOnly 用来描述根和目标元素的相交区域.
* `IntersectionObserverEntry.isIntersecting`只读
返回一个布尔值, 如果目标元素与交叉区域观察者对象(intersection observer) 的根相交,则返回 true .如果返回 true, 则 IntersectionObserverEntry 描述了变换到交叉时的状态; 如果返回 false, 那么可以由此判断,变换是从交叉状态到非交叉状态.
* `IntersectionObserverEntry.rootBounds`只读
返回一个 DOMRectReadOnly 用来描述交叉区域观察者(intersection observer)中的根.
* `IntersectionObserverEntry.target`只读
与根出现相交区域改变的元素 (Element).
* `IntersectionObserverEntry.time`只读
返回一个记录从 IntersectionObserver 的时间原点(time origin)到交叉被触发的时间的时间戳(DOMHighResTimeStamp).
其中 isIntersecting 和 target 就是本次需要用到的属性。原理很简单当图片与视口相交时isIntersecting就将图片target运行指定的函数以加载真正的图片。
```js
// 创建一个 observer 对象,接受一个回调
let observer = new IntersectionObserver((entries, observer) => {
// IntersectionObserverEntry 参数
// console.log(entries);
// entries 就是 IntersectionObserverEntry 的集合。
entries.forEach((entry) => {
// 当 IntersectionObserverEntry 的 isIntersecting 为 true 时,则图片出现
if (entry.isIntersecting) {
// 使用加载函数 加载目标entry.target 就是目标图片
loadImg(entry.target);
// 加载完成后取消监听图片
observer.unobserve(entry.target);
}
});
});
// 为所有图片监听
allImg.forEach((val) => {
observer.observe(val);
});
```
## 内容抖动
内容抖动指的是在替换 placehol 到真正的图片时大小不一致导致的布局变化。
由于每张图片的大小(宽高)可能都不一样,在加载了真正图片之后,布局被图片所撑开或缩小。内容抖动在浏览体验上可能影响不是非常大,但解决内容抖动也是图片懒加载的必要。
解决内容抖动有很多影响的因素,选择一个合适的 placeholder 是一个重要的步骤。根据图片的尺寸来选择合适的 placeholder
* 图片的尺寸已知
这种情况下可以手动制作一个等比例的 placeholder 或者使用类似 [placeholder.com](https://placeholder.com/) 这样的网站来制作合适的 placeholder。
* 图片的尺寸未知
在图片尺寸未知的情况下,可以考虑使用一些在线的云服务(如 CDN 的图片处理)来动态的根据图片制作 placeholder。
## 解决内容抖动
制作了等比例的 placeholder 还不能解决所有的问题。由于`<img>`标签的特性,在图片为加载时,该标签的尺寸是无法确定的。它会随着图片的大小变化。而正是这一特性,它和图片一样只指定一个宽或高时,会等比缩放。
除了特殊需求外,图片几乎都是等比缩放。所以只需要给图片的宽和高设置一个值即可。
### Aspect Ratio Boxes
长宽比盒子。在一个盒子模型中,`padding-top``padding-bottm`属性设置为百分比时,依据的是盒子的宽度。
也就是说,只要确定了盒子的宽度,就能够使用`padding`设置为百分比来画出一个等比例的盒子。
例如一个 16:9 的长方形:
<p class="codepen" data-height="321" data-theme-id="light" data-default-tab="css,result" data-user="Defectink" data-slug-hash="GRNzGam" data-preview="true" style="height: 321px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Aspect Ratio Boxes">
<span>See the Pen <a href="https://codepen.io/Defectink/pen/GRNzGam">
Aspect Ratio Boxes</a> by Defectink (<a href="https://codepen.io/Defectink">@Defectink</a>)
on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
```css
.wrapper {
overflow: hidden;
height: 0;
padding-top: 56.25%;
background-color: red;
position: relative;
}
```
上述 div 的宽度默认为 100%,而`padding-top`的值就是`9 / 16 * 100% = 56.25%`得出的值。当为指定比例的比值时,由内边距绘制出的盒子将一直保持着 16:9 的比例。
这种等比例的盒子对图片内容防抖动也有一定的用处,将图片包裹在一个等于图片比例的盒子内。由于 img 标签的特性,在图片未加载时,其本身和父元素均没有高度。而 div 元素本身的宽度默认为 100%,利用这一点,就可以在图片未加载完成时,先使用长宽比盒子为图片提供一个占位符。等图片懒加载完成后再覆盖内边距。
<p class="codepen" data-height="265" data-theme-id="light" data-default-tab="css,result" data-user="Defectink" data-slug-hash="ZEBwMyL" data-preview="true" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="长宽比盒子占位">
<span>See the Pen <a href="https://codepen.io/Defectink/pen/ZEBwMyL">
长宽比盒子占位</a> by Defectink (<a href="https://codepen.io/Defectink">@Defectink</a>)
on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
### 手动设置尺寸
上述长宽比盒子解决防抖动时是需要知道图片的比例的。如果图片都是一样的比例,那么使用长宽比盒子来防抖动也是个不错的选择。
但既然已经知道了图片的尺寸,那么直接给 img 标签添加对应的宽度来防止内容的抖动。将尺寸特别小的 placeholder 放大时,再配合上`filter: blur(10px)`来添加一个模糊。一个丝滑的图片懒加载就做好了。
通常从后端拿到的图片数据格式可能是这样的,其中会包含图片的尺寸信息:
```js
{
src: './img/file_4963928.webp',
ph: './img/placeholder/file_4963928.webp',
w: 450,
h: 512,
},
```
在生成图片时只需要设置长或宽其中一个尺寸即可:
```js
function createImg2() {
let frag = document.createDocumentFragment();
for (let i of requestImg) {
let img = document.createElement('img');
// placeholder 地址
img.src = `${i.ph}`;
// 图片真实地址
img.setAttribute('data-src', `${i.src}`);
// 根据请求的图片尺寸动态设置尺寸
img.style.width = `${i.w}px`;
// img.style.height = `${i.h}px`;
frag.append(img);
}
frag.append(document.createElement('hr'));
return frag;
}
```
## Demo
在线 [Demo](https://codesandbox.io/s/defectingcatlazy-load-image-wbiz0)
## 参考
* [Chrome Resource Priorities and Scheduling](https://docs.google.com/document/d/1bCDuq9H1ih9iNjgzyAL0gpwNFiEP4TZS-YLRp_RuMlc/edit#)
* [浏览器里的资源请求](https://blog.windstone.cc/front-end/browser-env/browser/browser-requests.html)
* [Intersection Observer API](https://developer.mozilla.org/zh-CN/docs/Web/API/Intersection_Observer_API)
* [IntersectionObserverEntry](https://developer.mozilla.org/zh-CN/docs/Web/API/IntersectionObserverEntry)
* [Aspect Ratio Boxes](https://css-tricks.com/aspect-ratio-boxes/)

View File

@ -1,3 +1,12 @@
---
title: 路由中的动态组件-keepAlive与路由
date: 2021-01-24 11:01:30
tags: [JavaScript, Vue]
categories: 笔记
url: router-with-keepalive
index_img: /images/路由中的动态组件-keepAlive与路由/logo.webp
---
## 真正的动态组件
`<keep-alive>`经常配合`componentIs`来动态的切换组件,当组件再次被切换回来的时候,组件的状态依然被保存。
@ -95,7 +104,7 @@ beforeRouteLeave(to, from, next) {
Uncaught (in promise) NavigationDuplicated: Avoided redundant navigation to current location: ""
```
![](../images/路由中的动态组件-keepAlive与路由/2021-01-20-10-08-18.png)
![](../images/路由中的动态组件-keepAlive与路由/2021-01-20-10-08-18.webp)
这个错误的主要原因是因为同一条路由被重写两遍NavigationDuplicated。

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 579 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB