Webpack+Vue+ES6 前端組件化開發mobile-multi-page應用實戰總結
本文版權歸博客園和作者吳雙本人共同所有 轉載和爬蟲請註明原文地址 www.cnblogs.com/tdws
一.寫在前面
項目上線有一段時間了,一個基於webpack+vue+ES6的手機端多頁面應用。其實說是多頁面應用,實際上在webpack中屬於四個app, 如果真是做純單頁面,那應該有二三十個頁面吧。所以我這裏的多頁面應用,是分為四個SPA。比如微信最下面,有四個導航,微信,通訊錄,發現,我。 那麽這四個導航,就是我的四個SPA,配置多個入口即可。
在這裏就不說太多代碼了,項目結構將會放到github上,地址在後面給出,以供參考,上傳的大概只是一個目錄加上配置情況,其實關鍵點也就在webpack.config.js了,這裏主要配置了entry,loader,plugins,output目錄啥的。
在這裏先附上package.json和webpack.config.js吧:
1 { 2 "name": "my-web", 3 "version": "1.0.0", 4 "description": "desc", 5 "main": "index.js", 6 "scripts": { 7 "test": "echo \"Error: no test specified\" && exit 1", 8 "start": "webpack-dev-server --inline --hot",View Code9 "dev1": "webpack-dev-server --open", 10 "dev": "webpack-dev-server --inline --hot", 11 "build": "set NODE_ENV=production&&webpack" 12 }, 13 "author": "ws", 14 "license": "ISC", 15 "devDependencies": { 16 "babel-core": "^6.24.1", 17 "babel-loader": "^7.0.0",18 "babel-plugin-transform-runtime": "^6.23.0", 19 "babel-preset-es2015": "^6.24.1", 20 "babel-runtime": "^6.23.0", 21 "css-loader": "^0.28.4", 22 "extract-text-webpack-plugin": "^2.1.0", 23 "glob": "^7.1.2", 24 "html-webpack-plugin": "^2.28.0", 25 "jquery": "^3.2.1", 26 "node-sass": "^4.5.3", 27 "sass-loader": "^6.0.5", 28 "slideout": "^1.0.1", 29 "style-loader": "^0.18.2", 30 "url-loader": "^0.5.8", 31 "vue": "^2.3.3", 32 "vue-croppa": "^0.1.0", 33 "vue-hot-reload-api": "^2.1.0", 34 "vue-html-loader": "^1.2.4", 35 "vue-ios-alertview": "^1.1.1", 36 "vue-loader": "^12.2.1", 37 "vue-resource": "^1.3.3", 38 "vue-router": "^2.7.0", 39 "vue-style-loader": "^3.0.1", 40 "vue-template-compiler": "^2.3.3", 41 "vue-touch": "^2.0.0-beta.4", 42 "webpack": "^2.6.1", 43 "webpack-dev-server": "^2.4.5" 44 } 45 }
1 var path = require(‘path‘); 2 var webpack = require(‘webpack‘); 3 // 將樣式提取到單獨的 css 文件中,而不是打包到 js 文件或使用 style 標簽插入在 head 標簽中 4 var ExtractTextPlugin = require(‘extract-text-webpack-plugin‘); 5 // 生成自動引用 js 文件的HTML 6 var HtmlWebpackPlugin = require(‘html-webpack-plugin‘); 7 var glob = require(‘glob‘); 8 9 var entries = getEntry(‘./source/**/*.js‘); // 獲得入口 js 文件 10 var chunks = Object.keys(entries); 11 console.log(‘輸出chunks‘, chunks); 12 module.exports = { 13 entry: entries, 14 output: { 15 path: path.resolve(__dirname, ‘public‘), // html, css, js 圖片等資源文件的輸出路徑,將所有資源文件放在 Public 目錄 16 publicPath: ‘/public/‘, // html, css, js 圖片等資源文件的 server 上的路徑 17 filename: ‘js/[name].js‘, // 每個入口 js 文件的生成配置 18 chunkFilename: ‘js/[id].[hash].js‘ 19 }, 20 externals: { 21 jquery: "$", 22 EXIF: "EXIF", 23 wx: "wx" 24 }, 25 resolve: { 26 extensions: [‘.js‘, ‘.vue‘], 27 alias: { 28 ‘vue‘: __dirname + ‘/lib/vue/vue.js‘, 29 //‘vue-alert‘: __dirname + ‘/lib/vue-alert/vue-alert.js‘ 30 }, 31 }, 32 33 module: { 34 loaders: [ 35 { 36 test: /\.css$/, 37 // 使用提取 css 文件的插件,能幫我們提取 webpack 中引用的和 vue 組件中使用的樣式 38 //loader: "style-loader!css-loader", 39 loader: ExtractTextPlugin.extract({ fallback: ‘style-loader‘, use: ‘css-loader‘ }) 40 }, 41 { 42 // vue-loader,加載 vue 組件 43 test: /\.vue$/, 44 loader: ‘vue-loader‘, 45 options: { 46 //解析.vue文件中樣式表 47 loaders: { 48 // Since sass-loader (weirdly) has SCSS as its default parse mode, we map 49 // the "scss" and "sass" values for the lang attribute to the right configs here. 50 // other preprocessors should work out of the box, no loader config like this necessary. 51 //‘scss‘: ‘vue-style-loader!css-loader!sass-loader‘, 52 //‘css‘: ‘vue-style-loader!css-loader!sass-loader‘, 53 //‘js‘: ‘babel-loader‘, 54 //‘sass‘: ‘vue-style-loader!css-loader!sass-loader?indentedSyntax‘ 55 css: ExtractTextPlugin.extract({ fallback: ‘vue-style-loader‘, use: ‘css-loader‘ }), 56 //exclude: [ 57 // path.resolve(__dirname, ""), 58 // //path.resolve(__dirname, "app/test") 59 //] 60 //exclude:‘/source/course/course-detail/course-detail.css‘ 61 } 62 // other vue-loader options go here 63 } 64 }, 65 { 66 test: /\.js$/, 67 // 使用 es6 開發,這個加載器幫我們處理 68 loader: ‘babel-loader‘, 69 exclude: /node_modules/ 70 }, 71 { 72 test: /\.(png|jpg|gif|svg)$/, 73 // 圖片加載器,較小的圖片轉成 base64 74 loader: ‘url-loader‘, 75 query: { 76 limit: 10000, 77 name: ‘./imgs/[name].[ext]?[hash:7]‘ 78 } 79 } 80 ] 81 }, 82 plugins: [ 83 // 提取公共模塊 84 new webpack.optimize.CommonsChunkPlugin({ 85 name: ‘vendors‘, // 公共模塊的名稱 86 chunks: chunks, // chunks 是需要提取的模塊 87 minChunks: chunks.length 88 }), 89 // 配置提取出的樣式文件 90 new ExtractTextPlugin(‘css/[name].css‘) 91 ] 92 }; 93 94 var prod = process.env.NODE_ENV === ‘production‘; 95 module.exports.plugins = (module.exports.plugins || []); 96 if (prod) { 97 module.exports.devtool = ‘source-map‘; 98 module.exports.plugins = module.exports.plugins.concat([ 99 // 借鑒 vue 官方的生成環境配置 100 new webpack.DefinePlugin({ 101 ‘process.env‘: { 102 NODE_ENV: ‘"production"‘ 103 } 104 }), 105 new webpack.optimize.UglifyJsPlugin({ 106 compress: { 107 warnings: false 108 } 109 }) 110 ]); 111 } else { 112 module.exports.devtool = ‘eval-source-map‘; 113 module.exports.output.publicPath = ‘/view/‘; 114 } 115 116 var pages = getEntry(‘./source/**/*.html‘); 117 for (var pathname in pages) { 118 // 配置生成的 html 文件,定義路徑等 119 var conf = { 120 filename: prod ? ‘../views/‘ + pathname + ‘.html‘ : pathname + ‘.html‘, // html 文件輸出路徑 121 template: pages[pathname], // 模板路徑 122 inject: true, // js 插入位置 123 minify: { 124 removeComments: true, 125 collapseWhitespace: false 126 }, 127 hash:true 128 }; 129 if (pathname in module.exports.entry) { 130 conf.chunks = [‘vendors‘, pathname]; 131 //conf.hash = false; 132 } 133 // 需要生成幾個 html 文件,就配置幾個 HtmlWebpackPlugin 對象 134 module.exports.plugins.push(new HtmlWebpackPlugin(conf)); 135 } 136 137 // 根據項目具體需求,輸出正確的 js 和 html 路徑 138 function getEntry(globPath) { 139 var entries = {}, 140 basename, tmp, pathname; 141 142 glob.sync(globPath).forEach(function (entry) { 143 basename = path.basename(entry, path.extname(entry)); 144 tmp = entry.split(‘/‘).splice(-3); 145 pathname = tmp.splice(0, 1) + ‘/‘ + basename; // 正確輸出 js 和 html 的路徑 146 entries[pathname] = entry; 147 }); 148 console.log(entries); 149 return entries; 150 }View Code
開發工具使用的VS2017,本來使用WS,但是用習慣VS的我還是受不了,畢竟17還是太強大了嘛。既然是vue項目,那數據請求肯定就是vue-res, 路由就是vue-loader,編譯es6大家都是babel。 下面是項目結構預覽:
他們分別是圖片資源,引用庫資源,發布打包後的js和css,src源碼和打包後的html
二.幾點重要的收獲
1.組件化開發爽啊, 調用者只需要關註輸入和輸出,代碼明朗,容易維護
2.vue-res promise異步風格太優美,太喜歡了
3.記得以前做一個手機端項目,完全沒有自動化,各個頁面間跳轉慢的一比,一點也不流暢,項目結構不容易管理,重復代碼特別多。
近百個頁面js版本得不到控制,管理js,css引用困難。微信靜態資源緩存如此嚴重,沒有版本控制,每個頁面js版本的修改要人命。
4.解決緩存問題,應禁止html緩存,由於使用extract-text-webpack-plugin,可以保證你html入口中只有簡單的幾行代碼,等著自動化幫你引入所需js,所以即使禁止html緩存,也不會影響響應速度,畢竟我們的html文件 也就1-2k左右.html禁止緩存的原因是防止,js更新後,js hash版本已改變,但瀏覽器緩存的html中,依然是請求舊版本js文件,這樣一來js版本控制變得沒有意義。
5. js調用手機拍照, 調用安卓手機拍照不容易呀,所以如果只想在微信上使用網頁的話,建議使用 微信js SDK。
6. 蘋果手機和個別安卓手機,使用原生input調用拍照後,圖片到頁面中會出現旋轉問題,所以在微信上 使用js sdk, 在其他瀏覽器上,就用EXIF.js 手動將其旋轉90度 或者180度進行矯正。
7.推薦一款mobile用的不錯的彈窗組件 vue-ios-alert. ios風格的彈窗。地址以及github: http://isay.me/vue-ios-alertview/example/
8.手機上的 日期 時分秒選擇器,推薦一個有坑的貨 https://github.com/k186 有坑哦,使用的話,請看closed的第一個issue。另外日期選擇還是比較推薦原生。加上時分秒的話原生的可能就不好用。
9.頁面touch切換tag 使用的一個vue-tab github找一找
10. 上拉加載更多 用的vue-loadmore,側方滑動菜單 使用了jquery的slideout
11. 如果路由比較多的話,建議路由單獨分一個js配置,並且一定要按需加載,否則打包文件太大。如果是用戶點擊率極高的路由,可以直接require進去。
12.一些js庫,就不要通過require了,直接在html引入進去算了,畢竟這些庫基本不會更改,也沒必要控制版本
13.前端AOP, vue-res的攔截器點個贊,我可以在攔截器中,為我每一個請求 都加上authentication header等信息,像用jq的時候,我不得不手動把ajax包裝一層
14.像有些數據的加載,文字方面,最好預先給出加載中這種提示,不能給空白。列表的加載 要多考慮加載中,加載完成和暫無數據的處理。見過很多app和網頁都是進入到列表頁,首先一個暫無數據的背景圖給出來 了,結果稍等一下,數據又加載出來了....
15.雖然已經組件化了,但我還建議有一個每個頁面公用需要require的js,我一般都叫application.js 在這裏 可以放一些你的常量,枚舉,公共方法,helpers,utils,ajax 等配置,並且在這裏可以import footer header vue-res vue-alert 等一些每個組件或者頁面都需要以來的組件
三.一些缺點
1.腦子抽風啊,分為四個SPA, 整套項目下來,感覺還是應該做一個SPA。畢竟SPA之間切換,一個SPA切換到另一個SPA 還是加載東西太多,不夠流暢。雖然微信瀏覽器緩存“嚴重”
2.項目結構劃分還是不夠合理,但感覺也還能對付用。
3.組件化沒有發揮到極致,自己vue組件間通信沒搞好,md找子組件,我竟然還有通過遍歷的方式。
4.有些組件用的jquery的,搭配的不是很流暢,導致個別操作強行使用dom操作。
5.我有四個環境,開發,測試,demo, 線上。 每次發布到一個環境 都需要改了配置後,重新打包,很痛苦啊,關於這一點有什麽好的辦法嗎?
四.寫在最後
這個項目產品將繼續開發,不過下一階段還有個項目,我將使用一個SPA完成,關於vue有什麽好的各種mobile組件,希望dalao不吝推薦。
如果,您認為閱讀這篇博客讓您有些收獲,不妨點擊一下右下加【推薦】按鈕。
如果,您希望更容易地發現我的新博客,不妨點擊下方紅色【關註】的。
因為,我的分享熱情也離不開您的肯定支持。
感謝您的閱讀,我將持續輸出分享,我是蝸牛, 保持學習,謹記謙虛。不端不裝,有趣有夢。
Webpack+Vue+ES6 前端組件化開發mobile-multi-page應用實戰總結