webpack的基本使用

参考:https://segmentfault.com/a/1190000006178770#articleHeader2

1、webpack的概念

本质上,webpack 是一个现代JavaScript 应用程序的静态模块打包器。当 webpack 处理应用程序时,webpack 从命令行或配置文件中定义的一个模块列表开始,处理你的应用程序。它会递归地构建一个依赖关系图,其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle通常只有一个 - 可由浏览器加载。

2、webpack的安装

全局安装:

npm i webpack -g

本地安装:

npm i webpack -D

webpack 如果全局安装了的话就可以直接在命令行中直接运行webpack,如果没有全局安装就无法在命令行直接编译,不管有没有配置 package.json 或者 webpack.config.js 文件。

通过命令行来直接运行 webpack 命令,有些webpack版本如果你不事先在出口文件夹中建立对应的出口文件的话就会报错。所以不建议直接用命令 webpack 来运行编译,而是通过本地安装然后使用 npm 命令编译。

所以webpack一般会安装在本地,先配置 package.json 文件中的 scripts 选项和配置 webpack.config.js,然后通过 npm 命令来运行,比如:运行 npm run build 来编译 。

2.1、版本问题

webpack中的版本并不是越新越好,新版本或许还在测试阶段,甚至还有bug。新的版本比如4.xx就需要安装webpack-cli,而旧的版本比如3.5.3就不需要安装webpack-cli(好像是因为新的版本webpack和webpack-cli分离了,而旧的版本中webpack包括了webpac-cli).

当我使用最新版本编译时会报一些莫名其妙的错误,目前使用 webpack@3.5.3 版本没有问题。

3、使用webpack

构建一个如下图的项目架构:

在 main.js 文件中引用 Greeter.js 文件,在 index.html 文件中直接引用 将要生成的 bundle.js 文件而不是 main.js 或者 Greeter.js 文件。

运行webpack:(注意,下面的是直接用全局安装的webpack进行编译,我们并不建议这么做)

webpack app/main.js public/bundle.js    //全局安装了webpack
node_modules/.bin/webpack app/main.js public/bundle.js    //没有全局安装 webpack

3.1、配置 webpack.config.js 文件

Webpack拥有很多其它的比较高级的功能,这些功能其实都可以通过命令行模式实现,但非常不方便,所以定义一个配置文件非常有必要。这个配置文件其实也是一个简单的JavaScript模块,我们可以把所有的与打包相关的信息放在里面。

 下面代码保存为一个 webpack.config.js 文件:

module.exports = {
  entry:  __dirname + "/app/main.js",  //已多次提及的唯一入口文件
  output: {
    path: __dirname + "/public",  //打包后的文件存放的地方,__dirname 是node.js中的一个全局变量,它指向当前执行脚本所在的目录。
    filename: "bundle.js"      //打包后输出文件的文件名
  }
}

配置好该文件之后,只需在终端里运行 webpack 命令就可以了(非全局安装此时仍需使用node_modules/.bin/webpack),这条命令会自动引用webpack.config.js文件中的配置选项

3.2、配置 package.json 文件来使用 npm 命令进行编译

对 npm 进行配置后可以在命令行中使用简单的 npm start命令来替代全局 webpack 命令,在package.json中对scripts对象进行相关设置即可。

{
  "name": "webpack-sample-project",
  "version": "1.0.0",
  "description": "Sample webpack project",
  "scripts": {
    "start": "webpack" // 修改的是这里,JSON文件不支持注释,引用时请清除。如果对应的命令名不是 start 而是其它的话,想要在命令行中运行时,需要加上 run,比如:npm run build
  },
  "author": "zhang",
  "license": "ISC",
  "devDependencies": {
    "webpack": "3.10.0"
  }
}

此后,直接在命令行运行 npm start 就可实现编译。注意:此时编译使用的 webpack 是先会找本地安装的 webpack,然后才是全局安装的。我们建议使用这种方式,且建议本地安装而不是全局安装 webpack。

4、使用 Source Maps(使调试更容易)

如果原文件中代码出错,通过打包后在浏览器中很难找到对应的出错的地方,此时的出错提示并不一定会指出正确的代码出错文件名和位置。而Source Maps能解决这个问题。

通过简单的配置,webpack就可以在打包时为我们生成的source maps,这为我们提供了一种对应编译文件和源文件的方法,使得编译后的代码可读性更高,也更容易调试。

webpack的配置文件中配置source maps,只需要配置devtool

module.exports = {
  devtool: 'eval-source-map',
  entry:  __dirname + "/app/main.js",
  output: {
    path: __dirname + "/public",
    filename: "bundle.js"
  }
}

devtool 字段有很多选项,网上看到都推荐开发时使用:cheap-module-eval-source-map 字段,生产时使用:cheap-module-source-map。

但是开发时使用cheap-module-eval-source-map 字段报的错误是在编译后的文件中

我觉得这非常有问题啊,cheap-module-eval-source-map 和 eval-source-map 字段在浏览器中都不能很好地显示错误的位置,而 cheap-module-source-map 和 source-map 字段就能很好显示错误出现的文件名和行数。

所以我建议使用:cheap-module-source-map

官网建议开发使用:eval-source-map,但是这个有些错误在浏览器中并不能很好地展示出来,而是要展开才行,错误文件在第一行。生产时使用:none

 5、构建本地服务器(浏览器自动刷新)

devserver 能让浏览器监听你的代码的修改,并自动刷新显示修改后的结果。

Webpack提供一个可选的本地开发服务器,该服务器基于node.js构建。它是一个单独的组件,在webpack中进行配置之前需要单独安装它作为项目依赖

npm install --save-dev webpack-dev-server

配置webpack.config.js 文件:

module.exports = {
  devtool: 'eval-source-map',
  entry:  __dirname + "/app/main.js",
  output: {
    path: __dirname + "/public",
    filename: "bundle.js"
  },
  devServer: {
    contentBase: "./public",//本地服务器所加载的页面所在的目录
    historyApiFallback: true,//不跳转
    inline: true//实时刷新
  } 
}

package.json中的scripts对象中添加如下命令,用以开启本地服务器:

  "scripts": {
    "test": "echo "Error: no test specified" && exit 1",
    "start": "webpack",
    "server": "webpack-dev-server --open"
  }

在终端中输入npm run server即可在本地的8080端口查看结果

6、Loaders

通过使用不同的loaderwebpack有能力调用外部的脚本或工具,实现对不同格式的文件的处理,比如说分析转换scss为css,或者把下一代的JS文件(ES6,ES7)转换为现代浏览器兼容的JS文件。对React的开发而言,合适的Loaders可以把React的中用到的JSX文件转换为JS文件。

Loaders需要单独安装并且需要在webpack.config.js中的modules关键字下进行配置,Loaders的配置包括以下几方面:

  • test:一个用以匹配loaders所处理文件的拓展名的正则表达式(必须)
  • loader:loader的名称(必须)
  • include/exclude:手动添加必须处理的文件(文件夹)或屏蔽不需要处理的文件(文件夹)(可选);
  • query:为loaders提供额外的设置选项(可选)

 6.1、CSS-loader

webpack提供两个工具处理样式表,css-loader 和 style-loader,二者处理的任务不同。css-loader使你能够使用类似@import 和 url(...)的方法实现 require()的功能,style-loader将所有的计算后的样式加入页面中,二者组合在一起使你能够把样式表嵌入webpack打包后的JS文件中。

//安装
npm install --save-dev style-loader css-loader  //css-loader版本太高编译可能会出错,建议降低版本比如 css-loader@1.0.1 可用
//使用
module.exports = {
   ...
    module: {
        rules: [
            {
                test: /(.jsx|.js)$/,
                use: {
                    loader: "babel-loader"
                },
                exclude: /node_modules/
            },
            {
                test: /.css$/,  //对同一个文件引入多个loader的方法。loader的作用顺序是后面的loader先开始作用
                use: [
                    {
                        loader: "style-loader"
                    }, {
                        loader: "css-loader"
                    }
                ]
            }
        ]
    }
};

然后就可以在JS文件中引入css文件了。比如使用下面的7.1配置好解析 jsx 的babel包后,就可以使用下面的语法:

假设有一个 main.css 文件:

body {
  backgroud: green;
}

我们这里例子中用到的webpack只有单一的入口,其它的模块需要通过 importrequireurl等与入口文件建立其关联,为了让webpack能找到”main.css“文件,我们把它导入”main.js “中,如下

//main.js
import React from 'react';
import {render} from 'react-dom';
import Greeter from './Greeter';
import './main.css';//使用require导入css文件

render(<Greeter />, document.getElementById('root'));

通常情况下,css会和js打包到同一个文件中,并不会打包为一个单独的css文件。不过通过合适的配置webpack也可以把css打包为单独的文件的。

webpack3.*/webpack2.*内置可处理JSON文件,而无需添加webpack1.*需要的json-loader,所以我们可以直接在一个 JS 文件中直接引入一个 json 文件,比如:

创建一个config.json文件:

{
  "greetText": "Hi there and greetings from JSON!"
}

在Greeter.js文件中直接引入:

var config = require('./config.json');
module.exports = function() {
  var greet = document.createElement('div');
  greet.textContent = config.greetText;
  return greet;
};

7、Babel

Babel其实是一个编译JavaScript的平台,它可以编译代码帮你达到以下目的:

  • 让你能使用最新的JavaScript代码(ES6,ES7...),而不用管新标准是否被当前使用的浏览器完全支持;
  • 让你能使用基于JavaScript进行了拓展的语言,比如React的JSX;

7.1、Babel的安装与配置

Babel其实是几个模块化的包,其核心功能位于称为babel-core的npm包中,webpack可以把其不同的包整合在一起使用,对于每一个你需要的功能或拓展,你都需要安装单独的包.

用得最多的是解析Es6的babel-preset-包和解析JSX的babel-preset-react包。

下面测试使用 babel 来解析支持 es6 和 react 语法。我们先来一次性安装这些依赖包:

// npm一次性安装多个依赖模块,模块之间用空格隔开
npm install --save-dev babel-core babel-loader babel-preset-env babel-preset-react

webpack中配置Babel的方法如下:

module.exports = {
    entry: __dirname + "/app/main.js",//已多次提及的唯一入口文件
    output: {
        path: __dirname + "/public",//打包后的文件存放的地方
        filename: "bundle.js"//打包后输出文件的文件名
    },
    devtool: 'eval-source-map',
    devServer: {
        contentBase: "./public",//本地服务器所加载的页面所在的目录
        historyApiFallback: true,//不跳转
        inline: true//实时刷新
    },
    module: {
        rules: [
            {
                test: /(.jsx|.js)$/,
                use: {
                    loader: "babel-loader",
                    options: {
                        presets: [
                            "env", "react"
                        ]
                    }
                },
                exclude: /node_modules/
            }
        ]
    }
};

配置完以上选项后就能支持ES6以及JSX的语法了。测试:

npm install --save react react-dom
//Greeter,js
import React, {Component} from 'react'
import config from './config.json';

class Greeter extends Component{
  render() {
    return (
      <div>
        {config.greetText}
      </div>
    );
  }
}

export default Greeter
// main.js
import React from 'react';
import {render} from 'react-dom';
import Greeter from './Greeter';

render(<Greeter />, document.getElementById('root'));

直接编译或者在本地服务器上就能看到运行结果

7.2、.babelrc 文件

Babel其实可以完全在 webpack.config.js 中进行配置,但是考虑到babel具有非常多的配置选项,在单一的webpack.config.js文件中进行配置往往使得这个文件显得太复杂,因此一些开发者支持把babel的配置选项放在一个单独的名为 ".babelrc" 的配置文件中。webpack会自动调用.babelrc里的babel配置选项

module.exports = {
    entry: __dirname + "/app/main.js",//已多次提及的唯一入口文件
    output: {
        path: __dirname + "/public",//打包后的文件存放的地方
        filename: "bundle.js"//打包后输出文件的文件名
    },
    devtool: 'eval-source-map',
    devServer: {
        contentBase: "./public",//本地服务器所加载的页面所在的目录
        historyApiFallback: true,//不跳转
        inline: true//实时刷新
    },
    module: {
        rules: [
            {
                test: /(.jsx|.js)$/,
                use: {
                    loader: "babel-loader"
                },
                exclude: /node_modules/
            }
        ]
    }
};
//.babelrc
{
  "presets": ["react", "env"]
}

8、CSS module(实现css模块化)

CSS modules意在把JS的模块化思想带入CSS中来,通过CSS模块,所有的类名,id 名默认都只作用于当前模块。

Webpack对CSS模块化提供了非常好的支持,只需要在CSS loader中进行简单配置即可,然后就可以直接把CSS的类名传递到组件的代码中,这样做有效避免了全局污染。具体的代码如下

module.exports = {
    ...
    module: {
        rules: [
            {
                test: /(.jsx|.js)$/,
                use: {
                    loader: "babel-loader"
                },
                exclude: /node_modules/
            },
            {
                test: /.css$/,
                use: [
                    {
                        loader: "style-loader"
                    }, {
                        loader: "css-loader",
                        options: {
                            modules: true, // 指定启用css modules
                            localIdentName: '[name]__[local]--[hash:base64:5]' // 指定css的类名格式
                        }
                    }
                ]
            }
        ]
    }
};

由此通过 import 引入的css 文件只会作用于该模块中,而且只有通过先引入再使用才能起作用,相当于把 css 文件看成了模块。比如:

创建一个Greeter.css文件来进行一下测试

/* Greeter.css */
.root {
  background-color: #eee;
  padding: 10px;
  border: 3px solid #ccc;
}
import React, {Component} from 'react';
import config from './config.json';
import styles from './Greeter.css';//导入一个 Greeter.css 样式,但是不会自动起作用,更不会影响其他 JS 模块,只有自动引入再使用才能起作用。

class Greeter extends Component{
  render() {
    return (
      <div className={styles.root}> //使用cssModule添加类名的方法,这里就是使用引入的 css 文件
        {config.greetText}
      </div>
    );
  }
}
export default Greeter

 如果没有使用 CSS Module 的话,在模块中引入 .css 文件,那么该 .css 文件将作用于全部模块而不仅仅是引入的模块(相当于直接在html文件的style标签中引入了样式,而且类名没有更改)

8.1、CSS预处理器

Sass 和 Less 之类的扩展语言是对原生CSS的拓展,它们允许你使用类似于variablesnestingmixinsinheritance等不存在于CSS中的特性来写CSS,CSS预处理器可以把这些特殊类型的语句转化为浏览器可识别的CSS语句。

以下是常用的CSS 处理loaders:

  • Less Loader
  • Sass Loader
  • Stylus Loader

8.2、css处理平台PostCSS(使css代码兼容各种浏览器)

使用PostCSS来为CSS代码自动添加适应不同浏览器的CSS前缀。首先安装postcss-loader 和 autoprefixer(自动添加前缀的插件)

npm install --save-dev postcss-loader autoprefixer

接下来,在webpack配置文件中添加postcss-loader,在根目录新建postcss.config.js,并添加如下代码之后,重新使用npm start打包时,你写的css会自动根据Can i use里的数据添加不同前缀了。

//webpack.config.js
module.exports = {
    ...
    module: {
        rules: [
            {
                test: /(.jsx|.js)$/,
                use: {
                    loader: "babel-loader"
                },
                exclude: /node_modules/
            },
            {
                test: /.css$/,
                use: [
                    {
                        loader: "style-loader"
                    }, {
                        loader: "css-loader",
                        options: {
                            modules: true
                        }
                    }, {
                        loader: "postcss-loader"
                    }
                ]
            }
        ]
    }
}
// postcss.config.js
module.exports = {
    plugins: [
        require('autoprefixer')
    ]
}

9、插件(Plugins)

插件是用来拓展Webpack功能的,它们会在整个构建过程中生效,执行相关的任务。loaders是在打包构建过程中用来处理源文件的(JSX,Scss,Less..),一次处理一个,插件并不直接操作单个文件,它直接对整个构建过程其作用。

要使用某个插件,我们需要通过npm安装它,然后要做的就是在webpack配置中的plugins关键字部分添加该插件的一个实例(plugins是一个数组)

9.1、BannerPlugin插件(添加版权说明)

下面我们添加了一个给打包后代码添加版权声明的插件。该插件是webpack中的内置插件不用安装。

const webpack = require('webpack');

module.exports = {
...
    module: {
        rules: [
            {
                test: /(.jsx|.js)$/,
                use: {
                    loader: "babel-loader"
                },
                exclude: /node_modules/
            },
            {
                test: /.css$/,
                use: [
                    {
                        loader: "style-loader"
                    }, {
                        loader: "css-loader",
                        options: {
                            modules: true
                        }
                    }, {
                        loader: "postcss-loader"
                    }
                ]
            }
        ]
    },
    plugins: [
        new webpack.BannerPlugin('wenxuehai版权所有,翻版必究')
    ],
};

9.2、HtmlWebpackPlugin插件

这个插件的作用是依据一个简单的index.html模板,生成一个自动引用你所有打包后的JS文件的新index.html。这在每次生成的js文件名称不同时非常有用(比如如果添加了hash值,每次生成的JS文件名称都不一样,如果不使用该模板,那么每次编译都需要修改html文件中引入的JS文件名称)。

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

这个插件将会自动生成一个新的 HTML 文件并自动引入生成的所有的 JS 文件。示例:

在app目录下,创建一个index.tmpl.html文件模板,这个模板包含title等必须元素,在编译过程中,插件会依据此模板生成最终的html页面,会自动添加所依赖的 css, js,favicon等文件,index.tmpl.html中的模板源代码如下:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Webpack Sample Project</title>
  </head>
  <body>
    <div id='root'>
    </div>
  </body>
</html>

更新webpack的配置文件,新建一个build文件夹用来存放最终的输出文件,使用插件

const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    entry: __dirname + "/app/main.js",//已多次提及的唯一入口文件
    output: {
        path: __dirname + "/build",
        filename: "bundle.js"
    },
    devtool: 'eval-source-map',
    devServer: {
        contentBase: "./public",//本地服务器所加载的页面所在的目录
        historyApiFallback: true,//不跳转
        inline: true//实时刷新
    },
    module: {
        rules: [
            {
                test: /(.jsx|.js)$/,
                use: {
                    loader: "babel-loader"
                },
                exclude: /node_modules/
            },
            {
                test: /.css$/,
                use: [
                    {
                        loader: "style-loader"
                    }, {
                        loader: "css-loader",
                        options: {
                            modules: true
                        }
                    }, {
                        loader: "postcss-loader"
                    }
                ]
            }
        ]
    },
    plugins: [new HtmlWebpackPlugin({
            template: __dirname + "/app/index.tmpl.html"//new 一个这个插件的实例,并传入相关的参数
        })
    ],
};

再次执行npm start会发现在build文件夹下面生成了bundle.jsindex.html。生成的新的 HTML 文件将自动引入所有的资源。

9.3、Hot Module Replacement 插件(热加载)

Hot Module Replacement(HMR)是webpack里很有用的一个插件,它允许你在修改组件代码后,自动刷新实时预览修改后的效果。热加载和webpack-dev-server不同,热替换在应用运行时,无需刷新页面,便能查看代码更新后的效果 ,就跟直接在浏览器上修改dom样式一样,而webpack-dev-server是要刷新页面的。

(1)在webpack配置文件中添加HMR插件;

(2)在Webpack Dev Server中添加“hot”参数;

不过配置完这些后,JS模块其实还是不能自动热加载的,还需要在你的JS模块中执行一个Webpack提供的API才能实现热加载

9.3、react实现热加载

React模块可以使用Babel实现功能热加载。Babel有一个叫做react-transform-hrm的插件,可以在不对React模块进行额外的配置的前提下让HMR正常工作;

安装react-transform-hmr

npm install --save-dev babel-plugin-react-transform react-transform-hmr
const webpack = require('webpack');

module.exports = {
    entry: __dirname + "/app/main.js",//已多次提及的唯一入口文件
    output: {
        path: __dirname + "/public",
        filename: "bundle.js"
    },
    devtool: 'eval-source-map',
    devServer: {
        contentBase: "./public",//本地服务器所加载的页面所在的目录
        historyApiFallback: true,//不跳转
        inline: true,
        hot: true
    },
    module: {
        rules: [
            {
                test: /(.jsx|.js)$/,
                use: {
                    loader: "babel-loader"
                },
                exclude: /node_modules/
            },
            {
                test: /.css$/,
                use: [
                    {
                        loader: "style-loader"
                    }, {
                        loader: "css-loader",
                        options: {
                            modules: true
                        }
                    }, {
                        loader: "postcss-loader"
                    }
                ]
            }
        ]
    },
    plugins: [
        new webpack.BannerPlugin('版权所有,翻版必究'),
     new webpack.HotModuleReplacementPlugin()  //热加载插件 ], };

配置Babel

// .babelrc
{
  "presets": ["react", "env"],
  "env": {
    "development": {
    "plugins": [["react-transform", {
       "transforms": [{
         "transform": "react-transform-hmr",         
         "imports": ["react"],       
         "locals": ["module"]
       }]
     }]]
    }
  }
}
//Greeter,js
import React, {
  Component
} from 'react'
import styles from './main.css'

class Greeter extends Component {
  render() {
    return ( 
        < div>
          <h1>
            aaaf
          </h1>
        </div>
    );
  }
}
export default Greeter
//main.js
import React from 'react';
import {
  render
} from 'react-dom';
import Greeter from './greeter.js';

render( < Greeter / > , document.getElementById('root'));

现在如果我们就可以实现热加载模块了,每次保存就能在浏览器上直接看到更新内容,浏览器不必刷新也不会自动刷新。

(有时候没有效果可能是版本问题)

10、缓存问题(hash)

当我们使用 webpack 来打包代码,webpack 会生成一个可部署的文件夹(比如:/dist),然后把打包后的内容放置在此目录中。只要把 /dist 目录中的内容部署到服务器上,客户端(通常是浏览器)就能够访问网站此服务器的网站及其资源。当浏览器会使用缓存技术来将文件缓存。当我们在部署新版本时如果不更改资源的文件名,浏览器可能会认为它没有被更新,就会使用它的缓存版本。由于缓存的存在,浏览器可能无法获取到我们部署的新版本。

通过使用 [hash] 来使得每次更改代码之后也能更改掉文件名,可以确保浏览器获取到修改后的文件。[hash] 替换可以用于在文件名中包含一个构建相关的 hash,但是更好的方式是使用 [chunkhash] 替换,在文件名中包含一个 chunk 相关的哈希。

const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');

module.exports = {
..
    output: {
        path: __dirname + "/build",
        filename: "[name].hash].js"
    },
   ...
};

[chunkhash] 跟热替换模块功能的插件 HotModuleReplacementPlugin 有冲突,要想使用 [chunkhash] 必须把热替换注释掉。[chunkhash] 只建议在生产环境中使用。

[hash] 是有一个文件修改那么所有的文件的hash值都修改。

11、使用 CommonChunkPlugin 去重代码

参考:https://www.webpackjs.com/guides/code-splitting/

如果多个入口文件都共同引入了同一个模块,正常编译的话webpack会将依赖的模块编译进每个出口文件中,而 CommonsChunkPlugin 插件可以将多个入口文件共同依赖的公共模块提取到一个新生成的出口文件中(chunk)。

const path = require('path');
  const webpack = require('webpack');
  const HTMLWebpackPlugin = require('html-webpack-plugin');
  module.exports = {
    entry: {
      index: './src/index.js',
      another: './src/another-module.js'
    },
    plugins: [
      new HTMLWebpackPlugin({
        title: 'Code Splitting'
      }),
      new webpack.optimize.CommonsChunkPlugin({
        name: 'common'   //指定公共 bundle 的名称。
      })
    ],
    output: {
      filename: '[name].bundle.js',
      path: path.resolve(__dirname, 'dist')
    }
  };

如此生成的多个出口文件将减轻了大小。使用 HTMLWebpackPlugin 插件将生成的模块自动引入 HTML 文件中。

11.1、将第三库的代码提取到单独的出口文件中

代码中引入的第三方依赖库(例如 lodash 或 react、vue)它们很少像本地的源代码那样频繁修改。因此将它们提取到一个单独的出口文件中,并且用固定的一个名字命名,这样浏览器可以只用缓存的文件,减少请求。

通过指定 entry 配置中一个未用到的名称,去重插件会自动将我们指定的第三方库提取到单独的包中:

var path = require('path');
  const webpack = require('webpack');
  const CleanWebpackPlugin = require('clean-webpack-plugin');
  const HtmlWebpackPlugin = require('html-webpack-plugin');

  module.exports = {
    entry: './src/index.js',
    entry: {
      main: './src/index.js',
      vendor: [
          'lodash' 
        ] 
    },
    plugins: [
      new CleanWebpackPlugin(['dist']),
      new HtmlWebpackPlugin({
        title: 'Caching'
      }),
      new webpack.optimize.CommonsChunkPlugin({
        name: 'vendor'   //指定的第三方库即上面的 lodash 将自动提取到这里
      }),
      new webpack.optimize.CommonsChunkPlugin({
        name: 'manifest'  //其他的共用代码提取到这里
      })
    ],
    output: {
      filename: '[name].[chunkhash].js',  //这个时候用[chunkhash]才有意义
      path: path.resolve(__dirname, 'dist')
    }
  };

配合 [chunkhash] 使用,当我们修改本地代码时,由第三方库提取出的文件的文件名并不会改变,因此浏览器将会使用缓存的文件,达到了减少请求的功能。

 11.2、解决去重代码出现的问题(模板标识符)

提取出第三库的文件有可能会因为在模块中的引用顺序发生变化而导致编译的文件名也发生改变,这显然不是我们想要的,因为第三库并没有改变。

可以使用两个插件来解决这个问题。第一个插件是 NamedModulesPlugin将使用模块的路径,而不是数字标识符。虽然此插件有助于在开发过程中输出结果的可读性,然而执行时间会长一些,推荐用于开发环境。第二个选择是使用 HashedModuleIdsPlugin,推荐用于生产环境构建。

只要在webpack的配置文件中添加代码:

//只要在plugins 中引入插件:
//使用HashedModuleIdsPlugin插件
new webpack.HashedModuleIdsPlugin() 

//使用NamedModulesPlugin插件
new webpack.NamedModulesPlugin()

12、清理输出文件夹的文件(CleanWebpackPlugin)

由于之前的一些编译可能会导致用于输出的文件夹中包含了一些没必要的文件,显得比较杂乱,我们可以用clean-webpack-plugin 插件清理输出的文件夹中的文件。

该插件会在每次构建前清理输出文件夹,只会生成用到的文件。

npm install clean-webpack-plugin --save-dev
  const path = require('path');
  const CleanWebpackPlugin = require('clean-webpack-plugin');
  module.exports = {
    entry: {
      app: './src/index.js',
      print: './src/print.js'
    },
    plugins: [
      new CleanWebpackPlugin(['public/*.*'], {
        root: __dirname,
        verbose: true, //开启在控制台输出信息
        dry: false   
      })
    ],
    output: {
      filename: '[name].bundle.js',
      path: path.resolve(__dirname, 'dist')
    }
  };

 12.1、webpack-dev-server和CleanWebpackPlugin同时使用的问题

有一个问题是当热加载和CleanWebpackPlugin插件同时使用时,运行dev-sever会重新编译,由此也重新使用清理插件,目标文件夹中的文件将被全部删除,但是此时并不生成新的文件。

虽然没报什么问题,代码也可以跑,但是还是困惑了我很久。这个问题应该是运行webpack-dev-server时webpack会重新编译,由此使用了清理插件,但是dev-server并不会在工作目录中生成编译的文件,而是在内存中生成,所以看不到,但是代码跑的没问题。

要想解决这个问题,我在GitHub上看到了一个issue,但是尝试了好像会报错:https://github.com/johnagan/clean-webpack-plugin/issues/96。过后会继续关注该问题。

原文地址:https://www.cnblogs.com/wenxuehai/p/10425028.html