当前位置: 首页 > news >正文

制作一个 MBTI 人格测试网页项目

我一直对心理学很感兴趣,尤其是 MBTI 人格测试这个话题,每次和朋友讨论起“你是 INFJ 吗?”、“他八成是个 ESTP”之类的桥段,总会引发一连串的共鸣和好奇。所以在一次创作中,我决定把这个兴趣落地为一个真正可用、体验良好且外观精美的 MBTI 人格测试网站,最终上线。

这篇文章就是我对整个项目开发过程的完整回顾。从页面交互设计、代码组织结构,到动画细节、数据模型与心理学逻辑,我都会尽量详实地记录下来,哪怕是一条看似简单的样式,我都尝试讲清它背后的思考。希望这不是一篇教程式的说明书,而是一次充满温度的创作记录。


项目的最初构思:如何让“测试”变得温柔些?

最开始我并不想直接做一个“冷冰冰”的测试表单,而是希望能营造一种心理游戏式的体验。我的目标用户也不是心理学专业人士,而是像我一样对人格测试感兴趣的普通人。我希望他们在进入这个网站时,就能被一种温柔、治愈的气质包围,而不是被问卷压得喘不过气。

所以我第一步先定了整体基调:视觉要清新,配色温柔,交互舒适流畅。基于这些方向,我选用了 Vue 3 + Vite 作为技术栈,UI 组件选择了 Element Plus,动画则配合 anime.js 和 echarts 来完成必要的图形反馈。整站只有 3 个核心页面:首页、测试页和结果页,我把所有的逻辑都控制在这三页之中,让整个项目保持轻量,但功能齐全。


整体结构设计:简洁三页式设计背后的权衡

首页设计思路

我设计首页的初衷是“不要急着做题,先感受氛围”。所以它的主视觉部分是一个渐变背景,搭配粉白色调,并设计了浮动樱花动画。在视觉上制造一种淡雅的安静感。这种感觉有点像走进了一家日式心理咖啡馆。

我用了一个小标题引导:“探索属于你的人格气质”,再用一个按钮承接“开始测试”。页面中间我放了四个图标:MBTI介绍、性格类型、理论背景、关于本站,这些其实都是静态卡片,但通过 hover 动效做出柔和的响应,不至于显得生硬。

首页代码使用 flexvh 单位确保自适应布局,渐变背景使用了线性渐变和动画:

<template><div class="home-container"><div class="slogan"><h1>探索属于你的人格气质</h1><el-button type="primary" @click="goToTest">开始测试</el-button></div><div class="features"><el-card v-for="feature in featureList" :key="feature.title"><h3>{{ feature.title }}</h3><p>{{ feature.description }}</p></el-card></div></div>
</template>
.home-container {background: linear-gradient(135deg, #fdeff9, #ecf0ff);height: 100vh;display: flex;flex-direction: column;justify-content: center;align-items: center;.slogan {text-align: center;h1 {font-size: 2.5rem;color: #444;}}
}

虽然只是几个容器,但我在 hover 和 focus 状态下补上了圆角放大、微妙阴影以及 transition 效果,从而让卡片在视觉上“呼吸”起来。


流程图:从首页到测试完成的流程思维

为了理清用户在站内的行为流转,我一开始就画了以下这张结构图,确认每个页面之间的跳转路径与信息传递逻辑。这个流程图是我后续开发过程中的导航灯塔。

在这里插入图片描述

这个图虽然简单,但极大帮助我在后续组件传参、路由跳转、状态管理中避免走弯路,Pinia 中的状态设计也直接围绕这个流程展开。


性格测试页的构建过程:状态管理与交互逻辑

测试页是整个项目最复杂的部分,既要动态显示题目,又要处理用户选项,还要支持中途返回重新作答。我一开始用了一个 questions 数组存储所有题目和选项,每题都是一对相对倾向,比如 “你更喜欢专注思考还是现实操作?”,用户通过左/右滑块或按钮选择更接近的选项。

我使用 Pinia 来维护全局状态。状态包括:

export const useMbtiStore = defineStore('mbti', {state: () => ({currentIndex: 0,answers: [] as string[],result: '',}),actions: {recordAnswer(answer: string) {this.answers.push(answer);this.currentIndex++;},reset() {this.answers = [];this.currentIndex = 0;}}
});

组件内通过 computed 获取当前题目,通过 watch 监听当前题目索引变化,控制进度条与交互反馈,逻辑清晰直观。点击选项按钮后立即保存答案并切换下一题,最后一题提交后跳转到结果页。

我没有使用表单提交或统一一次性上传,而是用每次作答立即记录的方式,在用户切换页面时也能保持状态。


性格计算算法设计:四维度拆解判断

MBTI 分为四个维度(E/I、S/N、T/F、J/P),每个维度有若干个问题代表某一方向。我在设计题目时,给每题加了一个 dimension 字段,这样就能在提交后统计各维度中用户选择的倾向。

最终人格类型是通过简单对比每个维度中得票高的那一方得出。例如:

function calculateMBTI(answers: string[]): string {const dimensionScore = {E: 0, I: 0,S: 0, N: 0,T: 0, F: 0,J: 0, P: 0,};answers.forEach(ans => {dimensionScore[ans]++;});return [dimensionScore.E > dimensionScore.I ? 'E' : 'I',dimensionScore.S > dimensionScore.N ? 'S' : 'N',dimensionScore.T > dimensionScore.F ? 'T' : 'F',dimensionScore.J > dimensionScore.P ? 'J' : 'P',].join('');
}

这个算法虽然简单,但非常直观易于调试,逻辑一目了然。


结果页的视觉设计与图表生成

结果页是这个项目中最需要“氛围感”的部分。我不想只显示“你是 INFJ”,那样未免太冰冷,所以我展示了:

  • 性格类型的详细介绍
  • 优势与劣势
  • 推荐职业
  • 同类型人物
  • 四维雷达图展示倾向分布

视觉上,我用 Echarts 生成雷达图,初次进入时附带渐入动画,四个维度分布一目了然。下方用卡片方式展示详细信息,配合合适的行间距和配图,使整个页面富有层次感与阅读节奏。

雷达图数据准备如下:

const chartData = {indicator: [{ name: '外向/内向', max: 10 },{ name: '感觉/直觉', max: 10 },{ name: '思维/情感', max: 10 },{ name: '判断/知觉', max: 10 },],value: [7, 3, 9, 5]
};

Echarts 配置中我设置了 areaStyleshadowColor,营造一种柔软包裹的视觉质感,曲线也设置为平滑贝塞尔曲线,避免尖锐突兀。


动效设计:从细节让页面“呼吸”起来

虽然是一个功能型网站,但我一直觉得,动效对于用户体验来说并不是“锦上添花”,而是“必需品”。一个页面哪怕内容再有价值,如果交互生硬,切换突兀,都会让人觉得有些“卡”。所以在这个 MBTI 网站中,我对动画做了很多打磨。

Vue 的过渡系统给了我很大的帮助。在测试页切换题目的时候,我加了一个简单的左右滑动动效,让问题像“翻书”一样地过渡:

<transition name="slide-fade" mode="out-in"><QuestionCard :question="currentQuestion" />
</transition>
.slide-fade-enter-active,
.slide-fade-leave-active {transition: all 0.4s ease;
}
.slide-fade-enter {transform: translateX(30px);opacity: 0;
}
.slide-fade-leave-to {transform: translateX(-30px);opacity: 0;
}

同时,在每次完成作答进入下一题的瞬间,我还使用 anime.js 给按钮加入了缩放反馈,用 scale(1.1) -> scale(1) 的方式做出一种“确认”感,增强用户点击后的满足感。这些动画看似简单,但让页面在静与动之间取得了很好的平衡。


响应式设计:从手机到桌面都要温柔呈现

一开始我只在桌面上做开发,等到测试的时候我才意识到这个项目非常适合移动端使用。大多数人会在微信或手机浏览器中打开,所以我花了不少时间做了响应式适配。

我没有使用 Tailwind 或 bootstrap 之类的 UI 框架,而是选择纯 CSS + SCSS 实现响应式布局。用 @media 媒体查询,配合 flex 和百分比宽度,来实现不同屏幕下的自动适配。

在测试页中,每一个题目组件都是 100% 宽度的卡片结构,在桌面上会居中展示、留出左右白边;而在手机上则会贴边,全屏渲染,提升沉浸感:

.question-card {width: 100%;max-width: 600px;margin: auto;padding: 2rem;background: white;border-radius: 12px;box-shadow: 0 8px 24px rgba(0, 0, 0, 0.05);@media screen and (max-width: 768px) {padding: 1.2rem;border-radius: 0;box-shadow: none;}
}

我也写了 rem 单位的 font-size 适配脚本,通过设置 html { font-size: 62.5%; } 再统一使用 rem,使得字体在不同分辨率下大小合适。


全局样式与主题控制:用 SCSS 和变量统一规范

为了让样式更加可控和统一,我在项目中引入了 SCSS,并配置了一套全局变量系统。比如颜色、圆角、阴影、字号等等,都集中管理,这不仅方便日后维护,也有助于组件之间保持一致的风格。

// styles/variables.scss
$primary-color: #7c83fd;
$secondary-color: #f8f9fc;
$text-color: #333;
$border-radius: 12px;
$shadow: 0 4px 16px rgba(0, 0, 0, 0.06);

在每个组件中,我都用变量替代硬编码颜色,比如:

.card {background-color: $secondary-color;border-radius: $border-radius;box-shadow: $shadow;
}

这样做带来了意外的好处:后期我加入深色模式切换时,只需要重新定义一套变量,而不需要重写所有组件的样式逻辑。


组件设计:结构清晰,职责明确

虽然整个站点页面不多,但我还是坚持把功能拆成了独立组件。比如:

  • QuestionCard.vue:单个题目卡片
  • ProgressBar.vue:顶部进度条
  • ResultRadar.vue:Echarts 生成雷达图
  • ResultDetail.vue:性格说明文字卡片

每个组件都有单独的 props 定义和局部样式,保持了良好的职责分离。Vue3 的 <script setup> 写法也让代码结构非常清爽,配合 TS 类型推导,即使组件间传参也不容易出错。


状态管理:Pinia 的使用体验

我之前在一些项目中使用 Vuex,后来用了 Pinia 之后就彻底“转粉”了。它的 API 更简洁,组合式逻辑更清晰,类型推导也优秀。

我创建了一个 mbti.ts 的 store 文件,负责管理当前题目索引、用户答案、最终结果。组件中通过 useMbtiStore() 获取状态并调用更新方法。

// store/mbti.ts
export const useMbtiStore = defineStore('mbti', {state: () => ({currentIndex: 0,answers: [] as string[],result: ''}),getters: {currentQuestion(state) {return questions[state.currentIndex];},isFinished(state) {return state.answers.length >= questions.length;}},actions: {next(answer: string) {this.answers.push(answer);this.currentIndex++;},calculate() {this.result = calculateMBTI(this.answers);},reset() {this.answers = [];this.currentIndex = 0;}}
});

结果页的跳转也是通过 store 控制逻辑判断:只有在作答完毕后才能跳转,避免跳过题目。


小插曲:从主题崩坏到渐变重构

有一次我尝试加入粉蓝渐变背景时,直接在 body 上用了 linear-gradient。结果部分移动端浏览器因为 height: 100% 设置不正确导致渐变只渲染到可视区域的一半,下面是白屏。

我一开始以为是某个 CSS 被覆盖了,结果调了半天才发现是页面内容高度不够导致 body 没撑开。最后的解决办法是在 html, body 上强制设定 min-height: 100vh;,并用 flex: 1 保证容器占满剩余空间,才成功解决。

这是前端开发中常见却很隐蔽的问题,写起来一句话,调试起来几个小时……


流程图补充:状态变化之间的逻辑链路

在结果页完成计算之前,我还加了一个“处理中…”的等待动画页,用来模拟分析过程。这部分虽然只是一个过渡状态,但也是用户体验的重要节点。

以下是状态变化的更详细逻辑图:

在这里插入图片描述

这个状态流很适合用状态机来实现,不过项目规模不大,我最终用 Pinia 状态 + Vue 路由跳转实现了全部逻辑。


题库结构设计:保持灵活与可扩展性

我一开始是把题目硬编码在组件内部的,但很快发现这个做法既不利于维护,也不方便扩展或更换题库。于是我将所有题目独立抽离到一个专门的 questions.ts 模块中,用标准化的数据结构来组织。

每道题都由四个字段组成:题干、两个选项、对应的维度标记。

// data/questions.ts
export const questions = [{question: '当你参加一个社交聚会时,你更倾向于?',optionA: { text: '与很多人交谈', trait: 'E' },optionB: { text: '只与熟人聊天', trait: 'I' }},{question: '你更相信哪种判断方式?',optionA: { text: '逻辑与客观分析', trait: 'T' },optionB: { text: '情感与个人价值', trait: 'F' }},// ...共40题
];

这种结构的好处是逻辑简单,扩展方便。后续如果要做不同语言的题库,也只需要提供多语言版本的数据文件,而无需修改组件逻辑。

在组件中通过当前索引动态渲染题目文本与选项,同时记录用户选择的 trait 字母,最终累积成四个维度的对比统计:

const traitCount = {E: 0, I: 0,S: 0, N: 0,T: 0, F: 0,J: 0, P: 0
};answers.forEach(trait => {traitCount[trait]++;
});

最后根据每组维度中数量较多的那个,组合出最终 MBTI 类型。


Vue Router:页面跳转的“仪表盘”

MBTI 测试共包含 4 个页面:首页 / 测试页 / 加载页 / 结果页。我使用 Vue Router 来组织这些页面的导航结构。

这不是一个 SPA 式的滚动单页,而是标准的路由切换式导航。页面结构在 router/index.ts 中这样定义:

const routes = [{ path: '/', component: Home },{ path: '/quiz', component: Quiz },{ path: '/loading', component: Loading },{ path: '/result', component: Result }
];

这种方式不仅清晰分层,还能更好地控制每页的 meta 信息与生命周期行为。例如我在结果页加入了如下钩子:

onMounted(() => {if (!store.result) {router.push('/');}
});

这个判断用来防止用户在未答题的情况下直接访问结果页,确保路径跳转的合理性和数据一致性。

另外,我为每个页面都设定了 <meta name="description">document.title,以便于 SEO 和分享到微信、微博时有更好的预览效果。


数据缓存:为刷新与返回设计容错机制

在最初的版本中,如果用户在测试过程中刷新了页面,之前的答题记录就会完全丢失。我发现这样体验非常糟糕。于是我加入了一个简单的 localStorage 缓存机制。

每次答题时,我都会将当前进度与答案数组同步存入本地:

watch(() => store.answers,() => {localStorage.setItem('mbti-progress', JSON.stringify({answers: store.answers,index: store.currentIndex}));},{ deep: true }
);

当用户再次访问测试页时,我会检查是否存在缓存,并提示是否恢复:

const saved = localStorage.getItem('mbti-progress');
if (saved) {const { answers, index } = JSON.parse(saved);if (answers.length < questions.length) {store.answers = answers;store.currentIndex = index;}
}

虽然只是一个简单的缓存方案,但对于移动端用户来说,它极大提升了容错性与用户友好度。


图表设计:ECharts 自定义雷达图美化

MBTI 的四个维度最适合的可视化方式就是雷达图。我用 ECharts 实现了一个动态雷达图,展示用户的四个维度得分比例。

const option = {radar: {indicator: [{ name: '外向/内向', max: 20 },{ name: '感知/直觉', max: 20 },{ name: '思维/情感', max: 20 },{ name: '判断/感知', max: 20 }]},series: [{type: 'radar',data: [{value: [store.ei, store.sn, store.tf, store.jp],name: '你的 MBTI'}]}]
};

我还特别在颜色、字体、渐变线条上下了功夫,让图表更贴合整站的柔和 UI 风格。

图表在移动端上默认宽度为 100%,并通过 resize 事件自适应屏幕宽度。为了让图表初次加载不闪烁,我加入了 nextTick 配合 ref 的加载方式:

onMounted(() => {nextTick(() => {myChart.value = echarts.init(chartRef.value);myChart.value.setOption(option);});
});

项目结构优化:从零碎到清晰

刚开始项目文件很乱,组件、页面、样式都混在一起。开发到中期,我痛下决心做了一次目录结构的优化,现在的结构如下:

src/
├── assets/         # 图片资源
├── components/     # 通用组件
├── data/           # 题库数据
├── pages/          # 页面组件
├── router/         # 路由配置
├── store/          # Pinia 状态管理
├── styles/         # 全局 SCSS
└── utils/          # 工具函数

这种结构对于协作开发、维护迭代都非常友好。每个模块的职责明确,阅读成本低。


页面效果一览(结构草图)

我还为网站整体结构绘制了一份草图图示,用于说明页面之间的层次关系与跳转路径:

在这里插入图片描述

这份结构图在我调试路由与状态同步时发挥了重要作用,帮助我快速梳理页面流程与跳转逻辑。


工具函数封装:小功能不小作用

开发过程中,我逐渐发现有些逻辑重复出现在多个页面中,比如获取当前 MBTI 字符串、清除缓存、分享链接构建等。如果直接写在页面逻辑中,会让代码显得臃肿且难维护。

于是我将这部分逻辑封装进 utils 文件夹,形成了一些小而美的工具函数:

// utils/mbti.tsexport function getMbtiType(traitCount: Record<string, number>): string {return [traitCount['E'] >= traitCount['I'] ? 'E' : 'I',traitCount['S'] >= traitCount['N'] ? 'S' : 'N',traitCount['T'] >= traitCount['F'] ? 'T' : 'F',traitCount['J'] >= traitCount['P'] ? 'J' : 'P'].join('');
}export function clearLocalCache() {localStorage.removeItem('mbti-progress');localStorage.removeItem('mbti-result');
}

这些函数配合 Pinia 中的 store 使用,可以极大地减少模板内逻辑的复杂性。比如:

const result = getMbtiType(store.traitCount);

功能虽然不复杂,但在项目中实实在在节省了大量重复劳动。


动画与交互细节:打磨才是关键

一个 UI 看上去漂亮与否,除了布局和颜色,最重要的就是「动效细节」。

我特别为测试页的题目切换加上了渐隐渐现的过渡动画,使用的是 Vue 内建的 <transition> 标签:

<transition name="fade" mode="out-in"><div :key="currentQuestionIndex"><QuestionItem :data="currentQuestion" /></div>
</transition>

对应的 CSS:

.fade-enter-active, .fade-leave-active {transition: opacity 0.5s ease;
}
.fade-enter-from, .fade-leave-to {opacity: 0;
}

这种细腻的过渡不仅提升了体验,也降低了用户在切换题目时的认知压力,尤其在移动端显得尤为重要。

此外,我还在按钮上加入了按下回弹的 scale 效果,让用户每次点击都感到「有反馈」:

button:active {transform: scale(0.95);
}

结果页内容:文字 + 图表 + 个性化推荐

结果页并不是简单的显示“你的类型是 INFP”,我希望它能承载更多内容,让用户感到这个测试有深度、有用、有趣。

所以我为每种 MBTI 类型都准备了一段简洁的性格介绍、适合的职业方向,以及代表性的动漫角色参考:

// data/traits.ts
export const mbtiProfiles = {INFP: {name: "调停者",description: "富有理想,追求内在价值,喜欢独处与自我探索。",career: ["作家", "心理咨询师", "创意设计"],characters: ["长门有希", "阿良良木历"]},ESTJ: {name: "监护人",description: "务实可靠,擅长组织事务,是典型的执行者。",career: ["项目经理", "公务员", "法务人员"],characters: ["伊尔迷", "卡卡西"]}
}

渲染时我用图文结合方式展示,让页面丰富但不杂乱:

<h2>你的性格类型:INFP「调停者」</h2>
<p>你注重内在体验,常常有丰富的情感和想象力...</p><h3>适合职业:</h3>
<ul><li>心理咨询师</li><li>文案编辑</li>
</ul><h3>代表角色:</h3>
<div class="characters"><img src="/imgs/nagato.jpg" alt="长门有希" /><img src="/imgs/aragi.jpg" alt="阿良良木历" />
</div>

搭配渐变背景与柔光卡片 UI,用户在浏览结果时会有一种轻松愉悦的感觉,而不是冰冷的文字。


社交分享卡片生成:前端动手做一切

为了让用户能分享到朋友圈、微博等,我开发了一个小模块用于生成分享卡片,效果类似于知乎分享图:

  • 卡片上包含昵称、测试时间、MBTI 类型图标、二维码等;
  • 使用 html2canvas 将结果页部分内容截屏为图片;
  • 允许用户保存图片或长按识别。

这部分实现我用的是一个隐藏区域 + html2canvas

import html2canvas from 'html2canvas';async function generateShareImage() {const element = document.getElementById('share-area');const canvas = await html2canvas(element);const url = canvas.toDataURL('image/png');downloadImage(url, 'mbti-result.png');
}

配套 UI:

<div id="share-area" class="share-card"><h2>{{ userMbti }}</h2><p>测评时间:{{ time }}</p><img :src="qrCode" />
</div>

虽然这不是一个必要功能,但它极大提升了网站的趣味性和传播力。每次我测试后生成图发到朋友圈,总会有人点进来看并留言「我也是 INFP」什么的。


页面风格与色彩搭配:现代感+治愈系

配色方面我没有走极简黑白风,而是采用了「淡紫 + 柔绿 + 雾蓝」的渐变色调,营造一种温暖、安静又带点科幻感的氛围。

主色调配比如下:

  • 主背景色:#f5f6fa
  • 按钮色:#7c8bd0
  • 高亮文本:#4c9aff
  • 错误提示:#ff6b81

全站使用了 SCSS 全局变量:

$color-primary: #7c8bd0;
$color-secondary: #a4d4ae;
$color-bg: #f5f6fa;
$color-danger: #ff6b81;

再配合玻璃拟态风格(backdrop-filter + box-shadow),页面整体感觉不再是单薄的静态网页,而像一个轻巧但功能完整的「App」。


项目打包与部署:静态托管一点也不麻烦

整个项目开发完成后,我用 vite build 构建为静态文件,并选择部署到 GitHub Pages:

vite build
cp -r dist/* ../mbti-pages/
git push

GitHub Pages 配置了自定义域名 https://mbti.onefan.top,响应速度尚可,全球可访问,不存在国内用户打不开的问题。

同时我还写了一个 deploy.sh 脚本,每次部署一键完成:

#!/bin/bash
pnpm run build
cp -r dist/* ../mbti-pages/
cd ../mbti-pages
git add .
git commit -m "deploy"
git push

SEO 与社交媒体优化:细节决定传播力

虽然是一个轻量的 MBTI 测试网站,但我在上线前还是做了一些 SEO 和社交分享优化。主要包括:

  • 为每个页面设置 <title><meta> 标签;
  • 首页和结果页增加 og:twitter: 协议的分享信息;
  • 针对微信等无法读取 meta 的平台,增加带有摘要信息的分享图。

示例:

<!-- index.html -->
<head><title>MBTI人格测试 | 简洁可爱的心理测试站</title><meta name="description" content="基于MBTI理论的免费心理测试网站,快速了解你的性格类型。"/><meta property="og:title" content="MBTI人格测试 - 性格探索从这里开始"/><meta property="og:description" content="免费测试你的 MBTI 类型,看看你属于哪一类人格!"/><meta property="og:image" content="/imgs/share.png"/>
</head>

虽然这些元信息不会直接让用户看到,但在百度、Google、微信浏览器、QQ 浏览器的分享卡片中,会影响点击率和传播力。属于那种「不做没人说,做了效果立现」的优化。


适配移动端体验:小屏幕不妥协

从一开始我就假设用户主要来自手机,所以整个 UI 从字体到按钮大小、内容布局,都优先为移动端设计。

我使用了 rem 单位和媒体查询辅助响应式适配,配合 viewport 控制:

<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0"/>

CSS 中部分关键样式:

html {font-size: 16px;@media screen and (max-width: 768px) {font-size: 14px;}
}

此外,我为每个模块设置了最大宽度和边距,让其在大屏浏览器中也不会左右拉伸过长,确保阅读体验:

.container {max-width: 768px;margin: 0 auto;padding: 1.5rem;
}

有意思的是,微信、QQ 的内置浏览器对 vh 单位存在 bug(尤其 iOS),我还特地用 JS 修正了一下页面高度:

function fixViewportHeight() {const vh = window.innerHeight * 0.01;document.documentElement.style.setProperty('--vh', `${vh}px`);
}
window.addEventListener('resize', fixViewportHeight);
fixViewportHeight();

这使得我们在 CSS 中可以使用 height: calc(var(--vh, 1vh) * 100),避免了手机输入框弹出时页面错乱的问题。


流程设计回顾:从进入到完成的完整旅程

这个小项目虽然页面不多,但从用户第一次访问,到完成测试、查看结果并分享,我还是做了一个完整流程图来帮助梳理整个用户体验路径:

在这里插入图片描述

每一步都有对应的页面、数据记录方式和用户交互。流程清晰才能避免用户迷路或中途放弃。


本地缓存与容错:不怕用户中途离开

有些用户可能会在做一半测试的时候关掉页面或误触刷新,我考虑到了这个情况,使用 localStorage 存储答题进度和当前题目序号。

watch(() => store.currentIndex, (val) => {localStorage.setItem('mbti-progress', JSON.stringify({current: val,traitCount: store.traitCount}));
}, { deep: true });

当用户再次访问时,程序会检测缓存并弹出提示:

const cache = localStorage.getItem('mbti-progress');
if (cache) {const confirm = window.confirm('检测到上次未完成的测试,是否继续?');if (confirm) {// 恢复测试进度} else {clearLocalCache();}
}

这样就算中途退出也不会浪费时间。也增强了用户粘性。


自动打乱题目顺序:防止记忆刷答案

为了提升测试的客观性,我没有固定题目顺序,而是在用户进入测试页时自动打乱:

function shuffle<T>(arr: T[]): T[] {return arr.map(item => ({ item, sort: Math.random() })).sort((a, b) => a.sort - b.sort).map(obj => obj.item);
}

每次加载测试页时我调用 shuffle(questions),确保每个人顺序不同,有效防止了“直接背答案”行为。


总结与收获

这次做 MBTI 测试网站的经历不算漫长,但确实让我把 Vue3、TypeScript、Pinia、SCSS、前端动画、静态图生成等多个知识点都实践了一遍,也在 UI/UX 的打磨上有了更高要求。

从一开始的“能跑起来”,到后来“好用”、“好看”、“能传播”,一步一步优化下来,整个过程既有成就感,也极具挑战。

最重要的是,每次朋友看到分享图都说“这个测试有点意思”,那种被认可的感觉,值了。

参考地址:https://gitee.com/Twilight-Fanyi/mbti-personality

在这里插入图片描述

http://www.dtcms.com/a/462414.html

相关文章:

  • Echarts如何实现line的实线虚线的分段,并且虚实线连接点平滑过度效果(未来预测场景)?
  • 苍穹外卖day06
  • mysql大表批量查询中IN vs JOIN vs CTE 性能实验
  • Cryptomator:免费的加密工具,
  • 逐位加|二分
  • 外贸行业网站推广wordpress galleria
  • 没技术怎么做网站湛江的高铁站建在哪里
  • MySQL 中数据完整性约束、外键管理(含级联策略) 和多表查询
  • 做效果图的网站有哪些软件有哪些wordpress漂浮
  • 为什么ffmpeg进行视频合成有时长误差
  • 做旅游销售网站平台ppt百度的域名
  • 网站建营销型企业网站有哪些类型
  • 2008服务器网站专门做定制的网站
  • 【软件设计师中级】计算机组成与结构(四):总线系统 - 计算机的“高速公路网络“
  • 专注服务于站长和网站的信息平台.网站建设需要学ps吗
  • 视频直播点播平台EasyDSS推拉流技术结合无人机推流在道路交通巡检场景中的应用
  • 涂鸦T5AI开发板直播互动游戏控制器实现方案【全开源】
  • Spring Boot 应用启动机制详解
  • 河南省建设工程造价协会网站joomla 2.5:你的网站建设_使用与管理 pdf
  • 只有通过Motor 获取 mongodb的collection,才能正常使用 async with collection.watch()监听集合变更
  • 做一个网站 如何盈利网站开发一般用什么软件有哪些
  • 能够完美“适配”不同传感器的语音芯片WT2003H
  • 怎样建设网站的步骤网站建设中js控制什么
  • 陇南地网站建设黄骅市有什么好玩的地方
  • 零基础从头教学Linux(Day 46)
  • RK3588从数据集到训练到部署YoloV8
  • 网站商城建设价格做网站的一定要开80或8080端口
  • STranslate(翻译工具OCR工具) 中文绿色版
  • 算法学习 || 动态规划(买卖股票的最佳时机2)
  • 网站常用图标素材哈尔滨手机网站建设