Vue組件庫(kù)工程探索與實(shí)踐——構(gòu)建工具篇

我們團(tuán)隊(duì)近期發(fā)布了移動(dòng)端 Vue 組件庫(kù) NutUI 的 2.0 版[1]租副,2.0 不是 1.0 的升級(jí)舟茶,而是一個(gè)全新的組件庫(kù)闸餐。從 1.0 到 2.0 一路走來(lái),我們積累了一些 Vue 組件庫(kù)的開(kāi)發(fā)經(jīng)驗(yàn)个盆,接下來(lái)的一段時(shí)間脖岛,我們將以系列文章的形式與大家進(jìn)行分享,歡迎大家關(guān)注砾省。

作為《Vue組件庫(kù)工程探索與實(shí)踐》系列文章開(kāi)篇之作杆煞,我們從“盤(pán)古開(kāi)天地”說(shuō)起吧们拙。

從當(dāng)年的靜態(tài)頁(yè)面到如今的 Web App宝鼓,前端工程越來(lái)越復(fù)雜和媳,對(duì)于一個(gè)稍大些的前端項(xiàng)目來(lái)說(shuō),代碼都寫(xiě)在一起難以維護(hù)狠鸳,團(tuán)隊(duì)分工協(xié)作也成問(wèn)題揣苏。根據(jù)軟件工程領(lǐng)域的經(jīng)驗(yàn),解決這些問(wèn)題的一個(gè)可行思路就是代碼的模塊化件舵,即對(duì)代碼按功能模塊進(jìn)行分拆卸察,封裝成組件,而反過(guò)來(lái)講铅祸,組件就是指能完成某個(gè)特定功能的獨(dú)立的坑质、可重用的代碼塊合武。

把一個(gè)大的應(yīng)用分解成若干小的組件,而每個(gè)組件只需要關(guān)注于某個(gè)小范圍的特定功能涡扼,但是把組件組合起來(lái)稼跳,就能構(gòu)成一個(gè)功能龐大的應(yīng)用。組件化的網(wǎng)頁(yè)開(kāi)發(fā)也是如此吃沪,就像搭積木汤善,各個(gè)組件拼接在一起就組成了一個(gè)完整的頁(yè)面。

組件化開(kāi)發(fā)可大大降低代碼耦合度票彪、提高代碼的可維護(hù)性和開(kāi)發(fā)效率红淡,同時(shí)有利于團(tuán)隊(duì)分工協(xié)作和降低開(kāi)發(fā)成本。這種開(kāi)發(fā)模式已日漸流行起來(lái)降铸。

當(dāng)前在旱,前端開(kāi)發(fā)領(lǐng)域最流行的三大框架 Vue、React垮耳、Angular 都推崇組件化開(kāi)發(fā)颈渊,組件是這些框架中極為重要的概念和功能。

以 Vue.js 來(lái)說(shuō)终佛,組件 (Component) 可以說(shuō)是其最強(qiáng)大的功能,它可以擴(kuò)展 HTML 元素雾家,封裝可重用的代碼铃彰。Vue.js 的組件系統(tǒng)讓我們可以用這些獨(dú)立可復(fù)用的小組件來(lái)構(gòu)建大型 Vue 應(yīng)用,幾乎任意類(lèi)型的 Vue 應(yīng)用的界面都可以抽象為一個(gè)組件樹(shù)芯咧。

如果我們把日常應(yīng)用開(kāi)發(fā)中常用的組件累積起來(lái)牙捉,后續(xù)的項(xiàng)目就可以復(fù)用這些組件,這對(duì)提高開(kāi)發(fā)效率敬飒、降低開(kāi)發(fā)成本有重要意義邪铲。

因此,一個(gè)前端團(tuán)隊(duì)擁有一個(gè)常用框架的組件庫(kù)是十分必要的无拗。

模塊化與構(gòu)建工具

組件庫(kù)自身就是一個(gè)大的工程带到,需要按照模塊化開(kāi)發(fā)思想進(jìn)行模塊劃分。通常英染,在一個(gè)組件庫(kù)里揽惹,組件、組件的樣式文件四康、配置文件…都是模塊搪搏,而最終我們需要把這些模塊組合成一個(gè)完整的組件庫(kù)文件,承擔(dān)這種組裝工作的就是打包構(gòu)建工具闪金。當(dāng)下主流的庫(kù)構(gòu)建工具主要有 Rollup 和 Webpack 等疯溺。在說(shuō)這些模塊打包構(gòu)建工具之前,我們先來(lái)了解一下目前主流的 JavaScript 模塊化方案。

JavaScript 語(yǔ)言一直以來(lái)飽受詬病的一個(gè)地方就是它的語(yǔ)言標(biāo)準(zhǔn)里沒(méi)有模塊(module)體系囱嫩,這對(duì)開(kāi)發(fā)大型的恃疯、復(fù)雜的項(xiàng)目形成了巨大障礙。直到 ES6 時(shí)期挠说,才在語(yǔ)言標(biāo)準(zhǔn)層面實(shí)現(xiàn)模塊功能(ES6 Module)澡谭。在 ES6 之前,業(yè)界流行的是社區(qū)制定的一些模塊加載方案损俭,如 CommonJS 和 AMD 蛙奖。而 ES6 Module 作為官方規(guī)范,且瀏覽器端和服務(wù)器端通用杆兵,未來(lái)一定會(huì)一統(tǒng)天下雁仲,但由于 ES6 Module 來(lái)的太晚,受限于兼容性等因素琐脏,可以預(yù)見(jiàn)的是今后一段時(shí)期內(nèi)攒砖,多種模塊化方案仍會(huì)共存。

  • ES6 Modue 規(guī)范:JavaScript 語(yǔ)言標(biāo)準(zhǔn)模塊化方案日裙,瀏覽器和服務(wù)器通用吹艇,模塊功能主要由 export 和 import 兩個(gè)命令構(gòu)成。export 用于定義模塊的對(duì)外接口昂拂,import 用于輸入其他模塊提供的功能受神。
  • CommonJS 規(guī)范:主要用于服務(wù)端的 JavaScript 模塊化方案,Node.js 采用的就是這種方案格侯,所以各種 Node.js 環(huán)境的前端構(gòu)建工具都支持該規(guī)范鼻听。CommonJS 規(guī)范規(guī)定通過(guò) require 命令加載其他模塊,通過(guò) module.exports 或者 exports 對(duì)外暴露接口联四。
  • AMD 規(guī)范:全稱(chēng)是 Asynchronous Modules Definition撑碴,異步模塊定義規(guī)范,一種更主要用于瀏覽器端的 JavaScript 模塊化方案朝墩,該方案的代表實(shí)現(xiàn)者是 RequireJS醉拓,通過(guò) define 方法定義模塊,通過(guò) require 方法加載模塊鱼辙。

一些“上年紀(jì)”的國(guó)內(nèi)前端老藝人們可能還會(huì)提到 CMD 規(guī)范廉嚼,它是 SeaJS 在推廣過(guò)程中對(duì)模塊定義的規(guī)范化產(chǎn)出,只是 SeaJS 并未實(shí)現(xiàn)國(guó)際化倒戏,且項(xiàng)目在2015年就已宣布停止維護(hù)了怠噪,算不上當(dāng)前主流模塊化方案。

介紹完主流模塊化規(guī)范杜跷,我們?cè)倩剡^(guò)頭來(lái)看 Rollup 和 Webpack 這兩個(gè)模塊打包構(gòu)建工具傍念。

rollup

Rollup 是一個(gè)頗有名氣的庫(kù)打包工具矫夷,很多知名的庫(kù)、框架都是使用它打的包憋槐,包括 Vue 和 React 自身双藕。Rollup 可以直接對(duì) ES6 模塊進(jìn)行打包,它率先提出并實(shí)現(xiàn)了 Tree-shaking 功能阳仔,即在打包時(shí)靜態(tài)分析 ES6 模塊代碼中的 import忧陪,排除未實(shí)際使用的代碼,這有助于減小構(gòu)建包的體積近范。

rollup

另一個(gè)打包工具 Webpack 名氣更大嘶摊,不過(guò)我們通常用它來(lái)打包應(yīng)用,而事實(shí)上它對(duì)庫(kù)打包也能提供很好的支持评矩。Webpack 支持代碼分割叶堆、模塊的熱更新(HMR)等功能,這讓它看起來(lái)非常適合打包應(yīng)用斥杜。而 Webpack 2 及后續(xù)版本陸續(xù)增加了對(duì) ES6 模塊虱颗、Tree-shaking、Scope Hoisting 的支持蔗喂,大大增強(qiáng)了其庫(kù)打包能力忘渔。

如今,Rollup 在庫(kù)打包方面的優(yōu)勢(shì)已不再那么明顯缰儿,而在對(duì)應(yīng)用打包的支持方面卻明顯落后于 Webpack 辨萍。所以打包應(yīng)用推薦使用 Webpack ,而打包庫(kù)的話(huà)返弹, Rollup 和 Webpack 基本都能勝任。

那么我們?cè)陂_(kāi)發(fā) NutUI 2 的時(shí)候?yàn)槭裁催x擇了 Webpack 而不是 Rollup 呢爪飘?其實(shí)主要還是上述這個(gè)原因义起,按照規(guī)劃,NutUI 的官網(wǎng)(包含示例和文檔)與庫(kù)在同一個(gè)項(xiàng)目中师崎,因此我們需要一個(gè)既能打包庫(kù)默终,又能打包應(yīng)用的工具,Webpack 顯然更適合犁罩。

Webpack打包Library

使用 Webpack 來(lái)打包應(yīng)用齐蔽,相信大多前端小伙伴都不會(huì)感到陌生〈补溃可如何使用 Webpack 來(lái)打包一個(gè)組件庫(kù)呢含滴?各位細(xì)聽(tīng)我來(lái)言。

首先丐巫,雖然基于 ES6 模塊規(guī)范開(kāi)發(fā)谈况,但考慮到瀏覽器兼容性勺美,我們需要打包出來(lái)的組件庫(kù)能兼容 AMD 等瀏覽器端模塊規(guī)范。同時(shí)碑韵,為了使組件庫(kù)能支持服務(wù)端渲染(SSR)等場(chǎng)景赡茸,它還需要支持 commonJS 規(guī)范。此外祝闻,還有一種常見(jiàn)的庫(kù)使用場(chǎng)景占卧,即在頁(yè)面上直接通過(guò) script 標(biāo)簽引入,也就是非模塊化環(huán)境同樣需要兼容联喘。

Webpack 中华蜒,output.libraryTarget 選項(xiàng)用來(lái)配置如何暴露庫(kù),可配置以 commonJS 模塊耸袜、AMD 模塊友多,甚至全局變量形式暴露庫(kù)〉炭颍可是如何讓這個(gè)庫(kù)可以同時(shí)兼容 commonJS域滥、AMD 和全局變量呢?

所幸蜈抓,這個(gè)選項(xiàng)還支持一個(gè)可選值—— umd启绰。UMD(Universal Module Definition,通用模塊規(guī)范)可以同時(shí)支持 CommonJS 和 AMD 規(guī)范沟使,以及非模塊化引用委可。

綜上,我們需要把 output.libraryTarget 的值設(shè)為“umd”腊嗡。

另外兩個(gè)與庫(kù)打包關(guān)系密切的Webpack配置項(xiàng)如下:

  • output.library 着倾,對(duì)外暴露的變量名或模塊名,具體作用與 output.libraryTarget 選項(xiàng)的值有關(guān)燕少。
  • output.umdNamedDefine 卡者,當(dāng) output.libraryTarget 的值為“umd”時(shí),設(shè)置該選項(xiàng)的值為 true 會(huì)對(duì) UMD 的構(gòu)建過(guò)程中的 AMD 模塊進(jìn)行命名客们,否則就使用匿名的 define崇决,匿名的 AMD 模塊。

這幾個(gè)選項(xiàng)配置完底挫,就可以打包出一個(gè)基于 umd 規(guī)范庫(kù)了恒傻。

output: {
        path: path.resolve(__dirname, '../dist/'),
        filename: 'nutui.js',
        library: 'nutui',
        libraryTarget: 'umd',
        umdNamedDefine: true
}

但是我們會(huì)發(fā)現(xiàn)構(gòu)建出來(lái)的庫(kù)在 Node.js 環(huán)境使用時(shí)會(huì)報(bào)錯(cuò):

window is not defined

是不是感到莫名其妙?說(shuō)好的 UMD 兼容 commonJS 呢建邓?查看 Webpack 構(gòu)建出的包代碼盈厘,我們會(huì)發(fā)現(xiàn),UMD 部分的代碼里的全局對(duì)象竟然是 window 涝缝!非瀏覽器環(huán)境哪有 window 對(duì)象扑庞,Node.js 中不報(bào)錯(cuò)才怪譬重。

(function webpackUniversalModuleDefinition(root, factory) {
 if(typeof exports === 'object' && typeof module === 'object')
 module.exports = factory(require("vue"));
 else if(typeof define === 'function' && define.amd)
 define("nutui", ["vue"], factory);
 else if(typeof exports === 'object')
 exports["nutui"] = factory(require("vue"));
 else
    root["nutui"] = factory(root["Vue"]);
})(window, function(__WEBPACK_EXTERNAL_MODULE__2__) {

查閱 Webpack 文檔,可以發(fā)現(xiàn) output 對(duì)象還有一個(gè)屬性叫 globalObject 罐氨,用來(lái)指定掛載這個(gè)庫(kù)的全局對(duì)象臀规,默認(rèn)值是 window 。而這部分文檔明確指出栅隐,當(dāng)構(gòu)建 UMD 包需要兼容瀏覽器和 Node.js 環(huán)境時(shí)塔嬉,值應(yīng)該設(shè)為 this 。

output: {
        path: path.resolve(__dirname, '../dist/'),
        filename: 'nutui.js',
        library: 'nutui',
        libraryTarget: 'umd',
        umdNamedDefine: true,
        globalObject: 'this'
}

我們將 globalObject 設(shè)置為 'this' 后租悄,構(gòu)建出來(lái)的包中 UMD 部分的 window 被替換為了 this 谨究,這樣在 Node.js 環(huán)境就不會(huì)再報(bào)上面那個(gè)錯(cuò)了,這對(duì)實(shí)現(xiàn)組件庫(kù)兼容服務(wù)端渲染功能來(lái)說(shuō)非常重要泣棋。

(function webpackUniversalModuleDefinition(root, factory) {
    if(typeof exports === 'object' && typeof module === 'object')
        module.exports = factory(require("vue"));
    else if(typeof define === 'function' && define.amd)
        define("nutui", ["vue"], factory);
    else if(typeof exports === 'object')
        exports["nutui"] = factory(require("vue"));
    else
        root["nutui"] = factory(root["Vue"]);
})(this, function(__WEBPACK_EXTERNAL_MODULE__2__) {

這里吐個(gè)槽胶哲,個(gè)人感覺(jué) Webpack 這部分設(shè)計(jì)欠妥,當(dāng) libraryTarget 值為 umd 時(shí) globalObject 默認(rèn)值應(yīng)該為 this 潭辈,而不能是 window 鸯屿,否則 umd 還有何意義?至少在文檔中 libraryTarget: 'umd' 部分對(duì)此問(wèn)題應(yīng)該有所提及把敢,不然還會(huì)有不少人踩此坑寄摆。

外部依賴(lài)Vue.js

Vue 組件庫(kù)不需要把 Vue.js 也打包進(jìn)去,可在運(yùn)行時(shí)從外部獲取修赞。Webpack 中可以通過(guò) externals 配置外部依賴(lài)婶恼。我們不妨以 jquery 為例看下 externals 的配置方法:

externals: {
    jquery: 'jQuery'
}

這樣 jquery 在構(gòu)建時(shí)不會(huì)打到包內(nèi),而是在運(yùn)行時(shí)需要 jquery 的時(shí)候去外部環(huán)境尋找 jQuery 這個(gè)模塊(或?qū)傩裕┌馗薄U肇埉?huà)虎勾邦,依葫蘆畫(huà)瓢,我們不需要打包 Vue.js 割择,那我們就這么寫(xiě):

externals: {
    vue: 'vue'
}

這時(shí)候構(gòu)建出來(lái)的包在各種模塊化場(chǎng)景使用都沒(méi)毛病检痰,可唯獨(dú)在非模塊化場(chǎng)景會(huì)報(bào)錯(cuò):

vue is not defined

這是為什么呢?我們先來(lái)看下 Vue.js 的部分源碼:

/*!
 * Vue.js v2.6.10
 * (c) 2014-2019 Evan You
 * Released under the MIT License.
 */
(function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
        typeof define === 'function' && define.amd ? define(factory) :
            (global = global || self, global.Vue = factory());

從上面的 Vue.js 源碼中锨推,我們可以看到掛到全局對(duì)象上的 vue 屬性名稱(chēng)是首字母大寫(xiě)的 Vue,而其 NPM 包名卻是小寫(xiě)的 vue 公壤,也就是說(shuō)不同環(huán)境下 Vue 名稱(chēng)不盡一致换可,這可如何是好?

{
  "name": "vue",
  "version": "2.6.10",

還好厦幅,externals 中屬性的值除了字符串沾鳄,還支持傳一個(gè)對(duì)象,可針對(duì)各種場(chǎng)景單獨(dú)設(shè)置模塊名(或?qū)傩悦┤泛@樣一來(lái)译荞,我們就可以為非模塊化環(huán)境配置 'Vue'瓤的,為模塊化環(huán)境配置 'vue'。

externals: {
        'vue': {
            root: 'Vue',
            commonjs: 'vue',
            commonjs2: 'vue',
            amd: 'vue'
        }
}

Vue.js 就是這樣被設(shè)置為組件庫(kù)外部依賴(lài)的吞歼。

Tree-shaking(搖樹(shù))

如前文所述圈膏,Tree-shaking 功能最早由 Rollup 提出并實(shí)現(xiàn),曾是 Rollup 的殺手锏篙骡,后來(lái) Webpack 等工具把它“借鑒”走了稽坤。

搖樹(shù)

Tree-shaking 的原理是在打包時(shí)通過(guò)對(duì)代碼進(jìn)行靜態(tài)分析將未使用的代碼排除,從而減小包體積糯俗。對(duì) JavaScript 進(jìn)行靜態(tài)分析尿褪,這在之前是不可能的。直到 ES6 模塊化方案的提出得湘,才使得 JavaScript 靜態(tài)分析成為可能杖玲,因?yàn)?ES6 模塊是編譯時(shí)加載,不用等到代碼運(yùn)行時(shí)就可以知道加載了哪些模塊淘正。因此要使用 Tree-shaking 功能摆马,就需要在代碼中使用 ES6 模塊方案,不管是用 Rollup 還是 Webpack 打包跪帝。

還有一個(gè)影響 Tree-shaking 施展的可能今膊,那就是 Babel 在 Webpack 開(kāi)始“搖”之前把你的 ES6 模塊轉(zhuǎn)成了 commonJS 模塊,那就“搖”不了了伞剑。這種情況并不罕見(jiàn)斑唬,大部分前端開(kāi)發(fā)者都樂(lè)于使用新語(yǔ)法,所以不止模塊化方案要用 ES6 Module 黎泣,甚至整個(gè)項(xiàng)目的 JavaScript 代碼都用 ES6+ 語(yǔ)法來(lái)寫(xiě)恕刘,為了兼容低版本環(huán)境,通常會(huì)使用 Babel 等工具把 ES6+ 語(yǔ)法轉(zhuǎn)成 低版本語(yǔ)法抒倚。這當(dāng)然沒(méi)問(wèn)題褐着,只是如果想讓 Tree-shaking 發(fā)揮作用,讓我們構(gòu)建出來(lái)的包體積更小托呕,一定要注意含蓉,不要讓 Babel 把ES6模塊語(yǔ)法轉(zhuǎn)成 commonJS ,Rollup 和較新版本的 Webpack 都支持直接處理 ES6 模塊项郊,可以也應(yīng)該把 ES6 模塊部分直接交給它們來(lái)處理馅扣。不使用 Babel 處理ES6模塊,并不意味著最終打出來(lái)的包就是 ES6 模塊着降,如前文所述差油,構(gòu)建出來(lái)的包如何暴露,要兼容哪些模塊規(guī)范打包工具就能搞定任洞。

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "modules": false
      }
    ]
  ]
}

我們測(cè)試了一下蓄喇,Tree-shaking 讓 NutUI 2.0 的完整版的構(gòu)建文件體積明顯減小发侵。

好了,關(guān)于構(gòu)建工具我們先說(shuō)到這里妆偏,具體實(shí)現(xiàn)細(xì)節(jié)可以參考 NutUI 2.0 的源碼[2]刃鳄。后續(xù)的文章我們還會(huì)談組件庫(kù)的按需加載、主題定制楼眷、國(guó)際化铲汪、單元測(cè)試、持續(xù)集成罐柳、基于Markdown文件生成靜態(tài)文檔網(wǎng)站掌腰、Vue公共組件開(kāi)發(fā)等方面的探索實(shí)踐經(jīng)驗(yàn),敬請(qǐng)關(guān)注张吉。

鏈接

[1] NutUI 2.0 官網(wǎng) https://nutui.jd.com
[2] NutUI 2.0 代碼倉(cāng)庫(kù) https://github.com/jdf2e/nutui

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末齿梁,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子肮蛹,更是在濱河造成了極大的恐慌勺择,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,490評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件伦忠,死亡現(xiàn)場(chǎng)離奇詭異省核,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)昆码,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)气忠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人赋咽,你說(shuō)我怎么就攤上這事旧噪。” “怎么了脓匿?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,830評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵淘钟,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我陪毡,道長(zhǎng)米母,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,957評(píng)論 1 295
  • 正文 為了忘掉前任毡琉,我火速辦了婚禮爱咬,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘绊起。我一直安慰自己,他們只是感情好燎斩,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,974評(píng)論 6 393
  • 文/花漫 我一把揭開(kāi)白布虱歪。 她就那樣靜靜地躺著蜂绎,像睡著了一般。 火紅的嫁衣襯著肌膚如雪笋鄙。 梳的紋絲不亂的頭發(fā)上师枣,一...
    開(kāi)封第一講書(shū)人閱讀 51,754評(píng)論 1 307
  • 那天,我揣著相機(jī)與錄音萧落,去河邊找鬼践美。 笑死,一個(gè)胖子當(dāng)著我的面吹牛找岖,可吹牛的內(nèi)容都是我干的陨倡。 我是一名探鬼主播,決...
    沈念sama閱讀 40,464評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼许布,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼兴革!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起蜜唾,我...
    開(kāi)封第一講書(shū)人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤杂曲,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后袁余,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體擎勘,經(jīng)...
    沈念sama閱讀 45,847評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,995評(píng)論 3 338
  • 正文 我和宋清朗相戀三年颖榜,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了棚饵。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,137評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡朱转,死狀恐怖蟹地,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情藤为,我是刑警寧澤怪与,帶...
    沈念sama閱讀 35,819評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站缅疟,受9級(jí)特大地震影響分别,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜存淫,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,482評(píng)論 3 331
  • 文/蒙蒙 一耘斩、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧桅咆,春花似錦括授、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,023評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)薛夜。三九已至,卻和暖如春版述,著一層夾襖步出監(jiān)牢的瞬間梯澜,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,149評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工渴析, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留晚伙,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,409評(píng)論 3 373
  • 正文 我出身青樓俭茧,卻偏偏與公主長(zhǎng)得像咆疗,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子恢恼,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,086評(píng)論 2 355

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

  • 前言 前一段時(shí)間利用空閑時(shí)間學(xué)習(xí)了一下vue組件的封裝民傻,也在工作中進(jìn)行了實(shí)踐,將公司常用的一個(gè)api抽象成了vue...
    朱小維閱讀 13,816評(píng)論 1 30
  • 你是否是 JavaScript 新手扁耐?并且對(duì)模塊,模塊加載器和模塊打包器感到困惑嗎产阱?或者你已經(jīng)編寫(xiě)了一段時(shí)間的 J...
    鋼鋼_94d5閱讀 1,842評(píng)論 0 2
  • rollup是一款小巧的javascript模塊打包工具婉称,更適合于庫(kù)應(yīng)用的構(gòu)建工具;可以將小塊代碼編譯成大塊復(fù)雜的...
    IOneStar閱讀 15,093評(píng)論 2 7
  • 你在南方的艷陽(yáng)里大雪紛飛,我在北方的寒夜里四季如春构蹬。誰(shuí)在街角唱起了那不為人知的歌謠王暗,誰(shuí)又在十字路口勾起了離...
    有年閱讀 248評(píng)論 0 1
  • 有人說(shuō),人生就像一場(chǎng)夢(mèng)庄敛,你永遠(yuǎn)預(yù)知不到接下來(lái)會(huì)夢(mèng)到什么俗壹,但是,夢(mèng)醒了藻烤,你卻可以有無(wú)窮的回憶和回味…… 人生就像一場(chǎng)...
    四月and先生閱讀 273評(píng)論 0 3