前言

无。


环境搭建

首先安装Vue3.0

找到官方文档下面的安装方法,使用create命令创建一个新的Vue项目:

npm create vue@latest

安装了3.11.1版本的vue,先安装vite构建工具:

npm install vite

代码完成后再通过build命令可以将vue项目构建为发布的前端代码:

npm run build

然后就会在项目根目录下生成一个新的dist目录,里面包含了js、css、html和其他静态文件等前端文件。

ElementUI

ElementUI是一个好用的组件库,方便我这种前端菜鸟抄来构建一个看得过去的前端界面,找到官方文档下面的安装方法:

npm install element-plus

通过ElementUI标签,就可以方便地在前端页面引入各种组件,比如按钮,修改一下App.vue:

<script setup>

</script>

<template>
  <div id="app">
    <el-button>{{ message }}</el-button>
  </div>
</template>

<script>
  export default {
    data() {
      return {
        message: "Hello ElementUI!"
      }
    }
  }
</script>

然后再修改一下main.js:

import './assets/main.css'

import { createApp } from 'vue'
import 'element-plus/dist/index.css'
import ElementPlus from 'element-plus'
import App from './App.vue'

const app = createApp(App);
app.use(ElementPlus);
app.mount("#app");

就能看到按钮了。

其他依赖

npm install axios moment qs

Vue基础语法

模板语法

两个大括号可以将数据当作纯文本填入模板:

<el-button>{{ message }}</el-button>

需要将数据当作HTML,即动态渲染页面时可以使用v-html:

<span v-html="rawHtml"></span>

不怎么用得到,动态渲染还是比较危险的行为,什么表达式注入、模板注入、XSS都是动态渲染导致的。

双大括号填充数据不能用在HTML标签的属性里面,需要使用v-bind,或者缩写:

<div v-bind:id="dynamicId"></div>
<div :id="dynamicId"></div>

也可以通过对象一次性绑定多个属性:

const objectOfAttrs = {
  id: 'container',
  class: 'wrapper',
  style: 'background-color:green'
}

除了单个变量,还可以填充一个表达式,包括字符串拼接和函数调用:

{{ number + 1 }}

{{ ok ? 'YES' : 'NO' }}

{{ message.split('').reverse().join('') }}

<div :id="`list-${id}`"></div>

<time :title="toTitleDate(date)" :datetime="date">
  {{ formatDate(date) }}
</time>

除此之外,其他v-语法也各有用处,比如v-if控制标签存在与否,v-for遍历数组并生成多个HTML代码块,具体可以看官方指令集文档

计算属性

一种特殊的属性,在每次访问它时会先经过代码块运算得到结果,比如:

<el-progress type="dashboard" :percentage="gotScorePercentage" :format="gotScore" :color="colors"></el-progress>

gotScorePercentage就是一个计算属性,在export default的computed中定义,访问时会先进行计算:

computed: {
    gotScorePercentage: function() {
        return 100 * (this.myScore / this.fullScore);
    }
},

条件渲染

先设置一个数据:

data() {
  return {
    ok: 1
  }
},

条件渲染即v-if指令:

<div v-if="ok === 3">
  A
</div>
<div v-else-if="ok === 2">
  B
</div>
<div v-else-if="ok === 1">
  C
</div>
<div v-else>
  Not A/B/C
</div>

和v-show指令:

<h1 v-show="ok === 1">Hello!</h1>

区别在于v-show的实现方式是切换标签的display属性,所以无论条件如何,标签都会被渲染。

而v-if是惰性的,只有条件符合时才会被渲染。

列表渲染

先设置一串数据:

data() {
  return {
    items: [
      {msg: "Twings"},
      {msg: "Aluvion"}
    ]
  }
}

v-for指令可以遍历数组,分割成index和数据项并生成多个HTML块:

<li v-for="(item, index) in items">
  {{ index }}: {{ item.msg }}
</li>

根据文档,使用v-for时最好给它提供一个唯一的key便于渲染。

事件处理

JavaScript常用的on事件,在Vue里可以用v-on指令或者@click的简写方式监听事件,先定义一个method:

methods: {
  clickButton(event) {
    console.log(event)
  }
},

然后再将它配置到click事件里:

<el-button @click="clickButton">{{ message }}</el-button>

这样点击按钮就能触发事件了。

表单输入绑定

Web页面中常用的表单传值,为了JavaScript异步传值需要将表单输入绑定到某个变量上面:

<input v-model="message" placeholder="edit me" />
<el-button @click="clickButton">{{ message }}</el-button>

将输入绑定到了message变量上面,这样按钮中的消息也会跟着输入框改变。

此外,textarea、checkbox、radio和select等输入类型也是类似的绑定方式,使用multiple属性可以将多个值绑定到同一个select变量上。

生命周期钩子

如created等方法,方便初始化环境。

ElementUI Plus

发现一个非常奇怪的问题,我最上层的app div只能显示非常小的一块地区,标签栏都被压缩了。

后面发现问题出在默认引入的main.css里面,删掉body和app定义的display样式就恢复正常了:

@media (min-width: 1024px) {
  body {
    /* display: flex; */
    place-items: center;
  }

  #app {
    /* display: grid; */
    grid-template-columns: 1fr 1fr;
    padding: 0 2rem;
  }
}

抽屉式导航栏

<el-switch v-model="isCollapse" size="large" style="margin-left: 24px; margin-bottom: 20px;"/>
<el-menu defaultActive="Reading" class="el-menu-vertical-demo" @select="handleTag" :collapse="!isCollapse">
    <el-menu-item index="Reading">
        <el-icon><Reading /></el-icon>
        <span style="width: 120px;">查看</span>
    </el-menu-item>
    <el-menu-item index="Plus">
        <el-icon><Plus /></el-icon>
        <span>新增</span>
    </el-menu-item>
    <el-menu-item index="Edit">
        <el-icon><Edit /></el-icon>
        <span>修改</span>
    </el-menu-item>
    <el-menu-item index="Delete">
        <el-icon><Delete /></el-icon>
        <span>删除</span>
    </el-menu-item>
    <el-menu-item index="Search">
        <el-icon><Search /></el-icon>
        <el-input v-if="isCollapse" v-model="search" style="width: 120px;" placeholder="输入搜索" clearable/>
    </el-menu-item>
</el-menu>

环形进度条

<div style="text-align: center;">
  <el-progress type="dashboard" :percentage="lunchTime" :color="colors"></el-progress>
  <br/>
  <span>上午工作还有: {{ leftTimeLunch() }}</span>
</div>
<div style="text-align: center; margin-top: 100px;">
  <el-progress type="dashboard" :percentage="AfterNoonTime" :color="colors"></el-progress>
  <br/>
  <span>下午工作还有: {{ leftAfterNoon() }}</span>
</div>
<div style="text-align: center; margin-top: 100px;">
  <el-progress type="dashboard" :percentage="workFinished" :color="colors"></el-progress>
  <br/>
  <span>距离下班还有: {{ leftTime() }}</span>
</div>

JS代码:

data() {
  nowDate: dayjs(),
  restDate: dayjs().endOf("week").subtract(20, 'day').subtract(20, 'hour').subtract(20, 'minute'),
  morningDate: dayjs().startOf("day").add(20, 'hour').add(20, 'minute'),
  AfterNoonDate: dayjs().startOf("day").add(20, 'hour').add(20, 'minute'),
  finishDate: dayjs().startOf('day').add(20, 'hour').add(20, 'minute'),
  lunchDate: dayjs().startOf('day').add(20, 'hour').add(20, 'minute'),
},
methods: {
  leftTime() {
    if (this.nowDate > this.finishDate) {
      return 0;
    }
    return this.nowDate < this.morningDate ? this.morningDate.to(this.finishDate, true) : this.nowDate.to(this.finishDate, true)
  },
  leftTimeLunch() {
    if (this.nowDate > this.lunchDate) {
      return 0;
    }
    return this.nowDate < this.morningDate ? this.morningDate.to(this.lunchDate, true): this.nowDate.to(this.lunchDate, true)
  },
  leftAfterNoon() {
    if (this.nowDate > this.finishDate) {
      return 0;
    }
    return this.nowDate < this.AfterNoonDate ? this.AfterNoonDate.to(this.finishDate, true) : this.nowDate.to(this.finishDate, true)
  },
},
computed: {
  workFinished: function() {
    var diff = this.finishDate.diff(this.nowDate)
    if (diff.valueOf() < 0) {
      return 0;
    }
    var percentage = Math.ceil(100 * diff.valueOf() / (3600000 * 8)) > 100 ? 100 : Math.ceil(100 * diff.valueOf() / (3600000 * 8))
    return 100 - percentage;
  },
  lunchTime: function() {
    var diff = this.lunchDate.diff(this.nowDate)
    if (diff.valueOf() < 0) {
      return 0;
    }
    var percentage = Math.ceil(100 * diff.valueOf() / (3600000 * 3 + 60000 * 10)) > 100 ? 100 : Math.ceil(100 * diff.valueOf() / (3600000 * 3 + 60000 * 10))
    return 100 - percentage
  },
  AfterNoonTime: function() {
    var diff = this.finishDate.diff(this.nowDate)
    if (diff.valueOf() < 0) {
      return 0;
    }
    var percentage = Math.ceil(100 * diff.valueOf() / (3600000 * 2.5 + 60000 * 10)) > 100 ? 100 : Math.ceil(100 * diff.valueOf() / (3600000 * 2.5 + 60000 * 10))
    return 100 - percentage
  },
},

标签式导航

<el-menu style="display:flex;" :default-active="defaultactiveIndex" class="el-menu-demo" mode="horizontal" @select="handleSelect" textColor="#A0CFFF" activeTextColor="#409EFF">
<el-menu-item index="index" style="margin-left: 5%">主页</el-menu-item>
<el-menu-item index="note" v-bind:disabled="!isLogin" @click="note_info();">备忘录</el-menu-item>
<el-menu-item index="todo" v-bind:disabled="!isLogin" @click="todo_info();">待办</el-menu-item>
<el-menu-item index="file" v-bind:disabled="!isLogin" @click="file_info();">附件</el-menu-item>

<el-dropdown style="align-self:center; position:fixed; right: 22%">
  <span class="el-dropdown-link">
    <el-button type="success">{{ loginForm.name != '' ? loginForm.name : '未登录'}}</el-button>
  </span>
  <!--用户相关-->
  <template #dropdown>
    <el-dropdown-menu>
    <div v-if="isLogin">
      <el-dropdown-item @click="logout()">
        退出
      </el-dropdown-item>
    </div>
    <div v-if="!isLogin">
      <el-dropdown-item @click="dialogFormVisible = true">
        登录
      </el-dropdown-item>
    </div>
  </el-dropdown-menu>
  </template>
</el-dropdown>
</el-menu>

日历

<div v-if="activeIndex == 'index'" style="width: 95%; margin: 0 auto;">
  <el-calendar v-model="dateValue" />
</div>

倒计时

<el-countdown format="DD [天] HH:mm:ss" :value="restDate">
  <template #title>
    <div style="display: inline-flex; align-items: center">
      <el-icon style="margin-right: 4px" :size="12">
        <Calendar />
      </el-icon>
      距离周末还有
    </div>
  </template>
</el-countdown>
<div class="countdown-footer">{{ nowDate.format('YYYY-MM-DD') }}</div>

dialog隐藏表单

<el-dialog title="登录" v-model="dialogFormVisible">
  <el-form v-model="loginForm">
      <el-form-item label="Username:" :label-width="formLabelWidth" required>
          <el-input placeholder="请输入用户" v-model="loginForm.name" maxlength="20" clearable></el-input>
      </el-form-item>
      <el-form-item label="Password:" :label-width="formLabelWidth" required>
          <el-input placeholder="请输入密码" v-model="loginForm.pwd" maxlength="20" clearable show-password></el-input>
      </el-form-item>
  </el-form>
  <div slot="footer" class="dialog-footer">
      <el-button @click="logout()">取 消</el-button>
      <el-button type="primary" @click="login()" v-bind:disabled="loginForm.name == '' || loginForm.pwd == ''">登 录</el-button>
  </div>
</el-dialog>

Vue3.0官方文档

ElementUI Plus官方文档


Web 前端 Vue ElementUI

本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!

RustWeb开发入门