首页
统计
墙纸
留言
Search
1
PVE8优化
13 阅读
2
Debian 12 / Ubuntu 22.04 使用源安装 LAMP 教程
12 阅读
3
内核版本 4.9 以上的 Linux 系统开启/关闭 BBR 的方法
10 阅读
4
CSS动画
10 阅读
5
jenkins根据分支、文件夹打包
9 阅读
web前端
Vue
CSS
javascript
React
那些年爬过过的坑
ES6
TypeScrippt
ES7
javascript图灵 - 总结
Node
面试总结
React-Native
Web优化
基础
AngularJS
拍摄
Flutter
Dart
Docker
Linux
mysql
PVE
登录
/
注册
Search
标签搜索
vue+elementui
Cicada
累计撰写
161
篇文章
累计收到
57
条评论
首页
栏目
web前端
Vue
CSS
javascript
React
那些年爬过过的坑
ES6
TypeScrippt
ES7
javascript图灵 - 总结
Node
面试总结
React-Native
Web优化
基础
AngularJS
拍摄
Flutter
Dart
Docker
Linux
mysql
PVE
页面
统计
墙纸
留言
搜索到
152
篇与
的结果
2017-12-26
javaScript内存生命周期
内存生命周期不管什么程序语言,内存生命周期基本是一致的:分配你所需要的内存使用分配到的内存(读、写)不需要时将其释放\归还在所有语言中第一和第二部分都很清晰。最后一步在低级语言中很清晰,但是在像JavaScript 等高级语言中,这一步是隐藏的、透明的。JavaScript 的内存分配值的初始化为了不让程序员费心分配内存,JavaScript 在定义变量时就完成了内存分配。var n = 123; // 给数值变量分配内存 var s = "azerty"; // 给字符串分配内存 var o = { a: 1, b: null }; // 给对象及其包含的值分配内存 // 给数组及其包含的值分配内存(就像对象一样) var a = [1, null, "abra"]; function f(a){ return a + 2; } // 给函数(可调用的对象)分配内存 // 函数表达式也能分配一个对象 someElement.addEventListener('click', function(){ someElement.style.backgroundColor = 'blue'; }, false);通过函数调用分配内存有些函数调用结果是分配对象内存:var d = new Date(); // 分配一个 Date 对象 var e = document.createElement('div'); // 分配一个 DOM 元素 有些方法分配新变量或者新对象: var s = "azerty"; var s2 = s.substr(0, 3); // s2 是一个新的字符串 // 因为字符串是不变量, // JavaScript 可能决定不分配内存, // 只是存储了 [0-3] 的范围。 var a = ["ouais ouais", "nan nan"]; var a2 = ["generation", "nan nan"]; var a3 = a.concat(a2); // 新数组有四个元素,是 a 连接 a2 的结果使用值使用值的过程实际上是对分配内存进行读取与写入的操作。读取与写入可能是写入一个变量或者一个对象的属性值,甚至传递函数的参数。当内存不再需要使用时释放大多数内存管理的问题都在这个阶段。在这里最艰难的任务是找到“所分配的内存确实已经不再需要了”。它往往要求开发人员来确定在程序中哪一块内存不再需要并且释放它。高级语言解释器嵌入了“垃圾回收器”,它的主要工作是跟踪内存的分配和使用,以便当分配的内存不再使用时,自动释放它。这只能是一个近似的过程,因为要知道是否仍然需要某块内存是无法判定的(无法通过某种算法解决)。垃圾回收如上文所述自动寻找是否一些内存“不再需要”的问题是无法判定的。因此,垃圾回收实现只能有限制的解决一般问题。本节将解释必要的概念,了解主要的垃圾回收算法和它们的局限性。引用垃圾回收算法主要依赖于引用的概念。在内存管理的环境中,一个对象如果有访问另一个对象的权限(隐式或者显式),叫做一个对象引用另一个对象。例如,一个Javascript对象具有对它原型的引用(隐式引用)和对它属性的引用(显式引用)。在这里,“对象”的概念不仅特指 JavaScript 对象,还包括函数作用域(或者全局词法作用域)。引用计数垃圾收集这是最简单的垃圾收集算法。此算法把“对象是否不再需要”简化定义为“对象有没有其他对象引用到它”。如果没有引用指向该对象(零引用),对象将被垃圾回收机制回收。示例var o = { a: { b:2 } }; // 两个对象被创建,一个作为另一个的属性被引用,另一个被分配给变量o // 很显然,没有一个可以被垃圾收集 var o2 = o; // o2变量是第二个对“这个对象”的引用 o = 1; // 现在,“这个对象”的原始引用o被o2替换了 var oa = o2.a; // 引用“这个对象”的a属性 // 现在,“这个对象”有两个引用了,一个是o2,一个是oa o2 = "yo"; // 最初的对象现在已经是零引用了 // 他可以被垃圾回收了 // 然而它的属性a的对象还在被oa引用,所以还不能回收 oa = null; // a属性的那个对象现在也是零引用了 // 它可以被垃圾回收了限制:循环引用该算法有个限制:无法处理循环引用。在下面的例子中,两个对象被创建,并互相引用,形成了一个循环。它们被调用之后不会离开函数作用域,所以它们已经没有用了,可以被回收了。然而,引用计数算法考虑到它们互相都有至少一次引用,所以它们不会被回收function f(){ var o = {}; var o2 = {}; o.a = o2; // o 引用 o2 o2.a = o; // o2 引用 o return "azerty"; } f();实际例子IE 6, 7 使用引用计数方式对 DOM 对象进行垃圾回收。该方式常常造成对象被循环引用时内存发生泄漏:var div; window.onload = function(){ div = document.getElementById("myDivElement"); div.circularReference = div; div.lotsOfData = new Array(10000).join("*"); };在上面的例子里,myDivElement 这个 DOM 元素里的 circularReference 属性引用了 myDivElement,造成了循环引用。如果该属性没有显示移除或者设为 null,引用计数式垃圾收集器将总是且至少有一个引用,并将一直保持在内存里的 DOM 元素,即使其从DOM 树中删去了。如果这个 DOM 元素拥有大量的数据 (如上的 lotsOfData 属性),而这个数据占用的内存将永远不会被释放。标记-清除算法这个算法把“对象是否不再需要”简化定义为“对象是否可以获得”。这个算法假定设置一个叫做根(root)的对象(在Javascript里,根是全局对象)。定期的,垃圾回收器将从根开始,找所有从根开始引用的对象,然后找这些对象引用的对象……从根开始,垃圾回收器将找到所有可以获得的对象和所有不能获得的对象。这个算法比前一个要好,因为“有零引用的对象”总是不可获得的,但是相反却不一定,参考“循环引用”。从2012年起,所有现代浏览器都使用了标记-清除垃圾回收算法。所有对JavaScript垃圾回收算法的改进都是基于标记-清除算法的改进,并没有改进标记-清除算法本身和它对“对象是否不再需要”的简化定义。循环引用不再是问题了在上面的示例中,函数调用返回之后,两个对象从全局对象出发无法获取。因此,他们将会被垃圾回收器回收。第二个示例同样,一旦 div 和其事件处理无法从根获取到,他们将会被垃圾回收器回收。限制: 那些无法从根对象查询到的对象都将被清除尽管这是一个限制,但实践中我们很少会碰到类似的情况,所以开发者不太会去关心垃圾回收机制。文章来自
2017年12月26日
0 阅读
0 评论
0 点赞
2017-12-16
React无限极菜单生成
//创建无菜单 generateMenu(menuObj) { let vdom = []; if (menuObj instanceof Array) { //判断是否为数组 let list = []; for (var item of menuObj) { //把数组内的数据再次传入(递归点) list.push(this.generateMenu(item)); } //为数组就添加ULul vdom.push( <ul key='single'> {list} </ul> ); } else { if(!menuObj){ return } //为对象时添加li vdom.push( <li key={menuObj.ModuleId} data-id={menuObj.ModuleId}> {menuObj.ModuleName} {this.generateMenu(menuObj.child)} </li> ); } return vdom; }
2017年12月16日
0 阅读
0 评论
0 点赞
2017-12-13
选择相机的曝光模式
摄影入门第一步,就是学会使用你手中的相机。现在的数码相机功能越来越强大,别说新手,可能有些经验的老朋友都不一定清楚相机身上每个功能的作用。在所有这些复杂的按钮当中,最重要莫过于曝光模式了――全自动?程序自动?光圈优先?快门优先?全手动?如果你还没弄清楚它们之间的关系,不妨看看这篇文章补补课吧。为了说明各种模式的特性与适用范围,我们需要引入几个基础概念――曝光三要素,即曝光长短、光圈大小、感光度高低,这三个关键因素将决定最终的成像效果。要素一:光圈大小即镜头通光口径的大小,通常用f值来表示,f=镜头的焦距/镜头的有效口径的直径。f值越小,光圈越大,单位通光量越多;f值越大,光圈越小,单位通光量越小。例如:光圈f/1.8要比f/5.6的口径更大,单位时间内通光量更多!同时,光圈也是影响景深的一个重要因素,在保持其它因素不变的情况下,光圈越大,景深越浅(虚化效果越明显);光圈越小,景深越深(清晰范围越宽广)。大光圈可以制造虚化,小光圈容易产生星芒。由此可见,我们通过控制光圈可以达到两个目的,一是控制光线的进入量,二是可以控制景深的效果。要素二:快门速度快门速度即是曝光时间,通常用分数来表示,速度越快,曝光时间越短,通光量越少;速度越慢,曝光时间越长,通光量越多。例如,1/250s的快门要比1/30s秒更快,也就意味着通光的时间更少,所以获取的光线也更少。同时,快门速度也决定了相机的抓拍效果,想要捕捉飞速运动的物体,我们就应该使用高速快门。要素三:ISO感光度(ISO),这是一个从胶片时期就流传下来的概念,代表感光材料对光线的敏感程度,ISO越高,感光元件对光线越敏感,达到同样效果需要的光线量越少;ISO越低,感光元件对光线越迟钝,达到同样效果需要的光线量越多。不过由于数码相机感光元件的限制,高ISO下,画面容易产生噪点,所以许多摄影爱好者在拍摄时都在尽量压低ISO,以获得更加纯净的画面。ISO的意义,就是在光线条件极其不好的情况下,通过提升感光元件的灵敏性,从而解放光圈和快门的组合。大家可能会疑问,为什么文章主题是“相机的曝光模式”,却还要重复听这些无聊的基础知识?不要不以为然,这正是“教你如何选择相机的曝光模式”这一问题的关键所在。一、P档及自动挡:适合纯新手或不想设置相机的懒同学在我们常识里,往往认为P档就是新手档,这是有那么几分道理的。虽然P档与全自动档稍有不同的,但是它们的原理基本相同。全自动档也就是傻瓜模式,全靠相机环境的测定制定出一套保证曝光足够的模式,他会自动选择白平衡、光圈、快门、ISO、闪光灯等一切手段促使拍摄成功,不会考虑拍摄意图。而P档是由相机根据拍摄环境,再结合内部储存的曝光资料来生成一个相对合适的光圈和快门的曝光组合进行曝光。看起来好像高级辣么一点,其实都是只需按下快门的傻瓜模式。固然,这种模式就是适合一无所知的新手,或是对相机设置毫无兴趣的“懒”同学。不过,使用这种方法并非意味着不思进取,有人认为,相机的程序曝光功能已经这么强大了,何不利用这个优势,把更多的时间花在提升在构图方面呢?手机摄影越来越厉害,也不都是P档的原理吗?似乎也有几分道理。二、光圈优先模式:适合想要学习摄影并想要对光线进行深入了解的同学顾名思义,摄影师根据自身所需先确定拍摄光圈,然后相机根据你的光圈给出一个保证合理曝光的快门速度。使用光圈优先模式,首要考虑的就是景深的变化,对于不同的主题肯定需要通过不同的景深来表现。例如使用大光圈让人像的背景虚化:又如在风光摄影师中,使用小光圈保证足够的景深:需要注意的是,使用小光圈拍摄,相应的快门速度也会变慢,常常需要用到三脚架来固定相机。就算使用光圈优先模式,也应该注意快门的速度。相机只会根据光线的通量来给出一个建议快门速度,它无法细致判定当时的拍摄环境,比如弱光环境,相机可能会根据你的光圈给出一个低于安全快门的速度,导致手抖。之所以说,这是最适合刚学习摄影的同学使用的曝光模式,是因为它涉及到了景深控制、曝光组合等设定,在每一次拍摄时,慢慢摸索这几个因素之间的关系,能够帮助你更好的理解曝光。三、快门优先模式:用于创作特殊的摄影效果与光圈优先模式相反,快门优先模式是先确定快门速度,然后相机给出一个适当的光圈大小。根据我们以往的认识,拍照就是定格某个精彩瞬间,的确,有时候就是这么简单。例如在拍摄运动场景时,面对高速移动的物体,只有通过同样高速的快门才能够捕捉。我们在使用高速快门时,要注意是否有足够大的光圈或是ISO来保证曝光量。通过控制快门,不仅能定格瞬间,还能“拖延”时间,例如在拍摄跑步或赛车,利用较慢的快门,拍摄时跟随画面主体移动,就能形成“主体清晰,背景模糊”的追焦效果。又或者是使用慢速快门,在拍摄时旋转你的相机,把普通的画面变得抽象起来。四、手动模式:适合认真玩摄影并有耐心持之以恒的同学到了这一步,才是真正开始玩摄影。摄影说白了,就是利用光影来和这个世界玩一场游戏,而手动模式则是让你把这场游戏的规则都掌握在自己手中。这里送上大家一句话,大家以后应该会深有体会――没有准确的曝光,只有合适的曝光。前面所说的几种自动模式、半自动模式,其实都有相机参与,相机帮你决定了一些参数设置,使你的画面变的“正确”而标准。既然是数码时代,手动模式也基本没有什么压力了,现在相机自带的测光功能已经非常强大,足以作为拍摄参考。这张剪影就是一个非常好的例子,通过手动曝光,保证背景和前景点亮,主体人物变为剪影。如果交给相机自己来完成,很可能出现的情况就是人物曝光准确了,但是背景却过爆了。手动模式的另一个用法就是B门,也就是常见的长曝光。在长至数分钟的拍摄过程中,它需要摄影师对画面有一个独立的判断与把控,反复尝试、不断调整,直到画面获得合适的曝光为止,这是无法通过相机的自带功能判断的。总结对于新手来说,如何去理解相机不同的曝光模式,以及怎样做出合理的选择,我们可以从这几点入手:(1)如果你怕麻烦,不想在参数设置上花太多时间,请使用P档;(2)如果你要学习摄影,理解相机与光影之间的关系,并控制景深,请使用光圈优先档;(3)如果你需要控制曝光时间,例如拍摄高速运动、慢速特殊效果等情况,请使用快门优先模式。(4)当然,如果你想要认真玩摄影,一定要学会使用手动挡。文章来自
2017年12月13日
6 阅读
0 评论
3 点赞
2017-12-06
css滚动条样式
/*定义滚动条高宽及背景 高宽分别对应横竖滚动条的尺寸*/ ::-webkit-scrollbar { width: 16px; height: 16px; background-color: #F5F5F5; } /*定义滚动条轨道 内阴影+圆角*/ ::-webkit-scrollbar-track { -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3); border-radius: 10px; background-color: #F5F5F5; } /*定义滑块 内阴影+圆角*/ ::-webkit-scrollbar-thumb { border-radius: 10px; -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,.3); background-color: #555; } 详细设置定义滚动条就是利用伪元素与伪类,那什么是伪元素和伪类呢?伪类大家应该很熟悉:link,:focus,:hover,此外CSS3中又增加了许多伪类选择器,如:nth-child,:last-child,:nth-last-of-type()等。CSS中的伪元素大家以前看过::first-line,:first-letter,:before,:after。那么在CSS3中,伪元素进行了调整,在以前的基础上增加了一个“:”也就是现在变成了“::first-letter,::first-line,::before,::after”,另外CSS3还增加了一个“::selection”。两个“::”和一个“:”在css3中主要用来区分伪类和伪元素。webkit的伪类和伪元素的实现很强,可以把滚动条当成一个页面元素来定义,再结合一些高级的CSS3属性,比如渐变、圆角、RGBa等等。然后如果有些地方要用图片,可以把图片也可以转换成Base64,不然每次都得加载那个多个图片,增加请求数。任何对象都可以设置:边框、阴影、背景图片等等,创建的滚动条任然会按照操作系统本身的设置来完成其交互的行为。下面的伪类可以应用到上面的伪元素中。有点小复杂,具体怎么写可以看第一个demo,那里也有注释。[css] view plain copy :horizontal //horizontal伪类适用于任何水平方向上的滚动条 :vertical //vertical伪类适用于任何垂直方向的滚动条 :decrement //decrement伪类适用于按钮和轨道碎片。表示递减的按钮或轨道碎片,例如可以使区域向上或者向右移动的区域和按钮 :increment //increment伪类适用于按钮和轨道碎片。表示递增的按钮或轨道碎片,例如可以使区域向下或者向左移动的区域和按钮 :start //start伪类适用于按钮和轨道碎片。表示对象(按钮 轨道碎片)是否放在滑块的前面 :end //end伪类适用于按钮和轨道碎片。表示对象(按钮 轨道碎片)是否放在滑块的后面 :double-button //double-button伪类适用于按钮和轨道碎片。判断轨道结束的位置是否是一对按钮。也就是轨道碎片紧挨着一对在一起的按钮。 :single-button //single-button伪类适用于按钮和轨道碎片。判断轨道结束的位置是否是一个按钮。也就是轨道碎片紧挨着一个单独的按钮。 :no-button no-button伪类表示轨道结束的位置没有按钮。 :corner-present //corner-present伪类表示滚动条的角落是否存在。 :window-inactive //适用于所有滚动条,表示包含滚动条的区域,焦点不在该窗口的时候。 ::-webkit-scrollbar-track-piece:start { /*滚动条上半边或左半边*/ } ::-webkit-scrollbar-thumb:window-inactive { /*当焦点不在当前区域滑块的状态*/ } ::-webkit-scrollbar-button:horizontal:decrement:hover { /*当鼠标在水平滚动条下面的按钮上的状态*/ }原文地址:http://www.xuanfengge.com/css3-webkit-scrollbar.html
2017年12月06日
0 阅读
0 评论
0 点赞
2017-12-05
for of循环和map()方法
在javascript有时候对数组数据进行操作时,会想的var arr=[1,2,3]; for(var i=0; i<arr.length; i++){ arr[i]+=1; }用ES6的for offor(var value of arr){ value=value+1 //特别注意,这里不会改变原数组的值 } //这是最简洁、最直接的遍历数组元素的语法 //这个方法避开了for-in循环的所有缺陷 //与forEach()不同的是,它可以正确响应break、continue和return语句ES6 map()方法和forEach()方法类是var newarr = arr.map((item,index,arr)=>{ //接收一个方法,接收3个参数 console.log(item,index,arr); //map方法返回一个新数组,这也是与forEach()不同之处 return item+1; }) console.log(newarr)
2017年12月05日
0 阅读
0 评论
0 点赞
2017-12-04
react的事件合成
在开发react项目中遇见在调用事件函数里的异步方法访问事件属性时报错.官方给出了解决方法.如果要以异步方式访问事件属性,应该对事件调用 event.persist() ,这将从池中删除合成事件,并允许用户代码保留对事件的引用。
2017年12月04日
1 阅读
0 评论
0 点赞
2017-11-29
那个坑爹的toString
在开发过程中,修改别人的BUG时发现报错无数,来看他代码if (curValue == null || curValue.toString().trim() == "") { this.queryParameter.dicCustomerTrans = ""; }我就想问了curValue=null,时会不会报错?再来let vehicleCode=this.detail.Vehicle_Number; if(vehicleCode!=null){ vehicleCode=vehicleCode.substring(0,vehicleCode.indexOf('_')); } 当vehicleCode=undefined时的会不会报错?总结在上面两种情况下都必须是字符串类型才能调用trim(),substring(),indexOf().在无法确认类型时,不要使用以上方法.还可以转换类型,String(),toString(),前者为类型强转,后者为Obj原型链上的方法,不可转null,undefined,NaN等
2017年11月29日
0 阅读
0 评论
0 点赞
2017-11-28
Redux
§ Store首先要区分 store 和 statestate 是应用的状态,一般本质上是一个普通对象例如,我们有一个 Web APP,包含 计数器 和 待办事项 两大功能那么我们可以为该应用设计出对应的存储数据结构(应用初始状态): /** 应用初始 state,本代码块记为 code-1 **/ { counter: 0, todos: [] }store 是应用状态 state 的管理者,包含下列四个函数:getState() # 获取整个 state dispatch(action) # ※ 触发 state 改变的【唯一途径】※ subscribe(listener) # 您可以理解成是 DOM 中的 addEventListener replaceReducer(nextReducer) # 一般在 Webpack Code-Splitting 按需加载的时候用 二者的关系是:state = store.getState()Redux 规定,一个应用只应有一个单一的 store,其管理着唯一的应用状态 stateRedux 还规定,不能直接修改应用的状态 state,也就是说,下面的行为是不允许的:var state = store.getState()state.counter = state.counter + 1 // 禁止在业务逻辑中直接修改 state若要改变 state,必须 dispatch 一个 action,这是修改应用状态的不二法门现在您只需要记住 action 只是一个包含 type 属性的普通对象即可例如 { type: 'INCREMENT' }上面提到,state 是通过 store.getState() 获取,那么 store 又是怎么来的呢?想生成一个 store,我们需要调用 Redux 的 createStore:import { createStore } from 'redux' ... const store = createStore(reducer, initialState) // store 是靠传入 reducer 生成的哦!现在您只需要记住 reducer 是一个 函数,负责更新并返回一个新的 state而 initialState 主要用于前后端同构的数据同步(详情请关注 React 服务端渲染)§ Action上面提到,action(动作)实质上是包含 type 属性的普通对象,这个 type 是我们实现用户行为追踪的关键例如,增加一个待办事项 的 action 可能是像下面一样:/** 本代码块记为 code-2 **/ { type: 'ADD_TODO', payload: { id: 1, content: '待办事项1', completed: false } }当然,action 的形式是多种多样的,唯一的约束仅仅就是包含一个 type 属性罢了也就是说,下面这些 action 都是合法的:/** 如下都是合法的,但就是不够规范 **/ { type: 'ADD_TODO', id: 1, content: '待办事项1', completed: false } { type: 'ADD_TODO', abcdefg: { id: 1, content: '待办事项1', completed: false } }虽说没有约束,但最好还是遵循规范如果需要新增一个代办事项,实际上就是将 code-2 中的 payload “写入” 到 state.todos 数组中(如何“写入”?在此留个悬念):/** 本代码块记为 code-3 **/ { counter: 0, todos: [{ id: 1, content: '待办事项1', completed: false }] }刨根问底,action 是谁生成的呢?⊙ Action CreatorAction Creator 可以是同步的,也可以是异步的顾名思义,Action Creator 是 action 的创造者,本质上就是一个函数,返回值是一个 action(对象)例如下面就是一个 “新增一个待办事项” 的 Action Creator:/** 本代码块记为 code-4 **/ var id = 1 function addTodo(content) { return { type: 'ADD_TODO', payload: { id: id++, content: content, // 待办事项内容 completed: false // 是否完成的标识 } } }将该函数应用到一个表单(假设 store 为全局变量,并引入了 jQuery ):<--! 本代码块记为 code-5 --> <input type="text" id="todoInput" /> <button id="btn">提交</button> <script> $('#btn').on('click', function() { var content = $('#todoInput').val() // 获取输入框的值 var action = addTodo(content) // 执行 Action Creator 获得 action store.dispatch(action) // 改变 state 的不二法门:dispatch 一个 action!!! }) </script> 在输入框中输入 “待办事项2” 后,点击一下提交按钮,我们的 state 就变成了: /** 本代码块记为 code-6 **/ { counter: 0, todos: [{ id: 1, content: '待办事项1', completed: false }, { id: 2, content: '待办事项2', completed: false }] }通俗点讲,Action Creator 用于绑定到用户的操作(点击按钮等),其返回值 action 用于之后的 dispatch(action)刚刚提到过,action 明明就没有强制的规范,为什么 store.dispatch(action) 之后,Redux 会明确知道是提取 action.payload,并且是对应写入到 state.todos 数组中?又是谁负责“写入”的呢?悬念即将揭晓...§ ReducerReducer 必须是同步的纯函数用户每次 dispatch(action) 后,都会触发 reducer 的执行reducer 的实质是一个函数,根据 action.type 来更新 state 并返回 nextState最后会用 reducer 的返回值 nextState 完全替换掉原来的 state注意:上面的这个 “更新” 并不是指 reducer 可以直接对 state 进行修改Redux 规定,须先复制一份 state,在副本 nextState 上进行修改操作例如,可以使用 lodash 的 cloneDeep,也可以使用 Object.assign / map / filter/ ... 等返回副本的函数在上面 Action Creator 中提到的 待办事项的 reducer 大概是长这个样子 (为了容易理解,在此不使用 ES6 / Immutable.js):/** 本代码块记为 code-7 **/ var initState = { counter: 0, todos: [] } function reducer(state, action) { // ※ 应用的初始状态是在第一次执行 reducer 时设置的 ※ if (!state) state = initState switch (action.type) { case 'ADD_TODO': var nextState = _.cloneDeep(state) // 用到了 lodash 的深克隆 nextState.todos.push(action.payload) return nextState default: // 由于 nextState 会把原 state 整个替换掉 // 若无修改,必须返回原 state(否则就是 undefined) return state } }通俗点讲,就是 reducer 返回啥,state 就被替换成啥§ 总结store 由 Redux 的 createStore(reducer) 生成state 通过 store.getState() 获取,本质上一般是一个存储着整个应用状态的对象action 本质上是一个包含 type 属性的普通对象,由 Action Creator (函数) 产生改变 state 必须 dispatch 一个 actionreducer 本质上是根据 action.type 来更新 state 并返回 nextState 的函数reducer 必须返回值,否则 nextState 即为 undefined实际上,state 就是所有 reducer 返回值的汇总(本教程只有一个 reducer,主要是应用场景比较简单)Action Creator => action => store.dispatch(action) => reducer(state, action) => 原 state state = nextState⊙ Redux 与传统后端 MVC 的对照Redux 传统后端 MVC store 数据库实例 state 数据库中存储的数据 dispatch(action) 用户发起请求 action: { type, payload } type 表示请求的 URL,payload 表示请求的数据 reducer 路由 + 控制器(handler) reducer 中的 switch-case 分支 路由,根据 action.type 路由到对应的控制器 reducer 内部对 state 的处理 控制器对数据库进行增删改操作 reducer 返回 nextState 将修改后的记录写回数据库
2017年11月28日
0 阅读
0 评论
0 点赞
2017-11-26
JS获取各种宽高
IE中: document.body.clientWidth ==> BODY对象宽度 document.body.clientHeight ==> BODY对象高度 document.documentElement.clientWidth ==> 可见区域宽度 document.documentElement.clientHeight ==> 可见区域高度 FireFox中: document.body.clientWidth ==> BODY对象宽度 document.body.clientHeight ==> BODY对象高度 document.documentElement.clientWidth ==> 可见区域宽度 document.documentElement.clientHeight ==> 可见区域高度 Opera中: document.body.clientWidth ==> 可见区域宽度 document.body.clientHeight ==> 可见区域高度 document.documentElement.clientWidth ==> 页面对象宽度(即BODY对象宽度加上Margin宽) document.documentElement.clientHeight ==> 页面对象高度(即BODY对象高度加上Margin高) 没有定义W3C的标准,则 IE为: document.documentElement.clientWidth ==> 0 document.documentElement.clientHeight ==> 0 FireFox为: document.documentElement.clientWidth ==> 页面对象宽度(即BODY对象宽度加上Margin宽) document.documentElement.clientHeight ==> 页面对象高度(即BODY对象高度加上Margin高) Opera为: document.documentElement.clientWidth ==> 页面对象宽度(即BODY对象宽度加上Margin宽) document.documentElement.clientHeight ==> 页面对象高度(即BODY对象高度加上Margin高) 网页可见区域宽: document.body.clientWidth 网页可见区域高: document.body.clientHeight 网页可见区域宽: document.body.offsetWidth (包括边线的宽) 网页可见区域高: document.body.offsetHeight (包括边线的高) 网页正文全文宽: document.body.scrollWidth 网页正文全文高: document.body.scrollHeight 网页被卷去的高: document.body.scrollTop 网页被卷去的左: document.body.scrollLeft 网页正文部分上: window.screenTop 网页正文部分左: window.screenLeft 屏幕分辨率的高: window.screen.height 屏幕分辨率的宽: window.screen.width 屏幕可用工作区高度: window.screen.availHeight 屏幕可用工作区宽度: window.screen.availWidth HTML精确定位:scrollLeft,scrollWidth,clientWidth,offsetWidth scrollHeight: 获取对象的滚动高度。 scrollLeft:设置或获取位于对象左边界和窗口中目前可见内容的最左端之间的距离 scrollTop:设置或获取位于对象最顶端和窗口中可见内容的最顶端之间的距离 scrollWidth:获取对象的滚动宽度 offsetHeight:获取对象相对于版面或由父坐标 offsetParent 属性指定的父坐标的高度 offsetLeft:获取对象相对于版面或由 offsetParent 属性指定的父坐标的计算左侧位置 offsetTop:获取对象相对于版面或由 offsetTop 属性指定的父坐标的计算顶端位置 event.clientX 相对文档的水平座标 event.clientY 相对文档的垂直座标 event.offsetX 相对容器的水平坐标 event.offsetY 相对容器的垂直坐标 document.documentElement.scrollTop 垂直方向滚动的值 event.clientX+document.documentElement.scrollTop 相对文档的水平座标+垂直方向滚动的量
2017年11月26日
1 阅读
0 评论
0 点赞
2017-11-25
React-Router
一、基本用法React Router 安装命令如下。$ npm install -S react-router使用时,路由器Router就是React的一个组件。import { Router } from 'react-router'; render(<Router/>, document.getElementById('app'));Router组件本身只是一个容器,真正的路由要通过Route组件定义。import { Router, Route, hashHistory } from 'react-router'; render(( <Router history={hashHistory}> <Route path="/" component={App}/> </Router> ), document.getElementById('app')); 上面代码中,用户访问根路由/(比如http://www.example.com/),组件APP就会加载到document.getElementById('app')。 你可能还注意到,Router组件有一个参数history,它的值hashHistory表示,路由的切换由URL的hash变化决定,即URL的#部分发生变化。举例来说,用户访问http://www.example.com/,实际会看到的是http://www.example.com/#/。 Route组件定义了URL路径与组件的对应关系。你可以同时使用多个Route组件。 <Router history={hashHistory}> <Route path="/" component={App}/> <Route path="/repos" component={Repos}/> <Route path="/about" component={About}/> </Router>上面代码中,用户访问/repos(比如http://localhost:8080/#/repos)时,加载Repos组件;访问/about(http://localhost:8080/#/about)时,加载About组件。二、嵌套路由Route组件还可以嵌套。<Router history={hashHistory}> <Route path="/" component={App}> <Route path="/repos" component={Repos}/> <Route path="/about" component={About}/> </Route> </Router>上面代码中,用户访问/repos时,会先加载App组件,然后在它的内部再加载Repos组件。<App> <Repos/> </App>App组件要写成下面的样子。export default React.createClass({ render() { return <div> {this.props.children} </div> } })上面代码中,App组件的this.props.children属性就是子组件。子路由也可以不写在Router组件里面,单独传入Router组件的routes属性。let routes = <Route path="/" component={App}> <Route path="/repos" component={Repos}/> <Route path="/about" component={About}/> </Route>; <Router routes={routes} history={browserHistory}/>三、 path 属性Route组件的path属性指定路由的匹配规则。这个属性是可以省略的,这样的话,不管路径是否匹配,总是会加载指定组件。请看下面的例子。<Route path="inbox" component={Inbox}> <Route path="messages/:id" component={Message} /> </Route>上面代码中,当用户访问/inbox/messages/:id时,会加载下面的组件。<Inbox> <Message/> </Inbox>如果省略外层Route的path参数,写成下面的样子。<Route component={Inbox}> <Route path="inbox/messages/:id" component={Message} /> </Route>现在用户访问/inbox/messages/:id时,组件加载还是原来的样子。<Inbox> <Message/> </Inbox>四、通配符path属性可以使用通配符。<Route path="/hello/:name"> // 匹配 /hello/michael // 匹配 /hello/ryan <Route path="/hello(/:name)"> // 匹配 /hello // 匹配 /hello/michael // 匹配 /hello/ryan <Route path="/files/*.*"> // 匹配 /files/hello.jpg // 匹配 /files/hello.html <Route path="/files/*"> // 匹配 /files/ // 匹配 /files/a // 匹配 /files/a/b <Route path="/**/*.jpg"> // 匹配 /files/hello.jpg // 匹配 /files/path/to/file.jpg通配符的规则如下。(1):paramName:paramName匹配URL的一个部分,直到遇到下一个/、?、#为止。这个路径参数可以通过this.props.params.paramName取出。(2)()()表示URL的这个部分是可选的。(3)**匹配任意字符,直到模式里面的下一个字符为止。匹配方式是非贪婪模式。(4) **** 匹配任意字符,直到下一个/、?、#为止。匹配方式是贪婪模式。path属性也可以使用相对路径(不以/开头),匹配时就会相对于父组件的路径,可以参考上一节的例子。嵌套路由如果想摆脱这个规则,可以使用绝对路由。路由匹配规则是从上到下执行,一旦发现匹配,就不再其余的规则了。上面代码中,路径/comments同时匹配两个规则,第二个规则不会生效。设置路径参数时,需要特别小心这一点。<Router> <Route path="/:userName/:id" component={UserPage}/> <Route path="/about/me" component={About}/> </Router>上面代码中,用户访问/about/me时,不会触发第二个路由规则,因为它会匹配/:userName/:id这个规则。因此,带参数的路径一般要写在路由规则的底部。此外,URL的查询字符串/foo?bar=baz,可以用this.props.location.query.bar获取。五、IndexRoute 组件下面的例子,你会不会觉得有一点问题?<Router> <Route path="/" component={App}> <Route path="accounts" component={Accounts}/> <Route path="statements" component={Statements}/> </Route> </Router>上面代码中,访问根路径/,不会加载任何子组件。也就是说,App组件的this.props.children,这时是undefined。因此,通常会采用{this.props.children || <Home/>}这样的写法。这时,Home明明是Accounts和Statements的同级组件,却没有写在Route中。IndexRoute就是解决这个问题,显式指定Home是根路由的子组件,即指定默认情况下加载的子组件。你可以把IndexRoute想象成某个路径的index.html。<Router> <Route path="/" component={App}> <IndexRoute component={Home}/> <Route path="accounts" component={Accounts}/> <Route path="statements" component={Statements}/> </Route> </Router>现在,用户访问/的时候,加载的组件结构如下。<App> <Home/> </App>这种组件结构就很清晰了:App只包含下级组件的共有元素,本身的展示内容则由Home组件定义。这样有利于代码分离,也有利于使用React Router提供的各种API。注意,IndexRoute组件没有路径参数path。六、Redirect 组件组件用于路由的跳转,即用户访问一个路由,会自动跳转到另一个路由。<Route path="inbox" component={Inbox}> {/* 从 /inbox/messages/:id 跳转到 /messages/:id */} <Redirect from="messages/:id" to="/messages/:id" /> </Route>现在访问/inbox/messages/5,会自动跳转到/messages/5。七、IndexRedirect 组件IndexRedirect组件用于访问根路由的时候,将用户重定向到某个子组件。<Route path="/" component={App}> <IndexRedirect to="/welcome" /> <Route path="welcome" component={Welcome} /> <Route path="about" component={About} /> </Route>上面代码中,用户访问根路径时,将自动重定向到子组件welcome。八、LinkLink组件用于取代元素,生成一个链接,允许用户点击后跳转到另一个路由。它基本上就是元素的React 版本,可以接收Router的状态。render() { return <div> <ul role="nav"> <li><Link to="/about">About</Link></li> <li><Link to="/repos">Repos</Link></li> </ul> </div> }如果希望当前的路由与其他路由有不同样式,这时可以使用Link组件的activeStyle属性。<Link to="/about" activeStyle={{color: 'red'}}>About</Link> <Link to="/repos" activeStyle={{color: 'red'}}>Repos</Link>上面代码中,当前页面的链接会红色显示。另一种做法是,使用activeClassName指定当前路由的Class。<Link to="/about" activeClassName="active">About</Link> <Link to="/repos" activeClassName="active">Repos</Link>上面代码中,当前页面的链接的class会包含active。在Router组件之外,导航到路由页面,可以使用浏览器的History API,像下面这样写。import { browserHistory } from 'react-router'; browserHistory.push('/some/path');九、IndexLink如果链接到根路由/,不要使用Link组件,而要使用IndexLink组件。这是因为对于根路由来说,activeStyle和activeClassName会失效,或者说总是生效,因为/会匹配任何子路由。而IndexLink组件会使用路径的精确匹配。<IndexLink to="/" activeClassName="active"> Home </IndexLink>上面代码中,根路由只会在精确匹配时,才具有activeClassName。另一种方法是使用Link组件的onlyActiveOnIndex属性,也能达到同样效果。<Link to="/" activeClassName="active" onlyActiveOnIndex={true}> Home </Link>实际上,IndexLink就是对Link组件的onlyActiveOnIndex属性的包装。十、histroy 属性Router组件的history属性,用来监听浏览器地址栏的变化,并将URL解析成一个地址对象,供 React Router 匹配。history属性,一共可以设置三种值。browserHistory hashHistory createMemoryHistory如果设为hashHistory,路由将通过URL的hash部分(#)切换,URL的形式类似example.com/#/some/path。import { hashHistory } from 'react-router' render( <Router history={hashHistory} routes={routes} />, document.getElementById('app') )如果设为browserHistory,浏览器的路由就不再通过Hash完成了,而显示正常的路径example.com/some/path,背后调用的是浏览器的History API。import { browserHistory } from 'react-router' render( <Router history={browserHistory} routes={routes} />, document.getElementById('app') )但是,这种情况需要对服务器改造。否则用户直接向服务器请求某个子路由,会显示网页找不到的404错误。如果开发服务器使用的是webpack-dev-server,加上--history-api-fallback参数就可以了。$ webpack-dev-server --inline --content-base . --history-api-fallbackcreateMemoryHistory主要用于服务器渲染。它创建一个内存中的history对象,不与浏览器URL互动。const history = createMemoryHistory(location)十一、表单处理Link组件用于正常的用户点击跳转,但是有时还需要表单跳转、点击按钮跳转等操作。这些情况怎么跟React Router对接呢?下面是一个表单。<form onSubmit={this.handleSubmit}> <input type="text" placeholder="userName"/> <input type="text" placeholder="repo"/> <button type="submit">Go</button> </form>第一种方法是使用browserHistory.pushimport { browserHistory } from 'react-router' // ... handleSubmit(event) { event.preventDefault() const userName = event.target.elements[0].value const repo = event.target.elements[1].value const path = `/repos/${userName}/${repo}` browserHistory.push(path) },第二种方法是使用context对象。export default React.createClass({ // ask for `router` from context contextTypes: { router: React.PropTypes.object }, handleSubmit(event) { // ... this.context.router.push(path) }, })十二、路由的钩子每个路由都有Enter和Leave钩子,用户进入或离开该路由时触发。<Route path="about" component={About} /> <Route path="inbox" component={Inbox}> <Redirect from="messages/:id" to="/messages/:id" /> </Route>上面的代码中,如果用户离开/messages/:id,进入/about时,会依次触发以下的钩子。/messages/:id的onLeave/inbox的onLeave/about的onEnter下面是一个例子,使用onEnter钩子替代组件。<Route path="inbox" component={Inbox}> <Route path="messages/:id" onEnter={ ({params}, replace) => replace(`/messages/${params.id}`) } /> </Route>onEnter钩子还可以用来做认证。const requireAuth = (nextState, replace) => { if (!auth.isAdmin()) { // Redirect to Home page if not an Admin replace({ pathname: '/' }) } } export const AdminRoutes = () => { return ( <Route path="/admin" component={Admin} onEnter={requireAuth} /> ) }下面是一个高级应用,当用户离开一个路径的时候,跳出一个提示框,要求用户确认是否离开。const Home = withRouter( React.createClass({ componentDidMount() { this.props.router.setRouteLeaveHook( this.props.route, this.routerWillLeave ) }, routerWillLeave(nextLocation) { // 返回 false 会继续停留当前页面, // 否则,返回一个字符串,会显示给用户,让其自己决定 if (!this.state.isSaved) return '确认要离开?'; }, }) )上面代码中,setRouteLeaveHook方法为Leave钩子指定routerWillLeave函数。该方法如果返回false,将阻止路由的切换,否则就返回一个字符串,提示用户决定是否要切换。文章来自阮一峰的网络日志!
2017年11月25日
0 阅读
0 评论
0 点赞
1
...
12
13
14
...
16