做一個gulp+webpack+vue的單頁應用開發架子


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/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM