初步解析小程序前端框架vant-ui源码

初步解析小程序前端框架vant-ui源码


本学期的系统分析课程要求我们做一个小项目,我们以微信小程序为框架进行了项目的前端搭建,在UI上以开源组件库vant-ui为基础进行了设计,其中用到了许多该开源库的设计,对于项目前端起到了很大的帮助。

组件库的使用教程在 https://youzan.github.io/vant-weapp/#/intro ,介绍说明比较详细且简单,因此这里不再赘述,这里希望对于框架的组成和实现方式进行初步的窥探。


应用实例

以Popup 弹出层为例,这个组件的实现功能如下图所示,可以实现在页面的各个位置以不同的方式弹出提示框选择框的效果

  • 效果
    初步解析小程序前端框架vant-ui源码_第1张图片

  • 使用方法:

    • 引入组件
    "usingComponents": {
        "van-popup": "path/to/vant-weapp/dist/popup/index"
    }
    
    • 代码演示
    • 基础用法
    • popup默认从中间弹出
    <van-popup show="{{ show }}" bind:close="onClose">内容</van-popup>
    Page({
    data: {
        show: false
    },
    
    onClose() {
        this.setData({ show: false });
    }
    });
    
    • 弹出位置
    • 通过position属性设置 Popup 弹出位置
    <van-popup
    show="{{ show }}"
    position="top"
    overlay="{{ false }}"
    bind:close="onClose"
    >
    内容
    van-popup>
    
  • API

    参数 说明 类型 默认值
    show 是否显示弹出层 Boolean false
    z-index z-index 层级 Number 100
    overlay 是否显示背景蒙层 Boolean true
    position 可选值为 top bottom right left String -
    duration 动画时长,单位为毫秒 Number / Object 300
    custom-style 自定义弹出层样式 String ``
    overlay-style 自定义背景蒙层样式 String ``
    close-on-click-overlay 点击蒙层是否关闭 Popup Boolean true
    safe-area-inset-bottom 是否为iPhoneX留出底部安全距离 Boolean true
    safe-area-inset-top 是否留出顶部安全距离(状态栏高度 + 导航栏高度) Boolean false

解析框架

根据上面的例子可以看出,这个框架实现的功能以及可调用的接口还是比价丰富的,那么首先的问题是组件库是怎么知道调用哪个组件?

  • 在配置文件引入组件时,使用了"van-popup": "path/to/vant-weapp/dist/popup/index",这样wxml在解析对应的组件时,遇到van-popup就会调用对应的index的插入到wxml。下面会说明插入的index这个组件的组成成分。

知道了对应的调用组件之后,组建内部是如何实现对应的效果的?

  • index内部文件组成:

    • index.wxss
      对应的css,除此之外,这里还引用了@import '../common/index.wxss'里面的css,不介绍
    • index.json
      配置文件,说明了这个组件库引用了另一个组件van-overlay,比较简单,不介绍
    {
        "component": true,
        "usingComponents": {
            "van-overlay": "../overlay/index"
        }
    }
    
    • index.wxml
    <wxs src="../wxs/utils.wxs" module="utils" />
    
    <van-overlay
        wx:if="{{ inited && overlay }}"
        mask
        show="{{ show }}"
        z-index="{{ zIndex }}"
        custom-style="{{ overlayStyle }}"
        duration="{{ duration }}"
        bind:click="onClickOverlay"
    />
    <view
        wx:if="{{ inited }}"
        class="custom-class {{ classes }} {{ utils.bem('popup', [position, { safe: isIPhoneX && safeAreaInsetBottom }]) }}"
        style="z-index: {{ zIndex }}; -webkit-transition-duration:{{ currentDuration }}ms; transition-duration:{{ currentDuration }}ms; {{ display ? '' : 'display: none;' }} {{ customStyle }}"
        bind:transitionend="onTransitionEnd"
        >
        <view wx:if="{{ safeAreaInsetTop }}" class="van-popup__safe-top" style="padding-top: {{ statusBarHeight }}px;">view>
        <slot />
    view>
    
    

    van-overlay是这个组件库的另一个组件,这里用来实现遮罩层的效果。

    这里的双括号{{}}里面的变量都由下面的index.js来控制,留意到这个wxml除了被index.js控制,还引入了/wxs/utils.wxs,前往utils.wxs查看,可以看见如下代码:

    var bem = require('./bem.wxs').bem;
    var memoize = require('./memoize.wxs').memoize;
    
    function isSrc(url) {
        return url.indexOf('http') === 0 || url.indexOf('data:image') === 0 || url.indexOf('//') === 0;
    }
    
    module.exports = {
        bem: memoize(bem),
        isSrc: isSrc,
        memoize: memoize
    };
    

    这里有个判断url属性的函数isSrc(),还引用了bemmemioze的函数,其中bem是一个命名格式,将输入统一成bem的风格,memioze用来处理缓存,返回一个装有对应函数的缓存。

    function memoize(fn) {
        var cache = {};
    
        return function() {
            var key = serializer(arguments);
            if (cache[key] === undefined) {
            cache[key] = call(fn, arguments);
            }
    
            return cache[key];
        };
    }
    
    

    memioze用于将bem函数装入utils.wxsbem里面,这个bemindex.wxml里面被调用,如下面,用于处理class的名字的处理拼接。

    class="custom-class {{ classes }} {{ utils.bem('popup', [position, { safe: isIPhoneX && safeAreaInsetBottom }]) }}"
    

    总的来说,这个文件用于将API和调用转换为对应的class name,以此实现特定的css呈现效果。

    • index.js
      这个文件是对应的组件的显示、动画和控制逻辑代码。
    import { VantComponent } from '../common/component';
    import { transition } from '../mixins/transition';
    import { safeArea } from '../mixins/safe-area';
    VantComponent({
        classes: [
            'enter-class',
            'enter-active-class',
            'enter-to-class',
            'leave-class',
            'leave-active-class',
            'leave-to-class'
        ],
        mixins: [transition(false), safeArea()],
        props: {
            transition: {
                type: String,
                observer: 'observeClass'
            },
            customStyle: String,
            overlayStyle: String,
            zIndex: {
                type: Number,
                value: 100
            },
            overlay: {
                type: Boolean,
                value: true
            },
            closeOnClickOverlay: {
                type: Boolean,
                value: true
            },
            position: {
                type: String,
                value: 'center',
                observer: 'observeClass'
            }
        },
        created() {
            this.observeClass();
        },
        methods: {
            onClickOverlay() {
                this.$emit('click-overlay');
                if (this.data.closeOnClickOverlay) {
                    this.$emit('close');
                }
            },
            observeClass() {
                const { transition, position } = this.data;
                this.updateClasses(transition || position);
                if (transition === 'none') {
                    this.set({ duration: 0 });
                }
            }
        }
    });
    

    首先是VantComponent,这个是一个函数声明,没有包含任何的具体实现。

    declare function VantComponent<Data, Props, Watch, Methods, Computed>(
        vantOptions?: VantComponentOptions<Data, Props, Watch, Methods, Computed, CombinedComponentInstance<Data, Props, Watch, Methods, Computed>>
        ): void;
    

    与之类似,transitionsafeArea也只是函数的格式声明,没有任何的实现代码,具体的实现都在这个index.js文件。
    接着上面的说明,VantComponent在这个index.js进行了具体的定义,定义了classes mixins props created()methods,这里可以很明显的看出vue的风格,具体的原理可以查看vue对应的教程。这个函数实现了将本框架的特定字诸如vant-popup用基于vue的微信前端框架来解释和实现。

总结

总的来说,vant-ui是一个比较优秀的开源前端组件库,可以实现绝大多数的普通需求,用户在使用的时候也可以通过修改源码来实现自己的特定需求。如果说有什么不足之处,那就是css在设计的时候使用的单位都是px,没有一处是使用rpx这个单位,这个会造成组件在不同终端的显示的大小不能随终端而自我调整,希望可以在接下来的更新能有所修改。

你可能感兴趣的:(初步解析小程序前端框架vant-ui源码)