Open jingzhiMo opened 6 years ago
自从工作之后就极少写文章了,因为空闲时间没这么多,到了周末又想轻松两天,但是周末其实并不轻松,或许归根到底最后就是一个字:懒!这周回家之后,感觉总算可以静下心来做点东西,把之前在项目用ng1.x按需加载的实现整理一下。
最近工作用到angularJs,也就是ng1.x版本开发一个网站,这个项目其中用ui-router来控制路由,webpack来构建项目。有个比较致命的痛点,ng1.x官方不支持懒加载!
angularJs
ng1.x
这个项目经过webpack打包之后主要形成两个js文件,一个是vendor.js,是引入的node_modules的公用文件,另外一个是app.js,是自己写的js文件。
vendor.js
node_modules
app.js
处理之前项目的js入口文件大概是这样子的;
// 引入主要工具和框架 import angular from './angular' import 'angular-ui-router' // 引入一些工具库 import tool1 from './tool1' // 引入ng的一些指令,组件,service等等 import aComponent from './a.component' import aService from './a.service' import aDirective from './a.directive' // 引入路由 import router from './router' // 项目模块 angular.module('app', [ 'ui.router', // ... 其他一些依赖 ]) .config(router) .component('aComponent', aComponent) // ...
从这个入口文件就可以看出,现在所有依赖的外部工具库和自己编写的内容都是一次性引入进来,尽管通过webpack来分开了两个文件,但是在入口的html文件还是一次引入了。特别是进入首页介绍页面的时候,逻辑功能比较少,但是却要加载全部功能。
因为项目通过一级url分工明显,每个url可以分离成一个模块,处理起来就更直观,例如:
/foo /bar /baz // ...
因此需要从ui-router先下手,能够指定一级url之后,交给对应的模块处理,对应的模块内部再处理子url,这样子每个模块之间就更加明确。从ui-router官网参考的例子如下:
ui-router
var contactsFutureState = { name: 'contacts.**', url: '/contacts', lazyLoad: function() { // lazy load the contacts module here } }
这里的例子大概意思是url为/contact的命名是contact.**,然后通过layLoad的函数加载对应的模块逻辑处理。这里要引入一个概念,叫futureState,字面上的意思是未来的状态,就是预先定义的。在配置页面全局路由的时候,大概就是这样子:
/contact
contact.**
layLoad
futureState
angular.module('app', ['ui-router']) .config(['$stateProvider', function ($stateProvider) { let states = [ { name: 'foo.**', url: '/foo', lazyLoad: function() { // 引入对应的模块 } }, { name: 'bar.**', url: '/bar', lazyLoad: function() { // 引入对应的模块 } } // ... ] // 定义相关url states.forEach(state => $stateProvider.state(state)) }])
然后具体foo模块就定义对应的二级url:
foo
angular.module('foo', ['ui-router']) .config(['$stateProvider', function ($stateProvider) { let states = [ { name: 'foo', url: '/foo', component: 'foo' }, { name: 'foo.second', // 实际访问的url是 /foo/second url: '/second', component: 'fooSecond' } // ... ] // 定义相关url states.forEach(state => $stateProvider.state(state)) }]) // foo和fooSecond也是在该模块引入,这里没有写出 .component('foo', foo) .component('fooSecond', fooSecond)
但是,这样子并跑不通,会报一个multiple define的多重定义的错,然而ui-router官方并没有给出相关例子,刚才的foo模块定义是根据之前定义全局模块的定义。直到后来在stackflow找到了ui-router注入对象$stateRegistry,替换子模块的$stateProvider,因此子模块定义url的时候,就变成了:
multiple define
$stateRegistry
$stateProvider
// 注册相关url,依赖的注入也要修改 states.forEach(state => $stateRegistry.state(state))
路由处理完毕之后,就要考虑一下怎么把刚才的子模块在对应路由触发的时候,动态注入,这个在ui-router给出了一个参考,就是利用第三方的ocLazyLoad来支撑,在定义全局路由的时候,表明懒加载,例如:
[{ name: 'foo.**', url: '/foo', lazyLoad: function ($transition$) { return $transition$.injector().get('$ocLazyLoad').load('./fooModule.js'); } } }]
动态注入ok了,然后就是用webpack工具来打包分离代码,分离比较简单,教程在这里,有使用import和require.ensure的方法,这里就使用了import的方法,修改lazyLoad的动态注入方法:
import
require.ensure
lazyLoad
[{ name: 'foo.**', url: '/foo', lazyLoad: function ($transition$) { return import(/* webpackChunkName: "foo" */ './fooModule.js') .then(mod => { // mod.defatut 是因为fooModule.js export default ... $transition$.injector().get('$ocLazyLoad').load(mod.default) }) } } }]
这样子webpack打包文件的时候会分割代码,当该模块触发的时候,再请求该模块的文件,然后给到ocLazyLoad来动态注入,实现按需加载,当访问过该模块的时候,下次进入已加载过的模块,也不会再次发出请求模块文件
到这里,整个流程就跑通了,回顾一下几个关键点:
ocLazyLoad
这次也是在填ng1.x的一些坑,使用相对较旧的框架实现一些看起来比较简单的需求,有时候也是挺折腾的。或许像同事所说的这是旧框架与人民日益增长的需求之间的矛盾。
回顾之前用hexo写文章的时候,换了电脑之后,源文件又要重新找,而且过程搭建也是挺麻烦的,所以这次写博客用到同事贡献自动化博客,安利一下地址,仅仅用github的issue就可以写了,而且不怕丢失了,而且一次配置,绝无手尾,写完issue就能更新到我们的博客了。END
前言
自从工作之后就极少写文章了,因为空闲时间没这么多,到了周末又想轻松两天,但是周末其实并不轻松,或许归根到底最后就是一个字:懒!这周回家之后,感觉总算可以静下心来做点东西,把之前在项目用ng1.x按需加载的实现整理一下。
需求背景
最近工作用到
angularJs
,也就是ng1.x
版本开发一个网站,这个项目其中用ui-router来控制路由,webpack来构建项目。有个比较致命的痛点,ng1.x官方不支持懒加载!这个项目经过webpack打包之后主要形成两个js文件,一个是
vendor.js
,是引入的node_modules
的公用文件,另外一个是app.js
,是自己写的js文件。处理之前项目的js入口文件大概是这样子的;
从这个入口文件就可以看出,现在所有依赖的外部工具库和自己编写的内容都是一次性引入进来,尽管通过webpack来分开了两个文件,但是在入口的html文件还是一次引入了。特别是进入首页介绍页面的时候,逻辑功能比较少,但是却要加载全部功能。
解决思路
路由分模块定义
因为项目通过一级url分工明显,每个url可以分离成一个模块,处理起来就更直观,例如:
因此需要从
ui-router
先下手,能够指定一级url之后,交给对应的模块处理,对应的模块内部再处理子url,这样子每个模块之间就更加明确。从ui-router官网参考的例子如下:这里的例子大概意思是url为
/contact
的命名是contact.**
,然后通过layLoad
的函数加载对应的模块逻辑处理。这里要引入一个概念,叫futureState
,字面上的意思是未来的状态,就是预先定义的。在配置页面全局路由的时候,大概就是这样子:然后具体
foo
模块就定义对应的二级url:但是,这样子并跑不通,会报一个
multiple define
的多重定义的错,然而ui-router官方并没有给出相关例子,刚才的foo
模块定义是根据之前定义全局模块的定义。直到后来在stackflow找到了ui-router注入对象$stateRegistry
,替换子模块的$stateProvider
,因此子模块定义url的时候,就变成了:动态注入
路由处理完毕之后,就要考虑一下怎么把刚才的子模块在对应路由触发的时候,动态注入,这个在ui-router给出了一个参考,就是利用第三方的ocLazyLoad来支撑,在定义全局路由的时候,表明懒加载,例如:
分离代码
动态注入ok了,然后就是用webpack工具来打包分离代码,分离比较简单,教程在这里,有使用
import
和require.ensure
的方法,这里就使用了import
的方法,修改lazyLoad
的动态注入方法:这样子webpack打包文件的时候会分割代码,当该模块触发的时候,再请求该模块的文件,然后给到ocLazyLoad来动态注入,实现按需加载,当访问过该模块的时候,下次进入已加载过的模块,也不会再次发出请求模块文件
总结
到这里,整个流程就跑通了,回顾一下几个关键点:
$stateRegistry
来注册路由;ocLazyLoad
实现动态注入;这次也是在填ng1.x的一些坑,使用相对较旧的框架实现一些看起来比较简单的需求,有时候也是挺折腾的。或许像同事所说的这是旧框架与人民日益增长的需求之间的矛盾。
安利
回顾之前用hexo写文章的时候,换了电脑之后,源文件又要重新找,而且过程搭建也是挺麻烦的,所以这次写博客用到同事贡献自动化博客,安利一下地址,仅仅用github的issue就可以写了,而且不怕丢失了,而且一次配置,绝无手尾,写完issue就能更新到我们的博客了。END