乐趣区

关于webpack:webpack配置完全指南

概念

  来看一下官网对 webpack 的定义:

实质上,webpack 是一个古代 JavaScript 应用程序的动态模块打包器(module bundler)。当 webpack 解决应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中蕴含应用程序须要的每个模块,而后将所有这些模块打包成一个或多个 bundle。

  首先 webpack 是一个动态模块打包器,所谓的动态模块,包含脚本、样式表和图片等等;webpack 打包时首先遍历所有的动态资源,依据资源的援用,构建出一个依赖关系图,而后再将模块划分,打包出一个或多个 bundle。再次白 piao 一下官网的图,活泼的形容了这个过程:

  提到 webpack,就不得不提 webpack 的四个 外围概念

  • 入口(entry):批示 webpack 应该应用哪个模块,来作为构建其外部依赖图的开始
  • 输入(output):在哪里输入它所创立的 bundles
  • loader:让 webpack 可能去解决那些非 JavaScript 文件
  • 插件(plugins):用于执行范畴更广的工作

你的第一个打包器

  咱们首先在全局装置 webpack:

npm install webpack webpack-cli –g

  webpack 能够不应用配置文件,间接通过命令行构建,用法如下:

webpack <entry> [<entry>] -o <output>

  这里的 entry 和 output 就对应了上述概念中的入口和输出,咱们来新建一个入口文件:

//demo1/index.js
var a = 1
console.log(a)
document.write('hello webpack')

  有了入口文件咱们还须要通过命令行定义一下输出门路 dist/bundle.js:

webpack index.js -o dist/bundle.js

  这样 webpack 就会在 dist 目录生成打包后的文件。

  咱们也能够在我的项目目录新建一个 html 引入打包后的 bundle.js 文件查看成果。

配置文件

  命令行的打包形式仅限于简略的我的项目,如果咱们的我的项目较为简单,有多个入口,咱们不可能每次打包都把入口记下来;因而个别我的项目中都应用配置文件来进行打包;配置文件的命令形式如下:

webpack [--config webpack.config.js]

  配置文件默认的名称就是 webpack.config.js,一个我的项目中常常会有多套配置文件,咱们能够针对不同环境配置不同的文件,通过--config 来进行切换:

// 生产环境配置
webpack --config webpack.prod.config.js
// 开发环境配置
webpack --config webpack.dev.config.js
相干 webpack 视频解说:进入学习

多种配置类型

  config 配置文件通过 module.exports 导出一个配置对象:

//webpack.config.js
var path = require('path');
module.exports = {
  mode: 'development',
  // 入口文件
  entry: './index.js',
  // 输入目录
  output: {path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js'
  }
};

  除了导出为对象,还能够导出为一个函数,函数中会带入命令行中传入的环境变量等参数,这样能够更不便的对环境变量进行配置;比方咱们在打包线上正式环境和线上开发环境能够通过 env 进行辨别:

var path = require('path');
//env: 环境对象
module.exports = function(env, argv){
  return {
    // 其余配置
    entry: './index.js',
    output: {}}
};

  另外还能够导出为一个 Promise,用于异步加载配置,比方能够动静加载入口文件:

module.exports = () => {return new Promise((resolve, reject)=>{setTimeout(()=>{
            resolve({
                entry: './index.js',
                output: {}})
        }, 5000)
    })
}

入口

  正如在下面提到的,入口是整个依赖关系的终点入口;咱们罕用的单入口配置是一个页面的入口:

module.exports = {entry: './index.js',}

  它是上面的简写:

module.exports = {
    entry: {main: './index.js'},
}

  然而咱们一个页面可能不止一个模块,因而须要将多个依赖文件一起注入,这时就须要用到数组了,代码在 demo2 中:

module.exports = {
    entry: [
        // 轮播图模块
        './src/banner.js',
        // 主模块
        './src/index.js', 
        // 底部模块
        './src/foot.js'
    ],
}

  有时候咱们一个我的项目可能有不止一个页面,须要将多个页面离开打包,entry 反对传入对象的模式,代码在 demo3 中:

//demo3
module.exports = {
    entry: {
        home: './src/home.js',
        list: './src/list.js',
        detail: ['./src/detail.js', './src/common.js'],
    },
}

  这样 webpack 就会构建三个不同的依赖关系。

输入

  output选项用来管制 webpack 如何输出编译后的文件模块;尽管能够有多个 entry,然而只能配置一个output

module.exports = {
    entry: './src/index.js',    output: {path: path.resolve(__dirname, 'dist'),
        filename: 'bundle.js',
        //CDN 地址
        publicPath: '/',
    },
}

  这里咱们配置了一个单入口,输入也就是 bundle.js;然而如果存在多入口的模式就行不通了,webpack 会提醒 Conflict: Multiple chunks emit assets to the same filename,即多个文件资源有雷同的文件名称;webpack 提供了 占位符 来确保每一个输入的文件都有惟一的名称:

module.exports = {
    entry: {
        home: './src/home.js',
        list: './src/list.js',
        detail: ['./src/detail.js', './src/common.js'],
    },
    output: {path: path.resolve(__dirname, 'dist'),
        filename: '[name].bundle.js',
    },
}

  这样 webpack 打包进去的文件就会依照入口文件的名称来进行别离打包生成三个不同的 bundle 文件;还有以下不同的占位符字符串:

占位符 形容
[hash] 模块标识符 (module identifier) 的 hash
[chunkhash] chunk 内容的 hash
[name] 模块名称
[id] 模块标识符
[query] 模块的 query,例如,文件名 ? 前面的字符串

  在这里引入 Module、Chunk 和 Bundle 的概念,下面代码中也常常会看到有这两个名词的呈现,那么他们三者到底有什么区别呢?首先咱们发现 module 是经常出现在咱们的代码中,比方 module.exports;而 Chunk 常常和 entry 一起呈现,Bundle 总是和 output 一起呈现。

  • module:咱们写的源码,无论是 commonjs 还是 amdjs,都能够了解为一个个的 module
  • chunk:当咱们写的 module 源文件传到 webpack 进行打包时,webpack 会依据文件援用关系生成 chunk 文件,webpack 会对这些 chunk 文件进行一些操作
  • bundle:webpack 解决好 chunk 文件后,最初会输入 bundle 文件,这个 bundle 文件蕴含了通过加载和编译的最终源文件,所以它能够间接在浏览器中运行。

  咱们通过上面一张图更深刻的了解这三个概念:

总结:

  module,chunk 和 bundle 其实就是同一份逻辑代码在不同转换场景下的取了三个名字:咱们间接写进去的是 module,webpack 解决时是 chunk,最初生成浏览器能够间接运行的 bundle。

hash、chunkhash、contenthash

  了解了 chunk 的概念,置信下面表中 chunkhash 和 hash 的区别也很容易了解了;

  • hash:是跟整个我的项目的构建相干,只有我的项目里有文件更改,整个我的项目构建的 hash 值都会更改,并且全副文件都共用雷同的 hash 值。
  • chunkhash:跟入口文件的构建无关,依据入口文件构建对应的 chunk,生成每个 chunk 对应的 hash;入口文件更改,对应 chunk 的 hash 值会更改。
  • contenthash:跟文件内容自身相干,依据文件内容创立出惟一 hash,也就是说文件内容更改,hash 就更改。

模式

  在 webpack2 和 webpack3 中咱们须要手动退出插件来进行代码的压缩、环境变量的定义,还须要留神环境的判断,非常的繁琐;在 webpack4 中间接提供了模式这一配置,开箱即可用;如果疏忽配置,webpack 还会收回正告。

module.exports = {mode: 'development',};
// 相当于
module.exports = {
   devtool:'eval',
   plugins: [new webpack.NamedModulesPlugin(),
      new webpack.NamedChunksPlugin(),
      new webpack.DefinePlugin({"process.env.NODE_ENV": JSON.stringify("development") 
      })
   ]
}

  开发模式是通知 webpack,我当初是开发状态,也就是打包进去的内容要对开发敌对,便于代码调试以及实现浏览器实时更新。

module.exports = {mode: 'production',};
// 相当于
module.exports = {
   plugins: [new UglifyJsPlugin(/*...*/),
      new webpack.DefinePlugin({"process.env.NODE_ENV": JSON.stringify("production") 
      }),
      new webpack.optimize.ModuleConcatenationPlugin(),
      new webpack.NoEmitOnErrorsPlugin()]
}

  生产模式不必对开发敌对,只须要关注打包的性能和生成更小体积的 bundle。看到这里用到了很多 Plugin,不必慌,上面咱们会一一解释他们的作用。

  置信很多童鞋都曾有过疑难,为什么这边 DefinePlugin 定义环境变量的时候要用 JSON.stringify("production"),间接用"production" 不是更简略吗?

  咱们首先来看下 JSON.stringify("production") 生成了什么;运行后果是 ""production"",留神这里,并不是你眼睛花了或者屏幕上有小黑点,后果的确比"production" 多嵌套了一层引号。

  咱们能够简略的把 DefinePlugin 这个插件了解为将代码里的所有 process.env.NODE_ENV 替换为字符串中的 内容。如果咱们在代码中有如下判断环境的代码:

//webpack.config.js
module.exports = {
  plugins: [
    new webpack.DefinePlugin({"process.env.NODE_ENV": "production"}),
  ]
}
//src/index.js
if (process.env.NODE_ENV === 'production') {console.log('production');
}

  这样生成进去的代码就会编译成这样:

//dist/bundle.js
// 代码中并没有定义 production 变量
if (production === 'production') {console.log('production');
}

  然而咱们代码中可能并没有定义 production 变量,因而会导致代码间接报错,所以咱们须要通过 JSON.stringify 来包裹一层:

//webpack.config.js
module.exports = {
  plugins: [
    new webpack.DefinePlugin({//"process.env.NODE_ENV": JSON.stringify("production")
      // 相当于
      "process.env.NODE_ENV": '"production"'
    }),
  ]
}
//dist/bundle.js
if ("production" === 'production') {console.log('production');
}

  这样编译进去的代码就没有问题了。

主动生成页面

  在下面的代码中咱们发现都是手动来生成 index.html,而后引入打包后的 bundle 文件,然而这样太过繁琐,而且如果生成的 bundle 文件引入了 hash 值,每次生成的文件名称不一样,因而咱们须要一个主动生成 html 的插件;首先咱们须要装置这个插件:

npm install --save-dev html-webpack-plugin

  在 demo3 中,咱们生成了三个不同的 bundle.js,咱们心愿在三个不同的页面能别离引入这三个文件,如下批改 config 文件:

module.exports = {
    // 省略其余代码
    plugins: [
        new HtmlWebpackPlugin({
            // 援用的模板文件
            template: './index.html',
            // 生成的 html 名称
            filename: 'home.html',
            chunks: ['home']
        }),
        new HtmlWebpackPlugin({
            template: './index.html',
            filename: 'list.html',
            chunks: ['list']
        }),
        new HtmlWebpackPlugin({
            template: './index.html',
            filename: 'detail.html',
            chunks: ['detail']
        }),
    ]
}

  咱们以 index.html 作为模板文件,生成 home、list、detail 三个不同的页面,并且通过 chunks 别离引入不同的 bundle;如果这里不写 chunks,每个页面就会引入所有生成进去的 bundle。

  html-webpack-plugin 还反对以下字段:

new HtmlWebpackPlugin({
    template: './index.html',
    filename: 'all.html',
    // 页面注入 title
    title: 'html webpack plugin title',
    // 默认引入所有的 chunks 链接
    chunks: 'all',
    // 注入页面地位
    inject: true,
    // 启用 hash
    hash: true,
    favicon: '',
    // 插入 meta 标签
    meta: {'viewport': 'width=device-width, initial-scale=1.0'},
    minify: {
        // 革除 script 标签引号
        removeAttributeQuotes: true,
        // 革除 html 中的正文
        removeComments: true,
        // 革除 html 中的空格、换行符
        // 将 html 压缩成一行
        collapseWhitespace: false,
        // 压缩 html 的行内款式成一行
        minifyCSS: true,
        // 革除内容为空的元素(慎用)removeEmptyElements: false,
        // 革除 style 和 link 标签的 type 属性
        removeStyleLinkTypeAttributes: false
    }
}),

  下面设置 title 后须要在模板文件中设置模板字符串:

<title><%= htmlWebpackPlugin.options.title %></title>

loader

  loader 用于对模块 module 的源码进行转换,默认 webpack 只能辨认 commonjs 代码,然而咱们在代码中会引入比方 vue、ts、less 等文件,webpack 就解决不过去了;loader 拓展了 webpack 解决多种文件类型的能力,将这些文件转换成浏览器可能渲染的 js、css。

  module.rules容许咱们配置多个 loader,可能很清晰的看出以后文件类型利用了哪些 loader,loader 的代码均在 demo4 中。

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

  咱们能够看到 rules 属性值是一个数组,每个数组对象示意了不同的匹配规定;test 属性时一个正则表达式,匹配不同的文件后缀;use 示意匹配了这个文件后调用什么 loader 来解决,当有多个 loader 的时候,use 就须要用到数组。

  多个 loader 反对 链式传递 ,可能对资源进行流水线解决,上一个 loader 解决的返回值传递给下一个 loader;loader 解决有一个优先级, 从右到左,从下到上;在下面 demo 中对 css 的解决就听从了这个优先级,css-loader 先解决,解决好了再给 style-loader;因而咱们写 loader 的时候也要留神前后程序。

css-loader 和 style-loader

  css-loader 和 style-loader 从名称看起来性能很类似,然而两者的性能有着很大的区别,然而他们常常会成对应用;装置办法:

npm i -D css-loader style-loader

  css-loader 用来解释 @import 和 url();style-loader 用来将 css-loader 生成的样式表通过<style> 标签,插入到页面中去。

/* /src/head.css */
.head{
    display: flex;
    background: #666;
}
/* /src/foot.css */
.foot{background: #ccc;}
/* /src/index.css */
@import './head.css';
@import './foot.css';
.wrap {background: #999;}

  而后在入口文件中将 index.css 引入,就能看到打包的成果,页面中插入了三个 style 标签,代码在 demo4:

sass-loader 和 less-loader

  这两个 loader 看名字大家也能猜到了,就是用来解决 sass 和 less 款式的。装置办法:

npm i -D sass-loader less-loader node-sass

  在 config 中进行配置,代码在 demo4:

{
    // 其余配置
    rules: {
        test: /\.scss$/,
        use: [{loader: 'style-loader'}, {loader: 'css-loader'},{loader: 'sass-loader'}]
    },{
        test: /\.less$/,
        use: [{loader: 'style-loader'}, {loader: 'css-loader'},{loader: 'less-loader'}]
    }
}

postcss-loader

  都 0202 年了,小伙伴必定不想一个一个的手动增加 -moz、-ms、-webkit 等浏览器公有前缀;postcss 提供了很多对款式的扩大性能;啥都不说,先装置起来:

npm i -D postcss-loader

  老规矩,还是在 config 中进行配置:

rules: [{
    test: /\.scss$/,
    use: [{loader: 'style-loader'}, {loader: 'css-loader'}, {loader: 'postcss-loader'},{loader: 'sass-loader'}]
},{
    test: /\.less$/,
    use: [{loader: 'style-loader'}, {loader: 'css-loader'}, {loader: 'postcss-loader'},{loader: 'less-loader'}]
}]

  正当咱们灰溜溜的打包看成果时,发现款式还是老样子,并没有什么扭转。

  这是因为 postcss 次要性能只有两个:第一就是把 css 解析成 JS 能够操作的形象语法树 AST,第二就是调用插件来解决 AST 并失去后果;所以 postcss 个别都是通过插件来解决 css,并不会间接解决,所以咱们须要先装置一些插件:

npm i -D autoprefixer postcss-plugins-px2rem cssnano

  在我的项目根目录新建一个 .browserslistrc 文件。

> 0.25%
last 2 versions

  咱们将 postcss 的配置独自提取到我的项目根目录下的postcss.config.js

module.exports = {
    plugins: [
        // 主动增加前缀
        require('autoprefixer'),
        //px 转为 rem,利用于挪动端
        require('postcss-plugins-px2rem')({remUnit: 75}),
        // 优化合并 css
        require('cssnano'),
    ]
}

  有了 autoprefixer 插件,咱们打包后的 css 就主动加上了前缀。

babel-loader

  兼容低版本浏览器的痛置信很多童鞋都经验过,写完代码发现自己的 js 代码不能运行在 IE10 或者 IE11 上,而后尝试着引入各种 polyfill;babel 的呈现给咱们提供了便当,将高版本的 ES6 甚至 ES7 转为 ES5;咱们首先装置 babel 所须要的依赖:

npm i -D babel-loader @babel/core @babel/preset-env @babel/plugin-transform-runtime
npm i -S @babel/runtime

  而后在 config 增加 loader 对 js 进行解决:

{
    // 省略其余配置
    rules: [{
        test: /\.js/,
        use: {loader: 'babel-loader'}
    }]
}

  同样的,咱们把 babel 的配置提取到根目录,新建一个 .babelrc 文件:

{
    "presets": ["@babel/preset-env"],
    "plugins": ["@babel/plugin-transform-runtime"]
}

  咱们能够在 index.js 中尝试写一些 es6 的语法,看到代码会被转译成 es5,代码在 demo4 中。因为 babel-loader 的转译速度很慢,在前面咱们退出了工夫插件后能够看到每个 loader 的耗时,babel-loader 是最耗时间;因而咱们要尽可能少的应用 babel 来转译文件,咱们对 config 进行改良,

{
    // 省略其余配置
    rules: [{
        test: /\.js$/,
        use: {loader: 'babel-loader'},
        // exclude: /node_modules/,
        include: [path.resolve(__dirname, 'src')]
    }]
}

  正则上应用 $ 来进行准确匹配,通过 exclude 将 node_modules 中的文件进行排除,include 将只匹配 src 中的文件;能够看进去 include 的范畴比 exclude 更放大更准确,因而也是举荐应用 include。

file-loader 和 url-loader

  file-loader 和 url-loader 都是用来解决图片、字体图标等文件;url-loader 工作时候两种状况:当文件大小小于 limit 参数,url-loader 将文件转为 base-64 编码,用于缩小 http 申请;当文件大小大于 limit 参数时,调用 file-loader 进行解决;因而咱们优先应用 url-loader,首先还是进行装置,装置 url-loader 之前还须要把 file-loader 先装置:

npm i file-loader url-loader -D

  接下来还是批改 config:

{
    // 省略其余配置
    rules: [{test: /\.(png|jpg|gif|jpeg|webp|svg|eot|ttf|woff|woff2)$/,
        use: {
            loader: 'url-loader',
            options: {
                //10k
                limit: 10240,
                // 生成资源名称
                name: '[name].[hash:8].[ext]',
                // 生成资源的门路
                outputPath: 'imgs/'
            },
            exclude: /node_modules/,
        }
    }]
}

  咱们在 css 中给 body 增加一个小于 10k 的居中背景图片:

body{
    width: 100vw;
    height: 100vh;
    background: url(./bg.png) no-repeat;
    background-size: 400px 400px;
    background-position: center center;
}

  打包后查看 body 的款式能够发现图片曾经被替换成 base64 格局的 url 了,代码在 demo4。

html-withimg-loader

  如果咱们在页面上援用一个图片,会发现打包后的 html 还是援用了 src 目录下的图片,这样显著是谬误的,因而咱们还须要一个插件对 html 援用的图片进行解决:

npm i -D html-withimg-loader

  老样子还是在 config 中对 html 进行配置:

{
    // 省略其余配置
    rules: [{test: /\.(htm|html)$/,
        use: {loader: 'html-withimg-loader'}
    }]
}

  然鹅,关上页面发现却是这样的:

  这是因为在 url-loader 中把每个图片作为一个模块来解决了,咱们还须要去 url-loader 中批改:

use: {
    loader: 'url-loader',
    options: {
        //10k
        limit: 10240,
        esModule: false
    }
}

  这样咱们在页面上的图片援用也被批改了,代码在 demo4 中。

  html-withimg-loader 会导致 html-webpack-plugin 插件注入 title 的模板字符串 <%= htmlWebpackPlugin.options.title %> 生效,一成不变的展现在页面上;因而,如果咱们想保留两者的性能须要在配置 config 中把 html-withimg-loader 删除并且通过上面的形式来援用图片:

<img src="<%=require('./src/bg1.png') %>" alt=""srcset="">

vue-loader

  最初说一下一个比拟非凡的 vue-loader,看名字就晓得是用来解决 vue 文件的。

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

  咱们首先来创立一个 vue 文件,具体代码在 demo5 中:

//src/App.vue
<template>
    <div id="app">
        <div class="box" @click="tap">{{title}}</div>
    </div>
</template>
<script>
export default {name: 'app',    data(){return {            title: 'app 实例'}    },    methods: {tap(){this.title = this.title.split('').reverse().join('')        }    }}
</script>
<style>
#app{font-size: 16px;    background: #ccc;}
</style>

  而后在 webpack 的入口文件中援用它:

//src/main.js
import Vue from 'vue'
import App from './App.vue'

new Vue({render: h => h(App)
}).$mount('#app')

  不过 vue-loader 和其余 loader 不太一样,除了将它和 .vue 文件绑定之外,还须要引入它的一个插件:

const VueLoaderPlugin = require('vue-loader/lib/plugin')
module.exports = {
    module: {
        rules: [
        // 省略其余 loader
        {
            test: /\.vue$/,
            loader: 'vue-loader'
        }]
    },
    plugins: [new VueLoaderPlugin(),
    ]
}

  这样咱们就能欢快的在代码中写 vue 了。

搭建开发环境

  在下面的 demo 中咱们都是通过命令行打包生成 dist 文件,而后间接关上 html 或者通过 static-server 来查看页面的;然而开发中咱们写完代码每次都来打包会重大影响开发的效率,咱们冀望的是写完代码后立刻就可能看到页面的成果;webpack-dev-server 就很好的提供了一个简略的 web 服务器,可能实时从新加载。

  首先在咱们的我的项目中装置依赖:

npm i -D webpack webpack-dev-server

  webpack-dev-server 的用法和 wepack 一样,只不过他会额定启动一个 express 的服务器。咱们在我的项目中新建一个 webpack.dev.config.js 配置文件,独自对开发环境进行一个配置,相干代码在 demo6 中:

module.exports = {
    // 省略其余配置
    devServer: {
        // 启动服务器端口
        port: 9000,
        // 默认是 localhost,只能本地拜访
        host: "0.0.0.0",
        // 主动关上浏览器
        open: false,
        // 启用模块热替换
        hot: true,
        // 启用 gzip 压缩
        compress: true
    },
    plugins: [
        // 热更新插件
        new webpack.HotModuleReplacementPlugin({})
    ]
}

  通过命令行 webpack-dev-server 来启动服务器,启动后咱们发现根目录并没有生成任何文件,因为 webpack 打包到了内存中,不生成文件的起因在于拜访内存中的代码比拜访文件中的代码更快。

  咱们在 public/index.html 的页面上有时候会援用一些本地的动态文件,间接关上页面的会发现这些动态文件的援用生效了,咱们能够批改 server 的工作目录,同时指定多个动态资源的目录:

contentBase: [path.join(__dirname, "public"),
  path.join(__dirname, "assets")
]

  热更新(Hot Module Replacemen 简称 HMR)是在对代码进行批改并保留之后,webpack 对代码从新打包,并且将新的模块发送到浏览器端,浏览器通过新的模块替换老的模块,这样就能在不刷新浏览器的前提下实现页面的更新。

  能够看出浏览器和 webpack-dev-server 之间通过一个 websock 进行连贯,初始化的时候 client 端保留了一个打包后的 hash 值;每次更新时 server 监听文件改变,生成一个最新的 hash 值再次通过 websocket 推送给 client 端,client 端比照两次 hash 值后向服务器发动申请返回更新后的模块文件进行替换。

  咱们点击源码旁的行数看一下编译后的源码是什么样的:

  发现跟咱们的源码差距还是挺大的,原本是一个简略 add 函数,通过 webpack 的模块封装,曾经很难了解原来代码的含意了,因而,咱们须要将编译后的代码映射回源码;devtool 中不同的配置有不同的成果和速度,综合性能和品质后,咱们个别在开发环境应用cheap-module-eval-source-map,在生产环境应用source-map

module.exports = {
    devtool: 'cheap-module-eval-source-map',
    // 其余配置
}

  其余各模式的比照:

plugins

  在下面咱们也介绍了 DefinePlugin、HtmlWebpackPlugin 等很多插件,咱们发现这些插件都可能不同水平的影响着 webpack 的构建过程,上面还有一些罕用的插件,plugins 相干代码在 demo7 中。

clean-webpack-plugin

  clean-webpack-plugin 用于在打包前清理上一次我的项目生成的 bundle 文件,它会依据 output.path 主动清理文件夹;这个插件在生产环境用的频率十分高,因为生产环境常常会通过 hash 生成很多 bundle 文件,如果不进行清理的话每次都会生成新的,导致文件夹十分宏大;这个插件装置应用十分不便:

npm i -D clean-webpack-plugin

  装置后咱们在 config 中配置一下就能够了:

const {CleanWebpackPlugin} = require('clean-webpack-plugin');
module.exports = {
    // 其余配置
    plugins: [new CleanWebpackPlugin(),
        new HtmlWebpackPlugin({
            template: './public/index.html',
            filename: 'index.html',
        })
    ]
}

mini-css-extract-plugin

  咱们之前的款式都是通过 style-loader 插入到页面中去,然而生产环境须要独自抽离款式文件,mini-css-extract-plugin 就能够帮我从 js 中剥离款式:

npm i -D mini-css-extract-plugin

  咱们在开发环境应用 style-loader,生产环境应用 mini-css-extract-plugin:

const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
    // 其余配置
    module: {
        rules: [
            {
                test: /\.less/,
                use: [{loader: isDev ? 'style-loader' : MiniCssExtractPlugin.loader},{loader: 'css-loader'},{loader: 'less-loader'}]
            }
        ]
    },
    plugins: [
        new MiniCssExtractPlugin({filename: "[name].[hash:8].css",
        })
    ]
}

  引入 loader 后,咱们还须要配置 plugin,提取的 css 同样反对 output.filename 中的占位符字符串。

optimize-css-assets-webpack-plugin

  咱们能够发现尽管配置了 production 模式,打包进去的 js 压缩了,然而打包进去的 css 确没有压缩;在生产环境咱们须要对 css 进行一下压缩:

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

  而后也是引入插件:

const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
module.exports = {
    // 其余配置
    plugins: [new OptimizeCSSAssetsPlugin(),    ]
}

copy-webpack-plugin

  和 demo6 中一样,咱们在 public/index.html 中引入了动态资源,然而打包的时候 webpack 并不会帮咱们拷贝到 dist 目录,因而 copy-webpack-plugin 就能够很好地帮我做拷贝的工作了

npm i -D copy-webpack-plugin

  在 config 中配置咱们须要拷贝的源门路和指标门路:

const CopyWebpackPlugin = require('copy-webpack-plugin');
module.exports = {
    plugins: [
        new CopyWebpackPlugin({
            patterns: [
                {
                    from: 'public/js/*.js',
                    to: path.resolve(__dirname, 'dist', 'js'),
                    flatten: true,
                }
            ]
        }),
    ]
}

ProvidePlugin

  ProvidePlugin 能够很快的帮咱们加载想要引入的模块,而不必 require。个别咱们加载 jQuery 须要先把它 import:

import $ from 'jquery'
$('.box').html('box')

  然而咱们在 config 中配置 ProvidePlugin 插件后可能不必 import,间接应用$

module.exports = {
    plugins: [
        new webpack.ProvidePlugin({
            $: 'jquery',
            jQuery: 'jquery'
        }),
    ]
}

  然而如果在我的项目中引入了太多模块并且没有 require 会让人摸不着头脑,因而倡议加载一些常见的比方 jQuery、vue、lodash 等。

loader 和 plugin 的区别

  介绍了这么多 loader 和 plugin,咱们来回顾一下他们两者的区别:

loader:因为 webpack 只能辨认 js,loader 相当于翻译官的角色,帮忙 webpack 对其余类型的资源进行转译的预处理工作。
plugins:plugins 扩大了 webpack 的性能,在 webpack 运行时会播送很多事件,plugin 能够监听这些事件,而后通过 webpack 提供的 API 来扭转输入后果。

退出移动版