Giter VIP home page Giter VIP logo

gogogo's Introduction

gogogo's People

Contributors

oliver1204 avatar

Watchers

 avatar  avatar

gogogo's Issues

Monorepo 代码管理方式

目前有不少大型开源项目采用了Monorepo如 Babel, pro-components, react-router等。monorepo 最主要的好处是统一的工作流Code Sharing

monorepo 是什么

monorepo 是把多个项目的所有代码放到一个 git 仓库中进行管理,多个项目既有共享的代码又可以分包引用。整个项目就是有 root 管理的 dependencies 加上多个 packages,每个 package 也可以在自己的作用域引入自己的 dependencies。

monorepo 方案实践

1. 锁定环境:Volta

volta 是一个 JavaScript 工具管理器,它可以让我们轻松地在项目中锁定 node,npm 和 yarn 的版本。

2. 管理工具: Lerna

monorepo 管理工具 lerna

react 性能优化

使用生产版本

  1. 生产环境是用生产版本,React开发版本 默认包含了许多有用的警告信息。这些警告信息在开发过程中非常有帮助。然而这使得 React 变得更大且更慢,所以你需要确保部署时使用了生产版本。

尽量使用单页应用

下图是使用react-router 构建的项目,在 Chrome 的 Performance 下 观察到的初始渲染所需要的时间:
image
)

跳转每个界面,渲染react-router 组件都需要浪费50%以上的时间。

使用react-tools

对于react 16.0 以上的版本,react-tools 添加了 Profiler 项。在页面更新时,变化部分会有彩色的框框。如下:

image

通过分析我们知道,在切换「积分排名」和「积分明细」时, 其实上部分是没有必要变化的。 虽然 react 有 VDom ,但是尽量避免非要的更新对于复杂的项目还是有很大的好处的。

柯理化函数

编程**

柯里化其实是函数式编程的一个过程,体现了JS的预处理能力,它将把含有N个参数的函数转变成,N个只有一个参数的函数。

让我们来看一个简单的示例:

function multiply(a, b, c) {
    return a * b * c;
}

柯里化后的版本:

function multiply(a) {
    return (b) => {
        return (c) => {
            return a * b * c
        }
    }
}
log(multiply(1)(2)(3)) // 6

我们已经将 multiply(1,2,3) 函数调用转换为多个 multiply(1)(2)(3) 的多个函数调用。

我们可以拆分 multiply(1)(2)(3) 以便更好的理解它:

const mul1 = multiply(1);
const mul2 = mul1(2);
const result = mul2(3);
console.log(result); // 6

预处理

上面说了,柯里化体现了JS的预处理能力,其实更多的是预处理this指向的问题,处理this指向问题,JS提供了两个方法call() 和 apply() 方法。但是我们在有些时候并不想让方法立即执行,这个时候使用H5中新增的方法bind() ,bind方法体现出了柯理化函数**,通俗点就是他可以将函数中的this指向改变但同时不立即运行方法,等需要运行的时候再运行。使用bind,返回改变上下文this后的函数

var p = {
	name: 'Manster'
};
function person(name,age){
   console.log(this.name,age)
}
var fn = person.bind(p,'manster','23');
fn()

但是es6后这问题应该自然的到来解决。

例如,第一个multiply例子:
用es6的写法是:

const multiply = a => b => c => a + b + c;

作用

1. 参数复用

var add = function(a) {
     return function(b) {
         return a + b;
     };
};
var addTen = add(10);
addTen(2); // 12

首先柯里化可以使得参数复用并且延迟计算,也就是说像上面的例子,比如我们的 a 值一直都是 10,只是 b 值在变化,那么这个时候用到柯里化,就可以减少一些重复性的传参。

比如 b 是 2,3,4

那么在柯里化前的调用就是:

add(10,2)
add(10,3)
add(10,4)

而在柯里化之后,就像上面的例子中写的,我们通过定义

var addTen = add(10);

将第一个参数 a 预置了 10,

之后我们只需要调用

addTen(2)
addTen(3)
addTen(4)

固定一个参数不变的柯里化函数也叫偏函数。

2. 延迟计算

而在这个过程中,如果使用柯里化前的代码,或当即就把结果计算出来,而在柯里化之后,我们可以现传入一个 10,然后在想得到真正结果的时候再传入另一个参数,体现了延迟计算。

3. 提前返回

兼容现代浏览器以及IE浏览器的事件添加方法。我们正常情况不使用柯里化可能会这样写

var addEvent = function(el, type, fn, capture) {
    if (window.addEventListener) {
        el.addEventListener(type, function(e) {
            fn.call(el, e);
         }, capture);
     } else if (window.attachEvent) {
        el.attachEvent("on" + type, function(e) {
           fn.call(el, e);
        });
     } 
 };

这个时候我们没调用一次 addEvent,就会进行一次 if else 的判断,而其实具体用哪个方法进行方法的绑定的判断执行一次就已经知道了,所以我们可以使用柯里化来解决这个问题:

var addEvent = (function() {
    if (window.addEventListener) {
         return function(el, sType, fn, capture) {
         		el.addEventListener(sType, function(e) {
            		fn.call(el, e);
         		}, (capture));
         }
     } else if (window.attachEvent) {
     	  return function(el, sType, fn, capture) {
        		el.attachEvent("on" + sType, function(e) {
           		fn.call(el, e);
        		});
        }
     } 
 })();

一开始的自执行函数,完成了对 addEvent 具体使用 哪个方法的判断,之后在调用传参的时候都是直接给了已经判断好的返回方法,所以使用了柯里化 减少了我们每次的判断,提前返回了我们需要的具体方法。

javascript 垃圾收集

程序运行需要内存,只要程序需要,操作系统就必须提供内存。 javascript 使用的是自动内存管理,被称为“垃圾回收机制”。

1.1 nodejs 中的内存管理

对于持续运行的服务进程,node 服务器程序,必须及时释放不再用到的内存,否则内存阅历啊越多,轻则影响系统性能,重则导致进程崩溃。如果用到的内存没有及时清除则会造成“内存泄露”。

1.2 v8 内存管理

1.2.1 v8 内存限制

  • 在 64 位操作系统中可以使用 1.4G 内存
  • 在 32 位操作系统中可以使用 0.7G 内存

1.2.2 v8 内存管理

  • javascript 对象都是通过 v8 来管理内存的
  • process.memoryusage 收集每个页面有关内存使用情况的信息
    heapTotal 表示已经使用了的,heapTotal 剩余可使用量

1.2.3 为什么会有内存大小限制

v8 垃圾收集工作原理导致,收集 1.4G 意思的垃圾需要超过 1 秒,在这个期间内性能和响应能力都会下降。

2. v8 垃圾回收机制

v8 是基于分代的垃圾回收,不同代垃圾回收机制也是不一样的,按存活的时间分为新生代和老生代。

2.1 分代

  • 年龄小的是新生代,由 From 区域(已使用空间)和 To 区域(空闲空间)两个区组成, 例如:一个局部作用域中,只要函数执行完毕之后变量就会回收。
    • 64 位系统中,新生代内存是 32M, From 区域和 To 区域 各占 16M
    • 32 位系统中,新生代内存是 16M, From 区域和 To 区域 各占 8M
  • 年龄大的是老生带, 例如:全局对象,闭包变量数据。

image

2.2 新生代对象回收

2.2.1 主要使用算法

采用赋值算法 + 标记整理算法

2.2.2 回收过程

  1. 首先会将所有活动对象存储于 From 空间,这个过程中 To 是空闲状态。
  2. 当 From 空间使用到一定程度之后就会触发 GC 操作,这个时候会进行标记整理对活动对象进行标记并移动位置将使用空间变得连续,便于后续不会产生碎片化空间。
  3. 将活动对象拷贝至 To 空间,拷贝完成之后活动空间就有了备份,这个时候就可以考虑回收操作了。
  4. 把 From 空间完成释放,回收完成
  5. 对 From 和 To 名称进行调换,继续重复之前的操作。

总结就是:
使用 From -> 触发 GC 标记整理 -> 拷贝到 To -> 回收 From -> 名称互换重复之前

2.3 晋升

什么时候触发晋升操作?

  1. 变量由 From 到 To,然后由 To 到 From 超过 5 次后还存活的新生代对象需要晋升为老生代
  2. 在拷贝过程中,To 空间的使用率超过 25%,将这次的活动对象都移动至老生代空间

2.4 回收老生代对象

主要采用标记清除 (首要) 、标记整理、增量标记算法

  • 标记清除:虽然使用标记清除会有空间碎片化的问题,但是标记清除提升的速度是很快的。
    例如: a (b) c (d) e 。b,d 是要被清除的,但是是清除而已,使用空间不会移动
  • 标记整理:在晋升的时候且老生代区域的空间也不够容纳的时候,就会采用标记整理进行 空间优化。
    ace(b)(d)。要被清除的 bd 会被移动到最右边,一并清除。清除后空间是还可以重复利用
  • 增量标记:将一整段的垃圾回收操作标记拆分成多个小段完成回收,主要是为了实现程序和垃圾回收的交替完成,这样进行 效率优化 带来的时间消耗更加的合理。

3. 循环引用 垃圾回收

3.1 引用计数

引用计数是一种垃圾回收的形式,每一个对象都会有一个计数来记录有多少指向它的引用。其引用计数会变换如下面的场景

  • 当对象增加一个引用,比如赋值给变量,属性或者传入一个方法,引用计数执行加 1 运算。
  • 当对象减少一个引用,比如变量离开作用域,属性被赋值为另一个对象引用,属性所在的对象被回收或者之前传入参数的方法返回,引用计数执行减 1 操作。
  • 当引用计数变为 0,代表该对象不被引用,可以标记成垃圾进行回收。

3.2 引用对象遍历 (主要方式)

垃圾回收器从被称为 GC Roots 的点开始遍历遍历对象,凡是可以达到的点都会标记为存活,堆中不可到达的对象都会标记成垃圾,然后被清理掉。

JS的节流与防抖

函数节流(throttle)

规定在一个单位时间内,只能触发一次函数。如果这个单位时间内触发多次函数,只有一次生效。
看一个🌰

  function throttle(fun, delay) {
        let last, deferTimer
        return function (args) {
            let that = this
            let _args = arguments
            let now = +new Date()
            if (last && now < last + delay) {
                clearTimeout(deferTimer)
                deferTimer = setTimeout(function () {
                    last = now
                    fun.apply(that, _args)
                }, delay)
            }else {
                last = now
                fun.apply(that,_args)
            }
        }
    }

    let throttleAjax = throttle(ajax, 1000)

    let inputc = document.getElementById('throttle')
    inputc.addEventListener('keyup', function(e) {
        throttleAjax(e.target.value)
    })

image

可以看到,我们在不断输入时,ajax会按照我们设定的时间,每1s执行一次。

结合刚刚biubiubiu的🌰

let biubiu = function () {
        console.log('biu biu biu', new Date().Format('HH:mm:ss'))
    }

setInterval(throttle(biubiu,1000),10)


不管我们设定的执行时间间隔多小,总是1s内只执行一次。

函数防抖(debounce)

在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时。

看一个🌰(栗子):

/模拟一段ajax请求
function ajax(content) {
  console.log('ajax request ' + content)
}

let inputa = document.getElementById('unDebounce')

inputa.addEventListener('keyup', function (e) {
    ajax(e.target.value)
})

image

可以看到,我们只要按下键盘,就会触发这次ajax请求。不仅从资源上来说是很浪费的行为,而且实际应用中,用户也是输出完整的字符后,才会请求。下面我们优化一下

//模拟一段ajax请求
function ajax(content) {
  console.log('ajax request ' + content)
}

function debounce(fun, delay) {
    return function (args) {
        let that = this
        let _args = args
        clearTimeout(fun.id)
        fun.id = setTimeout(function () {
            fun.call(that, _args)
        }, delay)
    }
}
    
let inputb = document.getElementById('debounce')

let debounceAjax = debounce(ajax, 500)

inputb.addEventListener('keyup', function (e) {
        debounceAjax(e.target.value)
})

image

可以看到,我们加入了防抖以后,当你在频繁的输入时,并不会发送请求,只有当你在指定间隔内没有输入时,才会执行函数。如果停止输入但是在指定间隔内又输入,会重新触发计时。 再看一个🌰

let biu = function () {
    console.log('biu biu biu',new Date().Format('HH:mm:ss'))
}

let boom = function () {
    console.log('boom boom boom',new Date().Format('HH:mm:ss'))
}

setInterval(debounce(biu,500),1000)
setInterval(debounce(boom,2000),1000)

image

这个🌰就很好的解释了,如果在时间间隔内执行函数,会重新触发计时。biu会在第一次1.5s执行后,每隔1s执行一次,而boom一次也不会执行。因为它的时间间隔是2s,而执行时间是1s,所以每次都会重新触发计时

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.