手写一个render函数

前言

在这个之前我们需要了解render是什么?

可以去之前的文章里面看看 《 搞懂vue-render函数(入门篇)》

废话不多说,快快快 上车!!!

定义一个对象

先定义一个数据对象来作为我们的数据

let data = {
    tag:"h2",
    props:{},
    children:"严老湿"
}

tag 作为标签名,props 作为属性对象,children 作为子元素或者文本节点

哈哈哈 还是老套路 先写一个 h2 标签试试水

获取根节点挂载

这个So Easy啦

<div id="app"></div>
// app 作为我们的根节点
let app = document.querySelector('#app')

获取到 app 节点之后呢? 我们是不是需要 appendChild 我们第一步定义的 data 对象

// 此时还没有render这个函数
app.appendChild(render(data))

这个 render 函数就是我们今天要写的重要内容

开始重头戏

天才第一步 ,先定义一个 render 函数

// 接收传入的参数
const render = (options) = >{
    // 获取数据中的标签并创建
    let el = document.createElement(options.tag);
 // 打印el
 console.log(el) // <h2></h2>
 // 将节点返回
 return el
}
app.appendChild(render(data))

此时我们的元素已经创建成功,接下来我们需要做的就是,把它的文本,渲染上去。

渲染文本节点

children 赋值给到节点文本内容

const render = (options) = >{
    let el = document.createElement(options.tag);
 // 我们在这里传入 children 文本
 el.textContent = options.children
 return el
}
app.appendChild(render(data))

严老湿 :好了,render 写完了,下课吧!

童鞋 :等等!! 就这???

开个玩笑 当然不是啦!这写得也真是太简陋了吧

升级render

刚刚我们其实已经写了一个简陋版本的render 有很多问题!

  • children 如果是数组,多个子元素呢?
  • 如果props 里面有属性呢?
  • 我还想要点事件触发函数呢?

....

还有很多问题,所以我们来加强一下,冲鸭!!!

props参数

我们现在先来升级一下 props 参数,如我们需要在元素上加 classidon

首先是一号选手 class

我们先将数据中的props中新增一个class 且 它的值为 myClass

let data = {
    tag:"h2",
    props:{
        class:"myClass"
    },
    children:"严老湿"
}

数据改造好了,我们来看看 render 改如何升级

const render = (options) = >{
    let el = document.createElement(options.tag);
 // 判断是否是对象,并且不是null
 if(typeof options.props === 'object' && options.props !== null){
     for(key in options.props){
            // 拿到 props 中的 key、val
            vla = options.props[key];
            // 给元素添加指定的属性
            el.setAttribute(key, vla);
        }
    }
 el.textContent = options.children;
 return el
}
app.appendChild(render(data))

这样我们就已经完成了class的添加

老湿,那ID呢?

等等,我拿下锤子...

第二位选手 id

id其实都不用说了,就改改数据而已,So Easy

let data = {
    tag:"h2",
    props:{
        class:"myClass",
        id:"myId"
    },
    children:"严老湿"
}

props的最后一位选手 on

on 里面是用来绑定事件的,所以我们不能跟classid 同等对待。渣男!区别对待

还是先改造数据,我们加上 on-click

let data = {
    tag:"h2",
    props:{
        class:"myClass",
        id:'myId',
        on:{
            // 传入点击事件
            click:(e)=>{
                console.log(e)
            }
        }
    },
    children:"严老湿"
}

数据改造完成,我们接着升级 render

const render = (options) = >{
    let el = document.createElement(options.tag);
 if(typeof options.props === 'object' && options.props !== null){
     for(key in options.props){
            vla = options.props[key];
            // 如果key等于on
            if(key === "on"){
                // 将on对象赋值给 incident
                incident = options.props[key]
                for(k in incident){
                    // 给元素绑定事件传入Event
                    el.addEventListener(k,e=>incident[k](e))
                }
            }else{
                // 其他的都进来这里
             el.setAttribute(key, vla);
            }
        }
    }
 el.textContent = options.children;
 return el
}
app.appendChild(render(data))

点击标签之后看看打印

是不是也是很简单

到这里我们就已经完成了这个 props 里面经常会用到的一些操作了。

多个子元素

有时候我们的数据中可能不止一个元素,那如果children 是一个数组呢?

那我们就不能直接赋值给元素的 textContent 了,继续加判断。

我们还是一样的先修改数据,将 children 改为一个数组,结构呢还是个之前一样

let data = {
    tag:"ul",
    props:{
        class:"myClass",
        id:'myId',
    },
    children:[
        {
            tag'li',
            props: {
                class"list",
            },
            children"4万面五星红旗挂上武汉街头"
        }, {
            tag'li',
            props: {
                class"list",
            },
            children"机场水门最高礼遇迎接烈士遗骸"
        }
    ]
}

数据结构修改完成之后了,我们接下来修改 render 函数

const render = (options) = >{
    let el = document.createElement(options.tag);
 if(typeof options.props === 'object' && options.props !== null){
     for(key in options.props){
            vla = options.props[key];
            if(key === "on"){
                incident = options.props[key]
                for(k in incident){
                    el.addEventListener(k,e=>incident[k](e))
                }
            }else{
             el.setAttribute(key, vla);
            }
        }
    }
 // 在内容赋值这里我们做一个判断
 // 如果 children 是一个数组
 if (options.children instanceof Array) {
        // 我们进行 forEach 遍历一下
        options.children.forEach((item)=>{
            // 进行递归 render 函数,传入 
            el.appendChild(render(item));
        });
    }else{
        // 如果不是数据,我们进行赋值
        el.textContent = options.children
    }
 return el
}
app.appendChild(render(data))

是不是已经渲染成功呢?

全部代码

给大家贴一下全部代码吧!

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>老严讲render</title>
</head>
<body>
    <div id="app"></div>
    <script>
        const app = document.querySelector('#app')
        let data = {
            tag"ul",
            props: {
                class"myClass",
                id'myId',
            },
            children: [
                {
                    tag'li',
                    props: {
                        class"list",
                    },
                    children"4万面五星红旗挂上武汉街头"
                }, {
                    tag'li',
                    props: {
                        class"list",
                    },
                    children"机场水门最高礼遇迎接烈士遗骸"
                }
            ]
        }
        const render = (options) => {
            let el = document.createElement(options.tag);
            if (typeof options.props === 'object' && options.props !== null) {
                for (key in options.props) {
                    vla = options.props[key];
                    if (key === "on") {
                        incident = options.props[key]
                        for (k in incident) {
                            el.addEventListener(k, e => incident[k](e))
                        }
                    } else {
                        el.setAttribute(key, vla);
                    }
                }
            }
            if (options.children instanceof Array) {
                options.children.forEach((item) => {
                    el.appendChild(render(item));
                });
            } else {
                el.textContent = options.children
            }
            return el;
        };
        app.appendChild(render(data));
    
</script>
</body>
</html>

总结

  • 我们写的代码是比较简陋的,没那么多判断逻辑,但是我们已经将render的简单实现学会了

- END -

posted @ 2020-09-28 10:19  疯狂的严先生  阅读(519)  评论(0编辑  收藏  举报