代码:http://jsbin.com/piwazag/1/edit?html,js,output

为什么说 React 是先进的、生产力的代表?
React 最重要的一个东西,就是关于 createElement 这部分内容!
需求:加 1 减 1

完成这个需求,显然需要用到 DOM API,如需要获取元素等……之后,你用了 React ,你就会明白你被颠覆了……
代码实现:

用图表示以上代码在干什么……
程序最重要的两个能力,一个是理解力,另一个就是抽象。你需要把上述的运行结果这样具体的东西,抽象成不具体的东西
抽象的反义词就是具体,而我们不需要具体,我们要的是「抽象」
那如何抽象呢?
简单回顾整个代码运行逻辑,我们可以把整个过程,分为两块:
我们透过 innerText 从页面中把数据提取到 JS 中,然后通过那两个 +1/-1 按钮,还是透过 innerText = 把数据回填到页面中!
这个代码运行逻辑并不复杂,但有点烦

总之,我们完成这个需求,总得做这一步:「获取页面数据,改数据,回填数据到页面」,而这一步是无法省略的 -> 这就是 DOM 提供的方法了!
然而写 React 的程序员,也就是作者,觉得这个过程太智障了,虽然代码挺简单的,但就是很不爽……
话说,有咩有一种更简便的方法呢?
可以这样做:把上边的 innerText 干掉,而下边的 innerText= 不能干掉,不然 JS 就无法通知页面改数据了!

带着这样的理念,React 就诞生了……
接下来,我们抱着这种理念,使用 React 重写这个需求!
cdn 引用两个库:
选择 umd,umd(Universal Module Definition)是 AMD 和 CommonJS 的糅合,跨平台(浏览器 & 服务端)的解决方案
💡:为什么需要依赖两个库?
React 是一个非常厉害的库,Facebook 的牛人写的。它能让页面更可控,性能极高,而且使用起来很简单。
React DOM 是用来操作 DOM 的,因为这些 DOM 是用 JSX 写的,所以需要一个专门的库来操作。
JSX 是对 JS 的扩展,它看起来跟 XML 差不多,可以用来写 HTML,你可以认为 JSX 是一种更优雅的 HTML 写法。
➹:在 2016 年学 JavaScript 是一种什么样的体验? - 知乎
之前说到,页面是咩有东西的,即你不用写这个了:

所以此时的页面结构,直接这样:
<div id="app"></div>
就行了……
完整代码:
<div id="app" class="container">
</div>
<button id="add" class="button is-primary"> + </button>
<button id="minus" class="button is-primary"> - </button>
<script src="https://cdn.bootcdn.net/ajax/libs/react/17.0.1/umd/react.development.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/react-dom/17.0.1/umd/react-dom.development.min.js"></script>
<script>
let number = 0
let span = React.createElement('span', {
className: 'tag'
}, number)
ReactDOM.render(span, document.querySelector("#app"))
let add = document.querySelector('#add')
add.onclick = () => {
number += 1
let span = React.createElement('span', {
className: 'tag'
}, number)
ReactDOM.render(span, document.querySelector("#app"))
}
let minus = document.querySelector('#minus')
minus.onclick = () => {
number -= 1
let span = React.createElement('span', {
className: 'tag'
}, number)
ReactDOM.render(span, document.querySelector("#app"))
}
</script>
效果:

总之,根据之前那张图,只有从 JS 到页面这个过程,没有页面到 JS 这个过程!
当然,目前的代码还是很傻的!不过,你至少有了这种想法「只有从 JS 到页面的过程」
优化一下代码(我与重复不共戴天):
let number = 0
render()
let add = document.querySelector('#add')
add.onclick = () => {
number += 1
render()
}
let minus = document.querySelector('#minus')
minus.onclick = () => {
number -= 1
render()
}
function render() {
let span = React.createElement('span', {
className: 'tag'
}, number)
ReactDOM.render(span, document.querySelector("#app"))
}
number为0就render一下,number为1就render一下……number变了就render一下!
可以看到,我们要做的就只有把number的值改一下,然后render一下,除此之外,不做任何事……
以上就是 React 的所有操作了,除此之外,没有任何操作!
可以看到,React 的操作,让我们不用去理会取元素的儿子、爸爸、爷爷等复杂繁琐的操作了……
要点:
代码:
<div id="app" class="container">
</div>
<script src="https://cdn.bootcdn.net/ajax/libs/react/17.0.1/umd/react.development.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/react-dom/17.0.1/umd/react-dom.development.min.js"></script>
<script>
let number = 0
let onClickButton = ()=>{
number += 1
render()
}
let onClickButton2 = ()=>{
number -= 1
render()
}
render()
function render() {
let span = React.createElement('span',{className:'tag'},number)
let button = React.createElement('button',{onClick:onClickButton,className:'button is-primary'},'+')
let button2 = React.createElement('button',{onClick:onClickButton2,className:'button is-primary'},'-')
let div = React.createElement('div',{className:"parent"},span,button,button2)
ReactDOM.render(div,document.querySelector("#app"))
}
</script>
效果:

不会全局更新,只会局部更新,哪里变化了,就会更新哪里!
可以看到整个页面内容,都是 JS 渲染的!而 React 的存在,简化了我们要写的很多代码,而且这性能要比我们自己用原生 DOM API 要高效得多!
对比我们之前的原生 JS 代码,用了 React,显然让代码更简洁、更易懂了……
小结:
render,数据变化,就render……结合一开始那个草稿图来理解……接下来,对这个render函数来一个更深层次的优化!
💡:在 HTML 元素上写click事件?
<button onclick="myFunction()">Click me</button>
➹:HTML onclick Event Attribute
💡:在 React 中,如何写多个class,是用classList吗?
直接{className: 'class1 class2'}就好了!
➹:javascript - Toggle Class in React - Stack Overflow
💡:我在div#app里边写了一个<div>hi</div>,你觉得这个元素会被渲染吗?
结果并咩有被渲染!ReactDOM.render(div,document.querySelector("#app"))这个操作,把div#app旗下的内容都给覆盖了,该renderAPI 的实现,并不是appendChild,而是类似于innerHTML 这样
目前,我们代码的问题:

谁这样写,谁傻逼!
所以,我们该如何优化它呢? -> 分三步走
第一步:
React.createElement既然每次都写,那为何不把它缩小一点呢?
所以就有了这样的代码:
let h = React.createElement
这是一个最简单的优化——把函数换一个名字
💡:为何把这个变量叫做h呢?叫ele等这样的变量名不行吗?
没有什么缘由,一般都叫h!
h代表的是 hyperscript(hyper 对应着 HTML 的 H),它本身表示的是 “生成 HTML 结构的脚本”,当我们正在处理一个脚本的时候,在虚拟 DOM 节点中去使用它进行替换已成为一种惯例。这个定义同时也被运用到其他的框架文档中,如 Cycle.js - API reference (dom)
第二步:
把变量减少

记住代码的形式,这形式可是非常重要的
第二步的优化——把临时变量都给删掉,直接用h函数嵌套起来!
第三步:
这是 React 最聪明的一步!React 开发者看了上述的代码,想到了某个问题,那就是这个代码跟某个代码非常相似,简直就是一模一样的说:
h('div',{className:"parent"},
h('span',{className:'tag'},number),
h('button',{onClick:onClickButton,className:'button is-primary'},'+'),
h('button',{onClick:onClickButton2,className:'button is-primary'},'-')
)
<div class="parent">
<span class="red">number</span>
<button onClick=onClickButton>+</button>
<button onClick=onClickButton2>-</button>
</div>
出现这样的
</span>闭合标签,就是代表着h()的这个),同理,h()的(就是开标签<span>
至此,React 的发明者惊讶地发现「我的代码其实跟标签没有什么区别」,所以「我」能否想一种办法让我们的使用者写下边这种代码:
<div class="parent">
<span class="red">number</span>
<button onClick=onClickButton>+</button>
<button onClick=onClickButton2>-</button>
</div>
然后,「我」用一个程序翻译成这种代码:
h('div',{className:"parent"},
h('span',{className:'tag'},number),
h('button',{onClick:onClickButton,className:'button is-primary'},'+'),
h('button',{onClick:onClickButton2,className:'button is-primary'},'-')
)
呢?
思于至此,React 作者就开始干了!他写了一个程序,可以把上边这种语法,也就是JSX语法,搞成是下边那种代码!
这个程序可以叫做「JSX 翻译器」
JSX 语法:可以让我们用 HTML 的形式来写 JS,注意,这可不是在写 HTML 哈!
这个 JSX 语法出来以后,就越来越流行了,于是,大家都很喜欢像写标签这样去写 JS 了,这种方法甚是简便,但实质上我们是在写下边那个h……代码……
之后,这种语法就被 Babel 采纳了,于是 Babel 就自带了可以翻译这种 JSX 语法的功能了!
💡:<span class="red">number</span>这个number是字符串,可我们要的是number变量呀!
很简单,我们要变量,直接加个{}包裹起来就好了,如<span class="red">{number}</span>。
同理,<button onClick=onClickButton>+</button>的onClickButton也是被当作字符串处理的(你点击按钮,并不会调用onClickButton这个函数),而我们要的是函数变量,所以也得加上个{} -> <button onClick={onClickButton}>+</button>
number和onClickButton这个变量的值是沿着当前作用域链往上找的……

➹:Babel · The compiler for next generation JavaScript
这就是 React 的最后一步优化了,即你写 JSX 语法的代码,就相当于是在写React.createElement(……)这样一大串 JS 语法了!
当然,我们写的 JSX 语法代码是不能运行的,因为这并不是合法的语法,那么我们如何才能让它合法呢?
有两种办法:

继续优化代码:

芳芳建议把
class写成是className,虽然不这样做,代码也能正常运行,但如果遇上了报错,那你就得写成className这样了!所以,不管怎样都建议写成className这样
有人认为为啥不写成onClick={onClickButton()}这样呢?即加个()这样 -> 点击按钮,就执行函数……
其实你捋一捋就清楚了,你如果加上,那么翻译的结果:

JSX 语法的事件监听函数就是一个
0x101这样的引用值,与 HTML 里边的<button onclick="xxx()">Click me</button>是不一样的形式!
回顾整个过程:
原生 JS 姿势操作 DOM 的代码是很复杂的(用 jQuery 也是如此),当然,这是相较于使用了 JSX 语法而言的!
以前用原生 JS 或 jQuery 写代码的姿势:
React 的想法:
页面你先给我空着,所有东西都是通过一个对象来表示的!

简单来说,就是把一个对象放到页面里边去,对象的某个内容更新了,就再把这个对象放到页面里边去!如此反复…… -> 改值,渲染,改值,渲染……,没有任何其它的操作,如没有innerText这样的操作,总之,你可以忘掉所有的 DOM API,当然,除了最后挂载的这个document.querySelector("#app")……毕竟,我们需要告诉浏览器这个对象要在页面哪个位置渲染!
总之,document.querySelector("#app")是唯一用到了 DOM API 的地方,除此之外,就没有用到其它 DOM API 了。
以上就是 React 实现最初的想法了,一句话总结之:
由于 DOM API 太难用了,所以 React 不择手段地做到了不去访问 DOM ,只用你更新数据构造出个虚拟 DOM 就完事儿了!
💡:什么是虚拟 DOM?
就是我们写的那个看起来像 HTML 的 JSX 语法代码,为啥叫虚拟 DOM 呢?因为它不是真正的 DOM 啊!所以就叫虚拟 DOM 了
简单来说,虚拟 DOM 就是不真实的 DOM,而「不真实的 DOM」就是一个对象! -> 虚拟 DOM 就是一个对象,没有任何神奇之处:

我们解决了一些悬而不决的问题:
JSX 是什么?
用 HTML 的形式来写 JS,所以就叫 JSX -> 一种 JS 的扩展,X 一般即扩展的意思!
虚拟 DOM 是什么?
非真实的 DOM 即虚拟 DOM,一般用对象来表示,虚拟 DOM 就是一个表示 DOM 节点的对象
这些概念都挺智障的,没有任何一点复杂的地方……
学到了什么?
知道了关于 React 这个想法是怎么来的,下一节芳芳会讲到 「React 的组件」这个想法是怎么来的!
Vue 是怎么来的?