深刻淺出的webpack構建工具---DevServer配置項(二)

深刻淺出的webpack構建工具---DevServer配置項(二)css

閱讀目錄html

 摘要:webpack-dev-server是一個使用了express的Http服務器,它的做用主要是爲了監聽資源文件的改變,該http服務器和client使用了websocket通訊協議,只要資源文件發生改變,webpack-dev-server就會實時的進行編譯。

1. contentBase

該配置項指定了服務器資源的根目錄,若是不配置contentBase的話,那麼contentBase默認是當前執行的目錄,通常是項目的根目錄。
可能如上解析還不夠清晰,沒有關係,咱們下面仍是先看下我整個項目的目錄結構,而後進行相關的配置,使用contentBase配置項再來理解下:

### 目錄結構以下:
demo1                                       # 工程名
|   |--- dist                               # dist是打包後生成的目錄文件             
|   |--- node_modules                       # 全部的依賴包
|   |--- js                                 # 存放全部js文件
|   | |-- demo1.js  
|   | |-- main.js                           # js入口文件
|   |
|   |--- webpack.config.js                  # webpack配置文件
|   |--- index.html                         # html文件
|   |--- styles                             # 存放全部的css樣式文件                              
|   |--- .gitignore  
|   |--- README.md
|   |--- package.json

index.html 代碼以下:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <link href="dist/main.css" rel="stylesheet" type="text/css" />
</head>
<body>
  <div id="app"></div>
  <script src="dist/bundle.js"></script>
</body>
</html>

main.js 代碼以下:

require('../styles/main.css');

import demo1 from './demo1.js';

demo1.js 代碼以下:

console.log(111);

webpack配置代碼以下:

const path = require('path');
// 提取css的插件
const ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
  entry: './js/main.js',
  output: {
    filename: 'bundle.js',
    // 將輸出的文件都放在dist目錄下
    path: path.resolve(__dirname, 'dist'),
    publicPath: '/dist'
  },
  mode: 'development',
  module: {
    rules: [
      {
        // 使用正則去匹配要用該loader轉換的css文件
        test: /\.css$/,
        loaders: ExtractTextPlugin.extract({
          // 轉換 .css文件須要使用的Loader
          use: ['css-loader']
        })
      },
      {
        test: /\.(png|jpg)$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: '[name].[ext]'
        }
      }
    ]
  },
  resolve: {
    // modules: ['plugin', 'js']
  },
  plugins: [
    new ExtractTextPlugin({
      // 從js文件中提取出來的 .css文件的名稱
      filename: `main.css`
    })
  ]
};

package.json 配置代碼以下:

"scripts": {
  "dev": "webpack-dev-server --progress --colors --devtool source-map --hot --inline",
  "build": "webpack --progress --colors"
}

運行 npm run dev後,一切正常成功後,在瀏覽器下 運行 http://localhost:8080/ 便可在控制檯看到 打印出 111 了。

如上是沒有使用devServer配置的狀況下。 下面咱們來看下使用 devServer配置.

在webpack配置加上以下配置,即配置項指定了服務器資源的根目錄。好比咱們打包後的文件放入 dist目錄下。

module.exports = {
  devServer: {
    contentBase: path.join(__dirname, "dist")
  },
}

如上配置完成後,咱們再運行 npm run dev, 再在地址欄中 運行 http://localhost:8080/ 後看到以下信息:

也就是說 配置了 contentBase後,服務器就指向了資源的根目錄,而再也不指向項目的根目錄。所以再訪問 http://localhost:8080/index.html 是訪問不到的。可是訪問 http://localhost:8080/bundle.js 該js文件是能夠訪問的到的。

2. port

該配置屬性指定了開啓服務器的端口號,好比以下配置:

module.exports = {
  devServer: {
    contentBase: path.join(__dirname, "dist"),
    port: 8081
  },
}

配置完成後,再運行打包命令 npm run dev 後,能夠看到以下圖所示:

如今咱們能夠經過 以下地址 http://localhost:8081/ 也能夠訪問了,也就是說 經過port配置,端口號從默認的8080改爲8081了。

3. host

該配置項用於配置 DevServer的服務器監聽地址。好比想讓局域網的其餘設備訪問本身的本地服務,則能夠在啓動DevServer時帶上 --host 0.0.0.0.
host的默認值是 127.0.0.1, 下面咱們也簡單的配置下 host 屬性。

module.exports = {
  devServer: {
    contentBase: path.join(__dirname, "dist"),
    port: 8081,
    host: '0.0.0.0'
  }
}

配置完成後,再運行打包命令 npm run dev 後,能夠看到以下圖所示:

咱們訪問 http://0.0.0.0:8081/  能夠訪問的到了,其餘局域網的同窗應該也能訪問的到吧。

4. headers

該配置項能夠在HTTP響應中注入一些HTTP響應頭。 好比以下:

module.exports = {
  devServer: {
    contentBase: path.join(__dirname, "dist"),
    port: 8081,
    host: '0.0.0.0',
    headers: {
      'X-foo': '112233'
    }
  }
}

如上配置完成後,打包下,刷新下瀏覽器,能夠看到請求頭加了上面的信息,以下所示:

5. historyApiFallback

該配置項屬性是用來應對返回404頁面時定向跳轉到特定頁面的。通常是應用在 HTML5中History API 的單頁應用,好比在訪問路由時候,訪問不到該路由的時候,會跳轉到index.html頁面。
咱們如今在dist目錄下 新建一個index.html, 代碼以下:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
  <div id="app">歡迎大家來訪問我</div>
</body>
</html>

爲了使配置項生效,咱們只須要設置該 屬性值爲true便可; 以下配置:

module.exports = {
  devServer: {
    contentBase: path.join(__dirname, "dist"),
    port: 8081,
    host: '0.0.0.0',
    headers: {
      'X-foo': '112233'
    },
    historyApiFallback: true
  },
}

如今咱們來訪問 http://0.0.0.0:8081/home 這個不存在的路由時,會發生什麼?以下所示:

如上能夠看到,當不存在該路由的時候,經過該配置項,設置屬性值爲true的時候,會自動跳轉到 index.html下。

固然如上只是簡單的配置下,固然咱們也能夠手動經過 正則來匹配路由,好比訪問 /user 跳轉到 user.html,訪問 /home 跳轉到 home.html, 以下配置:

固然咱們須要在 dist 目錄下 新建 home.html 和 user.html 了,以下基本配置:

module.exports = {
  devServer: {
    contentBase: path.join(__dirname, "dist"),
    port: 8081,
    host: '0.0.0.0',
    headers: {
      'X-foo': '112233'
    },
    historyApiFallback: {
      // 使用正則來匹配路由
      rewrites: [
        { from: /^\/user/, to: '/user.html' },
        { from: /^\/home/, to: '/home.html' }
      ]
    }
  },
}

從新運行打包下, 繼續訪問 http://0.0.0.0:8081/home 和 http://0.0.0.0:8081/user 便可看到能訪問獲得對應的頁面了。

6. hot

該配置項是指模塊替換換功能,DevServer 默認行爲是在發現源代碼被更新後經過自動刷新整個頁面來作到實時預覽的,
可是開啓模塊熱替換功能後,它是經過在不刷新整個頁面的狀況下經過使用新模塊替換舊模塊來作到實時預覽的。

咱們能夠在 devServer中 配置 hot: true 便可:以下配置代碼:

module.exports = {
  devServer: {
    contentBase: path.join(__dirname, "dist"),
    port: 8081,
    host: '0.0.0.0',
    headers: {
      'X-foo': '112233'
    },
    historyApiFallback: {
      // 使用正則來匹配路由
      rewrites: [
        { from: /^\/user/, to: '/user.html' },
        { from: /^\/home/, to: '/home.html' }
      ]
    },
    hot: true
  }
}

固然咱們也能夠在scripts命令行中配置,好比我項目中在package.json中的scripts配置以下:

"scripts": {
  "dev": "webpack-dev-server --progress --colors --devtool source-map --hot --inline",
  "build": "webpack --progress --colors"
}

7. inline

webpack-dev-server 有兩種模式能夠實現自動刷新和模塊熱替換機制。
1. iframe
頁面是被嵌入到一個iframe頁面,而且在模塊變化的時候重載頁面。

可能如上解釋,咱們還不能徹底能理解究竟是什麼意思,沒有關係,咱們繼續來看下配置和實踐效果。

module.exports = {
  devServer: {
    port: 8081,
    host: '0.0.0.0',
    headers: {
      'X-foo': '112233'
    },
    inline: false
  },
}

如上代碼配置 inline: false 就是使用iframe模式來重載頁面了。咱們的目錄結構仍是上面的那種結構,而後咱們只須要在webpack中全部
配置以下:

const path = require('path');
// 提取css的插件
const ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
  entry: './js/main.js',
  output: {
    filename: 'bundle.js',
    // 將輸出的文件都放在dist目錄下
    path: path.resolve(__dirname, 'dist'),
    publicPath: '/dist'
  },
  mode: 'development',
  module: {
    rules: [
      {
        // 使用正則去匹配要用該loader轉換的css文件
        test: /\.css$/,
        loaders: ExtractTextPlugin.extract({
          // 轉換 .css文件須要使用的Loader
          use: ['css-loader']
        })
      },
      {
        test: /\.(png|jpg)$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: '[name].[ext]'
        }
      }
    ]
  },
  resolve: {
    // modules: ['plugin', 'js']
  },
  devServer: {
    port: 8081,
    host: '0.0.0.0',
    headers: {
      'X-foo': '112233'
    },
    inline: false
  },
  plugins: [
    new ExtractTextPlugin({
      // 從js文件中提取出來的 .css文件的名稱
      filename: `main.css`
    })
  ]
};

而後當咱們在命令行中,輸入 webpack-dev-server 後 回車,能夠看到以下圖所示:

接着咱們在瀏覽器下 輸入 http://0.0.0.0:8081/webpack-dev-server/ 地址後 回車,便可看到頁面,咱們查看源代碼的時候,會看到嵌入了一個iframe頁面,以下圖所示:

當咱們從新修改main.js 或 它的依賴文件 demo1.js 的時候,保存後,它也會自動從新加載頁面,這就是使用 iframe 模式來配置加載頁面的。

iframe 模式的特色有:
1. 在網頁中嵌入了一個iframe,將咱們本身的應用代碼注入到 這個 iframe中去了。
2. 在頁面頭部會有一個 App ready. 這個提示,用於顯示構建過程的狀態信息。
3. 加載了 live.bundle.js文件,還同時包含了 socket.io的client代碼,進行了 websocket通信,從而完成了自動編譯打包,頁面自動刷新功能。

咱們看下請求的全部文件有以下:

2. inline 模式

開啓模式,只須要把上面的配置代碼變爲 inline: true便可,它在構建變化後的代碼會經過代理客戶端來控制網頁刷新。
如上配置後,咱們運行 webpack-dev-server 命令後,以下所示:

接着咱們在地址欄中 http://0.0.0.0:8081/ 運行下 就能夠訪問到 項目中的根目錄 index.html了,當咱們修改入口文件的代碼保存也同樣
能實時刷新,其實效果是同樣的。

inline模式的特色有:
1. 構建的消息在控制檯中直接顯示出來。
2. socket.io的client代碼被打包進bundle.js當中,這樣就能和websocket通信,從而完成自動編譯工做,頁面就能實現自動刷新功能。
3. 之後的每個入口文件都會插入上面的socket的一段代碼,這樣會使的打包後的bundle.js文件變得臃腫。

8. open

該屬性用於DevServer啓動且第一次構建完成時,自動使用咱們的系統默認瀏覽器去打開網頁。

以下配置:

module.exports = {
  devServer: {
    // contentBase: path.join(__dirname, "dist"),
    port: 8081,
    host: '0.0.0.0',
    headers: {
      'X-foo': '112233'
    },
    // hot: true,
    inline: true,
    open: true
  }
}

設置 open: true 便可,當咱們運行完成 npm run dev 打包的時候,會自動打開默認的瀏覽器來查看網頁。

9. overlay

該屬性是用來在編譯出錯的時候,在瀏覽器頁面上顯示錯誤。該屬性值默認爲false,須要的話,設置該參數爲true。
爲了演示下,咱們來在main.js 代碼內使用ES6的語法來編寫代碼,ES6是使用babel-loader 這樣的來轉化的,可是目前咱們的項目先不安裝該loader,應該會報錯的。好比在main.js 代碼加以下一句代碼:
const a;

配置 overlay: true便可:以下配置:

module.exports = {
  devServer: {
    // contentBase: path.join(__dirname, "dist"),
    port: 8081,
    host: '0.0.0.0',
    headers: {
      'X-foo': '112233'
    },
    // hot: true,
    inline: true,
    open: true,
    overlay: true
  }
}

運行 npm run dev 後,自動打開網頁,顯示以下所示:

10. stats(字符串)

該屬性配置是用來在編譯的時候再命令行中輸出的內容,咱們沒有設置 stats的時候,輸出是以下的樣子:以下所示:

該屬性值能夠有以下值:

stats: 'errors-only' 表示只打印錯誤,咱們添加下這個配置到devServer中;以下代碼配置:

module.exports = {
  devServer: {
    // contentBase: path.join(__dirname, "dist"),
    port: 8081,
    host: '0.0.0.0',
    headers: {
      'X-foo': '112233'
    },
    // hot: true,
    inline: true,
    open: true,
    overlay: true,
    stats: 'errors-only'
  }
}

如今咱們繼續 運行 npm run dev 後,會看到命令行中顯示以下:

該配置的含義是 只有錯誤的纔會被打印,沒有錯誤就不打印,所以多餘的信息就不會顯示出來了。

該屬性值還有 'minimal', 'normal', 'verbose' 等。

11. compress

該屬性是一個布爾型的值,默認爲false,當他爲true的時候,它會對全部服務器資源採用gzip進行壓縮。

12. proxy 實現跨域

有時候咱們使用webpack在本地啓動服務器的時候,因爲咱們使用的訪問的域名是 http://localhost:8081 這樣的,可是咱們服務端的接口是其餘的,

那麼就存在域名或端口號跨域的狀況下,可是很幸運的是 devServer有一個叫proxy配置項,能夠經過該配置來解決跨域的問題,那是由於 dev-server 使用了 http-proxy-middleware 包(瞭解該包的更多用法 )

假如如今咱們本地訪問的域名是 http://localhost:8081, 可是我如今調用的是百度頁面中的一個接口,該接口地址是:http://news.baidu.com/widget?ajax=json&id=ad。如今咱們只須要在devServer中的proxy的配置就能夠了:
以下配置:

proxy: {
  '/api': {
    target: 'http://news.baidu.com', // 目標接口的域名
    // secure: true,  // https 的時候 使用該參數
    changeOrigin: true,  // 是否跨域
    pathRewrite: {
      '^/api' : ''  // 重寫路徑
    }
  }
}

所以全部的配置以下:

module.exports = {
  devServer: {
    // contentBase: path.join(__dirname, "dist"),
    headers: {
      'X-foo': '112233'
    },
    // hot: true,
    port: '8081',
    inline: true,
    open: true,
    overlay: true,
    stats: 'errors-only',
    proxy: {
      '/api': {
        target: 'http://news.baidu.com', // 目標接口的域名
        // secure: true,  // https 的時候 使用該參數
        changeOrigin: true,  // 是否跨域
        pathRewrite: {
          '^/api' : ''  // 重寫路徑
        }
      }
    }
  }
}

而後咱們在main.js裏面編寫以下代碼:

import axios from 'axios';

axios.get('/api/widget?ajax=json&id=ad').then(res => {
  console.log(res);
});

在這裏請求我使用 axios 插件,其實和jquery是一個意思的。爲了方便就用了這個。

下面咱們來理解下上面配置的含義:

1. 首先是百度的接口地址是這樣的:http://news.baidu.com/widget?ajax=json&id=ad;
2. proxy 的配置項 '/api' 和 target: 'http://news.baidu.com' 的含義是,匹配請求中 /api 含有這樣的域名 重定向 到 'http://news.baidu.com'來。所以我在接口地址上 添加了前綴 '/api', 如: axios.get('/api/widget?ajax=json&id=ad'); 所以會自動補充前綴,也就是說,url: '/api/widget?ajax=json&id=ad' 等價
於 url: 'http://news.baidu.com/api/widget?ajax=json&id=ad'.
3. changeOrigin: true/false 還參數值是一個布爾值,含義是 是否須要跨域。
4. secure: true, 若是是https請求就須要改參數配置,須要ssl證書吧。
5. pathRewrite: {'^/api' : ''}的含義是重寫url地址,把url的地址裏面含有 '/api' 這樣的 替換成 '',
所以接口地址就變成了 http://news.baidu.com/widget?ajax=json&id=ad; 所以就能夠請求獲得了,最後就返回
接口數據了。

以下圖所示:

相關文章
相關標籤/搜索