webpack 從入門到精通

小實驗

我們一步步打包一個小項目看看 webpack 是如何工作的。

  1. 先寫一個 hello.js

    function hello(messgae){
      alert(messgae);
    }
    
  2. 然後對其打包,發現終端報錯。解決後知道在 webpack 2.0 的時候,我們打包一個 js 文件可能是這樣的,比如將 hello.js 打包爲 hello.bundle.js 。

    webpack hello.js hello.bundle.js
    

    但是在現在 webpack 4.5.0 的時候就需要指定 mode 和輸出路徑

    webpack --mode=development hello.js --output-file hello.bundle.js
    

    mode 有指定的3種值, development、production、none。區別在於 development 打包出來的東西是沒壓縮的、可讀的,production 打包出來的是壓縮的、不可讀的。

  3. 然後編寫一個 world.js 和 一個 style.css ,然後進行打包

    require('./world.js');
    require('./style.css');
    
    function hello(messgae){
      alert(messgae);
    }
    hello("hello webpack");
    

    通過報錯信息知道, webpack 對於 css 文件並不是默認支持的,需要指定相應的 loader 對其打包。

  4. 所以我們繼續安裝 css-loader、style-loader。然後指定 css 文件的 loader 爲 css-loader

    require('./world.js');
    require('css-loader!./style.css');
    
    function hello(messgae){
      alert(messgae);
    }
    
    hello("hello webpack");
    
  5. 接下來設置頁面的背景顏色,發現網頁並沒有生效。這是因爲 webpack 並不知道我們的樣式如何作用到 html 中,所以我們需要指定 style-loader

    //style.css
    body,html{
      margin: 0;
      padding: 0;
    }
    
    body{
      font-size: 17px;
      background: burlywood;
    }
    
    //hello.js
    require('./world.js');
    require('style-loader!css-loader!./style.css');
    
    function hello(messgae){
      alert(messgae);
    }
    
    hello("hello webpack");
    
  6. 查看網頁效果。發現函數確實執行了,背景顏色也生效了,我們寫的 css 代碼新建了一個 style標籤 被直接寫入到 html 中了。

  7. 說說2個 loader 的作用。

    • css-loader 就是 webpack 可以處理 css 文件。
    • style-loader 的作用就是將 css-loader 處理完的文件新建一個 style 標籤插入到 html 中
  8. 很多人會想 require(‘style-loader!css-loader!./style.css’); 我每次寫一個 css 文件,那麼都需要在前面加入 style-loader、css-loader 嗎?顯然不是,webpack 還爲我們提供了簡單寫法

    require('./world.js');
    // require('style-loader!css-loader!./style.css');
    require('./style.css');
    
    function hello(messgae){
      alert(messgae);
    }
    
    hello("hello webpack");
    

    webpack-cli 寫法爲

     webpack --mode=development hello.js --output-file hello.bundle.js --module-bind 'css=style-loader!css-loader'
    
  9. 之前的做法還存在一個弊端,就是每次修改了代碼,我們都需要在終端重新運行打包命令,十分繁瑣。這裏強大的 webpack 爲我們提供了一個 option,可以監聽代碼改變然後自動打包。如下

    webpack --mode=development hello.js --output-file hello.bundle.js --module-bind 'css=style-loader!css-loader' --watch
    

    這樣,我們在源代碼每次一修改,webpack 會自動打包

  10. 如果你想看到打包過程,那麼可以使用 pregress 參數。這樣在打包的時候可以看到左下角有構件的進度

    webpack --mode=development hello.js --output-file hello.bundle.js --module-bind 'css=style-loader!css-loader' --progress
    
  11. 如果像看到打包的模塊,可以使用 –display-modules

    webpack --mode=development hello.js --output-file hello.bundle.js --module-bind 'css=style-loader!css-loader' --progress --display-modules
    

  12. 如果想知道打包某個模塊的原因,可以使用 –display-reasons

    webpack --mode=development hello.js --output-file hello.bundle.js --module-bind 'css=style-loader!css-loader' --progress --display-modules --display-reasons
    

用 webpack.config.js 完成上述步驟

  1. 初始化項目,編輯 webpack.config.js

    var path = require('path');
    
    module.exports = {
      entry: './src/script/main.js',
      output:{
        path:  path.resolve(__dirname,'./dist/js'),
        filename: 'bundle.js'
      }
    }
    
  2. 有了 webpack.config.js 文件,就不需要和上面的方式一樣,指定對應的 configuration option。在終端運行 **webpack --mode=development **

  3. 注意:如果我們將 webpack.config.js 改名爲 webpack.dev.config.js ,然後在命令行打包,會發現沒效果。

    要將 webpack.dev.config.js 同樣生效,我們需要在命令行使用下面命令。

    webpack --mode=development --config webpack.dev.config.js
    

  4. 如果想像上個實驗一樣,看到打包時候的一些信息,怎麼辦呢?

    可以配合 npm 的 package.json 文件中的 scripts 標籤,在下面添加 key 爲 webpack 的項,然後將命令寫到後邊。然後在命令行運行 npm run webpack

    "scripts": {
       "test": "echo \"Error: no test specified\" && exit 1",
         "webpack": "webpack --config webpack.config.js --mode=development --progress --display-modules --display-reasons --colors"
       },
    

  5. 對於 webpack 的 entrt 主要有3種寫法,每種寫法都有不同區別。

    • 如果 webpack 只有單一入口,那麼就可以是字符串。

      entry: './src/script/main.js',
      
    • 如果 webpack 有多個入口,那麼就可以是數組。

      entry: ['./src/script/main.js','./src/script/a.js'],
      
    • 如果 webpack 有多個入口,那麼可以用對象。

       entry: {
           main: './src/script/main.js',
           a : './src/script/a.js'
        },
      
  6. 如果指定了多個入口,那麼執行打包會報錯,因爲 webpack 文檔說如果多個 entry,且只有一個 output 的 filename,那麼打包的結果會覆蓋。所以我們需要設置如下

    When combining with the output.library option: If an array is passed only the last item is exported.

    var path = require('path');
    
    module.exports = {
      entry: {
         main: './src/script/main.js',
         a : './src/script/a.js'
      },
      output:{
        path:  path.resolve(__dirname,'./dist/js'),
        filename: '[name]-[hash].js'
      }
    }
    

    將文件修改爲 filename: '[name]-[chunkhash].js’

    會發現 hash 和 chunkhash 的輸出的文件名並不一樣

    說明: chunkhash 是根據文件的內容生成的唯一標示(類似於md5生成的唯一標示、文件版本號)。如果一個資源在打包前後文本沒有變過的話,二次打包的生成的 chunkhash 是一致的。

生成項目中 html 頁面文件

對於生成的的 js,我們 html 如何使用呢?難道每次一打包,html 中的 script 需要修改 src 嗎?不是的,webpack 提供了 html-webpack-plugin

  1. 安裝

    npm install html-webpack-plugin --save-dev
    
  2. 然後運行命令,將現有的 js 打包引入到 html 文件中

    var htmlWebpackPlugin = require('html-webpack-plugin');
    //...
    plugins: [
        new htmlWebpackPlugin()
      ]
    

    然後生成的文件是 webpack 幫我們生成的 html 文件。當然我們可以新建一個自己的 html 作爲模版。

     plugins: [
        new htmlWebpackPlugin({
          template: 'index.html'
        })
      ]
    
    //模版
    <html>
      <head>
        <titile>webpack demo</titile>
      </head>
      <body>
        <h2>我是 webpack 生成 html 的模版</h2>
      </body>
      <script type="text/javascript" src="./dist/js/bundle.js" ></script>
    </html>
    //打包生成的
    <html>
      <head>
        <titile>webpack demo</titile>
      </head>
      <body>
        <h2>我是 webpack 生成 html 的模版</h2>
      <script type="text/javascript" src="main-4166ec84d15023f1cd10.js"></script><script type="text/javascript" src="a-c13b6c7a7bfe7bd0293e.js"></script></body>
      <script type="text/javascript" src="./dist/js/bundle.js" ></script>
    </html>
    

    說明:上面選中的 template 寫了 index.html 就會找到合適的文件是因爲 webpack 有個上下文參數 context,會根據上下文找到對應的 html(這裏就是根目錄)

  3. 上述的缺點是生成的 html 也會放在 dist/js 目錄下。

    [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-0rhV8wwB-1585763543195)(https://github.com/FantasticLBP/knowledge-kit/raw/master/[email protected])]

    需要做到的效果就是 html 放在根目錄, js 放在 dist 目錄下的 js 目錄下。需要對 webpack.config.js 的 output 屬性做修改

  4. output:{
       path:  path.resolve(__dirname,'./dist'),
       filename: 'js/[name]-[chunkhash].js'
    },
    
  5. plugins 的參數很多可以自定義

    plugins: [
        new htmlWebpackPlugin({
          template: 'index.html',
          filename: 'index-[hash].html',
          inject: 'head'
        })
      ]
    
    • template: 指定生成 html 的模版
    • filename:指定生成 html 的命名規則
    • inject :指定生成 js 的 script 插入的位置。head、body
  6. 如果想通過 plugins 傳值到 生成的 html,怎麼辦?

    • htmlWebpackPlugin.options 對象就可以拿到傳遞過來的值
    • <%= htmlWebpackPlugin.options.title %> 模版語法來拿值
    //webpack.config.js
    plugins: [
        new htmlWebpackPlugin({
          template: 'index.html',
          filename: 'index.html',
          inject: 'head',
          title: 'Webpack is awesome',
          date : new Date()
        })
      ]
    
    // 模版 html
    <html>
      <head>
        <titile><%= htmlWebpackPlugin.options.title %></titile>
      </head>
      <body>
        <h2>我是 webpack 生成 html 的模版</h2>
        <h3>時間:<%= htmlWebpackPlugin.options.date %></h3>
      </body>
    </html>
    
    //生成的html
    <html>
      <head>
        <titile>Webpack is awesome</titile>
      <script type="text/javascript" src="js/main-82c7521f0a4a776cc00b.js"></script><script type="text/javascript" src="js/a-273641522fd044fc27c7.js"></script></head>
      <body>
        <h2>我是 webpack 生成 html 的模版</h2>
        <h3>時間:Thu Aug 02 2018 15:40:50 GMT+0800 (CST)</h3>
      </body>
    </html>
    
  7. 我們很好奇 html-webpack-plugin 可以傳遞什麼參數?或者這個對象包含什麼信息。做個測試就知道了

    //模版 html
    <% for (var key in htmlWebpackPlugin){  %>
    <%= key %>  
    <% } %>
    
    //生成的 html
    files    
    options
    

    看到最外層的節點就2個:files、options。那麼我們分別對這2個節點遍歷輸出。因爲遍歷出的 value (**htmlWebpackPlugin.files[key] **)可能是對象、數組。所以用 JSON.Stringfy(htmlWebpackPlugin.files[key]) 打印

    <html>
      <head>
        <titile><%= htmlWebpackPlugin.options.title %></titile>
      </head>
      <body>
        <h2>我是 webpack 生成 html 的模版</h2>
        <h3>時間:<%= htmlWebpackPlugin.options.date %></h3>
        <% for (var key in htmlWebpackPlugin.files){  %>
        <%= key %>   <%=  JSON.stringify(htmlWebpackPlugin.files[key])  %>
        <% } %>
        <hr>
        <% for (var key in htmlWebpackPlugin.options){  %>
        <%= key %>   <%=  JSON.stringify(htmlWebpackPlugin.options[key])  %>
        <% } %>
      </body>
    </html>
    
    //生成的 html
            <html>
      <head>
        <titile>Webpack is awesome</titile>
      <script type="text/javascript" src="js/main-82c7521f0a4a776cc00b.js"></script><script type="text/javascript" src="js/a-273641522fd044fc27c7.js"></script></head>
      <body>
        <h2>我是 webpack 生成 html 的模版</h2>
        <h3>時間:Thu Aug 02 2018 15:51:27 GMT+0800 (CST)</h3>
    
        publicPath   ""
    
        chunks   {"main":{"size":28,"entry":"js/main-82c7521f0a4a776cc00b.js","hash":"82c7521f0a4a776cc00b","css":[]},"a":{"size":18,"entry":"js/a-273641522fd044fc27c7.js","hash":"273641522fd044fc27c7","css":[]} }
    
        js   ["js/main-82c7521f0a4a776cc00b.js","js/a-273641522fd044fc27c7.js"]
    
        css   []
    
        manifest   
    
        <hr>
    
        template   "/Users/liubinpeng/Desktop/webpackdemo/Demo2/node_modules/html-webpack-plugin/lib/loader.js!/Users/liubinpeng/Desktop/webpackdemo/Demo2/index.html"
    
        templateParameters   
    
        filename   "index.html"
    
        hash   false
    
        inject   "head"
    
        compile   true
    
        favicon   false
    
        minify   false
    
        cache   true
    
        showErrors   true
    
        chunks   "all"
    
        excludeChunks   []
    
        chunksSortMode   "auto"
    
        meta   {}
    
        title   "Webpack is awesome"
    
        xhtml   false
    
        date   "2018-08-02T07:51:27.110Z"
    
      </body>
    </html>
    
  8. 有時候我們想把部分 js 放到 head ,部分 js 放到 body 中。單獨通過 webpack.config.js 是沒辦法實現這個目的,結合上面的成果,我們可以拿到 htmlWebpackPlugin.files.chunks 屬性,比如將 a.js 放到 head 標籤,main.js 放到 body 標籤。

    plugins: [
        new htmlWebpackPlugin({
          template: 'index.html',
          filename: 'index.html',
          inject: false,
          title: 'Webpack is awesome',
          date : new Date()
        })
      ]
    
    //模版 html
    <html>
      <head>
        <titile><%= htmlWebpackPlugin.options.title %></titile>
        <script type="text/javascript" src="<%= htmlWebpackPlugin.files.chunks.a.entry %>"></script>
      </head>
      <body>
        <h2>我是 webpack 生成 html 的模版</h2>
        <h3>時間:<%= htmlWebpackPlugin.options.date %></h3>
        <script type="text/javascript" src="<%= htmlWebpackPlugin.files.chunks.a.entry %>"></script>
      </body>
    </html>
    
    //生成 html
    <html>
      <head>
        <titile>Webpack is awesome</titile>
        <script type="text/javascript" src="js/a-273641522fd044fc27c7.js"></script>
      </head>
      <body>
        <h2>我是 webpack 生成 html 的模版</h2>
        <h3>時間:Thu Aug 02 2018 15:58:56 GMT+0800 (CST)</h3>
        <script type="text/javascript" src="js/a-273641522fd044fc27c7.js"></script>
      </body>
    </html>
    

    需要注意的是當自定義 js 文件的位置的時候,需要將 webpack.config.js 中 plugins 下的 inject 設置爲 false

  9. 接下來看到的這種需求絕對很有料。前面我們看到的都是相對路徑,但是我們的產品需要上線,所以我們的 js 文件資源路徑需要改變。如下:

    //webpack.config.js
    output:{
        path:  path.resolve(__dirname,'./dist'),
        filename: 'js/[name]-[chunkhash].js',
        publicPath: 'http://test.lbp.com'
      },
    
    //生成的 html
    <html>
      <head>
        <titile>Webpack is awesome</titile>
        <script type="text/javascript" src="http://test.lbp.com/js/a-502c14d352874172253c.js"></script>
      </head>
      <body>
        <h2>我是 webpack 生成 html 的模版</h2>
        <h3>時間:Thu Aug 02 2018 16:25:12 GMT+0800 (CST)</h3>
        <script type="text/javascript" src="http://test.lbp.com/js/a-502c14d352874172253c.js"></script>
      </body>
    </html>
    
  10. 利用 webpack 我們還可以打包好的 html 做一些優化,比如刪除註釋、去掉空格.

    修改 webpack.config.js 中 plugins 節點下的 htmlWebpackPlugin 的 minify 屬性

    plugins: [
        new htmlWebpackPlugin({
          template: 'index.html',
          filename: 'index.html',
          inject: false,
          title: 'Webpack is awesome',
          date : new Date(),
          minify:{
            removeComments: true,
            collapseWhitespace: true
          }
        })
      ],
    

    我們對 模版 html 寫一些註釋,運行 npm run webpack 後看到生成的頁面中註釋、空格都被去掉了。

  11. 如果想打包生成多個 html 怎麼辦?可能使用 plugins 下的 new htmlWebpackPlugin() 多來幾組配置項

    plugins: [
       new htmlWebpackPlugin({
         template: 'index.html',
         filename: 'a.html',
         title: 'this is a.html',
         chunks: ['main','a'],
         inject: 'body'
       }),
       new htmlWebpackPlugin({
         template: 'index.html',
         filename: 'b.html',
         title: 'this is b.html',
         chunks: ['main','b'],
         inject: 'body'
       }),
       new htmlWebpackPlugin({
         template: 'index.html',
         filename: 'c.html',
         title: 'this is c.html',
         chunks: ['main','c'],
         inject: 'body'
       })
     ]
    

    注意:這裏我們可以指定每個生成的 filename 以及 title。實現上述需求關鍵點在於 chunks 這個屬性。用一個數組的形式來指定需要引用的 chunk。

  12. 上面只是實現了 a、b、c 3個頁面,如果多了的話按照上面的寫法要煩死人的。 webpack 爲我們提供了 excludeChunks 這個屬性,它指定了不需要包含的chunk。上面寫法的另一種寫法

    plugins: [
       new htmlWebpackPlugin({
         template: 'index.html',
         filename: 'a.html',
         title: 'this is a.html',
         // chunks: ['main','a'],
         inject: 'body',
         excludeChunks: ['b','c']
       }),
       new htmlWebpackPlugin({
         template: 'index.html',
         filename: 'b.html',
         title: 'this is b.html',
         //chunks: ['main','b'],
         inject: 'body',
         excludeChunks: ['a','c']
       }),
       new htmlWebpackPlugin({
         template: 'index.html',
         filename: 'c.html',
         title: 'this is c.html',
         // chunks: ['main','c'],
         inject: 'body',
         excludeChunks: ['a','b']
       })
     ],
    
  13. 有種需求是:當我們需要減小首頁 HTTP 請求(提高首頁的渲染速度),也就是將一些首頁必須用到的 JS 文件用內聯的方式寫在首頁 html 的 script 標籤裏面,不重要的 js 文件通過 script 的 src 引入。要怎麼做呢?webpack 在設計之初沒想到這種需求,很多人在 github 提了很多 issue ,官方認識到這種需求,所以在後期更新的 demo 中看到了解決方案。

    • htmlWebpackPlugin.files.chunks 對象拿到的是我們在 webpack.config.js 設置過 publicPath 生成的完整路徑

    • 通過截取字符串子串的方式拿到文件地址。 **htmlWebpackPlugin.files.chunks.main.entry.substr(htmlWebpackPlugin.files.publicPath.length) **

    • 官方的解決方案

      compilation.assets[jsFile.substr(htmlWebpackPlugin.files.publicPath.length)].source()
      

    在我們的項目中加以改造,爲生成的每個頁面的 header 裏面加入 main.js。在 body 部分加入除了 main.js 之外的其他 js。

    //模版html
    <html>
     <head>
       <title><%= htmlWebpackPlugin.options.title %></title>
       <script type="text/javascript">
      <%= compilation.assets[htmlWebpackPlugin.files.chunks.main.entry.substr(htmlWebpackPlugin.files.publicPath.length)].source() %>
    
       </script>
    
     </head>
     <body>
       <h2>我是 webpack 生成 html 的模版</h2>
       <% for(var key in htmlWebpackPlugin.files.chunks){ %>
         <% if( key !== 'main'){ %>
           <script type="text/javascript" src="<%= htmlWebpackPlugin.files.chunks[key].entry %>"></script>
         <% } %>
       <% } %>
     </body>
    </html>
    
    //webpack.config.js
    var htmlWebpackPlugin = require('html-webpack-plugin');
    var path = require('path');
    
    module.exports = {
     // entry: './src/script/main.js', 
     // entry: ['./src/script/main.js','./src/script/a.js'],
     entry: {
        main: './src/script/main.js',
        a : './src/script/a.js',
        b : './src/script/b.js',
        c : './src/script/c.js'
     },
     output:{
       path:  path.resolve(__dirname,'./dist'),
       filename: 'js/[name]-[hash].js',
       publicPath: 'http://test.lbp.com'
     },
     plugins: [
       new htmlWebpackPlugin({
         template: 'index.html',
         filename: 'a.html',
         title: 'this is a.html',
         inject: false,
         excludeChunks: ['b','c']
       }),
       new htmlWebpackPlugin({
         template: 'index.html',
         filename: 'b.html',
         title: 'this is b.html',
         inject: false,
         excludeChunks: ['a','c']
       }),
       new htmlWebpackPlugin({
         template: 'index.html',
         filename: 'c.html',
         title: 'this is c.html', 
         inject: false,
         excludeChunks: ['a','b']
       })
     ],
    }
    

    爲了驗證生效,我將 main.js 加入了 alert

    //main.js
    function helloworld(msg){
      alert(msg);
    }
    
    helloworld("hello webpack");
    

    看看打包後生成的 a.html

    <html>
     <head>
       <title>this is a.html</title>
       <script type="text/javascript">
      /******/ (function(modules) { // webpackBootstrap
    /******/     // The module cache
    /******/     var installedModules = {};
    /******/
    /******/     // The require function
    /******/     function __webpack_require__(moduleId) {
    /******/
    /******/         // Check if module is in cache
    /******/         if(installedModules[moduleId]) {
    /******/             return installedModules[moduleId].exports;
    /******/         }
    /******/         // Create a new module (and put it into the cache)
    /******/         var module = installedModules[moduleId] = {
    /******/             i: moduleId,
    /******/             l: false,
    /******/             exports: {}
    /******/         };
    /******/
    /******/         // Execute the module function
    /******/         modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
    /******/
    /******/         // Flag the module as loaded
    /******/         module.l = true;
    /******/
    /******/         // Return the exports of the module
    /******/         return module.exports;
    /******/     }
    /******/
    /******/
    /******/     // expose the modules object (__webpack_modules__)
    /******/     __webpack_require__.m = modules;
    /******/
    /******/     // expose the module cache
    /******/     __webpack_require__.c = installedModules;
    /******/
    /******/     // define getter function for harmony exports
    /******/     __webpack_require__.d = function(exports, name, getter) {
    /******/         if(!__webpack_require__.o(exports, name)) {
    /******/             Object.defineProperty(exports, name, { enumerable: true, get: getter });
    /******/         }
    /******/     };
    /******/
    /******/     // define __esModule on exports
    /******/     __webpack_require__.r = function(exports) {
    /******/         if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
    /******/             Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
    /******/         }
    /******/         Object.defineProperty(exports, '__esModule', { value: true });
    /******/     };
    /******/
    /******/     // create a fake namespace object
    /******/     // mode & 1: value is a module id, require it
    /******/     // mode & 2: merge all properties of value into the ns
    /******/     // mode & 4: return value when already ns object
    /******/     // mode & 8|1: behave like require
    /******/     __webpack_require__.t = function(value, mode) {
    /******/         if(mode & 1) value = __webpack_require__(value);
    /******/         if(mode & 8) return value;
    /******/         if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
    /******/         var ns = Object.create(null);
    /******/         __webpack_require__.r(ns);
    /******/         Object.defineProperty(ns, 'default', { enumerable: true, value: value });
    /******/         if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
    /******/         return ns;
    /******/     };
    /******/
    /******/     // getDefaultExport function for compatibility with non-harmony modules
    /******/     __webpack_require__.n = function(module) {
    /******/         var getter = module && module.__esModule ?
    /******/             function getDefault() { return module['default']; } :
    /******/             function getModuleExports() { return module; };
    /******/         __webpack_require__.d(getter, 'a', getter);
    /******/         return getter;
    /******/     };
    /******/
    /******/     // Object.prototype.hasOwnProperty.call
    /******/     __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
    /******/
    /******/     // __webpack_public_path__
    /******/     __webpack_require__.p = "http://test.lbp.com";
    /******/
    /******/
    /******/     // Load entry module and return exports
    /******/     return __webpack_require__(__webpack_require__.s = "./src/script/main.js");
    /******/ })
    /************************************************************************/
    /******/ ({
    
    /***/ "./src/script/main.js":
    /*!****************************!*\
     !*** ./src/script/main.js ***!
     \****************************/
    /*! no static exports found */
    /***/ (function(module, exports) {
    
    eval("function helloworld(msg){\n   alert(msg);\n}\n\n\nhelloworld(\"hello webpack\");\n\n//# sourceURL=webpack:///./src/script/main.js?");
    
    /***/ })
    
    /******/ });
    
       </script>
    
     </head>
     <body>
       <h2>我是 webpack 生成 html 的模版</h2>
           <script type="text/javascript" src="http://test.lbp.com/js/a-4caa8a542ab497f63bf8.js"></script>
     </body>
    </html>
    

    在瀏覽器調試後發現頁面是可以正常彈出“hello webpack”

loader

新建項目,一步步認識 loader

  1. Js loader

    我們寫的項目中會用 es6,但是並不是所有的瀏覽器都支持 es6(雖然各個瀏覽器廠商每年在不斷新增對 es6 的支持),所以我們需要使用 babel 將 es6 轉換爲瀏覽器都支持的 es2015 。所以使用 babel-loader 的時候需要指定 babel 轉換的模式。loader 官方給出了2種方式

    • 可以直接像 url 的 get 形式一樣,將參數傳遞在後面。

      //方式1
      require("babel-loader?presets=latest");
      //方式2
      {
          test: /\.png$/,
          loader: 'url-loader?presets=latest'
      }
      
    • 寫在 query 參數裏面

      {
            test: /\.js$/,
            loader: 'babel',
            query: {
              presets: ['latest']
            }
          }
      
    • 其實還有一種方式:在 package.json 文件裏面添加一個 key 爲 babel。

      "babel": {
          presets: ['latest']
      }
      

    注意:webpack 現在已經是4.5.0了。以前的版本的寫法是

    module: {
        loaders: [
          {
            test: /\.js$/,
            loader: 'babel-loader',
            query: {
              presets: ['latest']
            }
          }
        ]
      },
    

    現在的寫法爲

    module: {
        rules: [
          {
            test: /\.js$/,
            loader: 'babel-loader',
            options: {
              presets: ['latest']
            }
          }
        ]
      },
    

    注意:我們工程如果安裝的依賴非常多,node_modules 文件非常多,babel 轉換會很慢,這時候需要指定2個參數可以顯著提高速度

    module: {
        rules: [
          {
            test: /\.js$/,
            loader: 'babel-loader',
            options: {
              presets: ['latest']
            },
            exclude: path.resolve(__dirname,'./node_modules/'),
            include: path.resolve(__dirname,'./src')
          }
        ]
      },
    
  2. css loader

    打包 css 經常會用到 css-loader、style-loader。我們經常寫 flex 的時候很多瀏覽器兼容性不一致,所以我們需要加前綴,這時候需要使用 postcss-loader、autoprefixer

    官網給出2種寫法

    • loader

      {
         test: /\.css$/,
         loader: 'style-loader!css-loader!postcss-loader'
      }
      
    • loaders

      {
         test: /\.css$/,
         loaders: ['style-loader','css-loader','postcss-loader']
       }
      

    如果項目中不只是使用了 css 的話,比如還使用了 less 和 sass 的話,我們需要將 css 加額外的設置

    {
          test: /\.css$/,
          use:[
            'style-loader',
            {loader: 'css-loader', options: {importLoaders: 1} },
            {
              loader: 'postcss-loader',
              options:{
                plugins:function(){
                  return [
                    require('postcss-import')(),
                    require('autoprefixer')({browsers:['last 5 versions']})
                  ]
                }
              }
            }
          ]
        },
        {
          test: /\.less$/,
          use:[
            'style-loader',
            {loader: 'css-loader', options: {importLoaders: 1} },
            {
              loader: 'postcss-loader',
              options:{
                plugins:function(){
                  return [
                    require('postcss-import')(),
                    require('autoprefixer')({browsers:['last 5 versions']})
                  ]
                }
              }
            },
            'less-loader'
          ]
        },
    
  3. 處理模版

    在 webpack 經常打包處理的時候會遇到模版。有普通的 html 模版,也會有 ejs 模式下的 tpl 模版

    • html 模版
    <html>
      <head>
        <title><%= htmlWebpackPlugin.options.title %></title>
    
      </head>
      <body>
        <div id="app">
    
        </div>
      </body>
    </html>
    
    //layer.js
    import './layer.less'
    import tpl from './layer.html'
    
    function layer(){
      return {
        name: 'layer',
        tpl: tpl
      }
    }
    
    export default layer;
    
    //app.js
    import Layer from './components/layer/layer.js';
    import './css/common.css';
    
    
    const App = function(){
      var dom = document.getElementById("app");
      var layer = new Layer();
      dom.innerHTML = layer.tpl;
    }
    
    new App()
    

    • ejs 模版

      //layer.tpl
      <div class="layer">
        <div>this is <%= name %> layr</div>
        <% for(var i=0;i < arr.length; i++){ %>
          <%= arr[i] %>
        <% } %>
      
      </div>
      
      //layer.js
      import './layer.less'
      import tpl from './layer.tpl'
      
      function layer(){
        return {
          name: 'layer',
          tpl: tpl
        }
      }
      
      export default layer;
      
      
      //app.js
      import Layer from './components/layer/layer.js';
      import './css/common.css';
      
      
      const App = function(){
        var dom = document.getElementById("app");
        var layer = new Layer();
        dom.innerHTML = layer.tpl({
          name: 'john',
          arr: ['swift','Objective-C','JS','python']
        });
      }
      
      new App()
      

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章