提到前端的模块化,很多朋友第一时间想到的就是 AMD ,CMD, CommonJS 以及 ES Modules这些。
不过,这些都是js文件的模块化,而一个前端项目并不是只有js文件。
一个前端项目中,除了js文件 还有html,css,图片字体等等各种文件,既然是前端模块化,那么所有的前端资源都需要模块化。
总结一下我们前端项目模块化,需要支持以下几个功能:
前端的模块化打包工具,常见的有 webpack , rollup , parcel
webpack
它的核心特性就能很好的满足上面我们的需求,它为处理资源管理和分割代码而生,可以包含任何类型的文件。灵活,插件多。
rollup
用标准化的格式(es6)来写代码,通过减少死代码尽可能地缩小包体积。
parcel
超快的打包速度,多线程在多核上并发编译,不用任何配置。
打包工具解决的是前端整体的模块化,并不单指 JavaScript 模块化
webpack 依赖node环境运行,首先保证已经安装了node
然后在项目中安装对应的包 webpack 和 webpack-cli
安装包可以使用npm 或者 yarn
具体区别体现在运行上:
默认的约定
直接执行webpack 会默认把当前项目中 src 目录下的 index.js 打包到 dist 目录下 生成 main.js
并且webpack 默认自带处理ES Moudules语法的功能
上面我们说到,默认的打包目录。这个目录是可以通过项目根目录中的 webpack.config.js 这个文件进行修改的。
module.exports = {
entry: './src/main.js',
output: {
filename: 'bundle.js',
path: path.join( __dirname )
}
}
在我们执行webpack打包命令的时候 会发现有一行警告提示:
WARNING in configuration
The ‘mode’ option has not been set, webpack will fallback to ‘production’ for this value.
Set ‘mode’ option to ‘development’ or ‘production’ to enable defaults for each environment.
意思就是说,让我们去设置打包的模式。webpack的打包模式有三种:
设置打包模式可以通过两种方式:
module.exports = {
mode: 'development',
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.join(__dirname, 'dist/')
}
}
如果要想让webpack处理资源模块的打包,那么各种文件对应的loader就必不可少,Loader是webpack的核心特性。
借助于Loader就可以加载任何类型的资源。
加载器大致可以分成三类:
如果我们现在为项目中添加了css文件,也想通过webpack进行打包
那么此时运行webpack打包就会出现报错
ERROR in ./src/style/index.css 1:5
Module parse failed: Unexpected token (1:5)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
这是报错中的 You may need an appropriate loader to handle this file type
这句 意思就是说 你可能需要一个loader来处理这种类型的文件。
比如上面的 css 文件 我们可以借助于 style-loader 和 css-loader 其中 style-loader的作用是把样式添加到dom中, css-loader是用于把css打包到bundle.js中 ( 其实就是读取样式文件内容 转化字符串记录在数组或者对象中 )
module.exports = {
mode: 'none',
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.join(__dirname, 'dist/')
},
module: {
rules: [
{
test: /.css$/,
use: ['style-loader', 'css-loader']
}
]
}
}
在webpack.config.js 中添加module属性 在rules数组里,每一个成员对应了一种需要通过loader处理的文件类型。
test用于匹配对应后缀名的文件, use 表示要使用的loader 这里loader的使用顺序是不能乱写的 要遵循从后往前调用的原则。
webpack不仅仅是可以让我们在js中引入css代码,它建议我们引入任何类型,我们当前代码需要的资源文件
真正需要这些资源的不是应用,而是我们当前编写的代码。
如果是一些图片资源在js中导入,webpack的处理原理如下:
其实就是把对应文件复制到了打包结果目录中,同时把该文件的路径导入到了bundle.js中。
我们使用的Loader是 file-loader, 打包后的图片文件会默认以网站根目录的路径进行导入,如果需要指定文件夹需要在output中配置publicpath
import icon from './assets/icon.png'
let img = new Image()
img.src = icon
document.body.append(img)
module.exports = {
mode: 'none',
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.join(__dirname, 'dist/'),
publicPath: 'dist/'
},
module: {
rules: [
{
test: /.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /.png$/,
use: ['file-loader']
}
]
}
}
这里的url 加载器 指的就是 url-loader, 这个loader的作用可以把资源文件转化为Data URL。 一旦文件转为data url之后 dist目录中就不会复制文件了。
我们可以通过一些参数来设置一个值,让超过这个值大小的文件 依然以file-loader处理 ,小于这个值的文件转为data url
要想使用 url-loader 我们需要先安装file-loader
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 超过10kb的就还是用file-loader 复制文件 引入路径
}
}
}
]
}
}
资源模块(asset module)是一种模块类型,它允许使用资源文件(字体,图标等)而无需配置额外 loader.
在 webpack 5 之前,通常使用:
raw-loader 将文件导入为字符串
url-loader 将文件作为 data URI 内联到 bundle 中
file-loader 将文件发送到输出目录
资源模块类型(asset module type),通过添加 4 种新的模块类型,来替换所有这些 loader:
可以通过在 webpack 配置的 module rule 层级中,设置 Rule.parser.dataUrlCondition.maxSize 选项来修改此条件
当在 webpack 5 中使用旧的 assets loader(如 file-loader/url-loader/raw-loader 等)和 asset 模块时,你可能想停止当前 asset 模块的处理,并再次启动处理,这可能会导致 asset 重复,你可以通过将 asset 模块的类型设置为 ‘javascript/auto’ 来解决。
更多的具体配置可以查看官方文档 资源模块
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
assetModuleFilename: 'images/[hash][ext][query]', // 指定导出的文件名
},
module: {
rules: [
{
test: /\.png/,
type: 'asset/resource'
},
{
test: /\.html/,
type: 'asset/resource',
generator: {
filename: 'static/[hash][ext][query]' // 单独指定 名字
}
},
{
test: /\.svg/,
type: 'asset/inline' // inline 的时候不需要指定文件名
},
{
test: /\.txt/,
type: 'asset',
parser: {
dataUrlCondition: {
maxSize: 4 * 1024 // 4kb 指定大小
}
}
}
]
}
webpack 默认只能处理模块化的语法,因为它本身就是为了实现模块打包的,但是它并不支持所有的新特性,所以我们需要借助于babel-loader 来处理js代码中的新特性
需要安装 三个包
"@babel/core","@babel/preset-env", "babel-loader"
{
test: /.js$/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
}
处理html文件中的src属性 或者 a 标签的href属性 ,需要借助于 html-loader
{
test: /.html$/,
use: {
loader: 'html-loader',
options: {
attrs: ['img:src', 'a:href'] // 表示要处理的src 和 href
}
}
}
Loader 负责资源文件从输入到输出的转换,webpack要求输出的必须要是js的代码,我们可以对于同一个资源依次使用多个 Loader 处理
loader代码
const marked = require('marked')
// source 接收所有的匹配到的资源内容
module.exports = source => {
return marked(source)
}
webpack.config.js 代码
{
test: /.md$/,
use: [
'html-loader',
'./src/loader/md_loader.js'
]
}
导入md的代码
import './style/index.css'
import noteStr from './note.md'
const noteDiv = document.createElement('div')
noteDiv.innerHTML = noteStr
document.body.append(noteDiv)