Giter VIP home page Giter VIP logo

blog's People

Contributors

mambat avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar

blog's Issues

分支模型 Git Flow

分支模型 Git Flow

思考

  • 团队并行开发多个功能,这些功能有可能是跨版本,如何才能既保证跨版本的代码不影响当前版本、又保证跨版本功能的正常开发和协作?

  • 生产环境发现严重 Bug 需要立即修复,而当前版本的代码已提交 master 分支、且并未测试充分,这时该如何处理?

  • 交给你的新功能已经开发完毕,但最终删掉了这些新功能,如何才能将回退成本降到最低?

利用 Git 强大的分支,可以帮助我们很好的解决这些问题。

有个很成熟的叫 Git Flow 的分支模型,能够应对 99% 的场景。简单来说,Git Flow 就是给原本普普通通的分支赋予了不同的职责

The main branches

  • master
  • develop

master 和 develop 被称为主分支(main branches)。

master 分支上的代码应该是可以随时发布生产环境的,即 “production-ready”。

develop 分支上的代码应该是 upcoming release 所需要的最新的代码,也称为 “integration branch”。

image

Supporting branches

为了做到 并行开发跟踪新功能的开发生产打包快速修复线上Bug,还需要一些辅助分支(supporting branches)。

  • Feature branches
  • Release branches
  • Hotfix branches

辅助分支都是临时性的,最终都会被删除。

辅助分支从技术上来说,跟主分支没有任何区别,唯一的区别在于,我们赋予了它们不同的职责。

Feature branches

Branch off from:

develop

Merge back into:

develop

Branch naming convention:

除了 master, develop, release-, or hotfix- 都可以,最好是 feature-*

Feature branches 有时也称为 Topic branches,用来开发新功能,在创建时可能并不清楚该功能属于哪次发布版本。

image

创建 Feature 分支:

$ git checkout -b myfeature develop

如果新功能属于 upcoming release,开发完成后需要将其合并到 develop 分支:

$ git checkout develop
Switched to branch 'develop'
$ git merge --no-ff myfeature
Updating ea1b82a..05e9557
(Summary of changes)
$ git branch -d myfeature
Deleted branch myfeature (was 05e9557).
$ git push origin develop

Release branches

Branch off from:

develop

Merge back into:

develop and master

Branch naming convention:

release-*

upcoming release 所包含的新功能已全部开发完成、且达到稳定状态(测试通过)时,就可以从当前 develop 分支创建新的 Release 分支,用于发布生产环境。

Release 分支只允许进行小的 Bug 修复 以及 一些元信息(meta-data )的修改,比如版本号、构建时间等,原则上不允许再进行新功能的开发。

将用于发布生产的 Release 分支一旦创建,就代表着 develop 分支可以接受 next release 所包含的新的 Feature 分支的代码合并了。

创建 Release 分支:

$ git checkout -b release-1.2 develop
Switched to a new branch "release-1.2"
$ ./bump-version.sh 1.2
Files modified successfully, version bumped to 1.2. This can of course be a manual change
$ git commit -a -m "Bumped version number to 1.2"
[release-1.2 74d9424] Bumped version number to 1.2
1 files changed, 1 insertions(+), 1 deletions(-)

当 Release 分支已经达到发布要求时,需要将其合并到 master 和 develop 分支。

合并到 master 分支,同时打 tag:

$ git checkout master
Switched to branch 'master'
$ git merge --no-ff release-1.2
Merge made by recursive.
(Summary of changes)
$ git tag -a 1.2

合并到 develop 分支(可能会包含 bug fixes 代码 和 更新后的 meta-data):

$ git checkout develop
Switched to branch 'develop'
$ git merge --no-ff release-1.2
Merge made by recursive.
(Summary of changes)

发布验证成功后,删除 Release 分支:

$ git branch -d release-1.2
Deleted branch release-1.2 (was ff452fe).

Hotfix branches

Branch off from:

master

Merge back into:

develop and master

Branch naming convention:

hotfix-*

当生产环境出现严重 Bug、需要立即修复时,就需要从 master 分支对应的线上 tag 检出 Hotfix分支、进行 Bug 修复。

image

创建 Hotfix 分支:

$ git checkout -b hotfix-1.2.1 master
Switched to a new branch "hotfix-1.2.1"
$ ./bump-version.sh 1.2.1
Files modified successfully, version bumped to 1.2.1.
$ git commit -a -m "Bumped version number to 1.2.1"
[hotfix-1.2.1 41e61bb] Bumped version number to 1.2.1
1 files changed, 1 insertions(+), 1 deletions(-)

解决 Bug、提交代码:

$ git commit -m "Fixed severe production problem"
[hotfix-1.2.1 abbe5d6] Fixed severe production problem
5 files changed, 32 insertions(+), 17 deletions(-)

跟 Release 分支一样,最后需要将 Hotfix 分支合并到 master 和 develop 分支:

$ git checkout master
Switched to branch 'master'
$ git merge --no-ff hotfix-1.2.1
Merge made by recursive.
(Summary of changes)
$ git tag -a 1.2.1
$ git checkout develop
Switched to branch 'develop'
$ git merge --no-ff hotfix-1.2.1
Merge made by recursive.
(Summary of changes)

特别注意,当存在 Release 分支时,需要将 Hotfix 分支合并到 Release 分支而不是 develop 分支。

首先 Release 分支上是即将发布生产环境的代码、需要包含这次修复Bug 的内容;其次 Release 分支在发布生产环境后、最终还是要合并到 develop 分支。

如果 develop 分支急需这些修复 Bug 的内容、无法等待 Release 发布,也可以立即将 Hotfix 立即合并到 develop 分支。

发布验证成功后,删除 Hotfix 分支:

$ git branch -d hotfix-1.2.1
Deleted branch hotfix-1.2.1 (was abbe5d6).

Summary

image

References

git-flow cheatsheet
Issues with git-flow
gitflow consider harmful

非功能性需求知多少

非功能性需求(Non-Functional Requirements ,NFRs)的概念非常宽泛,从易用性到安全性都属于非功能性需求,现将我所了解的非功能性需求整理如下。

性能

  • 响应时间
  • 业务量
  • 系统容量
  • 精度
  • 资源使用率

安全

  • 身份认证
  • 授权管理
  • 运行日志
  • 恶意攻击
  • 数据安全

可靠性

  • 数据检查
  • 异常处理
  • 数据丢失
  • 故障恢复
  • 7×24运行

易用性

  • 失败率
  • 用户粘性
  • 业务培训

可测试性

  • 复杂度
  • 单测覆盖率
  • 回归测试

可维护性

  • 复杂度
  • 代码规范
  • Bug修复

服务治理(分布式系统)

  • 缓存
  • 缓冲
  • 熔断
  • 降级
  • 限流
  • 幂等
  • 重试&补偿
  • 异步
  • 资源隔离

Welcome to Git

Git简介

Git是什么?

Git是目前世界上最先进的分布式版本控制系统(没有之一)。

image

Linus Torvalds

  • Linux & Git
  • Talk is cheap, show me the code.
  • Software is like sex: it's better when it's free.

什么是版本控制系统(VCS)?


版本控制是一种记录若干文件内容变化,以便将来查阅特定版本修订情况的系统。

版本 用户 说明 日期
1 张三 删除了软件服务条款5 7/12 10:38
2 张三 增加了License人数限制 7/12 18:09
3 李四 财务部门调整了合同金额 7/13 9:51
4 张三 延长了免费升级周期 7/14 15:17

集中式vs分布式

猛戳Here

Git是分布式版本控制系统,同一个Git仓库,可以分布到不同的机器上。

最初,只有一台机器有一个原始版本库,此后,别的机器可以“Clone”这个原始版本库,而且每台机器的版本都是一样的,没有主次之分。

然而,实际情况往往是找一台电脑充当服务器的角色,7x24小时开机,交换大家的修改。


为什么 Git 比 SVN 好

猛戳Here


Git基础

image

  • 每个版本库都有一个 .git 目录,它是 Git 用来保存元数据和对象数据库的地方。该目录非常重要,每次克隆版本库的时候,实际拷贝的就是这个目录里面的数据。

  • 从版本库中取出某个版本的所有文件和目录,用以开始后续工作的叫做工作目录。

  • 所谓的暂存区域只不过是个简单的文件,一般都放在 Git 目录中。有时候人们会把这个文件叫做索引文件,不过标准说法还是叫暂存区域。

基本的 Git 工作流程如下:

  1. 在工作目录中修改某些文件。

  2. 将修改后的文件保存到暂存区域。

  3. 提交更新,将保存在暂存区域的文件快照永久转储到 Git 目录中。

我们可以从文件所处的位置来判断其状态:

  • 如果自上次取出后,作了修改但还没有放到暂存区域,就是已修改状态。

  • 如果作了修改并已放入暂存区域,就属于已暂存状态。

  • 如果是 Git 目录中保存着的特定版本文件,就属于已提交状态。

  • 如果是还未纳入版本管理,就属于未跟踪状态。


安装Git

Baidu一下,你就知道

Google一下,你就知道的太多了


创建版本库

本地初始化版本库(从无到有)

Command Line
```
mkdir gitlab-demo
cd gitlab-demo
git init
```
Intellij IDEA

image

此方式用的不多,一般都采用克隆远程版本库的方式

克隆远程版本库(从有到多)


克隆,顾名思义,远程版本库已存在

Command Line
git clone [email protected]:wanglei/gitlab-demo.git
Intellij IDEA

image


把文件添加到版本库

首先明确一下,所有的版本控制系统,其实只能跟踪文本文件的改动,比如TXT文件,网页,所有的程序代码等等,Git也不例外。而图片、视频这些二进制文件,虽然也能由版本控制系统管理,但没法跟踪文件的变化,只能把二进制文件每次改动串起来,也就是只知道图片从100KB改成了120KB,但到底改了啥,版本控制系统不知道,也没法知道。

Command Line

touch README.md
vi README.md
git add README.md
git commit -m "wrote a readme file"

Intellij IDEA

  • 选中待操作的文件或这个工程(注意文件颜色的变化

  • Add to VCS

image

> ***一旦文件被Add to VCS,后续无需再Add: IDEA会在文件被修改后,自动进行Add*** 
  • Commit Changes(支持Commit & Push)

image

  • As a Windows developer, do not commit CRLF (Carriage-Return Line-Feed) line seperators

image


查看文件状态

Command Line

git status

Intellij IDEA:

image


查看提交日志

Command Line

git log --pretty=oneline

Intellij IDEA

image


查看文件变化

Command Line

  • 当前的工作目录里的,没有 staged(添加到索引中),且在下次提交时 不会被提交的修改:git diff
  • 当前的索引和上次提交间的差异:git diff --cached
  • 工作目录与上次提交时之间的所有差别:git diff HEAD
  • 指定具体文件:git diff HEAD -- GroupBean.java

Intellij IDEA

  • 已提交的不同版本之间的差异:

    image

  • 未提交与已提交最新版本之间的差异:

    image


撤销修改

Command Line

  • 当你改乱了工作区某个文件的内容,而且已经添加到了暂存区时,想丢弃修改:

    • 首先,git reset HEAD file,对文件进行unstage
    • 然后,git checkout -- file,文件回到与版本库一致的状态

    Note:
    如果直接执行git checkout -- file,则文件回到与暂存区一致的状态

  • 当你改乱了工作区某个文件的内容,还未添加到暂存区时,想丢弃修改:

    git checkout -- file

Intellij IDEA

Note: IDEA会自动Add(stage)

image


版本回退

Command Line

  • 通常需要先查看版本提交日志

  • 可以通过从当前版本(HEAD)回退N个版本的方式进行版本回退

    • git reset --hard HEAD^,回退一个版本
    • git reset --hard HEAD^^,回退两个版本
    • git reset --hard HEAD~N,回退N个版本
  • 也可以回退到指定版本(双向的

    git reset --hard 3628164

  • 撤销回退:git log无法获得被回退了的版本号,可通过git reflog查看我们的操作历史,从中可以得到被回退的版本号信息

Intellij IDEA

Note: 注意HEAD、master、origin master指针的变化

image

image

To Commit 中输入回退目标版本号,默认为HEAD
image


删除文件

Command Line

  • git rm file & git commit -m "彻底删除file",从版本库和工作目录中同时删除file文件。
  • git rm --cached file & git commit -m "版本库中删除,工作目录中保留file",从暂存区删除file文件,从而实现版本库中删除,工作目录中保留的目的。
  • 不小心本地删除了文件rm file,可以通过git checkout -- file恢复

Intellij IDEA

Note: IDEA只支持本地工作目录和版本库同时删除

本地删除file,然后Commit Changes

image


Ignore Files

版本库根目录下创建文件: .gitignore

```
target
.DS_Store
.idea
*.iml
```

Git分支

几乎每一种版本控制系统都以某种形式支持分支。

使用分支意味着你可以从开发主线上分离开来,然后在不影响主线的同时继续工作,开发完成后,再一次性合并到开发主线上。

在很多版本控制系统中,这是个昂贵的过程,常常需要创建一个源代码目录的完整副本,对大型项目来说会花费很长时间。

但Git的分支是与众不同的,无论创建、切换和删除分支,Git在1秒钟之内就能完成!

image

Git 的开发者都喜欢以这种方式来开展工作:

  • 在master 分支中保留完全稳定的代码,即已经发布或即将发布的代码。

  • 与此同时,他们还有一个名为develop分支 专门用于后续的开发,或仅用于稳定性测试。当然并不是说一定要绝对稳定,不过一旦进入某种稳定状态,便可以把它合并到master分支。

  • 还有在工作中,将开发任务分解为各个功能或者模块,用topic分支(topic branch主题分支,有又称为feature branch特性分支),


创建与合并分支

Git默认只有一个分支,交主分支,即master分支。所有向master分支的提交可以串成一条时间线,如下:

image

Command Line

  • 创建分支:git checkout -b feature1,相当于git branch feature1git checkout feature1

  • 查看分支:git branch,当前分支前面会标一个*

  • 合并分支分四步:

      1. 切换到目标分支,如将feature1分支合并到master分支:git checkout master
      
      2. 执行合并:git merge feature1
      
      3. 如果存在冲突,则需要先处理冲突,然后再进行commit
      
      3. 合并完成后,就可以放心地删除本地feature1分支了:git branch -d dev
    
      4. 如果存在远程feature1分支,也可以将其删除:git push origin :feature1
    

Intellij IDEA

  • 创建分支

    image

    image

    image

  • 查看和切换分支

    image

  • 在feature1分支上进行修改提交后,新的分支图如下

    image

  • 合并分支

    • 切换至目标分支

    • 执行合并:feature1分支 -> Merge

    • 如果存在冲突,则需要首先解决冲突再合并(左侧为目标分支,右侧为待合并分支,中间为合并结果)

      image

      image

    • 冲突解决后,在目标分支上再进行commit

    • 合并完成后,可删除本地分支、远程分支


Stash & Unstash

开发新功能的过程中,如果需要及时解决线上的一个Bug,但是目前新开发的代码又不能提交,该怎么办?

将当前工作目录保存下来,以备后续恢复后继续工作

Command Line

git stash

Intellij IDEA

image

基于master创建bug修复分支issue-001

git checkout master
git checkout -b issue-001
...
git add -A
git commit -m "fix bug 001"

恢复工作目录(假设之前的工作分支为dev)

Command Line
git checkout dev
git stash list
git stash apply stash@{0}
git stash drop stash@{0}

Note: git stash pop恢复至最近一次stash,同时将该stash删除

Intellij IDEA

image


远程版本库

团队开发中,为了方便的交换大家的代码,通常需要搭建一个Git服务器,团队所有人都可以7x24访问。

服务器上创建的版本库,我们称之为远程版本库,所有人将本地版本库的修改推送至远程版本库。

Git服务器

Git服务器需要提供用户注册、权限管理、版本库管理、代码预览、issue跟踪等功能,常见的Git服务器如下:

  • GitHub,公有版本库免费,私有版本库则是收费的,而且比较贵。
  • GitLab,开源产品,需要自己搭建,通常用于企业内部。

Git服务器创建版本库

很简单,可以去GitHub练练手。

添加远程版本库

Note: 通过Clone方式创建的本地版本库,不需要此步骤。

git remote add origin [email protected]:wanglei/gitlab-demo.git

IDEA Git不支持add remote repository,需要在命令行中进行处理。


推送分支

Command Line

Note:通过Clone方式创建的本地版本库 无需指定-u参数、将本地分支与远程分支关联起来

git push -u origin master

Intellij IDEA


若远程库是空的,第一次推送时需指定具体的分支:

image

image


抓取分支

当你的小伙伴已经向远程分支推送了他的提交,而碰巧你也对同样的文件作了修改,那么你再推送时会失败,因为你的小伙伴的最新提交和你试图推送的提交有冲突。

那么此时你就需要将远程最新的提交抓取下来,然后再本地合并,解决冲突再提交。

Command Line

  • 将本地分支与远程分支关联起来

      git branch --set-upstream dev origin/dev
    
  • 抓取并合并最新提交,合并冲突

      git pull
    
  • 提交并推送远程

      git commit -m "merge and fix xxx"
      git push origin dev
    

Intellij IDEA

  • 抓取并合并最新提交,合并冲突

    image

    image

    Warn: pull前必须对当前修改进行commit或者stash,否则pull无法进行。

  • 提交并推送远程


Merge Request

通常,版本库的master分支只有少数人能够直接推送,其他人只能通过创建Feature分支、像Master分支提交Merge Request的方式来提交代码。

提交Merge Request时,可以指定受理人,受理人会收到相应的Merge Request邮件,然后进行处理:

更新远程仓库数据

Command Line

git fetch origin

Intellij IDEA

image

查看Merge Request的修改记录

Command Line

git log master..origin/feature1

Intellij IDEA

image

如若接受Merge Request,则在本地进行合并

Command Line
git checkout master
git merge origin/feature1
###### 如果合并后测试有问题,则进行回退
git reset --hard HEAD^ 或 master@{1} 
###### 推送远程
git push
Intellij IDEA

image

Note:

  • Merge Changes前最好对当前本地的修改进行commit或者stash。
  • Command Line环境下,如果本地存在未提交的变更,则无法进行后续的Merge操作。
  • IDEA中,则提供了Smart Merge功能,原理就是自动帮你Stash & UnStash
  • 也可以在Branch视图中,选择对应的Remote Branch,进行Merge操作。

选中要合并的分支,执行合并

image

合并完成后,推送远程

Note:

  • 需要到Git服务器提供的Web界面进行Merge Request的发起和关闭。
  • Merge Request可以保证代码Review

加入GitLab

注册

  • 发送注册申请邮件给GitLab管理员,邮箱为[email protected],邮件正文给出注册使用的邮箱地址。

  • 注册申请成功后,GitLab会将用户名、初始密码发送至你的注册邮箱。

  • 首次登录GitLab时,会强制重置密码,修改密码后重新登录即完成注册。

协议

GitLab支持Http和SSH两种传输协议:

  • 使用Http协议时,每次进行Push操作都需要输入用户名密码,不建议使用。
  • SSH协议只需要在第一次使用时建立与GitLab的互信,后续Push等操作不再需要输入用户名密码。

SSH协议

与GitLab建立互信的步骤如下:

  • 生成SSH密钥对

    • ssh-keygen -t rsa -C "你的注册邮箱",一路回车即可。

    • 最终在~/.ssh/下会生成id_rsa、id_rsa.pub两个文件

  • 将SSH公钥添加到 GitLab SSH Keys 中。(在User Profile中)

    id_rsa.pub中存放的即为SSH公钥

  • 修改/etc/hosts

    115.28.91.188 gitlab.server

  • 修改SSH默认端口

    • 由于Docker Container无法Link到Host主机的22端口(SSH默认端口),所以这里GitLab使用的端口为10022。

    • 但Git默认使用的端口即为22端口,所以需要设置在SSH GitLab时使用特定端口(这里为10022)

    • vi ~/.ssh/config,内容如下

       Host gitlab.server
       Port 10022
       Host *
       Port 22
      
  • 测试

    ssh -Tvvv [email protected]

    当看到Welcome to GitLab, xxxExit status 0时表示SSH配置成功。

Enjoy the Git

GitHub Flow Intro

GitHub Flow Intro

What is GitHub Flow ?

  • master 分支是随时可发布的。

  • 开发新功能时,直接从 master 分支创建新分支,赋予合适的分支名(ie: new-category-list)。

  • 本地直接向分支 commit 代码,阶段性的将代码 push 到远程 repo。

  • 当分支开发完成需要被 merge、或需要代码评审等其他帮助时,提交一个 PR。

  • 当分支代码被评审通过、且新功能被确认时,你可以将其 merge 回 master 分支。

  • 一旦分支被 merge 回 master,立即进行发布操作。

下面具体介绍一下上述的每一步:

master 分支是随时可发布的

这是 GitHub Flow 中唯一的一条硬性规定。

master 分支永远是 stable 的,所有提交上去的代码都是测试通过的。

从 master 创建功能分支

功能分支的命名一定要能够顾名思义,这样可以方便团队了解哪些功能正在开发中,而且当你中途去开发其他功能、还能帮你快速的找到之前的功能分支。

可以将功能分支与 master 分支进行 compare 操作,方便查询每个分支做了哪些改动。

还可以分别查看每个功能分支的 commit 记录、活跃度。

鼓励将功能分支阶段性的 push 到远程 Repo

这样做有两个好处:

  • 使用远程 Repo 作为备份,防止严重的代码丢失。

  • 其他人通过 git fetch 命令就可以看到最近活跃的功能开发。如果发现一个功能迟迟没有 push,也许需要一些帮助。

随时可以提交一个 PR

PR 是大家参与开源项目的利器:fork 一个开源项目、提交修改、然后向开源项目维护者提交 PR。

PR 也可以是内部项目 Code Review 工具。PR 既可以用来申请 merge,也可以用来请求别人的帮助 或者 请求一次代码 review

当功能分支代码通过测试之后,便可以向 master 分支提交一个 PR。

PR 经过 review 之后才能 merge 回 master 分支

由于 master 分支是随时可发布的,所有 PR 只有经过 review 和 signoff 后才能 merge 回主分支。

分支被 merge 回 master 之后,立即发布

一旦你的代码被 merge 回 master 分支,你需要立即发布,否则就可能会有其他功能分支被merge。因为别人都是不可靠的,你肯定不希望别人提交的代码破坏你的功能。

MyBatisCodeHelperPro IDEA 插件使用总结

挺不错的插件,未激活版的功能也可以免费一直用下去。戳这里了解更多

注意事项

  • 自动生成的 Java Comment 缩进使用的是 Tab
  • 自动生成的 Java Service 引入了没用到 java.util.List
  • 自动生成的 Java 代码格式可能不太友好,比如 { 前面应该有空格、方法的多个入参应该使用空格分隔
  • 启用 noJdbcType 后,SQL 中就不会充斥着各种 JDBCType.xxx
  • 启用 generateService 可以自动生成 Service 接口及实现
  • 启用 Lombok,简化 Model 代码
  • 启用 swaggerAnnotation,则会为 Model 添加 Swagger API 注解

插件配置

image

如果使用的 JDK 版本 >= JDK8,那么可以开启 useJava8LocalDateTimeEct 选项。
methodNameGenerateSql 下的配置按需启用。

MyBatis Generator 配置

image

前端入门

本文写于2016年06月22日,现在读起来仍能温故知新。

从模块化谈起

模块的定义

模块,又称构件,是能够单独命名并独立地完成一定功能的程序语句的集合(即程序代码和数据结构的集合体)。

模块的特征

  • 独立性 — 能够独立完成一个功能
  • 完整性 — 完成一个特定功能
  • 集合性 — 一组语句的集合
  • 依赖性 — 可以依赖已经存在的模块
  • 被依赖 - 可以被其他模块依赖

前端为什么要模块化

image

  • WebApp越来越庞大,越来越复杂
  • 团队分工协作、进度管理、单元测试
  • 代码复用,开发只需关注核心业务逻辑

Javascript模块化编程

  • 很久很久以前,JSer把完成特定功能的代码放入独立的文件中,以此来实现“模块化”。

  • 作为Netscape 10天就开发出来的玩具语言,它为JSer们提供了太多大展拳脚的机会。

原始写法


        function m1 () {
          //...
      }

   function m2 () {
     //...
   }

  • 文件作为模块组织单元

  • 缺点

    • “污染”了全局变量
    • 命名冲突

对象写法


        var module1 = new Object({
		  _count : 0,
		  m1 : function (){
		    //...
		  },
		  m2 : function (){
		    //...
		  }
		});
    
  • Object作为模块组织单元

  • 优点

    • 不会“污染”全局变量
    • 解决了命名冲突问题
  • 缺点

    • 内部状态可以被外部改写
    • module1._count = 5;

立即执行函数表达式写法


	  var module1 = (function(){
	    var _count = 0;
	    var m1 = function(){
	      //...
	    };
	    var m2 = function(){
	      //...
	    };
	    return {
	      m1 : m1,
	      m2 : m2
	    };
	  })();
    
  • 立即执行函数表达式(IIFE)

  • 优点

    • 外部代码无法读取内部的私有变量
    • module1._count; //undefined
  • 模块的基本写法(ES5)

  • IIFE是模块化的基础(ES5)

放大模式


	  var module1 = (function (mod){
	    mod.m3 = function () {
	      //...
	    };
	    return mod;
	  })(module1);
    
  • 扩展了module1模块

  • 优点

    • 扩展 & 继承
  • 缺点

    • module1可能是空对象(加载顺序不确定)

宽放大模式



	  var module1 = ( function (mod){
	    //...
	    return mod;
	  })(window.module1 || {});
    
  • 优点:

    • 参数允许是空对象

输入全局变量



		  var module1 = (function ($) {
		    //...
		  })(jQuery);
    
  • 依赖模块通过参数传入

  • 优点

    • 模块独立,依赖关系明显

思考


  • 函数声明 & 函数表达式

  • 回调函数 & 模块化 & 闭包

为什么需要模块化规范

大家都以同样的方式编写、加载模块,更易阅读和协作。

Javascript模块化规范


  • CommonJS
  • AMD
  • CMD
  • UMD
  • ES6 Module

CommonJS



		var x = 5;
		var addX = function(value) {
		  return value + x;
		};
		module.exports.addX = addX;
    

		var example = require('./demo.js');
                 // 6
		console.log(example.addX(1));
    
  • 优点

    • 服务器端模块便于重用
    • NPM 中已经有将近20万个可以使用模块包
    • 简单并容易使用
  • 缺点

    • 同步的模块加载方式不适合在浏览器环境中
    • 不能非阻塞的并行加载多个模块
  • 实现

    • 服务器端的Node.js
    • Browserify,浏览器端的CommonJS实现,可以使用NPM的模块,但编译打包后的文件体积可能很大

AMD - Asynchronous Module Definition




		define("module", ["dep1", "dep2"], 
			function(d1, d2) {  
		  		return someExportedValue;
			}
		);
    

		require(["module", "../file"], 
			function(module, file) { 
		    	// 
			}
		);  
    

  • 优点

    • 适合在浏览器环境中异步加载模块
    • 可以并行加载多个模块
  • 缺点

    • 依赖前置,代码的阅读和书写比较困难
  • 实现

    • RequireJS

CMD - Common Module Definition




  • 优点

    • 依赖就近,延迟执行
    • 可以很容易在 Node.js 中运行
  • 缺点

    • 依赖 SPM 打包,模块的加载逻辑偏重
  • 实现

    • Sea.js(目前由阿里和腾讯维护)

UMD - Universal Module Definition



  • UMD规范类似于兼容 CommonJS 和 AMD 的语法糖

  • 模块定义的跨平台解决方案

ES6 Module




  • 优点

    • 语言层面支持,编译阶段静态分析
    • 面向未来的 ECMAScript 标准
  • 缺点

    • 原生浏览器端还没有实现该标准
  • 实现

    • Babel(编译为浏览器能够识别的 ES5 规范)

不仅仅是Javascript


  • CSS

  • IMG

  • WebFonts

  • HTML / Template

  • ......

Webpack

Webpack是什么



  • Webpack 是一个模块打包器

  • 根据模块的依赖关系进行静态分析,然后将这些模块按照指定的规则生成对应的静态资源

借助 Babel 可以将 ES6 模块 编译成 CommonJS 和 AMD

Webpack的特点



  • Code Splitting(代码拆分)

    • 支持异步依赖,可实现模块的按需加载
  • Loaders(加载器/转换器)

    • 本身只能处理 Javascript
    • 借助 Loader 可以将 各种类型的资源 转换成 Javascript 模块
  • Clever parsing(智能解析)

    • 支持 CommonJS、AMD、普通 JS文件
    • 支持动态表达式 require("./templates/" + name + ".jade")
  • Plugin system(插件系统)

    • 大多数功能都是借助插件系统实现
    • 开发和使用开源的插件,满足各式各样的需求
  • Efficient(高性能)

    • 异步 I/O 和多级缓存,快速增量编译

在项目中使用Webpack



Hot Reload(HMR)



  • Express

    • 基于 Node.js 的 Web 开发框架
    • 本身仅提供路由和 static 中间件
    • 开发和使用第三方中间件,满足各式各样的需求
  • webpack-dev-middleware

    • 一个处理静态资源的 middleware
    • 用来处理 webpack 打包输出的静态资源
  • webpack-hot-middleware

    • 结合 webpack-dev-middleware 使用的 middleware
    • 实现浏览器的Hot Reload(无刷新更新),也即 HMR(Hot Module Replacement)
var express = require('express');
var webpack = require('webpack');
var config = require('./webpack.dev.conf');

var app = express();
var compiler = webpack(config);

var devMiddleware = require('webpack-dev-middleware')(compiler, {
  publicPath: config.output.publicPath,
  stats: {
    colors: true,
    chunks: false
  }
});

var hotMiddleware = require('webpack-hot-middleware')(compiler);
// force page reload when html-webpack-plugin template changes
compiler.plugin('compilation', function (compilation) {
  compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) {
    hotMiddleware.publish({action: 'reload'});
    cb();
  })
});

// serve webpack bundle output
app.use(devMiddleware);
// enable hot-reload and state-preserving
// compilation error display
app.use(hotMiddleware);
// serve pure static assets
app.use('/static', express.static('./static'));

Mock Server



  • 在浏览器端MV*时代,Mock Server 使得前后端分离变得相对容易。
  • 推荐RAP

var express = require('express');
var proxyMiddleware = require('http-proxy-middleware');

var app = express();

var proxyTable = {
  // http://thx.github.io/RAP/index_zh.html
  '/login': 'http://rap.taobao.org/mockjs/1986/'
};

// proxy api requests
Object.keys(proxyTable).forEach(function (context) {
  var options = proxyTable[context];
  if (typeof options === 'string') {
    options = {target: options};
  }
  app.use(proxyMiddleware(context, options));
});

Long-Term Caching



"There are only two hard things in Computer Science: cache invalidation and naming things."


*Phil Karlton*

output.filename

除了可以指定具体的文件名以外,还可以使用一些占位符,包括:

  • name 模块名称
  • hash 模块编译后的(整体)Hash值
  • chunkhash 分片的Hash值

name

  • 如果 entry 未指定名字,会使用 main 作为模块名字。
  • 多entry、且未指定entry name时,会将所有 entry 合并打包
    • entry: ['./example2.1','./example2.2']

hash

  • 所有模块整体进行 Hash,每个模块的 Hash 值都一样
    • filename: '[name]-[hash].js'

chunkhash

  • 每个模块单独进行 Hash,每个模块都有自己的 Hash值
    • filename: '[name]-[chunkhash].js'
    • chunkFilename: '[name]-[chunkhash].js'(非 entry 模块)

Option 1: One hash for the bundle



{
    output: {
        filename: "output.[hash].bundle.js",
        chunkFilename: "[id].[hash].bundle.js"
    }
}

Option 2: One hash per chunk



{
    output: {
        filename: "output.[chunkhash].bundle.js",
        chunkFilename: "[id].[chunkhash].bundle.js"
    }
}

Code Splitting


代码分隔,按需加载。


import main from '../../main.js';
import OrderQuery from '../../views/order/OrderQuery';
import OrderDetail from '../../views/order/OrderDetail';

// 定义路由规则
let routeMap = {
  '/': {
    name: 'orderQuery',
    component: OrderQuery
  },
  '/order-detail': {
    name: 'orderDetail',
    // 异步加载会导致丢失Style.(未被抽取到common.css中,也不在当前的chunks bundle js中)
    component: function (resolve) {
      require(['../../views/order/OrderDetail'], resolve);
    }
    // component: OrderDetail
  }
};

// 启动应用
main.startApp(routeMap);

CommonsChunkPlugin



  • 提取Entry间的公共依赖,防止重复加载。
  • 可指定 Common Chunks 的name
  • 可指定相关的 Entry

plugins: [
  // If 'chunks' option omitted then all entry chunks are selected
  new CommonsChunkPlugin({name: 'commons'}),
  // Mark meta.js as entry chunk instead commons.js.
  new CommonsChunkPlugin({name: 'meta', chunks: ['commons']})
]

UglifyJsPlugin



压缩和混淆代码,减小 Bundle 的大小,提高加载速度,节省流量。


  plugins: [
    new webpack.optimize.UglifyJsPlugin({
      compress: {
        warnings: false
      }
    })
  ]

HtmlWebpackPlugin



// generate dist index.html with correct asset hash for each entry.
Object.keys(baseConfig.entry).forEach(function (name) {
  var htmlConf = {
    filename: '../'.concat(name).concat('.html'),
    template: './src/index.html',
    inject: true,
    chunks: ['meta', 'commons', name],
    chunksSortMode: function (a, b) {
      var index = {'meta': 1, 'commons': 2};
      index[name] = 3;
      var ai = index[a.names[0]],
        bi = index[b.names[0]];
      return ai && bi ? ai - bi : -1;
    },
    minify: {
      // more options: https://github.com/kangax/html-minifier#options-quick-reference
      removeComments: true,
      collapseWhitespace: true
    }
  };
  baseConfig.plugins.push(new HtmlWebpackPlugin(htmlConf));
});

Loaders


loaders: [
  {
    test: /\.vue$/,
    loader: 'vue'
  },
  {
    test: /\.css$/,
    loader: 'vue-style!css!px2rem?remUnit=75' // 将 px 批量转成 rem
  },
  {
    test: /\.js$/,
    loader: 'babel',
    include: projectRoot,
    exclude: /node_modules/
  },
  {
    test: /\.html$/,
    loader: 'vue-html'
  },
  {
    test: /\.(png|jpg|gif|svg|woff2?|eot|ttf)(\?v=[0-9\.]+)?$/,
    loader: 'url',
    query: {
      limit: 10000,
      name: '[name].[ext]?[hash:7]'
    }
  }
]

响应式设计

保证尽可能多的用户都能正常访问网站。
渐进增强是保证基本内容能够正常展示的情况下,增强现代浏览器的用户体验。
优雅降级是保证老浏览器也能够正常展示网页内容,去除非必须的酷炫特效。

什么是响应式设计



  • 网页设计应该做到根据不同设备环境自动响应及调整。

  • 不仅仅是关于屏幕分辨率自适应以及自动缩放图片等。

  • 更是一种对于设计的全新思维模式:移动优先、向下兼容。


适配不同终端



  • 遵循移动优先原则,交互&设计应以移动端为主,PC则作为移动端的一个扩展。

  • 适配不同终端有两个关键点:响应式布局、响应式内容(图片、多媒体)。

响应式布局



  • CSS Media Queries

  • viewport

  • Flex

响应式图片



  • 屏幕尺寸和分辨率

  • 屏幕的DPI/PPI

移动端适配

一些概念



  • 物理像素(Physical Pixel)

  • 设备独立像素(Density-Independent Pixel)

  • 设备像素比(Device Pixel Ratio)

  • 位图像素(Bitmap Pixel)

物理像素(Physical Pixel)



一个物理像素是显示器(手机屏幕)上最小的物理显示单元,在操作系统的调度下,每一个物理像素都有自己的颜色值和亮度值。

设备独立像素(Density-Independent Pixel)



  • 也叫密度无关像素、CSS 像素、逻辑像素。

  • 一种Web编程上的概念,是一个抽象的单位。


在不同的设备中,CSS 中的 1px 所代表的物理像素可能是不同的。

设备像素比(Device Pixel Ratio)



  • 设备像素比(简称dpr)定义了物理像素和设备独立像素的对应关系。

  • 设备像素比 = 物理像素 / 设备独立像素 // 在某一方向上,x方向或者y方向

  • 在javascript中,可以通过window.devicePixelRatio获取到当前设备的dpr。

位图像素(Bitmap Pixel)



  • 位图像素是栅格图像(如:png, jpg, gif等)最小的数据单元。

  • 每一个位图像素都包含着一些自身的显示信息(如:显示位置,颜色值,透明度等)。

以 iPhone6s 为例



  • 设备宽高为375×667,可以理解为设备独立像素(或CSS像素)。

  • dpr为2,根据上面的计算公式,其物理像素就应该 ×2,为 750×1334。

用一张图来说明




在不同的屏幕上(普通屏幕 vs Retina屏幕),CSS像素所呈现的大小(物理尺寸)是一致的,不同的是1个CSS像素所对应的物理像素个数是不一致的。

图片模糊



  • 理论上,1个位图像素对应于1个物理像素,图片才能得到完美清晰的展示。

  • 假设在普通屏幕下完美展示,但是在Retina屏幕下就会出现位图像素点不够,从而导致图片模糊。



对于dpr=2的retina屏幕而言,1个位图像素对应于4个物理像素,由于单个位图像素不可以再进一步分割,所以只能就近取色,从而导致图片模糊。


对于图片高清问题,比较好的方案就是两倍图(@2x),如:200×300(CSS Pixel)img标签,就需要提供 400×600 的图片。

图片缺少锐利度



  • 两倍图在普通屏幕下显示,一个物理像素点对应4个位图像素点。

  • 它的取色也只能通过一定的算法来实现(显示结果就是一张只有原图像素总数四分之一,我们称这个过程叫做downsampling)



肉眼看上去虽然图片不会模糊,但是会觉得图片缺少一些锐利度,或者是有点色差(但还是可以接受的)。

  • 对比度:明暗
  • 锐利度:边缘
  • 清晰度:模糊

图片显示 Demo



100×100的图片,分别放在100×100,50×50,25×25的img容器中,在Retina屏幕下的显示效果:



  • 爱字图,可以通过看文字"爱"来区分图片模糊还是清晰。

  • 条形图,通过放大镜其实可以看出边界像素点取值的不同:

    • 图1,就近取色,色值介于红白之间,偏淡,图片看上去会模糊(可以理解为图片拉伸)。
    • 图2,没有就近取色,色值要么是红,要么是白,图片看上去很清晰。
    • 图3,就近取色,色值介于红白之间,偏重,图片看上去有色差,缺少锐利度(可以理解为图片挤压)。

只需上传大图(@2x),小图交给图片服务器处理,页面只要负责拼接 URL 即可。

图片高清适配方案



不同 dpr 下,加载不同尺寸的图片。


利用 CSS Media Queries 或 Javascript。


搭建图片服务器,通过url参数,控制图片质量,或将图片裁剪成不同的尺寸。

Q: Bootstrap 布局适配的原理?

布局适配方案



目前比较流行的、也是助手采用的布局适配方案是使用相对单位 rem。

rem 是什么




		html {
		  font-size: 75px;
		}
    

		div {
		  width: 2rem;
		}
    

div 的实际宽度 width = 75px * 2 (即 150px)

还得聊聊视觉稿



在前端开发之前,VD 会给我们一个psd文件,称之为视觉稿。


移动端开发中,为了做到页面高清的效果,视觉稿往往会遵循以下两点:


  • 首先,选取一款手机的屏幕宽高作为基准(以前是iPhone4s的320×480,现在更多的是iPhone6的375×667)。

  • 对于Retina屏幕(如dpr=2),为了达到高清效果,视觉稿的画布大小会是基准的2倍,也就是说像素点个数是原来的4倍(对iPhone6而言,原先的375×667,就会变成750×1334)。


现在你应该知道,对于dpr=2的手机,为什么画布大小×2,就可以解决高清的问题了。



思考:对于2倍大小的视觉稿,在具体的CSS编码中如何还原每一个区块的真实宽高(也就是布局问题)?

rem 方案原理



针对不同屏幕尺寸dpr动态改变根节点html的font-size(基准值)。


document.documentElement.clientWidth * dpr / 10


  • 乘以dpr,是因为页面有可能为了实现1px border页面会缩放(scale) 1/dpr 倍(如果没有,dpr=1)。

  • 除以10,是为了取整,方便计算(理论上可以是任何值)


iPhone4/5: 320px * 2 / 10 = 64px


iPhone6/6s: 375px * 2 / 10 = 75px

CSS方式动态改变根节点 html 的 font-size



/*iPhone 4/5*/
@media screen and (min-width: 320px) {
  html {
    font-size: 64px !important; /*no*/
  }
}

/*iPhone 6/6s*/
@media screen and (min-width: 375px) {
  html {
    font-size: 75px !important; /*no*/               缺点:不够精确,但是够用。
  }
}

/*iPhone 6p/6sp*/
@media screen and (min-width: 414px) {
  html {
    font-size: 124.2px !important; /*no*/
  }
}

JS方式动态改变根节点 html 的 font-size



let docEl = document.documentElement;
let fontEl = document.createElement('style');

let dpr = window.devicePixelRatio || 1;
let rem = docEl.clientWidth * dpr / 10;

// 动态写入样式
fontEl.innerHTML = 'html{font-size:' + rem + 'px!important;}';
docEl.firstElementChild.appendChild(fontEl);

// 给js调用的,某一dpr下rem和px之间的转换函数
window.rem2px = function (r) {                        精确计算rem基准值,但要加载一段 js
  r = parseFloat(r);
  return r * rem;
};
window.px2rem = function (p, base) {
  let r = p / (base || 75);
  return r.toFixed(6);
};
window.jspx = function (r) {
  r = parseFloat(r / 75.0);
  return r * rem;
};

http://teazean.com/2015/11/02/reflow&repaint

同时使用CSS方式JS方式



  • 如果仅使用JS方式,那么页面在加载后可能会重新布局(relayout/reflow),产生抖动。

  • 所以先通过CSS方式,基本确定页面布局;然后再通过JS方式精确计算后,页面进行微调。

如何在 CSS 中还原视觉稿的真实宽高



基于 iPhone6s 的 750×1334 高清视觉稿中有一个宽高 750×300px 的 div。



由前面的公式可算出 iPhone6s 下 rem基准值 = 75px


  • width: 10rem; // rem基准值 = 75px

  • height: 4rem; // rem基准值 = 75px

如何自动适配 iPhone4s



由前面的公式可算出 iPhone4s 下 rem基准值 = 64px


  • width: 10rem; => width: 640px;

  • height: 4rem; => height: 256px;

px2rem 自动化



利用 Webpack 的 px2rem-loader 实现 px2rem 的自动转换。


  • 不需要人肉进行 px2rem 的转换

  • 调整样式时更简单、直观

发现问题了吗?



  • iPhone6s的物理像素是 750×1334

  • VD 视觉稿也是基于 iPhone6s的物理像素 750×1334

  • CSS 中使用的逻辑像素也是基于 750×1334

  • CSS像素 = 物理像素 / dpr


这意味着在高清屏上,目前的 CSS 使页面放大到了两倍,页面无法完全展示。

页面缩放



let docEl = document.documentElement;
let metaEl = document.querySelector('meta[name="viewport"]');

let dpr = window.devicePixelRatio || 1;
let scale = 1 / dpr;

// 设置viewport,进行缩放
metaEl.setAttribute('content', 
	'width=' + dpr * docEl.clientWidth + ',initial-scale=' + scale +
	',maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');
	
// 设置data-dpr属性,留作的css hack之用(如根据不同的 dpr 设置不同的 font-size)
docEl.setAttribute('data-dpr', dpr);

普通屏幕,页面不进行缩放;2倍高清屏时,页面缩放50%;3倍高清屏时。。。



思考:rem 方案先按物理像素进行设计,然后又进行页面缩放、适配 CSS像素,这么折腾到底为了什么?开发时直接按照 CSS像素进行页面布局不是也可以么?

字体大小问题



在讨论字体是否需要缩放时,通常要考虑设计师的要求:


  • 字体大小要统一,大屏显示更多的文字。

  • 字体大小随着屏幕的大小进行相应的缩放。

字体大小的统一和缩放




		font-size: 16px; /* no */
		[data-dpr="2"] input {
		  font-size: 32px; /* no */
		}
    

		font-size: 32px;
    

前提VD 按照 iPhone6s 的物理像素 750×1334px 进行设计,且视觉稿上 font-size 为32px。(左边进行了页面 scale,右边使用了 rem 方案适配)



左边font-size = dpr * 16px,结合页面scale = 1 / dpr,这就保证了字体在所有屏幕上的大小均为16px(CSS 像素)。



右边font-size = 32px / 75(rem基准值),这使得字体能够根据屏幕的分辨率进行等比缩放处理。

Retina下,border: 1px; 问题



这大概是设计师最敏感,最关心的问题了。



上图中,对于一条1px宽的直线,它们在屏幕上的物理尺寸(灰色区域)的确是相同的,不同的其实是屏幕上最小的物理显示单元,即物理像素,所以对于一条直线,iphone5它能显示的最小宽度其实是图中的红线圈出来的灰色区域,用css来表示,理论上说是0.5px。


设计师想要的 Retina 下 1px边框,其实是1物理像素。对于CSS而言,相当于0.5px边框,这是 Retina下(dpr=2)下能显示的最小单位。


然而,无奈并不是所有手机浏览器都能识别border: 0.5px; ios7以下,android等其他系统里,0.5px会被当成为0px处理,那么如何实现这0.5px呢?


  • border: 1px solid #dcdcdc; // 跳过 px2rem 的处理

  • scale = 1 / dpr


CSS中仍然指定1px,但页面进行了1/dpr缩放,实际的物理像素仍是1px。

该特性最先由Apple引入,用于解决移动端的页面展示问题,后续被越来越多的厂商跟进。
http://www.quirksmode.org/mobile/viewports.html
http://www.quirksmode.org/mobile/viewports2.html
http://www.quirksmode.org/mobile/metaviewport
http://weizhifeng.net/viewports.html
http://weizhifeng.net/viewports2.html
https://segmentfault.com/a/1190000004403527

viewport


先聊聊 PC 浏览器下的 viewport


缩放



Original & Zoom Out & Zoom In



在进行缩放时,元素的 CSS 像素不变,只是它占据的物理像素变化了,例如:



CSS 像素为100×100px的元素,放大到200%时,其 CSS 像素仍为100×100px,但其占用的物理像素为 200×200px,缩小亦同理。



思考 有没有遇到过,手机浏览器访问网站时,出现页面显示不完整 或 全部显示但元素非常小、看不清楚的情况。(有没有思考过背后的原因和解决方法)

屏幕尺寸



  • 获取方法: screen.width/height
  • 意义: 用户屏幕的整体大小。
  • 度量单位: 物理像素。
  • 浏览器错误: IE8以 CSS 像素对其进行度量。


窗口尺寸



  • 获取方法: window.innerWidth/Height
  • 意义: 浏览器窗口的整体大小,包括滚动条。
  • 度量单位: CSS 像素。
  • 浏览器错误: IE7不支持,Opera以设备像素进行度量。


放大后(右图),window.innerWidth/Height 变小了,因为可放入这个浏览器窗口中的元素减少了。

滚动距离


  • 获取方法: window.pageX/YOffset
  • 意义: 页面滚动的距离。
  • 度量单位: CSS 像素。
  • 浏览器错误: 无。


用户向上滚动,然后放大(右图),浏览器保持相同的元素位于可见页面的顶部,window.pageYOffset 并没有真正的更改:被滚动出窗口的CSS像素的数量仍然(大概)是相同的。经实际验证,是有改变的!!

viewport的概念



viewport的功能是用来约束网站中最顶级包含块元素(containing block)<html> 的。


思考 假设页面上一个 div 具有 width: 10% 属性,现在这个 div 会随着浏览器窗口大小的调整而恰好的放大和收缩。但是这到底是如何工作的呢?

从技术上来说



  • div 获取了它父元素宽度的10%,比方说是<body>元素(并且你还没有给它设置过宽度),所以问题就变成了<body>的宽度是哪个?

  • 普通情况下,所有块级元素使用它们父元素宽度的100%(有哪些例外?),所以<body>元素和它的父元素<html>一样宽。

  • 那么<html>元素的宽度是多少?它的宽度和浏览器窗口宽度一样。这就是为什么拥有 width: 10% 属性的 div 会占据整个浏览器窗口的10%。

所有web开发者都很直观的知道并且在使用这种技术。

从理论上来说



  • <html>元素的宽度是被 viewport 的宽度所限制的,<html>元素使用viewport宽度的100%。

  • viewport 实际上等于浏览器窗口,它就是这么定义的。

  • viewport 不是一个 html 结构,所以你不能用 CSS 来改变它。


viewport 在桌面环境下只是拥有浏览器窗口的宽度和高度,在移动环境下它会有一些复杂。



思考 对这个 div 进行缩放会是什么效果?

度量 viewport


  • ***获取方法:***document.documentElement.clientWidth/Height
  • 意义: viewport尺寸。
  • 度量单位: CSS 像素。
  • 浏览器错误: 无。


document.documentElement实际上指的是<html>元素,viewport 要比它更高一层,它是包含<html>元素的元素。



如果你给<html>元素设置width属性(如右图),document.documentElement.clientWidth/Height 给出的仍然是viewport的尺寸,,而不是<html>元素的。

两个属性对



  • document.documentElement.clientWidth/Height 不包含滚动条。

  • window.innerWidth/Height 包含滚动条。


事实上两个属性对的存在是浏览器战争的产物。最初Netscape只支持 inner、IE 只支持 client,后来所有其他浏览器都开始支持 client,但是 IE 却没有支持 inner。



在 PC 上拥有这两个属性对是有一些累赘的,BUT,就像我们将要看到的,在移动端这将会得到祝福。

度量<html>元素



  • ***获取方法:***document.documentElement.offsetWidth/Height
  • 意义: 页面(也就是<html>)的尺寸。
  • 度量单位: CSS 像素。
  • 浏览器错误: IE 度量的是viewport。

事件中的坐标


  • 获取方法: pageX/Y, clientX/Y, screenX/Y
  • 意义: 如下。
  • 度量单位: 如下。
  • 浏览器错误: IE不支持 pageX/Y。IE 和Opera 以CSS像 素为单位计算 screenX/Y。


pageX/Y提供了相对于<html>元素的以CSS像素度量的坐标。
clientX/Y提供了相对于viewport的以CSS像素度量的坐标。
screenX/Y提供了相对于屏幕的以物理像素进行度量的坐标。


90%的时间你将会使用pageX/Y,其他的10%时间你将会使用clientX/Y,但你永远不需要知道事件相对于屏幕的坐标。

媒体查询



你可以声明「只在页面宽度大于,等于或者小于一个特定尺寸的时候才会被执行」的特殊的CSS规则。

div.sidebar {
    width: 300px;
}

@media screen and (max-width: 400px) {
    // styles assigned when width is smaller than 400px
    div.sidebar {
        width: 100px;
    }
}
  • width/height使用 viewport 宽高(CSS 像素)。

* `device-width/device-height`使用屏幕宽高(物理像素)。
* 在桌面环境下去使用`width`而去忘记`device-width`吧。
* IE 浏览器不支持媒体查询。

移动浏览器的问题



屏幕尺寸小,为 PC 设计的网站在移动浏览器中显示的内容明显要少。


  • 对网页进行缩放直到文字小得无法阅读。

  • 以合适的尺寸只显示网页中的一小部分内容。


viewport 太窄,以至于不能正常展示你的 CSS 布局(不管是固定宽度、百分比 或 流式布局)。

两个 viewport



明显的解决方案是使 viewport 变宽一些,这样就产生了两个视口:


* `layout viewport`(布局视口) * `visual viewport`(视觉视口)

George Cummins在Stack Overflow上给出了最佳解释:


layout viewport 想像成为一张不会改变大小和形状的大图。


在想像你有一个小一些的框子,你通过它来看这张大图。(可以理解为「管中窥豹」),这个小框子的周围被不透明的材料所包围,遮挡住了你的视线,你只能看到这张大图的一部分。


通过这个框子所能看到的大图的部分就是 visual viewport。


过远离(缩小)/ 靠近(放大)这种方式,你可以调整所能看大图的内容多少,但是大图(layout viewport)的大小和形状永远不会变。

layout viewport



  • 问题的本质在于手机屏幕的 viewport 太窄,所以 Apple 提出创建一个虚拟的 layout viewport 来解决这个问题。

  • 这个 layout viewport 的分辨率接近于 PC 显示器(Apple 将其定义为 980px,Android也基本都是 980px?)。

由于宽度趋近,CSS 只需要像在 PC 上那样渲染页面就行,原有的页面结构不会被破坏。

visual viewport



  • visual viewport 是页面当前显示在屏幕上的部分,可以简单的认为是手持设备物理屏幕的可视区域。

  • 可以通过滚动来改变所看到的页面的部分,或者通过缩放来改变 visual viewport 的大小。

无论怎样,CSS 布局,尤其是百分比宽度,是以 layout viewport 做为参照系来计算的,它被认为要比 visual viewport 宽。

缩放



  • 很显然两个 viewport 都是以 CSS 像素度量的。

  • 但是当进行缩放(如果你放大,屏幕上的 CSS 像素会变少)的时候,visual viewport的尺寸会发生变化,layout viewport的尺寸仍然跟之前的一样。


如果不是这样,缩放时layout viewport尺寸发生变化,那你的页面将会像百分比宽度被重新计算一样而经常被重新布局。

理解这两个 viewport



  • 大部分移动浏览器在初始情况下,会以完全缩小的模式来展示任何页面,此时 visual viewport = layout viewport。


  • 当用户放大的时候 layout viewport 尺寸保持不变。


  • 完全缩小模式下 旋转手机至横屏时,visual viewport 会发生变化,但浏览器通过轻微的放大来适配这个新的朝向,所以 layout viewport 又和 visual viewport 一样宽了。

  • 这对 layout viewport 的高度会有影响,现在的高度比肖像模式(竖屏)要小。但是web开发者不在乎高度,只在乎宽度。


度量 layout viewport


  • ***获取方法:***document.documentElement.clientWidth/Height
  • 意义: layout viewport 的尺寸。
  • 度量单位: CSS 像素。
  • 浏览器错误: 完全支持。

朝向会对高度产生影响,但对宽度不会产生影响。

度量 visual viewport



  • 获取方法: window.innerWidth/Height
  • 意义: visual viewport 的尺寸。
  • 度量单位: CSS 像素。
  • 浏览器错误: Apple 完全支持,其他移动设备问题比较多。

滚动距离 scrolling offset



  • 获取方法: window.pageX/YOffset
  • 意义: 滚动距离,与 visual 相对于 layout 的距离一样。
  • 度量单位: CSS 像素。
  • 浏览器错误: 完全支持iPhone、Android。

度量<html>元素



  • 跟 PC 上一样,document.documentElement.offsetWidth/Height 提供了以 CSS 像素为单位的<html>元素的整个尺寸,完全支持 iPhone 和 Android。

事件坐标


  • pageX/Y仍然是相对于页面,以 CSS 像素为单位。

  • clientX/Y是相对于visual viewport来计算,以CSS像素为单位。

  • screenX/Y是相对于屏幕来计算,以物理像素为单位。(这和clientX/Y用的参照系是一样的)


媒体查询


  • 媒体查询和其在 PC 上的工作方式一样。

  • width/height 使用 layout viewport做为参照物,并且以 CSS像素进行度量。

  • device-width/height 使用设备屏幕,以设备物理像素进行度量。


viewport meta标签



  • 获取方法: <head> 下的 <meta> 标签
  • 意义: 设置 layout viewport 的宽度。
  • 度量单位: CSS 像素。
  • 浏览器错误: 完全支持iPhone、Android、IE等。

  • 假设你创建了一个简单的页面,并且没有给你的元素设置「宽度」,那么现在它们会被拉伸来填满 layout viewport 宽度的100%。大部分浏览器会进行缩放从而在屏幕上展示整个 layout viewport,产生下面这样的效果:


  • 所有用户将会立刻进行放大操作,这个是工作的,但是大部分浏览器完好无缺的保持元素的宽度,这使得文字很难阅读(不停缩放、左右滑动等)。


  • 你应该尝试设置html {width: 320px},现在<html>元素收缩了,并且其他元素现在使用的是 320px 的 100%。这在用户进行放大操作的时候有用(无需左右滑动),但是在初始状态是没用的,你什么都看不清。


  • 为了绕开这个问题(初始状态无效、需要手动放大)苹果发明了viewport meta标签。

  • 当你设置的时候,你就设置了 layout viewport 的宽度为320px。现在页面的初始状态也是正确的:


viewport meta 标签的6个属性



  • width 设置layout viewport 的宽度,正整数或"width-device"。

  • initial-scale 设置页面的初始缩放值,数字且可带小数。

  • minimum-scale 允许用户缩放的最小倍数值,数字且可带小数。

  • maximum-scale 允许用户缩放的最大倍数值,数字且可带小数。

  • height 设置 layout viewport 的高度,但并不被支持。

  • user-scalable 是否允许用户进行缩放,值为"no"或"yes"。


比如:<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"/>

ideal viewport(完美视口)



<meta name="viewport" content="width=device-width, initial-scale=1.0"/>


device-width将 layout viewport 设置为屏幕的宽度,单位是 CSS 像素。



在指定了initial-scale=1.0时,可省略width=device-width

前端开发模式演变

简单明快的早期时代(Web 1.0 时代)





  • 非常适合创业型小项目,不分前后端,经常 3-5 人搞定所有开发。
  • 页面由 JSP、PHP 等工程师在服务端生成,浏览器负责展现,展现的控制在 Web Server 层。

  • BADJSP 里揉杂大量Java业务代码,代码的可维护性差。

  • BAD前后端没有合理分工。

后端为主的 MVC 时代





  • 代码可维护性得到明显好转,MVC 是个非常好的协作模式,从架构层面让开发者懂得什么代码应该写在什么地方。

  • 为了让 View 层更简单干脆,可以选择 Velocity、Freemaker 等模板,使得模板里写不了 Java 代码,前后端分工更清晰。


  • BAD前端开发重度依赖开发环境。

    • 前端写 demo,后端套模板,套完后还需要前端确定,来回沟通调整的成本比较大。
    • 前端负责浏览器端的所有开发和服务器端的 View 层模板开发,不足就是前端开发重度绑定后端环境。

  • BAD前后端职责依旧纠缠不清。

    • 模板引擎功能强大,变量、逻辑、宏等特性,依旧可以通过拿到的上下文变量来实现各种业务逻辑。
    • 存在灰色地带 - Controller。页面路由等功能本应该是前端最关注的,但却是由后端来掌控。
    • Controller 本身与 Model 往往也会纠缠不清,看了让人咬牙的代码经常会出现在 Controller 层。

2004 年 Gmail 像风一样的女子来到人间,很快 2005 年 Ajax 正式提出,加上 CDN 开始大量用于静态资源存储,于是出现了 JavaScript 王者归来的 SPA (Single Page Application 单页面应用)时代。

Ajax 带来的 SPA 时代





  • 前后端的分工非常清晰,前后端的关键协作点是 Ajax 接口。

  • 复杂度从服务端的 JSP 里移到了浏览器的 JavaScript,浏览器端变得很复杂。

  • 类似 Spring MVC,这个时代开始出现浏览器端的分层架构,如 Backbone。


  • 不足前后端接口的约定。

    • 如果后端接口一塌糊涂,如果后端的业务模型不够稳定,那么前端开发会很痛苦。

  • 不足前端开发的复杂度控制。

    • 大量 JS 代码的组织,与 View 层的绑定等,都不是容易的事情。典型的解决方案是业界的 Backbone。

Backbone的Model把服务器端的数据模型映射到浏览器端,绑定数据验证机制,并与相应的REST操作绑定。


Backbone的Model没有与UI视图数据绑定,而是需要在JS中自行操作DOM来更新或读取UI数据。

前端为主的 MV* 时代




  • 前后端分工很清晰,可以让开发并行,测试数据的模拟不难,前端可以本地开发,后端则可以专注于业务逻辑的处理。

  • 前端开发的复杂度可控,前端代码很重,但合理的分层(比如Templates、Controllers、Models等),让前端代码能各司其职。

  • 后端一套Server API,多端使用(Web、移动APP等)

  • 部署相对独立,产品体验可以快速改进。

  • 不足代码不能复用。

    • 后端依旧需要对数据做各种校验,校验逻辑无法复用浏览器端的代码。如果可以复用,那么后端的数据校验可以相对简单化。

  • 不足全异步,对SEO(搜索引擎优化)不利,往往还需要服务端做同步渲染的降级方案。

    • 能够被搜索引擎抓取的内容,需要有完整的 HTML 和内容。

  • 不足性能并非最佳,特别是移动互联网环境下。

    • 性能优化如果只在前端做空间非常有限,通常需要与后端配合,但由于后端框架限制,我们很难使用Comet、Bigpipe等技术方案来优化性能。

  • 不足SPA 不能满足所有需求,依旧存在大量多页面应用。URL Design 需要后端配合,前端无法完全掌控。

Node 带来的全栈时代




  • Front-end UI layer 处理浏览器层的展现逻辑。通过 CSS 渲染样式,通过 JavaScript 添加交互功能,HTML 的生成也可以放在这层,具体看应用场景。

  • Back-end UI layer 处理路由、模板、数据获取、cookie 等。通过路由,前端终于可以自主把控 URL Design,这样无论是单页面应用还是多页面应用,前端都可以自由调控。

  • 通过 Node,Web Server 层也是 JavaScript 代码,这意味着部分代码可前后复用。

  • 需要 SEO 的场景可以在服务端同步渲染。由于异步请求太多导致的性能问题也可以通过服务端来缓解。

Bigpipe:

http://www.ifeenan.com/javascript/2014-07-20-%E5%89%8D%E7%AB%AF%E9%A1%B5%E9%9D%A2%E4%BC%98%E5%8C%96%E4%B9%8BBigpipe/


  • 挑战前端需要对服务端编程有更进一步的认识,比如 network/tcp等知识的掌握。

  • 挑战架构上多了一层 Node,提升了系统的复杂度和风险,也肯定会有一定的性能损耗。由此带来的损失,一定要从其他方面弥补回来。

    • 通过优化通讯方式、通讯协议,尽可能把损耗降到最低。
    • 代理多个异步请求,实现 Bigpipe。
    • ......

  • 挑战对部署、运维层面的熟练了解,需要更多知识点和实操经验,无法一蹴而就。

  • 挑战大量历史遗留问题如何过渡,这可能是最大的阻力。(如何把一个已有的项目,过渡为这种模式?)

一些软件设计的原则:http://coolshell.cn/articles/4535.html

前端开发模式总结



  • 模式没有好坏高低之分,只有合不合适,在选择时需要考虑团队人员的实际情况。

* Ajax 给前端开发带来了一次质的飞跃,Node 很可能是第二次。(BTW,当只有 Node 才能解决你的痛点时才考虑引入)
* SoC(关注点分离) 是一条伟大的原则。上面种种模式,都是让前后端的职责更清晰,分工更合理高效。
* 还有个原则,让合适的人做合适的事。比如 Web Server 层的 UI Layer 开发,前端是更合适的人选。

思考


  • 为什么需要前后端分离?

  • 前后端分离为什么需要 Node ?

页面性能优化

Best Practices and Rules



  • Content

  • Server

  • Cookie

  • CSS

  • JavaScript

  • Images

Content


Make Fewer HTTP Requests



减少 http 请求数


DNS系统是从域名到 IP 地址映射系统,DNS 解析一次大约20-120毫秒,在这个时间内浏览器不能下载任何东西。

Reduce DNS Lookups



减少 DNS 解析时间


  • DNS 解析如果被缓存可提高性能,内网的 DNS 缓存服务器、用户的操作系统、甚至大部分浏览器都有自己的 DNS 缓存。

  • 当浏览器和操作系统的 DNS 缓存都为空时,这时 DNS 解析所花费的时间是由你的网页中有多少个独立域名决定的,这些域名包括网页的url、图片、js、css、flash等。

  • 减少独立域名的个数可以减少 DNS 解析的时间,但会潜在的减少并行下载的数量。怎么平衡呢?建议把独立域名限制在2-4个之间。

Avoid Redirects



避免使用重定向


使用重定向会降低用户体验,它会使加载延时。

Make Ajax Cacheable



缓存 ajax

Post-load/Preload Components



延迟/提前 加载模块


  • Post-load只加载当前页面需要的模块资源,以最快的速度展现当前页面,提升用户体验。

  • Preload利用浏览器的空闲时间,加载下一页面将要需要的模块资源,这样下一页面将更快速的展现给用户。

Reduce the Number of DOM Elements



减少dom的element数量



查看dom的元素数 document.getElementsByTagName('*').length

Split Components Across Domains



把资源分散到多个域名下


把资源分散到多个域名下可以尽量多的并行下载,但是不要超过2-4个域名,因为 DNS 解析会也有一定的损耗。

Avoid 404s



避免404


  • 降低用户体验,浪费服务器资源。
  • 当外部js文件出现404时,会阻塞并行下载。

Server


Use a Content Delivery Network



使用 CDN 内容分发网络系统


  • 最终用户80-90%的时间是花在下载页面的图片、css、js、flash等上的,这是性能的黄金法则。

  • 静态内容使用 cdn 分发后可以使最终用户节约20%或者更多的响应时间。

Cache-Control:
https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching?hl=zh-cn#cache-control

Add an Expires or a Cache-Control Header



为http头添加Expires或Cache-Control


Gzip Components



使用 Gzip 传输部件


  • 自 http/1.1 开始,http 头开始支持压缩传输:Accept-encoding:gzip,deflate

  • Server 能通过 Content-Encoding 头来通知浏览器内容所使用压缩格式:Content-Encoding:gzip

  • Gzip 的压缩比大约为 70%,但不要对图片与pdf格式的文件来gzip,因为它们已被压缩过了。(你如果再压缩,不只浪费cpu还会增大文件的大小。)

Use GET for Ajax Requests



Ajax 请求使用 Get 方法


  • POST 方法在浏览器上实现时需要两个步骤:第一步发送 header,第二步才是发送数据。而 Get 方法只需要一个 TCP 包(除非你的 Cookie 特别大)。

  • 当你只是想请求查询数据时,一定要使用 Get 方法,这也正好符合 HTTP 规范

Avoid Empty Image src



避免 src 为空的 Image 元素


  • 浪费用户时间。
  • 浪费服务器资源。

Cookie


Reduce Cookie Size



减小 Cookie 长度


客户端在跟服务端通信时,会带上Cookie信息,所以减小 Cookie 长度,可以一定程度的降低响应时间。

Use Cookie-free Domains for Components



把静态资源放在无 Cookie 的域名上



CSS


Put Stylesheets at Top



把 css 放在 head 里


  • 把 css 放在 head 里后可以使网页渲染逐步进行,让网页尽快显示给用户。

  • 把css放入底部,那它就会阻断浏览器的渲染。很多浏览器会阻断页面的渲染以避免由于样式被改而重新渲染网页。

Avoid CSS Expressions



避免在 css 中使用 js 表达式



Background-color:expression((new Date).getHours()%2?'#000':'#fff');


  • 网页渲染 或者 窗口缩放 时 expression 会被重新计算。

  • 页面滚动 或者 鼠标移动 时都会重新计算。

Choose over @import



加载 css 时,要用 link,不要用 @import


在 IE 上用 @import 的作用与把 放入网页底部的作用是一样的,所以不要使用。

Javascript


Put Scripts at Bottom



把 js 放在底部


如果一个 js 脚本可以被推迟加载,那就把它放到页面最下面吧,这样会让你的页面以最快的速度展现给用户。

Make JavaScript and CSS External



把 js 和 css 放入外部文件


  • 增加了 http 请求次数,但同时也可被浏览器缓存。所以问题的关键在于,css/js 如果被缓存后被再次请求的频率。

  • 多页面通用的 css/js 放入外部文件,页面独有的则采取内联的方式。

  • 或者,把 css/js 嵌入到 html 中,当网页下载完成后再动态的下载外部的css/js。

Minify JavaScript and CSS



最少化(压缩) js/css 源码


  • 去掉源码中的不必要的字符,可以减少源码量,比如 空格、换行符、tab、注释等。

  • 通过 混淆(牺牲了代码可读性) 和 Gzip 还可以大幅减小 js/css 源码的大小。

Remove Duplicate Scripts



去除重复的 js 脚本


  • 如果一个页面对同一个js文件加载了两次,会浪费 http 请求数与 js 的执行效率。

  • 增加相应的 js 管理机制(版本检测和依赖检查),在上线前及时发现重复加载的 js 文件。

Minimize DOM Access



减少 DOM 查询

Image


Optimize Images



优化图片 - 无损压缩

Optimize CSS Sprites



优化雪碧图


  • 横着排而不要竖着排,这样会使文件更小。
  • 图片间不要留大的空隙。

Do Not Scale Images in HTML



不要在html里缩放图片


当你需要100×100的图片时,不要使用500×500的图片进行缩放,直接提供一张100×100图片就好。

Make favicon.ico Small and Cacheable



让 favicon.ico 小一点儿(1K以下),并且可缓存(缓存几个月)


  • favicon.ico 是非常邪恶的,因为即使你的站点没有这个文件,浏览器依然会请求这个文件,为了避免返回404,你最好还是提供一个。

  • 而且请求时会带上 Cookie信息,且 IE 会在别的资源下载前先请求这个文件。

检测工具


PageSpeed Insights


YSlow


Chrome DevTools


Frontend Knowledge Structure



<iframe data-src="http://html5ify.com/fks/fks_chart" src="about:blank;"></iframe>

Frontend Books



Commit message




了解更多

关于分支模型 Git Flow 的思考

关于分支模型 Git Flow 的思考

复杂度

Git Flow 分支模型约定了 5 种分支,模型比较复杂。需要团队中每个人都能正确地理解和选择正确的分支进行工作,对整个团队的纪律性提出了很高的要求。

规则越复杂,应用起来就越难,导致很多团队不得不借助额外的 Git 扩展脚本 gitflow 去应用这一套复杂的规则。

Feature 分支

Feature 分支有一下两个明显的好处:

  • 各个 Feature 之间的代码是隔离的,可以独立地开发、构建、测试。
  • 当 Feature 的开发周期长于 Release 周期时,可以避免未完成的 Feature 进入生产环境。

同时 Feature 分支也带来如下两个问题:

提高了 merge 的成本

Feature 分支在较长周期的开发完成后才能 merge 回 develop 分支,而在此期间 develop 分支可能已经被其他 Feature 分支修改过。

即使其他 Feature 分支进行过 rename 这种非常简单的重构,merge 时也需要处理大量的冲突。

持续集成

Feature 分支降低了代码集成的频率,big bang conflict 总是无法避免。

原则上要求各 Feature 分支的负责人将代码 merge 回 develop 分支,发现冲突、即刻解决。但一旦持续集成时、发现代码运行状态异常,该由哪个 Feature 负责人去解决问题?

Feature 分支的问题归根结底是 代码隔离持续集成 的矛盾。

为每个分支提供持续集成环境是个挑战

Hotfix、Release 甚至 Feature 分支都有可能需要自己的持续集成环境,这是需要额外的硬件资源和虚拟化能力的,对小团队都是个很大的挑战。

团队中的 Git 规范

团队中的 Git 规范

版本

版本格式:主版本号.次版本号.修订号

版本号递增规则如下:

  • 主版本号:当你做了不兼容的 API 修改

  • 次版本号:当你做了向下兼容的功能性新增

  • 修订号:当你做了向下兼容的问题修正

先行版本号及版本编译信息可以加到**“主版本号.次版本号.修订号”**的后面,作为延伸。

这套版本系统被称为 语义化的版本控制

主版本号为零(0.yz)的软件处于开发初始阶段,一切都可能随时被改变。这样的公共API 不应该被视为稳定版。

每当次版本号递增时,修订号 “必须MUST” 归零。

每当主版本号递增时,次版本号和修订号 “必须MUST” 归零。

被标上先行版本号则表示这个版本并非稳定而且可能无法达到兼容的需求。范例:1.0​​.0-alpha1.0.0-alpha.11.0.0-0.3.71.0.0-x.7.z.92

版本编译信息 “可以MAY” 被标注在修订版或先行版本号之后,先加上一个加号再加上一连串以句点分隔的标识符号来修饰。范例:1.0.0-alpha+0011.0.0+201303131447001.0.0-beta+exp.sha.5114f85

版本的优先层级指的是不同版本在排序时如何比较。范例:1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta < 1.0.0-beta < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0- rc.1 < 1.0.0。

Commit Message

代码要有代码的规范,协作要有协作的规范,Commit Message 也可以有自己的参考规范

前端 JS 框架 Angular:

image

前端知名 JS 框架 MingGeJS

image

想想我们自己的 Commit Message,更像哪一个?

接下来我们来分析下 Angular 的 Commit Message!

Commit Message 格式

<type>(<scope>): <subject>
<空行>
<body>
<空行>
<footer>

分成标题、内容详情、结尾三个部分,各有各的用处,没有多余项。

标题(header)

标题是可以直接在页面中预览的部分,分为 type、scope、subject 三部分。

type

  • feat:新功能(feature)
  • fix:修补bug
  • docs:文档(documentation)
  • style: 格式(不影响代码运行的变动)
  • refactor:重构(即不是新增功能,也不是修改bug的代码变动)
  • test:增加测试
  • chore:构建过程或辅助工具的变动

scope

用来说明本次 Commit 影响的范围,即简要说明修改会涉及的部分。

subject

用来简要描述本次改动,概述就好了,因为后面还会在 body 里给出具体信息。但最好遵循下面三条:

  • 以动词开头,使用第一人称现在时,比如change,而不是changed或changes
  • 首字母不要大写
  • 结尾不用句号(.)

内容详情(body)

是对上面 subject 里内容的展开,在此做更加详尽的描述,内容里应该包含修改动机和修改前后的对比。

结尾(footer)

主要放置 不兼容变更 和 Issue 关闭的信息。

image

Revert

如果是撤销之前的 Commit,那么本次 Commit Message 必须以 revert:开头。

body 中必须要记录所撤销的 Commit 的 SHA 值。

image

辅助工具 Commitiz

image

commitizen
ruanyifeng
jianshu

Install goimports

安装 goimports(需要先安装 go)

go get golang.org/x/tools/cmd/goimports

将 goimports 的安装目录添加到环境变量 PATH 中

$ vi ~/.config/fish/config.fish

set PATH /usr/local/Cellar/go/1.8/bin/bin $PATH

设置 Gogland

image

image

团队中的 Git 分支

团队中的 Git 分支

前提

  • 相对固定的发布周期/迭代周期

  • 做好项目的版本计划

如果没有规律的迭代周期和可靠的版本计划,即使我们的 Git 分支模型再强大,开发团队也免不了焦头烂额。

分支模型

  • master:受保护分支,不允许开发人员直接 commit,且随时可发布生产环境

  • develop:只有本迭代相关代码、在开发完成以后才能通过以提交 PR 的形式、申请 merge 回 develop 分支。develop 分支用于发布测试环境

  • feature:一旦初始版本上线以后,所有功能开发必须在 feature 分支上进行开发,用完即删,包括对应的远程分支。

  • hotfix:如果线上发现严重 Bug、需要紧急修复,这时从 master 分支检出 hotfix 分支、进行 Bug 修复工作。

初始版本上线了

  • 给 master 分支打 tag
$ git checkout master
$ git tag -a 1.0.0
  • 从 master 分支创建 develop 分支
$ git checkout -b develop master

进行新功能开发

假设有4个新功能需要开发:

  • 属于当前迭代的: F1、F2(新功能)和 FF3(Bug修复)
  • 迭代不确定的:UF4

开发个人从 develop 分支创建 Feature 分支进行开发:

$ git checkout -b feature-f1 develop
$ git checkout -b feature-f2 develop
$ git checkout -b feature-ff3 develop
$ git checkout -b feature-uf4 develop

属于当前迭代的新功能开发完成后

开发个人切换至本地 develop 分支,pull 最新代码:

$ git checkout develop
$ git pull

开发个人切换至相关 Feature 分支,merge develop 分支的最新代码:

$ git checkout feature-f1
$ git merge --no-ff develop

merge 完成、自测通过后,开发个人develop 分支发起一个 PR/MR 请求,申请 Code Review,指定相关人员

处理 PR/MR

在收到 PR/MR 后,相关人员需要及时完成 Code Review,如有问题需要开发个人进行相关调整。

确认无误后(代码 OK、版本计划没有变动),相关人员接受 PR/MR、将 feature-f1 merge 回 develop 分支。

signoff 需要在 PR/MR 在对话列表中体现,作为 Code Review 的凭证。(由于 develop 不是保护分支,在接收到 signoff 后,也可以由开发个人自行接受 PR/MR、完成 merge)

发布测试

merge 完成后,通知测试人员feature-f1功能已实现、可以发布测试环境,最终由测试人员决定何时发布测试。

TODO:结合 GitHooks 自动触发 Jenkins 发布测试环境

此时线上发现严重 Bug

开发个人 从 master 分支检出 Hotfix 分支:

$ git checkout master
$ git pull
$ git checkout -b hotfix-0022

必要时需要为 Hotfix 分支提供独立的持续集成环境(CI 任务、主机、Web 容器、数据库实例等)。

Bug 修复成功后,开发个人masterdevelop 分支发起 PR/MR 请求代码合并,同时指定相关人员

相关人员完成 PR/MR 的处理,将修复代码 merge 回 masterdevelop 分支,通知发布人员发布生产。

发布生产以后,通知相关人员开始必要的验证工作。

当前迭代已开发测试完毕,准备发布生产

相关人员develop 代码 merge 回 master 分支,通知发布人员发布生产:

$ git checkout develop
$ git pull
$ git checkout master
$ git merge --no-ff develop
$ ./bump-version.sh 1.2.0
version bumped to 1.2.0
$ git tag -a 1.2.0

TODO:结合 GitHooks 自动触发 Jenkins 发布生产环境

生产发布完成后,属于下一迭代的 Feature 分支才可以向 develop 分支合并

必要时,需要为跨迭代的 Feature 分支提供独立的持续集成环境。

FAQ

......

Reasons to Use Vue.js

本文写于2016年08月15日

Documentation



  • 国人作者,官方提供详尽的中文文档。

  • 部分相关类库也提供了中文文档。

[slide]

Easy to Master



  • 概念少,API 简单,容易上手。

  • vue-cli,一分钟搞定 webpack 配置。

  • Model 就是普通的 JavaScript 对象。

  • 官方提供了全套方案(组件、路由和状态)

  • Webstorm/Sublime/Atom Plugins

[slide]

UI Components

[slide]

社区贡献的组件列表



Name Desc Link
VueStrap 基于 Bootstrap 的一套组件* vue-strap
VueBoot 基于 Bootstrap 的一套组件 vue-boot
VueAdmin 用于控制台面板开发的一套组件* vue-admin
Keen-UI MD 风格的一套组件* keen-ui
Vue-Bulma 基于 Bulma 的一套组件* vue-bulma
Vue-Comps 一套未 styled 的组件* vue-comps

更多请参考:[awesome-vue](https://github.com/vuejs/awesome-vue)

[slide]

Data Binding



[slide]

Router



官方提供路由类库 Vue-Router



	var Foo = Vue.extend({
	  template:
	    '&lt;div class="foo"&gt;' +
	      '&lt;h2&gt;This is Foo!&lt;/h2&gt;' +
	      '&lt;router-view&gt;' + 
	      '&lt;/router-view&gt;' + 
	    '&lt;/div&gt;'
	})
</code></pre>
<pre><code class="javascript">
	router.map({
	  '/foo': {
	    component: Foo,
	    subRoutes: {
	      '/bar': {
	        component: Bar
	      },
	      '/baz': {
	        component: Baz
	      }
	    }
	  }
	})
</code></pre>

[slide]

State Management



官方提供状态管理类库 Vuex



		import Vuex from 'vuex'
	const state = {
	  count: 0
	}
	const mutations = {
	  INCREMENT (state) {
	    state.count++
	  }
	}
	export default new Vuex.Store({
	  state,
	  mutations
	})
</code></pre>
<pre><code class="javascript">




	store.dispatch('INCREMENT')
	
	console.log(store.state.count) // -> 1
</code></pre>

[slide]

[slide]

Supported Browsers



Vue.js supports all ECMAScript 5 compliant browsers.


[slide]

Community



[slide]

Performance



Vue vs React 英文版

Vue vs React 中文版


  • Vue.js 更轻量,~24kb min+gzip。

  • 由于 React 的虚拟 Dom 的实现,它的 scripting 上运行的时间更长;Vue 由于要直接更改 Dom ,所以它有关在 painting 和 rendering 工作上更耗费资源。

  • 所有工作都做完以后,Vue 在大多数情况下仍然比 React 快25%。

[slide]

TODO Third Party Integration



  • JQuery
  • amChart

FAQ

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.