webpack4入門

一、什么是webpack盛泡,為什么使用它闷祥?

1.1 什么是webpack?

webpack是一個模塊打包工具傲诵,在開發(fā)中凯砍,各種各樣的資源都可以認為是一種獨特的模塊資源,比如css拴竹,js悟衩,png,json等栓拜。而我們通過webpack座泳,可以將這些資源打包壓縮在指定的文件中,需要注意的是幕与,webpack本身是支持js資源的挑势,但我們可以通過loader加載器,來打包除了js之外的資源啦鸣。

1.2 為什么使用它潮饱?

如果像以前開發(fā)時一個html文件可能會引用十幾個js文件,而且順序還不能亂,因為它們存在依賴關(guān)系诫给,同時對于ES6+等新的語法香拉,less, sass等CSS預(yù)處理都不能很好的解決……,此時就需要一個處理這些問題的工具中狂。
Webpack就是為處理這些問題而生的凫碌,它就是把你的項目當成一個整體,通過一個入口主文件(如:index.js),從這個文件開始找到你的項目所有的依賴文件并處理它們胃榕,最后打包成一個(或多個)瀏覽器可識別的JavaScript文件盛险。

1.3 它主要做些什么工作?

1.引入對應(yīng)文件的模塊解析。例如:vue-loader苦掘,babel-loader泉褐,css-loader等
2.提取css為外部引入,通過link標簽引入
3.文件壓縮
4.大文件拆分
5.公共文件提取為獨立文件
6.通過html-webpack-plugin將所有依賴的js/css等靜態(tài)資源注入模版文件鸟蜡,生成目標文件保存到指定服務(wù)器模版文件目錄

二膜赃、一個簡單的打包例子

2.1 準備工作

新建一個文件夾,用于創(chuàng)建項目(如:我在E盤新建一個叫webpack-project的文件揉忘,打開終端進入文件下跳座,使用npm init命令創(chuàng)建一個叫package.json的文件)

輸入這個命令之后,終端會問你一系列諸如項目名稱泣矛,項目描述疲眷,作者等信息,不過如果你不打算發(fā)布這個模塊您朽,直接一路回車就好狂丝。(也可以使用npm init -y這個命令來一次生成package.json文件,這樣終端不會詢問你問題)哗总。

2.2 安裝webpack

webpack4需要安裝webpack cli几颜,因為webpack模塊把一些功能分到了webpack-cli模塊,安裝方法如下:

1. 本地安裝(對于大多數(shù)項目讯屈,我們建議本地安裝)
npm install webpack webpack-cli --save-dev

2. 全局安裝(不推薦全局安裝 webpack蛋哭。這會將項目中的 webpack 鎖定到指定版本,并且在使用不同的 webpack 版本的項目中涮母,可能會導(dǎo)致構(gòu)建失敗谆趾。)
npm install webpack --global

install可簡寫為i, --global可簡寫為-g,--save-dev可簡寫為-D--save可簡寫為 -S

2.3 新建文件

在webpack-project文件夾下新建src和dist兩個文件夾和三個文件index.html,hello.js,index.js,項目結(jié)構(gòu)如下:

項目結(jié)構(gòu)

index.html 引入打包后的js文件

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>初識webpack</title>
</head>
<body>
  <div id='root'></div>
  <script src="bundle.js"></script>  <!--這是打包之后的js文件叛本,我們暫時命名為bundle.js -->
</body>
</html>

hello.js 導(dǎo)出一個模塊

module.exports = function () {
    let element = document.createElement('div');
    element.textContent = "Hello Webpack!";
    return element;
};

index.js 引入hello.js中導(dǎo)出的這個模塊

const hello = require('./hello.js');
document.querySelector("#root").appendChild(hello());

上述操作就相當于我們把hello.js模塊合并到了index.js模塊沪蓬,之后我們打包時就只需把index.js模塊打包成bundle.js,然后供index.html引用即可来候,這就是最簡單的webpack打包原理跷叉。

2.4 開始進行webpack打包

//若webpack沒有全局安裝的情況下,如下使用:
node_modules/.bin/webpack src/index.js --output dist/bundle.js
// 若webpack全局安裝的情況下 ,可直接用webpack
webpack src/index.js --output dist/bundle.js  
// --output可簡寫為-o
webpack打包完成

這個時候dist文件夾下就多了一個bundle.js


打包之后的項目結(jié)構(gòu)

可以看出webpack同時編譯了index.js和hello.js,現(xiàn)在瀏覽器打開index.html,可以看到如下結(jié)果

頁面預(yù)覽

現(xiàn)在吠勘,我們已經(jīng)成功使用webpack進行打包性芬!但是峡眶,每次都在終端中輸入這么長的命令剧防,感覺很不方便,還好有懶人方法辫樱,讓我們看看峭拘。

2.5 通過配置文件來使用webpack

在當前項目的根目錄下新建一個配置文件webpack.config.js砂代,我們寫下如下簡單配置代碼,目前只涉及入口配置(相當于我們的index.js庭呜,從它開始打包)和出口配置(相當于我們打包生成的bundle.js)懈贺。

module.exports = {
    entry: __dirname + "/src/index.js", // 入口文件
    output: {
        path: __dirname + "/dist", //打包后的文件存放的地方
        filename: "bundle.js" //打包后輸出文件的文件名
    }
}

注:__dirname是node.js中的一個全局變量,它指向當前執(zhí)行腳本所在的目錄拣展,即 E:\prictice\webpack-project\dist(這是我當前的目錄)

但平時我們看到的腳手架配置也比較喜歡采用node.js的path模塊來處理絕對路徑彭沼,所以我們也可以采用如下的寫法,和上述的效果是一樣的:

const path = require('path');
module.exports = {
    entry: path.join(__dirname, "/src/index.js"), // 入口文件
    output: {
        path: path.join( __dirname, "/dist"), //打包后的文件存放的地方
        filename: "bundle.js" //打包后輸出文件的文件名
    }
}

注:path.join的功能是拼接路徑片段备埃。

有了webpack.config.js這個配置文件姓惑,webpack 命令將默認選擇使用它,我們只需在終端中運行webpack(全稱:webpack --config webpack.config.js)就可進行打包按脚,這是全局安裝的情況下可以webpack運行于毙,不是全局安裝的情況下,可以用npx webpack辅搬,(使用 --config 選項只是向你表明唯沮,可以傳遞任何名稱的配置文件。這對于需要拆分成多個文件的復(fù)雜配置是非常有用)


webpack打包成功

搞定堪遂,是不是這樣更方便了介蛉,感覺沒那么low了,但還能不能更便捷智能呢溶褪?那必須的甘耿!

2.6 更智能的打包方式

我們現(xiàn)在只在終端中使用webpack命令來進行打包,要是以后在打包的同時還有更多的操作呢竿滨,那不是還得寫上更多的命令佳恬?所以我們得想辦法把這些命令都集成起來,現(xiàn)在我們修改下package.json如下:


package.json修改之后

這樣我們就可以在終端中直接執(zhí)行 npm start命令來進行打包于游,start命令比較特殊毁葱,可以直接npm加上start就可以執(zhí)行,如果我們想起其他的名稱贰剥,如build時倾剿,就需要使用npm run build 命令。
現(xiàn)在我們執(zhí)行 npm start命令:


OK蚌成,搞定前痘,是不是很簡單,但webpack的功能遠不止于此担忧,下面我們繼續(xù)芹缔。

三、構(gòu)建本地服務(wù)器

3.1 webpack-dev-server配置本地服務(wù)器

Webpack提供了一個可選的本地開發(fā)服務(wù)器瓶盛,這個本地服務(wù)器基于node.js構(gòu)建最欠,它是一個單獨的組件示罗,在webpack中進行配置之前需要單獨安裝它作為項目依賴:npm i webpack-dev-server -D
devServer作為webpack配置選項中的一項,以下是它的一些配置選項:

  • contentBase :設(shè)置服務(wù)器所讀取文件的目錄芝硬,當前我們設(shè)置為"./dist"
  • port :設(shè)置端口號蚜点,如果省略,默認為8080
  • inline :設(shè)置為true拌阴,當源文件改變時會自動刷新頁面
  • historyApiFallback :設(shè)置為true绍绘,所有的跳轉(zhuǎn)將指向index.html

現(xiàn)在我們把這些配置加到webpack.config.js文件上,如下:

module.exports = {
    entry: __dirname + "/src/index.js", // 入口文件
    output: {
        path: __dirname + "/dist", //打包后的文件存放的地方
        filename: "bundle.js" //打包后輸出文件的文件名
    },
    devServer: {
        contentBase: "./dist", // 本地服務(wù)器所加載文件的目錄
        port: "8088",   // 設(shè)置端口號為8088
        inline: true, // 文件修改后實時刷新
        historyApiFallback: true, //不跳轉(zhuǎn)
    }
}

package.json文件中添加啟動命令:


  "name": "webpack-project",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "webpack",
    "dev": "webpack-dev-server --open"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "webpack": "^4.23.1",
    "webpack-cli": "^3.1.2",
    "webpack-dev-server": "^3.1.10"
  }
}

我們用dev(development的縮寫迟赃,意指開發(fā)環(huán)境)來啟動本地服務(wù)器脯倒,webpack-dev-server就是啟動服務(wù)器的命令,--open是用于啟動完服務(wù)器后自動打開瀏覽器捺氢,這時候我們自定義命令方式的便捷性就體現(xiàn)出來了藻丢,可以多個命令集成在一起運行,即我們定義了一個dev命令名稱摄乒,就可以同時運行了webpack-dev-server--open兩個命令悠反。
現(xiàn)在在終端輸入npm run dev運行服務(wù)器,成功后我們即可在http://localhost:8088/中查看頁面:

3.2 Source Maps調(diào)試配置

作為開發(fā)馍佑,代碼調(diào)試當然少不了斋否,那么問題來了,經(jīng)過打包后的文件拭荤,你是不容易找到出錯的地方的茵臭,Source Map就是用來解決這個問題的。
通過如下配置舅世,我們會在打包時生成對應(yīng)于打包文件的.map文件旦委,使得編譯后的代碼可讀性更高,更易于調(diào)試:

const path = require('path');
module.exports = {
   entry: path.join(__dirname, "/src/index.js"), // 入口文件
   output: {
       path: path.join( __dirname, "/dist"), //打包后的文件存放的地方
       filename: "bundle.js" //打包后輸出文件的文件名
   },
   devServer: {
       contentBase: "./dist", // 本地服務(wù)器所加載文件的目錄
       port: "8088",  // 設(shè)置端口號為8088
       inline: true, // 文件修改后實時刷新
       historyApiFallback: true, //不跳轉(zhuǎn)
   },
   devtool: 'source-map'  // 會生成對于調(diào)試的完整的.map文件雏亚,但同時也會減慢打包速度
}

配置好后缨硝,我們再次運行npm start進行打包,這時我們會發(fā)現(xiàn)在dist文件夾中多出了一個bundle.js.map文件如下:


如果我們的代碼有bug罢低,在瀏覽器的調(diào)試工具中會提示錯誤出現(xiàn)的位置查辩,這就是devtool: 'source-map'配置項的作用。如下所示网持,在index.js中加入console.error(),最后在控制臺打印出的錯誤可以定位到具體的位置中宜岛。

四、Loaders

loaders是webpack最強大的功能之一功舀,通過不同的loader萍倡,webpack有能力調(diào)用外部的腳本或工具,實現(xiàn)對不同格式的文件的處理日杈,例如把scss轉(zhuǎn)為css遣铝,將ES66、ES7等語法轉(zhuǎn)化為當前瀏覽器能識別的語法莉擒,將JSX轉(zhuǎn)化為js等多項功能酿炸。

Loaders需要單獨安裝并且需要在webpack.config.js中的modules配置項下進行配置,Loaders的配置包括以下幾方面:

  • test:一個用以匹配loaders所處理文件的拓展名的正則表達式(必須)
  • loader:loader的名稱(必須)
  • include/exclude:手動添加必須處理的文件(文件夾)或屏蔽不需要處理的文件(文件夾)(可選)涨冀;
  • options:為loaders提供額外的設(shè)置選項(可選)

4.1 配置css-loader

如果我們要加載一個css文件填硕,需要安裝配置style-loader和css-loader:
npm i style-loader css-loader -D

const path = require('path');
module.exports = {
    entry: path.join(__dirname, "/src/index.js"), // 入口文件
    output: {
        path: path.join( __dirname, "/dist"), //打包后的文件存放的地方
        filename: "bundle.js" //打包后輸出文件的文件名
    },
    devServer: {
        contentBase: "./dist", // 本地服務(wù)器所加載文件的目錄
        port: "8088",  // 設(shè)置端口號為8088
        inline: true, // 文件修改后實時刷新
        historyApiFallback: true, //不跳轉(zhuǎn)
    },
    devtool: 'source-map',  // 會生成對于調(diào)試的完整的.map文件,但同時也會減慢打包速度
    module: {
        rules: [
            {
                test: /\.css$/,   // 正則匹配以.css結(jié)尾的文件
                use: ['style-loader', 'css-loader']  // 需要用的loader鹿鳖,一定是這個順序扁眯,因為調(diào)用loader是從右往左編譯的
            }
        ]
    }
}

我們在src文件夾下新建css文件夾,該文件夾內(nèi)新建style.css文件:

body {
    background: gray;
}

在index.js中引用它:

import './css/style.css';  //導(dǎo)入css

const hello = require('./hello.js');
document.querySelector("#root").appendChild(hello());

這時我們運行npm run dev翅帜,會發(fā)現(xiàn)頁面背景變成了灰色姻檀。


如果是要編譯sass文件呢?

4.2 配置sass

npm i sass-loader node-sass -D // 因為sass-loader依賴于node-sass涝滴,所以還要安裝node-sass

增加sass的rules:

module.exports = {
    entry: __dirname + "/src/index.js", // 入口文件
    output: {
        path: __dirname + "/dist", //打包后的文件存放的地方
        filename: "bundle.js" //打包后輸出文件的文件名
    },
    devServer: {
        contentBase: "./dist", // 本地服務(wù)器所加載文件的目錄
        port: "8088",   // 設(shè)置端口號為8088
        inline: true, // 文件修改后實時刷新
        historyApiFallback: true, //不跳轉(zhuǎn)
    },
    devtool: 'source-map',
    module: {
        rules: [
            {
                test: /\.css$/,   // 正則匹配以.css結(jié)尾的文件
                use: ['style-loader', 'css-loader']  // 需要用的loader绣版,一定是這個順序,因為調(diào)用loader是從右往左編譯的
            },
            {
                test: /\.(scss|sass)$/,   // 正則匹配以.scss和.sass結(jié)尾的文件
                use: ['style-loader', 'css-loader', 'sass-loader'] 
            }
        ]
    }
}

在css文件夾中新建blue.scss文件:

$blue: blue;
body{
    color: $blue;
} 

在index.js中引入blue.scss:

import './css/style.css';   // 導(dǎo)入css
import './css/blue.scss';   // 導(dǎo)入scss

const hello = require('./hello.js');
document.querySelector("#root").appendChild(hello());

這時npm run dev重新啟動服務(wù)器歼疮,應(yīng)該會出現(xiàn)如下結(jié)果:

image.png

有諸如圖片loader杂抽、字體loader等就不一一列出來了,感興趣的可前往webpack官網(wǎng)查看韩脏,都是一樣的套路缩麸。

五、Babel

Babel其實是一個編譯JavaScript的平臺赡矢,它可以編譯代碼幫你達到以下目的:

  • 讓你能使用最新的JavaScript代碼(ES6杭朱,ES7...),而不用管新標準是否被當前使用的瀏覽器完全支持吹散;
  • 讓你能使用基于JavaScript進行了拓展的語言痕檬,比如React的JSX;

5.1 Babel的安裝與配置

Babel其實是幾個模塊化的包送浊,其核心功能位于稱為babel-core的npm包中梦谜,webpack可以把其不同的包整合在一起使用,對于每一個你需要的功能或拓展袭景,你都需要安裝單獨的包(用得最多的是解析ES6的babel-preset-env包和解析JSX的babel-preset-react包)唁桩。

npm i babel-core babel-loader babel-preset-env babel-preset-react -D
//babel-preset-env的env表示是對當前環(huán)境的預(yù)處理,而不是像以前使用babel-preset-es2015只能針對某個環(huán)境

webpack.config.js修改如下:

module.exports = {
    entry: __dirname + "/src/index.js", // 入口文件
    output: {
        path: __dirname + "/dist", //打包后的文件存放的地方
        filename: "bundle.js" //打包后輸出文件的文件名
    },
    devServer: {
        contentBase: "./dist", // 本地服務(wù)器所加載文件的目錄
        port: "8088",   // 設(shè)置端口號為8088
        inline: true, // 文件修改后實時刷新
        historyApiFallback: true, //不跳轉(zhuǎn)
    },
    devtool: 'source-map',
    module: {
        rules: [
            {
                test: /\.css$/,   // 正則匹配以.css結(jié)尾的文件
                use: ['style-loader', 'css-loader']  // 需要用的loader耸棒,一定是這個順序荒澡,因為調(diào)用loader是從右往左編譯的
            },
            {
                test: /\.(scss|sass)$/,   // 正則匹配以.scss和.sass結(jié)尾的文件
                use: ['style-loader', 'css-loader', 'sass-loader'] 
            },
            {                            
                test: /(\.jsx|\.js)$/,     // jsx配置
                use: {                    // 注意use選擇如果有多項配置,可寫成這種對象形式
                    loader: "babel-loader",
                    options: {
                        presets: [
                            "env", "react"
                        ]
                    }
                },
                exclude: /node_modules/
            }
        ]
    }
}

現(xiàn)在我們已經(jīng)可以支持ES6及JSX的語法了与殃,我們用react來試試单山,但使用react還得先安裝兩個模塊reactreact-dom碍现。

npm i react react-dom -D

接下來我們把hello.js文件修改一下:

import React, { Component } from 'react'; // 這兩個模塊必須引入
let name = 'Wave';
export default class Hello extends Component {
    render() {
        return (
            <div>
                {name}
            </div>
        );
    }
}

修改index.js文件:

import './css/style.css';   // 導(dǎo)入css
import './css/blue.scss';   // 導(dǎo)入scss
import React from 'react';
import {render} from 'react-dom';
import Hello from './hello'; // 可省略.js后綴名

render(<Hello />, document.getElementById('root'));

此時運行npm run dev后你可能會發(fā)現(xiàn)如下結(jié)果:


這是因為官方默認babel-loader | babel對應(yīng)的版本需要一致: 即babel-loader需要搭配最新版本babel,詳細可參考這篇博客米奸。
兩種解決方案:

  • 回退低版本
npm i babel-loader@7 babel-core babel-preset-env -D
  • 更新到最高版本
npm i babel-loader @babel/core @babel/preset-env webpack -D

我這里采取的是第一個方案昼接,回退后,再此運行npm run dev悴晰,得到如下結(jié)果:


到這里了是不是感覺很爽慢睡,不就是配置嘛,想要使用什么就配置什么铡溪。

5.2 優(yōu)化babel配置

雖然babel完全可以在webpack.config.js中進行配置漂辐,但現(xiàn)在不是都提倡模塊化嘛,也許之后babel膨脹了棕硫,增加了更多的配置項呢髓涯?
那我們不如把它提取出來,把它放到根目錄下的.babelrc文件下(webpack會自動調(diào)用.babelrc里的babel配置選項)哈扮。
我們在項目根目錄下新建.babelrc文件:



然后在webpack.config.json刪掉如下圖部分:



在.babelrc文件添加如下代碼:
{
    "presets": ["env", "react"]
}

此時不出問題的話應(yīng)該一切運行正常复凳,接下來讓我們進入強大的插件模塊。

六灶泵、插件(Plugins)

插件(Plugins)是用來拓展Webpack功能的育八,它們會在整個構(gòu)建過程中生效,執(zhí)行相關(guān)的任務(wù)赦邻。
Loaders和Plugins常常被弄混髓棋,但是他們其實是完全不同的東西,可以這么來說惶洲,loaders是在打包構(gòu)建過程中用來處理源文件的(JSX按声,Scss,Less..)恬吕,一次處理一個签则,插件并不直接操作單個文件,它直接對整個構(gòu)建過程其作用铐料。

6.1 插件如何使用

使用某個插件渐裂,需要通過npm進行安裝,然后在webpack.config.js 配置文件的plugins(是一個數(shù)組)配置項中添加該插件的實例钠惩,下面我們先來使用一個簡單的版權(quán)聲明插件柒凉。

const webpack = require('webpack');  // 這個插件不需要安裝,是基于webpack的篓跛,需要引入webpack模塊
module.exports = {
    ...
    module: {
        rules: [
            {
                test: /\.css$/,   // 正則匹配以.css結(jié)尾的文件
                use: ['style-loader', 'css-loader']  // 需要用的loader膝捞,一定是這個順序,因為調(diào)用loader是從右往左編譯的
            },
            {
                test: /\.(scss|sass)$/,   // 正則匹配以.scss和.sass結(jié)尾的文件
                use: ['style-loader', 'css-loader', 'sass-loader']  
            },
            {                             // jsx配置
                test: /(\.jsx|\.js)$/,   
                use: {                    // 注意use選擇如果有多項配置愧沟,可寫成這種對象形式
                    loader: "babel-loader"
                },
                exclude: /node_modules/   // 排除匹配node_modules模塊
            }
        ]
    },
    plugins: [
        new webpack.BannerPlugin('版權(quán)所有蔬咬,翻版必究')  // new一個插件的實例 
    ]
}

運行npm start打包后我們看到bundle.js文件顯示如下:

image.png

6.2 自動生成html文件(HtmlWebpackPlugin)

到目前為止我們都是使用一開始建好的index.html文件鲤遥,而且也是手動引入bundle.js ,要是以后我們引入不止一個js文件林艘,而且更改js文件名的話盖奈,也得手動更改index.html中的js文件名,所以能不能自動生成index.html且自動引用打包后的js呢北启?HtmlWebpackPlugin插件就是用來解決這個問題的:

首先安裝該插件:

npm i html-webpack-plugin -D

然后我們對項目結(jié)構(gòu)進行一些更改:

  • dist整個文件夾刪除卜朗;
  • src文件夾下新建一個index.template.html(名稱自定義)文件模板(當然這個是可選的拔第,因為就算不設(shè)置模板咕村,HtmlWebpackPlugin插件也會生成默認html文件,這里我們設(shè)置模塊會讓我們的開發(fā)更加靈活)蚊俺,如下:
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Here is Template</title>
  </head>
  <body>
    <div id='root'>
    </div>
  </body>
</html>

webpack.config.js中我們引入了HtmlWebpackPlugin 插件懈涛,并配置了引用了我們設(shè)置的模板,如下:

const path = require('path');  // 路徑處理模塊
const webpack = require('webpack');  // 這個插件不需要安裝泳猬,是基于webpack的批钠,需要引入webpack模塊
const HtmlWebpackPlugin = require('html-webpack-plugin'); // 引入HtmlWebpackPlugin插件
module.exports = {
    ...
    module: {
        rules: [
            {
                test: /\.css$/,   // 正則匹配以.css結(jié)尾的文件
                use: ['style-loader', 'css-loader']  // 需要用的loader,一定是這個順序得封,因為調(diào)用loader是從右往左編譯的
            },
            {
                test: /\.(scss|sass)$/,   // 正則匹配以.scss和.sass結(jié)尾的文件
                use: ['style-loader', 'css-loader', 'sass-loader']  
            },
            {                             // jsx配置
                test: /(\.jsx|\.js)$/,   
                use: {                    // 注意use選擇如果有多項配置埋心,可寫成這種對象形式
                    loader: "babel-loader"
                },
                exclude: /node_modules/   // 排除匹配node_modules模塊
            }
        ]
    },
    plugins: [
        new webpack.BannerPlugin('版權(quán)所有,翻版必究'),  // new一個插件的實例 
        new HtmlWebpackPlugin({
            template: path.join(__dirname, "/src/index.template.html")// new一個這個插件的實例忙上,并傳入相關(guān)的參數(shù)
        })
    ]
}

然后我們使用 npm start進行打包拷呆,你會發(fā)現(xiàn),dist文件夾和html文件都會自動生成疫粥,如下:

為什么會自動生成dist文件夾呢茬斧?因為我們在output出口配置項中定義了出口文件所在的位置為dist文件夾,且出口文件名為bundle.js梗逮,所以HtmlWebpackPlugin會自動幫你在index.html中引用名為bundle.js文件项秉,如果你在webpack.config.js文件中更改了出口文件名,index.html中也會自動更改該文件名慷彤,這樣以后修改起來是不是方便多了娄蔼?

6.3 清理/dist文件夾(CleanWebpackPlugin)

你可能已經(jīng)注意到,在我們刪掉/dist文件夾之前底哗,由于前面的代碼示例遺留贷屎,導(dǎo)致我們的/dist文件夾比較雜亂。webpack會生成文件艘虎,然后將這些文件放置在/dist文件夾中唉侄,但是webpack無法追蹤到哪些文件是實際在項目中用到的。
通常野建,在每次構(gòu)建前清理/dist文件夾属划,是比較推薦的做法恬叹,因此只會生成用到的文件,這時候就用到CleanWebpackPlugin插件了同眯。

npm i clean-webpack-plugin -D
// webpack.config.js
...
const CleanWebpackPlugin = require('clean-webpack-plugin'); // 引入CleanWebpackPlugin插件

module.exports = {
    ...
    plugins: [
        new webpack.BannerPlugin('版權(quán)所有绽昼,翻版必究'),  // new一個插件的實例 
        new HtmlWebpackPlugin({
            template: path.join(__dirname, "/src/index.template.html")// new一個這個插件的實例,并傳入相關(guān)的參數(shù)
        }),
        new CleanWebpackPlugin(['dist']),  // 所要清理的文件夾名稱
    ]
}

插件的使用方法都是一樣的须蜗,首先引入硅确,然后new一個實例,實例可傳入?yún)?shù)明肮。

現(xiàn)在我們運行npm start后就會發(fā)現(xiàn)菱农,webpack會先將/dist文件夾刪除,然后再生產(chǎn)新的/dist文件夾柿估。

6.4 熱更新(HotModuleReplacementPlugin)

HotModuleReplacementPlugin(HMR)是一個很實用的插件循未,可以在我們修改代碼后自動刷新預(yù)覽效果。
方法:

  • devServer配置項中添加hot: true參數(shù)秫舌。
  • 因為HotModuleReplacementPluginwebpack模塊自帶的的妖,所以引入webpack后,在plugins配置項中直接使用即可足陨。
// webpack.config.js
...
const webpack = require('webpack');  // 這個插件不需要安裝嫂粟,是基于webpack的,需要引入webpack模塊

module.exports = {
    ...
    devServer: {
        contentBase: "./dist", // 本地服務(wù)器所加載文件的目錄
        port: "8088",  // 設(shè)置端口號為8088
        inline: true, // 文件修改后實時刷新
        historyApiFallback: true, //不跳轉(zhuǎn)
        hot: true // 熱更新
    },
    ...
    plugins: [
        new webpack.BannerPlugin('版權(quán)所有墨缘,翻版必究'),  // new一個插件的實例 
        new HtmlWebpackPlugin({
            template: path.join(__dirname, "/src/index.template.html")// new一個這個插件的實例星虹,并傳入相關(guān)的參數(shù)
        }),
        new CleanWebpackPlugin(['dist']),  // 傳入所要清理的文件夾名稱
        new webpack.HotModuleReplacementPlugin() // 熱更新插件 
    ]
}

此時我們重新啟動項目npm run dev后,修改hello.js的內(nèi)容飒房,會發(fā)現(xiàn)瀏覽器預(yù)覽效果會自動刷新(也許反應(yīng)會比較慢搁凸,因為我們使用了source-map和其他配置的影響,后面代碼分離的時候我們再處理)

七狠毯、項目優(yōu)化及拓展

7.1 代碼分離

在當前的開發(fā)環(huán)境都是提倡模塊化护糖,webpack自然不例外,我們前面的webpack.config.js配置文件嚼松,其實也沒配置多少東西就這么多了嫡良,要是以后增加了更多配置,豈不是看得眼花繚亂献酗,所以最好的方法就是把它拆分寝受,方便管理:

1. 我們在根目錄下新建三個文件,分別為 webpack.common.js罕偎、webpack.dev.js很澄、webpack.prod.js,分別代表公共配置文件、開發(fā)環(huán)境配置文件甩苛、生產(chǎn)環(huán)境(指項目上線時的環(huán)境)配置文件蹂楣。
2. 安裝一個合并模塊插件:

npm i webpack-merge -D

3. 將webpack.config.js的代碼拆分到上述新建的三個文件中,然后把webpack.config.js文件刪除讯蒲,具體如下:

// webpack.common.js
const path = require('path');  // 路徑處理模塊
const webpack = require('webpack');  // 這個插件不需要安裝痊土,是基于webpack的,需要引入webpack模塊
const HtmlWebpackPlugin = require('html-webpack-plugin'); // 引入HtmlWebpackPlugin插件

module.exports = {
    entry: path.join(__dirname, "/src/index.js"), // 入口文件
    output: {
        path: path.join( __dirname, "/dist"), //打包后的文件存放的地方
        filename: "bundle.js" //打包后輸出文件的文件名
    },
    module: {
        rules: [
            {
                test: /\.css$/,   // 正則匹配以.css結(jié)尾的文件
                use: ['style-loader', 'css-loader']  // 需要用的loader墨林,一定是這個順序赁酝,因為調(diào)用loader是從右往左編譯的
            },
            {
                test: /\.(scss|sass)$/,   // 正則匹配以.scss和.sass結(jié)尾的文件
                use: ['style-loader', 'css-loader', 'sass-loader'] 
            },
            {                             // jsx配置
                test: /(\.jsx|\.js)$/,   
                use: {                    // 注意use選擇如果有多項配置,可寫成這種對象形式
                    loader: "babel-loader"
                },
                exclude: /node_modules/   // 排除匹配node_modules模塊
            }
        ]
    },
    plugins: [
        new webpack.BannerPlugin('版權(quán)所有旭等,翻版必究'),  // new一個插件的實例 
        new HtmlWebpackPlugin({
            template: path.join(__dirname, "/src/index.template.html")// new一個這個插件的實例酌呆,并傳入相關(guān)的參數(shù)
        }),
        new webpack.HotModuleReplacementPlugin()
    ]
}
// webpack.dev.js
const merge = require('webpack-merge');  // 引入webpack-merge功能模塊
const common = require('./webpack.common.js'); // 引入webpack.common.js

module.exports = merge(common, {   // 將webpack.common.js合并到當前文件
    devServer: {
        contentBase: "./dist",   // 本地服務(wù)器所加載文件的目錄
        port: "8088",  // 設(shè)置端口號為8088
        inline: true,  // 文件修改后實時刷新
        historyApiFallback: true, //不跳轉(zhuǎn)
        hot: true     //熱加載
    }
})
// webpack.prod.js
const merge = require('webpack-merge');
const common = require('./webpack.common.js');
const CleanWebpackPlugin = require('clean-webpack-plugin'); // 引入CleanWebpackPlugin插件

module.exports = merge(common, { // 將webpack.common.js合并到當前文件
    devtool: 'source-map',  // 會生成對于調(diào)試的完整的.map文件,但同時也會減慢打包速度
    plugins: [
        new CleanWebpackPlugin(['dist']),  // 所要清理的文件夾名稱
    ]
})

此時我們的項目目錄如下:


4. 設(shè)置package.jsonscripts命令:

{
  "name": "webpack-project",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "webpack --config webpack.prod.js",
    "dev": "webpack-dev-server --open --config webpack.dev.js"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "babel-core": "^6.26.3",
    "babel-loader": "^7.1.5",
    "babel-preset-env": "^1.7.0",
    "babel-preset-react": "^6.24.1",
    "clean-webpack-plugin": "^1.0.0",
    "css-loader": "^2.1.0",
    "html-webpack-plugin": "^3.2.0",
    "node-sass": "^4.11.0",
    "react": "^16.7.0",
    "react-dom": "^16.7.0",
    "sass-loader": "^7.1.0",
    "style-loader": "^0.23.1",
    "webpack": "^4.27.1",
    "webpack-cli": "^3.1.2",
    "webpack-dev-server": "^3.1.14",
    "webpack-merge": "^4.2.1"
  }
}

我們把start命令改為了webpack --config webpack.prod.js辆雾,意思是把打包配置指向webpack.prod.js配置文件肪笋,而之前我們只需要使用一個webpack命令為什么就可以運行了月劈?因為webpack命令是默認指向webpack.config.js這個文件名稱了度迂,現(xiàn)在我們把文件名稱改了,所以就需要自定義指向新的文件猜揪,dev命令中的指令也同理惭墓。

然后我們運行npm startnpm run dev,效果應(yīng)該和我們分離代碼前是一樣的而姐。

7.2 多入口多出口

到目前為止我們都是一個入口文件和一個出口文件腊凶,要是我不止一個入口文件呢?下面我們來試試:

webpack.common.js中的entry入口有三種寫法拴念,分別為字符串钧萍、數(shù)組和對象,平時我們用得比較多的是對象政鼠,所以我們把它改為對象的寫法风瘦,首先我們在src文件夾下新建two.js文件,名稱任意公般。因為有多個入口万搔,所以肯定得多個出口來進行一一對應(yīng)了,所以entryoutput配置如下:

// webpack.common.js
...
module.exports = {
    entry: {
        index: path.join(__dirname, "/src/index.js"),
        two: path.join(__dirname, "/src/two.js")
    }, 
    output: {
        path: path.join( __dirname, "/dist"), //打包后的文件存放的地方
        filename: "[name].js" //打包后輸出文件的文件名
    },
    ...
}
// two.js
function two() {
    let element = document.createElement('div');
    element.innerHTML = '我是第二個入口文件';
    return element;
}

document.getElementById('root').appendChild(two());

然后我們運行npm start打包后發(fā)現(xiàn)/dist文件夾下會多出two.js文件官帘,同時index.html也會自動將two.js引入


然后我們運行npm run dev顯示如下:

7.3 增加css前綴瞬雹、分離css、消除冗余css刽虹、分離圖片

1.增加css前綴
平時我們寫css時酗捌,一些屬性需要手動加上前綴,比如-webkit-border-radius: 10px;,在webpack中我們能不能讓它自動加上呢胖缤?那是必須的馅巷,首先肯定得安裝模塊了:

npm i postcss-loader autoprefixer -D

安裝好這兩個模塊后,在項目根目錄下新建postcss.config.js文件:

// postcss.config.js
module.exports = {
    plugins: [
        require('autoprefixer')  // 引用autoprefixer模塊
    ]
}

style.css中增加以下樣式:

body {
    background: #999;
}

#root div{
    width: 200px;
    margin-top: 50px;
    transform: rotate(45deg); /* 這個屬性會產(chǎn)生前綴 */
}

修改webpack.common.js文件中的css-loader配置:

...
module.exports = {
    ...
    module: {
        rules: [
            {
                test: /\.css$/,   // 正則匹配以.css結(jié)尾的文件
                use: [            
                    {loader: 'style-loader'}, // 這里采用的是對象配置loader的寫法
                    {loader: 'css-loader'},
                    {loader: 'postcss-loader'} // 使用postcss-loader
                ]  
            },
            ...
        ]
    },
    ...
}

然后我們運行npm run dev后css樣式中會自動添加前綴草姻,顯示如下:


2.分離css
雖然webpack的理念是把css钓猬、js全都打包到一個文件里,但要是我們想把css分離出來該怎么做呢撩独?

npm i extract-text-webpack-plugin@next -D  // 加上@next是為了安裝最新的敞曹,否則會出錯

安裝完以上插件后在webpack.common.js文件中引入并使用該插件:

// webpack.common.js
...
const ExtractTextPlugin = require('extract-text-webpack-plugin') //引入分離插件

module.exports = {
    ...
    module: {
        rules: [
            {
                test: /\.css$/,   // 正則匹配以.css結(jié)尾的文件
                use: ExtractTextPlugin.extract({  // 這里我們需要調(diào)用分離插件內(nèi)的extract方法
                    fallback: 'style-loader',  // 相當于回滾,經(jīng)postcss-loader和css-loader處理過的css最終再經(jīng)過style-loader處理
                    use: ['css-loader', 'postcss-loader']
                })
            },
            ...
        ]
    },
    plugins: [
        ...
        new ExtractTextPlugin('css/index.css') // 將css分離到/dist文件夾下的css文件夾中的index.css
    ]
}

運行npm start后會發(fā)現(xiàn)/dist文件夾內(nèi)多出了/css文件夾及index.css文件综膀。


3.消除冗余css
有時候我們css寫得多了澳迫,可能會不自覺的寫重復(fù)了一些樣式,這就造成了多余的代碼剧劝,上線前又忘了檢查橄登,對于這方面,我們應(yīng)該盡量去優(yōu)化它讥此,webpack就有這個功能拢锹。

npm i purifycss-webpack purify-css glob -D

安裝完上述三個模塊后,因為正常來說是在生產(chǎn)環(huán)境中優(yōu)化代碼萄喳,所以我們應(yīng)該是在webpack.prod.js文件中進行配置卒稳,引入clean-webpack-pluginglob插件并使用它們:

// webpack.prod.js
const merge = require('webpack-merge');
const common = require('./webpack.common.js');
const CleanWebpackPlugin = require('clean-webpack-plugin'); // 引入CleanWebpackPlugin插件

const path = require('path');
const PurifyCssWebpack = require('purifycss-webpack'); // 引入PurifyCssWebpack插件
const glob = require('glob');  // 引入glob模塊,用于掃描全部html文件中所引用的css

module.exports = merge(common, {   // 將webpack.common.js合并到當前文件
    devtool: 'source-map',  // 會生成對于調(diào)試的完整的.map文件,但同時也會減慢打包速度
    plugins: [
        new CleanWebpackPlugin(['dist']),  // 所要清理的文件夾名稱
        new PurifyCssWebpack({
            paths: glob.sync(path.join(__dirname, 'src/*.html')) // 同步掃描所有html文件中所引用的css
        })
    ]
})

我們在style.css文件中增加一些多余的代碼試試:

/* style.css */
body {
    background: #999;
}

#root div{
    width: 200px;
    margin-top: 50px;
    transform: rotate(45deg); /* 這個屬性會產(chǎn)生前綴 */
}

.a{                 /* 冗余css */
    color: black;     
}

.b{                 /* 冗余css */
    width: 50px;
    height: 50px;
    background: yellow;
}

然后我們運行 npm start后發(fā)現(xiàn)打包后的index.css中是沒有多余的.a和.b代碼的:


4.處理圖片
到目前為止我們還沒講到圖片的問題他巨,如果要使用圖片充坑,我們得安裝兩個loader:

// 雖然我們只需使用url-loader,但url-loader是依賴于file-loader的染突,所以也要安裝
npm i url-loader file-loader -D 

然后在webpack.common.js中配置url-loader

// webpack.common.js
...
module.exports = {
    ...
    module: {
        rules: [
            {
                test: /\.css$/,   // 正則匹配以.css結(jié)尾的文件
                use: ExtractTextPlugin.extract({
                    fallback: 'style-loader',
                    use: ['css-loader', 'postcss-loader']
                })
            },
            {
                test: /\.(png|jpg|svg|gif)$/,  // 正則匹配圖片格式名
                use: [
                    {
                        loader: 'url-loader'  // 使用url-loader
                    }
                ]
            },
            ...
        ]
    },
    ...
}

我們修改一下style.css捻爷,把背景改為圖片背景:

/* style.css */
body {
    background: url(../images/dog.jpg) top right repeat-y;  /* 設(shè)為圖片背景 */
}

#root div{
    width: 200px;
    margin-top: 50px;
    transform: rotate(45deg); /* 這個屬性會產(chǎn)生前綴 */
}

.a{
    color: black;
}

.b{
    width: 50px;
    height: 50px;
    background: yellow;
}

運行npm start后顯示如下:


但是背景圖片變成了base64,因為webpack會自動優(yōu)化圖片份企,減少發(fā)送請求也榄,但是如果我想把它變成路徑的該怎么做?
我們可以把webpack.common.jsloader配置更改一下薪棒,增加options選項:

// webpack.common.js
...
module.exports = {
    ...
    module: {
        rules: [
            {
                test: /\.css$/,   // 正則匹配以.css結(jié)尾的文件
                use: ExtractTextPlugin.extract({
                    fallback: 'style-loader',
                    use: ['css-loader', 'postcss-loader']
                })
            },
            {
                test: /\.(png|jpg|svg|gif)$/,
                use: [
                    {
                        loader: 'url-loader',
                        options: {
                            limit: 1000  // 限制只有小于1kb的圖片才轉(zhuǎn)為base64手蝎,例子圖片為1.47kb,所以不會被轉(zhuǎn)化
                        }
                    }
                ]
            },
            ...
        ]
    },
    ...
}

然后我們運行 npm start后,再運行npm run dev俐芯,額棵介,圖片是沒有轉(zhuǎn)成base64了,但是圖片怎么不顯示了吧史?


問題就出在路徑上邮辽,我們之前圖片的路徑是在../images文件夾下,但是打包出來后沒有這個路徑了,圖片直接和文件同級了吨述,所以我們需要在webpack.common.js中給它設(shè)置一個文件夾:

// webpack.common.js
...
module.exports = {
    ...
    module: {
        rules: [
            ...
            {
                test: /\.(png|jpg|svg|gif)$/,
                use: [
                    {
                        loader: 'url-loader',
                        options: {
                            limit: 1000,  // 限制只有小于1kb的圖片才轉(zhuǎn)為base64岩睁,例子圖片為1.47kb,所以不會被轉(zhuǎn)化
                            outputPath: 'images'  // 設(shè)置打包后圖片存放的文件夾名稱
                        }
                    }
                ]
            },
            ...
        ]
    },
    ...
}

繼續(xù)npm start打包再npm run dev運行,我的天揣云!圖片還是不顯示捕儒!
調(diào)試工具上看圖片路徑有images文件夾了,但是我的../呢邓夕?


這又涉及到配置路徑的問題上了刘莹,我們還需要在css-loader中給背景圖片設(shè)置一個公共路徑publicPath: '../',如下:

// webpack.common.js
...
module.exports = {
    ...
    module: {
        rules: [
            {
                test: /\.css$/,   // 正則匹配以.css結(jié)尾的文件
                use: ExtractTextPlugin.extract({
                    fallback: 'style-loader',
                    use: ['css-loader', 'postcss-loader'],
                    publicPath: '../'  // 給背景圖片設(shè)置一個公共路徑
                })
            },
            {
                test: /\.(png|jpg|svg|gif)$/,
                use: [
                    {
                        loader: 'url-loader',
                        options: {
                            limit: 1000,  // 限制只有小于1kb的圖片才轉(zhuǎn)為base64焚刚,例子圖片為1.47kb,所以不會被轉(zhuǎn)化
                            outputPath: 'images'  // 設(shè)置打包后圖片存放的文件夾名稱
                        }
                    }
                ]
            },
            ...
        ]
    },
    ...
}

現(xiàn)在再npm start打包,再npm run dev啟動点弯,OK!沒毛部蠊尽抢肛!

7.4 壓縮代碼
在webpack4.x版本中當你打包時會自動把js壓縮了,而且 npm run dev運行服務(wù)器時碳柱,當你修改代碼時捡絮,熱更新很慢,這是因為你修改后webpack又自動為你打包士聪,這就導(dǎo)致了在開發(fā)環(huán)境中效率很慢锦援,所以我們需要把開發(fā)環(huán)境和生產(chǎn)環(huán)境區(qū)分開來猛蔽,這時就體現(xiàn)出我們代碼分離的便捷性了剥悟,webpack.dev.js代表開發(fā)環(huán)境的配置,webpack.prod.js代表生產(chǎn)環(huán)境的配置曼库,這時我們只要在package.json文件中配置對應(yīng)環(huán)境的命令即可:

{
  ...
  "scripts": {
    "start": "webpack --config webpack.prod.js --mode production",
    "dev": "webpack-dev-server --open --config webpack.dev.js --mode development"
  },
  ...
  }
}

--mode production表示打包時是生產(chǎn)環(huán)境区岗,會自己將js進行壓縮,而--mode development表示當前是開發(fā)環(huán)境毁枯,不需要進行壓縮慈缔。這同時也解決了之前一直遺留的警告問題:


最后,npm run dev啟動种玛,修改下hello.js藐鹤,熱刷新幾乎是秒刷,比不加 --mode development的時候要快n倍赂韵。

八娱节、總結(jié)

好了,到現(xiàn)在我們基本把webapck常用的功能都走了一遍祭示,寫得有點長肄满,感謝你能仔細的看到這里,希望能對你有所幫助默伍,如果有發(fā)現(xiàn)不對的地方荤胁,也請多多指教锌历。其實webpack還有很多功能欺嗤,這里也沒講述完全虑省,但相信你現(xiàn)在對webpack也有了一定的了解叨橱,更多的webpack探索一定難不倒你邻奠!請前往官方網(wǎng)站繼續(xù)探索吧劫拗。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末阅羹,一起剝皮案震驚了整個濱河市纠炮,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌灯蝴,老刑警劉巖恢口,帶你破解...
    沈念sama閱讀 222,729評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異穷躁,居然都是意外死亡耕肩,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,226評論 3 399
  • 文/潘曉璐 我一進店門问潭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來猿诸,“玉大人,你說我怎么就攤上這事狡忙∈崴洌” “怎么了?”我有些...
    開封第一講書人閱讀 169,461評論 0 362
  • 文/不壞的土叔 我叫張陵灾茁,是天一觀的道長窜觉。 經(jīng)常有香客問我,道長北专,這世上最難降的妖魔是什么禀挫? 我笑而不...
    開封第一講書人閱讀 60,135評論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮拓颓,結(jié)果婚禮上语婴,老公的妹妹穿的比我還像新娘。我一直安慰自己驶睦,他們只是感情好砰左,可當我...
    茶點故事閱讀 69,130評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著场航,像睡著了一般缠导。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上旗闽,一...
    開封第一講書人閱讀 52,736評論 1 312
  • 那天酬核,我揣著相機與錄音蜜另,去河邊找鬼。 笑死嫡意,一個胖子當著我的面吹牛举瑰,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蔬螟,決...
    沈念sama閱讀 41,179評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼此迅,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了旧巾?” 一聲冷哼從身側(cè)響起耸序,我...
    開封第一講書人閱讀 40,124評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎鲁猩,沒想到半個月后坎怪,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,657評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡廓握,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,723評論 3 342
  • 正文 我和宋清朗相戀三年搅窿,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片隙券。...
    茶點故事閱讀 40,872評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡男应,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出娱仔,到底是詐尸還是另有隱情沐飘,我是刑警寧澤,帶...
    沈念sama閱讀 36,533評論 5 351
  • 正文 年R本政府宣布牲迫,位于F島的核電站耐朴,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏恩溅。R本人自食惡果不足惜隔箍,卻給世界環(huán)境...
    茶點故事閱讀 42,213評論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望脚乡。 院中可真熱鬧,春花似錦滨达、人聲如沸奶稠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,700評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽锌订。三九已至,卻和暖如春画株,著一層夾襖步出監(jiān)牢的瞬間辆飘,已是汗流浹背啦辐。 一陣腳步聲響...
    開封第一講書人閱讀 33,819評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留蜈项,地道東北人芹关。 一個月前我還...
    沈念sama閱讀 49,304評論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像紧卒,于是被迫代替她去往敵國和親侥衬。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,876評論 2 361

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