这几天跟着慕课网上从JS到TS开发数独游戏(JS版)的讲解实现了数独游戏开发,在佩服大神牛批之余,还从中学到了不少关于前端项目开发的经验,主要在前端项目开发流程及实际编程两个方面。
前端项目开发流程
从数独游戏开发中,我发现一个前端项目开发应该遵循这样的流程:
- 确定技术选型和技术路线:选用什么开发语言,选择项目构建工具,这些工具直接如何协作;
- 确定项目的目录结构组织:你的项目有哪几个文件夹,每个文件夹内存放不同类型或者不同功能的文件。这个问题涉及到项目开发效率以及后期项目维护。
- 实际搭建项目基础:主要是构建项目目录结构,安装一些依赖库,配置编译以及自动化构建工具。
- 页面设计及数据结构、算法的确定。
- 实际编码以及单元测试:在不同目录中编写不同功能的代码,以模块化的方式互相调用,每个模块可以单独运行测试。
以数独项目开发为例来说明以上的开发流程。
确定技术选型:
前端三宝HTML5
、CSS
和CSS
用来实现具体的页面和功能。为了能够提高开发效率,CSS
换成更加简洁的LESS
,而JS
采用遵循ES6
规范的Typescript
。在项目实际运行时,LESS
需要编译成CSS
,ES6
也由于其兼容性问题需要运行之前转换成ES5
,这就要求用到一些前端构建工具。
Yarn是一种依赖管理工具,可以用来配置项目依赖,和NPM类似但是快一些。
Gulp是一种基于流的自动化构建工具,通过设置一系列的任务,可以完成项目的编译、监督等工作。其gulp-less插件完成LESS到CSS的编译。
Webpack人如其名就是一个用来打包web应用资源的工具,可以对各类资源做一个归并,提升资源加载的效率和应用安全性。
Babel是Javascript的编译器,项目开发中将ES6规范的JS文件转化为ES5规范的JS文件。
确定技术路线:
首先gulp-less
将.less
文件编译为.css
,然后Webpack
用它的babel-loader
将ES6
转为ES5
并进一步转换为bundle
捆绑文件(由多个js
文件打包成一个)。这些转译以及打包任务都可以在Gulp
的gulpfile.js
中进行配置并通过命令行方式执行。
项目的目录结构
src
目录存放的是源代码,www
存放编译以后实际执行的代码。
安装依赖库
这个项目主要安装和源码编译以及自动化构建相关的工具,命令行如下:
yarn init
yarn add --dev gulp gulp-util
yarn add --dev gulp-less
yarn add -D webpack-stream
yarn add --dev babel-core babel-loader babel-preset-es2015
编译工具以及自动化构建工具配置
下图是gulpfile.js
中配置情况
下图是webpack.config.js
中配置情况
接下来是实际设计开发过程不展开说明。
实际编程的经验
我想说明的是那些之前自己编码的时候没有接触过的,很实用或者很高效的编码技巧。必须要说明的是,以下代码示例均遵循ES6规范。
- 面向对象的思想
ES6
以前的JS
对面向对象支持并不好,遵循ES6
规范的JS
可以面向对象模式编程。面向对象变成最直观的一个体现就是类的概念,我们可以将那些需要反复使用的对象抽象为类,并把对象的功能抽象为类的方法,调用的时候只需要实例化生成新的对象即可。
//JS中类的基本构造如下
Class Object{
constructor(p1){
//构造函数
this.property1 = p1; //this.property说明这是类的属性
this._property2 = p2; //属性前加下划线声明为私有属性,不能直接读写,通过get的方式读取,set方式设置
}
function1(p3){
//这是类的方法函数
this.property3 = p3; //类的属性也可以在方法中指定
return ...
}
get function2(){
return this._property2;
}
}
- 模块化编程的思想
为了可读性和可维护性,采用模块化的方式来编程。无论是一个类,或者一个方法集合都可以发布为一个模块供外部调用。
/**
*一个类的导出导入
**/
//A.js导出
module.exports = class Object{
//TODO
}
//B.js引入并使用
const Object = require("./A.js");
const obj = new Object(p);
obj.function2();
/**
*方法集合的导出导入
**/
//A.js的导出
const functions = {
function1(){
//TODO
},
function2(){
//TODO
}
}
const functions2 = {
function3(){
//TODO
},
function4(){
//TODO
}
}
//以类的形式发布
module.exports = class Toolkit{
static get func1() {
return functions1;
}
static get func2() {
return functions2;
}
}
//B.js引入并使用
const Toolkit = require("./A.js");
this.XXX = Toolkit.func1.function1();
- 数组的遍历
map
方法加上箭头函数,可以让数组的遍历变得非常优雅。当map方法第一项参数只有一个值的时候(如例子中的rowValue
),其表示数组元素值,如果第一项参数有两个值(如例中(cellValue,colIndex)
),其表示数组元素值和其数组下标。
//假设arr是一个二维数组
arr.map(rowValue => rowValue.map((cellValue,colIndex) => {
return rowValue[colIndex] + cellValue;//实际上返回了每个元素的两倍值
}))
结合
jquery
和map
方法的模板生成方法
用$("<span>")
的方式可以生成模板内容,巧妙运用数组和取模符号、选择计算符可以为那些有规律排布的元素赋不同类名。
jquery
实现事件代理
容器中有很多span
,为了避免对每一个span绑定事件,可以在span
的父元素即容器元素中设置事件代理。事件代理的产生与事件传播机制相关:一个点击事件产生后,事件由document
逐级下传到目标元素,目标元素执行相关操作以后,事件冒泡。事件代理可以节省内存,使得代码更易读和后期维护。
this._$container.on("click", "span", e => {
const $cell = $(e.target);
if ($cell.is(".fixed")) {
return;
}
popupNumbers.popup($cell);
})