基於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 項目
pnpm create vite
  • 填寫項目信息

包括項目名稱、選擇框架、js 語言等。

√ Project name: ... tslx
√ Select a framework: » Vue
√ Select a variant: » TypeScript
  • 創建成功

根據以下步驟來安裝基本的依賴和運行項目。

cd tslx
pnpm install
pnpm run dev

安裝

創建好項目後,接下來安裝一些必備的依賴包。

必備依賴包

  • vue-router

這個是必須的,路由管理。

pnpm i vue-router -S
  • sass

這個是必須的,主要是使用 sass 寫項目樣式表。

pnpm i sass -S
  • axios

這個是必須的,主要是 http 請求數據。

pnpm i axios -S

可選依賴包

  • pinia

這個是可選的,主要是提供狀態管理。

pnpm i pinia -D
  • pinia-plugin-persist

這個是可選的,主要是提供狀態管理的持久化存儲。

pnpm i pinia-plugin-persist -D
  • unplugin-auto-import

這個是可選的,自動導入依賴插件。

pnpm i unplugin-auto-import -D
  • normalize.css

這個是可選的,主要是可定製化 CSS 樣式。

pnpm i normalize.css -D
  • mitt

這個是可選的,一個簡潔、靈活的 JavaScript 事件訂閱和發佈庫。

pnpm i mitt -D
  • @volar-plugins/vetur

這是可選的,一個支持 vue3 語法的插件。

pnpm i @volar-plugins/vetur -D
  • @vitejs/plugin-vue

這個是可選的,支持基於 Vite 構建的 Vue 項目。

pnpm i @vitejs/plugin-vue -D
  • @types/node

這個是可選的,主要是解決模塊的聲明問題。

pnpm i @types/node -D
  • path

這個是可選的,主要是 node 的 path 模塊。

pnpm i path -D
  • rollup-plugin-visualizer

這個是可選的,主要是應用模塊統計。

pnpm i rollup-plugin-visualizer -D
  • vue-i18n

這個是可選的,主要是多語言配置。

pnpm i vue-i18n -D

好了,以上就是日常開發項目常用的一些依賴包。

配置

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

vite 配置文件

打開vite.config.ts文件,主要是添加插件配置,文件路徑別名配置,css 全局樣式配置,服務端端口及代理配置。

引入依賴

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

插件配置

plugins中添加以下配置。

//...
{
  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:主要是存放全局方法文件;

下面是配置方法。

//...
{
  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的文件。

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

服務端端口及代理配置

最後就是一個服務端的配置,包括端口,自動打開網頁,跨域接口設置。

//...
{
  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屬性並配置如下。

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

完整的tsconfig.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的內容:

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

配置 main.ts

  • 整理main.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

// ./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文件。

// ./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文件。
//...
import router from "./router";
// ...
app.use(router);
//...

配置 pinia

src下面新建一個store的文件夾,裏面新建一個index.tstypes.ts以及user.ts的文件。

  • index.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聲明文件。

內容爲:

declare module "pinia-plugin-persist";
  • types.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;

pnpm i eslint-plugin-vue @typescript-eslint/parser @typescript-eslint/eslint-plugin -D

修改項目根目錄下的.eslintrc.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文件

這個文件主要是用戶的一些狀態信息。

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的接口定義。

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

配置 vue-i18n

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

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

語言內容

  • 中文簡體 zhCn
const zhCn = {
  home: "首頁",
  index: "主頁",
  list: "列表",
  info: "信息",
  welcome: "歡迎光臨",
};

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

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

export default en;

配置index.ts

  • 導入lang.ts
import zhCn from "./zhCn";
import zhHk from "./zhHk";
import en from "./en";

export default {
  zhCn,
  zhHk,
  en,
};
  • 導入index.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中添加類型
{
  "compilerOptions": {
    // ...
    "types": ["vue-i18n"]
  }
  // ...
}

全局引入

main.ts中引入vue-i18n

// ...
import i18n from "./locale/index";
// ...
app.config.globalProperties.$i18n = i18n;
// ...
app.use(i18n);

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

配置全局樣式

styles文件夾中新建一個font.scssreset.css樣式文件,然後導入main.ts文件中即可實現。

// ./src/main.ts
import "@s/reset.css";
import "@s/font.scss";

配置全局方法

  • http 方法

apis文件夾中新建一個http.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文件,封裝請求方法。

// ./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的接口。

// 任意對象類型
export interface AllAny {
  [propsName: string]: any;
}
  • mitt 方法

下面介紹以下如何配置mitt

apis文件夾下面新建一個mitts.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聲明以下類型。

// mitt類型
export type MittEvents = {
  [propsName: string]: any;
};
  • 導入main.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;
// ...

實戰

在項目創建,安裝依賴,配置全局環境結束後,寫一個簡單的組件案例。

組件組成

<!-- 組件模板 -->
<template></template>
<!-- 組件腳本 -->
<script lang="ts" name="Hello"></script>
<!-- 組件樣式 -->
<style lang="scss" scoped></style>

小案例

下面是home.vue組件中的一些小案例,可以練習一下。

  • 模板內容
<!-- 計算屬性 -->
<p>
  <span>{{ sayHi }}</span>
</p>
<p>
  <button type="button" @click="changeName">改變姓名</button>
</p>
<!-- 路由 -->
<p>
  <button type="button" @click="goNotFound">到404</button>
</p>
<!-- 請求接口 -->
<p>
  <button type="button" @click="getData">請求接口</button>
</p>
<p>接口數據:{{ msg }}</p>
<!-- 全局方法 -->
<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>
<!-- 發送消息 -->
<p>
  <button type="button" @click="sendMsg">發送消息</button>
</p>
  • 引入依賴
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";
  • 定義數據
// 組件路由
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);
  • 定義方法
// 組件監聽
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");
}

多語言案例

  • 路由使用
import i18n from "../locale/index";
const trans = i18n.global.t;

trans("home");
  • 組件使用
import { useI18n } from "vue-i18n";
const { t } = useI18n();

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

以上就是如何從零搭建一個 vue3+ts5+vue-router4+pinia2 的項目方法。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章