前后端分离之前端项目构建(grunt+require+angular)

时间:2024-01-10 08:10:50

前言

前段时间做了一个项目,前端开发页面,然后把代码给到后端同学,后端同学通过vm再来渲染页面。后来才发现,这种方式简直是太low了,因为前端代码在服务端同学那里,每次前端需要更改的时候都需要去到服务端同学那里修改代码,维护成本高,卖力不讨好==

工作了这么久,深深的感受到了那句名言的魅力,时间就是金钱呀!后来我发现,现在的web开发,谁还用这种低级的方法,大家都已经开始搞前后端分离了

前后端分离的目的和作用

要弄清前后端分离的目的和作用,首先要知道什么是前后端分离。

现在的web前端越来越偏向于独立的技术种类,在不久的将来,服务端的活都会被我们给承包了。我曾经在某个网站读到这样一句话,假如有一个大型网站,例如淘宝网,它肯定不止是一个web项目,而是多个web项目的集合,那么如果前端不作整合、封装,那么不同的项目开发必然会有大量的重复劳动。从这句话和我在前言中举的例子中可以看出,前端开发单独封装组件,单独开发项目,单独维护,前端代码不和服务端逻辑揉在一起,这就是我理解的前后端分离。唯一需要和服务端交互的,就是通过ajax去请求他们提供的接口。

所以,从另一个角度看,我们在开发的时候,只要和服务端约定好接口格式,从项目开工到结束,我们都不需要和服务端开发打交道,这无疑提高了项目质量和开发效率。前后端分离的终极目标应该是前端和服务端是完全独立的项目,一个项目开始之后,前端开发前端的,服务端开发服务端的,并且最后还需要独立部署,这样才真正实现了前后端解耦分离,前后端的沟通主要集中在数据接口的格式上。

前端项目构建

现在要做到前端项目独立,这时候为了项目便于管理维护,我们就需要项目化,工程化,开发规范,自动化压缩混淆,自动化发布,前端优化等等。

现在前端框架这么丰富,一个项目要引入哪些框架,就因人而异了,工作以来一直在学习angular,所以最近自己尝试着搭了一套基于grunt+requireJs+angularJs的应用。

  • grunt: 操作项目文件:比如文件转换、压缩、打包部署等等。

  • requireJs: js库加载管理,支持按需加载,模块化引入。

  • angularJs: js前端MVC框架,支持依赖注入、双向绑定等主要特性。

这几个库是现在中大型前端项目比较适合的搭配,很有必要去学习并掌握他们。

创建项目目录

mkdir myProject
cd myProject

创建项目文件夹,然后进入文件夹,一下操作均在此文件夹下执行。

创建package.json

首先我们需要为npm提供一个package.json,告诉它我们的项目信息,特别是项目中将会使用的插件。

可以用命令生成,后续也能够手动修改。

npm init

他会问我们一些问题,一路使用默认值,创建完毕。

安装grunt

npm install grunt --save-dev

使用npm安装grunt插件,它将被安装到根目录的node_modules文件夹下,所有npm插件都会放到这里。

--save-dev: 意思是安装插件的同时,也把它添加到项目信息文件package.json中的devDependencies字段里,意思是这个项目依赖于这些插件。下次使用npm install的时候就会自动安装这些插件。

为grunt创建配置文件Gruntfile.js

Gruntfile.js文件用于定义任务、任务组。它可以用来执行文件的类型转换、压缩、合并等等操作,为开发大大提高了效率。

  • 安装grunt-init

    npm install grunt-init -g      //全局安装grunt-init
  • 下载grunt模板

    git clone https://github.com/gruntjs/grunt-init-gruntfile.git ~/.grunt-init/gruntfile
  • 生成Gruntfile

    grunt-init gruntfile

    在项目根目录下生成Gruntfile.js文件,跟package.json文件一下,按需回答一些文件,就创建好了,后续能手动修改(其实大部分都是自己手写的)。

安装bower

bower是用来管理js库的一个工具,比如下载jquery、angularjs等库。并且下载的时候还能指定库的版本。

同样适用npm进行安装。

npm install bower -g

为bower生成配置文件bower.json

整个项目的信息文件是package.json,执行任务插件grunt的信息文件是Gruntfile.js,那么bower当然也有自己的信息文件了,那就是bower.json

bower init

不过我觉得bower.json基本没有什么作用,它最大的作用就是用来下载我们需要的各种技术库。

比如使用bower下载angularjs:

bower install angularjs

这行命令将会把angular下载下来,放在根目录下的bower_components文件夹下,不过它默认下载的是angular的最新版,有时候,我们可能不需要最新版。假如,我想下载angularjs 1.2.2版本该怎么办呢?

bower install angularjs#1.2.2

另外,bower只负责下载文件到bower_components目录下,但是我们项目中可能并不想把库放在这里,所以我们可以选择使用grunt的插件grunt-bower-task,在Gruntfile.js中定义一个任务来移动文件到想要的目录下。

关于Requirejs

官网上是这样说的:

RequireJS的目标是鼓励代码的模块化。

它使用了不同于传统的脚本加载步骤。可以用它来加速、优化代码,但其主要目的还是为了代码的模块化,按需加载。

使用Requirejs

<script data-main="scripts/main" src="scripts/require.js"></script>

一般在首页加载requiejs文件,然后属性data-main指定的文件就是接下来要加载的文件,然后我们再看main.js文件:

require.config({
baseUrl: 'script/lib',
paths: {
app: 'app',
jquery: '/jquery/jquery-min',
angular: '/angularjs/angular-min'
},
shim: {
'angular': {
exports: 'angular'
}
}
});
require([
'app'
], function(app) (
app.hello();
))
  • 我们在paths中声明了3个模块,app、jquery和angular,后面的路径是模块对应的文件路径。

  • shim中用来处理一些没有遵守requirejs规范的js库,比如angularjs库,所以要手动配置一个叫angular的模块。

  • 最后用require来导入我们自己的模块,可在后面的callback中拿到对应模块的实例,并对它进行一些操作,比如我们调用了app.hello()方法。

AngularJs实例

上面的步骤只是简单说了下大体步骤,纸上谈兵之后,就可以开始实际操作了。

目录结构

项目文件夹结构如下图(当然目录结构因人而异):

前后端分离之前端项目构建(grunt+require+angular)

  • node_modules: 用来存放项目依赖的grunt插件。

  • bower_components: 用来存放bower下载的库。

  • build: 用来存放经过build处理之后的js文件。

  • app: 是真正的开发需要的文件。

    • data: 用来存放mock数据用的json文件。

    • images: 用来存放静态图片。

    • scripts: 用来存放所有js文件。

      • controller: 用来存放控制器文件。

      • directive: 定义的指令文件。

      • filter: 定义的过滤器文件。

      • route: 路由文件。

      • service: 服务文件。

      • vendor: 公共库文件,比如angular,jquery,bootstrap等等。

    • styles目录用来存放样式文件。

    • views目录用来存放页面html文件。

    • partials目录用来存放页面片段的html文件。

目录创建好了,现在来写一些代码,让项目能够运行起来。

views/index.html

<html>
<head>
<meta charset="UTF-8">
<title>grunt+requirejs+angular项目构建</title>
<script src="../scripts/vendor/requirejs/require.js" data-main="../scripts/config"></script> <!-- 先引入requirejs, 然后引入config.js -->
</head>
<body>
<div ng-controller="testController">
{{app}}
</div>
</body>
</html>

上述index中,先引入了require.js,然后加载data-main对应的config.js文件。

scripts/config.js

var vendorPath = 'vendor/';
require.config({ //配置模块
baseUrl: '../scripts/',
paths: {
'app': 'app',
'angular': vendorPath + 'angular/angular',
'jquery': vendorPath + 'jquery/jquery'
},
shim: { //处理没有遵守requirejs规范的js库
'angular': {
exports: 'angular'
}
}
});
require(['./bootstrap'], function(bootstrap) {
// ...
});

然后通过require引入了bootstrap.js文件。

scripts/bootstrap.js

define([
'angular',
'app',
'jquery',
'./controller/_base'
], function(angular) {
// 手动将angular模块绑定到document对象
angular.element(document).ready(function() {
angular.bootstrap(document, ['myProject']);
});
});

注意:function的内容是requirejs引入所有文件之后的回调函数。

bootstrap是angular对象的一个方法,用于手动启动。上述代码除了引入了angular等js库外,还引入了app.js文件。

scripts/app.js

define([
'angular',
'./controller/controllers'
], function(angular) {
//定义将要绑定到document上的模块名称
return angular.module('myProject', ['projectController']);
});

定义模块名myProject的时候,同时指定它依赖于另一个模块projectController

同时在./controller/controllers.js中定义这个模块:

define(['angular'], function(angular) {
return angular.module('projectController', []);
});

这里是给所有的控制器定义了一个单独的模块,然后让主要模块依赖于这个模块,这样做的目的是方便管理维护,我们还可以给所有的服务、路由、过滤器都添加一个单独的模块,然后在app.js中添加注入即可。

controller/testController.js

如果想要让视图文件views/index.html能够看到效果,我们就要开始编写控制器文件了,

define([
'./controllers'
], function(mod) {
var controllerFn = function($scope, $location) {
$scope.app = 'hello, world';
};
controllerFn.$inject = ['$scope', '$location'];
mod.controller('testController', controllerFn);
});

引入控制器的单独模块文件'./controllers',然后在模块上新建一个名为testController的控制器,并在$scope上绑定了一个值为‘hello, world’的变量app

查看效果

使用python -m SimpleHTTPServer启动一个简单的web服务器,默认监听8000端口。在浏览器输入正确地址就能看到效果了。

hello, world

结语

现在,我们需要做的就是,和服务端约定好接口格式,自己根据格式mock需要的数据,然后,我们可以随心所欲的开发我们的项目了,不用再和人打交道了。开发完毕之后,再通过grunt压缩、合并、打包文件等操作,暴露给外部的只需一个js文件,另外还可以通过git等版本管理工具来迭代项目。有了这种开发方式,你再也不用担心服务端开发随时来烦你,自己也有更多时间来钻研前端本身的技术了,哈哈。