Compare commits

...

2 Commits

11 changed files with 170 additions and 168 deletions

101
README.md
View File

@@ -91,8 +91,9 @@ src/
```tsx ```tsx
// 首页 页面 // 首页 页面
import { Autobind, Component, Link, Mut, VueComponent } from 'vue3-oop' import { Component, Ref, Setup, Vue } from 'vue-facing-decorator'
import Header from '@/components/Header' import Header from '../components/Header'
import { RouteLocationNormalizedLoaded, Router, useRoute, useRouter } from "vue-router";
interface ListItemType { interface ListItemType {
id: number id: number
@@ -100,44 +101,52 @@ interface ListItemType {
} }
@Component() @Component()
export default class Home extends VueComponent { export default class Home extends Vue {
@Mut() counter: number = 1;
count: number = 1
@Mut() @Ref
text: string = "Home props 传给 Header 的字符串" readonly refH1!: HTMLDivElement
@Setup((props, ctx) => useRouter())
router!: Router
@Setup((props, ctx) => useRoute())
route!: RouteLocationNormalizedLoaded
@Mut()
newList: ListItemType[] = [ newList: ListItemType[] = [
{ id: 1, title: '新闻1' }, { id: 1, title: '新闻1' },
{ id: 2, title: '新闻2' }, { id: 2, title: '新闻2' },
{ id: 3, title: '新闻3' }, { id: 3, title: '新闻3' },
] ]
@Link() H1?: HTMLDivElement addCount() {
// console.log("ref dom: ", this.refH1)
@Autobind() // this.router.push({ name: 'about' })
async addCount() { // this.route.query
this.count++ this.counter++
this.text = "22222"
console.log(this.H1)
} }
testHtml() {
return <p>测试html jsx 函数</p>
}
render() { render() {
return ( return (
<div class="home" ref="H1"> <div class="home">
<h1>首页</h1> <h1 ref="refH1">首页</h1>
<p onClick={ this.addCount }>count响应式变量(点我):{ this.count }</p> <p onClick={ this.addCount }>counter 响应式变量(点我):{ this.counter }</p>
<Header <Header
v-slots={ { list={ this.newList }
text={ "测试" }>
{ {
default: () => <div class="default">父到子 default 插槽</div>, default: () => <div class="default">父到子 default 插槽</div>,
haveName: () => <div class="haveName">父到子 具名 插槽</div>, haveName: () => <div class="haveName">父到子 具名 插槽</div>,
item: (val) => <li key={ val.id }>{ val.title }</li> item: (val: ListItemType) => <li key={ val.id }>{ val.title }</li>
} } } }
list={ this.newList }
text={ this.text }>
</Header> </Header>
{ this.testHtml }
</div> </div>
) )
} }
@@ -146,53 +155,43 @@ export default class Home extends VueComponent {
```tsx ```tsx
// 头部组件 // 头部组件
import { Component, ComponentProps, Hook, useProps, VueComponent } from 'vue3-oop' import { Component, Prop, TSX, Vue } from 'vue-facing-decorator'
import { JSX } from "vue/jsx-runtime";
import { VNodeChild } from "vue"; interface Props {
text?: string
list?: ListItemType[]
}
interface ListItemType { interface ListItemType {
id: number id: number
title: string title: string
} }
interface HeaderProps {
text?: string;
list: ListItemType[]
slots: {
default: () => JSX.Element;
haveName: () => JSX.Element;
item: (val: ListItemType) => VNodeChild;
}
}
@Component() @Component()
export default class Header extends VueComponent<HeaderProps> { export default class Header extends TSX<Props>()(Vue) implements Props {
// required 的 true or false 需要和 类型中 text的? 对应 @Prop({ default: '默认数值' })
static defaultProps: ComponentProps<HeaderProps> = { text!: string;
text: { default: '默认数值', required: true },
list: { default: () => [] }
};
@Hook('Mounted') @Prop()
mounted() { list!: ListItemType[];
console.log("this: ", this);
console.log('Header 组件的 mounted 生命周期...') created() {
console.log("created: ", this)
} }
render() { render() {
const { text, list } = useProps<HeaderProps>();
return ( return (
<div class="header"> <div class="header">
头部组件 头部组件
<p>{ text }</p> <p>{ this.text }</p>
{ this.context.slots.default?.() } { this.$slots.default?.() }
{ this.context.slots.haveName?.() } { this.$slots.haveName?.() }
<ul> <ul>
{ {
list.map( this.list.map(
item => item =>
this.context.slots.item?.(item) this.$slots.item?.(item)
) )
} }
</ul> </ul>

View File

@@ -3,9 +3,6 @@
"version": "0.0.0", "version": "0.0.0",
"private": true, "private": true,
"type": "module", "type": "module",
"engines": {
"node": "^20.19.0 || >=22.12.0"
},
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
"dep": "concurrently \"npm run build\" \"npm run preview\"", "dep": "concurrently \"npm run build\" \"npm run preview\"",
@@ -16,18 +13,16 @@
"dependencies": { "dependencies": {
"pinia": "^3.0.3", "pinia": "^3.0.3",
"vue": "^3.5.18", "vue": "^3.5.18",
"vue-facing-decorator": "^4.0.1",
"vue-router": "^4.0.6" "vue-router": "^4.0.6"
}, },
"devDependencies": { "devDependencies": {
"@abraham/reflection": "^0.13.0",
"@originjs/vite-plugin-federation": "^1.4.1", "@originjs/vite-plugin-federation": "^1.4.1",
"@types/node": "^24.5.2", "@types/node": "^24.5.2",
"@vue3-oop/plugin-vue-jsx": "^1.5.1", "@vue3-oop/plugin-vue-jsx": "^1.5.1",
"injection-js": "^2.5.0",
"tslib": "^2.8.1", "tslib": "^2.8.1",
"typescript": "~5.8.0", "typescript": "~5.8.0",
"vite": "^7.0.6", "vite": "^7.0.6",
"vite-plugin-vue-devtools": "^8.0.0", "vite-plugin-vue-devtools": "^8.0.0"
"vue3-oop": "^1.2.1"
} }
} }

View File

@@ -1,8 +1,8 @@
import { Component, VueComponent } from 'vue3-oop' import { Component, Vue } from 'vue-facing-decorator'
import { RouterLink, RouterView } from "vue-router"; import { RouterLink, RouterView } from "vue-router";
@Component() @Component()
export class App extends VueComponent { export default class App extends Vue {
render() { render() {
return ( return (
<> <>

View File

@@ -1,50 +1,40 @@
import { Component, ComponentProps, Hook, useProps, VueComponent } from 'vue3-oop' import { Component, Prop, TSX, Vue } from 'vue-facing-decorator'
import { JSX } from "vue/jsx-runtime";
import { VNodeChild } from "vue"; interface Props {
text?: string
list?: ListItemType[]
}
interface ListItemType { interface ListItemType {
id: number id: number
title: string title: string
} }
interface HeaderProps {
text?: string;
list: ListItemType[]
slots: {
default: () => JSX.Element;
haveName: () => JSX.Element;
item: (val: ListItemType) => VNodeChild;
}
}
@Component() @Component()
export default class Header extends VueComponent<HeaderProps> { export default class Header extends TSX<Props>()(Vue) implements Props {
// required 的 true or false 需要和 类型中 text的? 对应 @Prop({ default: '默认数值' })
static defaultProps: ComponentProps<HeaderProps> = { text!: string;
text: { default: '默认数值', required: true },
list: { default: () => [] }
};
@Hook('Mounted') @Prop()
mounted() { list!: ListItemType[];
console.log("this: ", this);
console.log('Header 组件的 mounted 生命周期...') created() {
console.log("created: ", this)
} }
render() { render() {
const { text, list } = useProps<HeaderProps>();
return ( return (
<div class="header"> <div class="header">
<p>{ text }</p> <p>{ this.text }</p>
{ this.context.slots.default?.() } { this.$slots.default?.() }
{ this.context.slots.haveName?.() } { this.$slots.haveName?.() }
<ul> <ul>
{ {
list.map( this.list.map(
item => item =>
this.context.slots.item?.(item) this.$slots.item?.(item)
) )
} }
</ul> </ul>
@@ -52,3 +42,4 @@ export default class Header extends VueComponent<HeaderProps> {
) )
} }
} }

View File

@@ -1,11 +1,9 @@
// import './assets/main.css' // import './assets/main.css'
import '@abraham/reflection'
import { createApp } from 'vue' import { createApp } from 'vue'
import { createPinia } from 'pinia' import { createPinia } from 'pinia'
import { App } from './App' import App from './App'
import router from './router' import router from './router'
class Main { class Main {
constructor() { constructor() {
createApp(App) createApp(App)

View File

@@ -1,12 +1,12 @@
import {Component, VueComponent} from 'vue3-oop' import { Component, Vue } from 'vue-facing-decorator'
@Component() @Component()
export default class About extends VueComponent { export default class About extends Vue {
render() { render() {
return ( return (
<div class="about"> <div class="about">
<h1></h1> <h1></h1>
</div> </div>
) )
} }
} }

View File

@@ -1,5 +1,6 @@
import { Autobind, Component, Link, Mut, VueComponent } from 'vue3-oop' import { Component, Ref, Setup, Vue } from 'vue-facing-decorator'
import Header from '@/components/Header' import Header from '../components/Header'
import { RouteLocationNormalizedLoaded, Router, useRoute, useRouter } from "vue-router";
interface ListItemType { interface ListItemType {
id: number id: number
@@ -7,44 +8,52 @@ interface ListItemType {
} }
@Component() @Component()
export default class Home extends VueComponent { export default class Home extends Vue {
@Mut() counter: number = 1;
count: number = 1
@Mut() @Ref
text: string = "Home props 传给 Header 的字符串" readonly refH1!: HTMLDivElement
@Setup((props, ctx) => useRouter())
router!: Router
@Setup((props, ctx) => useRoute())
route!: RouteLocationNormalizedLoaded
@Mut()
newList: ListItemType[] = [ newList: ListItemType[] = [
{ id: 1, title: '新闻1' }, { id: 1, title: '新闻1' },
{ id: 2, title: '新闻2' }, { id: 2, title: '新闻2' },
{ id: 3, title: '新闻3' }, { id: 3, title: '新闻3' },
] ]
@Link() H1?: HTMLDivElement addCount() {
// console.log("ref dom: ", this.refH1)
@Autobind() // this.router.push({ name: 'about' })
async addCount() { // this.route.query
this.count++ this.counter++
this.text = "22222"
console.log(this.H1)
} }
testHtml() {
return <p>html jsx </p>
}
render() { render() {
return ( return (
<div class="home" ref="H1"> <div class="home">
<h1></h1> <h1 ref="refH1"></h1>
<p onClick={ this.addCount }>count响应式变{ this.count }</p> <p onClick={ this.addCount }>counter { this.counter }</p>
<Header <Header
v-slots={ { list={ this.newList }
text={ "测试" }>
{ {
default: () => <div class="default"> default </div>, default: () => <div class="default"> default </div>,
haveName: () => <div class="haveName"> </div>, haveName: () => <div class="haveName"> </div>,
item: (val) => <li key={ val.id }>{ val.title }</li> item: (val: ListItemType) => <li key={ val.id }>{ val.title }</li>
} } } }
list={ this.newList }
text={ this.text }>
</Header> </Header>
{ this.testHtml() }
</div> </div>
) )
} }

View File

@@ -1,11 +1,24 @@
import { defineComponent } from 'vue' // import { defineComponent } from 'vue'
//
// export default defineComponent({
// setup() {
// return () => (
// <div class="404">
// <h1>404</h1>
// </div>
// )
// }
// })
export default defineComponent({ import { Component, Vue } from 'vue-facing-decorator'
setup() {
return () => ( @Component()
export default class About extends Vue {
render() {
return (
<div class="404"> <div class="404">
<h1>404</h1> <h1>404</h1>
</div> </div>
) )
} }
}) }

View File

@@ -1,5 +1,5 @@
declare module '*.vue' { declare module '*.vue' {
import type {DefineComponent} from 'vue' import type { DefineComponent } from 'vue'
const component: DefineComponent<{}, {}, any> const component: DefineComponent<{}, {}, any>
export default component export default component
} }

View File

@@ -3,9 +3,7 @@
"target": "ESNext", "target": "ESNext",
"module": "ESNext", "module": "ESNext",
"importHelpers": true, "importHelpers": true,
"isolatedModules": true,
"experimentalDecorators": true, "experimentalDecorators": true,
"emitDecoratorMetadata": true,
"useDefineForClassFields": false, "useDefineForClassFields": false,
"moduleResolution": "Bundler", "moduleResolution": "Bundler",
"strict": true, "strict": true,

View File

@@ -1,47 +1,46 @@
import {fileURLToPath, URL} from 'node:url' import { fileURLToPath, URL } from 'node:url'
import {defineConfig} from 'vite' import { defineConfig } from 'vite'
import vueJsx from '@vue3-oop/plugin-vue-jsx' import vueJsx from '@vue3-oop/plugin-vue-jsx'
import federation from '@originjs/vite-plugin-federation'; import federation from '@originjs/vite-plugin-federation';
export default defineConfig({ export default defineConfig({
server: { server: {
port: 1300, port: 1300,
host: '0.0.0.0', host: '0.0.0.0',
strictPort: true, strictPort: true,
}, },
plugins: [ plugins: [
vueJsx(), vueJsx(),
federation({ federation({
name: 'mainApp', // 主应用模块名(联邦模块需通过此名引用) name: 'mainApp', // 主应用模块名(联邦模块需通过此名引用)
remotes: { // 配置远程联邦模块地址(开发/生产需对应) remotes: { // 配置远程联邦模块地址(开发/生产需对应)
product: 'http://localhost:1301/assets/remoteEntry.js', product: 'http://localhost:1301/assets/remoteEntry.js',
shared: 'http://localhost:1333/assets/remoteEntry.js' shared: 'http://localhost:1333/assets/remoteEntry.js'
}, },
shared: { shared: {
vue: { requiredVersion: '^3.5.18' }, vue: { requiredVersion: '^3.5.18' },
'vue-router': { requiredVersion: '^4.0.6' }, 'vue-router': { requiredVersion: '^4.0.6' },
pinia: { requiredVersion: '^3.0.3' } pinia: { requiredVersion: '^3.0.3' }
} }
}) })
], ],
build: { build: {
target: 'esnext', target: 'esnext',
minify: false, minify: false,
cssCodeSplit: true, cssCodeSplit: true,
rollupOptions: { rollupOptions: {
output: { output: {
minifyInternalExports: false minifyInternalExports: false
} }
} }
}, },
resolve: { resolve: {
alias: { alias: {
'@': fileURLToPath(new URL('./src/app/', import.meta.url)), '@': fileURLToPath(new URL('./src/app/', import.meta.url)),
'@shared': fileURLToPath(new URL('./src/shared/', import.meta.url)), '@shared': fileURLToPath(new URL('./src/shared/', import.meta.url)),
'@features': fileURLToPath(new URL('./src/features/', import.meta.url)), '@features': fileURLToPath(new URL('./src/features/', import.meta.url)),
'@domains': fileURLToPath(new URL('./src/domains/', import.meta.url)) '@domains': fileURLToPath(new URL('./src/domains/', import.meta.url))
},
}, },
},
}) })