工程中,咱们须要和 HTML、CSS、模板、图片和字体等打交道,那如何处理这些这类静态资源呢?css
其实在 webpack 的眼中,这些静态资源都是模块。webpack 自己只认识 js,其余类型资源必须预先定义一个或多个 loader 转译,输出为 webpack 能接收的形式在继续进行处理,因此说 loader 作的就是预处理工做。html
好比组件 js 中加载该组件须要的 css 文件(实现高内聚,若是都在全局引入组件css 文件,哪天去掉这个组件 js,还要同时去掉组件 css 文件),其实只是表达二者之间的依赖关系,由于 css 最终仍是会打包到输出资源目录下,对 js 没有任何实质性影响。vue
每一个 loader 本质都是一个函数,output = loader(input)
。在 webpack4 以前,input 和 output 都必须为字符串,而 webpack4 以后,也支持**抽象语法树(AST)**的传递,那 loader 就能够是链式的了,即 output = loaderA(loaderB(input))
。node
注意,第一个 loader 的输入时源文件,以后全部 loader 的输入是上一个 loader 的输出,最后一个 loader 输出给 webpack。webpack
module.exports = function loader(content, map, meta) {
var callback = this.async();
var result = handler(content, map, meta);
callback(
null, // err
result.content, // 转换后的 内容
result.map, // 转换后的 source-map
result.meta // 转换后的 AST
);
};
复制代码
从上面可看出,本质是个函数,功能是将收到后的内容进行转换,而后返回转换后的结果,可能有 source-map 和 AST 对象。git
一个组件中会 import './index.css'
,那 webpack 怎么处理呢?这就须要 css-loader
这个包来转译。github
1.安装包web
npm install css-loader -D
复制代码
2.配置正则表达式
module.exports = {
module: {
rules: [
{ test: /\.css$/, use: ['css-loader'] }
]
}
};
复制代码
css-loader 处理 css 的各种加载语法,而后可交给 style-loader 来将样式字符串包装成 style 标签插入页面。typescript
1.安装包
npm install style-loader -D
复制代码
2.配置
module.exports = {
module: {
rules: [
{ test: /\.css$/, use: ['style-loader', 'css-loader'] }
]
}
};
复制代码
style-loader 放在 css-loader 后面是由于 webpack 打包机制是按照数组从后往前的顺序将资源交个 loader 处理。
exclude
用来排除指定目录下的模块,即下面 node_modules
中的模块不会执行这条规则,该配置项一般是必加的,不然会拖慢总体打包速度;include
用来包含指定目录下的模块;module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
exclude: /node_modules/
}
]
}
};
复制代码
注意,exclude 和 include 都存在时,exclude 优先级高。
二者都是用于更加精确地肯定模块规则的做用范围。使用频率不高。二者关系以下:
好比组件 import './index.css'
,能够这么理解:被加载模块是 resource,加载方就是 issuer。一般 css 配置的加载方是全局的,如今咱们要限定配置。
module.exports = {
module: {
rules: [
{
use: ['style-loader', 'css-loader'],
resource: {
test: /\.css$/,
exclude: /node_modules/
},
issuer: {
test: /\.js$/,
exclude: '/node_modules/',
include: '/src/pages/'
}
}
]
}
};
复制代码
用来指定一个 loader 的种类,其默认值为 normal,可选值为
module.exports = {
module: {
rules: [
{
test: /\.js$/,
enforce: 'pre',
use: 'eslint-loader'
}
]
}
}
复制代码
其功能是用来将 ES6+ 编译为 ES5,从而没必要关注 ES6+ 特性在各平台不兼容问题。
1.安装包
npm install babel-loader @babel/core @babel/preset-env -D
复制代码
2.配置
module.exports = {
module: {
rules: [
{
test: /\.js$/,
exclude: '/node_modules/',
use: {
loader: 'babel-loader',
options: {
cacheDirectory: true,
presets: [
[ 'env', { modules: false } ]
]
}
}
}
]
}
};
复制代码
true
,在重复打包未改变的模块时防止二次编译,提升打包速度,指向 node_modules/.cache/babel-loader
;false
,意思是禁止让 @babel/preset-env 将模块语句转换,让 ES6 Module 语法给 webpack 处理,如果为 true
,会将 ES6 Module 模块转化为 CommonJS 形式,这将会致使 tree-shaking 特性失效;注意,babel-loader 支持从
.babelrc
文件读取 babel 配置,可将 presets 和 plugins 从配置中提取出来。
将 HTML 文件转化为字符串并进行格式化,而后将 HTML 片断经过 JS 加载进来。
1.安装包
npm install html-loader -D
复制代码
2.配置
module.exports = {
module: {
rules: [
{
test: /\.html$/,
use: 'html-loader'
}
]
}
};
复制代码
1.安装包
npm install ts-loader typescript -D
复制代码
2.配置
module.exports = {
module: {
rules: [
{
test: /\.ts$/,
use: 'ts-loader'
}
]
}
};
复制代码
注意,typescript 的配置不是在 ts-loader 中,而是在工程目录的 tsconfig.json 中,相似
{
"compilerOptions": {
"target": "es5",
"sourceMap": true
}
}
复制代码
打包文件类型文件,并返回到 output.publicPath 中。
1.安装包
npm install file-loader -D
复制代码
2.配置
module.exports = {
entry: './app.js',
output: {
path: path.join(__dirname, 'dist'),
filename: 'bundle.js',
publicPath: './assets/'
},
rules: [
{
test: /\.(png|jpg|jpeg|webp|gif)$/,
use: {
loader: 'file-loader',
options: {
name: '[name].[ext]',
// publicPath: './new-assets/'
}
}
}
]
};
复制代码
3.组件
import avatar from './assets/avatar.jpg';
console.log(avatar); // ./assets/xxxxxxx.jpg
复制代码
注意,配置中 file-loader 的 options.publicPath 会覆盖 output.publicPath,优先级高些。
和 file-loader 做用相似,区别在于 url-loader 用户能够设置一个文件大小的阈值,若大于阈值其返回和 file-loader 同样,若小于阈值返回文件以 base64 形式编码。
1.安装包
npm install url-loader -D
复制代码
2.配置
module.exports = {
module: {
rules: [
{
test: /\.(png|jpg|jpeg|webp|gif)$/,
use: {
loader: 'url-loader',
options: {
limit: 10240,
name: '[name].[ext]',
publicPath: './assets/'
}
}
}
]
}
};
复制代码
注意,options 参数多了 limit 。
咱们知道 vue 组件包含模板、js 和样式。vue-loader 用来将模板、JS 和样式拆分。因此还得额外安装另外的预加载器。
vue-template-compiler
来编译模板;css-loader
处理样式;1.安装包
npm install vue vue-loader vue-template-compiler css-loader -D
复制代码
2.配置
module.exports = {
module: {
rules: [
{
test: /\.vue$/,
use: 'vue-loader'
}
]
}
};
复制代码
说了有表明性的几个 xxx-loader,有时候咱们须要改写或新建本身的 loader 来实现本身的一些愿望或目的。好比如今要实现给全部 JS 文件启动严格模式。
1.新建目录 abc-strict-loader
,为何是这么名称?后面安装这个包后,从node_modules 中方便找,就在前面嘛; 2.进入 abc-strict-loader
目录; 3.npm 初始化 npm init -y
4.新建文件 index.js
;
module.exports = function(content) {
// 处理 content
var useStrict = "// 这是 abc-strict-loader \n'use strict';\n\n";
return useStrict + content;
};
复制代码
5.按说咱们应该先发布到 npm 这样的社区,而后在项目中在安装这个包,但是每当更改下就要重复上面 2 步,着实不是好办法。这时可使用 npm 或 yarn 的软链功能进行本地调试,等到符合咱们的预期需求后再发布到 npm 或 yarn 社区; 6.在项目工程目录安装 abc-strict-loader
,npm install ./abc-strict-loader
,此时项目的 node_modules 中会建立一个指向实际 abc-strict-loader
目录的软链,也就是说后面直接修改 abc-strict-loader
,那项目中的依赖 abc-strict-loader
也会有效; 7.更改 webpack.config.js 配置
module.exports = {
module: {
rules: [
{
test: /\.js$/,
use: 'abc-strict-loader'
}
]
}
};
复制代码
8.启动服务,查看,而后改动 abc-strict-loader
,而后再看下 node_modules/abc-strict-loader
是否发生变化;
若是文件和依赖包都没有更改,那 loader 就直接使用缓存,而不是重复转换。更改 abc-strict-loader/index.js
module.exports = function(content) {
// 判断缓存
if (this.cacheable) {
console.log('缓存');
this.cacheable();
}
// 处理 content
var useStrict = "// 这是 abc-strict-loader \n'use strict';\n\n";
return useStrict + content;
};
复制代码
1.首先,更改配置文件 webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.js$/,
use: {
loader: 'abc-strict-loader',
options: {
sourceMap: true
}
}
}
]
}
};
复制代码
在 options
加入了 sourceMap:true
2.更改 abc-strict-loader
,这里为了获取 webpack.config.js
中的配置,须要安装包 loader-utils
。
const loaderUtils = require('loader-utils');
module.exports = function(content) {
// 判断缓存
if (this.cacheable) {
console.log('缓存');
this.cacheable();
}
// source-map
var options = loaderUtils.getOptions(this) || {};
console.log('abc-strict-loader options: ', options); // abc-strict-loader options: { sourceMap: true }
// 处理 content
var useStrict = "// 这是 abc-strict-loader \n'use strict';\n\n";
return useStrict + content;
};
复制代码
控制台你会看到关于 webpack.config.js 文件中关于 options 的打印信息。
使用 loaderUtils.getOptions
获取配置对象,接下来就要展现真正 source-map 功能了。source-map 便于开发者在浏览器查看源代码。若是没有对 source-map 处理,最终也生成不了 map 文件,那在浏览器 devtool 中可能会看到错误的源码。
3.安装依赖包 source-map。进入 abc-strict-loader
目录。
npm install source-map
复制代码
4.继续更改 abc-strict-loader
文件。
const loaderUtils = require('loader-utils');
const SourceNode = require('source-map').SourceNode;
const SourceMapConsumer = require('source-map').SourceMapConsumer;
module.exports = function(content, sourceMap) {
const useStrict = "// 这是 abc-strict-loader \n'use strict';\n\n";
// 判断缓存
if (this.cacheable) {
console.log('缓存');
this.cacheable();
}
// 支持 source-map
const options = loaderUtils.getOptions(this) || {};
if (options.sourceMap && sourceMap) {
const currentRequest = loaderUtils.getCurrentRequest(this);
const node = SourceNode.fromStringWithSourceMap(
content,
new SourceMapConsumer(sourceMap)
);
node.prepend(useStrict);
const result = node.toStringWithSourceMap({
file: currentRequest
});
const callback = this.async();
callback(null, result.code, result.map.toJSON());
}
// 不支持 source-map
return useStrict + content;
};
复制代码
完整代码可查看目录 09 =>O(∩_∩)O~