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

前言

上两篇Mvvm教程的热度超出个人预期,不少码友留言表扬同时但愿我继续出下一篇教程,当时我也半开玩笑说只要点赞超10就兑现承诺,没想到还真破了10,因此就有了今天的文章。css

准备工做

熟读
【教学向】150行代码教你实现一个低配版的MVVM库(1)- 原理篇
【教学向】150行代码教你实现一个低配版的MVVM库(2)- 代码篇html

本篇是在上两篇的基础之上对代码进行进一步扩展,从而实现web component功能,因此读者务必掌握mvvm的实现机制才能深刻理解本篇的内容(mvvm是web component的基石)。前端

什么才是好的 web component 设计

目前市面上各大主流前端框架,凡事带web component功能的,他们的设计水准基本都不入个人法眼,惟一看得上眼的是google的polymer,可是在某些API设计层面也显得略微繁琐(想了解polymer的朋友看一翻一下我专栏里面10篇polymer入门系列教程web

什么是component

html提供的原生标签,好比DIV, BUTTON, INPUT家族,Hx家族等等,这些就比如俄罗斯方块里的一块块标准积木,咱们称它们为stand component
2164182735-560b52e32e896segmentfault

某一天这些积木不能知足你的需求了,被扩展或被组合造成了非基本形状
2061348422-560b891619854api

这些新形状就是custom component,自定义组件!为何要有component呢,好处是什么呢?前端框架

1. 能够复用
 2. 结构清晰
 3. 独立开发

你稍微开动下脑筋就能分析出来了,我就不展开了。框架

我心目中的web component

在座的各位都写过index.html么?很简单
主要就分红3大块内容,style, dom, scriptdom

<!--样式-->
<style>
</style>
<!--DOM UI-->
<body>
</body>
<!--逻辑-->
<script>
</script>

而后外面用个<html></html>包裹mvvm

因此这期低配版web component库设计目的很简单,做为一个开发人员,我但愿在写一个custom component的时候也能按照index.html的原生风格来写,这是多么的优雅,天然,没有学习成本啊!
这也应该是无数人心目中的web component设计

API设计

因此,咱们的SegmentFault.js v2.0的Web Component的设计宗旨就是,尽可能接近原生的html结构和使用习惯,接近原生从而把学习成本降到最低,是我追求的东西

<!-- myComp.html //文件名仍是以.html结尾,天然 -->
<sf-component>
    <style>
        <!--css-->
    </style>
    <template>
        <!-- any dom-->
    <template>
    <script>
        //js
    </script>
<sf-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>

一个Component的描述文件定义好了,那么接下去就是如何引入它了。沿用上篇Mvvm中的风格,咱们给SegmentFault这个Class弄个registerComponent(tagName,compPath)方法,好比在index.html中

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

而在父组件中咱们就能够经过"my-comp"这个咱们刚刚注册时起的标签名来引入这个组件

<body>
    <div>...<div>
    <my-comp></my-comp>
</body>

怎么样!四个字:干净利落

一个Web Component库必须具有的基本素(功)养(能)

1. Mvvm 具有双向绑定功能
 2. Shadow Style 具备独立的不污染全局的css功能
 3. Communication 具备和父子兄弟组件通信的功能
 4. 拥有生命周期 (属于高级功能,本低配版库不涉及)

第一点Mvvm

Mvvm以前已经实现,咱们只要套用以前的实现便可

第二点Shadow Style

可能不少人对这个没什么概念,我沿用前文中的内容,好比咱们在component中定义了它的style,如

<!-- myComp.html -->
<sf-component>
    <style>
        button{
            color:red;
        }
        p{
            color:yellow;
        }
    </style>
    <template>
        <div>
            ...
        </div>
    </template>
    <script>
        ...
    </script>
</sf-component>

这里咱们在<style></style>标签中,定义了css,其中p和button的写法在传统观念中都是会影响html页面中全部的p元素和button元素的,这是咱们不但愿发生的,咱们但愿的是这个<style></style>标签生效的做用域仅仅是在当前的,被定义的component中。这种有独立做用域的css就叫Shadow Style。

要实现Shadow Style,其实有比较简单的作法,本篇设计篇中不会涉及,你能够趁此独立思考下,待下篇看看是否与我不谋而合,或者有比我更加高级的方案。

第三点Communication

即组件之间的通信,常常有人在sf中问到这个组件通信问题,其实这个问题是有比较标准的答案的,即3点

1. 父子通信: 父->子 经过 set 属性, 子->父 抛事件
 2. 兄弟通信: 大儿子 抛事件给 -> 父 -> set 小儿子 的属性
 3. 远亲通信: 走消息总线 (其实就在一个单例上搞事件机制)

要实现通信机制,其实也不复杂,主要就2个功能,1 父组件能够set子组件的属性, 2 组件能够向外层抛事件,外层也能够监听组件抛出的事件,因此,咱们会如此设计这块的内容,觉个例子,代码说话

<body>
    <div>...<div>
    <my-comp sf-msg="vm.message" sf-oncustomevent="vm.customEventHandler"></my-comp>
</body>

你们注意看,从父组件的角度,我可使用sf- + propertyName(这里是msg) 来实现外部父组件对组件的赋值,并且还能使用sf-on + 自定义事件名称(这里是customevent) 对组件进行监听。

换个角度,从子组件角度出发,我能够被外部赋值,我能够能够向外部dispatch事件。

<sf-component>
    <style>
        //...
    </style>
    <template>
        <div>
            <div class="compClass">
                <input type="text" sf-value="this.message" />
                <button sf-innerText="this.buttonName" onclick="this.clickHandler()"></button>
                <my-comp2 sf-msg="this.message"></my-comp2>
                <p sf-innerText="this.message + ', hi Component1'">
                </p>
            </div>
        </div>
    </template>
    <script>
        this.buttonName = "click me";
        this.clickHandler = function () {
            alert(this.message);
            this.dispatchEvent("customevent", "hello world");//为component的vm,内置一个dispatchEvent方法,用法和原生的事件机制一毛同样。
        };
        Object.defineProperty(this, "msg", {
            set: function (value) {
                if (value) {
                    this.message = value;
                }
            }
        });
    </script>
</sf-component>

使用Object.defineProperty能够很大程度上知足咱们对set property的需求,另外再给component的vm挂载一个内置的函数this.dispatchEvent来发送自定义事件咱们就功德圆满了。

第四点生命周期

你能够给一个组件:由注册->加载定义->显示到DOM Tree->内容更新->从DOM Tree移除->销毁 等一系时间节点定义他的生命周期,若是是作的比较考究的库,你能够把这这些时间节点的变动都一一贯用户通知,或者提供api供用户控制。本文设计的低配版库阉割了这部分高级功能,咱们就是一教学向的库,不整这些有的没的。

结语

至此,设计篇结束,主要介绍了一下本教学库的设计理念和一些web component的基本概念,欢迎点赞收藏评论,投硬笔投香蕉

若是本文阅读没有问题,请继续服用下一篇
【教学向】150行代码教你实现一个低配版的MVVM库(2)- 代码篇

相关阅读

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

相关文章
相关标签/搜索