webpack4 enrty,output,library,babel7,publicPath,SplitChunks,多页面,postcss,eslint,stylelint,gzip

1.安装

npm init -y //生成默认的package.json文件

npm i webpack webpack-cli --save-dev //安装在 devDependencies依赖里面

2.默认配置文件  webpack.config.js

./node_modules/.bin/webpack 

//运行此指令,没有指令配置文件,默认是 webpack.config.js

//没有设置npm指令,直接调用依赖里面的webpack,后面会用npm指令,自动到依赖里面去查找

3.webpack.config.js

const path=require("path");

module.exports={

    mode:"production", //生产环境

    entry: "./src/index.js", //入口

    //打包的出口 
    output:{
        path: path.resolve(__dirname,'dist'), //打包的路径

        filename:"bundle.js" //打包后的文件名字
    }
}

webpack4 enrty,output,library,babel7,publicPath,SplitChunks,多页面,postcss,eslint,stylelint,gzip_第1张图片

// index.html

// 目前没用插件,先自己写个





    
    
    
    Document



    


4.Entry 入口

// entry String是单入口,Object,Array是多入口

// 如果传入一个字符串或字符串数组,chunk 会被命名为 main。
// 如果传入一个对象,则每个键(key)会是 chunk 的名称,该值描述了 chunk 的入口起点

module.exports={
    entry: "./src/index.js"
}

module.exports={
    entry: ["./src/index.js", "./src/behind.js"]
}

//上面两种打包后的文件都是 bundle.js

//下面这种会出现 app.js和 behind.js

module.exports = {
    mode: "production",
    entry: {
        app: "./src/index.js",
        behind: "./src/behind.js"
    },
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: "[name].js"
    }
}

5. Output 输出

5-1. filename  用的是占位符的模式

filename: "[name].[hash].js"

//每次构建过程中,唯一的 hash 生成
//就算是多入口的,每个入口的hash都是一样的,因为是同样的一次构建

filename: "[name].[chunkhash].js"

// 基于每个 chunk 内容的 hash

// 当通过多个入口起点(entry point)、代码拆分(code splitting)或各种插件(plugin)创建多个 bundle

 

filename: "[name].[id].js"

// 使用内部 chunk id

 

5-2. library 

 webpack 还可以用于打包 JavaScript library,比如 你正在编写一个名为 webpack-numbers 的小的 library,可以将数字 1 到 5 转换为文本表示,反之亦然,例如将 2 转换为 'two'

// 对于用途广泛的 library,我们希望它能够兼容不同的环境,例如 CommonJS,AMD,Node.js 或者作为一个全局变量。

// 为了让你的 library 能够在各种用户环境(consumption)中可用,需要在 output 中添加 library 属性

filename: 'webpack-numbers.js',
library: 'webpackNumbers'

// 当你在 import 引入模块时,这可以将你的 library bundle 暴露为名为 webpackNumbers 的全局变量
// 因为libraryTarget默认是var 全局变量
// 为了让 library 和其他环境兼容,还需要在配置文件中添加 libraryTarget 属性

filename: 'webpack-numbers.js',
library: 'webpackNumbers',
libraryTarget: 'umd' 

// 变量:作为一个全局变量,通过 script 标签来访问(libraryTarget:'var')
// this:通过 this 对象访问(libraryTarget:'this')
// window:通过 window 对象访问,在浏览器中(libraryTarget:'window')
// UMD:在 AMD 或 CommonJS 的 require 之后可访问(libraryTarget:'umd')
// libraryTarget: "global" - 入口起点的返回值将使用 output.library 中定义的值,分配给 global 对象的这个属性下

5-3. publicPath 

对于按需加载(on-demand-load)或加载外部资源(external resources)(如图片、文件等)来说,output.publicPath 是很重要的选项。如果指定了一个错误的值,则在加载这些资源时会收到 404 错误。

该选项的值是以 runtime(运行时) 或 loader(载入时) 所创建的每个 URL 为前缀。因此,在多数情况下,此选项的值都会以/结束

publicPath: "/assets/",


// 对于上面的配置,会多加/assets/路径

// 一个输出 HTML 的 loader 可能会像这样输出:  

// 加载 CSS 的一个图片: background-image: url(/assets/spinner.gif);


// 类型:

publicPath: "https://cdn.example.com/assets/", // CDN(总是 HTTPS 协议)

publicPath: "//cdn.example.com/assets/", // CDN (协议相同)

publicPath: "/assets/", // 相对于服务(server-relative)

publicPath: "assets/", // 相对于 HTML 页面

publicPath: "../assets/", // 相对于 HTML 页面

publicPath: "", // 相对于 HTML 页面(目录相同)

6.Babel v7

6-1. @babel/core  必须安装* 

babel 的核心功能包含在 @babel/core 模块中,要是进行代码转换的一些方法,可以将源代码根据配置转换成兼容目标环境的代码

 

6-2.@babel/cli  (可选)

是 babel 提供的命令行工具,用于命令行下编译源代码

 

6-3.@babel/plugin* (插件,按需求添加在plugins里面)

babel是通过插件来进行代码转换的,例如箭头函数使用@babel/plugin-transform-arrow-functions插件来进行转换

 

6-4.@babel/presets (js语法比较全面的插件包)   基本必须*

上面用的@babel/plugin-transform-arrow-functions解决箭头函数的转化,但是这只是一项功能

我们代码中仍然残留了其他 ES2015+ 的特性,我们希望对它们也进行转换

所以我们可以用官方的presets

@babel/preset-env支持所有现代的 JavaScript 的新特性

V7的stage的预设,已经被废弃

Babel 默认只转换新的 JavaScript 句法(syntax),而不转换新的 API
----这是因为babel 把 Javascript 语法分为syntax 和 api 

----api 指那些我们可以通过 函数重新覆盖的语法 ,类似 includes, map, includes, Promise, 凡是我们能想到重写的都可以归属到api。

----syntax 指像箭头函数,let,const,class, 依赖注入 Decorators等等这些

所以要用到垫片(@babel/polyfill)

6-5.@babel/polyfill

// @babel/polyfill由core-js2和regenerator-runtime组成

// 最新版本是core-js3,@babel/polyfill不支持从core-js2到core-js3的平滑过渡

-----所以会出现下面的提醒 start------
WARNING: We noticed you're using the useBuiltIns option without declaring a core-js version. Currently, we assume version 2.x when no version is passed. Since this default version will likely change in future versions of Babel, we recommend explicitly setting the core-js version you are using via the corejs option.
------ end ------

// 使用方法只要设置env的参数corejs: 3

// core-js3有很多优点,首先就是新,包含很多新特性,其次就是可以配合@babel/runtime,实现实例方法的支持
useBuiltIns:false(default):此时不对 polyfill 做操作。如果引入 @babel/polyfill,则无视配置的浏览器兼容,引入所有的 polyfill。(不太推荐)

useBuiltIns:"entry":根据配置的浏览器兼容,引入浏览器不兼容的 polyfill。需要在入口文件手动添加垫片,会自动根据 browserslist 替换成浏览器不兼容的所有 polyfill。
可以配合:
import "core-js/stable"
import "regenerator-runtime/runtime"
此第二方案可行,但是文件还是会大点

useBuiltIns:"usage":不需要在文件顶部手动引入@babel/polyfill,会根据代码中的使用进行按需添加,不包括实例方法

到此为止,仍然会有2个问题(上面的第二方案除外,已经用了corejs和runtime): 

1.高阶语法向低阶语法转化时引入了了很多helper函数(如_classCallCheck)。当文件数量很多时,每个文件都引入这些helper函数会使得文件体积增大,怎么这些helper函数抽离到单独的模块,然后按需引入呢

2.虽然polyfill是按需引入的,但是会污染全局命名空间,当你写的是公共库时,可能会与使用者本地的方法产生冲突。例如你在你的库中引入了polyfill中的Promise,使用者自身定义了自己的Promise,这就容易产生冲突。如何将你的公共库中引入的polyfill api隔离起来呢

 

------解决方案(transform-runtime 按需加载方案 的升级策略:)  
@babel/runtime-corejs3  @babel/plugin-transform-runtime ------

问题一: 

@babel/runtime以避免编译输出中的重复。运行时将编译到您的构建中。

@babel/runtime依赖 @babel/helpers和 regenerator-runtime,helper函数都可以从这里面引入,实现共享,但是不能自动引入

为了解决自动引入问题,于是 babel 提供了 @babel/plugin-transform-runtime 来替我们做这些转换


问题二:

@babel/runtime-corejs3是一个不会污染全局变量的,里面包含可以转换promises,symbols, collections, iterators等在内的polyfills,所以会比@babel/runtime能转换的东西多一些(包含@babel/runtime)

安装依赖后使用: @babel/plugin-transform-runtime option中的corejs参数

core-js3,实例方法这个问题得到了完美解决,具体配置如下

// .babelrc


{
    "presets": [
        [
            "@babel/preset-env",
            {
                "modules": false //对ES6的模块文件不做转化,以便使用tree shaking、sideEffects等
            }
        ]
    ],
    "plugins": [
        [
            "@babel/plugin-transform-runtime",
            {
                "corejs": 3
            }
        ]
    ]
}
// .browserslistrc

> 1%
last 2 versions
not ie <= 8
依赖:

devDependencies:{
  "@babel/core": "^7.6.0",
  "@babel/plugin-transform-runtime": "^7.6.2",
  "@babel/preset-env": "^7.6.0",
  "babel-loader": "^8.0.6"
},
dependencies:{
  "@babel/runtime-corejs3": "^7.6.3",
  "core-js": "^3.2.1",
}

 

 

Babel6和Babel7的runtime和transform-runtime对比:

webpack4 enrty,output,library,babel7,publicPath,SplitChunks,多页面,postcss,eslint,stylelint,gzip_第2张图片

7.splitChunks 抽取公共部分

optimization: {
    splitChunks: {
      chunks: "all",  //async表示异步,initial表示入口文件,all包含所有
      minSize: 30000, //模块的最小体积 单位bytes
      minChunks: 1,  //在拆分之前共享模块的最小块数
      maxAsyncRequests: 5, //按需加载的最大并行请求数
      maxInitialRequests: 3, //一个入口最大并行请求数
      automaticNameDelimiter: '~', //连接符
      name: true, //抽取出来文件的名字,默认为 true,表示自动生成文件名
      //缓存组,可以理解为分块的条件

      //特有的字段: 
      //test条件,priority优先级,
      //reuseExistingChunk:表示是否使用已有的 chunk,如果为 true 则表示如果当前的 chunk 包含的模块已经被抽取出去了,那么将不会重新生成新的

      //cacheGroups是一个对象,其中键是缓存组名称。所有的上面列出的选项是可能的:chunks,minSize,minChunks,maxAsyncRequests,maxInitialRequests,name,会覆盖外层的
      cacheGroups: {
        //定义base开头的公共块,包含 react 和 react-dom,优先级最高
        common: {
          name: "base",
          test: /(react|react-dom)/,
          priority: -5
        },
        //定义vendors开头的公共块,node_modules文件下的都在里面,比上面优先级低
        vendors: {
          name: "vendors",
          test: /[\\/]node_modules[\\/]/,
          priority: -10
        },
        //定义default开头的公共块,最少引用是2次,覆盖外层的一次
        default: {
          name: "default",
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true
        }
      }
    },
    runtimeChunk: {
      name: "manifest"  //生成manifest(runtime逻辑)
    }
  }

 打包文件:default的暂不满足条件,所以没有这个打包文件

webpack4 enrty,output,library,babel7,publicPath,SplitChunks,多页面,postcss,eslint,stylelint,gzip_第3张图片

8.多页面打包方案

const path = require("path");
const glob = require("glob");
const HtmlWebpackPlugin = require("html-webpack-plugin");

//多页面的通用方法

exports.setMulEntry = () => {

  const entry = {}; //入口

  const htmlWebpackPlugins = []; //多页面

  //glob进行遍历,遍历的条件自己定义
  //这里是src/pages/页面名字文件夹/index.js
  //结果是个路径的数组

  const entryFiles = glob.sync(
    path.resolve(__dirname, "../src/pages/*/index.js")
  );

  if (entryFiles && entryFiles.length > 0) {

    entryFiles.forEach(entryFile => {

      const match = entryFile.match(/src\/pages\/(.*)\/index\.js/); //匹配路径

      const pageName = match && match[1]; //获取路径上面的页面的名字

      entry[pageName] = entryFile; //设置入口的key

      htmlWebpackPlugins.push(
        new HtmlWebpackPlugin({
          filename: path.resolve(__dirname, `../dist/${pageName}.html`), //名字
          template: path.resolve(__dirname, `../htmls/${pageName}.html`), //模板
          inject: true,
          minify: {
            removeComments: true,
            collapseWhitespace: true,
            removeAttributeQuotes: true,
            minifyJS: true,
            minifyCSS: true
            // more options:
            // https://github.com/kangax/html-minifier#options-quick-reference
          },
          chunksSortMode: "dependency",

          chunks: [pageName, "manifest", "vendors", "base"] //依赖,根据SplitChunks,必要
        })
      );
    });
  }

  return {
    entry,
    htmlWebpackPlugins
  };
};

9.eslint+prettier格式化js代码

//.eslintrc.js

module.exports = {

    //根目录,不会再往上父级查找
    root: true,
    //全局变量的环境
    env: {
        browser: true,
        node: true,
        es6: true
        
    },
    parser: "babel-eslint", // 解析器
    parserOptions: {
        ecmaVersion: 7, //es的版本
        sourceType: "module", //es的模块
        ecmaFeatures: {
          "jsx": true
        }
    },
    // 插件的格式 plugin:插件名/配置名称
    // 插件名可以省略 eslint-plugin-
    // 省略包名的前缀eslint-config-
    // 扩展库
    extends: [
        "eslint:recommended",
        "plugin:react/recommended",
        "plugin:prettier/recommended"
    ],

    // 可以省略 eslint-plugin-
    // 插件
    plugins: [
        "react"
    ],

    // 插件的规则格式: 插件名/规则ID
    // 自定义规则
    rules: {
        "prettier/prettier": "error",  //prettier的格式不正确报错
        "react/jsx-uses-react": "error",  //plugin:react/recommended的格式不正确报错
        "react/jsx-uses-vars": "error", //plugin:react/recommended的格式不正确报错
    }
    
}

 

// eslint-loader
// 下面的加到webpack的module的rules的配置里面即可

exports.userEsLint = () => ({
  test: /\.js$/,
  loader: "eslint-loader",
  enforce: "pre", //前置
  include: [path.resolve(__dirname, "../src")], //限制src的js
  options: {
    formatter: require("eslint-friendly-formatter"), //ESLint的简单格式化程序/报告程序
    emitWarning: true
  }
});
// 依赖

devDependencies:{
  "babel-eslint": "^10.0.3",
  "eslint": "^6.5.1",
  "eslint-friendly-formatter": "^4.0.1",
  "eslint-loader": "^3.0.2",
  "eslint-plugin-react": "^7.16.0",
  "eslint-config-prettier": "^6.4.0",
  "prettier": "^1.18.2",
  "eslint-plugin-prettier": "^3.1.1",
}

 

10. stylelint规范css,scss,less等样式

// .stylelintrc.js

module.exports = {  
    extends: "stylelint-config-standard",  
    rules:{}  
}  
const StyleLintPlugin = require("stylelint-webpack-plugin");


plugins: [

    new StyleLintPlugin({
      files: ["**/*.{html,vue,css,sass,scss}"], //要格式化的文件
      context: "src", //上下文
      fix: true //保存时候是否自动修复,因为没有像eslint的快捷键,所以要设置下
    })

]

 

// 依赖

devDependencies:{
  "stylelint": "^11.1.1",
  "stylelint-config-standard": "^19.0.0",
  "stylelint-webpack-plugin": "^1.0.2",
}

11.postcss的配置(配合postcss-loader)

// .postcssrc.js

module.exports = {
    "plugins": {
        "postcss-import": {}, //引用node_modules模块的css的import
        "autoprefixer": {} //补充前缀
    }
}

 css通用loader配置:

// ------css loader的通用配置方法-------

const MiniCssExtractPlugin = require("mini-css-extract-plugin");

exports.cssLoaders = options => {
  options = options || {};

  const cssLoader = {
    loader: "css-loader",
    options: {
      sourceMap: options.sourceMap
    }
  };

  const postcssLoader = {
    loader: "postcss-loader",
    options: {
      sourceMap: options.sourceMap
    }
  };

  function generateLoaders(loader, innerLoaderOption) {

    //根据options参数usePostCss判断是否加postcss-loader
    const loaders = options.usePostCss
      ? [cssLoader, postcssLoader]
      : [cssLoader];

    // 如果参数有loader,就加到loaders数组里面去,一般是解析scss,less之类的
    if (loader) {
      loaders.push({
        loader: `${loader}-loader`,
        options: { ...innerLoaderOption, sourceMap: options.sourceMap } //内部配置参数
      });
    }
    
    // 参数options是否需要分离css,用MiniCssExtractPlugin
    if (options.extract) {
      return [MiniCssExtractPlugin.loader].concat(loaders);
    }
    return ["style-loader"].concat(loaders);
  }

  return {
    css: generateLoaders(),
    postcss: generateLoaders(),
    less: generateLoaders("less"),
    sass: generateLoaders("sass", { indentedSyntax: true }),
    scss: generateLoaders("sass")
  }; //针对不用的格式使用不用的loader数组
};

//组合成webpack的module的rules的格式
exports.styleLoaders = options => {
  const output = [];
  const loaders = exports.cssLoaders(options);
  for (const extension in loaders) {
    output.push({
      test: new RegExp(`\\.${extension}$`),
      use: loaders[extension]
    });
  }
  return output;
};

12.gzip压缩

// compression-webpack-plugin插件
// 新版没有asset选项,用filename

if (config.build.productionGzip) {
  const CompressionWebpackPlugin = require("compression-webpack-plugin");

  prodConfig.plugins.push(
    new CompressionWebpackPlugin({
      filename: "[path].gz[query]", // 目标资源名称
      // 匹配
      test: new RegExp(
        "\\.(" + config.build.productionGzipExtensions.join("|") + ")$"
      ),
      threshold: 10240, // 只处理比这个值大的资源 单位:字节
      minRatio: 0.8 // 只有压缩率比这个值小的资源才会被处理
    })
  );
}

 如何看gzip压缩已经成功开启:

webpack4 enrty,output,library,babel7,publicPath,SplitChunks,多页面,postcss,eslint,stylelint,gzip_第4张图片

打开chrome的Content-Encoding,如果有gzip就表示该文件启用了,没有就表示没有启用:

webpack4 enrty,output,library,babel7,publicPath,SplitChunks,多页面,postcss,eslint,stylelint,gzip_第5张图片

13.配置文件

// webpack.base.conf.js

const path = require("path");
const config = require("./config");
const StyleLintPlugin = require("stylelint-webpack-plugin");
const utils = require("./config/utils");

function resolve(dir) {
  return path.resolve(__dirname, dir);
}

module.exports = {
  entry: utils.setMulEntry().entry,
  output: {
    path: config.dev.assetsRoot,
    filename: "[name].js",
    publicPath:
      process.env.NODE_ENV === "production"
        ? config.build.assetsPublicPath
        : config.dev.assetsPublicPath
  },
  resolve: {
    extensions: [".js", ".scss", ".css"],
    alias: {
      "@": resolve("src"),
      "@img": resolve("src/images")
    }
  },
  module: {
    rules: [
      ...[utils.userEsLint()],
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: ["babel-loader"]
      },
      {
        test: /\.(jpg|png|gif|svg)$/,
        exclude: /node_modules/,
        use: [
          {
            loader: "url-loader",
            options: {
              limit: 10240,
              name: utils.assetsPath("img/[name].[hash:7].[ext]")
            }
          }
        ]
      },
      {
        test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
        loader: "url-loader",
        options: {
          limit: 10000,
          name: utils.assetsPath("fonts/[name].[hash:7].[ext]")
        }
      }
    ]
  },
  plugins: [
    new StyleLintPlugin({
      files: ["**/*.{html,vue,css,sass,scss}"],
      context: "src",
      fix: true
    })
  ]
};

 

// webpack.dev.conf.js

const baseConfig = require("./webpack.base.conf");
const merge = require("webpack-merge");
const config = require("./config");
const webpack = require("webpack");
const CopyWebpackPlugin = require("copy-webpack-plugin");
const FriendlyErrorsPlugin = require("friendly-errors-webpack-plugin"); //友好错误console界面
const portfinder = require("portfinder");
const path = require("path");
const utils = require("./config/utils");

const HOST = process.env.HOST;
const PORT = process.env.PORT && Number(process.env.PORT);

const devConfig = merge(baseConfig, {
  mode: "development",
  module: {
    rules: utils.styleLoaders({
      sourceMap: config.dev.cssSourceMap,
      usePostCss: true
    })
  },
  devtool: config.dev.devtool,
  devServer: {
    clientLogLevel: "warning",
    hot: true,
    contentBase: false, //CopyWebpackPlugin替代
    host: HOST || config.dev.host || "localhost",
    port: PORT || config.dev.port || 8080,
    open: config.dev.autoOpenBrowser, //是否运行自动开启浏览器
    //覆盖全屏错误
    overlay: config.dev.errorOverlay
      ? { warnings: false, errors: true }
      : false,
    publicPath: config.dev.assetsPublicPath,
    proxy: config.dev.proxyTable || {}, //代理
    quiet: true,
    //监听的配置
    watchOptions: {
      ignored: /node_modules/,
      poll: config.dev.poll
    }
  },
  plugins: [
    ...utils.setMulEntry().htmlWebpackPlugins, //多页面配置
    new webpack.DefinePlugin({
      "process.env": require("./config/dev.env")
    }),
    new webpack.HotModuleReplacementPlugin(), //热加载插件
    new webpack.NamedModulesPlugin(), // 展示更新的文件名
    //在编译出现错误时,使用 NoEmitOnErrorsPlugin 来跳过输出阶段。这样可以确保输出资源不会包含错误
    new webpack.NoEmitOnErrorsPlugin(),

    //复制static的静态文件
    new CopyWebpackPlugin([
      {
        from: path.resolve(__dirname, "static"),
        to: config.dev.assetsSubDirectory,
        ignore: [".*"]
      }
    ])
  ]
});

//这是错误弹框,可以没有
const createNotifierCallback = () => {
  const notifier = require("node-notifier");

  return (severity, errors) => {
    if (severity !== "error") return;

    const error = errors[0];

    notifier.notify({
      title: "my-webpack",
      message: severity + ": " + error.file
    });
  };
};

// portfinder处理端口的冲突 如8080自动变为8081
module.exports = new Promise((resolve, reject) => {
  portfinder.basePort = PORT || devConfig.devServer.port;
  portfinder.getPort((err, port) => {
    if (err) {
      reject(err);
    } else {
      process.env.PORT = port;
      devConfig.devServer.port = port;
      devConfig.plugins.push(
        new FriendlyErrorsPlugin({
          compilationSuccessInfo: {
            messages: [
              `webpack应用的系统地址: http://${devConfig.devServer.host}:${port}`
            ]
          },
          onErrors: config.dev.notifyOnErrors
            ? createNotifierCallback()
            : undefined
        })
      );
      resolve(devConfig);
    }
  });
});
// webpack.prod.conf.js

const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const merge = require("webpack-merge");
const utils = require("./config/utils");
const config = require("./config");
const webpack = require("webpack");
const env = require("./config/prod.env");
const path = require("path");
const CopyWebpackPlugin = require("copy-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const baseConfig = require("./webpack.base.conf");

const prodConfig = merge(baseConfig, {
  mode: "production",
  module: {
    rules: utils.styleLoaders({
      sourceMap: config.build.productionSourceMap,
      extract: true,
      usePostCss: true
    })
  },
  devtool: config.build.productionSourceMap ? config.build.devtool : false,
  output: {
    path: config.build.assetsRoot,
    filename: utils.assetsPath("js/[name].[chunkhash:8].js"),
    chunkFilename: utils.assetsPath("js/[name]-[id]-[chunkhash:8].js")
  },
  optimization: {
    splitChunks: {
      chunks: "all",
      minSize: 30000,
      minChunks: 1,
      maxAsyncRequests: 5,
      maxInitialRequests: 3,
      automaticNameDelimiter: "~",
      name: true,
      cacheGroups: {
        common: {
          name: "base",
          test: /(react|react-dom)/,
          priority: -5
        },
        vendors: {
          name: "vendors",
          test: /[\\/]node_modules[\\/]/,
          priority: -10
        },
        default: {
          name: "default",
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true
        }
      }
    },
    runtimeChunk: {
      name: "manifest"
    }
  },
  plugins: [
    ...utils.setMulEntry().htmlWebpackPlugins,
    new webpack.DefinePlugin({
      "process.env": env
    }),
    new MiniCssExtractPlugin({
      filename: utils.assetsPath("css/[name].[hash:8].css")
    }),
    new webpack.HashedModuleIdsPlugin(), // 用于vender的moudule.id会根据解析递增的问题
    // copy custom static assets
    new CopyWebpackPlugin([
      {
        from: path.resolve(__dirname, "./static"),
        to: config.build.assetsSubDirectory,
        ignore: [".*"]
      }
    ]),
    //清理文件,排除.bat的启动文件
    new CleanWebpackPlugin({
      cleanOnceBeforeBuildPatterns: ["**/*", "!*.bat"]
    })
  ]
});

if (config.build.productionGzip) {
  const CompressionWebpackPlugin = require("compression-webpack-plugin");

  prodConfig.plugins.push(
    new CompressionWebpackPlugin({
      filename: "[path].gz[query]", // 目标资源名称
      // 匹配
      test: new RegExp(
        "\\.(" + config.build.productionGzipExtensions.join("|") + ")$"
      ),
      threshold: 10240, // 只处理比这个值大的资源 单位:字节
      minRatio: 0.8 // 只有压缩率比这个值小的资源才会被处理
    })
  );
}

if (config.build.bundleAnalyzerReport) {
  const { BundleAnalyzerPlugin } = require("webpack-bundle-analyzer"); //分析打包文件体积大小
  prodConfig.plugins.push(new BundleAnalyzerPlugin());
}

module.exports = prodConfig;
// config的文件夹

// dev.env.js

const merge = require("webpack-merge");
const prodEnv = require("./prod.env");

module.exports = merge(prodEnv, {
  NODE_ENV: '"development"'
});

// prod.env.js

module.exports = {
  NODE_ENV: '"production"'
};

// index.js

const path = require("path");

module.exports = {
  dev: {
    proxyTable: {},
    assetsSubDirectory: "static",
    assetsPublicPath: "/",
    // host: "172.18.1.164",
    // port: 8080,
    autoOpenBrowser: false,
    errorOverlay: true,
    notifyOnErrors: true,
    poll: false,
    useEslint: false,
    showEslintErrorsInOverlay: false,
    devtool: "cheap-module-eval-source-map",
    cacheBusting: true,
    cssSourceMap: true,
    assetsRoot: path.resolve(__dirname, "../dist")
  },
  build: {
    // Paths
    assetsRoot: path.resolve(__dirname, "../dist"),
    assetsSubDirectory: "static",
    assetsPublicPath: "/",
    productionSourceMap: true,
    devtool: "source-map",
    productionGzip: true,
    productionGzipExtensions: ["js", "css"],
    bundleAnalyzerReport: false
  }
};

// utils.js 通用配置方法

const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const path = require("path");
const glob = require("glob");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const config = require("./index");

exports.assetsPath = pathname => {
  const prev =
    process.env.NODE_ENV === "production"
      ? config.build.assetsSubDirectory
      : config.dev.assetsSubDirectory;
  return path.posix.join(prev, pathname);
};

exports.cssLoaders = options => {
  options = options || {};

  const cssLoader = {
    loader: "css-loader",
    options: {
      sourceMap: options.sourceMap
    }
  };

  const postcssLoader = {
    loader: "postcss-loader",
    options: {
      sourceMap: options.sourceMap
    }
  };

  function generateLoaders(loader, innerLoaderOption) {
    const loaders = options.usePostCss
      ? [cssLoader, postcssLoader]
      : [cssLoader];
    if (loader) {
      loaders.push({
        loader: `${loader}-loader`,
        options: { ...innerLoaderOption, sourceMap: options.sourceMap }
      });
    }
    if (options.extract) {
      return [MiniCssExtractPlugin.loader].concat(loaders);
    }
    return ["style-loader"].concat(loaders);
  }

  return {
    css: generateLoaders(),
    postcss: generateLoaders(),
    less: generateLoaders("less"),
    sass: generateLoaders("sass", { indentedSyntax: true }),
    scss: generateLoaders("sass")
  };
};

exports.styleLoaders = options => {
  const output = [];
  const loaders = exports.cssLoaders(options);
  for (const extension in loaders) {
    output.push({
      test: new RegExp(`\\.${extension}$`),
      use: loaders[extension]
    });
  }
  return output;
};

exports.setMulEntry = () => {
  const entry = {};

  const htmlWebpackPlugins = [];

  const entryFiles = glob.sync(
    path.resolve(__dirname, "../src/pages/*/index.js")
  );

  if (entryFiles && entryFiles.length > 0) {
    entryFiles.forEach(entryFile => {
      const match = entryFile.match(/src\/pages\/(.*)\/index\.js/);
      const pageName = match && match[1];

      entry[pageName] = entryFile;

      htmlWebpackPlugins.push(
        new HtmlWebpackPlugin({
          filename: path.resolve(__dirname, `../dist/${pageName}.html`),
          template: path.resolve(__dirname, `../htmls/${pageName}.html`),
          inject: true,
          minify: {
            removeComments: true,
            collapseWhitespace: true,
            removeAttributeQuotes: true,
            minifyJS: true,
            minifyCSS: true
            // more options:
            // https://github.com/kangax/html-minifier#options-quick-reference
          },
          chunksSortMode: "dependency",
          chunks: [pageName, "manifest", "vendors", "base"]
        })
      );
    });
  }

  return {
    entry,
    htmlWebpackPlugins
  };
};

exports.userEsLint = () => ({
  test: /\.js$/,
  loader: "eslint-loader",
  enforce: "pre",
  include: [path.resolve(__dirname, "../src")],
  options: {
    formatter: require("eslint-friendly-formatter"),
    emitWarning: true
  }
});

项目目录:

webpack4 enrty,output,library,babel7,publicPath,SplitChunks,多页面,postcss,eslint,stylelint,gzip_第6张图片 

 

 

 

你可能感兴趣的:(webpack,webpack,stylelint,eslint,entry,ouput,publicPath,library,postcss,gzip,babel,splitChunks,多页面)