博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Vue 项目实战
阅读量:3959 次
发布时间:2019-05-24

本文共 5644 字,大约阅读时间需要 18 分钟。

前言

为了练习Vue,写了一个小项目,主要内容是答题。

项目简介

  • ElementUI框架
  • 无后端,数据来源为本地JSON文件
    • 也可以改造成有后端的,查找题目种类就在后端完成
  • 比较原生,适合刚刚接触Vue,想做练习的同学
  • 笔者水平有限,如有不严谨之处请多多批评

预览图

主页/开始页

在这里插入图片描述

答题页

在这里插入图片描述

正文

1. 初始化项目

  • 使用脚手架创建项目,cli3.x,选择router即可
  • 添加element,详见
  • 安装axios

2. 在views文件夹中创建如下目录

在这里插入图片描述

3. 写路由

import Vue from 'vue'import VueRouter from 'vue-router'Vue.use(VueRouter)const Main = () => import('@/views/Main')const Quiz = () => import('@/views/quiz/Quiz')const Start = () => import('@/views/quiz/Start')const routes = [    {
path: '/', redirect: '/index', component: Main, children: [ {
path: '/index', component: Start }, {
path: '/quiz', component: Quiz }, ] },]const router = new VueRouter({
mode: 'history', base: process.env.BASE_URL, routes})export default router

非常简单的路由设计,其中的路由的加载方式为懒加载。

4. 主页

4.1 布局

你好,请选择答题范围

全选
{
{ city }}
开始答题
4.2 逻辑
export default {
name: "Start", data() {
return {
checkAll: false, btnDisabled: false, checkedCities: ['文科', '理科'], cities: ['文科', '理科', '娱乐', '生活', '文艺', '流行'], isIndeterminate: true }; }, created() {
this.checkedCities = localStorage.checkedWords ? localStorage.checkedWords.split(',') : this.checkedCities }, methods: {
isDisabled() {
this.btnDisabled = this.checkedCities.length === 0 }, handleCheckAllChange(val) {
this.checkedCities = val ? this.cities : []; this.isIndeterminate = false; this.isDisabled() }, handleCheckedCitiesChange(value) {
let checkedCount = value.length; this.checkAll = checkedCount === this.cities.length; this.isIndeterminate = checkedCount > 0 && checkedCount < this.cities.length; }, start() {
this.$set(localStorage, 'checkedWords', this.checkedCities) this.$router.push('/quiz') this.$emit('checked', this.checkedCities, true) } }}
  • created函数,在创建组件完成后,检查浏览器缓存localStorage中是否已经存在选择的题目范围。如果有则直接使用,没有就使用默认选择的。
    • 注意,数组储存到localStorage中,是以,隔开的字符串形式,所以要用split函数来分割一下。用法详见
  • isDisabled函数是用来判断选中题目种类的个数是否为0,如果是则按钮不可点击。
  • start函数按钮点击后触发,目的是跳转路由至/quiz答题页,并将题目种类数组传给父组件Main
    • 传给父组件的目的是,通过父组件再传给兄弟组件Quiz,即子传父,父传其他子,也可以用过vuex实现。

5. Main主框架

5.1 布局

Vue答题

--zed 制作

历史排行榜

  1. {
    { item }}

布局采用ElementUI中如下布局

Header
Aside
Main
5.2 逻辑
export default {
name: "Main", data() {
return {
info: [], scores: localStorage.scores ? localStorage.scores.split(',') : [] } }, methods: {
setInfo(data) {
this.info = data }, setScore(data) {
this.scores.push(data) this.sortArr(this.scores) if (this.scores.length > 10) {
this.scores.splice(10) } localStorage.scores = this.scores }, sortArr(arr) {
return arr.sort((x, y) => y - x) } }}
  • data中的info数组,就是前面Start组件传过来的数据
  • data中的scores,每次打完题之后,Quiz数组都会将分数传过来。然后将这些分数压入数组,存储到localStorage
  • setInfo函数捕获子组件Start传过来的数据,并赋值给该组件
  • setScore函数捕获子组件Quiz传过来的数据,并压入分数数组,且要排序。如果分数数组的长度超过10,则只截取分数最高的十项
  • sort函数为数组排序,用法详见

6. 答题页

6.1 布局

score:{
{ score }}

hp:

科目范围:{
{ info }}
本题属:{
{ currentQuiz.school }} {
{ currentQuiz.type }}

{
{ currentQuiz.quiz }}

{
{ item }}
next
6.2 逻辑
  • created函数
    • 检查父组件传过来的info,即题目种类数组是否为空,如果是空则返回到首页,重新选择。
    • 在组件创建完毕后,就要获取JSON文件的内容了,也就是题目列表。
    • 接下来要根据题目种类数组,来筛选题目
    • 然后开始出题
  • fetch函数用来发送axios请求,这里的axios被笔者挂载到Vue 的原型上,这样便可全局使用。也可以只在该组件中引入axios
    • async && await用法详见轻松理解 async 与 await
    • main.js文件中书写
import axios from "axios"Vue.prototype.$http = axios
  • filter函数用来过滤题目列表,如果题目列表的长度为6,也就意味着全选,则不用筛选,直接返回。
  • randomQuiz函数用于随机出题,出题之后,为了防止重复,直接在题目列表中删除此题
  • killProgress用于管理进度条计时器
    • 做一些初始化工作。如果计时器已经存在,则先删除;每次都要恢复选中的答案answer值,因为answer只有1,2,3,4,所以恢复为0是可以的;恢复进度条百分比
    • 添加定时器,100ms意味着进度条会在10s后走完。
    • 如果走完,则触发判定答案和重新渲染题目等一系列工作,这些工作都在nextBtn函数中完成。也就是说,进度条走完和点击下一题按钮的效果相同
  • checkAnswer函数用于判定答案的对与错
    • Promise用法详见
    • 由于JSON文件中,每一道题的答案都是根据索引值来判断,且四个选项的索引为1,2,3,4。咱们选择的答案是0,1,2,3,所以要在题目正确答案-1或者再咱们的答案+1,都是可以的。
    • 这里也解释一下,渲染4个选项时,为何label值不直接绑定索引,而是题目的_id再加索引值。这是因为vue读取缓存的机制,这道题的选项的label值如果绑定了1,2,3,4,下道题也是1,2,3,4,这样vue会直接将缓存中的四个选项捞出来,而不是重新创建。这就意味着,咱们上一题的选中效果,切换到下一题的时候,依旧存在。
    • 为何用this.$set赋值,可见
    • 回到这个函数的业务,如果说用户答对了此道题,则直接切换到下一题;如果没有答对,则过一秒再切换,为的是让用户看一眼正确答案。返回值是布尔类型,但并不意味着true代表答对了,而是代表hp还是有的,也就是说可以继续出题;false代表hp用完了,不能再继续出题
  • nextBtn函数,一旦点击按钮或者进度条结束,就把按钮变为不可点击,这是为了屏蔽用户的无效操作,而且多次点击有可能导致计时器的混乱。一旦点击按钮或者进度条结束,就要判定是否继续出题;如果继续,则需要把按钮恢复可点击,调用出题函数;如果结束了,则把分数传给父组件Main,并且跳转路由至/index
  • 数据this.hp为何是一个数组呢?这个数组是用来渲染那个灯泡图标的。
    在这里插入图片描述
  • 数据isAnswer,配合选项v-for渲染来绑定样式类。初始该数组里有4个false,一旦进度条结束或点击按钮,则会将正确的那个选项绑定样式类。什么?你说选正确了就不用绑定了是吧?不错,但是正确没有延迟1s切换下一题,用户也就看不到这个效果了。
.isAnswer {
border: 2px solid #0f0;}

结语

总的来说,这个项目难度不大,但是一些基础琐碎的知识挺多的,适合练手。

源码

链接:https://pan.baidu.com/s/1HKvGly1H2lpQCkxfm2Onlw

提取码:z1ed

求赞!!!

你可能感兴趣的文章
xp 忘记密码
查看>>
xp 忘记密码
查看>>
java 过滤器
查看>>
java 过滤器
查看>>
as发送邮件
查看>>
AJAX应用之注册用户即时检测
查看>>
File 类小结
查看>>
java除去字符串空格
查看>>
jsp 2.0标记文件
查看>>
Hibernate中Criteria的完整用法
查看>>
sql jsp
查看>>
Word生成目录
查看>>
JSP彩色验证码源程序编写
查看>>
java操作Excel、PDF文件
查看>>
java 获得系统变量
查看>>
window.event对象用法讲解
查看>>
jive license保护原理
查看>>
java des加密
查看>>
struts&hibernate&spring例子
查看>>
inno使用教程
查看>>