<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      webpack 插件拾趣 (1) —— webpack-dev-server

      結束了一季的忙碌,我這封筆已久的博客也終究該從春困的咒印中復蘇,想來寫些實用易讀的作為開篇,自然是最好不過。

      新開個 webpack 插件/工具介紹的文章系列,約莫每周更新一篇篇幅適中的文章聊以共勉,興許合適。

      原本期望每篇文章里可以介紹若干個插件,但鑒于部分插件略為復雜,且單篇內容不想寫的唇焦舌敝惹人倦煩,所以像本文要介紹的 webpack-dev-server 就獨立一文了。

      回歸主題,今天你或許會花上30分鐘的時間讀完本章,并掌握 webpack-dev-server 的使用方法、理清一些容易困惑的配置(諸如 publicPath)或概念(如HMR)

      另外,本章涉及的相關用例,可以在我的github(https://github.com/VaJoy/webpack-plugins/tree/master/char1)上下載到。

      一. webpack-dev-server 他爹和他爹的朋友

      我們并不急著把 webpack-dev-server 直接拉出來介紹一通,我們先了解下他的兩位長輩 —— 他爹 webpack-dev-middleware,以及他爹的朋友 webpack-hot-middleware

      他們三人有著某些親密的聯系,不少讀者可能會對其身份存在認知混亂,所以很有必要按輩分次序來分別介紹。

       

      1.1 webpack-dev-middleware

      假設我們在服務端使用 express 開發一個站點,同時也想利用 webpack 對靜態資源進行打包編譯,那么在開發環節,每次修改完文件后,都得先執行一遍 webpack 的編譯命令,等待新的文件打包到本地,再做進一步調試。雖然咱們可以利用 webpack 的 watch mode 來監聽變更、自動打包,但等待 webpack 重新執行的過程往往很耗時。

      而 webpack-dev-middleware 的出現很好地解決了上述問題 —— 作為一個 webpack 中間件,它會開啟 watch mode 監聽文件變更,并自動地在內存中快速地重新打包、提供新的 bundle。

      說白了就是 —— 自動編譯(watch mode)+速度快(全部走內存)

      webpack-dev-middleware 的配置與使用其實很輕松,我們通過一個非常簡單的項目來示例(可以點這里獲取):

      PROJECT
      │  app.js  //應用入口文件
      │  express.config.js   // express 服務啟動配置
      │  package.json
      │  webpack.config.js  // webpack 配置
      │  
      └─src
          ├─html
          │      index.html  //首頁
          │      
          └─js
              └─page
                      index.js  //首頁腳本模塊

      它的 webpack.config.js 配置文件如下:

      module.exports = {
          entry: './app.js',
          output: {
              publicPath: "/assets/",
              filename: 'bundle.js',
              //path: '/'   //只使用 dev-middleware 的話可以忽略本屬性
          }
      };

      這里有一個非常關鍵的配置 —— publicPath,熟悉 webpack 的同學都知道,它是生成的新文件所指向的路徑,可以用于模擬 CDN 資源引用。

      打個比方,當我們使用 url-loader 來處理圖片時,把 publickPath 設為“http://abcd/assets/”,則最終打包后,樣式文件里所引用的圖片地址會加上這個前綴:

      /**-------------webpack配置項--------------**/
          output: {
              publicPath: "http://abcd/assets/",   //模擬CDN地址
              filename: 'bundle.js',
              path: path.join(__dirname, 'dist/')
          },
          module: {
              rules: [{
                      test: /\.css$/,
                      loader: ['style-loader', 'css-loader']
                  },
                  {
                      test: /\.(png|jpg|gif)$/,
                      loader: 'url-loader'
                  }]
          }
      
      /**-------------頁面引入的樣式模塊 index.css--------------**/
      section{
          width:300px;
          height: 300px;
          background-image: url(a.jpg);
      }

      打包后(dist/bundle.js 里的樣式執行效果)

      當然如果你沒把資源(比如這張md5化后的圖片)托管到CDN上,是請求不到的,不過通過Fiddler配置代理映射,可以解決這個問題。

      然而,在使用 webpack-dev-middleware (或其它走內存的工具)的情況下,publicPath 只建議配置相對路徑 —— 因為 webpack-dev-middleware 在使用的時候,也需要再配置一個 publicPath(見下文 express.config.js 的配置),用于標記從內存的哪個路徑去存放和查找資源,這意味著 webpack-dev-middleware 的 publicPath 必須是相對路徑。

      而如果 webpack.config.js 里的 publicPath 跟 webpack-dev-middleware 的 publicPath 不一致的話(比如前者配置了 http 的路徑),會導致資源請求到了內存外的地方去了(本地也沒這個文件,也沒法走 Fiddler 代理來解決),從而返回404~

      如果上面這段話瞧著糊涂,建議暫時擱置它,后續回過頭再來咀嚼,我們先了解下所謂的“webpack-dev-middleware 的 publicPath”是什么。

      如下是 express.config.js 文件:

      const path = require('path');
      const express = require("express");
      const ejs = require('ejs');
      const app = express();
      const webpack = require('webpack');
      const webpackMiddleware = require("webpack-dev-middleware");
      let webpackConf = require('./webpack.config.js');
      
      app.engine('html', ejs.renderFile);
      app.set('views', path.join(__dirname, 'src/html'));
      app.set("view engine", "html");
      
      var compiler = webpack(webpackConf);
      
      app.use(webpackMiddleware(compiler, {  //使用 webpack-dev-middleware
          publicPath: webpackConf.output.publicPath  //保持和 webpack.config.js 里的 publicPath 一致
      }));
      
      app.get("/", function(req, res) {
          res.render("index");
      });
      
      app.listen(3333);

      可見 webpack-dev-middleware 的使用語法其實就這么簡練,不外乎是:

      var webpackMiddleware = require("webpack-dev-middleware");
      
      app.use(webpackMiddleware(webpack(webpackConfig), options));

      其中 options 是 webpack-dev-middleware 的配置對象,詳盡的可選項可參考官方文檔,限于篇幅,此處只介紹 publicPath —— 它用于決定 webpack 打包編譯后的文件,要存放在內存中的哪一個虛擬路徑,并提供一個 SERVER,將路徑和文件映射起來(即使它們都是虛擬的,但依舊可請求的到)

      當前的例子,是將內存路徑配置為 /assets/,這意味著打包后的 bundle.js 會存放在虛擬內存路徑 SERVERROOT/assets/ 下(這里的“SERVERROOT”實際上即 html 文件的訪問路徑),也意味著我們可以直接在 src/html/index.html 中通過 src='assets/bundle.js' 的形式引用和訪問內存中的 bundle 文件:

      <body>
          <div></div>
          <script src="assets/bundle.js"></script>
      </body>

      我們執行一遍 node express.config,然后訪問 http://localhost:3333,便能正常訪問頁面、請求和執行 bundle.js:

      同時,只要我們修改了頁面的腳本模塊(比如 src/js/index.js),webpack-dev-middleware 便會自行重新打包到內存,替換掉舊的 bundle,我們只需要刷新頁面即可看到剛才的變更。

      這里寫個關于 webpack-dev-middleware 的小 tips:

      1. webpack-dev-middleware 配置項里的 publicPath 要與 webpack.config 里的 output.publicPath 保持一致(并且只能是相對路徑),不然會出現問題;
      2. 使用 webpack-dev-middleware 的時候,其實可以完全無視 webpack.config 里的 output.path,甚至不寫也可以,因為走的純內存,output.publicPath 才是實際的 controller;
      3. publicPath 配置的相對路徑,實際是相對于 html 文件的訪問路徑。

      1.2 HMR

      機智的小伙伴們在讀完 webpack-dev-middleware 的介紹后,會洞悉出它的一處弱點 —— 雖然 webpack-dev-middleware 會在文件變更后快速地重新打包,但是每次都得手動刷新客戶端頁面來訪問新的內容,還是略為麻煩。這是因為 webpack-dev-middleware 在應用執行的時候,沒辦法感知到模塊的變化。

      那么是否有辦法可以讓頁面也能自動更新呢?webpack-hot-middleware 便是幫忙填這個坑的人,所以我在前文稱之為 —— webpack-dev-middleware 的好朋友。

      webpack-hot-middleware 提供的這種能力稱為 HMR,所以在介紹 webpack-hot-middleware 之前,我們先來科普一下 HMR。

      HMR 即模塊熱替換(hot module replacement)的簡稱,它可以在應用運行的時候,不需要刷新頁面,就可以直接替換、增刪模塊。

      webpack 可以通過配置 webpack.HotModuleReplacementPlugin 插件來開啟全局的 HMR 能力,開啟后 bundle 文件會變大一些,因為它加入了一個小型的 HMR 運行時(runtime),當你的應用在運行的時候,webpack 監聽到文件變更并重新打包模塊時,HMR 會判斷這些模塊是否接受 update,若允許,則發信號通知應用進行熱替換。

      這里提及的“判斷模塊是否接受 update”是指判斷模塊里是否執行了 module.hot.accept(), 這里舉個小例子:

      如圖,白色的部分是編譯后的模塊依賴樹,這時候我們修改了 B 模塊,導致 B 模塊以及依賴它的 A 模塊都出現了變化(綠色部分)

      模塊變更的時候,webpack 會順著依賴樹一層一層往上冒泡,查詢哪個模塊是接受 update 的,查詢到了則終止冒泡,并通知 SERVER 更新其爬過的模塊。

      假設我們把 module.hot.accept() 放在 B 模塊執行,則 webpack 會查找到 B` 模塊的變更就停止繼續往上冒泡查找了(A`是不允許變更的模塊)—— 如果 B 的內容變更,是直接在 B 模塊調用的,那頁面就能直接展示出新的內容出來,這樣效率也高(繞過了A模塊);但如果 B 的內容,實際上是要經過 A 來調用,才能在頁面上展示出來,那此時頁面就不會刷新(即使 B 的內容變了)

      說白了就是 module.hot.accept() 放的好,就可以繞過一些不必要的模塊變更檢查來提升效率,不過對于懶人來說,直接置于最頂層的模塊(比如入口模塊)最為省心。

      關于更多的 HMR 的知識點,可以參考官方文檔

      1.3 webpack-hot-middleware 

      聊完了 HMR,我們回頭了解下 webpack-hot-middleware 的使用。

      我們試著對前文使用的項目來做一番改造 —— 引入 webpack-hot-middleware 來提升開發體驗。

      首先往 express.config.js 加上一小段代碼:

      app.engine('html', ejs.renderFile);
      app.set('views', path.join(__dirname, 'src/html'));
      app.set("view engine", "html");
      
      var compiler = webpack(webpackConf);
      
      app.use(webpackMiddleware(compiler, {
          publicPath: webpackConf.output.publicPath
      }));
      
      //添加的代碼段,引入和使用 webpack-hot-middleware
      app.use(require("webpack-hot-middleware")(compiler, {
          path: '/__webpack_hmr'
      }));
      
      app.get("/", function(req, res) {
          res.render("index");
      });
      
      app.listen(3333);

      即在原先的基礎上引入了 webpack-hot-middleware:

      app.use(require("webpack-hot-middleware")(webpackCompiler, options));

      這里的 options 是 webpack-hot-middleware 的配置項,詳細見官方文檔,這里咱們只填一個必要的 path —— 它表示 webpack-hot-middleware 會在哪個路徑生成熱更新的事件流服務,且訪問的頁面會自動與這個路徑通過 EventSource 進行通訊,來拉取更新的數據重新粉飾自己。

      這里要了解下,實際上 webpack-hot-middleware 最大的能力,是讓 SERVER 能夠和 HMR 運行時進行通訊,從而對模塊進行熱更新。

      然后是 webpack.config.js 文件:

      const path = require('path');
      const webpack = require('webpack');
      module.exports = {
          entry: ['webpack-hot-middleware/client', './app.js'],  //修改點1
          output: {
              publicPath: "/assets/",
              filename: 'bundle.js'
          },
          plugins: [  //修改點2
              new webpack.HotModuleReplacementPlugin(),
              new webpack.NoEmitOnErrorsPlugin()   //出錯時只打印錯誤,但不重新加載頁面
          ]
      };

      首先是 entry 里要多加上 'webpack-hot-middleware/client',此舉是與 server 創建連接。

      接著加上兩個相關的插件來打通 webpack HMR 的任督二脈,其中的 webpack.HotModuleReplacementPlugin 我們在上一節提及過,它是 HMR 的功能提供者。

      最后一步很重要,很多新手容易漏掉。我們需要在入口文件 app.js 里加上一小段代碼:

      import {init} from './src/js/page/index';
      
      //灰常重要,知會 webpack 允許此模塊的熱更新
      if (module.hot) {
          module.hot.accept();
      }
      
      init();

      此處的 module.hot.accept() 是知會 webpack 接受此模塊的 HMR update,在上一節已經提及多次。

      補充好上述的代碼,執行 node express.config 并訪問 http://localhost:3333,之后的模塊修改,都會自動打包并更新客戶端頁面模塊:

      1.4 webpack-dev-server

      雖然 webpack-dev-middleware + webpack-hot-middleware 的組合為開發過程提供了便利,但它們僅適用于服務側開發的場景。

      很多時候我們僅僅對客戶端頁面做開發,沒有直接的 server 來提供支持,這時候就需要 webpack-dev-server 來解囊相助了。

      顧名思義,webpack-dev-server 相對前兩個工具多了個“server”,實際上它的確也是在 webpack-dev-middleware 的基礎上多套了一層殼來提供 CLI 及 server 能力(這也是為何我稱 webpack-dev-middleware 是 webpack-dev-server 他爹)

      此處依舊以一個簡單的項目來展示如何配置、使用 webpack-dev-server,你可以點這里獲取相關代碼。

      脫離了 express,我們不再需求配置后端腳本,不過對于 webpack.config.js,需要多加一個名為“devServer”的  webpack-dev-server 配置項:

      const path = require('path');
      module.exports = {
          entry: './app.js',
          output: {
              publicPath: "/assets/",
              filename: 'bundle.js'
          },
          devServer: { //新增配置項
              contentBase: path.join(__dirname, "src/html"),
              port: 3333
          }
      };

      其中 devServer.port 表示 SERVER 的監聽端口,即運行后我們可以通過 http://localhost:3333 來訪問應用;

      而 devServer.contentBase 表示 SERVER 將從哪個目錄去查找內容文件(即頁面文件,比如 HTML)

      確保安裝好 webpack-dev-server 后執行其 CLI 命令來召喚支持熱更新的 SERVER:

      webpack-dev-server

      接著訪問 http://localhost:3333,似乎便能獲得前文 webpack-dev-middleware + webpack-hot-middleware 的熱更新能力~

      不過事實并非如此,雖然在我們修改模塊后,頁面的確自動刷新了。但截止此處,webpack-dev-server 跑起來其實只相當于捎上了 SERVER 的 webpack-dev-middleware,而沒有 HMR —— 在我們修改應用模塊后,頁面是整個刷新了一遍,而并非熱更新。

      希望讀者們可以記住,HMR 提供了局部更新應用模塊的能力,而不需要刷新整個應用頁面

      這塊的驗證也很簡單,直接在 index.html 里加個 script 打印 Date.now() 即可,若刷新頁面,打印的值直接會變。 

      要讓 webpack-dev-server 加上 HMR 的翅膀,其實就得像前面 webpack-hot-middleware 的配置那樣,把 HMR 相關的東西通通加上,同時將 devServer.hot 設為 true:

      // webpack.config.js
      const path = require('path');
      const webpack = require('webpack');
      module.exports = {
          entry: './app.js',
          output: {
              publicPath: "/assets/",
              filename: 'bundle.js'
          },
          devServer: {
              contentBase: path.join(__dirname, "src/html"),
              port: 3333,
              hot: true  // 讓 dev-server 開啟 HMR
          },
          plugins: [
              new webpack.HotModuleReplacementPlugin()  //讓 webpack 啟動全局 HMR
          ]
      };
      // 入口文件 app.js
      
      import {init} from './src/js/page/index';
      if (module.hot) {
          // 知會 webpack 該模塊接受 HMR update
          module.hot.accept();
      }
      init();

      這時候,再執行 webpack-dev-server,才是正宗的有 HMR 加持的 SERVER。 

      關于完整的 devServer 配置項可參考官方文檔,在文章的最后,我們羅列幾個常用項做簡單介紹。

      1. contentBase

      即 SERVERROOT,如上方示例配置為 “path.join(__dirname, "src/html")”,后續訪問 http://localhost:3333/index.html 時,SERVER 會從 src/html 下去查找 index.html 文件。

      它可以是單個或多個地址的形式:

      contentBase: path.join(__dirname, "public")
      //多個:
      contentBase: [path.join(__dirname, "public"), path.join(__dirname, "assets")]

      若不填寫該項,默認為項目根目錄。

      2. port

      即監聽端口,默認為8080。

      3. compress

      傳入一個 boolean 值,通知 SERVER 是否啟用 gzip。

      4. hot

      傳入一個 boolean 值,通知 SERVER 是否啟用 HMR。

      5. https

      可以傳入 true 來支持 https 訪問,也支持傳入自定義的證書:

      https: true
      //也可以傳入一個對象,來支持自定義證書
      https: {
        key: fs.readFileSync("/path/to/server.key"),
        cert: fs.readFileSync("/path/to/server.crt"),
        ca: fs.readFileSync("/path/to/ca.pem"),
      }

      6. proxy

      代理配置,適用場景是,除了 webpack-dev-server 的 SERVER(SERVER A) 之外,還有另一個在運行的 SERVER(SERVER B),而我們希望能通過 SERVER A 的相對路徑來訪問到 SERVER B 上的東西。

      舉個例子:

          devServer: {
              contentBase: path.join(__dirname, "src/html"),
              port: 3333,
              hot: true,
              proxy: {
                  "/api": "http://localhost:5050"
              }
          }

      運行 webpack-dev-server 后,你若訪問 http://localhost:3333/api/user,則相當于訪問 http://localhost:5050/api/user。

      更多可行的 proxy 配置見 https://webpack.js.org/configuration/dev-server/#devserver-proxy,這里不贅述。

      7. publicPath

      如同 webpack-dev-middleware 的 publicPath 一樣,表示從內存中的哪個路徑去存放和檢索靜態文件。

      不過官方文檔有一處錯誤需要堪正 —— 當沒有配置 devServer.publicPath 時,默認的 devServer.publicPath 并非根目錄,而是 output.publicPath:

      這也是為何咱們的例子里壓根沒寫 devServer.publicPath,但還能正常請求到 https://localhost:3333/assets/bundle.js。

      8. setup

      webpack-dev-server 的服務應用層使用了 express,故可以通過 express app 的能力來模擬數據回包,devServer.setup 方法就是干這事的:

          devServer: {
              contentBase: path.join(__dirname, "src/html"),
              port: 3333,
              hot: true,
              setup(app){  //模擬數據
                  app.get('/getJSON', function(req, res) {
                      res.json({ name: 'vajoy' });
                  });
              }
          }

      然后我們可以通過請求 http://localhost:3333/getJSON 來取得模擬的數據:

      posted @ 2017-06-17 12:09  vajoy  閱讀(9024)  評論(12)    收藏  舉報
      Copyright © 2014 - 2022 VaJoy Studio
      主站蜘蛛池模板: av一区二区中文字幕| 永久无码天堂网小说区| 亚洲av日韩av永久无码电影| 亚洲av色综合久久综合| 福利在线视频一区二区| 浮妇高潮喷白浆视频| 亚洲乱码中文字幕小综合| 亚洲国产精品一区二区第一页| 亚洲a免费| 夜夜添狠狠添高潮出水| 日韩免费无码视频一区二区三区| a在线免费| 亚洲成在人线AV品善网好看| 宫西光有码视频中文字幕| 久久99国产精品尤物| 国产不卡一区二区精品| 久播影院无码中文字幕| 丁香五月激情图片| 成人午夜在线观看刺激| 国产麻花豆剧传媒精品mv在线 | 不卡一区二区三区四区视频| 四虎国产精品永久入口| 国产免费无遮挡吃奶视频| 亚洲天堂av免费在线看| 人人澡人摸人人添| 欧美午夜精品久久久久久浪潮| 激情综合色综合久久丁香| 中文国产成人精品久久不卡| 精品人妻伦九区久久aaa片69| 久久精品国产亚洲av成人| 人妻少妇偷人一区二区| 国内少妇人妻偷人精品| 成人综合婷婷国产精品久久蜜臀 | 亚洲成熟女人av在线观看| 野花香电视剧免费观看全集高清播放 | 久久久久久久波多野结衣高潮| 与子敌伦刺激对白播放| 精品人妻伦一二三区久久aaa片| 成人精品久久一区二区三区 | 亚洲精品福利一区二区三区蜜桃| 国产精品一区二区av片|