27.前端工程化背后的項(xiàng)目組織設(shè)計(jì)(上)

通過上一節(jié)的學(xué)習(xí)续挟,我們看到了前端構(gòu)建工具及其背后蘊(yùn)含的技術(shù)設(shè)計(jì)。前端工程化包羅萬象侥衬,本節(jié)課诗祸,我們將分析項(xiàng)目組織設(shè)計(jì)的相關(guān)話題,包括:


接下來轴总,我們通過 2 節(jié)內(nèi)容來學(xué)習(xí)這個(gè)主題直颅。希望結(jié)束這個(gè)主題后大家可以從更高的視角看待項(xiàng)目管理和代碼組織設(shè)計(jì)。

大型前端項(xiàng)目的組織設(shè)計(jì)

隨著業(yè)務(wù)復(fù)雜度的直線上升怀樟,前端項(xiàng)目不管是從代碼量上功偿,還是從依賴關(guān)系上都爆炸式增長。同時(shí)往堡,團(tuán)隊(duì)中一般不止有一個(gè)業(yè)務(wù)項(xiàng)目械荷,多個(gè)項(xiàng)目之間如何配合共耍,如何維護(hù)相互關(guān)系?公司自己的公共庫版本如何管理吨瞎?這些話題隨著業(yè)務(wù)擴(kuò)展痹兜,紛紛浮出水面。一名合格的高級前端工程師颤诀,在宏觀上必需能妥善處理這些問題字旭。

當(dāng)然,不是每個(gè)開發(fā)者都有機(jī)會接觸項(xiàng)目設(shè)計(jì)着绊。如果讀者沒有面對過上述難題谐算,也許并不容易理解這些問題究竟意味著什么。舉個(gè)例子归露,團(tuán)隊(duì)主業(yè)務(wù)項(xiàng)目名為:App-project,這個(gè)倉庫依賴了組件庫:Component-lib斤儿,因此 App-project 項(xiàng)目的 package.json 會有類似的代碼:

{
   "name": "App-project",
   "version": "1.0.0",
   "description": "This is our main app project",
   "main": "index.js",
   "scripts": {
       "test": "echo \\"Error: no test specified\\" && exit 1"
   },
   "dependencies": {
       "Component-lib": "^1.0.0"
   }
}

這時(shí)新的需求來了剧包,產(chǎn)品經(jīng)理需要更改 Component-lib 組件庫中的 modal 組件樣式及交互行為。作為開發(fā)者往果,我們需要切換到 Component-lib 項(xiàng)目疆液,進(jìn)行相關(guān)需求開發(fā),開發(fā)完畢后進(jìn)行測試陕贮。這里的測試包括 Component-lib 當(dāng)中的單元測試堕油,當(dāng)然也包括在實(shí)際項(xiàng)目中進(jìn)行效果驗(yàn)收。為方便調(diào)試肮之,有經(jīng)驗(yàn)的開發(fā)者也許會使用 npm link/yarn link 來開發(fā)和調(diào)試效果掉缺。當(dāng)確認(rèn)一切沒問題后,我們還需要 npm 發(fā)包 Component-lib 項(xiàng)目戈擒,并提升版本為 1.0.1眶明。在所有這些都順利完成的基礎(chǔ)上,才能在 App-project 項(xiàng)目中進(jìn)行升級:

{
   //...
   "dependencies": {
       "Component-lib": "^1.0.1"
   }
}

這個(gè)過程已經(jīng)比較復(fù)雜了筐高。如果中間環(huán)節(jié)出現(xiàn)任何紕漏搜囱,我們都要重復(fù)上述所有步驟。另外柑土,這只是單一依賴關(guān)系蜀肘,現(xiàn)實(shí)中 App-project 不可能只依賴 Component-lib。這種項(xiàng)目管理的方式無疑是低效且痛苦的稽屏。那么在項(xiàng)目設(shè)計(jì)哲學(xué)上扮宠,有更好的方式嗎?

monorepo 和 multirepo

答案是肯定的诫欠,管理組織代碼的方式主要分為兩種:

  • multirepo
  • monorepo

顧名思義涵卵,multirepo 就是將應(yīng)用按照模塊分別在不同的倉庫中進(jìn)行管理浴栽,即上述 App-project 和 Component-lib 項(xiàng)目的管理模式;而 monorepo 就是將應(yīng)用中所有的模塊一股腦全部放在同一個(gè)項(xiàng)目中轿偎,這樣自然就完全規(guī)避了前文描述的困擾典鸡,不需要單獨(dú)發(fā)包、測試坏晦,且所有代碼都在一個(gè)項(xiàng)目中管理萝玷,一同部署上線,在開發(fā)階段能夠更早地復(fù)現(xiàn) bug昆婿,暴露問題球碉。

這就是項(xiàng)目代碼在組織上的不同哲學(xué):一種倡導(dǎo)分而治之,一種倡導(dǎo)集中管理仓蛆。究竟是把雞蛋放在同一個(gè)籃子里睁冬,還是倡導(dǎo)多元化,這就要根據(jù)團(tuán)隊(duì)的風(fēng)格以及面臨的實(shí)際場景進(jìn)行選型看疙。

我試著從 multirepo 和 monorepo 兩種處理方式的弊端說起豆拨,希望給讀者更多的參考和建議。

multirepo 存在以下問題:

  • 開發(fā)調(diào)試以及版本更新效率低下
  • 團(tuán)隊(duì)技術(shù)選型分散能庆,不同的庫實(shí)現(xiàn)風(fēng)格可能存在較大差異(比如有的庫依賴 Vue施禾,有的依賴 React)
  • changelog 梳理困難,issues 管理混亂(對于開源庫來說)

而 monorepo 缺點(diǎn)也非常明顯:

  • 庫體積超大搁胆,目錄結(jié)構(gòu)復(fù)雜度上升
  • 需要使用維護(hù) monorepo 的工具弥搞,這就意味著學(xué)習(xí)成本比較高

清楚了不同項(xiàng)目組織管理的缺點(diǎn),我們再來看一下社區(qū)上的經(jīng)典選型案例渠旁。

Babel 和 React 都是典型的 monorepo攀例,其 issues 和 pull requests 都集中到唯一的項(xiàng)目中,changelog 可以簡單地從一份 commits 列表梳理出來一死。我們參看 React 項(xiàng)目倉庫肛度,從目錄結(jié)構(gòu)即可看出其強(qiáng)烈的 monorepo 風(fēng)格:

react-16.2.0/
 packages/
   react/
   react-art/
   react-.../

因此,reactreact-dom 在 npm 上是兩個(gè)不同的庫投慈,它們只不過在 React 項(xiàng)目中通過 monorepo 的方式進(jìn)行管理承耿。至于為什么 react 和 react-dom 是兩個(gè)包,我把這個(gè)問題留給讀者伪煤。

而著名的 Rollup 目前是 multirepo 方式加袋。對于 monorepo 和 multirepo,選擇了 monorepo 的 Babel 貢獻(xiàn)了文章:Why is Babel a monorepo?抱既, 其中提到:

monorepo 的優(yōu)勢:

  • 所有項(xiàng)目擁有一致的 lint职烧,以及構(gòu)建、測試、發(fā)布流程
  • 不同項(xiàng)目之間容易調(diào)試蚀之、協(xié)作
  • 方便處理 issues
  • 容易初始化開發(fā)環(huán)境
  • 易于發(fā)現(xiàn) bug

monorepo 的劣勢:

  • 源代碼不易理解
  • 項(xiàng)目體積過大

這些分析與我們前文提到的類似蝗敢。但是,從業(yè)內(nèi)技術(shù)發(fā)展來看足删,monorepo 目前越來越受歡迎寿谴。了解了 monorepo 的利弊,我們應(yīng)該如何實(shí)現(xiàn) monorepo 呢失受?

使用 Lerna 實(shí)現(xiàn) monorepo
Lerna 是 Babel 管理自身項(xiàng)目并開源的工具讶泰,官網(wǎng)對 Lerna 的定位非常簡單直接:

A tool for managing JavaScript projects with multiple packages.

我們來建立一個(gè)簡單的 demo,首先安裝依賴拂到,并創(chuàng)建項(xiàng)目:

mkdir new-monorepo && cd new-monorepo
npm init -y
npm i -g lerna(有需要的話要 sudo)
git init new-monorepo
lerna init

成功后痪署,Lerna 會在 new-monorepo 項(xiàng)目下自動添加以下三個(gè)文件目錄:

  • packages
  • lerna.json
  • package.json

我們添加第一個(gè)項(xiàng)目 module-1:

cd packages
mkdir module-1
cd module-1
npm init -y

這樣,我們在 ./packages 目錄下新建了第一個(gè)項(xiàng)目:module-1兄旬,并在 module-1 中添加了一些依賴狼犯,模擬更加真實(shí)的場景。同樣的方式辖试,建立 module-2 以及 module-3辜王。

此時(shí),讀者可以自行觀察 new-monorepo 項(xiàng)目下的目錄結(jié)構(gòu)為:

packages/
 module-1/
   package.json
 module-2/
   package.json
 module-3/
   package.json

接下來罐孝,我們退到主目錄下,安裝依賴:

cd ..
lerna bootstrap

關(guān)于該命令的作用肥缔,官網(wǎng)直述為:

Bootstrap the packages in the current Lerna repo. Installs all of their dependencies and links any cross-dependencies.

也就是說莲兢,假設(shè)我們在 module-1 項(xiàng)目中添加了依賴 module-2,那么執(zhí)行 lerna bootstrap 命令后续膳,會在 module-1 項(xiàng)目的 node_modules 下創(chuàng)建軟鏈接直接指向 module-2 目錄改艇。也就是說 lerna bootstrap 命令會建立整個(gè)項(xiàng)目內(nèi)子 repo 之間的依賴關(guān)系,這種建立方式不是通過「硬安裝」坟岔,而是通過軟鏈接指向相關(guān)依賴谒兄。

Linux 中關(guān)于硬鏈接和軟鏈接的區(qū)別,可以參考文章:linux 硬鏈接與軟鏈接社付。

在正確連接了 Git 遠(yuǎn)程倉庫后承疲,我們可以發(fā)布:

lerna publish

這條命令將各個(gè) package 一步步發(fā)布到 npm 當(dāng)中。Lerna 還可以支持自動生成 changelog 等功能鸥咖。這里我們不再統(tǒng)一介紹燕鸽。

到這里,你可能覺得 Lerna 還挺簡單啼辣。但其實(shí)里面還是有更多學(xué)問啊研,比如 Lerna 支持下面兩種模式。

  • Fixed/Locked 模式
    Babel 便采用了這樣的模式。這個(gè)模式的特點(diǎn)是党远,開發(fā)者執(zhí)行 lerna publish 后削解,Lerna 會在 lerna.json 中找到指定 version 版本號。如果這一次發(fā)布包含某個(gè)項(xiàng)目的更新沟娱,那么會自動更新 version 版本號氛驮。對于各個(gè)項(xiàng)目相關(guān)聯(lián)的場景,這樣的模式非常有利花沉,任何一個(gè)項(xiàng)目大版本升級柳爽,其他項(xiàng)目的大版本號也會更新。

  • Independent 模式
    不同于 Fixed/Locked 模式碱屁,Independent 模式下磷脯,各個(gè)項(xiàng)目相互獨(dú)立。開發(fā)者需要獨(dú)立管理多個(gè)包的版本更新娩脾。也就是說赵誓,我們可以具體到更新每個(gè)包的版本。每次發(fā)布柿赊,Lerna 會配合 Git俩功,檢查相關(guān)包文件的變動,只發(fā)布有改動的 package碰声。

開發(fā)者可以根據(jù)團(tuán)隊(duì)需求進(jìn)行模式選擇诡蜓。

我們也可以使用 Lerna 安裝依賴,該命令可以在項(xiàng)目下的任何文件夾中執(zhí)行:

lerna add dependencyName

Lerna 默認(rèn)支持 hoist 選項(xiàng)胰挑,即默認(rèn)在 lerna.json 中:

{ bootstrap: { hoist: true } }

這樣項(xiàng)目中所有的 package 下 package.json 都會出現(xiàn) dependencyName 包:

  packages/
 module-1/
   package.json(+ dependencyName)
   node_modules
 module-2/
   package.json(+ dependencyName)
   node_modules
 module-3/
   package.json(+ dependencyName)
   node_modules
 node_modules
      dependencyName

這種方式蔓罚,會在父文件夾的 node_modules 中高效安裝 dependencyName(Node.js 會向上在祖先文件夾中查找依賴)。對于未開啟 hoist 的情況瞻颂,執(zhí)行 lerna add 后豺谈,需要執(zhí)行:

lerna bootstrap --hoist

如果我們想有選擇地升級某個(gè)依賴,比如只想為 module-1 升級 dependencyName 版本贡这,可以使用 scope 參數(shù):

lerna add dependencyName --scope=module-1

這時(shí)候 module-1 文件夾下會有一個(gè) node_modules茬末,其中包含了 dependencyName 的最新版本。

分析一個(gè)項(xiàng)目遷移案例

接下來盖矫,我選取一個(gè)正在線上運(yùn)行的 multirepo 項(xiàng)目丽惭,并演示使用 Lerna 將其遷移到 monorepo 的過程。此案例來自 mitter.io炼彪,該團(tuán)隊(duì)以往一直以 multirepo 的形式維護(hù)以下幾個(gè)項(xiàng)目:

  • @mitter-io/core吐根,mitter.io SDK 核心基礎(chǔ)庫
  • @mitter-io/models,TypeScript models 庫
  • @mitter-io/web辐马,Web 端 SDK 應(yīng)用
  • @mitter-io/react-native拷橘,React Native 端 SDK 應(yīng)用
  • @mitter-io/node局义,Node.js 端 SDK 應(yīng)用
  • @mitter-io/react-scl,React.js 組件庫

背景介紹

項(xiàng)目使用 TypeScript 和 Rollup 工具冗疮,以及 TypeDoc 生成規(guī)范化文檔萄唇。在使用 Lerna 做 monorepo 化之前,這樣的技術(shù)方案帶來的困擾顯而易見术幔,我們來分析一下當(dāng)前技術(shù)棧的弊端另萤,以及 monorepo 化能為這些項(xiàng)目帶來哪些收益。

  • 如果 @mitter-io/core 中出現(xiàn)任何一處改動诅挑,其他所有的包都需要升級到 @mitter-io/core 最新版本四敞,不管這些改動是 feature 還是 bug fix,成本都比較大
  • 如果所有這些包能共同分享版本拔妥,那么帶來的收益也是非常巨大的
  • 這些不同的倉庫之間忿危,由于技術(shù)棧近似,一些構(gòu)建腳本大體相同没龙,部署流程也都一致铺厨,如果能夠?qū)⑦@些腳本統(tǒng)一抽象,也將帶來便利

遷移步驟

我們運(yùn)用 Lerna 構(gòu)建 monorepo 項(xiàng)目硬纤,第一步:

mkdir my-new-monorepo && cd my-new-monorepo
git init .
lerna init

不同于之前的示例解滓,這是從現(xiàn)有項(xiàng)目中導(dǎo)入,因此我們可以使用命令:

lerna import ~/projects/my-single-repo-package-1 --flatten

這行命令不僅可以導(dǎo)入項(xiàng)目筝家,同時(shí)也會將已有項(xiàng)目中的 git commit 一并搬遷過來洼裤。我們可以放心地在新 monorepo 倉庫中使用 git blame 來進(jìn)行回溯。

如此一來溪王,得到了這樣的項(xiàng)目結(jié)構(gòu):

packages/
 core/
 models/
 node/
 react-native/
 web/
lerna.json
package.json

接下來逸邦,運(yùn)行熟悉的:

lerna boostrap
lerna publish

進(jìn)行依賴維護(hù)和發(fā)布。注意并不是每次都需要執(zhí)行 lerna bootstrap在扰,只需要在第一次切換到項(xiàng)目,安裝所有依賴時(shí)運(yùn)行雷客。

對于每一個(gè) package 來說芒珠,其 pacakge.json 文件中都有以下雷同的 npm script 聲明。

"scripts": {
   ...
   "prepare": "yarn run build",
   "prepublishOnly": "./../../ci-scripts/publish-tsdocs.sh",
   ...
   "build": "tsc --module commonjs && rollup -c rollup.config.ts && typedoc --out docs --target es6 --theme minimal --mode file src"
}

受益于 monorepo搅裙,所有項(xiàng)目得以集中管理在一個(gè)倉庫中皱卓,這樣我們將所有 package 公共的 npm 腳本移到 ./scripts 文件中。在單一的 monorepo 項(xiàng)目里部逮,我們就可以在不同 package 之間共享構(gòu)建腳本了 娜汁。

運(yùn)行公共腳本時(shí),有時(shí)候有必要知道當(dāng)前運(yùn)行的項(xiàng)目信息兄朋。npm 是能夠讀取到每個(gè) package.json 信息的掐禁。因此,對每個(gè) package,在其 package.json 中添加以下信息:

{
   "name": "@mitter-io/core",
   "version": "0.6.28",
   "repository": {
       "type": "git"
   }
}

之后傅事,如下變量都可以被 npm script 使用:

npm_package_name = @mitter-io/core
npm_package_version = 0.6.28
npm_package_repository_type = git

流程優(yōu)化

團(tuán)隊(duì)中正常的開發(fā)流程是每個(gè)程序員新建一個(gè) git branch缕允,通過代碼審核之后進(jìn)行合并。整套流程在 monorepo 架構(gòu)下變得非常清晰蹭越,我們來梳理一下障本。

  • step1:當(dāng)開發(fā)完成后,我們計(jì)劃進(jìn)行版本升級响鹃,只需要運(yùn)行:lerna version
  • step2:Lerna 會提供交互式 prompt驾霜,對下一版本進(jìn)行序號升級
    lerna version --force-publish
   lerna notice cli v3.8.1
   lerna info current version 0.6.2
   lerna info Looking for changed packages since v0.6.2
   ? Select a new version (currently 0.6.2) (Use arrow keys)
   ? Patch (0.6.3)
   Minor (0.7.0)
   Major (1.0.0)
   Prepatch (0.6.3-alpha.0)
   Preminor (0.7.0-alpha.0)
   Premajor (1.0.0-alpha.0)
   Custom Prerelease
   Custom Version

新版本被選定之后,Lerna 會自動改變每個(gè) package 的版本號买置,在遠(yuǎn)程倉庫中創(chuàng)建一個(gè)新的 tag粪糙,并將所有的改動推送到 GitLab 實(shí)例當(dāng)中。

接下來堕义,CI 構(gòu)建實(shí)際上只需要兩步:

  • Build 構(gòu)建
  • Publish 發(fā)布

構(gòu)建實(shí)際就是運(yùn)行:

lerna bootstrap
lerna run build

而發(fā)布也不復(fù)雜猜旬,需要執(zhí)行:

git checkout master
lerna bootstrap
git reset --hard
lerna publish from-package --yes

注意,這里我們使用了 lerna publish from-package倦卖,而不是簡單的 lerna publish洒擦。因?yàn)殚_發(fā)者在本地已經(jīng)運(yùn)行了 lerna version,這時(shí)候再運(yùn)行 lerna publish 會收到「當(dāng)前版本已經(jīng)發(fā)布」的提示怕膛。而 from-package 參數(shù)會告訴 Lerna 發(fā)布所有非當(dāng)前 npm package 版本的項(xiàng)目熟嫩。

通過這個(gè)案例,我們了解了 Lerna 構(gòu)建 monorepo 的經(jīng)典套路褐捻,Lerna 還封裝了更多的 API 來支持更加靈活的 monorepo 的創(chuàng)建掸茅,感興趣的讀者可以自行研究,歡迎在評論區(qū)留言討論柠逞,或者直接向我提問昧狮。個(gè)人認(rèn)為序无,未來 monorepo 和 multirepo 將會持續(xù)并存酥馍,每個(gè)開發(fā)者都應(yīng)該根據(jù)項(xiàng)目特點(diǎn)來進(jìn)行選擇。

到此帘饶,我們分析了 multirepo 和 monorepo 方案的各自特點(diǎn)绰精,通過實(shí)例和項(xiàng)目遷移了解了如何構(gòu)建 monorepo 項(xiàng)目撒璧。但是,項(xiàng)目組織不光這些內(nèi)容笨使,下一節(jié)我們將討論依賴關(guān)系這一話題卿樱。

總結(jié)

monorepo 目前來看是一個(gè)流行趨勢,筆者為項(xiàng)目團(tuán)隊(duì)引入了 monorepo 的架構(gòu)方案之后收益非常明顯硫椰,我們也是國內(nèi)最早采用 monorepo 架構(gòu)的團(tuán)隊(duì)之一繁调。

但是這篇課程難以做到面面俱到萨蚕,并且任何一個(gè)項(xiàng)目都有自己的獨(dú)立性和特殊性,究竟該如何組織調(diào)配涉馁、生產(chǎn)部署门岔,需要每一個(gè)開發(fā)者開動腦筋。

比如:monorepo 方式會導(dǎo)致整個(gè)項(xiàng)目體積變大烤送,在上線部署時(shí)寒随,用時(shí)更長,甚至難以忍受帮坚。在工程中如何解決這類問題妻往?針對于此,我設(shè)計(jì)了增量部署構(gòu)建方案试和,通過分析項(xiàng)目依賴以及拓?fù)渑判蜓镀瑑?yōu)化項(xiàng)目編譯構(gòu)建,這里不再多做介紹阅悍。

如果對工程化話題格外感興趣的讀者較多好渠,我會專門進(jìn)行講解。希望大家一起討論节视。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末拳锚,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子寻行,更是在濱河造成了極大的恐慌霍掺,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,591評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件拌蜘,死亡現(xiàn)場離奇詭異杆烁,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)简卧,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評論 3 392
  • 文/潘曉璐 我一進(jìn)店門兔魂,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人举娩,你說我怎么就攤上這事入热。” “怎么了晓铆?”我有些...
    開封第一講書人閱讀 162,823評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長绰播。 經(jīng)常有香客問我骄噪,道長,這世上最難降的妖魔是什么蠢箩? 我笑而不...
    開封第一講書人閱讀 58,204評論 1 292
  • 正文 為了忘掉前任链蕊,我火速辦了婚禮事甜,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘滔韵。我一直安慰自己逻谦,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,228評論 6 388
  • 文/花漫 我一把揭開白布陪蜻。 她就那樣靜靜地躺著邦马,像睡著了一般。 火紅的嫁衣襯著肌膚如雪宴卖。 梳的紋絲不亂的頭發(fā)上滋将,一...
    開封第一講書人閱讀 51,190評論 1 299
  • 那天,我揣著相機(jī)與錄音症昏,去河邊找鬼随闽。 笑死,一個(gè)胖子當(dāng)著我的面吹牛肝谭,可吹牛的內(nèi)容都是我干的掘宪。 我是一名探鬼主播,決...
    沈念sama閱讀 40,078評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼攘烛,長吁一口氣:“原來是場噩夢啊……” “哼魏滚!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起医寿,我...
    開封第一講書人閱讀 38,923評論 0 274
  • 序言:老撾萬榮一對情侶失蹤栏赴,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后靖秩,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體须眷,經(jīng)...
    沈念sama閱讀 45,334評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,550評論 2 333
  • 正文 我和宋清朗相戀三年沟突,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了花颗。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,727評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡惠拭,死狀恐怖扩劝,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情职辅,我是刑警寧澤棒呛,帶...
    沈念sama閱讀 35,428評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站域携,受9級特大地震影響簇秒,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜秀鞭,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,022評論 3 326
  • 文/蒙蒙 一趋观、第九天 我趴在偏房一處隱蔽的房頂上張望扛禽。 院中可真熱鬧,春花似錦皱坛、人聲如沸编曼。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽掐场。三九已至,卻和暖如春抹沪,著一層夾襖步出監(jiān)牢的瞬間刻肄,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評論 1 269
  • 我被黑心中介騙來泰國打工融欧, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留敏弃,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,734評論 2 368
  • 正文 我出身青樓噪馏,卻偏偏與公主長得像麦到,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子欠肾,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,619評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 1.模塊化的相關(guān)規(guī)范 1.能夠了解2.了解webpack3.了解使用Vue單文件組件4.能夠搭建Vue腳手架5.掌...
    Scincyc閱讀 657評論 0 0
  • 1.模塊化的分類 A.瀏覽器端的模塊化 B.服務(wù)器端的模塊化 C.ES6模塊化 小結(jié):推薦使用ES6模塊化瓶颠,因?yàn)锳...
    __method__閱讀 782評論 0 1
  • 前言 在日常開發(fā)過程中,構(gòu)建組件庫是必不可少的一環(huán)刺桃,此篇文章就是描述如何搭建一個(gè)完整的組件庫粹淋,解決組件庫開發(fā)發(fā)布過...
    WEB前端含光閱讀 1,472評論 0 5
  • 本文是本人正式開始學(xué)習(xí)webpack的記錄文檔,時(shí)間:2020-05-16瑟慈,webpack版本:"webpack"...
    Mstian閱讀 599評論 0 5
  • 表情是什么桃移,我認(rèn)為表情就是表現(xiàn)出來的情緒。表情可以傳達(dá)很多信息葛碧。高興了當(dāng)然就笑了借杰,難過就哭了。兩者是相互影響密不可...
    Persistenc_6aea閱讀 124,953評論 2 7