6 從loader本質看各種語言處理
語法糖?
6.1 loader的本質
- loader本質是一個方法,接收要處理的資源的內容,處理完畢后給出內容,作為打包結果。
所有的loader(例如babel-loader, url-loader等)export出一個方法。注冊loader去處理某種類型文件。
6.1.1 手搓一個loader
1 定義loader
// mycss-loader\index.js
module.exports = function (cssContent) {console.log("🚀 ~ cssContent:", cssContent);// 將0變為0pxcssContent = cssContent.replace("0", "0px");return cssContent;
}
2 注冊loader
// webpack.config.js
module.exports = {mode: "production", //webpack4以后要指定mode// loadermodule: {rules: [{test: /\.css/,use: [minicss.loader, "css-loader", "./mycss-loader"]},]}
}
最先調用mycss-loader.
注意路徑,mycss-loader在當前目錄下。
3 打包
打包出來的css文件里的padding,變為0px。
6.1.2 loader本質
-
從手搓的mycss-loader可以推測babe-loader
- 【babel-loader編譯】通過babel-loader編譯js,即babel-loader函數接收js文件內容,對es6字符串編譯為es5字符串等等。
-
同理,編譯ts, jsx都是這樣,目標都是轉換為js.
-
基本上各種語言處理可以看做
- 為改語言編寫loader
- 編寫loader配置
6.2 ts編譯打包
6.2.1 安裝ts的編譯loader和編譯庫
命令:npm install typescript ts-loader --save-dev
6.2.2 定義編譯規則
ts規則較多,可以集成到單獨的文件tsconfig.json里。
// webpack.config.js
module.exports = {mode: "development", //webpack4以后要指定mode// loadermodule: {rules: [{test: /\.tsx?$/,//匹配ts,tsx文件use: {loader: 'ts-loader'}}]}
}
// tsconfig.json
// 暫時為空
定義ts文件
// ts1.ts
let a: number = 123;
console.log("🚀 ~ a:", a);
在app.js里引入ts1.ts
// app.js
import b from './a.js'
import "./test.css"
import img1 from "./img/兩狗對視.jpg"
import './ts1.ts';
new Image().src = img1;
(() => {let a = 23;console.log(b);console.log(a);
})()
6.2.3 打包
ts語言在打包文件里編譯為js語言
6.3 規律總結
項目里所有的loader都是如此。面對新的loader,要學會如何使用,只需要查一下相關配置即可。
記義不記形。
7 webpack處理html
前面介紹了webpack處理js、css等,還剩下html。
一個項目在瀏覽器展示,先拿到html文件。
- 項目的3要素
- html
- js
- css
瀏覽器 | html | js | 執行js,創建內容 |
css | 執行css渲染樣式 |
解讀:先拿到項目的html,然后html加載js和css。加載js后執行js,創建內容。加載css后執行css,渲染頁面樣式。
html是起點。
- 需要html做什么
- 提供html模版,復用固定內容
- 打包時生成一個html
- 打包的html自動引入打包后的js, css
7.1 示例
7.1.0 html文件
<!-- index.html -->
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>webpackhtml</title>
</head><body><div id="app"></div>
</body></html>
快捷鍵: !
7.1.1 安裝plugin
- 使用插件處理html而非loader
- 為什么不用loader
- loader本質:webpack只能識別js,讓webpack識別引入的其他文件需要用loader
- 但html不需要webpack識別,而是作為載體去承載打包后的js文件
- 因此webpack不需要識別html,只是用html承接weboack處理的結果(幾乎不可能需要在js文件里import html文件)
- 因此不使用loader處理html
- 需要做什么
- 拿到html模板,將打包后的內容加入到html文件中
因此不是處理html,而是使用html去處理打包結果
安裝命令:npm install html-webpack-plugin --save-dev
7.1.2 引入plugin
// webpack.config.js
const htmlwebpack = require('html-webpack-plugin');
7.1.3 注冊plugin
// webpack.config.js
const htmlwebpack = require('html-webpack-plugin');
module.exports = {plugins: [new htmlwebpack({template: './index.html',// 寫法1,指定html模板// templateContent: function () { // 寫法2,自定義模板,使用頻率較低// return '<div>123</div>'// }filename: "index.html"})]
}
7.1.4 打包
打包結果
打包后的html
可以看到引入了打包后的js和css文件
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>webpackhtml</title>
<script defer src="app1.34fb.bundle.js"></script><link href="test.bundle.css" rel="stylesheet"></head><body><div id="app"></div>
</body></html>
如果mode為production,打包后的index.html會壓縮為一行代碼
7.2 多入口情況
- 傳統vue項目是單入口(單頁面項目)。
- vue項目中頁面的切換,本質還是同一個頁面,只是執行不同的js創建不同的內容
- 多入口,即多個頁面,用于多個html的情況。切換頁面時是切到另外一個html文件。
- 多入口,表示多個js文件。
下面給出多入口處理的示例
7.2.0 多入口html文件
在原來基礎上加入一個app2.js.
// app2.js
console.log("app2.js")
7.2.1 多入口文件打包配置
webpack.config.js的entry添加app2.js.
// webpack.config.js
const htmlwebpack = require('html-webpack-plugin');
module.exports = {// entry: "./app.js",//單入口寫法,入口指定為app.jsentry: { // 多入口寫法:入口名稱+入口文件app1: ["./app.js"],app2: './app2.js'},// entry: ["./app.js", './app2.js'] // 兩個文件同時作為一個入口output: {path: __dirname + '/dist', // 絕對路徑,__dirname是node的全局變量,表示當前目錄的絕對路徑filename: "[name].[hash:4].bundle.js", //將name加到filename里,打包結果文件是app.bundle.js和app2.bundle.js,hash是對文件是否有改變的標志,:4表示截取前4位},mode: "production", //webpack4以后要指定mode// loadermodule: {rules: []},plugins: [new htmlwebpack({template: './index.html',// 寫法1,指定html模板// templateContent: function () { // 寫法2,自定義模板,使用頻率較低// return '<div>123</div>'// }filename: "index.html"})]
}
7.2.2 打包
1 打包結果
打包結果包含兩個js文件。
兩個js文件在html如何加載呢
——都加到index.html里。
2 問題
- 多入口意味著多個html,每個入口js應該有自己的html。
- 正確邏輯:每個打包的js文件都應該有專屬的html,html只引入一個js文件,而不是一個html文件引入2個js文件。
- 正確做法:再new一個html-webpack-plugin插件
app2.js的html模板可以是之前的index.html,也可以指定新的,例如index2.html.
3 正確配置并打包
index2.html就不贅述了,與index.html一樣的。
- webpack.config.js的plugin再加一個plugin,用于給app2.js指定html模板,即index2.html.
- 注意還加了chunks屬性,注意chunks的值要與entry的key對應。
// webpack.config.js
const htmlwebpack = require('html-webpack-plugin');
module.exports = {// entry: "./app.js",//單入口寫法,入口指定為app.jsentry: { // 多入口寫法:入口名稱+入口文件app1: ["./app.js"],app2: './app2.js'},// entry: ["./app.js", './app2.js'] // 兩個文件同時作為一個入口output: {path: __dirname + '/dist', // 絕對路徑,__dirname是node的全局變量,表示當前目錄的絕對路徑filename: "[name].[hash:4].bundle.js", //將name加到filename里,打包結果文件是app.bundle.js和app2.bundle.js,hash是對文件是否有改變的標志,:4表示截取前4位},mode: "production", //webpack4以后要指定mode// loadermodule: {rules: []},plugins: [new htmlwebpack({template: './index.html',// 寫法1,指定html模板// templateContent: function () { // 寫法2,自定義模板,使用頻率較低// return '<div>123</div>'// }filename: "index.html",chunks: ["app1"]}),new htmlwebpack({template: './index2.html',filename: "index2.html",chunks: ["app2"]})]
}
打包完成,可以看到有2個html,每個html各引入對應的js.
7.3 其他配置項
7.3.1 代碼不處理壓縮
打出來的代碼是壓縮后的,不便于閱讀,可以稍加配置(minify屬性)。
// webpack.config.js
const htmlwebpack = require('html-webpack-plugin');
module.exports = {// entry: "./app.js",//單入口寫法,入口指定為app.jsentry: { // 多入口寫法:入口名稱+入口文件app1: ["./app.js"],app2: './app2.js'},// entry: ["./app.js", './app2.js'] // 兩個文件同時作為一個入口output: {path: __dirname + '/dist', // 絕對路徑,__dirname是node的全局變量,表示當前目錄的絕對路徑filename: "[name].[hash:4].bundle.js", //將name加到filename里,打包結果文件是app.bundle.js和app2.bundle.js,hash是對文件是否有改變的標志,:4表示截取前4位},mode: "production", //webpack4以后要指定mode// loadermodule: {rules: []},plugins: [new htmlwebpack({template: './index.html',// 寫法1,指定html模板// templateContent: function () { // 寫法2,自定義模板,使用頻率較低// return '<div>123</div>'// }filename: "index.html",chunks: ["app1"], minify: {collapseInlineTagWhitespace: false, // 是否一行展示removeComments: false, //是否移除注釋removeAttributeQuotes: false, //是否移除屬性之間的多余空格}}),new htmlwebpack({template: './index2.html',filename: "index2.html",chunks: ["app2"]})]
}
配置后打包出來的index.html
// dist\index.html
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>webpackhtml</title>
<script defer="defer" src="app1.506b.bundle.js"></script><link href="test.bundle.css" rel="stylesheet"></head><body><div id="app"></div>
</body></html>
7.3.2 js加載位置
- 可以看到目前script標簽是加載在head標簽里的。是否可以指定js文件的加載位置(默認加載到head標簽)?
- 加入inject屬性,值為body.
// webpack.config.js
const htmlwebpack = require('html-webpack-plugin');
module.exports = {// entry: "./app.js",//單入口寫法,入口指定為app.jsentry: { // 多入口寫法:入口名稱+入口文件app1: ["./app.js"],app2: './app2.js'},// entry: ["./app.js", './app2.js'] // 兩個文件同時作為一個入口output: {path: __dirname + '/dist', // 絕對路徑,__dirname是node的全局變量,表示當前目錄的絕對路徑filename: "[name].[hash:4].bundle.js", //將name加到filename里,打包結果文件是app.bundle.js和app2.bundle.js,hash是對文件是否有改變的標志,:4表示截取前4位},mode: "production", //webpack4以后要指定mode// loadermodule: {rules: []},plugins: [new htmlwebpack({template: './index.html',// 寫法1,指定html模板// templateContent: function () { // 寫法2,自定義模板,使用頻率較低// return '<div>123</div>'// }filename: "index.html",chunks: ["app1"], minify: {collapseInlineTagWhitespace: false, // 是否一行展示removeComments: false, //是否移除注釋removeAttributeQuotes: false, //是否移除屬性之間的多余空格},inject: "body" // 指定js加載位置: body|true(加載到body中), head, false(不加載)}),new htmlwebpack({template: './index2.html',filename: "index2.html",chunks: ["app2"]})]
}
7.3.3 其他配置
html的標題title標簽支持可配置,也可在webpack.config.js設置。
vue項目可以實現處理,這部分就不看了。