博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
用ES6语法来开发angularjs项目,webpack进行按需加载模块打包
阅读量:7113 次
发布时间:2019-06-28

本文共 12083 字,大约阅读时间需要 40 分钟。

最近公司用webpack和angular2来开发手机app页面,发现ES6的写法非常方便和清晰,于是我想到在angular1中也可以用ES6语法写,webpack来进行打包编辑。于是我查阅了相关资料之后终于实现了ES6语法来写angular1。

Webpack

关于webpack的配置,网上有许多的教程,在github上也有相当多的项目例子可以借鉴,我这就不详细说webpack的配置了。如果你是新手,我推荐,如果你对英文不感冒你可以查看webpack的学习相关知识。当然如果你实在对webpack的配置烦恼的话,我推荐GitHub上AngularClass组织的项目,这是webpack配置angualr2的例子,它里面的配置非常详细,对每个插件的使用有相关的说明,对学习webpack的配置很有帮助,本项目也是参照该例子,主要的目录结构如下。

angular-webpack/ ├──config/                        * 配置文件目录 │   ├──helpers.js                 * helper 方法用来获取目录以及一些相关配置参数 │   ├──webpack.common.js          * webpack 通用的配置信息 │   ├──webpack.dev.js             * webpack 配置开发环境文件 │   ├──webpack.prod.js            * webpack 配置生产环境文件 │   └──webpack.test.js            * webpack 配置测试环境文件 ├──src/                           * web文件的主目录  │   ├──app.module.js              * angularjs 主module │   │ │   ├──index.html                 * 项目的主入口 │   │ │   ├──app/                       * 文件主目录 │   │    ├──config/               * angularjs的config文件 │   │    │          │   │    ├──router/               * angularjs的路由配置 │   │    │ │   │    └──views/                * angularjs的页面 │   │    │   └──style/                     * 项目的样式文件   │ ├──package.json                   * npm 配置文件 └──webpack.config.js              * webpack 主配置文件

angularjs目录结构

对一个项目来说,目录组织是非常重要的,它能让你一目了然的了解项目信息。在angularjs的项目中,有不同的目录搭建,对于有些人喜欢把module、controller、directive、service等文件分别放在module、controller、directive、service目录下统一管理。html页面统一放在HTML目录下它的目录结构看起来是这样的:

app/                      ├──controller/              │      ├──*.controller.js  │      ├──*.controller.js  │      ├──*.controller.js │      │.... ├──directive/   │      ├──*.directive.js  │      ├──*.directive.js  │      │.... ├──html/             │      ├──*.html │      ├──*.html  │      │.... ├──module/             │      ├──*.module.js │      ├──*.module.js │      │.... │  ├──service/  │      ├──*.service.js  │      ├──*.service.js  │      │....

这一种目录结构相对来说不是很好,尤其是对模块化打包来说结构很不清楚,不利于模块化,页面一多很难管理,另一种目录结构是把每一个相关的页面controller、service、module、html放到对应的页面目录下,它的结构是这样的:

app/                      ├──login/              │      ├──login.module.js  │      ├──login.controller.js  │      ├──login.directive.js │      ├──login.router.js │      ├──login.html │      ├──login.css │ ├──home/   │      ├──home.module.js  │      ├──home.controller.js  │      ├──home.directive.js │      ├──home.router.js │      ├──home.html │      ├──home.css ├──other/  │      ├──other.module.js  │      ├──other.controller.js  │      ├──other.directive.js │      ├──other.router.js │      ├──other.html    │      ├──other.css

这样的目录结构有利于模块的移植。

angualrjs 文件构建

在ES6中有了新方法:Class来声明类,class写法看起来更简洁,方便。在angular1中把function方法定义的controller,service等修改为Class声明,具体各修改方式如下:

module

ES6关于angualr的module写法没有大的变化,注意ES6中需要对angular模块导入之后才能使用angular,其他模块的注入也是一样的。

//--------ES6之前   angular.module('webapp',['oc.lazyLoad',]);//--------ES6      import angular from 'angular'; //---- 或者  var angular = require('angular');   require('oclazyload');   angular.module('webapp',['oc.lazyLoad',]);

controller

login.controller.js

//ES5 controller的一般写法LoginController.$inject = ['$state', 'loginService'];ngModule.controller('loginController', LoginController);function LoginController($state, loginService) {    var vm = this;    vm.login = login;    function login() {        if (!vm.username) {            alert('请输入手机号');            return;        }        if (!vm.userPassword) {            alert('请输入密码');            return;        }        loginService.login({            username: vm.username,            userPassword: vm.userPassword        }).then(function(res){            $state.go('page');        }, function(res){            alert('登录失败');        })    }}//------------------ES6-----------------------class LoginController {    constructor($state, loginService) {        this.loginService = loginService;   //把依赖注入的方法放到this中,变为this的属性,使用this."property"来调用该变量                                            //与之前比最大的不同是依赖注入不在是该函数内的“全局变量”        this.$state = $state;    }    login() {        if (!this.username) {            alert('请输入手机号');            return;        }        if (!this.userPassword) {            alert('请输入密码');            return;        }        this.loginService.login({            username: this.username,            userPassword: this.userPassword        }).then((res) => {            this.$state.go('page');        }, (res) => {            alert('登录失败');        })    }}LoginController.$inject = ['$state', 'loginService'];ngModule.controller('loginController', LoginController);

注意在function声明中LoginController.$inject = ['$state', 'loginService']不管是放在function前面还是后面都不会有影响,但是在class声明中LoginController.$inject = ['$state', 'loginService']只能放在class后面定义,主要原因是function会在作用域中进行提升而class不会。

service

angular中service的写法与controller相似,可以说没有大区别

login.service.js

//ES5 service写法LoginService.$inject = ['$http'];function LoginService($http){    var self=this;    self.login=login;    function login(params) {        /*return $http({            url: '****!/!***!/login',            method: 'GET',            params: params        })*/        /*用ES6 Promise方法模仿$http,也可用angular中的$q实现*/        return new Promise(function (resolve, reject) {            if (params) {                resolve('success');            } else {                reject('fail');            }        });    }}//------------------ES6-----------------------class LoginService {    constructor($http) {        this.$http = $http;    }    login(params) {        /*return this.$http({            url: '****!/!***!/login',            method: 'GET',            params: params        })*/        /*用ES6 Promise方法模仿$http,也可用angular中的$q实现*/        return new Promise((resolve, reject) => {            if (params) {                resolve('success');            } else {                reject('fail');            }        });    }}LoginService.$inject = ['$http'];ngModule.service('loginService', LoginService);

provider

//------------------ES6-----------------------class LoginProvider  {    constructor($http) {        this.$http = $http;    }    $get(){         let that=this;                  login(params) {            return that.$http({    //注意this的指向问题                url: '****!/!***!/login',                method: 'GET',                params: params            })        }            return {            login:login;        }    }}LoginProvider.$inject = ['$http']ngModule.provider('LoginProvider', LoginProvider);

注意,在provider中需要定义$get函数

factory

关于factory的写法稍微与前面的不同,很多人想当然的会这和service一样写factory

class LoginFactory {    constructor($http) {        this.$http = $http;    }    login(params) {       ...       ...       ...    }}LoginFactory.$inject = ['$http'];ngModule.factory('LoginFactory', LoginFactory);   //TypeError: Cannot call a class as a function

class的定义是相似的,但是在module引入factory时如果按上面的写法写就会报TypeError: Cannot call a class as a function错误

可能有人会很奇怪,为什么controller,service,provider可以,但是到了factory就会错误。这个和factory的机制有关。编译器转ES6语法到ES5后class会变成这样:

var Loginfactory= function () {    function Loginfactory($http) {        _classCallCheck(this, Loginfactory);        this.$http= $http;    }    _createClass(Loginfactory, [{        key: 'login',        value: function login() {                    }    }]);    return Loginfactory;}();

关键是_classCallCheck方法,用来判断this是否为Loginfactory的实例,但是在factory中,angular内部更改了factory的返回对象,使this不在是factory的实例,所以会报错。我们需要手动new factory()方法,应该改成这样:

//LoginFactory.$inject = ['$http'];不需要了ngModule.factory('LoginFactory', ['$http',($http)=>new LoginFactory($http)]);

注:controller,service,provider也可以按factory的注入方法写

可是这么写如果有好多依赖要注入,会使ngModule.factory('LoginFactory', ['$http'....,($http,...)=>new LoginFactory($http,...)])看起来非常长而且不易阅读,怎样才能像controller一样以LoginFactory.$inject = ['$http']注入,可以先获取Class上的$inject的注入数组,在由该数组和Class的实例组成['a','b',function(a,b){}]样式,具体实现如下:

function constructorFn(configFn) {    let args = configFn.$inject || [];    let factoryFunction = (...args) => new configFn(...args);    /**     * 主要检测controller、directive的注入     * service、factory、provider、config在程序运行时只运行一次     * controller、directive每次页面载入时会重新注入,需要把上一个注入的function从数组中移除     * */    if(typeof args[args.length-1]==='function'){        args.splice(-1,1);    }    /**     * args.push(factoryFunction)类似于['a',function(a){}]     * */    return args.push(factoryFunction) && args;  //return args;}LoginFactory.$inject = ['$http'];ngModule.factory('LoginFactory', constructorFn(LoginFactory));

在写factory时我还发现一个小问题,在写angular的Http拦截器时候会出现意料之外的情况

export class HttpInterceptor {    constructor($q, $location) {        this.$q = $q;        this.$location = $location;    }    request(config){        console.log(this.$q);    //error        //this对象不是HttpInterceptor的实例而是undefined        return config;        }}HttpInterceptor.$inject = ['$q', '$location'];

在调用request、requestError等方法时,this指向丢失了,应该是angular在调用时,this对象被修改了。如果你在request、requestError等方法中没有用到this对象,可以这么写。如果用到this对象,则需要变换一下:

export class HttpInterceptor {    constructor($q, $location) {        this.$q = $q;        this.$location = $location;        this.request=(config)=>{            console.log(this.$q);    //success            return config;            };    }    }HttpInterceptor.$inject = ['$q', '$location'];

注意,this指向更改可能出现的情况不止在这里,需要小心

directive

directive写法稍有不同,由于directive需要返回一个包含restrict、template 、scope 等属性的对象,所以需要在constructor中定义这些熟悉,还有directive会有link,compile等函数方法

//vm.title
//------------------ES6-----------------------class PageDirective {    constructor($timeout) {        this.restrict = "EA";        this.template = `{
{vm.name}} Hello {
{vm.pageTitle}}`; this.controller = PageDController; this.controllerAs = "vm"; this.bindToController = true; this.scope = { pageTitle:'=' }; this.$timeout=$timeout; } link(scope,element,attr){ this.$timeout(()=>scope.vm.name='',1000); } }PageDirective.$inject = ['$timeout'];class PageDController{ constructor(){ this.name='pageDirective' }}ngModule.directive('myPage', constructorFn(PageDirective));

oclazyload 按需加载路由

webpack打包时会把HTML和js代码都放到一个js文件中,使这个文件非常大,导致打开页面加载会很慢,影响用户体验。所以我们需要把HTML以及部分js代码抽出放到块文件中。幸运的是我们可以用oclazyload配合webpack进行anglar路由的按需加载,oclazyload的有详细的说明和例子,我这边列了一个简单的例子:

路由配置

login.router.js

import angular from 'angular'//import LoginModule from './login.module'{    state: 'login',    url: '/login',    controller: 'loginController',    controllerAs: 'vm',    /*template: require('./login.html'),*/    templateProvider: ['$q', ($q)=> {                //templateProvider来动态的加载template        let defer = $q.defer();                      //require.ensure:webpack的方法在需要的时候才下载依赖的模块,         require.ensure([''], () => {                //只有require函数调用后再执行下载的模块                    let template = require('./login.html');  //require调用后加载login页面            defer.resolve(template);        });        return defer.promise;    }],    title: '登录',    resolve: {        deps: ['$q', '$ocLazyLoad', ($q, $ocLazyLoad)=> {            let defer = $q.defer();            require.ensure([], ()=> {                /**                *let module = LoginModule(angular);                  *注意import导入LoginModule方法与require('./login.module')直接引用LoginModule方法是有区别的,                *import导入LoginModule方法不能分离js                */                let module = require('./login.module').LoginModule(angular);   //动态加载Module                $ocLazyLoad.load({                    name: 'login'                                              //name就是你module的名称                });                defer.resolve(module);            });            return defer.promise;    }],}

login.module.js

import {loginControllerFunc} from "./login.controller";import {loginServiceFunc} from "./login.service";export function LoginModule(Angular) {    const loginModule = Angular.module('login', []);   //login名称就是$ocLazyLoad.load中的name;    loginControllerFunc(loginModule);   //注入模块controller    loginServiceFunc(loginModule);      //注入模块service}

注:$ocLazyLoad动态加载的module不需要在其他模块中引入,如angular.module('webapp',['login']),这是错误的

到这里用ES6构建anguar1项目就基本告一段落了。如果有更好的ES6写法,欢迎各位评说。

文章有不对的地方,望大神们指出。
项目地址请戳

转载地址:http://djwel.baihongyu.com/

你可能感兴趣的文章
7. DateTime,TimeSpan
查看>>
一起谈.NET技术,C#中使用#region指令的一些想法
查看>>
iOS json数据解析
查看>>
php关于精准计算的模块 BCMath
查看>>
【Android的从零单排开发日记】——Android数据存储(上)
查看>>
subversion配置(ubuntu,debian)
查看>>
SAMBA 共享服务器搭建
查看>>
windows安装React Native开发运行环境
查看>>
sharepoint 中根据loginName获取displayName以及用户信息 .
查看>>
t - sql的阶梯:超越基础水平9:动态t - sql代码
查看>>
第十一周进度条
查看>>
1140 - How Many Zeroes?
查看>>
【 一次性密码】TOTP
查看>>
java多线程细微知识点
查看>>
文档对象模型DOM(二)
查看>>
【Single Num II】cpp
查看>>
【 Sqrt(x) 】cpp
查看>>
第十七章: 自定义View
查看>>
从两个数组中查找相同的数字谈Hashtable
查看>>
201671010136 泛型类总结
查看>>