sequelize dnf模型修改改 怎么办

在 SegmentFault,解决技术问题
每个月,我们帮助 1000 万的开发者解决各种各样的技术问题。并助力他们在技术能力、职业生涯、影响力上获得提升。
一线的工程师、著名开源项目的作者们,都在这里:
获取验证码
已有账号?
标签:至少1个,最多5个
Migrations - 迁移
此系列文章的应用示例已发布于 . 可以 Fork 帮助改进或 Star 关注更新. 欢迎 Star.
就像您使用Git / SVN来管理源代码的更改一样,您可以使用迁移来跟踪数据库的更改。 通过迁移,您可以将现有的数据库转移到另一个状态,反之亦然:这些状态转换将保存在迁移文件中,它们描述了如何进入新状态以及如何还原更改以恢复旧状态。
您将需要。 CLI支持迁移和项目引导。
命令行界面
安装命令行界面
让我们从安装CLI开始,你可以在
找到说明。 最推荐的方式是这样安装
$ npm install --save sequelize-cli
要创建一个空项目,你需要执行 init 命令
$ node_modules/.bin/sequelize init
这将创建以下文件夹
config, 包含配置文件,它告诉CLI如何连接数据库
models,包含您的项目的所有模型
migrations, 包含所有迁移文件
seeders, 包含所有种子文件
在继续进行之前,我们需要告诉 CLI 如何连接到数据库。 为此,可以打开默认配置文件 config/config.json。 看起来像这样
development: {
username: 'root',
password: null,
database: 'database_development',
host: '127.0.0.1',
dialect: 'mysql'
username: 'root',
password: null,
database: 'database_test',
host: '127.0.0.1',
dialect: 'mysql'
production: {
username: 'root',
password: null,
database: 'database_production',
host: '127.0.0.1',
dialect: 'mysql'
现在编辑此文件并设置正确的数据库凭据和方言。
注意: 如果你的数据库还不存在,你可以调用 db:create 命令。 通过正确的访问,它将为您创建该数据库。
创建第一个模型(和迁移)
一旦您正确配置了CLI配置文件,您就可以首先创建迁移。 它像执行一个简单的命令一样简单。
我们将使用 model:generate 命令。 此命令需要两个选项
name, 模型的名称
attributes, 模型的属性列表
让我们创建一个名叫 User 的模型
$ node_modules/.bin/sequelize model:generate --name User --attributes firstName:string,lastName:string,email:string
这将发生以下事情
在 models 文件夹中创建了一个 user 模型文件
在 migrations 文件夹中创建了一个名字像 XXXXXXXXXXXXXX-create-user.js 的迁移文件
注意: Sequelize 将只使用模型文件,它是表描述。另一边,迁移文件是该模型的更改,或更具体的是说 CLI 所使用的表。 处理迁移,如提交或日志,以进行数据库的某些更改。
直到这一步,CLI没有将任何东西插入数据库。 我们刚刚为我们的第一个模型 User 创建了必需的模型和迁移文件。 现在要在数据库中实际创建该表,需要运行 db:migrate 命令。
$ node_modules/.bin/sequelize db:migrate
此命令将执行这些步骤
将在数据库中确保一个名为 SequelizeMeta 的表。 此表用于记录在当前数据库上运行的迁移
开始寻找尚未运行的任何迁移文件。 这可以通过检查 SequelizeMeta 表。 在这个例子中,它将运行我们在最后一步中创建的 XXXXXXXXXXXXXX-create-user.js 迁移,。
创建一个名为 User 的表,其中包含其迁移文件中指定的所有列。
现在我们的表已创建并保存在数据库中。 通过迁移,只需运行命令即可恢复为旧状态。
您可以使用 db:migrate:undo,这个命令将会恢复最近的迁移。
$ node_modules/.bin/sequelize db:migrate:undo
db:migrate:undo:all 命令撤消所有迁移,可以恢复到初始状态。 您还可以通过将其名称传递到 --to 选项中来恢复到特定的迁移。
$ node_modules/.bin/sequelize db:migrate:undo:all --to XXXXXXXXXXXXXX-create-posts.js
创建第一个种子
假设我们希望在默认情况下将一些数据插入到几个表中。 如果我们跟进前面的例子,我们可以考虑为 User 表创建演示用户。
要管理所有数据迁移,您可以使用 seeders。 种子文件是数据的一些变化,可用于使用样本数据或测试数据填充数据库表。
让我们创建一个种子文件,它会将一个演示用户添加到我们的 User 表中。
$ node_modules/.bin/sequelize seed:generate --name demo-user
这个命令将会在 seeders 文件夹中创建一个种子文件。文件名看起来像是
XXXXXXXXXXXXXX-demo-user.js,它遵循相同的 up/down 语义,如迁移文件。
现在我们应该编辑这个文件,将演示用户插入User表。
'use strict';
module.exports = {
up: (queryInterface, Sequelize) =& {
return queryInterface.bulkInsert('Users', [{
firstName: 'John',
lastName: 'Doe',
down: (queryInterface, Sequelize) =& {
return queryInterface.bulkDelete('Users', null, {});
在上一步中,你创建了一个种子文件。 但它还没有保存到数据库。 为此,我们需要运行一个简单的命令。
$ node_modules/.bin/sequelize db:seed:all
这将执行该种子文件,您将有一个演示用户插入 User 表。
seeders 执行不会存储在任何使用 SequelizeMeta 表的迁移的地方。 如果你想覆盖这个,请阅读 存储 部分
Seeders 如果使用了任何存储那么就可以被撤消。 有两个可用的命令
如果你想撤消最近的种子
node_modules/.bin/sequelize db:seed:undo
如果你想撤消所有的种子
node_modules/.bin/sequelize db:seed:undo:all
以下框架显示了一个典型的迁移文件。
module.exports = {
up: (queryInterface, Sequelize) =& {
// 转变为新状态的逻辑
down: (queryInterface, Sequelize) =& {
// 恢复更改的逻辑
传递的 queryInterface 对象可以用来修改数据库。 Sequelize 对象存储可用的数据类型,如 STRING 或 INTEGER。 函数 up 或 down 应该返回一个 Promise 。 让我们来看一个例子
module.exports = {
up: (queryInterface, Sequelize) =& {
return queryInterface.createTable('Person', {
name: Sequelize.STRING,
isBetaMember: {
type: Sequelize.BOOLEAN,
defaultValue: false,
allowNull: false
down: (queryInterface, Sequelize) =& {
return queryInterface.dropTable('Person');
.sequelizerc 文件
这是一个特殊的配置文件。 它允许您指定通常作为参数传递给CLI的各种选项。 在某些情况下,您可以使用它。
你想要覆盖到 migrations, models, seeders 或 config 文件夹的路径.
你想要重命名 config.json 成为别的名字比如 database.json
还有更多的, 让我们看一下如何使用这个文件进行自定义配置。
对于初学者,可以在项目的根目录中创建一个空文件。
$ touch .sequelizerc
现在可以使用示例配置。
const path = require('path');
module.exports = {
'config': path.resolve('config', 'database.json'),
'models-path': path.resolve('db', 'models'),
'seeders-path': path.resolve('db', 'seeders'),
'migrations-path': path.resolve('db', 'migrations')
通过这个配置你告诉CLI:
使用 config/database.json 文件来配置设置
使用 db/models 作为模型文件夹
使用 db/seeders 作为种子文件夹
使用 db/migrations 作为迁移文件夹
配置文件是默认的一个名为 config.json 的JSON文件。 但有时你想执行一些代码或访问环境变量,这在JSON文件中是不可能的。
Sequelize CLI可以从“JSON”和“JS”文件中读取。 这可以用.sequelizerc文件设置。 让我们来看一下
首先,您需要在项目的根文件夹中创建一个 .sequelizerc 文件。 该文件应该覆盖 JS 文件的配置路径。 推荐这个
const path = require('path');
module.exports = {
'config': path.resolve('config', 'config.js')
现在,Sequelize CLI将加载 config/config.js 以获取配置选项。 由于这是一个JS文件,您可以执行任何代码并导出最终的动态配置文件。
一个 config/config.js 文件的例子
const fs = require('fs');
module.exports = {
development: {
username: 'database_dev',
password: 'database_dev',
database: 'database_dev',
host: '127.0.0.1',
dialect: 'mysql'
username: 'database_test',
password: null,
database: 'database_test',
host: '127.0.0.1',
dialect: 'mysql'
production: {
username: process.env.DB_USERNAME,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
host: process.env.DB_HOSTNAME,
dialect: 'mysql',
dialectOptions: {
ca: fs.readFileSync(__dirname + '/mysql-ca-master.crt')
使用环境变量
使用CLI,您可以直接访问 config/config.js 内的环境变量。 您可以使用 .sequelizerc 来告诉CLI使用 config/config.js 进行配置。 这在上一节中有所解释。
然后你可以使用正确的环境变量来暴露文件。
module.exports = {
development: {
username: 'database_dev',
password: 'database_dev',
database: 'database_dev',
host: '127.0.0.1',
dialect: 'mysql'
username: process.env.CI_DB_USERNAME,
password: process.env.CI_DB_PASSWORD,
database: process.env.CI_DB_NAME,
host: '127.0.0.1',
dialect: 'mysql'
production: {
username: process.env.PROD_DB_USERNAME,
password: process.env.PROD_DB_PASSWORD,
database: process.env.PROD_DB_NAME,
host: process.env.PROD_DB_HOSTNAME,
dialect: 'mysql'
指定方言选项
有时你想指定一个 dialectOption,如果它是一个通用配置,你可以将其添加到 config/config.json 中。 有时你想执行一些代码来获取 dialectOptions,你应该为这些情况使用动态配置文件。
"production": {
"dialect":"mysql",
"dialectOptions": {
"bigNumberStrings": true
有关在生产环境中使用CLI和迁移设置的一些提示。
1) 使用环境变量进行配置设置。 这是通过动态配置更好地实现的。 样品生产安全配置可能看起来像
const fs = require('fs');
module.exports = {
development: {
username: 'database_dev',
password: 'database_dev',
database: 'database_dev',
host: '127.0.0.1',
dialect: 'mysql'
username: 'database_test',
password: null,
database: 'database_test',
host: '127.0.0.1',
dialect: 'mysql'
production: {
username: process.env.DB_USERNAME,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
host: process.env.DB_HOSTNAME,
dialect: 'mysql',
dialectOptions: {
ca: fs.readFileSync(__dirname + '/mysql-ca-master.crt')
我们的目标是为各种数据库秘密使用环境变量,而不是意外检查它们来源控制。
可以使用三种类型的存储:sequelize,json和none。
sequelize : 将迁移和种子存储在 sequelize 数据库的表中
json : 将迁移和种子存储在json文件上
none : 不存储任何迁移/种子
默认情况下,CLI 将在您的数据库中创建一个名为 SequelizeMeta 的表,其中包含每个执行迁移的条目。 要更改此行为,可以在配置文件中添加三个选项。 使用 migrationStorage 可以选择要用于迁移的存储类型。 如果选择 json,可以使用 migrationStoragePath 指定文件的路径,或者 CLI 将写入 sequelize-meta.json 文件。 如果要将数据保存在数据库中,请使用 sequelize,但是要使用其他表格,可以使用 migrationStorageTableName.
"development": {
"username": "root",
"password": null,
"database": "database_development",
"host": "127.0.0.1",
"dialect": "mysql",
// 使用不同的存储类型. Default: sequelize
"migrationStorage": "json",
// 使用不同的文件名. Default: sequelize-meta.json
"migrationStoragePath": "sequelizeMeta.json",
// 使用不同的表名. Default: SequelizeMeta
"migrationStorageTableName": "sequelize_meta"
注意: 不推荐使用 none 存储作为迁移存储。 如果您决定使用它,请注意将会没有任何移动记录或没有运行的记录.
默认情况下,CLI 不会保存任何被执行的种子。 如果您选择更改此行为(!),则可以在配置文件中使用 seederStorage 来更改存储类型。 如果选择 json,可以使用 seederStoragePath 指定文件的路径,或者 CLI 将写入文件 sequelize-data.json。 如果要将数据保存在数据库中,请使用 sequelize,您可以使用 seederStorageTableName 指定表名,否则将默认为SequelizeData。
"development": {
"username": "root",
"password": null,
"database": "database_development",
"host": "127.0.0.1",
"dialect": "mysql",
// 使用不同的存储空间. Default: none
"seederStorage": "json",
// 使用不同的文件名. Default: sequelize-data.json
"seederStoragePath": "sequelizeData.json",
// 使用不同的表名 Default: SequelizeData
"seederStorageTableName": "sequelize_data"
配置连接字符串
作为 --config 选项的替代方法,可以使用定义数据库的配置文件,您可以使用 --url 选项传递连接字符串。 例如:
$ node_modules/.bin/sequelize db:migrate --url 'mysql://root:password@/database_name'
通过SSL连接
确保ssl在 dialectOptions 和基本配置中指定。
"production": {
"dialect":"postgres",
"ssl": true,
"dialectOptions": {
"ssl": true
程序化使用
Sequelize 有一个 ,用于以编程方式处理迁移任务的执行和记录。
使用 queryInterface 对象描述之前,您可以更改数据库模式。 查看完整的公共方法列表,它支持
如果这篇文章对您有帮助, 感谢 下方点赞 或 Star
支持, 谢谢.
0 收藏&&|&&0
你可能感兴趣的文章
3 收藏,155
3 收藏,324
3 收藏,910
本作品采用 署名-非商业性使用-禁止演绎 4.0 国际许可协议 进行许可
分享到微博?
我要该,理由是:Growth: 全栈增长工程师指南
(Follow Me: 、、)
购买纸质版 Growth:《全栈应用开发:精益实践》
Warning: 请与
阅读过程中遇到语法错误、拼写错误、技术错误等等,不妨来个Pull Request,这样可以帮助到其他阅读这本电子书的童鞋。
我的其他电子书:
微信公众号
Growth: 全栈增长工程师指南
这是一本不止于全栈工程师的学习手册,它也包含了如何成为一个 Growth Hacker 的知识。
全栈工程师是未来
谨以此文献给每一个为成为优秀全栈工程师奋斗的人。
技术在过去的几十年里进步很快,也将在未来的几十年里发展得更快。今天技术的门槛下降得越来越快,原本需要一个团队做出来的 Web 应用,现在只需要一两个人就可以了。
同时,由于公司组织结构的变迁,以及到变化的适应度,也决定了赋予每个人的职责将会越来越多。尽管我们看到工厂化生产带来的优势,但是我们也看到了精益思想带来的变革。正是这种变革让越来越多的专家走向全栈,让组织内部有更好的交流。
你还将看到专家和全栈的两种不同的学习模式,以及全栈工程师的未来。
技术的革新史
从开始的 CGI 到 MVC 模式,再到前后端分离的架构模式,都在不断地降低技术的门槛。而这些门槛的降低,已经足以让一两个人来完成大部分的工作了。
二十年前的网站以静态的形式出现,这样的网站并不需要太多的人去维护、管理。接着,人们发明了 CGI (通用网关接口,英语:Common Gateway Interface)来实现动态的网站。下图是一个早期网站的架构图:
CGI 网站架构
当时这种网站的URL类似于:
https:///cgi-bin/getblog
(PS:这个链接是为了讲解而存在的,并没有真实存在。)
用户访问上面的网页的时候就会访问,cgi-bin 的路径下对应的 getblog 脚本。你可以用 Shell 返回这个网页:
echo Content-type: text/plain
echo hello,world
Blabla,各种代码混乱地夹杂在一起。不得不说一句:这样的代码在2012年,我也看了有一些。简单地来说,这个时代的代码结构就是这样的:
CGI脚本文件
这简直就是一场恶梦。不过,在今天好似那些 PHP 新手也是这样写代码的。
好了,这时候我们就可以讨论讨论 MVC 模式了。
我有理由相信 Martin Fowler 的《企业应用架构模式》在当时一定非常受欢迎。代码从上面的耦合状态变成了:
相似大家也已经对这样的架构很熟悉了,我们就不多解释了。如果你还不是非常了解的话,可以看看这本书后面的部分。
后台服务化与前端一致化架构
在今天看来,我们可以看到如下图所示的架构:
后台服务化与前台一致化架构
后台在不知不觉中已经被服务化了,即只提供API接口和服务。前端在这时已经尽量地和 APP 端在结合,使得他们可以保持一致。
软件开发的核心难题:沟通
软件开发在过去的几十年里都是大公司的专利,小公司根本没有足够的能力去做这样的事。在计算机发明后的几十年里,开发软件是大公司才能做得起的。一般的非技术公司无法定制自己的软件系统,只能去购买现有的软件。而随着技术成本的下降,到了今天一般的小公司也可以雇佣一两个人来做同样的事。这样的演进过程还真是有意思:
在这其中的每一个过程实质上都是为了解决沟通的问题。从瀑布到敏捷是为了解决组织内沟通的问题,从敏捷到精益不仅仅优化了组织内的沟通问题,还强化了与外部的关系。换句话说,精益结合了一部分的互联网思维。
在最开始的时候,我们预先设计好我们的功能,然后编码,在适当的时候发布我们的软件:
预先式设计的瀑布流
然而这种开发方式很难应对市场的变化——当我们花费了几年的时间开发出了一个软件,而这个软件是几年前人们才需要的。同时,由于软件开发本身的复杂度的限制,复制的系统在后期需要大量的系统集成工作。这样的集成工作可能要花费上大量的时间——几星期、几个月。
瀑布流的沟通模型
当人们意识到这个问题的时候,开始改进工作流程。出现了敏捷软件开发,这可以解释为什么产品经理会经常改需求。如果一个功能本身是没必要出现的话,那么为什么要花功夫去开发。但是如果一个功能在设计的初期就没有好好设计,那么改需求也是必然的。
现有的互联网公司的工作流程和敏捷软件开发在很多部分上是相似的,都有迭代、分析等等的过程:
敏捷软件开发
但是据我的所知:国内的多数互联网公司是不写测试的、没有 Code Review 等等。当然,这也不是一篇关于如何实践敏捷的文章。敏捷与瀑布式开发在很大的区别就是:沟通问题。传统的软件开发在调研完毕后就是分析、开发等等。而敏捷开发则会强调这个过程中的沟通问题:
敏捷软件开发的沟通模型
在整个过程中都不断地强调沟通问题,然而这时还存在一个问题:组织结构本身的问题。这样的组织结构,如下图所示:
如果市场部门/产品经理没有与研发团队坐一起来分析问题,那么问题就多了。当一个需求在实现的过程中遇到问题,到底是哪个部门的问题?
同样的如果我们的研发部门是这样子的结构:
那么在研发、上线的过程中仍然会遇到各种的沟通问题。
现在,让我们回过头来看看大公司的专家与小公司的全栈。
大公司的专家与小公司的全栈
如果你经常看一些关于全栈和专家的技术文章的时候,你就会发现不同的人在强调不同的方向。大公司的文章喜欢强调成为某个领域的专家,小公司喜欢小而美的团队——全栈工程师。
如我们所见的:大公司和小公司都在解决不同类型的问题。大公司要解决性能问题,小公司要活下去需要依赖于近乎全能的人。并且,大公司和小公司都在加班。如果从这种意义上来说,我们可以发现其实大公司是在剥削劳动力。
我们所见到的那些关于技术人员应该成为专家的文章,多数是已经成为某个技术领域里的专家写的文章。并且我们可以发现很有意思的一点是:他们都是管理者。管理者出于招聘的动机,因此更需要细分领域的专家来帮助他们解决问题。
相似的,我们所看到的那些关于成为全栈工程师的文章,多数是初创公司的 CTO 写的。而这些初创公司的 CTO 也多数是全栈工程师,他们需要招聘全栈工程师来帮助他们解决问题。
两种不同的学习模型
而不知你是否也注意到一点:专家们也在强调“一专多长”。因为单纯依靠于一个领域的技术而存在的专家已经很少了,技术专家们不得不依据于公司的需求去开拓不同的领域。毕竟“公司是指全部资本由股东出资构成,以营利为目的而依法设立的一种企业组织形式;”,管理人们假设技术本身是相通的,既然你在技术领域里有相当高的长板,那么进入一个新的技术也不是一件难事。
作为一个技术人员,我们是这个领域中的某个子领域专家。而作为这样一个专家,我们要扩展向另外一个领域的学习也不是一件很难的事。借鉴于我们先前的学习经验,我们可以很快的掌握这个新子域的知识。如我们所见,我们可以很快地补齐图中的短板:
在近来的探索中发现有一点非常有意思:如果依赖于20/80法则的话,那么成为专家和全栈的学习时间是相当的。在最开始的时候,我们要在我们的全栈工程和专家都在某个技术领域达到80分的水平。
那么专家,还需要80%的时间去深入这个技术领域。而全栈工程师,则可以依赖于这80%的时候去开拓四个新的领域:
全栈与专家学习时间
尽管理论上是如此,但是专家存在跨领域的学习障碍——套用现有模式。而全栈也存在学习障碍——如何成为专家,但是懂得如何学习新的领域。
解决问题的思路:不同的方式
有意思的是——成为专家还是成为全栈,取决于人的天性,这也是两种不同的性格决定的。成为管理者还是技术人员看上去就像一种简单的划分,而在技术人员里成为专家还是全栈就是另外一种划分。这取决于人们对于一个问题的思考方式:这件事情是借由外部来解决,还是由内部解决。下面这张图刚好可以表达我的想法:
内向与外向思维
而这种思维依据于不同的事情可能会发生一些差异,但是总体上来说是相似的。当遇到一个需要创轮子的问题时,我们就会看到两种不同的方式。
对于全栈工程师来说,他们喜欢依赖于外部的思维,用于产生颠覆式思维。如 AngularJS 这样的框架便是例子,前端结合后端开发语言 Java 的思维而产生。而专家则依赖于内部的条件,创造出不一样的适应式创新。如之前流行的 Backbone 框架,适应当时的情况而产生。
全栈工程师的未来:无栈
全栈工程师本身不应该仅仅局限于前端和后台的开发,而可以尝试去开拓更广泛的领域——因为全栈本身是依赖于工程师本身的学习能力,正是这种优秀的学习能力可以让他们接触更广泛的知识。
全栈的短板
如果你也尝试过面试过全栈工程师,你会怎么去面试他们呢?把你知道的所有的不同领域的问题都拿出来问一遍。是的,这就是那些招聘全栈工程师的公司会问你的问题。
人们以为全栈工程师什么都会,这是一个明显的误区——然而要改变这个误区很难。最后,导致的结果是大家觉得全栈工程师的水平也就那样。换句来说,人们根本不知道什么是全栈工程师。在平时的工作里,你的队伍都知道你在不同领域有丰富的知识。而在那些不了解你的人的印象里,就是猜测你什么都会。
因此,这就会变成一个骂名,也是一个在目前看来很难改变的问题。在这方面只能尽可能地去了解一些通用的问题,并不能去了解所有的问题。在一次被面试全栈工程师的过程中,有一个面试官准备了几个不同语言(JavaScript、Java、Python、Ruby)的问题来问我,我只想说 Ciao —— 意大利语:你好!
除了这个问题——人们不了解什么是全栈工程师。还有一个问题,就是刚才我们说的成为专家的老大难问题。
让我毫不犹豫地选择当全栈工程师有两个原因:
这个世界充满了未解的迷,但是我只想解开我感兴趣的部分。
没有探索,哪来的真爱?你都没有探索过世界,你就说这是你最喜欢的领域。
当我第一次看到全栈工程师这个名字的时候,我发现我已然是一个全栈工程师。因为我的学习路线比较独特:
中小学,因为比赛,开始学习编程
高中,因为兴趣,开始学习操作系统内核设计以及游戏编程
大学,因为专业走向硬件的道路,因为生活拮据,用Web开发来赚钱
工作:主要工作领域便是:Java后端、前后端分离、SPA前端,业余还写写APP、游戏、写作
而在当时我对 SEO 非常感兴趣,我发现这分析和 Marketing 似乎做得还可以。然后便往 Growth Hacking 发展了:
Growth Hacking
而这就是全栈学习带来的优势,学过的东西多,学习能力就变强。学习能力往上提的同时,你就更容易进入一个新的领域。
《精益企业: 高效能组织如何规模化创新》
《企业应用架构模式》
《敏捷软件开发》
《技术的本质》
基础知识篇
在我们第一次开始写程序的时候,都是以 Hello World 开始的。或者:
printf(&hello,world&);
alert('hello,world');
过去的十几年里,试过用二十几种不同的语言,每个都是以 hello,world 作为开头。在一些特定的软件,如 Nginx,则是 It Works。
这是一个很长的故事,这个程序最早出现于1972年,由贝尔实验室成员布莱恩·柯林汉撰写的内部技术文件《A Tutorial Introduction to the Language B》之中。不久,同作者于1974年所撰写的《Programming in C: A Tutorial》,也延用这个范例;而以本文件扩编改写的《C语言程序设计》也保留了这个范例程式。工作时,我们也会使用类似于 hello,world 的 boilerplate 来完成基本的项目创建。
同时需要注意的一点是,在每个大的项目开始之前我们应该去找寻好开发环境。搭建环境是一件非常重要的事,它决定了你能不能更好地工作。毕竟环境是生产率的一部分。高效的程序员和低效程序员间的十倍差距,至少有三倍是因为环境差异。
因此在这一章里,我们将讲述几件事情:
使用怎样的操作系统
如何去选择工具
如何搭建相应操作系统上的环境
如何去学习一门语言
工具只是辅助
一个好的工具确实有助于编程,但是他只会给我们带来的是帮助。我们写出来的代码还是和我们的水平保持着一致的。
什么是好的工具,这个说法就有很多了,但是有时候我们往往沉迷于事物的表面。有些时候 Vim 会比 Visual Studio 强大,当你只需要修改的是一个配置文件的时候,简单且足够快捷——在我们还未用 VS 打开的时候,我们已经用 Vim 做完这个活了。
“好的装备确实能带来一些帮助,但事实是,你的演奏水平是由你自己的手指决定的。” – 《REWORK》
WebStorm 还是 Sublime?
作为一个 IDE 有时候忽略的因素会过多,一开始的代码由类似于 Sublime text 之类的编辑器开始会比较合适。于是我们又开始陷入 IDE 及 Editor 之战了,无聊的时候讨论一下这些东西是有点益处的。相互了解一下各自的优点,也是不错的,偶尔可以换个环境试试。
刚开始学习的时候,我们只需要普通的工具,或者我们习惯了的工具去开始我们的工作。我们要的是把主要精力放在学习的东西上,而不是工具。刚开始学习一种新的语言的时候,我们不需要去讨论哪个是最好的开发工具,如 Java,有时候可能是 Eclipse,有时候可能是 Vim,如果我们为的只是去写一个 hello,world。在 Eclipse 上浪费太多的时间是不可取的,因为他用起来的效率可不比你在键盘上敲打来得快,当你移动你的手指去动你的鼠标的时候,我想你可以用那短短的时间完成编译,运行了。
工具是为了效率
寻找工具的目的和寻找捷径是一样的,我们需要更快更有效率地完成我们的工作,换句话说,我们为了获取更多的时间用于其他的事情。而这个工具的用途是要看具体的事物的,如果我们去写一个小说、博客的时候,word 或者 web editor 会比 tex studio 来得快,不是么。我们用 TEX 来排版的时候会比我们用 WORD 排版的时候来得更快,所以这个工具是相对而论的。有时候用一个顺手的工具会好很多,但是不一定会是事半功倍的。我们应该将我们的目标专注于我们的内容,而不是我们的工具上。
我们用 Windows 自带的画图就可以完成裁剪的时候,我们就没必要运行起 GIMP 或者 Photoshop 去完成这个简单的任务。效率在某些时候的重要性,会比你选择的工具有用得多,学习的开始就是要去了解那些大众推崇的东西。
了解、熟悉你的工具
Windows 的功能很强大,只是大部分人用的是只是小小一部分。而不是一小部分,即使我们天天用着,我们也没有学习到什么新的东西。和这个就如同我们的工具一样,我们天天用着他们,如果我们只用 Word 来写写东西,那么我们可以用 Abiword 来替换他。但是明显不太可能,因为强大的工具对于我们来说有些更大的吸引力。
如果你负担得起你手上的工具的话,那么就尽可能去了解他能干什么。即使他是一些无关仅要的功能,比如 Emacs 的煮咖啡。有一本手册是最好不过的,手册在手边可以即时查阅,不过出于环保的情况下,就不是这样子的。手册没有办法即时同你的软件一样更新,电子版的更新会比你手上用的那个手册更新得更快。
Linux 下面的命令有一大堆,只是我们常用的只有一小部分——20%的命令能够完成80%的工作。如同 CISC 和 RISC 一样,我们所常用的指令会让我们忘却那些不常用的指令。而那些是最实用的,如同我们日常工作中使用的 Linux 一样,记忆过多的不实用的东西,不比把他们记在笔记上实在。我们只需要了解有那些功能,如何去用他。
语言也是一种工具
越来越多的框架和语言出现、更新得越来越快。特别是这样一个高速发展的产业,每天都在涌现新的名词。如同我们选择语言一样,选择合适的有时候会比选得顺手的来得重要。然而,这个可以不断地被推翻。
当我们熟悉用 Python、Ruby、PHP 等去构建一个网站的时候,JavaScript 用来做网站后台,这怎么可能——于是 Node.js 火了。选择工具本身是一件很有趣的事,因为有着越来越多的可能性。
过去 PHP 是主流的开发,不过现在也是,PHP 为 WEB 而生。有一天 Ruby on Rails 出现了,一切就变了,变得高效,变得更 Powerful。MVC 一直很不错,不是么?于是越来越多的框架出现了,如 Django,Laravel 等等。不同的语言有着不同的框架,JavaScript 上也有着合适的框架,如 AngularJS。不同语言的使用者们用着他们合适的工具,因为学习新的东西,对于多数的人来说就是一种新的挑战。在学面向对象语言的时候,人们很容易把程序写成过程式的。
没有合适的工具,要么创造一个,要么选择一个合适的。
学习 Django 的时候习惯了有一个后台,于是开始使用 Laravel 的时候,寻找 Administartor。需要编译的时候习惯用 IDE,不需要的时候用 Editor,只是因为有效率,嵌入式的时候 IDE 会有效率一点。
以前不知道 WebStorm 的时候,习惯用 DW 来格式化 HTML,Aptana 来格式化 JavaScript。
以前,习惯用 WordPress 来写博客,因为可以有移动客户端,使用电脑时就不喜欢打开浏览器去写。
提高效率的工具
在提高效率的 N 种方法里:有一个很重要的方法是使用快捷键。熟练掌握快捷键可以让我们随着自己的感觉编写程序——有时候如果我们手感不好,是不是就说明今天不适合写代码!笑~~
由于我们可能使用不同的操作系统来完成不同的工具。下面就先说说一些通用的、不限操作的工具:
快速启动软件
在我还不和道有这样的工具的时候,我都是把图标放在下面的任务栏里:
Windows任务栏
直到有一天,我知道有这样的工具。这里不得不提到一本书《卓有成效的程序员》,在书中提到了很多提高效率的工具。使用快捷键是其中的一个,而还有一个是使用快速启动软件。于是,我在 Windows 上使用了 Launcy:
通过这个软件,我们可以在电脑上通过输入软件名,然后运行相关的软件。我们不再需要点击某个菜单,再从菜单里选中某个软件打开。
尽管在上一篇中,我们说过 IDE 和编辑器没有什么好争论的。但是如果是从头开始搭建环境的话,IDE 是最好的——编辑器还需要安装相应的插件。所以,这也就是为什么面试的时候会用编辑器的原因。
IDE 的全称是集成开发环境,顾名思义即它集成了你需要用到的一些工具。而如果是编辑器的话,你需要自己去找寻合适的工具来做这件事。不过,这也意味着使用编辑器会有更多的自由度。如果你没有足够的时间去打造自己的开发环境就使用 IDE 吧。
一般来说,他们都应该有下面的一些要素:
shortcut(快捷键)
Code HighLight(代码高亮)
Auto Complete(自动补全)
Syntax Check(语法检查)
而如果是编辑器的话,就需要自己去找寻这些相应的插件。
IDE 一般是针对特定语言才产生的,并且优化更好。而,编辑器则需要自己去搭配。这也意味着如果你需要在多个语言上工作时,并且喜欢折腾,你可以考虑使用编辑器。
DEBUG 工具
不得不提及的是在有些 IDE 自带了 Debug 工具,这点可能使得使用 IDE 更有优势。在简单的项目里,我们可能不需要这样的 Debug 工具。因为我们对我们的代码库比较熟悉,一个简单的问题一眼就知道是哪里的问题。而对于那些复杂的项目来说,可能就没有那么简单了。特别是当你来到一个新的大中型项目,一个简单的逻辑在实现上可能要经过一系列的函数才能处理完。
这时候我们就需要 Debug 工具——对于前端开发来说,我们可能使用 Chrome 的 Dev Tools。但是对于后端来说,我们就需要使用别的工具。如下图所示的是 Intellij Idea 的 Debug 界面:
Intellij Idea Debug
在 Debug 的过程中,我们可以根据代码的执行流程一步步向下执行。这也意味着,当出现 Bug 的时候我们可以更容易找到 Bug。这就是为什么他叫 Debug 工具的原因了。
终端或命令提示符
在开始写代码的时候,使用 GUI 可能是难以戒掉的一个习惯。但是当你习惯了使用终端之后,或者说使用终端的工具,你会发现这是另外一片天空。对于 GUI 应用上同样的菜单来说,在终端上也会有同样的工具——只是你觉得记住更多的命令。而且不同的工具对于同一实现可能会不同的规范,而 GUI 应用则会有一致的风格。不过,总的来说使用终端是一个很有益的习惯——从速度、便捷性。忘了提到一点,当你使用 Linux 服务器的时候,你不得不使用终端。
Linux 终端截图
使用终端的优点在于我们可以摆脱鼠标的操作——这可以让我们更容易集中精力于完成任务。而这也是两种不同的选择,便捷还是更快。虽是如此,但是这也意味着学习 Linux 会越来越轻松。
Linux 与 Windows 的学习曲线
虽然这是以 Linux 和 Windows 作了两个不同的对比,但是两个系统在终端工具上的差距是很大的。Linux 自身的哲学鼓励使用命令行来完成任务,这也意味着在 Linux 上会有更多的工具可以在命令行下使用。虽然 Windows 上也可以——如使用 CygWin 来完成,但是这看上去并不是那么让人满意!
虽然包管理不仅仅存在于操作系统中,还存在着语言的包管理工具。在操作系统中安装软件,最方便的东西莫过于包管理了。引自 OpenSUSE 官网的说明及图片:
Linux 发行版无非就是一堆软件包 (package) 形式的应用程序加上整体地管理这些应用程序的工具。通常这些 Linux 发行版,包括 OpenSUSE,都是由成千上万不同的软件包构成的。
软件包: 软件包不止是一个文件,内含构成软件的所有文件,包括程序本身、共享库、开发包以及使用说明等。
元数据 (metadata) 包含于软件包之中,包含软件正常运行所需要的一些信息。软件包安装之后,其元数据就存储于本地的软件包数据库之中,以用于软件包检索。
依赖关系 (dependencies) 是软件包管理的一个重要方面。实际上每个软件包都会涉及到其他的软件包,软件包里程序的运行需要有一个可执行的环境(要求有其他的程序、库等),软件包依赖关系正是用来描述这种关系的。
我们经常会使用各式各样的包管理工具,来加速我们地日常使用。而不是 Google 某个软件,然后下载,接着安装。
由于我近期工作在 Mac OS X 上,所以先以 Mac OS X 为例。
包管理工具,官方称之为 The missing package manager for OS X。
brew-cask 允许你使用命令行安装 OS X 应用。
iTerm2 是最常用的终端应用,是 Terminal 应用的替代品。
Zsh 是一款功能强大终端(shell)软件,既可以作为一个交互式终端,也可以作为一个脚本解释器。它在兼容 Bash 的同时 (默认不兼容,除非设置成 emulate sh) 还有提供了很多改进,例如:
更好的自动补全
更好的文件名展开(通配符展开)
更好的数组处理
可定制性高
Oh My Zsh 同时提供一套插件和工具,可以简化命令行操作。
强大的文件编辑器。
MacDown 是 Markdown 编辑器。
CheatSheet 能够显示当前程序的快捷键列表,默认的快捷键是长按?。
SourceTree 是 Atlassian 公司出品的一款优秀的 Git 图形化客户端。
Mac 用户不用鼠标键盘的必备神器,配合大量 Workflows,习惯之后可以大大减少操作时间。
上手简单,调教成本在后期自定义 Workflows,不过有大量雷锋使用者提供的现成扩展,访问这里挑选喜欢的,并可以极其简单地根据自己的需要修改。
Vimium 是一个 Google Chrome 扩展,让你可以纯键盘操作 Chrome。
相关参考:
Chocolatey 是一个软件包管理工具,类似于 Ubuntu 下面的 apt-get,不过是运行在 Windows 环境下面。
Wox 是一个高效的快速启动器工具,通过快捷键呼出,然后输入关键字来搜索程序进行快速启动,或者搜索本地硬盘的文件,打开百度、Google 进行搜索,甚至是通过一些插件的功能实现单词翻译、关闭屏幕、查询剪贴板历史、查询编程文档、查询天气等更多功能。它最大的特点是可以支持中文拼音的模糊匹配。
Windows PowerShell 是微软公司为 Windows 环境所开发的壳程序(shell)及脚本语言技术,采用的是命令行界面。这项全新的技术提供了丰富的控制与自动化的系统管理能力。
cmder 把 conemu,msysgit 和 clink 打包在一起,让你无需配置就能使用一个真正干净的 Linux 终端!她甚至还附带了漂亮的 monokai 配色主题。
Total Commander 是一款应用于 Windows 平台的文件管理器 ,它包含两个并排的窗口,这种设计可以让用户方便地对不同位置的“文件或文件夹”进行操作,例如复制、移动、删除、比较等,相对 Windows 资源管理器而言方便很多,极大地提高了文件操作的效率,被广大软件爱好者亲切地简称为:TC 。
Zsh 是一款功能强大终端(shell)软件,既可以作为一个交互式终端,也可以作为一个脚本解释器。它在兼容 Bash 的同时 (默认不兼容,除非设置成 emulate sh) 还有提供了很多改进,例如:
更好的自动补全
更好的文件名展开(通配符展开)
更好的数组处理
可定制性高
Oh My Zsh 同时提供一套插件和工具,可以简化命令行操作。
ReText 是一个使用 Markdown 语法和 reStructuredText (reST) 结构的文本编辑器,编辑的内容支持导出到 PDF、ODT 和 HTML 以及纯文本,支持即时预览、网页生成以及 HTML 语法高亮、全屏模式,可导出文件到 Google Docs 等。
Launchy 是一款免费开源的协助您摒弃 Windows “运行”的 Dock 式替代工具,既方便又实用,自带多款皮肤,作为美化工具也未尝不可。
环境搭建完毕!现在,就让我们来看看如何学好一门语言!
学好一门语言的艺术
一次语言学习体验
在我们开始学习一门语言或者技术的时候,我们可能会从一门 “hello,world” 开始。
好了,现在我是 Scala 语言的初学者,接着我用搜索引擎去搜索『Scala』来看看『Scala』是什么鬼:
Scala 是一门类 Java 的编程语言,它结合了面向对象编程和函数式编程。
接着又开始看『Scala ‘hello,world’』,然后找到了这样的一个示例:
object HelloWorld {
def main(args: Array[String]): Unit = {
println(&Hello, world!&)
GET 到了5%的知识。
看上去这门语言相比于 Java 语言来说还行。然后我找到了一本名为『Scala 指南』的电子书,有这样的一本目录:
表达式和值
函数是一等公民
按名称传递参数
看上去还行, 又 GET 到了5%的知识点。接着,依照上面的代码和搭建指南在自己的电脑上安装了 Scala 的环境:
brew install scala
Windows 用户可以用:
choco install scala
然后开始写一个又一个的 Demo,感觉自己 GET 到了很多特别的知识点。
到了第二天忘了!
接着,你又重新把昨天的知识过了一遍,还是没有多大的作用。突然间,你听到别人在讨论什么是这个世界上最好的语言——你开始加入讨论了。
于是,你说出了 Scala 这门语言可以:
支持高阶函数。lambda,闭包…
支持偏函数。 match..
mixin,依赖注入..
虽然隔壁的 Python 小哥赢得了这次辩论,然而你发现你又回想起了 Scala 的很多特性。
最流行的语言
你发现隔壁的 Python 小哥之所以赢得了这场辩论是因为他把 Python 语言用到了各个地方——机器学习、人工智能、硬件、Web开发、移动应用等。而你还没有用 Scala 写过一个真正的应用。
让我想想我能做什么?我有一个博客。对,我有一个博客,可以用 Scala 把我的博客重写一遍:
先找一 Scala 的 Web 框架,Play 看上去很不错,就这个了。这是一个 MVC 框架,原来用的 Express 也是一个 MVC 框架。Router 写这里,Controller 类似这个,就是这样的。
既然已经有 PyJS,也会有 Scala-js,前端就用这个了。
好了,博客重写了一遍了。
感觉还挺不错的,我决定向隔壁的 Java 小弟推销这门语言,以解救他于火海之中。
『让我想想我有什么杀手锏?』
『这里的知识好像还缺了一点,这个是什么?』
好了,你已经 GET 到了90%了。如下图所示:
希望你能从这张图上 GET 到很多点。
输出是最好的输入
上面那张图『学习金字塔』就是在说明——输出是最好的输入。
如果你不试着去写点博客、整理资料、准备分享,那么你可能并没有意识到你缺少了多少东西。虽然你已经有了很多的实践,然并卵。
因为你一直在完成功能、完成工作,你总会有意、无意地漏掉一些知识,而你也没有意识到这些知识的重要性。
Output is Input
从我有限的(500+)博客写作经验里,我发现多数时候我需要更多地参考资料才能更好也向人们展示这个过程。为了输出我们需要更多的输入,进而加速这个过程。
而如果是写书的时候则是一个更高水平的学习,你需要发现别人在他们的书中欠缺的一些知识点。并且你还要展示一些在别的书中没有,而这本书会展现这个点的知识,这意味着你需要挖掘得更深。
所以,如果下次有人问你如何学一门新语言、技术,那么答案就是写一本书。
如何应用一门新的技术
对于多数人来说,写书不是一件容易的事,而应用新的技术则是一件迫在眉睫的事。
通常来说,技术出自于对现有的技术的改进。这就意味着,在掌握现有技术的情况下,我们只需要做一些小小的改动就更可以实现技术升级。
而学习一门新的技术的最好实践就是用这门技术对现有的系统行重写。
第一个系统(v1): Spring MVC + Bootstrap + jQuery
那么在那个合适的年代里, 我们需要单页面应用,就使用了Backbone。然后,我们就可以用 Mustache + HTML 来替换掉 JSP。
第二个系统(v2): Spring MVC + Backbone + Mustache
在这时我们已经实现了前后端分离了,这时候系统实现上变成了这样。
第二个系统(v2.2): RESTful
Services + Backbone + Mustache
第二个系统(v2.2): RESTful Services + AngularJS 1.x
Spring 只是一个 RESTful 服务,我们还需要一些问题,比如 DOM 的渲染速度太慢了。
第三个系统(v3): RESTful
Services + React
系统就是这样一步步演进过来的。
尽管在最后系统的架构已经不是当初的架构,而系统本身的业务逻辑变化并没有发生太大的变化。
特别是对于如博客这一类的系统来说,他的一些技术实现已经趋于稳定,而且是你经常使用的东西。所以,下次试试用新的技术的时候,可以先从对你的博客的重写开始。
Web 编程基础
从浏览器到服务器
如果你的操作系统带有 cURL 这个软件(在 GNU/Linux、Mac OS 都自带这个工具,Windows 用户可以从下载到),那么我们可以直接用下面的命令来看这看这个过程(-v 参数可以显示一次 http 通信的整个过程):
我们就会看到下面的响应过程:
* Rebuilt URL to: /
Trying 54.69.23.11...
* Connected to
(54.69.23.11) port 443 (#0)
* TLS 1.2 connection using TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
* Server certificate:
* Server certificate: COMODO RSA Domain Validation Secure Server CA
* Server certificate: COMODO RSA Certification Authority
* Server certificate: AddTrust External CA Root
& GET / HTTP/1.1
& User-Agent: curl/7.43.0
& Accept: */*
& HTTP/1.1 403 Forbidden
& Server: phodal/0.19.4
& Date: Tue, 13 Oct :13 GMT
& Content-Type: text/html; charset=utf-8
& Content-Length: 170
& Connection: keep-alive
&head&&title&403 Forbidden&/title&&/head&
&body bgcolor=&white&&
&center&&h1&403 Forbidden&/h1&&/center&
&hr&&center&phodal/0.19.4&/center&
* Connection #0 to host
left intact
我们尝试用 cURL 去访问我的网站,会根据访问的域名找出其 IP,通常这个映射关系是来源于 ISP 缓存 DNS(英语:Domain Name System)服务器[^DNSServer]。
以“*”开始的前8行是一些连接相关的信息,称为响应首部。我们向域名 发出了请求,接着 DNS服务器告诉了我们网站服务器的 IP,即54.69.23.11。出于安全考虑,在这里我们的示例,我们是以 HTTPS 协议为例,所以在这里连接的端口是 443。因为使用的是 HTTPS 协议,所以在这里会试图去获取服务器证书,接着获取到了域名相关的证书信息。
随后以“&”开始的内容,便是向Web服务器发送请求。Host 即是我们要访问的主机的域名,GET / 则代表着我们要访问的是根目录,如果我们要访问 页面在这里,便是 GET 资源文件 /about。紧随其后的是 HTTP 的版本号(HTTP/1.1)。User-Agent 通常指向的是使用者行为的软件,通常会加上硬件平台、系统软件、应用软件和用户个人偏好等等的一些信息。Accept 则指的是告知服务器发送何种媒体类型。
这个过程,大致如下图所示:
DNS 到服务器的过程
在图中,我们会发现解析 DNS 的时候,我们需要先本地 DNS 服务器查询。如果没有的话,再向根域名服务器查询——这个域名由哪个服务器来解析。直至最后拿到真正的服务器IP才能获取页面。
当我们拿到相应的 HTML、JS、CSS 后,我们就开始渲染这个页面了。
说到这里,我们不得不说说 HTTP 协议——超文本传输协议。它也是一个基于文本的传输协议,这就是为什么你在上面看到的都是文本的传输过程。
从 HTML 到页面显示
而浏览器接收到文本的时候,就要开始着手将 HTML 变成屏幕上显示的内容。下图是 Chrome 渲染页面的一个时间线:
Chrome 渲染的 Timeline
及其整个渲染过程如下图所示:
Render HTML
(PS: 需要注意的是这里用的是 WebKit 内核的渲染过程,即 Chrome 和 Safari 等浏览器所使用的内核。)
从上面的两图可以看出来第一步都 Parse HTML,而 Parse HTML 实质上就是将其将解析为 DOM Tree。与此同时,CSS 解析器会解析 CSS 会产生 CSS 规则树。
随后会根据生成的 DOM 树和 CSS 规则树来构建 Render Tree,接着生成 Render Tree的布局,最后就是绘制出 Render Tree。
详细的内容还得参见相关的书籍~~。
相关内容:
让我们先从身边的语言下手,也就是现在无处不在的 HTML+Javascript+CSS。
之所以从 HTML 开始,是因为我们不需要配置一个复杂的开发环境,也许你还不知道开发环境是什么东西,不过这也没关系,毕竟这些知识需要慢慢的接触才能有所了解,尤其是对于普通的业余爱好者来说,当然,对于专业选手而言自然不是问题。HTML 是 Web 的核心语言,也算是比较基础的语言。
hello,world
hello,world 是一个传统,所以在这里也遵循这个有趣的传统,我们所要做的事情其实很简单,虽然也有一点点 hack 的感觉。——让我们先来新建一个文件并命名为“helloworld.html”。
(PS:大部分人应该都是在 Windows 环境下工作的,所以你需要新建一个文本,然后重命名,或者你需要一个编辑器,在这里我们推荐用 Sublime Text 。破解不破解,注册不注册都不会对你的使用有太多的影响。)
hello,world
保存为-&“helloworld.html”,
双击打开这个文件。 正常情况下都应该是用你的默认浏览器打开。只要是一个正常工作的现代浏览器,都应该可以看到上面显示的是“Hello,world”。
这才是最短的 hello,world 程序,但是呢?在 Ruby 中会是这样子的
2.0.0-p353 :001 & p &hello,world&
&hello,world&
=& &hello,world&
2.0.0-p353 :002 &
等等,如果你了解过 HTML 的话,会觉得这一点都不符合语法规则,但是他工作了,没有什么比安装完 Nginx 后看到 It works! 更让人激动了。
遗憾的是,它可能无法在所有的浏览器上工作,所以我们需要去调试其中的 bug。
调试 hello,world
我们会发现我们的代码在浏览器中变成了下面的代码,如果你和我一样用的是 Chrome,那么你可以右键浏览器中的空白区域,点击审查元素,就会看到下面的代码。
&head&&/head&
&body&hello,world&/body&
这个才是真正能在大部分浏览器上工作的代码,所以复制它到编辑器里吧。
说说 hello,world
我很不喜欢其中的&*&&/*&,但是我也没有找到别的方法来代替它们,所以这是一个设计得当的语言。甚至大部分人都说这算不上是一门真正的语言,不过 HTML 的原义是
超文本标记语言
所以我们可以发现其中的关键词是标记——markup,也就是说 html 是一个 markup,head 是一个 markup,body 也是一个 markup。
然而,我们真正工作的代码是在 body 里面,至于为什么是在这里面,这个问题就太复杂了。打个比方来说:
我们所使用的汉语是人类用智慧创造的,我们所正在学的这门语言同样也是人类创造的。
我们在自己的语言里遵循着 桌子是桌子,凳子是凳子 的原则,很少有人会问为什么。
所以我们也可以把计算机语言与现实世界里用于交流沟通的语言划上一个等号。而我们所要学习的语言,并不是我们最熟悉的汉语语言,所以我们便觉得这些很复杂,但是如果我们试着用汉语替换掉上面的代码的话
&头&&结束头&
&身体&你好,世界&结束身体&
&结束语言&
这看上去很奇怪,只是因为是直译过去的原因,也许你会觉得这样会好理解一点,但是输入上可就一点儿也不方便,因为这键盘本身就不适合我们去输入汉字,同时也意味着可能你输入的会有问题。
让我们把上面的代码代替掉原来的代码然后保存,打开浏览器会看到下面的结果
&语言& &头&&结束头& &身体&你好,世界&结束身体& &结束语言&
更不幸的结果可能是
&璇?█& &澶?&&缁撴潫澶?& &韬?綋&浣犲ソ锛屼笘鐣?&缁撴潫韬?綋& &缁撴潫璇?█&
这是一个编码问题,对中文支持不友好。
我们把上面的代码改为和标记语言一样的结构
&身体&你好,世界&/身体&
于是我们看到的结果便是
&语言& &头& &身体&你好,世界
被 Chrome 浏览器解析成什么样了?
&html&&head&&/head&&body&&语言&
&头&&!--头--&
&身体&你好,世界&!--身体--&
&!--语言--&
&/body&&/html&
以&!--开头,--&结尾的是注释,写给人看的代码,不是给机器看的,所以机器不会去理解这些代码。
但是当我们把代码改成
&whatwewanttosay&你好世界&/whatwewanttosay&
浏览器上面显示的内容就变成了
或许你会觉得很神奇,但是这一点儿也不神奇,虽然我们的中文语法也遵循着标记语言的标准,但是我们的浏览器不支持中文标记。
浏览器对中文支持不友好。
浏览器对英文支持友好。
刚开始的时候不要对中文编程有太多的想法,这是很不现实的:
现有的系统都是基于英语语言环境构建的,对中文支持不是很友好。
中文输入的速度在某种程度上来说没有英语快。
我们离开话题已经很远了,但是这里说的都是针对于那些不满于英语的人来说的,只有当我们可以从头构建一个中文系统的时候才是可行的,而这些就要将 CPU、软件、硬件都包含在内,甚至我们还需要考虑重新设计 CPU 的结构,在某种程度上来说会有些不现实。或许,需要一代又一代人的努力。忘记那些吧,师夷之长技以制夷。
其它 HTML 标记
添加一个标题,
&title&标题&/title&
&body&hello,world&/body&
我们便可以在浏览器的最上方看到“标题”二字,就像我们常用的淘宝网,也包含了上面的东西,只是还包括了更多的东西,所以你也可以看懂那些我们可以看到的淘宝的标题。
&title&标题&/title&
hello,world
&h1&大标题&/h1&
&h2&次标题&/h2&
&h3&...&/h3&
&li&列表1&/li&
&li&列表2&/li&
更多的东西可以在一些书籍上看到,这边所要说的只是一次简单的语言入门,其它的东西都和这些类似。
我们简单地上手了一门不算是语言的语言,浏览器简化了这其中的大部分过程,虽然没有 C 和其他语言来得有专业感,但是我们试着去开始写代码了。我们可能在未来的某一篇中可能会看到类似的语言,诸如 Python,我们所要做的就是
$ python file.py
=&hello,world
然后在终端上返回结果。只是因为在我看来学会 HTML 是有意义的,简单的上手,然后再慢慢地深入,如果一开始我们就去理解指针,开始去理解类。我们甚至还知道程序是怎么编译运行的时候,在这个过程中又发生了什么。虽然现在我们也没能理解这其中发生了什么,但是至少展示了
中文编程语言在当前意义不大,不现实,效率不高兼容性差
语言的语法是固定的。(ps:虽然我们也可以进行扩充,我们将会在后来支持上述的中文标记。)
已经开始写代码,而不是还在配置开发环境。
随身的工具才是最好的,最常用的 code 也才是实在的。
我们还没有试着去解决“某商店里的糖一颗5块钱,小明买了3颗糖,小明一共花了多少钱”的问题。也就是说我们学会的是一个还不能解决实际问题的语言,于是我们还需要学点东西,比如 JavaScript, CSS。我们可以将 JavaScript 理解为解决问题的语言,HTML 则是前端显示,CSS 是配置文件,这样的话,我们会在那之后学会成为一个近乎专业的程序员。我们刚刚学习了一下怎么在前端显示那些代码的行为,于是我们还需要 JavaScript。
如果说 HTML 是建筑的框架,CSS 就是房子的装修。那么 JavaScript 呢,我听到的最有趣的说法是小三——还是先让我们回到代码上来吧。
下面就是我们之前说到的代码,CSS 将 Red 三个字母变成了红色。
&!DOCTYPE html&
&p id=&para& style=&color:red&&Red&/p&
&script type=&text/javascript& src=&app.js&&&/script&
var para=document.getElementById(&para&);
para.style.color=&blue&;
将字体变成了蓝色,CSS+HTML 让页面有序的工作着,但是 JavaScript 却打乱了这些秩序,有着唯恐世界不乱的精彩,也难怪被冠以小三之名了——或许终于可以理解,为什么以前人们对于 JavaScript 没有好感了——不过这里要讲的是正室,也就是 CSS,这时还没有 JavaScript。
这不是一篇专业讲述 CSS 的书籍,所以我不会去说 CSS 是怎么来的,有些东西我们既然可以很容易从其他地方知道,也就不需要花太多时间去重复。诸如重构等这些的目的之一也在于去除重复的代码,不过有些重复是不可少的,也是有必要的,而通常这些东西可能是由其他地方复制过来的。
到目前为止我们没有依赖于任何特殊的硬件或者是软件,对于我们来说我们最基本的需求就是一台电脑,或者可以是你的平板电脑,当然也可以是你的智能手机,因为他们都有个浏览器,而这些都是能用的,对于我们的 CSS 来说也不会有例外的。
CSS(Cascading Style Sheets),到今天我也没有记得他的全称,CSS 还有一个中文名字是层叠式样式表,事实上翻译成什么可能并不是我们关心的内容,我们需要关心的是他能做些什么。作为三剑客之一,它的主要目的在于可以让我们方便灵活地去控制 Web 页面的外观表现。我们可以用它做出像淘宝一样复杂的界面,也可以像我们的书本一样简单,不过如果要和我们书本一样简单的话,可能不需要用到 CSS。HTML 一开始就是依照报纸的格式而设计的,我们还可以继续用上面说到的编辑器,又或者是其他的。如果你喜欢 DreamWeaver 那也不错,不过一开始使用 IDE 可无助于我们写出良好的代码。
忘说了,CSS 也是有版本的,和 Windows,Linux 内核等等一样,但是更新可能没有那么频繁,HTML 也是有版本的,JavaScript 也是有版本的,复杂的东西不是当前考虑的内容。
对于我们的上面的 Red 示例来说,如果没有一个好的结构,那么以后可能就是这样子。
&!DOCTYPE html&
&p style=&font-size: 22color: #f00;text-align:padding-left: 20&&如果没有一个好的结构&/p&
&p style=&font-size: 44color: #3text-indent: 2padding-left: 2&&那么以后可能就是这样子。。。。&/p&
虽然我们看到的还是一样的:
于是我们就按各种书上的建议重新写了上面的代码
&!DOCTYPE html&
&title&CSS example&/title&
&style type=&text/css&&
font-size: 22px;
color: #f00;
text-align: center;
padding-left: 20px;
font-size: 44px;
color: #3ed;
text-indent: 2em;
padding-left: 2em;
&p class=&para&&如果没有一个好的结构&/p&
&p class=&para2&&那么以后可能就是这样子。。。。&/p&
总算比上面好看也好理解多了,这只是临时的用法,当文件太大的时候,正式一点的写法应该如下所示:
&!DOCTYPE html&
&title&CSS example&/title&
&style type=&text/css& href=&style.css&&&/style&
&p class=&para&&如果没有一个好的结构&/p&
&p class=&para2&&那么以后可能就是这样子。。。。&/p&
&!DOCTYPE html&
&title&CSS example&/title&
&link href=&./style.css& rel=&stylesheet& type=&text/css& /&
&p class=&para&&如果没有一个好的结构&/p&
&p class=&para2&&那么以后可能就是这样子。。。。&/p&
然后我们有一个像 app.js 一样的 style.css 放在同目录下,而他的内容便是
font-size: 22px;
color: #f00;
text-align: center;
padding-left: 20px;
font-size: 44px;
color: #3ed;
text-indent: 2em;
padding-left: 2em;
这代码和 JS 的代码有如此多的相似
var para={
font_size: '22px',
color: '#f00',
text_align: 'center',
padding_left: '20px',
而22px、20px以及#f00都是数值,因此:
var para={
font_size: 22px,
color: #f00,
text_align: center,
padding_left: 20px,
目测差距已经尽可能的小了,至于这些话题会在以后讨论到,如果要让我们的编译器更正确的工作,那么我们就需要非常多这样的符号,除非你乐意去理解:
(dotimes (i 4) (print i))
总的来说我们减少了符号的使用,但是用 lisp 便带入了更多的括号,不过这是一种简洁的表达方式,也许我们可以在其他语言中看到。
\d{2}/[A-Z][a-z][a-z]/\d{4}
上面的代码,是为了从一堆数据中找出“某日/某月/某年”。如果一开始不理解那是正则表达式,就会觉得那个很复杂。
这门语言可能是为设计师而设计的,但是设计师大部分还是不懂编程的,不过相对来说这门语言还是比其他语言简单易懂一些。
样式与目标
如下所示,就是我们的样式
font-size: 22px;
color: #f00;
text-align: center;
padding-left: 20px;
我们的目标就是
如果没有一个好的结构
所以样式和目标在这里牵手了,问题是他们是如何在一起的呢?下面就是 CSS 与 HTML 沟通的重点所在了:
我们用到的选择器叫做类选择器,也就是 class,或者说应该称之为 class 选择器更合适。与类选择器最常一起出现的是 ID 选择器,不过这个适用于比较高级的场合,诸如用 JS 控制 DOM 的时候就需要用到 ID 选择器。而基本的选择器就是如下面的例子:
color: #f0f;
将代码添加到 style.css 的最下面会发现“如果没有一个好的结构”变成了粉红色,当然我们还会有这样的写法
color: #f0f;
为了产生上面的特殊的样式,虽然不好看,但是我们终于理解什么叫层叠样式了,下面的代码的权重比上面高,也因此有更高的优先规则。
而通常我们可以通过一个
text-align: left;
这样的元素选择器来给予所有的 p 元素一个左对齐。
还有复杂一点的复合型选择器,下面的是 HTML 文件
&!DOCTYPE html&
&title&CSS example&/title&
&link href=&./style.css& rel=&stylesheet& type=&text/css& /&
&p class=&para&&如果没有一个好的结构&/p&
&div id=&content&&
&p class=&para2&&那么以后可能就是这样子。。。。&/p&
还有 CSS 文件
font-size: 22px;
color: #f00;
text-align: center;
padding-left: 20px;
font-size: 44px;
color: #3ed;
text-indent: 2em;
padding-left: 2em;
color: #f0f;
div#content p {
font-size: 22px;
更有趣的 CSS
一个包含了 para2 以及 para_bg 的例子
&div id=&content&&
&p class=&para2 para_bg&&那么以后可能就是这样子。。。。&/p&
我们只是添加了一个黑色的背景
background-color: #000;
重新改变后的网页变得比原来有趣了很多,所谓的继承与合并就是上面的例子。
我们还可以用 CSS3 做出更多有趣的效果,而这些并不在我们的讨论范围里面,因为我们讨论的是 be a geek。
或许我们写的代码都是那么的简单,从 HTML 到 JavaScript,还有现在的 CSS,只是总有一些核心的东西,而不是去考虑那些基础语法,基础的东西我们可以在实践的过程中一一发现。但是我们可能发现不了,或者在平时的使用中考虑不到一些有趣的用法或者说特殊的用法,这时候可以通过观察一些精致设计的代码中学习到。复杂的东西可以变得很简单,简单的东西也可以变得很复杂。
JavaScript
JavaScript 现在已经无处不在了,也许你正打开的某个网站,他便可能是 node.js+json+javascript+mustache.js 完成的,虽然你还没理解上面那些是什么,也正是因为你不理解才需要去学习更多的东西。但是你只要知道 JavaScript 已经无处不在了,它可能就在你手机上的某个 app 里,就在你浏览的网页里,就运行在你 IDE 中的某个进程里。
hello,world
这里我们还需要有一个 helloworld.html,JavaScript 是专为网页交互而设计的脚本语言,所以我们一点点来开始这部分的旅途,先写一个符合标准的 helloworld.html
&!DOCTYPE html&
&head&&/head&
&body&&/body&
然后开始融入我们的 JavaScript,向 HTML 中插入JavaScript 的方法,就需要用到 HTML 中的 &script& 标签,我们先用页面嵌入的方法来写 helloworld。
&!DOCTYPE html&
document.write('hello,world');
&body&&/body&
按照标准的写法,我们还需要声明这个脚本的类型
&!DOCTYPE html&
&script type=&text/javascript&&
document.write('hello,world');
&body&&/body&
没有显示 hello,world ?试试下面的代码
&!DOCTYPE html&
&script type=&text/javascript&&
document.write('hello,world');
&noscript&
disable Javascript
&/noscript&
JavaScriptFul
我们需要让我们的代码看上去更像是 js,同时是以 js 结尾。就像 C 语言的源码是以 C 结尾的,我们也同样需要让我们的代码看上去更正式一点。于是我们需要在 helloworld.html 的同一文件夹下创建一个 app.js 文件,在里面写着
document.write('hello,world');
同时我们的 helloworld.html 还需要告诉我们的浏览器 js 代码在哪里
&!DOCTYPE html&
&script type=&text/javascript& src=&app.js&&&/script&
&noscript&
disable Javascript
&/noscript&
从数学出发
让我们回到第一章讲述的小明的问题,从实际问题下手编程,更容易学会编程。小学时代的数学题最喜欢这样子了——某商店里的糖一个5块钱,小明买了3个糖,小明一共花了多少钱。在编程方面,也许我们还算是小学生。最直接的方法就是直接计算 3x5=?
document.write(3*5);
document.write 实际也我们可以理解为输出,也就是往页面里写入 3*5 的结果,在有双引号的情况下会输出字符串。我们便会在浏览器上看到15,这便是一个好的开始,也是一个糟糕的开始。
设计和编程
对于实际问题,如果我们只是止于所要得到的结果,很多年之后,我们就成为了 code monkey。对这个问题进行再一次设计,所谓的设计有些时候会把简单的问题复杂化,有些时候会使以后的扩展更加简单。这一天因为这家商店的糖价格太高了,于是店长将价格降为了4块钱。
document.write(3*4);
于是我们又得到了我们的结果,但是下次我们看到这些代码的时候没有分清楚哪个是糖的数量,哪个是价格,于是我们重新设计了程序
document.write(price*num);
这才能叫得上是程序设计,或许你注意到了“;”这个符号的存在,我想说的是这是另外一个标准,我们不得不去遵守,也不得不去 fuck。
记得刚开始学三角函数的时候,我们会写
sin 30=0.5
而我们的函数也是类似于此,换句话说,因为很多搞计算机的先驱都学好了数学,都把数学世界的规律带到了计算机世界,所以我们的函数也是类似于此,让我们从一个简单的开始。
function hello(){
return document.write(&hello,world&);
当我第一次看到函数的时候,有些小激动终于出现了。我们写了一个叫 hello 的函数,它返回了往页面中写入 hello,world 的方法,然后我们调用了 hello 这个函数,于是页面上有了 hello,world。
function sin(degree){
return document.write(Math.sin(degree));
在这里 degree 就称之为变量。 于是输出了 -0.8602,而不是 0.5,因为这里用的是弧度制,而不是角度制。
的输出结果有点类似于sin 30。写括号的目的在于,括号是为了方便解析,这个在不同的语言中可能是不一样的,比如在 Ruby 中我们可以直接用类似于数学中的表达:
2.0.0-p353 :004 & Math.sin 30
=& -0.8618
2.0.0-p353 :005 &
我们可以在函数中传入多个变量,于是我们再回到小明的问题,就会这样去编写代码。
function calc(price,num){
result=price*num;
document.write(result);
calc(3,4);
但是从某种程度上来说,我们的 calc 做了计算的事又做了输出的事,总的来说设计上有些不好。
我们将输出的工作移到函数的外面,
function calc(price,num){
return price*num;
document.write(calc(3,4));
接着我们用一种更有意思的方法来写这个问题的解决方案
function calc(price,num){
return price*num;
function printResult(price,num){
document.write(calc(price,num));
printResult(3, 4)
看上去更专业了一点点,如果我们只需要计算的时候我们只需要调用 calc,如果我们需要输出的时候我们就调用 printResult 的方法。
object 和函数
我们还没有说清楚之前我们遇到过的 document.write 以及 Math.sin 的语法为什么看上去很奇怪,所以让我们看看他们到底是什么,修改 app.js 为以下内容
document.write(typeof document);
document.write(typeof Math);
typeof document 会返回 document 的数据类型,就会发现输出的结果是
object object
所以我们需要去弄清楚什么是 object。对象的定义是
无序属性的集合,其属性可以包含基本值、对象或者函数。
创建一个 object,然后观察这便是我们接下来要做的
store.candyPrice=4;
store.candyNum=3;
document.write(store.candyPrice * store.candyNum);
我们就有了和 document.write 一样的用法,这也是对象的美妙之处,只是这里的对象只是包含着基本值,因为
typeof store.candyPrice=&number&
一个包含对象的对象应该是这样子的。
store.candyPrice=4;
store.candyNum=3;
document.writeln(store.candyPrice * store.candyNum);
var wall=new Object();
wall.store=store;
document.write(typeof wall.store);
而我们用到的 document.write 和上面用到的 document.writeln 都是属于这个无序属性集合中的函数。
下面代码说的就是这个无序属性集合中的函数。
var IO=new Object();
function print(result){
document.write(result);
IO.print=print;
IO.print(&a obejct with function&);
IO.print(typeof IO.print);
我们定义了一个叫 IO 的对象,声明对象可以用
var store={};
var store=new Object();
两者是等价的,但是用后者的可读性会更好一点,我们定义了一个叫print的函数,他的作用也就是 document.write,IO 中的print 函数是等价于 print() 函数,这也就是对象和函数之间的一些区别,对象可以包含函数,对象是无序属性的集合,其属性可以包含基本值、对象或者函数。
复杂一点的对象应该是下面这样的一种情况。
var Person={name:&phodal&,weight:50,height:166};
function dream(){
Person.future=dream;
document.write(typeof Person);
document.write(Person.future);
而这些会在我们未来的实际编程过程中用得更多。
开始之前先让我们简化上面的代码,
Person.future=function dream(){
看上去比上面的简单多了,不过我们还可以简化为下面的代码。。。
var Person=function(){
this.name=&phodal&;
this.weight=50;
this.height=166;
this.future=function dream(){
return &future&;
var person=new Person();
document.write(person.name+&&br&&);
document.write(typeof person+&&br&&);
document.write(typeof person.future+&&br&&);
document.write(person.future()+&&br&&);
只是在这个时候 Person 是一个函数,但是我们声明的 person 却变成了一个对象 一个Javascript函数也是一个对象,并且,所有的对象从技术上讲也只不过是函数。 这里的“&br&”是 HTML 中的元素,称之为 DOM,在这里起的是换行的作用,我们会在稍后介绍它,这里我们先关心下 this。this 关键字表示函数的所有者或作用域,也就是这里的 Person。
上面的方法显得有点不可取,换句话说和一开始的
document.write(3*4);
一样,不具有灵活性,因此在我们完成功能之后,我们需要对其进行优化,这就是程序设计的真谛——解决完实际问题后,我们需要开始真正的设计,而不是解决问题时的编程。
var Person=function(name,weight,height){
this.name=name;
this.weight=weight;
this.height=height;
this.future=function(){
return &future&;
var phodal=new Person(&phodal&,50,166);
document.write(phodal.name+&&br&&);
document.write(phodal.weight+&&br&&);
document.write(phodal.height+&&br&&);
document.write(phodal.future()+&&br&&);
于是,产生了这样一个可重用的 JavaScript 对象, this 关键字确立了属性的所有者。
JavaScript 还有一个很强大的特性,也就是原型继承,不过这里我们先不考虑这些部分,用尽量少的代码及关键字来实际我们所要表达的核心功能,这才是这里的核心,其他的东西我们可以从其他书本上学到。
所谓的继承,
var Chinese=function(){
this.country=&China&;
var Person=function(name,weight,height){
this.name=name;
this.weight=weight;
this.height=height;
this.futrue=function(){
return &future&;
Chinese.prototype=new Person();
var phodal=new Chinese(&phodal&,50,166);
document.write(phodal.country);
完整的 JavaScript 应该由下列三个部分组成:
核心(ECMAScript)——核心语言功能
文档对象模型(DOM)——访问和操作网页内容的方法和接口
浏览器对象模型(BOM)——与浏览器交互的方法和接口
我们在上面讲的都是 ECMAScript,也就是语法相关的,但是 JS 真正强大的,或者说我们最需要的可能就是对 DOM 的操作,这也就是为什么 jQuery 等库可以流行的原因之一,而核心语言功能才是真正在哪里都适用的,至于 BOM,真正用到的机会很少,因为没有完善的统一的标准。
一个简单的 DOM 示例,
&!DOCTYPE html&
&noscript&
disable Javascript
&/noscript&
&p id=&para& style=&color:red&&Red&/p&
&script type=&text/javascript& src=&app.js&&&/script&
我们需要修改一下 helloworld.html 添加
&p id=&para& style=&color:red&&Red&/p&
同时还需要将 script 标签移到 body 下面,如果没有意外的话我们会看到页面上用红色的字体显示 Red,修改 app.js。
var para=document.getElementById(&para&);
para.style.color=&blue&;
接着,字体就变成了蓝色,有了 DOM 我们就可以对页面进行操作,可以说我们看到的绝大部分的页面效果都是通过 DOM 操作实现的。
这里说到的 JavaScript 仅仅只是其中的一小小部分,忽略掉的东西很多,只关心的是如何去设计一个实用的 app,作为一门编程语言,它还有其他强大的内制函数,要学好需要一本有价值的参考书。这里提到的只是其中不到20%的东西,其他的80%或者更多会在你解决问题的时候出现。
我们可以创建一个对象或者函数,它可以包含基本值、对象或者函数。
我们可以用 JavaScript 修改页面的属性,虽然只是简单的示例。
我们还可以去解决实际的编程问题。
前端与后台
前端 Front-end 和后端 Back-end 是描述进程开始和结束的通用词汇。前端作用于采集输入信息,后端进行处理。
这种说法给人一种很模糊的感觉,但是他说得又很对,它负责视觉展示。在 MVC 或者 MVP 结构中,负责视觉显示的部分只有 View 层,而今天大多数所谓的 View 层已经超越了 View 层。前端是一个很神奇的概念,但是而今的前端已经发生了很大的变化。你引入了 Backbone、Angluar,你的架构变成了 MVP、MVVM。尽管发生了一些架构上的变化,但是项目的开发并没有因此而发生变化。这其中涉及到了一些职责的问题,如果某一个层级中有太多的职责,那么它是不是加重了一些人的负担?
后台在过去的岁月里起着很重要的作用,当然在未来也是。就最近几年的解耦趋势来看,它在变得更小,变成一系列的服务。并向前台提供很多 RESTful API,看上去有点像提供一些辅助性的工作。
因此在这一章里,我们将讲述详细介绍:
后台语言与选型
前端框架与选型
前端一致化,后台服务化的趋势
前后端通讯
后台语言选择
如何选择一门好的后台语言似乎是大家都很感兴趣的问题?大概只是因为他们想要在一开始的时候去学一门很实用的语言——至少会经常用到,而不是学好就被遗弃了。或者它不会因为一门新的语言的出现而消亡。
JavaScript
在现在看来,JavaScript 似乎是一个性价比非常高的语言。只要是 Web 就会有前端,只要有前端就需要有 JavaScript。与此同时 Node.js 在后台中的地位已经愈发重要了。
对于 JavaScript 来说,它可以做很多类型的应用。这些应用都是基于浏览器来运行的,有:
Electron + Node.js + JavaScript 做桌面应用
Ionic + JavaScript 做移动应用
Node.js + JavaScript 网站前后台
JavaScript + Tessl 做硬件
So,这是一门很有应用前景的语言。
Python 诞生得比较早,其语言特性——做一件事情有且只有一种方法,也决定了这门语言很简单。在 ThoughtWorks University 的学习过程中,接触了一些外国小伙伴,这是大多数人学习的第一门语言。
Python 在我看来和 JavaScript 是相当划算的语言,除了它不能在前端运行,带来了一点劣势。Python 是一门简洁的语言,而且有大量的数学、科学工具,这意味着在不远的将来它会发挥更大的作用。我喜欢在我的各种小项目上用 Python,如果不是因为我对前端及数据可视化更感兴趣,那么Python 就是我的第一语言了。
除此呢,我相信 Java 在目前来说也是一个不错的选择。
在学校的时候,一点儿也不喜欢 Java。后来才发现,我从 Java 上学到的东西比其他语言上学得还多。如果 Oracle 不毁坏 Java,那么他会继续存活很久。我可以用 JavaScript 造出各种我想要的东西,但是通常我无法保证他们是优雅的实现。过去人们在 Java 上花费了很多的时间,或在架构上,或在语言上,或在模式上。由于这些投入,都给了人们很多的启发。这些都可以用于新的语言,新的设计,毕竟没有什么技术是独立于旧的技术产生出来的。
PHP 呢,据说是这个『世界上最好的语言』,我服务器上运行着几个不同的 WordPress 实例。对于这门语言,我还是相当放心的。并且这门语言由于上手简单,同时国内有大量的程序员已经掌握好了这门语言。不得不提及的是 WordPress 已经占领了 CMS 市场超过一半的份额,并且它也占领了全球网站的四分之一。还有 Facebook,这个世界上最大的 PHP 站点也在使用这门语言。
个人感觉 Go 也不错,虽然没怎么用,但是性能应该是相当可以的。
Ruby、Scala,对于写代码的人来说,这是非常不错的语言。但是如果是团队合作时,就有待商榷。
人们在不断地反思这其中复杂的过程,整理了一些好的架构模式,其中不得不提到的是我司 Martin Fowler 的《企业应用架构模式》。该书中文译版出版的时候是2004年,那时对于系统的分层是
提供服务、显示信息、用户请求、HTTP请求和命令行调用。
逻辑处理,系统中真正的核心。
与数据库、消息系统、事物管理器和其他软件包通讯。
化身于当时最流行的 Spring,就是 MVC。人们有了 iBatis 这样的数据持久层框架,即 ORM,对象关系映射。于是,你的 package 就会有这样的几个文件夹:
|____mappers
|____model
|____service
|____utils
|____controller
在 mappers 这一层,我们所做的莫过于如下所示的数据库相关查询:
&INSERT INTO users(username, password, enabled) & +
&VALUES (#{userName}, #{passwordHash}, #{enabled})&
@Options(keyProperty = &id&, keyColumn = &id&, useGeneratedKeys = true)
void insert(User user);
model 文件夹和 mappers 文件夹都是数据层的一部分,只是两者间的职责不同,如:
public String getUserName() {
return userN
public void setUserName(String userName) {
this.userName = userN
而他们最后都需要在 Controller,又或者称为 ModelAndView 中处理:
@RequestMapping(value = {&/disableUser&}, method = RequestMethod.POST)
public ModelAndView processUserDisable(HttpServletRequest request, ModelMap model) {
String userName = request.getParameter(&userName&);
User user = userService.getByUsername(userName);
userService.disable(user);
Map&String,User& map = new HashMap&String,User&();
Map &User,String& usersWithRoles= userService.getAllUsersWithRole();
model.put(&usersWithRoles&,usersWithRoles);
return new ModelAndView(&redirect:users&,map);
在多数时候,Controller 不应该直接与数据层的一部分,而将业务逻辑放在 Controller 层又是一种错误,这时就有了 Service 层,如下图:
Service MVC
Domain(业务)是一个相当复杂的层级,这里是业务的核心。一个合理的 Controller 只应该做自己应该做的事,它不应该处理业务相关的代码:
我们在 Controller 层应该做的事是:
处理请求的参数
渲染和重定向
选择 Model 和 Service
处理 Session 和 Cookies
业务是善变的,昨天我们可能还在和对手竞争谁先推出新功能,但是今天可能已经合并了。我们很难预见业务变化,但是我们应该能预见 Controller 是不容易变化的。在一些设计里面,这种模式就是 Command 模式。
模型用于封装与应用程序的业务逻辑相关的数据以及对数据的处理方法。
它是介于数据与控制器之间的层级,拥有对数据直接访问的权力——增删改查(CRUD)。Web 应用中,数据通常是由数据库来存储,有时也会用搜索引擎来存储
因此在实现这个层级与数据库交互时,可以使用 SQL 语句,也可以使用 ORM 框架。
SQL(Structured Query Language,即结构化查询语言), 语句是数据库的查询语言
ORM(Object Relational Mapping),即对象关系映射,主要是将数据库中的关系数据映射称为程序中的对象。
View 层在 Web 应用中,一般是使用模板引擎装载对应 HTML。如下所示的是一段 JSP 代码:
&head&&title&First JSP&/title&&/head&
double num = Math.random();
if (num & 0.95) {
&h2&You'll have a luck day!&/h2&&p&(&%= num %&)&/p&
&h2&Well, life goes on ... &/h2&&p&(&%= num %&)&/p&
&a href=&&%= request.getRequestURI() %&&&&h3&Try Again&/h3&&/a&
上面的 JSP 代码在经过程序解析、处理后,会变成相对应的 HTML。而我们可以发现在这里的 View 层不仅仅只有模板的作用,还加入了部分的逻辑。我们可以在后面细细看这些问题,对于前端的 View 层来说,它可能是这样的:
&div class=&information pure-g&&
&div class=&pure-u-1 &&
&div class=&l-box&&
&h3 class=&information-head&&&a href=&#/blog/{{slug}}& alt=&{{title}}&&{{title}}&/a&&/h3&
发布时间:&span&{{created}}&/span&
{{{content}}}
在这里的 View 层只是单纯的一个显示作用,这也是我们推荐的做法。业务逻辑应该尽可能的放置于业务层。
Controller
控制器层起到不同层面间的组织作用,用于控制应用程序的流程。
在前后端解耦合的系统中,通常系统的架构模式就变成了 MVP,又或者是 MVVM。
MVC、MVVM、MVP 对比
三者间很大的不同在于层级间的通讯模型、使用场景。
MVP 是从经典的模式 MVC 演变而来,它们的基本思想有相通的地方:Controller/Presenter 负责逻辑的处理,Model 提供数据,View 负责显示。
MVVM 是 Model-View-ViewModel 的简写。相比于MVC悠久的历史来说,MVVM 是一个相当新的架构,它最早于2005年被由的 WPF 和Silverlight 的架构师 John Gossman 提出,并且应用在微软的软件开发中。而 MVC 已经被提出了二十多年了,可见两者出现的年代差别有多大。
MVVM 在使用当中,通常还会利用双向绑定技术,使得 Model 变化时,ViewModel 会自动更新,而 ViewModel 变化时,View 也会自动变化。所以,MVVM 模式有些时候又被称作:model-view-binder 模式。
后台即服务
BaaS(Backend as a Service)是一种新型的云服务,旨在为移动和 Web 应用提供后端云服务,包括云端数据/文件存储、账户管理、消息推送、社交媒体整合等。
产生这种服务的主要原因之一是因为移动应用的流行。在移动应用中,我们实际上只需要一个 API 接口来连接数据库,并作一些相应的业务逻辑处理。对于不同的应用产商来说,他们打造 API 的方式可能稍有不同,然而他们都只是将后台作为一个服务。
在一些更特殊的例子里,即有网页版和移动应用端,他们也开始使用同一个 API。前端作为一个单页面的应用,或者有后台渲染的应用。其架构如下图所示:
Backend As A Service
API 演进史
在早期的工作中,我们会将大量的业务逻辑放置到 View 层——如迭代出某个结果。
而在今天,当我们有大量的逻辑一致时,我们怎么办,重复实现三次?
如下所示是笔者之前重构的系统的一个架构缩略图:
重复逻辑的系统架构
上面系统产生的主要原因是:技术本身的演进所造成的,并非是系统在一开始没有考虑到这个问题。
API 演进史
从早期到现在的互联网公司都有这样的问题,也会有同样的过程:
第一阶段: 因为创始人对于某个领域的看好,他们就创建了这样的一个桌面网站。这个时间点,大概可以在2000年左右。
第二阶段: 前“智能手机”出现了,人们需要开发移动版本的网站来适用用户的需要。由于当时的开发环境,以及技术条件所限,网站只会是桌面模板的简化。这时还没有普及 Ajax 请求、SPA 这些事物。
第三阶段: 手机应用的制作开始流行起来了。由于需要制作手机应用,人们就需要在网站上创建 API。由于当时的业务或者项目需求,这个 API 是直接耦合在系统中的。
第四阶段: 由于手机性能的不断提高,并且移动网络速度不断提升,人们便开始在手机上制作单页}

我要回帖

更多关于 sequelize 修改 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信