详细了解 Webpack(基础篇)

408 阅读5分钟

这是我参与「掘金日新计划 · 4 月更文挑战」的第5天,点击查看活动详情

大家好,我叫小杜杜,相信大家都知到我们代码有开发模式和打包模式,作为一个React前端,我们一帮用jsxtsx来书写代码,在样式方面可能会用到lesssass 等,而实际上浏览器识别不了这些文件,那如何让浏览器识别这些文件呢?实际上是依赖于babel,同时这种工具是 webpack

在之前讲过一篇有关 package.json的文章,有兴趣的小伙可以看看:通过开水果店,帮你全面了解package.json文件的作用

关于webpack,对于新手而言可以说是非常的遥远,为什么这么说呢?因为很多人都没有机会参与到项目中的Webpack配置,更不要说怎么去做优化了(我就是其中一员😅)

作为一个小菜鸟,要想向前一步,webpack可以说是极其重要的一环,所以今天主要说下有关webpack的知识,一起努力吧~

webpack 是什么?

webpack:是一种前端资源构建工具,一个静态模块打包器(module bundler)。

  • 前端资源构建工具:在文章的一开始说过,浏览器是不认识像jsxtsxlesssass的,甚至js的高级语法都不认识,而这些资源要像在浏览器中能正常运行,就需要对这些文件进行编译,而编译的工具就是前端资源构建工具(webpack便是其中之一)

  • 静态模块打包器:在webpack看来,前端的所有资源文件(js、css、json...),都会作为一个模块处理,构建一个依赖关系图,然后通过这个关系图将所有的静态资源打包成一个或多个bundle输出

为什么需要webpack

从前面的介绍来看,webpack最主要的作用就是将各种资源文件进过统一编辑,使浏览器去认识,然而实际上 webpack的作用远不止于此,举个简单的例子:

css 的单位中有 pxrem1rem = 16px,在移动端中,我们不能使用px的单位,需要使用rem,而我们的设计稿肯定是px的,难道我们要手动计算兑换为rem后的数值吗?就算你说用到的px数值比较少,我可以自己换算,好没问题,哪之后UI再次调动样式,或者之后再次修改,你每次都需要换算吗?那么其代价是否太大了?

那有没有一个比较好的方案,我在开发的时候就用px并且在开发环境下,看到的也是px,只有在打包下才看到rem呢? 当然有,这时便需要通过配置webpack来实现这个效果。

诸如此类的问题,我们都可以通过webpack去解决,做个形象的比喻,webpack就相当于交通工具,没有他只能徒步,有了它,将会给我们提供大大的便利

这里顺便说下,之前用 React 搭建一个了移动端框架,感觉还是不错的,有兴趣的小伙伴可以看看:打造开箱即用的 react 移动端框架

webpack的五大核心概念

  • Entry: 入口,指示 webpack 以哪个文件为入口起点开始打包,分析构建内部依赖图。
  • Output: 输出,指示 webpack 打包后的资源 bundles 输出到哪里去,以及如何命名。
  • Loader: 处理模块, 让 webpack 能够去处理那些非 JS 的文件,比如样式文件、图片文件(webpack 自身只理解JS)
  • Plugins: 插件,可以用于执行范围更广的任务。插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量等。
  • Mode: 模式,指示 webpack 使用相应模式的配置。

其中 Mode 分为developmentproduction

  • development:会将 DefinePlugin 中 process.env.NODE_ENV 的值设置为 development。对应的是本地环境。

  • production:会将 DefinePlugin 中 process.env.NODE_ENV 的值设置为 production。对应的是线上环境。

webpack 详细配置

entry

entry: 入口位置,共有三种模式, string | array | object

  • string: 单入口,打包形成一个 chunk, 输出一个 bundle 文件,并且 chunk 的名称默认是 main
  • array: 多入口,但所有入口文件只会形成一个 chunk, 输出一个 bundle 文件
  • object: 多入口,有几个入口文件就会形成几个 chunk,输出几个 bundle 文件,并且 chunk 的名称是对象的key
    // string
    entry: "./src/index.js"
    
    // array
    entry:['./src/index.js', './src/test.js']

    // object
    entry: {
       index: './src/index.js',
       test: './src/test.js
    }

output

output: 输出文件,模式为:object

  • path: 输出文件目录的位置(将来所有资源输出的公共目录)
  • filename:文件名称 (指定名称 + 目录)
  • publicPath:公共资源引入的前缀(如 ‘/’ => /文件名)
  • chunkFilename: 指定非入口chunk的名称
  • clean:打包前清空
  • library:打包整个库后向外暴露的变量名
  • libraryTarget:库暴露的方式(有 'window', 'global', 'commonjs')

其中 library 可以为对象,包含 libraryTarget, 分别为对象

    output: {
        path: resolve(__dirname, 'build'),
        filename: 'js/[name].js',
        publicPath: '/',
        chunkFilename: 'js/[name]_chunk.js',
        clean: true,
        
        //分开写
        library: '[name]',
        libraryTarget: 'window',
        
        //合并
        library:{
            name: '[name]',
            type: 'window'
        }
    }

mode

mode:模块:通过 rules 来配置 loader

loader: webpack 只能理解 JavaScriptjson 文件, loader 的作用就是让 webpack 理解识别其他文件

loader的参数:

  • test:单个loader
  • use:多个loader
  • exclude: 排除的文件
  • include:检查的文件
  • enforce:执行顺序,分 pre(优先执行) 和 post(延后执行)
  • loader:单个用loader
  • oneOf: 只会生效一个配置
  • option: 指定配置选项
   module: {
       rules: [
           {
               test: /\.css$/,
               use: ['style-loader', 'css-loader'], // 多个
               exclude: /node_modules/,
               include: resolve(__dirname, 'src'),
               enforce: 'pre',
               loader: 'eslint-loader',
               oneOf: [],
               options: {}
           }
       ]
   }

plugins

plugins: 插件,用于配置些功能。

const HtmlWebpackPlugin = require("html-webpack-plugin"); //创建html文件,并自动引入打包输出的bundles文件。支持html压缩。

plugins: [ 
    new HtmlWebpackPlugin({
        template:"index.html"
    })
]

resolve

resolve:解析模块的规则

  • alias配置别名,引入时,可用别名代替,如 @ 代表根目录
  • extensions:在引入时省略文件路径的后缀名, 如 import xxx from '@/index' => import xxx from '@/index.js'
  • modules: 告诉 webpack 解析模块应该去找哪个目录
 resolve: {
     alias: {
         [@]: path.resolve(__dirname, "src")
     },
     extensions: ['.js', '.json', '.jsx', '.css'],
     modules: [resolve(__dirname, '../../node_modules'), 'node_modules'] 
 }

devServer

devServer: 开发环境下的配置

  • contentBase: 运行代码所在的目录
  • watchContentBase:监视contentBase目录下的所有文件,一旦文件变化就会reload
  • watchOptions下的ignored:配置忽略文件
  • compress:是否gzip压缩
  • port:端口号
  • host:域名
  • open:是否自动打开浏览器
  • hot:是否开启HMR功能
  • clientLogLevel: 是否显示启动服务器日志信息
  • quiet:除了一些基本信息外,其他内容是否要显示
  • overlay:如果出错了,是否全屏提示
  • proxy:代理
    devServer: {
        contentBase: resolve(__dirname, 'build'),
        watchContentBase: true,
        watchOptions: {
            ignored: /node_modules/
        },
        compress: true,
        port: 5000,
        host: 'localhost',
        open: true,
        hot: true,
        clientLogLevel: 'none',
        quiet: true,
        overlay: false,
        proxy: {
            '/api': {
                  target: 'http://localhost:3000',
                  pathRewrite: {
                        '^/api': ''
                  }
            }
        }
    }

proxy以上述代码为例:

  • target:目标地址,当服务器接收到/api/xxx的请求,就会把请求转发到另外一个服务器3000
  • pathRewrite:简写,意思是由 /api/xxx可以写为/xxx

optimization

optimization:生产环境下的配置

  • splitChunks:官方下的配置,自动生效
  • runtimeChunk:把runtime部分的代码抽离出来单独打包
  • minimizer: 一些额外的配置
    optimization: {
        splitChunks: { //默认的
            chunks: 'async',
            minSize: 30000,
            minChunks: 1,
            maxAsyncRequests: 5,
            maxInitialRequests: 3,
            automaticNameDelimiter: '~',
            name: true,
            cacheGroups: {
                vendors: {
                  test: /[\/]node_modules[\/]/,
                  priority: -10
                },
                default: {
                  minChunks: 2,
                  priority: -20,
                  reuseExistingChunk: true
                }
            }
         },
        runtimeChunk: {
            name: entrypoint => `runtime-${entrypoint.name}`
        },
        minimizer: {
            // 配置生产环境的压缩方案:js/css
            new TerserWebpackPlugin({
              cache: true, // 开启缓存
              parallel: true, // 开启多进程打包
              sourceMap: true // 启用sourceMap(否则会被压缩掉)
            })
          ]
        }
    }

End

致此, 有关Webpack的基础就讲完了,相信看完的小伙伴,应该可以看懂自己项目的配置了,这样就能更好的理解项目了,喜欢的点个赞👍🏻支持下吧(● ̄(エ) ̄●)