Vue(九) 全局事件总线、Todo案例应用全局事件总线、消息订阅与发布、Todo案例应用消息订阅、编辑Item、$nextTick

news2024/11/28 17:52:15

文章目录

  • 一、全局事件总线 (GlobalEventBus)
    • 1. 总线前言
    • 2. 安装全局事件总线
    • 3. 使用总线事件
    • 4. 解绑总线事件
  • 二、Todo案例应用全局事件总线
  • 三、消息订阅与发布
    • 1. 前言
    • 2. 使用步骤
  • 四、Todo案例应用消息订阅
  • 五、Todo案例编辑Item
  • 六、$nextTick
  • 修改后的Todo完整代码

一、全局事件总线 (GlobalEventBus)

1. 总线前言

全局事件总线可以实现任意组件间通信。总线是独立于所有组件之外的。
在这里插入图片描述
假设总线名为x,总线x应该满足这两个条件:所有组件都能看的见,且能调用$on(绑定),$off(解绑),$emit(触发)

问题1:绑在谁身上,才能让所有组件都能看的见
可以将总线绑在Vue原型:Vue.prototype上。之前学过有一个重要的内置关系:VueComponent.prototype.__proto__===Vue.prototype。有了这个关系,可以将组件实例对象访问到Vue原型上的属性、方法(即顺着图中绿色的线)。
在这里插入图片描述

// main.js
Vue.prototype.x = {a:1,b:2}; //随便给总线x赋了个值

在School组件及Student组件中检验

// School组件  
mounted () {
    console.log('School', this.x);
},
// Student组件
mounted () {
  console.log('Student', this.x);
},

在这里插入图片描述
  确实所有组件都能看到x

问题二:总线得能调用$on/$emit/$off

$on/​$emit/$off都在Vue的原型对象上,x的值是对象时,无法调用这三个方法。所以x的值要么是vm,要么是vc

Vue.prototype.x = vm/vc
  • 绑定vc
// Vue.extend({}) 返回的是VueComponent函数,(就是刚学组件时,创建组件的写法)
const Demo = Vue.extend({})
// 通过构造函数得到组件实例,d就是组件实例,即vc
const d = new Demo()
Vue.prototype.x = d;
  • 绑定vm

  由于main.js中有一个现成的vm,所以一般选择赋值vm:
在这里插入图片描述
写在(1)处,vm还为创建;写在(3)处,有点晚了,此时App整个组件已经放到页面上去了,组件里对于$on的调用已经执行了,会报错。正确写法:
在这里插入图片描述

2. 安装全局事件总线

一般名称不用x,用$bus

  • 方式一:安装vc
const Demo = Vue.extend({})
const d = new Demo()
Vue.prototype.x = d;
  • 方式二:安装vm(更通用)
new Vue({
  el: '#app',// 挂载容器
  render: h => h(App),
  beforeCreate () {
    // 安装全局事件总线
    Vue.prototype.$bus = this
  }
})

3. 使用总线事件

School组件与Student组件是兄弟组件。School需要获取到Student组件内的学生姓名(name)的值。
School需要借助总线得到数据,所以School组件得给总线绑定事件:

mounted () {
    // 给总线绑定事件‘getName’,并写好事件的回调函数
    this.$bus.$on('getName', (name) => {
        console.log('School组件收到了学生姓名', name);
    })
},

Student组件触发总线上的事件,进而调用了School里的回调函数

methods: {
    giveName () {
        this.x.$emit('getName', this.name)
    }
},

在这里插入图片描述

4. 解绑总线事件

  组件在总线上绑定事件是想通过总线获取某些数据。所有组件都往这个总线bus上绑定事件,所以当组件被销毁时,该组件在总线$bus上绑定的事件也应该被解绑,不应该再占着了。

School组件

 beforeDestroy () {
    // 在组件销毁之前,在总线中解绑该事件
    this.$bus.$off('getName') // 指明要解绑的事件
   // this.$bus.$off() 这样写会将所有人给总线绑定的事件都解绑了
  }

二、Todo案例应用全局事件总线

之前App组件与孙组件MyItem通信是通过MyList,以props的方式。现在改为全局事件总线的方式

1、安装全局事件总线

// 创建vm实例
new Vue({
  // 将App组件放入容器中
  render: h => h(App),
  beforeCreate () {
    Vue.prototype.$bus = this     // 安装事件总线
  }
}).$mount('#app') // 挂载容器

2、App.vue
取消给MyList传递函数,MyList也取消接收,取消给MyItem传递函数。MyItem同样也取消props接收函数。
在这里插入图片描述
绑定事件

  mounted () {
    // 事件总线上绑定事件
    this.$bus.$on('checkTodo', this.checkTodo)
    this.$bus.$on('deleteTodo', this.deleteTodo)
  },
  beforeDestroy () {
    // 组件销毁时,销毁总线上的事件
    this.$bus.$off(['checkTodo', 'deleteTodo'])
  },

3、MyItem.vue
在这里插入图片描述

三、消息订阅与发布

消息订阅与发布是一个理念,不是技术。

1. 前言

以报纸的订阅与发布为例,包含两部分:
1、订阅报纸:需要提供住址
2、邮递员送报纸:报纸

消息的订阅与发布:
1、订阅消息:说明订阅什么消息 以及 回调函数
2、发布消息:发送消息内容
在这里插入图片描述
A组件订阅了C组件的demo消息,回调函数是test。C组件一发布demo消息,A由于订阅了该消息,其回调函数test就会被调用,消息内容666就以参数的形式传递给A组件。

需要数据的人----订阅消息
提供数据的人----发布消息。

2. 使用步骤

以School组件需要Student组件的学生姓名数据为例:

  1. 安装pubsub:npm i pubsub-js(用这个库去实现消息订阅与发布的理念)

  2. 引入(School与Student都要引入): import pubsub from 'pubsub-js'

  3. 接收数据:School组件想接收数据,则在School组件中订阅消息,订阅的回调留在A组件自身。

     mounted () {
        // 订阅消息(消息名,回调函数(消息名,参数))
        // msgName的值是消息名,data才是需要传递的参数
        // 将pubId放在vc上,方便后续取消订阅
        this.pubId = pubsub.subscribe('hello', (msgName, data) => {
          console.log('School订阅消息')
          console.log(data);
        })
      },
    
  4. Student提供数据

    giveInfo () {
        pubsub.publish('hello', this.name)
    }
    
  5. 最好在beforeDestroy钩子中,用PubSub.unsubscribe(pid)去取消订阅。

    beforeDestroy () {
        // 取消订阅
        pubsub.unsubscribe(this.pubId)
    }
    

四、Todo案例应用消息订阅

将MyItem里的删除改为消息订阅的形式:

App.vue

import pubsub from 'pubsub-js'
methods:{
   // 因为回调函数的第一个参数是msgName,函数里用不到这个参数,
   // 所以采用一个_占位,表示接收参数但不用
    deleteTodo (_, id) {
      this.todos = this.todos.filter((todo) => {
        return todo.id !== id
      })
    },
 }
  mounted () {
    // 订阅消息
    this.pubId = pubsub.subscribe('deleteTodo', this.deleteTodo)
  },
  beforeDestroy () {
    // 取消订阅
    pubsub.unsubscribe(this.pubId)
  },

MyItem.vue

import pubsub from 'pubsub-js'
 // 处理删除
 handleDelete (id) {
   if (confirm('确定要删除吗?')) {
     pubsub.publish('deleteTodo', id)
   }
 }

五、Todo案例编辑Item

1、鼠标移到item上,显示编辑按钮
2、 点击编辑按钮时,前边变成input框,框里的内容是todo.title。
3、编辑完成之后,input框失去焦点时,又变成文字展示。
解决:给todo身上加一个isEdit属性。

1、 鼠标悬浮在MyItem上,显示编辑按钮
在这里插入图片描述

<!--MyItem.vue-->
<button class="btn btn-danger" @click="handleDelete(todo.id)">删除</button>
<button class="btn btn-edit"">编辑</button>
<style>
/* 按钮的样式是写在App.vue里的
 因为同一个项目中,不同组件的删除按钮都是同一个风格的,因此只需要写一遍即可。不需要再每个组件里都写一遍。
 */
.btn-edit {
  color: #fff;
  background-color: skyblue;
  border: 1px solid rgb(98, 182, 216);
  margin-right: 5px;
}
</style>

2、点击编辑按钮时,前边变成input框,框里的内容是todo.title。
在这里插入图片描述
添加点击事件,给todo添加isEdit属性。如果是这样添加属性:todo.isEdit = true。这个属性确实加进todo里了,但是,这不是响应式的,点击编辑按钮时,这个属性值确实改变了,但不会被Vue监测到,也不会引起页面的更新。所以应该添加响应式的属性

<!--当处于编辑状态时,不应该有编辑按钮,因此采用v-show-->
 <button class="btn btn-edit" @click="handleEdit(todo) v-show="!todo.isEdit"">编辑</button>
 <script>
   handleEdit (todo) {
   // 防止每次点击编辑,都给todo加一下isEdit属性,因此用if判断一下是否需要添加该属性
   //  if里的条件判断还可以写成:if ('isEdit' in todo)
      if (todo.isEdit !== undefined) {
        console.log('todo里有isEdit属性了');
        todo.isEdit = true
      } else {
        console.log('todo没有isEdit属性');
        this.$set('todo', 'isEdit', true)
      }
    },
</script>

页面的变化:文字内容与输入框只能存在一个

  <span v-show="!todo.isEdit">{{ todo.title }}</span>
  <input type="input" :value="todo.title" v-show="todo.isEdit" />

3、编辑完成之后,input框失去焦点时,又变成文字展示。
在这里插入图片描述

给输入框添加一个失去焦点事件

  <input
    type="input"
    :value="todo.title"
    v-show="todo.isEdit"
    @blur="handleBlur(todo,$event)"
  />
<script>
// 这个函数里不用再考虑给todo添加isEdit属性了,既然失去焦点,说明曾经获得过焦点,也就是能够编辑,有isEdit属性
    handleBlur (todo, e) {
      todo.isEdit = false
      // 判断输入是否为空,为空的话不能够进行修改
      if (!e.target.value.trim()) return alert('输入为空')
      // 触发全局总线的事件,真正修改todo数据
      this.$bus.$emit('updateTodo', todo.id, e.target.value)
    }
</script>

App.vue

    // 编辑todo内容
    updateTodo (id, title) {
      this.todos.forEach((todo) => {
        if (todo.id === id) {
          todo.title = title
        }
      })
    },
  mounted () {
    this.$bus.$on('updateTodo', this.updateTodo)
  },
  beforeDestroy () {
    // 组件销毁时,销毁总线上的事件
    this.$bus.$off(['updateTodo'])
  },

仍然存在的问题:
点击编辑按钮后,若不想编辑了,需要先点击输入框(因为变成输入框的时候,输入框并没有自动获取焦点),再让数据框失去焦点,才能够让其不是编辑状态。
在这里插入图片描述

六、$nextTick

需要:一点击编辑按钮,输入框自动获取焦点。

 <input
   type="input"
   :value="todo.title"
   v-show="todo.isEdit"
   @blur="handleBlur(todo, $event)"
   ref="inputTitle"
 />
 <script>
   // 处理编辑
    handleEdit (todo) {
      if (todo.isEdit !== undefined) {
        console.log('todo里有isEdit属性了');
        todo.isEdit = true
      } else {
        console.log('todo没有isEdit属性');
        this.$set(todo, 'isEdit', true)
      }
      // 让输入框获取焦点
      this.$refs.inputTitle.focus()
    },
</script>

这样写发现并不起作用,原因是:
   vue会将handleEdit这个回调函数的所有代码都执行完后,再去重新解析模板,而不是执行一条代码,重新解析一次模板。
  案例中是用v-show控制input框的出现,执行完19行之前的代码时,isEdit的值发生了变化,v-show的值为true,但是模板并未重新解析,此时input框虽然确实存在,但是还是隐藏状态。所以执行19行代码,对input进行的操作并不起作用 (如果一个input框隐藏了,再调用input框的focus(),input框不会获取焦点)

解决办法一:定时器

 // 处理编辑
  handleEdit (todo) {
     ...if...
    // 让输入框获取焦点
    setTimeout(() => {
      this.$refs.inputTitle.focus()
    }, 200)
  },

解决方法二(官方方法):$nextTick

  • 语法:this.$nextTick(回调函数)
  • 作用:在下一次DOM更新结束后执行其指定的回调
  • 什么时候用:当改变数据后,要基于更新后的新DOM进行某些操作时,要在$nextTick所指定的回调函数中执行。
  handleEdit (todo) {
     ...if...
    // 让输入框获取焦点
    this.$nextTick(() => {
      this.$refs.inputTitle.focus()
    })
  },

简单来说,$nextTick所指定的回调,会在DOM节点更新之后再执行。

修改后的Todo完整代码

MyItem.vue

<template>
  <li>
    <label>
      <!-- 添加checked属性使得复选框被勾选上 -->
      <input
        type="checkbox"
        :checked="todo.done"
        @change="handleCheck(todo.id)"
      />
       <!-- 展示todo内容 -->
      <span v-show="!todo.isEdit">{{ todo.title }}</span>
      <input
        type="input"
        :value="todo.title"
        v-show="todo.isEdit"
        @blur="handleBlur(todo, $event)"
        ref="inputTitle"
      />
    </label>
    <button class="btn btn-danger" @click="handleDelete(todo.id)">删除</button>
    <button  class="btn btn-edit"  @click="handleEdit(todo)"  v-show="!todo.isEdit">编辑</button>
  </li>
</template>
<script>
import pubsub from 'pubsub-js'
export default {
  name: 'MyItem',
  // 接收MyList组件传递的对象
  props: ["todo"],
  methods: {
    // 处理是否勾选
    handleCheck (id) {
      // this.checkTodo(id)
      this.$bus.$emit('checkTodo', id)
    },

    // 处理删除
    handleDelete (id) {
      if (confirm('确定要删除吗?')) {
        // 通知App,删除对应的todo
        // this.deleteTodo(id)   props方法
        // this.$bus.$emit('deleteTodo', id) 全局事件总线
        // 消息订阅
        pubsub.publish('deleteTodo', id)
      }
    },

    // 处理编辑
    handleEdit (todo) {
      // 这个属性确实加进todo里了,但是不是响应式的,这个属性的修改不会被Vue监测到,进而不会引起页面的更新
      // todo.isEdit = true
      // 防止每次点击编辑,都给todo加一下isEdit属性,因此用if判断一下是否需要添加该属性
      // 'isEdit' in todo  也可以这样判断
      if (todo.isEdit !== undefined) {
        console.log('todo里有isEdit属性了');
        todo.isEdit = true
      } else {
        console.log('todo没有isEdit属性');
        this.$set(todo, 'isEdit', true)
      }
      this.$nextTick(() => {
        this.$refs.inputTitle.focus()
      })
      // 自动获取焦点
      this.$nextTick(() => {
        this.$refs.inputTitle.focus()
      })
    },
    // 输入框失去焦点回调函数(真正执行修改逻辑)
    handleBlur (todo, e) {
      todo.isEdit = false
      if (!e.target.value.trim()) return alert('输入为空')
      this.$bus.$emit('updateTodo', todo.id, e.target.value)
    }
  }
}
</script>

App.vue

<template>
  <div id="root">
    <div class="todo-container">
      <div class="todo-wrap">
        <!-- 给子组件传函数 -->
        <MyHeader @addTodo="addTodo"></MyHeader>
        <!-- 给子组件传数据 -->
        <MyList :todos="todos"></MyList>
        <MyFooter
          :todos="todos"
          @checkAllTodo="checkAllTodo"
          @clearAllTodo="clearAllTodo"
        ></MyFooter>
      </div>
    </div>
  </div>
</template>

<script>
import pubsub from 'pubsub-js'
import MyHeader from './components/MyHeader'
import MyFooter from './components/MyFooter'
import MyList from './components/MyList'
export default {
  name: 'App',
  components: {
    MyHeader,
    MyFooter,
    MyList
  },
  data () {
    return {
      // 3.读取数据
      todos: JSON.parse(localStorage.getItem('todos')) || []
    }
  },
  methods: {
    // 添加todoObj
    addTodo (todoObj) {
      this.todos.unshift(todoObj)
    },
    // 勾选or取消勾选一个todo
    checkTodo (id) {
      this.todos.forEach((todo) => {
        if (todo.id === id) {
          todo.done = !todo.done
        }
      })
    },
    // 编辑todo内容
    updateTodo (id, title) {
      this.todos.forEach((todo) => {
        if (todo.id === id) {
          todo.title = title
        }
      })
    },
    // 删除一个todo
    deleteTodo (_, id) {
      this.todos = this.todos.filter((todo) => {
        return todo.id !== id
      })
    },
    // 勾选or取消勾选所有todo
    checkAllTodo (done) {
      this.todos.forEach((todo) => {
        todo.done = done
      })
    },
    // 清除所有已完成的todo
    clearAllTodo () {
      this.todos = this.todos.filter((todo) => {
        return !todo.done
      })
    }
  },
  mounted () {
    // 事件总线上绑定事件
    this.$bus.$on('checkTodo', this.checkTodo)
    this.$bus.$on('updateTodo', this.updateTodo)
    // 订阅消息
    this.pubId = pubsub.subscribe('deleteTodo', this.deleteTodo)
  },
  beforeDestroy () {
    // 组件销毁时,销毁总线上的事件
    this.$bus.$off(['checkTodo', 'updateTodo'])
    // 取消订阅
    pubsub.unsubscribe(this.pubId)
  },
  // 1. 将最新的数据存到localStorage,数据会发生增删改查,只需监视todos数据,有变化时将新的重新存入即可
  watch: {
    todos: {
      // 2. 开启深度监视,todos是个对象数组,不开启深度监视,当某一个对象的元素发生变化时,watch监听不到
      deep: true,
      handler (value) {
        localStorage.setItem('todos', JSON.stringify(value))
      }
    }
  }
  </script>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2099626.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【负载均衡】LoadBalance场景演示

服务端⾼并发分布式结构演进之路-CSDN博客文章浏览阅读976次&#xff0c;点赞11次&#xff0c;收藏9次。在进行技术学习过程中&#xff0c;由于大部分读者没有经历过一些中大型系统的实际经验&#xff0c;导致无法从服务端⾼并发分布式结构演进之路-----在进行技术学习过程中&a…

低代码技术:简化应用开发,推动数字化转型

在当今快速变化的技术环境中&#xff0c;企业面临着巨大的压力&#xff0c;需要快速响应市场需求并持续推动数字化转型。传统的应用开发方式往往复杂且耗时&#xff0c;开发周期长且需要大量的编程工作。为了应对这些挑战&#xff0c;低代码技术应运而生&#xff0c;为企业提供…

谈谈人工智能在中国:现状与未来展望

随着科技的飞速发展&#xff0c;人工智能&#xff08;AI&#xff09;已经成为推动全球经济和社会发展的关键力量。作为全球科技创新的重要参与者&#xff0c;中国在AI领域取得了令人瞩目的进展&#xff0c;并展现出强大的未来发展潜力。本文将深入分析中国AI的现状&#xff0c;…

XML简介 xml配置文件和properties配置文件对比

目录标题 一、XML简介二、XML配置文件和properties配置文件对比三、XML约束 一、XML简介 XML是EXtensible Markup Language的缩写&#xff0c;翻译过来就是可扩展标记语言。所以很明显&#xff0c;XML和HTML一样都是标记语言&#xff0c;也就是说它们的基本语法都是标签。 可…

windows下安装docker操作步骤

因为最近dockerb被封&#xff0c;下载资源太不方便了&#xff0c;所以还是自己本地安装上docker吧 下载的地址一定不要找错&#xff0c;是这里 https://docs.docker.com/desktop/install/windows-install/ 电脑--“控制面板”--“程序与功能”--开启windows功能 “Hyper-V”…

webSocket的自学案例

问&#xff1a; 请展示一个简单websocket案例&#xff1f; 回答&#xff1a; <!DOCTYPE html><html lang"en"><head><meta charset"UTF-8"><title>简单小例子</title></head><body><input type&quo…

记录游戏高光时刻!4款电脑录屏工具分享

虽然之前也录制过游戏&#xff0c;但是想来还是有不少朋友不知道如何录制一个高清游戏视频&#xff0c;我来和大家聊聊游戏直播和录屏的那些事儿。作为一个游戏主播&#xff0c;我尝试过很多录屏软件&#xff0c;今天就来分享一下我用过的四款录屏软件它们在录制游戏视频时的表…

Win32设备I/O详解

Windows设备 在Windows平台下&#xff0c;设备被定义为能够与之进行通信的任何东西。最常见的 I/O 设备包括&#xff1a;文件、文件流、目录、物理磁盘、卷、控制台缓冲区、磁带驱动器、通信资源、mailslot 和管道等。 平常我们使用的文件&#xff0c;目录都可以称之为设备。…

机床采集网关在汽车智能工厂中的应用及成效-天拓四方

随着工业4.0的浪潮席卷全球&#xff0c;智能化、数字化成为了制造业转型升级的关键词。在这一背景下&#xff0c;机床采集网关以其强大的数据采集、传输和处理能力&#xff0c;为企业的数字化转型提供了强有力的支持。本文将通过一个实际案例&#xff0c;详细介绍机床采集网关在…

数据结构:(LeetCode144)二叉树的前序遍历

给你二叉树的根节点 root &#xff0c;返回它节点值的 前序 遍历。 示例 1&#xff1a; 输入&#xff1a;root [1,null,2,3] 输出&#xff1a;[1,2,3] 解释&#xff1a; 示例 2&#xff1a; 输入&#xff1a;root [1,2,3,4,5,null,8,null,null,6,7,9] 输出&#xff1a;…

CUDA与TensorRT学习二:CUDA编程入门

文章目录 一、理解CUDA的grid和Block1&#xff09;第一个cuda项目 二、理解.cu和.cpp的相互引用及Makefile三、利用CUDA矩阵乘法(matmul)计算、Error Handle 及硬件信息获取1&#xff09;矩阵乘法2&#xff09;Error Handle3&#xff09;硬件信息获取 四、安装Nsight system an…

【APP自动化】Appium 环境搭建

1 基础环境 安装 node.js (1) 安装node.js 安装的是10版本&#xff0c;node-v10.16.0-x64&#xff0c;node.js安装比较简单&#xff0c;直接采用默认选项即可&#xff0c;路径的话&#xff0c;可以自己更改下。 (2) 添加Path环境变量 (3) 验证node.js是否安装成功 可以在CMD…

STM32 IIC

第一块&#xff1a;介绍协议规则&#xff0c;然后用软件模拟的形式来实现协议&#xff0c; 第二块&#xff1a;介绍STM32的IIC外设&#xff0c;然后用硬件来实现协议 因为IIC是同步时序的额&#xff0c;软件模拟协议也非常方便&#xff0c;像我们单片机一样&#xff0c;外挂芯…

零基础入门转录组数据分析——基因Wilcoxon秩和检验

零基础入门转录组数据分析——基因Wilcoxon秩和检验 目录 零基础入门转录组数据分析——基因Wilcoxon秩和检验1. 单基因Wilcoxon秩和检验的基础知识2. 基因Wilcoxon秩和检验&#xff08;Rstudio&#xff09;——代码实操2. 1 数据处理2. 2 基因Wilcoxon秩和检验2. 3 Wilcoxon秩…

FreeRTOS学习笔记—①堆与栈

在嵌入式系统中&#xff0c;堆与栈通常表示操作系统对进程占用的两种管理方式&#xff0c;而RTOS中栈更为重要&#xff0c;每一个链路都要有自己的栈。因此对堆和栈的概念进行了些区分和了解。以下是自己学习总结的一些&#xff0c;如有不对的地方请指正&#xff1a; &#xf…

算法打卡 Day25(二叉树)-修剪二叉搜索树 + 将有序数组转换为二叉搜索树 + 把二叉搜索树转换为累加树

文章目录 Leetcode 669-修剪二叉搜索树题目描述解题思路 Leetcode 108-将有序数组转换为二叉搜索树题目描述解题思路 Leetcode 538-把二叉搜索树转换为累加树题目描述解题思路 Leetcode 669-修剪二叉搜索树 题目描述 https://leetcode.cn/problems/trim-a-binary-search-tree…

elementUI——checkbox复选框监听不到change事件,通过watch监听来解决——基础积累

今天在写后台管理系统的时候&#xff0c;遇到一个需求&#xff0c;就是要求监听复选框的change事件&#xff0c;场景就是&#xff1a;两个复选框互斥&#xff0c;且可以取消勾选。 就是这两个复选框可以同时都不勾选&#xff0c;如果勾选的话&#xff0c;另一个一定要取消勾选。…

​如何通过Kimi强化论文写作中的数据分析?

在学术研究领域&#xff0c;数据分析是验证假设、发现新知识和撰写高质量论文的关键环节。Kimi&#xff0c;作为一款先进的人工智能助手&#xff0c;能够在整个论文写作过程中提供支持&#xff0c;从文献综述到数据分析&#xff0c;再到最终的论文修订。本文将详细介绍如何将Ki…

OceanBase 的ODP OBproxy 的记录

OceanBase 的ODP的路由说明一、简述为什么使用ODP的原因 &#xff08;强一致性情况下&#xff09; 1.分布式数据库在SQL解析这块存在本地执行计划&#xff0c;远程执行计划&#xff0c;分布式执行计划。 本地执行计划&#xff1a;整个SQL的表都在session所在的Observer 节点上。…

ABAP 结构体变量的嵌套INCLUDE TYPE 和 INCLUDE STRUCTURE

文章目录 创建程序语法格式程序测试AS SPFLI_NAME2 RENAMING WITH SUFFIX _NAME2 后缀变量的结构程序结构类型嵌套表和结构字段类型TYPES嵌套类型程序 创建程序 语法格式 程序测试 AS SPFLI_NAME2 RENAMING WITH SUFFIX _NAME2 后缀 变量的结构 程序 *&------------------…