专栏名称: OSC开源社区
OSChina 开源中国 官方微信账号
目录
相关文章推荐
程序员的那些事  ·  卖二手闲置遭品牌方警告“只有使用权”,网友集 ... ·  2 天前  
程序猿  ·  “再见了 ... ·  2 天前  
神秘的程序员们  ·  他从游戏走到了AI,从AI走到了诺贝尔奖 ·  2 月前  
51好读  ›  专栏  ›  OSC开源社区

我的 react 组件化开发道路(三) 懒惰+执着,驱动产品的完善

OSC开源社区  · 公众号  · 程序员  · 2017-01-11 08:24

正文


程序猿注定是懒惰的,当然,这里的懒惰,并不是指生活中的懒惰,而是指在产品开发中寻求各种便捷的手段来让自己开发的更加高效,产品更加完美。


细数从开始着手写react至今已经近两个月时间了,虽然其中估计有一半时间忙工作上的事忙成狗,但是react的学习,还是犹如海绵吸水一般,获益匪浅,从一脸懵逼,到豁然开朗,再到各种踩坑、填坑中徘徊,跑得快,也应该抽空回顾下,沉淀自己,所以今天想说的,并不是各种组件的开发流程,而是在组件开发过程中遇到的坑以及解决方案。


具体项目地址:https://git.oschina.net/meichao/React-webpack


事件冒泡之坑


碰到这个问题,是在写下拉框组件的时候碰到的,问题描述:

   

这是完成的下拉框组件,当点击按钮时,下拉框的内容显示出来,当用户点击除了按钮部分,点击body的任何区域都会让下拉框消失(当然此时点击按钮部分也会让下拉框消失,因为此时下拉框已经出现了的),逻辑理清楚了,那么整体思路就是:需要在document.body绑定click事件,然后在按钮上绑定一个click事件,并且阻止冒泡到document.body上即可了,具体代码如下:


ComponentTool是自己写的一个工具类,bind,则为对应的元素绑定相应事件,最后是回调,unbind这刚好相反,解绑事件,然而测试的时候发现,事件还是冒泡上来了,查看元素的DOM事件可以看到:


这就有必要去看下react的事件机制了:React并不会真正的绑定事件到每一个具体的元素上,而是采用事件代理的模式:在根节点document上为每种事件添加唯一的Listener,然后通过事件的target找到真实的触发元素。这样从触发元素到顶层节点之间的所有节点如果有绑定这个事件,React都会触发对应的事件处理函数。这就是所谓的React模拟事件系统。而body是document的一个子元素,那么触发绑定在body上的事件就正常了,那么我们需要做的就是将事件绑定到document上去即可,更改了代码之后,会发现,然而并没有什么卵用,效果如下:


click还是冒泡上来了,这就头大了,难道我的冒泡没用?are you kidding me?好吧,又得得捋,stopPropagation函数只屏蔽冒泡,并不屏蔽自身的事件,这不是闹么,不过诧异的是,居然查到了stopImmediatePropagation这样的一个函数(原谅我先前的无知),stopImmediatePropagation会在调用对应的回调函数之后屏蔽对应元素对应事件所有回调函数的调用,并且禁止冒泡,OK,就它了,用起来!

   


报错,我的天,把event打印出来看看:


由于react代理事件的原因,这里我们得到的event仅仅是一个合成事件对象,它的主要属性如下:


想要获取浏览器事件的详情,则可以查看合成对象中的nativeEvent值:


可以看到,event.nativeEvent此时对应的是MouseEvent,即鼠标事件,代码修改为:

 

                        

成功阻止事件的冒泡并且阻止了默认行为,组件能正常使用。一个简单的阻止冒泡的事件绑定,扯出了如此多的知识点~~~~。


路由资源使用require.ensure按需加载之坑


我们常规的路由设置是类似这样的(当然,其实我一开始也是这样的):

 


这样当我们在使用webpack这些打包工具的时候,会把所有资源一起打包进来,如果子页面很多的话,而我们的首屏却只是一个简单的页面,那么一个仅仅用到一点点资源的首屏,却要在一开始的时候去加载所有的资源,必然会卡,这对于追求首屏极致响应的前端而言,是深恶痛绝的,那么这个时候我们就可以使用require.ensure来对路由资源进行按需加载,只有当我点击某个子页面的时候,才会去加载跟这个子页面相关的资源,首屏只需要加载那些必须的资源即可,让我们来进行相应的配置:

 


这里做了几个简单的配置项,然后在我们的react-router引入即可,如下:


这里的route资源,就是上一张图的配置输出,因为在require.ensure中未指定每个路由资源的名字,会由其自动创建,而我们需要在webpack中设置chunkFilename:


设置完成,让我们把项目跑起来试试:

 


console中报了这样的一个错:

   

只需要如下即可去除警告:



webpack把每个路由对应的资源单独打包出来了,命名规范就是我们在output中的chunkFilename那般,没问题,接下来让我们进入子页面:


404错误,这是为何?暂且把问题搁置,既然browserHistory不行,尝试下其他方法,将history换成hashHistory,在控制台中查看请求资源:


即使进入了子页面,资源仍然能请求到,这里就有区别了,为什么browserHistory不行而hashHistory却可以?原因在于:通过hashHistory来生成的URL不会出现这样的问题,因为它不是指向真实的路由。
而browserHistory需要服务器端做配置,路径是真实的URL,真实URL其实是指向服务器资源,比如我们经常使用的API接口,也是一个真实URL的资源路径,当通过真实URL访问网站的时候,第一次访问的是网站的域名,这个时候可以正常加载我们的网站js等文件,而用户手动刷新网页时,由于路径是指向服务器的真实路径,服务器端没有做路由配置,就会导致资源不存在,用户访问的资源不存在,返回给用户的是404错误。因为目前还没有后台,只是纯前端页面,所以暂时使用history解决了。


so?这样就完事了?可是你不觉得url里面有hash值(类似这样:http://localhost:8181/#/component/?_k=6nafo3),很丑么?别说用户受不受得了了,自己能受得了?反正我是受不了(难道是因为我是处女座,有洁癖?)。我既想用 history={browserHistory},又想用require.ensure来实现按需加载(突然觉得自己蛮作的),仔细考虑问题的关键点在哪:我们使用history={browserHistory}的时候,一开始进入页面是没有任何问题的,当我们进入子页面的时候,请求的静态资源的路径出现了问题,那么我们就该从解决资源的路径入手,webpack里面有没有控制我们请求静态资源路径的方法?这个时候就需要用到我们的 publicPath了,我们来看看path和publicPath的作用:

path:用来存放打包后文件的输出目录 
publicPath:指定资源文件引用的目录


所以,当我的path设置成path.resolve(__dirname, './build'),指定打包输出到该目录,所以publicPath就需要设置为”/”,表示当前路径(或者可以设置publicPath为绝对路径:http://localhost:8181/,同样可以,因为此时打包输出的build,即是网站的根目录),效果如下:


大功告成。


事件冒泡之坑


react的组件开发过程中,确实学到了很多很多,如何让组件高内聚,低耦合,在不影响组件本身的交互前提下,还能暴露出更多的接口让用户操作等等,都值得我去全面考虑,当然,最宝贵的还是在开发过程中遇到的坑,只有踩坑了,想方设法去解决这些问题,从中学到如何去思考问题、切入问题、解决问题,才是最难能可贵的!


我还在前端的路上不断前行中!!!


我的react组件化开发道路(二) 分页 组件开发

我的react组件化开发道路(一) 一脸懵比的踩坑中



推荐阅读

2016 年度开源中国新增开源软件排行榜 TOP 100

2017 值得关注的十个开源项目

2017 最值得关注的十大 APP、Web 界面设计趋势

小程序正式上线,你想知道的入口和玩法都在这…

DB-Engines:SQL Server 获评2016 年度 DBMS 榜首

点击“阅读原文”查看更多精彩内容