16

动图镇楼

图片描述

在线演示:https://momoko8443.github.io/...
图片描述

书接上文 【教学向】再加150行代码教你实现一个低配版的web component库(1) —设计篇

先回顾一下

上文说道一个基本款的custom web component由3大部分组成,同时也必须具备4大功能

三大部分是

1. Template(DOM)
2. Style   
3. Script (viewModel)

四大功能是

1. Mvvm
2. Shadow Style
3. Communication
4. Lifecycle (本文不涉及LC的API,但在实现中会隐形的涵盖这部分内容)

Component定义文件格式如下

<!-- myComp.html -->
<sf-component>
    <style>
        button{
            color:red;
        }
        p{
            color:yellow;
        }
    </style>
    <template>
        <div>
            <input type="text" sf-value="this.message"/>
            <button sf-innerText="this.buttonName" onclick="this.clickHandler()"></button>
            <p sf-innerText="this.message"> 
            </p>
        </div>
    </template>
    <script>
        this.message = "this is a component";
        this.buttonName = "click me";
        this.clickHandler = function(){
            alert(this.message);
        };
    </script>
</sf-component>

接下去,本篇就会一一讲解各个点的原理和大致实现思路,具体代码会在第三篇 代码篇中给出

Web Component库大致流程

图片描述

黑框以下是不是很眼熟,就是Mvvm篇中的流程。加入Component功能我们几乎不用动Mvvm部分的代码,只要在调用Scanner扫描viewModel和view的映射关系之前,我们把含有Component tag的主DOM Tree升级加工即可。其实Component定义文件中的<template/>和<script/>也是view和viewModel的关系。我们把Component的定义文件渲染到主DOM Tree上翻译成view和viewModel的关系,再由Mvvm篇中的Scanner,Renderer做扫描和渲染即可。是不是很简单?

我们把着重点放在黑框上面的部分,也就是生成Component的流程,我们更加图中的steps,一步步来看
先放张局部大图
图片描述

Step 1:RegisterComponent,这一步,是要在SF库init初始化之前就要进行的,我们给新的component起个名字tagName,比如my-comp,这样我们在以后就可以用<my-comp/>这个tag在任何地方引入这个component,另外还需要把component定义文件的路径告诉SF,方便其在初始化的时候加载这些定义文件

//伪代码
var sf = new SegmentFault();
sf.registerComponent("my-comp","./components/myComp.html");
sf.init();

Step 2 3 4: Loader加载器在sf.init()时被调用,去加载"./components/myComp.html"这些路径上的component定义文件,并且把这些定义和tagName形成一份Map返回给SF

Step 5: (第五步相当重要,web component 99%的精髓都在这一步,请认真阅读)
SF拿到Map后,通知Generator去扫描DOM Tree一旦发现有<my-comp/>标签出现,则使出DOM替换大法,把Map中my-comp对应的component定义中的template部分的DOM,替换上去。如下图

DOM替换大法
图片描述

思考题:

Q:辣么,Component定义中的<script>里的viewModel怎么办?
先上一张美图,大家思考3分钟

图片描述

A: 使用new Function()或者eval把<script/>中的viewModel生成一个function,我们姑且叫它CompViewModel,然后把template
上的sf-xxx=“this.xxxx”的attribute中,等式的左边全部含有this的部分,替换成vm_随机数,比如

<template>
    <div>
        <p sf-innerText="this.message"></p>
    </div>
</template>

替换成

<template>
    <div>
        <p sf-innerText="vm_2333.message"></p>
    </div>
</template>

还记得这个vm_2333是什么的?没错如果你没有白看Mvvm那两篇教程的话,vm_2333就是viewModel实例的alias。那还等什么?赶紧调用sf.registerViewModle("vm_2333",new CompViewModel())注册这个component的viewModel吧!
图片描述

有没有发现web component库的套路,是不是很简单?第一步,把index.html(或父组件)中含有的component tagName找出来,然后一通替换和伪装,第二步,让SF把它当成Mvvm篇中的普通view-viewModel和关系去处理即可。

template和script处理完了,那么接下去就只剩下style,如何让<style>标签中的css定义的作用于只发生在当前?

我给出的办法是,还是移花接木大法

<sf-component>
    <style>
        p{
            color:red;
        }
    </style>
</sf-component>

第一步,要把这坨css加到index.html的head中去,这样css才会生效,但是这样会污染全局

<html>
    <head>
        ...
        <style type=text/css>
            p{
                color:red;
            }
        </style>
    </head>

第二步,把这坨css进行加工,加上作用域 .myComp ,这样的话,根据css selector语义,只有在class="myComp"的DOM下的p元素才会生效

<html>
    <head>
        ...
        <style type=text/css>
            .myComp p{
                color:red;
            }
        </style>
    </head>

第三步,当然是给代表component的DOM最外层上增加一个叫myComp的class了,一个简单版的Shadow Style就这样实现了。

至此,一个component的三大组成说完,四大功能还剩communication没有讲

组件通讯

很简单,实现2个接口即可实现组件通讯,具体见上篇《设计篇》有提到

1. Component的属性可以被父组件set进值
2. viewModel可以向外dispatch事件

这2个接口都可以在step 5中通过一些小动作,给加进去,本篇就不具体解释了,作为思考题大家回去思考,俗话说的的talk is cheap,show me the code,具体实现会在第三篇《代码篇》中给出,大家看了自己一目了然,比我这里浪费口舌能更好的理解。

最后

《原理篇》到此结束,下一篇《代码篇》会在这两日放出,不过前提还是点赞超10,最后欢迎大家点赞评论收藏,投硬币,投香蕉,我们下次再见。

相关阅读

【教学向】150行代码教你实现一个低配版的MVVM库(1)- 原理篇
【教学向】150行代码教你实现一个低配版的MVVM库(2)- 代码篇
【教学向】再加150行代码教你实现一个低配版的web component库(1) —设计篇
【教学向】再加150行代码教你实现一个低配版的web component库(2) —原理篇


熊丸子
5.6k 声望293 粉丝

现在sf的文章质量堪忧~~~