溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

淺談Webpack自動(dòng)化構(gòu)建實(shí)踐指南

發(fā)布時(shí)間:2020-08-25 22:13:10 來源:腳本之家 閱讀:127 作者:熊建剛 欄目:web開發(fā)

由于現(xiàn)在的博客是使用wordpress搭建,自己得經(jīng)常修改過一些代碼,但是修改第三方源碼真的比較痛苦,于是決定計(jì)劃開始使用React + Node.js / Python開發(fā)新博客項(xiàng)目,最終替換當(dāng)前博客代碼,方便以后博客的維護(hù)和更新,也能實(shí)現(xiàn)自我開發(fā)技術(shù),架構(gòu)設(shè)計(jì),解決問題能力的提升,同時(shí)記錄下整個(gè)開發(fā)歷程,總結(jié),分享,希望能與讀者們一起進(jìn)步。本篇介紹如何使用Webpack和Babel,Eslint,documentation.js等搭建項(xiàng)目開發(fā)環(huán)境和生產(chǎn)環(huán)境,也算項(xiàng)目的準(zhǔn)備工作,下一期計(jì)劃介紹項(xiàng)目的架構(gòu)設(shè)計(jì)和技術(shù)棧選擇。

npm VS Yarn

在本項(xiàng)目我們使用Yarn管理項(xiàng)目三方依賴,不過放心,Yarn和NPM不沖突,也不是要替代NPM,使用方式基本一致,只需要簡(jiǎn)單了解以下幾點(diǎn)。

三方庫版本管理

npm 和 Yarn 都使用 package.json 來跟蹤項(xiàng)目的依賴,版本號(hào)并非一直準(zhǔn)確,因?yàn)槟憧梢远x版本號(hào)范圍,npm的不同更新范圍,可能導(dǎo)致在擁有相同 package.json 文件的機(jī)器上安裝不同版本包,這可能導(dǎo)致一些差異的異常和沖突。

那npm有解決方式嘛?npm中可以使用 npm shrinkwrap生成一個(gè)版本鎖文件npm-shrinkwrap.json,在 npm install 時(shí)會(huì)在讀取 package.json 前先讀取這個(gè)文件,但是當(dāng)更新包版本時(shí),版本鎖文件并不會(huì)自動(dòng)更新,我們得手動(dòng)再次執(zhí)行npm shrinkwrap命令更新它。

那么Yarn有什么優(yōu)勢(shì)呢?每次添加或更新安裝庫包時(shí),Yarn 都會(huì)創(chuàng)建(或更新)yarn.lock 文件,這樣可以確保所有機(jī)器安裝相同版本包,同時(shí)支持 package.json 中定義的允許版本范圍,和npm的區(qū)別在于Yarn總會(huì)自動(dòng)更新 yarn.lock,而npm需要手動(dòng)更新。

并發(fā)安裝

npm通常是按順序一個(gè)一個(gè)安裝依賴,而Yarn支持并行加載安裝多個(gè)三方庫包,所有其速度和效率都更快。

離線緩存

使用Yarn管理包時(shí),三方庫包存放在本地磁盤,下次安裝將直接使用本地文件而不是再次下載,這也從另一方面使其安裝速度優(yōu)于npm。

簡(jiǎn)而言之就是,Yarn和npm使用方式幾乎一樣,但是其版本管理更方便,安裝速度更快,更有優(yōu)勢(shì),但是實(shí)際上它的所有三方庫包加載地址和npm都是統(tǒng)一的。

Webpack

我們使用Webpack打包工具作為項(xiàng)目的自動(dòng)化構(gòu)建工具,將JavaScript,CSS,圖片等資源都當(dāng)作JavaScript模塊(使用Webpack loader處理轉(zhuǎn)換)進(jìn)行統(tǒng)一管理,關(guān)于Webpack博主之前總結(jié)過兩篇文章,可以參考:

  1. Webpack搭建SPA應(yīng)用開發(fā)環(huán)境
  2. Webpack模塊化管理CSS和圖片等資源

有了前文的鋪墊,本文就不打算展開介紹Webpack的工作原理和具體配置,而計(jì)劃從項(xiàng)目實(shí)踐開發(fā)和測(cè)試,打包層面思考如何更好的組織Webpack,如何使用Webpack提告項(xiàng)目開發(fā),打包效率。

Webpack配置文件

首先我們?cè)诟夸浵聞?chuàng)建webpack.config.js配置文件:

module.exports = function () {
 let env
 let _DEV_ = true // 開發(fā)環(huán)境
 let _PROD_ = false // 生產(chǎn)環(huán)境

 switch (process.env.NODE_ENV) {
 case 'dev':
 env = 'dev'
 _DEV_ = true
 _PROD_ = false
 break
 case 'production':
 env = 'prod'
 _DEV_ = false
 _PROD_ = true
 break
 default:
 env = 'dev'
 _DEV_ = true
 _PROD_ = false
 }
 // 根據(jù)環(huán)境參數(shù)動(dòng)態(tài)決定引入對(duì)應(yīng)配置文件
 return require(`./webpack/${env}.conf.js`)({
 ROOTPATH: __dirname,
 _DEV_,
 _PROD_
 })
}

根據(jù)process.env.NODE_ENV環(huán)境參數(shù)動(dòng)態(tài)決定加載對(duì)應(yīng)配置文件:

  1. dev:加載webpack/env.conf.js配置文件;
  2. prod:加載webpack/prod.conf.js配置文件;

我們?cè)陧?xiàng)目根目錄下創(chuàng)建了webpack目錄,其內(nèi)創(chuàng)建了三個(gè)配置文件:

  1. base.conf.js:基礎(chǔ)配置文件,在開發(fā),生產(chǎn)環(huán)境都需要的配置;
  2. dev.conf.js:開發(fā)環(huán)境配置文件;
  3. prod.conf.js:生產(chǎn)環(huán)境打包配置文件;

開發(fā)環(huán)境配置

開發(fā)環(huán)境配置文件中定義了一些開發(fā)使用的構(gòu)建配置,然后引入基礎(chǔ)配置文件,使用webpack-merge三方庫,將開發(fā)環(huán)境配置合并至基礎(chǔ)配置對(duì)象,然后返回開發(fā)環(huán)境打包構(gòu)建配置對(duì)象,作為Webpack打包構(gòu)建的參數(shù):

const webpackMerge = require('webpack-merge')
const PUBLICPATH = '/assets/'
const PORT = '9090'
let options = { /* ... */ }
module.exports = function (args) {
 options.ROOTPATH = args.ROOTPATH
 options.env = args.env
 return webpackMerge(require('./base.conf')(options), {
 devtool: 'source-map',
 devServer: {
 contentBase: path.join(args.ROOTPATH, './src'),
 historyApiFallback: true,
 inline: true,
 hot: true,
 port: PORT,
 proxy: {
 '*': `http://localhost:${PORT}/${PUBLICPATH}/`
 }
 },
 plugins: []
 })
}

生產(chǎn)環(huán)境配置

生產(chǎn)環(huán)境配置文件中定義了的是生產(chǎn)環(huán)境使用的構(gòu)建配置,然后也是引入基礎(chǔ)配置文件,使用webpack-merge三方庫,將生產(chǎn)環(huán)境配置合并至基礎(chǔ)配置,然后返回配置對(duì)象,作為Webpack打包構(gòu)建的參數(shù):

let options = { /* ... */}
module.exports = function (args) {
 options.ROOTPATH = args.ROOTPATH
 options.env = args.env
 return webpackMerge(require('./base.conf')(options), {
 plugins: [
 new webpack.DefinePlugin({
 'process.env': 'production'
 }),
 // 生成獨(dú)立css文件
 new ExtractTextPlugin({
 filename: 'css/[name].css'
 }),
 new webpack.optimize.UglifyJsPlugin({
 compress: {
 warnings: false
 }
 })
 ]
 })
}

指令

然后就是為不同環(huán)境配置可執(zhí)行指令,我們使用npm scripts方式,在package.json文件中配置執(zhí)行指令:

{
 "scripts": {
 "start": "cross-env NODE_ENV=dev webpack-dev-server",
 "build": "cross-env NODE_ENV=production webpack"
 }
}

start:開發(fā)環(huán)境運(yùn)行指令,使用cross-env三方庫設(shè)置process.env.NODE_ENV為dev,并在本地開啟webpack開放服務(wù)器,方便開放;

build:生產(chǎn)環(huán)境運(yùn)行指令,使用cross-env三方庫設(shè)置process.env.NODE_ENV為production,將打包輸出代碼和資源文件;

最后分別執(zhí)行yarn start和yarn build指令即可分別執(zhí)行開發(fā)和生產(chǎn)構(gòu)建打包了。

Babel

可自定義配置型的通用編譯器,需要明確指定期望babel做什么,通過安裝插件(plugins)或預(yù)設(shè)(presets,也就是一組插件)來指示 Babel 去做什么事情。

配置文件

首先需要?jiǎng)?chuàng)建一個(gè)配置文件,即在項(xiàng)目的根路徑下創(chuàng)建 .babelrc 文件。然后輸入以下內(nèi)容作為開始:

{
 "presets": [],
 "plugins": []
}

之后就可以拓展這個(gè)配置文件以指定此項(xiàng)目中 Babel 的功能。

babel-preset-es2015

我們期望在項(xiàng)目中能使用更新潮的ES6版本語法,但由于目前還有許多JavaScript環(huán)境不能很好兼容ES6,所以需要Babel將ES6代碼編譯成ES5語法代碼,保證應(yīng)用的使用范圍。

執(zhí)行如下命令,安裝 “es2015” Babel 預(yù)設(shè):

yarn add --dev babel-preset-es2015

修改.babelrc配置文件:

{
 "presets": [
 "es2015"
 ],
 "plugins": []
}

babel-preset-stage-num

另外,JavaScript還有一些提案,正在推進(jìn),不久的將來也可能成為標(biāo)準(zhǔn)的一部分,所以目前將這些草案提出,內(nèi)容更新直至最終成為標(biāo)準(zhǔn),添加進(jìn)標(biāo)準(zhǔn)庫的過程劃分為 5(0-4)個(gè)階段。 根據(jù)提案的狀態(tài)和內(nèi)容,將其在各個(gè)階段更新(階段0至階段3),最終在階段 4表明該提案被標(biāo)準(zhǔn)正式采納,當(dāng)然不被采納的提案不會(huì)進(jìn)入階段4。

以下是4個(gè)不同階段的打包預(yù)設(shè):

  1. babel-preset-stage-0
  2. babel-preset-stage-1
  3. babel-preset-stage-2
  4. babel-preset-stage-3

注: stage-4 預(yù)設(shè)不存在,它其實(shí)就是上文介紹的 es2015 預(yù)設(shè)。

以上每種預(yù)設(shè)都包含緊隨的后期階段預(yù)設(shè),同時(shí)還可能包含其他額外特性。例如,babel-preset-stage-0 包含 babel-preset-stage-1, babel-preset-stage-2,babel-preset-stage-3,而 babel-preset-stage-1則包含 babel-preset-stage-2,babel-preset-stage-3依次后推。

點(diǎn)此查看關(guān)于各階段預(yù)設(shè)的詳細(xì)特性內(nèi)容文檔

我們次選擇支持特性最全面的預(yù)設(shè):

yarn add --dev babel-preset-stage-0

在.babelrc 配置文件內(nèi)添加:

{
 "presets": [
 "es2015",
 "stage-0"
 ],
 "plugins": []
}

babel-preset-react

我們的項(xiàng)目期望使用React開發(fā),所以需要拓展支持React/JSX語法,安裝預(yù)設(shè):

yarn add --dev babel-preset-react

.babelrc 配置文件內(nèi)添加:

{
 "presets": [
 "es2015",
 "stage-0",
 "react"
 ],
 "plugins": []
}

babel-polyfill

至此,使用Babel,我們的·項(xiàng)目幾乎可以支持所有的ES6及ES7語法,但是對(duì)于新增的JavaScript API是無能為力的,如Symbol這種新API,并不是通過語法轉(zhuǎn)換能實(shí)現(xiàn)的,所以我們需要另外的方式解決。

業(yè)內(nèi)提出了Polyfill(填充),以添加額外代碼的方式使得當(dāng)前運(yùn)行環(huán)境支持不存在的原生Api ,拓展了尚處在推進(jìn)階段的API的使用范圍。

yarn add babel-polyfill

此處不需要添加--dev參數(shù)。

然后在文件入口引入即可:

import "babel-polyfill";

babel-runtime

前面提到的Babel通過轉(zhuǎn)換語法以支持我們以ES6等更新的語法方式開發(fā)代碼,這時(shí)Babel會(huì)在每一個(gè)處理的文件頭部注入輔助代碼,會(huì)產(chǎn)生很多冗余,重復(fù)性的內(nèi)容,導(dǎo)致代碼量暴增,所以我們需要將這些輔助代碼抽取至一個(gè)統(tǒng)一環(huán)境,Babel提供的就是運(yùn)行時(shí)(runtime)環(huán)境。

要實(shí)現(xiàn)Babel運(yùn)行時(shí)環(huán)境,需要安裝 babel-plugin-transform-runtime 和 babel-runtime

yarn add --dev babel-plugin-transform-runtime babel-runtime

然后更新 .babelrc:

{
 "plugins": [
 "transform-runtime",
 ]
}

按需加載(babel-plugin-import)

很多時(shí)候,我們開發(fā)業(yè)務(wù)并不需要自制UI,會(huì)選擇一些開源組件庫以快速開發(fā)實(shí)現(xiàn)產(chǎn)品,如antd,weui,material-ui等,我們可以選擇直接提前加載三方庫所有模塊,但是很多時(shí)候我們希望能實(shí)現(xiàn)按需加載,減少初始代碼包的體積,這時(shí),我們可以在babel配置文件中聲明按需加載該第三方庫,當(dāng)然首先得安裝插件babel-plugin-import

yarn add --dev babel-plugin-import

然后在配置文件.babelrc中添加配置:

{
 "plugins": [
 "import",
 {
 "style": "../styles", // 加載樣式解析方式,(值為true時(shí),可能是less/Sass),此處值設(shè)為相對(duì)libraryName具體模塊請(qǐng)求路徑值
 "libraryDirectory": "", // 加載包的目錄,(默認(rèn)是lib,即node_modules/lib/)
 "libraryName": "material-ui" // 加載三方組件庫名,當(dāng)然另外需要安裝該三方庫
 }
 ]
}

此時(shí),webapck loader處理css時(shí)不能添加exclude: /node_modules/。

其他插件

我們還可以根據(jù)項(xiàng)目實(shí)際需求和愛好自定義安裝插件,更多信息查看官方插件文檔。

在這里推薦一款babel-pliugin-transform-s2015-classes插件拓展以實(shí)現(xiàn)JavaScript內(nèi)置class對(duì)象的extends繼承特性,參考文檔ES2015 classes transform。

yarn add --dev babel-plugin-transform-es2015-classes

在.babelrc文件內(nèi)添加plugins內(nèi)容:

{
 "plugins": [
 "transform-runtime",
 "transform-es2015-classes",
 [
 "import",
 {
 "style": "css",
 "libraryDirectory": "",
 "libraryName": "material-ui"
 }
 ]
 ]
}

語法檢測(cè)(Eslint)

為了保證代碼質(zhì)量,統(tǒng)一代碼風(fēng)格是很重要的,而只靠團(tuán)隊(duì)口頭約定明顯是不能盡如人意,所以通常需要在自動(dòng)化構(gòu)建層面進(jìn)行代碼語法檢測(cè),有很多語法檢測(cè)工具如jslint,eslint,目前使用率最高的要數(shù)eslint了,所以我們的項(xiàng)目也引入eslint,首先安裝依賴:

yarn add --dev eslint

更多細(xì)節(jié)參考配置文檔,下文簡(jiǎn)要介紹主要。

配置文件

然后在項(xiàng)目根目錄下建立eslint配置文件.eslintrc,內(nèi)容是一個(gè)對(duì)象:

{}

解析器(parser)

另外,ESLint 默認(rèn)使用Espree作為其解析器,你可以在配置文件中指定一個(gè)不同的解析器,如babel-eslint,esprima等,我們項(xiàng)目使用babel-eslint:

yarn add --dev babel-eslint

在配置文件內(nèi)添加parser屬性:

{
 "parser": "babel-eslint"
}

eslint-plugin-babel

eslint還支持可選安裝插件,拓展eslint,如eslint-plugin-babel,該插件與babel-eslint協(xié)作,使得eslint可以更好的與babel同時(shí)工作,更多請(qǐng)查看參考文檔。

yarn add --dev eslint-plugin-babel

在配置文件添加聲明:

{
 "plugins": [
 "babel"
 ],
}

aslant-plugin-react

eslint默認(rèn)是檢測(cè)JavaScript語言語法的,而對(duì)于React/JSX這類包含其自定義語法和語法糖的框架而言,需要另外拓展安裝插件才能和eslint結(jié)合使用,所以使用eslint檢測(cè)React特定語法需要安裝eslint-plugin-react插件:

yarn add --dev eslint-plugin-react

添加配置文件:

{
 "plugins": [
 "babel",
 "react"
 ]
}

拓展(extends)

除了自定義語法檢查規(guī)則外,我們可以使用Eslint提供的集成拓展包,使用共享的語法檢測(cè)配置對(duì)象,如eslint-config-standard和eslint-config-standard-react:

復(fù)制代碼 代碼如下:

yarn add --dev eslint-config-standard eslint-config-standard-react eslint-plugin-standard eslint-plugin-promise eslint-plugin-import eslint-plugin-node eslint-plugin-react

注:這里包含了上一小節(jié)提到的eslint-plugin-react是為了支持eslint-config-standard-react配置包。

然后在.eslintrc配置文件中添加拓展:

{
 "extends": [
 "standard",
 "standard-react"
 ]
}

若不想使用這類集成語法檢測(cè)規(guī)則,可以移除配置文件中內(nèi)容并移除依賴:

復(fù)制代碼 代碼如下:

yarn remove eslint-config-standard eslint-config-standard-react eslint-plugin-standard eslint-plugin-promise eslint-plugin-import eslint-plugin-node eslint-plugin-react

語法規(guī)則(rules)

要添加語法規(guī)則,只需要聲明在rules屬性對(duì)象中,如:

{
 "rules": {
 "strict": 0,
 "semi": 2, // 強(qiáng)制語句末尾添加符號(hào),否則報(bào)錯(cuò)
 "quotes": [
 1,
 "single"
 ],
 }
}

規(guī)則結(jié)構(gòu)

當(dāng)聲明語法檢測(cè)規(guī)則時(shí),需要設(shè)置規(guī)則 ID為以下值之一:

  1. "off" 或 0 – 關(guān)閉規(guī)則
  2. "warn" 或 1 – 開啟規(guī)則,使用警告級(jí)別的錯(cuò)誤:warn (不會(huì)導(dǎo)致程序退出)
  3. "error" 或 2 – 開啟規(guī)則,使用錯(cuò)誤級(jí)別的錯(cuò)誤:error (當(dāng)被觸發(fā)的時(shí)候,程序會(huì)退出)
{
 "rules": {
 eqeqeq: 0, // or "off"
 curly: 2 // or "error"
 }
}

某些規(guī)則還可能有額外的配置選項(xiàng),可以使用數(shù)組指定,如:

{
 "rules": {
 "eqeqeq": "off",
 "curly": "error",
 "quotes": ["warn", "single"] // 開啟使用單引號(hào),若使用雙引號(hào)將發(fā)出警告
 }
}

指令

要執(zhí)行語法檢測(cè),只需要執(zhí)行./node_modules/.bin/eslint src(項(xiàng)目本地安裝eslint,而非全局安裝,則需要指定執(zhí)令腳本路徑),將會(huì)遍歷檢查src目錄下的所有源碼文件語法并輸出結(jié)果,當(dāng)然我們最終需要將指令根據(jù)npm scripts規(guī)范插入package.json文件:

{
 "scripts": {
 "lint": "eslint --cache --fix src"
 }
}

使用npm scripts執(zhí)行指令時(shí),無論項(xiàng)目本地安裝還是全局安裝,都可以省略指令腳本路徑,因?yàn)閚pm將自動(dòng)匹配可用路徑。

文檔

一個(gè)優(yōu)秀的項(xiàng)目當(dāng)然少不了文檔,文檔可以幫助其他開發(fā)者快速了解整個(gè)項(xiàng)目?jī)?nèi)容及進(jìn)度,也有助于bug修復(fù)時(shí)查找內(nèi)容,追蹤溯源,所以文檔是有必要的,于是通過調(diào)研發(fā)現(xiàn)了JSdoc和documentation.js幫助自動(dòng)化產(chǎn)出API文檔。

documentation

和JSdoc一樣,documentation也是根據(jù)代碼注釋自動(dòng)構(gòu)建出項(xiàng)目文檔,前提是我們的代碼注釋必須按照其規(guī)范指南,詳情參考JSdoc文檔。

我們首先安裝documentation.js:

yarn add --dev documentation

指令

然后可以執(zhí)行指令:

./node_modules/.bin/documentation build src/app.js -f md > API.md

會(huì)發(fā)現(xiàn)在根目錄輸出了API.md文件。

我們?cè)趐ackage.json文件中配置npm scripts執(zhí)行腳本:

"scripts": {
 "doc": "./node_modules/.bin/documentation build src/app.js -f md > API.md"
}

項(xiàng)目本地安裝documentation時(shí),直接在命令行終端執(zhí)行指令時(shí)需要指定./node_modules/.bin/documentation路徑,若全局安裝則只可直接使用documentation指令。而執(zhí)行package.json中的腳步,可以直接簡(jiǎn)寫,npm將為我們自動(dòng)匹配。

點(diǎn)此查看項(xiàng)目源碼地址

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持億速云。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI