Renkosky's Home

莲子的小窝


  • 首页

  • 归档

  • 标签

实现懒加载

发表于 2018-10-14
字数统计 279 字 | 阅读时长 1 分钟

对页面加载速度影响最大的就是图片,一张普通的图片可以达到几 M 的大小,因此大量加载图片可能需要很长时间。因此我们可以让不在可视范围内的图片加载,等到
滚到那个区域时再加载图片,这样可以提升页面的加载速度

原理

img 标签有一个 src 标签先把他指向一个小图片或者空的字符串,然后自定义一个属性 src

1
img src="default.jpg" data-src="http://ww4.sinaimg.cn/large/006y8mN6gw1fa5obmqrmvj305k05k3yh.jpg" />

代码

当元素进入可见区域时,就把当前元素的 src 替换为 data-src

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function lazyload() {
//监听页面滚动事件
var seeHeight = document.documentElement.clientHeight //可见区域高度
var scrollTop = document.documentElement.scrollTop || document.body.scrollTop //滚动条距离顶部高度
for (var i = n; i < num; i++) {
if (img[i].offsetTop < seeHeight + scrollTop) {
if (img[i].getAttribute('src') == 'default.jpg') {
img[i].src = img[i].getAttribute('data-src')
}
n = i + 1
}
}
}
window.addEventlistener('scroll', lazyload)

优化

可以使用防抖函数对其进行优化。防抖参照之前的文章

es6中的一些问题

发表于 2018-08-12
字数统计 1.2k 字 | 阅读时长 5 分钟

Class

ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过 class 关键字,可以定义类。

基本上,ES6 的 class 可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到.

1
2
3
4
5
6
7
8
9
10
class TestClass {
constructor(x, y) {
this.x = x
this.y = y
}

toString() {
return `${this.x},${this.y}`
}
}

constructor 方法是类的默认方法,通过 new 命令生成对象实例时,自动调用该方法。一个类必须有 constructor 方法,如果没有显式定义,一个空的 constructor 方法会被默认添加。

阅读全文 »

webpack功能以及原理

发表于 2018-08-12
字数统计 2.7k 字 | 阅读时长 10 分钟

核心思想

webpack 中一切皆为模块。由于 webpack 不支持 js 以外的文件,因此项目中的 css,字体等都会通过不同的 loaders 压缩打包成一个 js,公共的代码抽离,也可以指定部分单独打包。

#打包原理

webpack只是一个打包模块的机制,只是把依赖的模块转化成可以代表这些包的静态文件。并不是什么commonjs或者amd之类的模块化规范。webpack就是识别你的 入口文件。识别你的模块依赖,来打包你的代码。至于你的代码使用的是commonjs还是amd或者es6的import。webpack都会对其进行分析。来获取代码的依赖。webpack做的就是分析代码。转换代码,编译代码,输出代码。webpack本身是一个node的模块,所以webpack.config.js是以commonjs形式书写的(node中的模块化是commonjs规范的)

webpack中每个模块有一个唯一的id,是从0开始递增的。整个打包后的bundle.js是一个匿名函数自执行。参数则为一个数组。数组的每一项都为个function。function的内容则为每个模块的内容,并按照require的顺序排列。

功能

依赖管理

方便引用第三方模块、让模块更容易复用、避免全局注入导致的冲突、避免重复加载或加载不需要的模块。

例如在一个页面中,我引用了 a,b,c 三个 js 文件,他们互相之间有个字的依赖关系,所以引入的时候顺序出错就会导致问题。如果
使用 webpack 的话就会打成一个文件并自动处理依赖关系
WebPack 打包时, 会把有依赖关系的 js 模块(物理形式上,一般表现为一个.js 文件),打包成一个 bundle。而依赖关系是基于 require(‘…’),而且一般不要相互依赖。

合并代码

把各个分散的模块集中打包成大文件,减少 HTTP 的请求链接数,配合 UglifyJS 可以减少、优化代码的体积。

阅读全文 »

node相关的各种知识

发表于 2018-07-01
字数统计 353 字 | 阅读时长 1 分钟

什么是 Node

Node 是一个 Javascript 运行时,运行时(runtime)可以当作一种编程语言的运行环境,其中包括了运行代码需要的编译器以及操作系统的底层支持。

阅读全文 »

React知识点总结

发表于 2018-07-01
字数统计 4.6k 字 | 阅读时长 17 分钟

什么是 react

React 是一个声明式的,高效的,并且灵活的用于构建用户界面的 JavaScript 库。

react 的特点

声明式

react 采用声明式的渲染方法,当数据更改时,React 将高效地更新和正确的渲染组件。

虚拟 DOM

虚拟 DOM 是一种抽象的数据结构,在 DOM 的基础上建立了一个抽象层,对数据和状态所做的任何改动,都会被自动且高效的同步到虚拟 DOM,最后再批量同步到 DOM 中。虚拟 DOM 只有插入文档后才会变成真正的 DOM

diff 算法

react 中所有的 DOM 变动,都先在虚拟 DOM 上发生,然后再将实际发生变动的部分,反映在真实 DOM 上,这种算法叫做 DOM diff

DOM DIFF 是 react 应用中的精华所在,DOM DIFF 在使用时有一些约定如下:

  1. DOM 节点跨层级的移动操作特别少,可以忽略不计(例如 A 原本和 B 平级,随后 A 变成 B 的子节点)

  2. 拥有相同类的两个组件将会生成相似的树形结构,拥有不同类的两个组件将会生成不同的树形结构(A 和 B 组件结构不一致)

  3. 同一层级的一组子节点,它们可以通过 uid 进行区分。

DIFF 算法在执行时有三个维度,分别是 Tree DIFF、Component DIFF 和 Element DIFF,执行时按顺序依次执行,它们的差异仅仅因为 DIFF 粒度不同、执行先后顺序不同。

Tree DIFF 是对树的每一层进行遍历,如果某组件不存在了,则会直接销毁。

第二层进入 Component DIFF,同一类型组件继续比较下去,发现 A 组件没有,所以直接删掉 A、B、C 组件

Element DIFF 紧接着以上统一类型组件继续比较下去,常见类型就是列表。同一个列表由旧变新有三种行为,插入、移动和删除,它的比较策略是对于每一个列表指定 key,先将所有列表遍历一遍,确定要新增和删除的,再确定需要移动的。

什么是 VDOM?

首先,DOM 文档对象模型(Document Object Model))是一种对文档进行抽象化的树形结构,文档皆在这个结构中,而 VDOM 则是对 DOM 的抽象,其本身是一个 js 的对象,拥有 dom 的一些属性例如 class,id 等。

例如 react 中的 VDOM 由 VNODE 组成,其包含:

  • 标签类型:\$\$typeof
  • 节点属性:props(包括属性,事件,子节点,类选择器等)
  • 唯一 ID 值:key
  • 引用指针:ref
    在 react 中我们通过 jsx 去编写 js 和 html 的混合,然后通过 babel 插件编译成对象再调用 createElement
    例子:
1
const element = <h1 className="greeting">Hello, world!</h1>

编译为:

1
2
3
4
5
6
//jsx书写的代码会变成js对象然后调用createElement把其作为参数传入
const element = React.createElement(
'h1',
{ className: 'greeting' },
'Hello, world!'
)

最后返回

1
2
3
4
5
6
7
const element = {
type: 'h1',
props: {
className: 'greeting',
children: 'Hello, world'
}
}

使用 VDOM 的原因。

随着前端技术的发展,现在的网页应用变得越来越庞大,DOM 树也随之变得复杂。当我们要修改 DOM 的某个元素时,我们需要先遍历找个这个元素,然后才修改能修改。而且如果我们大量地去修改 DOM,每次 DOM 修改浏览器就得重绘甚至重排(repaint)页面,损耗了大量性能

数据驱动

react 通过数据驱动更新节点,通过更新虚拟的抽象数据来更新界面

组件化

每一个组件都拥有自己的状态(state)用其渲染复杂的界面。也可以声明无状态组件,在更高级的组件中保存 state,而子组件只负责视图部分。

阅读全文 »

函数防抖与节流

发表于 2018-06-18
字数统计 1.3k 字 | 阅读时长 5 分钟

函数防抖

函数防抖就是让某个函数在上一次执行后,满足等待某个时间内不再触发此函数后再执行,而在这个等待时间内再次触发此函数,等待时间会重新计算。

当用户在进行一系列的操作的时候例如输入一串字符,这时如果需求中需要我们去按照用户的输入做请求,比如联想输入时,有可能就会导致多次函数的调用,而事实上我们只需要最后的那个值去发请求,函数防抖就可以解决这个问题。

undersource.js

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
_.debounce = function(func, wait, immediate) {
var timeout, args, context, timestamp, result

var later = function() {
var last = _.now() - timestamp //_.now(),一个优化的方式来获得一个当前时间的整数时间戳

if (last < wait && last >= 0) {
timeout = setTimeout(later, wait - last)
} else {
timeout = null
if (!immediate) {
result = func.apply(context, args)
if (!timeout) context = args = null
}
}
}

return function() {
context = this
args = arguments
timestamp = _.now()
var callNow = immediate && !timeout
if (!timeout) timeout = setTimeout(later, wait)
if (callNow) {
result = func.apply(context, args)
context = args = null
}

return result
}
}

源码中 debounce 函数中的 later 是 setTimeout 中的回调函数,last 是调用后过了多少时间,如果 last 比 await 小就递归调用 later,等待时间重新计算。
如果设置了 immediate 就会立即触发一次函数,之后再计算时间。

另一种实现

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
// 这个是用来获取当前时间戳的
function now() {
return +new Date()
}
/**
* 防抖函数,返回函数连续调用时,空闲时间必须大于或等于 wait,func 才会执行
*
* @param {function} func 回调函数
* @param {number} wait 表示时间窗口的间隔
* @param {boolean} immediate 设置为ture时,是否立即调用函数
* @return {function} 返回客户调用函数
*/
function debounce(func, wait = 50, immediate = true) {
let timer, context, args

// 延迟执行函数
const later = () =>
setTimeout(() => {
// 延迟函数执行完毕,清空缓存的定时器序号
timer = null
// 延迟执行的情况下,函数会在延迟函数中执行
// 使用到之前缓存的参数和上下文
if (!immediate) {
func.apply(context, args)
context = args = null
}
}, wait)

// 这里返回的函数是每次实际调用的函数
return function(...params) {
// 如果没有创建延迟执行函数(later),就创建一个
if (!timer) {
timer = later()
// 如果是立即执行,调用函数
// 否则缓存参数和调用上下文
if (immediate) {
func.apply(this, params)
} else {
context = this
args = params
}
// 如果已有延迟执行函数(later),调用的时候清除原来的并重新设定一个
// 这样做延迟函数会重新计时
} else {
clearTimeout(timer)
timer = later()
}
}
}

函数节流

函数节流就是让某个函数每间隔一段时间执行防止多次调用

简单版本

1
2
3
4
5
6
function throttle(method, context,wait=100) {
clearTimeout(methor.tId);
method.tId = setTimeout(function(){
method.call(context);
}, wait);
}
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
/**
* underscore 节流函数,返回函数连续调用时,func 执行频率限定为 次 / wait
*
* @param {function} func 回调函数
* @param {number} wait 表示时间窗口的间隔
* @param {object} options 如果想忽略开始函数的的调用,传入{leading: false}。
* 如果想忽略结尾函数的调用,传入{trailing: false}
* 两者不能共存,否则函数不能执行
* @return {function} 返回客户调用函数
*/
_.throttle = function(func, wait, options) {
var context, args, result
var timeout = null
// 之前的时间戳
var previous = 0
// 如果 options 没传则设为空对象
if (!options) options = {}
// 定时器回调函数
var later = function() {
// 如果设置了 leading,就将 previous 设为 0
// 用于下面函数的第一个 if 判断
previous = options.leading === false ? 0 : _.now()
// 置空一是为了防止内存泄漏,二是为了下面的定时器判断
timeout = null
result = func.apply(context, args)
if (!timeout) context = args = null
}
return function() {
// 获得当前时间戳
var now = _.now()
// 首次进入前者肯定为 true
// 如果需要第一次不执行函数
// 就将上次时间戳设为当前的
// 这样在接下来计算 remaining 的值时会大于0
if (!previous && options.leading === false) previous = now
// 计算剩余时间
var remaining = wait - (now - previous)
context = this
args = arguments
// 如果当前调用已经大于上次调用时间 + wait
// 或者用户手动调了时间
// 如果设置了 trailing,只会进入这个条件
// 如果没有设置 leading,那么第一次会进入这个条件
// 还有一点,你可能会觉得开启了定时器那么应该不会进入这个 if 条件了
// 其实还是会进入的,因为定时器的延时
// 并不是准确的时间,很可能你设置了2秒
// 但是他需要2.2秒才触发,这时候就会进入这个条件
if (remaining <= 0 || remaining > wait) {
// 如果存在定时器就清理掉否则会调用二次回调
if (timeout) {
clearTimeout(timeout)
timeout = null
}
previous = now
result = func.apply(context, args)
if (!timeout) context = args = null
} else if (!timeout && options.trailing !== false) {
// 判断是否设置了定时器和 trailing
// 没有的话就开启一个定时器
// 并且不能不能同时设置 leading 和 trailing
timeout = setTimeout(later, remaining)
}
return result
}
}

深拷贝浅拷贝

发表于 2018-03-27
字数统计 324 字 | 阅读时长 1 分钟

定义

JavaScript中,基本类型以及引用类型的值存储方式是不一样的,基本类型的值保存在栈中。而引用类型的值保存在堆中,并在栈中保存一个指向堆的地址。因而在复制中,直接用等号复制引用类型的值只能复制地址,如果修改复制的变量也会影响原变量。如果新开辟一块内存,把引用类型中所有属性复制过去,这种拷贝就是深拷贝,否则就是浅拷贝。

阅读全文 »

JavaScript异步

发表于 2018-03-15
字数统计 1k 字 | 阅读时长 3 分钟

Javascript单线程

JavaScript作为一种脚本语言,其本身是单线程的,也就是说同一时间只能做一件事。其原因是作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,就会造成错误。

阅读全文 »

http相关知识

发表于 2018-03-11
字数统计 2k 字 | 阅读时长 7 分钟

HTTP 报文

HTTP 报文是在 HTTP 应用程序之间发送的数据块。和谐数据块以以西文本形式的元信息开头,这些信息描述了报文的内容及含义,后面跟着可选的数据部分。

报文的组成部分

  • 起始行
  • 头部
  • 主体
    阅读全文 »

作用域链,闭包

发表于 2018-03-05
字数统计 2.2k 字 | 阅读时长 8 分钟

执行环境及作用域

概念

执行环境定义了变量或函数有权访问其他数据,决定他们各自的行为。每个环境中都有一个与之关联的变量对象(variable object),环境中定义的所有变量和函数都保存在变量对象中,解析器会在处理数据时在后台使用它。

全局执行环境是最外层的执行环境,web浏览器中默认是window对象,因此所有的全局变量和对象都是作为window对象的属性创建的。环境中的代码执行完毕后,环境会被销毁,其中保存的变量和函数也会销毁。

每个函数都有自己的执行环境。当执行流进入一个函数是,函数的环境就会被推入一个环境栈,函数执行完成后再出栈。

代码在一个环境中执行的时候会创建一个作用域链,它保证了对执行环境有权访问的所有变量和函数的有序访问。作用域链的前端,始终都是当前执行代码所在环境的变量对象。如果环境是函数则将其活动对象作为变量对象。活动对象最开始只包含一个变量:arguments对象。作用域链下一个变量对象就是外部环境,再下一个就是外部的外部,一直到全局。

标识符解析就是沿着作用域链一级一级搜索标识符的过程,总是从前端开始,向后回溯。

阅读全文 »

123
Renko

Renko

30 日志
26 标签
© 2023 Renko
由 Hexo 强力驱动
主题 - NexT.Pisces
本站总访问量 次    本站访客数人次