不想看過程可以直接看最後的總結 :)
起因
前天的時候就遇到了一個非常棘手的問題,在高高興興寫完 Vue 項目後,使用
npm link
在别的項目裡導入自己的包報錯(這裡的變量都用 xxxx 或者 yyyy 來表示啦):
"export 'default' (imported as 'xxx') was not found in 'xxxx
但是如果我從 npm 上下載下傳自己的包是不報錯的,而使用
npm link
本地調試的時候就報上面的錯。而且我還自己點進原檔案看了下,npm 下載下傳的和本地
npm link
的
xxx.umd.js
根本就是一毛一樣啊,為什麼兩邊的結果不一樣呢?
谷歌了一波後發現别人也有有同樣的問題:
下面他就說了兩邊出現不同結果的問題:
尤大也回複了,但是他說這不是 vue-cli 的問題,是 webpack 自己沒有将檔案轉成 ES6 module 的形式。真的是看的我一臉問号。
為什麼報錯
沒辦法,他不說解決方法,我自己總得解決吧。首先将谷歌一下報錯資訊,得到的解答是:
同時使用了import
和 module.exports
文法,是以才報錯 。什麼?這怎麼可能,兩邊的項目肯定都沒這樣用過。除非是。。。。。我引入的那個打包後的檔案
xxx.umd.js
是有
module.exports
語句的。
難道真的是 Webpack 沒有轉成 ES6 的問題?
坑:官方文檔
第一反應是官方文檔應該有教怎麼打包項目的,裡面說不定有答案,是以回去認真讀了下文檔,可是:
woc,Webpack 真的不能轉成 ES6 Module 的啊。是以現在問題變成了:Webpack 将原來的項目打包成 ES5 的版本,然後在新項目裡用
import
裡引入了帶有
module.exports
語句的檔案,進而導緻上面的報錯。
坑:Vue CookBook
這時我注意到上面出現同樣問題的大哥說到可以用 Rollup.js 來打包,這就可以打包出 ES6 文法的 JS了。馬上開搞,然後查到了 Vue CookBook:
果然可以打包成
xxxx.esm.js
的方法,但是原來的項目是用 Webpack + Vue 來建構的,使用兩套打包工具真的好嗎?雖然不太好,我試了下,奈何在全局引入
.scss
檔案的地方試了一萬個方法都不行,隻能放棄這個方法。從頭開始分析。
Webpack 是真的坑
說實話那天我就放棄了,本地調試不行就算了吧,不就搞很多個 npm 版本麼。後面還是 Jetbrains 給了我靈感。
今天我想删除某個檔案的時候發現了這個選項:
Exclude?嗯。。。曾經的我就在 webpack.config.js 裡看到過這個選項,好像說是可以不對某些檔案進行編譯,這樣就能在
yarn run build
的時候提高性能。再結合一下前面分析的“沒有轉成 ES6 文法”的報錯,好像有點說通了。
非常有可能 Webpack 對從 npm 下載下傳下來的檔案進行預先編譯,将其轉成 ES6,而本地引入的話沒有預先編譯。
後面我做了如下測試: 1. 将 /dist 下所有檔案拷到新項目的 /src 裡,直接本地引入,同樣報錯。 2. 将 /dist 下所有檔案拷貝到新項目的 /node_modules 裡,直接本地使用
import '/node_modules/xxx/xxx.umd.js'
引入,成功! 3. 在新項目使用
npm link xxx
後,在 /node_modules 裡用上面的
import '/node_modules/xxx/xxx.umd.js'
引入,失敗。
再次分析
上面 1 和 2 的測試足以說明我的猜想是對的,Webpack 會對 /node_modules 下的檔案進行預先編譯,再引入到真實項目中,這樣就沒有module.exports
的語句了,是以也就不會報錯了。 但是為什麼 3 也失敗呢?我在指令行裡輸入
ls -a
也發現自己的 xxx 包呀,說明我的原來的項目包也在 /node_modules 下呀。後面想到 Mac 的
link
指令,
npm link
說不定是建立軟連結而已,是以用 Finder 打開新項目的 /node_modules ,果然這是個軟連結:
而這個軟連結指向的真實位址是本機的别的位址,也就是說這個包不在項目的 /node_modules 檔案夾下。是以不會預先被編譯,再次印證上面的猜想。
現在終于真相大白了。
總結
為什麼報錯
如果使用下載下傳的 npm package,那麼 Webpack 在項目引入前将代碼編譯成 ES6 子產品文法,是以這時候不會報錯。
如果使用
npm link
會将 npm 包放在本機的全局 /node_modules 下,新項目的 /node_modules 下隻是一個軟連結(快捷方式)。而不在新項目 /node_modules 下的檔案都不會預先編譯成 ES6 子產品方法。在項目裡引入也就等同于下面代碼:
// B.js
module.exports = {
}
// A.js
import "B.js"
而這兩種文法混合使用就會報錯:
"export 'default' (imported as 'xxx') was not found in 'xxxx
解決方法
我簡單搜尋了一下沒找到什麼解決方法(真的不知道要怎麼搜這種問題了)。是以現在最笨的方法就是每次
yarn run build
後将 /dist 目錄拷到别的項目的 /node_modules 下,然後在那個項目引入就可以了。