webpack配置起來確實麻煩,這不,之前用剛配好了vue1+的版本,結果在(部分)安卓機上測試,發現存在開啟熱加載(dev-server)的情況下不能識別vue語法的問題,試了很多方法,都沒能很好的解決,最后索性將vue升級到2+,竟然就能識別了,好吧!
1.先分享一下webpack配置vue2+的一些不同(本人親測):
(1)dependencies中的vue默認安裝2+,直接運行,會報如下錯:[Vue warn]: Failed to mount component: template or render function not defined。
如果dependencies中的vue選擇^1.0.26,那么devDependencies中對應的vue-loader最好選擇^7.3.0,vue-hot-reload-api最好選擇^1.2.0,否則就會報錯。
(2)如果vue選擇安裝1+,dependencies中的vue-router最好選擇^0.7.13(默認安裝2+,無法識別router.map()這個方法)。
(3)如果vue選擇安裝1+,dependencies中的vue-validator最好選擇^2.0.0(默認安裝2.1.7)。
(4)如果vue選擇安裝1+,后面在開啟webpack dev server的時候,處於同一內網中的安卓手機訪問本地設備的輸出頁面會出現不識別vue語法的兼容問題,ios手機可以正常訪問和解析,但是開啟別的server再來訪問並不會出現這種兼容問題,所以為了測試方便,建議vue選擇安裝2.0的版本。
(5)如果vue選擇安裝2+,vue2.0有兩種構建模式,默認情況下運行構建,但是不能解析單文件的template模板,所以要使用獨立構建,需要在alias中指定vue$的模塊別名地址,即
// 其他解決方案 resolve: { // require時省略的擴展名,遇到.vue結尾的也要去加載 extensions: ['','.js', '.vue'], // 模塊別名地址,方便后續直接引用別名,無須寫長長的地址,注意如果后續不能識別該別名,需要先設置root root:"../node_modules", alias:{ 'vue$':'vue/dist/vue.js' } },
(6)如果vue選擇安裝2+,對應的vue-validator就必須安裝3+,否則會報錯,但是這樣一來在開啟webpack dev server的時候,又會出現安卓手機不識別vue-validator的兼容問題,ios手機可以正常訪問和解析,所以為了測試方便,改用其他的基於vue的表單驗證插件,即vuerify,需要引入vue2+、vuerify和v-vuerify-next。
(7)如果vue選擇安裝2+,涉及到的loader盡量升級到最新,否則會報錯;最好是將vue升級到2.0.7,對應的vue-loader升級到8.5.4,vue-html-loader升級到1.2.3,vue-hot-reload-api升級到2.0.6。
(8)如果vue選擇安裝2+,對應的router要升級到2+,但是這時之前使用的表單驗證插件就會做出問題(例如:vue-validator-3.0.0-alpha.1和vuerify+v-vuerify-next這兩款插件在路由跳轉的時候都會報錯,嘗試了很多方法都無濟於事,感覺還是版本匹配的問題),awesome-vue集合了來自社區貢獻的數以千計的插件和庫,在這里我找了一些專門針對vue2+的表單驗證插件,發現Vee-Validate和Vue-Easy-Validator這兩款插件沒有出現之前遇到的問題,而且前者的英文文檔寫得相當詳細,贊之。
2.不要小瞧版本匹配問題,webpack自帶插件和第三方插件,vue和里面需要引入的插件,再加上各種模塊加載器,有時匹配對了一個,另一個又會出問題,我就是在這里浪費了太多的時間,最后索性跟着版本走,強勢擁抱2+。關於vue2+的語法,官網里寫得很詳細,這里就不贅述,我還是接着講webpack配置問題吧:
(1)配置文件里的入口和出口:
// 入口文件,路徑相對於本文件所在的位置,可以寫成字符串、數組、對象 entry: { // 以下是單頁面的入口路徑 index: path.resolve(__dirname, "../entry/index.js"), // 需要被提取為公共模塊的群組 vendors: ["vue","vue-router","vue-resource","vee-validate","jquery"], }, // 輸出配置 output: { // 輸出的js文件,路徑相對於本文件所在的位置 path: path.resolve(__dirname, "../output/js/"), // 將入口文件中涉及到的同步加載的js文件打包成一個js文件,基於文件的md5生成hash名稱的script來防止緩存 filename: "[name].[hash].js", // 異步加載的業務模塊,例如按需加載的.vue單文件組件 chunkFilename: "[id].[name].[chunkHash].js" }
這里需要注意的點不少,我主要說兩個:
一個是publicPath,上次說過測試環境里寫成 config.output.publicPath = "/",不建議在生產環境里動它,但最近導出文件的時候如果不設置publicPath,按需加載的.vue單文件組件中的script路徑會報錯,所以還是得設置一下publicPath,其路徑可以寫成相對於生成的html單文件所在位置的相對路徑;
另一個就是chunkFilename,上次沒怎么提它,主要是沒怎么用到它,如果項目里涉及到異步加載的業務模塊,就不得不提它了。如果使用AMD風格的requireJS來實現路由組件的懶加載,例如:
const Register = resolve => require(["../src/private/components/register"],resolve);
這樣寫的話,這個組件就不會和entry中引入的js文件一起打包,而是單獨打包成一個js文件,名字就是這里的chunkFilename,帶一個自動分配的,可讀性很差的[id]。如果想在命名的時候更有歸屬感,即帶上一個[name],可以使用require.ensure來實現路由組件的懶加載,例如:
const Register = resolve => require.ensure(["../src/private/components/register"], () => resolve(require("../src/private/components/register")), "register");
如果要把某個路由下的所有組件都打包在同個異步chunk中,無須明確列出require.ensure的依賴,即傳空數組就行。如果你還想在按需加載某個模塊的同時執行一些代碼,可以寫成:
const Register = resolve => { require.ensure(["../src/private/components/register"], () => { // 這里可以寫異步加載指定模塊之前的代碼 resolve(require("../src/private/components/register")); // 這里可以寫異步加載指定模塊之后的代碼 },"register") }
(2)在測試環境中寫了 config.output.publicPath = "/" 之后,當前配置文件下的很多相對路徑都是相對於這個來設定,即很多涉及到相對路徑的地方需要發生相應的變化,否則開啟dev-server之后會報錯找不到文件的錯誤,那么有哪些地方需要改呢,我個人建議改以下幾個地方:
某些模塊加載器的路徑,例如加載圖片的url-loader和加載圖標的file-loader;
某些插件的路徑,例如生成單個html文件的HtmlWebpackPlugin,提取css單文件的ExtractTextPlugin
(3)為了防止“找不到favicon.ico文件”這種錯誤帶來的干擾,找一張圖塞到項目根目錄下,輸出的時候直接在 new HtmlWebpackPlugin 插件參數列中寫 favicon: "favicon.ico",然后開啟dev-server就不會報錯啦,但是生產環境下還是會報錯,原因是導出的位置和輸出的js文件同級,所以得把它重新塞到和輸出的html文件同級,這里我是用的CopyWebpackPlugin這款插件(第三方插件),代碼如下:
// 把指定的文件復制到指定的目錄 new CopyWebpackPlugin([ // from寫的是源文件名,這里的位置是在項目根目錄下,to是寫將要復制過去的目錄位置,相對於輸出的js文件 {from:'favicon.ico',to:"../html/favicon.ico"} ])
(4)對於.vue單文件,css默認是內部樣式,現在要把它里面的css提取出來變成外部導入,但是如果.vue單文件組件是按需加載,那么此設置無效,即會重新變回內部樣式(也可能是我自己弄錯了),代碼如下:
vue: { loaders: { css: ExtractTextPlugin.extract('vue-style-loader', 'css-loader'), } }
(5)config.devtool這個就看自己喜好吧,開發環境下推薦使用cheap-module-eval-source-map,生產環境下推薦使用cheap-source-map或source-map,后者得到的.map文件體積比較大,但是能夠完全還原以前的js代碼
(6)如果要提取入口文件里面的公共模塊,配置文件中必須要有以下三步:
entry: { // path.resolve([from ...], to) 將to參數解析為絕對路徑 index:path.resolve(__dirname, '.index.js'), // 需要被提取為公共模塊的群組 vendors:['vue','vue-router','jquery'] }, new webpack.optimize.CommonsChunkPlugin({ name: 'vendors', filename: 'vendors.js', }),
第三步是 new HtmlWebpackPlugin 插件參數列里的chunks里一定要引入vendors;
(7)如果將css單獨提取出來,配置文件中必須要有以下三步:
var ExtractTextPlugin = require('extract-text-webpack-plugin'); // module.loaders里添加 { test: /\.css$/, // loader: 'style-loader!css-loader', // 將樣式抽取出來為獨立的文件 loader: ExtractTextPlugin.extract("style-loader", "css-loader"), exclude: /node_modules/ } new ExtractTextPlugin("../css/[name].[contenthash].css")
3.其他需要注意的地方:
(1)html文件中最好只寫和html相關的標簽語言,盡量不要導入外部的css或者js文件,也盡量不要寫內部樣式或者在<script>標簽里寫js代碼
(2)css文件中寫url的時候注意路徑問題,特別是在配置文件里設置了publicPath的情況下
(3)js文件中涉及到文件引入的,可以用require或define或import from,注意采用這種引入的前提是被引入文件已經進行了模塊化的代碼規范
(4)如果不想單獨引入某個文件,可以在全局掛載插件中進行設置,區別於window掛載
(5)vue組件中涉及到圖片路徑(主要是src形式)報錯的,可以在路徑外層加上require(),這樣測試環境下是沒有問題的,但是生產環境下可能還會報錯,關鍵在於弄清楚兩種環境下圖片的相對路徑分別是相對於誰
(6)webpack加載css的時候,遇到font-family一定要去掉屬性值的空格和雙引號,不然解析出來的樣式會出錯
未完待續~
