feat(build): add --inline-vue flag to optionally disable externalization of Vue (#4261)

Resolves #4055.
This commit is contained in:
Roman Pavlov
2019-08-19 16:05:46 +03:00
committed by Haoqun Jiang
parent 497fd5aa1f
commit 86f4f5fceb
10 changed files with 149 additions and 10 deletions

View File

@@ -15,6 +15,12 @@ App is the default build target. In this mode:
::: tip Note on Vue Dependency
In lib mode, Vue is *externalized*. This means the bundle will not bundle Vue even if your code imports Vue. If the lib is used via a bundler, it will attempt to load Vue as a dependency through the bundler; otherwise, it falls back to a global `Vue` variable.
To avoid this behavior provide `--inline-vue` flag to `build` command.
```
vue-cli-service build --target lib --inline-vue
```
:::
You can build a single entry as a library using
@@ -68,6 +74,12 @@ Web Component mode does not support IE11 and below. [More details](https://githu
::: tip Note on Vue Dependency
In web component mode, Vue is *externalized.* This means the bundle will not bundle Vue even if your code imports Vue. The bundle will assume `Vue` is available on the host page as a global variable.
To avoid this behavior provide `--inline-vue` flag to `build` command.
```
vue-cli-service build --target wc --inline-vue
```
:::
You can build a single entry as a web component using

View File

@@ -75,6 +75,7 @@ Options:
--modern build app targeting modern browsers with auto fallback
--target app | lib | wc | wc-async (default: app)
--formats list of output formats for library builds (default: commonjs,umd,umd-min)
--inline-vue include the Vue module in the final bundle of library or web component target
--name name for lib or web-component mode (default: "name" in package.json or entry filename)
--no-clean do not remove the dist directory before building the project
--report generate report.html to help analyze bundle content

View File

@@ -19,6 +19,12 @@
::: tip Примечание о зависимости Vue
В режиме библиотеки Vue *экстернализируется*. Это означает, что сборка не будет содержать Vue, даже если ваш код его импортирует. Если библиотека используется через сборщик, он должен попытаться загрузить Vue в качестве зависимости через сборщик; в противном случае, он должен вернуться к глобальной переменной `Vue`.
Чтобы избежать экстернализиции Vue установите флаг `--inline-vue` для команды `build`.
```
vue-cli-service build --target lib --inline-vue
```
:::
Вы можете запустить сборку одной точки входа в качестве библиотеки с помощью:
@@ -72,6 +78,12 @@ module.exports = {
::: tip Примечание зависимости Vue
В режиме веб-компонентов Vue *экстернализируется.* Это означает, что сборка не будет содержать Vue, даже если ваш код его импортирует. Сборка будет подразумевать, что `Vue` доступен на странице в качестве глобальной переменной.
Чтобы избежать экстернализиции Vue установите флаг `--inline-vue` для команды `build`.
```
vue-cli-service build --target wc --inline-vue
```
:::
Вы можете запустить сборку одной точки входа в качестве веб-компонента с помощью:

View File

@@ -67,6 +67,7 @@ npx vue-cli-service serve
--dest определить каталог сборки (по умолчанию: dist)
--modern собирать приложение для современных браузеров с авто-фоллбэком для старых
--target app | lib | wc | wc-async (по умолчанию: app)
--inline-vue включить Vue в содержимое сборки библиотеки или веб-компонента
--name имя библиотеки или режим веб-компонента (по умолчанию: "name" в package.json или имя файла точки входа)
--no-clean не удалять каталог dist перед сборкой проекта
--report сгенерировать report.html для анализа содержимого сборки

View File

@@ -214,3 +214,59 @@ test('build as lib without --name and --filename options (default to package nam
expect(project.has('dist/test-lib.umd.js')).toBe(true)
expect(project.has('dist/test-lib.umd.min.js')).toBe(true)
})
test('build as lib with --inline-vue', async () => {
const project = await create('build-lib-inline-vue', defaultPreset)
await project.write('src/main-lib.js', `
import Vue from 'vue'
import App from "./components/App.vue"
document.addEventListener("DOMContentLoaded", function() {
new Vue({
render: h => h(App),
}).$mount('body');
});
`)
await project.write('src/components/App.vue', `
<template>
<div>{{ message }}<div>
</template>
<script>
export default {
data() {
return {
message: 'Hello from Lib'
}
},
}
</script>
`)
const { stdout } = await project.run('vue-cli-service build --target lib --inline-vue --name testLib src/main-lib.js')
expect(stdout).toMatch('Build complete.')
expect(project.has('dist/demo.html')).toBe(true)
expect(project.has('dist/testLib.common.js')).toBe(true)
expect(project.has('dist/testLib.umd.js')).toBe(true)
expect(project.has('dist/testLib.umd.min.js')).toBe(true)
const port = await portfinder.getPortPromise()
server = createServer({ root: path.join(project.dir, 'dist') })
await new Promise((resolve, reject) => {
server.listen(port, err => {
if (err) return reject(err)
resolve()
})
})
const launched = await launchPuppeteer(`http://localhost:${port}/demo.html`)
browser = launched.browser
page = launched.page
const divText = await page.evaluate(() => {
return document.querySelector('div').textContent
})
expect(divText).toMatch('Hello from Lib')
})

View File

@@ -89,6 +89,61 @@ test('build as single wc', async () => {
expect(h1Text).toMatch('Welcome to Your Vue.js App')
})
test('build as wc with --inline-vue', async () => {
const project = await create('build-wc-inline-vue', defaultPreset)
await project.write('src/main-wc.js', `
import Vue from 'vue'
import App from "./components/App.vue"
document.addEventListener("DOMContentLoaded", function() {
new Vue({
render: h => h(App),
}).$mount('body');
});
`)
await project.write('src/components/App.vue', `
<template>
<div>{{ message }}<div>
</template>
<script>
export default {
data() {
return {
message: 'Hello from Wc'
}
},
}
</script>
`)
const { stdout } = await project.run('vue-cli-service build --target wc --inline-vue --name single-wc src/main-wc.js')
expect(stdout).toMatch('Build complete.')
expect(project.has('dist/demo.html')).toBe(true)
expect(project.has('dist/single-wc.js')).toBe(true)
expect(project.has('dist/single-wc.min.js')).toBe(true)
const port = await portfinder.getPortPromise()
server = createServer({ root: path.join(project.dir, 'dist') })
await new Promise((resolve, reject) => {
server.listen(port, err => {
if (err) return reject(err)
resolve()
})
})
const launched = await launchPuppeteer(`http://localhost:${port}/demo.html`)
browser = launched.browser
page = launched.page
const divText = await page.evaluate(() => {
return document.querySelector('div').textContent
})
expect(divText).toMatch('Hello from Wc')
})
afterEach(async () => {
if (browser) {
await browser.close()

View File

@@ -22,6 +22,7 @@ const args = require('minimist')(rawArgv, {
'modern',
'report',
'report-json',
'inline-vue',
'watch',
// serve
'open',

View File

@@ -29,6 +29,7 @@ module.exports = (api, options) => {
'--modern': `build app targeting modern browsers with auto fallback`,
'--no-unsafe-inline': `build app without introducing inline scripts`,
'--target': `app | lib | wc | wc-async (default: ${defaults.target})`,
'--inline-vue': 'include the Vue module in the final bundle of library or web component target',
'--formats': `list of output formats for library builds (default: ${defaults.formats})`,
'--name': `name for lib or web-component mode (default: "name" in package.json or entry filename)`,
'--filename': `file name for output, only usable for 'lib' target (default: value of --name)`,

View File

@@ -1,7 +1,7 @@
const fs = require('fs')
const path = require('path')
module.exports = (api, { entry, name, formats, filename }, options) => {
module.exports = (api, { entry, name, formats, filename, 'inline-vue': inlineVue }, options) => {
const { log, error } = require('@vue/cli-shared-utils')
const abort = msg => {
log()
@@ -97,11 +97,13 @@ module.exports = (api, { entry, name, formats, filename }, options) => {
rawConfig.externals = [
...(Array.isArray(rawConfig.externals) ? rawConfig.externals : [rawConfig.externals]),
{
vue: {
commonjs: 'vue',
commonjs2: 'vue',
root: 'Vue'
}
...(inlineVue || {
vue: {
commonjs: 'vue',
commonjs2: 'vue',
root: 'Vue'
}
})
}
].filter(Boolean)

View File

@@ -2,7 +2,7 @@ const path = require('path')
const webpack = require('webpack')
const { resolveEntry, fileToComponentName } = require('./resolveWcEntry')
module.exports = (api, { target, entry, name }) => {
module.exports = (api, { target, entry, name, 'inline-vue': inlineVue }) => {
// Disable CSS extraction and turn on CSS shadow mode for vue-style-loader
process.env.VUE_CLI_CSS_SHADOW_MODE = true
@@ -99,9 +99,7 @@ module.exports = (api, { target, entry, name }) => {
// externalize Vue in case user imports it
rawConfig.externals = [
...(Array.isArray(rawConfig.externals) ? rawConfig.externals : [rawConfig.externals]),
{
vue: 'Vue'
}
{ ...(inlineVue || { vue: 'Vue' }) }
].filter(Boolean)
const entryName = `${libName}${minify ? `.min` : ``}`