基础-Webpack文档学习总结

43 阅读5分钟

每次看一些webpack相关的学习视频总是看了之后记不住,也不知道怎么应用,看了眼官方文档感觉比视频讲的好很多,算是一个记录学习的文档吧

基础知识

Webpack定义

是一个静态的模块化打包工具,内部会从一个/多个入口点构建一个依赖图,然后将项目中所需要的每一个模块组合为一个或多个bundles

什么是静态资源?就是在开发阶段可以直接被webpack引用的资源

入口

module.exports = {
    entry: "xxxxx"
}

入口可以配置多个

module.exports = {
  entry: {
    app: './src/app.js',
    adminApp: './src/adminApp.js',
  },
};

此外还有描述入口的对象,有以下的这几种属性

  • dependOn: 当前入口所依赖的入口,必须在该入口被加载前被加载,不能是循环引用的
  • filename: 指定要输出的文件名称
  • import: 启动的时候需要加载的模块
  • runtime: 运行时chunk的名字,不能指向已经存在的入口名称,否则会抛出错误
module.exports = {
  entry: {
    a2: 'dependingfile.js',
    b2: {
      dependOn: 'x2',
      import: './src/app.js',
    },
  },
};

通常多入口会分离app和vendor(第三方库)入口

出口

注意下,出口必须是绝对路径

const path = require("path");
module.exports = {
    entry: "xxxxx",
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'xxx'
    }
}

如果出口要有多个,应该使用占位符来确保每个文件有唯一的名称

loader

为啥要用loader呢? 因为webpack自己只支持处理js和json格式的文件,也就是说他连css格式的都处理不了,那就得引入东西去进行转化呗,这个东西就是loader

作用:用于转换某些类型的模块

loader执行顺序:从右到左/从下到上

const path = require("path");
module.exports = {
    entry: "xxxxx",
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'xxx'
    },
    module: {
        rules: [{test: /\.jsx?$/i, use: 'raw-loader'}]
    }
}

注意 用正则表达式的时候不要加引号

举例:配置sass-loader

module.exports = {
  module: {
    rules: [
      {
        test: /.sass$/,
        use: [
          { loader: 'style-loader' },
          {
            loader: 'css-loader',
            options: {
              modules: true,
            },
          },
          { loader: 'sass-loader' },
        ],
      },
    ],
  },
};

plugin

可以用于执行范围更广的任务,如:打包优化,资源管理,注入环境变量,相当于是插件,可以执行很多loader无法实现的事情

在进行plugin的时候,向plugins属性传入一个new实例

模块热替换

HMR,这个功能会在应用程序运行过程中,替换、添加/删除模块,而无需重新加载整个页面

常见的loader

  • 加载css文件
module.exports = {
    module: {
        rules: [
        {test: /\.css/, use: ['style-loader', 'css-loader']}
        ]
    }
}
  • 加载image文件和字体
module.exports = {
    module: {
        rules: [
        {test: /\.(png|svg|jpg|jpeg|gif)/i, type: "asset/resource"},
        {test: /\.(woff|ttf|otf)/i, type: "asset/resource"}
        ]
    }
}
  • 加载某些数据,比如csv、tsv、xml
module.exports = {
    module: {
        rules: [
        {test: /\.(csv|tsv)/i, use:['csv-loader']},
        ]
    }
}
module.exports = {
    module: {
        rules: [
        {test: /\.(xml)/i, use:['xml-loader']},
        ]
    }
}

sourcemap

为什么需要sourcemap?当你写的代码过多,被分离成不同文件的时候,如果代码出错了,在浏览器打包后,只知道是文件出错了,但是不知道出错的地方在哪里,哪个文件出错了

因此产生了sourcemap sourcemap配置的时候是这样的

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

 module.exports = {
   mode: 'development',
   entry: {
     index: './src/index.js',
     print: './src/print.js',
   },
+  devtool: 'inline-source-map',
   plugins: [
     new HtmlWebpackPlugin({
       title: 'Development',
     }),
   ],
   output: {
     filename: '[name].bundle.js',
     path: path.resolve(__dirname, 'dist'),
     clean: true,
   },
 };

watch mode

watch的话可以在一个文件被更新的时候,代码被重新编译,不必再去重写手动运行创建 在package.json里的scripts这样

 {
   "name": "webpack-demo",
   "version": "1.0.0",
   "description": "",
   "private": true,
   "scripts": {
     "test": "echo "Error: no test specified" && exit 1",
+    "watch": "webpack --watch",
     "build": "webpack"
   },
   "keywords": [],
   "author": "",
   "license": "ISC",
   "devDependencies": {
     "html-webpack-plugin": "^4.5.0",
     "webpack": "^5.4.0",
     "webpack-cli": "^4.2.0"
   },
   "dependencies": {
     "lodash": "^4.17.20"
   }
 }

这样还有个缺点,为了查看修改后的实际效果,需要刷新下浏览器,如果能有类似于liveserver的效果就好了,而很幸运,webpack也有这个功能,他叫devServer

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

 module.exports = {
   mode: 'development',
   entry: {
     index: './src/index.js',
     print: './src/print.js',
   },
   devtool: 'inline-source-map',
+  devServer: {
+    static: './dist',
+  },
   plugins: [
     new HtmlWebpackPlugin({
       title: 'Development',
     }),
   ],
   output: {
     filename: '[name].bundle.js',
     path: path.resolve(__dirname, 'dist'),
     clean: true,
   },
+  optimization: {
+    runtimeChunk: 'single',
+  },
 };

代码分离

什么叫代码分离,就是把代码分离到不同的bundle中,然后可以按需加载/并行加载这些文件,代码分离可以获取更小的bundle,并且控制资源加载的优先级,这样的话就可以大大提高首页渲染的速度了

SplitChunksPlugin

可以消除重复的模块

  const path = require('path');

  module.exports = {
    mode: 'development',
    entry: {
      index: './src/index.js',
      another: './src/another-module.js',
    },
    output: {
      filename: '[name].bundle.js',
      path: path.resolve(__dirname, 'dist'),
    },
+   optimization: {
+     splitChunks: {
+       chunks: 'all',
+     },
+   },
  };

预获取 预加载

prefetch/preload
prefetch: 将来某些导航下可能需要的资源 preload:当前导航下可能需要的资源

  • preload在父chunk加载时,以并行的方式开始加载,prefetch会在父加载结束后开始加载;
  • prefetch在浏览器闲置的时候下载,preload有中等优先级,并立即下载
  • prefetch用于未来的某个时刻,preload会在父chunk中立即请求,用于当下时刻

写webpack魔法注释即可

import(/* webpackPrefetch: true */ './path/to/LoginModal.js');

TreeShaking

只用于ES Modules

怎么标除文件没有副作用

在package.json的sideEffects属性可以标记,这样的话可以安全的删除没有用到的export
用于移除JS上下文的未引用代码,webpack会自动识别出没有引用的代码,然后怎么去删除?

image.png

  • 依赖收集:webpack在打包时,会将每个模块中的导出语句格式化后保存在一个数组中,并最终存储在ModuleGraph,它记录了模块间的依赖关系

  • 标记:遍历所有模块,如果用到了收集来的导出的变量,将会打上已使用的标记

  • 删除:通过默认的压缩工具 tasertaser-webpack-plugin,对于没有打标记的导出,就会被删除掉

module.exports = {
  mode: 'development',
  devtool: 'source-map',
  optimization: {
    minimize: true, // 增加了这一行
    usedExports: true 
  }
}