【webpack 系列】基礎篇
- 2020 年 3 月 29 日
- 筆記
Webpack 基礎篇
基本概念
Webpack
是一個現代 JavaScript
應用程式的靜態模組打包器。當 webpack
處理應用程式時,它會遞歸地構建一個依賴關係圖,其中包含應用程式需要的每個模組,然後將所有這些模組打包成一個或多個 bundle
。
四個核心概念
- 入口(Entry): 構建
Webpack
內部依賴圖的入口模組 - 輸出(Output): 輸出
Webpack
打包好的Bundles
- Loader: 載入器,
Webpack
原生只能解析JavaScript
文件,Loader
讓webpack
擁有了載入和解析非JavaScript
文件的能力。 - 插件(Plugins): 擴展
Webpack
的功能,讓Webpack
具有更多的靈活性。在Webpack
運行的生命周期中會廣播出許多事件,Plugin
可以監聽這些事件,在合適的時機通過Webpack
提供的API
改變輸出結果。
Webpack 基礎配置
初始化項目
新建一個文件夾 webpack-demo
,在該目錄中使用 npm init -y
進行項目初始化。
mkdir webpack-demo && cd webpack-demo npm init -y
運行以下命令安裝最新版本或特定版本
npm i -D webpack npm i -D webpack@<version>
如果你使用 webpack 4+
版本,你還需要安裝 CLI
。
npm i -D webpack-cli
npm i -D
為npm install --save-dev
的縮寫,安裝一個用於開發環境的安裝包npm i -S
為npm install --save
的縮寫,安裝一個要打包到生產環境的安裝包
現在安裝的 webpack
版本號是:
├── [email protected] └── [email protected]
新建 src/index.js
文件:
// src/index.js class HelloComponent { constructor (content = '') { this.content = content; this.render(); } render () { const element = document.createElement('div'); element.innerHTML = this.content; document.body.appendChild(element); } } new HelloComponent('hello webpack');
現在可以直接執行 npx webpack
,默認是 production
模式。
也可以在 package.json
中的 scripts
里配置一個 build
命令,模式指定為 production
。
webpack
默認會將 ./src/index.js
作為入口文件,默認打包到 dist/main.js
。
// ... "scripts": { "build": "webpack --mode=production" } // ...
通過 npm run build
可以執行我們定義的命令,這是可以多了 dist/main.js
文件,這就是打包之後的 js
程式碼。
webpack 配置文件
上面例子中使用的是 webpack
的默認配置,下面我們來定義更加豐富的自定義配置。
根目錄下新建 webpack.config.js
文件
const path = require('path'); module.exports = { mode: 'development', // 模式 entry: path.resolve(__dirname, 'src/index.js'), // 入口文件 output: { path: path.resolve(__dirname, 'dist'), // 輸出目錄 filename: 'bundle.js' // 輸出文件名 } }
更改我們的 build
命令,指定 webpack
按照我們的配置文件來打包文件
"scripts": { "build": "webpack --config webpack.config.js" }
執行 npm run build
可以看到,dist
目錄下新增了 bundle.js
文件。並且 bundle.js
是在開發模式下打包的,可以看到更多的資訊。
html-webpack-plugin 插件
現在我們已經有了打包好的 js
文件了,需要添加個 html
文件來引入這個 js
文件在瀏覽器查看效果了。
在實際開發中,為了避免每次修改打包的 js
文件被瀏覽器快取而看不到最新的程式碼,我們會給打包文件加上 hash
,相當於這個文件的版本號。這樣每次修改後打包的 js
文件名都會不同,如果人工去修改 html
中的 js
文件名就太麻煩了,我們可以藉助 html-webpack-plugin
插件來自動完成這些事情。
安裝 html-webpack-plugin
npm i -D html-webpack-plugin
新建 public/index.html
文件,修改我們的 webpack.config.js
// webpack.config.js const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { //... plugins: [ new HtmlWebpackPlugin({ template: path.resolve(__dirname, 'public/index.html'), // 指定模板文件,不指定會生成默認的 index.html 文件 filename: 'index.html' // 打包後的文件名 }) ] }
執行 npm run build
可以看到 dist
目錄下新增了 index.html
文件,並且自動將打包好的 bundle.js
文件通過 script
標籤引入了。
webpack-dev-server 開發工具
現在可以通過瀏覽器直接打開 dist/index.html
查看了,但是這樣每次改完程式碼我們都需要手動 npm run build
一下,這樣太麻煩了。
我們可以藉助 webpack-dev-server
來解決這個問題。webpack-dev-server
會提供了一個簡單的 web
伺服器,並且能夠實時重新載入。
安裝 webpack-dev-server
npm i -D webpack-dev-server
修改 package.json 文件
// package.json "scripts": { "dev": "webpack-dev-server --config webpack.config.js", "build": "webpack --config webpack.config.js" }
npm run dev
之後,默認會在 localhost:8080
下建立服務,通過訪問這個地址可以訪問到 dist
目錄下的文件。
可以在 webpack.config.js
對 devServer
進行配置
// webpack.config.js module.exports = { // ... devServer: { contentBase: path.join(__dirname, 'dist'), port: '9000', // 指定埠,默認是8080 compress: true // 是否啟用 gzip 壓縮 } //... }
關於 webpack-dev-server
更多的配置可以點擊查看。
mode
我們在 package.json
定義了兩條命令,但是 mode
都為 development
。我們可以通過設置 process.env.NODE_ENV
的值來區分開發還是生產環境。
我們需要安裝一下 cross-env
, 來實現跨平台設置 NODE_ENV
npm i -D cross-env
// package.json "scripts": { "dev": "cross-env NODE_ENV=development webpack-dev-server --config webpack.config.js", "build": "cross-env NODE_ENV=production webpack --config webpack.config.js" }
修改 webpack.config.js
// webpack.config.js const isProduction = process.env.NODE_ENV == 'production'; module.exports = { mode: isProduction ? 'production' : 'development', // 模式 // ... }
設置 mode
的不同值可以啟用相應模式下的 webpack
內置的優化
選項 | 描述 |
---|---|
development |
會將 process.env.NODE_ENV 的值設為 development 。啟用 NamedChunksPlugin 和 NamedModulesPlugin 。 |
production |
會將 process.env.NODE_ENV 的值設為 production 。啟用 FlagDependencyUsagePlugin , FlagIncludedChunksPlugin , ModuleConcatenationPlugin , NoEmitOnErrorsPlugin , OccurrenceOrderPlugin , SideEffectsFlagPlugin 和 UglifyJsPlugin . |
用 babel 向後兼容 js 語法
現在我們的程式碼雖然已經完成了打包,但是並沒有被轉義為低版本的程式碼。我們需要通過 Babel
來將 ECMAScript 2015+
版本的程式碼轉換為向後兼容的 JavaScript
語法,以便能夠運行在當前和舊版本的瀏覽器或其他環境中。
安裝 babel-loader
npm i -D babel-loader
此外我們還需要安裝以下依賴
npm i -D @babel/core @babel/preset-env @babel/plugin-transform-runtime npm i -S @babel/runtime @babel/runtime-corejs3
在 webpack.config.js
配置 babel-loader
// webpack.config.js module.exports = { // ... module: { rules: [ { test: /.jsx?$/, use: ['babel-loader'], exclude: /node_modules/ // 排除 node_modules 目錄 } ] } // ... }
建議給 loader
指定 include
或是 exclude
,排除一些不需要編譯的目錄可以提高編譯效率,比如 node_modules
目錄。
有兩種方式配置 babel
- 通過
.babelrc
文件配置
根目錄下新建一個.babelrc
文件,配置如下:
// .babelrc { "presets": ["@babel/preset-env"], "plugins": [ [ "@babel/plugin-transform-runtime", { "corejs": 3 } ] ] }
- 在
webpack
中配置babel
// webpack.config.js module.exports = { module: { rules: [ { test: /.jsx?$/, use: { loader: 'babel-loader', options: { presets: ["@babel/preset-env"], plugins: [ [ "@babel/plugin-transform-runtime", { "corejs": 3 } ] ] } }, exclude: /node_modules/ } ] } }
通過執行 npm run dev
, 我們查看 http://localhost:9000/bundle.js
發現已經是轉義之後的低版本程式碼了。
使用 source map
當 webpack
打包源程式碼時,會很難追蹤到錯誤和警告在源程式碼中的原始位置。為了更容易地追蹤錯誤和警告,JavaScript
提供了 source map
功能,將編譯後的程式碼映射回原始源程式碼。
在開發環境中,可以設置 devtool
值為 inline-source-map
,生產環境設置為 none
或者 source-map
。
// webpack.config.js const isProduction = process.env.NODE_ENV == 'production'; module.exports = { // ... devtool: isProduction ? 'source-map' : 'inline-source-map', }
使用 source-map
最終會單獨打包出一個 .map
文件,我們可以根據報錯資訊和 map
文件定位到源程式碼。
但是一般不會直接將 .map
文件部署到 CDN
,而是將 .map
文件傳到錯誤監控系統,以便我們可以解析到出錯的源碼位置。
處理樣式文件
webpack
只能處理 js
文件,如果要處理 css
需要藉助 loader
。
如果是 .css
,我們需要的 loader
有: style-loader
、css-loader
,考慮到兼容性問題,還需要 postcss-loader
、autoprefixer
如果是 .less
, 還需要 less-loader
、less
如果是 .sass
的話,還需要 sass-loader
、node-sass
安裝相應的依賴
npm i -D style-loader css-loader postcss-loader autoprefixer less-loader less sass-loader node-sass
webpack.config.js
添加 css
、less
、sass loader
// webpack.config.js module.exports = { // .. module: { rules: [ // ... { test: /.(c|le)ss$/, use: ['style-loader', 'css-loader', 'postcss-loader', 'less-loader'], exclude: /node_modules/ // 排除 node_modules 目錄 }, { test: /.sass$/, use: ['style-loader', 'css-loader', 'postcss-loader', 'sass-loader'], exclude: /node_modules/ // 排除 node_modules 目錄 } ] } }
根目錄新建 postcss.config.js
// postcss.config.js module.exports = { plugins: [ // 兼容瀏覽器,添加前綴 require('autoprefixer')({ overrideBrowserslist: [ "Android 4.1", "iOS 7.1", "Chrome > 31", "ff > 31", "ie >= 8" // 'last 10 versions', // 所有主流瀏覽器最近10版本用 ], grid: true }) ] }
配置完成,新建幾個文件測試一下
/* src/index.css */ div { width: 200px; height: 100px; display: flex; }
// src/index.less @color: yellow; body { background: @color; display: flex; }
// src/index.sass $text-color: orange; div color: $text-color; display: flex;
再在入口文件中引入三個文件
// src/index.css import './index.css'; import './index.less'; import './index.sass';
我們可以看到樣式生效並且 flex
也自動加上瀏覽器前綴了。
需要注意的是 loader
的執行順序是從右向左執行的,執行順序為:
less-loader
/sass-loader
=> postcss-loader
=> css-loader
=> style-loader
less-loader
處理編譯.less
文件,將其轉為css
sass-loader
處理編譯.sass
文件,將其轉為css
postcss-loader
和autoprefixer
,自動生成瀏覽器兼容性前綴css-loader
處理css
中的@import
、url(...)
等語句style-loader
動態創建style
標籤,將css
插入到head
中
處理圖片、字體等媒體文件
我們可以使用 url-loader
或者 file-loader
來處理本地的資源文件。
file-loader
就是將文件在進行一些處理後(主要是處理文件名和路徑、解析文件 url
),將文件移動到輸出的目錄中,同時在 require
文件的地方會返迴文件的絕對路徑。
url-loader
一般與 file-loader
搭配使用,功能與 file-loader
類似,如果文件小於限制的大小,則會返回 base64
編碼。
需要同時安裝 file-loader
和 url-loader
npm i -D file-loader url-loader
配置 webpack.config.js
// webpack.config.js module.exports = { // .. module: { rules: [ // ... { test: /.(jpe?g|png|gif|webp|svg|eot|ttf|woff|woff2)$/i, use: [ { loader: 'url-loader', options: { limit: 10240, // 10K 資源大小小於 10K 時,將資源轉換為 base64,超過 10K,將圖片拷貝到 dist 目錄 name: '[name]_[hash:6].[ext]', // 設置文件名,默認情況下,生成的文件的文件名就是文件內容的 MD5 哈希值並會保留所引用資源的原始擴展名 outputPath: 'assets', // 輸出目錄 esModule: false // 表示是否使用es6模組的導出,默認是啟用的 } } ], exclude: /node_modules/ } ] } }
我們修改 src/index.sass
文件
// src/index.sass $text-color: orange; div color: $text-color; display: flex; background: url('../images/author.jpg');
修改了配置文件,我們重新 npm run dev 一下,可以看到圖片地址已經被替換了
npm run build
可以看到 dist/assets
有這個文件
注意此時如果需要在 html
文件中引用這個圖片需要這樣寫
<body> <img src="<%= require('../images/author.jpg') %>"> </body>
最終打包之後的路徑是
打包前清空 dist 目錄
我們修改文件打包之後,生成的 hash
值和之前 dist
中的不一樣,會導致 dist
下的文件越來越多,所以我們需要在打包前先清空 dist
目錄。
安裝 clean-webpack-plugin
npm i -D clean-webpack-plugin
// webpack.config.js const { CleanWebpackPlugin } = require('clean-webpack-plugin'); module.exports = { //... plugins: [ // ... new CleanWebpackPlugin() ] }
這樣每次打包前就會自動清除 dist
目錄下的文件了。
最後
通過上面的實踐,我們對 webpack
的基礎配置有了一個初步的了解。本文所有程式碼可以查看github。
後續將會繼續推出 webpack
系列的其他內容哦~
喜歡本文的話點個贊吧~
更多精彩內容,歡迎關注微信公眾號~