跳到主要内容

介绍下 webpack 热更新原理,是如何做到在不刷新浏览器的前提下更新页面的

Webpack 热更新(Hot Module Replacement,简称 HMR)是一种提供模块级别热更新的技术,它可以在不刷新整个页面的情况下,将更新的模块实时应用到正在运行的应用程序中。下面是Webpack热更新的工作原理:

  1. 开发服务器(Dev Server)和Webpack构建工具通过WebSocket或者轮询机制建立起一个持久的连接,用于实时通信。

  2. 在应用程序启动时,Webpack会将HMR runtime注入到生成的包中。这个runtime负责处理热更新的逻辑。

  3. 当文件发生改变并保存时,Webpack会监听到文件变化,并发出一个构建信号。

  4. Webpack构建工具通过构建信号,将编译后的新模块代码通过WebSocket或者轮询机制发送给开发服务器。

  5. 开发服务器接收到更新的模块代码后,通知HMR runtime进行模块替换。

  6. HMR runtime首先尝试将新模块代码直接替换到运行中的应用程序中。如果替换成功,则应用程序保持运行状态,只更新发生改变的模块。

  7. 如果模块替换失败(例如模块之间存在依赖关系),HMR runtime会通过热更新补丁(Hot Update Patch)来更新应用程序。热更新补丁包含了需要被替换的模块代码以及更新的逻辑。

  8. 应用程序接收到热更新补丁后,根据补丁进行模块替换,并更新应用程序的状态。

通过这种机制,Webpack热更新可以在不刷新浏览器的情况下,实时应用更新的模块代码,提供更快速的开发体验。它适用于各种前端框架和工具,可以在开发过程中快速查看修改的效果,提高开发效率。

webpack配置,webpack4.0有哪些优化点

Webpack的配置通常是通过创建一个名为webpack.config.js的文件来完成。以下是一个基本的Webpack配置示例:

const path = require('path');

module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js',
},
module: {
rules: [
// 添加各种加载器和规则
{
test: /\.js$/,
exclude: /node_modules/,
use: ['babel-loader'],
},
// 其他规则...
],
},
plugins: [
// 添加插件
// new HtmlWebpackPlugin(),
// new MiniCssExtractPlugin(),
// 其他插件...
],
};

上述配置文件中的主要部分包括:

  • entry:指定Webpack的入口文件,可以是单个文件或多个文件。
  • output:指定打包后的文件输出配置,包括路径和文件名等。
  • module.rules:定义各种加载器和规则,用于处理不同类型的文件。例如,使用babel-loader处理JavaScript文件,使用css-loaderstyle-loader处理CSS文件等。
  • plugins:配置各种插件,用于执行额外的构建任务。例如,使用HtmlWebpackPlugin生成HTML文件,使用MiniCssExtractPlugin提取CSS文件等。

现在来讨论Webpack 4.0的一些优化点:

  1. 模式(Mode)配置:通过设置mode选项为"production"或"development",Webpack 4.0可以自动应用一系列针对该模式的优化。生产模式下会进行代码压缩、作用域提升等优化,开发模式下会保留有用的调试信息。

  2. 代码压缩:Webpack 4.0内置了对ES6+代码的压缩功能。在生产模式下,会自动启用代码压缩,无需额外配置。

  3. Tree Shaking:通过配置mode为"production",Webpack 4.0可以自动执行无用代码的消除,只打包使用到的模块。

  4. 优化输出文件:Webpack 4.0引入了optimization.splitChunks选项,用于将公共的依赖模块提取成独立的文件,避免重复加载和提高缓存效果。

  5. NoEmitOnErrorsPlugin:Webpack 4.0默认启用了NoEmitOnErrorsPlugin插件,即在出现错误时,不会生成包含错误资源的输出文件。

  6. 缓存:Webpack 4.0引入了默认的持久化缓存机制,可以通过配置cache: true来开启。这样在下次构建时,Webpack会使用已缓存的模块,减少重新构建的时间。

  7. Scope Hoisting:Webpack 4.0通过ModuleConcatenationPlugin插件实现了作用域提升(Scope Hoisting)的优化,将模块封装成更小的函数,减少了函数声明和闭包的代码量。

  8. 按需加载(Code Splitting):Webpack 4.0支持动态导入(如import())和React的React.lazy()React.Suspense等特性,实现按需加载,减小初始加载的文件体积。

以上是Webpack 4.0的一些优化点,它们可以通过合理的配置和使用Webpack提供的内置功能来提高构建性能、减小打包体积,并优化前端应用的加载和运行效果。

常见的Webpack Loader? 如何实现一个Webpack Loader

常见的Webpack Loader是用于处理不同类型文件的加载器,它们可以在Webpack构建过程中对文件进行转换、处理和加载。以下是一些常见的Webpack Loader:

  1. babel-loader:用于将ES6+的JavaScript代码转换成向后兼容的JavaScript版本,以便在旧版浏览器中运行。

  2. css-loader:用于加载和解析CSS文件,支持处理CSS中的导入、URL引用等功能。

  3. style-loader:将CSS代码注入到HTML文件中,以实现样式的动态加载。

  4. sass-loader:用于加载和处理SCSS/Sass文件,将其转换为CSS代码。

  5. less-loader:用于加载和处理Less文件,将其转换为CSS代码。

  6. file-loader:用于处理文件资源(如图片、字体等),将文件复制到输出目录,并返回文件的URL路径。

  7. url-loader:类似于file-loader,但可以根据文件大小将文件转换为DataURL,并嵌入到JavaScript代码中,减少HTTP请求。

  8. image-webpack-loader:用于优化和压缩图像文件,减小图像文件的大小。

  9. eslint-loader:用于在构建过程中检查JavaScript代码的语法和代码风格,基于ESLint进行静态代码分析。

以上只是一些常见的Webpack Loader示例,实际上还有许多其他加载器可用于处理不同类型的文件。

现在来讨论如何实现一个简单的Webpack Loader。下面是一个示例,它将输入的JavaScript代码中的所有console.log语句删除:

module.exports = function(source) {
// 删除所有的console.log语句
const modifiedSource = source.replace(/console\.log\(.*\);?\n?/g, '');

return modifiedSource;
};

上述示例是一个简单的Webpack Loader函数,它接收源代码作为输入(source参数),并对源代码进行修改。在这个示例中,我们使用正则表达式将所有的console.log语句替换为空字符串,从而删除它们。最后,返回修改后的代码。

要使用这个自定义Loader,需要在Webpack配置文件中指定它。例如:

module.exports = {
// ...
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: ['babel-loader', 'your-custom-loader'],
},
// ...
],
},
};

在上述配置中,我们将自定义Loader添加到了Webpack的加载器链中,并指定它的执行顺序。注意,加载器的执行顺序是从右到左的,因此在这个示例中,先使用了babel-loader将JavaScript代码转换为向后兼容的版本,然后再使用自定义Loader对代码进行修改。

请注意,这只是一个简单的示例,实际的Loader可能需要更复杂的逻辑和处理步骤,具体取决于需要实现的功能和转换需求。

常见的Webpack Plugin? 如何实现一个Webpack Plugin

常见的Webpack插件用于在构建过程中执行额外的任务,例如优化输出、生成文件、注入变量等。以下是一些常见的Webpack插件:

  1. HtmlWebpackPlugin:用于生成HTML文件,并自动将打包后的资源文件(如CSS、JavaScript)注入到HTML中。

  2. MiniCssExtractPlugin:用于提取CSS代码到独立的文件,并支持CSS文件的压缩和优化。

  3. CleanWebpackPlugin:在构建之前清除输出目录中的旧文件。

  4. CopyWebpackPlugin:用于复制静态文件或目录到输出目录。

  5. DefinePlugin:用于在打包过程中注入全局变量或环境变量,可以用于区分开发环境和生产环境的代码。

  6. HotModuleReplacementPlugin:实现模块热替换(HMR),在开发过程中无需刷新页面即可实时预览修改的内容。

  7. OptimizeCSSAssetsPlugin:用于优化和压缩CSS文件。

  8. UglifyJsPlugin:用于压缩JavaScript代码。

  9. BundleAnalyzerPlugin:生成构建分析报告,可视化展示构建输出的模块大小和依赖关系。

以上只是一些常见的Webpack插件示例,实际上还有许多其他插件可以满足不同的需求。

接下来,让我们来讨论如何实现一个简单的Webpack插件。下面是一个示例,该插件会在构建完成后,在控制台输出构建结果的相关信息:

class MyCustomPlugin {
apply(compiler) {
compiler.hooks.done.tap('MyCustomPlugin', (stats) => {
console.log('构建完成!');
console.log('构建时间:', stats.endTime - stats.startTime, 'ms');
console.log('输出文件:', stats.compilation.outputOptions.filename);
console.log('输出路径:', stats.compilation.outputOptions.path);
// 可以执行其他自定义任务...
});
}
}

module.exports = MyCustomPlugin;

上述示例定义了一个名为MyCustomPlugin的插件类,它实现了Webpack插件的基本结构,并在构建完成后的done钩子中执行自定义的任务。在这个示例中,我们简单地在控制台输出了构建完成的信息,包括构建时间、输出文件和输出路径。

要使用这个自定义插件,需要在Webpack配置文件中进行配置。例如:

const MyCustomPlugin = require('./path/to/MyCustomPlugin');

module.exports = {
// ...
plugins: [
new MyCustomPlugin(),
// 其他插件...
],
};

在上述配置中,我们将自定义插件实例化,并添加到Webpack的插件列表中。

请注意,实现一个完整的Webpack插件可能需要更复杂的逻辑和处理步骤,具体取决于需要实现的功能和任务需求。上述示例只是一个简单的展示,你可以根据自己的需求进行更复杂的插件开发。

loader和plugin有什么区别?如何区分?

Loaders和Plugins是Webpack中的两个不同概念,它们在Webpack的构建过程中扮演不同的角色。

Loaders:Loaders用于处理特定类型的文件,将它们转换为Webpack可识别的模块。它们在模块加载阶段被应用,并且作为Webpack配置中module.rules的一部分进行配置。Loaders可以对文件进行转换、处理和加载,例如将ES6+的JavaScript代码转换为向后兼容的版本,将SCSS/Sass文件转换为CSS代码等。Loaders的主要作用是在Webpack构建过程中解析和转换文件。

Plugins:Plugins用于扩展Webpack的功能和自定义构建过程。它们通过在构建过程中监听Webpack的生命周期事件,并在特定的时间点执行额外的任务。Plugins可以实现各种功能,如生成HTML文件、优化输出、拷贝静态文件、压缩代码等。Plugins以实例化对象的形式存在,并作为Webpack配置中的plugins部分进行配置。Plugins的主要作用是扩展Webpack的能力和执行额外的构建任务。

区分Loaders和Plugins的主要依据是它们在Webpack的不同阶段的应用和功能:

  • Loaders主要在模块加载阶段起作用,用于处理文件的转换和加载,通常针对具体的文件类型。它们通过修改模块的内容来实现转换,例如将非JavaScript文件转换为JavaScript模块。Loaders在Webpack配置的module.rules中配置,并按照规则链的顺序依次应用。

  • Plugins主要在Webpack的构建生命周期中起作用,用于执行额外的任务和操作,例如优化输出、生成文件、注入变量等。它们通过监听Webpack的生命周期事件,如构建完成的事件,来执行相应的任务。Plugins以实例化对象的形式存在,并在Webpack配置的plugins中配置。

总结来说,Loaders用于在模块加载阶段对文件进行处理和转换,而Plugins用于扩展Webpack的能力和执行额外的构建任务。Loaders关注于文件级别的处理,而Plugins关注于构建过程的整体扩展和优化。

在Webpack配置中,通过配置module.rules来定义Loaders,配置plugins来定义Plugins。通过理解它们的不同功能和应用场景,你可以更好地区分它们并在Webpack配置中使用。

webpack 做过哪些优化,开发效率方面、打包策略方面等等

1)优化 Webpack 的构建速度

  • 使用高版本的 Webpack (使用webpack4)
  • 多线程/多实例构建:HappyPack(不维护了)、thread-loader
  • 缩小打包作用域:
    • exclude/include (确定 loader 规则范围)
    • resolve.modules 指明第三方模块的绝对路径 (减少不必要的查找)
    • resolve.extensions 尽可能减少后缀尝试的可能性
    • noParse 对完全不需要解析的库进行忽略 (不去解析但仍会打包到 bundle 中,注意被忽略掉的文件里不应该包含 import、require、define 等模块化语句)
    • IgnorePlugin (完全排除模块)
    • 合理使用alias
  • 充分利用缓存提升二次构建速度:
    • babel-loader 开启缓存
    • terser-webpack-plugin 开启缓存
    • 使用 cache-loader 或者 hard-source-webpack-plugin 注意:thread-loader 和 cache-loader 兩個要一起使用的話,請先放 cache-loader 接著是 thread-loader 最後才是 heavy-loader
  • DLL:
    • 使用 DllPlugin 进行分包,使用 DllReferencePlugin(索引链接) 对 manifest.json 引用,让一些基本不会改动的代码先打包成静态资源,避免反复编译浪费时间。 2)使用webpack4-优化原因
  • (a)V8带来的优化(for of替代forEach、Map和Set替代Object、includes替代indexOf)
  • (b)默认使用更快的md4 hash算法
  • (c)webpacks AST可以直接从loader传递给AST,减少解析时间
  • (d)使用字符串方法替代正则表达式 ①noParse
  • 不去解析某个库内部的依赖关系
  • 比如jquery 这个库是独立的, 则不去解析这个库内部依赖的其他的东西
  • 在独立库的时候可以使用
module.exports = {
module: {
noParse: /jquery/,
rules:[]
}
}

②IgnorePlugin

  • 忽略掉某些内容 不去解析依赖库内部引用的某些内容
  • 从moment中引用 ./locol 则忽略掉
  • 如果要用local的话 则必须在项目中必须手动引入
import 'moment/locale/zh-cn'
module.exports = {
plugins: [
new Webpack.IgnorePlugin(/./local/, /moment/),
]
}

③dillPlugin

  • 不会多次打包, 优化打包时间
  • 先把依赖的不变的库打包
  • 生成 manifest.json文件
  • 然后在webpack.config中引入
  • webpack.DllPlugin Webpack.DllReferencePlugin

④happypack -> thread-loader

    • 大项目的时候开启多线程打包
  • 影响前端发布速度的有两个方面,一个是构建,一个就是压缩,把这两个东西优化起来,可以减少很多发布的时间。

⑤thread-loader thread-loader 会将您的 loader 放置在一个 worker 池里面运行,以达到多线程构建。 把这个 loader 放置在其他 loader 之前(如下图 example 的位置), 放置在这个 loader 之后的 loader 就会在一个单独的 worker 池(worker pool)中运行。

// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /.js$/,
include: path.resolve("src"),
use: [
"thread-loader",
// 你的高开销的loader放置在此 (e.g babel-loader)
]
}
]
}
}

每个 worker 都是一个单独的有 600ms 限制的 node.js 进程。同时跨进程的数据交换也会被限制。请在高开销的loader中使用,否则效果不佳

⑥压缩加速——开启多线程压缩

  • 不推荐使用 webpack-paralle-uglify-plugin,项目基本处于没人维护的阶段,issue 没人处理,pr没人合并。 Webpack 4.0以前:uglifyjs-webpack-plugin,parallel参数
module.exports = {
optimization: {
minimizer: [
new UglifyJsPlugin({
parallel: true,
}),
],
},};
  • 推荐使用 terser-webpack-plugin
module.exports = {
optimization: {
minimizer: [new TerserPlugin(
parallel: true // 多线程
)],
},
};

2)优化 Webpack 的打包体积

  • 压缩代码
  • 提取页面公共资源:
  • Tree shaking
  • Scope hoisting
  • 图片压缩
  • 动态Polyfill

3)speed-measure-webpack-plugin 简称 SMP,分析出 Webpack 打包过程中 Loader 和 Plugin 的耗时,有助于找到构建过程中的性能瓶颈。 开发阶段

开启多核压缩 插件:terser-webpack-plugin

const TerserPlugin = require('terser-webpack-plugin')
module.exports = {
optimization: {
minimizer: [
new TerserPlugin({
parallel: true,
terserOptions: {
ecma: 6,
},
}),
]
}
}

https://juejin.cn/post/7016593221815910408

在 Webpack 中是如何做到支持类似于 JSX 语法的 Sourcemap 定位?

在 Webpack 中支持类似 JSX 语法的 Sourcemap 定位,需要进行以下配置:

  1. 安装依赖: 首先,确保安装了以下依赖:

    • webpack: Webpack 的核心库
    • ts-loader(或其他类似的 TypeScript Loader):用于将 TypeScript 代码编译为 JavaScript 代码
    • source-map-loader:用于加载 TypeScript 生成的 Sourcemap

    可以使用以下命令进行安装:

    npm install webpack ts-loader source-map-loader --save-dev
  2. 配置 Webpack: 在 Webpack 的配置文件中,进行如下配置:

    module.exports = {
    // ...
    devtool: 'source-map', // 启用 Sourcemap 生成
    resolve: {
    extensions: ['.ts', '.tsx', '.js'], // 支持的文件扩展名
    },
    module: {
    rules: [
    {
    test: /\.tsx?$/,
    use: [
    {
    loader: 'ts-loader',
    options: {
    transpileOnly: true, // 只进行转译,不进行类型检查
    },
    },
    'source-map-loader', // 加载 Sourcemap
    ],
    exclude: /node_modules/,
    },
    ],
    },
    // ...
    };

    上述配置中,devtool 选项设置为 'source-map',启用了 Sourcemap 生成。resolve.extensions 配置用于指定支持的文件扩展名,包括 .ts.tsx.jsmodule.rules 中的配置是针对 TypeScript 文件的处理规则,通过 ts-loader 进行编译,并使用 source-map-loader 加载生成的 Sourcemap。

    注意,为了更好地支持 Sourcemap 定位,确保 TypeScript 的配置文件 tsconfig.json 中的 sourceMap 选项被设置为 true

  3. 使用 JSX 语法: 现在,你可以在 TypeScript 代码中使用 JSX 语法了。当 Webpack 进行构建时,通过以上配置,会生成对应的 Sourcemap 文件,以便在调试过程中能够准确定位到 TypeScript 源码中的位置。

通过以上配置,Webpack 将能够正确地解析和加载 TypeScript 代码,并生成与源代码对应的 Sourcemap,从而实现了类似 JSX 语法的 Sourcemap 定位。

谈谈你对 SourceMap 的了解?

SourceMap 是一个信息文件,它包含了如何从压缩文件(例如 minified 文件)映射回原始源文件的信息。这对于在开发和调试过程中查看、理解和修改压缩后的代码非常有用。

在 JavaScript 开发中,由于经常使用工具如 Babel 或 TypeScript 将源代码转换为浏览器可以理解的代码,或者使用压缩工具如 UglifyJS 或 Terser 来压缩 JavaScript 代码,这些过程会改变原始代码的结构和行号,使得调试变得困难。这时,SourceMap 就派上了用场。

SourceMap 的工作原理是在转换源代码的同时生成一个映射文件(.map 文件),这个文件包含了源代码和转换后代码之间的位置信息(行和列)。当浏览器执行转换后的代码出错时,可以通过 SourceMap 找到对应的源代码位置,从而方便开发者定位和解决问题。

总的来说,SourceMap 是一个非常有用的工具,它可以帮助开发者更有效地进行代码调试和问题定位。希望这个解释对你有所帮助!

列举你知道的所有构建工具并说说这些工具的优缺点?这些构建工具在不同的场景下应该如何选型?

以下是一些常见的 JavaScript 构建工具,以及它们的优缺点和适用场景:

  1. Webpack

    • 优点:Webpack 是一个非常强大的模块打包器,它可以处理 JavaScript、CSS、图片等资源,支持热模块替换(HMR)和代码拆分。Webpack 的插件生态系统非常丰富,可以扩展其功能。
    • 缺点:Webpack 的配置可能会变得复杂和冗长,学习曲线较陡峭。
    • 适用场景:大型的前端项目,特别是使用模块化开发的项目。
  2. Gulp

    • 优点:Gulp 是一个基于流的构建工具,它使用 JavaScript 代码而不是配置文件来定义构建任务,这使得构建过程更加灵活和可控。Gulp 的插件生态系统也非常丰富。
    • 缺点:Gulp 可能不如 Webpack 那样适合处理模块化的代码。
    • 适用场景:需要自定义构建流程的项目,或者需要处理非模块化代码的项目。
  3. Rollup

    • 优点:Rollup 是一个模块打包器,它专注于创建高效的 ES6 模块束。Rollup 支持 tree-shaking,可以消除未使用的代码,从而减小打包后的文件大小。
    • 缺点:Rollup 的功能可能不如 Webpack 那样全面,例如它不支持热模块替换(HMR)。
    • 适用场景:需要创建小巧、高效的 ES6 模块束的项目。
  4. Parcel

    • 优点:Parcel 是一个零配置的构建工具,它支持多种文件类型,包括 JavaScript、CSS、HTML、图片等。Parcel 的构建速度非常快,因为它使用了多核编译和文件系统缓存。
    • 缺点:Parcel 的功能可能不如 Webpack 那样全面,而且它的社区和插件生态系统可能不如 Webpack 那样丰富。
    • 适用场景:需要快速构建的项目,或者不希望花费太多时间在配置上的项目。
  5. Vite

    • 优点:Vite 是一个由 Vue.js 的作者开发的构建工具,它使用原生 ES 模块(ESM)进行开发,这使得开发服务器启动非常快。Vite 支持热模块更新(HMR),并且可以处理 JavaScript、CSS、HTML、图片等资源。Vite 的配置相对简单,而且它支持 TypeScript、React、Preact、Vue 等多种框架。
    • 缺点:Vite 相对较新,可能不如 Webpack 或 Rollup 那样稳定,而且它的插件生态系统可能不如 Webpack 那样丰富。
    • 适用场景:需要快速启动和开发的现代 JavaScript 项目,特别是使用 Vue.js、React 或 Preact 的项目。

在选择 JavaScript 构建工具时,你应该考虑以下因素:

  • 项目的规模和复杂性:大型和复杂的项目可能需要一个强大且可配置的构建工具。
  • 团队的技能和经验:选择一个团队成员熟悉的构建工具可以提高生产力。
  • 构建速度和效率:构建速度和效率是一个重要的考虑因素,特别是在大型项目中。
  • 社区和插件生态系统:一个活跃的社区和丰富的插件生态系统可以使得构建工具更加强大和灵活。

Webpack 中的插件机制是如何设计的?

Webpack 的插件机制是其核心功能之一,它允许开发者在构建过程的各个阶段插入自定义的行为。以下是其设计的基本概念:

  1. 事件驱动:Webpack 的插件系统基于事件流(tapable)库,类似于 Node.js 的 EventEmitter。Webpack 在整个打包过程中会广播许多事件,插件可以监听这些事件,在特定时机执行特定的任务。

  2. 插件对象:每个插件都是一个具有 apply 方法的对象。Webpack 在启动时会调用这个方法,并将一个 compiler 对象作为参数传入。compiler 对象包含了关于编译过程的所有信息,插件可以通过这个对象来访问和操作编译过程。

  3. 钩子机制compilercompilation 对象都提供了许多钩子,插件可以通过这些钩子在构建过程的不同阶段执行任务。例如,emit 钩子会在生成资源到 output 目录之前触发。

  4. 异步处理:Webpack 的插件机制支持异步事件处理,这对于一些需要进行 I/O 操作或者其他异步操作的插件来说非常有用。

以下是一个简单的插件示例:

class MyPlugin {
apply(compiler) {
compiler.hooks.emit.tapAsync('MyPlugin', (compilation, callback) => {
// 在生成资源到 output 目录之前,可以在这里添加自定义的行为
// ...
callback();
});
}
}

在这个示例中,MyPlugin 插件会在 emit 阶段执行一些自定义的行为。希望这个解释对你有所帮助!

Webpack 有哪些优化手段

Webpack 是一个强大的模块打包工具,可以帮助你管理和打包你的资源。以下是一些优化Webpack的手段:

  1. 使用 Loaders 和 Plugins:Loaders 可以帮助你处理和打包各种类型的文件,而 Plugins 可以提供各种构建时的功能。合理使用 Loaders 和 Plugins 可以大大提高打包效率。

  2. 代码分割(Code Splitting):Webpack 支持将你的代码分割成多个 bundle,这样可以实现按需加载,提高应用的加载速度。

  3. Tree Shaking:Webpack 支持 Tree Shaking,可以帮助你移除未使用的代码,减小最终的 bundle 大小。

  4. 使用缓存(Caching):Webpack 支持输出结果到硬盘缓存,这样在下次构建时可以直接从缓存读取,提高构建速度。

  5. 使用压缩插件(Compression Plugin):Webpack 的 Compression Plugin 可以帮助你压缩你的代码,减小最终的 bundle 大小。

  6. 使用 DLL Plugin:DLL Plugin 可以帮助你将第三方库预编译成一个单独的文件,这样可以大大提高构建速度。

  7. 使用 HappyPack 或 thread-loader:这些工具可以让 Loader 的处理过程在 Node.js 的 Worker Pool 中并行执行,提高构建速度。

以上只是一些基本的优化手段,实际上,Webpack 的优化空间非常大,你可以根据你的项目需求进行深度定制和优化。希望这个解释对你有所帮助!

webpack文件指纹策略:hash chunkhash contenthash

hash策略:是以项目为单位的,项目内容改变则会生成新的hash,内容不变则hash不变

chunkhash策略:是以chunk为单位的,当一个文件内容改变,则整个相应的chunk组模块的hash回发生改变

contenthash策略:是以自身内容为单为的

推荐使用:

  • css :contenthash
  • js:chunkhash

说说webpack的构建流程

1.初始化参数:解析webpack配置参数,合并shell传入和webpack.config.js文件配置的参数,形成最后的配置结果。 2.开始编译:上一步得到的参数初始化compiler对象,注册所有配置的插件,插件监听webpack构建生命周期的事件节点,做出相应的反应,执行对象的 run 方法开始执行编译。 3.确定入口:从配置的entry入口,开始解析文件构建AST语法树,找出依赖,递归下去。 4.编译模块:递归中根据文件类型和loader配置,调用所有配置的loader对文件进行转换,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理。 5.完成模块编译:在经过第4步使⽤ Loader 翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系; 6.输出资源:根据⼊⼝和模块之间的依赖关系,组装成⼀个个包含多个模块的 Chunk,再把每个 Chunk 转换成⼀个单独的⽂件加⼊到输出列表,这步是可以修改输出内容的最后机会; 7.输出完成:在确定好输出内容后,根据配置确定输出的路径和⽂件名,把⽂件内容写⼊到⽂件系统。

说说Loader和Plugin的区别?编写Loader,Plugin的思路

一、区别

  • loader 是文件加载器,能够加载资源文件,并对这些文件进行一些处理,诸如编译、压缩等,最终一起打包到指定的文件中
  • plugin 赋予了 webpack 各种灵活的功能,例如打包优化、资源管理、环境变量注入等,目的是解决 loader 无法实现的其他事

从整个运行时机上来看,如下图所示:

可以看到,两者在运行时机上的区别:

  • loader 运行在打包文件之前
  • plugins 在整个编译周期都起作用

Webpack 运行的生命周期中会广播出许多事件,Plugin 可以监听这些事件,在合适的时机通过Webpack提供的 API改变输出结果

对于loader,实质是一个转换器,将A文件进行编译形成B文件,操作的是文件,比如将A.scssA.less转变为B.css,单纯的文件转换过程

二、编写loader

在编写 loader 前,我们首先需要了解 loader 的本质

其本质为函数,函数中的 this 作为上下文会被 webpack 填充,因此我们不能将 loader设为一个箭头函数

函数接受一个参数,为 webpack 传递给 loader 的文件源内容

函数中 this 是由 webpack 提供的对象,能够获取当前 loader 所需要的各种信息

函数中有异步操作或同步操作,异步操作通过 this.callback 返回,返回值要求为 string 或者 Buffer

代码如下所示:

// 导出一个函数,source为webpack传递给loader的文件源内容
module.exports = function(source) {
const content = doSomeThing2JsString(source);

// 如果 loader 配置了 options 对象,那么this.query将指向 options
const options = this.query;

// 可以用作解析其他模块路径的上下文
console.log('this.context');

/*
* this.callback 参数:
* error:Error | null,当 loader 出错时向外抛出一个 error
* content:String | Buffer,经过 loader 编译后需要导出的内容
* sourceMap:为方便调试生成的编译后内容的 source map
* ast:本次编译生成的 AST 静态语法树,之后执行的 loader 可以直接使用这个 AST,进而省去重复生成 AST 的过程
*/
this.callback(null, content); // 异步
return content; // 同步
}

一般在编写loader的过程中,保持功能单一,避免做多种功能

less文件转换成 css文件也不是一步到位,而是 less-loadercss-loaderstyle-loader几个 loader的链式调用才能完成转换

三、编写plugin

由于webpack基于发布订阅模式,在运行的生命周期中会广播出许多事件,插件通过监听这些事件,就可以在特定的阶段执行自己的插件任务

在之前也了解过,webpack编译会创建两个核心对象:

  • compiler:包含了 webpack 环境的所有的配置信息,包括 options,loader 和 plugin,和 webpack 整个生命周期相关的钩子
  • compilation:作为 plugin 内置事件回调函数的参数,包含了当前的模块资源、编译生成资源、变化的文件以及被跟踪依赖的状态信息。当检测到一个文件变化,一次新的 Compilation 将被创建

如果自己要实现plugin,也需要遵循一定的规范:

  • 插件必须是一个函数或者是一个包含 apply 方法的对象,这样才能访问compiler实例
  • 传给每个插件的 compilercompilation 对象都是同一个引用,因此不建议修改
  • 异步的事件需要在插件处理完任务时调用回调函数通知 Webpack 进入下一个流程,不然会卡住

实现plugin的模板如下:

class MyPlugin {
// Webpack 会调用 MyPlugin 实例的 apply 方法给插件实例传入 compiler 对象
apply (compiler) {
// 找到合适的事件钩子,实现自己的插件功能
compiler.hooks.emit.tap('MyPlugin', compilation => {
// compilation: 当前打包构建流程的上下文
console.log(compilation);

// do something...
})
}
}

emit 事件发生时,代表源文件的转换和组装已经完成,可以读取到最终将输出的资源、代码块、模块及其依赖,并且可以修改输出资源的内容

链接:https://juejin.cn/post/7202639428132274234

webpack 热更新是怎么做到的

通过webpack-dev-server创建两个服务器:提供静态资源的服务(express)和Socket服务 express server 负责直接提供静态资源的服务(打包后的资源直接被浏览器请求和解析) socket server 是一个 websocket 的长连接,双方可以通信 当 socket server 监听到对应的模块发生变化时,会生成两个文件.json(manifest文件)和.js文件(update chunk) 通过长连接,socket server 可以直接将这两个文件主动发送给客户端(浏览器) 浏览器拿到两个新的文件后,通过HMR runtime机制,加载这两个文件,并且针对修改的模块进行更新

webpack中常见的Loader

style-loader: 将css添加到DOM的内联样式标签style里 css-loader :允许将css文件通过require的方式引入,并返回css代码 less-loader: 处理less sass-loader: 处理sass postcss-loader: 用postcss来处理CSS autoprefixer-loader: 处理CSS3属性前缀,已被弃用,建议直接使用postcss file-loader: 分发文件到output目录并返回相对路径 url-loader: 和file-loader类似,但是当文件小于设定的limit时可以返回一个Data Url html-minify-loader: 压缩HTML babel-loader :用babel来转换ES6文件到ES

webpack中常见的Plugin

webpack 和gulp 区别(模块化与流的区别)

webpack 是一个前端模块化方案,更侧重模块打包,我们可以把开发中的所有资源(图片、js 文件、css 文件等)都看成模块,通过loader(加载器)和plugins(插件)对资源进行处理,打包成符合生产环境部署的前端资源。

gulp 强调的是前端开发的工作流程,我们可以通过配置一系列的task,定义task 处理的事务(例如文件压缩合并、雪碧图、启动server、版本控制等),然后定义执行顺序,来让gulp 执行这些task,从而构建项目的整个前端开发流程。