今早学习笔记
侧边栏壁纸
  • 累计撰写 36 篇文章
  • 累计收到 1 条评论

今早学习笔记

ASN__
2026-04-17 / 0 评论 / 6 阅读 / 正在检测是否收录...

今早学习笔记(父子组件通信 + Vuex 核心)

一、父子组件通信(企业级规范)

核心原则:父业务 + 子UI,单向数据流

核心逻辑:子组件只管表单渲染、交互,父组件只管业务、接口,不混乱、高复用

1. 子组件(表单/弹窗)职责

  • 只负责:表单渲染、数据校验、弹窗显隐(自己管理状态)、收集表单数据
  • 不负责:不写任何API请求、不处理业务逻辑
  • 核心方法:

- open\(row\):供父组件调用,打开弹窗(支持新增/编辑,row为编辑数据)

- close\(\):自己关闭弹窗,重置表单

- submit\(\):表单校验通过后,通过 $emit 把数据抛给父组件
  • 状态管理:弹窗isShow、表单formData,全部写在子组件data中,自己管理

2. 父组件(列表页)职责

  • 负责:列表数据、接口请求(新增/编辑/删除)、页面刷新、成功/失败提示
  • 核心操作:

- 通过 ref 调用子组件方法(open\(\)),打开弹窗

- 监听子组件 $emit 事件,接收表单数据,调用对应API

- API请求成功后,刷新列表,给出提示

3. 通信方式(标准写法)

  • 父 → 子:ref 调用子组件方法(如:this.$refs.formRef.open())
  • 子 → 父:$emit 抛数据(如:this\.$emit(\&\#39;submit\&\#39;, formData))
  • 禁忌:子组件不能直接修改props,不能写API,父组件不插手子组件内部状态

4. 示例核心代码(回顾)

// 子组件:只抛数据,不调API
handleSubmit() {
  this.$refs.form.validate(valid => {
    if (valid) {
      this.$emit('submit', this.formData); // 抛数据给父组件
      this.close();
    }
  });
}

// 父组件:接收数据,调API
async handleSubmit(formData) {
  try {
    formData.id ? await updateApi(formData) : await addApi(formData);
    this.$Message.success('操作成功');
    this.getList(); // 刷新列表
  } catch (e) {
    this.$Message.error('操作失败');
  }
}

二、API交互规范(企业级)

核心原则:API 按“全局/页面”划分存放,不滥用

1. API 存放位置(关键区分)

API类型存放位置典型例子原因
全局公共APIVuex 的 action 中获取用户信息、权限、菜单、登录、登出全局共享,调用后需更新Vuex全局状态,多页面复用
页面业务API父组件中列表查询、新增/编辑用户、修改密码、删除仅当前页面使用,数据只渲染当前页面,不共享
组件私有API子组件中(极少)仅子组件自身使用的极小接口不影响其他组件,无需全局/父组件管理

2. 禁忌

  • 禁止子组件写业务API(如新增、修改密码),影响复用性
  • 禁止把页面私有API放进Vuex,导致store臃肿、混乱
  • 禁止API调用后不处理异常(需加try/catch,给出提示)

三、Vuex 核心内容(重点中的重点)

核心定位:全局状态管理中心,解决跨组件共享数据、多层通信问题

1. Vuex 5大核心关键词(定义+作用+示例)

(1)State

  • 定义:Vuex 的数据源,存储全局共享数据(唯一数据源)
  • 通俗说:全局的 data,所有组件都能读取(不能直接修改)
  • 示例:

      `const state = \{

    userInfo: {}, // 全局用户信息
    permissions: [] // 全局权限码
    }`

  • 易错点:不能直接修改 state(如:this.$store.state.userInfo = {} 错误)

(2)Mutation

  • 定义:唯一能修改 State 的地方,必须是同步函数(不能写异步)
  • 作用:同步修改全局状态,接收 state 和 自定义参数
  • 示例:

      `const mutations = \{

    // state:Vuex自动传入;data:commit时传入的参数
    SET\_USER\_INFO(state, data) {
    state.userInfo = data; // 唯一正确修改state的方式
    }
    }`

  • 规则:只能同步,不能写API、定时器

(3)Action

  • 定义:处理异步操作(API请求)、复杂业务逻辑,不直接修改state
  • 作用:调API,拿到数据后,通过 commit 调用 Mutation 修改state
  • 核心语法:第一个参数是 context 上下文对象,可解构简化

      `// 解构写法(ES6,推荐)

    async getUserInfo({ commit }) {
    const res = await getUserInfoApi(); // 异步调API
    commit(\&\#39;SET\_USER\_INFO\&\#39;, res); // 调用mutation改state
    }

// 不解构写法(兼容ES5)
async getUserInfo(context) {
const res = await getUserInfoApi();
context.commit(\&\#39;SET\_USER\_INFO\&\#39;, res); // 完整写法
}`

  • 补充:Action 也能处理同步逻辑,还能通过 dispatch 调用其他Action

(4)Getter

  • 定义:Vuex 的全局计算属性,对 state 数据进行加工、过滤
  • 作用:派生数据,不修改原state,多组件复用相同计算逻辑
  • 示例:

      `// 全局getters\.js

    const getters = {
    userId: state =\> state.user.userInfo.user\_id, // 派生用户ID
    hasPerm: state =\> (permCode) =\> { // 派生权限判断方法
    return state.user.permissions.includes(permCode);
    }
    }`

(5)Module

  • 定义:Vuex 模块化拆分,按业务分模块管理(避免代码臃肿)
  • 核心:必须开启 namespaced: true(命名空间),防止模块间命名冲突
  • 示例(user模块):

      `export default \{

    namespaced: true, // 开启命名空间(企业级必开)
    state: {},
    mutations: {},
    actions: {}
    }`

  • 模块合并:在 store/index.js 中导入所有子模块,合并到 modules 中

2. 关键调用关键词(commit / dispatch)

关键词使用位置调用对象作用示例
commitAction 内部(主要)、组件(极少)Mutation触发同步修改statecommit(\&\#39;user/SET\_USER\_INFO\&\#39;, res)
dispatch组件(主要)、Action 内部Action触发异步操作/业务逻辑this.$store.dispatch(\&\#39;user/getUserInfo\&\#39;)

3. 组件中使用 Vuex(map系列辅助函数)

  • 核心原则:数据类(state、getters)放 computed;方法类(mutations、actions)放 methods
  • 示例(完整写法):

      `import \{ mapState, mapGetters, mapMutations, mapActions \} from \&\#39;vuex\&\#39;
    

export default {
computed: {

// 子模块state(带命名空间,必须写模块名)
\.\.\.mapState\(\&\#39;user\&\#39;, \[\&\#39;userInfo\&\#39;, \&\#39;permissions\&\#39;\]\),
// 全局getters(不用写模块名)
\.\.\.mapGetters\(\[\&\#39;userId\&\#39;, \&\#39;hasPerm\&\#39;\]\)

},
methods: {

// 子模块mutations(带命名空间)
\.\.\.mapMutations\(\&\#39;user\&\#39;, \[\&\#39;SET\_USER\_INFO\&\#39;\]\),
// 子模块actions(带命名空间)
\.\.\.mapActions\(\&\#39;user\&\#39;, \[\&\#39;getUserInfo\&\#39;, \&\#39;logout\&\#39;\]\),

// 调用方式(映射后直接调用)
login\(\) \{
  this\.getUserInfo\(\); // 等价于 this\.$store\.dispatch\(\&\#39;user/getUserInfo\&\#39;\)
  this\.SET\_USER\_INFO\(data\); // 等价于 this\.$store\.commit\(\&\#39;user/SET\_USER\_INFO\&\#39;, data\)
\}

}
}`

  • 易错点:

- mapState/mapGetters 不能放 methods 里,反之亦然

- 带命名空间的模块,映射时必须写模块名(如 \&\#39;user\&\#39;)

4. 多参数传递(Vuex 官方规则)

  • 规则:Mutation / Action 只能接收 1 个自定义参数
  • 多参数解决方案:打包成一个对象传递

      `// Action 中 commit(多参数打包)

    commit(\&\#39;SET\_USER\_INFO\&\#39;, {
    userInfo: res,
    test: \&\#39;测试数据\&\#39;
    })

// Mutation 中接收(解构对象)
SET\_USER\_INFO(state, { userInfo, test }) {
state.userInfo = userInfo;
state.testData = test;
}`

5. 兼容写法(不支持ES6/ES2020)

现代语法(ES6+)兼容写法(ES5)
解构 { commit }context.commit(不解构,完整调用)
obj?.a?.b(可选链)obj \&\& obj.a \&\& obj.b(逻辑与替代)
SET\_USER\_INFO(state, { a, b })SET\_USER\_INFO(state, payload) → const a = payload.a

补充:实际开发中用 Babel 自动转译,不用手写兼容代码

6. Vuex 模块化目录结构(企业级标准)

src/store/
├── index.js          # 入口文件,合并所有子模块、挂载Vuex
├── getters.js        # 全局getter汇总
└── modules/          # 业务模块拆分
    ├── user.js       # 用户模块(信息、权限)
    ├── permission.js # 权限模块(菜单、按钮权限)
    └── layout.js     # 布局模块(侧边栏、全屏)

四、核心易错点汇总(重点记忆)

  • 调用Action:this.$store\.dispatch\(\)(不是 $state,dispatch 不拼写错误)
  • Mutation 只能同步,Action 可异步,不能搞反
  • 修改state必须通过Mutation,不能直接修改
  • Vuex模块必须开启 namespaced: true,否则会命名冲突
  • map系列辅助函数:数据类放computed,方法类放methods
  • 多参数必须打包成对象,Vuex不支持多个自定义参数
  • API存放:全局公共API放Vuex,页面业务API放父组件

五、整体核心流程(串联所有知识点)

  1. 组件点击操作(如打开弹窗)→ 通过 ref 调用子组件 open() 方法
  2. 子组件完成表单交互、校验 → 通过 $emit 把数据抛给父组件
  3. 父组件接收数据 → 调用页面业务API(新增/编辑等)→ 提示+刷新列表
  4. 若涉及全局数据(如用户信息)→ 组件 dispatch 调用 Vuex 的 Action
  5. Action 调全局API → 拿到数据后 commit 调用 Mutation
  6. Mutation 同步修改 state → 组件通过 mapState/mapGetters 读取数据,视图更新

通用企业级设计思路:父业务 + 子 UI 表单弹窗模式
这套思路适用于所有后台管理系统:新增弹窗、编辑弹窗、表单抽屉、密码框、配置面板等,只要是「列表 + 表单弹窗」的结构,都能直接套用。
全程遵守:单向数据流、职责分离、高复用、无全局污染,是国内互联网公司后台系统的标准范式。
一、核心设计原则(4 条铁律)
子组件只做 UI,不碰业务
子组件 = 纯渲染组件:只负责表单、校验、弹窗开关、收集数据,绝不写接口。
父组件只做业务,不碰渲染细节
父组件 = 业务控制器:负责列表、接口请求、状态管理、刷新页面。
通信只走两条路
父 → 子:ref 调用方法
子 → 父:$emit 抛数据
谁的状态谁管理
弹窗显隐、表单数据,全部由子组件自己管理,父组件不插手内部状态。
二、通用职责划分(背会直接用)
🔹 子组件(Form 弹窗)负责:
弹窗显示 / 隐藏(isShow 写在自己 data)
表单数据(formData)
表单校验规则
打开 / 重置 / 关闭方法
表单校验通过后,把数据抛给父组件
不写任何 API、不请求后端
🔹 父组件(List 列表页)负责:
表格数据、分页、查询
点击按钮 → 通过 ref 打开子组件
接收子组件抛来的表单数据
调用新增 / 编辑 / 删除接口
接口成功 / 失败提示
刷新列表数据
三、通用开发步骤(万能流程)

  1. 拆分组件
    一个业务弹窗 = 一个独立子组件
    新增用户 → UserAddForm.vue
    编辑密码 → LoginPwdForm.vue
    配置参数 → ConfigForm.vue
  2. 编写子组件(固定结构)
    写弹窗(Modal/Drawer)
    写表单 + 校验规则
    data 里定义:isShow、formData、loading
    写公共方法:open(row)、close()、submit()
    校验通过后 $emit('submit', formData)
  3. 父组件引入并挂载
    import 子组件
    注册 components
    模板中放入子组件,绑定 ref 和 @submit
    点击按钮调用 this.$refs.xxx.open()
  4. 父组件处理业务逻辑
    接收子组件数据
    调用对应 API
    成功提示 + 刷新列表
    失败提示
0

评论

博主关闭了所有页面的评论