你可能不需要 Vue

注意
本文假设你已具有以下内容的相关知识或者实践经验:

  • [vue 单文件组件]
  • [vue 渲染函数]
  • [jsx(Vue)]
  • [pug]
  • babel 及其相关插件

VAR

毫无疑问, 2016 ~ 2017 是 Vue 势头最强劲的两年. 根据笔者的记忆, 2017 年, Vue 的在 Github 上的 Star 数目首次超过 4W, 彼时 React 的 star 数目还在 3W 内, Angular v2/v4 还在 beta 版本. 三大框架互相学习, 尽管粉丝间掐架不少, 利益相关的布道者也上蹿下跳, 但 Vue 2 引入了 vdom, 在实现上, 摆脱源自 Angular 1.x 的模板概念; AngularVue 的作者之间还保持着互通有无的关系; React 升级后放弃了对 IE 8 的支持, 基于 S/P 模式的状态管理方案 mobx 在社区冉冉升起.

时间步入 2018 年, VAR 三大框架在各方面的差异此消彼长, 甚至不约而同地对 Typescript 做了官方的支持. 如果你在同一时间在项目中对三个框架都有实践, 难免会生出"万变不离其宗"的感慨: 说到底, 大家都玩起了预编译和[渲染纯函数].

进一步地说, [*.jsx(React)] , angular 的组件 和 [*.vue(sfc)], 最终都会转化为 runtime 中对 DOM 不同的操作函数. 这一点在 [*.jsx(React)] 中体现得淋漓尽致 —— 连 *.jsx 这种语法的诞生都是为了让 React 组件的 createElement/cloneElement 函数对开发者更友好; Vue 2 的单文件组件(sfc) 将其包装成了模板中的 v-* 指令、属性和事件绑定, 后来干脆也支持了 [*.jsx(Vue)]. 无论 *.jsx 还是 sfc, 这些框架的 DSL 语法, 都是为了尽可能地减少(乃至彻底清除)自家框架在运行时解析特定指令的耗时, 同时能让开发者有良好的开发体验.

*.vue

对于 vue 新手而言, 直接在浏览器里引入完整版本的 vue.js 然后在 中书写 vue 的 template 是不错的开始, 但这样会导致 vue 在运行时,

  1. 先花费一部分时间去解析这段模板, 生成 vue 实例上的[渲染纯函数]
  2. 运行这些[渲染纯函数], 处理 vue 实例中的 data 和 dom.

其中的第 1 步, 通过使用[单文件组件(sfc)]是完全可以节省的.

vue 的[单文件组件(sfc)] 是 xml 格式的文件, 经过 Vue 官方工具链的处理(如使用基于 vue-cli 的 webpack 的脚手架进行编译),后, 将变成可以在 Vue runtime 中运行的的一系列纯函数集. 详情可参考这里.

如果你还没安装 vue-cli 并生成一个使用 [单文件组件(sfc)] 来构建应用的脚手架, 你可以通过 [vue-template-explorer] 了解 vue template 和编译后的[渲染纯函数]的联系.

[单文件组件(sfc)] 的优势很多, 比如:

  • 通过 webpack/browserify 的配置, 可以安心地使用 typescript/es201X 的语法, 同时在编译后得到经过 polyfill 处理的、可以在 es5 环境下运行的代码
  • 使用 css 和 html 预编译器
  • 干净的组件内 scoped 特性.
  • 配合 webpack/browserify, 使用 nodejs 风格的模块管理.

但在此前, [单文件组件(sfc)] 有个缺陷, 即需要较为完整的 vue-cli 工具链的支持, 如果你只是想:

  • 写个简单的页面, 里面只有一个 ajax/fetch 请求, 根据返回结果, 在页面上渲染该结果
  • 使用 vue
  • 最好能够支持 jsx(Vue), 这样就不用
  • 能直接书写 es6/typescript 语法, 并且能预编译为 es5 兼容的 js
  • 还希望能够使用 less/stylus/scss/sass 来书写样式, 并预编译为合法的 css

此时最快的途径, 似乎是使用 vue-cli 初始化一个 webpack-simple 的项目, 然后修改 webpack 配置, 使得 src/index.js 在编译后被注入 index.html, 得到一个页面.....稍等, 这类似于有时候你只是想写个简单的窗体应用, 却不得不去下载一个带完整 MFC 的 Visual Studio 一样. 我真的想这样做么? 我很可能配置 webpack 到一半就放弃了, 转而新建一个 index.html 文件, 然后直接在 里书写 vue 组件的模板, 然后运行起来 —— 尽管这样会引入完整的 vue.js 的文件(> 100KB), 并且 vue 需要耗时去解析模板, 但是这样的开发效率, 非常高啊!

别的思路

即便是在 [vue-component-compiler] 发布以后, 社区依然缺乏直接在 index.html 中书写 vue 组件并且能进行预编译的方案.

那么, 如果我们只是想写个简单的页面(就像 jQuery 盛行的时代中我们经常做的那样), 我们非得安装 vue-cli, 再初始化项目, 再安装庞大的 webpack 及其周边依赖么?

也许 [parcel] 适合做这件事, 它允许你在 index.html 中直接引入相对路径下的 .js 文件, 并根据 [parcel] 的配置(默认配置通常已足够你使用)合适地处理其中所有的资源, 包括 .js 文件自身超前的语法(es201X) 和 css 预编译器.

不过, 截至笔者书写到这里(北京时间 2018-02-11 17:14) 的时候, 根据 腾讯 imweb 的尝试, [parcel] 还有以下缺点:

  • 不支持SourceMap:在开发模式下,Parcel也不会输出SourceMap,目前只能去调试可读性极低的代码;
  • 不支持剔除无效代码(TreeShaking):很多时候我们只用到了库中的一个函数,结果Parcel把整个库都打包了进来;
  • 一些依赖会让Parcel出错:当你的项目依赖了一些Npm上的模块时,有些Npm模块会让Parcel运行错误;

注意 实际上, 这些缺点在下文笔者介绍的方案中也存在. 相信以上问题 [parcel] 会在不久的未来解决.

也许我们可以尝试用 [parcel] 来写一个简单的页面, 但如果你希望去了解 [parcel] 内部处理资源的细节, 对自己的项目有更多的了解, 又不必太符合 [parcel] 的社区形象(开箱即用, 零配置). 此时, 似乎 webpack 又更吸引你(尽管它的配置相对繁冗, 但至少对开发者可见)

有没有什么细节对开发者更为透明, 可以完全自己 DIY、不必造太多轮子的方案呢?如果抛开平时习惯 webpack/browserify 下的思维惯性, 整理一下, 其实真正帮助我们提高了效率的库是:

  • babel/bubble
  • JSX
  • pugjs/ejs/swig
  • stylus/less/scss/sass

回到本文的初衷: "我只是要写个简单的 html 页面, 里面需要运行一些 js 来操作 DOM/BOM, 如果可能, 我希望利用 vue runtime". 为了满足这个需求, 笔者认为, 至少要解决以下两个问题:

  •   支持 js/css/html 预编译器
  •   以 html 为入口, 直接在 html 中书写超前的的预编译器语法

预编译器处理的一种接口标准: jstransformer

[jstransformers] 是一系列 jstransformer-* 的统称, [jstransformer] 的目的, 是统一 js(一般指nodejs) 社区中各类预编译器库的 API, 将不同思路、不同风格、不同目的, 但都基于 js、处理 js 或其它资源(比如 less/typescript)的库进行接口标准化. 参看其 github 上的 README:

There are many good template engines and compilers written for Node.js. But there is a problem: all of them have slightly different APIs, requiring slightly different usage. JSTransformer unifies them into one standardized API. Code written for one transformer will work with any other transformer.

(Node.js 社区中)有非常多好的模板引擎和模板编译器. 但是存在这样一个问题: 它们的 API 有些微差异, 用法有细微的不同. JSTransformer 将它们统一为标准的 API. 一个 transformer 应该能与另一个 transformer (以同样的 API)进行协作.

[jstransformers] 要处理的目标很明确: 预编译器, 比如下表中的这些预编译器

预编译器种类 典型库
html 预编译器
  • pug/jade
  • ejs
  • juice
  • swig
css 预编译器
  • sass/scss
  • stylus
  • less
  • postcss
js 预编译器
  • coffeescript
  • babel
  • typescript

[jstransformers] 让笔者想到 [webpack-loader] 概念. 尽管 [webpack-loader] 集成给 webpack 项目的类库不只是预编译器, 但在只讨论"如何在项目中使用预编译器"的语境下, [jstransformers] 可以类比于 [webpack-loader].

现在我们的第一个问题解决了

  •   支持 js/css/html 预编译器
  •   以 html 为入口, 直接在 html 中书写超前的的 js 语法(es201X)

直接在 HTML 中写预编译器语法

all-in-js ·PK· html-first

在考虑第二个问题之前, 先讨论一下目前社区对 html/css/js 前端三大基石的一种态度: all-in-js

前两年, React 社区有一种 all-in-js 的思路, 即 html 提供一个页面载入的入口, 把所有的资源(包括所有的 css 一些体积可接受的图片)打包到 js 中, 这种思路倾向于:

  • 如果不必要, css 可以完全不在 html 中书写(使用