webpack掠影-4

304 阅读4分钟

这是我参与8月更文挑战的第27天,活动详情查看:8月更文挑战

前面我们介绍了 webpack 的压缩合并代码,babel 的配置,今天我们练习 webpack 中暴露全局变量和 webpack 内置插件的使用;

一、 暴露全局变量

开发的过程中我们有很多的时候需要把一些数据或者类库暴露到全局,例如 jQuery 等;webpack 对应这一需要,也提供了若干支持方式。

1.1 expose-loader

expose-loader 可以把 jq 等暴露到全局;

1.1.1 安装 expose-loader

yarn add expose-loader

1.1.2 修改配置文件

  • test: require.resolve('jquery') 当解析到 jquery 模块时,使用 expose-loader 暴露到全局
  • use: expose-loader?$ 等效的内联 import $ from 'expose-loader?$!jquery'; ?$ 是传递给 expose-loader 的参数,表示 jquery 暴露到全局后,$ 代表 jquery 的引用
const path = require('path');

const HTMLWebpackPlugin = require('html-webpack-plugin');
const MiniCSSExtractPlugin = require('mini-css-extract-plugin'); // 抽离 css文件

const OptimizeCSSAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin'); // 压缩合并 css 文件
const UglifyjsWebpackPlugin = require('uglifyjs-webpack-plugin'); // 压缩混淆 js 文件

module.exports = {
  entry: './src/index.js',
  // mode: 'development',
  mode: 'production',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js'
  },
  devServer: {
   
  },
  module: {
    rules: [
      // some ignored cfg loaders...
      {
        test: /.(htm|html)$/,
        use:  'html-withimg-loader' // 处理 html 中的 img 引用的图片
      },
+      {
+        test: require.resolve('jquery'),
+        use: 'expose-loader?$' 
+      }
    ]
  },
  plugins: [
    
  ],

  // 配置优化配置项
  optimization: {
    
  }
}
  • 经过上面的暴露后,修改 sthjs/a.js 测试暴露是否生效
+ console.log($) // 无需导入,直接可以访问到全局下的 $
+ console.log(typeof $.ajax)

1.2 webpack.ProvidePlugin

暴露到全局无非是希望各个模块都能访问的到,有另一种思路也可以解决这一问题。利用 webpack.ProvidePlugin 把某个模块注入到各个模块中,模块可以自动访问到这个模块;ProvidePlugin 也是我们之前说到的 webpack 内置的插件之一,还有几个,我们后面会介绍;

1.2.1 导入 ProvidePlugin 插件,并修改配置

  • webpack.config.jsplugins 配置中中增加 new webpack.ProvidePlugin({ $1: 'jquery',_: 'lodash' })
const path = require('path');

+ const webpack= require('webpack');

module.exports = {
  entry: './src/index.js',
  // mode: 'development',
  mode: 'production',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js'
  },
  devServer: {
    // 开发服务器的配置
  },
  module: {
    rules: [
      
    ]
  },
  plugins: [


+    // 注入模块: key 是模块中可以访问的变量,value 是对应的模块
+    new webpack.ProvidePlugin({
+      $1: 'jquery',
+      _: 'lodash'
+    })
  ],

  // 配置优化配置项
  optimization: {
    minimizer: [
    ]
  }
}

1.2.2 在 sthjs/a.js 中测试

console.log($) // 无需导入,直接可以访问到全局下的 $

+ console.log($1);
+ console.log(_);


// ...other code ignored
  • 效果如图

wbp4-provide-plugin.png

1.3 配置 externals

还有一种方式同样可以实现这一功能,那就是通过 externals;具体操作是通过 UMD 的方式,把需要的类库通过 script 标签引入到页面中;

这样做不仅可以暴露出全局变量,还有一个很大的优势是这些模块并不会被打包输出到 bundle 中,所以这也是一种很重要的减小代码体积的方式;

1.3.1 在文档中插入某类库

找一个类库的 cdn 连接,然后在 html 模板中增加 script 标签引入类库,以 vue 为例

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
<div id="root"></div>

+ <div id="root-vue"></div>

<p>
  这是一个有背景的p标签
</p>
<img src="./img/miss-you.png" alt="">


+ <script src="https://cdn.bootcss.com/vue/2.6.10/vue.common.dev.js"></script>
</body>
</html>

1.3.2 修改配置文件

增加配置 externalsexternals: { vue: 'Vue' }import Vue from 'vue' 时,就会寻找全局属性 window.vue

const path = require('path');

const webpack= require('webpack');

module.exports = {
  entry: './src/index.js',
  // mode: 'development',
  mode: 'production',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js'
  },
  devServer: {
   
  },
  module: {
    rules: [
     
  ],

  // 配置优化配置项
  optimization: {
    minimizer: [
    
    ]
  },

+  // 配置 externals
+  externals: {
+    vue: 'Vue' // import Vue from 'vue' 时,就会寻找 window.vue
+  }
}

1.3.3 在 index.js 中增加 vue 相关代码

import $ from 'jquery' // 使用 expose-loader 暴露 $ 到 全局;

+ import Vue from 'vue'

// ... other code ignored

+ /*!!!!!! vue code start */
+ let components = {
+   template: `<p @click="handler">{{content}}</p>`,
+   data () {
+     return {
+       content: 'root-vue'
+     }
+   },
+   methods: {
+     handler () {
+       console.log('the components was clicked')
+     }
+   }
+ };
+
+ let vm = new Vue(components);
+ vm.$mount('#root-vue');

/* vue code end*/

  • 效果如图

wbp4-externals-vue.png

二、 常用的 webpack 内置插件

2.1 webpack.DefinePlugin

DefinePlugin 插件可以通过配置定义一些数据供模块中使用;

2.1.1 修改配置文件

const path = require('path');

const webpack= require('webpack');

module.exports = {
  entry: './src/index.js',
  // mode: 'development',
  mode: 'production',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js'
  },
  devServer: {
   
  },
  module: {
    rules: [
     
    ]
  },
  plugins: [
    new HTMLWebpackPlugin({
      template: './src/index.html', // 需要引入输出文件的 html 文件模板
      hash: true, // 给引入的 js 加hash
      filename: 'index.html', // 输出的 html 文件的名字(插件会把模板 html 复制到输出目录中)
      minify: { // 压缩优化输出的 html 文件配置
        removeAttributeQuotes: true, // 删除行内属性的双引号
        collapseWhitespace: true // 删除换行,使内容保持在一行
      }
    }),
    // 抽离 css 文件
    new MiniCSSExtractPlugin({
      filename: 'css/main.[hash:5].css'
    }),

    // 注入模块: key 是模块中可以访问的变量,value 是对应的模块
    new webpack.ProvidePlugin({
      $1: 'jquery',
      _: 'lodash'
    }),

+    // 定义数据
+    new webpack.DefinePlugin({
+      // 在这个对象中 key 将来会变成一个变量,value 就是 js 代码表达式
+      DEV: JSON.stringify('dev')
+    })
  ],

  // 配置优化配置项
  optimization: {
    minimizer: [
     
    ]
  },

  // 配置 externals
  externals: {
    vue: 'Vue' // import Vue from 'vue' 时,就会寻找 window.vue
  }
}

2.1.2 新建 sthjs/b.js 文件,检验 DEV 变量

+ console.log(DEV) // 这个 DEV 就是 webpack.DefinePlugin() 定义的 DEV 变量

在浏览器中查看效果,可以看到控制台输出了 'dev' ;这个 'dev' 就是我们在 webpack.DefinePlugin() 中定义的 DEV 变量的值;

2.2 webpack.CleanWebpackPlugin

我们发现每次执行 npm run build 命令都会把生成一份新的打包出来的文件,之前的文件就没有用了,所以 webpack 提供了插件,专门清除过往打包出来的内容;

2.2.1 安装 clean-webpack-plugin

yarn add clean-webpack-plugin -D

2.2.2 修改配置文件

const path = require('path');

const webpack= require('webpack');

+ const { CleanWebpackPlugin } = require('clean-webpack-plugin');
+ const CopyWebpackPlugin = require('copy-webpack-plugin');


module.exports = {
  entry: './src/index.js',
  // mode: 'development',
  mode: 'production',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js'
  },
  devServer: {
    
  },
  module: {
    rules: [
      
    ]
  },
  plugins: [
    
+    // 清空打包的内容
+    new CleanWebpackPlugin(),
  ],

  // 配置优化配置项
  optimization: {
    minimizer: [
    
    ]
  },

  // 配置 externals
  externals: {
    vue: 'Vue' // import Vue from 'vue' 时,就会寻找 window.vue
  }
}

  • 然后修改文件执行 npm run build 命令,我们会发现之前打包的文件以及被删除了;经过这一陪之后,每次执行打包,都会把之前的文件清空,再输出新的打包文件;

2.3 copy-webpack-plugin

项目中经常有些文件需要从源文件目录复制到输出目录,此时就需要使用 copy-webpack-plugin;

2.3.1 安装 copy-webpack-plugin

yarn add copy-webpack-plugin -D

2.3.2 修改配置文件

我们在 src 目录下新建了一个 doc 目录,新建一个 reademe.md 文件;

  • 复制文件内容: 接收一个数组,数组项是对象,对象中的 from 表示被复制的目录或者文件,to 表示输出的目录;

  • new CopyWebpackPlugin([{from: './src/doc',to: path.resolve(__dirname, './dist/doc')}])

const path = require('path');

const webpack= require('webpack');

const { CleanWebpackPlugin } = require('clean-webpack-plugin');
+ const CopyWebpackPlugin = require('copy-webpack-plugin');


module.exports = {
  entry: './src/index.js',
  // mode: 'development',
  mode: 'production',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js'
  },
  devServer: {
    
  },
  module: {
    rules: [
     
    ]
  },
  plugins: [
    
+    new CopyWebpackPlugin([
+      {
+        from: './src/doc',
+        to: path.resolve(__dirname, './dist/doc')
+      }
+    ])
  ],

  // 配置优化配置项
  optimization: {
   
  },

  // 配置 externals
  externals: {
    vue: 'Vue' // import Vue from 'vue' 时,就会寻找 window.vue
  }
}
  • src/doc/reaeme.md
# 当你从我的全世界路过

## 你是一往而情深

## 是曾经年少也是纸短情长
  • 效果如图

wbp4-cp-plugin.png

2.4 webpack.BannerPlugin

有一些场景需要在代码中生成一些信息,例如协议、版权、作者等;

const path = require('path');

const webpack= require('webpack');



module.exports = {
  entry: './src/index.js',
  // mode: 'development',
  mode: 'production',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js'
  },
  devServer: {
   
  },
  module: {
    rules: [
     
    ]
  },
  plugins: [
   
+    // 在打包后的代码中输出必要信息
+    new webpack.BannerPlugin('发上等愿,结中等缘,享下等福;择高处立,寻平处住,想宽处行')
  ],

  // 配置优化配置项
  optimization: {
    minimizer: [
     
    ]
  },

  // 配置 externals
  externals: {
    vue: 'Vue' // import Vue from 'vue' 时,就会寻找 window.vue
  }
}
  • 效果如图

wbp4-banner-plugin.png