标签归档:xishui

MobX —— 10分钟极速入门 MobX 与 React

MobX 是一个简单、方便扩展、久经考验的状态管理解决方案。这个教程旨在十分钟内向你介绍 MobX 的一些重要概念。MobX 是一个独立的苦,不过大多数人都把它和 React 一起使用,所以本教程也就着眼于这个组合展开。

核心概念

State 是每一个应用程序的核心部分,而使用一个不合规范的 State 则是让你的应用充满 bug 和失控的不二法门,或者就是局部变量环绕,让你的 state 失去了同步。有很多框架试图解决这个问题,比如使用不可变的 state,但是这样以来又带来了新的问题,比如数据必须规格化,完整性约束失效等等。

MobX 让整个事情又变简单了:它不允许产生失控的 state。它的理念也很简单:所有可以从 state 中派生的事物,都会自动的派生。

把 MobX 想象成 Excel 表格。

  1. 首先,有一个 state,它可以是一个object,array,primitives等等任何组成你程序的部分。你可以把这个想象成你应用程序的“单元格”。
  2. 然后就是 derivations,一般它是指可以从 state 中直接计算的来的结果。比如未完成的任务的数量,这个比较简单,也可以稍复杂一些比如渲染你的任务显示的html。它类似于你的应用程序中的“公式和图表”。
  3. Reactions 和 derivations 很像,主要的区别在于 reactions 并不产生数据结果,而是自动完成一些任务,一般是和 I/O 相关的。他们保证了 DOM 和 网络请求会自动适时地出发。
  4. 最后是 actions。Actions 指的是所有会改变 state 的事情,MobX 保证所有 actions 都会有对应的 derivations 和 reactions 相伴,保证同步。

一个简单的 todo 的 state

理论说的够多的了,看一个例子也许会更明白一些。我们从一个简单的 todo 程序开始。

下面是一个简单直接的 TodoStore,没有鱼丸,没有粗面,没有 MobX ……

我们创建了一个 todoStore,它拥有一个 todos 集合。现在我们往这个 todoStore 里添加一些东西,为了明显起见,我们每修改一个地方,就调用todoStore.report

到现在为止,没有什么特别的。不过如果我们可以不再手动调用 report 方法,事情会不会更美好一些?我们只需要在想要的地方修改这个 state,所有的汇报都自动来做。

太巧了,这就是 MobX 能为你做的事情。自动执行只在 state 改变的时候触发,就好像 Excel 中的图表只在单元格数据改变时更新一样。为了达到这个目标,TodoStore 必须成为可观测的(observable)才行,让我们来改一些代码。

同时,completedTodosCount 属性应该被自动派生,使用 @observable 和 @computed 装饰器来做这些事情:

运行它,太棒了,我们每次赋值都能获得输出结果了。

有个 pendingRequests 暂时没用到,我们后面会用。另外这个教程都用了 ES6 的写法,不过 MobX 也支持 ES5 的写法。

在这个构造器中,我们使用autorun包裹了一个打出report的小函数。Autorun里的东西首先会运行一次,然后当其中的函数有observable的数据发生变化时,会再次运行。 这里我们使用了todos属性,每次todos变化了我们就打印出新的东西。

(可以自己试试结果)

非常有趣是吧,report确实自己执行了,而且同步又精准。如果你仔细查看运行结果的话,你挥发性我们的第四句语句没有产生输出,因为我们修改了todos[1]的数据,而我们在report中指明的数据,并没有todos[1]的变化而发生变化。而第五句话修改了todos[0]的数据则输出了。这个例子很好的说明了,autorun不是简单的监视了todos,而是精确到了具体的一项。

让React更美好

好了,到目前未知,我们使report自动化了,是实话把react拉出来遛遛了。为了是的react 的组件可以识别mobx,我们需要使用mobx-react包来完成,使用autorun,自动的让组件和state同步,这个简直就和上面的让report自动输出一样简单。

下面是一个react 组件,唯一MobX出场的地方就是一个@observer修饰符,这已经足够了,你再也不用使用setState了,你也不需要指明这个组件需要关注state的哪个部分,也不许手动写什么高阶组件。一般来说,所有的部件都变成人工智能了,即使他被定义成一个木偶(纯展示)组件。

执行下面的语句,我们会发现MobX帮我们把数据的更改反应到界面上去了。

 

使用引用(References)

到现在位置,我们已经使用 observable 创建了个数据类型了。也许你会想,MobX 能不能应付引用呢?在之前的例子里,你可能主意到了又一个 assignee 的属性,我们就在这里放另外的一个 store,然后把它赋值给 tasks。

毫无疑问的,MobX 把着一切打理的井井有条。使用 MobX ,不需要规格话数据,不需要指明控件,事实上你的数据在哪里都无所谓。只要 observale 了,什么都好了。

 

 

总结

好了,仅仅依靠一些简单的修饰器,我们就让 react 程序如此生动有趣。最后总结一些:

@observale 修饰器或者 observable 函数让对象可以被追踪;
@computed 修饰器创造了自动运算的表达式;
autorun 函数让依靠 observable 的函数自动执行,这个用来写 log,发请求很不错;
@observer 修饰器让 React 组建自动起来,它会自动更新,即便是在一个很大的程序里也会工作的很好;

最后,MobX 不是一个状态容器

很多人把 MobX 当作另外一个 Redux,但是它仅仅是一个库,不是一个什么架构。上面的例子还是需要程序员自己去组织逻辑和store或者控制器什么的,正如有人在 HackerNews 上所说的:

 

“MobX, it’s been mentioned elsewhere but I can’t help but sing its praises. Writing in MobX means that using controllers/ dispatchers/ actions/ supervisors or another form of managing dataflow returns to being an architectural concern you can pattern to your application’s needs, rather than being something that’s required by default for anything more than a Todo app.”

父亲节的碎碎念

自打换了工作,有了娃儿,博客又荒了。要说真挤不出一丁点时间来耕作,应该还不至于,但是总感觉精力不够,每天八九点多就昏昏欲睡,早上五六点就要起来,像我这种每晚都要睡足8小时,否则第二天就会假死状态的人,超级羡慕那些拥有“少睡基因”的家伙。

今天是父亲节,算起来是我成为父亲后的第一个父亲节啊,不过,也是我失去父亲后的第一个父亲节……

……多感慨也无益,随便唠叨点别的吧。

昨天不是6.18粉丝节么,趁着各种部件打折的势头,给老婆的笔记本+内存换SSD(就在我打下这句话的时候,顺丰到了,果然迅速)。本来也要给自己的六七年的老电脑更新换代的,B150 + i5 6500,16G DDR4,也换个SSD,买下来3000不到一点点,应该有质的飞跃,但是后来想想自己开PC时间太少了,又基本不玩游戏,何必呢……付款后还是退了款(根本原因还是因为穷吧?),就给老婆的笔记本升升级吧,她照顾孩子比我辛苦多了。

抱歉,我还是不可避免的想起彼岸的父亲,现在辛苦着的母亲,“生活从来不易,当你觉得容易时,是有人为你承担了那份不容易”,我不觉得生活容易,但也没有觉得艰辛,终究是因为自己依靠双亲太多了。

在知乎上看到有人说“父母没有经过你的同意就将你带到这个世界,所以他们必须抚养你,这个必然,孝道和赡养只有人类如此,并不是自然法则,不需要去遵守”,我无从辩驳,而且根本就不想去反驳。生而为人,我幸,艰辛多舛,我命。但有人能在我稚嫩的时候帮我开疆辟土,为我挡风遮雨,即便我长大之后行走的不是康庄大道,但我毕竟站在这里,我有前瞻的无限将来,有回首的美好回忆,此时、此刻,我在想着你们。

换了份工作

5月重新换了份工作,相对来说要严格的多,晚上回家还要陪宝宝玩儿,时间真是少少的,也就想不到更新博客了……又要荒了

2016说是VR元年,很多设备啊游戏啊视频啊AV啊(好像混入了什么不得了的东西)层出不穷,想着前两年挺热的刀剑神域动漫,也算是VR的高端成就,会有一天变成那样吗?有生之年我能看到不~不过想体验还是太不容易了,设备贵,还要很好的电脑。现在用来码字的这台台式机,应该有六七年了吧,但是自动升级到了Win10后,开机又快,Chrome跑的也很流畅,要是什么时候换个SSD,除了网速不够快,我还有什么追求呢?。。。还是省点奶粉钱吧。

 

不要在微信上用material-ui

微信浏览器已经全面升级Webkit内核了(好样的),所以这篇文章有点不合时宜了,仅留做参考。

—————————–

这里说的 material-ui 是指这个UI框架,并不是说 Google 的 Material Design 设计风格。

自打脱离 Angular 的怀抱拥抱 React 后,我把移动端的前端也都用上了 ReactJS,在前端框架的选择过程中,我瞄上了 material-ui 这个实现了google material 设计的框架,虽然部件并不是很丰富,但是已经可以给我省下很多力气了,Github 上的赞也很多,之前做过一个简单的微信应用,因为很简单,所以用起来没有什么太多的问题。这次开始做一个“比较复杂”的应用,没多想还是上了 material-ui,不想……

微信出了个调试工具,看起来像是用 atom 的同系 electron 君做出来的的,可以比较简单的调试公众号网页。因为是 electron,所以内核应该是webkit无误,在开发整个公众号的时候,我一直在上面调试,最后差不多完成迁移到服务器,用手机打开一看,我·乐·个·趣,这是我做的吗??

小小地总结如下:

微信Android版里用的是X5的核心,传说中2010年的技术,也能算的上是“现代浏览器”了,但是它不认识flex,material-ui中大量使用了flex布局,从AppBar到GridList,可以说flex布局是CSS3布局最让人振奋的一点,但是微信一脸茫然,都给解释成block了。

OK,我知道微信里可以用古老的-webkit-box布局,和flex是同根同源,先不说那反人类的写法,react中style用的是字典,意味着style={{display:”flex”,display:”-webkit-box”}}最终只能保留后者,那么不是android版微信的咋办?用 vendor-prefix 库吧,微信游览器根本就不在人家的支持范围内!

于是只能用 className 写原生的css,有些东西是可以这么来一下的,但是 material-ui 中的 component 包裹的太深啦,我都没法指明哪个div哪个 input…… 在写了无数的hack后,终于先点样子了。不过还有个坑是,如果你用postCSS,其中的cssnano会把box类型的声明给自动去掉,记住将autoprefixer的remove选项关掉,我们自己来注意不写费代码吧。

 

不过 toggle 组件一直无法使用,最后查看源码,发现它是依靠 input type=checkbox 来触发的,OK 其实很常规的写法,iPhone上的微信是可以正常动作的,然而在Android微信中,checkbox的input根本就不能修改大小,意味着整个组件只有那么一米米大的触发空间,正常情况下那么一个硕大的Toggle,用户只有点左上角一丁点大的地方的时候才能切换状态;而当你修改一下toggle的大小的时候,你根本不知道那个透明的input在什么位置…… 你给我滚出来我保证不打死你。

最后不得已,放弃组件本身的onToggle,使用外层的元素的onClick来修改toggle的状态,看起来倒也不错。

 

还有一个我到现在也没明白为啥,dropdown menu 和 select field 在微信里是空白,点开倒是能显示咯菜单背景,但是菜单项看不到,也不可点选,我实在是没力气去debug了,换回原生的select……

 

最后,material-ui本身还是不错的,在手机版chrome上运行良好,就是我大魅族自带浏览器都运行的很正常。只是没想到亿万用户的微信浏览器这么孱弱,之前一直用bootstrap给微信做前端,看来还是有道理的,太激进的技术是要代价滴。希望微信也多多加油,跟上日新月异的前端发展,不要成为移动时代的IE6哇。

60行的fly flappy bird

fly flappy bird现在是一点都不新鲜了,当初热遍全球的时候,还是有好多人在仿制,各种语言都有……除了对技术的兴趣,还有的应该就是“看,我也有水平做出一个火遍世界的游戏,只是没动手罢了”这点小心思吧,(●’◡’●)

记得曾经在网上看过js版本的,用的库也各式各样,按说我这里有很多pygame的教程,用pygame写一个是正统,诚然也不难,但是我现在越来越觉得pygame过于底层,是的,没有精灵,没有动画,没有碰撞,甚至连基本的场景管理也没有,对于理解游戏底层原理挺合适,但是恐怕真的不适合现代化的游戏开发吧。

工作上,我前后端都做,html5和js接触颇多,虽说html5的效率一直被诟病,还还是有很多粉丝对h5的未来充满希望,个人虽然没有那么那么的乐观,但是看到h5能做的事情越来越多,还是很高兴的,尤其是h5开发游戏也慢慢变得实际起来了。

说到h5的游戏引擎,国内最热的应该是cocos2D-html5吧,还有就是原生DOM操作了……但是因为cocos2D用的人太多了,完全没有我们外行插足的余地,所以我选择了一个相对冷门的引擎“Phaser”来完成今天的功课。若问为什么选这个?因为Github上赞数1.2W啊(cocos才2k多的说)!

这次只有60行的版本,并不完整,只有一个游戏中的状态,没有开始和结束,但是只是为了演示,所以就这样吧,要求不能太高。

首先是html的骨架:

其次就是main.js的全部内容了,因为不少注释,看起来挺多,其实有效代码就60行左右。

=======》点击这里试玩 《========

链接一点开,小鸟儿就开始掉落了,请及时按空格拯救它……

啊,我有点控制不住自己又要挖坑的洪荒之力了,要不咱开个Phaser的坑咋样?(其实我也不是那么喜欢挖坑的,真的是有趣的技术太多,自己时间又太少——)

ReactJS小记(3)

我又来标题党了,最近感觉,使用ReactJS都是高手,太基础的东西好像多说也没意思了,今天还是来点(湿嗒嗒的)干货吧!

ES6是JavaScript的未来(其实已经到来了),有了很多新的语法(糖?),给我们编码带来了不少方便,随意还是与时俱进用ES6和Webpack创造新的世界吧。对了顺便说一下,ES2015就是ES6,而ES7应该算ES2016?

ReactJS中Component用ES6的Class应该怎么写呢?

 ES7中更厉害,直接作为属性写就行,不过实在太新了,暂时还是不写了避免混淆。

ES6的Component怎么用Mixin呢?

文档说了,ES6的语法暂时不支持Mixin,不过我们有张良计,虽然麻烦了一点~

 

在Ubuntu 14.04上安装 MySQL 5.7

MySQL是什么不多介绍了,在5.7之后,MySQL很“识时务”地引入了JSON字段,这是要抢MongoDB的饭碗么?不过对我们来说是个大好事,不用手动转换的存放了,而且支持原生的读取查询,世界从此变得好美丽!

Ubuntu14.04因为是LTS版本,各大云主机商都支持这个版本,而且我也习惯使用了这个版本,只不过毕竟是两年前的东西,MySQL 5.7在官方源中自然不会出现,怎么办呢?山人自有妙计,且跟我来!

接下来就不用我教了,选择Server后选5.7,然后Apply就好了,和之前安装一样样的。 configure-mysql-apt-config

可以享用美味的MySQL 5.7了。

顺便安利一下MySQL 5.7中JSON的用法吧~

 JSON_EXTRACT中’$.name’的语法是这样的,“$”就是JOSN本身,“.”后面就是引用,其实和JavaScript也很类似,举个例子:

[3, {“a”: [5, 6], “b”: 10}, [99, 100]]

那么

  • $[0] 就是 3.
  • $[1] 就是 {“a”: [5, 6], “b”: 10}.
  • $[2] 就是 [99, 100].
  • $[3] 就是 NULL .
  • $[1].a 就是 [5, 6].
  • $[1].a[1] 就是 6.
  • $[1].b 就是 10.
  • $[2][0] 就是 99.

在MySQL 5.7.9之后,还可以不用麻烦的JSON_EXTRACT而使用->符号,比如user->’$[1].a’,然后,JSON_EXTRACT出来的值,是可以直接参与where比较的!在加上虚字段,真是黑科技呀……

CSS滑动下划线

搜集翻译了一些下划线的动态效果,感觉可能什么时候会用,这里贴一下~请使用现代浏览器查看效果。

主要就是使用了CSS的:after伪类,再加上一些transition动画效果,设置设置宽度高度,左右距离好像就完事儿了……

下划线从上到下出现

鼠标放上去,你会发现下划线“生长”出来了,不过页面所有的内容也往下走了3像素,这样视觉效果不是很好,你可以将它设置一下绝对定位(就好像下面的那个例子)或者用一个固定高度的容器包裹一下它。

下划线从下到上出现

这个就是使用绝对定位的效果。

下划线从左到右出现

下划线从右到左出现

下划线两边伸展

下划线滑进滑出

下划线反向滑进滑出

博客搬了个房间……

博客没有搬家,只是搬了房间。

前两天主机商联系我,说我的空间占用了大量的服务器资源,需要给我搬个服务器观察观察(羁押候审?),我正觉得空间慢了不少,忙不迭地答应了。

话说我一直安安静静的码一些文字,怎么会做出破坏环境的事情呢?前段时间为了加快访问速度,减轻服务器压力,还安装了缓存插件的说。

不管怎么说,搬就搬咯,然后还换了IP,使用了什么智能解析DNS,花了两天,不知道哪里有问题,还是时不时打不开,不得已我把IP写到Hosts里去了……换IP会不会降权重哇,虽然我也不在意这个事情,(-__-)b

现在访问好像快了一些,新服务器人居环境不错。如果有朋友出现时不时打不开页面的情况,请千万留言告知,我好去找主机商理论。万一连留言都留不了,那么请邮件eyehere艾特sian.com吧。万一页面一直打不开都看不到这篇文字,那……

好像本地的电信DNS还是没有更新,一直打不开,咋整哪~~~

ReactJS小记(2)

上一次我们简单的新建了一个组件,不过这个组件实在太简单了,根本无法把Components的优点表达出来,更主要的是,这个组件完全是静态的,这么一来,我们不如直接写HTML好了呀。

Props属性

props用于定义在新建组件时的属性,在组件的代码中,我们可以使用this.props来获取,光这么说太过抽象,我们来关门放代码……

运行它,我们可以看到原来的world被新建组件时传入的xishui所代替(居然想代替世界好大的胆子:-)。

组件的嵌套

在React中,一旦你创建了一个组件,你就可以把它当作一个标准的html标签用了,什么意思呢?我们上面的组件是用标准的<div>创建的,而事实上,我们完全可以使用另外的组件来创建一个新的组件!

好复杂好复杂,我们创建了四个组件呢,其中CommentBox用到了CommentList和CommentForm,而CommentList又用到了Comment,这样嵌套的使用组件也是完全么有问题的!事实上你也应该学会这么使用。

  • CommentBox – 最高级的组件,展示了整个留言App的界面
  • CommentList – 留言列表组件,调用Comment组件
  • CommentForm – 留言框组件,用于接收用户输入
  • Comment – 留言组件,用于展示一条留言
20151216151714

执行效果的话,就是这个样子

顺便看看Comment中,我们还用到了一个this.props.children,这是一个特别的属性,代表了书写组件标签内部的所有内容。

事件和状态

我们知道了Components初始化的时候传入的参数可以使用this.props来获得,但是这好像并没有多少改善吧……因为传入的数据不是一成不变的啊,Web页面或者说Web的UI就是要发生改变才有趣嘛。

再给大家介绍一位小兄弟this.state,事实上他可能比this.props更为重要,因为State是保持组件现有数据的一个属性,当你使用setState方法设置state属性的时候,React就会触发刷新UI的动作。如果你想在组件初始化的时候设置一下state,记得使用getInitialState。我们还是用一个例子来说明一下这个东西。

CommentBox又变复杂了一点,我们还增加了一个自定义的方法,用来增加count的值,而为了调用这个方法,使用了很常规的按钮,注意这里的onClick大小写要正确,包括<br />也要正确闭合,因为JSX是一种XML,无法适应html宽松的写法,我们要严格一些才行。

上面的效果就是,点一下按钮,comments的数量就会+1,当然改的只是那个数字,留言本身的条数还没有变化。

其实有件事情一开始出现了,我一直忍住没说……所有涉及到Components的属性或者方法的地方,我们都用了{}来包裹,这也是React的规定,如果不这么写,编译器很难判断这个到底是个表达式还是一个字符串。

有了getInitialState,有没有getInitialProps呢?否则如果新建Components的时候没有传入值想用默认的怎么办?你说的没错,React有这么一个东西,不过名字叫getDefaultProps,其实也很好理解,属性是默认的,状态是初始化后才有的。

除了HTML中的一些事件,React还专门为Components加了几个事件,事实上这也是组件生命周期上几个事件,罗列如下:

  • componentWillMount – 在第一次挂载的时候执行。
  • componentDidMount – 在第一次挂载完成后执行,这是一个用来加载远程数据的好地方。
  • shouldComponentUpdate – 每次重新渲染前执行,可以返回一个false值来阻止UI更新。
  • componentWillUnmount – 在挂载取消前执行。

我们再来为我们的程序引入更多的活力,那个CommentForm不是还空空如也么,就在这里开刀:

不得不说,与Angular的双向绑定相比,这个写法还是有点麻烦的……我们为文本框绑定了change事件,每次都捕获用户的输入,然后设置回去;我们同时捕获了表单的提交事件,阻止浏览器的默认提交动作,然后将用户输入的东西打了出来。

再来看看最常用的循环表示一个列表是怎么做的,CommentList里的数据还都是写死的,用一个变量来表示吧:

React中对于数据的循环并没有引入一些自定义的方法,而是纯粹是用了JS原生方法,当然如果你对map不熟悉,使用for循环也是可以的(这样的话{}里可以调用一个函数,这个函数的返回值就是拼接的结果)。

有一个注意点是Comment属性里我们加了一个key,这是React推荐的做法,当提供一组循环的组件时,最好为每一个子组件带上唯一的key值,如果你不这么做,React会给出一个警告。我们这里简单的使用了index来作为key,其实不是一个好方法,最好每个comments里每一个元素都有自己的标识(就好像数据表里的主键),不过这里为了简单,我们暂时这么写。

好了,我们都知道下面要做什么了,这么几个组件得有机的结合起来才行——当我们在CommentForm中输入一点什么的时候,我们希望把输入的东西加到CommentList中去。这里牵涉到组件之间的相互关联,我们看看React是怎么解决这个事情的。注意上方的CommentList将数据直接自己保管了,为了通讯上的方便,我们应该把数据交给最高层的CommentBox来管理;而不同层级间方法的调用,我们其实可以使用props值之间传入方法。为了方便对比纠错,把babel代码全部粘上:

最终效果如下图:

react-comment-box1

好了,现在我们已经学习了React的一些基本概念了,包括JSX,组件,以及一些常见的API。我们最后做了一个本地的Web App(数据都是存放在js变量里的),如果您有一些ajax的基础,可以很容易的把它改写成与服务器同步的应用。

下一次我们来说说React的一些更深层次的东西,大概会是Flux或者React编程思想吧(笑)。