章首發在我的個人博客:http://www.brandhuang.com/article/1586446191183
webpack基礎,自己配置 webpack 進行資源打包
這只是我個人學習整理的個人筆記,可以直接跳過前面去看文章中的「參考文章」
紙上得來終覺淺,絕知此事要躬行
請一定動手敲一敲代碼,看一看效果
請一定動手敲一敲代碼,看一看效果
請一定動手敲一敲代碼,看一看效果
一個現代 JavaScript 應用程序的靜態模塊打包器,當 webpack 處理應用程序時,會遞歸構建一個依賴關系圖,其中包含應用程序需要的每個模塊,然后將這些模塊打包成一個或多個 bundle。
Webpack 僅能理解 Javascript 和 JSON 文件,需要通過 Loader 來轉換
1. 項目初始化
創建文件夾 webpack-demo,進入文件夾執行
npm init -y
從 webpack 4 版本開始,webpack-cli 分離成一個單獨的模塊,安裝 webpack 時還需要單獨安裝 webpack-cli
不建議全局安裝 webpack,采用本地安裝的方式
npm install webpack webpack-cli --save-dev
在 webpack-demo 目錄下新建 webpack.config.js
Simple rule: one entry point per HTML page. SPA: one entry point, MPA: multiple entry points.
一個HTML頁面一個入口,單頁面一個入口,多頁面多個入口
官方地址:entry-context
module.exports={
entry: './src/index.js' //webpack的默認配置
}
module.exports={
entry: {
app: './src/index.js' // app是輸出的文件名,output中配置了filename后,這個名字無效
} //
}
module.exports={
entry: [
'./src/index.js',
'./src/index2.js'
]
}
entry 的配置可以是 字符串、數組、對象。
const path=require('path'); // node提供
module.exports={
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'), //輸出到的文件夾
filename: 'bundle.js', // 編譯后輸出的文件名
publicPath: '/' //通常是CDN地址
}
}
本文所使用的 webpack 版本:
webpack@4.42.1
webpack-cli@3.3.11
從 Webpack v4 開始,不引入任何配置文件的情況下也可以使用。
在 webpack-demo 下創建 src/index.js,在其中寫點內容
let arr=[1,2,3]
arr.map((item)=> {console.log(item)})
執行 npx webpack --mode=development 進行打包。
執行完后,在 webpack-demo 下可看到一個 dist 文件夾,其中的 main.js 文件,即為默認打包后的文件。
查看 main.js 文件
/***/ "./src/index.js":
/*!**********************!*\
!*** ./src/index.js ***!
\**********************/
/*! no static exports found */
/***/ (function(module, exports) {
eval("let arr=[1,2,3]\narr.map((item)=> {console.log(item)})\n\n//# sourceURL=webpack:///./src/index.js?");
/***/ })
代碼還是箭頭函數,沒有被打包成低版本代碼,這不是我們所需要的。此時需要使用 webpack 的 babel-loader 來將代碼轉換到低版本。
安裝 babel-loader
npm install babel-loader --save-dev
安裝 babel 依賴:推薦讀下:不容錯過的 Babel7 知識
npm install @babel/core @babel/preset-env @babel/plugin-transform-runtime --save-dev
npm install @babel/runtime @babel/runtime-corejs3
配置 loader
module.exports={
module: {
rules:[
{
test: /\.js$/,
use: ['babel-loader'],
exclude: /node_modules/
}
]
}
}
創建 .babelrc 文件,配置 babel
// 配置babel方式一
{
"presets": ["@babel/preset-env"],
"plugins": [
[
"@babel/plugin-transform-runtime",
{
"corejs": 3
}
]
]
}
// 配置 babel 方式二:在webpack.config.js中配置
module.exports={
module: {
rules: [
{
test: /\.js$/,
use: {
loader: 'babel-loader',
options: {
presets: ["@babel/preset-env"],
plugins: [
[
"@babel/plugin-transform-runtime",
{
"corejs": 3
}
]
]
}
},
exclude: /node_modules/
}
]
}
}
loader 是從右向左(或者從下至上)執行的
比如要配置 less-loader,還需要配置 css-loader 和 style-loader,代碼如下
module.exports={
module: {
rules: [
{
test: /\.(le|c)ss$/,
use: ['style-loader', 'css-loader', 'less-loader'],
exclude: /node_modules/
}
]
}
}
webpack 使用 loader 的方式建議閱讀官方文檔:Webpack Using Loaders
告知 webpack 使用相應的模式進行優化打包,打包出的文件有所不同。
默認值: production
可設置: none、 production 和 development。
OptionDescriptiondevelopmentSets process.env.NODE_ENV on DefinePlugin to value development . Enables NamedChunksPlugin and NamedModulesPlugin .productionSets process.env.NODE_ENV on DefinePlugin to value production . Enables FlagDependencyUsagePlugin , FlagIncludedChunksPlugin , ModuleConcatenationPlugin , NoEmitOnErrorsPlugin , OccurrenceOrderPlugin , SideEffectsFlagPlugin and TerserPlugin .noneOpts out of any default optimization options
需要用到插件 html-webpack-plugin
npm install html-webpack-plugin --save-dev
在 webpack-demo 目錄下新建 public/index.html
修改 webpack.config.js
const HtmlWebpackPlugin=require("html-webpack-plugin")
module.exports={
...,
plugins: [
new HtmlWebpackPlugin({
template: './public/index.html',
filename: 'index.html', // 打包后的文件名
})
]
}
安裝 cross-env,用來提供一個兼容性好的 scripts 來使用環境變量
npm install crosee-env --save-dev
具體用法,在 packge.json 中的 scripts 中添加內容
"start": "cross-env NODE_ENV=development webpack",
之前編譯執行的是 npx webpack --mode=development,現在只需要執行 npm run start
使用到的插件 webpack-dev-server
npm install webpack-dev-server --save-dev
修改 package.json
"start": "cross-env NODE_ENV=development webpack-dev-server",
配置 webpack-dev-server
官方文檔:dev-server
// webpack.config.js
module.expots={
...,
devServer: {
port: '8080', //默認是8080
quiet: false, //默認不啟用,如果開啟了,控制臺不會看到除了初始啟動信息外的任何console信息,包括錯誤提示
inline: true, //默認開啟 inline 模式,如果設置為false,開啟 iframe 模式
stats: 'errors-only', //終端僅打印 error
overlay: true, //默認不啟用,是否全屏顯示編譯的錯誤信息
clientLogLevel: "silent", //日志等級
compress: true //是否啟用 gzip 壓縮
}
}
方便我們在控制臺看到我們在代碼中的console信息或者錯誤信息的實際行數,方便定位問題,否則控制臺會顯示編譯后的位置,和實際位置打不一樣
官方可選參數:devtool配置
修改 webpack.config.js
module.exports={
...,
devtool: 'cheap-module-eval-source-map' //開發環境下使用,線上設置為 none 或者 source-map
}
需要用到插件 less-loader、css-loader 、 style-loader、postcss-loader 和 autoprefixer,后兩個是自動添加兼容性前綴。
npm install style-loader less-loader css-loader postcss-loader autoprefixer --save-dev
新建文件 src/index.less
@color: red;
body{
background-color: @color;
}
修改 webpack.config.js
module.exports={
...,
module: {
rules:[
...,
{
test: /\.(le|c)ss$/,
use: ['style-loader', 'css-loader', {
loader: 'postcss-loader',
options: {
plugins: function () {
return [
require('autoprefixer')({
"overrideBrowserslist": [
">0.25%",
"not dead"
]
})
]
}
}
}, 'less-loader'],
exclude: /node_modules/
}
]
}
}
推薦在根目錄新建 .browserslistrc 文件來配置 postcss-loader
需要用到插件 url-loader 和 file-loader
url-loader 處理資源時,將配置的limit限制大小以內的資源以 DataURL 返回
官方文檔:url-loader、file-loader
npm install url-loader file-loader --save-dev
配置 webpack.config.js,參考 vue-cli2.x 構建的項目配置
module.exports={
...,
module: {
rules:[
...,
{
test: /\.(woff2?|eot|ttf|otf|png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10240, // 10k,大于 10k 將使用 file-loader
esModule: false, // file-loader的配置,默認使用ES Module,否則使用 CommonJS
}
}
]
}
}
每次打包時,舊的打包文件不是基本不是我們需要的,所以需要清空文件夾,懶得手動去刪除文件夾,需要使用插件 clean-webpack-plugin
每次執行打包后都會清空文件夾中的內容重新生成
npm install clean-webpack-plugin -D
修改 webpack.config.js 的 plugins
const { CleanWebpackPlugin }=require('clean-webpack-plugin');
module.exports={
...,
plugins: [
...,
new CleanWebpackPlugin()
]
}
一個基礎的webpack就配置完了
官方文檔地址,建議隨時查閱鞏固:Webpack官網
祭出掘金大佬的三篇文章,從基礎到進階都有,跟著做完肯定有收獲
Webpack 的熱更新又稱熱替換 (Hot Module Replacement),縮寫為 HMR。 這個機制可以做到 「不用刷新瀏覽器」 而將新變更的模塊替換掉舊的模塊。
細節參考:Webpack HMR 原理解析
ebpack是開發Vue.js單頁面應用(SPA)最基本的工具。通過管理負責的構建步驟能夠使開發工作流非常的簡單,同時也能夠優化應用的大小提升應用的性能。
在這篇文章我將為大家展示Webpack是如何應用在Vue app中的,包括
1. 單文件組件
2. 優化Vue項目的構建
3. 瀏覽器緩存管理
4. 代碼分割
關于Vue-cli
如果你是使用vue-cli創建的項目整體框架,那么默認就提供了Webpack的配置文件。Webpack已經很好的集成到你的項目中了,我也沒有更進一步提升的優化的建議可以提供!
那么既然提供了一個開箱即用的工具,你可能對到底是如何運行工作的不是很了解,對嗎?在本篇文章我們將討論與vue-cli提供的默認配置類似的功能。
1.單文件組件
Vue一個非常明顯的特點是它使用HTML作為組件的模板。這也就必然伴隨著一個一直存在的問題,不管是你將組件模板的HTML標記比較笨拙的放置于JavaScript字符串中,還是將模板和組件的定義放置于單獨的文件中,管理起來都比較麻煩。
Vue提供了一個非常棒的解決方案單文件組件(Single File Components SFCs),單文件組件將模板,組件定義以及CSS都統一整齊的放置在一個.vue文件內。
<template> <div id="my-component">...</div> </template> <script> export default {...} </script> <style> #my-component {...} </style>
通過Webpack的vue-loader插件,我們可以在項目中靈活的使用SFCs。這個插件會將SFCs的模板、組件定義以及樣式進行拆分成塊并傳輸給特定的Webpack loader進行后續處理,例如:script塊將交給`bable-loader`處理,template快將交給Vue自己的`vue-template-loader`處理它會將模板轉換并傳輸給`render`函數。
vue-loader最后的輸出會是一個包含在Webpack bundle文件里的JavaScript模塊。
一個非常典型的vue-loader配置如下:
module: { rules: [ { test: /\.vue$/, loader: 'vue-loader', options: { loaders: { // Override the default loaders } } }, ] }
2.優化Vue項目的構建
## 只在運行時構建
如果你的Vue應用沒有HTML模板,只是使用到了render函數,那么你沒有必要使用到Vue的模板編譯功能。那么就可以在Webpack構建是忽略這部分的代碼降低打包后的文件大小。
*請記住在開發模式下單文件組件是被預編譯到render函數的*
Vue提供了一個`runtime-only`的庫,這個庫包含了除模板編譯外的所有功能,庫名是`vue.runtime.js`。它的搭建比完整版本要小20KB,因此如果可以使用這個版本那么非常值得這么做。
默認情況下就是使用的runtime-only,因此每當我們在項目中使用`import vue from 'vue';`就是使用的這個版本。通過配置`alias`也可以改變這種方式:
resolve: { alias: { 'vue$': 'vue/dist/vue.esm.js' // Use the full build } },
## 在生產環境上剝離警告和錯誤信息
在生產環境上降低最終打包文件大小的另外一種方法是移除警告和錯誤信息。這樣最終打包的文件里就不再有非必需的代碼,進而提高整個文件的加載速度。
如果你去檢查Vue的源碼你會發現警告信息處理是根據當前環境變量`process.env.NODE_ENV`來進行判斷的:
if (process.env.NODE_ENV !=='production') { warn(("Error in " + info + ": \"" + (err.toString()) + "\""), vm); }
如果`process.env.NODE_ENV `設置為`production`那么這部分代碼在構建的時候就會自動的被剝離。
可以使用`DefinePlugin`來設置`process.env.NODE_ENV `的值,也可以使用`UglifyJsPlugin`插件將代碼進行最小化和移除不使用的代碼塊的處理操作。
if (process.env.NODE_ENV==='production') { module.exports.plugins=(module.exports.plugins || []).concat([ new webpack.DefinePlugin({ 'process.env': { NODE_ENV: '"production"' } }), new webpack.optimize.UglifyJsPlugin() ]) }
3.瀏覽器緩存管理
用戶瀏覽器的緩存策略是瀏覽器會判斷網頁文件是否已經在本地有未過期的副本,如果存在則瀏覽器會使用本地的緩存文件而不會去服務器重新下載。
如果將所有的代碼打包在一個文件里面,那么任何微小的改變都意味著整個打包文件都得重新下載。理想情況下是用戶盡可能的少下載,多使用本地緩存副本。那么最明智的做好就是將經常需要變動的文件與很少變動的文件做分離。
## 第三方庫文件
使用*Common Chunks plugin*能夠將第三方庫文件(如:Vue.js庫)從你的應用代碼的抽出為一個獨立的文件。
我們可以配置插件判斷文件依賴是否是來自于`node_modules`文件夾,如果是的,那么將這些文件打包輸出到一個獨立的文件`vendor.js`
new webpack.optimize.CommonsChunkPlugin({ name: 'vendor', minChunks: function (module) { return module.context && module.context.indexOf('node_modules') !==-1; } })
## 文件指紋
當構建生成新的打包文件,我們怎么才能銷毀瀏覽器的緩存或是說怎么才能使緩存失效從而從服務器加載最新的文件呢?默認情況下只有當緩存文件失效過期,或是手動清空緩存后,瀏覽器才會從服務器請求資源文件。當服務器表名文件已經被改變后文件將被重新下載(否則服務器會返回304 Not Modified)。
為避免不必要的請求判斷,我們可以在文件發生變化時修改文件的名稱這樣強制瀏覽器重新下載。實現該功能一個簡單的辦法就是將“指紋”hash信息添加到文件名里,例如:
當文件內容發生變化的時候 `Common Chunks plugin`會發出生成一個“chunkhash”。Webpack在進行文件輸出的時候可以使用這個hash值將它添加到輸出的文件名里:
output: { filename: '[name].[chunkhash].js' },
當我們如此配置后,打包生成的文件就會發生變化,類似*app.3b80b7c17398c31e4705.js*
## 自動注入打包文件
當我們安裝上面提及的方法為文件添加指紋信息后,那么在每個引用文件的地方每當打包文件發生變化我們都得去更新引用信息,因為生成的文件名每次都會發生變化(hash值會改變)。
`<script src="app.3b80b7c17398c31e4705.js"></script>`,如果全部由人工手動的方式來做那么無疑這是個艱巨的任務,幸運的是我們可以使用*HTML Webpack Plugin *。這個插件可以在編譯運行時將相關引用(打包生成的文件)自動注入到html文件中。
一開始我們需要將相關引用從index.html中移除
index.html
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>test-6</title> </head> <body> <div id="app"></div> <!-- built files should go here, but will be auto injected --> </body> </html>
將*HTML Webpack Plugin*添加到配置信息
new HtmlWebpackPlugin({ filename: 'index.html' template: 'index.html', inject: true, chunksSortMode: 'dependency' }),
至此,構建生成的帶有指紋信息的文件將自動注入到index.html文件中。
4.代碼分割
默認情況下,Webpack將會把所有的應用代碼打包到一個文件里面。但是當我們的應用有多個頁面的時候將各自的代碼生成到獨立的文件會更加高效,當頁面加載時只加載各自需要的文件。
Webpack提供了 "code splitting" 的功能可以實現此要求。
## 異步組件
與將定義對象信息放置作為第二個參數相比不同,異步組件需要使用到Promise,例如:
Vue.component('async-component', function (resolve, reject) { setTimeout(()=> { resolve({ // Component definition including props, methods etc. }); }, 1000) })
## require
當需要加載使用異步組件的時候可以使用Webpack的require語法,這將會告訴Webpack將異步組件打包到一個獨立的文件,Webpack將通過AJAX的方式加載這個文件,因此在代碼里可以這樣寫:
Vue.component('async-component', function (resolve) { require(['./AsyncComponent.vue'], resolve) });
## 延遲加載
在Vue.js應用中我們會使用*vue-router*來管理將我們的單頁面應用轉換為多個頁面,延遲加載是使用Vue和Webpack實現代碼分割的方式
const HomePage=resolve=> require(['./HomePage.vue'], resolve); const rounter=new VueRouter({ routes: [ { path: '/', name: 'HomePage', component: HomePage } ] })
來源:[4 Ways To Boost Your Vue.js App With Webpack](https://vuejsdevelopers.com/2017/06/18/vue-js-boost-your-app-with-webpack/?fbclid=IwAR1E-I2lTdttUGgjHAU-aVtTWoDN0qYeVtDFlKEpevbGGlqEZ4taJIGyT4c)
作者:Anthony Gore
式是Vue CLI項目中的一個重要概念,默認情況下它有三種模式:
通過傳遞–mode選項標志,可以覆蓋用于命令的默認模式。例如,如果要在build命令中使用開發變量:
vue-cli-service --mode development
運行 vue-cli-service 時,將從所有相應的文件加載環境變量,如果它們不包含 NODE_ENV 變量,則會相應地進行設置。
例如,NODE_ENV 將在生產模式下設置為 “production”,在測試模式下設置為 “test”,否則默認為 “development”;然后 NODE_ENV 將確定應用程序運行的主要模式-開發、生產或測試-并因此創建什么樣的webpack配置。
注意:如果 NODE_ENV 的環境中有默認值,則它在運行 vue-cli-service 命令時將刪除或進行顯示設置。
環境變量我們可以從它的模式還有變量內容進行認識和了解:
我們可以通過在項目根目錄中放置以下文件來指定環境變量:
.env // 在所有情況下加載
.env.local // 在所有情況下加載,被git忽略
.env.[mode] // 僅以指定模式加載
.env.[mode].local // 僅在指定模式下加載,被git忽略
一個環境文件僅包含環境變量的key=value對;
只有以開頭的變量 VUE_APP_才會使用靜態嵌入到客戶端包中 webpack.DefinePlugin;
加載的變量將對所有 vue-cli-service 命令,插件和依賴項可用。
在客戶端代碼中使用 Env 變量
console.log(process.env.VUE_APP_SECRET)
在構建過程中,process.env.VUE_APP_SECRET 將被相應的值替換。在的情況下 VUE_APP_SECRET=secret,將被替換 "secret"。
所有已解析的env變量都將 public/index.html 在HTML插值中討論的內部可用。
局部變量
默認情況下,.gitignore 將忽略本地env文件。
有時候我們有不需要提交到代碼塊的env變量時,而且項目托管也在公共存儲庫中時,這種情況下,我們應該改用 .env.local 文件。.local 也可以附加到特定于模式的env文件中。
*請認真填寫需求信息,我們會在24小時內與您取得聯系。