一步步搭建物联网系统


⼀步步搭建物联⽹系统 其他:基于 REST 服务的最⼩物联⽹系统设计 ⼀步步搭建物联⽹系统 • 0.1 前⾔ • 0.2 ⽬标读者 • 0.3 不适合⼈群 • 0.4 介绍 – 0.4.1 为什么没有 C? – 0.4.2 为什么不是 JAVA ? – 0.4.3 为什么没有 Android ? • 0.5 如何阅读 • 1 ⽆处不在的 HTML – 1.1 html 的 hello,world * 1.1.1 调试 hello,world * 1.1.2 说说 hello,world * 1.1.3 想⽤中⽂? – 1.2 其他 html 标记 * 1.2.1 美妙之处 * 1.2.2 更多 • 2 ⽆处不在的 Javascript – 2.1 Javascript 的 Hello,world – 2.2 更 js ⼀点 * 2.2.1 从数学出发 – 2.3 设计和编程 * 2.3.1 函数 1 ⼀步步搭建物联⽹系统 * 2.3.2 重新设计 * 2.3.3 object 和函数 * 2.3.4 ⾯向对象 – 2.4 其他 – 2.5 美妙之处 • 3 ⽆处不在的 CSS – 3.1 CSS – 3.2 关于 CSS – 3.3 代码结构 – 3.4 样式与⽬标 * 3.4.1 选择器 – 3.5 更有趣的 CSS • 4 ⽆处不在的三剑客 – 4.1 Hello,Geek – 4.2 从源码学习 * 4.2.1 HTML – 4.3 DOM 树形结构图 * 4.3.1 javascript * 4.3.2 CSS – 4.4 CSS 盒模型图 – 4.5 笔记 • 5 GNU/Linux – 5.1 什么是 Linux – 5.2 操作系统 * 5.2.1 Linux 架构图 * 5.2.2 Shell * 5.2.3 GCC * 5.2.4 启动引导程序 – 5.3 从编译开始 * 5.3.1 开始之前 * 5.3.2 编译 Nginx * 5.3.3 其他 2 ⼀步步搭建物联⽹系统 – 5.4 包管理 • 6 Arduino – 6.1 极客的玩具 – 6.2 硬件熟悉 – 6.3 开发环境 – 6.4 点亮⼀个 LED – 6.5 串⼜通信 • 7 Python – 7.1 代码与散⽂ * 7.1.1 开始之前 * 7.1.2 Python 的 Hello,World * 7.1.3 我们想要的 Hello,World – 7.2 算法 – 7.3 实⽤主义哲学 – 7.4 包管理 • 8 Raspberry Pi – 8.1 Geek 的盛宴 – 8.2 Raspberry Pi 初始化 • 9 HTTP 与 RESTful – 9.1 你所没有深⼊的 HTTP * 9.1.1 打开⽹页时发⽣了什么 * 9.1.2 URL 组成 – 9.2 ⼀次 HTTP GET 请求 * 9.2.1 HTTP 响应 – 9.3 REST * 9.3.1 资源 • 10 设计 RESTful API – 10.1 REST 关键⽬标 – 10.2 判断是否是 RESTful 的约束条件 – 10.3 设计 RESTful 资源 – 10.4 设计 RESTful URI 3 ⼀步步搭建物联⽹系统 – 10.5 Laravel * 10.5.1 安装 Laravel – 10.6 MySQL * 10.6.1 安装 MySQL * 10.6.2 配置 MySQL – 10.7 数据库迁移 * 10.7.1 创建表 * 10.7.2 数据库迁移 – 10.8 创建 RESTful – 10.9 Laravel Resources * 10.9.1 修改 Create() * 10.9.2 创建表单 * 10.9.3 编辑模板 • 11 RESTful Evertywhere – 11.1 Javascript 与 ajax * 11.1.1 jQuery – 11.2 Java 与 json * 11.2.1 Android – 11.3 Python 与 json * 11.3.1 requests • 12 前端显⽰ – 12.1 jQuery Mobile – 12.2 Bootstrap – 12.3 库与车轮⼦ – 12.4 ⽹站前台显⽰ * 12.4.1 Highchart – 12.5 CoAP: 嵌⼊式系统的 REST – 12.6 CoAP 命令⾏⼯具 * 12.6.1 Node CoAP CLI * 12.6.2 libcoap – 12.7 CoAP Hello,World * 12.7.1 Node-CoAP 4 0.1 前⾔ * 12.7.2 Node CoAP ⽰例 – 12.8 CoAP 数据库查询 * 12.8.1 Node Module * 12.8.2 Node-Sqlite3 * 12.8.3 查询数据 * 12.8.4 GET * 12.8.5 IoT CoAP * 12.8.6 判断请求的⽅法 * 12.8.7 Database 与回调 – 12.9 CoAP Block * 12.9.1 CoAP POST * 12.9.2 JSON 请求 * 12.9.3 CoAP Content Types – 12.10 CoAP JSON * 12.10.1 返回 JSON * 12.10.2 CoAP 客户端代码 * 12.10.3 CoAP Server 端代码 • 13 MQTT – 13.1 Nodejs MQTT 0.1 前⾔ 设计物联⽹系统是⼀种有意思的事情,我们需要考虑到软件、硬件、通讯等等不同 的⼏个⽅案。探索不同的语⾔,不同的框架,形成不同的解决⽅案。 在⽂档中,我们将对设计物联⽹系统有⼀个简单的介绍,我们会探讨如何设计⼀个 最⼩的物联⽹系统。 0.2 ⽬标读者 本⽂档的⽬标读者是初⼊物联⽹领域,希望对物联⽹系统有⼀个⼤概的认识和把握, 并学会如何掌握好⼀个基础的物联⽹系统的设计。 本⽂档对⼀些概念 (如) 只做了⼀些基本介绍,以及便于理解。如果想进⼀步了解这 些概念,会列出⼀些推荐书⽬,以供参考。 5 0.3 不适合⼈群 • 硬件开发⼈员,对物联⽹有兴趣。 • 没有 web 开发经验 • 极少的 linux 使⽤经验 • 想快速将于⽣产环境 • 对硬件了解有限的开发⼈员。 • 没接触过 51、ARM、Arduino • 想了解以下的东西 • RESTful 与 IOT • CoAP 协议 • MQTT 0.3 不适合⼈群 • 如果你在这⽅⾯已经有了丰富经验的开发者 (ps: 我想你帮我们丰富⽂档) 0.4 介绍 关于内容的选择上,这是⼀个有意思的话题,我们很难判断不同的开发者⽤的是怎 样的语⾔,⽤的是怎样的框架。 于是我们便⾃作主张地选择了那些适合于理论学习的语⾔、框架、硬件,去除掉其 他那些我们不需要考虑的因素,如语法,复杂度等等。当然,这些语⾔、框架、硬件也 是流⾏的,如果找到相关的⽂档。 • Arduino: 如果你从头开始学过硬件的话,那些你会爱上它的。 • Raspberry PI: 如果你从头编译过 GNU/Linux 的话,我想你会爱上她的。 • Python: 简单地来说,你可以⽅便地使⽤⼀些扩展,同时代码就表达了你的想法。 • PHP : 这是⼀门容易部署的语⾔,我想你只需要在你的 Ubuntu 机器上,执⾏⼀下 脚本就能完成安装了。⽽且,如果你是⼀个硬件开发者的话,那么你会更容易找 到其他开发者的。 • Javascript : 考虑到 CoAP、MQTT 等版本是基于 Nodejs 的话,⽽且这门语⾔已经 ⽆处不在了,⽽且会更加流⾏。 • HTML、CSS : 这是必须的,他们仍然也是⽆处不在。 6 0.5 如何阅读 0.4.1 为什么没有 C? 如果你还想⽤ C 学理论的话,呵呵。 0.4.2 为什么不是 JAVA ? ⼤致有下⾯两个原因 • JAVA 学的⼈很多,然⽽不适合我们将主要精⼒集中于构建与学习,因为⽆关的代 码太多了。 • 当时以及现在,我还是不喜欢 JAVA(ps: 更喜欢脚本语⾔,可以在更少的时候做更 多的事)。 0.4.3 为什么没有 Android ? 在 IOT 的 repo 中: https://github.com/gmszone/iot 是有 Android 的⽰例,然⽽这 些理论不适合在这⾥讨论。 0.5 如何阅读 这是⼀个简单的建议,仅针对于在选择阅读没有经验的读者。 当前状态 建议 软件初学者 从头阅读 硬件开发者 从头阅读 有⼀天,⾛在回学校的路上,我在想:``未来是科技时代(现在也是),只是未来科 技会⽆处不在,⽽如果我们对于周围的⽆处不在的代码⼀⽆所知的话,或许我们会成为 ⿊客帝国中的⼀般⼈''。所以开始想着,⼈们会开始学习编程就像学习⼀门语⾔⼀样,直 到有⼀天我看到了学习编程如同学习⼀门语⾔。这算是⼀个有趣的时间点,于是我开始 想着像之前做最⼩物联⽹系统的那些步骤⼀样,写⼀个简单的⼊门。也可以补充好之前 在这个最⼩物联⽹系统缺失的那些东西,给那些正在开始试图去解决编程问题的⼈。 我们先从⾝边的语⾔下⼿,也就是现在⽆处不在的 html+javascript+css。 7 1 ⽆处不在的 HTML 1 ⽆处不在的 HTML 从 html 开始的原因在于我们不需要去配置⼀个复杂的开发环境,也许你还不知道 开始环境是什么东西,⽽这些需要去慢慢的了解才能接触,特别是对于普通的业余爱好 者来说,对于专业的选⼿那些⾃然不是问题。HTML 是 Web 的核⼼语⾔,也算是基础 的语⾔。 1.1 html 的 hello,world Hello,world 是⼀个传统,所以在这⾥也遵循这个有趣的传统,我们所要做的事情事 实很简单。虽然也有点 hack 的感觉,所以让我们新建⼀个⽂件叫 ``helloworld.html''。 (PS: ⼤部分⼈应该都是在 windows 下⼯作的,所以你需要新建⼀个⽂本,然后重命 名,或者你需要⼀个编辑器,在这⾥推荐⽤ sublime text。破解不破解,注册不注册 都不会对你的使⽤有太多的影响。) 1. 新建⽂件 2. 输⼊ hello,world 3. 保存为 ->``helloworld.html'', 4. 然后双击打开这个⽂件。正常情况下应该是刚好⽤你的默认浏览器打开。只要是 ⼀个现代的浏览器的话,应该可以看到上⾯显⽰的是 ``Hello,world''。 这才是最短的 hello,world 程序,其次呢?ruby 中的会是这样⼦的 2.0.0-p353 :001 > p "hello,world" "hello,world" => "hello,world" 2.0.0-p353 :002 > 等等,如果你了解过 html 的话,会觉得这⼀点都不符合语法规则,但是他⼯作了, 没有什么⽐安装完 Nginx 后看到 It works! 更让⼈激动了。 遗憾的是,它可能⽆法在所有的浏览器上⼯作,所以我们需要去调试其中的 bug。 8 1.1 html 的 hello,world 1 ⽆处不在的 HTML 1.1.1 调试 hello,world 我们会发现我们的代码在浏览器中变成了下⾯的代码,如果你和我⼀样⽤的是 chrome,那么你可以右键浏览器中的空⽩区域,点击审查元素,就会看到下⾯的代码。 hello,world 这个才是真正能在⼤部分浏览器上⼯作的代码,所以复制它到编辑器⾥吧。 1.1.2 说说 hello,world 我很不喜欢其中的 <*> 超⽂本标记语⾔ 所以我们可以发现其中的关键词是标记 ------markup,也就是说 html 是⼀个 markup,head 是⼀个 markup,body 是⼀个 markup。 ⽽后,我们真正⼯作的代码是在 body ⾥⾯,⾄于为什么是在⾥⾯,这个问题算是 太复杂了。 1. 我们所学的汉语是别⼈创造的,我们所正在学的这门语⾔也是别⼈创造的。 2. 我们在⾃⼰的语⾔⾥遵循着他代表是个男的,她代替是个⼥的。 1.1.3 想⽤中⽂? 所以我们也可以把计算机语⾔与现实世界的语⾔划上⼀个等号。⽽我们所要学习的 语⾔,因为没有⼀门真正意义上的汉语语⾔,所以我们便觉得这些很复杂,如果我们可 以⽤汉语代换掉上⾯的代码的话 <语 ⾔> <头><结 束 头> <⾝ 体>你 好 , 世 界<结 束 ⾝ 体> <结 束 语 ⾔> 看上去很奇怪,只是因为是⾳译过去的原因,也许你会觉得这样会好理解⼀点,但 是输⼊上可能⼀点⼉也不⽅便,因为这键盘都不适合我们去输⼊汉字,也意味着可能你 输⼊的会有问题。 9 1.1 html 的 hello,world 1 ⽆处不在的 HTML 让我们把上⾯的代码代替掉原来的代码然后保存,打开浏览器会看到下⾯的结果 <语 ⾔> <头><结 束 头> <⾝ 体>你 好 , 世 界<结 束 ⾝ 体> <结 束 语 ⾔> 更不幸的结果可能是 <璇￿█> <澶￿><缁 撴 潫 澶￿> <韬￿綋>浣 犲 ソ 锛 屼 笘 鐣￿<缁 撴 潫 韬￿綋> <缁 撴 潫 璇￿█> 这是⼀个编码问题,对中⽂⽀持不友好。 所以我们把上⾯的代码改为和标记语⾔⼀样的结构 <语 ⾔> <头> <⾝ 体>你 好 , 世 界 <结 束 语 ⾔> 于是我们看到的结果便是 <语 ⾔> <头> <⾝ 体>你 好 , 世 界 被 chrome 浏览器解析成什么样了? <语⾔> <头> <⾝体> 你好,世界 以 结尾的是注释,写给⼈看的代码,不是给机器看的,所以机器不会去理解这些代码。 但是当我们把代码改成 你 好 世 界 浏览器上⾯显⽰的内容就变成了 你 好 世 界 10 1.2 其他 html 标记 1 ⽆处不在的 HTML 或许你会觉得很神奇,但是这⼀点⼉也不神奇,虽然我们的中⽂语法也遵循着标记 语⾔的标准,但是我们的浏览器不⽀持中⽂标记。 结论: 1. 浏览器对中⽂⽀持不友好。 2. 浏览器对英⽂⽀持友好。 刚开始的时候不要对中⽂编程有太多的想法,这不是很现实的: 1. 现有的系统都是基于英语构建的,对中⽂⽀持不是很友好。 2. 中⽂输⼊的速度在某种程度上来说没有英语快。 我们离开话题已经很远了,但是这⾥说的都是针对于那些不满于英语的⼈来说的, 只有当我们可以从头构建⼀个中⽂系统的时候才是可⾏的,这些包括的东西有 cpu,软 件,硬件,⽽我们还需要考虑重新设计 cpu 的结构,在某种程度上来说会有些不现实。 需要⼀代又⼀代的⼈的努⼒,只是在当前就更不现实了。忘记那些,师夷长之技以治夷。 1.2 其他 html 标记 添加⼀个标题, 标 题 hello,world 我们便可以在浏览器的最上⽅看到 ``标题'' ⼆字,真实世界的淘宝⽹也包含了上⾯ 的东西,只是还包括了更多的东西,所以你也可以看懂那些我们可以看到的淘宝的标题。 标题 11 1.2 其他 html 标记 1 ⽆处不在的 HTML hello,world

⼤标题

次标题

...

  • 列表 1
  • 列表 2
更多的东西可以在⼀些书籍上看到,这边所要说的只是⼀次简单的语⾔⼊门,其他 的东西都和这些类似。 1.2.1 美妙之处 我们简单地上⼿了⼀门不算是语⾔的语⾔,浏览器简化了这其中的⼤部分过程,虽 然没有 C 和其他语⾔来得有专业感,但是我们试着去开始写代码了。我们可能在未来的 某⼀篇中可能会看到类似的语⾔,诸如 python,我们所要做的就是 $ python file.py =>hello,world 然后在终端上返回结果。只是因为在我看来学会 html 是有意义的,简单的上⼿,⽽ 后再慢慢地深⼊,如果⼀开始我们就开始去理解指针,开始去理解类。我们甚⾄还知道 程序是怎么编译运⾏的时候,在这个过程中又发⽣了什么。虽然现在我们也没能理解这 其中发⽣了什么,但是⾄少展⽰了 1. 中⽂编程语⾔在当前意义不⼤,不现实,效率不⾼兼容性差 2. 语⾔的语法是固定的。(ps: 虽然我们也可以进⾏扩充,我们将会在后来⽀持上述 的中⽂标记。) 3. 已经开始写代码,⽽不是还在配置开发环境。 4. 随⾝的⼯具才是最好的,最常⽤的 code 也才是实在的。 12 2 ⽆处不在的 JAVASCRIPT 1.2.2 更多 我们还没有试着去解决,某商店⾥的糖⼀个 5 块钱,⼩明买了 3 个糖,⼩明⼀共花 了多少钱的问题。也就是说我们学会的是⼀个还不能解决实际问题的语⾔,于是我们 还需要学点东西如 javascript,css。我们可以理解为 Javascript 是解决问题的语⾔,html 是前端显⽰,css 是配置⽂件,这样的话,我们会在那之后学会成为⼀个近乎专业的程 序员。我们刚学了下怎么在前端显⽰那些代码的⾏为,于是我们还需要 Javascript。 2 ⽆处不在的 Javascript Javascript 现在已经⽆处不在了,也许你正打开的某个⽹站他可能是 node.js+json +javascript+mustache.js 完成的,虽然你还没理解上⾯那些是什么,也正是因为你不理 解才需要去学习更多的东西。但是 Javascript 已经⽆处不在了,可能会在你⼿机上的某 个 app ⾥,在你浏览的⽹页⾥,在你 IDE 中的某个进程中运⾏的。 2.1 Javascript 的 Hello,world 这⾥我们还需要有⼀个 helloworld.html,Javascript 是专为⽹页交互⽽设计的脚本 语⾔,所以我们⼀点点来开始这部分的旅途,先写⼀个符合标准的 helloworld.html 然后开始融⼊我们的 javascript,向 HTML 中插⼊ Javascript 的⽅法,就需要⽤到 html 中的 13 2.2 更 js ⼀点 2 ⽆处不在的 JAVASCRIPT 按照标准的写法,我们还需要声明这个脚本的类型 没有显⽰ hello,world? 试试下⾯的代码 2.2 更 js ⼀点 我们需要让我们的代码看上去更像是 js,同时是以 js 结尾。C 语⾔的源码但是以 C 结尾的,所以我们要让我们的代码看上去更正式⼀点。于是我们需要在 helloworld.html 的同⽂件夹下创建⼀个 app.js,⾥⾯写着 14 2.3 设计和编程 2 ⽆处不在的 JAVASCRIPT document.write('hello,world'); 同时我们的 helloworld.html 还需要告诉我们的浏览器 js 代码在哪⾥ 2.2.1 从数学出发 让我们回到第⼀章讲述的⼩明的问题,从实际问题下⼿编程,更容易学会编程。⼩ 学时代的数学题最喜欢这样⼦了 ------某商店⾥的糖⼀个 5 块钱,⼩明买了 3 个糖,⼩ 明⼀共花了多少钱的问题。在编程⽅⾯,也许我们还算是⼩学⽣。最直接的⽅法就是直 接计算 3x5=? document.write(3*5); document.write 实际也我们可以理解为输出,也就是往页⾯⾥写⼊ 3*5 的结果,在 有双引号的情况下会输出字符串。我们便会在浏览器上看到 15,这便是⼀个好的开始, 也是⼀个不好的开始。 2.3 设计和编程 对于我们的实际问题如果总是⽌于所要的结果,很多年之后,我们成为了 code monkey。对这个问题进⾏⼀次设计,所谓的设计有些时候会把简单的问题复杂化,有 些时候会使以后的扩展更加简单。这⼀天因为这家商店的糖价格太⾼了,于是店长将价 格降为了 4 块钱。 document.write(3*4); 15 2.3 设计和编程 2 ⽆处不在的 JAVASCRIPT 于是我们又得到了我们的结果,但是下次我们看到这些代码的时候没有分清楚哪个 是糖的数量,哪个是价格,于是我们重新设计了程序 tang=4; num=3; document.write(tang*num); 这才能叫得上是程序设计,或许你注意到了 ``;'' 这个符号的存在,我想说的是这是 另外⼀个标准,我们不得不去遵守,也不得不去 fuck。 2.3.1 函数 记得刚开始学三⾓函数的时候,我们会写 sin 30=0.5 ⽽我们的函数也是类似于此,换句话说,因为很多搞计算机的先驱都学好了数学, 都把数学世界的规律带到了计算机世界,所以我们的函数也是类似于此,让我们做⼀个 简单的开始。 function hello(){ return document.write("hello,world"); } hello(); 当我第⼀次看到函数的时候,有些⼩激动终于出现了。我们写了⼀个叫 hello 的函 数,它返回了往页⾯中写⼊ hello,world 的⽅法,然后我们调⽤了 hello 这个函数,于是 页⾯上有了 hello,world。 function sin(degree){ return document.write(Math.sin(degree)); } sin(30); 在这⾥ degree 称之为变量,也就是可以改变的量。于是输出了 -0.9880316240928602, ⽽不是 0.5,因为这⾥⽤的是弧度制,⽽不是⾓度制。 sin(30) 16 2.3 设计和编程 2 ⽆处不在的 JAVASCRIPT 的输出结果有点类似于 sin 30。写括号的⽬的在于,括号是为了⽅便解析,这个在 不同的语⾔中可能是不⼀样的,⽐如在 ruby 中我们可以直接⽤类似于数学中的表达: 2.0.0-p353 :004 > Math.sin 30 => -0.9880316240928618 2.0.0-p353 :005 > 我们可以在函数中传⼊多个变量,于是我们再回到⼩明的问题,就会这样去写代码。 function calc(tang,num){ result=tang*num; document.write(result); } calc(3,4); 但是从某种程度上来说,我们的 calc 做了计算的事又做了输出的事,总的来说设计 上有些不好。 2.3.2 重新设计 我们将输出的⼯作移到函数的外⾯, function calc(tang,num){ return tang*num; } document.write(calc(3,4)); 接着我们⽤⼀种更有意思的⽅法来写这个问题的解决⽅案 function calc(tang,num){ return tang*num; } function printResult(tang,num){ document.write(calc(tang,num)); } printResult(3, 4) 看上去更专业了⼀点点,如果我们只需要计算的时候我们只需要调⽤ calc,如果我 们需要输出的时候我们就调⽤ printResult 的⽅法。 17 2.3 设计和编程 2 ⽆处不在的 JAVASCRIPT 2.3.3 object 和函数 我们还没有说清楚之前我们遇到过的 document.write 以及 Math.sin 的语法看上去 很奇怪,所以让我们看看他们到底是什么,修改 app.js 为以及内容 document.write(typeof document); document.write(typeof Math); typeof document 会返回 document 的数据类型,就会发现输出的结果是 object object 所以我们需要去弄清楚什么是 object。对象的定义是 ⽆序属性的集合,其属性可以包含基本值、对象或者函数。 创建⼀个 object,然后观察这便是我们接下来要做的 store={}; store.tang=4; store.num=3; document.write(store.tang*store.num); 我们就有了和 document.write ⼀样的⽤法,这也是对象的美妙之处,只是这⾥的对 象只是包含着基本值,因为 typeof story.tang="number" ⼀个包含对象的对象应该是这样⼦的。 store={}; store.tang=4; store.num=3; document.writeln(store.tang*store.num); var wall=new Object(); wall.store=store; document.write(typeof wall.store); 18 2.3 设计和编程 2 ⽆处不在的 JAVASCRIPT ⽽我们⽤到的 document.write 和上⾯⽤到的 document.writeln 都是属于这个⽆序 属性集合中的函数。 下⾯代码说的就是这个⽆序属性集中中的函数。 var IO=new Object(); function print(result){ document.write(result); }; IO.print=print; IO.print("a obejct with function"); IO.print(typeof IO.print); 我们定义了⼀个叫 IO 的对象,声明对象可以⽤ var store={}; 又或者是 var store=new Object{}; 两者是等价的,但是⽤后者的可读性会更好⼀点,我们定义了⼀个叫 print 的函数, 他的作⽤也就是 document.write,IO 中的 print 函数是等价于 print() 函数,这也就是 对象和函数之间的⼀些区别,对象可以包含函数,对象是⽆序属性的集合,其属性可以 包含基本值、对象或者函数。 复杂⼀点的对象应该是下⾯这样的⼀种情况。 var Person={name:"phodal",weight:50,height:166}; function dream(){ future; }; Person.future=dream; document.write(typeof Person); document.write(Person.future); ⽽这些会在我们未来的实际编编程中⽤得更多。 19 2.3 设计和编程 2 ⽆处不在的 JAVASCRIPT 2.3.4 ⾯向对象 开始之前先我们简化上⾯的代码, Person.future=function dream(){ future; } 看上去⽐上⾯的简单多了,不过我们还可以简化为下⾯的代码。。。 var Person=function(){ this.name="phodal"; this.weight=50; this.height=166; this.future=function dream(){ return "future"; }; }; var person=new Person(); document.write(person.name+"
"); document.write(typeof person+"
"); document.write(typeof person.future+"
"); document.write(person.future()+"
"); 只是在这个时候 Person 是⼀个函数,但是我们声明的 person 却变成了⼀个对象⼀ 个 Javascript 函数也是⼀个对象,并且,所有的对象从技术上讲也只不过是函数。这 ⾥的 +`` '' 是 HTML 中的元素,称之为 DOM,在这⾥起的是换⾏的作⽤,我们会在稍后介绍它, 这⾥我们先关⼼下 this。this 关键字表⽰函数的所有者或作⽤域,也就是这⾥的 Person。 上⾯的⽅法显得有点不可取,换句话说和⼀开始的 document.write(3*4); ⼀样,不具有灵活性,因此在我们完成功能之后,我们需要对其进⾏优化,这就是 程序设计的真谛 ------解决完实际问题后,我们需要开始真正的设计,⽽不是解决问题 时的编程。 20 2.4 其他 2 ⽆处不在的 JAVASCRIPT var Person=function(name,weight,height){ this.name=name; this.weight=weight; this.height=height; this.future=function(){ return "future"; }; }; var phodal=new Person("phodal",50,166); document.write(phodal.name+"
"); document.write(phodal.weight+"
"); document.write(phodal.height+"
"); document.write(phodal.future()+"
"); 于是,产⽣了这样⼀个可重⽤的 Javascript 对象,this 关键字确⽴了属性的所有者。 2.4 其他 Javascript 还有⼀个很强⼤的特性,也就是原型继承,不过这⾥我们先不考虑这些 部分,⽤尽量少的代码及关键字来实际我们所要表达的核⼼功能,这才是这⾥的核⼼, 其他的东西我们可以从其他书本上学到。 所谓的继承, var Chinese=function(){ this.country="China"; } var Person=function(name,weight,height){ this.name=name; this.weight=weight; this.height=height; this.futrue=function(){ return "future"; } } Chinese.prototype=new Person(); 21 2.4 其他 2 ⽆处不在的 JAVASCRIPT var phodal=new Chinese("phodal",50,166); document.write(phodal.country); 完整的 Javascript 应该由下列三个部分组成: • 核⼼ (ECMAScript)------核⼼语⾔功能 • ⽂档对象模型 (DOM)------访问和操作⽹页内容的⽅法和接⼜ • 浏览器对象模型 (BOM)------与浏览器交互的⽅法和接⼜ 我们在上⾯讲的都是 ECMAScript,也就是语法相关的,但是 JS 真正强⼤的,或者 说我们最需要的可能就是对 DOM 的操作,这也就是为什么 jQuery 等库可以流⾏的原 因之⼀,⽽核⼼语⾔功能才是真正在哪⾥都适⽤的,⾄于 BOM 真正⽤到的机会很少, 因为没有好的统⼀的标准。 ⼀个简单的 DOM ⽰例,

Red

我们需要修改⼀下 helloworld.html 添加

Red

同时还需要将 script 标签移到 body 下⾯,如果没有意外的话我们会看到页⾯上⽤ 红⾊的字体显⽰ Red,修改 app.js。 var para=document.getElementById("para"); para.style.color="blue"; 22 2.5 美妙之处 3 ⽆处不在的 CSS 接着,字体就变成了蓝⾊,有了 DOM 我们就可以对页⾯进⾏操作,可以说我们看 到的绝⼤部分的页⾯效果都是通过 DOM 操作实现的。 2.5 美妙之处 这⾥说到的 Javascript 仅仅只是其中的⼀⼩⼩部分,忽略掉的东西很多,只关⼼的 是如何去设计⼀个实⽤的 app,作为⼀门编程语⾔,他还有其他强⼤的内制函数,要学 好需要⼀本有价值的参考书。这⾥提到的只是其中的不到 20% 的东西,其他的 80% 或 者更多会在你解决问题的时候出现。 • 我们可以创建⼀个对象或者说函数,它可以包含基本值、对象或者函数。 • 我们可以⽤ Javascript 修改页⾯的属性,虽然只是简单的⽰例。 • 我们还可以去解决实际的编程问题。 3 ⽆处不在的 CSS CSS 或许你觉得他⼀点⼉也不重要,HTML 好⽐是建筑的框架,CSS 就是⽤于装修 房⼦。那么 Javascript 呢,我听到的最有趣的说法是⼩三,先让我们回到代码上来吧。 3.1 CSS 下⾯就是我们之前说到的代码,css 将 Red 三个字母变成了红⾊。

Red

只是, var para=document.getElementById("para"); para.style.color="blue"; 23 3.2 关于 CSS 3 ⽆处不在的 CSS 将字体变成了蓝⾊,CSS+HTML 让页⾯有序的⼯作着,但是 Javascript 打乱了这 些秩序,不过却也让⽣活多姿多彩,⼩三不都是这样的么 ------终于可以理解,为什么 以前⼈们对于 Javascript 没有好感了?不过这⾥要讲的是正室,也就是 CSS,这时还没 有 Javascript。 Red Fonts 3.2 关于 CSS 这不是⼀篇好的关于讲述 CSS 的书籍,所以不会去说 CSS 是怎么来的,有些东西既 然我们可以很容易从其他地⽅知道,也就不需要花太多时间去重复。诸如重构等这些的 ⽬的之⼀也在于去除重复的代码,不过有些重复是不可少的,也是有必要的,⽽通常这 些东西可能是由其他地⽅复制过来的。 到⽬前为⽌我们没有依赖于任何特殊的硬件或者是软件,对于我们来说我们最基本 的需求就是⼀台电脑,或者可以是你的平板电脑,当然也可以是你的智能⼿机,因为他 们都有个浏览器,⽽这些都是能⽤的,对于我们的 CSS 来说也不会有例外的。 CSS 是来⾃于 (Cascading Style Sheets),到今天我也没有记得他的全称,CSS 还有 ⼀个中⽂名字是层叠式样式表,翻译成什么样的可能并不是我们关⼼的内容,我们需要 关⼼的是他能做些什么。作为三剑客之⼀,它的主要⽬的在于可以让我们⽅便灵活地去 控制 Web 页⾯的外观表现。我们可以⽤它做出像淘宝⼀样复杂的界⾯,也可以像我们 的书本⼀样简单,不过如果要和我们书本⼀样简单的话,可能不需要⽤到 CSS。HTML ⼀开始就是依照报纸的格式⽽设计的,我们还可以继续⽤上⾯说到的编辑器,又或者是 其他的。如果你喜欢 DreamWeaver 那也不错,不过⼀开始使⽤ IDE 可⽆助于我们写出 良好的代码。 忘说了,CSS 也是有版本的,和 windows,Linux 内核等等⼀样,但是更新可能没有 那么频繁,HTML 也是有版本的,JS 也是有版本的,复杂的东西不是当前考虑的内容。 3.3 代码结构 对于我们的上⾯的 Red ⽰例来说,如果没有⼀个好的结构,那么以后可能就是这样 ⼦。 24 3.3 代码结构 3 ⽆处不在的 CSS

如 果 没 有 ⼀ 个 好 的 结 构

那么以后可能就是这样⼦。。。。

虽然我们看到的还是⼀样的: No Style 于是我们就按各种书上的建议重新写了上⾯的代码 CSS example

如 果 没 有 ⼀ 个 好 的 结 构

那么以后可能就是这样⼦。。。。

总算⽐上⾯好看也好理解多了,这只是临时的⽤法,当⽂件太⼤的时候,正式⼀点 的写法应该是下⾯: CSS example

如 果 没 有 ⼀ 个 好 的 结 构

那么以后可能就是这样⼦。。。。

我们需要 CSS example

如 果 没 有 ⼀ 个 好 的 结 构

那么以后可能就是这样⼦。。。。

26 3.3 代码结构 3 ⽆处不在的 CSS 然后我们有⼀个像 app.js ⼀样的 style.css 放在同⽬录下,⽽他的内容便是 .para{ font-size: 22px; color:#f00; text-align: center; padding-left: 20px; } .para2{ font-size:44px; color:#3ed; text-indent: 2em; padding-left: 2em; } 这代码和 JS 的代码有如此多的相似 var para={ font_size:'22px', color:'#f00', text_align:'center', padding_left:'20px', } ⽽ 22px、20px 以及 #f00 都是数值,因此。。 var para={ font_size:22px, color:#f00, text_align:center, padding_left:20px, } ⽬测差距已经尽可能的⼩了,⾄于这些话题会在以后讨论到,如果要让我们的编译 器更正确的⼯作,那么我们就需要⾮常多的这种符号,除⾮你乐意去理解: 27 3.4 样式与⽬标 3 ⽆处不在的 CSS (dotimes (i 4) (print i)) 总的来说我们减少了符号的使⽤,但是⽤ lisp 便带⼊了更多的括号,不过这是⼀种 简洁的表达⽅式,也许我们可以在其他语⾔中看到,或者说⽤这个去。。 \d{2}/[A-Z][a-z][a-z]/\d{4} 没有什么会⽐⼀开始不理解那是正则表达式,然后去修改上⾯的代码,为的是去从 ⼀堆数据中找出某⽇/某⽉/某年。 这门语⾔可能是为设计师⽽设计的,但是设计师⼤部分还是不懂编程的,不过相对 来说还是⽐其他语⾔好理解⼀些。 3.4 样式与⽬标 下⾯也就是我们的样式 .para{ font-size: 22px; color:#f00; text-align: center; padding-left: 20px; } 我们的⽬标就是 如 果 没 有 ⼀ 个 好 的 结 构 所以样式和⽬标在这⾥牵⼿了,问题是他们是如何在⼀起的呢?下⾯就是 CSS 与 HTML 沟通的重点所在了: 3.4.1 选择器 我们⽤到的选择器叫做类选择器,也就是 class,或者说应该称之为 class 选择器更 合适。与类选择器最常⼀起出现的是 ID 选择器,不过这个适⽤于⽐较⾼级的场合,诸 如⽤ JS 控制 DOM 的时候就需要⽤到 ID 选择器。⽽基本的选择器就是如下⾯的例⼦: 28 3.4 样式与⽬标 3 ⽆处不在的 CSS p.para{ color:#f0f; } 将代码添加到 style.css 的最下⾯会发现 ``如果没有⼀个的结构'' 变成了粉红⾊,当 然我们还会有这样的写法 p>.para{ color:#f0f; } 为了产⽣上⾯的特殊的样式,虽然不好看,但是我们终于理解什么叫层叠样式了, 下⾯的代码的重要度⽐上⾯⾼,也因此有更⾼的优先规则。 ⽽通常我们可以通过⼀个 p{ text-align:left; } 这样的元素选择器来给予所有的 p 元素⼀个左对齐。 还有复杂⼀点的复合型选择器,下⾯的是 HTML ⽂件 CSS example

如 果 没 有 ⼀ 个 好 的 结 构

那么以后可能就是这样⼦。。。。

还有 CSS ⽂件 29 3.5 更有趣的 CSS 3 ⽆处不在的 CSS .para{ font-size: 22px; color:#f00; text-align: center; padding-left: 20px; } .para2{ font-size:44px; color:#3ed; text-indent: 2em; padding-left: 2em; } p.para{ color:#f0f; } div#content p { font-size:22px; } 3.5 更有趣的 CSS ⼀个包含了 para2 以及 para_bg 的例⼦

那么以后可能就是这样⼦。。。。

我们只是添加了⼀个⿊⾊的背景 .para_bg{ background-color:#000; } 重新改变后的⽹页变得⽐原来有趣了很多,所谓的继承与合并就是如上⾯的例⼦。 30 4 ⽆处不在的三剑客 我们还可以⽤ CSS3 做出有趣的效果,⽽这些并不在我们的讨论范围⾥⾯,因为我 们讨论的是 be a geek。 或许我们写的代码都是那么的简单,从 HTML 到 Javascript,还有现在的 CSS,只 是有⼀些东西才是核⼼的,⽽不是去考虑⼀些基础的语法,基础的东西我们可以从实践 的过程中⼀⼀发现。但是我们可能发现不了,或者在平时的使⽤中考虑不到⼀些有趣的 ⽤法或者说特殊的⽤法,这些可以从观察⼀些⽐较好的设计的代码中学习到。复杂的东 西可以变得很简单,简单的东西也可以变得很复杂。 4 ⽆处不在的三剑客 这时我们终于了解了我们的三剑客,就这么可以结合到⼀起了,HTML+Javascript +CSS 是这⼀切的基础。⽽我们⽤到的其他语⾔如 PHP、Python、Ruby 等等的最后都会 变成上⾯的结果,当然还有 Coffeescript 之类的语⾔都是以此为基础,这才是我们需要 的知识。 4.1 Hello,Geek 有了⼀些些的基础之后,我们终于能试着去写⼀些程序了。也是时候去创建⼀个像 样的东西,或许你在⼀些见⾯设计⽅⾯的书籍看过类似的东西,可能我写得也没有那些 内容好,只是这些都是⼀些过程。过去我们都是⼀点点慢慢过来的,只是现在我们也是 如此,技术上的⼀些东西,事实上⼤家都是知道的。就好⽐我们都觉得我们可以开个超 市,但是如果让我们去开超市的话,我们并不⼀定能赚钱。 学习编程的⽬的可能不在于我们能找到⼀份⼯作,那只是在编程之外的东西,虽然 确实也是很确定的。但是除些之处,有些东西也是很重要的。、 过去没有理解为什么会⼀些⼈会不厌其烦地去回答别⼈的问题,有时候可能会想是 ⼀种能⼒越⼤责任越⼤的感觉,但是有时候在写⼀些博客或者回答别⼈的问题的时候我 们又重新思考了这些问题,又重新学习了这些技能。所以这⾥可能说的不是关于编程的 东西⽽是⼀些编程以外的东西,关于学习或者学习以外的东西。 4.2 从源码学习 过去总会觉得学了⼀种语⾔的语法便算是学了⼀种语⾔,于是有⼀天发现到了这个 语⾔的项⽬上的时候,虽然会写上⼏⾏代码,但是却不像这语⾔的风格。于是这也是这 ⼀篇的意义所在了: 31 4.2 从源码学习 4 ⽆处不在的三剑客 4.2.1 HTML 写好 HTML 的⼀个要点在于看别⼈写的代码,这只是⼀⽅⾯,我们所说的 HTML ⽅⾯的内容可能不够多,原因有很多,很多东西都需要在实战中去解决。读万卷书和⾏ 万⾥路,分不清哪个有重要的意义,但是如果可以同时做好两个的话,成长会很快的。 写好 HTML 应该会有下⾯的要点 • 了解标准及遵守绝⼤多数标准 • 注重可读性,从 ID 及 CLASS 的命名 • 关注 SEO 与代码的联系 或许在这⽅⾯我也算不上很了解,不过按笔者的经验来说,⼤致就是如此。 多数情况下我们的 HTML 是类似于下⾯这样⼦的
{% nevercache %} {% include "includes/user_panel.html" %} {% endnevercache %}
{% block right_panel %} {% ifinstalled mezzanine.twitter %} {% include "twitter/tweets.html" %} {% endifinstalled %} {% endblock %}
换句话说 HTML 只是基础,⽽不是⽇常⽤到的。我们的 HTML 是由 template ⽣成 的,我们可以借助于 mustache.js 又或者是 angluarjs 之类的 js 库来⽣成最后的 HTML, 所以这⾥只是⼀个开始。 还需要了解的⼀部分就是 HTML 的另外⼀个重要的部分,DOM 树形结构 32 4.3 DOM 树形结构图 4 ⽆处不在的三剑客 4.3 DOM 树形结构图 4.3.1 javascript 这⾥以未压缩的 jQuery 源码和 zepto.js 作⼀个⼩⼩的⽐较,zepto.js 是兼容 jQuery 的,因此我们举⼏个有意思的函数作⼀简单的⽐较,关于源码可以在官⽹上下载到。 在 zepto.js 下⾯判断⼀个值是否是函数的⽅⾯如下, function isFunction(value) { return type(value) == "function" } ⽽在 jQuery 下⾯则是这样的 isFunction: function( obj ) { return jQuery.type(obj) === "function"; }, ⽽他们的⽤法是⼀样的,都是 $.isFunction(); jQuery 的作法是将诸如 isFunction,isArray 这些函数打包到 jQuery.extend 中,⽽ zepto.js 的则是也是这样的,只不过多了⼀⾏ $.isFunction = isFunction 遗憾的是我也没去了解过为什么,之前我也没有看过这些库的代码,所以这个问题 就要交给读者去解决了。jQuery ⾥⾯提供了函数式编程接⼜,不过 jQuery 更多的是构 建于 CSS 选择器之上,对于 DOM 的操作⽐ javascript ⾃⾝提供的功能强⼤得多。如果 我们的⽬的在于更好的编程,那么可能需要诸如 Underscore.js 之类的库。或许说打包 ⾃⼰常⽤的函数功能为⼀个库,诸如 jQuery function isFunction(value) { return type(value) == "function" } function isWindow(obj) { return obj != null && obj == obj.window } function isDocument(obj) { return obj != null && obj.nodeType == obj.DOCUMENT_NODE } function isObject(obj) { return type(obj) == "object" } 我们需要去了解⼀些故事背后的原因,越来越害怕 GUI 的原因之⼀,在于不知道背 后发⽣了什么,即使是开源的,我们也⽆法了解真正的背后发⽣什么了。对于不是这个 33 4.3 DOM 树形结构图 4 ⽆处不在的三剑客 ⼯具、软件的⽤户来说,开源更多的意义可能在于我们可以添加新的功能,以及免费。 如果没有所谓的危机感,以及认为⾃⼰⼀直在学习⼯具的话,可以试着去打包⾃⼰的函 数,打包⾃⼰的库。 var calc={ add: function(a,b){ return a+b; }, sub: function(a,b){ return a-b; }, dif: function(a,b){ if(a>b){ return a; }else{ return b; } } } 然后⽤诸如 jslint 测试⼀下代码。 $ ./jsl -conf jsl.default.conf JavaScript Lint 0.3.0 (JavaScript-C 1.5 2004-09-24) Developed by Matthias Miller (http://www.JavaScriptLint.com) app.js /Users/fdhuang/beageek/chapter4/src/app.js(15): lint warning: missing semicolon } ........^ 0 error(s), 1 warning(s) 于是我们需要在第 15 ⾏添加⼀个分号。 最好的⽅法还是阅读别⼈的代码,⽽所谓的别⼈指的是⼀些相对较⼤的⽹站的,有 34 4.3 DOM 树形结构图 4 ⽆处不在的三剑客 好的开发流程,代码质量也不会太差。⽽所谓的复杂的代码都是⼀步步构建上去的,罗 马不是⼀天建成的。 有意思的是多数情况下,我们可能会⽤原型去开发我们的应⽤,⽽这也是我们需要 去了解和掌握的地⽅, function Calc(){ } Calc.prototype.add=function(a,b){ return a+b; }; Calc.prototype.sub=function(a,b){ return a-b; }; 我们似乎在这⾥展⽰了更多的 Javascript 的⽤法,但是这不是⼀好的关于 Javascript 的介绍,有⼀天我们还要⽤诸如 qunit 之类的⼯具去为我们的 function 写测试,这时就 是⼀个更好的开始。 如果我们乐意的话,我们也可以构建⼀个类似于 jQuery 的框架,以⽤来学习。 作为⼀门编程语⾔来说,我们学得很普通,在某种意义上来说算不上是⼀种⼊门。 但是如果我们可以在其他的好书在看到的内容,就没有必要在这⾥进⾏复述,⽬的在于 ⼀种学习习惯的养成。 4.3.2 CSS CSS 有时候很有趣,但是有时候有很多我们没有意识到的⽤法,这⾥以 Bootstrap 为例,这是⼀个不错的 CSS 库。最令⼈兴奋的是没有闭源的 CSS,没有闭源的 JS,这 也就是前端好学习的地⽅所在了,不过这是⼀个开源的 CSS 库,虽然是这样叫的,但是 称之为 CSS 库显然不合适。 a, a:visited { text-decoration: underline; } a[href]:after { content: " (" attr(href) ")"; 35 4.4 CSS 盒模型图 4 ⽆处不在的三剑客 } abbr[title]:after { content: " (" attr(title) ")"; } a[href^="javascript:"]:after, a[href^="#"]:after { content: ""; } 这⾥有⼀些有趣的,值得⼀讲的 CSS ⽤法。 • 伪类选择器, 如 a:visited 这样需要其他条件来对元素应⽤样式,⽤于已访问的链 接。 • 属性选择器, 如 a[href] 这样当 a 元素存在 href 这样的属性的时候来寻找应⽤元素。 其他的还需要去好好了解的就是 CSS 的盒模型,作为 CSS 的基⽯之⼀。 4.4 CSS 盒模型图 诸如 *{ margin: 0px; padding: 0px; font-family: Helvetica; } 这样的通⽤器⽤来进⾏全局选择的⼯具和我们⽤于抵消某个 body 对于⼦选择器的 影响⼀样值得注意得多。 4.5 笔记 写博客似乎是⼀个不错的好习惯,作为⼀个不是很优秀的写⼿。对于来说,有时候 发现原来能教会别⼈对于⾃⼰的能⼒来说算是⼀种肯定。有些时候教会别⼈才算是⾃⼰ 学会的表现,总会在项⽬上的时候需要⾃⼰去复述⼯作的⼀个过程,我们需要整理好我 们的思路才能带给别⼈更多的收获。我们的笔记上总会留下⾃⼰的学习的⼀些过程,有 些时候我们想要的只是⼀点点的⿎励,有时是诸如评论⼀类,有时可能是诸如访问量。 36 5 GNU/LINUX 更多的可能是我们可以重新整理⾃⼰的知识,好好复习⼀下,以便于好好记住,写出来 是⼀个好的过程。 ⽆处不在的三剑客就这样到了这⾥,写得似乎很多也很少,但是还是没有做出来⼀ 个东西,于是我们朝着这样⼀个⽅向前进。 5 GNU/Linux 5.1 什么是 Linux Linux 是⼀种⾃由和开放源码的类 UNIX 操作系统内核。⽬前存在着许多不同的 Linux 发⾏版,可安装在各种各样的电脑硬件设备,从⼿机、平板电脑、路由器和影⾳ 游戏控制台,到桌上型电脑,⼤型电脑和超级电脑。Linux 是⼀个领先的操作系统内核, 世界上运算最快的 10 台超级电脑运⾏的都是基于 Linux 内核的操作系统。 Linux 操作系统也是⾃由软件和开放源代码发展中最著名的例⼦。只要遵循 GNU 通⽤公共许可证, 任何⼈和机构都可以⾃由地使⽤ Linux 的所有底层源代码,也可以⾃ 由地修改和再发布。严格来讲,Linux 这个词本⾝只表⽰ Linux 内核,但在实际上⼈ 们已经习惯了⽤ Linux 来形容整个基于 Linux 内核,并且使⽤ GNU ⼯程各种⼯具和 数据库的操作系统(也被称为 GNU/Linux)。通常情况下,Linux 被打包成供桌上型 电脑和服务器使⽤的 Linux 发⾏版本。⼀些流⾏的主流 Linux 发⾏版本,包括 Debian (及其衍⽣版本 Ubuntu),Fedora 和 openSUSE 等。Linux 得名于电脑业余爱好者 Linus Torvalds。 ⽽不是如百度百科所讲的Linux 操作系统是 UNIX 操作系统的⼀种克隆系统。它诞 ⽣于 1991 年的 Linux 桌⾯ [1]10 ⽉ 5 ⽇(这是第⼀次正式向外公布的时间)。以后借助 于 Internet ⽹络,并通过全世界各地计算机爱好者的共同努⼒,已成为今天世界上使⽤ 最多的⼀种 UNIX 类操作系统,并且使⽤⼈数还在迅猛增长。 Linux 只是个内核,⽽不是操作系统,所以在这我们再理解⼀下操作系统是由什么 组成的。 5.2 操作系统 操作系统(英语:Operating System,简称 OS)是管理计算机硬件与软件资源的计 算机程序,同时也是计算机系统的内核与基⽯。操作系统需要处理如管理与配置内存、 决定系统资源供需的优先次序、控制输⼊与输出设备、操作⽹络与管理⽂件系统等基本 事务。操作系统也提供⼀个让⽤户与系统交互的操作界⾯。操作系统的型态⾮常多样, 37 5.2 操作系统 5 GNU/LINUX 不同机器安装的操作系统可从简单到复杂,可从⼿机的嵌⼊式系统到超级计算机的⼤型 操作系统。许多操作系统制造者对它涵盖范畴的定义也不尽⼀致,例如有些操作系统集 成了图形⽤户界⾯ (GUI),⽽有些仅使⽤命令⾏界⾯ (CLI),⽽将 GUI 视为⼀种⾮必要 的应⽤程序。 操作系统位于底层硬件与⽤户之间,是两者沟通的桥梁。⽤户可以通过操作系统的 ⽤户界⾯,输⼊命令。操作系统则对命令进⾏解释,驱动硬件设备,实现⽤户要求。以 现代标准⽽⾔,⼀个标准 PC 的操作系统应该提供以下的功能: • 进程管理(Processing management) • 内存管理(Memory management) • ⽂件系统(File system) • ⽹络通信(Networking) • 安全机制(Security) • ⽤户界⾯(User interface) • 驱动程序(Device drivers) ⽽让我们来看⼀下两者之间的不同之处,这是⼀张 linux 的架构图我们可以发现内 核只是位于底层。 5.2.1 Linux 架构图 5.2.1.1 ⽤户模式 应⽤程序(sh、vi、OpenOffice.org等) 复杂库(KDE、glib 等)简单库(opendbm、sin 等) C 库(open、fopen、socket、exec、calloc 等) 5.2.1.2 内核模式 • 系统中断、调⽤、错误等软硬件消息 • 内核(驱动程序、进程、⽹络、内存管理等) • 硬件(处理器、内存、各种设备) 我们可以发现,由 linux 内核 +shell 可以构成⼀个操作系统,⽽ linux 本⾝只是个 内核,也就是图中的内核模式,负责控制系统的这些部分。也就是我们可以发现,Linux 内核构成了⼀个操作系统除⽤户界⾯以外的部分,⽽ shell 就是这最后的⽤户界⾯。 ⽽ linux 内核以外的部分就是由 GNU 计划构成的。 38 5.2 操作系统 5 GNU/LINUX 5.2.2 Shell Shell 是系统的⽤户界⾯,提供了⽤户与内核进⾏交互操作的⼀种接⼜。它接收⽤户 输⼊的命令并把它送⼊内核去执⾏。 实际上 Shell 是⼀个命令解释器,它解释由⽤户输⼊的命令并且把它们送到内核。 不仅如此,Shell 有⾃⼰的编程语⾔⽤于对命令的编辑,它允许⽤户编写由 shell 命令组 成的程序。Shell 编程语⾔具有普通编程语⾔的很多特点,⽐如它也有循环结构和分⽀ 控制结构等,⽤这种编程语⾔编写的 Shell 程序与其他应⽤程序具有同样的效果 bash 是⼀个为 GNU 计划编写的 Unix shell。它的名字是⼀系列缩写:Bourne-Again SHell --- 这是关于 Bourne shell(sh)的⼀个双关语(Bourne again / born again)。Bourne shell 是⼀个早期的重要 shell,由史蒂夫·伯恩在 1978 年前后编写,并同 Version 7 Unix ⼀起发布。bash 则在 1987 年由布莱恩·福克斯创造。在 1990 年,Chet Ramey 成为了 主要的维护者。 shell 将会是我们在 GNU/linux 中经常⽤到的经常有到的⼯具之⼀,⽤来操作计算 机⽤的。在迁移到 linux 之前我们可以试⽤ cygwin 来进⾏模拟: Cygwin 是许多⾃由软件的集合,最初由Cygnus Solutions开发,⽤于各种版本 的Microsoft Windows上,运⾏UNIX 类系统。Cygwin 5.2.3 GCC GCC(GNU Compiler Collection,GNU 编译器套装),是⼀套由 GNU 开发的编程 语⾔编译器。它是⼀套以 GPL 及 LGPL 许可证所发⾏的⾃由软件,也是 GNU 计划的关 键部分,亦是⾃由的类 Unix 及苹果电脑 Mac OS X 操作系统的标准编译器。GCC(特 别是其中的 C 语⾔编译器)也常被认为是跨平台编译器的事实标准。 GCC 原名为 GNU C 语⾔编译器(GNU C Compiler),因为它原本只能处理 C 语⾔。 GCC 很快地扩展,变得可处理 C++。之后也变得可处理 Fortran、Pascal、Objective-C、 Java、Ada,以及 Go 与其他语⾔。 同 shell ⼀样,对于 GNU/linux 系统⽽⾔,GCC 的作⽤也是⽆可取代的。当然如果只 是⼀般⽤途的话,GCC 对于⼀般⽤户可能没⽤,但是在些 GNU/Linux 系统上,我们可 能就需要⾃⼰编译源码成⼆进制⽂件,⽽没有软件包,因⽽其重要性是不⾔⽽喻的。⾃ 然的如果我们⾃⼰动⼿编译 GNU/Linux 操作系统的话,我们会理解其的重要意义。有 兴趣的同学可以试⼀下:Linux From Scratch (LFS)。 39 5.2 操作系统 5 GNU/LINUX 5.2.4 启动引导程序 最后,当我们构成以上的那些之后,我们就需要⼀个引导程序,以便使系统启动, 引导进内核。 启动程序(bootloader)于电脑或其他计算机应⽤上,是指引导操作系统启动的程 序。启动程序启动⽅式与程序视应⽤机型种类。例如在普通 PC 上,引导程序通常分为 两部分:第⼀阶段引导程序位于主引导记录,⽤于引导位于某个分区上的第⼆阶段引导 程序,如 NTLDR、GNU GRUB 等。 BIOS 开机完成后,bootloader 就接⼿初始化硬件设备、创建存储器空间的映射,以 便为操作系统内核准备好 正确的软硬件环境。 简单的 bootloader 的虚拟汇编码,如其后的⼋个指令: • 0: 将 P 暂存器的值设为 8 • 1: 检查纸带 ({paper tape) 读取器,是否已经可以进⾏读取 • 2: 如果还不能进⾏读取, 跳⾄ 1 • 3: 从纸带读取器,读取⼀ byte ⾄累加器 • 4: 如为带⼦结尾,跳⾄ 8 • 5: 将暂存器的值,存储⾄ P 暂存器中的数值所指定的地址 • 6: 增加 P 暂存器的值 • 7: 跳⾄ 1 但是随着计算机操作系统越来越复杂,位于 MBR 的空间已经放不下引导操作系统 的代码,于是就有了第⼆阶段的引导程序,⽽ MBR 中代码的功能也从直接引导操作系 统变成了引导第⼆阶段的引导程序。 通常在⼀个 GNU/Linux 系统中选⽤ GNUGRUB 做为引导程序,例如 Ubuntu 就是 ⽤ GRUB2。 GNU GRUB(简称 ``GRUB'')是⼀个来⾃ GNU 项⽬的启动引导程序。GRUB 是多 启动规范的实现,它允许⽤户可以在计算机内同时拥有多个操作系统,并在计算机启动 时选择希望运⾏的操作系统。GRUB 可⽤于选择操作系统分区上的不同内核,也可⽤于 向这些内核传递启动参数。 GNU GRUB 的前⾝为 Grand Unified Bootloader。它主要⽤于类 Unix 系统;同⼤多 Linux 发⾏版⼀样,GNU 系统也采⽤ GNU GRUB 作为它的启动器。Solaris 从 10 1/06 版开始在 x86 系统上也采⽤ GNU GRUB 作为启动器。 40 5.3 从编译开始 5 GNU/LINUX 以上也就构成了⼀个简单的操作系统。 5.3 从编译开始 我们以⼀次编译开始我们的 Linux 学习之旅。 5.3.1 开始之前 • 如果你没有⽤过 GNU/Linux,我想你需要在虚拟机上安装⼀个。 • ⼀个主流的 GNU/Linux 发⾏版,如 Ubuntu,CentOS,Debian,Mint,OpenSUSE,Fedora 等等。 • 学会如何打开 shell(ps:bash,zsh,sh 等等)。 或者你也可以在 Windows 上安装 Cygwin。 5.3.2 编译 Nginx 1. 下载这个软件的源码包 wget http://nginx.org/download/nginx-1.7.4.tar.gz wget 是⼀个⽤于下载的软件,当然你也可以⽤软件,只是⽤ wget 似乎会⽐图形界 ⾯快哦。 2. 解压软件包 tar -vf nginx-1.7.4.tar.gz -vf 的意思是 Extract,也就是解压,⽽ tar 则是这个解压软件的名字。看上去似乎 ⽐ WinRAR 来得复制得多,但是你可以计时⼀下,从下载到解压完,和你⽤⿏标⽐哪 个⽐较快。 3. 到 nginx ⽬录下 这⾥需要分两部进⾏ 1). 列出所有⽂件 ls -al 41 5.3 从编译开始 5 GNU/LINUX drwxr-xr-x 15 fdhuang staff 510B Sep 2 13:44 nginx-1.7.4 -rw-r--r-- 1 fdhuang staff 798K Aug 5 21:55 nginx-1.7.4.tar.gz 2). 到 nginx-1.7.4 ⽬录 cd nginx-1.7.4 4. 配置 nginx ⼀次简单的配置如下 ./configure 当你熟练以后,你可能和我⼀样⽤下⾯的配置 (注意: ⽤下⾯的代码会出错。) ./configure --user=www --group=www --add-module=../ngx_pagespeed-1.8.3.4- beta -- add- module=../ ngx_cache_purge -- prefix=/ usr/ local/ nginx --with-pcre --with-http_spdy_module --with-http_ssl_module -- with- http_realip_module -- with- http_addition_module -- with- http_sub_module --with-http_dav_module --with-http_flv_module -- with-http_mp4_module --with-http_gunzip_module --with-http_gzip_static_module -- with-http_random_index_module --with-http_secure_link_module -- with-http_stub_status_module --with-mail --with-mail_ssl_module -- with-ipv6 过程中可能会提⽰你其中出了多少错误,⽽这时你便可以很愉快地去⽤搜索引擎搜 索他们。 5.make 这⾥就会⽤到 GCC 等等。 make 6. 运⾏ 如果运⾏顺利的话,应该可以直接 ./objs/nginx 42 5.4 包管理 5 GNU/LINUX 5.3.3 其他 1. 如果没有 wget,make,gcc 等命令的时候可以⽤类似于下⾯的⽅法安装, sudo apt-get install gcc,make,wget 2. 正常情况下⼀个开源项⽬都会有⼀个 README,会告诉你应该如何去做。 5.4 包管理 GNU/Linux 最⽅便的东西莫过于包管理了。 引⾃ OpenSUSE 官⽹的说明及图⽚1 package management 1. Linux 发⾏版⽆⾮就是⼀堆软件包 (package) 形式的应⽤程序加上整体地管理这些 应⽤程序的⼯具。通常这些 Linux 发⾏版,包括 openSUSE,都是由成千上万不 同的软件包构成的。 2. 软件包: 软件包不⽌是⼀个⽂件,内含构成软件的所有⽂件,包括程序本⾝、共享 库、开发包以及使⽤说明等。 3. 元数据 (metadata) 包含于软件包之中,包含软件正常运⾏所需要的⼀些信息。软 件包安装之后,其元数据就存储于本地的软件包数据库之中,以⽤于软件包检索。 4. 依赖关系 (dependencies) 是软件包管理的⼀个重要⽅⾯。实际上每个软件包都会 涉及到其他的软件包,软件包⾥程序的运⾏需要有⼀个可执⾏的环境(要求有其 他的程序、库等),软件包依赖关系正是⽤来描述这种关系的。 Linux 下的软件包通常是以下三种格式: 43 6 ARDUINO • tgz - tar gzip ⽂件。这类⽂件是基本的压缩软件包,可以容纳软件包维护者认为有 ⽤的所有的东西。此格式除本⾝的压缩格式外,并没有有关软件包内容的标准。 • deb - 此格式的软件包常⽤于 Debian 系统,是标准的 Debian 软件包格式。 • rpm - 此格式由 Red Hat Linux 所创建,并经由 LSB 标准化,现已为众多 Linux 发 ⾏版所采⽤,是⼀个优秀的软件包格式。openSUSE 即是⽤此格式。更多信息可以 参阅此处。 所以这就需要能⾃动解决依赖关系的软件包管理器。软件包管理系统就是⼀ 个⼯具集,为系统提供⼀个统⼀的安装、升级、删除软件的⽅式。 6 Arduino 6.1 极客的玩具 Arduino,是⼀个开放源代码的单芯⽚微电脑,它使⽤了 Atmel AVR 单⽚机,采⽤ 了基于开放源代码的软硬件平台,构建于开放源代码 simple I/O 接⼜板,并且具有使⽤ 类似 Java,C 语⾔的 Processing/Wiring 开发环境。 Arduino 开发板封装了常⽤的库到开发环境中,可以让⽤户在开发产品时,将主要 注意⼒放置于所需要实现的功能上,⽽不是开发的过程中。在为 Arduino 写串⼜程序 时,我们只需要⽤ Serial.begin(9600) 以 9600 的速率初始化串⼜,⽽在往串⼜发送数 据时,可以⽤ Serial.write(`1') 的⽅式向串⼜发送字串'1'。 Arduino 的出现很⼤程度上降低了电⼦制作的难度,初学者甚⾄不懂编程也可以上 ⼿ Arduino, 这也是它的魅⼒所在。 6.2 硬件熟悉 为 了 满 ⾜ 各 种 需 求,Arduino 团 队 设 计 了 很 多 款 开 发 板, 如 UNO、Pro mini、 Mega2560、Due、Leonardo、Yún、Pro、Fio、Nano 等⼗⼏种开发板和扩展板。最适 合初学者的⼀款是 Arduino UNO 。下图是 Arduino UNO 的外观图: 44 6.3 开发环境 6 ARDUINO Uno 注:后⾯的程序也是基于 Arduino UNO 开发板来讲解。 6.3 开发环境 Arduino 开发环境如上图,⼗分简洁,编写代码需要知道两个基本的函数: 45 6.4 点亮⼀个 LED 6 ARDUINO void setup(){ } void loop(){ } ``setup()'' 函数⽤于初始化(如 GPIO 初始化,串⼜初始化,定时器初始化等)特 点是只执⾏⼀次;``loop()'' 函数是⼀个死循环,可以看做 C 语⾔的 ``while(1)'' 函数。 6.4 点亮⼀个 LED 对初学者来说,点亮 led 已成为⼊门必修课,使⽤ Arduino 控制 led ⼗分简单,并 且很容易理解。使⽤到的函数: • pinMode(pin,mode) • digitalWrite(pin,value) 上⼀段代码分析: int led=13; void setup() { pinMode(led,OUTPUT); } void loop() { digitalWrite(led,HIGH); delay(1000); digitalWrite(led,LOW); delay(1000); } 该程序实现 Arduino 单⽚机 13 号引脚以 1S 时间电平翻转,如果外接⼀个 led,就 可以看到 led 以 1S 的间隔闪烁;函数 ``pinMode()'' 有两个参数 pin、value,pin 参数 ⽤来指定引脚号, 本程序中设置为 13 号引脚,mode ⽤于设置引脚模式,有三个值: 46 6.5 串⼜通信 6 ARDUINO • ``INPUT'' • ``OUTPUT'' • ``INPUT_PULLUP'' 表⽰让某⼀个 IO 引脚作输⼊,反之, • ``OUTPUT'' 则使⼀个 IO 引脚做输出 • ``INPUT_PULLUP'' 则配置⼀个 IO 引脚具有上拉输⼊功能 (上拉电阻的⽬的是为 了保证在⽆信号输⼊时输⼊端的电平为⾼电平),从英⽂意思也能很直观的看出来。 理解了 ``pinMode()'' 函数,``digitalWrite()'' 就很容易理解啦,value 的取值有两个 ``HIGH''、``LOW'',``HIGH'' 表⽰让某⼀个引脚输出⾼电平,反之,``LOW'' 则使某 ⼀个引脚输出低电平。程序中还是⽤到 ``delay(ms)'' 函数,它表⽰延时多少毫秒,例如 延时 500 ms , 直接调⽤ ``delay(500);'' 就可以了。 如果你仔细查看我的描述,你会发现我没有讲 13 号引脚怎么来的,是这样的: Arduino 团队为了简化对引脚描述,对每个引脚都进⾏了编号,以 UNO 开发板为例,可 以发现开发板排座的附近有对应的⽩颜⾊的数字,那便是所有的引脚编号,A0~A5 是 6 路 ADC 输⼊引脚,0-13 表⽰ 13 路基本 IO,数字前⾯的 ``~'' 表⽰该引脚具有 PWM 功 能。如果要使⽤某⼀引脚,只要知道引脚编号就可⾏操作 例如 ``digitalWrite(2,LOW)'' 表⽰向 2 号引脚输出低电平。其他操作类似,是不是 so easy -! 6.5 串⼜通信 使 ⽤ 到 的 基 本 函 数:+ Serial.begin() + Serial.write() + Serial.read() + Se- rial.available() 在此项⽬中需要使⽤串⼜,Arduino 串⼜初始化使⽤ ``Serial.begin(9600);'', 其 传 输 波 特 率 为 9600, 其 他 波 特 率 也 ⾏, 函 数 位 于 ``setup()'' 中, 之 后 可 以 使 ⽤ ``Serial.read()''、``Serial.write()'' 读⼊⼀个字符,输出⼀个字符,使⽤ ``Serial.print()'' 输出字符串. 代码如下: char ch='1'; void setup() { Serial.begin(9600); 47 7 PYTHON } void loop() { Serial.write(ch); while(1) { if(Serial.available()) { ch = Serial.read(); Serial.print(ch); } } } 以上程序实现字符的输出 (Serial.write(),Serial.print()) 和读⼊ (Serial.read())。如 果需要了解更多,可以参考:Arduino 官⽹ 7 Python 作为⼀门计算机语⾔来说,Python 会有下⾯的特点。 • 语⾔学习起来容易 • 解决⽣活中的实际问题 • ⽀持多学科 我们可以和其他不是脚本语⾔的语⾔进⾏⼀个简单的对⽐,如 C,你需要去编译去 运⾏,有时候还需要解决跨平台问题,本来你是在你的 Windows 上运⾏得好好的,但是 有⼀天你换了⼀个 Mac 电脑的时候,问题变得很棘⼿,你甚⾄不知道怎么去解决问题。 我没有⽤过 MFC,听说很⽅便,但是在其他平台下就没有⼀个好的解决⽅案。这⾥可能 跑得有点远,但是不同的⽤户可能在不同的平台上,这也就是脚本语⾔的优势所在了。 7.1 代码与散⽂ 你可能听过,也可能了解过,不过在这⾥我们可能不会去讲述那些基础的语法的东 西,我们想说的是代码格式的重要性,在 html 中你可以这样去写你的代码 48 7.1 代码与散⽂ 7 PYTHON This is a Title

flakjfaklfjalfa

又或者是 js 的 minify,它可能会使你的代码看起来像是这样的: function NolTracker(b,a){this.pvar=b;this.mergeFeatures(a)} 可能的是如果是 python 的话,你可能会遇到下⾯的问题。。 File "steps.py", line 10 try: ^ IndentationError: expected an indented block 如果你对 JSLint、Lint 这类的⼯具有点印象的话,你也可以认为 python 集成了这 类⼯具。整洁的代码⾄少应该看上去要有整洁的⾐服,就好像是我们看到的⼀个⼈⼀样, ⽽后我们才会有⼀个好的印象。更主要的⼀点是代码是写给⼈看的,⽽⾐服更多的时候 对于像我这样的⼈来说,他⾸先应该是要保暖的,其次对于⼀个懒的⼈来说。。。 程序应该是具有可读性的短⽂,它将在计算机上执⾏,从⽽解决某些问题 我们需要去读懂别⼈的代码,别⼈也需要去读懂我们的代码。计算机可以⽆条件地 执⾏你那未经编排过的程序,但是⼈就不是如此了。 var calc={add: function(a,b) {return a +b;},sub: function(a,b) {return a-b;},dif: function(a,b){if(a>b){return a;}else{return b;}}} 上⾯的代码相对于下⾯的代码可读性没有那么多,但是计算机可以⽆条件地执⾏上 ⾯的代码。上⾯的代码对于⽹络传输来说是好的,但是对于⼈来说并不是如此,我们需 要⼀些⼯具来辅助我们去读懂上⾯的代码。如果代码上写得没有⼀点可读性,诸如函数 命名没有多少实际意义,如果我们把前⾯的函数就成这样: var c={ a: function(a,b){ return a+b; 49 7.1 代码与散⽂ 7 PYTHON }, s: function(a,b){ return a-b; }, d: function(a,b){ if(a>b){ return a; }else{ return b; } } } 那么只有在我们理解了这个函数是⼲什么之后才能理解函数是⼲什么,⽽不是光看 函数名就可以了。 在 Javascript 解决⼀个函数的办法有很多,在其他⼀些语⾔如 Ruby 或者 Perl 中也 是如此,解决问题的办法有很多,对于写代码的⼈来说是⼀个享受的过程,但是对于维 护的⼈来说并⾮如此。⽽这个和 Python 的思想不是很⼀致的是,Python 设计的理念是 对于特定的问题,只要有⼀种最好的⽅法来解决就够了 可读性的代码在今天显得⽐以前重要的多,以前写程序的时候我们需要去考虑使⽤ 汇编或者其他⼯具来提⾼程序的效率。 .global _start .text _start: # write(1, message, 13) mov $1, %rax # system call 1 is write mov $1, %rdi # file handle 1 is stdout mov $message, %rsi # address of string to output mov $13, %rdx # number of bytes syscall # invoke operating system to do the write # exit(0) 50 7.1 代码与散⽂ 7 PYTHON mov $60, %rax # system call 60 is exit xor %rdi, %rdi # we want return code 0 syscall # invoke operating system to exit message: .ascii "Hello, world\n" 所以上⾯的代码的可读性在今天新⽣⼀代的程序员来说可能没有那么容易理解。芯 ⽚运⾏的速度越来越快,在程序上我们也需要⼀个越来越快的解决⽅案,⽽所谓的越来 越快的解决⽅案指的不是运⾏速度上,⽽是开发速度上。如果你没有办法在同样时间内 开发出更好的程序,那么你就可能输给你的竞争对⼿。 7.1.1 开始之前 我们终于又从⼀种语⾔跳到了另外⼀种语⾔,我们可能习惯了⼀种模式,⽽不敢于 去尝试新的东西,这些或许是我们的⼀些习惯又或者是因为害怕等等。 作为另外⼀个跨平台能⼒很强的语⾔,这⾥说的是与 Javascript、HTML ⽐较,或 许你会觉得 C 算是最好的,但是我们这⾥讨论更多的是脚本语⾔,也就是直接可以运⾏ 的。在现在主流的⼤多数移动平台上,python 也有良好的⽀持,如 Android,IOS,只是 这些算是类 Unix 系统内核,python 还⽀持之前 Nokia 的 Symbian。 开始之前我们需要确认我们的平台上已经有了 python 环境,也就是可以运⾏下⾯ 的 Hello,World,你可以在⽹上很多地⽅看到,最简单的地⽅还是到官⽹,又或者是所 ⽤移动平台的 store 下载。 7.1.2 Python 的 Hello,World Python 的 Hello,World 有两种形式,作为⼀种脚本语⾔来说,Javascript 也是⼀种 脚本语⾔,只是两者之间有太多的不同之处,每个⼈都会有不同的选择对于⼀种语⾔⽤ 来作为其的习惯。于是这就是我们的 print "Hello,World" 当我们把我们的脚本在 shell 环境下运⾏时 >>> print "Hello,world" File "", line 1 print "Hello,world" 51 7.2 算法 7 PYTHON ^ IndentationError: unexpected indent >>> print "Hello,world" Hello,world >>> 如果你没有预料到缩进带来的问题的时候,这就是⼀个问题了。 和我们在 Javascript 或者是 CSS ⾥⾯⼀样,我们也可以⽤⼀个⽂件的⽅式来写⼊我 们的代码,⽂件后缀名是 py,所以创建⼀个 helloworld.py,输⼊上⾯的代码,然后执⾏ python helloworld.py ⼀个理想的结果,或许你试过 C 语⾔的 helloworld,如果了解过 GCC 的话应该是可 以这样的: ./a.out 也就是执⾏编译完后的程序,需要注意的是 helloworld.py 没有编译,不过也会输出 Hello,world 7.1.3 我们想要的 Hello,World 我们想展⽰的是如何结合前⾯学习的内容做⼀个更有意思的 Hello,World。 import cherrypy class HelloWorld(object): def index(self): return "Hello World!" index.exposed = True cherrypy.quickstart(HelloWorld()) 7.2 算法 我们需要去了解算法 (algorithm),引经据典的话就是这样⼦: 52 7.3 实⽤主义哲学 9 HTTP 与 RESTFUL a process or set of rules to be followed in calculations or other problem- solving operations, especially by a computer 也就是计算或其他解决问题的操作需要遵循的⼀个过程或者⼀套规则,书上还提到 的说法是 ------解决问题的诀窍,让我想起了 hack ⼀词。我们总会去想某些东西是否有 ⼀个更快的计算⽅法,有时候在处理某些问题上也显⽰了⼀个好的算法的重要性。 7.3 实⽤主义哲学 7.4 包管理 8 Raspberry Pi 8.1 Geek 的盛宴 Raspberry Pi 是⼀款针对电脑业余爱好者、教师、⼩学⽣以及⼩型企业等⽤户的迷 你电脑,预装 Linux 系统,体积仅信⽤卡⼤⼩,搭载 ARM 架构处理器,运算性能和智 能⼿机相仿。在接⼜⽅⾯,Raspberry Pi 提供了可供键⿏使⽤的 USB 接⼜,此外还有千 兆以太⽹接⼜、SD 卡扩展接⼜以及 1 个 HDMI ⾼清视频输出接⼜,可与显⽰器或者 TV 相连。 Linux 是⼀套免费使⽤和⾃由传播的类 Unix 操作系统,是⼀个基于 POSIX 和 UNIX 的多⽤户、多任务、⽀持多线程和多 CPU 的操作系统。它能运⾏主要的 UNIX ⼯具软 件、应⽤程序和⽹络协议。它⽀持 32 位和 64 位硬件。Linux 继承了 Unix 以⽹络为核 ⼼的设计思想,是⼀个性能稳定的多⽤户⽹络操作系统。 Raspberry Pi 相⽐于⼀般的 ARM 开发板来说,由于其本⾝搭载着 Linux 操作系统, 可以⽤诸如 Python、Ruby 或 Bash 来执⾏脚本,⽽不是通过编译程序来运⾏,具有更 ⾼的开发效率。 8.2 Raspberry Pi 初始化 9 HTTP 与 RESTful 9.1 你所没有深⼊的 HTTP Internet 有两个核⼼协议: IP 和 TCP,这样讲述起来似乎会很漫长。 基本概念 53 9.1 你所没有深⼊的 HTTP 9 HTTP 与 RESTFUL 超⽂本传输协议 (HTTP-Hypertext transfer protocol) 是⼀种详细规定了浏 览器和万维⽹服务器之间互相通信的规则,通过因特⽹传送万维⽹⽂档的数 据传送协议。 • HTTP 是⽤于客户端与服务端之间的通信。 • 传输层的 TCP 是基于⽹络层的 IP 协议的, ⽽应⽤层的 HTTP 协议又是基于传输层 的 TCP 协议的。 注意: HTTP 协议只规定了客户端与服务端的通信规则,⽽没有规定其通讯协议,只 是现在的⼤部分实现都是将 TCP 作为通讯协议。 9.1.1 打开⽹页时发⽣了什么 简单地来说,当我们在浏览器上输⼊ URL 的敲下回车的时候。 • 浏览器需要查找域名2 的 IP,从不同的缓存直⾄ DNS 服务器。 • 浏览器会给 web 服务器发送⼀个 HTTP 请求 • 服务器 ``处理'' 请求 • 服务器发回⼀个 HTML 响应 • 浏览器渲染 HTML 到页⾯。 在StackOverflow上有⼀个这样的回答会⽐较详细。 • browser checks cache; if requested object is in cache and is fresh, skip to #9 • browser asks OS for server's IP address • OS makes a DNS lookup and replies the IP address to the browser • browser opens a TCP connection to server (this step is much more complex with HTTPS) • browser sends the HTTP request through TCP connection • browser receives HTTP response and may close the TCP connection, or reuse it for another request • browser checks if the response is a redirect (3xx result status codes), authorization request (401), error (4xx and 5xx), etc.; these are handled differently from normal responses (2xx) • if cacheable, response is stored in cache • browser decodes response (e.g. if it's gzipped) 54 9.1 你所没有深⼊的 HTTP 9 HTTP 与 RESTFUL • browser determines what to do with response (e.g. is it a HTML page, is it an image, is it a sound clip?) • browser renders response, or offers a download dialog for unrecognized types 忽略⼀些细节便剩下了 1. 从浏览器输⼊ URL 2. 浏览器找到服务器,服务器返回 HTML ⽂档 3. 从对应的服务器下载资源 说说第⼀步,开始时我们输⼊的是 URI(统⼀资源标识符,Uniform Resource Identi- fier),它还有另外⼀个名字叫统⼀资源定位器 (URL3,Uniform Resource Locator)。 9.1.2 URL 组成 ⽹址算是 URL 的⼀个俗称,让我们来看看⼀个 URL 的组成,以 HTTP 版 IOT 中的 URL 为例。 http://b.phodal.com/athome/1 开始之前,我们需要标出 URL 的 80 端⼜以及 json ⽂件的全称,那么上⾯的⽹址就 会变成 http://b.phodal.com:80/athome/1.json 那么对于这个 URL 的就有下⾯⼏部分组成 • http:// http 说的是这个 URL ⽤的是 HTTP 协议,⾄于//是⼀个分隔符,⽤法 和 C 语⾔中的; ⼀样。这样的协议还可以是 coap,https,ftp 等等。 • b 是⼦域名,⼀个域名在允许的情况下可以有不限数量的⼦域名。 • phodal.com 代表了⼀个 URL 是 phodal.com 下⾯的域名 • 80 80 是指 80 端⼜,默认的都是 80,对于⼀个不是 80 端⼜的 URL 应该是这样 的 http://iot-coap.phodal.com:8896/ • athome 指的是虚拟⽬录部分,或者⽂件路径 • 1.json 看上去就是⼀个⽂件名,然⽽也代表着这是⼀个资源。 对就⼀个稍微复杂点的例⼦就是 http://designiot.phodal.com/# 你所没有深⼊的 http 55 9.2 ⼀次 HTTP GET 请求 9 HTTP 与 RESTFUL 这⾥的 # 后⾯是锚部分,如果你打开这个 URL 就会发现会直接跳转到相应的锚部 分,对就于下⾯这样的⼀个例⼦来说 http://www.phodal.com/search/?q=iot&type=blog ? 后⾯的 q=iot&type=blog 的部分是参数部分,通常⽤于查询或者、搜索。 9.2 ⼀次 HTTP GET 请求 当我们打开最⼩物联⽹系统的⼀个页⾯时,如http://b.phodal.com/athome/1.json 我们在浏览器上看到的结果是 [ { "id": 1, "temperature": 19, "sensors1": 31, "sensors2": 7.5, "led1": 0 } ] 只是我们看到的是结果,忽略了这其中的过程,于是我们⽤ curl4 命令来看看详细 的情况。 curl -I -s http://b.phodal.com/athome/1.json 出于某种原因考虑,删去了其中⼀些元素,剩下下⾯这些。 HTTP/1.1 200 OK Content-Type: application/json Date: Fri, 05 Sep 2014 15:05:49 GMT [{"id": 1,"temperature": 19,"sensors1": 31,"sensors2": 7.5,"led1": 0}] 我们⽤ curl 命令向服务器发起了 GET 请求,服务器返回了上⾯的结果。 56 9.2 ⼀次 HTTP GET 请求 9 HTTP 与 RESTFUL 9.2.1 HTTP 响应 ⼀个 HTTP 响应由三部分组成 • 状态⾏ (状态码) • 消息报头 (响应报头) • 响应正⽂ (消息体) 9.2.1.1 HTTP 响应状态码 在上⾯的结果中,状态⾏是 HTTP/1.1 200 OK 返回的状态码是 200,OK 是状态码的原因短语。 如果是⼀个跳转的页⾯,它就可能是下⾯的结果: HTTP/1.0 301 MOVED PERMANENTLY Date: Mon, 08 Sep 2014 12:04:01 GMT Content-Type: text/html; charset=utf-8 HTTP Status 有五种状态,⽽这五种状态又有所细分,提⼀下这五种状态,详细可 参见http://zh.wikipedia.org/wiki/HTTP%E7%8A%B6%E6%80%81%E7%A0%81 • 1xx 消息 • 2xx 成功 • 3xx 重定向 • 4xx 客户端错误 • 5xx 服务器错误 9.2.1.2 HTTP 响应响应报头 在这次响应中,返回了两个报头,即 Content-Type: application/json Date: Fri, 05 Sep 2014 15:05:49 GMT Content-Type 和 Date,在这⾥的 Context-Type 是 application/json,⽽通常情况下 我们打开⼀个⽹站时,他的 Content-Type 应该是 text/html。 Content-Type: text/html; Content-Type 是最重要的报头。 57 9.2 ⼀次 HTTP GET 请求 9 HTTP 与 RESTFUL 9.2.1.3 HTTP 响应响应正⽂ 正⽂才是我们真正想要的内容,上⾯的都是写给浏览器 看的,⼀般的⼈不会去关注这些。 [{"id": 1,"temperature": 19,"sensors1": 31,"sensors2": 7.5,"led1": 0}] 通常这是以某种格式写的,在这⾥是以 JSON 写的,⽽对于⼀个⽹站的时候则是 HTML,如: Document 那么这次 GET 请求返回的就是: HTTP/1.0 200 OK Date: Mon, 08 Sep 2014 12:04:01 GMT Content-Type: text/html; charset=utf-8 Document [{"id":1,"temperature":19,"sensors1":31,"sensors2":7.5,"led1": 0}] 虽然与第⼀次请求的结果在游览器上看似乎是⼀样的 (ps: 可能有微⼩的差异),然 ⽽其本质是不同的。 58 9.3 REST 10 设计 RESTFUL API 推荐及参考书⽬: •《Web 性能权威指南》 •《图解 HTTP》 •《RESTful Web Services Cookbook》 •《RESTful Web APIs》 9.3 REST REST 从资源的⾓度来观察整个⽹络,分布在各处的资源由 URI 确定,⽽客 户端的应⽤通过 URI 来获取资源的表征。获得这些表征致使这些应⽤程序 转变了其状态。随着不断获取资源的表征,客户端应⽤不断地在转变着其状 态,所谓表征状态转移。 因为我们需要的是⼀个 Machine 到 Machine 沟通的平台,需要设计⼀个 API。⽽设 计⼀个 API 来说,RESTful 是很不错的⼀种选择,也是主流的选择。⽽设计⼀个 RESTful 服务,的⾸要步骤便是设计资源模型。 9.3.1 资源 10 设计 RESTful API 设计 RESTful API 是⼀个有意思的话题。下⾯是⼀些常⽤的 RESTful 设计原则: 10.1 REST 关键⽬标5 • 组件间交互的可伸缩性 • 接⼜的通⽤性 • 组件的独⽴部署 • 通过中间组件来减少延迟、实施安全策略和封装已有系统 10.2 判断是否是 RESTful 的约束条件 6 • 客户端 -服务器分离 • ⽆状态 • 可缓存 59 10.3 设计 RESTful 资源 10 设计 RESTFUL API • 多层系统 • 统⼀接⼜ • 随需代码(可选) 10.3 设计 RESTful 资源 10.4 设计 RESTful URI 10.5 Laravel Laravel 是⼀套简洁、优雅的 PHP Web 开发框架 (PHP Web Framework)。 它可以让你从⾯条⼀样杂乱的代码中解脱出来;它可以帮你构建⼀个完美的 ⽹络 APP,⽽且每⾏代码都可以简洁、富于表达⼒。 • RESTful 路由: 通过简单的闭包就能响应 HTTP 请求。帮你快速开始构建⾮凡的应 ⽤。 • 强⼤的数据操纵能⼒: Laravel ⾃带了强⼤的 Eloquent ORM 和迁移⼯具。能够完 美的与 MySQL、Postgres、SQL Server 和 SQLite 协同⼯作。 • 优雅的模版引擎: PHP 代码或轻量级的 Blade 模版引擎都可⽆缝融合。Blade 模版 可以继承,并且拥有极快的解析速度。相信你会喜欢它的。 • 为明天做准备: 构建⼤型的企业级应⽤或者只是提供简单的 JSON API;书写强⼤ 的控制器或轻巧的 RESTful 路由,Laravel 适应所有级别的开发⼯作。 • 可靠的基⽯: Laravel 的基⽯是数个 Symfony 组件,这些经过千锤百炼、可靠的组 件为你的应⽤提供坚实的基础。 • 基于 Composer 管理器: Composer 是⼀套帮你管理第三⽅扩展包的⼯具。能够让 你迅速在 Packagist 中找到需要的扩展包。 • 强⼤的社区⽀持: ⽆论你是⼀个 PHP 新⼿还是经验丰富的架构师,都能在社区中 找到需要的知识。你可以在 IRC 中讨论 Idea,或者在论坛中发布问题。 • 测试、重构: Laravel 从开始就将测试作为重点功能。我们提供了灵活的 IoC 容器, 集成了 PHPUnit 测试⼯具。不⽤担⼼,这些都很容易上⼿。 10.5.0.1 为什么是 Laravel • 因为个⼈喜爱,你也可以⽤ Ruby On Rails 来搭建这样⼀个功能,或者是 Java。 • PHP 在我的服务器上运⾏得挺不错的,⽽且我又不需要重新去写配置那些配置。 60 10.5 Laravel 10 设计 RESTFUL API • Laravel 可以简单的开发我们所需要的功能,换句话说他是 PHP 世界的 Ruby On Rails。 这⾥不会再重述之前的问题,这⾥只是将需要的步骤⼀个个写下来,然后丢到这⾥ 好好说⼀下。⾄于 RESTful 是什么,前⾯已经介绍了,就不再重复了。那么下⾯,我们 就⽤ Laravel 来搭建⼀个平台给物联⽹⽤的。 10.5.1 安装 Laravel 10.5.1.1 第⼀步安装 Composer 10.5.1.1.1 GNU/Linux 安装 Composer GNU/Linux Ubuntu/OpenSUSE 下可 以执⾏ $ curl -sS https://getcomposer.org/installer | php 10.5.1.1.2 Windows 安装 Composer 请直接下载 Composer-Setup 10.5.1.1.3 Mac OS 安装 Composer brew install homebrew/php/composer 10.5.1.2 安装 Laravel 第⼆步安装 Laravel composer global require "laravel/installer=~1.1" 10.5.1.3 安装 Laravel 第三步创建 Laravel ⼯程 composer create- project laravel/ laravel your- project- name -- prefer-dist 61 10.6 MySQL 10 设计 RESTFUL API 10.6 MySQL 10.6.1 安装 MySQL 出于某些原因,我建议⽤ MariaDB 替换 MySQL,如果你" 真正" 需要 mysql, 将 mariadb 替换为 mysql ps: 在下⽂中我会继续⽤ MySQL,⽽不是 MariaDB,MairaDB 是 MySQL 的⼀个分 ⽀,真正的开源分⽀。 Ubuntu/Debian/Mint $ sudo apt-get install mariadb-server Fedora/Centos $ sudo yum install mariadb-server openSUSE $ sudo zypper install mariadb-server Mac OS $ brew install mariadb 10.6.2 配置 MySQL 修改 database.php app/config/database.php 要修改的就是这个 'mysql' => array( 'driver' => 'mysql', 'host' => 'localhost', 'database' => 'iot', 'username' => 'root', 62 10.7 数据库迁移 10 设计 RESTFUL API 'password' => '940217', 'charset' => 'utf8', 'collation' => 'utf8_unicode_ci', 'prefix' => '', ), 如果你已经有 phpmyadmin,似乎对你来说已经很简单了,如果没有的话,就直接 ⽤ $ mysql -uroot -p 来创建⼀个新的 CREATE DATABASE IF NOT EXISTS iot default charset utf8 COLLATE utf8_general_ci; 数据库的⽬的在于存储数据等等的闲话这⾥就不多说了,创建⼀个 RESTful 的⽬的 在于产⽣下⾯的 JSON 格式数据,以便于我们在 Android、Java、Python、jQuery 等语 ⾔框架或者平台上可以调⽤,最主要的是可以直接⽤ Ajax 来产⽣更炫⽬的效果。 { "id": 1, "temperature": 14, "sensors1": 12, "sensors2": 12, "led1": 0 } 10.7 数据库迁移 这个名字是源⾃于 Ruby On Rails 在那时候的印象,不直接使⽤ MySQL 的⽬的在 于让我们可以专注于过程。 10.7.1 创建表 表的概念,类似于在 Excel 中的表,如果你真实不懂数据库。让我们创建⼀个 athomes 的表,为什么是 athomes,因为以前在写 android 程序的时候就叫的是 athome,忽略 掉这些次要的因素吧。 63 10.7 数据库迁移 10 设计 RESTFUL API $ php artisan migrate:make create_athomes_table 打开 app/database/migrations/create_athomes_table.php 这⾥的是由⽇期和 某些东西组成的,修改⽣成的代码为下⾯。 use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateAthomesTable extends Migration { public function up() { Schema::create('athomes', function(Blueprint $table) { $table--->increments('id'); $table->float('temperature'); $table->float('sensors1'); $table->float('sensors2'); $table->boolean('led1'); $table->timestamps(); }); } public function down() { Schema::drop('athomes'); } } 意思⼤致就是 id 是⾃加的,也就是我们在 localhost/athome/{id},当我们创建⼀个 新的数据的时候,会⾃动加上去,最后⼀个 timestamps 批的是时间,会包含创建时间 和修改时间。剩下的 temperature,sensors1,sensors2 是⼩数,以及只有真和假的 led1。 10.7.2 数据库迁移 我们只是写了我们需要的数据的格式⽽并没有丢到数据库⾥, 64 10.8 创建 RESTful 10 设计 RESTFUL API $ php artisan migrate 这个就是我们执⾏迁移的命令,如果你⽤ phpmyadmin 可以直接打开查看,没有的 话,可以。 $ mysql -uroot -p use iot; select * from athomes; 就可以看到我们写的东西,那么接下来就是创建 RESTful 服务了 10.8 创建 RESTful ⽤下⾯的代码实现我们称之为 Athomes 控制器的创建 $ php artisan controller:make AthomesController 就会在 app/controllers 下⾯⽣成下⾯的代码 class AthomesController extends \BaseController { public function index() { } public function create() { } public function store() { } public function show($id) { } 65 10.9 Laravel Resources 10 设计 RESTFUL API public function edit($id) { } public function update($id) { } public function destroy($id) { } } 10.9 Laravel Resources 上⾯的代码过于沉重,请让我⽤ Ctrl+C 来带来点知识吧。 Verb Path Action Route Name GET /resource index resource.index GET /resource/create create resource.create POST /resource store resource.store GET /re- source/{resource} show resource.show 所以我们只需要专注于创建 create, edit, show, destory 等等。好吧,你可能没有耐 ⼼了,但是在修改这个之前我们需要先在 app/model 加个 class class Athomes extends Eloquent { protected $table = 'athomes'; } 如果你想要的只是控制器 Athomes 的代码的话。。 66 10.9 Laravel Resources 10 设计 RESTFUL API class AthomesController extends \BaseController { public $restful=true; protected $athome; public function __construct(Athomes $athome) { $this--->athome = $athome ; } public function index() { $maxid=Athomes::all(); return Response::json($maxid); } public function create() { $maxid=Athomes::max('id'); return View::make('athome.create')->with('maxid',$maxid); } public function store() { $rules = array( 'led1'=>'required', 'sensors1' => 'required|numeric|Min:-50|Max:80', 'sensors2' => 'required|numeric|Min:-50|Max:80', 'temperature' => 'required|numeric|Min:-50|Max:80' ); $validator = Validator::make(Input::all(), $rules); if ($validator->fails()) { return Redirect::to('athome/create') 67 10.9 Laravel Resources 10 设计 RESTFUL API ->withErrors($validator) ->withInput(Input::except('password')); } else { $nerd = new Athomes; $nerd->sensors1 = Input::get('sensors1'); $nerd->sensors2 = Input::get('sensors2'); $nerd->temperature = Input::get('temperature'); $nerd->led1 = Input::get('led1'); $nerd->save(); Session::flash('message', 'Successfully created athome!'); return Redirect::to('athome'); } } public function show($id) { $myid=Athomes::find($id); $maxid=Athomes::where('id','=',$id) ->select('id','temperature','sensors1','sensors2','led1') ->get(); return Response::json($maxid); } public function edit($id) { $athome = Athomes::find($id); return View::make('athome.edit') ->with('athome', $athome); } public function update($id) { $rules = array( 68 10.9 Laravel Resources 10 设计 RESTFUL API 'led1'=>'required|', 'sensors1' => 'required|numeric|Min:-50|Max:80', 'sensors2' => 'required|numeric|Min:-50|Max:80', 'temperature' => 'required|numeric|Min:-50|Max:80' ); $validator = Validator::make(Input::all(), $rules); if ($validator->fails()) { return Redirect::to('athome/' . $id . '/edit') ->withErrors($validator); } else { $nerd = Athomes::find($id); $nerd->sensors1 = Input::get('sensors1'); $nerd->sensors2 = Input::get('sensors2'); $nerd->temperature = Input::get('temperature'); $nerd->led1 = Input::get('led1'); $nerd->save(); Session::flash('message', 'Successfully created athome!'); return Redirect::to('athome'); } } public function destroy($id) { $athome = Athomes::find($id); $athome->delete(); if(is_null($athome)) { return Response::json('Todo not found', 404); } Session::flash('message', 'Successfully deleted the nerd!'); return Redirect::to('athome'); } 69 10.9 Laravel Resources 10 设计 RESTFUL API } 希望你能读懂,没有的话,关注下⼀节。 下⾯这部分来⾃于之前的博客,这⾥就不多加论述了。这个也就是我们要的模板, 10.9.1 修改 Create() public function create() { $maxid=Athomes::max('id'); return View::make('athome.create')->with('maxid',$maxid); } 这⾥需要在 app/views/创建⼀个 athome ⾥⾯创建⼀个 create.blade.php,⾄于 maxid,暂时还不需要,后⾯会⽤到 show。如果只需要模板,可以简化为 public function create() { return View::make('athome.create'); } 这⾥只是对其中代码的进⾏⼀下说明。 10.9.2 创建表单 10.9.2.1 创建表单之前 由于使⽤到了 bootstrap 以及 bootstrap-select,记得添加 css。 以及 javascript 10.9.2.2 创建表单 这⾥⽤到的是之前提到的那个作者写下的,稍微修改了⼀下。
{{ HTML::ul($errors->all()) }} {{ Form::open(array('url' => 'athome')) }}
{{ Form::label('led1', '开 关1') }} {{ Form::select('led1',array('关','开'),$selected=NULL,array('class'=>'selectpicker')) }}
{{ Form::label('sensors1', 'sensors1') }} {{ Form::text('sensors1', Input::old('sensors1'), array('class' => 'form- control')) }}
{{ Form::label('sensors2', 'sensors2') }} {{ Form::text('sensors2', Input::old('sensors2'), array('class' => 'form- control')) }}
{{ Form::label('temperature', 'temperature') }} {{ Form::text('temperature', Input::old('temperature'), array('class' => 'form- control')) }} 71 10.9 Laravel Resources 10 设计 RESTFUL API
{{ Form::submit('Create!', array('class' => 'btn btn- primary')) }} {{ Form::close() }}
开关⼀开始打算⽤ checkbox,加上 bootstrap-switch 实现 ON OFF 弱弱地觉得还是 没掌握好的节奏,所以最后⽤ select 来实现。 还需要修改⼀下之前的 create(),添加⼀⾏ return Redirect::to('athome'); 也就是添加完后,重定向到⾸页查看,最后例⼦给出的 create 如下 public function store() { $rules = array( 'led1'=>'required', 'sensors1' => 'required|numeric|Min:-50|Max:80', 'sensors2' => 'required|numeric|Min:-50|Max:80', 'temperature' => 'required|numeric|Min:-50|Max:80' ); $validator = Validator::make(Input::all(), $rules); if ($validator->fails()) { return Redirect::to('athome/create') ->withErrors($validator); } else { // store $nerd = new Athomes; $nerd->sensors1 = Input::get('sensors1'); $nerd->sensors2 = Input::get('sensors2'); $nerd->temperature = Input::get('temperature'); 72 10.9 Laravel Resources 10 设计 RESTFUL API $nerd->led1 = Input::get('led1'); $nerd->save(); Session::flash('message', 'Successfully created athome!'); return Redirect::to('athome'); } } 10.9.3 编辑模板 完整的 blade 模板⽂件 @yield('title')
73 10.9 Laravel Resources 10 设计 RESTFUL API

Edit {{ $athome->id }}

{{ HTML::ul($errors->all()) }} {{ Form::model($athome, array('route' => array('athome.update', $athome->id), 'method' => 'PUT')) }}
{{ Form::label('led1', '开 关1') }} {{ Form::select('led1',array('关','开'), $selected=NULL,array('class'=>'selectpicker')) }}
{{ Form::label('sensors1', '传 感 器1') }} {{ Form::text('sensors1', Input::old('sensors1'), array('class' => 'form- control')) }}
{{ Form::label('sensors2', '传 感 器2') }} {{ Form::text('sensors2', Input::old('sensors2'), array('class' => 'form- control')) }}
{{ Form::label('temperature', '温 度 传 感 器') }} {{ Form::text('temperature', Input::old('temperature'), array('class' => 'form- control')) }}
{{ Form::submit('Edit the Nerd!', array('class' => 'btn btn- primary')) }} 74 10.9 Laravel Resources 10 设计 RESTFUL API {{ Form::close() }}