你一定喜歡看的 Webpack 2.× 入門實戰

SpringBride

最近在學習 Webpack,網上大多數入門教程都是基于 Webpack 1.x 版本的,我學習 Webpack 的時候是看了 zhangwang<<入門 Webpack,看這篇就夠了>> 寫的非常好,不過是基于 Webpack 1.x 版本的,語法上和 Webpack 2.x 有一點不同.我學習時是使用 Webpack 2.6.1 版本,所以我就尋思著基于 zhangwang<<入門 Webpack,看這篇就夠了>> 寫下這篇 Webpack 2.x 的入門實戰,是我學習 Webpack 的記錄.聽說 Webpack 3.x 版本快要出了,不得不感嘆前端領域發展的真是太快了!

Webpack 是什么?

Webpack 是前端資源模塊化管理和打包工具。

Webpack 可以將許多松散的模塊按照依賴和規則打包成符合生產環境部署的前端資源。

Webpack 可以將按需加載的模塊進行代碼分隔,等到實際需要的時候再異步加載。

Webpack 通過 loader 的轉換,任何形式的資源都可以視作模塊,比如 CommonJs 模塊、 AMD 模塊、 ES6 模塊、CSS、圖片、 JSON、Coffeescript、 LESS、 SASS 等。

一圖勝千言,下圖足夠說明上面巴巴拉拉一大堆是啥了!

bundle your assets

對于模塊的組織,通常有如下幾種方法:

  • 分開寫幾個 js 文件,使用 script 標簽加載.
  • CommonJS 進行同步加載, Node.js 就使用這種方式.
  • AMD進行異步加載, require.js 使用這種方式.
  • 新的 ES6 模塊.

Webpack 的特點

  • 豐富的插件,流行的插件都有,方便進行開發工作.
  • 大量的加載器,便于處理和加載各種靜態資源.
  • 將按需加載的模塊進行代碼分隔,等到實際需要的時候再異步加載.

Webpack 的優勢

  • Webpack 以 commonJS 的形式來書寫腳本,但對 AMD / CMD / ES6 模塊 的支持也很全面,方便舊項目進行代碼遷移。
  • 所有資源都能模塊化。
  • 開發便捷,能替代部分 Grunt / Gulp 的工作,比如打包、壓縮混淆、圖片轉 base64、SASS 解析成 CSS 等。
  • 擴展性強,插件機制完善,特別是支持模塊熱替換(見 模塊熱替換 )的功能讓人眼前一亮。

Webpack 與 Grunt / Gulp

在沒有學習 Webpack 之前我對 Webpack、Grunt、Gulp 的認識很模糊,只知道好像這三個東西都是前端自動化工具,都是用來使前端自動化、模塊化、工程化的,這三者是可以替代彼此的前端工具.

其實 Webpack 和 Gulp / Grunt 并沒有太多的可比性,Gulp / Grunt 是一種能夠優化前端開發流程的自動化工具,而 Webpack 是一種模塊化的解決方案,不過 Webpack 的優點使得 Webpack 可以替代 Gulp / Grunt 一部分工作。

Grunt / Gulp 的工作方式是:在一個配置文件中,指明對某些文件需要進行哪些處理,例如:編譯、組合、壓縮等任務的具體步驟,Grunt / Gulp 之后可以自動替你完成這些任務。Grunt / Gulp的工作流程如下圖:

Gulp / Grunt

Webpack 的工作方式是:把你的項目當做一個整體,通過一個給定的主文件( 如:index.js ),Webpack 將從這個文件開始找到你的項目的所有依賴文件,使用 loaders 處理它們,最后打包為一個瀏覽器可識別的 JavaScript 文件。Webpack工作方式如下圖:

Webpack

如果實在要進行比較,Webpack 的處理速度更快更直接,因為 Webpack 的歷史包袱小.Webpack 還能打包更多不同類型的文件。

開始使用 Webpack

初步了解 Webpack 后,就可以開始學習使用 Webpack。這里會以一個小的 Demo 為例子來一步一步進行動手學習!

新建 Webpack 項目

1. 新建一個文件夾,命名為 webpack-demo,webpack-demo 就是你的項目名,項目名建議使用小寫字母,并且不帶空格,不能含有大寫字母.

2. 安裝 Webpack,Webpack 可以使用 npm 安裝,如果你還不知道 npm 為何物,請 Google,也可以參考 Node.js 安裝配置NPM 使用介紹快速了解、安裝 npm.

使用終端在該文件夾中執行下述指令就可以完成安裝,由于網絡原因安裝過程可能需要一些時間。

//全局安裝
npm install -g webpack
//安裝到你的項目目錄
npm install --save-dev webpack

Webpack 可以全局安裝,也可以安裝到你的項目目錄.剛開始學習 Webpack 為了方便,建議同時全局安裝和安裝到你的項目目錄.

3. 在 webpack-demo 文件夾中創建一個 package.json 文件,這是一個標準的 npm 說明文件,里面蘊含了豐富的信息,包括當前項目的依賴模塊,自定義的腳本任務等等。在終端中使用 npm init 命令可以自動創建這個 package.json 文件.

npm init

輸入這個命令后,終端會問你一系列諸如項目名稱,項目版本,項目描述,入口文件,作者等信息,不過不用擔心,如果你不準備在 npm 中發布你的模塊,這些問題的答案都不重要,回車默認即可.這些信息今后都可以更改 package.json 來修改,所以不用擔心.

4. 在 webpack-demo 文件夾中創建兩個文件夾 app 文件夾和 public 文件夾, app 文件夾用來存放原始數據,例如: SASS 文件、LESS 文件、JavaScript 模塊等,public 文件夾用來存放經過 Webpack 處理過的 app 文件夾數據,這也是準備給瀏覽器讀取的數據,其中包括使用 Webpack 打包后的 js 文件等。在這里還需要在 public 文件夾中創建 index.html 文件.在 app 文件夾中創建 Greeter.js 和 main.js 文件,此時項目結構如下圖所示:

項目結構

5. 在 public 文件夾中的 index.html 文件只有最基礎的 html 代碼,它唯一的目的就是加載打包后的 js 文件 bundle.js.

// index.html

<!DOCTYPE html>
<html lang="zh">

<head>
    <meta charset="UTF-8">
    <title>webpack-demo</title>
</head>

<body>
    <div id='root'>
    </div>
    <script type="text/javascript" src="bundle.js"></script>
</body>

</html>

6. 在 app 文件夾中的 Greeter.js 只包括一個用來返回問候信息的 html 元素的函數。

// Greeter.js

module.exports = function() {
    var greet = document.createElement('div');
    greet.textContent = "Hi there and greetings!";
    return greet;
}

7. 在 app 文件夾中的 main.js 用來把 Greeter 模塊(其實可以簡單的把它看作 Greeter.js)返回的節點插入頁面。

// main.js

var greeting = require('./Greeter.js');
document.getElementById('root').appendChild(greeting());

Webpack 配置文件

Webpack 配置文件其實也是一個簡單的 JavaScript 模塊,可以把所有與項目構建相關的信息放在里面。在 webpack-demo 文件夾根目錄下新建一個名為 webpack.config.js 的文件,并在其中進行最簡單的配置.如下所示,它包含入口文件路徑和存放打包后文件的地方路徑。

// webpack.config.js

module.exports = {
    entry: __dirname + "/app/main.js", //入口文件路徑
    output: {
        path: __dirname + "/public/", //存放打包后文件的地方路徑
        filename: "bundle.js" //打包后的文件名
    }
}

注:__dirname 是 node.js 中的一個全局變量,它指向當前 js 文件所在的目錄.

現在只需要在終端里運行 webpack 命令就可以了,這條命令會自動參考 webpack.config.js 文件中的配置選項打包你的項目,輸出結果如下:

終端結果

此時項目的 public 文件夾下也會出現打包好的 bundle.js 文件.此時項目結構如下圖所示:

項目結構

可以看出 webpack 同時編譯了 main.js 和 Greeter.js,打開 public 目錄下的 index.html 文件,就可以看到最終效果,如下圖:

最終效果

利用 npm 更快捷的執行打包任務

通過 Webpack 配置文件和執行 webpack 命令其實是比較煩人且容易出錯的,不過值得慶幸的是 npm 可以引導任務執行,對其進行配置后可以使用簡單的 npm start 命令來代替這些繁瑣的命令。在 package.json 中對 npm 的腳本部分進行相關設置,相當于把 npm 的 start 命令指向 webpack 命令,設置方法如下:

// package.json

{
    "name": "webpack-demo",
    "version": "1.0.0",
    "description": "",
    "scripts": {
        "start": "webpack"
    },
    "author": "",
    "license": "ISC"
}

執行 npm start 后命令行的輸出顯示:

npmStartTermialResult

現在只需要使用 npm start 就可以打包文件了.打開 public 目錄下的 index.html 文件,看到的最終效果是不是與之前的一樣.

利用 Webpack 生成 Source Maps

簡單說,Source Maps 就是一個信息文件,里面儲存著位置信息。也就是說,轉換后的代碼的每一個位置,所對應的轉換前的位置.有了它,出錯的時候,除錯工具將直接顯示原始代碼,而不是轉換后的代碼。這無疑給開發者帶來了很大方便.為了方便調試可以利用 Webpack 生成 Source Maps.

在 Webpack 的配置文件中配置 Source Maps,需要配置 devtool,它有以下四種不同的配置選項,各有優缺點,描述如下:

  • source-map 在一個單獨的文件中產生一個完整且功能完全的文件。這個文件具有最方便調試的 Source Maps,但是這個文件會比較大,會減慢打包文件的構建速度.
  • cheap-module-source-map 在一個單獨的文件中生成一個不帶列映射的 Source Maps,不帶列映射能夠提高項目構建速度,但這也使得瀏覽器開發者工具只能對應到具體的行,不能對應到具體的列,會對調試造成不便.
  • eval-source-map 在同一個文件中生成干凈的完整的 Source Maps。這個選項可以在不影響構建速度的前提下生成完整的 Source Maps,但是對打包后輸出的 js 文件的執行具有性能和安全的隱患。不過在開發階段這是一個非常好的選項,但是在生產階段一定不要用這個選項.
  • cheap-module-eval-source-map 這是在打包文件時最快的生成 Source Maps 的方法,生成的Source Map 會和打包后的 js 文件同行顯示,沒有列映射,和 eval-source-map 選項具有相似的缺點,文件的執行具有性能和安全的隱患.

上述選項由上到下打包速度越來越快,不過同時也具有越來越多的負面作用,較快的構建速度的后果就是對打包的文件執行有一定影響。在學習階段以及在小到中型的項目上,eval-source-map是一個很好的選項,不過記得只在開發階段使用它.

編輯 webpack-demo 文件夾下的 webpack.config.js 文件配置 devtool 選項,生成 Source Maps 文件.配置 devtool 后的 webpack.config.js 文件如下:

// webpack.config.js

module.exports = {
    devtool: "source-map", //配置生成 Source Maps 的選項
    entry: __dirname + "/app/main.js", //入口文件路徑
    output: {
        path: __dirname + "/public/", //存放打包后文件的地方路徑
        filename: "bundle.js" //打包后的文件名
    }
}

執行 npm start 命令后就能生成對應的 Source Maps,終端輸出信息如下圖:

終端輸出信息

此時項目中 public 文件夾下也生成了名為 bundle.js.map 的 Source Maps 文件.此時項目結構如下圖所示:

項目結構

使用 Webpack 構建本地服務器

想不想讓你的瀏覽器監測你修改的代碼,并自動刷新修改后的結果.其實 Webpack 提供一個可選的本地開發服務器,這個本地服務器基于 node.js 構建,可以實現你想要的這些功能,不過它是一個單獨的組件,在 Webpack 中進行配置之前需要單獨安裝它作為項目依賴.在終端中輸入下面的指令安裝對應組件.建議同時全局安裝和安裝到你的項目目錄.

//全局安裝
npm install -g webpack-dev-server
//安裝到你的項目目錄
npm install --save-dev webpack-dev-server

devserver 作為 Webpack 配置選項中的一項,具有以下配置選項

  • contentBase 默認 webpack-dev-server 會為根文件夾提供本地服務器,如果想為另外一個目錄下的文件提供本地服務器,應該在這里設置其所在目錄(本例設置到“public"文件夾下).
  • port 設置默認監聽端口,如果省略,默認為"8080".
  • inline 設置為 true,當源文件改變時會自動刷新頁面.
  • historyApiFallback 在開發單頁應用時非常有用,它依賴于 HTML5 history API,如果設置為 true,所有的跳轉將指向 index.html.

編輯 webpack-demo 文件夾下的 webpack.config.js 文件配置以上選項.

// webpack.config.js

module.exports = {
    devtool: "source-map", //配置生成 Source Maps 的選項
    entry: __dirname + "/app/main.js", //入口文件路徑
    output: {
        path: __dirname + "/public/", //存放打包后文件的地方路徑
        filename: "bundle.js" //打包后的文件名
    },
    devServer: {
        contentBase: "./public",
        port: "9000",
        inline: true,
        historyApiFallback: true,
    }
}

在終端中輸入如下命令,構建本地服務器:

webpack-dev-server

終端輸出信息如下圖,表示 Webpack 構建的本地服務器已啟動.

終端輸出信息

在瀏覽器中打開 http://localhost:9000/ 就可以看到像之前一樣的問候語頁面.

結果

也可以使用 npm 更快捷的執行任務,編輯 webpack-demo 文件夾下的 package.json 文件 scripts 選項.

// package.json

{
    "name": "webpack-demo",
    "version": "1.0.0",
    "description": "",
    "scripts": {
        "start": "webpack",
        "dev": "webpack-dev-server --devtool eval --progress --content-base build"
    },
    "author": "",
    "license": "ISC"
}

在終端中執行 npm run dev 命令,輸出信息如下圖,一樣可以啟動的本地服務器.

image.png

Ctrl + C 即可退出本地服務器.

一切皆模塊

Webpack 有一個不可不說的優點,它把所有的文件都可以當做模塊處理,包括你的 JavaScript 代碼,也包括 CSS 和 fonts 以及圖片等等,只要通過合適的 Loaders,它們都可以被當做模塊被處理.

Loaders

webpack 可以使用 loader 來預處理文件。這允許你打包除 JavaScript 之外的任何靜態資源.通過使用不同的 loader,Webpack 通過調用外部的腳本或工具可以對任何靜態資源進行處理,比如說分析 JSON 文件并把它轉換為 JavaScript 文件,或者說把 ES6 / ES7 的 JS 文件轉換為現代瀏覽器可以識別的 JS 文件.對 React 開發而言,合適的 Loaders 可以把 React 的 JSX 文件轉換為 JS 文件.

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

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

繼續動手實踐,修改 app 文件夾下的 Greeter.js 文件,把問候消息放在一個單獨的 JSON 文件里,通過 loader 的使 Greeter.js 可以讀取該 JSON 文件.

1. 在 app 文件夾下創建 config.json 文件,并輸入如下代碼:

//config.json

{
    "greetText": "Hi there and greetings from JSON!"
}

2. 編輯 app 文件夾下的 Greeter.js 文件,修改后如下:

// Greeter.js

var config = require('./config.json');

module.exports = function() {
    var greet = document.createElement('div');
    greet.textContent = config.greetText;
    return greet;
}

3. 安裝支持導入 JSON 文件的 json-loader .由于 webpack 2.× 默認支持導入 JSON 文件.如果你使用自定義文件擴展名,可能仍然需要使用此 loader.在終端中運行如下命令,安裝 json-loader 到你的項目中.

//安裝到你的項目中
npm install --save-dev json-loader

因為 json-loader 安裝到你的項目目錄里了,所以 webpack-demo 項目下會新增一個 node_modules 文件夾用于存放安裝的 json-loader.此時的項目結構如下:

項目結構

4. 編輯 webpack.config.js 文件配置 modules 選項,添加 json-loader,編輯后的文件如下:

// webpack.config.js

module.exports = {
    devtool: "source-map", //配置生成 Source Maps 的選項
    entry: __dirname + "/app/main.js", //入口文件路徑
    output: {
        path: __dirname + "/public/", //存放打包后文件的地方路徑
        filename: "bundle.js" //打包后的文件名
    },
    devServer: {
        contentBase: "./public",
        port: "9000",
        inline: true,
        historyApiFallback: true,
    },
    module: {
        loaders: [{
            test: /\.json$/,
            loader: "json-loader"
        }]
    }
}

在終端中輸入 npm start 重新編譯打包,再在瀏覽器中打開 public 文件夾下的 index.html 文件,如果看到和下圖一樣的,就說明 json-loader 配置成功了.

index.html

Babel

Babel 其實是一個編譯 JavaScript 的平臺,它的強大之處表現在可以通過編譯幫你達到以下目的:

  • 把 ES6 / ES7 標準的 JavaScript 轉化為瀏覽器能夠解析的 ES5 標準的 JavaScript.
  • 使用基于 JavaScript 進行了拓展的語言,比如 React 的 JSX.

Babel的安裝與配置

Babel 其實是幾個模塊化的包,其核心功能位于稱為 babel-core 的 npm 包中,不過 Webpack 把它們整合在一起使用,但是對于每一個你需要的功能或拓展,你都需要安裝單獨的包.用得最多的是解析 ES6 的 babel-preset-es2015 包和解析 JSX 的 babel-preset-react 包.

先來安裝這些依賴包,輸入如下命令,把這些依賴包安裝到你的項目中.

// 利用 npm 一次性安裝多個依賴模塊,模塊之間用空格隔開
npm install --save-dev babel-core babel-loader babel-preset-es2015 babel-preset-react

//安裝 React 和 React-DOM
npm install --save react react-dom

編輯 webpack.config.js 文件配置 modules 選項,添加 Babel 配置,編輯后的文件如下:

// webpack.config.js

module.exports = {
    devtool: "source-map", //配置生成 Source Maps 的選項
    entry: __dirname + "/app/main.js", //入口文件路徑
    output: {
        path: __dirname + "/public/", //存放打包后文件的地方路徑
        filename: "bundle.js" //打包后的文件名
    },
    devServer: {
        contentBase: "./public",
        port: "9000",
        inline: true,
        historyApiFallback: true,
    },
    module: {
        loaders: [{
            test: /\.json$/,
            loader: "json-loader"
        }, {
            test: /\.js$/,
            exclude: /node_modules/, //編譯打包時需要排除 node_modules 文件夾
            loader: "babel-loader",
            query: {
                presets: ['es2015', 'react']
            }
        }]
    }
}

使用 ES6 的語法,更新 app 文件夾下的 Greeter.js 文件,并返回一個 React 組件,修改后的代碼如下:

// Greeter.js

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

class Greeter extends Component {
    render() {
        return (<div> { config.greetText } </div>);
        }
    }

    export default Greeter;

使用 ES6 的模塊定義和渲染 Greeter 模塊,修改 app 文件夾下的 main.js 文件,修改后的代碼如下:

// main.js

import React from 'react';
import {render} from 'react-dom';
import Greeter from './Greeter';

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

在終端中運行 npm start 命令重新編譯打包,終端輸出信息如下:

終端輸出信息

在瀏覽器中打開 public 文件夾下的 index.html 文件,如果看到和下圖一樣的,就說明已經成功配置了 Babel.

index.html

Babel的配置選項

Babel 其實可以完全在 webpack.config.js 文件中進行配置,但是考慮到 babel 具有非常多的配置選項,在單一的 webpack.config.js 文件中進行配置往往使得這個文件顯得太復雜,因此一些開發者支持把 babel 的配置選項放在一個單獨的名為 ".babelrc" 的配置文件中。我們現在的 babel 的配置并不算復雜,不過之后我們會再加一些東西,因此現在我們就提取出相關部分,分兩個配置文件進行配置, Webpack 會自動調用 .babelrc 里的 babel 配置選項.

編輯 webpack.config.js 文件配置 modules 選項,添加 Babel 配置,編輯后的文件如下:

// webpack.config.js

module.exports = {
    devtool: "source-map", //配置生成 Source Maps 的選項
    entry: __dirname + "/app/main.js", //入口文件路徑
    output: {
        path: __dirname + "/public/", //存放打包后文件的地方路徑
        filename: "bundle.js" //打包后的文件名
    },
    devServer: {
        contentBase: "./public",
        port: "9000",
        inline: true,
        historyApiFallback: true,
    },
    module: {
        loaders: [{
            test: /\.json$/,
            loader: "json-loader"
        }, {
            test: /\.js$/,
            exclude: /node_modules/, //編譯打包時需要排除 node_modules 文件夾
            loader: "babel-loader"
        }]
    }
}

在 webpack-demo 文件夾下新建 .babelrc 文件,添加如下代碼:

// .babelrc

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

在終端中運行 npm start 命令重新編譯打包,終端輸出信息如下:

終端輸出信息

在瀏覽器中打開 public 文件夾下的 index.html 文件,如果看到和下圖一樣的,就說明已經成功配置了 Babel.

index.html

CSS

Webpack 提供兩個工具處理樣式表,css-loader 和 style-loader.

  • css-loader 使你能夠使用類似 @import 和 url(...) 的方法實現 require() 的功能
  • style-loader 將所有的計算后的樣式加入頁面中

二者組合在一起使你能夠把樣式表嵌入 Webpack 打包后的 JS 文件中。

先來安裝 css-loader, style-loader,輸入如下命令,把這些依賴包安裝到你的項目中.

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

編輯 webpack.config.js 文件配置 modules 選項,添加處理樣式表配置,編輯后的文件如下:

// webpack.config.js

module.exports = {
    devtool: "source-map", //配置生成 Source Maps 的選項
    entry: __dirname + "/app/main.js", //入口文件路徑
    output: {
        path: __dirname + "/public/", //存放打包后文件的地方路徑
        filename: "bundle.js" //打包后的文件名
    },
    devServer: {
        contentBase: "./public",
        port: "9000",
        inline: true,
        historyApiFallback: true,
    },
    module: {
        loaders: [{
            test: /\.json$/,
            loader: "json-loader"
        }, {
            test: /\.js$/,
            exclude: /node_modules/, //編譯打包時需要排除 node_modules 文件夾
            loader: "babel-loader"
        }, {
            test: /\.css$/,
            loader: 'style-loader!css-loader' //添加對樣式表的處理,感嘆號的作用在于使同一文件能夠使用不同類型的 loader
        }]
    }
}

接下來,在 app 文件夾里創建一個名為 main.css 的文件,在文件中添加如下代碼,對一些元素設置樣式.

// 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 Neue', Helvetica, Arial, sans-serif;
}

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

Webpack 只有單一的入口,其它的模塊需要通過 import, require, url 等導入相關位置,為了讓 Webpack 能找到 main.css 文件,我們把它導入 app 文件夾下的 main.js 中.修改 app 文件夾下的 main.js 文件,修改后的代碼如下:

// main.js

import React from 'react';
import {render} from 'react-dom';
import Greeter from './Greeter';
import './main.css'; //導入css文件

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

在終端中運行 npm start 命令重新編譯打包,終端輸出信息如下:

終端輸出信息

在瀏覽器中打開 public 文件夾下的 index.html 文件,如果看到和下圖一樣的,就說明已經配置成功了.

index.html

通常情況下,css 會和 js 打包到同一個文件中,并不會打包為一個單獨的 css 文件,不過通過合適的配置 Webpack 也可以把 css 打包為單獨的文件的。
不過這也只是 Webpack 把 css 當做模塊而已,繼續看一個真的 CSS 模塊的實踐.

CSS module

在過去的一些年里,JavaScript 通過一些新的語言特性、更好的工具、更好的實踐方法(比如說模塊化)發展得非常迅速。模塊使得開發者把復雜的代碼轉化為小的、干凈的、依賴聲明明確的單元,且基于優化工具,依賴管理和加載管理可以自動完成。

不過前端的另外一部分,CSS 的發展就相對慢一些,大多的樣式表卻依舊是巨大且充滿了全局類名,這使得維護和修改都非常困難和復雜。

CSS modules 的技術就能夠把 JS 的模塊化思想帶入 CSS 中來,通過 CSS 模塊,所有的類名,動畫名默認都只作用于當前模塊.

Webpack 從一開始就對 CSS 模塊化提供了支持,在 CSS loader 中進行配置后,你所需要做的一切就是把 modules 傳遞到需要的地方,然后就可以直接把 CSS 的類名傳遞到組件的代碼中,且這樣做只對當前組件有效,不必擔心在不同的模塊中具有相同的類名可能會造成的問題。

編輯 webpack.config.js 文件配置 modules 選項,添加處理樣式表配置,編輯后的文件如下:

// webpack.config.js

module.exports = {
    devtool: "source-map", //配置生成 Source Maps 的選項
    entry: __dirname + "/app/main.js", //入口文件路徑
    output: {
        path: __dirname + "/public/", //存放打包后文件的地方路徑
        filename: "bundle.js" //打包后的文件名
    },
    devServer: {
        contentBase: "./public",
        port: "9000",
        inline: true,
        historyApiFallback: true,
    },
    module: {
        loaders: [{
            test: /\.json$/,
            loader: "json-loader"
        }, {
            test: /\.js$/,
            exclude: /node_modules/, //編譯打包時需要排除 node_modules 文件夾
            loader: "babel-loader"
        }, {
            test: /\.css$/,
            loader: 'style-loader!css-loader?modules' //跟前面相比就在后面加上了 ?modules
        }]
    }
}

接下來,在 app 文件夾里創建一個名為 Greeter.css 的文件,在文件中添加如下代碼,對一些元素設置樣式.

// Greeter.css

.root {
    background-color: #eee;
    padding: 10px;
    border: 3px solid #ccc;
}

導入 .root 到 Greeter.js 中,修改 app 文件夾下的 Greeter.js 文件,修改后的代碼如下:

// Greeter.js

import React, { Component } from 'react';
import config from './config.json';
import styles from './Greeter.css'; //導入 .root 到 Greeter.js 中

class Greeter extends Component {
    render() {
        return ( <div className={styles.root}> { config.greetText } </div>);
        }
    }

    export default Greeter;

在終端中運行 npm start 命令重新編譯打包,終端輸出信息如下:

終端輸出信息

在瀏覽器中打開 public 文件夾下的 index.html 文件,如果看到和下圖一樣的,就說明已經配置成功了.

index.html

CSS modules 也是一個很大的主題,有興趣的話可以去官方文檔查看更多消息.下面兩篇文章也可以看看:

CSS 預處理器

CSS 預處理器可以將 SASS、LESS 文件轉化為瀏覽器可識別的 CSS 文件,以下是常用的CSS 預處理器 loaders.

  • Less Loader
  • Sass Loader
  • Stylus Loader

其實也存在一個 CSS 的處理平臺 PostCSS,它可以幫助你的 CSS 實現更多的功能,可以看看<<PostCSS 是個什么鬼東西?>>.

舉例來說如何使用 PostCSS,我們使用 PostCSS 來為 CSS 代碼自動添加適應不同瀏覽器,不同版本的 CSS 前綴。首先安裝 postcss-loader 和 autoprefixer(自動添加前綴的插件),安裝到你的項目中.

npm install --save-dev postcss-loader autoprefixer

編輯 webpack.config.js 文件配置 modules 選項,添加 postcss-loader 處理樣式表配置,編輯后的文件如下:

// webpack.config.js

module.exports = {
    devtool: "source-map", //配置生成 Source Maps 的選項
    entry: __dirname + "/app/main.js", //入口文件路徑
    output: {
        path: __dirname + "/public/", //存放打包后文件的地方路徑
        filename: "bundle.js" //打包后的文件名
    },
    devServer: {
        contentBase: "./public",
        port: "9000",
        inline: true,
        historyApiFallback: true,
    },
    module: {
        loaders: [{
            test: /\.json$/,
            loader: "json-loader"
        }, {
            test: /\.js$/,
            exclude: /node_modules/, //編譯打包時需要排除 node_modules 文件夾
            loader: "babel-loader"
        }, {
            test: /\.css$/,
            loader: 'style-loader!css-loader?modules!postcss-loader' //跟前面相比就在后面加上了 !postcss-loader
        }]
    }
}

在 webpack-demo 文件夾下新建 postcss.config.js 文件,添加如下代碼:

// postcss.config.js

module.exports = {
    plugins: [
        //調用autoprefixer插件,還可以配置選項添加需要兼容的瀏覽器版本.
        require("autoprefixer")({ browsers: ['ie>=8', '>1% in CN'] })
    ]
}

現在你寫的樣式會自動根據 Can i use 里的數據添加不同前綴了.在終端中運行 npm start 命令重新編譯打包,終端輸出信息如下:

終端輸出信息

在瀏覽器中打開 public 文件夾下的 index.html 文件,如果看到和下圖一樣的,就說明已經成功配置了 PostCSS.

index.html

插件(Plugins)

插件(Plugins)是用來拓展 Webpack 功能的,它會在整個構建過程中生效,執行相關的任務。
Loaders 和 Plugins 常常被弄混,但是他們其實是完全不同的東西,可以這么說,Loaders 是在打包構建過程中用來處理源文件的(JSX,Scss,Less..),一次處理一個;插件并不直接操作單個文件,它直接對整個構建過程其作用。

Webpack 有很多內置插件,同時也有很多第三方插件,可以讓我們完成更加豐富的功能。

使用插件的方法

要使用某個插件,我們需要通過 npm 安裝它,然后要做的就是在 Webpack 配置中的 Plugins 關鍵字部分添加該插件的一個實例.

編輯 webpack.config.js 文件配置 Plugins 選項,添加一個實現版權聲明的 BannerPlugin 插件,BannerPlugin 是內置插件不需要使用 npm 安裝.編輯后的文件如下:

// webpack.config.js

var webpack = require("webpack");
module.exports = {
    devtool: "source-map", //配置生成 Source Maps 的選項
    entry: __dirname + "/app/main.js", //入口文件路徑
    output: {
        path: __dirname + "/public/", //存放打包后文件的地方路徑
        filename: "bundle.js" //打包后的文件名
    },
    devServer: {
        contentBase: "./public",
        port: "9000",
        inline: true,
        historyApiFallback: true,
    },
    module: {
        loaders: [{
            test: /\.json$/,
            loader: "json-loader"
        }, {
            test: /\.js$/,
            exclude: /node_modules/, //編譯打包時需要排除 node_modules 文件夾
            loader: "babel-loader"
        }, {
            test: /\.css$/,
            loader: 'style-loader!css-loader?modules!postcss-loader' //跟前面相比就在后面加上了 !postcss-loader
        }]
    },
    plugins: [
        new webpack.BannerPlugin("Copyright Flying Unicorns inc.")//在這個數組中new一個實例就可以了
    ]
}

在終端中運行 npm start 命令重新編譯打包,終端輸出信息如下:

終端輸出信息

在瀏覽器中打開 public 文件夾下的 index.html 文件,如果看到和下圖一樣的,就說明已經成功配置了 BannerPlugin 插件.

index.html

常用插件

給大家推薦幾個常用的插件

HtmlWebpackPlugin

這個插件的作用是依據一個簡單的模板,幫你生成最終的 html 文件,這個文件中自動引用了你打包后的 JS 文件。每次編譯都在文件名中插入一個不同的哈希值。

安裝 HtmlWebpackPlugin 到你的項目中
npm install --save-dev html-webpack-plugin

在使用 HtmlWebpackPlugin 之前,需要對 webpack-demo 項目結構做一些改變.

1. 移除 public 文件夾.

2. 在 app 目錄下,創建一個文件名為 index.tmpl.html 模板文件,在編譯過程中,HtmlWebpackPlugin 插件會依據此模板生成最終的 html 頁面,會自動添加所依賴的 css、 js、favicon等文件.index.tmpl.html 模板文件代碼如下:

// index.tmpl.html

<!DOCTYPE html>
<html lang="zh">

<head>
    <meta charset="UTF-8">
    <title>webpack-demo</title>
</head>

<body>
    <div id="root"></div>
</body>

</html>

3. 在 webpack-demo 文件夾下新建一個 build 文件夾用來存放最終的輸出文件.

4. 編輯 webpack.config.js 文件配置 Plugins 選項,添加 HtmlWebpackPlugin 插件,修改 output 選項.編輯后的文件如下:

// webpack.config.js

var webpack = require("webpack");
var HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    devtool: "source-map", //配置生成 Source Maps 的選項
    entry: __dirname + "/app/main.js", //入口文件路徑
    output: {
        path: __dirname + "/build/", //存放打包后文件的地方路徑
        filename: "bundle.js" //打包后的文件名
    },
    devServer: {
        contentBase: "./public",
        port: "9000",
        inline: true,
        historyApiFallback: true,
    },
    module: {
        loaders: [{
            test: /\.json$/,
            loader: "json-loader"
        }, {
            test: /\.js$/,
            exclude: /node_modules/, //編譯打包時需要排除 node_modules 文件夾
            loader: "babel-loader"
        }, {
            test: /\.css$/,
            loader: 'style-loader!css-loader?modules!postcss-loader' //跟前面相比就在后面加上了 !postcss-loader
        }]
    },
    plugins: [
        new webpack.BannerPlugin("Copyright Flying Unicorns inc."), //在這個數組中new一個實例就可以了
        new HtmlWebpackPlugin({
            template: __dirname + "/app/index.tmpl.html" //new一個插件的實例,并傳入相關的參數
        })
    ]
}

此時項目結構如下圖所示:

項目結構

在終端中運行 npm start 命令重新編譯打包,終端輸出信息如下:

終端輸出信息

此時項目結構已經發生改變,build 文件夾下存放了最終的輸出的文件,項目結構如下圖所示:

項目結構

在瀏覽器中打開 build 文件夾下的 index.html 文件,如果看到和下圖一樣的,就說明已經成功配置了 HtmlWebpackPlugin 插件.

index.html

Hot Module Replacement

Hot Module Replacement(HMR)也是 Webpack 里很有用的一個插件,它允許你在修改組件代碼后,自動刷新實時預覽修改后的效果。
在 Webpack 中使用 HMR 也很簡單,只需要做兩項配置.

  • 在 Webpack 配置文件中添加 HMR 插件
  • 在 Webpack Dev Server 中添加 hot 參數

不過配置完這些后,JS 模塊其實還是不能自動熱加載的,還需要在你的 JS 模塊中執行一個 Webpack 提供的 API 才能實現熱加載,雖然這個 API 不難使用,但是如果是 React 模塊,使用我們已經熟悉的 Babel 可以更方便的實現功能熱加載。

整理下思路,具體實現方法如下

  • Babel 和 Webpack 是獨立的工具,二者可以一起工作,二者都可以通過插件拓展功能.
  • HMR 是一個 Webpack 插件,它讓你能瀏覽器中實時觀察模塊修改后的效果,但是如果你想讓它工作,需要對模塊進行額外的配額.
  • Babel 有一個叫做 react-transform-hrm 的插件,可以在不同 React 模塊進行額外的配置下讓 HMR 正常工作.

編輯 webpack.config.js 文件配置 Plugins / devServer 選項,添加 Hot Module Replacement 插件.編輯后的文件如下:

// webpack.config.js

var webpack = require("webpack");
var HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    devtool: "source-map", //配置生成 Source Maps 的選項
    entry: __dirname + "/app/main.js", //入口文件路徑
    output: {
        path: __dirname + "/build/", //存放打包后文件的地方路徑
        filename: "bundle.js" //打包后的文件名
    },
    devServer: {
        port: "9000",
        inline: true,
        historyApiFallback: true,
        hot: true
    },
    module: {
        loaders: [{
            test: /\.json$/,
            loader: "json-loader"
        }, {
            test: /\.js$/,
            exclude: /node_modules/, //編譯打包時需要排除 node_modules 文件夾
            loader: "babel-loader"
        }, {
            test: /\.css$/,
            loader: 'style-loader!css-loader?modules!postcss-loader' //跟前面相比就在后面加上了 !postcss-loader
        }]
    },
    plugins: [
        new webpack.BannerPlugin("Copyright Flying Unicorns inc."), //在這個數組中new一個實例就可以了
        new HtmlWebpackPlugin({
            template: __dirname + "/app/index.tmpl.html" //new一個插件的實例,并傳入相關的參數
        }),
        new webpack.HotModuleReplacementPlugin() //熱加載插件
    ]
}

安裝 react-transform-hmr 插件

npm install --save-dev babel-plugin-react-transform react-transform-hmr

編輯在 webpack-demo 文件夾下的 .babelrc 文件,編輯后的文件如下:

// .babelrc

{
  "presets": ["react", "es2015"],
  "env": {
    "development": {
    "plugins": [["react-transform", {
       "transforms": [{
         "transform": "react-transform-hmr",
         "imports": ["react"],
         "locals": ["module"]
       }]
     }]]
    }
  }
}

編輯 webpack-demo 文件夾下的 package.json 文件 scripts 選項,添加 --hot 選項開啟代碼熱替換.

// package.json

{
    "name": "webpack-demo",
    "version": "1.0.0",
    "description": "",
    "scripts": {
        "start": "webpack",
        "dev": "webpack-dev-server --devtool eval --progress --content-base build --hot"
    },
    "author": "",
    "license": "ISC"
}

在終端中執行 npm run dev 命令,輸出信息如下圖,一樣可以啟動自動熱加載.

終端輸出信息

在瀏覽器中打開 http://localhost:9000/ 就可以看到像之前一樣的問候語頁面.

結果

現在當你使用 React 時,就可以熱加載模塊了.按 Ctrl + C 即可退出自動熱加載.

產品階段的構建

我們已經使用 Webpack 構建了一個完整的開發環境.但是在產品階段,可能還需要對打包的文件進行額外的處理,比如說優化、壓縮、緩存以及分離 CSS 和 JS.

對于復雜的項目來說,需要復雜的配置,這時候分解配置文件為多個小的文件可以使得事情井井有條,以 webpack-demo 項目來說,我們在 webpack-demo 文件夾下創建一個名為 webpack.production.config.js 的文件,在里面加上基本的配置代碼,如下:

// webpack.production.config.js

var webpack = require("webpack");
var HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    entry: __dirname + "/app/main.js", //入口文件路徑
    output: {
        path: __dirname + "/build/", //存放打包后文件的地方路徑
        filename: "bundle.js" //打包后的文件名
    },
    module: {
        loaders: [{
            test: /\.json$/,
            loader: "json-loader"
        }, {
            test: /\.js$/,
            exclude: /node_modules/, //編譯打包時需要排除 node_modules 文件夾
            loader: "babel-loader"
        }, {
            test: /\.css$/,
            loader: 'style-loader!css-loader?modules!postcss-loader' //跟前面相比就在后面加上了 !postcss-loader
        }]
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: __dirname + "/app/index.tmpl.html" //new一個插件的實例,并傳入相關的參數
        })
    ]
}

編輯 webpack-demo 文件夾下的 package.json 文件 scripts 選項,添加 build 選項,編輯后的文件如下:

// package.json

{
    "name": "webpack-demo",
    "version": "1.0.0",
    "description": "",
    "scripts": {
        "start": "webpack",
        "dev": "webpack-dev-server --devtool eval --progress --content-base build --hot",
        "build": "webpack --config ./webpack.production.config.js --progress"
    },
    "author": "",
    "license": "ISC"
}

在終端中執行 npm run build 命令,輸出信息如下圖:

輸出信息

說明分解配置文件為多個小的文件成功了.

優化插件

Webpack 提供了一些在發布階段非常有用的優化插件,它們大多來自于 Webpack 社區,可以通過 npm 安裝,通過以下插件可以完成產品發布階段所需的功能.

  • OccurrenceOrderPlugin: 為組件分配 ID,通過這個插件 Webpack 可以分析和優先考慮使用最多的模塊,并為它們分配最小的 ID.
  • UglifyJsPlugin:壓縮JS代碼.
  • ExtractTextPlugin:分離 CSS 和 JS 文件.

我們來看看如何使用它們,OccurrenceOrderPlugin 和 UglifyJS plugins 都是內置插件,我們只需要安裝 ExtractTextPlugin 插件.

安裝 ExtractTextPlugin 插件

npm install --save-dev extract-text-webpack-plugin

編輯 webpack.config.js 文件配置 Plugins 選項,添加這三個插件,因為要分離 css 所以還要配置 module 下的 loaders 選項.編輯后的文件如下:

// webpack.config.js

var webpack = require("webpack");
var HtmlWebpackPlugin = require('html-webpack-plugin');
var ExtractTextPlugin = require('extract-text-webpack-plugin');

module.exports = {
    devtool: "source-map", //配置生成 Source Maps 的選項
    entry: __dirname + "/app/main.js", //入口文件路徑
    output: {
        path: __dirname + "/build/", //存放打包后文件的地方路徑
        filename: "bundle.js" //打包后的文件名
    },
    devServer: {
        port: "9000",
        inline: true,
        historyApiFallback: true,
        hot: true
    },
    module: {
        loaders: [{
            test: /\.json$/,
            loader: "json-loader"
        }, {
            test: /\.js$/,
            exclude: /node_modules/, //編譯打包時需要排除 node_modules 文件夾
            loader: "babel-loader"
        }, {
            test: /\.css$/,
            use: ExtractTextPlugin.extract({
                fallback: "style-loader",
                use: "css-loader?modules!postcss-loader"
            })
        }]
    },
    plugins: [
        new webpack.BannerPlugin("Copyright Flying Unicorns inc."), //在這個數組中new一個實例就可以了
        new HtmlWebpackPlugin({
            template: __dirname + "/app/index.tmpl.html" //new一個插件的實例,并傳入相關的參數
        }),
        new webpack.HotModuleReplacementPlugin(), //熱加載插件
        new webpack.optimize.OccurrenceOrderPlugin(),
        new webpack.optimize.UglifyJsPlugin(),
        new ExtractTextPlugin("style.css")
    ]
}

在終端中執行 npm start 命令,輸出信息如下圖:

輸出信息

此時項目結構已經發生改變,build 文件夾下多出了抽離出來的 style.css 文件還有對應的 style.css.map 文件,項目結構如下圖所示:

項目結構

如果你打開 build 文件夾下的 bundle.js 文件,就可以看到 bundle.js 文件內容已經被壓縮處理了.

說明這三個插件已經配置成功了.

緩存

為了加快加載速度,合理的利用緩存是必不可少的.使用緩存的最好方法是保證文件名和文件內容是匹配的.內容改變,名稱也相應改變.

Webpack 可以把一個哈希值添加到打包文件的文件名中,添加特殊的字符串混合體([name], [id] and [hash])到輸出文件名前,便于修改 BUG 以后,對應更新用戶本地的緩存文件.

編輯 webpack.config.js 文件修改 output / plugins 選項.編輯后的文件如下:

// webpack.config.js

var webpack = require("webpack");
var HtmlWebpackPlugin = require('html-webpack-plugin');
var ExtractTextPlugin = require('extract-text-webpack-plugin');

module.exports = {
    devtool: "source-map", //配置生成 Source Maps 的選項
    entry: __dirname + "/app/main.js", //入口文件路徑
    output: {
        path: __dirname + "/build/", //存放打包后文件的地方路徑
        filename: "[name]-[hash].js" //打包后的文件名
    },
    devServer: {
        port: "9000",
        inline: true,
        historyApiFallback: true,
        hot: true
    },
    module: {
        loaders: [{
            test: /\.json$/,
            loader: "json-loader"
        }, {
            test: /\.js$/,
            exclude: /node_modules/, //編譯打包時需要排除 node_modules 文件夾
            loader: "babel-loader"
        }, {
            test: /\.css$/,
            use: ExtractTextPlugin.extract({
                fallback: "style-loader",
                use: "css-loader?modules!postcss-loader"
            })
        }]
    },
    plugins: [
        new webpack.BannerPlugin("Copyright Flying Unicorns inc."), //在這個數組中new一個實例就可以了
        new HtmlWebpackPlugin({
            template: __dirname + "/app/index.tmpl.html" //new一個插件的實例,并傳入相關的參數
        }),
        new webpack.HotModuleReplacementPlugin(), //熱加載插件
        new webpack.optimize.OccurrenceOrderPlugin(),
        new webpack.optimize.UglifyJsPlugin(),
        new ExtractTextPlugin("[name]-[hash].css")
    ]
}

在終端中執行 npm start 命令,輸出信息如下圖:

輸出信息

此時項目 build 文件夾下的 main.css 和 main.js 文件都對應的加上了哈希值.項目結構如下圖所示:

項目結構

如果你打開 build 文件夾下的 index.html 文件,就可以看到引用的 css、js 文件名也對應發生了改變,這樣修改 BUG 以后,也能對應更新用戶本地的緩存文件.

進階,永不止步

其實到這里我的這篇 Webpack 2.x 的入門實戰已經完結了!但這也只是個入門而已!在實際項目中運用還是不夠的,還有很多細節我并沒深入講,所以大家還想進階的話建議好好去看看 webpack-china 的文檔.

另外實戰項目 webpack-demo 的源碼,我已經放到 Github 上去了,歡迎大家提意見.

還有一點我覺得很重要,要學會看控制臺輸出信息,能夠看控制臺輸出信息解決的問題,就不要上 Google 搜了!

鳴謝

這篇 Webpack 2.x 的入門實戰是基于 zhangwang<<入門Webpack,看這篇就夠了>> 寫出來的,是我學習 Webpack 的實戰記錄.特別感謝 zhangwang 付出,如果你覺得這篇文章對你有幫助,請轉到 zhangwang 為他點個贊.

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,156評論 6 529
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 97,866評論 3 413
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 174,880評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,398評論 1 308
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,202評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,743評論 1 320
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,822評論 3 438
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 41,962評論 0 285
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,476評論 1 331
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,444評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,579評論 1 365
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,129評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,840評論 3 344
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,231評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,487評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,177評論 3 388
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,568評論 2 370

推薦閱讀更多精彩內容

  • 無意中看到zhangwnag大佬分享的webpack教程感覺受益匪淺,特此分享以備自己日后查看,也希望更多的人看到...
    小小字符閱讀 8,202評論 7 35
  • GitChat技術雜談 前言 本文較長,為了節省你的閱讀時間,在文前列寫作思路如下: 什么是 webpack,它要...
    蕭玄辭閱讀 12,704評論 7 110
  • 在現在的前端開發中,前后端分離、模塊化開發、版本控制、文件合并與壓縮、mock數據等等一些原本后端的思想開始...
    Charlot閱讀 5,459評論 1 32
  • 1. 新建一個文件夾,命名為 webpack-cli , webpack-cli 就是你的項目名,項目名建議使用小...
    魯大師666閱讀 1,487評論 1 3
  • 公休去哪里玩啊?你幾月份休息啊?我們把護照、通行證都辦了吧!最近我們的話題中多了“旅行”兩個字。 自從前段時間最流...
    有一只小熊閱讀 256評論 0 1