webpack構建vue項目(配置篇)

最近公司要求用vue重構項目,還涉及到模塊化開發,因而乎,我專門花了幾天的時間研究了一下webpack這個目前來看比較熱門的模塊加載兼打包工具,發現上手並非很容易,現將總結的一些有關配置的心得分享出來,歡迎大神來拍磚。。。css

1、新建一個項目目錄,cd /d 定位進去,而後輸入npm init,會提示你填寫一些項目的信息,一直回車默認就行了,或者直接執行npm init -y 直接跳過,這樣就在項目目錄下生成了一個package.json文件。html

2、接下來就是經過npm安裝項目依賴項,命令行輸入:npm install babel-loader babel-core babel-plugin-transform-runtime babel-preset-es2015 babel-preset-stage-0 babel-runtime vue-loader vue-html-loader vue-hot-reload-api css-loader style-loader webpack webpack-dev-server --save-dev ,繼續輸入npm install vue@^1.0.26 --save 。vue

這裏注意的幾個點以下:node

1.須要安裝的依賴項視具體的項目需求來定,我只是安了幾個必需的,後期會再加;jquery

2.輸入以後若是一直報錯或者光標一直在轉動,要麼是npm版本過低(須要3+),要麼將npm改爲cnpm,若是沒有安裝淘寶NPM鏡像,能夠先輸入npm install -g cnpm --registry=https://registry.npm.taobao.org,接着輸入cnpm -v查看是否安裝完成,而後就可使用cnpm來代替npm;webpack

3.能夠先修改package.json文件中的devDependencies和dependencies,而後再輸入npm install進行一次性安裝(偷懶的作法,嘿嘿);web

4.dependencies中的vue默認安裝2+,若是dependencies中的vue選擇^1.0.26,那麼devDependencies中對應的vue-loader最好選擇^7.3.0,vue-hot-reload-api最好選擇^1.2.0,不然就會報錯;正則表達式

5.dependencies中的vue-router默認安裝2+,沒法識別router.map()這個方法,若是想要用回這個方法,最好選擇^0.7.13;vue-router

6.有時安裝一個依賴項,會提示還須要一併安裝別的依賴項,例如:若是要安裝bootstrap-loader,會提示要求安裝node-sass sass-loader resolve-url-loader;要安裝less-loader,會提示要求安裝less;express

完成這一步以後,會在項目目錄下生成一個名node_modules的文件,對應的package.json文件中的內容變更以下(我額外添加了幾個依賴項):

  "devDependencies": {
    "autoprefixer-loader": "^3.2.0",
    "babel-core": "^6.18.2",
    "babel-loader": "^6.2.7",
    "babel-plugin-transform-runtime": "^6.15.0",
    "babel-preset-es2015": "^6.18.0",
    "babel-preset-stage-0": "^6.16.0",
    "babel-runtime": "^6.18.0",
    "css-loader": "^0.25.0",
    "debug": "^2.2.0",
    "express": "^4.14.0",
    "extract-text-webpack-plugin": "^1.0.1",
    "file-loader": "^0.9.0",
    "html-webpack-plugin": "^2.24.1",
    "jquery": "^3.1.1",
    "less": "^2.7.1",
    "less-loader": "^2.2.3",
    "style-loader": "^0.13.1",
    "url-loader": "^0.5.7",
    "vue-hot-reload-api": "^1.2.0",
    "vue-html-loader": "^1.2.3",
    "vue-loader": "^7.3.0",
    "webpack": "^1.13.3",
    "webpack-dev-middleware": "^1.8.4",
    "webpack-dev-server": "^1.16.2",
    "webpack-hot-middleware": "^2.13.1"
  },
  "dependencies": {
    "vue": "^1.0.26",
    "vue-router": "^0.7.13"
  }

3、在項目目錄下新建一個名爲src的目錄,裏面用於存放入口文件(index.js)、項目源文件(html,css,js,img之類的)、組件(.vue後綴),個人src目錄結構大體以下:

src
  -entry
      -index.js
  -pages
      -components
      -css
      -img
      -js
      -index.html
  -public

固然,有輸入目錄,就有輸出目錄,即在項目目錄下新建一個output目錄,用於放置生產出來的各類資源文件。

4、在項目目錄下新建一個名爲build目錄,裏面用於存放各類配置文件,涉及到基礎配置、開發和生產環境、靜態服務器以及熱加載,詳細的內容請看下面的代碼:

1.webpack.config.js(基礎配置文件)

// 引入依賴模塊
var path = require('path');
var webpack = require('webpack');
var ExtractTextPlugin = require('extract-text-webpack-plugin');

module.exports = {
    // 入口文件,路徑相對於本文件所在的位置,能夠寫成字符串、數組、對象
    entry: {
        // path.resolve([from ...], to) 將to參數解析爲絕對路徑
        index:path.resolve(__dirname, '../src/entry/index.js'),
        // 須要被提取爲公共模塊的羣組
        vendors:['vue','vue-router','jquery'],
    },

    // 輸出配置
    output: {
        // 輸出文件,路徑相對於本文件所在的位置
        path: path.resolve(__dirname, '../output/static/js/'),

        // 設置publicPath這個屬性會出現不少問題:
        // 1.能夠當作輸出文件的另外一種路徑,差異路徑是相對於生成的html文件;
        // 2.也能夠當作網站運行時的訪問路徑;
        // 3.該屬性的好處在於當你配置了圖片CDN的地址,本地開發時引用本地的圖片資源,上線打包時就將資源所有指向CDN了,若是沒有肯定的發佈地址不建議配置該屬性,特別是在打包圖片時,路徑很容易出現混亂,若是沒有設置,則默認從站點根目錄加載
        // publicPath: '../static/js/',

        // 基於文件的md5生成Hash名稱的script來防止緩存
        filename: '[name].[hash].js',
        // 非主入口的文件名,即未被列在entry中,卻又須要被打包出來的文件命名配置
        chunkFilename: '[id].[chunkhash].js'
    },

    // 其餘解決方案
    resolve: {
        // require時省略的擴展名,遇到.vue結尾的也要去加載
        extensions: ['','.js', '.vue'],
        // 模塊別名地址,方便後續直接引用別名,無須寫長長的地址,注意若是後續不能識別該別名,須要先設置root
        alias:{}
    },    

    // 不進行打包的模塊
    externals:{},

    // 模塊加載器
    module: {
        // loader至關於gulp裏的task,用來處理在入口文件中require的和其餘方式引用進來的文件,test是正則表達式,匹配要處理的文件;loader匹配要使用的loader,"-loader"能夠省略;include把要處理的目錄包括進來,exclude排除不處理的目錄       
        loaders: [
            //  使用vue-loader 加載 .vue 結尾的文件
            {
                test: /\.vue$/, 
                loader: 'vue-loader',
                exclude: /node_modules/    
            },
            // 使用babel 加載 .js 結尾的文件
            {
                test: /\.js$/,
                loader: 'babel',
                exclude: /node_modules/,
                query:{
                    presets: ['es2015', 'stage-0'],  
                    plugins: ['transform-runtime']                      
                }
            }, 
            // 使用css-loader和style-loader 加載 .css 結尾的文件
            {  
                test: /\.css$/,                  
                // 將樣式抽取出來爲獨立的文件
                loader: ExtractTextPlugin.extract("style-loader", "css-loader!autoprefixer-loader"),
                exclude: /node_modules/
            },
            // 使用less-loader、css-loader和style-loade 加載 .less 結尾的文件
            {  
                test: /\.less$/,                  
                // 將樣式抽取出來爲獨立的文件
                loader: ExtractTextPlugin.extract("style-loader", "css-loader!autoprefixer-loader!less-loader"),
                exclude: /node_modules/
            },           
            // 加載圖片
            {
                test: /\.(png|jpg|gif)$/,
                loader: 'url-loader',
                query: {
                    // 把較小的圖片轉換成base64的字符串內嵌在生成的js文件裏
                    limit: 10000,
                    // 路徑要與當前配置文件下的publicPath相結合
                    name:'../img/[name].[ext]?[hash:7]'
                }
            },
            // 加載圖標
            {
                test: /\.(eot|woff|woff2|svg|ttf)([\?]?.*)$/,
                loader: 'file-loader',
                query: {               
                    // 把較小的圖標轉換成base64的字符串內嵌在生成的js文件裏    
                    limit: 10000,
                    name:'../fonts/[name].[ext]?[hash:7]',
                    prefix:'font'
                }
            },              
        ]         
    },

    // 配置插件項
    plugins: []  
}

2.webpack.dev.config.js(開發環境下的配置文件)

// 引入依賴模塊
var path = require('path');
var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var ExtractTextPlugin = require('extract-text-webpack-plugin');

// 引入基本配置
var config = require('./webpack.config.js');

// 必須修改原配置中網站運行時的訪問路徑,至關於絕對路徑,修改完以後,當前配置文件下的不少相對路徑都是相對於這個來設定;
// 注意:webpack-dev-server會實時的編譯,可是最後的編譯的文件並無輸出到目標文件夾,而是保存到了內存當中
config.output.publicPath = '/';

// 從新配置模塊加載器
config.module= {
    // test是正則表達式,匹配要處理的文件;loader匹配要使用的loader,"-loader"能夠省略;include把要處理的目錄包括進來,exclude排除不處理的目錄       
    loaders: [
        //  使用vue-loader 加載 .vue 結尾的文件
        {
            test: /\.vue$/, 
            loader: 'vue-loader',
            exclude: /node_modules/    
        },
        // 使用babel 加載 .js 結尾的文件
        {
            test: /\.js$/,
            loader: 'babel',
            exclude: /node_modules/,
            query:{
                presets: ['es2015', 'stage-0'],  
                plugins: ['transform-runtime']                      
            }
        }, 
        // 使用css-loader、autoprefixer-loader和style-loader 加載 .css 結尾的文件
        {  
            test: /\.css$/,                  
            // 將樣式抽取出來爲獨立的文件
            loader: ExtractTextPlugin.extract("style-loader", "css-loader!autoprefixer-loader"),
            exclude: /node_modules/
        },
        // 使用less-loader、autoprefixer-loader、css-loader和style-loade 加載 .less 結尾的文件
        {  
            test: /\.less$/,                  
            // 將樣式抽取出來爲獨立的文件
            loader: ExtractTextPlugin.extract("style-loader", "css-loader!autoprefixer-loader!less-loader"),
            exclude: /node_modules/
        },           
        // 加載圖片
        {
            test: /\.(png|jpg|gif)$/,
            loader: 'url-loader',
            query: {
                // 把較小的圖片轉換成base64的字符串內嵌在生成的js文件裏
                limit: 10000,
                // 路徑和生產環境下的不一樣,要與修改後的publickPath相結合
                name: 'img/[name].[ext]?[hash:7]'
            }
        },
        // 加載圖標
        {
            test: /\.(eot|woff|woff2|svg|ttf)([\?]?.*)$/,
            loader: 'file-loader',
            query: {                   
                limit: 10000,
                // 路徑和生產環境下的不一樣,要與修改後的publickPath相結合
                name:'fonts/[name].[ext]?[hash:7]',
                prefix:'font'
            }
        },              
    ]         
};

// 從新配置插件項
config.plugins = [
    // 位於開發環境下
    new webpack.DefinePlugin({
        'process.env': {
            NODE_ENV: '"development"'
        }
    }),

    // 自動生成html插件,若是建立多個HtmlWebpackPlugin的實例,就會生成多個頁面
    new HtmlWebpackPlugin({
        // 生成html文件的名字,路徑和生產環境下的不一樣,要與修改後的publickPath相結合,不然開啓服務器後頁面空白
        filename: 'src/pages/index.html',
        // 源文件,路徑相對於本文件所在的位置
        template: path.resolve(__dirname, '../src/pages/index.html'),
        // 須要引入entry裏面的哪幾個入口,若是entry裏有公共模塊,記住必定要引入
        chunks: ['vendors','index'],
        // 要把<script>標籤插入到頁面哪一個標籤裏(body|true|head|false)
        inject: 'body',
        // 生成html文件的標題
        title:''
        // hash若是爲true,將添加hash到全部包含的腳本和css文件,對於解除cache頗有用
        // minify用於壓縮html文件,其中的removeComments:true用於移除html中的註釋,collapseWhitespace:true用於刪除空白符與換行符
    }),    

    // 提取css單文件的名字,路徑和生產環境下的不一樣,要與修改後的publickPath相結合
    new ExtractTextPlugin("[name].[contenthash].css"),    

    // 提取入口文件裏面的公共模塊
    new webpack.optimize.CommonsChunkPlugin({
        name: 'vendors',
        filename: 'vendors.js',
    }),    

    // 爲組件分配ID,經過這個插件webpack能夠分析和優先考慮使用最多的模塊,併爲它們分配最小的ID
    new webpack.optimize.OccurenceOrderPlugin(),

    // 模塊熱替換插件
    new webpack.HotModuleReplacementPlugin(),

    // 容許錯誤不打斷程序
    new webpack.NoErrorsPlugin(),

    // 全局掛載插件
    new webpack.ProvidePlugin({
        $:"jquery",
        jQuery:"jquery",
        "window.jQuery":"jquery"
    })        
];

// vue裏的css也要單獨提取出來
config.vue = {
    loaders: {
        css: ExtractTextPlugin.extract("css")
    }
};

// 啓用source-map,開發環境下推薦使用cheap-module-eval-source-map
config.devtool='cheap-module-eval-source-map';

// 爲了實現熱加載,須要動態向入口配置中注入 webpack-hot-middleware/client ,路徑相對於本文件所在的位置
// var devClient = 'webpack-hot-middleware/client';
// 爲了修改html文件也能實現熱加載,須要修改上面的devClient變量,引入同級目錄下的dev-client.js文件
var devClient = './build/dev-client';
// Object.keys()返回對象的可枚舉屬性和方法的名稱
Object.keys(config.entry).forEach(function (name, i) {
    var extras = [devClient];
    config.entry[name] = extras.concat(config.entry[name]);
})

module.exports = config;

3.webpack.prod.config.js(生產環境下的配置文件)

// 引入依賴模塊
var path = require('path');
var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var ExtractTextPlugin = require('extract-text-webpack-plugin');

// 引入基本配置
var config = require('./webpack.config');

// 從新配置插件項
config.plugins = [
    // 位於生產環境下
    new webpack.DefinePlugin({
        'process.env': {
            NODE_ENV: '"production"'
        }
    }),

    // 自動生成html插件,若是建立多個HtmlWebpackPlugin的實例,就會生成多個頁面
    new HtmlWebpackPlugin({
        // 生成html文件的名字,路徑相對於輸出文件所在的位置
        filename: '../../html/index.html',
        // 源文件,路徑相對於本文件所在的位置
        template: path.resolve(__dirname, '../src/pages/index.html'),
        // 須要引入entry裏面的哪幾個入口,若是entry裏有公共模塊,記住必定要引入
        chunks: ['vendors','special','index'],
        // 要把<script>標籤插入到頁面哪一個標籤裏(body|true|head|false)
        inject: 'body',
        // 生成html文件的標題
        title:'',
        // hash若是爲true,將添加hash到全部包含的腳本和css文件,對於解除cache頗有用
        // minify用於壓縮html文件,其中的removeComments:true用於移除html中的註釋,collapseWhitespace:true用於刪除空白符與換行符
    }),      

    // 提取css單文件的名字,路徑相對於輸出文件所在的位置
    new ExtractTextPlugin("../css/[name].[contenthash].css"),    

    // 提取入口文件裏面的公共模塊
    new webpack.optimize.CommonsChunkPlugin({
        name: 'vendors',
        filename: 'vendors.js',
    }),   
    
    // 壓縮js代碼
    new webpack.optimize.UglifyJsPlugin({
        compress: {
            warnings: false
        },
        // 排除關鍵字,不能混淆
        except:['$','exports','require']
    }),

    // 爲組件分配ID,經過這個插件webpack能夠分析和優先考慮使用最多的模塊,併爲它們分配最小的ID
    new webpack.optimize.OccurenceOrderPlugin(),

    // 全局掛載插件,當模塊使用這些變量的時候,wepback會自動加載,區別於window掛載
    new webpack.ProvidePlugin({
        $:"jquery",
        jQuery:"jquery",
        "window.jQuery":"jquery"
    })   
];

// vue裏的css也要單獨提取出來
config.vue = {
    loaders: {
        css: ExtractTextPlugin.extract("css")
    }
};

// 開啓source-map,生產環境下推薦使用cheap-source-map或source-map,後者獲得的.map文件體積比較大,可是可以徹底還原之前的js代碼
config.devtool='source-map';
// 關閉source-map
// config.devtool=false;

module.exports = config;

4.dev-server.js(服務器配置文件)

// 引入依賴模塊
var express = require('express');
var webpack = require('webpack');
var config = require('./webpack.dev.config.js');

// 建立一個express實例
var app = express();

// 對網站首頁的訪問返回 "Hello World!" 字樣
app.get('/', function (req, res) {
    res.send('Hello World!');
});

// 調用webpack並把配置傳遞過去
var compiler = webpack(config);

// 使用 webpack-dev-middleware 中間件,搭建服務器
var devMiddleware = require('webpack-dev-middleware')(compiler, {
    publicPath: config.output.publicPath,
    stats: {
        colors: true,
        chunks: false
    }
})

// 使用 webpack-hot-middleware 中間件,實現熱加載
var hotMiddleware = require('webpack-hot-middleware')(compiler);

// 爲了修改html文件也能實現熱加載,使用webpack插件來監聽html源文件改變事件
compiler.plugin('compilation', function (compilation) {
    compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) {
        // 發佈事件
        hotMiddleware.publish({ action: 'reload' });
        cb();
    })
});

// 註冊中間件
app.use(devMiddleware);
app.use(hotMiddleware);

// 監聽 8888 端口,開啓服務器
app.listen(8888, function (err) {
    if (err) {
        console.log(err);
        return;
    }
    console.log('Listening at http://localhost:8888');
})

5.dev-client.js(配合dev-server.js監聽html文件改動也可以觸發自動刷新)

// 引入 webpack-hot-middleware/client 
var hotClient = require('webpack-hot-middleware/client');

// 訂閱事件,當 event.action === 'reload' 時執行頁面刷新
hotClient.subscribe(function (event) {
    if (event.action === 'reload') {
        window.location.reload();
    }
})

5、爲了避免必每次構建項目都要輸入webpack --display-modules --display-chunks --config build/webpack.config.js這條長命令,咱們在package.js文件中修改「scripts」項:

"scripts": {
  "build":"webpack --display-modules --display-chunks --config build/webpack.config.js",
  "dev":"node ./build/dev-server.js"
}

注意:package.js中不能有註釋。

這樣,咱們就能夠經過執行 npm run build 來進行構建,同時還增長了一條開啓開發服務器的命令 npm run dev。

6、網上不少人講解webpack配置是按「先……而後……」的邏輯往下走,以及每走一步會說明走這一步的緣由是什麼,配完以後的結果是什麼,出了問題該怎麼解決,這種撰文方式確實幫了不少入門webpack的小白們(譬如我)很大的忙。因此這裏我就省略了這些步驟,而是直接將最後一步的配置結果展示出來給你們看,而且附上了詳細的註釋(寫得嘔心瀝血啊~)供你們理解,之後不出意外應該會出webpack構建vue的進階篇,敬請期待~

相關文章
相關標籤/搜索