webpack的五個核心概念(入口、輸出、loader、插件、模式)


1、入口(Entry)

entry 對象是用於 webpack 查找啟動並構建 bundle。entry 是應用程序的起點入口,從這個起點開始,應用程序啟動執行。如果傳遞一個數組,那么數組的每一項都會執行。入口起點(entry point) 指示 webpack 應該使用哪個模塊,來作為構建其內部依賴圖(dependency graph) 的開始。進入入口起點后,webpack 會找出有哪些模塊和庫是入口起點(直接和間接)依賴的。

簡單規則:每個 HTML 頁面都有一個入口起點。單頁應用(SPA):一個入口起點,多頁應用(MPA):多個入口起點。

默認值是 ./src/index.js,但你可以通過在 webpack configuration 中配置 entry 屬性,來指定一個(或多個)不同的入口起點。例如:

//單入口--字符串
module.exports = {
  entry: './path/to/my/entry/file.js',
};

//多入口--數組
module.exports = {
  entry: ['./src/index.js', './src/add.js']
};

//多入口--對象
module.exports = {
  entry: {
    home: './home.js',
    about: './about.js',
    contact: './contact.js'
  }
};

entry的值類型:

  • 字符串:單入口,打包形成一個chunk,最終只會輸出一個bundle文件,chunk 的名稱默認是 main
  • 數組:多入口,所有的入口文件最終也只會形成一個chunk,最終輸出一個 bundle 文件,chunk 的名稱默認為 main。一般只用在 HMR 功能中讓html熱更新生效
  • 對象:多入口,有多少個 key 就會形成多少個chunk,也就輸出多少個 bundle 文件,每個鍵(key)會是 chunk 的名稱。在對象類型中,每個key的值還可以是一個數組,不僅僅是一個字符串。

 

2、輸出(output)

output 指示 webpack 如何去輸出、以及在哪里輸出你的bundle、asset 和其他你所打包或使用 webpack 載入的任何內容。輸出的 bundle 的默認值是 ./dist/main.js,其他生成文件默認放置在 ./dist 文件夾中。

你可以通過在配置中指定一個 output 字段,來配置這些處理過程:

//webpack.config.js
const path = require('path');

module.exports = { entry: './path/to/my/entry/file.js', output: { path: path.resolve(__dirname, 'dist'), filename: 'my-first-webpack.bundle.js', }, };

我們可以通過 output.filename 和 output.path 屬性,來告訴 webpack bundle 的名稱,以及 bundle 生成到哪里。

 

2.1、output.filename(文件名和目錄)

此選項決定了每個輸出 bundle 的目錄和名稱。這些 bundle 將寫入到 output.path 選項指定的目錄下。

對於單個入口起點,filename 會是一個靜態名稱。然而,當通過多個入口起點(entry point)、代碼拆分(code splitting)或各種插件(plugin)創建多個 bundle,應該使用其他方法來讓每個 bundle 都有一個唯一的名稱。

//單入口時:
module.exports = {
  //...
  output: {
    filename: 'js/bundle.js'
  }
};

//多入口--使用入口名稱:
module.exports = {
  //...
  output: {
    filename: '[name].bundle.js'
  }
};

//多入口--使用每次構建過程中,唯一的 hash 生成
module.exports = {
  //...
  output: {
    filename: '[name].[hash].bundle.js'
  }
};

...

 

2.2、output.path(文件目錄)

output.path 指定所有輸出文件的目錄,即將來所有資源輸出的公共目錄。path 必須是絕對路徑。

module.exports = {
  //...
  output: {
    path: path.resolve(__dirname, 'dist/assets')
  }
};

 

2.3、output.publicPath(引用資源的路徑前綴)

publicPath 指定的是 html 文件中的所有資源引入的公共路徑前綴。它並不會對生成文件的路徑造成影響,而是在 html 文件引入各種資源時,將 publicPath 作為前綴加到引入資源的路徑前面。

實例:

在 vue-cli 生成的 webpack 配置中,生產環境下 publicPath 的值默認是 '/',即當前目錄的根目錄。

 

打包過后,我們打開 html 文件,可以看到 html 文件中引入的資源路徑為:

 可以看到,都在路徑前面加了 / 符號。當我們打開瀏覽器訪問生成的 html 文件時,會發現報錯,資源訪問不到,報404,此時資源的訪問類似如下:

在服務器上可能會是如下,但訪問一樣可能會有問題。

我們可以將 publicPath 修改為相對路徑,或者直接把它注釋掉也行。 

 

2.3.1、path和publicPath的區別

  • path 指定的是打包后文件在硬盤中的存儲位置,是webpack所有文件的輸出的路徑,必須是絕對路徑。比如:輸出的js、圖片,HtmlWebpackPlugin生成的html文件等,都會存放在以path為基礎的目錄下。
  • publicPath 並不會對生成文件的路徑造成影響,主要是對你的頁面里面引入的資源的路徑做對應的補全。

 

2.4、output.chunkFilename(非入口chunk的名稱)

output.chunkFilename 決定了非入口(non-entry) chunk 文件的名稱。也就是除了入口文件生成的chunk外,其他文件生成的chunk文件命名。

module.exports = {
  //...
  output: {
    chunkFilename: 'js/[name]_chunk.js'   //非入口chunk的名稱
  }
};

 

3、loader

webpack 本身只能理解 JavaScript 和 JSON 文件(webpack3+和webpack2+內置可處理JSON文件,但webpack1+並不支持,需要引入json-loader),這是 webpack 開箱可用的自帶能力。loader 讓 webpack 能夠去處理其他類型的文件,並將它們轉換為有效模塊,以供應用程序使用,以及被添加到依賴圖中。loader 可以將文件從不同的語言(如 TypeScript)轉換為 JavaScript 或將內聯圖像轉換為 data URL,loader 甚至允許你直接在 JavaScript 模塊中 import CSS文件!

通過使用不同的loaderwebpack有能力調用外部的腳本或工具,實現對不同格式的文件的處理,比如說分析轉換 scss為css,或者把下一代的JS文件(ES6,ES7)轉換為現代瀏覽器兼容的JS文件。對React的開發而言,合適的Loaders可以把React的中用到的JSX文件轉換為JS文件。

在 webpack 的配置中,loader 有兩個屬性:

  1. test 屬性,識別出哪些文件會被轉換。
  2. use 屬性,定義出在進行轉換時,應該使用哪個 loader。
  3. include/exclude(可選):手動添加必須處理的文件(文件夾)或屏蔽不需要處理的文件(文件夾)
  4. query(可選):為loaders提供額外的設置選項
//示例:webpack.config.js
const path = require('path');

module.exports = {
  output: {
    filename: 'my-first-webpack.bundle.js',
  },
  module: {
    rules: [
        { test: /\.txt$/, loader: 'raw-loader' },
        { test: /\.css$/, use: ['style-loader', 'css-loader'] }   //使用多個loader的話應該用 use
    ], 
  },
};

以上配置中,對一個單獨的 module 對象定義了 rules 屬性,里面包含兩個必須屬性:test 和 use。這相當於告訴 webpack 編譯器在碰到 require()/import 語句中被解析為 '.txt' 的路徑時,在對它打包之前,先使用raw-loader 轉換一下。

使用多個loader的話應該用 use,use 數組中的 loader 執行順序:從右到左,依次執行。比如上面的 css 文件,首先 css-loader 會將 css 文件編譯成 JS 加載到 JS文件中,然后再由 style-loader 創建 style 標簽,將 JS 中的樣式資源插入到 head 標簽中。

 

3.1、CSS-loader

webpack提供兩個工具處理樣式表,css-loader 和 style-loader,二者處理的任務不同。css-loader使你能夠使用類似import的方法來引入 css 文件,style-loader將所有的計算后的樣式加入頁面中,二者組合在一起使你能夠把樣式表嵌入webpack打包后的JS文件中,由此就可以在JS文件中引入css文件了。

//安裝
npm install --save-dev style-loader css-loader  //css-loader版本太高編譯可能會出錯,建議降低版本比如 css-loader@1.0.1 可用
//使用
module.exports = {
   ...
    module: {
        rules: [
            {
                test: /(\.jsx|\.js)$/,
                use: {
                    loader: "babel-loader"
                },
                exclude: /node_modules/
            },
            {
                test: /\.css$/,  //對同一個文件引入多個loader的方法。loader的作用順序是后面的loader先開始作用
                use: [
                    {
                        loader: "style-loader"
                    }, {
                        loader: "css-loader"
                    }
                ]
            }
        ]
    }
};

 

假設有一個 main.css 文件:

body {
  backgroud: green;
}

為了讓webpack能找到”main.css“文件,我們把它導入”main.js “中,如下:

//main.js
import React from 'react';
import {render} from 'react-dom';
import Greeter from './Greeter';
import './main.css';//使用require導入css文件

render(<Greeter />, document.getElementById('root'));

通常情況下,css會和js打包到同一個文件中,並不會打包為一個單獨的css文件。不過通過合適的配置webpack也可以把css打包為單獨的文件的。

 

4、插件(plugin)

loader 用於轉換某些類型的模塊,而插件則可以用於執行范圍更廣的任務,包括:打包優化、壓縮、資源管理、注入環境變量等。插件目的在於解決 loader 無法實現的其他事。

要使用某個插件,我們需要通過npm安裝它,然后在 plugins 屬性下添加該插件的一個實例。由於插件可以攜帶參數/選項,你必須在 webpack 配置中,向 plugins 屬性傳入 new 實例。多數插件可以通過選項自定義,你也可以在一個配置文件中因為不同目的而多次使用同一個插件。

//webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin'); // 通過 npm 安裝
const webpack = require('webpack'); // 用於訪問內置插件

module.exports = { module: { rules: [{ test: /\.txt$/, use: 'raw-loader' }], }, plugins: [new HtmlWebpackPlugin({ template: './src/index.html' })], };

在上面的示例中,html-webpack-plugin 為應用程序生成一個 HTML 文件,並自動注入所有生成的 bundle。

 

4.1、BannerPlugin插件(添加版權說明)

下面我們添加了一個給打包后代碼添加版權聲明的插件。該插件是webpack中的內置插件不用安裝。

const webpack = require('webpack');

module.exports = {
...
    module: {
        rules: [
            {
                test: /(\.jsx|\.js)$/,
                use: {
                    loader: "babel-loader"
                },
                exclude: /node_modules/
            },
            {
                test: /\.css$/,
                use: [
                    {
                        loader: "style-loader"
                    }, {
                        loader: "css-loader",
                        options: {
                            modules: true
                        }
                    }, {
                        loader: "postcss-loader"
                    }
                ]
            }
        ]
    },
    plugins: [
        new webpack.BannerPlugin('wenxuehai版權所有,翻版必究')
    ],
};

 

4.2、Hot Module Replacement 插件(熱加載)

Hot Module Replacement(HMR)是webpack里很有用的一個插件,它允許你在修改組件代碼后,自動刷新實時預覽修改后的效果。熱加載和webpack-dev-server不同,熱替換在應用運行時,無需刷新頁面,便能查看代碼更新后的效果 ,就跟直接在瀏覽器上修改dom樣式一樣,而webpack-dev-server是要刷新頁面的。

(1)在webpack配置文件中添加HMR插件;

(2)在Webpack Dev Server中添加“hot”參數;

 

4.2.1、react實現熱加載

React模塊可以使用Babel實現功能熱加載。Babel有一個叫做react-transform-hrm的插件,可以在不對React模塊進行額外的配置的前提下讓HMR正常工作;

安裝react-transform-hmr

npm install --save-dev babel-plugin-react-transform react-transform-hmr
const webpack = require('webpack');

module.exports = {
    entry: __dirname + "/app/main.js",//已多次提及的唯一入口文件
    output: {
        path: __dirname + "/public",
        filename: "bundle.js"
    },
    devtool: 'eval-source-map',
    devServer: {
        contentBase: "./public",//本地服務器所加載的頁面所在的目錄
        historyApiFallback: true,//不跳轉
        inline: true,
        hot: true
    },
    module: {
        rules: [
            {
                test: /(\.jsx|\.js)$/,
                use: {
                    loader: "babel-loader"
                },
                exclude: /node_modules/
            },
            {
                test: /\.css$/,
                use: [
                    {
                        loader: "style-loader"
                    }, {
                        loader: "css-loader",
                        options: {
                            modules: true
                        }
                    }, {
                        loader: "postcss-loader"
                    }
                ]
            }
        ]
    },
    plugins: [
        new webpack.BannerPlugin('版權所有,翻版必究'),
     new webpack.HotModuleReplacementPlugin()  //熱加載插件 ], };

配置Babel

// .babelrc
{
  "presets": ["react", "env"],
  "env": {
    "development": {
    "plugins": [["react-transform", {
       "transforms": [{
         "transform": "react-transform-hmr",         
         "imports": ["react"],       
         "locals": ["module"]
       }]
     }]]
    }
  }
}
//Greeter,js
import React, {
  Component
} from 'react'
import styles from './main.css'

class Greeter extends Component {
  render() {
    return ( 
        < div>
          <h1>
            aaaf
          </h1>
        </div>
    );
  }
}
export default Greeter
//main.js
import React from 'react';
import {
  render
} from 'react-dom';
import Greeter from './greeter.js';

render( < Greeter / > , document.getElementById('root'));

現在如果我們就可以實現熱加載模塊了,每次保存就能在瀏覽器上直接看到更新內容,瀏覽器不必刷新也不會自動刷新。

(有時候沒有效果可能是版本問題)

 

5、模式(mode)

通過選擇 developmentproduction 或 none 之中的一個,來設置 mode 參數,你可以啟用 webpack 內置在相應環境下的優化。其默認值為 production

module.exports = {
  mode: 'production', };

在配置文件中直接配置 mode 選項將告知 webpack 使用相應模式的內置優化,mode選項有development、production、none。

development : 開發模式,打包的代碼不會被壓縮,開啟代碼調試,
production : 生產模式,則正好反之。

將 mode 設為development或者production,webpack會自動同時也設置 process.env.NODE_ENV 的值,我們可以在任何文件夾中直接拿到該值。但如果只設置 NODE_ENV,則不會自動設置 mode。(在node中,全局變量 process 表示的是當前的node進程。process.env 屬性包含着用戶環境的信息。process.env 本身並不存在NODE_ENV這個屬性,我們一般會自己去定義 NODE_ENV 屬性,用它來判斷是生產環境還是開發環境)

(請注意:mode選項是webpack4新增的,在4之前都是用DefinePlugin插件設置,webpack4把DefinePlugin刪除了)

 

5.1、vue-cli項目mode配置詳解

在 webpack 中,一般都會在配置文件中配置 NODE_ENV 的值。在使用 vue-cli 默認生成的 vue 項目中,NODE_ENV 配置情況如下:

//webpack.dev.conf.js 文件下,引入了 dev.env.js 文件
new webpack.DefinePlugin({
      'process.env': require('../config/dev.env')
}), 
//dev.env.js 文件中
module.exports = merge(prodEnv, {
  NODE_ENV: '"development"'
})
//webpack.prod.conf.js 文件下,引入了 prod.env.js 文件
const env = require('../config/prod.env') new webpack.DefinePlugin({ 'process.env': env }),
//prod.env.js 文件中
module.exports = {
  NODE_ENV: '"production"'
}

從上面可以知道,在開發環境下,配置文件將 NODE_ENV 配置成了 'development';在生產環境下,配置文件將 NODE_ENV 配置成了 'production'。

我們在運行項目時,會執行 npm run dev 或者 npm run build,這兩個命令時使用了開發環境或者生產環境的配置文件來生成運行項目,由此也對應着配置了對應的 NODE_ENV 的值,我們也就能夠在項目的任一文件中(配置文件不一定,因為要看配置了 NODE_ENV 的值的配置文件有沒有生效了才行)獲取到對應的 NODE_ENV 的值。

 

5.2、process.env.NODE_ENV配置

process 是 node 的全局變量,並且 process 有 env 這個屬性,但是沒有 NODE_ENV 這個屬性。NODE_ENV 變量並不是 process.env 直接就有的,而是通過設置得到的,但是 NODE_ENV 變量通常約定用於定義環境類型。這個變量的作用是:我們可以通過判斷這個變量區分開發環境或生產環境。

(1)可以通過webpack的內置插件 DefinePlugin 來設置全局變量值:

new webpack.DefinePlugin({
      'process.env.NODE_ENV': JSON.stringify('production') }),

 設置完后在執行腳本上可以取到該值,比如:

// main.js
console.log(process.env.NODE_ENV);    //production

但是在webpack的配置文件 webpack.config.js 中取不到該值。

(2)通過 cross-env 包設置

先下載 cross-env 包:

cnpm i cross-env -D

設置 package.json 文件:

"build": "cross-env NODE_ENV=test webpack --config webpack.config.js"

此時在配置文件中可以取到該值(process.env.NODE_ENV),但是在可執行腳本中取不到,需要配合DefinePlugin 插件使用

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM