Skip to content

面经 H5 端:Vant 与项目搭建(四) ​

本页对应 面经 H5 端:Vant 与项目搭建,共 6 段视频,约 1 小时 17 分。

学习目标 ​

  • 能够将接口返回的分页数据(res.data.rows)存入组件 data 的 list 中,并能通过 Vue DevTools 按组件 name 搜索定位组件、核对数据是否正确挂载。
  • 能够基于 van-cell 的插槽(title、label、default 等)定制面经条目结构,将其抽离封装为 ArticleItem 组件并在 main.js 中注册为全局组件以供多页面复用。
  • 能够通过 props 接收父组件传入的 item 对象,正确书写类型校验与对象默认值(默认值必须写成函数返回),并在模板中渲染标题、作者、时间、简介、点赞量与浏览量等字段。
  • 能够使用 v-for 遍历 list 渲染 ArticleItem 并绑定唯一 key,完成父组件向子组件的数据传递。
  • 能够使用 van-list 包裹循环列表,定义 loading、finish 数据与 onLoad 方法,并说明 van-list 首次会自动触发 onLoad 因而无需在 created 中手动调用。
  • 能够实现上拉加载更多:请求页码自增、用展开运算符 push 累加数据、请求完成后重置 loading、页码超过总页数时将 finish 置为 true 并显示"没有更多"。
  • 能够根据 401 状态码、Network 面板请求头 Authorization(Bearer 拼写与空格、localStorage key 不一致)、模块或包找不到(路径/文件名/loader 缺失)等报错信息定位并修复常见错误。

知识点 ​

组件库的价值与选型 ​

组件库是第三方封装好了很多组件的集合,整合到一起就是一个组件库。日历、键盘、打分、登录等常见模块,如果每个项目都重写,既慢又难统一交互和样式。为什么需要:复用成熟组件,减少重复造轮子。怎么想:先按端选库,PC 与移动端不要混用;再查文档确认版本和 API。怎么做:移动端 Vue2 项目可看 vant-ui,PC 端常见有 element-ui、element-plus、iview、ant-design;移动端还有 Mint UI、Cube UI。Vant组件库示例 易错点:组件库不是唯一的,别背组件名,要会看文档的 API、插槽和示例;注意 Vue2/Vue3 版本差异。

用 Vant Cell 搭面经条目 ​

面经列表每一项结构相似,适合用 van-cell 做骨架。为什么需要:自己写 div 也能做,但 Cell 已处理标题、内容、图标等基础布局。怎么想:先看基础用法,再看插槽:title 放左侧标题,默认插槽放右侧内容,label 放标题下方内容,right-icon 放右侧图标。怎么做:先用双标签写 Cell,再用 <template #title>、<template #label> 和默认插槽定制。转写稿里“万杠线”就是 van-cell,“插锁”是插槽,“井号”是 #,即 v-slot: 的简写。易错点:要传插槽内容就不能用单标签;#title 要写在 template 上;不确定插槽位置时,按文档逐个试并观察页面。

把接口数据存进 data 并用调试工具核对 ​

接口返回的往往不是直接数组,而是分页对象。为什么需要:列表只渲染当前页数据,不能把整个响应乱塞。怎么想:先打印 res,确认数据在 res.data.rows,总页数在分页配置里。怎么做:

js
this.list = res.data.rows

然后在模板里 v-for 遍历。调试时打开 Vue 调试工具,按当前组件的 name 搜索,别在别的组件里找数据。易错点:存错层级;看错组件;把“当前页、每页条数、总页数”混在一起。转写稿里反复强调“存到 data 中”“看 Vue 调试工具”,就是为了先确认数据到底有没有拿到。

封装 ArticleItem 组件实现复用 ​

面经列表、收藏页、喜欢页都可能复用同一条面经结构。为什么需要:复制多份结构会导致改一处漏多处。怎么想:把 van-cell 这一整块结构和对应样式抽成子组件,再注册为全局组件。怎么做:在 components 下新建 ArticleItem.vue,把 Cell 结构及样式剪切过去;在 main.js 导入并 Vue.component 注册;父组件循环并传值:

vue
<ArticleItem v-for="item in list" :key="item.id" :item="item" />

子组件接收:

js
props: {
  item: {
    type: Object,
    default: () => ({})
  }
}

易错点:父组件传的是 :item,子组件 props 必须同名;对象默认值必须写成函数,箭头函数直接返回对象时要加小括号 () => ({}),否则会被当成函数体;抽组件时样式类名要一起迁移。

用 Vant List 做滚动加载更多 ​

移动端滚动到底部加载下一页,自己监听滚动很麻烦,Vant 提供了 List。为什么需要:统一“加载中、没有更多”的状态。怎么想:用 van-list 包裹循环项,准备 loading、finished,在 @load 触发的 onLoad 中请求下一页。怎么做:每页数据回来不要覆盖,而是累加:

js
this.list.push(...res.data.rows)
this.loading = false
this.page++

页码超过总页数后把 finished 设为 true。易错点:push 整个数组要展开,否则会变成嵌套数组;onLoad 要与文档事件对应;请求中 loading 为 true 防止重复加载;最后一页要停止加载。转写稿里“非历史”应纠正为 finished,“落点”应纠正为 loading。

接口与资源报错排查 ​

为什么需要:401、找不到模块是高频问题,不会排查会一直卡住。怎么想:接口错先看 Network,资源错先看控制台报错。怎么做:401 一定是权限/token 问题,看请求头 Authorization 是否为 Bearer + 空格 + token,并核对 localStorage 键名是否与代码一致;请求地址、GET/POST 方法要和接口文档一致。找不到模块时,检查路径、文件名、单词拼写;提示缺少 sass-loader 而样式写了 lang="scss",就安装对应包,或改成已支持的 less。易错点:Bearer 拼错、漏空格、token 键名写错;把接口地址随意加字符;用了 SCSS 却没装 loader。转写稿中故意改错 token、看 401、看请求头,再改回正确值,就是完整调试过程。

本节目标与结论 ​

全部导入与按需导入对比

本节要明确的是:全部导入和按需导入到底差在哪里。笔记给出的结论很直接:全部导入会引起项目打包后的体积变大,进而影响用户访问网站的性能;按需导入只会导入你使用的组件,进而节约资源。

为什么需要:项目上线后,包体积会直接影响首屏加载和访问速度。怎么想:不是“功能能跑就行”,而要判断某个导入是否真的被当前页面使用。怎么做:先区分“全部导入”和“按需导入”,再按项目所用组件库的官方文档选择引入方式;项目内组件也尽量做到用到才引入、能复用就抽离。易错点:把“全局注册”误当成“按需导入”,或者按需引入后忘了引入对应样式,导致页面样式缺失。

全部导入:为什么体积会变大 ​

为什么需要:全部导入会把整个组件库或大量未使用代码一起打进包里,用户访问时下载量变大。怎么想:如果当前页面只用了少数几个组件,却把整套组件都引入,打包体积自然不划算。怎么做:理解全部导入的代价,检查项目是否真的需要一次性引入全部内容;如果只是少量页面使用,应优先考虑按需方式。易错点:只看开发时是否方便,不看打包结果;以为“代码写在入口文件里”就一定被使用,实际上未使用代码也可能增加体积。

按需导入:只拿当前用到的组件 ​

为什么需要:按需导入的目标是只把当前用到的组件和资源引入,减少无关代码。怎么想:先看页面实际用了什么,再决定引入什么;不是“先全拿进来,后面再说”。怎么做:按组件库文档做按需引入;项目内组件也一样,用到哪个就导入哪个。转写稿里把面经列表中的 van-cell 结构抽成 ArticleItem,再在 main.js 中导入并注册为全局组件,体现的就是“把复用的部分单独拿出来、需要的地方再使用”的思路:

js
import ArticleItem from './components/ArticleItem.vue'
Vue.component('ArticleItem', ArticleItem)

易错点:转写稿中“慢点GS”实际是 main.js,“companist”实际是 components,“RRT艾特/阿吉艾”实际是 ArticleItem。导入路径、文件名大小写、单词拼错,都会导致“找不到模块”。

用组件抽离理解“按需”的工程价值 ​

为什么需要:面经列表、收藏页、喜欢页都可能复用同一条面经结构,如果每个页面都复制一遍,代码重复且难维护。怎么想:把 van-cell 这一块抽成独立组件,哪个页面需要就引入哪个组件。怎么做:把模板和对应样式剪切到 ArticleItem 中,注册后在列表页循环使用:

html
<ArticleItem
  v-for="item in list"
  :key="item.id"
  :item="item"
/>

易错点:父传子时属性名要一致,子组件用 props 接收;转写稿里反复强调“必须叫 item”,否则接收不到。默认值是对象时,default 要写成函数返回对象;箭头函数直接返回对象时,外层要加小括号,否则 {} 会被当成函数体。

调试与验证:别把“按需”做成“缺依赖” ​

为什么需要:按需引入后,最常见的问题不是“概念不懂”,而是路径、依赖、token、样式 loader 等细节出错。怎么想:先看控制台报错,再看 Network 面板;401 优先查 token。怎么做:转写稿中排查 401 时,要看请求头里的 Authorization 是否为 Bearer 加空格,再看本地存储的 key 是否写成了 mobile-token;找不到模块就看路径和单词,缺 sass/sass-loader 就补依赖或改 lang。列表加载更多时,数据要累加而不是覆盖:

js
this.list.push(...res.data.rows)

同时用 loading 控制“加载中”,用 finished 控制“没有更多”。易错点:转写稿里的“rose/roose”实际是 rows,“beer/B2”实际是 Bearer,“Ororization”实际是 Authorization,“莫贝尔刚偷看”实际是 mobile-token;只看报错不点开请求详情,很难定位真正原因。

小结 ​

全部导入和按需导入的核心差别不是写法花哨,而是打包体积和资源利用:全部导入省事但可能把不用的代码也带上;按需导入只拿当前使用的组件,更利于性能。项目内把 ArticleItem 抽出来、用到再引入,也是同一种“按需”思想。真正落地时,既要按文档配置,也要用 Network、控制台和打包结果验证,避免把“按需”做成“缺依赖”。

先安装 Vant:把组件库变成项目依赖 ​

为什么需要:本节要使用 Vant 的按钮等组件,第一步必须把组件库安装到项目依赖里;没有安装,后面的 import Vant from 'vant' 就找不到包。
怎么想:转写稿里排查“找不到模块”时反复强调,先判断是第三方包没装,还是自己写的文件路径/单词错了。这里属于前者,所以先在项目根目录安装。
怎么做:在项目根目录执行下面命令,二选一即可:

bash
yarn add vant@latest-v2
// 或者 npm i vant@latest-v2

易错点:命令要在项目根目录执行,不要在随便一个文件夹里执行;注意包名是 vant,不要被口误写成别的词。若控制台报 Cannot find module,先按转写稿思路核对:包是否安装、package.json 是否更新、终端是否在正确目录。

在 main.js 中全部导入并注册 ​

为什么需要:Vant 有很多组件,若每个页面都单独导入、注册,初学阶段代码会显得零散;本节先采用“全部导入”,让所有组件一次性可用,便于快速跑通页面。
怎么想:转写稿里说得很直白:把 Vant 整个导入,再通过 Vue.use(Vant) 注册,之后模板里就能直接写 van-button 等标签。注意这里导入的是组件库和它的样式,不是某个单独组件。
怎么做:在 main.js 中写:

js
import Vant from 'vant';
import 'vant/lib/index.css';
// 把vant中所有的组件都导入了
Vue.use(Vant)

易错点:第一,样式文件 vant/lib/index.css 不要漏,否则组件可能结构在、样式不对;第二,import 语句要集中放在文件上部,转写稿里提到过“所有导入要求放在一起”,不要把注册代码夹在多个 import 中间;第三,注意正确拼写 Vant、main.js、Vue.use(Vant),不要被同音口误带成“VE点company”之类。保存后若报错,先看控制台是“找不到包”还是“路径/单词不一致”。

在页面中直接使用 Vant 组件 ​

为什么需要:全部导入并注册后,要验证组件是否真的可用;最直观的方式就是写几个按钮看渲染效果。
怎么想:既然已经 Vue.use(Vant),页面模板里就不需要再局部注册按钮组件,直接写 Vant 规定的标签即可。
怎么做:在页面模板中写:

jsx
<van-button type="primary">主要按钮</van-button>
<van-button type="info">信息按钮</van-button>

渲染效果可对照下图:

Vant 按钮组件示例

易错点:若按钮出现但样式不对,优先检查 vant/lib/index.css 是否导入;若页面报“未知组件”,检查 main.js 中是否真的执行了 Vue.use(Vant),以及当前页面是否在 Vue 应用挂载范围内。转写稿中的调试经验是:不要一看到报错就慌,先看控制台错误类型,再核对导入、注册、标签名和单词拼写。

全部导入的代价:先跑通,再考虑优化 ​

为什么需要:全部导入虽然方便,但笔记最后明确提醒:“vant-ui提供了很多的组件,全部导入,会导致项目打包变得很大。”这句话是本节最重要的工程意识。
怎么想:初学阶段目标是先让组件可用、页面跑通;但要知道全量注册会把很多当前页面没用到的组件也带进构建结果。转写稿里也提到,组件抽离、全局注册是为了复用方便,但复用和体积要平衡。
怎么做:本节先按上面的方式完成安装、全量导入、注册和使用;当项目进入优化阶段,再考虑按需引入或局部注册。当前不要为了“看起来高级”提前改乱 main.js。
易错点:不要把“全部导入”理解成“永远这样最好”;也不要因为打包大就立刻删样式或乱改导入。先保证功能正确,再根据构建提示和实际使用情况优化。若改完 main.js 后项目报错,按转写稿的排查顺序:看控制台错误、核对包是否安装、核对导入路径和单词、确认导入语句位置是否规范。

课件图示 ​

课件第11页:vant 全部导入 和 按需导入 目标:明确 全部导入 和 按需导入 的区别 v

课件第12页:目标:阅读文档,掌握 全部导入 的基本使用 官网:vant-ui 全部导入: ①

课件第13页:目标:阅读文档,掌握 按需导入 的基本使用 按需导入: ① 安装 vant-ui

课件第3页:项目收获 目标:明确做完本项目,能够收获哪些内容 脚手架自定义项目构建 组件库v

课件第5页:ESlint 代码规范 目标:认识代码规范 代码规范:一套写代码的约定规则。例如

课件第9页:vant 组件库 目标:认识第三方 Vue组件库 vant-ui 组件库:第三方

课件第10页:其他 Vue 组件库 目标:了解其他 Vue 组件库 Vue的组件库并不是唯一的

课件第16页:目标:阅读vant组件库文档,实现底部导航 tabbar 路由设计配置 tabb

代码示例 ​

示例一:全部导入 vant(main.js + App.vue) ​

js
// main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router'

// 1. 导入 vant 组件库
import Vant from 'vant'
// 2. 导入 vant 的样式文件(不导入样式,组件长得就是“裸”的)
import 'vant/lib/index.css'

// 3. 注册:把 vant 里所有的组件一次性全部注册到 Vue 上
Vue.use(Vant)

Vue.config.productionTip = false

new Vue({
  router,
  render: h => h(App)
}).$mount('#app')
vue
<!-- App.vue -->
<template>
  <div id="app">
    <!-- 注册完就能直接当标签用,不用再 import -->
    <van-button type="primary">主要按钮</van-button>
    <van-button type="info">信息按钮</van-button>
  </div>
</template>

<script>
export default {
  name: 'App'
}
</script>

关键行与易错点

  • 安装:yarn add vant@latest-v2(或 npm i vant@latest-v2),本节用的是 vant 2.x,不要装成 3.x/4.x。
  • Vue.use(Vant) 是全部导入,所有组件一次性进来,项目打包体积会变大;按需导入只引入你用到的组件,能节约资源。
  • import 'vant/lib/index.css' 这一行不能漏,漏了组件能用但没样式。
  • 组件标签在模板里是 van-button、van-cell 这种短横线写法,不要写成 VantButton。
  • 全部导入后组件已全局注册,页面里不需要再 import,再 import 反而会重复打包。

示例二:封装单个面经单元格 ArticleItem.vue(van-cell + 插槽 + props) ​

vue
<!-- components/ArticleItem.vue -->
<template>
  <div class="article-item">
    <van-cell>
      <!-- title 插槽:左侧放小图,右侧放标题/作者/发布时间 -->
      <template #title>
        <div class="title-box">
          <img class="avatar" :src="item.avatar" alt="" />
          <div class="info">
            <p class="title">{{ item.stem }}</p>
            <p class="author">作者:{{ item.creator }}</p>
            <p class="time">发布时间:{{ item.created_at }}</p>
          </div>
        </div>
      </template>

      <!-- label 插槽:标题下方的简介 + 点赞量 + 浏览量 -->
      <template #label>
        <div class="label-box">
          <p class="content">{{ item.content }}</p>
          <p class="other">
            <span>点赞 {{ item.likeCount }}</span>
            <span>浏览 {{ item.views }}</span>
          </p>
        </div>
      </template>
    </van-cell>
  </div>
</template>

<script>
export default {
  name: 'ArticleItem',
  props: {
    // 父组件 v-for 循环时把每一行的小对象传进来
    item: {
      type: Object,
      // 对象/数组的默认值必须写成函数,箭头函数返回对象还要再包一层小括号
      default: () => ({})
    }
  }
}
</script>

<style lang="scss" scoped>
.article-item {
  .title-box {
    display: flex;
    align-items: center;
    .avatar {
      width: 40px;
      height: 40px;
      border-radius: 50%;
      margin-right: 10px;
    }
    .title {
      font-size: 14px;
      color: #333;
    }
    .author,
    .time {
      font-size: 12px;
      color: #999;
    }
  }
  .label-box {
    .content {
      font-size: 12px;
      color: #666;
    }
    .other {
      font-size: 12px;
      color: #999;
    }
  }
}
</style>

关键行与易错点

  • 用插槽就必须写双标签(<van-cell> ... </van-cell>),自闭合标签是没法往里传内容的。
  • 插槽简写 #title 等价于 v-slot:title;vant 的 van-cell 支持 title(左侧)、label(标题下方)、default(右侧)、value、icon、right-icon 等插槽,文档里都列了,不确定位置就挨个试一下。
  • default 是自定义右侧内容,本例没用到,所以默认插槽不传值也完全正常。
  • props 名字必须和父组件传的一模一样:父组件传 :item,子组件就得接 item,叫别的名字就接不到。
  • 默认值易错点:default: {} 会直接报错,必须写成函数 default: () => ({});如果省掉小括号写成 () => {},大括号会被当成函数体而不是返回对象,等于返回 undefined。
  • <style lang="scss"> 需要项目里装了 sass 和 sass-loader,否则报 Can't resolve 'sass-loader';不想装就改成 lang="less" 或不写 lang。

示例三:面经列表循环渲染 + van-list 加载更多(Article.vue) ​

vue
<!-- views/Article.vue -->
<template>
  <div class="article-page">
    <!-- van-list 负责监听滚动到底部,自动触发 load 事件 -->
    <van-list
      v-model="loading"
      :finished="finished"
      finished-text="没有更多了"
      @load="onLoad"
    >
      <!-- 循环渲染封装的单元格组件,父传子 :item -->
      <article-item
        v-for="item in list"
        :key="item.id"
        :item="item"
      />
    </van-list>
  </div>
</template>

<script>
import axios from 'axios'

export default {
  name: 'ArticlePage',
  data () {
    return {
      list: [],        // 已加载的所有面经数据(多页累加)
      loading: false,  // 是否正在加载中,由 van-list 自动置 true
      finished: false, // 是否已全部加载完毕
      count: 1,        // 当前请求的页码
      pageTotal: 0     // 总页数,接口返回
    }
  },
  methods: {
    // 方法名必须叫 onLoad,和 van-list 的 @load 对应
    async onLoad () {
      // 1. 拿当前页数据
      const res = await axios.get('http://localhost:3000/api/articles', {
        params: { page: this.count }
      })

      // 2. res.data.data 里才有 rows(数据数组)和 pageTotal(总页数)
      const { rows, pageTotal } = res.data.data

      // 3. 累加到 list 里,注意用展开运算符,不能直接赋值
      this.list.push(...rows)

      // 4. 页码 +1,为下一页做准备
      this.count++
      this.pageTotal = pageTotal

      // 5. 本次加载结束,loading 必须复位,否则不会再触发下一次 load
      this.loading = false

      // 6. 页码超过总页数,说明数据加载完了
      if (this.count > this.pageTotal) {
        this.finished = true
      }
    }
  }
}
</script>
js
// main.js 中把 ArticleItem 注册成全局组件(放在 vant 那几行 import 的下面,别夹在 import 中间)
import ArticleItem from '@/components/ArticleItem'
Vue.component('ArticleItem', ArticleItem)

关键行与易错点

  • van-list 必须把 v-for 的那一段包在中间,和普通列表写法一样,只是外面多套一层。
  • v-model="loading"、:finished="finished" 这两个变量必须有:loading 为 true 表示正在请求、不允许重复加载;finished 为 true 表示没有下一页了。
  • 数据累加是本节最容易错的地方:要 this.list.push(...rows)。写 this.list = rows 会把前一页数据覆盖掉;写 this.list.push(rows) 会变成数组套数组,渲染就崩了。
  • 页码自增 this.count++ 不能忘,忘了就永远请求第一页。
  • loading = false 必须复位,否则滚到底也只“加载中”不请求下一页。
  • 判断结束用 this.count > this.pageTotal,超过总页数就 finished = true,否则会一直请求 42、43 页拿到空数组。
  • 用 van-list 后,created 里手动调一次请求要删掉:组件挂载时 @load 会自动执行一次,手动再调会重复请求第一页。
  • 调试接口报错先看 Network:状态码 401 一定是 token 问题(看请求标头里的 Authorization,Bearer 拼写和空格都要对,本地存储的 key 也要对得上);Can't resolve 之类的是路径/单词写错或缺包。

常见错误 ​

  1. 现象:一进面经列表页就报一片网络错误,控制台状态码全是 401。 原因:请求头里的 token 拼装有问题——Bearer 单词拼错、后面漏空格、或从 localStorage 取值时 key 名与写入时不一致(如 mobel-token 写成 mobile-token),最终取到 null。 修法:打开 Network → 选中失败请求 → 看「请求标头」里的 Authorization,逐字核对前缀拼写、空格、token 值;再回代码核对 localStorage 的 key 与取值写法。记住 401 一定是鉴权问题,不用去翻业务代码。

  2. 现象:控制台报 Cannot find module ... 或 Failed to resolve ...,页面白屏或组件不渲染。 原因:导入路径/文件名拼写不一致(如 components 写成 compfiies、组件名大小写不符),或引用了未安装的第三方包。 修法:先判断这是"自己写的文件"还是"第三方包"。是自己的文件 → 对照目录树逐个字母核对路径与文件名,两边改成一致;是第三方包 → 安装对应依赖。凡是这类报错,原因只有"路径错、单词错、包没装"三种,不要盲改。

  3. 现象:新建 .vue 文件后编译报找不到 sass-loader。 原因:编辑器模板默认把 <style lang="scss"> 写上了,但项目没装 sass 与 sass-loader。 修法:要么装上 sass + sass-loader,要么把 lang 改成 less(并装 less 相关包),要么直接去掉 lang 用普通 CSS。用哪个预处理器就必须装对应的 loader,二者要配套。

  4. 现象:给 props 写默认值 default: {} 后报"默认值必须是函数"。 原因:对象/数组类型的默认值必须用工厂函数返回,直接写字面量会被所有实例共享。 修法:写成 default: () => ({})。注意箭头函数直接返回对象时外层必须加小括号,否则 {} 会被当成函数体而不是返回值。

  5. 现象:上拉加载第二页后,列表只剩 10 条,第一页数据没了。 原因:拿到新一页数据后写成了 this.list = res.data.rows,直接覆盖;或 push 时把整个数组塞进去,变成二维嵌套数组。 修法:改为 this.list.push(...res.data.rows),用展开运算符把新数组里的小对象逐个追加。同时每页成功后 this.page++,否则会反复请求同一页。

  6. 现象:用了 List 组件,滚到底部有"加载中"动画,但数据不增加,或一直转圈停不下来。 原因:回调方法名没写成 onLoad(组件只认这个事件名);或请求完成后忘了把 loading 置回 false。 修法:方法统一命名为 onLoad;请求成功、数据追加完成后立即 this.loading = false;当页码超过接口返回的总页数时 this.finish = true,避免继续请求不存在的页。另外 List 首次渲染会自动触发一次 onLoad,created 里不要再手动调一次。

  7. 现象:Vue 调试工具里翻遍组件树也找不到 list 数据。 原因:数据定义在哪个组件,就必须去哪个组件里看;组件多时容易点错。 修法:用调试工具面板顶部的搜索框,输入组件的 name(有 name 优先按 name 匹配,没有才用组件名),直接定位到目标组件再看 data。

  8. 现象:v-for 渲染出 10 条,但每条内容一模一样。 原因:只做了循环,没把当次遍历的 item 传给子组件,子组件也没用 props 接收。 修法:父组件 :item="item" 传值,子组件用 props 按同名接收(父传的名字和子接的名字必须一致),模板里全部改成 item.xxx 读取。

练习 ​

  1. 故障复现与排查:故意把请求头的 Bearer 改错(拼写、空格、token 取值各错一处),刷新页面后只用 Network 面板把三处问题依次找出来并修好,记录每一步看的是哪个面板、哪一栏。

  2. 组件抽离:把面经列表里的单条单元格结构抽成独立组件,注册为全局组件,并在列表页用 v-for 渲染,要求父传子把每条数据对象传进去,子组件用 props 接收并声明类型与默认值。

  3. 加载更多闭环:用 List 组件实现滚动加载,要求做到:新数据追加而非覆盖、页码自增、加载完重置 loading、到达总页数后显示"没有更多了"。完成后在 Network 里核对每次请求的页码是否依次递增。

  4. 导入方式对比:先用全部导入跑通一个按钮组件,再改成按需导入,分别打包并对比产物体积,写出结论。

  5. 扩展挑战:参照文档自行实现下拉刷新,说明它与"重新请求第一页"在实现上的差别。

原视频对照 ​

#视频时长
1把面经列表数据存到data中05:05
2封装单个ArticleItem组件19:11
3错误排查小结14:32
4遍历渲染面经列表数据10:44
5list组件初步使用16:54
6应用list组件完成加载更多11:07

小结 ​

  1. 组件库的学习路径是"读文档 → 抄基础用法 → 试插槽 → 配自己的样式",课上讲不完所有 API,能自己看文档试出来才是目标。
  2. 遇到不认识的插槽不要猜,写一段内容挨个插进去看渲染位置,一次就能建立位置感。
  3. 页面里重复出现、且多个页面都要用的结构,先抽成组件再注册全局,复用成本最低——判断标准是"这东西在别的页面会不会再出现"。
  4. 列表分页只有三件事:页码自增、数据累加(展开后 push)、结束标志;任何一件漏了都会表现为"数据不对"或"一直转圈"。
  5. 排错顺序固定:先看控制台第一条红字 → 再看 Network → 再看状态码 → 最后对照接口文档。状态码本身就是答案,401 查鉴权,404 查地址。
  6. 判断"对错"的基准是接口文档,不是自己的记忆;和文档一致就是对的,不一致就是错的,若一致仍报错则问题在后端。
  7. 前端报错绝大多数逃不出三类:路径/单词拼错、依赖没装、状态没重置。按这三类逐条排除,比漫无目的改代码快得多。
  8. 别一遇错就问人,先自己走一遍上面的排查流程——排错能力是练出来的,不是听出来的。