webpack基础

251 阅读6分钟

loader处理顺序

css文件经过loader的处理流程:

['style-loader', 'css-loader'],从右向左执行的

  • 1.先读出源文件 index.css

  • 2.把文件内容传递给css-loader,css-loader可以处理css中的@import和url语法,处理完之后会把内容传递给style-loader

  • 3.style-loader的作用是把CSS转换成style标签插入页面中

基础配置

以较为友好的方式显示打包后的文件,设置mode、devtool两个参数

    mode: 'development',//none production development
    //指定项目打包的入口
    entry: './src/index.js',
    output: {
        //指定输出的目录,默认是dist目录,目录的配置必须是一个绝对路径而非相对路径
        path: path.resolve(__dirname, 'dist'),
        //指定的是文件名,默认是main.js
        filename: 'main.js'
    },
    devtool: false,

output的path必须是一个绝对路径

模式(mode)

  • 日常的前端开发工作中,一般都会有两套构建环境
  • 一套开发时使用,构建结果用于本地开发调试,不进行代码压缩,打印 debug 信息,包含 sourcemap 文件
  • 一套构建后的结果是直接应用于线上的,即代码都是压缩后,运行时不打印 debug 信息,静态文件不包括 sourcemap
  • webpack 4.x 版本引入了 mode 的概念
  • 当你指定使用 production mode 时,默认会启用各种性能优化的功能,包括构建结果优化以及 webpack 运行性能优化
  • 而如果是 development mode 的话,则会开启 debug 工具,运行时打印详细的错误信息,以及更加快速的增量编译构建
选项描述
development会将 process.env.NODE_ENV 的值设为 development。启用 NamedChunksPlugin 和 NamedModulesPlugin
production会将 process.env.NODE_ENV 的值设为 production。启用 FlagDependencyUsagePlugin, FlagIncludedChunksPlugin, ModuleConcatenationPlugin, NoEmitOnErrorsPlugin, OccurrenceOrderPlugin, SideEffectsFlagPlugin 和 UglifyJsPlugin

\

webpack指令在执行时可以添加若干参数:

--mode用来设置模块内的process.env.NODE_ENV

--env用来设置webpack配置文件的函数参数

\

package.json文件里,在很多的script命令中,会添加cross-env,它的作用是用来设置node环境的process.env.NODE_ENV,且可以自动识别当前环境(mac linux windows)

设置环境变量的方式在windows和mac里不一样:

windows set Key=Value:set NODE_ENV=development webpack

Mac Key=Value:NODE_ENV=development webpack

\

需要注意node环境中的process.env.NODE_ENV和我们平时写的js(在浏览器环境中运行的代码)里面的process.env.NODE_ENV完全不同,后者相当于是浏览器里面定义的一个字符串token,webpack等打包工具会识别出这个token并做替换

这个工作,通常是由DefinePlugin插件来做

如果命令行中增加了mode参数,webpack.config.js也添加了mode参数,其优先级为:

默认优先级(production) < 配置文件webpack.config.js里的mode < package.json中的--mode的配置

webpack的config在导出时,除了可以以一个json的形式导出之外,还可以导出一个function,返回config作为其结果:

console.log('process.env.NODE_ENV', process.env.NODE_ENV)
module.exports = (env) => {
    return {
        mode: process.env.NODE_ENV === 'production' ? 'production' : 'development',//none production development
        //指定项目打包的入口
        entry: './src/index.js',
        output: {
            //指定输出的目录,默认是dist目录,目录的配置必须是一个绝对路径而非相对路径
            path: path.resolve(__dirname, 'dist'),
            //指定的是文件名,默认是main.js
            filename: 'main.js'
        },
        devtool: 'source-map',
        module: {
            rules: [
                {
                    test: /.css$/,
                    use: ['style-loader', 'css-loader']
                }
            ]
        },
        plugins: [
            new HtmlWebpackPlugin({
                template: './src/index.html'
            }),
            new webpack.DefinePlugin({
                'process.env.NODE_ENV': process.env.NODE_ENV
            })
        ]
    }
}

上述代码中的process.env.NODE_ENV就是package.json中script里通过cross-env定义的环境变量,除此之外,plugins里面的DefinePlugin也可以使用process来获取其值,因此定义执行模式通常通过cross-env这种方式,而不是写死一个mode参数

开发环境配置

devServer: {
	port: 8080,
  open: true
}

再在package.json中添加一个脚本dev:

  "scripts": {
    "build": "webpack",
    "dev": "webpack serve"
  },

样式相关配置

less 用于把less编译成CSS

less-loader

node-sass 用于把sass编译成CSS

sass-loader

由于sass需要通过ruby来装,因此出错概率较高,目前会采用dart-sass取代node-sass

css-loader参数:

如果要给css-loader传参,需要在loaders中写成对象格式

{
  loader: 'css-loader',
  options: {
    url: true,//启用/禁用url解析 url(./kf.jpg);
    import: true, //是否允许或者说禁 用@import语法处理 @import "base.css";
    modules: false,// 是否允许 CSS 模块化
    sourceMap: false,//是否生成sourcemap
    importLoaders: 0, //允许或者说启动几个数量的loaders应用在import 的文件
    esModule: false //默认情况下,css-loader生成使用ES Module的模块对象,如果你赋false的话,不包装成ESMODULES
  }
},
  • url: Enable/disable url() resolving.
  • import: Allows to enables/disables @import at-rules handling
  • modules: Allows to enable/disable CSS Modules or ICSS and setup configuration
  • sourceMap: By default generation of source maps depends on the devtool option
  • importLoaders: Allows to enables/disables or setups number of loaders applied before CSS loader for @import at-rules
  • esModule: By default, css-loader generates JS modules that use the ES modules syntax. There are some cases in which using ES modules is beneficial

node_modules下文件的引入

//为了引入node_modules下面的资源文件,比如说bootstrap,可以添加 ~前缀

background-image: url(~image/kf.jpg);

给css类增加浏览器前缀

首先要安装postcss-preset-env这个包,并添加postcss.config.js

let postcssPresetEnv = require('postcss-preset-env');
module.exports = {
    plugins: [
        postcssPresetEnv({
            browsers: 'last 5 version'
        })
    ]
}

然后在webpack.config.js的module.rules里面添加postcss相关的配置

            {
                test: /.css$/,
                use: [
                    'style-loader',
                    {
                        loader: 'css-loader',
                        options: {
                            url: true,//启用/禁用url解析 url(./kf.jpg);
                            import: true, //是否允许或者说禁 用@import语法处理 @import "base.css";
                            modules: false,// 是否允许 CSS 模块化
                            sourceMap: false,//是否生成sourcemap
                            importLoaders: 0, //允许或者说启动几个数量的loaders应用在import 的文件
                            esModule: false //默认情况下,css-loader生成使用ES Module的模块对象,如果你亩false的话,不包装成ESMODULES
                        }
                    },
                    'postcss-loader',
                    path.resolve(__dirname, 'loaders', 'loader1.js'),
                    path.resolve(__dirname, 'loaders', 'loader2.js'),
                    path.resolve(__dirname, 'loaders', 'loader3.js')
                ]
            },
            {
                test: /.less$/,
                use: [
                    'style-loader',
                    {
                        loader: 'css-loader',
                        options: {
                            importLoaders: 1
                        }
                    },
                    'postcss-loader',
                    'less-loader'
                ]
            }, {
                test: /.scss$/,
                use: ['style-loader', 'css-loader', 'postcss-loader', 'sass-loader']
            },

还要在package.json中配置开发、生产环境中分别需要兼容的浏览器版本:

  "browserslist": {
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ],
    "production": [
      ">0.2%"
    ]
  },

webpack中图片的处理

// webpack4 关于图片需要 使用file-loader url-loader 
// webpack5 不再需要,而改用asset/resource和asset/inline,分别替代file-loader url-loader
{
  test: /.(jpg|png|bmp|gif)/,
  type: 'asset/resource',
  generator: {
    filename: '[hash][ext]'
  }
}

file-loader=>asset/resource 把图片拷贝到输出目录里去,返回一个输出后的路径,包括文件

url-loader=>asset/inline 不拷贝文件,直接把源文件变成base64字符串内嵌到输出结果

css-loader的参数:importLoaders

允许或者说启动几个数量的loaders应用在import 的文件

例如下面的代码,importLoaders设置为了2,那么会从设置importLoaders的这一项(即css-loader这个对象)开始,往下找两个loader(此处就是postcss-loader和loader1),来对文件中从外部import进来的css进行解析(注意只有import的文件会受此影响,直接被引入页面的css不受该参数影响)

这个功能的出现是为了import引入的文件有可能会引各种类型的文件,例如css文件中引了less或scss,就可以通过这个参数来控制,但实际使用场合用的较少

{
  test: /.css$/,
  use: [
    'style-loader',
    {
      loader: 'css-loader',
      options: {
        url: true,//启用/禁用url解析 url(./kf.jpg);
        import: true, //是否允许或者说禁 用@import语法处理 @import "base.css";
        modules: false,// 是否允许 CSS 模块化
        sourceMap: false,//是否生成sourcemap
        importLoaders: 2, //允许或者说启动几个数量的loaders应用在import 的文件
        esModule: false //默认情况下,css-loader生成使用ES Module的模块对象,如果你亩false的话,不包装成ESMODULES
      }
    },
    'postcss-loader',
    path.resolve(__dirname, 'loaders', 'loader1.js'),
    path.resolve(__dirname, 'loaders', 'loader2.js'),
    path.resolve(__dirname, 'loaders', 'loader3.js')
  ]
},

vscode插件:rm-js-comment,删除代码中所有注释

对于比较常见的组件,很多页面都需要import,为了避免这些频繁的import,可以通过ProvidePlugin进行简化:

new webpack.ProvidePlugin({
  isarray: 'isarray'
})

相当于自动添加了import isarray from 'isarray'

但需要注意,ProvidePlugin里面自动import的东西并不是全局变量,所以在非模块环境下是不可以拿到isarray的

通过expose-loader添加全局变量

如果想要在window上挂载全局变量,需要用expose-loader

{
  test: /isarray/,
  use: [
    {
      loader: 'expose-loader',
      options: {
        exposes: {
          globalName: 'isarray',
          // 如果window上已经有了isarray这个变量,则直接覆盖
          override: true
        }
      }
    }
  ]
},
// require('isarray')引用一次以后就把isarray变量挂到了全局对象window上
let isarray = require('isarray');
let title = require('./title');
// title文件里可以直接用window.isarray

externals

    externals: {
        lodash: '_'
    },

lodash将不会参与打包,而且我们需要手动在HTML中添加lodash的引入(可以以cdn方式引入),但在项目中依然可以通过import或require引用,原理是webpack会生成一个模块,这个模块会在window上添加变量_,这个_变量将指向lodash:

(function () {
  var modules = ({
    "lodash":
      (function (module) {
        module.exports = window._;
      })
  });
  var cache = {};
  function require(moduleId) {
    var cachedModule = cache[moduleId];
    if (cachedModule !== undefined) {
      return cachedModule.exports;
    }
    var module = cache[moduleId] = {
      exports: {}
    };
    modules[moduleId](module, module.exports, require);
    return module.exports;
  }
  var exports = {};
  !function () {
    let lodash = require("lodash");
    console.log(lodash);
  }();
})()

开发服务器

    devServer: {
        port: 8080,//配置开发预览服务器的端口号8080
        open: true,//打包后会自动打开浏览器
        /* proxy: {
            //把访问路径是以/api开头的请求都转发到http://localhost:3000
            '/api': {
                target: 'http://localhost:3000',//重定向的域名
                pathRewrite: { //重写的路径
                    "^/api": ""
                }
            }
        } */
        //在 webpack-dev-server静态资源中间件处理之前,可以用于拦截部分请求返回特定内容,以实现简单的mock
      	// webpack5中使用
        onBeforeSetupMiddleware({ app }) {
            app.get('/api/users', (req, res) => {
                res.json([{ id: 1, name: "张三" }, { id: 2, name: "李四" }]);
            });
        }
    },

开发服务器原理简要介绍

let express = require('express');
//得到app应用对象 
const app = express();
app.get('/api/users', (req, res) => {
    res.json([{ id: 1, name: "张三" }, { id: 2, name: "李四" }]);
});
const webpack = require('webpack');
const webpackDevMiddleware = require('webpack-dev-middleware');
const config = require('./webpack.config');
//compiler就是编译大管家
const compiler = webpack(config);
//webpackDevMiddleware会返回一个中间件
//中间件负责根据配置文件打包当前的项目并且返回打包后的结果
//中间件有两件工作
//1.负责打包
//2.返回打包后的静态文件 index.html main.js
app.use(webpackDevMiddleware(compiler));
app.listen(3000);

\