基於webpack4.0手動搭建web專案(更新中)
閒暇之餘做的總結,仍在更新中,有錯誤或不足的地方請大家指正與分享見解,謝謝。
一.準備工作
1.初始化專案目錄:npm init -y
2.建立以下的目錄結構:
--dist
--src
----index.js
--package.json(初始化後生成)
3.完成基本的webpack配置:
首先,安裝基本的webpack包:
npm install --save-dev webpack webpack-cli
接著,進行簡單的webpack配置:
在根目錄下建立webpack.config.js:
const path = require('path'); module.exports = { entry: './src/index.js', output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dist') } }
4.在package.json檔案的scripts屬性下配置npm指令碼:
"scripts": {
...
"build": "webpack"
}
5.這個時候我們只要在終端輸入npm run build就可以對src目錄下的index.js檔案打包,在dist目錄下生成bundle.js檔案。
二.ES6的babel轉碼配置
1.安裝babel-preset-env包:npm install –save-dev babel-preset-env
2.在根目錄下建立.babelrc檔案:
{ "presets": [ "env" ], "plugins": [] }
3.為了檢驗我們的配置是否成功,我們再安裝babel-cli包:npm install --save-dev babel-cli,然後我們在根目錄下建立一個babel_test檔案用於測試,如下:
--babel_test
----test.js
test.js:
let arr = [1, 2, 3];
console.log([...arr]);
並在package.json檔案中新增npm指令碼:
"scripts": { ... "build": "webpack", "babel": "babel ./babel_test/test.js -o ./babel_test/res.js" }
該指令碼的作用就是將test.js進行轉碼,並將轉碼後的內容儲存到res.js檔案中。
在終端執行npm run babel則會在相同目錄下生成res.js:
"use strict";
var arr = [1, 2, 3];
console.log([].concat(arr));
該檔案已經轉碼成功,說明我們的配置是正確的。
三.安裝簡單的loader解析
webpack中,有一種操作就是在“.js”檔案中引入非javascript資源,所以在將其打包的過程中,我們需要某些loader解析器對相關的資源進行解析。
首先我們先來看看引入css資源:
安裝style-loader和css-loader兩個loader:npm install --save-dev style-loader css-loader
webpack.config.js:
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'vue$': 'vue/dist/vue.esm.js',
'@': './src'
}
},
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
'css-loader'
]
}
]
}
}
這個時候我們就可以往入口檔案(index.js)中import './style.css'。現在,當該模組執行時,含有css字串的<style>標籤,將被插到html檔案的<head>中。廢話不多說,實慄說話:
/src/style.css:
.hello {
color: red;
}
/src/index.js
import './style.css'
我們對index.js進行打包:npm run build,便在dist目錄下產生了bundle.js
為了驗證我們的樣式是否已經打包成功,我們在dist目錄下建立一個index.html檔案,並將bundle.js檔案引入進來:
/dist/index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>webpack_all</title>
</head>
<body>
<p class="hello">hello world</p>
<script src="./bundle.js"></script>
</body>
</html>
這個時候我們看到在頁面上呈現紅色字型的“hello world”:
小夥伴可能會說:“這不對啊,明明之前說css檔案中的樣式將會通過style標籤插入到html文字中,但是上面的index.html只是將bundle.js插入進去而已。”
別急,上圖說話:
看到沒有,當我們在瀏覽器中開啟該index.html檔案,則會發現包含內容的style標籤已經被插入到頁面中了。也就是說,通過style-loader和css-loader對入口檔案進行打包之後,我們可以通過在頁面中引入bundle.js的方式通過bundle.js來插入style標籤。(之後有時間想研究一下其中的原理,小夥伴們有知道的可以分享一下哦,想想大概也就是通過js來建立style標籤,然後插入)
那如果我們想要匯入圖片呢?這個時候我們就可以用file-loader。
npm install --save-dev file-loader
webpack.config.js:
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'vue$': 'vue/dist/vue.esm.js',
'@': './src'
}
},
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
'css-loader'
]
},
{
test: /\.(png|svg|jpg|gif)$/,
use: [
'file-loader'
]
}
]
}
}
現在,當你 import MyImage from './my-image.png',該影象將被處理並新增到 output 目錄,並且_ MyImage 變數將包含該影象在處理後的最終 url。當使用 css-loader 時,如上所示,你的 CSS 中的 url('./my-image.png') 會使用類似的過程去處理。loader 會識別這是一個本地檔案,並將 './my-image.png' 路徑,替換為輸出目錄中影象的最終路徑。html-loader 以相同的方式處理 <img src="./my-image.png" />。
現在我們向專案中新增一個影象:
現在我們在/src目錄下面新增一張圖片:
/src/Hydrangeas.jpg:
在index.js中匯入該圖片:
import Icon from './Hydrangeas.jpg'
現在我們只是將圖片匯入進來並沒有將它使用,故而我們還要新增以下的程式碼:
function component() {
let element = document.createElement('div');
// 將影象新增到我們現有的 div。
let myIcon = new Image();
myIcon.src = Icon;
element.appendChild(myIcon);
return element;
}
document.body.appendChild(component());
然後打包:npm run build
開啟index.html,呈現以下的頁面:
實際上,在打包的過程中我們也把該圖片打包到出口目錄中,如下:
只不過圖片的名字已經改成了隨機串,同時頁面中引用的圖片也是打包後的這張圖片,如下:
這樣來說,其實我們也就可以在頁面中引用這張圖片,不過有一個問題:我們每次打包完之後圖片的名稱都不一樣,也就說如果我們需要在頁面中直接引用該圖片,我們就需要每次改變在頁面中引用的圖片名,這是特別麻煩的一件事。有了問題就需要解決,不過這個問題我們把它放到講plugin(外掛)的時候再來解決。
現在我們已經將普遍會用到的兩種基本的資源通過loader來進行相應的解析操作,但是各位小夥伴們可能沒有發現,就是我們之前配置的babel轉碼在webpack中有用到嗎?對於這個問題,我們先來看看被打包後的檔案bundle.js:
從上面的圖片可以看到有Symbol的字串,小夥伴們會很肯定地覺得webpack打包的時候並沒有使用我們之前的配置對程式碼進行相應的轉碼。不過我們不應該這樣看,因為我們目前的配置確實不能將Symbol進行轉碼,要對它進行轉碼還需要配置其他的一些外掛。不過實際上我們也確實沒有對程式碼進行轉碼處理,如果小夥伴想要驗證它沒有轉碼,很簡單,就是在我們的路口檔案中新增“let arr = [1, 2];console.log([...arr]);”,然後在打包之後我們在打包後的檔案中搜索console.log找到我們上面console.log轉碼後的部分,然後就能看到了,小夥伴們可以去試試,這裡我們就不演示了。
接下來我們來了解下如何在打包的時候對程式碼進行相應的轉碼:
首先我們要安裝babel-loader包:npm install --save-dev babel-loader
然後在webpack.config.js中新增babel-loader規則:
module: {
rules: [
...
{
test: /\.js$/,
use: [
'babel-loader'
]
}
]
}
這裡我們先打斷一下,我們先來看看官方是怎麼做的:
上面是說還需要安裝babel-core包,該包是babel的核心依賴包,babel的核心api都在這裡面。
廢話就不多說了,我們繼續我們之前的操作,我們來看看沒有安裝babel-core能不能成功(很顯然是不能的,畢竟官網要下載babel-core包,也就說明babel-loader是建立在babel-core的基礎上來實現的),先來看看栗子:
先在我們的入口檔案,也就是index.js上,我們加入兩條語句:
let arr = [1, 2, 3];
console.log([...arr]);
然後我們打包生成bundle.js檔案,在該檔案中查詢console.log:
咦???語句已經被轉碼了,官網耍我們嗎???
別急,小夥伴們記得之前我們為了測試還安裝了babel-cli嗎,其實目前是這個在起作用,不信我們就把這個包給刪了。我們先把package.json中相應的模組刪除,然後刪除node_modules資料夾,然後再npm install一下。(個人不喜歡用uninstall來刪除包,因為之前有過幾次在通過這個命令刪除包之後,發現專案就出問題了)
完成上面的步驟,我們進行打包,發現出問題了。。。:
嗚嗚嗚。。。以後還是別太高看自己,官方不會騙人的,只能說你能力還不足而已。。。
然後我們再把babel-core進行安裝之後就和剛剛轉碼是一樣的,另外為了方便檢測我們仍然把babel-cli重新安裝,至於為什麼babel-core和babel-cli都可以,其中的原理小夥伴們可以自行去研究一下。
之前在網上看到別人寫的只安裝webpack和babel-core就能進行轉碼簡直就是扯淡,連最基本的babel-preset-XX都沒有,希望大家都嘗試去看看官方文件,以官方為準,或者自己多動手試試,不要被網上一些自以為是的人寫的文章所誤導。
四.plugins(外掛)
loader被用於轉換某些型別的模組,而外掛則可以用於執行範圍更廣的任務。外掛的範圍包括,從打包優化和壓縮,一直到重新定義環境中的變數。外掛介面功能極其強大,可以用來處理各種各樣的任務。
想要使用一個外掛,你只需要require()它,然後把它新增到plugins陣列中。多數外掛可以通過選項(option)自定義。你也可以在一個配置檔案中因為不同目的而多次使用同一個外掛,這時需要通過使用new操作符來建立它的一個例項。
這裡我們只簡單地講幾個外掛。
1.HtmlWebpackPlugin
這個外掛非常有用,它會生成一個html檔案,並將打包後的檔案新增到進去,也就省去了我們手動去建立去新增,特別是我們打包的檔名可能經常會更改,這時這個外掛就會非常有用。
現在我們來簡單演示一遍:
首先安裝該外掛:npm install --save-dev html-webpack-plugin
在webpack.config.js中進行配置,新增plugins屬性如下:
plugins: [
new HtmlWebpackPlugin({
title: 'yaodebian',
filename: 'index.html',
template: './src/index.html',
inject: true
})
]
當然別忘了在檔案開頭對該外掛進行引入:const HtmlWebpackPlugin = require('html-webpack-plugin');
上面我進行了某些配置:title、filename、template、inject
title: 其實就是html檔案中title標籤的內容,上面的title配置其實會被template中指定的html檔案配置。在沒有配置template屬性時,自動生成的html檔案的title標籤欄位就是該title屬性的值。
filename: 指定生成的html檔案的位置,其相對於出口目錄而定;
template: 模板就是用來copy的,該屬性就是指定一個html檔案作為將會生成的html檔案的模板,相對於webpack配置檔案目錄而言,它的作用就是生成的檔案內容將會在template指定的檔案內容基礎上再將打包後的bundle.js檔案新增進去組合成一個新的html檔案。
- inject: 它有四個值true、body、head、false
- true 預設值,script標籤位於html檔案的 body 底部
- body script標籤位於html檔案的 body 底部
- head script標籤位於html檔案的 head中
- false 不插入生成的js檔案,這個幾乎不會用到的
廢話不多說,舉個栗子:
像上面配置中,我是在src目錄下添加了一個index.html並作為模板,index.html中的內容如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>webpack_all</title>
</head>
<body>
<p class="hello">hello world</p>
<img src="./Hydrangeas.jpg" alt="">
</body>
</html>
我們再來打包一下,結果可以看到dist目錄下生成了一個新的index.html覆蓋了之前的index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>webpack_all</title>
</head>
<body>
<p class="hello">hello world</p>
<img src="./Hydrangeas.jpg" alt="">
<script type="text/javascript" src="bundle.js"></script></body>
</html>
看到了吧,就是將我們的模板新增上打包後的bundle.js檔案,這樣我們就不用自己建立html檔案並手動插入script。
不過有一個問題,這其實就是我們之前在講loader的時候講的那個問題,用file-loader引入的一張圖片,並會在出口目錄下會生成一張以圖片內容的MD5雜湊值命名的圖片。這個時候我們要在html檔案中通過img標籤來引用與呈現這張圖片時就需要手動更改標籤中src屬性中圖片的名字。我們想要讓它自動更改應該要怎麼實現呢?
自己在網上搜索了下,找到一個國人寫的loader: html-withimg-loader
首先,我們安裝該loader:npm install --save-dev html-withimg-loader,然後我們在webpack.config.js新增如下配置:
{
test: /\.html$/,
loader: 'html-withimg-loader'
}
最後在入口的js檔案中引入我們作為html-webpack-plugin模板的html檔案:
import './index.html'
這個時候我們進行打包,產生的index.html檔案如下:
dist/index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>webpack_all</title>
</head>
<body>
<p class="hello">hello world</p>
<img src="bdf3bf1da3405725be763540d6601144.jpg" alt="">
<script type="text/javascript" src="bundle.js"></script></body>
</html>
我們看下dist目錄下的檔案結構:
看到了吧,這個時候img標籤的圖片名就和打包後的圖片是一樣的了。
不過,這個loader在平時應該不怎麼會去用,這裡只是針對之前遇到的問題使用這個loader。
2.clean-webpack-plugin
你可能已經注意到,由於過去的指南和程式碼示例遺留下來,導致我們/dist資料夾相當雜亂。webpack 會生成檔案,然後將這些檔案放置在 /dist 資料夾中,但是 webpack 無法追蹤到哪些檔案是實際在專案中用到的。
通常,在每次構建前清理 /dist 資料夾,是比較推薦的做法,因此只會生成用到的檔案。讓我們完成這個需求。
上面兩段是官方的原話,可能有些小夥伴不理解。通俗的講,假如我們dist目錄下有一個hello.js檔案,但是我們打包生成的檔案中沒有這個檔案,那打包之後這個檔案就是多餘的,我們根本不會用到,所以我們需要通過某些手段在打包之前將dist目錄清空。這裡,我們用到了clean-webpack-plugin。
安裝該外掛:npm install --save-dev clean-webpack-plugin
在webpack.config.js檔案中進行如下配置:
新增外掛宣告:const CleanWebpackPlugin = require('clean-webpack-plugin');
使用外掛:new CleanWebpackPlugin(['dist'])
上面初始化外掛配置時傳入的引數就是包含了要清空的目錄的陣列,詳細的配置可以去官網檢視。
為了驗證外掛的效果,我們先在dist目錄下建立一個hello.js的檔案:
最後我們再進行打包:npm run build
結果如下:
說明外掛已經生效了!!!
對於上面的應用,官方提出了一個叫做Manifest的概念:
這裡就不贅述,小菜我也還沒研究過,希望之後找個時間再另外單獨研究並總結一下。同樣也希望各位小夥伴或大大分享相關方面的知識或文章哦!!!
五.suorce map
當 webpack 打包原始碼時,可能會很難追蹤到錯誤和警告在原始碼中的原始位置。例如,如果將三個原始檔(a.js, b.js 和 c.js)打包到一個 bundle(bundle.js)中,而其中一個原始檔包含一個錯誤,那麼堆疊跟蹤就會簡單地指向到 bundle.js。這並通常沒有太多幫助,因為你可能需要準確地知道錯誤來自於哪個原始檔。
為了更容易地追蹤錯誤和警告,JavaScript 提供了 source map 功能,將編譯後的程式碼映射回原始原始碼。如果一個錯誤來自於 b.js,source map 就會明確的告訴你。
Source map有很多不同的選項可用,請務必仔細閱讀它們,以便可以根據需要進行配置。
上面是官方的原話,本人懶,直接拷貝過來了。。。
好了,現在來看栗子了,這裡使用的是官方的栗子(其他的看心情再搗鼓搗鼓。。。似不似很欠扁。。。哈哈):
首先,我們在webpack.config.js中新增devtool選項:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
devtool: 'inline-source-map',
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
'css-loader'
]
},
{
test: /\.(png|svg|jpg|gif)$/,
use: [
'file-loader'
]
},
{
test: /\.js$/,
use: [
'babel-loader'
],
exclude: /node_modules/,
include: path.resolve(__dirname, 'src')
},
{
test: /\.html$/,
loader: 'html-withimg-loader'
}
]
},
plugins: [
new HtmlWebpackPlugin({
title: 'yaodebian',
filename: 'index.html',
template: './src/index.html',
inject: true
}),
new CleanWebpackPlugin(['dist'])
]
}
然後先對入口檔案進行一些修改,新增如下:
hahaha();
因為hahaha()之前我們並沒有宣告過,肯定會報錯。我們先打包:npm run build
然後我們執行一下:
點進去看看:
我們看到,明明是23行出錯,但是它卻說21行報錯,是source map解析出錯了嗎,不禁產生這樣的想法。
我們在22行處新增consoe.log('yaodebian'):
import './index.html'
import './style.css';
import Icon from './Hydrangeas.jpg'
console.log('yaodebian');
let arr = [1, 2, 3];
console.log([...arr]);
function component() {
let element = document.createElement('div');
// 將影象新增到我們現有的 div。
let myIcon = new Image();
myIcon.src = Icon;
element.appendChild(myIcon);
return element;
}
document.body.appendChild(component());
consoe.log('yaodebian');
hahaha();
我們再次打包看看:
這次卻是正確的,很好的標記出了錯誤,至於為什麼之前的不準確目前不知道其原理,姑且先將它視作是souce map解析的問題吧,有知道的小夥伴請告知一下下。
之後看了幾篇文章,對於devtool這個配置選項,不同的屬性值會導致不同程度的souce map解析定位,以及會影響構建速度,不過並沒有怎麼看懂。這裡就不再贅述了,涉及的內容比較多,想之後再單獨抽時間總結一下。
接著上面的繼續。。。還有好多知識點。。。
六.開發工具(自動編譯程式碼)
每次要編譯程式碼時,手動執行npm run build特別麻煩。
webpack中有幾個不同的選項,可以幫助你在程式碼發生變化後自動編譯程式碼:
1.webpack's Watch Mode(觀察模式)
2.webpack-dev-server(提供一個伺服器)
3.webpack-dev-middleware(是一個容器(wrapper),它可以把 webpack 處理後的檔案傳遞給一個伺服器(server)。 )
webpack's Watch Mode(觀察模式)
上面是官方原話,沒有看懂就先來看一個栗子:
首先在package.json中新增一個npm script指令碼:
=================以下內容更新於2018.9.20======================
拖了將近一個月了,媽賣批的~~~
======================================================
"script": {
...,
"watch": "webpack –watch",
...
}
現在我們就可以在命令列中執行npm run watch啟動觀察模式,然後開啟之前建立的index.html檔案,報錯:
這是因為之前為了檢測souce map而設定的錯誤,我們把它去掉就好。
然後我們在src中的入口檔案(index.js)檔案中新增一句“console.log('watch mode')”,儲存,會發現系統自動幫我們重新編譯了(說明我們的配置生效了),重新重新整理頁面則會看到如下:
控制檯上則多出了一句watch mode。
觀察模式雖然能夠達到自動編譯程式碼的效果,但是其有一個缺點,就是隻能幫我們自動重新編譯,但不會幫我們重新重新整理瀏覽器。如果想要在重新編譯程式碼的同時重新整理瀏覽器,則可以嘗試使用webpack-dev-server。
另外,在執行watch mode後,會看到webpack編譯程式碼卻不會退出命令列,這是因為script指令碼還在觀察檔案。
使用webpack-dev-server
webpack-dev-server提供一個簡單的web伺服器,並且能夠實時重新載入(living reloading)。
首先我們需要安裝相應的包:npm install –save-dev webpack-dev-server
然後在webpack.config.js中新增devServer選項:
module.exports = {
...,
devServer: {
contentBase: './dist'
},
...
}
以上的配置使得webpack-dev-server將在localhost:8080下建立服務,將dist目錄下的檔案,作為可訪問檔案。
最後再在package.json檔案中新增script指令碼:
"script": {
...,
"start": "webpack-dev-server --open",
...
}
現在,通過在命令列中執行npm start(注意這裡是直接用npm start,一般我們都是使用npm run start,webpack-dev-server比較特殊,我們還是使用通用的npm run start以免出錯),就會看到瀏覽器自動載入頁面。如果現在修改和儲存任意原始檔,web伺服器就會自動重新載入編譯後的程式碼。
使用webpack-dev-middleware
webpack-dev-middleware 是一個容器(wrapper),它可以把 webpack 處理後的檔案傳遞給一個伺服器(server)。 webpack-dev-server 在內部使用了它,同時,它也可以作為一個單獨的包來使用,以便進行更多自定義設定來實現更多的需求。
接下來是一個webpack-dev-middleware配合express server的示例。
首先安裝express和webpack-dev-middleware包:
npm install –save-dev express webpack-dev-middleware
接下來對webpack配置檔案做一些調整,以確保中介軟體(middleware)功能能夠正確啟用:
webpack.config.js:
output:{
...,
publicPath: '/'
}
publicPath也會在伺服器指令碼用到,以確保檔案資源能夠在http://localhost:3000下正確訪問,
我們稍後再設定埠號。下一步就是設定我們自定義的 express 服務:
專案根目錄下我們新增一個server.js檔案(server.js):
const express = require('express');
const webpack = require('webpack');
const webpackDevMiddleware = require('webpack-dev-middleware');
const app = express();
const config = require('./webpack.config.js');
const compiler = webpack(config);
// Tell express to use the webpack-dev-middleware and use the webpack.config.js
// configuration file as a base.
app.use(webpackDevMiddleware(compiler, {
publicPath: config.output.publicPath
}));
// Serve the files on port 3000.
app.listen(3000, function () {
console.log('Example app listening on port 3000!\n');
});
現在新增一個npm script:
"script": {
...,
"server": "node server.js",
...
}
七.tree shaking
新增一個通用模組:
src/math.js:
export function square(x) {
console.log('square');
return x*x;
}
export function cube(x) {
console.log('cube');
return x*x*x;
}
接著,更新入口指令碼,並使用其中一個方法:
src/index.js:
...
import {cube} from './math.js';
console.log(cube(2));
...
現在我們執行我們的npm指令碼npm run build,並檢查我們輸出的bundle:
注意,我們沒有匯入squre,但是它仍然被包含在bundle中,這個是為什麼呢,為什麼還會保留?小夥伴們可能會說“這還用問嗎,現在根本就沒有進行任何配置,自然不會幫我們進行優化”,真的是這樣嗎,我們繼續看。
現在,我們在package.json檔案中新增一個配置"sideEffects": false (這個配置的意思是將入口檔案涉及到的檔案都標識為不含副作用,以此來告知webpack,它可以安全地刪除未用到的export匯出)
然後輸入命令npm run build進行打包操作,結果如下:
我擦,根本沒有上面卵用,和文件上是一樣的啊。~~~莫急莫急,我們接著來~~~
前方高能,膽小勿入~~~(以下黑色粗體字為本人自我臆想,切莫相信,切莫相信)
同樣的,沒有進行treeShaking,沒有引用的函式還是包含在bundle.js檔案中,按道理,如果它真的會優化,當我們設定"sideEffects": false,它便會進行優化打包,實際上它沒有,那麼真的沒有嗎?
其實上面的所有情況(包括不設定sideEffects配置的情況)webpack都會幫我們進行優化,操作treeShaking,至於為什麼,這裡要提一下:
還記得官方怎麼說的嗎?
Tree shaking必須要依賴於es6的import和export,針對export引用的檔案以及真正使用到的部分進行優化處理,而我們在這之前進行了babel轉碼的操作,對於轉碼後的es5語法我們就不會使用到tree shaking。
是的,上面的純屬本人扯淡,其實事實並非如此,其實在加了sideEffects時,webpack確實已經將我們的程式碼進行tree shaking,我們繼續來玩玩吧:
我們在index.js入口檔案中新增這樣幾句,如:
function test() {
return 1234;
}
test();
咱打下包吧,然後搜尋一下1234看看,結果:
看到沒找不到,說明webpack已經啟動了tree shaking並檢測出上述程式碼是dead code(無用程式碼),故而將上述程式碼刪除。
那~~~,為哈子我們import時的程式碼中square函式仍然保留呢?這個嘛~~~這個確實跟es6語法有關:因為我們之前不是用了babel嗎,babel把es6轉化為es5的過程中,會使得轉化之後的程式碼產生副作用,故而上述math.js中的square會存在其中。
A:那~~~不是說sideEffects設為false會將程式碼標識為pure(純的)嗎,怎麼還會有呢???
B:好說好說,看來這位同學沒有理解其中的意思,該配置將程式碼標識為純,就說明程式碼沒有副作用,沒有副作用的話,我們就可以刪除那些看似無用的程式碼(根據webpack的解析規則),而不用擔心誤刪了某些程式碼。Babel將es6轉化為es5,而程式碼沒有刪除,說明轉化後的程式碼不在tree shaking的無用程式碼佇列中。
A:哦哦,貌似懂了~~~
所以呢,為了刪除上面沒有用到的square函式,我們需要去除掉babel的作用效果,即在webpack.config.js配置檔案中註釋掉babel的配置,如下:
接著我們重新打包檔案:
看到沒有,現在只剩下cube這個函數了。
好了,接下來我們來看看通過sideEffects來將某些檔案標識為有副作用吧,這樣一來,tree shaking就不會隨便將該檔案中“看似無用”的程式碼刪除,而將它們保留下來,就是它會將“可能有副作用”的程式碼保留下來(具體的解析原理暫時沒有了解過),那些確實無用的程式碼仍然會被刪除。
其實,就是說,當我們將程式碼標識為sideEffects:false時,可能會誤刪具有副作用的程式碼,下面我們就來舉一個栗子吧(目前sideEffects仍然為false):
我們在src目錄中建立一個menu.js檔案:
function Menu() {
}
Menu.prototype.show = function() {
}
Array.prototype.unique = function() {
// 將 array 中的重複元素去除
}
export default Menu;
並在index.js中將其引入:
import Menu from './menu.js';
然後我們打包看看:
居然找不到之前的Array.prototype.unique = function() {...},心裡媽賣批“這句是我們要的,是有副作用的,你憑啥刪了”。。。
然後tree shaking小姐姐回了一句:“就憑你沒告訴我啊,我怎麼知道這句是有用的”
“好了好了,下次執行之前,我跟你講一句好了~~~”
現在我們將sideEffects設定為一個數組如:
看到了沒,出來了吧,所以如果我們不能保證我們的程式碼是純的(pure),也就是沒有副作用,那麼我們便要將相應的檔案標識為有副作用,以免某些程式碼會被誤刪。上面的只是找到的一個特例,我們平常也不會那樣用,我們會import其中的menu,並執行它,這樣Array.prototype將會被引用進來,也就不需要上面標識了。具體還有什麼比較切實際的栗子目前暫不可知,有知道的哥們或者小姐姐請留言分享一下。
好了,基本上tree shaking就講的差不多了,不過還有一個東西要講,就是webpack4(上面測試的版本是4.16.5,是預設支援的,前面幾個版本就不知道了)預設是帶有tree shaking功能的,也就是說我們不用配置sideEffects其實已經有tree shaking了,sideEffects只是起一個標識的作用。下面我們來測試一下吧:
將sideEffects配置去掉,然後打包一下:
我們發現沒有square,而只有cube,另外我們查一下之前新增的test函式是否存在,就是這個函式:
結果:
所以,看到了吧,tree shaking生效了。
另外,除了預設具有tree shaking之外,還預設標記所有程式碼都有副作用,為了驗證這個猜想,我們進行一下檢測:
我們先把上一次打包後的bundle.js儲存到我們的src目錄下;
然後在pakage.json檔案中配置sideEffects為:["./src/*.*"],這樣就相當於標識多有檔案有副作用,我們只要對比此次打包後的檔案是否和上一次打包的檔案相同,就知道我們的猜想是否是正確的:
仔細對比一下就會發現它們是相同的,也就驗證了我們的猜想。
這一節也講了很久了,最後提醒一下,千萬要將引入的.css等一些檔案標識為有副作用,不然css檔案的引入會被刪除,也就不會應用於html檔案中。
更新中。。。
相關推薦
基於webpack4.0手動搭建web專案(更新中)
閒暇之餘做的總結,仍在更新中,有錯誤或不足的地方請大家指正與分享見解,謝謝。 一.準備工作 1.初始化專案目錄:npm init -y 2.建立以下的目錄結構: --dist --src ----index.js --package.json(初始化後生成)
Dockerfile 之 tomcat中執行MyEclipse搭建Web專案(Docker系列)
本文章來自【知識林】 在之前的講解中主要講述的是如何使用已經存在的Docker映象,當然這些映象對我們的使用肯定有很大的幫助,但很多時候我們是需要執行我們自己所定製開發的應用程式,這些應用程式在Doc
快速建立一個基於Gradle構建的SpringBoot Web專案(SpringBoot-01)
快速建立一個基於Gradle構建的SpringBoot Web專案 在建立SpringBoot專案之前需要提前配置好電腦環境:JDK 、Gradle。 首先我們會通過 SpringBoot 官方提供的 Spring Initializr 這樣的一個專
基於Springboot2.0的Dubbo入門專案(dubbo-spring-boot-starter)
Dubbo是阿里巴巴公司開源的一個高效能優秀的服務框架,使得應用可通過高效能的 RPC 實現服務的輸出和輸入功能,可以和Spring框架無縫整合。最近半年來,Dubbo的得到了快速的維護,官方也推出了整合Springboot的jar包,及其貼合時代潮流,那麼現在就來講Spri
springBoot 搭建web專案(前後端分離,附專案原始碼地址)
springBoot 搭建web專案(前後端分離,附專案原始碼地址) 概述 該專案包含springBoot-example-ui 和 springBoot-example,分別為前端與後端,前後端分離,利用ajax互動。 springBoot-exam
web專案(jsp+Java)錯誤總結
一:當部署專案時出現Project facet Java version 1.8 is not supported.(jdk版本不一致) 解決方案:錯誤專案右鍵properties ——》 找到project Facets修改裡面的Java版本(適合的版本)
idea 中執行web 專案(連線資料庫)
一共要匯入的包: 專案的建立: 在WEB-INF 資料夾下,建立classes 和 lib 資料夾 file----project structure--------Modules-------Path 將Output path 和 Te
docker部署web專案(用tomcat)
建立tomcat容器 -d指後臺執行 --name : 起別名 -p 對映的埠docker run tomcat --name mytomcat -p 8080:8080 -d進入容器內部 容器內部有一些設定檔案 只能通過這種方式進入 互動式的進入tomcat
手動搭建ssm框架(適宜新手)
在公司一直做分散式的專案,專案基本拿過來的時候架構師把整體框架,所用技術全都選擇好了,我們只要按照約束去寫程式碼就好了,沒事,我就想看自己是否還能搭建出ssm框架,說實話,還是耗費力氣的,不過總算弄出來了,適合顯新手。 我的jdk是1.7版本的 首先我們新建一個maven
IntelliJ IDEA 建立Web專案(全教程)
轉載:https://www.cnblogs.com/jxldjsn/p/8203859.html說明:IntelliJ IDEA 版本為14.JDK 版本為1.7tomcat 版本為apache-tomcat-7.0.70注:在建立過程中注意相關軟體版本位數的問題。32位,
如何用Maven建立web專案(具體步驟)
最後一步,我們要把當前的build path 指向 Maven Dependency, 直接點選add,選擇Java Build Path Entries 然後next 然後再點選finish完成 完成後如下圖: 至此一個基於maven的webapp就建立好了,並可以直接從e
無需付費,教你IDEA社群版中開發Web專案(SpringBoot\Tomcat)
1、IDEA 版本介紹 最近有小夥伴私信我說 IDEA 破解怎麼總是失效?難道就沒有使用長一點的嗎... 咳咳,除了給我留言「啟用碼」外,或許社群版可能完全滿足你的需求。 相信有挺多小夥伴可能不清楚或者沒聽過社群版,其實 IDEA 有三個版本: Community:社群版,相當於 OpenJDK 的存在,
優雅的使用 PhpStorm 來開發 Laravel 專案(翻譯中)
Laravel 是一個免費的開源的PHP框架,它建立在Symfony 元件之上、提供了使日常開發所用到的諸如認證、路由、sessions和快取管理的實現變得更加容易的框架。 在這個教程裡,我們將看到使用Phpstorm的Laravel外掛和Laravel IDE helper來充分利用Ph
《21個專案玩轉深度學習》在spyder3.6上跑2.7遇到的問題彙總(更新中...)
在spyder3.6上跑2.7遇到的問題彙總------------------------------------------------------1、NameError: name 'xrange' is not defined在Python 3中,range()的實現
PHP OAuth2.0 Server 搭建,問題解決持續更新中...
oauth2 server php http://oauth.net/2/ Step-By-Step Walkthrough Ref: http://bshaffer.github.io/oauth2-server-php-docs/cookbook/
Delphi - 手把手教你基於D7+Access常用管理系統架構的設計與實現 (更新中)
前言 從事軟體開發工作好多年了,學的越深入越覺得自己無知,所以還是要對知識保持敬畏之心,活到老,學到老! 健身和程式碼一樣都不能少,身體是革命的本錢,特別是我們這種高危工種,所以小夥伴們運動起來!有沒有健身擼鐵,體脂現在是多少呀?明年(2019/03/22)徐州的馬拉松有沒有報名呀!? 扯的有點遠了,
vfd-cloud——一個適合練習上手的雲端儲存網盤springboot專案(開發中)
# vfd-cloud [![](https://img.shields.io/badge/springboot-2.4.0-blue)](https://spring.io/projects/spring-boot) [![](https://img.shields.io
redis 在Windows下的安裝及基本操作(更新中~~~)
有用 redis 安裝 abc nbsp com inux eas pan 安裝目錄 Redis 安裝 Window 下安裝 下載地址:https://github.com/MSOpenTech/redis/releases。 Redis 支持 32 位和 64 位。這個需
《深入理解Java虛擬機:JVM高級屬性與最佳實踐》讀書筆記(更新中)
pen 內存區域 深度 span 進化 ria 最短 描述 core 第一章:走進Java 概述 Java技術體系 Java發展史 Java虛擬機發展史 1996年 JDK1.0,出現Sun Classic VM HotSpot VM, 它是 Sun JDK 和 Open
python總結(更新中)
註意 整數 元素 生成 更新 ron 外部 也會 mmu 1 python函數中的參數傳遞(註意可變和不可變傳遞) 可更改(mutable)與不可更改(immutable)對象 在 python 中,strings, tuples, 和 numbers 是不可更改的對象,而