Webpack笔记二

webpack配置DEMO

webpack.config.js文件官方标配示例如下:

module.exports = {
    dvtool: //配置生成Source Maps,选择合适的选项
    plugins: //插件项
    entry: //页面入口文件配置
    output: //入口文件输出配置
        path: //定义输出文件路径
        filename: //指定打包文件名称
    module: //加载器配置,对模块的处理逻辑
    loaders: //定义了一些列的加载器
        test: //正则,匹配到文件的后缀名
        loader/loaders: //处理匹配到的文件----注意现在要求loader关键字不准省略"-loader"后缀
        include: //包含的文件夹
        exclude: //排除的文件夹
    resolve: //其它解决方案的配置
        extensions: //自动补全识别后缀
}

参考各路大神的流程写下来.....应该没有漏掉哪一步吧?
DEMO 项目结构如图:

index.html(加载打包后的build.js)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>webpack test</title>
</head>
<body>
    <div id="test"></div>
</body>
<!-- webpack一会打包完成的js -->
<script src="build.js"></script> 
</html>

app.js(返回包含问候信息的html元素)

// app.js
module.exports = function() {
  var hello = document.createElement('div');
  hello.textContent = "Hello Everybody!";
  return hello;
};

main.js(把app.js返回的节点插入页面)

//main.js 
var hello = require('./app.js');
document.getElementById('test').appendChild(hello());

webpack可以在终端使用

webpack {entry file/入口文件} {destination for bundled file/存放build.js的地方}

非全局安装使用

node_modules/.bin/webpack app/main.js public/bundle.js

打开index.html

这里推荐通过配置文件来使用webpack

entry和output

在webpack.config.js中entry是唯一入口文件
首先entry的值可以有三种类型:1、字符串;2、数组;3、对象

//与DEMO无关,此为示例说明文件
//字符串,指定从这个文件路径下面的文件作为打包的入口文件
entry: './src/js/main.js' , //唯一入口文件
output: {
    path: 'dist/js', //打包后的文件存放地方
    filename: "build.js" //文件名
  }

//存在多个入口时,可以使用array的方式。会将里面的文件一起打包到build.js
{
  entry: ['./src/js/main.js', './src/js/test_entry.js'],
  output: {
    path: 'dist/js',
    filename: "build.js"
  }
}

//也可以是一个对象
{
    entry: {
        main: './src/js/main.js',
        test_entry: './src/js/test_entry.js'
    },
    output: {
        path: 'dist/js',
        filename: '[name]-[chunkhash].js' //[name]的值是entry的键值,[hash]是打包时候的hash值,chunkhash是md5加密的值,这里作为版本号使用
    }
}

每次修改文件,运行webpack后都会生成不一样的hash和chunkhash值,方便上线时候静态资源的版本管理。

因为文件名每次运行都是变化的,文件引入的文件名字也是需要变化的,这时候使用html-webpack-plugin插件 npm install html-webpack-plugin --save-dev

完成后在wenpack.config.js文件里添加plugins(plugins)的值是数组,里面的值都是new htmlWebpackPlugin(),参数见下文;
最后文件如下:

//与DEMO无关,此为示例说明文件
var htmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
    entry: {
        main: './src/js/main.js',
        test_entry: './src/js/test_entry.js'
    },
    output: {
        path: 'dist/js',
        filename: '[name]-[chunkhash].js' //[name]的值是entry的键值,[hash]是打包时候的hash值,chunkhash是md5加密的值,这里作为版本号使用
    },
    plugins: [
        new htmlWebpackPlugin({
            title: 'webpack demo',
            filename: 'index-[hash].html',
            template: 'index.html'
        })
    ]
}
  • title: 用来生成页面的 title 元素
  • filename: 输出的 HTML 文件名,默认是 index.html, 也可以直接配置带有子目录。
  • template: 模板文件路径,支持加载器,比如 html!./index.html
  • inject: true | 'head' | 'body' | false ,注入所有的资源到特定的 template 或者 templateContent 中,如果设置为 true 或者 body,所有的 javascript 资源将被放置到 body 元素的底部,'head' 将放置到 head 元素中。
  • favicon: 添加特定的 favicon 路径到输出的 HTML 文件中。
  • minify: {} | false , 传递 html-minifier 选项给 minify 输出
  • hash: true | false, 如果为 true, 将添加一个唯一的 webpack 编译 hash 到所有包含的脚本和 CSS 文件,对于解除 cache 很有用。
  • cache: true | false,如果为 true, 这是默认值,仅仅在文件修改之后才会发布文件。
  • showErrors: true | false, 如果为 true, 这是默认值,错误信息会写入到 HTML 页面中
  • chunks: 允许只添加某些块 (比如,仅仅 unit test 块)
  • chunksSortMode: 允许控制块在添加到页面之前的排序方式,支持的值:'none' | 'default' | {function}-default:'auto'
  • excludeChunks: 允许跳过某些块,(比如,跳过单元测试的块)

在当前demo根目录下新建一个webpack.config.js的文件,首先最简单的写一下入口文件和存放打包后文件的地方的路径

//DEMO
module.exports = {
    entry: __dirname + "/app/main.js", //唯一入口文件
    output: {
        path: __dirname + "/public", //打包后的文件存放地方
        filename: "build.js" //打包后输出的文件的文件名
    }
}

_dirname 是node.js的一个全局变量,指向当前执行脚本所在的目录

在终端里运行webpack node_modules/.bin/webpack(非全局安装)

那么在非全局安装webpack中,或者是类似于上面命令很长很容易出错的时候,我们可以用npm引导任务执行,在package.json中对npm脚本部分进行设置,可以使用简单的npm start命令来代替:

{
  "name": "demo",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "webpack" //相当于把npm的start命令指向webpack命令
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "webpack": "^2.3.3"
  }
}

这时候的编译后是虚拟的,本地其实是没有编译的,所以很多使用本地路径的都需要我们去处理。

无论全局还是局部安装的webpack,这里都不需要写前面详细路径,因为package.json脚本部分已经默认在命令中添加了node_module/.bin路径。

devtool

生成Source Maps可以使开发更容易。打包后的文件有时候是不容易找到错的地方对应的源码的。通过简单的配置,webpack在打包时候会为我们生成source maps,这可以为我们提供一种对应编译文件和源文件的方法,使得编译后的代码可读性更高,更容易调试。

devtool选项 配置结果
source-map 在一个单独的文件中产生一个完整且功能完全的文件。这个文件具有最好的source map,但是它会减慢打包文件的构建速度;
cheap-module-source-map 在一个单独的文件中生成一个不带列映射的map,不带列映射提高项目构建速度,但是也是的浏览器开发者工具只能对应到具体的行,不能对应到具体的列(符号),会对调试造成不便;
eval-source-map 使用eval打包源文件模块,在同一个文件中生成干净的完整的source map。这个选项可以在不影响构建速度的前提下生成完整的source map,但是对打包后输出的js文件的执行具有性能和安全的隐患。不过在开发阶段这是一个非常好的选项,但是在生产阶段一定不要用这个选项;
cheap-module-eval-source-map 这是在打包文件时最快的生成source map的方法,生成的source map会和打包后的JavaScript文件同行显示,没有列映射,和eval-source-map选项具有相似的缺点

在webpack中配置source maps需要配置devtool,它共有四种不同的配置选项,优缺点描述如下:

devtool选项 配置结果
source-map 在一个单独的文件中产生一个完整且功能完全的文件。这个文件具有最好的source map,但是它会减慢打包文件的构建速度;
cheap-module-source-map 在一个单独的文件中生成一个不带列映射的map,不带列映射提高项目构建速度,但是也是的浏览器开发者工具只能对应到具体的行,不能对应到具体的列(符号),会对调试造成不便;
eval-source-map 使用eval打包源文件模块,在同一个文件中生成干净的完整的source map。这个选项可以在不影响构建速度的前提下生成完整的source map,但是对打包后输出的js文件的执行具有性能和安全的隐患。不过在开发阶段这是一个非常好的选项,但是在生产阶段一定不要用这个选项;
cheap-module-eval-source-map 这是在打包文件时最快的生成source map的方法,生成的source map会和打包后的JavaScript文件同行显示,没有列映射,和eval-source-map选项具有相似的缺点

接下来生成Source Maps这可以使调试更加的容易,因为这只是一个测试的小项目,所以推荐使用eval-source-map,不过只是在开发阶段使用。

//DEMO
module.exports = {
    devtool: 'eval-source-map',//配置生成Source Maps,选择合适的选项
    entry: __dirname + "/app/main.js", //唯一入口文件
    output: {
        path: __dirname + "/public", //打包后的文件存放地方
        filename: "build.js" //打包后输出的文件的文件名
    }
}

使用webpack构建本地服务器

webpack可以提供一个可选的本地开发服务器,该服务器是基于node.js构建,不过它是一个单独的组件(webpack-dev-server是一个独立的NPM包),在webpack中进行配置之前需要单独安装它作为项目依赖。这一步搞好了,嗯哼~你的浏览器就可以监测你代码的修改然后自动刷新了。

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

接下来我们调整一下配置文件:

module.exports = {
    devtool: 'eval-source-map',//配置生成Source Maps,选择合适的选项
    entry: __dirname + "/app/main.js", //唯一入口文件
    output: {
        path: __dirname + "/public", //打包后的文件存放地方
        publicPath:"/assets/", //运行后你看不到,因为实时编译是保存到了内存中
        filename: "build.js" //打包后输出的文件的文件名
    }
}

调整一下index.html的js引用路径 <script src="assets/build.js"></script>

运行webpack-dev-server --content-base ./public得到如下(这里如果不进行设定的话,默认是当前目录下的):

打开浏览器输入:localhost:8080 你就能看到你的hello信息了....

对上面设定的content-base做一些说明:

注意:如果在webpack.config.js里面如果配置了output的publicPath这个字段的值的话,在index.html文件里面也应该做出调整,因此才需要调整index.html。上述配置了这个字段是为了对实时编译保存到内存中做一个说明,你也可以省略这个字段,那么index.html的js引用路径就依然是<script src="build.js"></script>

有同学要说,坑爹啊,我想要的是实时编译,实时刷新,浏览器自刷新。你这个是手动的什么鬼??

webpack-dev-server目前支持两种自动刷新的方式:

  1. iframe mode
  2. inline mode

这2种模式的配置方式和访问路径稍微有点区别,最主要的区别是:Iframe mode是在网页中嵌入了一个iframe,将我们自己的应用注入到这个iframe中,因此你每次修改的文件都是对这个iframe进行了reload。而inline mode是webpack-dev-server会在wenpack.config.js的入口配置文件中再添加一个入口。

module.exports = {
    devtool: 'eval-source-map',//配置生成Source Maps,选择合适的选项
    entry: {
        app: [
            'webpack-dev-derver/client?http://localhost:8080/',
            __dirname + "/app/main.js", //唯一入口文件
        ]
    },
    output: {
        path: __dirname + "/public", //打包后的文件存放地方
        filename: "build.js" //打包后输出的文件的文件名
    }
}

你也可以直接在index.html中引入这部分代码:<script src="http://localhost:8080/webpack-dev-server.js"></script>

Iframe mode

浏览器访问路径变为:localhost:8080/webpack-dev-server/index.html

页面的header部分会出现整个reload消息的状态,当改变源文件时候,就可以自动完成编译打包,页面自动刷新。

Inline code

使用时候,cmd line需要写成webpack-dev-server --inline --content-base ./public

浏览器访问路径是:localhost:8080/index.html

它会在控制台中显示reload状态,也具有自动编译打包的功能。

hot module replacement

开启hot module replacement 功能,在cmd line里面添加 --hot就可以

webpack-dev-derver --hot --inline --content-base ./public

其它的配置项比如:

  • quiet 控制台中不输出打包信息
  • compress 开启gzip压缩
  • propress 显示打包的进度

调整package.json里面的配置项

"scripts": {
    "dev": "webpack-dev-server --devtool eval-source-map --progress --colors --hot --inline --content-base ./public",
    "build": "webpack --progress --colors"
  }

接下来就可以使用npm run dev 在浏览器中打开localhost:8080/index.html 进行愉快开发了

备注一下devserver配置选项

devserver配置选项 描述
contentBase 默认webpack-dev-server会为根文件夹提供本地服务器,如果想为另外一个目录下的文件提供本地服务器,应该在这里设置其所在的目录(比如DEMO中的“public”目录)
port 设置默认监听端口,默认为“8080”
inline 设置为true,当源文件改变时,会自动刷新页面
colors 设置为true,终端输出的文件为彩色的
historyApiFallback 开发单页面应用时非常有用,它依赖于HTML5 history API,如果设置为true,所有的跳转将指向index.html

module-loaders

loaders中对我来说最难的是babel,我看着大神的教程N久没折腾出来,就为了体验把ES6,也是拼了。
按照大神的教程,继续....希望这次折腾成功 come on baby
首先明确大名鼎鼎的loaders在webpack中为啥是个很犀利的功能。主要就是通过使用不同的loader,webpack可以通过调用外部的脚本或工具,对各种格式的文件进行处理。比如分析JSON文件把它转换为JS文件,或者是ES6/ES7转换为现代浏览器可以识别的JS文件。还可以将react的jsx文件转换成JS文件。厉害了我的loader....

loaders需要单独安装

npm install --save-dev json-loader

安装成功后需要在webpack.config.js下的module关键字下进行配置,配置项如下:

  • test: 一个匹配loaders处理的文件的拓展名的正则表达式(必写)
  • loader: loader的名称(必写)
  • include/exclude: 手动添加必须处理的文件(文件夹)/屏蔽不需要处理的文件(文件夹)(非必写)
  • query: 为loaders提供额外的设置选项(非必写)

webpack.config.js

module.exports = {
    devtool: 'eval-source-map',//配置生成Source Maps,选择合适的选项
    entry: {
        app: [
            __dirname + "/app/main.js", //唯一入口文件
        ]
    },
    output: {
        path: __dirname + "/public", //打包后的文件存放地方
        filename: "build.js" //打包后输出的文件的文件名
    },
    module: { //配置文件里添加json loader
        loaders: [
            {
                test: /\.json$/,
                loader: "json-loader"
            }
        ]
    },
}

DEMO继续,把app.js的message单独放在一个message.json文件里,再进行配置,让app.js可以读取到json中的message。

message.json

{
    "hello": "Hello everybody and from json file!"
}

app.js

// app.js
var message = require('./message.json');

module.exports = function() {
  var hello = document.createElement('div');
  hello.textContent = message.hello;
  return hello;
};

嗯哼,走你~~

这个是简单的了。接下来是我有爱有恨的babel
Babel其实是一个编译JavaScript的平台,强大之处就是:ES6/ES7转换为现代浏览器可以识别的JS文件。还可以将react的jsx文件转换成JS文件,简直占据了目前我的loader的半壁江山啊。

Babel其实就是几个模块化的包,核心功能位于babel-core的npm包中,不过webpack把它们整合在一起使用了,但是对于每一个你需要的功能或者拓展,你都需要安装单独的包(比如解析Es6的babel-preset-es2015和解析JSX的babel-preset-react包)

我们来个全家桶一次搞定:

npm install --save-dev babel-core babel-loader babel-preset-es2015 babel-preset-react(npm一次性安装多个依赖模块,模块之间用空格隔开)

配置webpack.config.js:

module.exports = {
    devtool: 'eval-source-map',//配置生成Source Maps,选择合适的选项
    entry: {
        app: [
            __dirname + "/app/main.js", //唯一入口文件
        ]
    },
    output: {
        path: __dirname + "/public", //打包后的文件存放地方
        filename: "build.js" //打包后输出的文件的文件名
    },
    module: { //配置文件里添加json loader
        loaders: [
            {
                test: /\.json$/,
                loader: "json-loader"
            },
            {
                test: /\.js$/,
                exclude: /node_modules/,
                loader: 'babel-loader',
                query: {
                    presets: ['es2015', 'react']
                }
            }
        ]
    }
}

因为demo会用的react所以记得安装react和react-dom

npm install --save react react-dom

在app.js里使用es6新语法开始愉快的返回一个react组件试一下吧

import React, {Component} from 'react'
import message from './message.json'

class Hello extends Component {
    render() {
        return (
            <div>
                {message.hello}
            </div>
        );
    }
}

export default Hello

在main.js使用ES6的模块定义和渲染Hello模块

import React from 'react';
import {render} from 'react-dom';
import Hello from './app.js';

render(<Hello />, document.getElementById("test"));

考虑到babel有非常多的配置选项,在webpack.config.js里面进行配置的话会显得太复杂,因此比较好的方式是把babel的配置选项单独放在一个.babelrc的配置文件中。webpack会自动调用这个配置文件的选项。因此我们将刚才的配置分离出来到.babelrc:

{
  "presets": ["react", "es2015"]
}

穿插点题外的。webpack对于模块有非常强大的处理功能,那么什么算是模块呢??

事实上一切皆模块,包括我们的css、fonts、图片、js等,当通过合适的loaders时,它们都可以被当成模块来处理。

CSS

webpack提供了两个工具处理样式表,css-loader和style-loader,这两个处理的任务不同。css-loader使你能够以类似@import 或者 url(...) 这种方式实现 require()的功能,style-loader是将所有计算后的样式加入页面中,两者组合在一起就能使样式表嵌入webpack打包后的js文件中。

DEMO继续:

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

webpack.config.js中添加配置:

 {
    test: /\.css$/,
    loader: 'style-loader!css-loader'//添加对样式表的处理
 }

感叹号的作用在于使同一文件能够使用不同类型的loader

在app文件夹创建一个main.css文件,写点样式

html {
  box-sizing: border-box;
  -ms-text-size-adjust: 100%;
  -webkit-text-size-adjust: 100%;
}

*, *:before, *:after {
  box-sizing: inherit;
}

body {
  margin: 0;
  font-family: '微软雅黑', Helvetica, Arial, sans-serif;
  background: #f2f2f2;
}

h1, h2, h3, h4, h5, h6, p, ul {
  margin: 0;
  padding: 0;
}

webpack只有单一入口,其它的模块需要通过import、require、url等导入相关位置,现在将main.css导入main.js中

import React from 'react';
import {render} from 'react-dom';
import Hello from './app.js';

import './main.css';

render(<Hello />, document.getElementById("test"));

通常情况下,css会和js打包到同一个文件中,并不会打包一个单独的css文件,通过合适的配置webpack的话也可以把css打包为单独的文件的。

CSS module

随着前端模块化开发的推动,css module出现了。它将js的模块化思想带入css中来,通过css模块,所有的类名、动画名默认都只作用于当前模块。webpack一开始就对css模块化提供了支持,在css loader中进行配置后,你所需要做的就是把modules传递到需要的地方,然后就可以直接把css的类名传递到组件的代码中,且只对当前组件有效,不比担心在不同的模块中具有相同的类名可能会造成的问题,实现方式先调整webpack.config.js中的css配置

{
    test: /\.css$/,
    loader: 'style-loader!css-loader?modules' //跟前面相比就在后面加上了?modules
}

创建一个app.js文件

#test {
    background-color: pink;
    padding: 10px;
    border: 5px solid green;
}

导入#test 到app.js中

import React, {Component} from 'react';
import message from './message.json';
import styles from './app.css';

class Hello extends Component {
    render() {
        return (
            <div className={styles.test}>
                {message.hello}
            </div>
        );
    }
}

export default Hello

相同的类名也不会造成不同组件之间的污染..
css modules是一个很大的主题,可以参考cssModules官方文档查看更多

css预处理器

Sass和Less之类的预处理器是对原生css的拓展,它们允许你使用类似于variables,nesting,mixins,inheritance等不存在于css中的特性来写css,css预处理器可以使这些特殊的语句转化为浏览器可以识别的css语句。

常用的loaders:

  • Less Loader
  • Sass Loader
  • Stylus Loader

还有一个css的处理平台-PostCss,具体可以参考PostCss官方文档

安装postcss和autoprefixer(自动添加前缀的插件)可以参考autoprefixer官方文档

npm install --save-dev postcss-loader autoprefixer

新建一个postcss.config.js文件,并在里面申明依赖的插件

module.exports = {
    plugins: [
        require ('autoprefixer')
    ]
}

webpack.config.js 如下现在你写的css会自动根据can i use 里的数据添加不同的前缀了。

module.exports = {
    devtool: 'eval-source-map',//配置生成Source Maps,选择合适的选项
    entry: {
        app: [
            __dirname + "/app/main.js", //唯一入口文件
        ]
    },
    output: {
        path: __dirname + "/public", //打包后的文件存放地方
        filename: "build.js" //打包后输出的文件的文件名
    },
    module: { //配置文件里添加json loader
        loaders: [
            {
                test: /\.json$/,
                loader: "json-loader"
            },
            {
                test: /\.js$/,
                exclude: /node_modules/,
                loader: 'babel-loader'
            },
            {
                test: /\.css$/,
                loader: 'style-loader!css-loader?modules!postcss-loader'
            }       
        ]
    }
}

插件(plugins)

loaders是在打包构建过程中用来处理源文件的(JSX,Scss,Less...),一次处理一个,插件并不直接操作单个文件,它直接对整个构建过程其作用。

webpack分为内置插件和第三方插件。在笔记二中对这两者有基础介绍。下一节笔记就正式开始参考官方进行vue的渐进学习。

更多文章请移步 http://www.yuki.kim

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

推荐阅读更多精彩内容