精华内容
下载资源
问答
  • webpack原理详解
    2022-02-08 13:35:36

    一、proxy是什么?

    proxy是Webpack提供的代理服务,用来解决本地开发时的跨域问题(浏览器安全策略限制),实现的基本方式就是接收客户端的请求后转发到目标服务器。

    二、实现原理

    ​ 在开发阶段中,webpack-dev-server会启动一个本地开发的服务器,即跨域所使用的代理服务器,通过利用http-proxy-middleware代理中间件,代理服务器会响应本地请求,继而转发到目标服务器,目标服务器响应数据后再将数据返回给代理服务器,代理服务器再将数据返回给本地。

    ​ 在此阶段中不涉及任何跨域问题,因为代理服务器跟本地同源,而服务器之间不存在跨域问题,跨域问题是浏览器安全策略限制。

    三、使用

    module.exports = {
      devServer: {
        proxy: {
          // 第一种写法
          '/api': 'http://www.baidu.com' // 本地发送localhost:8080/api请求,相当于请求http://www.100tal.com/api
          
          // 第二种写法
          '/api': {
          	target: 'http://www.baidu.com',
          	pathRewrite: {'^/api': ''},  // 本地发送localhost:8080/api/user请求,相当于请求http://www.100tal.com/user
        		secure: false,	// 是否支持https
        		changeOrigin: true, // 表示是否更新代理后请求的 headers 中host地址,设置为true之后,headers中host地址为target值
        	},
        }
    		// 第三种写法
    		proxy: [
          {
            // 多路径代理到同一目标,即发送/auth或/api请求,均会转发至http://www.100tal.com
            context: ['/auth', '/api'],
            target: 'http://www.baidu.com'
          }
        ]
      }
    }
    
    更多相关内容
  • 27、webpack的配置详解

    2022-04-18 17:07:50
    1、webpack是什么?作用是什么?在项目中能解决什么样的问题?谈谈理解 答: (1)webpack是什么? webpack是一种前端资源构建工具,一个静态资源模块打包器。在webpack来看,前端所有资源(js/json/css/less/img)...
    1、webpack是什么?作用是什么?在项目中能解决什么样的问题?谈谈理解
    答:
    (1)webpack是什么?
    webpack是一种前端资源构建工具,一个静态资源模块打包器。在webpack来看,前端所有资源(js/json/css/less/img)等都会作为模块去处理,它将根据模块的依赖关系进行静态分析,打包生成对应的静态资源(bundle)
    (2)随着框架的诞生后,单页应用技术的流行,前端网页功能比较丰富,JavaScript的复杂度也随之增长,需要的依赖包也比较多,而且对于ES6+新语法,以及css的一些扩展语言,比如:less sass typescript等,webpack可以去对这些进行编译转换。充当了这么一个作用。
    2、工作原理:
    项目中,把它充当为一个打包机器,会分析项目的结构,找到JavaScript模块和浏览器不能直接运行的一些扩展语言(ts,less、sass),webpack会对这些进行转换为浏览器能识别的格式并打包后供给浏览器使用。
    3、打包原理:
    把项目的静态资源文件当为模块:js html css都是互相依赖相互引用的, 通过定义entry入口,对所有依赖的文件进行跟踪,通过loader和plugins处理对这些进行转换打包。
    按需加载:把打包后的bundle资源模块通过code split代码拆分功能(使用optimization配置优化)进行拆分为多个chunk,并将重复的抽取出来合并为chunk,从而实现按需加载,把node_modules依赖包打包成一个bundle输出。
    4、webpack的五个核心概念
    (1)Entry:入口,为webpack构建的一个入口,告诉webpack要从哪一个模块开始构建(./src/index.js)。
    (2)Output:出口,告诉webpack从哪里输出打包好的代码以及如何命名文件(默认./dist)
    (3)Loader:项目构建时,loader能够让webpack去处理一些非JavaScript文件(webpack本身只能理解JavaScript)
    (4)Plugins:插件(Plugins)可以用于执行范围更广的任务。插件的范围包括,从打包优化和压缩,
    一直到重新定义环境中的变量等
    (5)Mode:mode指示webpack使用相应的模式去配置构建项目。
    5、webpack详细配置
    (1)entry
    const { resolve } = require('path');
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    
    /*
      entry: 入口起点
        1. string --> './src/index.js'
          单入口
          打包形成一个chunk。 输出一个bundle文件。
          此时chunk的名称默认是 main
        2. array  --> ['./src/index.js', './src/add.js']
          多入口
          所有入口文件最终只会形成一个chunk, 输出出去只有一个bundle文件。
            --> 只有在HMR功能中让html热更新生效~
        3. object
          多入口
          有几个入口文件就形成几个chunk,输出几个bundle文件
          此时chunk的名称是 key
    
          --> 特殊用法
            {
              // 所有入口文件最终只会形成一个chunk, 输出出去只有一个bundle文件。
              index: ['./src/index.js', './src/count.js'], 
              // 形成一个chunk,输出一个bundle文件。
              add: './src/add.js'
            }
    */
    
    module.exports = {
      entry: {
        index: ['./src/index.js', './src/count.js'], 
        add: './src/add.js'
      },
      output: {
        filename: '[name].js',
        path: resolve(__dirname, 'build')
      },
      plugins: [new HtmlWebpackPlugin()],
      mode: 'development'
    };
    
    
    (2)output
    const { resolve } = require('path');
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    
    module.exports = {
      entry: './src/index.js',
      output: {
        // 文件名称(指定名称+目录)
        filename: 'js/[name].js',
        // 输出文件目录(将来所有资源输出的公共目录)
        path: resolve(__dirname, 'build'),
        // 所有资源引入公共路径前缀 --> 'imgs/a.jpg' --> '/imgs/a.jpg'
        publicPath: '/',
        chunkFilename: 'js/[name]_chunk.js', // 非入口chunk的名称
        // library: '[name]', // 整个库向外暴露的变量名
        // libraryTarget: 'window' // 变量名添加到哪个上 browser
        // libraryTarget: 'global' // 变量名添加到哪个上 node
        // libraryTarget: 'commonjs'
      },
      plugins: [new HtmlWebpackPlugin()],
      mode: 'development'
    };
    
    
    (3)配置module(loader)
    const { resolve } = require('path');
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    
    module.exports = {
      entry: './src/index.js',
      output: {
        filename: 'js/[name].js',
        path: resolve(__dirname, 'build')
      },
      module: {
        rules: [
          // loader的配置
          {
            test: /\.css$/,
            // 多个loader用use
            use: ['style-loader', 'css-loader']
          },
          {
            test: /\.js$/,
            // 排除node_modules下的js文件
            exclude: /node_modules/,
            // 只检查 src 下的js文件
            include: resolve(__dirname, 'src'),
            // 优先执行
            enforce: 'pre',
            // 延后执行
            // enforce: 'post',
            // 单个loader用loader
            loader: 'eslint-loader',
            options: {}
          },
          {
            // 以下配置只会生效一个
            oneOf: []
          }
        ]
      },
      plugins: [new HtmlWebpackPlugin()],
      mode: 'development'
    };
    
    
    (4)配置resolve
    const { resolve } = require('path');
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    
    module.exports = {
      entry: './src/js/index.js',
      output: {
        filename: 'js/[name].js',
        path: resolve(__dirname, 'build')
      },
      module: {
        rules: [
          {
            test: /\.css$/,
            use: ['style-loader', 'css-loader']
          }
        ]
      },
      plugins: [new HtmlWebpackPlugin()],
      mode: 'development',
      // 解析模块的规则
      resolve: {
        // 配置解析模块路径别名: 优点简写路径 缺点路径没有提示
        alias: {
          $css: resolve(__dirname, 'src/css')
        },
        // 配置省略文件路径的后缀名
        extensions: ['.js', '.json', '.jsx', '.css'],
        // 告诉 webpack 解析模块是去找哪个目录
        modules: [resolve(__dirname, '../../node_modules'), 'node_modules']
      }
    };
    
    
    (5)配置dev server
    const { resolve } = require('path');
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    
    module.exports = {
      entry: './src/js/index.js',
      output: {
        filename: 'js/[name].js',
        path: resolve(__dirname, 'build')
      },
      module: {
        rules: [
          {
            test: /\.css$/,
            use: ['style-loader', 'css-loader']
          }
        ]
      },
      plugins: [new HtmlWebpackPlugin()],
      mode: 'development',
      resolve: {
        alias: {
          $css: resolve(__dirname, 'src/css')
        },
        extensions: ['.js', '.json', '.jsx', '.css'],
        modules: [resolve(__dirname, '../../node_modules'), 'node_modules']
      },
      devServer: {
        // 运行代码的目录
        contentBase: resolve(__dirname, 'build'),
        // 监视 contentBase 目录下的所有文件,一旦文件变化就会 reload
        watchContentBase: true,
        watchOptions: {
          // 忽略文件
          ignored: /node_modules/
        },
        // 启动gzip压缩
        compress: true,
        // 端口号
        port: 5000,
        // 域名
        host: 'localhost',
        // 自动打开浏览器
        open: true,
        // 开启HMR功能
        hot: true,
        // 不要显示启动服务器日志信息
        clientLogLevel: 'none',
        // 除了一些基本启动信息以外,其他内容都不要显示
        quiet: true,
        // 如果出错了,不要全屏提示~
        overlay: false,
        // 服务器代理 --> 解决开发环境跨域问题
        proxy: {
          // 一旦devServer(5000)服务器接受到 /api/xxx 的请求,就会把请求转发到另外一个服务器(3000)
          '/api': {
            target: 'http://localhost:3000',
            // 发送请求时,请求路径重写:将 /api/xxx --> /xxx (去掉/api)
            pathRewrite: {
              '^/api': ''
            }
          }
        }
      }
    };
    
    
    (6)配置optimization
    const { resolve } = require('path');
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    const TerserWebpackPlugin = require('terser-webpack-plugin')
    
    module.exports = {
      entry: './src/js/index.js',
      output: {
        filename: 'js/[name].[contenthash:10].js',
        path: resolve(__dirname, 'build'),
        chunkFilename: 'js/[name].[contenthash:10]_chunk.js'
      },
      module: {
        rules: [
          {
            test: /\.css$/,
            use: ['style-loader', 'css-loader']
          }
        ]
      },
      plugins: [new HtmlWebpackPlugin()],
      mode: 'production',
      resolve: {
        alias: {
          $css: resolve(__dirname, 'src/css')
        },
        extensions: ['.js', '.json', '.jsx', '.css'],
        modules: [resolve(__dirname, '../../node_modules'), 'node_modules']
      },
      optimization: {
        splitChunks: {
          chunks: 'all'
          // 默认值,可以不写~
          /* minSize: 30 * 1024, // 分割的chunk最小为30kb
          maxSiza: 0, // 最大没有限制
          minChunks: 1, // 要提取的chunk最少被引用1次
          maxAsyncRequests: 5, // 按需加载时并行加载的文件的最大数量
          maxInitialRequests: 3, // 入口js文件最大并行请求数量
          automaticNameDelimiter: '~', // 名称连接符
          name: true, // 可以使用命名规则
          cacheGroups: {
            // 分割chunk的组
            // node_modules文件会被打包到 vendors 组的chunk中。--> vendors~xxx.js
            // 满足上面的公共规则,如:大小超过30kb,至少被引用一次。
            vendors: {
              test: /[\\/]node_modules[\\/]/,
              // 优先级
              priority: -10
            },
            default: {
              // 要提取的chunk最少被引用2次
              minChunks: 2,
              // 优先级
              priority: -20,
              // 如果当前要打包的模块,和之前已经被提取的模块是同一个,就会复用,而不是重新打包模块
              reuseExistingChunk: true
            }
          }*/
        },
        // 将当前模块的记录其他模块的hash单独打包为一个文件 runtime
        // 解决:修改a文件导致b文件的contenthash变化
        runtimeChunk: {
          name: entrypoint => `runtime-${entrypoint.name}`
        },
        minimizer: [
          // 配置生产环境的压缩方案:js和css
          new TerserWebpackPlugin({
            // 开启缓存
            cache: true,
            // 开启多进程打包
            parallel: true,
            // 启动source-map
            sourceMap: true
          })
        ]
      }
    };
    
    
    6、代码分割:将打包输出的一个大的bundle文件分割为多个chunk文件,从而可以实现按需加载
    (1)、多入口方式配置代码分割,有多少个入口就打包输出多少个chunk文件
    entry: {
        index: './src/js/index.js',
        test: './src/js/test.js'
      },
    
    (2)、多入口方式加入optimization配置实现,不管构建是多入口还是单入口都能将node_modules文件单独打包成一个chunk,自动分析多入口chunk中,有没有引入公共的模块。如果有会将公共的模块打包成单独一个chunk。
    在module.exports添加optimization配置
    optimization: {
        //splitChunks插件会对代码进行分析
        splitChunks: {
          chunks: 'all'
        }
      },
    
    (3)、单入口方式,配置optimization方式,在入口文件中通过js代码让引入的某个文件被单独打包成一个chunk,import动态导入语法:能让某个文件单独打包
    /*
      通过js代码,让某个文件被单独打包成一个chunk
      import动态导入语法:能将某个文件单独打包
    */
    import(/* webpackChunkName: 'test' */'./test')
      .then(({ mul, count }) => {
        // 文件加载成功~
        // eslint-disable-next-line
        console.log(mul(2, 5));
      })
      .catch(() => {
        // eslint-disable-next-line
        console.log('文件加载失败~');
      });
    
    展开全文
  • Webpack Tapable原理详解

    2019-02-16 14:19:00
    Webpack 就像一条生产线, 要经过一系列的处理流程才能将源文件转换成输出结果。这条生产线上的每个流程都是单一的, 多个流程之间存在依赖关系。只能完成当前处理后才会转交到下一个流程。 插...

    directory

        - src
            - sim    ---- 简单的模拟实现
            - /.js$/ ---- 使用

    代码已上传github, 地址

    Detailed

    Webpack 就像一条生产线, 要经过一系列的处理流程才能将源文件转换成输出结果。这条生产线上的每个流程都是单一的, 多个流程之间存在依赖关系。只能完成当前处理后才会转交到下一个流程。

    插件就像一个插入到生产线中的一个功能, 它会在特定的时机对生产线上的资源进行处理。

    这条生产线很复杂, Webpack则是通过 tapable 核心库来组织这条生产线。

    Webpack 在运行中会通过 tapable 提供的钩子进行广播事件, 插件只需要监听它关心的事件,就可以加入到这条生产线中,去改变生产线的运作。使得 Webpack整体扩展性很好。

    Tapable Hook

    Tapable 提供同步(Sync)和异步(Async)钩子类。而异步又分为 异步串行异步并行钩子类。

    右键图片,在新标签中查看完整图片

    Tapable Hook Class

    逐个分析每个钩子类的使用及其原理

    同步钩子类

    • SyncHook
    • SyncBailHook
    • SyncWaterfallHook
    • SyncLoopHook

    同步钩子类通过实例的 tap 方法监听函数, 通过 call发布事件

    SyncHook

    同步串行不关心订阅函数执行后的返回值是什么。其原理是将监听(订阅)的函数存放到一个数组中, 发布时遍历数组中的监听函数并且将发布时的 arguments传递给监听函数

    class SyncHook {
        constructor(options) {
            this.options = options
            this.hooks = []  //存放监听函数的数组
        }
        tap(name, callback) {
            this.hooks.push(callback)
        }
        call(...args) {
            for (let i = 0; i < this.hooks.length; i++) {
                this.hooks[i](...args)
            }
        }
    }
    
    const synchook = new SyncHook('name')
    // 注册监听函数
    synchook.tap('name', (data) => {
        console.log('name', data)
    })
    synchook.tap('age', (data) => {
        console.log('age', data)
    })
    
    // 发布事件
    synchook.call('qiqingfu')

    打印结果:

    name qiqingfu
    age qiqingfu

    SyncBailHook

    同步串行, 但是如果监听函数的返回值不为 null, 就终止后续的监听函数执行

    class SyncBailHook {
            constructor(options) {
              this.options = options
              this.hooks = []
        }
        tap(name, callback) {
            this.hooks.push(callback)
        }
        call(...args) {
            let ret, i = 0
            do {
                // 将第一个函数的返回结果赋值给ret, 在while中如果结果为 true就继续执行do代码块
                ret = this.hooks[i++](...args)
            } while(!ret)
        }
    }
    
    const syncbailhook = new SyncBailHook('name')
    
    syncbailhook.tap('name', (data) => {
        console.log('name', data)
        return '我的返回值不为null'
    })
    syncbailhook.tap('age', (data) => {
        console.log('age', data)
    })
    
    syncbailhook.call('qiqingfu')

    执行结果

    name qiqingfu

    SyncWaterfallHook

    同步串行瀑布流, 瀑布流指的是第一个监听函数的返回值,做为第二个监听函数的参数。第二个函数的返回值作为第三个监听函数的参数,依次类推...

    class SyncWaterfallHook {
        constructor(options) {
              this.options = options
              this.hooks = []
        }
        tap(name, callback) {
            this.hooks.push(callback)
        }
        call(...args) {
            let [firstHook, ...otherHooks] = this.hooks
            /**
             * 通过解构赋值先取出第一个监听函数执行
             * 并且将第一个函数的执行结果传递给第二个, 第二个传递给第三个,迭代的过程 
             */
            let ret = firstHook(...args)
            otherHooks.reduce((f,n) => {
                return n(f)
            }, ret)
        }
    }
    
    const syncWaterfallHook = new SyncWaterfallHook('name')
    
    syncWaterfallHook.tap('name', data => {
        console.log('name', data)
        return 23
    })
    syncWaterfallHook.tap('age', data => {
        console.log('age', data)
    })
    
    syncWaterfallHook.call('qiqingfu')

    打印结果

    name qiqingfu
    age 23

    SyncLoopHook

    同步串行, 如果监听函数的返回值为 true, 则反复执行当前的监听函数,直到返回指为 undefind则继续执行下面的监听函数

    class SyncLoopHook {
      constructor(options) {
        this.options = options
        this.hooks = []
        }
        tap(name, callback) {
        this.hooks.push(callback)
        }
        call(...args) {
            for (let i = 0; i < this.hooks.length; i++) {
                let hook = this.hooks[i], ret
                do{
                    ret = hook(...args)
                }while(ret === true && ret !== undefined)
            }
        }
    }
    
    const syncLoopHook = new SyncLoopHook('name')
    
    let n1 = 0
    syncLoopHook.tap('name', data => {
        console.log('name', data)
        return n1 < 2 ? true : undefined
    })
    syncLoopHook.tap('end', data => {
        console.log('end', data)
    })
    
    syncLoopHook.call('qiqingfu')

    执行结果

    name qiqingfu
    name qiqingfu
    name qiqingfu  第三次打印的时候, n1的指为2, 返回值为 undefined则执行后面的监听函数
    end qiqingfu

    异步钩子

    • 异步并行 (Parallel)
      • AsyncParallelHook
      • AsyncParalleBailHook
    • 异步串行 (Series)
      • AsyncSeriesHook
      • AsyncSeriesBailHook
      • AsyncSeriesWaterfallHook

    凡有异步,必有回调

    同步钩子是通过 tap来监听函数的, call来发布的。

    异步钩子是通过 tapAsynctapPromise 来监听函数,通过 callAsyncpromise来发布订阅的。

    AsyncParallelHook

    异步并行, 监听的函数会一块执行, 哪个函数先执行完就先触发。不需要关心监听函数的返回值。

    class AsyncParallelHook {
        constructor(options) {
            this.options = options
            this.asyncHooks = []
        }
        // 订阅
        tapAsync(name, callback) {
            this.asyncHooks.push(callback)
        }
        // 发布
        callAsync(...args) {
            /**
             * callAsync(arg1, arg2,..., cb)
             * 发布的时候最后一个参数可以是回调函数
             * 订阅的每一个函数的最后一个参数也是一个回调函数,所有的订阅函数执行完
             * 且都调用了最后一个函数,才会执行cb 
             */
        const finalCallback = args.pop()
            let i = 0
            // 将这个作为最后一个参数传过去,使用的时候选择性调用
            const done = () => {
                ++i === this.asyncHooks.length && finalCallback()
            }
            this.asyncHooks.forEach(hook => {
                hook(...args, done)
            })
        }
    }
    
    const asyncParallelHook = new AsyncParallelHook('name')
    
    asyncParallelHook.tapAsync('name', (data, done) => {
        setTimeout(() => {
        console.log('name', data)
        done()
      }, 2000)
    })
    asyncParallelHook.tapAsync('age', (data, done) => {
        setTimeout(() => {
        console.log('age', data)
        done()
      }, 3000)
    })
    
    console.time('time')
    asyncParallelHook.callAsync('qiqingfu', () => {
      console.log('监听函数都调用了 done')
      console.timeEnd('time')
    })

    打印结果

    name qiqingfu
    age qiqingfu
    监听函数都调用了 done
    time: 3002.691ms

    AsyncParalleBailHook

    暂时不理解

    AsyncSeriesHook

    异步串行钩子类, 不关心 callback的参数。异步函数一个一个的执行,但是必须调用 done函数。

    class AsyncSeriesHook {
        constructor(options) {
            this.options = options
            this.asyncHooks = []
        }
        tapAsync(name, callback) {
            this.asyncHooks.push(callback)
        }
        callAsync(...args) {
            const finalCallback = args.pop()
            
            let i = 0
            const done = () => {
                let task = this.asyncHooks[i++]
                task ? task(...args, done) : finalCallback()
            }
            done()
        }
    }
    
    const asyncSeriesHook = new AsyncSeriesHook('name')
    
    asyncSeriesHook.tapAsync('name', (data, done) => {
        setTimeout(() => {
            console.log('name', data)
            done()
        }, 1000)
    })
    
    asyncSeriesHook.tapAsync('age', (data, done) => {
        setTimeout(() => {
            console.log('age', data)
            done()
        }, 2000)
    })
    
    console.time('time')
    asyncSeriesHook.callAsync('qiqingfu', () => {
        console.log('end')
        console.timeEnd('time')
    })

    执行结果

    name qiqingfu
    age qiqingfu
    end
    time: 3010.915ms

    AsyncSeriesBailHook

    同步串行钩子类, callback的参数如果不是 null, 后面所有的异步函数都不会执行,直接执行 callAsync方法的回调函数

    class AsyncSeriesBailHook {
        constructor(options) {
            this.options = options
            this.asyncHooks = []
        }
        tapAsync(name, callback) {
            this.asyncHooks.push(callback)
        }
        callAsync(...args) {
            const finalCallback = args.pop()
    
            let i = 0
            const done = data => {
          if (data) return finalCallback()
          let task = this.asyncHooks[i++]
          task ? task(...args, done) : finalCallback()
            }
            done()
        }
    }
    
    const asyncSeriesBailHook = new AsyncSeriesBailHook('name')
    
    asyncSeriesBailHook.tapAsync('1', (data, done) => {
        setTimeout(() => {
            console.log('1', data)
            done(null)
        }, 1000)
    })
    
    asyncSeriesBailHook.tapAsync('2', (data, done) => {
        setTimeout(() => {
            console.log('2', data)
            done(null)
        }, 2000)
    })
    
    console.time('times')
    asyncSeriesBailHook.callAsync('qiqingfu', () => {
        console.log('end')
        console.timeEnd('times')
    })

    打印结果

    1 qiqingfu
    2 qiqingfu
    end
    times: 3012.060ms

    AsyncSeriesWaterfallHook

    同步串行钩子类, 上一个监听函数 callback(err, data)的第二个参数, 可以作为下一个监听函数的参数

    class AsyncSeriesWaterfallHook {
        constructor(options) {
            this.options = options
            this.asyncHooks = []
        }
        tapAsync(name, callback) {
            this.asyncHooks.push(callback)
        }
        callAsync(...args) {
            const finalCallback = args.pop()
    
            let i = 0, once
            const done = (err, data) => {
                let task = this.asyncHooks[i++]
                if (!task) return finalCallback()
                if (!once) {
                    // 只执行一次
                    task(...args, done)
                    once = true
                } else {
                    task(data, done)
                }
            }
            done()
        }
    }
    
    const asyncSeriesWaterfallHook = new AsyncSeriesWaterfallHook('name')
    
    asyncSeriesWaterfallHook.tapAsync('1', (data, done) => {
        setTimeout(() => {
            console.log('1', data)
            done(null, '第一个callback传递的参数')
        }, 1000)
    })
    
    asyncSeriesWaterfallHook.tapAsync('2', (data, done) => {
        setTimeout(() => {
            console.log('2', data)
            done(null)
        }, 1000)
    })
    
    console.time('timer')
    asyncSeriesWaterfallHook.callAsync('qiqingfu', () => {
        console.log('end')
        console.timeEnd('timer')
    })

    打印结果

    1 qiqingfu
    2 第一个callback传递的参数
    end
    timer: 2015.445ms

    END

    如果理解有误, 麻烦纠正!

    参考文章

    webpack4.0源码分析之Tapable

    webpack 4.0 Tapable 类中的常用钩子函数源码分析

    转载于:https://www.cnblogs.com/qiqingfu/p/10387634.html

    展开全文
  • (二)webpack babel详解

    2021-07-14 11:37:51
    也可以在webpack.json.js中设置 module: { rules: [ { test: /\.js$/, use: { loader: "babel-loader", options: { presets:[ ["@babel/preset-env",{ targets:["chrome 88"] }] ] } } } ] } 5.babel底层原理 ...

    1.安装

    npm install @babel/core -d
    npm install @babel/cli -d
    

    2.使用

    npx babel src --out-dir result //指将src文件夹下文件用babel转化后放到result文件夹下
    
    

    3.使用插件

    npm install @babel/plugin-transform-arrow-functions -d //安装箭头函数转化插件
    npx babel src --out-dir result --plugins=@babel/plugin-transform-arrow-functions  //使用箭头函数转化插件
    
    npm install @babel/plugin-transform-block-scoping -d //安装作用域(const=>var)转化插件
    npx babel src --out-dir result --plugins=@babel/plugin-transform-arrow-functions,@babel/plugin-transform-block-scoping //使用上述两个插件
    

    4.使用babel预设

    npm install @babel/preset-env -d //安装相当于一系列插件组合
    npx babel src --out-dir result  --presets=@babel/preset-env //使用babel预设
    

    这个preset会使用哪些插件取决于.browserslistrc中你要适配哪些浏览器,preset会帮你使用需要的插件来适配。
    面对哪些浏览器,也可以在webpack.json.js中设置

    module: {
            rules: [
                {
                    test: /\.js$/,
                    use: {
                        loader: "babel-loader",
                        options: {
                            presets:[
                                ["@babel/preset-env",{
                                    targets:["chrome 88"]
                                }]
                            ]
                        }
                    }
                }
            ]
        }
    

    5.babel底层原理
    在这里插入图片描述
    在这里插入图片描述
    源代码:
    在这里插入图片描述
    tokens:
    在这里插入图片描述
    抽象语法树:
    在这里插入图片描述
    在这里插入图片描述
    6.使用babel-loader

    //webpack中配置
    //法一
    module: {
        rules: [
            {
                test: /\.js$/,
                use: {
                    loader: "babel-loader",
                    options: {
                        plugins: [
                            '@babel/plugin-transform-arrow-functions',
                            '@babel/plugin-transform-block-scoping'
                        ]
                    }
                }
            }
        ]
    }
    
    法二:
    module: {
        rules: [
            {
                test: /\.js$/,
                use: {
                    loader: "babel-loader",
                    options: {
                        presets:[
                            "@babel/preset-env"
                        ]
                    }
                }
            }
        ]
    }
    

    7.babel配置文件

    //babel.config.js
    module.exports={
    presets:[
    "@babel/preset-env"
    ]
    }
    

    8.folyfill

    npm install core-js regenerator-runtime --save
    
    //babel.config.js(相当于设置webpack.config.js中的module中使用babel-loader的详细设置)
    module.exports = {
        presets: [
            ["@babel/preset-env",{ //使用@babel/preset-env preset
                useBuiltIns:"usage", //使用polyfill  该属性值(false:不适用polyfill,usage:使用代码里有的特性,entry:要适配的浏览器所有特性)
                					//当值为entry时,还要在入口文件写入 import "core-js/stable";  import "regenerator-runtime/runtime";否则该属性无效
                corejs:3  //corejs的默认版本为2,所以得设置为3
            }]
        ]
    }
    
    注意:在webpack.config.js中,要将exclude设置为/node_modules/,因为在node_modules中,也可能使用了与folyfill不同的特性,因此不需要对其进行转化。
    

    9.plugin-transform-runtime
    因为我们在使用useBuiltIns时,加入的特性是放在全局里面的,可能会污染一些代码。而plugin-transform-runtime却不是这样。因此我们在写一些第三方库的时候会使用plugin-transform-runtime而不是useBuiltIns。

    npm install @babel/plugin-transform-runtime -d
    npm install @babel/runtime-corejs3 --save
    
    //babel.config.js
    module.exports = {
        presets: [
            ["@babel/preset-env"]
        ],
        plugins:[
            ["@babel/plugin-transform-runtime",{corejs:3}]
        ]
    }
    

    10.ts的转换

    法一:ts-loader

    npm install typescript -g
    npm install ts-loader -d
    npm link typescript
    tsc --init
    
    //webpack.config.js
    {
        test:/\.ts$/,
        use:"ts-loader"
    }
    

    法二:babel-loader
    如果ts中也有promise等es6特性,那么需要使用babel来进行转化。

    npm install @babel/preset-typescript -d
    
    //babel.config.js
    module.exports = {
        presets: [
            ["@babel/preset-env",{
                useBuiltIns:"usage",
                corejs:3
            }],
            ["@babel/preset-typescript"]
        ]
    }
    
    //webpack.config.js
    {
        test:/\.ts$/,
        use:"babel-loader"
    }
    

    两种方法的优缺点
    babel-loader可以使用polyfill,但不能对类型进行校验

    因此,建议在其他特性少时直接使用ts-loader,其他特性多时使用babel-loader进行打包,但用"tsc --noEmit"命令来检测类型是否正确

    展开全文
  • webpack原理与实践

    2021-11-01 10:30:59
    webpack
  • webpack提供的一个非常强大的功能就是code spliting(代码切割)。 在webpack 1.x中提供了 require.ensure([], () => { let module = require('./page1/module'); // do something }, 'module1') 利用require....
  • webpack详解

    2022-08-04 10:38:04
    loader是webpack中一个非常核心的概念。在我们之前的实例中,我们主要是用webpack来处理我们写的js代码,并且webpack会自动处理js之间相关的依赖。但是,在开发中我们不仅仅有基本的js代码处理,我们也需要加载css、...
  • webpack之loader详解

    2021-08-06 22:31:01
    webpack是一款强大的模块打包工具,它可以引入配置文件完成前端高度定制化的构建工作. webpack默认只能理解JavaScript和JSON文件,但实际工作中各种需求层出不穷,文件类型也多种多样.比如.vue、.ts、图片、.css等,这就...
  • webpack配置之后端渲染2017年, vue, react, angular 已经占据前端的主流, 不得不承认这也是前端的未来发展方向. 但是后端渲染的开发方式仍然很常见, 不管是个人项目还是商业项目, 后端渲染搞起来真是糙猛快. 但是借...
  • devServer 配置的本质是 webpack-dev-server 这个包提供的功能,而 webpack-dev-middleware 则是这个包的底层依赖。 截至本文发表前,webpack-dev-middleware 的最新版本为 webpack-dev-middleware@3.7.2,本文的...
  • webpack使用详解(二)

    2020-12-17 22:18:26
    webpack历史 前端工具的极速发展 grunt打包工具,速度太慢,快死了 gulp打包工具,速度可以,但没webpack繁荣 require.js 快死了 sea.js 死了 Browserify 已经挂了 与webpack竞争的工具 Rollup 比webpack的打包...
  • 最近要做一个js解析markdown的项目,所以当然想到了ant design,不过ant design内部又使用了atool-build脚手架,所以决定好好研究一下。...1.atool-build的简单说明该脚手架只是对webpack进行了简单的封装。首先,w
  • webpack配置(webpack.config.js) let path = require('path'); module.export = { mode: 'development', // 生产环境,开发环境 entry: { index: './src/index.js' // 入口 }, output: { filename: '...
  • 主要介绍了详解webpack loader和plugin编写,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • Gulp & webpack 配置详解

    2019-10-31 23:38:15
    1. Gulp VS webpack 比较 Gulp 是一个任务管理工具,让简单的任务更清晰,让复杂的任务易于掌控;而 webpack 的理念是,一切皆为模块,每个模块在打包的时候都会经过一个叫做 loader 的东西,它具备非常强大的精细...
  • webpack基础配置详解

    2021-03-29 10:56:40
    原理:分析项目结构,找到JavaScript模块以及其他浏览器不能直接运行的模块(Scss,TypeScript等),转换并打包为浏览器可以识别并运行的格式,让浏览器使用。 image.png 工作流程: 1、通过配置找到给定的...
  • 主要介绍了详解webpack import()动态加载模块踩坑,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • webpack 开启监听模式,有两种方式: 1.启动 webpack 命令时,带上 –watch 参数。  唯一缺点:需要手动刷新才能看到变化; 2.在配置 webpack.config.js 中设置 watch: true。  优点: (1) WDS 不刷新浏览器 (2)...
  • webpack核心概念详解及其运行原理

    千次阅读 2018-08-06 10:20:47
    * Entry: 入口, webpack执行构建的第一步将从Entry开始,可抽象成输入 * Module: 模块,在webpcak中一切皆模块,一个模块对应一个文件。webpack会从配置的Entry开始递归找出所有依赖的模块。 * Chunk: 代码块,一...
  • 官网的解释:是 webpack 的一个核心工具,但也可用于其他地方, 以提供类似的插件接口。 在 Webpack 中的许多对象都扩展自 Tapable 类。 它对外暴露了 tap ,tapAsync 和 tapPromise 等方法, 插件可以使用这些方法...
  • Webpack3.0项目打包视频教程详解各种webpack配置,熟悉webpack配置原理
  • 主要介绍了详解Webpack-dev-server的proxy用法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • 主要介绍了详解webpack与SPA实践之开发环境搭建,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • 主要介绍了详解vue-cli之webpack3构建全面提速优化,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • webpack详解

    2020-03-04 09:50:37
    npm i webpack --save-dev 开发环境下的依赖 运行cnpm run dev自动打开 //package.json文件 "scripts": { "dev": "webpack-dev-server --open --port 3000 --hot" } "scripts": { "test": "echo \"Error: no te....
  • 本篇文章主要介绍了webpack源码之loader机制详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • 主要给大家介绍了关于webpack2.0搭建前端项目的相关资料,文中介绍的非常详细,对大家学习或者使用webpack2.0具有一定的参考学习价值,需要的朋友们下面来一起看看吧。
  • 主要介绍了最新vue-cli 2.9.1的webpack存在问题,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 3,351
精华内容 1,340
关键字:

webpack原理详解