webpack
是一个现代 JavaScript 应用程序的静态模块打包器(module bundler),当 webpack
处理应用程序时,会递归构建一个依赖关系图,其中包含应用程序须要的每一个模块,而后将这些模块打包成一个或多个 bundle
。css
新建 index.html
,以下:html
<!DOCTYPE html>
<html lang="en">
<body>
<p>这是网页内容</p>
<div id="root"></div>
<script src="./header.js"></script>
<script src="./content.js"></script>
<script src="./footer.js"></script>
<script src="./index.js"></script>
</body>
</html>
复制代码
再创建index.js
、header.js
、 content.js
、 footer.js
vue
//index.js
new Header()
new Content()
new Footer()
//header.js
function Header () {
var dom = document.getElementById('root')
var header = document.createElement('div')
header.innerText = 'header'
dom.append(header)
}
//content.js
function Content () {
var dom = document.getElementById('root')
var content = document.createElement('div')
content.innerText = 'content'
dom.append(content)
}
//footer.js
function Footer () {
var dom = document.getElementById('root')
var footer = document.createElement('div')
footer.innerText = 'footer'
do
m.append(footer)
}
复制代码
这样面向对象new出各个模块的构造函数,在必定程度上比面相过程所有写在一个文件上好维护,可是Header
、Contet
这些构造函数指向问题还要回到index.html
文件一一对应查找,就形成了在index.js
上指定不明确,若是咱们能像如下代码同样导入就能解决这个问题了node
//index.js
import Header from './header.js'
import Content from './content.js'
import Footer from './footer.js'
new Header()
new Content()
new Footer()
复制代码
遗憾的是,浏览器不会识别这种es6的语法,使用浏览器访问index.html
会发现控制台会报错。此时咱们的webpack
就能够为了解决这种问题而诞生了webpack
首先想使用webpack
,咱们得先npm
初始化包并安装webpackcss3
npm init
npm install webpack webpack-cli -D
复制代码
想用ESmodule
语法导出,咱们得修增长导出语法es6
//header.js
export default Header
//content.js
export default Content
//footer.js
export default Footer
复制代码
而后咱们就可使用npx
运行 webpack
执行 index.js
文件web
npx webpack index.js
复制代码
执行后,咱们发发现根目录下生成了一个 dist/main.js
目录文件,这即是webpack默认出口输出(output
),后面会介绍。这个文件即是webpack翻译咱们的index.js
生成后的文件,因此有的人会称webpack
是JS
的翻译器,但这说法也并不完成正确。既然咱们翻译了文件,咱们就须要在index.html
修改为导入后的文件,以下修改:npm
//index.html
<script src="./dist/main.js"></script>
复制代码
修改完成后打开浏览器从新访问index.html
发现能够生成相对于模块而且没有报错了,这就是webpack
的模块开发。json
webpack 的核心概念
entry
: 入口
output
: 输出
loader
: 模块转换器,用于把模块原内容按照需求转换成新内容
插件(plugins
): 扩展插件,在webpack构建流程中的特定时机注入扩展逻辑来改变构建结果或作你想要作的事情
在 webpack v4
中,能够无须任何配置,由于webpack
提供了默认配置,然而大多数项目会须要很复杂的设置,这就是为何 webpack 仍然要支持 配置文件。这比在 terminal(终端) 中手动输入大量命令要高效的多,因此让咱们在根目录建立一个配置文件:webpack.config.js
(默认配置文件,可经过npx webpack --config webpack.config.js
修改)
//webpack.config.js
const path = require('path')
module.exports = {
entry: './src/index.jss' , //入口文件 默认:src/index.js
output: { //出口文件 默认: dist/main.js
filename: 'bundle.js', //输出的文件名
path: path.resolve(__dirname,'dist') //输出的路径,只能是绝对路径
}
}
复制代码
新建src文件夹,移动index.js,header.js
等js到src文件夹,执行如下代码会发现dist文件夹生成了新的出口文件bundele.js
npx webpack src/index.js
复制代码
配置须要打包入口文件
//webpack.config.js
<!--单个文件-->
module.exports = {
entry: './src/index.jss' , //入口文件 默认:src/index.js
}
<!--打包多个入口文件-->
module.exports = {
entry: {
main: './src/index.js', //入口文件 默认:src/index.js
sub: './src/sub.js'
},
}
复制代码
配置打包输出的文件
output: { //出口文件 默认: dist/main.js
filename: 'bundle.js', //输出的文件名
path: path.resolve(__dirname,'dist') //输出的路径,只能是绝对路径
}
<!--多个入口文件须要不一样名称文件输出配置-->
output: { //出口文件 默认: dist/main.js
filename: '[name].js', //输出的文件名
path: path.resolve(__dirname, 'dist') //输出的路径,只能是绝对路径
},
复制代码
考虑到用 CLI 这种方式来运行本地的 webpack 副本并非特别方便,咱们能够设置一个快捷方式。调整 package.json 文件,添加在 npm scripts
中添加一个 npm 命令:
//package.json
"scripts": {
"bundle" : "webpack"
},
复制代码
如今,可使用 npm run build
命令,来替代咱们以前使用的 npx
命令。注意,使用 npm scripts
,咱们能够像使用 npx 那样经过模块名引用本地安装的 npm packages。这是大多数基于 npm
的项目遵循的标准,由于它容许全部贡献者使用同一组通用脚本(若是必要,每一个 flag 都带有 --config 标志)。
如今运行如下命令,而后看看你的脚本别名是否正常运行:
npm run bundle
复制代码
npm run bundle
> webpack4@1.0.0 bundle E:\project\webpack4
> webpack
Hash: 768c04b37ed214487576
Version: webpack 4.42.0
Time: 98ms
Built at: 2020-03-24 4:57:54 PM
Asset Size Chunks Chunk Names
bundle.js 1.29 KiB 0 [emitted] main
Entrypoint main = bundle.js
[0] ./src/index.js + 3 modules 741 bytes {0} [built]
| ./src/index.js 147 bytes [built]
| ./src/header.js 202 bytes [built]
| ./src/content.js 198 bytes [built]
| ./src/footer.js 194 bytes [built]
WARNING in configuration
The 'mode' option has not been set, webpack will fallback to 'production' for this value. Set 'mode' option to 'development' or 'production' to enable defaults for each environment.
You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/configuration/mode/
复制代码
从webpack
打包输出的结果咱们能够看出有个警告,那是由于咱们没有指定打包输出的环境('development'
or'production'
),咱们能够在webpack.config.js
新增如下代码:
module.exports = {
mode: 'development', //默认为production
...
}
复制代码
从新执行npm run bundle
发现不会提出警告了,而且生成的bundle.js
的代码没有被压缩
webpack 可使用 loader
来预处理文件。这容许你打包除 JavaScript 以外的任何静态资源。若是你在默认配置下打包除js文件外出错,因此咱们要借助loader
来打包js外的文件
如今咱们在src
文件下存放一张logo.jpg
的图片,而后在index.js引入后使用webpack
打开
//index.js
const logo = require('./logo.jpg')
复制代码
执行npm run bundle
打包后会抛出以下错误:
ERROR in ./src/logo.jpg 1:0
Module parse failed: Unexpected character '�' (1:0)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
(Source code omitted for this binary file)
@ ./src/index.js 5:13-34
复制代码
这是由于webpack像咱们上面说的同样,只能打包js文件,若是想打包除js之外的静态资源,此时咱们就须要借用loader来帮助咱们打包图片资源。想打包图片咱们首先得先安装相关的loader
npm install file-loader -D
复制代码
而后咱们须要在webpack.config.js
文件下配置相关的module
规则:
//webpack.config.js
...
module: {
rules: [
{
test: /\.jpg$/,
use: [
{
loader: 'file-loader' //使用相对应的loader
}
]
}
]
}
复制代码
控制台npm run bundle
再次运行时,发现打包成功没有报错,而且在dist出口目录生成了相应的图片资源。那咱们导入的图片资源变量会是什么呢,咱们试着打印一下:
const logo = require('./logo.jpg')
console.log('logo',logo)
复制代码
vue
的脚手架项目中能够这样引入.vue相关文件的了
import Header from 'Header.vue'
复制代码
//webpack.config.js
rules: [
...
{
test: /\.vue$/,
use: { //只使用一个loader能够直接用对象配置
loader: 'vue-loader'
}
}
]
复制代码
上面案例中,咱们能够看到options
字段,这是咱们能够打包文件资源的时候定义相对应的规则,好比:
rules: [
{
test: /\.jpg$/,
use: [
{
loader: 'file-loader', //使用相对应的loader
options: {
name:'[name].[ext]', //定义打包生成的文件名字
outputPath: 'images' //定义输出的文件目录:dist/images 下
},
}
]
}
]
复制代码
这样咱们就能够自定义打包文件的名字和目录,更多的规则能够查看官方文档配置
咱们能够用url-loader
取代file-loader
来实现静态文件资源打包,那为何咱们有file-loader还要用url-loader,让我看看下面的例子就知道了:
首先咱们先npm install url-loader --save-dev
安装file-loader
,而后在webpack.config.js
进行相对应的配置
rules: [
{
test: /\.jpg$/,
use: [
{
loader: 'url-loader', //使用相对应的loader
options: {
limit: 10240 //单位:字节 超过10kb 的文件生成图片,不然生成base64编码
}
}
]
}
]
复制代码
运行打包后咱们能够发现,超过10kb 的文件生成图片,不然生成base64编码
。这样作的好处是什么呢,图片生成base64编码能够大大减小咱们https请求,但不是全部的图片都生成base64编码,好比图片几M的生成的话,相对应js文件大小也会增长,网页打空白的时间也相对应增长,至于哪些须要转,limit须要设置多大限制根据本身的项目来,个人项目中通常是icon图标类的会转base64编码,其余大的相对应打包生成文件。
当咱们尝试打包css文件的时候,若是没有使用相对应的样式loader
就会打包失败。咱们在src
目录下建立新的文件index.css
、logo.css
,而且在index.js引入该样式文件:
//logo.css
.logo{
width: 100px;
height: 100px;
}
复制代码
//index.css
@import './logo.css'
复制代码
//index.js
import logo from './logo.jpg'
import './index.css'
var img = new Image()
img.src = logo
img.classList.add('logo')
var root = document.getElementById('root')
root.append(img)
复制代码
打包以后能够看到控制台抛出了相对应的报错,此时咱们应该接入css-loader
和style-loader
来解决这个问题,首先咱们先得安装两个loader
npm install css-loader style-loader -D
复制代码
rules: [
...
{
test: /\.css$/,
//注:打包执行顺利从右到左,从下到上,不能颠倒,先接css-loader转换语法在使用style-loader解析到头部标签
use: ['style-loader','css-loader']
}
]
复制代码
主要用于打包css文件中解析@import
等语法,将 CSS 转化成 CommonJS 模块。
css-loader
还能够配置更多的选项,好比importLoaders
modules
等。若是没有配置importLoaders
,在一个scss文件中@import
其余的scss文件,可能该导入的scss文件不会生效css-loader后面配置的loader(sass-loader
,postcss-loader
)
use: ['style-loader',{
loader: 'css-loader',
options: {
importLoaders: 2 // 0 => no loaders (default); 1 => postcss-loader; 2 => postcss-loader, sass-loader
}
},'sass-loader','postcss-loader'
]
复制代码
配置modules
参数为true能够模块化导入相关的样式文件,不然会全局样式污染。以下列案例:
//index.js
import logo from './logo.jpg'
import './index.scss'
import createLogo from './logo.js'
createLogo()
var img = new Image()
img.src = logo
img.classList.add('logo')
var root = document.getElementById('root')
root.append(img)
复制代码
//logo.js
import logo from './logo.jpg'
function createLogo () {
var img = new Image()
img.src = logo
img.classList.add('logo')
var root = document.getElementById('root')
root.append(img)
}
export default createLogo
复制代码
上面由于没有配置相关的样式模块导入,因此导入index.scss
文件的样式都在两张图片成功生效,下面咱们增长下模块配置引入:
//webpack.congig.js
...
test: /\.scss$/,
use: ['style-loader', {
loader: 'css-loader',
options: {
importLoaders: 2, // 0 => no loaders (default); 1 => postcss-loader; 2 => postcss-loader, sass-loader
modules: true //按模块化引入
}
}, 'sass-loader', 'postcss-loader'
]
}
复制代码
//index.js
import logo from './logo.jpg'
import style from './index.scss'
import createLogo from './logo.js'
createLogo()
var img = new Image()
img.src = logo
img.classList.add(style.logo)
var root = document.getElementById('root')
root.append(img)
复制代码
从新打包后,咱们发现只有index.js文件的图片生效了样式,咱们模块化导入样式成功,更多的options
配置样式能够查看官方文档
配合css-loader
使用,以形式在html页面中头部标签插入css
代码。
npm install sass-loader node-sass webpack --save-dev
复制代码
咱们除了安装sass-lader
外,而且还须要你预先安装 Node Sass
。 这能够控制全部依赖的版本, 并选择要使用的 Sass
实现。新建src/index.sass
//index.sass
body {
.logo{
width: 100px;
height: 100px;
}
}
复制代码
//index.js
import logo from './logo.jpg'
import './index.sass'
var img = new Image()
img.src = logo
img.classList.add('logo')
var root = document.getElementById('root')
root.append(img)
复制代码
咱们须要在webpack.config.js
新增相对应的规则配置:
//webpack.config.js
rules: [
...
{
test: /\.sass$/,
use: ['style-loader','css-loader','sass-loader'] //先把sass转成css ,再进行起来的loader操做(右到左)
}
]
复制代码
配置后从新执行npm run bundle
打包,在浏览器中能够正常访问,把sass-loader
去掉再打包后,能够查看控制台头部样式中sass
的语法没有转成css
,这就是sass-loader的做用
npm install postcss-loader -D
复制代码
postcss-loader
能够对css3
样式前缀自动补全,兼容各个浏览器,使用postcss-loader
前咱们得配置相关的插件等,根目录下新建postcss.config.js
,安装相对应的插件:autoprefixer
(补全css3语法插件)
npm install autoprefixer -D
复制代码
//postcss.config.js
module.exports = {
plugins: [
require('autoprefixer')
]
}
复制代码
//webpack.config.js
rules: [
...
{
test: /\.scss$/,
use: ['style-loader','css-loader','sass-loader','postcss-loader']
}
]
复制代码
autoprefixer
补全得结合browserslist
一块儿使用
//package.json
"browserslist": [
"defaults",
"ie >= 10",
"last 2 versions",
"> 1%",
"iOS 7",
"last 3 iOS versions"
],
复制代码
插件(plugins
): 扩展插件,在webpack
构建流程中的特定时机注入扩展逻辑来改变构建结果或作你想要作的事情,就相似vue
生命周期钩子同样,在某种场景,帮你作某些事情。官方已收录的插件
一种用于打包生成html的插件:HtmlWebpackPlugin
会在打包结束后,自动生成html文件,并把打包生成的js自动引入到这个html文件中。具体配置可查看HtmlWebpackPlugin文档
//安装HtmlWebpackPlugin文档
npm install --save-dev html-webpack-plugin
复制代码
//webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
...
plugins: [
new HtmlWebpackPlugin({
template: './public/index.html' //生成的模板文件
}),
]
}
复制代码
//public/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>html 模板</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
复制代码
有时候咱们打包老是要手动删除掉上一次打包的文件,咱们就想有没有什么工具能帮助咱们在打包前自动删除掉dist目录,CleanWebpackPlugin
就能够帮咱们解决这个问题,详细配置
//webpack.config.js
...
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
template: './public/index.html'
}),
]
复制代码
source-map
能够解决打包后代码报错的地方是打包后的代码而不是源业务代码的问题
//index.js
console.log('devtool',test)
复制代码
这是未设置source-map报错的打包代码
devtool: 'source-map'
复制代码
配置后从新打包久能够看到报错的是原业务代码,可是咱们不建议直接用source-map
,我建议开发环境使用eval-cheap-module-source-map
,生成环境用cheap-module-source-map
,不一样的配置打包的速度不同,能够简单总结,source-map
会生成.map文件来映射,打包速度会很慢,由于还要映射打包文件,inline
能够不生产.map文件,直接打包在出门文件里面转成和base64,cheap
能够只报行除出错而不加列出错,module
可让第三方loader 插件也生效报错,eval
能够直接执行eval函数因此速度最快,具体能够参考官方文档配置
有时候咱们修改了打包入口的文件,老是要从新打包编译打开浏览器访问,有没有一种配置能让咱们监听到入口文件修改,就能自动打包编译在浏览器刷新呢,webpackDevServer
就能够帮你作到,webpackDevServer
会在本地帮你的项目搭建一个服务器来跑,只要你更新它就能够帮你从新打包编译~
首先咱们得安装webpackDevServer
npm install webpack-dev-server -D
复制代码
而后配置相关的参数
//webpack.config.js
module.exports = {
...
devServer: {
contentBase: './dist',
port: 9000, //服务端口号
open: true, //首次打包编译自动打开浏览器
proxy: {//反向代理,通常用于解决跨域问题
'/api': 'http://localhost:3000'
}
},
复制代码
执行npm run start
就能够打包编译帮你打开相关的服务了~
//package.json
"scripts": {
"start": "webpack-dev-server"
},
复制代码
有时候咱们须要作的是,改了该模块的代码,浏览器不刷新,只更新该的模块代码上去,Hot Module Replacement
就能帮咱们实现这个效果。
//webpack.config.js
const webpack = require('webpack')
devServer: {
...
hot: true,//使用 Hot Module Replacement
hotOnly: true, //Hot Module Replacement 出错的时候,浏览器照样不刷新
},
plugins: [
...
new webpack.HotModuleReplacementPlugin()
]
复制代码
js模块代码更新的话还须要增长,css模块的话css-loader
已经帮咱们处理了,那像vue的文件修改vue-loader
也已经作了相关的处理
if (module.hot) {
module.hot.accept('./number.js', function() {
// Do something with the updated library module...
});
}
复制代码