玖叶教程网

前端编程开发入门

webpack原理分析(webpack的理解)

webpack的作用

  • 模块打包,可以将不同模块的文件打包整合在一起,并且保证它们之间的引用正确,执行有序。利用打包我们就可以在开发的时候根据我们自己的业务自由划分文件模块,保证项目结构的清晰和可读性。
  • 编译兼容,通过webpack的Loader机制,不仅仅可以帮助我们对代码做polyfill,还可以编译转换诸如.less, .vue, .jsx这类在浏览器无法识别的格式文件,让我们在开发的时候可以使用新特性和新语法做开发,提高开发效率。
  • 能力扩展,通过webpack的Plugin机制,我们在实现模块化打包和编译兼容的基础上,可以进一步实现诸如按需加载,代码压缩等一系列功能,帮助我们进一步提高自动化程度,工程效率以及打包输出的质量。

模块打包运行原理

  • 读取webpack的配置参数 webpack.config.js
  • 启动webpack,创建Compiler对象并开始解析项目
  • 从入口文件(entry)开始解析,并且找到其导入的依赖模块,递归遍历分析,形成依赖关系树
  • 对不同文件类型的依赖模块文件使用对应的Loader进行编译,最终转为Javascript文件
  • 整个过程中webpack会通过发布订阅模式,向外抛出hooks,而webpack的插件即可通过监听这些关键的事件节点,执行插件任务进而达到干预输出结果的目的

在webpack源码中主要依赖于compiler和compilation两个核心对象实现。

compiler对象是一个全局单例,他负责把控整个webpack打包的构建流程,每次都会重新生成一个新的compilation对象,负责此次更新的构建过程

compilation对象是每一次构建的上下文对象,它包含了当次构建所需要的所有信息,每次热更新和重新构建

每个模块文件在通过Loader解析完成之后,会通过acorn库生成模块代码的AST语法树,通过语法树就可以分析这个模块是否还有依赖的模块,进而继续循环执行下一个模块的编译解析。

sourceMap

sourceMap是一项将编译、打包、压缩后的代码映射回源代码的技术,由于打包压缩后的代码并没有阅读性可言,一旦在开发中报错或者遇到问题,直接在混淆代码中debug问题会带来非常糟糕的体验,sourceMap可以帮助我们快速定位到源代码的位置,提高我们的开发效率。sourceMap其实并不是Webpack特有的功能,而是Webpack支持sourceMap

Loader

Webpack最后打包出来的成果是一份Javascript代码,实际上在Webpack内部默认也只能够处理JS模块代码,在打包过程中,会默认把所有遇到的文件都当作 JavaScript代码进行解析,因此当项目存在非JS类型文件时,我们需要先对其进行必要的转换,才能继续执行打包任务,这也是Loader机制。

Plugin

Loader负责文件转换,那么Plugin负责功能扩展。Loader和Plugin作为Webpack的两个重要组成部分,承担着不同的职责。

webpack基于发布订阅模式,在运行的生命周期中会广播出许多事件,插件通过监听这些事件,就可以在特定的阶段执行自己的任务,其中compiler暴露了和 Webpack整个生命周期相关的钩子(compiler-hooks),而compilation则暴露了与模块和依赖有关的粒度更小的事件钩子(Compilation Hooks)。

Webpack的事件机制基于Tapable事件流方案

Plugin的开发和开发Loader一样,需要遵循一些开发上的规范和原则:

  • 插件必须是一个函数或者是一个包含 apply 方法的对象,这样才能访问compiler实例
  • 传给每个插件的 compiler 和 compilation 对象都是同一个引用,若在一个插件中修改了它们身上的属性,会影响后面的插件

webpack配置文件

// 自动清空dist目录
const { CleanWebpackPlugin } = require("clean-webpack-plugin")

// js 或者 css 文件可以自动引入到 Html 中
const HtmlWebpackPlugin = require("html-webpack-plugin")
// CSS 文件的形式引入到页面上
const MiniCssExtractPlugin = require("mini-css-extract-plugin")
// require("webpack-dev-server")


const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin

const TerserPlugin = require('terser-webpack-plugin');

const path = require("path")



console.log("process.env.NODE_ENV=", process.env.NODE_ENV) //打印环境变量

// 路径处理方法
function resolve(dir) {
  return path.join(__dirname, dir);
}

const config = {
  // mode:"development", //模式
  entry: "./src/index.js", //打包入口地址
  output: {
    filename: "bundle.js", //输出文件名
    path: path.join(__dirname, 'dist') //输出文件目录
  },
  // 从输出的 bundle 中排除依赖
  // externals: {
  //     jquery: 'jQuery',
  //   },

  // 解析自定义 loader
  // resolveLoader: {
  //     modules: ['node_modules',resolve('loader')]
  //   },
  // 压缩 JS
  optimization: {
    minimize: true, // 开启最小化
    minimizer: [
      // ...
      new TerserPlugin({})
    ]
  },
  resolve: {
    // 配置别名
    alias: {

      '@': path.join(__dirname, 'src')
    },
    // extensions:['',""]
    //  webpack 优先 src 目录下查找需要解析的文件
    modules: [resolve('src'), 'node_modules']
  },
  module: {
    // 不需要解析依赖的第三方大型类库
    // noParse:"",
    rules: [ //转换规则
      {
        test: /\.(s[ac]|c)ss$/i, //匹配所有的css/sass/scss文件
        // test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader,
          // "style-loader",
          "css-loader",
          // "postcss-loader",
          "sass-loader"
        ] //use:对应loader的名称
      },
      {
        test: /\.(jpe?g|png|gif)$/i, // 匹配图片文件
        use: [
          // webpack5
          // {
          //     test: /\.(jpe?g|png|gif)$/i,
          //     type: 'asset',
          //     generator: {
          //       // 输出文件位置以及文件名
          //       // [ext] 自带 "." 这个与 url-loader 配置不同
          //       filename: "[name][hash:8][ext]"
          //     },
          //     parser: {
          //       dataUrlCondition: {
          //         maxSize: 50 * 1024 //超过50kb不转 base64
          //       }
          //     }
          //   }
          // 'file-loader', // 使用 file-loader
          // {
          //     loader: 'file-loader',
          //     options: {
          //         name: '[name][hash:8].[ext]'
          //     }
          // },
          {
            loader: 'url-loader',
            options: {
              name: '[name][hash:8].[ext]',
              // 文件小于 50k 会转换为 base64,大于则拷贝文件
              limit: 50 * 1024
            }
          }



        ]
      },
      {
        test: /\.js$/i,
        include: resolve('src'),
        exclude: /node_modules/,
        use: [
          {
            loader: 'babel-loader',
            options: {
              presets: [
                '@babel/preset-env'
              ],
              module: false, // Tree-shaking
              cacheDirectory: true // 启用缓存
            }
          }
        ]
      }


    ]
  },
  // 反向定位到源码
  devtool: 'source-map',
  plugins: [ //配置插件
    new HtmlWebpackPlugin({
      template: "./src/index.html"
    }),
    new CleanWebpackPlugin(), // 引入插件
    new MiniCssExtractPlugin({
      filename: "[name].[hash:8].css"
    }),
    new BundleAnalyzerPlugin({
      analyzerMode: 'disabled',  // 不启动展示打包报告的http服务器
      generateStatsFile: true, // 是否生成stats.json文件
    })

  ],
  devServer: {
    contentBase: path.resolve(__dirname, "public"), //静态文件目录
    compress: true, //是否启动压缩gzip
    port: 8080, // 端口号
    //  open:true   是否自动打开浏览器
  }
}

module.exports = (env, argv) => {
  console.log("argv.mode=", argv.mode) //打印mode(模式)值
  // 这里可以通过不同的模式修改config配置
  return config
}

发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言