npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@chensi-thunder/fe-micro-app

v1.0.0

Published

特定微前端app入口框架

Downloads

3

Readme

APP前端构建指南

1. (开文强调)全局变量声明注意

尽量不要再在window/vue/vue-router/react中混入属性,避免不同app和主工程之间变量混乱,以及可能难以追踪的内存泄漏

  • windows上主要占用的属性

    1. app主工程依赖 window.exportApp/window.vue/window.vueRouter/window._idss_micro_render
    2. 项目主工程依赖(主要使用element-ui,避免样式覆盖,供app使用) window.ElementUI ...其他暂未发现
  • 现有vue开发工程中,现有项目主工程中,已经在vue库中继承了

    1. axios请求二次封装插件 $requet/$requestCancel
    2. socket相关方法:$socketInit/$socketSend/$socketIsOpen/$socketClose
    3. 刷新当前页面的父级跳转页面方法 $refreshParentPage
    4. 暂无数据抽象service提取变量 $EMPTY_CONFIG
    5. app主工程依赖占用变量:appsList
  • vue-router中,现有项目主工程已经继承的函数或者方法

    1. replaceTagPush 关闭tags并且跳转
    2. pushWithFromPath 新开路由,query中添加from属性,from为父级页面路径
    3. backToFromPath 回到原始路由(与pushWithFromPath搭配使用)

2. 前端使用、构建指南

现有开发使用方式支持vuereact两种前端开发框架开发项目,同时需要依赖fe-micro-app包,对项目进行入口文件以及其他部分代码进行微调,很方便的将一个普通项目改造为一个可以支持独立运行的app。 下面将主要介绍具体使用过程中的一些操作和注意事项

2.1 fe-micro-app 通用依赖包安装

安装app需要依赖文件fe-micro-app,该依赖封装了app内部处理的核心暴露函数、以及webpack的manifest提取plugin

# yarn 方式安装
yarn add fe-micro-app
# npm
npm install fe-micro-app

2.2 vue 搭建和构建

2.2.1 vue搭建框架

建议使用vue-cli搭建 参考链接

# 安装
npm install -g @vue/cli
# OR
yarn global add @vue/cli

2.2.2 创建一个项目

vue-cli搭建使用文档,参考链接

    vue create 英文项目名称xxx

2.2.3 项目构建相关配置

修改vue.config.js文件

// vue.config.js
const appManifestPlugin = require('fe-micro-app/plugin/app-manifest-plugin.js')

const config = {
    ..., // 你需要在vue.confi.js配置的参数
    devServer: {
        ...
        port: 8000, // 建议使用固定端口号,方便开发环境下测试
        ... // 你自己的其他配置
    }
}
module.exports = appManifestPlugin(config, {
  publicPath: '/app-fe/yourAppName', // 部署链接,生产环境的部署地址
  externals: {} // 生产环境打包时,项目自定义的external
})

2.2.4 启动项目

到此为止,一个基础的项目配置已经完成,可以成功运行了。参考链接

    npm run serve
    # 或者
    yarn serve

2.2.5 打包构建

上述vue.config.js文件中,在最后发布上线的时候,publicPath参数一定要配置正确,需要根据自己app部署名称进行匹配,publicPath: '/app-fe/yourAppName',主工程根据该路径来加载资源

npm run build
# 或者
yarn buld

2.3 react 搭建和构建

2.3.1 搭建框架

建议使用creat-react-app搭建react app 参考链接

npx create-react-app my-app
cd my-app
# react项目需要修改webpack配置项,需要启动eject指令,将webpack配置项暴露
npm run eject

2.3.2 项目构建相关配置

  • 修改webpack配置相关处理文件config/webpack.config.js
  const HtmlManifestPlugin = require('fe-micro-app/plugin/html-mainfest-plugin.js')
  const manifest = {}
  module.exports = function () {
    return {
      plugins: [
        /** 将InlineChunkHtmlPlugin 插件注释,该插件将生成的runtime script打入到index.html的文件内部script执行,该方式暂时不支持 */
        // isEnvProduction &&
          //   shouldInlineRuntimeChunk &&
          //   new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [/runtime-.+[.]js/]),
        ...

        // 提取manifest文件
        new HtmlManifestPlugin({ manifest }),

        // plugins 添加manifest插件,并自定义处理manifest
        new ManifestPlugin({
          generate: (seed, files) => {
            // 判断多页面,并处理入口文件
            return Object.assign(seed, manifest)
          }
        })
      ]
    }
  }
  • 修改webpack devServer配置config/webpackDevServer.config.js
  module.exports = function(proxy, allowedHost) {
    return {
  
        // 开环境下作为app调试时,需要设置跨域处理
        headers: {
          'Access-Control-Allow-Origin': '*'
        }
      ...
    }
  }
  • 修改构建后的publicPath处理 config/paths.js
  // publicUrlOrPath变量为webpack内部获取的publicPath变量
    const publicUrlOrPath = getPublicUrlOrPath(
      process.env.NODE_ENV === 'development',
      '你的本地测试path',
      '生产环境path'
    )

2.3.3 启动项目

到此为止,一个基础的项目配置已经完成,可以成功运行了。

    npm start
    # 或者
    yarn start

2.3.4 打包构建

npm run build
# 或者
yarn buld

2.4 vue3 搭建和构建

2.4.1 搭建框架

建议使用@vue/[email protected]搭建vue3项目(目前暂不支持vite构建) 参考链接

vue create my-app
cd my-app

2.4.2 项目构建相关配置

修改vue.config.js文件

// vue.config.js
const appManifestPlugin = require('fe-micro-app/plugin/app-manifest-plugin.js')

const config = {
    ..., // 你需要在vue.confi.js配置的参数
    devServer: {
        ...
        port: 8000, // 建议使用固定端口号,方便开发环境下测试
        ... // 你自己的其他配置
    }
}
module.exports = appManifestPlugin(config, {
  publicPath: '/app-fe/yourAppName', // 部署链接,生产环境的部署地址
  externals: {} // 生产环境打包时,项目自定义的external
})

2.4.3 启动项目

到此为止,一个基础的项目配置已经完成,可以成功运行了。参考链接

    npm run serve
    # 或者
    yarn serve

2.3.4 打包构建

npm run build
# 或者
yarn buld

3. 项目开发

3.1 入口文件修改

vue工程入口

// src/main.js
import { registerApp } from 'fe-micro-app'
    
// 注册app并渲染app
export default registerApp({
  // 用于传递vue作用域,避免提取公共方法后,vue版本不一致问题
  factory: Vue,
  // 用于主函数渲染的对象,不需要包裹new Vue
  app: {
    router,
    el: '#app',
    components: { App: () => import('./App.vue') },
    template: '<App/>',
    // vue的生命周期钩子都接受props属性,用于接收主工程向子工程传递的属性,用于主项目和子app进行通讯
    beforeCreate: (props) => {
      console.log(props)
    }
  },
  // 开发测试阶段api前缀,app声明周期中,用于设置开发环境默认路由
  apiPrefix: '/api'
})

注意点:vue的生命周期函数中,都支持props传递,支持的生命周期函数主要包括vue声明周期钩子

vue3工程入口

// src/main.js
import { createApp } from 'vue'
import { registerApp } from 'fe-micro-app'
import App from './App.vue'
import request from '@/plugins/request.js'
import socket from '@/plugins/socket.js'
import router from '@/router'

export default registerApp({
  factory: createApp,
  // 用于主函数渲染的对象
  app: {
    App,
    el: '#app'
  },
  // 用于在vue3中app初始化成功后,挂载各种插件
  useCallback (app) {
    app.use(request)
    app.use(router)
    app.use(socket)
  },
  // 开发测试阶段api前缀,app声明周期中,用于设置开发环境默认路由
  apiPrefix: '/api'
})

react工程

  1. src/index.js 入口文件修改
  import { registerApp, appConfig } from 'fe-micro-app'

  // 路由组件处理,app运行时,会默认为app提供appConfig.routerBase,用于为当前app路由添加nameSpace
  const RouteExample = () => {
    return (
      // appConfig.routerBase为app提供的nameSpace
      <Router basename={appConfig.routerBase}>
        <nav>
          <Link to="/">Home</Link>
          <Divider type="vertical" />
          <Link to="/about">About</Link>
        </nav>
        <Suspense fallback={null}>
          <Switch>
            <Route path="/" exact component={Home} />
            <Route path="/about" component={About} />
          </Switch>
        </Suspense>
      </Router>
    )
  }
  // 入口app组件
  const App = () => {
    return (
      <div className="app-main">
        <LibVersion />
        <HelloModal />
        <Divider />
        <RouteExample />
      </div>
    )
  }

  // app暴露方式
  export default registerApp({
    factory: ReactDOM,
    app: {
      template: <App/>,
      el: document.getElementById('root')
    },
    lifeCycles: {
      bootstrap () {
        console.log('[react16] react app bootstraped')
      },
      mount (props) {
        console.log('[react16] props from main framework', props)
      },
      unmount () {
        ReactDOM.unmountComponentAtNode(document.getElementById('root'))
      }
    },
    // 开发测试阶段api前缀,app声明周期中,通过this.api调用
    apiPrefix: '/api'
  })

注意点:与vue的生命周期函数不同,react在class components中才会有生命周期的钩子函数支持,在函数组件中不能支持。

3.2 业务开发

3.2.1 api前缀部分

在app对接到主工程中,主工程会对当前app的所有api在nginx层进行代理转发,而前端需要对该api进行控制,在主工程,自动对该api前缀进行替换,替换成主工程使用的api前缀,所以主工程会传递一个apiPrefix变量供app开发使用,功能中如果需要使用路由跳转、下钻功能的,见如下示例:

使用方式如下:

import { appConfig } from 'fe-micro-app'
export default {
    methods: {
        async getList () {
            await axios({
              url: `${appConfig.apiPrefix}/ti/userList`,
              method: 'get'
            })
        }
    }
}

3.2.2 websocket 事件前缀部分

在app对接到主工程中,通常会存在全局ws事件进行拦截处理,对不同app的不同事件进行分发,所以主工程会传递一个wsEvPrefix变量供app开发使用,用于区分不同的app websocket前缀,见如下示例:

使用方式如下:

import { appConfig } from 'fe-micro-app'
export default {
    methods: {
      getWsList () {
        this.$socketSend(`${appConfig.wsEvPrefix}query-list`)
      }
    },
    created () {
      // 发送socket请求
      this.getWsList()
      // 监听ws请求
      this.bus.$on(`${appConfig.wsEvPrefix}query-list`, (res) => {
        this.list = res
      })
    }
}

3.2.3 host当前资源请求

在app工程中,存在部分场景即获取当前工程下的静态资源的情况,为了支持该种情况,添加了host支持,用于满足工程中对于该需求的使用场景

使用方式如下:

import { appConfig } from 'fe-micro-app'
export default {
  methods: {
    async getList () {
      const data = (await (await fetch(`${appConfig.host}/config.json`', {
        method: 'get',
        headers: new Headers({
          'Content-Type': 'application/json'
        })
      })).json()).content
      return data
    }
  }
}

3.2.4 区分当前app是独立渲染还是app渲染

在项目开发中,通常会遇到一些需要判断当前项目为app渲染还是普通渲染的场景,针对不同的场景有不同的代码处理逻辑,如下示例:

下面的示例展示的是项目二级菜单在app渲染时append to body, 否则在父级节点下展示

  <template>
    <app-menu
      :isMenuOpen="isMenuOpen"
      :popperAppendToBody="menuPopperAppendToBody"></app-menu>
  </template>

  <script>
    // 判断当前工程为app渲染还是普通渲染
    import { checkIsMicroRender } from 'fe-micro-app'
    export default {
      data () {
        return {
          // menu 菜单的二级菜单popper弹框是否append to body判断,app渲染时false,否则为true
          menuPopperAppendToBody: !checkIsMicroRender()
        }
      }
    }
  </script>

3.2.5 app内部跳转外部链接

import { appHistory } from 'fe-micro-app'

export default {
  methods: {
    gotoMainAppHome () {
      // 跳转到主框架的用户管理页面
      appHistory.push('/manage/manage-base/user')

      // 当前路由页面替换为主框架的用户管理页面
      appHistory.replace('/manage/manage-base/user')
    }
  }
}

4. 开发中注意事项

  1. 现有支持功能有限,建议vue/vue-template-compiler保持版本使用2.6.10

  2. 全局变量升名注意事项,见开头

  3. 样式尽量使用scoped方式升名,避免样式冲突,优先推荐BEM、CSS Modules 方案管理样式

  4. 尽量使用import('xxxx/xxx.vue')动态引入的方式,降低主入口文件的体积

  5. 尽量不要使用全局样式,以及添加reset css,这样可能造成整个主框架应用显示异常(后期优化,尽量做到css隔离)

  6. 目前建议不要在在vue.prototype上挂载任何实例,避免主工程和其他app发生方法、属性覆盖的情况

  7. 如果需要在测试开发环境中的app时,需要为webpack等构建工具添加publicPath(注:vue-cli方式生成的app工程不需要修改,已经设置内置publicPath)

  8. 关于ui框架,目前主工程使用了element-ui,所以如果想使用相同ui框架,建议处理方式

    • 主入口文件修改
    // main.js
    Vue.use(window.ElementUI || ElementUI)
    if (!window.ElementUI) {
      // eslint-disable-next-line
      import('element-ui/lib/theme-chalk/index.css')
    }
    • vue.confi.js修改
    module.exports = appManifestPlugin(config, {
      publicPath: '/app-fe/yourAppName', // 部署链接,生产环境的部署地址
      // 生产环境打包时,项目自定义的external
      externals: {
        'element-ui': 'window.ElementUI'
      }
    })
  9. 关于public静态资源文件夹下的资源,无法添加publicPath,主要原因是任何放置在 public 文件夹的静态资源都会被简单的复制,而不经过 webpack,所以需要使用绝对路径来引用,所以针对静态资源使用方式

在模板中,你首先需要向你的组件传入基础 URL:

  <template><img :src="`${publicPath}my-image.png`"></template>
  <script>
    export default {
      data () {
        return {
          publicPath: process.env.BASE_URL
        }
      }
    }
  </script>

在css中,根据项目使用的不同编译器,可以使用变量,postcss可以通过postcss-function插入函数引入

  yarn add [email protected] -D
  // postcss.config.js
  module.exports = {
    ...
      'postcss-functions': {
        functions: {
          staticUrl (url, type = '{path}') {
            return type.replace(`{path}`, `${process.env.BASE_URL}${url}`)
          }
        }
      }
    ...
  }
  <style scoped lang="postcss">
  #home {
    ...
    background: staticUrl(/403.png, url({path}));
    ...
  }
  </style>