Webpack
用于前端工程化代码的底层构建,angular、react、vue三大框架都采用了webpack来做构建。webpack具备的功能:tree shaking、懒加载、代码分割等等。其核心定义是一个模块打包工具;
能够识别所有模块引入方式,例如:
- es6 module import引入;
- commonJs 中module.exports=Header;
- AMD;
- CMD;
Webpack 4
的构建速度更快,在构建大型的项目时,甚至可以提升90%
的构建速度。Nodejs和Webpack版本尽可能用最新稳定(LTS)版本,也可以提高打包速度;
会涉及的知识点如下图:
安装
新建一个,webpack01
文件夹,并cd webpack01
进入文件夹,安装好需要的包webpack和webpack-cli,并指定版本安装(新版本尚未研究差异,暂且用老版本),其中webpack-cli
用于实现命令行功能。
当没有使用-g
进行全局安装时,执行webpack -v
会提示命令不存在;需要使用npx webpack -v
来执行,npx
会进入到项目中的node_module
目录,寻找到webpack
包来执行;
//不推荐全局安装-g
npm i webpack@4.43.0 webpack-cli@3.3.12 --save-dev
npm i webpack@4.43.0 webpack-cli@3.3.12-D
设置淘宝镜像
在根目录下创建.npmrc
配置文件:
registry=https://registry.npm.taobao.rog/
其他方法:
1、npm config set registry https://registry.npm.taobao.org
2、npm config set registry https://registry.npm.taobao.org/
重置webpack打包命令**
修改package.json文件的script属性值,执行npm run dev
命令时,等同于npx webpack
"scripts": {
"dev":"npx webpack",
...
},
看一个入门小例子
-
目录结构
-
代码
index.html
的代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>首页</title>
</head>
<body>
<div id="root"></div>
<script src="./index.js"></script>
</body>
</html>
index.js
的代码
import Header from "./header";
import Content from "./content";
import Footer from "./footer";
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);
}
export default Header;
content.js
的代码
function Content() {
var dom = document.getElementById("root");
var content = document.createElement("div");
content.innerText = "content";
dom.append(content);
}
export default Content;
footer.js
的代码
function Footer() {
var dom = document.getElementById("root");
var footer = document.createElement("div");
footer.innerText = "footer";
dom.append(footer);
}
export default Footer;
-
运行
执行命令npx webpack index.js
完成后,会在项目根目录下生成一个dist
目录,里面 有个main.js
文件。打开index.html
,页面显示如下:
Webpack默认配置文件webpack.config.js
1. 入口(entry)
当没有创建webpack.config.js
文件,或者此文件中为空时,直接执行npx webpack
,终端控制台报错;写入如下代码:
module.exports = {
entry: "./index.js"//等同于{main:"./index.js"},会在输出目录中打包生成main.js文件。
};
再次执行npx webpack
,则在根目录生成'/dist/main.js',效果等同于npx webpack index.js
。
2. 出口(output)
const path = require('path');
module.exports = {
entry: "./index.js",
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'my-first-webpack.bundle.js'
}
};
执行npx webpack
,生成文件如下:
常用配置:
- publicPath:"http://cdn.com.cn",打包完成后,会在index.html中引入的script路径中加上这个域名;
3. mode模式
production
:默认,代码会被压缩
development
:代码不会被压缩
4. loader
webpack
不能识别非 JavaScript 文件,需要loader
来识别;如果看到引入的模块的后缀不是.js
,就需要想到用对应的loader
来引入;loader中支持配置各种options
参数,例如:file-loader的选项值;loader的执行顺序是由右往左
的。
常用的loader:
- file-loader:指示webpack发出所需的对象文件,并返回其公共URL;可用于加载iconfont中的字体文件。
-
url-loader:
url-loader
功能类似于file-loader
,但是在文件大小(单位 byte)低于指定的限制时,可以返回一个 DataURL。
test: /\.(png|jpg|gif)$/, use: {
loader: "url-loader",
options: {
name: 'images/[name].[ext]',
limit: 8192//单位B,如果不设置,则会把所有图片都用base64
}
}
//js引入图片需要使用require,直接写相对路径无效
var img = new Image();
img.src = require('./image/img1.jpg').default;
document.getElementById("main").appendChild(img);
- style-loader:将css文件中的内容解析提取出来,并挂载到head中style里面;
-
css-loader:解释
@import
和url()
,会import/require()
后再解析(resolve)它们。例如css文件中使用@import "./avator.css"
; -
sass-loader:加载一个SASS / SCSS文件,编译成 CSS。安装
npm install sass-loader node-sass --save-dev
-
postcss-loader:
PostCSS
利用JavaScript
的强大编程能力对CSS
代码进行转换。数以百计的PostCSS
插件可以用来为CSS
属性添加特定于浏览器厂商的前缀、压缩工具cssnano、支持未来 CSS 语法、模块。如果给class添加厂商前缀 -
babel-loader:用来处理ES6语法,将其编译为浏览器可以执行的js语法;使用
userBuildIns:'usage'
配置,可以有效减少打包文件大小(只打包用到的es6语法)。使用target
设定目标浏览器版本,打包的时候就会对这些版本下支持的es6语法免打包;
再结合"@babel/polyfill"
,可以完全解决es6在老浏览器中运行问题,但是打包文件也会因此而变大,因为系统会把很多老浏览器不支持的方法,以自定义代码的形式打包到文件中。所以要使用useBuiltIns:'usage'
,设置如果用到了就打包,没用到就不打包。高阶前端工程师需要学习babel各种配置,了解抽象语法树。
如果是写的项目代码,那么可以用babel+@babel/polyfill来;如果是发布上线的组件或插件,就不能用这种方式,会污染全局。需要改用
@babel/plugin-transform-runtime
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader",
options: {
"presets": [
["@babel/preset-env", {
"targets": {
"chrome": "67"
},
"useBuiltIns": "usage"
}]
]
}
},
babel-loader
中options
配置项的代码也可以单独放入跟目录下的.babelrc
文件中,执行效果是一样的。
//vue init webpack my-project脚手架项目默认.babelrc文件代码
{
"presets": [
["env", {
"modules": false,
"targets": {
"browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
}
}],
"stage-2"
],
"plugins": ["transform-vue-jsx", "transform-runtime"],
"env": {
"test": {
"presets": ["env", "stage-2"],
"plugins": ["transform-vue-jsx", "transform-es2015-modules-commonjs", "dynamic-import-node"]
}
}
}
5. plugins
插件目的在于解决 loader
无法实现的其他事;可以在webpack
运行到某个时刻的时候,帮你做一些事情;类似于生命周期钩子;例如html-webpack-plugin
查看,就是在webpack
打包完成的时刻,自动生成一个index.html
到打包目录中去。
常用plugins:
- html-webpack-plugin:自动生成一个HTML文件
- clean-webpack-plugin:清空打包目录
- webpack.ProvidePlugin,例如引入jquery
//安装
npm install jquery --save-dev
//引用,修改webpack.config.js文件。
const webpack = require('webpack')
plugins: [
new webpack.ProvidePlugin({
jQuery: "jquery",
$: "jquery"
})
],
//使用
$("#header").html("init");
6. sourceMap
devtool
用于打包前和打包后文件代码位置映射,方便代码出错时定位到源代码位置;
当 webpack 打包源代码时,可能会很难追踪到错误和警告在源代码中的原始位置。例如,如果将三个源文件(a.js
, b.js
和 c.js
)打包到一个 bundle(bundle.js
)中,而其中一个源文件包含一个错误,那么堆栈跟踪就会简单地指向到 bundle.js
。这并通常没有太多帮助,因为你可能需要准确地知道错误来自于哪个源文件。
为了更容易地追踪错误和警告,JavaScript 提供了 source map 功能,将编译后的代码映射回原始源代码。如果一个错误来自于 b.js
,source map 就会明确的告诉你。 devtool详细配置、JavaScript Source Map 详解
module.exports = {
entry: {
app: './src/index.js',
print: './src/print.js'
},
devtool: 'cheap-module-eval-source-map', //调试版本建议用
//devtool: 'cheap-module-source-map', //打包后版本建议用
plugins: [
new CleanWebpackPlugin(['dist']),
new HtmlWebpackPlugin({
title: 'Development'
})
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist')
}
};
7. devServer
用于调试阶段项目热重载;
- a.安装;
npm i webpack-dev-server -D
- b.修改
webpack.config.js
devServer: {
contentBase: path.join(__dirname, "dist"),//
compress: true,
open: true,
port: 9000
},
- c.修改
package.json
"scripts": {
"watch": "webpack --watch",//观察者模式。源代码更改后,dist中代码会自动打包;
"start": "webpack-dev-server"//npm run start启动后,会自动打开一个端口为9000的页面,并且源代码修改后,页面会热重载;
}
package.json配置
- 修改打包命令
"scripts": {
"bundle": "webpack"//可以不用写npx webpack,因为script脚本命令,会优先到当前工程目录中查找是否支持此命令,没找到再到全局查找;
"watch": "webpack --watch",//监听模式,如发现源代码改变了,则将文件自动打包到dist目录(页面不会自动刷新)
"start": "webpack-dev-server"//自动热重载,如果发现域代码改变了,则自动打包到内存,并自定刷新浏览器页面
},
执行npm run bundle
,等同于执行npx webpack
。
附件
webpack.config.js文件代码:
const path = require('path');
const webpack = require('webpack')
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: {
app: './src/index.js'
},
mode: "development",//production
devtool: 'cheap-module-eval-source-map', //调试版本建议用
plugins: [
new CleanWebpackPlugin(),
new webpack.HotModuleReplacementPlugin(),//热更新
new HtmlWebpackPlugin({
template: "./src/index.html"
}),
new webpack.ProvidePlugin({
jQuery: "jquery",
$: "jquery"
})
],
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader",
options: {
"presets": [
["@babel/preset-env", {
"targets": {
"chrome": "67"
},
"useBuiltIns": "usage"
}]
]
}
},
{
test: /\.(png|jpg|gif)$/, use: {
loader: "url-loader",
options: {
name: 'images/[name].[ext]',
limit: 8192
}
}
},
{
test: /\.css$/, use: ["style-loader", "css-loader"]
},
{
test: /\.scss$/, use: ["style-loader", "css-loader", "sass-loader", "postcss-loader"]
}
],
},
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist')
},
devServer: {
contentBase: path.join(__dirname, "dist"),
compress: true,
open: true,
port: 9000,
hot:true, //改了哪个文件,就更新哪个文件,最小化更新,提交开发效率;
hotOnly: true, //启用热模块替换,而无需页面刷新作为构建失败时的回退。
proxy: {
"/api": {
target: "http://localhost:8081"//dev环境设置代理,方便测试;
}
}
},
};
package.json代码
{
"name": "jquerytowebpack4",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "webpack-dev-server"
},
"author": "",
"license": "ISC",
"devDependencies": {
"@babel/core": "^7.10.4",
"@babel/preset-env": "^7.10.4",
"clean-webpack-plugin": "^3.0.0",
"css-loader": "^3.6.0",
"file-loader": "^6.0.0",
"html-webpack-plugin": "^4.3.0",
"postcss-loader": "^3.0.0",
"sass-loader": "^9.0.2",
"style-loader": "^1.2.1",
"url-loader": "^4.1.0",
"webpack": "^4.43.0",
"webpack-cli": "^3.3.12",
"webpack-dev-server": "^3.11.0"
},
"dependencies": {
"babel-loader": "^8.1.0",
"jquery": "^3.5.1"
}
}
问题汇总:
1、安装 xxx-loader成功后,打包报错:this.getOption is not function
由于xxx-loader
版本过高导致,降版本就行了,可以看npm上的版本记录,找前个版本的稳定版;
2、配置browserslist
node项目中不同位置设置browserslist对postcss-loader影响的权重
前端工程基础知识点--Browserslist (基于官方文档翻译)