webpack打包

ESM存在环境兼容问题
模块文件过多
网络请求频繁
所有的前端资源都需要模块化

综上所述,模块化是必要的


散落的模块文件打包到一起,资源统合到一起

  • 新特性代码编译
  • 模块化JavaScript打包
  • 支持不同类型的资源模块

模块打包工具

Webpack

  • 模块打包器(Module bundler)解决模块化JavaScript代码打包的问题,可以将零散的模块代码统合到一个JS文件当中
  • 模块加载器(Lodar)将环境兼容问题的代码进行编译转换
  • 代码拆分(Code Splitting)将应用中所有的代码按照需求打包,不用担心代码打包到一起,比如应用加载过程中初次运行所必要的代码打包到一块,在运行过程中,实现增量加载,不用担心文件太大
  • 资源模块(Asset Module)支持在JavaScript中以模块化的方式去载入任意类型的资源文件,比如在webpack当中,通过JavaScript import一个CSS文件,最终通过style标签的形式工作,其他文件类似如此
    打包工具解决的是整个前端的模块化,并非单指JavaScript

webpack初体验

  • 步骤1:
    初始化项目
yarn init --yes
  • 步骤2:
    添加webpack依赖作为开发依赖
yarn add webpack webpack-cli --dev
  • 步骤3:
    检验webpack版本号
yarn webpack --version
  • 步骤4:
    使用webpack打包
yarn webpack
打包成功终端显示信息

webpack会将文件打包为运行文件夹dist,并且将js代码压缩为main.js


  • 如果觉得每次都用指令

yarn webpack

很麻烦的话,可以在package.json里用script注册build指令替换掉



再次进行打包就是

yarn build

webpack 配置文件

webpack 4以后的版本支持0配置的方式直接启动打包,整个打包过程会按照约定,将src/index.js作为入口,存入dist/main.js中

  • 自定义打包配置
    项目根目录创建webpack.config.js的文件。这个文件是运行在node环境中的JS文件,需要按照commonJS的方法编写代码
const path = require('path')

module.exports = {
  // 指定webpack打包入口文件的路径  ./是不能省略的
  entry: './src/main.js',
  // 设置输出文件的位置,要求是对象,通过对象的filename,指定输出名称,
  output: {
    filename: 'bundle.js',
    // 指定输出文件所在的目录,path必须要是绝对路径,载入path模块,获得路径
    path: path.join(__dirname, 'output')
  }
}

webpack工作模式

不同环境的几组预设配置
默认的工作模式的production
修改工作模式的指令就是在运行webpack打包时的指令加上mode,例如

yarn webpack --mode 工作模式

工作模式分为三种:

  • production 生产模式,webpack会自动优化打包结果,这也是默认的工作模式
  • development 开发模式,webpack会自动优化打包的速度,添加一些调试过程中的辅助
  • None模式,webpack就是运行最原始的打包,不作任何的额外处理

差异可以在官方文档找到:

https://webpack.js.org/configuration/mode/

也可以在配置文件里设置工作模式

  • 在webpack.config.js文件里声明mode属性的属性值,就无需配置指令的参数了


webpack打包结果运行原理

小知识:VScode快捷折叠代码的快捷键是ctrl+k+ctrl+0
将所有的模块放入一个文件,并且构造成为一个立即执行函数,可以通过打断点的方式进行一步步调试,将各个模块通过互相调用联系起来

webpack 资源模块加载

通过webpack引入前端项目中的任意文件

  • webpack内部默认只处理JavaScript文件
  • 要想让webpack处理其他类型的文件例如css等等就需要新的loader
  • CSS需要的新loader有:
    • css-loader
    • style-loader
yarn add style-loader --dev
yarm add css-style --dev

另外安装了loader之后还需要在webpack.config.js文件里进行设置


设置module下的rules
test:表示的是匹配打包过程中的文件路径
use:匹配到的文件需要使用的loader,多个loader的话执行顺序是从后往前执行
Loader是webpack的核心特性
通过不同的Loader可以实现加载任何类型的资源

webpack 导入资源模块

打包入口=>运行入口
JavaScript驱动整个前端应用的业务

JS代码通过import引入CSS文件才是正确的做法

例如:
在js引入css文件要使用import

因为是单独的类名,所以还需要使用element.classList.add来添加标签的类名实现选择器

  • 逻辑合理,JS确实需要资源文件
  • 保证上线资源不缺失,都是必要的

webpack 文件资源加载器

导入一个png资源


同理,也是需要import导入的,这里需要接收资源模块的默认导出,也就是资源路径,使用路径设置为src

这个时候同样是需要新的Loader,因为导入了webpack默认不能识别的资源类型

yarn add file-loader --dev

同样,需要在webpack配置文件设置


rules下的多个数组

webpack会默认的认为打包的内容会放在网站的根目录下面,可能会造成路径问题
更改问题:
通过配置文件告知webpack

    publicPath: 'dist/'

publicPath是默认空字符串的,所以要记得修改

运行原理

webpack Data URLs 与 url-loader

  • 特殊的URL协议,当前的url就可以直接表示文件内容的方式



    例子

    图片或者是字体这一类无法通过文本表示的二进制文件,就可以通过base64编码结果为字符串来标示内容

yarn add url-loader --dev

然后将rules下的图片类改成url-loader

          loader: 'url-loader',

url-load 适合小文件的使用,小文件使用Data URLs,可以减少请求次数。大文件单独提取存放,使用file-loader,提高加载速度

  • use作为类,loader指定url-loader,options的limit则是指定范围大小内的文件进行url处理,之外的进行file-loader处理,前提是必须要添加file-loader的依赖
use: {
          loader: 'url-loader',
          // 添加配置选项
          options: {
            // 只将10KB以下的文件进行url-loader的处理,以上的依然使用file-loader
            limit: 10 * 1024 // 10 KB
          }
        }

webpack 常用加载器分类

  • 编译转换类
    加载到的资源模块转换为JavaScript代码,比如css-loader
  • 文件操作类
    加载到的资源模块拷贝到输出目录,导出访问路径,比如file-loader
  • 代码检查类
    统一代码风格,提高代码质量,比如eslint-loader


webpack 处理ES2015

因为模块打包需要,所以处理import,export,并不能处理ES6的其他特性
如果需要webpack打包过程中同时处理其他的ES6特性的转换,需要为JS文件配置一个额外的编译器loader。比如babel-loader

yarn add babel-loader @babel/core @babel/preset-env --dev

修改webpack.config.js文件

{
        test: /.js$/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env']
          }
        }
      },
  • webpack只是打包工具
  • 加载器可以用来转换编译代码

webpack 模块加载方式

  • 遵循ESM 标准的import声明
  • 遵循CommonJS标准的require函数
  • 遵循AMD标准的define函数和require函数
    不要混合使用,不方便统一标准的实施
  • Loader加载的非JavaScript也会触发资源加载
    • 样式代码中的@import属性和url函数


    • HTML代码中图片标签的src属性


      要使用html还得添加html-loader


      html-loader只会处理img标签的src属性,如果其他属性也想要触发打包的话,添加attrs属性的规则就行了

webpack核心工作原理

loader机制是webpack的核心
先找到一个.js文件作为入口,就犹如树状一样,每个支点找到对应的资源文件,最后整合到一起写入bundle.js文件


webpack Loader的工作原理

loader就是负责资源文件从输入到输出的转换
对于同一个资源可以依次使用多个loader
比如:
css-loader->style-loader

webpack 插件机制

增强webpack自动化能力
Loader专注实现资源模块加载
Plugin解决其他自动化工作
比如:清除dist目录,拷贝静态文件到输出目录,压缩输出代码

webpack 自动清除输出目录插件

yarn add clean-webpack-plugin --dev

使用

const { CleanWebpackPlugin } = require('clean-webpack-plugin')

  plugins: [
    new CleanWebpackPlugin()
  ]

webpack 自动生成HTML插件

通过webpack输出HTML文件
插件:html-webpack-plugin
不需要解构:

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

与上述自动清除目录插件的导入与使用基本是一致的
生成模板,以供webpack生成html参照结构


titie表示标题,meta内部可以设置viewport等等,template就是指定的模板文件

模板文件内部也可以通过调用内容来写入

同时输出多个页面文件



以后如果要添加多个HTML文件就写多个实例对象就可以了

webpack常用插件使用总结&copy-webpack-plugin

以下这个插件最好不要在开发阶段使用,一般都是留在上线前的那一次打包中使用

  • copy-webpack-plugin的作用就是将指定文件夹下面的文件拷贝到输出目录
    用法与导入导出都与上述一致,不需要赘述
    之后在 官方说明多看看特别用法,社区还提供了更多的插件
    需求->关键词->搜索

webpack 开发一个插件

相比于loader,plugin拥有更宽的能力范围
plugin通过钩子机制实现

class MyPlugin {
  apply (compiler) {
    console.log('MyPlugin 启动')

    compiler.hooks.emit.tap('MyPlugin', compilation => {
      // compilation => 可以理解为此次打包的上下文
      for (const name in compilation.assets) {
        // console.log(name)
        // console.log(compilation.assets[name].source())
        if (name.endsWith('.js')) {
          const contents = compilation.assets[name].source()
          const withoutComments = contents.replace(/\/\*\*+\*\//g, '')
          compilation.assets[name] = {
            source: () => withoutComments,
            size: () => withoutComments.length
          }
        }
      }
    })
  }
}

插件通过在生命周期的钩子中挂载函数实现拓展

webpack 开发体验的设想

过于原始的方式

理想的开发环境

  • 以HTTP server 运行
  • 自动编译+自动刷新
  • 提供Source Map支持

实现自动编译

监听文件变化,自动重新打包
启动命令后面加个watch参数,启动监听模式

yarn webpack --watch

自动刷新浏览器

browserSync工具,以前有过介绍

browser-sync dist --files "**/*"

弊端:
操作麻烦,要开两个终端,效率上降低了

webpack Dev Server

集成了自动编译和自动刷新浏览器等功能

yarn add webpack-dev-server --dev

运行

yarn webpack-dev-server

打包结果不会存放在磁盘上,而是存放在内存当中,所以不会有dist文件夹,可以减少磁盘读写操作,大大提高效率
自动唤醒浏览器

yarn webpack-dev-server --open

webpack Dev Server静态资源访问

Dev Server默认只会serve打包输出文件
只要是webpack输出的文件,都可被直接访问到
静态资源文件也需要serve

devServer: {
    // 指定额外的静态资源路径,可以为字符串或者数组,也就是一个或者多个
    contentBase: './public',
  }

webpack Dev Server 代理API

回到开发环境会造成跨域请求问题

并不是任何情况下API都应该支持CORS同源策略
如果是同源部署,根本没必要开启CORS
出现问题:开发阶段接口跨域问题
解决问题:配置代理,把接口服务代理到本地的开发地址

webpack Dev Server支持配置代理
目标:将Github的API代理到本地开发服务器
在devServer当中添加一个proxy属性,这个属性就是添加代理服务配置的

 devServer: {
    proxy: {
      // 请求路径前缀
      '/api': {
        // http://localhost:8080/api/users -> https://api.github.com/api/users
        // 代理目标
        target: 'https://api.github.com',
        // http://localhost:8080/api/users -> https://api.github.com/users
        // 实现代理路径的重写
        pathRewrite: {
          // 替换为空,^为开头
          '^/api': ''
        },
        // 不能使用 localhost:8080 作为请求 GitHub 的主机名
        // 默认使用的就是用户的localhost:8080
        // changeOrigin:true就会以实际代理的主机名去请求,就是api.github.com
        changeOrigin: true
      }
    }
  },
代理效果

Source Map

运行代码和源代码之间完全不同,如果需要调试应用,错误信息没法定位,调试或者报错都是基于运行代码,Source Map就是解决这类问题的办法(源代码地图)
用于映射源代码和转换代码之间的关系

version属性:记录Source Map版本号
sources属性:转换之前源文件的名称,可能是多个文件,所以是数组
name属性:源代码成员名称
mapping属性:转换之后的代码的字符与转换之前对应的映射关系
最新版jQuery已经去除了引入source map的注释

引入

可以调试未压缩的代码了

source map 解决了源代码和运行代码不一致所产生的问题

webpack 配置 Source Map

在webpack配置文件里有一个属性叫做devtool
,配置开发过程中的辅助工具
webpack 支持12种不同的方式
每种方式的效率和效果不同


生成了map文件,在开发者工具内也可以直接定位到出问题的文件

eval模式下的Source Map

构建速度,重新打包速度,是否在生产环境下使用,所生成的map质量

这种模式下不会生成source-map文件,只能定位源代码名称,不知道行列信息

不同devtool之间的差异

所有的不同模式下的devtool测试:

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

const allModes = [
    'eval',
    'cheap-eval-source-map',
    'cheap-module-eval-source-map',
    'eval-source-map',
    'cheap-source-map',
    'cheap-module-source-map',
    'inline-cheap-source-map',
    'inline-cheap-module-source-map',
    'source-map',
    'inline-source-map',
    'hidden-source-map',
    'nosources-source-map'
]

module.exports = allModes.map(item => {
    return {
        devtool: item,
        mode: 'none',
        entry: './src/main.js',
        output: {
            filename: `js/${item}.js`
        },
        module: {
            rules: [
                {
                    test: /\.js$/,
                    use: {
                        loader: 'babel-loader',
                        options: {
                            presets: ['@babel/preset-env']
                        }
                    }
                }
            ]
        },
        plugins: [
            new HtmlWebpackPlugin({
                filename: `${item}.html`
            })
        ]
    }
})

// module.exports = [
//  {
//      entry: './src/main.js',
//      output: {
//          filename: 'a.js'
//      }
//  },
//  {
//      entry: './src/main.js',
//      output: {
//          filename: 'b.js'
//      }
//  }
// ]

运行之后可以慢慢观察各个模式的差距

  • eval:是否使用eval执行模块代码
  • cheap:Source Map是否包含行信息
  • module:是否能够得到Loader处理之前的源代码
  • inline:使用dataURL的方式嵌入到url当中(最少用到的)
  • hidden:看不到source-map的效果,但是确实生成了map文件,跟jquery是一样的,代码中并没有引入,开发第三方包比较有用
  • nosources:能看到错误出现的位置,但是点击错误信息,看不到源代码的,可以找到错误,确保源代码不会被暴露的情况

webpack选择合适的 Source Map模式

来自师傅的个人开发经验之谈:
选择cheap-module-eval-source-map

  • 代码每行不超过80个字符(定位到行就可以了)
  • 经过Loader转换过后的差异较大,频繁使用框架(需要查阅转换之前的代码)
  • 首次打包速度比较慢,但是重写打包相对较快

生产环境下
选择None

  • Source Map会暴露源代码
  • 调试是开发阶段的事情,在开发阶段尽可能找到所有的bug
    再不济也要选择nosources-source-map
  • 不要暴露源代码的内容

没有选择的差异,理解不同模式的差异,适配不同的环境

webpack 自动刷新的问题

比如我测试一个文本输入页面,一旦有变化,文本会丢失,又要重新输入
页面不刷新的前提下,模块也可以及时更新

webpack HMR

HMR又名模块热替换(热更新)
应用程序运行过程中实时替换掉某个模块,运行状态不会改变,以解决自动刷新导致页面内容丢失的问题。
热替换只是将修改的模块实时替换到应用中
HMR是webpack中最强大的功能之一
已经集成到了webpack-dev-server中,不必要专门添加什么依赖
命令:

webpack-dev-server --hot

相对应的也有可以在配置文件中添加相应的配置打开HMR

  devServer: {
    hot: true
  },

然后载入webpack模块

const webpack = require('webpack')

再在plugins里写入实例

new webpack.HotModuleReplacementPlugin()

HMR还需要额外的操作才能正常工作

webpack中的HMR需要手动处理模块热替换逻辑

样式文件热更新开箱即用的原理:
经过loader处理了,style-loader中已经自动处理了热更新
在框架下的开发,每种文件都是有规律的。所以JS就可以自动热更新,通过脚手架创建的项目内部都集成了HMR方案

HMR APIs

// ============ 以下用于处理 HMR,与业务代码无关 ============

// console.log(createEditor)

if (module.hot) {
  let lastEditor = editor
  module.hot.accept('./editor', () => {
    // console.log('editor 模块更新了,需要这里手动处理热替换逻辑')
    // console.log(createEditor)

    const value = lastEditor.innerHTML
    document.body.removeChild(lastEditor)
    const newEditor = createEditor()
    newEditor.innerHTML = value
    document.body.appendChild(newEditor)
    lastEditor = newEditor
  })

  module.hot.accept('./better.png', () => {
    img.src = background
    console.log(background)
  })
}

HMR注意事项

  • 处理HMR的代码报错会导致自动刷新
    解决方法:
    hotOnly: true // 只使用 HMR,不会 fallback 到 live reloading
  • 没启用HMR的情况下,HMR API报错
    缺失:
    new webpack.HotModuleReplacementPlugin()

解决:
添加控制条件

if(module.hot)
  • 代码中多了一些与业务无关的代码
    移除了热更新的实例与导入代码,业务模块的相关代码会自动移除

webpack 生产环境优化

生产环境和开发环境有很大的差异
生产环境注重运行效率
开发环境注重开发效率
webpack 提供模式(mode) 为不同的工作环境创建不同的配置

webpack不同环境下的配置

  • 配置文件根据环境不同导出不同配置
    (中小型项目)
  • 一个环境对应一个配置文件
const webpack = require('webpack')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin')

module.exports = (env, argv) => {
  const config = {
    mode: 'development',
    entry: './src/main.js',
    output: {
      filename: 'js/bundle.js'
    },
    devtool: 'cheap-eval-module-source-map',
    devServer: {
      hot: true,
      contentBase: 'public'
    },
    module: {
      rules: [
        {
          test: /\.css$/,
          use: [
            'style-loader',
            'css-loader'
          ]
        },
        {
          test: /\.(png|jpe?g|gif)$/,
          use: {
            loader: 'file-loader',
            options: {
              outputPath: 'img',
              name: '[name].[ext]'
            }
          }
        }
      ]
    },
    plugins: [
      new HtmlWebpackPlugin({
        title: 'Webpack Tutorial',
        template: './src/index.html'
      }),
      new webpack.HotModuleReplacementPlugin()
    ]
  }
// 判断什么模式
  if (env === 'production') {
    config.mode = 'production'
    config.devtool = false
    config.plugins = [
      ...config.plugins,
      new CleanWebpackPlugin(),
      new CopyWebpackPlugin(['public'])
    ]
  }

  return config
}

  • 不同环境的配置文件


    公共,开发环境,生产环境

    prod

    dev

common.js:

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

module.exports = {
  entry: './src/main.js',
  output: {
    filename: 'js/bundle.js'
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          'style-loader',
          'css-loader'
        ]
      },
      {
        test: /\.(png|jpe?g|gif)$/,
        use: {
          loader: 'file-loader',
          options: {
            outputPath: 'img',
            name: '[name].[ext]'
          }
        }
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      title: 'Webpack Tutorial',
      template: './src/index.html'
    })
  ]
}

使用什么模式,就用这条指令:

yarn webpack --config 哪一个文件

webpack DefinePlugin

为代码注入全局成员
这是webpack内置的插件

  plugins: [
    new webpack.DefinePlugin({
      // 值要求的是一个代码片段
      API_BASE_URL: JSON.stringify('https://api.example.com')
    })
  ]
直接替换一为设置的内容

webpack Tree-shaking

  • [摇掉]代码中没有引用的部分,未引用代码(dead-code)
  • Tree-shaking会在生产模式production下自动开启
  • Tree-shaking不是某一个配置选项
  • 非生产模式下的配置:
// 集中配置webpack内部的优化功能
  optimization: {
    // 模块只导出被使用的成员
    usedExports: true,
    // 尽可能合并每一个模块到一个函数中
    concatenateModules: true,
    // 压缩输出结果
    minimize: true
  }

webpack Tree-shaking 与 babel

  • Tree Shaking的前提是ESM
  • 由webpack打包的代码必须使用ESM
          options: {
            presets: [
              // 如果 Babel 加载模块时已经转换了 ESM,则会导致 Tree Shaking 失效
              // ['@babel/preset-env', { modules: 'commonjs' }]
              // ['@babel/preset-env', { modules: false }]
              // 也可以使用默认配置,也就是 auto,这样 babel-loader 会自动关闭 ESM 转换
              ['@babel/preset-env', { modules: 'auto' }]
            ]
          }

Babel并不会让Tree-shaking失效

webpack sideEffcts(副作用)

副作用:模块执行时除了导出成员之外所作的事情
一般用于npm包标记是否有副作用

 optimization: {
    sideEffects: true,
  }

标明代码真的没有副作用

确保你的代码真的没有副作用,否则会误删

  • 样式文件属于副作用模块


    extend是写的一个有副作用的模块,这样设置不会误删

Code Splitting(代码分割)

所有的代码都会被打包到一起,bundle体积过大
并不是每一个模块在启动的时候都是必要的
分包,按需加载

  • 多入口打包(多页面应用程序)
    一个页面去对应一个打包入口
    公共部分单独提取


plugin部分

提取公共模块
optimization中的splitChunks属性

  • ESM动态导入
    按需加载:需要用到某个模块时,再加载这个模块
    动态导入的模块会被自动分包(更为灵活)

魔法注释


灵活组织动态加载的模块输出的文件

MinCssExtraCtPlugin

提取CSS到单个文件

yarn add mini-css-extract-plugin --dev

在webpack配置文件中导入,再通过实例引用

const MiniCssExtractPlugin = require('mini-css-extract-plugin')

new MiniCssExtractPlugin()

会将样式的CSS代码单独提取到一个文件中,再引用,也就是说不需要style-loader了,取而代之的是

MiniCssExtractPlugin.loader

如果css文件体积大于150kb了,就考虑提取到单独文件当中

OptimizeCssAssetsWebpackPlugin

压缩输出的CSS文件
webpack内置的压缩插件只针对JS的压缩,CSS没有压缩,需要额外的插件


yarn add optimize-css-assets-webpack-plugin --dev

运行原理照旧,有一点需要注意:配置在optimization的minimizer属性当中


如果在生产模式下就会自动进行压缩了

但是有一个缺点,如果配置了minimizer,编辑器就会自动认为要使用配置的压缩规则,那么JS就不会被压缩了


内置的JS压缩为这个,配置回来就行了,这个插件也需要先下载依赖

输出文件名Hash

生产模式下,文件名使用Hash值
一旦资源发生改变,文件名也会发生改变,对于客户端来说全新的文件名就是全新的请求,就没有缓存的问题,不用担心文件更新之后的问题

webpack中的filename属性和绝大多数插件的filename属性都支持通过占位符的方式为文件名设置Hash

  • [name]-[hash]:项目级别,有任何一个地方发生变化,hash值就全部发生变化了
  • [name]-[chunkhash]:同一路的打包,chunkhash都是同样的hash,在同一路下的任意文件作出改动,其他的同一路文件都会发生变化
  • [name]-[contenthash]:文件级别的hash
    (解决缓存问题最好的方式)
    可以通过:number的方式来指定hash长度


    八位hash是解决缓存最好的选择
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,686评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,668评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,160评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,736评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,847评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,043评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,129评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,872评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,318评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,645评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,777评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,861评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,589评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,687评论 2 351

推荐阅读更多精彩内容