古人云:不做死就不會死css
本人的項目是 vue + webpack,vue單文件中使用了 Jade 模板與 less 預編譯器html
原由是由於談論到 Jade 模板問題,Jade 早已更名爲 Pug,而且發佈了2.0版本,想着不如升級了吧,順便把 webpack 與 vue 也一併升級了,事實證實,升級需謹慎 = =vue
首先以前的版本以下:node
"vue": "2.4.2"
"webpack": "2.7.0"複製代碼
升級後的版本爲:webpack
"vue": "2.5.7"
"webpack": "3.8.1"複製代碼
運行 npm update
,一切正常,然而運行 vue 項目就開始報錯了git
// webpack 報錯
(node:45948) DeprecationWarning: loaderUtils.parseQuery() received a non-string value which can be problematic, see https://github.com/webpack/loader-utils/issues/56
parseQuery() will be replaced with getOptions() in the next major version of loader-utils.
// vue報錯
// 略過一些重複的報錯
warning in ./src/pages/List.vue
(Emitted value instead of an instance of Error) the "scope" attribute for scoped slots have been deprecated and replaced by "slot-scope" since 2.5. The new "slot-scope" attribute can also be used on plain elements in addition to <template> to denote scoped slots.
@ ./src/pages/List.vue 9:2-305
@ ./src/router/index.js
@ ./src/main.js複製代碼
咱們一個個的分析報錯的緣由並改正github
node 中的報錯很不友好,光從報錯中很難發現出問題的地方在哪,咱們甚至不知道這是 webpack 的報錯仍是哪一個模塊的錯誤,抑或是本身代碼的錯誤,只能經過錯誤信息中找線索,第一行的報錯信息中能夠提取出以下信息web
loaderUtils.parseQuery()
方法引發的錯誤loader-utils
能夠說是一個 webpack 的方法更新引發的錯誤,且和 loader 相關vue-router
這是因爲webpack的一個loader
相關的公用方法更新致使的,所以須要更新全部的相應的loader,若是你的項目下有npm
所有更新到最新版就能夠了
不過先別急,即便如此仍會有遺漏,所以咱們還須要在node_module
文件中查找使用到loaderUtils.parseQuery
方法的模塊,也一樣更新到最新版,例以下圖中的html-webpack-plugin
模塊
咱們能夠先看一下報錯中提到的 issue
The deprecation warning is for webpack loader authors, because calling parseQuery with a non-string value can have bad side-effects (see below). If you're a webpack user, you can set process.traceDeprecation = true in your webpack.config.js to find out which loader is causing this deprecation warning. Please file an issue in the loader's repository.
Starting with webpack 2, it is also possible to pass an object reference from the webpack.config.js to the loader. This is a lot more straight-forward and it allows the use of non-stringifyable values like functions.
For compatibility reasons, this object is still read from the loader context via this.query. Thus, this.query can either be the options object from the webpack.config.js or an actual loader query string. The current implementation of parseQuery just returns this.query if it's not a string.
Unfortunately, this leads to a situation where the loader re-uses the options object across multiple loader invocations. This can be a problem if the loader modifies the object (for instance, to add sane defaults). See jtangelder/sass-loader#368 (comment).
Modifying the object was totally fine with webpack 1 since this.query was always a string. Thus, parseQuery would always return a fresh object.
Starting with the next major version of loader-utils, we will unify the way of reading the loader options:
We will remove parseQuery and getLoaderConfig
We will add getOptions as the only way to get the loader options. It will look like this:
const options = loaderUtils.getOptions(this);複製代碼
The returned options object is read-only, you should not modify it. This is because getOptions can not make assumptions on how to clone the object correctly.
這個報錯是給那些寫
loader
模塊的人看的(一個微笑的表情.jpg),在webpack2中,能夠在配置裏傳遞一個對象(或者function)給loader
,因爲一些緣由,如今是經過query
這個變量傳給loader
的上下文的如今,
query
這個變量有了多重意思(便是一個options
對象,又是一個查詢參數),當一個場景用了多個loader
的時候(例如),這個對象可能會被其中一個loader
修改webpack1的時候由於
query
是string
類型的因此沒問題爲了不不可預知的狀況,因此作了以下修改
- 刪了
parseQuery
和getLoaderConfig
方法- 加了
getOptions
方法去讀取loader
的options
對象,且這個方法返回值是隻讀的,不能修改所以,當你升級了webpack後,就須要配套的升級相應的全部
loader
,以及全部用到該方法的模塊,還真是強制性的呢...不過若是你的
webpack.config.js
中沒有用到多個loader
,且query
只是用於查詢參數的話,基本上只會提示錯誤,但仍是能正常編譯的原文還好心的提醒了咱們一下如何調試 webpack 以查明是哪一個 loader 致使的報錯,能夠在 webpack.config.js 中設置 process.traceDeprecation = true
可能不多人會遇到這個錯誤,這是因爲2.5版本之後 vue 對做用域插槽的使用方式更新形成的,錯誤信息裏說的也比較直觀
官方文檔說明:做用域插槽
將全部用到做用域插槽的地方,把屬性名 scope 換成 slot-scope 便可
在以前(version >= 2.1),若是咱們想寫一個通用的列表組件,同時想讓不一樣列表中每一個子元素有本身獨特的交互,該怎麼作呢
咱們能夠寫一個列表組件,寫兩個子元素組件,經過做用域插槽將這些組件組合在一塊兒,即實現了列表的一致性,又解耦了列表與列表內元素的耦合性,舉一個很簡單的例子
咱們先寫一個 List 組件,它的樣式是一個 ul 無序列表,且有一個外層 border
[ html ] <div class="list"> <ul> <li v-for="item in items"> {{item}} </li> </ul> </div> [ js ] const List = { props: { items: { type: Array, default: [] } } } // 調用 <list :items="['a', 'b', 'c', 'd', 'e']"></list> 複製代碼
![]()
若是咱們有個新需求,這時候須要展現一個列表A,這個列表的每一行都須要添加一個 title ,該怎麼作
- 新寫一個 List 組件,顯然不太合適,由於只有一點變化但要複製整個組件,失去了組件的意義
- 加一個判斷條件 isA 來控制 item 的展現方式,簡單來講是能夠的,可是若是之後 item 的種類愈來愈多,交互也愈來愈騷怎麼辦(例如加一個動畫效果),若是都放在 List 組件裏,那這個組件能夠預計的代碼將突破上千行,每調整一處甚至須要考慮其餘列表的兼容,pass
- 讓每一個 item 抽離出來,獨立的控制本身的交互與樣式,List 組件控制本身的樣式,不負責 item 的交互,這個看起來很美好,幸運的是,vue 也提供了一樣美好的使用方式
接下來咱們修改一下上面的代碼,首先修改 List 組件,將本來的
li
標籤改爲一個slot
[ html ] 原來的 li 標籤能夠保留,看成一個默認樣式 <div class="list"> <ul> <slot name="item" v-for="item in items" :item="item"> <li> {{item}} </li> </slot> </ul> </div>複製代碼
添加一個 Item 組件
[ html ] 這個 li 標籤與默認的不一樣,在每一個 item 的前面加了一句描述 <li> 這是A列表:{{ item }} </li> [ js ] const ItemA = { props: { item: { type: Number, default: 0 } } }複製代碼
最後要怎麼使用呢,若是是 version >= 2.1 的狀況下
<list :items="[0, 1, 2, 3, 4]"> <template slot="item" scope="props"> <item-a :item="props.item"></item-a> </template> </list>複製代碼
![]()
能夠看到,列表的樣式按照 Item 的自定義樣式展現出來了,每一行都加了一個通用的描述在 vue 的 version >= 2.5 後,使用的方式略微有些變化,可是更加直觀,去掉了中間的 template 組件,而且把屬性名 scope 改成了 slot-scope
<list :items="[0, 1, 2, 3, 4]"> <item-a slot="item" slot-scope="props" :item="props.item"></item-a> </list>複製代碼
這也是這個報錯所提示的地方
以上代碼的 demo 能夠參見 這裏
當你覺得大功告成的時候,代碼總會給你點驚喜
通過了以上的修改,編譯成功了,也不報錯了,覺得能夠安心的繼續以前的工做的時候,打開瀏覽器看到的倒是一個新的報錯
[Vue warn]: Failed to mount component: template or render function not defined.
found in
---> <Anonymous>
<App> at src\App.vue
<Root>
......複製代碼
WTF?這個提示直譯過來就是加載不了組件: 找不到模板或者沒有 render
然而代碼沒有變過,組件也確實引入了,因而百度大法、StackOverflow大法、GitHub issue大法,不論怎麼搜最終都是一個解決方案,甚至 vue 官方也寫了這個方案,那就是
module.exports = {
// ...
resolve: {
alias: {
'vue$': 'vue/dist/vue.esm.js' // 'vue/dist/vue.common.js' for webpack 1
}
}
}複製代碼
加了之後會發現沒起做用,爲何呢,仔細一想,這個是由於在運行的時候有動態編譯的模板,須要加入編譯器才作的配置,而個人項目中並無須要在運行的時候編譯的模板,很明顯是別的緣由
先說解決方案
require
引入組件的話,所有改成 require(xxx).default
import
方式,() => import(xxx)
在引入組件的時候,除了ES6的
import
以外,還能夠用 webpack 的require
,好比,在個人路由配置裏,就寫了大量的以下代碼routes: [ { path: '/', name: 'home', component: require('src/pages/Home.vue') } ... ]複製代碼
在舊版本里,這是ok的,能夠引入組件,可是最新版的 vue-loader 中,默認開啓了
ES modules
模塊,因而,要配合新的語法進行一些代碼上的更改咱們來看下更新文檔的說明 releases#v13.0.0
New
- Now uses ES modules internally to take advantage of webpack 3 scope hoisting. This should result in smaller bundle sizes.
- Now uses PostCSS@6.
Breaking Changes
- The
esModule
option is nowtrue
by default, because this is necessary for ES-module-based scope hoisting to work. This means the export from a*.vue
file is now an ES module by default, so async components via dynamic import like this will break:const Foo = () => import('./Foo.vue')複製代碼
Note: the above can continue to work with Vue 2.4 + vue-router 2.7, which will automatically resolve ES modules' default exports when dealing with async components. In earlier versions of Vue and vue-router you will have to do this:const Foo = () => import('./Foo.vue').then(m => m.default)複製代碼
Alternatively, you can turn off the new behavior by explicitly using esModule: false in vue-loader options. Similarly, old CommonJS-style requires will also need to be updated:// before const Foo = require('./Foo.vue') // after const Foo = require('./Foo.vue').default複製代碼
- PostCSS 6 might break old PostCSS plugins that haven't been updated to work with it yet.
文檔裏說的很清楚了
- 能夠在
vue-loader
的options
裏經過esModule: false
配置來關閉 ES 模塊或者
- 同步引入組件,正經常使用
import
,而原來使用require
引入 ES6 語法的文件(例如:export default {...}
),如今須要多加一個default
屬性來引用- 異步引入組件,須要用動態
import
語法注:若是使用的是 Babel,須要添加 syntax-dynamic-import 插件,才能使 Babel 能夠正確地解析語法。
至此,這次升級的坑所有填完,仍是要吐槽一下,沒事千萬不要隨便升級,雖然可使用新的功能,前提是你有時間踩坑且短期內能將代碼修復,不然仍是乖乖保持原樣吧
最後,我我的建議,在 package.json
裏最好將版本寫死,或者寫成 ~2.5.7
的形式
而默認的寫法 ^2.5.7
會自動下載 2.x.x
版本的模塊,若是有新員工加入,頗有可能裝了最新的包,致使他的環境與你的或者線上的有很大出入
謝謝各位觀看,若是能解決你目前遇到的問題,那對我來講就是最大的欣慰了