從0到1搭建webpack2+vue2自定義模板詳細(xì)教程

前言

webpack2和vue2已經(jīng)不是新鮮東西了,滿大街的文章在講解webpack和vue,但是很多內(nèi)容寫(xiě)的不是很詳細(xì),對(duì)于很多個(gè)性化配置還是需要自己過(guò)一遍文檔。Vue官方提供了多個(gè)vue-templates,基于vue-cli用的最多,不過(guò)對(duì)于很多人來(lái)說(shuō),vue-cli 的配置還是過(guò)于復(fù)雜,對(duì)于我們了解細(xì)節(jié)實(shí)現(xiàn)不是很好,所以想自己從零開(kāi)始搭建一個(gè)模板工程,也順便重新認(rèn)識(shí)一下webpack和vue工程化。

webpack 核心概念

Webpack 是當(dāng)下最熱門(mén)的前端資源模塊化管理和打包工具。它可以將許多松散的模塊按照依賴(lài)和規(guī)則打包成符合生產(chǎn)環(huán)境部署的前端資源。還可以將按需加載的模塊進(jìn)行代碼分隔,等到實(shí)際需要的時(shí)候再異步加載。通過(guò) loader 的轉(zhuǎn)換,任何形式的資源都可以視作模塊,比如 CommonJs 模塊、 AMD 模塊、 ES6 模塊、CSS、圖片、 JSON、Coffeescript、 LESS 等。

官方網(wǎng)站:https://webpack.js.org/

安裝

在開(kāi)始前,先要確認(rèn)你已經(jīng)安裝Node.js的最新版本。使用 Node.js 最新的 LTS 版本,是理想的起步。使用舊版本,你可能遇到各種問(wèn)題,因?yàn)樗鼈兛赡苋鄙?webpack 功能或缺少相關(guān) package 包。

本地局部安裝:

# 安裝 latest release
npm install --save-dev webpack
# 簡(jiǎn)寫(xiě)模式
npm install -D webpack
# 安裝特定版本
npm install --save-dev webpack@<version> 

全局安裝:

npm install -g webpack

注意:不推薦全局安裝 webpack。這會(huì)鎖定 webpack 到指定版本,并且在使用不同的 webpack 版本的項(xiàng)目中可能會(huì)導(dǎo)致構(gòu)建失敗。但是全局安裝可以在命令行調(diào)用 webpack 命令。

【補(bǔ)充】npm install 安裝模塊參數(shù)說(shuō)明:

-g, --global 全局安裝(global)
-S, --save 安裝包信息將加入到dependencies(生產(chǎn)階段的依賴(lài))
-D, --save-dev 安裝包信息將加入到devDependencies(開(kāi)發(fā)階段的依賴(lài)),所以開(kāi)發(fā)階段一般使用它
-O, --save-optional 安裝包信息將加入到optionalDependencies(可選階段的依賴(lài))
-E, --save-exact 精確安裝指定模塊版本

npm 相關(guān)的更多命令參考這篇文章:npm 常用命令詳解

然后在根目錄下創(chuàng)建一個(gè) webpack.config.js 文件后,你可以通過(guò)配置定義webpack的相關(guān)操作。

入口(Entry)

入口起點(diǎn)告訴 webpack 從哪里開(kāi)始,并遵循著依賴(lài)關(guān)系圖表知道要打包什么。可以將您應(yīng)用程序的入口起點(diǎn)認(rèn)為是根上下文(contextual root)或 app 第一個(gè)啟動(dòng)文件。

單個(gè)入口(簡(jiǎn)寫(xiě))語(yǔ)法:
用法:entry: string|Array<string>

webpack.config.js:

module.exports = {
  entry: './src/main.js'
};

對(duì)象語(yǔ)法:
用法:entry: {[entryChunkName: string]: string|Array<string>}

webpack.config.js:

module.exports = {
  entry: {
    app: './src/main.js',
    vendor: ['vue']
  }
};

這里我們將vue作為庫(kù)vendor打包,業(yè)務(wù)邏輯代碼作為app打包,實(shí)現(xiàn)了多個(gè)入口,同時(shí)也可以將多個(gè)頁(yè)面分開(kāi)打包。

多頁(yè)面應(yīng)用程序通常使用對(duì)象語(yǔ)法構(gòu)建。對(duì)象語(yǔ)法是“可擴(kuò)展的 webpack 配置”,可重用并且可以與其他配置組合使用。這是一種流行的技術(shù),用于將關(guān)注點(diǎn)(concern)從環(huán)境(environment)、構(gòu)建目標(biāo)(build target)、運(yùn)行時(shí)(runtime)中分離。然后使用專(zhuān)門(mén)的工具(如webpack-merge)將它們合并。

注:vue-cli 生成的模板中build文件夾下有四個(gè)配置文件:

后三個(gè)文件通過(guò)webpack-merge插件合并了基本配置,將不同環(huán)境下的配置拆分多個(gè)文件,這樣更加方便管理。

出口(Output)

將所有的資源(assets)歸攏在一起后,還需要告訴 webpack 在哪里打包應(yīng)用程序。webpack 的 output 屬性描述了如何處理歸攏在一起的代碼(bundled code)。output 選項(xiàng)控制 webpack 如何向硬盤(pán)寫(xiě)入編譯文件。注意,即使可以存在多個(gè)入口起點(diǎn),但只指定一個(gè)輸出配置。

在 webpack 中配置output 屬性的最低要求是,將它的值設(shè)置為一個(gè)對(duì)象,包括以下兩點(diǎn):

  • output.filename:編譯文件的文件名;
  • output.path對(duì)應(yīng)一個(gè)絕對(duì)路徑,此路徑是你希望一次性打包的目錄。

單個(gè)入口:

const path = require('path');
module.exports = {
  entry: './src/app.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'build')  //__dirname + '/build'
  }
}

多個(gè)入口:
如果你的配置創(chuàng)建了多個(gè) "chunk"(例如使用多個(gè)入口起點(diǎn)或使用類(lèi)似CommonsChunkPlugin 的插件),你應(yīng)該使用以下的替換方式來(lái)確保每個(gè)文件名都不重復(fù)。

  • [name] 被 chunk 的 name 替換。
  • [hash] 被 compilation 生命周期的 hash 替換。
  • [chunkhash] 被 chunk 的 hash 替換。
const path = require('path');
module.exports = {
  entry: {
    app: './src/main.js',
    vendor: ['vue']
  },
  output: {
    filename: '[name].js',
    path: path.resolve(__dirname, 'build')
  }
}

// 寫(xiě)入到硬盤(pán):./build/app.js, ./build/vendor.js

加載器(Loaders)

loader 用于對(duì)模塊的源代碼進(jìn)行轉(zhuǎn)換。loader 可以使你在 require() 或"加載"模塊時(shí)預(yù)處理文件。因此,loader 類(lèi)似于其他構(gòu)建工具中“任務(wù)(task)”,并提供了處理前端構(gòu)建步驟的強(qiáng)大方法。loader 可以將文件從不同的語(yǔ)言(如 TypeScript)轉(zhuǎn)換為 JavaScript,或?qū)?nèi)聯(lián)圖像轉(zhuǎn)換為 data URL。loader 甚至允許你在 JavaScript 中 require() CSS文件!

在你的應(yīng)用程序中,有三種方式使用 loader:

這里我們主要說(shuō)明一下使用webpack.config.js配置,使用loader需要在module的rules下配置相應(yīng)的規(guī)則,以css-loader的webpack.config.js為例說(shuō)明:

module.exports = { 
    module: { 
        rules: [
            {test: /\.css$/, use: 'css-loader'}
        ]
    }
};

這三種配置方式等效:

{test: /\.css$/, use: 'css-loader'}
{test: /\.css$/, loader: 'css-loader',options: { modules: true }}
{test: /\.css$/, use: {
    loader: 'css-loader',
    options: {
      modules: true
    }
}}

注:loader/query可以和options可以在同一級(jí)使用,但是不要使用use和options在同一級(jí)使用。

CSS樣式分離

為了用 webpack 對(duì) CSS 文件進(jìn)行打包,你可以像其它模塊一樣將 CSS 引入到你的 JavaScript 代碼中,同時(shí)用css-loader(像 JS 模塊一樣輸出 CSS),也可以選擇使用ExtractTextWebpackPlugin(將打好包的 CSS 提出出來(lái)并輸出成 CSS 文件)。

引入 CSS:

import 'bootstrap/dist/css/bootstrap.css';

安裝css-loader和style-loader:

npm install --save-dev css-loader style-loader

在 webpack.config.js 中配置如下:

module.exports = {
    module: {
        rules: [{
            test: /\.css$/,
            use: ['style-loader', 'css-loader']
        }]
    }
}

資源路徑處理

因?yàn)?png等圖片文件不是一個(gè) JavaScript 文件,你需要配置 Webpack 使用file-loader或者url-loader去處理它們。使用它們的好處:

  • file-loader 可以指定要復(fù)制和放置資源文件的位置,以及如何使用版本哈希命名以獲得更好的緩存。此外,這意味著 你可以就近管理你的圖片文件,可以使用相對(duì)路徑而不用擔(dān)心布署時(shí)URL問(wèn)題。使用正確的配置,Webpack 將會(huì)在打包輸出中自動(dòng)重寫(xiě)文件路徑為正確的URL。
  • url-loader 允許你有條件將文件轉(zhuǎn)換為內(nèi)聯(lián)的 base-64 URL(當(dāng)文件小于給定的閾值),這會(huì)減少小文件的 HTTP 請(qǐng)求。如果文件大于該閾值,會(huì)自動(dòng)的交給 file-loader 處理。

安裝 file-loader 和 url-loader:

npm install --save-dev file-loader url-loader

配置說(shuō)明:

{
    test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
    loader: 'url-loader',
    options: {
        limit: 10000,
        name: 'img/[name]_[hash:7].[ext]'
    }
},
{
    test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
    loader: 'url-loader',
    options: {
        limit: 10000,
        name: 'fonts/[name].[hash:7].[ext]'
    }
}

插件(Plugins)

由于 loader 僅在每個(gè)文件的基礎(chǔ)上執(zhí)行轉(zhuǎn)換,而插件(plugins)最常用于(但不限于)在打包模塊的“compilation”和“chunk”生命周期執(zhí)行操作和自定義功能(查看更多)。webpack 的插件系統(tǒng)極其強(qiáng)大和可定制化

想要使用一個(gè)插件,你只需要 require() 它,然后把它添加到 plugins 數(shù)組中。多數(shù)插件可以通過(guò)選項(xiàng)(option)自定義。你也可以在一個(gè)配置文件中因?yàn)椴煌康亩啻问褂猛粋€(gè)插件,你需要使用 new 創(chuàng)建實(shí)例來(lái)調(diào)用它。

生產(chǎn)環(huán)境構(gòu)建

對(duì)于Vue生產(chǎn)環(huán)境構(gòu)建過(guò)程中壓縮應(yīng)用代碼和使用Vue.js 指南 - 刪除警告去除 Vue.js 中的警告,這里我們參考vue-loader文檔中的配置說(shuō)明:

if (process.env.NODE_ENV === 'production') {
    // http://vue-loader.vuejs.org/zh-cn/workflow/production.html
    module.exports.plugins = (module.exports.plugins || []).concat([
        new webpack.DefinePlugin({
            'process.env': {
                NODE_ENV: '"production"'
            }
        }),
        new webpack.optimize.UglifyJsPlugin({
            sourceMap: false,
            compress: {
                warnings: false
            }
        }),
        new webpack.LoaderOptionsPlugin({
            minimize: true
        })
    ])
}

顯然我們不想在開(kāi)發(fā)過(guò)程中使用這些配置,所以這里我們需要使用環(huán)境變量動(dòng)態(tài)構(gòu)建,我們也可以使用兩個(gè)分開(kāi)的 Webpack 配置文件,一個(gè)用于開(kāi)發(fā)環(huán)境,一個(gè)用于生產(chǎn)環(huán)境,類(lèi)似于vue-cli中使用 webpack-merge 合并配置的方式。

可以使用 Node.js 模塊的標(biāo)準(zhǔn)方式:在運(yùn)行 webpack 時(shí)設(shè)置環(huán)境變量,并且使用 Node.js 的process.env
來(lái)引用變量。NODE_ENV變量通常被視為事實(shí)標(biāo)準(zhǔn)(查看這里)。使用cross-env
包來(lái)跨平臺(tái)設(shè)置(cross-platform-set)環(huán)境變量。

安裝cross-env:

npm install --save-dev cross-env

設(shè)置package.json中的scripts字段:

"scripts": {
    "dev": "cross-env NODE_ENV=development webpack-dev-server --open --hot",
    "build": "cross-env NODE_ENV=production webpack --progress --hide-modules"
}

這里我們使用了cross-env插件,cross-env使得你可以使用單個(gè)命令,而無(wú)需擔(dān)心為平臺(tái)正確設(shè)置或使用環(huán)境變量。

模塊熱替換

模塊熱替換功能會(huì)在應(yīng)用程序運(yùn)行過(guò)程中替換、添加或刪除模塊,而無(wú)需重新加載頁(yè)面。這使得你可以在獨(dú)立模塊變更后,無(wú)需刷新整個(gè)頁(yè)面,就可以更新這些模塊,極大地加速了開(kāi)發(fā)時(shí)間。

這里我們使用webpack-dev-server插件,webpack-dev-server 為你提供了一個(gè)服務(wù)器和實(shí)時(shí)重載(live reloading)功能。webpack-dev-server是一個(gè)小型的node.js Express服務(wù)器,它使用webpack-dev-middleware中間件來(lái)為通過(guò)webpack打包生成的資源文件提供Web服務(wù)。它還有一個(gè)通過(guò)Socket.IO連接著webpack-dev-server服務(wù)器的小型運(yùn)行時(shí)程序。webpack-dev-server發(fā)送關(guān)于編譯狀態(tài)的消息到客戶端,客戶端根據(jù)消息作出響應(yīng)。

安裝 webpack-dev-server:

npm install --save-dev webpack-dev-server

安裝完成之后,你應(yīng)該可以使用 webpack-dev-server 了,方式如下:

webpack-dev-server --open

上述命令應(yīng)該自動(dòng)在瀏覽器中打開(kāi) http://localhost:8080

webpack.config.js配置:

module.exports = {
    ...
    devServer: {
        historyApiFallback: true, // 任意的 404 響應(yīng)都替代為 index.html
        hot: true, // 啟用 webpack 的模塊熱替換特性
        inline: true // 啟用內(nèi)聯(lián)模式
    },
    plugins: [
        new webpack.HotModuleReplacementPlugin()
    ]
    ...
}

更多的配置說(shuō)明可以看文檔:DevServer

動(dòng)態(tài)生成 html 文件

該插件將為你生成一個(gè)HTML5文件,其中包括使用script標(biāo)簽的body中的所有webpack包,也就是我們不需要手動(dòng)通過(guò)script去引入打包生成的js,特別是如果我們生成的文件名是動(dòng)態(tài)變化的,使用這個(gè)插件就可以輕松的解決,只需添加插件到您的webpack配置如下:

const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    ...
    plugins: [
        new HtmlWebpackPlugin({
            filename: 'index.html',
            template: 'index.html',
            inject: true
        })
    ]
    ...
}

提取 CSS 文件

extract-text-webpack-plugin是一個(gè) 可以將*.vue 文件內(nèi)的 <style> 提取,以及JavaScript 中導(dǎo)入的 CSS 提取為單個(gè) CSS 文件。配置文檔具體見(jiàn)這里:extract-text-webpack-plugin

安裝:

npm install --save-dev extract-text-webpack-plugin

配置:

const ExtractTextPlugin = require("extract-text-webpack-plugin");

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ExtractTextPlugin.extract({
          fallback: "style-loader",
          use: "css-loader"
        })
      }
    ]
  },
  plugins: [
    new ExtractTextPlugin("styles.css"),
  ]
}

同時(shí)支持我們可以配置生成多個(gè)css文件,這樣我們可以將業(yè)務(wù)邏輯代碼和引用的樣式組件庫(kù)分離。

const ExtractTextPlugin = require('extract-text-webpack-plugin');

// Create multiple instances
const extractCSS = new ExtractTextPlugin('stylesheets/[name]-one.css');
const extractLESS = new ExtractTextPlugin('stylesheets/[name]-two.css');

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: extractCSS.extract([ 'css-loader', 'postcss-loader' ])
      },
      {
        test: /\.less$/i,
        use: extractLESS.extract([ 'css-loader', 'less-loader' ])
      },
    ]
  },
  plugins: [
    extractCSS,
    extractLESS
  ]
};

clean-webpack-plugin

在編譯前,刪除之前編譯結(jié)果目錄或文件:

npm install --save-dev clean-webpack-plugin

配置:

plugins: [
    new CleanWebpackPlugin(['dist'])
]

這樣當(dāng)我們?cè)跇?gòu)建的時(shí)候可以自動(dòng)刪除之前編譯的代碼。

解析(Resolve)

這些選項(xiàng)能設(shè)置模塊如何被解析。webpack 提供合理的默認(rèn)值,但是還是可能會(huì)修改一些解析的細(xì)節(jié)。

resolve: {
  alias: {
    'vue$': 'vue/dist/vue.esm.js',
    '@': path.join(__dirname, 'src')
  },
  extensions: ['.js', '.json', '.vue', '.css']
}

我們使用最多的就是別名(alias)和自動(dòng)解析確定的擴(kuò)展(extensions),例如上面的@可以代替項(xiàng)目中src的路徑,例如:

import tab from '@/components/tab.vue'

我們引用src/components目錄下的tab.vue組件,不需要通過(guò)../之類(lèi)的計(jì)算文件相對(duì)路徑。這里的extensions可以讓我們?cè)谝肽K時(shí)不帶擴(kuò)展:

import tab from '@/components/tab'

至此我們已經(jīng)學(xué)習(xí)了我們項(xiàng)目devDependencies依賴(lài)中常用的模塊:

webpack 
css-loader / style-loader
file-loader / url-loader 
cross-env 
webpack-dev-server 
html-webpack-plugin 
extract-text-webpack-plugin
clean-webpack-plugin

這里我們只說(shuō)明了css、圖片、html模板資源webpack相關(guān)的加載器和插件,對(duì)于js相關(guān)的內(nèi)容絲毫沒(méi)有提到,顯然這是不合乎情理的。之所以要把js單獨(dú)拿出來(lái)是因?yàn)閖s相關(guān)的內(nèi)容很重要,獨(dú)立出來(lái)詳細(xì)去歸納一下更合適。


webpack 中如何使用 es6 ~ es8?

作為一個(gè)前端,相信 es6 幾乎是無(wú)人不知,很多人也一定知道可以使用Babel做語(yǔ)法轉(zhuǎn)換,但是對(duì)于Babel有哪一些版本,每個(gè)版本支持的es6語(yǔ)法有哪一些應(yīng)該不是所有人都清楚的,這就是這部份內(nèi)容要寫(xiě)的意義。畢竟如果我們的插件只用到了es6中的沒(méi)一些新特性,為此將整個(gè)包引入就有點(diǎn)不太合適,另外為了更好的用上新特性,我們至少要明白有哪一些新特性吧。

ECMAScript 標(biāo)準(zhǔn)建立的過(guò)程

ECMAScript 和 JavaScript 的關(guān)系在此不再贅述,建議閱讀一下阮一峰老師的《ECMAScript 6簡(jiǎn)介》,我們需要了解的是從ECMAScript 2016開(kāi)始,ECMAScript將進(jìn)入每年發(fā)布一次新標(biāo)準(zhǔn)的階段。制定ECMAScript 標(biāo)準(zhǔn)的組織是ECMAScript TC39TC39(ECMA技術(shù)委員為39)是推動(dòng)JavaScript發(fā)展的委員會(huì)。 它的成員是都是企業(yè)(主要是瀏覽器廠商)。TC39會(huì)定期的開(kāi)會(huì), 會(huì)議的主要成員時(shí)是成員公司的代表,以及受邀請(qǐng)的專(zhuān)家。

一種新的語(yǔ)法從提案到變成正式標(biāo)準(zhǔn),需要經(jīng)歷五個(gè)階段。每個(gè)階段的變動(dòng)都需要由 TC39 委員會(huì)批準(zhǔn)。

  • Stage 0 - Strawman(展示階段)
  • Stage 1 - Proposal(征求意見(jiàn)階段)
  • Stage 2 - Draft(草案階段)
  • Stage 3 - Candidate(候選人階段)
  • Stage 4 - Finished(定案階段)

建議看一下alinode 團(tuán)隊(duì)的圖說(shuō)ECMAScript新標(biāo)準(zhǔn)(一)就可以大致了解整個(gè)過(guò)程。

安裝 Babel

Babel 現(xiàn)在的官網(wǎng)提供了一個(gè)可以根據(jù)你的工具提示下載合適的包,具體見(jiàn)這里:Using Babel

如果你想要在命令行使用Babel,你可以安裝babel-cli,但是全局的安裝babel-cli不是一個(gè)好的選擇,因?yàn)檫@樣限定了你Babel的版本;如果你需要在一個(gè)Node項(xiàng)目中使用Babel,你可以使用babel-core。

我們這里自然選擇webpack構(gòu)建我們的工程,下載方案如下:

npm install --save-dev babel-loader babel-core

然后我們需要在項(xiàng)目根目錄下建立.babelrc文件:

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

注:在window下無(wú)法通過(guò) 右鍵=>新建 命令來(lái)創(chuàng)建以點(diǎn)開(kāi)頭的文件和文件夾,我們可以通過(guò)下面的命令生成.babelrc文件:

type NUL > .babelrc

Linux和Mac下可以通過(guò)touch命令生成:

touch .babelrc

Babel 預(yù)設(shè)(presets)

Babel是一個(gè)編譯器。 在高層次上,它有3個(gè)階段,它運(yùn)行代碼:解析,轉(zhuǎn)換和生成(像許多其他編譯器)。默認(rèn)情況下,Babel 6并沒(méi)有攜帶任何轉(zhuǎn)換器,因此如果對(duì)你的代碼使用Babel的話,它將會(huì)原文輸出你的代碼,不會(huì)有任何的改變。因此你需要根據(jù)你需要完成的任務(wù)來(lái)單獨(dú)安裝相應(yīng)的插件。

你可以通過(guò)安裝插件(plugins)或預(yù)設(shè)(presets,也就是一組插件)來(lái)指示 Babel 去做什么事情。Babel 提供了多個(gè)版本的官方預(yù)設(shè):

babel-preset-env

babel-preset-env可以根據(jù)你配置的選項(xiàng),自動(dòng)添加一些其他的轉(zhuǎn)換器,來(lái)滿足你當(dāng)前的裝換需求。.babelrc文件新增了options選項(xiàng):

{
  "presets": ["env", options]
}

具體的配置內(nèi)容:

  • targets.node 支持到哪個(gè)版本的 node
  • targets.browsers 支持到哪個(gè)版本的瀏覽器
  • loose 啟動(dòng)寬松模式,配合 webpack 的 loader 使用
  • modules 使用何種模塊加載機(jī)制
  • debug 開(kāi)啟調(diào)試模式
  • include 包含哪些文件
  • exclude 排除哪些文件
  • useBuiltIns 是否對(duì) babel-polyfill 進(jìn)行分解,只引入所需的部分

babel-preset-es2015

es2015(ES6)相關(guān)方法轉(zhuǎn)譯使用的插件,具體見(jiàn)文檔

  • check-es2015-constants // 檢驗(yàn)const常量是否被重新賦值
  • transform-es2015-arrow-functions // 編譯箭頭函數(shù)
  • transform-es2015-block-scoped-functions // 函數(shù)聲明在作用域內(nèi)
  • transform-es2015-block-scoping // 編譯const和let
  • transform-es2015-classes // 編譯class
  • transform-es2015-computed-properties // 編譯計(jì)算對(duì)象屬性
  • transform-es2015-destructuring // 編譯解構(gòu)賦值
  • transform-es2015-duplicate-keys // 編譯對(duì)象中重復(fù)的key,其實(shí)是轉(zhuǎn)換成計(jì)算對(duì)象屬性
  • transform-es2015-for-of // 編譯for...of
  • transform-es2015-function-name // 將function.name語(yǔ)義應(yīng)用于所有的function
  • transform-es2015-literals // 編譯整數(shù)(8進(jìn)制/16進(jìn)制)和unicode
  • transform-es2015-modules-commonjs // 將modules編譯成commonjs
  • transform-es2015-object-super // 編譯super
  • transform-es2015-parameters // 編譯參數(shù),包括默認(rèn)參數(shù),不定參數(shù)和解構(gòu)參數(shù)
  • transform-es2015-shorthand-properties // 編譯屬性縮寫(xiě)
  • transform-es2015-spread // 編譯展開(kāi)運(yùn)算符
  • transform-es2015-sticky-regex // 正則添加sticky屬性
  • transform-es2015-template-literals // 編譯模版字符串
  • transform-es2015-typeof-symbol // 編譯Symbol類(lèi)型
  • transform-es2015-unicode-regex // 正則添加unicode模式
  • transform-regenerator // 編譯generator函數(shù)

babel-preset-es2016

es2016(ES7)相關(guān)方法轉(zhuǎn)譯使用的插件,具體見(jiàn)文檔

  • transform-exponentiation-operator // 編譯冪運(yùn)算符

babel-preset-es2017

es2017(ES8)相關(guān)方法轉(zhuǎn)譯使用的插件,具體見(jiàn)文檔

  • syntax-trailing-function-commas // function最后一個(gè)參數(shù)允許使用逗號(hào)
  • transform-async-to-generator // 把a(bǔ)sync函數(shù)轉(zhuǎn)化成generator函數(shù)

babel-preset-latest

latest是一個(gè)特殊的presets,包括了es2015,es2016,es2017的插件,不過(guò)已經(jīng)廢棄,使用babel-preset-env代替,具體見(jiàn)文檔

stage-x(stage-0/1/2/3/4)

stage-x預(yù)設(shè)中的任何轉(zhuǎn)換都是尚未被批準(zhǔn)為發(fā)布Javascript的語(yǔ)言(如ES6 / ES2015)的更改。

stage-x和上面的es2015等有些類(lèi)似,但是它是按照J(rèn)avaScript的提案階段區(qū)分的,一共有5個(gè)階段。而數(shù)字越小,階段越靠后,存在依賴(lài)關(guān)系。也就是說(shuō)stage-0是包括stage-1的,以此類(lèi)推。

babel-preset-stage-4:

stage-4的插件:

babel-preset-stage-3:

除了stage-4的內(nèi)容,還包括以下插件:

babel-preset-stage-2:

除了stage-3的內(nèi)容,還包括以下插件:

babel-preset-stage-1:

除了stage-2的內(nèi)容,還包括以下插件:

babel-preset-stage-0:

除了stage-1的內(nèi)容,還包括以下插件:

為了方便,我們暫時(shí)引用 babel-preset-envbabel-preset-stage-2這兩個(gè)預(yù)設(shè)。為了啟用預(yù)設(shè),必須在.babelrc文件中定義預(yù)設(shè)的相關(guān)配置,這里參考vue-cli 模板中的配置
安裝:

npminstall --save-dev babel-preset-env babel-preset-stage-2

.babelrc配置說(shuō)明:

{
  "presets": [
    ["env", { 
      "modules": false 
    }],
    "stage-2"
  ]
}

Babel 插件(plugins)

我們看一下預(yù)設(shè)的構(gòu)成就知道,其實(shí)就是plugins的組合。如果你不采用presets,完全可以單獨(dú)引入某個(gè)功能,比如以下的設(shè)置就會(huì)引入編譯箭頭函數(shù)的功能,在.babelrc文件中進(jìn)行配置:

{
  "plugins": ["transform-es2015-arrow-functions"]
}

babel-polyfill 與 babel-runtime

Babel默認(rèn)只轉(zhuǎn)換新的JavaScript句法(syntax),而不轉(zhuǎn)換新的API,比如Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise等全局對(duì)象,以及一些定義在全局對(duì)象上的方法(比如 Object.assign)都不會(huì)轉(zhuǎn)碼。

舉例來(lái)說(shuō),ES6在 Array 對(duì)象上新增了 Array.from 方法。Babel就不會(huì)轉(zhuǎn)碼這個(gè)方法。如果想讓這個(gè)方法運(yùn)行,必須使用 babel-polyfill ,為當(dāng)前環(huán)境提供一個(gè)墊片。babel-polyfill 是對(duì)瀏覽器缺失API的支持。

babel-runtime 是為了減少重復(fù)代碼而生的。 babel生成的代碼,可能會(huì)用到一些_extend(), classCallCheck() 之類(lèi)的工具函數(shù),默認(rèn)情況下,這些工具函數(shù)的代碼會(huì)包含在編譯后的文件中。如果存在多個(gè)文件,那每個(gè)文件都有可能含有一份重復(fù)的代碼。babel-runtime插件能夠?qū)⑦@些工具函數(shù)的代碼轉(zhuǎn)換成require語(yǔ)句,指向?yàn)閷?duì)babel-runtime的引用,如require('babel-runtime/helpers/classCallCheck'). 這樣, classCallCheck的代碼就不需要在每個(gè)文件中都存在了。

啟用插件 babel-plugin-transform-runtime 后,Babel 就會(huì)使用 babel-runtime 下的工具函數(shù)。除此之外,babel 還為源代碼的非實(shí)例方法(Object.assign,實(shí)例方法是類(lèi)似這樣的 "foobar".includes("foo"))和 babel-runtime/helps 下的工具函數(shù)自動(dòng)引用了 polyfill。這樣可以避免污染全局命名空間,非常適合于 JavaScript 庫(kù)和工具包的實(shí)現(xiàn)。

總結(jié):

  • 具體項(xiàng)目還是需要使用 babel-polyfill,只使用 babel-runtime 的話,實(shí)例方法不能正常工作(例如 "foobar".includes("foo"));
  • JavaScript 庫(kù)和工具可以使用 babel-runtime,在實(shí)際項(xiàng)目中使用這些庫(kù)和工具,需要該項(xiàng)目本身提供 polyfill。
  • transform-runtime只會(huì)對(duì)es6的語(yǔ)法進(jìn)行轉(zhuǎn)換,而不會(huì)對(duì)新api進(jìn)行轉(zhuǎn)換。如果需要轉(zhuǎn)換新api,就要引入babel-polyfill。

安裝插件

npm install --save-dev babel-plugin-transform-runtime

.babelrc 配置:

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

options主要有以下設(shè)置項(xiàng):

  • helpers: boolean,默認(rèn)true,使用babel的helper函數(shù);
  • polyfill: boolean,默認(rèn)true,使用babel的polyfill,但是不能完全取代babel-polyfill;
  • regenerator: boolean,默認(rèn)true,使用babel的regenerator;
  • moduleName: string,默認(rèn)babel-runtime,使用對(duì)應(yīng)module處理。

注:默認(rèn)moduleName為babel-runtime,這里我們可以不必顯式的下載babel-runtime,因?yàn)閎abel-plugin-transform-runtime依賴(lài)于babel-runtime。

babel-register

babel-register 模塊改寫(xiě) require 命令,為它加上一個(gè)鉤子。此后,每當(dāng)使用 require 加載 .js 、 .jsx 、 .es 和 .es6 后綴名的文件,就會(huì)先用Babel進(jìn)行轉(zhuǎn)碼。引入babel-register,這樣后面的文件就可以用 import 代替require,import的優(yōu)點(diǎn)在于可以引入所需方法或者變量,而不需要加載整個(gè)模塊,提高了性能。

安裝:

npm install --save-dev babel-register

這部分我們又介紹了下面幾個(gè)模塊的安裝:

babel-loader
babel-core
babel-preset-env 
babel-preset-stage-2 
babel-plugin-transform-runtime
babel-register

webpack 中如何使用 vue?

既然本文的目標(biāo)是vue的自定義模板工程,那么自然這里需要單獨(dú)介紹一下webpack中vue相關(guān)的插件。

Vue2 dist 目錄下各個(gè)文件的區(qū)別

npm 安裝:

npm install --save vue

vue2 經(jīng)過(guò) 2.2 版本升級(jí)后, 文件變成 8 個(gè):

|| UMD | CommonJS | ES Module |
| :----: | :----: | :----: |
| 獨(dú)立構(gòu)建 | vue.js | vue.common.js | vue.esm.js |
| 運(yùn)行構(gòu)建 | vue.runtime.js | vue.runtime.common.js | vue.runtime.esm.js |

vue.min.js 和 vue.runtime.min.js 都是對(duì)應(yīng)的壓縮版。

  • AMD:異步模塊規(guī)范
    vue 沒(méi)有單獨(dú)提供 AMD 模塊的版本,但是UMD版本中進(jìn)行了包裝,可以直接用作 AMD 模塊,使用方法如下:
define(["Vue"],function(Vue) {
    function myFn() {
        ...
    }
    return myFn;
});
  • CommonJS:
    node中常用的模塊規(guī)范,通過(guò)require引入模塊,module.exports導(dǎo)出模塊。
...
function Vue$3() {
   ...
}
...
module.exports = Vue$3;
  • UMD: 通用模塊規(guī)范
    兼容了AMD和CommonJS,同時(shí)還支持老式的“全局”變量規(guī)范:
(function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
    typeof define === 'function' && define.amd ? define(factory) :
    (global.Vue = factory());
}(this, (function () { 'use strict';
    ...
    function Vue$3() {
        ...
    }
    ...
    return Vue$3;
})));
  • ES Module
    ES6在語(yǔ)言標(biāo)準(zhǔn)的層面上,實(shí)現(xiàn)的模塊功能。模塊功能主要由兩個(gè)命令構(gòu)成:export和import。export命令用于規(guī)定模塊的對(duì)外接口,import命令用于輸入其他模塊提供的功能。
...
function Vue$3() {
   ...
}
export default Vue$3;

總結(jié):

  • vue.js 和 vue.runtime.js 可以用于直接 CDN 引用;
  • vue.common.js和vue.runtime.common.js可以使用Webpack1 / Browserify 打包構(gòu)建;
  • vue.esm.js和vue.runtime.esm.js可以使用Webpack2 / rollup 打包構(gòu)建。

vue有兩種構(gòu)建方式,獨(dú)立構(gòu)建和運(yùn)行時(shí)構(gòu)建。它們的區(qū)別獨(dú)立構(gòu)建前者包含模板編譯器而運(yùn)行構(gòu)建不包含。模板編譯器的職責(zé)是將模板字符串編譯為純 JavaScript 的渲染函數(shù)。如果你想要在組件中使用 template 選項(xiàng),你就需要編譯器。

  • 獨(dú)立構(gòu)建包含模板編譯器并支持 template 選項(xiàng)。 它也依賴(lài)于瀏覽器的接口的存在,所以你不能使用它來(lái)為服務(wù)器端渲染。
  • 運(yùn)行時(shí)構(gòu)建不包含模板編譯器,因此不支持 template 選項(xiàng),只能用 render 選項(xiàng),但即使使用運(yùn)行時(shí)構(gòu)建,在單文件組件中也依然可以寫(xiě)模板,因?yàn)閱挝募M件的模板會(huì)在構(gòu)建時(shí)預(yù)編譯為 render 函數(shù)。運(yùn)行時(shí)構(gòu)建比獨(dú)立構(gòu)建要輕量30%,只有 17.14 Kb min+gzip大小。

獨(dú)立構(gòu)建方式可以這樣使用template選項(xiàng):

import Vue from 'vue'
new Vue({
  template: `
    <div id="app">
      <h1>Basic</h1>
    </div>
  `
}).$mount('#app')

這里我們使用ES Module規(guī)范,默認(rèn) NPM 包導(dǎo)出的是運(yùn)行時(shí)構(gòu)建。為了使用獨(dú)立構(gòu)建,在 webpack 配置中添加下面的別名:

resolve: {
  alias: {
    'vue$': 'vue/dist/vue.esm.js'
  }
}

vue-loader

安裝:

npm install --save-dev vue-loader vue-template-compiler

vue-loader 依賴(lài)于 vue-template-compiler。

vue-loader 是一個(gè) Webpack 的 loader,可以將用下面這個(gè)格式編寫(xiě)的 Vue 組件轉(zhuǎn)換為 JavaScript 模塊。這里有一些 vue-loader 提供的很酷的特性:

  • ES2015 默認(rèn)支持;
  • 允許對(duì) Vue 組件的組成部分使用其它 Webpack loaders,比如對(duì) <style> 使用 SASS 和對(duì) <template> 使用 Jade;
  • .vue 文件中允許自定義節(jié)點(diǎn),然后使用自定義的 loader 處理他們;
  • <style><template> 中的靜態(tài)資源當(dāng)作模塊來(lái)對(duì)待,并使用 Webpack loaders 進(jìn)行處理;
  • 對(duì)每個(gè)組件模擬出 CSS 作用域;
  • 支持開(kāi)發(fā)期組件的熱重載。

簡(jiǎn)而言之,編寫(xiě) Vue.js 應(yīng)用程序時(shí),組合使用 Webpack 和 vue-loader 能帶來(lái)一個(gè)現(xiàn)代,靈活并且非常強(qiáng)大的前端工作流程。

在 Webpack 中,所有的預(yù)處理器需要匹配對(duì)應(yīng)的 loader。 vue-loader 允許你使用其它 Webpack loaders 處理 Vue 組件的某一部分。它會(huì)根據(jù) lang 屬性自動(dòng)推斷出要使用的 loaders。

上述我們提到extract-text-webpack-plugin插件提取css,這里說(shuō)明一下.vue中style標(biāo)簽之間的樣式提取的辦法:

var ExtractTextPlugin = require("extract-text-webpack-plugin");

module.exports = {
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader',
        options: {
          loaders: {
            css: ExtractTextPlugin.extract({
              use: 'css-loader',
              fallback: 'vue-style-loader' // <- 這是vue-loader的依賴(lài),所以如果使用npm3,則不需要顯式安裝
            })
          }
        }
      }
    ]
  },
  plugins: [
    new ExtractTextPlugin("app.css")
  ]
}

pug 模板

用過(guò)模板的都知道,熟悉了模板寫(xiě)起來(lái)快多了,大名鼎鼎的jade恐怕無(wú)人不知吧。pug是什么鬼?第一次聽(tīng)到的時(shí)候我也好奇了,然后查了一下才知道,Pug原名不叫Pug,原來(lái)是大名鼎鼎的jade,后來(lái)由于商標(biāo)的原因,改為Pug,哈巴狗。以下是官方解釋?zhuān)?/p>

it has been revealed to us that "Jade" is a registered trademark, and as a result a rename is needed. After some discussion among the maintainers, "Pug" has been chosen as the new name for this project.

簡(jiǎn)單看了看還是原來(lái)jade熟悉的語(yǔ)法規(guī)則,果斷在這個(gè)模板工程里面用上。

vue-loader里面對(duì)于模版的處理方式略有不同,因?yàn)榇蠖鄶?shù) Webpack 模版處理器(比如 pug-loader)會(huì)返回模版處理函數(shù),而不是編譯的 HTML 字符串,我們使用原始的 pug 替代 pug-loader:

npm install pug --save-dev

使用:

<template lang="pug">
div
  h1 Hello world!
</template>

重要: 如果你使用 vue-loader@<8.2.0, 你還需要安裝 template-html-loader

PostCSS

安裝vue-loader的時(shí)候默認(rèn)安裝了postcss,由vue-loader處理的 CSS 輸出,都是通過(guò)PostCSS進(jìn)行作用域重寫(xiě),你還可以為 PostCSS 添加自定義插件,例如autoprefixer或者CSSNext

在 webpack 工程中使用 postcss,我們需要下載 postcss-loader:

npm install --save-dev postcss-loader

cssnext

cssnext 是一個(gè) CSS transpiler,允許你使用最新的 CSS 語(yǔ)法。cssnext 把 新 CSS 規(guī)范轉(zhuǎn)換成兼容性更強(qiáng)的 CSS,所以不需要等待各種瀏覽器支持。

安裝:

npm install --save-dev postcss-cssnext

postcss.config.js:

module.exports = {
    plugins: [
        require('postcss-cssnext')
    ]
}

webpack.config.js:

module.exports = {
    module: {
        loaders: [
            {
                test:   /\.css$/,
                use: ['style-loader', 'css-loader', 'postcss-loader']
            }
        ]
    }
}

cssnext 依賴(lài)了autoprefixer,所以我們無(wú)需顯式下載autoprefixer。更多關(guān)于postcss的插件可以看這里:postcss plugins

這一部分我們學(xué)習(xí)了這些依賴(lài):

vue
vue-loader 
vue-template-compiler
pug
postcss-loader
postcss-cssnext

webpack2 開(kāi)啟 eslint 校驗(yàn)

規(guī)范自己的代碼從ESlint開(kāi)始。ESlint和webpack集成,在babel編譯代碼開(kāi)始前,進(jìn)行代碼規(guī)范檢測(cè)。這里我們使用javascript-style-standard風(fēng)格的校驗(yàn)。

主要依賴(lài)的幾個(gè)包:

eslint —— 基礎(chǔ)包
eslint-loader —— webpack loader
babel-eslint —— 校驗(yàn)babel
eslint-plugin-html —— 提取并檢驗(yàn)?zāi)愕?.vue 文件中的 JavaScript
eslint-friendly-formatter —— 生成美化的報(bào)告格式

# javascript-style-standard 依賴(lài)的包
eslint-config-standard
eslint-plugin-import
eslint-plugin-node
eslint-plugin-promise
eslint-plugin-standard

安裝:

npm install --save-dev eslint eslint-loader babel-eslint eslint-plugin-html eslint-friendly-formatter eslint-config-standard eslint-plugin-import eslint-plugin-node eslint-plugin-node eslint-plugin-promise eslint-plugin-standard

關(guān)于eslint的配置方式,比較多元化,具體可以看配置文檔

  • js注釋
  • .eslintrc.*文件
  • package.json里面配置eslintConfig字段

安裝eslint-loader之后,我們可以在webpack配置中使用eslint加載器。webpack.config.js

...
module: {
  loaders: [
    {
         test: /\.vue|js$/,
         enforce: 'pre',
         include: path.resolve(__dirname, 'src'),
         exclude: /node_modules/,
         use: [{
             loader: 'eslint-loader',
             options: {
                 formatter: require('eslint-friendly-formatter')
             }
         }]
    }
  ]
},
...

此外,我們既可以在webpack配置文件中指定檢測(cè)規(guī)則,也可以遵循最佳實(shí)踐在一個(gè)專(zhuān)門(mén)的文件中指定檢測(cè)規(guī)則,我們就采用后面的方式。
在根目錄下:

touch .eslintrc.js

.eslintrc.js:

module.exports = {
  root: true,
  parser: 'babel-eslint',
  parserOptions: {
    sourceType: 'module'
  },
  env: {
    browser: true
  },
  extends: 'standard',
  // required to lint *.vue files
  plugins: [
    'html'
  ],
  // add your custom rules here
  rules: {
    // allow paren-less arrow functions
    'arrow-parens': 0,
    // allow async-await
    'generator-star-spacing': 0,
    // allow debugger during development
    'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0
  }
}

這部份我們主要學(xué)習(xí)了一下eslint相關(guān)插件的含義和配置方法。

創(chuàng)建屬于你的模板

如果你對(duì)官方的模板不感興趣,你可以自己fork下來(lái)然后進(jìn)行修改(或者重新寫(xiě)一個(gè)),然后用 vue-cli 來(lái)調(diào)用。因?yàn)?vue-cli 可以直接拉取 git源:

vue init username/repo my-project

這里我們參考vue-cli的模板工程自己寫(xiě)一個(gè)模板工程,主要是需要通過(guò)meta.*(js,json)進(jìn)行配置:

module.exports = {
  "helpers": {
    "if_or": function (v1, v2, options) {
      if (v1 || v2) {
        return options.fn(this);
      }

      return options.inverse(this);
    }
  },
  "prompts": {
    "name": {
      "type": "string",
      "required": true,
      "message": "Project name"
    },
    "version": {
      "type": "string",
      "required": false,
      "message": "Project version",
      "default": "1.0.0"
    },
    "description": {
      "type": "string",
      "required": false,
      "message": "Project description",
      "default": "A Vue.js project"
    },
    "author": {
      "type": "string",
      "message": "Author"
    },
    "router": {
      "type": "confirm",
      "message": "Install vue-router?"
    },
    "vuex": {
      "type": "confirm",
      "message": "Install vuex?"
    }
  },
  "completeMessage": "To get started:\n\n  {{^inPlace}}cd {{destDirName}}\n  {{/inPlace}}npm install\n  npm run dev\n\nDocumentation can be found at https://github.com/zhaomenghuan/vue-webpack-template"
};

這里我們就是采用最簡(jiǎn)單的方式,對(duì)于vue-router、vuex的配置每個(gè)人習(xí)慣不一樣,所以不寫(xiě)在模板工程里面。

然后使用vue-cli使用這個(gè)模板創(chuàng)建工程,沒(méi)有安裝vue-cli的執(zhí)行:

npm install --global vue-cli

然后創(chuàng)建工程:

# 創(chuàng)建一個(gè)基于 webpack 模板的新項(xiàng)目
vue init zhaomenghuan/vue-webpack-template my-project
# 安裝依賴(lài),走你
cd my-project
npm install
npm run dev

這里按照國(guó)際慣例安利一下本文的模板工程:vue-webpack-template

參考

webpack官方文檔
babel官方文檔
vue-loader中文文檔
JavaScript books by Dr. Axel Rauschmayer
ES7新特性及ECMAScript標(biāo)準(zhǔn)的制定流程
如何寫(xiě)好.babelrc?Babel的presets和plugins配置解析
babel的polyfill和runtime的區(qū)別
webpack2集成eslint

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

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