首页 前端知识 AngularJS进阶(二十五)requirejs angular angular-route 浅谈HTML5单页面架构_requirejs angular

AngularJS进阶(二十五)requirejs angular angular-route 浅谈HTML5单页面架构_requirejs angular

2024-05-05 12:05:27 前端知识 前端哥 923 519 我要收藏

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title>Angular & Requirejs</title>
</head>
<body>
<div id="container" ng-view></div>
<script data-baseurl="./" data-main="main.js" src="libs/require.js" id="main"></script>
</body>
</html>

相对angular的写法,这里由于使用requirejs管理全部模块,所以index.html中不需要引入angular等,只是设置了一个带ng-view属性的div,用于充当整个App的视图区域。

data-baseurl是额外加入的属性,主要好处是可以轻松在html(0缓存)中对js的url进行修改。

data-main就是requirejs的标准写法了,跳过不说。

第二步,main.js,也就是requirejs的配置

'use strict';
(function (win) {
    //配置baseUrl
    var baseUrl = document.getElementById('main').getAttribute('data-baseurl');
    /*
     * 文件依赖
     */
    var config = {
        baseUrl: baseUrl,             // 依赖相对路径
        paths: {                    // 如果某个前缀的依赖不是按照baseUrl拼接这么简单,就需要在这里指出
            underscore: 'libs/underscore',
            angular: 'libs/angular',
            'angular-route': 'libs/angular-route',
            text: 'libs/text'             // 用于requirejs导入html类型的依赖
        },
        shim: { //引入没有使用requirejs模块写法的类库。例如underscore这个类库,本来会有一个全局变量'_'。这里shim等于快速定义一个模块,把原来的全局变量'_'封装在局部,并导出为一个exports,变成跟普通requirejs模块一样
            underscore: {
                exports: '_'
            },
            angular: {
                exports: 'angular'
            },
            'angular-route': {
                deps: ['angular'],   //依赖什么模块
                exports: 'ngRouteModule'
            }
        }
    };
    require.config(config);
    require(['angular', 'router'], function(angular){
        angular.bootstrap(document, ['webapp']);
    });
})(window);

requirejs的语法,说来话长,简单在代码中做了注释。有兴趣了解详情的可以参考官网:http://requirejs.org/;

angular可以参考:https://docs.angularjs.org/guide/filter

这里配置好requirejs后,就做第一步工作,引入angular和angular的路由配置,然后用angular.bootstrap(document, [‘webapp’]); 手工启动angular,这里webapp是router.js中定义的angular module。

第三步,配置这个router

define(['angular', 'require', 'angular-route'], function (angular, require) {
    var app = angular.module('webapp', ['ngRoute']);
    app.config(['$routeProvider', '$controllerProvider',
        function($routeProvider, $controllerProvider) {
            $routeProvider.
                when('/module1', {
                    templateUrl: 'module1/tpl.html',
                    controller: 'module1Controller',
                    resolve: {
                        /*
                        这个key值会被注入到controller中,对应的是后边这个function返回的值,或者promise最终resolve的值。函数的参数是所需的服务,angular会根据参数名自动注入对应controller写法(注意keyName):
           controllers.controller('module2Controller', ['$scope', '$http', 'keyName',
                             function($scope, $http, keyName) {
                         }]);
                         */
                        keyName: function ($q) {
                            var deferred = $q.defer();
                            require(['module1/module1.js'], function (controller) {
                                $controllerProvider.register('module1Controller', controller);      //由于是动态加载的controller,所以要先注册,再使用
                                deferred.resolve();
                            });
                            return deferred.promise;
                        }
                    }
                }).
                otherwise({
                    redirectTo: '/module1'      //angular就喜欢斜杠开头
                });
        }]);
    return app;
});

上述代码看起来长,实际很短,因为有一堆绿色的注释,嘿嘿。。。

如果大家用过angular-route,这里的语法就很简单,如果没用过,则建议直接阅读angular-route源代码中的注释,非常清晰。

简单而言,就是when函数配置一个路由规则,对应一个template和一个controller。otherwise就是默认路由,也就是遇到一个未定义路径的时候如何跳转。

如果没有使用requirejs,那么我们需要在路由配置前加载完全部controller。angular-route需要做的只是切换HTML模版,重新编译,绑定新的controller。

但是这里用了requirejs,事情就变化了。我们要按需加载,不可能页面刚加载就全部controller都load回来,这样得耗费多少流量。。。

所以,这里利用了angular-route提供的resolve功能,也就是路由更改html前先把resolve里边该做的事完成。

resolve的写法比较特殊,接受的是一个key:value对象,keyName将会导入到controller中(如果controller有注明依赖)。而value应该是一个函数,函数的写法类似controller,angular会自动根据参数名导入相应依赖的服务,例如 q 、 q、 qroute。

上述例子中,module1.js定义了模块1的controller,后续我们再看代码。

由于路由配置前还不存在这个controller,所以现在需要动态注册这个controller。也就是:

$controllerProvider.register(‘module1Controller’, controller);

第四步,看看模块1的controller是怎么写的

define(['angular'], function (angular) {
    //angular会自动根据controller函数的参数名,导入相应的服务
    return function($scope, $http, $interval){
        $scope.info = 'kenko';      //向view/模版注入数据
        //模拟请求cgi获取数据,数据返回后,自动修改界面,不需要啰嗦的$('#xxx').html(xxx)
        $http.get('module2/tpl.html').success(function(data) {
            $scope.info = 'vivi';
        });
        var i = 0;
        //angularjs修改了原来的setTimeout和setInterval,要用这两个玩意,必须引入$timeout和$interval,否则无法修改angular范围内的东西
        $interval(function () {
            i++;
            $scope.info = i;
        }, 1000);
    };
});

angular有太多牛逼的功能,但实际上我业务太简单,用不到。所以这里只演示了3种最简单的情况。

这里不得不说,由于双向绑定,拉cgi和修改dom这些操作就变得非常简单了。

貌似一切解决了?这样的模块化似乎已经很好,跳转到某个模块的时候才加载对应的html和controller js。

但是对于追求极致的团队来说,模块的html和js应该打包在一起,一次请求就拉回来,这样能大大减少HTTP请求的时间。而现在按照angular-route,只能利用templateUrl单独拉取一个html文件。

那么接下来,我们再动动歪脑筋,修改一下。

第五步,修改angular-route,实现HTML和js打包加载。

function ngViewFillContentFactory($compile, $controller, $route) {
  return {
    restrict: 'ECA',
    priority: -400,
    link: function(scope, $element) {
      var current = $route.current,
          locals = current.locals;
      $element.html(current.template);  //原来是locals.$template

首先,先修改一下angular-route的源代码,这个源代码非常精简,不用太纠结,狠狠的去修改就好了。

另外,想问我为什么知道或者想到在这修改?咳咳咳,我会大摇大摆的说我认识angular-route的作者么?。。。。。。。开玩笑,作者叫什么,我都没去找,还说认识作者。其实就是逐步调,稍加变量搜索,发现一些不对劲,就做了这个小刀。

再另外,有专家要拍板了,这样乱修改,肯定带来毛病。是的,我不得不说,我自己都没彻底的检查是否有问题,但按照实际情况来看,暂时没遇到问题。

然后,做一个新的when配置:

       when('/module2', {
                    template: '',
                    controller: 'module2Controller',
                    resolve:{
                        keyName: function ($route, $q) {
                            var deferred = $q.defer();
                            require(['module2/module2.js'], function (module2) {
                                $controllerProvider.register('module2Controller', module2.controller);
                                $route.current.template = module2.tpl;
                                deferred.resolve();
                            });
                            return deferred.promise;
                        }
                    }
                })

这里用module2做例子,跟module1不同,这里初始设置的template是空字符串,然后在resolve中require回来后,动态修改$route.current.template。

因为我知道,这个修改能赶在angular-route修改HTML前,也就是小把戏能凑效。

相应,看看module2怎么写:

define(['angular', 'text!module2/tpl.html'], function (angular, tpl) {
    //angular会自动根据controller函数的参数名,导入相应的服务
    return {
        controller: function ($scope, $http, $interval) {
            $scope.date = '2015-07-13';
        },
        tpl: tpl
    };
});

算法刷题

大厂面试还是很注重算法题的,尤其是字节跳动,算法是问的比较多的,关于算法,推荐《LeetCode》和《算法的乐趣》,这两本我也有电子版,字节跳动、阿里、美团等大厂面试题(含答案+解析)、学习笔记、Xmind思维导图均可以分享给大家学习。

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

写在最后

最后,对所以做Java的朋友提几点建议,也是我的个人心得:

  1. 疯狂编程

  2. 学习效果可视化

  3. 写博客

  4. 阅读优秀代码

  5. 心态调整

转载请注明出处或者链接地址:https://www.qianduange.cn//article/6942.html
评论
会员中心 联系我 留言建议 回顶部
复制成功!