首先場(chǎng)景如下:
- 構(gòu)建一個(gè)lib包,內(nèi)含多個(gè)vue組件,父工程通過Vue.use方式進(jìn)行全局引入
- 由于組件可能會(huì)很多很大,所以想實(shí)現(xiàn)這個(gè)lib包里面的組件實(shí)現(xiàn)懶加載
- 初步設(shè)想的是采用動(dòng)態(tài)導(dǎo)入(dynamic imports)實(shí)現(xiàn)懶加載
- (element-ui的按需加載方式好像是通過babel-component-plugin將未使用的component標(biāo)記后,通過tree shaking優(yōu)化掉的,所以不適合這里的懶加載)
lib包代碼:
function install (Vue, options) {
Vue.component('d2p-file-uploader', () =>import('./lib/file-uploader'))
Vue.component('d2p-images-format', () =>import('./lib/images-format'))
}
打包出來是這個(gè)樣子:
看上去還不錯(cuò),成功按照懶加載的方式打成多個(gè)chunk了

但是,引用這個(gè)lib之后,報(bào)如下錯(cuò)誤:
Uncaught SyntaxError: Unexpected token <
vue.runtime.esm.js:619 [Vue warn]: Failed to resolve async component: function(){return t.e(3).then(t.bind(null,"50bf"))}
Reason: ChunkLoadError: Loading chunk 3 failed.
(missing: http://localhost:8080/d2p-extends.umd.min.3.js)
為啥會(huì)報(bào)這個(gè)錯(cuò)呢:
仔細(xì)看上面的錯(cuò)誤是懶加載50bf這段chunk的時(shí)候,d2p-extends.umd.min.3.js這個(gè)文件找不到
其實(shí)因?yàn)閐2p-extends.umd.min.3.js這段js是在lib里面的,而引用它的父工程打包出來的js是這樣的

lib包里面懶加載分離出來的chunk并沒有按照原來的名字復(fù)制到父工程的dist/js目錄下面
所以這種lib的打包方式并不能實(shí)現(xiàn)lib里面的component懶加載
然后baidu、google、Stack Overflow、github一通查:
最后在webpack的issue上查到了一些對(duì)于這個(gè)問題的討論
https://github.com/webpack/webpack/issues/2471
該問題被標(biāo)記為困難,16年問題就提出來了,18年1月關(guān)閉時(shí)候都沒有解決,看來這個(gè)需求實(shí)現(xiàn)還是真的挺麻煩的

然后18年9月又被人打開了,至今未解決
https://github.com/webpack/webpack/issues/6818

所以結(jié)論是目前為止webpack暫時(shí)不支持lib里的動(dòng)態(tài)導(dǎo)入方式的懶加載
但是Stack Overflow上提到了一個(gè)解決方案
https://stackoverflow.com/questions/54149892/webpack-cannot-resolve-node-modules-lazy-assets

簡(jiǎn)單來說,就是通過源碼方式導(dǎo)入,而不是dist/xxx.umd.js導(dǎo)入。
這樣就相當(dāng)于把lib包放到父工程里面去重新打包,就跟你把lib包里面的源代碼復(fù)制到你的父工程里面一樣。(只是這樣就要求父工程與lib的eslint配置要一致,可以通過設(shè)置忽略來解決)
所以拋棄dist通過src方式引入即可獲得懶加載功能
1、方式一:lib包里面的package.json的main改成 'src/index'
{
"main": "src/index"
}
2、方式二:父工程import src目錄
import componentName from 'subLibName/src'
懶加載效果
項(xiàng)目地址:https://github.com/greper/d2-crud-plus
示例地址:http://qiniu.veryreader.com/D2CrudPlusExample/index.html#/form/uploader

最后幾個(gè)附加題
1. 如果這個(gè)lib還想提供給那些不追求懶加載的人通過dist方式引用呢?
因?yàn)槲覀兤鋵?shí)并沒有解決dist引入時(shí)發(fā)生的ChunkLoadError: Loading chunk 3 failed錯(cuò)誤
要解決這個(gè)問題,就是不讓他打包成多個(gè)chunk
翻遍了vue-cli的文檔,發(fā)現(xiàn)沒有什么簡(jiǎn)單的方法關(guān)閉它。
然后去webpack去找,最后找到一個(gè)插件“LimitChunkCountPlugin”,它本是一個(gè)用來優(yōu)化合并小chunk的插件,但是它有一個(gè)maxChunks參數(shù),可以限制最大chunk數(shù)量,把它設(shè)置為1就可以實(shí)現(xiàn)不打包成多個(gè)chunk的目的了。
vue.config.js 中配置如下插件即可
const webpack = require('webpack')
const plugins = [new webpack.optimize.LimitChunkCountPlugin({
maxChunks: 1,
minChunkSize: 1000
})]
module.exports = {
configureWebpack: {
plugins: plugins
}
}
2. 既然報(bào)的錯(cuò)誤是懶加載的chunk下載不到,寫個(gè)插件從lib里面把相關(guān)的chunk復(fù)制出來是不是也可以?
理論上確實(shí)可以,我試過手動(dòng)從lib里面把chunk復(fù)制出來放到父工程打包后的/js/目錄下,部署執(zhí)行完全沒有問題。
這個(gè)方式可以解決源碼導(dǎo)入的缺陷(父工程與lib之間eslint配置要求一致的問題)
這樣的插件寫起來應(yīng)該蠻簡(jiǎn)單。
但這樣一來就要求父工程一定要配置這個(gè)自定義的復(fù)制插件,使得這個(gè)lib的應(yīng)用受到一定的限制。
3. 更好的辦法
更好的辦法是將lib根據(jù)第1點(diǎn)打包成單個(gè)chunk,然后寫個(gè)插件,父工程在build的時(shí)候通過這個(gè)插件將lib拆分,把懶加載的模塊提取出來,寫到新的chunk里面。
這種方式即不破壞lib的通用性,又實(shí)現(xiàn)了lib組件的懶加載。