乐趣区

关于javascript:从vueloader开始理解webpack的一些设计思想

写在后面

为了更好的阐明,咱们模拟 Vue.js 开发了一个相似的简化版本的前端框架 Quick Paper(文档) 来帮忙你了解一些细节。因而在开始之前,让咱们先大抵理解一下此我的项目的构造,不便后续形容。

舒适提醒:咱们举荐你在开始之前去 Github 上把此我的项目 clone 下来后,对照着源码进行学习!

目录构造

其实你只须要关注上面四个文件夹:

  1. src:框架源码;
  2. loader:相似 vue-loader,用来解析.paper 文件的 loader;
  3. style-loader:和下面的一样,只不过这个是用来解析款式文件的(包含.css 文件和.paper 文件中的 style 标签局部);
  4. loader-plug:一些辅助性能,比方校对 webpack 的一些配置。

框架源码

接着,咱们对源码 src 局部的目录构造再略微开展一下(因为咱们这里的重点不是源码局部,而是那些 loader 或 plug 是如何配置实现一系列解析工作的,因而源码局部就在上面简略的阐明就点到为止)。

  • core:框架对象的根底代码

    • global-api:给框架对象挂载的全局办法
    • instance:框架对象

      • index.js:框架对象运行入口
      • init.js:负责对象的初始化相干工作
      • lifecycle.js:负责对象的生命周期治理
      • render.js:对象的渲染启动等方面的工作
    • observe:监听数据扭转办法(被框架对象应用)
    • vnode:虚构 DOM 相干代码(被框架对象应用)
  • module:为框架对象扩大内置指令,组件等的中央
  • tools:一些工具办法,因为复用性和方便管理,集中写在一起
  • index.js:打包入口文件,也就是这份文件把所有的资源整合成一个残缺的框架

所以从下面的代码就可以看进去,文件 src/core/instance/index.js 是对象自身,从这个文件开始开即可!

如果有什么不分明的,能够去 issue 给咱们留言。

Loader 和执行程序

对于咱们用于学习的我的项目 Quick Paper 而言,咱们是把代码整合到文件.paper 中去,文件构造大抵如下:

<template>
    <!-- 页面的元素在这里 -->
</template>
<script>
    // 逻辑控制代码在这里
    export default { };
</script>
<style>
    /* 在这里编辑款式代码 */
</style>

你想,咱们应用 webpack 打包我的项目的时候,他是不可能意识.paper 文件的,当然就无奈晓得如何解析下面这份文件了,而开发一个 loader 用以解析下面的文件,就是这里要阐明的。

loader

在阐明 loader 之前,咱们先要看看咱们编辑的.paper 是如何被咱们应用的。因为如何应用就决定了咱们须要如何解析。

和 vue 相似,先假如咱们有一个 App.paper 文件:

import App from './App.paper';
new QuickPaper({render:createElement => createElement(App),
    // ...
});

因为 render 外面只记录了页面内容,可是.paper 文件外面可是记录了页面内容 + 逻辑管制 + 页面款式的。其余的内容怎么办?

// 导入 js [逻辑管制]
import script from './${filename}?QuickPaper&type=script&lang=js&hash=${id}&';

// 导入 css [页面款式]
import './${filename}?QuickPaper&type=style&lang=css&hash=${id}&';

script.render=${code};

// [页面内容]
export default script;

能够看进去,页面内容间接默认导出后给 render 配置项即可,别的内容因为新增了导入语句,会触发对应的 loader 进行解析,也就是说,这里其实能够分为两步:

  • 第一步:对于未思考到的内容执行新的导入语句,触发对应的 loader 解析
  • 第二步:导出 render 须要的内容

style-loader

比方页面款式局部的导入语句:

import './${filename}?QuickPaper&type=style&lang=css&hash=${id}&';

咱们是如何让 webpack 晓得这是一个款式文件,并且是应用 css 还是 scss 或别的 loader 来解析的,这属于插件须要阐明的局部。在此之前,咱们还须要先阐明一下款式 loader 的工作原理。

为什么款式 loader 比拟非凡?

依据返回值类型,能够把 loader 分成两种:一种是返回 js 代码(也就是一个模块的代码,有相似 module.export 语句)的 loader,一个是不能作为最右边 loader 的其余 loader(比方返回一个 CSS 字符串)。

咱们来看看咱们 webpack 外面是如何配置 css 的 loader 的:

{
    test: /\.css$/,
    loader: ['quick-paper/style-loader/index.js', 'css-loader', 'postcss-loader']
}

这里的重点是 css-loader,他属于第一种,返回 js 代码的 loader,对于咱们自定义的 ’quick-paper/style-loader/index.js’ 而言,如果让 loader 依照从右往左的程序执行,很难拿到真正的 css 代码。

执行程序(loader 和 picth)

在阐明如何解决上个问题前,咱们须要先阐明一下 loader 的 picth 和执行程序。

比方下面配置的三个 loader 而言,执行程序分为 Pitch 阶段和 Normal 阶段(能够了解为 loader 自身的行为):

  • Pitch 阶段:’quick-paper/style-loader/index.js’->’css-loader’->’postcss-loader’
  • Normal 阶段:’postcss-loader’->’css-loader’->’quick-paper/style-loader/index.js’

有一个特点是,在 Pitch 阶段,如果某个 loader 有返回值,就会进行后续执行。

舒适提醒:进行执行的意思是,在其左边的 loader,包含本人都执行结束了(Pitch 阶段和 Normal 阶段都完结了),返回的值会返回给前一个 loader(Normal 阶段)!

如何实现?

这里,咱们就能够借助给 ’quick-paper/style-loader/index.js’ 设置一个有返回值的 Pitch 来实现。

看看代码构造:

// quick-paper/style-loader/index.js

const loaderApi = () => {};
loaderApi.pitch = function (remainingRequest) {

    // request = ""!!../../node_modules/css-loader/dist/cjs.js!../../node_modules/postcss-loader/src/in...
    let request = loaderUtils.stringifyRequest(this, '!!' + remainingRequest)

    return `

        // 获取真正的 css 内容
        var content = require('+ request +');
        // 而后调用办法增加到页面中失效
        require('./addStylesClient.js')(content);
    `;
};
module.exports = loaderApi;

咱们在 ’quick-paper/style-loader/index.js’ 中定义了 Picth 办法,在此办法外面,返回了一个 js 字符串,我的项目运行的时候会运行这段字符串,这段字符串的意义就是调用款式 loader 获取真正的 css 后,运行 addStylesClient.js 办法使得在页面失效。

舒适提醒:对于 addStylesClient.js 办法请间接查看我的项目源码,很容易读懂,给款式增加 hash 值让 scope 失效,就是这个办法里。

插件的作用和一些技巧

咱们这里来解释一下,一个.paper 文件拆分当前,如何让对应的 loader 来进行解析。

插件的执行机会

首先须要了解,什么是插件?

你能够这样了解:如果说 loader 帮忙 webpack 取得解析更多类型文件,那插件就是一个打杂工,前者有专门的分工,后者是在非凡状况下帮忙,而不是针对某个文件。

比方你能够在每次打包前调用一个查看删除上次打包的后果,或者在打包失败的时候重置一些参数,或者是别的一些操作等。

如何实现?

那么,咱们这里须要插件干什么?

别忘了咱们的需要是(拿 css 举例子),如果遇到:

import './${filename}?QuickPaper&type=style&lang=css&hash=${id}&';

这样的导入语句,咱们工具 lang=css 发现是一个款式文件,应该交给专门解析 css 的 loader 解决,当然,咱们能够被动批改 webpack 的配置:

{
    test: /type=style&lang=css/,
    loader: ['quick-paper/style-loader/index.js', 'css-loader', 'postcss-loader']
}

可是,为了更简略,咱们能够通过插件,在每次打包前对 loader 配置进行批改(当然,也包含 js 等相干项),如此,便实现了。

退出移动版