Skip to content

wujie-vue3原理

底层使用的还是 wujie 的 startApp 方法,只不过用vue3的方式封装了一下。 通讯方式使用的是 wujie 的 bus 事件总线。发布订阅模式。

项目初始化

config
  src
    index.ts
    type.ts
  index.d.ts
  package.json
  tsconfig.json
  webpack.config.js

package.json

json
{
  "name": "@biye/wujie-vue-setup",
  "version": "1.0.0",
  "description": "",
  "main": "lib/index.js",
  "module": "esm/index.js",
  "scripts": {
    "build": "webpack",
    "esm": "swc src/index.ts -d esm"
  },
  "files": [
    "lib",
    "esm",
    "index.d.ts"
  ],
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "wujie": "^1.0.22"
  },
  "devDependencies": {
    "@swc/core": "^1.6.0",
    "swc-loader": "^0.2.6",
    "ts-loader": "^9.5.1",
    "typescript": "^5.4.5",
    "vue": "^3.4.29",
    "webpack": "^5.92.0",
    "webpack-cli": "^5.1.4"
  }
}

files 配置 上传到npm时需要上传的文件夹和文件 module 配置 esm格式的文件 main 配置 其他格式引入的文件

使用swc来编译ts文件,是传统的ts-loader编译速度的70倍,是用rust写的。

参考swc官网 https://swc.rs/

webpack配置文件

js
const { Configuration } = require('webpack')
const path = require('path')

/**
 * @type {Configuration} //配置智能提示
 */
const config = {
  entry: './src/index.ts',
  output: {
    filename: 'wujie.js', // 输出的文件名
    path: path.resolve(__dirname, './lib'), // 输出的文件目录
    library: 'Wujie', // 外部使用我们包时 需要使用的变量名
    libraryTarget: 'umd', // 输出的文件格式
    umdNamedDefine: true,
  },
  externals: { // vue 和 wujie 不要打包 排除一下
    vue: 'vue',
    wujie: 'wujie',
  },
  mode: 'none', // 源码的方式打包 不要压缩
  cache: true,
  module: {
    rules: [
      {
        test: /\.ts$/, //解析ts
        // loader: 'swc-loader', //使用新技术swc-loader compiled successfully in 140 ms
        loader: 'ts-loader', //ts-loader 很慢 compiled successfully in 3613 ms
      },
    ],
  },
}

module.exports = config

配置 Configuration 可以让webpack配置文件有智能提示 使用的是注解的方式。 打包后的文件使用umd格式,umd打包生成的文件支持 amd cmd commonjs 和全局变量的方式,但是不支持esm。 使用 swc 编译成esm格式,参考swc官网 https://swc.rs/ 需要有个 .swcrc 配置文件

json
/* https://swc.rs/ */
{
  "$schema": "https://swc.rs/schema.json",
  "jsc": {
    "parser": {
      // "syntax": "ecmascript", // 需要改成typescript
      "syntax": "typescript" //
    },
    "target": "es5",
    "loose": false,
    "externalHelpers": false,
    // Requires v1.2.50 or upper and requires target to be es2016 or upper.
    "keepClassNames": false
  },
  "minify": false
}

src/index.ts

ts
import { startApp, bus } from 'wujie'
import { h, defineComponent, onMounted, getCurrentInstance, onBeforeUnmount, watch } from 'vue'
import type { App, PropType } from 'vue'
import { Props } from './type'
const WuJie = defineComponent({
  props: {
    width: { type: String, default: '' },
    height: { type: String, default: '' },
    name: { type: String, default: '', required: true },
    loading: { type: HTMLElement, default: undefined },
    url: { type: String, default: '', required: true },
    sync: { type: Boolean, default: undefined },
    prefix: { type: Object, default: undefined },
    alive: { type: Boolean, default: undefined },
    props: { type: Object, default: undefined },
    attrs: { type: Object, default: undefined },
    replace: { type: Function as PropType<Props['replace']>, default: undefined },
    fetch: { type: Function as PropType<Props['fetch']>, default: undefined },
    fiber: { type: Boolean, default: undefined },
    degrade: { type: Boolean, default: undefined },
    plugins: { type: Array as PropType<Props['plugins']>, default: null },
    beforeLoad: { type: Function as PropType<Props['beforeLoad']>, default: null },
    beforeMount: { type: Function as PropType<Props['beforeMount']>, default: null },
    afterMount: { type: Function as PropType<Props['afterMount']>, default: null },
    beforeUnmount: { type: Function as PropType<Props['beforeUnmount']>, default: null },
    afterUnmount: { type: Function as PropType<Props['afterUnmount']>, default: null },
    activated: { type: Function as PropType<Props['activated']>, default: null },
    deactivated: { type: Function as PropType<Props['deactivated']>, default: null },
  },
  setup(props: Props, { emit }) {
    const instance = getCurrentInstance()
    const handlerEmit = (event: string, ...args: any[]) => {
      emit(event, ...args)
    }

    const init = () => {
      startApp({
        name: props.name,
        url: props.url,
        el: instance?.refs.wujie as HTMLElement,
        loading: props.loading,
        alive: props.alive,
        fetch: props.fetch,
        props: props.props,
        attrs: props.attrs,
        replace: props.replace,
        sync: props.sync,
        prefix: props.prefix,
        fiber: props.fiber,
        degrade: props.degrade,
        plugins: props.plugins,
        beforeLoad: props.beforeLoad,
        beforeMount: props.beforeMount,
        afterMount: props.afterMount,
        beforeUnmount: props.beforeUnmount,
        afterUnmount: props.afterUnmount,
        activated: props.activated,
        deactivated: props.deactivated,
      })
    }
    onMounted(() => {
      bus.$onAll(handlerEmit) //添加事件订阅
      //初始化无界
      init()
    })

    watch([props.name, props.url], () => {
      init()
    })

    onBeforeUnmount(() => {
      bus.$offAll(handlerEmit) //取消事件订阅
    })

    return () =>
      h(
        'div',
        {
          style: {
            width: props.width || 200,
            height: props.height || 200,
          },
          ref: 'wujie',
        },
        ''
      )
  },
})

WuJie.install = (app: App) => {
  app.component('wujie', WuJie)
}

export default WuJie

src/type.ts

ts
import type { plugin } from 'wujie'
type lifecycle = (appWindow: Window) => any
interface Props {
  width?: string
  height?: string
  /** 唯一性用户必须保证 */
  name: string
  /** 需要渲染的url */
  url: string
  /** 需要渲染的html, 如果用户已有则无需从url请求 */
  html?: string
  /** 渲染的容器 */
  loading?: HTMLElement
  /** 路由同步开关, false刷新无效,但是前进后退依然有效 */
  sync?: boolean
  /** 子应用短路径替换,路由同步时生效 */
  prefix?: { [key: string]: string }
  /** 子应用保活模式,state不会丢失 */
  alive?: boolean
  /** 注入给子应用的数据 */
  props?: { [key: string]: any }
  /** js采用fiber模式执行 */
  fiber?: boolean
  /** 子应用采用降级iframe方案 */
  degrade?: boolean
  /** 自定义运行iframe的属性 */
  attrs?: { [key: string]: any }
  /** 自定义降级渲染iframe的属性 */
  degradeAttrs?: { [key: string]: any }
  /** 代码替换钩子 */
  replace?: (codeText: string) => string
  /** 自定义fetch,资源和接口 */
  fetch?: (input: RequestInfo, init?: RequestInit) => Promise<Response>
  /** 子应插件 */
  plugins: Array<plugin>
  /** 子应用生命周期 */
  beforeLoad?: lifecycle
  /** 没有做生命周期改造的子应用不会调用 */
  beforeMount?: lifecycle
  afterMount?: lifecycle
  beforeUnmount?: lifecycle
  afterUnmount?: lifecycle
  /** 非保活应用不会调用 */
  activated?: lifecycle
  deactivated?: lifecycle
}

export { Props }

index.d.ts

ts
// import { bus, preloadApp, destroyApp, setupApp } from 'wujie'
import type { App } from 'vue'

declare const WujieVue: {
  // bus: typeof bus
  // setupApp: typeof setupApp
  // preloadApp: typeof preloadApp
  // destroyApp: typeof destroyApp
  install: (app: App) => void
}

export default WujieVue

index.d.ts

ts
// import { bus, preloadApp, destroyApp, setupApp } from 'wujie'
import type { App } from 'vue'

declare const WujieVue: {
  // bus: typeof bus
  // setupApp: typeof setupApp
  // preloadApp: typeof preloadApp
  // destroyApp: typeof destroyApp
  install: (app: App) => void
}

export default WujieVue