我們用webpack做單頁面應用開發應該嘗試過很多次了,如果在同一個項目需要支持PC端和移動端,做成多頁面應用,開發時能根據請求的終端返回不同的內容,應該怎么做呢?以下描述的是我嘗試的一種方案,并且以vue-cli 2.x
提供的模板為例,訪問 Github 可查看本項目源碼。
目錄架構
因為是PC端和移動端兩個模塊,所以我們可以在src
下拆分為pc
和mobile
兩個目錄,分別放兩端的代碼,再有一個common
目錄放置常量、數據層、api層等公共資源和可復用代碼:
├── build
│ ├── webpack.config.base.js
│ ├── webpack.config.dev.js
│ └── webpack.config.prod.js
├── src
│ ├──common
│ │ ├── assets
│ │ ├── constants
│ │ ├── store
│ │ │ └── index.js
│ │ |── api
│ │ │ └── index.js
│ ├── pc
│ │ |── pages
│ │ │ |── Home.vue
│ │ │ └── About.vue
│ │ |── App.vue
│ │ |── index.html
│ │ └── main.js
│ ├── mobile
│ │ │ |── Home.vue
│ │ │ └── About.vue
│ │ |── App.vue
│ │ |── index.html
│ │ └── main.js
復制代碼
webpack配置
因為有PC端和移動端,所有開發環境下應該有兩個entry
,分別為src/pc/main.js
和src/mobile/main.js
,參考webpack
文檔的多入口配置,所以我們在webpack.config.base.js
可做如下修改:
entry: {app: './src/pc/main.js',mobile: './src/mobile/main.js',},
復制代碼
完成以上修改后,我們分別對開發環境和打包環境作配置。
開發環境配置
在這里我們要做的是,可以讓webpack
既可以同時根據PC端和mobile端的模版生成對應的html
并注入打包后的js
文件,這個時候我們要借助HtmlWebpackPlugin
這個插件幫我們實現。所以,在webpack.config.dev.js
的plugins
里面,我們作以下配置:
plugins: [// ....// PC端new HtmlWebpackPlugin({filename: 'index.html', // 最后生成的文件名template: 'src/pc/index.html', // 模版htmlchunks: ['manifest', 'vendor', 'app'], // 注入打包后的js文件inject: true,}),// 移動端new HtmlWebpackPlugin({filename: 'index.mobile.html',template:'src/mobile/index.html',chunks: ['manifest', 'vendor', 'mobile'],inject: true,}),// ....],
復制代碼
上面的配置要特別說明下的是chunks
字段。webpack經過打包后一般會生成vendor.js
,manifest.js
,app.js
。vendor.js
一般是公共代碼,manifest.js
是與webpack
加載代碼有關的包。app.js
一般是你寫的業務代碼,要注意的是,你配置了多少個入口文件,就會生成多少個這樣的包,比如我們現在有兩個入口文件,分別是app
和mobile
,那么就會生成app.js
和mobile.js
。
上面的配置了兩個HtmlWebpackPlugin
,分別代表PC端和移動端的模板,他們chunks
字段也表明了在他們生成的html里分別注入app.js
和mobile.js
。
接下來我們想在開發時,想根據訪問的客戶端,決定加載的是PC端模版還是mobile端模板。比如在chrome
瀏覽器直接打開時,我們就加載PC端模版index.html
,如果我們打開了 chrome devtools
,切換到移動端調試工具,那么刷新之后我們加載移動端的模版index.mobile.html
,這個時候我們就可以借助webpack-dev-server
工具了。
我們在webpack環境下開發,都會用到這個工具,無論是vue
的腳手架vue-cli
還是react
的腳手架create-react-app
都自帶了這個工具。腳手架就是利用這個工具來啟動本地服務的,其實webpack-dev-server
內部使用了一個中間件叫做webpack-dev-middleware
來啟動web服務。
只要我們在webpack
中配置devServer
這個屬性,就能使用了webpack-dev-server
了。我們作如下配置(如果是vue-cli
創建的項目,則在config/index.js
里作相應配置):
devServer: {proxy: {'/': {target: 'http://localhost:8080', // 你項目的本地服務地址bypass: function(req, res, proxyOptions) {const userAgent = req.headers['user-agent'];if (req.headers.accept.indexOf('html') !== -1) {// 根據訪問終端返回模板if (/mobile/i.test(userAgent) && !/iPad/i.test(userAgent)) {return '/index.mobile.html';}return '/index.html';}},},},}
復制代碼
這里我們代理了/
的每個請求,如果用戶的請求資源類型不是html
,那么就然后根據用戶的user-agent
返回不同的模板。
這里要說一下的是bypass
函數,官方文檔介紹如下:
webpack.js.org/configurati…
Sometimes you don't want to proxy everything. It is possible to
bypass
the proxy based on the return value of a function.
In the function you get access to the request, response and proxy options. It must return either false or a path that will be served instead of continuing to proxy the request.
E.g. for a browser request, you want to serve a HTML page, but for an API request you want to proxy it.
這段文字的大意是,有時候對于瀏覽器的某些請求,你希望提供HTML頁面,你可設置bypass
函數,在函數里你可以拿到req
,res
和proxy
的引用, 最后必須返回false
或資源提供的路徑,使這個請求不再繼續代理請求。
經過上面配置之后,我們的開發就相對方便些了,只要我們在chrome devtools
切換環境并刷新,webpack就會自動返回對應的模板。
注意:如果直接訪問http://localhost:8080
是無法按照客戶端的user-agent
返回任務東西的(不經過bypass
函數),必須在后面加多一個路徑才行,比如http://localhost:8080/path
,這個問題有待解決。
生產環境配置
生產環境要配置的不多,只要配置HtmlWebpackPlugin
就可以了
plugins: [// ....// PC端模版new HtmlWebpackPlugin({filename: path.resolve(__dirname, '../dist/index.html'),template: 'src/pc/index.html',inject: true,minify: {removeComments: true,collapseWhitespace: true,removeAttributeQuotes: true,},chunksSortMode: 'dependency',chunks: ['manifest', 'vendor', 'app'],}),// 移動端模版new HtmlWebpackPlugin({filename: path.resolve(__dirname, '../dist/mobile_index.html'),template: 'src/mobile/index.html',inject: true,minify: {removeComments: true,collapseWhitespace: true,removeAttributeQuotes: true,},chunksSortMode: 'dependency',chunks: ['manifest', 'vendor', 'mobile'],}),// ....],
復制代碼
經過配置就會生成文件了,但是有一個問題是,vendor.js
會包含PC端和移動端的代碼,可能有一些代碼其中由一方是用不上的,比如UI
框架,我的解決辦法是在模版手動注入vue/react
包和對應的UI
框架,這種方法還有一個好處是減少vendor
包的大小。。。
最后再貼一下項目 github 地址,歡迎star~
本文鏈接:www.zzfweb.cn/post/webpac…