Vue 项目架构

Vue 项目架构 #

在上一页我们创建了一个初始的 vue2/vue3 项目,现在本文将对这个几乎空白的项目框架进行简要介绍。

阅读本文时并不要求理解 Vue 项目中各部分是如何编写的,只大致了解它们的用途

总体概述 #

下面是我们创建的项目的文件目录:

vue-structure

简要介绍如下:

目录/文件说明
node_modulesnpm 加载的项目依赖模块
public静态资源,build 构建后为根目录,含网站导航栏图标、首页入口文件
src开发做的事情基本都在这个目录下,含:
assets: 放置一些图片、字体等资源
components: 放置组件文件,一般为全局组件
router: 网站路由跳转设置
store: 前端数据存储
views: 放置各页面文件
App.vue: 项目入口文件
main.js: 项目的核心文件,在这里可以导入各种全局依赖
.xxx文件配置文件,包括语法配置、git配置(.gitignore)等
package.json项目 npm 包配置文件
package-lock.json项目 npm 包版本控制文件
README.md项目的说明文档

下面对主要模块进行讲解

node_modules #

node_modules 目录下放置项目所需要的依赖环境,当使用 npm install packageName --save 命令给项目添加依赖时,信息会记录于 package.json 文件的 dependencies 中,而依赖的包将存储于 node_modules。

在该目录下,可以发现有我们在创建项目时选择的 vue-router 包,这是用于管理路由跳转的依赖。之后如果引入了某个 npm 包后,由于文档缺失等原因不熟悉其中的某个 API,可以进入对应的文件夹查看源码。

在编辑器中可以看到该目录名字显示灰色的,因为它在 .gitignore 是被忽略的。在合作开发项目中,往往使用 git 来协调配合,而该目录内容庞大,且并非由程序员编写,因此往往不加入 git 中,在新机器上使用 npm install,则会根据 package.json 的配置信息生成 node_modules。

public #

初始项目中,public 目录下有 favicon.ico 和 index.html 两个文件,前者是网站图标 logo,注意如果说我们自己的项目也希望使用个性化的网站图标,那么必须是 ico 格式的;后者是网站首页入口文件,代码如下:

<!DOCTYPE html>
<html lang="">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
    <title><%= htmlWebpackPlugin.options.title %></title>
  </head>
  <body>
    <noscript>
      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <div id="app"></div>
    <!-- built files will be auto injected -->
  </body>
</html>

上述代码包含了一个网页最基本的信息,在 head 标签中,我们可以编写网站的标题 title、编码 charset、图标 icon、描述 description、关键词 keywords 等信息;在 body 标签中,vue 项目通常只需要包含 <div id="app"></div>,表示 App.vue 文件。

src #

assets #

assets 目录放置项目所需要的资源,如图片、字体等,并由 vue 文件使用。

components #

components 目录放置项目的全局组件。

在项目开发中,存在部分小模块需要多次复用,比如导航栏、某些样式的按钮等,通常被提取出来作为全局组件。与之对应的是局部组件,通常是某个页面内容较多,使用多个 vue 文件编写,其中某个 vue 文件和其它文件为父子关系,父引入子,实现低耦合的特性。

初始项目中,该目录下有 HelloWorld.vue 文件,可以看到在 views/HomeView.vue 中,引入了该文件。

在这里也简略说明一下 vue 文件的组成。vue 文件主要由三部分组成:

  • template:html,网页结构内容
  • script:该 vue 文件所需的数据对象格式、js 方法等
  • style:css 页面样式

router #

router/index.js 编写路由规则,即赋予 vue 文件相应的相对路由,核心代码为:

const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path: '/about',
    name: 'About',
    component: () => import('../views/AboutView.vue')
  }
]

其中,path 为分配的相对路由,component 指定 vue 视图页面。

初始项目运行在 localhost:8080 时,访问 localhost:8080/about 将显示 About.vue 的内容。

store #

store 目录下主要放置前端存储数据的格式,利用 vuex 和 localStorage 在用户的网页端保存基本的用户信息。例如用户登录之后,将用户名存于前端,这样在渲染网页时可以直接使用,且向后端发送请求时可以携带用户名,以验证用户的登录信息是否有效。

vuex 所代表的思路是实现组件间通信的最终解决方案,它功能强大,原理严谨,同学们在学习过程中只要学会了它,可以不去学习 事件总线消息订阅和发布 也可以只做了解。 点击这里查看 vuex 中文文档

vuex 的持久化存储也是 vuex 中必须学习的一环。所谓持久化存储就是指借助浏览器保存前端记录的信息,这样就不会在关闭浏览器时就丢失所有信息。同学们在调试的时候,可以通过控制台的“Application/应用程序”看到在对应网页下的前端持久化存储。

在 vue3 中,为了进一步简化 vuex 的步骤,pinia 逐渐崛起,甚至在 Vue 官方最新发布的 create-vue (新一代 vue + vite 项目创建工具)中,pinia 已经代替 vuex 成为官方推荐设置的 状态管理工具。 点击这里查看 pinia 中文文档

pinia

views #

components 和 views 文件夹下均放置的是 vue 文件,不同的是,前者是可以被多个“页面”复用的“页面碎片”,后者是作为项目中的一整个“页面”,往往对应了一个路由。

看初始项目,里面有 About.vueHome.vue,分别对应路由 /about/。简单看一下这两个文件:

About.vue:内容很简单,仅包含 template 部分,仅仅是一句话。

<template>
  <div class="about">
    <h1>This is an about page</h1>
  </div>
</template>

Home.vue:包含 html 和 script 两部分,可以看到在 script 标签中引入了 HelloWorld 组件,然后在 html 部分中进行调用,这样该页面的效果为,logo 图片加 HelloWorld 组件。

<template>
  <div class="home">
    <img alt="Vue logo" src="../assets/logo.png">
    <HelloWorld msg="Welcome to Your Vue.js App"/>
  </div>
</template>

<script>
// @ is an alias to /src
import HelloWorld from '@/components/HelloWorld.vue'

export default {
  name: 'HomeView',
  components: {
    HelloWorld
  }
}
</script>

这里对 @ 进行一个简要说明:所谓 @ 可以从英文注释中看出,它是一个 /src 的别名。我们通常叫它软链接。可以想象,当 src 下目录结构复杂时,如果使用相对地址进行寻址,那么地址中的 ../ 会有很多,采用 @ 就可以以 /src 为基地址简化路径书写。这里的配置在 jsconfig.json 中可以看到:

// jsconfig.json
{
  "compilerOptions": {
    "target": "es5",
    "module": "esnext",
    "baseUrl": "./",
    "moduleResolution": "node",
    "paths": {
      "@/*": [
        "src/*"
      ]
    },
    "lib": [
      "esnext",
      "dom",
      "dom.iterable",
      "scripthost"
    ]
  }
}

当然,采用软链接带来的最大问题就是:代码补全的丢失。具体来说,在 VS Code 中,以引入子组件为例,如果采用软链接的方式引入子组件,那么是无法实现点击跳转的。因此建议在项目开发最终阶段再统一更换为软链接。

App.vue #

无论是 vue2 还是 vue3,App.vue 都是整个项目的入口文件,该文件描述整个网站将渲染的内容。一般来说,我们会把整个项目都会使用的样式在这里配置。一个好的项目应该避免在 App.vue 中有过于繁琐的内容。具体的实现细节应该尽量抽离为 views 文件夹下的 vue 文件,在 App.vue 中仅有对于某些子组件的引用,或者是路由的切换。

<template>
  <div id="app">
    <div id="nav">
      <router-link to="/">Home</router-link> |
      <router-link to="/about">About</router-link>
    </div>
    <router-view/>
  </div>
</template>

如上述代码,它由 nav 的代码块和 router-view 组成。前者是两个超链接,有点类似于网站的导航栏,在各个路由下都会展示;后者表示根据路由渲染对应的 vue 组件,如果当前是 /about 则渲染 About.vue 文件描述的内容。

main.js #

main.js 是项目的核心文件,在这里可以导入各种全局依赖。比如全局导入 elementUI(Vue3 对应的是 elementPlus) 等 UI 组件库或 ECharts 可视化图表库等,npm install 后将在该文件中全局引入。

Vue2 版本 #

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'

Vue.config.productionTip = false

new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')

上面是 Vue2 项目中的 main.js,简单分析后可以看出,它引入了 Vue 核心运行库中的 Vue 构造函数、App.vue、vue-router 相关文件创造的 router 对象,vuex 创建的 store 对象。

在将工具库导入后,利用 Vue 创建一个 Vue 实例对象,并在这个过程中将 App 组件渲染到 index.htmlid="app" 的 DOM 节点上(即 <div id="app"></div>)。

Vue3 版本 #

import { createApp } from 'vue'
import App from './App.vue'

import router from './router'
import store from './store'

createApp(App)
.use(router)
.use(store)
.mount('#app')

上面是 Vue3 项目中的 main.js,可以看到,引入的不再是 Vue 构造函数了,而是一个名为 createApp 的工厂函数;利用这个工厂函数,创建应用实例对象,将引入的 router 和 store “使用”后,再挂载到 index.htmlid="app" 的 DOM 节点上(即 <div id="app"></div>)。

Bonus #

事实上,在真正的项目中,还有一些文件目录是不可或缺的。

vue-structure2

api #

这个文件夹往往用于放置前端向后端发起请求的函数。但是,作为其模板的 request.js 和配置 请求拦截器响应拦截器intercept.js 往往是放在 utils 文件夹下的。

关于前后端如何进行请求交互,这里推荐大家了解 Promise 的基础知识,并阅读 Axios 中文文档

constants #

顾名思义,这里就是用于放置在各个文件中规定好的通用常量。比如说一些约定的键值。就类似于操作系统课程中,对于一些地址、指令、权限的宏定义。

utils #

这个文件夹主要放置一些工具类,最常见的有两类:字符串处理函数 和 文件处理函数。

字符串处理函数是指一些通用的处理逻辑,比如说前端在拿到后端数据库的时间戳后需要进行格式化处理。

关于时间相关的字符串处理,这里强烈推荐一个工具库 moment.js

文件处理函数则是指对一些特定格式文件的操作,比如说对于 Excel/CSV 表格文件的解析,对于 Word/PDF 文件的导出等。此外文件的上传的一些高级操作(比如秒传)也可以放在这里。

结语 #

vue 虽然号称是当前入门最简单的主流前端框架,但是相比三件套本身,需要新掌握的东西可以说成指数级增长。我们思量再三,最终决定不在指导书中对于 vue 的语法进行介绍——因为网上已经有太多好的教程,我们这里的一番絮语也无法让大家快速、扎实地入门 vue。比表达,几页指导书很难比得过翔实的视频教程,比内容,我们的一家之言又很难比得上官方的说明文档。因此,我们这里能做的,仅仅是作为一个前辈,将一些别的地方提及甚少的细节、学习路上质量极高的资料收集汇总给大家。

前端之路漫漫,诸君仍需努力。