原文地址:GitBook

最初设计这门 Webpack 课程的时候共包含 6 节课,这篇算是一个 bonus,来详细介绍一下在去哪儿网使用的构建工具 —— YKit。

YKit 的由来

在上一篇《在去哪儿网如何做大型项目工程化》中已经提到,我们的构建工具演进经历了大概 3 个阶段:

  1. 完全统一化的构建流程,所使用的工具是 FEkit。
  2. 放开约束的自定义构建,Webpack & Gulp & Grunt 共存。
  3. 约束与自由的平衡,经历过前两个阶段而出现了 YKit。

在一个大的公司环境下统一的构建工具是必须的。因为多套工具和流程的共存很容易出现重复造轮子的情形,并且会给开发者带来额外的学习成本。统一的工具可以使业务同学即使在面对一个新项目时也能较快地上手进行开发。

另一方面,允许构建方式的多样性是必须的。因为不同的业务线、不同类型的项目需要实现各自不同的构建需求,而随着前端框架以及开发方式的演进也会不断出现新的需求。这就要求构建工具必须具备极强的扩展性和定制性,否则将很快被淘汰。

YKit 就是基于这两点而产生的构建工具。

YKit 是什么?

如果采用正式的定义,YKit 是一个基于 Webpack 的打包工具,主要目的是帮助开发者封装配置来快速搭建项目环境。下面我来把 YKit 与一些现有的工具进行比较,希望能更清楚地介绍它的特性。

YKit 与 Webpack

在日常开发中,每个 JavaScript 应用其实都需要一个通用基础配置,而不幸的是很多时候这个基础配置都需要我们从零开始一点一点搭建起来。列举几个最常见的配置:

  • 资源输入输出目录
  • JavaScript ES6+ 的转译
  • SASS/LESS 等样式预处理器
  • 代码压缩

一个 Webpack 工程中的配置文件本身就有上百行是一个非常正常的现象,而最令人头疼的问题是管理项目中复杂的依赖。

比如 Webpack 的各种 loaders 和 plugins,它们有些对于 node 版本有要求、有的对于 Webpack 的版本有要求,每个 loader 和 plugin 还有属于自己的配置,任何一处微小的改动都有可能导致整个工程构建失败。当我们从头开始一个工程时候往往就要先花几个钟头在不停地调试项目的配置。

YKit 是基于 Webpack 的,但它采用了插件机制将配置封装起来进行管理。举个例子,假如我们的项目需要转译 SASS 代码,在使用 Webpack 的情况下可能会需要安装和配置如下模块:

  • sass-loader
  • node-sass
  • css-loader
  • style-loader
  • extract-text-webpack-plugin

这还只是刚开始,接下来你可能还会遇到如下问题:

  • node-sass 的二进制包默认放在 GitHub,这会使下载速度较慢,需要人工地去解决镜像源问题。
  • node-sass 不支持模块去重,最终打出来的样式可能会有很多冗余代码。
  • 需要手动地根据环境配置是否需要 source-map。比如只有开发环境加上 source-map,而生成环境要将其去掉。

而在一个 YKit 项目中如果要使用 SASS,我们只需安装一个 ykit-config-sass 的插件。具体的依赖安装和配置工作在插件内部都已经完成,并且包含了镜像源、代码去重和根据当前环境添加 source-map 等功能。

类似的,像是转译 ES6+ 的代码、Mock 服务等常用的配置都可以封装为 YKit 插件。当我们安装了相应的插件就相当于应用了该方案的最佳实践,而不必再走弯路。

YKit 与脚手架

为了解决项目配置过于复杂的问题,很多人选择使用现成的脚手架来开始一个应用。使用脚手架最大的好处在于可以快速把环境搭建起来,并且安装上一套现成的依赖模块。

虽然使用脚手架看上去很简洁和容易上手,但是也潜在着一个问题——你的应用中直接内置了几百行别人写的配置,而你完全不知道它们是怎么工作的。当我们尝试增加自己的需求或者做一些改动的时候,往往很难达到预期的效果,因为有可能会和现有的配置发生冲突。即便开始的时候看上去很轻松,但最后还是要了解脚手架内包含的配置,否则很难定位到问题。

与脚手架的处理方式不同,YKit 采用插件的形式来将复杂的配置进行抽象。与此同时也会通过接口将语义化过后的选项暴露给开发者,帮助其实现自身的需求。

比如在做 ES6+ 代码转译的时候,有些项目希望去掉 Babel 默认加上的 'use strict' 来不使用严格模式。如果要实现这一功能就需要开发者自己写一个 loader 并整合到项目中。而如果使用了 YKit 相关插件,则只要添加一项 removeStrict 的配置就好了。

// ykit.js
module.exports = {
    plugins: [{
        name: 'es6',
        options: {
            removeStrict: true
        }
    }],
    // ...
}

YKit 插件的职责更加单一,提供的配置互相之间耦合也比较松散,因此从扩展性和可维护性上来说都比脚手架要强。

YKit 设计思路

和现有方案对比过后,让我们来看一下 YKit 的设计思路。

YKit 不是一个黑盒,它允许开发者侵入开发和构建中的各个环节来实现定制化的需求。

在 YKit 中配置分为三级。最底层的是 YKit 的核心配置,其中只有一些基本的东西,比如公共的配置项和必备的依赖等。在中间层是 YKit 插件,这也是实现构建流程中各个功能的主要部分。最上层的是业务中的配置文件 ykit.js,它的优先级最高,可以获取到任何底层的配置项并进行修改。

三级配置充分体现了约束与自由的平衡。YKit 仅仅在最底层的核心部分进行了约束,而开发者可以在其基础上选择适用于自身项目的插件,并且再进一步更改各项配置。

如何使用 YKit?

首先通过 npm 全局安装 YKit:

[sudo] npm i ykit -g

安装之后你可以通过 ykit -h 查看所有当前的指令。

项目初始化

利用 YKit 可以通过一个命令快速生成项目基本结构,比如在一个空的目录下执行:

ykit init # 初始化项目结构
ykit server -p 3000 # 启动服务

访问 http://localhost:3000/index.html 即可看到效果。

你也可以通过指定项目类型来生成一个特定的项目,如:

ykit init react

这会帮你自动生成一个包含 react 配置的项目,其中包含了转译 JSX、设置 NODE_ENV 环境变量等功能。

添加 YKit 插件

插件命名格式均为 ykit-config-{插件名},通过以下命令在项目中安装插件:

npm install ykit-config-{插件名}

安装之后不会立马生效,还需要在项目的 ykit.js 配置文件中引入插件。

module.exports = {
    plugins: ['react', 'mock'],
    // ...
};

如果想知道目前有哪些插件,可以查看官方的插件列表,或者也可以在这里找到所有包含第三方提供的插件。

配置资源入口

在 YKit 中简化了 entry 和 output 的概念,而合并为了 config.exports 配置项,我们可以简单地把它理解成从项目 src 目录下开始的资源入口路径。

module.exports = {
    plugins: ['es6'],
    config: {
        // 资源入口
        exports: ['./scripts/app.js']
    }
};

YKit 默认的资源目标目录为 /prd,开发者不需要配置 Webpack 中的 output 配置项。比如当 ykit server 接收到 /prd/script/index.js 的请求的时候,会自动打包编译 /src/script/index.js 并返回结果。

更改 Webpack 配置

通过 config.modifyWebpackConfig,我们可以获取到底层的配置并对其进行更改。

module.exports = {
    plugins: ['es6'],
    config: {
        exports: ['./scripts/app.js'],
        modifyWebpackConfig: function(config) {
            // 添加一个 loader
            config.module.loaders.push({
                test: /\.mustache$/,
                loader: 'mustache'
            })
            return config;
        }
    }
};

这个配置项给了业务实际使用者非常大的扩展能力,因为它把整个 Webpack 配置对象暴露了出来。这个配置对象包含 YKit 底层的配置以及所有插件中的配置,而业务可以对所有这些配置进行更改。

获取更多文档

限于篇幅这里无法把 YKit 的所有功能介绍一遍,如需查看更详细的使用方法请到 YKit 官网

在去哪儿网的使用现状

目前公司内部大部分业务线都已经开始在使用 YKit 进行构建和发布,截止到目前上线的工程有 170 个左右。

其中不乏很多是从之前的构建工具迁移到 YKit 上来的,YKit 专门为这类业务提供了迁移的插件。里面对打包的过程进行了全面适配,帮助业务同学尽可能无痛切换。

另外,YKit 也在为满足各类业务的需求而不断提供新类型的插件。最常见的包括内置了 Babel 的 ykit-config-es6、用于 React 项目的 ykit-config-react 等。而在开发服务方面,也有 ykit-config-mock 等工具类插件。利用插件机制将重复性的构建工作都交给 YKit 来做,业务同学仍然可以去玩各种各样的技术方案,而开发体验仍是相对统一的。

另外值得一提的是,除了 Web 工程的构建以外,目前去哪儿网的微信小程序也是使用 YKit 进行构建的。YKit 有专门为开发小程序而量身打造的插件,它内置了工程模板初始化、编译小程序语法、npm 模块引入等功能。这表明了对于 YKit 来说即便是截然不同的开发环境也可以实现开发体验的相对统一,开发同学也不必在电脑中安装一大堆开发工具了。

未来展望

在以后我们还会着眼更多的场景,就像现在为小程序提供的脚手架和开发工具一样,将来可能会有更多这样的模式,使得搭建各种各样的开发环境都变得更加简单,让开发同学花更少的精力在构建上面而更加专注于研发本身。

现在 YKit 已经开源(Github 地址)。欢迎提出宝贵意见,一起为它添砖加瓦:)