zoukankan      html  css  js  c++  java
  • 前端工程化4-webpack之loader和plugin

    写在前面

    弄清楚了tapable,我们基本上对webpack的plugin没有什么问题了,下面我们将webpack的plugin和loader组合起来看一下。我们主要从以下几个方面来深入研究。照着网上的资料写几个例子(模仿),然后大致了解loader是什么(作用),它是怎么执行的(原理)以及找一个比较有代表性的loader来看看它的代码(源码),最后我们可以自己写一个自定义的loader(学以致用)。plugin也可以套用这样的模式来进行学习。参考了官网给出的一些例子之后pluginloader之后
    ,我开了两个工程webpack-demo-loaderwebpack-demo-plugin来写一些示例来深入研究下webpack的loader和plugin

    loader

    loader即加载器,由于webpack只能打包js模块,如果要打包其他文件,如jsx、vue、css等文件,必须要将其转化成js模块形式。而这个转化的中间工具就是loader,我们可以理解为loader即文件的预处理。例如我们要打包css文件,我们就要使用css-loader和style-loader,要打包图片文件我们要使用file-loader或者url-loader。我们常用的loader有如下几个:
    style-loader、css-loader、vue-loader、babel-loader、ts-loader、file-loader、url-loader等

    loader使用

    loader的使用方法大概就是在webpack.config.js配置文件中的module的rules中添加项。具体的使用方法在官网的文档loader以及Loader Interface写的非常清楚,照着写几遍大概就能熟练使用了。我们主要看下Loader Interface这节,这里解释了loader的原理,对我们理解loader以及后面写自定义loader有很重大的意义。loader 本质上是导出为函数的 JavaScript 模块。loader runner 会调用此函数,然后将上一个 loader 产生的结果或者资源文件传入进去。loader也给出了很多钩子,都存在上下文对象this上的,这个上下文对外开放了很多方法,我们能很轻松的操作源码字符串。

    loader执行原理

    为了研究loader的执行原理,我们建了一个webpack应用实例来专门探索loader。webpack-demo-loader,通过上述的例子我们可以了解到如何在项目中配置使用loader。通过使用上述几个常用的loader我们可以大致窥探出webpack执行loader的工作的过程,大概的过程就是先注册,可以理解成数据结构--栈。

    vue-loader研究

    vue-loader是一个比较有代表性的loader,我们找到其源码来研究一下vue的模版语法。基本上就是基于webpack从0开始创建一个vue脚手架项目webpack-demo-vueloader
    创建该工程也比较简单,按照如下步骤即可

    • 创建目录并初始化
    mkdir webpack-demo-vueloader && cd webpack-demo-vueloader
    
    npm init -y 
    
    • 安装vue vue-loader vue-template-compiler
    • 安装 webpack webpack-cli webpack-dev-server(这三个包版本会相互影响,所以固定版本了)
    npm install --save vue vue-loader vue-template-compiler
    
    npm install --save webpack webpack-cli webpack-dev-server@3.11.0
    

    根目录新建webpack.config.js

    const VueLoaderPlugin = require('vue-loader/lib/plugin');
    
    module.exports = {
      entry: './src/index.js',
      mode: 'development',
      output: {
        filename: '[name].bundle.js',
        path: __dirname + '/dist'
      },
      module: {
        rules: [
          {
            test: /.vue$/,
            loader: 'vue-loader'
          }
        ]
      },
      devServer: {
        contentBase: __dirname + '/dist',
        host: '127.0.0.1',
        port: 8000
      },
      plugins: [
        new VueLoaderPlugin()
      ],
    }
    
    • 新建src目录,创建index.js和app.vue
    import Vue from 'vue';
    import App from './app.vue';
    
    new Vue({
      el: '#root',
      render: h => h(App)
    });
    
    
    <template>
      <div class="example">{{ msg }}</div>
    </template>
    
    <script>
    export default {
      data() {
        return {
          msg: "Hello world!",
        };
      },
      created: function () {
        console.log("create");
      },
      mounted: function () {
        console.log("mounted");
      },
    };
    </script>
    
    • 最后在package.json的script下添加执行命令就完成了一个最基本的vue脚手架
      "start": "webpack serve",
      "build": "webpack",
    

    测试项目可以完全跑起来了,我们下一步来研究一下vue-loader是如何工作的,从上面创建的工程中package.json中依赖的包我们可以看到,要让vue项目完整的在webpack中跑起来,最少要依赖三个包,分别是vue、vue-loader和vue-template-compiler。我们知道vue-loadervue-template-compiler是webpack和vue的中间连接接工具,查看源码我们得知,vue-template-compiler主要的作用是将vue的模版代码的字符串编译成ast和渲染函数的形式。vue-loader的作用在官网解释的很清楚,总结起来就是一句话,vue-loader的作用就是能将.vue文件转化成渲染函数的形式,可以让webpack完成打包。webpack只能打包js模块,在单页应用中,一个js文件可以理解为一个js模块,vue-loader的作用就是能将.vue文件编译成webpack能够识别的模块形式.

    写一个自定义的loader

    我们知道,loader能对js模块字符串的形式进行操作,那我们就写一个能够帮助我们清除console.log的loader。源码地址在webpack-demo-vueloader里的loaders/removeLog.js

    plugin

    上一节我们知道了webpack的plugin的核心是tapable,本质上是一个订阅-发布模式,先把插件写好注册到webpack.config.js文件中,在编译源代码的时候再调用它。我们呢常用的plugin有如下几个:
    webpack-bundle-analyzer、CommonsChunkPlugin、DllPlugin、ExtractTextWebpackPlugin、HtmlWebpackPlugin、HotModuleReplacementPlugin等

    plugin使用

    我们说loader就是一个js函数,可以操作js字符串形式的函数,那么plugin就是一个类。plugin使用非常简单,如果是引用第三方插件,也就是别人写好的npm包,只需在webpack.config.js配置一下即可,首先require进来,然后在plugins中new一个实例,如果有参数,直接传进去即可。示例(html-webpack-plugin)如下

    const HtmlWebpackPlugin = require('html-webpack-plugin')
    module.exports = {
      ...
      plugins: [
        new HtmlWebpackPlugin()
      ]
      ...
    }
    

    自定义的plugin,首先需要你先开发好plugin(参考tapable democompilercompilation),然后用同样的方式引入即可。
    plugin会深入到代码编译和构建的各个阶段,学习plugin可以帮助我们窥探代码编译的全过程。plugin对外提供了两个主要的钩子,一个是compiler,另一个是compilation

    • Compiler 对象包含了 Webpack 环境所有的的配置信息,包含 options,loaders,plugins 这些信息,这个对象在 Webpack 启动时候被实例化,它是全局唯一的,可以简单地把它理解为 Webpack 实例;
    • Compilation 对象包含了当前的模块资源、编译生成资源、变化的文件等。当 Webpack 以开发模式运行时,每当检测到一个文件变化,一次新的 Compilation 将被创建。Compilation 对象也提供了很多事件回调供插件做扩展。通过 Compilation 也能读取到 Compiler 对象。

    VueLoaderPlugin研究

    VueLoaderPlugin是有两个plugin分别兼容webpack的<=4、5两个版本。为什么会有兼容问题,查看文档发现webpack<=4 normalModuleLoader已经废弃,也就是说webpack<=4可以使用normalModuleLoader钩子来访问loader,webpack5就要使用NormalModule.getCompilationHooks(compilation).loader来访问loader。
    plugin-webpack4.js
    plugin-webpack5.js

    两个plugin的代码量都不多,我们就来读一下plugin-webpack5.js的代码,看下这个plugin究竟做了什么事。官网上也说了很清楚,vueLoaderPlugin的职责是将你定义过的其它规则复制并应用到.vue 文件里相应语言的块。例如,如果你有一条匹配 /.js$/ 的规则,那么它会应用到.vue 文件里的<script>块。一个vue文件包含template模版,script和style等3个部分,编译的时候需要将他们区分开,并且把loader应用到各个部分,因此需要这个plugin来做这件事。

    写一个自定义plugin

    在写自定义plugin之前,我们要了解webpack打包过程,大概有如下几个步骤

    • 读取配置
    • 生成compiler(编译器)对象
    • 初始化
    • run/watch
    • 生成compilation(构建包)
    • emit:文件内容准备完成,准备生成文件
    • afterEmit:文件已经写入磁盘完成
    • done: 完成编译
      在compilation生成之后,emit阶段之前我们都可以操作源码模块,compilation也提供了很多对外的钩子,以便开发者能更好的操作模块。
      弄清楚大概的打包过程之后,我们就知道怎么使用webpack plugin compiler钩子和compilation钩子的使用时机了。

    webpack-demo-plugin

    上面的demo我先把构建之后的包输出到一个json文件中,以便我们查看构建之后的模块是什么结构还有里面有什么内容。通过构建完成之后的包可以发现我们的js代码已经完全转化成ast的形式,我们可以在astexplorer网站上测试我们的js代码。

    参考

  • 相关阅读:
    hdu 4622 Reincarnation 字符串hash 模板题
    NYOJ 994 海盗分金 逆向递推
    hdu 4679 Terrorist’s destroy 树形DP
    Educational Codeforces Round 12 E. Beautiful Subarrays 预处理+二叉树优化
    hdu 5535 Cake 构造+记忆化搜索
    poj 3415 Common Substrings 后缀数组+单调栈
    poj 3518 Corporate Identity 后缀数组->多字符串最长相同连续子串
    poj 2774 Long Long Message 后缀数组LCP理解
    hdu 3518 Boring counting 后缀数组LCP
    poj 3641 Pseudoprime numbers Miller_Rabin测素裸题
  • 原文地址:https://www.cnblogs.com/xingguozhiming/p/15424014.html
Copyright © 2011-2022 走看看