天天看點

gulp + webpack: webpack-stream 多項目整合環境搭建demo

最近越看項目越不順眼,流程和打包實在太老了,用node8以上的版本無法識别gulp,打包後的代碼也沒有壓縮,頁面也沒有source指引,html手動更換連結等等。。。

so,下決心把這塊兒更新一下

原本的node版本也太低,這次一起升,下載下傳node.js。。。

有興趣的可以在github下載下傳試試,一起交流。

因為用慣了之前的gulp流程化,加上項目的多入口多出口,這次決定還是繼續gulp+webpack。查了一下現在已經不是gulp-webpack了,而是webpack-stream,本次gulp隻作為任務流,主要還是webpack,配置來了

先看最終效果圖
gulp + webpack: webpack-stream 多項目整合環境搭建demo
檔案結構

多個項目整合在一起,要分開單獨打包,感覺還是用gulp來執行webpack更合适一點,個人了解而已。

gulp + webpack: webpack-stream 多項目整合環境搭建demo
package.json

以下就是所有的依賴了,這裡沒什麼好說的,要什麼就install什麼

下面的 browserslist 是為css自動添加字首用的

-2020.5.27 更新-

-2020.6.8 更新-

-2020.6.18 更新-

{
  "name": "webpack-stream",
  "version": "1.0.0",
  "description": "",
  "private": true,
  "keywords": [],
  "author": "JackSY",
  "license": "ISC",
  "devDependencies": {
    "@babel/core": "^7.9.6",
    "@babel/plugin-proposal-class-properties": "^7.4.4",
    "@babel/plugin-proposal-decorators": "^7.4.4",
    "@babel/plugin-transform-object-assign": "^7.2.0",
    "@babel/plugin-transform-runtime": "^7.4.4",
    "@babel/preset-env": "^7.9.6",
    "@babel/runtime": "^7.4.4",
    "@babel/runtime-corejs2": "^7.4.4",
    "babel-core": "^6.26.3",
    "babel-eslint": "^10.1.0",
    "babel-loader": "^8.1.0",
    "babel-plugin-dynamic-import-webpack": "^1.1.0",
    "babel-plugin-lodash": "^3.3.4",
    "clean-css": "^4.2.3",
    "clean-webpack-plugin": "^3.0.0",
    "css-loader": "^3.5.3",
    "cssnano": "^4.1.10",
    "eslint": "^7.1.0",
    "eslint-config-standard": "^14.1.1",
    "eslint-friendly-formatter": "^4.0.1",
    "eslint-loader": "^4.0.2",
    "eslint-plugin-flowtype": "^5.1.3",
    "eslint-plugin-html": "^6.0.2",
    "eslint-plugin-import": "^2.20.2",
    "eslint-plugin-node": "^11.1.0",
    "eslint-plugin-promise": "^4.2.1",
    "eslint-plugin-standard": "^4.0.1",
    "eslint-plugin-vue": "^6.2.2",
    "file-loader": "^6.0.0",
    "friendly-errors-webpack-plugin": "^1.7.0",
    "gulp": "^4.0.2",
    "html-loader": "^1.1.0",
    "html-webpack-plugin": "^4.3.0",
    "image-webpack-loader": "^6.0.0",
    "less-loader": "^6.1.0",
    "lodash": "^4.17.15",
    "lodash-webpack-plugin": "^0.11.5",
    "mini-css-extract-plugin": "^0.9.0",
    "node-notifier": "^7.0.1",
    "node-sass": "^4.14.1",
    "postcss-import": "^12.0.1",
    "postcss-loader": "^3.0.0",
    "postcss-preset-env": "^6.7.0",
    "sass-loader": "^8.0.2",
    "style-loader": "^1.2.1",
    "uglify-js": "^3.9.3",
    "url-loader": "^4.1.0",
    "vue": "^2.6.11",
    "vue-loader": "^15.9.2",
    "vue-router": "^3.3.2",
    "vue-server-renderer": "^2.6.11",
    "vue-style-loader": "^4.1.2",
    "vue-template-compiler": "^2.6.11",
    "vuex": "^3.4.0",
    "webpack": "^4.43.0",
    "webpack-dev-server": "^3.11.0",
    "webpack-stream": "^5.2.1"
  },
  "browserslist": [
    "defaults",
    "not ie < 11",
    "last 2 versions",
    "> 1%",
    "iOS 7",
    "last 3 iOS versions"
  ]
}
           
webpack.dev.conf.js

開發模式已經添加了devServer,在gulp中動态添加,可以及時響應更改,而無需一遍又一遍地打包

-2020.5.27 更新-

-2020.6.8 更新-

-2020.6.18 更新-

const webpack = require("webpack");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const VueLoaderPlugin = require("vue-loader/lib/plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const FriendlyErrorsPlugin = require("friendly-errors-webpack-plugin");
const LodashWebpackPlugin = require("lodash-webpack-plugin");
const notifier = require("node-notifier");
const InsertHtmlPlugin = require("./public/insert-html-code-plugin");
const statistics = require("./public/statisticsTemplate");

module.exports = {
  mode: "development",
  output: {
    filename: "build.js",
    globalObject: "this"
  },
  stats: {
    children: false // Tells stats whether to add information about the children.
  },
  plugins: [
    new HtmlWebpackPlugin({
      hash: true,
      meta: false
    }),
    new VueLoaderPlugin(),
    new MiniCssExtractPlugin(),
    new LodashWebpackPlugin(),
    new FriendlyErrorsPlugin({
      //  運作錯誤
      onErrors: function (severity, errors) {
        // 可以收聽插件轉換和優先級的錯誤
        // 嚴重性可以是"錯誤"或"警告"
        if (severity !== "error") {
          return;
        }
        const error = errors[0];
        notifier.notify({
          title: "Webpack error",
          message: severity + ": " + error.name,
          subtitle: error.file || ""
        });
      },
      // 是否每次編譯之間清除控制台
      // 預設為true
      clearConsole: true
    }),
    new InsertHtmlPlugin({
      minimize: false,
      scriptCode: statistics,
      scriptPaths: [
        'https://cdn.bootcdn.net/ajax/libs/jquery/1.8.2/jquery.min.js'
      ]
    }),
    new webpack.HotModuleReplacementPlugin() // webpack内置的熱更新插件
  ],
  externals: {
    jquery: "jQuery"
  },
  resolve: {
    extensions: [".vue", ".js", ".scss", ".sass", ".less", ".css", ".json"],
    alias: {
      vue$: "vue/dist/vue.esm.js"
    }
  },
  devtool: "inline-source-map",
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          "vue-style-loader",
          "style-loader",
          {
            loader: MiniCssExtractPlugin.loader,
            options: {
              esModule: true
            }
          },
          {
            loader: "css-loader",
            options: {
              sourceMap: true,
              importLoaders: 1
            }
          },
          {
            loader: "postcss-loader",
            options: {
              plugins: (loader) => [
                require("postcss-import")({
                  root: loader.resourcePath
                }),
                require("postcss-preset-env")(),
                require("cssnano")()
              ],
              sourceMap: true
            }
          }
        ]
      },
      {
        test: /\.(sass|scss)$/,
        use: [
          "vue-style-loader",
          "style-loader",
          {
            loader: MiniCssExtractPlugin.loader,
            options: {
              esModule: true
            }
          },
          {
            loader: "css-loader",
            options: {
              sourceMap: true,
              importLoaders: 2
            }
          },
          {
            loader: "postcss-loader",
            options: {
              plugins: (loader) => [
                require("postcss-import")({
                  root: loader.resourcePath
                }),
                require("postcss-preset-env")(),
                require("cssnano")()
              ],
              sourceMap: true
            }
          },
          {
            loader: "sass-loader",
            options: {
              sourceMap: true
            }
          }
        ]
      },
      {
        test: /\.less$/,
        use: [
          "vue-style-loader",
          "style-loader",
          {
            loader: MiniCssExtractPlugin.loader,
            options: {
              esModule: true
            }
          },
          {
            loader: "css-loader",
            options: {
              sourceMap: true,
              importLoaders: 2
            }
          },
          {
            loader: "postcss-loader",
            options: {
              plugins: (loader) => [
                require("postcss-import")({
                  root: loader.resourcePath
                }),
                require("postcss-preset-env")(),
                require("cssnano")()
              ],
              sourceMap: true
            }
          },
          {
            loader: "less-loader",
            options: {
              sourceMap: true
            }
          }
        ]
      },
      {
        test: /\.vue$/,
        loader: "vue-loader"
      },
      {
        test: /\.js$/,
        use: [{
          loader: "eslint-loader",
          options: { // 這裡的配置項參數将會被傳遞到 eslint 的 CLIEngine
            formatter: require("eslint-friendly-formatter") // 指定錯誤報告的格式規範
          }
        }],
        enforce: "pre", // 編譯前檢查
        exclude: [/node_modules/] // 不檢測的檔案
      },
      {
        test: /\.(js|jsx)$/,
        loader: "babel-loader",
        exclude: file => (
          /node_modules/.test(file) &&
          !/\.vue\.js/.test(file)
        )
      },
      {
        test: /\.(png|jp?g|gif|svg|ico)$/,
        use: [
          {
            loader: "url-loader",
            options: {
              limit: 8192, // 小于8192位元組的圖檔打包成base 64圖檔
              name: "images/[name].[hash:8].[ext]",
              publicPath: "./",
              esModule: false
            }
          }
        ]
      },
      {
        // 檔案依賴配置項——字型圖示
        test: /\.(woff|woff2|svg|eot|ttf)$/,
        use: [{
          loader: "file-loader",
          options: {
            limit: 8192,
            name: "fonts/[name].[hash:8].[ext]",
            publicPath: "./"
          }
        }]
      },
      {
        // 檔案依賴配置項——音頻
        test: /\.(wav|mp3|ogg)?$/,
        use: [{
          loader: "file-loader",
          options: {
            limit: 8192,
            name: "audios/[name].[hash:8].[ext]",
            publicPath: "./"
          }
        }]
      },
      {
        // 檔案依賴配置項——視訊
        test: /\.(ogg|mpeg4|webm|mp4)?$/,
        use: [{
          loader: "file-loader",
          options: {
            limit: 8192,
            name: "videos/[name].[hash:8].[ext]",
            publicPath: "./"
          }
        }]
      },
      {
        test: /\.(html)?$/,
        use: {
          loader: "html-loader",
          options: {
            minimize: false
          }
        }
      }
    ]
  }
}
           
gulpfile.js

-2020.5.27 更新-

-2020.6.8 更新-

-2020.6.18 更新-

為了拆分gulp任務,廢了我不少功夫

gulp + webpack: webpack-stream 多項目整合環境搭建demo

結構大概是這樣

gulp + webpack: webpack-stream 多項目整合環境搭建demo

index.js這樣

const activity2020 = require('./project');

const allGulpTasks = Object.assign({},
  activity2020
);
Object.keys(allGulpTasks).forEach(key => {
  exports[key] = allGulpTasks[key];
});
           

接下來是其他子任務project.js

const { src, dest } = require('gulp');
const webpackStream = require('webpack-stream');
const { webpackEntryOption, openWebpackDevServe } = require('../public/webpackOptionChange');
const gulpTasks = require('../gulpfile.json/project.json');

Object.keys(gulpTasks).forEach(key => {
  const path = gulpTasks[key][0];
  const outPath = gulpTasks[key][1] || 'dist';
  const main = gulpTasks[key][2] || 'main';
  const template = gulpTasks[key][3] || 'index.html';
  exports['activity2020_' + key + '_dev'] = async function () {
    openWebpackDevServe(path, outPath, main, template);
  };
  exports['activity2020_' + key + '_prod'] = function () {
    return src('**/*.js', { allowEmpty: true })
      .pipe(webpackStream(webpackEntryOption(path, outPath, true, main, template)))
      .pipe(dest(path + outPath + '/'));
  };
});
           

這裡在dev任務時gulp是異步任務,為的是開啟webpack的devServer

當然,一些方法我封裝了一下,比如 openWebpackDevServe 和 webpackEntryOption ,具體可以去我的github上交流一下

注意

有的電腦可能需要 powershell 開啟權限,管理者執行 set-executionpolicy RemoteSigned

這個配置裡:

  1. 本項目以 webpack 打包為主,gulp 隻提供任務流
  2. node.js ^v12.14.3
  3. gulp -g ^v4.0.0 (全局更新可能會失敗,用 --force 覆寫即可)
  4. gulp-cli -g ^v2.2.1
  5. 若 npm 安裝失敗請使用 cnpm 或 yarn
webpack.prod.conf.js

-2020.5.27 更新-

-2020.6.8 更新-

-2020.6.18 更新-

const HtmlWebpackPlugin = require("html-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const VueLoaderPlugin = require("vue-loader/lib/plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const LodashWebpackPlugin = require("lodash-webpack-plugin");
const UglifyjsWebapckPlugin = require('uglifyjs-webpack-plugin');
const InsertHtmlPlugin = require("./public/insert-html-code-plugin");
const statistics = require("./public/statisticsTemplate");

module.exports = {
  mode: "production",
  output: {
    filename: "js/[name].build.js",
    globalObject: "this"
  },
  stats: {
    children: false // Tells stats whether to add information about the children.
  },
  performance: {
    maxEntrypointSize: 500000, // 入口檔案最大限制提示
    maxAssetSize: 200000 // 輸出檔案最大限制提示
  },
  optimization: {
    minimizer: [
      new UglifyjsWebapckPlugin({
        uglifyOptions: {
          compress: {
            // 設定打包時将console語句不打包進去
            drop_console: true
          }
        }
      })
    ],
    splitChunks: {
      cacheGroups: {
        // 處理入口chunk,同步的
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          chunks: 'initial',
          name: 'vendors'
        },
        // 處理異步chunk
        'async-vendors': {
          test: /[\\/]node_modules[\\/]/,
          minChunks: 2,
          chunks: 'async',
          name: 'async-vendors'
        },
        // 處理css
        styles: {
          name: 'styles',
          test: /\.css$/,
          chunks: 'all',
          enforce: true
        }
      }
    },
    // 為每個入口提取出webpack runtime子產品
    runtimeChunk: { name: 'manifest' }
  },
  plugins: [
    new HtmlWebpackPlugin({
      minify: { // 壓縮HTML檔案
        removeComments: true, // 移除HTML中的注釋
        collapseWhitespace: true, // 删除空白符與換行符
        minifyCSS: true, // 壓縮内聯css(使用clean-css進行的壓縮)
        minifyJS: true, // 壓縮html裡的js(使用uglify-js進行的壓縮)
        removeAttributeQuotes: true // 移除屬性的引号
      },
      hash: true,
      meta: false
    }),
    new VueLoaderPlugin(),
    new MiniCssExtractPlugin({
      filename: 'css/[name].css'
    }),
    new LodashWebpackPlugin(),
    new InsertHtmlPlugin({
      minimize: true,
      scriptCode: statistics,
      scriptPaths: [
        'https://cdn.bootcdn.net/ajax/libs/jquery/1.8.2/jquery.min.js'
      ]
    }),
    new CleanWebpackPlugin()
  ],
  externals: {
    jquery: "jQuery"
  },
  resolve: {
    extensions: [".vue", ".js", ".scss", ".sass", ".less", ".css", ".json"],
    alias: {
      vue$: "vue/dist/vue.min.js"
    }
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          "vue-style-loader",
          "style-loader",
          {
            loader: MiniCssExtractPlugin.loader,
            options: {
              esModule: true
            }
          },
          {
            loader: "css-loader",
            options: {
              importLoaders: 1
            }
          },
          {
            loader: "postcss-loader",
            options: {
              plugins: (loader) => [
                require("postcss-import")({
                  root: loader.resourcePath
                }),
                require("postcss-preset-env")(),
                require("cssnano")()
              ]
            }
          }
        ]
      },
      {
        test: /\.(sass|scss)$/,
        use: [
          "vue-style-loader",
          "style-loader",
          {
            loader: MiniCssExtractPlugin.loader,
            options: {
              esModule: true
            }
          },
          {
            loader: "css-loader",
            options: {
              importLoaders: 2
            }
          },
          {
            loader: "postcss-loader",
            options: {
              plugins: (loader) => [
                require("postcss-import")({
                  root: loader.resourcePath
                }),
                require("postcss-preset-env")(),
                require("cssnano")()
              ]
            }
          },
          "sass-loader"
        ]
      },
      {
        test: /\.less$/,
        use: [
          "vue-style-loader",
          "style-loader",
          {
            loader: MiniCssExtractPlugin.loader,
            options: {
              esModule: true
            }
          },
          {
            loader: "css-loader",
            options: {
              importLoaders: 2
            }
          },
          {
            loader: "postcss-loader",
            options: {
              plugins: (loader) => [
                require("postcss-import")({
                  root: loader.resourcePath
                }),
                require("postcss-preset-env")(),
                require("cssnano")()
              ]
            }
          },
          "less-loader"
        ]
      },
      {
        test: /\.vue$/,
        loader: "vue-loader"
      },
      {
        test: /\.(js|jsx)$/,
        loader: "babel-loader",
        exclude: file => (
          /node_modules/.test(file) &&
          !/\.vue\.js/.test(file)
        )
      },
      {
        test: /\.(png|jp?g|gif|svg|ico)$/,
        use: [
          {
            loader: "url-loader",
            options: {
              limit: 8192, // 小于8192位元組的圖檔打包成base 64圖檔
              name: "images/[name].[hash:8].[ext]",
              publicPath: "../",
              esModule: false
            }
          },
          {
            loader: "image-webpack-loader"
          }
        ]
      },
      {
        // 檔案依賴配置項——字型圖示
        test: /\.(woff|woff2|svg|eot|ttf)$/,
        use: [{
          loader: "file-loader",
          options: {
            limit: 8192,
            name: "fonts/[name].[hash:8].[ext]",
            publicPath: "./"
          }
        }]
      },
      {
        // 檔案依賴配置項——音頻
        test: /\.(wav|mp3|ogg)?$/,
        use: [{
          loader: "file-loader",
          options: {
            limit: 8192,
            name: "audios/[name].[hash:8].[ext]",
            publicPath: "./"
          }
        }]
      },
      {
        // 檔案依賴配置項——視訊
        test: /\.(ogg|mpeg4|webm|mp4)?$/,
        use: [{
          loader: "file-loader",
          options: {
            limit: 8192,
            name: "videos/[name].[hash:8].[ext]",
            publicPath: "./"
          }
        }]
      },
      {
        test: /\.(html)?$/,
        use: {
          loader: "html-loader",
          options: {
            minimize: false
          }
        }
      }
    ]
  }
}
           

暫時就這些吧,後續還要完善不少,但是已經可以出貨啦~~

繼續閱讀