不要再说虚拟 DOM 有多快了

如果你觉得它很快,那么这篇文章可能就是你所缺少的 我经常听到有人在群里,或者在社区里说的一个很严重的错误,那就是说 React 的 Virtual Dom 是以快出名的,比原生 Dom 快多了,啥啥啥的,每次都一两句话说不清楚,所以下次有谁再说 React 是以快出名的,你就把这篇文章丢给他,下面进入正题。 在过去的几年里,你一直在跟踪 JavaScript 社区的发展,你至少听说过 Virtual DOM(React,Vue.js 2,Riot.js,Angular 2等等)。他们承诺(或者更确切地说,他们的宣传)更快的渲染界面,特别是更新,减少麻烦。你很快的上手了使用虚拟DOM的应用程序,这很好。几个月后,您的应用程序现在变得越来越复杂,你可能从用户交互到屏幕更新只需要一两秒钟的更新。你可能会想,这东西很神奇,应该会比 jQuery 快,但是实际上不是这个样子的。 虽然我同意虚拟 DOM 为我们提供了很多便利,但我将解释为什么我认为根据定义,更快的渲染和更快的更新是不正确的。要付出代价,其利益并不是大多数人想象或至少希望的。 要阅读本文,您需要熟悉DOM。理想情况下,您至少可以使用 DOM API。如果你只使用 DOM API 构建东西,你可能不需要这篇文章,但我仍然希望你阅读它并在评论中留下一点评语。 渲染和更新 让我们来看看手动执行 DOM 节点的创建和更新的鸟瞰图。这对于理解虚拟DOM如何工作以及它解决了哪些问题非常重要。 在谈论 JavaScript Web 应用程序时,用户界面的更改通过 DOM 操作发生。这个过程分为两个阶段: JS 部分:定义 JavaScript 世界中的变化 DOM 部分:使用 DOM API 函数和属性执行更改 性能是根据整个过程的速度来衡量的,但了解每部分的速度也很重要,以便了解要优化的内容。 有两种方法可以创建和更新DOM树的各个部分。 ①字符串方式创建 使用字符串既快速又简单,但在更新方面并不是非常精细。对于字符串,JS部分是它如此之快的原因。您可以在几毫秒内创建一段代表5000个节点的HTML。这是一个例子: const userList = document.getElementById("user-list"); // JS 部分 const html = users.map(function (user) { return ` <div id="${user.id}" class=”user”> <h2 class="header">${user....

November 20, 2018 · 2 min · 352 words · 桃翁

浅谈未来几年前端的发展方向

在知乎上看到这么一个问题,觉得很有意思,以下是原提问者的见解 过去五年前端的发展过程基本上是一个工程化的过程,框架和工程化工具层出不穷。 近两年其实发展已经比较迟滞了。 框架方面:基本就是三大框架鼎立的局面,三大框架都在相互借鉴吸收,而且方向各有侧重,未来短时间内我看格局不可能有什么大变化. 工程化工具:基本上是 webpack 一统江湖的趋势,虽然有 parcel 等来小打小闹,但是生态一旦形成,没有革命性的项目是无法取代 webpack 的,而且 webpack 也在进化. 个人认为前面五年是前端生产力提高的五年,工程化使得前端的生产力得到了极大提升,但是现在也基本上是在已有的格局中修修补补了 我谈谈我对前端未来几年的发展方向的看法。 看未来的发展方向,无非就是看现在的解决方案所存在的痛点。 1. 浏览器的性能问题 做 web 前端的同学都知道,和原生的 App 相比,性能一直一个致命的痛点,如果要追求性能,肯定得用原生 App。那么在性能上,未来几年可能是一个方向。 ①前端代码编译为字节码 浏览器这几年在 Chrome 的带动下,性能飞速发展,但毕竟其核心原理没有变化,性能始终难以达到原生 App 的水平,这部分是很有可能出现大的变化的,一个可能的方向就是浏览器变成虚拟机,前端代码编译为字节码,通过这种方式来将性能提升一个等级,虽然还是难以达到原生App的水平,但已经能够满足绝大部分应用的性能需求,类似于Java对比C/C++一样。 –李运华 因为 js 是边解释边执行的,这肯定是要比编译型语言要慢,为了解决解释器的低效问题,大概在 2008 年的时候,提出了 JIT 的概念,它是使 JavaScript 运行更快的一种手段(JIT,内联缓存和隐藏类)之一,通过监视代码的运行状态,把 hot 代码(重复执行多次的代码)进行优化。通过这种方式,可以使 JavaScript 应用的性能提升很多倍。 但是时至今日,还是觉得不够快,所以各大浏览器厂商开始支持 WebAssembly。WebAssembly 是一种新的字节码格式,主流浏览器都已经支持 WebAssembly。 和 JS 需要解释执行不同的是,WebAssembly 字节码和底层机器码很相似可快速装载运行,因此性能相对于 JS 解释执行大大提升。 也就是说 WebAssembly 并不是一门编程语言,而是一份字节码标准,需要用高级编程语言编译出字节码放到 WebAssembly 虚拟机中才能运行. 他的优点就是: 体积小:由于浏览器运行时只加载编译成的字节码,一样的逻辑比用字符串描述的 JS 文件体积要小很多; 加载快:由于文件体积小,再加上无需解释执行,WebAssembly 能更快的加载并实例化,减少运行前的等待时间; 目前可以编译成为 WebAssembly 字节码有 :AssemblyScript(语法跟 TS 差不多,)、c\c++、Rust、Kotlin。...

November 13, 2018 · 1 min · 132 words · 桃翁

一篇文章搞定 ES6

JavaScript ES6 带来了新的语法和新的强大功能,使您的代码更现代,更易读。它允许您编写更少的代码并执行更多操作。 ES6 向我们介绍了许多强大的功能,如箭头函数,模板字符串,对象结构,模块等,让我们来看看。 const and let const 是 ES6 中用于声明变量的新关键字。 const 比 var 更强大。使用后,无法重新分配变量。换句话说,它是一个不可变的变量,除非它与对象一起使用。 这对于定位选择器非常有用。例如,当我们有一个触发事件的按钮时,或者当您想在 JavaScript 中选择 HTML 元素时,请使用 const 而不是 var。这是因为 var 会被提升,当不想重新分配变量时,最好使用 const。 // ES5 var MyBtn = document.getElementId('mybtn'); // ES6 const MyBtn = document.getElementById('mybtn'); 在上面的代码中,const 不会更改,也不能重新分配。如果您尝试为其赋予新值,则会返回错误。 let name = "Said"; name = "Rick"; console.log(name); // Rick let 可以重新分配并获得新的价值。它创建了一个可变变量。 let 与 const 相同,因为两者都是块级作用域,这意味着该变量仅在其块级范围内可用。 箭头函数 箭头函数非常棒,使您的代码更具可读性,更具结构性,并且看起来像现代代码,而不是使用这个: // ES5 function myFunc(name) { return 'Hello ' + name; } console....

September 29, 2018 · 4 min · 784 words · 桃翁

JavaScript核心概念之执行上下文和栈

桃翁桃翁,问个问题呢,据说 js 里面有个执行上下文,这个概念是个什么东东哦?据说挺重要的,给我科普科普呗。 Emm… 这个概念非常的抽象,简单来说呢,就是 JS 在执行某段代码的时候做的一些事情。 具体做的事情就是定义了变量或函数有权访问的其他数据决定了它们各自的行为(作用域链)。每个执行环境都有一个与之关联的变量对象(variable object),环境中定义的所有变量和函数都保存在这个对象中(变量包括 this、arguments)。虽然我们编写的代码无法访问这个对象,但解析器在处理数据时会在后台使用它。 哇,还是好抽象啊,你能不能画个图举个栗子呢? 在之前说的执行上下文就是解释器在执行 JS 某段代码的时候做的一些事,那么首先我们把代码分个类。 Global 代码:代码第一次执行时默认的环境。 Function 代码:执行到一个函数中。 Eval 代码:文本在eval函数内部执行。 看到这个图相信现在分清楚各种类型的代码,每种类型代码会都会产生执行上下文,我们把 Global 代码产生的执行环境叫**「全局执行上下文」,把 Function 代码产生的执行环境叫「执行上下文」**吧,Eval 代码不考虑。 那我看这个图似乎有很多执行上下文(execution context),这个具体是怎么来的呢? 全局执行上下文只有一个,而执行环境的话是每次函数调用都会产生一个执行上下文。注意要调用才会产生哦,不调用是不会产生的。 那这个执行上下文基本知道是个什么东西了,那执行上下文栈又是啥呢? 见名知意,执行上下文栈就是执行上下文(包含全局执行上下文)形成的栈嘛。 那为什么要有这个执行上下文栈呢? 浏览器中 JavaScript 解释器是单线程的,这就是说同一时间代码只会做一件事,那么创建这么多执行上下文,又不能同一时间执行多个上下文,所以就必须要有个顺序,这个顺序就是就是先进后出,这很明显就是一个栈结构嘛。 那我就疑惑了,为啥要先进后出,不先进先出呢? 我们分析一下图一的代码,结合上图,首先我们看图 1,解释代码的时候首先创建的就是全局上下文,然后再创建 person 的执行上下文,然后再创建 firstName 的上下文,然后再执行完毕 firstName ,就把 firstName 的上下文弹出,再 创建 lastName 的上下文,然后执行完毕,再弹出 lastName 的上下文,然后执行完 person 的上下文,再弹出 person 的上下文,再执行全局上下文,然后全局上下文弹出。 如下是一张经典的执行上下文栈的图。 默认进入全局上下文。如果你的全局代码中调用了一个函数,那么程序将会进入这个被调用函数的上下文,创建一个新的执行上下文,并把当前上下文放到栈顶。浏览器总是会把当前执行上下文放到栈的顶部,一旦函数执行完成,这个执行上下文就会从栈中移除,返回到栈中的下一个上下文。 这些大概明白了,不过你说在创建执行上下文做的那些事儿,我还是有点迷糊,能再详细说说吗? 那我们首先看点代码: // 例1 console.log(a); // 报错,a is not defined // 例2 console....

September 25, 2018 · 1 min · 169 words · 桃翁

JavaScript核心概念(1):类型转换

看到这个是不是有一种想打人的感觉,垃圾 JavaScript,这特么都什么鬼,相信很多人不管是笔试还是面试,都被 JS 的类型转换难道过,相信认真看完我这篇文章,妈妈再也不用担心类型转换的问题了。 原始值到原始值的转换 原始值转化为布尔值 所有的假值(undefined、null、0、-0、NaN、””)会被转化为 false,其他都会被转为 true 原始值转化为字符串 都相当于 原始值 + "" 原始值转为数字 布尔转数字:true -> 1, false -> 0 字符串转数字:以数字表示的字符串可以直接会转为字符串,如果字符串头尾有空格会忽略,但是空格在中间,转换结果就是 NaN。 +" 66" // 66 +" 6 7 " // NaN 原始值到对象的转换 null 和 undefined 转对象直接抛异常 原始值通过调用 String()、Number()、Boolean()构造函数,转换为他们各自的包装对象 对象到原始值的转换 对象转为布尔都为 true 对象到字符串 如果对象有 toString() 方法,就调用 toString() 方法。如果该方法返回原始值,就讲这个值转化为字符串。 如果对象没有 toString() 方法或者 该方法返回的不是原始值,就会调用该对象的 valueOf() 方法。如果存在就调用这个方法,如果返回值是原始值,就转化为字符串。 否则就报错 对象到数字 对象转化为数字做了跟对象转化为字符串做了想同的事儿,不同的是后者是先调用 valueOf 方法,如果调用失败或者返回不是原始值,就调用 toString 方法。 补充。一些常用内置对象 toString 方法和 valueOf 的转换规则 toString 相关 valueOf 相关 == 运算符如何进行类型转换 如果一个值是null,另一个值是undefined,则相等 如果一个是字符串,另一个值是数字,则把字符串转换成数字,进行比较 如果任意值是true,则把true转换成1再进行比较;如果任意值是false,则把false转换成0再进行比较 如果一个是对象,另一个是数值或字符串,把对象转换成基础类型的值再比较。对象转换成基础类型,利用它的 toString 或者 valueOf 方法。 js 核心内置类,会尝试 valueOf 先于 toString(可以理解为对象优先转换成数字);例外的是 Date,Date 利用的是 toString 转换。非 js 核心的对象,通过自己的实现中定义的方法转换成原始值。 + 运算符如何进行类型转化 如果作为一元运算符就是转化为数字,常常用来将字符串转化为数字 +“2” // 2 2+false // 0 ```...

August 7, 2018 · 1 min · 181 words · 桃翁