前端架构(git、模块化)

协作流程

1.职责

页面工程师

《前端架构(git、模块化)》

前端工程师

《前端架构(git、模块化)》

接口设计

《前端架构(git、模块化)》

1.页面入口规范

《前端架构(git、模块化)》

基本信息

输入参数

模板列表
接口列表

2.同步数据规范

《前端架构(git、模块化)》

基本信息

预填数据

注入接口

3.异步接口规范

《前端架构(git、模块化)》

基本信息

输入数据

输出结果

同步请求,异步请求?

版本管理

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

1.分支模型

产品级的分支模型:

《前端架构(git、模块化)》

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

配置级别:

《前端架构(git、模块化)》

2.git init

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

3.git status

《前端架构(git、模块化)》

《前端架构(git、模块化)》

跟踪:track

4.git add

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

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

5..gitignore

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

仅作用于未跟踪的文件。

《前端架构(git、模块化)》

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 <full command>

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

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

9.git diff

显示版本差异。

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

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

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

10.git checkout --<file>

撤销本地修改。

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

11.git reset HEAD <file>

取消暂存。

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

12.git checkout HEAD --<file>

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

《前端架构(git、模块化)》

git分支操作

13.git branch

  • git branch <branch-name> 新建分支

  • git branch -d <branch-name> 删除指定分支

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

14.git checkout

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

  • git checkout <branch-name> 切换分支

  • git checkout -b <branch-name> 新建一个分支并切换到新分支

  • git checkout -b <reference> 切换到其他引用对象,比如 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 在操作分支与操作文件时的不同

《前端架构(git、模块化)》

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 <remote> <branch>

  • =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的实现:

《前端架构(git、模块化)》

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

该方法特点:

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

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

依赖管理(dependency manage)

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

<body>
  <script src="module1.js"></script>
  <script src="module2.js"></script>
</body>

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

二、模块系统

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 <···主要区别

  • 决定应用程序生命周期

  • 一般会集成大量的库

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

《前端架构(git、模块化)》
框架决定了什么时候调用库,什么时候要求你的代码去实现某些功能。

框架和库,他们都是解决方案。关于解决方案,分为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

《前端架构(git、模块化)》

4.Component组件

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

  1. Bootstrap

  2. Foundation

5.Routing路由

分类:Client Side / Server Side

职责:

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

  • 通过Javascript进行主动跳转

  • 历史管理

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

route库

《前端架构(git、模块化)》

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

分类:MVC / MVVM / MV*

职责:

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

  • 试图与模型分离

  • 更容易进行单元测试

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

《前端架构(git、模块化)》

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

7.Component组件

开发实践

系统设计

1.系统说明

2.系统分解

3.接口设计

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

  • 模板资源

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

  • 页面摘要

4.工程构建

  • 项目结构

  • 初始代码

  • 模拟数据

系统实现

1.组件封装

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

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

  • 复合组件(比如评论)

  • 浮层弹窗

一个组件(BannerSlifer)的栗子:

《前端架构(git、模块化)》

2.逻辑实现

测试发布

1.测试联调

  1. 本地测试

  2. 异步测试

  3. 对接联调

2.发布上线

  1. 打包发布

  2. 优化配置

    原文作者:工具
    原文地址: https://segmentfault.com/a/1190000009341189
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞