美文网首页
05-01-webpack

05-01-webpack

作者: magic_pill | 来源:发表于2022-05-05 16:22 被阅读0次

    js 痛点

    • 文件依赖度高;
    • 命名冲突/变量污染;
    • 可读性差;

    模块化

    • 把复杂问题分解成相对独立的模块,这样的设计可以降低程序复杂性,提高代码的重用,也有利于团队协作开发与后期的维护和扩展;

    模块化规范

    node:cjs-commonjs
    • 有一个全局对象:module;
    • 导出一:通过 module.exports;
    • 导出二:通过 exports.a = function() {};
    • 引入:require(path),const a = require('./a.js');
    • 优点:
      • 拥有独立作用域;
      • 依赖关系处理简单;
    浏览器:amd,异步模块定义
    • 定义模块:define(id?, depend?, factory)
    • 工厂函数会提供三个参数:
    • require
    • exports
    cmd,类似于 amd
    umd:处理各种模块化之间的差异
    es6:esm-ECMAScript module
    • 使用:<script type='module'></script>

    • 导出一:export default 模块,默认导出;

    • 引入一:import 模块名 from 模块路径,默认导入;

    • 要求必须在服务器环境下,才能运行

    • 导出二:export 模块;,多导出;

    • 引入二:import {a as A} from './a.js';,模块名必须等同于导出模块名,可以通过 as 重命名;

    • esm,IE8、IE9 不支持,这是引入了 webpack,模块打包器。

    webpack

    • webpack:核心代码;
    • webpack-cli:cli 命令行界面;
    正常目录解读
    • src:js 代码,逻辑代码;
    • public:html 存放目录;
    使用
    • webpack 只认识相对路径;
    没有配置时
    • 可以在控制台,执行 node_modules/.bin/webpack ./src/index.js
    自定义配置
    • 创建 webpack.config.js;

    • webpack 会运行到 node 环境下面,所有使用module.exports = {};

    • entry:入口文件;

    • output:输出目录,是一个对象;,里面路径接受绝对路径;

      // 一对一
      const { resolve } = require("path");
      
      module.exports = {
        // 入口文件
        entry: "./src/index.js",
      
        // 输出目录
        output: {
          // 接受绝对路径
          path: resolve(__dirname, "build"),
          filename: "index.js",
        },
      };
      
      // 多对一
      const { resolve } = require("path");
      
      module.exports = {
        // 入口文件
        entry: [
          './src/index.js',
          './src/b.js'
        ],
      
        // 输出目录
        output: {
          // 接受绝对路径
          path: resolve(__dirname, "build"),
          filename: "index.js",
        },
      };
      
      // 多对多
      entry: {
        index: "./src/index.js",
        b: "./src/b.js"
      },
      
      // 输出目录
      output: {
        // 接受绝对路径
        path: resolve(__dirname, "dist"),
        filename: "[name].js",
        clean: true,
      },
      
    • mode:production、development、none

      • 默认 production;

    webpack 会将任意文件数据作为模块进行处理;

    当 webpack 遇到不能解析的模块时,webpack 会找到 module 对象下面的 rules,去匹配对应规则;如果有对应规则匹配时,我们就使用对应的 loader 去解析;

    • module

      module: {
        rules: [
          {
            test: /\.txt$/,
            use: "raw-loader",
          },
          {
            test: /\.(png|gif|jpe?g)$/,
            use: {
              loader: "file-loader",
              options: {
                // 图片打包后文件名
                name: "[name]_[contenthash].[ext]",
                // 打包后存放目录,相对于打包文件夹 dist
                outputPath: './images',  // 使用时,获取的是绝对路径
                // 打包后存放目录,相对于 dist 文件
                publicPath: "./images", // 使用时,引用的是相对路径 ./dist/images/redux_xxx.jpg
      
                // url-loader,多一个 limit,number 类型;
                // 当图片小于此值时,url-loader 不会对文件进行打包,而是转为 base64 格式,引入到 js 文件中
                // limit: 10240,
              },
            },
          },
          {
            test: /\.css$/,
            // loader 执行顺序:从下至上,从右至左
            use: [
              "style-loader",
              {
                loader: "css-loader",
                options: {
                  // 启用 url
                  url: true,
                  // 启用 @import
                  import: true,
                  // 启用 sourcemap
                  sourceMap: false,
                },
              },
            ],
          },
        ],
      }
      
      // 自定义 loader
      module.exports = {
        // ...
        // 配置 loader 解析路径,当自定义 loaders 时,需要使用
        // 如在根目录下创建 loaders 来存放自定义 loader
        resolveLoader: {
          modules: ["node_modules", resolve(__dirname, "./loaders")],
        },
        module: {
          rules: [
            {
              test: /\.doc$/,
              use: "doc-loader",
            },
          ],
        },
      }
      
      const MarkDown = require('markdown-it');
      
      const md = new MarkDown(); 
      
      module.exports = (source) => {
        console.log('source: ', md.render(source));
      
        // return a Buffer or String,返回的结果会直接 eval
        return `export default \`${md.render(source)}\``;
      }
      
      • webpack5 之后,loader 变更
      - asset/resource 发送一个单独的文件并导出 URL。之前通过使用 file-loader 实现。
      - asset/inline 导出一个资源的 data URI。之前通过使用 url-loader 实现。
      - asset/source 导出资源的源代码。之前通过使用 raw-loader 实现。
      
    • plugin:都是以类或者构造函数的形式出现

      • clean-webpack-plugin
        // CleanWebpackPlugin
        // webpack4.0
        const { CleanWebpackPlugin } = require("clean-webpack-plugin");
        // ...
        plugins: [
          new CleanWebpackPlugin(),
        ]
        
        // webpack5.0
        output: {
          // 接受绝对路径
          path: resolve(__dirname, "dist"),
          filename: "main.js",
          clean: true,
        },
        
      • html-webpack-plugin:自动生成一个 html 文件,与打包后的 js 文件相关联
      • mini-css-extract-plugin:
        const MiniCssExtractPlugin = require("mini-css-extract-plugin");
        // ...
        module: {
          rules: [
            {
              test: /\.css$/,
              // loader 执行顺序:从下至上,从右至左
              use: [
                // "style-loader", // 将 css 样式添加到 head 当中的 style 里面;如果样式很多时,导致html文件过大,同时也不能对 css 进行优化,这时可以用 mini-c's's
                MiniCssExtractPlugin.loader,
                "css-loader", // 处理 css 文件
              ],
            },
            {
              test: /\.doc$/,
              use: "doc-loader",
            },
          ],
        },
        
        // ...
        plugins: [
          new MiniCssExtractPlugin({
            filename: '[name].css'
          }),
        ],
        
    • devServer

      devServer: {
        port: 9000,
        open: true, // 自动开启浏览器
        proxy: {
          // 请求地址:服务器地址
          // '/api' : 'http://localhost:3000'
          'api': {
            target: 'http://localhost:3000',
            pathRewrite: {'^/api': ''},
            secure: false,
          }
        },
        hot: true,  // 默认开启
      }
      
    • devtool:开发模式下,方便调试用

      // source-map,要严格遵循下列顺序:[inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map
      devtool: 'eval-cheap-source-map',
      
    webpack-dev-server
    • webpack4.0:在 package.json 中添加执行命令 dev: webpack-dev-server
    • webpack5.0:在 package.json 中添加执行命令 dev: webpack serve
    • 启动开发服务器,打包后生成的文件,会存到内存中,不会写入硬盘,这样可以提高开发效率

    代码分割

    1.最基本的代码分割
      // 多对多打包方式,可能会导致同一模块打包进多个 chunk
      entry: {
        index: {
          import: "./src/index.js",
          dependOn: ["axios", "a"]
        },
        b: {
          import: "./src/b.js",
          dependOn: ["axios", "a"]
        },
        axios: "axios",
        a: "./src/a.js"
      },
      // 当你选择使用代码分割时,webpack可能会受[启发式]的在多个chunk中复制依赖模块。如果发生了这种事情,这时您有可能得到相同模块的多个实例,他们之间的状态将会很难保证同步。当然,您可以通过设置 `optimization.runtimeChunk` 为 `“single”` 进行解决。这并不能阻止Webpack在入口点之间复制模块代码,但它可以防止Webpack在运行时创建同一模块的两个实例。
      optimization: {
        // 配置 runtimeChunk 为 single,webpack 会帮助生成一个 runtime chunk,然后他会把加载的包移到 runtime 中
        runtimeChunk: "single"
      },
    
    2.SplitChunksPlugin
    • CommonsChunkPlugin 曾被用来避免他们之间的重复依赖,但是不可能再做进一步的优化。
    • 从 webpack v4 开始,移除了 CommonsChunkPlugin,取而代之的是 optimization.splitChunks。
    entry: {
      index: "./src/index.js",
      b: "./src/b.js",
    },
    optimization: {
      splitChunks: {
        // async 表示只从异步加载模块中进行拆分
        // initial 表示只从入口模块进行拆分
        chunks: 'all'
      }
    },
    
    • 默认配置
    module.exports = {
      //...
      optimization: {
        splitChunks: {
          chunks: 'async',
          minSize: 20000,
          minRemainingSize: 0,
          minChunks: 1,
          maxAsyncRequests: 30,
          maxInitialRequests: 30,
          enforceSizeThreshold: 50000,
          cacheGroups: {
            defaultVendors: {
              test: /[\\/]node_modules[\\/]/,
              priority: -10,
              reuseExistingChunk: true,
            },
            default: {
              minChunks: 2,
              priority: -20,
              reuseExistingChunk: true,
            },
          },
        },
      },
    };
    
    动态导入
    // 异步加载 按需加载
    // 使用 import() 函数时,会返回一个 promise 对象
    // 内联注释 magic comments,需要按照 json 格式编写
    import( 
      /* webpackChunkName: "my-chunk-name" */
      'axios'
    ).then(res => {
      console.log('axios: ', res);
    })
    

    预加载 / 预获取

    • 其实是针对于动态导入资源加载优化
    • preload:预加载,当我们配置时,被配置的模块会在父 chunk 加载时并行加载该模块;
    • prefetch:预获取,当我们配置时,被配置的模块会在浏览器闲置时,也就是未来某个时刻下载模块
    import( 
      /* webpackChunkName: "my-chunk-name" */
      /* webpackPrefetch: true */
      /* webpackPreload: true */
      'axios'
    ).then(res => {
      console.log('axios: ', res);
    })
    

    外部扩展

    • 防止将某些 import 的包(package)打包到 bundle 中,而是在运行时(runtime)再去从外部获取这些扩展依赖(external dependencies)。
    • key:是我们需要进行外部扩展的包名,后续打包时不会将该包打包;
    • value:是该库声明的全局变量;
    module.exports = {
      //...
      externals: {
        jquery: 'jQuery',
      },
    };
    

    tree shaking

    • 将上下文中的dead-code移除,就像摇树上的枯叶使其掉落一样;
    optimization: {
      usedExports: true,
    },  
    mode: 'production',
    

    webpack.config.js

    const { resolve } = require("path");
    const { CleanWebpackPlugin } = require("clean-webpack-plugin");
    const HtmlWebpackPlugin = require("html-webpack-plugin");
    const MiniCssExtractPlugin = require("mini-css-extract-plugin");
    
    module.exports = {
      // 最基本的代码分割
      // 多对多打包方式,可能会导致同一模块打包进多个 chunk
      entry: {
        index: "./src/index.js",
        b: "./src/b.js",
      },
      // 当你选择使用代码分割时,webpack可能会受[启发式]的在多个chunk中复制依赖模块。如果发生了这种事情,这时您有可能得到相同模块的多个实例,他们之间的状态将会很难保证同步。当然,您可以通过设置 `optimization.runtimeChunk` 为 `“single”` 进行解决。这并不能阻止Webpack在入口点之间复制模块代码,但它可以防止Webpack在运行时创建同一模块的两个实例。
      // optimization: {
      //   // runtimeChunk: "single"
      //   splitChunks: {
      //     // async 表示只从异步加载模块中进行拆分
      //     // initial 表示只从入口模块进行拆分
      //     chunks: 'all'
      //   }
      // },
    
      // 输出目录
      output: {
        // 接受绝对路径
        path: resolve(__dirname, "dist"),
        filename: "[name].js",
        clean: true,
      },
    
      mode: "development",
    
      // 配置 loader 解析路径,当自定义 loaders 时,需要使用
      // 在根目录下创建 loaders 来存放自定义 loader
      resolveLoader: {
        modules: ["node_modules", resolve(__dirname, "./loaders")],
      },
    
      // loader 使用
      module: {
        rules: [
          {
            test: /\.txt$/,
            use: "raw-loader",
          },
          {
            test: /\.(png|gif|jpe?g)$/,
            use: {
              loader: "file-loader",
              options: {
                // 图片打包后文件名
                name: "[name]_[contenthash].[ext]",
                // 打包后存放目录,相对于打包文件夹 dist
                outputPath: "./images", // 使用时,获取的是绝对路径
                // 打包后引用目录
                publicPath: "./images", // 使用时,引用的是相对路径 ./images/redux_xxx.jpg
    
                // url-loader,多一个 limit,number 类型;
                // 当图片小于此值时,url-loader 不会对文件进行打包,而是转为 base64 格式,引入到 js 文件中
                // limit: 10240,
              },
            },
          },
          {
            test: /\.css$/,
            // loader 执行顺序:从下至上,从右至左
            use: [
              // "style-loader", // 将 css 样式添加到 head 当中的 style 里面;如果样式很多时,导致html文件过大,同时也不能对 css 进行优化,这时可以用 mini-c's's
              MiniCssExtractPlugin.loader,
              "css-loader", // 处理 css 文件
            ],
          },
          {
            test: /\.doc$/,
            use: "doc-loader",
          },
        ],
      },
    
      // 注册插件的位置,plugins 的使用
      // webpack 会自动调用对应插件的 apply 方法,把插件注册到 webpack 对应的生命周期当中
      // 插件 是 webpack 的 支柱 功能。Webpack 自身也是构建于你在 webpack 配置中用到的 相同的插件系统 之上
      // 插件目的在于解决 loader 无法实现的其他事。Webpack 提供很多开箱即用的 插件。
      plugins: [
        // new CleanWebpackPlugin(),
        new HtmlWebpackPlugin({
          filename: "app.html", // 会自动创建一个纯净的 app.html 文件
          template: "./public/index.html", // 会把已有 html 文件中的信息带上
          title: "app", // <title><%=htmlWebpackPlugin.options.title%></title>
        }),
        new MiniCssExtractPlugin({
          filename: '[name].css'
        }),
      ],
    
      devServer: {
        port: 9000,
        open: true, // 自动开启浏览器
        proxy: {
          // 请求地址:服务器地址
          // '/api' : 'http://localhost:3000'
          'api': {
            target: 'http://localhost:3000',
            pathRewrite: {'^/api': ''},
            secure: false,
          }
        },
        hot: true,  // 默认开启
      },
    
      // source-map,要严格遵循下列顺序:[inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map
      devtool: 'eval-cheap-source-map',
    };
    

    相关文章

      网友评论

          本文标题:05-01-webpack

          本文链接:https://www.haomeiwen.com/subject/mwmmlrtx.html