动图镇楼
在线演示: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) —原理篇