webpack
隨著前端技術的發展,前後端開發的分離,前端專案的構建顯得越來越有必要,而 Webpack 就是一個前端的打包工具,幫助我們完成前端專案的構建。
前端的專案構建工具也逐漸的由藉助其他語言的構建工具,到使用 Grunt,後來被 Stream 概念的 Gulp 所取代,再到現在的 Rullup, Webpack。而 Webpack 也成為了三大框架官方腳手架所使用的構建工具,在我們做專案的時候會經常接觸到,非常有學習瞭解的必要。
其起源於作者在 Google 就職時,後專案被 Instagram 支援。
其各個版本更迭情況大概如下:
- v1
- 編譯,打包
- 模組熱更新
- 程式碼分割
- 檔案處理
- v2
- Tree Sharking
- 支援 ES Module
- 動態 Import()
- v3
- Scope Hoisting
- Magic Comments
- v4
- 零配置
- mode
- 新增支援的 js 模組
- 外掛的改變和優化
Webpack 的作用,用官網的一張圖就能概括:
核心概念
v3 中有四個核心概念:entry,output,loader,plugin
entry
entry 是 webpack 進行打包的入口,webpack 會從入口開始去尋找入口檔案的依賴項進行打包。
output
output 是 webpack 打包後的輸出檔案。
loader
webpack 本身只能打包 js,而其他型別的檔案就要藉助於各種各樣的 loader。
plugin
外掛可以幫助我們實現更多樣的功能。
打包普通 js
npm init
npm install webpack@^3.0.0
touch webpack.config.js
mkdir src
touch src/app.js
touch src/sum.js
複製程式碼
app.js:
import sum from './sum'
console.log(sum(1 + 2))
複製程式碼
sum.js:
export default function (a, b) {
return a + b
}
複製程式碼
webpack.config.js
module.exports = {
entry: {
app: './src/app.js'
},
output: {
// output 中可以使用佔位符
filename: '[name].[hash].js',
path: __dirname + '/dist'
}
}
複製程式碼
執行 webpack
打包命令即可成功打包。
打包 ES6
npm init
npm install webpack
// babel包會在下面解釋作用
npm install babel-loader@8.0.0-beta.0 @babel/core @babel/preset-env @babel/polyfill @babel/runtime-corejs3 @babel/plugin-transform-runtime core-js regenerator-runtime
touch webpack.config.js
touch .babelrc
mkdir src
touch src/app.js
touch src/sum.js
複製程式碼
app.js:
import sum from './sum'
let a = 1
const b = 3
const c = [...[1, 2], 3]
console.log(sum(a + b + c[0]))
複製程式碼
sum.js:
export default (a, b, c) => a + b + c
複製程式碼
為了讓 webpack 識別並打包 ES6 語法,我們就需要使用 babel 和 babel-loader 了。
如果要使用 babel v7 版本,則 babel-loader 需要安裝 babel-loader@8.0.0-beta.0
webpcak.config.js
module.exports = {
entry: './src/app.js',
output: {
filename: '[name].[hash].js',
path: __dirname + '/dist'
},
module: {
rules: [
{
test: /\.js$/,
use: 'babel-loader',
exclude: '/node_modules/'
}
]
}
}
複製程式碼
.babelrc
{
"presets": ["@babel/preset-env"]
}
複製程式碼
我們在 rules 裡配置了 .js 結尾的檔案呼叫 babel-loader。使用了 @babel-core(核心) 和 配置了 @babel/preset-env (識別ES6語法),
執行 webpack
命令打包,ES6 語法正確轉譯:
@babel/preset-env 只是讓babel 支援了新的 ES6 語法,但是 ES6 中新加的功能在一些低階的瀏覽器中並不會被支援,這就需要使用到我們安裝的其他的 babel 包了。下面說到的包其實都是必須要安裝到 --save 中的, 因為這些包都是要在應用中使用到的(@babel/plugin-transform-runtime 為 dev,搭配 @babel/runtime 使用)
其主要分為兩類使用:
@babel/polyfill
@babel/polyfill 其是就是一個功能包,裡面幫我們實現了 ES6 的各種 API,我們只需要在入口檔案中引入即可:
import 'babel-polyfill'
複製程式碼
注意這樣我們的打包生成檔案會大上許多,多出來的就是我們引入的全部的 polyfill 的大小,另外,引入的 polyfill 是會汙染全域性變數的。
使用core-js v2 的時候可以像上面引入。
core-js 到了 v3,@babel/polyfill 被廢棄了,改而要使用 core-js regenerator-runtime,之前的全部引入 polyfill 也要改成:
import 'core-js/stable';
import 'regenerator-runtime/runtime';
複製程式碼
在 babel 中,還可以通過 preset-env 中的配置來實現 polyfill 的按需引入來節省空間:
.babelrc:
{
"presets": [
[
"@babel/preset-env",
{
"useBuiltIns": "usage",
// 指定core-js為v3
"corejs": 3
}
]
]
}
複製程式碼
@babel/runtime & @babel/plugin-transform-runtime
藉助於 @babel/runtime & @babel/plugin-transform-runtime 同樣可以實現支援 ES6 API,並且這種方式是按需引入的,還不會汙染全域性變數。
之前的說法是這種方式比較適合開發庫使用,但是這種方式是有缺陷的,如 [1,2,3].includes(1)
這種依賴於 Array.prototype.includes
的方法就不支援。所以在產品中還是使用 polyfill 的方式為好。
但是在新版本中可以指定 core-js 為 v3(對應的 runtime 包為 @babel/runtime-corejs3),就支援了,所以目前來看還是 runtime 的方法最優。
使用時也只要在 .babelrc 中配置:
{
"presets": [
"@babel/preset-env"
],
"plugins": [
["@babel/plugin-transform-runtime", {
"corejs": {
"version": 3,
"proposals": true
}
}]
]
}
複製程式碼
打包 typescript
打包 ts 我們要藉助 ts-loader 和 tsconfig.json(ts 編譯設定檔案)。
但是隨著babel的發展,其實是更推薦使用 babel 的方式去轉譯 ts。至於為什麼,我想把答案交給下面這篇譯文:juejin.im/post/5c822e…
ts-loader
npm init
// 最新的ts-loader要求webpackv4
npm install webpack@^4.0.0 webpack-cli typescript ts-loader
touch tsconfig.json
touch webpack.config.js
mkdir src
touch src/app.ts
複製程式碼
app.ts:
const a = 45
interface Cat {
name: String,
sex: String
}
function touchCat(cat: Cat): void {
console.log('miao, I am ' + cat.name)
}
touchCat({
name: 'kafe',
sex: 'male'
})
複製程式碼
webpack.config.js:
module.exports = {
mode: 'development',
entry: './src/app.ts',
output: {
filename: '[name].[hash].js',
path: __dirname + '/dist'
},
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader'
}
]
}
}
複製程式碼
tsconfig.json:
{
"compilerOptions": {
"module": "commonjs",
"target": "es5",
"allowJs": true
},
"include": [
"./src/*"
],
"exclude": [
"./node_modules"
]
}
複製程式碼
@babel/preset-typescript
npm init
npm install webpack@^3.0.0 babel-loader@8.0.0-beta.0 @babel/core @babel/preset-env @babel/preset-typescript
touch webpack.config.js .babelrc
mkdir src
touch src/app.ts
複製程式碼
app.ts:
const a = 45
interface Cat {
name: String,
sex: String
}
function touchCat(cat: Cat): void {
console.log('miao, I am ' + cat.name)
}
touchCat({
name: 'kafe',
sex: 'male'
})
複製程式碼
webpack.config.js:
module.exports = {
entry: './src/app.ts',
output: {
filename: '[name].[hash].js',
path: __dirname + '/dist'
},
module: {
rules: [
{
test: /\.tsx?$/,
use: 'babel-loader',
exclude: '/node_modules/'
}
]
}
}
複製程式碼
.babelrc:
{
"presets": ["@babel/preset-env", "@babel/preset-typescript"]
}
複製程式碼
打包後如下:
注意這樣因為沒有使用 tsc 編譯,所以是沒有型別檢查的,如果需要這個功能,可以使用webpack中的 ts 型別檢查外掛。