Vue3项目的搭建与使用

最后更新:2023-03-26

官方地址:Vue - 快速上手

前言

因前端各方面技术更新速度极快,网络上的各类教程可能都会使用不同的方式,其中大部分具有时效性,故决定写一篇时时更新的教程,并记录最新更新时间与所使用各项技术的版本,确保并不过时。

本文专注于Vue3+TypeScript+组合式API+常用组件的前端项目搭建,不涉及Git或VSCode之类相关工具的安装。

工具版本

git: 2.40.0.windows.1
node:14.18.0 | 18.15.0 | 19.8.1
vite: 4.2.1
vue-cli: 2.9.6

项目构建

一、使用Vite构建项目

Vite | 下一代的前端工具链

开发环境下的性能比Vue cli要快10~100倍。

  1. 初始化项目

    1. 使用Git BashcmdVSCode打开一处文件夹
    2. 执行指令npm create vite@latest
      • 接下来出现的选项,单选项可以通过方向键选择,或输入YN进行是否选择,多选项通过空格选中,回车确认
    3. 若为初次使用,可能会提示Need to install the following packages: create-vite@latest Ok to proceed? (y),需要安装create-vite
      • 输入y,确认安装
    4. 提示Project name:,输入项目名称
    5. 提示Select a framework:,选择一个前端框架
      • 选择Vue
    6. 提示Select a variant:,选择一种变体
      • 选择Customize with create-vue
        • 显示Vue.js - The Progressive JavaScript Framework标题,进入Vue的项目构建流程
    7. 提示Add TypeScript?,是否添加TypeScript
      • 选择Yes,使用TypeScript作为脚本语言
    8. 提示Add JSX Support?,是否添加JSX支持
      • 选择No,暂时不需要JSX支持
    9. 提示Add Vue Router for Single Page Application development?,是否添加Vue Router
      • 选择Yes,使用Vue Router作为路由工具
    10. 提示Add Pinia for state management?,是否添加Pinia
      • 选择Yes,使用Pinia作为项目全局状态管理工具
    11. 提示Add Vitest for Unit Testing?,是否添加Vitest
      • 选择No,暂不使用单元测试
    12. 提示Add an End-to-End Testing Solution?,是否添加端到端测试方案
      • 选择No,暂不需要端到端的自动化测试
    13. 提示Add ESLint for code quality?,是否添加ESLint
      • 选择Yes
    14. 提示Add Prettier for code formatting?,是否添加Prettier
      • 选择Yes,使用ESLint + Prettier作为代码质量检查工具
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    > npm create vite@latest
    Need to install the following packages:
    create-vite@latest
    Ok to proceed? (y) y
    √ Project name: ... vite-project
    √ Select a framework: » Vue
    √ Select a variant: » Customize with create-vue ↗

    Vue.js - The Progressive JavaScript Framework

    √ Add TypeScript? ... No / Yes
    √ Add JSX Support? ... No / Yes
    √ Add Vue Router for Single Page Application development? ... No / Yes
    √ Add Pinia for state management? ... No / Yes
    √ Add Vitest for Unit Testing? ... No / Yes
    √ Add an End-to-End Testing Solution? » No
    √ Add ESLint for code quality? ... No / Yes
    √ Add Prettier for code formatting? ... No / Yes
  2. 等待项目构建,提示如下时说明构建完成

    1
    2
    3
    4
    5
    6
    7
    8
    Scaffolding project in E:\Other Project\vite\vite-project...

    Done. Now run:

    cd vite-project
    npm install
    npm run format
    npm run dev
  3. 项目结构

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    vite-project
    |- node_modules
    |- public
    |- src
    | |- assets
    | |- components
    | |- router
    | |- stores
    | |- views
    | |- App.vue
    | |- main.ts
    |- .eslintrc.cjs
    |- .prettierrc.json
    |- index.html
    |- package.json
    |- tsconfig.json
    |- vite.config.js
    |- README.md

    package.json

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    {
    "name": "vite-project",
    "version": "0.0.0",
    "private": true,
    "scripts": {
    "dev": "vite",
    "build": "run-p type-check build-only",
    "preview": "vite preview",
    "build-only": "vite build",
    "type-check": "vue-tsc --noEmit",
    "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
    "format": "prettier --write src/"
    },
    "dependencies": {
    "pinia": "^2.0.32",
    "vue": "^3.2.47",
    "vue-router": "^4.1.6"
    },
    "devDependencies": {
    "@rushstack/eslint-patch": "^1.2.0",
    "@types/node": "^18.14.2",
    "@vitejs/plugin-vue": "^4.0.0",
    "@vue/eslint-config-prettier": "^7.1.0",
    "@vue/eslint-config-typescript": "^11.0.2",
    "@vue/tsconfig": "^0.1.3",
    "eslint": "^8.34.0",
    "eslint-plugin-vue": "^9.9.0",
    "npm-run-all": "^4.1.5",
    "prettier": "^2.8.4",
    "typescript": "~4.8.4",
    "vite": "^4.1.4",
    "vue-tsc": "^1.2.0"
    }
    }

二、使用Vue Cli构建项目

Vue Cli - 创建一个项目

Vue Cli目前已不再更新,处于维护状态,可能会过时。

  1. 安装Vue Cli

    1. 打开git bashcmdvscode
    2. 在命令行执行npm install -g @vue/cli
  2. 初始化项目

    1. 使用git bashcmdvscode打开一处文件夹
    2. 执行指令vue create <项目名称>
      • 接下来出现的选项,单选项可以通过方向键选择,或输入YN进行是否选择,多选项通过空格选中,回车确认
    3. 提示Please pick a preset:,选择项目预设
      • 选择Manually select feature,手动选择所需功能
    4. 提示Check the features needed for your project:,选择所需功能
      • 选中BabelTypeScriptRouterVuexLinter / Formatter这几项
      • Babel用于将ES6、ES7的语法转为ES5的语法
      • 使用TypeScript作为项目的脚本语言
      • 使用Router作为项目的路由工具
      • 使用Vuex作为项目的全局状态管理工具
      • 选中Linter / Formatter说明需要代码质量检查工具
    5. 提示Choose a version of Vue.js that you want to start the project with,选择所用的Vue版本
      • 选择3.x,使用Vue3
    6. 提示Use class-style component syntax?,是否使用类风格语法
      • 输入N,不使用
    7. 提示Use Babel alongside TypeScript (required for modern mode, auto-detected polyfills, transpiling JSX)?,是否使用Babel自动为转换后的TypeScript代码注入polyfills
      • 输入Y,使用
    8. 提示Use history mode for router? (Requires proper server setup for index fallback in production),是否使用Router的历史模式
      • 输入Y,使用
    9. 提示Pick a linter / formatter config:,选择代码检查工具
      • 选择ESLint + Prettier
    10. 提示Pick additional lint features:,选择何时进行代码检查
      • 选中Lint on save,在保存后检查
    11. 提示Where do you prefer placing config for Babel, ESLint, etc.?,选择配置文件的存放位置
      • 选择In dedicated config files,为工具的配置分别生成独立文件,而不是都放在*package.json`中
    12. 提示Save this as a preset for future projects? (y/N),是否将本次的选项保存为预设
      • 输入YN皆可,保存后可在之后新建项目时的第三步看到
      • Y:提示Save preset as:,为预设起名后回车
    13. 等待项目构建
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    > vue create vue-project


    Vue CLI v5.0.8
    ? Please pick a preset: Manually select features
    ? Check the features needed for your project: Babel, TS, Router, Vuex, Linter
    ? Choose a version of Vue.js that you want to start the project with 3.x
    ? Use class-style component syntax? No
    ? Use Babel alongside TypeScript (required for modern mode, auto-detected polyfills, transpiling JSX)? Yes
    ? Use history mode for router? (Requires proper server setup for index fallback in production) Yes
    ? Pick a linter / formatter config: Prettier
    ? Pick additional lint features: Lint on save
    ? Where do you prefer placing config for Babel, ESLint, etc.? In dedicated config files
    ? Save this as a preset for future projects? Yes
    ? Save preset as: vue-ts
  3. 提示如下则构建成功

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    🎉  Preset vue-ts saved in C:\Users\zzz\.vuerc


    Vue CLI v5.0.8
    ✨ Creating project in E:\Other Project\vue-project.
    🗃 Initializing git repository...
    ⚙️ Installing CLI plugins. This might take a while...


    added 885 packages in 2m
    🚀 Invoking generators...
    📦 Installing additional dependencies...


    added 132 packages in 30s
    ⚓ Running completion hooks...

    📄 Generating README.md...

    🎉 Successfully created project vue-project.
    👉 Get started with the following commands:

    $ cd vue-project
    $ npm run serve
  4. 项目结构

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    vite-project
    |- node_modules
    |- public
    |- src
    | |- assets
    | |- components
    | |- router
    | |- store
    | |- views
    | |- App.vue
    | |- main.ts
    |- .eslintrc.js
    |- package.json
    |- tsconfig.json
    |- vue.config.js
    |- README.md

    package.json

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    {
    "name": "vue-project",
    "version": "0.1.0",
    "private": true,
    "scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "lint": "vue-cli-service lint"
    },
    "dependencies": {
    "core-js": "^3.8.3",
    "vue": "^3.2.13",
    "vue-router": "^4.0.3",
    "vuex": "^4.0.0"
    },
    "devDependencies": {
    "@typescript-eslint/eslint-plugin": "^5.4.0",
    "@typescript-eslint/parser": "^5.4.0",
    "@vue/cli-plugin-babel": "~5.0.0",
    "@vue/cli-plugin-eslint": "~5.0.0",
    "@vue/cli-plugin-router": "~5.0.0",
    "@vue/cli-plugin-typescript": "~5.0.0",
    "@vue/cli-plugin-vuex": "~5.0.0",
    "@vue/cli-service": "~5.0.0",
    "@vue/eslint-config-typescript": "^9.1.0",
    "eslint": "^7.32.0",
    "eslint-config-prettier": "^8.3.0",
    "eslint-plugin-prettier": "^4.0.0",
    "eslint-plugin-vue": "^8.0.3",
    "prettier": "^2.4.1",
    "typescript": "~4.5.5"
    }
    }

组件安装与使用

往现有项目中添加组件的方式

  1. npminstall指令
    • 传统方式,安装组件包,手动写代码来引入所需内容
  2. Vue Clivue add指令
    • 将其他组件作为插件添加,可能会对现有代码进行变动,建议在commit后再添加

Vuex

Vuex

老牌状态管理工具

  1. npm方式

    1. 安装

      • 执行npm install vuex@next --save安装Vuex
    2. 引入

      1. 创建src/store/文件夹,在其中创建index.ts文件,内容如下:

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        import { createStore } from "vuex";

        export default createStore({
        state: {},
        getters: {},
        mutations: {},
        actions: {},
        modules: {},
        });

      2. src/main.ts中使用store

        1
        2
        3
        4
        5
        import store from "@/store";

        // ...

        app.use(store);
    3. 版本:vuex@4.0.2

  2. Vue Cli方式

    • 执行vue add vuex,该指令会自动创建src/store/index.ts,并在main.ts中使用
    • 版本:vuex@4.1.0
  3. 使用范例

    store/index.ts

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    import { createStore } from "vuex";

    export default createStore({
    state: {
    counter: 0,
    },
    getters: {},
    mutations: {},
    actions: {},
    modules: {},
    });

    MyComponent.vue

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <template>
    <button @click="counter++">{{ counter }}</button>
    </template>

    <script setup lang="ts">
    import { ref } from "vue";
    import store from "@/store";

    const counter = ref(store.state.counter);
    </script>

Pinia

Pinia | 符合直觉的Vue.js 状态管理库

新的状态管理工具,API比Vuex更简单,提供了组合式API。

  1. npm方式

    1. 安装

      • 执行npm install pinia,安装Pinia
    2. 引入

      • src/main.ts中使用pinia

        1
        2
        3
        4
        5
        import { createPinia } from "pinia";

        // ...

        app.use(createPinia());
    3. 版本:pinia@2.0.32

  2. vue-cli方式

    • 执行vue add vue-cli-plugin-pinia,该指令会自动在main.ts中使用Pinia,并在scr/storesrc/stores下生成一个样例
    • 版本:pinia@^2.0.28
  3. 使用范例

    store/counter.ts

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    import { ref, computed } from 'vue'
    import { defineStore } from 'pinia'

    export const useCounterStore = defineStore('counter', () => {
    const count = ref(0)
    const doubleCount = computed(() => count.value * 2)
    function increment() {
    count.value++
    }

    return { count, doubleCount, increment }
    })

    MyComponent.vue

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <template>
    <button @click="counter.count++">{{ counter.count }}</button>
    </template>

    <script setup lang="ts">
    import { useCounterStore } from "@/store";

    const counter = useCounterStore();
    </script>

Vue Router

Vue Router | Vue.js 的官方路由

  1. npm方式:

    1. 安装

      • 执行npm install vue-router@4,安装Vue Router
    2. 引入

      1. 创建src/router文件夹,在其中创建index.ts文件,内容如下:

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        import { createRouter, createWebHistory } from 'vue-router'
        import HomeView from '../views/HomeView.vue'

        const router = createRouter({
        history: createWebHistory(import.meta.env.BASE_URL),
        routes: [
        {
        path: '/',
        name: 'home',
        component: HomeView
        },
        {
        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('../views/AboutView.vue')
        }
        ]
        })

        export default router

      2. routes数组内容改为项目中的实际组件内容

      3. src/main.ts中使用router

        1
        2
        3
        4
        5
        import router from "@/router";

        // ...

        app.use(router);
    3. 版本:vue-router@4.1.6

  2. 使用范例

    router/index.ts

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    import { createRouter, createWebHashHistory, RouteRecordRaw } from "vue-router";

    const routes: Array<RouteRecordRaw> = [
    {
    path: "/title",
    name: "title",
    component: () => import("../views/TitleView.vue"),
    },
    {
    path: "/table",
    name: "table",
    component: () => import("../views/TableView.vue"),
    },
    ];

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

    export default router;

    App.vue

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <template>
    <router-view />
    </template>

    <script setup lang="ts">
    import router from "@/router";

    router.push("/title");
    </script>

    TitleView.vue

    1
    2
    3
    4
    5
    6
    7
    <template>
    <button @click="router.push('/table')">开始</button>
    </template>

    <script setup lang="ts">
    import router from "@/router";
    </script>

    TableView.vue

    1
    2
    3
    4
    5
    6
    7
    <template>
    <button @click="router.back()">返回</button>
    </template>

    <script setup lang="ts">
    import router from "@/router";
    </script>

Vue3+组合式API+TypeScript

<script setup>

组合式 API 常见问答

说到底,就是因为TypeScript、组合式API和setup语法糖写起来更舒服。

  1. 说明

    1. Vue3已不推荐使用class-style
    2. 通过defineProps这个全局方法来定义props,需要默认值时则通过withDefaults方法处理
    3. 所有顶层量都会默认可提供给模板使用
  2. 使用范例

    MyComponent.vue

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    <template>
    <div>{{ data }}</div>
    <div>{{ computedData }}</div>
    <button @click="func()">func button</button>
    </template>

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

    /* props */

    const props = defineProps<{
    // ...
    }>();

    /* props with default */

    export interface Props {
    msg?: string
    labels?: string[]
    }

    const props = withDefaults(defineProps<Props>(), {
    msg: 'hello',
    labels: () => ['one', 'two']
    });

    /* data */

    const data = 1;

    const msg = ref(props.msg);

    /* computed */

    const computedData = computed(() => data * 2);

    /* methods */

    function func(): void {
    console.log("func");
    }
    </script>

问题与解决方案

cmd:无法加载文件 vue.ps1 因为在此系统上禁止运行脚本

问题:使用cmd执行vue指令时,可能会出现错误信息:cmd:无法加载文件 vue.ps1 因为在此系统上禁止运行脚本。

解决方案:以管理员身份运行cmd或者Windows PowerShell,执行set-ExecutionPolicy RemoteSigned,选择A

项目打包后运行,页面空白

问题:通过npm run build指令构建打包后本地打开index.html,页面全白无内容

  1. 使用Vue Cli的情况

    原因:默认资源路径错误与。

    Vue Cli的默认资源路径为”/“,而非”./“。

    解决方案:修改vue.config.js文件,添加publicPath配置

    1
    2
    3
    4
    5
    6
    const { defineConfig } = require("@vue/cli-service");
    module.exports = defineConfig({
    transpileDependencies: true,
    publicPath: "./",
    });

  2. 使用Vite的情况

    原因:Vite打包后的项目不支持file引用协议,产生跨域错误,需要用服务器方式部署打开。

    控制台:

    1
    Access to script at 'file:///xxx.js' from origin 'null' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, isolated-app, chrome-extension, chrome, https, chrome-untrusted.

    解决方案:不应使用这种方式来访问由Vite搭建的项目。

    官方解释:故障排除 | Vite - 构建

    希望可以直接通过打开index.html文件的方式来打开项目,实际上是希望可以有一个本地的图标入口来打开项目。这样的话可以考虑使用Electron进行包装,有待研究。

    网络上有的方案是使用legacy插件并在项目build后做二次处理,这既不优雅也不合理。

路由404

问题:路由跳转后出现404错误

原因:路由模式错误。

Vue Router默认的历史记录模式为hash的,但是对应的创建方法并不是与hash对应的。

解决方案:修改routr/index.ts文件,将routerhostory创建方法改为createWebHashHistory

1
2
3
4
const router = createRouter({
history: createWebHashHistory(process.env.BASE_URL),
routes,
});