前置简介
上一节: https://juejin.im/post/6866966785032355848/
本章旨在通过react实现一个小功能,为了演示效果就先使用cdn的方式进行引入react。
创建项目
请通过上节说的cdn方式自行创建一个如下图红框中的文件结构目录下来。
index.css
.red {
color: red;
}
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="index.css">
</head>
<body>
<div id="root">
</div>
<script src="https://cdn.bootcdn.net/ajax/libs/react/16.9.0/umd/react.development.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/react-dom/16.9.0/umd/react-dom.development.js"></script>
<script src="index.js"></script>
</body>
</html>
index.js
// 获取React对象
const React = window.React
// 获取ReactDOM对象
const ReactDOM = window.ReactDOM
// 获取id是root的元素
const root = document.querySelector("#root")
// 定义一个n变量
let n = 0
// 使用React创建一个div对象,class属性是red,内容是变量n 以及 一个button元素,点击事件是让n这个变量+1
const App = React.createElement('div', {className: 'red'}
,
[n , React.createElement('button', {
onClick: () => {
n += 1
}
}, '+1')])
// 将App这个div对象放到root元素里面去
ReactDOM.render(App, root)
React的元素创建各种使用基本都是在js中完成的,可以看一下上面的js以及每行的注释,
完成上面的步骤之后,我们刷新界面就会出现下面的效果:
根据js里的写法,我们点击网页中的button就会让左边的数字加1,对不对,
但是事实我们点击其实没有任何效果,这是为啥呢?
首先我们看下 ReactDOM.render(App, root)
这行代码,这行代码是进行元素的渲染,
只调用了一次,在我们点击button事件的时候并没有重新渲染,所以这个是其中问题之一,
下面我们可以在点击时间里重新渲染一下这个元素:
....
// 使用React创建一个div对象,class属性是red,内容是变量n 以及 一个button元素,点击事件是让n这个变量+1 , 并且重新渲染App元素
const App = React.createElement('div', {className: 'red'}
,
[n , React.createElement('button', {
onClick: () => {
n += 1
// 重新渲染
ReactDOM.render(App, root)
}
}, '+1')])
....
但是这样真的可以了嘛? 我们尝试一下后,发现还是没有任何反应,这是为什么呢?
我们看一下 App 这个元素的赋值操作,n在代码执行的时候 就已经当时的值 0 存放到 App这个元素中了,
所以事后我们再怎么改变n的值,也和App这个元素没有关系了,
所以在onclick重新渲染是对的,已经生效了的,只是我们的App元素里放的内容一直都是0,所以看起来没效果,
下面我们将App这个元素改成一个函数,让每次调用的时候都重新获取一下最新n的值看看,
// 获取React对象
const React = window.React
// 获取ReactDOM对象
const ReactDOM = window.ReactDOM
// 获取id是root的元素
const root = document.querySelector("#root")
// 定义一个n变量
let n = 0
// 使用React创建一个div对象,class属性是red,内容是变量n 以及 一个button元素,点击事件是让n这个变量+1 , 并且重新渲染App元素
const App = () => React.createElement('div', {className: 'red'}
,
[n , React.createElement('button', {
onClick: () => {
n += 1
// 重新渲染
ReactDOM.render(App(), root)
}
}, '+1')])
// 将App这个div对象放到root元素里面去
ReactDOM.render(App(), root)
最后我们将App改为一个箭头函数之后,每次重新渲染,+1的效果就出来啦。
总结:React的很多坑的地方,其实是需要用到JS的基础知识的,它并没有像vue一样,帮我们做了自动渲染等等机制,我们要用原生JS的基础知识思考,来思考React。
回顾JS的基础知识
let i
for (i=0; i<6; i++) {
setTimeout(()=>{console.log(i)},1000)
}
上述代码的执行结果:一秒钟后输出6个6,因为函数执行时遇到外部变量会去读取其最新的值。
这里解释一下setTimeout,setTimeout会将你要执行的代码放入到类似一个队列里面去,它告诉浏览器,就在多少毫秒后尽快执行这个代码逻辑,
所以上面的for循环里的setTimeout即使你设置的是0毫秒,他也要等到for循环执行完毕之后,才会执行,这时候获取到的最新值,自然是6。
如果打印出0,1,2,3…这种值呢?
for (let i=0; i<6; i++) {
setTimeout(()=>{console.log(i)},1000)
}
将let放入到for循环内即可,这是一个作用域的问题,let标记的变量i,作用域只存在于 {}括号内,
事后每个setTimeout查找作用域i的时候,找到都是当时的那个i,也就是0,1,2,3这种,
读者可以自己实验一下。
还有一种方式,就是使用立即执行函数:
for(var i=0; i<6; i++) {
!function(j) {
setTimeout(console.log(j),1000)
}(i)
}
这种立即执行函数,将i传递给方法里的形参j,j就保存了当时i的值,所以在最后输出的时候,也是当时的值。
So,说这个基础知识,其实是想映射的是,既然函数是可以获取到外部变量最新的值得,所以在上面的index.js
里,我们渲染元素的时候,
将App改为了一个函数,每次调用这个函数,都会获取到n的最新值,所以这正是我们想要的结果,
这里总结一下,函数可类比为延迟执行的代码,只有当你调用它的时候,它才会真正执行内部的逻辑。
最终总结
总结一下我们这里接触到的React知识
-
React元素
1.1 createElement的返回值 element 可以代表一个div
1.2 element并不是真正的div(DOM对象)
1.3 我们一般称element为虚拟DOM对象
-
()=>React元素
2.1 返回element的函数,也可以代表一个div
2.2 这个函数可以多次执行,每次得到最新的虚拟div
2.3 React会对比两个虚拟div,找出不同,局部更新视图,找不同的技术称为 DOM Diff算法,什么叫找不同呢,意思就是我们每次点击按钮重新调用渲染的时候,只会更新n元素的值,button按钮因为没有变动不会被重新渲染。
下一节:https://juejin.im/post/6867027735979261959