webpack啟蒙教學

自我介紹環(huán)節(jié)

webpack 是一個現(xiàn)代 JavaScript 應用程序的模塊打包器(module bundler) 。當 webpack 處理應用程序時,它會遞歸地構(gòu)建一個依賴關系圖(dependency graph),其中包含應用程序需要的每個模塊,然后將所有這些模塊打包成少量的 bundle(通常只有一個,由瀏覽器加載)。

webpack.png

如上是webpack官網(wǎng)上的簡介。從中我們可以看出webpack是一個模塊打包器,它獲取帶依賴的模塊并產(chǎn)生出與這些模塊相對于的靜態(tài)資源。然而,自從發(fā)布后,webpack逐漸發(fā)展成為所有前端代碼的管理工具。

為什么使用Webpack

分塊轉(zhuǎn)換的想法
模塊要能夠在客戶端中去執(zhí)行,則必須將它們通過請求從server端下載到browser端。通常一個請求對應一個模塊,只有需要的模塊會被轉(zhuǎn)換。當所有資源都在一個模塊中時,不需要的模塊也會被轉(zhuǎn)換,這樣就顯得很浪費資源和時間。將眾多的模塊切成許多片,在初始化時的請求不會包括完整的代碼,并且在初始化時不需要的模塊切片會在后續(xù)加載過程中按需加載。并且將模塊化的切片方式是可以由開發(fā)人員自己定義的。

常用的模塊系統(tǒng)解決方案:
一、<script>標簽類型
最原汁原味的腳本引入方案,缺點如下(請不要問我為什么不介紹優(yōu)點):

  • 全局作用域下造成變量的沖突;
  • 文件加載順序很重要;
  • 模塊與模塊之間的依賴要解決;
  • 在大型項目中難以維護和管理;

二、CommonJs
該規(guī)范的核心思想是允許模塊通過require方法來同步加載所要依賴的其他模塊,然后通過exportsmodule.exports來導出需要暴露的接口。

require("module");
require("../file.js");
exports.doStuff = function() {};
module.exports = someValue;

優(yōu)點:

  • 服務端模塊能夠重復利用;
  • 有優(yōu)秀的包管理工具npm;
  • 簡單,上手容易;

缺點:

  • 同步的模塊加載方式不適合在瀏覽器環(huán)境中,同步意味著阻塞加載,瀏覽器資源是異步加載的;
  • 不能非阻塞的并行加載多個模塊;

三、AMD
由于瀏覽器端的模塊不能采用同步的方式加載,會影響后續(xù)模塊的加載執(zhí)行,因此AMD(Asynchronous Module Definition異步模塊定義)規(guī)范誕生了。

define("module", ["dep1", "dep2"], function(d1, d2) {
  return someExportedValue;
});
require(["module", "../file"], function(module, file) { /* ... */ });

require接口用來加載一系列模塊,define接口用來定義并暴露一個模塊。
優(yōu)點:

  • 適合瀏覽器的異步加載機制;
  • 并行加載模塊;

缺點:

  • 提高了開發(fā)成本,代碼的閱讀和書寫比較困難,模塊定義方式的語義不順暢;

四、CMD
CMD(Common Module Definition)規(guī)范和AMD很相似,盡量保持簡單,并與CommonJS和Node.js的 Modules 規(guī)范保持了很大的兼容性。在CMD規(guī)范中,一個模塊就是一個文件。

define(function(require, exports, module) {
  var $ = require('jquery');
  var Spinning = require('./spinning');
  exports.doSomething = ...
  module.exports = ...
})

優(yōu)點:

  • 依賴就近,延遲執(zhí)行;
  • 可以很容易在 Node.js 中運行;

缺點:

  • 依賴 SPM 打包,模塊的加載邏輯偏重;

五、ES6
EcmaScript6標準增加了JavaScript語言層面的模塊體系定義。在 ES6 中,我們使用export關鍵字來導出模塊,使用import關鍵字引用模塊。需要說明的是,ES6的這套標準和目前的標準沒有直接關系,目前也很少有JS引擎能直接支持。

import "jquery";
export function doStuff() {}
module "localModule" {}

優(yōu)點:

  • 未來的ES規(guī)范;

缺點:

  • 瀏覽器對ES6的支持還需要一段時間;
  • 能夠依賴的現(xiàn)有的模塊少;

webpack的目標如下:

  • 拆分依賴樹成塊并按需加載;
  • 讓初始化加載時間更少;
  • 每一個靜態(tài)資源應該是一個模塊;
  • 能夠集成第三方類庫;
  • 適用于大型項目;
  • 能夠定制模塊打包的每一個部分;

webpack較之其他類似工具有什么不同?

  • 有同步和異步兩種不同的加載方式;
  • 加載器(Loaders)可以將其他資源整合到JS文件中;
  • 支持 CommonJs AMD 規(guī)范有豐富的開源插件庫,可以根據(jù)自己的需求自定義webpack的配置

眾所周知作為一個SEO的小TIPS,瀏覽器加載的資源越少,響應的速度也就越快,所以有時候我們通常會盡可能的將資源合并到一個主文件app.js里面。但有時候也會適得其反:當項目十分龐大的時候,不同的頁面不能做到按需加載,而是將所有的資源一并加載,耗費時間長,性能降低。會導致依賴庫之間關系的混亂,特別是大型項目時,會變得難以維護和跟蹤。

雖然webpack也會將所有資源放在一個文件里面,但是webpack可以很好的解決以上缺點,因為它是一個十分聰明的模塊打包系統(tǒng),當你正確配置后,它會比你想象中的更強大,更優(yōu)秀。

webpack工作流程.png

核心概念

入口(Entry)
webpack 創(chuàng)建應用程序所有依賴關系圖的起點。入口告訴 webpack 從哪里開始,并根據(jù)依賴關系圖確定需要打包的內(nèi)容??梢詫贸绦虻?em>入口認為是根上下文(contextual root)app 第一個啟動文件。參見如下示例:

// 單入口
module.exports = {
  entry: {
     main: './src/main.js'
  }
}

// 多入口
module.exports = {
  entry: {
     app: ["./home.js", "./events.js"]
  }
}

// 多入口,app(應用主入口),vendors(公共庫)
module.exports = {
  entry: {
     app: './src/main.js',
     vendors: './src/vendors.js'
  }
}

出口(Output)
將所有的資源(assets)歸攏在一起后,還需要告訴 webpack 在哪里打包應用程序。webpack 的 output 屬性描述了如何處理歸攏在一起的代碼(bundled code)。

const path = require('path');
module.exports = {
  output: {
    // 打包輸出的目錄,這里是絕對路徑,必選設置項
    path: path.resolve(__dirname, './dist'),
    // 資源基礎路徑
    publicPath: '/dist/',
    // 打包輸出的文件名,也可以設置為[name].js,動態(tài)對應入口的定義
    filename: 'my-first-bundle.js' 
  }
}

加載器(Loader)
webpack 會把所有引用到的資源(.css, .html, .scss, .jpg, etc.) 都作為模塊處理。然而 webpack 自身只理解 JavaScript。webpack loader 在文件被添加到依賴圖中時,將其轉(zhuǎn)換為模塊。在 webpack 的配置中 loader 有兩個目標:

  • 識別出(identify)應該被對應的 loader 進行轉(zhuǎn)換(transform)的那些文件;
  • 轉(zhuǎn)換這些文件(test 屬性),從而使其最終添加到 bundle 中(use 屬性);

示例如下:

module.exports = {
  module: {
    rules: [
      {
        // 這是個正則表達式
        test: /\.jsx$/,
        // 指定loader
        loader: 'babel-loader'
      },
      {
        test: /\.css$/,
        // 指定多個loader
        use: [
          'css-loader',
          'style-loader'
        ]
      }
    ]
  }
}

插件(Plugins)
loader僅在每個文件的基礎上執(zhí)行轉(zhuǎn)換,而插件(plugins) 更常用于在打包模塊的 生命周期執(zhí)行操作和自定義功能。webpack 的插件系統(tǒng)極其強大和可定制化。想要使用一個插件,你只需要 require() 它,然后把它添加到 plugins 數(shù)組中。多數(shù)插件可以通過選項(option)自定義。你也可以在一個配置文件中因為不同目的而多次使用同一個插件,這時需要通過使用 new 來創(chuàng)建它的一個實例。

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

// 應用插件
const path = require('path');
const config = { 
    main: './src/main.js',
    output: { 
        path: path.resolve(__dirname, 'dist'), 
        filename: 'my-first-bundle.js' 
    }, 
    module: { 
        rules: [ 
            { test: /\.txt$/, use: 'raw-loader' } 
        ] 
    }, 
    plugins: [ 
        // 壓縮JS
        new webpack.optimize.UglifyJsPlugin(), 
        // 生成HTML
        new HtmlWebpackPlugin({template: './src/index.html'}) ,
        // 分離入口文件vendor作為單獨的模塊
        new webpack.optimize.CommonsChunkPlugin({
           name: 'vendor',
           filename: 'vendor.js'
        })
    ]
};
module.exports = config;

完整配置文件DEMO

**webpack.config.js**

var path = require('path'),
webpack = require('webpack'),
ExtractTextPlugin = require('extract-text-webpack-plugin');

module.exports = {
  devtool: 'eval',
  entry: {
    main: './src/main.js'
  },
  resolve: {
    // 自動解析確定的擴展
    extensions: ['.js', '.jsx'],
    // 告訴 webpack 解析模塊時應該搜索的目錄
    modules: [
      path.resolve(__dirname, 'src'),
      'node_modules'
    ],
    alias: {
      'src': path.resolve(__dirname, './src')
    }
  },
  output: {
    // 打包輸出的目錄,這里是絕對路徑,必選設置項
    path: path.resolve(__dirname, './dist'),
    // 資源基礎路徑
    publicPath: '/dist/',
    // 打包輸出的文件名
    filename: 'build.js'
  },
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        exclude: /node_modules/,
        loader: 'babel-loader',
        options: {
          cacheDirectory: true
        }
      },
      {
        test: /\.css$/,
        /*
        use: [
          'css-loader',
          'style-loader'
        ]
        */
        use: ExtractTextPlugin.extract({
          fallback: 'style-loader',
          use: 'css-loader?minimize'
        })
      },
      // 支持less
      {
        test: /\.less$/,
        use: ExtractTextPlugin.extract({
          fallback: 'style-loader',
          use: [
            { loader: "css-loader?minimize" },
            { loader: "less-loader" }
          ]
        })
      },
      {
        // 處理圖片文件
        test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 7186, // inline base64 if <= 7K
          name: 'static/images/[name].[ext]'
        }
      },
      {
        // 處理字體文件
        test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 7186, // inline base64 if <= 7K
          name: 'static/fonts/[name].[ext]'
        }
      }
    ]
  },
  plugins: [
    // https://webpack.github.io/docs/list-of-plugins.html#uglifyjsplugin
    new webpack.optimize.UglifyJsPlugin({
      compress: {
        warnings: false
      }
    }),
    new ExtractTextPlugin({ filename: 'static/css/app.css', allChunks: true })
  ]
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • 版權(quán)聲明:本文為博主原創(chuàng)文章,未經(jīng)博主允許不得轉(zhuǎn)載。 webpack介紹和使用 一、webpack介紹 1、由來 ...
    it筱竹閱讀 11,444評論 0 21
  • GitChat技術(shù)雜談 前言 本文較長,為了節(jié)省你的閱讀時間,在文前列寫作思路如下: 什么是 webpack,它要...
    蕭玄辭閱讀 12,869評論 7 110
  • 無意中看到zhangwnag大佬分享的webpack教程感覺受益匪淺,特此分享以備自己日后查看,也希望更多的人看到...
    小小字符閱讀 8,359評論 7 35
  • 最近在學習 Webpack,網(wǎng)上大多數(shù)入門教程都是基于 Webpack 1.x 版本的,我學習 Webpack 的...
    My_Oh_My閱讀 8,320評論 40 247
  • 書,不知要從何時說起。對于書的情感個人的情感很淡,上學的那些年并不是能沉得住氣的人,都說看書的人能耐得住寂寞,在現(xiàn)...
    Zachary鴹羽閱讀 201評論 0 0

友情鏈接更多精彩內(nèi)容