前端架构(git、模块化)

协作流程

1.职责

页面工程师

前端工程师

接口设计

1.页面入口规范

基本信息

输入参数

模板列表
接口列表

2.同步数据规范

基本信息

预填数据

注入接口

3.异步接口规范

基本信息

输入数据

输出结果

同步请求,异步请求?

版本管理

版本控制系统VCS (Version control system)

1.分支模型

产品级的分支模型:

2.git

git是一个基于内容寻址的存储系统。基于文件内容,而不是基于文件。

安装

Windows: msysgit http://msysgit.github.io

Mac: brew install git

Ubuntu: apt-get install git

git基础操作

1.git config

用户配置:

git config --global user.name "Darcy"
git config --global user.name text.example.com

配置级别:

2.git init

初始化之后会出现一个.git/目录,下面存储着包括config文件在内的几乎所有git相关文件。

3.git status

跟踪:track

4.git add

添加文件内容到暂存区,同时文件被跟踪

批量添加: git add . 添加当前目录所有文件

5..gitignore

如果有不希望跟踪的文件,那么需要配置忽略文件。

仅作用于未跟踪的文件。

gitignore常见配置:github中的示例

6.git rm

暂存区删除文件。

  • git rm --cached 仅仅从暂存区删除

  • git rm 同时从暂存区和工作目录删除

  • git rm $(git ls-files --deleted) 删除所有被跟踪,但是在工作目录被删除的文件

7.git commit

提交。

  • git commit -m "initial commit"

  • git commit -a -m "initial commit" 直接提交

8.git log

提交历史记录。

  • git log

  • git log --oneline 只有7位hash和提交时输入的commit message

  • git log --color --graph --pretty=format:(此处省略2行) 更美观,且有分支链

上面的命令太长了,不可能每次都这样输入,因此需要配置别名alias

  • 语法:git config --global alias.shortname

  • 例子:git config --global alias.lg "log --color --graph --pretty=format:(此处省略2行)"
    这样就可以用 git lg 来表示上面那行命令了。

别名其实也存储在gitcofig文件中

9.git diff

显示版本差异。

  • git diff 工作目录与暂存区的差异

  • git diff -cached[] 暂存区与某次提交的差异,默认是,指向当前的提交

  • git diff [] 工作目录与某次提交的差异

10.git checkout --

撤销本地修改。

即:将工作内容从暂存区复制到工作目录。

11.git reset HEAD

取消暂存。

即:将文件内容从上次提交复制到暂存区。

12.git checkout HEAD --

撤销全部改动:取消暂存 + 撤销本地修改。

git分支操作

13.git branch

  • git branch 新建分支

  • git branch -d 删除指定分支

  • git branch -v 显示所有分支信息

14.git checkout

通过移动HEAD检出版本,可用于分支切换。

  • git checkout 切换分支

  • git checkout -b 新建一个分支并切换到新分支

  • git checkout -b 切换到其他引用对象,比如 commit id 或 标签

  • git checkout - 回到上一个分支(把HEAD移动到上一个分支)

15.git reset

将当前分支恢复到某个历史版本。以下三种模式的主要区别是内容是否会恢复到工作目录和暂存区。

  • git reset --mixed e33e42 --mixed是默认参数,不写也行,当前内容(即原来的提交)会被复制到暂存区

  • git reset --hard e33e42 --hard 时,当前内容(即原来的提交)会被复制到暂存区和工作目录

  • git reset --soft e33e42 --soft时,暂存区和工作目录都不会有任何改变,原来的提交变成了一个无索引的提交,有可能会被回收,可以用 git reflog 找回来

  • 捷径:git reset HEAD^/HEAD~1/HEAD~n HEAD的上一次提交,前第n次提交

区分resetcheckout 在操作分支与操作文件时的不同

16.git stash

我们在git checkout切换分支的时候,经常会被提示“当前有未提交的内容,请commitstash”,而我们通常是写到一半不希望commit的,所以这时就需要git stash

作用是:保存目前的工作目录和暂存区状态,返回一个干净的工作空间。

  • git stash save 'push to stash area' 第一步:保存

  • git stash list 第二步:查看已有列表 会显示:stash@{0}: On master: push to stash area

  • git stash apply stash@{0} 第三步:把保存的内容恢复到工作目录

  • git stash drop stash@{0} 第四步:把对应的stash命令删除掉

  • git stash pop stash@{0} 捷径:第三步+第四步

17.git merge

假定当前在master分支。

  • git merge next 合并 next 分支的内容到master分支

如有冲突,会是下面这样:

<<<<<<< HEAD

next 

=======

origin/master

>>>>>>> origin/master

====上面指当前分支的提交,下面是要merge过来的分支的提交内容。

18.git rebase

修剪提交历史的基线,俗称“变基”。

  • git rebase master

不要在共有分支使用rebase。

19.git tag

标签,一个不变的别名。用于标记某次发布。指向一个commit对象。

  • git tag v0.1 e39d0b2

打完标签之后,可以直接使用标签名切换分支: git checkout v0.1

git远程操作

20.git push

提交本地历史到远程。

21.git remote

  • git remote add origin ~/git-server 添加一个远程仓库别名origin

  • git remote -v 查看远程仓库信息

22.git fetch

获取远程仓库的提交历史。

  • git fetch origin/master

  • git merge origin/master

23.git pull

  • git pull

  • =git fetch + git merge

23.git clone

获取一个远程仓库作为本地仓库。

  • git clone ~/git-server test2 会克隆远程仓库到 test2目录下

技术选型

模块化(JS)

一、模块

1.模块的职责:

  • 封装实现

  • 暴露接口

  • 声明依赖

2.第一步:没有应用任何模块系统(反模式 Anti-Pattern)

math模块:

//math.js
function add(a, b) {
  return a + b;
}
function sub(a, b) {
  return a - b;
}

caculator模块:

//caculator.js
var action = "add";

function compute(a, b){
  switch(action){
    case "add": return add(a, b);
    case "sub": return sub(a, b);
  }
}

可以看出 caculator模块是依赖math模块的。

math模块特点:

  1. 无封装性:变量全部散落在全局里。

  2. 接口结构不明显:如果我们没有简化代码,那么并不能清楚的知道math到底输出了哪些接口

caculator模块特点:

  1. 没有依赖声明:依赖了math模块但是却没有声明。

  2. 使用全局状态:使用了action这个全局状态,应该尽量避免。

3.第二步:使用字面量(Object Literal)优化

math模块:

//math.js
var math = {
  add: function add(a, b) {
    return a + b;
  }
  sub: function sub(a, b) {
    return a - b;
  }
}

caculator模块:

//caculator.js
var caculator = {
  action: "add",
  compute: function compute(a, b){
    switch(action){
      case "add": return add(a, b);
      case "sub": return sub(a, b);
    }
  }
}

math模块特点:

  1. 结构性好:用字面量把接口进行了结构化。

  2. 访问控制差:依然没有进行控制。

caculator模块特点:

  1. 依然没有依赖声明:依赖了math模块但是却没有声明。

  2. 无法设置私有属性action虽然是成员属性,但在外部依然可以访问到。

4.第三步:使用立即执行的函数表达式IIFE(Immediately-invoked Function Expression)解决无法设置私有属性的问题。

caculator模块:

//caculator.js
var caculator = (function(){
  var action = "add";
  return {
    compute: function compute(a, b){
      switch(action){
        case "add": 
          return math.add(a, b);
        case "sub": 
          return math.sub(a, b);
      }
    }
  }
})();

caculator模块特点:

  1. 依然依然没有依赖声明:依赖了math模块但是却没有声明。

  2. 有了私有属性action是我们要的私有属性,compute函数可以访问到,而且在caculator外面无法访问到。

5.第四步:增加依赖声明

caculator模块:

//caculator.js
var caculator = (function(m){
  var action = "add";
  function compute(a, b){
    switch(action){
      case "add": 
        return m.add(a, b);
      case "sub": 
        return m.sub(a, b);
    }
  }
  return {
    compute: conpute
  }
})(math);

caculator模块特点:

  1. 显示了依赖声明:把math模块作为参数传了进去,并且可以对形参进行命名,这里命名为m

  2. math模块仍然污染了全局变量

  3. 必须手动进行依赖管理:math模块是手动传进去的,必须手动保证math是在这之前就被加载了。

  4. 注意return的部分与原来不一样了:学名叫 揭露模块模式review module pattern,优点是在暴露的模块进行增删查改的时候会非常方便

6.第五步:使用命名空间(name space)解决污染全局空间的问题

帮助我们只暴露一个类似于namespace的全局变量。而不是将math这样的模块都注册在全局作用域中。

math模块:

//math.js
namespace("math", [], function(){
  function add(a, b) {
    return a + b;
  }
  function sub(a, b) {
    return a - b;
  }
  return {
    add: add,
    sub: sub
  }
})
//第一个参数为模块声明,第二个参数为依赖的模块,第三个参数为模块的构成

caculator模块:

//caculator.js
namespace("caculator", ["math"], function(m){
  var action = "add";
  function compute(a, b){
    return m[action](a, b);
  }
  return {
    compute: compute
  }
}

依赖是统一注册在某个地方,而不是全局中的一个变量。

namespace的实现:

cache中缓存了所有的模块。
实际返回的是createModule这个函数,参数包括:模块名,依赖的模块,当前模块的实现。
如果只传入了一个参数,就返回这个模块cache[name]
取得所有依赖的模块deps,即保证前面的模块都已经被定义好了,这样当前模块(这里为caculator模块)才能运行。
最后初始化模并返回定义的模块cache[name]

该方法特点:

  1. 不再污染全局环境:把模块都定义在一个namespace变量中。

  2. 没有依赖管理:依然是我们手动进行依赖管理。

依赖管理(dependency manage)

如果这些模块分散在不同的文件中,我们在用的时候就要对引入的脚本顺序进行手动排序。
比如 module2.js中依赖了module1.js,那么写的时候就要先写module.js,像这样:


  
  

但是我们在实际开发过程中的依赖总是很复杂。那是一条又长又复杂的依赖链。非要人肉分析是会抓狂的。而这其实是模块系统的工作。

二、模块系统

1.模块系统的职责

  • 依赖管理(加载 / 注入 / 分析 / 初始化)

  • 决定模块写法

2.CommonJS

//main.js
function add(a, b){
  return a + b;
}
function sub(a, b){
  return a - b;
}
exports.add = add
exports.sub = sub

比原来的写法多了接口暴露:exports.add = add exports.sub = sub

//caculator.js
var math = require("./math");

function Caculator(container){
  //...
}

exports.Caculator = Caculator

比原来的写法多了依赖声明: var math = require("./math"); 和 接口暴露:exports.Caculator = Caculator

优点:

  • 运行时支持,模块定义非常简单:只是利用了几个全局变量exports, module, require

  • 文件级别的模块作用域隔离:这几个全局变量的作用域都是文件级别的,虽然JS没有文件级别的作用域,但我们对它进行了封装,使得使用时一个文件有一个作用域,它们使用起来非常安全。

  • 可以处理循环依赖。

缺点:

  • 不是标准组织的规范。

  • 同步的require,没有考虑浏览器环境。而我们的浏览器文件加载是一个异步的过程,这是最大的问题,这是否就意味着我们的浏览器没办法使用了呢?当然不是。现在有很多工具比如browserify,比如webpack,可以帮助我们把多个文件级别的模块打包成一个文件,这样我们引入单个文件就可以在浏览器里使用了。

因为CommonJS天然的不适合异步环境,所以出现了天然异步的AMD(Asynchronous Module Definition)

3.AMD

与我们前面的namespace非常像。

//main.js
define([], function(){
  function add(a, b){
    return a + b;
  }
  function sub(a, b){
    return a - b;
  }
  return {
    add: add,
    sub: sub
  }
})

比原来的写法多了包裹函数:define,第一个参数为依赖的模块列表,第二个参数为当前模块的实现。

//caculator.js
define(["./math"], function(math){
  function Caculator(container){
    //...
  }
  return {
    Caculator: Caculator
  }
}

同时AMD还支持一个简单的CommonJS写法,只不过要用一层函数包裹起来define(function(require, exports){ ... })

优点:

  • 专为异步I/O环境打造,适合浏览器环境。

  • 支持类似CommonJS的书写方式。

  • 通过插件支持可以加载非JS资源。

  • 成熟的打包构建工具,并可结合插件实现一些预处理的工作。

缺点:

  • 模块定义繁琐,需要额外的函数嵌套。

  • 只是库级别的支持,需要引入额外的库,比如requireJS。

  • 无法处理循环依赖。

  • 无法实现条件加载,因为只是库级别的。

4.原生JS语言级别支持的模块化标准ES6 Module(Javascript module definition for future)

//main.js
function add(a, b){
  return a + b;
}
function sub(a, b){
  return a - b;
}
export { add, sub }

比原来的写法多了接口暴露:export {add, sub}

//caculator.js
import { math } from './math';

function Caculator(container){
  //...
}

export { Caculator }

比原来的写法多了依赖声明: import { math } from './math'; 和 接口暴露:export { Caculator }

优点:

  • 真正官方的规范,未来的趋势。

  • 语言级别的支持。

  • 适应所有的JavaScript运行时环境,包括浏览器。

  • 可以处理循环依赖。

框架(JS框架)

什么是库和框架

  • 针对特定问题的解答,就有专业性

  • 不控制应用程序的流程

  • 被动的被调用

比如,一个DatePicker时间选择器是一个库,一个Backbone.view是一个框架。

框架

  • 控制反转 Inverse of control <···主要区别

  • 决定应用程序生命周期

  • 一般会集成大量的库

下面这个图很好的解释了控制反转


框架决定了什么时候调用库,什么时候要求你的代码去实现某些功能。

框架和库,他们都是解决方案。关于解决方案,分为7各方面:

  • DOM

  • communication 通信

  • Utility 工具库

  • Template 模板技术

  • Component 组件

  • Route 路由

  • Architecture MV*架构

1.DOM解决方案

重点:Selector / Manipulation(操作) / Event(dom) / Animation

  1. jQuery

  2. zepto.JS

  3. Mootools

  4. 手势支持:Hammer.js

  5. 局部滚动:iScroll.js

  6. 高级动画:Velocity.js

  7. 视频播放:video.js

2.Communication(通信)解决方案

重点:XMLHttpRequest / Form / JSONP / Socket

作用:

  • 处理与服务器的请求与响应

  • 预处理请求数据/响应数据 & Error/Success的判断封装

  • 多种类型请求,统一接口

  • 处理浏览器兼容性

  1. jQuery

  2. zepto.JS

  3. Reqwest

  4. qwest

以上都是异步的请求,但对于实时性要求非常高的产品比如im聊天工具,就需要立即响应。这时需要用websocket。推荐下面的库:

  1. socket.io

3.Utility(工具包)解决方案

重点:函数增强 & shim / Flow Control

职责:

  • 提供JS原生不提供的功能

  • 方法门面包装,使其易于使用。即shim(语言垫片),保证实现与规范一致。

  • 异步队列 / 流程控制 比如promise

3.Template

三种类型:String-based / Dom-based / Living Template

4.Component组件

常用组件: Modal / Slider / DatePicker / Tabs / Editor

  1. Bootstrap

  2. Foundation

5.Routing路由

分类:Client Side / Server Side

职责:

  • 监听url变化,并通知注册的模块,进行页面切换

  • 通过Javascript进行主动跳转

  • 历史管理

  • 对目标浏览器的兼容性的支持

route库

6.Architecture架构(目的:解耦)

分类:MVC / MVVM / MV*

职责:

  • 提供一种范式帮助(强制)开发者进行模块解耦

  • 试图与模型分离

  • 更容易进行单元测试

  • 更容易实现应用程序的扩展

各种框架比较的参考网站:
http://todomvc.com/
https://www.javascripting.com/
https://www.javascriptoo.com/
http://microjs.com/#

7.Component组件

开发实践

系统设计

1.系统说明

2.系统分解

3.接口设计

  • 数据类型(每个页面的每个模块都要单独定义包含的数据类型列表)

  • 模板资源

  • 异步接口(请求方式,请求地址,输入参数,输出结果)

  • 页面摘要

4.工程构建

  • 项目结构

  • 初始代码

  • 模拟数据

系统实现

1.组件封装

  • 通用原件(logo,输入框,图标,按钮,翻页,复选框列表,loading)

  • 通用列表(歌单。歌手,收藏的节目)

  • 复合组件(比如评论)

  • 浮层弹窗

一个组件(BannerSlifer)的栗子:

2.逻辑实现

测试发布

1.测试联调

  1. 本地测试

  2. 异步测试

  3. 对接联调

2.发布上线

  1. 打包发布

  2. 优化配置

你可能感兴趣的:(javascript,git,前端,前端工程化)