要解决的问题
如何用更高阶的编程语言来驱动机器减少工作量
解决方案
用高阶编程语言编写代码,然后转换成机器直接支持的代码,从而允许利用机器不直接支持的抽象模式来减少工作量
构成
| 构成 | 解释 |
|---|---|
| source | 源代码 |
| object | 目标文件,需要静态链接才能变成executable |
| executable | 可执行文件 |
| compiler | 编译器,把 source 编译成 object,或者一步到位变成 executable |
解决方案案例
tsc
typescript 提供了两类编程上的便利
- 给 javascript 增加了类型
- 把高版本的 javascript 编译为低版本的 javascript 以兼容更多的 executor
source.ts
let messages: string[] = ['hello', 'world']
for (let elem of messages) {
console.log(elem)
}
build.sh
# --target ES3 把高版本的 emcascript 编译为最初版的 javascript
tsc --target ES3 --outFile executable.js source.tsexecutable.js
// /opt/my_pkg/source.ts
var messages = ['hello', 'world'];
for (var _i = 0, messages_1 = messages; _i < messages_1.length; _i++) {
var elem = messages_1[_i];
console.log(elem);
}
可以看到类型信息没有了, for of 的语法被编译成了等价的普通的 for 循环
| 构成 | 对应 |
|---|---|
| source | source.ts |
| executable | executable.js |
| compiler | tsc |
| executor | node.js 或者浏览器 |
rustc
rust 提供了更灵活和安全的类型系统来辅助描述驱动机器的逻辑
hello.rs
fn main() {
let msgs = ["Hello", "World"];
for msg in &msgs {
println!("{}", msg);
}
}build.sh
rustc hello.rs
./hello
# Output:
# Hello
# World
编译出来的是二进制的可执行文件,运行在CPU上
| 构成 | 对应 |
|---|---|
| source | hello.rs |
| executable | ./hello |
| compiler | rustc |
| executor | CPU |
vue-loader
webpack/vue-loader/vue-template-compiler 组合成了一个完整的编译器。 它可以把一个 .vue 单文件组件编译成 javascript 写成的 render 函数。 .vue 单文件组件,可以使用 vue 的模板语法,比 javascript 渲染 dom 的写法更可读。
hello.vue
<template>
<div>
<ul>
<li v-for="item in items">{{ item }}</li>
</ul>
</div>
</template>
<script>
module.exports = {
props: ['items']
}
</script>webpack.config.js
// webpack.config.js
const VueLoaderPlugin = require('vue-loader/lib/plugin')
module.exports = {
mode: 'development',
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader'
}
]
},
plugins: [
// 请确保引入这个插件来施展魔法
new VueLoaderPlugin()
],
externals: {
vue: 'vue'
},
output: {
library: 'hello',
libraryTarget: 'var'
}
}build.sh
webpack-cli --mode development src/hello.vue --output dist/hello.js编译出来的 hello.js 文件是这个样子的。它全局定义了 hello 这个变量。
hello.js
var hello =
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/ }
/******/ };
/******/
/******/ // define __esModule on exports
/******/ __webpack_require__.r = function(exports) {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/
/******/ // create a fake namespace object
/******/ // mode & 1: value is a module id, require it
/******/ // mode & 2: merge all properties of value into the ns
/******/ // mode & 4: return value when already ns object
/******/ // mode & 8|1: behave like require
/******/ __webpack_require__.t = function(value, mode) {
/******/ if(mode & 1) value = __webpack_require__(value);
/******/ if(mode & 8) return value;
/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/ var ns = Object.create(null);
/******/ __webpack_require__.r(ns);
/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/ return ns;
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = "./src/hello.vue");
/******/ })
/************************************************************************/
/******/ ({
/***/ "../../node_modules/vue-loader/lib/index.js?!./src/hello.vue?vue&type=script&lang=js&":
/*!*********************************************************************************************************************************!*\
!*** /home/xiaoju/workspace/toolchain/node_modules/vue-loader/lib??vue-loader-options!./src/hello.vue?vue&type=script&lang=js& ***!
\*********************************************************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {
eval("//\n//\n//\n//\n//\n//\n//\n\nmodule.exports = {\n props: ['items']\n}\n\n\n//# sourceURL=webpack://hello/./src/hello.vue?/home/xiaoju/workspace/toolchain/node_modules/vue-loader/lib??vue-loader-options");
/***/ }),
/***/ "../../node_modules/vue-loader/lib/loaders/templateLoader.js?!../../node_modules/vue-loader/lib/index.js?!./src/hello.vue?vue&type=template&id=be4ac86e&":
/*!**************************************************************************************************************************************************************************************************************************************************!*\
!*** /home/xiaoju/workspace/toolchain/node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!/home/xiaoju/workspace/toolchain/node_modules/vue-loader/lib??vue-loader-options!./src/hello.vue?vue&type=template&id=be4ac86e& ***!
\**************************************************************************************************************************************************************************************************************************************************/
/*! exports provided: render, staticRenderFns */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"render\", function() { return render; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"staticRenderFns\", function() { return staticRenderFns; });\nvar render = function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n return _c(\"div\", [\n _c(\n \"ul\",\n _vm._l(_vm.items, function(item) {\n return _c(\"li\", [_vm._v(_vm._s(item))])\n }),\n 0\n )\n ])\n}\nvar staticRenderFns = []\nrender._withStripped = true\n\n\n\n//# sourceURL=webpack://hello/./src/hello.vue?/home/xiaoju/workspace/toolchain/node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!/home/xiaoju/workspace/toolchain/node_modules/vue-loader/lib??vue-loader-options");
/***/ }),
/***/ "../../node_modules/vue-loader/lib/runtime/componentNormalizer.js":
/*!***************************************************************************************************!*\
!*** /home/xiaoju/workspace/toolchain/node_modules/vue-loader/lib/runtime/componentNormalizer.js ***!
\***************************************************************************************************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return normalizeComponent; });\n/* globals __VUE_SSR_CONTEXT__ */\n\n// IMPORTANT: Do NOT use ES2015 features in this file (except for modules).\n// This module is a runtime utility for cleaner component module output and will\n// be included in the final webpack user bundle.\n\nfunction normalizeComponent (\n scriptExports,\n render,\n staticRenderFns,\n functionalTemplate,\n injectStyles,\n scopeId,\n moduleIdentifier, /* server only */\n shadowMode /* vue-cli only */\n) {\n // Vue.extend constructor export interop\n var options = typeof scriptExports === 'function'\n ? scriptExports.options\n : scriptExports\n\n // render functions\n if (render) {\n options.render = render\n options.staticRenderFns = staticRenderFns\n options._compiled = true\n }\n\n // functional template\n if (functionalTemplate) {\n options.functional = true\n }\n\n // scopedId\n if (scopeId) {\n options._scopeId = 'data-v-' + scopeId\n }\n\n var hook\n if (moduleIdentifier) { // server build\n hook = function (context) {\n // 2.3 injection\n context =\n context || // cached call\n (this.$vnode && this.$vnode.ssrContext) || // stateful\n (this.parent && this.parent.$vnode && this.parent.$vnode.ssrContext) // functional\n // 2.2 with runInNewContext: true\n if (!context && typeof __VUE_SSR_CONTEXT__ !== 'undefined') {\n context = __VUE_SSR_CONTEXT__\n }\n // inject component styles\n if (injectStyles) {\n injectStyles.call(this, context)\n }\n // register component module identifier for async chunk inferrence\n if (context && context._registeredComponents) {\n context._registeredComponents.add(moduleIdentifier)\n }\n }\n // used by ssr in case component is cached and beforeCreate\n // never gets called\n options._ssrRegister = hook\n } else if (injectStyles) {\n hook = shadowMode\n ? function () { injectStyles.call(this, this.$root.$options.shadowRoot) }\n : injectStyles\n }\n\n if (hook) {\n if (options.functional) {\n // for template-only hot-reload because in that case the render fn doesn't\n // go through the normalizer\n options._injectStyles = hook\n // register for functioal component in vue file\n var originalRender = options.render\n options.render = function renderWithStyleInjection (h, context) {\n hook.call(context)\n return originalRender(h, context)\n }\n } else {\n // inject component registration as beforeCreate hook\n var existing = options.beforeCreate\n options.beforeCreate = existing\n ? [].concat(existing, hook)\n : [hook]\n }\n }\n\n return {\n exports: scriptExports,\n options: options\n }\n}\n\n\n//# sourceURL=webpack://hello//home/xiaoju/workspace/toolchain/node_modules/vue-loader/lib/runtime/componentNormalizer.js?");
/***/ }),
/***/ "./src/hello.vue":
/*!***********************!*\
!*** ./src/hello.vue ***!
\***********************/
/*! no static exports found */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _hello_vue_vue_type_template_id_be4ac86e___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./hello.vue?vue&type=template&id=be4ac86e& */ \"./src/hello.vue?vue&type=template&id=be4ac86e&\");\n/* harmony import */ var _hello_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./hello.vue?vue&type=script&lang=js& */ \"./src/hello.vue?vue&type=script&lang=js&\");\n/* harmony reexport (unknown) */ for(var __WEBPACK_IMPORT_KEY__ in _hello_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_1__) if(__WEBPACK_IMPORT_KEY__ !== 'default') (function(key) { __webpack_require__.d(__webpack_exports__, key, function() { return _hello_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_1__[key]; }) }(__WEBPACK_IMPORT_KEY__));\n/* harmony import */ var _node_modules_vue_loader_lib_runtime_componentNormalizer_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js */ \"../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\");\n\n\n\n\n\n/* normalize component */\n\nvar component = Object(_node_modules_vue_loader_lib_runtime_componentNormalizer_js__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(\n _hello_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_1__[\"default\"],\n _hello_vue_vue_type_template_id_be4ac86e___WEBPACK_IMPORTED_MODULE_0__[\"render\"],\n _hello_vue_vue_type_template_id_be4ac86e___WEBPACK_IMPORTED_MODULE_0__[\"staticRenderFns\"],\n false,\n null,\n null,\n null\n \n)\n\n/* hot reload */\nif (false) { var api; }\ncomponent.options.__file = \"src/hello.vue\"\n/* harmony default export */ __webpack_exports__[\"default\"] = (component.exports);\n\n//# sourceURL=webpack://hello/./src/hello.vue?");
/***/ }),
/***/ "./src/hello.vue?vue&type=script&lang=js&":
/*!************************************************!*\
!*** ./src/hello.vue?vue&type=script&lang=js& ***!
\************************************************/
/*! no static exports found */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _node_modules_vue_loader_lib_index_js_vue_loader_options_hello_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! -!../../../node_modules/vue-loader/lib??vue-loader-options!./hello.vue?vue&type=script&lang=js& */ \"../../node_modules/vue-loader/lib/index.js?!./src/hello.vue?vue&type=script&lang=js&\");\n/* harmony import */ var _node_modules_vue_loader_lib_index_js_vue_loader_options_hello_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_vue_loader_lib_index_js_vue_loader_options_hello_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_0__);\n/* harmony reexport (unknown) */ for(var __WEBPACK_IMPORT_KEY__ in _node_modules_vue_loader_lib_index_js_vue_loader_options_hello_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_0__) if(__WEBPACK_IMPORT_KEY__ !== 'default') (function(key) { __webpack_require__.d(__webpack_exports__, key, function() { return _node_modules_vue_loader_lib_index_js_vue_loader_options_hello_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_0__[key]; }) }(__WEBPACK_IMPORT_KEY__));\n /* harmony default export */ __webpack_exports__[\"default\"] = (_node_modules_vue_loader_lib_index_js_vue_loader_options_hello_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_0___default.a); \n\n//# sourceURL=webpack://hello/./src/hello.vue?");
/***/ }),
/***/ "./src/hello.vue?vue&type=template&id=be4ac86e&":
/*!******************************************************!*\
!*** ./src/hello.vue?vue&type=template&id=be4ac86e& ***!
\******************************************************/
/*! exports provided: render, staticRenderFns */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_vue_loader_lib_index_js_vue_loader_options_hello_vue_vue_type_template_id_be4ac86e___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! -!../../../node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!../../../node_modules/vue-loader/lib??vue-loader-options!./hello.vue?vue&type=template&id=be4ac86e& */ \"../../node_modules/vue-loader/lib/loaders/templateLoader.js?!../../node_modules/vue-loader/lib/index.js?!./src/hello.vue?vue&type=template&id=be4ac86e&\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"render\", function() { return _node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_vue_loader_lib_index_js_vue_loader_options_hello_vue_vue_type_template_id_be4ac86e___WEBPACK_IMPORTED_MODULE_0__[\"render\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"staticRenderFns\", function() { return _node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_vue_loader_lib_index_js_vue_loader_options_hello_vue_vue_type_template_id_be4ac86e___WEBPACK_IMPORTED_MODULE_0__[\"staticRenderFns\"]; });\n\n\n\n//# sourceURL=webpack://hello/./src/hello.vue?");
/***/ })
/******/ });其中 eval 的关键源代码,就是把 vue 模板编译出来的 javascript 代码
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "render", function() { return render; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "staticRenderFns", function() { return staticRenderFns; });
var render = function() {
var _vm = this
var _h = _vm.$createElement
var _c = _vm._self._c || _h
return _c("div", [
_c(
"ul",
_vm._l(_vm.items, function(item) {
return _c("li", [_vm._v(_vm._s(item))])
}),
0
)
])
}
var staticRenderFns = []
render._withStripped = true
使用这个编译好的 hello.js 文件
index.html
<html>
<head>
<script src="https://unpkg.com/vue@2.6.8/dist/vue.js"></script>
<script src="dist/hello.js"></script>
</head>
<body>
<div id="app">
<hello :items="['a', 'b', 'c']"></hello>
</div>
<script>
new Vue({
components: {
hello: hello.default
},
el: '#app'
})
</script>
</body>
</html>rollup-plugin-vue
rollup/rollup-plugin-vue/vue-template-compiler 组合成了一个完整的编译器。 它可以把一个 .vue 单文件组件编译成 javascript 写成的 render 函数。 .vue 单文件组件,可以使用 vue 的模板语法,比 javascript 渲染 dom 的写法更可读。
hello.vue
<template>
<div>
<ul>
<li v-for="item in items">{{ item }}</li>
</ul>
</div>
</template>
<script>
export default {
props: ['items']
}
</script>rollup.config.js
import vue from 'rollup-plugin-vue'
import commonjs from 'rollup-plugin-commonjs';
export default {
input: 'src/hello.vue',
output: {
format: 'esm',
file: 'dist/hello.js'
},
plugins: [
commonjs(),
vue()
]
}build.sh
rollup -c编译出来的 hello.js 文件是这个样子的。它是一个 es6 的 module
hello.js
//
//
//
//
//
//
//
var script = {
props: ['items']
};
function normalizeComponent(template, style, script, scopeId, isFunctionalTemplate, moduleIdentifier
/* server only */
, shadowMode, createInjector, createInjectorSSR, createInjectorShadow) {
if (typeof shadowMode !== 'boolean') {
createInjectorSSR = createInjector;
createInjector = shadowMode;
shadowMode = false;
} // Vue.extend constructor export interop.
var options = typeof script === 'function' ? script.options : script; // render functions
if (template && template.render) {
options.render = template.render;
options.staticRenderFns = template.staticRenderFns;
options._compiled = true; // functional template
if (isFunctionalTemplate) {
options.functional = true;
}
} // scopedId
if (scopeId) {
options._scopeId = scopeId;
}
var hook;
if (moduleIdentifier) {
// server build
hook = function hook(context) {
// 2.3 injection
context = context || // cached call
this.$vnode && this.$vnode.ssrContext || // stateful
this.parent && this.parent.$vnode && this.parent.$vnode.ssrContext; // functional
// 2.2 with runInNewContext: true
if (!context && typeof __VUE_SSR_CONTEXT__ !== 'undefined') {
context = __VUE_SSR_CONTEXT__;
} // inject component styles
if (style) {
style.call(this, createInjectorSSR(context));
} // register component module identifier for async chunk inference
if (context && context._registeredComponents) {
context._registeredComponents.add(moduleIdentifier);
}
}; // used by ssr in case component is cached and beforeCreate
// never gets called
options._ssrRegister = hook;
} else if (style) {
hook = shadowMode ? function () {
style.call(this, createInjectorShadow(this.$root.$options.shadowRoot));
} : function (context) {
style.call(this, createInjector(context));
};
}
if (hook) {
if (options.functional) {
// register for functional component in vue file
var originalRender = options.render;
options.render = function renderWithStyleInjection(h, context) {
hook.call(context);
return originalRender(h, context);
};
} else {
// inject component registration as beforeCreate hook
var existing = options.beforeCreate;
options.beforeCreate = existing ? [].concat(existing, hook) : [hook];
}
}
return script;
}
var normalizeComponent_1 = normalizeComponent;
/* script */
const __vue_script__ = script;
/* template */
var __vue_render__ = function() {
var _vm = this;
var _h = _vm.$createElement;
var _c = _vm._self._c || _h;
return _c("div", [
_c(
"ul",
_vm._l(_vm.items, function(item) {
return _c("li", [_vm._v(_vm._s(item))])
}),
0
)
])
};
var __vue_staticRenderFns__ = [];
__vue_render__._withStripped = true;
/* style */
const __vue_inject_styles__ = undefined;
/* scoped */
const __vue_scope_id__ = undefined;
/* module identifier */
const __vue_module_identifier__ = undefined;
/* functional template */
const __vue_is_functional_template__ = false;
/* style inject */
/* style inject SSR */
var hello = normalizeComponent_1(
{ render: __vue_render__, staticRenderFns: __vue_staticRenderFns__ },
__vue_inject_styles__,
__vue_script__,
__vue_scope_id__,
__vue_is_functional_template__,
__vue_module_identifier__,
undefined,
undefined
);
export default hello;
通过 package.json 发布出去,可以被做为一个 es6 模块引用
package.json
{
"private": true,
"name": "@compiler/rollup-plugin-vue",
"version": "1.0.0",
"scripts": {
"build": "./build.sh"
},
"module": "dist/hello.js",
"dependencies": {
"rollup": "^1.6.0"
},
"devDependencies": {
"rollup-plugin-commonjs": "^9.2.1",
"rollup-plugin-vue": "^4.7.2"
}
}
通过 es6 模块引用编译好的 .vue 文件
App.vue
<template>
<div>
<hello :items="['a','b','c']"></hello>
</div>
</template>
<script>
import hello from '@compiler/rollup-plugin-vue'
export default {
components: {hello}
}
</script>