elementUI源码分析-03-container、button、link

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了elementUI源码分析-03-container、button、link相关的知识,希望对你有一定的参考价值。

参考技术A

用于布局的容器组件,方便快速搭建页面的基本结构。

<el-container> :外层容器。当子元素中包含 <el-header> 或 <el-footer> 时,全部子元素会垂直上下排列,否则会水平左右排列。

<el-header> :顶栏容器。

<el-aside> : 侧边栏容器。

<el-main> :主要区域容器。

<el-footer> :底栏容器。

<el-container> 的子元素只能是后四者,后四者的父元素也只能是 <el-container> 。

也就是说, <el-container> , <el-header> , <el-aside> , <el-main> , <el-footer> 只能是组合出道, 因为以上采用了flex 布局,也就是说这套组件就是使用了flex布局,是flex布局属性排列组合后的各种布局方式。

el-container这个组件只接受一个参数就是direction,用来描述子元素的排列方向.
默认情况下,如果子元素中有el-header或el-footer时为竖向排列,其余情况下默认横向排列。

源码如下

默认的el-container容器有一个el-container类名,用来设置默认style样式

通过接收的direction的值,在结合computed的isVertical属性,判断是否给元素添加is-vertical的class类名。如果有,则用is-vertical的样式去覆盖el-container的子元素排放方式的样式。

el-header组件,只接受一个height属性,用来添加style设置高度,默认为60px。

源码如下

el-header的默认样式

el-aside只接收一个width组件,用来设置侧边栏的宽度,默认是300px

el-aside默认样式

el-main只是一个包括的容器

el-main的默认样式

el-footer与el-hearder类似,只接收一个height属性,用来设置footer的高度。

el-footer的默认样式

el-button实际上就是对原生button的再封装。

el-button组件的引用方式如下

el-button的参数列表如下

再结合el-button源码,看一下具体原理

el-button的默认样式如下

display: inline-block; 表示其为内联元素,可横向排列
white-space: nowrap; 表示文本不换行
cursor: pointer; 鼠标滑过显示小手形状
-webkit-appearance: none; 将按钮原始样式去掉
line-height: 1; 行高等于字体大小

type: 通过接受type值,来生成描述背景色等的lass名、比如type=primary, 即el-button--primary,用来设置按钮的背景色,字体颜色、边框颜色。

size : 通过设置size,来生成描述按钮大小的class名,比如size=small, 即el-button--small。

plain : 通过设置plain,用来生成描述对应type下的朴素样式的按钮、

round : 用来描述是否是圆角按钮,来生成对应描述圆角的class名,is-round

circle : 是否是原型按钮,来生成对应50%圆角的class名,is-circle

loading : 是否是加载中按钮,如果是加载中的按钮,则给元素添加el-icon-loading类,添加在加载的图标
disabled : 是否是禁用状态,添加对应的类名is-disabled,并且给原生button设置disabled="disabled",使其不可点击

icon : 通过设置icon,可以添加的内部提供的图标
autofocus : 用来设置按钮是否聚焦,如果autofocus=true,则给原生button设置autofocus="autofocus"
native-type : 用来设置原生button的type属性,用来描述按钮的类型,即button、reset、submit

inject是跟表单相关的,等后面表单再说吧。

按钮组

源码如下

el-button-group就是用来嵌套一组el-button,并且相邻两个按钮内侧的圆角去掉,且有一条空白线相连。具体实现的style如下

其实就是运用选择器,将第一个按钮右侧圆角,最后一个按钮左侧圆角,以及中间按钮的全部圆角去掉,即形成了圆角设置,空白线也是类似处理。因为按钮默认都有border,颜色和按钮主体颜色一致,这里只是修改了border的颜色

文字超链接

参数如下

el-link,其实是对原生a标签的封装。

源码如下

el-link的默认样式如下

type : 通过设置type,添加对应的class类,给a标签设置对应的字体颜色

underline : 是否有下滑线,如果添加,那么在hover的时候,会添加一个after伪类来创建下划线

disabled : 是否是禁用状态,如果设置了,会根据对应type,设置对应样式

href : 即设置原生a标签的href属性
icon : 通过设置icon,可以添加的内部提供的图标

Jetty - Container源码分析

1. 描述

Container提供管理bean的能力。

基于Jetty-9.4.8.v20171121。

1.1 API

public interface Container
{
    // 增加一个bean,如果bean是一个Container.Listener则隐含调用addEventListener(Container.Listener)方法
    // Container.Listener只关心两个事件:(1)增加bean(2)删除bean
    public boolean addBean(Object o);

    // 返回该Container里面所有的bean
    public Collection<Object> getBeans();

    // 返回指定类型(包括子类)的bean
    public <T> Collection<T> getBeans(Class<T> clazz);

    // 返回指定类型(包括子类)的第一个bean,如果不存在则返回null
    public <T> T getBean(Class<T> clazz);

    // 删除指定的bean,如果bean是一个Container.Listener,隐含调用removeEventListener(Container.Listener)
    public boolean removeBean(Object o);
    
    // 增加一个Listener
    public void addEventListener(Listener listener);
    
    // 删除一个Listener
    public void removeEventListener(Listener listener);

    // 未托管一个bean(必须已经存在在Container里面),所以该bean不应该启动,停止或销毁
    void unmanage(Object bean);

    // 托管一个bean(必须已经存在在Container里面),所以该bean已启动,已停止或销毁
    void manage(Object bean);


    // 检测该Container是否托管一个bean
    boolean isManaged(Object bean);

    // 增加一个bean,并且明确是否托管(即是否管理该bean的生命周期)
    // 如果已经增加返回true,如果已经存在返回false 
    boolean addBean(Object o, boolean managed);

    // Container事件的监听器
    // 如果一个增加的bean实现该接口将会收到该Container的事件
    public interface Listener
    {
        void beanAdded(Container parent,Object child);
        void beanRemoved(Container parent,Object child);
    }
    
    /**
     * Inherited Listener.
     * If an added bean implements this interface, then it will 
     * be added to all contained beans that are themselves Containers
* 如果增加的bean实现该接口,则将该bean增加到当前Container里面所有bean类型为Container里面。 */ public interface InheritedListener extends Listener { } /** * @param clazz the class of the beans * @return the list of beans of the given class from the entire managed hierarchy * @param <T> the Bean type */ public <T> Collection<T> getContainedBeans(Class<T> clazz); }  

从API可以看出Container主要维护bean并且监听bean的增加和删除事件。

 

1.2 类图

从类图可以看出,Container与LifeCycle接口很类似,都是很多组件的基本特征,其默认实现是ContainerLifeCycle。

2. ContainerLifeCycle

1.2类图可以看出ContainerLifeCycle不仅是Container的默认实现,而且也是很多组件(Connector,Handler等)默认实现的父类。

2.1 类图

 

ContainerLifeCycle自然要实现Container接口; 

ContainerLifeCycle继承AbstractLifeCycle,而AbstractLifeCycle里面实现了LifeCycle的模板启停方法start和stop;

继承AbstractLifeCycle的子类只需要实现AbstractLifeCycle中增加的doStart和doStop实现子类具体的启动和停止,具体请参考【Jetty - LifeCycle源码分析】

ContainerLifeCycle.Bean:内部类,表示管理的Bean对象。

ContainerLifeCycle.Managed:内部类,被管理的Bean有几种类型:POJO,MANAGED,UNMANAGED,AUTO。

2.2 doStart和doStop

  启动主要分为如下两个步骤:

 (1)设置标志位_doStart = true;

 (2)启动具有生命周期的bean(a)如果托管bean并且未运行的,则启动(b)如果是自动bean并且运行中,则设置为未托管;未运行的,则设置为托管,并且启动; 

    // 以添加的顺序启动托管的bean
    @Override
    protected void doStart() throws Exception
    {
        if (_destroyed)
            throw new IllegalStateException("Destroyed container cannot be restarted");

        // 标示已经启动,addBean可以启动其他的bean
        _doStarted = true;

        // 启动托管和自动beans
        for (Bean b : _beans) // 遍历所有bean
        {
            if (b._bean instanceof LifeCycle)
            {
                LifeCycle l = (LifeCycle)b._bean;
                switch(b._managed)
                {
                    case MANAGED: // 如果是托管bean,并且未运行,则启动
                        if (!l.isRunning())
                            start(l);
                        break;
                    case AUTO: // 如果是自动bean
                        if (l.isRunning()) // 如果已经运行了,则设置为未托管
                            unmanage(b);
                        else // 如果未运行,设置为托管,并且启动
                        {
                            manage(b); 
                            start(l);
                        }
                        break;
                }
            }
        }
        // 此处调用父类的doStart方法,就是AbstractLifeCycle的doStart方法,其实是个空实现
        super.doStart();
    }  

停止主要分为两个步骤:

(1)设置标志位;

(2)逆序停止具有生命周期的托管bean,为什么逆序?主要与启动顺序比较,防止bean之间有关联出现错误,类似资源释放。

    // 以添加的逆序停止具有生命周期的托管bean
    @Override
    protected void doStop() throws Exception
    {  
        _doStarted = false; // 设置停止状态位
        super.doStop(); // 调用AbstractLifeCycle的doStop方法,其实是个空方法
        List<Bean> reverse = new ArrayList<>(_beans);
        Collections.reverse(reverse); // 逆序
        for (Bean b : reverse) 
        {   // 具有生命周期并且托管的bean
            if (b._managed==Managed.MANAGED && b._bean instanceof LifeCycle)
            {
                LifeCycle l = (LifeCycle)b._bean;
                stop(l);
            }
        }
    }  

2.3 addBean

// o:bean,managed:bean类型
// 注意基本原则:在ContainerLifeCycle类里面有两个字段_beans和_listener,如果添加的bean也是Container.Listener类型,则需要在_listener里面也增加一个
public boolean addBean(Object o, Managed managed)
    {
        if (o==null || contains(o)) // 如果bean为null或者已经存在
            return false;

        Bean new_bean = new Bean(o); // 包装为Bean对象

        // 如果bean是Container.Listener
        if (o instanceof Container.Listener)
            addEventListener((Container.Listener)o);

        // 添加bean
        _beans.add(new_bean);

        // 通知所有_listeners,有新bean添加的事件
        for (Container.Listener l:_listeners)
            l.beanAdded(this,o);

        try
        {
            switch (managed)
            {
                case UNMANAGED:
                    unmanage(new_bean);
                    break;

                case MANAGED:
                    manage(new_bean);
                    // 如果ContainerLifeCycle在启动中,即调用doStart还没有退出
                    if (isStarting() && _doStarted) 
                    {  // 此处o是一个任意类型且是个public方法,此处直接转为LifeCycle是否有问题?
                        LifeCycle l = (LifeCycle)o;
                        // 为什么有这样的判断?
                        // doStart的过程(1)设置状态位(2)以bean添加的顺序启动具有生命周期的bean,如果此时调用了addBean有可能同步的问题,导致新添加的bean没有通过doStart启动,所以需要在此处判断如果未启动,则启动一下
                        if (!l.isRunning()) 
                            start(l);
                    }
                    break;

                case AUTO:
                    if (o instanceof LifeCycle)
                    {
                        LifeCycle l = (LifeCycle)o;
                        if (isStarting()) // 如果ContainerLifeCycle启动中
                        {
                            if (l.isRunning()) // 如果bean运行中,则设置为未托管,不需要ContainerLifeCycle管理启动
                                unmanage(new_bean);
                            else if (_doStarted) // 如果bean未运行,并且ContainerLifeCyle启动中,则设置为托管bean并且启动之
                            {
                                manage(new_bean);
                                start(l);
                            }
                            else
                                new_bean._managed=Managed.AUTO;      
                        }
                        else if (isStarted()) // 如果ContainerLifeCycle已经启动
                            unmanage(new_bean);
                        else // ContainerLifeCycle未启动
                            new_bean._managed=Managed.AUTO;
                    }
                    else
                        new_bean._managed=Managed.POJO;
                    break;

                case POJO:
                    new_bean._managed=Managed.POJO;
            }
        }
        catch (RuntimeException | Error e)
        {
            throw e;
        }
        catch (Exception e)
        {
            throw new RuntimeException(e);
        }

        if (LOG.isDebugEnabled())
            LOG.debug("{} added {}",this,new_bean);

        return true;
    }

 

// 删除bean
private boolean remove(Bean bean)
    {
        if (_beans.remove(bean))
        {
            boolean wasManaged = bean.isManaged(); // bean是否是托管类型
            
            unmanage(bean); // 设置bean为未托管类型

            for (Container.Listener l:_listeners) // 通知监听器
                l.beanRemoved(this,bean._bean);
            // 如果被remove的bean是Listener,需要调用removeEventListener
            if (bean._bean instanceof Container.Listener) 
                removeEventListener((Container.Listener)bean._bean);

            // 如果是具有生命周期托管的bean需要停止。
            if (wasManaged && bean._bean instanceof LifeCycle)
            {
                try
                {
                    stop((LifeCycle)bean._bean);
                }
                catch(RuntimeException | Error e)
                {
                    throw e;
                }
                catch (Exception e)
                {
                    throw new RuntimeException(e);
                }
            }
            return true;
        }
        return false;
    }

 

2.4 插播Container管理bean的规则

通过前面的doStart和addBean可以基本确定Container管理bean的如下几条规则:

ContainerLifeCycle是对容器化bean组件的一个生命周期的实现。

bean可以作为托管bean或未托管bean放入ContainerLifeCycle里面。

托管bean的启动停止和销毁由ContainerLifeCycle控制;未托管主要是为了dump,它们的生命周期必须独立管理。

当一个没有指定类型具有生命周期的bean加入到ContainerLifeCycle,ContianerLifeCycle可以推断它的类型:

(1)如果增加的bean运行中,它将以未托管类型加入container;

(2)如果增加的bean未运行且container也未运行,它将以AUTO类型加入container;

(3)如果增加的bean未运行且container在启动中,它将以托管类型加入container;

(4)如果增加的bean未运行且container已经启动,它将以未托管类型加入container;

当container已经启动,所有的托管bean也应该启动。

任何AUTO类型的bean都将依据它们的状态被分为托管或未托管,如果已经启动则为未托管,否则将启动它们然后设置为托管类型。

Contianer启动之后添加的bean将不会被启动,它们的状态需要显式管理。

当停止Container的时候,只有被这个Container启动的bean才会停止。

如果一个bean被多个Container共享,那么该bean只能是未托管的,即在增加之前,应该被启动

2.4.1 实例

 

2.5 manage和unmanage

 manage是设置bean为托管类型,unmanage设置bean为未托管类型。

可以理解为两个相反的操作,需要注意如果被设置的bean是个Container,则需要将当前_listeners里面所有类型为InheritedListener的监听器添加到该bean里面或从该bean里面移除。

// 托管bean
private void manage(Bean bean)
    {
        if (bean._managed!=Managed.MANAGED)
        {
            bean._managed=Managed.MANAGED; // 设置bean为托管

            if (bean._bean instanceof Container)
            {
                for (Container.Listener l:_listeners)
                {
                    if (l instanceof InheritedListener) // 如果当前bean的listener里面有是InheritedListener需要增加到bean的_beans列表中
                    {
                        if (bean._bean instanceof ContainerLifeCycle)
                            ((ContainerLifeCycle)bean._bean).addBean(l,false);
                        else
                            ((Container)bean._bean).addBean(l);
                    }
                }
            }

            if (bean._bean instanceof AbstractLifeCycle)
            {
                ((AbstractLifeCycle)bean._bean).setStopTimeout(getStopTimeout());
            }
        }
    }

 

// 未托管bean    
private void unmanage(Bean bean)
    {
        if (bean._managed!=Managed.UNMANAGED)
        {
            if (bean._managed==Managed.MANAGED && bean._bean instanceof Container)
            {
                for (Container.Listener l:_listeners)
                {  // 如果监听器是InheritedListener,需要将其从未托管的bean中移除
                    if (l instanceof InheritedListener)
                        ((Container)bean._bean).removeBean(l);
                }
            }
            bean._managed=Managed.UNMANAGED;
        }
    }

  

2.6 addEventListener和removeEventListener

两个是相反的操作,一个是增加Listener,另一个是删除Listener。

如果待操作的Listener是InheritedListener子类,需要级联操作。

@Override
    public void addEventListener(Container.Listener listener)
    {
        if (_listeners.contains(listener))
            return;
        
        _listeners.add(listener);

        // 新加的Listener需要被告知所有bean
        for (Bean b:_beans)
        {
            listener.beanAdded(this,b._bean);

            // 如果是InheritedListener需要增加到bean为Container的_beans列表中
            if (listener instanceof InheritedListener && b.isManaged() && b._bean instanceof Container)
            {
                if (b._bean instanceof ContainerLifeCycle)
                     ((ContainerLifeCycle)b._bean).addBean(listener, false);
                 else
                     ((Container)b._bean).addBean(listener);
            }
        }
    }

 

  @Override
    public void removeEventListener(Container.Listener listener)
    {
        if (_listeners.remove(listener))
        {
            // remove existing beans
            for (Bean b:_beans)
            {
                listener.beanRemoved(this,b._bean);
                // 与增加相反,需要级联移除
                if (listener instanceof InheritedListener && b.isManaged() && b._bean instanceof Container)
                    ((Container)b._bean).removeBean(listener);
            }
        }
    }  

2.7 updateBean  

最后ContainerLifeCycle还提供了重载的updateBean,入参一般是一个老bean和一个新bean。

一般操作都是先删除老bean,然后增加新bean,都是复用上面提到的removeBean和addBean,不在详细描述。 

以上是关于elementUI源码分析-03-container、button、link的主要内容,如果未能解决你的问题,请参考以下文章

从 ElementUI 源码的构建流程来看前端 UI 库设计

Vue 组件创建的流程源码分析

从零开始,用elementui躺坑vue - Router原理分析

修改elementUI源码新增组件/修改组件

修改elementUI源码新增组件/修改组件

vue2使用elementUI进行表单验证实操(附源码)