前段時間作了一個項目,前端開發頁面,而後把代碼給到後端同窗,後端同窗經過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等版本管理工具來迭代項目。有了這種開發方式,你不再用擔憂服務端開發隨時來煩你,本身也有更多時間來鑽研前端自己的技術了,哈哈。