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,安裝依賴後就能嘗試斷點調試了。
設置參考源自vscode-ts-webpack-node-debug-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協同編輯!