一、初始化项目
在public/index.html下增加以下标签
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" /> // ie 兼容模式
<meta name="format-detection" content="telephone=no,email=no,date=no,address=no" /> // ios默认事件
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /> // 移动端适配
抽离公共样式
新建pages目录和asset目录, 删除多余的文件
二、在App.js中该function无状态组件为有状态组件
初次体验jsx, 被花括号包起来的变量
class App extends React.Component {
render() {
let username = '张三'
let content = "<span style='color: #ff0000'>搞颜色</span>"
return (
<div className="App">
{username}
<div dangerouslySetInnerHTML={{__html:content}}></div>
</div>
)
}
}
export default App;
三、通过ref获取虚拟dom
class App extends React.Component {
componentDidMount() { // 页面渲染完成钩子函数
console.log(this.refs.node.innerHTML)
}
render() {
let username = '张三'
let content = "<span style='color: #ff0000'>搞颜色</span>"
return (
<div className="App">
{username}
<div dangerouslySetInnerHTML={{__html:content}}></div>
<div ref="node">节点信息</div>
</div>
)
}
}
export default App;
四、通过reactDom获取虚拟dom
ref只能获取本组件中的dom,用reactDom来获取
import HeaderComponent from './component/header'
import ReactDom from 'react-dom'
class App extends React.Component {
componentDidMount() {
// console.log(this.refs.node.innerHTML)
console.log(document.getElementById('header')) // 这个是真实dom操作性能低
console.log(ReactDom.findDOMNode(document.getElementById('header')))
}
render() {
let username = '张三'
let content = "<span style='color: #ff0000'>搞颜色</span>"
return (
<div className="App">
{/*头部组件*/}
<HeaderComponent />
{username}
<div dangerouslySetInnerHTML={{__html:content}}></div>
{/*<div ref="node">节点信息</div>*/}
</div>
)
}
}
五、面试题虚拟Dom和Diff算法的原理
1.虚拟Dom原理
虚拟dom相当于在js和真实dom之间加了一个缓存,将真实dom转换成json对象,
利用dom diff算法避免了没必要的dom操作, 从而提高性能。
2. Diff算法的原理
一个diff函数有两个参数一个是真实的Dom,一个是虚拟的dom,使用递归对: 组件,文本节点,非文本dom节点,属性作比较,如果相同不更新,如果不相同才更新。
对比子节点:子节点和之前的不同,子节点是一个数组,他们可能改变顺序,
或者数量有所变化,我们很难确定要和虚拟dom哪一个节点做对比。所以
我们要给他设置一个key,如果有key则使用key查找子节点的值(性能高),
如果没有key则按照dom类型查找(性能低)
六、手写JSX实现虚拟DOM
1.引入babel
https://unpkg.com/babel-standalone@6/babel.min.js
2. 重写createElement方法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script src="js/babel.min.js"></script>
<script type="text/babel">
/*@jsx createElement*/ //@jsx babel的自执行命令
let vmDom = (<div id="app" name="app">hello</div>)
function createElement(nodeName, attr, ...args) {
console.log( {nodeName,attr,children: [].concat(...args)})
return {nodeName,attr,children: [].concat(...args)}
}
</script>
</body>
</html>
自己的jsx语法创建成功
3. 实现render方法
/*@jsx createElement*/ //@jsx babel的自执行命令
let vmDom = (<div id="app" name="app">
hello
<ul>
<li>1</li>
<li>2</li>
</ul>
</div>)
/**
*
* @param nodeName
* @param attr
* @param args
* @returns {{nodeName: "div", children: ["hello"], attr:{id: "app", name: "app"}}}
* 真实dom转成json格式的虚拟dom
*/
function createElement(nodeName, attr, ...args) {
return {nodeName,attr,children: [].concat(...args)}
}
function render(vnode) {
console.log(vnode)
}
let dom = render(vmDom)
效果如下
4. 把构建好的虚拟dom树,递归渲染成真实dom
<script src="js/babel.min.js"></script>
<script type="text/babel">
let userList = ['张三','李四','王五']
/*@jsx createElement*/ //@jsx babel的自执行命令
let vmDom = (<div id="app" name="app">
hello
<ul>
{userList.map((v,i) => {
return (
<li key={i}>{v}</li>
)
})}
</ul>
</div>)
/**
*
* @param nodeName
* @param attr
* @param args
* @returns {{nodeName: "div", children: ["hello"], attr:{id: "app", name: "app"}}}
* 真实dom转成json格式的虚拟dom
*/
function createElement(nodeName, attr, ...args) {
return {nodeName:nodeName,attr:attr,children:[].concat(...args)}
}
function render(vnode) {
console.log(vnode)
// 如果是文本
if(vnode.split) {
return document.createTextNode(vnode)
}
// 如果是节点
let node = document.createElement(vnode.nodeName)
// 节点属性
let attr = vnode.attr || {}
let attrKeys = Object.keys(attr)
attrKeys.forEach(v => {
node.setAttribute(v,attr[v])
})
let arr = (vnode.children || [])
arr.forEach(v => {
node.appendChild(render(v))
})
return node
}
let dom = render(vmDom)
document.body.appendChild(dom)
</script>