vue3,你还为状态为何物而发愁吗?(二)

简介: 为啥需要状态?因为组件划分的非常原子化(细腻),所以造成了很多的组件,那么组件之间就需要一种“通讯方式”,这个就是状态了。不仅仅是传递数据,还可以实现事件总线。

文件结构

基础功能搭建好了之后,剩下的就简单了,建立组件设置模板、控件、组件和使用状态即可。总体结构如下:

9.png


文件结构


列表状态的使用


基础工作做好之后我们来看看,在各个组件里面是如何使用状态的。


1查询

首先看看查询,用户设置查询条件后,查询控件把查询条件记入状态里面。然后调用状态管理里的 reloadFirstPager ,获取列表数据。查询控件支持防抖功能。


<template>
  <!--查询-->
  <nf-el-find
    v-model="listState.query"
    v-bind="findProps"
    @my-change="myChange"
  />
</template>

直接使用查询控件,模板内容是不是很简单了?


import { reactive } from'vue'
// 加载json
import loadJson from'./control/loadjson.js'
// 状态
import VueDS from'vue-data-state'
  // 组件
  import nfElFind from'/ctrl/nf-el-find/el-find-div.vue'
  // 属性:模块ID、查询条件
  const props = defineProps({
    moduleId:  [Number, String]
  })
  // 设置 查询的 meta
  const findProps = reactive({reload: true})
  loadJson(props.moduleId, 'find', findProps)
  // 访问状态
  const { get } = VueDS.useStore()
  // 获取状态
  const listState = get.dataListState()
  // 用户设置查询条件后触发
  const myChange = (query) => {
    // 获取第一页的数据,并且重新统计总数
    listState.reloadFirstPager()
  }


2分页

分页就很简单了,查询条件由查询控件搞定,所以这里只需要按照 el-pagination 的要求,把分页状态设置给 el-pagination 的属性即可。


<template>
  <!--分页-->
  <el-pagination
    background
    layout="prev, pager, next"
    v-model:currentPage="pager.pageIndex"
    :page-size="pager.pageSize"
    :total="pager.pageTotal">
  </el-pagination>
</template>


直接把状态作为属性值。

// 状态
import VueDS from'vue-data-state'
// 访问状态
const { get } = VueDS.useStore()
// 获取分页信息
const pager = get.dataListState().pager


直接获取分页状态设置 el-pagination 的属性即可。翻页的时候 el-pagination 会自动修改 pager.pageIndex 的值,而状态管理里面会监听其变化,然后获取对应的列表数据。


3添加、修改

添加完成之后,总记录数会增加,所以需要重新统计总记录数,然后翻到第一页。而修改之后,一般总记录数并不会变化,所以只需要重新获取当前页号的数据即可。

<template>
  <div>
    <!--表单-->
    <el-form
      ref="formControl"
      v-model="model"
      :partModel="partModel"
      v-bind="formProps"
    >
    </el-form>
    <span class="dialog-footer">
      <el-button @click="">取 消</el-button>
      <el-button type="primary" @click="mysubmit">确 定</el-button>
    </span>
  </div>
</template>


使用表单控件和两个按钮。

import { computed, reactive, watch } from'vue'
import { ElMessage } from'element-plus'
// 加载json
import loadJson from'./control/loadjson.js'
// 状态
import VueDS from'vue-data-state'
// 仿后端API
import service from'./api/data-service.js'
  // 表单组件
  import elForm from'/ctrl/nf-el-form/el-form-div.vue'
  // 访问状态
  const { get } = VueDS.useStore()
  // 定义属性
  const props = defineProps({
    moduleId:  [Number, String], // 模块ID
    formMetaId:  [Number, String], // 表单的ID
    dataId: Number, // 修改或者显示的记录的ID
    type: String// 类型:添加、修改、查看
  })
  // 模块ID + 表单ID = 自己的标志
  const modFormId = computed(() => props.moduleId + props.formMetaId)
  // 子组件里面获取状态
  const dataListState = get.dataListState(modFormId.value)
  // 表单控件的 model
  const model = reactive({})
  // 表单控件需要的属性
  const formProps = reactive({reload:false})
  // 加载需要的 json
  loadJson(props.moduleId, 'form_' + props.formMetaId,  formProps)
  // 仿后端API
  const { getData, addData, updateData } = service(modFormId.value)
  // 监听记录ID的变化,加载数据便于修改
  watch(() => props.dataId, (id) => {
    if (props.type !== 'add') {
      // 加载数据
      getData( id ).then((data) => {
        Object.assign(model, data[0])
        formProps.reload = !formProps.reload
      })
    }
  },
  {immediate: true})
  // 提交数据
  const mysubmit = () => {
    // 判断是添加还是修改
    if (props.type === 'add'){
      // 添加数据
      addData(model).then(() => {
        ElMessage({
          type: 'success',
          message: '添加数据成功!'
        })
        // 重新加载第一页的数据
        dataListState.reloadFirstPager()
      })
    } elseif (props.type === 'update') {
      // 修改数据
      updateData(model, props.dataId).then(() => {
        ElMessage({
          type: 'success',
          message: '修改数据成功!'
        })
        // 重新加载当前页号的数据
        dataListState.reloadCurrentPager()
      })
    }
  }


代码稍微多了一些,基本上就是在合适的时机调用状态里的重新加载数据的事件。


4删除

删除之后也会影响总记录数,所以需要重新统计,然后刷新当前页号的列表数据。删除的代码写在了操作按钮的组件里面,对应删除按钮触发的事件:


case'delete':
        dialogInfo.show = false
        // 删除
        ElMessageBox.confirm('此操作将删除该记录, 是否继续?', '温馨提示', {
          confirmButtonText: '删除',
          cancelButtonText: '后悔了',
          type: 'warning'
        }).then(() => {
          // 后端API
          const { deleteData } = service(props.moduleId + meta.formMetaId)
          deleteData(dataListState.choice.dataId).then(() => {
            ElMessage({
              type: 'success',
              message: '删除成功!'
            })
            dataListState.reloadPager() // 刷新列表数据
          })
        }).catch(() => {
          ElMessage({
            type: 'info',
            message: '已经取消了。'
          })
        })
        break


5快捷键

我是喜欢用快捷键实现一些操作的,比如翻页、添加等操作。用鼠标去找到“上一页”、“下一页”或者需要的页号,这个太麻烦。如果通过键盘操作就能翻页,是不是可以更方便一些呢?比如 w、a、s、d,分别表示上一页、下一页、首页、末页;数字键就是要翻到的页号。

是不是有一种打游戏的感觉?
实现方式也比较简单,一开始打算用 Vue 的键盘事件,但是发现似乎不太好用,于是改用监听document 的键盘事件。


  /**
   * 列表页面的快捷键
   */
  const setHotkey = (dataListState) => {
    // 设置分页、操作按钮等快捷键
    // 计时器做一个防抖
    let timeout
    let tmpIndex = 0// 页号
    document.onkeydown = (e) => {
      if (!(e.target instanceof HTMLBodyElement)) return// 表单触发,退出
      if (e.altKey) {
        // alt + 的快捷键,调用操作按钮的事件
        dataListState.hotkey(e.key)
      } else {
        // 翻页
        const maxPager = parseInt(dataListState.pager.pageTotal / dataListState.pager.pageSize) + 1
        switch (e.key) {
          case'ArrowLeft': // 左箭头 上一页
          case'PageUp':
          case'a':
            dataListState.pager.pageIndex -= 1
            if (dataListState.pager.pageIndex <= 0) {
              dataListState.pager.pageIndex = 1
            }
            break
          case'ArrowRight': // 右箭头 下一页
          case'PageDown':
          case'd':
            dataListState.pager.pageIndex += 1
            if (dataListState.pager.pageIndex >= maxPager) {
              dataListState.pager.pageIndex = maxPager
            }
            break
          case'ArrowUp': // 上箭头
          case'Home': // 首页
          case'w':
            dataListState.pager.pageIndex = 1
            break
          case'ArrowDown': // 下箭头
          case'End': // 末页
          case's':
            dataListState.pager.pageIndex = maxPager
            break
          default:
            // 判断是不是数字
            if (!isNaN(parseInt(e.key))) {
              // 做一个防抖
              tmpIndex = tmpIndex * 10 + parseInt(e.key)
              clearTimeout(timeout) // 清掉上一次的计时
              timeout = setTimeout(() => {
                // 修改 modelValue 属性
                if (tmpIndex === 0) {
                  dataListState.pager.pageIndex = 10
                } else {
                  if (tmpIndex >= maxPager) {
                    tmpIndex = maxPager
                  }
                  dataListState.pager.pageIndex = tmpIndex
                }
                tmpIndex = 0
              }, 500)
            }
        }
      }
      e.stopPropagation()
    }
  }


这段代码,其实是放在状态管理类里面的,拿出来单独介绍一下,避免混淆。

  • document.onkeydown 监听键盘按下的事件,这个 e 并不是原生的 e,而是Vue封装之后的 e。首先要判断一下事件来源,如果是 input 等触发的需要跳过,以免影响正常的数据输入。然后是判断按了哪个按键,根据需求调用对应的函数。

10.png


e 的样子

  • altKey 是否按下了 alt 键。有些快捷键可以是组合方式,本来想用 ctrl 键的,但是发现在网页里面 ctrl 开头的快捷键实在太多,抢不过,所以只好 用 alt。
  • alt + a 相当于按 添加按钮
  • alt + s 相当于按 修改按钮
  • alt + d 相当于按 删除按钮

你觉得 a 代表 add,d 代表 delete吗?其实不是的,a、s、d 的键位可以对应操作按钮里面前三个按钮。就酱。

  • 数字翻页的防抖 如果不做防抖的话,只能实现 1-9 的页号翻页,如果做了防抖的话,基本可以做到三位数页号的翻页。所以手欠做了个防抖。


开源

https://gitee.com/naturefw/nf-vite2-element

10.png

在线演示

https://naturefw.gitee.io/nf-vue-cdn/elecontrol/

nf-vite2-element 的仓库没来得及开通pager服务,所以放在另一个仓库里面了。

本文作者:自然框架

个人网址:jyk.cnblogs.com

声明:本文为 脚本之家专栏作者 投稿,未经允许请勿转载。

相关文章
|
2月前
|
缓存 JavaScript UED
Vue3中v-model在处理自定义组件双向数据绑定时有哪些注意事项?
在使用`v-model`处理自定义组件双向数据绑定时,要仔细考虑各种因素,确保数据的准确传递和更新,同时提供良好的用户体验和代码可维护性。通过合理的设计和注意事项的遵循,能够更好地发挥`v-model`的优势,实现高效的双向数据绑定效果。
142 64
|
2月前
|
JavaScript 前端开发 API
Vue 3 中 v-model 与 Vue 2 中 v-model 的区别是什么?
总的来说,Vue 3 中的 `v-model` 在灵活性、与组合式 API 的结合、对自定义组件的支持等方面都有了明显的提升和改进,使其更适应现代前端开发的需求和趋势。但需要注意的是,在迁移过程中可能需要对一些代码进行调整和适配。
115 60
|
10天前
|
JavaScript API 数据处理
vue3使用pinia中的actions,需要调用接口的话
通过上述步骤,您可以在Vue 3中使用Pinia和actions来管理状态并调用API接口。Pinia的简洁设计使得状态管理和异步操作更加直观和易于维护。无论是安装配置、创建Store还是在组件中使用Store,都能轻松实现高效的状态管理和数据处理。
38 3
|
2月前
|
前端开发 JavaScript 测试技术
Vue3中v-model在处理自定义组件双向数据绑定时,如何避免循环引用?
Web 组件化是一种有效的开发方法,可以提高项目的质量、效率和可维护性。在实际项目中,要结合项目的具体情况,合理应用 Web 组件化的理念和技术,实现项目的成功实施和交付。通过不断地探索和实践,将 Web 组件化的优势充分发挥出来,为前端开发领域的发展做出贡献。
39 8
|
2月前
|
存储 JavaScript 数据管理
除了provide/inject,Vue3中还有哪些方式可以避免v-model的循环引用?
需要注意的是,在实际开发中,应根据具体的项目需求和组件结构来选择合适的方式来避免`v-model`的循环引用。同时,要综合考虑代码的可读性、可维护性和性能等因素,以确保系统的稳定和高效运行。
33 1
|
2月前
|
JavaScript
Vue3中使用provide/inject来避免v-model的循环引用
`provide`和`inject`是 Vue 3 中非常有用的特性,在处理一些复杂的组件间通信问题时,可以提供一种灵活的解决方案。通过合理使用它们,可以帮助我们更好地避免`v-model`的循环引用问题,提高代码的质量和可维护性。
42 1
|
2月前
|
JavaScript
在 Vue 3 中,如何使用 v-model 来处理自定义组件的双向数据绑定?
需要注意的是,在实际开发中,根据具体的业务需求和组件设计,可能需要对上述步骤进行适当的调整和优化,以确保双向数据绑定的正确性和稳定性。同时,深入理解 Vue 3 的响应式机制和组件通信原理,将有助于更好地运用 `v-model` 实现自定义组件的双向数据绑定。
|
2月前
|
JavaScript 索引
Vue 3.x 版本中双向数据绑定的底层实现有哪些变化
从Vue 2.x的`Object.defineProperty`到Vue 3.x的`Proxy`,实现了更高效的数据劫持与响应式处理。`Proxy`不仅能够代理整个对象,动态响应属性的增删,还优化了嵌套对象的处理和依赖追踪,减少了不必要的视图更新,提升了性能。同时,Vue 3.x对数组的响应式处理也更加灵活,简化了开发流程。
|
2月前
|
JavaScript 前端开发 API
从Vue 2到Vue 3的演进
从Vue 2到Vue 3的演进
44 0
|
2月前
|
JavaScript 前端开发 API
Vue.js响应式原理深度解析:从Vue 2到Vue 3的演进
Vue.js响应式原理深度解析:从Vue 2到Vue 3的演进
65 0

热门文章

最新文章