Webpack5

一、Webpack基础

打包工具:将框架(React、Vue),ES6,Less/Sass等语法编译成浏览器能识别的JS、CSS;压缩代码、兼容性处理、提升代码性能等。

一、entry(入口)

指示Webpack 从哪个文件开始打包

二、output(输出)

指示Webpack打包完的文件输出到哪里去,如何命名等

三、loader(加载器)

webpack本身只能处理JS、JSON等资源,其他资源需要借助loader,webpack才能解析

四、plugins(插件)

扩展webpack的功能

五、mode(模式)

  • 开发模式:development
  1. 编译代码,使浏览器能识别运行
  2. 代码质量检查,梳理代码规范
  • 生产模式:production
  1. 优化代码运行性能 
  2. 优化代码打包速度

//webpack.config.js
const path = require("path");// nodejs核心模块,专门用来处理路径问题
const ESLintWebpackPlugin = require('eslint-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CssMinimizePlugin = require('css-minimize-plugin');
const TerserWebpackPlugin = require("terser-webpack-plugin");
const os = require("os");//nodejs核心模块,直接使用

const threads = os.cpus().length;//cpu核数

module.exports = {
    //入口
    entry:"./src/main.js",//相对路径
    //输出
    output:{
        //所有文件的输出路径
        //_dirname nodejs的变量,代表当前文件的文件夹目录
        path:path.resolve(_dirname,"dist"),//绝对路径
        //入口文件打包输出文件名
        filename:"static/js/[main].js",
        //给打包输出的其他文件命名
        chunkFilename:"static/js/[name].chunk.js",
        //图片、字体等通过type:asset处理资源命名方式
        assetModuleFilename:"static/media/[hash:10][ext][query]",
        // 自动清空上次打包内容 原理:在打包前,将path整个目录内容清空,再进行打包
        clean: true,
    },
    //加载器
    module:{
        rules:[
        //loader的配置
            // css配置
            {
                test:/\.css$/, //只检测.css文件
                use:[
                    //执行顺序:从右都左(从下到上)
                    //"style-loader",// 将js中的css通过创建style标签添加html文件中生效
                    MiniCssExtractPlugin.loader,// 提取css成单独文件
                    "css-loader",// 将css资源编译成commonjs的模块到js中
                    {
                        loader:"postcss-loader",
                        options:[
                            postcssOptions:{
                                plugins:[
                                    "postcss-preset-env",//能解决大多数样式兼容性问题
                                ],
                            },
                        ],
                    },
                ]
            },
            // less配置
            {
                test:/\.less$/, 
                // loader:'xxx', //只能使用1个loader
                use:[// 使用多个loader
                    //"style-loader",// 将js中的css通过创建style标签添加html文件中生效
                    MiniCssExtractPlugin.loader,// 提取css成单独文件
                    "css-loader",// 将css资源编译成commonjs的模块到js中
                    {
                        loader:"postcss-loader",
                        options:[
                            postcssOptions:{
                                plugins:[
                                    "postcss-preset-env",//能解决大多数样式兼容性问题
                                ],
                            },
                        ],
                    },
                    "less-loader", // 将less编译成css文件
                ]
            },
            // Sass/Scss配置
            {
                test:/\.s[ac]ss$/, 
                use:[
                    //"style-loader",// 将js中的css通过创建style标签添加html文件中生效
                    MiniCssExtractPlugin.loader,// 提取css成单独文件
                    "css-loader",// 将css资源编译成commonjs的模块到js中
                    {
                        loader:"postcss-loader",
                        options:[
                            postcssOptions:{
                                plugins:[
                                    "postcss-preset-env",//能解决大多数样式兼容性问题
                                ],
                            },
                        ],
                    },
                    "sass-loader", // 将sass编译成css文件
                ]
            },
            // 图片配置
            {
                test:/\.(png|jpe?g|gif|webp|svg)$/,
                type:"asset",
                parser:{
                    dataUrlCondition:{
                        //小于10kb的图片转base64,优点:减少请求数量 缺点:体积会更大
                        maxSize:10*1024, // 10kb
                    },
                },
                generator:{
                    //输出图片名称  [hash:10] hash值取前10位
                    //filename: "static/images/[hash:10][ext][query]",
                },
            },
            // 字体图表及其他资源配置
            {
                test:/\.(ttf|woff2?|map3|map4|avi)$/,
                type:"asset/resourse",
                generator:{
                    //输出名称
                    //filename: "static/desia/[hash:10][ext][query]",
                },
            },
            // babel配置
            {
                test:/\.js$/,
                //exclude:/node_modules/, // 排除node_moudles下的文件,其他文件都处理
                include:path.resolve(_dirname,"../src"),// 只处理src下的文件,其他文件不做处理
                use:[
                    {
                        loader:"thread-loader",// 开启多进程
                        options:{
                            workers:threads, //进程数量
                        }
                    },
                    {
                        loader: "babel-loader",
                        options:{
                        //    //智能预设
                        //    presets: ["@babel/preset-env"],
                        cacheDirectoy:true,// 开启babel缓存
                        cacheCompression:false,// 关闭缓存文件压缩
                        plugins:["@babel/plugin-transform-runtime"],//减少代码体积
                    },               
                ],
            },
        ]
    },
    //插件
    plugins:[
        //ESLint的配置
        new ESLintPlugin({
            //检测哪些文件
            context: path.resolve(_dirname,"src"),
            exclude:"node_modules",// 默认值
            cache:true,//开启缓存
            cacheLocation:path.resolve(_dirname,"../mode_modules/.cache/eslintcache"),
            threads, //开启多进程和进程数量
        }),
        new HtmlWebpackPlugin({
            // 模板:以public/index.html文件创建新的html文件(自动引入打包输出的资源)
            template:path.resolve(_dirname,"public/index.html"),
        }),
        new MiniCssExtractPlugin({
            filename:"static/css/[main].css",
            chunkFilename:"static/css/[name].chunk.css",
        }),
        //new CssMinimizePlugin(),
        //new TerserWebpackPlugin({
        //    parallel:threads, //开启多进程和进程数量
        //}),
    ],
    optimization:{
        //压缩的操作
        minimizer:[
            //压缩css
            new CssMinimizePlugin(),
            //压缩js
            new TerserWebpackPlugin({
                parallel:threads, //开启多进程和进程数量
            }),
        ],
    },
    代码分割配置
    splitChunks:{
        chunks:"all",// 其他都用默认值
    },
    runtimeChunk:{
        name:(entrypoint) => `runtime~${entrypoint.name}.js`,
    },
    //开发服务器:不会输出资源,在内存中编译打包的
    devServer:{
        host:"localhost",// 启动服务器域名
        port:"3000",// 启动服务器端口号
        open:true, //是否自动打开浏览器
        hot:true, // 开启HMR(默认开启)
    },
    //模式
    mode:"development",
};

Eslint :可组装的Javascript和JSX检查工具(检测js和jsx语法的工具,可配置各项功能)

文档:https://eslint.nodejs.cn/docs/latest/use/getting-started

Babel:JavaScript编译器。主要用于将ES6语法编写的代码转换为向后兼容的JavaScript语法,以便能够运行在当前和旧版本的浏览器或其他环境中

// package.json文件
"script":{
    "dev":"webpack serve --config ./config/webpack.dev.js",//启动开发服务器,内存编译打包没有输出
    "build":"webpack --config ./config/webpack.prod.js",//直接打包输出
}

生产环境:

提取css成单独文件:css打包到js文件中,当js文件加载时会创建一个style标签来生成样式,这样对网站的用户体验不好,会出现闪屏现象

解决:使用MiniCssExtractPlugin,提取css成单独文件,通过link标签加载

样式兼容性处理:postcss-loader

//package.json文件
"browserslist":[
    //"ie >= 8",
    "last 2 version",
    "> 1%",
    "not dead"
]

css压缩:CssMinimizePlugin

二、Webpack优化

1.提升开发体验 

SourceMap(源代码映射):用来生成源代码与构建后的代码一一映射的文件的方案,帮助我们更快找到错误根源。

开发模式:cheap-module-source-map

优点:打包编译速度快,只包含行映射  缺点:没有列映射

// webpack.config.js
module.exports=[
    mode:"development",
    devtool:"cheap-module-source-map",
]

生产模式:source-map

优点:包含行、列映射  缺点:打包编译速度更慢

// webpack.config.js
module.exports=[
    mode:"production",
    devtool:"source-map",
]

2.提升打包构建速度 

(1)HotModuleReplacement(HMR:热模块替换)

在程序运行中,替换、添加或删除某个模块代码,就只有这个模块代码需要重新打包编译,其他模块不变,这样打包速度更快,无需重新加载整个页面。(开发环境)

(2)OneOf

//加载器
module:{
    rules:[
        // loader的配置
        {
            //每个文件只能被其中一个loader配置处理
            oneOf:[
                ......
            ],
        },
    ],
},

(3)Babel、Eslint 对node_modules下的文件 include或exclude处理 

(4)Cache:对Eslint检查和Babel编译结果进行缓存

每次打包时js文件都要经过Eslint检查和Babel编译,速度比较慢。可以缓存之前的Eslint检查和Babel编译结果,这样第二次打包时速度更快。(生产环境)

(5)多进程打包:开启电脑的多个进程同时干一件事,速度更快。注意:仅在特别耗时的操作中使用,因为每个进程启动就有大约600ms左右的开销。

//nodejs核心模块,直接使用
const os = require("os");
//cpu核数
const threads = os.cpus().length;
npm i thread-loader -D

3.减少代码体积 

(1)Tree Shaking:用于移除JavaScript中的没有使用上的代码  注意:它依赖ES Module

webpack已经默认开启了这个功能,无需其他配置。

(2)减少babel生产文件的体积

Babel为编译的每个文件都插入了辅助代码,使代码体积过大。

Babel对一些公共方法使用了非常小的辅助代码,比如_extend。默认情况下会被添加到每一个需要它的文件中。可以将这些辅助代码作为一个独立模块,来避免重复引入。

@babel/plugin-transform-runtime:禁用了babel自动对每个文件的runtime注入,而是引入@babel/plugin-transform-runtime并且使所有辅助代码从这里引入

(3)压缩图片

本地项目静态图片需要压缩

image-minimizer-webpack-plugin:用来压缩图片的插件

4.优化代码运行性能

(1)代码分割Code Split

1.分割文件:将打包生成的文件进行分割,生成多个js文件

2.按需加载:需要哪个文件就加载哪个文件

打包代码时会将所有的js文件打包到一个文件中,体积太大了,如果只需要渲染首页,就应该只加载首页的js文件,其他文件不应该加载。

需要将打包生成的文件进行代码分割,生成多个js文件,渲染哪个页面就只加载某个js文件,这样加载的资源就少,速度就更快。

document.getElementById("btn").onclick = function () {
    // import动态导入:会将动态导入的文件代码分割(拆分成单独模块),在需要使用的时候自动加载
    // /* webpackChunkName: "count" */ webpack魔法命名
    import(/* webpackChunkName: "count" */ "./js/count")
        .then((res) => {
            console.log("模块加载成功", res.default(2,1));
        })
        .catch((err) => {
            cosnole.log("模块加载失败", err);
        });
}

(2)Preload和Profetch技术

Preload:告诉浏览器立即加载资源

Prefetch:告诉浏览器在空闲时才开始加载资源

共同点:都只会加载资源,并不执行;都有缓存

区别:preload加载优先级高,prefetch加载优先级低;preload只能加载当前页面需要使用的的资源,prefetch可以加载当前页面资源,也可以加载下一个页面需要使用的资源。

总结:当前页面优先级高的资源用preload加载;下一个页面需要使用的资源用prefetch加载

问题:兼容性较差(preload相对于prefetch兼容性好一点) https://caniuse.com/查看兼容性

(3)Core-js:解决JS兼容性问题,专门用来做ES6及以上API的polyfill(补丁)

polyfill:用社区上提供的一段代码,让我们在不兼容某些新特性的浏览器上,使用该新特性。

//babel.config.js
module.export = {
    //智能预设,能够编译ES6语法
    presets: [
        "@babel/preset-env",
        {
            useBuiltIns:"usage",// 按需加载自动引入
            corejs: 3,
        },
    ],
},

(4)PWA(progressive web application):渐进式网络应用程序

可以提供类似于native app(原生应用程序)体验的web app技术,在离线(offline)时应用程序能够继续运行功能,内部通过service workers技术实现的。

https://www.webpackjs.com/guides/progressive-web-application/#registering-our-service-worker

三、loader原理

loader:帮助webpack将不同类型的文件转换为webpack可识别的模块。

loader执行顺序:pre>normal>inline>post,相同优先级的loader执行顺序为:从右到左,从下到上

使用loader的方式:

  • 配置方式:在webpack.config.js文件中指定loader。(pre、normal、post loader)
  • 内联方式:在每个import语句中显示指定loader。(inline loader)
// 含义:使用css-loader和style-loader处理style.css文件;通过!将资源中的loader分开
// 可以通过添加不同前缀,跳过其他类型loader
// (!:跳过normal   -!:跳过pre和normal    !!:跳过pre和normal和post)
import Styles from 'style-loader!css-loader?modules!./style.css'
/* loader就是一个函数,当webpack解析资源时,会调用相应的loader去处理,loader接收到文件内容作为参数,返回内容出去
    content 文件内容
    map SourceMap
    meta 别的loader传递的数据
*/
module.exports = function (content, map, meta) {
    console.log(content);
    return content;
};

(1)同步loader

//module.exports = function (content) {
//    return content;
//}

module.exports = function (content, map, meta) {
    // 第一给参数: err 代表是否有错误
    this.callback(null, content, map, meta);
};

(2)异步loader

module.exports = function (content, map, meta) {
    const callback = this.async();
    
    setTimeout(() => {
        callback(null, content, map, meta);
    },1000);
};

(3)raw loader

// raw loader接收到content是buffer数据
module.exports = function (content) {
    return(content);
};

module.exports.raw = true;

(4) pitch loader

module.exports = function (content) {
    return(content);
};

module.exports.pitch = function () {
    console.log("pitch");
};

loader API:

四、Plugin原理

 通过插件我们可以扩展webpack,加入自定义的构建行为,使webpack可以执行更广泛的任务,拥有更强的构建能力。

webpack就像一条生产线,要经过一系列处理流程后才能将源文件转换成输出结果。这条生产线上的每个处理流程的职责都是单一的,多个流程之间存在依赖关系,只有完成当前处理后才能交给下一个流程去处理。插件就像是一个插入到生产线中的一个功能,在特定的时机对生产线上的资源做处理。webpack通过Tabable来组织这条复杂的生产线。webpack在运行过程中会广播事件,插件只需要监听它所关心的事件,就能加入到这条生产线中,去改变生产线的运行。webpack的事件流机制保证了插件的有序性,使得整个系统扩展性很好。

                                                                                                      ——【深入浅出Webpack】

 webpack在编译代码的过程中,会触发一系列的Tabable钩子事件,插件所做的就是找到对应的钩子,往上面挂上自己的任务,也就是注册事件,这样当webpack构建的时候,插件注册的事件就会随钩子的触发而执行了。

钩子:本质是事件。为了方便我们直接介入和控制编译过程,webpack把编译过程中触发的关键事件封装成事件接口暴露出来。这些接口很形象地被称作:hooks(钩子)。

Tapable:为webpack提供了统一的插件接口(钩子)类型定义,它是webpack的核心功能库。

Tapable统一暴露了3个方法给插件,用于注入不同类型的自定义构建行为:

  • tab:可以注册同步钩子和异步钩子。
  • tapAsync:回调方式注册异步钩子。
  • tabPromise:Promise方式注册异步钩子。

Plugin构建对象

Complier:保存着完整的webpack环境配置,每次启动webpack构建时它都是独一无二的,仅仅会创建一次的对象。这个对象会在首次启动webpack时创建,我们可以通过complier对象上访问到webpack的主环境配置,比如loader、plugin等等配置信息。

Compilation:compilation对象代表一次资源的构建,compilation实例能够访问所有的模块和它们的依赖。

/* 
1.webpack加载webpack.config.js中的所有配置,此时就会new TestPlugin(),执行插件的constructor
2.webpack创建compiler对象
3.遍历所有plugins中插件,调用插件的aplly方法
4.执行剩下编译流程(触发各个hooks事件)
*/
class TestPlugin {
    constructor() {
        console.log("TestPlugin constructor");
    }

    apply(compiler){
        console.log("TestPlugin apply");
    }
}

module.exports = TestPlugin;
  • 18
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值