一、为什么使用webpack
个人将前端开发分为三个阶段:
1.1 Web1.0
Web1.0前端主要工作:
- 前端主要编写静态页面
- 对于JavaScript的使用,主要是进行表单验证和动画效果制作
1.2 Web2.0之AJAX
伴随着AJAX的诞生,前端的工作模式也发生了很大变化,前端不仅仅是展示界面,而且还可以管理数据以及和用户进行数据的交互。在这样的阶段过程中,诞生了像jQuery这样的优秀前端工具库。
1.3 大前端开发(现代Web开发)
在这个阶段中,前端的工作变得更加多样化和复杂化,例如现在前端不仅仅需要开发PC端Web界面,还有移动端的Web界面,以及小程序和公众号,甚至有些时候还需要做App和桌面客户端。
伴随着需要做的事情越来越多,流程也越来越复杂,因此就会出现一些问题。比如说:
现代Web开发“问题”
-
采用模块化开发
- 不同浏览器对模块化的支持不同,而且模块化本身存在多种实现规范,这些给最终产出带来了影响
-
使用新特性提高效率保证安全性
- 编码过程中,为了提高开发效率,还会使用ES6+、TypeScript、Saas、Less,这些条件浏览器在默认情况下不能正常处理
-
实时监听开发过程使用热更新
-
项目结果打包压缩优化
需要有一个工具站出来解决问题,可以让开发者在入口的地方随心所欲,用个人喜欢的技术栈完成开发,从而不需要关系过程,但是最终的结果可以在浏览器上正常展示,因此这里就会用到打包工具。当前Vue、React、Angular本身集成Webpack。
二、Webpack 上手
Webpack定义:为现代JavaScript应用提供静态模块打包
Webpack功能:
- 打包:将不同类型资源按模块处理进行打包。可以把js、css、img等资源按照模块的方式处理,然后统一的打包输出
- 静态:打包后最终产出静态资源
- 模块:Webpack支持不同规范的模块化开发(ES Module、CommonJS、AMD等)
构件如图目录结构,并编码
Web サーバーでプレビューしたところ、 index.html で使用されていますがtype="module"
、ES Module と CommonJS を同時に認識できないことがわかりました。
このとき、プリインストールされている Webpack が動作するので、コマンドライン ターミナルで Webpack と入力すると、ディレクトリに dist ディレクトリが出力されます。なお、Webpack のパッケージングは、デフォルトではプロジェクトディレクトリ配下の src ディレクトリを探し、index.js をエントリファイルとして見つけ、依存関係をパッケージ化し、dist ディレクトリに出力します。デフォルト。次のように:
(() => {
var o = {
50: o => {
o.exports = () => ({
name: "zce", age: 40 }) } }, e = {
}; function r(s) {
var t = e[s]; if (void 0 !== t) return t.exports; var n = e[s] = {
exports: {
} }; return o[s](n, n.exports, r), n.exports } (() => {
"use strict"; const o = r(50); console.log(30), console.log(100), console.log(o()) })() })();
main.js を観察すると、現在の Webpack では ES6+ の構文互換性の問題が解決されていないことがわかります
この時点で、index.html で導入された js ファイルを dist/main.js に変更します。
<body>
<script src="./dist/main.js"></script>
</body>
三、webpack構成ファイル
- コマンドライン引数によるパッケージ化
yarn Webpack --entry ./src/main.js --output-path ./build
、--entry
エントリ ファイルを指定し、--output-path
入力パスを指定します。
- package.json を介して短いコマンドを構成する
...
"scripts": {
"build": "Webpack --entry ./src/main.js --output-path ./build"
}
...
コマンドライン実行によるyarn build
パッケージ化
- webpack.config.js 構成ファイルを使用して構成します
const path = require('path')
module.exports = {
entry: './src/index.js',
output: {
filename: 'build.js',
path: path.resolve(__dirname, 'dist') // 必须使用绝对路径,不然会Webpack抛出错误
}
}
4. Webpack 依存関係グラフ
ディレクトリ構造:
index.js に lg.js をインポートし、index.html の dist ディレクトリの下に main.js をインポートします。パッケージ化プロセス中に、Webpack は自動的に依存関係を見つけてインポートし、最終的にそれらを main.js としてパッケージ化します。
前述のように、コマンド ライン パラメーターを使用して、エントリ ファイル、出力ディレクトリ、および出力ファイル名を構成できます。ここでは、–config パラメーターを使用して、Webpack 構成ファイルをカスタマイズできます。例えば:
...
{
"scripts" : {
"build": "Webpack --config my-Webpack-config.js"
}
}
...
この時点で、Webpack はパッケージ化のためにmy-Webpack-config.jsから構成を読み取ります。
5. CSSローダー
index.html のスクリプトは、パッケージ化された main.js を導入します。
index.js エントリ ファイルに login.js を導入する
// login.js
function createDom() {
const h2 = document.createElement('h2')
h2.innerHTML = '拉勾教育'
h2.className = 'title'
return h2
}
document.body.appendChild(createDom())
この時点で、css スタイルを .title に追加する必要があります。
/*
login.css
*/
.title {
color: red
}
login.js に login.css を導入し、Webpack でパックすると、例外がスローされ、css ファイルはモジュールではなくなります。
import '../css/login.css'
function login() {
const oH2 = document.createElement('h2')
oH2.innerHTML = '拉勾教育前端'
oH2.className = 'title'
return oH2
}
document.body.appendChild(login())
ローダーとは
ローダーは、特定のロジックを実装するために内部で js を使用するモジュールです. たとえば、login.css コードを Webpack が認識できるモジュールに変換するためにローダーが必要です.
cssローダー
cssローダーをインストールする
yarn add css-loader
webpack4 でのローダーの使用は、一般的に 3 つのタイプに分けられます。
- インラインローダー
- 設定ファイルローダー
- Webpack-cli コマンドラインでローダーを使用する
Webpack5 では、cli でローダーを使用することは推奨されず、破棄されました。
行内のローダーを使用してください。複数のローダーは英語で区切られています **!**
import 'css-loader!../css/login.css'
function login() {
const oH2 = document.createElement('h2')
oH2.innerHTML = '拉勾教育前端'
oH2.className = 'title'
return oH2
}
document.body.appendChild(login())
を再実行するyarn webpack
と、構文エラーはありませんが、スタイルは有効になりません。もう 1 つ必要ですstyle-loader
。
使用する構成ファイルcss-loader
const path = require('path')
module.exports = {
entry: './src/index.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
// {
// test: /\.css$/, // 一般就是一个正则表达式,用于匹配我们需要处理的文件类型
// use: [
// {
// loader: 'css-loader'
// }
// ]
// },
// {
// test: /\.css$/,
// loader: 'css-loader'
// },
{
test: /\.css$/,
use: ['css-loader']
}
]
}
}
6. スタイルローダーの使用
次に、webpack.config.js に戻り、エントリ ファイルのパスを新しく作成された css ファイルにポイントします。次に、ローダー コンポーネントを構成します。テスト値は正規表現 /.css$/ であり、配列をそれぞれ使用して構成しstyle-loader
、style-loader
const path = require('path')
module.exports = {
mode: 'none',
entry: './src/main.css',
output: {
filename: 'bundle.js',
path: path.join(__dirname, 'dist')
},
module: {
// rules数组针对于其它资源模块的加载规则,每个规则对象的都需要设置两个属性。
rules: [
{
test: /.css$/, // test用来去匹配在打包过程当中所遇到的文件路径
// use用来去指定我们匹配到的文件,需要去使用的loader
use: [
'style-loader',
'css-loader'
]
}
]
}
}
コマンド ラインを開始し、yarn Webpack を実行して serve . を実行し、ブラウザーでアクセスして、css が有効になっていることを確認します。
ps : 使用中、複数のローダーが構成されている場合、実行順序は配列の最後の要素から先頭になります。したがって、ここでは css-loader を最後に配置する必要があります。これは、通常どおりにパッケージ化する前に、まず css-loader を介して css コードをモジュールに変換する必要があるためです。
style-loader の作業コードは bundle.js にあり、コードの一部は次のとおりです。
ローダーは、フロントエンド全体のモジュール化を実現するための webpack のコアであり、さまざまなローダーを介してあらゆるタイプのリソースをロードできます。
セブン、レスローダー
プロジェクト内にlessを使ってcssコードを記述して、login.jsにlogin.lessを普通にインポートし、Webpackを使って普通にコンパイルすると、エラーレポートはcss-loaderがない場合と基本的に同じであることがわかりました。Webpack はデフォルトでより少ないファイルのコンパイルをサポートしていないため、この考えに従って、最初により少ないファイルを css ファイルにコンパイルし、次に css-loader と style-loader を一緒に使用して css スタイルを index.html にインポートする必要があります。以下で試してみてください:
最初に less をインストールし、login.less を index.css にコンパイルしてみてください
npm i less -D # 安装less
npx less ./src/css/login.less index.css # 使用less将login.less编译为index.css
次に、login.js に導入します。
// import 'css-loader!../css/login.css'
import '../css/login.css'
import '../css/login.less'
function login() {
const oH2 = document.createElement('h2')
oH2.innerHTML = '拉勾教育前端'
oH2.className = 'title'
return oH2
}
document.body.appendChild(login())
このときパッケージング用の Webpack を実行すると、上記の Webpack が css ファイルをコンパイルできない場合と同じエラーがスローされることがわかります。
最初のアイデアに戻ると、より少ないファイルを css ファイルにコンパイルするために less-loader が必要であり、次に style-loader で css-loader を使用して css スタイルを html ファイルにコンパイルするため、構成が必要であり、構成の考え方は同じですcssとして。次のコード:
const path = require('path')
module.exports = {
entry: './src/index.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /\.less$/,
use: ['style-loader', 'css-loader', 'less-loader']
}
]
}
}
ps: ローダーの読み込み順序は、右から左、下から上です。
8. .browserslistrc (異なるプラットフォームとの互換性を支援) ワークフロー - 互換性の処理
.browserslistrc は、異なるフロントエンド ツール間でターゲット ブラウザーとノード バージョンを共有するための構成ファイルです。主に次のツールで使用されます。
Autoprefixer
Babel
post-preset-env
eslint-plugin-compat
stylelint-unsupported-browser-features
postcss-normalize
Webpack はデフォルトで browserlistrc をインストールします
フロントエンド エンジニアリングは package.json で構成する必要があります
{
"browserslist": [
"last 1 version",
"> 1%",
"maintained node versions",
"not dead"
]
}
.browserslistrc ファイルで設定することもできます
# 注释是这样写的,以#号开头
last 1 version #最后的一个版本
> 1% #代表全球超过1%使用的浏览器
maintained node versions #所有还被 node 基金会维护的 node 版本
not dead
構成されていない場合、デフォルトは次のとおりです: > 0.5%、最新の 2 つのバージョン、Firefox ESR、デッドではない
現在のディレクトリでターゲット ブラウザをクエリしますnpx browserslist
クエリ条件のリスト
次のクエリ条件を使用して、ブラウザとノードのバージョン範囲を制限できます (大文字と小文字は区別されません)。
> 5%: 基于全球使用率统计而选择的浏览器版本范围。>=,<,<=同样适用。
> 5% in US : 同上,只是使用地区变为美国。支持两个字母的国家码来指定地区。
> 5% in alt-AS : 同上,只是使用地区变为亚洲所有国家。这里列举了所有的地区码。
> 5% in my stats : 使用定制的浏览器统计数据。
cover 99.5% : 使用率总和为99.5%的浏览器版本,前提是浏览器提供了使用覆盖率。
cover 99.5% in US : 同上,只是限制了地域,支持两个字母的国家码。
cover 99.5% in my stats :使用定制的浏览器统计数据。
maintained node versions :所有还被 node 基金会维护的 node 版本。
node 10 and node 10.4 : 最新的 node 10.x.x 或者10.4.x 版本。
current node :当前被 browserslist 使用的 node 版本。
extends browserslist-config-mycompany :来自browserslist-config-mycompany包的查询设置
ie 6-8 : 选择一个浏览器的版本范围。
Firefox > 20 : 版本高于20的所有火狐浏览器版本。>=,<,<=同样适用。
ios 7 :ios 7自带的浏览器。
Firefox ESR :最新的火狐 ESR(长期支持版) 版本的浏览器。
unreleased versions or unreleased Chrome versions : alpha 和 beta 版本。
last 2 major versions or last 2 ios major versions :最近的两个发行版,包括所有的次版本号和补丁版本号变更的浏览器版本。
since 2015 or last 2 years :自某个时间以来更新的版本(也可以写的更具体since 2015-03或者since 2015-03-10)
dead :通过last 2 versions筛选的浏览器版本中,全球使用率低于0.5%并且官方声明不在维护或者事实上已经两年没有再更新的版本。目前符合条件的有 IE10,IE_Mob 10,BlackBerry 10,BlackBerry 7,OperaMobile 12.1。
last 2 versions :每个浏览器最近的两个版本。
last 2 Chrome versions :chrome 浏览器最近的两个版本。
defaults :默认配置> 0.5%, last 2 versions, Firefox ESR, not dead。
not ie <= 8 : 浏览器范围的取反。
可以添加not在任和查询条件前面,表示取反
注:
1. 次の書き込み方法を使用して、browserslist 構成を出力する別のパッケージから構成データをインポートできます。
"browserslist": [
"extends browserslist-config-mycompany"
]
セキュリティ上の理由から、追加の構成パッケージは、browserslist-config- で始まるパッケージ名のみをサポートします。npm パッケージ スコープは、@scope/browserslist-config もサポートします。例:
@scope/browserslist-config or @scope/browserslist-config-mycompany.
#When writing a shared Browserslist package, just export an array.
#browserslist-config-mycompany/index.js:
module.exports = [
'last 1 version',
'> 1%',
'ie 10'
]
2. 環境の差別化された構成 環境
ごとに異なるブラウザー クエリ条件を構成できます。Browserslist は、ブラウザのバージョン範囲を照会するために BROWSERSLIST_ENV または NODE_ENV に依存します。2 つの環境変数のどちらも正しいクエリ条件で構成されていない場合、最初に production に対応する構成アイテムからクエリ条件が読み込まれ、そうでない場合は既定の構成が適用されます。
package.json 内:
"browserslist": {
"production": [
"> 1%",
"ie 10"
],
"development": [
"last 1 chrome version",
"last 1 firefox version"
]
}
構成ファイル.broswerslistrc
で
[production staging]
> 1%
ie 10
[development]
last 1 chrome version
last 1 firefox version
九、ポストCSSワークフロー
公式ウェブサイトには、「PostCSS、JavaScript を使用して CSS を処理するためのフレームワーク」と記載されています。この文は PostCSS の役割を高度に要約していますが、抽象的すぎます。私が理解している限り、PostCSS は主に次の 3 つのことを行います。
parse
: CSS ファイルの文字列は、Abstract Syntax Tree (Abstract Syntax Tree) のフレームワークに解析されます. 解析プロセス中に、CSS 構文が正しいかどうかがチェックされます. 正しくない場合は、エラー メッセージが表示されます. .runPlugin:
プラグイン機能を実行します。PostCSS 自体は特定のタスクを処理しません。特定のプロパティまたはルールにちなんで名付けられたイベントを提供します。特定の機能を持つプラグイン (autoprefixer、CSS モジュールなど) は、イベント リスナーを登録します。PostCSS はこの段階で AST を再スキャンし、登録されたリスナー関数を実行します。generate
: プラグインが AST を処理した後、PostCSS は処理された AST オブジェクトを CSS 文字列に変換します。
「プラグインがない場合」、渡される最初の CSS 文字列は、generate によって生成された CSS 文字列と同じです。PostCSS 自体は特定のタスクを処理するわけではなく、さまざまなプラグインを接続する場合にのみ実用的であることがわかります。
第 1 段階: 解析
CSS 構文の簡単な紹介
CSS ルール セットは、セレクターと宣言ブロックで構成されます。
- セレクターは、スタイルを設定する必要がある HTML 要素を指します。
- 宣言ブロックには、セミコロンで区切られた 1 つ以上の宣言が含まれます。
- 各宣言は、コロンで区切られた CSS プロパティ名と値で構成されます。
- 複数の CSS 宣言はセミコロンで区切り、宣言のブロックは中括弧で囲みます。
5種類のオブジェクト
AST は、CSS 構文を 5 つのクラスのオブジェクトで記述します。以下に具体的な例を示し、対応する AST の結果を出力して、5 種類の AST オブジェクトと CSS 構文の対応を理解します。
app.css ファイルに次のように記述します。
@import url('./app-02.css');
.container {
color: red;
}
宣言オブジェクト
宣言オブジェクトは、CSS の各宣言ステートメントを記述するために使用されます。
- type は現在のオブジェクトの型を示します
- 親は親オブジェクトのインスタンスを記録します
- prop は宣言にプロパティ名を記録します
- value レコード宣言の値
- raws フィールドは、宣言前の文字列、宣言属性と値の間の記号の文字列を記録します。
- 残りのフィールドについては、コード内のコメントで説明されています。
上記の CSS ファイルは、color: red;
次のオブジェクトとして記述されます。
{
parent: Rule, // 外层的选择器被转译成 Rule 对象,是当前声明对象的 parent
prop: "color", // prop 字段记录声明的属性
raws: {
// raws 字段记录声明前、后的字符串,声明属性和值之间的字符串,以及前边语句是否分号结束。
before: '\n ', // raws.before 字段记录声明前的字符串
between: ': ', // raws.between 字段记录声明属性和值之间的字符串
},
source: {
// source 字段记录声明语句的开始、结束位置,以及当前文件的信息
start: {
offset: 45, column: 3, line: 4 },
end: {
offset: 55, column: 13, line: 4 },
input: Input {
css: '@import url('./app-02.css');\n\n.container {\n color: red;\n}',
file: '/Users/admin/temp/postcss/app.css',
hasBOM: false,
Symbol(fromOffsetCache): [0, 29, 30, 43, 57]
}
},
Symbol('isClean'): false, // Symbol(isClean) 字段默认值都是 false,用于记录当前对象关联的 plugin 是否执行。plugin 会在后续解释
Symbol('my'): true, // Symbol(my) 字段默认值都是 true,用于记录当前对象是否是对应对象的实例,如果不是,可以根据类型把对象的属性设置为普通对象的 prototype 属性
type: 'decl', // type 记录对象类型,是个枚举值,声明语句的 type 固定是 decl
value: "red" // value 字段记录声明的值
}
各フィールドの意味と機能は、コメントの形で説明されています。
ルール オブジェクト
ルール オブジェクトはセレクターを記述します。
- レコード オブジェクトのタイプを入力します
- 親は親オブジェクトのインスタンスを記録します
- ノードはサブオブジェクトのインスタンスを記録します
- selector レコードセレクター文字列
- raws は、セレクターの前の文字列、セレクターと中括弧の間の文字列、最後の宣言と右中括弧の間の文字列を記録します。
- 残りのフィールドについては、コード内のコメントで説明されています。
上記の app.css ファイルの postcss 変換後の .container のオブジェクトは次のとおりです (各フィールドの意味と機能はコメントの形式で説明されています)。
{
nodes: [Declaration], // nodes 记录包含关系,Rule 对象包含 Declaration 对象
parent: Root, // 根对象是 Root 对象,是当前声明对象的 parent
raws: {
// raws 字段记录如下
before: '\n\n', // raws.before 字段记录选择器前的字符串
between: ' ', // raws.between 字段记录选择器和大括号之间的字符串
semicolon: true, // raws.semicolon 字段记录前置声明语句是正常分号结束
after: '\n' // raws.after 字段记录最后一个声明和结束大括号之间的字符串
},
selector:'.container', // selector 记录 selector
source: {
// source 字段记录选择器语句的开始、结束位置,以及当前文件的信息
start: {
offset: 30, column: 1, line: 3 },
input: Input {
css: '@import url('./app-02.css');\n\n.container {\n color: red;\n}',
file: '/Users/admin/temp/postcss/app.css',
hasBOM: false,
Symbol(fromOffsetCache): [0, 29, 30, 43, 57]
},
end: {
offset: 57, column: 1, line: 5 }
},
Symbol('isClean'): false, // Symbol(isClean) 字段默认值都是 false,用于记录当前对象关联的 plugin 是否执行。plugin 会在后续解释
Symbol('my'): true, // Symbol(my) 字段默认值都是 true,用于记录当前对象是否是对应对象的实例,如果不是,可以根据类型把对象的属性设置为普通对象的 prototype
type: 'rule' // type 记录对象类型,是个枚举值,声明语句的 type 固定是 rule
}
ルート オブジェクト
Root オブジェクトは、AST オブジェクトのルート オブジェクトです。
type は現在のオブジェクトのタイプを記録し、
nodes 属性は子ノードに対応するオブジェクトのインスタンスを記録します。
上記の app.css ファイルのルート オブジェクトは次のとおりです (各フィールドの意味と機能はコメントの形式で説明されています)。
{
nodes: [AtRule, Rule], // nodes 记录子对象(选择器和 @开头的对象),AtRule 对象会在后边提到
raws: {
// raws 字段记录如下
semicolon: false, // raws.semicolon 最后是否是分号结束
after: '' // raws.after 最后的空字符串
},
source: {
// source 字段记录根目录语句的开始,以及当前文件的信息
start: {
offset: 0, column: 1, line: 1 },
input: Input {
css: '@import url('./app-02.css');\n\n.container {\n color: red;\n}',
file: '/Users/admin/temp/postcss/app.css',
hasBOM: false,
Symbol(fromOffsetCache): [0, 29, 30, 43, 57]
}
},
Symbol('isClean'): false, // Symbol(isClean) 字段默认值都是 false,用于记录当前对象关联的 plugin 是否执行。plugin 会在后续解释
Symbol('my'): true, // Symbol(my) 字段默认值都是 true,用于记录当前对象是否是对应对象的实例,如果不是,可以根据类型把对象的属性设置为普通对象的 prototype
type: 'root' // type 记录对象类型,是个枚举值,声明语句的 type 固定是 root
}
AtRule オブジェクト
CSS のセレクターに加えて、 、 、など @
で始まるタイプの構文もあり、PostCSS はこのタイプの構文を AtRule オブジェクトに解析します。 @import
@keyframes
@font-face
- type は現在のオブジェクトの型を記録します
- parent は、現在のオブジェクトの親オブジェクトを記録します
- name は @ に続く単語を記録します
- params レコード名 値
たとえば、次のオブジェクトに解析され@import url("./app-02.css");
ます。
{
name: "import", // name 记录 @ 紧跟着的单词
params: "url('./app-02.css')", // params 记录 name 值
parent: Root, // parent 记录父对象
raws: {
// raws 字段记录如下
before: '', // raws.before 记录 @语句前的空字符串
between: '', // raws.between 记录 name 和 { 之间的空字符串
afterName: '', // raws.afterName 记录 name 和 @ 语句之间的空字符串
after: '', // raws.after 记录大括号和上一个 rule 之间的空字符串
semicolon: false // raws.semicolon 上一个规则是否是分号结束
},
source: {
// source 字段记录@语句的开始,以及当前文件的信息
start: {
offset: 0, column: 1, line: 1 },
end: {
offset: 27, column: 28, line: 1 },
input: Input {
css: '@import url('./app-02.css');\n\n.container {\n color: red;\n}',
file: '/Users/admin/temp/postcss/app.css',
hasBOM: false,
Symbol(fromOffsetCache): [0, 29, 30, 43, 57]
}
},
Symbol('isClean'): false, // Symbol(isClean) 字段默认值都是 false,用于记录当前对象关联的 plugin 是否执行。plugin 会在后续解释
Symbol('my'): true, // Symbol(my) 字段默认值都是 true,用于记录当前对象是否是对应对象的实例,如果不是,可以根据类型把对象的属性设置为普通对象的 prototype
type: 'atrule' // type 记录对象类型,是个枚举值,声明语句的 type 固定是 atrule
}
コメント オブジェクト
css ファイル内のコメントは Comment オブジェクトに解析されます。テキスト フィールドには、コメントの内容が記録されます。/* 你好 */
次のように解析されます。
{
parent: Root, // parent 记录父对象
raws: {
// raws 字段记录如下
before: '', // raws.before 记录注释语句前的空字符串
left: ' ', // raws.left 记录注释语句左侧的空字符串
right: ' ' // raws.right 记录注释语句右侧的空字符串
},
source: {
// source 字段记录注释语句的开始、结束位置,以及当前文件的信息
start: {
…}, input: Input, end: {
…}
},
Symbol('isClean'): false, // Symbol(isClean) 字段默认值都是 false,用于记录当前对象关联的 plugin 是否执行。plugin 会在后续解释
Symbol('my'): true, // Symbol(my) 字段默认值都是 true,用于记录当前对象是否是对应对象的实例,如果不是,可以根据类型把对象的属性设置为普通对象的 prototype
text: '你好', // text 记录注释内容
type: 'comment' // type 记录对象类型,是个枚举值,声明语句的 type 固定是 comment
}
5 種類のオブジェクト間の継承関係を示す
前の段落からわかるように、CSS は Declaration、Rule、Root、AtRule、および Comment オブジェクトに解析されます。これらのオブジェクトには多くのパブリック メソッドがありますが、PostCSS はオブジェクト指向の継承の考え方を使用して、パブリック メソッドとパブリック プロパティを親クラスに抽出します。
Root、Rule、AtRule はいずれも子ノードを持つことができ、いずれも nodes 属性を持ち、この 3 つは Container クラスを継承しており、ノードに対する操作メソッドはすべて Container クラスに記述されています。Container, Declaration, and Comment are inherited from the Node class. すべてのオブジェクトには、Symbol('isClean')、Symbol('my')、raws、source、type 属性、および toString() や error() などのメソッドがあります。属性とメソッドは Node クラスで定義されます。
Container と Node は、パブリック プロパティとメソッドを抽出するために使用され、それらのインスタンスは生成されません。
5 つのクラス間の継承関係を下の図に示します。
図にはクラスを網羅的に列挙する方法はなく、興味のある学生はソース コード ファイルを直接見ることができます: https://github.com/postcss/postcss/ツリー/メイン/ライブラリ .
CSS 構文を AST オブジェクトに解析するための特定のアルゴリズム
postcss/lib/parser.js
アルゴリズムに対応するソース コード内の位置は次のとおりです。parse
コードの量は大きくないので、自分で確認できます。
第 2 段階: runPlugin
PostCSS 自体は特定のタスクを処理するわけではなく、さまざまなプラグインを接続する場合にのみ役立ちます。
PostCSS が CSS 文字列を AST オブジェクトに解析した後、AST オブジェクトの片側をスキャンし、AST オブジェクトの各タイプに対応するリスナーを設定できます。特定のタイプのオブジェクトにトラバースするとき、オブジェクトのリスナーがあれば、そのリスナーが実行されます。
ファーストクラスのリスナー
PostCSS が提供する「特定のプロパティまたはルールにちなんで名付けられた」イベントリスナーは次のとおりです。
CHILDREAN 子ノードのイベント リスナーを表します。
// root
['Root', CHILDREN, 'RootExit']
// AtRule
['AtRule', 'AtRule-import', CHILDREN, 'AtRuleExit', 'AtRuleExit-import']
// Rule
['Rule', CHILDREN, 'RuleExit']
// Declaration
['Declaration', 'Declaration-color', 'DeclarationExit', 'DeclarationExit-color']
// Comment
['Comment', 'CommentExit']
PostCSS は AST ツリーを深さ優先でトラバースします。
- ルート ルート オブジェクトへのトラバース。最初のステップでは、プラグインによって登録されたすべてのルート イベント リスナーが実行されます。2 番目のステップでは、ルートにサブオブジェクトがあるかどうかがチェックされます。サブオブジェクトに対応するイベント リスナー; サブオブジェクト オブジェクトがない場合は、直接 3 番目の手順に進み、3 番目の手順では、すべてのプラグインによって登録された RootExit イベント リスナーが実行されます。プラグインによって登録される Root および RootExit イベントのリスナーは関数のみです。関数の第1引数は現在アクセスしているASTのRootオブジェクト、第2引数はpostcssのResultオブジェクトとその他の属性で、Resultオブジェクトを通じてCSS文字列やoptsなどの情報を取得することができます。
{
Root: (rootNode, helps) => {
},
RootExit: (rootNode, helps) => {
}
}
- Rule オブジェクトへのトラバースは、ルート ルート オブジェクトへのアクセスと同じロジックです。最初にすべてのプラグインによって登録された Rule イベント リスナーを実行し、次に子オブジェクトをトラバースし、最後にすべてのプラグインによって登録された RuleExit イベント リスナーを実行します。プラグインによって登録される Rule および RuleExit イベントのリスナーは、関数のみです。
{
Rule: (ruleNode, helps) => {
},
RuleExit: (ruleNode, helps) => {
}
}
- AtRule オブジェクトにトラバースします。プラグインによって登録される AtRule のイベント リスナーは、関数またはオブジェクトです。オブジェクト型リスナー。オブジェクト プロパティのキーは AtRule オブジェクトの名前の値で、値は関数です。AtRuleExit も同じロジックです。イベントの実行順序は次のとおりです
['AtRule', 'AtRule-import', CHILDREN, 'AtRuleExit', 'AtRuleExit-import']
。CHILDREAN は、子ノードのイベントを表します。```// function { AtRule: (atRuleNode, help) => {} }
// 对象
{
AtRule: {
import: (atRuleNode, helps) => {
},
keyframes: (atRuleNode, helps) => {
}
}
}
- Declaration オブジェクトにトラバースします。プラグインによって登録された Declaration のイベント リスナーは、関数またはオブジェクトにすることができます. object プロパティのキーは Declaration オブジェクトの prop 値であり、値は関数です. DeclarationExitExit は同じロジックです。イベントの実行順序は次のとおりです
['Declaration', 'Declaration-color', 'DeclarationExit', 'DeclarationExit-color']
。宣言にはサブオブジェクトがなく、現在のオブジェクトのイベントを実行するだけでよく、サブオブジェクトのイベントを詳細に実行する必要はありません。
// 函数
{
Declaration: (declarationNode, helps) => {
}
}
// 对象
{
Declaration: {
color: (declarationNode, helps) => {
},
border: (declarationNode, helps) => {
}
}
}
- Comment オブジェクトにトラバースします。すべてのプラグインで登録されている Comment イベント リスナーを順番に実行してから、すべてのプラグインで登録されている CommentExit イベント リスナーを実行します。
2 番目のタイプのリスナー
特定のプロパティまたはルールにちなんで名付けられたイベント リスナーに加えて、PostCSS には次の 4 つがあります。
{
postcssPlugin: string,
prepare: (result) => {
},
Once: (root, helps) => {
},
OnceExit: (root, helps) => {
},
}
PostCSS プラグイン イベントの全体的な実装は次のとおりです。postcssPlugin は[prepare, Once, ...一类事件,OnceExit]
プラグイン名であり、イベント リスナーではありません。
- postcssPlugin: 文字列型、プラグインの名前、プラグインが実行されるとエラーが報告され、どのプラグインがエラーを報告したかをユーザーに確認するメッセージが表示されます。
- prepare: 関数タイプ。準備はすべてのイベントが実行される前に最初に実行され、プラグインの複数のリスナーがデータを共有する場合に使用されます。prepare の入力パラメータは Result オブジェクト、戻り値はリスナーオブジェクトで、Result オブジェクトを介して css string や opts などの情報を取得できます。
{
postcssPlugin: "PLUGIN NAME",
prepare(result) {
const variables = {
};
return {
Declaration(node) {
if (node.variable) {
variables[node.prop] = node.value;
}
},
OnceExit() {
console.log(variables);
},
};
},
};
- Once: 準備の後、イベントのクラスの前に実行される関数タイプ。一度だけ実行されます。
{
Once: (root, helps) => {
}
}
プラグイン ソース コードのスクリーンショット
現時点では、市場で人気のある postcss ベースのツールを見てください。
- 自動接頭辞
- postcss-import-parser
- postcss-モジュール
- postcss-モジュール
多くの postcss ベースのプラグインがあり、https: //github.com/postcss/postcss/blob/main/docs/plugins.mdで見つけることができます。
第 3 段階: 生成
生成プロセスは引き続き AST オブジェクトを深さ優先でトラバースし、さまざまなインスタンス オブジェクトの文字列をつなぎ合わせます。アルゴリズムの対応するソース コード内の場所は次のとおりです。 のpostcss/lib/stringifier.js
stringify メソッド。コードの量は多くありません。自分で確認できます。
10. postcss-loader は css 互換性を処理します
css3 は自動的にプレフィックス -webkit を付けます
const path = require('path')
module.exports = {
entry: './src/index.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
'css-loader',
{
loader:'postcss-loader',
options:{
// Webpack选项
postcssOptions:{
// loader配置选项
plugins:[
require('autoprefixer')
]
}
}
}
]
},
{
test: /\.less$/,
use: [
'style-loader',
'css-loader',
'postcss-loader',
'less-loader'
]
}
]
}
}
色の 8 進数を処理する
color: #12345678 最後の 2 桁は透明度を指定するために使用されます
postcss-preset-env
postcss-preset-env
プリセットは、すでに含まれているプラグインのコレクションなautoprefixer
ので、そのまま使用できますpostcss-preset-env
const path = require('path')
module.exports = {
entry: './src/index.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
'css-loader',
{
loader:'postcss-loader',
options:{
// Webpack选项
postcssOptions:{
// loader配置选项
plugins:[
require('postcss-preset-env')
]
}
}
}
]
},
{
test: /\.less$/,
use: [
'style-loader',
'css-loader',
'postcss-loader',
'less-loader'
]
}
]
}
}
npm i postcss-preset-env -D
構成ファイルのみを使用して postcss プラグインを構成する
postcss.config.js
module.exports = {
plugins: [
require('postcss-preset-env')
]
}
イレブン、importLoaders 属性
問題:
test.css の内容は次のとおりです。
.title {
transition: all .5s;
user-select: none;
}
login.css の内容は次のとおりです。
/* test.css をインポート */
@import './test.css';
.title {
color: #12345678;
}
繰り返しになりますが、npm run build は、実行後の test.css 内のコードが互換性のために処理されていないことを発見しました。
問題分析:
- login.css @import ステートメントは test.css をインポートします
- login.css は一致する可能性があり、一致すると postcss-loader が動作します
- 現在のコードに基づいて、postcss-loader は login.css のコードを取得し、追加処理なしでフィルター条件に基づいて分析します。
- 最後に、コードは css-loader に渡されます
- この時点で、css-loader は @import メディア、url を処理できます。この時点で、test.css ファイルを再度取得しますが、ローダーは後戻りしません。
- 最後に、処理された css コードを style-loader に渡して表示します
問題の解決: Webpack.config.js を変更して、css-loader のいくつかのプロパティを設定します。
const path = require('path')
module.exports = {
entry: './src/index.js',
output: {
filename: 'build.js',
//output必须设置绝对路径,所以这里导入path模块
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
//简写方式
test: /\.css$/,
//先执行style-loader再执行css-loader
//顺序规则,从右往左,从下往上,因为兼容性处理要在css调用之前,所以需要将postcss-loader的配置放在css-loader右边
use: ['style-loader', {
loader: 'css-loader',
options: {
// css-loader工作时,遇到css文件时,再往前找一个loader,即追回到postcss-loader
importLoaders: 1
}
}, 'postcss-loader']
},
{
//简写方式
test: /\.less$/,
//先执行style-loader再执行css-loader
//顺序规则,从右往左,从下往上
use: ['style-loader', 'css-loader', 'postcss-loader', 'less-loader']
}
]
}
}
再び正常に実行されます。実行結果は以下の通りで、test.cssの内容も正常に修正されています。
12. ファイルローダーが画像を処理する
1 JS は画像をインポートして HTML に書き込みます
img 画像を js ファイルにインポートし、ページに出力します
jpg、png、およびその他の形式の画像を処理するには、対応するローダー: file-loader も必要です。ファイル ローダーの役割は、import/require() などによって導入されたファイル リソースを処理し、出力フォルダーに配置するのを支援することです; もちろん、その名前とフォルダーを変更することもできます
インストールfile-loader
npm install file-loader -D
Image.js に画像をインポートし、ページに表示します。
import oImgSrc from '../img/01.wb.png'
function packImg() {
// 01 创建一个容器元素
const oEle = document.createElement('div')
// 02 创建 img 标签,设置 src 属性
const oImg = document.createElement('img')
oImg.width = 600
// 写法1:使用require...default取值
// require导入默认一个对象,有一个default的键,代表的导入的内容
// oImg.src = require('../img/01.wb.png').default
// 写法2:lg.Webpack.js配置文件搭配使用,不需要写default取值
// esModule: false // 不转为 esModule
// oImg.src = require('../img/01.wb.png')
// 写法3:使用import导入,不需要写default或者config配置esModule
oImg.src = oImgSrc
oEle.appendChild(oImg)
return oEle
}
document.body.appendChild(packImg())
lg.Webpack.js
const path = require('path')
module.exports = {
entry: './src/index.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 1,
esModule: false
}
},
'postcss-loader'
]
},
{
test: /\.less$/,
use: [
'style-loader',
'css-loader',
'postcss-loader',
'less-loader'
]
},
{
test: /\.(png|svg|gif|jpe?g)$/,
// use: [
// {
// loader: 'file-loader',
// options: {
// esModule: false // 不转为 esModule,在js导入时无需写default取值
// }
// }
// ]
use: ['file-loader']
}
]
}
}
2 JS は画像をインポートして css スタイルに設定します
css-loader を処理すると、background-image: url('../img/02.react.png')
デフォルトでは require の形で処理され、require は ESModule を返すため、Webpack の設定に css-loader の属性値を追加する必要があります ->esModule: false
{
test: /\.css$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 1,
esModule: false
}
},
'postcss-loader'
]
},
img.css
.bgBox {
width: 240px;
height: 310px;
border: 1px solid #000;
background-image: url('../img/02.react.png');
}
Image.js
import oImgSrc from '../img/01.wb.png'
import '../css/img.css'
function packImg() {
// 01 创建一个容器元素
const oEle = document.createElement('div')
// 02 创建 img 标签,设置 src 属性
const oImg = document.createElement('img')
oImg.width = 600
// 写法1:使用require...default取值
// require导入默认一个对象,有一个default的键,代表的导入的内容
// oImg.src = require('../img/01.wb.png').default
// 写法2:lg.Webpack.js配置文件搭配使用,不需要写default取值
// esModule: false // 不转为 esModule
// oImg.src = require('../img/01.wb.png')
// 写法3:使用import导入,不需要写default或者config配置esModule
oImg.src = oImgSrc
oEle.appendChild(oImg)
// 03 设置背景图片
const oBgImg = document.createElement('div')
oBgImg.className = 'bgBox'
oEle.appendChild(oBgImg)
return oEle
}
document.body.appendChild(packImg())
lg.Webpack.js
const path = require('path')
module.exports = {
entry: './src/index.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 1,
esModule: false
}
},
'postcss-loader'
]
},
{
test: /\.less$/,
use: [
'style-loader',
'css-loader',
'postcss-loader',
'less-loader'
]
},
{
test: /\.(png|svg|gif|jpe?g)$/,
// use: [
// {
// loader: 'file-loader',
// options: {
// esModule: false // 不转为 esModule
// }
// }
// ]
use: ['file-loader']
}
]
}
}
十三、画像名を設定して出力する
file-loader のオプションを変更して、イメージ名と出力を設定します。
一般的なプレースホルダー:
[ext]: 扩展名
[name]: 文件名称
[hash]: 文件内容+MD4生成128为占位置,作为文件名
[contentHash]: 文件内容+MD4生成128为占位置,作为文件名
[hash:<length>]: hash截取,作为文件名
[path]: 文件路径
lg.Webpack.js
{
test: /\.(png|svg|gif|jpe?g)$/,
use: [
{
loader: 'file-loader',
options: {
name: 'img/[name].[hash:6].[ext]',
// outputPath: 'img'
}
}
]
}
このうち、ディレクトリの書き方には 2 つの方法があり、1 つは を追加する方法outputPath: 'img'
、もう 1 つは名前に直接書いてimg/
再パッケージ化する方法です。
14. url-loader は画像を処理します
URLローダーとは
url-loader
インポートされたファイルはエンコードされて生成されますDataURL
。これは、ファイルを文字列の文字列に変換してから、文字列をJavaScript
.
いつ使用するか
通常、画像またはフォント ファイルを取得するためのリクエストを送信します。画像ファイルが多い場合 (いくつかの など icon
)、頻繁な要求が何度もやり取りされますが、これは不要です。この時点で、これらの小さな画像をローカルに配置することを検討しurl-loader
、base64
これらの画像をコードに渡す方法を使用できます。これにより、リクエストの数が節約され、ページのパフォーマンスが向上します。
使い方
URLローダーをインストールする
npm install url-loader --save-dev
webapck を構成する
module.exports = {
module: {
rules: [
{
test: /\.(png|jpg|gif)$/,
use: [
{
loader: 'url-loader',
options: {
},
},
],
},
],
},
};
ファイルを導入するimport
(または require
)
import logo from '../assets/image/logo.png';
console.log('logo的值: ', logo); // 打印一下看看 logo 是什么
たった3つの簡単なステップ。
奇跡の瞬間を目撃
Webpack
実行後Webpack
、dist
生成されるディレクトリは 1 つだけですbundle.js
。file-loader
違いは、ご紹介した絵が生成されていないことです。前述のように、画像を 1 つに変換してからコードにパックするurl-loader
ことです。DataURL
JavaScript
その後、必要なものがあるbundle.js
かどうかを DataURL:
// bundle.js
(function(module, exports) {
module.exports = "data:image/jpeg;base64.........."; // 省略无数行
})
このモジュールが標準をエクスポートしていることがわかりますDataURL
。
標準の DataURL:data:[<mediatype>][;base64],<data>
この DataURL を介してローカルからこの画像をロードできるため、画像ファイルを dist ディレクトリにパックする必要はありません。
base64 を使用して画像を読み込むことにも、次の 2 つの側面があります。
- 利点: リクエストを保存し、ページのパフォーマンスを向上させます
- デメリット: ローカル ファイルのサイズが大きくなり、読み込みのパフォーマンスが低下する
したがって、トレードオフを行う必要があり、いくつかのsize
小さなbase64
、他の大きな画像のリクエストを送信する必要があります。
url-loader
当然、これはすでに行われており、簡単な構成で上記の要件を達成できます。
オプション
- limit: ファイルのサイズがファイルの処理に使用されるサイズ
limit
よりもfallback
loader
- フォールバック
loader
:limit
より大きいファイルを処理するには、 を指定します。file-loader
設定してみましょう limit
:
{
test: /\.(png|jpg|gif)$/,
use: [
{
loader: 'url-loader',
options: {
name: 'img/[name].[hash:6].[ext]',
limit: 25 * 1024 // 25kb
}
}
]
},
/**
* 01 url-loader base64 uri 文件当中,减少请求次数
* 02 file-loader 将资源拷贝至指定的目录,分开请求
* 03 url-loader 内部其实也可以调用 file-loader
* 04 limit
*/
を導入したので再実行は より大きいので、このファイルを処理するために。イメージは dist ディレクトリにパックされ、返される値はそのアドレスです。Webpack
logo.png
1000
file-loader
(function(module, exports, __Webpack_require__) {
module.exports = __Webpack_require__.p + "dab1fd6b179f2dd87254d6e0f9f8efab.png";
}),
ソースコード分析
file-loader
コードはあまりないので、コピーしてコメントで説明しました。
import {
getOptions } from 'loader-utils'; // loader 工具包
import validateOptions from 'schema-utils'; // schema 工具包
import mime from 'mime';
import normalizeFallback from './utils/normalizeFallback'; // fallback loader
import schema from './options.json'; // options schema
// 定义一个是否转换的函数
/*
*@method shouldTransform
*@param {Number|Boolean|String} limit 文件大小阈值
*@param {Number} size 文件实际大小
*@return {Boolean} 是否需要转换
*/
function shouldTransform(limit, size) {
if (typeof limit === 'boolean') {
return limit;
}
if (typeof limit === 'number' || typeof limit === 'string') {
return size <= parseInt(limit, 10);
}
return true;
}
export default function loader(src) {
// 获取 Webpack 配置里的 options
const options = getOptions(this) || {
};
// 校验 options
validateOptions(schema, options, {
name: 'URL Loader',
baseDataPath: 'options',
});
// 判断是否要转换,如果要就进入,不要就往下走
// src 是一个 Buffer,所以可以通过 src.length 获取大小
if (shouldTransform(options.limit, src.length)) {
const file = this.resourcePath;
// 获取文件MIME类型,默认值是从文件取,比如 "image/jpeg"
const mimetype = options.mimetype || mime.getType(file);
// 如果 src 不是 Buffer,就变成 Buffer
if (typeof src === 'string') {
src = Buffer.from(src);
}
// 构造 DataURL 并导出
return `module.exports = ${
JSON.stringify(
`data:${
mimetype || ''};base64,${
src.toString('base64')}`
)}`;
}
// 判断结果是不需要通过 url-loader 转换成 DataURL,则使用 fallback 的 loader
const {
loader: fallbackLoader,
options: fallbackOptions,
} = normalizeFallback(options.fallback, options);
// 引入 fallback loader
const fallback = require(fallbackLoader);
// fallback loader 执行环境
const fallbackLoaderContext = Object.assign({
}, this, {
query: fallbackOptions,
});
// 执行 fallback loader 来处理 src
return fallback.call(fallbackLoaderContext, src);
}
// 默认情况下 Webpack 对文件进行 UTF8 编码,当 loader 需要处理二进制数据的时候,需要设置 raw 为 true
export const raw = true;
十五、資産処理写真
Webpack
の前は、フロントエンド開発者はや などのgrunt
ツールを使用してリソースを処理し、それらをフォルダまたはディレクトリに移動していました。最も優れた機能の 1 つは、インポートに加えて、組み込みのリソース モジュールが他の種類のファイルもインポートです。gulp
/src
/dist
/build
Webpack
JavaScript
Asset Modules
前後にWebpack4
、通常はfile-loader
andを使用して、url-loader
他のリソース タイプをロードできるようにします。
1. 4種類のAsset Modules Type
また、Webpack5 は it と呼ばれるリソース モジュールを使用するのに役立ちます。これにより、などAsset Modules
の他のリソース タイプをパッケージ化できます。字体文件、图表文件、图片文件
その中で、リソース モジュール タイプと呼びますAsset Modules Type
。置換するタイプは全部で 4 つありますloader
。つまり、次のとおりです。
asset/resource:
代わりに、別のファイルとエクスポート URL を送信します。file-loader
asset/inline:
data URI
の代わりにリソースをエクスポートするurl-loader
asset/source:
raw-loader を使用して以前に達成された、リソースのソース コードをエクスポートします。asset:
[Between]asset/resource
とasset/inline
[Between] では、リソースをエクスポートしdata URI
て別のファイルを送信し、URL をエクスポートするかを選択しますurl-loader+limit
。
ただし、これら 4 種類のリソース モジュールを紹介する前に、これらの出力リソース モジュールのファイル名をカスタマイズする方法について説明しましょう。
2 カスタム リソース モジュール名
2.1 アセットモジュールのファイル名
最初の方法は、Webpack
構成で設定しoutput.assetModuleFilename
てテンプレート文字列を変更することです。この場合、assetModuleFilename はデフォルトでファイル名サフィックスのドットを処理するため、手動でドットを追加する必要はありません。このメソッドは公開処理メソッドです. イメージ リソースとフォント リソースを同時に処理する必要がある場合, 一般的なメソッドでは 2 つのリソース タイプが同じディレクトリに配置されます. ここで assetModuleFilename を使用することはお勧めしません.
例えば、写真の出力ファイル名については、ファイル名を示すimages
フォルダの下に出力させることができ、[ext]は画像ファイルの拡張子を示す.png、.jpg、.gif、 [contenthash]
jpegなど、および[query]
可能なパラメータ
output: {
···
assetModuleFilename: 'images/[contenthash][ext][query]'
···
},
2.2 ジェネレータ属性
2 番目の方法は、次のようなmodule.rules
特定のリソース ファイルを内部で構成するときにgeneator
属性を追加することです。
rules: [
{
test: /\.png/,
type: 'asset/resource',
generator: {
filename: 'images/[contenthash][ext][query]'
}
}
]
[注]
generator
よりも優先度が高いassetModuleFilename
3 4種類の輸入品
まず、テスト用に新しいフォルダーを作成します. フォルダーディレクトリは次のとおりです. src の下に新しい assets フォルダーを作成し、その中に事前に用意されたさまざまな種類の画像 index.js を配置します
.
import hello from './hello'
import img1 from './assets/man.jpeg'
import img2 from './assets/store.svg'
import img3 from './assets/women.jpg'
import Txt from './assets/wenzi.txt'
import dynamic from './assets/dongtu.gif'
hello()
const IMG1 = document.createElement('img')
IMG1.src = img1
document.body.appendChild(IMG1)
const IMG2 = document.createElement('img')
IMG2.src = img2
IMG2.style.cssText = 'width:200px;height:200px'
document.body.appendChild(IMG2)
const IMG3 = document.createElement('img')
IMG3.src = img3
document.body.appendChild(IMG3)
const TXT = document.createElement('div')
TXT.textContent = Txt
TXT.style.cssText = 'width:200px;height:200px;backGround:aliceblue'
document.body.appendChild(TXT)
const DYNAMIC = document.createElement('img')
DYNAMIC.src = dynamic
document.body.appendChild(DYNAMIC)
hello.js
function hello(){
console.log("hello-world!!!")
}
export default hello
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>
</body>
</html>
Webpack.config.js
const path = require('path')
const HtmlWebpackPlugin = require('html-Webpack-plugin')
module.exports = {
entry : './src/index.js',
output : {
filename:'bundle.js',
path:path.resolve(__dirname,'./dist'),
clean:true,
//如果不设置,打包完之后资源会直接打包在dist目录下
assetModuleFilename:'images/[contenthash][ext][query]'
},
mode : 'development',
devtool:'inline-source-map',
plugins:[
new HtmlWebpackPlugin({
template:'./index.html',
filename:'app.html',
inject:"body"
})
],
devServer:{
static:'./dist'
},
module:{
rules:[{
test:/\.jpeg$/,
type:"asset/resource",
generator:{
filename:'images/[contenthash][ext][query]'
}
},{
test:/\.svg$/,
type:'asset/inline'
},{
test:/\.txt$/,
type:'asset/source'
},{
test:/\.(gif|jpg)$/,
type:'asset',
parser:{
dataUrlCondition:{
maxSize : 10 * 1024 * 1024
}
}
}]
}
}
3.1 リソース リソース タイプ
アセット/リソースは別のファイルとエクスポート URL を送信できます
画像のタイプを .jpeg サフィックスで設定しasset/resource
、画像を index.js にインポートして本文に挿入します。つまり、リソースとしてページに表示します。
npx Webpack
パッケージング後、イメージは dist フォルダーの下のイメージ ファイルに表示されます。
3.2 インライン リソース タイプ
asset/inline はリソースのデータ URI をエクスポートします
上記の方法に従って、接尾辞が .svg の画像のタイプを asset/inline に設定し、画像を index.js にインポートして本文に挿入します。つまり、リソースとしてページに表示されます。 、そして単にスタイルを設定します
ただし、違いは、npx Webpack がパッケージ化された後、dist フォルダーの下に .svg タイプのイメージがパッケージ化されないことです。
npx Webpack-dev-server --open
ブラウザを自動的に開き、コンソールで画像タイプを確認したところ、アセット/インライン タイプがデータ URI の形式でパスを実際にエクスポートできることがわかりました。
3.3 ソース リソース タイプ
source
リソース、リソースのソース コードをエクスポートします
上記の方法に従って、接尾辞 .txt を持つテキスト ファイルを作成し、タイプを に設定しasset/source
、テキストを index.js にインポートして本文に挿入します。つまり、リソースとしてページに表示されます。スタイルを設定するだけです
ただし、違いは、npx Webpack がパッケージ化された後、dist フォルダーの下に .txt タイプのテキスト ファイルがパッケージ化されないことです。
npx Webpack-dev-server --open
ブラウザーを自動的に開きます。コンソールでテキスト タイプを確認し、そのasset/source
タイプが実際にリソースのソース コードをエクスポートできることを確認します。
3.4 アセットの一般的なリソース タイプ
アセットはアセット/リソースとアセット/インラインの間にあり、別のファイルを送信して URL をエクスポートするか、リソース データ URI をエクスポートするかを選択します
デフォルトでは、Webpack5 は判断する制限として 8k を使用します。
リソースが 8k 以上の場合、自動で を押してasset/resource
判定
リソースが 8k 未満の場合、自動で を押してasset/inline
判定
しきい値を手動で変更し、パーサー (分析) と呼ばれる固定属性を持つオブジェクトを名前に設定できます示唆する、データdataUrlCondition
転送 url になる条件、つまり、bas64 になる条件はmaxSize
Limit に相当します。
module:{
rules:[
···
{
test:/\.(gif|jpg)$/,
type:'asset',
parser:{
dataUrlCondition:{
maxSize : 100 * 1024
}
}
}
···
]
}
ここでは、100 * 1024 または 100kb を臨界値として設定します
【1b * 1024 = 1kb,1kb * 1024 = 1M】
上記の方法に従って、接尾辞.gif
付きの.jpg
写真のタイプをアセット リソース タイプとして設定し、2 つの写真を index.js にインポートして本文に挿入します。つまり、リソースとしてページに表示され、サイズ.gif のサイズは 128.11kb (100kb のしきい値を超えています)、.jpg のサイズは 12kb (100kb のしきい値を超えていません)
npx Webpack がパッケージ化された後、dist フォルダーの下にパッケージ化された .gif タイプの写真がありますが、パッケージ化された .jpg タイプの写真はありません
npx Webpack-dev-server --open
ブラウザーを自動的に開き、コンソールで 2 種類の画像を確認したところ、.gif 画像は単一ファイルの URL パスであり、.jpg 画像はデータ URI 形式の base64 パスであることがわかりました
16、資産処理アイコンのフォント
前述のとおり、フォントアイコンファイルを扱う場合は、リソースリソースとして直接コピーする必要があるため、asset/resource を使用する必要があります。用意されているフォントファイルとそのディレクトリは次のとおりです。
フォントディレクトリにはiconfont.cssとそのフォントファイルが用意されており、iconfont.cssはfont-familyに対応するフォントを割り当てています。
共通の font.js ファイルを分けて、そのファイルに iconfont.css とカスタムの index.css ファイルを導入し、ページの DOM 要素を作成して表示します。
Font.js
import '../font/iconfont.css'
import '../css/index.css'
function packFont() {
const oEle = document.createElement('div')
const oSpan = document.createElement('span')
oSpan.className = 'iconfont icon-linggan lg-icon'
oEle.appendChild(oSpan)
return oEle
}
document.body.appendChild(packFont())
もちろん、この時点で直接実行すると、 Webpackはこの時点で他のリソースをyarn build
認識しないため、間違いなくエラーが報告されます。そのため、パッケージ構成にはアセット/リソースを個別に使用する必要があります。ttf/woff/woff2
lg.Webpack.js
const path = require('path')
module.exports = {
entry: './src/index.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist'),
// assetModuleFilename: "img/[name].[hash:4][ext]"
},
module: {
rules: [
{
test: /\.(ttf|woff2?)$/,
type: 'asset/resource', // 使用资源复制
generator: {
filename: 'font/[name].[hash:3][ext]' // 指定字体文件输出路径
}
}
]
}
}
今回の実行でyarn build
、distディレクトリ配下にfontディレクトリが追加されており、fontディレクトリ内のフォントファイルがWebpackによってコピーされていることがわかりました。ページを開くと、iconfont.css およびカスタム index.css ファイル スタイルが有効になっていることがわかります。
Seventeen、Webpack プラグインの使用
プラグイン メカニズムは、Webpack のもう 1 つのコア機能です。その目的は、Webpack プロジェクトの自動化の機能を強化することです。ローダーは、プロジェクト全体のパッケージ化を実現するために、さまざまなリソース モジュールのロードを担当します。プラグインは、他の問題を解決するために使用されます。プロジェクト内のリソースに加えて、次のような自動化タスク:
- プラグインは、最後のパッケージングの結果である、パッケージ化の前に dist ディレクトリを自動的にクリアするのに役立ちます。
- または、パッケージ化に参加する必要のないリソース ファイルを出力ディレクトリにコピーするために使用できます。
- または、結果出力をパッケージ化するコードを圧縮するために使用できます。
要するに、プラグインの Webpack を使用すると、フロントエンド エンジニアリングで頻繁に使用される部分のほとんどをほぼ全能で実現できるため、多くの初心者が Webpack がフロントエンド エンジニアリングであると理解しているのです。
clean-Webpack-plugin
: dist ディレクトリを自動的にクリアします
以前のテストでは、ユーザーは毎回 dist ディレクトリを手動で削除する必要がありました. Webpack がパックするたびに、最初に以前の dist ディレクトリを削除してからパックすることを願っています. ここでは処理に使用しますclean-Webpack-plugin
.
同様に、最初にインストールする必要がありますclean-Webpack-plugin
yarn add clean-Webpack-plugin -D
次に、使用方法に従ってlg.Webpack.js
プラグインを構成します。最初に require to import を使用しますclean-Webpack-plugin
。ここでは、エクスポートされたものが多すぎて、分解する必要がありますconst { CleanWebpackPlugin } = require('clean-Webpack-plugin')
。次に、エクスポートされた各オブジェクトは、プラグインで使用するときに必要な独自のコンストラクターを持つクラスですnew CleanWebpackPlugin
。コードは以下のように表示されます:
const path = require('path')
const {
CleanWebpackPlugin } = require('clean-Webpack-plugin')
module.exports = {
entry: './src/index.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist'),
// assetModuleFilename: "img/[name].[hash:4][ext]"
},
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 1,
esModule: false
}
},
'postcss-loader'
]
},
{
test: /\.less$/,
use: [
'style-loader',
'css-loader',
'postcss-loader',
'less-loader'
]
},
{
test: /\.(png|svg|gif|jpe?g)$/,
type: 'asset',
generator: {
filename: "img/[name].[hash:4][ext]"
},
parser: {
dataUrlCondition: {
maxSize: 30 * 1024
}
}
},
{
test: /\.(ttf|woff2?)$/,
type: 'asset/resource',
generator: {
filename: 'font/[name].[hash:3][ext]'
}
}
]
},
plugins: [
new CleanWebpackPlugin() // 每个插件就是一个类
]
}
最初yarn build
に使用してパッケージ化し、dist ディレクトリを生成し、dist ディレクトリに a.txt ファイルを手動で追加し、再度実行しyarn build
て正常に動作しています。a.txt
clean-Webpack-plugin
18. html-webapck-plugin の使用
クリーンdist
アップさ、パッケージ化された結果を使用する HTML を自動的に生成するという非常に一般的な要件もあります.これ以前は、HTML はハードコーディングによってプロジェクトのルート ディレクトリに個別に格納されていました。
index.html
各パッケージが完了したら、タイトルを手動で変更する必要があり、パッケージによって生成されたファイルは、パッケージがパッケージ化された後に多数のファイルの種類または数量があるため、手動で変更する必要がありますhtml-Webpack-plugin
。
デフォルトでは、手動でファイルを作成する必要はありません.プラグインをindex.html
使用した後、Webpack は、デフォルトで、パッケージ化された結果の dist ディレクトリに index.html ファイルを自動的に作成します.html-Webpack-plugin
まず用意した index.html を手動で削除 プラグインを使用しない場合はyarn build to package をhtml-Webpack-plugin
実行 観察するとdist
ディレクトリ内にファイルが生成されていないことが判明index.html
1 デフォルトの index.html テンプレートを使用する
構成ファイルでは、最初に html-Webpack-plugin をインポートします。
const HtmlWebpackPlugin = require('html-Webpack-plugin')
プラグイン フィールドで使用します。
const HtmlWebpackPlugin = require('html-Webpack-plugin')
...
plugins: [
new HtmlWebpackPlugin()
]
...
この時点で、パッケージ化プロセスはindex.html ファイルが既にディレクトリに存在するyarn build
ことを確認できます。現時点では、コンテンツはデフォルトで提供されており、 node_modulesにあります。dist
index.html
html-Webpack-plugin
html-Webpack-plugin
default_index.ejs
2 カスタム index.html テンプレートの使用
デフォルト テンプレートのプレースホルダーについては、公式ドキュメントで詳しく説明されています。
プレースホルダーについては、プラグインにパラメーターを渡し、デフォルト値を与えることができます。
new HtmlWebpackPlugin({
title: 'html-Webpack-plugin', // title占位符
})
const path = require('path')
const {
DefinePlugin } = require('Webpack')
const {
CleanWebpackPlugin } = require('clean-Webpack-plugin')
const HtmlWebpackPlugin = require('html-Webpack-plugin')
module.exports = {
entry: './src/index.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist'),
// assetModuleFilename: "img/[name].[hash:4][ext]"
},
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 1,
esModule: false
}
},
'postcss-loader'
]
},
{
test: /\.less$/,
use: [
'style-loader',
'css-loader',
'postcss-loader',
'less-loader'
]
},
{
test: /\.(png|svg|gif|jpe?g)$/,
type: 'asset',
generator: {
filename: "img/[name].[hash:4][ext]"
},
parser: {
dataUrlCondition: {
maxSize: 30 * 1024
}
}
},
{
test: /\.(ttf|woff2?)$/,
type: 'asset/resource',
generator: {
filename: 'font/[name].[hash:3][ext]'
}
}
]
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
title: 'html-Webpack-plugin', // title占位符
})
]
}
再度yarn build
パッケージ化した後、index.html
タイトルを更新しました。この時点で、組み込みの html テンプレート ファイル
を使用しています。html-Webpack-plugin
ただし、実際の使用では、特別なテンプレート ファイルを使用する必要がある場合があります。この時点で、テンプレート フィールドを使用して独自の index.html テンプレートを定義します。この時点でパッケージ
を使用した後、カスタムテンプレート ファイルが使用されます。この時点で、Web サイト アイコンのパスが使用され、グローバル構成の定数を定義するために使用されます (Webpack のデフォルト、インストールの必要はありません)。yarn build
index.html
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
DefinePlugin
new DefinePlugin({
BASE_URL: '"./"'
})
この時点で、完全な構成ファイルは次のようになります。
const path = require('path')
const {
DefinePlugin } = require('Webpack')
const {
CleanWebpackPlugin } = require('clean-Webpack-plugin')
const HtmlWebpackPlugin = require('html-Webpack-plugin')
module.exports = {
entry: './src/index.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist'),
// assetModuleFilename: "img/[name].[hash:4][ext]"
},
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 1,
esModule: false
}
},
'postcss-loader'
]
},
{
test: /\.less$/,
use: [
'style-loader',
'css-loader',
'postcss-loader',
'less-loader'
]
},
{
test: /\.(png|svg|gif|jpe?g)$/,
type: 'asset',
generator: {
filename: "img/[name].[hash:4][ext]"
},
parser: {
dataUrlCondition: {
maxSize: 30 * 1024
}
}
},
{
test: /\.(ttf|woff2?)$/,
type: 'asset/resource',
generator: {
filename: 'font/[name].[hash:3][ext]'
}
}
]
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
title: 'html-Webpack-plugin',
template: './public/index.html'
}),
new DefinePlugin({
BASE_URL: '"./"' // Webpack会将常量原封不动的拿走,所以需要使用引号包裹
})
]
}
再度パッケージ化すると、結果は次のようになります。
出力ファイルの内容をカスタマイズするだけでなく、複数のページ ファイルを同時に出力することも非常に一般的な要件です。実際、構成は非常に単純で、構成ファイルに新しい HtmlWebpackPlugin オブジェクトを追加します。構成は次のとおりです。
plugins: [
new CleanWebpackPlugin(),
// 用于生成 index.html
new HtmlWebpackPlugin({
title: 'Webpack Plugin Sample',
meta: {
viewport: 'width=device-width'
},
template: './src/index.html'
}),
// 用于生成 about.html
new HtmlWebpackPlugin({
filename: 'about.html', // 用于指定生成的文件名称,默认值是index.html
title: 'About html'
})
]
19、コピー Webpack プラグイン
プロジェクトには通常、構築に参加する必要のないいくつかの静的ファイルがあり、オンラインで公開する必要もあります.たとえば、私たちのウェブサイトでは、これらのファイルは通常、プロジェクト ディレクトリに配置されfavicon.icon
ますpublic
.Webpack
パッケージ化すると、出力ディレクトリにもコピーできます。
この種の要求に対して、信頼できるのはcopy-Webpack-plugin
、まずこのプラグインをインストールしてから、このプラグインのタイプをインポートする最後に、このタイプのインスタンスをプラグイン属性に追加するこのタイプのコンストラクターを渡す必要があるコピーする必要があるファイルのパスを指定するために使用される配列. ワイルドカード、ディレクトリ、またはファイルへの相対パスを指定できます. ここではプラグインが使用されます. つまり、パッケージ化時にすべてのファイルがパッケージ化されます. . 出力ディレクトリ内のすべてのファイルを出力ディレクトリにコピーし、再度 Webpack コマンドを実行します. パッケージ化が完了すると、パブリック ディレクトリ内のすべてのファイルが同時に出力ディレクトリにコピーされます.
const path = require('path')
const {
CleanWebpackPlugin } = require('clean-Webpack-plugin')
const HtmlWebpackPlugin = require('html-Webpack-plugin')
const CopyWebpackPlugin = require('copy-Webpack-plugin')
module.exports = {
mode: 'none',
entry: './src/main.js',
output: {
filename: 'bundle.js',
path: path.join(__dirname, 'dist'),
// publicPath: 'dist/'
},
module: {
rules: [
{
test: /.css$/,
use: [
'style-loader',
'css-loader'
]
},
{
test: /.png$/,
use: {
loader: 'url-loader',
options: {
limit: 10 * 1024 // 10 KB
}
}
}
]
},
plugins: [
new CleanWebpackPlugin(),
// 用于生成 index.html
new HtmlWebpackPlugin({
title: 'Webpack Plugin Sample',
meta: {
viewport: 'width=device-width'
},
template: './src/index.html'
}),
// 用于生成 about.html
new HtmlWebpackPlugin({
filename: 'about.html'
}),
new CopyWebpackPlugin({
patterns: [
{
from: 'public',
globOptions: {
ignore: ['**/index.html'] // 必须写入**/, ** 两个星号的意思是在当前路径
}
}
]
})
]
}
20、バベル使用
Webpack
コード内のimport
合計はデフォルトで処理できるためexport
、Webpack が自動的に ES6 コードをコンパイルすると考える人もいるでしょう。実際にはそうではありません。つまり、Webpack はモジュールのパッケージ化作業のみを完了するため、コード内のインポートとエクスポートに対応する変換をいくつか実行し、コード内の他の ES6 機能を変換することはできません。
ES6 コードを ES5 コードにパッケージ化してコンパイルする必要がある場合は、コンパイル済みの他のシェイプ ローダーが必要です。ここでいくつかの追加プラグインをインストールします。
まず、Webpack は ES6+ の構文を認識できます. ここでテストしてみましょう. ES6+ の構文を index.js に記述し、yarn build を使用してパッケージ化します. パッケージ化されたコードを観察すると、Webpack が構文をそのまま保持していることがわかりますindex.js
.ES6+
コードは何も処理せずに持ち込まれました。
したがって、ES6+ 構文では、処理に特別なツールを使用する必要があります. ここでは、コード テスト用の@babel/core
コマンド ライン ツールをインストールして@babel/cli
、babel がデフォルトで ES6+ 構文の処理に役立つかどうかを確認します。
yarn add @babel/core @babel/cli
yarn babel
それを使用した後、babel はまだ ES6+ 構文を処理するのに役立たないことがわかりました.なぜですか? その理由は、babel も処理のために特別なプラグインを使用する必要があるためです。
yarn babel
宛先パス--out-put
出力パス
yarn babel src --out-put build
したがって、矢印関数または const および let キーワードを処理するための特別なプラグインが必要です。
@babel/plugin-transform-arrow-functions
(矢印関数の処理)@babel/plugin-transform-block-scoping
(ブロックレベルのスコープを処理)
yarn add @babel/plugin-transform-arrow-functions @babel/plugin-transform-block-scoping
# 执行babel
yarn babel src --out-dir build --plugins=@babel/plugin-transform-arrow-functions,@babel/plugin-transform-block-scoping
let
再実行すると、アロー関数とキーワードスコープがキーワードconst
に加工されていることがわかります。var
しかし、さまざまな特殊なケースに対処する必要があるたびに、さまざまな babel プラグインをインストールする必要があることがわかりました。これは非常に不便です。したがって、babel は ES6+ 文法とステージ ドラフトに関連するほとんどのプラグインを セット に結合し、@babel/preset-env,
このセットを使用するだけでほとんどのES6+
文法を処理できます。
# 安装@babel/preset-env
yarn add @babel/preset-env
# 使用babel进行编译
yarn babel src --out-dir build --presets=@babel/preset-env