從 iOS 到大前端(1)- Webpack 入門

webpack 是什么?

官方文檔里的定義:

webpack 是一個現代 JavaScript 應用程序的靜態模塊打包器(module bundler)。當 webpack 處理應用程序時,它會遞歸地構建一個依賴關系圖(dependency graph),其中包含應用程序需要的每個模塊,然后將所有這些模塊打包成一個或多個 bundle。

為什么需要 Webpack?

現在的網頁可看做是功能豐富的應用,它們包含大量的 JavaScript 代碼和一些開源的依賴包,如何管理這些文件也是一個問題。有了 JavaScript 模塊化編程,我們可以把復雜的程序細化為小的文件,WebPack 幫助我們管理這些文件的依賴關系,把各種模塊打包合并成一個文件輸出給瀏覽器。在打包的過程中還會對資源做一些處理,比如把 Typescript 編譯成 JavaScript、sass 編譯成 css 等。

使用 Webpack

WebPack 有四個核心概念:

  • 入口 (entry)
  • 輸出 (output)
  • Loader
  • 插件 (plugins)

下面結合 Vue 項目模板介紹 Webpack 基本的使用方法

1. 準備工作

按照 Vue 教程 創建一個新項目

# 全局安裝 vue-cli
$ npm install --global vue-cli
# 創建一個基于 webpack 模板的新項目
$ vue init webpack my-project
# 安裝依賴,走你
$ cd my-project
$ npm install
$ npm run dev

進入項目的 build 目錄,這里有三個文件用于設置 WebPack:

  • webpack.base.conf.js
  • webpack.dev.conf.js
  • webpack.prod.conf.js

看名字就能猜到它們使用環境:webpack.dev.conf.js 保存的是開發環境的配置,webpack.prod.conf.js 是生成環境的配置,webpack.base.conf.js 是這兩個環境公用的配置。

webpack.base.conf.js 文件不到 100 行,主要內容在 module.exports = { } 中:

module.exports = {
  context: path.resolve(__dirname, '../'),
  entry: {   
    app: './src/main.js'
  },
  output: {
    path: config.build.assetsRoot,
    filename: '[name].js',
    publicPath: process.env.NODE_ENV === 'production'
      ? config.build.assetsPublicPath
      : config.dev.assetsPublicPath
  },
  resolve: {
    extensions: ['.js', '.vue', '.json'],
    alias: {
      'vue$': 'vue/dist/vue.esm.js',
      '@': resolve('src'),
    }
  },
  module: {
    rules: [
      ...(config.dev.useEslint ? [createLintingRule()] : []),
      {
        test: /\.vue$/,
        loader: 'vue-loader',
        options: vueLoaderConfig
      },
      {
        test: /\.js$/,
        loader: 'babel-loader',
        include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')]
      },
      {
        test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: utils.assetsPath('img/[name].[hash:7].[ext]')
        }
      },
      {
        test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: utils.assetsPath('media/[name].[hash:7].[ext]')
        }
      },
      {
        test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
        }
      }
    ]
  },
  node: {
    // prevent webpack from injecting useless setImmediate polyfill because Vue
    // source contains it (although only uses it if it's native).
    setImmediate: false,
    // prevent webpack from injecting mocks to Node native modules
    // that does not make sense for the client
    dgram: 'empty',
    fs: 'empty',
    net: 'empty',
    tls: 'empty',
    child_process: 'empty'
  }
}

2. 入口 (entry)

定義在 webpack.base.conf.js 中:

module.exports = {
  context: path.resolve(__dirname, '../'),
  entry: {
    app: './src/main.js'
  },

這里指定 ./src/main.js 為入口文件,Webpack 已這文件為起點,找出它依賴的模塊,處理每個依賴項,然后輸出到 bundle 文件中。單頁應用指定一個入口就可以了,多頁應用需要指定多個入口,具體語法參考文檔 入口起點

3. 出口

output 屬性告訴 webpack 在哪里輸出它所創建的 bundles,以及如何命名這些文件。webpack.base.conf.js 中的出口定義:

module.exports = {
  context: path.resolve(__dirname, '../'),
  entry: {   
    app: './src/main.js'
  },
  output: {
    path: config.build.assetsRoot,
    filename: '[name].js',
    publicPath: process.env.NODE_ENV === 'production'
      ? config.build.assetsPublicPath
      : config.dev.assetsPublicPath
  },
  xxx

filenamepath 屬性,用來告訴 webpack bundle 的名稱,以及我們想要生成(emit)到哪里。
config.build.assetsRoot 這個屬性定義在 ./config/index.js 文件中,

build: {
    // Template for index.html
    index: path.resolve(__dirname, '../dist/index.html'),

    // Paths
    assetsRoot: path.resolve(__dirname, '../dist'),
    assetsSubDirectory: 'static',
    assetsPublicPath: '/',

實際值是 ../dist/index.html, 運行 npm run build 后會在生成 dist 目錄和 index.html 文件。
注意到 filename 的名稱不是固定的,使用了 [name] 占位符,webpack 編譯后 name 會被替換為該模塊的名稱,類似的,還可以使用其它占位符作為 filename,如 [hash][chunkhash][id][query] 等,它們的含義在 output-filename 文檔中有詳細介紹。

publicPath 項指定在瀏覽器中所引用的「此輸出目錄對應的公開 URL」。這里通過判斷 process.env.NODE_ENV 的值來區分開發和生成環境。和 config.build.assetsRoot 類似,config.build.assetsPublicPathconfig.dev.assetsPublicPath 都是定義在 ./config/index.js 文件中,它們的值都是 /process.env.NODE_ENV 有時定義在哪里呢?process.env 是 Node 的環境變量,這篇文章中有介紹。這里的 process.env.NODE_ENV 定義在config目錄下,dev.env.jsprod.env.jstest.env.js 三個文件中都有定義,分別對應開發、生成、和測試環境。

4. loader

Webpack 自身只理解 JavaScript,通常項目還會包含 CSS、圖片、文本等其它資源,loader 可以將這些文件轉換為 webpack 能夠處理的有效模塊,然后你就可以利用 Webpack 的打包能力,對它們進行處理。loader 的配置在 module.rules 屬性里,以第一個 loader 配置為例:

{
    test: /\.vue$/,
    loader: 'vue-loader',
    options: vueLoaderConfig
}
  • test: 識別需要轉換的文件,這個正則表示 ".vue" 后綴類型文件
  • loader: 設置 loader 名稱,常用的 loader 可以在 這里 查詢
  • options: 可以是字符串或者對象,它的值會被傳遞到 loader 中,需要參考各 loader 的使用文檔,這里

這個配置告訴 Webpack,遇到 require 或者 import .vue 類型文件時,先交給 vue-loader 轉換一下,然后再對它打包。

5. Plugins

插件可以處理多種類型的任務,從打包優化和壓縮,一直到重新定義環境中的變量。插件的使用方法也很簡單,首先 require,然后 new 一個實例放到 plugins 數組中。Vue 模板項目中的插件配置分別在 webpack.dev.conf.jswebpack.prod.conf.js 兩個文件中,以 webpack.dev.conf.js 中配置為例:

  plugins: [
    new webpack.DefinePlugin({
      'process.env': require('../config/dev.env')
    }),
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update.
    new webpack.NoEmitOnErrorsPlugin(),
    // https://github.com/ampedandwired/html-webpack-plugin
    new HtmlWebpackPlugin({
      filename: 'index.html',
      template: 'index.html',
      inject: true
    }),
    // copy custom static assets
    new CopyWebpackPlugin([
      {
        from: path.resolve(__dirname, '../static'),
        to: config.dev.assetsSubDirectory,
        ignore: ['.*']
      }
    ])
  ]

從上到下依次為:

  • DefinePlugin: 通過配置了 DefinePlugin,這里面的標識 (process.env) 就相當于全局變量,業務代碼可以直接使用配置的標識。
  • HotModuleReplacementPlugin: 模塊熱替換插件,在應用程序運行過程中替換、添加或刪除模塊,而無需重新加載整個頁面,加快開發速度。
  • NoEmitOnErrorsPlugin: 跳過編譯時出錯的代碼并記錄,使編譯后運行時的包不會發生錯誤。
  • HtmlWebpackPlugin: 根據你提供的 html 模板 生成 html。
  • CopyWebpackPlugin: 拷貝靜態資源到指定目錄

6. 其它配置項目

注意到 webpack.base.conf.js 文件中,在 output 屬性下面,還有個 resolve 屬性,它有什么作用呢?

resolve: {
    extensions: ['.js', '.vue', '.json'],
    alias: {
      'vue$': 'vue/dist/vue.esm.js',
      '@': resolve('src'),
    }
  }

我們在項目中 import 的模塊有的是自己定義的,有的是第三方的庫,resolver 幫助 webpack 找到這些模塊的絕對路徑。

  • alias: 上述配置中定義了兩個別名, vue 指向 node_modules 下的 vue/dist/vue.esm.js 文件,在使用的時候直接 import Vue from 'vue',webpack 就能夠找到該文件了。@ 指向了 src 目錄,引用 src 目錄下的模塊可以這樣寫 import HelloWorld from '@/components/HelloWorld'
  • extensions: 在 import 模塊時不帶擴展名,webpack 依然可找到對應的文件,具體哪些文件可以不帶呢?就是在這里定義的。extensions 數組有 .js.vue.json,所以我們引用這些文件時就可以不加擴展名了。

總結

對于剛接觸前端項目的新人,了解 Webpack 的基本配置很重要。這里只是簡單介紹了 webpack 最基本的功能,還有如 devserver 等其它常用配置就留給你去探索吧。官方文檔真的很全,各種屬性的使用方法都有詳細的介紹,一定不要浪費這么好的資源。

參考鏈接

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,460評論 6 538
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,067評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,467評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,468評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,184評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,582評論 1 325
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,616評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,794評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,343評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,096評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,291評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,863評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,513評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,941評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,190評論 1 291
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,026評論 3 396
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,253評論 2 375