热门标签 | 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


推荐阅读
  • 本文介绍了JavaScript进化到TypeScript的历史和背景,解释了TypeScript相对于JavaScript的优势和特点。作者分享了自己对TypeScript的观察和认识,并提到了在项目开发中使用TypeScript的好处。最后,作者表示对TypeScript进行尝试和探索的态度。 ... [详细]
  • 本文记录了在vue cli 3.x中移除console的一些采坑经验,通过使用uglifyjs-webpack-plugin插件,在vue.config.js中进行相关配置,包括设置minimizer、UglifyJsPlugin和compress等参数,最终成功移除了console。同时,还包括了一些可能出现的报错情况和解决方法。 ... [详细]
  • 微信民众号商城/小顺序商城开源项目介绍及使用教程
    本文介绍了一个基于WeiPHP5.0开发的微信民众号商城/小顺序商城的开源项目,包括前端和后端的目录结构,以及所使用的技术栈。同时提供了项目的运行和打包方法,并分享了一些调试和开发经验。最后还附上了在线预览和GitHub商城源码的链接,以及加入前端交流QQ群的方式。 ... [详细]
  • React 小白初入门
    推荐学习:React官方文档:https:react.docschina.orgReact菜鸟教程:https:www.runoob.c ... [详细]
  • RN即ReactNative基于React框架针对移动端的跨平台框架,在学习RN前建议最好熟悉下html,css,js,当然如果比较急,那就直接上手吧,毕竟用学习前面基础的时间,R ... [详细]
  • 本文介绍了在rhel5.5操作系统下搭建网关+LAMP+postfix+dhcp的步骤和配置方法。通过配置dhcp自动分配ip、实现外网访问公司网站、内网收发邮件、内网上网以及SNAT转换等功能。详细介绍了安装dhcp和配置相关文件的步骤,并提供了相关的命令和配置示例。 ... [详细]
  • 本文介绍了在Linux下安装Perl的步骤,并提供了一个简单的Perl程序示例。同时,还展示了运行该程序的结果。 ... [详细]
  • 本文介绍了在CentOS上安装Python2.7.2的详细步骤,包括下载、解压、编译和安装等操作。同时提供了一些注意事项,以及测试安装是否成功的方法。 ... [详细]
  • 网络请求模块选择——axios框架的基本使用和封装
    本文介绍了选择网络请求模块axios的原因,以及axios框架的基本使用和封装方法。包括发送并发请求的演示,全局配置的设置,创建axios实例的方法,拦截器的使用,以及如何封装和请求响应劫持等内容。 ... [详细]
  • 本文介绍了在Mac上安装Xamarin并使用Windows上的VS开发iOS app的方法,包括所需的安装环境和软件,以及使用Xamarin.iOS进行开发的步骤。通过这种方法,即使没有Mac或者安装苹果系统,程序员们也能轻松开发iOS app。 ... [详细]
  • uniapp开发H5解决跨域问题的两种代理方法
    本文介绍了uniapp开发H5解决跨域问题的两种代理方法,分别是在manifest.json文件和vue.config.js文件中设置代理。通过设置代理根域名和配置路径别名,可以实现H5页面的跨域访问。同时还介绍了如何开启内网穿透,让外网的人可以访问到本地调试的H5页面。 ... [详细]
  • VueCLI多页分目录打包的步骤记录
    本文介绍了使用VueCLI进行多页分目录打包的步骤,包括页面目录结构、安装依赖、获取Vue CLI需要的多页对象等内容。同时还提供了自定义不同模块页面标题的方法。 ... [详细]
  • Python已成为全球最受欢迎的编程语言之一,然而Python程序的安全运行存在一定的风险。本文介绍了Python程序安全运行需要满足的三个条件,即系统路径上的每个条目都处于安全的位置、"主脚本"所在的目录始终位于系统路径中、若python命令使用-c和-m选项,调用程序的目录也必须是安全的。同时,文章还提出了一些预防措施,如避免将下载文件夹作为当前工作目录、使用pip所在路径而不是直接使用python命令等。对于初学Python的读者来说,这些内容将有所帮助。 ... [详细]
  • 本文介绍了在Android Studio中使用命令行build gradle的方法,并解决了一些常见问题,包括手动配置gradle环境变量和解决External Native Build Issues的方法。同时提供了相关参考文章链接。 ... [详细]
  • 1223  drf引入以及restful规范
    [toc]前后台的数据交互前台安装axios插件,进行与后台的数据交互安装axios,并在main.js中设置params传递拼接参数data携带数据包参数headers中发送头部 ... [详细]
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社区 版权所有