@wulechuan/vue2-ui--page
TypeScript icon, indicating that this package has built-in type declarations

0.1.9 • Public • Published

吴乐川的针对 Vue 2.x 的用户界面组件:可视页,即 Page(支持 TypeScript 语法)

Multilingual Editions of this Article

NPM 页

NPM 包名

@wulechuan/vue2-ui--page

作者

南昌吴乐川

简介

顾名思义,本组件系面向 Vue 2.x 框架制作的一种用户界面(UI)成分(或者说零件)。该零件之功用为轻松构建一个视觉上的所谓“页面”。

本组件认为,一个视觉上的页面由三个重要部分组成:【页眉】(page-header)、【页脚】(page-footer)和【页面主体】(page-body)。本组件同时提供此三个组成部分。并且,【页眉】与【页脚】为可选配件,可通过输入项(即 Vue 组件之所谓 prop)配置二者存在与否。【页面主体】之存在性不可配置,即【页面主体】恒存在。

本组件可解决一些用程序迎合美术设计思路之痛点。具体而言,常见之页面设计之布局有若干种,每种设计对应的程序实现均有所区别。本工具之设计目的即是一应俱全(流行语所谓“提供‘一站式’解决方案”),令采用者单用这一个可配置的“页面”组件,就可灵活应对多种设计要求。本组件所能满足的设计形式不妨列举如下:

  • 有否【页眉】。

  • 有否【页脚】。

  • 【页眉】是否悬挂在页面顶部,不随【页面主体】滚动。

  • 【页脚】是否沉淀在页面底部,不随【页面主体】滚动。

  • 【页眉】是否总是覆盖【页面主体】。这一设计要求往往为了追求美观,且【页眉】本身会有半透明底色,以使衬于其下的【页面主体】若隐若现。

  • 【页脚】是否总是覆盖【页面主体】。这一设计要求往往为了追求美观,且【页脚】本身会有半透明底色,以使衬于其下的【页面主体】若隐若现。

  • 是否强制【页面主体】之高度至少应充满整个窗口,即 min-height100%

又,以上设计要求组合变化,会得到有些复杂的结果。特进一步做如下琐碎之说明:

  • 即便【页眉】不悬挂于页面顶部,本组件仍允许将【页眉】配置为“覆盖于【页面主体】之上层”。如是,尽管【页眉】会随【页面主体】而滚动,但【页眉】恒定覆盖【页面主体】之首部。【页眉半透明】,【页面主体】之首部衬于【页眉】下方,若隐若现。

    诚然,如果此时【页面主体】之首部一片空白,则此设计之意义不大。因此,实践中,此种设计往往是在【页面主体】之首部设计了装饰性的图案。透过半透明的【页眉】,【页面主体】首部的该装饰依稀可见,微妙动人。

  • 即便【页脚】不沉淀于页面底部,本组件仍允许将【页脚】配置为“覆盖于【页面主体】之上层”。参见上一条目。

  • 为防止覆盖在【页面主体】上层的【页眉】遮挡【页面主体】之正文内容,本组件会自动根据【页眉】之高度,为必要的页面构成 DOM 元素设置准确的 padding-top。采用本组件之程序无需担心这些细节。

  • 为防止覆盖在【页面主体】上层的【页脚】遮挡【页面主体】之正文内容,本组件会自动根据【页脚】之高度,为必要的页面构成 DOM 元素设置准确的 padding-bottom。采用本组件之程序无需担心这些细节。

  • 当【页眉】须悬挂于页面顶部时,本组件故意采用 position: absolute 而非 position: fixed。因此,为确保【页眉】不随【页面主体】滚动,本组件故意要求【页面主体】之外层容器之高度恰好充满整个浏览器窗口,并令【页面主体】按需自动配备滚动条。【页面主体】位于 HTML 层级之较内层,其内容之滚动不至于带动【页眉】,故而达到目的。

  • 当【页脚】须沉淀于页面底部是,本组件故意要求【页面主体】之高度恰好充满整个浏览器窗口。此举与上一条目类同。

用法

基本使用思路说明

鄙人自以为本组件之用法符合直觉。而略显讽刺之处在于,鄙人觉得有必要多费口舌,讲解本品之使用思路。此举看似自相矛盾,实则非也。符合直觉并非同义于简单。 欲应对灵活多变之设计要求,本组件之用法亦不得不有繁复、琐碎之处须理解、注意。

目前,本组件之用法思路由 3 部分内容组成:

  1. 核心组件之引用。

    此部分既包含模板(<template>)和逻辑(<script>),也包含用于布局的、故而不可或缺的最基础之样式(<style>)。应用好该部分功能,则【页面】大体成形。但某些尺寸尚且未定。

  2. 所谓“关键但不确定”之样式的运用。

    这是指一些虽然必不可少,但却因项目而异的样式配置。典型的例子是【页眉】之高度、【页脚】之高度。于此两数值,本组件不便冒然给出所谓默认值,故而有意空缺,留待本组件之外的代码补充给出。 换而言之,诸君采用本组件时,莫要忘记自行定义这些样式,以获得正确的效果。

    还需指出,于这部分样式之定义、配置,本组件有推荐做法。参阅下文《所谓“关键但不确定”的样式》一节。

  3. 非必须的所谓“额外的样式预设”之运用。

    这些额外样式视同所谓“预设”,系为达成某些习惯提供方便而已。这些样式亦由本组件附带,可供选用。它们存在于 .vue 文件中,而是存在于一些独立的 .css 中。这些独立的 .css 文件源于一些由本组件提供的 .styl 源文件,而这些 .styl 文件显然也是独立于 .vue 文件的,否则不成其为 .styl 文件。

开发任务拆解

于开发者而言,使用思路明确之后,还需明白开发的具体任务。众所周知,在基于 Vue 框架的开发过程中,不论你采用 Vue 的【单文件组件】(所谓 Single-filed Component,SFC)与否,开发任务总可以为三个部分:模板部分、脚本逻辑部分和样式部分。下文将分此三个部分,并结合前述使用思路和示例,介绍本组件之用法。

模板(<template>)部分

撰写模板的任务,又可进一步细分为三个小任务:

  1. 在模板上按需给出 Props。例如 :has-header="true"
  2. 将内容插入本组件模板预先设计好的插槽(slot)中。
  3. 为了令【页眉】悬挂等特定风格的布局正常呈现,须小心配置本组件(即整个视觉页)的【根 div的祖先元素,即包裹着本组件的、不属于本组件的那些外层 DOM。
模板的 Props

暂略。见下文中的示例。另见下文《应用编程接口(所谓 API)》一节。

模板插槽
  1. 模板包含以下插槽(slot):

    • 默认插槽,被本组件内建的 div.page-body-content 所包裹,用于接纳【页面主体】之内容。
    • 名为 page-header-content 的插槽,被本组件内建的 div.page-header 所包裹。功能不言自明。
    • 名为 page-footer-content 的插槽,被本组件内建的 div.page-footer 所包裹。功能不言自明。
    • 名为 before-page-body-content 的插槽,被本组件内建的 div.before-page-body-content 所包裹。功能不言自明。
    • 名为 after-page-body-content 的插槽,被本组件内建的 div.after-page-body-content 所包裹。功能不言自明。
  2. 模板根标签所接纳的属性(attributes)。熟悉 Vue 开发的朋友都知道,它们实际上对应于本 Vue 组件的输入项(Props)。此处暂略,见下文《应用编程接口(所谓 API)》一节。

  3. 一段较完整的示例代码如下:

    <template>
        <page
            class="my-first-page"
            :has-header="true"
            :has-footer="true"
            :header-is-fixed-above="true"
            :footer-is-fixed-below="true"
            :header-should-cover-body="true"
            :footer-should-cover-body="true"
        >
            <template #page-header-content>
                <div class="my-first-page-header-content">
                    <h2>此乃页眉之内容</h2>
                </div>
            </template>
    
            <template #page-footer-content>
                <div class="my-first-page-footer-content">
                    <p>此乃页脚之内容</p>
                </div>
            </template>
    
            <div class="my-first-page-body-content">
                <p>为便于充分展示本组件之多个布局特性,本示例程序<em>有意枉顾美观</em>。见谅。</p>
                <hr>
                <dl>
                    <dt>复旦大学中国研究院院长张维为教授在其所著
                        《中国超越:一个文明型国家的光荣与梦想》
                        中写道:</dt>
                    <dd>
                        <p>中国人的爱国主义
                            是长江、黄河、珠穆朗玛峰;
                            是《诗经》、《楚辞》、先秦散文;
                            是唐诗、宋词、元曲、明清小说;
                            是屈原、岳飞、文天祥、毛泽东;
                            是普通话、四川话、广东粤语、上海方言;
                            是万里长城、北京故宫、桂林山水、陕西兵马俑;
                            是川菜、粤菜、鲁菜、淮扬菜;
                            是西湖龙井、黄山毛峰、武夷岩茶、洞庭碧螺春;
                            是《梅花三弄》、《高山流水》、《二泉映月》、《春江花月夜》;
                            是四合院、广东骑楼、徽派大院、江南民居;
                            是昆剧、京剧、粤剧、黄梅戏;
                            是南昌起义、平型关大捷、台儿庄血战、抗美援朝;
                            是两弹一星、北斗导航、神舟号飞船、高铁八纵八横;
                            是己所不欲,勿施于人;
                            是四海之内皆兄弟;
                            是胸怀祖国,放眼世界等等等等。</p>
                    </dd>
                </dl>
            </div>
        </page>
    </template>
正确配置本组件根元素的祖先元素

为实现诸如【页眉】悬挂于页面顶端的布局设计,本组件的【根 div 】须得知 100% 之高度具体几何。这显然要求 HTML 层级中自 <html> 以来的所有祖先元素均提供准确的 heightmin-height。而本组件有意尝试自动配置这类属性,于是,本组件明确要求,其【根 div】的直接父元素须配备 CSS 类名 .page-container

众所周知,常见的或者说典型的应用情景是:作为“视觉页面”的本组件,其【根 div 】的直接父元素应为 #app。而 #app 的直接父元素为 <body>。针对这种典型配置,本组件确有少量代码直接应对。故诸君唯一要做的是,不厌其烦的为诸君每个项目的 #app 添加一个 CSS 类名——.page-container。即有:

<div id="app" class="page-container">
    <!-- 此处省去实际的代码 -->
</div>

这样的设计看似繁琐,但私以为是必须的。

当然,类似 Nuxtjs 这样的应用平台,会在 #app 之上层额外插入更多的 div。此时,这些额外的祖先 div 显然超出本组件的掌控范围。换句话说,本组件的关于自动确定祖先元素 min-height 之尝试可能恰巧(恰不巧?)失败,无如期效果。于是失败之情形,本组件无可奈何。 但本组件会在浏览器控制台(console)中打印警告信息,以提示诸君须手工做额外的配置。而诸君须做的亦非难事,无非是确保 HTML 层级中自 <html> 以来的所有祖先元素均提供准确的 heightmin-height 仅此而已。


脚本语言(<script>)部分

针对 TypeScript 编程环境的用法示例

注意!采用本 Vue 组件之 TypeScript 版本时,import 语句的 from 须指向 ./source/index.vue

import Vue from 'vue'
import { Component, Prop } from 'vue-property-decorator'
import UIPage from '@wulechuan/vue2-ui--page/source/index.vue'

@Component({
    components: {
        'page': UIPage,
    },
})
export default class MyFirstPage extends Vue {}
针对 JavaScript 编程环境的用法示例

注意!采用本 Vue 组件之 JavaScript 版本时,import 语句的 from 须指向 ./dist/index.vue

import Vue from 'vue'
import UIPage from '@wulechuan/vue2-ui--page/dist/index.vue'

export default {
    components: {
        'page': UIPage,
    },
}

样式(<style>)部分

所谓“关键但不确定”的样式

所谓“关键但不确定”的样式,是指那些运用本组件时必不可少,但本组件却不直接提供,而是要求用户自行确定的样式。 目前,实则仅涉及 3 个数据:

  1. 【页眉】之高度,
  2. 【页脚】之高度,
  3. 【页面主体】之背景色。

以上数据往往因设计稿而异,故本组件不便冒然给出所谓默认值,而是交由采纳本组件之项目自行明确之。

【页眉】与【页脚】之高度

需要格外注意的是,【页眉】之高度和【页脚】之高度是“牵一发而动全身”的。因为本组件在幕后负责自动协调、匹配好多个 HTML 元素的多处尺度。例如,倘若【页眉】覆盖【页面主体】,那么页面主体之最外层元素的 padding-top 应与【页眉】之高度吻合。要确保本组件的这些内部设计运转如期,在项目中对【页眉】、【页脚】之高度配置的 CSS 写法也必须遵照如下规则:

  • 避免直接为 .page-header 设置 height
  • 避免为直接 .page-footer 设置 height
  • 应该.page 上,设置由本组件设计好两个的 CSS 变量:--page-header-height--page-footer-height

简而言之,要用 CSS 变量,而不要直接用 height

以下是错误的做法:

// 直接设置 height 是错误的做法!
.page-header {
    height: 123px;
}

// 直接设置 height 是错误的做法!
.page-footer {
    height: 45px;
}

以下是正确的做法:

// 这是正确的做法。
.my-first-page {
    --page-header-height: 123px;
    --page-footer-height: 45px;
}

诸君如果好奇幕后起作用之代码,可参考下方的源代码片段。完整源代码在 ./source/index.vue样式代码块中,尊请阅读。

展开可查阅源代码片段
.page-header {
    height var(--page-header-height)
}

.page-footer {
    height var(--page-footer-height)
}

.page {
    // 此处省略了很多代码

    &.page-footer-should-cover-page-body {

        &,
        &.page-footer-is-fixed-below {
            padding-bottom 0
        }

        .page-footer {
            margin-top calc(-1 * var(--page-footer-decided-height))
        }

        .page-body {
            padding-bottom       var(--page-footer-decided-height)
        }
    }

    // 此处省略了很多代码
}
【页面主体】之背景色

页面背景色的配置是自由的,诸君直接给出即可。例如:

.my-first-page {
    background-color: white;
}
综合示例

综上,一个较完整的正确示例如下:

.my-first-page {
    // 以正确之方式配置【页眉】、【页脚】之高度。
    --page-header-height: 123px;
    --page-footer-height: 45px;

    // 自由配置页面之背景色。
    background-color: white;
}
锦上添花之样式 1:自由编写

本组件默认不迎合特定的视觉风格或要求,故而默认不包含任何与视觉风格有关的样式规则。诸君可以自由书写锦上添花的样式。

下面是所谓额外样式之示例代码。

.my-first-page {

    .page-header {
        color black
    }

    .page-footer {
        color white
        background-color rgba(black, 0.75)
    }

    .my-first-page-header-content,
    .my-first-page-footer-content {
        height 100%
        display flex
        justify-content center
        align-items center
    }
}
锦上添花之样式 2:本组件自带的额外样式

为多个项目甚至为同一项目的多个页面反复书写额外样式,未免乏味,甚至令人厌烦。于是,本组件亦附带了两种预设好的 .css 文件(共三个),以方便地实现符合本人喜好的样式。诸君若有兴趣,欢迎尝试。

我先逐一介绍这三个 CSS 文件之功用或目的。至于在代码层面如何将这三个 .css 文件用于诸君的 Vue 项目中,令其发挥效用,稍后再于《上述三个额外 CSS 文件的使用方法》一节统一介绍。

第 1 个自带的额外 CSS 文件之功用

第 1 个自带的额外 CSS 文件是./dist/default-shadings.css

该 CSS 达到的目的(或者说提供的效果)其实很简单,归纳起来有两点:

  1. 如果【页眉】被配置为“覆盖于【页面主体】之上层”,则给【页眉】添加流行的“毛玻璃”(一说“亚克力”)视觉效果,如此或许更加美观。【页脚】亦作类同处理。

    幕后的具体做法也很简单:如果浏览器支持 backdrop-filter: blur(1rem),则采用之;否则,不采用。

  2. 给【页眉】和【页脚】设置背景色。

    同样是令【页眉】或【页脚】具有半透明效果,取决于“毛玻璃”视觉效果应用与否,【页眉】或【页脚】的背景色之透明度也大相径庭。具体而言,一旦应用了“毛玻璃”效果,则背景透明度应较高,背景色的 alpha 通道取值不应高于 0.6,往往越低越好,即越透明越好;反之。遵循此原则,可令【页眉】、【页脚】之半透明效果悦目、舒适。

正因为上述琐碎细节,看似简单的“给【页眉】、【页脚】设置半透明背景色和特效”的任务,本组件用到了多达 6 个 css 变量。它们的定义如下:

.page {
    --page-header-backdrop-filter blur(1rem)
    --page-footer-backdrop-filter blur(1rem)

    --page-header-bg-color-with-backdrop-filter    rgba(black, 0)
    --page-header-bg-color-without-backdrop-filter rgba(black, 0)

    --page-footer-bg-color-with-backdrop-filter    rgba(white, 0.79)
    --page-footer-bg-color-without-backdrop-filter rgba(white, 0.79)
}

诚然,你可以故意避免采用这 6 个 CSS 变量,改为直接为 .page-header.page-footer 设置 background-colorbackdrop-filter。而采用上述 6 个 CSS 变量之微弱的好处是,幕后所涉及的 CSS 代码会确保毛玻璃效果仅在有必要时才得到采用,且总是自动据此选用对应的背景色方案。仅此而已。

顺便指出,如果你不喜欢“毛玻璃”效果,或设计师坚持避免采用该类视觉效果,仅需如下代码即可禁用之:

.page {
    --page-header-backdrop-filter: none;
    --page-footer-backdrop-filter: none;
}
第 2 个自带的额外 CSS 文件之功用

第 2 个自带的额外 CSS 文件是./dist/default-extra-measurements.css

该 CSS 文件达到的目的也很简单:

  1. .page-header.page-footer 分别定高。
  2. 为所谓“页面内容块”在水平方向配置统一的 padding,即 padding-leftpadding-right
  3. .page-body-content 配置较美观、实用的垂直方向的 padding,即其 padding-toppadding-bottom

其源代码很短,遂全文抄录于此:

展开可查阅源代码全文
.page {
    --page-header-height 5rem // 默认值而已
    --page-footer-height 3rem // 默认值而已

    // 所谓 page-contents 包括
    //     .page-header、
    //     .page-footer 和
    //     .page-body-content
    // 三者。
    --page-contents-horizontal-padding 2rem
}



.page-header,
.page-footer,
.page-body-content {
    padding-left  var(--page-contents-horizontal-padding, 1rem)
    padding-right var(--page-contents-horizontal-padding, 1rem)
}



.page-body-content {
    padding-top    1rem
    padding-bottom 5rem // 本人在此鼓吹,饱满的 padding-bottom 可改善阅读体验。
}
第 3 个自带的额外 CSS 文件之功用

第 3 个自带的额外 CSS 文件是./dist/default-extra-measurements-for-small-screen.css

该 CSS 文件借助 @import 语句导入了前述 ./dist/default-extra-measurements.css 之内容。换言之,该 CSS 系在前者基础上做额外增补。故而,如果采用该 CSS,则不必采用 ./dist/default-extra-measurements.css简而言之,二者取其一即可。

该 CSS 增补之功能有些古怪,或许仅适合鄙人。具体如下:

  1. 令页面的父容器,即 .page-container 拥有 padding
  2. 同时,令页面本身,即 .page,拥有最大宽度(max-width)。如此,当页面的布局面向较小的浏览器窗口做优化时,一旦令页面在较宽阔的浏览器(例如微软 Windows 平台的浏览器)上呈现时,布局不至于过分丑陋,或完全错乱,而是较好的保持其在窄小浏览器上的布局风貌。
  3. 在浏览器尺寸较大而 .page 本身较窄小时,给 .page 添加了一点微妙的 box-shadow,以期更加美观。
上述三个额外 CSS 文件的使用方法

在了解了上述三个 CSS 文件之目的或作用之后,我介绍一下它们在诸君 Vue 项目中的正确用法。

凡 Vue 项目,均配备 main.jsmain.ts。诸君之 Vue 项目也不例外。为方便指代,不妨暂称其为“main 文件”。欲采用前述三个 CSS 文件之任一,即应在“main 文件”的首部引入此 CSS 文件。见下例:

import '@wulechuan/vue2-ui--page/dist/default-shadings.css'
import '@wulechuan/vue2-ui--page/dist/default-extra-measurements-for-small-screen.css'

import Vue from 'vue'
import App from './app.vue'

Vue.config.productionTip = false

new Vue({
    render: h => h(App),
}).$mount('#app')

须注意,我推荐令导入 css 之 import 语句位于“main 文件”之最顶端。此举可确保这些 CSS 之代码早于.vue 文件中书写的样式被纳入诸君项目最终编译得到的汇总 .css 文件中。


可运转的完整示例项目

本代码库自带的可运转的示例项目:


应用编程接口(所谓 API)

输入项(即 Vue 组件的 Props)

本人自信以下各 Props 之功用均不言自明。另,目前所有输入项之值类型均恰巧为布尔(boolean),且所有输入项之默认值均为 false

小驼峰(camelCase)记法

@Prop() hasHeader?:                                 boolean;
@Prop() hasFooter?:                                 boolean;
@Prop() headerIsFixedAbove?:                        boolean;
@Prop() footerIsFixedBelow?:                        boolean;
@Prop() headerShouldCoverBody?:                     boolean;
@Prop() footerShouldCoverBody?:                     boolean;
@Prop() pageMinHeightShouldFitScreen?:              boolean;
@Prop() shouldDisablePageContainerAncestorWarning?: boolean;

葫芦串(所谓 kebab)记法

<page
    :has-header="true"
    :has-footer="true"
    :header-is-fixed-above="true"
    :footer-is-fixed-below="false"
    :header-should-cover-body="true"
    :footer-should-cover-body="true"
    :page-min-height-should-fit-screen="false"
    :should-disable-page-container-ancestor-warning="false"
></page>

Vue 组件插槽(即 slot

  • 默认插槽,被本组件内建的 div.page-body-content 所包裹,用于接纳页面主体内容。
  • 名为 page-header-content 的插槽,被本组件内建的 div.page-header 所包裹。功能不言自明。
  • 名为 page-footer-content 的插槽,被本组件内建的 div.page-footer 所包裹。功能不言自明。
  • 名为 before-page-body-content 的插槽,被本组件内建的 div.before-page-body-content 所包裹。功能不言自明。
  • 名为 after-page-body-content 的插槽,被本组件内建的 div.after-page-body-content 所包裹。功能不言自明。

必须正确配置的 CSS 类名

注意!不论本组件【根 div 】的直接父元素是否为 #app,诸君总是必须为该直接父元素添加一个 CSS 类名——.page-container 即有:

<div class="page-container">
    <page ...></page>
</div>

层叠样式表自定义变量

必须正确配置的变量

以下 CSS 变量在 HTML 层级中全部源于 .page

  • --page-header-height
  • --page-footer-height

于额外 CSS 文件中设计的 CSS 变量

以下 CSS 变量在 HTML 层级中全部源于 .page

  • --page-header-backdrop-filter
  • --page-footer-backdrop-filter
  • --page-header-bg-color-with-backdrop-filter
  • --page-header-bg-color-without-backdrop-filter
  • --page-footer-bg-color-with-backdrop-filter
  • --page-footer-bg-color-without-backdrop-filter
  • --page-contents-horizontal-padding

未来计划

暂无。


许可证类型

WTFPL

注意:

我未研究过许可证的约束。因此姑且声明为 WTFPL 类型。但实际上该许可证类型可能与我采用的开源模块有冲突。

Versions

Current Tags

  • Version
    Downloads (Last 7 Days)
    • Tag
  • 0.1.9
    0
    • latest

Version History

Package Sidebar

Install

npm i @wulechuan/vue2-ui--page

Weekly Downloads

0

Version

0.1.9

License

WTFPL

Unpacked Size

58 kB

Total Files

11

Last publish

Collaborators

  • wulechuan