1.目標
最近專案上的事情不多,根據我自己的開發習慣,決定開發一些簡單的開發架子,方便以後事情多的時候直接套用。本文講的一個gulp+webpack+vue的單頁應用架子,想要達到的目的:
- 可以通過命令打包開發chunk,並支援熱替換
- 可以通過命令打包可釋出的chunk
- 支援路由
- 路由中的程式碼實現按需載入
- 用CommonJs的風格組織程式碼
- 程式碼結構儘量清晰易懂
盡我所能先做出一個滿足以上特點的架子吧,最近看完ES6,準備再去看看flux和reduce,看過之後再來思考下前端資料如何管理比較科學規範。架子中有做的不規範可改進的地方,煩請大家指出,我好更新。
首先來看一下整個架子的結構:
2.實現
2.1合併庫檔案
庫檔案不會隨業務程式碼發生變化,所以所有庫檔案打包成一個檔案就好了,這部分程式碼需要直接在頁面中以<script></script>
標籤引入,不能和業務程式碼打包到一起。如果和業務程式碼打包到一起,一旦業務程式碼發生變化,整個打包的檔案在瀏覽器中都需要被重新載入,這種做法不利於客戶端做快取,也會使webpack打包業務程式碼的過程變得非常慢,所以這裡使用gulp合併一下庫檔案:
/**
* 合併lib檔案
*/
gulp.task('concat-lib',function(){
gulp.src(['vue/dist/vue.min.js','vue-router/dist/vue-router.min.js'],{
cwd:'../lib'
}).pipe(concat('vue.min.js')).pipe(gulp.dest('../release'));
})
2.2組織業務程式碼
從上圖可以看到,所有的業務開發程式碼都放在src目錄下,展開來看:
- components目錄用來存放公用的vue元件,這塊架子中沒有,所以是空著的
- css用來存放所有的樣式檔案,modules和components目錄下分別存放各模組和各個元件所使用的樣式,app.css是主入口頁面樣式,main.css是所有樣式。把樣式集中起來是為了方便樣式的打包。
- modules用來存放各個模組的程式碼,模組的模板和js程式碼放在同一目錄下。
- app.js是程式主入口js,在此檔案中定義單頁應用的路由,指向各個模組
- index.html是應用主頁面
核心程式碼是這塊:
var Vue = require('vue')
var VueRouter = require('vue-router');
Vue.use(VueRouter);
var compo1=require('./modules/module1');
require('./css/main.css');
// 路由器需要一個根元件。
// 出於演示的目的,這裡使用一個空的元件,直接使用 HTML 作為應用的模板
var App = Vue.extend({})
// 建立一個路由器例項
// 建立例項時可以傳入配置引數進行定製,為保持簡單,這裡使用預設配置
var router = new VueRouter()
// 定義路由規則
// 每條路由規則應該對映到一個元件。這裡的“元件”可以是一個使用 Vue.extend
// 建立的元件建構函式,也可以是一個元件選項物件。
// 稍後我們會講解巢狀路由
router.map({
'/': {
component: compo1
},
'/path1': {
component: compo1
},
'/path2': {
component: function (resolve) {
//amd規範 實現效果:
//路由1中的模組和主頁面模組打包在一起
//路由2中的模組按需載入
require(['./modules/module2'],resolve);
//commonJs規範實現方式:
//require.ensure([],function(require){
// var comm2=require('./components/compo2');
// resolve(comm2)
//});
}
}
});
//預設路徑
//router.go('/path1');
// 現在我們可以啟動應用了!
// 路由器會建立一個 App 例項,並且掛載到選擇符 #app 匹配的元素上。
router.start(App, '#app')
預設模組是moduel1,/path1路由指向module1,/path2路由指向module2,module2的模組並不是和module1一樣在主頁面中一開始就載入好的,而是在路由到此路徑後才去載入,app.js中提供了vue元件文件中提供的兩種方式:CommonJs和AMD兩種規範的方式來載入,兩種方式是等價的。
require('./css/main.css');
把所有css載入到應用中,以便在開發模式下可以看到樣式,在打包釋出程式碼的時候會忽略此require,將樣式打包成獨立的檔案。
2.3打包開發程式碼
打包開發程式碼的webpack配置是build目錄下的webpack.config.dev.js
var webpack = require('webpack');
var path=require('path');
module.exports={
//這裡寫成陣列是為了dev server插入服務配置
entry: {
"app":['../src/app.js'],
},
output:{
path:path.resolve(__dirname, "../release"),//__dirname+'/../release',
publicPath: "/release/",//dev server 會從此路徑去拿hot-update.json
filename:'[name].bundle.js'
},
externals: {
'vue': 'Vue',
'vue-router':'VueRouter'
},
module: {
loaders: [
{ test: /\.css$/, loader: 'style-loader!css-loader' },
{test:/\.html$/,loader:'html-loader'}
]
},
plugins: [
],
devtool: "source-map"
}
程式主入口是app.js,所有entry只需要配置一個app.js。
output配置中的publicPath是用來配置專案中靜態檔案路徑的,這裡開發過程中會使用webpack-dev-server,給配置到release目錄下就行了。
externals下面配置的是通過標籤引入,可以在全域性環境下訪問到的變數,可以通過require這裡配置的key來獲取那些變數。
devtool: "source-map"
可以為壓縮之後的程式碼生成source-map檔案,這裡開發打包的程式碼並沒有被壓縮,所以這個其實沒意義。
{test:/\.html$/,loader:'html-loader'}
是用來在元件中載入html模板的:
var template=require('./module1.html');
// 定義元件
var comm = Vue.extend({
template: template,
data:function () {
return {
items:[{a:1,b:2,c:3},{a:4,b:5,c:5},{a:7,b:8,c:9}]
}
}
});
用上面的配置來打包,就會得到開發版本的打包程式碼了。
2.4使用webpack-dev-server和熱替換外掛HotModuleReplacementPlugin
為了方便開發除錯,需要啟動一個server來訪問專案,並支援熱替換,自動重新整理瀏覽器,以方便修改程式碼之後能夠實時看到效果。
在gulpfile.js做如下配置:
ar webpackConfigDev=require("./webpack.config.dev.js");
var WebpackDevServer = require("webpack-dev-server");
/**
* 使用測試配置打包,啟動hot dev server
*/
gulp.task('webpack-dev',['concat-lib'],function(){
var config = Object.create(webpackConfigDev);
//這兩項配置原本是在webpack.config.dev.js裡邊配置,可是通過gulp啟動devserver,那種配置無效,只能在此處寫入
//官網的解釋是webpack-dev-server沒有許可權讀取webpack的配置
config.entry.app.unshift("webpack-dev-server/client?http://localhost:8080/", "webpack/hot/dev-server");
config.plugins.push(new webpack.HotModuleReplacementPlugin());
var compiler = webpack(config);
var server = new WebpackDevServer(compiler, {
contentBase: "../",
publicPath: "/release/",
hot: true,
compress: false,
stats: { colors: true }
});
server.listen(8080, "localhost", function() {});
// server.close();
});
這樣會啟動一個本地的8080埠監聽,用來訪問某個目錄下的靜態檔案
contentBase: "../"
配置,指定了靜態檔案目錄在專案根目錄下,所以訪問http://localhost:8080 會看到根目錄下的檔案列表,點進去src目錄,就會預設訪問index.html,看到單頁應用的效果了publicPath: "/release/"
這個配置很重要,它指定了webpack-dev-server提供的打包靜態檔案路徑,值得注意的是,使用WebpackDevServer的時候,並不會在release目錄生成webpack打包檔案,只會在記憶體中生成打包檔案,通過localhost:8080/release/ 路徑,可以訪問到開發打包後的程式碼。
通過gulp啟動此server之後,訪問http://localhost:8080/src 路徑,可以看到用下面的效果:
可以看到,訪問主頁面的時候,載入了app.bundle.js打包檔案,訪問路由/path2的時候,才會去載入1.1.bundle.js檔案,子元件是延遲2s後才載入的。
更新程式碼之後,會實時打包並重新整理瀏覽器,看到實時效果。
2.5打包生產環境程式碼
和開發程式碼不同,生產環境程式碼具有以下特點:
- 程式碼需要壓縮
- 打包生成的檔名中需要包含檔案hash值,以方便控制客戶端快取
- css不能像開發環境那樣打包到js程式碼中,需要打包成獨立的檔案,在頁面開頭直接引入
基於以上特點,webpack配置檔案如下:
var webpack = require('webpack');
var ExtractTextPlugin = require("extract-text-webpack-plugin");
var path=require('path');
var cssExtract=new ExtractTextPlugin("[name].[contenthash:8].css");
module.exports={
entry: {
index:'../src/app.js'
},
output:{
path:path.resolve(__dirname, "../release"),
publicPath:"",//TODO 填寫生產環境靜態檔案路徑
filename:'[name].[chunkhash:8].bundle.js'
},
externals: {
'vue': 'Vue',
'vue-router':'VueRouter'
},
module: {
loaders: [
{ test: /\.css$/, loader: cssExtract.extract("style-loader", "css-loader") },
{test:/\.html$/,loader:'html-loader'}
]
},
plugins: [
new webpack.optimize.UglifyJsPlugin({
compress: {
drop_console: true,
warnings: false
}
}),
cssExtract
]
}
其中publicPath是填寫靜態檔案路徑的,如果圖片或其他靜態資源需要存放在CDN伺服器上,可以把CDN地址配置到這裡。
生成打包檔案之後,可以通過gulp替換掉主入口檔案 index.html裡面的靜態檔案路徑,這裡通過webpack模板也可以完成此工作,但配置較為繁瑣,個人感覺還是通過gulp來替換比較方便一點:
gulp.src('../src/index.html')
//.pipe(greplace(/xxxxx/g,"xxxxx"))
.pipe(gulp.dest('../release'));
打包後的程式碼:
後面的步驟就是上傳靜態檔案到CDN或其他上線流程了,這裡可以通過根據自己業務編寫的gulp外掛來完成,大家業務不同,處理方式不盡相同,我就不繼續往下寫了。
3.把命令都整合到npm中
我個人不太喜歡專案根目錄下一堆跟打包相關的檔案,所以在這個專案中,我把所有跟打包相關的檔案都放到了build目錄下,然後在package.json中:
"scripts": {
"dev": "gulp default --gulpfile build/gulpfile.js",
"build": "gulp build --gulpfile build/gulpfile.js",
"release": "gulp release --gulpfile build/gulpfile.js"
},
這樣就可以使用npm命令來執行上面的操作了:
npm run dev
啟動webpack-dev-server,使用開發webpack配置來打包程式碼,支援熱替換
npm run build
打包開發程式碼
npm run release
打包生產環境程式碼
4.後續
對於一個可用單頁應用而言,這個架子可能還缺著很多東西,對前端資料流程的管理、網路請求的管理、公共元件的組織等,在以後的專案中都會加上這些東西,用到了再往裡邊更新吧!
程式碼地址:https://github.com/zouchengzhuo/scaffold/tree/master/gulp-webpack-vue
本文轉自我的個人站點:http://zoucz.com/blog/2016/07/19/gulp-webpack-vue/