webpack從配置到跑路v1

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

核心概念

  • entry 入口
  • output 輸出
  • loader 模塊轉換器,用于把模塊原內容按照需求轉換成新內容;
  • plugins 擴展插件,在webpack構建流程中的特定時機注入擴展邏輯,來改變構建結果或做你想要做的事情。

作用:模塊化打包
webpack是一個模塊打包工具,一個打包模塊化JavaScript的工具,是工程化、自動化思想在前端開發中的體現。
webpack將項目的資源文件當成一個個模塊,模塊之間會有依賴關系,它會對這些有依賴關系的文件進行處理 -- 從入口模塊出發,識別出模塊之間的依賴關系(模塊化導入語句),遞歸找出入口文件的所有依賴,將它們打包到一個或多個文件中(bundle)。

初始化項目

  • 新建一個目錄, npm init -y
  • 安裝核心依賴
    npm i webpack webpack-cli -D  // webpack4.x.x
    

局部安裝的命令無法像全局安裝那樣直接使用,需要配合 npx,它會從當前項目的node_modules中查找,比如 npx webpack -v(查看版本號)
wepack V4.0.0 開始, webpack是開箱即用的,在不引入任何配置文件的情況下就可以使用。

  • 新建src/index.js
    class Animal {
        constructor(name) {
            this.name = name;
        }
    }
    const dog = new Animal('dog');
  • 構建:npx webpack --mode=development,默認 production 模式。development模式可以更好的查看打包后的代碼。
(function(module, exports) {
eval("class Animal {\r\n    constructor(name) {\r\n  ......
})

默認輸出目錄和文件:dist/main.js,更多默認配置在node_modules/webpack/lib/WebpackOptionsDefaulter.js

查看dist/main.js可知,src/index.js中的代碼并沒有被轉譯為低版本代碼,這顯然不是我們想要的。

babel-loader

loaderwebpack的四大核心之一,用于對源代碼進行轉換,而轉換工具正是Babel。
babel-loaderBabelwebpack的使用方式,可以將JS代碼向低版本轉換。

  • 安裝Babel相關的依賴
    npm i @babel/core @babel/preset-env @babel/plugin-transform-runtime -D
    npm i @babel/runtime @babel/runtime-corejs3 -S
    
    npm i babel-loader -D
    
  • 新建webpack的配置文件webpack.config.js
    // ! Webpack是基于NodeJS的,必須使用CommonJS規范導出一個對象
    module.exports = {
        module: {
            rules: [
                {
                    test: /\.js$/,
                    use: ['babel-loader'],
                    exclude: /node_modules/ //排除 node_modules 目錄
                }
            ]
        }
    }
    
    建議給 loader 指定 includeexclude,有效提升編譯效率
  • 新建Babel配置文件 .babelrc
    {
        "presets": ["@babel/preset-env"],
        "plugins": [
            ["@babel/plugin-transform-runtime", { "corejs": 3 }]
        ]
    }
    

重新編譯,發現代碼已經被Babel降級轉換了。

如果不想創建.babelrc,還可以在 webpack.config.js 配置 Babel

use: {
    loader: 'babel-loader',
    options: {
        presets: ["@babel/preset-env"],
        plugins: [
            ["@babel/plugin-transform-runtime", { "corejs": 3 }]
        ]
    }
},

loader配置項

loader 需要配置在 module.rules 中,rules是一個數組。

  • 字段 test 表示匹配規則,對項目中符合規則的文件進行處理;
  • loader 的配置方式有多種
    • 只適用于一個 loader 的情況
      {
          test: /\.js$/,
          loader: 'babel-loader',
          options: {
              //...
          }
      }
      
    • 使用 use 字段則比較自由,可以是一個字符串
      {
          test: /\.js$/,
          use: 'babel-loader'
      }
      
    • 可以是一個數組
      {
          test: /\.css$/,
          use: ['style-loader', 'css-loader']
      }
      
    • 還可以是一個對象,其 options 屬性用于配置 loader
      {
          test: /\.js$/,
          use: {
              loader: 'babel-loader',
              options: {
                  presets: ["@babel/preset-env"]
              }
          },
      }
      

mode

mode 用于指定打包構建時的模式,支持兩個配置項

  • development
    process.env.NODE_ENV 的值設置為development,啟用NamedChunksPlugin、NamedModulesPlugin
  • production
    process.env.NODE_ENV 的值設置為production,啟用FlagDependencyUsagePlugin、FlagIncludedChunksPlugin、ModuleConcatenationPlugin、NoEmitOnErrorsPlugin、OccurrenceOrderPlugin、SideEffectsFlagPlugin、UglifyJsPlugin(JS壓縮)

mode可以配置在webpack.config.js中,然后直接使用 npx webpack 進行編譯。

module.exports = {
    mode: "development",
    // ...
}

cross-env

這是一個運行跨平臺設置和使用環境變量的腳本。
在配置文件中,經常會使用 process.env.NODE_ENV 來判斷當前是development還是production,但process.env默認并沒有NODE_ENV
cross-env可以兼容運行平臺去設置環境變量NODE_ENV,比如Windows系統不支持NODE_ENV=development的設置方式。
cross-env能夠提供一個設置環境變量的scripts,讓你能夠以unix方式設置環境變量,然后在Windows上兼容運行。

  • 安裝
    npm i cross-env -D
    
  • 配置package.json
        "scripts": {
            "dev": "cross-env NODE_ENV=development webpack",
            "build": "cross-env NODE_ENV=production webpack"
        }
    
  • webpack.config.js中使用process.env.NODE_ENV,如動態配置mode
    const ENV = process.env.NODE_ENV
    module.exports = {
        mode: ENV,
        //...
    }
    

構建HTML頁面

編譯HTMLwebpack插件:html-webpack-plugin

  • 安裝插件
    npm i html-webpack-plugin -D 
    
  • 在根目錄下創建 index.html,快捷鍵 html:5 生成HTML
  • 配置webpack.config.js,plugins字段是一個數組,存放所有的webpack插件
    // 引入插件
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    module.exports = {
        //...
        plugins: [
            new HtmlWebpackPlugin({
                template: './index.html',
                filename: 'index.html', //打包后的文件名
                minify: {
                    minimize: true,  // 是否打包為最小值
                    removeAttributeQuotes: true,  //是否刪除屬性的引號
                    collapseWhitespace: false,  //去除空格
                    removeComments: true,  // 去除注釋
                    minifyCSS: true,  // 壓縮HTML內的CSS
                    minifyJS: true,  //壓縮HTML內的JS
                    removeEmptyElements: true  //清理內容為空的元素
                },
                hash: true  //引入產出資源時加上哈希,避免緩存
            })
        ]
    }
    
  • 執行 npm run dev 構建,生成dist/index.html,且自動注入了<script>腳本,引入打包后的js文件
    <script src="main.js?ccec26148a41640c2cf0"></script>
    // hash: true 在引入資源后加上哈希值
    

html-webpack-plugin 中的 config 妙用
html-webpack-plugin插件在解析HTML時,支持類似ejs語法。同時會向HTML中注入該插件的對象htmlWebpackPlugin,使用其中配置的各種屬性。
例如,我們想自由配置開發版和發布版的頁面展示不同的內容;

  • 新增一個自定義的配置文件public/config.js
    module.exports = {
        development: {  // 開發配置項
            template: {
                title: 'Hello development',
                header: false,  // 不要頭部和尾部
                footer: false
            }
        },
        production: {  // 發布配置項
            template: {
                title: 'Hi production',
                header: true,  // 只要頭部
                footer: false
            }
        }
    }
    
  • webpack.config.js 中配置html-webpack-pluginconfig 屬性
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    const ENV = process.env.NODE_ENV
    const config = require('./public/config')[ENV]
    module.exports = {
        mode: ENV,
        //...
        plugins: [
            new HtmlWebpackPlugin({
                template: './index.html',
                filename: 'index.html',
                config: config.template
            })
        ]
    }
    
  • index.html
    <!DOCTYPE html>
    <html lang="en">
    <head>
        // ...
        <% if(htmlWebpackPlugin.options.config.header) { %>
        <link rel="stylesheet" type="text/css" href="http://common/css/header.css">
        <% } %>
        <title><%= (htmlWebpackPlugin.options.config.title) %></title>
    </head>
    <body>
        hello back
    </body>
    <% if(htmlWebpackPlugin.options.config.footer) { %>
    <script src="http://common/header.min.js" type="text/javascript"></script> 
    <% } %>
    </html>
    
  • 分別使用 npm run devnpm run build 構建,對比dist/index.html的不同

更多html-webpack-plugin配置項

實時展示頁面

webpack-dev-server 用于配置本地服務器,可以為webpack打包生成的資源文件提供web服務

  • 為靜態文件提供web服務
  • 自動刷新和熱替換(HMR)
npm i webpack-dev-server -D

修改package.json

  "scripts": {
    "dev": "cross-env NODE_ENV=development webpack-dev-server",
    "build": "cross-env NODE_ENV=production webpack"
  },

webpack.config.js 中進行webpack-dev-server的其他配置

module.exports = {
    //...
    devServer: {
        open: true,   // 自動打開首頁,默認false
        port: '3000',   // 默認是8080
        hot: true,  // 熱加載
        clientLogLevel: "warning",   // 日志等級
        compress: true,    // 是否啟用 gzip 壓縮
        proxy:  // 代理
        // ...
    }
}

執行npm run dev,現在修改 src/index.js,頁面控制臺會實時刷新,當前刷新的是整個頁面。
更多的配置可以點擊查看。

devtool

sourcemap是為了解決實際運行代碼(打包后)出現問題時,無法定位到開發環境中的源代碼;

查看頁面控制臺上打印的log日志,會發現對應的行號是代碼編譯后的,而不是當前源碼中的行號。
devtool 配置項可以幫助我們將編譯后的代碼映射回源碼中,不同的值會明顯影響構建和重新構建的速度。
綜合構建速度,在開發模式下通常設置為 cheap-module-eval-source-map;在生產環境下則設置為source-map

// webpack.config.js
module.exports = {
    devtool: 'cheap-module-eval-source-map'
}
  1. sourcemap的5種基本devtool選項
    • eval 每個模塊都使用eval()執行,每個模塊后會增加sourceURL來關聯模塊處理前后的對應關系;
      由于會映射到轉換后的代碼,而不是映射到原始代碼(沒有從loader中獲取sourcemap),所以不能正確顯示行號;又因為不需要生成模塊的sourcemap,所以打包速度很快。
    • source-map 會為模塊生成獨立的sourcemap(.map)文件,我們可以根據報錯信息和.map文件進行錯誤分析,定位到源碼;
    • inline 不會生成獨立的.map文件,sourcemap轉換為DataUrl后添加到bundle中;
    • cheap 在打包后同樣會為每個模塊生成.map文件,但與source-map的區別在于,它生成的.map文件會忽略原始代碼中的列信息,也不包含loadersourcemap
    • moudle:包含了loader模塊之間的sourcemap,將loader source map簡化為每行一個映射;
  2. 基本類型之間可以相互搭配,如eval-source-map、inline-cheap-model-source-map、cheap-module-eval-source-map、hidden-source-map、nosources-source-map
    • 開發環境常用:eval、eval-source-map、cheap-eval-source-map、cheap-module-eval-source-map
    • 生產環境常用:none(省略devtool選項,不生成sourcemap)、source-map、hidden-source-map、nosources-source-map
  3. source-maphidden-source-map 都會打包生成單獨的 .map 文件。區別在于,source-map會在打包出的js文件中增加一個引用注釋,以便開發工具知道在哪里可以找到它;hidden-source-map 則不會在打包的js中增加引用注釋。

但是我們一般不會直接將 .map 文件部署到CDN,因為會直接映射到源碼,更希望將 .map 文件傳到錯誤解析系統,然后根據上報的錯誤信息,直接解析到出錯的源碼位置。

CSS樣式

webpack處理css也必須借助loaderstyle-loader、css-loader
考慮到兼容性問題,還需要 postcss-loader 配合 autoprefixer 插件。
對于Less、Sass,還需要 less-loadersass-loader

// 以 less 為例
npm i style-loader css-loader less-loader postcss-loader autoprefixer less -D
  • style-loader 動態創建 style 標簽,將 css 插入到 head 中;
  • css-loader 負責編譯CSS,處理 @import 等語句;
  • postcss-loaderautoprefixer,自動生成瀏覽器兼容性前綴;
  • less-loader 負責處理編譯 .less 文件,將其轉為 css

配置 webpack.config.js

    test: /\.(le|c)ss$/,
    use: ['style-loader', 'css-loader', {
        loader: 'postcss-loader',
        options: {
            plugins: [require('autoprefixer')({  // 應該配置到 package.json 或 .browserslistrc
                overrideBrowserslist: ['ie >= 8', 'Firefox >= 20', 'Safari >= 5', 
                    'Android >= 4', 'Ios >= 6',
                    'last 4 version'  // 瀏覽器最新的4個版本
                ]
            })]
        }
    }, 'less-loader']

應該將 overrideBrowserslist 配置到package.json

// package.json
"browserslist": [
  "ie >= 8", "Firefox >= 20", "Safari >= 5",
  "Android >= 4", "Ios >= 6", "last 4 version"
]

// webpack.config.js
    test: /\.(le|c)ss$/,
    use: ['style-loader', 'css-loader', {
        loader: 'postcss-loader',
        options: {
            plugins: [require('autoprefixer')]
        }
    }, 'less-loader']

src/index.js 中引入CSS嘗試一下吧:import './base.less';

注意:loader的執行順序 從右向左。另外,loader中還有一個指定優先級的參數enforce --> 值 pre(優先執行),post (滯后執行)

文件處理

CSS中引入圖片、字體等資源文件時,如background-image: url(./a.png),需要使用 url-loaderfile-loader 來處理。
它們功能類似,但url-loader可以限制文件大小,返回DataURL。另外,使用url-loader時也必須安裝file-loader,但不要同時配置,會沖突。

  1. 安裝與配置

    npm install url-loader file-loader -D
    
    //webpack.config.js
        test: /\.(png|jpg|gif|jpeg|webp|svg|eot|ttf|woff|woff2)$/,
        use: [
            {
                loader: 'url-loader',
                options: {
                    limit: 10240, // 10K 的限制
                    esModule: false 
                }
            }
        ]
    
    • limit 資源小于10K時,則轉為base64;超過10K 則將圖片拷貝到dist目錄。base64可以較少網絡請求,但base64數據太大或資源太多,都會導致加載變慢,所以設置limit時要兩者兼顧;
    • esModule 設置為false,否則<img src={require('XXX.jpg')} /> 會出現<img src=[Module Object] />
  2. CSS中引入圖片資源

    body {
        box-sizing: border-box;
        background-image: url(./images/bg.png);
    }
    
  3. 默認情況下,打包生成的資源名是文件內容的 MD5 哈希值,并會保留其擴展名。

    // 圖片默認拷貝到 dist 根目錄
    background-image: url(c5c9a52347e5c5899c2bca539cd016f2.png);
    

url-loader options的更多配置

    • [hash]:內容的哈希值,作為打包后的默認文件名,[hash:6]表示取哈希值的前6位作為文件名;
    • [ext]:資源文件的原擴展名;
    • [name]:資源文件的原名稱;
    • [path]:資源相對于context的路徑,即默認相對于webpack.config.js的路徑。
  • 屬性
    • name:配置打包后的文件名,默認值為 [hash].[ext]
    • context:配置文件的上下文,默認為webpack.config.js
      [path]配合使用可以控制打包后的文件目錄,默認相對于webpack.config.js
      1. 假設 bg.png 存放目錄 src/images,src目錄與webpack.config.js同級別
      2. 配置文件名
         options: {
            name: '[path][name].[ext]'
         }
      3. 編譯打包后,bg.png 輸出目錄 dist/src/images
         background-image: url(src/images/bg.png);
      4. 配置context,假設項目目錄名為webtest
         options: {
             name: '[path][name].[ext]',
             context: '../'  // 向上一級,webpack.config.js上一級目錄是項目目錄
         }
      5. 編譯打包后,bg.png 輸出目錄 dist/webtest/src/images
         background-image: url(webtest/src/images/bg.png);
      
    • publicPath:文件的 public 發布目錄,可以使用該屬性配置資源的CDN路徑
      options: {
          name: '[name]_[hash:6].[ext]',
          publicPath: 'http://www.xxx.com/img'
      }
      
       background-image: url(http://www.xxx.com/img/bg_c5c9a5.png);
      
    • outputPath:文件的 output 輸出目錄,默認會拷貝到dist根目錄
      options: {
          name: '[name].[ext]',
          outputPath: 'assets/'
      }
      // 匹配的資源文件會拷貝到 dist/assets 目錄中
      background-image: url(assets/bg.png);
      

HTML中的本地資源

標簽<img /> 也可以引入圖片資源,但url-loader是不能直接處理的,需要使用html-loader。
html-loader處理HTML中的<img />時,還需要結合url-loader/file-loader 才可以將<img />的路徑正確打包,同時記得配置url-loaderesModule: false

<img src="./imgs/a.png" />

npm i html-loader -D

# webpack.config.js
test: /\.html$/,
loader: 'html-loader',
options: {
    attributes: false,
    // attributes: {
    //     list: [
    //         { tag: 'img', attribute: 'src', type: 'src' },
    //         // ...
    //     ]
    // },
}

除了img src,html-loader還可以處理video src。
但是,html-loader會導致html-webpack-plugin解析HTML中的<% ... %>失敗。

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