您的位置:首页 > 其它

webpack 快速入门 系列 —— 实战一

2021-05-16 21:03 337 查看

实战一

准备本篇的环境

虽然可以仅展示核心代码,但笔者认为在一个完整的环境中边看边做,举一反三,效果更佳。

这里的环境其实就是初步认识 webpack一文完整的示例,包含 webpack、devServer、处理css、生成 html。

项目结构如下:

webpack-example2
- src                 // 项目源码
- a.css
- b.js
- c.js
- index.html        // 页面模板
- index.js          // 入口
- package.json        // 存放了项目依赖的包
- webpack.config.js   // webpack配置文件

src中的代码如下:

// a.css
body{color:blue;}

// b.js
import './c.js'
console.log('moduleB')
console.log('b2')

// c.js
console.log('moduleC')

// index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=`, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<p>请查看控制台</p>
</body>
</html>

// index.js
import './b.js'
import './a.css'
console.log('moduleA')

package.json:

{
"name": "webpack-example2",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack",
"dev": "webpack-dev-server"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"css-loader": "^5.2.4",
"html-webpack-plugin": "^4.5.2",
"style-loader": "^2.0.0",
"webpack": "^4.46.0",
"webpack-cli": "^3.3.12",
"webpack-dev-server": "^3.11.2"
}
}

webpack.config.js:

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry: './src/index.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.css$/i,
use: ["style-loader", "css-loader"]
},
]
},
plugins: [
new HtmlWebpackPlugin({
template: 'src/index.html'
})
],
mode: 'development',
devServer: {
open: true,
contentBase: path.join(__dirname, 'dist'),
compress: true,
port: 9000,
},
};

在 webpack-example2 目录下运行项目:

// 安装项目依赖的包
> npm i
// 启动服务
> npm run dev

启动服务器后,浏览器会自动打开页面,如果看到蓝色文字”请查看控制台“,说明环境已准备就绪。

打包样式

处理 css 和 less

less 是一种 css 预处理语言,在 webpack 中要处理 less 需要使用

less-loader
,用于将 less 转为 css。

首先安装依赖,然后修改配置文件:

// 安装包。版本8安装失败,所以降了一个版本
> npm i -D less-loader@7

// webpack.
56c
config.js
// 增加对 less 文件处理的loader
rules: [
// 需要保留,否则识别不了 css 文件
{
test: /\.css$/i,
use: ["style-loader", "css-loader"]
},
// +
{
test: /\.less$/i,
loader: [
// compiles Less to CSS
"style-loader",
"css-loader",
"less-loader",
],
},
],

然后增加 a.less 文件,在 index.js 中引入 a.less,重新启动服务进行测试:

// src/a.less
body{
p{
color:pink;
}
}

// index.js
import './b.js'
import './a.css'
// +
import './a.less'
console.log('moduleA')

// 启动服务
> npm run dev

在新开的页面中,看到粉色文字”请查看控制台“,说明 less 处理成功。

提取 css 成单独文件

通过浏览器我们发现现在 css 是嵌在页面内的,就像这样:

<head>
...
<style>body{color:blue;}</style>
<style>body p {
color: pink;
}
</style>
</head>

通常我们会通过 link 来引入 css 文件,所以接下来就将 css 取成单独的文件。这里需要使用

mini-css-extract-plugin
这个包。

我们只需要 ad8 安装依赖包,修改配置文件即可:

// 安装依赖包
> npm i -D mini-css-extract-plugin@1
// 不在需要 style-loader,卸载
> npm r -D style-loader

// webpack.config.js
// +
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
...
module: {
rules: [
// 修改规则
{
test: /\.css$/i,
// 将 style-loader 改为 MiniCssExtractPlugin.loader
use: [MiniCssExtractPlugin.loader, "css-loader"],
},
{
test: /\.less$/i,
loader: [
// 将 style-loader 改为 MiniCssExtractPlugin.loader
MiniCssExtractPlugin.loader,
"css-loader",
"less-loader",
],
},
],
},
plugins: [
// +
new MiniCssExtractPlugin(),
...
],

};

启动服务(

npm run dev
),在打开的页面中可以看到 css 已经改为 link 的方式引入,就是这样:

// 从 style 改为 link 方式
<link href="main.css" rel="stylesheet">

// 通过网络查看 main.css 的内容是:
body{color:blue;}
body p {
color: pink;
}

由于我们对 css 和 less 都使用了 MiniCssExtractPlugin.loader,所以 a.css 和 a.less 都被提取到 main.css 中。

Tip:如果通过

npm run build
打包,则可以看到 dist/main.css 文件。

使用 PostCSS

PostCSS - 使用JavaScript转换CSS的工具。

可以将 postcss 当作一个平台,下面我们通过 postcss 做两件事:

  • 增加代码可读性(或增加前缀)
:fullscreen {
}

// 转为

:-webkit-full-screen {
}
:-ms-fullscreen {
}
:fullscreen {
}
  • 立即使用明天的CSS
body {
color: lch(53 105 40);
}

// 转为

body {
color: rgb(250, 0, 4);
}

webpack 可以通过

postcss-loader
来使用 postcss。

由于 postcss 只是一个平台,具体功能需要通过插件来实现,这里我们使用

postcss-preset-env

postcss-preset-env 可以将现代CSS转换为大多数浏览器可以理解的内容,并根据目标浏览器或运行时环境确定所需的polyfill。而且它包含自动前缀。

首先安装相关依赖,并修改配置文件:

> npm i -D postcss-loader@4 postcss-preset-env@6

// webpack.config.js
// +
const postcssPresetEnv = require('postcss-preset-env');
// +
const postcssLoader
564
= {
loader: 'postcss-loader',
options: {
// postcss 只是个平台,具体功能需要使用插件
// Set PostCSS options and plugins
postcssOptions:{
plugins:[
// 配置插件 postcss-preset-env
[
"postcss-preset-env",
{
// 自动前缀。默认是true
// autoprefixer: true,
// 根据您所支持的浏览器来确定需要哪些polyfill。这里仅做演示
browsers: 'ie >= 8, chrome > 10',
// stage 默认是 2
// stage:2
},
],
]
}
}
}

module.exports = {
module: {
rules: [
{
test: /\.css$/i,
use: [
MiniCssExtractPlugin.loader,
"css-loader",
// + 放在css-loader后面
postcssLoader
]
},
{
test: /\.less$/i,
loader: [
MiniCssExtractPlugin.loader,
"css-loader",
// +
postcssLoader,
"less-loader",
],
},
]
},
};

接着修改 a.css 和 a.less,重新启动服务器:

// a.css
body{
color: lch(53 105 40);
}

// a.less
body{
p{
transform: scale(1, 2);
}
}

// 启动服务
> npm run dev

在新开的页面中,我们 56c 看到红色文字”请查看控制台“,而且文字纵向拉长了一倍。通过浏览器查看 main.css 源码如下:

body{
color: rgb(250, 0, 4);
}
body p {
-webkit-transform: scale(1, 2);
-ms-transform: scale(1, 2);
transform: scale(1, 2);
}

至此,增加前缀以及立即使用明天的CSS都已经完成。

Tip:stage(阶段)可以是0(实验)到4(稳定),默认是2,如果我们改为3或4,重新打包,

lch(53 105 40);
则不会转为
rgb(250, 0, 4)
;将plugins换成下面的写法效果相同。

plugins:[
postcssPresetEnv({
browsers: 'ie >= 8, chrome > 10',
})
]

postcss-preset-env 支持任何标准的 browserslist 配置,可以是 .browserslistrc 文件,package.json 中的browserslist 键或 browserslist 环境变量。

如果将

browsers: 'ie >= 8, chrome > 10',
注释,browsers 将使用默认的 browserslist 查询(即
> 0.5%, last 2 versions, Firefox ESR, not dead
),重新构建,则不会添加前缀。

如果不想在 browsers 中写,在 package.json 中的 browserslist 中配置也是可以的:

// package.json
{
...
"bro
ad8
wserslist": [
"ie >= 8",
"chrome > 10"
]
}

注:package.json 不能写注释,本文在 package.json 中的注释仅作说明。

如果觉得 package.json 写的内容太多,我们甚至可以将这部分提取到一个单独的文件中来写:

// .browserslistrc
// from github browserslist
# Browsers that we support

ie >= 8
chrome > 10

最后,如果我们针对开发环境和生成环境做不同的处理,比如开发环境支持 ie8+,而生产环境支持 chrom10+,我们可以这么写:

// .browserslistrc
# Browsers that we support

[production]
chrome > 10

[development]
ie >= 8

然后在配置文件中通过 process.env 来指定环境:

// +
// process.env属性返回一个包含用户环境的对象
process.env.NODE_ENV = 'development' // or production

Browserslist将根据BROWSERSLIST_ENV或NODE_ENV变量选择,所以设置

process.env.BROWSERSLIST_ENV
也是可以的。

再次打包

npm run build
,则只会针对 ie,生成的 main.css 内容如下:

body{
color: rgb(250, 0, 4);
}
body p {
-ms-transform: scale(1, 2);
transform: scale(1, 2);
}

压缩 css

如果我们需要压缩 css 代码,可以使用

optimize-css-assets-webpack-plugin
,用于优化或最小化 css。

首先安装依赖,然后修改配置:

> npm i -D optimize-css-assets-webpack-plugin@5

// webpack.config.js
// +
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');

plugins: [
new HtmlWebpackPlugin({
template: 'src/index.html'
}),
new MiniCssExtractPlugin(),
// +
new OptimizeCssAssetsPlugin()
],

重新打包,原来的 main.css 则变成一行,请看:

> npm run build

// main.css(优化前)
body{
/* 注释 */
color: rgb(250, 0, 4);
}
body p {
-ms-transform: scale(1, 2);
transform: scale(1, 2);
}

// main.css(优化后)
body{color:#fa0004}body p{-ms-transform:scaleY(2);transform:scaleY(2)}

优化后,css 变成了一行,注释也删除了。

打包图片

前端资源通常有图片,由于 webpack 只识别 javascript,所以需要 loader 来帮们识别图片。

我们使用

url-loader
,能将图片转为 base64。

首先安装依赖,并修改配置文件:

> npm i -D url-loader@4

// webpack.config.js
module: {
rules: [
...
// +
{
test: /\.(png|jpg|gif)$/i,
use: [
{
loader: 'url-loader',
options: {
// 指定文件的最大大小(以字节为单位)
limit: 1024*7,
},
},
],
},
]
},
接着引入图片,启动服务:

// 引入图片。src/6.68kb.png

// a.less
body{
p{
transform: scale(1, 2);
}
.m-box{display:block;width:100px;height:100px;}
.img-from-less{background:url(./6.68kb.png) no-repeat;background-size:100% 100%;}
}

// index.html
...
<body>
<p>请查看控制台</p>
<span class='m-box img-from-less'></span>
</body>
...

// 启动服务
> npm run dev

Tip: 笔者的图片大小为 6.68kb,上面的 limit 只需要大于6.68kb即可

在新开的页面中,我们在”请查看控制台“文字下面看见了我们设置的图片。通过检查元素会发现这张图片是 base64。

body .img-from-less{
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgA... no-repeat;
...
}

如果将

limit: 1024*7
修改为
limit: 1024*6
(也就是将 limit 设置的比图片的 size 更小),再次运行
npm run dev
,会发现报错了。还会提示找不到 file-loader。这是因为这张图片(6.68kb.png)大于 1024*6,所以就不会被打包成 base64,所以需要 file-loader 来处理。

安装依赖包

npm i -D file-loader@6
,再次启动服务器,页面上又看到我们的图片,而且这次不再是 base64,而是直接生成了一张图片。

body .img-from-less{
background: url(26bd867dd65e26dbc77d1e151ffd36e0.png) no-repeat;
...
}

图片除了在 css 中使用,我们也会通过 img 元素引用,于是我们在 index.html 中新增

<img class='m-box' src="./6.68kb.png" alt="">
再次启动服务,在打开的浏览器页面中发现 img 引用的图片没生效,而且源码也没变化。

这里需要使用 html-loader 这个包,它能让每个被加载的属性(例如:

<img src="image.png"
)能被引入(imported)。

安装依赖包,修改配置:

> npm i -D html-loader@1

// webpack.config.js
module: {
rules: [
...
// +
{
test: /\.html$/i,
loader: 'html-loader',
},
]
},

再次启动服务,就能看到两张一样的图片了。img 的代码变为

<img class="m-box" src="26bd867dd65e26dbc77d1e151ffd36e0.png" alt="">

如果再次将

limit: 1024*6
修改为
limit: 1024*7
,启动服务你会发现这两处图片都变为 base64。

打包 javascript

js 语法检查

有时我们希望团队成员写的 javascript 代码风格一致。

我们可以使用 eslint,它能查找并修复JavaScript代码中的问题;可以自定义 eslint,使其完全按照项目所需的方式工作。代码风格,笔者选用 airbnb,一个流行的 javascript 风格指南(此刻是第 6 名(topics javascript))。

在 webpack 中使用 eslint,需要使用

eslint-webpack-plugin
( eslint-loader废弃了),而 eslint-webpack-plugin 依赖于
eslint

eslint-config-airbnb 默认导出包含我们所有的ESLint规则,包括ECMAScript 6+和React,而 我们不需要使用 react,所以使用

eslint-config-airbnb-base
即可。

首先安装依赖包,修改配置:

// 没有引入 eslint-plugin-import
> npm
ad8
i -D eslint@7 eslint-webpack-plugin@2 eslint-config-airbnb-base@14

// webpack.config.js
// +
const ESLintPlugin = require('eslint-webpack-plugin');

module.exports = {
// ...
plugins: [
new ESLintPlugin({
// 将启用ESLint自动修复功能。此选项将更改源文件
fix: true
})

],
// ...
};

// package.json
{
// +
"eslintConfig": {
"extends": "airbnb-base"
}
}

重新打包

npm run build
,出现了一些警告和错误,核心信息如下:

WARNING in

webpack-example2\src\index.js
6:1  warning  Unexpected console statement  no-console

✖ 6 problems (2 errors, 4 warnings)

ERROR in

webpack-example2\src\index.js
1:8  error  Unexpected use of file extension "js" for "./b.js"  import/extensions

✖ 5 problems (2 errors, 3 warnings)

错误(import/extensions)是不希望使用 js 扩展名,将

./b.js
改为
./b
就好了,可参考issues:import/extensions

警告(no-console)是因为不能出现 console.log。可以通过配置将这个告警关闭:

// package.json
{
"eslintConfig": {
"extends": "airbnb-base",
// +
"rules": {
"no-console": "off",
}
}
}

import/extensions
修复,并将警告关闭,重新打包
npm run build
则不会出现警告和错误。

Tip:打包后,源码也会自动修复,比如 src/index.js 中的 sum() 方法,

a,
后面是多个空格,打包后会合并成一个空格:

function sum(a,    b) {
return a + b;
}
// 需要调用 sum() 方法
// 否则报错:error  'sum' is defined but never used  no-unused-vars
console.log(sum(1, 100));

// 修复后

function sum(a, b) {
return a + b;
}

如果在 js 文件中使用 window ,再次打包会报错,就像这样:

// index.js
// +
setTimeout(() => {
window.location = 'https://www.baidu.com/';
}, 1000);

// 打包
> npm run build
...
error  'window' is not defined  no-undef

可以在配置文件中指定环境来解决这个问题。就像这样:

// package.json
"eslintConfig": {
// +
"env": {
"browser": true
}
}

如果不想写到 package.json,也可以配置到单独的文件(.eslintrc.js)中:

// .eslintrc.js
module.exports = {
"exte
ad8
nds": "airbnb-base",
"rules": {
"no-console": "off"
},
"env": {
"browser": true
}
}

js 兼容性处理

我们想使用 es6 来编写代码,但有的浏览器支持的不够全面,所以我们会将 es6 转成 es5。

接着上面的例子进行,重写 index.js,放入一个箭头函数,再次打包,你会发现 webpack 不会对 es6 语法做处理,const 还是 const,而不是 var:

// src/index.js
const sum = (a, b) => (a + b);
console.log(sum(1, 10));

// sum 还是我们的箭头函数
eval("const sum = (a, b) => (a + b);\nconsole.log(sum(1, 10));\n\n\n//# sourceURL=webpack:///./src/index.js?");

Babel 是一个 JavaScript 编译器。通过它可以让我们使用下一代的 JavaScript 语法编程。

在 webpack 中要使用 babel 就得用

label-loader
。用法(Usage)如下:

// 安装依赖包。没有使用 @babel/core
> npm i -D babel-loader@8 @babel/preset-env@7

// webpack.config.js
module: {
rules: [
// +
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: [
['@babel/preset-env']
]
}
}
}
]
}

重新打包,箭头函数就变成普通函数:

eval("var sum = function sum(a, b) {\n  return a + b;\n};\n\nconsole.log(sum(1, 10));\n\n//# sourceURL=webpack:///./src/index.js?");

如果我们在 js 中使用 Promise,重新打包后 Promise 还是 Promise,而且在不识别 Promise 语法的浏览器中(比如 ie11)运行会报错。

// index.js
Promise.resolve('aaron').then((v) => {
console.log(v);
});

// 重新打包,Promise 还是 Promise
eval("Promise.resolve('aaron').then(function (v) {\n  console.log(v);\n});\n\n//# sourceURL=webpack:///./src/index.js?");

babel 官网提到,使用@babel/polyfill,就可以使用新的内置函数(例如Promise或WeakMap),静态方法(例如Array.from或Object.assign),实例方法(例如Array.prototype.includes)等等。所以这个 polyfill 是我们的解决方案。

但是 @babel/polyfill 废弃了。而 @babel/polyfill 包含 regenerator runtime 和 core-js。

core-js,包括适用于2021年前ECMAScript的polyfill,而且仅加载必需的功能。

在 useBuiltIns 参数中也提到:由于在7.4.0中已 ad8 弃用@ babel/polyfill,因此我们建议直接添加core-js并通过corejs选项设置版本。

于是我们知道 core-js 能解决 Promise 这类问题。

如何使用 core-js ?我们先来介绍一下插件预设

babel 通过将插件(或预设)应用于配置文件来启用Babel的代码转换。比如插件列表中的

es2015
,这是一个集合,包含了箭头函数(arrow-functions)、类(classes)等插件;

而预设(presets)其实是多个插件(plugin)的集合。比如 @babel/preset-env 这种预设则包含了 es2015、es2016、es2017等最新的插件。

最后根据babel-preset-env中的介绍,我们将 core-js 应用上:

// 安装依赖
> npm i -D core-js@3.11

// webpack.config.js
{
loader: 'babel-loader',
options: {
presets: [
[
'@babel/preset-env',
// +
{
// 配置处理polyfill的方式
useBuiltIns: "usage",
// 版本与我们下载的版本保持一致
corejs: { version: "3.11"},
"targets": "> 0.25%, not dead"
}
]
]
}
}

重新打包,dist/main.js 的Size变成 109 Kib,而之前还不到 4kiB。

启动服务,在不支持 Promise 语法的浏览器中,比如 ie11,也能在控制台输入 aaron。

至此 javascript 的兼容处理完毕。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: