主页

页面渲染传参的方式 - Node实战

动态页面的开发,基本会涉及一个比较关键的问题,那就是传参。后台把不同的参数传递给前台,前台页面根据不同的参数显示不同的页面效果,这叫做渲染。

不同的后台开发环境均有不同的渲染方式,Node开发过程中,传递参数的方式非常简单直观。直接就是渲染模版即可,如下代码:

res.render('error', {
  message: err.message,
  error: err
});

渲染页面模版error,给它传递了2个参数。这样前台页面模版就可以使用这两个参数了。如下error页面代码:

<h1><%= message %></h1>
<h2><%= error.status %></h2>
<pre><%= error.stack %></pre>

当然页面上不仅仅只可以使用render方法传递的那几个参数,如果你使用的是express框架的话,你还可以通过app.locals以及res.locals对象赋值的方式传递其他的参数到页面模版引擎。例如:

router.use(function (req, res, next) {
  res.locals.period = moment().day(5).format('YYYYMMDD');  // 得到本周五的日期
  res.locals.prePeriod = moment().day(5 - 7).format('YYYYMMDD'); // 得到上周五的日期
});

这个路由方法,会将period以及prePeriod两个变量传递到该路由下所有路径的模版引擎上。在该路由下的所有页面模版上均可以使用这两个变量。

但是,存在另外一种情况,页面跳转的时候,参数该什么传递呢?

页面的跳转,例如:res.redirect('/login'),这条语句是把请求跳转到另外一个路径上;或者,res.redirect('back')返回上一页。页面的跳转相当于重新访问一遍目标路径的路由。这种情况下,我们想传递参数,通过redirect方法貌似不可以实现。可能有人会想到使用session对象来作为一个中介进行传参,参数展现后就自动删除该条session,没错,这就是connect-flash模版所实现的功能。

具体可参考:

  1. Express框架之connect-flash详解 - 高山上的鱼 - 博客频道 - CSDN.NET
  2. connect-flash 用法详解 - 云库网

原理和使用方法都已经有了,我就不赘述了,我的使用场景一般都是当进入一个没有权限访问的页面时,跳转到登陆界面所提示信息的展现。好多场景其实是可以通过前台Ajax来取代的,例如:登陆界面的验证等等。

总结

Node Web开发中,后台三种传参渲染页面的方式:

  1. 普通页面渲染使用res.render()方法;
  2. 还有别忘了express提供的app.locals以及res.locals对象;
  3. 针对跳转页面的场景可以尝试使用connect-flash模块

阅读更多

学会使用配置文件 - Node实战

在开发项目中,一些配置文件不便直接在代码中显示出来,通常我们会创建一个配置文件用来保存本地开发过程中的一些配置参数,例如数据库连接参数,session参数等等;有时,我们可能需要创建多个参数文件来对应不同的开发环境。下面简单介绍一下Node项目中参数文件的使用方法。

阅读更多

《上帝不仅掷骰子》演讲稿

今天我要分享的内容是关于量子理论的发展史。《上帝不仅掷骰子》这个题目是取至霍金的《时间简史》一书:

上帝不仅掷骰子,他有时候还会把骰子掷到我们看不到的地方去。

选择这个主题的原因是去年年底我读了一些这方面的内容,感悟颇深,但是这些知识概念在我的脑海里却是一片混沌。一直想梳理一下,然而拖着拖着就过了一年。于是就借学习会这个机会给大家分享一下,同时,也算是自己对这部分知识的一个梳理。

分享完这些内容,可能并不会对你的物理或是数学能力有一丝的帮助,但是至少能拓宽你对这个世界的认知。这也是我希望能达到的一个目标。

概述

常规尺度就是咱们日常生活的环境,苹果会自然从树上掉下来,对着球门,凌空一脚,球会向着球门飞去,也许它并没有朝球门飞去,但是它肯定不会化作一缕青烟而消失在我们的视线里。如果这个球是个量子微粒,那可就没准了。

超越常规尺度之后,牛顿力学就不再适用了,就像牛顿力学并不能解释空间和时间为什么是弯曲的,也不能解释光照射在金属上为什么能打出电子。从宏观的尺度去观察这个世界,解释它的运动规律,你需要去使用相对论;从微观的尺度去观察这个世界,解释它的一些奇怪现象,你需要去使用量子理论。那么有没有可能把他们总结成一个规律,万物都适用的规律或者理论呢?这就是万物理论,不过不好意思,这个理论目前还不存在。

霍金曾在1999年的时候说,他愿意以1:1的赌局,预言万物理论必定在20年内被人类研究出来,这不是只剩下3年了吗,咱们可以拭目以待。不过我悄悄告诉你,霍金大神的打赌貌似从来没有赢过。

1. 引子

1.1 什么是光?

研究物理学,就要学会去发现大自然的规律。在远古时代,天亮了,有了光,能看见东西了;天黑了,没有光了,看不见了;光是希望与美好事物的象征,哪里有光,哪里就有正义。但是,什么是光?

在最早的时候,针对光有两种说法,一是射出说,一是射入说。射出说指的是我们能看到东西是因为我们眼睛射出了光;射入说指的是有光射入我们的眼睛才使我们能看到;这个道理对现在来说显而易见,小学生都知道物体反光才导致我们能看到东西。然而这么简单的道理直到公元1000年左右才被波斯的科学家阿尔哈桑的小孔成像实验所证实。

那么光的本质是什么呢?是什么组成了光?当时有两种说法:微粒说,波动说。

1.2 第一次微波战争

到了17世纪中期,关于光是什么有两大理论:波动说与微粒说。这里要谈到主要是胡克牛顿两位科学家。牛顿不用说,大家肯定都认识,不知道大家对胡克有没有印象?能在咱们的教科书里找到的估计只剩下“胡克定律”了,那个描述弹簧力学特性的定律。但是,当时仅次于牛顿的伟大科学家胡克为何落到如此境地?这就是著名的第一次微波战争导致的。

1672年,一位叫做艾萨克·牛顿的年轻人向皇家学会评议委员会递交了一篇论文,名字叫做《关于光与色的新理论》。这篇论文里描述了光的色散与复合的现象,牛顿把它解释为光是由不同颜色的微粒合成的。胡克当时的成就远高于牛顿,他是评议委员会的一员,看了牛顿的文章,指出光的复合是借鉴了他之前的一篇论文中的观点,并指出文中的微粒说不值一谈,纯属牛顿YY出来的,因为胡克是波动说的绝对支持者。

牛顿何等心高气傲,知道了此事后直接撤回了论文。并赌气不再在此投论文了,转而研究他的力学了。中间偶尔也会和胡克写信争论,并且在万有引力的公式发明上也有争论。

1704年,牛顿才出版了他的辉煌巨著《光学》,这是在胡克死后第二年。在这本划时代的作品中,牛顿详尽地阐述了光的色彩叠合与分散,从粒子的角度解释了薄膜透光,牛顿环以及衍射实验中发现的种种现象。他驳斥了波动理论,质疑如果光如同声波一样,为什么无法绕开障碍物前进。他也对双折射现象进行了研究,提出了许多用波动理论无法解释的问题。而粒子方面的基本困难,牛顿则以他的天才加以解决。他从波动对手那里吸收了许多东西,比如将波的一些有用的概念如振动,周期等引入微粒论,从而很好地解答了牛顿环的难题。在另一方面,牛顿把粒子说和他的力学体系结合在了一起,于是使得这个理论顿时呈现出无与伦比的力量。

当时的牛顿已经是爵士,皇家学会会长。波动说就在牛顿的打压下,几乎销声匿迹。而牛顿对胡克的抹杀更是甚之。在牛顿的权威之下,第一次微波战争以波动说的完败而告终,并在此后足足统治了一个世纪之久。

PS. 胡克也是一位伟大的科学家,他曾帮助波义耳发现波义耳定律,用自己的显微镜发现了植物的细胞,他在地质学方面的工作(尤其是对化石的观测)影响了这个学科整整30年,他发明和制造的仪器(如显微镜、空气唧筒、发条摆轮、轮形气压表等)在当时无与伦比。他所发现的弹性定律是力学最重要的定律之一。

1.3 第二次微波战争

19世纪初,一个叫托马斯杨的天才少年,做了一个足足影响至今的实验《杨氏双缝干涉实验》。很简单的一个实验,点一根蜡烛,前面一个挡板开两条细缝,挡板后面放置一个白屏,就能看到烛光映射在上面的明暗条纹。只要是关于光学的课程,必定会有这个经典的实验。伟大的相对论以及量子理论的诞生都跟它有关系。

然而在当时托马斯杨的实验以及结论发表后,竟多年来无人问津,牛顿体系的地位如此崇高,他提出的光是一种粒子的观念如此深入人心,以致人们都忘了当年对手的存在。然而根据相位差的计算,能准确得出明暗条纹的位置,并且通过距离的调整,甚至能得出光做为一种波它的波长是多少。这是不争的事实,让你不容忽视。

1819年,第二次微波战争终于由法国年轻的工程师-菲涅耳打响的,他的一篇论文采用光的波动学说观点,并通过缜密的数学推理完美解释了光的衍射问题(注意:这原本属于微粒学说的领地,因为牛顿当时指出如果光是一种波,为什么不能像声波一样绕开障碍物)。

随着科学的发展,X射线,Y射线都陆续发现并被证实他们都是一种电磁波,光也是一种电子波。最后麦克斯韦的电磁理论的出世让微粒学说几乎销声匿迹。第二次微波战争以波动说的完胜而告终,但是光真是就是一种电磁波了吗?

PS. 麦克斯韦电磁理论是牛顿力学之后又一座「大厦」,他用他那优美的4个数学公式解释了世间一切电磁波的运动规律。当时的人们能认识到的宇宙中的自然力有:万有引力,电磁力。这两大力几乎能解释世间万物的运动规律。

2. 诞生

2.1 量子的诞生

1900年12月14日,普朗克在柏林宣读了他关于黑体辐射的论文,宣告了量子的诞生。那一年他42岁。他的这篇论文提出的观念比黑体辐射本身都具有重大意义。黑体辐射指的是只要是物体的温度在绝对零度以上(注意是绝对零度,不是摄氏零度)都在对外发送辐射,并且吸收外界的辐射。而且,辐射的能量并不是连续的,而是一份一份的传输的。每一份能量简称为“能量子”,最小的单位就是普朗克常数h。

说到这,也许你并不觉得有什么惊讶的?但是如果我说,你烧水的时候,温度从20摄氏度到90摄氏度,这不是一个连续的过程,而是跳跃的;你从A地点缓缓走到B地点,不是连续的,而是一闪一闪过来,你会怎么想?

普朗克通过研究黑体辐射提出的这个观念,他自己本身都难以相信,麦克斯韦的电磁理论如此完美,波就应该是连续的,而不是离散的。如果世界正如他所言,首先就要推倒麦克斯韦电磁理论的「大厦」。传统而纠结的普朗克并没有勇气接着往下研究下去,故而他对量子理论的贡献就止于此了。

下面就来看看量子理论的奠基人,这一年,爱因斯坦刚刚从大学毕业,正在为未来的生计发愁,由于大学不好好上课,导致挂科过多,本想留校做研究,却被导师给赶了出来。15岁的玻尔还在上高中,比他小两岁的薛定谔也在上高中,才8个月大的泡利还在妈妈的怀中,再加上还要过12月才出生的海森堡,这样就构成了量子理论发展道路上的「主角」。

2.2 第三次微波战争

量子诞生的前几年,因为被自己的主人所抛弃,并没有受到重视。下面第一位主角登场,1905年3月,爱因斯坦发表了一篇论文,《关于光的产生和转化的一个启发性观点》。这篇论文解决了困扰物理学界多年的一个问题,那就是为什么光会在金属上“打出”电子来——也就是咱们熟知的光电效应。爱因斯坦认为光是由叫做“光量子”的微粒组成,它照射到金属上,撞击电子从而使电子脱离金属而蹦出。撞击出电子的动能等于光的能量减去激发金属的最低能量。

电磁理论认为,光作为一种波动,它的强度代表了它的能量,增强光的强度应该能够打击出更高能量的电子。但实验表明,增加光的强度只能打击出更多数量的电子,而不能增加打击电子的能量。要打击出更高能量的电子,则必须提高照射光线的频率,比如紫光就比红光能量要高,紫外线又比紫光能力更高。

这个实验的重要发现表明其实光并不是一种电磁波,而是由“光量子”组成的。这个结论又挑起了著名的微波战争,这时距离杨氏双缝干涉实验又过了一个世纪之久。

后来康普顿效应的实验再次论证了爱因斯坦的这个结论,康普顿效应指的是用光对X射线进行散射,散射出来的X射线分成两个部分,一部分和原来的入射射线波长相同,而另一部分却比原来的射线波长要长,具体的大小和散射角存在着函数关系;康普顿起初尝试使用麦克斯韦电磁理论来做这一现象的解释,然而并没有任何的结果。当他引入爱因斯坦的“光量子”假设,瞬间就能解释通了,并且通过数学计算,推导出波长变化和散射角的关系式,和实验符合得一丝不苟;

因为光照射到X射线上后,光量子与X射线发生了碰撞,导致撞击到的X射线失去了部分动能,能量减小,频率降低,波长自然就长了。

这一次光的微粒学说再次回到了历史的舞台,可是光到底是个什么东西?又是微粒,又表现出波的特性,这一次双方再次势均力敌。

2.3 原子模型

光的本质问题暂时先放一段落,因为科学家们根据量子性推断出更可怕的结果。咱们接着往下说,大家都知道,整个世界都是由一种叫做“原子”的小球构成的,20世纪初,卢瑟福通过α粒子散射的实验,告诉我们原子可以分解为原子核电子

α粒子散射实验是用α粒子去撞击铝箔,他发现有时候α粒子会反弹回来,并且角度非常大,他推断α粒子被反弹回来,必定是因为它们和铝箔原子中某种极为坚硬密实的核心发生了碰撞。这个核心应该是带正电,集中了原子的大部分质量,并且核心所占据的地方非常小,不到原子半径的万分之一。

随后卢瑟福便提出了如图所示的原子行星模型,他认为电子就像宇宙中的行星一样围绕着自己的恒星运转,原子核便是其中的恒星。但是这个模型有个致命的要害,那就是-这个体系是不稳定的,两者之间会放射出强烈的电磁辐射,从而导致电子一点点地失去自己的能量。作为代价,它便不得不逐渐缩小运行半径,直到最终“坠毁”在原子核上为止,整个过程用时不过一眨眼的工夫。也就是说如果整个世界是由这种原子模型构成,那么瞬间将会毁于一旦。

这个困难一直无法解决,那么就到了另外一个天才少年登场的时候了,玻尔是卢瑟福的学生,他并没有因为这个困难而放弃这个理论的研究。他放弃了麦克斯韦电磁理论,而结合量子非连续的特性,指出:电子在围绕原子核运转时,只能处于一些“特定的”能量状态中。这些能量状态是不连续的,称为定态。

当放射能量的时候,电子会从高能量态降到低能量态;当吸收能量的时候,电子会从低能量态跃迁到高能量态,辐射的能量正好符合普朗克能量公式。

后来人们很快发现,一个原子的化学性质,主要取决于它最外层的电子数量,并由此表现出有规律的周期性来。但是人们也曾经十分疑惑,那就是对于拥有众多电子的重元素来说,为什么它的一些电子能够长期地占据外层的电子轨道,而不会失去能量落到靠近原子核的低层轨道上去。

好了,该另外一位主角泡利登场了,他进一步完善了玻尔的原子模型,他解释道:没有两个电子能够享有同样的状态,而一层轨道所能够包容的不同状态,其数目是有限的,也就是说,一个轨道有着一定的容量。当电子填满了一个轨道后,其他电子便无法再加入到这个轨道中来。

什么意思?通俗的举个例子,这里有座大厦,叫做“钠原子大厦”,一楼有2个房间,二楼有8个房间,三楼也有8个房间。泡利这个大厦管理员规定,每个房间只能住一个电子,而且必须从一楼开始住,也就是说一楼住满了,才能往二楼去。这天来了11个电子,于是一楼二楼住满了,三楼住了一个。隔壁是“氯原子大厦”,同样也是泡利他们家的,住了17个电子,一楼二楼住满了,三楼8个房间住了7个,还有一个空着。电子喜欢群居,于是“钠原子大厦”三楼那个电子直接搬到了“氯原子大厦”上,组成了“食盐社区”。

从量子理论的探索,衍生出了整个化学学科,足见量子理论的威力。玻尔和泡利的这场革命是一次不彻底的革命,量子的假设没有在他的体系里得到根本的地位,而似乎只是一个调和经典理论和现实矛盾的附庸。玻尔理论没法解释,为什么电子有着离散的能级和量子化的行为,它只知其然,而不知其所以然。每个物理现象的发现而导出的物理理论,必须都要有严格的数学来验证,数学是凌驾于物理之上的客观事实。

2.4 两大力学体系的争斗

当时整个物理学界貌似在等待一场更彻底的革命,能够直接推翻经典理论。

电子居然是个波,一个叫德布罗意的人说到。他通过爱因斯坦的质能方程以及普朗克能量公式推出:

他指出每一个电子的运动都伴随着一个波,这个理论一提出来,瞬间又炸开了锅,现在不再是光是微粒还是波的问题了,这个问题直接上升到了整个宇宙是粒子还是波的问题了。电子,质子,中子组成了原子,原子组成了这个世界,现在电子是个波,是不是就是说其实你我都是一缕一缕的波组成的。

另外两大主角儿是时候该上场了,海森堡VS薛定谔矩阵力学VS波动力学,他们都是通过严格的数学来验证电子的运动规律。只要是自然的物理规律都可以用数学的方式表达出来,比如牛顿的万有引力公式,麦克斯韦方程组,爱因斯坦的质能方程以及相对论,都是通过简单而优美的数学形式表达了出来。

海森堡从电子在原子中的运动出发,先建立起基本的运动模型来。事实证明他这条路走对了,新的量子力学很快就要被建立起来,但那却是一种人们闻所未闻,之前连想都不敢想象的形式——矩阵。一个电子的动能以及势能就能代表它的运动规律,用p表示电子的动量,q表示电子的位置,通过矩阵的运算,正好可以解释玻尔模型中电子离散的能力以及量子化的运动规律。

薛定谔从另外的一个角度出发,通过经典力学公式(哈密顿方程)以及德布罗意关系式和变分法,构造一个体系的新函数ψ,(此处省略一万字)最后得出著名的薛定谔方程。

换句话说就是:海森堡从离散的角度出发,导出矩阵力学;薛定谔从连续的角度出发导出了波动力学。此后就是两大力学的争斗,海森堡的矩阵力学是从电子的运动出发建立起来的体系,然而晦涩难懂的矩阵计算并不被大家所认可;然而薛定谔的波动方程如此优美,反倒更容易被大家所接受,但是他所引入的ψ是个什么物理意义,连他自己也无法解释清楚。有的人甚至会说:“薛定谔方程比薛定谔他本人更加聪明”。

PS. 通常我们会以为,先有物理量的定义,然后才谈得上寻找它们的数学关系。比如我们懂得了力F,加速度a和质量m的概念,之后才会理解F=ma的意义。但现代物理学的路子往往可能是相反的,比如物理学家很可能会先定义某个函数F,让F=ma,然后才去寻找F的物理意义,发现它原来是力的量度。薛定谔的ψ,就是在空间中定义的某种分布函数,只是人们还不知道它的物理意义是什么。

3. 核心

3.1 概率解释

1926年7月,玻恩将骰子带进物理学后,在物理界引起了轩然大波。玻恩在尝试解释薛定谔方程中ψ的时候,指出:骰子,这才是薛定谔波函数ψ的解释,它代表的是一种随机,一种概率。ψ,或者更准确一点,ψ的平方,代表了电子在某个地点出现的“概率”。电子本身不会像波那样扩展开去,但是它的出现概率则像一个波,严格地按照ψ的分布所展开。

咱们回想一下杨氏双缝干涉实验的情境,光通过双缝后显示出明暗条纹,如果一个光子通过的时候,它会怎么样呢?经过大量的实验观察,它是随机的,概率性的。也就是说不管你怎么测量,怎么精准的控制这个光子,它表现出来的都是随机性,概率性。

玻恩的概率解释一出来,瞬间点燃了物理学界,你居然说整个世界只是一种概率,我们无法得到电子的运动规律,难道上帝真的掷骰子,掷到几点就是几点?

物理决定论: 物理学不仅能够解释过去和现在,它还能预言未来。我们的定律和方程能够毫不含糊地预测一颗炮弹的轨迹以及它降落的地点;我们能预言几千年后的日食,时刻准确到秒;给我一张电路图,多复杂都行,我能够说出它将做些什么;我们制造的机器乖乖地按照我们预先制定好的计划运行。事实上,对于任何一个系统,只要给我足够的初始信息,赋予我足够的运算能力,我能够推算出这个体系的一切历史,从它最初怎样开始运行,一直到它在遥远的未来的命运,一切都不是秘密。是的,一切系统,哪怕骰子也一样。告诉我骰子的大小,质量,质地,初速度,高度,角度,空气阻力,桌子的质地,摩擦系数,告诉我一切所需要的情报,那么,只要我拥有足够的运算能力,我可以毫不迟疑地预先告诉你,这个骰子将会掷出几点来。

物理学统治整个宇宙,它的过去和未来,一切都尽在掌握。这已经成了物理学家心中深深的信仰。19世纪初,法国的大科学家拉普拉斯(Pierre Simon de Laplace)在用牛顿方程计算出了行星轨道后,把它展示给拿破仑看。拿破仑问道:“在你的理论中,上帝在哪儿呢?”拉普拉斯平静地回答:“陛下,我的理论不需要这个假设。”

现在来做一个思维实验,A是波动,B是微粒。

  • A说:“假如光是微粒,那么前面两条缝,它仅能通过一条缝,不可能同时通过两条缝吧?”
  • B说:“没错,只能通过一条缝,并且打在电子屏上,显示出一个亮点。”
  • A说:“照你这么说,它是怎么根据干涉模式的概率来显示呢?比如它怎么知道打在明条纹的概率是50%呢?并且这个概率是与两条缝之间的距离密切相关的呀,它怎么知道两缝之间的距离而得出应该出现的概率呢?”
  • B说:“我承认,它在运动的时候,会伴随着某种类波的东西,能够探知双缝之间的距离而做出概率反应,但是实体必须只能通过一条缝。”
  • A反驳说:“一点道理也没有,那假设那个光子在通过一条缝的瞬间,有人关掉了另一条缝,那么它是怎么瞬间能知道由干涉的概率模式转换成普通的模式,而只是打出一条亮纹?你要知道,电子是小得不能再小的粒子,双缝的距离针对它来说是非常遥远的,除非它能收到瞬时的信号,难道你想反对相对论吗?”
  • B不服气的说:“我倒想听听你的解释”
  • A得意的说:“很简单,光子就是一个波,它同时穿过了两条缝,从而产生完美的干涉,就想水波和声波一样,如果你关掉了一条缝,那么就等于关闭了波的一条路径,从而也就谈不上光的干涉了”
  • B说:“听起来不错,然后呢?击中感应屏前发生了什么?”
  • A有些语塞的解释道:“由于某种原因,在击中感应屏前,波坍缩成了一个点。”
  • B得意的说:“哈,真奇妙哈,在感应屏前,波动家族全体罢工,变成了微粒?”
  • A面红耳赤的争辩道:“这个还处于我们尚且不知道的原因。”

3.2 不确定性原理

玻恩以及他最尊重的老师玻尔都开始偏向于薛定谔的波动力学,尝试去解释薛定谔方程中的波函数的意义。海森堡当然非常不爽也很不服气,他仍然沉浸在他的矩阵力学中,只是有个地方怎么也想不明白,那就是为什么矩阵不符合乘法交换律呢?假设一个电子的动量是P,位置是q,一个代表了动能,一个代表了势能,这两者基本就代表了电子的运动规律。可是:

这个代表的物理意义是什么呢?他起初怎么也想不明白,然而突然有一天豁然开朗,这就是灵感。他想到pq是不是就代表了先测量p再测量q,而qp是不是就代表了先测量q,再测量p。也就是说测量的顺序不一样,得到的结果不一样。

随着这个想法深入下去,(此处省去一万字),他指出:动量p和位置q,它们是“不共戴天”的。只要一个量出现在宇宙中,另一个就神秘地消失。要么,两个都以一种模糊不清的面目出现。

后来(此处再省略一万字),又发现另一对类似的「仇敌」,它们是能量E和时间t。只要能量E测量得越准确,时刻t就愈加模糊;反过来,时间t测量得愈准确,能量E就开始大规模地起伏不定。

海森堡称之为:“测不准原理”,后来被称为“海森堡不确定性原理”。

3.3 互补原理

在海森堡发现这个后,就赶紧把他的发现告诉了他的老师玻尔,想继续得到老师的肯定,认可他的矩阵力学。玻尔之前一直在研究薛定谔方程里的波分布函数ψ以及玻恩提到的概率解释,在收到海森堡的不确定性原理的理论后,心中就有了定论,他随即发表了关于互补原理的论文。

他指出:任何时候我们观察电子,它当然只能表现出一种属性,要么是粒子要么是波。声称看到粒子-波混合叠加的人要么是老花眼,要么是纯粹在胡说八道。但是,作为电子这个整体概念来说,它却表现出一种波-粒的二象性来,它可以展现出粒子的一面,也可以展现出波的一面,这完全取决于我们如何去观察它。

第三次微波战争便以这样一种戏剧化的方式收场。而量子世界的这种奇妙结合,就是大名鼎鼎的“波粒二象性”。

3.4 物理?哲学?

我们注意一下上面玻尔提到的最后一句话,这完全取决于我们如何去观察它,我们看它是波,世界就是波;我们看它是粒子,世界就是粒子。怎么听起来,好像在讲哲学,世界是什么,取决于我们怎么看?唯物理论告诉我们世界是客观存在的呀,怎么会取决于我们如何去观察呢?

上面的思维实验中,如果我们在粒子穿过缝的时机观察它,那么它就表现出来是微粒,只能随机的选择一个缝通过,如果我们不观察他,那么粒子就按照干涩模型的概率穿过双缝,表现出波的特性。在穿过之后,如果我们又要观察它(使用感应屏),那么它就瞬间坍缩成一个粒子表现出来而打到感应屏上。也就是玻尔说的:“任何时候我们观察电子,它当然只能表现出一种属性,要么是粒子要么是波。”

意识使波函数坍缩,没错,到最后的最后,物理学竟然演变成形而上学的唯心主义,电子的运动,甚至说整个世界都是我们的意识决定的,我们看到的是什么,那就是什么,没有客观存在的事物。白马非马,关键取决于你怎么去观察。

这个结论当然不是我随便说的,这就是当时最权威的针对量子理论的解释,也可以说就是量子理论的核心。上面提到的“概率解释”、“不确定性原理”以及“互补原理”就是著名的哥本哈根解释

4. 争论

4.1 论战

这个理论是起止为今最权威的量子理论解释,它是以玻尔为首的“哥本哈根解释”。当然挑战他们的人不在少数,但是都败下阵下。最著名的论战莫过于爱因斯坦以及薛定谔的挑战。爱因斯坦发现了光子,同样也是量子理论的奠基人,但是对于说整个世界不过是“上帝掷骰子”的一个随机事件,这个无论如何也不能让爱因斯坦接受。在爱因斯坦的心中,物理学的决定论是不容置疑的。

爱因斯坦与玻尔,针对量子理论的解释的论战不在少数,这里列举一个最经典的EPR思维实验的论战。下面用最简单的描述来讲解一下这个著名的EPR佯谬

爱因斯坦说:现在我们想象有一个大粒子,它是不稳定的,会衰变成两个小粒子向相反的方向飞去。我们假设这种粒子有两种可能的自旋,分别叫“左”和“右”,那么如果粒子A的自旋为“左”,粒子B的自旋便一定是“右”,以保持总体守恒,反之亦然。好,现在大粒子分裂了,两个小粒子相对飞了出去。但是要记住,在我们没有观察其中任何一个之前,它们的状态都是不确定的,只有一个波函数可以描绘它们。只要我们不去探测,每个粒子的自旋便都处在一种左/右可能性叠加的混合状态。那么现在我们要观察A了,于是它的波函数瞬间坍缩,随机选择了一个状态,假如说是左旋,那么另一个粒子B肯定就是“右”旋了。问题是,在这之前,粒子A和粒子B之间可能已经相隔非常遥远的距离,比如说几万光年好了。它们怎么能够做到及时地互相通信,使得在粒子A坍缩成左的一刹那,粒子B毅然坍缩成右呢?量子论的概率解释告诉我们,粒子A选择“左”,那是一个完全随机的决定,两个粒子并没有事先商量好,说粒子A一定会选择左。事实上,这种选择是它被观测的那一刹那才做出的,并没有先兆。关键在于,当A随机地作出一个选择时,远在天边的B便一定要根据它的决定而作出相应的坍缩,变成与A不同的状态以保持总体守恒。那么,B是如何得知这一遥远的信息的呢?难道有超过光速的信号来回于它们之间?

爱因斯坦等人认为,既然不可能有超过光速的信号传播,那么说粒子A和B在观测前是“不确定的”显然是难以自圆其说的。唯一的可能是两个粒子从分离的一刹那开始,其状态已经确定了,后来人们的观测只不过是得到了这种状态的信息而已,就像经典世界中所描绘的那样。粒子在观测时才变成真实的说法显然违背了相对论的原理,它其中涉及到瞬间传播的信号。

玻尔收到爱因斯坦这个思维实验的时候,也是大吃一惊,然而由于他与爱因斯坦针对这个问题已经对战了多次,随即便明白了爱因斯坦的这个思维实验的要点在哪里?

玻尔认为,当没有观测的时候,不存在一个客观独立的世界。所谓“实在”只有和观测手段连起来讲才有意义。在观测之前,并没有“两个粒子”,而只有“一个粒子”,直到我们观测了A或者B,两个粒子才变成真实,变成客观独立的存在。但在那以前,它们仍然是互相联系的一个虚无整体。并不存在什么超光速的信号,两个遥远的粒子只有到观测的时候才同时出现在宇宙中,它们本是协调的一体,之间无需传递什么信号。

这就是著名的EPR佯谬

站在爱因斯坦这边的薛定谔也提出了一个著名的思维实验,被人称为薛定谔的猫,他说,根据哥本哈根的解释,没有测量之前,一个粒子的状态是模糊不定的,处于各种可能性的混合叠加,如果一个放射性原子,那么它何时衰变完全是概率性的。只要没有观察,它便处于衰变/不衰变的叠加状态中,只有测量了,它才随机选择一种状态而出现。

假设我们把这个原子放在一个不透明的箱子中,设计一个结构巧妙的精密装置,如果原子衰变那么就会激发一连串的连锁反应而打破箱子里的一个毒气瓶,同时箱子里还有一只可怜的猫。事实很明显:如果衰变了,猫就被毒死了;如果没有衰变,猫就好好活着。

自然的推论就是:原子处于一种衰变/不衰变的叠加状态,而处于同一体系里的猫也就是同样处于死/活的叠加状态,也就是说如果我们没有观察这只猫,这只猫就是处于不死不活的状态。

薛定谔的实验把量子效应放大到了我们的日常世界,这个实验虽然简单,但是却也让哥本哈根够头疼的了,“是的,当我们没有观察的时候,那只猫的确是又死又活的状态”。

这个实验最终又会回到“意识”的问题上,记得上面提到过“意识使波函数坍缩”,从量子理论的角度,当我们去观察的时候,事物是定态的,而如果我们没有去观察,那么事物都是一种叠加的状态。

可能你认为这些争论其实毫无意义,但是你不可否认,正是由于在这些争论的引导下,科学才显得如此朝气蓬勃,它的各个分支以火箭般的速度发展,给人类社会带来了伟大的技术革命。从半导体到核能,从激光到电子显微镜,从集成电路到分子生物学,量子论把它的光辉播撒到人类社会的每一个角落,成为有史以来在实用中最成功的物理理论。

4.2 上帝不仅掷骰子

在哲学基础上的不同观念使得玻尔派爱因斯坦派之间的意见分歧,然而直到爱因斯坦死了,玻尔也没能让他信服,认为量子论的解释是完备的。

随着科学技术的发展,特别是激光技术的发展,当时爱因斯坦提出的EPR的思维实验终于可以搬到实验室中,当然这一切都是都要归功于一个叫贝尔的数学天才,他找到一种方法来测量电子的自旋方向。也就是著名的“贝尔不等式”,由于前方数学“高能”,略去一万字,公式如下:

这个公式可以简单解释为:如果世界的本质是经典的,也就是爱因斯坦所信仰的“上帝不掷骰子”,那么取三个方向观测A和B粒子的自旋,最后结果必定会满足“贝尔不等式”;

实验很快就被设计出来了,如下图

贝尔本来是站在爱因斯坦这边的,然而最后的结果却出乎他的意料-爱因斯坦错了,随着不断的实验,不同的粒子,粒子分离的距离越远,爱因斯坦就错得越离谱。

也就有了霍金所说:“爱因斯坦犯了双重错误,量子力学显示,上帝不仅掷骰子,他有时候还会把骰子掷到我们看不到的地方去。”

5. 总结

5.1 新探索

针对量子怪异的表现,除了最权威的“哥本哈根解释”,当时也出了很多种解释。比如多世界解释、退相干历史、GRW、隐变量…,其中比较火的一个解释就是多世界解释,很多科幻电影都有这个桥段。它认为,薛定谔的猫并不是处于一种不死不活的状态,而是存在多个世界,每个世界里只是一个状态,而我们本身也存在于多个世界,每个世界里都有一个你我。

说着说着,越来越科幻了。其他的解释不多说了,有兴趣可以自行搜索了解。

5.2 应用

量子理论的研究引申出的学科都不少,不仅有物理方面,还有化学,生物学都跟它有关系。应用实在是太庞大了,这里主要讲解近年来特别火的两个应用。一个是量子计算机,一个是量子传输。

计算机最小组成单元是“门”,“与门”,“或门”,“非门”,传统计算机使用半导体电路,使用高低电压来表示0或者1;量子计算机可以通过电子的左旋或者右旋来表示,根据量子理论,每一个电子都是0和1的叠加状态。

  • 传统计算机:1bit要么是0,要么是1。读入10bit信息,相当于处理了10个二进制数。
  • 量子计算机:一个bit不仅只有0或者1的可能性,它更可以表示一个0和1的叠加!一个“比特”可以同时记录0和1,我们叫做量子比特。假如我们的量子计算机读入了一个10bits的信息,所得到的就不仅仅是一个10位的二进制数了,事实上,因为每个bit都处在0和1的叠加态,我们的计算机所处理的是2的十次方个10位数的叠加!

另一个比较火的应用就是量子传输,它是从爱因斯坦的EPR佯谬的实验中的一个启发,两个分离的粒子如何能感知对方的状态呢?因为没有观察之前,他们就是一个整体,当一个粒子被观察而确定后,另一个粒子瞬间会得到信息。把这个衍生开来,这就是量子传输,这两个粒子就组成了量子纠缠态。

原理就是这样,但是是否有实际应用,或者说,实际应用到了什么程度不得而知。当然这个并不是所谓的“超光速传输”,但凡看到这等字眼,肯定是某个科盲记者胡乱报道的文章。

5.3 宇宙是一场交响乐

说到这里,宇宙到底是什么呢?依然还是没有定论,原子可以分解为原子核和电子,原子核又可以分为质子和中子,那么质子中子电子又是什么组成的呢?

随着现代量子物理的发展,质子中子又可以分解为夸克,然后又是各种“子”被发现,如费米子、玻色子等,没有人知道分解到最后,得到的最终是什么。

目前最火的一个理论叫“超弦理论”,它认为万物皆空,唯有音乐。我们这个宇宙是一个十维的宇宙,但是有六个维度紧紧蜷缩了起来。就像远远地看一根吸管,它细得就像一条一维的线,但是当我们凑近一看,发现它其实是一根三维的管,其中的二维卷起来了。那六个维度的空间收缩得如此之紧,以至于你必须要放大一亿亿亿亿(1后面34个零)多倍才能发现。其实所有的粒子都不是一个点,而是一个六维的“橡皮筋圈”,不停地在空间振动,发出曼妙的音乐。

菩提本无树, 明镜亦非台。 本来无一物, 何处惹尘埃。

最后的最后给大家推荐一些书籍,《上帝掷骰子吗?》《时间的形状》《时间简史》《黑洞与时间弯曲》《宇宙的琴弦》《宇宙的结构》《时间之箭》《上帝与新物理学》《物理学的困惑》。

本文大部分内容摘自《上帝掷骰子吗?》《时间的形状》两本书。

(完)

阅读更多

Node连接mysql数据库方法

使用Node做Web开发,基本上都是使用NoSQL数据库,最频繁的就是使用MongoDB了,自己做了一些简单的Web开发,为了降低学习门槛,一直使用MySQL来做数据库。这里简单介绍一下连接MySQL数据库的方式,希望能帮助到其他人。

npm install --save mysql

使用上述命令安装完MySQL的模块后,就可以直接使用了,官网的DOCS里一个简单的例子如下就可以入门了。

var mysql = require('mysql');
var connection = mysql.createConnection({
  host: 'localhost',
  user: 'me',
  password : 'secret',
  database : 'my_db'
});
connection.connect();
connection.query('SELECT 1 + 1 AS solution', function(err, rows, fields) {
  if (err) throw err;
  console.log('The solution is: ', rows[0].solution);
});
connection.end();

很简单的一个例子,从上面的例子可以得出:使用createConnection(option)方法创建一个连接对象,然后连接对象的connect()方法创建连接,最后使用query()方法执行SQL语句,返回结果作为回调函数的参数rows返回,rows为数组类型。

1. 连接

创建连接对象,需要传入连接数据库的一些连接参数,也就是createConnection(option)里的optionoption是一个对象,以键值对的形式传入createConnection()方法里。上例列举出了最基本的参数:

  • host 主机名
  • user 连接数据库的用户
  • password 密码
  • database 数据库名称

还有其他的参数,可以查询下官方DOCS,这里不一一列举了,初期学习上面这些参数就足以。

2. 关闭

关闭一个连接使用end()方法,end()方法提供一个回调函数,如下:

connect.end(function(err){
    console.log('End a connection');
});

这是建议使用的方法,end()方法会等待连接回调完成后才关闭连接。官方还提供了另外一种方法destroy()方法,这个方法直接关闭连接,不会等待回调完成。

举个简单的例子:

var mysql = require('mysql');
var option = require('./connect.js').option;
var conn = mysql.createConnection(option);
conn.query('select * from message',function(err,rows,fields){
  if(!err){
    console.log(rows);
  }
});
conn.end(function(err){
  console.log('end a connection');
});

最终结果会是:先打印完SELECT数据表结果后,再打印end a connection。而如果你将关闭方法换成conn.destroy();,那么你就别想返回任何结果了,因为还没等回调结束就已经终止连接了。

3. 连接池

连接池的原理是一开始就给你创建多个连接对象放在一个“池子”里,用的时候取一个,用完了放回“池子”里,在一定程度上是有利于节省系统开销的,因为连接对象是在最开始的时候就创建好了,使用的时候不再需要系统开销去创建数据库连接对象。官方DOCS介绍了连接方法:

var mysql = require('mysql');
var pool  = mysql.createPool({
  connectionLimit : 10,
  host            : 'example.org',
  user            : 'bob',
  password        : 'secret',
  database        : 'my_db'
});
pool.query('SELECT 1 + 1 AS solution', function(err, rows, fields) {
  if (err) throw err;
  console.log('The solution is: ', rows[0].solution);
});

创建连接池的方法是createPool(option)option里多了一个参数connectionLimit指的是一次性在连接池里创建多少个连接对象,默认10个。如果你想共享一个连接对象,可以使用下面方法进行连接;

var mysql = require('mysql');
var pool  = mysql.createPool({
  host     : 'example.org',
  user     : 'bob',
  password : 'secret',
  database : 'my_db'
});
pool.getConnection(function(err, connection) {
  // Use the connection
  connection.query( 'SELECT something FROM sometable', function(err, rows) {
    // And done with the connection.
    connection.release();
    // Don't use the connection here, it has been returned to the pool.
  });

// Use the connection
  connection.query( 'SELECT something2 FROM sometable2', function(err, rows) {
    // And done with the connection.
    connection.release();
    // Don't use the connection here, it has been returned to the pool.
  });
});

使用一个连接对象执行两次query()函数。

4. 示例1

使用基本的连接方式来连接数据库,分别定义数据连接以及关闭的function,如下示例:

// connect.js 数据库连接与关闭
var mysql = require('mysql');
var config = require('./config.json'); // 将数据库连接参数写入mysql对象,即config.mysql
var connCount = 0; // 统计目前未关闭的连接
exports.getConn = function(){
  connCount ++;
  console.log('............................OPEN a connection, has '+ connCount + ' connection.');
  return mysql.createConnection(config.mysql);
};
exports.endConn = function(conn){
  conn.end(function(err){
    if(!err){
      connCount --;
      console.log('.........................CLOSE a connection, has '+ connCount + ' connection.');
    }
  });
};

然后给个使用数据库的示例,

// db.js 查询用户信息
var connect = require('./connect.js'); // 引入数据连接方法
exports.getUser = function(username, callback){
    var connection = connect.getConn();
    var sql = 'select * from user where username = "' + username + '"';
    connection.query(sql,function(err,rows,fields){
        callback(err,rows,fields);    
    });
    connect.endConn(connection);
}

5. 示例2

使用数据库连接池,同样先创建数据库连接池的方法,如下两种方式:

// connect.js 直接使用
var mysql = require('mysql');
var config = require('./config.json');
var pool = mysql.createPool(config.mysql);

exports.querySQL = function(sql,callback){
    pool.query(sql, function(err,rows,fields){
        callback(err,rows,fields);
    });
}
// connect.js 使用getConnection方法
var mysql = require('mysql');
var config = require('./config.json');
var pool = mysql.createPool(config.mysql);

exports.querySQL = function(sql, callback){
    pool.getConnection(function(err,conn){
        conn.query(sql,function(err,rows,fields){
            callback(err,rows,fields); 
            conn.release();   // 不要忘了释放
        });        
    });
}

使用的时候,直接使用querySQL方法即可,如下:

// db.js 查询用户信息
var connect = require('./connect.js');
exports.getUser = function(username,callback){
    var sql = 'select * from user where username = "' + username + '"';
    connect.querySQL(sql,function(err,rows,fields){
        callback(err,rows,fields);        
    });
};

官方是推荐使用连接池的方式进行连接的,但是,是直接使用pool.query()连接还是pool.getConnection()的方法来连接,官方并没有介绍其优劣,我简单做了个测试,貌似这两种方式并没有多大的区别,也就没再研究,有知道的烦请告知,谢了~

阅读更多

Diagram Designer - 小巧免费的流程图绘制软件

这里推荐一款小巧的流程图绘制软件,遗憾的是只是针对Windows平台。

关于流程图软件,各个平台都不在少数,比较有名的有visio以及Mac平台上的OmniGraffle,功能强大,界面漂亮,当然费用也不小。如果是各种各样的流程图重度用户,购买一个还算不错。互联网发展如此迅速,基于Web页面的流程图工具也有很多,比如:

这里只是列举几个相对而言较出名的,当然还有好多开源的流程图工具,这里就不一一列举了。

而我要推荐的并不是这些,而是Diagram Designer - 小巧免费的流程图绘制软件,公司办公使用Windows7系统,国有企业,电脑配置你懂的。偶尔画个简单的流程图梳理一下思路,想找个简单的工具,要求就3点:免费、离线、快。神马功能全不全,好看不好看并不是特别重要,重点是要简单并且启动快,偶尔一个思路需要画个简单的流程图梳理一下,确实没有勇气去打开Visio,就怕等打开了,思路也没了。

Diagram Designer轻量小巧,打开快速,虽然功能较简单,但是基本还是能满足简单的流程图需求的。下面这个简单的『重置密码』流程图就是通过它画的,其实看起来还不错,不是吗/cy

阅读更多

Node项目之需求收集平台(三)- 使用cookie实现点赞功能

又是一个临时 YY 出来想要添加的功能,需求收集平台旨在收集用户的需求,然后给出基本的答复以及更新需求状态,但是针对那些重复的需求,也就没有必要要求不同的用户重复的去提交,但是又为了让收集者知道哪些需求是用户频繁提出的,这样就要求有个类似于点赞的功能,如果看到相同的需求,不需要重新添加一条需求,只需要在该需求上点个赞即可。

首先从用户的角度简单分析一下这个功能:

  • 需要给每个需求条目添加点赞按钮来触发点赞动作
  • 点过赞的条目与没有点过赞的条目样式要不一样
  • 不允许重复点赞
  • 点赞可以取消

再来分析一下系统如何实现:

  • 数据库:后台表需要有记录每个需求条目点赞数量的字段,添加完成后初始值为1,点赞+1,取消点赞-1
  • 重复点赞:这个问题实现的方法其实挺多,比较灵活。比较常见的一种实现方法应该是通过用户名来查重,该用户针对一个需求条目只能点赞一次,如果点过赞再次点击则为取消点赞。

但是这个项目刚开始并没有考虑设计用户登陆功能,因为需求收集可能就是一个开放的平台,在公司内网环境下,都可以通过需求收集平台来提交用户的问题或者建议,并不需要登陆。于是这里我想到是否可以通过 cookie 的方式来实现这个功能,正好最近学习 jQuery 看到 cookie 那块。

大体思路:

页面加载后,检查需求条目是否有对应的 cookie,如果没有即没有点过赞,设置样式 A;如果找到对应的 cookie,证明已经点过赞,设置样式 B。点击动作同理,同样是判断是否有对应条目的 cookie,有的话,点击即为-1;没有的话,点击即为+1。

想到就动手实践了,首先下载carhartl/jquery-cookie插件,并在项目中引入以备后用。

前台样式如下动画:

第一次点赞+1,背景变成浅红色;再次点赞-1,样式恢复;并且点赞后,刷新页面后依然是点赞状态。

接下来看看js是怎么实现的:

/* 已经赞过的message 样式设置,防止刷新页面后样式恢复原样 */
$('.message-list-item').each(function () {
    var mid = $(this).attr('mid');
    var cookie = $.cookie('haveUp' + mid);
    if (cookie && cookie == 2) { // 2代表赞过,1代表没有赞过
        $(this).find('div.up').addClass('up-yes'); // up-yes为红色背景样式
    }
});
/* up a message 赞一个需求 */
$('.qa-rank .up').click(function () {
    var messageId = $(this).attr('data-messageId');
    var $plus = $('<span id="plus"><strong>+1</strong></span>');
    var $minus = $('<span id="minus"><strong>-1</strong></span>');
    var $this = $(this);
    var bool = $.cookie('haveUp' + messageId); // 是否Up
    if (!bool || bool == 1) { // 赞一个需求
        $plus.insertAfter($this).css({
            'position': 'relative',
            'z-index': '1',
            'color': '#C30'
        }).animate({
            top: -30 + 'px',
            left: +30 + 'px'
        }, 'slow', function () {
            $(this).fadeIn('slow').remove();
        });
        $.ajax({
            url: '/ajax/up/' + messageId,
            method: 'POST',
            global: false,
            success: function (result) {
                $this.addClass('up-yes');
                $.cookie('haveUp' + messageId, 2, { path: '/', expires: 1 });
            }
        });
        return false;
    } else {
        $minus.insertAfter($this).css({  // 取消赞
            'position': 'relative',
            'z-index': '1',
            'color': '#5cb85c'
        }).animate({
            top: -30 + 'px',
            left: +30 + 'px'
        }, 'slow', function () {
            $(this).fadeIn('slow').remove();
        });
        $.ajax({
            url: '/ajax/cancel/' + messageId,
            method: 'POST',
            global: false,
            success: function (result) {
                $this.removeClass('up-yes');
                $.cookie('haveUp' + messageId, 1, { path: '/' });
            }
        });
        return false;
    }
});

代码逻辑很简单,主要就是判断是否点赞,如果点赞了,那么创建 id 为 minus 的 span 节点插入到 DOM 中,然后给个动画效果;如果没有点赞,那么创建 id 为 plus 的 span 节点插入到 DOM 中,同样给个动画效果;同时,通过 ajax 异步请求数据到后台更新数据库中的点赞数量。

好了,这个小功能算是基本实现了,思路是不是对的暂不清楚,如果不对,请指正,学习就是不断尝试的过程。后面再继续介绍该项目的一些内容。

阅读更多

Node项目之需求收集平台(二)- 上传插件使用

该项目前台表单上使用的是 bootstrap-fileinput 的样式,bootstrap-fileinput 是挺强大的一个文件上传插件,注意是文件上传,不仅仅只是针对图片上传,初次接触它光是调成自己想要的样式也是花了半天时间呢,下面简单介绍一下需求收集平台这个项目使用的这个插件一些细节问题。

对了,别忘了详细看下官方说明文档:Bootstrap File Input - © Kartik,英文的,看着头疼也要看下去。

1. 提交模式

首先文档简要对比了一下两种上传模式,一是表单方式提交,一是Ajax方式提交。从下图可以看出官方显然是推荐你使用Ajax方式上传的,很明显使用Ajax方式上传更加自由,并且能获取更多的功能。

2. 导入文件

然后需要导入必要的 CSS 以及 JS 文件,以下这些是必须要导入的,别调试了半天才发现插件都没有导入就搞笑了。

  1. bootstrap.css
  2. bootstrap.js
  3. jquery.js
  4. bootstrap-fileinput/css/fileinput.css
  5. bootstrap-fileinput/js/fileinput.js
  6. bootstrap-fileinput/js/locales/zh.js (中文语言文件,可选择是否导入)

自己对号入座,寻找对应的文件,以上6个必须要导入,bootstrap-fileinput/js/plugins/ 目录里还有一些插件视具体实现的功能导入。

3. 基本使用

接下来就是插件的使用了,起初我就是为了改善一下file类型的表单的样式,默认的文件上传样式实在不忍直视。

在不引入 bootstrap-fileinput 插件相关文件的时候,如下html代码:

<form method="POST" action="#">
    <div class="form-group">
        <label for="file">文件输入</label>
        <input type="file" name="file" id='myfile'>
    </div>
    <input type="submit" name="submit" value='提交' class="btn btn-primary">
</form>

显示的效果是这样的:

在引入 bootstrap-fileinput 插件后,同样的html代码,显示效果是这样的:

引入基本文件后,在页面底端添加一行js代码 $('#myfile').fileinput();,就能出现不一样的效果。完整的HTML代码如下:

<!DOCTYPE html>
<html>
<head>
  <title>fileinput-example</title>
  <meta charset="utf-8">
  <link rel="stylesheet" type="text/css" href="css/bootstrap.css">
  <link rel="stylesheet" type="text/css" href="css/fileinput.css">
  <script type="text/javascript" src="js/jquery.js"></script>
  <script type="text/javascript" src="js/bootstrap.js"></script>
  <script type="text/javascript" src="js/fileinput.js"></script>
  <script type="text/javascript" src="js/zh.js"></script>
</head>
<body>
  <div class="container">
    <div class="row">
      <div class="col-lg-6">
        <h1>fileinput-example</h1>
        <form method="POST" action="#">
          <div class="form-group">
            <label for="file">文件输入</label>
            <input type="file" name="file" id='myfile'>
          </div>
          <input type="submit" name="submit" value='提交' class="btn btn-primary">
        </form>
      </div>
    </div>
  </div>
  <script type="text/javascript">
    $('#myfile').fileinput();
  </script>
</body>
</html>

接下来,上传几个文件试试默认的样式是啥样。

  • 上传一个图片试试

  • 上传一个文件试试

看起来是不是还可以,该项目最终显示成如下样子:

功能上做了一些限制,比如允许上传多张照片,但只允许上传3张,每个照片最大只能是1MB。要想实现一些控制,那么就要了解下面的一些参数。

4. 控制参数

在官方的说明文档里都有介绍,但是很多情况,虽然有说明但也不一定能看懂,对于该插件的功能我也不并不是很熟悉,我只是实现了我想要的效果,有兴趣的可以自行学习。

下面是该项目中配置的一些参数:

$("#input-id").fileinput({
    showUpload: false,
    previewFileType: 'any',
    language: 'zh',
    browseLabel: '图片多选',
    browseClass: 'btn btn-default',
    allowedFileTypes: ['image'], // 限制文件类型为图片
    allowedFileExtensions: ['jpg', 'png'], // 限制文件后缀名为jpg,png,gif
    maxFileCount: 3,  // 限制最多3张图片
    maxFileSize: 1024, // 限制图片大小,最大1024KB
    allowedPreviewTypes: ['image'], // 允许预览的文件类型
    initialCaption: '可以选择最多3张图片,格式为png或者jpg,大小不超过1M', // 初始化说明框框,比如该项目上默认显示:可以选择最多3张图片,格式为png或者jpg,大小不超过1M
    layoutTemplates: {
      main1: '{preview}\n' +
      '<div class="input-group {class}">\n' +
      '   <div class="input-group-btn">\n' +
      '       {browse}\n' +
      '       {remove}\n' +
      '   </div>\n' +
      '   {caption}\n' +
      '</div>',
      footer: '<div class="file-thumbnail-footer">\n' +
      '    <div class="file-caption-name">{caption}{size}</div>\n' +
      '</div>'
    }
  }); // 修改默认样式,比如按钮移到左侧,预览窗口中图片的脚标等等(这里只显示文件名,如下图)

这里只是简单介绍了一下 bootstrap-fileinput 插件的简单功能,更多功能的学习还是要靠自己的研究,特别是Ajax上传功能。作者研究深度有限,暂不做过多介绍了,有机会的话,再做深入学习并分享。

阅读更多

Node项目之需求收集平台(一)- 基本介绍

上个月只大概花了几个小时就完成简单的评分系统,非常简单的第一个项目。然而在写这个需求收集平台的时候,考虑的问题很多,需求也变了好几回(自己定需求自己开发也是醉了),而且刚开始学习使用 jQuery 框架来实现页面交互,所以直到现在还没有完整的完成所有功能。这个需求收集平台相对于第一个项目有如下方面的改进:

  • 适当添加了部分CSS代码,不再只是使用原生bootstrap样式而不写一句css代码
  • 全面使用 jQuery 来实现界面交互(现学现卖)
  • 大幅使用 ajax 来异步获取响应,而不再是没写一句js代码
  • 每个页面不再是独立的一个文件,适当的使用了分块拼凑的方式,但仍然使用ejs(下次该换了它)
  • 使用 bootstrap-table 以及 bootstrap-fileinput 等等插件实现部分功能
  • 数据依然使用的MySQL,只是根据表分文件编写,不再混为一谈

整个项目到目前为止,前前后后也差不多1个月时间了,边学习边开发,个人感觉进步还是比较明显的,个人技能的主要更新都在 jQuery 上了,从开始做这个项目的时候,才刚开始学习 jQuery,在进行这个项目的这个月,也看完了《锋利的jQuery》一书,收获颇丰,书中学习的好多内容也在这个项目中得到了实践。下面简单介绍一下整个项目的界面以及基本功能。

前台

前台目前就两个界面,一个是首页 index.ejs,一个是需求添加页面 add.ejs,简单的两个界面需求变更了好几次,前期没有提前设计出来,都是靠想象来写前端代码,故而总是写完后看着不爽就换了。最终形成下面的样子,也不想再变动了。

首页:index.ejs

点击条目的时候,会异步加载详细内容以及回复内容等等。

添加页面:add.ejs

上传图片样式

功能方面:

  • 首页上点击消息行项目异步请求获取详情内容以及回复
  • 首页aside边栏页面加载完成后异步获取实时内容
  • 添加页动态获取需求分类以及对应的详细分类值
  • 添加页aside边栏在点击详细分类后触发获取可能要提交的问题
  • 引入 bootstrap-fileinput 插件美化图片上传样式

后台

整个后台看上去就一个页面 admin.ejs,不同的设置页面都是通过 bootstrap-table 异步加载json的方式来显示的,其中包括基本信息的设置、部门分类设置,需求分类设置以及用户账号密码设置。其实也可以理解为有4个单独的页面,只是URL是不变的,全部通过异步加载的方式来处理。

后台首页:admin.ejs

使用 bootstrap 原生 modal 实现的效果:

功能方面:

  • 使用 bootstrap-table 插件来实现整体的功能框架
  • 辅助使用 bootstrap 一些原生javascript插件来展示部分效果,如 modal

做这个项目的时间跨度已经将近一个月时间,其中不乏各种需求变更,迟迟得不到自己想要的效果,很多时候都是梦想很美好,现实很残酷(PS. 能力有限)。虽然现在好多的功能还没有写完,但是大体的样式以及要实现的功能已经定型了,不想再改了,善始善终,接下来的时间就是完成它了。

完成之前在这里简单记录一下,也算做是自己学习Node Web的一个项目实践吧。关于这个项目的一些细节单独再整理文章进行介绍。

阅读更多

Node项目之评分系统(三)- Web开发

在数据库设计完成之后,就可以开始项目开发了,实实在在的编写代码了。

生成项目目录

这里使用的是 express 框架,于是大大简化了 HTTP 方法以及路由访问的实现过程。安装完 express 模块后,建议同时装上 express-generator 项目生成器,快捷生成 express 项目初始目录及必要文件。

npm install --save express
npm install express-generator -g

注意:express-generator 模块需要全局安装。

安装完 express-generator生成器后,在项目目录下,运行 express -e 命令,即可生成 express项目目录。

例如:express -e ./test01 命令就是在当前目录下创建一个以 ejs 模版引擎的名叫 test01 的项目目录。

创建完之后,进入项目目录并安装依赖包,执行如下命令:

cd ./test01 && npm install

安装完之后,执行命令 npm start,一个 Node Web 项目就启动了,打开浏览器输入 http://localhost:3000 就可以看到 express 为你准备的首页了。

数据库方法

由于这里使用的是 MySQL 数据库,Node项目中需要安装 MySQL 模块以便使用 MySQL 数据库。使用 npm 包管理器很容易就能安装完成。

npm install mysql --save

开始一个项目,最好是先把数据库相关功能完成,由于项目简单,仅仅是表 message 以及 vote 两个表的操作,于是数据库的连接我全部写在了一个文件上了 db.js(新建db文件夹,在其中新建db.js),查看 MySQL 模块的 README 文档,大概知道了该模块的使用方法。

PS. 在安装了新的模块而不会用的情况下,最好是把模块里的 README 文档浏览一遍,虽然大部分是英文的,但是好过各种百度。 – 过来人忠告

看完文档,使用了最简单的连接方式,在 db.js 中开始编写代码:

var mysql = require('mysql');

var options = {
  host: 'localhost',
  user: 'lupeng',
  password: '080910',
  database: 'vote'
}

// 获取数据库对象
function getConn (){
  var client = mysql.createConnection(options);
  return client;
}

// 得到管理员帐户
exports.getAdmin = function(callback){
  var client = getConn();
  var statement = 'select username,password from user where Id = 1';
  client.query(statement, function(errs,rows,fields){
    callback(errs,rows);
  });
  client.end();
}

这里只是贴上了获取管理员账户的方法示例,其他获取数据库的方法可以都写在这个 db.js 的文件里,这里就不多赘述,后面会附上项目代码自行查看。

路由方法

express 提供了非常简单的路由编写方式,这里分别使用了 HTTP 协议 GET//show/login/admin/add 以及 /edit 来分别获得首页、查看页、登陆页、后台首页、添加页以及编辑页。然后使用 POST / 来实现投票;使用 /del 来实现删除;使用 /edit 来提交更新;使用 /add 来实现添加主题等。

这里的代码我自认为写的非常的乱,需要好好设计以及重新编写,这里就不贴出来。

在路由方法里有个简易的过滤器需要实现,也就是在用户登陆后台的时候,需要 session 的一个保存以及获取,来证明你是管理员。这里我同样采取了一个简单的 express 中间件来实现了,也就是在访问 /admin 的时候,需要验证 seesion,见下面简要代码:

/* 登陆权限 */
router.use(function (req, res, next) {
  db.getAdmin(function (errs, rows) {
    if (errs) {
      res.render('error', {
        message: errs,
        error: {}
      });
    } else {
      var username = rows[0].username;
      var password = rows[0].password;
      if (req.session && req.session.username == username && req.session.password == password) {
        next();
      } else {
        res.render('login', {
          title: '后台登陆',
          info: '请登陆'
        });
      }
    }
  });
});

这个中间件放在访问 /admin 的路由上,凡是对 /admin 的所有访问请求都需要经过该中间件来验证是否存在 session,这样就大概实现了管理员验证的一个功能。当然,这里还需要使用到 express-session 的模块,安装并在 app.js 中简单配置即可。以下是这里的简要配置:

// session config
app.set('trust proxy', 1);
app.use(session({
  secret: 'keyboard cat',
  resave: false,
  saveUninitialized: true,
  cookie: {
    maxAge: 1000*60*60*1
  }
}));

由于没有做登出功能,session 设置了失效时间为1个小时。

大概的项目实现过程就写到这里,详细可以看下该项目源代码:vote-node - Coding.net or vote-node - Github.com

由于初次尝试使用 Node 写一个 Web 项目,写的不好的地方请见谅,后续有可能对该项目进行进一步完善。

阅读更多

Node项目之评分系统(二)- 数据库设计

经过前面的介绍,可以发现,其实这是一个非常小的入门级项目,整个功能的实现就是简单的增删改查。增加一条主题,修改一条主题,删除一条主题或评分,查看所有主题或是所有评分。根据需求得出要实现的功能,然后根据大体功能就要想如何设计数据库表。

这里我选用的是关系型数据库 MySQL,所以在项目动手之前最重要的要设计好数据库表,大家常说:一个项目数据库设计好了,基本上就完成了一大部分的工作。这么说其实是有些道理的。好的数据表设计会让后续的编程工作更加轻松。

该项目的功能简单,所以数据表自然也很简单,这里我只是创建了三个数据表:messagevote 以及 user,一个保存主题信息,一个保存评分信息,一个保存管理员账号信息。

数据库SQL语句不熟悉的可以选择一个 GUI 工具,方便数据库方面的工作,避免由于数据库方面的一些问题阻碍了整个项目的编程工作。这一点很重要,特别对于像我这样的小白来说,很有可能由于整个过程中的一点困难障碍,就阻碍了整个编程工作,甚至是放弃。

这个项目我是在Windows平台上进行了,于是选择了一个简单的 MySQL GUI 工具 MySQL-Front,创建数据库,创建表,新建测试数据等等都可以直接在软件上点点鼠标就可以完成了,大大简化了开发流程。整个数据表的设计也非常简单:

message:

  • title: 代表评分主题
  • author: 主题作者
  • average: 该主题平均分
  • status: 主题状态,是否允许评分
  • createTime: 创建时间
  • endTime: 关闭时间(该字段后来没有使用)

vote:

  • titleId: 评分的主题 id
  • voteIp: 评分者 IP,由于在内网环境,每台电脑对应一个 IP 地址
  • voteTime: 评分时间
  • voteScore: 分数

user:

这个就不多说了,用户名和密码,在这个项目里只是为了存储管理员账号使用。

由于项目比较简单以及本人是个数据库小白,所以看出这是个很简单的数据表结构,能满足该项目的需求即可。

  • 新建主题的时候,直接insert into message;
  • 删除主题的时候,delete from message ;
  • 修改主题的时候,update message ;
  • 查找的时候,select * from message。 同理,vote 评分表也是这样。

  • 在计算平均分的时候,通过 titleId 去表 vote 中查找 average(voteScore),然后更新到 message 表中即可。如下 SQL 语句:

      select avg(voteScore) average from vote v , message m where v.titleId = m.Id and v.titleId = titleId
    
  • 在检查重复评分的时候,直接查找 titleId 以及 voteIp 是否存在即可。如下 SQL 语句:

      select count(*) voteNum from vote where titleId= `titleId` and voteIp = `voteIp`
    

    当结果为0的时候,即表示没有评过分。

  • 获取每个主题的评分数量时,在 vote 表中查找对应 titleId 对应的行数即可。SQL 语句如下:

      select count(*) voteNum from vote where titleId= `Id`
    

整个后台的数据库设计大概就这些,并没有很复杂的结构。

阅读更多