從0開始構(gòu)建一個前端工程架構(gòu)

2017年4月1日遠(yuǎn)眺天津之眼

零、前端技術(shù)選型

1、多頁應(yīng)用

① 特點(diǎn):

  • 內(nèi)容都是由服務(wù)端用模板生成首昔,如JSP敢课,Django等
  • 每次頁面跳轉(zhuǎn)都要經(jīng)過服務(wù)端旬盯,會給用戶一定等待時間
  • JavaScript更多只是做頁面的事件、動畫等

② 主要的類庫:

  • jQuery(快速操作DOM翎猛,兼容各類瀏覽器)
  • YUI

③ 架構(gòu)工具:

  • 沒有特定的前端工具胖翰,跟后端配合
  • grunt / gulp

④ 模塊化工具:
seajs
requirejs

⑤ 靜態(tài)文件
使用gulp或grunt等工作手動編譯到html中,自由度低切厘,操作復(fù)雜萨咳,或者不處理,直接交給后端疫稿,讓后端處理培他。

2、單頁應(yīng)用

① 特點(diǎn):

  • 所有內(nèi)容都在前端生成
  • JavaScript承擔(dān)更多的業(yè)務(wù)邏輯遗座,后端只提供API
  • 頁面路由跳轉(zhuǎn)不需要經(jīng)過后端舀凛,由前端生成路由

② 常用類庫:

  • Backbone.js
  • Angular.js(typescript, .ts,http2)
  • React.js(.jsx途蒋,單向數(shù)據(jù)流猛遍,方便數(shù)據(jù)管理)
  • Vue.js(.vue, template,數(shù)據(jù)雙向綁定)

③ 架構(gòu)工具

  • npm包管理器
  • bower
  • jspm(前端類庫單獨(dú)出來号坡,使用http2懊烤,效率更高)

④ 模塊化工具

  • webpack
  • rollup(按需加載,打包更快)
  • browserify

⑤ 靜態(tài)文件
可以直接在JavaScript代碼中進(jìn)行引用宽堆,并且交由模塊化工具轉(zhuǎn)化成線上可用的靜態(tài)資源腌紧,可以定制轉(zhuǎn)化過程適應(yīng)不同的需求場景。

⑥ 其他考慮因素

  • 瀏覽器兼容性
  • 移動端還是PC端
  • 面向市場用戶(重交互畜隶,性能)壁肋,還是面向企業(yè)使用(重安全,功能)

一籽慢、為什么要使用工程架構(gòu)浸遗?

① 極大地解放了生產(chǎn)力,提高了開發(fā)效率

  • 源代碼預(yù)處理嗡综,將項(xiàng)目框架語言最終轉(zhuǎn)化為瀏覽器能看懂的JavaScript
  • 自動打包乙帮,自動更新頁面顯示
  • 自動圖片處理依賴,保證開發(fā)和正式環(huán)境的統(tǒng)一

② 圍繞解決方案搭建環(huán)境

  • 不同的前端框架需要不同的運(yùn)行架構(gòu)
  • 語氣可能出現(xiàn)的問題并規(guī)避

③ 保證項(xiàng)目質(zhì)量

  • 代碼規(guī)范极景,使用Eslint配置
  • 不同操作系統(tǒng)或編輯器下差異的排除察净,使用editorconfig配置
  • git commit預(yù)處理驾茴,提交代碼前,強(qiáng)制執(zhí)行代碼規(guī)范

二氢卡、webpack基本配置

1锈至、什么是webpack?

Webpack is a module bundler for modern JavaScript applications.
webpack官方介紹译秦,它是一個現(xiàn)代JavaScript應(yīng)用誕生的模塊打包器峡捡。

2、基礎(chǔ)配置
// webpack.config.client.js
const path = require('path');

module.exports = {
   entry: {      // (1)
       app: path.join(__dirname, '../client/app.js'),
   },
   output: {    // (2)
       filename: '[name].[hash:5].js',    // (3)
       path: path.join(__dirname, '../dist'),    // (4)
       publicPath: '/public'          // (5)
   }
}
  • (1) enrty是指定了一個資源文件的路徑
  • (2) output指定了輸出文件的目錄
  • (3) filename是輸出文件名筑悴,可以設(shè)置帶有hash值的名稱们拙,方便文件修改時文件名改變,避免瀏覽器緩存帶來的不必要麻煩阁吝。
  • (4) path用來存放打包后文件的輸出目錄
  • (5) publicPath用來配置發(fā)布到線上的URL前綴砚婆,默認(rèn)值為' ',即使用相對路徑突勇。
    當(dāng)需要構(gòu)建出的資源文件上傳到CDN服務(wù)商時候装盯,為了加快頁面的打開速度,需要如下配置:
filename: '[name]_[chunkhash:5].js',
publicPath: 'https://cdn.example.com/assets/'

此時發(fā)布到線上的HTML在引入JavaScript文件時甲馋,就需要如下配置項(xiàng):
<script src="https://cdn.example.com/assets/a_12345.js"></script>

3埂奈、loader基礎(chǔ)應(yīng)用
// webpack.config.client.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    entry: {
        app: path.join(__dirname, '../client/app.js'),
    },
    output: {
        filename: '[name].[hash:5].js',
        path: path.join(__dirname, '../dist'),
        publicPath: '',
    },
    module: {
        rules: [
            {
                test: /.jsx$/,
                loader: 'babel-loader',     // (1)
            },
            {
                test: /.js$/,
                loader: 'babel-loader', 
                exclude: [          // (2)
                    path.join(__dirname, '../node_modules')
                ]
            }
        ]
    },
    plugins: [
        new HtmlWebpackPlugin()     // (3)
    ]
}
  • (1) 當(dāng)頁面需要引入一些非原生JavaScript的模塊(module)時,需要在module中設(shè)置各種loader進(jìn)行操作定躏,方便瀏覽器識別账磺,如對react的.jsx文件進(jìn)行支持,需要引入babel-loader
  • (2) 不需要對node_modules中的js進(jìn)行編譯共屈,則需要用exclude進(jìn)行排除绑谣,同時需要設(shè)置在項(xiàng)目根目錄設(shè)置.babelrc,如下拗引,對es6和react進(jìn)行編譯和寬松的支持。
// .babelrc
{
    "presets": [
        ["es2015", {"loose": true}],
        "react"
    ]
}
  • (3) webpack 通過plugins安裝插件類實(shí)現(xiàn)一些功能幌衣,常用的如矾削,HtmlWebpackPlugin,它依據(jù)一個簡單的模板豁护,生成最終的html文件哼凯,這個文件中自動引用了打包后的JS文件。每次編譯都在文件名中插入一個不同的帶有哈希值的JS文件楚里。

三断部、服務(wù)端渲染基礎(chǔ)配置

1、為什么需要服務(wù)端渲染(SSR:Server-Side-Render)
  • (1) SEO不友好班缎,單頁應(yīng)用在瀏覽器端渲染HTML頁面蝴光,搜索引擎不會去執(zhí)行JS代碼她渴,不便于搜索引擎的錄入
  • (2) 用戶體驗(yàn)不好,頁面渲染每次要等待JS加載完成之后才出現(xiàn)蔑祟,首次請求等待時間較長趁耗,會出現(xiàn)一個短暫的白屏。
2疆虚、React中怎么使用服務(wù)端渲染

react-dom 是React專門為web端開發(fā)的渲染工具苛败,可以在客戶端用react-dom的render方法渲染組件,而服務(wù)端径簿,react-dom/server 提供我們將react組件渲染成HTML的方法罢屈。

// 入口文件app.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App.jsx';

ReactDOM.hydrate(<App />, document.getElementById('root'));  // (1)
  • (1) hydrate 描述的是 ReactDOM 復(fù)用 ReactDOMServer 服務(wù)端渲染的內(nèi)容時盡可能保留結(jié)構(gòu),并補(bǔ)充事件綁定等 Client 特有內(nèi)容的過程篇亭。

服務(wù)端渲染時使用Nodejs儡遮,Nodejs是沒有document或者window對象的,需要創(chuàng)建一個特定的js文件(server-entry.js)去導(dǎo)出要在服務(wù)端渲染的內(nèi)容暗赶,如下:

// server-entry.js
import React from 'react';
import App from 'App.jsx';

export  default <App />;

因?yàn)檫@個server-entry.js需要在Nodejs中執(zhí)行鄙币,所以導(dǎo)出的jsx格式是需要webpack打包編譯的,所以需要再建一個webpack配置文件蹂随,如下:

// webpack.config.server.js
const path = require('path');

module.exports = {
    target: 'node',  //  (1)
    entry: {
        app: path.join(__dirname, '../client/server-entry.js'),
    },
    output: {
        filename: 'server-entry.js',                 // (2) 
        path: path.join(__dirname, '../dist'),
        publicPath: '/public',
        libraryTarget: 'commonjs2',    // (3)
    },
    module: {
        rules: [
            {
                test: /.jsx$/,
                loader: 'babel-loader',
            },
            {
                test: /.js$/,
                loader: 'babel-loader', 
                exclude: [
                    path.join(__dirname, '../node_modules')
                ]
            }
        ]
    },
          //  (4)
}

與webpack.config.client.js基本相同十嘿,個別地方做了一些特定的改動:

  • (1) target: 打包出來的內(nèi)容執(zhí)行環(huán)境設(shè)置,這里因?yàn)樾枰虬蠓诺椒?wù)端Nodejs中執(zhí)行,所以選擇node岳锁。還可以是web绩衷,就是在瀏覽器端執(zhí)行。

  • (2) 因?yàn)榉?wù)端不會有緩存一說激率,所以不需要對壓縮后的文件名進(jìn)行hash處理咳燕,所以打包后的文件名不變。

  • (3) libraryTarget:設(shè)置打包出來的js模塊方案乒躺,此處使用commonjs2規(guī)范招盲,即最新的commonjs最新的模塊加載方案,適用于Nodejs端嘉冒。還可以設(shè)置amd曹货,cmd等。

  • (4) 同時刪除了HtmlWebpackPlugin讳推,因?yàn)椴恍枰?wù)端渲染時顶籽,不需要再生成一個Html文件了。

此時银觅,需要對package.json中的scripts做一些修改:

  "scripts": {
    "build:client": "webpack --config build/webpack.config.client.js",
    "build:server": "webpack --config build/webpack.config.server.js",
    "clear": "rimraf dist",                                            // (1)
    "build": "npm run clear && npm run build:client && npm run build:server",// (2)
    "start": "node server/server.js"
  },
  • (1) rimraf是nodejs一個專門用來刪除文件夾的包礼饱,這里是在每次打包壓縮新的dist文件之前把之前的dist文件刪除

  • (2) build命令把build:client和build:server兩個命令都去執(zhí)行一遍

在client文件夾中新增一個t模板emplate.html文件,同時在webpack.config.client,js中添加如下配置,將模板template.html合到導(dǎo)出后的index.html中镊绪。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <div id="root"><app></app></div>
</body>
</html>
plugins: [
        new HtmlWebpackPlugin({
            template: path.join(__dirname, '../client/template.html')
        })
    ]

當(dāng)打包完成后匀伏,在服務(wù)端就可以開始渲染server-entry.js了:
使用Node.js的Express框架,來做服務(wù)端的渲染工作:

const express = require('express');
const ReactSSR = require('react-dom/server');
const serverEntry = require('../dist/server-entry.js').default;  (1)
const fs = require('fs');
const path = require('path');

const app = express();
const template = fs.readFileSync(path.join(__dirname, '../dist/index.html'), 'utf8');

app.use('/public', express.static(path.join(__dirname, '../dist')));  // (2)

app.get('*', function(req, res) {
    const appString = ReactSSR.renderToString(serverEntry); // (3)
    res.send(template.replace('<app></app>', appString));  // (4)
});

app.listen(3333, function(){
    console.log('server is running! Visit http://localhost:3333');
})
  • (1) const serverEntry = require('../dist/server-entry.js').default;這里結(jié)尾之所以要加上.default镰吆,是因?yàn)橛胷equire引入的模塊是整個內(nèi)容帘撰,和import不同,當(dāng)打印沒有加.default的serverEntry時万皿,可以發(fā)現(xiàn)如下:
{ __esModule: true,
  default:
   { '$$typeof': Symbol(react.element),
     type: [Function: App],
     key: null,
     ref: null,
     props: {},
     _owner: null,
     _store: {} } }

default里才是需要的內(nèi)容摧找,所以需要找到default里。

  • (4) 用來區(qū)分靜態(tài)內(nèi)容還是服務(wù)端代碼牢硅。

  • (3) 在瀏覽器端最終產(chǎn)生的是DOM元素蹬耘,而在服務(wù)器端,最終產(chǎn)生的是字符串减余,因?yàn)榉祷亟o瀏覽器的是HTML字符串综苔,所以服務(wù)器渲染不需要指定容器元素,只有一個返回字符串函數(shù)renderToString位岔。

  • (4) 為了和瀏覽器端一致如筛,把返回的字符串嵌入到id="root"的與元素里,即用appString代替index.js中id="root"里的'<app></app>'抒抬。
    服務(wù)端渲染后的頁面代碼杨刨,如下圖:


    服務(wù)端渲染

四、webpack-dev-server

Use webpack with a development server that provides live reloading. This should be used for development only.

根據(jù)官方的說明擦剑,可以知道妖胀,webpack-dev-server是一個用在開發(fā)環(huán)境中的一個服務(wù),可以實(shí)時更新惠勒。

1赚抡、安裝

安裝webpack-dev-server和cross-env包
npm install webpack-dev-server --save-dev
npm i cross-env --save-dev

2、使用

首先纠屋,在package.json中的scripts中增加一個"dev:client",如下:
"dev:client": "cross-env NODE_ENV=development webpack-dev-server --config build/webpack.config.client.js",
因?yàn)閣ebpack-dev-server用于開發(fā)環(huán)境涂臣,所以在webpack配置文件中需要加以區(qū)分是開發(fā)環(huán)境還是生產(chǎn)環(huán)境,在webpack.config.client,js添加如下配置:
將之前的module.exports={}的寫法改成const config={};然后給一個if條件判斷if(diDev) {}巾遭;最后再module.exports = config;

const  isDev = process.env.NPDE_ENV === 'development'; // (1)

const config = {
    entry: {
        app: path.join(__dirname, '../client/app.js'),
    },
    output: {
        filename: '[name].[hash:5].js',
        path: path.join(__dirname, '../dist'),
        publicPath: '/public',
    },
    module: {
        rules: [
            {
                test: /.jsx$/,
                loader: 'babel-loader',
            },
            {
                test: /.js$/,
                loader: 'babel-loader', 
                exclude: [
                    path.join(__dirname, '../node_modules')
                ]
            }
        ]
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: path.join(__dirname, '../client/template.html')
        })
    ]
}

// 只有在開發(fā)中才使用的
if (isDev) {
    config.entry = {
        app:[
            'react-hot-loader/patch',
            path.join(__dirname, '../client/app.js')
        ]
    }
    config.devServer = {  // (2)
        host: '0.0.0.0',  // (3)
        port: '8088',
        contentBase: path.join(__dirname, '../dist'),// (4)
        hot: true,  // (5)
        overlay: {   // (6)
            errors: true
        },
        publicPath: '/public/', // (7)
        historyApiFallback: {  // (8)
            index: '/public/index.html'
        },
    }
}

module.exports = config;
  • (1) 定義一個開發(fā)環(huán)境給變量isDev
  • (2) 設(shè)置一個devServer
  • (3) 如果想外部其他人也能訪問肉康,host就設(shè)為0.0.0.0,默認(rèn)為localhost
  • (4) 告訴服務(wù)器從哪里提供內(nèi)容灼舍。只有在你想要提供靜態(tài)文件時才需要。因?yàn)殪o態(tài)文件是放在output中的path中配置涨薪,所以這里和output中的path路徑一致就行骑素。
  • (5) 啟用 webpack 的模塊熱替換(hot-module-replacement)特性
  • (6) 如果webpack在編譯過程中出錯,則在網(wǎng)頁顯示信息
  • (7) 此路徑下的打包文件可在瀏覽器中訪問刚夺,確保 publicPath 總是以斜杠(/)開頭和結(jié)尾献丑。
    假設(shè)服務(wù)器運(yùn)行在 http://localhost:8080 并且 output.filename 被設(shè)置為 bundle.js末捣。默認(rèn) publicPath 是 "/public/",所以你的包(bundle)可以通過 http://localhost:8080/public/bundle.js 訪問创橄。
  • (8) 任意的 404 響應(yīng)被替代為設(shè)置的index

注意:當(dāng)啟動webpack-dev-server時箩做,最好是刪除之前已經(jīng)打包的文件,如dist文件夾妥畏,因?yàn)楫?dāng)啟動devServer時候邦邦,默認(rèn)在硬盤中檢測打包項(xiàng)目目錄,如果有醉蚁,則直接訪問這個dist文件下js文件的燃辖,但這個dist下的js文件和html引入的js文件名不一致,所以會造成404錯誤网棍。

五黔龟、hot-module-replacement

可以在開發(fā)環(huán)境中,修改代碼后滥玷,在頁面中無刷新的看到代碼變化后的效果氏身,極大的提高了開發(fā)效率。

1惑畴、安裝

安裝react-hot-loader插件蛋欣,它提供了React的hot-module-replacement的功能的工具。
npm install react-hot-loader --save-dev

2桨菜、配置

配置.babelrc文件:

"plugins": [
        "react-hot-loader/babel"
    ]

修改webpack.config.client.js文件豁状,添加HotModuleReplacementPlugin:

const webpack = require('webpack');  // 引入webpack
if (isDev) {
    config.entry = {
        app:[
            'react-hot-loader/patch',
            path.join(__dirname, '../client/app.js')
        ]
    }
// 在開發(fā)環(huán)境中加入一個HotModuleReplacementPlugin
   config.plugins.push(new webpack.HotModuleReplacementPlugin())
}

修改app.js:

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App.jsx';
import { AppContainer } from 'react-hot-loader';

const render = Component => {
    ReactDOM.hydrate(
        <AppContainer>
            <Component />
        </AppContainer>,
        document.getElementById('root')
    )
}

render(App);

if (module.hot) {
    module.hot.accept('./App.jsx', () => {  // (1)
        const NextApp = require('./App.jsx').default;
        render(NextApp)
    })
}
  • (1) accept方法給定依賴模塊的更新,并觸發(fā)一個 回調(diào)函數(shù) 來對這些更新做出響應(yīng)倒得。

六泻红、規(guī)范代碼

常用的規(guī)范代碼主要從代碼規(guī)范和代碼書寫格式等方面進(jìn)行規(guī)范,如eslint規(guī)范代碼風(fēng)格霞掺,editorconfig規(guī)范代碼文本格式等谊路。

1、為什么要規(guī)范代碼
  • (1) 規(guī)范代碼有利于團(tuán)隊協(xié)作
  • (2) 純手工規(guī)范費(fèi)時費(fèi)力菩彬,而且不能保證準(zhǔn)確性
  • (3) 能配合編輯器(如vscode)自動提醒錯誤缠劝,提高開發(fā)效率
2、Eslint

安裝:
npm install eslint --save-dev
npm install babel-eslint --save-dev
npm install eslint-config --save-dev
npm install eslint-config-airbnb --save-dev
npm install eslint-config-standard --save-dev
npm install eslint-loader --save-dev
npm install eslint-plugin-import --save-dev
npm install eslint-plugin-jsx-a11y --save-dev
npm install eslint-plugin-node --save-dev
npm install eslint-plugin-promise --save-dev
npm install eslint-plugin-react --save-dev
npm install eslint-plugin-standard --save-dev
配置:
在項(xiàng)目根目錄創(chuàng)建一個.eslintrc文件骗灶,然后添加配置項(xiàng):

{
    "extends": "standard"
}

直接繼承標(biāo)準(zhǔn)的eslint標(biāo)準(zhǔn)就行

另外惨恭,在client文件目錄下再建一個.eslintrc文件,因?yàn)閏lient文件中主要代碼是用jsx語法來書寫的耙旦,很多寫法和nodejs端是不一樣的脱羡,需要單獨(dú)配置,如下:

{
    "parser": "babel-eslint",
    "env": {
        "browser": true,
        "es6": true,
        "node": true
    },
    "parserOption": {
        "ecmaVersion": 6,
        "sourceType": "module"       // (1)
    },
    "extends":"airbnb",
    "rules": {
        "semi": [0]
    }
}

主要繼承了美國Airbnb公司的React eslint規(guī)則。

  • (1) 定義文件是 ECMAScript 模塊

如果希望代碼在每次編譯之前使用這些eslint規(guī)則去檢查一遍锉罐,可以在webpack.client.config.js中設(shè)置:
在module中增加一條rules:

{
    enforce: 'pre',  // (1)
    test: /.(js|jsx)$/,
    loader: 'exlint-loader',
    exclude: [
                    path.join(__dirname, '../node_modules')
     ]
}
  • (1) 在代碼編譯之前帆竹,執(zhí)行eslint-loader,如果出現(xiàn)錯誤脓规,停止編譯栽连,報出錯誤。
3侨舆、editorconfig

在根目錄創(chuàng)建.editorconfig文件
注意:如果是在vscode編輯器中使用.editorconfig秒紧,需要安裝一個插件 EditorConfig for VS Code,才能生效态罪。

root = true

[*]

charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
insert_final_newline = true

完整代碼:請移步github

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末噩茄,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子复颈,更是在濱河造成了極大的恐慌绩聘,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件耗啦,死亡現(xiàn)場離奇詭異凿菩,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)帜讲,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進(jìn)店門衅谷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人似将,你說我怎么就攤上這事获黔。” “怎么了在验?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵玷氏,是天一觀的道長。 經(jīng)常有香客問我腋舌,道長盏触,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任块饺,我火速辦了婚禮赞辩,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘授艰。我一直安慰自己辨嗽,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布淮腾。 她就那樣靜靜地躺著召庞,像睡著了一般岛心。 火紅的嫁衣襯著肌膚如雪来破。 梳的紋絲不亂的頭發(fā)上篮灼,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天,我揣著相機(jī)與錄音徘禁,去河邊找鬼诅诱。 笑死,一個胖子當(dāng)著我的面吹牛送朱,可吹牛的內(nèi)容都是我干的娘荡。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼驶沼,長吁一口氣:“原來是場噩夢啊……” “哼炮沐!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起回怜,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤大年,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后玉雾,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體翔试,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年复旬,在試婚紗的時候發(fā)現(xiàn)自己被綠了垦缅。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡驹碍,死狀恐怖壁涎,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情志秃,我是刑警寧澤怔球,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站洽损,受9級特大地震影響庞溜,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜碑定,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一流码、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧延刘,春花似錦漫试、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽外构。三九已至,卻和暖如春播掷,著一層夾襖步出監(jiān)牢的瞬間审编,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工歧匈, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留垒酬,地道東北人。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓件炉,卻偏偏與公主長得像勘究,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子斟冕,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評論 2 344

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