美文网首页
qiankun 踩坑指南

qiankun 踩坑指南

作者: 神刀 | 来源:发表于2021-03-18 20:16 被阅读0次

    鉴于前一段时间公司内部项目用到了微前端框架qiankun,总理了一些常见的坑:

    指定 yarn 模块下载源

    yarn config set registry https://registry.npm.taobao.org

    指定 node-sass 下载源

    yarn config set sass-binary-site http://npm.taobao.org/mirrors/node-sass

    指定 electron 下载源

    yarn config set electron_mirror https://npm.taobao.org/mirrors/electron/

    指定 chromedriver 下载源

    yarn config set chromedriver_cdnurl https://npm.taobao.org/mirrors/chromedriver

    qiankun 常见报错
    子项目未 export 需要的生命周期函数
    先检查下子项目的入口文件有没有 export 生命周期函数,再检查下子项目的打包,最后看看请求到的子项目的文件对不对。

    子项目加载时,容器未渲染好

    检查容器 div 是否是写在了某个路由里面,路由没匹配到所有未加载。如果只在某个路由页面加载子项目,可以在页面的 mounted 周期里面注册子项目并启动。 ⚠️:在mounted注册也会有问题 主项目路由的** hash history **之争

    主项目 history : 需要使用 location.pathname 来区分不同的子项目 hash 模式子项目路由跳转不改变 path,所以无影响, history 模式子项目路由设置 base 属性即可。 缺点:

    1. history 模式路由需要设置 base
    2. 子项目之间的跳转需要使用父项目的 router 对象(不用 <a> 链接直接跳转的原因是 <a> 链接会刷新页面)。

    其实不传递 router 对象,用原生的 history 对象跳转也行: history.pushState(null, 'name', '/app-vue-hash/#/about'),同样不会刷新页面。

    不管是父项目的 router 对象,还是原生的 history 对象,跳转都是 js 的方式。这里有一个小小的用户体验问题:标签(<router-link> 和 <a>)形式的跳转是支持浏览器默认的右键菜单的,js 方式则没有:

    主项目路由用 hash 模式且子项目没有history 模式路由

    也就是说主项目和所有子项目都是 hash 模式,这种情况下也有两种做法:

    1. 用 path 来区分子项目

    做法就不赘述了

    优点:无需修改子项目内部代码

    缺点:项目之间的跳转,都得靠原生的 history 对象

    1. 用 hash 来区分子项目

    这样做主项目和子项目会共同接管路由,举个栗子:

    • /#/vue/home: 会加载 vue 子项目的 home 页面,但是其实,单独访问这个子项目的 home 页面的完整路由就是/#/vue/home

    • /#/react/about: 会加载 react 子项目的 about 页面,同样,单独访问这个子项目的 about 页面的完整路由就是/#/react/about

    • /#/about: 会加载主项目的about页面

    做法就是自定义 activeRule :

    const getActiveRule = hash => location => location.hash.startsWith(hash);

    registerMicroApps([

    {

    name: 'app-vue-hash',

    entry: 'http://localhost:1111',

    container: '#appContainer',

    activeRule: getActiveRule('#/app-vue-hash'),

    },

    ])

    复制代码

    然后需要在子项目的所有路由前加上这个前缀,或者将子项目的根路由设置为这个前缀。

    const routes = [

    {

    path: '/app-vue-hash',

    name: 'Home',

    component: Home,

    children: [

    // 其他的路由都写到这里

    ]

    }

    ]

    复制代码

    如果子项目是新项目还好,如果是旧项目,则影响还是比较大,子项目里面的路由跳转(<router-link>、router.push()、router.repace())如果使用的是 path ,则需要修改,得加上这个前缀,如果使用的是 name跳转,则无需改动:router.push({ name: 'user'})。

    优点: 所有项目之间的跳转都可以直接使用自己的 router 对象或者 <router-link>,不需要借助父项目的路由对象或者原生的 history 对象

    缺点: 对子项目是入侵式修改,如果是全新项目,则无影响。

    主项目路由用 hash 模式且子项目有history 模式路由

    主项目是hash 模式,子项目间的跳转就只能借助原生的 history 对象了,我们既可以用 path 也可以用 hash 来区分子项目:

    1. 用 path 来区分子项目

    与主项目是 history 没有太大的差异,优缺点也一样。

    • /vue-hash/#/home: 会加载 vue 子项目的 home 页面
    • /vue-history/about: 会加载 vue-history 子项目的 about 页面
    • /#/about: 会加载主项目的about页面
    1. 用 hash 来区分子项目

    这样做其实不太好,有点反常规,但是也可以用:

    • /home/#/vue: 会加载 vue 子项目的 home 页面
    • /#/vue-hash/about: 会加载 vue-hash 子项目的 about 页面
    • /#/about: 会加载主项目的about页面

    优点:无

    缺点: 对 hash 子项目是入侵式修改,如果是全新项目,则无影响。

    总结

    主项目路由的 hash 与 history 模式都可以使用,各有优劣,看情况取舍。

    vue 项目 hash 模式改 history:
    new Router 时设置 mode 为 history

    webpack 打包的配置( vue.config.js )

    如果一些资源报 404,相对路径改为绝对路径

    <img src="./img/logo.jpg"> 改为 <img src="/img/logo.jpg"> 即可

    css 污染问题及加载 bug
    1
    ,qiankun 只能解决子项目之间的样式相互污染,不能解决子项目的样式污染主项目的样式

    主项目要想不被子项目的样式污染,子项目是 vue 技术,样式可以写 css-scoped ,如果子项目是 jQuery 技术呢?所以主项目本身的 id/class 需要特殊一点,不能太简单,被子项目匹配到。 2,从子项目页面跳转到主项目自身的页面时,主项目页面的 css 未加载的bug 临时解决办法:先复制一下 HTMLHeadElement.prototype.appendChild 和 window.addEventListener ,路由钩子函数 beforeEach 中判断一下,如果当前路由是子项目,并且去的路由是父项目的,则还原这两个对象.

    const childRoute = ['/app-vue-hash','/app-vue-history'];

    const isChildRoute = path => childRoute.some(item => path.startsWith(item))

    const rawAppendChild = HTMLHeadElement.prototype.appendChild;

    const rawAddEventListener = window.addEventListener;

    router.beforeEach((to, from, next) => {

    // 从子项目跳转到主项目

    if(isChildRoute(from.path) && !isChildRoute(to.path)){

    HTMLHeadElement.prototype.appendChild = rawAppendChild;

    window.addEventListener = rawAddEventListener;

    }

    next();

    });

    主子通信
    三种方式:
    1,动态通信 通过rx.js

    部署二级目录
    必须配置 publicPath,vue-cli3 官网描述
    entry 最后面加一个 /,正确是 http://location:5000/good/ 而不是 http://location:5000/good
    要配置 publicPath :https://cli.vuejs.org/zh/config/#publicpath
    打包之后,你的js的路径应该是/good/static/css.js,而不是/static/css.js

    div[id^='_qiankun_microapp_wrapper'] {

    height: 100%;

    }

    主子应用css隔离方案

    子应用:

    配置:
    // postcss.config.js

    module.exports = {

    plugins: {

    autoprefixer: {},

    'postcss-selector-namespace': {

    namespace: function(css) {

    // element-ui的样式不需要添加命名空间

    if (css.includes('element-variables.scss')) return '';

    return ‘#micro-view’;

    },

    },

    },

    };

    micro-view #app[data-v-7ba5bd90] {}

    主应用:

    业务线使用element-ui
    dialog,drawer 的v-modal 层级问题:设置属性append-to-body=true

    静态资源 404(字体,img等)
    webpack打包注入完整路径配置

    file-loader|url-loader 增加publicPath

    const publicPath = http://localhost:${port}
    {

    test: /.(woff2?|eot|ttf|otf)$/i,

    use: [

    {

    loader: 'file-loader',

    options: {

    name: 'fonts/[name].[hash:8].[ext]',

    publicPath

    },

    },

    ],

    },

    Uncaught Error: application 'cmsClient' died in status LOADING_SOURCE_CODE: only one instance of babel-polyfill is allowed

    子应用通过<script src=“babel-polyfill” ignore /> 引入, script 标签加上 ignore 属性

    Throw Error "NavigationDuplicated"

    使用 $router.push 更改“ page”。如果您使用的是同一个页面,vue-router 抛出一个 Error。

    这个是 vue-router 的报错,你应该是 push 相同路由了

    // 避免跳转相同路由报错

    const originalPush = VueRouter.prototype.push

    VueRouter.prototype.push = function push(location) {

    return originalPush.call(this, location).catch(err => err)

    }

    至于 Uncaught SyntaxError: Unexpected token '<',这是 vue-cli-service 的问题

    默认情况下,它打包出来的 js 资源地址是相对路径,比如 <script type="text/javascript" src="js/app.js"></script>

    当你在子应用路径下的时候(比如 http://localhost:7770/basic/about) ,上面的 js 资源就指向了 /basic/js/app.js,

    1. publicPath
    部署到二级目录也是要配置 publicPath 的:https://cli.vuejs.org/zh/config/#publicpath

    1. webpack 的 webpack_public_path 配置是用来给动态加载的资源做路径补全的,不适用于那些直接在 html 中静态引入的 js/css/img 等,这些资源要想变更路径前缀需要配置 webpack publicPath,这个可以翻下 webpack 文档或者自己做下测试验证
    2. 你的 html 里静态引入的 /static/css.js 是绝对路径的资源,这种写法无论是 http://localhost:5000 还是 http://localhost:5000/good/ 上下文中访问,计算出来的地址都会是 http://localhost:5000/static/css.js,原因上面 @gongshun 的截图里说明了,你可以自己随便搞个 html 测试一下
    3. assetPublicPath 或许应该叫 assetContext 或者 assetRuntimePublicPath,现在这个命名确实容易让人误解其等价于 webpack 的 publicPath 配置

    webpack配置的publicpath设置成当前运行时的路径

    父应用使用了babel-pollfill,子应用不要在在bable-pollfill

    子应用的代理将失效,代理需要配置在父应用中

    项目中使用了百度地图等组件,会出现在子应用中无法使用的情况 ,在看源码时发现子应用的document.body中添加script标签失败没报错,但无法正确添加到body中,类似性质的问题还有美洽客服的引用,pdfjs的引用两种解决方法:

    更新qiankun版本至2.0.17,在start中添加excludeAssetFilter,在主应用中引入script标签

    • 使用iframe单独调用百度地图的页面(没有太多页面交互推荐用这种)

    <div id="output"></div>

    <script type="text/javascript">

    //document.write('<' + 'script src="' + src + '"' +' type="text/javascript"><' + '/script>')

    document.querySelector("#output").innerHTML = '<' + 'script src="' + src + '"' + ' type="text/javascript"><' + '/script>'

    </script>

    相关文章

      网友评论

          本文标题:qiankun 踩坑指南

          本文链接:https://www.haomeiwen.com/subject/qluscltx.html