前言
大家好,之前出了一篇面试篇webpack入门,这篇文章继续介绍接下来更深入东西。
概览
- 如何载入自己的loader
- 使用loader-utils,schema-utils
- 编写自己的loader
- 扩展
如何载入自己的loader
讲道理大家都是直接import一个loader或者使用webpack内置的loader的。
如果调试自己的loader,应该如下写法:
//webpack.config.js
const path = require("path");
module.exports = {
module: {
rules: [
{
test: /\.js$/,
use: [
{
loader: path.resolve("loader.js"),
options: {
test: 'apple'
}
}
]
}
]
}
};
其实和正常载入loader一样,只是这里指向的是本地文件的路径。
path.resolve('loader.js') 得出路径
/Users/kev1nzh/Desktop/my/webpack/loader.js
使用单个loader
在使用一个loader的时候,loader会接收你正则匹配的资源文件(如上,所有js文件)的字符串。
loader通过代码转化模块后,最后返回传递出去。
使用多个loader
-
当使用多个loader的时候,从传入loader数组的最后一个开始反向传入资源文件字符串。
-
最后一个loader接收最原始的资源文件字符串,转化后传入下一个lodaer。
-
中间的loader接收上一个loader,转化后传入下一个。
-
第一个loader最后接收转化,并传出所有loader处理完的资源文件字符串。
{
test: /\.css$/,
use: [
{
loader: 'css-loader'
},
{
loader: 'style-loader'
},
]
}
//style-loader接收所有css的文件,转化完再传给css-loader,转化完后再怼出来。
使用loader-utils,schema-utils
loader-utils, schema-utils是webpack的loader工具库,有很多便捷的方法可以调用。
const { getOptions,stringifyRequest, parseQuery } = require("loader-utils");
const validateOptions = require("schema-utils");
const schema = {
type: "object",
properties: {
test: {
type: "string"
}
}
};
module.exports = function(source) {
//getOptions 用于在loader里获取传入的options,返回的是对象值。
const options = getOptions(this);
// stringifyRequest转换路径,避免require()或impot时使用的绝对路径
stringifyRequest(this, "./test.js"); // Result => "\"./test.js\""
//parseQuery获取query参数的,这个很简单就不说啦
parseQuery('?name=kev&age=14') // Result => {name: 'kev', age: '14'}
//验证参数的类型是否正确。
validateOptions(schema, options, "loader");
};
编写自己的loader
总算到手写环节了!!!!
//webapck.config.js
const path = require("path");
module.exports = {
entry: "./src",
output: {
path: path.resolve(__dirname, "dist"),
filename: "package.js"
},
mode: "production",
module: {
rules: [
{
test: /\.js$/,
use: [
{
loader: path.resolve("loader.js"),
options: {
work: '996',
sick: 'ICU',
}
}
]
}
]
}
};
首先载入工具库,为了后续使用。
第一步先验证options是否符合类型,
第二步获取参数,然后替换传入的资源文件字符串。
//loader.js
const { getOptions } = require("loader-utils");
const validateOptions = require("schema-utils");
const schema = {
type: "object",
properties: {
work: {
type: 'String'
},
sick: {
type: 'String'
}
}
};
module.exports = function(source) {
const options = getOptions(this);
validateOptions(schema, options, 'loader');
const {work, sick} = options;
source = source.replace(/\[work\]/g, work).replace(/\[sick\]/g, sick);
return `export default ${JSON.stringify(source)}`;
};
展示下要转换的js。
// src/index.js
console.log('工作[work] 生病[sick] 加班不规范 亲人两行泪');
最后在命令行,webpack!!!!!
扩展
webpack是如何运行的?
const index = require('./index');
const console = require('./console');
//index.js
const axios = require('./scripts/debounce.js'');
const moment = require('moment');
// do something
- webpack会解析所有模块,如果模块中有依赖其他文件,那就继续解析依赖的模块。直到文件没有依赖为止。
- 解析结束后,webpack会把所有模块封装在一个函数里,并放入一个名为modules的数组里。
- 将modules传入一个自执行函数中,自执行函数包含一个installedModules对象,已经执行的代码模块会保存在此对象中。
- 最后自执行函数中加载函数(webpack__require)载入模块。
分离代码
// ./src/moment.js
const moment = require('moment');
console.log(moment().format('MMMM Do YYYY, h:mm:ss a'))
// ./index.js
const momentJs = require('./src/moment');
console.log(123);
如上代码,我们打包一下试试看。
两个文件有依赖关系,所以打包后,都会把moment模块打包进去。SplitChunksPlugin
webpack4.x的分离代码方法,之前的CommonsChunkPlugin插件已被移除。 此模块开箱即用,默认情况下,它仅影响按需块,因为更改初始块会影响HTML文件应包含的脚本标记以运行项目。
webpack将根据以下条件自动拆分块:
- 可以共享新块或来自该node_modules文件夹的模块
- 新块将大于30kb(在min + gz之前)
- 根据需要加载块时的最大并行请求数将小于或等于5
- 初始页面加载时的最大并行请求数将小于或等于3
- 当试图满足最后两个条件时,首选更大的块。
让我们看下代码如何实现!
module.exports = {
//...
optimization: {
splitChunks: {
chunks: 'initial', //选择哪些模块需要优化, 参数为 all、async、initial
minSize: 30000, // 要生成的块的最小数
maxSize: 0, //要生成的块的最大数
minChunks: 2, // 分割前共享模块的最小块数
maxAsyncRequests: 5, //按需加载时的最大并行请求数
maxInitialRequests: 3, // 入口的最大并行请求数
automaticNameDelimiter: '~', //指定生成文件名当中的分隔符
name: true, //拆分块的名称
cacheGroups: { //缓存组
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true
}
}
}
}
};
好了这篇需要讲的东西已经结束了。
面试系列第一篇: 面试官:你知道Callback Hell(回调地狱)吗?
面试系列第二篇: 面试官:react和vue有什么区别吗?
面试系列第三篇: 面试官:你了解es6的知识吗?
面试系列第四篇: 面试官:你了解Webpack吗?