博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
webpack从入门到进阶
阅读量:4083 次
发布时间:2019-05-25

本文共 23465 字,大约阅读时间需要 78 分钟。

前言

在我们平时的开发中,会经常使用到webpack,但很多时候我们都不做任何配置(webpack4)也可以进行开发,但是默认的配置真的够我们使用了吗?所以本文将带你开启webpack的大门。

前置知识篇

学习webpack前先让我们一起学习下必要的辅助概念

路径知识

在此之前我们有必要了解下webpack中会使用到的NodeJS路径知识:

核心概念之 Entry

Entry 用来指定 webpack 的打包⼊口

Entry 的⽤法

单⼊⼝:entry 是⼀个字符串

module.exports = {    entry: './src/index.js'};复制代码

多⼊⼝:entry 是⼀个对象

module.exports = {    entry: {        index: './src/index.js',        manager: './src/manager.js'    }};复制代码

核⼼概念之 Output

Output ⽤来告诉 webpack 如何将编译后的⽂件输出到磁盘的指定路径

Output 的⽤法:单⼊⼝配置

module.exports = {    entry: './src/index.js',    output: {        filename: 'bundle.js’,        path: __dirname + '/dist'    }};复制代码

Output 的⽤法:多⼊⼝配置

module.exports = {    entry: {        app: './src/app.js',        search: './src/search.js'    },    output: {        filename: '[name].js',        path: __dirname + '/dist'    }};复制代码

通过[name]占位符确保⽂件名称的唯⼀

核⼼概念之 Loader

本身是一个函数,接受源文件作为参数,返回转换的结果。

常⻅的 Loader 有哪些?

babel-loader, 将es6+语法转换为es3/es5语法css-loader,style-loader,scss-loader, less-loader,postcss-loader,file-loader, 进行图片字体等资源的打包ts-loader, 将ts转换为jsraw-loader 将文件以字符串形式导入...复制代码

Loader 的⽤法

const path = require('path');module.exports = {    output: {        filename: 'bundle.js'    },    module: {        rules: [            { test: /\.js$/, use: 'babel-loader' }        ]    }};复制代码

test 指定匹配规则

use 指定使⽤的 loader 名称

核⼼概念之 Plugins

插件⽤于 bundle ⽂件的优化,资源管理和环境变量注⼊ 作⽤于整个构建过程

常见的Plugins

CleanWebpackPlugin 清理构建目录MiniCssExtractPlugin 将css从bundle文件里提取为一个独立的css文件htmlWebpackPlugin 创建html去承载输出的bundle文件UglifyWebpackPlgin 压缩js 去除console等指定语句...复制代码

Plugins 的⽤法

const path = require('path');module.exports = {    output: {        filename: 'bundle.js'    },    plugins: [        new HtmlWebpackPlugin({template:'./src/index.html'})    ]};复制代码

所有的插件都应放到plugins数组里

实战篇

在此之前我们先安装下必须依赖:

npm install webpack webpack-cli -D复制代码

转换 ES6 为 ES5语法

安装所需依赖 npm i @babel/core @babel/preset-env babel-loader -D

配置如下:

babel的配置⽂件是:.babelrc[项目根目录下新建]

module: {    rules: [        {            test: /\.js$/,            use: 'babel-loader'        },    ]};复制代码

增加ES6的babel preset配置

babel的配置⽂件是:.babelrc[项目根目录下新建]

{    "presets": [        "@babel/preset-env" //可以根据配置的目标浏览器或者运行环境来自动将ES2015+的代码转换为es5。    ]}复制代码

解析 React JSX

安装所需依赖

npm i @babel/preset-react -D

配置如下:

babel的配置⽂件是:.babelrc[项目根目录下新建]

{    "presets": [        "@babel/preset-env",        "@babel/preset-react"    ],    "plugins": []}复制代码

资源解析:解析 CSS

npm install style-loader css-loader less-loader -D复制代码
module: {        rules: [            {                test: /\.css$/,                use: [                    'style-loader',                    'css-loader'                ]            },            {                test: /\.less$/,                use: [                    'style-loader',                    'css-loader',                    'less-loader'                ]            },        ]},复制代码

css-loader ⽤于加载 .css ⽂件,并且转换成 commonjs 对象

style-loader 将样式通过 <style> 标签插⼊到 head 中

style-loader配置:

options: {    insertAt: 'top', // 样式插入到     singleton: true, //将所有的style标签合并成一个}复制代码

less-loader 将less语法转换为css语法

注意: loader的执行顺序的从下到上,从右到左

资源解析:解析图⽚和字体文件

npm install url-loader file-loader -D复制代码
module: {        rules: [            {                test: /\.(png|jpg|gif|jpeg)$/,                use: [                    {                        loader: 'url-loader',                        options: {                            limit: 10240                        }                    }                ]            },            {                test: /\.(woff|woff2|eot|ttf|otf)$/,                use: 'file-loader'            }        ]},复制代码

file-loader ⽤于处理字体⽂件

url-loader 可以用来处理图⽚和字体 可以设置limit将较⼩资源⾃动 base64

url-loader中用到了file-loader

webpack 中的⽂件监听

⽂件监听是在发现源码发⽣变化时,⾃动重新构建出新的输出⽂件。

webpack 开启监听模式,有两种⽅式:

·启动 webpack 命令时,带上 --watch 参数 [推荐]

·在配置 webpack.config.js 中设置 watch: true

webpack 中的⽂件监听使⽤

在package.json中配置

{    "name": "hello-webpack",    "main": "index.js",    "scripts": {        "watch": "webpack --watch",        "build": "webpack --config webpack.prod.js",    },}复制代码

热更新:webpack-dev-server

npm install webpack-dev-server -D复制代码

package.json中的scripts对象下配置:

{    "scripts": {        "dev": "webpack-dev-server --open"    },}复制代码

--open代表自动打开浏览器

webpack-dev-server 不刷新浏览器 不输出⽂件,⽽是放在内存中

使⽤ HotModuleReplacementPlugin插件(webpack内置插件)可以使浏览器自动刷新

webpack.config.js中配置:

const webpack = require('webpack');plugins: [    new webpack.HotModuleReplacementPlugin(),],devServer:{    hot: true,    contentBase:'./dist'}复制代码

热更新的原理分析

Webpack Compile: 将 JS 编译成 Bundle

HMR Server: 将热更新的⽂件输出给 HMR Rumtime

Bundle server: 提供⽂件在浏览器的访问 (比如localhost:8080/Bundle.js)

HMR Rumtime: 在开发阶段的打包阶段会被注⼊到浏览器端的bundle.jjs中,浏览器端的bundle.jjs会和浏览器建立一个连接,通常是一个websocket,这样就可以更新文件的变化,当收到文件的一些变化消息时会自动更新文件

bundle.js: 构建输出的⽂件

⽂件哈希值

⽂件哈希值就是打包后输出的⽂件名的后缀

 

 

 

⽂件哈希值如何⽣成

Hash:和整个项⽬的构建相关,只要项⽬⽂件有修改,整个项⽬构建的 hash 值就会更改

Chunkhash:和 webpack 打包的 chunk 有关,不同的 entry 会⽣成不同的 chunkhash 值

Contenthash:根据⽂件内容来定义 hash ,⽂件内容不变,则 contenthash 不变

JS 的⽂件哈希设置

设置 output 的 filename,使⽤ [chunkhash]

output: {    filename: '[name][chunkhash:8].js',    path: __dirname + '/dist'}复制代码

注意: chunkhash无法和热更新一起使用

CSS 的⽂件哈希设置

设置 MiniCssExtractPlugin 的 filename,

使⽤ [contenthash]

npm i install mini-css-extract-plugin -D复制代码
const MiniCssExtractPlugin = require('mini-css-extract-plugin');plugins: [     new MiniCssExtractPlugin({         filename: `[name][contenthash:8].css`     });]复制代码

如果想把css提取出来,那么style-loader就不能用了,因为两个是互斥的,所以我们可以这样写:

module: {        rules: [            {                test: /\.css$/,                use: [                    - 'style-loader',                    + MiniCssExtractPlugin.loader                    'css-loader'                ]            },            {                test: /\.less$/,                use: [                    - 'style-loader',                    + MiniCssExtractPlugin.loader                    'css-loader',                    'less-loader'                ]            },        ]},复制代码

图片&字体文件哈希设置

module: {rules: [    {        test: /\.(png|svg|jpg|gif)$/,        use: [{              loader: 'file-loader’,              options: {                 name: 'img/[name][hash:8].[ext] '              }        }]    }]复制代码

占位符介绍

[ext] 资源名后缀 [name] 文件名称 [path] 文件的相对路径 [folder] 文件所在的文件夹 [contenthash] 文件的内容hash 默认md5生成 [hash] 文件内容的hash 默认md5生成

代码压缩

HTML 压缩

CSS 压缩

JS 压缩

JS ⽂件的压缩

npm install uglifyjs-webpack-plugin -Dconst UglifyJsPlugin = require('uglifyjs-webpack-plugin');plugins: [     new UglifyJsPlugin()]复制代码

CSS ⽂件的压缩

npm install optimize-css-assets-webpack-plugin cssnano -D复制代码

使⽤ optimize-css-assets-webpack-plugin, 同时使⽤ cssnano[预处理器]

const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');plugins: [     new OptimizeCSSAssetsPlugin({         assetNameRegExp: /\.css$/g,         cssProcessor: require('cssnano’)    })]复制代码

html ⽂件的压缩

npm install html-webpack-plugin -D复制代码

使用html-webpack-plugin,设置压缩参数

webpack.config.js中

const HtmlWebpackPlugin = require('html-webpack-plugin');plugins: [     new HtmlWebpackPlugin({         template: path.join(__dirname, '/dist/index.html’),         filename: 'index.html’,         chunks: ['index’],         inject: true,         minify: {             html5: true,             collapseWhitespace: true, //去除空格             preserveLineBreaks: false, //去除换行             minifyCSS: true,             minifyJS: true,             removeComments: false //去除注释        }    })]复制代码

自动清理构建⽬录

避免构建前每次都需要⼿动删除 dist

使⽤ clean-webpack-plugin

默认会删除 output 指定的输出⽬录

npm install clean-webpack-plugin -Dconst CleanWebpackPlugin = require('clean-webpack-plugin');plugins: [    new CleanWebpackPlugin(),]复制代码

PostCSS 插件 autoprefixer补充css3前缀

IE: Trident(-ms) 火狐: Geko(-moz) 谷歌: Webkit(-webkit) 欧鹏:Presto(-o)

npm install postcss-loader autoprefixer -D复制代码
module: {        rules: [            {                test: /\.less$/,                use: [                    MiniCssExtractPlugin.loader,                    'css-loader',                    'less-loader',                    {                        loader: 'postcss-loader',                        options: {                            plugins: () => [                                require('autoprefixer')({                                    browsers: ['last 2 version', '>1%', 'ios 7']                                })                            ]                        }                    },                ]            },        ]    },复制代码

移动端 CSS px ⾃动转换成 rem

rem 是什么?

W3C 对 rem 的定义: font-size of the root element

rem 和 px 的对⽐:

rem 是相对单位

px 是绝对单位

使⽤ px2rem-loader

⻚⾯渲染时计算根元素的 font-size 值,可以使⽤⼿淘的lib-flexible库

npm install px2rem-loader -Dnpm i lib-flexible raw-loader@0.5.1 -Smodule: {        rules: [            {                test: /\.less$/,                use: [                    MiniCssExtractPlugin.loader,                    'css-loader',                    'less-loader',                    {                        loader: 'postcss-loader',                        options: {                            plugins: () => [                                require('autoprefixer')({                                    browsers: ['last 2 version', '>1%', 'ios 7']                                })                            ]                        }                    },                    {                        loader: 'px2rem-loader',                        options: {                            remUnit: 75,                            remPrecision: 8 //转换好rem的小数点位数                        }                    }                ]            }        ]}复制代码

index.html中使用raw-loader内联lib-flexible

复制代码

raw-loader 内联 html

复制代码

多⻚⾯应⽤(MPA)概念

每⼀次⻚⾯跳转的时候,后台服务器都会给返回⼀个新的 html ⽂档, 这种类型的⽹站也就是多⻚⽹站,也叫做多⻚应⽤。

多⻚⾯打包通⽤⽅案: 动态获取 entry 并设置 html-webpack-plugin

利用glob

webpack.config.js中编写如下代码:

npm install glob -Dconst glob = require('glob');const HtmlWebpackExternalsPlugin = require('html-webpack-externals-plugin');const setMPA = () => {    const entry = {};    const htmlWebpackPlugins = [];    const entryFiles = glob.sync(path.join(__dirname, './src/*/index.js'));    Object.keys(entryFiles)        .map((index) => {            const entryFile = entryFiles[index];            const match = entryFile.match(/src\/(.*)\/index\.js/);            const pageName = match && match[1];            entry[pageName] = entryFile;            htmlWebpackPlugins.push(                new HtmlWebpackPlugin({                    inlineSource: '.css$',                    template: path.join(__dirname, `src/${pageName}/index.html`),                    filename: `${pageName}.html`,                    chunks: ['vendors', pageName],                    inject: true,                    minify: {                        html5: true,                        collapseWhitespace: true,                        preserveLineBreaks: false,                        minifyCSS: true,                        minifyJS: true,                        removeComments: false                    }                })            );        });    return {        entry,        htmlWebpackPlugins    }}const { entry, htmlWebpackPlugins } = setMPA();module.exports = {    entry: entry,    plugins: [].concat(htmlWebpackPlugins)}复制代码

sourceMap 类型

 

 

 

可以通过devtool:'cheap-source-map'配置

基础库分离

思路:将 react、react-dom,axios,element-ui 基础包通过 cdn 引⼊,不打⼊ bundle 中

npm install html-webpack-externals-plugin -D使⽤ html-webpackexternals-pluginconst HtmlWebpackExternalsPlugin = require('html-webpack-externals-plugin');plugins:[    new HtmlWebpackExternalsPlugin({                externals: [                      {                        module: 'react',                        entry: 'cdn.com/react.min.js',                        global: 'React',                      },                      {                        module: 'axios',                        entry: 'cdn.com/axios.min.js',                        global: 'Axios',                      },                ]    }),]复制代码

利⽤ SplitChunksPlugin 进⾏公共脚本分离

SplitChunksPlugin是Webpack4 内置的,替代webpack3的CommonsChunkPlugin插件

async 异步引⼊的库进⾏分离(默认)

initial 同步引⼊的库进⾏分离

all 所有引⼊的库进⾏分离(推荐)

optimization: {     splitChunks: {        chunks: 'all',        minSize: 30000,//抽离的公共包最小的大小        maxSize: 0, //抽离的公共包最大的大小        minChunks: 1, //一段代码多处都使用的次数 如果大于这里的次数就抽离成公共的文件        maxAsyncRequests: 5,        maxInitialRequests: 3,//浏览器同时请求的异步js的数量        name: true,        cacheGroups: {            vendors: {                test: /(axios|react)/,                priority: -10,                minChunks: 1            }        }    } }复制代码

treeShaking(摇树优化)

概念:1 个模块可能有多个⽅法,只要其中的某个⽅法使⽤到了,则整个⽂件都会被打到bundle ⾥⾯去,tree shaking 就是只把⽤到的⽅法打⼊ bundle ,没⽤到的⽅法会在uglify 阶段被擦除掉。

使⽤:webpack4 默认⽀持,在 .babelrc ⾥设置 modules: false 即可 要求:必须是 ES6 的语法,CommonJS 的⽅式不⽀持.

production mode的情况下默认开启

treeShaking的情况:

代码执⾏的结果不会被⽤到代码不会被执⾏,不可到达代码只会影响死变量(只写不读)复制代码

Tree-shaking 原理 利⽤ ES6 模块的特点:

只能作为模块顶层的语句出现

import 的模块名只能是字符串常量

import binding 是 immutable的

代码擦除: uglify 阶段删除⽆⽤代码

如果代码中有副作用则tree-shaking失效

可以在package.json中配置sideEffect:[] 比如babel-polyfill

构建后的代码存在⼤量闭包代码

 

 

 

⼤量作⽤域包裹代码,导致体积增⼤(模块越多越明显)

运⾏代码时创建的函数作⽤域变多,内存开销变⼤

模块转换分析

 

 

 

结论:

被 webpack 转换后的模块会带上⼀层包裹

import 会被转换成 __webpack_require

进⼀步分析 webpack 的模块机制

 

 

 

分析: 打包出来的是⼀个 IIFE (匿名闭包)

modules 是⼀个数组,每⼀项是⼀个模块初始化函数

__webpack_require ⽤来加载模块,返回 module.exports

使用scope hoisting消除大量闭包现象

原理:将所有模块的代码按照引⽤顺序放在⼀个函数作⽤域⾥,然后适当的重命名⼀些变量以防⽌变量名冲突

必须是 ES6 语法,CJS 不⽀持

plugins: [ new webpack.optimize.ModuleConcatenationPlugin()]复制代码

模块懒加载

webpack 有⼀个功能就是将你的代码库分割成chunks(语块),当代码运⾏到需要它们的时候再进⾏加载。

CommonJS:require.ensure

ES6:动态 import(⽬前还没有原⽣⽀持,需要 babel 转换)

如何使⽤动态 import?

安装 babel 插件 ES6:动态 import(⽬前还没有原⽣⽀持,需要 babel 转换)

在.babelrc中配置:

npm install @babel/plugin-syntax-dynamic-import --save-dev{    "plugins": ["@babel/plugin-syntax-dynamic-import"],}复制代码

代码中的使用:

loadComponent() {    import('./text.js').then((Text) => {        this.setState({            Text: Text.default        });    });}复制代码

这样的话text.js在打包时就会被自动拆分为一个单独的文件,当我们调用这个方法时才进行加载,也算是一个优化手段

webpack 打包库和组件

webpack 除了可以⽤来打包应⽤,也可以⽤来打包 js 库

实现⼀个加法库的打包

需要打包压缩版和⾮压缩版本

⽀持 AMD/CJS/ESM 模块引⼊

//src下index.jsexport default function add(a, b) {    return a + b;}复制代码

如何将库暴露出去?

只对.min 压缩可以使用TerserPlugin插件进行匹配

npm install terser-webpack-plugin -Dconst TerserPlugin = require('terser-webpack-plugin');module.exports = {    entry: {        'large-number': './src/index.js',        'large-number.min': './src/index.js'    },    output: {        filename: '[name].js',        library: 'largeNumber',        libraryTarget: 'umd',        libraryExport: 'default'    },    mode: 'none',    optimization: {        minimize: true,        minimizer: [            new TerserPlugin({                include: /\.min\.js$/,            })        ]    }}//index.js中if (process.env.NODE_ENV === 'production') {    module.exports = require('./dist/large-number.min.js');} else {    module.exports = require('./dist/large-number.js');}复制代码

设置⼊⼝⽂件

package.json 的 main 字段为 index.js

然后webpack打包就可以了

SSR

思路:

服务端 使⽤ react-dom/server 的 renderToString ⽅法将 React 组件渲染成字符串,服务端路由返回对应的模板

客户端

打包出针对服务端的组件

复制代码

这里使用注释进行占位

const fs = require('fs');const path = require('path');const express = require('express');const { renderToString } = require('react-dom/server');const SSR = require('../dist/search-server');const template = fs.readFileSync(path.join(__dirname, 'search.html'), 'utf-8');const server = (port) => {    const app = express();    app.use(express.static('dist'));    app.get('/search', (req, res) => {        const html = renderMarkup(renderToString(SSR));        res.status(200).send(html);    });    app.listen(port, () => {        console.log('Server is running on port:' + port);    });};server(process.env.PORT || 3000);const renderMarkup = (str) => {    return template.replace('
', str)}复制代码

如何优化命令⾏的构建⽇志

使⽤ friendly-errors-webpack-plugin

const FriendlyErrorsWebpackPlugin = require('friendly-errors-webpack-plugin');plugins: [    new FriendlyErrorsWebpackPlugin()],stats: 'errors-only'复制代码

 

 

 

构建错误上报

plugins:[    new FriendlyErrorsWebpackPlugin(),    function() {            this.hooks.done.tap('done', (stats) => {                if (stats.compilation.errors && stats.compilation.errors.length && process.argv.indexOf('--watch') == -1)                {                    console.log('错误上报');                    process.exit(1);                }            })        }  ]复制代码

compiler 在每次构建结束后会触发 done 这 个 hook

webpack4中是this.hooks.done.tap webpack3中是this.plugin

Node.js 中的 process.exit 规范

0 表示成功完成,回调函数中,err 为 null

⾮ 0 表示执⾏失败,回调函数中,err 不为 null,err.code 就是传给 exit 的数字

发布到 npm

升级补丁版本号:npm version patch  //一般是修改了bug升级小版本号:npm version minor //一般是发布了feture升级大版本号:npm version major //一般是重大更新复制代码

webpack优化篇

webpack-bundle-analyzer 分析体积

npm install webpack-bundle-analyzer -Dconst { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');plugins:[    new BundleAnalyzerPlugin(),]复制代码

构建完成后会在 8888 端口展示大小

一般推荐使用高版本的node和webpack,因为他们内部自己做了优化

使用 webpack4的原因

V8 带来的优化(for of 替代 forEach、Map 和 Set 替代 Object、includes 替代 indexOf)

默认使用更快的 md4 hash 算法

webpack AST 可以直接从 loader 传递给 AST,减少解析时间

使用字符串方法替代正则表达式

多进程/多实例构建:资源并行解析可选方案

npm install cache-loader thread-loader -D复制代码

使用 thread-loader 解析资源

原理:每次 webpack 解析一个模块,thread-loader 会将它及它的依赖分配给 worker 线程

module: {        rules: [            {                test: /\.js$/,                use: [                     {                         loader: 'thread-loader',                         options: {                             workers: 3                         }                     },                     'cache-loader',                     'babel-loader',                ]        },                ] }复制代码

多进程/多实例:并行压缩

方式一:使用 parallel-uglify-plugin 插件

plugins:[ new ParallelUglifyPlugin({    uglifyJS:{        output:{            beautify:false,            comments:false        },        compress:{            warning:false,            drop_console:true,            collapse_vars:true,            reduce_vars:true        }    } })]复制代码

方式二:uglifyjs-webpack-plugin 开启 parallel 参数

plugins:[ new UglifyJsPlugin({    uglifyOptions:{        warning:false    },    parallel:true })]复制代码

方法三:terser-webpack-plugin 开启 parallel 参数

optimization:{    minimizer:[        new TerserPlugin({            parallel:4        })    ]}复制代码

缩小构建目标

目的:尽可能的少构建模块

比如 babel-loader 不解析 node_modules

rules: [            {                test: /\.js$/,                exclude: 'node_modules',                use: [                     'babel-loader',                ]            }]复制代码

减少文件搜索范围

优化 resolve.extensions 配置

合理使用 alias

resolve: {    alias: {         'components': path.resolve(__dirname, './src/components'),         'util': path.resolve(__dirname, './src/util'),    },    extensions: ['.js']}复制代码

图片压缩

使用:配置 image-webpack-loader

{        test: /\.(png|jpg|gif|jpeg)$/,        use: [            {                loader: 'file-loader',                options: {                    name: '[name]_[hash:8].[ext]'                }            },            {                loader: 'image-webpack-loader',                options: {                  mozjpeg: {                    progressive: true,                    quality: 65                  },                  optipng: {                    enabled: false,                  },                  pngquant: {                    quality: '65-90',                    speed: 4                  },                  gifsicle: {                    interlaced: false,                  },                  webp: {                    quality: 75                  }        }}复制代码

无用的 CSS 如何删除掉?

PurifyCSS: 遍历代码,识别已经用到的 CSS class 这个需要和 mini-css-extract-plugin 配合使用

也就是说先提取为css文件然后再使用PurifyCSS

npm install purgecss-webpack-pluginconst PurgecssPlugin = require('purgecss-webpack-plugin');plugins:[    new MiniCssExtractPlugin({            filename: '[name]_[contenthash:8].css'    }),    new PurgecssPlugin({        paths: glob.sync(`${path.join(__dirname, 'src')}/**/*`,  { nodir: true }),    })]复制代码

构建体积优化

我们可以使用动态 Polyfill -> Polyfill Service

原理:识别 User Agent,下发不同的 Polyfill

如何使用动态 Polyfill service

polyfill.io 官方提供的服务:

体积优化总结

Scope Hoisting

Tree-shaking

公共资源分离

图片压缩

动态 Polyfill

原理篇

loader开发

//raw-loader.jsmodule.exports = function(source) {    const json = JSON.stringify(source)        .replace('foo', '')        .replace(/\u2028/g, '\\u2028')        .replace(/\u2029/g, '\\u2029');    return `export default ${json}`;}复制代码

我们这个loader是把指定文件中的foo字符替换为空的 使用loader-runner测试loader是否正常工作:

npm install loader-runner -D

loader-runner用法具体可以去github查看

const { runLoaders } = require('loader-runner');const fs = require('fs');const path = require('path');runLoaders({    resource: path.join(__dirname, './src/demo.txt'),//资源路径    loaders: [        {            loader: path.join(__dirname, './src/raw-loader.js'),//指定loader路径        }    ],    context: {        minimize:true    },    readResource: fs.readFile.bind(fs)}, (err, result) => {    err ? console.log(err) : console.log(result);});复制代码

loader 的参数获取

npm install loader-utils -D复制代码

通过 loader-utils 的 getOptions 方法获取

const loaderUtils = require("loader-utils");module.exports = function(content) {    const { name } = loaderUtils.getOptions(this);};复制代码

loader 异常处理

loader 内直接通过 throw 抛出,通过 this.callback 传递错误

this.callback(    err: Error | null, content: string | Buffer, sourceMap?: SourceMap, meta?: any);复制代码

loader 的异步处理

通过 this.async 来返回一个异步函数

第一个参数是 Error,第二个参数是处理的结果

module.exports = function(input) {    const callback = this.async();    this.cacheable(false)    // No callback -> return synchronous results    // if (callback) { ... }    callback(null, input + input);};复制代码

this.async()第一个参数也是err对象,第二个参数是数据

在 loader 中使用缓存

webpack 中默认开启 loader 缓存

可以使用 this.cacheable(false) 关掉缓存

缓存条件: loader 的结果在相同的输入下有确定的输出

有依赖的 loader 无法使用缓存

loader 如何进行文件输出?

通过 this.emitFile 进行文件写入

const loaderUtils = require("loader-utils");module.exports = function(content) {    const url = loaderUtils.interpolateName(this, "[hash].[ext]", {    content});    this.emitFile(url, content);    const path = `__webpack_public_path__ + ${JSON.stringify(url)};`;    return `export default ${path}`;};复制代码

interpolateName方法是用于替换占位符,__webpack_public_path__是webpack的全局变量

plugin开发

插件没有像 loader 那样的独立运行环境, 只能在 webpack 里面运行

插件的运行环境搭建

const path = require('path');const MyPlugin = require('./plugins/myPlugin');//插件开发阶段的路径module.exports = {    entry: './src/index.js',    output: {        path: path.join(__dirname, 'dist'),        filename: 'main.js'    },    mode: 'production',    plugins: [        new MyPlugin({            name: 'myname'        })    ]}复制代码

开发最简单的插件

module.exports = class MyPlugin { //MyPlugin是插件名    constructor(options){        this.options = options    }    apply(compiler){ //必须是apply        console.log('我的插件执行了');        console.log('我的插件配置项',this.options)    }}复制代码

插件的错误处理

参数校验阶段可以直接 throw 的方式抛出

throw new Error(“ Error Message”)复制代码

如果已经进入hooks逻辑,那么可以通过 compilation 对象的 warnings 和 errors 接收

compilation.warnings.push("warning");compilation.errors.push("error");复制代码

插件文件写入

文件生成阶段webpack会调用emit这个hooks,所以我们可以监听emit阶段进行操作

文件写入需要使用 [webpack-sources](()

const { RawSource } = require("webpack-sources");module.exports = class DemoPlugin {    constructor(options) {    this.options = options;    }    apply(compiler) {        const { name } = this.options;        compiler.plugin("emit", (compilation, cb) => {            compilation.assets[name] = new RawSource("demo");//demo为文件内容,name为文件的名称            cb();        });    }};复制代码

总结

相信本文应该已经涵盖了webpack最常使用的点以及如何进行优化,感兴趣的小伙伴可以在文章下方进行交流哦

作者:_Dreams
链接:https://juejin.im/post/5dcfbdfa6fb9a01fff5e73c0
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

你可能感兴趣的文章
poj 1976 A Mini Locomotive (dp 二维01背包)
查看>>
大数据入门:Zookeeper结构体系
查看>>
大数据入门:Spark RDD基础概念
查看>>
大数据入门:Scala函数式编程
查看>>
C++报错:C4700:使用了非初始化的局部变量
查看>>
C++类、结构体、函数、变量等命名规则详解
查看>>
C++ goto语句详解
查看>>
【数据结构周周练】008 二叉树的链式创建及测试
查看>>
《软件体系结构》 第九章 软件体系结构评估
查看>>
《软件体系结构》 第十章 软件产品线体系结构
查看>>
《软件过程管理》 第六章 软件过程的项目管理
查看>>
《软件过程管理》 第九章 软件过程的评估和改进
查看>>
《数据库系统概论》 第一章 绪论
查看>>
《数据库系统概论》 第三章 关系数据库标准语言SQL
查看>>
《计算机网络》第五章 运输层 ——TCP和UDP 可靠传输原理 TCP流量控制 拥塞控制 连接管理
查看>>
堆排序完整版,含注释
查看>>
二叉树深度优先遍历和广度优先遍历
查看>>
生产者消费者模型,循环队列实现
查看>>
IA32时钟周期的一些内容
查看>>
获得github工程中的一个文件夹的方法
查看>>