【sgTree】自定义组件:加载el-tree树节点整棵树数据,实现增删改操作。

特性 

  1. 可以自定义主键、配置选项
  2. 支持预定义节点图标:folder文件夹|normal普通样式
  3. 多个提示文本可以自定义
  4. 支持动态接口增删改节点
  5. 可以自定义根节点id
  6. 可以设置最多允许添加的层级深度
  7. 支持拖拽排序,排序过程还可以针对拖拽的节点深度进行自定义限制
  8. 支持隐藏一级节点(根节点)复选框☑
  9. 支持屏蔽一级节点(根节点)勾选☑

sgTree源码 

<template><div :class="$options.name"><div class="tree-header" v-if="!(readonly || readonly === '')"><div class="sg-left"><template v-if="uploadData"><el-tooltippopper-class="sg-el-tooltip":enterable="false"effect="dark":content="`支持拖拽到树上传文件`"placement="top-start"><el-buttontype="text"icon="el-icon-upload"size="mini"@click="(d) => $refs.sgUpload.triggerUploadFile()">批量导入</el-button></el-tooltip><el-button type="text" icon="el-icon-download" size="mini" @click="downloadTpl">下载模板</el-button></template></div><div class="sg-right"><el-button type="text" size="mini" @click.stop="addRoot">{{ (data.text || {}).addRootButtonText || `添加根节点`}}<i class="el-icon-circle-plus-outline"></i></el-button></div></div><div class="tree-container"><el-tree:class="hideRootNodeCheckbox === '' || hideRootNodeCheckbox? 'hideRootNodeCheckbox': ''"ref="tree":data="treeData":node-key="mainKey":props="data.props || {label: 'label', //指定节点标签为节点对象的某个属性值children: 'children', //指定子树为节点对象的某个属性值disabled: 'leaf', //指定节点选择框是否禁用为节点对象的某个属性值isLeaf: 'leaf', //指定节点是否为叶子节点,仅在指定了 lazy 属性的情况下生效}":icon-class="`${data.iconType}-tree-node`":indent="data.indent || 10"@current-change="current_change"@node-click="nodeClick"highlight-current@node-drag-start="nodeDragStart"@node-drag-enter="nodeDragEnter"@node-drag-leave="nodeDragLeave"@node-drag-over="nodeDragOver"@node-drag-end="nodeDragEnd"@node-drop="nodeDrop":draggable="draggable === '' || draggable":allow-drop="allowDrop":allow-drag="allowDrag":show-checkbox="showCheckbox":default-checked-keys="defaultCheckedKeys"@check-change="handleCheckChange"><el-popoverpopper-class="tree-el-popover"placement="right"trigger="hover"title=""content="":disabled="readonly || readonly === ''"slot-scope="{ node, data }"><span class="right"><el-buttontitle="添加"type="text"size=""icon="el-icon-circle-plus-outline"@click.stop="addNode(node, data)"v-if="showAddButton(node)">添加</el-button><el-buttontitle="删除"type="text"size=""icon="el-icon-remove-outline"@click.stop="remove(node, data)">删除</el-button></span><div slot="reference" class="node-label"><div class="left" :title="node.label">{{ node.label }}</div></div></el-popover></el-tree><!-- 上传组件 --><sgUpload:disabledWhenShowSels="['.v-modal']":drag="uploadData ? dragUpload : false"ref="sgUpload":data="uploadData"@uploadSuccess="uploadSuccess"@uploadError="uploadError"@importError="importError"@showLoading="showLoading"@hideLoading="hideLoading"hideUploadTray/></div></div>
</template><script>
import sgUpload from "@/vue/components/admin/sgUpload";
export default {name: "sgTree",components: {sgUpload,},data() {return {// 动态树:增删改_________________________________________________________rootNode: null, //根节点rootResolve: null, //根节点focusNodeId: null, //聚焦高亮新添加IDmainKey: "id", //默认主键defaultRootId: "root", //默认根节点ID就是rootmaxAddLevel: null, // 最多允许添加的层级dragUpload: true, //在拖拽节点过程中控制上传组件能否拖拽上传// _________________________________________________________};},props: ["treeData","data","readonly","draggable", //是否开启拖拽节点功能"uploadData",/* 例子 uploadData: {accept: '.xls,.xlsx',actionUrl: `${this.$d.API_ROOT_URL}/core/resource/upload`,}, */"allowNodeDrag","allowNodeDrop","showCheckbox", //节点是否可被选择"hideRootNodeCheckbox", //隐藏一级节点复选框☑"disabledRootNode", //屏蔽一级节点勾选☑"defaultCheckedKeys", //默认勾选的节点的 key 的数组],watch: {data: {/* data.iconType= 节点图标:folder  文件夹normal  普通样式plus    加减符号样式*/handler(d) {d.nodeKey && (this.mainKey = d.nodeKey); //主键d.rootId && (this.defaultRootId = d.rootId); //根节点IDd.maxAddLevel && (this.maxAddLevel = d.maxAddLevel); // 最多允许添加的层级},deep: true,immediate: true,},},methods: {showLoading(file) {this.$emit(`showLoading`, file);},hideLoading(file) {this.$emit(`hideLoading`, file);},// 取消选中unCheckAll(d) {this.$refs.tree.setCheckedKeys([]);this.handleCheckChange([], []);},handleCheckChange(data, checked, indeterminate) {this.$emit(`checkChange`, {checkedNodes: this.$refs.tree.getCheckedNodes(),checkedLeafOnlyNodes: this.$refs.tree.getCheckedNodes(true, false), //(leafOnly, includeHalfChecked) 接收两个 boolean 类型的参数,1. 是否只是叶子节点,默认值为 false 2. 是否包含半选节点,默认值为 false【注意:懒加载树形不管用!必须要明确叶子节点展开后面没有子节点了才能识别!】data,checked,indeterminate,});},// 拖拽----------------------------------------nodeDragStart(node, ev) {this.dragUpload = false;this.$emit(`nodeDragStart`, node, ev);},nodeDragEnter(draggingNode, dropNode, ev) {this.$emit(`nodeDragEnter`, draggingNode, dropNode, ev);},nodeDragLeave(draggingNode, dropNode, ev) {this.$emit(`nodeDragLeave`, draggingNode, dropNode, ev);},nodeDragOver(draggingNode, dropNode, ev) {this.$emit(`nodeDragOver`, draggingNode, dropNode, ev);},nodeDragEnd(draggingNode, dropNode, dropType, ev) {// dropType有'before'、'after'、'inner'和'none'4种情况this.dragUpload = true;this.$emit(`nodeDragEnd`, draggingNode, dropNode, dropType, ev);},nodeDrop(draggingNode, dropNode, dropType, ev) {// dropType有'before'、'after'、'inner'和'none'4种情况this.$emit(`nodeDrop`, draggingNode, dropNode, dropType, ev);},allowDrop(draggingNode, dropNode, dropType) {// 拖拽时判定目标节点能否被放置。dropType 参数有三种情况:'prev'、'inner' 和 'next',分别表示放置在目标节点前、插入至目标节点和放置在目标节点后(注意:很奇葩上面node开头的绑定方法dropType有before、after、inner和none4种情况)return this.allowNodeDrop? this.allowNodeDrop(draggingNode, dropNode, dropType): true;},allowDrag(draggingNode) {return this.allowNodeDrag ? this.allowNodeDrag(draggingNode) : true;},// ----------------------------------------showAddButton(node) {if (this.maxAddLevel) {return node.level < this.maxAddLevel; // 最多允许添加的层级} else return true;},downloadTpl(d) {this.$emit(`downloadTpl`);},uploadSuccess(d, f) {this.$emit(`uploadSuccess`, d, f);},uploadError(d, f) {this.$emit(`uploadError`, d, f);},importError(d, f) {this.$emit(`importError`, d, f);},// 聚焦到某一个节点focusNode(id) {if (!id) return;this.$nextTick(() => {this.$refs.tree.setCurrentKey(id); //高亮显示某个节点this.$emit(`currentChange`, this.$refs.tree.getCurrentNode());this.$nextTick(() => {let dom = document.querySelector(`.el-tree-node.is-current`);dom &&dom.scrollIntoView({behavior: "smooth",block: "nearest",inline: "nearest",}); //缓慢滚动});});},// 添加根节点addRoot() {this.addNode(this.$refs.tree.root, { [this.mainKey]: this.defaultRootId });},// 通过id展开指定节点(通常是用于外部调用)expandNodeById(id) {let node = this.$refs.tree.getNode(id);node.expand();},//通过id勾选节点setCheckedKeys(ids) {this.$refs.tree.setCheckedKeys(ids);},// 添加节点addNode(node, data) {let resolve = (d) => {if (data.ID === this.defaultRootId) {this.treeData.unshift(d);} else {data.children || this.$set(data, "children", []);data.children.push(d);}node.expand();};let reject = (d) => {this.rootLoading = false;node.loading = false;this.$message.error(d.msg); //添加节点失败};this.$emit(`addNode`, { node, data, resolve, reject });},// 删除节点remove(node, data) {this.$confirm((this.data.text || {}).removeConfirmTip ||`此操作将永久删除该节点及其下面的节点,是否继续?`,(this.data.text || {}).removeConfirmTitle || `提示`,{dangerouslyUseHTMLString: true,confirmButtonText: `确定`,cancelButtonText: `取消`,type: "warning",}).then(() => {this.removeNodeData(node, data);}).catch(() => {});},// 删除节点数据(通过接口向后台删除数据)removeNodeData(node, data) {node.loading = true; //出现加载动画let resolve = (d) => {node.loading = false;this.$message.success(`删除成功`);// 从父节点异步删除子节点const parent = node.parent;const children = parent.data.children || parent.data;const index = children.findIndex((d) => d[this.mainKey] === data[this.mainKey]);children.splice(index, 1);// 从显示界面删除节点(有bug,只是删除了树节点的Virtual DOM,实际数据还在)/* let childNodes = node.parent.childNodes; childNodes.splice( childNodes.findIndex((d) => d.data[this.mainKey] === data[this.mainKey]), 1 ); */};let reject = (d) => {this.rootLoading = false;node.loading = false;this.$message.error(d.msg); //删除失败};this.$emit(`removeNode`, { node, data, resolve, reject });},// 当前选中节点变化时触发的事件current_change(d) {this.$emit(`currentChange`, d);},//点击节点nodeClick(d) {this.focusNodeId = null;this.$emit(`nodeClick`, d);},},
};
</script><style lang="scss" scoped>
@import "~@/css/sg";.sgTree {$treeHeaderHeight: 30px;width: 100%;height: 100%;display: flex;flex-wrap: nowrap;flex-direction: column;white-space: nowrap;flex-shrink: 0;flex-grow: 1;position: relative;.tree-header {display: flex;justify-content: space-between;align-items: center;height: $treeHeaderHeight;& > .sg-left {}& > .sg-right {}}.tree-container {position: relative;overflow: auto;box-sizing: border-box;height: calc(100% - #{$treeHeaderHeight});flex-shrink: 0;flex-grow: 1;user-select: none;@include scrollbarHover();/* >>> .tree-container .el-tree .el-tree-node__content {cursor: pointer;} */>>> .el-tree--highlight-current .el-tree-node.is-current > .el-tree-node__content {background-color: #409eff22; // 高亮当前选中节点背景}>>> .el-tree {* {transition: none;}.el-tree-node__children {min-width: max-content; //这样才会出现水平滚动条}.normal-tree-node,.plus-tree-node,.folder-tree-node {& + label:not(.el-checkbox) {/*单行省略号*/overflow: hidden;white-space: nowrap;text-overflow: ellipsis;}flex-shrink: 0;display: block;padding: 0 !important;margin: 0;width: 20px;height: 20px;margin-right: 5px;background: transparent url("~@/../static/img/fileType/folder/folder.svg")no-repeat center / contain;margin-left: 20px;& ~ span:not(.el-icon-loading) {width: 100%;.node-label {height: 40px;display: flex;align-items: center;}}&.expanded {flex-shrink: 0;transform: rotate(0deg);background-image: url("~@/../static/img/fileType/folder/folder-open.svg");}&.is-leaf {background-image: none;}}.normal-tree-node {margin-left: 10px;background-image: url("~@/../static/img/fileType/folder/arrow-right.svg");&.expanded {transform: rotate(90deg);background-image: url("~@/../static/img/fileType/folder/arrow-right.svg");}&.is-leaf {background-image: none;}}.plus-tree-node {margin-left: 10px;background-image: url("~@/../static/img/fileType/folder/plus.svg");&.expanded {background-image: url("~@/../static/img/fileType/folder/minus.svg");}&.is-leaf {background-image: none;}}// 隐藏一级节点的复选框&.hideRootNodeCheckbox > div > .el-tree-node__content .el-checkbox {display: none;}}}
}.tree-el-popover {.el-button {padding-top: 0;padding-bottom: 0;}
}
</style>

 应用

<template><div :class="$options.name"><sgTreev-loading="loading":key="$route.query.BMID + sgTree_fresh":treeData="treeData":data="treeConfigData"@currentChange="currentChange"@addNode="addNode"@removeNode="removeNode":uploadData="{name: `file`,accept: '.xls,.xlsx',actionUrl: `${$d.API_ROOT_URL}/core/column/importColumn`, //批量导入树结构接口actionData: {BMID: $global.getBMID(),PID: `root`,sgLog: `强哥请求来源:${$options.name}导入栏目xls`,},}"@uploadSuccess="uploadSuccess"@uploadError="uploadError"@importError="importError"@downloadTpl="downloadTpl"draggable:allowNodeDrop="allowNodeDrop"@nodeDragEnd="nodeDragEnd"/></div>
</template><script>
import sgTree from "@/vue/components/admin/sgTree";
export default {name: "sgBody",components: {sgTree,},data() {return {sgTree_fresh: false,autoId: 0, //自增编号treeConfigData: {nodeKey: `ID`, //主键props: { label: "MC", isLeaf: "leaf" }, //配置选项iconType: "plus", //节点图标:folder文件夹|normal普通样式|plus加减符号样式text: {addRootButtonText: "添加根目录", //添加根节点按钮文本removeConfirmTitle: "警告!!!", //删除节点提示标题removeConfirmTip: "此操作将永久删除该节点及其下面的子节点,是否继续?", //删除节点提示内容},},treeData: [],loading: false,};},created() {this.initTreeData();},methods: {//初始化数据initTreeData({ d } = {}) {this.$global.获取整棵树的数据({cb: (d) => {this.treeData = d;},});},// 拖拽节点相关方法----------------------------------------allowNodeDrop(draggingNode, dropNode, dropType) {// 只允许拖拽同级别前后排序let isPrevOrNext = dropType === "prev" || dropType === "next";// 同在第一级根节点下let isSameRootLevel =draggingNode.level === dropNode.level && draggingNode.level === 1;// 同在一个节点(非根节点)下let isSameChildLevel =draggingNode.parent &&dropNode.parent &&draggingNode.parent.data &&dropNode.parent.data &&draggingNode.parent.data.ID === dropNode.parent.data.ID;return isPrevOrNext && (isSameRootLevel || isSameChildLevel);},nodeDragEnd(draggingNode, dropNode, dropType, ev) {// 只允许拖拽同级别前后排序let isBeforeOrAfter = dropType === "before" || dropType === "after";if (isBeforeOrAfter) {/* console.log("被拖拽的节点", draggingNode.data.MC, draggingNode.data.PXZ); console.log("停靠的节点", dropNode.data.MC, dropNode.data.PXZ); */let theSameLevelDatas = (dropNode.parent.childNodes || []).map((v) => v.data); // 获取同一级节点数据theSameLevelDatas.forEach((v, i) => (v.PXZ = i)); //重新排序// console.log("拖拽排序", theSameLevelDatas); //这里需要调用后台接口let IDS = theSameLevelDatas.map((v) => v.ID); //排序后的ID顺序数组let data = {IDS,sgLog: `强哥请求来源:${this.$options.name}更改同一层级树节点排序值`,};this.$d.修改节点排序({data,r: {s: (d) => {// console.log("【成功】", d);},},});}},// ----------------------------------------// 获取当前聚焦节点的数据currentChange(d) {console.log(``, d);},// 添加节点addNode({ data, resolve }) {this.$d.新增节点({data: {MC: `新增节点名称(${++this.autoId})`,},doing: {l: { show: () => (this.loading = true), close: () => (this.loading = false) },s: (d) => resolve(d),f: (d) => reject(d), //删除失败},});},// 删除节点removeNode({ node, data, resolve, reject }) {this.$d.删除节点({data: { ID: data.ID },doing: {s: (d) => resolve(d),f: (d) => reject(d), //删除失败},});},updateList(d) {},uploadSuccess(d, f) {this.sgTree_fresh = !this.sgTree_fresh;},uploadError(d, f) {this.$message.error(d.msg);},// 导入失败importError(d, f) {},// 下载导入模板downloadTpl(d) {},},
};
</script>

关联懒加载树节点组件【sgLazyTree】自定义组件:动态懒加载el-tree树节点数据,实现增删改、懒加载及局部数据刷新。_el-tree 动态刷新-CSDN博客文章浏览阅读464次。【代码】【sgLazyTree】自定义组件:动态懒加载el-tree节点数据,实现增删改。_el-tree 动态刷新https://blog.csdn.net/qq_37860634/article/details/132639389

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

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

相关文章

C语言——栈的实现

栈---类似于实现制的线性表----只能从一端经行插入和删除 特点 后进先出&#xff08;Last In First Out&#xff0c;LIFO&#xff09;&#xff1a;栈遵循后进先出的原则&#xff0c;即最后一个进入栈的元素将是第一个被移除的元素。单端操作&#xff1a;栈只允许在栈顶进行插…

新建虚拟机并安装配置linux

记得看目录哦&#xff01; 1. 打开VMware2. 新建虚拟机3. 配置虚拟机内存4. 配置处理器5. 配置网络6. 开始安装CentOS系统系统安装包此处自取 7. 启动虚拟机进行安装如果出现这种情况这样转成图形化界面继续安装 1. 打开VMware 点击文件—点击新建虚拟机 2. 新建虚拟机 3. 配置…

JavaScript注释使用

JavaScript注释使用心得 在学习JavaScript的过程中&#xff0c;我渐渐发现了注释的奥妙。它们不仅仅是代码的附属品&#xff0c;更是提升代码可读性和调试效率的神器。 单行注释初探 你知道吗&#xff1f;JavaScript中的单行注释&#xff0c;只需要在代码前面加上//就可以了。…

基于 MQTT 的开源桥接器:自由控制物联网设备 | 开源日报 No.151

Koenkk/zigbee2mqtt Stars: 10.5k License: GPL-3.0 Zigbee2MQTT 是一个 Zigbee &#x1f41d; 到 MQTT 桥接器 &#x1f309;&#xff0c;可以摆脱专有的 Zigbee 桥接器 &#x1f528; 允许您在不使用供应商桥接器或网关的情况下使用 Zigbee 设备通过 MQTT 桥接事件并控制 Z…

多方式登录

后台 插件 >: pip install djangorestframework-jwturls.py path(login/, views.LoginViewSet.as_view({post: login})),dev.py import datetime JWT_AUTH {JWT_EXPIRATION_DELTA: datetime.timedelta(days7), }views.py from rest_framework.viewsets import ViewSet…

redis持久化之RDBAOF压缩

前引 1、redis持久化的文件是什么 dump.rdb appendonly.aof 2、这两中文件有什么异同 save 秒 1 alaways everysec no 3、文件存放的位置 dir ./ 4、默认的存放位置:命令启动的地方 dir 自定义的路径 rdb 和aof 文件 存放在同一个路径下面 5、rdb文件默认备份的策略是什么&…

redis + 拦截器 :防止数据重复提交

1.项目用到,不是核心 我们干系统开发,不免要考虑一个点&#xff0c;数据的重复提交。 我想我们之前如果要校验数据重复提交要求&#xff0c;会怎么干?会在业务层&#xff0c;对数据库操作&#xff0c;查询数据是否存在,存在就禁止插入数据; 但是吧,我们每次crud操作都会连接…

【docker】解决docker overlay2目录占用大量磁盘空间,导致验证码出不来,报错Can‘t create output stream!

问题&#xff1a; 验证码出现Cant create output stream!报错信息 排查&#xff1a; 所在服务器磁盘使用率已经到达100%&#xff0c;经排查&#xff0c;服务器目录/var/lib/docker/overlay2占用大量磁盘空间&#xff0c; 解决&#xff1a; 使用【docker system prune】命令删…

基于移动边缘计算 (MEC) 的资源调度分配优化研究(提供MATLAB代码)

一、优化模型简介 边缘计算资源调度优化模型是为了解决边缘计算场景下的资源分配和任务调度问题而提出的一种数学模型。该模型旨在通过优化算法来实现资源的有效利用和任务的高效执行&#xff0c;以提高边缘计算系统的性能和用户的服务体验。 在边缘计算资源调度优化模型中&a…

Python基础学习 -04

编码 默认情况下&#xff0c;Python 3 源码文件以 UTF-8 编码&#xff0c;所有字符串都是 unicode 字符串。 当然你也可以为源码文件指定不同的编码&#xff1a; # -*- coding: cp-1252 -*- 上述定义允许在源文件中使用 Windows-1252 字符集中的字符编码&#xff0c;对应适合…

Dapper中使用Tuple元组报错问题

在一次dapper查询中为了不返回多余的字段,也为了方便创建自定义类,使用了元组Tuple<guid,sytring>,查询如下 /// <summary>/// 获取企业集合/// </summary>/// <param name="userId"></param>/// <returns></returns>p…

LeetCode_13_简单_罗马数字转整数

文章目录 1. 题目2. 思路及代码实现&#xff08;Python&#xff09;2.1 模拟 1. 题目 罗马数字包含以下七种字符: I I I&#xff0c; V V V&#xff0c; X X X&#xff0c; L L L&#xff0c; C C C&#xff0c; D D D 和 M M M 。 字符数值 I I I1 V V V5 X X X10 L L …

【python题解17】给你一个有符号整数x,返回将x中的数字部分反转后的结果。输入的整数不超过int类型的最大范围。

1. 题目&#xff1a;给你一个有符号整数x&#xff0c;返回将x中的数字部分反转后的结果。输入的整数不超过int类型的最大范围。 输入样例&#xff1a;-123 输出样例&#xff1a;-321 2. 源代码 n int(input()) flag True #代表正数 if n < 0: #当n是负数时候&#xff…

美易平台:诺基亚四季度财报超预期

正文&#xff1a; 近日&#xff0c;诺基亚发布了其四季度财报&#xff0c;显示调整后营业利润达到了8.46亿欧元&#xff0c;超出市场预估的7.627亿欧元。同时&#xff0c;调整后每股收益&#xff08;EPS&#xff09;为0.10欧元&#xff0c;符合市场预期。这一成绩表明诺基亚在…

前端学习:HTTP协议、请求响应、分层解耦

HTTP协议 HTTP-概述 HTTP&#xff1a;Hyper Text Transfer Protocol(超文本传输协议)&#xff0c;规定了浏览器与服务器之间数据传输的规则。如果想知道http协议的数据传输格式有哪些&#xff0c;可以打开浏览器&#xff0c;点击 F12 打开开发者工具&#xff0c;点击Network 来…

vue router 右到左过渡动画

Vue Router提供了内置的路由切换效果&#xff0c;可以通过设置<transition>组件来添加过渡动画。要实现从右向左的过渡动画&#xff0c;需要使用CSS样式来定义过渡效果。 首先&#xff0c;在Vue项目中安装并引入Vue Router库&#xff1a; npm install vue-router --sav…

Java零基础学习21:学生管理系统

编写博客目的&#xff1a;本系列博客均根据B站黑马程序员系列视频学习和编写目的在于记录自己的学习点滴&#xff0c;方便后续回忆和查找相关知识点&#xff0c;不足之处恳请各位有缘的朋友指正。 一、管理系统初级版 黑马教授的管理系统初级版本 package StudentSystem;imp…

洛谷刷题-【入门3】循环结构

1.找最小值 题目描述 给出 n 和 n 个整数 ai​&#xff0c;求这 n 个整数中最小值是什么。 输入格式 第一行输入一个正整数 n&#xff0c;表示数字个数。 第二行输入 n 个非负整数&#xff0c;表示 a1,a2…an&#xff0c;以空格隔开。 输出格式 输出一个非负整数&#xff0c;表…

致远OA如何开发 第十一篇 能做什么需求

能做什么需求 此栏目技术支持 技术大佬对栏目文章的支持 特别感谢 前言 前面我们讲过如何开发的一些基础&#xff0c;但是没有对应的思维&#xff0c;比如这些开发技巧能够做什么&#xff0c;所以即便是知道需求&#xff0c;也不知道如何实现 例子 1&#xff0c;前端页面…

React16源码: React中commit阶段的commitBeforeMutationLifecycles的源码实现

commitBeforeMutationLifecycles 1 &#xff09;概述 在 react commit阶段的 commitRoot 第一个while循环中调用了 commitBeforeMutationLifeCycles现在来看下&#xff0c;里面发生了什么 2 &#xff09;源码 回到 commit 阶段的第一个循环中&#xff0c;在 commitRoot 函数…