webpack-best-practice-最佳實踐-部署生產

tip

webpack的入門篇能夠看個人這一片博文。
《如何使用webpack—webpack-howto》.css

前言

最近一段時間在項目中使用了webpack和React來開發,總之來講也是遇到了許多坑,webpack畢竟仍是比較新的技術,並且也很難有一個很好的構建案例來適應全部的項目,總之,在看了許多項目demo和官方文檔以及官方推薦的tutorials以後,也算是本身總結出的一套最佳實踐吧。html

代碼

代碼能夠在個人Github上。
能夠戳這裏~~前端

package.json 命令配置

既然是須要用到的是實際項目的構建,那麼必然就要考慮開發環境和生產環境下的配置項了:node

// package.json
{
  // ...
  "scripts": {
    "build": "webpack --progress --colors --watch",
    "watch": "webpack-dev-server --hot --progress --colors",
    "dist": "NODE_ENV=production webpack --progress --colors"
  },
  // ...
}

能夠在目錄下執行react

npm run build
npm run watch
npm run dist

解釋一下:webpack

  • build 是在咱們開發環境下執行的構建命令;git

  • watch 也是在開發環境下執行,可是加了webpack最強大的功能--搭建靜態服務器和熱插拔功能(這個在後面介紹;github

  • dist 是項目在要部署到生產環境時打包發佈。web

dist 裏面的NODE_ENV=production是聲明瞭當前執行的環境是production-生產環境npm

後面跟着幾個命令:

  • --colors 輸出的結果帶彩色

  • --progress 輸出進度顯示

  • --watch 動態實時監測依賴文件變化而且更新

  • --hot 是熱插拔

  • --display-error-details 錯誤的時候顯示更多詳細錯誤信息

  • --display-modules 默認狀況下 node_modules 下的模塊會被隱藏,加上這個參數能夠顯示這些被隱藏的模塊

  • -w 動態實時監測依賴文件變化而且更新

  • -d 提供sorcemap

  • -p 對打包文件進行壓縮

目錄結構

如今前端模塊化的趨勢致使目錄結構也發生了很大的改變和爭議,這只是我本身用到的一種形式,能夠參考。

.
├── app                 #開發目錄
|   ├──assets           #存放靜態資源
|   |   ├──datas        #存放數據 json 文件
|   |   ├──images       #存放圖片資源文件
|   |   └──styles       #存放全局sass變量文件和reset文件
|   ├──lib
|   |   ├──components   #存放數據 模塊組件 文件
|   |   |   └──Header
|   |   |       ├──Header.jsx
|   |   |       └──Header.scss
|   |   |       
|   |   └──utils        #存放utils工具函數文件
|   |
|   └──views
|       ├──Index        #入口文件
|       |   ├──Index.html #html文件
|       |   ├──Index.jsx
|       |   └──Index.scss
|       └──Index2
├── dist                #發佈目錄
├── node_modules        #包文件夾
├── .gitignore     
├── .jshintrc      
├── webpack.config.js   #webpack配置文件
└── package.json

具體能夠到Github上看demo。

webpack.config.js

引入包

var webpack = require('webpack');
var path = require('path');
var fs = require('fs');

這個毋庸置疑吧。

判斷是不是在當前生產環境

定義函數判斷是不是在當前生產環境,這個很重要,一位開發環境和生產環境配置上有一些區別

var isProduction = function () {
  return process.env.NODE_ENV === 'production';
};

聲明文件夾

// 定義輸出文件夾
var outputDir = './dist';
// 定義開發文件夾
var entryPath = './app/views';

定義插件

var plugins = [
  new webpack.optimize.CommonsChunkPlugin({
    name: 'commons',
    filename: 'js/commons.js',
  }),
  new webpack.ProvidePlugin({
    React: 'react',
    ReactDOM: 'react-dom',
    reqwest: 'reqwest',
  }),
];
if( isProduction() ) {
  plugins.push(
    new webpack.optimize.UglifyJsPlugin({
      test: /(\.jsx|\.js)$/,
      compress: {
        warnings: false
      },
    })
  );
}
  1. CommonsChunkPlugin 插件能夠打包全部文件的共用部分生產一個commons.js文件。

  2. ProvidePlugin 插件能夠定義一個共用的入口,好比下面加的 React ,他會在每一個文件自動require了react,因此你在文件中不須要 require('react'),也可使用 React。

  3. 若是是在生產環境下,則加入插件 UglifyJsPlugin ,執行代碼壓縮,而且去除 warnings。

自動遍歷多文件入口

var entris = fs.readdirSync(entryPath).reduce(function (o, filename) {
    !/\./.test(filename) &&
    (o[filename] = './' + path.join(entryPath, filename, filename + '.jsx'));
    return o;
  }, {}
);

函數會自動遍歷開發的入口文件夾下面的文件,而後一一輩子產入口而且返回一個對象--入口。

若是在這一步不須要多頁面多入口

那麼可使用html-webpack-plugin插件,它能夠自動爲入口生成一個html文件,配置以下:

var HtmlWebpackPlugin = require('html-webpack-plugin');
plugins.push(new HtmlWebpackPlugin({
  title: 'index',
  filename: outputDir+'/index.html',  #生成html的位置
  inject: 'body',                     #插入script在body標籤裏
}));

entry 就能夠自定義一個入口就夠了

config的具體配置

var config = {
  target: 'web',
  cache: true,
  entry: entris,
  output: {
    path: outputDir,
    filename: 'js/[name].bundle.js',
    publicPath: isProduction()? 'http://******' : 'http://localhost:3000',
  },
  module: {
    loaders: [
      {
        test: /(\.jsx|\.js)$/,
        loaders: ['babel?presets[]=es2015&presets[]=react'],
        exclude: /node_modules/
      },
      {
        test: /\.scss$/,
        loaders: ['style', 'css?root='+__dirname, 'resolve-url', 'sass']
      },
      {
        test: /\.json$/,
        loader: 'json',
      },
      {
        test: /\.(jpe?g|png|gif|svg)$/,
        loader: 'url?limit=1024&name=img/[name].[ext]'
      },
      {
        test: /\.(woff2?|otf|eot|svg|ttf)$/i,
        loader: 'url?name=fonts/[name].[ext]'
      },
      {
        test: /\.html$/,
        loader: 'file?name=views/[name].[ext]'
      },
    ]
  },
  plugins: plugins,
  resolve: {
    extensions: ['', '.js', 'jsx'],
  },
  devtool: isProduction()?null:'source-map',
};

這裏來一一說明:

對於output

path和filename都不用多說了,path是生成文件的存放目錄,filename是文件名,固然能夠在前面加上目錄位置。
這裏提醒一下,filename 的相對路徑就是 path了,而且下面 靜態文件生成的filename也是相對於這裏的path的,好比 image 和 html。
publicPath 的話是打包的時候生成的文件連接,好比 圖片 資源,
若是是在生產環境固然是用服務器地址,若是是開發環境就是用本地靜態服務器的地址。

module loaders 打包加載的處理器

能夠不用夾 loader了 好比 原來 url-loader 如今 url

js/jsx

{
  test: /(\.jsx|\.js)$/,
  loaders: ['babel?presets[]=es2015&presets[]=react'],
  exclude: /node_modules/
},

對於js文件和jsx文件用了babel來處理,這裏注意一下,最新版本的babel吧es2015和react的處理分開了,全部要這麼寫。

處理scss文件

{
  test: /\.scss$/,
  loaders: ['style', 'css?root='+__dirname, 'resolve-url', 'sass']
},

這裏用了sass、css、style的loader這不用多說了。
那麼root和resolve-url是怎麼回事呢,root是定義了scss文件裏面聲明的url地址是相對於根目錄的,而後resolve-url回去相對解析這個路徑,而不用require去獲取,好比

background: url('./assets/images/webpack.png');

這樣就能夠加載到./assets/images/webpack.png這個文件,而不用使用相對路徑和require

處理json文件

{
  test: /\.json$/,
  loader: 'json',
},

對於json文件,能夠自動請求該模塊而且打包。

處理 圖片 字體 資源文件

{
  test: /\.(jpe?g|png|gif|svg)$/,
  loader: 'url?limit=1024&name=img/[name].[ext]'
},
{
  test: /\.(woff2?|otf|eot|svg|ttf)$/i,
  loader: 'url?name=fonts/[name].[ext]'
},

這裏使用了 url 這個loader,可是url依賴 file-loader,它是對file-loader的二次封裝。
在請求圖片的時候若是文件大小小於 1024k ,使用內聯 base64 URLs,不然會自動導入到name所聲明的目錄,這裏是相對以前聲明的 outputDir 路徑。
字體資源也是同樣。

處理html文件

{
  test: /\.html$/,
  loader: 'file?name=views/[name].[ext]'
},

在多頁面的項目中須要,能夠自動吧html文件導入到指定的生產文件夾下。

resolve

resolve: {
  extensions: ['', '.js', 'jsx'],
},

是能夠忽略的文件後綴名,好比能夠直接require('Header');而不用加.jsx。

devtool

devtool: isProduction()?null:'source-map',

規定了在開發環境下才使用 source-map。

疑問

目前爲止,對於多頁面項目仍是沒有找到一個很好的方案去構建自動化。

原文能夠看個人博客 webpack-best-practice-最佳實踐-部署生產;

相關文章
相關標籤/搜索