react系列学习(2)---JSX语法学习

215 阅读2分钟

一、初始化项目

在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目录, 删除多余的文件

image.png

二、在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;

image.png

三、通过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;

image.png

四、通过reactDom获取虚拟dom

ref只能获取本组件中的dom,用reactDom来获取

image.png

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>
        )
    }
}

image.png

五、面试题虚拟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语法创建成功

image.png

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)

效果如下

image.png

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>

效果如下

image.png