1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > webpack4.6.0打包包含oclazyload组件(懒加载)的angular1.x工程

webpack4.6.0打包包含oclazyload组件(懒加载)的angular1.x工程

时间:2019-11-26 03:54:34

相关推荐

webpack4.6.0打包包含oclazyload组件(懒加载)的angular1.x工程

当前能百度到的webpack打包包含oclazyload组件的angular1.x工程的文章大多用的是webpack1.x版本,但实际上webpack.config里很多1.x版本的配置项在最新的4.6.0里已经废弃了(或名称、位置发生了调整),这意味着我们直接拿用webpack1.x版本可以成功打包的源码工程过来用最新版本的webpack打包是会报错的,出错信息可以看这里了解一下:webpack打包时提示Invalid configuration object错误

下面介绍一下在webpack4.6.0版本下如何打包包含oclazyload组件的angular1.x工程。

首先给出笔者的一些环境信息:

node v8.10.0

npm 5.6.0

webpack 4.6.0

angular 1.4.6

angular-ui-router 0.2.18

oclazyload 1.0.1

接下来来简单分析一下如果要用webpack来打包工程要做哪些调整:

webpack的基础功能就是把工程里多个有依赖关系的javascript文件打包压缩成一个文件,但使用了oclazyload组件即意味着工程里的javascript文件并不是同时加载的(不是同时加载的文件肯定不能打包压缩在同一个文件中),这意味着webpack也要支持懒加载,好在webpack早就支持了,这个懒加载功能可以通过require.ensure来完成,接口文档可以看这里:require.ensure接口

ps:有个坏消息,笔者写这篇博客的时候webpack的接口文档貌似已经更新到4.8.1了,请各位同学注意(没错,接口可能又变了)。

首先看一下在不使用webpack打包时懒加载是怎么玩的:

$stateProvider.state("default",{url:"/default",//本地直接在浏览器中运行index.html则不能使用templateUrl,否则会有跨域问题// templateUrl:"html/default.html",template: "<div><span>{{default}}</span></div>",controller: "defaultCtrl",resolve:{'default': function($ocLazyLoad) {let mod = require('./controller/defaultCtrl');return $ocLazyLoad.load({name: mod});}}});

直接把需要懒加载的文件load进来就可以了(有兴趣的同学戳这里看基本用法:angularjs1.4懒加载的基本用法)。

然后按正常人的脑回路来说一般会尝试这么写:

$stateProvider.state("default",{url:"/default",//本地直接在浏览器中运行index.html则不能使用templateUrl,否则会有跨域问题// templateUrl:"html/default.html",template: "<div><span>{{default}}</span></div>",controller: "defaultCtrl",resolve:{'default': ["$ocLazyLoad", function($ocLazyLoad) {require.ensure(['./controller/defaultCtrl'], function() {let mod = require('./controller/defaultCtrl');return $ocLazyLoad.load({name: mod});}, 'defaultCtrl');}]}});

不过按一般的套路来说这么容易就搞定的话笔者就没什么好写的了,实际上按上面的写法运行时会提示路由所需的controller不存在,并抛出异常终止程序运行:

推测是require.ensure内部有异步行为,resolve对应的回调函数在执行完成时require.ensure的回调函数其实还没有执行,下面尝试用angular的promise把require.ensure包起来:

$stateProvider.state("default",{url:"/default",//本地直接在浏览器中运行index.html则不能使用templateUrl,否则会有跨域问题// templateUrl:"html/default.html",template: "<div><span>{{default}}</span></div>",controller: "defaultCtrl",resolve:{'default': ["$q", "$ocLazyLoad", function($q, $ocLazyLoad) {let deferred = $q.defer();require.ensure(['./controller/defaultCtrl'], function() {let mod = require('./controller/defaultCtrl');$ocLazyLoad.load({name: mod});deferred.resolve();}, 'defaultCtrl');return deferred.promise;}]}});

重新用webpack打包后看一下运行结果:

再手动切换一下路由看看其他路由所需的controller是否可以正常加载:

嗯,简单验证了一下基本没什么大问题了。

下面贴一下路由模块的完整代码:

"use strict";let configModule = angular.module("config", []);let config = ["$stateProvider", "$urlRouterProvider", "$controllerProvider",function($stateProvider, $urlRouterProvider){$urlRouterProvider.otherwise("/default");$stateProvider.state("default",{url:"/default",//本地直接在浏览器中运行index.html则不能使用templateUrl,否则会有跨域问题// templateUrl:"html/default.html",template: "<div><span>{{default}}</span></div>",controller: "defaultCtrl",resolve:{'default': ["$q", "$ocLazyLoad", function($q, $ocLazyLoad) {let deferred = $q.defer();require.ensure(['./controller/defaultCtrl'], function() {let mod = require('./controller/defaultCtrl');$ocLazyLoad.load({name: mod});deferred.resolve();}, 'defaultCtrl');return deferred.promise;}]// 'default': ["$ocLazyLoad", function($ocLazyLoad) {//require.ensure(['./controller/defaultCtrl'], function() {// let mod = require('./controller/defaultCtrl');// return $ocLazyLoad.load({// name: mod// });//}, 'defaultCtrl');// }]}});$stateProvider.state("routerA",{url:"/routerA",// templateUrl:"html/routerA.html",template: "<div>{{say}}</div>`",controller: "routerACtrl",resolve:{'routerA': ["$q", "$ocLazyLoad", function($q, $ocLazyLoad) {let deferred = $q.defer();require.ensure(['./controller/routerACtrl'], function() {let mod = require('./controller/routerACtrl');$ocLazyLoad.load({name: mod});deferred.resolve();}, 'routerACtrl');return deferred.promise;}]}});}];configModule.config(config);module.exports = configModule.name;

可以看到有2个路由模块,那么使用webpack打包后应该会生成三个文件:

额外生成的0.bundle.js和1.bundle.js对应的就是懒加载的2个路由对应的controller文件。

下面再贴一下默认路由的controller代码:

"use strict";let defaultCtrl = ["$rootScope", "$scope", function ($rootScope, $scope) {console.log("enter default controller");$scope.default = "default page."}];module.exports = angular.module("config", []).controller("defaultCtrl", defaultCtrl).name;

另外要注意的一点是export default和module.exports导出的模块require时的写法是不同的(export default如果用import就没问题,但是要想混用require就必须注意写法),详情可以参考在路由的resolve中使用lazyload加载控制器无效这篇文章。

上面的例子已经简单说明该如何打包包含oclazyload组件(懒加载)的angular1.x工程,但深入使用的话还有坑要踩。之前的路由模块代码只给出了一层路由的情况,一旦配置成多层路由就会有下面这样的问题:

包含多层路由的路由模块代码:

"use strict";let configModule = angular.module("config", []);let config = ["$stateProvider", "$urlRouterProvider", "$controllerProvider",function($stateProvider, $urlRouterProvider){// $urlRouterProvider.otherwise("/default/subPage");$urlRouterProvider.otherwise("/default");$stateProvider.state("default",{url:"/default",//本地直接在浏览器中运行index.html则不能使用templateUrl,否则会有跨域问题// templateUrl:"html/default.html",template: "<div><span>{{default}}</span><div ui-view></div></div>",controller: "defaultCtrl",resolve:{'default': ["$q", "$ocLazyLoad", function($q, $ocLazyLoad) {let deferred = $q.defer();require.ensure(['./controller/defaultCtrl'], function() {let mod = require('./controller/defaultCtrl');$ocLazyLoad.load({name: mod});deferred.resolve();}, 'defaultCtrl');return deferred.promise;}]// 'default': ["$ocLazyLoad", function($ocLazyLoad) {//require.ensure(['./controller/defaultCtrl'], function() {// let mod = require('./controller/defaultCtrl');// return $ocLazyLoad.load({// name: mod// });//}, 'defaultCtrl');// }]}});$stateProvider.state("default.subPage",{url:"/subPage",// templateUrl:"html/routerA.html",template: "<div>{{subPageData}}</div>`",controller: "subPageCtrl",resolve:{'subPageCtrl': ["$q", "$ocLazyLoad", function($q, $ocLazyLoad) {let deferred = $q.defer();require.ensure(['./controller/subPageCtrl'], function() {let mod = require('./controller/subPageCtrl');$ocLazyLoad.load({name: mod});deferred.resolve();}, 'subPageCtrl');return deferred.promise;}]}});$stateProvider.state("routerA",{url:"/routerA",// templateUrl:"html/routerA.html",template: "<div>{{say}}</div>`",controller: "routerACtrl",resolve:{'routerA': ["$q", "$ocLazyLoad", function($q, $ocLazyLoad) {let deferred = $q.defer();require.ensure(['./controller/routerACtrl'], function() {let mod = require('./controller/routerACtrl');$ocLazyLoad.load({name: mod});deferred.resolve();}, 'routerACtrl');return deferred.promise;}]}});}];configModule.config(config);module.exports = configModule.name;

subPage路由的controller代码:

"use strict";let subPageCtrl = ["$rootScope", "$scope", function ($rootScope, $scope) {console.log("enter subPage controller");$scope.subPageData = "subPage page."}];module.exports = angular.module("config", []).controller("subPageCtrl", subPageCtrl).name;

执行webpack后访问/default/subPage路由:

笔者的开发代码(不是github里给出的代码)里这似乎是个偶现问题,不是每次都会报错,且提示未定义的controller有可能是父路由也有可能是子路由。

导致这个问题发生的根因是路由模块路由controller的加载方式及路由controller的定义方式。

首先来看一下路由controller的定义方式:

default路由controller:

"use strict";let defaultCtrl = ["$rootScope", "$scope", function ($rootScope, $scope) {console.log("enter default controller");$scope.default = "default page."}];module.exports = angular.module("config", []).controller("defaultCtrl", defaultCtrl).name;

subPage路由controller:

"use strict";let subPageCtrl = ["$rootScope", "$scope", function ($rootScope, $scope) {console.log("enter subPage controller");$scope.subPageData = "subPage page."}];module.exports = angular.module("config", []).controller("subPageCtrl", subPageCtrl).name;

父子路由的controller都定义在config这个module上,再看一下路由模块路由controller的加载方式(这里只截取了路由模块default路由的片段):

let deferred = $q.defer();require.ensure(['./controller/defaultCtrl'], function() {let mod = require('./controller/defaultCtrl');$ocLazyLoad.load({name: mod});deferred.resolve();}, 'defaultCtrl');

可以看到ocLazyLoad.load的是export出来的module名称,如果打印出来的话可以看到父子路由export出来的module名称都是config,而ocLazyLoad.load以name的方式来完成module的懒加载属于覆盖式加载,即意味着我们最终load进来的config这个module要么少了defaultCtrl,要么少了subPageCtrl(因为defaultCtrl这个文件export出来的config module没有subPageCtrl,subPageCtrl这个文件export出来的config module没有defaultCtrl),这样报错就不难理解了。

问题改起来也很简单,定义的controller不挂在config这个module上就没问题了,以default路由controller为例(当然所有的controller都要调整):

"use strict";let defaultCtrl = ["$rootScope", "$scope", function ($rootScope, $scope) {console.log("enter default controller");$scope.default = "default page."}];module.exports = angular.module("defaultCtrl", []).controller("defaultCtrl", defaultCtrl).name;

把module的名称调整为defaultCtrl(其实随便什么名字都行,只要确保module名称不重复即可),然后执行webpack后访问/default/subPage路由(再说一遍,其他controller挂的module名称都要调整):

这样即使刷新再多次也不会报错了。也许有同学有疑问:为什么controller挂的这些module都没有被config或app这两个module声明依赖,但实际上确可以使用这些module上挂的controller?

答案很明显,ocLazyLoad.load以name的方式来完成module的懒加载默认是作为执行ocLazyLoad.load所在module(在这里是config)的依赖项load进来,所以直接使用完全没问题。同理,在这些controller也可以随便使用在config或app上定义的service、factory甚至是directive。

完整工程代码:/liqing-taoyanzoukaila/angular-lazyload-webpack。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。