前端自動(dòng)化
- 您需要跟蹤文件應(yīng)該加載的正確順序,包括哪些文件依賴于哪些其他文件,并確保不包含您不需要的任何文件。
- 過多的
<script>會(huì)增多網(wǎng)絡(luò)需求 - 這些都可以自動(dòng)化
為什么要使用webpack
- 新工具具有很多新特性,避免了一些前輩的問題
- 使用簡(jiǎn)單,如果僅用來打包js文件不使其他花哨的東西,甚至不用過多的配置
- 插件系統(tǒng)完善,你可以作為工具使用它
開始使用
npm init-
npm install webpack -D/ -D 意味這作為依賴安裝,相當(dāng)于 --save-dev -
npm install lodash -S/ -S 意味著--save - main.js
var map=require("lodash/map");
var square=function(n){
return n*n
}
console.log(map([1,2,3,4,5,6], square));
node main.js
如何使用webpack來打包呢?下面循序漸進(jìn)的為大家介紹
1. 使用webpack命令行
開始使用webpack而不浪費(fèi)時(shí)間在配置文件上的最簡(jiǎn)單的方法就是從命令行運(yùn)行它。 無需使用配置文件的webpack命令的最簡(jiǎn)單版本就是輸入文件路徑和輸出文件路徑。 Webpack將從該輸入文件中讀取,跟蹤其依賴關(guān)系樹,將所有文件合并到一個(gè)文件中,并將文件輸出到您指定為輸出路徑的位置。 對(duì)于這個(gè)例子,我們的輸入路徑是main.js,我們要將捆綁的文件輸出到bundle.js。 所以,讓我們創(chuàng)建一個(gè)npm腳本來做到這一點(diǎn)(我們沒有在全球安裝webpack,所以我們不能直接從命令行運(yùn)行它)。 在package.json中,編輯<script>部分,如下所示:
"scripts": {
"build": "webpack src/main.js dist/bundle.js",
}
在探索webpack之前,我們?cè)賮硪槐?,通過在重建之前刪除dist目錄及其內(nèi)容,并添加一些腳本來執(zhí)行我們的bundle,讓我們的構(gòu)建腳本變得更加專業(yè)。 我們需要做的第一件事是安裝del-cli,以便我們可以刪除目錄,而不會(huì)使不使用與我們相同的操作系統(tǒng)的人員(不要因?yàn)槭褂肳indows而討厭我); npm安裝del-cli -D應(yīng)該做的伎倆。 然后,我們將將npm腳本更新為以下內(nèi)容:
scripts": {
"prebuild": "del-cli dist -f",
"build": "webpack src/main.js dist/bundle.js",
"execute": "node dist/bundle.js",
"start": "npm run build -s && npm run execute -s"
}
我們保持build與以前一樣,但現(xiàn)在我們有prebuild來做一些清理,這將在build之前運(yùn)行,每次build被告知運(yùn)行。 我們還有execute,它使用Node.js來執(zhí)行捆綁的腳本,我們可以使用start使用一個(gè)命令來完成所有操作(-s位只是使npm腳本不輸出為 無用的東西到控制臺(tái))。 繼續(xù)運(yùn)行npm開始。 您應(yīng)該看到webpack的輸出,快速跟隨我們的平方陣列,顯示在您的控制臺(tái)。 恭喜! 剛剛完成了我之前提到的存儲(chǔ)庫(kù)的example1分支中的所有內(nèi)容。
2. 使用配置的webpack
與使用webpack命令行一樣有趣,一旦你開始使用更多的webpack的功能,你將要遠(yuǎn)離傳遞所有的選項(xiàng)通過命令行,而是使用一個(gè)配置文件 ,這將具有更多的能力,但由于它是用JavaScript編寫的,它們也將更加易讀。
所以,我們來創(chuàng)建一個(gè)配置文件。 在項(xiàng)目的根目錄中創(chuàng)建名為webpack.config.js的新文件。 這是默認(rèn)情況下webpack將要查找的文件名,但如果要將配置文件命名為其他目錄或?qū)⑵浞旁诓煌哪夸浿?,則可以將--config [filename]選項(xiàng)傳遞給webpack。
對(duì)于本教程,我們將使用標(biāo)準(zhǔn)文件名,現(xiàn)在我們將嘗試使其工作方式與使用命令行一樣。 為此,我們需要將以下代碼添加到配置文件中:
module.exports = {
entry: './src/main.js',
output: {
path: './dist',
filename: 'bundle.js'
}
};
3. 學(xué)習(xí)使用loader
使用webpack處理文件有兩種方式,loader或者插件,先討論loader,loader被用來轉(zhuǎn)換或者操作文件指定類型。我們可以對(duì)一個(gè)文件使用多個(gè)loader,例如我們可以先對(duì)一個(gè)文件進(jìn)行Eslint操作之后用Babel把他轉(zhuǎn)化為ES5,如果ESLint給出警告,竟會(huì)打印出警告信息,如果報(bào)錯(cuò),就不會(huì)繼續(xù)運(yùn)行后面的loader。
下面用ES2015語(yǔ)法來下寫main2.js
import { map } from 'lodash';
console.log(map([1,2,3,4,5,6], n => n*n));
上面代碼運(yùn)用ES2015寫法,用了import 代替 require (import {map} from loadsh 這樣寫會(huì)把整個(gè)loadsh引入,當(dāng)然也可以用 import map from 'lodash/map) ;用箭頭函數(shù)代替了function。這些都是ES2015語(yǔ)法
我們需要編譯使之在一些老舊瀏覽器中運(yùn)行良好,為此我們需要使用Babel和webpack來運(yùn)行編譯。
需要的loader:
- babel-core(Babel 核心功能,完成大部分工作)
- babel-loader(webpack提供的和babel-core的接口)
- babel-preset-es2015(告訴babeles2015編譯為ES5的規(guī)則)
- babel-plugin-transform-runtime , babel-polyfill(這兩者都改變??了Babel向您的代碼庫(kù)添加polyfills和helper函數(shù)的方式,盡管它們有所不同,因此它們適合于不同類型的項(xiàng)目。)
安裝這些包,然后更新webpack.config.js代碼
module.exports = {
entry: './src/main.js',
output: {
path: './dist',
filename: 'bundle.js'
},
module: {
rules: [
…
]
}
};
我們看到增加了module,module下兩個(gè)有一個(gè)rules屬性,這是一個(gè)數(shù)組,對(duì)于每組rule,我們?cè)O(shè)置兩個(gè)選項(xiàng)test和loader.
- test用來測(cè)試文件類型,通常是一個(gè)正則表達(dá)式,例如
/\.js$/就是以js結(jié)尾的文件名,/\.jsx?$/匹配以js或jsx結(jié)尾的文件. -
loader用來指定運(yùn)用哪些loader處理匹配到的文件。通過傳入帶有加載器名稱的字符串來指定,用!分隔。例如babel-loader!eslint-loader,webpack從右向做開始編譯,先經(jīng)過eslint再經(jīng)過babel-loader,如果loader有特殊選項(xiàng)你想指定,你可以使用babel-loader?fakeoption=true!eslint-loader這種形式,用option形式。
module:{
rules:[
{
test:/\.jsx?$/,
loader:'babel-core',
exclude: /node_modules/,
option:{
plugins: ['transform-runtime'],
presets: ['es2015']
}
}
]
}
我們需要設(shè)置預(yù)設(shè),使所有的ES2015功能都將轉(zhuǎn)換為ES5,我們還將設(shè)置使用我們安裝的轉(zhuǎn)換運(yùn)行時(shí)插件。如前所述,這個(gè)插件是沒有必要的,但它在那里告訴你如何做到這一點(diǎn)。另一種方法是使用.babelrc文件來設(shè)置這些選項(xiàng),但是我無法向您展示如何在webpack中執(zhí)行此操作。一般來說,我建議使用.babelrc,但我們將在此項(xiàng)目中保留配置。exclude文件排除了node_modules文件夾.
4. 使用 Handlebars loader
使用handlebars模板的loader,僅僅舉個(gè)例子
- 安裝handlebars以及他的loader
cnpm install handlebars handlebars-loader -D -
import template from 'handlebars-loader!./numberlist.hbs或者使用loader{ test: /\.hbs$/, loader: 'handlebars-loader' }. - 使用npm start就會(huì)打印出li標(biāo)簽
5. 學(xué)習(xí)使用插件
除了loader之外,插件是將自定義功能安裝到Webpack中。您可以自由地將它們添加到Webpack工作流程中,因?yàn)樗鼈儾粌H限于在加載特定文件類型時(shí)被使用;它們幾乎可以在任何地方注入,因此能夠做得更多.下面舉兩個(gè)關(guān)于插件的例子.
- HTML Webpack Plugin
- npm i http-server html-webpack-plugin -D
- package.json
"scripts": {
"prebuild": "del-cli dist -f",
"build": "webpack",
"server": "http-server ./dist",
"start": "npm run build -s && npm run server -s"
},
3. webpack.config.js
const path = require('path');
var HtmlWebpackPlugin=require("html-webpack-plugin")
module.exports = {
entry: [
'babel-polyfill',
'./src/main.js'
],
output: {
path: path.resolve(__dirname, './dist'),
filename: 'bundle.js'
},
module:{
rules:[
{
test:/\.jsx?$/,
loader:'babel-loader',
exclude: /node_modules/,
options:{
plugins: ['transform-runtime'],
presets: ['es2015']
}
},
{
test: /\.hbs$/,
loader: 'handlebars-loader'
}
]
},
plugins:[
new HtmlWebpackPlugin()
]
};
我們所做的兩個(gè)更改是將新安裝的插件導(dǎo)入文件的頂部,然后在配置對(duì)象的末尾添加一個(gè)plugins,我們?cè)谶@個(gè)插件中傳入新實(shí)例。
在這一點(diǎn)上,我們沒有將任何選項(xiàng)傳遞給插件,所以它使用的標(biāo)準(zhǔn)模板不包括很多,但它包含了我們的捆綁腳本。如果您運(yùn)行npm啟動(dòng),然后訪問瀏覽器中的URL,您將看到一個(gè)空白頁(yè)面,但是如果您打開開發(fā)人員的工具,則應(yīng)該會(huì)將HTML輸出到控制臺(tái)。
我們不應(yīng)讓其只從控制臺(tái)輸出,應(yīng)該讓他出現(xiàn)在HTML中,在src下新建index.html文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<h2>This is my Index.html Template</h2>
<div id="app-container"></div>
</body>
</html>
注意我們通過向ejs文件傳遞插件中參數(shù)
沒有指定應(yīng)該添加腳本的位置。這是因?yàn)槟J(rèn)情況下,該插件會(huì)將腳本添加到body標(biāo)簽的末尾。
webpack.config.js中增加下面選項(xiàng)
plugins: [
new HtmlwebpackPlugin({
title: 'Intro to webpack',
template: 'src/index.html'
})
]
我在嘗試按照上面例子寫demo的時(shí)候發(fā)現(xiàn)并不能將hbs模板插入到html中不知道為啥子.
我知道為啥子了,window.onload!!!
6. 使用懶加載
有一件事我非常喜歡與RequireJS,不能很好地使用Browserify(盡管可能)是懶惰加載模塊。一個(gè)大規(guī)模的JavaScript文件將有助于限制所需的HTTP請(qǐng)求數(shù)量,但實(shí)際上保證下載的代碼不一定在該會(huì)話中被訪問者使用。
Webpack有一種方法可以將捆綁分割成可以進(jìn)行延遲加載的大塊,甚至不需要任何配置。所有你需要做的是以兩種方式之一編寫代碼,webpack將處理其余的代碼。 Webpack為您提供了兩種方法:一種基于CommonJS,另一種基于AMD。要使用CommonJS來懶惰加載模塊,你可以這樣寫:
require.ensure(["module-a", "module-b"], function(require) {
var a = require("module-a");
var b = require("module-b");
// …
});
使用require.ensure,這將確保模塊可用(但不執(zhí)行),并傳遞一個(gè)模塊名稱數(shù)組,然后傳回一個(gè)回調(diào)。要在該回調(diào)中實(shí)際使用該模塊,您需要使用傳遞給回調(diào)的參數(shù)來明確地要求它。
AMD版本
require(["module-a", "module-b"], function(a, b) {
// …
});
import { map } from 'lodash';
let numbers = map([1,2,3,4,5,6], n => n*n);
setTimeout( () => {
require(['./numberlist.hbs'], template => {
document.getElementById("app-container").innerHTML = template({numbers});
})
}, 2000);
現(xiàn)在,如果您運(yùn)行npm start,您將看到生成另一個(gè)文件,它應(yīng)該命名為1.bundle.js。如果您在瀏覽器中打開頁(yè)面并打開您的開發(fā)工具來觀看網(wǎng)絡(luò)流量,您會(huì)看到延遲2秒后,新文件將被加載并執(zhí)行。這并不是很難實(shí)現(xiàn),可以使用戶的體驗(yàn)好多了。
請(qǐng)注意,這些子包或塊包含其所有依賴關(guān)系,除了每個(gè)子包中包含的子包。 (您可以有多個(gè)條目,每個(gè)條目都延遲加載此塊,因此,每個(gè)父項(xiàng)都加載不同的依賴項(xiàng)。)
7. Creating A Vendor Chunk
我們來談一談可以做的一個(gè)優(yōu)化:wendor。你可以定義要構(gòu)建的單獨(dú)的包,該包將存儲(chǔ)不太可能更改的“常用”或第三方代碼。這允許訪問者將庫(kù)從應(yīng)用程序代碼緩存在一個(gè)單獨(dú)的文件中,以便在更新應(yīng)用程序時(shí)不需要再次下載這些庫(kù)。
為此,我們將使用一個(gè)名為CommonsChunkPlugin的webpack附帶的插件。它被包含,我們不需要安裝任何東西;我們需要做的是對(duì)webpack.config.js進(jìn)行一些編輯
var HtmlwebpackPlugin = require('html-webpack-plugin');
var UglifyJsPlugin = require('webpack/lib/optimize/UglifyJsPlugin');
var CommonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin');
module.exports = {
entry: {
vendor: ['babel-polyfill', 'lodash'],
main: './src/main.js'
},
output: {
path: './dist',
filename: 'bundle.js'
},
module: {
rules: [
{
test: /\.jsx?$/, loader: 'babel-loader', exclude: /node_modules/,
options: { plugins: ['transform-runtime'], presets: ['es2015'] }
},
{ test: /\.hbs$/, loader: 'handlebars-loader' }
]
},
plugins: [
new HtmlwebpackPlugin({
title: 'Intro to webpack',
template: 'src/index.html'
}),
new UglifyJsPlugin({
beautify: false,
mangle: { screw_ie8 : true },
compress: { screw_ie8: true, warnings: false },
comments: false
}),
new CommonsChunkPlugin({
name: "vendor",
filename: "vendor.bundle.js"
})
]
};
第3行是我們導(dǎo)入插件的地方。然后,在入口部分,我們使用不同的設(shè)置(對(duì)象字面值)來指定多個(gè)入口點(diǎn)。vendor中包括polyfill以及Lodash,我們將主條目文件放入主條目。然后,我們只需要將CommonsChunkPlugin添加到插件部分,將vendor指定為基礎(chǔ)的塊,并指定vendor將存儲(chǔ)在名為vendor.bundle.js的文件中。
通過指定vendor,此插件會(huì)將其他條目文件中指定的所有依賴項(xiàng)提取到該供應(yīng)商塊中。如果這里沒有指定塊名,它將根據(jù)條目之間共享的依賴關(guān)系創(chuàng)建一個(gè)單獨(dú)的文件。
運(yùn)行webpack時(shí),現(xiàn)在應(yīng)該會(huì)看到三個(gè)JavaScript文件:bundle.js,1.bundle.js和vendor.bundle.js。您可以運(yùn)行npm開始,并在瀏覽器中查看結(jié)果(如果需要)??雌饋韜ebpack甚至?xí)汛蟛糠肿约旱拇a用于處理不同模塊的加載到vendor中.
我表示并沒有看到1.bundle.js這個(gè)文件
結(jié)語(yǔ)
當(dāng)然webpack不僅僅能實(shí)現(xiàn)上述功能,他還能輕松實(shí)現(xiàn)CSS模塊,高速緩存清除散列,圖像優(yōu)化,希望在以后的學(xué)習(xí)多多運(yùn)用。( Webpack enables easy CSS modules, cache-busting hashes, image optimization and much much more — so much that even if I wrote a massive book on the subject, I couldn’t show you everything, and by the time I finished writing that book, most (if not all) of it would be outdated! So, give webpack a try today, and let me know if it improves your workflow. God bless and happy coding!)