再次谈论React.js实现原生js拖拽效果引起的一系列问题


Posted in Javascript onApril 03, 2016

React 起源于 Facebook 的内部项目,因为该公司对市场上所有 JavaScript MVC 框架,都不满意,就决定自己写一套,用来架设 Instagram 的网站。做出来以后,发现这套东西很好用,就在2013年5月开源了。由于 React 的设计思想极其独特,属于革命性创新,性能出众,代码逻辑却非常简单。所以,越来越多的人开始关注和使用,认为它可能是将来 Web 开发的主流工具。

前几天写的那个拖拽,自己留下的疑问。。。这次在热心博友的提示下又修正了一些小小的bug,也加了拖拽的边缘检测部分。。。就再聊聊拖拽吧

一、不要直接操作dom元素

react中使用了虚拟dom的概念,目地就是要尽量避免直接操作dom元素,所以我们在对dom元素进行操作的时候需要注意,我之前为了获取form的参数就直接用了var dragBox=document.getElementById('form')去找dom,但是其实记录from的初始位置,可以在其子组件更新父组件参数的时候调用。即在MyFrom组件中获取,如下代码:

onChildChanged:function(newState){
/*以下为修改处*/
var computedStyle=document.defaultView.getComputedStyle(ReactDOM.findDOMNode(this.refs.dragBox),null);
newState.left=computedStyle.left;
newState.top=computedStyle.top;
/*以上为修改处*/
this.setState(newState);
},

这样就可以直接在父组件中操作自己,而不是在子组件中调用。

二、onmousemove和onmouseup事件应该绑定到document上

拖拽事件中,当鼠标在DragArea中按下后,就应该检测鼠标在document中移动的距离及何时弹起。否则直接绑定在form的话会有一个不雅的地方,就是拖动条拖动边缘附近的时候,如果鼠标速度快一点会失效,鼠标再回来拖动条会自动吸上鼠标。因此利用react初始化阶段的componentDidMount函数,这个函数是组件被装载后才会被调用,也就是说调用这个方法的时候,组件已经被渲染到了页面上,这个时候可以修改DOM。也就是说此时把相应事件再绑定到document上面,如下代码:

componentDidMount:function(){
document.addEventListener('mousemove',(e)=>{this.move(e);},false);/*ES6新特性,箭头函数,需要依赖jsx编译工具才能正确运行*/
document.addEventListener('mouseup',(e)=>{this.endDrag(e);},false);
},

这样就可以消除那个小小的bug啦!

三、增加边缘检测

一般情况下的拖拽,我们都是不希望能够拖出可视窗口之外的,因此这就需要检测。检测四个方向上的位置,即上、下、左、右。显然,上的距离top和左边left的距离必须要大于等于0,下边和右的距离必须要小于视口大小减去from本身的元素宽高。

具体代码:

move:function(event){
var e = event ? event : window.event;
var dBox=ReactDOM.findDOMNode(this.refs.dragBox);
if (this.state.flag) {
var nowX = e.clientX, nowY = e.clientY;
var disX = nowX - this.state.currentX, disY = nowY - this.state.currentY;
/*增加拖拽范围检测*/
var currentLeft=parseInt(this.state.left) + disX;
var currentTop=parseInt(this.state.top) + disY;
var docX=document.documentElement.clientWidth||document.body.clientWidth;
var docY=document.documentElement.clientHeight||document.body.clientHeight;
if(currentLeft<=250){//检测屏幕左边,因为我这里的初始居中是利用了负1/2的盒子宽度的margin,所以用250px判断边界
dBox.style.left=250+"px";
}else if(currentLeft>=(docX-dBox.offsetWidth+250)){ //检测右边
dBox.style.left=(docX-this.state.offsetX)+"px";
}else{
dBox.style.left =currentLeft+ "px";
}
if(currentTop<=200){ //检测屏幕上边,因为我这里的初始居中是利用了负1/2的盒子高度的margin,所以用200px判断边界 <br> dBox.style.top=200+"px"; <br> }else if(currentTop>=(docY-dBox.offsetHeight+200)){ //检测下边<br> dBox.style.top=(docY-this.state.offsetY)+"px";<br> }else{<br> dBox.style.top = currentTop + "px"; <br> }<br> }

PS:新的代码已经更新在我的github上面,大家可以研究一下。

ReactJS的背景和原理

在Web开发中,我们总需要将变化的数据实时反应到UI上,这时就需要对DOM进行操作。而复杂或频繁的DOM操作通常是性能瓶颈产生的原因(如何进行高性能的复杂DOM操作通常是衡量一个前端开发人员技能的重要指标)。React为此引入了虚拟DOM(Virtual DOM)的机制:在浏览器端用Javascript实现了一套DOM API。基于React进行开发时所有的DOM构造都是通过虚拟DOM进行,每当数据变化时,React都会重新构建整个DOM树,然后React将当前整个DOM树和上一次的DOM树进行对比,得到DOM结构的区别,然后仅仅将需要变化的部分进行实际的浏览器DOM更新。而且React能够批处理虚拟DOM的刷新,在一个事件循环(Event Loop)内的两次数据变化会被合并,例如你连续的先将节点内容从A变成B,然后又从B变成A,React会认为UI不发生任何变化,而如果通过手动控制,这种逻辑通常是极其复杂的。尽管每一次都需要构造完整的虚拟DOM树,但是因为虚拟DOM是内存数据,性能是极高的,而对实际DOM进行操作的仅仅是Diff部分,因而能达到提高性能的目的。这样,在保证性能的同时,开发者将不再需要关注某个数据的变化如何更新到一个或多个具体的DOM元素,而只需要关心在任意一个数据状态下,整个界面是如何Render的。

如果你像在90年代那样写过服务器端Render的纯Web页面那么应该知道,服务器端所要做的就是根据数据Render出HTML送到浏览器端。如果这时因为用户的一个点击需要改变某个状态文字,那么也是通过刷新整个页面来完成的。服务器端并不需要知道是哪一小段HTML发生了变化,而只需要根据数据刷新整个页面。换句话说,任何UI的变化都是通过整体刷新来完成的。而React将这种开发模式以高性能的方式带到了前端,每做一点界面的更新,你都可以认为刷新了整个页面。至于如何进行局部更新以保证性能,则是React框架要完成的事情。

借用Facebook介绍React的视频中聊天应用的例子,当一条新的消息过来时,传统开发的思路如上图,你的开发过程需要知道哪条数据过来了,如何将新的DOM结点添加到当前DOM树上;而基于React的开发思路如下图,你永远只需要关心数据整体,两次数据之间的UI如何变化,则完全交给框架去做。可以看到,使用React大大降低了逻辑复杂性,意味着开发难度降低,可能产生Bug的机会也更少。

Javascript 相关文章推荐
网页中实现浏览器的最大,最小化和关闭按钮
Mar 12 Javascript
javascript preload&amp;lazy load
May 13 Javascript
JavaScript 操作键盘的Enter事件(键盘任何事件),兼容多浏览器
Oct 11 Javascript
一样的table?不一样的table(可编辑状态table)
Sep 19 Javascript
js菜单点击显示或隐藏效果的简单实例
Jan 13 Javascript
js解析json读取List中的实体对象示例
Mar 11 Javascript
jQuery中:first-child选择器用法实例
Dec 31 Javascript
JavaScript数组常用方法
Mar 02 Javascript
Jquery效果大全之制作电脑健康体检得分特效附源码下载
Nov 02 Javascript
解析Javascript单例模式概念与实例
Dec 05 Javascript
jQuery实现标签页效果实战(4)
Feb 08 Javascript
element-ui 弹窗组件封装的步骤
Jan 22 Javascript
jQuery qrcode生成二维码的方法
Apr 03 #Javascript
Node.js 应用跑得更快 10 个技巧
Apr 03 #Javascript
AngularJs 60分钟入门基础教程
Apr 03 #Javascript
深入浅析JSON.parse()、JSON.stringify()和eval()的作用详解
Apr 03 #Javascript
基于JavaScript实现 网页切出 网站title变化代码
Apr 03 #Javascript
BootStrap的弹出框(Popover)支持鼠标移到弹出层上弹窗层不隐藏的原因及解决办法
Apr 03 #Javascript
js一维数组、多维数组和对象的混合使用方法
Apr 03 #Javascript
You might like
php数组函数序列之each() - 获取数组当前内部指针所指向元素的键名和键值,并将指针移到下一位
2011/10/31 PHP
php Imagick获取图片RGB颜色值
2014/07/28 PHP
PHP 中 var_export、print_r、var_dump 调试中的区别
2018/06/19 PHP
JavaScript Cookie的读取和写入函数
2009/12/08 Javascript
javascript 密码强度验证规则、打分、验证(给出前端代码,后端代码可根据强度规则翻译)
2010/05/18 Javascript
基于javascript实现文字无缝滚动效果
2016/03/22 Javascript
js判断所有表单项不为空则提交表单的实现方法
2016/09/09 Javascript
JS控制页面跳转时未请求要跳转的地址怎么回事
2016/10/14 Javascript
JS关于刷新页面的相关总结
2018/05/09 Javascript
vue+webpack中配置ESLint
2018/11/07 Javascript
JavaScript实现学生在线做题计时器功能
2018/12/05 Javascript
vue 通过 Prop 向子组件传递数据的实现方法
2020/10/30 Javascript
Jquery Fade用法详解
2020/11/06 jQuery
Python抓取框架Scrapy爬虫入门:页面提取
2017/12/01 Python
使用Python进行QQ批量登录的实例代码
2018/06/11 Python
在PyCharm下打包*.py程序成.exe的方法
2018/11/29 Python
Python openpyxl读取单元格字体颜色过程解析
2019/09/03 Python
python中对_init_的理解及实例解析
2019/10/11 Python
python实现超市商品销售管理系统
2019/11/22 Python
tensorflow实现二维平面模拟三维数据教程
2020/02/11 Python
解决pymysql cursor.fetchall() 获取不到数据的问题
2020/05/15 Python
pycharm使用技巧之自动调整代码格式总结
2020/11/04 Python
详解pandas赋值失败问题解决
2020/11/29 Python
浅谈h5自定义audio(问题及解决)
2016/08/19 HTML / CSS
Europcar英国:英国汽车和货车租赁
2017/01/21 全球购物
宝拉珍选官方旗舰店:2%水杨酸精华液,收缩毛孔粗大和祛痘
2018/07/01 全球购物
什么是反射?如何实现反射?
2016/07/25 面试题
活动总结的格式
2014/05/07 职场文书
党的群众路线对照检查材料范文
2014/09/24 职场文书
优秀共产党员事迹材料
2014/12/18 职场文书
产品调价通知函
2015/04/20 职场文书
民间借贷被告代理词
2015/05/23 职场文书
党员证明信
2015/06/19 职场文书
Linux安装apache服务器的配置过程
2021/11/27 Servers
手写Spirit防抖函数underscore和节流函数lodash
2022/03/22 Javascript
SQL Server的存储过程与触发器以及系统函数和自定义函数
2022/04/10 SQL Server