構建工具備不少,好比Npm Script任務執行者、Grunt也是任務執行者、Gulp基於流的自動化構建工具、Fis3百度構建工具、Webpack打包模塊化JavaScript
工具和Rollup模塊打包工具。javascript
這裏談談用得很頻繁的webpack
構建工具。css
從最基本的概念開始瞭解:html
entry
是配置模塊的入口,必填。前端
module.exports = {
entry: './path/to/my/entry/file.js'
}
複製代碼
output
配置如何輸出最終想要的代碼。output
是一個object
,裏面包含一系列的配置項。java
output.filename配置輸出文件的名稱,爲string類型。node
output.path配置輸出文件存放在本地的目錄(路徑),必須是string類型的絕對路徑。react
path: path.resolve(__dirname, 'dist_[hash]')
複製代碼
output.publicPath配置發佈到線上資源的URL前綴,爲string類型。默認爲空字符串''
,即便用相對路徑。jquery
好比須要將構建的資源上傳到CDN服務上,以便加快網頁的打開速度。配置代碼以下:webpack
filename: '[name]_[chunkhash:8].js'
publicPath: 'https://cdn.example.com/assets/'
複製代碼
發佈到線上時候,HTML中引入的JavaScript
文件以下:git
<script src='https://cdn.example.com/assets/a_12345678.js'></script>
複製代碼
線上出現404錯誤的時候,看下路徑有沒有錯~
還有其餘配置請看文檔
module
配置如何處理模塊。
module.rules配置模塊的讀取和解析規則,一般用來配置Loader
。其類型是一個數組,數組裏每一項都描述瞭如何去處理部分文件。應用一項rules
時大體經過如下方式:
test
、include
、exclude
三個配置項來命中Loader
要應用規則的文件。use
配置項來應用Loader
,能夠只應用一個Loader
或者按照從後往前的順序應用一組Loader
,同時還能夠給Loader
傳入參數。Loader
執行順序默認是從右往左執行,經過enforce
選項可讓其中一個Loader
的執行順序放在前面或者最後。module: {
rules: [
{
// 命中scss文件
test: /\.scss$/,
// 處理順序從右往左
use: ['style-loader', 'css-loader', 'sass-loader'],
// 排除node_modules目錄下的文件
exclude: path.resolve(__dirname, 'node_modules'),
}
]
}
複製代碼
Loader須要傳入多個參數的時候的例子:
use: [
{
loader:'babel-loader',
options:{
cacheDirectory:true,
},
// enforce:'post' 的含義是把該 Loader 的執行順序放到最後
// enforce 的值還能夠是 pre,表明把 Loader 的執行順序放到最前面
enforce:'post'
},
// 省略其它 Loader
]
複製代碼
module.noParse配置項可讓webpack忽略對部分沒采用模塊化的文件的遞歸解析和處理,這樣作有助於提升構建性能。好比:
module: {
noParse: (content) => /jquery|lodash/.test(content)
}
複製代碼
module.rules.parser屬性能夠更細粒度的配置哪些模塊須要解析,哪些不須要,和noParse
配置項的區別在於parser
能夠精確到語法層面,而noParse
只能控制哪些文件不被解析。
module: {
rules: [
{
test: /\.js$/,
use: ['babel-loader'],
parser: {
amd: false, // 禁用 AMD
commonjs: false, // 禁用 CommonJS
...
}
}
]
}
複製代碼
Resolve
配置webpack如何尋找模塊所對應的文件。Webpack
內置Javascript
模塊化語法解析功能,默認會採用模塊化標準裏面約定好的規則去尋找,你也能夠按照需求修改默認規則。
resolve.alias配置項經過別名來把原導入的路徑映射成一個新的導入路徑。以下:
resolve: {
alias: {
components: './src/components/'
}
}
複製代碼
當你經過import Button from 'components/button'
導入時,實際上被alias
等價替換了import Button from './src/components/button'
。
resolve.modules配置webpack
去哪些目錄下找第三方模塊,默認只會去node_modules
目錄下尋找。
resolve.enforceExtension若是配置爲true
全部導入語句都必須帶有後綴,例如開啓前import './foo
能正常工做,開啓後就必須寫成import './foo.js'
。
Plugin
用於擴展Webpack
功能,各類各樣的Plugin
幾乎讓Webpack
能夠作任何構建相關的事情。
舉個例子:
const CommonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin');
module.exports = {
plugins: [
// 全部頁面都會用到的公共代碼提取到 common 代碼塊中
new CommonsChunkPlugin({
name: 'common',
chunks: ['a', 'b']
})
]
}
複製代碼
在開發環境的時候使用。要配置DevServer
,除了在配置文件裏面經過devServer
傳入參數外,還能夠經過命令行參數傳入。
注意:只有在經過DevServer
去啓動Webpack
時配置項文件裏devServer
纔會生效。
devServer.hot配置是否啓用使用DevServer中提到的模塊熱替換功能。
devServer.host配置項用於配置 DevServer 服務監聽的地址。
devServer.port配置項用於配置 DevServer 服務監聽的端口,默認使用8080
端口。
devServer.https配置HTTPS協議服務。某些狀況下你必須使用HTTPS,HTTP2 和 Service Worker 就必須運行在 HTTPS 之上。
devServer: {
https: true
}
複製代碼
Webpack的運行是一個串行的過程,從啓動到結束會執行如下流程:
Loader
對文件的轉換操縱很耗時,須要讓儘量少的文件被Loader
處理。
在使用Loader時能夠經過test
、include
、exclude
三個配置項來命中Loader
要應用規則的文件。
module.exports = {
module: {
rules: [
{
test: /\.js$/,
use: ['babel-loader?cacheDirectory'],
include: path.resolve(__dirname, 'src')
}
]
}
}
複製代碼
resolve.modules
配置resolve.modules
用於配置webpack去哪些目錄下尋找第三方模塊。
resolve.modules
的默認值是['node_modules']
,含義是先去當前的目錄下./node_modules
目錄下去找想找的模塊,以此類推,若是沒有找到就去上一級目錄../node_modules
中找,再沒有去上上一級,以此類推...
若是知道安裝的模塊在項目的根目錄下的./node_modules
時候,沒有必要按照默認的方式一層層找:
module.exports = {
resolve: {
modules: [path.resolve(__dirname, 'node_modules')]
}
}
複製代碼
resolve.alias
配置resolve.alias
配置項經過別名來把原導入路徑映射成一個新的導入路徑。能夠減小耗時的遞歸解析操做。
module.noParse
配置module.noParse
配置項可讓Webpack
忽略對部分沒采用模塊化的文件的遞歸解析處理,這樣作的好處是能提升構建性能。
const path = require('path');
module.exports = {
module: {
// 獨完整的 `react.min.js` 文件就沒有采用模塊化,忽略對 `react.min.js` 文件的遞歸解析處理
noParse: [/react\.min\.js$/],
}
}
複製代碼
HappyPack
把任務分解成多個子進程併發執行,子進程處理完後再把結果發送給主進程。減小了總的構建時間。
const HappyPack = require('happypack');
const os = require('os');
const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length });
module.exports = {
module: {
rules: [
{
test: /\.js$/,
loader: 'happypack/loader?id=happyBabel',
exclude: /node_modules/
}
]
},
plugins: [
new HappyPack({
id: 'happyBabel',
loaders: [{
loader: 'babel-loader?cacheDirectory= true',
}],
// 共享進程池
threadPool: happyThreadPool,
// 容許happypack輸出日誌
verbose: true,
})
]
}
複製代碼
例如:
module.export = {
watch: true,
watchOptions: {
// 監聽到變化發生後會等300ms再去執行動做,防止文件更新太快致使從新編譯頻率過高
// 默認爲 300ms
aggregateTimeout: 300,
// 判斷文件是否發生變化是經過不停的去詢問系統指定文件有沒有變化實現的
// 默認每隔1000毫秒詢問一次
poll: 1000
}
}
複製代碼
因爲保存文件的路徑和最後編輯時間須要佔用內存,定時檢查週期檢查須要佔用CPU
以及文件I/O
,因此最好減小須要監聽的文件數量和下降檢查頻率。
熱替換就是當一個源碼發生改變的時,只從新編譯發生改變的模塊,再用新輸出的模塊替換掉瀏覽器中對應的老模塊。
開啓熱替換:
webpack-dev-server --hot
複製代碼
區分開發環境和生產環境,進行不一樣的構建~
CDN又叫內容分發網絡,經過把資源部署到世界各地,用戶在訪問時按照就近原則從離用戶最近的服務器獲取資源,從而加速資源的獲取速度。
CDN 實際上是經過優化物理鏈路層傳輸過程當中的網速有限、丟包等問題來提高網速的。
結合publicPath
來處理:
const path = require('path');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const { WebPlugin } = require('web-webpack-plugin');
module.exports = {
output: {
filename: '[name]_[chunkhash:8].js',
path: path.resolve(__dirname, './dist'),
publicPath: '//js.cdn.com/id/'
},
module: {
rules: [
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
use: ['css-loader?minimize'],
publicPath: '//img.cdn.com/id/'
}),
}
]
},
plugins: [
new WebPlugin({
template: './template.html',
filename: 'index.html',
// 指定存放 CSS 文件的 CDN 目錄 URL
stylePublicPath: '//css.cdn.com/id/',
}),
new ExtractTextPlugin({
// 給輸出的 CSS 文件名稱加上 Hash 值
filename: `[name]_[contenthash:8].css`,
}),
]
}
複製代碼
將多餘的代碼移除。
webpack --display-used-exports --optimize-minimize
複製代碼
公共代碼的提取。
const CommonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin');
new CommonsChunkPlugin({
// 從哪些 Chunk 中提取
chunks: ['a', 'b'],
// 提取出的公共部分造成一個新的 Chunk,這個新 Chunk 的名稱
name: 'common'
})
複製代碼
對於採用單頁應用做爲前端架構的網站來講,會面臨一個網頁須要加載的代碼量很大的問題,由於許多功能都作到了一個HTML裏面,這會致使網頁加載緩慢、交互卡頓、用戶體驗將很是糟糕。
致使這個問題的根本緣由在於一次性的加載全部功能對應的代碼,但其實用戶每一階段只可能使用其中一部分功能。 因此解決以上問題的方法就是用戶當前須要用什麼功能就只加載這個功能對應的代碼,也就是所謂的按需加載。
Webpack 內置了強大的分割代碼的功能去實現按需加載。好比:
main.js
文件,網頁會展現一個按鈕main.js
文件中只包含監聽按鈕事件和加載按需加載的代碼。show.js
文件,加載成功後再執行show.js
裏的函數。main.js中:
window.document.getElementById('btn').addEventListener('click', function () {
// 當按鈕被點擊後纔去加載 show.js 文件,文件加載成功後執行文件導出的函數
import(/* webpackChunkName: "show" */ './show').then((show) => {
show('Webpack');
})
});
複製代碼
show.js中:
module.exports = function (content) {
window.alert('Hello ' + content);
};
複製代碼
代碼中最關鍵的一句是import(/* webpackChunkName: "show" */ './show')
,Webpack 內置了對import(*)
語句的支持,當 Webpack 遇到了相似的語句時會這樣處理:
./show.js
爲入口新生成一個Chunk
;import
所在語句時纔會去加載由Chunk
對應生成的文件。import
返回一個Promise
,當文件加載成功時能夠在Promise
的then
方法中獲取到show.js
導出的內容。在工做中具體使用到的時候再按須要進行更改配置項啦~