手把手,完整的從0搭建vite-vue3-ts項目框架:配置less+svg+pinia+vant+axios

来源:https://www.cnblogs.com/jing-zhe/archive/2022/11/17/16895284.html
-Advertisement-
Play Games

項目同步git:https://gitee.com/lixin_ajax/vue3-vite-ts-pinia-vant-less.git 覺得有幫助的小伙伴請點下小心心哦 為避免贅述,過於基礎的點會直接省略或貼圖,比如創建文件夾/文件的路徑/路由一類 配置相應功能,也儘量只貼相關代碼,並不代表整個 ...


項目同步git:https://gitee.com/lixin_ajax/vue3-vite-ts-pinia-vant-less.git

 覺得有幫助的小伙伴請點下小心心哦

 

為避免贅述,過於基礎的點會直接省略或貼圖,比如創建文件夾/文件的路徑/路由一類

配置相應功能,也儘量只貼相關代碼,並不代表整個文件內容

我會儘量把每一步都記錄下來,讓跟隨文檔操作的朋友也能還原項目

項目不盡完美,但是主體功能應該都可以有所參考

一.本地初始環境

 

二.使用vite腳手架,創建vue3+ts

yarn create vite

輸入項目名,回車確認

 

選擇Vue和TypeScript

 

文件目錄如下,項目創建成功!

 

 三.啟動項目:根據提示進入項目運行項目,或自行使用編碼器輸入指令進行啟動

yarn    // 安裝依賴
yarn dev // 運行項目

 

瀏覽器打開地址,運行成功!

 

四.必備依賴

This package contains type definitions for Node.js (http://nodejs.org/)

yarn add @types/node -S -D

 

五.配置路徑別名@

1.位置:直接改寫vite.config.ts -- 順便就添加alias

// vite.config.ts

import vue from "@vitejs/plugin-vue";
import { resolve } from "path";

function pathResolve(dir: string) {
  return resolve(process.cwd(), ".", dir);
}

// https://vitejs.dev/config/
export default () => {
  return {
    resolve: {
      alias: [
        {
          find: "@",
          replacement: pathResolve("src"),
        },
        {
          find: "views",
          replacement: pathResolve("src/views"),
        },
      ],
    },
    plugins: [vue()],
  };
};

 

2.位置:tsconfig.json -- 修改baseUrl及paths

// tsconfig.json
{   "compilerOptions": {      ......   "baseUrl": "./",     "paths": {       "@/*": ["src/*"],       "views/*": ["src/views/*"],       "components/*": ["src/components/*"],       "assets/*": ["src/assets/*"]     }   },   "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],   "references": [{ "path": "./tsconfig.node.json" }] }

 

六.配置vue-router

yarn add vue-router -S

推薦一個很好的插件nprogress【進度條】

yarn add @types/nprogress nprogress -D

main.ts引入router

// main.ts

import { createApp } from 'vue'
import './style.css'

import App from './App.vue'
import router from "./router";

const app = createApp(App as any);
app.use(router)

app.mount('#app')

src下創建router文件夾,項目往往需要模塊化,所以代碼儘量不要雜糅在一起

 /router/index.ts

/router/index.ts

import { createRouter, createWebHashHistory, RouteRecordRaw } from "vue-router";
import NProgress from "nprogress";
import "nprogress/nprogress.css";

const modules: any = import.meta.glob("./modules/**/*.ts", { eager: true });

const routes: Array<RouteRecordRaw> = [];
for (const key in modules) {
    routes.push(...modules[key].default);
}

const router = createRouter({
    history: createWebHashHistory(), // history 模式則使用 createWebHistory()
    routes,
});

router.beforeEach(async (_to, _from, next) => {
    NProgress.start();
    next();
});

router.afterEach((_to) => {
    NProgress.done();
});

export default router;

/router/typings.d.ts 路由meta格式受控

/router/typing.d.ts

import "vue-router";

declare module "vue-router" {
    interface RouteMeta {
        // options
          title?: string;
        // every route must declare
          show?: boolean; //
    }
}

然後就是test下隨便創建一個路由,對應的,views下創建相應的vue文件,App.vue給上router-view

test/index.ts

app.vue

 

 test/index.vue

查看頁面

頁面正確顯示,路由部署成功!

 

七.配置css預處理:less

yarn add less less-loader -D

為了配置全局的less樣式文件,同時引入fs模塊

yarn add fs -D

進入項目根目錄的配置文件 

位置:vite.config.ts -- css

// vite.config.ts

import vue from "@vitejs/plugin-vue";
import { resolve } from "path";
import fs from 'fs'

function pathResolve(dir: string) {
  return resolve(process.cwd(), ".", dir);
}

// https://vitejs.dev/config/
export default () => {
  const lessResources: Array<String> = []
  fs.readdirSync('src/assets/styles').map((dirname) => {
    if (fs.statSync(`src/assets/styles/${dirname}`).isFile()) {
      lessResources.push(`@import "src/assets/styles/${dirname}";`)
    }
  })
  return {
    ......,
  // css
    css: {
      preprocessorOptions: {
        less: {
          charset: false,
          additionalData: lessResources.join(''),
          modifyVars: {
            'primary-color': '#0080FF',
            'link-color': '#0080FF',
            'border-radius-base': '4px',
          },
          javascriptEnabled: true,
        },
      },
    },
  };
};

 

這裡配置的公共less文件路徑為src/assets/styles,創建styles文件夾和index.less文件

 進入index.less聲明全局樣式,測試less配置是否成功

 進入test/index.vue使用聲明

 查看頁面

盒子背景顏色改變,less及全局less配置成功!

 

八.配置svg

yarn add vite-plugin-svg-icons -D
yarn add fast-glob -D
vite.config.ts引入插件
// vite.config.ts

import vue from "@vitejs/plugin-vue";
import { createSvgIconsPlugin } from "vite-plugin-svg-icons";
import path from "path";

// https://vitejs.dev/config/
export default () => {
  return {
    ......,
    plugins: [
      vue(),
      createSvgIconsPlugin({
        // 指定需要緩存的圖標文件夾
          iconDirs: [path.resolve(process.cwd(), "src/assets/icons")],
        // 指定symbolId格式
          symbolId: "icon-[dir]-[name]",
      }),
    ],
  };
};

根據config配置創建存放svg的目錄文件,並創建SvgIcon組件

 SvgIcon組件

// SvgIcon/index.vue

<template>
    <svg aria-hidden="true" class="svg-icon">
        <use :xlink:href="symbolId" :fill="color" />
    </svg>
</template>

<script lang="ts" setup>
import { computed } from "vue";

const props = defineProps({
    prefix: {
        type: String,
        default: "icon",
    },
    name: {
        type: String,
        required: true,
    },
    color: {
        type: String,
        default: "#333",
    },
});
const symbolId = computed(() => `#${props.prefix}-${props.name}`);
</script>
<style lang="less" scoped>
.svg-icon {
    width: 1em;
    height: 1em;
    fill: v-bind(color);
    vertical-align: middle;
    color: v-bind(color);
}
</style>

在main.ts中註冊SvgIcon為全局組件

// main.ts

import { createApp } from 'vue'
import './style.css'
import "virtual:svg-icons-register";
import SvgIcon from "@/components/SvgIcon/index.vue";

import App from './App.vue'
import router from "./router";

const app = createApp(App as any);
app.use(router)

app.mount('#app')
app.component("SvgIcon", SvgIcon);

在test/index.vue中引入組件

// test/index.vue

<svg-icon name="test-vue" />

查看頁面,測試是否成功

 頁面顯示svg圖標,svg組件配置成功!

 

九.配置pinia

pinia: 類似vuex的倉庫
pinia-use-persist: 持久加密緩存pinia數據
yarn add pinia pinia-use-persist -S

main.ts中引入pinia

// main.ts

import { createApp } from 'vue'
import { createPinia } from 'pinia'
import { usePersist } from 'pinia-use-persist'

import App from './App.vue'

const app = createApp(App as any);
const pinia = createPinia()
pinia.use(usePersist)
app.use(pinia)

src下創建store目錄存放相關文件

 /store/modules下存放項目不同模塊需要通過pinia通信的數據,假裝項目有一個test模塊,存放了一個數據number

// store/modules/test/index.ts

import { defineStore } from "pinia";

interface stateType {
  number: number;
}

const useTestStore = defineStore("user", {
  state: (): stateType => ({
    number: 0,
  }),

  getters: {},

  actions: {
    setNumber(number: number): void {
      this.number = number;
    },
  },

  persist: {
    enabled: true,
    encryptionKey: "vueTest",
  },
});

export { useTestStore };

store/index.ts引入各模塊

// store/index.ts

import { createPinia } from "pinia";
import { useTestStore } from "./modules/test";

const pinia = createPinia();

export { useTestStore };
export default pinia;

回到test/index.vue,測試pinia配置是否成功

// test/index.vue

<template>
    <!-- 測試pinia -->
    <button @click="number += 1">{{ number }}</button>
  </div>
</template>

<script setup lang="ts">
import { computed } from 'vue'
import { useTestStore } from '@/store'

const store = useTestStore()
const number = computed<number>({
  get() {
    return store.number
  },
  set(value) {
    store.setNumber(value)
  },
})
</script>

點擊按鈕,刷新後查看頁面是否變化

頁面數據沒有初始化,pinia配置成功!

 

十.配置vant ui

vant ui:https://vant-contrib.gitee.io/vant/v4/#/zh-CN/home

yarn add vant

這裡再推薦一個插件,unplugin-vue-components【自動引入】,引入ui可以省去很多麻煩

yarn add unplugin-vue-components -D

vite.config.ts中引入vant ui

// vite.config.ts

import vue from '@vitejs/plugin-vue';
import Components from 'unplugin-vue-components/vite';
import { VantResolver } from 'unplugin-vue-components/resolvers';

export default {
  plugins: [
    vue(),
    Components({
      resolvers: [VantResolver()],
    }),
  ],
};

回到test/index.vue測試vant ui引入是否成功

// test/index.vue

<!-- 測試vant ui -->
<div>
  <van-button type="primary">vant button</van-button>
</div>

刷新頁面查看

 

 

 按鈕有樣式,vant ui引入成功!

但是官方描述:Vant 中有個別組件是以函數的形式提供的,包括 Toast,Dialog,Notify 和 ImagePreview 組件。在使用函數組件時,unplugin-vue-components 無法自動引入對應的樣式,因此需要手動引入樣式。

所以,這幾個組件需要使用的話需要在main.ts中引入樣式

// main.ts

// Toast
import 'vant/es/toast/style';
// Dialog
import 'vant/es/dialog/style';
// Notify
import 'vant/es/notify/style';
// ImagePreview
import 'vant/es/image-preview/style';

回到test/index.vue測試,示例toast

// test/index.vue

import { Toast } from 'vant'

Toast('使用vant')

查看頁面

 

 

 toast有樣式,vant ui引入成功!

但是,使用vant ui多是移動端,所以還要做移動端做以下適配

參考: https://vant-contrib.gitee.io/vant/v4/#/zh-CN/advanced-usage

1.適配安全距離

根據vant ui提供,在根文件index.html修改

// index.html

  <!-- 在 head 標簽中添加 meta 標簽,並設置 viewport-fit=cover 值 -->
  <meta name="viewport"
    content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, viewport-fit=cover" />

  <!-- 開啟頂部安全區適配 -->
  <van-nav-bar safe-area-inset-top />

  <!-- 開啟底部安全區適配 -->
  <van-number-keyboard safe-area-inset-bottom />

2.Viewport 佈局

postcss-px-to-viewport-8-plugin:postcss-px-to-viewport-8-plugin 是一款 PostCSS 插件,用於將 px 單位轉化為 vw/vh 單位。

yarn add postcss-px-to-viewport-8-plugin -D

vite.config.ts中更改配置

// vite.config.ts

import pxtovw from 'postcss-px-to-viewport-8-plugin'
const loder_pxtovw = pxtovw({
//這裡是設計稿寬度 自己修改
  viewportWidth: 375,
  viewportUnit: 'vw'
})
export default defineConfig({
  ......,
  css: {
    postcss: {
      plugins: [loder_pxtovw]
    }
  }
})

創建一個types/index.d.ts,用於處理包括postcss-px-to-viewport-8-plugin一類的沒有聲明文件的依賴

// src/types/index.d.ts

declare module "postcss-px-to-viewport-8-plugin"

刷新頁面,F12查看樣式

 px已被轉換,vant ui 及 移動端配置成功!

 

十一.配置axios

yarn add axios

 tsconfig.json:types裡加上"vite/client"

// tsconfig.json

{
  "compilerOptions": {
    ......,
    "types": ["vite/client", "vite-plugin-svg-icons/client"]
  },
  "include": [
    "src/**/*.ts",
    "src/**/*.d.ts",
    "src/**/*.tsx",
    "src/**/*.vue",
    "*.ts",
  ],
  "exclude": ["node_modules", "dist"],
  "references": [{ "path": "./tsconfig.node.json" }]
}

src下創建axios請求文件

 創建axios

// utils/http/axios/index.ts

import axios, {
  AxiosInstance,
  AxiosRequestConfig,
  AxiosResponse,
  AxiosError,
} from "axios";
import { IResponse } from "./type";

// 如果請求超過 `timeout` 的時間,請求將被中斷
axios.defaults.timeout = 5000;

const axiosInstance: AxiosInstance = axios.create({
  baseURL: import.meta.env.VITE_APP_API_BASEURL + "",
});

// axios實例攔截請求
axiosInstance.interceptors.request.use(
  (config: AxiosRequestConfig) => {
    // 配置headers
    config.headers = {
      ...config.headers,
    };
    return config;
  },
  (error: any) => {
    return Promise.reject(error);
  }
);

// axios實例攔截響應
axiosInstance.interceptors.response.use(
  // 請求成功
  (response: AxiosResponse) => {
    return response;
  },
  // 請求失敗
  (error: AxiosError) => {
    const { response } = error;
    console.error(response, "response error");
    if (response) {
      return Promise.reject(response.data);
    }
  }
);

const request = <T = any>(config: AxiosRequestConfig): Promise<T> => {
  const conf = config;
  return new Promise((resolve) => {
    axiosInstance
      .request<any, AxiosResponse<IResponse>>(conf)
      .then((res: AxiosResponse<IResponse>) => {
        const {
          data: { result },
        } = res;
        resolve(result as T);
      });
  });
};

export function get<T = any>(config: AxiosRequestConfig): Promise<T> {
  return request({ ...config, method: "GET" });
}

export function post<T = any>(config: AxiosRequestConfig): Promise<T> {
  return request({ ...config, method: "POST" });
}

export default request;
export type { AxiosInstance, AxiosResponse };
// utils/http/axios/type.ts

export interface RequestOptions {
    // Whether to process the request result
    isTransformResponse?: boolean;
}

// 返回res.data的interface
export interface IResponse<T = any> {
    code: number | string;
    result: T;
    data: T;
    message: string;
    status: string | number;
}

根目錄創建.env.development配置開發請求地址

// .env.development

# 開發環境

VITE_APP_API_BASEURL = 你的請求地址

NODE_ENV = development

vite.config.ts配置server

// vite.config.ts

    server: {
      hmr: { overlay: false }, // 禁用或配置 HMR 連接 設置 server.hmr.overlay 為 false 可以禁用伺服器錯誤遮罩層
            // 服務配置
            port: 3030, // 類型: number 指定伺服器埠;
            open: false, // 類型: boolean | string在伺服器啟動時自動在瀏覽器中打開應用程式;
            cors: false, // 類型: boolean | CorsOptions 為開發伺服器配置 CORS。預設啟用並允許任何源
           host: "0.0.0.0", // IP配置,支持從IP啟動
           ["/api"]: {
        target: process.env.VITE_APP_API_BASEURL,
        changeOrigin: true,
        rewrite: (path: string) => path.replace(new RegExp("^/api"), ""),
      },
    },

創建api存放目錄

  創建一個api

// api/test/index.ts

import { post } from "@/utils/http/axios";
import { IResponse } from "@/utils/http/axios/type";

export interface LoginData {
  username?: string;
  password?: string;
}

enum URL {
  login = "/api/user_center/testLogin",
}

/**
 * @description: 用戶登錄
 * @params {LoginData} params
 * @return {Promise}
 */

const login = async (data: LoginData) =>
  post<IResponse>({ url: URL.login, data });

export { login };

回到test/index.vue調用api測試axios

// test/index.vue

<script setup lang="ts">
import { login } from '@/api/test'

login({})
</script>

回到頁面,查看network

 介面請求成功,axios配置成功!

 最後,配置一下打包

// vite.config.ts

import { UserConfig, ConfigEnv, loadEnv } from "vite";

// https://vitejs.dev/config/
export default ({ command, mode }: ConfigEnv): UserConfig => {
  const env = loadEnv(mode, __dirname);
  return {
    base: env.NODE_ENV === "development" ? "/" : "./",
    build: {
      outDir: "dist",
      assetsDir: "assets", //指定靜態資源存放路徑
        sourcemap: false, //是否構建source map 文件
    },
  };
};

啟動dist,沒問題!

 

 

結語:

項目到此主體功能就已經配置完畢了,細節之處大家多多查看官網和眾多網友的分享

項目還有很多不完善甚至錯誤的地方,踩坑還會繼續,後續有時間還會繼續優化,實際使用中還有很多地方需要改進

 

項目同步git:https://gitee.com/lixin_ajax/vue3-vite-ts-pinia-vant-less.git


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • //源文件 void UartGpioConfig() { RCC->AHB1ENR |= (1<<3); //使能GPIOD RCC->APB1ENR |= (1<<18); //使能USART3 RCC->APB2ENR |= //PD8 TXD GPIOD->MODER |= (2<<16); ...
  • //源文件 void TimerPhyConfig() { RCC->APB1ENR |= (1<<1); //使能Timer3 TIM3->ARR = arr; TIM3->PSC = psc; TIM3->DIER = (1<<0); //Update interrupt enabled TIM ...
  • 本文主要從技術層面探討大數據目前的現狀以及面臨的挑戰。在此之前,如果你對大數據的概念還比較模糊,可閱讀什麼是大數據一文瞭解。 如何定義大數據 目前我們已經瞭解到,大數據是由於數據量的巨大增長而產生的。所以,“大數據”一詞主要描述的是規模巨大的混合數據集,這種數據集是結構化與非結構化數據的融合。 通常 ...
  • 摘要:一文帶你細數幾種ETCD服務異常實例狀態。 本文分享自華為雲社區《【實例狀態】GaussDB ETCD服務異常》,作者:酷哥 。 首先確認是否是虛擬機、網路故障 虛擬機故障導致ETCD服務異常告警 問題現象 管控面上報etcd服務異常告警,虛擬機發生重啟,熱遷移、冷遷移,HA等動作。 問題分析 ...
  • 最近,我們袋鼠雲的UED部⻔小伙伴們,不聲不響地⼲了⼀件⼤事——升級了全新設計語言「數棧UI5.0」。 眾所周知,用戶在使用產品時,是一個動態的過程,用戶和產品之間進行交互的可用性,能否讓用戶愉悅、快速地在產品內達成目的,直接影響用戶使用產品的體驗。 在設計中,有一個廣泛的經驗法則被稱為「尼爾森十大 ...
  • Babelfish for PostgreSQL開源已快一月,不過全網還沒有實踐者總結。今天我們就測試看看,Babelfish到底是如何部署與使用的! ...
  • 現在要是說mysql是什麼東西,就不禮貌了 雖然有的同學沒有進行系統的深入學習,但應該也有個基本概念 【不瞭解也沒關係,後續會進行mysql專欄講解】簡單來說,存儲數據的 學習mysql,就要先安裝它 上官網 : https://dev.mysql.com/downloads/mysql/ 打開網址 ...
  • 11月15日,HMS Core手語服務在2022(第二十一屆)中國互聯網大會 “互聯網助力經濟社會數字化轉型”案例評選活動中,榮獲“特別推薦案例”。 經過一年多的技術迭代和經驗積累,HMS Core手語服務已與多個行業的開發者合作,將AI手語翻譯能力應用在了教育、社交、新聞、政務辦理等場景,助力開發 ...
一周排行
    -Advertisement-
    Play Games
  • Timer是什麼 Timer 是一種用於創建定期粒度行為的機制。 與標準的 .NET System.Threading.Timer 類相似,Orleans 的 Timer 允許在一段時間後執行特定的操作,或者在特定的時間間隔內重覆執行操作。 它在分散式系統中具有重要作用,特別是在處理需要周期性執行的 ...
  • 前言 相信很多做WPF開發的小伙伴都遇到過表格類的需求,雖然現有的Grid控制項也能實現,但是使用起來的體驗感並不好,比如要實現一個Excel中的表格效果,估計你能想到的第一個方法就是套Border控制項,用這種方法你需要控制每個Border的邊框,並且在一堆Bordr中找到Grid.Row,Grid. ...
  • .NET C#程式啟動閃退,目錄導致的問題 這是第2次踩這個坑了,很小的編程細節,容易忽略,所以寫個博客,分享給大家。 1.第一次坑:是windows 系統把程式運行成服務,找不到配置文件,原因是以服務運行它的工作目錄是在C:\Windows\System32 2.本次坑:WPF桌面程式通過註冊表設 ...
  • 在分散式系統中,數據的持久化是至關重要的一環。 Orleans 7 引入了強大的持久化功能,使得在分散式環境下管理數據變得更加輕鬆和可靠。 本文將介紹什麼是 Orleans 7 的持久化,如何設置它以及相應的代碼示例。 什麼是 Orleans 7 的持久化? Orleans 7 的持久化是指將 Or ...
  • 前言 .NET Feature Management 是一個用於管理應用程式功能的庫,它可以幫助開發人員在應用程式中輕鬆地添加、移除和管理功能。使用 Feature Management,開發人員可以根據不同用戶、環境或其他條件來動態地控制應用程式中的功能。這使得開發人員可以更靈活地管理應用程式的功 ...
  • 在 WPF 應用程式中,拖放操作是實現用戶交互的重要組成部分。通過拖放操作,用戶可以輕鬆地將數據從一個位置移動到另一個位置,或者將控制項從一個容器移動到另一個容器。然而,WPF 中預設的拖放操作可能並不是那麼好用。為瞭解決這個問題,我們可以自定義一個 Panel 來實現更簡單的拖拽操作。 自定義 Pa ...
  • 在實際使用中,由於涉及到不同編程語言之間互相調用,導致C++ 中的OpenCV與C#中的OpenCvSharp 圖像數據在不同編程語言之間難以有效傳遞。在本文中我們將結合OpenCvSharp源碼實現原理,探究兩種數據之間的通信方式。 ...
  • 一、前言 這是一篇搭建許可權管理系統的系列文章。 隨著網路的發展,信息安全對應任何企業來說都越發的重要,而本系列文章將和大家一起一步一步搭建一個全新的許可權管理系統。 說明:由於搭建一個全新的項目過於繁瑣,所有作者將挑選核心代碼和核心思路進行分享。 二、技術選擇 三、開始設計 1、自主搭建vue前端和. ...
  • Csharper中的表達式樹 這節課來瞭解一下表示式樹是什麼? 在C#中,表達式樹是一種數據結構,它可以表示一些代碼塊,如Lambda表達式或查詢表達式。表達式樹使你能夠查看和操作數據,就像你可以查看和操作代碼一樣。它們通常用於創建動態查詢和解析表達式。 一、認識表達式樹 為什麼要這樣說?它和委托有 ...
  • 在使用Django等框架來操作MySQL時,實際上底層還是通過Python來操作的,首先需要安裝一個驅動程式,在Python3中,驅動程式有多種選擇,比如有pymysql以及mysqlclient等。使用pip命令安裝mysqlclient失敗應如何解決? 安裝的python版本說明 機器同時安裝了 ...