從零開始搭建React項目
1. Webpack基礎概念
webpack主要作用:
- 代碼轉換:TypeScript 編譯成 JavaScript、SCSS 編譯成 CSS 等。
- 文件優化:壓縮 JavaScript、CSS、HTML 代碼,壓縮合並圖片等。
- 代碼分割:提取多個頁面的公共代碼、提取首屏不需要執行部分的代碼讓其異步加載。
- 模塊合併:在採用模塊化的項目裏會有很多個模塊和文件,需要構建功能把模塊分類合併成一個文件。
- 自動刷新:監聽本地源代碼的變化,自動重新構建、刷新瀏覽器。
- 代碼校驗:在代碼被提交到倉庫前需要校驗代碼是否符合規範,以及單元測試是否通過。
- 自動發佈:更新完代碼後,自動構建出線上發佈代碼並傳輸給發佈系統。
Webpack 有以下幾個核心概念:
- Entry:入口,Webpack 執行構建的第一步將從 Entry 開始,可抽象成輸入。
- Module:模塊,在 Webpack 裏一切皆模塊,一個模塊對應着一個文件。Webpack 會從配置的 Entry 開始遞歸找出所有依賴的模塊。
- Chunk:代碼塊,一個 Chunk 由多個模塊組合而成,用於代碼合併與分割。
- Loader:模塊轉換器,用於把模塊原內容按照需求轉換成新內容。
- Plugin:擴展插件,在 Webpack 構建流程中的特定時機會廣播出對應的事件,插件可以監聽這些事件的發生,在特定時機做對應的事情。
- Output:輸出結果,在 Webpack 經過一系列處理並得出最終想要的代碼後輸出結果。
構建過程:
- 從 Entry 裏配置的 Module 開始遞歸解析 Entry 依賴的所有 Module。
- 每找到一個 Module, 就會根據配置的 Loader 去找出對應的轉換規則。
- 對 Module 進行轉換後,再解析出當前 Module 依賴的 Module。
- 這些模塊會以 Entry 爲單位進行分組,一個 Entry 和其所有依賴的 Module 被分到一個組也就是一個 Chunk。
- 最後 Webpack 會把所有 Chunk 轉換成文件輸出。
- 在整個流程中 Webpack 會在恰當的時機執行 Plugin 裏定義的邏輯。
2. 項目需求
使用Webpack 4.x 搭建項目,滿足以下需求:
- 使用ES6語言
- 使用React框架
- 自動生成HTML
- webpack-dev-server
- 加載樣式(CSS、SCSS)
- 加載靜態資源(圖片、字體)
- 使用第三方庫
- 其他(clean,merge,source map)
3. 實戰
3.1 基礎環境
NodeJS版本10.4.1。
-
初始化生成一個
package.json
文件。npm init -y
-
本地安裝webpack
webpack4版本命名行相關的功能獨立到Webpack-cli。
npm i webpack webpack-cli -D
-
新建webpack.config.js 配置文件。
webpack默認會查找 webpack.config.js 作爲配置文件。 自定義配置文件名稱: webpack --config xx.js
-
基礎配置
webpack配置採用commonJS規範,通過module.export導出一個描述如何構建的 Object 對象。 module.exports={ entry:{}, //入口配置* output:{}, //出口配置* modules:{}, //module.rules loader plugins:[], //插件 devServer:{} //開發服務器 }
3.2 ES6
-
按如下結構創建相應文件與目錄:
│ package.json │ webpack.config.js ├─dist │ index.html └─src main.js
-
main.js 內容:
class Ui { constructor(name, age) { this.name = name; this.age = age; } coding() { return `${this.name} is coding`; } } let ui= new Ui('hqz','18'); document.getElementById('app').innerText=ui.coding();
-
index.html 內容:
<html> <head> <meta charset="UTF-8"> <title>ES6</title> </head> <body> <div id="app"></div> <script src="bundle.js"></script> //手動引入打包後的JS </body> </html>
-
webpack配置
const path=require("path"); module.exports={ entry:'./src/main.js', //入口文件 output:{ //出口配置 filename: 'bundle.js', //出口文件名 path: path.resolve(__dirname, 'dist') //出口文件路徑 }, module: {}, //module.rules loader plugins:[], //插件 devServer:{} //開發服務器 };
目前瀏覽器對ES6的標準支持不全,所以我們需要把ES6轉換成ES5,包含以下兩件事:
- 把新的 ES6 語法用 ES5 實現。
- 給新的 API 注入 polyfill。
babel可以方便的完成以上2件事。 babel-preset-env的工作方式類似於babel-preset-latest,但它允許您指定環境並僅轉換該環境中缺少的功能。
babel 7.X的主要變更:
- 刪除對未維護的 Node 版本的支持:0.10,0.12,2,5
- 刪除“Stage” & 年度預設(preset-es2015 等), 用@babel/preset-env 取代。
- 對部分軟件包進行重命名(e.g. babel-core–>@babel/core)
簡單升級:
-
利用babel-upgrade
npm i babel-upgrade -g babel-upgrade --write
-
重新安裝包
-
修改配置文件中的包名
配置:
-
本地安裝Babel
npm i -D @babel/core @babel/preset-env babel-loader @babel/plugin-transform-object-rest-spread @babel/plugin-transform-export-extensions @babel/plugin-transform-class-properties @babel/plugin-syntax-dynamic-import
-
配置webpack
在module.rules中添加如下: module: { rules: [{ test: /\.js$/, //使用正則匹配所有需要使用babel-loader的文件 use: { loader: "babel-loader", //指明要使用的loader options: { //傳入loader的參數 presets: [ //用於解析一組語法特性 [ "@babel/preset-env", //包含當前所有 ECMAScript 標準裏的最新特性 { "targets": { //指定需要兼容的瀏覽器類型和版本 "browsers": [ "> 1%", //支持市場份額超過1%的瀏覽器。 "ie >= 9" //支持IE9以上的版本 ] } } ] ], plugins: [ //用於解析某個語法特性 "@babel/plugin-proposal-object-rest-spread", //解析對象的擴展運算符(ES2018) "@babel/plugin-proposal-export-default-from", //解析額外的export語法:export v from "xx/xx" "@babel/plugin-proposal-export-namespace-from", //解析額外的export語法:export v as vv from "xx/xx"; "@babel/plugin-proposal-class-properties", //解析class中的靜態屬性 "@babel/plugin-syntax-dynamic-import" //解析import方法 ] } } }] },
-
執行編譯
package.json 的 script字段添加如下: "build": "webpack" 執行 npm run build
編譯完成後,dist文件夾下多出一個bundle.js文件,打包成功。
3.3 React
-
本地安裝react
npm i -D react react-dom
-
mian.js
import React, { Component } from 'react'; import ReactDOM from 'react-dom'; class App extends Component { render() { return <h1>Hello word!</h1> } } ReactDOM.render(<App />, document.getElementById('app'));
Babel也可用於解析JSX,需要使用babel-preset-react。
-
本地安裝babel-preset-react
npm i -D @babel/preset-react
-
配置webpack (在ES6環境的基礎上)
在module.rules中添加如下: module: { rules: [{ test: /\.js$/, use: { loader: "babel-loader", options: { presets: ["@babel/preset-env","@babel/preset-react"] //用於解析ES6+React } } }] },
3.4 自動生成HTML
使用html-webpack-plugin 自動生成HTML,並引入相應文件。
-
將index.html移到src目錄下,並重命名:
│ package.json │ webpack.config.js ├─dist └─src main.js template.html
-
template.html 內容:
<html> <head> <meta charset="UTF-8"> <title>自動生成HTML</title> </head> <body> <div id="app"></div> //去掉手動引入script </body> </html>
-
本地安裝
npm i -D html-webpack-plugin
-
webpack配置
const HtmlWebpackPlugin = require('html-webpack-plugin'); //引入插件 plugins:[ new HtmlWebpackPlugin({ template:'./src/template.html', //html模板 }) ]
-
編譯
-
編譯後生成的HTML
<html> <head> <meta charset="UTF-8"> <title>自動生成HTML</title> </head> <body> <div id="app"></div> <script type="text/javascript" src="bundle.js"></script> //自動引入JS文件 </body> </html>
如果單頁應用中需要多個頁面入口,或者多頁應用時配置多個html時,那麼就需要實例化該插件多次。
-
目錄結構要求:
│ package.json │ webpack.config.js │ template.html ├─dist └─src ├─A │ A.js └─B B.js
-
配置webpack
const HtmlWebpackPlugin = require('html-webpack-plugin'); //引入插件 entry: { //多入口使用對象形式配置,chunk名稱爲key值 A: './src/pages/A/A.js', B: './src/pages/B/B.js', }, output:{ filename:'[name].bundle.js', //[name]代表chunk名稱 path: path.resolve(__dirname, 'dist') }, plugins:[ new HtmlWebpackPlugin({ chunks: ['A'], //要引入的chunk filename:'A.html', //生成的文件名 template:'template.html', //模板文件 }), new HtmlWebpackPlugin({ chunks:['B'], filename:'B.html', template:'template.html' }), ]
-
編譯後的目錄結構
│ package.json │ webpack.config.js │ template.html ├─dist | A.bundle.js | A.html │ B.bundle.js │ B.html └─src ├─A │ A.js └─B B.js
3.5 Webpack-dev-server
webpack-dev-server提供了一個簡單的服務器,用於訪問 webpack 構建好的靜態文件,我們日常開發時可以使用它來調試前端代碼。 webpack-dev-server將構建好的項目存在內存中。
DevServer 支持模塊熱替換, 可在不刷新整個網頁的情況下實時預覽頁面。 原理是當一個源碼發生變化時,只重新編譯發生變化的模塊,再用新輸出的模塊替換掉瀏覽器中對應的老模塊。
-
本地安裝devServer
npm i -D webpack-dev-server
-
配置webpack:
const webpack = require('webpack'); plugins: [ new webpack.HotModuleReplacementPlugin() //啓用 HMR (webpack 4) ], devServer:{ hot: true, //開啓模塊熱替換 contentBase: './dist', //將dist目錄下的文件,作爲額外可訪問文件 host: '0.0.0.0', //DevServer 服務監聽的地址,默認是localhost。 當需要同個局域網可訪問你的服務時,可設成0.0.0.0 port: 3000, //DevServer 服務監聽的端口,默認8080 https: false, //是否使用HTTPS服務 open: true //自動打開網頁,地址是host:port }, 只有在通過 DevServer 去啓動 Webpack 時配置文件裏 devServer 纔會生效, 因爲這些參數所對應的功能都是 DevServer 提供的,Webpack 本身並不認識 devServer 配置項。
-
執行編譯
package.json 的 script字段添加如下: "start": "webpack-dev-server" 執行 npm start
3.5.2 open-browser-webpack-plugin
-
本地安裝
npm i -D open-browser-webpack-plugin
-
配置webpack
const OpenBrowserPlugin = require('open-browser-webpack-plugin'); plugins: [ new OpenBrowserPlugin({ url: 'http://192.168.1.87:3000/A.html' }) //開啓服務後,自動打開的地址 ]
-
執行 npm start後,會自動打開http://192.168.1.87:3000/A.html頁。
3.6 加載樣式
webpack本身只認得JS文件,其他非JS文件需要用loader進行轉換。
處理css文件,需要用到以下兩個loader:
- css-loader 負責解析 CSS 代碼,主要是爲了處理 CSS 中的依賴,例如 @import 和 url() 等引用外部文件的聲明。
- style-loader 會將 css-loader 解析的結果轉變成 JS 代碼,運行時動態插入 style 標籤來讓 CSS 代碼生效。
-
本地安裝loader
npm i -D css-loader style-loader
-
配置webpack
module: { rules: [ { test: /\.css$/, //使用正則匹配所有需要使用此loader的文件 use: [ //先由css-loader處理後,在交給style-loader處理 'style-loader', { loader:'css-loader', options:{ //傳入css-loader的參數 minimize:true, //是否壓縮css代碼 module:true //是否使用css module } } ] }, ] },
先將SCSS轉成CSS,後續處理同上。
-
本地安裝
npm i -D sass-loader node-sass
-
配置webpack
module: { rules: [ { test: /\.scss$/, //使用正則匹配所有需要使用此loader的文件 use: ['style-loader','css-loader', 'sass-loader'] //處理順序:sass-loader->css-loader->style-loader }, ] },
3.7 加載靜態資源
file-loader, url-loader可用於處理圖片,字體等靜態資源。
url-loader封裝了file-loader:
- 文件大小小於limit參數時,url-loader將會把文件轉爲DataURL。
- 文件大小大於limit,url-loader會調用file-loader進行處理。
-
本地安裝loader
npm i -D file-loader url-loader
-
配置webpack
module: { rules: [ { test: /\.(png|svg|jpg|gif)$/, use: [{ loader: 'url-loader', options: { limit: 1024 * 30, //30KB 以下的文件採用 url-loader fallback: 'file-loader', //否則採用 file-loader,默認值就是 file-loader outputPath: 'images', //圖片輸出路徑,相對於output.path } }] }, { test: /\.(eot|ttf|woff|svg)$/, use: [{ loader: 'url-loader', options: { limit: 1024 * 30, //30KB 以下的文件採用 url-loader fallback: 'file-loader', //否則採用 file-loader,默認值就是 file-loader outputPath: 'fonts', //字體輸出路徑 } }] }, ] },
-
編譯後:
- 大於30KB的資源,用file-loader處理,複製到dist/images目錄下。
- 小於30KB的資源,和js一起打包,形成dataURL形式。
將不需要webpack處理的靜態資源(e.g. favicon),原樣輸出到指定目錄下。
-
本地安裝
npm i copy-webpack-plugin -D
-
配置webpack
const CopyWebpackPlugin = require('copy-webpack-plugin'); plugins:[ new CopyWebpackPlugin([{ from: './src/assets/public', // 將此目錄下的文件 to:'./public' // 輸出到此目錄,相對於output.path目錄 }]) ]
-
編譯後 src/assets/public 下的文件將原封不動的輸出到 dist/public 目錄下。
3.8 第三方庫
通過ProvidePlugin引用某些模塊作爲應用運行時的變量,從而不必每次都用 require
或者 import
, 是內置的插件。
-
安裝jquery
npm i -D jquery
-
配置webpack
plugins:[ new webpack.ProvidePlugin({ $: 'jquery', }) ]
-
在JS文件中就可直接使用jquery,不用導入。
3.9 其他
webpack打包的文件都放在dist文件夾下,但webpack無法追蹤到哪些文件是實際項目中用到的。所以建議在每次構建前都清理下dist文件夾。
-
本地安裝插件
npm i -D clean-webpack-plugin
-
配置webpack
const CleanWebpackPlugin=require('clean-webpack-plugin'); new CleanWebpackPlugin(['dist'])
實際項目開發中,一般會有三份配置文件:
- webpack.dev.js 開發環境:devserver配置,生成SourceMap
- webpack.prod.js 生產環境:壓縮代碼
- webpack.common.js 公共配置
可使用webpack-merge合併配置。
-
本地安裝
npm i -D webpack-merge
-
拆分webpack配置
webpack.prod.js: const merge = require("webpack-merge"); const common = require("./webpack.common.js"); //引入公共配置 const CleanWebpackPlugin = require("clean-webpack-plugin"); module.exports = merge(common, { //合併配置 plugins: [ new CleanWebpackPlugin(["dist"]) ], });
-
修改package.json的script字段
"build": "webpack --config webpack.prod.js", "start": "webpack-dev-server --config webpack.dev.js"
React, ES6等經過webpack轉換後,代碼可讀性非常差,不利於在瀏覽器中調試代碼。可通過加載 Source Map 文件,在瀏覽器中調試源碼。
各種source map的差異見:https://github.com/webpack/webpack/tree/master/examples/source-map
devtool: "cheap-module-eval-source-map" //開發環境
4. 優化
4.1 模式
我們在日常的前端開發工作中,一般都會有開發&生產兩套構建環境。
webpack 4.X新增用mode字段指定當前環境,並啓用相應模式下的webpack內置的優化。
module.exports = {
mode: 'production'
};
選項 | 描述 |
---|---|
development (開發環境) | process.env.NODE_ENV =development 並啓用以下插件: NamedChunksPlugin , NamedModulesPlugin |
production (生產環境) | process.env.NODE_ENV =production 並啓用以下插件: FlagDependencyUsagePlugin , FlagIncludedChunksPlugin , ModuleConcatenationPlugin , NoEmitOnErrorsPlugin , OccurrenceOrderPlugin , SideEffectsFlagPlugin , UglifyJsPlugin |
可通過optimization字段,手動配置或覆蓋mode配置。
4.2 代碼壓縮
將mode設置成production,在此模式下optimization.minimize默認爲true, webpack 會自動調用UglifyjsWebpackPlugin壓縮JS代碼。
若不需要壓縮,可將optimization.minimize設置爲false。
-
配置webpack.prod.js
mode: "production"
optimization: { // minimize:false, //若不需要進行壓縮,可使用此句禁用 // minimizer: [ //用於指定使用的壓縮插件或自定義UglifyjsWebpackPlugin插件配置,覆蓋默認配置。 // new UglifyJsPlugin({ /* your config */ }) // ] },
3.7章節介紹瞭如何加載樣式,但加載後的樣式是寫在JS文件中的。隨着項目越來越大,js文件也會越來越大,所以,我們就需要對css文件進行分離並壓縮。
-
本地安裝插件
npm i -D mini-css-extract-plugin optimize-css-assets-webpack-plugin uglifyjs-webpack-plugin
-
配置webpack:分離css文件
webpack.common.js: const MiniCssExtractPlugin = require("mini-css-extract-plugin"); module.exports = { plugins: [ new MiniCssExtractPlugin({ filename: "[name].css", // [name]爲chunk名稱 }) ], module: { rules: [ { test: /\.(sc|c)ss$/, use: [ MiniCssExtractPlugin.loader, // style-loader改用MiniCssExtractPlugin 'css-loader', 'sass-loader', ], } ] } } 注:在Webpack4上用extract-text-webpack-plugin會出錯,可以安裝beta版本extract-text-webpack-plugin@next。
-
配置webpack:壓縮css文件
webpack.prod.js: const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin"); //用於壓縮CSS代碼 const UglifyJsPlugin = require("uglifyjs-webpack-plugin"); //用於壓縮JS代碼 optimization: { minimizer: [ new UglifyJsPlugin({}), new OptimizeCSSAssetsPlugin({}) //壓縮css ] },
HtmlWebpackPlugin支持壓縮輸出的HTML文件。
plugins:[
new HtmlWebpackPlugin({
template:'src/index.html',
minify:{ //壓縮輸出
collapseWhitespace:true //摺疊空白區域
minifyCSS: true, // 壓縮 HTML 中出現的 CSS 代碼
minifyJS: true // 壓縮 HTML 中出現的 JS 代碼
}
})
]
-
本地安裝
npm i -D image-webpack-loader
-
配置webpack
{ test: /\.(png|svg|jpg|gif)$/, use: [ { loader: 'url-loader', options: { limit: 1024 * 30, fallback: 'file-loader', outputPath: 'images', } }, 'image-webpack-loader' // 壓縮圖片 ] },
4.3 Tree Sharing
Tree Shaking 可以用來剔除 JavaScript 中用不上的代碼。
Tree Shaking要求:
- 必須遵循 ES6 的模塊規範(即 import 和 export)。
- 在項目
package.json
文件中,添加一個 “sideEffects” 屬性。作用於引入重導出的模塊。 - 引入一個能夠刪除未引用代碼(dead code)的壓縮工具(minifier)(例如
UglifyJSPlugin
)。
-
新建util.js
export funcA from './TESTA' export funcB from './TESTB' TESTA.js: import React from 'react'; export default function funcA() { return <h1>I'm funcA</h1>; } console.log("funcAfuncAfuncA") // 副作用代碼 TESTB.js: import React from 'react'; export default function funcB() { return <h1>I'm funcB</h1>; } console.log("funcBfuncBfuncB") // 副作用代碼
-
在A.js中引用util.funcA,則funcB就是用不上的代碼。
import {funcA} from '../util';
-
修改babel-loader配置。
presets: [ [ "env", {modules: false} // 關閉Babel的模塊轉換功能,保留原本的ES6模塊化語法 ], "react" ]
-
package.json添加sideEffects字段: 將文件標記爲無副作用
"sideEffects": ["*.css","*.scss"] //避免樣式文件被刪除 注:此字段是webpack4新增的。
-
開啓壓縮,參照4.2.1
執行編譯後,A.bundle.js無funcB相關代碼。
4.4 提取公共代碼
提取公共代碼的原理:用戶第一次訪問頁面後,頁面公共代碼的文件已經被瀏覽器緩存起來。用戶切換到其它頁面時,存放公共代碼的文件就不會再重新加載,而是直接從緩存中獲取。 加快了其他頁面的訪問速度,減少了網絡傳輸流量。
3種拆分方案瀏覽器的加載情況:
- 未拆分代碼:所有代碼被打包到一個JS文件中,當修改了代碼,瀏覽器就得重新加載所有代碼。
- 拆分公共代碼:代碼被打包成兩個JS文件(公共代碼 & 其餘代碼),修改其中一個文件的代碼,瀏覽器只需要重新加載修改的代碼文件。但第三方庫的代碼也屬於公共代碼,若修改了非第三方庫的公共代碼,那麼瀏覽器也會重新加載不常變更的第三方庫代碼。
- 拆分公共代碼&第三方庫:代碼被打包成3個以上的JS文件。
mode=production時,webpack 4.X 會默認對代碼進行拆分,拆分的規則是:
- 模塊被重複引用,或者是來自
node_modules
中的模塊。 - 文件大於30kb。
- 在按需加載時,請求數量小於等於5。
- 在初始化加載時,請求數量小於等於3。
注:webpack 4.x 通過optimization.splitChunks配置, webpack 4.x之前可用CommonsChunkPlugin。
默認配置:
optimization: {
splitChunks: {
chunks: "async", //異步加載的模塊
minSize: 30000, //模塊大於30k會被抽離到公共模塊
minChunks: 1, //被引用超過1次
maxAsyncRequests: 5, //異步模塊,一次最多隻能被加載5個
maxInitialRequests: 3, //入口模塊最多隻能加載3個
},
},
}
拆分公共代碼配置:
optimization: {
splitChunks: {
chunks: "all", // 所有的 chunks 代碼公共的部分分離出來成爲一個單獨的文件
},
},
}
拆分公共代碼&第三方庫配置:
- 根據所使用的技術棧,找出所有頁面都需要用到的基礎庫。如react、react-dom 等庫,把它們提取到一個單獨vendor.js文件。
- 在剔除了各個頁面中被 vendor.js 包含的部分代碼外,再找出所有頁面都依賴的公共部分的代碼提取出來放到 common.js中去。
- 再爲每個頁面都生成一個單獨的文件,這個文件中不再包含 vendor.js 和 common.js中包含的部分,而只包含各個頁面單獨需要的部分代碼。
optimization: {
splitChunks: {
cacheGroups: { //緩存組
commons: { //提取入口文件之間的公共代碼
chunks: 'all', //塊的範圍,有三個可選值:initial、async、all,默認爲all
minChunks: 2, //被引用次數
minSize: 0, //文件大小
name: "common" //拆分出來塊的名字
},
vendor: {
chunks: "all",
test: /node_modules/,//控制哪些模塊被這個緩存組匹配到
name: "vendor",
priority: 10,
},
},
}
}
new HtmlWebpackPlugin({
chunks: ['A','common','vendor'], //引入拆分出來的chunk
filename:'A.html',
template:'template.html',
minify:{
collapseWhitespace:true
}
}),
編譯完成後dist目錄結構如下:
│ A.bundle.js
│ A.css
│ A.html
│ B.bundle.js
│ B.css
│ B.html
│ common.bundle.js //A和B之間的公共代碼
│ vendor.bundle.js //node_modules下的第三方依賴代碼
├─images
│ 230280f0ff880c8273b99fdae150dc96.png
└─public
ico.png
4.5 優化loader配置
使用 Loader 時可以通過 test 、 include、 exclude 三個配置項來命中 Loader 要應用規則的文件。 爲了儘可能少的讓文件被 Loader 處理,可以通過 include && exclude 去命中/排除文件。
示例配置:
babel-loader:
exclude: path.resolve(__dirname, 'node_modules')
url-loader:
include: path.resolve(__dirname, 'src/assets')
4.6 Resolve
Webpack 在啓動後會從配置的入口模塊出發找出所有依賴的模塊,Resolve 配置 Webpack 如何尋找模塊所對應的文件。
模塊引入方式:
import * as m from './index.css' // 相對路徑
import React from 'react' // 模塊名
- 解析相對路徑
- 查找相對當前模塊的路徑下是否有對應文件或文件夾
- 是文件則直接加載
- 是文件夾則繼續查找文件夾下的 package.json 文件
- 有 package.json 文件則按照文件中 browser/module/main 字段的文件名來查找文件 (配置項:resolve.mainFields )
- 無 package.json 或者無 browser/module/main字段則查找 index.js 文件 (配置項:resolve.mainFiles)
- 解析模塊名
- 查找當前文件目錄下,父級目錄及以上目錄下的 node_modules 文件夾,看是否有對應名稱的模塊 (配置項:resolve.modules)
resolve.modules 用於配置 Webpack 去哪些目錄下尋找第三方模塊。
默認值是 [‘node_modules’],作用:
- 先去當前目錄下的node_modules目錄下查找第三方模塊。
- 如果沒找到,就去上級目錄的node_modules目錄下查找。
- 還是沒找到,再去上級的node_modules目錄下查找。
- 直到根目錄。
通常安裝的第三方模塊都放在項目根目錄下的 node_modules 目錄下,就沒有必要按照默認的方式去一層層的尋找,可以指明存放第三方模塊的絕對路徑,以減少尋找。
配置如下:
module.exports = {
resolve: {
// 使用絕對路徑指明第三方模塊存放的位置,以減少搜索步驟
modules: [path.resolve(__dirname, 'node_modules')]
},
};
resolve.extensions 用於配置在嘗試過程中用到的後綴列表,默認是:
extensions: ['.js', '.json']
如果這個列表越長,或者正確的後綴在越後面,就會造成嘗試的次數越多。
建議:
- 後綴嘗試列表要儘可能的小,不要把項目中不可能存在的情況寫到後綴嘗試列表中。
- 頻率出現最高的文件後綴要優先放在最前面,以做到儘快的退出尋找過程。
- 在源碼中寫導入語句時,要儘可能的帶上後綴,從而可以避免尋找過程。例如在你確定的情況下把
require('./data')
寫成require('./data.json')
。
相關 Webpack 配置如下:
module.exports = {
resolve: {
// 儘可能的減少後綴嘗試的可能性
extensions: ['.js'],
},
};
resolve.alias
配置項通過別名來把原導入路徑映射成一個新的導入路徑,可簡化import/require路徑。
resolve:{
alias:{
'assets': path.resolve(__dirname, 'src','assets')
//把導入語句裏的 assets 關鍵字替換成 根目錄/src/assets/
}
}
import img from 'assets/images/1.png'
==> import img from 'xx/src/assets/images/1.png'
。
css中的url不支持。
解決在webstorm中無法解析alias後的路徑:
- 在webpack settings中指定webpack配置文件的路徑。
- 由於我們腳手架的webpack配置中resolve.alias配置是直接寫在alias字段下的,webstorm無法解析。所以需要在配置文件中配置resolve.alias字段。
4.7 Hash
在打包出來的文件名上加上文件內容的hash
是目前最常見的有效使用瀏覽器長緩存的方法,js文件如果有內容更新,hash
就會更新,瀏覽器請求路徑變化所以更新緩存,如果js內容不變,hash
不變,直接用緩存。
hash類型:
- hash 跟整個項目的構建相關,只要項目裏有文件更改,整個項目構建的hash值都會更改,並且全部文件都共用相同的hash值。
- chunkhash 和hash不一樣,它根據不同的入口文件(Entry)進行依賴文件解析、構建對應的chunk,生成對應的哈希值。
- contenthash 更細緻地根據內容更改,生成對應的哈希值。爲css文件生成獨立的hash,讓css文件不受js文件的影響。
webpack配置:
output:{
filename:'[name].[chunkhash:8].bundle.js',
path: path.resolve(__dirname, 'dist')
},
plugins:[
new MiniCssExtractPlugin({
filename: "[name].[contenthash:8].css",
}),
],
4.8 遺留
-
安裝
yarn add react-hot-loader
-
webpack配置
1.使用HotModuleReplacementPlugin插件 plugins: [ new webpack.HotModuleReplacementPlugin(), ], 2.開啓devServer的模塊熱替換 devServer:{ hot: true, //開啓模塊熱替換 }, 3.在babel-loader中使用react-hot-loader/babel插件 plugins: ["react-hot-loader/babel"
-
import React, { Component } from 'react'; import { AppContainer } from 'react-hot-loader'; import ReactDOM from 'react-dom'; require('./a.css'); import Test from './Test'; function render(RootElement) { ReactDOM.render( <AppContainer> <RootElement /> </AppContainer>, document.getElementById('app') ); } render(Test); if (module.hot) { module.hot.accept('./Test', () => { render(Test); }); }
proxy 用於配置 webpack-dev-server 將特定 URL 的請求代理到另外一臺服務器上。例如:
proxy: {
'/api/test': {
target: "http://localhost:3000", // 將URL中帶有/api/test的請求代理到本地的3000端口的服務上
pathRewrite: { '^/api': '' }, // 把URL中path部分的api移除掉
},
}
- 請求到
/api/test
會被代理到請求http://localhost:3000/api/users
。 - 請求到
/api/test
會被代理到請求http://localhost:3000/users
。
想要整理更多的碎片知識,掃碼關注下面的公衆號,讓我們在哪裏接着嘮!