webpack 入門教程

webpack 是什么鹉胖?

本質上芬膝,webpack 是一個現(xiàn)代 JavaScript 應用程序的靜態(tài)模塊打包器(module bundler)悲没。當 webpack 處理應用程序時篮迎,它會遞歸地構建一個依賴關系圖(dependency graph),其中包含應用程序需要的每個模塊示姿,然后將所有這些模塊打包成一個或多個 bundle甜橱。

webpack

快速了解幾個基本的概念

mode 開發(fā)模式

webpack 提供 mode 配置選項,配置 webpack 相應模式的內置優(yōu)化栈戳。

// webpack.production.config.js
module.exports = {
+  mode: 'production',
}

入口文件(entry)

入口文件岂傲,類似于其他語言的起始文件。比如:c 語言的 main 函數(shù)所在的文件子檀。

入口起點(entry point)指示 webpack 應該使用哪個模塊镊掖,來作為構建其內部依賴圖的開始乃戈。進入入口起點后,webpack 會找出有哪些模塊和庫是入口起點(直接和間接)依賴的亩进。

可以在 webpack 的配置文件中配置入口症虑,配置節(jié)點為: entry,當然可以配置一個入口,也可以配置多個归薛。

輸出(output)

output 屬性告訴 webpack 在哪里輸出它所創(chuàng)建的 bundles谍憔,以及如何命名這些文件。

const path = require('path');

module.exports = {
  entry: './path/to/my/entry/file.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'my-first-webpack.bundle.js'
  }
};

loader

loader 讓 webpack 能夠去處理那些非 JavaScript 文件(webpack 自身只理解 JavaScript)主籍。loader 可以將所有類型的文件轉換為 webpack 能夠處理的有效模塊习贫,然后你就可以利用 webpack 的打包能力,對它們進行處理千元。

插件(plugins)

loader 被用于轉換某些類型的模塊沈条,而插件則可以用于執(zhí)行范圍更廣的任務。插件的范圍包括诅炉,從打包優(yōu)化和壓縮蜡歹,一直到重新定義環(huán)境中的變量。插件接口功能極其強大涕烧,可以用來處理各種各樣的任務月而。

webpack 的安裝

請確保安裝了 Node.js 的最新版本。而且已經(jīng)在您的項目根目錄下已經(jīng)初始化好了最基本的package.json文件

本地安裝 webpack

$ npm install --save-dev webpack

# 如果你使用 webpack 4+ 版本议纯,你還需要安裝 CLI父款。
npm install --save-dev webpack-cli

安裝完成后,可以添加npmscript腳本

// package.json
"scripts": {
    "start": "webpack --config webpack.config.js"
}

全局安裝 webpack(不推薦)

將使 webpack 在全局環(huán)境下可用:

npm install --global webpack

注意:不推薦全局安裝 webpack瞻凤。這會將你項目中的 webpack 鎖定到指定版本憨攒,并且在使用不同的 webpack 版本的項目中,可能會導致構建失敗阀参。

快速入門完整 demo

  • 第一步:創(chuàng)建項目結構

首先我們創(chuàng)建一個目錄肝集,初始化 npm,然后 在本地安裝 webpack蛛壳,接著安裝 webpack-cli(此工具用于在命令行中運行 webpack):

mkdir webpack-demo && cd webpack-demo
npm init -y
npm install webpack webpack-cli --save-dev

項目結構

  webpack-demo
+ |- package.json
+ |- /dist
+   |- index.html
+ |- /src
+   |- index.js
  • 第二步:安裝 loadash 依賴和編寫 js 文件
npm install --save lodash

編寫:src/index.js 文件

import _ from 'lodash';

function createDomElement() {
  let dom = document.createElement('div');
  dom.innerHTML = _.join(['aicoder', '.com', ' wow'], '');
  return dom;
}

document.body.appendChild(createDomElement());

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>起步</title>
</head>
<body>
  <script src="./main.js"></script>
</body>
</html>
  • 第三步:編寫 webpack 配置文件

根目錄下添加 webpack.config.js文件杏瞻。

  webpack-demo
  |- package.json
+ |- webpack.config.js
  |- /dist
    |- index.html
  |- /src
    |- index.js

webpack.config.js 內容如下:

const path = require('path');

module.exports = {
  mode: 'development',
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, './dist')
  }
};
  • 執(zhí)行構建任務

直接執(zhí)行構建任務:

npx webpack

打開: dist/index.html 可以查看到頁面的結果。

加載非 js 文件

webpack 最出色的功能之一就是衙荐,除了 JavaScript捞挥,還可以通過 loader 引入任何其他類型的文件

加載 CSS 文件

  • 第一步: 安裝 css 和 style 模塊解析的依賴 style-loadercss-loader
npm install --save-dev style-loader css-loader
  • 第二步: 添加 css 解析的 loader
const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      }
    ]
  }
};
  • css-loader: 輔助解析 js 中的 import './main.css'
  • style-loader: 把 js 中引入的 css 內容 注入到 html 標簽中,并添加 style 標簽.依賴 css-loader

你可以在依賴于此樣式的 js 文件中 導入樣式文件忧吟,比如:import './style.css'∑龊現(xiàn)在,當該 js 模塊運行時,含有 CSS 字符串的 <style> 標簽讹俊,將被插入到 html 文件的 <head>中垦沉。

  • 第三步: 編寫 css 文件和修改 js 文件

在 src 目錄中添加 style.css文件

 webpack-demo
  |- package.json
  |- webpack.config.js
  |- /dist
    |- bundle.js
    |- index.html
  |- /src
+   |- style.css
    |- index.js
  |- /node_modules

src/style.css

.hello {
  color: red;
}

修改 js 文件

  import _ from 'lodash';
+ import './style.css';

  function createDomElement() {
    let dom = document.createElement('div');
    dom.innerHTML = _.join(['aicoder', '.com', ' wow'], '');
+   dom.className = 'hello';
    return dom;
  }

  document.body.appendChild(createDomElement());

最后重新打開 dist 目錄下的 index.html 看一下文字是否變成了紅色的了。

module 配置補充

模塊(module): 這些選項決定了如何處理項目中的不同類型的模塊劣像。

webpack 模塊可以支持如下:

  • ES2015 import 語句
  • CommonJS require() 語句
  • AMD define 和 require 語句
  • css/sass/less 文件中的 @import 語句乡话。
  • 樣式(url(...))或 HTML 文件(<img src=...>)中的圖片鏈接(image url)

module.noParse

值的類型: RegExp | [RegExp] | function

防止 webpack 解析那些任何與給定正則表達式相匹配的文件。忽略的文件中不應該含有 import, require, define 的調用耳奕,或任何其他導入機制绑青。忽略大型的 library 可以提高構建性能。

module.exports = {
  mode: 'devleopment',
  entry: './src/index.js',
  ...
  module: {
    noParse: /jquery|lodash/,
    // 從 webpack 3.0.0 開始,可以使用函數(shù)屋群,如下所示
    // noParse: function(content) {
    //   return /jquery|lodash/.test(content);
    // }
  }
  ...
};

module.rules

創(chuàng)建模塊時闸婴,匹配請求的規(guī)則數(shù)組。這些規(guī)則能夠修改模塊的創(chuàng)建方式芍躏。這些規(guī)則能夠對模塊(module)應用 loader邪乍,或者修改解析器(parser)。

module.exports = {
  ...
  module: {
    noParse: /jquery|lodash/,
    rules: [
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      }
    ]
  }
  ...
};

module Rule

  • Rule 條件詳解
    • 字符串:匹配輸入必須以提供的字符串開始对竣。是的庇楞。目錄絕對路徑或文件絕對路徑。
    • 正則表達式:test 輸入值否纬。
    • 函數(shù):調用輸入的函數(shù)吕晌,必須返回一個真值(truthy value)以匹配。
    • 條件數(shù)組:至少一個匹配條件临燃。
    • 對象:匹配所有屬性睛驳。每個屬性都有一個定義行為。

Rule.test

  • { test: Condition }:匹配特定條件膜廊。一般是提供一個正則表達式或正則表達式的數(shù)組乏沸,但這不是強制的。
module.exports = {
  ...
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      }
    ]
  }
  ...
};

其他的條件比如:

  • { include: Condition }:匹配特定條件爪瓜。一般是提供一個字符串或者字符串數(shù)組蹬跃,但這不是強制的。
  • { exclude: Condition }:排除特定條件钥勋。一般是提供一個字符串或字符串數(shù)組炬转,但這不是強制的。
  • { and: [Condition] }:必須匹配數(shù)組中的所有條件
  • { or: [Condition] }:匹配數(shù)組中任何一個條件
  • { not: [Condition] }:必須排除這個條件
module.exports = {
  ...
  module: {
    rules: [
      {
        test: /\.css$/,
        include: [
          path.resolve(__dirname, "app/styles"),
          path.resolve(__dirname, "vendor/styles")
        ],
        use: ['style-loader', 'css-loader']
      }
    ]
  }
  ...
};

Rule.use

應用于模塊指定使用一個 loader算灸。

Loaders can be chained by passing multiple loaders, which will be applied from right to left (last to first configured).

加載器可以鏈式傳遞,從右向左進行應用到模塊上驻啤。

use: [
  'style-loader',
  {
    loader: 'css-loader'
  },
  {
    loader: 'less-loader',
    options: {
      noIeCompat: true
    }
  }
];

傳遞字符串(如:use: [ "style-loader" ])是 loader 屬性的簡寫方式(如:use: [ { loader: "style-loader "} ])菲驴。

加載 Sass 文件

加載 Sass 需要sass-loader

安裝

npm install sass-loader node-sass webpack --save-dev

使用:

// webpack.config.js
module.exports = {
  ...
  module: {
    rules: [{
      test: /\.scss$/,
      use: [{
        loader: "style-loader"
      }, {
        loader: "css-loader"
      }, {
        loader: "sass-loader"
      }]
    }]
  }
};

為 sass 文件注入內容:

如果你要將 Sass 代碼放在實際的入口文件(entry file)之前骑冗,可以設置 data 選項赊瞬。此時 sass-loader 不會覆蓋 data 選項先煎,只會將它拼接在入口文件的內容之前。

{
    loader: "sass-loader",
    options: {
        data: "$env: " + process.env.NODE_ENV + ";"
    }
}

注意:由于代碼注入, 會破壞整個入口文件的 source map巧涧。 通常一個簡單的解決方案是薯蝎,多個 Sass 文件入口。

創(chuàng)建 Source Map

css-loadersass-loader都可以通過該 options 設置啟用 sourcemap谤绳。

// webpack.config.js
module.exports = {
  ...
  module: {
    rules: [{
      test: /\.scss$/,
      use: [{
        loader: "style-loader"
      }, {
        loader: "css-loader",
        options: {
          sourceMap: true
        }
      }, {
        loader: "sass-loader",
        options: {
          sourceMap: true
        }
      }]
    }]
  }
};

PostCSS 處理 loader(附帶:添加 css3 前綴)

PostCSS是一個 CSS 的預處理工具占锯,可以幫助我們:給 CSS3 的屬性添加前綴,樣式格式校驗(stylelint)缩筛,提前使用 css 的新特性比如:表格布局消略,更重要的是可以實現(xiàn) CSS 的模塊化,防止 CSS 樣式?jīng)_突瞎抛。

我們常用的就是使用 PostCSS 進行添加前綴艺演,以此為例:

安裝

npm i -D postcss-loader
npm install autoprefixer --save-dev

# 以下可以不用安裝
# cssnext可以讓你寫CSS4的語言,并能配合autoprefixer進行瀏覽器兼容的不全桐臊,而且還支持嵌套語法
$ npm install postcss-cssnext --save-dev

# 類似scss的語法胎撤,實際上如果只是想用嵌套的話有cssnext就夠了
$ npm install precss --save-dev

# 在@import css文件的時候讓webpack監(jiān)聽并編譯
$ npm install postcss-import --save-dev
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
  mode: 'development',
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, './dist')
  },
  module: {
    rules: [
      {
        test: /\.(sa|sc|c)ss$/,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              sourceMap: true
            }
          },
          {
            loader: 'postcss-loader',
            options: {
              ident: 'postcss',
              sourceMap: true,
              plugins: loader => [
                require('autoprefixer')({ browsers: ['> 0.15% in CN'] }) // 添加前綴
              ]
            }
          },
          {
            loader: 'sass-loader',
            options: {
              sourceMap: true
            }
          }
        ]
      }
    ]
  }
};

樣式表抽離成專門的單獨文件并且設置版本號

首先以下的 css 的處理我們都把 mode 設置為 production

webpack4 開始使用: mini-css-extract-plugin插件, 1-3 的版本可以用: extract-text-webpack-plugin

抽取了樣式断凶,就不能再用 style-loader注入到 html 中了伤提。

npm install --save-dev mini-css-extract-plugin
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const devMode = process.env.NODE_ENV !== 'production'; // 判斷當前環(huán)境是開發(fā)環(huán)境還是 部署環(huán)境,主要是 mode屬性的設置值懒浮。

module.exports = {
  mode: 'development',
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, './dist')
  },
  module: {
    rules: [
      {
        test: /\.(sa|sc|c)ss$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          'postcss-loader',
          'sass-loader'
        ]
      }
    ]
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: devMode ? '[name].css' : '[name].[hash].css', // 設置最終輸出的文件名
      chunkFilename: devMode ? '[id].css' : '[id].[hash].css'
    })
  ]
};

再次運行打包:

在 dist 目錄中已經(jīng)把 css 抽取到單獨的一個 css 文件中了飘弧。修改 html,引入此 css 就能看到結果了砚著。

壓縮 CSS

webpack5 貌似會內置 css 的壓縮次伶,webpack4 可以自己設置一個插件即可。

壓縮 css 插件:optimize-css-assets-webpack-plugin

安裝

npm i -D optimize-css-assets-webpack-plugin
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const autoprefixer = require('autoprefixer');

module.exports = {
  mode: 'production',
  entry: './src/index.js',
  output: {
    filename: 'main.[hash].js',
    path: path.resolve(__dirname, './dist')
  },
  module: {
    rules: [
      {
        test: /\.(sa|sc|c)ss$/,
        use: [
          MiniCssExtractPlugin.loader,
          {
            loader: 'css-loader'
          },
          {
            loader: 'postcss-loader',
            options: {
              ident: 'postcss',
              plugins: loader => [autoprefixer({ browsers: ['> 0.15% in CN'] })]
            }
          },
          {
            loader: 'sass-loader'
          }
        ]
      }
    ]
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: '[name][hash].css',
      chunkFilename: '[id][hash].css'
    })
  ],
  optimization: {
    minimizer: [new OptimizeCSSAssetsPlugin({})]
  }
};

JS 壓縮

壓縮需要一個插件: uglifyjs-webpack-plugin, 此插件需要一個前提就是:mode: 'production'.

安裝

npm i -D uglifyjs-webpack-plugin
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const autoprefixer = require('autoprefixer');

module.exports = {
  mode: 'production',
  entry: './src/index.js',
  output: {
    filename: 'main.[hash].js',
    path: path.resolve(__dirname, './dist')
  },
  module: {
    rules: [
      {
        test: /\.(sa|sc|c)ss$/,
        use: [
          MiniCssExtractPlugin.loader,
          {
            loader: 'css-loader'
          },
          {
            loader: 'postcss-loader',
            options: {
              ident: 'postcss',
              plugins: loader => [autoprefixer({ browsers: ['> 0.15% in CN'] })]
            }
          },
          {
            loader: 'sass-loader'
          }
        ]
      }
    ]
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: '[name][hash].css',
      chunkFilename: '[id][hash].css'
    })
  ],
  optimization: {
    minimizer: [
      new UglifyJsPlugin({
        cache: true,
        parallel: true,
        sourceMap: true // set to true if you want JS source maps
      }),
      new OptimizeCSSAssetsPlugin({})
    ]
  }
};

解決 CSS 文件或者 JS 文件名字哈希變化的問題

HtmlWebpackPlugin插件稽穆,可以把打包后的 CSS 或者 JS 文件引用直接注入到 HTML 模板中冠王,這樣就不用每次手動修改文件引用了。

安裝

npm install --save-dev html-webpack-plugin
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const autoprefixer = require('autoprefixer');

module.exports = {
  mode: 'production',
  entry: './src/index.js',
  output: {
    filename: 'main.[hash].js',
    path: path.resolve(__dirname, './dist')
  },
  module: {
    rules: [
      {
        test: /\.(sa|sc|c)ss$/,
        use: [
          MiniCssExtractPlugin.loader,
          {
            loader: 'css-loader'
          },
          {
            loader: 'postcss-loader',
            options: {
              ident: 'postcss',
              plugins: loader => [autoprefixer({ browsers: ['> 0.15% in CN'] })]
            }
          },
          {
            loader: 'sass-loader'
          }
        ]
      }
    ]
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: '[name][hash].css',
      chunkFilename: '[id][hash].css'
    }),
    new HtmlWebpackPlugin({
      title: 'AICODER 全棧線下實習', // 默認值:Webpack App
      filename: 'main.html', // 默認值: 'index.html'
      template: path.resolve(__dirname, 'src/index.html'),
      minify: {
        collapseWhitespace: true,
        removeComments: true,
        removeAttributeQuotes: true // 移除屬性的引號
      }
    })
  ],
  optimization: {
    minimizer: [
      new UglifyJsPlugin({
        cache: true,
        parallel: true,
        sourceMap: true // set to true if you want JS source maps
      }),
      new OptimizeCSSAssetsPlugin({})
    ]
  }
};

清理 dist 目錄

每次構建舌镶,我們的 /dist 文件夾都會保存生成的文件柱彻,然后就會非常雜亂。

通常餐胀,在每次構建前清理 /dist 文件夾哟楷,是比較推薦的做法

clean-webpack-plugin 是一個比較普及的管理插件,讓我們安裝和配置下否灾。

npm install clean-webpack-plugin --save-dev

webpack.config.js

  const path = require('path');
  ....
+ const CleanWebpackPlugin = require('clean-webpack-plugin');

  module.exports = {
    entry: {
      app: './src/index.js',
      print: './src/print.js'
    },
    plugins: [
+     new CleanWebpackPlugin(['dist'])
      ...
    ],
    output: {
      filename: '[name].bundle.js',
      path: path.resolve(__dirname, 'dist')
    }
    ...
  };

現(xiàn)在執(zhí)行 npm run build卖擅,再檢查 /dist 文件夾。如果一切順利,你現(xiàn)在應該不會再看到舊的文件惩阶,只有構建后生成的文件挎狸!

加載圖片與圖片優(yōu)化

在 css 文件或者 sass 文件中添加如下代碼

$red: #900;
$size: 20px;

.box {
  height: 30px*2;
  font-size: $size;
  transform: translate3d( 0, 0, 0 );
+ background: url('../static/1.jpeg')
}

運行打包發(fā)現(xiàn)如下錯誤:

ERROR in ./src/static/1.jpeg 1:0
Module parse failed: Unexpected character '?' (1:0)
You may need an appropriate loader to handle this file type.

解決方案:file-loader處理文件的導入

npm install --save-dev file-loader

webpack.config.js

  const path = require('path');

  module.exports = {
    entry: './src/index.js',
    output: {
      filename: 'bundle.js',
      path: path.resolve(__dirname, 'dist')
    },
    module: {
      rules: [
        {
          test: /\.css$/,
          use: [
            'style-loader',
            'css-loader'
          ]
        },
+       {
+         test: /\.(png|svg|jpg|gif)$/,
+         use: [
+           'file-loader'
+         ]
+       }
      ]
    }
  };

此時運行打包,發(fā)現(xiàn) dist 目錄多了一個圖片文件断楷,另外報錯不再出現(xiàn)锨匆。

那更進一步,圖片如何進行優(yōu)化呢冬筒?

image-webpack-loader可以幫助我們對圖片進行壓縮和優(yōu)化恐锣。

npm install image-webpack-loader --save-dev

使用:webpack.config.js

  const path = require('path');

  module.exports = {
    entry: './src/index.js',
    output: {
      filename: 'bundle.js',
      path: path.resolve(__dirname, 'dist')
    },
    module: {
      rules: [
        {
          test: /\.css$/,
          use: [
            'style-loader',
            'css-loader'
          ]
        },
        {
          test: /\.(png|svg|jpg|gif|jpeg|ico)$/,
          use: [
            'file-loader',
+           {
+             loader: 'image-webpack-loader',
+             options: {
+               mozjpeg: {
+                 progressive: true,
+                 quality: 65
+               },
+               optipng: {
+                 enabled: false,
+               },
+               pngquant: {
+                 quality: '65-90',
+                 speed: 4
+               },
+               gifsicle: {
+                 interlaced: false,
+               },
+               webp: {
+                 quality: 75
+               }
+             }
+           },
          ]
        }
      ]
    }
  };

此時在運行 webpack,發(fā)現(xiàn)會 生成的圖片的大小會被壓縮很多账千。

更進一步處理圖片成 base64

url-loader功能類似于 file-loader侥蒙,可以把 url 地址對應的文件,打包成 base64 的 DataURL匀奏,提高訪問的效率鞭衩。

如何使用:

npm install --save-dev url-loader

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.(png|svg|jpg|gif|jpeg|ico|woff|woff2|eot|ttf|otf)$/,
        use: [
          {
            loader: 'url-loader', // 根據(jù)圖片大小,把圖片優(yōu)化成base64
            options: {
              limit: 10000
            }
          },
          {
            loader: 'image-webpack-loader', // 先進行圖片優(yōu)化
            options: {
              mozjpeg: {
                progressive: true,
                quality: 65
              },
              optipng: {
                enabled: false
              },
              pngquant: {
                quality: '65-90',
                speed: 4
              },
              gifsicle: {
                interlaced: false
              },
              webp: {
                quality: 75
              }
            }
          }
        ]
      }
    ]
  }
};

字體的處理(同圖片)

由于 css 中可能引用到自定義的字體娃善,處理也是跟圖片一致论衍。

const path = require('path');

  module.exports = {
    entry: './src/index.js',
    output: {
      filename: 'bundle.js',
      path: path.resolve(__dirname, 'dist')
    },
    module: {
      rules: [
        {
          test: /\.css$/,
          use: [
            'style-loader',
            'css-loader'
          ]
        },
        {
          test: /\.(png|svg|jpg|gif)$/,
          use: [
            'file-loader'
          ]
        },
+       {
+         test: /\.(woff|woff2|eot|ttf|otf)$/,
+         use: [
+           'file-loader'
+         ]
+       }
      ]
    }
  };

開發(fā)相關輔助

js 使用 source map

當 webpack 打包源代碼時,可能會很難追蹤到錯誤和警告在源代碼中的原始位置聚磺。例如坯台,如果將三個源文件(a.js, b.js 和 c.js)打包到一個 bundle(bundle.js)中,而其中一個源文件包含一個錯誤瘫寝,那么堆棧跟蹤就會簡單地指向到 bundle.js蜒蕾。

使用 inline-source-map 選項,這有助于解釋說明 js 原始出錯的位置焕阿。(不要用于生產(chǎn)環(huán)境):

webpack.config.js

  const path = require('path');
  const HtmlWebpackPlugin = require('html-webpack-plugin');
  const CleanWebpackPlugin = require('clean-webpack-plugin');

  module.exports = {
    entry: {
      app: './src/index.js',
      print: './src/print.js'
    },
+   devtool: 'inline-source-map',
    plugins: [
      new CleanWebpackPlugin(['dist']),
      new HtmlWebpackPlugin({
        title: 'Development'
      })
    ],
    output: {
      filename: '[name].bundle.js',
      path: path.resolve(__dirname, 'dist')
    }
  };
inline-source-map

監(jiān)控文件變化咪啡,自動編譯。使用觀察模式

每次修改完畢后暮屡,都手動編譯異常痛苦撤摸。最簡單解決的辦法就是啟動watch

npx webpack --watch

當然可以添加到 npm 的 script 中

package.json

{
    "name": "development",
    "version": "1.0.0",
    "description": "",
    "main": "webpack.config.js",
    "scripts": {
      "test": "echo \"Error: no test specified\" && exit 1",
+     "watch": "npx webpack --watch",
      "build": "npx webpack"
    },
    "devDependencies": {
      "clean-webpack-plugin": "^0.1.16",
      "css-loader": "^0.28.4",
      "csv-loader": "^2.1.1",
      "file-loader": "^0.11.2",
      "html-webpack-plugin": "^2.29.0",
      "style-loader": "^0.18.2",
      "webpack": "^3.0.0",
      "xml-loader": "^1.2.1"
    }
  }

但是有個 bug褒纲,就是每次我們修改 js 或者 css 文件后准夷,要看到修改后的 html 的變化,需要我自己重新刷新頁面莺掠。

如何能不刷新頁面衫嵌,自動更新變化呢?

使用 webpack-dev-server 和熱更新

webpack-dev-server 為你提供了一個簡單的 web 服務器彻秆,并且能夠實時重新加載(live reloading)渐扮。

安裝

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

webpack.config.js

  const path = require('path');
  const HtmlWebpackPlugin = require('html-webpack-plugin');
  const CleanWebpackPlugin = require('clean-webpack-plugin');

  module.exports = {
    entry: {
      app: './src/index.js',
      print: './src/print.js'
    },
    devtool: 'inline-source-map',
+   devServer: {
+     contentBase: './dist'
+   },
    plugins: [
      new CleanWebpackPlugin(['dist']),
      new HtmlWebpackPlugin({
        title: 'Development'
      })
    ],
    output: {
      filename: '[name].bundle.js',
      path: path.resolve(__dirname, 'dist')
    }
  };

啟動此 webserver:

webpack-dev-server --open

官網(wǎng)其他配置

devServer: {
  clientLogLevel: 'warning', // 可能的值有 none, error, warning 或者 info(默認值)
  hot: true,  // 啟用 webpack 的模塊熱替換特性, 這個需要配合: webpack.HotModuleReplacementPlugin插件
  contentBase:  path.join(__dirname, "dist"), // 告訴服務器從哪里提供內容论悴, 默認情況下掖棉,將使用當前工作目錄作為提供內容的目錄
  compress: true, // 一切服務都啟用gzip 壓縮
  host: '0.0.0.0', // 指定使用一個 host墓律。默認是 localhost。如果你希望服務器外部可訪問 0.0.0.0
  port: 8080, // 端口
  open: true, // 是否打開瀏覽器
  overlay: {  // 出現(xiàn)錯誤或者警告的時候幔亥,是否覆蓋頁面線上錯誤消息耻讽。
    warnings: true,
    errors: true
  },
  publicPath: '/', // 此路徑下的打包文件可在瀏覽器中訪問。
  proxy: {  // 設置代理
    "/api": {  // 訪問api開頭的請求帕棉,會跳轉到  下面的target配置
      target: "http://192.168.0.102:8080",
      pathRewrite: {"^/api" : "/mockjsdata/5/api"}
    }
  },
  quiet: true, // necessary for FriendlyErrorsPlugin. 啟用 quiet 后针肥,除了初始啟動信息之外的任何內容都不會被打印到控制臺。這也意味著來自 webpack 的錯誤或警告在控制臺不可見香伴。
  watchOptions: { // 監(jiān)視文件相關的控制選項
    poll: true,   // webpack 使用文件系統(tǒng)(file system)獲取文件改動的通知慰枕。在某些情況下,不會正常工作即纲。例如具帮,當使用 Network File System (NFS) 時。Vagrant 也有很多問題低斋。在這些情況下蜂厅,請使用輪詢. poll: true。當然 poll也可以設置成毫秒數(shù)膊畴,比如:  poll: 1000
    ignored: /node_modules/, // 忽略監(jiān)控的文件夾掘猿,正則
    aggregateTimeout: 300 // 默認值,當?shù)谝粋€文件更改唇跨,會在重新構建前增加延遲
  }
}

如何啟用熱更新呢稠通?

webpack.config.js

  const path = require('path');
  const HtmlWebpackPlugin = require('html-webpack-plugin');
  const CleanWebpackPlugin = require('clean-webpack-plugin');
+ const webpack = require('webpack');

  module.exports = {
    entry: {
       app: './src/index.js'
    },
    devtool: 'inline-source-map',
    devServer: {
      contentBase: './dist',
+     hot: true
    },
    plugins: [
      new CleanWebpackPlugin(['dist']),
      new HtmlWebpackPlugin({
        title: 'Hot Module Replacement'
      }),
+     new webpack.NamedModulesPlugin(),  // 更容易查看(patch)的依賴
+     new webpack.HotModuleReplacementPlugin()  // 替換插件
    ],
    output: {
      filename: '[name].bundle.js',
      path: path.resolve(__dirname, 'dist')
    }
  };

JS啟用babel轉碼

雖然現(xiàn)代的瀏覽器已經(jīng)兼容了96%以上的ES6的語法了,但是為了兼容老式的瀏覽器(IE8买猖、9)我們需要把最新的ES6的語法轉成ES5的改橘。那么babel的loader就出場了。

安裝

npm i -D babel-loader babel-core babel-preset-env

用法

在webpack的配置文件中政勃,添加js的處理模塊唧龄。

module: {
  rules: [
    {
      test: /\.js$/,
      exclude: /(node_modules)/,  // 加快編譯速度,不包含node_modules文件夾內容
      use: {
        loader: 'babel-loader'
      }
    }
  ]
}

然后奸远,在項目根目錄下既棺,添加babel的配置文件 .babelrc.

.babelrc文件如下:

{
  "presets": ["env"]
}

最后,在入口js文件中懒叛,添加ES6的?新語法:

class Temp {
  show() {
    console.log('this.Age :', this.Age);
  }
  get Age() {
    return this._age;
  }
  set Age(val) {
    this._age = val + 1;
  }
}

let t = new Temp();
t.Age = 19;

t.show();

最后打包:

npx webpack

最終打包后的js代碼:

var a = 1,
    b = 3,
    c = 9;

console.log('a :', a);
console.log('b :', b);
console.log('c :', c);

var Temp = function () {
  function Temp() {
    _classCallCheck(this, Temp);
  }

  _createClass(Temp, [{
    key: 'show',
    value: function show() {
      console.log('this.Age :', this.Age);
    }
  }, {
    key: 'Age',
    get: function get() {
      return this._age;
    },
    set: function set(val) {
      this._age = val + 1;
    }
  }]);

  return Temp;
}();

var t = new Temp();
t.Age = 19;

t.show();

Babel優(yōu)化

babel-loader可以配置如下幾個options:

  • cacheDirectory:默認值為 false丸冕。當有設置時,指定的目錄將用來緩存 loader 的執(zhí)行結果薛窥。之后的 webpack 構建胖烛,將會嘗試讀取緩存眼姐,來避免在每次執(zhí)行時,可能產(chǎn)生的佩番、高性能消耗的 Babel 重新編譯過程(recompilation process)众旗。如果設置了一個空值 (loader: 'babel-loader?cacheDirectory') 或者 true (loader: babel-loader?cacheDirectory=true),loader 將使用默認的緩存目錄 node_modules/.cache/babel-loader趟畏,如果在任何根目錄下都沒有找到 node_modules 目錄贡歧,將會降級回退到操作系統(tǒng)默認的臨時文件目錄。

  • cacheIdentifier:默認是一個由 babel-core 版本號赋秀,babel-loader 版本號利朵,.babelrc 文件內容(存在的情況下),環(huán)境變量 BABEL_ENV 的值(沒有時降級到 NODE_ENV)組成的字符串猎莲∩艿埽可以設置為一個自定義的值,在 identifier 改變后著洼,強制緩存失效樟遣。

  • forceEnv:默認將解析 BABEL_ENV 然后是 NODE_ENV。允許你在 loader 級別上覆蓋 BABEL_ENV/NODE_ENV郭脂。對有不同 babel 配置的年碘,客戶端和服務端同構應用非常有用。

注意:sourceMap 選項是被忽略的展鸡。當 webpack 配置了 sourceMap 時(通過 devtool 配置選項)屿衅,將會自動生成 sourceMap。

babel 在每個文件都插入了輔助代碼莹弊,使代碼體積過大.babel 對一些公共方法使用了非常小的輔助代碼涤久,比如 _extend。 默認情況下會被添加到每一個需要它的文件中忍弛。你可以引入 babel runtime 作為一個獨立模塊响迂,來避免重復引入。

安裝:

npm install babel-plugin-transform-runtime --save-dev
npm install babel-runtime --save

配置:

webpack.config.js

rules: [
  // 'transform-runtime' 插件告訴 babel 要引用 runtime 來代替注入细疚。
  {
    test: /\.js$/,
    exclude: /(node_modules|bower_components)/,
    use: {
      loader: 'babel-loader',
    }
  }
]

修改.babelrc

{
  "presets": ["env"],
  "plugins": [
    ["transform-runtime", {
      "helpers": true,
      "polyfill": true,
      "regenerator": true,
      "moduleName": "babel-runtime"
    }]
  ]
}

此時蔗彤,webpack打包的時候,會自動優(yōu)化重復引入公共方法的問題疯兼。

ESLint校驗代碼格式規(guī)范

安裝

npm install eslint --save-dev
npm install eslint-loader --save-dev

# 以下是用到的額外的需要安裝的eslint的解釋器然遏、校驗規(guī)則等
npm i -D babel-eslint standard

使用

// webpack.config.js
module.exports = {
  // ...
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: "eslint-loader",
        options: {
          // eslint options (if necessary)
          fix: true
        }
      },
    ],
  },
  // ...
}

eslint配置可以直接放到webpack的配置文件中,也可以直接放到項目根目錄的 .eslintrc文檔吧彪。

// .eslintrc.js
// https://eslint.org/docs/user-guide/configuring
module.exports = {
  root: true,
  parserOptions: {
    parser: 'babel-eslint'
  },
  env: {
    browser: true
  },
  extends: [
    // https://github.com/standard/standard/blob/master/docs/RULES-en.md
    'standard'
  ],
  globals: {
    NODE_ENV: false
  },
  rules: {
    // allow async-await
    'generator-star-spacing': 'off',
    // allow debugger during development
    'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
    // 添加待侵,分號必須
    semi: ['error', 'always'],
    'no-unexpected-multiline': 'off',
    'space-before-function-paren': ['error', 'never'],
    // 'quotes': ["error", "double", { "avoidEscape": true }]
    quotes: [
      'error',
      'single',
      {
        avoidEscape: true
      }
    ]
  }
};

此時eslint的配置就結束了。

到此為止姨裸,一個完整的開發(fā)階段的webpack的配置文件

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CleanWebpackPlugin = require('clean-webpack-plugin');
const autoprefixer = require('autoprefixer');
const webpack = require('webpack');

module.exports = {
  mode: 'development',
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, './dist')
  },
  devtool: 'inline-source-map',
  devServer: {
    clientLogLevel: 'warning', // 可能的值有 none, error, warning 或者 info(默認值)
    hot: true, // 啟用 webpack 的模塊熱替換特性, 這個需要配合: webpack.HotModuleReplacementPlugin插件
    contentBase: path.join(__dirname, "dist"), // 告訴服務器從哪里提供內容秧倾, 默認情況下怨酝,將使用當前工作目錄作為提供內容的目錄
    compress: true, // 一切服務都啟用gzip 壓縮
    host: '0.0.0.0', // 指定使用一個 host。默認是 localhost那先。如果你希望服務器外部可訪問 0.0.0.0
    port: 8085, // 端口
    open: true, // 是否打開瀏覽器
    overlay: { // 出現(xiàn)錯誤或者警告的時候农猬,是否覆蓋頁面線上錯誤消息。
      warnings: true,
      errors: true
    },
    publicPath: '/', // 此路徑下的打包文件可在瀏覽器中訪問胃榕。
    proxy: { // 設置代理
      "/api": { // 訪問api開頭的請求盛险,會跳轉到  下面的target配置
        target: "http://192.168.0.102:8080",
        pathRewrite: {
          "^/api": "/mockjsdata/5/api"
        }
      }
    },
    quiet: true, // necessary for FriendlyErrorsPlugin. 啟用 quiet 后,除了初始啟動信息之外的任何內容都不會被打印到控制臺勋又。這也意味著來自 webpack 的錯誤或警告在控制臺不可見。
    watchOptions: { // 監(jiān)視文件相關的控制選項
      poll: true, // webpack 使用文件系統(tǒng)(file system)獲取文件改動的通知换帜。在某些情況下楔壤,不會正常工作。例如惯驼,當使用 Network File System (NFS) 時蹲嚣。Vagrant 也有很多問題。在這些情況下祟牲,請使用輪詢. poll: true隙畜。當然 poll也可以設置成毫秒數(shù),比如:  poll: 1000
      ignored: /node_modules/, // 忽略監(jiān)控的文件夾说贝,正則
      aggregateTimeout: 300 // 默認值议惰,當?shù)谝粋€文件更改,會在重新構建前增加延遲
    }
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /(node_modules)/, // 加快編譯速度乡恕,不包含node_modules文件夾內容
        use: [{
          loader: 'babel-loader'
        },{
          loader: 'eslint-loader',
          options: {
            fix: true
          }
        }]
      },
      {
        test: /\.(sa|sc|c)ss$/,
        use: [
          'style-loader', {
            loader: 'css-loader',
            options: {
              sourceMap: true
            }
          }, {
            loader: 'postcss-loader',
            options: {
              ident: 'postcss',
              sourceMap: true,
              plugins: (loader) => [autoprefixer({browsers: ['> 0.15% in CN']})]
            }
          }, {
            loader: 'sass-loader',
            options: {
              sourceMap: true
            }
          }
        ]
      }, {
        test: /\.(woff|woff2|eot|ttf|otf)$/,
        use: [
          {
            loader: 'url-loader',
            options: {
              limit: 10000
            }
          }
        ]
      }, {
        test: /\.(png|svg|jpg|gif|jpeg|ico)$/,
        use: [
          {
            loader: 'url-loader',
            options: {
              limit: 10000
            }
          }, {
            loader: 'image-webpack-loader',
            options: {
              mozjpeg: {
                progressive: true,
                quality: 65
              },
              optipng: {
                enabled: false
              },
              pngquant: {
                quality: '65-90',
                speed: 4
              },
              gifsicle: {
                interlaced: false
              },
              webp: {
                quality: 75
              }
            }
          }
        ]
      }
    ]
  },
  plugins: [
    new MiniCssExtractPlugin({filename: '[name].css', chunkFilename: '[id].css'}),
    new CleanWebpackPlugin(['dist']),
    new webpack.NamedModulesPlugin(), // 更容易查看(patch)的依賴
    new webpack.HotModuleReplacementPlugin(), // 替換插件
    new HtmlWebpackPlugin({
      title: 'AICODER 全棧線下實習', // 默認值:Webpack App
      filename: 'index.html', // 默認值: 'index.html'
      minify: {
        collapseWhitespace: true,
        removeComments: true,
        removeAttributeQuotes: true, // 移除屬性的引號
      },
      template: path.resolve(__dirname, 'src/index.html')
    })
  ],
  optimization: {}
};

用于生產(chǎn)環(huán)境的配置

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const UglifyJsPlugin = require("uglifyjs-webpack-plugin");
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
const CleanWebpackPlugin = require('clean-webpack-plugin');
const autoprefixer = require('autoprefixer');

module.exports = {
  mode: 'production',
  entry: './src/index.js',
  output: {
    filename: 'main.[hash].js',
    path: path.resolve(__dirname, './dist')
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /(node_modules)/, // 加快編譯速度言询,不包含node_modules文件夾內容
        use: [{
          loader: 'babel-loader'
        },{
          loader: 'eslint-loader',
          options: {
            fix: true
          }
        }]
      },
      {
        test: /\.(sa|sc|c)ss$/,
        use: [
          MiniCssExtractPlugin.loader, {
            loader: 'css-loader'
          }, {
            loader: 'postcss-loader',
            options: {
              ident: 'postcss',
              plugins: (loader) => [autoprefixer({browsers: ['> 0.15% in CN']})]
            }
          }, {
            loader: 'sass-loader'
          }
        ]
      }, {
        test: /\.(woff|woff2|eot|ttf|otf)$/,
        use: [
          {
            loader: 'url-loader',
            options: {
              limit: 10000
            }
          }
        ]
      }, {
        test: /\.(png|svg|jpg|gif|jpeg|ico)$/,
        use: [
          'file-loader', {
            loader: 'image-webpack-loader',
            options: {
              mozjpeg: {
                progressive: true,
                quality: 65
              },
              optipng: {
                enabled: false
              },
              pngquant: {
                quality: '65-90',
                speed: 4
              },
              gifsicle: {
                interlaced: false
              },
              webp: {
                quality: 75
              }
            }
          }
        ]
      }
    ]
  },
  plugins: [
    new MiniCssExtractPlugin({filename: '[name][hash].css', chunkFilename: '[id][hash].css'}),
    new CleanWebpackPlugin(['dist']),
    new HtmlWebpackPlugin({
      title: 'AICODER 全棧線下實習', // 默認值:Webpack App
      filename: 'index.html', // 默認值: 'index.html'
      template: path.resolve(__dirname, 'src/index.html'),
      minify: {
        collapseWhitespace: true,
        removeComments: true,
        removeAttributeQuotes: true, // 移除屬性的引號
      }
    })
  ],
  optimization: {
    minimizer: [
      new UglifyJsPlugin({
        cache: true, parallel: true, sourceMap: true // set to true if you want JS source maps
      }),
      new OptimizeCSSAssetsPlugin({})
    ]
  }
};

相關的loader列表

webpack 可以使用 loader 來預處理文件。這允許你打包除 JavaScript 之外的任何靜態(tài)資源傲宜。你可以使用 Node.js 來很簡單地編寫自己的 loader运杭。

文件

  • raw-loader 加載文件原始內容(utf-8)
  • val-loader 將代碼作為模塊執(zhí)行,并將 exports 轉為 JS 代碼
  • url-loader 像 file loader 一樣工作函卒,但如果文件小于限制辆憔,可以返回 data URL
  • file-loader 將文件發(fā)送到輸出文件夾,并返回(相對)URL

JSON

  • json-loader 加載 JSON 文件(默認包含)
  • json5-loader 加載和轉譯 JSON 5 文件
  • cson-loader 加載和轉譯 CSON 文件

轉換編譯(Transpiling)

  • script-loader 在全局上下文中執(zhí)行一次 JavaScript 文件(如在 script 標簽)报嵌,不需要解析
  • babel-loader 加載 ES2015+ 代碼虱咧,然后使用 Babel 轉譯為 ES5
  • buble-loader 使用 Bublé 加載 ES2015+ 代碼,并且將代碼轉譯為 ES5
  • traceur-loader 加載 ES2015+ 代碼沪蓬,然后使用 Traceur 轉譯為 ES5
  • ts-loaderawesome-typescript-loader 像 JavaScript 一樣加載 TypeScript 2.0+
  • coffee-loader 像 JavaScript 一樣加載 CoffeeScript

模板(Templating)

  • html-loader 導出 HTML 為字符串彤钟,需要引用靜態(tài)資源
  • pug-loader 加載 Pug 模板并返回一個函數(shù)
  • jade-loader 加載 Jade 模板并返回一個函數(shù)
  • markdown-loader 將 Markdown 轉譯為 HTML
  • react-markdown-loader 使用 markdown-parse parser(解析器) 將 Markdown 編譯為 React 組件
  • posthtml-loader 使用 PostHTML 加載并轉換 HTML 文件
  • handlebars-loader 將 Handlebars 轉移為 HTML
  • markup-inline-loader 將內聯(lián)的 SVG/MathML 文件轉換為 HTML。在應用于圖標字體跷叉,或將 CSS 動畫應用于 SVG 時非常有用逸雹。

樣式

  • style-loader 將模塊的導出作為樣式添加到 DOM 中
  • css-loader 解析 CSS 文件后营搅,使用 import 加載,并且返回 CSS 代碼
  • less-loader 加載和轉譯 LESS 文件
  • sass-loader 加載和轉譯 SASS/SCSS 文件
  • postcss-loader 使用 PostCSS 加載和轉譯 CSS/SSS 文件
  • stylus-loader 加載和轉譯 Stylus 文件

清理和測試(Linting && Testing)

  • mocha-loader 使用 mocha 測試(瀏覽器/NodeJS)
  • eslint-loader PreLoader梆砸,使用 ESLint 清理代碼
  • jshint-loader PreLoader转质,使用 JSHint 清理代碼
  • jscs-loader PreLoader,使用 JSCS 檢查代碼樣式
  • coverjs-loader PreLoader帖世,使用 CoverJS 確定測試覆蓋率

框架(Frameworks)

  • vue-loader 加載和轉譯 Vue 組件
  • polymer-loader 使用選擇預處理器(preprocessor)處理休蟹,并且 require() 類似一等模塊(first-class)的 Web 組件
  • angular2-template-loader 加載和轉譯 Angular 組件
  • Awesome 更多第三方 loader,查看 awesome-webpack 列表日矫。

other

webpack還是有很多其他需要學習的內容赂弓。 請參考官網(wǎng),或者研究一下vue-cli的生成的webpack的相關配置哪轿,也很值得學習盈魁。

另外其他腳手架生成的相關配置都可以研究一下比如:create-react-appyo

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末窃诉,一起剝皮案震驚了整個濱河市杨耙,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌飘痛,老刑警劉巖珊膜,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異宣脉,居然都是意外死亡车柠,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進店門脖旱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來堪遂,“玉大人,你說我怎么就攤上這事萌庆∪芡剩” “怎么了?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵践险,是天一觀的道長猿妈。 經(jīng)常有香客問我,道長巍虫,這世上最難降的妖魔是什么彭则? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮占遥,結果婚禮上俯抖,老公的妹妹穿的比我還像新娘温峭。我一直安慰自己参滴,他們只是感情好,可當我...
    茶點故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著疙筹,像睡著了一般简逮。 火紅的嫁衣襯著肌膚如雪鸟赫。 梳的紋絲不亂的頭發(fā)上师郑,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天,我揣著相機與錄音漫蛔,去河邊找鬼嗜愈。 笑死,一個胖子當著我的面吹牛莽龟,可吹牛的內容都是我干的蠕嫁。 我是一名探鬼主播,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼轧房,長吁一口氣:“原來是場噩夢啊……” “哼拌阴!你這毒婦竟也來了?” 一聲冷哼從身側響起奶镶,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎陪拘,沒想到半個月后厂镇,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡左刽,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年捺信,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片欠痴。...
    茶點故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡迄靠,死狀恐怖,靈堂內的尸體忽然破棺而出喇辽,到底是詐尸還是另有隱情掌挚,我是刑警寧澤,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布菩咨,位于F島的核電站吠式,受9級特大地震影響,放射性物質發(fā)生泄漏抽米。R本人自食惡果不足惜特占,卻給世界環(huán)境...
    茶點故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望云茸。 院中可真熱鬧是目,春花似錦、人聲如沸标捺。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至长踊,卻和暖如春功舀,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背身弊。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工辟汰, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人阱佛。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓帖汞,卻偏偏與公主長得像,于是被迫代替她去往敵國和親凑术。 傳聞我的和親對象是個殘疾皇子翩蘸,可洞房花燭夜當晚...
    茶點故事閱讀 42,802評論 2 345