# 前端项目持续集成(一)
# 什么是持续集成
# 百度百科
- 是一种软件开发实践,每个开发人员每天至少要集成一次代码,每次集成都通过自动化构建「包括编译、发布、自动化测试」,从而尽早的发现集成错误。
- 持续集成一般和持续部署一起被提及【CI/CD】
- 减少风险
- 减少重复过程
- 任何时间任何地点都可部署软件
- 增强项目的可见性
- 建立团队对开发产品的信心
- 持续集成要遵从的原则
- 所有开发人员需要在本地机器上做本地构建,在提交版本控制库中,从而确保变更不会导致持续集成失败
- 开发人员每天至少向版本控制库提交一次代码
- 开发人员每天至少从版本控制库中更新一次代码到本地机器
- 需要有专门的集成服务器来执行集成构建,每天要执行多次构建
- 每次构建都要 100%通过
- 每次构建都可以生成可发布的产品
- 修复失败的构建是优先级最高的事情
- 测试是未来,未来是测试
# CI/CD 流程
- 本地机器写代码
- 提交代码,push 到 git 远程仓库
- git hook 触发 jenkins 的构建 job(自动)
- jenkins job 中拉取项目代码,运行
npm run unit
和npm run build
,如果失败,发邮件通知相关的人。(自动) - jenkins job 中执行测试服务器的部署脚本(自动)
当代码发生变更到 gitlab 的时候,就可以通过钩子将代码变更事件提交到 CI 系统,CI 系统也就是构建平台,一些插件组成的系统,可以在上面写脚本,这是一个服务器,上面有很多软件,如 jenkins 等(还有一个发布服务器,专门负责发布的),这台服务器也有引用代码仓库的钩子,检测到一些变更事件后就自动构建部署到内网,自动测试,自动提测。【最在 jenkins 上利用 webpack/grunnt 等进行项目构建并自动化测试,若成功,交付给 QA,若失败,打回开发】
# 前端工程化
# 自动化编译里都发生了什么
-> 读取foo.es文件内容,babel编译成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_2afob.png)
-> 替换完毕所有资源定位标记,对css内容进行压缩
-> 返回css内容
-> 根据foo.css最终内容计算md5戳,替换foo.scss为 /static/scss/foo_base39.css
-> 替换完毕所有资源定位标记,对js内容进行压缩
-> 返回js内容
-> 根据最终的js内容计算md5戳,得到foo.coffee的资源url为 /static/scripts/foo_3fc20.js
# 模块化
每个文件就是一个模块,有自己的作用域,这个文件里所定义的变量、函数、类都是私有的,CMD 和 AMD 都是 CommonJS 的一种规范实现定义,RequireJS 和 SeaJS 是对应的实践。
- AMD=> requirejs(预加载)提前读取并加载
1.1 AMD 推崇依赖前置1.2 AMD 对加载的模块是提前读取并加载 1.3 模块化原理
define(['dep1', 'dep2'], function(dep1, dep2){ // 内部只能使用指定模块 return function(){} })
// 文件加载/文件运行 顺序: 1.js 2.js 3.js
// 模块运行 顺序 3.js 2.js 1.js
// 1.js中 入口用require,其他用define
require(['2.js'], function(A){
// A是2.js的返回值
// 2.js, 3.js都加载完,才执行1.js的这个回调函数
})
// 2.js中
define(['3.js', 'xxx.js'], functionA(B, c){
return 2; // 2.js模块的返回值
})
// 3.js 中
define([], functionA(){
return {} // 3.js的返回值
})
// 子模块优先执行,主模块最后执行
1.4 优缺点 1.4.1 AMD 的依赖是提前声明的,依赖无需静态分析,无论是加载器还是自动化工具都可以直接获取到依赖 1.4.2 AMD 在代码书写上不怎么友好 1.4.3 AMD 模块内部与 NodeJS 的 modules 有一点差异
var onload = function(){
clearTimeout(tid);
}
if('onload' in script){
sript.onload = onload;
} else {
script.onreadystatechange=function(){
if(this.readyState === 'load' || this.readyState === 'complete'){
onload();
}
}
}
)(script, id);
- CMD => SeaJS(懒加载)提前读取文件,但在需要在加载 2.1 CMD 推崇就近依赖 2.2 CMD 对加载的模块是提前读取并不加载,而是需要时加载(依赖模块 CMD 是延迟执行) 2.3 加载原理
// CMD
define(finction(require, exports, module){
// 此处如果需要加载某模块,就声明加载
var xx = require("xxx");
})
2.4 模块化原理
// 只有define,没有require
// 1.js
define(function(){
var a = require("2.js");
console.log(22222);
var b = require("4.js")
})
// 2.js
define(function(){
var b=require("3.js")
})
....
先把 require 那部分用正则解析出来,需要什么模块就加载什么模块(先把所需要的包下载下来)。全部加载完之后再执行。先执行主模块 1.js,碰到 require("2.js")就执行 2.js,2.js 中碰到 require('3.js')就执行 3.js... 2.5 优缺点 2.5.1 CMD 依赖是就近声明,通过内部 require 方法进行声明。但因为是异步模块,加载器需要加载这些模块,所以模块真正使用前需要提取模块里的所有依赖。 2.5.2 不能直接压缩,require 局部变量如果替换无法加载资源 2.5.3 CMD 路径参数不能进行字符串运算(用正则解析)
- 为什么要使用 webpack 3.1 Webpack 执行 CommonJS 标准,解决了依赖配置和请求流量。 3.2 对于 Webpack 来讲,万物都可是模块,所有文件都被合并到 JS 中,最终在浏览器。 3.3 兼容 AMD,CMD 3.4 js 模块化不仅仅为了提高代码复用性,更是为了让资源文件更合理的进行缓存。
# 前端开发组件化
- 每一个前端模块都是一个小项目,配合 mock.js 可进行本地开发测试。package.json 是标配,通过 webpack 对环境配置进行本地环境,线上环境的统一编译。
- 有 page 组装 widget,由 widget 组装 WebComponents(X-TAG)。
- 能够根据路由快速选择配置 SPA
# WebComponents
WebComponents 标准能够使开发者将 HTML 页面功能封装成 CustomElements(自定义标签),而不需要 React, Vue 等来帮助了。
# custom element
CustomElemenntRegistry
接口实例用来处理 web 文档中的 custom elements->注册一个 custom element,并返回已注册的信息。
# CustomElementRegistry.define()
方法用来注册一个 custom element,该方法接受以下参数
- 表示所创建元素名称符合 DOMString 标准的字符串【custom element 名称不能是一个单词,且其中必须要有短横线】
- 用于定义元素行为的类
- 可选参数,一个包含 extends 属性的配置对象,是可选参数,他指定所创建的元素继承自哪个内置元素,可继承任何内置元素。
// main.js
class WordCount extends HTMLParagraphElement{
constructor(){
// 必须首先调用super方法
super();
// 元素功能写在这
// 计算器指向元素父级
var wcParent = this.parentNode;
function countWords(node){
var text = node.innerText || node.textContent;
return text.trim().split(/\s+/g).length;
}
var count = "Words: "+countWords(wcParent);
// 创建一个shadowRoot
var shadow = this.attachShadow({mode: "open"});
var text = document.createElement("span");
text.textContext = count;
// 将span 添加到shadow root上
shadow.appendChild(text);
setInterval(function(){
var count = "Words: "+ countWords(wcParent);
text.textCountent = count;
}, 200)
this.addEventListener("click", ()=>{
alert("hello world");
})
}
// 当custom element首次被插入文档DOM时,被调用
connectedCallback(){
console.log("load");
}
// 当custom element从文档删除时,被调用
disconnectedCallback(){};
// 当custom element被移动到新文档时,被调用
adoptedCallback(){}
// 当customelement增加、删除、修改自身属性时,被调用
attributeChangedCallback(){}
}
customElements.define("word-count", WordCount, {extends: "p"});
// index.html
...
<p>weeee</p>
<p>weeee</p>
<p is="word-count">1111</word-count>
...
# 共有两种 customElements
- Autonomous custom elements 是独立的元素,它不继承其他内建的 HTML 元素,可以直接写成 HTML 标签形式,在页面上用如
<popup-info>
,或者document.createElement("popup-info")
这样。 - Customized built-in elementts 继承自基本的 HTML 元素,在创建时,需要制定扩展元素,如上面例子的
<p>
元素,使用时,用is
属性指定 custom element 的名称,例如<p is="word-count">
或document.createElement("p", {is: "word-count"})
tips: shadow root【实验属性】 具体看 MDN 文档,会更新
AST
js 堵塞 MVC esjs router
支持 MVC (MVVM 双向数据绑定) 沙箱环境
Web Components
Custom Elements Html Imports Html Templates Shadow Dom
X-Tag