1. 程式人生 > >webpack4 系列教程(十六):開發模式和生產模式·實戰

webpack4 系列教程(十六):開發模式和生產模式·實戰

作者按:這是webpack4系列最後一篇教程了。這篇文章在之前所有教程的基礎上,做了一個真正意義上的 webpack 專案!我花費了三個月整理了這份教程,並且完善了相關示例程式碼,也更熟悉 webpack 的理論和應用,當然,也感謝大家的支援。好了,感慨完畢,開始正題 ?

0. 課程介紹和資料

本節課的程式碼目錄如下:

1. 如何分離開發環境和生產環境?

熟悉 Vuejs 或者 ReactJs 的腳手架的朋友應該都知道:在根目錄下有一個/build/資料夾,專門放置webpack配置檔案的相關程式碼。

不像我們前 15 節課的 demo (只有一個配置檔案webpack.config.js

),為了分離開發環境和生產環境,我們需要分別編寫對應的webpack配置程式碼。

毫無疑問,有一些外掛和配置是兩種環境共用的,所以應該提煉出來,避免重複勞動。如前文目錄截圖,build/webpack.common.conf.js就儲存了兩種環境都通用的配置檔案。而build/webpack.dev.conf.jsbuild/webpack.prod.conf.js分別是開發和生產環境需要的特殊配置。

2. 編寫package.json

類似上一節講的,為了讓命令更好呼叫,需要配置scripts選項。模仿vue-cli的命令格式,編寫如下package.json:

{
  "scripts": {
"dev": "webpack-dev-server --env development --open --config build/webpack.common.conf.js", "build": "webpack --env production --config build/webpack.common.conf.js" }, "devDependencies": { "babel-core": "^6.26.3", "babel-loader": "^7.1.5", "babel-plugin-transform-runtime": "^6.23.0"
, "babel-preset-env": "^1.7.0", "clean-webpack-plugin": "^0.1.19", "css-loader": "^1.0.0", "extract-text-webpack-plugin": "^4.0.0-beta.0", "html-webpack-plugin": "^3.2.0", "jquery": "^3.3.1", "style-loader": "^0.21.0", "webpack": "^4.16.1", "webpack-cli": "^3.1.0", "webpack-dev-server": "^3.1.4", "webpack-merge": "^4.1.3" }, "dependencies": { "babel-polyfill": "^6.26.0", "babel-runtime": "^6.26.0" } }

按照配置,執行:

  • npm run dev: 進入開發除錯模式
  • npm run build: 生成打包檔案

還可以看出來,build/webpack.common.conf.js不僅僅是存放著兩種環境的公共程式碼,還是webpack命令的入口檔案。

3. 如何合併 webpack 的不同配置?

根據前面所講,我們有 3 個配置檔案。那麼如何在build/webpack.common.conf.js中引入開發或者生產環境的配置,並且正確合併呢?

此時需要藉助webpack-merge這個第三方庫。下面是個示例程式碼:

const merge = require("webpack-merge");

const productionConfig = require("./webpack.prod.conf");
const developmentConfig = require("./webpack.dev.conf");

const commonConfig = {}; // ... 省略

module.exports = env => {
  let config = env === "production" ? productionConfig : developmentConfig;
  return merge(commonConfig, config); // 合併 公共配置 和 環境配置
};

4. 如何在程式碼中區分不同環境?

4.1 配置檔案

如果這個 js 檔案是 webpack 命令的入口檔案,例如build/webpack.common.conf.js,那麼mode的值(production 或者 development)會被自動傳入module.exports的第一個引數,開發者可以直接使用。

如下面的程式碼,先判斷是什麼環境,然後再決定使用什麼配置,最後 return 給 webpack:

module.exports = env => {
  let config = env === "production" ? productionConfig : developmentConfig;
  return merge(commonConfig, config); // 合併 公共配置 和 環境配置
};

4.2 專案檔案

如果這個 js 檔案是專案中的指令碼檔案,那麼可以訪問process.env.NODE_ENV這個變數來判斷環境:

if (process.env.NODE_ENV === "development") {
  console.log("開發環境");
} else {
  console.log("生產環境");
}

5. 編寫配置檔案

5.1 編寫公共配置檔案

// /build/webpack.common.conf.js

const webpack = require("webpack");
const merge = require("webpack-merge");
const ExtractTextPlugin = require("extract-text-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");

const path = require("path");

const productionConfig = require("./webpack.prod.conf.js"); // 引入生產環境配置檔案
const developmentConfig = require("./webpack.dev.conf.js"); // 引入開發環境配置檔案

/**
 * 根據不同的環境,生成不同的配置
 * @param {String} env "development" or "production"
 */
const generateConfig = env => {
  // 將需要的Loader和Plugin單獨宣告

  let scriptLoader = [
    {
      loader: "babel-loader"
    }
  ];

  let cssLoader = [
    {
      loader: "css-loader",
      options: {
        minimize: true,
        sourceMap: env === "development" ? true : false // 開發環境:開啟source-map
      }
    }
  ];

  let styleLoader =
    env === "production"
      ? ExtractTextPlugin.extract({
          // 生產環境:分離、提煉樣式檔案
          fallback: {
            loader: "style-loader"
          },
          use: cssLoader
        })
      : // 開發環境:頁內樣式嵌入
        cssLoader;

  return {
    entry: { app: "./src/app.js" },
    output: {
      publicPath: env === "development" ? "/" : __dirname + "/../dist/",
      path: path.resolve(__dirname, "..", "dist"),
      filename: "[name]-[hash:5].bundle.js",
      chunkFilename: "[name]-[hash:5].chunk.js"
    },
    module: {
      rules: [
        { test: /\.js$/, exclude: /(node_modules)/, use: scriptLoader },
        { test: /\.css$/, use: styleLoader }
      ]
    },
    plugins: [
      // 開發環境和生產環境二者均需要的外掛
      new HtmlWebpackPlugin({
        filename: "index.html",
        template: path.resolve(__dirname, "..", "index.html"),
        chunks: ["app"],
        minify: {
          collapseWhitespace: true
        }
      }),
      new webpack.ProvidePlugin({ $: "jquery" })
    ]
  };
};

module.exports = env => {
  let config = env === "production" ? productionConfig : developmentConfig;
  return merge(generateConfig(env), config);
};

5.2 編寫開發環境配置檔案

// /build/webpack.dev.conf.js

const webpack = require("webpack");

const path = require("path");

module.exports = {
  mode: "development",
  devtool: "source-map",
  devServer: {
    contentBase: path.join(__dirname, "../dist/"),
    port: 8000,
    hot: true,
    overlay: true,
    proxy: {
      "/comments": {
        target: "https://m.weibo.cn",
        changeOrigin: true,
        logLevel: "debug",
        headers: {
          Cookie: ""
        }
      }
    },
    historyApiFallback: true
  },
  plugins: [
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NamedModulesPlugin()
  ]
};

5.3 編寫生產環境配置檔案

// /build/webpack.comm.conf.js

const ExtractTextPlugin = require("extract-text-webpack-plugin");
const CleanWebpackPlugin = require("clean-webpack-plugin");

const path = require("path");

module.exports = {
  mode: "production",
  plugins: [
    new ExtractTextPlugin({
      filename: "[name].min.css",
      allChunks: false // 只包括初始化css, 不包括非同步載入的CSS
    }),
    new CleanWebpackPlugin(["dist"], {
      root: path.resolve(__dirname, "../"),
      verbose: true
    })
  ]
};

6. 其他檔案

在專案目錄截圖中展示的樣式檔案,vendor 下的檔案還有 app.js,程式碼就不一一列出了。完全可以根據自己的需要,寫一些簡單的程式碼,然後執行一下。畢竟前面的配置檔案的架構和講解才是最重要的。

這裡僅僅給出原始碼地址(歡迎 Star 哦):

7. 執行效果和測試

鼓搗這麼半天,肯定要測試下,要不怎麼才能知道正確性(這才是另人激動的一步啦啦啦)。

7.1 跑起來:開發模式

進入專案目錄,執行npm run dev:

成功跑起來,沒出錯(廢話,都是被除錯了好多次了哈哈哈)。

開啟瀏覽器的控制檯看一下:

很好,都是按照編寫的app.js的邏輯輸出的。

7.2 跑起來:生產模式

Ctrl+C退出開發模式後,執行npm run build,如下圖打包成功:

打包後的檔案也放在了指定的位置:

直接點選index.html,並且開啟瀏覽器控制檯:

ok, 符合app.js的輸出:成功辨識了是否是開發環境!!!

8. 最終

完結撒花 ✿✿ ヽ(°▽°)ノ ✿