webpack学习笔记

一、 安装

1.npm init -y初始化目录

2.npm i webpack webpack-cli -D安装webpack。

可以执行npx webpack ./src/main.js --mode=production --mode=development进行打包指定的文件

二、 基本内容

配置webpack.config.js,后使用npx webpack可直接打包 配置css-loader,安装npm i css-loader style-loader -D,然后在配置文件的module rules中添加一个对象,test填写正则,匹配css文件,use表示要使用的loader。 处理less,npm i less less-loader -D

处理scss,npm i sass sass-loader -D

处理stylus,npm i stylus-loader -D

···

sass:不用写大括号和分号{};,stly:冒号都不用写
图片资源可以不用配置loader就可以加载,如果要设置小图片转base64,则配置rules,type:'asset',parser等。图片资源输出目录,则配置generator,filename使用绝对路径.
打包前清空dist目录,则在output中配置clean:true
处理字体图标资源,和图片资源类似,不过type配置为asset/resource,以及没有了转化base64的配置,音视频等同理。
js代码格式检查,配置eslint。

  1. .eslintrc.js配置文件示例
css 复制代码
    module.exports={
        parserOptions:{
            ecmaVersion:6,
            sourceType:'module',
            ecmaFeatures:{
                jsx:true
            }
        },
        rules:{
            semi:'warn'//禁止分号,'off'|0,'warn'|1,'error'|2,分别代表提示的三种等级。
        },
        extends:[]
    }
  1. eslint可继承的规则如:eslint:recommended,plugin:vue/essential,react-app

  2. 安装npm i eslint eslint-webpack-plugin -D

  3. 在配置文件中const ESLintPlugin=require('webpack-eslint-plugin'),然后在plugins中写详细配置

arduino 复制代码
 new ESLintPlugin({
            context:path.resolve(__dirname,'src')//配置要检查的目录
        })
  1. 配置.eslintignore来忽略dist等,主要使用vscode编辑器插件eslint时,在dist目录也会提示报错信息。

js代码兼容,使用babel

  1. 配置文件babel.config.js或.babelrc.js,后缀写json和不写都是可以的。
  2. 需要下载的包有babel-loader,@babel/core以及预设包 @babel/preset-env
  3. 在webpack配置文件中给js配置babel-loader,options可以写在loader下面,也可以单独写在babel配置文件中,主要内容是presets预设

处理html,装包html-webpack-plugin,配置中可使用template指定使用的html模板。

开发服务器,安装webpack-dev-server,并在webpack配置文件中增加devServer配置,通过npx webpack serve启动服务。

三、分开发模式和生产模式

  1. 新建config文件夹,将webpack.config.js分成两个文件,一个webpack.dev.js,一个webpack.prod.js
  2. 注意绝对路径(path.resolve(__dirname,'../src'))
  3. 开发模式的配置不需要输出路径
  4. 运行方式npx webpack serve --config ./config/webpack.dev.js
  5. 生产模式的配置不需要devServe,mode改为production
  6. 打包npx webpack --config ./config/webpack.prod.js
  7. 指令配置,在package.json中,增加script指令
json 复制代码
"start":"npm run dev"
"dev":"webpack serve --config ./config/webpack.dev.js",
"build":"webpack --config ./config/webpack.prod.js"

四、

  1. css文件原本是一同打包进css的,现需要单独打包成css文件,装包mini-css-extract-plugin 2.在配置文件中引入,并将style-loader改成MiniCssExtractPlugin.loader,最后在plugins中调用一下,可以指定filename

css兼容性处理

  1. 使用postcss,需要下载的包有postcss postcss-loader postcss-preset-env
  2. 在配置文件的css-loader后面增加配置
css 复制代码
{
    loader:'postcss-loader',
    options:{
        postcssOptions:{
            plugins:[
                'postcss-preset-env'
            ]
        }
    }
}
  1. 在package.json中备注需要兼容的浏览器列表
bash 复制代码
"browserslist":[
    "ie >= 8"
    ## 实际开发中常用的写法:
    "last 2 version",
    "> 1%",
    "not dead"
]
  1. 可以将css的loader封装成一个方法,在配置文件中
javascript 复制代码
function getStyleLoader(pre){
    return [
        MinicssExtraxtPlugin.loader,
        "css-loader",
        {
            loader:'postcss-loader',
            options:{
                postcssOptions:{
                    plugins:[
                        'postcss-preset-env'
                    ]
                }
            }
        },
        pre //传入后面使用的其他loader,如sass-loader
    ].filter(Boolean) //如果pre是undefined,则过滤掉
}
  1. css压缩,安装css-minimizer-webpack-plugin,使用方法可以同其他插件一样,也可以在webpack中新增配置项(1)
  2. html-webpack-plugin本身会压缩html文件

5.高级配置

  1. sourceMap

开发模式,在webpack配置文件中加入devtool:'cheap-module-source-map'

生产模式,值为source-map

提升打包速度

  1. hotModuleReplacement 仅开发模式

实际上就是在DevServer中加入hot:true,也是默认值 对于js,可以在main.js中加入下面代码,对于使用了vue-loader或react-hot-loader的项目,默认已经有这些功能了

arduino 复制代码
if(module.hot){
    // 是否有模块热替换的功能
module.hot.accept('./js/count')
}
  1. oneOf 开发生产皆可

对于loader,会遍历每一个rule项

使用oneOf,当匹配到一个rule项,就不会继续向下匹配了

css 复制代码
rules:[
    {
        oneOf:[...]
    }
]
  1. include、exclude 开发生产皆可

在处理js的rule中写

javascript 复制代码
 {
        test:/\.js$/,
       // 不能同时用
       // exclude:/node_modules/,//表示不处理node_module中的文件
       include:path.resolve(__dirname,'../src') ,//表示只处理src里的文件
        loader:'babel-loader',
               
},
  1. cache

主要是缓存eslint检查和babel编译结果

babel写法,在loader处配置options

arduino 复制代码
 options:{
    cacheDirectory:true,//开启babel缓存
    cacheCompression:false,//关闭缓存压缩
   }

对于eslint,则在插件调用的地方加入参数

lua 复制代码
 cache:true,
 cacheLocation:path.resolve(__dirname,'../node_modules/.cache/eslintcache')
  1. thead 对js多进程打包 开发生产皆可

对js处理的主要是eslint,babel,terser,每个进程启动大概需要600ms时间,建议大项目使用 获取cpu核数

ini 复制代码
const os=require('os')
const threads=os.cpus().length

需要下载thread-loader包

在babel-loader出,增加loader

css 复制代码
{
    loader:'thread-loader',
    options:{
        works:threads
    }
}

处理terser,需要现在配置文件中引入插件

javascript 复制代码
const TerserWebpackPlugin=require('terser-webpack-plugin')
...
new TerserWebpackPlugin({
    parallel:threads
})

处理eslint,直接在调用插件那里加入参数threads
webpack中可以加入一项optimization:{minimizer:[...内容同plugins,放一些压缩的插件]}

  1. tree shaking 一种说法,实际就是只打包引入的文件,如import {count} from 'count',依赖es6的模块化

  2. babel会产生很多辅助代码,这里避免产生重复的辅助代码,需要安装包@babel/plugin-transform-runtime,然后在写babel-loader 的地方添加options配置项plugins:[@babel/plugin-transform-runtime]

  3. 图片压缩 (这部分操作报错的原因暂未实际测试过)

需要下载插件image-minimizer-webpack-plugin,imagemin 还需要下载的包,压缩方式有有损压缩和无损压缩,下载的包也有所不同

复制代码
  无损:  imagemin-gifsicle imagemin-jpegtran imagemin-optipng imagemin-svgo
  有损:  imagemin-gifsicle imagemin-mozjpeg imagemin-pngquant imagemin-svgo

然后在webpack配置中引入插件

csharp 复制代码
const ImageMinimizerPlugin=require('image-minimizer-webpack-plugin')
...
//在plugins中配置
new ImageMinimizerPlugin({
    minimizer:{
        implementation:ImageMinimizerPlugin.imageminGenerate,
        options:{
            plugins:[
                ["gifsicle",{interlaced:true}],
                ["jpegtran",{progressive:true}],
                ["optipng",{optimizationLevel:5}],
                ["svgo",{
                    plugins:[
                        "preset-default",
                        "prefixIds",{
                            name:"sortAttrs",
                            params:{
                                xmlnsOrder:"alphabetical"
                            }
                        }
                    ]
                }]
            ]
        }
    }
})

安装无损或有损的包时可能会报错。。。 安装成功,打包也可能会报错,提示缺少jpegtran.exe和optipng.exe,将对应exe放到nodemodules中对应文件夹后再次打包即可。

多入口,code split

  1. entry配置为对象,一个模块名对应一个路径,如main:'./main.js',output的filename配置为[name].js,则打包后会生成对应名称的js
  2. 对于不同文件引用的公共方法,会都打包一份,如果要将公共方法提出来,则配置optimization的splitChunks
ruby 复制代码
    optimization:{
        splitChunks:{
            chunks:'all',//对所有模块进行分割
            // 一些默认值
            // minChunks:1,//至少被引用一次才被分割
            //maxAsyncRequests:30,//并行加载的最大文件数,超过则合并至其他文件
            // minSize:2000,//分割代码的最小大小
            // minRemainingSize:0,//类似minSize,保证提取的文件大小不为0
            // maxInitialRequests:30,//入口js最大并行请求数量
            // enforceSizeThreshold:50000,//超过50kb强制单独打包
            // cacheGroups:{ // 分组,哪些模块要打包到一个组
               
            //     defaultVendors:{//组名
            // 这里面可以写上面的那些配置,表示覆盖上面的那些配置
            //         test:/[\\/]node_modules[\\/]/,//需要打包到一起的模块
            //         priority:-10,//权重,越大越搞
             //           name:'chunk-react',//打包出来的js文件的名称

            //         reuseExistingChunk:true,//如果当前chunk已包含从主bundle中拆分出的模块,则被重用,而不是生成新的
            //     },
            //表示把react和antd相关的代码单独打包为一个js文件
                    react:{
                        test:/[\\/]node_modules[\\/]react(.*)?[\\/]/,
                        name:'chunk-react',
                        priority:40
                    },
                     antd:{
                        test:/[\\/]node_modules[\\/]antd[\\/]/,
                        name:'chunk-antd',
                        priority:40
                    },
            // }

            // 修改配置
            cacheGroups:{
                default:{
                    minSize:0,//我们定义的文件太小了,所以要改到最小的文件体积
                    minChunks:2,
                    priority:-20,
                    reuseExistingChunk:true
                }
            }
        }
    }
  1. 按需加载,动态导入

比如,点击按钮触发的事件不需要一开始就加载,可以点击之后再加载

javascript 复制代码
ducument.getElementById('btn').onclick=function(){
    //import动态导入,会将动态导入的代码文件分割(单独打包为一个文件),在需要使用的时候自动加载
    import('./count').then(res=>{
        //加载成功
    }).catch(err=>{
       //加载失败 
    })
}

单入口

  1. 只需要配置optimization的splitChunks的chunks:'all'
  2. 对于打包后的文件命名,首先需要在import中这样写import(/* webpackChunkName: "math" */'./js/btn'),then(res=>{, 然后需要在output中配置 chunkFilename:'static/js/[name].js'

统一命名

  1. output的filename:'static/js/[name].js', chunkFilename:'static/js/[name].chunk.js', type:asset的资源命名assetModuleFilename:'static/media/[hash:10][ext][query]', 还有css的命名类似

preload,prefetch

  1. preload立即加载,prefetch空闲时加载,兼容性较低需注意
  2. 装包preload-webpack-plugin,报错则@vue/preload-webpack-plugin

使用插件

php 复制代码
const PreloadWebpackPlugin=require('@vue/preload-webpack-plugin')
...
new PreloadWebpackPlugin({
    rel:'preload',
    as:'script'
    //如果是prefetch,则只用写rel:'prefetch',不用写as
})

网络缓存,打包后hash值

由于文件1存在对文件2的引用,文件2的文件发生变化,hash也变化,导致文件1不得不变化,为了解决这个问题, 将引用时文件hash单独存放在一个runtime文件,文件1在runtime文件查找文件2的引用名,这样文件1不用变化,实现方法如下

javascript 复制代码
//在optimization中加入
runtimeChunk:{
    name:(entrypoint) => `runtime~${entrypoint.name}.js`
}

然后filename的hash改成contenthash

js兼容问题

  1. babel能处理箭头函数,点点点运算符等,但无法处理async,promise,includes等,这里使用code-js
  2. 安装npm i core-js,
  3. 全量加载在main.js中import 'core-js'
  4. 按需加载,在main.js中import "core-js/es/promise"
  5. 自动加载,在babel的配置文件中配置,无需在main.js中引入,注意多了一层中括号,注意这个兼容范围跟package.json中的browserslist有关
lua 复制代码
 presets:[['@babel/preset-env',{
    useBuiltIns:'usage',//按需加载自动引入
    corejs:3//corejs版本
 }]]

pwa 离线访问

  1. 装包workbox-webpack-plugin
  2. 配置文件中,
javascript 复制代码
const WorkboxPlugin=require('workbox-webpack-plugin')
...
new WorkboxPlugin.GenerateSW({
    //这些选项帮助快速启用serviceworkers
    //不允许遗留任何旧的
    clientsClaim:true,
    skipWaiting:true
})
  1. 需要在main.js中加入代码
javascript 复制代码
if("serviceWorker" in navigator){
    window.addEventListener("load",()=>{
        navigator.serviceWorker.register('/service-work.js').then(registration=>{
            console.log("SW registered:"+registration)
        }).catch(registrationError=>{
            console.log("SW registration failed:"+registrationError)
        })
    })
}

注意打开需要以dist为根服务器地址,不然会找不到资源

这里可以用npm i serve -g,然后serve dist

总结

  1. 提升开发体验
    · source map,让开发或上线的代码有更准确的错误提示
  2. 提升webpack打包构建速度
    · HotModuleReplacement,开发时只更新修改的部分
    · OneOf,让文件一旦被某个loader处理了,就不继续遍历,提升打包速度
    · Include/Exclude 排除或只检测某些文件,处理的文件更少,速度更快。
    · Cache对babel和eslint处理结果进行缓存,让第二次打包速度更快
    · thead,多进程处理babel和eslint任务
    3.减少代码体积
    · Tree Shaking 剔除了没有使用的多余代码,让代码体积更小
    · @babel/plugin-transform-runtime 插件对 babel 进行处理,让辅助代码从中引入,而不是每个文件都生成辅助代码,从而体积更小,
    · Image Minimizer对项目中图片进行压缩,体积更小,请求速度更快。(需要注意的是,如果项目中图片都是在线链接,那么就不需 要了。本地项目静态图片才需要进行压缩。)
  3. 优化代码运行性能
    · code split对代码进行分割成多个js 文件,从而便单个文件体积更小,并行加载js速度更快,并通过 import 动态导入语法进行按需 加载,从而达到需要使用时才加载该资源,不用时不加载资源。
    · Preload、prefetch 对代码进行提前加载,等未来需要便用时就能直接使用,从而用户体验更好
    · network cache 能对输出资源文件进行更好的命名,将来好做缓存,从而用户体验更好。
    · core-js 对js 进行容性处理,让我们代码能运行在低版本浏览器。
    · PWA能让代码离线也能访问,从而提升用户体验。

react配置

  1. eslintrc文件,还需要安装eslint-config-react-app
css 复制代码
extends:['react-app'],
parserOptions:{
    babelOptions:{
        presets:[
            //解决页面报错问题
            ['babel-preset-react-app',false],
            "babel-preset-react-app/prod"
        ]
    }
}
  1. babel-loader,test:/.jsx?$/
  2. babel需要安装预设包babel-preset-react-app,babel.config.js,react-app包含了core-js等,无需多余配置
vbnet 复制代码
presets:["react-app"]

注意使用babel-preset-react-app时,打包会报错,需要安装cross-env,然后配置package.json中的dev指令

json 复制代码
"dev":"cross-env NODE_ENV=development webpack serve --config ./config/webpack.dev.js"
  1. 安装 npm i react react-dom
  2. 在webpack配置中加入
css 复制代码
resolve:{
    //自动补全文件后缀
    extensions:['.jsx','.js','.json']
}

然后可以运行简单的react项目。 6. react的hmr

typescript 复制代码
devServer中hot:true
npm i -D @pmmmwh/react-refresh-webpack=plugin react-refresh
配置文件中
1.const ReactRefreshPlugin=require('@pmmmwh/react-refresh-webpack=plugin')
2.babel-loader的options中加入
plugins:[
    'react-refresh/babel'
],
3.在外层plugins中
new ReactRefreshPlugin()
  1. react devServer路由刷新404问题,需要在DevServer中加入配置historyApiFallback:true

生产模式配置

favicon.icon的显示,将public中资源复制到dist,下载插件copy-webpack-plugin

css 复制代码
const CopyPlugin=require('copy-webpack-plugin')
...
new CopyPlugin({
    patterns:[
        {
            from:path.resolve(__dirname,'../public'),
            to:path.resolve(__dirname,'../dist'),
            globOptions:{
            ignore:["**/index.html"]
             }
        }
    ]
})

合并生产和开发配置

在webpack.config.js中使用process.env.NODE_ENV获取当前是production还是development

sql 复制代码
const isProduction=process.env.NODE_ENV==='production'
1. output路径区分
2. babel-loader的plugins:[
    !isProduction&&"react-refresh/babel"
]
3. copy功能,MiniCssExtractPlugin在生产模式用
4.过滤数组用.filter(Boolean)
5. mode,devtool,minimizer
6. webpack配置的minimize:isProduction,表示是否需要压缩,为true的话minimizer选项才生效

使用antd的优化

因为antd使用的less,这里对less-loader做一些改造 在返回样式处理函数中

javascript 复制代码
const getStyleLoader=(pre)=>{
    ...
    pre&&{
        loader:pre,
        options:pre==='less-loader'?{
            //antd自定义主题色
            lessOption:{
                modifyVars:{
                    "@primary-color":"#1da57a",
                },
                javascriptEnabled:true
            }
        }:{}
    }
}
//注意main.js中应当引入antd的less样式

打包优化,splitChunk中进行分包,以及可以performance:false关闭性能分析,提升打包速度

vue编译

以vue3为例

  1. 安装vue-loader,并修改配置
javascript 复制代码
const {VueLoaderPlugin}=require('vue-loader')
...
{
    test:/\.vue$/,
    loader:'vue-loader'
}
...
new VueLoaderPlugin()
  1. 安装vue-style-loader,并将style-loader改为vue-style-loader
  2. eslint配置
vbnet 复制代码
root:true,
env:{
    node:true
},
extends:["plugin:vue/vue3-essential","eslint-recommended"],
parserOptions:{
    parser:"@babel/eslint-parser"
}
//需要下载包@babel/eslint-parser,eslint-plugin-vue
  1. babel配置
perl 复制代码
presets:["@vue/cli-plugin-babel/preset"]

需要下载包@vue/cli-plugin-babel

  1. 然后运行会提示vue某环境变量未定义,这里修改一下webpack配置
javascript 复制代码
const {DefinePlugin}=require("webpack")
...
//cross-env的环境变量是给打包工具使用的
//DefinePlugin的环境变量是给源代码使用的
new DefinePlugin({
    __VUE_OPTIONS_API__:true,
    __VUE_PROD_DEVTOOLS__:false
}}
  1. resolve:{ extensions:['.vue','.js','.json']}

element-plus

  1. 按需导入,参照官网配置
  2. 配置路径别名,在resolve中添加配置
bash 复制代码
alias:{
    "@":path.resolve(__dirname:'../src')
}

原理

loader

  1. pre:前置loader,normal:普通,inline:内联,post:后置,相同优先级loader从下到上,从右到左执行。
javascript 复制代码
{
    enforce:'pre',//或'post'
    test:/\.js$/,
    loader:'loader1'
}
  1. 注意inline loader的写法
javascript 复制代码
import Styles from 'style-loader!css-loader?modules!./style.css'
import Styles from '!style-loader!css-loader?modules!./style.css'//表示跳过normal loader
import Styles from '-!style-loader!css-loader?modules!./style.css'//表示跳过pre和normal loader
import Styles from '!!style-loader!css-loader?modules!./style.css'//表示跳过pre,post和normal loader
  1. 写一个loader

loader本质是一个函数

content是文件内容,map sourceMap,meta 别的loader传递的数据

javascript 复制代码
//loader.js
module.exports=function(content,map,meta){
    return content
}
//webpack.config.js
...
loader:'./loader/loader.js'
...
  1. 4种loader,同步、异步、raw、pitch
javascript 复制代码
// 同步loader
// module.exports=function (content){
//     console.log(content)
//     return content
// }

module.exports=function (content,map,meta){
    // callback第一个参数表示是否有错误
    // 第二个参数是处理后的内容
    // 第三个
    // 第四个给下一个loader的参数
    console.log(content)
    //如清除console.log的写法
    content.replace(/console\.log\(.*\);?/g,"")
   this.callback(null,content,map,meta)
}

===

javascript 复制代码
// 异步loader
module.exports=function (content,map,meta){
    console.log('异步loader')
    const callback=this.async()
    setTimeout(()=>{
     callback(null,content,map,meta)
    },1000)
   
}

======

javascript 复制代码
// raw loader,接收的content是buffer数据

module.exports=function (content){
    console.log(content)
//    this.callback(null,content,map,meta)
return content
}
module.exports.raw=true
// 另一种写法
// function loaderName(content){
// return content

// }
// loaderName.raw=true
// module.exports=loaderName

======

javascript 复制代码
// pitch loader
module.exports=function (content){
    console.log(content)
//    this.callback(null,content,map,meta)
return content
}
// pitch 方法的执行顺序在loader解析前,
// 如use:['loader1','loader2','loader3'],pitch方法执行顺序loader1>loader2>loader3,然后再执行loader解析
// 如果pitch2有return,则不会执行pitch3,loader2,loader3,只会执行pitch loader2前面的loader1
module.exports.pitch=function(){

}
  1. 一些loader中的api

this.async异步回调,返回this.callback,const callback=this.async()

this.callback(err,content,sourceMap?,meta?)

this.getOptions(schema),获取loader的option

this.emitFile(name,content,sourceMap)产生一个文件 this.utils.contextify(context,request)返回一个相对路径 this.utils.absolutify(context,request)返回一个绝对路径

javascript 复制代码
//schema.json:
{
    "type":"object",
    "properties":{
        "author":{
            "type":"string"
        }
    },
    "additionalProperties":false
}
//additionalProperties表示不能新增字段
//loader.js
const schema=require('./schema.json')
...
const options=this.getOptions(schema)
console.log(schema.author)

实现简易babel-loader

  1. 安装@babel/core @babel/preset-env
  2. 在webpack中配置对js使用自定义的loader文件
css 复制代码
  {
                test:/\.js$/,
                loader:'./loaders/babel-loader.js',
                options:{
                    presets:['@babel/preset-env']
                }
            }
  1. 新建babel-loader.js文件,babel中的方法可以参考官网
javascript 复制代码
const schema=require('./schema.json')
const babel=require('@babel/core')
module.exports=function(content){
    const callback=this.async()
    const options=this.getOptions(schema)
    babel.transform(content,options,function(err,result){
        if(err){
            callback(err)
        }else{
            callback(null,result.code)
        }
        
    })
}
  1. schema配置
json 复制代码
{
    "type":"object",
    "properties":{
        "preset":{
            "type":"array"
        }
    },
    "additionalProperties":true
}

实现file-loader

  1. loader写法
javascript 复制代码
const loaderUtils=require('loader-utils')

module.exports=function(content){
// 1.根据文件内容生成hash值文件名(借助webpack官方提供的处理文件名的方法)
const interpolateName=loaderUtils.interpolateName(this,"[hash].[ext][query]",{
    content
})
console.log(interpolateName)
// 2.将文件输出
this.emitFile(interpolateName,content)
// 3.返回module.exports="文件路径(文件名)"
return `module.exports="${interpolateName}"`
}
module.exports.raw=true
javascript 复制代码
 {
                test:/\.(png|jpe?g|gif)$/,
                loader:'./loaders/file-loader.js',
                type:'javascript/auto'//阻止webpack默认的asset去处理这些资源
                
            },

实现style-loader

ini 复制代码
module.exports=function(content){
    //直接使用style-laoder只能处理样式,不能处理样式中引入其他资源的问题
    //借助css-loader处理样式中引入其他资源的问题
    //问题是css-loader暴露了一段js代码,style-loader需要执行js代码,得到返回值
    const script=`
    const styleEL=document.createElement('style');
    styleEL.innerHTML=${JSON.stringify(content)};
    document.head.appendChild(styleEL)
    `
    return script
}
  1. 基于以上代码,要处理css-loader处理后的代码,首先注释掉script等内容
kotlin 复制代码
module.exports.pitch=function(remainingRequest){
    // remainingRequest返回两段绝对路径,用!分隔,一个css-loader的,一个css的
    // 1.绝对路径处理成两个相对路径
   const relativePath= remainingRequest.split('!').map(absolutePath=>{
        return this.utils.contextify(this.context,absolutePath)
    }).join('!')
    console.log(relativePath)

    const script=`
    import style from "!!${relativePath}"
    const styleEL=document.createElement('style');
    styleEL.innerHTML=style
    document.head.appendChild(styleEL)
    `
    // 终止后面loader
    return script
}

plugin 原理

  1. webpack打包过程会触发一系列tapable钩子事件

tap 可注册同步和异步钩子

tapAsync 回调异步

tapPromise promise方式异步

  1. 基本结构
javascript 复制代码
class Plugin1{
    constructor(options){
        this.options=options
        
    }
        apply(compiler){
        console.log('test apply')
        compiler.hooks.environment.tap('TestPlugin',()=>{
            console.log('test Env')
        })
        compiler.hooks.make.tap('TestPlugin',(compilation)=>{
            debugger;
            console.log(compilation)
            compilation.hooks.seal.tap('Test Plugin',()=>{
                console.log('seal')
            })
        })
    }
}
// node 调试
/**
 * "debug":"node --inspect-brk ./node_modules/webpack-cli/bin/cli.js"
 * package.json添加debug的命令,代码需要调试的地方加入debugger;浏览器控制台会出现node图标,点击会看到调试数据,可用于观测compilation结构
 */
相关推荐
腾讯TNTWeb前端团队4 小时前
helux v5 发布了,像pinia一样优雅地管理你的react状态吧
前端·javascript·react.js
范文杰8 小时前
AI 时代如何更高效开发前端组件?21st.dev 给了一种答案
前端·ai编程
拉不动的猪8 小时前
刷刷题50(常见的js数据通信与渲染问题)
前端·javascript·面试
拉不动的猪8 小时前
JS多线程Webworks中的几种实战场景演示
前端·javascript·面试
FreeCultureBoy9 小时前
macOS 命令行 原生挂载 webdav 方法
前端
uhakadotcom9 小时前
Astro 框架:快速构建内容驱动型网站的利器
前端·javascript·面试
uhakadotcom9 小时前
了解Nest.js和Next.js:如何选择合适的框架
前端·javascript·面试
uhakadotcom9 小时前
React与Next.js:基础知识及应用场景
前端·面试·github
uhakadotcom9 小时前
Remix 框架:性能与易用性的完美结合
前端·javascript·面试
uhakadotcom10 小时前
Node.js 包管理器:npm vs pnpm
前端·javascript·面试