资讯专栏INFORMATION COLUMN

【React进阶系列】从零开始手把手教你实现一个Virtual DOM(二)

dendoink / 1548人阅读

摘要:上集回顾从零开始手把手教你实现一个一上一集我们介绍了什么是,为什么要用,以及我们要怎样来实现一个。完成后,在命令行中输入安装下依赖。最后返回这个目标节点。明天,我们迎接挑战,开始处理数据变动引起的重新渲染,我们要如何新旧,生成补丁,修改。

上集回顾

从零开始手把手教你实现一个Virtual DOM(一)
上一集我们介绍了什么是VDOM,为什么要用VDOM,以及我们要怎样来实现一个VDOM。我们再来看一下这张蓝图,今天我们要实现的是这张图的左半部分。

package.json
{
  "name": "vdom",
  "version": "1.0.0",
  "description": "",
  "scripts": {
    "compile": "babel index.js --out-file compiled.js"
  },
  "author": "",
  "license": "",
  "devDependencies": {
    "babel-cli": "^6.23.0",
    "babel-plugin-transform-react-jsx": "^6.23.0"
  }
}

这里主要主要两点:

devDependencies中依赖babel-cli和babel-plugin-transform-react-jsx这两个库,前者提供Babel的命令行功能,后者主要帮我们把jsx转化成js。

scripts中我们指定了一条命令:complile,每次当我们在当前目录下的命令行中敲npm run compile时,babal就会将我们的index.js转化后新建一个compile.js文件。

完成后,在命令行中输入npm install安装下依赖。

.babelrc
{
  "plugins": [
    ["transform-react-jsx", {
      "pragma": "h"  // default pragma is React.createElement
    }]
  ]
}

在babel的配置文件中,我们指定transform-react-jsx这个插件将转化后的函数名设置为h。默认的函数名是React.createElement,我们不依赖react,所以显然换个自己的名字更合适。这里不清楚h是干什么的不要紧,等会看到代码你就知道了。

index.html


  
    
    VDOM
    
  
  
    
    

这个HTML还是很直观的,类似React,我们有一个根节点id是app。然后我们render函数最终生成的DOM会插入到app这个根节点里。注意我们引用的compile.js文件是babel根据等会要写的index.js文件自动生成的。

index.js

首先,我们用JSX来编写“模板”:

function view() {
  return 
  • Detective Chinatown Vol 2
  • Ferdinand
  • Paddington 2
}

接下来,我们要将JSX编译成js, 也就是hyperscript。我们先用Babel编译一下,看这段JSX转成js会是什么样子,打开命令行,输入npm run compile,得到的compile.js:

function view() {
  return h(
    "ul",
    { id: "filmList", className: "list" },
    h(
      "li",
      { className: "main" },
      "Detective Chinatown Vol 2"
    ),
    h(
      "li",
      null,
      "Ferdinand"
    ),
    h(
      "li",
      null,
      "Paddington 2"
    )
  );
}

可以看出h函数接收的参数,第一个参数是node的类型,比如ul,li,第二个参数是node的属性,之后的参数是node的children,假如child又是一个node的话,就会继续调用h函数。

清楚了Babel会将我们的JSX编译成什么样子后,接下来我们就可以继续在index.js中来写h函数了。

function flatten(arr) {
  return [].concat(...arr)
}

function h(type, props, ...children) {
  return {
    type,
    props: props || {},
    children: flatten(children)
  }
}

我们的h函数主要的工作就是返回我们真正需要的hyperscript对象,只有三个参数,第一个参数是节点类型,第二个参数是属性对象,第三个是子节点的数组。

这里主要用了ES6的rest, spread参数,不清楚代码中两个...分别是什么意思的可以先去看我的介绍ES6文章30分钟掌握ES6/ES2015核心内容(上)。简单来说,rest就是上面的...children,它将函数多余的参数放到一个数组里,所以children此时变成了一个数组。而spread则是rest的逆运算,也就是上面的...arr,它将一个数组转为用逗号分隔的参数序列。

flatten(children)这个操作是因为children这个数组里的元素有可能也是个数组,那样就成了一个二维数组,所以我们需要将数组拍平成一维数组。[].concat(...arr)是ES6写法,传统的写法是[].concat.apply([], arr)

我们现在可以先来看一下h函数最终返回的对象长什么样子。

function render() {
  console.log(view())
}

我们在render函数中打印出执行完view()的结果,再npm run compile后,用浏览器打开我们的index.html,看控制台输出的结果。

可以,很完美!这个对象就是我们的VDOM了!

下面我们就可以根据VDOM, 来渲染真实DOM了。先改写render函数:

function render(el) {
  el.appendChild(createElement(view(0)))
}

createElement函数生成DOM,然后再插入到我们在index.html中写的根节点app。注意render函数式在index.html中被调用的。

function createElement(node) {
  if (typeof(node) === "string") {
    return document.createTextNode(node)
  }

  let { type, props, children } = node
  const el = document.createElement(type)
  setProps(el, props)
  children.map(createElement)
    .forEach(el.appendChild.bind(el))

  return el
}

function setProp(target, name, value) {
  if (name === "className") {
    return target.setAttribute("class", value)
  }

  target.setAttribute(name, value)
}

function setProps(target, props) {
  Object.keys(props).forEach(key => {
    setProp(target, key, props[key])
  })
}

我们来仔细看下createElement函数。假如说node,即VDOM的类型是文本,我们直接返回一个创建好的文本节点。否则的话,我们取出node中类型,属性和子节点, 先根据类型创建相应的目标节点,然后再调用setProps函数依次设置好目标节点的属性,最后遍历子节点,递归调用createElement方法,将返回的子节点插入到刚刚创建的目标节点里。最后返回这个目标节点。

还需要注意的一点是,jsx中class的写成了className,所以我需要特殊处理一下。

大功告成,complie后浏览器打开index.html看看结果吧。

今天我们成功的完成了蓝图的左半部分,将JSX转化成hyperscript,再转化成VDOM,最后根据VDOM生成DOM,渲染到页面。明天,我们迎接挑战,开始处理数据变动引起的重新渲染,我们要如何DIFF新旧VDOM,生成补丁,修改DOM。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/94603.html

相关文章

  • React进阶系列从零开始把手教你实现一个Virtual DOM(一)

    摘要:可实际上并不是创造的,将这个概念拿过来以后融会贯通慢慢地成为目前前端最炙手可热的框架之一。则是将再抽象一层生成的简化版对象,这个对象也拥有上的一些属性,比如等,但它是完全脱离于浏览器而存在的。所以今天我要手把手教大家怎么从零开始实现。 假如你的项目使用了React,你知道怎么做性能优化吗?你知道为什么React让你写shouldComponentUpdate或者React.PureCo...

    PumpkinDylan 评论0 收藏0
  • React进阶系列从零开始把手教你实现一个Virtual DOM(三)

    摘要:函数依次做了这几件事调用函数,对比新旧两个,根据两者的不同得到需要修改的补丁将补丁到真实上当计数器小于等于的时候,将加,再继续下一次当计数器大于的时候,结束下面我们来实现函数和函数。 上集回顾 【React进阶系列】从零开始手把手教你实现一个Virtual DOM(二) 上集我们实现了首次渲染从JSX=>Hyperscript=>VDOM=>DOM的过程,今天我们来看一下当数据变动的时...

    qqlcbb 评论0 收藏0
  • Vue.js最佳实践(五招让你成为Vue.js大师)

    摘要:但如果你想更加高效地使用来开发,成为大师,那下面我要传授的这五招你一定得认真学习一下了。虽然损失了一丢丢性能,但避免了无限的。所以我们需要设置,这些默认行为将会被去掉以上两点的优化才能成功。陆续可能还会更新一些别的招数,敬请期待。 本文面向对象是有一定Vue.js编程经验的开发者。如果有人需要Vue.js入门系列的文章可以在评论区告诉我,有空就给你们写。 对大部分人来说,掌握Vue.j...

    CocoaChina 评论0 收藏0
  • javascript知识点

    摘要:模块化是随着前端技术的发展,前端代码爆炸式增长后,工程化所采取的必然措施。目前模块化的思想分为和。特别指出,事件不等同于异步,回调也不等同于异步。将会讨论安全的类型检测惰性载入函数冻结对象定时器等话题。 Vue.js 前后端同构方案之准备篇——代码优化 目前 Vue.js 的火爆不亚于当初的 React,本人对写代码有洁癖,代码也是艺术。此篇是准备篇,工欲善其事,必先利其器。我们先在代...

    Karrdy 评论0 收藏0
  • 前方来报,八月最新资讯--关于vue2&3的最佳文章推荐

    摘要:哪吒别人的看法都是狗屁,你是谁只有你自己说了才算,这是爹教我的道理。哪吒去他个鸟命我命由我,不由天是魔是仙,我自己决定哪吒白白搭上一条人命,你傻不傻敖丙不傻谁和你做朋友太乙真人人是否能够改变命运,我不晓得。我只晓得,不认命是哪吒的命。 showImg(https://segmentfault.com/img/bVbwiGL?w=900&h=378); 出处 查看github最新的Vue...

    izhuhaodev 评论0 收藏0

发表评论

0条评论

dendoink

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<