webpack 知识梳理

webpack 是什么?咱们为何要用它?

首先贴出官方解释:javascript

本质上,webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序须要的每一个模块,而后将全部这些模块打包成一个或多个 bundle。css

通俗的讲,webpack 是一个代码加工机器,咱们把本身的代码丢给他,它通过加工以后再还给咱们。
以上所说的加工,包括但不限于如下几点:html

  • 代码打包压缩
  • 代码编译(ES6转ES五、ts 转 js 、less\sass 转 css 等)
  • 代码优化,提取公共模块

webpack 还能够为咱们提供前端静态服务,极大地方便了咱们平常的开发调试。前端

综上,咱们之因此使用 webpack ,主要是为了提升开发效率,优化自身代码。java

webpack 核心概念

当咱们把自身代码交给 webpack 加工时,必然会须要一个入口,而加工事后的代码须要还给咱们,确定也会须要出口,而在加工的过程当中 webpack 须要进行一系列的工序。如同面粉变成面包,不仅是烘烤这么简单。
由上,咱们来理解 webpack 核心的四个概念:node

  • entry : 文件入口
  • output : 文件出口
  • module : 工序一 (经过配置,对不一样的文件类型作处理,完成模块代码的转换)
  • plugins : 工序二 (经过插件,来获取更增强大的加工处理能力,完成更复杂的构建任务)

到这里,咱们即可以写出一份简单的配置文件骨架:react

module.exports = {
  entry: {
    ··· 文件入口
  },
  output: {
    ··· 文件出口
  },
  module: {
    rules: [
        ··· 一些列的 loader 
    ],
  },
  plugins: [
    ··· 一系列的插件
  ]
}
复制代码

webpack 的基本配置使用

在 webpack 4.0以上版本中为咱们提供了 --mode这个命令配置项,mode 分为 development 和 production 两个选项,默认为production。它为咱们提供了一些简单的基本配置,对于简单的项目,咱们再也不须要配置文件,mode 默认入口文件为 src 文件夹 下的 index.js,出口为 dist 文件夹,打包时,咱们只须要选择 mode 的参数便可。webpack

webpack --mode development/production
复制代码

在 mode 的默认配置中,已经为咱们处理了一些常见的用法。可是,这些默认配置,没法处理非 js 文件内容。web

下面这段话来自知乎:正则表达式

development默认值会给你最好的开发体验,它注重:  
浏览器调试工具
快速开发周期中的快速增量编译
在运行过程当中提供有效的错误信息


而production默认值会给你提供一系列有效的默认值以便部署你的应用,它注重:
小的输出体积
运行快速的代码
忽略仅在开发时须要的代码
不暴露源码和文件路径
易于使用的输出产物
复制代码

实际上,就目前看来,在项目开发中,咱们仍需配置本身的配置文件。
在平常的开发中,咱们通常须要 webpack 为咱们解决下面这些问题:

  • 构建咱们发布须要的 HTML、CSS、JS 文件
  • 使用 CSS 预处理器来编写样式
  • 处理和压缩图片
  • 使用 Babel 来支持 ES 新特性
  • 本地提供静态服务以方便开发调试

根据以上需求,咱们能够这样配置本身 webpack.config.js :

const HtmlWebpackPlugin = require('html-webpack-plugin'); 
const CleanWebpackPlugin = require('clean-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin'); // 注意版本号 webpack 4 以上版本请下载 @next 版本
const path = require('path');

module.exports = {
  entry: {
    index: './src/index.js',
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].min.js'
  },
  module:{
    rules: [
      {
        test: /\.jsx?$/,// 匹配文件路径的正则表达式,一般咱们都是匹配文件类型后缀
        exclude: /(node_modules|bower_components)/, // 过滤掉不须要处理的文件
        use: { loader: 'babel-loader' } // 指定使用的 loader
      },
      {
        test: /\.less/,
        use: ExtractTextPlugin.extract({ // 使用插件抽离 css ,生成单独的 css 文件
          fallback: 'style-loader',
          use: ['css-loader', 'less-loader']
        })
      },
      {
        test: /\.html$/,
        use: [ 
          {
            loader: 'html-loader',
            options: { // 压缩 html
              minimize: true
            }
          }
        ]
      },
      {
        test: /\.(png|jpg|gif)$/,
        use: [
          {
            loader: 'file-loader',
            options: {
              // 自定义配置,图片压缩、处理等
            } 
          }
        ]
      }
    ]  
  },

  // 提供静态服务
  devServer:{ 
    port: 9999,
    headers: { // 添加头部信息
      "X-Custom-Foo": "bar"
    },
    proxy: { // 请求代理
      "/api": {
        target: "http://localhost:3000"
      }
    }
  },
  plugins: [
    // 每次打包前清除 dist 下的文件
    new CleanWebpackPlugin('dist'),

    // 提取样式,生成单独文件
    new ExtractTextPlugin("styles.css"),
    
    // 生成新的 html 文件
    new HtmlWebpackPlugin({ 
      filename: 'index.html', // 若是文件名不是 index , 开发时要在 url 处添加文件名
      template: path.resolve(__dirname + '/src/index.html'), // 注意路径,
    }) 
  ]
}
复制代码

package.json (请注意版本号,由于版本更新会形成不可预期的错误)

{
  "name": "webpack-share",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "dev": "webpack-dev-server --open --inline --mode development --progress",
    "build": "webpack --mode production --progress"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "babel": "^6.23.0",
    "babel-core": "^6.26.0",
    "babel-loader": "^7.1.4",
    "babel-preset-env": "^1.6.1",
    "clean-webpack-plugin": "^0.1.19",
    "css-loader": "^0.28.11",
    "extract-text-webpack-plugin": "^3.0.2",
    "file-loader": "^1.1.11",
    "html-loader": "^0.5.5",
    "html-webpack-plugin": "^3.2.0",
    "less": "^3.0.1",
    "less-loader": "^4.1.0",
    "path": "^0.12.7",
    "style-loader": "^0.20.3",
    "webpack": "^4.5.0",
    "webpack-cli": "^2.0.14",
    "webpack-dev-server": "^3.1.2"
  }
}

复制代码

这样,一个简单的前端开发环境就已经配置好了,这份配置文件解决了以前咱们所提出的问题:

一、构建咱们发布须要的 HTML、CSS、JS 文件

  • 使用 html-loader 、 html-webpack-plugin 处理 html 文件,引入处理后的 css , js 文件,使用 extract-text-webpack-plugin 抽离单独的 css 文件(注意版本号,在 webpack 4 中须要下载 @next 版本)

二、使用 CSS 预处理器来编写样式

  • 使用 less-loader、 css-loader 、style-loader 处理样式文件

三、处理和压缩图片

  • 使用 file-loader 处理图片资源

四、使用 Babel 来支持 ES 新特性

  • 使用 babel-loader 对 js 编译

五、本地提供静态服务以方便开发调试

  • 使用 devServer 提供静态服务,配置请求代理,避免产生跨域安全问题

若是在项目中引入 react ,咱们要作一下简单修改:

{
        test: /\.jsx?$/,// 匹配文件路径的正则表达式,一般咱们都是匹配文件类型后缀
        exclude: /(node_modules|bower_components)/, // 过滤掉不须要处理的文件
        use: { // 指定使用的 loader
          loader: 'babel-loader',
          options: {
            // presets: ['es2015','react'] webpack 3
            presets: ['@babel/preset-react','@babel/preset-es2015'] // webpack 4
          }
        } 
      },
复制代码

package.json :

"@babel/preset-es2015": "^7.0.0-beta.44",
"@babel/preset-react": "^7.0.0-beta.44",
复制代码

若是要引入 antd , 在 less-loader 中还须要作一下修改:

{
    test: /\.less/,
    use: ExtractTextPlugin.extract({ // 使用插件抽离 css ,生成单独的 css 文件
      fallback: 'style-loader',
      use: [
        {
          loader: 'css-loader'
        },
        {
          loader:  'less-loader',
          options: { // 引入 js
            javascriptEnabled: true 
          } 
        }
      ]
    })
  }
复制代码

一些经常使用的其余配置

resolve

这里只作简单介绍,更多详情,请看官网
在 webpack 中,和模块路径解析相关的配置都在 resolve 字段下,咱们能够经过配置 resolve 来提升自身开发的体验,例如经常使用的 alias 别名设置,简化了咱们引入文件的路径。 首先咱们来了解一下在 webpack 中的解析规则:

  • 解析相对路径

一、若是是文件,直接加载
二、若是是文件夹,则查找文件夹下 package.json 文件

  • 找到 package.json: 则通常状况会对照 main 属性,获取文件路径来解析,若是找不到 main 字段,通常状况话会寻找文件夹下的 index.js
  • 找不到 package.json : 会寻找 index.js
  • 解析绝对路径

直接查找文件

  • 解析模块

由下自上查找 node_modules 中的模块

alias 别名

alias 能够为一段路径建立一个别名,使一些较为经常使用的冗长路径获得简化:
题外话:

path.join([path1][, path2][, ...])用于链接路径。该方法的主要用途在于,会正确使用当前系统的路径分隔符,Unix系统是/,Windows系统是\。

path.resolve([from ...], to) 将 to 参数解析为绝对路径。

resolve: {
  alias: {
    Js: path.resolve(__dirname + '/src/js'),
    Less: path.resolve(__dirname, './src/css'), // 模糊匹配: 引用时,只要匹配到 Less 都会被替换
    Css$: path.resolve(__dirname, './src/css/index.css') // 精确匹配:引用时,只能 improt 'Css'
  }
},
复制代码

这样,咱们在引入 css 和 js 时,只须要在别名下查找相应文件就好:

// 设置别名前
import '../css/a.less';

// 设置别名后
import 'Less/a.less';
improt 'Css'
复制代码

extensions 自动解析肯定的扩展

resolve: {
    // 默认配置
    // extensions: [".js", ".json"]
    extensions: ['.js','.jsx','.less','.css']
},

复制代码

设置补全扩展名数组,引用文件时,能够不加后缀,wepack 会从数组中自动补全。

resolve 其余配置
resolve: {
    // 当目录下有 package.json 文件时,默认查找字段
    // 配置 target === "web" 或者 target === "webworker" 时 mainFields 默认值是:
    mainFields: ['browser', 'module', 'main'],
    // target 的值为其余时,mainFields 默认值为:
    mainFields: ["module", "main"],
    
    // 当目录下没有 package.json 文件时,默认查找文件
    mainFiles: ["index"],
    
    modules: [
      path.resolve(__dirname, 'my_modules'), //告诉 webpack 解析模块时应该搜索的目录,能够自定义一些本身的模块路径。
      'node_modules',
    ]
}
复制代码

devtool

devtool: 'source-map',
复制代码

此选项控制是否生成,以及如何生成 source map。在开发环境时使用,便于准肯定位代码位置。

webpack-dev-serve

devServer:{ 
  contentBase: path.resolve(__dirname, "dist"), // 未经 webpack 处理的静态文件访问路径
  port: 9999,
  publicPath: '/', // 肯定应该从哪里提供 bundle,而且此选项优先。建议将 devServer.publicPath 和 output.publicPath 的值保持一致。
  overlay:{ //当有编译错误或者警告的时候显示一个全屏 overlay
    errors:true,
    warnings:true,
  },
  headers: { // 添加头部信息
    "X-Custom-Foo": "bar"
  },
  proxy: { // 请求代理
    "/api": {
      target: "http://localhost:3000",
    }
  },
},
复制代码

开发环境和生产环境

基于开发环境和生产环境的需求差别,咱们通常须要使用两套不一样的配置文件。
首先,咱们来看一下开发环境和生产环境环境需求点的异同:

相同点

一、共同的入口
二、共同的代码处理
三、一样的解析配置
三、共同的出口
复制代码

不一样点

开发环境

一、模块热更新 // 提升开发效率
二、接口代理  // 方便接口调用
三、devtool  // 准肯定位代码位置


生产环境

一、提取公共代码    
二、压缩混淆   
三、去除无用代码  
四、文件压缩  // 减少代码体积
复制代码

咱们发现,在 wenpack 4 中,mode 的两个参数已经能很好地解决咱们大部分的需求,可是仍有些个性配置须要咱们手动建立。咱们须要根据不一样的业务需求对配置文件进行拆分,而后利用 webpack-merge 对不一样的配置文件进行合并。
一般状况下,咱们会拆分出三个配置文件:

webpack.base.conf.js    // 公共配置
webpack.prod.conf.js    // 生产环境配置
webpack.dev.conf.js    // 开发环境配置
复制代码

你能够把两份差别配置引入公共配置,而后判断传入参数来决定使用那份配置:

webpack-dev-server --config ./webpack.base.conf.js --mode development/production 或 --env development/production

const HtmlWebpackPlugin = require('html-webpack-plugin'); 
const CleanWebpackPlugin = require('clean-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin'); // 注意版本号 webpack 4 以上版本请下载 @next 版本
const merge = require('webpack-merge')
const webpack = require('webpack');
const path = require('path');

const prodConf = require('./webpack.prod.conf');
const devConf = require('./webpack.dev.conf');

module.exports = (env, argv) => {
  console.log(env,'==================', argv.mode)

  const baseConf = {
    entry: {
      index:  path.resolve(__dirname, '../src/index.js'),
    },
    output: {
      path: path.resolve(__dirname, '../dist'),
      filename: 'js/[name].min.js'
    },
    ····
  }
  const config = env === 'dev' ? devConf : prodConf;
  return merge(baseConf,devConf)
}

复制代码

也能够将共同配置分别引入差别配置中,在启动命令上指定所使用的配置文件:

webpack-dev-server --config 配置文件
复制代码

优化

相关文章
相关标签/搜索