推荐一个 Vue3 全家桶 + TS+ Vite2 + element-plus 的网站实战项目

博主分享了使用Vue3、Vite2、TypeScript和ElementPlus重构个人博客的详细过程,包括初始化项目、引入TypeScript、配置ESLint和Prettier、集成Vue Router和Vuex,以及添加ElementPlus。项目源码已开源,重构后项目体积减小,性能提升。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

五一期间,花了 3 天时间,边学 Vue3 和 Vite2,边重构自己的项目,终于都用 Vue3 + TypeScript + Vite2 + Vuex4 + Vue-Router4 + element-plus 重构完啦!

终于完成一项心心念念的 2021 年度目标了 ✌️

项目地址:

https://github.com/biaochenxuying/blog-vue-typescript

效果

效果图:

  • pc 端

  • 移动端

完整效果请看:

https://biaochenxuying.cn

功能

已经完成功能

  • [x] 登录

  • [x] 注册

  • [x] 文章列表

  • [x] 文章归档

  • [x] 标签

  • [x] 关于

  • [x] 点赞与评论

  • [x] 留言

  • [x] 历程

  • [x] 文章详情(支持代码语法高亮)

  • [x] 文章详情目录

  • [x] 移动端适配

  • [x] github 授权登录

前端主要技术

所有技术都是当前最新的。

  • vue:^3.0.5

  • typescript : ^4.1.3

  • element-plus: ^1.0.2-beta.41

  • vue-router : ^4.0.6

  • vite: ^2.2.3

  • vuex: ^4.0.0

  • axios: ^0.21.1

  • highlight.js: ^10.7.2

  • marked:^2.0.3

1. 初化化项目

用 vite-app 创建项目

yarn create vite-app <project-name>

# 或者
npm init vite-app <project-name>

然后按照提示操作即可!

进入项目,安装依赖

cd <project-name>

yarn # 或 npm i

运行项目

yarn dev 

打开浏览器 http://localhost:3000 查看

2. 引入 TypeScript

在创建项目的时候可以 TypeScript 的,如果你选择了 TypeScript ,可以忽略第 2 个步骤。

加入 ts 依赖

yarn add --dev typescript

在 项目根目录下创建 TypeScript 的配置文件 tsconfig.json

{
  "compilerOptions": {
    // 允许从没有设置默认导出的模块中默认导入。这并不影响代码的输出,仅为了类型检查。
    "allowSyntheticDefaultImports": true,
    
    // 解析非相对模块名的基准目录
    "baseUrl": ".",

    "esModuleInterop": true,

    // 从 tslib 导入辅助工具函数(比如 __extends, __rest等)
    "importHelpers": true,

    // 指定生成哪个模块系统代码
    "module": "esnext",

    // 决定如何处理模块。
    "moduleResolution": "node",

    // 启用所有严格类型检查选项。
    // 启用 --strict相当于启用 --noImplicitAny, --noImplicitThis, --alwaysStrict, 
    // --strictNullChecks和 --strictFunctionTypes和--strictPropertyInitialization。
    "strict": true,

    // 生成相应的 .map文件。
    "sourceMap": true,

    // 忽略所有的声明文件( *.d.ts)的类型检查。
    "skipLibCheck": true,

    // 指定ECMAScript目标版本 
    "target": "esnext",
    
    // 要包含的类型声明文件名列表
    "types": [

    ],

    "isolatedModules": true,

    // 模块名到基于 baseUrl的路径映射的列表。
    "paths": {
      "@/*": [
        "src/*"
      ]
    },
    // 编译过程中需要引入的库文件的列表。
    "lib": [
      "ESNext",
      "DOM",
      "DOM.Iterable",
      "ScriptHost"
    ]
  },
  "include": [
    "src/**/*.ts",
    "src/**/*.tsx",
    "src/**/*.vue",
    "tests/**/*.ts",
    "tests/**/*.tsx"
  ],
  "exclude": [
    "node_modules"
  ]
}

在 src 目录下新加 shim.d.ts 文件

/* eslint-disable */
import type { DefineComponent } from 'vue'

declare module '*.vue' {
  const component: DefineComponent<{}, {}, any>
  export default component
}

把 main.js 修改成 main.ts

在根目录,打开 Index.html

<script type="module" src="/src/main.js"></script>
修改为:
<script type="module" src="/src/main.ts"></script>

3. 引入 eslint

安装 eslint prettier 依赖

@typescript-eslint/parser @typescr ipt-eslint/eslint-plugin 为 eslint 对 typescript 支持。

yarn add --dev eslint prettier eslint-config-prettier eslint-plugin-prettier eslint-plugin-vue @typescript-eslint/parser @typescr ipt-eslint/eslint-plugin

在根目录下建立 eslint 配置文件:.eslintrc.js

module.exports = {
  parser: 'vue-eslint-parser',
  parserOptions: {
    parser: '@typescript-eslint/parser',
    ecmaVersion: 2020,
    sourceType: 'module',
    ecmaFeatures: {
      jsx: true
    }
  },
  extends: [
    'plugin:vue/vue3-recommended',
    'plugin:@typescript-eslint/recommended',
    'prettier/@typescript-eslint',
    'plugin:prettier/recommended'
  ],
  rules: {
    '@typescript-eslint/ban-ts-ignore': 'off',
    '@typescript-eslint/explicit-function-return-type': 'off',
    '@typescript-eslint/no-explicit-any': 'off',
    '@typescript-eslint/no-var-requires': 'off',
    '@typescript-eslint/no-empty-function': 'off',
    'vue/custom-event-name-casing': 'off',
    'no-use-before-define': 'off',
    // 'no-use-before-define': [
    //   'error',
    //   {
    //     functions: false,
    //     classes: true,
    //   },
    // ],
    '@typescript-eslint/no-use-before-define': 'off',
    // '@typescript-eslint/no-use-before-define': [
    //   'error',
    //   {
    //     functions: false,
    //     classes: true,
    //   },
    // ],
    '@typescript-eslint/ban-ts-comment': 'off',
    '@typescript-eslint/ban-types': 'off',
    '@typescript-eslint/no-non-null-assertion': 'off',
    '@typescript-eslint/explicit-module-boundary-types': 'off',
    '@typescript-eslint/no-unused-vars': [
      'error',
      {
        argsIgnorePattern: '^h$',
        varsIgnorePattern: '^h$'
      }
    ],
    'no-unused-vars': [
      'error',
      {
        argsIgnorePattern: '^h$',
        varsIgnorePattern: '^h$'
      }
    ],
    'space-before-function-paren': 'off',
    quotes: ['error', 'single'],
    'comma-dangle': ['error', 'never']
  }
};

建立 prettier.config.js

module.exports = {
  printWidth: 100,
  tabWidth: 2,
  useTabs: false,
  semi: false, // 未尾逗号
  vueIndentScriptAndStyle: true,
  singleQuote: true, // 单引号
  quoteProps: 'as-needed',
  bracketSpacing: true,
  trailingComma: 'none', // 未尾分号
  jsxBracketSameLine: false,
  jsxSingleQuote: false,
  arrowParens: 'always',
  insertPragma: false,
  requirePragma: false,
  proseWrap: 'never',
  htmlWhitespaceSensitivity: 'strict',
  endOfLine: 'lf'
}

4. vue-router、vuex

npm install vue-router@4 vuex

4.1 vuex

在根目录下创建 store/index.ts

import { InjectionKey } from 'vue'
import { createStore, Store } from 'vuex'

export interface State {
  count: number
}

export const key: InjectionKey<Store<State>> = Symbol()

export const store = createStore<State>({
  state() {
    return {
      count: 0
    }
  },
  mutations: {
    increment(state) {
      state.count++
    }
  }
})

main.ts 修改

import { createApp } from 'vue'
import { store, key } from './store'
import App from './App'
import './index.css'

const app = createApp(App)

app.use(store, key)

app.mount('#app')

components/HelloWord.vue 修改

<template>
  <h1>{{ msg }}</h1>
  <button @click="inCrement"> count is: </button>
  <p>{{ count }}</p>
</template>

<script>
  import { defineComponent, computed } from 'vue'
  import { useStore } from 'vuex'
  import { key } from '../store'

  export default defineComponent({
    name: 'HelloWorld',
    props: {
      msg: {
        type: String,
        default: ''
      }
    },
    setup() {
      const store = useStore(key)

      const count = computed(() => store.state.count)

      return {
        count,
        inCrement: () => store.commit('increment')
      }
    }
  })
</script>

4.2 vue-router

在 src 目录下建立 router/index.ts,内容如下:

import { createRouter, createWebHistory, RouteRecordRaw } from "vue-router";
import HelloWorld from "../components/HelloWorld.vue";

const routes: Array<RouteRecordRaw> = [
    {
        path: "/",
        name: "HelloWorld",
        component: HelloWorld,
    },
    {
        path: "/about",
        name: "About",
        // route level code-splitting
        // this generates a separate chunk (about.[hash].js) for this route
        // which is lazy-loaded when the route is visited.
        component: () =>
            import(/* webpackChunkName: "About" */ "../components/About.vue")
    }
];

const router = createRouter({
    history: createWebHistory(process.env.BASE_URL),
    routes,
});

export default router;

再新建一个 components/About.vue 文件,内容如下:

<template>
  <img
    alt="Vue logo"
    src="../assets/logo.png"
  />
  <h1>{{ msg }}</h1>
</template>

<script lang="ts">
import { defineComponent } from 'vue'

export default defineComponent({
  name: 'About',
  data() {
    return {
      msg: 'Hello Vue 3.0 + Vite!'
    }
  },
  setup() {}
})
</script>

再修改 main.ts

import { createApp } from 'vue'
import { store, key } from './store'
import router from "./router";
import App from './App'
import './index.css'

const app = createApp(App)

app.use(store, key)
app.use(router)
app.mount('#app')

再访问 http://localhost:3000/

和 http://localhost:3000/about 即可

5. 加入 Element Plus

5.1 安装 element-plus

全局安装

npm install element-plus --save

5.2 引入 Element Plus

你可以引入整个 Element Plus,或是根据需要仅引入部分组件。我们先介绍如何引入完整的 Element。

完整引入

在 main.js 中写入以下内容:

import { createApp } from 'vue'
import ElementPlus from 'element-plus';
import router from "./router";
import 'element-plus/lib/theme-chalk/index.css';
import App from './App.vue';
import './index.css'

const app = createApp(App)
app.use(ElementPlus)
app.use(router)
app.mount('#app')

以上代码便完成了 Element Plus 的引入。需要注意的是,样式文件需要单独引入。


按需引入

借助 babel-plugin-component,我们可以只引入需要的组件,以达到减小项目体积的目的。

首先,安装 babel-plugin-component:

npm install babel-plugin-component -D

然后,将 .babelrc 修改为:

{
  "plugins": [
    [
      "component",
      {
        "libraryName": "element-plus",
        "styleLibraryName": "theme-chalk"
      }
    ]
  ]
}

接下来,如果你只希望引入部分组件,比如 Button 和 Select,那么需要在 main.js 中写入以下内容:

import { createApp } from 'vue'
import { store, key } from './store';
import router from "./router";
import { ElButton, ElSelect } from 'element-plus';
import App from './App.vue';
import './index.css'

const app = createApp(App)
app.component(ElButton.name, ElButton);
app.component(ElSelect.name, ElSelect);

/* or
 * app.use(ElButton)
 * app.use(ElSelect)
 */

app.use(store, key)
app.use(router)
app.mount('#app')
app.mount('#app')

更详细的安装方法请看 快速上手。

5.3 全局配置

在引入 Element Plus 时,可以传入一个全局配置对象。

该对象目前支持 size 与 zIndex 字段。size 用于改变组件的默认尺寸,zIndex 设置弹框的初始 z-index(默认值:2000)。按照引入 Element Plus 的方式,具体操作如下:

完整引入 Element:

import { createApp } from 'vue'
import ElementPlus from 'element-plus';
import App from './App.vue';

const app = createApp(App)
app.use(ElementPlus, { size: 'small', zIndex: 3000 });

按需引入 Element:

import { createApp } from 'vue'
import { ElButton } from 'element-plus';
import App from './App.vue';

const app = createApp(App)
app.config.globalProperties.$ELEMENT = option
app.use(ElButton);

按照以上设置,项目中所有拥有 size 属性的组件的默认尺寸均为 'small',弹框的初始 z-index 为 3000。

5.4 配置 vite.config.ts

其中 proxy 和 alias 是和 vue-cli 区别比较大的地方。

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import styleImport from 'vite-plugin-style-import'
import path from 'path'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    vue(),
    styleImport({
      libs: [
        {
          libraryName: 'element-plus',
          esModule: true,
          ensureStyleFile: true,
          resolveStyle: (name) => {
            return `element-plus/lib/theme-chalk/${name}.css`;
          },
          resolveComponent: (name) => {
            return `element-plus/lib/${name}`;
          },
        }
      ]
    })
  ],

  /**
   * 在生产中服务时的基本公共路径。
   * @default '/'
   */
  base: './',
  /**
  * 与“根”相关的目录,构建输出将放在其中。如果目录存在,它将在构建之前被删除。
  * @default 'dist'
  */
  // outDir: 'dist',
  server: {
    // hostname: '0.0.0.0',
    host: "localhost",
    port: 3001,
    // // 是否自动在浏览器打开
    // open: true,
    // // 是否开启 https
    // https: false,
    // // 服务端渲染
    // ssr: false,
    proxy: {
      '/api': {
        target: 'http://localhost:3333/',
        changeOrigin: true,
        ws: true,
        rewrite: (pathStr) => pathStr.replace('/api', '')
      },
    },
  },
  resolve: {
    // 导入文件夹别名
    alias: {
      '@': path.resolve(__dirname, './src'),
      views: path.resolve(__dirname, './src/views'),
      components: path.resolve(__dirname, './src/components'),
      utils: path.resolve(__dirname, './src/utils'),
      less: path.resolve(__dirname, "./src/less"),
      assets: path.resolve(__dirname, "./src/assets"),
      com: path.resolve(__dirname, "./src/components"),
      store: path.resolve(__dirname, "./src/store"),
      mixins: path.resolve(__dirname, "./src/mixins")
    },
  }
})

踩到坑

npm run dev 打包时不报错,但是在 npm run build 时却报错了,build 的时候会把 node_modules 里面的文件也编译,所以挺多 element-plus 的类型文件报错了。

tsconfig.json 里面的 includeexclude 修改一下就不会了,配置如下

{
  "compilerOptions": {
    "target": "esnext",
    "module": "esnext",
    "moduleResolution": "node",
    "strict": true,
    "jsx": "preserve",
    "sourceMap": true,
    // 忽略 this 的类型检查, Raise error on this expressions with an implied any type.
    "noImplicitThis": false,
    "resolveJsonModule": true,
    "esModuleInterop": true,
    "lib": ["esnext", "dom"],
    "types": ["vite/client"]
  },
  "include": ["/src/**/*.ts", "/src/**/*.d.ts", "/src/**/*.tsx", "/src/**/*.vue"],
  // ts 排除的文件
  "exclude": ["node_modules"]
}

Vue3 + vite2 打包出来的文件和原来 vue2 版的差别也挺大的,由原来 2.5M 直接变成了 1.8M ,amazing!

最后

项目代码大多都是 2 年前的,还有很多可以优化的地方,这次重构的过程没对原来的样式和代码做什么改动,没那么多时间,加上我懒 ????

这次就升级了主要框架与相应的 ui 库,过了一遍 Vue3 中的 API,发现很多 Vue3 中新的 API 都用不上,主要是要熟练一下 Vue3 和 Vite2 项目搭建,这假期也算有所收获。

具体项目源码请看:

https://github.com/biaochenxuying/blog-vue-typescript

至此,一个基于 Vue3 全家桶 + Vite2 + TypeScript + Element Plus 的开发环境已经搭建完毕,现在就可以编写代码了,各个组件的使用方法请参阅它们各自的文档。

不得不说 Vue3 + Element Plus + Vite + TypeScript 是真的香!

推荐一个 Vue3 相关的资料汇总:Vue3 的学习教程汇总、源码解释项目、支持的 UI 组件库、优质实战项目,相信你会挖到矿哦!

推荐阅读

<think>我们正在创建一个使用Vue3、TypeScript、Pinia和ElementPlus的前端项目。根据引用内容,我们可以按照以下步骤进行:1.**使用Vite创建Vue3 +TypeScript项目**:Vite一个快速的前端构建工具,我们可以用它来初始化项目2.**添加VueRouter**:用于路由管理。 3. **添加Pinia**:作为状态管理库,并可以配合持久化插件使用。 4. **添加ElementPlus**:一个基于Vue3的UI组件库。 5. **添加Axios**:用于网络请求。下面我们详细说明每一步。###第一步:创建Vite项目在终端中运行以下命令来创建项目: ```bashnpm createvite@latest``` 然后按照提示操作: -输入项目名称-选择框架:Vue-选择变体:TypeScript创建完成后,进入项目目录并安装依赖: ```bashcd项目名称npminstall``` ###第二步:安装VueRouter在项目目录中运行:```bashnpminstall vue-router@4``` ###第三步:安装Pinia及其持久化插件安装Pinia和持久化插件: ```bashnpm installpinianpminstall pinia-plugin-persistedstate```然后,我们需要创建和配置Pinia。在`src`目录下创建一个`store`目录,并在其中创建`index.ts`文件: ```ts// src/store/index.tsimport {createPinia }from &#39;pinia&#39; importpiniaPluginPersistedstate from&#39;pinia-plugin-persistedstate&#39; constpinia= createPinia() pinia.use(piniaPluginPersistedstate) exportdefault pinia``` 同时,我们可以创建模块,例如`account.ts`(用户账户状态存储):```ts//src/store/account.tsimport {defineStore} from&#39;pinia&#39;export constuseAccountStore =defineStore(&#39;account&#39;, {state: ()=> ({user: null,token:&#39;&#39;}),persist:true,//开启持久化actions: {setUser(user: any) {this.user =user},setToken(token: string) {this.token =token}}}) ```在`main.ts`中引入并使用pinia: ```tsimport {createApp} from&#39;vue&#39; importApp from&#39;./App.vue&#39; importpiniafrom &#39;./store&#39;createApp(App).use(pinia).mount(&#39;#app&#39;)``` ###第四步:安装ElementPlus安装ElementPlus:```bashnpminstall element-plus``` 在`main.ts`中全局引入ElementPlus(或者你也可以按需引入,但全局引入更简单): ```tsimport ElementPlus from&#39;element-plus&#39;import &#39;element-plus/dist/index.css&#39;constapp =createApp(App)app.use(ElementPlus)app.mount(&#39;#app&#39;)``` ###第五步:安装Axios```bashnpminstall axios```然后我们可以封装一个Axios实例,例如在`src/utils/request.ts`中。###第六步:配置Vue Router在`src`目录下创建`router`目录,并在其中创建`index.ts`:```tsimport{ createRouter,createWebHistory,RouteRecordRaw }from &#39;vue-router&#39; constroutes:Array<RouteRecordRaw>= [//定义路由] constrouter =createRouter({history:createWebHistory(),routes})export defaultrouter``` 然后在`main.ts`中引入并使用:```tsimportrouter from&#39;./router&#39; app.use(router)``` ###项目结构最终的项目结构大致如下:``` src/├──assets/├── components/├──router/│└──index.ts├──store/│├── index.ts│└──account.ts├──utils/│└──request.ts├──views/(或pages,用于存放页面组件)├── App.vue└── main.ts``` ###运行项目使用以下命令运行项目:```bashnpmrun dev```这样,我们就完成了使用Vue3、TypeScript、Pinia和Element Plus创建前端项目的基本步骤。§§相关问题§§1.如何配置Vite项目的别名(alias)? 2.在Vue3项目中如何按需引入ElementPlus组件? 3.如何配置Axios的拦截器?</think>### 使用 Vue 3 + TypeScript + Pinia + Element Plus 创建前端项目 以下是完整的创建步骤(基于 Vite 构建工具): #### 1. 创建基础项目 ```bash # 初始化项目 npm create vite@latest my-vue-project -- --template vue-ts # 进入项目目录 cd my-vue-project ``` #### 2. 安装核心依赖 ```bash # 安装 Pinia (状态管理) npm install pinia # 安装 Element Plus (UI 框架) npm install element-plus # 安装图标库 npm install @element-plus/icons-vue # 安装 CSS 预处理器 (可选) npm install sass -D ``` #### 3. 配置 Pinia 在 `src/store/index.ts` 中创建: ```typescript import { createPinia } from &#39;pinia&#39; import piniaPluginPersistedstate from &#39;pinia-plugin-persistedstate&#39; const pinia = createPinia() pinia.use(piniaPluginPersistedstate) // 启用状态持久化 export default pinia // 导出模块 (示例) export * from &#39;./userStore&#39; ``` 创建用户模块 `src/store/userStore.ts`: ```typescript import { defineStore } from &#39;pinia&#39; export const useUserStore = defineStore(&#39;user&#39;, { state: () => ({ name: &#39;&#39;, token: &#39;&#39; }), actions: { login(userData: { name: string; token: string }) { this.name = userData.name this.token = userData.token } }, persist: true // 开启持久化存储 }) ``` #### 4. 配置 Element Plus 在 `src/main.ts` 中全局引入: ```typescript import { createApp } from &#39;vue&#39; import App from &#39;./App.vue&#39; import pinia from &#39;./store&#39; import ElementPlus from &#39;element-plus&#39; import &#39;element-plus/dist/index.css&#39; const app = createApp(App) app.use(pinia) app.use(ElementPlus) app.mount(&#39;#app&#39;) ``` #### 5. 项目结构优化 ``` src/ ├── assets/ ├── components/ ├── store/ # Pinia 状态管理 │ ├── index.ts │ └── userStore.ts ├── views/ # 页面组件 ├── utils/ # 工具函数 ├── router/ # 路由配置 ├── App.vue └── main.ts ``` #### 6. 使用示例 在 Vue 组件中使用 Pinia 和 Element Plus: ```vue <template> <el-button type="primary" @click="login">登录</el-button> <div v-if="userStore.name">欢迎, {{ userStore.name }}</div> </template> <script setup lang="ts"> import { useUserStore } from &#39;@/store/userStore&#39; import { ElMessage } from &#39;element-plus&#39; const userStore = useUserStore() const login = () => { userStore.login({ name: &#39;张三&#39;, token: &#39;abc123&#39; }) ElMessage.success(&#39;登录成功&#39;) } </script> ``` #### 7. 运行项目 ```bash npm run dev ``` ### 关键配置说明 1. **状态持久化**:通过 `pinia-plugin-persistedstate` 插件实现状态自动保存到 localStorage 2. **按需导入**:如需优化体积,可使用 `unplugin-element-plus` 实现组件按需导入 3. **主题定制**:在 `vite.config.ts` 中添加 CSS 配置可自定义 Element Plus 主题 4. **TypeScript 支持**:所有 Pinia store 都具备完整的类型推断 ### 常见问题解决方案 1. **Element Plus 图标不显示**: ```typescript // 在 main.ts 中注册图标 import * as ElementPlusIconsVue from &#39;@element-plus/icons-vue&#39; const app = createApp(App) for (const [key, component] of Object.entries(ElementPlusIconsVue)) { app.component(key, component) } ``` 2. **Pinia 状态重置问题**: ```typescript // 在 store 中启用持久化 persist: { key: &#39;user-store&#39;, // 自定义存储键名 storage: localStorage, // 指定存储方式 paths: [&#39;token&#39;] // 只持久化特定字段 } ``` 3. **Vite 路径别名配置**: ```typescript // vite.config.ts export default defineConfig({ resolve: { alias: { &#39;@&#39;: path.resolve(__dirname, &#39;src&#39;) } } }) ```
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值