Subscribe on changes!

After using the script tag to introduce the vue component, the function api fails and the page cannot be rendered

avatar
Nov 12th 2020

Version

3.0.0

Reproduction link

https://github.com/nxl3477/function-api-umd-demo

Steps to reproduce

Directory Structure

.
├── components
│   └── test1
├── frame
│   ├── App.vue
│   ├── main.js
│   └── views
└── shims-vue.d.ts

'components/test1.vue'

<template>
  <div>
    <div class="box">
      function api count: {{count}}
    </div>
    <button @click="inc">functionAPI</button><br/>
  </div>
</template>
<script>
import { ref, computed, defineComponent, reactive, watchEffect } from 'vue'
export default defineComponent({
  setup() {
    const count = ref(0)
    const inc = () => {
      count.value  
    }
    return {
      count,
      inc
    }
  }
})
</script>
<style>
.box {
  color: skyblue;
}
</style>

'frame/main.js'

import { createApp } from 'vue'
import App from './App.vue'

const loadScript = (src, crossorigin=true) => new Promise(function (resolve, reject) {
  const $script = document.createElement('script')
  $script.onload = function () {
    resolve(this)
  }
  $script.onerror = function (e) {
    reject(e)
  }
  crossorigin && $script.setAttribute('crossorigin', 'anonymous')
  $script.src = src
  document.body.appendChild($script)
})

const loadComponent = (name) => loadScript(`/${name}.js`)

;(async () => {
  await loadComponent('test1')
  const app = createApp(App)
  app.use(window['test1'].default)
  app.mount('#app')
})();

'frame/views/combine.vue'

<template>
  <div>
    <component :is="'test1'"></component>
  </div>
</template>
<script>
export default {
  setup() {
  }
}
</script>
<style lang="less">
* {
  margin: 0;
  padding: 0;
}
</style>

vue.config.js

const path = require('path')
const fs = require("fs");

const filesDirs = fs.readdirSync(path.join(__dirname, './src/components'))
const _entry = filesDirs.reduce((total, dir) => {
  const _path = path.join(__dirname, './src/components', dir)
  const isFolder = fs.statSync(_path)
  if( isFolder.isDirectory() ) {
    const { name, main } = require(path.join(__dirname, `./src/components/${dir}/project.json`))
    total[name] = path.join(__dirname, `./src/components/${dir}/${main}`)
  }
  return total
}, {})

module.exports = {
  pages: {
    index: {
      // page 的入口
      entry: 'src/frame/main.js',
    }
  },
  configureWebpack: {
    entry: _entry,
    output: {
      path: path.join(__dirname, './dist'),
      filename: "[name].js",
      publicPath: '/',
      library: '[name]',
      libraryTarget: 'umd',
    },
  },
  devServer: {
    port: 9001,
    disableHostCheck: true,
    open: true
  }
}

'components/test1.vue' is packaged as an umd format file, imported in'frame/main.js' and rendered in'frame/views/combine.vue' Able to show the initial state of the component, but subsequent updates of the function api will be invalid

The github address of the demo has been put, and you can view it by'npm run serve'

I don’t know if I haven’t enabled a certain parameter or a bug, but this feature is very important to me

What is expected?

Each click count increases by 1

What is actually happening?

Only the inc method in test1.vue is triggered, but the page is not rendered

avatar
Nov 12th 2020

I can't run your code right now, but presumably, you have a second copy of Vue bundled within the tests umd file.

avatar
Nov 13th 2020

Thank you, you are right, I tried to filter vue in the packaging, and introduced in index.html by cdn to solve this problem

vue.config.js

externals: {
      vue: 'Vue',
    },

index.html

<script src="https://unpkg.com/vue@next"></script>