Skip to content

项目介绍与 vue-admin 认知(一) ​

本页对应 项目介绍与 vue-admin 认知,共 7 段视频,约 46 分钟。

学习目标 ​

  1. 能说明本项目的整体构成(后台管理系统 + 可视化大屏两个子项目),并描述“后台提供数据、前台可视化展示数据”的核心业务闭环,指出两项目后续通过微前端(qiankun)整合为主/子应用的关系。

  2. 能将 vue-admin 拉取到本地并成功启动:正确执行依赖安装、依据 package.json 的 scripts 判断并执行启动命令,且在安装输出中识别成功标识、排查常见安装失败。

  3. 能区分项目目录中 SRC 与非 SRC 两部分:说明 SRC 为参与打包的业务源码目录、非 SRC 为仅在开发阶段生效的配置文件,并能按职责将 api、assets、components、constants、directive、icons、layout、utils、views 等目录与具体开发需求正确对应。

  4. 能读懂 package.json 中的 scripts、dependencies、devDependencies 三项配置:说明命令别名可自定义而实际执行命令固定,区分生产/开发依赖的差异,并正确选用 -D 参数将包装入 devDependencies。

  5. 能解释 main.js 作为应用入口的作用(打包时由此递归查找依赖),说明其中全局初始化工作(Vue 引入、样式重置、ElementUI 全局注册、根组件/store/router 挂载),并判断哪些代码不应写入该文件。

  6. 能描述由 App.vue 生成的整体组件树:说明 App.vue 仅含一级 router-view、layout 负责架子布局并提供二级路由出口,能解释 :key 绑定当前路由路径以破坏路由缓存的原因,并能仿照现有规则为新增模块正确配置一级路由、默认二级路由(path 为空)及嵌套路由。

知识点 ​

项目定位:一个数字化园区,两个前端项目 ​

园区数字化管理项目示意图

为什么需要:园区管理既要维护楼宇、企业、员工、车辆、一体杆等业务数据,又要把园区概况实时、直观地展示出来。只做后台管理,数据不直观;只做大屏,数据没有维护入口。
怎么想:先把项目拆成“后台管理主应用”和“可视化大屏子应用”。后台负责增删改查,大屏负责展示。转写稿里反复强调:说是“一个项目”,其实是两个项目,后面再通过微前端合二为一。
怎么做:演示时先打开后台管理系统,界面是常规后台结构;点击“可视化大屏”后跳转到前台可视化项目。大屏左侧是二维图表,右侧加载 3D 园区模型。3D 资源较大,演示中出现“加载中”要稍等;加载出来后可以拖动、点击交互。
易错点:不要把“黑马智数”理解成单一后台项目;不要忽略大屏是独立子应用;3D 加载慢时不要误判为失败,先观察网络和等待资源加载。

数据闭环:后台供数,大屏消费 ​

为什么需要:大屏如果写死数据,就无法反映园区真实变化;后台如果只存数据不展示,也失去了数字化管理价值。
怎么想:本次项目的核心闭环是“后台提供数据,前台大屏展示数据”。后台维护业务数据,大屏通过接口读取并渲染。
怎么做:在后台完成楼宇、企业、员工、车辆、一体杆等管理;大屏用二维图表展示统计信息,用 3D 模型展示园区楼宇、停车场等空间信息。演示中点击大屏入口会跳转到前台项目,说明两个项目在业务上是连通的。
易错点:只关注页面效果,忽略后台接口;把大屏数据写死;调试时只看大屏不检查后台数据是否已维护。

微前端整合:两个项目如何合二为一 ​

为什么需要:后台使用 Vue2.6,大屏使用 Vue3,技术栈不同,直接塞进一个工程容易冲突。
怎么想:用“主应用 + 子应用”的方式整合,而不是简单复制页面。转写稿明确说后面会用“乾坤”把两个项目合起来。
怎么做:后台作为主应用,大屏作为子应用,通过乾坤(qiankun,蚂蚁金服出品的微前端框架)整合。这样既能保留两个项目的技术选型,又能形成统一入口。
易错点:把微前端理解成 iframe 或普通跳转;忽略路由、样式隔离;以为两个项目必须统一成同一个 Vue 版本。

技术栈与版本:后台 Vue2.6,大屏 Vue3 ​

为什么需要:后台管理需要成熟稳定的生态,可视化大屏需要更新的 Vue3 能力和图表、3D 插件。
怎么想:后台沿用常见后台技术栈,大屏单独选可视化技术栈。转写稿提到后台是 Vue2.6,并配有常见后台配套;前台是 Vue3,二维图表使用 ECharts,3D 可视化使用相应插件。
怎么做:后台按 Vue2.6 的写法开发;大屏按 Vue3 的写法开发;2D 图表负责统计展示,3D 插件负责园区模型展示和交互。
易错点:把 Vue2 和 Vue3 的语法混用;看到两个版本就以为项目写错了;忽略 3D 资源体积和加载性能。

环境与启动:先跑起来,再读代码 ​

为什么需要:拿到现成项目,第一步不是盲目改代码,而是确认依赖能装、项目能启动。
怎么想:先看项目文档和 package.json 的 scripts,不要凭经验猜启动命令。转写稿中强调:执行什么命令由 package.json 决定。
怎么做:克隆项目后安装依赖,再按脚本启动。Node 版本按转写稿要求:不要低于 8.19,也不要高于 17,具体以课程文档为准。安装依赖时看到“added N packages”一般说明安装完成。

bash
npm i
# 启动命令以 package.json 的 scripts 为准
npm run <启动脚本>

易错点:一上来就执行 npm run dev;不看 scripts;Node 版本过高或过低导致安装失败;装依赖卡住或报错时不看日志。

学习模式与项目周期 ​

为什么需要:本次实战内容多,需要明确主次,避免两个项目同时抓。
怎么想:转写稿说明后台部分约 8 天,可视化部分约 3 天,合计 11 天;白天以园区项目为主,晚上或休息日自学人资项目。
怎么做:先跟着白天课程把园区项目做明白,再迁移到相似的人资项目;遇到新模块时按现有项目规则添加。
易错点:主次颠倒;园区项目没做完就急着自学;把两个项目完全当成不同技术体系,忽略它们的相似性。

先认识“核心业务闭环”到底闭环了什么 ​

参考在线预览地址:https://fe-hmzs.itheima.net/workbench

本节的核心不是先写页面,而是先建立项目全局观:本次“黑马智数”并不是一个单纯的后台管理系统,而是由两个项目共同组成——后台管理系统负责园区内各项数据的管理,前台可视化项目负责重点园区数据的展现。转写中反复强调“后台那边会提供我们的数据,前台那边负责把数据展示出来”,这就是核心业务闭环。

为什么需要先讲闭环? 因为后面所有目录、路由、入口文件、微前端整合,都是为这条数据链路服务的。如果不先知道“谁生产数据、谁消费数据”,很容易把可视化大屏的展示逻辑写进后台,或者把后台的增删改查逻辑塞进大屏。

怎么想? 把两个项目按职责拆开:后台是“数据维护端”,完成常规的增删改查;前台是“数据展示端”,把重点园区数据用二维图表和三维模型呈现出来。两者不是各做各的,而是通过接口数据连接。

怎么做? 演示时先从后台进入,后台页面是常见的后台管理布局;后台中会有一个“可视化大屏”入口,点击后跳转到前台可视化项目。前台左侧是二维图表,右侧是三维可视化资源;三维资源较大,会先显示加载中,加载完成后可拖动、点击交互。

易错点: 不要把“跳转过去”理解成闭环完成,真正闭环是后台维护的数据能到前台展示;三维资源加载慢是正常现象,不要误判为代码错误;两个项目最终还要通过微前端合二为一,路由和资源路径要特别小心。

技术栈与微前端:为什么两个项目能合成一个项目 ​

为什么需要微前端? 因为后台和前台可视化技术栈、页面形态、加载资源都不同。转写中提到后台使用 Vue 2.6,前台可视化部分使用 Vue 3;后台有常见 UI 组件库、Vuex 等,前台有二维图表和三维可视化插件。硬塞进一个工程会增加耦合。

怎么想? 把两个项目分别开发,最后用微前端框架 qiankun(转写中“乾坤”为同音误识别)按主应用、子应用的方式整合。这样既保留各自技术栈,又能让用户在后台点击入口进入大屏。

怎么做? 先分别保证两个项目能独立运行,再按 qiankun 的主/子应用规则接入。项目环境上要注意 Node 版本要求,转写中说不低于 8.19、不高于 17,实际以课程环境为准;开发时对照接口文档封装请求。

易错点: 不要一开始就强行合并,先独立跑通;微前端下静态资源路径、路由前缀、公共依赖容易冲突;接口字段要对齐,否则大屏“有图无数据”。

用项目骨架支撑闭环:目录、入口与路由 ​

为什么需要看骨架? 拉到现成 vue-admin 架子后,先要知道代码写哪里。转写中把目录分成非 src 和 src:src 是业务源代码文件夹,参与打包上线;非 src 多是配置文件,如 package.json、构建配置等。src 下 api 放接口函数,assets 放静态资源,components 放公共组件,constants 放前端维护的常量数据,directives 放自定义指令,icons 放图标,layout 放后台整体布局,utils 放工具函数,views 放页面级组件。

怎么想? 后台闭环的“架子”由 layout 完成:左侧菜单、顶部栏、右侧内容区;App.vue 只保留一级 router-view,layout 内部再有二级 router-view。默认二级路由的 path 为空时,会作为默认二级路由一上来就渲染。

怎么做? 本地拉项目后先 npm i,看到“added N packages in Xs”说明依赖安装正常;执行命令前先看 package.json 的 scripts,不盲目 npm run dev。main.js 是入口,只做全局初始化,如引入 Vue、样式重置、UI 组件库全局注册、router、store,最后挂载 App;不要在入口堆业务代码。App.vue 里二级出口的 key 使用当前路由路径,可破坏路由缓存,避免从 id=1001 跳到 id=1002 时仍显示旧数据。

易错点: 依赖装错位置——生产依赖和开发依赖要按文档区分;入口文件堆业务;把默认二级路由误认为多此一举;忽略 key 导致详情页缓存不更新。

本地验证与调试:按闭环走一遍 ​

为什么需要按闭环调试? 因为本节最终要验证“后台数据管理→前台可视化展示”是否成立。

怎么想? 先跑后台,确认架子正常、菜单切换正常;再找可视化大屏入口,观察是否跳转到前台;最后看前台二维图表和三维资源是否加载、能否交互。

怎么做? 启动后先看后台常规增删改查页面;点击可视化大屏入口;前台右侧三维资源加载中时等待出现,出现后尝试拖动和点击。若样式保存时格式化报错,可按转写做法在工作区关闭保存格式化,避免 ESLint 与 VS Code 格式化冲突。

易错点: 只跑一个项目就以为闭环完成;三维资源未加载完就断言失败;微前端整合后忘记检查路由跳转和缓存。把这条链路走通,才算真正理解本节“核心业务闭环”。

代码示例 ​

示例 1:入口文件 main.js 的等价写法(Vue2.6 + VueRouter + ElementUI 全局注册) ​

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8" />
  <title>入口文件 main.js 等价示例</title>
  <!-- 等价于 main.js 里的 import 'normalize.css':样式重置 -->
  <link rel="stylesheet" href="https://unpkg.com/normalize.css@8.0.1/normalize.css" />
  <!-- ElementUI 配套样式 -->
  <link rel="stylesheet" href="https://unpkg.com/element-ui@2.15.14/lib/theme-chalk/index.css" />
</head>
<body>
  <!-- 挂载点:对应 main.js 末尾的 $mount('#app') -->
  <div id="app"></div>

  <!-- 核心框架:本项目用的是 Vue 2.6 -->
  <script src="https://unpkg.com/vue@2.6.14/dist/vue.js"></script>
  <script src="https://unpkg.com/vue-router@3.6.5/dist/vue-router.js"></script>
  <!-- ElementUI:下面用 Vue.use() 做全局注册 -->
  <script src="https://unpkg.com/element-ui@2.15.14/lib/index.js"></script>
  <script>
    // 等价于 main.js 里的 Vue.use(ElementUI):全局注册所有组件
    Vue.use(ELEMENT);

    // 根组件 App.vue:里面什么都没有,只有一个一级路由出口
    const App = { template: '<router-view></router-view>' };

    // 一级路由①:登录
    const Login = { template: '<h2>登录页 login</h2>' };
    // 一级路由②:404
    const NotFound = { template: '<h2>404 页面不存在</h2>' };
    // 二级路由页面
    const Workbench = { template: '<h3>工作台 workbench</h3>' };
    // 一级路由③:Layout 布局架子(左侧菜单 + 顶部 navbar + 内容区)
    const Layout = {
      template: `
        <div>
          <aside style="float:left;width:160px">
            <router-link to="/workbench">工作台</router-link>
          </aside>
          <section style="margin-left:180px">
            <header>当前用户:admin</header>
            <!-- 二级路由出口 -->
            <router-view></router-view>
          </section>
        </div>`
    };

    const router = new VueRouter({
      routes: [
        {
          path: '/',
          component: Layout,
          children: [
            { path: '', component: Workbench },        // 默认二级路由:path 为空
            { path: 'workbench', component: Workbench }
          ]
        },
        { path: '/login', component: Login },
        { path: '*', component: NotFound }
      ]
    });

    // 等价于 main.js 末尾:new Vue({ router, store, render: h => h(App) }).$mount('#app')
    new Vue({
      router,
      render: h => h(App)
    }).$mount('#app');
  </script>
</body>
</html>

关键行与易错点

  • Vue.use(ELEMENT):ElementUI 必须做全局注册,否则任何 .vue 文件里用 el-xxx 都不生效;样式文件(theme-chalk)和组件包要一起引,缺一不可。
  • const App = { template: '<router-view></router-view>' }:根组件只负责一级路由出口,不要往里堆业务代码;main.js 同理,只做全局初始化,业务多了要抽出去。
  • path: '' 的二级路由:这就是默认二级路由,URL 上显示的是一级路由地址(如 /),但二级组件一上来就渲染出来,不需要再用二级 path 匹配。
  • render: h => h(App) 会覆盖 #app 里原有内容,所以挂载点写成空的 <div id="app"></div>,不要把模板写在挂载点里。

示例 2:Layout 布局 + 二级路由出口 :key="$route.path" 破坏缓存 ​

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8" />
  <title>Layout 布局与二级路由出口 key</title>
  <style>
    * { box-sizing: border-box; }
    body { margin: 0; }
    .layout { display: flex; height: 100vh; }
    .sidebar { width: 180px; border-right: 1px solid #eee; padding: 12px; }
    .sidebar a { display: block; padding: 8px; color: #333; text-decoration: none; }
    .sidebar a.router-link-active { color: #409eff; }
    .main { flex: 1; display: flex; flex-direction: column; }
    .navbar { height: 50px; line-height: 50px; padding: 0 16px; border-bottom: 1px solid #eee; }
    .content { flex: 1; padding: 16px; }
  </style>
</head>
<body>
  <div id="app"></div>

  <script src="https://unpkg.com/vue@2.6.14/dist/vue.js"></script>
  <script src="https://unpkg.com/vue-router@3.6.5/dist/vue-router.js"></script>
  <script>
    // 布局组件:只负责搭架子,不写具体业务
    const Layout = {
      template: `
        <div class="layout">
          <aside class="sidebar">
            <router-link to="/workbench">工作台</router-link>
            <router-link to="/detail/1001">文章 1001</router-link>
            <router-link to="/detail/1002">文章 1002</router-link>
          </aside>
          <section class="main">
            <header class="navbar">当前用户:admin</header>
            <div class="content">
              <!-- 关键:二级路由出口加 key,破坏路由默认缓存 -->
              <router-view :key="$route.path"></router-view>
            </div>
          </section>
        </div>`
    };

    const Workbench = { template: '<h3>工作台</h3>' };

    // 同一个组件被 /detail/1001 与 /detail/1002 复用
    const Detail = {
      props: ['id'],
      data() {
        return { createdId: null };
      },
      created() {
        // 组件被复用时 created 不会再执行;只有 key 变化销毁重建才会再次执行
        this.createdId = this.$route.params.id;
      },
      template: `
        <div>
          <p>路由参数 id:{{ id }}</p>
          <p>created 时拿到的 id:{{ createdId }}</p>
        </div>`
    };

    const router = new VueRouter({
      routes: [
        {
          path: '/',
          component: Layout,
          children: [
            { path: '', component: Workbench },              // 默认二级路由
            { path: 'workbench', component: Workbench },
            { path: 'detail/:id', component: Detail, props: true }
          ]
        },
        { path: '*', redirect: '/workbench' }
      ]
    });

    const App = { template: '<router-view></router-view>' };
    new Vue({ router, render: h => h(App) }).$mount('#app');
  </script>
</body>
</html>

关键行与易错点

  • <router-view :key="$route.path"></router-view>:路由默认有缓存机制,从 /detail/1001 跳到 /detail/1002 时路径主体不变、只有参数变,组件会被复用,页面还是旧数据。给出口加 key 后 key 不同 → 每次销毁重建 → 显示新数据。
  • 可以试着把 :key="$route.path" 删掉再点 1001 / 1002,created 时拿到的 id 会一直停在 1001,这就是典型的缓存踩坑。
  • props: true 是把路由参数以 props 形式传给组件,比在模板里写 $route.params.id 更解耦。
  • Layout 里只放 sidebar / navbar / 二级 router-view,业务组件一律放到二级路由页面里,架子与业务分离。

示例 3:package.json 关键配置与装包命令 ​

json
{
  "name": "hm-zs-admin",
  "version": "1.0.0",
  "scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "lint": "vue-cli-service lint"
  },
  "dependencies": {
    "core-js": "^3.8.3",
    "element-ui": "^2.15.14",
    "normalize.css": "^8.0.1",
    "vue": "^2.6.14",
    "vue-router": "^3.5.1",
    "vuex": "^3.6.2"
  },
  "devDependencies": {
    "eslint": "^7.32.0",
    "sass": "^1.49.0",
    "sass-loader": "^8.0.2"
  }
}
bash
# 1. 拉到项目后第一步:装依赖
npm i
# 成功标志:终端显示 added N packages in Xs

# 2. 看 scripts 里写的是什么再启动,不要盲目 npm run dev
npm run serve

# 3. 装到 devDependencies(只在开发时生效,不参与打包)
npm i sass sass-loader -D

# 4. 不加 -D 默认装到 dependencies(开发、生产都依赖)
npm i element-ui

关键行与易错点

  • scripts 里的 key 可以自定义(serve 改成 dev 都行),但 value 不能改,它才是实际执行的命令 vue-cli-service serve;所以启动前一定先看 package.json。
  • dependencies:开发和打包上线都需要的包;devDependencies:只在开发阶段生效,打包后不参与,装错会让包体变大。
  • npm i xxx -D 进 devDependencies,npm i xxx 进 dependencies,这个区别要卡死。
  • npm i 没有出现 added N packages 提示,大概率是装失败了,先检查 Node 版本(本项目要求 8.19 ~ 17 之间)。

常见错误 ​

  1. 现象:克隆完架子直接 npm run dev(或 npm run serve)报错、白屏、控制台提示找不到模块。 原因:跳过安装依赖,node_modules 不存在;或安装时中途卡住、报错,却没留意终端输出。 修法:先 npm i,确认终端出现 added xxx packages in xx s;再执行 package.json 里 scripts 实际定义的命令(不靠猜)。

  2. 现象:本地开发一切正常,打包后包体积异常变大、构建变慢。 原因:把只在开发期使用的包(如 sass、eslint 相关、构建插件)装进了 dependencies,被一起打进产物。 修法:装包前先看该包官方文档给的安装命令;需要开发依赖就加 -D,装错了先卸载再重装,不要靠手改 package.json 硬挪。

  3. 现象:改了 scripts 里的命令内容,结果 npm run xxx 直接失效,或报 command not found。 原因:把“命令别名”和“实际执行的命令”搞混,动了不该动的那一半。 修法:别名(key)随便改,值(value)必须保持框架内置命令原样;出问题就对照原始仓库的 package.json 还原。

  4. 现象:保存文件时满屏红波浪线、格式被反复改乱,甚至和项目自带的 ESLint 规则互相打架。 原因:编辑器自带的“保存时格式化”与项目工程化规则同时生效,两套规则冲突。 修法:在工作区级别(而非用户全局)关闭编辑器的 formatOnSave,生成项目内的 .vscode/settings.json,让项目规则唯一生效。

  5. 现象:新增一个后台页面,路由写了、组件也写了,点菜单却进不去,或者进去后没有左侧菜单和顶部栏(整页只剩内容)。 原因:把业务页面配成了一级路由,脱离了 Layout 布局组件;或 path 写成了绝对路径,与父级不匹配。 修法:后台业务页统一作为 Layout 的 children 二级路由,path 用相对写法;配完刷新,检查地址栏层级与页面骨架是否都在。

  6. 现象:从列表页跳到详情页,切换不同 id 时页面数据不更新,仍显示上一条。 原因:同一路由仅参数变化,组件被复用,走了默认缓存,不会重新渲染。 修法:给路由出口绑定 :key="$route.fullPath"(或其他能区分参数的值),强制每次重建;改完用两条不同 id 的数据验证。

  7. 现象:main.js 越写越长,几十行业务逻辑、接口调用、业务组件注册全堆在里面。 原因:把入口文件当成“万能文件”,职责边界失守。 修法:入口只保留全局初始化(框架、UI 库、store、router、挂载);业务逻辑抽到对应模块,业务组件就近注册或放公共组件目录。

练习 ​

  1. 跑通并读命令:把项目拉到本地、装依赖、启动,记录 package.json 中 scripts 的每个别名与实际命令;尝试改一个别名并验证是否仍可用,然后说明“哪个能改、哪个不能改”。 验收:终端成功启动;能口头解释别名与命令值的区别。

  2. 依赖归类实验:分别用默认方式和 -D 安装一个包(如 dayjs 与某个构建/校验类工具),对比 package.json 中两处内容的变化;再故意装错一次并修正。 验收:能说明两个字段的生效阶段差异,并给出“装错会导致什么后果”。

  3. 新增一个二级路由页面:在 Layout 下新增“设备管理”页面,要求左侧菜单可点击、地址栏层级正确、页面骨架(菜单+头部+内容区)完整。 验收:不参考已有代码也能独立配出;能解释为什么不能配成一级路由。

  4. 复现并修复缓存问题:做一个带参数的路由页面(如 /detail/:id),连续切换两个不同 id,观察是否刷新;然后用 key 方案修复。 验收:修复前后现象对比明显,能说清“什么情况下会走缓存”。

  5. 治理编辑器冲突:故意开启全局保存格式化,制造 ESLint 报错,再用工作区配置解决。 验收:产出项目内 .vscode/settings.json,并说明工作区设置与用户设置的作用范围差异。

课堂画面 ​

课堂画面 01

课堂画面 02

课堂画面 03

课堂画面 04

课堂画面 05

课堂画面 06

原视频对照 ​

#视频时长
1项目前置介绍05:17
2认识vue-admin07:01
3项目目录熟悉07:35
4项目关键文件-package.json05:05
5项目关键文件-main.js04:59
6项目关键文件-app.vue10:30
7项目关键文件-vue-router05:59

小结 ​

  • 二次开发型项目的正确起手式是“先跑起来,再读代码”:跑不起来时,第一嫌疑永远是依赖,而不是源码。
  • 后台管理项目的目录与文件命名高度同构,记住“哪类东西放哪个文件夹”比通读代码更能提升定位效率。
  • 依赖归类错误属于“当下无感、打包爆雷”的隐性坑,必须在装包那一刻就做对,事后补救成本更高。
  • 现成的架子一定带着作者的主张(路由结构、目录划分、工程化规则),扩展时先模仿既有写法,不要自创风格。
  • 入口文件与根组件的职责边界守不住,项目就会从“架子清晰”退化成“到处是全局逻辑”。
  • 工程化冲突要按项目维度解决(工作区配置),污染全局设置会把坑带到下一个项目。
  • 缓存类 bug 的特征是“代码没错、数据不对”,排查时应优先怀疑组件复用而非接口。
  • 微前端把两套技术栈合成一个产品,前提是各自边界清晰——子应用越独立,整合越省事。