從搭建vue-腳手架到掌握webpack配置(二.插件與提取)

前言

上一期從零構建了一個基礎版的vue-cli項目,主要介紹了loader的安裝和一些配置項的用法,還給項目添加了less預處理器javascript

上一期的連接-從搭建vue-腳手架到掌握webpack配置(一.基礎配置)css

本期開始引入經常使用的插件實現開發環境和生成環境會用到的一些功能,好比熱插拔、css樣式提取、公共模塊提、取代碼壓縮等等html

注意 本教程適用webpack3,並不支持webpack4以上

區分開發與生產環境

不少插件功能是在開發環境(development)用到的可是在生產環境(production)用不到的,反之亦然。好比vue

-development用到的
  • 熱插拔調試
  • 生成html模板
-production用到的
  • 生成html模板
  • css樣式提取
  • 公共模塊提取
  • JavaScript壓縮
  • ......

引用官方的說法 ,區分生產和開發環境有兩種方法,以下圖 java

image

第二種方法涉及到二次封裝,就像官方vue-cli構建的項目同樣,分紅了三個配置文件,對目前的咱們來講比較複雜,咱們使用第一種方法,設置環境變量來區分部署環境。node

參考vue-cli生成的簡單版工程(webpack-simple),咱們發現npm script寫得有點奇怪jquery

"scripts": {
    "dev": "cross-env NODE_ENV=development webpack-dev-server --open --hot",
    "build": "cross-env NODE_ENV=production webpack --progress --hide-modules"
  }
複製代碼

在運行webpack命令以前運行了 cross-env NODE_ENV=develpomentproduction,這就是給環境變量賦值的過程,可是單單這樣寫是沒法執行的,咱們須要安裝一個插件——cross-envwebpack

npm install --save cross-env
複製代碼

這樣咱們就能夠在以後運行在node環境的js 文件中訪問到這些環境變量,經過process.env對象還能拿到package.json裏面的配置信息,這就涉及到node的知識了,很少說。web

const env = process.env.NODE_ENV
//獲取工程的版本號
const version = process.env.npm_package_version
複製代碼

簡單點寫,把環境變量的判斷直接放到webpack.config.js文件的最下面vue-cli

const path = require('path')
const webpack = require('webpack')

module.exports = {
    entry:{
        app:'./src/main.js'
    },
    //...
}
/**
 * 生成生產代碼的時候才觸發
 */
if (process.env.NODE_ENV === 'production') {
    // http://vue-loader.vuejs.org/en/workflow/production.html
    module.exports.plugins = (module.exports.plugins || []).concat([
        new webpack.DefinePlugin({
            'process.env': {
            NODE_ENV: '"production"'
            }
        }),
    ])
  }
複製代碼

若是之後額外的配置項愈來愈多的話,像上面這樣寫是不太好合並配置項的,到最後仍是要抽離出另外一個js文件裝載新增或重寫的配置項,用webpack-merge中間件合併配置對象。

webpack.DefinePlugin插件是設置全局常量的插件,要記住!賦值的時候記得寫成'"production"', 官方對DefinePlugin插件 是這麼說的

注意,由於這個插件直接執行文本替換,給定的值必須包含字符串自己內的實際引號。一般,有兩種方式來達到這個效果,使用 '"production"', 或者使用 JSON.stringify('production')。

生成html模板

以前根目錄下index.html要咱們本身引入js資源地址,有新的資源都要手動引入,很麻煩,這時候就會用到HtmlWebpackPlugin 插件,按照index.html做爲模板在dist目錄下生成帶上全部資源的html 文件。

npm install --save-dev html-webpack-plugin
複製代碼

先經過require引入插件,而後在輸出對象裏面添加plugins屬性,數據值類型是數組,數組成員new [插件]()添加插件就行。每一個插件都有本身的配置項和規範,能夠查 npmjs 或者 他們的官方文檔

const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const webpack = require('webpack')

module.exports = {
    entry:{
        app:'./src/main.js'
    },
    output:{
        path:path.resolve(__dirname,'./dist'),
        filename:"js/[name].js",
    },
    module:{
        rules:[
        //...
        ]
    },
    plugins:[
        new HtmlWebpackPlugin({
            filename:'index.html',
            title:'vue demo',
            template:'./index.html',
        })
    ],
    externals:{
        'jquery':'window.jQuery'
    }
}
複製代碼
說明
  • filename 生成的html的文件名,不填就默認是原文件名
  • title title標籤的內容
  • template html模板地址,這裏咱們用我上一期建在跟目錄的index.html

這裏有前輩對HtmlWebpackPlugin的詳細說明文章

index.html的內容要改一改了,由於webpack打包完以後自動添加資源地址到html文件裏,因此咱們要刪掉本來寫上去的script標籤

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>vue demo</title>
</head>
<body>
    <div id="app">
     
    </div>
    <script src="http://apps.bdimg.com/libs/jquery/2.1.4/jquery.js"></script>
</body>
</html>
複製代碼

有人可能會奇怪,這裏爲何加了一個cdn的jQuery,由於我要在這裏帶過一個知識點:有時候咱們會有用到cdn加速的庫資源,可是不知道怎麼在工程中使用。

很簡單咱們在html模板中直接引入,而後在webpack.config.js配置中加一項「外部引入」(externals)

// webpack.config.js
externals:{
    'jquery':'window.jQuery'
}
//app.vue中引入
import $ from 'jquery'
複製代碼

熱替換

web服務器

使用熱替換以前固然要先有一個web服務器環境啦,安裝webpack-dev-server

npm install --save-dev webpack-dev-server
複製代碼

webpack-dev-server實際上是一個獨立的插件,可是webpack內置了它的配置項,屬性devServer對應的就是它的配置項。

module.exports = {
    entry:{
        app:'./src/main.js'
    },
    output:{
        path:path.resolve(__dirname,'./dist'),
        filename:"js/[name].js",
    },
    devServer:{
        contentBase:"./dist"
    }
}
複製代碼

端口地址什麼的都默認 http://localhost:8080/ ,就設置了跟資源目錄地址contentBase。 想更深刻的去配置能夠看官方文檔 dev-server。我還真沒認真看過,嘻嘻。

熱替換插件

熱替換就是開發的過程當中修改文件內容以後不用頻繁刷新頁面,修改會自動同步到瀏覽器中,webpack內部已經有這份插件了,不用安裝直接都用就能夠。在plugins添加一項 new webpack.HotModuleReplacementPlugin()就ok了

plugins:[
        new HtmlWebpackPlugin({
            filename:'index.html',
            title:'vue demo',
            template:'./index.html',
        }),
        new webpack.HotModuleReplacementPlugin()
    ]
複製代碼

改一下npm scripts

"scripts": {
    "dev": "cross-env NODE_ENV=development webpack-dev-server --open --hot",
    "build": "cross-env NODE_ENV=production webpack --progress --hide-modules"
  }
複製代碼

運行 npm run dev,熱部署搞定

以上是開發環境要用到的插件,下面就是生成環境用到的插件了

css文件和vue內樣式提取

若是不提取css樣式,全部的.css文件和vue內的style都會以style標籤的形式被添加到頁面的head裏面,不利於資源的緩存並且下降了頁面的加載速度。

好的,就用extract-text-webpack-plugin插件吧,老規矩安裝一下

npm install extract-text-webpack-plugin --save-dev
複製代碼
簡單使用

在使用css相關loader以前先用本插件過濾一遍

var ExtractTextPlugin = require("extract-text-webpack-plugin")

module.exports = {
  // other options...
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader',
        options: {
          loaders: {
            css: ExtractTextPlugin.extract({
              use: 'css-loader',
              fallback: 'vue-style-loader' // <- 這是vue-loader的依賴
            }),
            //用了less或者sass的地方都要用上哦
            'less': ExtractTextPlugin.extract({
                use:[
                    'css-loader',
                    'less-loader'
                ],
                fallback:'vue-style-loader'
            })
          }
        }
      }
    ]
  },
  plugins: [
    new ExtractTextPlugin("styles/style.css")
  ]
}
複製代碼

vue內部的style須要先抽取出來,因此要在fallback屬性上添加預先的加載器 'vue-style-loader','vue-style-loader'是vue-loader自帶的哦,若是運行時報錯的話那就手動install一下他吧。

生成多文件

我通常的習慣是把外部引入的css文件認爲是能夠複用的,而vue內的style是每一個頁面都不同的要另外生成的,因此我建了兩ExtractTextPlugin實例分別抽取樣式到兩個文件裏。

const path = require('path')
const webpack = require('webpack')
const ExtractTextPlugin = require("extract-text-webpack-plugin")
const ExtractRootCss = new ExtractTextPlugin({filename:'styles/root.css',allChunks:false});
const ExtractVueCss = new ExtractTextPlugin({filename:'styles/[name]/style.css',allChunks:true});

module.exports = {
    //other options...
    module:{
        rules:[
        //...
            {
                test:/\.css$/,
                //這裏用的ExtractRootCss
                use:ExtractRootCss.extract({
                    fallback:'style-loader',
                    use:['css-loader']
                })
            },
            {
                test:/\.less$/,
                //這裏用的ExtractRootCss
                use:ExtractRootCss.extract({
                    fallback:'style-loader',
                    use:[
                        'css-loader',
                        'less-loader'
                    ]
                })
            },
            {
                test:/\.vue$/,
                loader:'vue-loader',
                options:{
                    loaders:{
                        //這裏用的ExtractVueCss
                        'css': ExtractVueCss.extract({
                            use: 'css-loader',
                            fallback: 'vue-style-loader' // <- 這是vue-loader的依賴,因此若是使用npm3,則不須要顯式安裝
                          }),
                        //這裏用的ExtractVueCss
                        'less':
                        ExtractVueCss.extract({
                            use:[
                                'css-loader',
                                'less-loader'
                            ],
                            fallback:'vue-style-loader'
                        })
                    },
                }
            },
        ]
    },
    plugins:[
        new HtmlWebpackPlugin({
            filename:'index.html',
            title:'vue demo',
            template:'./index.html',
        }),
        ExtractRootCss,//填入插件實例,複用的css
        ExtractVueCss,//記得按順序填入,vue內的css
        new webpack.HotModuleReplacementPlugin(),
    ]
}
複製代碼

這就是ExtractTextPlugin插件生成多個文件的方法。你也能夠按照本身的習慣去配置。

公共代碼提取

在多頁面或者多入口的時候(entry設了不僅一個),不一樣的模塊(chunks)會屢次引入同樣的資源模塊(module,也就是import引入的js文件),還有vue等庫的代碼,以上這些複用的代碼最好是能夠獨立出來,一方面方便緩存,一方面減小包的體積。

CommonsChunkPlugin插件就是解決這一問題的,它從屬於webpack.optimize對象因此也是不用安裝的。具體使用以下

new webpack.optimize.CommonsChunkPlugin({
    name: 'vender',
    minChunks:2
})
複製代碼

minChunks參數能夠是number類型,填2 就是說有2個chunk以上用到的公共塊就會被打包的vender.js裏面。minChunks也能夠傳一個方法,返回值是boolean類型.

(chunk能夠簡單理解爲entry屬性設置的入口而生成的整條關係樹,因此到目前爲止本項目也只有一個chunk,就是'app',固然插件生成的vender也是一個chunk。對初學者來講就這樣理解吧,用多了天然會有概念)

既然只有一個chunk 那就先抽取公用庫中的代碼吧,如vue包中的代碼。把代碼放到生產環境判斷裏面哦~

/*生成生產代碼的時候才觸發*/
if (process.env.NODE_ENV === 'production') {
    module.exports.plugins = (module.exports.plugins || []).concat([
        new webpack.DefinePlugin({
            'process.env': {
            NODE_ENV: '"production"'
            }
        }),
        //抽取從node_modules引入的模塊,如vue
        new webpack.optimize.CommonsChunkPlugin({
            name: 'vender',
            minChunks:function(module,count){
                var sPath = module.resource;
                // console.log(sPath,count);
                //匹配 node_modules文件目錄
                return sPath &&
                    /\.js$/.test(sPath) &&
                    sPath.indexOf(
                        path.join(__dirname, 'node_modules')
                    ) === 0
            }
        })
    ])
  }
複製代碼

這是中文文檔上的介紹 commons-chunk-plugin

這是一個好心人總結的各類配置狀況下打包的結果 segmentfault.com/a/119000000…

其餘插件

源碼映射

由於重構和壓縮後的代碼不利於debug,因此咱們先要開啓source map功能,在webpack配置裏面添加一項devtool,以下

module.exports = {
    //entry: ...
    devtool: '#eval-source-map'
}
if (process.env.NODE_ENV === 'production') {
    module.exports.devtool = '#source-map'
}
複製代碼

eval-source-map是開發環境用的源碼映射,source-map是生成環境用的源碼映射

官方對 devtool的介紹在這裏

阮一峯老師對 source map 的介紹在這裏

js代碼壓縮

css文件在 build(抽取和裝載)的同時已經進行了簡單的壓縮,因此下面主要是對js代碼的壓縮,也就是經常的UglifyJs(醜化js),webpack自帶了UglifyJsPlugin插件,在plugins上啓用就行。

new webpack.optimize.UglifyJsPlugin({
    sourceMap: true,//開啓源碼映射
    compress: {
        warnings: false//去到警告
    }
}),
複製代碼

可是以上的用法是webpack1.0遺留下來的,用的舊版的UglifyJs,他的使用說明也在wepack1.0的文檔裏。你能夠有手動安裝uglifyjs-webpack-plugin,引入最新的UglifyJs

/* npm install -save-dev uglifyjs-webpack-plugin */

const UglifyJsPlugin = require('uglifyjs-webpack-plugin')

new UglifyJsPlugin({
  uglifyOptions: {
    compress: {
      warnings: false
    }
  },
  sourceMap: true
})
複製代碼

webpack3.0中文文檔對該插件的說明 在這裏

官方文檔的介紹 在這裏

webpack1.0遷移插件

loader-options-plugin 和其餘插件不一樣。它的用途是幫助人們從 webpack 1 遷移至 webpack 2。官方說明

new webpack.LoaderOptionsPlugin({
    minimize: true
}),
複製代碼

運行構建試一下

好了到目前爲止大部分會用到的插件都引入到了webpack配置裏面,構建一下試試。

完整webpack.config.js的代碼在這裏 pan.baidu.com/s/1jKnDSYa

npm run dev
複製代碼

dev

npm run build
複製代碼

image

發現uglifyJs報錯,是由於咱們沒有配置babel的翻譯器和編譯規則,篇幅有限babel的配置說明放到下一期。

解決方法:在根目錄下建立文件.babelrc,內容以下

{
  "presets": [
    ["env", {
       "modules": false 
    }]
  ]
}
複製代碼

安裝babel-preset-env,npm install --save-dev babel-preset-env

而後再build,沒問題了

image

打包後的目錄結構以下

image

嘮叨幾句

想要深刻了解每一個插件的具體用法,定製本身的需求必定要多點去參考文檔和資料。爲了方便你們我已經在教程中每個插件的下面給了大量的連接,能夠說省去了你們百度的時間,忽然感受本身好細心。

官方文檔也不須要所有都看,用到什麼看什麼,要什麼功能配置就重點看那部分就好,等到有時間再簡要的過一遍文檔。

下期預告

到目前爲止,整個工程能夠說徹底可用了。樣式抽離,公共提取,壓縮都用到了,對比一下vue init webpack-simple project-name構建的簡單工程,會發現咱們比它的功能還完整,有沒有一點成就感呢?

很惋惜,沒想到講插件用了這麼長的篇幅,仍是沒有提到postcss和babel的配置,下一期開始簡要提一下這些,而後咱們繼續優化構建過程,讓他能夠適應多入口多頁面的開發。想要了解之後的內容能夠關注哦~~

第三期已更新:從搭建vue-腳手架到掌握webpack配置(三.多頁面構建)

參考

相關文章
相關標籤/搜索