webpack+vite前端构建工具 -6从loader本质看各种语言处理 7webpack处理html
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项目可以实现处理,这部分就不看了。