webpack 基礎(chǔ)
Wepack 工程打包機(jī)
Webpack 基本概念
Webpack Demos
Node.js Debugger
webpack學(xué)習(xí)實(shí)踐系列
Webpack Dev Server
Webpack loader 十問
Webpack 總得來說是一個(gè)資源模塊化 JS Module 打包工具拷泽,它的核心思想是模塊化思想,不管你是圖片嬉愧,JS挣柬,CSS,SCSS耻台,LESS凶杖,還是 JSX使碾,統(tǒng)統(tǒng)都打包成 JS Module种远,Anythin to JS Modules涩澡。各種資源都有相適配的加載器 Loaders 負(fù)責(zé)對(duì)資源進(jìn)行處理,Webpack 通過執(zhí)行加載器完成原始資源的轉(zhuǎn)換坠敷。
Hello Webpack
TypeScript
TypeScript Samples
TypeScript Node Starte
TS-Node
Webpack with TypeScript
Awesome TypeScript Loader
TypeScript loader for webpack
Babel loader for webpack
Base64 URL Loader
Webpack 資源管理
TypeScript聲明文件 .d.ts
Build Performance
這里同過 TypeScript + Webpack 編寫一個(gè) Demo 工程來入門 Webpack 工程化打包機(jī)的理念妙同,工程化是必然的軟件技術(shù)發(fā)展方向射富。TypeScript 作為一個(gè)強(qiáng)大的靜態(tài)類型檢查語(yǔ)言是作為工程化開發(fā)的理想工具,所以 Node.js + TypeScript + Webpack + 框架的工程結(jié)構(gòu)將會(huì)是非常流行的技術(shù)棧渐溶。TypeScript 現(xiàn)在發(fā)布 v3.5.3 版本辉浦,官方提供的示例 [TypeScript Node Starte] 是非常值得學(xué)習(xí)的案例。
Node.js 命令行的 TypeScript 編譯器可以使用 npm 來安裝茎辐,安裝后會(huì)有一個(gè) tsc 命令來轉(zhuǎn)譯 TypeScript 代碼為 JavaScript,也可以安裝 ts-node 來直接解析運(yùn)行掂恕。
npm install --save-dev typescript@3.5.3 ts-node@8.3.0
tsc helloworld.ts
ts-node helloworld.ts
使用 VSCode拖陆、Sublime Text、Vim 作為開發(fā)工具都是很好的選擇懊亡。
通過 Node.js 環(huán)境使用 Webpack依啰,先通過 npm 來進(jìn)行全局安裝,創(chuàng)建示例工程 webpack-demo
目錄店枣,npm init
項(xiàng)目初始化命令生成默認(rèn)的 package.json
配置文件速警,可以傳入默許參數(shù) --yes
忽略設(shè)置內(nèi)容,關(guān)于它得用法可以查詢 npm help init
鸯两。一并安裝 webpack-dev-server
這個(gè)開發(fā)用的 Web 服務(wù)器闷旧,還有 TypeScript 支持。
Webpack 處理 TypeScript (.ts
) 原代碼文件需要用到 ts-loader
或 awesome-typescript-loader
钧唐,前者使用得更多忙灼。在代碼中導(dǎo)入文件資源時(shí)需要 file-loader
,導(dǎo)入圖片時(shí)需要 url-loader
钝侠,它可以對(duì)常用得圖片進(jìn)行 Base64編碼该园,可以設(shè)置 limit
選項(xiàng)來限定待編碼文件大小。在處理 CSS 樣式文件時(shí)會(huì)用到 css-loader
帅韧,打包后的樣式在頁(yè)面上還原出來時(shí)需要style-loader
提供的功能里初,它會(huì)在 <head>
節(jié)點(diǎn)下插入一個(gè) <style>
節(jié)點(diǎn),通過寫入樣式規(guī)則來還原經(jīng)過打包的樣式忽舟。
由于 TypeScript 靜態(tài)類型的特殊性双妨,那些非代碼資源需要為 TypeScript 提供一個(gè) Type Declaration 類型聲明文件 .d.ts
,在工程原代碼目錄下保存即可萧诫,TypeScript 會(huì)自動(dòng)解析斥难。如編寫一個(gè) .svg
資源的D類型聲明文件:
// custom.d.ts
declare module "*.svg" {
const content: any;
export default content;
}
這個(gè) SVG 類型聲明模塊指明,任何以 .svg
結(jié)尾的文件導(dǎo)入時(shí)將擁有一個(gè) any 任意類型屬性的 content帘饶,即數(shù)據(jù)部分是任意類型哑诊。還可以顯式定義 url
屬性為 string,即文件的地址及刻。 這個(gè)De類型聲明規(guī)則同樣適用于 CSS, SCSS, JSON 等等镀裤,這是 TypeScript 靜態(tài)類型系統(tǒng)特有的做法竞阐。
TypeScript 相比 JavaScript 增加了類型聲明。這些類型聲明幫助編譯器識(shí)別類型暑劝,從而防止開發(fā)者搬起石頭砸自己的腳骆莹。
原則上,TypeScript 需要開發(fā)者做到先聲明后使用担猛。這就導(dǎo)致開發(fā)者在調(diào)用很多原生接口(瀏覽器幕垦、Node.js)或者第三方模塊的時(shí)候,因?yàn)槟承┤肿兞炕蛘邔?duì)象的方法并沒有聲明過傅联,導(dǎo)致編譯器的類型檢查失敗先改。
用 ts 寫的模塊在發(fā)布的時(shí)候仍然是用 js 發(fā)布,這就導(dǎo)致一個(gè)問題:ts 那么多類型數(shù)據(jù)都沒了蒸走,所以需要一個(gè) d.ts 文件來標(biāo)記某個(gè) js 庫(kù)里面對(duì)象的類型
然后 typings 就是一個(gè)網(wǎng)絡(luò)上的 .d.ts
數(shù)據(jù)庫(kù)仇奶。
Lodash 是一個(gè)一致性、模塊化比驻、高性能的 JavaScript 實(shí)用工具庫(kù)该溯。它通過降低 array、number别惦、objects狈茉、string 等等的使用難度從而讓 JavaScript 變得更簡(jiǎn)單的工具類。Lodash 的模塊化方法 非常適用于做 array步咪、object 和 string 的遍歷操作论皆,對(duì)值進(jìn)行操作和檢測(cè),創(chuàng)建符合功能的函數(shù)等猾漫。
source-map-loader
是一個(gè) SourceMap 代碼地圖工具点晴,可以生成代碼地圖方便做調(diào)式。配置時(shí)可以在配置 devtool
設(shè)置內(nèi)聯(lián) inline-source-map
或 source-map
方式悯周,前者會(huì)將調(diào)式代碼嵌入打包輸出粒督,后者則獨(dú)立保存為 .map
代碼地圖文件。
mkdir webpack-demo
cd webpack-demo
npm init --yes
npm install --save-dev webpack@4.1.1 webpack-cli@3.3.6 webpack-dev-server@3.7.2
npm install --save-dev file-loader@4.0.0 url-loader@2.0.1 ts-loader@6.0.4 css-loader@3.0.0 style-loader@0.23.1
npm install --save-dev awesome-typescript-loader@5.2.1
npm install --save-dev lodash@4.17.14 source-map-loader@0.2.4
配置文件參考禽翼,main
這里設(shè)置的式項(xiàng)目入口程序屠橄,如果已經(jīng)準(zhǔn)備好配置文件,直接執(zhí)行 npm install
就可以根據(jù)配置好的依賴模塊列表進(jìn)行下載安裝:
{
"name": "webpack",
"version": "1.0.0",
"description": "Webpack Getting Started",
"main": "index.ts",
"devDependencies": {
"clean-webpack-plugin": "^3.0.0",
"css-loader": "^3.0.0",
"file-loader": "^4.0.0",
"html-webpack-plugin": "^3.2.0",
"html-webpack-template": "^6.2.0",
"lodash": "^4.17.14",
"source-map-loader": "^0.2.4",
"style-loader": "^0.23.1",
"ts-loader": "^6.0.4",
"ts-node": "^8.3.0",
"typescript": "^3.5.3",
"url-loader": "^2.0.1",
"webpack": "^4.1.1",
"webpack-cli": "^3.3.6",
"webpack-dev-server": "^3.7.2"
},
"dependencies": {},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "webpack-dev-server --devtool eval --progress --colors",
"build": "set NODE_ENV=production&& webpack -p"
},
"author": "Jeango",
"license": "ISC"
}
需要注意的式闰挡,加載的模塊愈多锐墙,就越消耗硬件資源,因此沒有必要使用的組件就不要安裝了长酗,直接從配置文件中移除然后從新執(zhí)行 npm install
安裝即可更新配置溪北。
例如給給規(guī)則指定 include
目錄節(jié)省搜索時(shí)間,簡(jiǎn)化 resolve.modules
, resolve.extensions
, resolve.mainFiles
, resolve.descriptionFiles
。使用 DllPlugin
插件將那些不怎么修改的內(nèi)容分離到另一個(gè)編譯單元之拨,避免在開發(fā)過程使用 source-map
或 minimize
茉继,特別是代碼地圖 Source maps 它非常耗資源,請(qǐng)考慮是否真的需要蚀乔。
可以使用并行編譯烁竭,parallel-webpack
和 cache-loader
提供并行編譯的能力,后者對(duì)性能開銷較大的 loader 提供緩存服務(wù)吉挣。具體要點(diǎn)參考官方的構(gòu)建優(yōu)化文檔[Build Performance]派撕。
rules: [
{
test: /\.js$/,
include: path.resolve(__dirname, 'src'),
loader: 'babel-loader'
}
]
webpack 命令的基本用法
webpack – building for development
webpack -p – building for production (minification)
webpack --watch – for continuous incremental building
webpack -d – including source maps
webpack --colors – making building output pretty
為了方便使用打包命令,可以配置到 package.json
中的 scripts
听想,例如要運(yùn)行開發(fā)服務(wù)器 webpack-dev-server
只需要執(zhí)行 npm run dev
腥刹,要生成發(fā)布打包就執(zhí)行 npm run build
,另外 VSCode 對(duì)這里設(shè)置的命令配置項(xiàng)支持很到位汉买,直接通過 VSCode 的 Terminal 菜單 Run Task
就可以執(zhí)行。Sublime
{
"scripts": {
"dev": "webpack-dev-server --devtool eval --progress --colors",
"build": "NODE_ENV=production webpack -p"
},
}
接下來需要一個(gè) TypeScript 配置文件 tsconfig.json
佩脊,因?yàn)?TypeScript 需要使用到 Node.js 的類型信息蛙粘,模塊解析moduleResolution
設(shè)置成 node
模式。其它配置項(xiàng)信息可以參考官方文檔 [TypeScript]威彰。
{
"compilerOptions": {
"outDir": "./dist/", // path to output directory
"sourceMap": true, // allow sourcemap support
"strictNullChecks": true, // enable strict null checks as a best practice
"module": "es6", // specifiy module code generation
"target": "es5", // specify ECMAScript target version
"allowJs": true, // allow a partial TypeScript and JavaScript codebase
"moduleResolution": "node",
"noImplicitAny": true, // disallow implicit any type
"noImplicitReturns": true,
"strict": true,
},
"include": ["./src/"]
}
安裝各種 Loaders 后出牧,需要根據(jù) Loader 開發(fā)文檔參考配置,Webpack 得配置文件是 webpack.config.js
官方文檔有很詳細(xì)得說明歇盼。對(duì)于 Loader舔痕,主要是配置 rules
規(guī)則,test
是文件名匹配正則規(guī)則豹缀,符合匹配條件得文件就交給指定的 loader
進(jìn)行處理伯复,各個(gè) Loader 的配置選項(xiàng)參考文檔設(shè)置。
其中 entry
和 output
是比較重要的配置邢笙,entry 表示程序入口啸如,output 表示打包出口,即打包生成的文件氮惯。publicPath
是訪問資源時(shí)使用的參考路徑叮雳,打包后的資源存放路徑與它直接關(guān)聯(lián)。在使用開發(fā)服務(wù)器時(shí)妇汗,它就是項(xiàng)目的根目錄下的路徑帘不,使用相對(duì)目錄時(shí)要參考根目錄來設(shè)置。output
還可以設(shè)置 path
屬性來指定打包文件存放位置杨箭,默認(rèn)是 dist
目錄寞焙。結(jié)合 filename
指定輸出文件就是 /dist/bin/bundle.js
。如果對(duì)資源文件的發(fā)布目錄有自定義需求,可以通過 process.env.NODE_ENV
變量判端是否是發(fā)布編譯棺弊,然后再指定一個(gè) publicPath
目錄晶密。
在 Webpack 4 中,不再?gòu)?qiáng)制要求指定 entry 和 output 路徑模她。webpack 4 會(huì)默認(rèn) entry 為 ./src
稻艰,output 為 ./dist
。
mode
模式設(shè)置侈净,基本上有 development, production, none 幾種模式尊勿。根據(jù)不同的模式使用不同的配置文件來優(yōu)化開發(fā)/發(fā)布。
resolve
設(shè)定要解析的文件類型畜侦,設(shè)置錯(cuò)誤解析不到的文件類型會(huì)產(chǎn)生 Module not found: Error: Can't resolve...
module.exports = {
// change to .tsx if necessary
entry: './src/index.ts',
mode: 'development',
output: {
publicPath: "/",
filename: './bin/bundle.js'
},
resolve: {
extensions: [".ts", ".tsx", ".js", ".jsx"]
},
module: {
rules: [{
test: /\.(t|j)sx?$/,
use: {
loader: 'ts-loader'
}
}, {
test: /\.(png|jpg|gif)$/i,
use: [{
loader: 'url-loader',
options: {
limit: 8192,
},
}, ],
}, {
test: /\.css$/i,
use: [{
loader: "style-loader"
}, {
loader: 'css-loader',
options: {
modules: true,
}
}]
}]
// },
// devtool: 'inline-source-map',
// devtool: "source-map",
// optimization: {
// minimize: true
// },
// externals: {
// "react": "React",
// "react-dom": "ReactDOM",
}
}
if (process.env.NODE_ENV === "production") {
module.exports.output.publicPath = "./release";
}
如果工程有多個(gè)主程序入口文件元扔,那么可以將 entry
和 output
修改成分組打包方式。Webpack 的輸出參數(shù) output
指定規(guī)則生成輸出文件旋膳。所有的入口產(chǎn)生的輸出文件都必須使用這一套規(guī)則澎语,不能針對(duì)某一個(gè)特定的入口來制定 output
規(guī)則。輸出項(xiàng)中用 [name]
來引用 entry
每一項(xiàng)中的鍵值验懊,用以批量指定生成后文件的名稱擅羞。[hash]
引用本次編譯的一個(gè)hash版本號(hào),[chunkhash]
引用的是當(dāng)前chunk的一個(gè)hash版本义图。也就是說减俏,在同一次編譯中,每一個(gè)chunk的hash都是不一樣的碱工;而在兩次編譯中娃承,如果某個(gè)chunk根本沒有發(fā)生變化,那么該chunk的hash也就不會(huì)發(fā)生變化怕篷。
html-webpack-plugin
和 html-webpack-template
是兩個(gè)生成 HTML 模板的插件历筝,為了對(duì)發(fā)布目錄 dist
自動(dòng)清理,可以使用 clean-webpack-plugin
插件匙头。這幾個(gè)插件都是 Webpack 提供學(xué)習(xí)如何在 Node.js 平臺(tái)下做 Webpack 插件開發(fā)用的漫谷,也具有一定的實(shí)用。
Node.js 提供的內(nèi)置模塊 path
可以用來解析絕對(duì)路徑蹂析。
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
entry: {
app: './src/index.js',
print: './src/print.js'
},
// entry: {
// home: ['./home.js', './home.scss'],
// account: ['./account.js', './account.scss']
// },
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist')
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
title: 'Output Management'
})
]
};
先準(zhǔn)備一個(gè)頁(yè)面模板 index.html
用它來加載 Webpack 打包生成的輸出文件 bundle.js
舔示,為了簡(jiǎn)化這里就不引用第三方 JavaScript 庫(kù):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>TypeScript with Webpack</title>
<style>
.frame {
width:50%;
padding:10%;
color:white;
background: #282828;
}
</style>
</head>
<body>
<div id="content" class="frame"></div>
<script src="./bin/bundle.js"></script>
</body>
</html>
新建 src
目錄用來放源代碼 index.ts
:
class Student {
fullName: string = "";
readonly age:number = 18;
constructor( public firstName:string, public middle:string, public lastName:string){
this.fullName = firstName + ' ' + middle + ' ' + lastName;
}
}
interface Person {
firstName: string;
lastName: string;
}
function greeter(person: Person) {
return "Hello, " + person.firstName;
}
let user = { firstName: "Jane", lastName: "User" };
// let user = new Student("Jane", "M.", "User");
let $ = (id:string, msg:string) => {
let tag = document.getElementById(id);
if (!tag){
document.body.innerHTML = ("HTML element not found #"+id);
}else{
tag.innerHTML = msg;
}
}
$("content", greeter(user));
其它不需要打包的資源文件單獨(dú)放放到 public
子目錄下,這樣的工程目錄結(jié)構(gòu)是比較通用合理的电抚。