React實戰之webpack搭建開發工作流

1、介紹


Webpack 是一個前端資源加載/打包工具。它將根據模塊的依賴關系進行靜態分析,然后將這些模塊按照指定的規則生成對應的靜態資源。

2、安裝


  • 全局安裝

以下的 NPM 安裝方式,將使 webpack 在全局環境下可用:

npm install -g webpack

不推薦全局安裝 webpack。這會將您項目中的 webpack 鎖定到指定版本,并且在使用不同的 webpack 版本的項目中,可能會導致構建失敗。

  • 本地安裝

//安裝最新版本
npm install --save-dev webpack
//安裝特定版本
npm install --save-dev webpack@<version>

對于大多數項目,我們建議本地安裝。這可以使我們在引入破壞式變更(breaking change)的依賴時,更容易分別升級項目。

  • 安裝操作

首先我們創建一個目錄,初始化 npm,以及在本地安裝 webpack:

mkdir webpack-demo && cd webpack-demo
npm init -y
npm install --save-dev webpack
安裝操作
執行完以后目錄結構

2、Webpack使用


  • 簡單使用webpack

現在我們將創建以下目錄結構和內容
目錄結構
src/index.js
function component() {
    var element = document.createElement('div');
    element.innerHTML = 'Hello, webpack';
    return element;
}
document.body.appendChild(component());
dist/index.html
 <html>
   <head>
     <title>Getting Started</title>
   </head>
   <body>
     <script src="bundle.js"></script>
   </body>
 </html>
執行 webpack,會將我們的腳本作為入口起點,然后輸出為 bundle.js
./node_modules/.bin/webpack src/index.js dist/bundle.js
執行 webpack
在瀏覽器中打開 index.html,如果一切訪問都正常,你應該能看到以下文本:'Hello webpack'。
在瀏覽器中打開 index.html
  • 使用配置文件webpack.config.js

大多數項目會需要很復雜的設置,這就是為什么 webpack 要支持配置文件。這比在終端(terminal)中輸入大量命令要高效的多,所以讓我們創建一個取代以上使用 CLI 選項方式的配置文件:webpack.config.js

目錄結構
webpack.config.js
const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  }
};
現在,讓我們再次執行構建,通過使用我們的新配置:
./node_modules/.bin/webpack --config webpack.config.js
使用webpack.config.js
NPM 腳本(NPM Scripts)

考慮到用 CLI 這種方式來運行本地的 webpack 不是特別方便,我們可以設置一個快捷方式。在 package.json 添加一個 npm 腳本(npm script):

package.json
package.json

現在,可以使用 npm run build 命令,來替代我們之前用到的較長命令。注意,使用 npm 的 scripts,我們可以通過模塊名,來引用本地安裝的 npm 包,而不是寫出完整路徑。這是大多數基于 npm 的項目遵循的標準,允許我們直接調用 webpack,而不是去調用 ./node_modules/.bin/webpack。

現在運行以下命令,然后看看你的腳本別名是否正常運行:
npm run build
npm run build 運行結果

2、React和ES6以及JSX語法的支持


  • ES6以及JSX語法的支持

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

安裝Babel依賴包
// npm一次性安裝多個依賴模塊,模塊之間用空格隔開
npm install --save-dev babel-core babel-loader babel-preset-es2015 babel-preset-react
目錄結構
.babelrc
{
  "presets": ["react", "es2015"]
}
babel 在webpack.config.js 中進行配置
babel 在webpack.config.js 中進行配置
React的支持

安裝 React 和 React-DOM

npm install --save react react-dom
使用React和ES6的語法更新index.js

注意這里我沒有直接渲染到 document.body上,而是特別選擇了 id 為 root 的 :
ReactDOM.render(<Hello />, document.getElementById('root'));
React官方不推薦將組件渲染到 document.body 上,因為這個節點很可能會改變,比如動態添加一個 <script> 標簽等,這將使 React 的 DOM diff計算變得更加困難。

import ReactDOM from 'react-dom';
import React, {Component} from 'react';

class Hello extends Component{
  render() {
    return (
      <div>
        Hello, webpack
      </div>
    );
  }
}

ReactDOM.render(<Hello />, document.getElementById('root'));
在body中增加一個div標簽
  <html>
   <head>
     <title>Getting Started</title>
   </head>
   <body>
        <div id='root'></div>
        <script src="bundle.js"></script>
   </body>
  </html>
運行
運行

瀏覽器運行效果

3、通過 loader 引入任何其他類型的文件


  • 加載 CSS

為了從 JavaScript 模塊中 import 一個 CSS 文件,你需要在 module 配置中 安裝并添加 style-loader 和 css-loader:

npm install --save-dev style-loader css-loader
加載css在webpack.config.js 中進行配置
加載css在webpack.config.js 中進行配置
我們嘗試一下,通過在項目中添加一個新的 style.css 文件,并將其導入到我們的 index.js 中:
目錄結構
src/style.css
.hello {
  color: red;
}
src/index.js
src/index.js
運行
npm run build

瀏覽器運行結果
CSS module

CSS modules 的技術就意在把JS的模塊化思想帶入CSS中來,通過CSS模塊,所有的類名,動畫名默認都只作用于當前模塊。Webpack從一開始就對CSS模塊化提供了支持,在CSS loader中進行配置后,你所需要做的一切就是把”modules“傳遞到所需要的地方,然后就可以直接把CSS的類名傳遞到組件的代碼中,且這樣做只對當前組件有效,不必擔心在不同的模塊中使用相同的類名造成沖突。

CSS module在webpack.config.js 中進行配置
CSS module在webpack.config.js 中進行配置
src/index.js
屏幕快照 2017-09-06 10.48.17.png
運行
npm run build

瀏覽器運行結果
  • 加載圖片

使用 file-loader,我們可以輕松地將這些內容混合到 CSS 中:
npm install --save-dev file-loader
加載圖片在webpack.config.js 中進行配置
加載圖片在webpack.config.js 中進行配置
增加一張test.png圖片
目錄結構
src/style.css
src/style.css
運行
npm run build

瀏覽器運行結果

4、webpack plugin


  • HtmlWebpackPlugin

作用

1、html文件中引入的外部資源如script、link動態添加每次compile后的hash,防止引用緩存的外部文件問題
2、可以生成創建html入口文件,比如單頁面可以生成一個html文件入口,配置N個html-webpack-plugin可以生成N個頁面入口

安裝
npm install --save-dev html-webpack-plugin
創建src/index.tmpl.html,并刪除dist/index.html
目錄結構
src/index.tmpl.html
  <html>
   <head>
     <title>Getting Started</title>
   </head>
   <body>
        <div id='root'></div>
   </body>
  </html>
webpack.config.js配置
webpack.config.js配置
運行
npm run build

dist目錄下生成了index.html

瀏覽器運行index.html
了解更多 HtmlWebpackPlugin 插件

如果你想要了解更多 HtmlWebpackPlugin 插件提供的全部功能和選項,那么你就應該多多熟悉HtmlWebpackPlugin 倉庫。
你還可以看一下 html-webpack-template,除了默認模板之外,還提供了一些額外的功能。

  • clean-webpack-plugin

你可能已經注意到,由于過去的指南和代碼示例遺留下來,導致我們的 /dist 文件夾相當雜亂。webpack 會生成文件,然后將這些文件放置在 /dist 文件夾中,但是 webpack 無法追蹤到哪些文件是實際在項目中用到的。

通常,在每次構建前清理 /dist 文件夾,是比較推薦的做法,因此只會生成用到的文件。讓我們完成這個需求。

安裝
npm install clean-webpack-plugin --save-dev
webpack.config.js配置
webpack.config.js配置
現在執行 npm run build,再檢查 /dist 文件夾。如果一切順利,你現在應該不會再看到舊的文件,只有構建后生成的文件!

5、開發常用配置


  • webpack-dev-server

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

安裝
npm install --save-dev webpack-dev-server
webpack.config.js配置
webpack.config.js配置

localhost:8080 下建立服務,并啟用gzip壓縮

package.json配置npm script
package.json配置npm script
運行
npm start

自動打開瀏覽器并修改自動刷新
  • source map

介紹

當 webpack 打包源代碼時,可能會很難追蹤到錯誤和警告在源代碼中的原始位置。例如,如果將三個源文件(a.js, b.js 和 c.js)打包到一個 bundle(bundle.js)中,而其中一個源文件包含一個錯誤,那么堆棧跟蹤就會簡單地指向到 bundle.js。這并通常沒有太多幫助,因為你可能需要準確地知道錯誤來自于哪個源文件。

為了更容易地追蹤錯誤和警告,JavaScript 提供了 source map 功能,將編譯后的代碼映射回原始源代碼。如果一個錯誤來自于 b.js,source map 就會明確的告訴你。

devtool選項
以下選項是開發的理想選擇:

eval- 每個模塊都用eval()和執行//@ sourceURL。這很快。主要的缺點是它不會正確顯示行號,因為它被映射到折疊代碼而不是原始代碼(沒有來自加載器的源地圖)。

eval-source-map- 每個模塊都執行,eval()并將SourceMap作為DataUrl添加到eval()。最初它很慢,但它提供了快速的重建速度,并產生真實的文件。行號被正確映射,因為它被映射到原始代碼。它產生最好的質量SourceMaps進行開發。

cheap-eval-source-map- 類似于eval-source-map每個模塊都執行eval()。它是“便宜的”,因為它沒有列映射,它只映射行號。它忽略來自Loader的SourceMaps,并且僅顯示與devtool類似的折疊代碼eval。

cheap-module-eval-source-map- cheap-eval-source-map但是,與此類似,處理來自加載程序的源地圖以獲得更好的結果。然而,Loader Source Maps簡化為每行一個映射。

對小到中型的項目中,eval-source-map是一個很好的選項,再次強調你只應該開發階段使用它。
cheap-module-eval-source-map方法構建速度更快,但是不利于調試,推薦在大型項目考慮時間成本時使用。
webpack.config.js 配置
屏幕快照 2017-09-06 14.38.07.png
  • 代碼分離及優化

介紹
  • OccurenceOrderPlugin :為組件分配ID,通過這個插件webpack可以分析和優先考慮使用最多的模塊,并為它們分配最小的ID
  • UglifyJsPlugin:壓縮JS代碼;
  • ExtractTextPlugin:用于將 CSS 從主應用程序中分離
安裝

OccurenceOrder 和 UglifyJS plugins 都是內置插件,你需要做的只是安裝其它非內置插件

npm install --save-dev extract-text-webpack-plugin
webpack.config.js配置
webpack.config.js配置
運行
npm start

運行后將css文件分離到style.css文件中


將css文件分離到style.css文件中

運行后js文件被壓縮


運行后js文件被壓縮
  • 緩存

介紹

通過使用 output.filename 進行文件名替換,可以確保瀏覽器獲取到修改后的文件。[hash] 替換可以用于在文件名中包含一個構建相關(build-specific)的 hash,但是更好的方式是使用 [chunkhash] 替換,在文件名中包含一個 chunk 相關(chunk-specific)的哈希。

webpack.config.js 配置
webpack.config.js 配置
  • 生產環境構建

現在,我們把 scripts 重新指向到新配置。我們將 npm start 定義為開發環境腳本,并在其中使用 webpack-dev-server,將 npm run build 定義為生產環境腳本

創建webpack.config.prod.js文件
創建webpack.config.prod.js文件
webpack.config.prod.js
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const ExtractTextPlugin = require("extract-text-webpack-plugin");

module.exports = {
    entry: './src/index.js',
    output: {
        filename: '[name].[chunkhash].js',
        path: path.resolve(__dirname, 'dist')
    },
    module: {
        rules: [{
                test: /(\.jsx|\.js)$/,
                use: {
                    loader: "babel-loader"
                },
                exclude: /node_modules/
            },
            {
                test: /\.css$/,
                use: ExtractTextPlugin.extract({
                    fallback: "style-loader",
                    use: {
                        loader: "css-loader",
                        options: {
                            modules: true,
                            minimize: true
                        }
                    }
                })
            },
            {
                test: /\.(png|svg|jpg|gif)$/,
                use: [
                    'file-loader'
                ]
            }
        ]
    },
    plugins: [
        new ExtractTextPlugin("[name].[contenthash].css"),
        new CleanWebpackPlugin([__dirname + "/dist"]),
        new webpack.optimize.OccurrenceOrderPlugin(),
        new webpack.optimize.UglifyJsPlugin(),
        new HtmlWebpackPlugin({
            template: __dirname + "/src/index.tmpl.html",//new 一個這個插件的實例,并傳入相關的參數
            minify: {
                removeComments: true, //是否去掉注釋
                collapseWhitespace: true, //是否去掉空格
                minifyJS: true, //是否壓縮html里的js(使用uglify-js進行的壓縮)
                minifyCSS: true, //是否壓縮html里的css(使用clean-css進行的壓縮)
            },
        })
    ],
};
webpack.config.js去除一些配置
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    entry: './src/index.js',
    output: {
        filename: '[name].[chunkhash].js',
        path: path.resolve(__dirname, 'dist')
    },
    devtool: 'eval-source-map',
    module: {
        rules: [{
                test: /(\.jsx|\.js)$/,
                use: {
                    loader: "babel-loader"
                },
                exclude: /node_modules/
            },
            {
                test: /\.css$/,
                use: [
                    {
                        loader: "style-loader"
                    }, {
                        loader: "css-loader",
                        options: {
                            modules: true
                        }
                    }
                ]
            },
            {
                test: /\.(png|svg|jpg|gif)$/,
                use: [
                    'file-loader'
                ]
            }
        ]
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: __dirname + "/src/index.tmpl.html",//new 一個這個插件的實例,并傳入相關的參數
            minify: {
                removeComments: true, //是否去掉注釋
                collapseWhitespace: true, //是否去掉空格
                minifyJS: true, //是否壓縮html里的js(使用uglify-js進行的壓縮)
                minifyCSS: true, //是否壓縮html里的css(使用clean-css進行的壓縮)
            },
        })
    ],
    devServer: {
        //一切服務都啟用gzip 壓縮:
        compress: true,
        port: 8080
    },
};
package.json配置
package.json配置

最后


本篇文章將涵蓋webpack安裝、使用、plugin、開發中常用的配置、緩存等等,我在邊寫邊跟著構建,并將每一步構建生成git記錄,代碼傳送門,如果有疑問的地方歡迎在下面評論或者私信給我。
如果您覺得有幫助,請給我點個??。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容