近來得閒,從新擼了一邊webpack文檔,將webpack的用法以及做用基本理清了脈絡,雖然各大前端框架的生態系統愈來愈完善,每一個框架基本都搭配了一套標準的腳手架工具,可是那是搭配好的套餐,用起來雖然也還行,仍是總以爲哪裏不得勁兒,就像開車開了個自動檔模式,不能根據本身的項目爲所欲爲的搭配功能。因此今天咱們就來好好啃一啃webpack,知其然也知其因此然。css
webpack的核心功能就是模塊化打包,既然是打包就必定涉及到entry(入口)和output(出口),剩下的兩個核心概念是loader(加載器)和plugin(插件)以及mode(模式)。html
entry和output就是字面意思上的入口和出口,就是打包過程中讀取代碼的入口以及編譯輸出文件。前端
mode有點相似於半自動擋模式:提供mode配置選項,webpack會使用相應模式的內置功能進行自動優化。就這麼一句話就能夠歸納了模式。至於在哪一種模式下,webpack會開啓那些功能,能夠看以下表:node
選項 | 描述 |
---|---|
development | 會將 process.env.NODE_ENV 的值設爲 development。啓用 NamedChunksPlugin 和 NamedModulesPlugin。 |
production | 會將 process.env.NODE_ENV 的值設爲 production。啓用 FlagDependencyUsagePlugin, FlagIncludedChunksPlugin, ModuleConcatenationPlugin, NoEmitOnErrorsPlugin, OccurrenceOrderPlugin, SideEffectsFlagPlugin 和 UglifyJsPlugin. |
loader是加載器,用於對模塊的源代碼進行轉換。loader 能夠將全部類型的文件轉換爲 webpack 可以處理的有效模塊,而後你就能夠利用 webpack 的打包能力,對它們進行處理。react
在 webpack 的配置中 loader 有兩個目標:webpack
const config = {
module: {
rules: [
{ test: /\.txt$/, use: 'raw-loader' }
]
},
};
複製代碼
以上配置中,對一個單獨的 module 對象定義了 rules 屬性,裏面包含兩個必須屬性:test 和 use。這告訴 webpack 編譯器(compiler) 以下信息:git
「嘿,webpack 編譯器,當你碰到「在 require()/import 語句中被解析爲 '.txt' 的路徑」時,在你對它打包以前,先使用 raw-loader 轉換一下。」github
當你須要對各個文件轉爲webpack可以處理的有效模塊時能夠去loader列表選擇一個合適的加載器web
插件則能夠用於執行範圍更廣的任務。插件的範圍包括,從打包優化和壓縮,一直到從新定義環境中的變量。插件接口功能極其強大,能夠用來處理各類各樣的任務。 想要使用一個插件,你只須要 require() 它,而後把它添加到 plugins 數組中。多數插件能夠經過選項(option)自定義。你也能夠在一個配置文件中由於不一樣目的而屢次使用同一個插件,這時須要經過使用 new 操做符來建立它的一個實例。 當你想用某些實現某些任務優化時能夠去插件列表看沒有現成的插件開箱即用,npm
這裏有個例子能夠說明loader以及plugin用法
const HtmlWebpackPlugin = require('html-webpack-plugin'); // 經過 npm 安裝
const webpack = require('webpack'); // 用於訪問內置插件
const config = {
...
plugins: [
new HtmlWebpackPlugin({template: './src/index.html'})
]
};
module.exports = config;
複製代碼
引入了HtmlWebpackPlugin插件自動生成一個HTML文件。
雖然webpack支持在命令行中手動敲下webpack-cli命中以構建文件,可是通常的項目要複雜的多, 因此webpack支持提供配置文件。這比在終端(terminal)中手動輸入大量命令要高效的多。因此讓咱們建立一個取代以上使用 CLI 選項方式的配置文件:
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
}
};
複製代碼
這是一個最簡單的配置文件,定義了輸入文件與輸出文件,讀取並運行src下的index.js文件將生成的bundle.js輸出到dist文件夾中。 有了這樣的一個配置文件以後,有兩中方式啓動它
npx webpack --config webpack.config.js
這行命令表示以指定的webpack配置文件運行webpack命令若是 webpack.config.js 存在,則 webpack 命令將默認選擇使用它。咱們在這裏使用 --config 選項只是向你代表,能夠傳遞任何名稱的配置文件。這對於須要拆分紅多個文件的複雜配置是很是有用。
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack"
},
複製代碼
如今,可使用 npm run build 命令,來替代咱們以前使用的 npx 命令。
談談我本身對webpack的理解,把webpack看做是要編織一條美麗的珍珠項鍊的話,那麼配置文件則是項鍊的鏈子,而webpack中loader列表和插件列表則至關於原料庫,咱們能夠從原料庫中挑出咱們所須要的珍珠,entry和output則是選擇以及打磨這條項鍊的入口及出口。好了一切準備就緒了,咱們要學習的變成如何打造一條美麗的珍珠項鍊。首先是要學習如何打磨完美的入口和出口。而後咱們要學習應該在什麼地方放上怎樣的珠子,以及如何放上去。
如今咱們能夠從簡單到複雜一步一步來完成一個針對react的從0到1的配置文件:
1,初始化npm項目,生成package.json。
2,添加public/index.html。
3, 安裝Babel,建立.babelrc進行配置。
4,安裝react、react-dom、熱更新react-hot-loader。
5,安裝webpack,建立webpack.config.js進行配置。
6,添加src/index.js、src/App.js、src/App.css。
7,配置package.json,修改啓動命令。
8,啓動項目
建立一個新項目,並cd進入。
$ mkdir webpack-demo
$ cd webpack-demo
複製代碼
建立package.json文件, 若是你想跳過初始化時的問題,加上 -y,以默認選項安裝:
npm init -y
複製代碼
mkdir public
cd public
touch index.html
複製代碼
index.html
<!-- sourced from https://raw.githubusercontent.com/reactjs/reactjs.org/master/static/html/single-file-example.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>React Starter</title>
</head>
<body>
<div id="root"></div>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<script src="../dist/bundle.js"></script>
</body>
</html>
複製代碼
這裏引入的bundle.js後面會用到。
cd ../
npm install -D @babel/core@7.1.0 @babel/cli@7.1.0 @babel/preset-env@7.1.0 @babel/preset-react@7.0.0
touch .babelrc
複製代碼
.babelrc
{
"presets": ["@babel/env", "@babel/preset-react"]
}
複製代碼
npm i react react-dom -S
npm i react-hot-loader -D
複製代碼
npm i webpack webpack-cli webpack-dev-server style-loader css-loader babel-loader --save-dev
touch webpack.config.js
複製代碼
webpack.config.js
const path = require("path");
const webpack = require("webpack");
module.exports = {
entry: "./src/index.js",
mode: "development",
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /(node_modules|bower_components)/,
loader: "babel-loader",
options: { presets: ["@babel/env"] }
},
{
test: /\.css$/,
use: ["style-loader", "css-loader"]
}
]
},
resolve: { extensions: ["*", ".js", ".jsx"] },
output: {
path: path.resolve(__dirname, "dist/"),
publicPath: "/dist/",
filename: "bundle.js"
},
devServer: {
contentBase: path.join(__dirname, "public/"),
port: 3000,
publicPath: "http://localhost:3000/dist/",
hotOnly: true
},
plugins: [new webpack.HotModuleReplacementPlugin()]
};
複製代碼
mkdir src
cd src
touch index.js
touch App.js
touch App.css
複製代碼
index.js
import React from "react";
import ReactDOM from "react-dom";
import App from "./App.js";
ReactDOM.render(<App />, document.getElementById("root"));
複製代碼
App.js
import React, { Component} from "react";
import {hot} from 'react-hot-loader';
import "./App.css";
class App extends Component{
render(){
return(
<div className="App">
<h1> Hello</h1>
</div>
);
}
}
export default hot(module)(App);
複製代碼
App.css
.App {
margin: 1rem;
font-family: Arial, Helvetica, sans-serif;
border: 1px solid red;
}
複製代碼
在package.json裏配置scripts字段。
"scripts": {
"start": "webpack-dev-server --mode development",
"test": "echo \"Error: no test specified\" && exit 1"
}
複製代碼
npm start
複製代碼
在瀏覽器鍵入http://localhost:3000以啓動項目