AngularJS路由

本文是自己學(xué)習(xí)angularJS的第一篇總結(jié),并不是一個(gè)step by step 的教程,而是將自己學(xué)習(xí)過程中有疑惑的地方記錄下來??紤]到之后的AngularJS總結(jié)也將依賴這個(gè)應(yīng)用,因此本文會(huì)在前面兩個(gè)章節(jié)具體的介紹應(yīng)用搭建過程,在之后的總結(jié)中,將不再涉及應(yīng)用搭建的內(nèi)容。

路由是前端構(gòu)建單頁(yè)面應(yīng)用(SPA)必不可少的一部分。AngularJS中有兩種路由實(shí)現(xiàn),一個(gè)是內(nèi)置的ngRouter,還有一個(gè)是基于 ngRoute 開發(fā)的第三方路由模塊uiRouter。考慮到目前主要使用的是功能更強(qiáng)大uiRouter模塊,因此本文將主要介紹uiRouter的使用。
需要注意的是,AngularJS1.2以后做了模塊化的處理,也就是路由沒有包含在Angular.js這個(gè)文件里面,而是把它獨(dú)立出來成了一個(gè)模塊。也就是說,不管是 使用ngRouter還是uiRouter,都需要單獨(dú)引入模塊文件。ngRouter對(duì)應(yīng)的外部 js文件是 angular-route.js,uiRouter對(duì)應(yīng)的外部文件是angular-ui-router.js。引入模塊的方式有兩種,一種是直接在index.html中中引入文件模塊文件,這種方式比較簡(jiǎn)單便捷,甚至可以不用單獨(dú)下載,直接引入cdn地址,如下:

<script src="http://cdn.static.runoob.com/libs/angular.js/1.4.6/angular.min.js"></script>
 <script src="http://apps.bdimg.com/libs/angular-route/1.3.13/angular-route.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.1.5/angular.min.js"></script>
這里的地址是分別從不同的地方找過來的,有出入可以在網(wǎng)上查查

還有一種方式是使用npm包管理器安裝依賴。為了響應(yīng)前端工程化開發(fā),這里使用npm包管理器安裝依賴,考慮到有些同學(xué)坑沒有接觸了模塊化,詳細(xì)的介紹以下npm配合webpack搭建前端應(yīng)用的過程。

新建應(yīng)用

創(chuàng)建一個(gè)項(xiàng)目并安裝依賴。

  • 利用npm 初始化一個(gè)項(xiàng)目。
mkdir routerStart
cd routerStart
npm init
  • 安裝依賴
查看angular 版本: npm view angular version
根據(jù)查出來的版本進(jìn)行安裝  
安裝angular :npm install --save angular@1.6.6
安裝angular-route:npm install --save angular-route@1.6.6
安裝@uirouter/angularjs:npm install --save @uirouter/angularjs@1.0.10

也可以直接在這個(gè)網(wǎng)站查看各個(gè)包的信息:npm包管理器

啟動(dòng)應(yīng)用

以上已經(jīng)安裝好了最重要的三個(gè)依賴包,但為了能夠更方便便捷的進(jìn)行測(cè)試,再引入一個(gè)概念:webpack??梢园褀ebpack看作是一個(gè)組織模塊的框架,它的作用是:分析你的項(xiàng)目結(jié)構(gòu),找到JavaScript模塊以及其它的一些瀏覽器不能直接運(yùn)行的拓展語(yǔ)言(Scss,TypeScript等),并將其轉(zhuǎn)換和打包為合適的格式供瀏覽器使用。有關(guān)于webpack的詳細(xì)講解配置,webpack

接下來新建幾個(gè)文件:index.html、index.js、webpack.config.js。其中index.htm作為在瀏覽器上展示的界面,index.js是index.html中用到的腳本,webpack.config.js是webpack的配置文件,各個(gè)文件的內(nèi)容如下:

//package.json配置文件
{
  "name": "routerstart",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "webpack"
  },
  "author": "spilledyear",
  "license": "ISC",
  "dependencies": {
    "angular": "^1.6.6",
    "angular-route": "^1.6.6",
    "@uirouter/angularjs": "^1.0.10",
    "webpack": "^3.8.1"
  }
}
//webpack.config.js
module.exports = {
  entry: './index.js',
  output: {
    filename: './bundle.js',
  },

  module: {
    rules: [{
      // test: /\.less$/,
      // use: [{
      //   loader: "style-loader"  // creates style nodes from JS strings
      // }, {
      //   loader: "css-loader"    // translates CSS into CommonJS
      // }, {
      //   loader: "less-loader"   // compiles Less to CSS
      // }]
    }]
  }
};
//index.js
import angular from 'angular';
import ngRoute from 'angular-route';
import uiRouter from '@uirouter/angularjs';

var app = angular.module("app", []) 

  .controller("appCtrl",function($scope){
      $scope.name = "hello!";
  });
index.html
<html ng-app="app" ng-controller="appCtrl"> 
  <head>
    <title>test</title>
    <meta charset="utf-8">  
  </head>   
 <body> 
    <input ng-model="name"/>
    <h1>{{name}}</h1>

        <script src="bundle.js"></script> 
</body> 
</html>

需要注意index.html中的這部分內(nèi)容

<script src="bundle.js"></script> 

并沒有直接引用index.js文件,而是引入了bundle.js文件?這里會(huì)讓人產(chǎn)生疑惑,為什不引入index.js文件?bundle.js文件從哪里來?這也是前端模塊化工程與非模塊化工程的主要區(qū)別,在非模塊化工程中,習(xí)慣性的將所有依賴的js框架引入到html界面,這種做法造成的后果就是項(xiàng)目結(jié)構(gòu)組織混亂、文件依賴關(guān)系錯(cuò)綜復(fù)雜,代碼功底不夠還會(huì)讓各種全局滿天飛。模塊化很好的解決了這些問題,想知道模塊化更具體的細(xì)節(jié),請(qǐng)自行g(shù)oogle。而這里引入的bundle.js文件,其實(shí)和webpack有關(guān),如果此時(shí)沒有利用webpack做任何事情,僅僅是用瀏覽器打開index.html文件,那么肯定是可以在控制臺(tái)看到報(bào)錯(cuò)信息的,此時(shí)的bundle.js文件是不存在的。在webpack的配置文件中,有這么一段內(nèi)容:

 entry: './index.js',
 output: {
    filename: './bundle.js',
  },

根據(jù)字面的意思,應(yīng)該可以猜到這段代碼的用意,事實(shí)也是如此,entry和output是webpack中的兩個(gè)概念,entry作為整個(gè)應(yīng)用的入口文件,webpack會(huì)找到入口文件,然后根據(jù)這個(gè)文件里面的內(nèi)容依次加載其它依賴的模塊,然后進(jìn)行編譯等操作,最后將所有編譯后結(jié)果輸出到output 配置的輸出文件中,文中配置的是bundle.js,這就是為什么只需要在index.html引入bundle.js的原因。如果你有仔細(xì)觀察bundle.js文件的話,會(huì)發(fā)現(xiàn)這個(gè)文件很大(最少有1M),這就很好的說明了它是編譯后的產(chǎn)物。
還有一個(gè)需要注意的地方是package.json文件

  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "webpack"
  },

在scripts腳本中配置了start命令,大概就是說在執(zhí)行npm start的時(shí)候相當(dāng)于執(zhí)行了 webpack命令。具體的細(xì)節(jié)還請(qǐng)參照這篇文章:webpack

接下來是啟動(dòng)過程

在命令行以下執(zhí)行
npm start
如果一切正常,會(huì)在命令行下得到以下信息


image.png

這時(shí)候再觀察應(yīng)用目錄結(jié)構(gòu),會(huì)發(fā)現(xiàn)根目錄下多了一個(gè)bundle.js文件,此時(shí)完全可以把 npm start 當(dāng)作是一個(gè)編譯過程。
準(zhǔn)備工作完畢,直接在瀏覽器下打開index.html文件,運(yùn)行結(jié)果正常


image.png

如果模塊化工程開發(fā)過程是這樣的,那也太麻煩了吧??赡苓@樣嗎?不存在的。想使用更強(qiáng)大的功能,只需要修改webpack配置文件就可以了,比如開發(fā)中最期待的熱部署??紤]到之后的許多AngularJS特性都需要利用這個(gè)應(yīng)用來測(cè)試,在此貼出webpack比較全面的配置文件(以后可能還會(huì)添加),想了解詳情請(qǐng)?zhí)D(zhuǎn)上文中提到的鏈接

//webpack.congfigjs
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: './app/index.js',
  output: {
    path: __dirname + "/dist",
    filename: "app.js"
   },
  devtool: 'eval-source-map',
  devServer: {
    port: 9000,                              //端口
    contentBase: "./app",            //本地服務(wù)器所加載的頁(yè)面所在的目錄
    historyApiFallback: true,     //不跳轉(zhuǎn)
    inline: true                              //實(shí)時(shí)刷新
  },
  module: {
    rules: [
    {
      test: /(\.jsx|\.js)$/,
      use: {
        loader: "babel-loader"
      },
      exclude: /node_modules/
    },

    {
      test: /\.less$/,
      use: [{
        loader: "style-loader"  // creates style nodes from JS strings
      }, {
        loader: "css-loader",    // translates CSS into CommonJS
        options: {
          modules: true
        }
      }, {
        loader: "less-loader"   // compiles Less to CSS
      }]
    }]
  },
  plugins: [
    //new 一個(gè)這個(gè)插件的實(shí)例,并傳入相關(guān)的參數(shù)
    new webpack.BannerPlugin('版權(quán)所有,翻版必究'),
    new HtmlWebpackPlugin({
      template: __dirname + "/app/index.tmpl.html"    
    })
  ],
};

同時(shí),應(yīng)用的目錄結(jié)構(gòu)也做了相應(yīng)的調(diào)整,如下:

image.png

app目錄下存放的是源文件,dist下是編譯后的輸出文件。其它幾個(gè)配置文件的配置信息如下:

//.babelrc
{
  "presets": ["es2015", "stage-0"]      //babelr支持的模塊
}
//package.json
{
  "name": "routerstart",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "webpack-dev-server --open",
    "webpack": "webpack"
  },
  "author": "spilledyear",
  "license": "ISC",
  "dependencies": {
    "angular": "^1.6.6",
    "angular-route": "^1.6.6",
    "@uirouter/angularjs": "^1.0.10",
    "bootstrap": "^3.3.7"
  },
  },
  "devDependencies": {
    "babel-core": "^6.26.0",
    "babel-loader": "^7.1.2",
    "babel-preset-es2015": "^6.24.1",
    "babel-preset-stage-0": "^6.24.1",
    "html-webpack-plugin": "^2.30.1",
    "webpack": "^3.8.1",
    "webpack-dev-server": "^2.9.3"
  }
}

工作完畢,命令行下直接執(zhí)行以下命令

npm start

瀏覽器自動(dòng)打開,結(jié)果和剛才的一樣,同時(shí),修改源代碼,瀏覽器也會(huì)即時(shí)刷新。
注意,雖然應(yīng)用運(yùn)行正常,但是這里還有些問題。從package.json可以看出目前配置了兩個(gè)命令,一個(gè)是 npm start,另一個(gè)是 npm run webpack。其中npm run webpack 完全是用來打包的, npm start 配置的是webpacke-dev,是為了方便調(diào)試。但是angularJS開發(fā)和react開發(fā)是有不同的,這里也沒有用JSX語(yǔ)法,這就意味著需要將angularJS中的一些html文件搬到編譯后的目錄(dist),可能需要用gulp,為了不再增加應(yīng)用的復(fù)雜性,這里不再引入gulp相關(guān)內(nèi)容。因此,再之后的開發(fā)過程中,不需要考慮 npm run webpack 這個(gè)命令。如果需要打包,可能需要將app目錄下的一些文件手動(dòng)拷貝到dist目錄下。

uiRouter

uiRouter是基于 ngRoute 開發(fā)的第三方路由模塊,功能比ngRoute 強(qiáng)大,使用方式也比較簡(jiǎn)答,因此先介紹uiRouter。當(dāng)然,最好的文檔還是官方文檔:ui-router ,例如:

image.png

uiRouter的首頁(yè)就詳細(xì)的介紹了與ngRoute的關(guān)系以及使用方式。通過api文檔API可以發(fā)現(xiàn),uiRouter 中存在這么幾個(gè)概念 。

image.png

接下來介紹使用最多的幾個(gè)。
模塊
ui.router

服務(wù)
urlRouterProviderstateProvider

指令
ui-sref
ui-view

首先需要明白“state” 是路由中非常重要的一個(gè)概念,在使用過程中,可以為每個(gè)state分配所對(duì)應(yīng)的模塊(界面),當(dāng)需要改變界面時(shí),只需要找到對(duì)應(yīng)界面(模板)的那個(gè)state,最后uiRouter將模板加載到模板容器(ui-view),用戶就可以在界面看到想要的效果。

urlRouterProvider起著監(jiān)視location服務(wù)的作用。用官方的一段原話:

urlRouterProvider has the responsibility of watchinglocation. When location changes it runs through a list of rules one by one until a match is found.urlRouterProvider is used behind the scenes anytime you specify a url in a state configuration. All urls are compiled into a UrlMatcher object (see urlMatcherFactory below). 大概就是說當(dāng)location發(fā)生改變的時(shí)候,urlRouterProvider將會(huì)采用遍歷的方式找到發(fā)生變化的那個(gè)選項(xiàng)。因?yàn)樵谂渲胹tate的時(shí)候是會(huì)綁定url的,而這個(gè)過程其實(shí)使用到了urlRouterProvider。

stateProvider用于管理state和控制state狀態(tài)直接的轉(zhuǎn)換,進(jìn)而通過改變state改變界面效果。它提供了一些觸發(fā)事件和回調(diào)函數(shù),異步解析目標(biāo)狀態(tài)的任何依賴項(xiàng),并更新 location以反映當(dāng)前狀態(tài)。

ui-sref指令

Equivalent to href or ng-href in <a /> elements except the target value is a state name. Adds an appropriate href to the element according to the state associated URL.
可以作用在<a>標(biāo)簽上,它指是一個(gè) state 對(duì)應(yīng)的值。當(dāng)點(diǎn)擊鏈接的時(shí)候,就會(huì)找到state對(duì)應(yīng)的URL,進(jìn)而導(dǎo)航到對(duì)應(yīng)的URl。

ui-view指令

Renders views defined in the current state. Essentially ui-view directives are (optionally named) placeholders that gets filled with views defined in the current state.
可以看作時(shí)一個(gè)界面的容器。通過路由加載的界面,都必須放到一個(gè)或者多哥ui-view容器中。它最主要的作用是呈現(xiàn)當(dāng)前狀態(tài)下定義的視圖,也就意味著如果沒有定義ui-view,當(dāng)前state對(duì)應(yīng)的視圖就沒辦法展現(xiàn),在界面上就看不到任何效果。

接下來通過一個(gè)實(shí)例來講解uiRouter的使用方式。

//index.js
import angular from 'angular';
import ngRoute from 'angular-route';
import uiRouter from '@uirouter/angularjs';

var app = angular.module("app", [uiRouter]) 
  .config(["$stateProvider", "$urlRouterProvider",
    function ($stateProvider, $urlRouterProvider) {
        $stateProvider
          .state("app", { 
            url: "/",
            templateUrl: "index.html"
          })
        .state("page01", { 
          url: "/page01",
          templateUrl: "template/page01.html"
        })
        .state("page02", { 
          url: "/page02",
          templateUrl: "template/page02.html"
        })
        .state("page03", { 
          url: "/page03",
          templateUrl: "template/page03.html"
        })
    }
  ])
  .controller("appCtrl",function($scope, $state){
    $scope.name = "hello!";
    $scope.goPage = function(){
      $state.go('page03');
    }
  });
//index.html
<html ng-app="app" ng-controller="appCtrl"> 
  <head>
    <title>test</title>
    <meta charset="utf-8">  
  </head>   
 <body> 
    
    <div style="display: flex;flex-direction: row;justify-content: space-between;">
        <div style="">
            <div>
                <a ui-sref="page01">page01</a>  
            </div>

            <div>
                <a ui-sref="page02">page02</a>  
            </div>

            <div>
                <button ng-click="goPage()">page03</button>  
            </div>
        </div>

        <div class="item">
            <ui-view></ui-view>
        </div>
    </div>

    <script type="text/javascript" src="index.js"></script>
 </body> 
</body> 
</html>

從上可以看出,在index.js中定義了4個(gè)state, $stateProvider的state方法接收兩個(gè)參數(shù),第一個(gè)參數(shù)是 “state”的名稱,第二個(gè)參數(shù)是“state"相關(guān)的配置,其中包括url和對(duì)應(yīng)的視圖模板(界面)。

image.png

同時(shí),在index.html中,使用了ui-serf指令。如上所述,這個(gè)指令的作用其實(shí)和$state.go('xxx')是一樣的,都是為了跳轉(zhuǎn)到對(duì)應(yīng)state的界面。通過改變state的指向從而改變ui-view 模板容器渲染的內(nèi)容。
為了看到界面效果,還需要在app目錄下添加一個(gè)template目錄,這個(gè)目錄的作用就是用來存放視圖模板(界面、html文件)。因此,最終的目錄結(jié)構(gòu)如下:

image.png

執(zhí)行 npm start 命令,在瀏覽器查看效果;

初始界面效果圖如下


image.png

點(diǎn)擊相應(yīng)的按鈕,觀察瀏覽器地址欄的變化:


image1.png

image2.png

image3.png

可以很容易的發(fā)現(xiàn),地址欄在變,界面右側(cè)區(qū)域的內(nèi)容也在變化,這就是一個(gè)最基本的路由系統(tǒng)。在此之余,簡(jiǎn)單的分析其中的一些細(xì)節(jié)和路由過程。


image.png

從上圖可以看出,此處有兩種路由實(shí)現(xiàn),ui-sref 和 添加一個(gè)單擊事件,然后在單擊事件中完成邏輯處理。

簡(jiǎn)單看看 goPage() 實(shí)現(xiàn):
$scope.goPage = function(){
  $state.go('page03');
}

調(diào)用的就是 $state.go('x'x'x') 方法。

路由過程
點(diǎn)擊對(duì)應(yīng)鏈接,觸發(fā) $state.go('x'x'x') 方法,uiRouter根據(jù)state找到對(duì)應(yīng)的視圖并加載在ui-view容器中,實(shí)現(xiàn)界面的變化。一定要注意,是將視圖模板加載到 ui-view容器中,沒有ui-view容器,是不能夠成功顯示對(duì)應(yīng)的模板內(nèi)容的,這點(diǎn)必須要記住。

父子路由

父子路由實(shí)現(xiàn)的方式也比較簡(jiǎn)單。對(duì)index.js 稍作修改

import angular from 'angular';
import ngRoute from 'angular-route';
import uiRouter from '@uirouter/angularjs';

var app = angular.module("app", [uiRouter]) 
  .config(["$stateProvider", "$urlRouterProvider",
    function ($stateProvider, $urlRouterProvider) {
        $stateProvider
          .state("app", { 
            url: "/",
            templateUrl: "index.html"
          })
        .state("page01", { 
          url: "/page01",
          templateUrl: "template/page01.html"
        })
        .state("page02", { 
          url: "/page02",
          templateUrl: "template/page02.html"
        })
        .state("page03", { 
          url: "/page03",
          templateUrl: "template/page03.html"
        })
        .state("page03.children", { 
          url: "/children",
          views: {
            'children': {
              templateUrl: "template/children.html"
            }
          }
        })
    }
  ])
  .controller("appCtrl",function($scope, $state){
    $scope.name = "hello!";
    $scope.goPage = function(){
      $state.go('page03');
    }
  });

注意觀察這一部分內(nèi)容:

 .state("page03.children", { 
      url: "/children",
      views: {
        'children': {
             templateUrl: "template/children.html"
        }
     }
})
這是實(shí)現(xiàn)父子路由的關(guān)鍵部分,”page03.children“ ,以”."作為分隔符,代表“page03"作為一個(gè)父界面, 
新的界面作為”page03"對(duì)應(yīng)的一個(gè)子界面。同時(shí),需要對(duì)page03.html文件做修改,page03.html文件內(nèi)容如下:
//page03.html
<html> 
  <head>
    <title>page03</title>
    <meta charset="utf-8">      
  </head>   
 <body> 
    <div>
        <h1>hello, page03</h1>
        <a ui-sref="page03.children">children</a>  

        <div>
            chirdren content area
            <ui-view name="children"></ui-view>
        </div>
    </div>
</body> 
</html>
image.png

同時(shí),在template目錄下添加一個(gè)新html文件: children.html,作為 page03的子界面。

// children.html
<html> 
  <head>
    <title>children</title>
    <meta charset="utf-8">  
  </head>   
 <body> 
    <div>
        <h1>hello, children</h1>
    </div>
</body> 
</html>

查看界面效果:

page03.png

chirldre.png

觀察瀏覽器地址欄變化,觀察界面變化。

Angular4

設(shè)置環(huán)境信息

node:8.x
npm:5.x
angular:4.x

全局安裝 Angular CLI

npm install -g   @angular/cli

創(chuàng)建應(yīng)用

Angular CLI 是一個(gè)腳手架工具,功能非常強(qiáng)大,對(duì)開發(fā)非常便利。這里直接利用 Angular CLI 創(chuàng)建一個(gè)新應(yīng)用。

ng new ngStart

執(zhí)行以上命令就會(huì)在當(dāng)前目錄下新建一個(gè)名為 “ngStart” 的angular4項(xiàng)目。

啟動(dòng)應(yīng)用

cd ngStart
ng serve --open

啟動(dòng)成功。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,094評(píng)論 25 709
  • Angular CLI 是什么? Angular CLI 是一個(gè)命令行接口(Command Line Interf...
    semlinker閱讀 4,330評(píng)論 0 39
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,578評(píng)論 19 139
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 47,275評(píng)論 6 342
  • 每當(dāng)我想起你 總會(huì)想起那一座孤墳 不知道今天 有沒有人給你捧幾捧新土 我習(xí)慣了逆著光仰望 想看一看你現(xiàn)在居住的地方...
    廣意_閱讀 399評(píng)論 0 3

友情鏈接更多精彩內(nèi)容