SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

LINEフロントエンドレンジャーのWeb開発術

SEO検証! GoogleはAngularJSを正しくクロールするのか?(2015年2月版)

LINEフロントエンドレンジャーのWeb開発術 第3回


  • X ポスト
  • このエントリーをはてなブックマークに追加

検証に利用したAngularJSアプリのコードについて

基本コード

 今回は検証が目的だったため、単純なシングルページアプリケーションを作成しました。下記のコードはサンプルです。実際に検証したコードと構造は同様ですが、細かい機能はすべて省略しています。

HTMLファイル

sample.html

 形式としては一つのHTMLファイル内にng-templateを使い、各種テンプレートを記載しています。

<!DOCTYPE html>
<html ng-app="sampleApp">
    <head>
        <title>{{$root.title}}|sample site</title>
        <meta name="description" content="{{$root.description}}">
        <!--OGP TwitterCard の設定-->
        <link href="http://example.com/css/common.css" type="text/css" rel="stylesheet">
    </head>
    <body>
    
        <div ng-view></div>
        
        <!--
        =============================================
        list pageのtemplate
        =============================================
        -->
        <script id="listCtrl.html" type="text/ng-template">
            <section>
                <h1>サンプル一覧 No1</h1>
                <ul ng-repeat="item in items">
                    <li><a href="#/end/{{item.id}}/">{{item.name}}</a></li>
                </ul>
            </section>
        </script>
        
        <!--
        =============================================
        End pageのtemplate
        =============================================
        -->
        <script id="endCtrl.html" type="text/ng-template">
            <section>
                <h1>{{item.name}}</h1>
                <img ng-src="{{item.img}}" alt="{{item.name}}">
                <p>{{item.text}}</p>
            </section>
        </script>
        
        <script src="http://example.com/js/common.js"></script>
        <script src="http://example.com/js/sample/router.js"></script>
        <script src="http://example.com/js/sample/listModel.js"></script>
        <script src="http://example.com/js/sample/listCtrl.js"></script>
        <script src="http://example.com/js/sample/endCtrl.js"></script>
        
    </body>
</html>

JSファイル

common.js

 angular.jsや共通的に使うconfig、$resourceの設定を記載したJSファイルを格納しています。

router.js

 router処理を記載しています。list、endのルーティングを設定しています。

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

    config(['$routeProvider', function($routeProvider) {
        $routeProvider.
            when('/', {
                templateUrl: 'listCtrl.html',
                controller: 'listCtrl',
                reloadOnSearch: false
            }).
            when('/end/:id/', {
                templateUrl: 'endCtrl.html',
                controller: 'endCtrl',
                reloadOnSearch: false
            }).
            otherwise({redirectTo : '/'});
    }]);

listCtrl.js

 listページのController。listModelからコンテンツデータを取得しScopeに格納しています。

var controller = function($rootScope, $scope, listModel){
    
    //scope設定
    $rootScope.title = "タイトル";
    $rootScope.description = "タイトルの一覧ページです。";
    $scope.items = listModel.get();

    //JSONデータを取得し、scopeに格納
    listModel.fetch().then(function(resp){
        $scope.items = listModel.get();
    });
};

controller.$inject = ['$rootScope','$scope', 'listModel'];
app.controller('listCtrl', controller);
endCtrl.js

 endページのController。listModelからコンテンツデータを取得しScopeに格納しています。

var controller = function($rootScope, $scope, $routeParams, listModel){
    
    //scope設定
    $scope.item = {};
    $scope.id = $routeParams.id;

    //JSONデータを取得し、scopeに格納
    listModel.fetch().then(function(resp){
        $scope.item = listModel.get($scope.id);
        $rootScope.title = $scope.item.name;
        $rootScope.description = $scope.item.name + "について紹介。";
    });
};

controller.$inject = ['$rootScope','$scope', '$routeParams', 'listModel'];
app.controller('endCtrl', controller);
listModel.js

 コンテンツデータを記載しているJSONファイルを取得し、指定の形式で返却しています。

var model = function($http, $q, list01Resource){
    this.datas = [];

    //データの読み込みmethod
    this.fetch = function(){
        var that = this,
            deferred = $q.defer();

        //api
        //list01用のコンテンツデータが記載されたJSONファイルを取得
        list01Resource.get({}, function(resp) {
            that.datas = resp;
            deferred.resolve(resp);
        //error
        }, function(){
            deferred.reject({});
        });
        return deferred.promise;
    };

    //データの取得method
    this.get = function(id){
        if(id){
            var result = null;
            for (var i = 0; i < this.datas.length; i++) {
                if(this.datas[i].id === id){
                    result = this.datas[i];
                    break;
                }
            }
            return result;
        }
        return this.datas;
    };
};

model.$inject = ['$http', "$q", "list01Resource"];
app.service('listModel', model);

条件に応じたコードの変更点

HTML5モード

 通常のhash形式とpushStateでの挙動の差異を把握するために、pushStateに関しては下記のコードをrouter.jsのconfigに追記しています。

$locationProvider.html5Mode(true);

レンダリング済みのLISTのHTMLを埋め込む

 AngularJSで生成したHTMLを把握できず、Endページをクロールしない想定がありましたので、レンダリング済みのListのHTMLを記載しています。Endアクセス時にもレンダリング済みのListのHTMLが表示されますが、意図的に行っています。その状態でインデックスにどのような変化が起きるのかを検証するためです。

<!DOCTYPE html>
<html ng-app="sampleApp">
    <head>
        ~~ 省略 ~~
    </head>
    <body>
        <div ng-view>
            <section>
                <h1>サンプル一覧 No3</h1>
                <ul>
                    <li><a href="#/end/1/">サンプル一覧 No3 詳細1</a></li>
                    <li><a href="#/end/2/">サンプル一覧 No3 詳細2</a></li>
                    <li><a href="#/end/3/">サンプル一覧 No3 詳細3</a></li>
                </ul>
            </section>
        </div>
        ~~ ng-template 記載 ~~
    </body>
</html>

リストのng-templateを埋め込む

 こちらに関しては、AngularJSのテンプレート変数がどのように解釈されるか興味があったため、作成しました。想定としては、/end/{{item.id}}/に対してクローラーが回り、検索結果に表示されるバグが生じるのではないかと予測しました。

<!DOCTYPE html>
<html ng-app="sampleApp">
    <head>
        ~~ 省略 ~~
    </head>
    <body>
        <div ng-view>
            <section>
                <h1>サンプル一覧 No2</h1>
                <ul ng-repeat="item in items">
                    <li><a href="#/end/{{item.id}}/">{{item.name}}</a></li>
                </ul>
            </section>
        </div>
        ~~ ng-template 記載 ~~
    </body>
</html>

次のページ
検証の履歴(1)

修正履歴

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
LINEフロントエンドレンジャーのWeb開発術連載記事一覧

もっと読む

この記事の著者

姜 勝陽(LINE株式会社)(キョウ ショウヨウ)

LINE株式会社に在籍しているフロントエンドエンジニア。LINE内Webアプリを担当。2002年からSEOの情報を趣味の一環として収集している。最近はサーバーサイドレンダリング無しで、Single Page ApplicationをGoogleに認識させる為の検証を進めている。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/8543 2016/07/19 11:09

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング