前言

因为之前只是用Vue来开发IaaS平台项目,其使用场景通常都是PC,所以没怎么涉及到Web响应式设计开发相关知识。最近项目刚好是应用在PC和平板,甚至是手机上的,所以就需要考虑响应式的问题。

1. 技术选型

Web响应式开发,主要是基于CSS里@media媒体查询实现,允许不同视图尺寸使用不同的布局。

1.1 方案一:Vue 3 + Vite + Bootstrap5

1.1.1 优点

  1. 容易上手:只要对 HTML 和 CSS 有基本了解的人都可以很快速的使用 Bootstrap。

  2. 响应式设计:Bootstrap 可以根据不同平台(手机、平板电脑和台式机)进行调整。

  3. 移动优先:在 Bootstrap 中,自适应移动端是框架的核心部分。

  4. 浏览器兼容性:Bootstrap5 兼容所有主流浏览器(Chrome、Firefox、Edge、Safari 和 Opera)。 如果您需要支持 IE11 及以下版本,请使用 Bootstrap4 或 Bootstrap3。

  5. github 星数 164k

  6. Bootstrap5 效果演示:https://www.bootstrap-admin.top/

1.1.2 缺点

  1. 没办法使用我们积累的vue next组件库

  2. Bootstrap5 需要学习成本,但不会很难

  3. 相对紧急的项目,通常不应该引入新技术去开发实现,而是以快速交付为主。

1.2 方案二:Vue 3 + Vite + Element Plus + @media

1.2.1 优点

  1. 基于前端现有技术积累,可快速开发项目功能

1.2.2 缺点

  1. 前端封装组件库,并没考虑过响应式设计,潜在未知样式问题,如开发此项目,各组件需考虑各平台(电脑、平板、手机)屏幕适配的同时,还要考虑已引用该库开发的项目兼容性问题。

  2. @media媒体查询需每个页面、每个平台去适配(Bootstrap5做的事情),工作量相对较大,因无更多平台设备去验证,潜在未知的样式问题。

1.3 结论

简单来说,可以理解为方案一Bootstrap5帮我们处理好了屏幕适配的问题,我们只需要关注功能开发即可,方案二则是功能也要开发,样式也要关注。长远来看,倾向于方案一开发。

2. 快速搭建

Bootstrap5官方文档有提供Vite基于开发的指南《Bootstrap & Vite》,我们虽然是基于Vue3开发的,但是相关配置基本都一样的,只是有一点点差异而已。

2.1 下载

通过Vite直接生成基于Typescript开发的Vue3模版。

npm create vite@latest my-vue-app -- --template vue-ts

2.2 安装


npm install
// bootstrap5、popperjs
npm install --save bootstrap @popperjs/core
// sass
npm install --save-dev sass
// bootstrap-icons 官方图标库
npm install bootstrap-icons

2.3 配置

2.3.1 开发热重载

vite.config.ts中配置,我们需要让Vite知道在哪里可以找到我们项目的TypeScript,以及开发服务器应该如何表现(热重载从src文件夹中拉取)。

import path from 'path'

export default defineConfig({
  resolve: {
    alias: {
      root: path.resolve(__dirname, 'src'),
    }
  },
  server: {
    port: 8080,
    host: true
  }
})

2.3.2 Bootstrap5

  1. vite.config.ts中配置,让导入变得尽可能简单:

     resolve: {
        alias: {
          '~bootstrap': path.resolve(__dirname, 'node_modules/bootstrap'),
        }
      }
    
  2. src/assets/style文件夹新建一个文件bootstrap5.scss ,引入bootstrap5样式:

    @import "~bootstrap/scss/bootstrap";
    
  3. main.ts文件修改为main.js,然后在index.html中同步修改:

    <script type="module" src="/src/main.js"></script>
    
  4. main.js中引入bootstrap5

    // 自定义样式
    import '../src/assets/style/bootstrap5.scss'
    
    // Bootstrap5 JS
    import * as bootstrap from 'bootstrap'
    

2.3 验证

  <div class="container mt-3">
    <h2 class="my-3 text-start">按钮样式</h2>
    <button type="button" class="btn">基本按钮</button>
    <button type="button" class="btn btn-primary ms-2">主要按钮</button>
    <button type="button" class="btn btn-secondary ms-2">次要按钮</button>
    <button type="button" class="btn btn-success ms-2">成功</button>
    <button type="button" class="btn btn-info ms-2">信息</button>
    <button type="button" class="btn btn-warning ms-2">警告</button>
    <button type="button" class="btn btn-danger ms-2">危险</button>
    <button type="button" class="btn btn-dark ms-2">黑色</button>
    <button type="button" class="btn btn-light ms-2">浅色</button>
    <button type="button" class="btn btn-link ms-2">链接</button>
  </div>

2.4 效果

3. 组件

3.1 日期选择器

一开始打算使用<input type="datetime-local">实现的,但是效果不是很理想,一些样式调整起来很复杂,加上本着不重复造轮子的思想,就打算引用相关日期插件实现。

  1. 安装
npm install @popperjs/core @eonasdan/tempus-dominus
  1. main.js中引入样式
// 日期组件样式
import '@eonasdan/tempus-dominus/dist/css/tempus-dominus.css'
  1. 在.vue中使用时,需引入
import { TempusDominus} from '@eonasdan/tempus-dominus';
import {  name } from "@eonasdan/tempus-dominus/dist/locales/ru";
  1. 实现代码
<div class="input-group mb-3" id="datetimepicker1">
    <input type="text" class="form-control" readonly aria-label="">
    <span class="input-group-text"><i class="bi bi-calendar"></i></span>
</div>
    
onMounted(() => {
    const element: any = document.getElementById('datetimepicker1')
    var datetimepicker1 = new TempusDominus(element, {
        localization: {
            format: 'yyyy-MM-dd HH:mm:ss',
            hourCycle: 'h24'
        },
        display: {
            viewMode: 'calendar',
            components: {
                year: true,
                month: true,
                date: true,
                clock: true,
                hours: true,
                minutes: true,
                seconds: true
            },
            icons: {
                time: 'bi bi-clock',
                date: 'bi bi-calendar',
                up: 'bi bi-arrow-up',
                down: 'bi bi-arrow-down',
                previous: 'bi bi-chevron-left',
                next: 'bi bi-chevron-right',
                today: 'bi bi-calendar-check',
                clear: 'bi bi-trash',
                close: 'bi bi-x',
            },
            buttons: {
                today: true,
                clear: true,
                close: true,
            },
        }
    });
    datetimepicker1.locale(name);
})
  1. 效果如下

3.2 自定义主题色

因为Bootstrap5主题默认是蓝色,和项目设计的主题色不一致,所以就需要修改定制主题色,这一块在官网Sass模块也有详细说明。特别注意的是,同一Sass文件中的变量覆盖可以在默认变量之前或之后。但是,当跨Sass文件覆盖时,必须在导入Bootstrap的Sass文件之前进行覆盖。比如要将主题色修改为紫色(#6610f2),就需要在我们自定义的样式文件bootstrap5.scss中设置,如下所示:

$primary: #6610f2;
$danger: #ff4136;

$theme-colors: (
  "primary": $primary,
  "danger": $danger
);

// scss
@import "~bootstrap/scss/bootstrap";
// Required
@import "../node_modules/bootstrap/scss/functions";
@import "../node_modules/bootstrap/scss/variables";
@import "../node_modules/bootstrap/scss/mixins";

效果如下图所示,可以看到按钮设置为primary时,颜色已经变成紫色(#6610f2)了,但是出现了一个问题,那就是次要按钮、成功、信息等按钮样式异常。

为了解决这个问题,就去查看下_variables.scss 文件,其中有一段样式代码:

// scss-docs-start theme-color-variables
$primary:       $blue !default;
$secondary:     $gray-600 !default;
$success:       $green !default;
$info:          $cyan !default;
$warning:       $yellow !default;
$danger:        $red !default;
$light:         $gray-100 !default;
$dark:          $gray-900 !default;

也就是我们在自定义样式中覆盖了,导致相关字段颜色值丢失,所以就在自定义样式文件中,补上,如下所示:


// 修改主题色
$primary: #6610f2;
$secondary: #6c757d;
$success: #198754;
$info: #0dcaf0;
$warning: #ffc107;
$danger: #dc3545;
$light: #f8f9fa;
$dark: #212529;

$theme-colors: (
    "primary": $primary,
    "secondary": $secondary,
    "success": $success,
    "info": $info,
    "warning": $warning,
    "danger": $danger,
    "light": $light,
    "dark": $dark,
);

效果如下所示,虽然解决了按钮的样式问题,但是其他组件其实可能有潜在的问题,这一块后续再补充。

3.3 Echarts

3.3.1 安装

npm install echarts --save

3.3.2 引入 ECharts

因为Echarts官网上面其实都有Demo的,但是结合我们实际项目,还是可以把相对贴切的模版给出来的,如下所示:


<template>
  <div class="card">
    <div id="echarts" style="width: 500px; height: 300px;"></div>
  </div>
</template>
<script setup lang="ts">

import * as echarts from 'echarts';
import { onMounted } from 'vue';

onMounted(() => {
  // 基于准备好的dom,初始化echarts实例
  const element: any = document.getElementById('echarts')
  const myChart: any = echarts.init(element);
  const colors = ['#5470C6', '#91CC75', '#EE6666'];
  const options = {
    color: colors,
    tooltip: {
      trigger: 'axis',
      axisPointer: {
        type: 'cross'
      }
    },
    grid: {
      left: '30%'
    },
    legend: {
      data: ['电压', '电流', '功率']
    },
    xAxis: [
      {
        type: 'category',
        axisTick: {
          alignWithLabel: true
        },
        // prettier-ignore
        data: ['06:00:00', '07:00:00', '08:00:00', '09:00:00', '10:00:00', '12:00:00', '14:10:00', '15:30:00', '16:00:00', '17:00:00', '18:00:00', '19:00:00']
      }
    ],
    yAxis: [
      {
        type: 'value',
        name: '电压',
        position: 'left',
        alignTicks: true,
        offset: 0,
        axisLine: {
          show: true,
          lineStyle: {
            color: colors[0]
          }
        },
        axisLabel: {
          formatter: '{value} V'
        }
      },
      {
        type: 'value',
        name: '电流',
        position: 'left',
        alignTicks: true,
        offset: 50,
        axisLine: {
          show: true,
          lineStyle: {
            color: colors[1]
          }
        },
        axisLabel: {
          formatter: '{value} A'
        }
      },
      {
        type: 'value',
        name: '功率',
        position: 'left',
        alignTicks: true,
        offset: 100,
        axisLine: {
          show: true,
          lineStyle: {
            color: colors[2]
          }
        },
        axisLabel: {
          formatter: '{value} KW'
        }
      }
    ],
    series: [
      {
        name: '电压',
        type: 'line',
        data: [
          2.0, 4.9, 7.0, 23.2, 25.6, 76.7, 135.6, 162.2, 32.6, 20.0, 6.4, 3.3
        ]
      },
      {
        name: '电流',
        type: 'line',
        yAxisIndex: 1,
        data: [
          2.6, 5.9, 9.0, 26.4, 28.7, 70.7, 175.6, 182.2, 48.7, 18.8, 6.0, 2.3
        ]
      },
      {
        name: '功率',
        type: 'line',
        yAxisIndex: 2,
        data: [2.0, 2.2, 3.3, 4.5, 6.3, 10.2, 20.3, 23.4, 23.0, 16.5, 12.0, 6.2]
      }
    ]
  };
  // 绘制图表
  myChart.setOption(options);
})


</script>

<style scoped>
</style>

3.3.2 屏幕自适应

因为使用Bootstrap5开发本身就是为了Web响应式功能,所以Echarts也需要处理屏幕自适应的问题,这个ECharts官方文档已经有相关说明了《响应容器大小的变化》,如下所示:

<style>
  #main,
  html,
  body {
    width: 100%;
  }
  #main {
    height: 400px;
  }
</style>
<div id="main"></div>
<script type="text/javascript">
  var myChart = echarts.init(document.getElementById('main'));
  window.addEventListener('resize', function() {
    myChart.resize();
  });
</script>

3.3.3 热更新

待解决。

4. 打包

4.1 本地打开

因为项目应用场景不太一样,所以有些时候是想要直接打开dist/index.html就可以看到网页内容的,实现这个有现成的插件可以使用vite-plugin-singlefile,其原理就是将jscss文件内容填充到index.html里,这样就可以通过打开index.html查看打包好的项目了。如下所示:

  1. 安装
npm install vite-plugin-singlefile --save-dev
  1. 在vite.config.ts中配置
import { defineConfig } from "vite"
import vue from "@vitejs/plugin-vue"
import { viteSingleFile } from "vite-plugin-singlefile"

export default defineConfig({
	plugins: [vue(), viteSingleFile()],
})