最近學習了一波webpack打包部署,作為一名前端,不會一個打包的工具可真的是丟人啊。不過,用webpack又要接觸到了配置文件,這個當初當我放棄寫后端的東西,不過以后還是要全面發(fā)展的。好了,話不多說,這篇文章用的是webpack4.x版本的,下面將細數(shù)當初才過的坑。
1.基礎準備
創(chuàng)建一個文件夾webpackDemo,我們使用命令行進入這個文件夾,運行 npm init 命令,初始化這個文件夾,初始化過程中出現(xiàn)的詢問的一些配置,一路回車就好了。初始化好了之后,我們會發(fā)現(xiàn)文件夾里面已經(jīng)多了package.json文件。
npm install webpack --save-dev webpack-cli --save-dev
安裝必要的 webpack 和 webpack-cli,安裝成功之后的package.json文件如下
在根目錄下創(chuàng)建如下的目錄結(jié)構(gòu)
在test.js文件里面編寫如下代碼,一萬年經(jīng)典的Hello World
document.write("Hello World");
在index.html里面編寫代碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>Webpack初探</title>
</head>
<body>
<script src="./dist/js/bundle.js"></script>
</body>
</html>
這邊引用一個bundle.js文件,bundle.js文件使我們打包之后生成的文件,接下來使用webpack的打包命令進行打包
webpack src/apps/test.js -o dist/js/bundle.js
wepack的基礎打包命令 webpack {entryFile} -o {aimFile} 執(zhí)行完之后會發(fā)現(xiàn)我們的目錄里已經(jīng)生成了 bundle.js 文件,我們運行 index.html 文件,可以看到親切的Hello World了。
打包過程中,出現(xiàn)的輸出如下
這邊會顯示webpack打包一共花了501ms,打包后的 bundle.js 體積是957bytes,因為我們的代碼比較簡單,所以打包生成的文件還是很小的。
2.webpack.config.js配置文件
在根目錄下創(chuàng)建文件 webpack.config.js 這是weback運行所要依據(jù)的配置。大致分為入口配置,出口配置,loader配置,plugins配置
2.1 entry和output###
entry配置的是入口文件,告訴webpack從哪個文件開始解析,分為單入口和多入口。有了入口,就要配置出口,配置打包生成的文件位置和文件名稱。在webpack.config.js里面編寫如下代碼
const path = require('path');
module.exports = {
entry: path.resolve(__dirname, 'src/apps/test.js'),
output: {
path: path.resolve(__dirname, 'dist'),
filename: "js/bundle.js",
}
}
首先這邊引用的path是node.js的一個模塊,是用來解析路徑用的,path.resolve指的是當前文件的所在路徑,那么這邊我們entry配置的就是/src/apps/test.js,這是單入口的配置形式。
下面對出口進行配置,output是一個對象,基礎的兩個屬性,一個是輸出文件的路徑,另一個是輸出文件的名稱。
在package.json里面編寫如下代碼:
這邊配置一下webpack的打包命令,之后通過npm run start進行打包,就不需要輸一大串的命令了。打包一下我們可以看一下控制臺的輸出
發(fā)現(xiàn)有一個warning,這是因為我們還沒有配置我們的mode,我們修改webpack.config.js文件
const path = require('path');
module.exports = {
mode: 'development',
devtool: 'source-map',
entry: path.resolve(__dirname, 'src/apps/test.js'),
output: {
path: path.resolve(__dirname, 'dist'),
filename: "js/bundle.js",
}
}
在打包一下會發(fā)現(xiàn)這邊的warning已經(jīng)沒有了。
2.2 devtool配置
上面的配置文件我們是加了一個devtool的配置,這是因為webpack打包后的這個文件,我們可以看一下,已經(jīng)不是我們所熟知的代碼了,這樣不方便于我們的調(diào)試,所以webpack為了解決這個問題,有一個devtool的配置。這邊給一個別人博客的傳送門webpack之devtool詳解,webpack官方解釋
2.3 loader配置
loader配置是webpack中的重點,因為webpack默認是只能處理html文件,如果要處理比如react應用中的jsx文件那么就需要配置一下loader,或者js、css等文件,都需要配置一下loader。這邊已react應用為例。由于目前瀏覽器的兼容性問題,所以我們需要大量的插件和loader來轉(zhuǎn)換我們寫的高級js語法,就有了各種es6轉(zhuǎn)es5,es7啥的。具體的還得好好研究。首先安裝react相關(guān)的插件
npm install react --save react-dom --save
安裝解析react的相關(guān)插件和解析es6語法的相關(guān)插件。
npm install babel-loader -save-dev babel-core --save-dev babel-preset-env --save-dev babel-preset-react --save-dev
babel-core
把 js 代碼分析成 ast (抽象語法樹, 是源代碼的抽象語法結(jié)構(gòu)的樹狀表現(xiàn)形式),方便各個插件分析語法進行相應的處理。有些新語法在低版本 js 中是不存在的,如箭頭函數(shù),rest 參數(shù),函數(shù)默認值等,這種語言層面的不兼容只能通過將代碼轉(zhuǎn)為 ast,再通過語法轉(zhuǎn)換器分析其語法后轉(zhuǎn)為低版本 js。
babel-preset-*
babel-preset-* 代表了一系列的轉(zhuǎn)碼插件
有了 babel-plugin 系列,可以按需配置自己想要的特性,若是想搭個 es6 環(huán)境,一個個地配置各個插件,我猜你會瘋掉。babel-preset 系列就可以滿足我們的需求,babel-preset 系列打包了一組插件,類似于餐廳的套餐。如 babel-preset-es2015 打包了 es6 的特性,babel-preset-stage-0 打包處于 strawman 階段的語法
下面看具體的配置,配置文件如下,這邊首先我們要將package.json文件里面的babel-loader的版本更換成7.1.5然后重新安裝一下模塊,不然下面會因為babel-loader的版本問題出現(xiàn)打包失敗的問題。
// webpack.config.js
const path = require('path');
module.exports = {
mode: 'development',
devtool: 'source-map',
entry: path.resolve(__dirname, 'src/apps/console.jsx'),
output: {
path: path.resolve(__dirname, 'dist'),
filename: "js/bundle.js",
},
module: {
rules: [{
test: /(\.js)|(\.jsx)$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['env', 'react']
}
}
}]
}
}
test 指明要對哪一種后綴的文件進行解析,使用正則進行驗證
exclude 規(guī)定一些文件夾或文件不參與解析
use.loader 配置使用的loader名稱
use.option 配置loader的其他選項,這邊的presets是告訴webpack解析react語法和es6語法。
// console.jsx
import React from 'react';
import ReactDOM from 'react-dom';
const mountNode = document.getElementById("root");
class App extends React.Component {
constructor(props) {
super(props)
}
render() {
return (
<div>
Hello China!
</div>
);
}
}
ReactDOM.render(<App />, mountNode);
執(zhí)行npm run start,然后運行我們的index.html文件可以看到Hello China!。
2.4 plugin配置
插件賦予了webpack更多的功能,比如說 js和css分離打包。比如說happyPack的多線程打包,比如說每次打包前自動清空dist目錄。
以js、css分離打包為例,這是使用的extract-text-webpack-plugin插件,
首先安裝一下,注意這邊安裝的是extract-text-webpack-plugin@next而不是extract-text-webpack-plugin
npm install --save-dev extract-text-webpack-plugin@next
npm install style-loader --save-dev css-loader --save-dev
// webpack.config.js
const path = require('path');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
mode: 'development',
devtool: 'source-map',
entry: path.resolve(__dirname, 'src/apps/console.jsx'),
output: {
path: path.resolve(__dirname, 'dist'),
filename: "js/bundle.js",
},
module: {
rules: [{
test: /(\.js)|(\.jsx)$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['env', 'react']
}
}
}, {
test: /\.css$/i,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: ['css-loader']
})
}]
},
plugins: [
new ExtractTextPlugin({
filename: 'style/css/[name].css',
allChunks: true
})
]
}
2.5 其他配置###
stats配置
在進行webpack打包的時候會出現(xiàn)很多的輸出,但是有的輸出是我們不需要看見的輸出,這就可以采用stats進行配置
// 對webpack輸出信息的配置,可以減少一些不必要的輸出
stats: {
children: false
}
resolve.extensions 解決引入模塊不需要加后綴
// extensions 自動解決擴展,配置這個在引用模塊時不用加后綴
// modules 配置解析模塊的搜索目錄
// path.resolve 返回參數(shù)的絕對路徑 path.join 拼接路徑 然后返回絕對路徑
resolve: {
modules: [path.resolve(__dirname, 'node_modules'), path.join(__dirname, 'src')],
extensions: ['.wasm', '.mjs', '.js', '.json', '.jsx']
},
optimization配置 抽離公共模塊
optimization: {
minimize: false,
splitChunks: {
chunks: 'all',
name: 'common'
}
},
last.一些解釋及踩過的那些坑
last.1 npm install 的時候 --save-dev 和--save的區(qū)別
首先這邊的 --save 是將我們install的包寫進package.json文件里面,后面的-dev才是要描述的重點,-dev是寫進我們的devDependencies里面,如果不加這個那么是寫進dependencies里面的,在生產(chǎn)環(huán)境下,是不會安裝devDependencies里面的包的。這邊找了一個別人的博客,寫的比我這個更全面一點。對--save-dev和--save的區(qū)別詳解
last.2 webpack基礎命令打包的時候報ERROR in multi ./src/apps/test.js dist/js/bundle.js
Module not found: Error: Can't resolve 'dist/js/bundle.js' in 'F:\WebDemo\webpackDemo'
這是因為webpack的版本問題,在webpack4.x版本之前的打包命令是
webpack {entryFile} {aimFile}
但是在webpack4.x開始基礎打包命令就變成了
webpack {entryFile} -o {aimFile}
last.3出現(xiàn)Error: Cannot find module '@babel/core'###
這是因為babel-loader版本的問題,如果之前我們安裝babel-loader的時候是采用npm install babel-loader這樣寫的話是默認安裝最新版本的babel-loader最新的是8.x的,我們回退到7.1.5版本就可以避免這個報錯了。
last.4 webpack-dev-server問題
1.webpack-dev-server 不會讀取webpack.config.js配置的output也不會將生產(chǎn)的文件添加進項目目錄里
2.webpack-dev-server 生成的文件和你dist里面的文件不是同一個文件。dist里面的是output里面決定
的webpack-dev-server打包生成的文件位置取決于contentBase配置
last.5 的 package.json里面的打包命令###
--colors 輸出結(jié)果帶有顏色
--profile 輸出性能數(shù)據(jù)看見每一步的耗時
--proress 輸出當前的編譯進度
--display-error-details 輸出詳細的錯誤信息
lats.6 使用extractTextPlugin時報 Chunk.entrypoints: Use Chunks.groupsIterable and filter by instanceof Entrypoint instead###
這是因為安裝extract-text-webpack-plugin的時候安裝命令問題,實際上我們需要安裝的是
"extract-text-webpack-plugin": "^4.0.0-beta.0",
解決辦法,刪除之前安裝的extract插件,更換安裝命令為
npm install --save-dev extract-text-webpack-plugin@next
last.7 入口文件配置[name].js 但是打包出來的文件總是main.js并不會根據(jù)入口名稱決定出口名稱###
這邊的main.js main是取決你的主入口的,就是在執(zhí)行npm init的時候那一堆默認的主入口。之所以沒有按照入口文件來改變出口文件的名稱,是因為入口文件采用的單入口的形式,也就是如下寫法
entry: path.resolve(__dirname, 'src/apps/console.jsx')
使用單入口的形式,那么
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'js/[name].js'
},
打包生成的文件只會有一個main.js 并不會生成console.js
解決辦法是將單入口的寫法換成多入口的形式,也就是下面這種寫法
entry: {
console: path.resolve(__dirname, 'src/apps/console')
},
這樣打包出來的文件就是console.js
last.7 bundle.js和vendor.js的引入順序問題###
vendor.js一定是最先引用的,因為bundle.js要依賴于vendor.js生存。
last.8 名詞解釋
webpack.config.dev.js 開發(fā)環(huán)境下的webpack配置文件
webpack.config.prod.js 生產(chǎn)環(huán)境下的配置文件
vendor.js 這是打包我們引入的第三方包的。