• Webpack Loader种类以及执行顺序


      我们在用webpack构建项目的时候,有两种配置打包文件的方式:

    1. import或者require :a-loader!b-loader!.././static/dog.png(打包某一个文件)
    2. 配置webpack.config.js文件的module.rules(打包某一类的文件)

      针对于以上的第二种方式我贴下我之前一篇博客中的配置 Vue动态注册异步组件(非同一个工程的组件)

    var path = require('path')
    var webpack = require('webpack')
    
    module.exports = {
      entry: process.NODE_ENV === 'development' ? './src/main.js' : './src/component/index.js',
      output: {
        path: path.resolve(__dirname, './dist'),
        publicPath: '/dist/',
        filename: 'async-component.js'
      },
      module: {
        rules: [
          {
            test: /.css$/,
            use: [
              'vue-style-loader',
              'css-loader'
            ],
          },
          {
            test: /.scss$/,
            use: [
              'vue-style-loader',
              'css-loader',
              'sass-loader'
            ],
          },
          {
            test: /.sass$/,
            use: [
              'vue-style-loader',
              'css-loader',
              'sass-loader?indentedSyntax'
            ],
          },
          {
            test: /.vue$/,
            loader: 'vue-loader',
            options: {
              loaders: {
                // Since sass-loader (weirdly) has SCSS as its default parse mode, we map
                // the "scss" and "sass" values for the lang attribute to the right configs here.
                // other preprocessors should work out of the box, no loader config like this necessary.
                'scss': [
                  'vue-style-loader',
                  'css-loader',
                  'sass-loader'
                ],
                'sass': [
                  'vue-style-loader',
                  'css-loader',
                  'sass-loader?indentedSyntax'
                ]
              }
              // other vue-loader options go here
            }
          },
          {
            test: /.js$/,
            loader: 'babel-loader',
            exclude: /node_modules/
          },
          {
            test: /.(png|jpg|gif|svg)$/,
            loader: 'file-loader',
            options: {
              name: '[name].[ext]?[hash]'
            }
          }
        ]
      },
      resolve: {
        alias: {
          'vue$': 'vue/dist/vue.esm.js'
        },
        extensions: ['*', '.js', '.vue', '.json']
      },
      devServer: {
        historyApiFallback: true,
        noInfo: true,
        overlay: true
      },
      performance: {
        hints: false
      },
      devtool: '#eval-source-map'
    }
    
    if (process.env.NODE_ENV === 'production') {
      module.exports.devtool = '#source-map'
      // http://vue-loader.vuejs.org/en/workflow/production.html
      module.exports.plugins = (module.exports.plugins || []).concat([
        new webpack.DefinePlugin({
          'process.env': {
            NODE_ENV: '"production"'
          }
        }),
        // new webpack.optimize.UglifyJsPlugin({
        //   sourceMap: true,
        //   compress: {
        //     warnings: false
        //   }
        // }),
        new webpack.LoaderOptionsPlugin({
          minimize: false
        })
      ])
    }

      对单文件打包的方式的loader被称为行内(inline)loader;对于rules中的loader,webpack还定义了一个属性 enforce,可取值有 pre(为pre loader)、post(为post loader),如果没有值则为(normal loader)。所以loader在webpack中有4种:normal,inline,pre,post

      贴下官方源码地址 :Loader Type

    for (const r of result) {
                            if (r.type === "use") {
                                if (r.enforce === "post" && !noPrePostAutoLoaders) {
                                    useLoadersPost.push(r.value);
                                } else if (
                                    r.enforce === "pre" &&
                                    !noPreAutoLoaders &&
                                    !noPrePostAutoLoaders
                                ) {
                                    useLoadersPre.push(r.value);
                                } else if (
                                    !r.enforce &&
                                    !noAutoLoaders &&
                                    !noPrePostAutoLoaders
                                ) {
                                    useLoaders.push(r.value);
                                }
                            } else if (
                                typeof r.value === "object" &&
                                r.value !== null &&
                                typeof settings[r.type] === "object" &&
                                settings[r.type] !== null
                            ) {
                                settings[r.type] = cachedCleverMerge(settings[r.type], r.value);
                            } else {
                                settings[r.type] = r.value;
                            }
                        }

      所有的loader的执行顺序都有两个阶段:pitching和normal阶段,类似于js中的事件冒泡、捕获阶段(有人嫌官方的词描述的比较晦涩,修改为loader的标记阶段(mark stage)和执行阶段(execution/run stage))。

      对于第二种方式的解释:webpack 官方解释

    1. Pitching阶段: post,inline,normal,pre
    2. Normal阶段:pre,normal,inline,post

      我看有大神的解释如下:(很好理解)

    It's like the two phases of event bubbling...
    
    a!b!c!resource
    
    pitch a
      pitch b
        pitch c
          read file resource (adds resource to dependencies)
        run c
      run b
    run a
    When a loader return something in the pitch phase the process continues with the normal phase of the next loader... Example:
    
    pitch a
      pitch b (returns something)
    run a

      对应的方式1解析loader的源码

    let elements = requestWithoutMatchResource
        .replace(/^-?!+/, "")
        .replace(/!!+/g, "!")
        .split("!");

      对应的方式2解析loader的源码

    const result = this.ruleSet.exec({
                            resource: resourcePath,
                            realResource:
                                matchResource !== undefined
                                    ? resource.replace(/?.*/, "")
                                    : resourcePath,
                            resourceQuery,
                            issuer: contextInfo.issuer,
                            compiler: contextInfo.compiler
                        });

      那么问题来了,如果我们采用了两种解析loader的方式,他们的执行是什么样的呢?答案是inline loader优先级高于config配置文件中的loader:源码

      webpack使用了neo-async库(用来提供js异步编程的工具库)来解析loader模块,解析inline loader的源码

    解析config loader的源码

      webpack官方文档推荐不适用inline loader,最好用在配置文件中使用loader(注意:loader处理的代码中是含有inline loader的)。另外,在特殊情况下我我们可以在inline loader间接改变loader的执行顺序(禁止某些另外3种loader),比如在我们的自己公司某个同事的不是很规范的js库在引入的时候需要禁止掉eslint-loader对其进行处理

    1. 加入 !   前缀禁用配置文件中的普通loader,比如:require("!raw!./script.coffee")
    2. 加入 !!  前缀禁用配置文件中所有的loader,比如:require("!!raw!./script.coffee")
    3. 加入 -!  前缀禁用配置文件中的pre loader和普通loader,但是不包括post loader,比如:require("!!raw!./script.coffee")

      关于loader的禁用,webpack官方的建议是:除非从另一个loader处理生成的,一般不建议主动使用

    1. pre loader 配置:图片压缩
    2. 普通loader 配置:coffee-script转换
    3. inline loader 配置:bundle loader
    4. post loader 配置: 代码覆盖率工具
  • 相关阅读:
    gcc/g++动态链接库和静态库的链接顺序
    linux文件描述符--转载
    mysql之test表
    linux之eventfd()
    spring data jpa实现多条件查询(分页和不分页)
    List的remove方法里的坑
    启动Spring boot报错:nested exception is java.sql.SQLException: Field 'id' doesn't have a default value
    CentOS下yum安装jdk
    CentOS下yum安装mysql
    Redis有序Set、无序Set的使用经历
  • 原文地址:https://www.cnblogs.com/hanshuai/p/11287231.html
Copyright © 2020-2023  润新知