Skip to content

面经 PC 端(上)(三) ​

本页对应 面经 PC 端(上),共 6 段视频,约 57 分钟。

学习目标 ​

  1. 能够从 Vuex 模块的 state 中读取购物车列表,并通过 v-for 渲染商品,或通过 props 将单个商品数据传给子组件完成展示。
  2. 能够为商品加减按钮绑定点击事件,根据当前数量计算新数量,并实现数量为 1 时禁止继续减少的边界判断。
  3. 能够在 Vuex action 中同时完成两件事:调用 mutation 更新仓库中的商品数量,并发送 PATCH 请求按商品 ID 更新服务器数据,刷新后数量仍保持正确。
  4. 能够使用 getters 计算购物车商品总数量和总价格,并在组件中通过 mapGetters 映射后渲染到页面。
  5. 能够说明 Vuex 中 state、getters、mutation、action 的调用方式,并正确使用模块名/方法名及辅助函数写法。
  6. 能够使用 Vue CLI 创建 Vue2 + Vuex + SCSS + Element UI 项目,完成依赖安装、Element UI 引入配置,并使用 pnpm 启动项目验证页面效果。

知识点 ​

本节范围与转写稿说明 ​

本节笔记标题是“登录表单中的细节分析”,核心是 6 个表单细节:@submit、v-model、name、label、:rules、placeholder。需要说明:你给的转写稿实际在讲购物车渲染、Vuex 的 mutation/action、项目安装等内容,并没有登录表单的推导过程。因此下面不把购物车代码硬套成登录代码,只借用转写稿里可迁移的调试意识:先看页面控制台,不要只盯终端;代码改好保存后错误会自动消失,不要反复重启终端。转写稿中的“VVX/VUEX”应纠正为 Vuex,“付传子”应理解为父子传值,“mation”应理解为 mutation。本节笔记未附图片与原始代码,下面用最小表单片段标注各知识点位置。

一、@submit 事件:提交入口要可控 ​

为什么需要:登录表单不能只靠按钮点击,用户按回车也应提交;同时要阻止浏览器默认刷新。转写稿里反复强调“一刷新数据就恢复”,登录表单同理,默认提交刷新会打断前端校验和接口流程。
怎么想:把提交统一收口到 @submit,按钮写 type="submit",在事件里做校验和请求。
怎么做:用 @submit.prevent="handleSubmit",阻止默认行为后再执行登录逻辑。
易错点:只给按钮写 @click,回车不触发;忘记 .prevent,页面刷新;按钮未写 type="submit"。

html
<form @submit.prevent="handleSubmit">
  <button type="submit">登录</button>
</form>

二、v-model 双向绑定:让数据和输入框同步 ​

为什么需要:登录需要拿到用户输入,手动监听 input 再赋值很繁琐。
怎么想:输入框显示什么由数据决定,用户输入又写回数据,形成双向绑定。
怎么做:v-model="form.username",提交时直接读 form.username。
易错点:只写 :value 不写 v-model,输入不更新;v-model 绑定的对象层级写错;密码框忘记 type="password"。

三、name 属性:收集的 key 要与接口文档对应 ​

为什么需要:提交给后端的字段名必须和接口文档一致,否则后端收不到。
怎么想:name 不是给人看的标题,而是数据键名。先看接口文档要求 username 还是 userName,再写 name。
怎么做:name="username",并与 v-model 的字段、接口参数保持同一套命名。
易错点:前端写 name="user",接口要 username;大小写不一致;多个输入框 name 重复。

四、label:输入框的标题与可访问性 ​

为什么需要:用户要知道每个框填什么;点击文字能聚焦输入框,体验更好。
怎么想:label 是标题,不是占位提示。标题应长期可见,提示语只是辅助。
怎么做:<label for="username">用户名</label> 配合 <input id="username">,for 与 id 对应。
易错点:只写文字不写 for;for 和 id 不一致;把 label 当 placeholder 用。

五、:rules:表单校验规则 ​

为什么需要:登录不能等接口报错才发现必填、格式问题,前端要先校验。
怎么想:规则要围绕字段名配置,:rules 绑定规则对象,表单项通过 prop 找到对应规则。
怎么做:在组件库表单中写 :rules="rules",规则里配置 required、message、trigger 等。
易错点:规则字段名和 prop 不一致;只写 rules 没写 prop;提交时忘记调用校验方法。

六、placeholder:提示语不是标题 ​

为什么需要:提示用户输入格式,例如“请输入手机号”。
怎么想:placeholder 输入后消失,不能承担字段标题职责。
怎么做:placeholder="请输入用户名",简洁说明输入内容。
易错点:把必填说明全放占位符;提示语与 label 重复;密码框提示“请输入密码”却未说明规则。

七、调试与易错点汇总 ​

转写稿里“错误看页面控制台,不要只盯终端”的习惯可以直接迁移:登录提交失败时,先看控制台报错、网络请求参数和接口文档字段名。重点检查三组对应关系:v-model 字段、name 字段、接口文档字段是否一致;label for 与 input id 是否一致;:rules 的 key 与表单项 prop 是否一致。提交入口统一用 @submit.prevent,不要只写按钮点击。这样登录表单的细节才不容易散。

本节目标与转写稿说明 ​

本节要完成注册页的静态布局:先把页面骨架、表单字段、校验规则、提交入口和登录跳转搭好,提交逻辑暂时只打印表单值。需要说明的是,提供的转写稿主要围绕购物车、Vuex、项目安装等内容,未包含“注册静态布局”的推导和调试记录;因此下面不虚构调试过程,只依据 Register.vue 笔记代码展开。本节笔记没有图片资源,故不添加图片占位。

页面骨架:NavBar 与页面容器 ​

为什么需要:注册页首先要有明确的页面标题和内容容器,否则后续表单元素没有稳定的布局落点。怎么想:用 van-nav-bar 承担顶部标题,外层 div.login-page 作为页面容器,表单和登录链接都放在容器内。怎么做:写入 <van-nav-bar title="面经注册" />,下面接 van-form。易错点:van-nav-bar 是 Vant 组件,项目需已引入 Vant;标题写在 title 属性中,不要写成普通文本节点。

表单容器:van-form 与提交入口 ​

为什么需要:注册页需要统一收集用户名、密码,并在提交时做校验。怎么想:用 van-form 包住所有字段,把提交事件统一挂在表单上,而不是散落在按钮上。怎么做:写 <van-form @submit="onSubmit">,在 methods 中定义 onSubmit(values),先 console.log('submit', values) 观察提交值。易错点:@submit 要写在 van-form 上;values 是表单收集到的对象,字段名由 name 决定,所以每个 van-field 都要写 name。

用户名/密码字段与 v-model ​

为什么需要:输入框既要显示用户输入,又要让脚本拿到值,必须做双向绑定。怎么想:用户名和密码都用 v-model 绑定到 data 中的 username、password;密码额外设置 type="password" 隐藏输入。怎么做:用户名 van-field 写 v-model="username" name="username" label="用户名" placeholder="用户名";密码同理,并加 type="password"。易错点:v-model 绑定的变量必须在 data 中初始化;name 不要漏,否则提交对象里的字段名不符合预期;密码框不要忘记 type="password"。

校验规则:required 与 pattern ​

为什么需要:静态布局阶段就把前端校验挂上,能提前暴露空值和格式问题。怎么想:用 Vant 的 rules 数组,一条规则管必填,一条规则管格式。怎么做:用户名规则为 { required: true, message: '请填写用户名' } 和 { pattern: /^\w{5,}$/, message: '用户名至少包含5个字符' };密码规则为必填和 /^\w{6,}$/,提示“密码至少包含6个字符”。易错点:rules 是数组,不要写成对象;pattern 是正则,不是字符串;\w 表示字母、数字、下划线,并不等价于“任意字符”;两条规则是并列关系,空值时也会触发格式规则,实际提示以组件校验顺序为准。

提交按钮:native-type 的关键 ​

为什么需要:按钮要真正触发 van-form 的提交事件,而不是只做样式展示。怎么想:van-button 的 type="primary" 控制按钮视觉样式,原生提交行为要用 native-type="submit"。怎么做:写 <van-button block type="primary" native-type="submit">注册</van-button>,外层用 style="margin: 16px" 留出间距。易错点:不要写成 type="submit",那会改变按钮样式语义;block 让按钮占满一行;按钮必须在 van-form 内部,否则提交事件不会按表单流程触发。

登录跳转与样式 ​

为什么需要:注册页通常要提供“已有账号去登录”的入口。怎么想:用 router-link 做前端路由跳转,目标为 /login;用 scoped less 写局部样式。怎么做:写 <router-link class="link" to="/login">有账号,去登录</router-link>,样式设置颜色、字号、右内边距和右浮动。易错点:to 指向的路由必须已配置;float: right 可能影响父容器高度,后续若布局异常可考虑清除浮动或改用 flex;scoped 只作用于当前组件,类名要写对。

完整代码 ​

jsx
<template>
  <div class="login-page">
    <van-nav-bar title="面经注册" />

    <van-form @submit="onSubmit">
      <van-field
        v-model="username"
        name="username"
        label="用户名"
        placeholder="用户名"
         :rules="[
          { required: true, message: '请填写用户名' },
          { pattern: /^\w{5,}$/, message: '用户名至少包含5个字符' }
        ]"
      />
      <van-field
        v-model="password"
        type="password"
        name="password"
        label="密码"
        placeholder="密码"
        :rules="[
          { required: true, message: '请填写密码' },
          { pattern: /^\w{6,}$/, message: '密码至少包含6个字符' }
        ]"
      />
      <div style="margin: 16px">
        <van-button block type="primary" native-type="submit"
          >注册</van-button
        >
      </div>
    </van-form>
    <router-link class="link" to="/login">有账号,去登录</router-link>
  </div>
</template>

<script>
export default {
  name: 'Register-Page',
  data () {
    return {
      username: '',
      password: ''
    }
  },
  methods: {
    onSubmit (values) {
      console.log('submit', values)
    }
  }
}
</script>

<style lang="less" scoped>
.link {
  color: #069;
  font-size: 12px;
  padding-right: 20px;
  float: right;
}
</style>

易错点汇总 ​

先检查 Vant 组件是否可用;再检查 v-model 变量是否在 data 中;然后检查每个字段是否有 name;最后确认提交按钮使用 native-type="submit"、表单使用 @submit。静态阶段不写接口请求是合理的,onSubmit 只打印 values 便于后续接注册接口。

课件图示 ​

课件第2页:项目演示 目标:查看项目效果,明确功能模块 登录 注册 首页 面经详情 面经 收

课件第15页:目标:分析项目页面,设计路由,配置一级路由 路由设计配置 登录 注册 首页 面经

课件第19页:目标:阅读文档,实现 登录页 & 注册页 静态布局 登录 & 注册功能

课件第20页:目标:阅读文档,实现 登录页 & 注册页 静态布局 登录 & 注册功能 阅读文档

课件第21页:目标:将 axios 请求方法,封装到 request 模块 登录 & 注册功能

课件第22页:目标:将请求封装成方法,统一存放到 api 模块,与页面分离 登录 & 注册功能

课件第23页:目标:将请求封装成方法,统一存放到 api 模块,与页面分离 登录 & 注册功能

课件第24页:目标:阅读文档 toast轻提示,完成 注册功能 提示&跳转 登录 & 注册功能

代码示例 ​

1. 注册页静态布局(Register.vue) ​

vue
<template>
  <div class="login-page">
    <van-nav-bar title="面经注册" />

    <van-form @submit="onSubmit">
      <van-field
        v-model="username"
        name="username"
        label="用户名"
        placeholder="用户名"
        :rules="[
          { required: true, message: '请填写用户名' },
          { pattern: /^\w{5,}$/, message: '用户名至少包含5个字符' }
        ]"
      />
      <van-field
        v-model="password"
        type="password"
        name="password"
        label="密码"
        placeholder="密码"
        :rules="[
          { required: true, message: '请填写密码' },
          { pattern: /^\w{6,}$/, message: '密码至少包含6个字符' }
        ]"
      />
      <div style="margin: 16px">
        <van-button block type="primary" native-type="submit"
          >注册</van-button
        >
      </div>
    </van-form>

    <router-link class="link" to="/login">有账号,去登录</router-link>
  </div>
</template>

<script>
export default {
  name: 'Register-Page',
  data () {
    return {
      username: '',
      password: ''
    }
  },
  methods: {
    onSubmit (values) {
      console.log('submit', values)
    }
  }
}
</script>

<style lang="less" scoped>
.link {
  color: #069;
  font-size: 12px;
  padding-right: 20px;
  float: right;
}
</style>

关键行与易错点

  • @submit="onSubmit":点击带 native-type="submit" 的按钮时自动触发 submit 事件,表单校验通过后才会执行;按钮漏写 native-type="submit" 就永远进不了 onSubmit。
  • v-model="username" / v-model="password":双向绑定,data 里必须先声明这两个字段的初始值,否则绑定不生效。
  • name="username" / name="password":这是提交时收集的 key,必须和接口文档的字段名对应,写错了后端收不到参数。
  • label="用户名":输入框的标题;placeholder="用户名":输入框的提示语,两者是不同属性,不要混用。
  • :rules="[...]":冒号不能省,省了就是普通字符串;required: true 管必填,pattern: /^\w{5,}$/ 是正则,{5,} 表示至少 5 个字符,正则不要加引号。
  • type="password":密码框必须加,否则输入内容明文显示。
  • onSubmit(values):形参 values 是 { username, password } 对象,就是按各 van-field 的 name 收集来的。
  • .link 用了 float: right,父容器会塌陷,若布局异常需自行清除浮动;scoped 保证样式只作用于当前组件。

常见错误 ​

  • 现象:表单提交后 values 字段名与接口不一致。→ 原因:name 属性未按接口文档核对。→ 修法:逐项核对 name,提交前打印 values 验证。
  • 现象:点击“注册”没有触发表单校验/提交。→ 原因:按钮未设 native-type="submit" 或 van-form 未绑定 @submit。→ 修法:补齐 @submit="onSubmit" 与 native-type="submit"。
  • 现象:输入后校验提示不对或校验不到值。→ 原因:v-model 字段与 data 声明、rules 配置不一致。→ 修法:统一字段名,按字段分别配置 required/pattern。
  • 现象:购物车加减后页面变了,刷新又恢复。→ 原因:只改 Vuex state,未请求服务器持久化。→ 修法:action 中先发 PATCH,再 commit mutation 改本地状态。
  • 现象:更新数量时改错商品或只传 count。→ 原因:缺少唯一标识 id。→ 修法:传 { id, count },按 id 查找后更新。
  • 现象:数量能减到 0 或负数。→ 原因:未做边界判断。→ 修法:n === -1 && item.count === 1 时直接返回。
  • 现象:json-server 启动后端口/数据异常。→ 原因:终端当前路径找不到目标 json 文件。→ 修法:补全文件路径,或先 cd 到对应目录再启动。
  • 现象:终端报错后反复重启,问题仍存在。→ 原因:主要错误应看浏览器控制台,终端不是首要排查入口。→ 修法:改代码并保存,以页面/控制台报错为准。

练习 ​

  1. 在 Register.vue 中完善用户名、密码校验:用户名至少 5 位、密码至少 6 位;提交时打印 values,并检查 name 是否与接口文档一致。
  2. 实现购物车加减:点击 +/- 调用同一方法并传 ±1;当数量为 1 时禁止继续减,说明最终传给 action 的参数结构。
  3. 完成 updateCount action:要求先更新服务器数据,再通过 mutation 更新 Vuex 数据,并说明为什么不能只改 state。
  4. 用 getters 计算购物车总数量和总价格,并在 Footer 组件中用 mapGetters 使用;说明 mapGetters 应放在哪个选项里。
  5. 模拟从项目根目录启动 cut.json 失败,给出两种修复方式,并说明判断终端路径是否正确的依据。

原视频对照 ​

#视频时长
1购物车-渲染数据10:13
2购物车-点击加号减号08:57
3购物车-更新数量12:03
4购物车-总数量和总价格06:31
5vuex小结09:22
6安装新项目10:12

小结 ​

  • 表单字段 name 与接口文档一致,是提交数据可用的前提。
  • 表单校验生效依赖 van-form @submit 与提交按钮类型正确。
  • Vuex 中改 state 走 mutation,接口请求与异步逻辑放 action。
  • 购物车数量更新要同时处理服务器持久化和本地状态,且必须携带 id + count。
  • 数量边界判断和循环 key 使用唯一 id,是购物车稳定性的关键。
  • 总数量、总价格适合用 getters,组件侧用 mapGetters 并放在 computed。
  • json-server 启动以终端路径能准确找到 json 文件为准,排查优先看浏览器控制台。
  • 新项目脚手架按文档选型,组件库引入后先用简单按钮做验证。