一.为什么学typescript

var三大框架都是用ts写的,做个类比,如果你不晓得阿拉伯数字和加减法,怎么解应用题?同理不会ts如何应用vue开发工作我的项目?

二.开发环境筹备

1.装置node,https://nodejs.org/en/,下载安装,一路默认下一步 ,输出node -v显示版本胜利

2.装置ts,npm install -g typescript,输出tsc -v显示版本 阐明胜利

3.装置vscode https://code.visualstudio.com/

三.新建一个Helloworld 文件夹,用vscode关上,而后新建app.ts,外面就写一句

console.log('hello world')

而后在vscode 终端输出 tsc app.ts ,如果报错 先输出 set-ExecutionPolicy RemoteSigned

最初看到生成一个编译同名app.js阐明胜利了。你说我想看看打印成果,整个index.html

<!DOCTYPE html><html lang="en"><head>    <meta charset="UTF-8">    <meta http-equiv="X-UA-Compatible" content="IE=edge">    <meta name="viewport" content="width=device-width, initial-scale=1.0">    <title>Document</title>    <script src="app.js"></script></head><body>    </body></html>

console.log('hello world')改成console.log('真香')终端按上箭头,从新tsc app.ts,看浏览器控制台,能看到真香。

三、typescript基础知识

1.模块治理

传统教程该讲数据类型了,咱们先放放间接将外围的模块治理,因为根底的数据类型number string 不讲你也会,高级的数据类型讲到了当初你也不明确怎么用。就说一件事,ts下怎么定义各种数据类型省的遇见ts外面类型报错,你就懵圈了,这里跟js是不一样的。

就简要提一嘴,常见的就是定义数组:

let num = 0;let str = '彬哥好帅';//下面的没区别,上面说不一样的//js这么写是没问题的://定义数组,第一种:let person: string[] = ['张三', '帅彬'];//第一种有个毛病,比方我要解决一个数组过程中split的时候,可能会呈现,[1,'张三',2,'帅彬'],这样你定义成string就报错两种类型let worker: [number, string] = [1, "张三",2,"帅彬"];//记住以上两种数组就够用了,接下来咱们说说数组外面套对象{},这个最罕用,以前咱们在js下定义一个数组含对象//js下let worker = [    {1, "张三"},    {2, "帅彬"}  ];//ts下 这样写interface Worker {  id: number;  name: string;}let worker: Worker[] = [  { id: 1, name: "张三" },  { id: 2, name: "帅彬" }];//你在我的项目中很可能遇到一种状况就是你通过对象的key的值去索引对象的value,比方worker[//这里是什么也可能是动静的变量],//这种状况怎么定义?interface Worker {  id: number;  [key: string]: string;//要害是这一句,前面你甭管它叫啥只有是string类型编译就不错}let worker: Worker[] = [  { id: 1, name: "张三" },  { id: 2, jobName: "帅彬" }];

最初一句,数据类型你不晓得也不要用any,不要用any,不要用any,因为往往报any的时候,意味着你代码写的有问题。

咱们回到vue中立即就要会用到的模块治理,也就是组件。

咱们先定义一个组件,你叫一个文件也好,一个模块也好不必管它叫啥,反正就这么个玩意,就叫mod1.ts

各个文件外面代码是这样子的:

//mod1.ts//我定义了一个a 并导出export let a = 12;
//app.ts//因为我定义的是a 所以导入的名字也必须跟mod1.ts外面的统一,也得是a还得用花括号括起来,另外mod1门路用./同级 mod1不必加后缀import {a} from './mod1'console.log(a);
<!DOCTYPE html><html lang="en"><head>    <meta charset="UTF-8">    <meta http-equiv="X-UA-Compatible" content="IE=edge">    <meta name="viewport" content="width=device-width, initial-scale=1.0">    <title>Document</title></head><body>    <script type="module" src="app.js"></script>  </body></html>

index.html留神一下几点:

1.script type="module" 必须写

2.要扔到body外面

3.要想反对import和export 必须用服务器环境相似这样 http://localhost/HelloWorld/,能够轻易装个wamp之类的而后扔到www文件夹下

4.这些都记不住也没事依照我说的做即可,我只是阐明mod1.ts和app.ts外面的内容,这两个文件内容是外围

vscode终端输出:

tsc --module ES2015 app.ts

这个命令也不必记,就是为了让程序跑起来,咱们持续外围,批改下mod1.ts

export let a = 12; //mod1.ts//我定义了一个a 并导出export let a = 12;

每次改货色tsc 太吃力,咱们用监督命令,仍然不必记,只看文件中源码即可,用了这个命令你就不必管tsc了,改变主动变

tsc  --w --module ES2015 app.ts mod1.ts

批改mod1.ts

export let a = 12;export let b = 5;export let c = 8;

批改app.ts

import {a,b,c} from './mod1'console.log(a,b,c);

浏览器控制台,呈现 12,5,8,大家看从mod1外面导入了三个mod1中导出的, a,b,c等导出的名字太多了记不住怎么办?我也不想去mod1.ts外面去找导出的叫什么名字,那就不记了,间接批改app.ts,这样都被我放到了arrAbc数组里

import * as arrAbc from './mod1'console.log(arrAbc.a,arrAbc.b,arrAbc.c);//12,5,8

你说 我想默认一个导出,也不想记名字,怎么办?

批改mod1.ts

let a = 666;export default a;export let b = 5;export let c = 8;

批改,app.ts

// import * as arrAbc from './mod1'// console.log(arrAbc.a,arrAbc.b,arrAbc.c);import numA from './mod1'import{b,c} from './mod1'console.log(numA,b,c);

刷浏览器,呈现666,12,5,8

总结一下:

1.通过 export default形式导入,在导入时不须要加 {} 在一个文件或模块中,名字轻易起,比方numA

2.export default只能有一个 export default前面不能跟const或let的关键词

export default 一个文件只能有一个

对了,你不定义名字都没事,比方,批改mod1.ts

let a = 666;export default {    "name":"大彬哥",    "age":18};export let b = 5;export let c = 8;

批改app.ts

import teacher from  './mod1'console.log(teacher.name,teacher.age);

刷浏览器,后果就进去了,大彬哥 18,这种形式 vue中用的最多

2.类型定义(vue用到再说)

总结,到这里你就彻底明确了vue中 import了或者export 什么import * as 什么的。就有了写组件或者援用他人库的根底。有了这个根底咱们就能够做应用题了,咱们装上Vue最新版,看看还有啥用不明确的。

四、vue基础知识

一、咱们怎么创立vue我的项目?

npm create vite@latest

你不必管下面vite不vite,你就依照这个命令跑一把梭,而后你就会看到,

咱们从index.html看,有这么一句,

<script type="module" src="/src/main.ts"></script>

是不是咱们方才说的模块治理的货色?顺藤摸瓜,看看main.ts

import { createApp } from 'vue'import './style.css'import App from './App.vue'createApp(App).mount('#app')

引入了vue模块下的 createApp,还有其它一堆,显著咱们当初只关怀

import App from './App.vue'

持续顺藤摸瓜,App.vue,仍然是三段论,script、template、style

<script setup lang="ts">import HelloWorld from './components/HelloWorld.vue'</script><template>  <div>    <a href="https://vitejs.dev" target="_blank">      <img src="/vite.svg" class="logo" alt="Vite logo" />    </a>    <a href="https://vuejs.org/" target="_blank">      <img src="./assets/vue.svg" class="logo vue" alt="Vue logo" />    </a>  </div>  <HelloWorld msg="Vite + Vue" /></template><style scoped>.logo {  height: 6em;  padding: 1.5em;  will-change: filter;}.logo:hover {  filter: drop-shadow(0 0 2em #646cffaa);}.logo.vue:hover {  filter: drop-shadow(0 0 2em #42b883aa);}</style>

外围就两句话,第一句:

<script setup lang="ts">import HelloWorld from './components/HelloWorld.vue'</script>

这里引入了一个组件,setup是啥你也不必管,反正你晓得引入了HelloWorld.vue就OK(前面我会讲),而后是

<HelloWorld msg="Vite + Vue" />

给HelloWorld的msg属性服了一个"Vite + Vue" 的值,再致力一下就到底了!看看components外面的HelloWorld组件,敲黑板划,重点来了:

<script setup lang="ts">  import { ref } from "vue";  defineProps<{ msg: string }>();  const count = ref(0);</script><template>  <h1>{{ msg }}</h1>  <div class="card">    <button type="button" @click="count++">count is {{ count }}</button>  </div></template><style scoped>  .read-the-docs {    color: #888;  }</style>

当然咱们写我的项目不可能这么简略,更多状况下是一个父组件通过数组迭代子组件,就像上面这样,App.vue外面:

<script setup lang="ts">import { ref } from "vue";import HelloWorld from './components/HelloWorld.vue'const fatherItems = ref([  { id: 1, title: "春天", content: "我把裤衩一脱,春姑娘就来了"},  { id: 2, title: "夏天", content: "我把短裤一穿,夏姑娘就来了" }])</script><template>  <HelloWorld v-for="item in fatherItems" :key="item.id" :title="item.title" :content="item.content" /></template><style scoped></style>

Helloword.vue

<script setup>defineProps(["title","content"]);</script><template>  <h4>{{ title }}</h4>  <p>{{ content }}</p></template>

展现成果如下:

好了,到这咱们做我的项目就够用,整太简单就懵逼了。接下来咱们就间接撸我的项目。

五、开始撸我的项目

1.看看市场需求文档

咱们看看需要文档,bula,bula一堆,我看到的是:

第一个问题:我为什么要开发霍兰德职业趣味测试零碎?
1.手工测试速度太慢(客户和本人),开发着玩玩
2.值得我做一天搞定
3.市面上的都须要付费,搞完当前我能够让他人付费

说白了就是
想做
能做
值得做

第二个问题:要实现什么内容?
性能:https://baike.baidu.com/item/...
翻译翻译:
原理:据趣味的不同,人格可分为研究型
第一步:人分六种,(I)、艺术型(A)、社会型(S)、企业型(E)、传统型(C)、事实型(R)六个维度
第二步:十题一测 每种通过10道题测试,最初判断你是哪一种,得出你是什么人格,比方SEC
第三步:对号入座,依据索引找对应工作

因为是我本人用,所以没什么要说的。然而你要是公司开发,咋说呢,活当然要干的丑陋,然而不好开发的尽量开发之前就砍掉,不论你是利诱还是理由,反正尽可能扼杀需要在摇篮之中,如果额杀不了也要记住,需要上的失误,不要到代码实现层去沟通。

你只是大自然的搬运工,把市场需求搬运给计算机,不必带太多脑子。

2.整个产品文档

原理:据趣味的不同,人格可分为研究型
第一步:人分六种,(I)、艺术型(A)、社会型(S)、企业型(E)、传统型(C)、事实型(R)六个维度
第二步:十题一测 每种通过10道题测试,最初判断你是哪一种,得出你是什么人格,比方SEC
第三步:对号入座,依据索引找对应工作

3.我的项目开发文档

接到市场需求和产品需要文档,我的想法:你们给我圆润的走开,翻滚吧牛宝宝。我不想干活。(简称,滚犊子),既然推不掉就想怎么实现:

在应用 Vue.js 来实现霍兰德职业测试的应用程序时,我思考到的:
1.筹备问卷:须要筹备一份问卷,其中蕴含若干个问题,用于测试人的趣味、偏向和人格
特点。(用ant-design-vue,本场上演就我本人,又不想界面丑爆了)
2.创立表单:你须要应用 Vue.js 的模板语法来创立表单,包含问卷中的各个问题和答案选项。
3.解决表单提交:当用户实现填写并提交表单时,你须要应用 Vue.js 的办法来解决表单提
交,并计算出测试后果。(用pinia状态治理,必定会用到跨组件通信,顺便还能够装下13)
4.展现测试后果:最初,你能够应用 Vue.js 的指令来展现测试后果,并向用户提供倡议的职
业列表。(用个vue-chartjs雷达图,用了很屡次很稳固,也是一款装13的利器)

以上纯属脑袋中意淫,咳咳,头脑风暴。而后开始写我的项目开发文档。大家认为我是这么想的:

开发思路:
1.创立 Vue.js 我的项目:应用 构建工具创立 Vue.js 我的项目。
2.筹备测试题目和选项:筹备好测试题目和选项,能够应用数组格局存储。
3.创立组件:创立一个测试组件,用于显示测试题目和选项,并解决用户的抉择。
4.实现组件逻辑:在组件中实现逻辑,包含显示以后测试题目和选项,解决用户的抉择,计
算测试后果等。
5.显示测试后果:应用另一个组件显示测试后果,能够依据测试后果提供倡议的职业。

其实我是这样想的:

1.有没有现成的开源的我的项目,我改吧改吧完事儿
2.有没有网上现成的,让我扒一个改吧改吧,能实现完事儿

然而我是一个脱离了低级趣味的人呢,我决定本人实现一个,毕竟我的头发还容许我再折腾一把。顺着方才的开发思路,要实现这个指标我得有三个步骤:

第一步:先整一个vue表单组件,可能输出数据,能切换上一题下一题
第二步:把输出数据能装一个数组,实现各种计算,提供给雷达图
第三步:通过雷达图展现数据

想明确了,就一把梭上代码。
别的不说我先用router 搭一个 三个页面切换的SPA利用。首先咱们先在src>views下创立三个文件,别离是Home.vue,CareerTest.vue和About.vue

其中Home.vue 内容如下,其余只是文字不同,略。

<template>  <div>{{ msg }}</div></template>  <script lang="ts">import { defineComponent } from "vue";export default defineComponent({  setup() {    // 在 `setup` 办法里申明变量    const msg = "我是Home页";    // 将须要在 `<template />` 里应用的变量 `return` 进来    return {      msg,    };  },});</script>  <style scoped></style>

留神这里我没有用

<script setup>const msg = "我是Home页";</script><template>    <div>{{msg}}</div></template>

这样的语法糖,不写setup是规范的写法,两种都写写,大家就都会用了。至于区别也不必特意记,写着写着也就会了。其实你一比照就晓得,无非就是省了return,还有不必写setup办法,当然还有组件不必注册跟this什么的,这些你不必记,记住:

鲁迅说过,只有我的项目中你用不到又不影响你撸我的项目效率和成果的性能,那就是没有用的性能。

配置路由,src>router(src上面建一个router文件夹,上面都这么简写),上面两个文件 index.vue和routes.vue,为什么这么做,因为这样最适宜理论工作架构清晰

其中 index.ts 是路由的入口文件,如果路由很少,那么能够只保护在这个文件里,但对简单我的项目来说,往往须要配置上二级、三级路由,逻辑和配置都放到一个文件的话,太臃肿了。

所以如果我的项目略微简单一些,能够像下面这个构造一样拆分成两个文件: index.ts 和 routes.ts ,在 routes.ts 里保护路由树的构造,在 index.ts 导入路由树结构并激活路由,同时能够在该文件里配置路由钩子。

如果我的项目更加简单,例如做一个 Admin 后盾,能够依照业务模块,再把 routes 拆分得更细,例如 game.ts / member.ts / order.ts 等业务模块,再对立导入到 index.ts 文件里。这里我用最新的router4 写法,传统的写法,这里会报错,你不必放心为什么我这么写,你照猫画HelloKity就行了。

//router>index.tsimport { createRouter, createWebHistory } from 'vue-router'import type { RouteRecordRaw } from 'vue-router'import routes from './routes';const router = createRouter({  routes,//外围就这个单词而已别的能够都不必管,这个来自于routes.vue})export default router;

别写那些引入vue啦 引入vue-router等写法,那个是过来式的写法了,她就像你的后任,忘了她吧。彬哥敲代码养你。

咱们再看看routes.vue怎么写

import Home from '../views/Home.vue';import CareerTest from '../views/CareerTest.vue';import About from '../views/About.vue';// 有多少路由都能够在这外面配置const routes = [    {        path: '/',        name: 'Home',        component: Home    },    {        path: '/Career-Test',        name: 'CareerTest',        component: CareerTest    },    {        path: '/about',        name: 'About',        component: About    }];export default routes;

咱们搭了三个页面,又配置了路由,总得弄个导航点击能力切换吧,往App.ts外面写,

<script setup lang="ts"></script><template>  <div>    <router-link to="/">首页</router-link>    <router-link to="/career-test">职业测评</router-link>    <router-link to="/about">对于咱们</router-link>    <router-view />  </div></template><style scoped></style>

而后,你要再main.js上搞事件,就game over了。你得告诉整个SPA利用,我要全局应用路由,如下:

import { createApp } from 'vue'import App from './App.vue'// import './assets/main.css'import router from './router';createApp(App).use(router).mount('#app')

页面会报错,因为咱们用了router然而没有装啊!

npm create vite@latest

装的是小纯净版本,什么都本人装,本人开发,省的我的项目臃肿。

咱们先整一个router装上,官网:https://router.vuejs.org/zh/i...

命令:

npm install vue-router@4

最初,切换好用了

总结一下:

1.用components上面默认的HelloWorld.vue,在新建的views上面另存三个页面,Home、About和CareerTest.vue

2.新建文件夹router,上面两个文件一个是index.ts做入口配置,一个是routers.vue专门写门路对应

3.App.vue上面弄三个router-link和一个router-view,三个链接切换,一个显示。

4.main.ts外面import和use router。

5.装置router

大家留神我是从我的项目角度倒着操作的,个别教程是会通知你先装router而后在弄main.ts,然而那样容易让你懵逼。咱们从性能登程,缺什么补什么,你就明确为什么这么做了。比方当初这么丑爆的LowB,你是不是想整个好看的界面?咱们就用ant-design-vue UI库造一下。

1.在四vue基础知识的根底上,先装个ant-design-vue先搭个架子。

npm i --save ant-design-vue@next

2.关上官网复制代码,https://2x.antdv.com/componen... 到App.vue,

<style scoped></style><template>  <a-layout>    <a-layout-header :style="{ position: 'fixed', zIndex: 1, width: '100%' }">      <div class="logo" />      <a-menu        theme="dark"        mode="horizontal"        v-model:selectedKeys="selectedKeys"        :style="{ lineHeight: '64px' }"      >        <router-link to="/"          ><a-menu-item key="1">首页</a-menu-item></router-link        >        <router-link to="/Career-Test"          ><a-menu-item key="2">职业测评</a-menu-item></router-link        >        <router-link to="/About"          ><a-menu-item key="3">对于咱们</a-menu-item></router-link        >      </a-menu>    </a-layout-header>    <a-layout-content :style="{ padding: '0 50px', marginTop: '64px' }">      <a-breadcrumb :style="{ margin: '16px 0' }">        <a-breadcrumb-item>Home</a-breadcrumb-item>        <a-breadcrumb-item>List</a-breadcrumb-item>        <a-breadcrumb-item>App</a-breadcrumb-item>      </a-breadcrumb>      <div :style="{padding: '24px', minHeight: '380px' }">        <router-view />      </div>    </a-layout-content>    <a-layout-footer :style="{ textAlign: 'center' }">      彬哥头发多 ©2022 Created by Leolau    </a-layout-footer>  </a-layout></template><script lang="ts">import { defineComponent, ref } from "vue";export default defineComponent({  setup() {    return {      selectedKeys: ref<string[]>(["2"]),    };  },});</script><style>#components-layout-demo-fixed .logo {  width: 120px;  height: 31px;  background: rgba(255, 255, 255, 0.2);  margin: 16px 24px 16px 0;  float: left;}.site-layout .site-layout-background {  background: #fff;}[data-theme="dark"] .site-layout .site-layout-background {  background: #141414;}</style>

显然页面是乱的因为咱们只是装了antdv没有应用它,怎么搞?关上main.ts,引入组件和css,而后全局use一下。

import { createApp } from 'vue'import App from './App.vue'import router from './router';import Antd from 'ant-design-vue';import 'ant-design-vue/dist/antd.css'createApp(App).use(router).use(Antd).mount('#app')//留神这里我用了两个use,没用.use(Antd,router),还是那句话坑我给你屏蔽了你不须要晓得用就好了

刷新,页面如下就鸟了:

这个就是套Html的活儿,仔细点就行。

总结,

1.其实下面的路由配置写法很多,然而我没讲是因为不想减少复杂度同时很多写法都有坑我给屏蔽了你不须要晓得

2.你是要开发我的项目,不是为了秀技术,知不知道茴香豆的茴字有四种写法不影响你写好文章

到这里,根本的架子打完了,剩下的活无非就是,拉组件,而后写逻辑。接下来咱们先把组件构造搞清楚。

六、整站的组件组织构造

架构解释两点:

1.为什么我要从CareerTest跳到HollandTest?因为一级导航只想作为page页解释阐明和展现,波及业务的跳转到对应的page,这样业务page和组件一体化同时防止了 CareerTest的臃肿与凌乱

2.数据和款式别离放到独自的文件夹,同理。尤其是数据这块拆分解决理论我的项目尤其重要,比方这个我的项目60个选择题,如果都扔到components外面就太臃肿了。也利于将来数据交互与扩大。

啥也不说先撸一个CareerTest的界面,咱们应用antdv的cart组件,有UI库撸代码就是快,间接复制粘贴代码地址:

https://2x.antdv.com/componen... 外面的栅格卡片

<template>  <div class="gutter-example">    <p class="title">人格的六种类型</p>    <p>      霍兰德职业趣味自测(Self-Directed Search)是由美国职业指导专家霍兰德(John      Holland)依据他自己大量的职业咨询教训及其职业类型实践编制的测评工具。霍兰德认为,集体职业趣味个性与职业之间应有一种外在的对应关系。依据趣味的不同,人格可分为研究型(I)、艺术型(A)、社会型(S)、企业型(E)、传统型(C)、事实型(R)六个维度,每个人的性情都是这六个维度的不同水平组合。    </p>    <a-row :gutter="[16, 16]" type="flex" justify="center">      <a-col :span="12">        <router-link to="/holland-test"          ><a-button type="primary" class="testBtn" block size="large"            >进入测试零碎</a-button          >        </router-link>      </a-col>    </a-row>    <div style="padding: 0px 20px 20px 20px">      <a-row :gutter="16">        <a-col :span="8" class="vGap">          <a-card title="事实型(Realistic)" :bordered="false">            <span>独特特色:</span>            <p>              违心应用工具从事操作性工作,入手能力强,做事手脚灵便,动作协调。偏好于具体任务,不善言辞,做事激进,较为虚心。            </p>          </a-card>        </a-col>        <a-col :span="8" class="vGap">          <a-card title="研究型(Investigative)" :bordered="false">            <span>独特特色:</span>            <p>              思想家而非实干家,抽象思维能力强,求知欲强,肯动脑,善思考,不愿入手。喜爱独立的和富裕创造性的工作。            </p>          </a-card>        </a-col>        <a-col :span="8" class="vGap">          <a-card title="艺术型(Artistic)" :bordered="false">            <span>独特特色:</span>            <p>              有创造力,乐于发明新鲜、不同凡响的成绩,渴望体现本人的共性,实现本身的价值。做事理想化,谋求完满,不重理论。            </p>          </a-card>        </a-col>        <a-col :span="8" class="vGap">          <a-card title="社会型(Social)" :bordered="false">            <span>独特特色:</span>            <p>              喜爱与人来往、一直结交新的敌人、善言谈、违心教诲他人。喜爱关怀社会问题、渴望施展本人的社会作用。            </p>          </a-card>        </a-col>        <a-col :span="8" class="vGap">          <a-card title="企业型(Enterprise)" :bordered="false">            <span>独特特色:</span>            <p>              谋求势力、权威和物质财富,具备领导能力。喜爱竞争、敢冒风险、有野心、抱负。            </p>          </a-card>        </a-col>        <a-col :span="8" class="vGap">          <a-card title="传统型(Conventional)" :bordered="false">            <span>独特特色:</span>            <p>              尊重权威和规章制度,喜爱按计划办事,仔细、有条理,习惯承受别人的指挥和领导,本人不谋求领导职务。            </p>          </a-card>        </a-col>      </a-row>    </div>  </div></template><style scoped>.gutter-example :deep(.ant-row > div) {  border: 0;}.gutter-box {  padding: 5px 0;}.title {  text-align: center;  font-size: 24px;  font-weight: bold;}.ant-card-body span {  font-weight: bold;}.testBtn {  margin: 5px 0;  display: block;}.vGap {  margin-top: 20px;}</style>

这里强调两点:

1.你不管怎么套UI,你肯定要写成6个card的反复

<a-col :span="8" class="vGap">    <a-card title="Card title" :bordered="false">        <p>card content</p>    </a-card></a-col>……反复六次

别写成3个一组两头加间距,那样后盾一套数据就乱了,容易跟你打起来。我这里是写死的就不从数据外面取了。

2.我加了一个vGap类,不是说你用UI库,就不本人搞事件了。别被UI库限制住了后果本人不会写改款式了。

到这里大家看到我,整了一个跳转 ,咱们依照架构图来:

<router-link to="/holland-test"><a-button type="primary" class="testBtn" block size="large">进入测试零碎</a-button>

而后咱们创立一个HollandTest.vue的组件,里用到了HollandLib组件views>HollandTest.vue文件如下

<script setup lang="ts">import { Modal } from "ant-design-vue";import { defineComponent, ref } from "vue";import { onMounted } from "vue";import { useRouter } from "vue-router";import HollandLib from "../components/HollandLib.vue";const router = useRouter();onMounted(() => {  Modal.confirm({    title: () => "舒适提醒",    okText: () => "开始吧>>",    cancelText: "我再想想……",    content:      () => `测评选项没有对错之分,如果您想对本人有一个实在的认知,请依据本人的理论状况抉择是或否√         本测试题共60题,测试工夫为20分钟。         当初,请找到一个宁静的场合...`,    onCancel() {      router.push({ path: "/Career-Test" });    },  });});</script><template>  <HollandLib /></template>

这里我用了一个模态确认框,https://2x.antdv.com/componen... 用的是自定义按钮文字的模态框

重点是这句,

const router = useRouter();//这是新写法 相当于原来的$routerrouter.push({ path: "/Career-Test" });

点击勾销按钮跳转。另外,定义组件用setup语法糖 十分的嗨皮,你不必return,你就放心大胆的定义各种变量和应用各种组件,剩下的交给setup.

敲黑板划重点要写外围构造了。

七、外围测试性能实现

<template>  <div>    <a-row type="flex" justify="center" align="middle" class="rowContainer">      <a-col :span="4" class="previous">        <div>          <a-button>上一题</a-button>        </div>      </a-col>      <a-col :span="8" class="process">        <div>          <a-progress type="line" :percent="75" :format="() => `12/60`" />        </div>      </a-col>      <a-col :span="4"><div class="timeC">04:33</div></a-col>    </a-row>    <a-row      :gutter="[16, 16]"      type="flex"      justify="center"      align="middle"      class="rowContainer"    >      <a-col class="gutter-row" :span="16">        <div class="gutter-box">          <a-card title="1.我喜爱把一件事件做完后再做另一件事。">            <a-button              size="large"              block              class="yesBtn"              @click="nextQuestion('是')"              >是</a-button            >            <a-button size="large" block @click="nextQuestion('否')"              >否</a-button            >          </a-card>        </div>      </a-col>    </a-row>  </div></template><script setup></script><style scoped>.gutter-box {  margin-top: 10px;}.ant-card {  padding: 30px;}.previous {  text-align: left;}.process {  min-height: 35px;}.timeC {  text-align: right;}.yesBtn {  margin: 10px 0;}</style>

对于怎么套页面我就不多墨迹,你看就明确了,咱们核心在性能上。

1.测评页套数据

将数据处理完而后套到card上,data>QuestionsData.ts

当初QuestionsData.ts是这样的:

export const quesStr = `1、我喜爱把一件事件做完后再做另一件事。                           2、在工作中我喜爱单独策划,不愿受他人干预。                        ……`;

这里留神市场给你的是word文档,你须要记事本透一下格局,第二千万别本人一个个写数组,容易把本人写死。也别要求需求方依照你的格局给你数据,那是你的事件跟人家没有半毛钱关系,过分要求他人做不该人家干的事儿,容易被怼死或者打死。

还有就是能不必数据库就别搞太简单,一是为了响应速度,第二是为了开发速度够用就好。

当下咱们只有解决成

['1、我喜爱把一件事件做完后再做另一件事。', '2、在工作中我喜爱单独策划,不愿受他人干预'。]

的模式,留神每一个问题前面有不定数量空格须要解决

代码也很简略一句话:

let questArrr = quesStr.split('\n').map(s => s.trim()).filter(s => s);
// 定义一个响应式数组questions 来提供卡片内容let  questions = reactive(questArrr);

而后在模板外面套questions[0]就能够了,代码如下:

<template>  <div>    <a-row type="flex" justify="center" align="middle" class="rowContainer">      <a-col :span="4" class="previous">        <div>          <a-button>上一题</a-button>        </div>      </a-col>      <a-col :span="8" class="process">        <div>          <a-progress type="line" :percent="75" :format="() => `12/60`" />        </div>      </a-col>      <a-col :span="4"><div class="timeC">04:33</div></a-col>    </a-row>    <a-row      :gutter="[16, 16]"      type="flex"      justify="center"      align="middle"      class="rowContainer"    >      <a-col class="gutter-row" :span="16">        <div class="gutter-box">            <a-card :title="questions[0]">              <a-button                size="large"                block                class="yesBtn"                @click="nextQuestion('是')"                >是</a-button              >              <a-button size="large" block @click="nextQuestion('否')"                >否</a-button              >            </a-card>        </div>      </a-col>    </a-row>  </div></template><script setup>import { quesStr } from "../data/QuestionsData";import { ref, reactive } from "vue";let questArrr = quesStr  .split("\n")  .map((s) => s.trim())  .filter((s) => s);// 定义一个响应式数组questions 来提供卡片内容let questions = reactive(questArrr);</script><style scoped>.gutter-box {  margin-top: 10px;}.ant-card {  padding: 30px;}.previous {  text-align: left;}.process {  min-height: 35px;}.timeC {  text-align: right;}.yesBtn {  margin: 10px 0;}</style>

接下来要实现上一题下一题,还有把你抉择的后果用一个resArr数组接管,为计算最终后果给雷达图做筹备。代码如下:

<template>  <div>    <a-row type="flex" justify="center" align="middle" class="rowContainer">      <a-col :span="4" class="previous">        <div>          <a-button @click="previousQuestion">上一题</a-button>        </div>      </a-col>      <a-col :span="8" class="process">        <div>          <a-progress type="line" :percent="75" :format="() => `12/60`" />        </div>      </a-col>      <a-col :span="4"><div class="timeC">04:33</div></a-col>    </a-row>    <a-row      :gutter="[16, 16]"      type="flex"      justify="center"      align="middle"      class="rowContainer"    >      <a-col class="gutter-row" :span="16">        <div class="gutter-box">          {{ resArr }}          <a-card :title="questions[curIndex]">            <a-button              size="large"              block              class="yesBtn"              @click="nextQuestion('是')"              >是</a-button            >            <a-button size="large" block @click="nextQuestion('否')"              >否</a-button            >          </a-card>        </div>      </a-col>    </a-row>  </div></template><script setup lang="ts">import { quesStr } from "../data/QuestionsData";import { ref, reactive } from "vue";let questArrr = quesStr  .split("\n")  .map((s) => s.trim())  .filter((s) => s);// 定义一个响应式数组questions 来提供卡片内容let questions = reactive(questArrr);// 点击任意一个选项就是下一题// 定义一个题号indexlet curIndex = ref(0);// 定义一个抉择后果的数组const resArr = ref([] as any[]);function nextQuestion(answer: any) {  resArr.value.splice(curIndex.value, 1, answer);  curIndex.value++;  if (curIndex.value == questions.length) {    curIndex.value = questions.length - 1;  }}function previousQuestion() {  curIndex.value--;  if (curIndex.value == -1) {    alert("第一题");    curIndex.value = 0;  }}</script><style scoped>.gutter-box {  margin-top: 10px;}.ant-card {  padding: 30px;}.previous {  text-align: left;}.process {  min-height: 35px;}.timeC {  text-align: right;}.yesBtn {  margin: 10px 0;}</style>

成果如下:

解决一下上一题,第一题就没必要显示上一题了,

<a-button v-if="curIndex > 0" @click="previousQuestion">上一题</a-button>

最初一题,提醒是否提交的模态框

import { Modal } from "ant-design-vue";function showCompop() {  Modal.confirm({    title: () => "舒适提醒",    okText: () => "提交",    cancelText: "勾销",    content: () => `祝贺你所有问题曾经实现,确认提交查看后果吗?`,    onOk() {      router.push({        path: "/result",      });    },    onCancel() {      router.push({ path: "/holland-test" });    },  });}

路由跳转还是老办法,就不赘述。另存一个Result.vue,在routes.ts外面加配置具体如下:

import Home from '../views/Home.vue';import CareerTest from '../views/CareerTest.vue';import About from '../views/About.vue';import HollandTest from '../views/HollandTest.vue';// 有多少路由都能够在这外面配置const routes = [    {        path: '/',        name: 'Home',        component: Home    },    {        path: '/Career-Test',        name: 'CareerTest',        component: CareerTest    },    {        path: '/About',        name: 'About',        component: About    },    {        path: '/holland-test',        name: '霍兰德测试',        component: HollandTest    },    {        path: '/result',        name: '测试后果',        component: Result    },];export default routes;

1.后果页套数据

我须要整一个雷达图,而后一段文字说一下测评后果。

npm i vue-chartjs chart.js

而后咱们套一个界面,仍然用antdv

<template>  <div style="background: #fff; padding: 14px 24px; min-height: 380px">    <a-descriptions      title="职业趣味报告:"      bordered      size="middle"      layout="vertical"    >      <a-descriptions-item        label="您的职业趣味代码是"        :labelStyle="{          textAlign: 'center',          display: 'block',          fontWeight: 'bold',        }"        :span="1"        :contentStyle="{ textAlign: 'center', display: 'block' }"      >        <div class="codeBox">          <div class="code1">S</div>          <div class="code2">E</div>          <div class="code3">C</div>          <p class="code">SEC</p>        </div>      </a-descriptions-item>      <a-descriptions-item        label="您的各类型占比"        :labelStyle="{          textAlign: 'center',          display: 'block',          fontWeight: 'bold',        }"        :contentStyle="{ textAlign: 'center', display: 'block' }"        :span="2"      >        <div style="max-width: 350px; margin: 0 auto">          <Radar id="my-chart-id" :data="chartData" />        </div>      </a-descriptions-item>      <a-descriptions-item label="CAS型适宜的职业" :span="3">        <a-badge          text="厨师、林务员、跳水员、潜水员、染色员、电器修理、眼镜制作、电工、纺织机器装配工、服务员、装玻璃工人"        />      </a-descriptions-item>      <a-descriptions-item label="状态" :span="1">事实型R</a-descriptions-item>      <a-descriptions-item label="人格偏向" :span="1"        >喜爱与人来往、一直结交新的敌人、善言谈、违心教诲他人。关怀社会问题、渴望施展本人的社会作用。寻求宽泛的人际关系,比拟看重社会任务和社会道德</a-descriptions-item      >      <a-descriptions-item label="典型职业" :span="3"        ><div class="codeBox">工人 农民 土木工程师</div></a-descriptions-item      >      <a-descriptions-item label="状态" :span="1">事实型R</a-descriptions-item>      <a-descriptions-item label="人格偏向" :span="1"        >具备抗拒、坦白、虚心、天然、刚毅、理论、有礼、害羞、持重、勤俭的特色,体现为        1、青睐实用性的职业或情境,以从事所爱好的流动,防止社会性的职业或情境        2、用具体理论的能力解决工作及其他方面的问题,较不足人际关系方面的能力。        3、器重具体的事物,如金钱,势力、位置等。</a-descriptions-item      >      <a-descriptions-item label="典型职业" :span="3"        ><div class="codeBox">喜爱要求与人打交道的工作,可能一直结交新的敌人,从事提供信息、启迪、帮忙、培训、开发或医治等事务,并具备相应能力。如: 教育工作者(老师、教育行政人员),社会工作者(征询人员、公关人员)。</div></a-descriptions-item      >      <a-descriptions-item label="状态" :span="1">事实型R</a-descriptions-item>      <a-descriptions-item label="人格偏向" :span="1"        >具备抗拒、坦白、虚心、天然、刚毅、理论、有礼、害羞、持重、勤俭的特色,体现为        1、青睐实用性的职业或情境,以从事所爱好的流动,防止社会性的职业或情境        2、用具体理论的能力解决工作及其他方面的问题,较不足人际关系方面的能力。        3、器重具体的事物,如金钱,势力、位置等。</a-descriptions-item      >      <a-descriptions-item label="典型职业" :span="3"        ><div class="codeBox">工人 农民 土木工程师</div></a-descriptions-item      >      <a-descriptions-item        label="舒适提醒"        :labelStyle="{          textAlign: 'left',          display: 'block',          fontWeight: 'bold',        }"        :contentStyle="{ textAlign: 'left' }"        :span="3"      >        <p>          除了关注得分最高的前三项CAS,找到适宜本人倒退的职业畛域外,还心愿您也特地关注一下本人最不适宜倒退的职业畛域,即得分最低的那个类型所指向的畛域,防止长时间地在不适宜本人倒退的职业畛域摸索。        </p>      </a-descriptions-item>    </a-descriptions>  </div></template><script setup lang="ts">import { Radar } from "vue-chartjs";import { ref, reactive } from "vue";import {  Chart as ChartJS,  RadialLinearScale,  PointElement,  LineElement,  Filler,  Tooltip,  Legend,} from "chart.js";ChartJS.register(  RadialLinearScale,  PointElement,  LineElement,  Filler,  Tooltip,  Legend);let chartData = reactive({  labels: [    "研究型(I)",    "艺术型(A)",    "社会型(S)",    "企业型(E)",    "传统型(C)",    "事实型(R)",  ],  datasets: [    {      label: "您的各类型占比",      backgroundColor: "rgba(255,99,132,0.2)",      borderColor: "rgba(255,99,132,1)",      pointBackgroundColor: "rgba(255,99,132,1)",      pointBorderColor: "#fff",      pointHoverBackgroundColor: "#fff",      pointHoverBorderColor: "rgba(255,99,132,1)",      data: [0, 0, 0, 0, 0, 0],    },  ],});</script>  <style scoped>.codeBox {  position: relative;  width: 300px;}.code1,.code2,.code3 {  width: 100px;  height: 100px;  position: absolute;  border-radius: 50px;  line-height: 100px;  font-size: 40px;  color: #fff;}.code {  position: absolute;  color: black;  z-index: 100;  left: 125px;  top: 80px;  font-size: 30px;}.code1 {  background: green;  left: 100px;  top: -110px;}.code2 {  background: orange;  left: 50px;  top: -25px;}.code3 {  background: blue;  left: 150px;  top: -25px;}</style>

报告局部用了形容列表https://2x.antdv.com/componen...

八、数据对接

当初咱们测试选项页能把测试题后果拿到了,而后后果报告页动态页面也进去了,当初最要害的点就来了,

如何将测试后果数组解决成 报告页的数据展现进去。

这里分三步:

1.把数据传过来

2.把数据处理成用来展现的

3.套数据展现

九、状态治理与pinia

解决第一步数据的时候,咱们须要把数据components>从HollandLib.vue 的resArr传到views>Result.vue外面。

咱们当然能够用通过向父组件传递数据而后传递给兄弟组件,然而大量数据的时候就很乱了,所以咱们应用的Vue 状态治理库pinia。

别墨迹 先装上再说,官网https://pinia.vuejs.org/zh/ge...

npm install pinia

关上main.ts跟应用router和ant-design-vue一样。

import { createApp } from 'vue'import App from './App.vue'import router from './router';import Antd from 'ant-design-vue';import 'ant-design-vue/dist/antd.css'import { createPinia } from 'pinia'const pinia = createPinia()createApp(App).use(router).use(Antd).use(pinia).mount('#app')//留神这里我用了多个个use,没用.use(Antd,router),还是那句话坑我给你屏蔽了你不须要晓得用就好了

装上了就开整,你不要一看到状态治理就感觉简单和高大上。其实简略的令人发指。我做个类比你就秒懂。

你家水果能够放厨房柜子里,也能够放客厅冰箱里,你家里好几个人都吃水果,洗衣局部顺手放在水池子边上或者其它两个地位,下一个人吃,就得很麻烦的找来找去。另外你一会拿个香蕉一会拿个苹果忒麻烦了吧。怎么办呢?

你家里就规定了,水果都放在冰箱的保鲜区,用水果篮子洗和放水果吃完都要放回去。

咱们在src>stores>userCheckStore.ts,内容如下:

import { defineStore } from 'pinia'import { reactive } from 'vue'//defineStore以冰箱 userCheckStoreexport const userCheckStore = defineStore('QueuserChecktionRes', {    // sate就是保鲜区,resArr就是苹果、香蕉各种你要拿走的水果(数据),你不必管为啥我用reactive,你包一层不错,有时候你不包可能就错,所以不必记照着搞就行了    state: () => {        return {            resArr: reactive([])        }    },})

而后咱们在各个组件用resArr用它,怎么用HollandLib.vue下

import { userCheckStore } from "../stores/userCheckStore";import { storeToRefs } from "pinia";const store = userCheckStore();const {resArr} = storeToRefs(store);

userCheckStore 就是咱们冰箱,store就是保鲜区,resArr是苹果,是不是很简略?storeToRefs是为了响应式数据的,你也不必管是啥,你就包上就完事儿了。反正作为老手,我讲storeToRefs包不包的区别意义也不大。下面那段话你想在哪个组件用就在那个组件这么写就行。而后咱们说下怎么把香蕉放回冰箱,也就是批改数据。

store.resArr.splice(curIndex.value, 1, answer);

就跟批改对象没区别留神后面带上store.resArr(改的是保鲜区里的香蕉,你不能改错了,这样所有组件都能接管到你的批改。

store.name = 'xxxx'这样的模式,因为我玩的是数组,所以splice了一下,情理一样。这样做目标是我把每道题的选项上一题的时候能批改成点击的那个值,最初是[][]['是','否','是'],这个数组views>Result.vue能拿到,就能够计算和嵌套数据了,计算数据这个跟ts和vue3无关,是原生js根底的货色。这里去强调一点要纯熟应用数组、对象和正则解决数据,而嵌套数据就跟咱们后面七、外围测试性能实现嵌套问题卡没区别了。

最初是打包上线,

npm run build

把dist文件家里的内容扔到服务器根目录下就能够了。

十、对于开发效率

最初我说一下如何进步开发效率,用框架必定是快,然而前提是原生js的语法和算法要很理解,还有逻辑清晰。

比方给大家举个例子:resultData.ts这个数据是市场部们给的,

export let sixTypeStr = `1、社会型:(S)独特特色:喜爱与人来往、一直结交新的敌人、善言谈、违心教诲他人。关怀社会问题、渴望施展本人的社会作用。寻求宽泛的人际关系,比拟看重社会任务和社会道德典型职业:喜爱要求与人打交道的工作,可能一直结交新的敌人,从事提供信息、启迪、帮忙、培训、开发或医治等事务,并具备相应能力。如: 教育工作者(老师、教育行政人员),社会工作者(征询人员、公关人员)。2、企业型:(E)独特特色:谋求势力、权威和物质财富,具备领导能力。喜爱竞争、敢冒风险、有野心、抱负。为人求实,习惯以利益得失,权力、位置、金钱等来掂量做事的价值,做事有较强的目的性。典型职业:喜爱要求具备经营、治理、劝服、监督和领导能力,以实现机构、政治、社会及经济指标的工作,并具备相应的能力。如项目经理、销售人员,营销管理人员、政府官员、企业领导、法官、律师。3、惯例型:(C)独特特点:尊重权威和规章制度,喜爱按计划办事,仔细、有条理,习惯承受别人的指挥和领导,本人不谋求领导职务。喜爱关注理论和细节状况,通常较为审慎和激进,不足创造性,不喜爱冒险和竞争,富裕自我牺牲精力。典型职业:喜爱要求留神细节、精确度、有零碎有条理,具备记录、归档、据特定要求或程序组织数据和文字信息的职业,并具备相应能力。如:秘书、办公室人员、记事员、会计、行政助理、图书馆管理员、出纳员、打字员、投资分析员。4、理论型:(R)独特特点:违心应用工具从事操作性工作,入手能力强,做事手脚灵便,动作协调。偏好于具体任务,不善言辞,做事激进,较为虚心。不足社交能力,通常喜爱独立做事。典型职业:喜爱应用工具、机器,须要根本操作技能的工作。对要求具备机械方面能力、膂力或从事与物件、机器、工具、运动器材、动物、动物相干的职业有趣味,并具备相应能力。如:技术性职业(计算机硬件人员、摄影师、制图员、机械装配工),技能性职业(木匠、厨师、技工、修理工、农民、个别劳动)。5、调研型:(I)独特特点:思想家而非实干家,抽象思维能力强,求知欲强,肯动脑,善思考,不愿入手。喜爱独立的和富裕创造性的工作。常识渊博,有学识能力,不长于领导别人。思考问题感性,做事喜爱准确,喜爱逻辑剖析和推理,一直探讨未知的畛域。典型职业:喜爱智力的、形象的、剖析的、独立的定向工作,要求具备智力或剖析能力,并将其用于察看、估测、掂量、造成实践、最终解决问题的工作,并具备相应的能力。 如科学研究人员、老师、工程师、电脑编程人员、医生、系统分析员。6、艺术型:(A)独特特点:有创造力,乐于发明新鲜、不同凡响的成绩,渴望体现本人的共性,实现本身的价值。做事理想化,谋求完满,不重理论。具备肯定的艺术能力和共性。长于表白、念旧、心态较为简单。典型职业:喜爱的工作要求具备艺术修养、创造力、表达能力和直觉,并将其用于语言、行为、声音、色彩和模式的审美、考虑和感触,具备相应的能力。不长于事务性工作。如艺术方面(演员、导演、艺术设计师、雕刻家、建筑师、摄影家、广告制作人),音乐方面(歌唱家、作曲家、乐队指挥),文学方面(小说家、诗人、剧作家)。`;

拿到当前多余空格不必管,你用编辑器格式化就全没了,然而空格你必定不能手动删除吧?很多人想都不想间接split

let resultArr = resultStr.split("\n");

也的确该这么做,然而问题就来了,

这就要求你纯熟的应用数组办法,正则了和字符串操作方法。

还有再解决sixTypeStr

export let sixTypeStr = `1、社会型:(S)独特特色:喜爱与人来往、一直结交新的敌人、善言谈、违心教诲他人。关怀社会问题、渴望施展本人的社会作用。寻求宽泛的人际关系,比拟看重社会任务和社会道德典型职业:喜爱要求与人打交道的工作,可能一直结交新的敌人,从事提供信息、启迪、帮忙、培训、开发或医治等事务,并具备相应能力。如: 教育工作者(老师、教育行政人员),社会工作者(征询人员、公关人员)。2、企业型:(E)独特特色:谋求势力、权威和物质财富,具备领导能力。喜爱竞争、敢冒风险、有野心、抱负。为人求实,习惯以利益得失,权力、位置、金钱等来掂量做事的价值,做事有较强的目的性。典型职业:喜爱要求具备经营、治理、劝服、监督和领导能力,以实现机构、政治、社会及经济指标的工作,并具备相应的能力。如项目经理、销售人员,营销管理人员、政府官员、企业领导、法官、律师。3、惯例型:(C)

时候,你split完会是这样,

你其实要的是 三个一组,蕴含社会型,独特特色,还有职业。个别人又会去循环解决数组再扔到一个对象里,这样做当然更好更灵便,然而你解决的时候又会波及到又要隔3个循环,还有思考数组中对象类型如何对应,比方S要对应前三项,E要对应456我的项目,这样又要做一个数组放六种类型……

其实到这里曾经能够用了,你只有找到蕴含字母S的,而后下标+1 +2你要的数据就能够嵌套了,一句话搞定。

const index = sixTypeArr.findIndex((item) => item.includes(searchChar[i]));

应用了数组的findIndex和includes办法,所以大家能够看到,纯熟的应用数组操作可能极大的晋升开发效率与升高代码量。

十一,与需求方和产品沟通与扯皮

很多程序员要跟产品、测试和需求方关系十分缓和,吵架。很大部分起因是没有搞明确程序员是干什么的。

程序员是把需求方的需要准确无误的通知电脑高效的执行,这是最低需要。

比方我测试后果是ECA,然而并没有ECA类型的测试后果与职业匹配对照后果,这个问题第一做好揭示需求方,第二你解决好这种状况的提醒即可。至于你通过威逼利诱砍掉你不想开发的性能那是进阶了,至多你得做到后面这点,你才是一个合格的程序员。

十二,写在最初

1.程序员不要自嗨,够用就好。

因为这个工具我本人和公司用,另外想通过这个我的项目让大家迅速的可能用vue3开发我的项目,而不是卡住大家,所以我省略了很多的细节,比方路由我理论开发的时候应用v-for循环的,然而讲的时候,我就间接写死了,因为不想减少大家了解复杂度。

2.不要手里有锤子,眼里都是钉子

程序是为了实现性能和业务服务,而程序永远不是惟一的实现形式(然而往往是高效的形式),能不必程序就能高效解决,那就不要写程序。

最初奉上效果图:

最初网站展现成果:https://leolau2012.github.io/
对于这个教程Follow的过程中,有人任何问题和卡顿,欢送留言或私信。