前段时间作了一个项目,前端开发页面,而后把代码给到后端同窗,后端同窗经过vm再来渲染页面。后来才发现,这种方式简直是太low了,由于前端代码在服务端同窗那里,每次前端须要更改的时候都须要去到服务端同窗那里修改代码,维护成本高,卖力不讨好==html
工做了这么久,深深的感觉到了那句名言的魅力,时间就是金钱呀!后来我发现,如今的web开发,谁还用这种低级的方法,你们都已经开始搞先后端分离了
!前端
要弄清先后端分离的目的和做用,首先要知道什么是先后端分离。node
如今的web前端愈来愈偏向于独立的技术种类,在不久的未来,服务端的活都会被咱们给承包了。我曾经在某个网站读到这样一句话,假若有一个大型网站,例如淘宝网,它确定不止是一个web项目,而是多个web项目的集合,那么若是前端不做整合、封装,那么不一样的项目开发必然会有大量的重复劳动。从这句话和我在前言中举的例子中能够看出,前端开发单独封装组件,单独开发项目,单独维护,前端代码不和服务端逻辑揉在一块儿,这就是我理解的先后端分离。惟一须要和服务端交互的,就是经过ajax去请求他们提供的接口。python
因此,从另外一个角度看,咱们在开发的时候,只要和服务端约定好接口格式,从项目开工到结束,咱们都不须要和服务端开发打交道,这无疑提升了项目质量和开发效率。先后端分离的终极目标应该是前端和服务端是彻底独立的项目,一个项目开始以后,前端开发前端的,服务端开发服务端的,而且最后还须要独立部署,这样才真正实现了先后端解耦分离,先后端的沟通主要集中在数据接口的格式上。jquery
如今要作到前端项目独立,这时候为了项目便于管理维护,咱们就须要项目化,工程化,开发规范,自动化压缩混淆,自动化发布,前端优化等等。git
如今前端框架这么丰富,一个项目要引入哪些框架,就因人而异了,工做以来一直在学习angular,因此最近本身尝试着搭了一套基于grunt+requireJs+angularJs的应用。angularjs
grunt: 操做项目文件:好比文件转换、压缩、打包部署等等。github
requireJs: js库加载管理,支持按需加载,模块化引入。web
angularJs: js前端MVC框架,支持依赖注入、双向绑定等主要特性。ajax
这几个库是如今中大型前端项目比较适合的搭配,颇有必要去学习并掌握他们。
mkdir myProject cd myProject
建立项目文件夹,而后进入文件夹,一下操做均在此文件夹下执行。
首先咱们须要为npm提供一个package.json,告诉它咱们的项目信息,特别是项目中将会使用的插件。
能够用命令生成,后续也可以手动修改。
npm init
他会问咱们一些问题,一路使用默认值,建立完毕。
npm install grunt --save-dev
使用npm安装grunt插件,它将被安装到根目录的node_modules
文件夹下,全部npm插件都会放到这里。
--save-dev
: 意思是安装插件的同时,也把它添加到项目信息文件package.json
中的devDependencies
字段里,意思是这个项目依赖于这些插件。下次使用npm install
的时候就会自动安装这些插件。
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是用来管理js库的一个工具,好比下载jquery、angularjs等库。而且下载的时候还能指定库的版本。
一样适用npm进行安装。
npm install bower -g
整个项目的信息文件是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的目标是鼓励代码的模块化。
它使用了不一样于传统的脚本加载步骤。能够用它来加速、优化代码,但其主要目的仍是为了代码的模块化,按需加载。
<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()
方法。
上面的步骤只是简单说了下大致步骤,纸上谈兵以后,就能够开始实际操做了。
项目文件夹结构以下图(固然目录结构因人而异):
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文件。
目录建立好了,如今来写一些代码,让项目可以运行起来。
<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文件。
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文件。
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文件。
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中添加注入便可。
若是想要让视图文件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等版本管理工具来迭代项目。有了这种开发方式,你不再用担忧服务端开发随时来烦你,本身也有更多时间来钻研前端自己的技术了,哈哈。