webpack搭建简单vue项目 第 6.5 章 webpack-config.js 拆分配置

480 阅读4分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

本文同时参与 「掘力星计划」                   ,赢取创作大礼包,挑战创作激励金

前言

我们开发的时候看的有 开发、生产、预生产等环境,当然不一样的环境肯定需要不一样的组件

但是,总不能每个配置都塞到一个webpack.confing.js下面吧?所以就有了配置拆解

拆分配置要点

一个踩坑的小栗子:

package.json

"build": "cross-env NODE_ENV=production webpack --progress --hide-modules --config ./build/webpack.config.base",

--config 自定义路径的时候需要添加 不然会报错

da0f8e9f2cdabfb1ad82f4d154b56192.jpg

开始拆解

小栗子

webpack.config.dev.js

const { merge } = require("webpack-merge")
const webpack = require("webpack")
const base = require("./webpack.config.base")
// 是webpack的插件,为模块提供中间缓存步骤。
// const HardSourceWebpackPlugin = require("hard-source-webpack-plugin") // 热更新 冷启动
module.exports = merge(base, {
  mode: "development",
  // SourceMap是一种映射关系。当项目运行后,如果出现错误,错误信息只能定位到打包后文件中错误的位置。
  // 如果想查看在源文件中错误的位置,则需要使用映射关系,找到对应的位置。
  devtool: "source-map",
  /* 配置webpack-dev-serve */
  /* 跨域也需要靠这个 */
  devServer: {
    historyApiFallback: true,
    overlay: true,
    openPage: "page1.html#/home",
    // 3)有服务端,但是不想用代理来处理,而是在服务端中启动webpack,web端口也用服务端端口
    //2)前端mock模拟数据,app为express里的app
    // before (app) {
    //   console.log('app', 111)
    //   app.get('/user', (req, res) => {
    //     res.json({ name: 'jack,before' })
    //   });
    // }
    // 1)
    // proxy: {// 重写方式,代理
    //   '/api': {
    //     target: 'http://localhost:3000',
    //     // changeOrigin: true,
    //     pathReWrite: { '^/api': '' }
    //   }
    // }
  },
  // HMR 绝对不能被用在生产环境。Hot Module Replacement
  plugins: [new webpack.HotModuleReplacementPlugin()],
})

webpack.config.prod.js

const { merge } = require("webpack-merge")
const base = require("./webpack.config.base")
const TerserPlugin = require("terser-webpack-plugin")
const BundleAnalyzerPlugin = require("webpack-bundle-analyzer")
  .BundleAnalyzerPlugin
module.exports = merge(base, {
  optimization: {
    // 优化项
    minimizer: [
      new TerserPlugin({
        cache: true, // 开启缓存
        parallel: true, // 支持多进程
        sourceMap: true,
      }),
    ],
    splitChunks: {
      chunks: "async",
      minSize: 20000,
      maxSize: 0,
      minChunks: 1,
      maxAsyncRequests: 30,
      maxInitialRequests: 30,
      automaticNameDelimiter: "~",
      name: true,
      cacheGroups: {
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          priority: -10,
        },
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true,
        },
      },
    },
  },
  mode: "production",
  devtool: false,
  plugins: [
    new BundleAnalyzerPlugin({
      analyzerMode: "server",
      analyzerHost: "127.0.0.1",
      analyzerPort: 8889,
      reportFilename: "report.html",
      defaultSizes: "parsed",
      openAnalyzer: true,
      generateStatsFile: false,
      statsFilename: "stats.json",
      statsOptions: null,
      logLevel: "info",
    }),
  ],
})

webpack.config.base.js

const path = require("path")
const webpack = require("webpack")
// 使用该插件,会自动创建并更新html文件
const HtmlWebpackPlugin = require("html-webpack-plugin")
// 使用该插件 会清理每次打包后, 过去遗留在dist中的旧代码
const { CleanWebpackPlugin } = require("clean-webpack-plugin")
const MiniCssExtractPlugin = require("mini-css-extract-plugin")
// 使用该插件 , 会解析vue文件
const VueLoaderPlugin = require("vue-loader/lib/plugin")
// const TerserPlugin = require('terser-webpack-plugin')
const fs = require("fs")
const pagesDirPath = path.resolve(__dirname, "../src/pages")
// 编译进度条
const WebpackBar = require("webpackbar")
// webpack 美化工具
const DashboardPlugin = require("webpack-dashboard/plugin")
// 自动生成路由
const VueRouteWebpackPlugin = require("@xiyun/vue-route-webpack-plugin")
// 加速编译
const HappyPack = require("happypack")
const os = require("os")
const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length })
/**
 * 通过约定,降低编码复杂度
 * 每新增一个入口,即在src/pages目录下新增一个文件夹,以页面名称命名,内置一个index.js作为入口文件
 * 通过node的文件api扫描pages目录
 * 这样可以得到一个形如{page1: "入口文件地址", page2: "入口文件地址", ...}的对象
 */
const getEntries = () => {
  const result = fs.readdirSync(pagesDirPath)
  const entry = {}
  result.forEach((item) => {
    entry[item] = path.resolve(__dirname, `../src/pages/${item}/main.ts`)
  })
  return entry
}
/**
 * 扫描pages文件夹,为每个页面生成一个插件实例对象
 */
const generatorHtmlWebpackPlugins = () => {
  const arr = []
  const result = fs.readdirSync(pagesDirPath)
  result.forEach((item) => {
    // 判断页面目录下有无自己的index.html
    let templatePath
    const selfTemplatePath = pagesDirPath + `/${item}/index.html`
    const publicTemplatePath = path.resolve(
      __dirname,
      "../src/public/index.html"
    )
    try {
      fs.accessSync(selfTemplatePath)
      templatePath = selfTemplatePath
    } catch {
      templatePath = publicTemplatePath
    }
    arr.push(
      new HtmlWebpackPlugin({
        template: templatePath, // html模板路径
        filename: `${item}.html`, // 生成的html存放路径,相对于 path
        chunks: ["manifest", "vendor", item], // 加载指定模块中的文件,否则页面会加载所有文件
        hash: false, // 为静态资源生成hash值
      })
    )
  })
  return arr
}
/**
 * 扫描pages文件夹,为每个页面生成路由 自动生成路由emmmm失败,暂时先注释
 */
const vueRoutePlugins = () => {
  const arr = []
  const result = fs.readdirSync(pagesDirPath)
  result.forEach((item) => {
    console.log(item)
    arr.push(
      new VueRouteWebpackPlugin({
        // 文件扩展名,默认只查询 .vue 类型的文件,根据实际需要可以进行扩展
        extension: ["vue", "js", "jsx"],
        // 配置 import 路径前缀
        prefix: "@/",
        // 插件扫描的项目目录,默认会扫描 "src/pages" 下的子目录
        directory: `src/pages/${item}`,
        // 生成的路由文件存放地址,默认存放到 "src/router/index.js"
        routeFilePath: `src/pages/${item}/router/children.ts`,
        // 生成的文件中的 import 路径是否使用双引号规范,默认使用
        // 注意:生成的路由文件中的 path 的引号是原封不动使用用户的
        doubleQoute: true,
      })
    )
  })
  return arr
}

const createLoader = (name, options) => {
  return {
    loader: `${name}-loader`,
    options: options,
  }
}
// 样式引用
const cssLoader = (type) => {
  const options = {
    sourceMap: false,
  }
  return [
    createLoader("vue-style", options),
    {
      loader: MiniCssExtractPlugin.loader,
      options: { hmr: true },
    },
    createLoader("css", options),
  ].concat(
    type === "css"
      ? []
      : []
          .concat(type === "sass" ? [{ loader: "resolve-url-loader" }] : [])
          .concat([createLoader(type)])
  )
}
module.exports = {
  // 将编译、打包、压缩后的代码映射回源代码的过程。打包压缩后的代码不具备良好的可读性,想要调试源码就需要 soucre map。
  devtool: "source-map",
  mode: "development", //  webpack4.x版本中需要加入这个属性
  /* webpack 入口起点*/
  // 入口,起点或是应用程序的起点入口。从这个起点开始,应用程序启动执行。如果传递一个数组,那么数组的每一项都会执行。
  // 每个 HTML 页面都有一个入口起点。单页应用(SPA):一个入口起点,多页应用(MPA):多个入口起点。
  // 一般指向项目中,src目录下的main.js文件
  entry: getEntries(),
  /* webpack 输出*/
  // 输出, 指示 webpack 如何去输出、以及在哪里输出你的「bundle、asset 和其他你所打包或使用 webpack 载入的任何内容」
  // 指的是通过webpack打包后的生成的文件以什么名称, 放置在什么位置,  这里一般放置在dist目录下,
  // output: {
  //   path: path.resolve(__dirname, './dist'), // 项目的打包文件路径
  //   publicPath: '/dist/', // 通过devServer访问路径
  //   filename: 'build.js', // 打包后的文件名
  // },
  // 这个属性,用于设定项目中不同类型的模块所对应的处理规则, 即用到的一些, 诸如sass,less, css, vue, 图片, 文件, 都在
  // 这个属性中进行设置处理规则, 当然, 都会有对应处理的loader.  loader 用于对模块的源代码进行转换
  module: {
    rules: [
      {
        test: /.vue$/, // 匹配对象的后缀, 如这里匹配.vue文件
        loader: "vue-loader", // 用于转换该文件类型的loader, 依赖于 vue-template-compiler,需要额外安装
        options: {
          // 内部配置
          transformAssetUrls: {
            video: ["src", "poster"],
            source: "src",
            img: "src",
            image: "xlink:href",
            embed: "src",
          },
        },
      },
      {
        test: /\.(js|ts)$/,
        // loader: 'babel-loader',
        //把对.js 的文件处理交给id为happyBabel 的HappyPack 的实例执行
        loader: "happypack/loader?id=happyBabel",
        //排除node_modules 目录下的文件
        exclude: /node_modules/,
      },
      {
        test: /\.(js|vue|tsx?)$/,
        exclude: /node_modules/,
        /**
         * 预先进行
         */
        enforce: "pre",
        loader: "eslint-loader",
      },
      {
        test: /\.ts?$/,
        loader: "ts-loader",
        exclude: /node_modules/,
        // 必须添加 不然会报错
        options: { appendTsSuffixTo: [/\.vue$/] },
      },
      {
        test: /\.css$/,
        use: cssLoader("css"),
      },
      {
        test: /.less$/,
        use: cssLoader("less"),
      },
      {
        test: /\.sc|ass$/,
        use: cssLoader("sass"),
      },
      {
        test: /\.(eot|svg|ttf|woff|woff2)(\?\S*)?$/,
        use: [
          "file-loader",
          {
            loader: "image-webpack-loader",
            options: {
              name: "img/[name][hash:8].[ext]",
            },
          },
        ],
      },
    ],
  },
  /* 配置webpack-dev-serve */
  devServer: {
    historyApiFallback: true,
    overlay: true,
    openPage: "page1.html#/home",
  },
  /* 插件配置 */
  // 自定义webpack构建过程, 例如,当多个 bundle 共享一些相同的依赖,使用 CommonsChunkPlugin 有助于提取这些依赖到共享的 bundle 中,来避免重复打包
  plugins: [
    /* HTML 生成插件 */
    ...generatorHtmlWebpackPlugins(),
    // 添加 进度条
    new WebpackBar(),
    // 美化工具
    new DashboardPlugin(),
    new MiniCssExtractPlugin(),
    new CleanWebpackPlugin(),
    new webpack.HashedModuleIdsPlugin(),
    new VueLoaderPlugin(),
    ...vueRoutePlugins(),
    new HappyPack({
      //用id来标识 happypack处理那里类文件
      id: "happyBabel",
      //如何处理  用法和loader 的配置一样
      loaders: [
        {
          loader: "babel-loader",
          options: {
            // 按需加载lodash.js
            plugins: ["lodash"],
            presets: ["@babel/preset-env"],
          },
        },
      ],
      //共享进程池
      threadPool: happyThreadPool,
      //允许 HappyPack 输出日志
      verbose: true,
    }),
  ],
  // eslint-disable-next-line no-dupe-keys
  mode: "development",
  // 配置模块如何被解析, 即设定相对应模块的解析规则
  resolve: {
    // 自动补全的扩展名
    extensions: [".js", ".css", ".vue", ".json", ".ts"],
    // 默认路径代理
    // 例如 import Vue from 'vue',会自动到 'vue/dist/vue.common.js'中寻找
    // 这样可以使之后在开发项目的时候, 引用文件时不必关注不同层级的问题
    alias: {
      "@": path.join(__dirname, "../", "src"),
      "@api": path.join(__dirname, "../", "src/api"),
      "@styles": path.join(__dirname, "../", "src/styles"),
      "@config": path.join(__dirname, "../", "config"),
      vue$: "vue/dist/vue.esm.js",
      "@components": path.join(__dirname, "../", "src/components"),
    },
  },
}
/*
修改webpack.config.js,判断NODE_ENV为production时,压缩js代码
*/
// if (process.env.NODE_ENV === 'production') {
//   module.exports.devtool = '#source-map'

//   module.exports.optimization = {
//     minimizer: [
//       new TerserPlugin({
//         cache: true, // 开启缓存
//         parallel: true, // 支持多进程
//         sourceMap: true,
//       }),
//     ],
//   }
// }

src=http___dingyue.ws.126.net_1kNX5UOlvvZhqzVi4eMVTefclpP9NYP6nSIQPkU9McOAq1553651252093compressflag.jpg&refer=http___dingyue.ws.126.jpg