webpack學習入門

內(nèi)容提綱

  • electron-vue項目中的webpack工程實例
  • 思考
  • webpack與gulp/grunt
  • HMR

electron-vue項目中的webpack工程實例

從electron-vue項目中的實際使用例子來入手

如下是 webpack.renderer.config.js 文件

'use strict'

process.env.BABEL_ENV = 'renderer'

const path = require('path')
const { dependencies } = require('../package.json')
const webpack = require('webpack')

const BabiliWebpackPlugin = require('babili-webpack-plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')

let whiteListedModules = ['vue']

let rendererConfig = {
  /**
   * 控制打包后代碼的形式辅辩,影響調(diào)試。當前設置為cheap-module-eval-source-map,調(diào)試時僅可看到原始源代碼      
   *(僅限行),如果同時使用了代碼壓縮插件妥泉,代碼被合成一行,將無法獲取有效的調(diào)試內(nèi)容埋虹。
   */
  devtool: '#cheap-module-eval-source-map',
  
  /**
   * webpack處理項目的入口文件侵贵,以該文件作為構(gòu)建其內(nèi)部依賴圖的開始。
   * 通常是項目的 index 或 main 文件
   * 注意 entry 可以是一個數(shù)組贴彼,即 webpack 可以處理多個入口文件潜腻,將其
   */
  entry: {
    renderer: path.join(__dirname, '../src/renderer/main.js')
  },
  
  /**
   * 聲明外部依賴,在此聲明的文件器仗,即使在工程中使用import引入融涣,也不會被打到bundle中。
   * 一般我們會把package.json中的dependencies排除在外精钮。
   * 當我們需要使用cdn引用第三方庫時威鹿,我們也要將其放在external中,比如使用jquery cdn
   * 思考1:node_modules中的三方庫被排除在打包的bundle文件外轨香,為什么我們的程序在打包后仍然能夠使用
   * 三方庫
   */
  externals: [
    ...Object.keys(dependencies || {}).filter(d => !whiteListedModules.includes(d))
  ],
    
  /**
   * 模塊是 webpack 的核心忽你,webpack 幫我們把文件打包成模塊,然后我們就可以使用 import 的方式來
   * 使用臂容。webpack 通過 loader 來各種處理文件科雳,因此我們可以在項目中使用模塊化的方法引用任何類型的文件
   */
  module: {
    rules: [
      {
        /**
         * test 做正則匹配,所有符合該匹配規(guī)則的文件都將應用該 loader 規(guī)則
         */ 
        test: /\.(js|vue)$/,
        /**
         * enfore 用來定義規(guī)則的執(zhí)行順序脓杉,取值 ['pre', 'post']糟秘,此處為 pre,將在所有 loader 執(zhí)行前
         * 執(zhí)行球散。即在所有 loader 規(guī)則之前蚌堵,使用 eslint-loader 對代碼做靜態(tài)檢查
         */ 
        enforce: 'pre',
        /**
         * exclude 排除不想應用該規(guī)則的目錄
         */ 
        exclude: /node_modules/,
        /**
         * useEntry 配置loader
         */ 
        use: {
          loader: 'eslint-loader',
          options: {
            formatter: require('eslint-friendly-formatter')
          }
        }
      },
      {
        test: /\.css$/,
        /**
         * ExtractTextPlugin 插件,用于將 *.css 分離到單獨的 style.css 文件中沛婴,而不是作為樣式
         * 放入 bundle.js 文件中
         * fallback 是當 CSS 沒有被提取時應用的 loader
         * style-loader 的作用是使用 <style></style> 標簽將樣式添加到頁面 dom 中
         */ 
        use: ExtractTextPlugin.extract({
          fallback: 'style-loader',
          use: 'css-loader'
        })
      },
      {
        test: /\.scss$/,
        /**
         * 不同于 use 的形式吼畏,loader 寫成內(nèi)聯(lián)的方式
         * 執(zhí)行順序是“從右到左,從下到上“嘁灯,即 sass-loader -> css-loader -> vue-style-loader
         * loader 之間通過 ! 分隔泻蚊,如果需要為 loader 配置參數(shù),則可以使用 ?key=value&foo=bar 的形
         * 式丑婿,類似于 url 的參數(shù)性雄。
         */
        loader: 'vue-style-loader!css-loader!sass-loader'
      },
      {
        test: /\.html$/,
        use: 'vue-html-loader'
      },
      {
        test: /\.js$/,
        use: 'babel-loader',
        exclude: /node_modules/
      },
      {
        test: /\.json$/,
        use: 'json-loader'
      },
      {
        test: /\.node$/,
        use: 'node-loader'
      },
      {
        test: /\.vue$/,
        use: {
          loader: 'vue-loader',
          options: {
            extractCSS: process.env.NODE_ENV === 'production',
            loaders: {
              /**
               * 帶參數(shù)的 loader
               */
              sass: 'vue-style-loader!css-loader!sass-loader?indentedSyntax=1',
              scss: 'vue-style-loader!css-loader!sass-loader'
            }
          }
        }
      },
      {
        test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
        use: {
          loader: 'url-loader',
          /**
           * query 老的寫法没卸,等同于 options
           */
          query: {
            limit: 10000,
            name: 'imgs/[name].[ext]'
          }
        }
      },
      {
        test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
        use: {
          loader: 'url-loader',
          query: {
            limit: 10000,
            name: 'fonts/[name].[ext]'
          }
        }
      },
      {
        test: /\.(ogg)$/,
        use: 'file-loader'
      }
    ]
  },
  plugins: [
    new ExtractTextPlugin('styles.css'),
      
    /**
     * HtmlWebpackPlugin 這個插件的作用是依據(jù)一個簡單的index.html模板,生成一個自動引用你打包后的JS文
     * 件的新index.html秒旋。這在每次生成的js文件名稱不同時非常有用(比如添加了hash值)
     * 當我們發(fā)布一個網(wǎng)站應用時约计,我們需要通過生成帶 hash 值的文件名來更新瀏覽器對靜態(tài)文件的緩存
     * 這樣會有一個問題,我們每次使用 webpack 打包后都需要手動修改 index.html 文件中引用的 
     * bundle.[hash].js 文件名迁筛,顯然很麻煩煤蚌。
     * HtmlWebpackPlugin 的引入就是為了解決此問題,通過配置一個 index 模板细卧,webpack 會在每次打包完成
     * 后尉桩,將 bundle.[hash].js 的引用注入 index.html 中
     */
    new HtmlWebpackPlugin({
      filename: 'index.html',
      template: path.resolve(__dirname, '../src/index.ejs'),
      minify: {
        collapseWhitespace: true,
        removeAttributeQuotes: true,
        removeComments: true
      },
      nodeModules: process.env.NODE_ENV !== 'production'
        ? path.resolve(__dirname, '../node_modules')
        : false
    }),
      
    /**
     * HotModuleReplacementPlugin webpack 提供的最有用的功能之一。它允許在運行時更新各種模塊贪庙,而無需
     * 進行頁面刷新蜘犁。使用 HMR 的方法:
     * 1.在webpack配置文件中添加HMR插件;
     * 2.在Webpack Dev Server中添加“hot”參數(shù)止邮; 在我們的electron-vue應用中使用了接口調(diào)用的方式这橙,查看
     * dev-runner.js 文件
     */
    new webpack.HotModuleReplacementPlugin(),
      
    new webpack.NoEmitOnErrorsPlugin(),
      
    /**
     * 自動加載模塊,并定義到全局變量导披,隨時可以引用屈扎。相當于 import 了一個全局對象
     * new webpack.ProvidePlugin({
     *     identifier: 'module1',
     *     identifier: ['module1', 'property1'],
     *     // ...
     * });
     * 任何時候,當 identifier 被當作未賦值的變量時盛卡,module 就會自動被加載助隧,并且 identifier 會被這個
     * module 輸出的內(nèi)容所賦值。
     * 支持支持命名導出滑沧,如我們想全局使用 _.map 這個方法并村,那么我們可以這樣來引入:
     * new webpack.ProvidePlugin({
     *     _map: ['lodash', 'map']
     * });
     * 在項目中即可以使用 _map 來使用 _.map 方法
     */
    new webpack.ProvidePlugin({
      jQuery: "jquery",
      jquery: "jquery",
      $: "jquery",
      "window.jQuery": "jquery"
    })
  ],
    
  /**
   * 定義打包文件名,[name] 參數(shù)由 entry 中的key決定滓技,此處為render.js
   * 這里文件名沒有使用 hash 值哩牍,是因為在 electron 項目中,升級不需要考慮瀏覽器緩存的問題令漂,整個應用都會
   * 被替換
   */
  output: {
    filename: '[name].js',
    libraryTarget: 'commonjs2',
    path: path.join(__dirname, '../dist/electron')
  },
    
  /**
   * 在此可以定義 import 或 require 的別名膝昆,來確保模塊引入變得更簡單。
   * 當目錄下的文件沒有文件后綴時叠必,使用 extensions 中的后綴依次嘗試解析
   */
  resolve: {
    alias: {
      '@': path.join(__dirname, '../src/renderer'),
      'vue$': 'vue/dist/vue.esm.js',
      'config' : path.join(__dirname, '../config'),
      'components': path.join(__dirname, '../src/renderer/components'),
      'utils': path.join(__dirname, '../src/renderer/utils'),
      'renderer': path.join(__dirname, '../src/renderer'),
      'services': path.join(__dirname, '../src/renderer/services'),
      'store': path.join(__dirname, '../src/renderer/store'),
      'router': path.join(__dirname, '../src/renderer/router'),
      'plugins': path.join(__dirname, '../src/renderer/plugins'),
      'css': path.join(__dirname, '../src/renderer/css'),
      'images': path.join(__dirname, '../src/renderer/images'),
      'jquery': path.join(__dirname, '../node_modules/jquery/src/jquery')
    },
    extensions: ['.js', '.vue', '.json', '.css', '.node']
  },
  /**
   * 構(gòu)建目標, https://webpack.docschina.org/configuration/target
   */
  target: 'electron-renderer'
}

/**
 * Adjust rendererConfig for development settings
 */
if (process.env.NODE_ENV !== 'production') {
  /**
   * DefinePlugin 允許創(chuàng)建一個在編譯時可以配置的全局常量荚孵。
   * 比如定義后端接口的url
   * https://webpack.docschina.org/plugins/define-plugin/
   */
  rendererConfig.plugins.push(
    new webpack.DefinePlugin({
      '__static': `"${path.join(__dirname, '../static').replace(/\\/g, '\\\\')}"`
    })
  )
}

/**
 * Adjust rendererConfig for production settings
 */
if (process.env.NODE_ENV === 'production') {
  rendererConfig.devtool = ''

  rendererConfig.plugins.push(
    /**
     * 這是一款基于 Babel 的壓縮工具,支持 es6 的一些特性纬朝,取代 UglifyJS
     */
    new BabiliWebpackPlugin({
      removeConsole: true,
      removeDebugger: true
    }),
    /**
     * Copies individual files or entire directories to the build directory.
     * 一般會使用插件把需要使用的靜態(tài)文件考到構(gòu)建后對應的目錄
     */
    new CopyWebpackPlugin([
      {
        from: path.join(__dirname, '../static'),
        to: path.join(__dirname, '../dist/electron/static'),
        ignore: ['.*']
      }
    ]),
    new webpack.DefinePlugin({
      'process.env.NODE_ENV': '"production"'
    }),
    /**
     * 用于 webpack2 對 webpack1 配置的兼容收叶,抹平差異
     */
    new webpack.LoaderOptionsPlugin({
      minimize: true
    })
  )
}

module.exports = rendererConfig

思考1:external

node_modules中的三方庫被排除在打包的bundle文件外,為什么我們的程序在打包后仍然能夠使用三方庫

// package.json
{
    ...
    "build": {
        ...
        "files": [
          "dist/electron",
          "node_modules/",
          "package.json"
        ]
    }
}

在 package.json 文件中共苛,配置了 files 文件判没,electron-build 在打包時會將 files 中的文件夾全部打包到工程中蜓萄,即 resources/app.asar 文件

我們使用命令查看

asar list ./resources/app.asar

可以看到如下一系列文件

/node_modules/argparse/lib/action/store/false.js
/node_modules/argparse/lib/action/store/true.js
/node_modules/argparse/lib/action/append
/node_modules/argparse/lib/action/append/constant.js
...
/dist
/dist/electron
/dist/electron/index.html
/dist/electron/main.js
/dist/electron/renderer.js
/dist/electron/static/update/process.html
/dist/electron/static/update/process.js
/dist/electron/static/update/libs
...
/dist/electron/fonts
/dist/electron/fonts/element-icons.ttf

webpack與gulp/grunt

Gulp/Grunt是一種能夠優(yōu)化前端的開發(fā)流程的工具,而WebPack是一種模塊化的解決方案澄峰,不過Webpack的優(yōu)點使得Webpack在很多場景下可以替代Gulp/Grunt類的工具

Grunt和Gulp的工作方式

在一個配置文件中嫉沽,指明對某些文件進行類似編譯,組合俏竞,壓縮等任務的具體步驟绸硕,工具之后可以自動替你完成這些任務。

Webpack的工作方式是

把你的項目當做一個整體胞此,通過一個給定的主文件(如:index.js)臣咖,Webpack將從這個文件開始找到你的項目的所有依賴文件跃捣,使用loaders處理它們漱牵,最后打包為一個(或多個)瀏覽器可識別的JavaScript文件。

如果實在要把二者進行比較疚漆,Webpack的處理速度更快更直接酣胀,能打包更多不同類型的文件。

開發(fā)模式熱更新

開發(fā)模式熱更新分為兩部分:

  • 監(jiān)控代碼修改娶聘,并自動重新編譯打包
  • 通知瀏覽器同步修改的內(nèi)容(注意闻镶,不是刷新瀏覽器重新加載,是動態(tài)替換部分代碼丸升,保持原頁面的數(shù)據(jù)狀態(tài)铆农,這是HMR的關鍵特性)

通常使用webpack-dev-server

  • 該工具包括自動監(jiān)控代碼編譯,以及HMR狡耻。
  • webpack-dev-server將代碼打包在內(nèi)存中墩剖,所以修改代碼時在項目下找不到動態(tài)生成的編譯文件。

注意

依靠HMR的熱更新機制夷狰,我們可以享受修改的文件被實時同步到瀏覽器的便利岭皂,但對于頁面來說,這并不是全部沼头。我們?nèi)匀恍枰陧撁娴膉s代碼中做一些處理爷绘。

比如:我們在按鈕上綁定了一個click事件的處理函數(shù)handleClick,我們修改了handleClick的代碼进倍,它被推送到瀏覽器中土至,但是頁面并不會自動替換之前綁定的click事件,當我們點擊按鈕時猾昆,仍然得到舊的響應陶因。

所以除了使用webpack-dev-server毡庆,我們需要在js代碼中監(jiān)聽HMR 的 accept 方法烙如,在此方法中更新類似的處理函數(shù)亚铁。

這也就是為什么在react項目中螟加,我們需要使用react-hot-loader庫捆探,本質(zhì)上是實現(xiàn)了HMR 的 accept 的處理。

參考文檔

入門 Webpack曾雕,看這篇就夠了

官方中文文檔

Webpack Hot Module Replacement 的原理解析

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市搔弄,隨后出現(xiàn)的幾起案子丰滑,更是在濱河造成了極大的恐慌褒墨,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,470評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件柬唯,死亡現(xiàn)場離奇詭異圃庭,居然都是意外死亡,警方通過查閱死者的電腦和手機拘央,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評論 3 392
  • 文/潘曉璐 我一進店門书在,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人栏账,你說我怎么就攤上這事挡爵〔杈椋” “怎么了?”我有些...
    開封第一講書人閱讀 162,577評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長兑障。 經(jīng)常有香客問我旺垒,道長肤无,這世上最難降的妖魔是什么骇钦? 我笑而不...
    開封第一講書人閱讀 58,176評論 1 292
  • 正文 為了忘掉前任眯搭,我火速辦了婚禮窥翩,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘寇蚊。我一直安慰自己,他們只是感情好仗岸,可當我...
    茶點故事閱讀 67,189評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著借笙,像睡著了一般。 火紅的嫁衣襯著肌膚如雪业稼。 梳的紋絲不亂的頭發(fā)上盗痒,一...
    開封第一講書人閱讀 51,155評論 1 299
  • 那天,我揣著相機與錄音低散,去河邊找鬼俯邓。 笑死,一個胖子當著我的面吹牛稽鞭,可吹牛的內(nèi)容都是我干的川慌。 我是一名探鬼主播吃嘿,決...
    沈念sama閱讀 40,041評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼琴拧,長吁一口氣:“原來是場噩夢啊……” “哼降瞳!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起沛膳,我...
    開封第一講書人閱讀 38,903評論 0 274
  • 序言:老撾萬榮一對情侶失蹤扔枫,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后锹安,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體短荐,經(jīng)...
    沈念sama閱讀 45,319評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,539評論 2 332
  • 正文 我和宋清朗相戀三年叹哭,在試婚紗的時候發(fā)現(xiàn)自己被綠了忍宋。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,703評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡风罩,死狀恐怖糠排,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情超升,我是刑警寧澤入宦,帶...
    沈念sama閱讀 35,417評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站廓俭,受9級特大地震影響云石,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜研乒,卻給世界環(huán)境...
    茶點故事閱讀 41,013評論 3 325
  • 文/蒙蒙 一汹忠、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦宽菜、人聲如沸谣膳。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽继谚。三九已至,卻和暖如春阵幸,著一層夾襖步出監(jiān)牢的瞬間花履,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評論 1 269
  • 我被黑心中介騙來泰國打工挚赊, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留诡壁,地道東北人。 一個月前我還...
    沈念sama閱讀 47,711評論 2 368
  • 正文 我出身青樓荠割,卻偏偏與公主長得像妹卿,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子蔑鹦,可洞房花燭夜當晚...
    茶點故事閱讀 44,601評論 2 353

推薦閱讀更多精彩內(nèi)容

  • 1 Webpack 1.1 概念簡介 1.1.1 WebPack是什么 1夺克、一個打包工具 2、一個模塊加載工具 3...
    Kevin_Junbaozi閱讀 6,659評論 0 16
  • 寫在前面的話 閱讀本文之前嚎朽,先看下面這個webpack的配置文件铺纽,如果每一項你都懂,那本文能帶給你的收獲也許就比較...
    不忘初心_9a16閱讀 3,234評論 0 17
  • 最近在學習 Webpack,網(wǎng)上大多數(shù)入門教程都是基于 Webpack 1.x 版本的,我學習 Webpack 的...
    My_Oh_My閱讀 8,180評論 40 247
  • 文:【渺小沙】 “我美嗎火鼻?”她認真的看著我問道室囊。盡管從別人口中聽了千萬遍,但我還是要親口告訴她:“美魁索!很美!”她滿...
    渺小沙閱讀 397評論 0 5
  • 我聽說過太湖卻不知道同里湖盼铁,直到已經(jīng)身處同里卻還在問這是同里還是太湖粗蔚,然后就看到“同里湖大飯店”確定這是同里而不是...
    南妡淺月閱讀 391評論 0 3