├── .adr.json
├── .gitignore
├── CNAME
├── LICENSE
├── Makefile
├── README.md
├── _config.yml
├── build
├── author.html
├── head.html
├── metadata.xml
├── share.html
├── stats.html
└── title.txt
├── chapters
├── 00-prelude.md
├── 001-title.md
├── 01-what.md
├── 02-backend.md
├── 03-iot.md
├── 04-mobile.md
├── 05-desktop.md
├── 06-nr.md
├── 07-visualization.md
├── 08-game.md
├── 10-fe.md
├── 11-ued.md
├── 12-cli.md
└── 999-fe.md
├── css
└── vendor.css
├── docs
└── adr
│ ├── 0001-添加分章阅读的功能方便于-seo.md
│ └── README.md
├── ebook.md
├── epub.css
├── images
└── iot-layers.png
├── img
├── cover.jpg
└── favicon.ico
├── index.html
├── init.sh
├── listings-setup.tex
├── style.css
└── template
└── template.tex
/.adr.json:
--------------------------------------------------------------------------------
1 | {"language":"en","path":"docs/adr/","prefix":"","digits":4}
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 |
8 | # Runtime data
9 | pids
10 | *.pid
11 | *.seed
12 | *.pid.lock
13 |
14 | # Directory for instrumented libs generated by jscoverage/JSCover
15 | lib-cov
16 |
17 | # Coverage directory used by tools like istanbul
18 | coverage
19 |
20 | # nyc test coverage
21 | .nyc_output
22 |
23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
24 | .grunt
25 |
26 | # Bower dependency directory (https://bower.io/)
27 | bower_components
28 |
29 | # node-waf configuration
30 | .lock-wscript
31 |
32 | # Compiled binary addons (https://nodejs.org/api/addons.html)
33 | build/Release
34 |
35 | # Dependency directories
36 | node_modules/
37 | jspm_packages/
38 |
39 | # TypeScript v1 declaration files
40 | typings/
41 |
42 | # Optional npm cache directory
43 | .npm
44 |
45 | # Optional eslint cache
46 | .eslintcache
47 |
48 | # Optional REPL history
49 | .node_repl_history
50 |
51 | # Output of 'npm pack'
52 | *.tgz
53 |
54 | # Yarn Integrity file
55 | .yarn-integrity
56 |
57 | # dotenv environment variables file
58 | .env
59 |
60 | # next.js build output
61 | .next
62 |
--------------------------------------------------------------------------------
/CNAME:
--------------------------------------------------------------------------------
1 | frontend.phodal.com
2 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | License
2 |
3 | Copyright (c) 2019 Fengda Huang
4 |
5 | THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
6 |
7 | BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.
8 |
9 | 1. Definitions
10 |
11 | "Collective Work" means a work, such as a periodical issue, anthology or encyclopedia, in which the Work in its entirety in unmodified form, along with one or more other contributions, constituting separate and independent works in themselves, are assembled into a collective whole. A work that constitutes a Collective Work will not be considered a Derivative Work (as defined below) for the purposes of this License.
12 | "Derivative Work" means a work based upon the Work or upon the Work and other pre-existing works, such as a translation, musical arrangement, dramatization, fictionalization, motion picture version, sound recording, art reproduction, abridgment, condensation, or any other form in which the Work may be recast, transformed, or adapted, except that a work that constitutes a Collective Work will not be considered a Derivative Work for the purpose of this License. For the avoidance of doubt, where the Work is a musical composition or sound recording, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered a Derivative Work for the purpose of this License.
13 | "Licensor" means the individual, individuals, entity or entities that offers the Work under the terms of this License.
14 | "Original Author" means the individual, individuals, entity or entities who created the Work.
15 | "Work" means the copyrightable work of authorship offered under the terms of this License.
16 | "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation.
17 | 2. Fair Use Rights. Nothing in this license is intended to reduce, limit, or restrict any rights arising from fair use, first sale or other limitations on the exclusive rights of the copyright owner under copyright law or other applicable laws.
18 |
19 | 3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below:
20 |
21 | to reproduce the Work, to incorporate the Work into one or more Collective Works, and to reproduce the Work as incorporated in the Collective Works; and,
22 | to distribute copies or phonorecords of, display publicly, perform publicly, and perform publicly by means of a digital audio transmission the Work including as incorporated in Collective Works.
23 | The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats, but otherwise you have no rights to make Derivative Works. All rights not expressly granted by Licensor are hereby reserved, including but not limited to the rights set forth in Sections 4(d) and 4(e).
24 |
25 | 4. Restrictions.The license granted in Section 3 above is expressly made subject to and limited by the following restrictions:
26 |
27 | You may distribute, publicly display, publicly perform, or publicly digitally perform the Work only under the terms of this License, and You must include a copy of, or the Uniform Resource Identifier for, this License with every copy or phonorecord of the Work You distribute, publicly display, publicly perform, or publicly digitally perform. You may not offer or impose any terms on the Work that restrict the terms of this License or the ability of a recipient of the Work to exercise the rights granted to that recipient under the terms of the License. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties. When You distribute, publicly display, publicly perform, or publicly digitally perform the Work, You may not impose any technological measures on the Work that restrict the ability of a recipient of the Work from You to exercise the rights granted to that recipient under the terms of the License. This Section 4(a) applies to the Work as incorporated in a Collective Work, but this does not require the Collective Work apart from the Work itself to be made subject to the terms of this License. If You create a Collective Work, upon notice from any Licensor You must, to the extent practicable, remove from the Collective Work any credit as required by Section 4(c), as requested.
28 | You may not exercise any of the rights granted to You in Section 3 above in any manner that is primarily intended for or directed toward commercial advantage or private monetary compensation. The exchange of the Work for other copyrighted works by means of digital file-sharing or otherwise shall not be considered to be intended for or directed toward commercial advantage or private monetary compensation, provided there is no payment of any monetary compensation in connection with the exchange of copyrighted works.
29 | If You distribute, publicly display, publicly perform, or publicly digitally perform the Work (as defined in Section 1 above) or Collective Works (as defined in Section 1 above), You must, unless a request has been made pursuant to Section 4(a), keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or (ii) if the Original Author and/or Licensor designate another party or parties (e.g. a sponsor institute, publishing entity, journal) for attribution ("Attribution Parties") in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; the title of the Work if supplied; to the extent reasonably practicable, the Uniform Resource Identifier, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work. The credit required by this Section 4(c) may be implemented in any reasonable manner; provided, however, that in the case of a Collective Work, at a minimum such credit will appear, if a credit for all contributing authors of the Collective Work appears, then as part of these credits and in a manner at least as prominent as the credits for the other contributing authors. For the avoidance of doubt, You may only use the credit required by this clause for the purpose of attribution in the manner set out above and, by exercising Your rights under this License, You may not implicitly or explicitly assert or imply any connection with, sponsorship or endorsement by the Original Author, Licensor and/or Attribution Parties, as appropriate, of You or Your use of the Work, without the separate, express prior written permission of the Original Author, Licensor and/or Attribution Parties.
30 | For the avoidance of doubt, where the Work is a musical composition:
31 |
32 | Performance Royalties Under Blanket Licenses. Licensor reserves the exclusive right to collect whether individually or, in the event that Licensor is a member of a performance rights society (e.g. ASCAP, BMI, SESAC), via that society, royalties for the public performance or public digital performance (e.g. webcast) of the Work if that performance is primarily intended for or directed toward commercial advantage or private monetary compensation.
33 | Mechanical Rights and Statutory Royalties. Licensor reserves the exclusive right to collect, whether individually or via a music rights agency or designated agent (e.g. Harry Fox Agency), royalties for any phonorecord You create from the Work ("cover version") and distribute, subject to the compulsory license created by 17 USC Section 115 of the US Copyright Act (or the equivalent in other jurisdictions), if Your distribution of such cover version is primarily intended for or directed toward commercial advantage or private monetary compensation.
34 | Webcasting Rights and Statutory Royalties. For the avoidance of doubt, where the Work is a sound recording, Licensor reserves the exclusive right to collect, whether individually or via a performance-rights society (e.g. SoundExchange), royalties for the public digital performance (e.g. webcast) of the Work, subject to the compulsory license created by 17 USC Section 114 of the US Copyright Act (or the equivalent in other jurisdictions), if Your public digital performance is primarily intended for or directed toward commercial advantage or private monetary compensation.
35 | 5. Representations, Warranties and Disclaimer
36 |
37 | UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND ONLY TO THE EXTENT OF ANY RIGHTS HELD IN THE LICENSED WORK BY THE LICENSOR. THE LICENSOR MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MARKETABILITY, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
38 |
39 | 6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
40 |
41 | 7. Termination
42 |
43 | This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Collective Works (as defined in Section 1 above) from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License.
44 | Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above.
45 | 8. Miscellaneous
46 |
47 | Each time You distribute or publicly digitally perform the Work (as defined in Section 1 above) or a Collective Work (as defined in Section 1 above), the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License.
48 | If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
49 | No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent.
50 | This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You.
51 | Creative Commons Notice
52 |
53 | Creative Commons is not a party to this License, and makes no warranty whatsoever in connection with the Work. Creative Commons will not be liable to You or any party on any legal theory for any damages whatsoever, including without limitation any general, special, incidental or consequential damages arising in connection to this license. Notwithstanding the foregoing two (2) sentences, if Creative Commons has expressly identified itself as the Licensor hereunder, it shall have all rights and obligations of Licensor.
54 |
55 | Except for the limited purpose of indicating to the public that the Work is licensed under the CCPL, Creative Commons does not authorize the use by either party of the trademark "Creative Commons" or any related trademark or logo of Creative Commons without the prior written consent of Creative Commons. Any permitted use will be in compliance with Creative Commons' then-current trademark usage guidelines, as may be published on its website or otherwise made available upon request from time to time. For the avoidance of doubt, this trademark restriction does not form part of this License.
56 |
57 | Creative Commons may be contacted at https://creativecommons.org/.
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | include_dir=build
2 | source=chapters/*.md
3 | title='大前端:前端的扩张'
4 | filename='ebook'
5 |
6 |
7 | all: html epub rtf pdf mobi
8 |
9 | markdown:
10 | awk 'FNR==1{print ""}{print}' $(source) > $(filename).md
11 |
12 | html: markdown
13 | pandoc -s $(filename).md -t html5 -o index.html -c style.css \
14 | --include-in-header $(include_dir)/head.html \
15 | --include-before-body $(include_dir)/author.html \
16 | --include-before-body $(include_dir)/share.html \
17 | --include-after-body $(include_dir)/stats.html \
18 | --title-prefix $(title) \
19 | --toc-depth=4 \
20 | --toc
21 |
22 | epub: markdown
23 | pandoc -s $(filename).md -t epub -o $(filename).epub \
24 | --epub-metadata $(include_dir)/metadata.xml \
25 | --epub-cover-image img/cover.jpg \
26 | --title-prefix $(title) \
27 | --toc
28 |
29 | rtf: markdown
30 | pandoc -s $(filename).md -o $(filename).rtf \
31 | --title-prefix $(title)
32 |
33 | pdf: markdown
34 | # OS X: http://www.tug.org/mactex/
35 | # Then find its path: find /usr/ -name "pdflatex"
36 | # Then symlink it: ln -s /path/to/pdflatex /usr/local/bin
37 | pandoc -s $(filename).md -o $(filename).pdf \
38 | --title-prefix $(title) \
39 | --listings -H listings-setup.tex \
40 | --template=template/template.tex \
41 | --toc
42 |
43 | mobi: epub
44 | # Symlink bin: ln -s /path/to/kindlegen /usr/local/bin
45 | kindlegen $(filename).epub
46 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 大前端:前端的扩张
2 |
3 | > 大前端是指通过 Web 开发相关的技术(WebView、JavaScript/TypeScript)所能开发、处理的领域。除了包含传统的 Web 前端相比,大前端还包含了——后端(Node.js 如 BFF 层、Serverless)、HTML 5 游戏、物联网、嵌入式应用、移动应用、桌面应用。它的显著特性是:**一次开发,多种平台**。
4 |
5 |
6 | **适合对象**:任何级别的前端工程师。
7 |
8 | **简介**:
9 |
10 | ## 引子
11 |
12 | 当我在知乎上回答了一个问题:坚持学前端的你,如今有什么感悟?,我的答案是:
13 |
14 | ----------------------------
15 |
16 | - 2015 年 GET 了 Three.js 和 Oculus,可以开发 VR 应用,于是有了 [oculus-nodejs-threejs-example](https://github.com/phodal/oculus-nodejs-threejs-example)
17 | - 2016 年 GET 了 Cordova,可以开发混合应用,写了开源应用 Ionic 版本的 [Growth](https://github.com/phodal/growth-ionic)
18 | - 2017 年 GET 了 React Native 和原生开发技能,可以开发跨平台应用,于是写了基于 React Native 的混合应用框架 [Dore](https://github.com/phodal/dore)
19 | - 2017 年 GET 了 Serverless,可以快速使用 Node.js 开发后台应用,于是有了 [Serverless](https://github.com/phodal/serverless) 系列的应用。
20 | - 2018 年 GET 了 Electron,可以写桌面应用了,于是有了自己的 Markdown 编辑器 [Phodit](https://github.com/phodal/phodit)
21 | - 2019 年 GET 了 Canvas,可以写游戏应用了,于是有了 [Milano](https://github.com/phodal/milano)
22 |
23 | 所以……VR 应用、移动应用、桌面应用、游戏应用、后台应用。
24 |
25 | 等等,你说这是前端相关的?方面看并非如此,可实际上却是如此。
26 |
27 | ----------------------------
28 |
29 | 喔,我好像不是一个前端,我好像是一个前端。哦,不对,我并不只是一个前端工程师,我是一个程序员,我是一个 Geek,我是一个作者。
30 |
31 | 总而言之,言而总之:
32 |
33 | - **我是一个程序员,然后才是一个前端。**
34 | - **我是一个程序员,然后才是一个前端。**
35 | - **我是一个程序员,然后才是一个前端。**
36 |
37 | 所以,放下前端程序员这个执念,让我们来看看:只凭现有的前端技术栈,我们又能做些什么呢?
38 |
39 | ## 目录
40 |
41 | * [前言](https://javascript.ren/#%E5%89%8D%E8%A8%80)
42 | * [桌面浏览器](https://javascript.ren/#%E6%A1%8C%E9%9D%A2%E6%B5%8F%E8%A7%88%E5%99%A8)
43 | * [移动设备浏览器](https://javascript.ren/#%E7%A7%BB%E5%8A%A8%E8%AE%BE%E5%A4%87%E6%B5%8F%E8%A7%88%E5%99%A8)
44 | * [嵌入式设备浏览器](https://javascript.ren/#%E5%B5%8C%E5%85%A5%E5%BC%8F%E8%AE%BE%E5%A4%87%E6%B5%8F%E8%A7%88%E5%99%A8)
45 | * [大前端](https://javascript.ren/#%E5%A4%A7%E5%89%8D%E7%AB%AF)
46 | * [大前端:写给大家看的渐进式前端发展指南](https://javascript.ren/#%E5%A4%A7%E5%89%8D%E7%AB%AF%EF%BC%9A%E5%86%99%E7%BB%99%E5%A4%A7%E5%AE%B6%E7%9C%8B%E7%9A%84%E6%B8%90%E8%BF%9B%E5%BC%8F%E5%89%8D%E7%AB%AF%E5%8F%91%E5%B1%95%E6%8C%87%E5%8D%97)
47 | * [为什么会有大前端](https://javascript.ren/#%E4%B8%BA%E4%BB%80%E4%B9%88%E4%BC%9A%E6%9C%89%E5%A4%A7%E5%89%8D%E7%AB%AF)
48 | * [JavaScript 的神奇魔力](https://javascript.ren/#javascript-%E7%9A%84%E7%A5%9E%E5%A5%87%E9%AD%94%E5%8A%9B)
49 | * [无处不在的 WebView](https://javascript.ren/#%E6%97%A0%E5%A4%84%E4%B8%8D%E5%9C%A8%E7%9A%84-webview)
50 | * [什么是大前端??](https://javascript.ren/#%E4%BB%80%E4%B9%88%E6%98%AF%E5%A4%A7%E5%89%8D%E7%AB%AF%EF%BC%9F%EF%BC%9F)
51 | * [大前端的技术领域](https://javascript.ren/#%E5%A4%A7%E5%89%8D%E7%AB%AF%E7%9A%84%E6%8A%80%E6%9C%AF%E9%A2%86%E5%9F%9F)
52 | * [前端中的 Web 前端](https://javascript.ren/#%E5%89%8D%E7%AB%AF%E4%B8%AD%E7%9A%84-web-%E5%89%8D%E7%AB%AF)
53 | * [前端中的 GUI](https://javascript.ren/#%E5%89%8D%E7%AB%AF%E4%B8%AD%E7%9A%84-gui)
54 | * [前端中的游戏](https://javascript.ren/#%E5%89%8D%E7%AB%AF%E4%B8%AD%E7%9A%84%E6%B8%B8%E6%88%8F)
55 | * [前端中的物联网(IoT)](https://javascript.ren/#%E5%89%8D%E7%AB%AF%E4%B8%AD%E7%9A%84%E7%89%A9%E8%81%94%E7%BD%91%EF%BC%88iot%EF%BC%89)
56 | * [前端中的移动端](https://javascript.ren/#%E5%89%8D%E7%AB%AF%E4%B8%AD%E7%9A%84%E7%A7%BB%E5%8A%A8%E7%AB%AF)
57 | * [前端中的 AR/VR](https://javascript.ren/#%E5%89%8D%E7%AB%AF%E4%B8%AD%E7%9A%84-arvr)
58 | * [选题](https://javascript.ren/#%E9%80%89%E9%A2%98)
59 | * [如何成为大前端?](https://javascript.ren/#%E5%A6%82%E4%BD%95%E6%88%90%E4%B8%BA%E5%A4%A7%E5%89%8D%E7%AB%AF%EF%BC%9F)
60 | * [大前端:后端](https://javascript.ren/#%E5%A4%A7%E5%89%8D%E7%AB%AF%EF%BC%9A%E5%90%8E%E7%AB%AF)
61 | * [Node.js 打造后端服务](https://javascript.ren/#nodejs-%E6%89%93%E9%80%A0%E5%90%8E%E7%AB%AF%E6%9C%8D%E5%8A%A1)
62 | * [Serverless](https://javascript.ren/#serverless)
63 | * [BFF 层](https://javascript.ren/#bff-%E5%B1%82)
64 | * [大前端:IoT](https://javascript.ren/#%E5%A4%A7%E5%89%8D%E7%AB%AF%EF%BC%9Aiot)
65 | * [传统的物联网](https://javascript.ren/#%E4%BC%A0%E7%BB%9F%E7%9A%84%E7%89%A9%E8%81%94%E7%BD%91)
66 | * [嵌入式设备分类](https://javascript.ren/#%E5%B5%8C%E5%85%A5%E5%BC%8F%E8%AE%BE%E5%A4%87%E5%88%86%E7%B1%BB)
67 | * [两种前端类型](https://javascript.ren/#%E4%B8%A4%E7%A7%8D%E5%89%8D%E7%AB%AF%E7%B1%BB%E5%9E%8B)
68 | * [嵌入式设备作为服务端](https://javascript.ren/#%E5%B5%8C%E5%85%A5%E5%BC%8F%E8%AE%BE%E5%A4%87%E4%BD%9C%E4%B8%BA%E6%9C%8D%E5%8A%A1%E7%AB%AF)
69 | * [嵌入式设备作为服务端和客户端](https://javascript.ren/#%E5%B5%8C%E5%85%A5%E5%BC%8F%E8%AE%BE%E5%A4%87%E4%BD%9C%E4%B8%BA%E6%9C%8D%E5%8A%A1%E7%AB%AF%E5%92%8C%E5%AE%A2%E6%88%B7%E7%AB%AF)
70 | * [嵌入式设备分类](https://javascript.ren/#%E5%B5%8C%E5%85%A5%E5%BC%8F%E8%AE%BE%E5%A4%87%E5%88%86%E7%B1%BB-1)
71 | * [低资源受限设备:编译型语言](https://javascript.ren/#%E4%BD%8E%E8%B5%84%E6%BA%90%E5%8F%97%E9%99%90%E8%AE%BE%E5%A4%87%EF%BC%9A%E7%BC%96%E8%AF%91%E5%9E%8B%E8%AF%AD%E8%A8%80)
72 | * [普通嵌入式设备:](https://javascript.ren/#%E6%99%AE%E9%80%9A%E5%B5%8C%E5%85%A5%E5%BC%8F%E8%AE%BE%E5%A4%87%EF%BC%9A)
73 | * [固件编写](https://javascript.ren/#%E5%9B%BA%E4%BB%B6%E7%BC%96%E5%86%99)
74 | * [Johnny-Five](https://javascript.ren/#johnny-five)
75 | * [IoT.js](https://javascript.ren/#iotjs)
76 | * [Be Professinoal](https://javascript.ren/#be-professinoal)
77 | * [Arduino 与 Processing](https://javascript.ren/#arduino-%E4%B8%8E-processing)
78 | * [ESP866/ESP32 与 Lua](https://javascript.ren/#esp866esp32-%E4%B8%8E-lua)
79 | * [C 语言](https://javascript.ren/#c-%E8%AF%AD%E8%A8%80)
80 | * [大前端:移动端](https://javascript.ren/#%E5%A4%A7%E5%89%8D%E7%AB%AF%EF%BC%9A%E7%A7%BB%E5%8A%A8%E7%AB%AF)
81 | * [混合应用](https://javascript.ren/#%E6%B7%B7%E5%90%88%E5%BA%94%E7%94%A8)
82 | * [跨平台应用](https://javascript.ren/#%E8%B7%A8%E5%B9%B3%E5%8F%B0%E5%BA%94%E7%94%A8)
83 | * [React Native](https://javascript.ren/#react-native)
84 | * [Flutter](https://javascript.ren/#flutter)
85 | * [Weex](https://javascript.ren/#weex)
86 | * [小程序](https://javascript.ren/#%E5%B0%8F%E7%A8%8B%E5%BA%8F)
87 | * [大前端:桌面应用](https://javascript.ren/#%E5%A4%A7%E5%89%8D%E7%AB%AF%EF%BC%9A%E6%A1%8C%E9%9D%A2%E5%BA%94%E7%94%A8)
88 | * [跨平台 WebView 应用框架](https://javascript.ren/#%E8%B7%A8%E5%B9%B3%E5%8F%B0-webview-%E5%BA%94%E7%94%A8%E6%A1%86%E6%9E%B6)
89 | * [跨平台框架](https://javascript.ren/#%E8%B7%A8%E5%B9%B3%E5%8F%B0%E6%A1%86%E6%9E%B6)
90 | * [大前端:VR/AR](https://javascript.ren/#%E5%A4%A7%E5%89%8D%E7%AB%AF%EF%BC%9Avrar)
91 | * [VR](https://javascript.ren/#vr)
92 | * [AR](https://javascript.ren/#ar)
93 | * [大前端:数据可视化](https://javascript.ren/#%E5%A4%A7%E5%89%8D%E7%AB%AF%EF%BC%9A%E6%95%B0%E6%8D%AE%E5%8F%AF%E8%A7%86%E5%8C%96)
94 | * * [高级娄](https://javascript.ren/#%E9%AB%98%E7%BA%A7%E5%A8%84)
95 | * [D3.js](https://javascript.ren/#d3js)
96 | * [大前端:游戏](https://javascript.ren/#%E5%A4%A7%E5%89%8D%E7%AB%AF%EF%BC%9A%E6%B8%B8%E6%88%8F)
97 | * [WebView 游戏](https://javascript.ren/#webview-%E6%B8%B8%E6%88%8F)
98 | * [JavaScript 语言游戏](https://javascript.ren/#javascript-%E8%AF%AD%E8%A8%80%E6%B8%B8%E6%88%8F)
99 | * [大前端:前端](https://javascript.ren/#%E5%A4%A7%E5%89%8D%E7%AB%AF%EF%BC%9A%E5%89%8D%E7%AB%AF)
100 | * [Web 应用](https://javascript.ren/#web-%E5%BA%94%E7%94%A8)
101 | * [SPA (单页面应用)框架:A/V/R](https://javascript.ren/#spa-%EF%BC%88%E5%8D%95%E9%A1%B5%E9%9D%A2%E5%BA%94%E7%94%A8%EF%BC%89%E6%A1%86%E6%9E%B6%EF%BC%9Aavr)
102 | * [MPA (多页面应用)框架](https://javascript.ren/#mpa-%EF%BC%88%E5%A4%9A%E9%A1%B5%E9%9D%A2%E5%BA%94%E7%94%A8%EF%BC%89%E6%A1%86%E6%9E%B6)
103 | * [语言](https://javascript.ren/#%E8%AF%AD%E8%A8%80)
104 | * [TypeScript](https://javascript.ren/#typescript)
105 | * [前端,永不止境](https://javascript.ren/#%E5%89%8D%E7%AB%AF%EF%BC%8C%E6%B0%B8%E4%B8%8D%E6%AD%A2%E5%A2%83)
106 |
107 | ## LICENSE
108 |
109 | © 2019 [Phodal Huang](https://www.phodal.com/). This code is distributed under the Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 License. See `LICENSE` in this directory.
110 |
--------------------------------------------------------------------------------
/_config.yml:
--------------------------------------------------------------------------------
1 | remote_theme: phodal/mifa-jekyll
2 |
--------------------------------------------------------------------------------
/build/author.html:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/build/stats.html:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phodal/frontend/f553c283b93197c2458d9da9bde01afb144c575f/build/stats.html
--------------------------------------------------------------------------------
/build/title.txt:
--------------------------------------------------------------------------------
1 | % 大前端:前端的扩张
2 | % Phodal HUANG
--------------------------------------------------------------------------------
/chapters/00-prelude.md:
--------------------------------------------------------------------------------
1 | # 前言
2 | 下一步,我们要去哪里?
3 |
4 | 成为程序员并不是一件容易的事,又并不代表你与你隔壁的小王相比,就能多赚很多钱。这个行业有各种有趣的人,有的是科班出身,有的是自学成长 ,有的是培训养成。来自哪里并不重要,重要的是你要去哪里。
5 |
6 | 也许是金三银四的到来,也许是越来越多的人被优化,也许是春天的到来。它们结合起来,让愈来愈的人开始焦虑起来。尤其是对于那些工作 2~3 年的人来说,这种焦虑愈加的明显。工作 1~2 年的,技术上的问题还有一系列待解决。像我这样工作 4~5 年的人,又有别的焦虑。如最近,我一直忙于构建自己想要的 Tech Lead 的模型、技能——如果有一个东西绕不开,那便是时候学习相关的内容。与此同时,我还开始在编写一个大前端相关的电子书——《大前端的扩张》。
7 |
8 | ## 前端工程师是什么?
9 |
10 | **前端工程师是工程师,后端工程师是工程师,他/她们并没有不同。**
11 |
12 | 前端工程师是精细分工的一个产物。工程师被分为了前端工程师、后端工程师,对应的团队的 Tech Lead 也有了两个,相应的 KPI 考察也变成了两个团队。尽管出于方便的原因,某些公司向上汇报的渠道往往只有一个:后端工程师。这对于想往管理发展的前端程序员来说,这并不是一件好事。但是对于想往技术发展的前端工程师来说,这一点相当的不错——少去了一系列的会议,还不需要一些“没有意义”的加班。
13 |
14 | 前端工程师和后端工程师并没有区别,他/她们首先都是人,然后才是工程师,然后才是前/后端工程师。至于这些人,什么时候被机器替换,倒是有一个好问题。后端有 Serverless 这样的大山压在前面,前端有 AI 生成代码的挑战在面前。他/她们有各自的焦虑,后端有自己的无聊,前端有自己的热闹。
15 |
16 | **前端没有地位?**
17 |
18 | 有没有地位,这是一个好问题。可是呢,“你真的在乎这个地位吗?”当我在吐槽“前端没有地位”的时候,我可能有多个意思:
19 |
20 | 1. 公司给前端的机会太少了。
21 | 2. 我做的事情带来了更多的价值,应该有更多的收入。
22 | 3. 前端开发的话语权太小了。
23 |
24 | 那么,你呢?是否也是如此。我们只是在一些利益上做争夺而已——可是呢,当我们投入同样的时间,却没有同样的回报时。我们也应该去争夺一些相关的权益。
25 |
26 | 可若是呢,要是你的项目里,不懂前端的后端为前端下决策,那就说明:在你的公司里,前端是真没地位,又或者是没有人愿意去争取。原因呢,无非就是一个萝卜一个坑,而那个坑已经被后端占据了。
27 |
28 | 前端的历史太短,刚刚浴火重新的时候,后端已经把持朝政好几年了。
29 |
30 | **前端不重要?**
31 |
32 | 前端有没有那么重要,是人们心中的想法——大多数程序员(包含我)很难去说服别人,我只会以实际行动去证明。可是,前端真的不重要么?这是一个相当大的误区,既然你需要前端,那说明在这个项目前端是必不可少的。既然必不可少,那它就是重要的。
33 |
34 | 只是大家口中的前端不重要,并不是说前端不重要。而是前端的某些部分对于他/她而言,不那么重要了:
35 |
36 | - “我不需要复杂的交互,只需要可以工作就好”。
37 | - “我不需要酷炫的动画,只需要可以串起来就好”。
38 | - “我不知道校验输入结果,反正后端可以校验就好”。
39 |
40 | 然后,等你按这个标准实现的时候,会发现他/她们对这个效果非常不满意。总有人会如此,口口声声觉得前端不重要,最后还是需要一个重要的前端角色。不重要,只是压缩支出和时间的一个手段;不重要,只是当前不重要。
41 |
42 | 我们觉得重要,那就是重要。
43 |
44 | **前端工程师是工程师,后端工程师是工程师,他/她们并没有不同。**
45 |
46 | 一个不学习前端的后端,不能成为一个优秀的工程师。一个不学习后端的前端,也无法成为优秀的工程师。“学习” 包含了两层含义,一个是 GET 学习这个技能,一个是掌握新的技能。
47 |
48 | 一个不了解后端的前端,和那个你鄙视的后端没有太大的区别。编程语言是一个前端,前后端是一个谋生的端点。**你是一个程序员,你需要持续的学习。**
49 |
50 | ## 广度与深度
51 |
52 | 前端是一个门槛越来越低的岗位,那些新人啊,可以快速来到这个行业,COPY/Paste 出类似的代码
53 |
54 | 广度与深度,并不值得讨论。一个公司中的架构师,绝不只是前端工程师,也绝不只是一个后端工程师。同样的问题,如果有一个人让你只学前端,那个人可能是你领导,或者是大公司想要的那种螺丝钉。下次,遇到这样的问题的时候,不妨站在对方的角度来看这个问题。而如果你只想当一辈子的前端工程师,深入到浏览器底层,那也不是问题。找自己喜欢的那部分就好。
55 |
56 | 而就个人来说,只有你会了前端,会了后端,你才会开发出一个完整的应用。毕竟优秀的工程师,早晚都是全栈的。如我们之前项目遇到的一个 “URL 编码问题”,它是从前端生成的 URL 开始,要经过浏览器转义(在 Chrome 和 Safari 上有所不同),经过 Vanrish 缓存,再来到 Apache 服务器,还有 Tomcat 服务器,最后还要经历 Spring 框架,才能到达代码层。最后的最后,我们找到了问题是 Firefox 浏览器的预加载,和我们预想的完全不一样。过程中,你需要前端、后端,还需要 DevOps 的相关技能。
57 |
58 | 扩展你的边境,对你的学习能力会有更大的提升。尤其是,当你觉得前端无聊,或者没有新的东西,请毫不犹豫地拥抱变化。
59 |
60 | 当然了,如果你只工作不到一年,请先往深度发展。
61 |
62 | ### 深度:复杂应用的架构
63 |
64 | 如果只是前端应用来考虑,那大抵是只有少数的应用,可以称得上是复杂应用。这一类型的应用:
65 |
66 | - 可能存在复杂的业务逻辑
67 | - 可能是因为业务功能数量大,产生了大量的代码
68 | - 可能是技术实现上困难
69 |
70 | 这能归类到一级别的前端应用,数量并不多。
71 |
72 | ### 广度:更大领域的前端技术
73 |
74 | 从最近几年的趋势来看,还有一个趋势是,客户端融合,导致前端的快速融合,。
75 |
76 | 当我第一次全面使用 JavaScript + Web 浏览器来开发应用时,我发现它真的非常的快速、省事。
77 |
78 | 所以,那时在我的第一本书里《自己动手设计物联网》,我完全使用 JavaScript 来开发应用。
79 |
80 | - 前端
81 | - APP
82 | - 后端
83 |
84 | 限于当时的条件,我并没有使用 Node.js 来开发嵌入式应用。而随着更好的 JavaScript 引擎的出现,在嵌入式的机会也越来越大。
85 |
86 | ## Tech Lead vs Tech Manager vs Tech Expert
87 |
88 | 技术变得越来越卓越,也意味着我们会承担一些带领她/她人成长的任务,也意味着我们要开始做越来越多的非技术工作,也意味着我们面临这样的抉择。从发展的路线上来看,会有三种不同的角色或岗位需要我们考虑:
89 |
90 | **Tech Manager**,岗位。技术管理者是一个真实的岗位,它拥有一些真实的权力,以及对应的 KPI 压力。在一些公司里,它是一个角色的多种职责之一,如它是项目经理(Project Manger),只是集中于技术部分。但是这个岗位,往往不会深入到代码中,只是做技术上的决策——如数据库选哪个,如何去部署等等之类的问题。
91 |
92 | **Tech Lead**,角色。技术领导者是一种角色,
93 |
94 | **Tech Expert**,underfined。
95 |
96 | ## 前端领域
97 |
98 | ### 桌面浏览器
99 |
100 | ### 移动设备浏览器
101 |
102 | ### 嵌入式设备浏览器
103 |
104 | ## 写作的模式
105 |
106 | 1. 引子 ->
107 |
108 | 模式 - 你并不需要使用 Node.js 来写这样的功能。除非:
109 |
110 | - 你没有其它语言的开发技能
111 | - 使用前端技术、JavaScript 开发更快
112 |
113 | 2. 应用示例
114 |
115 | 3. 架构规划方案 ->
116 |
117 |
118 | 使用前端技术开发时,应该采用怎样的技术方案?
119 |
120 | 4. Demo 应用
121 |
122 |
123 | 5. 模式介绍 ->
124 |
125 | 在编写这样的技术栈时,你可以采用怎样的模式。诸如
126 |
127 | Chrome 的发布-订阅者模式
128 |
--------------------------------------------------------------------------------
/chapters/001-title.md:
--------------------------------------------------------------------------------
1 | # 前端地位
2 |
3 | > 前端困境三步曲之前端地位
4 |
5 | 最近的,最远的最近,或者说在过去的几个月里,我与几个前端同事,一直在讨论一个话题:『作为一个前端开发人员,我们面临怎样的困境?又该如何去解决?』。
6 |
7 | 而在较老的一次历史讨论(可能是在 6 小时以前)里,我便想重新理清一次其中的思路,也就有了这篇文章。
8 |
9 | ## 前端是不是没有地位?
10 |
11 | 答案:不是,也是。
12 |
13 | 当我们在技术领域,技术团队,讨论地位的时候,说的实际上是话语权——技术的话语权,KPI 的话语权。技术话语权,是因人而异,当你可被信赖时,你就有了话语权。而 KPI 话语权,实际上指的是 title。
14 |
15 | 1. **来得晚的前端没有 Title**。Title 是一个很有意思的东西:**先到先得**,你去了一家高速发展的创业公司,你的 title 就升得很快——站在风口,大象都能飞。而,大部分 Web 应用,前期注重的往往都是应用的功能,这也导致了:这些组织在前期并不需要优秀的前端开发。而发展起来之后,便开始追求用户体验、视觉效果、多平台,到了这个时候呢,关键的坑位已经被后端占据了。毕竟好的前端很贵,但是能实现页面的前端到处都是——甚至是后端也是。
16 |
17 | 2. **后端懂点前端,而前端不懂 CRUD**。事实上,大部分的组织对于团队负责人,都有一个默认的要求:『精通』整个系统——无论是前后端。这就意味着,前端需要懂后端,后端也需要懂前端。所以,一个不懂后端的前端,站不到 title 上;一个不懂前端的后端,站不到 title 上。可是呢,对于普通的开发人员来说,要达到中等前端水平的时间花费,要比后端少得多。而如果放到大前端的领域来考虑,这个问题就需要额外商榷了。
18 |
19 | PS:懂后端也并不要求,你精通后端。因为最好的篮球教练,并不要求会打篮球。而打篮球最好的不一定会当技术负责人/Coach,比如——科比被女儿怼:“你不会打篮球,教练是这么教我的” 。当然了,有技术底子是最好的,但是它也可能在一定程度上限制你。
20 |
21 | 3. **需求导向**(可选)。对于服务型公司,如我司,需求方决定了架构的复杂性,决定了其所需要的 title。而需求方对于架构、复杂度的考量,往往是来自于整个市场的平均知识水平。也就是说,一旦业务方需求不复杂,也就不需要高级的前端开发,便谈不上就不话语权。
22 |
23 | 综上所述,若是想争取地位需要:去得早,懂后端,机会好。
24 |
25 | 扯太远了,那么继续往下扯。
26 |
27 | ## 5 个因素决定前端
28 |
29 | ### 1. 复杂度,决定前端
30 |
31 | 同样是做一个手机,诺基亚的功能机,和 iPhone 有不一样的成本。
32 |
33 | 项目的业务人员/产品经理/产品负责人对于产品的需求,出因此决定了应用/产品的复杂度。诸如于,同样是一个搜索功能,它有不同的实现方式:
34 |
35 | 1. 普通模式。前端生成搜索的 URL,跳转到对应的搜索结果页。
36 | 2. 标签搜索 + 普通搜索。后端返回标签
37 | 3. AutoComplete + 普通搜索
38 | 4. AutoComplete + 标签搜索 + 普通搜索
39 | 5. AutoComplete + 地图搜索 + 标签搜索 + 普通搜索。对了,还要有地图和标签的联动。
40 | 6. AutoComplete + 地图搜索 + 标签搜索 + 普通搜索 + 热门搜索。
41 | 7. ……
42 |
43 | 复杂度,决定了对于优秀前端工程师的需求。也因此在某种程度上,决定了前端的话语权。比如说,『出于设计上的需要,决定了后端应该这么做 xxxx』
44 |
45 | 也因此,诸如于腾讯这样的产品型公司,前后端都没有地垃。
46 |
47 | 但是,它避免了后端决定了前端需求的要素——这一点非常重要。在产品话语权不高的团队,必然是先到先得的后端管理者,决定了整个产品的走向,也由后端决定了前端的设计。
48 |
49 | ### 2. 团队规模,决定前端
50 |
51 | 只有组织内的前端团队达到一定的规模,才能迫使组织的管理者意识到:『我们需要更优秀的前端开发,才能解决当前的瓶颈』。
52 |
53 | 按 xx 划分:
54 |
55 | - HTML 5 广告页
56 | - 小型前端应用(微信小程序)
57 | - 中型前端应用(普通的 Web 应用)
58 | - 大型前端应用(toB)
59 |
60 | 按团队规模来划分:
61 |
62 | - 页面级
63 | - 6 人团队
64 | - 两个 Pizza 团队级
65 | - 组织级
66 |
67 | 所以,如果你只是在切图,如果你只是在画 HTML5
68 |
69 | ### 3. 流水线式开发
70 |
71 | 大型组织,需要更明确的分工,以便于机械工的生产更多的应用。
72 |
73 | 也因此需要更明确的分工,来解决效率的问题。
74 |
75 | - 工具支撑团队
76 | - 框架开发团队
77 | - 业务开发团队
78 | - DevOps 团队
79 |
80 | ### 4. 客户端多样式
81 |
82 | 在最近的几年里,前端走向大前端的原因也在于此,对于多种客户端开发的需求:微信小程度、桌面客户端、跨平台应用等等。使得一个个前端开发人员,身为多技。
83 |
84 | 作者手疼,省去了几十个字。
85 |
86 | ### 5. 新的领域
87 |
88 | 嗯,只有新的领域,才存在更多的机会。
89 |
90 | 1. 边缘计算
91 | 2. 区块链
92 | 3. 客户端计算
93 | 4. ……
94 |
95 | 作者手疼,省去了几十个字。
96 |
97 | ### 6. 业务熟悉度
98 |
99 | 如果你不关心业务,对业务不了解,那么你哪来的自信,去领导整个前后端团队。
100 |
101 | 作者手疼,省去了几百个字。
102 |
103 | ## 结论
104 |
105 | 言而总之,总而言之:只有优秀的前端,才有必要讨论地位。抱怨,解决不了问题——只有起而行动,才能有效地解决问题。
106 |
107 | 这些也意味着,我们需要更深入的学习。笑~ :)
108 |
109 | 可是,到底从哪里开始学呢?有没有一本书,能解决这样的问题。
110 |
111 |
--------------------------------------------------------------------------------
/chapters/01-what.md:
--------------------------------------------------------------------------------
1 | # 大前端
2 |
3 | > 大前端是指通过 Web 开发相关的技术(WebView、JavaScript/TypeScript)所能开发、处理的领域。除了包含传统的 Web 前端相比,大前端还包含了——后端(Node.js 如 BFF 层、Serverless)、HTML 5 游戏、物联网、嵌入式应用、移动应用、桌面应用。它的显著特性是:**一次开发,多种平台**。
4 |
5 |
6 | ## 大前端:写给大家看的渐进式前端发展指南
7 |
8 | ## 为什么是大前端
9 |
10 | 为什么不是大后端呢?
11 |
12 | 前端,即面向用户交互,存在一系列的方式。
13 |
14 | 前端在变窄,
15 |
16 | ### JavaScript 的神奇魔力
17 |
18 | 易学、广泛使用、熟悉的语法、
19 |
20 | ### 无处不在的 WebView
21 |
22 | ## 什么是大前端??
23 |
24 | > 大前端是指通过 Web 开发相关的技术(WebView、JavaScript/TypeScript)所能开发、处理的领域。除了包含传统的 Web 前端相比,大前端还包含了——后端(Node.js 如 BFF 层、Serverless)、HTML 5 游戏、物联网、嵌入式应用、移动应用、桌面应用。它的显著特性是:**一次开发,多种平台**。
25 |
26 | 值得注意的是:对于某些领域而言,大前端技术并不是最好的技术,但是它是实现起来最快的技术,也因此特别适合于 **MVP 原型构建**。与此同时,前端技术的动态特性,特别适合于远程更新业务——只需要更新对应的代码,而不需要更新整个应用。
27 |
28 | 若是就这样领域,在前端里,我们可以分为多个领域。
29 |
30 | ## 大前端的技术领域
31 |
32 | ### 前端中的 Web 前端
33 |
34 | ### 前端中的 GUI
35 |
36 | 前端是软件 GUI(图形用户界面)的一种形式。从传统的应用厂商,到互联网应用的迁移,我们便可以看到相关的变化。
37 |
38 | ### 前端中的游戏
39 |
40 | ### 前端中的物联网(IoT)
41 |
42 | ### 前端中的移动端
43 |
44 | ### 前端中的 AR/VR
45 |
46 | ## 如何成为大前端?
47 |
48 |
--------------------------------------------------------------------------------
/chapters/02-backend.md:
--------------------------------------------------------------------------------
1 | # 大前端:后端
2 |
3 | 所谓的前端 in 后端,便是**在后端开发中,使用前端相关的语言和技术栈**。最典型的场景,便是使用 Node.js 开发后端服务。虽然 Node.js 已经有了 10 年的历史了,但是以我(Phodal)的角度来看,我更希望的是使用编译型语言,来开发后端服务。动态语言,无法使用编译器来检测错误,难以约束代码变动。
4 |
5 | ## 你并不需要 JavaScript 开发后端服务
6 |
7 | 我是一个程序员,然后才是一个前端工程师。使用 JavaScript 来开发后端服务,对于我来说吸引力并不是那么大。
8 |
9 | 作为一个程序员,我会使用 Java 语言编写后端代码,又或者是开发 Android 应用;因为我博客的缘故,我也会使用 Python 世界里的 Django 来开发后端应用,对于 Flask 也有一定的使用经验。使用 Java 这一点,对于大部分科班出身的程序员来说,它是不可缺少的一项技能。虽然,我并非科班出身,但是在实习的时候,我也有了大量的这门语言的使用经验。
10 |
11 | 只是呢,并不是所有的时候,我们都有足够的时间来编译运行。所以,我们有了 JavaScript,它的动态语言特性,非常适合于快速开发应用。于是乎,我们便有了
12 |
13 | ## 服务端渲染
14 |
15 |
16 | ### 模式:模板渲染
17 |
18 |
19 |
20 | 在早期,前端开发人员使用的是数据、代码与模板分离的设计,诸如在 Java 里使用 Mustache 来进行服务端渲染。
21 |
22 | 而随着前端技术的发展,代码和模板往往糅合在一起。
23 |
24 | ### 模式:异构渲染
25 |
26 | ## Node.js 打造后端服务
27 |
28 | 从社区的探索来看,存在一些完全使用 Node.js 开发的后台服务。但是,也存在一系列由于代码不规范造成的问题。从社区的经验来看,Node.js + Express + MongoDB + Angular/Vue/React,便是一些不错的选择。当然了,也有相当多的应用,只是采用了 Node.js 来完成 BFF 层(Backend For Frontends)。在这一层业务上,它只做业务数据的中间处理。
29 |
30 | 虽然,我经常建议在一些关键的节点上,不要采用 Node.js 来打造后台服务。可一旦涉及到 SPA 的服务端渲染,我们就不得不使用 Express、Koa 等这样的服务端 JavaScript/TypeScript 框架,来解决这样的问题。
31 |
32 | ### 模式
33 |
34 | - Express
35 | - Koa
36 | - Egg.js
37 | - NestJS
38 | - Hapi.js
39 | - Sails.js
40 |
41 | ### 是否大规模采用?
42 |
43 | 国内最大规模使用 Node.js 开发后端服务的团队是阿里巴巴:
44 |
45 | - Node 的 Collaborator 有 5 个国人, 其中 4 个在阿里巴巴这边,并且有国内唯一一个 CTC 成员。
46 |
47 | 所以,对于稍有余力的小型公司来说,除非逼不得请不要大规模采用。
48 |
49 | ## FaaS & Serverless
50 |
51 | > Serverless 架构是指大量依赖第三方服务(也叫做后端即服务,即“BaaS”)或暂存容器中运行的自定义代码(函数即服务,即“FaaS”)的应用程序,函数是无服务器架构中抽象语言运行时的最小单位。在这种架构中,我们并不看重运行一个函数需要多少 CPU 或 RAM 或任何其他资源,而是更看重运行函数所需的时间,我们也只为这些函数的运行时间付费。[^serverless]
52 |
53 | [^serverless]: [https://serverless.ink/](https://serverless.ink/)
54 |
55 | 作为一种折中方案,也是我最喜欢的方案。Serverless 架构是指大量依赖第三方服务(也叫做后端即服务,即“BaaS”)或暂存容器中运行的自定义代码(函数即服务,即“FaaS”)的应用程序,函数是无服务器架构中抽象语言运行时的最小单位。
56 |
57 | 对于没有后台经验的前端开发人员来说,使用 Node.js 开发后端应用是一种相当大的挑战。大多数非科班的前端程序员,不知道从数据库到 RESTful API 的一系列操作,并且还需要了解到部署等一系列的系统底层知识。因此,使用 Serverless 这种不关心基础设施的技术,可以进一步地降低开发成本。
58 |
59 |
60 | 采用 Serverless 架构,也就意味着,我们提取出了大量的基础设施。而使用 Node.js + JavaScript 作为胶水,来快速连接不同的服务,以形成一个快速有效的方案。并且,编写更少的代码,也意味着更安全、快速。
61 |
62 | 使用 AWS 来运行大量的 Serverless 计算的成本很高,但是自己搭建一个 Serverless 服务器,来运行自己的 Serverless 应用,则变成了一种更廉价的方式。除了直接基于 AWS 的 Serverless Framework 框架的方案,还有 OpenFaaS、Kubeless、OpenWhisk、Fission 等不同的 Serverless 框架。
63 |
64 | ## API Gateway & BFF
65 |
66 | ### API Gateway
67 |
68 | ### BFF
69 |
70 | > BFF,即 Backends For Frontends (服务于前端的后端),也就是服务器设计 API 时会考虑客户端的使用情况,在服务端根据不同的设备类型,返回不同客户端所需要的结果。BFF 模式,这种模式不会为所有的客户端创建通用的 API。而是创建多个 BFF 服务:一个用于 Web 前端、一个用于移动客户端(甚至一个用于 iOS,另一个用于 Android)等等。[^aofe]
71 |
72 | [^aofe]: 前端架构:从入门到微前端
73 |
--------------------------------------------------------------------------------
/chapters/03-iot.md:
--------------------------------------------------------------------------------
1 | # 大前端:IoT
2 |
3 | Web 应用的架构相比于一个物联网系统,无非就是多了一层硬件层以及可选的协调层。
4 |
5 | 
6 |
7 |
8 | 这个硬件层决定了物联网应用比Web应用更加复杂。对于大部分的Web应用来说 ,客户端都是手机、电脑、平板这些设备,都有着强大的处理能力,不需要考虑一些额外的因素。
9 |
10 | 对于物联网应用来说,我们需要考虑设备上的MCU的处理能力,根据其处理能力和使用环境使用不同的通信协议,如我们在一些设备上需要使用CoAP协议。在一些设备上不具备网络功能,需要考虑借助于可以联网的协助层,并且还需要使用一些短距离的无线传输协议,如低功耗蓝牙、红外、Zigbee等等。
11 |
12 | ## 传统的物联网
13 |
14 | 我毕业的时候,选定的毕业论文是一篇关于物联网的论文——《基于 REST 服务的最小物联网系统设计》。它是一篇入门级的论文,但凡是有 CS 基础的人,再加上一些硬件知识,都能写出这样的论文。不过我们的是毕业设计,论文反而有些其次,毕竟能不能过是以实物为主的。因此毕业设计动不动就要烧个几百 RMB,还不算服务器费用。然而,对于我们这些学习硬件为主的专业来说,就不是如此。
15 |
16 | 这篇论文是之前参加比赛的作品论文的“最小化”,里面使用到的主要就是创建RESTful服务,而它甚至称不上是一种技术。在这个作品里:
17 |
18 | * 我们使用Python语言里的Django框架作为Web服务框架,使用Django REST Framework来创建RESTful服务。
19 | * 为了使用手机当控制器,我们还要用Java写一个Android应用。
20 | * 我们使用Raspberry Pi作为硬件端的协调层,用于连接网络,并传输控制信号给硬件。在最初的设计里,因为便宜,我们打算使用运行OpenWRT的路由器来当硬件端的控制器。
21 | * 我们在硬件端使用Arduino作为控制器,写起代码特别简单,可以让我们关注于业务。
22 | * 我们还使用了Zigbee模块Xbee及I2C作为连接不同Arduino模块的的介质。
23 | * 最后,我们还需要在网页上做一个图表来显示实时数据。
24 |
25 | 所有的这些,我们需要使用Python、Java、JavaScript、C、Arduino五种语言。而如果我们要写相应的iOS应用,我们还需要Objective-C。
26 |
27 | 而同样的:
28 |
29 | 我刚实习的时候(2013 年),项目里使用的是 Backbone,作为单页面应用框架的核心来打造 Web 应用。这时,我开始关注于 Node.js,使用它实现物联网应用的可能性。当时,已经有了物联网协议 MQTT 和 CoAP 相关的库,便照猫画虎地写了一个支持 HTTP、CoAP、WebSocket 和 MQTT 的物联网。由于,当时缺乏一些大型应用的开发经典,所以做得并不是很好,但是已经可以看到 JavaScript 在这方面的远景。
30 |
31 | 一年多后,Ionic 也还没推出正式版,便发现到了这个框架真的很棒——它自带了一系列的 UI 组件,还用 NgCordova 封装了一系列 Cordova 的插件。便开始使用 Ionic 写了一些移动应用,发现还挺顺手的。接着,便拿这个框架尝试写物联网应用,这需要一些原生的插件,如 BLE、MQTT。后来,我也写了一个简单的 CoAP 插件。
32 |
33 | 后来,我们不再需要编译 Node.js(早期,使用的是 Raspberry Pi 来运行 Node.js),就可以在 ARM 处理器上运行 Node.js。并且我们已经有 Tessel、Espruino、Kinoma Create、Ruff 这些可以直接运行 JavaScript 的开发板。三星还推出 IoT.js,可以让更多的嵌入式设备,直接使用 JavaScript 作为开发语言。
34 |
35 | 人们开始在硬件上使用 JavaScript 的原因有很多,如 Web 的开发人员是最多的,而 JavaScript 很容易上手。
36 |
37 | ## 嵌入式设备分类
38 |
39 | ### 两种前端类型
40 |
41 | #### 嵌入式设备作为服务端
42 |
43 | 诸如 ESP32 或者 ESP8266,它们可以和传统的 Web 应用一样,托管前端应用。
44 |
45 | #### 嵌入式设备作为服务端和客户端
46 |
47 | 如 Android 系统中的
48 |
49 | Android Things 相似的还有 Microsoft 10 IoT、Raspberry Pi
50 |
51 | ### 嵌入式设备分类
52 |
53 | - 低资源受限设备,只能使用传统的编译型语言编写,如 C、C++、汇编,
54 | -
55 |
56 | #### 低资源受限设备:编译型语言
57 |
58 | 它们只能使用
59 |
60 | #### 普通嵌入式设备:
61 |
62 | Android 是用得最多的嵌入式系统,。
63 |
64 | OpenWRT 是用得最广泛的嵌入式系统之一,
65 |
66 | 在某些资源受限的设备上,我们无法运行一个带着完整 JavaScript 引擎的 WebView——出于软件体积、运行内存等因素的限制,去除了这些特性。诸如 CSS 3 中的动画特性,会占用大量的运算资源,算去掉了相应的支持。
67 |
68 | ## 固件编写
69 |
70 | ### Johnny-Five
71 |
72 | ### IoT.js
73 |
74 | ## Be Professinoal
75 |
76 | ### Arduino 与 Processing
77 |
78 | ### ESP866/ESP32 与 Lua
79 |
80 | ### C 语言
81 |
--------------------------------------------------------------------------------
/chapters/04-mobile.md:
--------------------------------------------------------------------------------
1 | # 大前端:移动端
2 |
3 | ## 混合应用
4 |
5 | 依我的角度来看,使用什么跨平台框架来看,区别并不是太大。目前主流的方案,仍然是原生(含跨平台框架) + HTML5 应用。从业务的角度上来看待这个问题,那么还是希望,可以用 HTML 5 的地方多——更新功能方便。
6 |
7 | 也因此,虽然在过去,笔者写过基于 React Native 的混合应用框架 Dore。我相信:Flutter 也会出现这样的混合应用框架。不过,对于有原生开发能力的团队来说,它们的框架还会是三部分:
8 |
9 | * 原生功能部分
10 | * 原生 + H5 的频繁更新部分
11 | * Fultter 的跨平台部分
12 |
13 | 写业务嘛,框架都只是工具。
14 |
15 | ## 跨平台应用
16 |
17 | ### React Native
18 |
19 | ### Flutter
20 |
21 | ### Weex
22 |
23 | ## 小程序
24 |
25 | 小程序,即 HTML5 小程序,即无需安装即可下载运行的应用程序。与普通的移动 Web 应用不同的是,**小程序相当于是高阶版的混合应用**。
26 |
27 | 如果只是从这一点上来看,其实是不是和微信一样的定制型小程序,并不是那么重要。重要的,在于与原生**界面结合,并提供离线使用功能。**它也是小程序与普通的 HTML 应用的区别。
28 |
--------------------------------------------------------------------------------
/chapters/05-desktop.md:
--------------------------------------------------------------------------------
1 | # 大前端:桌面应用
2 |
3 | ## 跨平台 WebView 应用框架
4 |
5 | Electron/NW.js
6 |
7 | ## 跨平台框架
8 |
9 | QT + WebView
10 |
--------------------------------------------------------------------------------
/chapters/06-nr.md:
--------------------------------------------------------------------------------
1 | # 大前端:VR/AR
2 |
3 | ## VR
4 |
5 | ## AR
6 |
--------------------------------------------------------------------------------
/chapters/07-visualization.md:
--------------------------------------------------------------------------------
1 | # 大前端:数据可视化
2 |
3 | 在过去我阅读的一些书籍里面,主要是以 Processing 作为可视化的语言——它起始于 2001 年,它最初是面向美术工作者和设计者创建的,后来变成了全面的设计和原型工具,可以用于创建复杂数据可视化领域。
4 |
5 | ### 高级篇
6 |
7 |
8 | ### D3.js
--------------------------------------------------------------------------------
/chapters/08-game.md:
--------------------------------------------------------------------------------
1 | # 大前端:游戏
2 |
3 | 随着移动端的性能不断变好,在 2019 年,我开始看好使用 HTML 5 技术来开发一些游戏。当然了,主要原因还是微信小游戏的出现。但是,不管怎样,我开始尝试在这个领域的探索。
4 |
5 | ## WebView 游戏
6 |
7 | ## JavaScript 语言游戏
8 |
9 |
--------------------------------------------------------------------------------
/chapters/10-fe.md:
--------------------------------------------------------------------------------
1 | # 大前端:前端
2 |
3 | 前端领域,在 2018 年已经趋于平衡,Angular、Vue、React 都没有出现太大的变化。
4 |
5 | ## Web 应用
6 |
7 | ### SPA (单页面应用)框架:A/V/R
8 |
9 | 架构选型上,也趋势于平衡。该用啥的还是用啥,偶尔还是会出现一些框架切换的新闻。尽管在 2019 年,会出现一些新的框架,但是还不太可能快引起变化。
10 |
11 | ### MPA (多页面应用)框架
12 |
13 | ## 语言
14 |
15 | ### TypeScript
16 |
17 | TypeScript 真香。
18 |
19 | 前端,没什么好看——除了,娱乐新闻。
20 |
--------------------------------------------------------------------------------
/chapters/11-ued.md:
--------------------------------------------------------------------------------
1 | # 大前端:UI & UX
2 |
3 | 自诩是一个 “斜杠青年” 的我,为自己添加不同的 “title”,设计师/设计学徒便是其中的一个。
4 |
5 | 一来,作为一个前端开发人员,日常就是要和用户打交道。几年经验下来,多多少少也能积累点经验;二来,除了职业本身,我也是一个喜爱画画的人。只是受限于工作的影响,在这方面的投入便没有那么多。
6 |
7 | 也因此,这样一来,我们便也是能得到一些慰藉。
8 |
9 | ## 绘画
10 |
11 | ## 用户体验设计
12 |
13 | ## 图形界面设计
14 |
15 |
--------------------------------------------------------------------------------
/chapters/12-cli.md:
--------------------------------------------------------------------------------
1 | # 大前端:命令行接口
2 |
3 | ## 传统 CLI vs 跨平台 CLI
4 |
5 | ### 优势?
6 |
7 | ###
8 |
9 | ## 相关实践和原则
10 |
11 |
12 | Best Practise?
13 |
14 | Mopass、ADR、Stepping,多多少少也算是写过一些 CLI。
15 |
16 | 除了此,使用 Node.js
17 |
18 | Clone 代码,执行复制
19 |
20 |
21 | 一般型 Shell,如 ``git clone``
22 |
23 | 而跨平台,则是 ``fs.copy`` 而非 GNU/Linux 上的 ``cp``,又或者是 Windows 上的 ``xcopy``
24 |
25 | 相似的工具和语言有 Python 等
26 |
27 | ### 为什么是 Node.js
28 |
29 | 因为我们是个前端,相关的环境肯定是有的。只要是个前端开发,一定会有 Node.js 环境。
30 |
31 |
--------------------------------------------------------------------------------
/chapters/999-fe.md:
--------------------------------------------------------------------------------
1 | # 前端,永不止境
2 |
3 | ## 大前端架构
4 |
5 |
6 | ## 练习
7 |
8 |
--------------------------------------------------------------------------------
/css/vendor.css:
--------------------------------------------------------------------------------
1 | @charset "UTF-8";
2 |
3 | .acodecode {
4 | cursor: pointer
5 | }
6 | .acodecodeactive {
7 | cursor: default
8 | }
9 | .codecodecode {
10 | position: fixed;
11 | z-index: 100;
12 | left: 2%;
13 | bottom: -30px;
14 | width: 96%;
15 | height: 0;
16 | max-height: 270px;
17 | text-align: left
18 | }
19 | .codecodecode>div {
20 | box-shadow: 0 0 3px #444
21 | }
22 | .codecodecontrols {
23 | height: 30px;
24 | margin-top: -30px;
25 | background-color: #fff;
26 | background-color: rgba(255, 255, 255, .8);
27 | border-radius: 8px 8px 0 0
28 | }
29 | .codecodecontrols a {
30 | float: left;
31 | line-height: 30px;
32 | margin-left: 6px;
33 | font-family: Arial;
34 | font-size: 12px
35 | }
36 | .codecodecontrols .closeCodeCode {
37 | float: right;
38 | margin-right: 6px
39 | }
40 | .acodecode.codecode, .codecode {
41 | border-radius: 0 !important;
42 | position: relative !important;
43 | width: 100% !important;
44 | margin: 0 !important;
45 | overflow: auto !important;
46 | cursor: default !important
47 | }
48 | div.codecode [id^=highlighter] div.bar.show, div.codecode [id^=highlighter] div.toolbar {
49 | display: none !important
50 | }
51 | h1, h2, h3, h4, h5, h6, p {
52 | margin-top: 0;
53 | padding-right: 15px;
54 | padding-left: 15px;
55 | text-align: left;
56 | font-family: Georgia
57 | }
58 | h1 {
59 | text-align: left
60 | }
61 | h2.booktitle {
62 | font-size: 1.5em;
63 | color: #666
64 | }
65 | .subhead-link {
66 | font-size: .75em;
67 | margin-left: -15px;
68 | margin-top: 5px;
69 | float: left;
70 | visibility: hidden
71 | }
72 | .subhead-link:hover {
73 | text-decoration: none;
74 | visibility: visible
75 | }
76 | h1:hover .subhead-link, h2:hover .subhead-link, h3:hover .subhead-link, h4:hover .subhead-link {
77 | visibility: visible
78 | }
79 | .container {
80 | background: #FFF
81 | }
82 | header {
83 | background: #fff
84 | }
85 | .content {
86 | padding: 10px 0;
87 | text-align: left
88 | }
89 | .footer {
90 | background: #fff
91 | }
92 | .fltrt {
93 | float: right;
94 | margin-left: 8px
95 | }
96 | .fltlft {
97 | float: left;
98 | margin-right: 8px
99 | }
100 | .clearfloat {
101 | clear: both;
102 | height: 0;
103 | font-size: 1px;
104 | line-height: 0
105 | }
106 | .copyright {
107 | text-align: left
108 | }
109 | .booktitle {
110 | text-align: center;
111 | line-height: 41px;
112 | border-bottom: 1px solid #fff;
113 | padding: 0;
114 | font-size: 2.2em
115 | }
116 | .booktitle.author {
117 | font-size: 24px
118 | }
119 | #contents-list {
120 | background: none repeat scroll 0 0 #EEE;
121 | border: 3px solid #DDD;
122 | padding: 1em 1em 1em 3em
123 | }
124 | .subitem {
125 | margin-left: 25px
126 | }
127 | #references-list {
128 | word-wrap: break-word
129 | }
130 | pre {
131 | display: block;
132 | line-height: 18px;
133 | background-color: #f5f5f5;
134 | -webkit-border-radius: 4px;
135 | -moz-border-radius: 4px;
136 | border-radius: 4px;
137 | white-space: pre;
138 | white-space: pre-wrap;
139 | word-break: break-all;
140 | word-wrap: break-word
141 | }
142 | div.syntaxhighlighter {
143 | padding: 1em 0
144 | }
145 | .syntaxhighlighter a, .syntaxhighlighter code, .syntaxhighlighter div, .syntaxhighlighter table, .syntaxhighlighter table caption, .syntaxhighlighter table tbody, .syntaxhighlighter table td, .syntaxhighlighter table thead, .syntaxhighlighter table tr, .syntaxhighlighter textarea {
146 | -moz-border-radius: 0 !important;
147 | -webkit-border-radius: 0 !important;
148 | background: none !important;
149 | border: 0 !important;
150 | bottom: auto !important;
151 | float: none !important;
152 | height: auto !important;
153 | left: auto !important;
154 | line-height: 1.1em !important;
155 | margin: 0 !important;
156 | outline: 0 !important;
157 | overflow: visible !important;
158 | padding: 0 !important;
159 | position: static !important;
160 | right: auto !important;
161 | text-align: left !important;
162 | top: auto !important;
163 | vertical-align: baseline !important;
164 | width: auto !important;
165 | box-sizing: content-box !important;
166 | font-family: Consolas, "Bitstream Vera Sans Mono", "Courier New", Courier, monospace !important;
167 | font-weight: 400 !important;
168 | font-style: normal !important;
169 | font-size: 1em !important;
170 | min-height: inherit !important;
171 | min-height: auto !important
172 | }
173 | .syntaxhighlighter {
174 | width: 100% !important;
175 | margin: 1em 0 !important;
176 | position: relative !important;
177 | overflow: auto !important;
178 | font-size: 1em !important
179 | }
180 | .syntaxhighlighter.source {
181 | overflow: hidden !important
182 | }
183 | .syntaxhighlighter .bold {
184 | font-weight: 700 !important
185 | }
186 | .syntaxhighlighter .italic {
187 | font-style: italic !important
188 | }
189 | .syntaxhighlighter .line {
190 | white-space: pre !important
191 | }
192 | .syntaxhighlighter table {
193 | width: 100% !important
194 | }
195 | .syntaxhighlighter table caption {
196 | text-align: left !important;
197 | padding: .5em 0 .5em 1em !important
198 | }
199 | .syntaxhighlighter table td.code {
200 | width: 100% !important
201 | }
202 | .syntaxhighlighter table td.code .container {
203 | position: relative !important
204 | }
205 | .syntaxhighlighter table td.code .container textarea {
206 | box-sizing: border-box !important;
207 | position: absolute !important;
208 | left: 0 !important;
209 | top: 0 !important;
210 | width: 100% !important;
211 | height: 100% !important;
212 | border: 0 !important;
213 | background: #fff !important;
214 | padding-left: 1em !important;
215 | overflow: hidden !important;
216 | white-space: pre !important
217 | }
218 | .syntaxhighlighter table td.gutter .line {
219 | text-align: right !important;
220 | padding: 0 .5em 0 1em !important
221 | }
222 | .syntaxhighlighter table td.code .line {
223 | padding: 0 1em !important
224 | }
225 | .syntaxhighlighter.nogutter td.code .container textarea, .syntaxhighlighter.nogutter td.code .line {
226 | padding-left: 0 !important
227 | }
228 | .syntaxhighlighter.show {
229 | display: block !important
230 | }
231 | .syntaxhighlighter.collapsed table {
232 | display: none !important
233 | }
234 | .syntaxhighlighter.collapsed .toolbar {
235 | padding: .1em .8em 0 !important;
236 | font-size: 1em !important;
237 | position: static !important;
238 | width: auto !important;
239 | height: auto !important
240 | }
241 | .syntaxhighlighter.collapsed .toolbar span {
242 | display: inline !important;
243 | margin-right: 1em !important
244 | }
245 | .syntaxhighlighter.collapsed .toolbar span a {
246 | padding: 0 !important;
247 | display: none !important
248 | }
249 | .syntaxhighlighter.collapsed .toolbar span a.expandSource {
250 | display: inline !important
251 | }
252 | .syntaxhighlighter .toolbar {
253 | position: absolute !important;
254 | right: 1px !important;
255 | top: 1px !important;
256 | width: 11px !important;
257 | height: 11px !important;
258 | font-size: 10px !important;
259 | z-index: 10 !important
260 | }
261 | .syntaxhighlighter .toolbar span.title {
262 | display: inline !important
263 | }
264 | .syntaxhighlighter .toolbar a {
265 | display: block !important;
266 | text-align: center !important;
267 | text-decoration: none !important;
268 | padding-top: 1px !important
269 | }
270 | .syntaxhighlighter .toolbar a.expandSource {
271 | display: none !important
272 | }
273 | .syntaxhighlighter.ie {
274 | font-size: .9em !important;
275 | padding: 1px 0 !important
276 | }
277 | .syntaxhighlighter.ie .toolbar {
278 | line-height: 8px !important
279 | }
280 | .syntaxhighlighter.ie .toolbar a {
281 | padding-top: 0 !important
282 | }
283 | .syntaxhighlighter.printing .line.alt1 .content, .syntaxhighlighter.printing .line.alt2 .content, .syntaxhighlighter.printing .line.highlighted .number, .syntaxhighlighter.printing .line.highlighted.alt1 .content, .syntaxhighlighter.printing .line.highlighted.alt2 .content {
284 | background: none !important
285 | }
286 | .syntaxhighlighter.printing .line .number {
287 | color: #bbb !important
288 | }
289 | .syntaxhighlighter.printing .line .content {
290 | color: #000 !important
291 | }
292 | .syntaxhighlighter.printing .toolbar {
293 | display: none !important
294 | }
295 | .syntaxhighlighter.printing a {
296 | text-decoration: none !important
297 | }
298 | .syntaxhighlighter.printing .plain, .syntaxhighlighter.printing .plain a {
299 | color: #000 !important
300 | }
301 | .syntaxhighlighter.printing .comments, .syntaxhighlighter.printing .comments a {
302 | color: #008200 !important
303 | }
304 | .syntaxhighlighter.printing .string, .syntaxhighlighter.printing .string a {
305 | color: #00f !important
306 | }
307 | .syntaxhighlighter.printing .keyword {
308 | color: #069 !important;
309 | font-weight: 700 !important
310 | }
311 | .syntaxhighlighter.printing .preprocessor {
312 | color: gray !important
313 | }
314 | .syntaxhighlighter.printing .variable {
315 | color: #a70 !important
316 | }
317 | .syntaxhighlighter.printing .value {
318 | color: #090 !important
319 | }
320 | .syntaxhighlighter.printing .functions {
321 | color: #ff1493 !important
322 | }
323 | .syntaxhighlighter.printing .constants {
324 | color: #06c !important
325 | }
326 | .syntaxhighlighter.printing .script {
327 | font-weight: 700 !important
328 | }
329 | .syntaxhighlighter.printing .color1, .syntaxhighlighter.printing .color1 a {
330 | color: gray !important
331 | }
332 | .syntaxhighlighter.printing .color2, .syntaxhighlighter.printing .color2 a {
333 | color: #ff1493 !important
334 | }
335 | .syntaxhighlighter.printing .color3, .syntaxhighlighter.printing .color3 a {
336 | color: red !important
337 | }
338 | .syntaxhighlighter.printing .break, .syntaxhighlighter.printing .break a {
339 | color: #000 !important
340 | }
341 | .syntaxhighlighter, .syntaxhighlighter .line.alt1, .syntaxhighlighter .line.alt2 {
342 | background-color: #1b2426 !important
343 | }
344 | .syntaxhighlighter .line.highlighted.alt1, .syntaxhighlighter .line.highlighted.alt2 {
345 | background-color: #323e41 !important
346 | }
347 | .syntaxhighlighter .line.highlighted.number, .syntaxhighlighter table caption {
348 | color: #b9bdb6 !important
349 | }
350 | .syntaxhighlighter .gutter {
351 | color: #afafaf !important
352 | }
353 | .syntaxhighlighter .gutter .line {
354 | border-right: 3px solid #435a5f !important
355 | }
356 | .syntaxhighlighter .gutter .line.highlighted {
357 | background-color: #435a5f !important;
358 | color: #1b2426 !important
359 | }
360 | .syntaxhighlighter.printing .line .content {
361 | border: 0 !important
362 | }
363 | .syntaxhighlighter.collapsed {
364 | overflow: visible !important
365 | }
366 | .syntaxhighlighter.collapsed .toolbar {
367 | color: #5ba1cf !important;
368 | background: #000 !important;
369 | border: 1px solid #435a5f !important
370 | }
371 | .syntaxhighlighter.collapsed .toolbar a {
372 | color: #5ba1cf !important
373 | }
374 | .syntaxhighlighter.collapsed .toolbar a:hover {
375 | color: #5ce638 !important
376 | }
377 | .syntaxhighlighter .toolbar {
378 | color: #fff !important;
379 | background: #435a5f !important;
380 | border: 0 !important
381 | }
382 | .syntaxhighlighter .toolbar a {
383 | color: #fff !important
384 | }
385 | .syntaxhighlighter .toolbar a:hover {
386 | color: #e0e8ff !important
387 | }
388 | .syntaxhighlighter .plain, .syntaxhighlighter .plain a {
389 | color: #b9bdb6 !important
390 | }
391 | .syntaxhighlighter .comments, .syntaxhighlighter .comments a {
392 | color: #878a85 !important
393 | }
394 | .syntaxhighlighter .string, .syntaxhighlighter .string a {
395 | color: #5ce638 !important
396 | }
397 | .syntaxhighlighter .keyword {
398 | color: #5ba1cf !important
399 | }
400 | .syntaxhighlighter .preprocessor {
401 | color: #435a5f !important
402 | }
403 | .syntaxhighlighter .variable {
404 | color: #ffaa3e !important
405 | }
406 | .syntaxhighlighter .value {
407 | color: #090 !important
408 | }
409 | .syntaxhighlighter .functions {
410 | color: #ffaa3e !important
411 | }
412 | .syntaxhighlighter .constants {
413 | color: #e0e8ff !important
414 | }
415 | .syntaxhighlighter .script {
416 | font-weight: 700 !important;
417 | color: #5ba1cf !important;
418 | background-color: none !important
419 | }
420 | .syntaxhighlighter .color1, .syntaxhighlighter .color1 a {
421 | color: #e0e8ff !important
422 | }
423 | .syntaxhighlighter .color2, .syntaxhighlighter .color2 a {
424 | color: #fff !important
425 | }
426 | .syntaxhighlighter .color3, .syntaxhighlighter .color3 a {
427 | color: #ffaa3e !important
428 | }
429 | .sausage-set {
430 | position: fixed;
431 | right: 0;
432 | top: 0;
433 | width: 15px;
434 | height: 100%;
435 | border-left: solid 2px #fff;
436 | border-right: solid 2px #fff;
437 | background-color: #fff;
438 | font-family: 'Helvetica Neue', Arial, sans-serif
439 | }
440 | .sausage {
441 | position: absolute;
442 | left: 0;
443 | width: 100%;
444 | height: 100%;
445 | background-color: #f1f1f1;
446 | text-decoration: none;
447 | -moz-border-radius: 8px;
448 | -webkit-border-bottom-left-radius: 8px;
449 | -webkit-border-top-left-radius: 8px;
450 | -webkit-border-bottom-right-radius: 8px;
451 | -webkit-border-top-right-radius: 8px;
452 | -moz-box-shadow: inset 0 1px 2px 4px rgba(0, 0, 0, .025);
453 | -webkit-box-shadow: inset 0 1px 2px 4px rgba(0, 0, 0, .025);
454 | cursor: pointer
455 | }
456 | .sausage-current, .sausage-hover {
457 | background-color: #f2e4ed;
458 | -moz-box-shadow: inset 0 1px 2px 4px rgba(51, 63, 70, .025)
459 | }
460 | .sausage-span {
461 | position: absolute;
462 | right: 24px;
463 | top: 5px;
464 | z-index: 2;
465 | display: none;
466 | width: 100px;
467 | padding: 2px 3px;
468 | color: #000;
469 | background-color: #fff;
470 | border: solid 2px #906;
471 | font-size: 10px;
472 | line-height: 12px;
473 | font-weight: 700;
474 | text-align: center;
475 | -moz-border-radius: 7px;
476 | -webkit-border-bottom-left-radius: 7px;
477 | -webkit-border-top-left-radius: 7px;
478 | -webkit-border-bottom-right-radius: 7px;
479 | -webkit-border-top-right-radius: 7px;
480 | -moz-box-shadow: 1px 1px 1px 1px rgba(0, 0, 0, .05);
481 | -webkit-box-shadow: 1px 1px 1px 1px rgba(0, 0, 0, .05)
482 | }
483 | .sausage-current .sausage-span, .sausage-hover .sausage-span {
484 | display: block
485 | }
486 | a, abbr, acronym, address, article, aside, blockquote, body, caption, code, dd, del, dfn, dialog, div, dl, dt, em, fieldset, figure, footer, form, h1, h2, h3, h4, h5, h6, header, hgroup, html, iframe, img, label, legend, li, nav, object, ol, p, pre, q, section, span, table, tbody, td, tfoot, th, thead, tr, ul {
487 | margin: 0;
488 | padding: 0;
489 | border: 0;
490 | font-weight: inherit;
491 | font-style: inherit;
492 | font-size: 100%;
493 | font-family: inherit;
494 | vertical-align: baseline
495 | }
496 | article, aside, dialog, figure, footer, header, hgroup, nav, section {
497 | display: block
498 | }
499 | body {
500 | line-height: 1.5
501 | }
502 | table {
503 | border-collapse: separate;
504 | border-spacing: 0
505 | }
506 | caption, td, th {
507 | text-align: left;
508 | font-weight: 400
509 | }
510 | table, td, th {
511 | vertical-align: middle
512 | }
513 | blockquote:after, blockquote:before, q:after, q:before {
514 | content: ""
515 | }
516 | blockquote, q {
517 | quotes: "" ""
518 | }
519 | a img {
520 | border: 0
521 | }
522 | .search, body, input[type=submit], input[type=text] {
523 | font-family: Palatino, "Palatino Linotype", Georgia, Times, "Times New Roman", serif
524 | }
525 | html {
526 | font-size: 100.01%
527 | }
528 | h1, h2, h3, h4, h5, h6 {
529 | font-weight: 400;
530 | color: #000
531 | }
532 | h1 {
533 | font-size: 3em;
534 | line-height: 1;
535 | margin-bottom: .5em
536 | }
537 | h2 {
538 | font-size: 2em;
539 | margin-bottom: .75em
540 | }
541 | h3 {
542 | font-size: 1.5em;
543 | line-height: 1
544 | }
545 | h4 {
546 | font-size: 1.2em;
547 | line-height: 1.25;
548 | margin-bottom: 1.25em
549 | }
550 | h5 {
551 | font-size: 1em;
552 | font-weight: 700;
553 | margin-bottom: 1.5em
554 | }
555 | h6 {
556 | font-size: 1em;
557 | font-weight: 700
558 | }
559 | h1 img, h2 img, h3 img, h4 img, h5 img, h6 img {
560 | margin: 0
561 | }
562 | p {
563 | margin: 0 0 1.5em
564 | }
565 | p img.left {
566 | float: left;
567 | margin: 1.5em 1.5em 1.5em 0;
568 | padding: 0
569 | }
570 | p img.right {
571 | float: right;
572 | margin: 1.5em 0 1.5em 1.5em
573 | }
574 | strong {
575 | font-weight: 700
576 | }
577 | dfn, em {
578 | font-style: italic
579 | }
580 | dfn {
581 | font-weight: 700
582 | }
583 | sub, sup {
584 | line-height: 0
585 | }
586 | address {
587 | margin: 0 0 1.5em;
588 | font-style: italic
589 | }
590 | del {
591 | color: #666
592 | }
593 | li ol, li ul {
594 | margin: 0
595 | }
596 | ol, ul {
597 | margin: 0 1.5em 1.5em 0;
598 | padding-left: 3.333em
599 | }
600 | ul {
601 | list-style-type: disc
602 | }
603 | ol {
604 | list-style-type: decimal
605 | }
606 | dl {
607 | margin: 0 0 1.5em
608 | }
609 | dl dt {
610 | font-weight: 700
611 | }
612 | dd {
613 | margin-left: 1.5em
614 | }
615 | table {
616 | margin-bottom: 1.4em
617 | }
618 | th {
619 | font-weight: 700
620 | }
621 | thead th {
622 | background: #c3d9ff
623 | }
624 | caption, td, th {
625 | padding: 4px 10px 4px 5px
626 | }
627 | tfoot {
628 | font-style: italic
629 | }
630 | body {
631 | font-size: 100%;
632 | color: #000;
633 | background: #F6f6F6 url(../images/base.png) repeat 0 0
634 | }
635 | h3 {
636 | border-bottom: 1px solid #CCC;
637 | margin-bottom: .5em;
638 | padding-bottom: .5em
639 | }
640 | .lead {
641 | font-size: 1.5em
642 | }
643 | .stage_links {
644 | color: #777
645 | }
646 | a, a:link, a:visited {
647 | color: #906;
648 | text-decoration: none
649 | }
650 | a:active, a:focus, a:hover {
651 | color: #E106B2;
652 | text-decoration: underline
653 | }
654 | hr.space {
655 | background: #fff;
656 | color: #fff;
657 | visibility: hidden
658 | }
659 | hr {
660 | background: #CCC;
661 | color: #CCC;
662 | clear: both;
663 | float: none;
664 | width: 100%;
665 | height: .1em;
666 | margin: 0 0 1.45em;
667 | border: 0
668 | }
669 | hr.bold {
670 | height: 1px;
671 | background-color: #906;
672 | color: #906
673 | }
674 | blockquote {
675 | overflow: hidden;
676 | margin: 0 0 1.5em;
677 | padding: 0 1.5em;
678 | color: #000;
679 | font-style: normal
680 | }
681 | blockquote p {
682 | margin-bottom: .5em
683 | }
684 | .attribution {
685 | font-style: italic;
686 | text-align: right;
687 | color: #777
688 | }
689 | table {
690 | width: 100%
691 | }
692 | code {
693 | padding: 2px 4px;
694 | color: #D14;
695 | background-color: #F7F7F9;
696 | border: 1px solid #E1E1E8;
697 | font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
698 | font-size: 12px;
699 | -webkit-border-radius: 3px;
700 | -moz-border-radius: 3px;
701 | border-radius: 3px
702 | }
703 | .caption, caption {
704 | caption-side: bottom;
705 | background: 0 0;
706 | font-style: italic
707 | }
708 | tr.even td {
709 | background: #F4F4F4
710 | }
711 | tfoot td {
712 | border-top: 1px solid #EAEAEA;
713 | font-weight: 700;
714 | font-style: normal
715 | }
716 | abbr, acronym {
717 | border: 0
718 | }
719 | abbr[title]:hover {
720 | border-bottom: 1px dotted #666;
721 | cursor: help
722 | }
723 | .red {
724 | background: red
725 | }
726 | .highlight {
727 | background: #EEC3C3
728 | }
729 | .blocks:after, .clearfix:after, .container:after, .edition_list:after, .editions:after, .inner:after {
730 | content: "\0020";
731 | display: block;
732 | height: 0;
733 | clear: both;
734 | visibility: hidden;
735 | overflow: hidden
736 | }
737 | .blocks, .clearfix, .container, .edition_list, .editions, .inner {
738 | display: block
739 | }
740 | .clear {
741 | clear: both
742 | }
743 | .container {
744 | max-width: 940px;
745 | margin: 0 auto;
746 | padding: 0 16px;
747 | text-align: left
748 | }
749 | .gttr {
750 | margin-right: 2.9%;
751 | float: left
752 | }
753 | .last {
754 | float: left
755 | }
756 | .sidebar {
757 | width: 32%;
758 | float: right;
759 | margin-right: 0
760 | }
761 | .content_footer {
762 | clear: both
763 | }
764 | .span10, .span2, .span3, .span4, .span6, .span8 {
765 | margin-right: 2.3%;
766 | float: left
767 | }
768 | .span2 {
769 | width: 14.6341%
770 | }
771 | .span3 {
772 | width: 23.1707%
773 | }
774 | .span4 {
775 | width: 31.7073%
776 | }
777 | .span6 {
778 | width: 48.7805%
779 | }
780 | .span8 {
781 | width: 65.853658%
782 | }
783 | .span10 {
784 | width: 73.17073%
785 | }
786 | .blocks, .editions {
787 | margin: 1.5em 0;
788 | clear: both
789 | }
790 | .editions {
791 | margin-bottom: 0
792 | }
793 | .last {
794 | margin-right: 0 !important
795 | }
796 | .lyt_img {
797 | max-width: 100%
798 | }
799 | .page_header {
800 | overflow: hidden;
801 | padding: 0 0 .2em;
802 | margin: 1.5em 0
803 | }
804 | .logo {
805 | width: 49.5%;
806 | float: left;
807 | margin-bottom: 0
808 | }
809 | .logo span {
810 | display: block;
811 | font-style: italic;
812 | font-size: .5em
813 | }
814 | .logo a, .logo a:link, .logo a:visited {
815 | color: #000
816 | }
817 | .logo a:active, .logo a:focus, .logo a:hover {
818 | color: #906;
819 | text-decoration: none
820 | }
821 | .search_box {
822 | width: 50%;
823 | float: right
824 | }
825 | .search {
826 | width: auto;
827 | float: right;
828 | vertical-align: middle
829 | }
830 | .edition_list, .info_bubble, .inner, .stage {
831 | padding: 1em 1.5em;
832 | background: #FFF;
833 | -moz-border-radius: 3px;
834 | -webkit-border-radius: 3px;
835 | border-radius: 3px;
836 | -moz-box-shadow: 0 0 8px #999;
837 | -webkit-box-shadow: 0 0 8px #999;
838 | box-shadow: 0 0 8px #999
839 | }
840 | .stage {
841 | margin-bottom: 1.5em;
842 | padding: 3em 50% 1.5em 3em
843 | }
844 | .edition_list {
845 | margin: 0 0 1em;
846 | clear: both;
847 | overflow: hidden
848 | }
849 | .edition_list p {
850 | margin-bottom: 0;
851 | font-style: italic
852 | }
853 | .edition_list h2 {
854 | margin-bottom: 0;
855 | font-size: 1.8em;
856 | line-height: 1.5;
857 | font-weight: 700
858 | }
859 | .edition_list ul {
860 | margin: 0;
861 | padding: 0;
862 | list-style: none;
863 | display: inline
864 | }
865 | .edition_list ul li {
866 | display: inline;
867 | margin-right: 1em;
868 | padding-right: 1em;
869 | border-right: 1px solid #F6f6F6
870 | }
871 | .edition_list ul li:last-child {
872 | margin-right: 0;
873 | padding-right: 0;
874 | border-right: 0
875 | }
876 | .info_bubble {
877 | padding-bottom: 1em;
878 | color: #FFF;
879 | text-shadow: 1px 1px 0 #000;
880 | background: #906;
881 | position: relative;
882 | overflow: visible
883 | }
884 | .info_bubble p {
885 | margin-bottom: 0
886 | }
887 | .info_bubble.startpage {
888 | padding-bottom: 1em;
889 | min-height: 9em;
890 | height: auto !important;
891 | height: 9em;
892 | z-index: 0
893 | }
894 | .info_bubble.startpage span {
895 | position: absolute;
896 | display: block;
897 | bottom: 1em;
898 | left: -2em;
899 | width: 40px;
900 | height: 40px;
901 | z-index: 100
902 | }
903 | .blocks>div {
904 | margin-bottom: 1.5em
905 | }
906 | .footer {
907 | margin: 1.5em 0 0;
908 | padding: 1.5em 0 0;
909 | overflow: hidden
910 | }
911 | #buy {
912 | position: relative
913 | }
914 | #offers {
915 | position: absolute;
916 | bottom: 0;
917 | font-size: .625em;
918 | margin: 0;
919 | padding: 0;
920 | list-style: none;
921 | display: none
922 | }
923 | .save_a_tree h3 {
924 | display: none;
925 | line-height: 1.5;
926 | border-bottom: 0
927 | }
928 | #offers li {
929 | display: inline;
930 | margin-right: 1em;
931 | padding-right: 1em;
932 | border-right: 1px solid #F6f6F6
933 | }
934 | #offers li:last-child {
935 | margin-right: 0;
936 | padding-right: 0;
937 | border-right: 0
938 | }
939 | .shopping_cart_button_line, .shopping_cart_button_line_bottom {
940 | border-top: 1px solid #CCC;
941 | margin-top: .5em;
942 | padding: .5em 1px 0;
943 | overflow: hidden;
944 | clear: both
945 | }
946 | .shopping_cart_button_line_bottom {
947 | margin-bottom: 7em
948 | }
949 | .shopping_cart_button_line:first-child {
950 | border-top: 0
951 | }
952 | div.header {
953 | font-size: 1.5em;
954 | line-height: 1;
955 | border-bottom: 1px solid #CCC;
956 | margin-bottom: .5em;
957 | padding-bottom: .5em;
958 | color: #000;
959 | text-shadow: 1px 1px 0 #FFF
960 | }
961 | .product_title {
962 | font-style: italic;
963 | margin-bottom: 1.5em
964 | }
965 | .product_price {
966 | float: left
967 | }
968 | .price {
969 | color: #666
970 | }
971 | .product_buy_link {
972 | float: right;
973 | line-height: 1
974 | }
975 | .ebook_formats, .safari_read_now, .whatisthis {
976 | font-size: .75em
977 | }
978 | .safari_read_now {
979 | float: right;
980 | line-height: 2
981 | }
982 | input[type=submit], input[type=text] {
983 | font-size: 1em;
984 | -moz-border-radius: .5em;
985 | -webkit-border-radius: .5em;
986 | border-radius: .5em;
987 | vertical-align: middle
988 | }
989 | input[type=text] {
990 | width: 140px;
991 | margin: .5em 0;
992 | padding: .5em .5em .3em;
993 | border: 1px solid #F1F1F0;
994 | color: #666;
995 | -moz-box-shadow: inset 3px 3px 2px #999;
996 | -webkit-box-shadow: inset 3px 3px 2px #999;
997 | box-shadow: inset 3px 3px 2px #999
998 | }
999 | input[type=text]:active, input[type=text]:focus, input[type=text]:hover {
1000 | background: #FFF;
1001 | color: #000;
1002 | outline: 0
1003 | }
1004 | input[type=submit] {
1005 | width: auto;
1006 | margin: 0;
1007 | padding: .28em .5em;
1008 | background: #906;
1009 | color: #FFF;
1010 | border: 0;
1011 | text-transform: uppercase;
1012 | cursor: pointer;
1013 | text-shadow: 1px 1px 0 #000;
1014 | -moz-box-shadow: 0 0 2px #999;
1015 | -webkit-box-shadow: 0 0 2px #999;
1016 | box-shadow: 0 0 2px #999
1017 | }
1018 | input[type=submit]:active, input[type=submit]:focus, input[type=submit]:hover {
1019 | color: #FFF;
1020 | background: #E106B2;
1021 | text-shadow: 1px 1px 0 #000;
1022 | -moz-box-shadow: 0 0 3px #333;
1023 | -webkit-box-shadow: 0 0 3px #333;
1024 | box-shadow: 0 0 3px #333
1025 | }
1026 | a.anchor {
1027 | color: #000
1028 | }
1029 | a.anchor:hover {
1030 | text-decoration: none
1031 | }
1032 | .figure {
1033 | padding-top: 1.5em
1034 | }
1035 | .caption, .figure {
1036 | display: block;
1037 | margin-bottom: 1.5em;
1038 | text-align: center
1039 | }
1040 | .figure * {
1041 | text-align: left
1042 | }
1043 | .caption {
1044 | font-size: .875em
1045 | }
1046 | .figure .caption {
1047 | text-align: center
1048 | }
1049 | .figure img {
1050 | margin-bottom: .5em;
1051 | max-width: 100%
1052 | }
1053 | .attribution::before {
1054 | content: ""
1055 | }
1056 | .sidebar ul {
1057 | font-size: .9em;
1058 | padding-left: 2em;
1059 | list-style-type: square
1060 | }
1061 | .disabled, .footer a, .footer a:active, .footer a:focus, .footer a:hover, .footer a:link, .footer a:visited, .info_bubble a, .info_bubble a:active, .info_bubble a:focus, .info_bubble a:hover, .info_bubble a:link, .info_bubble a:visited {
1062 | color: #BBB
1063 | }
1064 | .buybuttonswidget {
1065 | padding-bottom: 10px;
1066 | background: #FFF
1067 | }
1068 | @media (max-width:320px) {
1069 | .container {
1070 | max-width: 300px
1071 | }
1072 | #reviews {
1073 | display: none
1074 | }
1075 | }
1076 | @media (min-width:768px) and (max-width:1024px) {
1077 | .container {
1078 | max-width: 740px;
1079 | width: 740px
1080 | }
1081 | }
1082 | @media (min-width:1025px) {
1083 | .container {
1084 | width: 740px
1085 | }
1086 | }
--------------------------------------------------------------------------------
/docs/adr/0001-添加分章阅读的功能方便于-seo.md:
--------------------------------------------------------------------------------
1 | # 1. 添加分章阅读的功能,方便于 SEO
2 |
3 | Date: 2019-01-22
4 |
5 | ## Status
6 |
7 | 2019-01-22 proposed
8 |
9 | ## Context
10 |
11 | Context here...
12 |
13 | ## Decision
14 |
15 | Decision here...
16 |
17 | ## Consequences
18 |
19 | Consequences here...
20 |
--------------------------------------------------------------------------------
/docs/adr/README.md:
--------------------------------------------------------------------------------
1 | # Architecture Decision Records
2 |
3 | * [1. 添加分章阅读的功能方便于-seo](0001-添加分章阅读的功能方便于-seo.md)
--------------------------------------------------------------------------------
/ebook.md:
--------------------------------------------------------------------------------
1 |
2 | # 前言
3 | 下一步,我们要去哪里?
4 |
5 | 成为程序员并不是一件容易的事,又并不代表你与你隔壁的小王相比,就能多赚很多钱。这个行业有各种有趣的人,有的是科班出身,有的是自学成长 ,有的是培训养成。来自哪里并不重要,重要的是你要去哪里。
6 |
7 | 也许是金三银四的到来,也许是越来越多的人被优化,也许是春天的到来。它们结合起来,让愈来愈的人开始焦虑起来。尤其是对于那些工作 2~3 年的人来说,这种焦虑愈加的明显。工作 1~2 年的,技术上的问题还有一系列待解决。像我这样工作 4~5 年的人,又有别的焦虑。如最近,我一直忙于构建自己想要的 Tech Lead 的模型、技能——如果有一个东西绕不开,那便是时候学习相关的内容。与此同时,我还开始在编写一个大前端相关的电子书——《大前端的扩张》。
8 |
9 | ## 前端工程师是什么?
10 |
11 | **前端工程师是工程师,后端工程师是工程师,他/她们并没有不同。**
12 |
13 | 前端工程师是精细分工的一个产物。工程师被分为了前端工程师、后端工程师,对应的团队的 Tech Lead 也有了两个,相应的 KPI 考察也变成了两个团队。尽管出于方便的原因,某些公司向上汇报的渠道往往只有一个:后端工程师。这对于想往管理发展的前端程序员来说,这并不是一件好事。但是对于想往技术发展的前端工程师来说,这一点相当的不错——少去了一系列的会议,还不需要一些“没有意义”的加班。
14 |
15 | 前端工程师和后端工程师并没有区别,他/她们首先都是人,然后才是工程师,然后才是前/后端工程师。至于这些人,什么时候被机器替换,倒是有一个好问题。后端有 Serverless 这样的大山压在前面,前端有 AI 生成代码的挑战在面前。他/她们有各自的焦虑,后端有自己的无聊,前端有自己的热闹。
16 |
17 | **前端没有地位?**
18 |
19 | 有没有地位,这是一个好问题。可是呢,“你真的在乎这个地位吗?”当我在吐槽“前端没有地位”的时候,我可能有多个意思:
20 |
21 | 1. 公司给前端的机会太少了。
22 | 2. 我做的事情带来了更多的价值,应该有更多的收入。
23 | 3. 前端开发的话语权太小了。
24 |
25 | 那么,你呢?是否也是如此。我们只是在一些利益上做争夺而已——可是呢,当我们投入同样的时间,却没有同样的回报时。我们也应该去争夺一些相关的权益。
26 |
27 | 可若是呢,要是你的项目里,不懂前端的后端为前端下决策,那就说明:在你的公司里,前端是真没地位,又或者是没有人愿意去争取。原因呢,无非就是一个萝卜一个坑,而那个坑已经被后端占据了。
28 |
29 | 前端的历史太短,刚刚浴火重新的时候,后端已经把持朝政好几年了。
30 |
31 | **前端不重要?**
32 |
33 | 前端有没有那么重要,是人们心中的想法——大多数程序员(包含我)很难去说服别人,我只会以实际行动去证明。可是,前端真的不重要么?这是一个相当大的误区,既然你需要前端,那说明在这个项目前端是必不可少的。既然必不可少,那它就是重要的。
34 |
35 | 只是大家口中的前端不重要,并不是说前端不重要。而是前端的某些部分对于他/她而言,不那么重要了:
36 |
37 | - “我不需要复杂的交互,只需要可以工作就好”。
38 | - “我不需要酷炫的动画,只需要可以串起来就好”。
39 | - “我不知道校验输入结果,反正后端可以校验就好”。
40 |
41 | 然后,等你按这个标准实现的时候,会发现他/她们对这个效果非常不满意。总有人会如此,口口声声觉得前端不重要,最后还是需要一个重要的前端角色。不重要,只是压缩支出和时间的一个手段;不重要,只是当前不重要。
42 |
43 | 我们觉得重要,那就是重要。
44 |
45 | **前端工程师是工程师,后端工程师是工程师,他/她们并没有不同。**
46 |
47 | 一个不学习前端的后端,不能成为一个优秀的工程师。一个不学习后端的前端,也无法成为优秀的工程师。“学习” 包含了两层含义,一个是 GET 学习这个技能,一个是掌握新的技能。
48 |
49 | 一个不了解后端的前端,和那个你鄙视的后端没有太大的区别。编程语言是一个前端,前后端是一个谋生的端点。**你是一个程序员,你需要持续的学习。**
50 |
51 | ## 广度与深度
52 |
53 | 前端是一个门槛越来越低的岗位,那些新人啊,可以快速来到这个行业,COPY/Paste 出类似的代码
54 |
55 | 广度与深度,并不值得讨论。一个公司中的架构师,绝不只是前端工程师,也绝不只是一个后端工程师。同样的问题,如果有一个人让你只学前端,那个人可能是你领导,或者是大公司想要的那种螺丝钉。下次,遇到这样的问题的时候,不妨站在对方的角度来看这个问题。而如果你只想当一辈子的前端工程师,深入到浏览器底层,那也不是问题。找自己喜欢的那部分就好。
56 |
57 | 而就个人来说,只有你会了前端,会了后端,你才会开发出一个完整的应用。毕竟优秀的工程师,早晚都是全栈的。如我们之前项目遇到的一个 “URL 编码问题”,它是从前端生成的 URL 开始,要经过浏览器转义(在 Chrome 和 Safari 上有所不同),经过 Vanrish 缓存,再来到 Apache 服务器,还有 Tomcat 服务器,最后还要经历 Spring 框架,才能到达代码层。最后的最后,我们找到了问题是 Firefox 浏览器的预加载,和我们预想的完全不一样。过程中,你需要前端、后端,还需要 DevOps 的相关技能。
58 |
59 | 扩展你的边境,对你的学习能力会有更大的提升。尤其是,当你觉得前端无聊,或者没有新的东西,请毫不犹豫地拥抱变化。
60 |
61 | 当然了,如果你只工作不到一年,请先往深度发展。
62 |
63 | ### 深度:复杂应用的架构
64 |
65 | 如果只是前端应用来考虑,那大抵是只有少数的应用,可以称得上是复杂应用。这一类型的应用:
66 |
67 | - 可能存在复杂的业务逻辑
68 | - 可能是因为业务功能数量大,产生了大量的代码
69 | - 可能是技术实现上困难
70 |
71 | 这能归类到一级别的前端应用,数量并不多。
72 |
73 | ### 广度:更大领域的前端技术
74 |
75 | 从最近几年的趋势来看,还有一个趋势是,客户端融合,导致前端的快速融合,。
76 |
77 | 当我第一次全面使用 JavaScript + Web 浏览器来开发应用时,我发现它真的非常的快速、省事。
78 |
79 | 所以,那时在我的第一本书里《自己动手设计物联网》,我完全使用 JavaScript 来开发应用。
80 |
81 | - 前端
82 | - APP
83 | - 后端
84 |
85 | 限于当时的条件,我并没有使用 Node.js 来开发嵌入式应用。而随着更好的 JavaScript 引擎的出现,在嵌入式的机会也越来越大。
86 |
87 | ## Tech Lead vs Tech Manager vs Tech Expert
88 |
89 | 技术变得越来越卓越,也意味着我们会承担一些带领她/她人成长的任务,也意味着我们要开始做越来越多的非技术工作,也意味着我们面临这样的抉择。从发展的路线上来看,会有三种不同的角色或岗位需要我们考虑:
90 |
91 | **Tech Manager**,岗位。技术管理者是一个真实的岗位,它拥有一些真实的权力,以及对应的 KPI 压力。在一些公司里,它是一个角色的多种职责之一,如它是项目经理(Project Manger),只是集中于技术部分。但是这个岗位,往往不会深入到代码中,只是做技术上的决策——如数据库选哪个,如何去部署等等之类的问题。
92 |
93 | **Tech Lead**,角色。技术领导者是一种角色,
94 |
95 | **Tech Expert**,underfined。
96 |
97 | ## 前端领域
98 |
99 | ### 桌面浏览器
100 |
101 | ### 移动设备浏览器
102 |
103 | ### 嵌入式设备浏览器
104 |
105 | ## 写作的模式
106 |
107 | 1. 引子 ->
108 |
109 | 模式 - 你并不需要使用 Node.js 来写这样的功能。除非:
110 |
111 | - 你没有其它语言的开发技能
112 | - 使用前端技术、JavaScript 开发更快
113 |
114 | 2. 应用示例
115 |
116 | 3. 架构规划方案 ->
117 |
118 |
119 | 使用前端技术开发时,应该采用怎样的技术方案?
120 |
121 | 4. Demo 应用
122 |
123 |
124 | 5. 模式介绍 ->
125 |
126 | 在编写这样的技术栈时,你可以采用怎样的模式。诸如
127 |
128 | Chrome 的发布-订阅者模式
129 |
130 | # 前端地位
131 |
132 | > 前端困境三步曲之前端地位
133 |
134 | 最近的,最远的最近,或者说在过去的几个月里,我与几个前端同事,一直在讨论一个话题:『作为一个前端开发人员,我们面临怎样的困境?又该如何去解决?』。
135 |
136 | 而在较老的一次历史讨论(可能是在 6 小时以前)里,我便想重新理清一次其中的思路,也就有了这篇文章。
137 |
138 | ## 前端是不是没有地位?
139 |
140 | 答案:不是,也是。
141 |
142 | 当我们在技术领域,技术团队,讨论地位的时候,说的实际上是话语权——技术的话语权,KPI 的话语权。技术话语权,是因人而异,当你可被信赖时,你就有了话语权。而 KPI 话语权,实际上指的是 title。
143 |
144 | 1. **来得晚的前端没有 Title**。Title 是一个很有意思的东西:**先到先得**,你去了一家高速发展的创业公司,你的 title 就升得很快——站在风口,大象都能飞。而,大部分 Web 应用,前期注重的往往都是应用的功能,这也导致了:这些组织在前期并不需要优秀的前端开发。而发展起来之后,便开始追求用户体验、视觉效果、多平台,到了这个时候呢,关键的坑位已经被后端占据了。毕竟好的前端很贵,但是能实现页面的前端到处都是——甚至是后端也是。
145 |
146 | 2. **后端懂点前端,而前端不懂 CRUD**。事实上,大部分的组织对于团队负责人,都有一个默认的要求:『精通』整个系统——无论是前后端。这就意味着,前端需要懂后端,后端也需要懂前端。所以,一个不懂后端的前端,站不到 title 上;一个不懂前端的后端,站不到 title 上。可是呢,对于普通的开发人员来说,要达到中等前端水平的时间花费,要比后端少得多。而如果放到大前端的领域来考虑,这个问题就需要额外商榷了。
147 |
148 | PS:懂后端也并不要求,你精通后端。因为最好的篮球教练,并不要求会打篮球。而打篮球最好的不一定会当技术负责人/Coach,比如——科比被女儿怼:“你不会打篮球,教练是这么教我的” 。当然了,有技术底子是最好的,但是它也可能在一定程度上限制你。
149 |
150 | 3. **需求导向**(可选)。对于服务型公司,如我司,需求方决定了架构的复杂性,决定了其所需要的 title。而需求方对于架构、复杂度的考量,往往是来自于整个市场的平均知识水平。也就是说,一旦业务方需求不复杂,也就不需要高级的前端开发,便谈不上就不话语权。
151 |
152 | 综上所述,若是想争取地位需要:去得早,懂后端,机会好。
153 |
154 | 扯太远了,那么继续往下扯。
155 |
156 | ## 5 个因素决定前端
157 |
158 | ### 1. 复杂度,决定前端
159 |
160 | 同样是做一个手机,诺基亚的功能机,和 iPhone 有不一样的成本。
161 |
162 | 项目的业务人员/产品经理/产品负责人对于产品的需求,出因此决定了应用/产品的复杂度。诸如于,同样是一个搜索功能,它有不同的实现方式:
163 |
164 | 1. 普通模式。前端生成搜索的 URL,跳转到对应的搜索结果页。
165 | 2. 标签搜索 + 普通搜索。后端返回标签
166 | 3. AutoComplete + 普通搜索
167 | 4. AutoComplete + 标签搜索 + 普通搜索
168 | 5. AutoComplete + 地图搜索 + 标签搜索 + 普通搜索。对了,还要有地图和标签的联动。
169 | 6. AutoComplete + 地图搜索 + 标签搜索 + 普通搜索 + 热门搜索。
170 | 7. ……
171 |
172 | 复杂度,决定了对于优秀前端工程师的需求。也因此在某种程度上,决定了前端的话语权。比如说,『出于设计上的需要,决定了后端应该这么做 xxxx』
173 |
174 | 也因此,诸如于腾讯这样的产品型公司,前后端都没有地垃。
175 |
176 | 但是,它避免了后端决定了前端需求的要素——这一点非常重要。在产品话语权不高的团队,必然是先到先得的后端管理者,决定了整个产品的走向,也由后端决定了前端的设计。
177 |
178 | ### 2. 团队规模,决定前端
179 |
180 | 只有组织内的前端团队达到一定的规模,才能迫使组织的管理者意识到:『我们需要更优秀的前端开发,才能解决当前的瓶颈』。
181 |
182 | 按 xx 划分:
183 |
184 | - HTML 5 广告页
185 | - 小型前端应用(微信小程序)
186 | - 中型前端应用(普通的 Web 应用)
187 | - 大型前端应用(toB)
188 |
189 | 按团队规模来划分:
190 |
191 | - 页面级
192 | - 6 人团队
193 | - 两个 Pizza 团队级
194 | - 组织级
195 |
196 | 所以,如果你只是在切图,如果你只是在画 HTML5
197 |
198 | ### 3. 流水线式开发
199 |
200 | 大型组织,需要更明确的分工,以便于机械工的生产更多的应用。
201 |
202 | 也因此需要更明确的分工,来解决效率的问题。
203 |
204 | - 工具支撑团队
205 | - 框架开发团队
206 | - 业务开发团队
207 | - DevOps 团队
208 |
209 | ### 4. 客户端多样式
210 |
211 | 在最近的几年里,前端走向大前端的原因也在于此,对于多种客户端开发的需求:微信小程度、桌面客户端、跨平台应用等等。使得一个个前端开发人员,身为多技。
212 |
213 | 作者手疼,省去了几十个字。
214 |
215 | ### 5. 新的领域
216 |
217 | 嗯,只有新的领域,才存在更多的机会。
218 |
219 | 1. 边缘计算
220 | 2. 区块链
221 | 3. 客户端计算
222 | 4. ……
223 |
224 | 作者手疼,省去了几十个字。
225 |
226 | ### 6. 业务熟悉度
227 |
228 | 如果你不关心业务,对业务不了解,那么你哪来的自信,去领导整个前后端团队。
229 |
230 | 作者手疼,省去了几百个字。
231 |
232 | ## 结论
233 |
234 | 言而总之,总而言之:只有优秀的前端,才有必要讨论地位。抱怨,解决不了问题——只有起而行动,才能有效地解决问题。
235 |
236 | 这些也意味着,我们需要更深入的学习。笑~ :)
237 |
238 | 可是,到底从哪里开始学呢?有没有一本书,能解决这样的问题。
239 |
240 |
241 | # 大前端
242 |
243 | > 大前端是指通过 Web 开发相关的技术(WebView、JavaScript/TypeScript)所能开发、处理的领域。除了包含传统的 Web 前端相比,大前端还包含了——后端(Node.js 如 BFF 层、Serverless)、HTML 5 游戏、物联网、嵌入式应用、移动应用、桌面应用。它的显著特性是:**一次开发,多种平台**。
244 |
245 |
246 | ## 大前端:写给大家看的渐进式前端发展指南
247 |
248 | ## 为什么是大前端
249 |
250 | 为什么不是大后端呢?
251 |
252 | 前端,即面向用户交互,存在一系列的方式。
253 |
254 | 前端在变窄,
255 |
256 | ### JavaScript 的神奇魔力
257 |
258 | 易学、广泛使用、熟悉的语法、
259 |
260 | ### 无处不在的 WebView
261 |
262 | ## 什么是大前端??
263 |
264 | > 大前端是指通过 Web 开发相关的技术(WebView、JavaScript/TypeScript)所能开发、处理的领域。除了包含传统的 Web 前端相比,大前端还包含了——后端(Node.js 如 BFF 层、Serverless)、HTML 5 游戏、物联网、嵌入式应用、移动应用、桌面应用。它的显著特性是:**一次开发,多种平台**。
265 |
266 | 值得注意的是:对于某些领域而言,大前端技术并不是最好的技术,但是它是实现起来最快的技术,也因此特别适合于 **MVP 原型构建**。与此同时,前端技术的动态特性,特别适合于远程更新业务——只需要更新对应的代码,而不需要更新整个应用。
267 |
268 | 若是就这样领域,在前端里,我们可以分为多个领域。
269 |
270 | ## 大前端的技术领域
271 |
272 | ### 前端中的 Web 前端
273 |
274 | ### 前端中的 GUI
275 |
276 | 前端是软件 GUI(图形用户界面)的一种形式。从传统的应用厂商,到互联网应用的迁移,我们便可以看到相关的变化。
277 |
278 | ### 前端中的游戏
279 |
280 | ### 前端中的物联网(IoT)
281 |
282 | ### 前端中的移动端
283 |
284 | ### 前端中的 AR/VR
285 |
286 | ## 如何成为大前端?
287 |
288 |
289 | # 大前端:后端
290 |
291 | 所谓的前端 in 后端,便是**在后端开发中,使用前端相关的语言和技术栈**。最典型的场景,便是使用 Node.js 开发后端服务。虽然 Node.js 已经有了 10 年的历史了,但是以我(Phodal)的角度来看,我更希望的是使用编译型语言,来开发后端服务。动态语言,无法使用编译器来检测错误,难以约束代码变动。
292 |
293 | ## 你并不需要 JavaScript 开发后端服务
294 |
295 | 我是一个程序员,然后才是一个前端工程师。使用 JavaScript 来开发后端服务,对于我来说吸引力并不是那么大。
296 |
297 | 作为一个程序员,我会使用 Java 语言编写后端代码,又或者是开发 Android 应用;因为我博客的缘故,我也会使用 Python 世界里的 Django 来开发后端应用,对于 Flask 也有一定的使用经验。使用 Java 这一点,对于大部分科班出身的程序员来说,它是不可缺少的一项技能。虽然,我并非科班出身,但是在实习的时候,我也有了大量的这门语言的使用经验。
298 |
299 | 只是呢,并不是所有的时候,我们都有足够的时间来编译运行。所以,我们有了 JavaScript,它的动态语言特性,非常适合于快速开发应用。于是乎,我们便有了
300 |
301 | ## 服务端渲染
302 |
303 |
304 | ### 模式:模板渲染
305 |
306 |
307 |
308 | 在早期,前端开发人员使用的是数据、代码与模板分离的设计,诸如在 Java 里使用 Mustache 来进行服务端渲染。
309 |
310 | 而随着前端技术的发展,代码和模板往往糅合在一起。
311 |
312 | ### 模式:异构渲染
313 |
314 | ## Node.js 打造后端服务
315 |
316 | 从社区的探索来看,存在一些完全使用 Node.js 开发的后台服务。但是,也存在一系列由于代码不规范造成的问题。从社区的经验来看,Node.js + Express + MongoDB + Angular/Vue/React,便是一些不错的选择。当然了,也有相当多的应用,只是采用了 Node.js 来完成 BFF 层(Backend For Frontends)。在这一层业务上,它只做业务数据的中间处理。
317 |
318 | 虽然,我经常建议在一些关键的节点上,不要采用 Node.js 来打造后台服务。可一旦涉及到 SPA 的服务端渲染,我们就不得不使用 Express、Koa 等这样的服务端 JavaScript/TypeScript 框架,来解决这样的问题。
319 |
320 | ### 模式
321 |
322 | - Express
323 | - Koa
324 | - Egg.js
325 | - NestJS
326 | - Hapi.js
327 | - Sails.js
328 |
329 | ### 是否大规模采用?
330 |
331 | 国内最大规模使用 Node.js 开发后端服务的团队是阿里巴巴:
332 |
333 | - Node 的 Collaborator 有 5 个国人, 其中 4 个在阿里巴巴这边,并且有国内唯一一个 CTC 成员。
334 |
335 | 所以,对于稍有余力的小型公司来说,除非逼不得请不要大规模采用。
336 |
337 | ## FaaS & Serverless
338 |
339 | > Serverless 架构是指大量依赖第三方服务(也叫做后端即服务,即“BaaS”)或暂存容器中运行的自定义代码(函数即服务,即“FaaS”)的应用程序,函数是无服务器架构中抽象语言运行时的最小单位。在这种架构中,我们并不看重运行一个函数需要多少 CPU 或 RAM 或任何其他资源,而是更看重运行函数所需的时间,我们也只为这些函数的运行时间付费。[^serverless]
340 |
341 | [^serverless]: [https://serverless.ink/](https://serverless.ink/)
342 |
343 | 作为一种折中方案,也是我最喜欢的方案。Serverless 架构是指大量依赖第三方服务(也叫做后端即服务,即“BaaS”)或暂存容器中运行的自定义代码(函数即服务,即“FaaS”)的应用程序,函数是无服务器架构中抽象语言运行时的最小单位。
344 |
345 | 对于没有后台经验的前端开发人员来说,使用 Node.js 开发后端应用是一种相当大的挑战。大多数非科班的前端程序员,不知道从数据库到 RESTful API 的一系列操作,并且还需要了解到部署等一系列的系统底层知识。因此,使用 Serverless 这种不关心基础设施的技术,可以进一步地降低开发成本。
346 |
347 |
348 | 采用 Serverless 架构,也就意味着,我们提取出了大量的基础设施。而使用 Node.js + JavaScript 作为胶水,来快速连接不同的服务,以形成一个快速有效的方案。并且,编写更少的代码,也意味着更安全、快速。
349 |
350 | 使用 AWS 来运行大量的 Serverless 计算的成本很高,但是自己搭建一个 Serverless 服务器,来运行自己的 Serverless 应用,则变成了一种更廉价的方式。除了直接基于 AWS 的 Serverless Framework 框架的方案,还有 OpenFaaS、Kubeless、OpenWhisk、Fission 等不同的 Serverless 框架。
351 |
352 | ## API Gateway & BFF
353 |
354 | ### API Gateway
355 |
356 | ### BFF
357 |
358 | > BFF,即 Backends For Frontends (服务于前端的后端),也就是服务器设计 API 时会考虑客户端的使用情况,在服务端根据不同的设备类型,返回不同客户端所需要的结果。BFF 模式,这种模式不会为所有的客户端创建通用的 API。而是创建多个 BFF 服务:一个用于 Web 前端、一个用于移动客户端(甚至一个用于 iOS,另一个用于 Android)等等。[^aofe]
359 |
360 | [^aofe]: 前端架构:从入门到微前端
361 |
362 | # 大前端:IoT
363 |
364 | Web 应用的架构相比于一个物联网系统,无非就是多了一层硬件层以及可选的协调层。
365 |
366 | 
367 |
368 |
369 | 这个硬件层决定了物联网应用比Web应用更加复杂。对于大部分的Web应用来说 ,客户端都是手机、电脑、平板这些设备,都有着强大的处理能力,不需要考虑一些额外的因素。
370 |
371 | 对于物联网应用来说,我们需要考虑设备上的MCU的处理能力,根据其处理能力和使用环境使用不同的通信协议,如我们在一些设备上需要使用CoAP协议。在一些设备上不具备网络功能,需要考虑借助于可以联网的协助层,并且还需要使用一些短距离的无线传输协议,如低功耗蓝牙、红外、Zigbee等等。
372 |
373 | ## 传统的物联网
374 |
375 | 我毕业的时候,选定的毕业论文是一篇关于物联网的论文——《基于 REST 服务的最小物联网系统设计》。它是一篇入门级的论文,但凡是有 CS 基础的人,再加上一些硬件知识,都能写出这样的论文。不过我们的是毕业设计,论文反而有些其次,毕竟能不能过是以实物为主的。因此毕业设计动不动就要烧个几百 RMB,还不算服务器费用。然而,对于我们这些学习硬件为主的专业来说,就不是如此。
376 |
377 | 这篇论文是之前参加比赛的作品论文的“最小化”,里面使用到的主要就是创建RESTful服务,而它甚至称不上是一种技术。在这个作品里:
378 |
379 | * 我们使用Python语言里的Django框架作为Web服务框架,使用Django REST Framework来创建RESTful服务。
380 | * 为了使用手机当控制器,我们还要用Java写一个Android应用。
381 | * 我们使用Raspberry Pi作为硬件端的协调层,用于连接网络,并传输控制信号给硬件。在最初的设计里,因为便宜,我们打算使用运行OpenWRT的路由器来当硬件端的控制器。
382 | * 我们在硬件端使用Arduino作为控制器,写起代码特别简单,可以让我们关注于业务。
383 | * 我们还使用了Zigbee模块Xbee及I2C作为连接不同Arduino模块的的介质。
384 | * 最后,我们还需要在网页上做一个图表来显示实时数据。
385 |
386 | 所有的这些,我们需要使用Python、Java、JavaScript、C、Arduino五种语言。而如果我们要写相应的iOS应用,我们还需要Objective-C。
387 |
388 | 而同样的:
389 |
390 | 我刚实习的时候(2013 年),项目里使用的是 Backbone,作为单页面应用框架的核心来打造 Web 应用。这时,我开始关注于 Node.js,使用它实现物联网应用的可能性。当时,已经有了物联网协议 MQTT 和 CoAP 相关的库,便照猫画虎地写了一个支持 HTTP、CoAP、WebSocket 和 MQTT 的物联网。由于,当时缺乏一些大型应用的开发经典,所以做得并不是很好,但是已经可以看到 JavaScript 在这方面的远景。
391 |
392 | 一年多后,Ionic 也还没推出正式版,便发现到了这个框架真的很棒——它自带了一系列的 UI 组件,还用 NgCordova 封装了一系列 Cordova 的插件。便开始使用 Ionic 写了一些移动应用,发现还挺顺手的。接着,便拿这个框架尝试写物联网应用,这需要一些原生的插件,如 BLE、MQTT。后来,我也写了一个简单的 CoAP 插件。
393 |
394 | 后来,我们不再需要编译 Node.js(早期,使用的是 Raspberry Pi 来运行 Node.js),就可以在 ARM 处理器上运行 Node.js。并且我们已经有 Tessel、Espruino、Kinoma Create、Ruff 这些可以直接运行 JavaScript 的开发板。三星还推出 IoT.js,可以让更多的嵌入式设备,直接使用 JavaScript 作为开发语言。
395 |
396 | 人们开始在硬件上使用 JavaScript 的原因有很多,如 Web 的开发人员是最多的,而 JavaScript 很容易上手。
397 |
398 | ## 嵌入式设备分类
399 |
400 | ### 两种前端类型
401 |
402 | #### 嵌入式设备作为服务端
403 |
404 | 诸如 ESP32 或者 ESP8266,它们可以和传统的 Web 应用一样,托管前端应用。
405 |
406 | #### 嵌入式设备作为服务端和客户端
407 |
408 | 如 Android 系统中的
409 |
410 | Android Things 相似的还有 Microsoft 10 IoT、Raspberry Pi
411 |
412 | ### 嵌入式设备分类
413 |
414 | - 低资源受限设备,只能使用传统的编译型语言编写,如 C、C++、汇编,
415 | -
416 |
417 | #### 低资源受限设备:编译型语言
418 |
419 | 它们只能使用
420 |
421 | #### 普通嵌入式设备:
422 |
423 | Android 是用得最多的嵌入式系统,。
424 |
425 | OpenWRT 是用得最广泛的嵌入式系统之一,
426 |
427 | 在某些资源受限的设备上,我们无法运行一个带着完整 JavaScript 引擎的 WebView——出于软件体积、运行内存等因素的限制,去除了这些特性。诸如 CSS 3 中的动画特性,会占用大量的运算资源,算去掉了相应的支持。
428 |
429 | ## 固件编写
430 |
431 | ### Johnny-Five
432 |
433 | ### IoT.js
434 |
435 | ## Be Professinoal
436 |
437 | ### Arduino 与 Processing
438 |
439 | ### ESP866/ESP32 与 Lua
440 |
441 | ### C 语言
442 |
443 | # 大前端:移动端
444 |
445 | ## 混合应用
446 |
447 | 依我的角度来看,使用什么跨平台框架来看,区别并不是太大。目前主流的方案,仍然是原生(含跨平台框架) + HTML5 应用。从业务的角度上来看待这个问题,那么还是希望,可以用 HTML 5 的地方多——更新功能方便。
448 |
449 | 也因此,虽然在过去,笔者写过基于 React Native 的混合应用框架 Dore。我相信:Flutter 也会出现这样的混合应用框架。不过,对于有原生开发能力的团队来说,它们的框架还会是三部分:
450 |
451 | * 原生功能部分
452 | * 原生 + H5 的频繁更新部分
453 | * Fultter 的跨平台部分
454 |
455 | 写业务嘛,框架都只是工具。
456 |
457 | ## 跨平台应用
458 |
459 | ### React Native
460 |
461 | ### Flutter
462 |
463 | ### Weex
464 |
465 | ## 小程序
466 |
467 | 小程序,即 HTML5 小程序,即无需安装即可下载运行的应用程序。与普通的移动 Web 应用不同的是,**小程序相当于是高阶版的混合应用**。
468 |
469 | 如果只是从这一点上来看,其实是不是和微信一样的定制型小程序,并不是那么重要。重要的,在于与原生**界面结合,并提供离线使用功能。**它也是小程序与普通的 HTML 应用的区别。
470 |
471 | # 大前端:桌面应用
472 |
473 | ## 跨平台 WebView 应用框架
474 |
475 | Electron/NW.js
476 |
477 | ## 跨平台框架
478 |
479 | QT + WebView
480 |
481 | # 大前端:VR/AR
482 |
483 | ## VR
484 |
485 | ## AR
486 |
487 | # 大前端:数据可视化
488 |
489 | 在过去我阅读的一些书籍里面,主要是以 Processing 作为可视化的语言——它起始于 2001 年,它最初是面向美术工作者和设计者创建的,后来变成了全面的设计和原型工具,可以用于创建复杂数据可视化领域。
490 |
491 | ### 高级篇
492 |
493 |
494 | ### D3.js
495 |
496 | # 大前端:游戏
497 |
498 | 随着移动端的性能不断变好,在 2019 年,我开始看好使用 HTML 5 技术来开发一些游戏。当然了,主要原因还是微信小游戏的出现。但是,不管怎样,我开始尝试在这个领域的探索。
499 |
500 | ## WebView 游戏
501 |
502 | ## JavaScript 语言游戏
503 |
504 |
505 | # 大前端:前端
506 |
507 | 前端领域,在 2018 年已经趋于平衡,Angular、Vue、React 都没有出现太大的变化。
508 |
509 | ## Web 应用
510 |
511 | ### SPA (单页面应用)框架:A/V/R
512 |
513 | 架构选型上,也趋势于平衡。该用啥的还是用啥,偶尔还是会出现一些框架切换的新闻。尽管在 2019 年,会出现一些新的框架,但是还不太可能快引起变化。
514 |
515 | ### MPA (多页面应用)框架
516 |
517 | ## 语言
518 |
519 | ### TypeScript
520 |
521 | TypeScript 真香。
522 |
523 | 前端,没什么好看——除了,娱乐新闻。
524 |
525 | # 大前端:UI & UX
526 |
527 | 自诩是一个 “斜杠青年” 的我,为自己添加不同的 “title”,设计师/设计学徒便是其中的一个。
528 |
529 | 一来,作为一个前端开发人员,日常就是要和用户打交道。几年经验下来,多多少少也能积累点经验;二来,除了职业本身,我也是一个喜爱画画的人。只是受限于工作的影响,在这方面的投入便没有那么多。
530 |
531 | 也因此,这样一来,我们便也是能得到一些慰藉。
532 |
533 | ## 绘画
534 |
535 | ## 用户体验设计
536 |
537 | ## 图形界面设计
538 |
539 |
540 | # 大前端:命令行接口
541 |
542 | ## 传统 CLI vs 跨平台 CLI
543 |
544 | ### 优势?
545 |
546 | ###
547 |
548 | ## 相关实践和原则
549 |
550 |
551 | Best Practise?
552 |
553 | Mopass、ADR、Stepping,多多少少也算是写过一些 CLI。
554 |
555 | 除了此,使用 Node.js
556 |
557 | Clone 代码,执行复制
558 |
559 |
560 | 一般型 Shell,如 ``git clone``
561 |
562 | 而跨平台,则是 ``fs.copy`` 而非 GNU/Linux 上的 ``cp``,又或者是 Windows 上的 ``xcopy``
563 |
564 | 相似的工具和语言有 Python 等
565 |
566 | ### 为什么是 Node.js
567 |
568 | 因为我们是个前端,相关的环境肯定是有的。只要是个前端开发,一定会有 Node.js 环境。
569 |
570 |
571 | # 前端,永不止境
572 |
573 | ## 大前端架构
574 |
575 |
576 | ## 练习
577 |
578 |
--------------------------------------------------------------------------------
/epub.css:
--------------------------------------------------------------------------------
1 | html, body, div, span, h1, h2, h3, h4, h5, h6, p, a, em, strong, b, u, i, pre, code, del, strike, abbr, acronym, address, q, cite, blockquote, big, small, sub, sup, tt, var, center, img, dfn, ins, kbd, s, samp, dl, dt, dd, ol, ul, li, fieldset, legend, label, table, caption, tbody, tfoot, thead, tr, th, td {
2 | margin: 0;
3 | padding: 0;
4 | border: 0;
5 | outline: 0;
6 | font-size: 100%;
7 | vertical-align: baseline;
8 | background: transparent;
9 | }
10 | @page {
11 | margin: 5px;
12 | }
13 | p {
14 | margin-bottom: 9px;
15 | line-height: 1.4;
16 | }
17 | a {
18 | color: #0069d6;
19 | }
20 | a:hover {
21 | color: #0050a3;
22 | text-decoration: none;
23 | }
24 | a img {
25 | border: none;
26 | }
27 | h1, h2, h3, h4, h5, h6 {
28 | color: #404040;
29 | line-height: 1.5;
30 | margin: 1em 0 0.5em;
31 | -webkit-hyphens: none;
32 | hyphens: none;
33 | adobe-hyphenate: none;
34 | }
35 | h1 {
36 | font-size: 220%;
37 | margin-bottom: 1.5em;
38 | }
39 | h2 {
40 | font-size: 190%;
41 | }
42 | h3 {
43 | font-size: 160%;
44 | }
45 | h4 {
46 | font-size: 140%;
47 | }
48 | h5 {
49 | font-size: 130%;
50 | }
51 | h6 {
52 | font-size: 120%;
53 | }
54 | hr {
55 | margin: 0 0 19px;
56 | border: 0;
57 | border-bottom: 1px solid #ccc;
58 | }
59 | blockquote {
60 | padding: 13px 13px 21px 15px;
61 | margin-bottom: 18px;
62 | font-family: georgia, serif;
63 | font-style: italic;
64 | }
65 | blockquote:before {
66 | content: "\201C";
67 | font-size: 300%;
68 | margin-left: -10px;
69 | font-family: serif;
70 | color: #eee;
71 | }
72 | blockquote p {
73 | font-size: 120%;
74 | margin-bottom: 0;
75 | font-style: italic;
76 | }
77 | code, pre {
78 | font-family: monospace;
79 | }
80 | code {
81 | background-color: #fee9cc;
82 | color: rgba(0, 0, 0, 0.75);
83 | padding: 1px 3px;
84 | -webkit-border-radius: 5px;
85 | border-radius: 5px;
86 | font-size: 85%;
87 | }
88 | pre {
89 | display: block;
90 | padding: 14px;
91 | margin: 0 0 18px;
92 | font-size: 85%;
93 | line-height: 1.3;
94 | border: 1px solid #d9d9d9;
95 | white-space: pre-wrap;
96 | word-wrap: break-word;
97 | -webkit-hyphens: none;
98 | hyphens: none;
99 | adobe-hyphenate: none;
100 | }
101 | pre code {
102 | background-color: #fff;
103 | color: #737373;
104 | padding: 0;
105 | }
106 | code.sourceCode span.kw {
107 | color: #007020;
108 | font-weight: bold;
109 | }
110 | code.sourceCode span.dt {
111 | color: #902000;
112 | }
113 | code.sourceCode span.dv {
114 | color: #40a070;
115 | }
116 | code.sourceCode span.bn {
117 | color: #40a070;
118 | }
119 | code.sourceCode span.fl {
120 | color: #40a070;
121 | }
122 | code.sourceCode span.ch {
123 | color: #4070a0;
124 | }
125 | code.sourceCode span.st {
126 | color: #4070a0;
127 | }
128 | code.sourceCode span.co {
129 | color: #60a0b0;
130 | font-style: italic;
131 | }
132 | code.sourceCode span.ot {
133 | color: #007020;
134 | }
135 | code.sourceCode span.al {
136 | color: red;
137 | font-weight: bold;
138 | }
139 | code.sourceCode span.fu {
140 | color: #06287e;
141 | }
142 | code.sourceCode span.re {
143 | }
144 | code.sourceCode span.er {
145 | color: red;
146 | font-weight: bold;
147 | }
148 | body {
149 | font-family: serif;
150 | }
--------------------------------------------------------------------------------
/images/iot-layers.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phodal/frontend/f553c283b93197c2458d9da9bde01afb144c575f/images/iot-layers.png
--------------------------------------------------------------------------------
/img/cover.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phodal/frontend/f553c283b93197c2458d9da9bde01afb144c575f/img/cover.jpg
--------------------------------------------------------------------------------
/img/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phodal/frontend/f553c283b93197c2458d9da9bde01afb144c575f/img/favicon.ico
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
大前端:前端的扩张 – ebook
8 |
14 |
15 |
18 |
19 |
20 |
21 |
大前端:前端的扩张
22 |
23 |
By Phodal (Follow Me: 微博 、知乎 、SegmentFault )
25 |
26 |
27 |
Phodal(黄峰达)是一个咨询师、geek、作者和设计师,现作为一个资深的咨询师为 ThoughtWorks 工作。作为一个 geek,他是一名狂热的开源社区贡献者,在 GitHub 上拥有大量的开源项目,以及接近 40k 的
28 | stars。作为一个首席 markdown
29 | 印刷工,他是《自己动手设计物联网》繁、简体版、《全栈应用开发:精益实践》简体版的作者,并合译有两本物联网相关书籍;并作为技术专家,审阅了七本英文技术书籍。作为一个设计师,他自豪地用自己半生半熟地笔触,为自己的文章作插画。
30 |
31 |
32 |
33 |
36 |
37 |
40 |
41 |
44 |
45 |
46 |
47 |
我的著作:
48 |
52 |
我的其他电子书:
53 |
64 |
65 |
微信公众号
66 |
67 |
68 | 当前为预览版,在使用的过程中遇到任何遇到请及时与我联系。阅读过程中问题,不烦在GitHub上提出来:
69 | Issues
70 |
71 |
72 | 阅读过程中遇到语法错误、拼写错误、技术错误等等,不烦来个Pull Request,这样可以帮助到其他阅读这本电子书的童鞋。
73 |
74 |
75 |
76 |
211 |
212 |
前言
213 |
下一步,我们要去哪里?
214 |
成为程序员并不是一件容易的事,又并不代表你与你隔壁的小王相比,就能多赚很多钱。这个行业有各种有趣的人,有的是科班出身,有的是自学成长 ,有的是培训养成。来自哪里并不重要,重要的是你要去哪里。
215 |
也许是金三银四的到来,也许是越来越多的人被优化,也许是春天的到来。它们结合起来,让愈来愈的人开始焦虑起来。尤其是对于那些工作 2~3 年的人来说,这种焦虑愈加的明显。工作 1~2 年的,技术上的问题还有一系列待解决。像我这样工作 4~5 年的人,又有别的焦虑。如最近,我一直忙于构建自己想要的 Tech Lead 的模型、技能——如果有一个东西绕不开,那便是时候学习相关的内容。与此同时,我还开始在编写一个大前端相关的电子书——《大前端的扩张》。
216 |
前端工程师是什么?
217 |
前端工程师是工程师,后端工程师是工程师,他/她们并没有不同。
218 |
前端工程师是精细分工的一个产物。工程师被分为了前端工程师、后端工程师,对应的团队的 Tech Lead 也有了两个,相应的 KPI 考察也变成了两个团队。尽管出于方便的原因,某些公司向上汇报的渠道往往只有一个:后端工程师。这对于想往管理发展的前端程序员来说,这并不是一件好事。但是对于想往技术发展的前端工程师来说,这一点相当的不错——少去了一系列的会议,还不需要一些“没有意义”的加班。
219 |
前端工程师和后端工程师并没有区别,他/她们首先都是人,然后才是工程师,然后才是前/后端工程师。至于这些人,什么时候被机器替换,倒是有一个好问题。后端有 Serverless 这样的大山压在前面,前端有 AI 生成代码的挑战在面前。他/她们有各自的焦虑,后端有自己的无聊,前端有自己的热闹。
220 |
前端没有地位?
221 |
有没有地位,这是一个好问题。可是呢,“你真的在乎这个地位吗?”当我在吐槽“前端没有地位”的时候,我可能有多个意思:
222 |
223 | 公司给前端的机会太少了。
224 | 我做的事情带来了更多的价值,应该有更多的收入。
225 | 前端开发的话语权太小了。
226 |
227 |
那么,你呢?是否也是如此。我们只是在一些利益上做争夺而已——可是呢,当我们投入同样的时间,却没有同样的回报时。我们也应该去争夺一些相关的权益。
228 |
可若是呢,要是你的项目里,不懂前端的后端为前端下决策,那就说明:在你的公司里,前端是真没地位,又或者是没有人愿意去争取。原因呢,无非就是一个萝卜一个坑,而那个坑已经被后端占据了。
229 |
前端的历史太短,刚刚浴火重新的时候,后端已经把持朝政好几年了。
230 |
前端不重要?
231 |
前端有没有那么重要,是人们心中的想法——大多数程序员(包含我)很难去说服别人,我只会以实际行动去证明。可是,前端真的不重要么?这是一个相当大的误区,既然你需要前端,那说明在这个项目前端是必不可少的。既然必不可少,那它就是重要的。
232 |
只是大家口中的前端不重要,并不是说前端不重要。而是前端的某些部分对于他/她而言,不那么重要了:
233 |
234 | “我不需要复杂的交互,只需要可以工作就好”。
235 | “我不需要酷炫的动画,只需要可以串起来就好”。
236 | “我不知道校验输入结果,反正后端可以校验就好”。
237 |
238 |
然后,等你按这个标准实现的时候,会发现他/她们对这个效果非常不满意。总有人会如此,口口声声觉得前端不重要,最后还是需要一个重要的前端角色。不重要,只是压缩支出和时间的一个手段;不重要,只是当前不重要。
239 |
我们觉得重要,那就是重要。
240 |
前端工程师是工程师,后端工程师是工程师,他/她们并没有不同。
241 |
一个不学习前端的后端,不能成为一个优秀的工程师。一个不学习后端的前端,也无法成为优秀的工程师。“学习” 包含了两层含义,一个是 GET 学习这个技能,一个是掌握新的技能。
242 |
一个不了解后端的前端,和那个你鄙视的后端没有太大的区别。编程语言是一个前端,前后端是一个谋生的端点。你是一个程序员,你需要持续的学习。
243 |
广度与深度
244 |
前端是一个门槛越来越低的岗位,那些新人啊,可以快速来到这个行业,COPY/Paste 出类似的代码
245 |
广度与深度,并不值得讨论。一个公司中的架构师,绝不只是前端工程师,也绝不只是一个后端工程师。同样的问题,如果有一个人让你只学前端,那个人可能是你领导,或者是大公司想要的那种螺丝钉。下次,遇到这样的问题的时候,不妨站在对方的角度来看这个问题。而如果你只想当一辈子的前端工程师,深入到浏览器底层,那也不是问题。找自己喜欢的那部分就好。
246 |
而就个人来说,只有你会了前端,会了后端,你才会开发出一个完整的应用。毕竟优秀的工程师,早晚都是全栈的。如我们之前项目遇到的一个 “URL 编码问题”,它是从前端生成的 URL 开始,要经过浏览器转义(在 Chrome 和 Safari 上有所不同),经过 Vanrish 缓存,再来到 Apache 服务器,还有 Tomcat 服务器,最后还要经历 Spring 框架,才能到达代码层。最后的最后,我们找到了问题是 Firefox 浏览器的预加载,和我们预想的完全不一样。过程中,你需要前端、后端,还需要 DevOps 的相关技能。
247 |
扩展你的边境,对你的学习能力会有更大的提升。尤其是,当你觉得前端无聊,或者没有新的东西,请毫不犹豫地拥抱变化。
248 |
当然了,如果你只工作不到一年,请先往深度发展。
249 |
深度:复杂应用的架构
250 |
如果只是前端应用来考虑,那大抵是只有少数的应用,可以称得上是复杂应用。这一类型的应用:
251 |
252 | 可能存在复杂的业务逻辑
253 | 可能是因为业务功能数量大,产生了大量的代码
254 | 可能是技术实现上困难
255 |
256 |
这能归类到一级别的前端应用,数量并不多。
257 |
广度:更大领域的前端技术
258 |
从最近几年的趋势来看,还有一个趋势是,客户端融合,导致前端的快速融合,。
259 |
当我第一次全面使用 JavaScript + Web 浏览器来开发应用时,我发现它真的非常的快速、省事。
260 |
所以,那时在我的第一本书里《自己动手设计物联网》,我完全使用 JavaScript 来开发应用。
261 |
262 | 前端
263 | APP
264 | 后端
265 |
266 |
限于当时的条件,我并没有使用 Node.js 来开发嵌入式应用。而随着更好的 JavaScript 引擎的出现,在嵌入式的机会也越来越大。
267 |
Tech Lead vs Tech Manager vs Tech Expert
268 |
技术变得越来越卓越,也意味着我们会承担一些带领她/她人成长的任务,也意味着我们要开始做越来越多的非技术工作,也意味着我们面临这样的抉择。从发展的路线上来看,会有三种不同的角色或岗位需要我们考虑:
269 |
Tech Manager ,岗位。技术管理者是一个真实的岗位,它拥有一些真实的权力,以及对应的 KPI 压力。在一些公司里,它是一个角色的多种职责之一,如它是项目经理(Project Manger),只是集中于技术部分。但是这个岗位,往往不会深入到代码中,只是做技术上的决策——如数据库选哪个,如何去部署等等之类的问题。
270 |
Tech Lead ,角色。技术领导者是一种角色,
271 |
Tech Expert ,underfined。
272 |
前端领域
273 |
桌面浏览器
274 |
移动设备浏览器
275 |
嵌入式设备浏览器
276 |
写作的模式
277 |
278 | 引子 ->
279 |
280 |
模式 - 你并不需要使用 Node.js 来写这样的功能。除非:
281 |
282 | 你没有其它语言的开发技能
283 | 使用前端技术、JavaScript 开发更快
284 |
285 |
286 | 应用示例
287 | 架构规划方案 ->
288 |
289 |
使用前端技术开发时,应该采用怎样的技术方案?
290 |
291 | Demo 应用
292 | 模式介绍 ->
293 |
294 |
在编写这样的技术栈时,你可以采用怎样的模式。诸如
295 |
Chrome 的发布-订阅者模式
296 |
前端地位
297 |
298 | 前端困境三步曲之前端地位
299 |
300 |
最近的,最远的最近,或者说在过去的几个月里,我与几个前端同事,一直在讨论一个话题:『作为一个前端开发人员,我们面临怎样的困境?又该如何去解决?』。
301 |
而在较老的一次历史讨论(可能是在 6 小时以前)里,我便想重新理清一次其中的思路,也就有了这篇文章。
302 |
前端是不是没有地位?
303 |
答案:不是,也是。
304 |
当我们在技术领域,技术团队,讨论地位的时候,说的实际上是话语权——技术的话语权,KPI 的话语权。技术话语权,是因人而异,当你可被信赖时,你就有了话语权。而 KPI 话语权,实际上指的是 title。
305 |
306 | 来得晚的前端没有 Title 。Title 是一个很有意思的东西:先到先得 ,你去了一家高速发展的创业公司,你的 title 就升得很快——站在风口,大象都能飞。而,大部分 Web 应用,前期注重的往往都是应用的功能,这也导致了:这些组织在前期并不需要优秀的前端开发。而发展起来之后,便开始追求用户体验、视觉效果、多平台,到了这个时候呢,关键的坑位已经被后端占据了。毕竟好的前端很贵,但是能实现页面的前端到处都是——甚至是后端也是。
307 | 后端懂点前端,而前端不懂 CRUD 。事实上,大部分的组织对于团队负责人,都有一个默认的要求:『精通』整个系统——无论是前后端。这就意味着,前端需要懂后端,后端也需要懂前端。所以,一个不懂后端的前端,站不到 title 上;一个不懂前端的后端,站不到 title 上。可是呢,对于普通的开发人员来说,要达到中等前端水平的时间花费,要比后端少得多。而如果放到大前端的领域来考虑,这个问题就需要额外商榷了。
308 |
309 |
PS:懂后端也并不要求,你精通后端。因为最好的篮球教练,并不要求会打篮球。而打篮球最好的不一定会当技术负责人/Coach,比如——科比被女儿怼:“你不会打篮球,教练是这么教我的” 。当然了,有技术底子是最好的,但是它也可能在一定程度上限制你。
310 |
311 | 需求导向 (可选)。对于服务型公司,如我司,需求方决定了架构的复杂性,决定了其所需要的 title。而需求方对于架构、复杂度的考量,往往是来自于整个市场的平均知识水平。也就是说,一旦业务方需求不复杂,也就不需要高级的前端开发,便谈不上就不话语权。
312 |
313 |
综上所述,若是想争取地位需要:去得早,懂后端,机会好。
314 |
扯太远了,那么继续往下扯。
315 |
5 个因素决定前端
316 |
1. 复杂度,决定前端
317 |
同样是做一个手机,诺基亚的功能机,和 iPhone 有不一样的成本。
318 |
项目的业务人员/产品经理/产品负责人对于产品的需求,出因此决定了应用/产品的复杂度。诸如于,同样是一个搜索功能,它有不同的实现方式:
319 |
320 | 普通模式。前端生成搜索的 URL,跳转到对应的搜索结果页。
321 | 标签搜索 + 普通搜索。后端返回标签
322 | AutoComplete + 普通搜索
323 | AutoComplete + 标签搜索 + 普通搜索
324 | AutoComplete + 地图搜索 + 标签搜索 + 普通搜索。对了,还要有地图和标签的联动。
325 | AutoComplete + 地图搜索 + 标签搜索 + 普通搜索 + 热门搜索。
326 | ……
327 |
328 |
复杂度,决定了对于优秀前端工程师的需求。也因此在某种程度上,决定了前端的话语权。比如说,『出于设计上的需要,决定了后端应该这么做 xxxx』
329 |
也因此,诸如于腾讯这样的产品型公司,前后端都没有地垃。
330 |
但是,它避免了后端决定了前端需求的要素——这一点非常重要。在产品话语权不高的团队,必然是先到先得的后端管理者,决定了整个产品的走向,也由后端决定了前端的设计。
331 |
2. 团队规模,决定前端
332 |
只有组织内的前端团队达到一定的规模,才能迫使组织的管理者意识到:『我们需要更优秀的前端开发,才能解决当前的瓶颈』。
333 |
按 xx 划分:
334 |
335 | HTML 5 广告页
336 | 小型前端应用(微信小程序)
337 | 中型前端应用(普通的 Web 应用)
338 | 大型前端应用(toB)
339 |
340 |
按团队规模来划分:
341 |
342 | 页面级
343 | 6 人团队
344 | 两个 Pizza 团队级
345 | 组织级
346 |
347 |
所以,如果你只是在切图,如果你只是在画 HTML5
348 |
3. 流水线式开发
349 |
大型组织,需要更明确的分工,以便于机械工的生产更多的应用。
350 |
也因此需要更明确的分工,来解决效率的问题。
351 |
352 | 工具支撑团队
353 | 框架开发团队
354 | 业务开发团队
355 | DevOps 团队
356 |
357 |
4. 客户端多样式
358 |
在最近的几年里,前端走向大前端的原因也在于此,对于多种客户端开发的需求:微信小程度、桌面客户端、跨平台应用等等。使得一个个前端开发人员,身为多技。
359 |
作者手疼,省去了几十个字。
360 |
5. 新的领域
361 |
嗯,只有新的领域,才存在更多的机会。
362 |
363 | 边缘计算
364 | 区块链
365 | 客户端计算
366 | ……
367 |
368 |
作者手疼,省去了几十个字。
369 |
6. 业务熟悉度
370 |
如果你不关心业务,对业务不了解,那么你哪来的自信,去领导整个前后端团队。
371 |
作者手疼,省去了几百个字。
372 |
结论
373 |
言而总之,总而言之:只有优秀的前端,才有必要讨论地位。抱怨,解决不了问题——只有起而行动,才能有效地解决问题。
374 |
这些也意味着,我们需要更深入的学习。笑~ :)
375 |
可是,到底从哪里开始学呢?有没有一本书,能解决这样的问题。
376 |
大前端
377 |
378 | 大前端是指通过 Web 开发相关的技术(WebView、JavaScript/TypeScript)所能开发、处理的领域。除了包含传统的 Web 前端相比,大前端还包含了——后端(Node.js 如 BFF 层、Serverless)、HTML 5 游戏、物联网、嵌入式应用、移动应用、桌面应用。它的显著特性是:一次开发,多种平台 。
379 |
380 |
大前端:写给大家看的渐进式前端发展指南
381 |
为什么是大前端
382 |
为什么不是大后端呢?
383 |
前端,即面向用户交互,存在一系列的方式。
384 |
前端在变窄,
385 |
JavaScript 的神奇魔力
386 |
易学、广泛使用、熟悉的语法、
387 |
无处不在的 WebView
388 |
什么是大前端??
389 |
390 | 大前端是指通过 Web 开发相关的技术(WebView、JavaScript/TypeScript)所能开发、处理的领域。除了包含传统的 Web 前端相比,大前端还包含了——后端(Node.js 如 BFF 层、Serverless)、HTML 5 游戏、物联网、嵌入式应用、移动应用、桌面应用。它的显著特性是:一次开发,多种平台 。
391 |
392 |
值得注意的是:对于某些领域而言,大前端技术并不是最好的技术,但是它是实现起来最快的技术,也因此特别适合于 MVP 原型构建 。与此同时,前端技术的动态特性,特别适合于远程更新业务——只需要更新对应的代码,而不需要更新整个应用。
393 |
若是就这样领域,在前端里,我们可以分为多个领域。
394 |
大前端的技术领域
395 |
前端中的 Web 前端
396 |
前端中的 GUI
397 |
前端是软件 GUI(图形用户界面)的一种形式。从传统的应用厂商,到互联网应用的迁移,我们便可以看到相关的变化。
398 |
前端中的游戏
399 |
前端中的物联网(IoT)
400 |
前端中的移动端
401 |
前端中的 AR/VR
402 |
如何成为大前端?
403 |
大前端:后端
404 |
所谓的前端 in 后端,便是在后端开发中,使用前端相关的语言和技术栈 。最典型的场景,便是使用 Node.js 开发后端服务。虽然 Node.js 已经有了 10 年的历史了,但是以我(Phodal)的角度来看,我更希望的是使用编译型语言,来开发后端服务。动态语言,无法使用编译器来检测错误,难以约束代码变动。
405 |
你并不需要 JavaScript 开发后端服务
406 |
我是一个程序员,然后才是一个前端工程师。使用 JavaScript 来开发后端服务,对于我来说吸引力并不是那么大。
407 |
作为一个程序员,我会使用 Java 语言编写后端代码,又或者是开发 Android 应用;因为我博客的缘故,我也会使用 Python 世界里的 Django 来开发后端应用,对于 Flask 也有一定的使用经验。使用 Java 这一点,对于大部分科班出身的程序员来说,它是不可缺少的一项技能。虽然,我并非科班出身,但是在实习的时候,我也有了大量的这门语言的使用经验。
408 |
只是呢,并不是所有的时候,我们都有足够的时间来编译运行。所以,我们有了 JavaScript,它的动态语言特性,非常适合于快速开发应用。于是乎,我们便有了
409 |
服务端渲染
410 |
模式:模板渲染
411 |
在早期,前端开发人员使用的是数据、代码与模板分离的设计,诸如在 Java 里使用 Mustache 来进行服务端渲染。
412 |
而随着前端技术的发展,代码和模板往往糅合在一起。
413 |
模式:异构渲染
414 |
Node.js 打造后端服务
415 |
从社区的探索来看,存在一些完全使用 Node.js 开发的后台服务。但是,也存在一系列由于代码不规范造成的问题。从社区的经验来看,Node.js + Express + MongoDB + Angular/Vue/React,便是一些不错的选择。当然了,也有相当多的应用,只是采用了 Node.js 来完成 BFF 层(Backend For Frontends)。在这一层业务上,它只做业务数据的中间处理。
416 |
虽然,我经常建议在一些关键的节点上,不要采用 Node.js 来打造后台服务。可一旦涉及到 SPA 的服务端渲染,我们就不得不使用 Express、Koa 等这样的服务端 JavaScript/TypeScript 框架,来解决这样的问题。
417 |
模式
418 |
419 | Express
420 | Koa
421 | Egg.js
422 | NestJS
423 | Hapi.js
424 | Sails.js
425 |
426 |
是否大规模采用?
427 |
国内最大规模使用 Node.js 开发后端服务的团队是阿里巴巴:
428 |
429 | Node 的 Collaborator 有 5 个国人, 其中 4 个在阿里巴巴这边,并且有国内唯一一个 CTC 成员。
430 |
431 |
所以,对于稍有余力的小型公司来说,除非逼不得请不要大规模采用。
432 |
FaaS & Serverless
433 |
434 | Serverless 架构是指大量依赖第三方服务(也叫做后端即服务,即“BaaS”)或暂存容器中运行的自定义代码(函数即服务,即“FaaS”)的应用程序,函数是无服务器架构中抽象语言运行时的最小单位。在这种架构中,我们并不看重运行一个函数需要多少 CPU 或 RAM 或任何其他资源,而是更看重运行函数所需的时间,我们也只为这些函数的运行时间付费。
435 |
436 |
作为一种折中方案,也是我最喜欢的方案。Serverless 架构是指大量依赖第三方服务(也叫做后端即服务,即“BaaS”)或暂存容器中运行的自定义代码(函数即服务,即“FaaS”)的应用程序,函数是无服务器架构中抽象语言运行时的最小单位。
437 |
对于没有后台经验的前端开发人员来说,使用 Node.js 开发后端应用是一种相当大的挑战。大多数非科班的前端程序员,不知道从数据库到 RESTful API 的一系列操作,并且还需要了解到部署等一系列的系统底层知识。因此,使用 Serverless 这种不关心基础设施的技术,可以进一步地降低开发成本。
438 |
采用 Serverless 架构,也就意味着,我们提取出了大量的基础设施。而使用 Node.js + JavaScript 作为胶水,来快速连接不同的服务,以形成一个快速有效的方案。并且,编写更少的代码,也意味着更安全、快速。
439 |
使用 AWS 来运行大量的 Serverless 计算的成本很高,但是自己搭建一个 Serverless 服务器,来运行自己的 Serverless 应用,则变成了一种更廉价的方式。除了直接基于 AWS 的 Serverless Framework 框架的方案,还有 OpenFaaS、Kubeless、OpenWhisk、Fission 等不同的 Serverless 框架。
440 |
API Gateway & BFF
441 |
API Gateway
442 |
BFF
443 |
444 | BFF,即 Backends For Frontends (服务于前端的后端),也就是服务器设计 API 时会考虑客户端的使用情况,在服务端根据不同的设备类型,返回不同客户端所需要的结果。BFF 模式,这种模式不会为所有的客户端创建通用的 API。而是创建多个 BFF 服务:一个用于 Web 前端、一个用于移动客户端(甚至一个用于 iOS,另一个用于 Android)等等。
445 |
446 |
大前端:IoT
447 |
Web 应用的架构相比于一个物联网系统,无非就是多了一层硬件层以及可选的协调层。
448 |
449 | IoT 层级
450 |
451 |
这个硬件层决定了物联网应用比Web应用更加复杂。对于大部分的Web应用来说 ,客户端都是手机、电脑、平板这些设备,都有着强大的处理能力,不需要考虑一些额外的因素。
452 |
对于物联网应用来说,我们需要考虑设备上的MCU的处理能力,根据其处理能力和使用环境使用不同的通信协议,如我们在一些设备上需要使用CoAP协议。在一些设备上不具备网络功能,需要考虑借助于可以联网的协助层,并且还需要使用一些短距离的无线传输协议,如低功耗蓝牙、红外、Zigbee等等。
453 |
传统的物联网
454 |
我毕业的时候,选定的毕业论文是一篇关于物联网的论文——《基于 REST 服务的最小物联网系统设计》。它是一篇入门级的论文,但凡是有 CS 基础的人,再加上一些硬件知识,都能写出这样的论文。不过我们的是毕业设计,论文反而有些其次,毕竟能不能过是以实物为主的。因此毕业设计动不动就要烧个几百 RMB,还不算服务器费用。然而,对于我们这些学习硬件为主的专业来说,就不是如此。
455 |
这篇论文是之前参加比赛的作品论文的“最小化”,里面使用到的主要就是创建RESTful服务,而它甚至称不上是一种技术。在这个作品里:
456 |
457 | 我们使用Python语言里的Django框架作为Web服务框架,使用Django REST Framework来创建RESTful服务。
458 | 为了使用手机当控制器,我们还要用Java写一个Android应用。
459 | 我们使用Raspberry Pi作为硬件端的协调层,用于连接网络,并传输控制信号给硬件。在最初的设计里,因为便宜,我们打算使用运行OpenWRT的路由器来当硬件端的控制器。
460 | 我们在硬件端使用Arduino作为控制器,写起代码特别简单,可以让我们关注于业务。
461 | 我们还使用了Zigbee模块Xbee及I2C作为连接不同Arduino模块的的介质。
462 | 最后,我们还需要在网页上做一个图表来显示实时数据。
463 |
464 |
所有的这些,我们需要使用Python、Java、JavaScript、C、Arduino五种语言。而如果我们要写相应的iOS应用,我们还需要Objective-C。
465 |
而同样的:
466 |
我刚实习的时候(2013 年),项目里使用的是 Backbone,作为单页面应用框架的核心来打造 Web 应用。这时,我开始关注于 Node.js,使用它实现物联网应用的可能性。当时,已经有了物联网协议 MQTT 和 CoAP 相关的库,便照猫画虎地写了一个支持 HTTP、CoAP、WebSocket 和 MQTT 的物联网。由于,当时缺乏一些大型应用的开发经典,所以做得并不是很好,但是已经可以看到 JavaScript 在这方面的远景。
467 |
一年多后,Ionic 也还没推出正式版,便发现到了这个框架真的很棒——它自带了一系列的 UI 组件,还用 NgCordova 封装了一系列 Cordova 的插件。便开始使用 Ionic 写了一些移动应用,发现还挺顺手的。接着,便拿这个框架尝试写物联网应用,这需要一些原生的插件,如 BLE、MQTT。后来,我也写了一个简单的 CoAP 插件。
468 |
后来,我们不再需要编译 Node.js(早期,使用的是 Raspberry Pi 来运行 Node.js),就可以在 ARM 处理器上运行 Node.js。并且我们已经有 Tessel、Espruino、Kinoma Create、Ruff 这些可以直接运行 JavaScript 的开发板。三星还推出 IoT.js,可以让更多的嵌入式设备,直接使用 JavaScript 作为开发语言。
469 |
人们开始在硬件上使用 JavaScript 的原因有很多,如 Web 的开发人员是最多的,而 JavaScript 很容易上手。
470 |
嵌入式设备分类
471 |
两种前端类型
472 |
嵌入式设备作为服务端
473 |
诸如 ESP32 或者 ESP8266,它们可以和传统的 Web 应用一样,托管前端应用。
474 |
嵌入式设备作为服务端和客户端
475 |
如 Android 系统中的
476 |
Android Things 相似的还有 Microsoft 10 IoT、Raspberry Pi
477 |
嵌入式设备分类
478 |
479 | 低资源受限设备,只能使用传统的编译型语言编写,如 C、C++、汇编,
480 |
481 |
482 |
低资源受限设备:编译型语言
483 |
它们只能使用
484 |
普通嵌入式设备:
485 |
Android 是用得最多的嵌入式系统,。
486 |
OpenWRT 是用得最广泛的嵌入式系统之一,
487 |
在某些资源受限的设备上,我们无法运行一个带着完整 JavaScript 引擎的 WebView——出于软件体积、运行内存等因素的限制,去除了这些特性。诸如 CSS 3 中的动画特性,会占用大量的运算资源,算去掉了相应的支持。
488 |
固件编写
489 |
Johnny-Five
490 |
IoT.js
491 |
Be Professinoal
492 |
Arduino 与 Processing
493 |
ESP866/ESP32 与 Lua
494 |
C 语言
495 |
大前端:移动端
496 |
混合应用
497 |
依我的角度来看,使用什么跨平台框架来看,区别并不是太大。目前主流的方案,仍然是原生(含跨平台框架) + HTML5 应用。从业务的角度上来看待这个问题,那么还是希望,可以用 HTML 5 的地方多——更新功能方便。
498 |
也因此,虽然在过去,笔者写过基于 React Native 的混合应用框架 Dore。我相信:Flutter 也会出现这样的混合应用框架。不过,对于有原生开发能力的团队来说,它们的框架还会是三部分:
499 |
500 | 原生功能部分
501 | 原生 + H5 的频繁更新部分
502 | Fultter 的跨平台部分
503 |
504 |
写业务嘛,框架都只是工具。
505 |
跨平台应用
506 |
React Native
507 |
Flutter
508 |
Weex
509 |
小程序
510 |
小程序,即 HTML5 小程序,即无需安装即可下载运行的应用程序。与普通的移动 Web 应用不同的是,小程序相当于是高阶版的混合应用 。
511 |
如果只是从这一点上来看,其实是不是和微信一样的定制型小程序,并不是那么重要。重要的,在于与原生界面结合,并提供离线使用功能。 它也是小程序与普通的 HTML 应用的区别。
512 |
大前端:桌面应用
513 |
跨平台 WebView 应用框架
514 |
Electron/NW.js
515 |
跨平台框架
516 |
QT + WebView
517 |
大前端:VR/AR
518 |
VR
519 |
AR
520 |
大前端:数据可视化
521 |
在过去我阅读的一些书籍里面,主要是以 Processing 作为可视化的语言——它起始于 2001 年,它最初是面向美术工作者和设计者创建的,后来变成了全面的设计和原型工具,可以用于创建复杂数据可视化领域。
522 |
高级篇
523 |
D3.js
524 |
大前端:游戏
525 |
随着移动端的性能不断变好,在 2019 年,我开始看好使用 HTML 5 技术来开发一些游戏。当然了,主要原因还是微信小游戏的出现。但是,不管怎样,我开始尝试在这个领域的探索。
526 |
WebView 游戏
527 |
JavaScript 语言游戏
528 |
大前端:前端
529 |
前端领域,在 2018 年已经趋于平衡,Angular、Vue、React 都没有出现太大的变化。
530 |
Web 应用
531 |
SPA (单页面应用)框架:A/V/R
532 |
架构选型上,也趋势于平衡。该用啥的还是用啥,偶尔还是会出现一些框架切换的新闻。尽管在 2019 年,会出现一些新的框架,但是还不太可能快引起变化。
533 |
MPA (多页面应用)框架
534 |
语言
535 |
TypeScript
536 |
TypeScript 真香。
537 |
前端,没什么好看——除了,娱乐新闻。
538 |
大前端:UI & UX
539 |
自诩是一个 “斜杠青年” 的我,为自己添加不同的 “title”,设计师/设计学徒便是其中的一个。
540 |
一来,作为一个前端开发人员,日常就是要和用户打交道。几年经验下来,多多少少也能积累点经验;二来,除了职业本身,我也是一个喜爱画画的人。只是受限于工作的影响,在这方面的投入便没有那么多。
541 |
也因此,这样一来,我们便也是能得到一些慰藉。
542 |
绘画
543 |
用户体验设计
544 |
图形界面设计
545 |
大前端:命令行接口
546 |
传统 CLI vs 跨平台 CLI
547 |
优势?
548 |
549 |
相关实践和原则
550 |
Best Practise?
551 |
Mopass、ADR、Stepping,多多少少也算是写过一些 CLI。
552 |
除了此,使用 Node.js
553 |
Clone 代码,执行复制
554 |
一般型 Shell,如 git clone
555 |
而跨平台,则是 fs.copy
而非 GNU/Linux 上的 cp
,又或者是 Windows 上的 xcopy
556 |
相似的工具和语言有 Python 等
557 |
为什么是 Node.js
558 |
因为我们是个前端,相关的环境肯定是有的。只要是个前端开发,一定会有 Node.js 环境。
559 |
前端,永不止境
560 |
大前端架构
561 |
练习
562 |
569 |
570 |
571 |
--------------------------------------------------------------------------------
/init.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | read -p "名书:" bookname
4 | read -p "作者:" author
5 |
6 | sed -i "s/Phodal Huang/$author/g" "build/author.html" "build/metadata.xml" "build/title.txt" "Makefile"
7 | sed -i "s/电子书模板/$bookname/g" "build/author.html" "build/metadata.xml" "build/title.txt" "Makefile"
--------------------------------------------------------------------------------
/listings-setup.tex:
--------------------------------------------------------------------------------
1 | % Contents of listings-setup.tex
2 | \usepackage{xcolor}
3 |
4 | \lstset{
5 | basicstyle=\ttfamily,
6 | numbers=left,
7 | keywordstyle=\color[rgb]{0.13,0.29,0.53}\bfseries,
8 | stringstyle=\color[rgb]{0.31,0.60,0.02},
9 | commentstyle=\color[rgb]{0.56,0.35,0.01}\itshape,
10 | numberstyle=\footnotesize,
11 | stepnumber=1,
12 | numbersep=5pt,
13 | backgroundcolor=\color[RGB]{248,248,248},
14 | showspaces=false,
15 | showstringspaces=false,
16 | showtabs=false,
17 | tabsize=2,
18 | captionpos=b,
19 | breaklines=true,
20 | breakatwhitespace=true,
21 | breakautoindent=true,
22 | escapeinside={\%*}{*)},
23 | linewidth=\textwidth,
24 | basewidth=0.5em,
25 | }
--------------------------------------------------------------------------------
/style.css:
--------------------------------------------------------------------------------
1 | @media screen and (min-width: 768px) {
2 | body {
3 | width: 748px;
4 | margin: 40px auto;
5 | }
6 | }
7 |
8 |
9 | /*!
10 | * Mifa v0.2.0
11 | * https://github.com/phodal/mifa#readme
12 | *
13 | * Copyright (c) 2018 Phodal Huang
14 | * Licensed under the MIT license
15 | */
16 |
17 | .color-primary {
18 | color: #384452;
19 | background-color: #384452;
20 | }
21 |
22 | .color-grey {
23 | color: #d1d8df;
24 | background-color: #d1d8df;
25 | }
26 |
27 | .color-green {
28 | color: #1abc9c;
29 | background-color: #1abc9c;
30 | }
31 |
32 | .color-dark-grey {
33 | color: #5e6772;
34 | background-color: #5e6772;
35 | }
36 |
37 | .color-blue {
38 | color: #23B7F3;
39 | background-color: #23B7F3;
40 | }
41 |
42 | .color-code-grey {
43 | color: #eef1f5;
44 | background-color: #eef1f5;
45 | }
46 |
47 | .color-red {
48 | color: #f53d3d;
49 | background-color: #f53d3d;
50 | }
51 |
52 | .color-yellow {
53 | color: #ffff3a;
54 | background-color: #ffff3a;
55 | }
56 |
57 | .color-light-grey {
58 | color: #fdfdfd;
59 | background-color: #fdfdfd;
60 | }
61 |
62 | *,
63 | *:after,
64 | *:before {
65 | box-sizing: inherit;
66 | }
67 |
68 | html {
69 | box-sizing: border-box;
70 | font-size: 62.5%;
71 | }
72 |
73 | body {
74 | color: #384452;
75 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", SimSun, sans-serif;
76 | font-size: 1.6em;
77 | font-weight: 300;
78 | letter-spacing: .01em;
79 | line-height: 1.6;
80 | }
81 |
82 | blockquote {
83 | border-left: 0.3rem solid #1abc9c;
84 | margin-left: 0;
85 | margin-right: 0;
86 | padding: 1rem 1.5rem;
87 | }
88 |
89 | blockquote *:last-child {
90 | margin-bottom: 0;
91 | }
92 |
93 | q:before {
94 | color: #1abc9c;
95 | content: open-quote;
96 | font-size: 4em;
97 | line-height: 0.1em;
98 | margin-right: 0.25em;
99 | vertical-align: -0.4em;
100 | }
101 |
102 | q:after {
103 | content: none;
104 | }
105 |
106 | .button,
107 | button,
108 | input[type='button'],
109 | input[type='reset'],
110 | input[type='submit'] {
111 | background-color: #384452;
112 | border: 0.1rem solid #384452;
113 | border-radius: .4rem;
114 | color: #fff;
115 | cursor: pointer;
116 | display: inline-block;
117 | font-size: 1.1rem;
118 | font-weight: 700;
119 | height: 3.8rem;
120 | letter-spacing: .1rem;
121 | line-height: 3.8rem;
122 | padding: 0 3.0rem;
123 | text-align: center;
124 | text-decoration: none;
125 | text-transform: uppercase;
126 | white-space: nowrap;
127 | }
128 |
129 | .button:focus, .button:hover,
130 | button:focus,
131 | button:hover,
132 | input[type='button']:focus,
133 | input[type='button']:hover,
134 | input[type='reset']:focus,
135 | input[type='reset']:hover,
136 | input[type='submit']:focus,
137 | input[type='submit']:hover {
138 | background-color: #1abc9c;
139 | border-color: #1abc9c;
140 | color: #fff;
141 | outline: 0;
142 | }
143 |
144 | .button[disabled],
145 | button[disabled],
146 | input[type='button'][disabled],
147 | input[type='reset'][disabled],
148 | input[type='submit'][disabled] {
149 | cursor: default;
150 | opacity: .5;
151 | }
152 |
153 | .button[disabled]:focus, .button[disabled]:hover,
154 | button[disabled]:focus,
155 | button[disabled]:hover,
156 | input[type='button'][disabled]:focus,
157 | input[type='button'][disabled]:hover,
158 | input[type='reset'][disabled]:focus,
159 | input[type='reset'][disabled]:hover,
160 | input[type='submit'][disabled]:focus,
161 | input[type='submit'][disabled]:hover {
162 | background-color: #384452;
163 | border-color: #384452;
164 | }
165 |
166 | .button.button-outline,
167 | button.button-outline,
168 | input[type='button'].button-outline,
169 | input[type='reset'].button-outline,
170 | input[type='submit'].button-outline {
171 | background-color: transparent;
172 | color: #384452;
173 | }
174 |
175 | .button.button-outline:focus, .button.button-outline:hover,
176 | button.button-outline:focus,
177 | button.button-outline:hover,
178 | input[type='button'].button-outline:focus,
179 | input[type='button'].button-outline:hover,
180 | input[type='reset'].button-outline:focus,
181 | input[type='reset'].button-outline:hover,
182 | input[type='submit'].button-outline:focus,
183 | input[type='submit'].button-outline:hover {
184 | background-color: transparent;
185 | border-color: #1abc9c;
186 | color: #1abc9c;
187 | }
188 |
189 | .button.button-outline[disabled]:focus, .button.button-outline[disabled]:hover,
190 | button.button-outline[disabled]:focus,
191 | button.button-outline[disabled]:hover,
192 | input[type='button'].button-outline[disabled]:focus,
193 | input[type='button'].button-outline[disabled]:hover,
194 | input[type='reset'].button-outline[disabled]:focus,
195 | input[type='reset'].button-outline[disabled]:hover,
196 | input[type='submit'].button-outline[disabled]:focus,
197 | input[type='submit'].button-outline[disabled]:hover {
198 | border-color: inherit;
199 | color: #384452;
200 | }
201 |
202 | .button.button-clear,
203 | button.button-clear,
204 | input[type='button'].button-clear,
205 | input[type='reset'].button-clear,
206 | input[type='submit'].button-clear {
207 | background-color: transparent;
208 | border-color: transparent;
209 | color: #384452;
210 | }
211 |
212 | .button.button-clear:focus, .button.button-clear:hover,
213 | button.button-clear:focus,
214 | button.button-clear:hover,
215 | input[type='button'].button-clear:focus,
216 | input[type='button'].button-clear:hover,
217 | input[type='reset'].button-clear:focus,
218 | input[type='reset'].button-clear:hover,
219 | input[type='submit'].button-clear:focus,
220 | input[type='submit'].button-clear:hover {
221 | background-color: transparent;
222 | border-color: transparent;
223 | color: #1abc9c;
224 | }
225 |
226 | .button.button-clear[disabled]:focus, .button.button-clear[disabled]:hover,
227 | button.button-clear[disabled]:focus,
228 | button.button-clear[disabled]:hover,
229 | input[type='button'].button-clear[disabled]:focus,
230 | input[type='button'].button-clear[disabled]:hover,
231 | input[type='reset'].button-clear[disabled]:focus,
232 | input[type='reset'].button-clear[disabled]:hover,
233 | input[type='submit'].button-clear[disabled]:focus,
234 | input[type='submit'].button-clear[disabled]:hover {
235 | color: #384452;
236 | }
237 |
238 | code {
239 | background: #eef1f5;
240 | border-radius: .4rem;
241 | font-size: 86%;
242 | margin: 0 .2rem;
243 | padding: .2rem .5rem;
244 | white-space: nowrap;
245 | }
246 |
247 | pre {
248 | background: #eef1f5;
249 | border-left: 0.3rem solid #1abc9c;
250 | overflow-y: hidden;
251 | }
252 |
253 | pre > code {
254 | border-radius: 0;
255 | display: block;
256 | padding: 1rem 1.5rem;
257 | white-space: pre;
258 | }
259 |
260 | hr {
261 | border: 0;
262 | border-top: 0.1rem solid #384452;
263 | margin: 3.0rem 0;
264 | }
265 |
266 | input[type='email'],
267 | input[type='number'],
268 | input[type='password'],
269 | input[type='search'],
270 | input[type='tel'],
271 | input[type='text'],
272 | input[type='url'],
273 | input:not([type]),
274 | textarea,
275 | select {
276 | -webkit-appearance: none;
277 | -moz-appearance: none;
278 | appearance: none;
279 | background-color: transparent;
280 | border: 0.1rem solid #d1d8df;
281 | border-radius: .4rem;
282 | box-shadow: none;
283 | box-sizing: inherit;
284 | height: 3.8rem;
285 | padding: .6rem 1.0rem;
286 | width: 100%;
287 | }
288 |
289 | input[type='email']:focus,
290 | input[type='number']:focus,
291 | input[type='password']:focus,
292 | input[type='search']:focus,
293 | input[type='tel']:focus,
294 | input[type='text']:focus,
295 | input[type='url']:focus,
296 | input:not([type]):focus,
297 | textarea:focus,
298 | select:focus {
299 | border-color: #1abc9c;
300 | outline: 0;
301 | }
302 |
303 | select {
304 | background: url('data:image/svg+xml;utf8,
') center right no-repeat;
305 | padding-right: 3.0rem;
306 | }
307 |
308 | select:focus {
309 | background-image: url('data:image/svg+xml;utf8,
');
310 | }
311 |
312 | textarea {
313 | min-height: 6.5rem;
314 | }
315 |
316 | label,
317 | legend {
318 | display: block;
319 | font-size: 1.6rem;
320 | font-weight: 700;
321 | margin-bottom: .5rem;
322 | }
323 |
324 | fieldset {
325 | border-width: 0;
326 | padding: 0;
327 | }
328 |
329 | input[type='checkbox'],
330 | input[type='radio'] {
331 | display: inline;
332 | }
333 |
334 | .label-inline {
335 | display: inline-block;
336 | font-weight: normal;
337 | margin-left: .5rem;
338 | }
339 |
340 | .container {
341 | margin: 0 auto;
342 | max-width: 112.0rem;
343 | padding: 0 2.0rem;
344 | position: relative;
345 | width: 100%;
346 | }
347 |
348 | .row {
349 | display: flex;
350 | flex-direction: column;
351 | padding: 0;
352 | width: 100%;
353 | }
354 |
355 | .row.row-no-padding {
356 | padding: 0;
357 | }
358 |
359 | .row.row-no-padding > .column {
360 | padding: 0;
361 | }
362 |
363 | .row.row-wrap {
364 | flex-wrap: wrap;
365 | }
366 |
367 | .row.row-top {
368 | align-items: flex-start;
369 | }
370 |
371 | .row.row-bottom {
372 | align-items: flex-end;
373 | }
374 |
375 | .row.row-center {
376 | align-items: center;
377 | }
378 |
379 | .row.row-stretch {
380 | align-items: stretch;
381 | }
382 |
383 | .row.row-baseline {
384 | align-items: baseline;
385 | }
386 |
387 | .row .column {
388 | display: block;
389 | flex: 1 1 auto;
390 | margin-left: 0;
391 | max-width: 100%;
392 | width: 100%;
393 | }
394 |
395 | .row .column.column-offset-10 {
396 | margin-left: 10%;
397 | }
398 |
399 | .row .column.column-offset-20 {
400 | margin-left: 20%;
401 | }
402 |
403 | .row .column.column-offset-25 {
404 | margin-left: 25%;
405 | }
406 |
407 | .row .column.column-offset-33, .row .column.column-offset-34 {
408 | margin-left: 33.3333%;
409 | }
410 |
411 | .row .column.column-offset-50 {
412 | margin-left: 50%;
413 | }
414 |
415 | .row .column.column-offset-66, .row .column.column-offset-67 {
416 | margin-left: 66.6666%;
417 | }
418 |
419 | .row .column.column-offset-75 {
420 | margin-left: 75%;
421 | }
422 |
423 | .row .column.column-offset-80 {
424 | margin-left: 80%;
425 | }
426 |
427 | .row .column.column-offset-90 {
428 | margin-left: 90%;
429 | }
430 |
431 | .row .column.column-10 {
432 | flex: 0 0 10%;
433 | max-width: 10%;
434 | }
435 |
436 | .row .column.column-20 {
437 | flex: 0 0 20%;
438 | max-width: 20%;
439 | }
440 |
441 | .row .column.column-25 {
442 | flex: 0 0 25%;
443 | max-width: 25%;
444 | }
445 |
446 | .row .column.column-33, .row .column.column-34 {
447 | flex: 0 0 33.3333%;
448 | max-width: 33.3333%;
449 | }
450 |
451 | .row .column.column-40 {
452 | flex: 0 0 40%;
453 | max-width: 40%;
454 | }
455 |
456 | .row .column.column-50 {
457 | flex: 0 0 50%;
458 | max-width: 50%;
459 | }
460 |
461 | .row .column.column-60 {
462 | flex: 0 0 60%;
463 | max-width: 60%;
464 | }
465 |
466 | .row .column.column-66, .row .column.column-67 {
467 | flex: 0 0 66.6666%;
468 | max-width: 66.6666%;
469 | }
470 |
471 | .row .column.column-75 {
472 | flex: 0 0 75%;
473 | max-width: 75%;
474 | }
475 |
476 | .row .column.column-80 {
477 | flex: 0 0 80%;
478 | max-width: 80%;
479 | }
480 |
481 | .row .column.column-90 {
482 | flex: 0 0 90%;
483 | max-width: 90%;
484 | }
485 |
486 | .row .column .column-top {
487 | align-self: flex-start;
488 | }
489 |
490 | .row .column .column-bottom {
491 | align-self: flex-end;
492 | }
493 |
494 | .row .column .column-center {
495 | -ms-grid-row-align: center;
496 | align-self: center;
497 | }
498 |
499 | @media (min-width: 40rem) {
500 | .row {
501 | flex-direction: row;
502 | margin-left: -1.0rem;
503 | width: calc(100% + 2.0rem);
504 | }
505 | .row .column {
506 | margin-bottom: inherit;
507 | padding: 0 1.0rem;
508 | }
509 | }
510 |
511 | img {
512 | max-width: 100%;
513 | }
514 |
515 | a {
516 | color: #1abc9c;
517 | text-decoration: none;
518 | }
519 |
520 | a:focus, a:hover {
521 | color: #23B7F3;
522 | }
523 |
524 | dl,
525 | ol,
526 | ul {
527 | list-style: none;
528 | margin-top: 0;
529 | padding-left: 0;
530 | }
531 |
532 | dl dl,
533 | dl ol,
534 | dl ul,
535 | ol dl,
536 | ol ol,
537 | ol ul,
538 | ul dl,
539 | ul ol,
540 | ul ul {
541 | font-size: 90%;
542 | margin: 1.5rem 0 1.5rem 3.0rem;
543 | }
544 |
545 | ol {
546 | list-style: decimal inside;
547 | }
548 |
549 | ul {
550 | list-style: circle inside;
551 | }
552 |
553 | .button,
554 | button,
555 | dd,
556 | dt,
557 | li {
558 | margin-bottom: 1.0rem;
559 | }
560 |
561 | fieldset,
562 | input,
563 | select,
564 | textarea {
565 | margin-bottom: 1.5rem;
566 | }
567 |
568 | blockquote,
569 | dl,
570 | figure,
571 | form,
572 | ol,
573 | p,
574 | pre,
575 | table,
576 | ul {
577 | margin-bottom: 2.5rem;
578 | }
579 |
580 | table {
581 | border-spacing: 0;
582 | width: 100%;
583 | }
584 |
585 | table thead th,
586 | thead th {
587 | border-top: 0.1rem solid #d1d8df;
588 | background: #fdfdfd;
589 | }
590 |
591 | tbody tr:nth-child(even) {
592 | background: #fdfdfd;
593 | }
594 |
595 | tbody tr:nth-child(odd) {
596 | background: #eef1f5;
597 | }
598 |
599 | td,
600 | th {
601 | border-bottom: 0.1rem solid #d1d8df;
602 | padding: 1.2rem 1.5rem;
603 | text-align: left;
604 | }
605 |
606 | td:first-child,
607 | th:first-child {
608 | padding-left: 0;
609 | }
610 |
611 | td:last-child,
612 | th:last-child {
613 | padding-right: 0;
614 | }
615 |
616 | u {
617 | text-decoration: none;
618 | border-bottom: 1px dashed;
619 | }
620 |
621 | a {
622 | text-decoration: none;
623 | }
624 |
625 | u {
626 | background: #ffff3a;
627 | }
628 |
629 | b,
630 | strong {
631 | font-weight: bold;
632 | }
633 |
634 | p {
635 | margin-top: 0;
636 | }
637 |
638 | h1,
639 | h2,
640 | h3,
641 | h4,
642 | h5,
643 | h6 {
644 | font-weight: 300;
645 | letter-spacing: -.1rem;
646 | margin-bottom: 2.0rem;
647 | margin-top: 0;
648 | }
649 |
650 | h1 {
651 | font-size: 4.0rem;
652 | line-height: 1.2;
653 | }
654 |
655 | h2 {
656 | font-size: 3.2rem;
657 | line-height: 1.25;
658 | }
659 |
660 | h3 {
661 | font-size: 2.8rem;
662 | line-height: 1.3;
663 | }
664 |
665 | h4 {
666 | font-size: 2.4rem;
667 | letter-spacing: -.08rem;
668 | line-height: 1.35;
669 | }
670 |
671 | h5 {
672 | font-size: 2.0rem;
673 | letter-spacing: -.05rem;
674 | line-height: 1.5;
675 | }
676 |
677 | h6 {
678 | font-size: 1.6rem;
679 | letter-spacing: 0;
680 | line-height: 1.4;
681 | }
682 |
683 | .clearfix:after {
684 | clear: both;
685 | content: ' ';
686 | display: table;
687 | }
688 |
689 | .float-left {
690 | float: left;
691 | }
692 |
693 | .float-right {
694 | float: right;
695 | }
696 |
--------------------------------------------------------------------------------
/template/template.tex:
--------------------------------------------------------------------------------
1 | \documentclass[a4paper, 11pt]{article}
2 | \usepackage{geometry} % 設定邊界
3 | \geometry{
4 | top=1in,
5 | inner=1in,
6 | outer=1in,
7 | bottom=1in,
8 | headheight=3ex,
9 | headsep=2ex
10 | }
11 | \usepackage{tabu}
12 | \usepackage[T1]{fontenc}
13 | \usepackage{lmodern}
14 | \usepackage{booktabs}
15 | \usepackage{amssymb,amsmath}
16 | \usepackage{ifxetex,ifluatex}
17 | \usepackage{fixltx2e} % provides \textsubscript
18 | % use upquote if available, for straight quotes in verbatim environments
19 | \IfFileExists{upquote.sty}{\usepackage{upquote}}{}
20 | \ifnum 0\ifxetex 1\fi\ifluatex 1\fi=0 % if pdftex
21 | \usepackage[utf8]{inputenc}
22 | $if(euro)$
23 | \usepackage{eurosym}
24 | $endif$
25 | \else % if luatex or xelatex
26 | \usepackage{fontspec} % 允許設定字體
27 | \usepackage{xeCJK} % 分開設置中英文字型
28 | \setCJKmainfont{STSong} % 設定中文字型
29 | \setmainfont[Mapping=tex-text]{Times New Roman}%\rmfamily 使用的字体,默认英文和数字的字体。 % 設定英文字型
30 | \setromanfont{Georgia} % 字型
31 | \setmonofont{Courier New}
32 | \linespread{1.2}\selectfont % 行距
33 | \XeTeXlinebreaklocale "zh" % 針對中文自動換行
34 | \XeTeXlinebreakskip = 0pt plus 1pt % 字與字之間加入0pt至1pt的間距,確保左右對整齊
35 | \parindent 0em % 段落縮進
36 | \setlength{\parskip}{20pt} % 段落之間的距離
37 | \ifxetex
38 | \usepackage{xltxtra,xunicode}
39 | \fi
40 | \defaultfontfeatures{Mapping=tex-text,Scale=MatchLowercase}
41 | \newcommand{\euro}{€}
42 | $if(mainfont)$
43 | \setmainfont{$mainfont$}
44 | $endif$
45 | $if(sansfont)$
46 | \setsansfont{$sansfont$}
47 | $endif$
48 | $if(monofont)$
49 | \setmonofont{$monofont$}
50 | $endif$
51 | $if(mathfont)$
52 | \setmathfont{$mathfont$}
53 | $endif$
54 | \fi
55 | % use microtype if available
56 | \IfFileExists{microtype.sty}{\usepackage{microtype}}{}
57 | $if(geometry)$
58 | \usepackage[$for(geometry)$$geometry$$sep$,$endfor$]{geometry}
59 | $endif$
60 | $if(natbib)$
61 | \usepackage{natbib}
62 | \bibliographystyle{plainnat}
63 | $endif$
64 | $if(biblatex)$
65 | \usepackage{biblatex}
66 | $if(biblio-files)$
67 | \bibliography{$biblio-files$}
68 | $endif$
69 | $endif$
70 | $if(listings)$
71 | \usepackage{listings}
72 | $endif$
73 | $if(lhs)$
74 | \lstnewenvironment{code}{\lstset{language=Haskell,basicstyle=\small\ttfamily}}{}
75 | $endif$
76 | $if(highlighting-macros)$
77 | $highlighting-macros$
78 | $endif$
79 | $if(verbatim-in-note)$
80 | \usepackage{fancyvrb}
81 | $endif$
82 | $if(tables)$
83 | \usepackage{longtable}
84 | $endif$
85 |
86 | \usepackage{graphicx}
87 | \usepackage{caption}
88 | % We will generate all images so they have a width \maxwidth. This means
89 | % that they will get their normal width if they fit onto the page, but
90 | % are scaled down if they would overflow the margins.
91 | \makeatletter
92 | \def\maxwidth{\ifdim\Gin@nat@width>\linewidth\linewidth
93 | \else\Gin@nat@width\fi}
94 | \makeatother
95 | \let\Oldincludegraphics\includegraphics
96 | \renewcommand{\includegraphics}[1]{\Oldincludegraphics[width=\maxwidth]{#1}}
97 | \ifxetex
98 | \usepackage[setpagesize=false, % page size defined by xetex
99 | unicode=false, % unicode breaks when used with xetex
100 | xetex]{hyperref}
101 | \else
102 | \usepackage[unicode=true]{hyperref}
103 | \fi
104 | \hypersetup{breaklinks=true,
105 | bookmarks=true,
106 | pdfauthor={$author-meta$},
107 | pdftitle={$title-meta$},
108 | colorlinks=true,
109 | urlcolor=$if(urlcolor)$$urlcolor$$else$blue$endif$,
110 | linkcolor=$if(linkcolor)$$linkcolor$$else$magenta$endif$,
111 | pdfborder={0 0 0}}
112 | \urlstyle{same} % don't use monospace font for urls
113 | $if(links-as-notes)$
114 | % Make links footnotes instead of hotlinks:
115 | \renewcommand{\href}[2]{#2\footnote{\url{#1}}}
116 | $endif$
117 | $if(strikeout)$
118 | \usepackage[normalem]{ulem}
119 | % avoid problems with \sout in headers with hyperref:
120 | \pdfstringdefDisableCommands{\renewcommand{\sout}{}}
121 | $endif$
122 | \setlength{\parindent}{0pt}
123 | %\setlength{\parskip}{6pt plus 2pt minus 1pt}
124 | \setlength{\emergencystretch}{3em} % prevent overfull lines
125 | \usepackage{titling}
126 | \setlength{\droptitle}{-8em} % 將標題移動至頁面的上面
127 |
128 | \usepackage{fancyhdr}
129 | \usepackage{lastpage}
130 | \pagestyle{fancyplain}
131 |
132 | $if(numbersections)$
133 | \setcounter{secnumdepth}{5}
134 | $else$
135 | \setcounter{secnumdepth}{0}
136 | $endif$
137 | $if(verbatim-in-note)$
138 | \VerbatimFootnotes % allows verbatim text in footnotes
139 | $endif$
140 | $if(lang)$
141 | \ifxetex
142 | \usepackage{polyglossia}
143 | \setmainlanguage{$mainlang$}
144 | \else
145 | \usepackage[$lang$]{babel}
146 | \fi
147 | $endif$
148 | $for(header-includes)$
149 | $header-includes$
150 | $endfor$
151 |
152 | $if(title)$
153 | \title{$title$}
154 | $endif$
155 | \author{$for(author)$$author$$sep$ \and $endfor$}
156 | \date{$date$}
157 |
158 | %%%% 段落首行缩进两个字 %%%%
159 | \makeatletter
160 | \let\@afterindentfalse\@afterindenttrue
161 | \@afterindenttrue
162 | \makeatother
163 | \setlength{\parindent}{2em} %中文缩进两个汉字位
164 |
165 |
166 | %%%% 下面的命令重定义页面边距,使其符合中文刊物习惯 %%%%
167 | \addtolength{\topmargin}{-2pt}
168 | \setlength{\oddsidemargin}{0.63cm} % 3.17cm - 1 inch
169 | \setlength{\evensidemargin}{\oddsidemargin}
170 | \setlength{\textwidth}{14.66cm}
171 | \setlength{\textheight}{24.00cm} % 24.62
172 |
173 | %%%% 下面的命令设置行间距与段落间距 %%%%
174 | \linespread{1.4}
175 | % \setlength{\parskip}{1ex}
176 | \setlength{\parskip}{0.5\baselineskip}
177 |
178 |
179 | \begin{document}
180 | %%%% 定理类环境的定义 %%%%
181 | \newtheorem{example}{例} % 整体编号
182 | \newtheorem{algorithm}{算法}
183 | \newtheorem{theorem}{定理}[section] % 按 section 编号
184 | \newtheorem{definition}{定义}
185 | \newtheorem{axiom}{公理}
186 | \newtheorem{property}{性质}
187 | \newtheorem{proposition}{命题}
188 | \newtheorem{lemma}{引理}
189 | \newtheorem{corollary}{推论}
190 | \newtheorem{remark}{注解}
191 | \newtheorem{condition}{条件}
192 | \newtheorem{conclusion}{结论}
193 | \newtheorem{assumption}{假设}
194 |
195 | \newcommand{\tightlist}{%
196 | \setlength{\itemsep}{0pt}\setlength{\parskip}{0pt}}
197 |
198 | %%%% 重定义 %%%%
199 | \renewcommand{\contentsname}{目录} % 将Contents改为目录
200 | \renewcommand{\abstractname}{摘要} % 将Abstract改为摘要
201 | \renewcommand{\refname}{参考文献} % 将References改为参考文献
202 | \renewcommand{\indexname}{索引}
203 | \renewcommand{\figurename}{图}
204 | \renewcommand{\tablename}{表}
205 | \renewcommand{\appendixname}{附录}
206 |
207 | $for(include-before)$
208 | $include-before$
209 |
210 | $endfor$
211 | $if(toc)$
212 | {
213 | \newpage
214 | \hypersetup{linkcolor=black}
215 | \setcounter{tocdepth}{$toc-depth$}
216 | \tableofcontents
217 | }
218 | \newpage
219 | $endif$
220 | $body$
221 |
222 | $if(natbib)$
223 | $if(biblio-files)$
224 | $if(biblio-title)$
225 | $if(book-class)$
226 | \renewcommand\bibname{$biblio-title$}
227 | $else$
228 | \renewcommand\refname{$biblio-title$}
229 | $endif$
230 | $endif$
231 | \bibliography{$biblio-files$}
232 |
233 | $endif$
234 | $endif$
235 | $if(biblatex)$
236 | \printbibliography$if(biblio-title)$[title=$biblio-title$]$endif$
237 |
238 | $endif$
239 | $for(include-after)$
240 | $include-after$
241 |
242 | $endfor$
243 | \end{document}
--------------------------------------------------------------------------------