likes
comments
collection
share

【温故知新】从常用配置项深入浅出 webpack

作者站长头像
站长
· 阅读数 15

我正在参加「掘金·启航计划」

  • 从最基础配置开始, 逐步理解 webpack 是如何工作的
  • 可以直接用作 webpack 配置项的参考,按需在项目中添加
  • 加深前端开发对工程化项目的认知

基础配置

要安装最新版本或特定版本,请运行以下命令之一:

npm install --save-dev webpack
npm install --save-dev webpack@<version>

如果你使用 webpack 4+ 版本,你还需要安装 CLI。

npm install --save-dev webpack-cli

默认的配置文件是当前目录下的webpack.config.js

const path = require('path')

module.exports = {
  // 选择打包模式 development 或者 production 后者会对代码进行压缩编译
  mode: 'development',  
    
  // 入口文件
  entry: './src/main.js',
  // 出口文件
  output: {
    filename: 'bundle.js', // 打包后的文件名字
    path: path.resolve(__dirname, 'dist') // 储存路径 用nodejs path模块 转换成绝对路径传入
  }
}
  • 我们可以为 出口的js 设置hash 来防止总是覆盖 旧版js

    module.exports = {
      ...
      output: {
        filename: 'bundle.[hash:8].js' // 打包后的文件名字
        path: path.resolve(__dirname, 'dist') 
      }
    }
    
  • package.json 中 可以简化 我们在终端输入的命令 并且 为这条命令绑定 一些参数 (--config 可以手动指定webpack的配置文件)

      "scripts": {
        "build": "webpack --config webpack.config.js"
      },
    

    终端运行如下代码 等同于运行了 webpack --config webpack.config.js

    npm run build
    

最初始的webpack只能帮助我们打包编译 js文件 我们可以为它配置更多的东西 让它变得很酷o(∩_∩)o

html 插件

webpack-dev-server

我们把 打包好的 js 放在 dist中 创建一个html 导入它 点击这个html在浏览器进行试调。然而我们更希望,把dist部署在一个 开发服务端上 让我们去请求 。

webpack内置了一个这样的开发服务 webpack-dev-server ,它的内部是通过express实现的,它不会真实的打包文件,而是在内存中进行编译。

通过npm安装

npm i webpack-dev-server -D

package.json 中配置

  "scripts": {
    "dev": "webpack-dev-server"
  }

在终端运行

npm run dev

webpack-dev-server 默认将当前目录映射到了本机的8080服务器中,当然我们可以在webpack中配置它

module.exports = {
  devServer: {
    port: 3001, // 设置端口号
    contentBase: path.join(__dirname, "dist"), // 指向的文件目录
    progress: true, // 显示启动进度
    open: false // 直接打开浏览器
  },
  ...
}

html-webpack-plugin

webpack-dev-server 帮我们实现了把dist 映射到了开发服务器,但我们不希望自己手动去创建 dist、html ,手动去引入bundle.js,我们需要一个插件 html-webpack-plugin 来帮助我们

npm i html-webpack-plugin -D

在webpack中引入并配置它

const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
    ...
    plugins: [  // 这里面存放了所有的webpack的插件
        
    // 借助 html-webpack-plugin 自动在dist下 创建 html 并引入 bundle.js
    new HtmlWebpackPlugin({ 
      template: './src/index.html',  // 需要的模版html
      filename: 'index.html', // 编译之后文件名
      minify:{ // 压缩 html
        removeAttributeQuotes: true, // 删除 属性双引号
        collapseWhitespace: true // 折叠 空行
      }, 
      hash: true // 在引用bundle.js时 添加 hash (解决缓存问题)
    })
        
  ]
}

加载样式

在模版html中用link引入css,但是在打包到dist中的时候,不会动态的获取css的路径。但是我们可以在index.js(入口js)中,把css当做模块引入 require('css文件路径') ,此时我们需要一个合适的 loader 去解析这个文件。

module.exports = {
  ...
  module: { // 模块
    rules: [ // 规则
      { test: /正则匹配/, use: 'loader' }
    ]
  }
}
  • 关于 loader

    每个loader的功能单一

    在use中传递一个数组,可使用多个loader

    loader 默认的执行顺序是 从右到左,从下到上

    如果需要为某个loader传入参数,写成对象形式

css

npm i style-loader css-loader -D

css-loader 负责解析 @ import 这种语法 和 路径

style-loader 把css插入到head标签中

rules: [ 
    { test: /\.css$/, use: ['style-loader','css-loader'] }
]
  • 默认style-loader 把css插入到了head标签的底部

    如果我们想要html中 书写的style标签不被覆盖 可以对 style-loader 设置参数,将css插入到顶部

    {
      test: /\.css$/, 
      use: [ 
        { 
          loader:'style-loader',
          options:{
            insertAt: 'top'
          }
        },
        'css-loader'
      ]
    }
    

scss

npm i node-sass sass-loader -D

sass-loader 会调用 node-sass 编译scss文件

{
  test: /\.scss$/, 
  use: [ 
    { 
      loader:'style-loader',
      options:{
        insertAt: 'top'
      }
    },
   'css-loader',
   'sass-loader'
  ] 
}

全局引入 scss 变量

借助sass-resources-loader插件

npm install sass-resources-loader -D

webpack.config.js

{
  test: /\.scss$/, 
  use: [ 
   'style-loader',
   'css-loader'
   'postcss-loader',
   'sass-loader',
   {
    loader: 'sass-resources-loader',
    options: {
      resources: './src/style/common.scss'  // 全局变量资源的路径
    }
   }
  ]
}

在vue-cli3.0中引入全局

vue.config.js

module.exports = {
  ...

  css: {
    loaderOptions: {
      sass: {
        data: `
          @import "@/assets/css/common.scss";
        `
      }
    }
  }
}

less

npm i less less-loader -D

stylus

npm i stylus stylus-loader -D

图片处理

引入图片

引入图片的几种方式

  • js创建图片
  • css引入
  • html img标签

js中引入

let image =  new Image()
image.src = './images/test.jpg'
document.body.appendChild(image)

'./images/test.jpg' 引入,webpack在打包的过程中会认为它只是一个普通的字符串。

需要建立路径与资源的关系,使用 import 或 require 引入

import test from './images/test.jpg'
let image =  new Image()
image.src = test

document.body.appendChild(image)

css中引入

在css中同样需要建立路径与资源的关系,但 css-loader 默认帮我们做了处理。

所以只需要用原来的方式书写css即可

background: url('../images/test.jpg');  

html中引入

在html中使用相对路径引入图片,在打包到dist文件夹后,路径就不有效了。

使用 html-withimg-loader 解析html,帮助编译图片

npm i html-withimg-loader -D

webpack.config.js

{
 test: /\.html$/,
 use: 'html-withimg-loader'
},

file-loader

需要 file-loader 帮助我们处理 将要用到的资源文件

npm i file-loader -D

webpack.config.js

{
  test: /\.(png|jpg|gif)$/,
  use: 'file-loader'
},

url-loader

url-loader可以将图片转为base64字符串,能更快的加载图片,一旦图片过大, 就需要使用file-loader的加载本地图片,故url-loader可以设置图片超过多少字节时,使用file-loader加载图片。

npm i url-loader -D

webpack.config.js

{
  test: /\.(png|svg|jpg|jpeg|gif)$/,
  loader: 'url-loader',
  options: {
    limit: 200*1024,
    outputPath: 'img/' // 输出目录
  }
},
  • url-loader依赖file-loader (两个都安装)
  • 当使用url-loader加载图片,图片大小小于上限值,则将图片转base64字符串;否则使用file-loader加载图片,都是为了提高浏览器加载图片速度。
  • 使用url-loader加载图片比file-loader更优秀,所以我们通常用 url-loader 的配置替换 file-loader的配置 使用(两者取一)
  • 可以为图片资源设置输出目录

公共路径

webpack.config.js

在出口配置中添加publicPath , 可以为所有的url添加上这个公共路径

  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist'),
    publicPath: 'http://www.test.cn/'
  },

如果我们只需要在部分url上添加,则可以单独为相应loader添加配置

{
  test: /\.(png|svg|jpg|jpeg|gif)$/,
  loader: 'url-loader',
  options: {
    limit: 200*1024,
    outputPath: 'img/', // 输出目录
    publicPath: 'http://www.test.cn/' // 单独为图片资源添加公共路径
  }
},

抽离样式

通过模块化的方式加载样式,在loader的处理下,最终会以style标签的形式写入到html的head中。随着样式增多,可能会导致堵塞,因此我们更希望用 link 的方式引入它们。

mini-css-extract-plugin

npm i mini-css-extract-plugin -D

mini-css-extract-plugin 可以将编译好的css,抽离到一个指定的文件。

webpack中的配置

const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = {
  plugins: [  // 这里面存放了所有的webpack的插件 
        
    new MiniCssExtractPlugin({ // 配置抽离css插件的相关属性
      filename: 'style/main.css' // 生成的文件路径和文件名
    })
        
  ],
  module: { // 模块
    rules: [ // 规则
  
      { 
        test: /\.css$/, 
        use: [ 
          MiniCssExtractPlugin.loader,// 替换 style-loader 
          'css-loader'
        ] 
      }
      
    ]
  }
}
  • 如果我们想要生成多个css文件,不妨引入 多个 mini-css-extract-plugin 分别进行配置

    const MiniCssExtractPlugin1 = require('mini-css-extract-plugin')
    const MiniCssExtractPlugin2 = require('mini-css-extract-plugin')
    

autoprefixer

什么是浏览器前缀(www.fly63.com/article/det…

为了兼容低版本浏览器,我们要为css3的属性添加不同浏览器的前缀。但是,我们不希望自己手动的去添加,可以使用autoprefixer 插件自动添加,让我们在编程的过程中忘记浏览器前缀这回事。在webpack中,如果你想使用它还需要借助相应的loader postcss-loader 将它调用。

npm i postcss-loader autoprefixer -D

postcss-loader 需要一个postcss.config.js 的配置文件,可以将我们想要使用的 autoprefixer 插件 声明在这个配置里,以便loader去调用。

postcss.config.js

module.exports = {
  plugins: [ 
      require('autoprefixer')
  ]
}

webpack.config.js

{
  test: /\.scss$/, 
  use: [ 
    MiniCssExtractPlugin.loader,
   'css-loader',
   'postcss-loader', // 在css-loader之前处理 postcss-loader
   'sass-loader'
  ] 
}

配置目标浏览器 browserslist

压缩样式

webpack 可以在production 模式下,默认启动js的压缩,但对css文件无能为力。如果我们需要将css压缩。可以为webpack设置优化项。

npm i optimize-css-assets-webpack-plugin terser-webpack-plugin -D

webpack.config.js

const TerserJSPlugin = require('terser-webpack-plugin')
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin')
module.exports = {
  optimization: {
    minimizer: [ new TerserJSPlugin({}), new OptimizeCSSAssetsPlugin({}) ],
  },
  ...
}
  • 设置optimization 时,webpack默认启动的js压缩会被取消,需要手动的重新配置terser-webpack-plugin js的压缩插件

抽离后图片路径问题

在引入MiniCssExtractPlugin.loader时使用对象方式,并在options目录下添加 publicPath: '../' 配置,这样就只会在css文件中引入的资源中添加 “../”,就不会影响其他文件中的路径

      {
        test: /\.scss$/, 
        use: [ 
          {
            loader:MiniCssExtractPlugin.loader,
            options: {
                publicPath: '../'
            }
          },
         'css-loader',
         'postcss-loader',
         'sass-loader'
        ] 
      }

babel7

如果想要对js进行处理,将 ECMAScript 2015+ 版本的代码向后兼容,以便能够运行在当前和旧版本的浏览器或其他环境中。可以使用 babel ,同样我们需要相应loader去加载它。

基础配置

npm i babel-loader @babel/core @babel/preset-env -D

babel-loader加载器

@babel/core babel 核心模块

@babel/preset-env babel 转化语法模块 es6 -> es5

webpack.config.js

{
  test: /\.js$/,
  exclude: /node_modules/,
  include: path.resolve(__dirname, './src'),
  use: [

    {
      loader: 'babel-loader',
      options: { 
        presets: [ // 预设
          '@babel/preset-env'
        ]
      }
    }
    
  ]
}

更高级的语法

想要兼容更高级的语法,需要额外的添加一些babel的插件,并配置

class

npm i @babel/plugin-proposal-class-properties -D

webpack.config.js

use: [

  {
    loader: 'babel-loader',
    options: { 
      presets: [ // 预设
        '@babel/preset-env'
      ],
      plugins: [ // 插件
        '@babel/plugin-proposal-class-properties'
      ]
    }
  }

]

装饰器(decorators)

介绍(zhuanlan.zhihu.com/p/20139834)

npm i @babel/plugin-proposal-decorators -D

webpack.config.js

options: { 
  ...
  plugins: [ // 插件
    ["@babel/plugin-proposal-decorators", { "legacy": true }],
    ["@babel/plugin-proposal-class-properties", { "loose" : true }]
  ]
}

@babel/plugin-transform-runtime

一个插件,可以重复使用Babel注入的帮助程序代码来节省代码。

它在运行时取决于我们部署的代码,所以还要为他安装生生产依赖 @babel/runtime .

npm i @babel/plugin-transform-runtime -D
npm i @babel/runtime -S

webpack.config.js

options: { 
  ...
  plugins: [ // 插件
    ...
    '@babel/plugin-transform-runtime'
  ]
}

@babel/polyfill

填充工具,模拟完整的ES2015 +环境。有了这个模块,可以使用新的内置函数(如Promise或WeakMap),静态方法(如Array.from或Object.assign),实例方法(如Array.prototype.includes)和生成器函数(假设使用的是再生器插件)

npm i @babel/polyfill -S

在需要使用的 js 文件中

import "@babel/polyfill"

.babelrc

也可以把 options 的配置 直接写在 .babelrc 文件中, babel-loader 会去这个文件中读取配置。

{ 
  "presets": [ // 预设
    "@babel/preset-env"
  ],
  "plugins": [ // 插件
    ["@babel/plugin-proposal-decorators", { "legacy": true }],
    ["@babel/plugin-proposal-class-properties", { "loose" : true }],
    "@babel/plugin-transform-runtime"
  ]
}

ts-loader

搭建 typescript 的运行环境

npm install ts-loader typescript --save-dev

webpack.config.js

module.exports = {
	...
  resolve: { // 添加解析 后缀
    extensions: [".ts", ".js"]
  },
  module: {
    rules: [ // 处理 ts 或 tsx 文件
      { test: /\.tsx?$/, loader: "ts-loader" }
    ]
  }
}

新建 tsconfig.json 可以为解析运行时添加相应配置

{
  "compilerOptions": {
    "sourceMap": true // 开启试调 映射
  },
  "include": [ // 解析目录
    "src/**/*"
  ],
  "exclude": [ // 排除目录 
      "node_modules",
      "**/*.spec.ts"
  ]
}

vue-loader

npm i  vue-loader vue-template-compiler -D

Vue Loader 的配置和其它的 loader 不太一样。除了通过一条规则将 vue-loader 应用到所有扩展名为 .vue 的文件上之外,要确保在 webpack 配置中添加 Vue Loader 的插件:

webpack.base.js

const VueLoaderPlugin = require('vue-loader/lib/plugin')

module.exports = {
  module: {
    rules: [
      // ... 其它规则
      {
        test: /\.vue$/,
        loader: 'vue-loader'
      }
    ]
  },
  plugins: [
    // 请确保引入这个插件!
    new VueLoaderPlugin()
  ]
}
  • VueLoaderPlugin 将你定义过的其它规则复制并应用到 .vue 文件里相应语言的块

代码校验(eslint)

如需在 webpack 中 使用eslint ,需要加载器eslint-loader

npm i eslint eslint-loader -D

eslint.cn/demo/ 选择相应规则,并下载json,将文件名改为 .eslintrc.json 放入开发根目录

webpack.config.js

{
  test: /\.js$/,
  exclude: /node_modules/,
  use: [
    {
      loader: 'eslint-loader',
      options: {
        enforce: 'pre' // 将 eslint-loader 提前执行
      }
    }
  ]
},

loader的类型

  • pre 提前执行的loader
  • post 后置的loader
  • normal 普通的loader
  • 内联loader

插件全局引入

为了解决一些插件不支持commonJs引入的问题(如:bootstrap.js,它只允许jQuery暴露为全局变量才可用)

全局暴露(expose-loader)

npm i expose-loader -S

该加载器可以将模块添加到全局对象,以内联loader使用

**入口 js 直接使用 **

import $ from 'expose-loader?$!jquery' 

或在webpack.config.js中配置

webpack.config.js

{
  test: require.resolve('jquery'),
  use: 'expose-loader?$'
},

入口js

import $ from 'jquery'

require.resolve 简介

将变量注入每个模块

webpack 内置了一个 webpack.ProvidePlugin 插件,可以为每一个模块都提供需要的插件

webpack.config.js

const webpack = require('webpack')
module.exports = {
    ...
    plugins: [
        ...
        new webpack.ProvidePlugin({
          $: 'jquery' // 将 jquery 提供给 $
        })
    ]
}
  • 配置完成后则无需在各个模块中再次引入
  • 只是为每个模块注入了变量,并没有挂载到全局上,window.$ 是 undefined

在html中引入,但不打包

index.html

<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>

webpack.config.js

如果我们想外部引用一个库,但是又不想让webpack打包,并且又不影响我们在程序中以CMD、AMD或者window/global全局等方式进行使用,那就可以通过配置externals。

module.exports = {
  ...
  externals: {
      jquery: '$'
  },
  ...
}

则我们在模块中 使用 import $ from 'jquery'时,webpack会忽略这条语句的打包处理

多入口

多入口配置

多个入口js

  // 入口文件
  entry: {
    index: './src/index.js',
    other: './src/other.js'
  },
  // 出口文件
  output: {
    filename: '[name].js', // 打包后的文件名字 [name]相当于变量储存了 入口文件的键名 index other
    path: path.resolve(__dirname, 'dist') // 储存路径
  },

多个 html,创建多个 HtmlWebpackPlugin 并配置

const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
  ...
  plugins: [  // 这里面存放了所有的webpack的插件
        
    // 借助 html-webpack-plugin 自动在dist下 创建 html 并引入 指定js
    new HtmlWebpackPlugin({ 
      template: './src/index.html',  // 需要的模版html
      filename: 'index.html', // 编译之后文件名
      chunks: ['index'], // 需要引入的js块 可同时引入多个
      minify:{ // 压缩 html
        removeAttributeQuotes: true, // 删除 属性双引号
        collapseWhitespace: true // 折叠 空行
      }, 
      hash: true // 在引用js时 添加 hash (解决缓存问题)
    }),
      
    // 多个 html
    new HtmlWebpackPlugin({ 
      template: './src/index.html',
      filename: 'other.html', 
      chunks: ['other'], 
      minify:{ 
        removeAttributeQuotes: true, 
        collapseWhitespace: true 
      }, 
      hash: true
    })
        
  ]

}

抽离公共模块

当多入口同时引用一个模块的时候,我们希望这个模块在引用的时候被缓存,避免重复加载

配置优化项

webpack.config.js

module.export = {
  optimization: {
    ...
    splitChunks: { // 分割 代码块
      cacheGroups: { // 缓存组
        common: { // 公共模块
          chunks: 'initial' // 从初始模块 开始
          minSize: 0, // 超过 0 字节的
          minChunk: 2 // 使用 超过 2次的
        }
      }
    }
  }
  ...
}

单独抽离第三方模块

webpack.config.js

module.export = {
  optimization: {
    ...
    splitChunks: { // 分割 代码块
      cacheGroups: { // 缓存组
        ...
        vender: { // 第三方模块
          priority: 1, // 权重 优先在 模块抽离前抽离 
          test: /node_modules/, // 在 node_modules 目录下
          chunks: 'initial'
          minSize: 0,
          minChunks: 2
        }
      }
    }
  }
  ...
}

调试(source-map)

文件压缩打包之后,在浏览器中出现错误,却无法定位bug出现的具体位置。

我们需要的将源码映射出来,以供试调。

webpack.config.js

module.exports = {
  ...
  devtool: 'source-map', 
  ...
}

source-map 会生成一个 map 文件,大而全,出错时会标识,显示行和列

此外,也可以使用:

  • eval-source-map不会生成单独的文件,但是可以显示行和列
  • cheap-module-source-map 会生成单独的文件,但不显示列 (没有调试功能,但是可以保留)
  • cheap-module-eval-source-map 不会生成单独的文件,也不显示列

实时打包

如果我们需要实时查看打包之后的文件,可以为 webpack 配置 watch 监控 及 watchOptions 监控选项,

实时打包

webpack.config.js

module.exports = {
  ...
  watch: true, 
  watchOptions: {
    poll: 1000, // 1s 轮询 
    aggregateTimeout: 600, // 防抖
    ignored: /node_modules/ // 忽略文件
  },
  ...
}

其他小插件

clean-webpack-plugin

在每一次打包之前帮我们清空出口文件夹

npm i clean-webpack-plugin -D

webpack.config.js

const CleanWebpackPlugin = require('clean-webpack-plugin')
module.exports = {
  ...
  plugins: [
    ...
    new CleanWebpackPlugin() 
  ],
  ...
}

copy-webpack-plugin

直接拷贝文件到出口文件夹

npm i copy-webpack-plugin -D

webpack.config.js

const CopyWebpackPlugin = require('copy-webpack-plugin')
module.exports = {
  ...
  plugins: [
    ...
    new CopyWebpackPlugin([
      {
        from: 'doc', // 从 根目录下的什么文文件
        to: 'doc'	// 到 入口文件夹 的哪里
      }
    ]) 
      
  ],
  ...
}

webpack.BannerPlugin

webpack内置插件,在打包好的 js 文件开头标注信息, 通常标注作者和版权信息

webpack.config.js

const webpack = require('webpack')
module.exports = {
  ...
  plugins: [
    ...
    new webpack.BannerPlugin('2019 by cxsl')    
  ],
  ...
}

处理跨域

ajax

js

// 创建对象
let xhr = (window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP")

// 初始化一个请求
xhr.open('GET', '/api/user', true)     

// 事件触发
xhr.onreadystatechange = function () { 
  if (xhr.readyState === 4 && xhr.status === 200) { 
    console.log(xhr.response) 
  } 
}

// 发送 HTTP 请求
xhr.send()

ajax文档 xhr api

配置代理

在xhr初始化一个请求,存在请求的url域名、端口号不一样时,会产生跨域报错。为解决这个问题,可以使用代理的方式,原理是将请求进行转发。可以通过 http-proxy 这个模块实现。

WebPack中devServer的proxy代理其实是集成了http-proxy-middleware,可以直接配置。

webpack.config.js

module.exports = {
  ...
  devServer: {
  	proxy: {
      '/api':'http://localhost:3000'
    }
  }
  ...
}

更多配置

proxy: {
	'/api': {
		target: 'https://www.runoob.com',
		secure: false,
		changeOrigin: true,
		pathRewrite: { '/api': '' }
	}
} 

模拟数据

如果我们前端只是想单纯的模拟数据,devServer本身就是通过 express 实现的,可以直接在配置中模拟服务端响应

webpack.config.js

module.exports = {
  ...
  devServer: {
    ...
  	proxy: {
      '/api':'http://localhost:3000'
    }
  }
  ...
}

服务端启动 webpack

自己有服务端,可以将前端和服务端启动在一起

需要express中间件webpack-dev-middleware webpack提供

npm i webpack-dev-middleware -D

server.js

const express = require('express')
const app = express()

const webpack = require('webpack') // 引入webpack模块
const webpackDevMiddleware = require('webpack-dev-middleware') // 引入webpackdev中间件
const config = require('./webpack.config') // 引入配置文件
let compiler = webpack(config) // 处理配置文件,返回结果
app.use(webpackDevMiddleware(compiler)) // 使用中间件webpackDevMiddleware 传入配置

app.get('/user', (req, res) => res.json({
  message: 'hello world'
}))

app.listen(3000, () => console.log('Example app listening on port 3000!'))

第三方包解析配置(resolve)

nodejs提供了默认的第三方包查找规则,webpack 配置 resolve 可以覆盖一些解析规则。

别名

webpack.config.js

module.exports = {
  ...
  resolve: {
  	modules: [ path.resolve('node_modules') ], // 指明第三方包路径,可以多个
    alias:{ // 别名
      // 在js中 import 'bootstrap' 会根据配置路径查找文件 
      bootstrap: 'bootstrap/dist/css/bootstrap.css'
    }
  }
  ...
}

主入口

webpack.config.js

module.exports = {
  ...
  resolve: {
  	modules: [ path.resolve('node_modules') ], // 指明第三方包路径,可以多个
	//  主入口字段 在第三方的 package.json 中先找 'style' 指向 后找 'main'
    mainFields: [ 'style', 'main' ]
    // 主入口文件
    // mainFiles: [] 
  }
  ...
}

添加扩展名

webpack.config.js

module.exports = {
  ...
  resolve: {
  	modules: [ path.resolve('node_modules') ], // 指明第三方包路径,可以多个
	//  主入口字段 在第三方的 package.json 中先找 'style' 指向 后找 'main'
    extensions: [ '.js','.scss','css','.vue' ] // 在引入模块时 从左到右依次尝试添加扩展名
  }
  ...
}

生产与开发环境分离

webpack.DefinePlugin

定义全局变量

webpack.config.js

  plugins: [  // 这里面存放了所有的webpack的插件
    new webpack.DefinePlugin({
      DEV: JSON.stringify('dev') // 定义全局变量 DEV为 字符串dev
    }),
    ...
  ]

则可以根据这个变量判断环境

js

if (DEV === 'dev') {
  url = 'http://www.dev.com'
} else {
  url = 'http://www.prod.com'
}

分离配置文件

生产相关配置写入 webpack.prod.js

开发相关配置写入 webpack.dev.js

公共部分写入 webpack.base.js

通过webpack-merge 合并

npm i webpack-merge -D

webpack.dev.js

const { smart } = require('webpack-merge')
const base = require('./webpack.base.js')
const webpack = require('webpack')
...
module.exports = smart(base,{
  mode: 'development',  
  ...
  plugins: [  // 这里面存放了所有的webpack的插件
    new webpack.DefinePlugin({
      DEV: JSON.stringify('dev') // 定义全局变量 DEV为 字符串dev
    }),
    ...
  ]
})

webpack.prod.js

const { smart } = require('webpack-merge')
const base = require('./webpack.base.js')
const webpack = require('webpack')
...
module.exports = smart(base,{
  mode: 'production',  
  ...
  plugins: [  // 这里面存放了所有的webpack的插件
    new webpack.DefinePlugin({
      DEV: JSON.stringify('prod') // 定义全局变量 DEV为 字符串dev
    }),
    ...
  ]
})

页面自动更新

启用 devServer hot

当hot 为true 会开启强制刷新,当文件保存时,会强制刷新页面

webpack.dev.js

devServer: {
  hot: true,
  ...
}

配置热更新

在开启 hot 的基础上,我们还需要webpack.NamedModulesPlugin

webpack.HotModuleReplacementPlugin() 的辅助

plugins: [
  ...
  new webpack.NamedModulesPlugin(), // 打印更新的文件路径
  new webpack.HotModuleReplacementPlugin() // 热更新插件
]

样式表

借助于 style-loader 的帮助,CSS 的模块热替换实际上是相当简单的。当更新 CSS 依赖模块时,此 loader 在后台使用 module.hot.accept 来修补(patch) <style> 标签。

模块

查看更多

打包优化项

noParse

这是module中的一个属性,作用: 不去解析属性值代表的库的依赖

webpack解析引入的第三方包的时候,会去解析它是否还存在其他依赖,如果我们明确引入的包,不再有其他依赖,例如,jquery。

可以在webpack的配置中增加noParse属性,来提高解析效率

webpack.base.js

...
module:{
		noParse:/jquery/,// 不去解析jquery中的依赖库
		...
}

webpack.IgnorePlugin

例如 moment.js 引入了完整的语言包, webpack.IgnorePlugin 可以忽略掉某些引入的内容。

webpack.base.js

const webpack = require('webpack')
...
module.exports = {
  ...
  plugins: [
    // 忽略 moment 中的 ./locale 引入
    new webpack.IgnorePlugin(/\.\/locale/, /moment/),
	  ...
  ]
}

则我们需要手动引用相应的语言包

js

import moment from 'moment'
import 'moment/locale/zh-cn'

动态链接库

一些较大的模块,每次打包都要重新编译。我们可以单独的先将这些模块打包完成,再作引入。

新建webpack 配置

webpack.vue.js

const path = require('path')
const webpack = require('webpack')
module.exports = {
    mode: 'development',
    // 入口文件
    entry: {
      vue: ['vue','vue-router']
    },
    // 出口文件
    output: {
      filename: '_dll_[name].js', // 打包后的文件名字 [name]相当于变量储存了 入口文件的键名 index other
      path: path.resolve(__dirname, 'dist'), // 储存路径
      library: '_dll_[name]', // 将结果赋值给变量  
      libraryTarget: 'var' // 赋值形式 默认为 var 
    },
    plugins: [
      // 使用插件生成任务清单
      new webpack.DllPlugin({ // name == library
        name: '_dll_[name]',
        // 需要保存到一个json文件中
        path: path.resolve(__dirname,'dist','manifest.json')
      })
    ]
}

在html中引入打包好的模块

index.html

<script src="/_dll_vue.js"></script>

告诉webpack主配置,当我们引入这些模块的时候先去任务清单(动态链接库)上查找

webpack.base.js

  plugins: [

    new webpack.DllReferencePlugin({
      manifest: path.resolve(__dirname,'dist','manifest.json')
    }),
		...
  ]

Happypack

多线程打包

npm i happypack -D

webpack.base.js

const Happypack = require('happypack')
...
module.exports = {
  ...
  module: {
    rules:[
      {
        test: /\.js$/,
        exclude: /node_modules/,
        include: path.resolve(__dirname, './src'),
        use: 'Happypack/loader?id=js' // js 文件开启多线程打包
      }
    ]
  },
  plugins: [
    new Happypack({
			id: 'js',
  		use: ['babel-loader'] // 原有配置 转移至此处
    }),
    ...
  ]
}

懒加载

有的时候,我们不需要在一开始的时候就加载资源,而是通过需求,在特定的时候加载。

js

loadA () {
  // es6 草案中的语法 底层是通过jsonp实现动态加载文件 
  // 引入的资源会被编译成 一个 promise 
  import('./a.js').then( (data) => {
    // 原始资源 被放在 data.default 中
    console.log(data.default)
  })
}

需要借助 @babel/plugin-syntax-dynamic-import 插件

npm i @babel/plugin-syntax-dynamic-import -D

.babelrc

"plugins": [ // 插件
	...
  "@babel/plugin-syntax-dynamic-import"
]
转载自:https://juejin.cn/post/7247045258842767418
评论
请登录