demo15链接
首先新建一个文件夹:demo15,执行npm init -y初始化package.json,生成后的文件如下:

  1. {
  2. "name": "demo15",
  3. "version": "1.0.0",
  4. "description": "",
  5. "main": "index.js",
  6. "scripts": {
  7. "test": "echo \"Error: no test specified\" && exit 1"
  8. },
  9. "keywords": [],
  10. "author": "",
  11. "license": "ISC"
  12. }

我们先将无用的代码清除掉,只留下关键代码:

  1. {
  2. "scripts": {}
  3. }

首先安装webpack所需依赖

npm i webpack webpack-cli webpack-dev-server —save-dev

安装babel7,因为目前主要使用ES6来编写代码,所以需要转译

npm i @babel/core babel-loader @babel/preset-env @babel/plugin-transform-runtime —save-dev
npm i @babel/polyfill @babel/runtime

现在package.json中的依赖为:

  1. {
  2. "scripts": {},
  3. "license": "ISC",
  4. "devDependencies": {
  5. "@babel/core": "^7.4.5",
  6. "@babel/plugin-transform-runtime": "^7.4.4",
  7. "@babel/preset-env": "^7.4.5",
  8. "babel-loader": "^8.0.6",
  9. "webpack": "^4.33.0",
  10. "webpack-cli": "^3.3.4",
  11. "webpack-dev-server": "^3.7.1"
  12. },
  13. "dependencies": {
  14. "@babel/polyfill": "^7.4.4",
  15. "@babel/runtime": "^7.4.5"
  16. }
  17. }

新建.babelrc来配置babel插件,代码如下:

  1. {
  2. "presets": ["@babel/preset-env"],
  3. "plugins": ["@babel/plugin-transform-runtime"]
  4. }

新建.browserslistrc文件配置该项目所支持的浏览器版本:

  1. # 所支持的浏览器版本
  2. > 1% # 全球使用情况统计选择的浏览器版本
  3. last 2 version # 每个浏览器的最后两个版本
  4. not ie <= 8 # 排除小于 ie8 及以下的浏览器

在开始配置webpack.config.js文件之前,需要注意一下,因为我们现在有两种模式,production(生产)和development(开发)模式。

安装自动生成html依赖

npm i html-webpack-plugin html-loader clean-webpack-plugin —save-dev

安装css/字体图标处理依赖

npm i css-loader style-loader mini-css-extract-plugin optimize-css-assets-webpack-plugin —save-dev

安装scss处理依赖

npm i node-sass sass-loader —save-dev

为不同内核的浏览器加上CSS前缀

npm install postcss-loader autoprefixer —save-dev

图片及字体处理

npm i url-loader file-loader image-webpack-loader —save-dev

第三方js库

npm i jquery

安装了这些依赖后的package.json为:

  1. {
  2. "scripts": {},
  3. "license": "ISC",
  4. "devDependencies": {
  5. "@babel/core": "^7.4.5",
  6. "@babel/plugin-transform-runtime": "^7.4.4",
  7. "@babel/preset-env": "^7.4.5",
  8. "autoprefixer": "^9.6.0",
  9. "babel-loader": "^8.0.6",
  10. "clean-webpack-plugin": "^3.0.0",
  11. "css-loader": "^2.1.1",
  12. "file-loader": "^4.0.0",
  13. "html-loader": "^0.5.5",
  14. "html-webpack-plugin": "^3.2.0",
  15. "image-webpack-loader": "^5.0.0",
  16. "mini-css-extract-plugin": "^0.7.0",
  17. "node-sass": "^4.12.0",
  18. "optimize-css-assets-webpack-plugin": "^5.0.1",
  19. "postcss-loader": "^3.0.0",
  20. "sass-loader": "^7.1.0",
  21. "style-loader": "^0.23.1",
  22. "url-loader": "^2.0.0",
  23. "webpack": "^4.33.0",
  24. "webpack-cli": "^3.3.4",
  25. "webpack-dev-server": "^3.7.1"
  26. },
  27. "dependencies": {
  28. "@babel/polyfill": "^7.4.4",
  29. "@babel/runtime": "^7.4.5",
  30. "jquery": "^3.4.1"
  31. }
  32. }

之前我们大多都是写生产模式,也就是经常说的打包,但是我们日常开发项目,用的是开发模式。

只有在项目做完后,要部署到nginx上的时候才使用生产模式,将代码打包放到nginx

之所以要分两种模式是因为,开发模式下,需要加快编译的速度,可以热更新以及设置跨域地址,开启源码调试(devtool:’source-map‘)

而生产模式下,则需要
压缩js/css代码,拆分公用代码,拆分第三方的js库**等。

所以这里的配置我们分成三个文件夹来写,一个是生产配置,一个是开发配置,另外一个是基础配置。

即:webpack.base.conf.js(基础配置),webpack.dev.conf.js(开发配置),webpack.prod.conf.js(生产配置)
新建build文件夹,创建上述是哪个文件,项目结构为:
image.png

这里需要使用到一个插件,webpack-merge来合并配置,比如开发环境就合并开发环境+基础配置,生产就合并生产配置+基础配置

npm i webpack-werge —save-dev

首先简单写个webpack.base.conf.js的示例代码:

  1. const merge = require('webpack-merge')
  2. const productionConfig = require('./webpack.prod.conf') // 引入生产环境配置文件
  3. const developmentConfig = require('./webpack.dev.conf') // 引入开发环境配置文件
  4. const baseConfig = {} // ... 省略
  5. module.exports = env => {
  6. let config = env === 'production' ? productionConfig : developmentConfig
  7. return merge(baseConfig, config) // 合并 公共配置 和 环境配置
  8. }
  • 引入webpack-merge插件来合并配置
  • 引入生产环境和开发环境
  • 编写基础配置
  • 导出合并后的配置文件

在代码中区分不同环境:

  1. module.exports = env => {
  2. let config = env === 'production' ? productionConfig : developmentConfig
  3. return merge(baseConfig, config) // 合并 公共配置 和 环境配置
  4. }

这里的env在package.json中进行配置,修改script,添加’dev‘和’build‘命令
注意,这里有个—dev字段与 webpack.base.conf.js 中的 env 是联动的,告诉它当前是什么环境,然后合并成什么环境

  1. {
  2. "scripts": {
  3. "dev": "webpack-dev-server --env development --open --config build/webpack.base.conf.js",
  4. "build": "webpack --env production --config build/webpack.base.conf.js"
  5. }
  6. }

(一)编写基础配置


  1. const webpack = require('webpack')
  2. const merge = require('webpack-merge')
  3. const HtmlWebpackPlugin = require('html-webpack-plugin')
  4. const MiniCssExtractPlugin = require('mini-css-extract-plugin') // 将 css 单独打包成文件
  5. const CleanWebpackPlugin = require('clean-webpack-plugin')
  6. const path = require('path')
  7. const productionConfig = require('./webpack.prod.conf.js') // 引入生产环境配置文件
  8. const developmentConfig = require('./webpack.dev.conf.js') // 引入开发环境配置文件
  9. /**
  10. * 根据不同的环境,生成不同的配置
  11. * @param {String} env "development" or "production"
  12. */
  13. const generateConfig = env => {
  14. // 将需要的 Loader 和 Plugin 单独声明
  15. let scriptLoader = [
  16. {
  17. loader: 'babel-loader'
  18. }
  19. ]
  20. let cssLoader = [
  21. 'style-loader',
  22. 'css-loader',
  23. 'postcss-loader', // 使用 postcss 为 css 加上浏览器前缀
  24. 'sass-loader' // 使用 sass-loader 将 scss 转为 css
  25. ]
  26. let cssExtractLoader = [
  27. {
  28. loader: MiniCssExtractPlugin.loader
  29. },
  30. 'css-loader',
  31. 'postcss-loader', // 使用 postcss 为 css 加上浏览器前缀
  32. 'sass-loader', // 使用 sass-loader 将 scss 转为 css
  33. ]
  34. let fontLoader = [
  35. {
  36. loader: 'url-loader',
  37. options: {
  38. name: '[name]-[hash:5].min.[ext]',
  39. limit: 5000, // fonts file size <= 5KB, use 'base64'; else, output svg file
  40. publicPath: 'fonts/',
  41. outputPath: 'fonts/'
  42. }
  43. }
  44. ]
  45. let imageLoader = [
  46. {
  47. loader: 'url-loader',
  48. options: {
  49. name: '[name]-[hash:5].min.[ext]',
  50. limit: 10000, // size <= 10KB
  51. outputPath: 'images/'
  52. }
  53. },
  54. // 图片压缩
  55. {
  56. loader: 'image-webpack-loader',
  57. options: {
  58. // 压缩 jpg/jpeg 图片
  59. mozjpeg: {
  60. progressive: true,
  61. quality: 50 // 压缩率
  62. },
  63. // 压缩 png 图片
  64. pngquant: {
  65. quality: '65-90',
  66. speed: 4
  67. }
  68. }
  69. }
  70. ]
  71. let styleLoader =
  72. env === 'production'
  73. ? cssExtractLoader // 生产环境下压缩 css 代码
  74. : cssLoader // 开发环境:页内样式嵌入
  75. return {
  76. entry: { app: './src/app.js' },
  77. output: {
  78. publicPath: env === 'development' ? '/' : './',
  79. path: path.resolve(__dirname, '..', 'dist'),
  80. filename: '[name]-[hash:5].bundle.js',
  81. chunkFilename: '[name]-[hash:5].chunk.js'
  82. },
  83. module: {
  84. rules: [
  85. { test: /\.js$/, exclude: /(node_modules)/, use: scriptLoader },
  86. { test: /\.(sa|sc|c)ss$/, use: styleLoader },
  87. { test: /\.(eot|woff2?|ttf|svg)$/, use: fontLoader },
  88. { test: /\.(png|jpg|jpeg|gif)$/, use: imageLoader }
  89. ]
  90. },
  91. plugins: [
  92. // 开发环境和生产环境二者均需要的插件
  93. new HtmlWebpackPlugin({
  94. title: 'webpack4 实战',
  95. filename: 'index.html',
  96. template: path.resolve(__dirname, '..', 'index.html'),
  97. // chunks: ['app'],
  98. minify: {
  99. collapseWhitespace: true
  100. }
  101. }),
  102. new webpack.ProvidePlugin({ $: 'jquery' }),
  103. new CleanWebpackPlugin()
  104. ]
  105. }
  106. }
  107. module.exports = env => {
  108. let config = env === 'production' ? productionConfig : developmentConfig
  109. return merge(generateConfig(env), config) // 合并 公共配置 和 环境配置
  110. }

以上配置建议多看几遍熟悉熟悉,为什么要这样写

(二)编写开发环境配置


  1. const webpack = require("webpack");
  2. const path = require("path");
  3. module.exports = {
  4. mode: "development",
  5. devtool: "source-map", // 调试源码
  6. devServer: {
  7. contentBase: path.join(__dirname, "../dist/"),
  8. port: 8000,
  9. hot: true,
  10. overlay: true,
  11. proxy: {
  12. "/comments": {
  13. target: "https://m.weibo.cn",
  14. changeOrigin: true,
  15. logLevel: "debug",
  16. headers: {
  17. Cookie: ""
  18. }
  19. }
  20. },
  21. historyApiFallback: true
  22. },
  23. plugins: {
  24. new webpack.HotModuleReplacementPlugin(),
  25. new webpack.NamedChunksPlugin()
  26. }
  27. };

开发配置主要是设置跨域,开启源码调试,热更新

(三)编写生产环境配置


  1. const MiniCssExtractPlugin = require("mini-css-extract-plugin"); // 将 css 单独打包成文件
  2. const OptimizeCssAssetsPlugin = require("optimize-css-assets-webpack-plugin"); // 压缩css
  3. module.exports = {
  4. mode: "production",
  5. optimization: {
  6. splitChunks: {
  7. chunks: "all",
  8. cacheGroups: {
  9. jquery: {
  10. name: "chunk-jquery", // 单独将 jquery 拆包
  11. priority: 15,
  12. test: /[\\/]node_modules[\\/]jquery[\\/]/
  13. }
  14. }
  15. }
  16. },
  17. plugins: [
  18. new MiniCssExtractPlugin({
  19. filename: "[name].css",
  20. chunkFilename: "[id].css"
  21. }),
  22. // 压缩css
  23. new OptimizeCssAssetsPlugin({
  24. assetNameRegExp: /\.css$/g, //一个正则表达式,指示应优化/最小化的资产的名称。提供的正则表达式针对配置中ExtractTextPlugin实例导出的文件的文件名运行,而不是源CSS文件的文件名。默认为/\.css$/g
  25. cssProcessor: require("cssnano"), //用于优化\最小化 CSS 的 CSS处理器,默认为 cssnano
  26. cssProcessorOptions: { safe: true, discardComments: { removeAll: true } }, //传递给 cssProcessor 的选项,默认为{}
  27. canPrint: true //一个布尔值,指示插件是否可以将消息打印到控制台,默认为 true
  28. })
  29. ]
  30. };

生产配置主要是拆分代码,压缩css

(四)测试开发模式


运行 npm run dev
image.png
并且自动打开浏览器,图文和文字都出来了,打开控制台也能看到跨域成功、源码定位,因为将 devtool 设置为 ‘source-map’,所以就会生成 map 文件,体积较大
image.png
**

(五)测试生产模式


运行npm run build
image.png
打开 dist/index.html 文件
image.png

生产模式下跨域失败是很正常的,而且如果是 vue 项目打包完之后是无法直接打开 index.html 文件查看效果的,必须要放在服务器上,一般都是将打包后的文件放入 nginx 中,在 nginx 中配置跨域地址

还有一种配置 webpack 开发和生产环境的方式,会比较常用:

修改 webpack.base.conf.js

  1. const path = require('path')
  2. const webpack = require('webpack')
  3. const HtmlWebpackPlugin = require('html-webpack-plugin')
  4. const CleanWebpackPlugin = require('clean-webpack-plugin')
  5. module.exports = {
  6. entry: {
  7. app: './src/app.js'
  8. },
  9. output: {
  10. path: path.resolve(__dirname, '..', 'dist')
  11. },
  12. module: {
  13. rules: [
  14. {
  15. test: /\.js$/,
  16. exclude: /node_modules/,
  17. use: [
  18. {
  19. loader: 'babel-loader'
  20. }
  21. ]
  22. },
  23. {
  24. test: /\.(png|jpg|jpeg|gif)$/,
  25. use: [
  26. {
  27. loader: 'url-loader',
  28. options: {
  29. name: '[name]-[hash:5].min.[ext]',
  30. limit: 1000, // size <= 1KB
  31. outputPath: 'images/'
  32. }
  33. },
  34. // img-loader for zip img
  35. {
  36. loader: 'image-webpack-loader',
  37. options: {
  38. // 压缩 jpg/jpeg 图片
  39. mozjpeg: {
  40. progressive: true,
  41. quality: 65 // 压缩率
  42. },
  43. // 压缩 png 图片
  44. pngquant: {
  45. quality: '65-90',
  46. speed: 4
  47. }
  48. }
  49. }
  50. ]
  51. },
  52. {
  53. test: /\.(eot|ttf|svg)$/,
  54. use: {
  55. loader: 'url-loader',
  56. options: {
  57. name: '[name]-[hash:5].min.[ext]',
  58. limit: 5000, // fonts file size <= 5KB, use 'base64'; else, output svg file
  59. publicPath: 'fonts/',
  60. outputPath: 'fonts/'
  61. }
  62. }
  63. }
  64. ]
  65. },
  66. plugins: [
  67. // 开发环境和生产环境二者均需要的插件
  68. new HtmlWebpackPlugin({
  69. title: 'webpack4 实战',
  70. filename: 'index.html',
  71. template: path.resolve(__dirname, '..', 'index.html'),
  72. minify: {
  73. collapseWhitespace: true
  74. }
  75. }),
  76. new webpack.ProvidePlugin({ $: 'jquery' }),
  77. new CleanWebpackPlugin()
  78. ],
  79. performance: false
  80. }

修改 webpack.dev.conf.js

const webpack = require('webpack')
const merge = require('webpack-merge')
const commonConfig = require('./webpack.base.conf.js')

const path = require('path')

const devConfig = {
  mode: 'development',
  output: {
    filename: '[name].js',
    chunkFilename: '[name].js'
  },
  module: {
    rules: [
      {
        test: /\.(sa|sc|c)ss$/,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              importLoaders: 2 // 在一个 css 中引入了另一个 css,也会执行之前两个 loader,即 postcss-loader 和 sass-loader
            }
          },
          'postcss-loader', // 使用 postcss 为 css 加上浏览器前缀
          'sass-loader' // 使用 sass-loader 将 scss 转为 css
        ]
      }
    ]
  },
  devtool: 'cheap-module-eval-soure-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()
  ]
}

module.exports = merge(commonConfig, devConfig)

修改 webpack.prod.conf.js

const merge = require('webpack-merge')
const commonConfig = require('./webpack.base.conf.js')

const MiniCssExtractPlugin = require('mini-css-extract-plugin') // 将 css 单独打包成文件
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin') // 压缩 css

const prodConfig = {
  mode: 'production',
  output: {
    filename: '[name].[contenthash].js',
    chunkFilename: '[name].[contenthash].js'
  },
  devtool: 'cheap-module-source-map',
  module: {
    rules: [
      {
        test: /\.(sa|sc|c)ss$/,
        use: [
          {
            loader: MiniCssExtractPlugin.loader
          },
          {
            loader: 'css-loader',
            options: {
              importLoaders: 2 // 在一个 css 中引入了另一个 css,也会执行之前两个 loader,即 postcss-loader 和 sass-loader
            }
          },
          'postcss-loader', // 使用 postcss 为 css 加上浏览器前缀
          'sass-loader' // 使用 sass-loader 将 scss 转为 css
        ]
      }
    ]
  },
  optimization: {
    splitChunks: {
      chunks: 'all',
      cacheGroups: {
        jquery: {
          name: 'jquery', // 单独将 jquery 拆包
          priority: 15,
          test: /[\\/]node_modules[\\/]jquery[\\/]/
        },
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors'
        }
      }
    }
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: '[name]-[contenthash].css',
      chunkFilename: '[id]-[contenthash].css'
    }),
    // 压缩 css
    new OptimizeCssAssetsPlugin({
      assetNameRegExp: /\.css$/g, //一个正则表达式,指示应优化/最小化的资产的名称。提供的正则表达式针对配置中ExtractTextPlugin实例导出的文件的文件名运行,而不是源CSS文件的文件名。默认为/\.css$/g
      cssProcessor: require('cssnano'), //用于优化\最小化 CSS 的 CSS处理器,默认为 cssnano
      cssProcessorOptions: { safe: true, discardComments: { removeAll: true } }, //传递给 cssProcessor 的选项,默认为{}
      canPrint: true //一个布尔值,指示插件是否可以将消息打印到控制台,默认为 true
    })
  ]
}

module.exports = merge(commonConfig, prodConfig)

修改package.json的script配置命令

{
  "scripts": {
    "dev": "webpack-dev-server --open --config ./build/webpack.dev.conf.js",
    "build": "webpack --config ./build/webpack.prod.conf.js"
  }
}

在之前的基础上重新更改了配置,可以打包在看下