ThankNeko's Blog ThankNeko's Blog
首页
  • 操作系统

    • Linux基础
    • Linux服务
    • WindowsServer笔记
    • Ansible笔记
    • Shell笔记
  • 容器服务

    • Docker笔记
    • Kubernetes笔记
    • Git笔记
  • 数据库服务

    • MySQL笔记
    • ELK笔记
    • Redis笔记
  • 监控服务

    • Zabbix笔记
  • Web服务

    • Nginx笔记
    • Tomcat笔记
  • 数据处理

    • Kettle笔记
  • Python笔记
  • Bootstrap笔记
  • C笔记
  • C++笔记
  • Arduino笔记
  • 分类
  • 标签
  • 归档
  • 随笔
  • 关于
GitHub (opens new window)

Hoshinozora

尽人事,听天命。
首页
  • 操作系统

    • Linux基础
    • Linux服务
    • WindowsServer笔记
    • Ansible笔记
    • Shell笔记
  • 容器服务

    • Docker笔记
    • Kubernetes笔记
    • Git笔记
  • 数据库服务

    • MySQL笔记
    • ELK笔记
    • Redis笔记
  • 监控服务

    • Zabbix笔记
  • Web服务

    • Nginx笔记
    • Tomcat笔记
  • 数据处理

    • Kettle笔记
  • Python笔记
  • Bootstrap笔记
  • C笔记
  • C++笔记
  • Arduino笔记
  • 分类
  • 标签
  • 归档
  • 随笔
  • 关于
GitHub (opens new window)
  • Python笔记

  • C笔记

  • C++笔记

  • Arduino笔记

  • Web笔记

    • Html与标签介绍
    • Html常用标签
    • CSS基础
    • JavaScript基础
    • BOM和DOM
    • Bootstrap5
    • Vue介绍
    • Vue指令系统
    • Axios请求
    • Vue计算与监听属性
    • Vue组件
    • NodeJS环境
    • Vue项目
      • Vue项目
        • 介绍
        • Vue‑CLI
        • Vite
        • 父子组件通信
        • ref属性模板
        • parent
        • ref函数
        • reactive函数
        • pinia状态管理器
    • Vue路由
  • Dev
  • Web笔记
Hoshinozora
2025-11-13
目录

Vue项目

# Vue项目

# 介绍

Vue项目是一套完整的前端工程化体系,适合中大型、需要可维护、可扩展的应用。虽然Vue也可以直接在HTML中使用,但只适合一次性页面或学习阶段的快速实验。

一个完整的Vue项目通常包括:

  • 项目脚手架:负责初始化目录、配置构建工具(如Vite、Vue‑CLI)。
  • 组件系统:把页面拆分成若干个单文件组件,支持复用与封装。
  • 响应式API:让数据变化自动映射到视图。
  • 路由和状态管理:帮助组织多页面应用和跨组件共享状态。
  • 生态插件:满足从单页小工具到大型系统的各种需求。
  • 构建与部署:通过Rollup/Vite打包、代码分割、压缩、生成静态资源,配合CI/CD实现持续交付。

# Vue‑CLI

# 介绍使用

Vue CLI是Vue.js官方提供的脚手架工具,用于快速搭建和配置Vue项目,支持工程化开发。

需要先安装Node.js环境,目前Vue-CLI最高支持Node 22版本。

# 安装脚手架
yarn global add @vue/cli

# 创建Vue项目,执行后会弹出交互式界面供我们进行配置
vue create [项目名]

# 选择预设,我们选手动选择
? Please pick a preset:
  Default ([Vue 3] babel, eslint)
  Default ([Vue 2] babel, eslint)
> Manually select features

# 选择要安装的功能,空格选择Babel、Router、Vuex即可,然后回车确认
? Check the features needed for your project: (Press <space> to select, <a> to toggle all, <i> to invert selection, and <enter> to proceed)
>(*) Babel # 向后兼容工具包,能将新JS语法转为旧JS语法以兼容旧浏览器
 ( ) TypeScript
 ( ) Progressive Web App (PWA) Support
 (*) Router # 路由管理包
 (*) Vuex # 状态管理包
 ( ) CSS Pre-processors
 ( ) Linter / Formatter # 格式检查工具,会严格检查JS语法,如行末的分号,所以不安装
 ( ) Unit Testing
 ( ) E2E Testing

# 选择Vue版本
? Choose a version of Vue.js that you want to start the project with (Use arrow keys)
> 3.x
  2.x

# 选Y使用历史模式进行路由管理
# 不启用则默认使用哈希模式,URL中会使用#,浏览器只在前端解析它,不会把#及其后面的部分发送给服务器
# 启用历史模式则URL中不会使用#,而是和普通URL一致,浏览器会把完整的路径发送给服务器
Use history mode for router? (Requires proper server setup for index fallback in production) (Y/n)

# 选择第一个将Babel配置文件放在专用文件
Where do you prefer placing config for Babel, ESLint, etc.? (Use arrow keys)
> In dedicated config files
  In package.json

# 是否将配置保存为预设,保存的话,下次再创建项目时可以直接使用该预设进行搭建
Save this as a preset for future projects? (y/N)

# 设置预设的名称
Save preset as: Vue3

# 选择模块包管理工具
? Pick the package manager to use when installing dependencies: (Use arrow keys)
> Use Yarn
  Use NPM

# 然后等待项目搭建完毕即可
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
44
45
46
47
48
49
50
51

# 项目结构

根目录

文件/目录 主要作用
node_modules/ 存放安装的所有依赖包,构建、运行时会直接引用这里的代码。
public/ 存放不会被WebPack处理的静态资源,例如index.html(唯一入口页面)、favicon.ico(图标)等。
src/ 项目源码的核心目录,所有Vue组件、路由、状态管理、业务代码均放在这里。
package.json 项目元信息、依赖声明、脚本命令。
README.md 项目说明文档,通常包含项目简介、启动方式、目录结构概览等。
.gitignore 指定 Git 在提交时忽略的文件或目录,如 node_modules、dist。
babel.config.js Babel 编译配置,负责把ES6+代码转为兼容性更好的ES5。
vue.config.js Vue‑CLI的可选配置文件,用于自定义WebPack、代理、路径别名等。
jsconfig.json 用于为编辑器提供JavaScript项目的类型信息、路径别名以及编译选项。

./public/index.html:单页面应用的唯一入口页面,其中的<div id="app"></div>是Vue的挂载点。

src目录

文件/目录 主要作用
assets/ 存放会被WebPack处理的静态资源,例如图片、字体、全局样式等。
components/ 可复用的小组件,例如工具组件、UI组件等。
views/ 路由对应的页面级组件。
router/ 路由配置,定义路径和组件的映射。
utils/ 项目通用的工具函数。
api/ 与后端交互的封装(axios实例、接口函数)。
store/ Vuex状态管理,模块化存放
App.vue 根组件,包含全局布局、<router-view/>占位。
main.js 入口文件,创建 Vue 实例、挂载根组件、引入路由/状态管理。

# 运行项目

# 运行命令在生成的package.json文件的scripts字段中可以查看到
# 然后直接在项目目录用命令行执行即可

# 开启服务端并监听HTTP请求,适用于调试阶段
yarn serve

# 将Vue项目打包成静态资源文件,会保存到项目根目录的dist目录中
yarn build
1
2
3
4
5
6
7
8

# Vite

# 介绍及特点

Vite是由Vue官方主导开发的新一代前端构建工具。相较于Vue CLI来说它的构建打包、服务器启动、热更新的速度更加快,所以推荐新项目首选Vite。

他有如下几个特点:

极速冷启动:启动本地HTTP服务器时无需打包,直接利用浏览器原生ES模块加载源码。

按需编译:使用本地HTTP服务器时,只编译当前页面用到的文件。

支持多框架:不仅支持Vue,还支持React、Svelte、Lit等。

现代化:基于 ESBuild(Go编写,构建速度快) + Rollup(生产打包文件更小)。

# 创建项目

# 安装并创建项目
yarn create vite my-app --template vue

# 是否使用实验性构建底层工具,选择No
Use rolldown-vite (Experimental)?:
> No

# 是否使用yarn安装并立即启动,选择YES
Install with yarn and start now?
> Yes


1
2
3
4
5
6
7
8
9
10
11
12

# 插件安装

创建的项目默认不带任何插件,需要自行安装挂载存储、路由等插件。

# 安装存储、路由、HTTP请求插件
yarn add pinia vue-router axios

# 安装自动导入组件、自动导入API插件,使用后无需手动ref, computed等接口
yarn add -D unplugin-auto-import unplugin-vue-components
1
2
3
4
5

# 配置文件

vite的配置在项目目录下的 vite.config.js 文件中。

import {defineConfig} from 'vite'
import vue from '@vitejs/plugin-vue'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import {fileURLToPath, URL} from 'url'

export default defineConfig({

    // 部署时的公共基础路径,会在所有资源路径前加上
    base: '/',

    // 定义使用的插件列表
    plugins: [

        // 启用Vue单文件组件支持
        vue(),

        // 自动导入Vue相关API
        AutoImport({
            // 要自动导入的库
            imports: ['vue', 'vue-router'],
            // 自动扫描并导入目录下的函数(必须以use开头)
            dirs: ['src/stores'],
            // 不生成类型声明文件(因为不用TS)
            dts: false,
            // 在<template>中也能使用(比如@click="increment")
            vueTemplate: true,
        }),

        // 自动导入并注册组件
        Components({
            // 组件目录(默认就是src/components)
            dirs: ['src/components'],
            // 不生成类型声明
            dts: false,
            // 允许子目录组件(如components/layout/Header.vue → 可用为LayoutHeader)
            deep: true,
            // 组件名格式,保持原名(不加前缀)
            directoryAsNamespace: false
        }),

    ],


    // 模块解析配置
    resolve: {
        // 路径别名,让@指向src目录,使路径支持@/写法,避免../../写法
        alias: {
            '@': fileURLToPath(new URL('./src', import.meta.url)),
        },
    },

    // 开发服务器配置(在vite dev时生效)
    server: {
        // 监听端口
        port: 3000,
        // 启动时是否自动打开浏览器
        open: true,
        // 是否严格使用指定端口(被占用时退出而非换端口)
        strictPort: false,
        // 允许局域网访问
        host: true,

        // 代理配置(可用于解决开发时跨域问题)
        proxy: {
            // 将/api开头的请求代理到后端服务
            '/api': {
                // 目标后端地址
                target: 'http://localhost:8080',
                // 是否更改 origin 头(一般设为 true)
                changeOrigin: true,
                // 重写路径去掉/api前缀再转发,例如/api/user → 转发为/user
                rewrite: (path) => path.replace(/^\/api/, ''),
            },

        }
    },

    // 构建配置(在vite build时生效)
    build: {
        // 输出目录
        outDir: 'dist',
        // 是否生成source map,用于调试生产环境问题,建议开发时使用
        sourcemap: true,
        // 压缩工具:esbuild(速度更快) 或 terser(压缩率更高)
        minify: 'terser',
        // Terser压缩选项(仅在使用terser时生效)
        terserOptions: {
            compress: {
                // 是否删除console.log、debugger等,生产环境推荐
                drop_console: true,
                drop_debugger: true,
            },
        },
        rollupOptions: {
            output: {
                // 将第三方依赖从应用代码文件中拆分出来,打包成独立的JS文件,让依赖代码文件可以长期缓存,不用每次修改业务代码都要重新下载一遍依赖文件
                manualChunks(id) {
                    if (id.includes('node_modules')) {
                        return 'vendor';
                    }
                },
            }
        }
    }
})
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106

# 使用路由

// src/router/index.js

import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '@/views/HomeView.vue'
import AboutView from '@/views/AboutView.vue'

const routes = [
  {
    path: '/',
    name: 'home',
    component: HomeView,
  },
  {
    path: '/about',
    name: 'about',
    component: AboutView,
  },
]

const router = createRouter({
  history: createWebHistory(),
  routes,
})

export default router
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
// src/main.js

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

createApp(App).use(router).mount('#app')
1
2
3
4
5
6
7
8

# 环境变量

vite支持在 .env 文件中定义环境变量,且可以在代码中使用,但只有以 VITE_ 为前缀的变量才会暴露出来供代码使用。

# .env文件中定义
VITE_API_BASE_URL=https://api.example.com

# JS代码中使用
console.log(import.meta.env.VITE_API_BASE_URL)
1
2
3
4
5

# 运行项目

# 开启服务端并监听HTTP请求,适用于调试阶段
yarn dev

# 将Vue项目打包成静态资源文件,会保存到项目根目录的dist目录中
yarn build

# 预览生产版本
yarn preview
1
2
3
4
5
6
7
8

# 父子组件通信

# 介绍

组件之间的数据是独立的,需要通信来传递数据。父传子用Props,子传父用Events。

父组件用在子组件引用标签内用 v-bind 传递数据,子组件用 props 接收数据。

子组件用Vue专属的方法 this.$emit 发送自定义事件来传递数据,父组件通过事件处理函数接收数据。

# 例子

ParentView.vue 父组件文件(选项式)

<template>
  <div id="parent">
    <h1>{{ say }}</h1>
    <!--
        :parent_name='name' 通过属性传递数据给子组件
        @child-btn="handleChildBtn" 定义并绑定自定义事件,用于接收处理子组件发来的数据
     -->
    <Child :parent_name='name' @child-btn-click="handleChildBtn"></Child>
  </div>
</template>

<script>
// 导入子组件
import Child from '@/components/Child';

export default {
  name: "ParentView",
  data() {
    return {
      say: "I'm Parent",
      name: "Evil",
    };
  },
  methods: {
    handleChildBtn(data) {
      console.log("收到子组件发来的数据 => ", data)
    }
  }
  ,
// 注册组件
  components: {
    Child
  }
}
</script>
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

Child.vue 子组件文件(选项式)

<template>
  <div id="child">
    <!-- 使用父组件传递过来的数据 -->
    <p>My parent is => {{ parent_name }}</p>
    <!-- 触发函数发送自定义事件 -->
    <button @click="sendData">点我</button>
  </div>
</template>

<script>
export default {
  name: "Child",
  // 使用props接收父组件传递过来的参数,接收后可直接使用
  props: ["parent_name"],
  methods: {
    sendData() {
      // 触发Vue自定义事件发送数据
      this.$emit("child-btn-click", ["你好", "我是子组件的数据"])
    }
  }
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

组合式需要使用 defineProps(['参数名',...]) 来接收。

# props参数

我们还可以在子组件的props中指定参数的默认值、是否必传、类型等。

<script>
export default {
  name: "Child",
  props: {
    name: {
        // 指定name参数是否必传
        required: true,
        // 指定参数类型,如果传入值不符合该类型会报错
        type: String
    },
    message: {
        // 父组件没有传该参数时message的默认值
        default: "hello"
    }
  }
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# ref属性模板

# 介绍

ref属性用于直接引用DOM元素或组件实例,在模板中通过 ref 属性给元素或子组件打上标记,然后在JS代码中就能直接对元素或子组件进行引用。

// 标签添加ref标记
<div ref="myDiv">I am tag</div>
<Child ref="myChild">I am component</Child>
1
2
3

# 选项式使用

选项式API需要通过 this.$refs.[ref属性值] 对象来引用,它包含所有带有 ref 属性的元素或组件,在 mounted 钩子之后才能访问到 $refs。

<template>
  <div id="parent">
    <!-- 给元素添加ref属性 -->
    <div ref="myDiv">{{ say }}</div>
    <!-- 给子组件实例添加ref属性 -->
    <Child ref="myChild"></Child>
    <button @click="handleBtn">点我</button>
  </div>
</template>

<script>
// 导入组件
import Child from '@/components/Child';

export default {
  name: "ParentView",
  data() {
    return {
      say: "I'm Parent",
    };
  },
  methods: {
    handleBtn() {
      // 获取并修改元素的文本内容
      this.$refs.myDiv.textContent = "Who are you?"
      // 获取并修改子组件中的响应式属性
      this.$refs.myChild.name = "you are child"
    }
  }
  ,
// 注册组件
  components: {
    Child
  }
}
</script>
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

# 组合式使用

组合式API中ref属性需要绑定到名称相同的响应式对象变量来引用,然后通过 [变量名].value 来访问。

<template>
  <div id="parent">
    <!-- 将元素绑定到ref对象 -->
    <div ref="myDiv">{{ say }}</div>
    <!-- 将子组件实例绑定到ref对象 -->
    <Child ref="myChild"/>
    <button @click="handleBtn">点我</button>
  </div>
</template>

<script>
import {ref} from 'vue'
import Child from '@/components/Child'

export default {
  name: 'ParentView',
  components: {
    Child
  },
  setup() {
    // 响应式数据
    const say = ref("I'm Parent")
    // 用于引用元素或子组件实例
    const myDiv = ref(null)
    const myChild = ref(null)
    const handleBtn = () => {
      // 修改元素的文本内容
      myDiv.value.textContent = 'Who are you?'
      // 修改子组件中的响应式属性
      myChild.value.name = 'you are child'
    }
    // 返回模板中需要使用的变量和函数
    return {
      say, myDiv, myChild, handleBtn
    }
  }
}
</script>
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

# parent

子组件可以通过 this.$parent.[名称] 来访问父组件的变量或对象。

<template>
  <div id="child">
    <p>I am {{ name }}</p>
    <p>{{ test() }}</p>
  </div>
</template>

<script>
export default {
  name: "Child",
  data() {
    return {name: "John"}
  },
  methods:{
    test(){
      return this.$parent.say
    }
  }
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# ref函数

# 介绍

ref(原始值) 用于创建响应式数据,它接收一个原始值,并将其作为内部值返回一个ref对象 (响应式引用对象),该对象的内部有一个 .value 属性,用于访问或修改内部值。

Vue3的响应式系统基于Proxy,但Proxy只能代理对象,所以就需要ref函数来将原始值包装成对象。

# 组合式例子

选项式会自动将data选项返回的数据包装成响应式,无需使用ref()函数。

<template>
  <div class="hello">
    {{ msg }}
    <input type="text" v-model="msg" />
    <button @click="handleBtn">点我</button>
  </div>
</template>

<script>
import {ref} from 'vue'

export default {
  name: "Hello",
  setup() {
    // 定义响应式对象变量
    let msg = ref("I am msg!")
    // 修改响应式对象的值,响应式数据更新后会实时同步到页面
    const handleBtn = () => {
      msg.value = "I am msg again!"
    }
    return { msg, handleBtn }
  }
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

在模板中使用响应式数据时,不需要写.value,Vue会自动解包。

# reactive函数

reactive() 和 ref() 函数作用类似,不同的是 reactive() 用于将一个普通对象转换为响应式对象。

# 组合式例子

<script setup>
import { reactive } from 'vue'

const state = reactive({
  count: 0,
  name: 'Alice',
  items: []
})

// 解构需要使用toRefs才能保持响应性
const { count } = toRefs(state)
</script>
1
2
3
4
5
6
7
8
9
10
11
12

# pinia状态管理器

# 介绍

Pinia是Vue官方推荐的状态管理库,在Vue中,状态就是组件里保存的数据,而状态管理器就是一个全局的数据中心,所有组件都可以直接读写这个中心的数据,无需层层传递。

如果不使用状态管理器,多个组件想要共享数据则只能通过如下方式:

  • 通过props一层层传(祖传 props)
  • 用事件 $emit 一层层冒泡(回调地狱)
  • 用 mitt / eventBus 全局发消息(难以追踪)

会导致代码混乱、难以维护、容易出错等问题。

# 使用方式

  1. 安装Pinia
npm install pinia
1
  1. 在main.js中启用Pinia
// 导入方法
import { createPinia } from 'pinia'

// 创建pinia实例
const pinia = createPinia()

// 将pinia插件挂载到Vue应用
app.use(pinia)
1
2
3
4
5
6
7
8
  1. 创建一个Store(仓库)

例如:src/stores/user.js

import { defineStore } from 'pinia'

// 定义一个叫user的store
// defineStore会返回一个函数,用于返回同一个store实例
export const useUserStore = defineStore('user', {
  // state必须是一个函数,用于返回初始数据,相当于组件里的data,会自动将返回的数据转为响应式数据
  state: () => ({
    name: '',
    avatar: '',
    isLoggedIn: false
  }),

  // actions用于修改state
  actions: {
    login(name, avatar) {
      this.name = name
      this.avatar = avatar
      this.isLoggedIn = true
    },
    logout() {
      this.name = ''
      this.avatar = ''
      this.isLoggedIn = false
    }
  }
})
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
  1. 在组件中修改Store中的状态

例如:view/LoginView.vue

<template>
  <button @click="handleLogin">模拟登录</button>
</template>

<script setup>
// 导入定义的store
import { useUserStore } from '@/stores/user'

// 获取store实例
const userStore = useUserStore()

function handleLogin() {
  // 调用action修改store中的状态
  userStore.login('小明', 'avatar.jpg')
  // Pinia也支持直接修改属性
  userStore.name = "小张"
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  1. 在组件中读取Store中的状态

例如:components/Navbar.vue

<template>
  <!-- 直接通过"实例.数据名"即可访问 -->
  <div v-if="userStore.isLoggedIn">
    欢迎,{{ userStore.name }}!
    <img :src="userStore.avatar" />
  </div>
  <div v-else>请登录</div>
</template>

<script setup>
import { useUserStore } from '@/stores/user'

// 获取store实例
const userStore = useUserStore()
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 解构数据

解构state会丢失响应性,结构出来的数据会变成普通值。

import { storeToRefs } from 'pinia'
const { count } = storeToRefs(useCounterStore())
1
2
#前端#Vue#项目
NodeJS环境
Vue路由

← NodeJS环境 Vue路由→

最近更新
01
Vue路由
12-09
02
FastAPI实现用户管理
11-23
03
Tortoise ORM
11-23
更多文章>
Theme by Vdoing | Copyright © 2022-2026 Hoshinozora | MIT License
湘ICP备2022022820号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式