源码级剖析了 Naive UI 的 Button 完整过程

前端迷

共 35482字,需浏览 71分钟

 ·

2021-11-15 20:01

注意:为了让篇幅尽可能简洁一丢丢,在有些地方贴源码时,我尽可能贴最能反映要讲解内容的源码,其他重复性的代码就略去了,所以如果你自己尝试去阅读源码时,可能会发现和文章里的代码有出入。文章跑通 Naive UI 所用到的源码仓库为:https://github.com/pftom/naive-app[1]

简洁的抽象

前端开发者现在几乎已经离不开 UI 组件库了,典型的如 Ant Design、Material Design、以及最近 Vue 生态兴起的 Naive UI 等,组件库提供了简单、灵活、易用的使用形式,如一个页面中最常见的 Button 的使用如下:


上述几行简单的代码就可以完成如下有意思的效果:

甚至是,可以一键切换皮肤,如 Dark Mode:

当然还可以处理事件、添加 Icon、处理 Loading 等,通过简单给定一些 Props,我们就可以拥有一个好看、实用的 Button,相比原始的 HTML 标签来说,实在是不可同日而语...

冰山理论

组件库在带来灵活、方便的同时,其内部的原理却并非如它使用般简单,就像上述的冰山图一样引人深思。

让我们翻一翻最近的 Vue 组件库新秀 Naive UI 的 CHANGELOG,就可以窥见编写一个入门的组件库大致需要多少时间:

可以看到,2020-03-21 就发布了 1.x 版本,而在 1.x 之前又是漫长的思考、设计与开发,至今应该差不多两年有余。

而为了跑通一个 Naive UI 的 Button,大致需要如下的文件或代码:

.

|_____utils

| |____color

| | |____index.js

| |____vue

| | |____index.js

| | |____flatten.js

| | |____call.js

| | |____get-slot.js

| |____index.js

| |____naive

| | |____warn.js

| | |____index.js

| |____cssr

| | |____create-key.js

| | |____index.js

|_____internal

| |____loading

| | |____index.js

| | |____src

| | | |____Loading.jsx

| | | |____styles

| | | | |____index.cssr.js

| |____index.js

| |____icon-switch-transition

| | |____index.js

| | |____src

| | | |____IconSwitchTransition.jsx

| |____fade-in-expand-transition

| | |____index.js

| | |____src

| | | |____FadeInExpandTransition.jsx

| |____wave

| | |____index.js

| | |____src

| | | |____Wave.jsx

| | | |____styles

| | | | |____index.cssr.js

| |____icon

| | |____index.js

| | |____src

| | | |____Icon.jsx

| | | |____styles

| | | | |____index.cssr.js

|_____styles

| |____common

| | |_____common.js

| | |____light.js

| | |____index.js

| |____transitions

| | |____fade-in-width-expand.cssr.js

| | |____icon-switch.cssr.js

| |____global

| | |____index.cssr.js

|____config-provider

| |____src

| | |____ConfigProvider.js

|____button

| |____styles

| | |_____common.js

| | |____light.js

| | |____index.js

| |____src

| | |____Button.jsx

| | |____styles

| | | |____button.cssr.js

|____assets

| |____logo.png

|_____mixins

| |____use-style.js

| |____use-theme.js

| |____index.js

| |____use-form-item.js

| |____use-config.js

看似困难的背后

虽然跑通一个看似简单的



        ) : null}

        // 第四部分

        

          {{

            default: () =>

              $slots.icon || this.loading ? (

                
                  class={`${mergedClsPrefix}-button__icon`}

                  style={{

                    margin: !$slots.default ? 0 : "",

                  }}

                >

                  

                    {{

                      default: () =>

                        this.loading ? (

                          
                            clsPrefix={mergedClsPrefix}

                            key="loading"

                            class={`${mergedClsPrefix}-icon-slot`}

                            strokeWidth={20}

                          />

                        ) : (

                          
                            key="icon"

                            class={`${mergedClsPrefix}-icon-slot`}

                            role="none"

                          >

                            {renderSlot($slots, "icon")}

                          


                        ),

                    }}

                  

                

              ) : null,

          }}

        

        // 第三部分

        {$slots.default && this.iconPlacement === "left" ? (

          ${mergedClsPrefix}-button__content`}>{children}

        ) : null}

        // 第五部分

        {!this.text ? (

          "waveRef" clsPrefix={mergedClsPrefix} />

        ) : null}

        // 第六部分

        {this.showBorder ? (

          
            aria-hidden

            class={`${mergedClsPrefix}-button__border`}

            style={this.customColorCssVars}

          />

        ) : null}

        // 第六部分

        {this.showBorder ? (

          
            aria-hidden

            class={`${mergedClsPrefix}-button__state-border`}

            style={this.customColorCssVars}

          />

        ) : null}

    

    )

  }

});

可以看到,上述的主要展示出了

浏览 156
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报