作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.
胡安·卡洛斯·阿里亚斯·安布里兹
Verified Expert in Engineering

Juan拥有超过10年的自由用户体验经验. 他的作品植根于完美主义 & 为用户提供最好的体验.

Expertise

PREVIOUSLY AT

Meta
Share

很少有人不喜欢框架, 但即使你是他们中的一员, 您应该注意并采用使生活更轻松的功能.

我反对使用框架 in the past. 然而,最近,我有了和 React and Angular 在我的一些项目中. The first couple of times I opened my code editor and started writing code in Angular it felt weird and unnatural; especially after more than ten years of coding without using any frameworks. 过了一段时间,我决定致力于学习这些技术. 很快,一个很大的区别就显现出来了:操纵DOM非常容易, 所以在需要的时候很容易调整节点的顺序, 它不需要一页又一页的代码来构建一个UI.

尽管我仍然喜欢不依附于框架或体系结构的自由, 我不能忽视这样一个事实:在一个容器中创建DOM元素要方便得多. 所以我开始寻找在普通JS中模仿这种体验的方法. 我的目标是从React中提取出一些想法,并演示如何在普通JavaScript(通常称为vanilla JS)中实现相同的原则,以使开发人员的工作更轻松一点. 为了实现这一点,让我们构建一个简单的应用程序来浏览GitHub项目.

简单的GitHub搜索应用程序

我们正在开发的应用程序.

无论我们用哪种方式使用JavaScript构建前端, 我们将访问和操作DOM. 对于我们的应用程序, 我们需要构造每个存储库的表示(缩略图), name, and description), 并将其作为列表元素添加到DOM中. 我们将使用 GitHub Search API 来获取结果. 既然我们讨论的是JavaScript,让我们来搜索JavaScript存储库. When we query the API,我们得到以下JSON响应:

{
    “total_count”:398819年,
    “incomplete_results”:假的,
    "items": [
        {
            "id": 28457823,
            “名称”:“freeCodeCamp”,
            :“full_name freeCodeCamp / freeCodeCamp”,
            "owner": {
                “登录”:“freeCodeCamp”,
                "id": 9892522,
                :“avatar_url http://avatars0.githubusercontent.com/u/9892522?v=4",
                “gravatar_id”:“”,
                :“url http://api.github.com/users/freeCodeCamp”,
                “site_admin”:假的
            },
           “私人”:假的,
           :“html_url http://github.com/freeCodeCamp/freeCodeCamp”,
           description: http://freeCodeCamp.Org开源代码库”+
                          "and curriculum. 学习编程并帮助非营利组织.",
           //更多被省略的信息
        }, //...
    ]
}

React’s approach

React使得将HTML元素写入页面变得非常简单,并且是我在用纯JavaScript编写组件时一直想要的功能之一. React uses JSX,这与常规HTML非常相似.

然而,这不是浏览器读取的内容.

在底层,React将JSX转换为对a的一系列调用 React.createElement function. 让我们看一个使用GitHub API中的一个项目的JSX示例,看看它转换成什么.

{item.name}

{item.description}

;
;
React.createElement(
    "div",
    {className: "repository"},
    React.createElement(
        "div",
        null,
        item.name
    ),
    React.createElement(
        "p",
        null,
        item.description
    ),
    React.createElement(
        "img", 
       { src: item.owner.avatar_url }
    )
);

JSX非常简单. 编写常规HTML代码,并通过添加大括号从对象注入数据. 括号内的JavaScript代码将被执行, 并将值插入到生成的DOM中. JSX的优点之一是React创建了一个虚拟DOM(页面的虚拟表示)来跟踪更改和更新. 而不是重写整个HTML, 每当信息更新时,React都会修改页面的DOM. 这是创建React要解决的主要问题之一.

jQuery approach

开发人员过去经常使用jQuery. 我想在这里提到它,因为它仍然很流行,也因为它非常接近纯JavaScript的解决方案. jQuery通过查询DOM获取对DOM节点(或DOM节点集合)的引用. 它还用修改其内容的各种功能包装了该引用.

而jQuery有自己的DOM构造工具, 我在野外看到的最多的就是HTML连接. 方法将HTML代码插入到选定的节点中 html() function. According to jQuery文档,如果我们想改变a的内容 div 节点与类 demo-container 我们可以这样做:

$( "div.demo-container”).html( "

All new content.You bet!

" );

This approach makes it easy to create DOM elements; however, 当我们需要更新节点时, 我们需要查询所需的节点,或者(更常见的是)在需要更新时退回到重新创建整个代码片段.

DOM API approach

浏览器中的JavaScript有一个内置的 DOM API 这使我们能够直接访问页面中的节点的创建、修改和删除. 这反映在React的方法中, 以及通过使用DOM API, 我们离这种方法的好处又近了一步. 我们只修改页面中实际需要更改的元素. 但是,React还跟踪一个单独的虚拟DOM. 通过比较虚拟DOM和实际DOM之间的差异, 然后React能够识别哪些部分需要修改.

这些额外的步骤有时是有用的, but not always, 而直接操作DOM会更有效率. 创建新的DOM节点 _document.createElement_ 函数,该函数将返回对所创建节点的引用. 跟踪这些引用为我们提供了一种简单的方法,可以只修改包含需要更新部分的节点.

使用与JSX示例中相同的结构和数据源, 我们可以用下面的方法构造DOM:

Var item =文件.createElement (div);
item.className = 'repository';

var nameNode = document.createElement (div);
nameNode.innerHTML = item.name
item.列表末尾(nameNode);

Var description =文档.createElement(“p”);
description.innerHTML = item.description;
item.列表末尾(描述);

var image = new image ();
Image.src = item.owner.avatar_url;
item.列表末尾(图片);

document.body.列表末尾(项);

如果您唯一考虑的是代码执行的效率,那么这种方法非常好. However, 效率不仅仅是用执行速度来衡量的, 而且也便于维护, scalability, and plasticity. 这种方法的问题是它非常冗长,有时令人费解. 即使我们只是构造一个基本的结构,我们也需要编写一堆函数调用. 第二个大的缺点是创建和跟踪的变量的绝对数量. Let’s say, 您正在使用的组件包含自己的30个DOM元素, 您需要创建和使用30个不同的DOM元素和变量. 您可以重用其中的一些,并以牺牲可维护性和可塑性为代价进行一些杂耍, 但它会变得非常混乱, really quickly.

另一个明显的缺点是由于需要编写的代码行数. 随着时间的推移,将元素从一个父元素移动到另一个父元素变得越来越困难. 这是我非常欣赏React的一点. 我可以查看JSX语法,并在几秒钟内得到包含的节点, where, 并在需要时进行更改. And, 虽然乍一看这没什么大不了的, 大多数项目都有不断的变化,这将促使你寻找更好的方法.

建议的解决方案

直接使用DOM可以完成工作, 但它也使构建页面变得非常冗长, 特别是当我们需要添加HTML属性和嵌套节点时. So, 我们的想法是获得使用JSX等技术的一些好处,并使我们的生活更简单. 我们试图复制的优势如下:

  1. 用HTML语法编写代码,使DOM元素的创建变得易于阅读和修改.
  2. 因为我们没有像React那样使用等价的虚拟DOM, 我们需要有一种简单的方法来指示和跟踪我们感兴趣的节点.

下面是一个简单的函数,它可以使用一个HTML片段完成这个任务.

Browser.DOM = function (html, scope) {
   //创建空节点并注入html字符串using .innerHTML 
   //如果变量不是字符串,我们假定它已经是一个节点
   var node;
   if (html.构造函数===字符串){
       Var节点=文件.createElement (div);
       node.innerHTML = html;
   } else {
       node = html;
   }

   //创建用户和对象,我们将为其创建变量
   //指向已创建的节点

   Var _scope = scope || {};

   //将读取每个节点的递归函数
   //包含var属性,在scope对象中添加一个引用

   函数toScope(节点,作用域){
       Var children = node.children;
       for (var iChild = 0; iChild < children.length; iChild++) {
           如果(儿童(iChild).getAttribute (var)) {
               var names = children[icchild].getAttribute(“var”).split('.');
               var obj = scope;
               while (names.length > 0)
               {
                   Var _property = names.shift();
                   if (names.length == 0)
                   {
                       obj[_property] = children[icchild];
                   }
                   else
                   {
                       if (!obj.hasOwnProperty (_property)) {
                           Obj [_property] = {};
                       }
                       Obj = Obj [_property];
                   }
               }
           }
           toScope(儿童(iChild)、范围);
       }
   }

   toScope(节点,_scope);

   if (html.constructor != String) {
       return html;
   }
   //如果最高层次结构中的节点是1,则返回它

   if (node.childNodes.length == 1) {
    //如果没有设置添加节点变量的作用域
    //将我们创建的对象附加到最高层次结构节点
    
       //添加nodes属性.
       if (!scope) {
           node.childNodes[0].nodes = _scope;
       }
       return node.childNodes[0];
   }

   //如果最高层次结构中的节点多于一个,则返回一个fragment
   Var fragment = document.createDocumentFragment ();
   Var children = node.childNodes;
   
   //在DocumentFragment中添加注释
   while (children.length > 0) {
       if (fragment.append){
           fragment.追加(孩子[0]);
       }else{
          fragment.列表末尾(孩子[0]);
       }
   }

   fragment.nodes = _scope;
   return fragment;
}

The idea is simple but powerful; we send the function the HTML we want to create as a string, 在HTML字符串中,我们将var属性添加到我们想要为我们创建引用的节点. 第二个参数是一个对象,这些引用将存储在其中. 如果没有指定,我们将在返回的节点或文档片段上创建一个“nodes”属性(如果最高层次节点多于一个)。. 一切都在不到60行的代码中完成.

该函数分为三个步骤:

  1. 创建一个新的空节点,并在该新节点中使用innerHTML来创建整个DOM结构.
  2. 遍历节点并查看var属性是否存在, 在范围对象中添加指向具有该属性的节点的属性.
  3. 返回层次结构中的顶部节点,如果有多个,则返回文档片段.

那么现在呈现示例的代码是什么样子的呢?

var UI = {};
Var template = ";
template += '
' template += '
'; template += '

' template += ' ' template += '
'; var item =浏览器.DOM(模板,UI); UI.name.innerHTML = data.name; UI.text.innerHTML = data.description; UI.image.src = data.owner.avatar_url;

首先,定义对象(UI),我们将在其中存储对所创建节点的引用. 然后,我们编写将要使用的HTML模板, as a string, 用“var”属性标记目标节点. 之后,我们调用Browser函数.使用模板和将存储引用的空对象. 最后,我们使用存储的引用将数据放置在节点中.

这种方法还将构建DOM结构和将数据插入到单独的步骤中,这有助于保持代码的组织性和良好的结构. 这使我们能够分别创建DOM结构并在数据可用时填充(或更新)数据.

Conclusion

虽然我们中的一些人不喜欢切换到框架并交出控制权的想法, 认识到这些框架带来的好处是很重要的. 它们如此受欢迎是有原因的.

虽然框架可能并不总是适合您的风格或需求, 可以采用一些功能和技术, 模拟或有时甚至与框架解耦. 有些东西总是会在翻译中丢失, 但是,只需花费框架成本的一小部分,就可以获得和使用很多东西.

了解基本知识

  • What is JSX?

    JSX是一个React组件,它简化了语法和创建HTML模板和DOM元素的过程. JSX在源代码中以HTML内联形式编写,但被音译为用于DOM构造的JavaScript指令, 这样就能两全其美.

  • 什么是React中的虚拟DOM?

    虚拟DOM是React对DOM的表示. 通过将其与页面DOM进行比较,可以跟踪更改并仅修改页面中实际需要修改的部分, 而不是将页面的大部分排队等待解析和重新呈现.

聘请Toptal这方面的专家.
Hire Now
胡安·卡洛斯·阿里亚斯·安布里兹

胡安·卡洛斯·阿里亚斯·安布里兹

Verified Expert in Engineering

墨西哥瓜达拉哈拉

2016年6月6日加入

About the author

Juan拥有超过10年的自由用户体验经验. 他的作品植根于完美主义 & 为用户提供最好的体验.

作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.

Expertise

PREVIOUSLY AT

Meta

世界级的文章,每周发一次.

输入您的电子邮件,即表示您同意我们的 privacy policy.

世界级的文章,每周发一次.

输入您的电子邮件,即表示您同意我们的 privacy policy.

Toptal开发者

Join the Toptal® community.