在线演示:https://momoko8443.github.io/...css
书接上文 【教学向】再加150行代码教你实现一个低配版的web component库(1) —设计篇html
上文说道一个基本款的custom web component由3大部分组成,同时也必须具有4大功能git
三大部分是github
1. Template(DOM) 2. Style 3. Script (viewModel)
四大功能是web
1. Mvvm 2. Shadow Style 3. Communication 4. Lifecycle (本文不涉及LC的API,但在实现中会隐形的涵盖这部份内容)
Component定义文件格式以下segmentfault
<!-- 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>
接下去,本篇就会一一讲解各个点的原理和大体实现思路,具体代码会在第三篇 代码篇中给出mvvm
黑框如下是否是很眼熟,就是Mvvm篇中的流程。加入Component功能咱们几乎不用动Mvvm部分的代码,只要在调用Scanner扫描viewModel和view的映射关系以前,咱们把含有Component tag的主DOM Tree升级加工便可。其实Component定义文件中的<template/>和<script/>也是view和viewModel的关系。咱们把Component的定义文件渲染到主DOM Tree上翻译成view和viewModel的关系,再由Mvvm篇中的Scanner,Renderer作扫描和渲染便可。是否是很简单?this
咱们把着重点放在黑框上面的部分,也就是生成Component的流程,咱们更加图中的steps,一步步来看
先放张局部大图spa
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) —原理篇