使用Webpack實現(xiàn)前端構(gòu)建工具

使用Webpack實現(xiàn)前端構(gòu)建工具

webpack簡單介紹

webpack 是一個現(xiàn)代 JavaScript 應(yīng)用程序的靜態(tài)模塊打包器。

核心概念:入口(entry)瓢宦、輸出(output)蟆盹、loader猜年、插件(plugins)

主要功能點(要解決的問題)

  • 日常開發(fā)時替饿,代碼編譯
  • 團(tuán)隊協(xié)作開發(fā)赞别,盡量減少開發(fā)時的代碼沖突
  • 前端資源緩存控制滤馍,減少流量花費

一岛琼、日常開發(fā)時,代碼編譯

  • ES6編譯
  • vue編譯
  • Less編譯
1.正常配置

初始化項目:

npm init

npm i webpack webpack-cli -D

創(chuàng)建webpack.config.js文件:

const path = require('path');

module.export = {
    entry: './app.js',
    output: {
        path: './dist',
        publicPath: '/dist',
        filename: 'bundle.js'
    },
    module: {
        rules: [{
            test: /\.css$/,
            use: [{
                loader: 'css-loader'
            }]
        }]
    },
    plugins: []
};
2.ES6編譯

1)webpack 已支持ES6 不需要單獨配置babel

2)項目中代碼需要做ES5的兼容

babel主要是把ES6規(guī)范的代碼編譯為ES5巢株,ES5規(guī)范中的一些方法低版本瀏覽器還沒有做兼容槐瑞,所以需要我們在項目中使用polyfill庫來兼容ES5。

項目中安裝:

npm i @babel/polyfill -P

項目中引用:

import "@babel/polyfill";
3.Vue編譯

1)配置vue-loader

vue-loader主要用作編譯*.vue文件

VueLoaderPlugin 用作把你定義的其他規(guī)則 也應(yīng)用到*.vue文件當(dāng)中

vue-loader 安裝:

npm i vue vue-loader -D
npm i vue-template-compile -D
const path = require('path');

const VueLoaderPlugin = require('vue-loader/lib/plugin'); 

module.export = {
    entry: './app.js',
    output: {
        path: './dist',
        publicPath: '/dist',
        filename: 'bundle.js'
    },
    module: {
        rules: [{ //編譯vue文件
            test: /\.vue$/,
            loader: 'vue-loader'
        },{
            test: /\.css$/,
            use: [{
                loader: 'css-loader'
            }]
        }]
    },
    plugins: [
        new VueLoaderPlugin()
    ]
};

2)配置vue-style-loader

vue-style-loader主要是用來處理*.vue文件中<style>標(biāo)簽

vue-style-loader 安裝:

npm i vue-style-loader -D
const path = require('path');

const VueLoaderPlugin = require('vue-loader/lib/plugin'); 

module.export = {
    entry: './app.js',
    output: {
        path: './dist',
        publicPath: '/dist',
        filename: 'bundle.js'
    },
    module: {
        rules: [{ //編譯vue文件
            test: /\.vue$/,
            loader: 'vue-loader'
        },{
            test: /\.css$/,
            use: [{
                loader: 'vue-style-loader'
            }, {
                loader: 'css-loader'
            }]
        }]
    },
    plugins: [
        new VueLoaderPlugin()
    ]
};
4.Less編譯

Less 安裝:

npm i less-loader less -D
const path = require('path');

const VueLoaderPlugin = require('vue-loader/lib/plugin'); 

module.export = {
    entry: './app.js',
    output: {
        path: './dist',
        publicPath: '/dist',
        filename: 'bundle.js'
    },
    module: {
        rules: [{ //編譯vue文件
            test: /\.vue$/,
            loader: 'vue-loader'
        }, {
            test: /\.css$/,
            use: [{
                loader: 'vue-style-loader'
            }, {
                loader: 'css-loader'
            }, {
                loader: 'less-loader'
            }]
        }, {
            test: /\.less$/,
            use: [{
                loader: 'css-loader'
            }, {
                loader: 'less-loader'
            }]
        }]
    },
    plugins: [
        new VueLoaderPlugin()
    ]
};

可以合并一下 css與less的處理

{
    test: /\.(c|le)ss$/,
    use: [{
        loader: 'vue-style-loader'
    }, {
        loader: 'css-loader'
    }, {
        loader: 'less-loader'
    }]
}

二阁苞、團(tuán)隊協(xié)作開發(fā)困檩,盡量減少開發(fā)時的代碼沖突

  • 目錄結(jié)構(gòu)安排
  • Webpack對目錄結(jié)構(gòu)的處理
1.目錄結(jié)構(gòu)安排

團(tuán)隊開發(fā)過程中祠挫,要盡量保證各自的功能模塊獨立纸泡。在保持各自開發(fā)獨立的同時董朝,還要不時提取一些常用到的方法作為工具類,提取一些經(jīng)常用到的組件作為公共組件翁巍,提取一些常用的樣式作為公共樣式糟趾。這樣做可以減少代碼沖突慌植,并且使我們的開發(fā)速度越來越快。

要做到這些义郑,我們需要合理的配置我們的目錄結(jié)構(gòu)蝶柿。

  • 根目錄
--- dist //編譯后文件目錄,上傳到服務(wù)器
    src //源文件非驮,正常開發(fā)用的目錄
    package.json //各種包交汤、編譯命令配置等
    webpack.config.js //前端構(gòu)建工具

源文件結(jié)構(gòu)如下:

src  ---common //用來放工具類、公共組件和公共樣式
        pages //平時開發(fā)業(yè)務(wù)的目錄院尔,跟每個頁面相對應(yīng)
        static //用來放一些不需打包的靜態(tài)資源蜻展,如網(wǎng)站的圖片喉誊、第三方不支持import引用的庫
      
        common---css
              ---js
              ---fonts //字體圖標(biāo)
              ---components
              ---index.js //入口文件
              ---index.css
        pages---index---index.js //入口文件
                     ---index.css
                     ---css
                     ---js
                     ---images
                     ---components //可以用來放vue組件
        static---css
               ---js
               ---images

編譯后的文件結(jié)構(gòu)如下:

dist ---css---pages---somePage.css
            |      ---index.css    
            |-common.css
        fonts---materialIcon.woff2
        images---pages---somePage---logo.png
        js---pages---somePage.js
           |      ---index.js    
           |-common.js
        static---css---echarts.css
              ---js---echarts.js
              ---images---favicon.ico

對應(yīng)的webpack.config.js修改:

  1. 多入口處理

定義好入口文件 -> 遍歷所有文件 -> 找出入口文件路徑

定義好入口文件:

./src/common/index.js
./src/pages/*/index.js

遍歷所有文件:

npm i glob -D //glob遍歷文件工具
const glob = require('glob'); //遍歷文件
let entryFile = {};

const files = [
  ...glob.sync(path.join(__dirname, './src/common/index.js')), // common入口
  ...glob.sync(path.join(__dirname, './src/pages/*/index.js')), // pages 入口
];

找出入口文件路徑:

files.forEach(val => {
    let filePath = val.split('/src/')[1];
    let folder = filePath.split('/index.js')[0];
    entryFile[folder] = val;
});
/*
    entryFile
    {
        'common': '/work/project/src/common/index.js',
        'pages/index': '/work/project/src/pages/index/index.js',
    }
    
*/

  1. 輸出處理
{
    filename: 'js/[name].js',// js/pages/index.js pages/index為入口文件的文件名
    publicPath: __dirname + '/dist',//為項目中的所有資源指定一個基礎(chǔ)路徑邀摆,部分插件會用到
    path: __dirname + '/dist'
}

通過入口和輸出的處理,能夠完成js文件的編譯和打包伍茄,但是css文件會被打包到j(luò)s當(dāng)中栋盹,這樣不利于日常開發(fā)調(diào)試,頁面訪問加載速度也會被減慢敷矫,圖片例获、字體文件還沒有處理,所以要針對css和靜態(tài)文件需要做特殊處理曹仗。

  1. 靜態(tài)文件處理

css文件處理:

npm i mini-css-extract-plugin -D  //mini-css-extract-plugin 用作處理css文件榨汤,將css打包到css文件當(dāng)中
const path = require('path');

const VueLoaderPlugin = require('vue-loader/lib/plugin'); 

const glob = require('glob'); //遍歷文件

const MiniCssExtractPlugin = require('mini-css-extract-plugin'); //專門處理css,將css打包到一個css文件中

let entryFile = {};

const files = [
  ...glob.sync(path.join(__dirname, './src/common/index.js')), // common入口
  ...glob.sync(path.join(__dirname, './src/pages/*/index.js')), // pages 入口
];

files.forEach(val => {
    let filePath = val.split('/src/')[1];
    let folder = filePath.split('/index.js')[0];
    entryFile[folder] = val;
});

module.export = {
    entry: entryFile,
    output: {
        filename: 'js/[name].js',// js/pages/index.js pages/index為入口文件的文件名
        publicPath: __dirname + '/dist',//為項目中的所有資源指定一個基礎(chǔ)路徑,部分插件會用到
        path: __dirname + '/dist'
    },
    module: {
        rules: [{ //編譯vue文件
            test: /\.vue$/,
            loader: 'vue-loader'
        }, {
            test: /\.(c|le)ss$/,
            use: [{
                loader: 'vue-style-loader'
            }, {
                loader: MiniCssExtractPlugin.loader,
            }, {
                loader: 'css-loader'
            }, {
                loader: 'less-loader'
            }]
        }]
    },
    plugins: [
        new VueLoaderPlugin(),
        new MiniCssExtractPlugin({ 
          filename:'css/[name].css',
          chunkFilename: 'css/[id].css',
        })
    ]
};

靜態(tài)文件處理:

npm i copy-webpack-plugin -D //用來從src目錄copy文件到dist目錄 處理不需要編譯的文件 直接copy過去
npm i file-loader -D //處理被引用的靜態(tài)文件如background-image: url(./images/logo.png) 將資源copy到指定目錄并修改對應(yīng)url()中的地址
const path = require('path');

const VueLoaderPlugin = require('vue-loader/lib/plugin'); 

const glob = require('glob'); //遍歷文件

const MiniCssExtractPlugin = require('mini-css-extract-plugin'); //專門處理css,將css打包到一個css文件中

const CopyWebpackPlugin = require('copy-webpack-plugin'); //copy一些靜態(tài)文件用

let entryFile = {};

const files = [
  ...glob.sync(path.join(__dirname, './src/common/index.js')), // common入口
  ...glob.sync(path.join(__dirname, './src/pages/*/index.js')), // pages 入口
];

files.forEach(val => {
    let filePath = val.split('/src/')[1];
    let folder = filePath.split('/index.js')[0];
    entryFile[folder] = val;
});

module.export = {
    entry: entryFile,
    output: {
        filename: 'js/[name].js',// js/pages/index.js pages/index為入口文件的文件名
        publicPath: __dirname + '/dist',//為項目中的所有資源指定一個基礎(chǔ)路徑怎茫,部分插件會用到
        path: __dirname + '/dist'
    },
    module: {
        rules: [{ //編譯vue文件
            test: /\.vue$/,
            loader: 'vue-loader'
        }, {
            test: /\.(c|le)ss$/,
            use: [{
                loader: 'vue-style-loader'
            }, {
                loader: MiniCssExtractPlugin.loader,
            }, {
                loader: 'css-loader'
            }, {
                loader: 'less-loader'
            }]
        }, {
            test: /\.(png|jpg|gif)$/,
            use: [{
                loader: 'file-loader',
                options: {
                    name: (path) => {///work/Asoxer/asoxer-server/static/v1/src/pages/index/images/logo.png
                        return path.replace('/images', '').split('/src/')[1].split('.')[0] + '.[ext]';
                    },
                    outputPath: 'images/',
                    publicPath: '/v1/dist/images/'
                  }
            }]
        }, {
            test: /\.(woff2)$/,
            use: [{
                loader: 'file-loader',
                options: {
                    name: (path) => {///work/Asoxer/asoxer-server/static/v1/src/common/fonts/xxx.woff2
                        return path.replace('/common/fonts', '').split('/src/')[1].split('.')[0] + '.[ext]';
                    },
                    outputPath: 'fonts/',
                    publicPath: '/v1/dist/fonts/'
                  }
            }]
        }]
    },
    plugins: [
        new VueLoaderPlugin(),
        new MiniCssExtractPlugin({ 
          filename:'css/[name].css',
          chunkFilename: 'css/[id].css',
        }),
        new CopyWebpackPlugin([
            {
                from: path.join(__dirname, './src/static'),
                to: path.join(__dirname, './dist/static') + '/[name].[ext]'
            }
        ])
    ]
};

通過以上配置收壕,可以實現(xiàn)一個基本的前端構(gòu)建工具。

團(tuán)隊開發(fā)過程中轨蛤,每個人接到不同的需求后蜜宪,可以在pages目錄新建一個文件夾作為本次業(yè)務(wù)的開發(fā)目錄,并將相關(guān)資源放到此目錄祥山,保證了業(yè)務(wù)的獨立性圃验,不會與其他人發(fā)生沖突。

因為dist中js與css文件的文件名與pages中的目錄名對應(yīng)缝呕,調(diào)試js與css也很便捷澳窑。

通過不斷的擴(kuò)充common目錄斧散,可以不斷的提高開發(fā)效率。

沒有依賴關(guān)系的靜態(tài)文件(html文件中引用的不需要編譯的文件)也被妥善處理了摊聋。


三颅湘、前端資源緩存控制,減少流量花費

瀏覽器的對靜態(tài)文件緩存來能提高頁面的加載速度栗精,但是這樣闯参,我們更新的代碼,不會第一時間被獲取到悲立,這樣會產(chǎn)生一些BUG鹿寨。

我們需要對資源進(jìn)行版本控制,發(fā)生變化的文件薪夕,修改版本后脚草,瀏覽器會重新進(jìn)行緩存。

  • hash配置

webpack有三種緩存處理策略[hash][chunkhash][contenthash]

區(qū)別:

[hash] 當(dāng)前文件發(fā)生變化原献,所有文件版本都發(fā)生變化馏慨。

[chunkhash] 當(dāng)前文件發(fā)生變化,引用該文件的文件版本也會發(fā)生變化

[contenthash] 當(dāng)前文件發(fā)生變化姑隅,只該文件版本發(fā)生變化

哈希值配置方式:

 output: {
    filename: 'js/[name].[contenthash].js',// js/pages/index.js pages/index為入口文件的文件名
    publicPath: __dirname + '/dist',//為項目中的所有資源指定一個基礎(chǔ)路徑写隶,部分插件會用到
    path: __dirname + '/dist'
}

完整配置

const path = require('path');

const VueLoaderPlugin = require('vue-loader/lib/plugin'); 

const glob = require('glob'); //遍歷文件

const MiniCssExtractPlugin = require('mini-css-extract-plugin'); //專門處理css,將css打包到一個css文件中

const CopyWebpackPlugin = require('copy-webpack-plugin'); //copy一些靜態(tài)文件用

let entryFile = {};

const files = [
  ...glob.sync(path.join(__dirname, './src/common/index.js')), // common入口
  ...glob.sync(path.join(__dirname, './src/pages/*/index.js')), // pages 入口
];

files.forEach(val => {
    let filePath = val.split('/src/')[1];
    let folder = filePath.split('/index.js')[0];
    entryFile[folder] = val;
});

module.export = {
    entry: entryFile,
    output: {
        filename: 'js/[name].[contenthash].js',// js/pages/index.js pages/index為入口文件的文件名
        publicPath: __dirname + '/dist',//為項目中的所有資源指定一個基礎(chǔ)路徑,部分插件會用到
        path: __dirname + '/dist'
    },
    module: {
        rules: [{ //編譯vue文件
            test: /\.vue$/,
            loader: 'vue-loader'
        }, {
            test: /\.(c|le)ss$/,
            use: [{
                loader: 'vue-style-loader'
            }, {
                loader: MiniCssExtractPlugin.loader,
            }, {
                loader: 'css-loader'
            }, {
                loader: 'less-loader'
            }]
        }, {
            test: /\.(png|jpg|gif)$/,
            use: [{
                loader: 'file-loader',
                options: {
                    name: (path) => {///work/Asoxer/asoxer-server/static/v1/src/pages/index/images/logo.png
                        return path.replace('/images', '').split('/src/')[1].split('.')[0] + '.[hash:8].[ext]';
                    },
                    outputPath: 'images/',
                    publicPath: '/v1/dist/images/'
                  }
            }]
        }, {
            test: /\.(woff2)$/,
            use: [{
                loader: 'file-loader',
                options: {
                    name: (path) => {///work/Asoxer/asoxer-server/static/v1/src/common/fonts/xxx.woff2
                        return path.replace('/common/fonts', '').split('/src/')[1].split('.')[0] + '.[hash:8].[ext]';
                    },
                    outputPath: 'fonts/',
                    publicPath: '/v1/dist/fonts/'
                  }
            }]
        }]
    },
    plugins: [
        new VueLoaderPlugin(),
        new MiniCssExtractPlugin({ 
          filename:'css/[name].[contenthash].css',
          chunkFilename: 'css/[id].[contenthash].css',
        }),
        new CopyWebpackPlugin([
            {
                from: path.join(__dirname, './src/static'),
                to: path.join(__dirname, './dist/static') + '/[name].[hash:8].[ext]'
            }
        ])
    ]
};
  • hash值變化記錄

我們?yōu)槲募恿斯V抵蠼惭觯残枰薷膶?yīng)頁面中資源引用慕趴。所以,在文件版本發(fā)生變化后鄙陡,我們需要記錄下文件的變化。

處理版本的插件

const fs = require('fs');

function readFile(filePath) {
    return new Promise((resolve, reject) => {
        if(fs.existsSync(filePath)) {
            fs.readFile(filePath, 'utf8', function(err, data) {
                if(err) {
                    reject(err);
                }else {
                    resolve(data);
                }
            });
        }else {
            resolve();
        }
    });
}

class HandleHashPlugin {
    constructor(options) {
        this.options = options;
    }
    apply(compiler) {
        compiler.hooks.done.tap('HandleHashPlugin', (arg) => {
            let compiledFile = arg.toJson().assets;
            let fileData = {};
            /*
                {
                    'images/pages/index/logo.png': {
                        last: '408685cc',
                        cur: '91d265fb'
                    }
                }
            */ 
            const filePath = path.join(__dirname, './filePath.json');
            

            readFile(filePath).then((fileContent) => {
                fileContent = fileContent ? JSON.parse(fileContent) : {};
                compiledFile.forEach((val) => {
                    let filename = val.name;
                    let hash = filename.match(/\.[0-9|a-z]*\.[0-9|a-z]*$/)[0].split('.')[1];
                    filename = filename.replace(`.${hash}`, '');
                    fileData[filename] = {
                        last: fileContent[filename] ? fileContent[filename]['cur'] || '' : '',
                        cur: hash
                    }
                });
                if(fs.existsSync(filePath)) {
                    fs.unlinkSync(filePath);
                }
                fs.writeFile(filePath, JSON.stringify(fileData), {
                    flag: 'a'
                }, function(err) {
                    if(err) {
                        console.error(err);
                    }else {
                        console.log('寫入成功耙册!');
                    }
                });
            }).catch((err) => {
                console.log(err);
            });
        })
    }
}

插件使用:

const path = require('path');

const VueLoaderPlugin = require('vue-loader/lib/plugin'); 

const glob = require('glob'); //遍歷文件

const MiniCssExtractPlugin = require('mini-css-extract-plugin'); //專門處理css,將css打包到一個css文件中

const CopyWebpackPlugin = require('copy-webpack-plugin'); //copy一些靜態(tài)文件用

let entryFile = {};

const files = [
  ...glob.sync(path.join(__dirname, './src/common/index.js')), // common入口
  ...glob.sync(path.join(__dirname, './src/pages/*/index.js')), // pages 入口
];

files.forEach(val => {
    let filePath = val.split('/src/')[1];
    let folder = filePath.split('/index.js')[0];
    entryFile[folder] = val;
});

module.export = {
    entry: entryFile,
    output: {
        filename: 'js/[name].[contenthash].js',// js/pages/index.js pages/index為入口文件的文件名
        publicPath: __dirname + '/dist',//為項目中的所有資源指定一個基礎(chǔ)路徑毫捣,部分插件會用到
        path: __dirname + '/dist'
    },
    module: {
        rules: [{ //編譯vue文件
            test: /\.vue$/,
            loader: 'vue-loader'
        }, {
            test: /\.(c|le)ss$/,
            use: [{
                loader: 'vue-style-loader'
            }, {
                loader: MiniCssExtractPlugin.loader,
            }, {
                loader: 'css-loader'
            }, {
                loader: 'less-loader'
            }]
        }, {
            test: /\.(png|jpg|gif)$/,
            use: [{
                loader: 'file-loader',
                options: {
                    name: (path) => {///work/Asoxer/asoxer-server/static/v1/src/pages/index/images/logo.png
                        return path.replace('/images', '').split('/src/')[1].split('.')[0] + '.[hash:8].[ext]';
                    },
                    outputPath: 'images/',
                    publicPath: '/v1/dist/images/'
                  }
            }]
        }, {
            test: /\.(woff2)$/,
            use: [{
                loader: 'file-loader',
                options: {
                    name: (path) => {///work/Asoxer/asoxer-server/static/v1/src/common/fonts/xxx.woff2
                        return path.replace('/common/fonts', '').split('/src/')[1].split('.')[0] + '.[hash:8].[ext]';
                    },
                    outputPath: 'fonts/',
                    publicPath: '/v1/dist/fonts/'
                  }
            }]
        }]
    },
    plugins: [
        new HandleHashPlugin(),
        new VueLoaderPlugin(),
        new MiniCssExtractPlugin({ 
          filename:'css/[name].[contenthash].css',
          chunkFilename: 'css/[id].[contenthash].css',
        }),
        new CopyWebpackPlugin([
            {
                from: path.join(__dirname, './src/static'),
                to: path.join(__dirname, './dist/static') + '/[name].[hash:8].[ext]'
            }
        ])
    ]
};

收集到文件版本變化的信息后详拙,我們只需在上線前培漏,遍歷所有的頁面文件溪厘,把相應(yīng)的文件替換掉即可牌柄。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末畸悬,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蹋宦,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,884評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件守屉,死亡現(xiàn)場離奇詭異蒿辙,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)思灌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來熄守,“玉大人,你說我怎么就攤上這事耗跛。” “怎么了调塌?”我有些...
    開封第一講書人閱讀 158,369評論 0 348
  • 文/不壞的土叔 我叫張陵烟阐,是天一觀的道長紊扬。 經(jīng)常有香客問我,道長餐屎,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,799評論 1 285
  • 正文 為了忘掉前任屿聋,我火速辦了婚禮藏鹊,結(jié)果婚禮上润讥,老公的妹妹穿的比我還像新娘。我一直安慰自己盘寡,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,910評論 6 386
  • 文/花漫 我一把揭開白布脆粥。 她就那樣靜靜地躺著,像睡著了一般变隔。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上猖闪,一...
    開封第一講書人閱讀 50,096評論 1 291
  • 那天肌厨,我揣著相機(jī)與錄音,去河邊找鬼检柬。 笑死竖配,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的进胯。 我是一名探鬼主播,決...
    沈念sama閱讀 39,159評論 3 411
  • 文/蒼蘭香墨 我猛地睜開眼偎血,長吁一口氣:“原來是場噩夢啊……” “哼盯漂!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起就缆,我...
    開封第一講書人閱讀 37,917評論 0 268
  • 序言:老撾萬榮一對情侶失蹤竭宰,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后切揭,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,360評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡哼审,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,673評論 2 327
  • 正文 我和宋清朗相戀三年棺蛛,在試婚紗的時候發(fā)現(xiàn)自己被綠了怔蚌。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片旁赊。...
    茶點故事閱讀 38,814評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡终畅,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出离福,到底是詐尸還是另有隱情,我是刑警寧澤蝶涩,帶...
    沈念sama閱讀 34,509評論 4 334
  • 正文 年R本政府宣布絮识,位于F島的核電站,受9級特大地震影響次舌,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜挪圾,卻給世界環(huán)境...
    茶點故事閱讀 40,156評論 3 317
  • 文/蒙蒙 一逐沙、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧酱吝,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春江醇,著一層夾襖步出監(jiān)牢的瞬間濒憋,已是汗流浹背陶夜。 一陣腳步聲響...
    開封第一講書人閱讀 32,123評論 1 267
  • 我被黑心中介騙來泰國打工条辟, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人羽嫡。 一個月前我還...
    沈念sama閱讀 46,641評論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像婚惫,于是被迫代替她去往敵國和親魂爪。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,728評論 2 351