本文使用 Webpack 从零开始搭建一个开发环境的脚手架配置,在此作个记录,也方便之后使用。css
个人上一篇文章简单介绍了一下 Webpack 的一些核心概念和基本配置,须要了解的朋友能够先参考一下Webpack 的简单介绍html
从 webpack v4.0.0 开始,能够不用引入一个配置文件。直接使用 webpack 命令就可进行打包。可是,通常咱们须要进行更灵活的配置功能,因此本文我也建立一个 webpack 的配置文件,对webpack 的一些属性进行配置。node
本文 Demo 地址react
首先咱们建立一个目录,初始化 npm,而后 在本地安装 webpack,接着安装 webpack-cli(此工具用于在命令行中运行 webpack):webpack
$ mkdir webpack-dev-demo && cd webpack-dev-demo
$ npm init -y
$ npm install webpack webpack-cli --save-dev
复制代码
projectgit
webpack-dev-demo
|- package.json
|- /public
|- index.html
|- /src
|- index.js
复制代码
src/index.jsgithub
function component() {
var element = document.createElement('div');
element.innerHTML = 'Hello World';
return element;
}
document.body.appendChild(component());
复制代码
public/index.jsweb
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Webpack 开发环境配置</title>
</head>
<body>
</body>
</html>
复制代码
package.jsonnpm
{
"name": "webpack-dev-demo",
"version": "1.0.0",
"description": "",
"private": true,
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack --config webpack.config.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"webpack": "^4.29.0",
"webpack-cli": "^3.2.1"
}
}
复制代码
在项目根目录下建立 webpack.config.js 配置文件编程
project
webpack-dev-demo
|- package.json
|- /public
|- index.html
|- /src
|- index.js
+ |- webpack.config.js
复制代码
webpack.config.js
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: '[name]-[hash:8].js',
path: path.resolve(__dirname, 'dist')
}
}
复制代码
$ npm run build
复制代码
控制台的打印结果
能够看到打印日志,打包成功了,可是此时在浏览器打开咱们的 index.html 文件,却发现界面上什么都不显示,这个也好理解,由于 index.html 此时尚未引入任何的 js 文件。因此这个时候就要将打包后的文件引入到 index.html 文件中,可是能够看到 dist 文件夹下的 js 文件名有不少的 hash 值,并且每次编译均可能不一样,怎么办呢?这时候就要引入 html-webpack-plugin 插件了
安装插件:
$ npm install html-webpack-plugin --save-dev
复制代码
使用插件:
webpack.config.js
...
const HTMLWebpackPlugin = require('html-webpack-plugin');
module.exports = {
...
plugins: [
new HTMLWebpack({
// 用于生成的HTML文档的标题
title: 'Webpack 开发环境配置',
// webpack 生成模板的路径
template: './public/index.html'
})
]
}
复制代码
关于 html-webpack-plugin 插件更多配置请参考:插件文档。
再次运行 webpack
$ npm run build
复制代码
能够看到 dist 文件夹下生成了一个 index.html 文件,在浏览器中打开这个 index.html 文件,能够看到,'Hello World' 已经可以正常显示了
至此,项目可以正常打包了,可是还不够,此时能够看到 dist 文件夹下有两个 js 文件,可是明明只打了一个包啊。是由于另外一个包使咱们上一次操做打出来的,并无删除掉。因此,为了不 dist 文件夹中的文件变得杂乱,咱们还须要引入 clean-webpack-plugin 插件帮助咱们清理 dist 文件夹
安装插件:
$ npm install clean-webpack-plugin --save-dev
复制代码
用法:new CleanWebpackPlugin(paths [, {options}])
webpack.config.js
...
const CleanWebpackPlugin = require('clean-webpack-plugin');
module.exports = {
...
plugins: [
...,
// 用法:new CleanWebpackPlugin(paths [, {options}])
new CleanWebpackPlugin(['dist'])
]
}
复制代码
再次运行 webpack
$ npm run build
复制代码
此时 dist 文件夹下只有一个 js 和 html 文件了。说明插件配置成功了,关于 clean-webpack-plugin 更多配置请参考:插件文档。
安装 webpack-dev-server 包:
$ npm install --save-dev webpack-dev-server
复制代码
使用:
webpack.config.js
...
const webpack = require('webpack');
module.exports = {
...
devServer: {
// 必须配置的选项,服务启动的目录,默认为跟目录
contentBase: './dist',
// 使用热加载时须要设置为 true
hot: true,
/** * 下面为可选配置 */
// 指定使用一个 host。默认是 localhost
host: 'localhost',
// 端口号
port: 8000,
// 当使用 HTML5 History API 时,任意的 404 响应均可能须要被替代为 index.html。经过设置为 true 进行启用
historyApiFallback: {
disableDotRule: true
},
// 出现错误时是否在浏览器上出现遮罩层提示
overlay: true,
/** * 在 dev-server 的两种不一样模式之间切换 * 默认状况下,应用程序启用内联模式 inline * 设置为 false,使用 iframe 模式,它在通知栏下面使用 <iframe> 标签,包含了关于构建的消息 */
inline: true,
/** * 统计信息,枚举类型,可供选项: * "errors-only": 只在发生错误时输出 * "minimal": 只在发生错误或有新的编译时输出 * "none": 没有输出 * "normal": 标准输出 * "verbose": 所有输出 */
stats: "errors-only",
// 设置接口请求代理,更多 proxy 配置请参考 https://github.com/chimurai/http-proxy-middleware#options
proxy: {
'/api/': {
changeOrigin: true,
// 目标地址
target: 'http://localhost:3000',
// 重写路径
pathRewrite: {
'^/api/': '/'
}
}
}
},
plugins: [
...,
// 添加 NamedModulesPlugin,以便更容易查看要修补(patch)的依赖,因为设置了 mode: 'development',因此这个插件能够省略
// new webpack.NamedModulesPlugin(),
// 进行模块热替换
new webpack.HotModuleReplacementPlugin()
]
}
复制代码
启用热加载功能:(上面已经添加了) 一、在 devServer 配置中添加
hot: true
属性 二、在 plugins 中添加new webpack.NamedModulesPlugin()
和new webpack.HotModuleReplacementPlugin()
在 package.json 中添加一个执行命令
package.json
...
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack --config webpack.config.js",
+ "start": "webpack-dev-server"
}
...
复制代码
启动 Http 服务
执行命令:
$ npm run start
复制代码
能够看到控制台打印输出:
打开浏览器,输入:http://localhost:8000/
,能够看到浏览器中能够正常显示 Hello World。
webpack 配置中有一个 mode 属性的配置,三个可选属性:
production 会将 process.env.NODE_ENV 的值设为 production。启用 FlagDependencyUsagePlugin, FlagIncludedChunksPlugin, ModuleConcatenationPlugin, NoEmitOnErrorsPlugin, OccurrenceOrderPlugin, SideEffectsFlagPlugin 和 UglifyJsPlugin。
development 会将 process.env.NODE_ENV 的值设为 development。启用 NamedChunksPlugin 和 NamedModulesPlugin。
none 不选用任何默认优化选项
这里咱们配置的是开发环境,因此须要将 mode 设置为 development
webpack.config.js
...
module.exports = {
+ mode: 'development',
...
}
复制代码
此时项目可以正常运行,因此没有什么问题,可是如今咱们修改一下,将 index.js 中的 return element
改为错误的 return ele
。咱们 F12 打开开发工具,能够看到控制台的错误提示,点进去却发现跟咱们写的代码不一致,难以对错误的代码进行调试,此时 Source Map 就派上用场了。
在 webpack.config.js 中添加 devtool 属性
webpack.config.js
...
module.exports = {
mode: 'development',
+ devtool: inline-source-map
...
}
复制代码
devtool 的多个属性之间的差别
devtool | 构建速度 | 从新构建速度 | 生产环境 | 品质(quality) |
---|---|---|---|---|
(none) | +++ | +++ | yes | 打包后的代码 |
eval | +++ | +++ | no | 生成后的代码 |
cheap-eval-source-map | + | ++ | no | 转换过的代码(仅限行) |
cheap-module-eval-source-map | o | ++ | no | 原始源代码(仅限行) |
eval-source-map | -- | + | no | 原始源代码 |
cheap-source-map | + | o | yes | 转换过的代码(仅限行) |
cheap-module-source-map | o | - | yes | 原始源代码(仅限行) |
inline-cheap-source-map | + | o | no | 转换过的代码(仅限行) |
inline-cheap-module-source-map | o | - | no | 原始源代码(仅限行) |
source-map | -- | -- | yes | 原始源代码 |
inline-source-map | -- | -- | no | 原始源代码 |
hidden-source-map | -- | -- | yes | 原始源代码 |
nosources-source-map | -- | -- | yes | 无源代码内容 |
再次运行项目:
$ npm run start
复制代码
能够看到报错依旧,可是在开发工具的控制台上,查看错误提示,能够根据提示清楚的找到咱们写的代码所在位置.
测试以后请将错误的
return ele
改成正确的return element
此时,开发环境已经配置的差很少了,可是我如今想给 div 加一个样式,想让文字编程蓝色,居中显示,那么此时就须要用的 loader 了,由于 webpack 默认没法解析 css,因此就须要咱们本身配置了
安装所需插件:
$ npm install css-loader style-loader --save-dev
复制代码
css-loader 用来解析 css 文件,而 style-loader 则用来将解析好的 css 内容注入到 JavaScript 里。因为 loader 执行顺序是从下到上,因此要将 css-loader 写在下面。
使用:
webpack.config.js
...
module.exports = {
...
plugins: [...],
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
// 还能够给 loader 添加一些配置
{
loader: 'css-loader',
options: {
// 开启 sourceMop
sourceMap: true
}
}
]
}
]
}
}
复制代码
在 src 目录下新建一个 css 文件
src/index.css:
div {
color: blue;
text-align: center;
}
复制代码
src/index.js
require('./index.css');
...
复制代码
从新运行项目:
$ npm run start
复制代码
能够看到浏览器上此时文字颜色已经变蓝,而且居中显示。
除了 css 以外,其余文件在 webpack 也都被认为是一个模块,也都须要对应的 loader 进行解析。 下面就不一一演示了,先把代码贴出来看一看。
下载全部须要的插件:
$ npm install file-loader csv-loader xml-loader html-loader markdown-loader --save-dev
复制代码
webpack.config.js
...
module.exports = {
...
plugins: [...],
module: {
rules: [
...
// 解析图片资源
{
test: /\.(png|svg|jpg|gif)$/,
use: [
'file-loader'
]
},
// 解析 字体
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [
'file-loader'
]
},
// 解析数据资源
{
test: /\.(csv|tsv)$/,
use: [
'csv-loader'
]
},
// 解析数据资源
{
test: /\.xml$/,
use: [
'xml-loader'
]
},
// 解析 MakeDown 文件
{
test: /\.md$/,
use: [
"html-loader",
"markdown-loader"
]
}
]
}
}
复制代码
这些配置基本上能够知足常规开发中使用到的各类模块资源,不过在开发过程当中可能还会须要用到 less、scss 等 css 预编译语言,还须要使用 less-loader,sass-loader 进行配置。更多配置用法这里也没法一一详述,等你们用到的时候再去查阅对应文档便可。
目前项目能够正常运行,可是如今 ES六、7 语法已经出来了,可是浏览器中还不能彻底识别,因此咱们须要 babel 讲 js 文件转换成浏览器能够识别的 ES5 语法。
安装 bable-loader 插件
$ npm install babel-core babel-loader --save-dev
复制代码
babel 还能进行配置,能够像上面那样直接在 loader 中进行配置,也能够在根目录下建立 .babelrc 文件配置,项目运行会自动今后文件中读取
使用 babel-loader:
webpack.config.js
...
module.exports = {
...
plugins: [...],
module: {
rules: [
{
test: /\.js/,
include: path.resolve(__dirname, 'src'),
loader: 'babel-loader?cacheDirectory',
},
]
}
}
复制代码
默认值为 false。当有设置时,指定的目录将用来缓存 loader 的执行结果。以后的 webpack 构建,将会尝试读取缓存,来避免在每次执行时,可能产生的、高性能消耗的 Babel 从新编译过程。若是设置了一个空值 (loader: 'babel-loader?cacheDirectory'
) 或者 true (loader: babel-loader?cacheDirectory=true)
,loader 将使用默认的缓存目录 node_modules/.cache/babel-loader
,若是在任何根目录下都没有找到 node_modules
目录,将会降级回退到操做系统默认的临时文件目录。
使用 cacheDirectory 选项,将 babel-loader 提速至少两倍。
presets 属性告诉 Babel 要转换的源码使用了哪些新的语法特性,一个 Presets 对一组新语法特性提供支持,多个 Presets 能够叠加。 Presets 实际上是一组 Plugins 的集合,每个 Plugin 完成一个新语法的转换工做。Presets 是按照 ECMAScript 草案来组织的,一般能够分为如下三大类:
已经被写入 ECMAScript 标准里的特性,因为以前每一年都有新特性被加入到标准里,因此又可细分为:
被社区提出来的但还未被写入 ECMAScript 标准里特性,这其中又分为如下四种:
为了支持一些特定应用场景下的语法,和 ECMAScript 标准没有关系,例如 babel-preset-react 是为了支持 React 开发中的 JSX 语法。
plugins 属性告诉 Babel 要使用哪些插件,插件能够控制如何转换代码。
安装项目中须要使用的 Presets 插件
$ npm install babel-preset-env babel-preset-stage-0 --save-dev
复制代码
安装项目中须要的 babel Plugin
$ npm install babel-plugin-transform-class-properties babel-plugin-transform-runtime babel-runtime --save-dev
复制代码
babel-plugin-transform-class-properties 能够在项目中使用新增的 class 属性用法
babel-plugin-transform-runtime 因为 babel 转换文件时会在每一个文件中都写入辅助代码,使用此插件能够直接使用 babel-runtime 中的代码进行转换,避免代码冗余。因此 babel-plugin-transform-runtime 和 babel-runtime 成对使用
.babelrc
{
"presets": ["env", "stage-0"],
"plugins": [
"transform-runtime",
"transform-class-properties"
]
}
复制代码
配置完成,此时就能够在项目中自由的使用 ES6 等新增 js 语法了。
有时候项目提示错误,多是编译错误,多是 ESLint 提示错误等等,咱们但愿错误提示可以友好一些,就可使用这个插件
插件安装:
npm install friendly-errors-webpack-plugin --save-dev
复制代码
使用:
webpack.config.js
...
const FriendlyErrorsWebpackPlugin = require('friendly-errors-webpack-plugin');
module.exports = {
...,
plugins: [
...
new FriendlyErrorsWebpackPlugin()
]
}
复制代码
更多配置请参考插件文档
开发的时候咱们常常会须要引入本身写的文件模块,可能会须要按照路径一级一级的找,这个时候咱们就能够配置 resolve,为一些经常使用的路径设置别名
配置:
webpack.config.js
...
module.exports = {
...
plugins: [...],
modules: {...},
resolve: {
alias: {
src: path.resolve(__dirname, 'src')
}
}
}
复制代码
使用: 不管在任何文件里,引入 src 目录下的 index.css 文件时,路径均可以按照下面的这个引入路径来写
index.js
- require('./index.css');
+ import 'src/index.css';
复制代码
从新运行项目,发现项目正常启动,index.css 中的样式也正常生效
至此,一个简单的开发环境的 Webpack 脚手架搭建完成了。
project
webpack-dev-demo
|- package.json
|- /public
|- index.html
|- /src
|- index.js
|- index.css
复制代码
public/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Webpack 开发环境配置</title>
</head>
<body>
</body>
</html>
复制代码
src/index.js
import 'src/index.css';
function component() {
var element = document.createElement('div');
element.innerHTML = 'Hello World';
return element;
}
document.body.appendChild(component());
复制代码
src/index.css
div {
color: blue;
text-align: center;
}
复制代码
.babelrc
{
"presets": ["env", "stage-0"],
"plugins": [
"transform-runtime",
"transform-class-properties"
]
}
复制代码
webpack.config.js
const path = require('path');
const HTMLWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const webpack = require('webpack');
module.exports = {
mode: 'development',
devtool: 'inline-source-map',
entry: './src/index.js',
output: {
filename: '[name]-[hash:8].js',
path: path.resolve(__dirname, 'dist')
},
devServer: {
// 必须配置的选项,服务启动的目录,默认为跟目录
contentBase: './dist',
// 使用热加载时须要设置为 true
hot: true,
/** * 下面为可选配置 */
// 指定使用一个 host。默认是 localhost
host: 'localhost',
// 端口号
port: 8000,
// 当使用 HTML5 History API 时,任意的 404 响应均可能须要被替代为 index.html。经过设置为 true 进行启用
historyApiFallback: {
disableDotRule: true
},
// 出现错误时是否在浏览器上出现遮罩层提示
overlay: true,
/** * 在 dev-server 的两种不一样模式之间切换 * 默认状况下,应用程序启用内联模式 inline * 设置为 false,使用 iframe 模式,它在通知栏下面使用 <iframe> 标签,包含了关于构建的消息 */
inline: true,
/** * 统计信息,枚举类型,可供选项: * "errors-only": 只在发生错误时输出 * "minimal": 只在发生错误或有新的编译时输出 * "none": 没有输出 * "normal": 标准输出 * "verbose": 所有输出 */
stats: "errors-only",
// 设置接口请求代理,更多 proxy 配置请参考 https://github.com/chimurai/http-proxy-middleware#options
proxy: {
'/api/': {
changeOrigin: true,
// 目标地址
target: 'http://localhost:3000',
// 重写路径
pathRewrite: {
'^/api/': '/'
}
}
}
},
plugins: [
new HTMLWebpackPlugin({
// 用于生成的HTML文档的标题
title: 'Webpack 开发环境配置',
// webpack 生成模板的路径
template: './public/index.html'
}),
// 用法:new CleanWebpackPlugin(paths [, {options}])
new CleanWebpackPlugin(['dist']),
// 添加 NamedModulesPlugin,以便更容易查看要修补(patch)的依赖,因为设置了 mode: 'development',因此这个插件能够省略
// new webpack.NamedModulesPlugin(),
// 进行模块热替换
new webpack.HotModuleReplacementPlugin()
],
module: {
rules: [
{
test: /\.js/,
include: path.resolve(__dirname, 'src'),
loader: 'babel-loader?cacheDirectory'
},
// 解析 css
{
test: /\.css$/,
include: path.resolve(__dirname, 'src'),
use: [
'style-loader',
// 还能够给 loader 添加一些配置
{
loader: 'css-loader',
options: {
// 开启 sourceMop
sourceMap: true
}
}
]
},
// 解析图片资源
{
test: /\.(png|svg|jpg|gif)$/,
use: [
'file-loader'
]
},
// 解析 字体
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [
'file-loader'
]
},
// 解析数据资源
{
test: /\.(csv|tsv)$/,
use: [
'csv-loader'
]
},
// 解析数据资源
{
test: /\.xml$/,
use: [
'xml-loader'
]
},
// 解析 MakeDown 文件
{
test: /\.md$/,
use: [
"html-loader",
"markdown-loader"
]
}
]
},
resolve: {
alias: {
src: path.resolve(__dirname, 'src')
}
}
}
复制代码
package.json
{
"name": "webpack-dev-demo",
"version": "1.0.0",
"description": "",
"private": true,
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack --config webpack.config.js",
"start": "webpack-dev-server"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"babel-core": "^6.26.3",
"babel-loader": "^7.1.5",
"babel-plugin-transform-class-properties": "^6.24.1",
"babel-plugin-transform-runtime": "^6.23.0",
"babel-preset-env": "^1.7.0",
"babel-preset-stage-0": "^6.24.1",
"babel-runtime": "^6.26.0",
"clean-webpack-plugin": "^1.0.1",
"css-loader": "^2.1.0",
"html-webpack-plugin": "^3.2.0",
"style-loader": "^0.23.1",
"webpack": "^4.29.0",
"webpack-cli": "^3.2.1",
"webpack-dev-server": "^3.1.14"
}
}
复制代码
本文篇幅较长,感谢各位的耐心阅读。本文主要从 入口、输出、插件(Plugins)、模块处理(Module)、loader、解析(resolve)等 6 个配置项着手配置了一个基本的 webpack 开发环境脚手架。本文主要讲解的内容为:
loader 的做用以及如何配置使用
babel 的做用以及配置项
各个插件的功能以及适用场景
解析可以为开发带来的效率
本文内容对于已经熟练掌握 Webpack 的朋友来讲,可能有些浅薄,可是着重讲解了各个配置项的功能以及配置后对项目产生的效果。对于准备入门 webpack 的朋友应该会有必定的帮助。