webpack 作為目前主流的構建工具,其較快的版本迭代和複雜的配置方式,使得每次開發前不得不規劃相當部分時間來除錯。這裡將記錄整個環境的搭建過程,為新手提供基礎思路。
就像我在開發vue-sitemap
時一樣,構建工具往往需要達到下面幾個需求:
- 構建生成 CommonJS/UMD/ES Modules 三種模式的程式碼提供給使用者
- 需執行測試和檢查測試覆蓋的進度
- 開發時候使用 VS Code 編輯器進行斷點除錯
以上三個作為開發一個元件(package
)是基礎中基礎的需求,當然還有更多細節內容需要新增,由於篇幅過長另加文章再說吧。(歡迎各位讀者評論留下你認為需要的功能( • ̀ω•́ )✧)
第一步:構建工具
接下來我們先從最基礎的開始,需要安裝 Node.js(10.x) 作為所有程式碼的執行環境, webpack 也是一樣。
初始化專案
由於我需要把專案釋出至 npm 的,使用命令初始化專案描述檔案 package.json
npm init
複製程式碼
初次化細節各位讀者找其他文章補全吧,這裡不細說
接下來看看目錄結構
│ package.json //專案描述檔案
│ README.md //GitHub建立倉庫時預設建立
├─src //原始碼目錄
│ index.js //入口檔案
├─tests //測試程式碼目錄
│
├─dist //生產檔案的目錄
│
└─docs //文件目錄
複製程式碼
新增 webpack
npm install -D webpack webpack-cli cross-env
//or
//yarn add webpack webpack-cli cross-env -D
複製程式碼
這裡使用的 webpack v4,後續設定也是基於4來設定,
cross-env
是幫助在 win 下能正常使用環境變數的包,我開發在 win 環境於是在這加上。
yarn 是一款快速、可靠、安全的依賴管理工具。如果你覺得 npm 安裝時候較慢的話,不妨試試。
等依賴下載解決完畢之後,,在package.json
設定構建命令方便之後使用。
//# package.json
{
//...
"scripts": {
"build": "cross-env NODE_ENV=production webpack --propress --hide-modules",
}
}
複製程式碼
這裡我們可以嘗試執行一下命令npm run build
嘗試能否構建成功,成功的情況下在dist
目錄下會生成main.js
的檔案。
配置 webpack
建立 webpack.config.js
檔案來配置 webpack 。為滿足我們的第一個需要生成三種模式的程式碼:
//# webpack.config.js
const package = require('./package.json')
const path = require('path')
const config = {
entry: "./src/index.js", //入口檔案
output: { //輸出設定
path: path.resolve(__dirname, "./dist"),
filename: `${package.name}.js`
},
resolve: {
alias: {
"@": path.resolve(__dirname, "./src")
}
}
}
if (process.env.NODE_ENV === "umd") {
config.optimization = { minimize: false };
config.output.library = package.name;
config.output.libraryTarget = "umd2";
config.output.filename = `${package.name}.js`;
}
if (process.env.NODE_ENV === "umd:min") {
config.output.library = package.name;
config.output.libraryTarget = 'umd2';
config.output.filename = `${package.name}.min.js`;
}
if (process.env.NODE_ENV === "es") {
config.output.library = package.name;
config.output.libraryTarget = "amd";
config.output.filename = `${package.name}.es.js`;
}
if (process.env.NODE_ENV === "commonjs") {
config.output.library = package.name;
config.output.libraryTarget = "commonjs2";
config.output.filename = `${package.name}.common.js`;
}
module.exports = config
複製程式碼
新增構建命令
為 package.json
新增新的執行命令
//# package.json
{
"version": "0.1.0",
"name": "vscode-mocha-webpack-example",
"description": "用於管理導航、麵包屑及路由等基於vue的功能整合",
"main": "./src/index.js",
"scripts": {
"build": "npm run build:commonjs && npm run build:es && npm run build:umd && npm run build:umd:min",
"build:umd": "cross-env NODE_ENV=umd webpack --mode=production --progress --hide-modules",
"build:umd:min": "cross-env NODE_ENV=umd:min webpack --mode=production --progress --hide-modules",
"build:es": "cross-env NODE_ENV=es webpack --mode=production --progress --hide-modules",
"build:commonjs": "cross-env NODE_ENV=commonjs webpack --mode=production --progress --hide-modules"
}
...
}
複製程式碼
執行npm run build
就會 CommonJS/UMD/ES Modules 三種模式生成對應的檔案。
大概是這樣子:
├─dist
│ vscode-mocha-webpack-example.common.js
│ vscode-mocha-webpack-example.es.js
│ vscode-mocha-webpack-example.min.js
│ vscode-mocha-webpack-example.js
複製程式碼
指定終端
為了使你的構建檔案成為最終釋出包的一部分,你必須宣告它們。將以下內容新增到package.json:
"main": "dist/vscode-mocha-webpack-example.common.js",
"module": "dist/vscode-mocha-webpack-example.es.js",
"jsnext:main": "dist/vscode-mocha-webpack-example.es.js",
"files": [
"dist",
"src"
],
複製程式碼
files
部分告訴npm在釋出時打包這些資料夾(否則,它們將被忽略,因為它們列在.gitignore
檔案中)main
定義CommonJS構建的終端jsnext:main
和module
定義了ES2015構建的終端(我們定義了兩個終端,因為jsnext:main
是最早使用的規範,而module
則更符合標準規範)。
第二步,設定babel
通過 babel
使得我們使用最新的語法,而不必擔心執行環境不支援的問題。在webpack
的下我們需要用到babel-loader
來匯入babel
支援,關於最新的相容設定還需使用上babel-preset-env
:
npm install -D babel babel-cli babel-preset-env
//or
//yarn add babel babel-cli babel-preset-env -D
複製程式碼
建立 babel 配置檔案
接著在.babelrc
檔案裡設定babel相容的規則:
{
"presets": [
[
"env",
{
"useBuiltIns": false,
"modules": false
}
]
]
}
複製程式碼
為 webpack 新增 babel-loader
當我們使用最新語法編寫 JavaScript 時,webpack 會匹配將所有 JS 檔案給 babel
的處理。
const package = require('./package.json')
const path = require('path')
const config = {
entry: "./src/index.js",
output: {
path: path.resolve(__dirname, "./dist"),
filename: `${package.name}.js`
},
resolve: {
alias: {
"@": path.resolve(__dirname, "./src")
}
},
module: {
rules: [
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/
}
]
}
}
...
module.exports = config
複製程式碼
當執行構建時webpack
便會載入babel
及其相關的設定將程式碼轉換並生成,到這步構建相關的設定基本完成。
第三步,新增自動化測試
相信對自動化測試有所瞭解的讀者應該對mocha
並不陌生,不瞭解的可以先去補補相關知識再往下看。簡單的測試較多使用mocha
來進行處理,還有斷言庫chai
和提供promise支援的chai-as-promised
等等,下面先把這些依賴安裝上:
npm install -D mocha mocha-webpack chai chai-as-promised
//or
//yarn add mocha mocha-webpack chai chai-as-promised -D
複製程式碼
測試程式碼想使用es新特性時可以使用
mocha-webpack
這個外掛。
然後在package.json
新增上測試命令:
{
//...
"scripts": {
"build": "npm run build:commonjs && npm run build:es && npm run build:umd && npm run build:umd:min",
"build:umd": "cross-env NODE_ENV=umd webpack --mode=production --progress --hide-modules",
"build:umd:min": "cross-env NODE_ENV=umd:min webpack --mode=production --progress --hide-modules",
"build:es": "cross-env NODE_ENV=es webpack --mode=production --progress --hide-modules",
"test": "cross-env NODE_ENV=test mocha-webpack tests/**/*.spec.js"
}
//...
}
複製程式碼
.babelrc
也需要設定一下:
{
//...
"env": {
"test": {
"presets": [
[
"env",
{
"modules": false,
"targets": {
"node": "current"
}
}
]
]
}
}
}
複製程式碼
為了能測試新增tests/unit/example.spec.js
和src/index.js
兩個檔案,程式碼如下:
├─src
│ index.js
└─tests
└─unit
example.spec.js
複製程式碼
//# src/index.js
export function getRole(user){
switch(user){
case "Packy":
return "admin"
case "Joan":
return "reader"
}
}
//# tests/unit/example.spec.js
import { assert } from "chai";
import { getRole } from "@/index";
describe('Testing', ()=>{
it('Packy is admin', () => { assert.equal(getRole('Packy'), 'admin') })
it("Joan is reader", () => { assert.equal(getRole("Joan"), "reader") });
})
複製程式碼
現在執行測試命令就能得出測試結果了:
npm run test
複製程式碼
大概輸出是這個樣子:
WEBPACK Compiling...
[======================= ] 91% (additional chunk assets processing)
WEBPACK Compiled successfully in 5893ms
MOCHA Testing...
Testing
√ Packy is admin
√ Joan is reader
2 passing (39ms)
MOCHA Tests completed successfully
複製程式碼
關於測試覆蓋率的問題
有了測試還得知道測試是否都覆蓋了所有程式碼(聽說基本要到80%,有些團隊可能要求更高90~95%),那如何得知?
nyc
這個包就能幫助到我去檢驗測試覆蓋率,首先先安裝依賴:
npm install -D nyc babel-plugin-istanbul
複製程式碼
再設定檢查範圍和新增命令:
//# package.json
{
...
"scripts": {
"build": "npm run build:commonjs && npm run build:es && npm run build:umd && npm run build:umd:min",
"build:umd": "cross-env NODE_ENV=umd webpack --mode=production --progress --hide-modules",
"build:umd:min": "cross-env NODE_ENV=umd:min webpack --mode=production --progress --hide-modules",
"build:es": "cross-env NODE_ENV=es webpack --mode=production --progress --hide-modules",
"build:commonjs": "cross-env NODE_ENV=commonjs webpack --mode=production --progress --hide-modules",
"test": "cross-env NODE_ENV=test nyc mocha-webpack tests/**/*.spec.js"
},
...
"nyc": {
"include": [
"src/**"
],
"instrument": false,
"sourceMap": false
}
...
}
複製程式碼
安裝依賴中也看到babel
也需要新增相關的設定:
//# .babelrc
{
...
"env": {
"test": {
"presets": [
[
"env",
{
"modules": false,
"targets": {
"node": "current"
}
}
]
],
"plugins": [
"istanbul"
]
}
}
}
複製程式碼
執行npm run test
將會得到以下內容:
WEBPACK Compiling...
[======================= ] 91% (additional chunk assets processing)
WEBPACK Compiled successfully in 5893ms
MOCHA Testing...
Testing
√ Packy is admin
√ Joan is reader
2 passing (39ms)
MOCHA Tests completed successfully
----------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
----------|----------|----------|----------|----------|-------------------|
All files | 100 | 100 | 100 | 100 | |
index.js | 100 | 100 | 100 | 100 | |
----------|----------|----------|----------|----------|-------------------|
複製程式碼
簡單說一下這四欄東西代表什麼意思:
- Stmts :
Statement coverage
宣告覆蓋率,程式中的每個語句都已執行嗎? - Branch:
Branch coverage
分支覆蓋率,是否已執行每個控制結構的每個分支(也稱為DD路徑)(例如if和case語句)?例如,給定if語句,是否已執行true和false分支? - Funcs:
Function coverage
方法覆蓋率,是否已呼叫程式中的每個函式(或子例程)? - Lines:
Line coverage
行程式碼覆蓋,是否已執行原始檔中的每個可執行的行?
不在覆蓋範圍內的程式碼的行數會在Uncovered Line
這欄顯示。
為測試提供async/await支援
在測試中想使用async/await語法,需新增setup.js檔案並在入口處新增babel-polyfill
:
require("babel-polyfill");
複製程式碼
並在.babelrc
修改useBuiltIns
為entry
:
{
...
"env": {
"test": {
"presets": [
[
"env",
{
"useBuiltIns": "entry",
"modules": false,
"targets": {
"node": "current"
}
}
]
],
"plugins": [
"istanbul"
]
}
}
}
複製程式碼
接下來在src/index.js
和tests/example.spec.js
兩個檔案新增新的程式碼:
//# src/index.js
export function getUsers(){
return new Promise((resolve, reject)=>{
setTimeout(()=>{
console.log('123')
resolve(['Packy', 'Joan'])
}, 1000)
})
}
//# tests/unit/example.spec.js
describe('GetUsers', ()=>{
it('get result is Array', async ()=>{
const users = await getUsers();
assert.isArray(users, "[message]");
})
})
複製程式碼
執行測試就能看到效果!
讓測試更進一步,在 VS Code 中除錯
想在VS Code斷點除錯程式碼需要額外增加一些設定,新增以下程式碼至 webpack.config.js
。
//# webpack.config.js
//...
if (process.env.NODE_ENV === "test") {
config.devtool = "eval-source-map";
config.output = Object.assign(config.output, {
devtoolModuleFilenameTemplate: "[absolute-resource-path]",
devtoolFallbackModuleFilenameTemplate: '[absolute-resource-path]?[hash]'
});
}
module.exports = config;
複製程式碼
在VS Code 新增除錯程式碼
開啟 VS Code 除錯皮膚在下拉選項中選擇新增配置(或者直接建立並開啟.vscode/launch.json
檔案):
// 使用 IntelliSense 瞭解相關屬性。
// 懸停以檢視現有屬性的描述。
// 欲瞭解更多資訊,請訪問: https://go.microsoft.com/fwlink/?linkid=830387
{
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Mocha-webpack Tests",
"program": "${workspaceFolder}/node_modules/mocha-webpack/bin/mocha-webpack",
"args": [
"--full-trace",
"--timeout",
"999999",
"--colors",
"tests/**/*.js"
],
"sourceMaps": true,
"env": {
"NODE_ENV": "test"
},
"internalConsoleOptions": "openOnSessionStart"
}]
}
複製程式碼
在src
目錄下的原始碼或是tests
目錄下的測試程式碼都能獲得斷點效果,想馬上嘗試可以下載本文例子vscode-mocha-webpack-example,安裝依賴後就能嘗試斷點除錯了。
值得一提的是,上面參考例子原文說devtool
使用eval
相關的設定並不能斷點,但是在使用mocha-webpack
除錯時上面例子並不能實現斷點。在我和公司小夥伴多番尋找vscode
和mocha-webpack
的issue後,經過各種嘗試下發現設定成eval-source-map
便能實現最佳斷點效果(eval也能實現但是由於斷點sourcemap指向的源是生成後的檔案導致在斷點時多少會產生偏移)。
吐槽:在使用
nvm
切換nodejs
環境時發現npm
下載不了,開啟github的下載連結直接404了,驚悚地發現npm
整個搬走 (`Д´*)9 ┴┴,為解決這個問題請下載最新版本v1.1.7
的nvm
。
最後:
我的動力來自你的指頭,請用你的指頭使勁給我個贊吧!d(´ω` )
覺得本文有幫助的話不要忘記點一下收藏φ(>ω<*) 哦!
同時歡迎各路新手、大神在本文下方吐槽留言,謝謝參與討論的各位仁兄!( • ̀ω•́ )✧
下面是本文完整例子,記得star一下!
同時非常感謝Mather協同編輯!