热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

webpack4实用配置指南上手篇

零、前言算起来已经有3到4个项目的webpack构建打包经历。然而每次搭建起来还是有新手既视感,比较捉急。工具用法的东西,好记性不如烂笔头,有必要系统梳理下webpack的配置

零、前言

算起来已经有3到4个项目的webpack构建打包经历。然而每次搭建起来还是有新手既视感,比较捉急。工具用法的东西,好记性不如烂笔头,有必要系统梳理下webpack的配置常用配置以及构建优化。

分为上手篇和优化篇,本篇为上手篇,先介绍常用配置。

篇幅较长,可完整阅读,也可在遇到问题时即查即用。

此次采用webpack4,也顺便尝尝鲜。

# webpack4 把命令行工具抽离成了独立包 webpack-cli
npm install webpack webpack-cli -D

一、了解下webpack4的零配置

项目下没有webpack.config.js情况下,命令行直接运行webpack,webpack4不再像webpack3一样,提示未找到配置文件:

而是提示:

修改后可以发现零配置下系统的默认配置为:

  1. 入口路径为:/src/index.js,打包输出路径:/dist/main.js
  2. 未传--mode参数时,默认是-mode production,会进行压缩混淆。传入--mode development指定为开发环境打包。

├── dist
│ └── main.js
├── node_modules
├── package-lock.json
├── package.json
└── src
├── index.js
└── utils.js

体验了一把,所谓的零配置的灵活度极低,目测没啥实际用处,了解一下罢了。所以现阶段还是老老实实地学怎么配置吧。

二、webpack cli执行

如果命令行直接webpack会运行全局安装的webpack,想运行当前目录下的webpack,可以采取以下方法,不嫌麻烦当然也可以每次都./node_modules/.bin/webpack

npx webpack

npx是npm 5.2.0及以上内置的包执行器,npx webpack --mode development会直接找项目的/node_modules/.bin/里面的命令执行,方便快捷。

npm run build

使用npm脚本,配置好之后直接npm run xxx

// package.json
"scripts": {
"build": "webpack --mode development"
},

三、配置结构

// webpack.config.js
module.exports = {
mode: 'development', // development|production
entry: '', // 入口配置
output: {}, // 输出配置
module: {}, // 放置loader加载器,webpack本身只能打包commonjs规范的js文件,用于处理其他文件或语法
plugins: [], // 插件,扩展功能
// 以下内容进阶篇再涉及
resolve: {}, // 为引入的模块起别名
devServer: {} // webpack-dev-server
};

各个配置项的用法和细节将结合具体的功能实现来讲。

官方配置文档

四、基本的项目脚手架功能

1. 多入口配置

一个入口文件对应输出一个出口文件,因为太简单,不再赘述。这里讲下多对一、多对多。

这里涉及到webpack配置中的灵魂成员:entryoutput

(1) 多进一出

entry传入数组相当于将数组内所有文件都打包到bundle.js中。

const path = require('path');
module.exports = {
entry: ['./src/index.js', './src/index2.js'], // 入口文件
output: {
filename: 'bundle.js', // 打包输出文件名
path: path.join(__dirname, './dist') // 打包输出路径(必须绝对路径,否则报错)
}
};

(2) 多进多出

  1. entry传入对象,key称之为chunk,将不同入口文件分别打包到不同的js。
  2. output.filename改为用中括号占位来命名,从而生成多个文件,name是entry中各个chunk,具体可参考官方文档

const path = require('path');
module.exports = {
entry: { // 入口文件,传入对象,定义不同的chunk(如app, utils)
app: './src/index.js',
utils: './src/utils.js'
},
output: {
// filename: 'bundle.js', // 此时因为有多个chunk,因此不能只定义一个输出文件,否则报错
filename: '[name].[hash].js',
path: path.join(__dirname, './dist')
}
};

PS. 在output中,还有一个叫publicPath非常重要,设置不正确会导致生成错的引用路径,从而找不到资源。这里先不展开,后面结合图片处理再细述。

2. 清空某目录或子目录及文件

这里先插入一个实用功能,因为在每次打包后,dist目录都有无用文件残留,最好每次打包前都清空dist目录。

npm install -D clean-webpack-plugin

// webpack.config.js
const CleanWebpackPlugin = require('clean-webpack-plugin');
const cdnDir = path.resolve(__dirname, '../../../', 'imgcache.gtimg.cn/vip/')
module.exports = {
...
plugins: [
new CleanWebpackPlugin(['dist']), // 清空项目根目录下dist
new CleanWebpackPlugin(['dist/images', 'dist/style']),
new CleanWebpackPlugin(['dist'], {
root: cdnDir // 指定根目录 清空cdn目录下的dist
}),
]
};

3. html自动构建

回到正题,通过上面的配置,js已实现正确的进出关系,那该怎么引用呢,难道需要手动引入吗?下面看下怎样配置实现将html文件进行自动构建。这里需要借助插件。

npm install html-webpack-plugin -D

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
...
plugins: [
new HtmlWebpackPlugin({
filename: path.join(__dirname, 'entry.html'), // 生成的html(绝对路径:可用于生成到根目录)
filename: 'html/entry.html', // 生成的html文件名(相对路径:将生成到output.path指定的dist目录下)
template: './src/index.html' // 以哪个文件作为模板,不指定的话用默认的空模板
})
]
};

在上面1的配置基础上加上plugins,就可以将打包文件自动注入到entry.html中,而且资源的引用路径都是正确的。

多页面场景

需要构建多个页面,每个页面分别引用不同入口,怎么破?

module.exports = {
...
plugins: [
new HtmlWebpackPlugin({
filename: 'html/page1.html',
template: './src/index.html',
chunks: ['utils', 'app']
}),
new HtmlWebpackPlugin({
filename: 'html/page2.html',
template: './src/index2.html',
chunks: ['app'],
minify: {
removeComments: true // 删除注释
},
hash: true // 加hash
})
]
};

要几个页面就new几个,通过chunks传入需要引用的入口。

其他功能

从上面的配置可以看到,除了自动引用之外,html-webpack-plugin还提供了压缩、url后加hash等实用功能。具体参考配置文档。

4. CSS处理——内联

有了JS和HTML,我们看看CSS该怎样自动内联进页面。

因为webpack原生具有了模块打包的能力,因此我们可以直接用commonjs规范,无需其他插件。而如果我们在js中直接require或者import了一个css文件,此时肯定是需要额外步骤告诉webpack该怎样处理。这里涉及到webpack另一个配置项:module及相关的loader

下面以处理css以及less为例:

  • less:先编译成css,再把css内联进页面。
  • css:内联进页面

loader

处理less和css等非js资源,需要安装相对应的loader

npm install -D css-loader # 负责处理其中的@import和url()
npm install -D style-loader # 负责内联
npm install -D less less-loader # less编译,处理less文件

module配置

我觉得module配置是webpack里面最繁琐的一块,光是配置loader就有三种不同的写法。下面只列出loader配置项,具体其他的module配置项可参见官方文档。

记住module的配置的其中一种套路:

  • module.rules[i].test:命中规则
  • module.rules[i].use:传入数组(loader名或对象数组),从右到左执行
  • module.rules[i].loader :传入字符串,这是module.rules[i].use: [ { loader } ]的简写。

// index.js
import './style/index.css';
import './style/test.less';
// webpack.config.js
module.exports = {
...
module: {
rules: [
{
test: /\.css$/,
// 从右到左,loader安装后无需引入可直接使用
use: ['style-loader', 'css-loader']
},
{
test: /\.less$/,
use: [
{loader: 'style-loader'},
{loader: 'css-loader'},
{loader: 'less-loader'}
]
}
]
}
};

最终以style的形式内联进页面

5. CSS处理——合并抽离

样式少可以内联,多了还是得抽离。而抽离文件已超过了loader的范围,需要借助plugins来完成:extract-text-webpack-plugin

BTW: 有了之前的html自动构建配置,抽离后的CSS也会自动引入

# @next为webpack4使用版本
npm install -D extract-text-webpack-plugin@next

抽离套路:

  1. 实例化ExtractTextPlugin
    • 每个实例抽离成文件时是以entry为单位,所以一个入口文件(entry)只能抽出一个文件,多entry时在设置filename需要注意写法。
    • 可多次实例化,分别抽离CSS、LESS,下同
  2. 将实例放入到plugins
  3. 在css对应的module.rules.use调用extract方法。

const ExtractTextPlugin = require('extract-text-webpack-plugin');
// 实例化1:用于CSS
const extractCSS = new ExtractTextPlugin({
disable: process.env.NODE_ENV == 'development' ? true : false, // 开发环境下直接内联,不抽离
filename: 'style/extractFromCss.css', // 单个entry时,可写死
filename: 'style/[name].css', // 多entry时
});
// 实例化2:用于LESS
const extractLESS = new ExtractTextPlugin({省略...});
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: extractCSS.extract({
fallback: 'style-loader',
use: 'css-loader'
})
},
{
test: /\.less$/,
use: extractLESS.extract({
fallback: 'style-loader',
use: ['css-loader', 'less-loader']
})
}
]
},
plugins: [extractCSS, extractLESS]
};

关于extract方法

这里将use的值改成extract之后,感觉怎么和上面说的套路又不一样了。

莫慌,其实它只是个语法糖,从返回值就知道,还是返回loader对象数组。

console.log(extractCSS.extract({
fallback: 'style-loader',
use: ['css-loader']
}));

此外,它可以作为实例方法也可以作为静态方法调用,见源码。

// 等价
extractCSS.extract()
ExtractTextPlugin.extract()

关于options.fallback

顾名思义就是不被抽离时的降级处理。什么时候降级呢?

  1. 实例时传入了disable: true
  2. code splitting异步打包的文件内如果有引用样式,默认情况下这些样式不会被抽离,此时被降级。参考博文

如果异步文件也想抽离样式怎么办?用allChunks

const extractCSS = new ExtractTextPlugin({
disable: false,
filename: 'style/[name].css',
allChunks: true //设置为true
});

6. 图片(字体/svg)处理

好了轮到图片、字体这些资源了。我们希望做到:

  1. 图片能正确被webpack打包,小于一定大小的图片直接base64内联。
  2. 打包之后各个入口(css/js/html)还能正常访问到图片,图片引用路径不乱。

字体和svg等资源同理,以下以图片为例。

(1) 安装依赖

npm install -D url-loader file-loader

url-loader: 小于limit值时,直接base64内联,大于limit就干脆不管了,直接扔给file-loader处理,不装直接报错,之前还以为会自动调用,所以这两者都得装上。

(2) 不同入口(css/js/html)引用图片

html

html文件是通过html-wepback-plugin生成的,如果希望webpack能够正确处理打包之后图片的引用路径,需要在模板文件中这样引用图片。





css/js

/* 图片作为背景图 */
#main {
background: url("../images/cjl.jpg") #999;
color: #fff
}

// app.js
import sett1 from './images/sett1.png';
const img = document.createElement('img');
img.src = sett1;
document.body.appendChild(img);

(3) 配置

// webpack.config.js
module.exports = {
...
modules: {
rules: [
{
test: /\.(png|jpe?g|gif)$/,
use: {
loader: 'url-loader',
options: {
limit: 1024 * 8, // 8k以下的base64内联,不产生图片文件
fallback: 'file-loader', // 8k以上,用file-loader抽离(非必须,默认就是file-loader)
name: '[name].[ext]?[hash]', // 文件名规则,默认是[hash].[ext]
outputPath: 'images/', // 输出路径
publicPath: '' // 可访问到图片的引用路径(相对/绝对)
}
}
}
]
}
};

上述配置除了limitfallback是url-loader(文档)的参数以外,其他配置项如name, outputPath都会透传给file-loader(文档)。

关于name, outputPath, publicPath

  1. 图片最终的输出路径:path.join(outputPath, name)
  2. 图片的引用路径:
    • 指定了publicPath:path.join(publicPath, name),这里会忽略掉outputPath
    • 否则用默认的output.publicPath:path.join(__webpack_public_path__, outputPath, name)

(4) 打包结果

html

css背景图

js动态插入

咦,有没发现背景图引用路径不对?

根据上面的引用路径生成规则path.join('', 'images/','[name].[ext]?[hash]'),也就是各引用入口(html/css)必须和images目录同级才能访问到图片。而css放在了style目录下,与images不同级。

引用路径不对?用publicPath修正

要解决上面的问题,可以在抽离css时设定publicPath。

extractCSS.extract({
fallback: 'style-loader',
use: 'css-loader',
publicPath: '../' // 默认取output.publicPath
})

【重点】来看下到底output.publicPath是什么?

publicPath的值会作为前缀附加在loaders生成的所有URL前面。

比如上面的images/cjl.jpg,如果设置了output.publicPath:"../",那最终打包之后就会变成../images/cjl.jpg

The value of the option is prefixed to every URL created by the runtime or loaders. Because of this the value of this option ends with / in most cases.

它指定了output目录的访问路径,也就是浏览器怎样找到output目录。

This option specifies the public URL of the output directory when referenced in a browser.

比如设置了output.publicPath:"../",就说明output目录在html所在目录的上一级。

那这样设置了的话,css和html的目录层级关系并不符合要求,所以单独在extractCSS.extract中设置publicPath起到了覆盖output.publicPath的作用。

7. ES6转义

npm install -D babel-core babel-loader babel-preset-env babel-preset-stage-0

  • babel-core 核心包
  • babel-loader
  • babel-preset-env 定案内语法编译 (babel-preset-es2015已废弃)
  • babel-preset-stage-0 预案内语法编译

// webpack.config.js
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/, // npm包不做处理
include: /src/, // 只处理src里面的
use: {
loader: 'babel-loader',
options: {
presets: ['env', 'stage-0'] // 【重要】顺序右到左,先处理高级或特殊语法
}
}
}
]
}

options的内容也可以单独写在.babelrc

{
"presets": ["env", "stage-0"]
}

8. webpack-dev-server

开发调试怎么少的了本地服务器。npm install -D webpack-dev-server。基础配置比较简单,参见官方文档,配置好之后直接webpack-dev-server即可。

下面提到的是使用时会遇到的一些问题。

contentBase和publicPath

contentBase和publicPath两个参数比较重要,设置错了的话会导致文件404

devServer: {
contentBase: './public',
publicPath: '/',
host: 'localhost',
port: 3000
}

ℹ 「wds」: webpack output is served from /
ℹ 「wds」: Content not from webpack is served from ./public

(1) contentBase

Content not from webpack is served from

也就是指定静态服务器的根目录,可以访问到不通过webpack处理的文件

devServer: {
index: 'main.html', // 为了可以展示目录
contentBase: __dirname, // 默认值就是项目根目录
host: 'localhost',
port: 3000
}

启动服务器之后,访问localhost:3000,可以看到根目录的所有文件

需要说明下的是,为了演示,如果不设置index: 'main.html'localhost:3000会直接访问项目入口文件index.html

举个具体例子,如果我们在入口文件直接引入,这个资源不会经过webpack处理,因此最终访问路径是localhost:3000/public/test.css

同理,当contentBase: path.join(__dirname, 'public')时,访问localhost:3000只会显示public下的文件。因此在出现文件404时,检查下引用的资源url是否和contentBase里的文件一一对应。

如果所有的静态资源文件都会经过webpack打包,其实可以直接设置为false,此时可以再试试访问localhost:3000 (Cannot GET /)。

(2) publicPath

webpack output is served from

对于webpack打包的文件:虽然我们指定了打包输出目录dist,但是实际上并不会生成dist,而是打包后直接传给devserver,然后放到内存中。不过可以通过:http://localhost:3000/webpack-dev-server查看打包目录下的文件。

publicPath是告诉浏览器通过什么路径去访问上面的webpack打包目录。默认值是/。也就是说我们可以通过:http://localhost:3000/index.html,http://localhost:3000/style/vutify.css来访问打包文件。这点要和contentBase的静态文件服务器区分开。

另一个容易导致文件404的是:把publicPath设置为打包目录/dist。这样的话,就需要多加一层:

http://localhost:3000/dist/index.html,

http://localhost:3000/dist/style/vutify.css才能访问。

热更新 HMR

webpack-dev-server在试图重新加载整个页面(LiveReload)之前,会尝试使用热更新(HMR)来更新。

devServer: {
host: 'localhost',
port: 3000,
hot: true // 还需要在plugins中配置new webpack.HotModuleReplacementPlugin()
}

可以发现在更新css以及html文件时,页面是不会刷新的(css-loader/html-webpack-plugin已具备热更新功能),但更新js时会。因此可以在入口文件添加以下代码,实现热更新。

// index.js
import './dependency.js';
if (module.hot) {
module.hot.accept();
}

域名与代理

场景1:

真机上访问devServer,进行开发、调试、体验。

解决方法:

host指定为无线网卡的ip,如192.168.0.104,PC与其他移动设备处于同一wifi环境下时即可访问。

devServer: {
host: '192.168.0.104', // 默认值是localhost
port: 3000,
hot: true
}

场景2:

后台数据接口为http://c.y.qq.com/xxx,如果用localhost:3000访问的话,会遇到跨域的问题,需要使用y.qq.com域名访问。

解决方法:

将本地服务器放在80端口上(Mac下需要sudo起服务),配置host:y.qq.com 127.0.0.1,此时使用http://y.qq.com/即可访问本地服务器。

devServer: {
host: '127.0.0.1',
port: 80,
hot: true,
allowedHosts: [ // 允许哪些域名访问devServer
'y.qq.com'
]
}

场景3:

后台服务搭在本地8360端口,页面在3000端口。

// bad: 入侵代码
const url = process.env.NODE_ENV == 'development'
? 'http://127.0.0.1:8360/common/getmultiple'
: '/common/getmultiple'

解决方法:

devServer: {
host: '127.0.0.1',
port: 3000,
hot: true,
proxy: {
"/common/getmultiple": "http://localhost:3000"
}
}

proxy配置项非常强大,可以pathRewrite重写请求路径,bypass绕过代理等,这里抛砖引玉,其他功能参考devserver-proxy官方文档。

五、未完待续

经过上面的配置,对webpack的entry、output、publicPath、loader等灵魂级配置项有了一定的理解,同时实现了基本的脚手架功能配置。在优化篇会分享一下code spliting、dll预编译等优化及提升开发体验的功能,敬请期待。

 

原文链接:https://cloud.tencent.com/developer/article/1148463


推荐阅读
  • 本文介绍了在Windows环境下如何配置php+apache环境,包括下载php7和apache2.4、安装vc2015运行时环境、启动php7和apache2.4等步骤。希望对需要搭建php7环境的读者有一定的参考价值。摘要长度为169字。 ... [详细]
  • 本文介绍了前端人员必须知道的三个问题,即前端都做哪些事、前端都需要哪些技术,以及前端的发展阶段。初级阶段包括HTML、CSS、JavaScript和jQuery的基础知识。进阶阶段涵盖了面向对象编程、响应式设计、Ajax、HTML5等新兴技术。高级阶段包括架构基础、模块化开发、预编译和前沿规范等内容。此外,还介绍了一些后端服务,如Node.js。 ... [详细]
  • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
  • 本文介绍了Python高级网络编程及TCP/IP协议簇的OSI七层模型。首先简单介绍了七层模型的各层及其封装解封装过程。然后讨论了程序开发中涉及到的网络通信内容,主要包括TCP协议、UDP协议和IPV4协议。最后还介绍了socket编程、聊天socket实现、远程执行命令、上传文件、socketserver及其源码分析等相关内容。 ... [详细]
  • 本文讨论了在手机移动端如何使用HTML5和JavaScript实现视频上传并压缩视频质量,或者降低手机摄像头拍摄质量的问题。作者指出HTML5和JavaScript无法直接压缩视频,只能通过将视频传送到服务器端由后端进行压缩。对于控制相机拍摄质量,只有使用JAVA编写Android客户端才能实现压缩。此外,作者还解释了在交作业时使用zip格式压缩包导致CSS文件和图片音乐丢失的原因,并提供了解决方法。最后,作者还介绍了一个用于处理图片的类,可以实现图片剪裁处理和生成缩略图的功能。 ... [详细]
  • Spring常用注解(绝对经典),全靠这份Java知识点PDF大全
    本文介绍了Spring常用注解和注入bean的注解,包括@Bean、@Autowired、@Inject等,同时提供了一个Java知识点PDF大全的资源链接。其中详细介绍了ColorFactoryBean的使用,以及@Autowired和@Inject的区别和用法。此外,还提到了@Required属性的配置和使用。 ... [详细]
  • 本文详细介绍了SQL日志收缩的方法,包括截断日志和删除不需要的旧日志记录。通过备份日志和使用DBCC SHRINKFILE命令可以实现日志的收缩。同时,还介绍了截断日志的原理和注意事项,包括不能截断事务日志的活动部分和MinLSN的确定方法。通过本文的方法,可以有效减小逻辑日志的大小,提高数据库的性能。 ... [详细]
  • 本文介绍了lua语言中闭包的特性及其在模式匹配、日期处理、编译和模块化等方面的应用。lua中的闭包是严格遵循词法定界的第一类值,函数可以作为变量自由传递,也可以作为参数传递给其他函数。这些特性使得lua语言具有极大的灵活性,为程序开发带来了便利。 ... [详细]
  • 本文介绍了使用Java实现大数乘法的分治算法,包括输入数据的处理、普通大数乘法的结果和Karatsuba大数乘法的结果。通过改变long类型可以适应不同范围的大数乘法计算。 ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • Android中高级面试必知必会,积累总结
    本文介绍了Android中高级面试的必知必会内容,并总结了相关经验。文章指出,如今的Android市场对开发人员的要求更高,需要更专业的人才。同时,文章还给出了针对Android岗位的职责和要求,并提供了简历突出的建议。 ... [详细]
  • 本文介绍了Hyperledger Fabric外部链码构建与运行的相关知识,包括在Hyperledger Fabric 2.0版本之前链码构建和运行的困难性,外部构建模式的实现原理以及外部构建和运行API的使用方法。通过本文的介绍,读者可以了解到如何利用外部构建和运行的方式来实现链码的构建和运行,并且不再受限于特定的语言和部署环境。 ... [详细]
  • Java验证码——kaptcha的使用配置及样式
    本文介绍了如何使用kaptcha库来实现Java验证码的配置和样式设置,包括pom.xml的依赖配置和web.xml中servlet的配置。 ... [详细]
  • 本文介绍了在mac环境下使用nginx配置nodejs代理服务器的步骤,包括安装nginx、创建目录和文件、配置代理的域名和日志记录等。 ... [详细]
  • CentOS 6.5安装VMware Tools及共享文件夹显示问题解决方法
    本文介绍了在CentOS 6.5上安装VMware Tools及解决共享文件夹显示问题的方法。包括清空CD/DVD使用的ISO镜像文件、创建挂载目录、改变光驱设备的读写权限等步骤。最后给出了拷贝解压VMware Tools的操作。 ... [详细]
author-avatar
mobiledu2502861377
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有