---
tags: 前端工具
---
【筆記】Webpack
===
[教學文](https://medium.com/@Mike_Cheng1208/%E4%BB%80%E9%BA%BC%E6%98%AFwebpack-%E4%BD%A0%E9%9C%80%E8%A6%81webpack%E5%97%8E-2d8f9658241d)
[中文手冊](https://webpack.docschina.org/configuration/output/)
| | gulp | webpack |
| ---- | --------------------------- | --------------------------------------- |
| 短詞說明 | 自動化工具 | 模組化打包工具 |
| 工作內容 | 前端、開發雜事自動化,用於繁瑣的任務管理,簡化前端流程 | 當前端引入大量模組 (CSS、JS)時,可透過 Webpack 來作模組化開發 |
| 適合範圍 | 廣 | 較小 |
| 輸出 | 依據需求調整 | 全部都打成一包 |
# 專案結構
一般我們的專案會有兩個很重要的資料夾src與dist,這兩個資料夾是什麼?
* src : 專門放我們Preprocess的檔案,包括es6、pug、sass、vue、jsx等檔案,這個資料夾不會丟上去server部署。
* dist : 經過webpack編譯打包後,產生出瀏覽器看得懂的html、css、js,要部署也是這個資料夾去部署。
# webpack
- 想引入npm第三方套件, 瀏覽器對es6支援度低
- 把每個資源都看成模組
- 是一個以自己編寫設定檔然後透過指令去驅動的一個自動化工具
- 我們必須透過javascript的引入語法讓webpack知道需要它幫忙編譯什麼東西,例如pug或是sass等
- 進入點的js專門注入(import)那些Preprocess,讓那些Preprocess可以通過這個進入點的js讓webpack去編譯它,進而打包到dist資料夾
## 安裝webpack
* npm init
產生package.json
npm init -y
不想回答那些問題時
* npm install webpack webpack-cli --save-dev
npm install後產生node_modules
[Pug and Webpack](https://medium.com/@NorthBei/%E5%A6%82%E6%9E%9C%E4%BD%A0%E6%98%AF%E5%B8%B8%E5%88%87%E7%89%88%E7%9A%84%E5%89%8D%E7%AB%AF%E5%B7%A5%E7%A8%8B%E5%B8%AB-%E4%BD%A0%E4%B8%80%E5%AE%9A%E8%A6%81%E7%9F%A5%E9%81%93pug-8b2cbc0a784c)
## 執行webpack
- 新版本webpack**必須透過npm script**才可以執行
# 實作 Webpack 打包
webpack 的主要概念如下(也是我們在 config 裡要設定的參數):
- 我們使用的 ./ 是一種相對路徑, 代表**現在所在的資料夾**
- entry: 待打包的 source 從哪裡來 ,可放object
- context: 指定所有entry的資料夾
```javascript
context: path.resolve(__dirname, 'src'),
entry: {
index: './index.js',
about: './about.js'
}
```
- output: 打包完要匯出到哪
- bundle, assets和其它你所打包或使用webpack載入的任何內容
- __dirname: nodejs中的特殊變數,當前執行目錄之所在目錄的完整目錄位置
- [name] 會依照entry的名稱來更改output的名稱
```javascript
//例子
entry: {
cc_index: './index.js'
},
output: {
filename: '[name].js'
}
// result: 產生 cc_index.js
```
```javascript
// [name] 會依照entry的name來更改output
output: {
path: path.resolve(__dirname,"dist"),
filename: '[name].js'
}
```
- module
rules: 每個loader的規則
```javascript
// test 正規表示式
module: {
rules: [{
test: /\.css$/,
use: ['style-loader', 'css-loader']
}]
}
```
loader: 將 webpack 看不懂的檔案轉成 webpack 看的懂 js, loader由後往前執行
- plugins: 使用什麼套件來處理這些檔案, 處理loader無法處理的事s
```javascript
plugins: [
extractCSS
]
```
```javascript
// webpack.config.js
var path = require('path')
module.exports = {
entry: ['./src/index'],
output: {
path: path.join(__dirname, 'dist'),
filename: 'bundle.js'
}
```
### Path
#### path.resolve()
- 將「相對路徑」或「路徑片段」解析成絕對路徑
- __dirname: nodejs中的特殊變數,當前執行目錄之所在目錄的完整目錄位置
### Resolve 路徑可縮寫
resolve: 調整模組與**entry的路徑**
- modules: 在webpack加上resolve module在**引入模塊**時,可以省略路徑
- extensions: 可以省略副檔名,因檔名重覆會出現問題,建議只省略js
> output不可使用縮寫
> 僅限開發
```javascript
// webpack config js
resolve: {
modules: [
path.resolve('src'),
path.resolve('src/js'),
path.resolve('src/scss'),
// 沒加這行 webpack-dev-server會讀不到自己
path.resolve('node_modules')
],
extensions: ['.js']
}
```
# webpack 套件 & loader
- loader執行順序: **由後往前執行**
#### cross-env (windows開發者需加入)
```
npm i -D cross-env
```
NODE_ENV為node.js其中一個環境變數
```
NODE_ENV
```
```javascript
process.env.NODE_ENV
```
#### extract-text-webpack-plugin: 獨立拆分出檔案 (webpack-4 以前)
```javascript
// 安裝 非release版
npm install --save-dev extract-text-webpack-plugin@next
//用法 webpack.config.js
const ExtractTextPlugin = require('extract-text-webpack-plugin');
// Create multiple instances
const extractCSS = new ExtractTextPlugin('css/[name].css');
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: extractCSS.extract([ 'css-loader', 'postcss-loader' ])
},
{
test: /\.less$/i,
use: extractLESS.extract([ 'css-loader', 'less-loader' ])
},
]
},
plugins: [
extractCSS,
extractLESS
]
};
```
#### mini-css-extract-plugin
// publicPath : 解決css中背景圖的路徑問題
```javascript
//webpack config
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const devMode = process.env.NODE_ENV !== 'production'
{
test: /\.(sa|sc)ss$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
publicPath: "../"
}
},
'css-loader',
'postcss-loader',
'sass-loader',
]
}
// 實體化
// filename output
plugins: [
new MiniCssExtractPlugin({
filename: 'css/[name].css'
})
]
```
#### postCSS: 使用js轉換css的工具,搭配autoprefixer加入前綴: -webkit-
npm i postcss-loader autoprefixer
[說明](https://cythilya.github.io/2018/08/10/postcss/)
```javascript
// postcss.config.js
module.exports = {
plugins: [
require('autoprefixer')({
browsers: [
'> 1%',
'last 5 versions',
'Firefox > 45',
'iOS >= 8',
'Safari >= 8',
'ie >= 10'
]
})
]
}
```
#### sass-loader
#### postcss-loader
- 使用js轉換css的工具
- 搭配autoprefixer加入瀏覽器的prefix: -webkit-, -moz-
```javascript
// npm install -D sass-loader node-sass postcss-loader autoprefixer
test: /\.(sass|scss)$/,
use: [
'style-loader',
'css-loader',
'postcss-loader',
'sass-loader'
]
```
把css檔案獨立出來的作法
``` javascript
test: /\.(sass|scss)$/,
use: extractCSS.extract([
'css-loader',
'postcss-loader',
'sass-loader'
])
```
#### file-loader: 搬移檔案, 不用經過loader
```javascript
test: /\.html$/,
use: [{
loader: 'file-loader',
options: {
name: '[path][name].[ext]'
// 路徑,檔名,副檔名
}
}]
```
#### webpack-dev-server:
把src資料夾內容,做預處理編譯放在記憶體裡
```javascript
// webpack.config.js設定
devServer: {
compress: true,
port: 3000,
stats: {
assets: true,
cached: false,
chunkModules: false,
chunkOrigins: false,
chunks: false,
colors: true,
hash: false,
modules: false,
reasons: false,
source: false,
version: false,
warnings: false
}
}
// package.json設定 script, --open: 開瀏覽器
"server": "cross-env NODE_ENV=development webpack-dev-server --open"
// 區網中使用
"server-c": "cross-env NODE_ENV=development webpack-dev-server --host 區域ip --open"
```
#### babel: 轉換高版本的js,for舊瀏覽器的轉譯器
安裝時參考官方文件([Babel · The compiler for next generation JavaScript](https://babeljs.io/))
npm install -D babel-loader @babel/core @babel/preset-env webpack
```
@babel/core 程式需要調用babel的api進行編譯
@babel/preset-env 使用最新版本的編譯器,不用理會哪些語法需轉換
```
```javascript
module: {
rules: [
{
test: /\.m?js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
}
]
}
```
- [babel config](https://babeljs.io/docs/en/config-files#file-relative-configuration)
#### @babel/polyfill: 有些語法ie不支援,靠@babel/polyfill來解決舊ie問題 (winXP)
```
npm install @babel/polyfill
```
> 沒有要support 舊型的ie就不建議裝
```
//在js import
import '@babel-polyfill';
```
#### url loader
檔案小於8192bytes時,轉成base64 URIs,通常不搭配extractCss,路徑會有問題
建議用官方的limit: 8192 bytes
```javascript
{
test: /\.(jpe?g|png|gif)$/,
use: [{
loader: 'url-loader',
options: {
limit: 8192,
name: '[path][name].[ext]?[hash:8]'
}
}]
}
```
> 在 sass 裡面要使用載入圖片的話可以使用 ~ 符號來代替路徑
> 不使用的話可以用原本的 ../ 的方式來指向路徑
#### image-webpack-loader: 壓縮圖片
先壓縮圖片再轉成base64
```javascript
{
loader: 'image-webpack-loader',
options: {
publicPath: '../',
mozjpeg: {
progressive: true,
quality: 65
},
optipng: {
enabled: false,
},
pngquant: {
quality: '65-90',
speed: 4
},
gifsicle: {
interlaced: false,
}
}
}
```
#### copy-webpack-plugin: 原封不動複製至目的資料夾,可能需要搭配『**file-loader**』
If you want webpack-dev-server to write files to the output directory during development, you can force it with the **write-file-webpack-plugin**
libs(自定義): 放不用經過loader轉換的檔案,如.mp3、字型檔
> 若js或sass有引入libs檔案時,需透過『**file-loader**』判別副檔名
```javascript
// webpack config
const CopyWebpackPlugin = require('copy-webpack-plugin')
plugins: [
new CopyWebpackPlugin([
{ from: 'libs', to: 'libs' }
])
]
```
```javascript
// sass, js..等有引入assets檔案時,需透過『file-loader』判別副檔案
{
test: /\.(woff|woff2|ttf|eot)$/,
loader: 'file-loader',
options: {
name: '[path][name].[ext]?[hash:8]'
}
}
```
#### ProvidePlugin: 透過ProvidePlugin讓每支js裡不用import第三方套件就可以在全域取得
ProvidePlugin是原本就在webpack裡面的一個功能,所以我們先抓取他的模組
**非必要盡量不要使用 ProvidePlugin 這個做法!!!**
npm install jquery (production mode也會用到,所以不加-dev)
```javascript
var webpack = require('webpack');
// plugins
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
'window.jQuery': 'jquery',
// '自定義名字': 'jquery'
})
```
#### Html-Webpack-Plugin-template: 管理所有的html&要載入的js
[options](https://github.com/jantimon/html-webpack-plugin#options)
npm install --save-dev html-webpack-plugin
If you have any CSS assets in webpack's output (for example, CSS extracted with the ExtractTextPlugin) then these will be included with **\<link\>** tags in the HTML head. 若css獨立出來,則透過htmlwebpackplugin,會自動引入
Chunks就是對每個不同的html注入不同的 js
- 使用pug一定必須搭配此plugins
```javascript
const HtmlWebpackPlugin = require('html-webpack-plugin');
// plugins
new HtmlWebpackPlugin({
title: 'Webpack前端自動化開發',
filename: 'index.html',
template: 'html/template.html',
viewport: 'width=device-width, initial-scale=1.0',
description: 'Webpack前端自動化開發,讓你熟悉現代前端工程師開發的方法',
Keywords: 'Webpack前端自動化開發、前端、工程師、線上教學、教學範例',
chunks: ['index' ],
}),
```
# require & import
- require: es6 **前**模組化開發方式
- import: es6 **後**模組化開發方式
| require/module.exports | import/export |
| :------------- |:-------------|
| CommonJS 的規範 | ES6 的規範 |
|Node.js 有支援,瀏覽器沒有 | node.js、瀏覽器部分支援 |
#### 模塊檔名:含相對路徑
| 輸出 | 引入 |
| :------------- |:-------------|
| module.exports = 模塊 | var 名稱 = require("模塊檔名") |
| export default 模塊; | import 名稱 from "模塊檔名" |
# dependencies devdependencies
* dependencies : 使用在已經發布的環境下,換句話說,是指發布後仍然需要依賴使用的 plug-in。舉個例子來說,如果我需要使用 jQuery 與 AngularJs 來開發,就算開發完之後發佈到伺服器,我仍然需要依賴 jQuery 與 AngularJs 的套件,這些套件會在發布後繼續使用。
用法:當我執行 npm install –production 或是註明 NODE_ENV 變量值为為 production 時,只會下載 dependencies 中的套件。
* devDependencies : 使用在開發中的環境下,意思是指——只單純會在開發時應用到的 plug-in。同樣舉個例子,如果我在開發時需要使用 Js ES6 並使用 babel 轉換成 ES5,或是我希望可以使用 gulp-stylus 的套件來使用,但在發佈之後,我們並不會在用到 gulp-stylus 這個套件。換句話說,他只需要存在於開發環境中,而不需要繼續放到發布環境裏。
用法:鍵入 npm install 時,會同時抓下來 dependencies & devDependencies 兩個節點之中的套件。
# Vendor.js 與 Entry.js
透過Vendor.js獨立出來可以有效率的進行打包
```javascript
optimization: {
splitChunks: {
cacheGroups: {
vendor: {
test: /node_modules/,
name: 'vendor',
chunks: 'initial',
enforce: true
}
}
}
},
```
```javascript
new HtmlWebpackPlugin({
title: 'index',
filename: 'index.html',
template: 'template/template.html',
chunks: ['vendor', 'index' ],
}),
```
# 打包與排除
- include 哪些目錄中的文件需要進行 loader 轉換
- exclude 哪些目錄中的文件不需要進行 loader 轉換
## SourceMap: debug較方便
- 原始碼和打包對應
```
<!-- js檔較看得出在第幾行 -->
devtool: 'inline-source-map',
```
# 問題集
- [打包圖片與壓縮的介紹](https://codingwife.com/2018/03/23/webpack/webpack03/)
# Pug 使用
npm install --save-dev html-loader pug-html-loader
**pug的轉換一定要搭配html-webpack-plugin來使用**
chunks: 注入 js / 獨立css檔案
```javascript
// plugins
new HtmlWebpackPlugin({
title: 'pug轉換',
filename: 'views/about.html',
template: 'pug/about.pug',
chunks: ['about']
})
```
```javascript
// html壓縮
new HtmlWebpackPlugin({
title: 'pug轉換',
filename: 'views/about.html',
template: 'pug/about.pug',
chunks: ['about'],
minify: {
collapseWhitespace: true,
removeComments: true,
removeRedundantAttributes: true,
removeScriptTypeAttributes: true,
removeStyleLinkTypeAttributes: true,
useShortDoctype: true
}
})
```
# Bootstrap
- Importing JavaScript
```
import 'bootstrap';
```
- Importing Styles
```
@import "~bootstrap/scss/bootstrap";
```
# Vue 開發
```
// --save-dev
npm install vue-loader -D
npm install vue-template-compiler -D
npm install vue-html-loader -D
npm install vue-style-loader -D
// --save
npm install vue -S
```
- 引入VueLoaderPlugin
```javascript
var VueLoaderPlugin = require('vue-loader/lib/plugin');
```
- 加入 vue-loader 跟 vue-style-loader,移除 include 設定
```javascript
// module rule
{
test: /\.(vue)$/,
use: 'vue-loader'
}
// 修改sass loader設定
{
test: /\.(sa|sc)ss$/,
use: [
'vue-style-loader',
{
loader: MiniCssExtractPlugin.loader,
options: {
publicPath: "../"
}
},
'css-loader',
'postcss-loader',
'sass-loader',
]
},
// plugins
new VueLoaderPlugin()
```
- new VueLoaderPlugin
## Vue 資料夾結構
src/
- components/
- js/
- App.vue
- index.js (進入點)
## Vue 套件
- Vetur (VS Code套件)
## 單純編譯指令
```
webpack <entry> <output>
```
## Project5
- webpack版本: "^5.72.1" (鎖住第五版本)
- 本地端測試流程
- yarn watch-all
- 瀏覽器開啟 `project5.test/dist/views