diff --git a/site/.vitepress/code/ReloadChildren.vue b/site/.vitepress/code/ReloadChildren.vue
new file mode 100644
index 0000000..2a91d8d
--- /dev/null
+++ b/site/.vitepress/code/ReloadChildren.vue
@@ -0,0 +1,72 @@
+<template>
+  <button @click="handleClearChildren">Clear node-1 children</button>
+  <button @click="handleSetChildren">Set node-1 children</button>
+  <button @click="handleUpdateChildren">Update node-1 children</button>
+  <div :style="{ height: '300px' }">
+    <VTree ref="tree" checkable selectable />
+  </div>
+</template>
+
+<script setup lang="ts">
+import { computed, onMounted, ref } from 'vue'
+import VTree from '@wsfe/vue-tree'
+
+const tree = ref()
+
+const children = Array.from({ length: 100000 }).map((_, i) => {
+  return {
+    title: `node-1-${i + 1}`,
+    id: `node-1-${i + 1}`,
+  }
+})
+
+const data = [
+  {
+    title: 'node-1',
+    id: 'node-1',
+    children,
+  },
+  {
+    title: 'node-2',
+    id: 'node-2',
+    children: [
+      {
+        title: 'node-2-1',
+        id: 'node-2-1',
+      },
+    ],
+  },
+]
+
+onMounted(() => {
+  tree.value.setData(data)
+})
+
+const handleSetChildren = () => {
+  tree.value.updateNode('node-1', { children })
+}
+const handleClearChildren = () => {
+  tree.value.updateNode('node-1', { children: [] })
+}
+const handleUpdateChildren = () => {
+  tree.value.updateNode('node-1', {
+    children: children.map((child) => {
+      return {
+        ...child,
+        title: `${child.title} ${Date.now()}`,
+        checked: true,
+      }
+    })
+  })
+}
+</script>
+
+<style scoped>
+button {
+  border: 1px solid lightgray;
+  border-radius: 8px;
+  padding-left: 10px;
+  padding-right: 10px;
+  margin-right: 20px;
+}
+</style>
diff --git a/site/.vitepress/code/UpdateCustomField.vue b/site/.vitepress/code/UpdateCustomField.vue
new file mode 100644
index 0000000..34a7a3d
--- /dev/null
+++ b/site/.vitepress/code/UpdateCustomField.vue
@@ -0,0 +1,66 @@
+<template>
+  <button @click="handleUpdateCount">Update node-1 count</button>
+  <VTree ref="tree">
+    <template #node="{ node }">
+      <span>{{ node.title }}</span>
+      <span v-if="typeof node.count === 'number'">
+        Count: {{ node.count }}
+      </span>
+    </template>
+  </VTree>
+</template>
+
+<script setup lang="ts">
+import { onMounted, ref } from 'vue'
+import VTree from '@wsfe/vue-tree'
+
+const tree = ref()
+
+const data = [
+  {
+    title: 'node-1',
+    id: 'node-1',
+    count: 0,
+    children: [
+      {
+        title: 'node-1-1',
+        id: 'node-1-1',
+      },
+      {
+        title: 'node-1-2',
+        id: 'node-1-2',
+      },
+    ],
+  },
+  {
+    title: 'node-2',
+    id: 'node-2',
+    children: [
+      {
+        title: 'node-2-1',
+        id: 'node-2-1',
+      },
+    ],
+  },
+]
+
+onMounted(() => {
+  tree.value.setData(data)
+})
+
+const handleUpdateCount = () => {
+  const key = 'node-1'
+  const currentCount = tree.value.getNode(key).count
+  tree.value.updateNode(key, { count: currentCount + 1 })
+}
+</script>
+
+<style scoped>
+button {
+  border: 1px solid lightgray;
+  border-radius: 8px;
+  padding-left: 10px;
+  padding-right: 10px;
+  margin-right: 20px;
+}
+</style>
diff --git a/site/.vitepress/code/UpdateNodeTitle.vue b/site/.vitepress/code/UpdateNodeTitle.vue
new file mode 100644
index 0000000..9eb4e82
--- /dev/null
+++ b/site/.vitepress/code/UpdateNodeTitle.vue
@@ -0,0 +1,73 @@
+<template>
+  <button @click="handleUpdateSingleNode">Update node-1</button>
+  <button @click="handleUpdateMultipleNode">Update node-1 & node-2</button>
+  <VTree ref="tree" />
+</template>
+
+<script setup lang="ts">
+import { computed, onMounted, ref } from 'vue'
+import VTree from '@wsfe/vue-tree'
+
+const tree = ref()
+
+const data = [
+  {
+    title: 'node-1',
+    id: 'node-1',
+    children: [
+      {
+        title: 'node-1-1',
+        id: 'node-1-1',
+      },
+      {
+        title: 'node-1-2',
+        id: 'node-1-2',
+      },
+    ],
+  },
+  {
+    title: 'node-2',
+    id: 'node-2',
+    children: [
+      {
+        title: 'node-2-1',
+        id: 'node-2-1',
+      },
+    ],
+  },
+]
+
+onMounted(() => {
+  tree.value.setData(data)
+})
+
+const count = ref(0)
+
+const handleUpdateSingleNode = () => {
+  count.value++
+  tree.value.updateNode('node-1', { title: `node-1 - ${count.value}` })
+}
+const handleUpdateMultipleNode = () => {
+  count.value++
+  tree.value.updateNodes([
+    {
+      id: 'node-1',
+      title: `node-1 - ${count.value}`,
+    },
+    {
+      id: 'node-2',
+      title: `node-2 - ${count.value}`,
+    },
+  ])
+}
+</script>
+
+<style scoped>
+button {
+  border: 1px solid lightgray;
+  border-radius: 8px;
+  padding-left: 10px;
+  padding-right: 10px;
+  margin-right: 20px;
+}
+</style>
diff --git a/site/api/vtree.md b/site/api/vtree.md
index 41d6d34..fe1aafa 100644
--- a/site/api/vtree.md
+++ b/site/api/vtree.md
@@ -97,6 +97,8 @@
 | filter                 | 过滤节点                                        | `keyword: string`: 过滤关键词<br/>`filterMethod: (keyword: string, node: TreeNode) => boolean`: 过滤方法,默认为 filterMethod Prop ,如果没有传 filterMethod Prop 则为搜索 title 字段的一个内置方法 | `void`                            |
 | showCheckedNodes       | 展示已选节点                                    | `showUnloadCheckedNodes: boolean`: 是否显示未加载的选中节点,默认为 Prop 传入的值                                                                                                                   | `void`                            |
 | loadRootNodes          | 从远程加载根节点                                | 无                                                                                                                                                                                                  | `Promise<void>`                   |
+| updateNode `4.1.0`     | 更新单个节点                                    | `key: string \| number`: 节点 key<br/>`newNode: object`: 新节点数据,某些字段将被忽略,例如以下划线 "_" 开头的字段,以及 key 字段和 `indeterminate`, `visible`, `isLeaf` 等                         | `void`                            |
+| updateNodes `4.1.0`    | 更新多个节点                                    | `newNodes: object[]`: 新节点数据数组,与 `updateNode` 相同,特定的字段会被忽略,且没有 key 字段的元素将被忽略                                                                                       | `void`                            |
 | scrollTo               | 滚动到指定节点位置                              | `key: string \| number`: 节点 key<br/>`verticalPosition: 'top' \| 'center' \| 'bottom' \| number`: 滚动的垂直位置                                                                                   | `void`                            |
 
 ## VTree Slots
diff --git a/site/en/api/vtree.md b/site/en/api/vtree.md
index ef725a0..0eb7e9b 100644
--- a/site/en/api/vtree.md
+++ b/site/en/api/vtree.md
@@ -97,6 +97,8 @@ Note: Since `2.0.8`, the node info returned in events contains the full node inf
 | filter                 | Filter nodes                                                  | `keyword: string`: filter keyword<br/>`filterMethod: (keyword: string, node: TreeNode) => boolean`: filter method, default to filterMethod prop. if filterMethod prop is not present, it's an internal method that searches node title | `void`                                   |
 | showCheckedNodes       | Show checked nodes                                            | `showUnloadCheckedNodes: boolean`: whether to show checked nodes that are not loaded, default to prop value                                                                                                                            | `void`                                   |
 | loadRootNodes          | Load root nodes from remote                                   | None                                                                                                                                                                                                                                   | `Promise<void>`                          |
+| updateNode `4.1.0`     | Update single node                                            | `key: string \| number`: node key<br/>`newNode: object`: new node data, some fields will be ignored, like those start with underscore '_', the key field and `indeterminate`, `visible`, `isLeaf`, etc.                                | `void`                                   |
+| updateNodes `4.1.0`    | Update multiple nodes                                         | `newNodes: object[]`: new nodes array, some specific fields will be ignored like `updateNode`, and the elements without key field also will be ignored                                                                                 | `void`                                   |
 | scrollTo               | Scroll to specific node position                              | `key: string \| number`: node key<br/>`verticalPosition: 'top' \| 'center' \| 'bottom' \| number`: vertical position of scrolling                                                                                                      | `void`                                   |
 
 ## VTree Slots
diff --git a/site/en/examples/node-manipulation.md b/site/en/examples/node-manipulation.md
index 5e4f92e..216d33c 100644
--- a/site/en/examples/node-manipulation.md
+++ b/site/en/examples/node-manipulation.md
@@ -22,3 +22,23 @@ Enable `draggable` and `droppable`
 - Invoke `remove` to remove a node
 
 <CodeDemo component="NodeCreationAndRemoval" />
+
+## Update Node Title {#update-node-title}
+
+Invoke `updateNode` method to update some fields of tree node
+
+Invoke `updateNodes` to update multiple nodes
+
+<CodeDemo component="UpdateNodeTitle" />
+
+## Update Custom Field {#update-custom-field}
+
+Invoke `updateNode` method to update custom fields in tree node
+
+<CodeDemo component="UpdateCustomField" />
+
+## Reload Child Nodes {#reload-children}
+
+Invoke `updateNode` and pass a new `children` list to reload child nodes
+
+<CodeDemo component="ReloadChildren" />
diff --git a/site/examples/node-manipulation.md b/site/examples/node-manipulation.md
index 36a1e41..98947b0 100644
--- a/site/examples/node-manipulation.md
+++ b/site/examples/node-manipulation.md
@@ -22,3 +22,23 @@
 - 调用树组件的 `remove` 方法,可移除节点
 
 <CodeDemo component="NodeCreationAndRemoval" />
+
+## 更新节点名称 {#update-node-title}
+
+调用树组件的 `updateNode` 方法可更新节点部分字段
+
+调用 `updateNodes` 可批量更新
+
+<CodeDemo component="UpdateNodeTitle" />
+
+## 更新自定义字段 {#update-custom-field}
+
+调用树组件的 `updateNode` 方法更新自定义字段
+
+<CodeDemo component="UpdateCustomField" />
+
+## 重新加载子节点 {#reload-children}
+
+调用 `updateNode` 传入新的 `children` 列表可以重新加载子节点
+
+<CodeDemo component="ReloadChildren" />
diff --git a/src/components/Tree.vue b/src/components/Tree.vue
index e8ab382..4f70e63 100644
--- a/src/components/Tree.vue
+++ b/src/components/Tree.vue
@@ -405,6 +405,8 @@ const {
   filter,
   showCheckedNodes,
   loadRootNodes,
+  updateNode,
+  updateNodes,
 } = usePublicTreeAPI(nonReactive, props, {
   resetSpaceHeights,
   updateExpandedKeys,
@@ -705,6 +707,8 @@ defineExpose({
   filter,
   showCheckedNodes,
   loadRootNodes,
+  updateNode,
+  updateNodes,
   scrollTo,
 })
 
diff --git a/src/constants/index.ts b/src/constants/index.ts
index b316bdf..6a5ae03 100644
--- a/src/constants/index.ts
+++ b/src/constants/index.ts
@@ -39,6 +39,8 @@ export const TREE_API_METHODS = [
   'filter',
   'showCheckedNodes',
   'loadRootNodes',
+  'updateNode',
+  'updateNodes',
   'scrollTo'
 ] as const
 
diff --git a/src/hooks/usePublicTreeAPI.ts b/src/hooks/usePublicTreeAPI.ts
index 7c553a2..d22737f 100644
--- a/src/hooks/usePublicTreeAPI.ts
+++ b/src/hooks/usePublicTreeAPI.ts
@@ -235,6 +235,18 @@ export const usePublicTreeAPI = (
         isRootLoading.value = false
       })
   }
+  /**
+   * 更新单个节点
+   */
+  function updateNode(key: TreeNodeKeyType, newNode: ITreeNodeOptions) {
+    return nonReactive.store.updateNode(key, newNode)
+  }
+  /**
+   * 更新多个节点
+   */
+  function updateNodes(newNodes: ITreeNodeOptions[]) {
+    return nonReactive.store.updateNodes(newNodes)
+  }
 
   return {
     unloadCheckedNodes,
@@ -269,5 +281,7 @@ export const usePublicTreeAPI = (
     filter,
     showCheckedNodes,
     loadRootNodes,
+    updateNode,
+    updateNodes,
   }
 }
diff --git a/src/store/tree-store.ts b/src/store/tree-store.ts
index 79e5d60..15d1bd1 100644
--- a/src/store/tree-store.ts
+++ b/src/store/tree-store.ts
@@ -232,8 +232,8 @@ export default class TreeStore extends TreeEventTarget {
    * @param triggerDataChange 是否触发视图刷新
    */
   private triggerCheckedChange(
-    triggerEvent: boolean = true,
-    triggerDataChange: boolean = true
+    triggerEvent = true,
+    triggerDataChange = true,
   ) {
     if (triggerEvent) {
       this.emit('checked-change', this.getCheckedNodes(), this.getCheckedKeys())
@@ -244,6 +244,24 @@ export default class TreeStore extends TreeEventTarget {
     }
   }
 
+  /**
+   * 触发 selected-change 的快捷方法
+   * @param triggerEvent 是否触发事件
+   * @param triggerDataChange 是否触发视图刷新
+   */
+  private triggerSelectedChange(
+    triggerEvent = true,
+    triggerDataChange = true,
+  ) {
+    if (triggerEvent) {
+      this.emit('selected-change', this.getSelectedNode(), this.getSelectedKey())
+    }
+
+    if (triggerDataChange) {
+      this.emit('render-data-change')
+    }
+  }
+
   /**
    * 设置单选选中
    * @param key 选中节点 key
@@ -273,18 +291,15 @@ export default class TreeStore extends TreeEventTarget {
     } else {
       // 设置的节点不是当前已选中节点,要么当前没有选中节点,要么当前有选中节点
       if (value) {
-        if (this.currentSelectedKey === null) {
-          // 当前没有选中节点
-          node.selected = value
-          this.currentSelectedKey = node[this.options.keyField]
-        } else {
+        if (this.currentSelectedKey !== null) {
           // 取消当前已选中,设置新的选中节点
           if (this.mapData[this.currentSelectedKey]) {
             this.mapData[this.currentSelectedKey].selected = false
           }
-          node.selected = value
-          this.currentSelectedKey = node[this.options.keyField]
         }
+        node.selected = value
+        this.currentSelectedKey = node[this.options.keyField]
+        this.unloadSelectedKey = null
       }
     }
 
@@ -294,17 +309,9 @@ export default class TreeStore extends TreeEventTarget {
       } else {
         this.emit('unselect', node)
       }
-
-      this.emit(
-        'selected-change',
-        this.getSelectedNode(),
-        this.getSelectedKey()
-      )
     }
 
-    if (triggerDataChange) {
-      this.emit('render-data-change')
-    }
+    this.triggerSelectedChange(triggerEvent, triggerDataChange)
   }
 
   /**
@@ -317,9 +324,7 @@ export default class TreeStore extends TreeEventTarget {
     triggerDataChange: boolean = true
   ): void {
     if (value) {
-      if (this.currentSelectedKey) {
-        this.setSelected(this.currentSelectedKey, false, false, false)
-      }
+      this.currentSelectedKey = null
       this.unloadSelectedKey = key
     } else {
       if (this.unloadSelectedKey === key) {
@@ -327,17 +332,7 @@ export default class TreeStore extends TreeEventTarget {
       }
     }
 
-    if (triggerEvent) {
-      this.emit(
-        'selected-change',
-        this.getSelectedNode(),
-        this.getSelectedKey()
-      )
-    }
-
-    if (triggerDataChange) {
-      this.emit('render-data-change')
-    }
+    this.triggerSelectedChange(triggerEvent, triggerDataChange)
   }
 
   /**
@@ -359,17 +354,7 @@ export default class TreeStore extends TreeEventTarget {
     } else if (this.unloadSelectedKey !== null) {
       this.unloadSelectedKey = null
 
-      if (triggerEvent) {
-        this.emit(
-          'selected-change',
-          this.getSelectedNode(),
-          this.getSelectedKey()
-        )
-      }
-
-      if (triggerDataChange) {
-        this.emit('render-data-change')
-      }
+      this.triggerSelectedChange(triggerEvent, triggerDataChange)
     }
   }
 
@@ -407,23 +392,7 @@ export default class TreeStore extends TreeEventTarget {
           })
             .then(children => {
               if (Array.isArray(children)) {
-                const parentIndex: number = this.findIndex(node)
-                if (parentIndex === -1) return
-                node._loaded = true
-                node.expand = value
-                node.setChildren(children)
-                // 如果单选选中的值为空,则允许后续数据覆盖单选 value
-                const currentCheckedKeys = this.getCheckedKeys()
-                const flattenChildren = this.flattenData(
-                  node.children,
-                  this.getSelectedKey === null
-                )
-                this.insertIntoFlatData(parentIndex + 1, flattenChildren)
-                // 如果有未加载的选中节点,判断其是否已加载
-                this.setUnloadCheckedKeys(currentCheckedKeys)
-                if (this.unloadSelectedKey !== null) {
-                  this.setUnloadSelectedKey(this.unloadSelectedKey)
-                }
+                this.loadChildren(node, children, value)
                 this.emit('set-data')
               }
             })
@@ -518,6 +487,116 @@ export default class TreeStore extends TreeEventTarget {
     }
   }
 
+  private isChildrenChanged(node: TreeNode, newNode: ITreeNodeOptions): boolean {
+    return ('children' in newNode) && (!!node.children.length || !!newNode.children?.length)
+  }
+
+  updateNode(key: TreeNodeKeyType, newNode: ITreeNodeOptions, triggerEvent = true, triggerDataChange = true) {
+    const node = this.mapData[key]
+    if (!node) return
+
+    const newNodeCopy: ITreeNodeOptions = {}
+    const notAllowedFields = [
+      this.options.keyField,
+      'indeterminate',
+      'visible',
+      'isLeaf',
+    ]
+
+    // Exclude key field and fields starting with '_'
+    Object.keys(newNode).forEach((field) => {
+      if (!field.startsWith('_') && !notAllowedFields.includes(field)) {
+        newNodeCopy[field] = newNode[field]
+      }
+    })
+
+    const previousCheckedKeys = this.getCheckedKeys()
+    const previousSelectedKey = this.getSelectedKey()
+    let triggerSetDataFlag = this.isChildrenChanged(node, newNodeCopy)
+
+    if (('children' in newNodeCopy) && (!!node.children.length || !!newNodeCopy.children?.length)) {
+      // remove all children
+      this.removeChildren(key, false, false)
+
+      // add new children
+      if (Array.isArray(newNodeCopy.children)) {
+        this.loadChildren(node, newNodeCopy.children, node.expand)
+      }
+
+      delete newNodeCopy.children
+    }
+    if ('checked' in newNodeCopy) {
+      this.setChecked(key, newNodeCopy.checked, false, false)
+      delete newNodeCopy.checked
+    }
+    if ('selected' in newNodeCopy) {
+      this.setSelected(key, newNodeCopy.selected, false, false)
+      delete newNodeCopy.selected
+    }
+    if ('expand' in newNodeCopy) {
+      this.setExpand(key, newNodeCopy.expand, false, false, false)
+      delete newNodeCopy.expand
+    }
+    Object.keys(newNodeCopy).forEach((field) => {
+      node[field] = newNodeCopy[field]
+    })
+
+    const currentCheckedKeys = this.getCheckedKeys()
+    const currentSelectedKey = this.getSelectedKey()
+
+    if (triggerEvent) {
+      if (JSON.stringify(currentCheckedKeys.sort()) !== JSON.stringify(previousCheckedKeys.sort())) {
+        this.triggerCheckedChange(true, false)
+      }
+
+      if (currentSelectedKey !== previousSelectedKey) {
+        this.triggerSelectedChange(true, false)
+      }
+    }
+
+    if (triggerDataChange) {
+      if (triggerSetDataFlag) {
+        this.emit('set-data')
+      }
+      this.emit('visible-data-change')
+    }
+  }
+
+  updateNodes(newNodes: ITreeNodeOptions[]) {
+    const validNodes = newNodes.filter((node) => node[this.options.keyField] != null)
+    if (!validNodes.length) return
+
+    const previousCheckedKeys = this.getCheckedKeys()
+    const previousSelectedKey = this.getSelectedKey()
+    let triggerSetDataFlag = false
+
+    validNodes.forEach((newNode) => {
+      const key = newNode[this.options.keyField]
+      const node = this.mapData[key]
+      if (node) {
+        triggerSetDataFlag = triggerSetDataFlag || this.isChildrenChanged(node, newNode)
+        this.updateNode(key, newNode, false, false)
+      }
+    })
+
+    const currentCheckedKeys = this.getCheckedKeys()
+    const currentSelectedKey = this.getSelectedKey()
+
+    if (JSON.stringify(currentCheckedKeys.sort()) !== JSON.stringify(previousCheckedKeys.sort())) {
+      this.triggerCheckedChange(true, false)
+    }
+
+    if (currentSelectedKey !== previousSelectedKey) {
+      this.triggerSelectedChange(true, false)
+    }
+
+    if (triggerSetDataFlag) {
+      this.emit('set-data')
+    }
+
+    this.emit('visible-data-change')
+  }
+
   //#endregion Set api
 
   //#region Get api
@@ -618,12 +697,14 @@ export default class TreeStore extends TreeEventTarget {
 
   insertBefore(
     insertedNode: TreeNodeKeyType | ITreeNodeOptions,
-    referenceKey: TreeNodeKeyType
+    referenceKey: TreeNodeKeyType,
+    triggerEvent = true,
+    triggerDataChange = true,
   ): TreeNode | null {
     const node = this.getInsertedNode(insertedNode, referenceKey)
     if (!node) return null
 
-    this.remove(node[this.options.keyField], false)
+    this.remove(node[this.options.keyField], false, false)
 
     const referenceNode = this.mapData[referenceKey]
     const parentNode = referenceNode._parent
@@ -636,19 +717,25 @@ export default class TreeStore extends TreeEventTarget {
     const dataIndex =
       (parentNode && -1) || this.findIndex(referenceKey, this.data)
 
-    this.insertIntoStore(node, parentNode, childIndex, flatIndex, dataIndex)
-    this.emit('visible-data-change')
+    this.insertIntoStore(node, parentNode, childIndex, flatIndex, dataIndex, triggerEvent, triggerDataChange)
+
+    if (triggerDataChange) {
+      this.emit('visible-data-change')
+    }
+
     return node
   }
 
   insertAfter(
     insertedNode: TreeNodeKeyType | ITreeNodeOptions,
-    referenceKey: TreeNodeKeyType
+    referenceKey: TreeNodeKeyType,
+    triggerEvent = true,
+    triggerDataChange = true,
   ): TreeNode | null {
     const node = this.getInsertedNode(insertedNode, referenceKey)
     if (!node) return null
 
-    this.remove(node[this.options.keyField], false)
+    this.remove(node[this.options.keyField], false, false)
 
     const referenceNode = this.mapData[referenceKey]
     const parentNode = referenceNode._parent
@@ -674,57 +761,77 @@ export default class TreeStore extends TreeEventTarget {
     const dataIndex =
       (parentNode && -1) || this.findIndex(referenceKey, this.data) + 1
 
-    this.insertIntoStore(node, parentNode, childIndex, flatIndex, dataIndex)
-    this.emit('visible-data-change')
+    this.insertIntoStore(node, parentNode, childIndex, flatIndex, dataIndex, triggerEvent, triggerDataChange)
+
+    if (triggerDataChange) {
+      this.emit('visible-data-change')
+    }
+
     return node
   }
 
   append(
     insertedNode: TreeNodeKeyType | ITreeNodeOptions,
-    parentKey: TreeNodeKeyType
+    parentKey: TreeNodeKeyType,
+    triggerEvent = true,
+    triggerDataChange = true,
   ): TreeNode | null {
     const parentNode = this.mapData[parentKey]
     if (!parentNode.isLeaf) {
       const childrenLength = parentNode.children.length
       return this.insertAfter(
         insertedNode,
-        parentNode.children[childrenLength - 1][this.options.keyField]
+        parentNode.children[childrenLength - 1][this.options.keyField],
+        triggerEvent,
+        triggerDataChange,
       )
     }
 
     const node = this.getInsertedNode(insertedNode, parentKey, true)
     if (!node) return null
 
-    this.remove(node[this.options.keyField], false)
+    this.remove(node[this.options.keyField], false, false)
 
     const flatIndex = this.findIndex(parentKey) + 1
 
-    this.insertIntoStore(node, parentNode, 0, flatIndex)
-    this.emit('visible-data-change')
+    this.insertIntoStore(node, parentNode, 0, flatIndex, undefined, triggerEvent, triggerDataChange)
+
+    if (triggerDataChange) {
+      this.emit('visible-data-change')
+    }
+
     return node
   }
 
   prepend(
     insertedNode: TreeNodeKeyType | ITreeNodeOptions,
-    parentKey: TreeNodeKeyType
+    parentKey: TreeNodeKeyType,
+    triggerEvent = true,
+    triggerDataChange = true,
   ): TreeNode | null {
     const parentNode = this.mapData[parentKey]
     if (!parentNode.isLeaf) {
       return this.insertBefore(
         insertedNode,
-        parentNode.children[0][this.options.keyField]
+        parentNode.children[0][this.options.keyField],
+        triggerEvent,
+        triggerDataChange,
       )
     }
 
     const node = this.getInsertedNode(insertedNode, parentKey, true)
     if (!node) return null
 
-    this.remove(node[this.options.keyField], false)
+    this.remove(node[this.options.keyField], false, false)
 
     const flatIndex = this.findIndex(parentKey) + 1
 
-    this.insertIntoStore(node, parentNode, 0, flatIndex)
-    this.emit('visible-data-change')
+    this.insertIntoStore(node, parentNode, 0, flatIndex, undefined, triggerEvent, triggerDataChange)
+
+    if (triggerDataChange) {
+      this.emit('visible-data-change')
+    }
+
     return node
   }
 
@@ -734,7 +841,8 @@ export default class TreeStore extends TreeEventTarget {
    */
   remove(
     removedKey: TreeNodeKeyType,
-    triggerDataChange: boolean = true
+    triggerEvent: boolean = true,
+    triggerDataChange: boolean = true,
   ): TreeNode | null {
     const node = this.mapData[removedKey]
     if (!node) return null
@@ -781,8 +889,53 @@ export default class TreeStore extends TreeEventTarget {
         node._parent.indeterminate = false
       }
       // 更新被移除处父节点状态
-      this.updateMovingNodeStatus(node)
+      this.updateMovingNodeStatus(node, triggerEvent, triggerDataChange)
+    }
+
+    if (triggerDataChange) {
+      this.emit('visible-data-change')
+    }
+
+    return node
+  }
+
+  private removeChildren(
+    parentKey: TreeNodeKeyType,
+    triggerEvent: boolean = true,
+    triggerDataChange: boolean = true,
+  ) {
+    const node = this.mapData[parentKey]
+    if (!node || !node.children.length) return null
+
+    const firstChild = node.children[0]
+    let movingNode = firstChild
+
+    // 从 flatData 中移除
+    const index = this.findIndex(node)
+    if (index === -1) return null
+    let deleteCount = 0
+    const length = this.flatData.length
+    for (let i = index + 1; i < length; i++) {
+      if (this.flatData[i]._level > node._level) {
+        // 从 mapData 中移除
+        delete this.mapData[this.flatData[i][this.options.keyField]]
+        deleteCount++
+
+        // 如果是 Selected 的节点,则记录
+        if (this.flatData[i].selected) {
+          movingNode = this.flatData[i]
+        }
+      } else break
     }
+    this.flatData.splice(index + 1, deleteCount)
+
+    // 从父节点 children 中移除
+    node.children.splice(0, node.children.length)
+    node.isLeaf = true
+    node.indeterminate = false
+
+    // 更新被移除处父节点状态
+    this.updateMovingNodeStatus(movingNode, triggerEvent, triggerDataChange)
 
     if (triggerDataChange) {
       this.emit('visible-data-change')
@@ -791,6 +944,29 @@ export default class TreeStore extends TreeEventTarget {
     return node
   }
 
+  private loadChildren(node: TreeNode, children: any[], expand: boolean) {
+    const parentIndex: number = this.findIndex(node)
+    if (parentIndex === -1) return
+    node._loaded = true
+    node.expand = expand
+    node.setChildren(children)
+    node.isLeaf = !node.children.length
+    // 如果单选选中的值为空,则允许后续数据覆盖单选 value
+    const currentCheckedKeys = this.getCheckedKeys()
+    const flattenChildren = this.flattenData(
+      node.children,
+      this.getSelectedKey() === null
+    )
+    this.insertIntoFlatData(parentIndex + 1, flattenChildren)
+    // 如果有未加载的选中节点,判断其是否已加载
+    this.setUnloadCheckedKeys(currentCheckedKeys)
+    if (this.unloadSelectedKey !== null) {
+      this.setUnloadSelectedKey(this.unloadSelectedKey)
+    }
+
+    this.checkNodeUpward(node, true)
+  }
+
   private getInsertedNode(
     insertedNode: TreeNodeKeyType | ITreeNodeOptions,
     referenceKey: TreeNodeKeyType,
@@ -833,7 +1009,9 @@ export default class TreeStore extends TreeEventTarget {
     parentNode: TreeNode | null,
     childIndex: number,
     flatIndex: number,
-    dataIndex?: number
+    dataIndex?: number,
+    triggerEvent = true,
+    triggerDataChange = true,
   ): void {
     if (flatIndex === -1) return
 
@@ -873,16 +1051,16 @@ export default class TreeStore extends TreeEventTarget {
     this.insertIntoFlatData(flatIndex, nodes)
 
     // 更新插入节点父节点状态
-    this.updateMovingNodeStatus(node)
+    this.updateMovingNodeStatus(node, triggerEvent, triggerDataChange)
   }
 
-  private updateMovingNodeStatus(movingNode: TreeNode): void {
+  private updateMovingNodeStatus(movingNode: TreeNode, triggerEvent = true, triggerDataChange = true): void {
     // 处理多选
     this.checkNodeUpward(movingNode)
-    this.triggerCheckedChange()
+    this.triggerCheckedChange(triggerEvent, triggerDataChange)
     // 处理单选
     if (movingNode.selected) {
-      this.setSelected(movingNode[this.options.keyField], true)
+      this.setSelected(movingNode[this.options.keyField], true, triggerEvent, triggerDataChange)
     }
   }
 
@@ -1012,8 +1190,6 @@ export default class TreeStore extends TreeEventTarget {
       if (node.checked && this.options.cascade) {
         // 向下勾选,包括自身
         this.checkNodeDownward(node, true)
-        // 向上勾选父节点直到根节点
-        this.checkNodeUpward(node)
       }
 
       if (node.selected && overrideSelected) {
@@ -1035,6 +1211,12 @@ export default class TreeStore extends TreeEventTarget {
         this.flattenData(node.children, overrideSelected, result)
       }
     }
+
+    if (this.options.cascade && !!length) {
+      // 向上勾选父节点直到根节点
+      this.checkNodeUpward(nodes[0])
+    }
+
     return result
   }
 
@@ -1074,9 +1256,10 @@ export default class TreeStore extends TreeEventTarget {
   /**
    * 向上勾选/取消勾选父节点,不包括自身
    * @param node 需要勾选的节点
+   * @param fromCurrentNode 是否从当前节点开始处理
    */
-  private checkNodeUpward(node: TreeNode) {
-    let parent = node._parent
+  private checkNodeUpward(node: TreeNode, fromCurrentNode = false) {
+    let parent = fromCurrentNode ? node : node._parent
     while (parent) {
       this.checkParentNode(parent)
       parent = parent._parent
diff --git a/tests/unit/tree.spec.ts b/tests/unit/tree.spec.ts
index 7050458..f342c34 100644
--- a/tests/unit/tree.spec.ts
+++ b/tests/unit/tree.spec.ts
@@ -168,7 +168,7 @@ describe('树展示测试', () => {
       ).toBe(true)
       expect(
         treeNodes[1].find('.vtree-tree-node__checkbox_indeterminate').exists()
-      ).toBe(true)
+      ).toBe(false)
       expect(
         treeNodes[2].find('.vtree-tree-node__title_selected').exists()
       ).toBe(true)