从零开始为Web项目定制构建系统(三)——简单交互页

268 阅读6分钟

原文:nakeman.cn/engineering…

我们现在已经给项目添加了git系统,为 项目管理 打好了基础条件。接下来的一个版本继续完善「构建系统」的形式,并给wgp添加少许复杂度,做一个完整的交互页面。

wgp项目新版本功能计划如下:

第一,充实build任务,划出独立的build目录,将发布版与开发版分开;添加clean反向清理任务;
第二,start 任务除了启动http server,还打开浏览器;
第三,添加热加载功能,不用每次修改都重新手动刷新浏览器;如 LiveReload / watch
第四,添加交互行为,类似hello XXXX!

定制实验项目的计划指导

至此,「我们的定制系列」有了一定的发展模式——都是给项目的build sys完整形式,并增强功能(注意构建系统和目标应用程序功能是一对一)。「我们的系列」最后的一个目标是定制出高效的「React项目构建系统」,然而由此(简单交互页)至最后目标(React SPA)之间,还有几个版本的「构建系统」,每个版本都计划“生长”些什么,是没有严格指引的。而我们为了更好为了下一个版本作计划,我们需要这种指引。

构建系统的发展指引

根据我们目前对 WGP理论的认识,对WGP形式的总结,再加上现有的工程经验,我们大略总结了「前端编程」和「前端应用程序」的复杂度增加的模式:

第一,程序功能增加;
第二,程序智能增加,例如有会话;
第三,开发工具(构建系统)的改进,增加抽象提高易用性,例如各种语法糖果🍬;
第四,程序性能优化的要求,例如语法错误检查,数据压缩,安全规范等。

简单交互页项目的计划

有了指引,我们可给当前的项目(定名为「简单交互页」)定性。

参考复杂度增加模式,计划(上面的wgp项目新版本功能计划)中,前三个都属于 「构建系统」改进,最后一则增加「程序功能」;

计划已足,可以准备编写「程序的功能代码」,和「构建系统的构建脚本」了。

创建git 公共开发分支

编写新代码的第一步必须谨记,是为 新功能代码创建本地私有分支。在私有分支上开发和测试,并跟踪版本历史,待功能稳定点再合并到通用分支。这是git 协作分工的铁律。

我们看看当前项目版本分支状况,并尝试创建工作分支:

[keminlau@localhost a-wgp]$ git branch
* master
[keminlau@localhost a-wgp]$ git branch dev
[keminlau@localhost a-wgp]$ git branch
  dev
* master
[keminlau@localhost a-wgp]$ git checkout dev
Switched to branch 'dev'
[keminlau@localhost a-wgp]$ git branch
* dev
  master

以上创建了独立于master分支的开发分支dev,参考最佳的规范,master分支上的版本是产品级,可以立即发布,相对的,dev上的版本则是实验性。其实「项目发展的分支」的定名,数量和方向是没有严格规定的,在正式编写代码以前,我们得先参考一些流行的「版本分支模型」,一些最佳实践。

git版本分支模型

根据项目的性质的不同,目前流行两种不同的版本分支模型:

git flow

  • 第一,git flow;此模型有多条并行的公私分支组成,针对计划性较强的软件项目,适用于定期发布新版本的软件项目;

github flow

  • 第二,github flow;由网站github提出,流行于前端开发领域,针对前端项目需要持续发布(部署)的特性,引入Pull Reuqest概念,提供在线讨论和代码审查功能(一种在git基础上扩展的应用功能),能对需求、及其实现技术进行头脑风暴,非常适用于动态性强,无固定开发计划、具有野蛮生长特性的项目。

wgp项目性质上是一前端项目,但是由它是教学实践性质的,生成无太多野蛮性,所以选择传统的git flow分支模型。

创建私有功能分支

在传统的git flow分支模型上,master和dev是并行的,私有分支应该从dev分出。

查看并在dev分支创建私有分支:fea-simple-UX-page

[keminlau@localhost a-wgp]$ git branch
* dev
  master
[keminlau@localhost a-wgp]$ git branch fea-simple-UI-page 
[keminlau@localhost a-wgp]$ git checkout fea-simple-UI-page 
Switched to branch 'fea-simple-UI-page'
[keminlau@localhost a-wgp]$ 

fea是feature,功能分类;UI是用户交互;

充实build任务

好了,一切准备就绪了,可以编写第一个commit:对项目的数据“动手动脚”,有问题可以随时回滚。

编辑package.json,添加 build构建任务

  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start":"http-server src",
    "build":"mkdir build && cp src/* build",
    "prebuild":"rmdir build"
  },

编辑.gitignore,过滤掉项目会话数据文件

编辑了项目文件,我看查看git状态,发现:

[keminlau@localhost a-wgp]$ git status
On branch fea-simple-UI-page
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

	modified:   package.json

Untracked files:
  (use "git add <file>..." to include in what will be committed)

	a-wgp.sublime-project
	a-wgp.sublime-workspace
	build/

no changes added to commit (use "git add" and/or "git commit -a")
[keminlau@localhost a-wgp]$ 

提示一个跟踪的 package.json被修改过,三项新文件(Untracked files)。由于build数据用于发布的,无需版本跟踪;另外我使用 了sublime管理项目,所以创建了项目会话文件(a-wgp.sublime-project...),暂时不加跟踪。

添加.gitignore过滤规则

*.sublime-project
*.sublime-workspace
build/

再看看git 状态

[keminlau@localhost a-wgp]$ git status
On branch fea-simple-UI-page
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

	modified:   .gitignore
	modified:   package.json

no changes added to commit (use "git add" and/or "git commit -a")
[keminlau@localhost a-wgp]$ 

创建第一个私有commit

[keminlau@localhost a-wgp]$ git add * -f
[keminlau@localhost a-wgp]$ 
[keminlau@localhost a-wgp]$ git commit -m "add build script"
[fea-simple-UI-page 328c732] add build script
 Committer: kemin lau <keminlau@localhost.localdomain>

 5 files changed, 466 insertions(+), 1 deletion(-)
 create mode 100644 a-wgp.sublime-project
 create mode 100644 a-wgp.sublime-workspace
 create mode 100644 build/app.js
 create mode 100644 build/index.html
[keminlau@localhost a-wgp]$ 

充实start 任务

编辑package.json,增加start 任务功能

  "scripts": {
...
    "start":"http-server src -c-1 -o",
...
}

添加了不使用缓存(-c -1),和打开默认浏览器(-o)

创建第二个私有commit

[keminlau@localhost a-wgp]$ git add * -f
[keminlau@localhost a-wgp]$ git commit -m " start script add auto run the browser"

添加热加载功能

热加载功能是指,一边编辑源代码,一边立即能在浏览器上看到效果,非常有用的功能。提供热加载功能的node包有很多,例如 livestyle,livereload,browser-sync,webpack(webpack本身也自带开发服务器,后话)。这里选用最简易的 livereload

安装和配置livereload

$ npm install -g livereload

livereload 支持多种配置和使用使用,这里选用最简易的在交互页(index.html)上添加「客户端脚本」(其它参考官方文档):

<script>
  document.write('<script src="http://' + (location.host || 'localhost').split(':')[0] +
  ':35729/livereload.js?snipver=1"></' + 'script>')
</script>

安装 npm并发执行器 npm-run-all

由于http-server执行后会驻留在内存,不会返回,所以不能使用“&&”将它们串联,必须是并行执行,npm script没有内置这个功能,必须安装 npm-run-all

$ npm install npm-run-all --save-dev

package.josn自动新增了开发依赖项:

  "devDependencies": {
    "npm-run-all": "^4.1.5"
  }

添加构建任务脚本

package.josn:

  "scripts": {
...
    "livereload": "livereload src -w 1000 -d",
    "start": "run-p httpserver livereload",
    "httpserver":"http-server src -c-1 -o",
...
  },

分别将独立两个任务:httpserver和livereload,再并发(run-p )执行它们。

创建第三个私有commit

先查看状态:

[keminlau@localhost a-wgp]$ git status
On branch fea-simple-UI-page
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

	modified:   .gitignore
	modified:   package.json
	modified:   src/index.html

Untracked files:
  (use "git add <file>..." to include in what will be committed)

	node_modules/
	package-lock.json

no changes added to commit (use "git add" and/or "git commit -a")

有两个新的未跟踪项目,node_modules/绝对要排除的,添加过滤项到.gitignore(具体略),再添加package-lock.json跟踪:

$ git add package-lock.json
$ git commit -m " start script add live page reload  func"

版本回滚 git reset

忘记将工作区的修改过的文件(modified)添加到暂存区,commit操作过早了。git原来不会自动识别改过的数据,必须手动记录(添加到l暂存区,构建新的commit形式)。为了减少commit数量,先回滚退回来。版本回滚 (git reset)有三种模式: [图] 按照我们目前的状况,绝对不能用--hard模式,因为这种模式会用前一版本覆盖掉整个工作目录,弄丢数据;--soft和--mixed模式区别不大,都可以用,反正暂存的数据也是来自工作区,暂存区的数据回滚的情况应该不多见。

在reset前先查看前一个commit的id:

$ git log
commit 90ab4b0f2494da8f2f9175b6f100034d1dc3bbaf (HEAD -> fea-simple-UI-page)
Author: kemin lau <keminlau@localhost.localdomain>
Date:   Fri Mar 20 18:55:27 2020 +0800

     start script add live page reload  func

commit 1b9505d1a2e7966ad2cfd7e792cb97363a587bcc
Author: kemin lau <keminlau@localhost.localdomain>
Date:   Fri Mar 20 01:15:49 2020 +0800

     start script add auto run the browser

commit 328c73216cf792cd8f7aab79c3b2186a06be61e5
Author: kemin lau <keminlau@localhost.localdomain>
Date:   Fri Mar 20 00:52:57 2020 +0800

    add build script

commit 170c28971cfb415bd8d52284cbdae0b1ed60695e (origin/master, master, dev)
Merge: 440e5a9 5e7657b
Author: kemin lau <keminlau@localhost.localdomain>
Date:   Sat Mar 14 00:34:52 2020 +0800

执行reset

$ git reset 1b9505d1a2e7966ad2cfd7e792cb97363a587bcc

$ git add * -f
$ git commit -m " start script add live page reload  func"


[keminlau@localhost a-wgp]$ git commit -m " start script add live page reload  func"
[fea-simple-UI-page 61952df]  start script add live page reload  func
 Committer: kemin lau <keminlau@localhost.localdomain

 3 files changed, 505 insertions(+), 4 deletions(-)
 create mode 100644 package-lock.json
[keminlau@localhost a-wgp]$ 

添加交互功能

上一版本只是直接打印出“hello world!”,这一版本加一点点交互:一个greeting,就是用户输入名字,提交后打一声招呼,非常的简单。

交互UI

先编辑index.html,加入交互UI:

<div>
	<div><label>Your Name Pls:</label><input type="text" id="GuestName" name="GuestName"><button id="bt_submit">OK</button></div>
	<div><span id="greeting"></span></div>

</div>

app.js

app.js 是程序的入口,或主体。如果程序比较复杂(有界面智能,有会话,有多个交互步骤等),那app.js 可会使用相当的组织技术,例如IIFE创建全局对象,或抽取出部分通用功能模块,独立为“模块”,在app.js之前加载;由于此版本功能非常的简单,我们可直接将代码写在全局,或者只是一个简单的全局函数(用 函数名 提示它的功能)。当然交互事件处理必须是一个独立的函数。

此版本功能可称为 greeting,程序只有两个部分,一个是view UI的初始化(绑定交互事件处理),另一个是事件处理本身。

// simple greeting page

function appinit(){
	var bt_submit = document.querySelector("#bt_submit");

	bt_submit.addEventListener('click', greet, false);
}
/**
**/
function greet(event){
	var guestinput = document.querySelector("#GuestName");
	var greeting =  document.querySelector("#greeting");
	greeting.innerText = "Hello " + guestinput.value + "!";

}

appinit();

提交私有Commit

$ git add .gitignore 
$ git add src/app.js src/index.html
$ git commit -m "add simple greeting UI"

创建公共Commit

至此,一个完整的wgp程序种类——简单交互页——基本完成,可以合并入公共分支进行 版本跟踪,以此作为下一个版本的基础。而私有分支则可以考虑删除,当然如果是正式项目,可能还需要保留,并进行更严格的测试后,才合并入公共分支,和删除多余的私有分支,此实例项目可忽略。

分支合并的方式

在正式操作之前,我们得先认识几种「分支合并方式」的特点和区别,看下图:

默认是fast-forwrad的快速合并方式,此方式的特点是直接将「当前分支的HEAD」直接移向「待合并分支的HEAD」上,「待并分支」的历史成为了「当前分支」的历史组成,而不能删除。此方式适用需要保留「待并分支」历史的情况。

另两种方式则相反,都会在「当前分支」创建新的历史点,「待并分支」可以删除;不同的地方,--squash将「待并分支」上的历史碎片压缩成一个,--no-off则会保留详细历史。

合并到公共分支dev 和 master

我们希望保留历史详细,所以选择--no-off方式合并。

$ git checkout dev
$ git merge fea-simple-UI-page --no-ff

打tag

现在可以合并到公共主分支,并打上容易标识的tag,方便往后回滚这个本版

$ git checkout master
$ git merge dev --no-ff -m "a simple-UI-page"
$ git tag -a v0.1 -m "simpe UI page"

推上github

[keminlau@localhost a-wgp]$ git push origin master 
Enumerating objects: 30, done.
Counting objects: 100% (29/29), done.
Delta compression using up to 4 threads
Compressing objects: 100% (23/23), done.
Writing objects: 100% (24/24), 8.38 KiB | 572.00 KiB/s, done.
Total 24 (delta 10), reused 0 (delta 0)
remote: Resolving deltas: 100% (10/10), done.
To github.com:nakeman/a-wgp.git
   170c289..7796eb7  master -> master
[keminlau@localhost a-wgp]$ 
[keminlau@localhost a-wgp]$ git push --tag
Enumerating objects: 1, done.
Counting objects: 100% (1/1), done.
Writing objects: 100% (1/1), 169 bytes | 169.00 KiB/s, done.
Total 1 (delta 0), reused 0 (delta 0)
To github.com:nakeman/a-wgp.git
 * [new tag]         v0.1 -> v0.1
[keminlau@localhost a-wgp]$ 

参考

git merge 几种方式对的区别? blog.csdn.net/qq_31024823…