# Koa

// 封装
const http = require('http')

class Application {
  constructor() {
    this.middlewares = []
  }

  listen(...args) {
    const server = http.createServer(async (req, res) => {
      const ctx = new Context(req, res)

      await runMiddlewares(this.middlewares, ctx)

      ctx.res.end(ctx.body)
    })
    server.listen(...args)
  }

  use(middleware) {
    this.middlewares.push(middleware)
  }
}

class Context {
  constructor(req, res) {
    this.req = req
    this.res = res
  }
}

async function runMiddlewares(middlewares, ctx) {
  const run = async (i) => {
    const middleware = middlewares[i]
    if (i === middlewares.length) {
      return
    }
    await middleware(ctx, () => run(i + 1))
  }
  await run(0)
}

module.exports = Application;
// 使用
const Application = require('./index')
const app = new Application()

app.use(async (ctx, next) => {
  console.log('Middleware 1 Start')
  await next()
  console.log('Middleware 1 End')
})

app.use(async (ctx, next) => {
  console.log('Middleware 2 Start')
  await next()
  console.log('Middleware 2 End')
})

app.listen(4000)

# mvvm

<body>
	<div id="app">
		<input type="text" v-modal="name">
		<div class="outer">
			<span>{{name}}</span>
			<p><span v-html="name"></span></p>
		</div>
		<button @click="reset">重置</button>
	</div>

	<script>
		class Wvue {
			constructor(option) {

				this.$option = option
				this.$data = option.data
				this.$methods = option.methods
				let that=this;
				Object.keys(option.data).forEach(function (key) {
					that.proxy(key);
				});
				//数据劫持
				this.observer(this.$data)
				//模板解析
				this.$compile = new Compile(option.el, this)
				
				
			}
			observer(obj) {
				if (!obj || typeof obj !== "object") {
					return;
				}
				Object.keys(obj).forEach(key => {
					this.defineProperty(obj, key, obj[key])
				})
			}
			defineProperty(obj, key, val) {
				this.observer(val)
				const dep = new Dep()
				Object.defineProperty(obj, key, {
					get() {
						// 每次访问name 都会创建一个watcher,并加入到Dep中
						Dep.target !== null && dep.addDep(Dep.target)
						return val
					},
					set(newVal) {

						val = newVal
						dep.notify()
					}
				})
			}
			proxy(key) {
				//使得读写vm的方法编程读写vm.data的
				var that = this;
				Object.defineProperty(that, key, {
					configurable: false,
					enumerable: true,
					get() {
						return that.$data[key];
					},
					set(newVal) {
						that.$data[key] = newVal;
					}
				});
			}
		}

		class Dep {
			constructor() {
				this.dep = []
			}
			addDep(dep) {
				this.dep.push(dep)
			}
			notify() {
				// 通知所有的watcher执行更新
				this.dep.forEach(watcher => {
					watcher.update()
				})
			}
		}

		class Watcher {
			constructor(vm, key, cb) {
				this.$vm = vm
				this.$key = key
				this.$cb = cb
				// 用一个全局变量来指代当前watch
				Dep.target = this
				// 实际是访问了this.name,触发了当前变量的get,
				// 当前变量的get会收集当前Dep.target指向的watcher,即当前watcher
				this.$vm.$data[this.$key]
				Dep.target = null

			}
			update() {
				// 执行
				// console.log(his.$vm[this.$key])
				this.$cb.call(this.$vm, this.$vm[this.$key])
			}
		}


		class Compile {
			constructor(el, vm) {
				this.$vm = vm
				// $el挂载的就是需要处理的DOM
				this.$el = document.querySelector(el)
				// 将真实的DOM元素拷贝一份作为文档片段,之后进行分析
				const fragment = this.node2Fragment(this.$el)
				// 解析文档片段
				this.compileNode(fragment)
				// 将文档片段加入到真实的DOM中去
				this.$el.appendChild(fragment)
			}
			node2Fragment(el) {
				// 创建空白文档片段
				const fragment = document.createDocumentFragment()
				let child
				//  appendChild会把原来的child给移动到新的文档中,当el.firstChild为空时,
				// while也会结束 a = undefined  => 返回 undefined
				while ((child = el.firstChild)) {
					fragment.appendChild(child);
				}
				return fragment
			}
			// 通过迭代循环来找出{{}}中的内容,v-xxx与@xxx的内容,并且单独处理
			compileNode(node) {
				const nodes = node.childNodes
				// 类数组的循环
				Array.from(nodes).forEach(node => {
					if (this.isElement(node)) {
						this.compileElement(node)
					} else if (this.isInterpolation(node)) {
						this.compileText(node)
					}
					node.childNodes.length > 0 && this.compileNode(node)
				});
			}
			isElement(node) {
				return node.nodeType === 1;
			}
			isInterpolation(node) {
				return node.nodeType === 3 && /\{\{(.*)\}\}/.test(node.textContent)
			}
			compileText(node) {
				const reg = /\{\{(.*?)\}\}/g
				const string = node.textContent.match(reg)
				// RegExp.$1是RegExp的一个属性,指的是与正则表达式匹配的第一个 子匹配(以括号为标志)字符串
				// 以此类推,RegExp.$2,RegExp.$3,..RegExp.$99总共可以有99个匹配
				this.text(node, RegExp.$1)
			}
			compileElement(node) {
				const nodeAttrs = node.attributes;
				Array.from(nodeAttrs).forEach(arr => {
					if (arr.name.indexOf('v-') > -1) {
						this[`${arr.name.substring(2)}`](node, arr.value)
					}
					if (arr.name.indexOf('@') > -1) {
						// console.log(node, arr.value)
						this.eventHandle(node, arr.name.substring(1), arr.value)
					}
				})
			}
			// 因为是大括号里面的内容,所以沿用之前的逻辑,都加上watcher
			text(node, key) {
				new Watcher(this.$vm, key, () => {
					node.textContent = this.$vm.$data[key]
				})
				// 第一次初始化界面, 不然如果不进行赋值操作,
				// 就不会触发watcher里面的回调函数
				node.textContent = this.$vm.$data[key]
			}
			html(node, key) {

				new Watcher(this.$vm, key, () => {
					node.innerHTML = this.$vm.$data[key]
				})
				node.innerHTML = this.$vm.$data[key]

			}
			// 对@xxx事件的处理
			eventHandle(node, eventName, methodName) {
				node.addEventListener(eventName, () => {
					this.$vm.$methods[methodName].call(this.$vm)

				})
			}
			// v-modal的处理 不仅仅当赋值的时候回触发watcher,并且为input添加事件
			// input中的值去修改this.$data.$xxx的值,实现双向绑定
			modal(node, key) {
				new Watcher(this.$vm, key, () => {
					node.value = this.$vm.$data[key]
				})
				node.value = this.$vm.$data[key]
				node.addEventListener('input', (e) => {
					this.$vm.$data[key] = e.target.value
				})
			}
		}




	</script>
	<script>
		const data = {
			el: '#app',
			data: {
				name: '米粒'
			},
			methods: {
				reset() {
					this.name = ''
				}
			},
		}
		const app = new Wvue(data)
	</script>
</body>

# rollup

// modules/myModule.js
export const name = 'jiahang'
export const age = 18
export const height = 180

// src/mian.js
import { name, age } from './modules/myModule'
function say() {
  console.log(`my name is ${name}`);
}
console.log(age);
say();

// 打包后
'use strict';

const name = 'jiahang';
const age = 18;
function say() {
  console.log(`my name is ${name}`);
}
console.log(age);
say();

实现

# vite-server

// 写一个node服务器,相当于devServer
const Koa = require("koa");
const app = new Koa();
const fs = require("fs");
const path = require("path");
const compilerSfc = require("@vue/compiler-sfc");
const compilerDom = require("@vue/compiler-dom");

// 返回用户首页
app.use(async (ctx) => {
  const { url, query } = ctx.request;
  if (url === "/") {
    // 首页
    ctx.type = "text/html";
    const p = path.join(__dirname, "./index.html");
    //mock process
    const content = fs.readFileSync(p, "utf8").replace(
      '<script type="module" src="/src/main.js"></script>',
      `<script>
        window.process = { env: { NODE_ENV: 'dev' } }
      </script>
      <script type="module" src="/src/main.js"></script>
      `
    );

    ctx.body = content;
  } else if (url.endsWith(".js")) {
    // 响应js请求
    const p = path.join(__dirname, url);
    console.log(p);
    ctx.type = "text/javascript";
    const file = rewriteImport(fs.readFileSync(p, "utf8"));
    ctx.body = file;
  } else if (url.startsWith("/@modules/")) {
    // 获取@modules后面部分,模块名称
    const moduleName = url.replace("/@modules/", "");
    const prefix = path.join(__dirname, "../node_modules", moduleName);
    // 要加载文件的地址
    const module = require(prefix + "/package.json").module;
    const filePath = path.join(prefix, module);
    const ret = fs.readFileSync(filePath, "utf8");
    ctx.type = "text/javascript";
    ctx.body = rewriteImport(ret);
  } else if (url.indexOf(".vue") > -1) {
    // 读取vue文件内容
    const p = path.join(__dirname, url.split("?")[0]);
    // compilerSfc解析SFC, 获得一个ast
    const ret = compilerSfc.parse(fs.readFileSync(p, "utf8"));

    // 没有query.type,说明是SFC
    if (!query.type) {
      // 处理内部script
      console.log(ret);
      // 获取脚本内容
      const scriptContent = ret.descriptor.script.content;
      // 转换默认导出配置对象为变量
      const script = scriptContent.replace(
        "export default ",
        "const __script = "
      );
      ctx.type = "text/javascript";
      ctx.body = `
      ${rewriteImport(script)}
      // template解析转换为另一个请求单独做
      import {render as __render} from '${url}?type=template'
      __script.render = __render
      export default __script
    `;
    } else if (query.type === "template") {
      const tpl = ret.descriptor.template.content;
      // 编译为包含render模块
      const render = compilerDom.compile(tpl, { mode: "module" }).code;
      ctx.type = "text/javascript";
      ctx.body = rewriteImport(render);
    }
  } else if (url.endsWith(".png")) {
    ctx.body = fs.readFileSync("src" + url);
  }
});

// 重写导入,变成相对地址
function rewriteImport(content) {
  return content.replace(/ from ['"](.*)['"]/g, function (s0, s1) {
    // s0匹配字符串,s1分组内容
    // 看看是不是相对地址
    if (s1.startsWith("./") || s1.startsWith("/") || s1.startsWith("../")) {
      // 原封不动的返回
      return s0;
    } else {
      // 裸模块
      return ` from '/@modules/${s1}'`;
    }
  });
}

app.listen(3001, () => {
  console.log("kvite start!");
});