使用模塊化工具打包自己開(kāi)發(fā)的JS庫(kù)(webpack/rollup)對(duì)比總結(jié)

打包JS庫(kù)demo項(xiàng)目地址:https://github.com/BothEyes1993/bes-jstools

背景

最近有個(gè)需求,需要為小程序?qū)懸粋€(gè)SDK,監(jiān)控小程序的后臺(tái)接口調(diào)用和頁(yè)面報(bào)錯(cuò)(類(lèi)似fundebug)

聽(tīng)起來(lái)高大上的SDK,其實(shí)就是一個(gè)JS文件,類(lèi)似平時(shí)開(kāi)發(fā)中我們引入的第三方庫(kù):

const moment = require('moment');
moment().format();

小程序的模塊化采用了Commonjs規(guī)范。也就是說(shuō),我需要提供一個(gè)monitor.js文件,并且該文件需要支持Commonjs,從而可以在小程序的入口文件app.js中導(dǎo)入:

// 導(dǎo)入sdk
const monitor = require('./lib/monitor.js');
monitor.init('API-KEY');

// 正常業(yè)務(wù)邏輯
App({
    ...
})

所以問(wèn)題來(lái)了,我應(yīng)該怎么開(kāi)發(fā)這個(gè)SDK? (注意:本文并不具體討論怎么實(shí)現(xiàn)監(jiān)控小程序)

方案有很多種:比如直接把所有的邏輯寫(xiě)在一個(gè)monitor.js文件里,然后導(dǎo)出

module.exports = {
    // 各種邏輯
}

但是考慮到代碼量,為了降低耦合度,我還是傾向于把代碼拆分成不同模塊,最后把所有JS文件打包成一個(gè)monitor.js。平時(shí)有使用過(guò)Vue和React開(kāi)發(fā)的同學(xué),應(yīng)該能體會(huì)到模塊化開(kāi)發(fā)的好處。

src目錄下存放源代碼,dist目錄打包最后的monitor.js

src/main.js SDK入口文件

import { Engine } from './module/Engine';

let monitor = null;

export default {
    init: function (appid) {
        if (!appid || monitor) {
            return;
        }
        monitor = new Engine(appid);
    }
}

// src/module/Engine.js

import { util } from '../util/';

export class Engine {
    constructor(appid) {
        this.id = util.generateId();
        this.appid = appid;
        this.init();
    }

    init() {
        console.log('開(kāi)始監(jiān)聽(tīng)小程序啦~~~');
    }
}

 // src/util/index.js

export const util = {
    generateId() {
        return Math.random().toString(36).substr(2);
    }
}

所以,怎么把這堆js打包成最后的monitor.js文件,并且程序可以正確執(zhí)行?


webpack

我第一個(gè)想到的就是用webpack打包,畢竟工作經(jīng)常用React開(kāi)發(fā),最后打包項(xiàng)目用的就是它。
基于webpack4.x版本

npm i webpack webpack-cli --save-dev

靠著我對(duì)于webpack玄學(xué)的微薄知識(shí),含淚寫(xiě)下了幾行配置:
webpack.config.js

var path = require('path');
var webpack = require('webpack');
module.exports = {
    mode: 'development',
    entry: './src/main.js',
    output: {
        path: path.resolve(__dirname, './dist'),
        publicPath: '/dist/',
        filename: 'monitor.js',
    }
};

運(yùn)行webpack,打包倒是打包出來(lái)了,但是引入到小程序里試試

小程序入口文件app.js

var monitor = require('./dist/monitor.js');

控制臺(tái)直接報(bào)錯(cuò)。。。

在這里插入圖片描述

原因很簡(jiǎn)單:打包出來(lái)的monitor.js使用了eval關(guān)鍵字,而小程序內(nèi)部并支持eval。

我們只需要更改webpack配置的devtool即可

var path = require('path');
var webpack = require('webpack');
module.exports = {
    mode: 'development',
    entry: './src/main.js',
    output: {
        path: path.resolve(__dirname, './dist'),
        publicPath: '/dist/',
        filename: 'monitor.js',
    },
    devtool: 'source-map'
};

source-map模式就不會(huì)使用eval關(guān)鍵字來(lái)方便debug,它會(huì)多生成一個(gè)monitor.js.map文件來(lái)方便debug

再次webpack打包,然后倒入小程序,問(wèn)題又來(lái)了:

var monitor = require('./dist/monitor.js');
console.log(monitor); // {}

打印出來(lái)的是一個(gè)空對(duì)象!

//src/main.js

import { Engine } from './module/Engine';

let monitor = null;

export default {
    init: function (appid) {
        if (!appid || monitor) {
            return;
        }
        monitor = new Engine(appid);
    }
}

monitor.js并沒(méi)有導(dǎo)出一個(gè)含有init方法的對(duì)象!

我們期望的是monitor.js符合commonjs規(guī)范,但是我們?cè)谂渲弥胁](méi)有指出,所以webpack打包出來(lái)的文件,什么也沒(méi)導(dǎo)出。

我們平時(shí)開(kāi)發(fā)中,打包時(shí)也不需要導(dǎo)出一個(gè)變量,只要打包的文件能在瀏覽器上立即執(zhí)行即可。你隨便翻一個(gè)Vue或React的項(xiàng)目,看看入口文件是咋寫(xiě)的?

main.js

import Vue from 'vue'
import App from './App'

new Vue({
  el: '#app',
  components: { App },
  template: '<App/>'
})

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App.js';

ReactDOM.render(
    <App />,
  document.getElementById('root')
);

是不是都類(lèi)似這樣的套路,最后只是立即執(zhí)行一個(gè)方法而已,并沒(méi)有導(dǎo)出一個(gè)變量。

libraryTarget


libraryTarget就是問(wèn)題的關(guān)鍵,通過(guò)設(shè)置該屬性,我們可以讓webpack知道使用何種規(guī)范導(dǎo)出一個(gè)變量

var path = require('path');
var webpack = require('webpack');
module.exports = {
    mode: 'development',
    entry: './src/main.js',
    output: {
        path: path.resolve(__dirname, './dist'),
        publicPath: '/dist/',
        filename: 'monitor.js',
        libraryTarget: 'commonjs2'
    },
    devtool: 'source-map'
    
};

commonjs2就是我們希望的commonjs規(guī)范

重新打包,這次就正確了

var monitor = require('./dist/monitor.js');
console.log(monitor);

在這里插入圖片描述

我們導(dǎo)出的對(duì)象掛載到了default屬性上,因?yàn)槲覀儺?dāng)初導(dǎo)出時(shí):

export default {
    init: function (appid) {
        if (!appid || monitor) {
            return;
        }
        monitor = new Engine(appid);
    }
}

現(xiàn)在,我們可以愉快的導(dǎo)入SDK

var monitor = require('./dist/monitor.js').default;
monitor.init('45454');
在這里插入圖片描述

你可能注意到,我打包時(shí)并沒(méi)有使用babel,因?yàn)樾〕绦蚴侵С謊s6語(yǔ)法的,所以開(kāi)發(fā)該sdk時(shí)無(wú)需再轉(zhuǎn)一遍,如果你開(kāi)發(fā)的類(lèi)庫(kù)需要兼容瀏覽器,則可以加一個(gè)babel-loader

module: {
        rules: [
            {
                test: /\.js$/,
                loader: 'babel-loader',
                exclude: /node_modules/
            }
        ]
    }

注意點(diǎn):

1,平時(shí)開(kāi)發(fā)調(diào)試sdk時(shí)可以直接webpack -w
2,最后打包時(shí),使用webpack -p進(jìn)行壓縮

完整的webpack.config.js

var path = require('path');
var webpack = require('webpack');
module.exports = {
    mode: 'development', // production
    entry: './src/main.js',
    output: {
        path: path.resolve(__dirname, './dist'),
        publicPath: '/dist/',
        filename: 'monitor.js',
        libraryTarget: 'commonjs2'
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                loader: 'babel-loader',
                exclude: /node_modules/
            }
        ]
    },
    devtool: 'source-map' // 小程序不支持eval-source-map
};

其實(shí),使用webpack打包純JS類(lèi)庫(kù)是很簡(jiǎn)單的,比我們平時(shí)開(kāi)發(fā)一個(gè)應(yīng)用,配置少了很多,畢竟不需要打包c(diǎn)ss,html,圖片,字體這些靜態(tài)資源,也不用按需加載。

rollup


文章寫(xiě)到這里本來(lái)可以結(jié)束了,但是在前期調(diào)研如何打包模塊的時(shí)候,我特意看了下Vue和React是怎么打包代碼的,結(jié)果發(fā)現(xiàn),這倆都沒(méi)使用webpack,而是使用了rollup。

Rollup 是一個(gè) JavaScript 模塊打包器,可以將小塊代碼編譯成大塊復(fù)雜的代碼,例如 library 或應(yīng)用程序。

Rollup官網(wǎng)的這段介紹,正說(shuō)明了rollup就是用來(lái)打包library的。
https://www.rollupjs.com/guide/zh#-using-plugins-

如果你有興趣,可以看一下webpack打包后的monitor.js,絕對(duì)會(huì)吐槽,這一坨代碼是啥東西?

module.exports =
/******/ (function(modules) { // webpackBootstrap
/******/     // The module cache
/******/     var installedModules = {};
/******/
/******/     // The require function
/******/     function __webpack_require__(moduleId) {
/******/
/******/         // Check if module is in cache
/******/         if(installedModules[moduleId]) {
/******/             return installedModules[moduleId].exports;
/******/         }
/******/         // Create a new module (and put it into the cache)
/******/         var module = installedModules[moduleId] = {
/******/             i: moduleId,
/******/             l: false,
/******/             exports: {}


// 以下省略1萬(wàn)行代碼

webpack自己實(shí)現(xiàn)了一套__webpack_exports__ __webpack_require__ module機(jī)制

/***/ "./src/util/index.js":
/*!***************************!*\
  !*** ./src/util/index.js ***!
  \***************************/
/*! exports provided: util */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "util", function() { return util; });
const util = {
    generateId() {
        return Math.random().toString(36).substr(2);
    }
}

/***/ })

它把每個(gè)js文件包裹在一個(gè)函數(shù)里,實(shí)現(xiàn)模塊間的引用和導(dǎo)出。

如果使用rollup打包,你就會(huì)驚訝的發(fā)現(xiàn),打包后的代碼可讀性簡(jiǎn)直和webpack不是一個(gè)級(jí)別!

npm install --global rollup

新建一個(gè)rollup.config.js

export default {
  input: './src/main.js',
  output: {
    file: './dist/monitor.js',
    format: 'cjs'
  }
};

format: cjs指定打包后的文件符合commonjs規(guī)范

運(yùn)行rollup -c

這時(shí)會(huì)報(bào)錯(cuò),說(shuō)[!] Error: Could not resolve '../util' from src\module\Engine.js

這是因?yàn)椋瑀ollup識(shí)別../util/時(shí),并不會(huì)自動(dòng)去查找util目錄下的index.js文件(webpack默認(rèn)會(huì)去查找),所以我們需要改成../util/index

打包后的文件:

'use strict';

const util = {
    generateId() {
        return Math.random().toString(36).substr(2);
    }
};

class Engine {
    constructor(appid) {
        this.id = util.generateId();
        this.appid = appid;
        this.init();
    }

    init() {
        console.log('開(kāi)始監(jiān)聽(tīng)小程序啦~~~');
    }
}

let monitor = null;

var main = {
    init: function (appid) {
        if (!appid || monitor) {
            return;
        }
        monitor = new Engine(appid);
    }
}

module.exports = main;

是不是超簡(jiǎn)潔!

而且導(dǎo)入的時(shí)候,無(wú)需再寫(xiě)個(gè)default屬性
webpack打包

var monitor = require('./dist/monitor.js').default;
monitor.init('45454');

rollup打包

var monitor = require('./dist/monitor.js');
monitor.init('45454');

同樣,平時(shí)開(kāi)發(fā)時(shí)我們可以直接rollup -c -w,最后打包時(shí),也要進(jìn)行壓縮

npm i rollup-plugin-uglify -D
import { uglify } from 'rollup-plugin-uglify';
export default {
  input: './src/main.js',
  output: {
    file: './dist/monitor.js',
    format: 'cjs'
  },
  plugins: [
    uglify()
  ]
};

當(dāng)然,你也可以使用babel轉(zhuǎn)碼

npm i rollup-plugin-terser babel-core babel-preset-latest babel-plugin-external-helpers -D

.babelrc

{
  "presets": [
    ["latest", {
      "es2015": {
        "modules": false
      }
    }]
  ],
  "plugins": ["external-helpers"]
}

rollup.config.js

import { terser } from 'rollup-plugin-terser';
import babel from 'rollup-plugin-babel';
export default {
    input: './src/main.js',
    output: {
        file: './dist/monitor.js',
        format: 'cjs'
    },
    plugins: [
        babel({
            exclude: 'node_modules/**'
        }),
        terser()
    ]
};

UMD

我們剛剛打包的SDK,并沒(méi)有用到特定環(huán)境的API,也就是說(shuō),這段代碼,其實(shí)完全可以運(yùn)行在node端和瀏覽器端。

如果我們希望打包的代碼可以兼容各個(gè)平臺(tái),就需要符合UMD規(guī)范(兼容AMD,CMD, Commonjs, iife)

import { terser } from 'rollup-plugin-terser';
import babel from 'rollup-plugin-babel';
export default {
    input: './src/main.js',
    output: {
        file: './dist/monitor.js',
        format: 'umd',
        name: 'monitor'
    },
    plugins: [
        babel({
            exclude: 'node_modules/**'
        }),
        terser()
    ]
};

通過(guò)設(shè)置format和name,這樣我們打包出來(lái)的monitor.js就可以兼容各種運(yùn)行環(huán)境了

在node端

var monitor = require('monitor.js');
monitor.init('6666');

在瀏覽器端

<script src="./monitor.js"></srcipt>
<script>
    monitor.init('6666');
</srcipt>

原理其實(shí)也很簡(jiǎn)單,你可以看下打包后的源碼,或者看我之前寫(xiě)過(guò)的一篇文章

總結(jié)

rollup通常適用于打包JS類(lèi)庫(kù),通過(guò)rollup打包后的代碼,體積較小,而且沒(méi)有冗余的代碼。rollup默認(rèn)只支持ES6的模塊化,如果需要支持Commonjs,還需下載相應(yīng)的插件rollup-plugin-commonjs

webpack通常適用于打包一個(gè)應(yīng)用,如果你需要代碼拆分(Code Splitting)或者你有很多靜態(tài)資源需要處理,那么可以考慮使用webpack

原文地址:https://segmentfault.com/a/1190000015221988#articleHeader0


文章二 使用Rollup打包JavaScript

rollup是一款小巧的javascript模塊打包工具,更適合于庫(kù)應(yīng)用的構(gòu)建工具;可以將小塊代碼編譯成大塊復(fù)雜的代碼,基于ES6
modules,它可以讓你的 bundle
最小化,有效減少文件請(qǐng)求大小,vue在開(kāi)發(fā)的時(shí)候用的是webpack,但是最后將文件打包在一起的時(shí)候用的是 rollup.js

全局安裝

npm install --global rollup

開(kāi)始使用rollup

創(chuàng)建第一個(gè)bundle
創(chuàng)建main.js

console.log(111);

執(zhí)行 rollup --input main.js --output bundle.js --format cjs, 該命令編譯 main.js 生成 bundle.js, --format cjs 意味著打包為 node.js 環(huán)境代碼, 請(qǐng)觀察 bundle.js 文件內(nèi)容

'use strict'
console.log(111);

命令行參數(shù)簡(jiǎn)介:

輸入(input -i/--input)
String 這個(gè)包的入口點(diǎn) (例如:你的 main.js 或者 app.js 或者 index.js)
文件(file -o/--output.file)
String 要寫(xiě)入的文件。也可用于生成 sourcemaps,如果適用
格式(format -f/--output.format)
關(guān)于format選項(xiàng)
rollup提供了五種選項(xiàng):

1,amd – 異步模塊定義,用于像RequireJS這樣的模塊加載器
2,cjs – CommonJS,適用于 Node 和 Browserify/Webpack
3,es – 將軟件包保存為ES模塊文件
4,iife – 一個(gè)自動(dòng)執(zhí)行的功能,適合作為<script>標(biāo)簽。(如果要為應(yīng)用程序創(chuàng)建一個(gè)捆綁包,您可能想要使用它,因?yàn)樗鼤?huì)使文件大小變小。)
5,umd – 通用模塊定義,以amdcjsiife 為一體

使用配置文件

rollup.config.js

export default {
    input: 'src/main.js',
    output: {
        file: 'bundle.js',
        format: 'cjs'
    }
};

執(zhí)行 rollup -c rollup.config.js啟動(dòng)配置項(xiàng);

rollup 提供了 --watch / -w 參數(shù)來(lái)監(jiān)聽(tīng)文件改動(dòng)并自動(dòng)重新打包

使用rollup插件
npm install --save-dev rollup-plugin-json

我們用的是 --save-dev 而不是 --save,因?yàn)榇a實(shí)際執(zhí)行時(shí)不依賴(lài)這個(gè)插件——只是在打包時(shí)使用。

在配置文件中啟用插件

import json from 'rollup-plugin-json';
export default {
    input: './main.js',
    output: {
        file: 'bundle.js',
        format: 'umd'
    },
    plugins: [
        json(),
    ],
}

新建文件 data.json

{
    "name": "xiaoming",
    "age": 12
}

main.js 引入 data.json

import { name } from './data.json';
console.log(name);

執(zhí)行 rollup -c rollup.config.js,并查看 bundle.js

(function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ? factory() :
    typeof define === 'function' && define.amd ? define(factory) :
    (factory());
}(this, (function () { 'use strict';

var name = "xiaoming";

console.log(name);

})));

看到bundle中僅引用了data.json中的name字段,這是因?yàn)閞ollup會(huì)自動(dòng)進(jìn)行 Tree-shaking,main.js中僅引入了name,age并沒(méi)有沒(méi)引用,所以age并不會(huì)被打包

rollup基礎(chǔ)插件

rollup-plugin-alias: 提供modules名稱(chēng)的 alias 和reslove 功能

rollup-plugin-babel: 提供babel能力

rollup-plugin-eslint: 提供eslint能力

rollup-plugin-node-resolve: 解析 node_modules 中的模塊

rollup-plugin-commonjs: 轉(zhuǎn)換 CJS -> ESM, 通常配合上面一個(gè)插件使用

rollup-plugin-serve: 類(lèi)比 webpack-dev-server, 提供靜態(tài)服務(wù)器能力

rollup-plugin-filesize: 顯示 bundle 文件大小

rollup-plugin-uglify: 壓縮 bundle 文件

rollup-plugin-replace: 類(lèi)比 Webpack 的 DefinePlugin , 可在源碼中通過(guò) process.env.NODE_ENV 用于構(gòu)建區(qū)分 Development 與 Production 環(huán)境.

rollup于其他工具集成

打包npm 模塊

webpackBrowserify不同, rollup 不會(huì)去尋找從npm安裝到你的node_modules文件夾中的軟件包;
rollup-plugin-node-resolve 插件可以告訴 Rollup 如何查找外部模塊

npm install --save-dev rollup-plugin-node-resolve

打包 commonjs模塊

npm中的大多數(shù)包都是以CommonJS模塊的形式出現(xiàn)的。 在它們更改之前,我們需要將CommonJS模塊轉(zhuǎn)換為 ES2015 供 Rollup 處理。
rollup-plugin-commonjs 插件就是用來(lái)將 CommonJS 轉(zhuǎn)換成 ES2015 模塊的。
請(qǐng)注意,rollup-plugin-commonjs應(yīng)該用在其他插件轉(zhuǎn)換你的模塊之前 - 這是為了防止其他插件的改變破壞CommonJS的檢測(cè)

npm install --save-dev rollup-plugin-commonjs

使用babel

使用 Babel 和 Rollup 的最簡(jiǎn)單方法是使用 rollup-plugin-babel

npm install --save-dev rollup-plugin-babel

新建.babelrc

{
    "presets": [
        ["latest", {
            "es2015": {
                "modules": false
            }
        }]
    ],
    "plugins": ["external-helpers"]
}

1,首先,我們?cè)O(shè)置"modules": false,否則 Babel 會(huì)在 Rollup 有機(jī)會(huì)做處理之前,將我們的模塊轉(zhuǎn)成 CommonJS,導(dǎo)致 Rollup 的一些處理失敗

2,我們使用external-helpers插件,它允許 Rollup 在包的頂部只引用一次 “helpers”,而不是每個(gè)使用它們的模塊中都引用一遍(這是默認(rèn)行為)
運(yùn)行 rollup之前, 需要安裝latest preset 和external-helpers插件

npm i -D babel-preset-latest babel-plugin-external-helpers

一個(gè)簡(jiǎn)單的配置項(xiàng)

import resolve from 'rollup-plugin-node-resolve';
import commonjs from 'rollup-plugin-commonjs';
import babel from 'rollup-plugin-babel';
import json from 'rollup-plugin-json';
export default {
    input: './main.js',
    output: {
        file: 'bundle.js',
        format: 'umd'
    },
    watch: {
        exclude: 'node_modules/**'
    },
    plugins: [
        resolve(),
        commonjs(),
        json(),
        babel({
            exclude: 'node_modules/**',
            plugins: ['external-helpers'],
        }),
    ],
}

原文地址:http://www.lxweimin.com/p/6a7413481bd2


附一份react-redux開(kāi)源項(xiàng)目的rollup配置文件

import nodeResolve from 'rollup-plugin-node-resolve'     // 幫助尋找node_modules里的包
import babel from 'rollup-plugin-babel'                             // rollup 的 babel 插件,ES6轉(zhuǎn)ES5
import replace from 'rollup-plugin-replace'                       // 替換待打包文件里的一些變量,如 process在瀏覽器端是不存在的,需要被替換
import commonjs from 'rollup-plugin-commonjs'              // 將非ES6語(yǔ)法的包轉(zhuǎn)為ES6可用
import uglify from 'rollup-plugin-uglify'                              // 壓縮包

const env = process.env.NODE_ENV

const config = {
  input: 'src/index.js',
  external: ['react', 'redux'],                           // 告訴rollup,不打包react,redux;將其視為外部依賴(lài)
  output: { 
    format: 'umd',                  // 輸出 UMD格式,各種模塊規(guī)范通用
    name: 'ReactRedux',         // 打包后的全局變量,如瀏覽器端 window.ReactRedux 
    globals: {
      react: 'React',                                         // 這跟external 是配套使用的,指明global.React即是外部依賴(lài)react
      redux: 'Redux'
    }
  },
  plugins: [
    nodeResolve(),
    babel({
      exclude: '**/node_modules/**'
    }),
    replace({
      'process.env.NODE_ENV': JSON.stringify(env)
    }),
    commonjs()
  ]
}

if (env === 'production') {
  config.plugins.push(
    uglify({
      compress: {
        pure_getters: true,
        unsafe: true,
        unsafe_comps: true,
        warnings: false
      }
    })
  )
}

export default config
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,983評(píng)論 6 537
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,772評(píng)論 3 422
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 176,947評(píng)論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 63,201評(píng)論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,960評(píng)論 6 410
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 55,350評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,406評(píng)論 3 444
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,549評(píng)論 0 289
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,104評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,914評(píng)論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,089評(píng)論 1 371
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,647評(píng)論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,340評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 34,753評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 36,007評(píng)論 1 289
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,834評(píng)論 3 395
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,106評(píng)論 2 375

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

  • 寫(xiě)在開(kāi)頭 先說(shuō)說(shuō)為什么要寫(xiě)這篇文章, 最初的原因是組里的小朋友們看了webpack文檔后, 表情都是這樣的: (摘...
    Lefter閱讀 5,311評(píng)論 4 31
  • 版權(quán)聲明:本文為博主原創(chuàng)文章,未經(jīng)博主允許不得轉(zhuǎn)載。 webpack介紹和使用 一、webpack介紹 1、由來(lái) ...
    it筱竹閱讀 11,208評(píng)論 0 21
  • rollup是一款小巧的javascript模塊打包工具,更適合于庫(kù)應(yīng)用的構(gòu)建工具;可以將小塊代碼編譯成大塊復(fù)雜的...
    IOneStar閱讀 15,119評(píng)論 2 7
  • GitChat技術(shù)雜談 前言 本文較長(zhǎng),為了節(jié)省你的閱讀時(shí)間,在文前列寫(xiě)作思路如下: 什么是 webpack,它要...
    蕭玄辭閱讀 12,706評(píng)論 7 110
  • 說(shuō)在前面:這些文章均是本人花費(fèi)大量精力研究整理,如有轉(zhuǎn)載請(qǐng)聯(lián)系作者并注明引用,謝謝本文的受眾人群不是webpack...
    RockSAMA閱讀 6,951評(píng)論 2 7