从JS继承实现一个VueJs的render函数基础知识_render菜鸟教程

市面上的主流框架,相信作为一个前端搬砖人员,或多或少都会有所接触到。如ReactJs、VueJs、AngularJs。那么对于每个框架的使用来说其实是比较简单的,还记得上大学时候,老师曾经说过:”技术就是窗户纸,捅一捅就破了”,也就是说,任何一门技术,只要深入去研究,那么它也不再是很神秘的东西了。我个人在工作中用VueJs是比较多的,当然React也会,那今

从JS继承实现一个VueJs的render函数基础知识

首先来看一段代码:

从JS继承实现一个VueJs的render函数基础知识_render菜鸟教程

    <div id="div1">
        <span>test</span>
        <Tab></Tab>
        <UserLogin></UserLogin>
    </div>

最终在页面上的呈现是怎样的呢?毫无疑问,只看到了test这一段文本内容。因为html不认识Tab、UserLogin这两个”异类”元素。那么假如现在要实现的是,通过一个render方法:

render({
    root:'#div1',
    components:{
        Tab,UserLogin
    }
})

将Tab、UserLogin这两个组件的内容渲染出来,该去怎样实现呢?这里涉及到的知识点如下:

  • 类型判断
  • DOM操作
  • Js的继承、多态
  • 组件化思想

首先通过Js的继承及组件化思想来定义两个类Tab、UserLogin,它们都有一个自身的render方法(从父类Component)继承而来并进行了重写。直接上代码:

Component类:

class Component{
        render(){
            throw new Error('render is required');
        }
}

Tab类:

class Tab extends Component{
        render(){
            let div1 = document.createElement('div');
            div1.innerHTML = '我是Tab组件';
            return div1;
        }
}

UserLogin类:

class UserLogin extends Component{
        render(){
            let div2 = document.createElement('div');
            div2.innerHTML = "我是登录组件"
            return div2
        }
}

到这里,相信大家学过ES6的,对这样的代码都是感觉很熟悉的,重点是render函数究竟该怎样去实现。再来看一下render函数的基本骨架:

render({
        root:'#div1',
        components:{
            Tab,UserLogin
        }
})

render函数接收了一个参数(对象类型),里面包括两个属性root(挂载的元素),以及components(带渲染的组件类)。对于render的实现,先从root这个属性入手。灵魂拷问,root属性一定是某个元素的id吗?对于一个函数的参数来说,使用者传递什么类型都是可以的,但只要符合规定的参数才能有效,说那么多其实就是需要对render函数对象参数的root属性进行校验。代码如下:

function render(opts){
        let root = null;
        if(typeof opts.root === "string"){
            root = document.querySelector(opts.root);
            if(!root){
                throw new Error(`can't found ${opts.root}`)
            }
        }else if(opts.root instanceof HTMLElement){
            root = opts.root
        }else{
            throw new Error(`root invalid`)
        }
}

这里的操作的目的就是为了找到root这个(父)元素。

接下来就是针对参数对象的components属性来进行处理了,也就是说需要找到所有自定义元素(Tab、UserLogin),又一次灵魂拷问,可以通过父元素找到其包含的所有子元素,但是该怎样去区分哪些元素是自定义的呢?先来看一下通过父元素找到所有子元素的代码:

let elements = root.getElementsByTagName("*");

打印看看elements是怎样的数据结构:

从JS继承实现一个VueJs的render函数基础知识_render菜鸟教程

可以看到,有一个是我们很熟悉的自有元素span,还有两个未知的元素tab、userlogin,这时你可能回想,将elements转换为数组、然后遍历进行判断是否有自定义元素,哎,其实这思路还不错。看下代码:

  Array.from(elements).forEach((ele) =>{
       if(ele.tagName ==='tab'){
         //找到了自定义元素tab
       }
       if(ele.tagName ==='userlogin'){
         //找到了自定义元素userlogin  
       }     
  })

这样行吗?显然是不行的。万一将元素标签结构改成这样呢

<div id="div1">
    <span>test</span>
    <Tab></Tab>
    <UserLogin></UserLogin>
    <List></List>
</div>

那是不是要写很多个if判断,显示不对。我们知道,一个html文档的继承结构大致如下:

从JS继承实现一个VueJs的render函数基础知识_render菜鸟教程

对于上述的自有元素span,它继承是HTMLElement,也就是说span元素的构造函数是HTMLSpanElement,那么对于上述两个未知元素,它们的构造函数是什么呢?其实是HTMLUnknownElement。这下就可以通过构造函数类再结合参数对象中components属性来找到未知元素了,代码如下:

Array.from(elements).forEach((ele) =>{
    if(ele.constructor === HTMLUnknownElement){
        for(let compName in opts.components){
            if(compName.toLowerCase() === ele.tagName.toLowerCase()){
                let CmpCls = opts.components[compName];
            }
        }
    }
})

代码中CmpCls其实就是我们最初定义的两个类Tab、UserLogin,然后通过实例化它们,并调用各自实例对象的render方法,再通过找到的未知元素ele来进行父元素(root)里内容的替换渲染了。代码如下:

Array.from(elements).forEach((ele) =>{
    if(ele.constructor === HTMLUnknownElement){
        for(let compName in opts.components){
            if(compName.toLowerCase() === ele.tagName.toLowerCase()){
                let CmpCls = opts.components[compName];
                let cmp = new CmpCls();
                let res = cmp.render();
                ele.parentNode.replaceChild(res,ele);
            }
        }
    }
})

再看下页面最终呈现的内容:

从JS继承实现一个VueJs的render函数基础知识_render菜鸟教程

正确地将我们自定义Tab、UserLogin两个组件的内容渲染了出来。

最终完整代码如下:

<!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="div1">
        <span>test</span>
        <Tab></Tab>
        <UserLogin></UserLogin>
    </div>
    <script>

        function render(opts){
            //1.找到root
            let root = null;
            if(typeof opts.root === "string"){
                root = document.querySelector(opts.root);
                if(!root){
                    throw new Error(`can't found ${opts.root}`)
                }
            }else if(opts.root instanceof HTMLElement){
                root = opts.root
            }else{
                throw new Error(`root invalid`)
            }

            //2.找出所有自定义的元素
            let elements = root.getElementsByTagName("*");
            Array.from(elements).forEach((ele) =>{
                if(ele.constructor === HTMLUnknownElement){
                    for(let compName in opts.components){
                        if(compName.toLowerCase() === ele.tagName.toLowerCase()){
                            let CmpCls = opts.components[compName];
                            let cmp = new CmpCls();
                            let res = cmp.render();
                            ele.parentNode.replaceChild(res,ele);
                        }
                    }
                }
            })

        }

        class Component{
            render(){
                throw new Error('render is required');
            }

        }

        class Tab extends Component{
            render(){
                let div1 = document.createElement('div');
                div1.innerHTML = '我是Tab组件';
                return div1;
            }
        }

        class UserLogin extends Component{
            render(){
                let div2 = document.createElement('div');
                div2.innerHTML = "我是登录组件"
                return div2
            }
        }
        render({
            root:'#div1',
            components:{
                Tab,UserLogin
            }
        })
    </script>
    
</body>
</html>

就这样,一个简洁版的Vuejs render函数就实现了, 与Vuejs中的render函数相比,还差很多很多技术点未实现。但并不阻碍我们来了解部分实现思想。

海计划公众号
(0)
上一篇 2020/03/24 05:36
下一篇 2020/03/24 05:36

您可能感兴趣的内容

  • Vue路由守卫之路由独享守卫使用帮助_路由入门百科

    路由独立守卫,顾名思义就是这个路由自己的守卫任务,就如同咱们LOL,我们守卫的就是独立一条路,保证我们这条路不要被敌人攻克(当然我们也得打团配合)在官方定义是这样说的:你可以在路由配置上直接定义 beforeEnter 守卫,这些守卫与全局前置守卫的方法参数是一样的。const router = new VueRouter({routes: [{path:

    2020/03/26
  • React Native 添加 Redux 支持菜鸟攻略_native入门教程

    0x1 前言之前写的项目都是人家编写好的脚手架,里面包含项目所需的环境文件,但由于有些东西用不到打包增加软件体积,所以自己从头搭建个环境。是基于 Native Base + react-navigation + Redux 的 React Native 脚手架,现在项目环境如下:{“name”: “app”,”version”: “0.0.1”,”priva

    2020/03/26
  • 为什么程序员使用电脑时,很少使用鼠标,只需要键盘就能工作?小白指南_键盘攻略教程

    现在很多人在使用电脑的时候会特别依赖鼠标,因为使用鼠标操作会比较方便,身边有程序员的人可能会疑惑为什么他们在使用电脑的时候,不需要鼠标,只需要一个键盘就能工作了呢?普通的电脑使用者都习惯用自己的左手来操作鼠标,右手来控制着鼠标,两者相互结合,对于程序员来说,使用鼠标就会比较的麻烦,因为他们对电脑的掌握程度是非常高的,他们很熟练的掌握如何用键盘来实现鼠标的功能

    2020/03/30
  • 关于SbWebServer页面缓存的设计攻略教程_缓存指南教程

    对于一个网站页面来说,不同的页面被访问的可能性不同,像主页被访问的概率是最大的。如果利用这个特点,对高访问概率的页面存入缓存,这样每次连接过来就不用每次都要经历本地找文件,打开这样一个过程。对于这个缓存的设计,首先考虑:1.主页一定是一直在缓存中的。2.用一个哈希表来建立filename—>文件在内存中地址的映射。3.用shared_ptr来指向文件地址

    2020/03/29
  • Doxygen菜鸟教程_一种开源跨平台的文档系统

    Doxygen菜鸟教程 官方网址:http://www.doxygen.nl/ GitHub:https://github.com/doxygen/doxygen 简介描述:一种开…

    2020/03/06
  • js的类型菜鸟教程_类型入门知识

    基本类型按值访问,可以操作保存在变量中实际的值基本类型存在栈内存当基本类型调用对象的方法时,不会起效,底层会有一个叫包装对象酷炫操作,效果是…不报错引用类型复制:操作的是对象的引用添加属性:操作的是实际的对象引用类型数据存在堆内存,而引用存在栈区,也就是说引用类型同时保存在栈区和堆区引用类型的比较是比较引用==的类型转换关于==的执行机制,ECMASri

    2020/04/05
  • 程序员面试必备的5个问题入门教程_面试小白知识

    年年有面试,岁岁有面试.如果说工作内容占据了职业生涯的90%,那么面试就占据了10%.面试决定了我们是否可以进入某个公司,是否可以谈判一个较高的薪水,是否可以胜任某些重要的职位.千万不要以为技术水平决定了你的工资和等级,如果不能有一个好的面试过程,那么之后的试用期和正式期除了任务的数量和困难程度可能指数级增加以外,其他的并不会改变.就算短时间做出超越几倍的努

    2020/03/24
  • 敲开通往架构师的门小白帮助_架构师小白知识

    最近学习了一些关于架构设计的知识想分享给大家。俗话说得好,不想当架构师的程序员不是好厨子。那么如何成为一名架构师呢?接下来就聊一聊我的一些想法。什么是架构师之前有同学问我,做了几年技术,应该转管理还是转架构师?对于这位同学,我给他的答案是,你要先踏踏实实做好现在的工作。因为就他提的问题来看,应该是刚入行不久或者是在校学生。专心做技术的,都想做架构师。但架构师

    2020/03/23
  • javascript如何判断对象是否包含某属性?使用攻略_属性小白知识

    javascript如何判断对象是否包含某属性?下面本篇就来给大家介绍几种使用javascript判断对象是否包含有某属性的常见方法,希望对大家有所帮助。一、使用“!==”“!==”方法在工作中很常见,可以看出该方法可以判断继承来的属性。let obj = { x: 1 };
    obj.x !== undefined; // true

    2020/03/22
  • 在PHP7中不要做的 10 件事基础教程_php指南攻略

    1. 不要使用 mysql_ 函数这一天终于来了,从此你不仅仅“不应该”使用mysql_函数。PHP 7 已经把它们从核心中全部移除了,也就是说你需要迁移到好得多的mysqli_函数,或者更灵活的 PDO 实现。2. 不要编写垃圾代码这一条可能易于理解,但是会变得越来越重要,因为 PHP 7 的速度提升可能会隐藏你的一些问题。不要仅仅满足于你的站点速度,因为

    2020/04/03
  • 学node可以用什么书籍?入门知识_书籍使用帮助

    进入Node.js,一定要搭配轻松易懂的书籍,这样才能花费最少的时间,获得最高的收益。那么学node可以用什么书?Node.js基础书籍推荐:1、《Node.js高级编程》Node.js是一种主流框架,它允许你使用JavaScript快速构建具有高度可伸缩性的网络程序。可是,它有自己的学习曲线,这本较为深入的指南性图书首先介绍了Node.js平台的安装,然后

    2020/03/20
  • MobileMozaic基础教程_移动端 UI 设计欣赏,可通过分类和组件元素进行筛选查找

    MobileMozaic基础入门 官方网址:http://www.mobilemozaic.com/ 简介描述:移动端 UI 设计欣赏,可通过分类和组件元素进行筛选查找 Mobil…

    2020/03/06
  • Node.js 指南(迁移到安全的Buffer构造函数)指南攻略_指南入门百科

    概述本指南介绍了如何迁移到安全的Buffer构造函数方法,迁移修复了以下弃用警告:由于安全性和可用性问题,不建议使用 Buffer()和 new Buffer()构造函数,请改用 new Buffer.alloc()、Buffer.allocUnsafe()或 Buffer.from()构造方法。变式1:放弃对Node.js ≤4.4.x和5.0.0 – 5

    2020/03/24
  • jquery基础入门教程_一个快速、简洁的JavaScript代码库

    jquery基础入门 官方网址:http://jquery.com/ GitHub:https://github.com/jquery/jquery 简介描述:一个快速、简洁的Ja…

    2020/03/05
  • H5与App的通讯方式基础入门_通讯使用帮助

    前言现在不管是桌面客户端还是移动客户端,都会夹杂着一部分H5页面,这种混合式的应用也是我们常说的Hybrid App。为什么会出现Hybrid App呢,早期是因为开发一个Android或iOS的客户端,需要的人力成本比较大,开发周期比较长,后来有些团队就通过将部分页面拆分出来,由前端来完成,再通过在客户端里的Webview来展示。由于小编我半路转行当程序猿

    2020/03/23
  • jsrender小白指南下一代Jquery模板,前端模板引擎(jQuery模板)

    jsrender基础入门 官方网址:http://www.jsviews.com/ GitHub:https://github.com/BorisMoore/jsrender 简介…

    2020/03/05