Storybook:簡(jiǎn)介卑雁、實(shí)例募书、填坑

1 什么是 Storybook

Storybook is an open source tool for developing UI components and pages in isolation. It simplifies building, documenting, and testing UIs.

Storybook 是一個(gè)開源工具,它能有組織和高效地構(gòu)建 UI 組件测蹲,文檔編制和測(cè)試莹捡,包括 React、Vue 和 Angular 扣甲。

特點(diǎn):

  • 分開展示各個(gè)組件不同屬性下的狀態(tài)篮赢;

  • 能追蹤組件的行為并且具有屬性調(diào)試的功能;

  • 可以為組件自動(dòng)生成文檔和屬性列表琉挖;

2 安裝

根據(jù)官網(wǎng)启泣,本人使用的 react 項(xiàng)目,所以示辈,直接控制臺(tái)運(yùn)行如下命令寥茫,集成 Storybook:本人安裝當(dāng)前最新版本為 "@storybook/react": "^6.2.9",

# Add Storybook:
npx sb init

安裝成功后,直接在控制臺(tái)運(yùn)行如下命令矾麻,就可以看到啟動(dòng)頁(yè)面:

# Starts Storybook in development mode
npm run storybook
成功啟動(dòng)運(yùn)行在 6006 端口

3 說明

  • 根目錄生成的 .storybook為 storybook 默認(rèn)配置目錄纱耻;

  • src/stories 目錄為 storybook 頁(yè)面組件目錄;

  • 本人項(xiàng)目是 ts险耀,安裝完成 storybook 后弄喘, storybook 頁(yè)面組件默認(rèn)就是 tsx,無需再額外配置甩牺;

4 decorators

decorators 的作用主要是統(tǒng)一修飾組件展示區(qū)域的樣式蘑志,例如:設(shè)置組件展示都居中,或者是 margin贬派、padding 的距離等等急但。

在對(duì)應(yīng)的組件配置如下:例如(xxx.stories.tsx,組件展示區(qū)域都距離 1em 邊距)

export default {
  title: 'components/Button',
  component: Button,
  decorators: [(storyFn) => <div style={{ margin: '1em' }}>{storyFn()}</div>],
};

詳細(xì)配置赠群,參考相關(guān)的官網(wǎng)說明文檔羊始。

5 parameters

parameters 通常是用于控制 Storybook 功能和插件的行為。詳細(xì)配置查描,參考相關(guān)的官網(wǎng)說明文檔突委。

簡(jiǎn)單給個(gè) Story parameters 例子:

export default {
  title: 'components/Button',
  component: Button,
  decorators: [(storyFn) => <div style={{ margin: '1em' }}>{storyFn()}</div>],
  parameters: {
    docs: {
      source: {
        code: 'Some custom string here',
        state: true,
      }
    }
  }
};

6 注釋

storybook 解析的組件柏卤,只要注釋符合 JSDoc 標(biāo)準(zhǔn),通過 docs 插件匀油,目前安裝的版本缘缚,應(yīng)該已經(jīng)集成了,組件就會(huì)被自動(dòng)解析敌蚜。

7 實(shí)例

說明:這只是個(gè)例子桥滨,樣式文件本人只是測(cè)試相關(guān)的 less 引用是否有問題,官網(wǎng) demo 給的示例弛车,組件樣式是使用 css齐媒,使用 less 或者 scss 需要額外的配置,上面有說明纷跛。

  • src/components/Button/Button.tsx
/*
 * Author: lin.zehong
 * Date: 2021-04-30 10:38:00
 * Desc: Button 組件
 */
import React from 'react';
import classnames from 'classnames';
import './Button.less';

export type ButtonType = 'default' | 'primary' | 'danger';

export type ButtonSize = 'lg' | 'sm';

interface IButtonProps {
  /**
   * 按鈕類型
   */
  btnType?: ButtonType;
  /**
   * 按鈕大小
   */
  size?: ButtonSize;
  /**
   * 按鈕自定義 className
   */
  className?: string;
  /**
   * 超鏈接按鈕
   */
  link?: string;
  /**
   * 按鈕是否不可以操作
   */
  disabled?: boolean;
  /**
   * 按鈕內(nèi)容
   */
  children?: React.ReactNode;
  /**
   * Optional click handler
   */
  onClick?: () => void;
}

// & 聯(lián)合屬性喻括,并關(guān)系; | 或者關(guān)系
type NativeButtonProps = IButtonProps & React.ButtonHTMLAttributes<HTMLElement>;
type AnchorButtonProps = IButtonProps & React.AnchorHTMLAttributes<HTMLElement>;

// Partial,把屬性都設(shè)置為可選
export type ButtonProps = Partial<NativeButtonProps & AnchorButtonProps>;

/**
 * 我的 Button 組件
*/
const Button: React.FC<ButtonProps> = (props) => {
  const { btnType, size, className, link, disabled, children, ...restProps } = props;

  const classes = classnames('btn', className, {
    [`btn-${btnType}`]: btnType,
    [`btn-${size}`]: size,
    [`btn-link`]: link,
    disabled: disabled && link,
  });

  if (link) {
    return (
      <a href={link} className={classes} {...restProps}>
        {children}
      </a>
    );
  }

  return (
    <button className={classes} disabled={disabled} {...restProps}>
      {children}
    </button>
  );
};

Button.defaultProps = {
  disabled: false,
  btnType: 'default',
  children: '按鈕'
};

export default Button;

  • src/components/Button/Button.less
@import '../../mixin.less';
@import '../../vartest.less';

.btn{
  .button-size(@btn-padding-y, @btn-padding-x, @btn-font-size, @btn-border-radius);
  position: relative;;
  display: inline-block;
  cursor: pointer;
  text-align: center;
  vertical-align: middle;
  white-space: nowrap;
  outline: none;
  font-weight: @btn-font-weight;
  font-family: @btn-font-family;
  line-height: @btn-line-height;
  border: @btn-border-width solid @border-color;
  background-image: none;
  background: transparent;
  box-shadow: @btn-box-shadow;
  transition: @btn-transition;
  &.disabled,
  &[disabled] {
    pointer-events: none;
    box-shadow: none;
    opacity: @btn-disabled-opacity;
    cursor: not-allowed;
  }
}

.btn-lg {
  .button-size(@btn-padding-y-lg, @btn-padding-x-lg, @btn-font-size-lg, @btn-border-radius-lg);
}

.btn-sm {
  .button-size(@btn-padding-y-sm, @btn-padding-x-sm, @btn-font-size-sm, @btn-border-radius-sm);
}

.btn-default {
  .button-style(@body-color, transparent, @border-color,  @primary,  transparent,  @primary);
}

.btn-primary {
  .button-style(@white, @primary, @primary);
}

.btn-danger {
  .button-style(@white, @danger, @danger);
}

.btn-link{
  border: none;
  box-shadow: none;
  color: @btn-link-color;
  text-decoration: @link-decoration;
  padding: 0;

  &:hover,
  &.hover,
  &:focus,
  &.focus{
    color: @btn-link-hover-color;
    border: none;
  }
  &.disabled{
    color: @btn-link-disabled-color;
    &:hover{
      text-decoration: none;
    }
  }
}

  • mixin.less
// 按鈕
.button-size(@padding-y, @padding-x, @font-size, @border-raduis) {
  padding: @padding-y @padding-x;
  font-size: @font-size;
  border-radius: @border-raduis;
}
.button-style(
  @color,
  @background,
  @border,
  @hover-color: lighten(@color, 10%),
  @hover-background: lighten(@background, 10%),
  @hover-border: lighten(@border, 10%),
) {
  color: @color;
  background: @background;
  border: @border-width solid @border;
  &:hover,
  &.hover {
    color: @hover-color;
    background: @hover-background;
    border: @border-width solid @hover-border;
  }
  // &:focus,
  // &.focus{
  //   color: @hover-color;
  //   background: @hover-background;
  //   border: @border-width solid @hover-border;
  // }
  &:active,
  &.active {
    color: @color;
    background: @background;
    border: @border-width solid @border;
  }
}
// 按鈕 end

// 動(dòng)畫
.animation-zoom(
  @direction: 'top',
  @scaleStart: scaleY(0),
  @scaleEnd: scaleY(1),
  @ransform-origin: center top,
) {
  .zoom-in-@{direction}-enter {
    opacity: 0;
    transform: @scaleStart;
  }
  .zoom-in-@{direction}-enter-active {
    opacity: 1;
    transform: @scaleEnd;
    transition: opacity 500ms cubic-bezier(0.23, 1, 0.32, 1),
      transform 500ms cubic-bezier(0.23, 1, 0.32, 1);
    transform-origin: @ransform-origin;
  }
  .zoom-in-@{direction}-exit {
    opacity: 1;
    transform: @scaleEnd;
  }
  .zoom-in-@{direction}-exit-active {
    opacity: 0;
    transform: @scaleStart;
    transition: opacity 500ms cubic-bezier(0.23, 1, 0.32, 1) 100ms,
      transform 500ms cubic-bezier(0.23, 1, 0.32, 1) 100ms;
    transform-origin: @ransform-origin;
  }
}
// 動(dòng)畫 end

  • vartest.less
  // 自定義顏色
  @white: #fff;
  @gray-100: #f8f9fa;
  @gray-200: #e9ecef;
  @gray-300: #dee2e6;
  @gray-400: #ced4da;
  @gray-500: #adb5bd;
  @gray-600: #6c757d;
  @gray-700: #495057;
  @gray-800: #343a40;
  @gray-900: #212529;
  @black: #000;

  @blue: #0d6efd;
  @indigo: #6610f2;
  @purple: #6f42c1;
  @pink: #d63384;
  @red: #dc3545;
  @orange: #fd7e14;
  @yellow: #fadb14;
  @green: #52c41a;
  @teal: #20c997;
  @cyan: #17a2b8;

  @primary: @blue;
  @secondary: @gray-600;
  @success: @green;
  @info: @cyan;
  @warning: @yellow;
  @danger: @red;
  @light: @gray-100;
  @dark: @gray-800;

  // @theme-colors: @primary; @secondary; @success; @info; @warning; @danger; @light; @dark;

  // 字體
  @font-family-sans-serif:
  '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"';
@font-family-monospace:
  'SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace';
@font-family-base: @font-family-sans-serif;


  // 字體大小
  @font-size-base: 1rem; // Assumes the browse;
  @font-size-lg: @font-size-base * 1.25;
  @font-size-sm: @font-size-base * .875;
  @font-size-root: null;

  // // 字重
  @font-weight-lighter: lighter;
  @font-weight-light: 300;
  @font-weight-normal: 400;
  @font-weight-bold: 700;
  @font-weight-bolder: bolder;
  @font-weight-base: @font-weight-normal;

  // // 行高
  @line-height-base: 1.5;
  @line-height-lg: 2;
  @line-height-sm: 1.25;

  // // 標(biāo)題大小
  @h1-font-size: @font-size-base * 2.5;
  @h2-font-size: @font-size-base * 2;
  @h3-font-size: @font-size-base * 1.75;
  @h4-font-size: @font-size-base * 1.5;
  @h5-font-size: @font-size-base * 1.25;
  @h6-font-size: @font-size-base;

  // // 鏈接
  @link-color: @primary;
  @link-decoration: none;
  @link-hover-color: lighten(@link-color; 15%);
  @link-hover-decoration: underline;

  // body
  @body-bg: @white;
  @body-color: @gray-900;
  @body-text-align: null;

  // Spacing
  @spacer: 1rem;

  // Paragraphs

  @paragraph-margin-bottom: 1rem;

  // 字體其他部分 heading list hr 等等
  @headings-margin-bottom: @spacer / 2;
  @headings-font-family: null;
  @headings-font-style: null;
  @headings-font-weight: 500;
  @headings-line-height: 1.2;
  @headings-color: null;

  @display1-size: 6rem;
  @display2-size: 5.5rem;
  @display3-size: 4.5rem;

  @display4-size: 3.5rem;
  @display1-weight: 300;
  @display2-weight: 300;
  @display3-weight: 300;
  @display4-weight: 300;
  @display-line-height: @headings-line-height;

  @lead-font-size: @font-size-base * 1.25;
  @lead-font-weight: 300;

  @small-font-size: .875em;

  @sub-sup-font-size: .75em;

  @text-muted: @gray-600;

  @initialism-font-size: @small-font-size;

  @blockquote-small-color: @gray-600;
  @blockquote-small-font-size: @small-font-size;
  @blockquote-font-size: @font-size-base * 1.25;

  @hr-color: inherit;
  @hr-height: 1px;
  @hr-opacity: .25;

  @legend-margin-bottom: .5rem;
  @legend-font-size: 1.5rem;
  @legend-font-weight: null;

  @mark-padding: .2em;

  @dt-font-weight: @font-weight-bold;

  @nested-kbd-font-weight: @font-weight-bold;

  @list-inline-padding: .5rem;

  @mark-bg: #fcf8e3;

  @hr-margin-y: @spacer;

  // Code

  @code-font-size: @small-font-size;
  @code-color: @pink;
  @pre-color: null;

  // options 可配置選項(xiàng)
  @enable-pointer-cursor-for-buttons: true;

  // 邊框 和 border radius

  @border-width: 1px;
  @border-color: @gray-300;

  @border-radius: .25rem;
  @border-radius-lg: .3rem;
  @border-radius-sm: .2rem;

  // 不同類型的 box shadow
  @box-shadow-sm: 0 .125rem .25rem rgba(@black; .075);
  @box-shadow: 0 .5rem 1rem rgba(@black; .15);
  @box-shadow-lg: 0 1rem 3rem rgba(@black; .175);
  @box-shadow-inset: inset 0 1px 2px rgba(@black; .075);

  // 按鈕
  // 按鈕基本屬性
  @btn-font-weight: 400;
  @btn-padding-y: .375rem;
  @btn-padding-x: .75rem;
  @btn-font-family: @font-family-base;
  @btn-font-size: @font-size-base;
  @btn-line-height: @line-height-base;

  //不同大小按鈕的 padding 和 font size
  @btn-padding-y-sm: .25rem;
  @btn-padding-x-sm: .5rem;
  @btn-font-size-sm: @font-size-sm;

  @btn-padding-y-lg: .5rem;
  @btn-padding-x-lg: 1rem;
  @btn-font-size-lg: @font-size-lg;

  // 按鈕邊框
  @btn-border-width: @border-width;

  // 按鈕其他
  @btn-box-shadow: inset 0 1px 0 rgba(@white; .15) 0 1px 1px rgba(@black; .075);
  @btn-disabled-opacity: .65;

  // 鏈接按鈕
  @btn-link-color: @link-color;
  @btn-link-hover-color: @link-hover-color;
  @btn-link-disabled-color: @gray-600;

  // 按鈕 radius
  @btn-border-radius: @border-radius;
  @btn-border-radius-lg: @border-radius-lg;
  @btn-border-radius-sm: @border-radius-sm;

  @btn-transition:
    color .15s ease-in-out, background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out;


  • src/components/Button/Button.stories.tsx
import React from 'react';
import { Story } from '@storybook/react';
import Button, { ButtonProps } from './Button';
import { action } from '@storybook/addon-actions'

//?? This default export determines where your story goes in the story list
export default {
  title: 'components/Button',
  component: Button,
  decorators: [(storyFn) => <div style={{ margin: '1em' }}>{storyFn()}</div>],
  // parameters: {docs: { previewSource: 'open' } }
  parameters: {
    docs: {
      source: {
        // code: 'Some custom string here',
        state: true,
      }
    }
  }
};

//?? We create a “template” of how args map to rendering
const Template: Story<ButtonProps> = (args) => <Button onClick={action('12222')} {...args} />;

// Template.parameters = {
//   docs: { previewSource: 'open' },
// }

export const FirstStory = Template.bind({});
FirstStory.args = {
  /*?? The args you need here will depend on your component */
  btnType: 'primary',
};

// export const DisabledButton = Template.bind({});
// DisabledButton.storyName = 'So simple!1';
// DisabledButton.args = {
//   /*?? The args you need here will depend on your component */
//   disabled: true,
// };

成功

8 填坑

8.1 less 不支持,需要配置

先不要急著安裝唬血,往下看,不然唤崭,啟動(dòng)會(huì)有相關(guān)的報(bào)錯(cuò)?胶蕖!谢肾!

yarn add style-loader css-loader less-loader

由于上面安裝的是最新的 less-loader 版本腕侄,本人裝完過后是 8.1.1的版本,啟動(dòng)項(xiàng)目后芦疏,出現(xiàn)了各種錯(cuò)誤兜挨,例如:

Cannot find module 'less'

Module build failed less-loader this.getOptions is not a function

但是,本人確定 less-loader 是安裝成功眯分,最后,發(fā)現(xiàn)問題是由于 less-loader版本過高柒桑,所以弊决,安裝了較低的版本后 yarn add less-loader@7.0.0,啟動(dòng)成功

  • .storybook / mian.js 配置
const path = require('path');

module.exports = {
  "stories": [
    "../src/**/*.stories.mdx",
    "../src/**/*.stories.@(js|jsx|ts|tsx)"
  ],
  "addons": [
    "@storybook/addon-links",
    "@storybook/addon-essentials"
  ],
  webpackFinal: async (config, { configType }) => {
    // `configType` has a value of 'DEVELOPMENT' or 'PRODUCTION'
    // You can change the configuration based on that.
    // 'PRODUCTION' is used when building the static version of storybook.

    // Make whatever fine-grained changes you need
    config.module.rules.push({
      test: /\.less$/,
      loaders: ['style-loader', 'css-loader', 'less-loader'],
      include: path.resolve(__dirname, '../src/')
    });

    // Return the altered config
    return config;
  },
}

擴(kuò)展:less 模塊化
由于項(xiàng)目都是使用 less 模塊化魁淳,所以飘诗,這里需要新增模塊化的配置,上面的配置更改為:

const path = require('path');

module.exports = {
  "stories": [
    "../src/**/*.stories.mdx",
    "../src/**/*.stories.@(js|jsx|ts|tsx)"
  ],
  "addons": [
    "@storybook/addon-links",
    "@storybook/addon-essentials"
  ],
  webpackFinal: async (config, { configType }) => {
    // `configType` has a value of 'DEVELOPMENT' or 'PRODUCTION'
    // You can change the configuration based on that.
    // 'PRODUCTION' is used when building the static version of storybook.

    // Make whatever fine-grained changes you need
    config.module.rules.push({
      test: /\.less$/,
      exclude: /node_modules/,
      // loaders: ['style-loader', 'css-loader', 'less-loader'],
      use: [
        {
          loader: 'style-loader'
        },
        {
          loader: 'css-loader',
          options: {
            modules: {
              localIdentName: '[local]_[hash:base64:5]'
            }
          }
        },
        {
          loader: 'less-loader'
        }
      ],
      include: path.resolve(__dirname, '../src/')
    });

    // Return the altered config
    return config;
  },
}

8.2 使用 yarn 安裝

本人使用 cnpm 安裝完依賴后界逛,一直啟動(dòng)不成功昆稿,要么就是項(xiàng)目啟動(dòng)有問題,要么就是 Storybook 啟動(dòng)有問題息拜,使用 yarn 安裝完成之后溉潭,問題都解決净响,所以,這里推薦使用 yarn 安裝喳瓣。

8.3 樣式問題

本人使用的框架是 umi馋贤,組件使用的主題色和變量相關(guān)的配置是在 theme.ts 配置文件,項(xiàng)目啟動(dòng)沒有問題畏陕,但是配乓,使用 Storybook 配置相關(guān)的組件,就找不到在 umi 配置文件 theme.ts 的相關(guān)變量惠毁,導(dǎo)致樣式相關(guān)錯(cuò)誤犹芹;

所以,變量和 mixin 等相關(guān)的樣式變量鞠绰,要放在單獨(dú)的 less 文件腰埂,方便 Storybook 配置對(duì)應(yīng)的組件引入樣式。

8.4 npx sb init 一直無法安裝相關(guān)的 react 依賴

根據(jù) Storybook 官網(wǎng) 說明洞豁,使用如下命令 npx 進(jìn)行安裝

# Add Storybook:
npx sb init

命令安裝下載默認(rèn)的配置文件 .storybook 和示例 src/stories盐固,如下圖:

接著檢查到為 react 項(xiàng)目,下載 storybook react 相關(guān)依賴丈挟,一直有問題刁卜,報(bào)各種文件已存在,不受 npm 控制等如下錯(cuò)誤:如下圖

解決方案:

通過手動(dòng)安裝 storybook react 相關(guān)依賴包曙咽,報(bào)錯(cuò)后蛔趴,不使用 npx sb init storybook cli 進(jìn)行安裝,storybook react 相關(guān)依賴包為:

cnpm i @storybook/react@6.2.9 -D
cnpm i @storybook/addon-links@6.2.9 -D
cnpm i @storybook/addon-essentials@6.2.9 -D
cnpm i @storybook/addon-actions@6.2.9 -D

最后例朱,在 package.json scripts 中孝情,添加對(duì)應(yīng)的命令 "storybook": "start-storybook -p 6006", "build-storybook": "build-storybook",如下:

  "scripts": {
    "start": "koi dev",
    "build": "koi build",
    "publish": "koi publish",
    "eslint": "eslint --fix --ext .js --ext .jsx --ext .ts --ext .tsx ./src",
    "lint-staged": "lint-staged",
    "test": "umi-test",
    "test:coverage": "umi-test --coverage",
    "storybook": "start-storybook -p 6006",
    "build-storybook": "build-storybook"
  },

添加完成后洒嗤,控制臺(tái)運(yùn)行命令 yarn run storybook箫荡,就可以看到成功的界面了。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末渔隶,一起剝皮案震驚了整個(gè)濱河市羔挡,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌间唉,老刑警劉巖绞灼,帶你破解...
    沈念sama閱讀 216,324評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異呈野,居然都是意外死亡低矮,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門被冒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來军掂,“玉大人轮蜕,你說我怎么就攤上這事×寄罚” “怎么了肠虽?”我有些...
    開封第一講書人閱讀 162,328評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)玛追。 經(jīng)常有香客問我税课,道長(zhǎng),這世上最難降的妖魔是什么痊剖? 我笑而不...
    開封第一講書人閱讀 58,147評(píng)論 1 292
  • 正文 為了忘掉前任韩玩,我火速辦了婚禮,結(jié)果婚禮上陆馁,老公的妹妹穿的比我還像新娘找颓。我一直安慰自己,他們只是感情好叮贩,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,160評(píng)論 6 388
  • 文/花漫 我一把揭開白布击狮。 她就那樣靜靜地躺著,像睡著了一般益老。 火紅的嫁衣襯著肌膚如雪彪蓬。 梳的紋絲不亂的頭發(fā)上坊罢,一...
    開封第一講書人閱讀 51,115評(píng)論 1 296
  • 那天胸蛛,我揣著相機(jī)與錄音缩抡,去河邊找鬼售淡。 笑死,一個(gè)胖子當(dāng)著我的面吹牛恩溅,可吹牛的內(nèi)容都是我干的造锅。 我是一名探鬼主播原献,決...
    沈念sama閱讀 40,025評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼态坦,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼盐数!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起伞梯,我...
    開封第一講書人閱讀 38,867評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤娘扩,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后壮锻,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,307評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡涮阔,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,528評(píng)論 2 332
  • 正文 我和宋清朗相戀三年猜绣,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片敬特。...
    茶點(diǎn)故事閱讀 39,688評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡掰邢,死狀恐怖牺陶,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情辣之,我是刑警寧澤掰伸,帶...
    沈念sama閱讀 35,409評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站怀估,受9級(jí)特大地震影響狮鸭,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜多搀,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,001評(píng)論 3 325
  • 文/蒙蒙 一歧蕉、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧康铭,春花似錦惯退、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至夷野,卻和暖如春懊蒸,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背扫责。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評(píng)論 1 268
  • 我被黑心中介騙來泰國(guó)打工榛鼎, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人鳖孤。 一個(gè)月前我還...
    沈念sama閱讀 47,685評(píng)論 2 368
  • 正文 我出身青樓者娱,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親苏揣。 傳聞我的和親對(duì)象是個(gè)殘疾皇子黄鳍,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,573評(píng)論 2 353

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