基于vue3+ts5+vue-router4+pinia2的PC端项目搭建教程

导语:在日常开发中,有时候会在项目中引入 ts 来解决一些 js 的问题,下面就简单介绍一下如何使用 vue3+ts+router+pinia 来搭建一个项目。

目录

  • 简介
  • 创建
  • 安装
  • 配置
  • 实战

简介

vue3 目前是常用的 vue 版本,提供了组合式 API 以及一些新的功能和特性;ts 这种类型编程语言可以在编译时通过静态分析检测出很多常见错误,减少了生产环境中的运行时错误,改善了开发体验和效率;vue-router 也更新到了 4 版本,pinia 则是最新退出的替代 vuex 的一个 vue 官方状态管理库;下面就结合以上提到的技术栈组合来简单创建一个项目。

创建

下面打开 cmd 或其他命令行,输入以下命令创建一个 vite 项目。

这里我选择使用pnpm来创建。

  • 创建 vite 项目
shell 复制代码
pnpm create vite
  • 填写项目信息

包括项目名称、选择框架、js 语言等。

shell 复制代码
√ Project name: ... tslx
√ Select a framework: >> Vue
√ Select a variant: >> TypeScript
  • 创建成功

根据以下步骤来安装基本的依赖和运行项目。

shell 复制代码
cd tslx
pnpm install
pnpm run dev

安装

创建好项目后,接下来安装一些必备的依赖包。

必备依赖包

  • vue-router

这个是必须的,路由管理。

shell 复制代码
pnpm i vue-router -S
  • sass

这个是必须的,主要是使用 sass 写项目样式表。

shell 复制代码
pnpm i sass -S
  • axios

这个是必须的,主要是 http 请求数据。

shell 复制代码
pnpm i axios -S

可选依赖包

  • pinia

这个是可选的,主要是提供状态管理。

shell 复制代码
pnpm i pinia -D
  • pinia-plugin-persist

这个是可选的,主要是提供状态管理的持久化存储。

shell 复制代码
pnpm i pinia-plugin-persist -D
  • unplugin-auto-import

这个是可选的,自动导入依赖插件。

shell 复制代码
pnpm i unplugin-auto-import -D
  • normalize.css

这个是可选的,主要是可定制化 CSS 样式。

shell 复制代码
pnpm i normalize.css -D
  • mitt

这个是可选的,一个简洁、灵活的 JavaScript 事件订阅和发布库。

shell 复制代码
pnpm i mitt -D
  • @volar-plugins/vetur

这是可选的,一个支持 vue3 语法的插件。

shell 复制代码
pnpm i @volar-plugins/vetur -D
  • @vitejs/plugin-vue

这个是可选的,支持基于 Vite 构建的 Vue 项目。

shell 复制代码
pnpm i @vitejs/plugin-vue -D
  • @types/node

这个是可选的,主要是解决模块的声明问题。

shell 复制代码
pnpm i @types/node -D
  • path

这个是可选的,主要是 node 的 path 模块。

shell 复制代码
pnpm i path -D
  • rollup-plugin-visualizer

这个是可选的,主要是应用模块统计。

shell 复制代码
pnpm i rollup-plugin-visualizer -D
  • vue-i18n

这个是可选的,主要是多语言配置。

shell 复制代码
pnpm i vue-i18n -D

好了,以上就是日常开发项目常用的一些依赖包。

配置

下面配置一下vite.config.ts文件和main.ts文件以及其他需要配置的文件。

vite 配置文件

打开vite.config.ts文件,主要是添加插件配置,文件路径别名配置,css 全局样式配置,服务端端口及代理配置。

引入依赖

ts 复制代码
import AutoImport from "unplugin-auto-import/vite";
import vetur from "@volar-plugins/vetur";
import path from "path";
import { visualizer } from "rollup-plugin-visualizer";

插件配置

plugins中添加以下配置。

ts 复制代码
//...
{
  plugins: [
    vue({
      template: {
        compilerOptions: {
          isCustomElement: () => {
            return false;
          },
        },
      },
    }),
    vetur,
    AutoImport({
      dts: "src/auto-import.d.ts",
      include: [
        /\.[tj]sx?$/, // .ts, .tsx, .js, .jsx
        /\.vue$/,
        /\.vue\?vue/, // .vue
      ],
      imports: [
        "vue",
        "vue-router",
        {
          from: "vue-router",
          imports: ["RouteLocationRaw"],
          type: true,
        },
      ],
    }),
    visualizer({
      emitFile: false,
      filename: "stats.html",
      open: true,
    }),
  ];
}
// ...

文件路径别名配置

src文件夹下面新建三个文件,可以选择去掉assets文件夹。

  • types:主要是存放 ts 声明等内容;
  • styles:主要是存放全局样式文件;
  • apis:主要是存放全局方法文件;

下面是配置方法。

ts 复制代码
//...
{
  resolve: {
    alias: {
      "@": path.resolve(__dirname, "./src"),
      "@c": path.resolve(__dirname, "./src/components"),
      "@t": path.resolve(__dirname, "./src/types"),
      "@s": path.resolve(__dirname, "./src/styles"),
      "@a": path.resolve(__dirname, "./src/apis"),
    },
  }
}
//...

css 全局样式配置

下面设置 css 全局样式配置,在刚刚创建的styles文件夹下面创建一个global.scss的文件。

ts 复制代码
// ...
{
  css: {
    preprocessorOptions: {
      scss: {
        additionalData: '@import "./src/styles/global.scss";',
        javascriptEnabled: true,
      },
    },
  },
}
//...

服务端端口及代理配置

最后就是一个服务端的配置,包括端口,自动打开网页,跨域接口设置。

ts 复制代码
//...
{
  server: {
    host: "0.0.0.0",
    port: 6060,
    open: true,
    proxy: {
      "/api": {
        autoRewrite: true,
        target: "http://127.0.0.1:9999",
        changeOrigin: true,
        ws: true,
      },
    },
  },
}
//...

配置好以后,重启一下服务就生效了。

配置 ts

  • 添加paths路径

tsconfig.json中新增一个paths属性并配置如下。

json 复制代码
{
  "paths": {
    "@/*": ["src/*"],
    "@c/*": ["src/components/*"],
    "@t/*": ["src/types/*"],
    "@s/*": ["src/styles/*"],
    "@a/*": ["src/apis/*"]
  }
}
  • 完整配置

完整的tsconfig.json内容:

json 复制代码
{
  "compilerOptions": {
    "target": "ESNext",
    "useDefineForClassFields": true,
    "module": "ESNext",
    "lib": ["ESNext", "DOM", "DOM.Iterable"],
    "skipLibCheck": true,

    /* Bundler mode */
    "moduleResolution": "Node",
    "allowImportingTsExtensions": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "preserve",

    /* Linting */
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noFallthroughCasesInSwitch": true
  },
  "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
  "references": [{ "path": "./tsconfig.node.json" }],
  "paths": {
    "@/*": ["src/*"],
    "@c/*": ["src/components/*"],
    "@t/*": ["src/types/*"],
    "@s/*": ["src/styles/*"],
    "@a/*": ["src/apis/*"]
  }
}

完整的tsconfig.node.json的内容:

json 复制代码
{
  "compilerOptions": {
    "composite": true,
    "skipLibCheck": true,
    "module": "ESNext",
    "moduleResolution": "Node",
    "allowSyntheticDefaultImports": true
  },
  "include": ["vite.config.ts"]
}

配置 main.ts

  • 整理main.ts文件

修改为以下内容,这样方便后面挂载全局组件,方法和插件等内容。

ts 复制代码
import { createApp } from "vue";
import App from "./App.vue";

const app = createApp(App);

app.mount("#app");
  • vue 组件声明

打开main.ts会看到找不到模块"./App.vue"或其相应的类型声明 的报错,下面就在src下面新建一个 vue 声明文件global.d.ts

ts 复制代码
// ./src/global.d.ts
declare module "*.vue" {
  import type { DefineComponent } from "vue";
  const vueComponent: DefineComponent<{}, {}, any>;
  export default vueComponent;
}

这个可以让 ts 识别 vue 组件类型声明。

配置 vue-router

下面简单的配置一个路由文件,在src下面新建一个router文件夹,并创建一个index.ts文件。

ts 复制代码
// ./src/router/index.ts

// 导入依赖
import { createRouter, createWebHistory, RouteRecordRaw } from "vue-router";

// 配置routes
const routes: Array<RouteRecordRaw> = [
  {
    path: "/",
    name: "Home",
    component: () => import("@c/home.vue"),
    children: [],
  },
  {
    path: "/404",
    name: "NotFound",
    component: () => import("@c/404.vue"),
    meta: {
      title: "404",
      auth: false,
    },
  },
  {
    path: "/:pathMatch(.*)",
    redirect: "/404",
  },
];

// 配置router
const router = createRouter({
  history: createWebHistory(),
  routes,
  scrollBehavior(to, from) {
    console.log(to, from);
    return {
      left: 0,
      top: 0,
    };
  },
});

// 配置钩子
router.beforeEach((to, from, next) => {
  console.log(to, from);
  // ...
  next();
});

router.afterEach((to, from) => {
  console.log(to, from);
  // window.scrollTo(0, 0);
});

// 导出路由
export default router;
  • 导入main.ts文件。
ts 复制代码
//...
import router from "./router";
// ...
app.use(router);
//...

配置 pinia

src下面新建一个store的文件夹,里面新建一个index.tstypes.ts以及user.ts的文件。

  • index.ts文件

这个文件主要是放置基础的配置,包括插件,持久化存储。

ts 复制代码
import { createPinia } from "pinia";
import piniaPluginPerisit from "pinia-plugin-persist";

// 全局设置
export const pinia = createPinia();
pinia.use(piniaPluginPerisit);

export default pinia;

在配置的时候,pinia-plugin-persist插件可能会报错,原因是找不到模块声明。

可以采取以下方法解决:

  1. 升级到最新版试试看;
  2. types目录下新建一个pinia-plugin-persist.d.ts声明文件。

内容为:

ts 复制代码
declare module "pinia-plugin-persist";
  • types.ts文件

这个文件主要是放置类型变量。

ts 复制代码
const enum NAMES {
  user = "USER",
}

export default NAMES;

enum可能会报错,Parsing error: The keyword 'enum' is reserved, enum 是 Javascript 为未来特性保留的关键字,我们不应该使用它,属于eslint检查错误。

可以采取以下方法解决:

安装三个插件eslint-plugin-vue@typescript-eslint/parser@typescript-eslint/eslint-plugin;

shell 复制代码
pnpm i eslint-plugin-vue @typescript-eslint/parser @typescript-eslint/eslint-plugin -D

修改项目根目录下的.eslintrc.json配置文件;

json 复制代码
{
  "env": {
    "browser": true,
    "es2021": true,
    "node": true,
    "vue/setup-compiler-macros": true
  },
  "extends": [
    "plugin:vue/vue3-essential",
    "eslint:recommended",
    "prettier",
    "plugin:@typescript-eslint/recommended"
  ],
  "parser": "vue-eslint-parser",
  "parserOptions": {
    "ecmaVersion": "latest",
    "sourceType": "module",
    "parser": "@typescript-eslint/parser"
  },
  "plugins": ["vue", "prettier"],
  "rules": {
    "semi": ["warn", "never"]
  },
  "settings": {}
}

重启一下项目就可以了。

  • user.ts文件

这个文件主要是用户的一些状态信息。

ts 复制代码
import { defineStore } from "pinia";
import NAMES from "./types";
import { User } from "../types/interface";

// 用户
const user = defineStore(NAMES.user, {
  state: () => {
    return {
      userInfo: {
        id: 1,
        name: "mark",
      },
    };
  },
  getters: {
    getUserInfo(state) {
      return state.userInfo;
    },
  },
  actions: {
    saveUser(user: User) {
      this.userInfo = user;
    },
  },
});

export default user;

上面简单做了个interface的接口定义。

ts 复制代码
// ./src/types/interface.ts
// 用户信息
export interface User {
  id: number;
  name: string;
}
  • 导入main.ts文件。
ts 复制代码
//...
import pinia from "./store";
// ...
app.use(pinia);
//...

配置 vue-i18n

src文件夹下面新建locale文件夹,用了存放vue-i18n配置信息。

包括zhCn.tszhHk.tsen.tslang.tsindex.ts等文件。

语言内容

  • 中文简体 zhCn
ts 复制代码
const zhCn = {
  home: "首页",
  index: "主页",
  list: "列表",
  info: "信息",
  welcome: "欢迎光临",
};

export default zhCn;
  • 中文繁体 zhHk
ts 复制代码
const zhHk = {
  home: "首頁",
  index: "主頁",
  list: "清單",
  info: "資訊",
  welcome: "歡迎光臨",
};

export default zhHk;
  • 英文 en
ts 复制代码
// 英文
const en = {
  home: "Home",
  index: "Index",
  list: "List",
  info: "Info",
  welcome: "Welcome",
};

export default en;

配置index.ts

  • 导入lang.ts
ts 复制代码
import zhCn from "./zhCn";
import zhHk from "./zhHk";
import en from "./en";

export default {
  zhCn,
  zhHk,
  en,
};
  • 导入index.ts
ts 复制代码
import { createI18n } from "vue-i18n";
import messages from "./lang";

const i18n: any = createI18n({
  locale: localStorage.getItem("lang") || "zhCn",
  globalInjection: true,
  legacy: false,
  messages,
});

export default i18n;
  • tsconfig.json中添加类型
json 复制代码
{
  "compilerOptions": {
    // ...
    "types": ["vue-i18n"]
  }
  // ...
}

全局引入

main.ts中引入vue-i18n

ts 复制代码
// ...
import i18n from "./locale/index";
// ...
app.config.globalProperties.$i18n = i18n;
// ...
app.use(i18n);

接下来就可以在组件或者其他 ts 文件中使用了。

配置全局样式

styles文件夹中新建一个font.scssreset.css样式文件,然后导入main.ts文件中即可实现。

ts 复制代码
// ./src/main.ts
import "@s/reset.css";
import "@s/font.scss";

配置全局方法

  • http 方法

apis文件夹中新建一个http.ts文件,封装请求方法。

ts 复制代码
// ./src/apis/http.ts
import axios from "axios";

// 创建axios实例
const http = axios.create({
  baseURL: "/",
  timeout: 30000,
  headers: {
    "Content-Type": "application/json",
  },
});

// 请求拦截
http.interceptors.request.use(
  (config) => {
    config.headers.version = "v1";
    return config;
  },
  (err) => {
    return Promise.reject(err);
  }
);

// 响应拦截
http.interceptors.response.use(
  (res) => {
    let data = res.data;
    return data;
  },
  (err) => {
    return Promise.reject(err);
  }
);

export default http;
  • util 方法

apis文件夹中新建一个util.ts文件,封装请求方法。

ts 复制代码
// ./src/apis/util.ts
// 全局方法
import { AllAny } from "../types/interface";

const sum = (a: number, b: number): number => a + b;

const util: AllAny = {
  sum,
};

export default util;

这里我又定义了util的接口。

ts 复制代码
// 任意对象类型
export interface AllAny {
  [propsName: string]: any;
}
  • mitt 方法

下面介绍以下如何配置mitt

apis文件夹下面新建一个mitts.ts文件。

ts 复制代码
// ./src/apis/mitts.ts
import mitt, { Emitter } from "mitt";
import { MittEvents } from "../types/interface";

const mitts: Emitter<MittEvents> = mitt<MittEvents>();

export default mitts;

记得在interface.ts声明以下类型。

ts 复制代码
// mitt类型
export type MittEvents = {
  [propsName: string]: any;
};
  • 导入main.ts文件
ts 复制代码
// ./src/main.ts
// ...
import http from "./apis/http";
import util from "./apis/util";
import mitts from "./apis/mitts";
// ...
app.config.globalProperties.$http = http;
app.config.globalProperties.$util = util;
app.config.globalProperties.$mitts = mitts;
// ...

实战

在项目创建,安装依赖,配置全局环境结束后,写一个简单的组件案例。

组件组成

vue 复制代码
<!-- 组件模板 -->
<template></template>
<!-- 组件脚本 -->
<script lang="ts" name="Hello"></script>
<!-- 组件样式 -->
<style lang="scss" scoped></style>

小案例

下面是home.vue组件中的一些小案例,可以练习一下。

  • 模板内容
html 复制代码
<!-- 计算属性 -->
<p>
  <span>{{ sayHi }}</span>
</p>
<p>
  <button type="button" @click="changeName">改变姓名</button>
</p>
html 复制代码
<!-- 路由 -->
<p>
  <button type="button" @click="goNotFound">到404</button>
</p>
html 复制代码
<!-- 请求接口 -->
<p>
  <button type="button" @click="getData">请求接口</button>
</p>
<p>接口数据:{{ msg }}</p>
html 复制代码
<!-- 全局方法 -->
<p>
  <input v-model="sumInfo.num1" type="number" name="num1" id="num1" placeholder="数字1" />+
  <input v-model="sumInfo.num2" type="number" name="num2" id="num2" placeholder="数字2" />= {{
  sumInfo.sum }}
</p>
<p>
  <button type="button" @click="getSum">计算和</button>
</p>
html 复制代码
<!-- 发送消息 -->
<p>
  <button type="button" @click="sendMsg">发送消息</button>
</p>
  • 引入依赖
ts 复制代码
import { reactive, ref, watch, computed } from "vue";
import { useRoute, useRouter } from "vue-router";
import userStore from "../store/user";
import { Store } from "pinia";
import { useCurrentInstance } from "../types/util";
import { AllAny } from "../types/interface";
  • 定义数据
ts 复制代码
// 组件路由
const route = useRoute();
const router = useRouter();
console.log("route:", route, router);

// 用户状态
const user: Store = userStore();
console.log("store:", user);

// 组件数据
const msg: Ref = ref("");
console.log("data:", msg);

const info = reactive({
  id: 1,
  name: "mark",
});
console.log("data:", info);

const sumInfo = reactive({
  num1: 0,
  num2: 0,
  sum: 0,
});

console.log("sum:", sumInfo);
  • 定义方法
ts 复制代码
// 组件监听
watch(
  () => [info.name],
  (val: string[]) => {
    console.log("watch:", val);
  }
);

// 组件计算
const sayHi = computed(() => {
  return `Hi,${info.name}!`;
});

// 组件当前实例
const { proxy } = useCurrentInstance();
console.log("proxy:", proxy);

// 改变姓名
function changeName() {
  info.name = "jack";
}

// 到404
function goNotFound() {
  router.push("/404");
}

// 请求接口
async function getData() {
  let data: AllAny = await proxy.$http.get("/todos/1");
  msg.value = data;
  console.log("http:", data);
}

// 计算和
function getSum() {
  let sum: number = proxy.$util.sum(sumInfo.num1, sumInfo.num2);
  sumInfo.sum = sum;
}

// 发送消息
function sendMsg() {
  proxy.$mitts.emit("user", "mark");
}

多语言案例

  • 路由使用
ts 复制代码
import i18n from "../locale/index";
const trans = i18n.global.t;

trans("home");
  • 组件使用
ts 复制代码
import { useI18n } from "vue-i18n";
const { t } = useI18n();

console.log(t("welcome"));

以上就是如何从零搭建一个 vue3+ts5+vue-router4+pinia2 的项目方法。

相关推荐
小月鸭4 小时前
如何理解HTML语义化
前端·html
jump6805 小时前
url输入到网页展示会发生什么?
前端
诸葛韩信5 小时前
我们需要了解的Web Workers
前端
brzhang5 小时前
我觉得可以试试 TOON —— 一个为 LLM 而生的极致压缩数据格式
前端·后端·架构
yivifu5 小时前
JavaScript Selection API详解
java·前端·javascript
这儿有一堆花5 小时前
告别 Class 组件:拥抱 React Hooks 带来的函数式新范式
前端·javascript·react.js
十二春秋5 小时前
场景模拟:基础路由配置
前端
六月的可乐5 小时前
实战干货-Vue实现AI聊天助手全流程解析
前端·vue.js·ai编程
一 乐6 小时前
智慧党建|党务学习|基于SprinBoot+vue的智慧党建学习平台(源码+数据库+文档)
java·前端·数据库·vue.js·spring boot·学习
BBB努力学习程序设计7 小时前
CSS Sprite技术:用“雪碧图”提升网站性能的魔法
前端·html