diff --git a/example/HelloWorld/App.js b/example/HelloWorld/App.js new file mode 100644 index 0000000..f8bce19 --- /dev/null +++ b/example/HelloWorld/App.js @@ -0,0 +1,12 @@ +import { h } from '../../lib/guide-mini-vue.esm.js' + +export const App = { + render() { + return h('div', 'hi, ' + this.msg) + }, + setup() { + return { + msg: 'mini-vue' + } + } +} \ No newline at end of file diff --git a/example/HelloWorld/index.html b/example/HelloWorld/index.html new file mode 100644 index 0000000..4b94f5f --- /dev/null +++ b/example/HelloWorld/index.html @@ -0,0 +1,16 @@ + + + + + + + + Document + + + +
+ + + + \ No newline at end of file diff --git a/example/HelloWorld/main.js b/example/HelloWorld/main.js new file mode 100644 index 0000000..8acf9c1 --- /dev/null +++ b/example/HelloWorld/main.js @@ -0,0 +1,6 @@ + +import { createApp } from '../../lib/guide-mini-vue.esm.js' +import { App } from './App.js' + +const rootContainer = document.querySelector('#app') +createApp(App).mount(rootContainer) \ No newline at end of file diff --git a/lib/guide-mini-vue.cjs.js b/lib/guide-mini-vue.cjs.js new file mode 100644 index 0000000..0abbb0f --- /dev/null +++ b/lib/guide-mini-vue.cjs.js @@ -0,0 +1,80 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { value: true }); + +function createComponentInstance(vnode) { + const component = { + vnode, + type: vnode.type, + }; + return component; +} +function setupComponent(instance) { + // initProps() + // initSlots() + setupStatefulComponent(instance); +} +function setupStatefulComponent(instance) { + const Component = instance.type; + const { setup } = Component; + if (setup) { + const setupResult = setup(); + handleSetupResult(instance, setupResult); + } +} +function handleSetupResult(instance, setupResult) { + if (typeof setupResult === "object") { + instance.setupState = setupResult; + } + finishSetupComponent(instance); +} +function finishSetupComponent(instance) { + const Component = instance.type; + if (Component.render) { + instance.render = Component.render; + } +} + +function render(vnode, container) { + patch(vnode); +} +function patch(vnode, container) { + processComponent(vnode); +} +function processComponent(vnode, container) { + mountComponent(vnode); +} +function mountComponent(vnode, container) { + const instance = createComponentInstance(vnode); + setupComponent(instance); + setupRenderEffect(instance); +} +function setupRenderEffect(instance, container) { + const subTree = instance.render(); + patch(subTree); +} + +function createVNode(type, props, children) { + const vnode = { + type, + props, + children, + }; + return vnode; +} + +function createApp(rootComponent) { + return { + mount(rootContainer) { + const vnode = createVNode(rootComponent); + render(vnode); + }, + }; +} + +function h(type, props, children) { + return createVNode(type, props, children); +} + +exports.createApp = createApp; +exports.h = h; diff --git a/lib/guide-mini-vue.esm.js b/lib/guide-mini-vue.esm.js new file mode 100644 index 0000000..68cfe72 --- /dev/null +++ b/lib/guide-mini-vue.esm.js @@ -0,0 +1,75 @@ +function createComponentInstance(vnode) { + const component = { + vnode, + type: vnode.type, + }; + return component; +} +function setupComponent(instance) { + // initProps() + // initSlots() + setupStatefulComponent(instance); +} +function setupStatefulComponent(instance) { + const Component = instance.type; + const { setup } = Component; + if (setup) { + const setupResult = setup(); + handleSetupResult(instance, setupResult); + } +} +function handleSetupResult(instance, setupResult) { + if (typeof setupResult === "object") { + instance.setupState = setupResult; + } + finishSetupComponent(instance); +} +function finishSetupComponent(instance) { + const Component = instance.type; + if (Component.render) { + instance.render = Component.render; + } +} + +function render(vnode, container) { + patch(vnode); +} +function patch(vnode, container) { + processComponent(vnode); +} +function processComponent(vnode, container) { + mountComponent(vnode); +} +function mountComponent(vnode, container) { + const instance = createComponentInstance(vnode); + setupComponent(instance); + setupRenderEffect(instance); +} +function setupRenderEffect(instance, container) { + const subTree = instance.render(); + patch(subTree); +} + +function createVNode(type, props, children) { + const vnode = { + type, + props, + children, + }; + return vnode; +} + +function createApp(rootComponent) { + return { + mount(rootContainer) { + const vnode = createVNode(rootComponent); + render(vnode); + }, + }; +} + +function h(type, props, children) { + return createVNode(type, props, children); +} + +export { createApp, h }; diff --git a/package.json b/package.json index edc495f..3e04c53 100644 --- a/package.json +++ b/package.json @@ -1,19 +1,24 @@ { "name": "guide-mini-vue", "version": "1.0.0", - "main": "index.js", + "main": "lib/guide-mini-vue.cjs.js", + "module": "lib/guide-mini-vue.esm.js", "license": "MIT", "scripts": { - "test": "jest" + "test": "jest", + "build": "rollup -c rollup.config.js" }, "devDependencies": { "@babel/core": "^7.18.0", "@babel/preset-env": "^7.18.0", "@babel/preset-typescript": "^7.17.12", + "@rollup/plugin-typescript": "^8.3.2", "@types/jest": "^27.5.1", "babel-jest": "^28.1.0", "jest": "^28.1.0", + "rollup": "^2.75.5", "ts-jest": "^28.0.3", + "tslib": "^2.4.0", "typescript": "^4.7.2" } } diff --git a/rollup.config.js b/rollup.config.js new file mode 100644 index 0000000..4c63bb5 --- /dev/null +++ b/rollup.config.js @@ -0,0 +1,19 @@ +import pkg from './package.json' +import typescript from "@rollup/plugin-typescript" + +export default { + input: './src/index.ts', + output: [ + // cjs属于commonjs规范 + // esm属于es规范 + { + format: 'cjs', + file: pkg.main + }, + { + format: 'es', + file: pkg.module + } + ], + plugins: [typescript()] +} \ No newline at end of file diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..fad4f92 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,3 @@ +// mini-vue的出口 + +export * from "./runtime-core/index" diff --git a/src/runtime-core/component.ts b/src/runtime-core/component.ts new file mode 100644 index 0000000..0ce3610 --- /dev/null +++ b/src/runtime-core/component.ts @@ -0,0 +1,35 @@ +export function createComponentInstance(vnode) { + const component = { + vnode, + type: vnode.type, + } + return component +} + +export function setupComponent(instance) { + // initProps() + // initSlots() + setupStatefulComponent(instance) +} + +function setupStatefulComponent(instance: any) { + const Component = instance.type + + const { setup } = Component + if (setup) { + const setupResult = setup() + handleSetupResult(instance, setupResult) + } +} +function handleSetupResult(instance: any, setupResult: any) { + if (typeof setupResult === "object") { + instance.setupState = setupResult + } + finishSetupComponent(instance) +} +function finishSetupComponent(instance: any) { + const Component = instance.type + // if (Component.render) { + instance.render = Component.render + // } +} diff --git a/src/runtime-core/createApp.ts b/src/runtime-core/createApp.ts new file mode 100644 index 0000000..b647035 --- /dev/null +++ b/src/runtime-core/createApp.ts @@ -0,0 +1,11 @@ +import { render } from "./renderer" +import { createVNode } from "./vnode" + +export function createApp(rootComponent) { + return { + mount(rootContainer) { + const vnode = createVNode(rootComponent) + render(vnode, rootContainer) + }, + } +} diff --git a/src/runtime-core/h.ts b/src/runtime-core/h.ts new file mode 100644 index 0000000..965a24e --- /dev/null +++ b/src/runtime-core/h.ts @@ -0,0 +1,5 @@ +import { createVNode } from "./vnode" + +export function h(type, props?, children?) { + return createVNode(type, props, children) +} diff --git a/src/runtime-core/index.ts b/src/runtime-core/index.ts new file mode 100644 index 0000000..ba2da94 --- /dev/null +++ b/src/runtime-core/index.ts @@ -0,0 +1,2 @@ +export { createApp } from "./createApp" +export { h } from "./h" diff --git a/src/runtime-core/renderer.ts b/src/runtime-core/renderer.ts new file mode 100644 index 0000000..972bc25 --- /dev/null +++ b/src/runtime-core/renderer.ts @@ -0,0 +1,27 @@ +import { createComponentInstance, setupComponent } from "./component" + +export function render(vnode, container) { + patch(vnode, container) +} + +function patch(vnode, container) { + // 需要区分element和component + // 如果是function就认为是component? + // 如果是object就认为是element? + processComponent(vnode, container) +} + +function processComponent(vnode: any, container: any) { + mountComponent(vnode, container) +} + +function mountComponent(vnode: any, container) { + const instance = createComponentInstance(vnode) + setupComponent(instance) + setupRenderEffect(instance, container) +} + +function setupRenderEffect(instance: any, container) { + const subTree = instance.render() + patch(subTree, container) +} diff --git a/src/runtime-core/vnode.ts b/src/runtime-core/vnode.ts new file mode 100644 index 0000000..9f5fc87 --- /dev/null +++ b/src/runtime-core/vnode.ts @@ -0,0 +1,8 @@ +export function createVNode(type, props?, children?) { + const vnode = { + type, + props, + children, + } + return vnode +} diff --git a/tsconfig.json b/tsconfig.json index 1d92181..b73f9a3 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -27,7 +27,7 @@ // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ /* Modules */ - "module": "commonjs", /* Specify what module code is generated. */ + "module": "esnext", /* Specify what module code is generated. */ // "rootDir": "./", /* Specify the root folder within your source files. */ // "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */