Webpack+Vue+ES6 前端組件化開發mobile-multi-page應用實戰總結和踩坑

本文版權歸博客園和做者吳雙本人共同全部 轉載和爬蟲請註明原文地址 www.cnblogs.com/tdwscss

一.寫在前面

項目上線有一段時間了,一個基於webpack+vue+ES6的手機端多頁面應用。其實說是多頁面應用,實際上在webpack中屬於四個app,  若是真是作純單頁面,那應該有二三十個頁面吧。因此我這裏的多頁面應用,是分爲四個SPA。好比微信最下面,有四個導航,微信,通信錄,發現,我。 那麼這四個導航,就是個人四個SPA,配置多個入口便可。html

在這裏就不說太多代碼了,項目結構將會放到github上,地址在後面給出,以供參考,上傳的大概只是一個目錄加上配置狀況,其實關鍵點也就在webpack.config.js了,這裏主要配置了entry,loader,plugins,output目錄啥的。前端

在這裏先附上package.json和webpack.config.js吧:vue

 

 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",
 9     "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 }
View Code

 

  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。 下面是項目結構預覽:node

他們分別是圖片資源,引用庫資源,發佈打包後的js和css,src源碼和打包後的htmljquery

二.幾點重要的收穫

1.組件化開發爽啊, 調用者只須要關注輸入和輸出,代碼明朗,容易維護webpack

2.vue-res promise異步風格太優美,太喜歡了。可是有坑,ios8.x,使用內核瀏覽器運行js, 不支持promise語法,因此須要在入口中,import幾個npm下載的node module:ios

 npm i whatwg-fetch core-js es6-promise --save-dev
 

3.記得之前作一個手機端項目,徹底沒有自動化,各個頁面間跳轉慢的一比,一點也不流暢,項目結構不容易管理,重複代碼特別多。git

 近百個頁面js版本得不到控制,管理js,css引用困難。微信靜態資源緩存如此嚴重,沒有版本控制,每一個頁面js版本的修改要人命。es6

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找一找,ios8不支持 flex-shrink,要使用-webkit-flex-shrink。

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 等一些每一個組件或者頁面都須要以來的組件

16.熱替換是必須的,比之前用gulp livereload方便

17.手機端頁面調試,推薦vConsole(去github找)。 示例:

18.通過babel編譯es5的都沒問題.。  我有個單獨的小功能,沒用es6,直接谷歌調試開發,結果到了ios9.x上  不支持也不報錯,之後避免踩進去吧。下面是代碼:

19. IOS上計算時間 須要new Date('2017/09/09')的格式,  而不能使用橫槓的格式

三.一些缺點

 1.腦子抽風啊,分爲四個SPA, 整套項目下來,感受仍是應該作一個SPA。畢竟SPA之間切換,一個SPA切換到另外一個SPA 仍是加載東西太多,不夠流暢。雖然微信瀏覽器緩存「嚴重」

2.項目結構劃分仍是不夠合理,但感受也還能對付用。

3.組件化沒有發揮到極致,本身vue組件間通訊沒搞好,md找子組件,我居然還有經過遍歷的方式。

4.有些組件用的jquery的,搭配的不是很流暢,致使個別操做強行使用dom操做。

5.我有四個環境,開發,測試,demo, 線上。 每次發佈到一個環境  都須要改了配置後,從新打包,很痛苦啊,關於這一點有什麼好的辦法嗎? 

四.寫在最後

 這個項目產品將繼續開發,不過下一階段還有個項目,我將使用一個SPA完成,關於vue有什麼好的各類mobile組件,但願dalao不吝推薦。

 

 

相關文章
相關標籤/搜索