前言
无。
环境搭建
首先安装Vue3.0
找到官方文档下面的安装方法,使用create命令创建一个新的Vue项目:
安装了3.11.1版本的vue,先安装vite构建工具:
代码完成后再通过build命令可以将vue项目构建为发布的前端代码:
然后就会在项目根目录下生成一个新的dist目录,里面包含了js、css、html和其他静态文件等前端文件。
ElementUI
ElementUI是一个好用的组件库,方便我这种前端菜鸟抄来构建一个看得过去的前端界面,找到官方文档下面的安装方法:
1
| npm install element-plus
|
通过ElementUI标签,就可以方便地在前端页面引入各种组件,比如按钮,修改一下App.vue:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <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:
1 2 3 4 5 6 7 8 9 10
| 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");
|
就能看到按钮了。
其他依赖
1
| npm install axios moment qs
|
Vue基础语法
模板语法
两个大括号可以将数据当作纯文本填入模板:
1
| <el-button>{{ message }}</el-button>
|
需要将数据当作HTML,即动态渲染页面时可以使用v-html:
1
| <span v-html="rawHtml"></span>
|
不怎么用得到,动态渲染还是比较危险的行为,什么表达式注入、模板注入、XSS都是动态渲染导致的。
双大括号填充数据不能用在HTML标签的属性里面,需要使用v-bind,或者缩写:
1 2
| <div v-bind:id="dynamicId"></div> <div :id="dynamicId"></div>
|
也可以通过对象一次性绑定多个属性:
1 2 3 4 5
| const objectOfAttrs = { id: 'container', class: 'wrapper', style: 'background-color:green' }
|
除了单个变量,还可以填充一个表达式,包括字符串拼接和函数调用:
1 2 3 4 5 6 7 8 9 10 11
| {{ 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代码块,具体可以看官方指令集文档。
计算属性
一种特殊的属性,在每次访问它时会先经过代码块运算得到结果,比如:
1
| <el-progress type="dashboard" :percentage="gotScorePercentage" :format="gotScore" :color="colors"></el-progress>
|
gotScorePercentage就是一个计算属性,在export default的computed中定义,访问时会先进行计算:
1 2 3 4 5
| computed: { gotScorePercentage: function() { return 100 * (this.myScore / this.fullScore); } },
|
条件渲染
先设置一个数据:
1 2 3 4 5
| data() { return { ok: 1 } },
|
条件渲染即v-if指令:
1 2 3 4 5 6 7 8 9 10 11 12
| <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指令:
1
| <h1 v-show="ok === 1">Hello!</h1>
|
区别在于v-show的实现方式是切换标签的display属性,所以无论条件如何,标签都会被渲染。
而v-if是惰性的,只有条件符合时才会被渲染。
列表渲染
先设置一串数据:
1 2 3 4 5 6 7 8
| data() { return { items: [ {msg: "Twings"}, {msg: "Aluvion"} ] } }
|
v-for指令可以遍历数组,分割成index和数据项并生成多个HTML块:
1 2 3
| <li v-for="(item, index) in items"> {{ index }}: {{ item.msg }} </li>
|
根据文档,使用v-for时最好给它提供一个唯一的key便于渲染。
事件处理
JavaScript常用的on事件,在Vue里可以用v-on指令或者@click的简写方式监听事件,先定义一个method:
1 2 3 4 5
| methods: { clickButton(event) { console.log(event) } },
|
然后再将它配置到click事件里:
1
| <el-button @click="clickButton">{{ message }}</el-button>
|
这样点击按钮就能触发事件了。
表单输入绑定
Web页面中常用的表单传值,为了JavaScript异步传值需要将表单输入绑定到某个变量上面:
1 2
| <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样式就恢复正常了:
1 2 3 4 5 6 7 8 9 10 11 12 13
| @media (min-width: 1024px) { body { place-items: center; }
#app { grid-template-columns: 1fr 1fr; padding: 0 2rem; } }
|
抽屉式导航栏
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| <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>
|
环形进度条
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <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代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| 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 }, },
|
标签式导航
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| <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>
|
日历
1 2 3
| <div v-if="activeIndex == 'index'" style="width: 95%; margin: 0 auto;"> <el-calendar v-model="dateValue" /> </div>
|
倒计时
1 2 3 4 5 6 7 8 9 10 11
| <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隐藏表单
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <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官方文档