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两个参数

  1. mode: 'development',//none production development
  2. //指定项目打包的入口
  3. entry: './src/index.js',
  4. output: {
  5. //指定输出的目录,默认是dist目录,目录的配置必须是一个绝对路径而非相对路径
  6. path: path.resolve(__dirname, 'dist'),
  7. //指定的是文件名,默认是main.js
  8. filename: 'main.js'
  9. },
  10. 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作为其结果:

  1. console.log('process.env.NODE_ENV', process.env.NODE_ENV)
  2. module.exports = (env) => {
  3. return {
  4. mode: process.env.NODE_ENV === 'production' ? 'production' : 'development',//none production development
  5. //指定项目打包的入口
  6. entry: './src/index.js',
  7. output: {
  8. //指定输出的目录,默认是dist目录,目录的配置必须是一个绝对路径而非相对路径
  9. path: path.resolve(__dirname, 'dist'),
  10. //指定的是文件名,默认是main.js
  11. filename: 'main.js'
  12. },
  13. devtool: 'source-map',
  14. module: {
  15. rules: [
  16. {
  17. test: /\.css$/,
  18. use: ['style-loader', 'css-loader']
  19. }
  20. ]
  21. },
  22. plugins: [
  23. new HtmlWebpackPlugin({
  24. template: './src/index.html'
  25. }),
  26. new webpack.DefinePlugin({
  27. 'process.env.NODE_ENV': process.env.NODE_ENV
  28. })
  29. ]
  30. }
  31. }

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

开发环境配置

  1. devServer: {
  2. port: 8080,
  3. open: true
  4. }

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

  1. "scripts": {
  2. "build": "webpack",
  3. "dev": "webpack serve"
  4. },

样式相关配置

less 用于把less编译成CSS
less-loader
node-sass 用于把sass编译成CSS
sass-loader

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

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

  1. {
  2. loader: 'css-loader',
  3. options: {
  4. url: true,//启用/禁用url解析 url(./kf.jpg);
  5. import: true, //是否允许或者说禁 @import语法处理 @import "base.css";
  6. modules: false,// 是否允许 CSS 模块化
  7. sourceMap: false,//是否生成sourcemap
  8. importLoaders: 0, //允许或者说启动几个数量的loaders应用在import 的文件
  9. esModule: false //默认情况下,css-loader生成使用ES Module的模块对象,如果你赋false的话,不包装成ESMODULES
  10. }
  11. },
  • 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

  1. let postcssPresetEnv = require('postcss-preset-env');
  2. module.exports = {
  3. plugins: [
  4. postcssPresetEnv({
  5. browsers: 'last 5 version'
  6. })
  7. ]
  8. }

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

  1. {
  2. test: /\.css$/,
  3. use: [
  4. 'style-loader',
  5. {
  6. loader: 'css-loader',
  7. options: {
  8. url: true,//启用/禁用url解析 url(./kf.jpg);
  9. import: true, //是否允许或者说禁 @import语法处理 @import "base.css";
  10. modules: false,// 是否允许 CSS 模块化
  11. sourceMap: false,//是否生成sourcemap
  12. importLoaders: 0, //允许或者说启动几个数量的loaders应用在import 的文件
  13. esModule: false //默认情况下,css-loader生成使用ES Module的模块对象,如果你亩false的话,不包装成ESMODULES
  14. }
  15. },
  16. 'postcss-loader',
  17. path.resolve(__dirname, 'loaders', 'loader1.js'),
  18. path.resolve(__dirname, 'loaders', 'loader2.js'),
  19. path.resolve(__dirname, 'loaders', 'loader3.js')
  20. ]
  21. },
  22. {
  23. test: /\.less$/,
  24. use: [
  25. 'style-loader',
  26. {
  27. loader: 'css-loader',
  28. options: {
  29. importLoaders: 1
  30. }
  31. },
  32. 'postcss-loader',
  33. 'less-loader'
  34. ]
  35. }, {
  36. test: /\.scss$/,
  37. use: ['style-loader', 'css-loader', 'postcss-loader', 'sass-loader']
  38. },

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

  1. "browserslist": {
  2. "development": [
  3. "last 1 chrome version",
  4. "last 1 firefox version",
  5. "last 1 safari version"
  6. ],
  7. "production": [
  8. ">0.2%"
  9. ]
  10. },

webpack中图片的处理

  1. // webpack4 关于图片需要 使用file-loader url-loader
  2. // webpack5 不再需要,而改用asset/resourceasset/inline,分别替代file-loader url-loader
  3. {
  4. test: /\.(jpg|png|bmp|gif)/,
  5. type: 'asset/resource',
  6. generator: {
  7. filename: '[hash][ext]'
  8. }
  9. }

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,就可以通过这个参数来控制,但实际使用场合用的较少

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

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

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

  1. new webpack.ProvidePlugin({
  2. isarray: 'isarray'
  3. })

相当于自动添加了import isarray from ‘isarray’
但需要注意,ProvidePlugin里面自动import的东西并不是全局变量,所以在非模块环境下是不可以拿到isarray的

通过expose-loader添加全局变量

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

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

externals

  1. externals: {
  2. lodash: '_'
  3. },

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

  1. (function () {
  2. var modules = ({
  3. "lodash":
  4. (function (module) {
  5. module.exports = window._;
  6. })
  7. });
  8. var cache = {};
  9. function require(moduleId) {
  10. var cachedModule = cache[moduleId];
  11. if (cachedModule !== undefined) {
  12. return cachedModule.exports;
  13. }
  14. var module = cache[moduleId] = {
  15. exports: {}
  16. };
  17. modules[moduleId](module, module.exports, require);
  18. return module.exports;
  19. }
  20. var exports = {};
  21. !function () {
  22. let lodash = require("lodash");
  23. console.log(lodash);
  24. }();
  25. })()

开发服务器

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

开发服务器原理简要介绍

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