1. 程式人生 > >如何搭建SPA-單頁面應用

如何搭建SPA-單頁面應用

背景

初出社會,剛實習兩個月,在公司做Java開發的時候偶爾使用AngularJS框架寫一些頁面。覺得用起來特別爽。
言歸正傳,為了完成畢設,被迫在工作之餘,研究了AngularJS路由的使用,進而瞭解了SPA應用在使用者體驗上所佔據的優勢。於是從畢設專案中提取出了自己搭建SPA的過程,做了一個小的Demo,在此做記錄。

準備工作

在這個Demo中我使用npm管理框架的依賴包,需要準備的框架分別如下:

npm命令
AngularJS npm install angular
Angular Route npm install angular-route
為了讓頁面看起來爽,還加了妹子UI的框架包 ↓↓↓↓
Amaze UI npm install amazeui
jQuery npm install jquery

所以整個專案的目錄結構如下:

這裡寫圖片描述

注:view放置的是為主頁面引用的模板html程式碼,js放置的是各模板的Controller和整個應用的配置檔案

編寫主頁面

因為是SPA,所以理論上頁面只能有一個,其他的都為主頁面所引用。在主頁中要做的工作就是把應用的整體結構搭建出來,以及引用一些框架資源和Controller。

頁面內容分成了頭、內容、腳,其中內容部分通過路由引用其他html程式碼


main.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>用AngularJS構建SPA</title>
    <link rel="stylesheet" href="../node_modules/amazeui/dist/css/amazeui.min.css"/>
</head>
<body ng-app="spaApp">
    <!--header start 引入頁標頭檔案-->
<section id="header" ng-include="'header.html'"> </section> <!--header end--> <!--content start 使用ng-view顯示路由控制的html模板 --> <section id="content" class="am-container"> <ng-view></ng-view> </section> <!--content end--> <!--footer start 引入頁尾檔案--> <section id="footer" ng-include="'footer.html'"></section> <!--footer end--> <!-- js framework file --> <script src="../node_modules/jquery/dist/jquery.min.js"></script> <script src="../node_modules/amazeui/dist/js/amazeui.min.js"></script> <script src="../node_modules/angular/angular.js"></script> <script src="../node_modules/angular-route/angular-route.js"></script> <!-- js config file 各配置檔案 --> <script src="js/config/app_config.js"></script> <script src="js/config/route_config.js"></script> <!-- js controller file 各html模板控制器 --> <script src="js/controller/headerCtrl.js"></script> <script src="js/controller/footerCtrl.js"></script> <script src="js/controller/pageOneCtrl.js"></script> <script src="js/controller/pageTwoCtrl.js"></script> </body> </html>

編寫頁頭頁尾

頁標頭檔案和頁尾檔案因為是這個應用的基本顯示部分,每個頁面都具有相同的頁頭頁尾,所以不需要路由控制,獨立出header.html和footer.html及他們對應的控制器(當然,如果不需要現實全域性變數,不需要控制器也行),程式碼如下。
header.html

<div ng-controller="headerCtrl">
    <header class="am-topbar">
        <h1 class="am-topbar-brand">
            <a href="#toOne">{{APP_NAME}}</a>
        </h1>
        <button class="am-topbar-btn am-topbar-toggle am-btn am-btn-sm am-btn-success am-show-sm-only" data-am-collapse="{target: '#doc-topbar-collapse'}"><span class="am-sr-only">導航切換</span> <span class="am-icon-bars"></span></button>
        <div class="am-collapse am-topbar-collapse" id="doc-topbar-collapse">
            <ul class="am-nav am-nav-pills am-topbar-nav">
                <li><a href="#toOne">One</a></li>
                <li><a href="#toTwo">Two</a></li>
            </ul>
        </div>
    </header>
</div>

footer.html

<div ng-controller="footerCtrl">
    <footer data-am-widget="footer"
            class="am-footer am-footer-default am-topbar-fixed-bottom"
            data-am-footer="{  }">
        <div class="am-footer-switch">
            <span>{{APP_NAME}}</span>
            <span class="am-footer-divider"> | </span>
            <span>好好學習,天天向上</span>
        </div>
        <div class="am-footer-miscs ">

            <p><a href class="">{{DEVELOPER_NAME}}</a>
                提供技術支援</p>
            <p>2018 </p>
        </div>
    </footer>
</div>

編寫應用主體部分的內容

這裡我簡單使用了兩個頁面pageOne.html、pageTwo.html來模擬單頁面應用的兩個獨立頁面。頁面程式碼以及對於Controller如下:
pageOne.html

<div>
    <h1>I am page One</h1>
    <h2>Here is my data: {{data}}</h2>
</div>

pageOneCtrl.js

spaApp.controller('pageOneCtrl',['$scope', '$rootScope', function($scope, $rootScope){
    $scope.data = "Welcome to page one !";
}]);

pageTwo.html

<div>
    <span class="am-text-xxxl">I am page Two</span>
    <hr>
    <span class="am-text-lg">Here is my data: {{data}}</span>
</div>

pageTwoCtrl.js

spaApp.controller('pageTwoCtrl',['$scope', '$rootScope', function($scope, $rootScope){
    $scope.data = 'Get out from page two !';
}]);

按照期望,點選header中的One應該在頁面上顯示“Welcome to page one !”,而點選Two應該在頁面上顯示“Get out from page two !”。所以接下來要寫的就是將模板程式碼引用到主頁面上的配置。

編寫整體配置檔案

1. 全域性配置

包含了對module的定義、全域性變數的設定
app_config.js

/*
 * 全域性配置檔案
 * 1. 定義module
 * 2. 配置全域性變數
 */
var spaApp = angular.module('spaApp', ['ngRoute']);

spaApp.run(['$rootScope', function($rootScope){
    //通過rootScope把全域性使用的東西配置好,比如應用名
    $rootScope.APP_NAME = 'SPA搭建總結';
    $rootScope.DEVELOPER_NAME = '謝仲東';
}]);

2.路由配置

主要配置路由中各個錨點指向的html檔案和對應的Controller
route_config.js

/*
 * 路由配置檔案
 * 1.配置路由地址和對應的View&Controller
 */
spaApp.config(function ($routeProvider, $locationProvider) {
    //防止路由地址出現感嘆號和亂碼
    $locationProvider.hashPrefix('');

    //當錨點為toOne時,ng-view載入pageOne.html和pageOneCtrl
    //當錨點為toTwo時,ng-view載入pageTwo.html和pageTwoCtrl
    //其餘情況重定向到toOne
    $routeProvider
        .when(
            '/toOne',
            {
                templateUrl: 'view/pageOne.html',
                controller: 'pageOneCtrl'
            }
        ).when(
            '/toTwo',
            {
                templateUrl: 'view/pageTwo.html',
                controller: 'pageTwoCtrl'
            }
        ).otherwise(
            {
                redirectTo: '/toOne'
            }
        )
});

日後開發流程

經過路由的輔助,再也不用使用js往iframe裡面裝東西了。豈不爽哉?搭建完這個應用的整體結構後,接下來的開發就可以分為這幾步:

  • 寫新頁面,例如pageThree.html。
  • 寫控制這個頁面的Controller,例如pageThreeCtrl.js。
  • 在主頁面main.html中引用新的Controller
<script src="js/controller/pageThreeCtrl.js"></script>
  • 去route_config.js 加一個錨點指向設定
.when(
          '/toThree',
          {
              templateUrl: 'view/pageThree.html',
              controller: 'pageThreetrl'
          }
      )
  • 最後在任意頁面的任意位置使用<a>標籤來在應用內改變主體內容。
<a href="#/toThree">Three</a>
  • 愉快地開發吧!

效果圖

這裡寫圖片描述

搭建過程遇到的問題

  1. 跨域問題:因為AngularJS路由使用了Ajax非同步請求,然後將模板注入到主頁中。所以會出現跨域問題。所以我把Demo放在了一個SpringBoot專案中,在tomcat上訪問就不會出現跨域報錯問題。當然也可以放到Apache httpd上,或者在瀏覽器上做手腳。
  2. ng-include指令不生效:原因是ng-include指令的值沒有加單引號,正確的使用方法應該是<div ng-include="'xxx.html'"></div>,粗心。
  3. 有一個缺點就是一個module裡只能使用一個路由,所以如果你主頁上有多個地方要實現路由就要建立多個module,分別進行配置。

專案地址