1 webpack使用来做什么的

在我们愉快的使用vue,React等框架时,听说过webpack,确很少了解过它,在vue-cli3中和create-react-app等脚手架中往往看不到webpack的身影,在vue-cli3中,它的作者使用了vue.config.js来覆盖它,当在它最里面还是webpack。在create-react-app中你要使用npm run eject才能把webpack的配置暴露出来。vue和React是现在流行的前端框架都在使用webpack。那webpack是用来做什么的了?当然不用我说你的知道,它是用来打包的。官网上是这么说的“本质上,webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle”。通俗的说就是将 import 包进行整理,翻译成浏览器能运行的程序。

2 大体流程

  1. 确定入口:根据配置中的 entry 找出所有的入口文件
  2. 编译模块:从入口文件出发,调用所有配置的 Loader
    对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理;
  3. 完成模块编译:在经过第4步使用 Loader 翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系;
  4. 输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk
    转换成一个单独的文件加入到输出列表,
  5. 输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统。

3 从开始哪转换

const config = {
  entry: {
    app: './src/app.js',
  }
};

入口文件就是webpack要开始翻译打包的初始文件,然后根据这个文件找到它的依赖包,并把他的依赖包进行翻译

4 如何转换

如何翻译这件事情就要交给loader了。

module: {
    rules: [
    {
        test: /\.css$/,
        use: [
          { loader: 'style-loader' },
          {
            loader: 'css-loader',
            options: {
              modules: true
            }
          }
        ]
      }
    ]
 }

对应的文件交给对应的loader进行转换,比如上述代码中的根据正则,以.css结尾的文件先交个css-loader转换后再给style-loader转换。更多loader,请查看webpack官方提供的

5 转换过中的webpack进行的动作

在打包期间,webpack要做代码切割,代码压缩等事情,而这些事情loader是无法解决的,所以交给了plugins(插件)。插件也是webpack的支柱功能,webpack 插件是一个具有 apply 属性的 JavaScript 对象。apply 属性会被 webpack compiler 调用,并且 compiler 对象可在整个编译生命周期访问

plugins: [
    new webpack.optimize.UglifyJsPlugin(),
    new webpack.HotModuleReplacementPlugin(),
    new HtmlWebpackPlugin({template: './src/index.html'})
]

在面代码中webpack.optimize.UglifyJsPlugin是用来js代码压缩的,HtmlWebpackPlugin是用来指定模板的,HotModuleReplacementPlugin是用来模块热更新的(开发环境下使用),更多插件请查阅webpack官方提供的

6 输出

转换完成进行输出

output: {
  path: config.build.assetsRoot,
  filename: utils.assetsPath('js/[name].[chunkhash].js'),
  chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
}

output 位于对象最顶级键(key),包括了一组选项,指示 webpack 如何去输出、以及在哪里输出你的「bundle、asset 和其他你所打包或使用 webpack 载入的任何内容」。
上述代码,path 代表代码输出的目录对应一个绝对路径,filename代表定了每个输出 bundle(打包内容) 的名称,chunkFilename,决定了非入口(non-entry) chunk 文件的名称,更多配置看webpack output配置介绍

7 不完整配置介绍

   {
  mode: 'development', //知 webpack 使用相应模式的内置优化
  context: '', //用于从配置中解析入口起点(entry point)和 loader
  devtool: 'cheap-source-map',//此选项控制是否生成,以及如何生成 source map(用于调试定位等)
  node: { //这些选项可以配置是否 polyfill 或 mock 某些 Node.js 全局变量和模块
    setImmediate: false,
    process: 'mock',
    dgram: 'empty',
    fs: 'empty',
    net: 'empty',
    tls: 'empty',
    child_process: 'empty'
  },
  output: { //指示 webpack 如何去输出、以及在哪里输出
    path: '\\dist',
    filename: 'static/js/[name].js',
    publicPath: '',
    chunkFilename: 'static/js/[name].js'
  },
  resolve: { //配置模块如何解析
    alias: { //创建 import 或 require 的别名,来确保模块引入变得更简单
      '@': '\\src',
      vue$: 'vue/dist/vue.runtime.esm.js',
      assets: '\\src\\assets',
      api: '\\src\\api',
      views: '\\src\\views',
      components: '\\src\\components'
    },
    extensions: [ //自动解析确定的扩展 代码中可以省略的后缀名
      '.mjs',
      '.js',
      '.jsx',
      '.vue',
      '.json',
      '.wasm'
    ],
    modules: [ //告诉 webpack 解析模块时应该搜索的目录
      'node_modules',
      '\\node_modules',
      '\\node_modules\\_@vue_cli-service@4.5.4@@vue\\cli-service\\node_modules'
    ],
    plugins: [ //应该使用的额外的解析插件列表
      /* config.resolve.plugin('pnp') */
      {}
    ]
  },
  module: { //这些选项决定了如何处理项目中的不同类型的模块
    noParse: /^(vue|vue-router|vuex|vuex-router-sync)$/, //防止 webpack 解析那些任何与给定正则表达式相匹配的文件。忽略的文件
    rules: [ //创建模块时,匹配请求的规则数组
      {
        test: /\.vue$/,
        use: [
          /* config.module.rule('vue').use('cache-loader') */
          {
            loader: '\\node_modules\\_cache-loader@4.1.0@cache-loader\\dist\\cjs.js',
            options: {
              cacheDirectory: '\\node_modules\\.cache\\vue-loader',
              cacheIdentifier: '4e8d3dea'
            }
          },
          /* config.module.rule('vue').use('vue-loader') */
          {
            loader: 'vue-loader',
            options: {
              compilerOptions: {
                whitespace: 'condense',
                preserveWhitespace: true
             },
            cacheDirectory: '\\node_modules\\.cache\\vue-loader',
            cacheIdentifier: '4e8d3dea'
          }
        }
      ]
    },
  ]
  },
  optimization: { //优化打包
    splitChunks: { //对于动态导入模块,默认使用 webpack v4+ 提供的全新的通用分块策略(common chunk strategy)。
      cacheGroups: {
        vendors: {
          name: 'chunk-vendors',
          test: /[\\\/]node_modules[\\\/]/,
          priority: -10,
          chunks: 'initial'
        },
        common: {
          name: 'chunk-common',
          minChunks: 2,
          priority: -20,
          chunks: 'initial',
          reuseExistingChunk: true
        }
      }
    },
minimizer: [ //允许你通过提供一个或多个定制过的 TerserPlugin 实例, 覆盖默认压缩工具(minimizer)。
  /* config.optimization.minimizer('terser') */
  new TerserPlugin(
    {
      terserOptions: {
        compress: {
          arrows: false,
          collapse_vars: false,
          comparisons: false,
          computed_props: false,
          hoist_funs: false,
          hoist_props: false,
          hoist_vars: false,
          inline: false,
          loops: false,
          negate_iife: false,
          properties: false,
          reduce_funcs: false,
          reduce_vars: false,
          switches: false,
          toplevel: false,
          typeofs: false,
          booleans: true,
          if_return: true,
          sequences: true,
          unused: true,
          conditionals: true,
          dead_code: true,
          evaluate: true
        },
        mangle: {
          safari10: true
        }
      },
      sourceMap: false,
      cache: true,
      parallel: true,
      extractComments: false
    }
  )
]
  },
  plugins: [ //plugins 选项用于以各种方式自定义 webpack 构建过程
    /* config.plugin('vue-loader') */
    new VueLoaderPlugin(),
    /* config.plugin('define') */
    new DefinePlugin(
      {
        'process.env': {
          NODE_ENV: '"development"',
          VUE_APP_ENV: '"development"',
          BASE_URL: '""'
        }
      }
    ),
    /* config.plugin('case-sensitive-paths') */
    new CaseSensitivePathsPlugin(),
    /* config.plugin('friendly-errors') */
    new FriendlyErrorsWebpackPlugin(
      {
        additionalTransformers: [
          function () { /* omitted long function */ }
        ],
        additionalFormatters: [
          function () { /* omitted long function */ }
        ]
      }
    ),
    /* config.plugin('html') */
    new HtmlWebpackPlugin(
      {
        title: 'Alex、Hu',
        templateParameters: function () { /* omitted long function */ },
        template: '\\public\\index.html'
      }
    ),
    /* config.plugin('copy') */
    new CopyPlugin(
      [
        {
          from: '\\public',
          to: '\\dist',
          toType: 'dir',
          ignore: [
            '.DS_Store',
            {
              glob: 'index.html',
              matchBase: false
            }
          ]
        }
      ]
    )
  ],
  entry: { //开始应用程序打包过程的一个或多个起点。如果传入数组,则会处理所有条目
    app: [
      './src/main.js'
    ]
  }
}
文章目录