webpack 基本语法

都是在 webpack.config.js 页面上写的

  1. const path = require('path') // path 模块是node自带的 可自行查看
  2. module.exports = {
  3. mode:'none',//工作模式 development(开发模式 会添加一些注释) none (原始状态) production(生产模式也就是打包发布的模式 自动压缩模式)
  4. entry:'./src/index.js',// 需要打包的文件路径 入口文件 所有需要打包的都需要在入口文件import加入
  5. output:{ // 输出路径 类似重定向路径 一开始打包的路径为 dist/main.js 现在为dist/bundle.js
  6. filename:'bundle.js',
  7. path:path.join(__dirname,'dist'),// path 必须为绝对路径否则会报错。 dist 可修改
  8. //如果publicPath为空 则表示在根目录下会找不到打包后的图片 而图片打包后在dist目录下,所以需要指定
  9. publicPath:'dist/' //publicPath 是打包路径
  10. },
  11. }
  12. // 默认打包的路径为 src/index.js ==> dist/main.js

webpack 模块

webpack 只是一个打包工具,是将文件打包不会对里面的代码进行处理 如果需要对代码进行处理就需要使用到 loader loader是webpack对文件进行处理的模块

rules里面的每个对象就是一个模块

use 还有一种写法可以写绝对路径 就跟require一样 。use是以test中写入的XX文件格式用use下的模块来执行

css-loader 模块

css-loader 安装完css-loader 你会发现css格式的文件还是无法执行 也会就需要安装style-loader

  1. yarn add css-loader --dev // css-loader
  2. yarr add style-loader --dev //style-loader
const path = require('path') 

module.exports = {
    mode:'none',
    entry:'./src/index.js',
    output:{ 
        filename:'bundle.js',
        path:path.join(__dirname,'dist'),
        publicPath:'dist/'
    },
    module:{//加入模块
        rules:[ // rules:[] 是模块的规则加载器 webpack 提供了很多关于 loader插件
            {
                test:/\.css$/, // 以css结尾的文件用use中指定的loader
                // css-loader: 将文件安装css来执行 style-loader:将css文件用style标签追加到页面上
                use:['style-loader','css-loader']  // 注意:use如果有多个loader它的执行顺序是从后往前执行 也就是先执行css-loader
            }, // 每一个对象就是一个模块加载器
    },

}

webpack 是把所有的css ,html 以javascript来呈现的


/* 支持加载其他的样式加载模块 */
@import url(./style.css); 

body{
    min-height: 100vh;
    color: white;
    background: url(./background.png); 
    background-size: cover;
}
/* webpack 打包到css 文件时将文件按 css-loader来打包但在打包过程中遇到了.png文件 png 文件就会作为一个模块打包,webpack 将png文件用url-loader来处理 */

/* css-loader 会触发文件中别的文件 如 png css 都可以触发 */

babel-loader 模块

webpack不会处理js中es6和更高版本的新特性就需要用babel-loader来代替原来的js打包工具 babel-loader只相当于一个平台 在babel里面的@babel/core 和 @babel/preset-env 必须包含这两个才会打包生效

yarn add babel-loader @babel/core @babel/preset-env --dev
module:{
    rules:[ 
        { 
            test:/\.js$/,
            use:{
                loader:'babel-loader',
                options:{
                    presets:['@babel/preset-env'] //@babel/preset-env 包含了es6的所有新特性
                }
            }
        },
        {// 多个模块就往后累加
                test:/\.css$/, // 以css结尾的文件用use中指定的loader
                // css-loader: 将文件安装css来执行 style-loader:将css文件用style标签追加到页面上
                use:['style-loader','css-loader']  // 注意:use如果有多个loader它的执行顺序是从后往前执行 也就是先执行css-loader
            } // 每一个对象就是一个模块加载器

    ]
},

file-loader 模块 和url-loader模块 文件模块

注意:小文件使用url-loader可以减少请求次数 大文件用file-loader来单独提取存放可以提高加载速度

yarn add file-loader --dev
yarn add url-loader --dev
{ // 文件资源加载器 图片  //file-loader
    test:/\.png$/,
    //use 也可以是个对象
    use:{
        loader:'url-loader' , // loader属性 用url-loader来处理.png结尾的文件 url-loader转换出来的文件是base64格式
        options:{ // 配置选项  
            //options 中limit就是限制 如果文件大于10KB就使用file-loader 如果小于就使用url-loader 但要想使用file-loader 需要安装 file-loader 
            limit: 10 * 1024 //10kb
        }
    } //webpack 执行时候是以file-loader来处理png文件

},//直接加到module下面的rules数组中就行

html-loader 模块

yarn add html-loader --dev
{ // html的打包loader
    //html-loader默认只会处理 img标签的资源 a标签的不会处理
    test:/\.html$/,
    use:{
        loader:'html-loader',
        options:{
            sources:{ //webpakc默认可以识别img的src文件但不识别a标签的href  所有加上a标签的href属性
                list:[
                    {
                        tag:'img',
                        attribute:'data-src',
                        type:'src'
                    },{
                        tag:'a',
                        attribute:'href',
                        type:'src'
                    }
                ]
            } // attrs 默认是 img:src 我们可以按照这个格式添加 a:href
        }
    }
},

自定义loader

比如我们想让项目中md文件加入打包就需要自己模拟个

loader相当于一个管道 如 文件类似是md格式的就需要通过管道(loader)转为javascript(管道可以有多个)。如果想让md成为loader直接上传到npm上就可以使用了

md文件
webpack1.png

markdown-loader.js 就是use 引入的 markdown-loader模块

需要安装 marked 模块 模块作用是将 md 文件转为html html格式又不是js格式的需要在转换下有两种写法 下面介绍:

这里使用的是第二种

yarn add marked --dev //将md文件的内容转为html的模块
{ // 自己模拟的原理
    test:/\.md$/,
    use:[
       'html-loader',
       './markdown-loader'
    ] // use 跟requirt一样可以使用绝对路径引入
}

markdown-loader.js

const marked = require('marked')

// 模拟一个loader的工作原理 webpack 最重要的是loader的转换器
module.exports = source =>{ //输入的是文件的内容 输出是文件处理后的结果
    console.log(source) // 打印的是文件里面的内容
    // return 'console.log("hello~")' //需要返回的是javascript类型的格式

    const html = marked(source) // 返回的是html格式
    // 两种方法 
    //第一种直接module.exports 解释下为什么需要json.stringify()因为直接module.exports = html会是文件中的换行符什么导致错乱
    // 在打包的bundle.js中 结果是 module.exports = "<h1 id=\"关于我\">关于我</h1>\n<p>我是何晓丹,一个手艺人~</p>\n"
    // return `module.exports = ${ JSON.stringify(html) }`

    //第二种使用html-loader
    return html //在配置文件中修改use加入html-loader 

}
// 我们可以用多个loader 来完成一个loader

webpack 插件

清除打包文件目录 dist

clean-webpack-plugin 插件

yarn add clean-webpack-plugin --dev

webpack.config.js

const path = require('path') // path 模块是node自带的 可自行查看
// 注名字不能乱写
const { CleanWebpackPlugin } = require('clean-webpack-plugin') //清除上次的打包目录(dist) 

module.exports = {
    mode:'none',
    entry:'./src/index.js',
    output:{ 
        filename:'bundle.js',
        path:path.join(__dirname,'dist'),
        publicPath:'dist/'
    },
    module:{//模块
        rules:[ // rules:[] 是模块的规则加载器 webpack 提供了很多关于 loader插件
            { //  yarn add babel-loader @babel/core @babel/preset-env --dev
                test:/\.js$/,
                use:{
                    loader:'babel-loader',
                    options:{
                        presets:['@babel/preset-env'] //@babel/preset-env 包含了es6的所有新特性
                    }
                }
            }
            .......
        ]
    },
    plugins:[ // 插件
        new CleanWebpackPlugin() // 清除打包文件目录
    ]
}

自动生成html文件插件

html-webpack-plugin

使用这个插件的原因是:没使用之前index.html在打包的范围外,而且需要确定index.html里的文件引用是否正确

webpack是知道项目中生成的包,会自动引用到html文件中。将下图中index.html生成到dist目录里 这样发布时候只需要发布dist文件
webpack2.png

yarn add html-webpack-plugin --dev

安装完成后在 webpack.config.js中引入插件

const HtmlWebpackPlugin  = require('html-webpack-plugin') 

plugins:[ // 插件
        new CleanWebpackPlugin(), // 清除打包文件目录
        new HtmlWebpackPlugin() //自动生成html
]

另外需要注意的是 output中的publicPath的值 如果html在dist目录下里面的资源也在是不需要指定具体目录的

webpack3.png

index.html在dist下 ,所以里面引入文件就不需要加dist publicPath不需要设置值。 因为publicPath 默认就是在打包目录下找资源文件

HtmlWebpackPlugin 插件 的options选项

  plugins:[ // 插件

    new HtmlWebpackPlugin({

      title:'webpack html plugin', // 修改自动生成html文件的标题头

      meta:{ // 添加 meta标签 <meta name="viewport" content="width=device-width">

        viewport:'width=device-width'

      },

      template:'./src/index.html' // 根据模板打包

    }), //自动生成html
    //可根据自己需要看是否需要加多个html页面这两个html页面打包出来是一致的内容,可修改options 这里只是介绍有这种功能
    new HtmlWebpackPlugin({ // 一个HtmlWebpackPlugin代表一个html文件 如果需要多个就写多个HtmlWebpackPlugin插件
        filename:'about.html'
    })

  ]

copy-webpack-plugin 插件 复制文件到dist目录

    plugins:[ // 插件
        new CleanWebpackPlugin(), // 清除打包文件目录
        new HtmlWebpackPlugin({ // 一个HtmlWebpackPlugin生产一个html页面
            title:'webpack html plugin',
            meta:{
                viewport:'width=device-width'
            },
            template:'./src/index.html'
        }), //自动生成html
        // yarn add copy-webpack-plugin --dev 
        new CopyWebpackPlugin({ //文件copy 从public 复制到dist下 to:"值为dist下的文件夹" 
            patterns: [
                { from: "public", to: "dest" }
            ]
        }),
    ]

可以根据需求到webpack官网 查找插件和模块 根据关键字查找插件和模块

自定义webpack插件

参考网站:https://blog.csdn.net/qq_36380426/article/details/104471422

Webpack 插件组成

在自定义插件之前,我们需要了解,一个 Webpack 插件由哪些构成,下面摘抄文档:

一个具名 JavaScript 函数;
在它的原型上定义 apply 方法;
指定一个触及到 Webpack 本身的事件钩子;
操作 Webpack 内部的实例特定数据;
在实现功能后调用 Webpack 提供的 callback。

插件由一个构造函数实例化出来。构造函数定义 apply 方法,在安装插件时,apply 方法会被 Webpack compiler 调用一次。apply 方法可以接收一个 Webpack compiler对象的引用,从而可以在回调函数中访问到 compiler 对象。

官方文档提供一个简单的插件结构:

class HelloWorldPlugin {
  apply(compiler) {
    compiler.hooks.done.tap('Hello World Plugin', (
      stats /* 在 hook 被触及时,会将 stats 作为参数传入。 */
    ) => {
      console.log('Hello World!');
    });
  }
}
module.exports = HelloWorldPlugin;

使用插件:

// webpack.config.js
var HelloWorldPlugin = require('hello-world');

module.exports = {
  // ... 这里是其他配置 ...
  plugins: [new HelloWorldPlugin({ options: true })]
};

apply 方法为插件原型方法,接收 compiler 作为参数。

选择插件触发时机

选择插件触发时机,其实是选择插件触发的 compiler 钩子(即何时触发插件)。
Webpack 提供钩子有很多,这里简单介绍几个,完整具体可参考文档《Compiler Hooks》:

entryOption : 在 webpack 选项中的 entry 配置项 处理过之后,执行插件。
afterPlugins : 设置完初始插件之后,执行插件。
compilation : 编译创建之后,生成文件之前,执行插件。。
emit : 生成资源到 output 目录之前。
done : 编译完成。

事件钩子函数

compiler.hooks 下指定事件钩子函数,便会触发钩子时,执行回调函数。
Webpack 提供三种触发钩子的方法:

  • tap :以同步方式触发钩子;
  • tapAsync :以异步方式触发钩子;
  • tapPromise :以异步方式触发钩子,返回 Promise;

MyPlugin.js

class MyPlugin{
    apply(compiler){
        console.log("myplugin 启动")

        compiler.hooks.emit.tap('MyPlugin',(compilation) => { 
            // compilation => 可以理解为此次打包的上下文 所以打包的文件内容

            for(const name in compilation.assets){ // compilation.assets是需要打包的对象
                //name 是打包的文件名称
                // console.log(compilation.assets[name].source()) //source()访问到文件的具体值
                //endsWith('.js') 判断字符串是否以.js的为结尾 true返回的是此方法 否则false
                if(name.endsWith('.js')){
                    const contents = compilation.assets[name].source()
                    const withoutComments = contents.replace(/\/\*\*+\*\//g,'') //用正则给js中不用的注释删除
                    compilation.assets[name] = { //替换原来的数据 source() 是个方法调用时候给的就是具体值 赋值时候也要给函数,size为必写项。
                        source:()=> withoutComments,// function(){ return withoutComments }
                        size:()=> withoutComments.length
                    }
                }
            }
        })
    }
}

module.exports = MyPlugin;

在webpack.config.js引入插件

const MyPlugin = require('./MyPlugin.js')

//在插件中调用
    plugins:[ // 插件
        new MyPlugin()
    ]

没使用插件前

webpack4.png

使用插件后

webpack5.png