五. Webpack詳解

1. 什麼是Webpack

1.1 引入

什麼是webpack?這個webpack還真不是一兩句話可以說清楚的。

我們先看看官方的解釋:At its core, webpack is a static module bundler for modern JavaScript applications.

從本質上來講webpack是一個現代的JavaScript應用的靜態模組打包工具。但是它是什麼呢?用概念解釋概念還是不清晰。

我們從兩個點來解釋上面這句話:模組打包

image-20201129131452049

1.2 模組化

在前面我已經用了大量的篇幅解釋了為什麼前端需要模組化。而且我也提到了目前使用前端模組化的一些方案:AMDCMDCommonJSES6

在ES6之前我們要想進行模組化開發,就必須藉助於其他的工具讓我們可以進行模組化開發。並且在通過模組化開發完成了項目後還需要處理模組間的各種依賴並且將其進行整合打包。而webpack其中一個核心就是讓我們可以使用各種規範進行模組化開發,並且會幫助我們處理模組間的依賴關係。

而且不僅僅是JavaScript文件,我們的CSS、圖片、json文件等等在webpack中都可以被當做模組來使用(在後續我們會看到)。這就是webpack中模組化的概念。

1.3 打包

理解了webpack可以幫助我們進行模組化並且處理模組間的各種複雜關係後,打包的概念就非常好理解了。

打包就是將webpack中的各種資源模組進行打包合併成一個或多個包(Bundle)。並且在打包的過程中還可以對資源進行處理,比如壓縮圖片,將scss轉成css,將ES6語法轉成ES5語法,將TypeScript轉成JavaScript等等操作。

但是打包的操作似乎 grunt/gulp 也可以幫助我們完成,它們有什麼不同呢?

1.4 Webpack和grunt/gulp對比

grunt/gulp的核心是Task

我們可以配置一系列的task,並且定義task要處理的事務(例如ES6、ts轉化,圖片壓縮,scss轉成css)

之後讓 grunt/gulp來依次執行這些task,而且讓整個流程自動化。

所以 grunt/gulp 也被稱為前端自動化任務管理工具。

我們來看一個gulp的task

下面的task就是將src下面的所有js文件轉成ES5的語法。

並且最終輸出到dist文件夾中。

const gulp = require('gulp');
const babel = require('gulp-babel');

gulp.task('js', () =>
    gulp.src('src/*.js')
        .pipe(babel)({
            presets: ['es 2015']
        })
        .pipe(gulp.dest('dist'))
);

什麼時候用grunt/gulp呢?

如果你的工程模組依賴非常簡單,甚至是沒有用到模組化的概念。

只需要進行簡單的合併、壓縮,就使用 grunt/gulp 即可。

但是如果整個項目使用了模組化管理而且相互依賴非常強,我們就可以使用更加強大的webpack了。

所以grunt/gulp和webpack有什麼不同呢?

grunt/gulp 更加強調的是前端流程的自動化,模組化不是它的核心。

webpack更加強調模組化開發管理,而文件壓縮合併、預處理等功能,是他附帶的功能。

2. Webpack安裝

安裝webpack首先需要安裝Node.js,Node.js自帶了軟體包管理工具npm

查看自己的node版本:

node -v

全局安裝webpack(這裡我先指定版本號3.6.0,因為vue cli2依賴該版本)

vue cli2 中才能看到webpack的所有配置,vue cli3把webpack配置隱藏起來了不好查看

npm install [email protected] -g

局部安裝webpack(後續才需要)

–save-dev :指定該依賴為開發時依賴,項目打包後不需要繼續使用的。webpack只是一個模組打包工具,項目打包後就不再需要它了

cd 對應目錄
npm install [email protected] --save-dev

為什麼全局安裝後,還需要局部安裝呢?

  • 在終端直接執行 webpack命令,使用的全局安裝的webpack
  • 當在 package.json 中定義了scripts時,其中包含了webpack命令,那麼使用的是局部webpack

3. 使用Webpack

3.1 準備環境

我們創建如下文件和文件夾

image-20201129184359774

文件和文件夾解析

dist文件夾:用於存放之後打包的文件

src文件夾:用於存放我們寫的源文件

  • main.js:項目的入口文件

index.html:瀏覽器打開展示的首頁html

package.json:通過npm init生成,npm包管理的文件(暫時沒有用上,後面才會用上)

註:

① 任何項目想單獨依賴於一些node的環境,一般都會有一個package.json文件

② 如果package.json中配置了一些依賴,我們就可以通過npm install去安裝這些依賴

寫一些測試數據

CommonJS.js

function add(num1, num2) {
    return num1 + num2;
}

function mul(num1, num2) {
    return num1 * num2;
}

module.exports = {
    add,
    mul
}

ES6.js

//ES6的模組化規範
export let name = 'rose';
export let age = 18;
export let gender = '男';

main.js

//CommonJS的模組化規範
const math = require('./CommonJS');
//ES6的模組化規範
import * as person from './ES6';

console.log('Hello Webpack');
console.log(math.add(10,20));
console.log(math.mul(10,20));

console.log(person.name);
console.log(person.age);
console.log(person.gender);

js文件的打包

現在的js文件中使用了模組化的方式進行開發,他們可以直接使用嗎?=> 不可以。

  • 因為如果直接在index.html引入這兩個js文件,瀏覽器並不識別其中的模組化程式碼。

  • 另外,在真實項目中當有許多這樣的js文件時,我們一個個引用非常麻煩,並且後期非常不方便對它們進行管理。

我們應該怎麼做呢?=> 使用webpack工具對多個js文件進行打包。

  • 我們知道,webpack就是一個模組化的打包工具,所以它支援我們程式碼中寫模組化,可以對模組化的程式碼進行處理。(如何處理的待會兒在原理中,我會講解)
  • 另外,如果在處理完所有模組之間的關係後,將多個js打包到一個js文件中,引入時就變得非常方便了。OK,如何打包呢?使用webpack的指令即可
webpack ./src/main.js ./dist/bundle.js

image-20201129184648675

使用打包後的文件

打包後會在dist文件下生成一個bundle.js文件

  • 文件內容有些複雜,這裡暫時先不看,後續再進行分析。
  • bundle.js文件是webpack處理了項目直接文件依賴後生成的一個js文件,我們只需要將這個js文件在index.html中引入即可。
<script src="./dist/bundle.js"></script>
3.2 Webpack配置

入口和出口

我們考慮一下,如果每次使用webpack的命令都需要寫上入口和出口作為參數就非常麻煩 – webpack ./src/main.js ./dist/bundle.js,有沒有一種方法可以將這兩個參數寫到配置中,在運行時直接讀取呢?

webpack.config.js

當然可以,我們可以在項目根路徑下創建一個webpack.config.js文件,該名字是固定的(我們可以修改這個默認名字)。然後寫如下配置即可

//node的語法:導入path包,該path不用我們自己寫而是去node的包里找的(在這之前注意使用npm init命令初始化生成package.json文件)
const path = require('path');

module.exports = {
    //入口:可以是字元串/數組/對象,這裡我們入口只有一個所以寫一個字元串即可
    entry: 'src/main.js',
    //出口:通常是一個對象,裡面至少包含兩個重要屬性,path 和 filename
    output: {
    	//注意:path要寫絕對路徑(為了防止結構改變,且要動態的獲取絕對路徑)
        //path.resolve()函數:對兩個參數進行拼接
        //__dirname:node上下文中的全局變數,保存的就是當前文件所在的絕對路徑
    	path: path.resolve(__dirname, 'dist'),
        //打包之後生成文件的名字
        filename: 'bundle.js' 
	}
}

然後直接在當前項目命令行輸入webpack即可進行打包

image-20201129184821334

局部安裝webpack

目前,我們使用的webpack是全局的webpack,如果我們想使用局部來打包呢?

因為一個項目往往依賴特定的webpack版本,全局的版本可能和這個項目的webpack版本不一致導致打包出現問題。

所以通常一個項目,都有自己局部的webpack。

  • 第一步,項目中需要安裝自己局部的webpack。這裡我們讓局部安裝 webpack3.6.0Vue CLI3中已經升級到 webpack4,但是它將配置文件隱藏了起來,所以查看起來不是很方便。
npm install [email protected] --save-dev
  • 第二步,通過node_modules\.bin\webpack啟動webpack打包

image-20201129185431419

package.json中定義啟動 配置webpack與npm之間的映射

但是,每次執行都敲這麼一長串命令有沒有覺得不方便呢?

OK,我們可以在package.jsonscripts 中定義自己的執行腳本

package.json中的scripts的腳本在執行時,會按照一定的順序尋找命令對應的位置

  • 首先,會尋找本地的node_modules/.bin路徑中對應的位置
  • 如果沒有找到,會去全局的環境變數中尋找
  • 如何執行我們的build指令呢? => npm run build

image-20201129185850487

3.3 CSS文件處理

什麼是loader

loader是webpack中一個非常核心的概念

webpack用來做什麼呢?

  • 在我們之前的實例中,我們主要是用webpack來處理我們寫的js程式碼,並且webpack會自動處理js之間相關的依賴。
  • 但是在開發中我們不僅僅有基本的js程式碼處理,我們也需要載入css、圖片,也包括一些高級的將ES6轉成ES5程式碼,將TypeScript轉成JavaScript程式碼,將scss、less轉成css,將.jsx、.vue文件轉成js文件等。
  • 對於webpack本身的能力來說這些轉化是不支援的那怎麼辦呢?給webpack擴展對應的loader就可以啦。

loader使用過程:

  • 步驟一:通過npm安裝需要使用的loader

  • 步驟二:在webpack.config.js中的modules關鍵字下進行配置

大部分loader我們都可以在webpack的官網中找到,並且學習對應的用法。

準備環境

項目開發過程中我們必然需要添加很多的樣式,而樣式我們往往寫到一個單獨的文件中。

  • 在src目錄中創建一個css文件,其中創建一個normal.css文件。
  • 我們也可以重新組織文件的目錄結構,將零散的js文件放在一個js文件夾中。

image-20201129224117612

normal.css中的程式碼非常簡單,就是將body設置為skyblue。但是這個時候normal.css中的樣式會生效嗎?=> 當然不會,因為我們壓根就沒有引用它,webpack也不可能找到它,因為我們只有一個入口,webpack會從入口開始查找其他依賴的文件。

在入口文件中引用:

image-20201129224216466

打包報錯資訊

重新打包,會出現如下錯誤:這個錯誤告訴我們載入normal.css文件必須有對應的loader。

image-20201129224535167

css-loader

在webpack的官方網站中,我們可以找到如下關於樣式的loader使用方法:

註:

直接下載css-loader是最新版的,可能會與當前vue版本不匹配。我們可以指定下載的版本:如 npm install --save-dev [email protected]

image-20201129233402572

按照官方配置webpack.config.js文件

注意:配置中有一個style-loader,我們並不知道它是什麼,所以可以暫時不進行配置。

重新打包項目並運行index.html會發現已經不再報錯,但是樣式並沒有生效。原因是css-loader只負責載入css文件,但是並不負責將css具體樣式嵌入到文檔中。這個時候我們還需要一個style-loader幫助我們處理。

style-loader

我們來安裝style-loader,並在webpack.config.js進行配置

注意:style-loader需要放在css-loader的前面。

module: {
    rules: [
        {
            // 匹配.css結尾的
            test: /\.css$/,
            //css-loader只負責將css文件進行載入
            //style-loader負責將樣式添加到DOM中
            use: [ 'style-loader', 'css-loader' ]
        }
    ]
}

疑惑:不對吧?按照我們的邏輯,在處理css文件過程中應該是css-loader先載入css文件,再由style-loader來進行進一步的處理,為什麼會將style-loader放在前面呢?

答案:這是因為webpack在讀取使用的loader的過程中,是按照從右向左的順序讀取的。

3.4 Less文件處理

如果我們希望在項目中使用less、scss、stylus來寫樣式,webpack是否可以幫助我們處理呢?

這裡以Less為例,其他也是一樣的

先創建一個less文件,依然放在css文件夾中。並在main.js中進行引入

image-20201130001719017

繼續在官方中查找,我們會找到less-loader相關的使用說明。

  • 首先還是需要安裝對應的loader

注意:我們這裡還安裝了less,因為webpack會使用less對less文件進行編譯

//注意,可能會出現less-loader版本過高的情況,請適當降低版本
npm install --save-dev [email protected] less
  • 其次修改對應的配置文件。添加一個rules選項,用於處理.less文件

image-20201130002308682

3.5 圖片文件處理

url-loader

首先,我們在項目中加入兩張圖片:一張較小的圖片test01.png(小於8kb),一張較大的圖片test02.png(大於8kb)
待會兒我們會針對這兩張圖片進行不同的處理。

我們先考慮在css樣式中引用圖片的情況,所以我更改了normal.css中的樣式:

body {
    background-color: skyblue;
    background: url(../imgs/test01.png);
}

如果我們現在直接打包,會出現如下問題

image-20201130003934698

圖片處理,我們使用url-loader來處理,依然先安裝url-loader

npm install --save-dev url-loader

修改webpack.config.js配置文件:

image-20201130004149833

再次打包,運行index.html,就會發現我們的背景圖片顯示了出來。而仔細觀察會發現背景圖是通過base64顯示出來的。其實這也是limit屬性的作用,當圖片小於8kb時會對圖片進行base64編碼

file-loader

那麼問題來了,如果大於8kb呢?我們將background的圖片改成test02.png
這次因為大於8kb的圖片會通過file-loader進行處理(如果),但是我們的項目中並沒有file-loader
image-20201130004935888

所以,我們需要安裝file-loader(如果已經在webpack.config.js中配置了url-loader,這裡就不需要再配置file-loader了

//注意:版本過高會報錯
npm install --save-dev [email protected]

再次打包,就會發現dist文件夾下多了一個圖片文件

image-20201130005632591

修改文件名稱

我們發現webpack自動幫助我們生成一個非常長的名字。這是一個32位hash值,目的是防止名字重複。但是真實開發中我們可能對打包的圖片名字有一定的要求。比如將所有的圖片放在一個文件夾中,跟上圖片原來的名稱,同時也要防止重複。所以我們可以在options中添加上如下選項:

  • img:文件要打包到的文件夾
  • name:獲取圖片原來的名字,放在該位置
  • hash:8:為了防止圖片名稱衝突,依然使用hash,但是我們只保留8位
  • ext:使用圖片原來的擴展名
{
    test: /\.(png|jpg|gif)$/,
        use: [
            {
                loader: 'url-loader',
                options: {
                    limit: 8192,
                    //[]里寫變數,ext代表擴展名
                    name: 'image/[name]_[hash:8].[ext]'
                }
            }
        ]
},

但是我們發現圖片並沒有顯示出來,這是因為圖片使用的路徑不正確。默認情況下webpack會將生成的路徑直接返回給使用者。但是我們整個程式是打包在dist文件夾下的,所以這裡我們需要在路徑下再添加一個dist/

image-20201130011801590

3.6 ES6語法處理

如果你仔細閱讀webpack打包的js文件會發現寫的ES6語法並沒有轉成ES5,那麼就意味著可能一些對ES6還不支援的瀏覽器沒有辦法很好的運行我們的程式碼。

在前面我們說過如果希望將ES6的語法轉成ES5就需要使用babel這個工具。而在webpack中我們直接使用babel對應的loader就可以了。

安裝 babel-loader

//1. 我們安裝了 7版本的babel-loader, babel-core,babel-preset-es2015
//2.官網是babel-preset-env而不是babel-preset-es2015,如果是babel-preset-env的話我們還需要對它進
//  行配置。這裡我們只使用了ES6的語法就先簡單用一下babel-preset-es2015即可,後面再了解babel-preset-
//  env的配置
npm install [email protected] babel-core babel-preset-es2015

配置webpack.config.js文件

{
      test: /\.js$/,
      //排除node_modules這個文件
      exclude: /(node_modules|bower_components)/,
      use: {
        loader: 'babel-loader',
        options: {
          presets: ['es2015']
        }
      }
}

重新打包並查看bundle.js文件,發現其中的內容變成了ES5的語法

4. Webpack配置Vue

4.1 引入vue.js

後續項目中我們會使用Vuejs進行開發,而且會以特殊的文件來組織vue的組件。所以下面我們來學習一下如何在我們的webpack環境中集成Vuejs

現在我們希望在項目中使用Vuejs那麼必然需要對其有依賴,所以需要先進行安裝

註:因為我們後續是在實際項目中也會使用vue的,所以並不是開發時依賴

npm install [email protected] --save

那麼,接下來就可以按照我們之前學習的方式來使用Vue了

image-20201130214219403

修改完成後重新打包運行程式。打包過程沒有任何錯誤(因為只是多打包了一個vue的js文件而已)。但是運行程式沒有出現想要的效果,而且瀏覽器中有報錯。

image-20201130214426085

這個錯誤說的是我們使用的是runtime-only版本的Vue,這個版本的Vue不允許我們的程式碼中有任何的template(它不能對template進行編譯),而我們通過el掛載的#app元素就是一個template。此外,runtime-compiler版本的Vue實例中可以允許有template,因為有compiler可以用於編譯template。

這裡我只說解決方案,後續我具體講解runtime-onlyruntime-compiler的區別。

我們修改webpack的配置,添加如下內容即可

resolve: {
    //alias: 別名
	alias: {
        //當我們通過 import Vue from vue 導入vue時,會去找我們配置的指定vue版本
        //默認找的是vue.runtime.js
		'vue$': 'vue/dist/vue.esm.js'
	}
}
4.2 el和template的區別

正常運行之後,我們來考慮另外一個問題:如果我們希望將data中的數據顯示在介面中就必須是修改index.html。如果我們後面自定義了組件也必須修改index.html來使用組件。但是html模板在之後的開發中,我並不希望手動的來頻繁修改,是否可以做到呢?

定義template屬性

在前面的Vue實例中我們定義了el屬性,用於和index.html中的#app進行綁定,讓Vue實例之後可以管理它其中的內容。這裡我們可以將div元素中的{{message}}內容刪掉只保留一個基本的id為app的div元素。但是如果我依然希望在其中顯示{{message}}的內容,應該怎麼處理呢?

我們可以在Vue實例中再定義一個template屬性,程式碼如下:

import Vue from 'vue';

new Vue({
    el: '#app',
    template: `
		<div id="app">
    		<h2>{{name}}</h2>
		</div>`,
    data: {
        name: 'polaris'
    }
})

重新打包運行程式顯示的還是一樣的結果和HTML程式碼結構!那麼el和template模板的關係是什麼呢?

我們知道el用於指定Vue要管理的DOM,可以幫助解析其中的指令、事件監聽等。而如果Vue實例中同時指定了template,那麼template模板的內容會替換掉掛載的對應el的模板。

這樣做有什麼好處呢?這樣做之後我們就不需要在以後的開發中再次操作index.html,只需要在template中寫入對應的標籤即可。但是書寫template模組非常麻煩怎麼辦呢?沒有關係,稍後我們會將template模板中的內容進行抽離。分成三部分書寫:templatescriptstyle,結構變得非常清晰。

4.3 Vue組件化開發引入

在學習組件化開發的時候,我說過以後的Vue開發過程中我們都會採用組件化開發的思想。

那麼在當前項目中如果我也想採用組件化的形式進行開發應該怎麼做呢?

看下面的程式碼!

import Vue from 'vue';

const App = {
    template: '<h2>{{name}}</h2>',
    data(){
        return {
            name: 'polaris'
        }
    }
}

new Vue({
    el: '#app',
    template: 
        `<div id="app">
            <App/>
        </div>`,
    components: {
        App
    }
})

當然我們也可以將上面的程式碼抽取到一個js文件中並且導出。

export default {
    template: '<h2>{{name}}</h2>',
    data(){
        return {
            name: 'polaris'
        }
    }
}
import Vue from 'vue';
import App from './vue/app.js';

new Vue({
    el: '#app',
    template: 
        `<div id="app">
            <App/>
        </div>`,
    components: {
        App
    }
})
4.4 .vue文件封裝處理

但是一個組件以一個js對象的形式進行組織和使用的時候是非常不方便的。一方面編寫template模組非常的麻煩,另外一方面如果有樣式的話我們寫在哪裡比較合適呢?

現在我們以一種全新的方式來組織一個vue的組件,但是這個時候這個文件可以被正確的載入嗎?

<template>
    <div>
        <h2>{{name}}</h2>
    </div>
</template>

<script>
    export default {
        data(){
            return {
                name: 'polaris'
            }
        }
    }
</script>

<style scoped>
 h2 {
     color: blueviolet;
 }
</style>
import Vue from 'vue';
// import App from './vue/app.js';
import App from './vue/App.vue';

new Vue({
    el: '#app',
    template: 
        `<div id="app">
            <App/>
        </div>`,
    components: {
        App
    }
})

必然不可以,這種特殊的文件以及特殊的格式必須有人幫助我們處理。誰來處理呢?vue-loader以及vue-template-compiler

安裝vue-loadervue-template-compiler

//vue-loader的版本在14+之後還需要配置一個插件
//vue-template-compiler的版本與vue版本對應即可

npm install [email protected] [email protected] --save-dev

修改webpack.config.js的配置文件:

//注意:如果寫了css,less等,記得還要加上對應的loader
{
	test: /\.vue$/,
	use: ['vue-loader']
}
4.5 實現導入.vue文件時不加後綴

我們只需要在webpack配置文件中加如下配置即可

//webpack中resolve主要是解決一些路徑相關的問題
resolve: {
	extensions:['.js','.css','.vue']
}

5. plugin的使用

5.1 認識plugin

plugin是什麼?

plugin是插件的意思,通常是用於對某個現有的架構進行擴展。webpack中的插件就是對webpack現有功能的各種擴展,比如打包優化,文件壓縮等。

loader和plugin區別

loader主要用於轉換某些類型的模組,它是一個轉換器。

plugin是插件,它是對webpack本身的擴展,是一個擴展器。

plugin的使用過程

步驟一:通過npm安裝需要使用的plugins(某些webpack已經內置的插件不需要安裝)。

步驟二:在webpack.config.js中的plugins中配置插件。

下面我們就來看看可以通過哪些插件對現有的webpack打包過程進行擴容,讓我們的webpack變得更加好用。

5.2 添加版權的Plugin

我們先來使用一個最簡單的插件,為打包的文件添加版權聲明。該插件名字叫BannerPlugin,屬於webpack自帶的插件。

按照下面的方式來修改webpack.config.js的文件:

const webpack = require('webpack');

module.exprots = {
    ...
    plugins: [
        new webpack.BannerPlugin('最終版權歸polaris所有')
    ]
}

image-20201130232301024

5.3 打包html的Plugin

目前我們的index.html文件是存放在項目的根目錄下的。我們知道在真實發布項目時,發布的是dist文件夾中的內容,但是dist文件夾中如果沒有index.html文件,那麼打包的js等文件也就沒有意義了。所以我們需要index.html文件打包到dist文件夾中,這個時候就可以使用HtmlWebpackPlugin插件。

HtmlWebpackPlugin插件的功能

自動生成一個index.html文件(可以指定模板來生成)

將打包的js文件,自動通過script標籤插入到body中

安裝HtmlWebpackPlugin插件

//報錯就換一下版本
npm install [email protected] --save-dev

使用插件

修改webpack.config.js文件中plugins部分的內容如下:

const htmlWebpackPlugin = require('html-webpack-plugin');

plugins: [
	new htmlWebpackPlugin({
        //template表示根據什麼模板來生成index.html
        //如下配置會在當前webpack配置文件所在目錄下找index.html作為模板
		template: 'index.html'
	}),
]

注意:我們需要刪除之前在webpack配置文件的output中添加的publicPath屬性,否則插入的script標籤中的src可能會有問題

image-20201130235647957

5.4 js壓縮的Plugin

在項目發布之前我們必然需要對js等文件進行壓縮處理,這裡我們就對打包的js文件進行壓縮

webpack自帶的也有一個類似功能的插件

我們使用一個第三方的插件uglifyjs-webpack-plugin,並且版本號指定1.1.1,和CLI2保持一致

npm install [email protected] --save-dev

修改webpack.config.js文件,使用插件:

const uglifyJsPlugin = require('uglifyjs-webpack-plugin');

module.exprots = {
    ...
    plugins: [
        new uglifyJsPlugin()
    ]
}

查看打包後的bunlde.js文件,是已經被壓縮過了。

6. 本地伺服器

6.1 搭建本地伺服器

webpack提供了一個可選的本地開發伺服器,這個本地伺服器基於node.js搭建,內部使用express框架,可以實現我們想要的讓瀏覽器自動刷新顯示我們修改後的結果。

express的原理是服務於某一個文件夾,根據配置文件決定可能會生成的東西並暫時放入記憶體中,我們做測試時瀏覽器所展示的東西是基於記憶體的。最後當我們執行了npm run build,才會將之輸入到磁碟中。

不過它是一個單獨的模組,在webpack中使用之前需要先安裝它

//vue cli2 => [email protected] => [email protected]

npm install [email protected] --save-dev

devserver也是作為webpack中的一個選項,選項本身可以設置如下屬性:

  • contentBase:為哪一個文件夾提供本地服務,默認是根文件夾,我們這裡要填寫./dist
  • port:埠號,默認8080
  • inline:頁面實時刷新
  • historyApiFallback:在SPA頁面中,依賴HTML5的history模式

webpack.config.js文件配置修改如下:

devServer: {
	contentBase: './dist',
	inline: true
}

我們可以再配置另外一個scripts: --open參數表示直接打開瀏覽器

"dev":"webpack-dev-server --open"
6.2 webpack配置文件的分離

先將之前的webpack.config.js配置文件進行分離

image-20201201004645729

base.config.js

/* base.config.js 只放公共的配置,即開發和部署後都需要的配置 */

const path = require('path');
const webpack = require('webpack');
const htmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    entry: './src/main.js',
    output: {
        //注意:這裡不要直接拼接'dist'了,因為當前文件base.config.js在build文件夾下
        path: path.resolve(__dirname, '../dist'),
        filename: 'bundle.js',
    },
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [ 'style-loader', 'css-loader' ]
            },
            {
                test: /\.vue$/,
                use: ['vue-loader']
            }
        ]
    },
    resolve: {
        alias: {
            'vue$': 'vue/dist/vue.esm.js'
        }
    },
    plugins: [
        new webpack.BannerPlugin('最終版權歸polaris所有'),
        new htmlWebpackPlugin({
            template: 'index.html'
        }),
        // new uglifyJsPlugin()
    ],
    // devServer: {
    //     contentBase: './dist',
    //     inline: true,
    //     port:3300
    // }
} 

dev.config.js

//開發需要而部署後不需要的配置
module.exports = {
    devServer: {
        contentBase: '../dist',
        inline: true,
        port:3300
    }
} 

prod.config.js

//部署後需要的配置,但是開發時不推薦使用的配置
const uglifyJsPlugin = require('uglifyjs-webpack-plugin');

module.exports = {
    plugins: [
        new uglifyJsPlugin()
    ],
} 

使用webpack-merge對配置文件進行合併

安裝webpack-merge

npm install webpack-merge --save-dev

修改dev.config.jsprod.config.js

const webpackMerge = require('webpack-merge');
const baseConfig = require('./base.config.js');

module.exports = webpackMerge.merge(baseConfig, {
    devServer: {
        contentBase: './dist',
        inline: true,
        port:3300
    }
})
const uglifyJsPlugin = require('uglifyjs-webpack-plugin');
const webpackMerge = require('webpack-merge');
const baseConfig = require('./base.config.js');

module.exports = webpackMerge.merge(baseConfig, {
    plugins: [
        new uglifyJsPlugin()
    ],
})

在package.json的script腳本動態指定build和dev的環境

"build": "webpack --config ./build/dev.config.js",
"dev": "webpack-dev-server --open --config ./build/dev.config.js"