最近把一个比较旧的业余项目重新升级了下,将主文件进行了剥离,增加了些惰性加载的配置,将过程中一些零散的知识点做个总结,同时尽量深入原理实现层面。
项目环境:
前端框架:angular2.0.0-beta.21
构建工具:webpack4.11.0
加载惰性模块:
跑了下环境,再次验证了每次回头看以前自己写的东东的时候都会不能忍这一潜在的法则,发现首屏的加载事件竟然超过了1s,于是果断切割主资源包,进行按需加载
如果使用angular-cli命令工具,该需求基本自动化实现,只需要在对应的路由下配置需要懒加载的模块即可,其他工作如模块的异步加载等,cli会进行自动处理,如下所示
const routes: Routes = [
{
path: 'customers',
loadChildren: './customers/customers.module#CustomersModule'
}
];
但是一直偏向于自己构建脚手架,不喜欢拿来即用,看了下angular-cli的源码,其实也是基于webpack构建(部分配置如下),只是没看到其中异步加载模块和output输出中模块语义化的实现过程。只能自己实现模块的异步加载,解决“can not find module...”报错。
{
resolve: {
extensions: ['.ts', '.js'],
symlinks: !buildOptions.preserveSymlinks,
modules: [appRoot, 'node_modules'],
alias
},
resolveLoader: {
modules: loaderNodeModules
},
context: projectRoot,
entry: entryPoints,
output: {
path: path.resolve(buildOptions.outputPath),
publicPath: buildOptions.deployUrl,
filename: `[name].bundle.js`,
chunkFilename: `[id]${hashFormat.chunk}.chunk.js`
},
module: {
rules: [
{ test: /\.html$/, loader: 'raw-loader' },
{
test: /\.(eot|svg|cur)$/,
loader: 'file-loader',
options: {
name: `[name]${hashFormat.file}.[ext]`,
limit: 10000
}
},
{
test: /\.(jpg|png|webp|gif|otf|ttf|woff|woff2|ani)$/,
loader: 'url-loader',
options: {
name: `[name]${hashFormat.file}.[ext]`,
limit: 10000
}
}
].concat(extraRules)
},
plugins: [
new webpack.NoEmitOnErrorsPlugin()
].concat(extraPlugins)
}
查阅webpack文档,找到一个内置的ensure方法可实现模块异步,使用如下
require.ensure(dependencies: String[], callback: function(require), errorCallback: function(error), chunkName: String)
dependencies:字符串构成的数组,声明 callback 回调函数中所需的所有模块。
callback:只要加载好全部依赖,webpack 就会执行此函数。require 函数的实现,作为参数传入此函数。当程序运行需要依赖时,可以使用 require() 来加载依赖。函数体可以使用此参数,来进一步执行 require() 模块
errorCallback:当 webpack 加载依赖失败时,会执行此函数。
chunkName:由 require.ensure() 创建出的 chunk 的名字。通过将同一个 chunkName 传递给不同的 require.ensure() 调用,我们可以将它们的代码合并到一个单独的 chunk 中,从而只产生一个浏览器必须加载的 bundle
将方法应用到路由模块,代码如下:
export const routes: Routes = [
{
path: 'course',
loadChildren: () => new Promise(resolve => {
(require as any).ensure(
[],
require => {
resolve(require('../course/module').CourseModule);
},
'haha'
);
})
}
];
该方法基于浏览器内置promise对象,如果需要兼容多种环境,最好增加polyfill库来填充promise环境
最后
如果不想在路由模块写异步方法,也可以引入一些封装好的插件实现,如es6-promise-loader、angular2-load-children-loader,但其实也没有本质的变化,只不过稍做封装而已,意义不大,具体usage可github查看
先搞这些,依赖包升级乃至换个环境重写等下次再有闲情哈!!