# 从0到大论前端持续集成

# 什么是持续集成。

# CI

  • 在持续集成环境中,开发人员将会频繁的提交 代码到主干。这些新提交在终合并到主线之 前,都需要通过编译和自动化测试流进行验 证。这样做是基于之前持续集成过程中很重视 自动化测试验证结果,以保障所有的提交在合 并主干之后的质量问题,对可能出现的一些问 题进行预警。

# 持续交付(CONTINUOUS DELIVERY)

  • 持续交付就是讲我们的应用发布出去的过程。这个过程 可以确保我们尽可能快的实现交付。这就意味着除了自 动化测试,我们还需要有自动化的发布流,以及通过一 个按键就可以随时随地实现应用的部署上线。
  • 通过持续交付,您可以决定每天,每周,每两周发布一 次,这完全可以根据自己的业务进行设置。
  • 但是,如果您真的希望体验持续交付的优势,就需要先 进行小批量发布,尽快部署到生产线,以便在出现问题 时方便进行故障排除。

# 持续部署(CONTINUOUS DEPLOYMENT)

  • 如果我们想更加深入一步的话,就是持续部署了。通过这 个方式,任何修改通过了所有已有的工作流就会直接和客 户见面。没有人为干预(没有一键部署按钮),只有当一 个修改在工作流中构建失败才能阻止它部署到产品线。
  • 持续部署是一个很优秀的方式,可以加速与客户的反馈循 环,但是会给团队带来压力,因为不再有“发布日”了。开 发人员可以专注于构建软件,他们看到他们的修改在他们 完成工作后几分钟就上线了。基本上,当开发人员在主分 支中合并一个提交时,这个分支将被构建、测试,如果一 切顺利,则部署到生产环境中。

# 持续集成需求

  1. 持续集成是通过平台串联各个开发环节,实现和沉淀 工作自动化的方法。
  2. 线上代码和代码仓库不同步,影响迭代和团队协 作。
  3. 静态资源发布依赖人工,浪费开发人力。
  4. 缺少自动化测试,产品质量得不到保障。
  5. 文案简单修改上线,需要技术介入。

图

# 121齐步走

  1. 统一代码仓库通过分支管理合并主干SVN。
  2. 自动化构建工具,编译、部署、测试、监控、本机 开发上线环境。FIS3/Webpack/jdists/package.json/ chai/supertest/mocha/selenium-webdriver
  3. 持续集成平台。Jenkins、Travis CI
  4. 部署工具。rsync、shelljs、yargs
  5. 运营同学有权限操作运营页面保存即可上线。

# 统⼀一代码仓库多分⽀支开发 ( Subversion Git)

# 合成步骤

  1. svn checkout svn地址 --username 用户名
  2. svn branch 分支名(add/commit)。
  3. svn merge 主干svn地址 分支svn地址。
  4. Beyond Compare -> svn resolved。
  5. svn copy 主干SVN地址 /tags/2017

# 前端⼯工程化

# 前端工程化目标

  • 自动化编译。
  • 前端模块化。
  • 定位静态资源。
  • 前端开发组件化。
  • 自动化部署测试配合版本库。
  • 自动化性能优化(前端架构开发下)

# 自动化编译

图

  • -> 读入foo.es的文件内容,编译成js内容
  • -> 分析js内容,找到资源定位标记 'foo.scss'
  • -> 对foo.scss进行编译:
    -> 读入foo.scss的文件内容,编译成css内容
    -> 分析css内容,找到资源定位标记 url(foo.png)
    -> 对 foo.png 进行编译:
    -> 读入foo.png的内容
    -> 图片压缩
    -> 返回图片内容
    -> 根据foo.png的终内容计算md5戳,替换url(foo.png)为url(/static/img/foo_2af0b.png)
    -> 替换完毕所有资源定位标记,对css内容进行压缩
    -> 返回css内容
  • -> 根据foo.css的终内容计算md5戳,替换'foo.scss'为 '/static/scss/foo_bae39.css'
  • -> 替换完毕所有资源定位标记,对js内容进行压缩
  • -> 返回js内容
  • -> 根据终的js内容计算md5戳,得到foo.coffee的资源url为 '/static/scripts/foo_3fc20.js'

# 前端模块化

  1. 前端模块化框架肩负着 模块管理、资源加载 两项重要的功能,这 两项功能与工具、性能、业务、部署等工程环节都有着非常紧密的联 系。因此,模块化框架的设计应该高优先级考虑工程需要。
  2. CommonJS API定义很多普通应用程序(主要指非浏览器的应用) 使用的API,从而填补了这个空白。它的终极目标是提供一个类似 Python,Ruby和Java标 准库。
  3. 根据这个规范,每个文件就是一个模块,有自己的作用域。在一个 文件里面定义的变量、函数、类,都是私有的,对其他文件不可见。 4. CMD和AMD都是CommonJS的一种规范的实现定义,RequireJS和 SeaJS是对应的实践。

优缺点

  1. CMD 依赖是就近声明,通过内部require方法进行声明。但是因 为是异步模块,加载器需要提前加载这些模块,所以模块真正使用 前需要提取模块里面所有的依赖。
  2. 不能直接压缩,require局部变量如果替换无法加载资源
  3. CMD路径参数不能进行字符串运算。
  4. AMD的依赖是提前声明。这种优势的好处就是依赖无需通过静态 分析,无论是加载器还是自动化工具都可以很直接的获取到依赖。
  5. AMD依赖提前声明在代码书写上不是那么友好。
  6. AMD模块内部与 NodeJS 的 Modules 有一定的差异。

依赖后置

  1. requirejs和seajs二者在加载上都有缺陷,就是模块的依赖要等到模块加载完 成后,通过静态分析(seajs)或者deps参数(requirejs)来获取,这就为 合 并请求 和 按需加载 带来了实现上的矛盾:要么放弃按需加载,把所有js合成一个文件。要么放弃请求合并,请求独立的 模块文件,从而满足按需加载。
  2. AMD规范在执行callback的时候,要初始化所有依赖的模块,而CMD只有执 行到require的时候才初始化模块。所以用AMD实现某种if-else逻辑分支加载不 同的模块的时候,就会比较麻烦了。 require(['page/index', 'page/detail'], function(index, detail){ switch(location.hash){ case '#index': index(); break; }});
  3. 以纯前端方式实现模块化框架 不能 同时满足 按需加载,请求合并 和 依赖 管理 三个需求。

总结

  1. 传统模块化的意义,是让开发者不再需要写一堆 script 标签 引入js 了,让编码更爽。但是对于浏览器那边,没啥大的变化。
  2. require.js只是采取了某种“方法”,让你在写代码时只写一个 script,运行html文档时却是多个script
  3. AMD: 3.js,2.js,1.js,,,即如果模块以及该模块的依赖都加载完 了,那么就执行。。。 比如 3.js 加载完后,发现自己也没有依赖啊, 那么直接执行3.js的回调了,,,2.js加载完后探查到依赖的3.js也加载 完了,那么2.js就执行自己的回调了。。。。 主模块一定在后执行
  4. CMD: 1.js,2.js,3.js,,,即先执行主模块1.js,碰到 require('2.js')就执行2.js,2.js中碰到require('3.js')就执行3.js

# 静态资源定位

  1. 配置超长时间的本地缓存 — 节省带宽,提高 性能
  2. 采用内容摘要作为缓存更新依据— 精确的缓 存控制
  3. 静态资源CDN部署—— 优化网络请求
  4. 更资源发布路径实现非覆盖式发布 — 平滑升 级

# 开发组件化

  1. 每一个前端模块都是一个小项目,配合mock.js 可以进行本地的开发测试,package.json是标 配产物。经过webpack的环境配置统一进行本 地环境、上线环境的编译过程。
  2. 由page组装widget,由widget组装Web Components(X-TAG)。
  3. 能够根据路由快速抉择配置SPA或者直出。
# Web Components
  1. Custom Elements
  2. HTML Imports
  3. HTML Templates
  4. Shadow DOM
# Custom Elements

提供⼀一种⽅方式让开发者可以⾃自定义 HTML 元素,包括特定 的组成,样式和⾏行行为。

class ButtonHelloElement extends HTMLButtonElement {   
    constructor() {     
        super() 
        this.addEventListener('click', () => {      
            alert('hello world')     
        })   
    } 
} 
customElements.define('button-hello', ButtonHelloElement, { extends: 'button' }) 
<button is="button-hello">hello world</button> 
<button-hello>hello</button-hello>
1
2
3
4
5
6
7
8
9
10
11
# HTML Imports

HTML Imports 是⼀一种在 HTMLs 中引⽤用以及复⽤用其他的 HTML ⽂文档的⽅方式。这个 Import 很漂亮,可以简单理理解为 我们常⻅见的模板中的 include 之类的作⽤用。

<link rel="import" href=“/components/header.html"> 
const link = document.querySelector('link[rel=import]') const header = link.import; 
const pulse = header.querySelector(‘div.logo'); //获取 import 的 HTML 的 document  const d = document.currentScript.ownerDocument
1
2
3
# HTML Templates

⽤用过 handlebars 的⼈人都知道有这么⼀一个东⻄西: 那么 HTML Templates 便便是把这个东⻄西官⽅方标准 化,提供了了⼀一个 template 标签来存放以后需要但是暂时不不 渲染的 HTML 代码。

<template id="template"><p>Smile!</p></template>     
<script>       
    let num = 3;       
    const fragment = document.getElementById('template').content.cloneNode(true);       
    while (num-- > 1) {         
        fragment.firstChild.before(fragment.firstChild.cloneNode(true));         
        fragment.firstChild.textContent += fragment.lastChild.textContent;       
    }       
    document.body.appendChild(fragment);
</script>

1
2
3
4
5
6
7
8
9
10
11
# Shadow DOM

Shadow DOM 最本质的需求是需要⼀一个隔离组件代码作⽤用域的 东⻄西,例例如我组件代码的 CSS 不不能影响其他组件之类的,⽽而 iframe ⼜又太重并且可能有各种奇怪问题。旨在提供⼀一种更更好地 组织⻚页⾯面元素的⽅方式,来为⽇日趋复杂的⻚页⾯面应⽤用提供强⼤大⽀支 持,避免代码间的相互影响。

const div = document.getElementById('id') 
const shadowRoot = div.createShadowRoot() 
const span = document.createElement('span') 
span.textContent = 'hello world' shadowRoot.appendChild(span)
1
2
3
4
# Shadow DOM CSS
<x-foo>    
    <"shadow tree">      
        <div>        
            <span id="not-top">...</span>      
        </div>      
        <span id="top">...</span>    
    </>  
</x-foo>
1
2
3
4
5
6
7
8

x-foo::shadow > span 可以匹配到 #top 元素 x-foo /deep/ span 可以匹配到 #not-top 和 #top 元素 :host(.foo) 匹配 x-foo 元素

# Web Components
<template id=""> 
    <style> 
        ::content li {   
            display: inline-block;   
            padding: 20px 10px; 
            } 
    </style> 
<content select="ul"></content> 
</template>
1
2
3
4
5
6
7
8
9
<head>
<link rel="import"  href="components/header.html">   
</head>   
<body>     
    <test-header>       
        <ul>         
            <li>Home</li>         
            <li>About</li>       
        </ul>     
    </test-header>   
</body>

1
2
3
4
5
6
7
8
9
10
11
12

# 自动化部署

jenkins travis CI