diff --git a/README.md b/README.md
index acce74a413..86dc20a48e 100644
--- a/README.md
+++ b/README.md
@@ -11,7 +11,7 @@ See the [Vue Docs Writing Guide](https://v3.vuejs.org/guide/writing-guide.html)
1. Clone repository
```bash
-git clone git@github.com:vuejs/docs-next.git
+git clone https://github.com/vuejs/docs.git
```
2. Install dependencies
diff --git a/package.json b/package.json
index f7e4932033..e17c9463ed 100644
--- a/package.json
+++ b/package.json
@@ -14,7 +14,6 @@
"@docsearch/css": "^1.0.0-alpha.27",
"@docsearch/js": "^1.0.0-alpha.27",
"algoliasearch": "^4.4.0",
- "axios": "^0.21.1",
"intersection-observer": "^0.11.0",
"showdown": "^1.9.1"
}
diff --git a/src/.vuepress/components/community/team/emeriti.js b/src/.vuepress/components/community/team/emeriti.js
index fe7e1fb9f2..d1c343bf56 100644
--- a/src/.vuepress/components/community/team/emeriti.js
+++ b/src/.vuepress/components/community/team/emeriti.js
@@ -1,6 +1,27 @@
import { shuffle } from 'lodash'
export default shuffle([
+ {
+ name: 'Sarah Drasner',
+ city: 'Denver, CO, USA',
+ languages: ['en'],
+ work: {
+ role: 'Director of Engineering, Core Developer Web',
+ org: 'Google',
+ orgUrl: 'https://google.com/',
+ },
+ github: 'sdras',
+ twitter: 'sarah_edo',
+ codepen: 'sdras',
+ reposOfficial: ['vuejs.org'],
+ reposPersonal: [
+ 'vue-vscode-snippets',
+ 'intro-to-vue',
+ 'vue-vscode-extensionpack',
+ 'ecommerce-netlify',
+ ],
+ links: ['https://sarah.dev/'],
+ },
{
name: 'Chris Fritz',
title: 'Good Word Putter-Togetherer',
@@ -9,11 +30,9 @@ export default shuffle([
github: 'chrisvfritz',
twitter: 'chrisvfritz',
work: {
- role: 'Educator & Consultant'
+ role: 'Educator & Consultant',
},
- reposPersonal: [
- 'vue-enterprise-boilerplate'
- ]
+ reposPersonal: ['vue-enterprise-boilerplate'],
},
{
name: 'Blake Newman',
@@ -23,10 +42,10 @@ export default shuffle([
work: {
role: 'Software Engineer',
org: 'Attest',
- orgUrl: 'https://www.askattest.com/'
+ orgUrl: 'https://www.askattest.com/',
},
github: 'blake-newman',
- twitter: 'blakenewman'
+ twitter: 'blakenewman',
},
{
name: 'kingwl',
@@ -36,12 +55,10 @@ export default shuffle([
work: {
role: 'Software Development Engineer',
org: 'Chaitin',
- orgUrl: 'https://chaitin.cn/'
+ orgUrl: 'https://chaitin.cn/',
},
github: 'kingwl',
- reposOfficial: [
- 'vue'
- ]
+ reposOfficial: ['vue'],
},
{
name: 'Alan Song',
@@ -51,12 +68,10 @@ export default shuffle([
work: {
role: 'Cofounder',
org: 'Futurenda',
- orgUrl: 'https://www.futurenda.com/'
+ orgUrl: 'https://www.futurenda.com/',
},
github: 'fnlctrl',
- reposOfficial: [
- 'vue-router'
- ]
+ reposOfficial: ['vue-router'],
},
{
name: 'defcc',
@@ -66,8 +81,8 @@ export default shuffle([
github: 'defcc',
work: {
org: 'zbj.com',
- orgUrl: 'http://www.zbj.com/'
- }
+ orgUrl: 'http://www.zbj.com/',
+ },
},
{
name: 'gebilaoxiong',
@@ -77,8 +92,8 @@ export default shuffle([
github: 'gebilaoxiong',
work: {
org: 'zbj.com',
- orgUrl: 'http://www.zbj.com/'
- }
+ orgUrl: 'http://www.zbj.com/',
+ },
},
{
name: 'Denis Karabaza',
@@ -90,8 +105,8 @@ export default shuffle([
work: {
role: 'Software Engineer',
org: 'Neolant',
- orgUrl: 'http://neolant.ru/'
- }
+ orgUrl: 'http://neolant.ru/',
+ },
},
{
name: 'Edd Yerburgh',
@@ -101,16 +116,10 @@ export default shuffle([
github: 'eddyerburgh',
twitter: 'EddYerburgh',
work: {
- role: 'Full Stack Developer'
+ role: 'Full Stack Developer',
},
- reposOfficial: [
- 'vue-test-utils'
- ],
- reposPersonal: [
- 'avoriaz'
- ],
- links: [
- 'https://www.eddyerburgh.me/'
- ]
- }
+ reposOfficial: ['vue-test-utils'],
+ reposPersonal: ['avoriaz'],
+ links: ['https://www.eddyerburgh.me/'],
+ },
])
diff --git a/src/.vuepress/components/community/team/members.js b/src/.vuepress/components/community/team/members.js
index c4a6977093..d374aa1541 100644
--- a/src/.vuepress/components/community/team/members.js
+++ b/src/.vuepress/components/community/team/members.js
@@ -10,11 +10,11 @@ const members = [
twitter: 'youyuxi',
work: {
role: 'Creator',
- org: 'Vue.js'
+ org: 'Vue.js',
},
reposOfficial: ['vuejs/*', 'vuejs-templates/*'],
- links: ['https://www.patreon.com/evanyou']
- }
+ links: ['https://www.patreon.com/evanyou'],
+ },
].concat(
shuffle([
{
@@ -25,11 +25,11 @@ const members = [
github: 'posva',
twitter: 'posva',
work: {
- role: 'Freelance Developer & Consultant'
+ role: 'Freelance Developer & Consultant',
},
reposOfficial: ['vuefire', 'vue-router'],
reposPersonal: ['vuex-mock-store', 'vue-promised', 'vue-motion'],
- links: ['https://www.patreon.com/posva']
+ links: ['https://www.patreon.com/posva'],
},
{
name: 'Sodatea',
@@ -37,7 +37,7 @@ const members = [
languages: ['zh', 'en'],
github: 'sodatea',
twitter: 'haoqunjiang',
- reposOfficial: ['vue-cli', 'vue-loader']
+ reposOfficial: ['vue-cli', 'vue-loader'],
},
{
name: 'Pine Wu',
@@ -46,9 +46,9 @@ const members = [
github: 'octref',
twitter: 'octref',
work: {
- role: 'Nomad'
+ role: 'Nomad',
},
- reposOfficial: ['vetur']
+ reposOfficial: ['vetur'],
},
{
name: 'Jinjiang',
@@ -61,8 +61,8 @@ const members = [
'vue-a11y-utils',
'vue-mark-display',
'mark2slides',
- 'vue-keyboard-over'
- ]
+ 'vue-keyboard-over',
+ ],
},
{
name: 'Katashin',
@@ -72,12 +72,12 @@ const members = [
work: {
role: 'Software Engineer',
org: 'ClassDo',
- orgUrl: 'https://classdo.com/'
+ orgUrl: 'https://classdo.com/',
},
github: 'ktsn',
twitter: 'ktsn',
reposOfficial: ['vuex', 'vue-class-component'],
- reposPersonal: ['vue-designer']
+ reposPersonal: ['vue-designer'],
},
{
name: 'Kazupon',
@@ -89,7 +89,7 @@ const members = [
work: {
role: 'Engineer',
org: 'PLAID, Inc.',
- orgUrl: 'https://plaid.co.jp/'
+ orgUrl: 'https://plaid.co.jp/',
},
reposOfficial: ['vuejs.org', 'jp.vuejs.org'],
reposPersonal: [
@@ -98,9 +98,9 @@ const members = [
'vue-i18n-loader',
'eslint-plugin-vue-i18n',
'vue-i18n-extensions',
- 'vue-cli-plugin-p11n'
+ 'vue-cli-plugin-p11n',
],
- links: ['https://www.patreon.com/kazupon']
+ links: ['https://www.patreon.com/kazupon'],
},
{
name: 'Rahul Kadyan',
@@ -110,13 +110,13 @@ const members = [
work: {
role: 'Software Engineer',
org: 'Grammarly',
- orgUrl: 'https://grammarly.com/'
+ orgUrl: 'https://grammarly.com/',
},
github: 'znck',
twitter: 'znck0',
reposOfficial: ['rollup-plugin-vue', 'vue-next'],
reposPersonal: ['vue-developer-experience', 'prop-types', 'grammarly'],
- links: ['https://znck.me/']
+ links: ['https://znck.me/'],
},
{
name: 'Linusborg',
@@ -127,7 +127,7 @@ const members = [
twitter: 'Linus_Borg',
reposOfficial: ['vuejs/*'],
reposPersonal: ['portal-vue'],
- links: ['https://forum.vuejs.org/']
+ links: ['https://forum.vuejs.org/'],
},
{
name: 'Guillaume Chau',
@@ -139,37 +139,16 @@ const members = [
work: {
role: 'Frontend Developer',
org: 'Livestorm',
- orgUrl: 'https://livestorm.co/'
+ orgUrl: 'https://livestorm.co/',
},
reposOfficial: ['vue-devtools', 'vue-cli', 'vue-curated'],
reposPersonal: [
'vue-apollo',
'vue-meteor',
'vue-virtual-scroller',
- 'v-tooltip'
- ],
- links: ['http://patreon.com/akryum']
- },
- {
- name: 'Sarah Drasner',
- city: 'Denver, CO, USA',
- languages: ['en'],
- work: {
- role: 'VP of Developer Experience',
- org: 'Netlify',
- orgUrl: 'https://url.netlify.com/HJ8X2mxP8'
- },
- github: 'sdras',
- twitter: 'sarah_edo',
- codepen: 'sdras',
- reposOfficial: ['vuejs.org'],
- reposPersonal: [
- 'vue-vscode-snippets',
- 'intro-to-vue',
- 'vue-vscode-extensionpack',
- 'ecommerce-netlify'
+ 'v-tooltip',
],
- links: ['https://sarah.dev/']
+ links: ['http://patreon.com/akryum'],
},
{
name: 'Damian Dulisz',
@@ -179,10 +158,10 @@ const members = [
github: 'shentao',
twitter: 'DamianDulisz',
work: {
- role: 'Consultant'
+ role: 'Consultant',
},
reposOfficial: ['news.vuejs.org'],
- reposPersonal: ['shentao/vue-multiselect', 'shentao/vue-global-events']
+ reposPersonal: ['shentao/vue-multiselect', 'shentao/vue-global-events'],
},
{
name: 'Michał Sajnóg',
@@ -193,10 +172,10 @@ const members = [
work: {
role: 'Senior Frontend Developer / Team Leader',
org: 'Netguru',
- orgUrl: 'https://netguru.co/'
+ orgUrl: 'https://netguru.co/',
},
reposOfficial: ['eslint-plugin-vue', 'vue-devtools'],
- reposPersonal: ['vue-computed-helpers', 'vue-content-placeholders']
+ reposPersonal: ['vue-computed-helpers', 'vue-content-placeholders'],
},
{
name: 'GU Yiling',
@@ -205,7 +184,7 @@ const members = [
work: {
role: 'Senior web developer',
org: 'Baidu, inc.',
- orgUrl: 'https://www.baidu.com/'
+ orgUrl: 'https://www.baidu.com/',
},
github: 'Justineo',
twitter: '_justineo',
@@ -213,8 +192,8 @@ const members = [
reposPersonal: [
'Justineo/vue-awesome',
'ecomfe/vue-echarts',
- 'ecomfe/veui'
- ]
+ 'ecomfe/veui',
+ ],
},
{
name: 'ULIVZ',
@@ -223,11 +202,11 @@ const members = [
work: {
role: 'Senior Frontend Developer',
org: 'AntFinancial',
- orgUrl: 'https://www.antfin.com/'
+ orgUrl: 'https://www.antfin.com/',
},
github: 'ulivz',
twitter: '_ulivz',
- reposOfficial: ['vuepress']
+ reposOfficial: ['vuepress'],
},
{
name: 'Phan An',
@@ -239,25 +218,25 @@ const members = [
work: {
role: 'Engineering Team Lead',
org: 'InterNations',
- orgUrl: 'https://www.internations.org/'
+ orgUrl: 'https://www.internations.org/',
},
reposOfficial: ['vuejs.org'],
reposPersonal: ['vuequery', 'vue-google-signin-button'],
- links: ['https://vi.vuejs.org/', 'https://phanan.net/']
+ links: ['https://vi.vuejs.org/', 'https://phanan.net/'],
},
{
name: 'Natalia Tepluhina',
title: 'Fox Tech Guru',
- city: 'Kyiv, Ukraine',
+ city: 'Amsterdam, Netherlands',
languages: ['uk', 'ru', 'en'],
reposOfficial: ['vuejs.org', 'vue-cli'],
work: {
role: 'Staff Frontend Engineer',
org: 'GitLab',
- orgUrl: 'https://gitlab.com/'
+ orgUrl: 'https://gitlab.com/',
},
github: 'NataliaTepluhina',
- twitter: 'N_Tepluhina'
+ twitter: 'N_Tepluhina',
},
{
name: 'Yosuke Ota',
@@ -268,9 +247,9 @@ const members = [
work: {
role: 'Lead Web Engineer',
org: 'Future Corporation',
- orgUrl: 'https://www.future.co.jp/'
+ orgUrl: 'https://www.future.co.jp/',
},
- reposOfficial: ['eslint-plugin-vue']
+ reposOfficial: ['eslint-plugin-vue'],
},
{
name: 'Ben Hong',
@@ -278,12 +257,12 @@ const members = [
languages: ['en', 'zh'],
work: {
role: 'Developer Experience (DX) Engineer',
- org: 'Cypress.io'
+ org: 'Cypress.io',
},
reposOfficial: ['vuejs.org', 'vuepress', 'vuejs/events'],
github: 'bencodezen',
twitter: 'bencodezen',
- links: ['https://bencodezen.io/']
+ links: ['https://bencodezen.io/'],
},
{
name: 'Kia King Ishii',
@@ -293,12 +272,12 @@ const members = [
work: {
role: 'Tech Talent',
org: 'Global Brain',
- orgUrl: 'https://globalbrains.com/'
+ orgUrl: 'https://globalbrains.com/',
},
github: 'kiaking',
twitter: 'KiaKing85',
reposOfficial: ['vuex'],
- reposPersonal: ['vuex-orm/*']
+ reposPersonal: ['vuex-orm/*'],
},
{
name: 'Anthony Fu',
@@ -308,8 +287,8 @@ const members = [
twitter: 'antfu7',
reposOfficial: ['composition-api'],
reposPersonal: ['vueuse', 'vue-demi', 'vue-reactivity/*'],
- links: ['https://antfu.me/']
- }
+ links: ['https://antfu.me/'],
+ },
])
)
diff --git a/src/.vuepress/components/community/team/partners.js b/src/.vuepress/components/community/team/partners.js
index 5e1dd97968..2caeb13211 100644
--- a/src/.vuepress/components/community/team/partners.js
+++ b/src/.vuepress/components/community/team/partners.js
@@ -211,15 +211,14 @@ export default shuffle([
github: 'rstoenescu',
twitter: 'quasarframework',
work: {
- role: 'Developer',
+ role: 'Author',
org: 'Quasar Framework',
- orgUrl: 'http://quasar-framework.org/'
+ orgUrl: 'http://quasar.dev/'
},
reposPersonal: [
- 'quasarframework/quasar',
- 'quasarframework/quasar-cli',
- 'quasarframework/quasar-play'
- ]
+ 'quasarframework/quasar'
+ ],
+ links: ['https://quasar.dev/']
},
{
name: 'Jilson Thomas',
diff --git a/src/.vuepress/components/community/themes/theme-data.js b/src/.vuepress/components/community/themes/theme-data.js
index 02d3e4d344..7e0d9ffc1b 100644
--- a/src/.vuepress/components/community/themes/theme-data.js
+++ b/src/.vuepress/components/community/themes/theme-data.js
@@ -127,63 +127,98 @@ export default [
},
{
name: 'PrimeVue',
- description: `The open-source UI component library [PrimeVue](https://www.primefaces.org/primevue/#/?af_id=4218) offers over 50 flexible components to build your apps with! They have a ton of different component themes and Vue-CLI application templates available to get the look&feel that suits you best.`,
+ description: `The open-source UI component library [PrimeVue](https://www.primefaces.org/primevue/#/?af_id=4218) offers over 80 flexible components to build your apps with! They have a ton of different component themes and Vue-CLI application templates available to get the look & feel that suits you best.`,
seeMoreUrl: 'https://www.primefaces.org/primevue/#/?af_id=4218',
products: [
{
- name: 'Sapphire',
+ name: 'Sakai',
+ price: 0,
+ description: 'Free Admin Template',
+ url: 'https://www.primefaces.org/sakai-vue/#/?af_id=4218',
+ image: 'https://www.primefaces.org/vue-templates/sakai.jpg'
+ },
+ {
+ name: 'Atlantis',
+ price: 59,
+ description: 'Premium Admin Template',
+ url: 'https://www.primefaces.org/layouts/atlantis-vue?af_id=4218',
+ image: 'https://www.primefaces.org/vue-templates/atlantis.jpg'
+ },
+ {
+ name: 'Freya',
+ price: 59,
+ description: 'Premium Admin Template',
+ url: 'https://www.primefaces.org/layouts/freya-vue?af_id=4218',
+ image: 'https://www.primefaces.org/vue-templates/freya.jpg'
+ },
+ {
+ name: 'Ultima',
price: 79,
description: 'Material Design Admin Template',
+ url: 'https://www.primefaces.org/layouts/ultima-vue?af_id=4218',
+ image: 'https://www.primefaces.org/vue-templates/ultima.jpg'
+ },
+ {
+ name: 'Diamond',
+ price: 59,
+ description: 'PrimeOne Design Admin Template',
+ url: 'https://www.primefaces.org/layouts/diamond-vue?af_id=4218',
+ image: 'https://www.primefaces.org/vue-templates/diamond.jpg'
+ },
+ {
+ name: 'Sapphire',
+ price: 49,
+ description: 'Material Design Admin Template',
url: 'https://www.primefaces.org/layouts/sapphire-vue?af_id=4218',
image: 'https://www.primefaces.org/vue-templates/sapphire.jpg'
},
{
name: 'Avalon',
- price: 79,
+ price: 49,
description: 'Bootstrap Inspired Admin Template',
url: 'https://www.primefaces.org/layouts/avalon-vue?af_id=4218',
image: 'https://www.primefaces.org/vue-templates/avalon.jpg'
},
{
name: 'Serenity',
- price: 79,
+ price: 49,
description: 'Material Design Admin Template',
url: 'https://www.primefaces.org/layouts/serenity-vue?af_id=4218',
image: 'https://www.primefaces.org/vue-templates/serenity.jpg'
},
{
name: 'Apollo',
- price: 79,
+ price: 49,
description: 'Admin Template with a Dark Mode',
url: 'https://www.primefaces.org/layouts/apollo-vue?af_id=4218',
image: 'https://www.primefaces.org/vue-templates/apollo.jpg'
},
{
name: 'Babylon',
- price: 79,
+ price: 49,
description: 'Admin Template with Extensive Options',
url: 'https://www.primefaces.org/layouts/babylon-vue?af_id=4218',
image: 'https://www.primefaces.org/vue-templates/babylon.jpg'
},
{
name: 'Roma',
- price: 59,
+ price: 39,
description: 'Admin Template with a Clean Design System',
url: 'https://www.primefaces.org/layouts/roma-vue?af_id=4218',
image: 'https://www.primefaces.org/vue-templates/roma.jpg'
},
{
- name: 'Sigma',
- price: 0,
- description: 'Free Admin Template',
- url: 'https://www.primefaces.org/sigma-vue/#/?af_id=4218',
- image: 'https://www.primefaces.org/vue-templates/sigma.jpg'
+ name: 'Prestige',
+ price: 39,
+ description: 'Highly Customizable Admin Template',
+ url: 'https://www.primefaces.org/layouts/prestige-vue?af_id=4218',
+ image: 'https://www.primefaces.org/vue-templates/prestige.jpg'
}
]
},
{
name: 'Flatlogic',
- description: `Check the admin dashboards templates built by our partners from [Flatlogic](https://flatlogic.com/templates?ref=x-fdkuTAVW). With these themes you can see how real applications is built. Additionally these templates will help you to start a new application and save you time and money.`,
+ description: `Check out the admin dashboard templates built by our partners from [Flatlogic](https://flatlogic.com/templates?ref=x-fdkuTAVW). With these themes you can see how real applications are built. Additionally, these templates will help you to start a new application and save you time and money.`,
seeMoreUrl: 'https://flatlogic.com/templates?ref=x-fdkuTAVW',
products: [
{
diff --git a/src/.vuepress/components/guide/contributing/translations-data.js b/src/.vuepress/components/guide/contributing/translations-data.js
new file mode 100644
index 0000000000..aef5d0ef29
--- /dev/null
+++ b/src/.vuepress/components/guide/contributing/translations-data.js
@@ -0,0 +1,22 @@
+export const labels = {
+ language: 'Language',
+ github: 'GitHub',
+ lastCommit: 'Last commit',
+ last90Days: 'Last 90 days',
+ loadDetails: 'Load Details',
+ commits: 'commits',
+ loading: 'Loading...'
+}
+
+// Repos are in alphabetical order by the language code
+// You may need to clear your sessionStorage when adding a new item to this list
+export const repos = [
+ { lang: 'en-us', owner: 'vuejs', repo: 'docs', branch: 'master', url: 'https://v3.vuejs.org/' },
+ { lang: 'fr', owner: 'demahom18', repo: 'docs-next', branch: 'master', url: 'https://vue3-fr.netlify.app/' },
+ { lang: 'id', owner: 'vuejs-id', repo: 'docs-next', branch: 'indonesian', url: 'https://v3-vuejsid-docs.netlify.app/' },
+ { lang: 'ja', owner: 'vuejs-jp', repo: 'ja.vuejs.org', branch: 'lang-ja', url: 'https://v3.ja.vuejs.org/' },
+ { lang: 'ko', owner: 'vuejs-kr', repo: 'docs-next', branch: 'rootKoKr', url: 'https://v3.ko.vuejs.org/' },
+ { lang: 'pt-br', owner: 'vuejs-br', repo: 'docs-next', branch: 'master', url: 'https://vuejsbr-docs-next.netlify.app/' },
+ { lang: 'ru', owner: 'translation-gang', repo: 'docs-next', branch: 'master', url: 'https://v3.ru.vuejs.org/' },
+ { lang: 'zh-cn', owner: 'vuejs', repo: 'docs-next-zh-cn', branch: 'master', url: 'https://v3.cn.vuejs.org/' }
+]
diff --git a/src/.vuepress/components/guide/contributing/translations.vue b/src/.vuepress/components/guide/contributing/translations.vue
new file mode 100644
index 0000000000..ad041b1155
--- /dev/null
+++ b/src/.vuepress/components/guide/contributing/translations.vue
@@ -0,0 +1,206 @@
+
+
+
+
+ {{ labels.language }}
+ {{ labels.github }}
+ {{ labels.lastCommit }}
+ {{ labels.last90Days }}
+
+
+
+
+
+
+ {{ lang }}
+
+
+ {{ lang }}
+
+
+
+ {{ owner }}/{{ repo }}
+
+
+
+ {{ labels.loadDetails }}
+
+
+
+ {{ date }}
+
+ {{ count }}{{ count === 100 ? '+' : '' }}
+
+ {{ labels.commits }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/.vuepress/components/support/Coins.vue b/src/.vuepress/components/support/Coins.vue
index e239bc2130..d274bbd33c 100644
--- a/src/.vuepress/components/support/Coins.vue
+++ b/src/.vuepress/components/support/Coins.vue
@@ -54,13 +54,18 @@ export default {
}
}
}),
- created () {
- if (typeof window === 'undefined') return
+ mounted () {
+ this.updateType()
- this.type = window.location.hash.slice(1)
- window.addEventListener('hashchange', function () {
+ window.addEventListener('hashchange', this.updateType)
+ },
+ beforeDestroy () {
+ window.removeEventListener('hashchange', this.updateType)
+ },
+ methods: {
+ updateType () {
this.type = window.location.hash.slice(1)
- })
+ }
}
}
@@ -74,7 +79,12 @@ export default {
a {
margin: 20px;
- color: $medium;
+ color: $textColor;
+ text-decoration: none !important;
+
+ &:hover {
+ color: $accentColor;
+ }
}
svg {
diff --git a/src/.vuepress/config.js b/src/.vuepress/config.js
index 45d81474ac..3370925c2c 100644
--- a/src/.vuepress/config.js
+++ b/src/.vuepress/config.js
@@ -6,7 +6,8 @@ const sidebar = {
children: [
'/cookbook/',
'/cookbook/editable-svg-icons',
- '/cookbook/debugging-in-vscode'
+ '/cookbook/debugging-in-vscode',
+ '/cookbook/automatic-global-registration-of-base-components'
]
}
],
@@ -79,6 +80,7 @@ const sidebar = {
title: 'Advanced Guides',
collapsable: false,
children: [
+ '/guide/web-components',
{
title: 'Reactivity',
children: [
@@ -105,7 +107,12 @@ const sidebar = {
{
title: 'Scaling Up',
collapsable: false,
- children: ['/guide/routing', '/guide/state-management', '/guide/ssr']
+ children: [
+ '/guide/routing',
+ '/guide/state-management',
+ '/guide/ssr',
+ '/guide/security'
+ ]
},
{
title: 'Accessibility',
@@ -147,10 +154,33 @@ const sidebar = {
children: [
'/api/basic-reactivity',
'/api/refs-api',
- '/api/computed-watch-api'
+ '/api/computed-watch-api',
+ '/api/effect-scope',
]
},
- '/api/composition-api'
+ '/api/composition-api',
+ {
+ title: 'Single File Components',
+ collapsable: false,
+ children: [
+ {
+ title: 'Spec',
+ path: '/api/sfc-spec'
+ },
+ {
+ title: 'Tooling',
+ path: '/api/sfc-tooling'
+ },
+ {
+ title: '
diff --git a/src/.vuepress/theme/components/NavLink.vue b/src/.vuepress/theme/components/NavLink.vue
index 4ccd13d88b..3f4754f4d1 100644
--- a/src/.vuepress/theme/components/NavLink.vue
+++ b/src/.vuepress/theme/components/NavLink.vue
@@ -22,8 +22,26 @@
diff --git a/src/.vuepress/theme/components/Newsletter.vue b/src/.vuepress/theme/components/Newsletter.vue
index 9967cbceb7..c16a2bdb44 100644
--- a/src/.vuepress/theme/components/Newsletter.vue
+++ b/src/.vuepress/theme/components/Newsletter.vue
@@ -85,6 +85,7 @@
box-sizing: border-box;
border: 1px solid currentColor;
appearance: none;
+ cursor: pointer;
}
}
diff --git a/src/.vuepress/theme/components/PageEdit.vue b/src/.vuepress/theme/components/PageEdit.vue
index c1386f6b86..28161a592f 100644
--- a/src/.vuepress/theme/components/PageEdit.vue
+++ b/src/.vuepress/theme/components/PageEdit.vue
@@ -4,17 +4,20 @@
Deployed on
Netlify .
-
+
+
+
Caught a mistake or want to contribute to the documentation?
-
- {{ editLinkText }}
-
-
-
+
+ {{ editLinkText }}
+
+
+
+
{{ lastUpdatedText }}:
diff --git a/src/.vuepress/theme/components/sponsors/VueMasteryBanner.vue b/src/.vuepress/theme/components/sponsors/VueMasteryBanner.vue
deleted file mode 100644
index d29bd1a022..0000000000
--- a/src/.vuepress/theme/components/sponsors/VueMasteryBanner.vue
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
-
-
diff --git a/src/.vuepress/theme/data/patreon-sponsors.js b/src/.vuepress/theme/data/patreon-sponsors.js
index 6a87bd9927..608cb5541c 100644
--- a/src/.vuepress/theme/data/patreon-sponsors.js
+++ b/src/.vuepress/theme/data/patreon-sponsors.js
@@ -7,10 +7,9 @@ export default {
],
"special_sponsors": [
{
- "url": "https://autocode.com/",
- "img": "autocode.svg",
- "name": "Autocode",
- "description": "Build app-to-app workflows and connect APIs"
+ "url": "https://www.dcloud.io/hbuilderx.html?hmsr=vue-en&hmpl=&hmcu=&hmkw=&hmci=",
+ "img": "hbuilder.png",
+ "name": "HBuilder"
}
],
"platinum_sponsors": [
@@ -36,8 +35,13 @@ export default {
},
{
"url": "https://www.storyblok.com/",
- "img": "storyblok.png",
+ "img": "storyblok.svg",
"name": "Storyblok"
+ },
+ {
+ "url": "https://ionicframework.com/vue?utm_source=partner&utm_medium=referral&utm_campaign=vuesponsorship&utm_content=vuedocs",
+ "img": "ionic.png",
+ "name": "Ionic"
}
],
"gold_sponsors": [
diff --git a/src/.vuepress/theme/layouts/404.vue b/src/.vuepress/theme/layouts/404.vue
index 2cbfa0f179..915deb1d51 100644
--- a/src/.vuepress/theme/layouts/404.vue
+++ b/src/.vuepress/theme/layouts/404.vue
@@ -3,7 +3,13 @@
404
-
{{ getMsg() }}
+
+ Whoops! This page doesn't exist.
+
+
+
+ New pages are added to the documentation all the time. This page might not be included in all of the translations yet.
+
Take me home.
@@ -13,17 +19,22 @@
-
-
diff --git a/src/.vuepress/theme/styles/code.styl b/src/.vuepress/theme/styles/code.styl
index fe065d88f4..a27e3b0f20 100644
--- a/src/.vuepress/theme/styles/code.styl
+++ b/src/.vuepress/theme/styles/code.styl
@@ -31,6 +31,7 @@ div[class*="language-"]
background-color #f6f6f6
border-radius 6px
.highlight-lines
+ border-radius 6px
background-color #f6f6f6
color #3a385d
user-select none
@@ -142,7 +143,7 @@ div.reactivecontent
// @import 'http://github.com/~prismjs/themes/prism-tomorrow.css'
-.token.punctuation
+.token.punctuation
color #a8a9cc
.token.tag, .token.attr-name, .token.namespace, .token.deleted
@@ -169,4 +170,6 @@ div.reactivecontent
div[class*=language-].line-numbers-mode .line-numbers-wrapper, div[class*=language-].line-numbers-mode:after
background-color #f6f6f6
color #c4c4c6
- border-right 1px solid #e7e6e6
\ No newline at end of file
+ border-right 1px solid #e7e6e6
+ border-top-left-radius 6px
+ border-bottom-left-radius 6px
diff --git a/src/.vuepress/theme/styles/index.styl b/src/.vuepress/theme/styles/index.styl
index a35b13f7c0..f23c9deebf 100644
--- a/src/.vuepress/theme/styles/index.styl
+++ b/src/.vuepress/theme/styles/index.styl
@@ -115,7 +115,7 @@ ul, ol
padding-left 1.2em
strong
- font-weight 500
+ font-weight 600
h1, h2, h3, h4, h5, h6
font-weight 500
diff --git a/src/.vuepress/theme/styles/vuemastery-banner.styl b/src/.vuepress/theme/styles/vuemastery-banner.styl
deleted file mode 100644
index af1a66a4b2..0000000000
--- a/src/.vuepress/theme/styles/vuemastery-banner.styl
+++ /dev/null
@@ -1,280 +0,0 @@
-// Screen sizes
-tiny = 330px
-small = 465px
-phone = 700px
-tablet = 900px
-desktop = 1000px
-
-// Media queries mixins
-mqMax(val)
- @media all and (max-width: val)
- {block}
-mqMin(val)
- @media all and (min-width: val + 1px)
- {block}
-
-// Animations easing
-animIn = all cubic-bezier(0.34, 0.06, 0.01, 1) 2s
-animOut = all cubic-bezier(0.34, 0.06, 0.01, 1) 1s
-
-.vuemastery-banner
- background #071532
- overflow hidden
- position relative
- transition animIn
- cursor pointer
- z-index 11
-
- &:after,
- &:before
- content ''
- position absolute
- pointer-events none
- transition animOut
- bottom 0
-
- &:after
- width 100%
- position absolute
- transition animIn
- background-image url(http://github.com/images/vuemastery/background.png)
- background-position center
- background-size: 100% auto;
- top 0
-
- &:before
- height 100%
- width 100%
-
- &:hover
- +mqMin(desktop)
- &:before,
- &:after
- transition animIn
-
- &:before
- transform translateX(50%)
-
- &:after
- transform scale(1.2)
-
-
- .vuemastery-planet
- transform rotate(-35deg) scale(10) translateX(40%)
- &:after
- transform translateX(-93px) scale(1.3)
- opacity 0
-
- .vuemastery-banner--close
- &:before,
- &:after
- transform-origin 100%
-
- &:hover
- &:before,
- &:after
- transition transform .2s ease-in .5s, transform-origin .2s ease-in
- transform-origin 50%
-
- .vuemastery-button:after
- left: 120%;
- transition: left 1.5s cubic-bezier(.19,1,.22,1);
-
-.vuemastery-banner--link
- display flex
- height 80px
- justify-content center
- overflow hidden
- position relative
- z-index 2
-
- +mqMax(phone)
- justify-content start
- height 65px
-
-.vuemastery-banner--wrapper
- display flex
- height 100%
- align-items center
- position relative
- color #fff
- z-index 3
- transition animIn
-
- p
- margin -3px 50px 0 20px
- font-size 1.17rem
- font-weight 600
- white-space nowrap
- position relative
-
- span
- font-size 1rem
- display block
-
- +mqMax(phone)
- flex-direction: column;
- width: calc(100% - 172px);
-
- p, span
- margin: .5rem 0 0rem 0;
- font-size .8rem
- color #fff
-
- +mqMax(phone)
- span
- display none
-
- +mqMax(tiny)
- p
- font-size 0.6rem
- margin -3px 28px 0 0px
-
-.vuemastery-banner--logo
- height 102%
- margin-top -1px
- margin-left -90px
- position relative
- z-index 2
- transition animIn
-
- +mqMax(phone)
- margin-left: -40px;
- transform: rotateY(190deg);
- height: 105%;
-
-.vuemastery-banner--close
- height 100%
- width 75px
- position absolute
- top 0
- right 0
- z-index 2
- -webkit-tap-highlight-color transparent
- cursor pointer
-
- &:before,
- &:after
- content ''
- width 25px
- height 2px
- position absolute
- top 39px
- right 25px
- background-color #fff
- transform-origin 50%
- transform rotate(-45deg)
- transition all .2s ease-out
-
- &:after
- transform rotate(45deg)
-
- &:hover
- &:before,
- &:after
- transform rotate(180deg)
-
- +mqMax(phone)
- &:before,
- &:after
- width 15px
- top 19px
- right 14px
-
-.vuemastery-promo
- .navbar
- position relative
-
- .sidebar
- position absolute
- top 7.60625rem
-
- &.vuemastery-menu-fixed
- .sidebar,
- .navbar
- position fixed
-
- .sidebar
- top 3.6rem
-
- +mqMax(tablet)
- .vuemastery-banner
- margin-bottom 40px
-
-.vuemastery-planet
- position absolute
- z-index 2
- width 100%
- height 100%
- transform-origin bottom right
- transition animIn
-
- +mqMax(phone)
- transform translateX(70px)
-
- &:after,
- &:before
- content ''
- position absolute
- pointer-events none
- bottom 0
- width 100%
- top 0
- right 0
- background-image url(http://github.com/images/vuemastery/black-hole.png)
- background-attachment fixed
- background-size: auto 200px;
- background-position: top -52px right -24px;
- background-repeat: no-repeat;
-
- &:before
- +mqMax(phone)
- background-size: auto 200px;
- background-position: top -81px right -82px;
-
- &:after
- background-image: url(http://github.com/images/vuemastery/planets.png);
- transition: all cubic-bezier(0.34, 0.06, 0.01, 1) 3s;
- background-position: left -80px top -80px;
- background-size: auto 180px;
-
- +mqMax(phone)
- display none
-
-@media print
- .vuemastery-banner
- display none
-
-
-.vuemastery-button
- display: inline-flex;
- background: linear-gradient(to top right,#3d2c61,#835ec2);
- height: 38px;
- margin: .5em 0;
- line-height: 38px;
- padding: 0 30px;
- color: #fff;
- text-decoration: none;
- align-items: center;
- justify-content: center;
- outline: 0;
- text-transform: uppercase;
- border: none;
- border-radius: 36px;
- font-weight: bold;
- font-size: 12px;
- cursor: pointer;
- position: relative
- overflow hidden
-
- &:before,
- &:after
- background: linear-gradient(to top right,transparent,#fff);
- content: "";
- height: 150px;
- left: -175px;
- opacity: .1;
- position: absolute;
- top: -50px;
- transform: rotate(35deg);
- width: 100px;
diff --git a/src/README.md b/src/README.md
index d537873e8d..77fd39c19c 100644
--- a/src/README.md
+++ b/src/README.md
@@ -27,7 +27,7 @@ features:
Minimal Optimization Efforts
footer: |
Released under the MIT License
- Copyright © 2014-2020 Evan You
+ Copyright © 2014-2021 Evan You
socialIcons:
- type: GitHub
link: https://github.com/vuejs/vue-next
diff --git a/src/api/application-api.md b/src/api/application-api.md
index a0a9ee7c59..b6a1470cc8 100644
--- a/src/api/application-api.md
+++ b/src/api/application-api.md
@@ -96,7 +96,8 @@ app.directive('my-directive', {
mounted() {},
// called before the containing component's VNode is updated
beforeUpdate() {},
- // called after the containing component's VNode and the VNodes of its children // have updated
+ // called after the containing component's VNode and the VNodes of its
+ // children have updated
updated() {},
// called before the bound element's parent component is unmounted
beforeUnmount() {},
@@ -191,7 +192,7 @@ Apart from `el`, you should treat these arguments as read-only and never modify
- **Usage:**
- Mounts a root component of the application instance on the provided DOM element.
+ The `innerHTML` of the provided DOM element will be replaced with the rendered template of the application root component.
- **Example:**
@@ -234,7 +235,7 @@ app.mount('#my-app')
Providing values via the application is especially useful when writing plugins, as plugins typically wouldn't be able to provide values using components. It is an alternative to using [globalProperties](application-config.html#globalproperties).
:::tip Note
- The `provide` and `inject` bindings are NOT reactive. This is intentional. However, if you pass down an observed object, properties on that object do remain reactive.
+ The `provide` and `inject` bindings are NOT reactive. This is intentional. However, if you pass down a reactive object, properties on that object do remain reactive.
:::
- **Example:**
@@ -297,10 +298,46 @@ setTimeout(() => app.unmount(), 5000)
- **Usage:**
- Install a Vue.js plugin. If the plugin is an Object, it must expose an `install` method. If it is a function itself, it will be treated as the install method.
+ Install a Vue.js plugin. If the plugin is an Object, it must expose an `install` method. If it is a function itself, it will be treated as the `install` method.
- The install method will be called with the application as its first argument. Any `options` passed to `use` will be passed on in subsequent arguments.
+ The `install` method will be called with the application as its first argument. Any `options` passed to `use` will be passed on in subsequent arguments.
When this method is called on the same plugin multiple times, the plugin will be installed only once.
+- **Example:**
+
+ ```js
+ import { createApp } from 'vue'
+ import MyPlugin from './plugins/MyPlugin'
+
+ const app = createApp({})
+
+ app.use(MyPlugin)
+ app.mount('#app')
+ ```
+
- **See also:** [Plugins](../guide/plugins.html)
+
+## version
+
+- **Usage:**
+
+ Provides the installed version of Vue as a string. This is especially useful for community [plugins](/guide/plugins.html), where you might use different strategies for different versions.
+
+- **Example:**
+
+ ```js
+ export default {
+ install(app) {
+ const version = Number(app.version.split('.')[0])
+
+ if (version < 3) {
+ console.warn('This plugin requires Vue 3')
+ }
+
+ // ...
+ }
+ }
+ ```
+
+- **See also**: [Global API - version](/api/global-api.html#version)
diff --git a/src/api/application-config.md b/src/api/application-config.md
index af9e006fb9..b53487fe81 100644
--- a/src/api/application-config.md
+++ b/src/api/application-config.md
@@ -3,7 +3,7 @@
Every Vue application exposes a `config` object that contains the configuration settings for that application:
```js
-const app = Vue.createApp({})
+const app = createApp({})
console.log(app.config)
```
@@ -26,7 +26,7 @@ app.config.errorHandler = (err, vm, info) => {
}
```
-Assign a handler for uncaught errors during component render function and watchers. The handler gets called with the error and the application instance.
+Assign a handler for uncaught errors during component render function and watchers. The handler gets called with the error and the corresponding application instance.
> Error tracking services [Sentry](https://sentry.io/for/vue/) and [Bugsnag](https://docs.bugsnag.com/platforms/browsers/vue/) provide official integrations using this option.
@@ -73,31 +73,10 @@ This can replace Vue 2.x `Vue.prototype` extending:
Vue.prototype.$http = () => {}
// After
-const app = Vue.createApp({})
+const app = createApp({})
app.config.globalProperties.$http = () => {}
```
-## isCustomElement
-
-- **Type:** `(tag: string) => boolean`
-
-- **Default:** `undefined`
-
-- **Usage:**
-
-```js
-// any element starting with 'ion-' will be recognized as a custom one
-app.config.isCustomElement = tag => tag.startsWith('ion-')
-```
-
-Specifies a method to recognize custom elements defined outside of Vue (e.g., using the Web Components APIs). If component matches this condition, it won't need local or global registration and Vue won't throw a warning about an `Unknown custom element`.
-
-> Note that all native HTML and SVG tags don't need to be matched in this function - Vue parser performs this check automatically
-
-::: tip Important
-This config option is only respected when using the runtime compiler. If you are using the runtime-only build, `isCustomElement` must be passed to `@vue/compiler-dom` in the build setup instead - for example, via the [`compilerOptions` option in vue-loader](https://vue-loader.vuejs.org/options.html#compileroptions).
-:::
-
## optionMergeStrategies
- **Type:** `{ [key: string]: Function }`
@@ -107,13 +86,13 @@ This config option is only respected when using the runtime compiler. If you are
- **Usage:**
```js
-const app = Vue.createApp({
+const app = createApp({
mounted() {
console.log(this.$options.hello)
}
})
-app.config.optionMergeStrategies.hello = (parent, child, vm) => {
+app.config.optionMergeStrategies.hello = (parent, child) => {
return `Hello, ${child}`
}
@@ -126,7 +105,7 @@ app.mixin({
Define merging strategies for custom options.
-The merge strategy receives the value of that option defined on the parent and child instances as the first and second arguments, respectively. The context application instance is passed as the third argument.
+The merge strategy receives the value of that option defined on the parent and child instances as the first and second arguments, respectively.
- **See also:** [Custom Option Merging Strategies](../guide/mixins.html#custom-option-merge-strategies)
@@ -139,3 +118,91 @@ The merge strategy receives the value of that option defined on the parent and c
- **Usage**:
Set this to `true` to enable component init, compile, render and patch performance tracing in the browser devtool performance/timeline panel. Only works in development mode and in browsers that support the [performance.mark](https://developer.mozilla.org/en-US/docs/Web/API/Performance/mark) API.
+
+## compilerOptions
+
+- **Type:** `Object`
+
+Configure runtime compiler options. Values set on this object will be passed to the in-browser template compiler and affect every component in the configured app. Note you can also override these options on a per-component basis using the [`compilerOptions` option](/api/options-misc.html#compileroptions).
+
+::: tip Important
+This config option is only respected when using the full build (i.e. the standalone `vue.js` that can compile templates in the browser). If you are using the runtime-only build with a build setup, compiler options must be passed to `@vue/compiler-dom` via build tool configurations instead.
+
+- For `vue-loader`: [pass via the `compilerOptions` loader option](https://vue-loader.vuejs.org/options.html#compileroptions). Also see [how to configure it in `vue-cli`](https://cli.vuejs.org/guide/webpack.html#modifying-options-of-a-loader).
+
+- For `vite`: [pass via `@vitejs/plugin-vue` options](https://github.com/vitejs/vite/tree/main/packages/plugin-vue#example-for-passing-options-to-vuecompiler-dom).
+:::
+
+### compilerOptions.isCustomElement
+
+- **Type:** `(tag: string) => boolean`
+
+- **Default:** `undefined`
+
+- **Usage:**
+
+```js
+// any element starting with 'ion-' will be recognized as a custom one
+app.config.compilerOptions.isCustomElement = tag => tag.startsWith('ion-')
+```
+
+Specifies a method to recognize custom elements defined outside of Vue (e.g., using the Web Components APIs). If a component matches this condition, it won't need local or global registration and Vue won't throw a warning about an `Unknown custom element`.
+
+> Note that all native HTML and SVG tags don't need to be matched in this function - Vue parser performs this check automatically.
+
+### compilerOptions.whitespace
+
+- **Type:** `'condense' | 'preserve'`
+
+- **Default:** `'condense'`
+
+- **Usage:**
+
+```js
+app.config.compilerOptions.whitespace = 'preserve'
+```
+
+By default, Vue removes/condenses whitespaces between template elements to produce more efficient compiled output:
+
+1. Leading / ending whitespaces inside an element are condensed into a single space
+2. Whitespaces between elements that contain newlines are removed
+3. Consecutive whitespaces in text nodes are condensed into a single space
+
+Setting the value to `'preserve'` will disable (2) and (3).
+
+### compilerOptions.delimiters
+
+- **Type:** `Array`
+
+- **Default:** `{{ "['\u007b\u007b', '\u007d\u007d']" }}`
+
+- **Usage:**
+
+```js
+// Delimiters changed to ES6 template string style
+app.config.compilerOptions.delimiters = ['${', '}']
+```
+
+Sets the delimiters used for text interpolation within the template.
+
+Typically this is used to avoid conflicting with server-side frameworks that also use mustache syntax.
+
+### compilerOptions.comments
+
+- **Type:** `boolean`
+
+- **Default:** `false`
+
+- **Usage:**
+
+```js
+app.config.compilerOptions.comments = true
+```
+
+By default, Vue will remove HTML comments inside templates in production. Setting this option to `true` will force Vue to preserve comments even in production. Comments are always preserved during development.
+
+This option is typically used when Vue is used with other libraries that rely on HTML comments.
+
+## isCustomElement
+
+Deprecated in 3.1.0. Use [`compilerOptions.isCustomElement`](#compileroptions-iscustomelement) instead.
diff --git a/src/api/basic-reactivity.md b/src/api/basic-reactivity.md
index 8fed58a1dc..6efea0e87c 100644
--- a/src/api/basic-reactivity.md
+++ b/src/api/basic-reactivity.md
@@ -18,6 +18,44 @@ The reactive conversion is "deep"—it affects all nested properties. In the [ES
function reactive(target: T): UnwrapNestedRefs
```
+::: tip Note
+`reactive` will unwrap all the deep [refs](./refs-api.html#ref), while maintaining the ref reactivity
+
+```ts
+const count = ref(1)
+const obj = reactive({ count })
+
+// ref will be unwrapped
+console.log(obj.count === count.value) // true
+
+// it will update `obj.count`
+count.value++
+console.log(count.value) // 2
+console.log(obj.count) // 2
+
+// it will also update `count` ref
+obj.count++
+console.log(obj.count) // 3
+console.log(count.value) // 3
+```
+
+:::
+
+::: warning Important
+When assigning a [ref](./refs-api.html#ref) to a `reactive` property, that ref will be automatically unwrapped.
+
+```ts
+const count = ref(1)
+const obj = reactive({})
+
+obj.count = count
+
+console.log(obj.count) // 1
+console.log(obj.count === count.value) // true
+```
+
+:::
+
## `readonly`
Takes an object (reactive or plain) or a [ref](./refs-api.html#ref) and returns a readonly proxy to the original. A readonly proxy is deep: any nested property accessed will be readonly as well.
@@ -39,6 +77,19 @@ original.count++
copy.count++ // warning!
```
+As with [`reactive`](#reactive), if any property uses a `ref` it will be automatically unwrapped when it is accessed via the proxy:
+
+```js
+const raw = {
+ count: ref(123)
+}
+
+const copy = readonly(raw)
+
+console.log(raw.count.value) // 123
+console.log(copy.count) // 123
+```
+
## `isProxy`
Checks if an object is a proxy created by [`reactive`](#reactive) or [`readonly`](#readonly).
@@ -153,6 +204,8 @@ isReactive(state.nested) // false
state.nested.bar++ // non-reactive
```
+Unlike [`reactive`](#reactive), any property that uses a [`ref`](/api/refs-api.html#ref) will **not** be automatically unwrapped by the proxy.
+
## `shallowReadonly`
Creates a proxy that makes its own properties readonly, but does not perform deep readonly conversion of nested objects (exposes raw values).
@@ -171,3 +224,5 @@ state.foo++
isReadonly(state.nested) // false
state.nested.bar++ // works
```
+
+Unlike [`readonly`](#readonly), any property that uses a [`ref`](/api/refs-api.html#ref) will **not** be automatically unwrapped by the proxy.
diff --git a/src/api/built-in-components.md b/src/api/built-in-components.md
index 9571bcc0c2..0f1e4bb398 100644
--- a/src/api/built-in-components.md
+++ b/src/api/built-in-components.md
@@ -1,17 +1,31 @@
# Built-In Components
+Built-in components can be used directly in templates without needing to be registered.
+
+The ``, ``, ``, and `` components can all be tree-shaken by bundlers, so that they are only included in the build if they're used. They can also be imported explicitly if you need direct access to the component itself:
+
+```js
+// CDN build of Vue
+const { KeepAlive, Teleport, Transition, TransitionGroup } = Vue
+```
+
+```js
+// ESM build of Vue
+import { KeepAlive, Teleport, Transition, TransitionGroup } from 'vue'
+```
+
+`` and `` are component-like features of template syntax. They are not true components and they can't be imported like the components shown above.
+
## component
- **Props:**
- - `is` - `string | Component`
+ - `is` - `string | Component | VNode`
- **Usage:**
A "meta component" for rendering dynamic components. The actual component to render is determined by the `is` prop. An `is` prop as a string could be either an HTML tag name or a Component name.
-- **Example:**
-
```html
@@ -27,6 +41,39 @@
```
+- **Usage with built-in components:**
+
+ The built-in components `KeepAlive`, `Transition`, `TransitionGroup`, and `Teleport` can all be passed to `is`, but you must register them if you want to pass them by name. For example:
+
+ ```js
+ const { Transition, TransitionGroup } = Vue
+
+ const Component = {
+ components: {
+ Transition,
+ TransitionGroup
+ },
+
+ template: `
+
+ ...
+
+ `
+ }
+ ```
+
+ Registration is not required if you pass the component itself to `is` rather than its name.
+
+- **Usage with VNodes:**
+
+ In advanced use cases, it can sometimes be useful to render an existing VNode via a template. Using a `` makes this possible, but it should be seen as an escape hatch, used to avoid rewriting the entire template as a `render` function.
+
+ ```html
+
+ ```
+
+ A caveat of mixing VNodes and templates in this way is that you need to provide a suitable `key` attribute. The VNode will be considered static, so any updates will be ignored unless the `key` changes. The `key` can be on the VNode or the `` tag, but either way it must change every time you want the VNode to re-render. This caveat doesn't apply if the nodes have different types, e.g. changing a `span` to a `div`.
+
- **See also:** [Dynamic Components](../guide/component-dynamic-async.html)
## transition
@@ -88,7 +135,7 @@
```
```js
- const app = Vue.createApp({
+ const app = createApp({
...
methods: {
transitionComplete (el) {
@@ -145,7 +192,7 @@
When wrapped around a dynamic component, `` caches the inactive component instances without destroying them. Similar to ``, `` is an abstract component: it doesn't render a DOM element itself, and doesn't show up in the component parent chain.
- When a component is toggled inside ``, its `activated` and `deactivated` lifecycle hooks will be invoked accordingly.
+ When a component is toggled inside ``, its `activated` and `deactivated` lifecycle hooks will be invoked accordingly, providing an alternative to `mounted` and `unmounted`, which are not called. (This applies to the direct child of `` as well as to all of its descendants.)
Primarily used to preserve component state or avoid re-rendering.
diff --git a/src/api/composition-api.md b/src/api/composition-api.md
index a770a3731d..7101d40b74 100644
--- a/src/api/composition-api.md
+++ b/src/api/composition-api.md
@@ -4,32 +4,52 @@
## `setup`
-A component option that is executed **before** the component is created, once the `props` are resolved, and serves as the entry point for composition API's
+A component option that is executed **before** the component is created, once the `props` are resolved. It serves as the entry point for composition APIs.
- **Arguments:**
- `{Data} props`
- `{SetupContext} context`
+ Similar to `this.$props` when using Options API, the `props` object will only contain explicitly declared props. Also, all declared prop keys will be present on the `props` object, regardless of whether it was passed by the parent component or not. Absent optional props will have a value of `undefined`.
+
+ If you need to check the absence of an optional prop, you can give it a Symbol as its default value:
+
+ ```js
+ const isAbsent = Symbol()
+
+ export default {
+ props: {
+ foo: { default: isAbsent }
+ },
+ setup(props) {
+ if (props.foo === isAbsent) {
+ // foo was not provided.
+ }
+ }
+ }
+ ```
+
- **Typing**:
-```ts
-interface Data {
- [key: string]: unknown
-}
+ ```ts
+ interface Data {
+ [key: string]: unknown
+ }
-interface SetupContext {
- attrs: Data
- slots: Slots
- emit: (event: string, ...args: unknown[]) => void
-}
+ interface SetupContext {
+ attrs: Data
+ slots: Slots
+ emit: (event: string, ...args: unknown[]) => void
+ expose: (exposed?: Record) => void
+ }
-function setup(props: Data, context: SetupContext): Data
-```
+ function setup(props: Data, context: SetupContext): Data
+ ```
-::: tip
-To get type inference for the arguments passed to `setup()`, the use of [defineComponent](global-api.html#definecomponent) is needed.
-:::
+ ::: tip
+ To get type inference for the arguments passed to `setup()`, the use of [defineComponent](global-api.html#definecomponent) is needed.
+ :::
- **Example**
@@ -70,12 +90,37 @@ To get type inference for the arguments passed to `setup()`, the use of [defineC
setup() {
const readersNumber = ref(0)
const book = reactive({ title: 'Vue 3 Guide' })
- // Please note that we need to explicitly expose ref value here
+ // Please note that we need to explicitly use ref value here
return () => h('div', [readersNumber.value, book.title])
}
}
```
+ If you return a render function then you can't return any other properties. If you need to expose properties so that they can be accessed externally, e.g. via a `ref` in the parent, you can use `expose`:
+
+ ```js
+ // MyBook.vue
+
+ import { h } from 'vue'
+
+ export default {
+ setup(props, { expose }) {
+ const reset = () => {
+ // Some reset logic
+ }
+
+ // Expose can only be called once.
+ // If you need to expose multiple properties, they must all
+ // be included in the object passed to expose.
+ expose({
+ reset
+ })
+
+ return () => h('div')
+ }
+ }
+ ```
+
- **See also**: [Composition API `setup`](../guide/composition-api-setup.html)
## Lifecycle Hooks
@@ -117,6 +162,9 @@ The component instance context is also set during the synchronous execution of l
- `errorCaptured` -> `onErrorCaptured`
- `renderTracked` -> `onRenderTracked`
- `renderTriggered` -> `onRenderTriggered`
+ - `activated` -> `onActivated`
+ - `deactivated` -> `onDeactivated`
+
- **See also**: [Composition API lifecycle hooks](../guide/composition-api-lifecycle-hooks.html)
@@ -126,40 +174,40 @@ The component instance context is also set during the synchronous execution of l
- **Typing**:
-```ts
-interface InjectionKey extends Symbol {}
-
-function provide(key: InjectionKey | string, value: T): void
-
-// without default value
-function inject(key: InjectionKey | string): T | undefined
-// with default value
-function inject(key: InjectionKey | string, defaultValue: T): T
-// with factory
-function inject(
- key: InjectionKey | string,
- defaultValue: () => T,
- treatDefaultAsFactory: true
-): T
-```
+ ```ts
+ interface InjectionKey extends Symbol {}
+
+ function provide(key: InjectionKey | string, value: T): void
+
+ // without default value
+ function inject(key: InjectionKey | string): T | undefined
+ // with default value
+ function inject(key: InjectionKey | string, defaultValue: T): T
+ // with factory
+ function inject(
+ key: InjectionKey | string,
+ defaultValue: () => T,
+ treatDefaultAsFactory: true
+ ): T
+ ```
-Vue provides an `InjectionKey` interface which is a generic type that extends `Symbol`. It can be used to sync the type of the injected value between the provider and the consumer:
+ Vue provides an `InjectionKey` interface which is a generic type that extends `Symbol`. It can be used to sync the type of the injected value between the provider and the consumer:
-```ts
-import { InjectionKey, provide, inject } from 'vue'
+ ```ts
+ import { InjectionKey, provide, inject } from 'vue'
-const key: InjectionKey = Symbol()
+ const key: InjectionKey = Symbol()
-provide(key, 'foo') // providing non-string value will result in error
+ provide(key, 'foo') // providing non-string value will result in error
-const foo = inject(key) // type of foo: string | undefined
-```
+ const foo = inject(key) // type of foo: string | undefined
+ ```
-If using string keys or non-typed symbols, the type of the injected value will need to be explicitly declared:
+ If using string keys or non-typed symbols, the type of the injected value will need to be explicitly declared:
-```ts
-const foo = inject('foo') // string | undefined
-```
+ ```ts
+ const foo = inject('foo') // string | undefined
+ ```
- **See also**:
- [Provide / Inject](../guide/component-provide-inject.html)
@@ -167,7 +215,11 @@ const foo = inject('foo') // string | undefined
## `getCurrentInstance`
-`getCurrentInstance` enables access to an internal component instance useful for advanced usages or for library creators.
+`getCurrentInstance` enables access to an internal component instance.
+
+:::warning
+`getCurrentInstance` is only exposed for advanced use cases, typically in libraries. Usage of `getCurrentInstance` is strongly discouraged in application code. Do **NOT** use it as an escape hatch to get the equivalent of `this` in Composition API.
+:::
```ts
import { getCurrentInstance } from 'vue'
diff --git a/src/api/computed-watch-api.md b/src/api/computed-watch-api.md
index f691c7300e..29baa3d3c2 100644
--- a/src/api/computed-watch-api.md
+++ b/src/api/computed-watch-api.md
@@ -34,10 +34,31 @@ console.log(count.value) // 0
```ts
// read-only
-function computed(getter: () => T): Readonly[>>
+function computed](
+ getter: () => T,
+ debuggerOptions?: DebuggerOptions
+): Readonly[>>
// writable
-function computed](options: { get: () => T; set: (value: T) => void }): Ref
+function computed(
+ options: {
+ get: () => T
+ set: (value: T) => void
+ },
+ debuggerOptions?: DebuggerOptions
+): Ref
+
+interface DebuggerOptions {
+ onTrack?: (event: DebuggerEvent) => void
+ onTrigger?: (event: DebuggerEvent) => void
+}
+
+interface DebuggerEvent {
+ effect: ReactiveEffect
+ target: any
+ type: OperationTypes
+ key: string | symbol | undefined
+}
```
## `watchEffect`
@@ -84,9 +105,17 @@ type StopHandle = () => void
**See also**: [`watchEffect` guide](../guide/reactivity-computed-watchers.html#watcheffect)
+## `watchPostEffect`
+
+Alias of `watchEffect` with `flush: 'post'` option.
+
+## `watchSyncEffect`
+
+Alias of `watchEffect` with `flush: 'sync'` option.
+
## `watch`
-The `watch` API is the exact equivalent of the Options API [this.$watch](./instance-methods.html#watch) (and the corresponding [watch](./options-data.html#watch) option). `watch` requires watching a specific data source and applies side effects in a separate callback function. It also is lazy by default - i.e. the callback is only called when the watched source has changed.
+The `watch` API is the exact equivalent of the Options API [this.\$watch](./instance-methods.html#watch) (and the corresponding [watch](./options-data.html#watch) option). `watch` requires watching a specific data source and applies side effects in a separate callback function. It also is lazy by default - i.e. the callback is only called when the watched source has changed.
- Compared to [watchEffect](#watcheffect), `watch` allows us to:
diff --git a/src/api/directives.md b/src/api/directives.md
index 7ef2c4bb9f..02cc8a0a85 100644
--- a/src/api/directives.md
+++ b/src/api/directives.md
@@ -35,7 +35,7 @@
- **Example:**
```html
-
+
```
- **See also:** [Data Binding Syntax - Interpolations](../guide/template-syntax.html#raw-html)
@@ -237,7 +237,7 @@
## v-bind
-- **Shorthand:** `:`
+- **Shorthand:** `:` or `.` (when using `.prop` modifier)
- **Expects:** `any (with argument) | Object (without argument)`
@@ -246,6 +246,8 @@
- **Modifiers:**
- `.camel` - transform the kebab-case attribute name into camelCase.
+ - `.prop` - force a binding to be set as a DOM property.
+ - `.attr` - force a binding to be set as a DOM attribute.
- **Usage:**
@@ -278,23 +280,34 @@
-
-
-
-
+
-
-
+
+
+
-
-
+
+
-
-
+
+
-
-
-
+
+
+
+
+
+ ```
+
+ When setting a binding on an element, Vue by default checks whether the element has the key defined as a property using an `in` operator check. If the property is defined, Vue will set the value as a DOM property instead of an attribute. This should work in most cases, but you can override this behavior by explicitly using `.prop` or `.attr` modifiers. This is sometimes necessary, especially when [working with custom elements](/guide/web-components.html#passing-dom-properties).
+
+ The `.prop` modifier also has a dedicated shorthand, `.`:
+
+ ```html
+
+
+
+
```
The `.camel` modifier allows camelizing a `v-bind` attribute name when using in-DOM templates, e.g. the SVG `viewBox` attribute:
@@ -451,34 +464,52 @@
```
+ Since 3.2, you can also memoize part of the template with invalidation conditions using [`v-memo`](#v-memo).
+
- **See also:**
- [Data Binding Syntax - interpolations](../guide/template-syntax.html#text)
+ - [v-memo](#v-memo)
+
+## v-memo
+
+- **Expects:** `Array`
+
+- **Details:**
+
+ Memoize a sub-tree of the template. Can be used on both elements and components. The directive expects a fixed-length array of dependency values to compare for the memoization. If every value in the array was the same as last render, then updates for the entire sub-tree will be skipped. For example:
+
+ ```html
+
+ ...
+
+ ```
-## v-is
+ When the component re-renders, if both `valueA` and `valueB` remain the same, all updates for this `` and its children will be skipped. In fact, even the Virtual DOM VNode creation will also be skipped since the memoized copy of the sub-tree can be reused.
-> Note: this section only affects cases where Vue templates are directly written in the page's HTML.
+ It is important to specify the memoization array correctly, otherwise we may skip updates that should indeed be applied. `v-memo` with an empty dependency array (`v-memo="[]"`) would be functionally equivalent to `v-once`.
-- **Expects:** string literal
+ **Usage with `v-for`**
-- **Limited to:** native HTML elements
+ `v-memo` is provided solely for micro optimizations in performance-critical scenarios and should be rarely needed. The most common case where this may prove helpful is when rendering large `v-for` lists (where `length > 1000`):
-- **Usage:** When using in-DOM templates, the template is subject to native HTML parsing rules. Some HTML elements, such as `
`, ``, `` and `` have restrictions on what elements can appear inside them, and some elements such as ``, ``, and `` can only appear inside certain other elements. As a workaround, we can use `v-is` directive on these elements:
+ ```html
+
+
ID: {{ item.id }} - selected: {{ item.id === selected }}
+
...more child nodes
+
+ ```
-```html
-
-```
+ When the component's `selected` state changes, a large amount of VNodes will be created even though most of the items remained exactly the same. The `v-memo` usage here is essentially saying "only update this item if it went from non-selected to selected, or the other way around". This allows every unaffected item to reuse its previous VNode and skip diffing entirely. Note we don't need to include `item.id` in the memo dependency array here since Vue automatically infers it from the item's `:key`.
-:::warning
-`v-is` functions like a dynamic 2.x `:is` binding - so to render a component by its registered name, its value should be a JavaScript string literal:
+ :::warning
+ When using `v-memo` with `v-for`, make sure they are used on the same element. **`v-memo` does not work inside `v-for`.**
+ :::
+
+ `v-memo` can also be used on components to manually prevent unwanted updates in certain edge cases where the child component update check has been de-optimized. But again, it is the developer's responsibility to specify correct dependency arrays to avoid skipping necessary updates.
-```html
-
-
+- **See also:**
+ - [v-once](#v-once)
-
-
-```
+## v-is
-:::
+Deprecated in 3.1.0. Use [`is` attribute with `vue:` prefix](/api/special-attributes.html#is) instead.
diff --git a/src/api/effect-scope.md b/src/api/effect-scope.md
new file mode 100644
index 0000000000..f154162eee
--- /dev/null
+++ b/src/api/effect-scope.md
@@ -0,0 +1,59 @@
+# Effect Scope API
+
+:::info
+Effect scope is an advanced API primarily intended for library authors. For details on how to leverage this API, please consult its corresponding [RFC](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0041-reactivity-effect-scope.md).
+:::
+
+## `effectScope`
+
+Creates an effect scope object which can capture the reactive effects (e.g. computed and watchers) created within it so that these effects can be disposed together.
+
+**Typing:**
+
+```ts
+function effectScope(detached?: boolean): EffectScope
+
+interface EffectScope {
+ run(fn: () => T): T | undefined // undefined if scope is inactive
+ stop(): void
+}
+```
+
+**Example:**
+
+```js
+const scope = effectScope()
+
+scope.run(() => {
+ const doubled = computed(() => counter.value * 2)
+
+ watch(doubled, () => console.log(doubled.value))
+
+ watchEffect(() => console.log('Count: ', doubled.value))
+})
+
+// to dispose all effects in the scope
+scope.stop()
+```
+
+## `getCurrentScope`
+
+Returns the current active [effect scope](#effectscope) if there is one.
+
+**Typing:**
+
+```ts
+function getCurrentScope(): EffectScope | undefined
+```
+
+## `onScopeDispose`
+
+Registers a dispose callback on the current active [effect scope](#effectscope). The callback will be invoked when the associated effect scope is stopped.
+
+This method can be used as a non-component-coupled replacement of `onUnmounted` in reusable composition functions, since each Vue component's `setup()` function is also invoked in an effect scope.
+
+**Typing:**
+
+```ts
+function onScopeDispose(fn: () => void): void
+```
diff --git a/src/api/global-api.md b/src/api/global-api.md
index b5b7582fc6..eec4d7c9e8 100644
--- a/src/api/global-api.md
+++ b/src/api/global-api.md
@@ -4,12 +4,26 @@ sidebarDepth: 1
# Global API
+If you're using a CDN build then the functions of the global API are accessible via the global `Vue` object. e.g.:
+
+```js
+const { createApp, h, nextTick } = Vue
+```
+
+If you're using ES modules then they can be imported directly:
+
+```js
+import { createApp, h, nextTick } from 'vue'
+```
+
+Global functions that handle reactivity, such as `reactive` and `ref`, are documented separately. See [Reactivity API](/api/reactivity-api.html) for those functions.
+
## createApp
Returns an application instance which provides an application context. The entire component tree mounted by the application instance share the same context.
```js
-const app = Vue.createApp({})
+const app = createApp({})
```
You can chain other methods after `createApp`, they can be found in [Application API](./application-api.html)
@@ -19,7 +33,7 @@ You can chain other methods after `createApp`, they can be found in [Application
The function receives a root component options object as a first parameter:
```js
-const app = Vue.createApp({
+const app = createApp({
data() {
return {
...
@@ -34,7 +48,7 @@ const app = Vue.createApp({
With the second parameter, we can pass root props to the application:
```js
-const app = Vue.createApp(
+const app = createApp(
{
props: ['username']
},
@@ -49,6 +63,8 @@ const app = Vue.createApp(
```
+The root props are raw props, much like those passed to [`h`](#h) to create a VNode. In addition to component props, they can also include attributes and event listeners to be applied to the root component.
+
### Typing
```ts
@@ -64,11 +80,11 @@ export type CreateAppFunction = (
## h
-Returns a returns "virtual node", usually abbreviated to **VNode**: a plain object which contains information describing to Vue what kind of node it should render on the page, including descriptions of any child nodes. It is intended for manually written [render functions](../guide/render-function.md):
+Returns a "virtual node", usually abbreviated to **VNode**: a plain object which contains information describing to Vue what kind of node it should render on the page, including descriptions of any child nodes. It is intended for manually written [render functions](../guide/render-function.md):
```js
render() {
- return Vue.h('h1', {}, 'Some title')
+ return h('h1', {}, 'Some title')
}
```
@@ -82,7 +98,7 @@ Accepts three arguments: `type`, `props` and `children`
- **Details:**
- An HTML tag name, a component or an async component. Using function returning null would render a comment. This parameter is required
+ An HTML tag name, a component, an async component, or a functional component. Using function returning null would render a comment. This parameter is required
#### props
@@ -177,16 +193,14 @@ createApp({
})
```
-For advanced usage, `defineAsyncComponent` can accept an object:
-
-The `defineAsyncComponent` method can also return an object of the following format:
+For advanced usage, `defineAsyncComponent` can accept an object of the following format:
```js
import { defineAsyncComponent } from 'vue'
const AsyncComp = defineAsyncComponent({
// The factory function
- loader: () => import('./Foo.vue')
+ loader: () => import('./Foo.vue'),
// A component to use while the async component is loading
loadingComponent: LoadingComponent,
// A component to use if the load fails
@@ -220,6 +234,44 @@ const AsyncComp = defineAsyncComponent({
**See also**: [Dynamic and Async components](../guide/component-dynamic-async.html)
+## defineCustomElement
+
+This method accepts the same argument as [`defineComponent`](#definecomponent), but instead returns a native [Custom Element](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements) that can be used within any framework, or with no frameworks at all.
+
+Usage example:
+
+```html
+
+```
+
+```js
+import { defineCustomElement } from 'vue'
+
+const MyVueElement = defineCustomElement({
+ // normal Vue component options here
+ props: {},
+ emits: {},
+ template: `...`,
+
+ // defineCustomElement only: CSS to be injected into shadow root
+ styles: [`/* inlined css */`]
+})
+
+// Register the custom element.
+// After registration, all `` tags on the page will be upgraded.
+customElements.define('my-vue-element', MyVueElement)
+
+// You can also programmatically instantiate the element:
+// (can only be done after registration)
+document.body.appendChild(
+ new MyVueElement({
+ // initial props (optional)
+ })
+)
+```
+
+For more details on building Web Components with Vue, especially with Single File Components, see [Vue and Web Components](/guide/web-components.html#building-custom-elements-with-vue).
+
## resolveComponent
:::warning
@@ -228,10 +280,10 @@ const AsyncComp = defineAsyncComponent({
Allows resolving a `component` by its name, if it is available in the current application instance.
-Returns a `Component` or `undefined` when not found.
+Returns a `Component` or the argument `name` when not found.
```js
-const app = Vue.createApp({})
+const app = createApp({})
app.component('MyComponent', {
/* ... */
})
@@ -296,7 +348,7 @@ Allows resolving a `directive` by its name, if it is available in the current ap
Returns a `Directive` or `undefined` when not found.
```js
-const app = Vue.createApp({})
+const app = createApp({})
app.directive('highlight', {})
```
@@ -451,3 +503,96 @@ const app = createApp({
```
**See also**: [`$nextTick` instance method](instance-methods.html#nexttick)
+
+## mergeProps
+
+Takes multiple objects containing VNode props and merges them into a single object. A newly created object is returned, the objects passed as arguments are not modified.
+
+Any number of objects can be passed, with properties from later arguments taking precedence. Event listeners are handled specially, as are `class` and `style`, with the values of these properties being merged rather than overwritten.
+
+```js
+import { h, mergeProps } from 'vue'
+
+export default {
+ inheritAttrs: false,
+
+ render() {
+ const props = mergeProps(
+ {
+ // The class will be merged with any class from $attrs
+ class: 'active'
+ },
+ this.$attrs
+ )
+
+ return h('div', props)
+ }
+}
+```
+
+## useCssModule
+
+:::warning
+`useCssModule` can only be used within `render` or `setup` functions.
+:::
+
+Allows CSS modules to be accessed within the [`setup`](/api/composition-api.html#setup) function of a [single-file component](/guide/single-file-component.html):
+
+```vue
+
+
+
+```
+
+For more information about using CSS modules, see [SFC Style Features: `
+
+
+ This could be e.g. documentation for the component.
+
+```
+
+## Language Blocks
+
+### ``
+
+- Each `*.vue` file can contain at most one top-level `` block at a time.
+
+- Contents will be extracted and passed on to `@vue/compiler-dom`, pre-compiled into JavaScript render functions, and attached to the exported component as its `render` option.
+
+### `
+```
+
+`lang` can be applied to any block - for example we can use `
+```
+
+Note the intergration with pre-processors may differ based on the toolchain. Check out the respective documentations for examples:
+
+- [Vite](https://vitejs.dev/guide/features.html#css-pre-processors)
+- [Vue CLI](https://cli.vuejs.org/guide/css.html#pre-processors)
+- [webpack + vue-loader](https://vue-loader.vuejs.org/guide/pre-processors.html#using-pre-processors)
+
+## Src Imports
+
+If you prefer splitting up your `*.vue` components into multiple files, you can use the `src` attribute to import an external file for a language block:
+
+```vue
+
+
+
+```
+
+Beware that `src` imports follow the same path resolution rules as webpack module requests, which means:
+
+- Relative paths need to start with `./`
+- You can import resources from npm dependencies:
+
+```vue
+
+
+
+
+ hi
+
+```
+
+Into the following:
+
+```vue
+
+
+
+ hi
+
+```
+
+### Child Component Root Elements
+
+With `scoped`, the parent component's styles will not leak into child components. However, a child component's root node will be affected by both the parent's scoped CSS and the child's scoped CSS. This is by design so that the parent can style the child root element for layout purposes.
+
+### Deep Selectors
+
+If you want a selector in `scoped` styles to be "deep", i.e. affecting child components, you can use the `:deep()` pseudo-class:
+
+```vue
+
+```
+
+The above will be compiled into:
+
+```css
+.a[data-v-f3f3eg9] .b {
+ /* ... */
+}
+```
+
+:::tip
+DOM content created with `v-html` are not affected by scoped styles, but you can still style them using deep selectors.
+:::
+
+### Slotted Selectors
+
+By default, scoped styles do not affect contents rendered by ` `, as they are considered to be owned by the parent component passing them in. To explicitly target slot content, use the `:slotted` pseudo-class:
+
+```vue
+
+```
+
+### Global Selectors
+
+If you want just one rule to apply globally, you can use the `:global` pseudo-class rather than creating another `
+```
+
+### Mixing Local and Global Styles
+
+You can also include both scoped and non-scoped styles in the same component:
+
+```vue
+
+
+
+```
+
+### Scoped Style Tips
+
+- **Scoped styles do not eliminate the need for classes**. Due to the way browsers render various CSS selectors, `p { color: red }` will be many times slower when scoped (i.e. when combined with an attribute selector). If you use classes or ids instead, such as in `.example { color: red }`, then you virtually eliminate that performance hit.
+
+- **Be careful with descendant selectors in recursive components!** For a CSS rule with the selector `.a .b`, if the element that matches `.a` contains a recursive child component, then all `.b` in that child component will be matched by the rule.
+
+## `
+```
+
+The resulting classes are hashed to avoid collision, achieving the same effect of scoping the CSS to the current component only.
+
+Refer to the [CSS Modules spec](https://github.com/css-modules/css-modules) for more details such as [global exceptions](https://github.com/css-modules/css-modules#exceptions) and [composition](https://github.com/css-modules/css-modules#composition).
+
+### Custom Inject Name
+
+You can customize the property key of the injected classes object by giving the `module` attribute a value:
+
+```vue
+
+ red
+
+
+
+```
+
+### Usage with Composition API
+
+The injected classes can be accessed in `setup()` and `
+
+
+```
+
+The syntax works with [`
+
+
+ hello
+
+
+
+```
+
+The actual value will be compiled into a hashed CSS custom property, so the CSS is still static. The custom property will be applied to the component's root element via inline styles and reactively updated if the source value changes.
diff --git a/src/api/sfc-tooling.md b/src/api/sfc-tooling.md
new file mode 100644
index 0000000000..bd5bdf7c72
--- /dev/null
+++ b/src/api/sfc-tooling.md
@@ -0,0 +1,96 @@
+# SFC Tooling
+
+## Online Playgrounds
+
+You don't need to install anything on your machine to try out Vue SFCs - there are many online playgrounds that allow you to do so right in the browser:
+
+- [Vue SFC Playground](https://sfc.vuejs.org) (official, deployed from latest commit)
+- [VueUse Playground](https://play.vueuse.org)
+- [Vue on CodeSandbox](https://codesandbox.io/s/vue-3)
+- [Vue on Repl.it](https://replit.com/@templates/VueJS-with-Vite)
+- [Vue on Codepen](https://codepen.io/pen/editor/vue)
+- [Vue on StackBlitz](https://stackblitz.com/fork/vue)
+- [Vue on Components.studio](https://components.studio/create/vue3)
+- [Vue on WebComponents.dev](https://webcomponents.dev/create/cevue)
+
+It is also recommended to use these online playgrounds to provide reproductions when reporting bugs.
+
+## Project Scaffolding
+
+### Vite
+
+[Vite](https://vitejs.dev/) is a lightweight and fast build tool with first-class Vue SFC support. It is created by Evan You, who is also the author of Vue itself! To get started with Vite + Vue, simply run:
+
+```sh
+npm init vite@latest
+```
+
+Then select the Vue template and follow the instructions.
+
+- To learn more about Vite, check out the [Vite docs](https://vitejs.dev/guide/).
+- To configure Vue-specific behavior in a Vite project, for example passing options to the Vue compiler, check out the docs for [@vitejs/plugin-vue](https://github.com/vitejs/vite/tree/main/packages/plugin-vue#readme).
+
+The [SFC Playground](https://sfc.vuejs.org/) also supports downloading the files as a Vite project.
+
+### Vue CLI
+
+[Vue CLI](https://cli.vuejs.org/) is the official webpack-based build tool for Vue projects. To get started with Vue CLI:
+
+```sh
+npm install -g @vue/cli
+vue create hello-vue
+```
+
+- To learn more about Vue CLI, check out [Vue CLI docs](https://cli.vuejs.org/guide/installation.html).
+
+### Vite or Vue CLI?
+
+We recommend starting new projects with Vite as it offers significantly better development experience in terms of dev server startup and HMR update performance ([details](https://vitejs.dev/guide/why.html)). Only go with Vue CLI if you rely on specific webpack features (e.g. Module Federation).
+
+If you are a [Rollup](https://rollupjs.org/) user, you can safely adopt Vite as it uses Rollup for production builds and supports a Rollup-compatible plugin system. [Even Rollup's maintainer recommends Vite as THE web development wrapper for Rollup](https://twitter.com/lukastaegert/status/1412119729431584774).
+
+## IDE Support
+
+The recommended IDE setup is [VSCode](https://code.visualstudio.com/) + the [Volar](https://github.com/johnsoncodehk/volar) extension. Volar provides syntax highlighting and advanced IntelliSense for template expressions, component props and even slots validation. We strongly recommend this setup if you want to get the best possible experience with Vue SFCs, especially if you are also using TypeScript.
+
+[WebStorm](https://www.jetbrains.com/webstorm/) also provides decent support for Vue SFCs. However, do note as of now its support for `
```
-
+
[Read documentation on skip link to main content](https://www.w3.org/WAI/WCAG21/Techniques/general/G1.html)
@@ -94,16 +94,16 @@ Users can navigate an application through headings. Having descriptive headings
Landmarks provide programmatic access to sections within an application. Users who rely on assistive technology can navigate to each section of the application and skip over content. You can use [ARIA roles](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles) to help you achieve this.
-| HTML | ARIA Role | Landmark Purpose |
-| --------------- | ----------------------------------------------------------------- | -------------------------------------------------------------------------------------- |
-| header | role="banner" | Prime heading: title of the page |
-| nav | role="navigation" | Collection of links suitable for use when navigating the document or related documents |
-| main | role="main" | The main or central content of the document. |
-| footer | role="contentinfo" | Information about the parent document: footnotes/copyrights/links to privacy statement |
-| aside | role="complementary" | Supports the main content, yet is separated and meaningful on its own content |
-| _Not available_ | role="search" | This section contains the search functionality for the application |
-| form | role="form" | Collection of form-associated elements |
-| section | role="region" | Content that is relevant and that users will likely want to navigate to. Label must be provided for this element |
+| HTML | ARIA Role | Landmark Purpose |
+| --------------- | -------------------- | ---------------------------------------------------------------------------------------------------------------- |
+| header | role="banner" | Prime heading: title of the page |
+| nav | role="navigation" | Collection of links suitable for use when navigating the document or related documents |
+| main | role="main" | The main or central content of the document. |
+| footer | role="contentinfo" | Information about the parent document: footnotes/copyrights/links to privacy statement |
+| aside | role="complementary" | Supports the main content, yet is separated and meaningful on its own content |
+| _Not available_ | role="search" | This section contains the search functionality for the application |
+| form | role="form" | Collection of form-associated elements |
+| section | role="region" | Content that is relevant and that users will likely want to navigate to. Label must be provided for this element |
:::tip Tip:
It is recommended to use landmark HTML elements with redundant landmark role attributes in order to maximize compatibility with legacy [browsers that don’t support HTML5 semantic elements](https://caniuse.com/#feat=html5semantic).
diff --git a/src/guide/a11y-resources.md b/src/guide/a11y-resources.md
index 23d3bb45ba..b8c5922124 100644
--- a/src/guide/a11y-resources.md
+++ b/src/guide/a11y-resources.md
@@ -40,7 +40,7 @@ The World Health Organization estimates that 15% of the world's population has s
There are a huge range of disabilities, which can be divided roughly into four categories:
- _[Visual](https://webaim.org/articles/visual/)_ - These users can benefit from the use of screen readers, screen magnification, controlling screen contrast, or braille display.
-- _[Auditory](https://webaim.org/articles/auditory/)_ - These users can benefit from captioning, transcripts or sign language video.
+- _[Auditory](https://webaim.org/articles/auditory/)_ - These users can benefit from captioning, transcripts or sign language video.
- _[Motor](https://webaim.org/articles/motor/)_ - These users can benefit from a range of [assistive technologies for motor impairments](https://webaim.org/articles/motor/assistive): voice recognition software, eye tracking, single-switch access, head wand, sip and puff switch, oversized trackball mouse, adaptive keyboard or other assistive technologies.
- _[Cognitive](https://webaim.org/articles/cognitive/)_ - These users can benefit from supplemental media, structural organization of content, clear and simple writing.
diff --git a/src/guide/a11y-semantics.md b/src/guide/a11y-semantics.md
index 255e832401..86ba1a7fa1 100644
--- a/src/guide/a11y-semantics.md
+++ b/src/guide/a11y-semantics.md
@@ -21,7 +21,7 @@ Labels are typically placed on top or to the left of the form fields:
```
-
+
Notice how you can include `autocomplete='on'` on the form element and it will apply to all inputs in your form. You can also set different [values for autocomplete attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete) for each input.
@@ -34,7 +34,7 @@ Provide labels to describe the purpose of all form control; linking `for` and `i
```
-
+
If you inspect this element in your chrome developer tools and open the Accessibility tab inside the Elements tab, you will see how the input gets its name from the label:
@@ -68,7 +68,7 @@ You can also give the input an accessible name with [`aria-label`](https://devel
/>
```
-
+
Feel free to inspect this element in Chrome DevTools to see how the accessible name has changed:
@@ -76,7 +76,7 @@ Feel free to inspect this element in Chrome DevTools to see how the accessible n
#### aria-labelledby
-Using [`aria-labelledby`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques/Using_the_aria-labelledby_attribute) is similar to `aria-label` expect it is used if the label text is visible on screen. It is paired to other elements by their `id` and you can link multiple `id`s:
+Using [`aria-labelledby`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques/Using_the_aria-labelledby_attribute) is similar to `aria-label` except it is used if the label text is visible on screen. It is paired to other elements by their `id` and you can link multiple `id`s:
```html
```
-
+
#### Functional Images
@@ -276,4 +276,4 @@ You can use this technique to create functional images.
```
-
+
diff --git a/src/guide/class-and-style.md b/src/guide/class-and-style.md
index 0febb18f0f..dd6dbd9af9 100644
--- a/src/guide/class-and-style.md
+++ b/src/guide/class-and-style.md
@@ -3,6 +3,7 @@
A common need for data binding is manipulating an element's class list and its inline styles. Since they are both attributes, we can use `v-bind` to handle them: we only need to calculate a final string with our expressions. However, meddling with string concatenation is annoying and error-prone. For this reason, Vue provides special enhancements when `v-bind` is used with `class` and `style`. In addition to strings, the expressions can also evaluate to objects or arrays.
## Binding HTML Classes
+Watch a free video lesson on Vue School
### Object Syntax
@@ -111,7 +112,7 @@ If you would like to also toggle a class in the list conditionally, you can do i
```
-This will always apply `errorClass`, but will only apply `activeClass` when `isActive` is truthy.
+This will always apply `errorClass`, but `activeClass` will only be applied when `isActive` is truthy.
However, this can be a bit verbose if you have multiple conditional classes. That's why it's also possible to use the object syntax inside array syntax:
@@ -230,7 +231,7 @@ The array syntax for `:style` allows you to apply multiple style objects to the
### Auto-prefixing
-When you use a CSS property that requires [vendor prefixes](https://developer.mozilla.org/en-US/docs/Glossary/Vendor_Prefix) in `:style`, for example `transform`, Vue will automatically detect and add appropriate prefixes to the applied styles.
+When you use a CSS property that requires a [vendor prefix](https://developer.mozilla.org/en-US/docs/Glossary/Vendor_Prefix) in `:style`, Vue will automatically add the appropriate prefix. Vue does this by checking at runtime to see which style properties are supported in the current browser. If the browser doesn't support a particular property then various prefixed variants will be tested to try to find one that is supported.
### Multiple Values
diff --git a/src/guide/component-attrs.md b/src/guide/component-attrs.md
index 576d73ec3b..691a7fa6d3 100644
--- a/src/guide/component-attrs.md
+++ b/src/guide/component-attrs.md
@@ -2,7 +2,7 @@
> This page assumes you've already read the [Components Basics](component-basics.md). Read that first if you are new to components.
-A component non-prop attribute is an attribute or event listener that is passed to a component, but does not have a corresponding property defined in [props](component-props) or [emits](component-custom-events.html#defining-custom-events). Common examples of this include `class`, `style`, and `id` attributes. You can access those attributes via `$attrs` property.
+A component non-prop attribute is an attribute or event listener that is passed to a component, but does not have a corresponding property defined in [props](component-props.html) or [emits](component-custom-events.html#defining-custom-events). Common examples of this include `class`, `style`, and `id` attributes. You can access those attributes via `$attrs` property.
## Attribute Inheritance
@@ -12,13 +12,13 @@ When a component returns a single root node, non-prop attributes will automatica
app.component('date-picker', {
template: `
-
+
`
})
```
-In the event we need to define the status of the date-picker component via a `data-status` property, it will be applied to the root node (i.e., `div.date-picker`).
+In the event we need to define the status of the date-picker component via a `data-status` attribute, it will be applied to the root node (i.e., `div.date-picker`).
```html
@@ -26,7 +26,7 @@ In the event we need to define the status of the date-picker component via a `da
-
+
```
@@ -82,7 +82,7 @@ If you do **not** want a component to automatically inherit attributes, you can
The common scenario for disabling an attribute inheritance is when attributes need to be applied to other elements besides the root node.
-By setting the `inheritAttrs` option to `false`, you can control to apply to other elements attributes to use the component's `$attrs` property, which includes all attributes not included to component `props` and `emits` properties (e.g., `class`, `style`, `v-on` listeners, etc.).
+By setting the `inheritAttrs` option to `false`, you can then apply attributes to the element of your choice by using the component's `$attrs` property, which includes all attributes not included to component `props` and `emits` properties (e.g., `class`, `style`, `v-on` listeners, etc.).
Using our date-picker component example from the [previous section](#attribute-inheritance), in the event we need to apply all non-prop attributes to the `input` element rather than the root `div` element, this can be accomplished by using the `v-bind` shortcut.
@@ -91,7 +91,7 @@ app.component('date-picker', {
inheritAttrs: false,
template: `
-
+
`
})
@@ -105,7 +105,7 @@ With this new configuration, our `data-status` attribute will be applied to our
-
+
```
diff --git a/src/guide/component-basics.md b/src/guide/component-basics.md
index cda0c9b5c6..9d647f4c71 100644
--- a/src/guide/component-basics.md
+++ b/src/guide/component-basics.md
@@ -1,5 +1,7 @@
# Components Basics
+Learn component basics with a free video course on Vue School
+
## Base Example
Here's an example of a Vue component:
@@ -54,7 +56,7 @@ Components can be reused as many times as you want:
```
-
+
Notice that when clicking on the buttons, each one maintains its own, separate `count`. That's because each time you use a component, a new **instance** of it is created.
@@ -111,7 +113,7 @@ Once a prop is registered, you can pass data to it as a custom attribute, like t
```
-
+
In a typical app, however, you'll likely have an array of posts in `data`:
@@ -227,7 +229,7 @@ Then the child component can emit an event on itself by calling the built-in [**
Thanks to the `@enlarge-text="postFontSize += 0.1"` listener, the parent will receive the event and update the value of `postFontSize`.
-
+
We can list emitted events in the component's `emits` option:
@@ -406,6 +408,18 @@ That's all you need to know about dynamic components for now, but once you've fi
## DOM Template Parsing Caveats
+If you are writing your Vue templates directly in the DOM, Vue will have to retrieve the template string from the DOM. This leads to some caveats due to browsers' native HTML parsing behavior.
+
+:::tip
+It should be noted that the limitations discussed below only apply if you are writing your templates directly in the DOM. They do NOT apply if you are using string templates from the following sources:
+
+- String templates (e.g. `template: '...'`)
+- [Single-file (`.vue`) components](single-file-component.html)
+- `
```
-Note that [refs](../api/refs-api.html#ref) returned from `setup` are [automatically unwrapped](/guide/reactivity-fundamentals.html#ref-unwrapping) when accessed in the template so you shouldn't use `.value` in templates.
+Note that [refs](../api/refs-api.html#ref) returned from `setup` are [automatically shallow unwrapped](/guide/reactivity-fundamentals.html#ref-unwrapping) when accessed in the template so you shouldn't use `.value` in templates.
## Usage with Render Functions
@@ -157,12 +163,35 @@ export default {
setup() {
const readersNumber = ref(0)
const book = reactive({ title: 'Vue 3 Guide' })
- // Please note that we need to explicitly expose ref value here
+ // Please note that we need to explicitly use ref value here
return () => h('div', [readersNumber.value, book.title])
}
}
```
+Returning a render function prevents us from returning anything else. Internally that shouldn't be a problem, but it can be problematic if we want to expose methods of this component to the parent component via template refs.
+
+We can solve this problem by calling `expose`, passing it an object that defines the properties that should be available on the external component instance:
+
+```js
+import { h, ref } from 'vue'
+
+export default {
+ setup(props, { expose }) {
+ const count = ref(0)
+ const increment = () => ++count.value
+
+ expose({
+ increment
+ })
+
+ return () => h('div', count.value)
+ }
+}
+```
+
+The `increment` method would then be available in the parent component via a template ref.
+
## Usage of `this`
**Inside `setup()`, `this` won't be a reference to the current active instance** Since `setup()` is called before other component options are resolved, `this` inside `setup()` will behave quite differently from `this` in other options. This might cause confusions when using `setup()` along other Options API.
diff --git a/src/guide/composition-api-template-refs.md b/src/guide/composition-api-template-refs.md
index f04674edc8..a642610ec0 100644
--- a/src/guide/composition-api-template-refs.md
+++ b/src/guide/composition-api-template-refs.md
@@ -85,3 +85,66 @@ Composition API template refs do not have special handling when used inside `v-f
}
```
+
+## Watching Template Refs
+
+Watching a template ref for changes can be an alternative to the use of lifecycle hooks that was demonstrated in the previous examples.
+
+But a key difference to lifecycle hooks is that `watch()` and `watchEffect()` effects are run *before* the DOM is mounted or updated so the template ref hasn't been updated when the watcher runs the effect:
+
+```vue
+
+ This is a root element
+
+
+
+```
+
+Therefore, watchers that use template refs should be defined with the `flush: 'post'` option. This will run the effect *after* the DOM has been updated and ensure that the template ref stays in sync with the DOM and references the correct element.
+
+```vue
+
+ This is a root element
+
+
+
+```
+
+* See also: [Computed and Watchers](./reactivity-computed-watchers.html#effect-flush-timing)
diff --git a/src/guide/computed.md b/src/guide/computed.md
index ab4bb2965b..d3b1247df8 100644
--- a/src/guide/computed.md
+++ b/src/guide/computed.md
@@ -2,6 +2,8 @@
## Computed Properties
+Learn how computed properties work with a free lesson on Vue School
+
In-template expressions are very convenient, but they are meant for simple operations. Putting too much logic in your templates can make them bloated and hard to maintain. For example, if we have an object with a nested array:
```js
diff --git a/src/guide/conditional.md b/src/guide/conditional.md
index c35f1e2efb..d6f7be9f6c 100644
--- a/src/guide/conditional.md
+++ b/src/guide/conditional.md
@@ -1,5 +1,7 @@
# Conditional Rendering
+Learn how conditional rendering works with a free lesson on Vue School
+
## `v-if`
The directive `v-if` is used to conditionally render a block. The block will only be rendered if the directive's expression returns a truthy value.
@@ -91,4 +93,4 @@ Generally speaking, `v-if` has higher toggle costs while `v-show` has higher ini
Using `v-if` and `v-for` together is **not recommended**. See the [style guide](../style-guide/#avoid-v-if-with-v-for-essential) for further information.
:::
-When `v-if` and `v-for` are both used on the same element, `v-if` will be evaluated first. See the [list rendering guide](list#v-for-with-v-if) for details.
+When `v-if` and `v-for` are both used on the same element, `v-if` will be evaluated first. See the [list rendering guide](list.html#v-for-with-v-if) for details.
diff --git a/src/guide/contributing/doc-style-guide.md b/src/guide/contributing/doc-style-guide.md
index a465d866aa..27e6b0f2c8 100644
--- a/src/guide/contributing/doc-style-guide.md
+++ b/src/guide/contributing/doc-style-guide.md
@@ -64,6 +64,7 @@ export default {
````
**Rendered Output**
+
```js
export default {
name: 'MyComponent'
diff --git a/src/guide/contributing/translations.md b/src/guide/contributing/translations.md
index 23e4999a16..f7e848f1c4 100644
--- a/src/guide/contributing/translations.md
+++ b/src/guide/contributing/translations.md
@@ -1,11 +1,43 @@
# Translations
-Vue has spread across the globe, with the core team being in at least half a dozen different timezones. [The forum](https://forum.vuejs.org/) includes 7 languages and counting and many of our docs have [actively-maintained translations](https://github.com/vuejs?utf8=%E2%9C%93&q=vuejs.org). We're very proud of Vue's international reach, but we can do even better.
+Vue has spread across the globe, with the core team being in at least half a dozen different timezones. The community is growing all the time and we want to make the documentation accessible to as many people as possible.
-## Can we start translating Vue 3 docs?
+Providing translations for the documentation is not something that the core team can manage alone. Thankfully, we have a great community of translators making contributions both large and small. If you've been thinking about contributing to the project, perhaps this is a good place to start?
-Yes! We are ready for translation!
+## Community translations
+
+The table below lists GitHub repositories for community translations. These are translations made by the community, for the community. Some of these translations may be incomplete but they can still make the Vue experience significantly more enjoyable for readers who prefer these languages.
+
+For completeness, we've also included the official documentation in the list.
+
+
+
+Thank you to everyone who has contributed to these translations. Your hard work is very much appreciated.
## How can I get involved with translations?
-The best way to get started is to check out [this pinned issue](https://github.com/vuejs/docs-next/issues/478) that contains active discussions on the various initiatives happening in the community.
+The first step is to check whether there is an existing translation for the relevant language. In addition to the table above, a good place to start is [this pinned issue](https://github.com/vuejs/docs/issues/478), which is used to keep track of the various translation initiatives happening in the community.
+
+If there's already an active translation then there are various ways you can help out. The GitHub repository should be a good place to start gathering information about how a particular translation is managed and who does what. All translation repositories should allow you to file issues and open Pull Requests if you have suggestions for improving the translation.
+
+## What if there isn't an existing translation?
+
+If you want to start a new translation then please do. You don't need our permission to start but you should read the rest of this page carefully to make sure you're clear about what's involved.
+
+Before you start, you might want to check whether there was a community translation for Vue 2. [This pinned issue](https://github.com/vuejs/vuejs.org/issues/2015) from the Vue 2 documentation may help.
+
+Working on a translation is more fun, and more likely to succeed, if there are multiple contributors. Even if you don't know anyone personally who can help out you may be able to find other translators from within the Vue community.
+
+The documentation is constantly being updated. Many changes are small and don't directly impact the translations but others make important improvements that also need translating. Before you start work on a new translation you may want to take a look at the GitHub repositories for some other languages to get a sense of how they handle the update process.
+
+When you first start a translation, you may want to use a personal GitHub repository. That's fine, so long as it's publicly accessible and open to contributions from others. Make sure the *Issues* tab is enabled in the repository's settings. You can migrate the repository to a shared organization at a later stage.
+
+Once you start a translation, be sure to add it to [the pinned issue](https://github.com/vuejs/docs/issues/478) so that the rest of the community can find it.
+
+We also encourage you to update the root-level `README.md` at an early stage. Most translations add extra information to help potential contributors get involved.
+
+## Can I add my translation to this page?
+
+If you've translated more than half of the documentation then please open a Pull Request to add your repository to the table of community translations on this page.
+
+The official documentation is deployed on [Netlify](https://url.netlify.com/HJ8X2mxP8). We encourage you to do the same. Some community translators use their own Netlify accounts but we're happy to host community translations on our account once they're at an advanced stage.
diff --git a/src/guide/contributing/writing-guide.md b/src/guide/contributing/writing-guide.md
index 80661d0f35..5b444770cd 100644
--- a/src/guide/contributing/writing-guide.md
+++ b/src/guide/contributing/writing-guide.md
@@ -57,7 +57,7 @@ Writing documentation is an exercise in empathy. We're not describing an objecti
- **Avoid abbreviations** in writing and code examples (e.g. `attribute` is better than `attr`, `message` is better than `msg`), unless you are specifically referencing an abbreviation in an API (e.g. `$attrs`). Abbreviation symbols included on standard keyboards (e.g. `@`, `#`, `&`) are OK.
- **When referencing a directly following example, use a colon (`:`) to end a sentence**, rather than a period (`.`).
-- **Use the Oxford comma** (e.g. "a, b, and c" instead of "a, b and c"). 
+- **Use the Oxford comma** (e.g. "a, b, and c" instead of "a, b and c"). 
- Source: [The Serial (Oxford) Comma: When and Why To Use It](https://www.inkonhand.com/2015/10/the-serial-oxford-comma-when-and-why-to-use-it/)
- **When referencing the name of a project, use the name that project refers to itself as.** For example, "webpack" and "npm" should both use lowercase as that's how their documentation refers to them.
- **Use Title Case for headings** - at least for now, since it's what we use through the rest of the docs. There's research suggesting that sentence case (only first word of the heading starts with a capital) is actually superior for legibility and also reduces the cognitive overhead for documentation writers, since they don't have to try to remember whether to capitalize words like "and", "with", and "about".
diff --git a/src/guide/custom-directive.md b/src/guide/custom-directive.md
index 2832d45e7a..0f0d8b6db5 100644
--- a/src/guide/custom-directive.md
+++ b/src/guide/custom-directive.md
@@ -49,7 +49,7 @@ A directive definition object can provide several hook functions (all optional):
- `beforeMount`: called when the directive is first bound to the element and before parent component is mounted.
-- `mounted`: called when the bound element's parent component is mounted.
+- `mounted`: called before the bound element's parent component is mounted.
- `beforeUpdate`: called before the containing component's VNode is updated
@@ -132,7 +132,7 @@ Our custom directive is now flexible enough to support a few different use cases
Scroll down the page
-
Stick me {{ pinPadding + 'px' }} from the {{ direction }} of the page
+
Stick me {{ pinPadding + 'px' }} from the {{ direction || 'top' }} of the page
```
@@ -214,4 +214,4 @@ app.component('my-component', {
Unlike attributes, directives can't be passed to a different element with `v-bind="$attrs"`.
-With [fragments](/guide/migration/fragments.html#overview) support, components can potentially have more than one root nodes. When applied to a multi-root component, directive will be ignored and the warning will be thrown.
+With [fragments](/guide/migration/fragments.html#overview) support, components can potentially have more than one root node. When applied to a multi-root component, directive will be ignored and the warning will be thrown.
diff --git a/src/guide/data-methods.md b/src/guide/data-methods.md
index 69c6a14c80..4806bb5f8a 100644
--- a/src/guide/data-methods.md
+++ b/src/guide/data-methods.md
@@ -1,5 +1,7 @@
# Data Properties and Methods
+Learn how to work with data and methods with a free Vue School lesson
+
## Data Properties
The `data` option for a component is a function. Vue calls this function as part of creating a new component instance. It should return an object, which Vue will then wrap in its reactivity system and store on the component instance as `$data`. For convenience, any top-level properties of that object are also exposed directly via the component instance:
diff --git a/src/guide/events.md b/src/guide/events.md
index 7d9e2d5701..b6104cf059 100644
--- a/src/guide/events.md
+++ b/src/guide/events.md
@@ -1,5 +1,7 @@
# Event Handling
+Learn how to handle events in a free Vue School lesson
+
## Listening to Events
We can use the `v-on` directive, which we typically shorten to the `@` symbol, to listen to DOM events and run some JavaScript when they're triggered. The usage would be `v-on:click="methodName"` or with the shortcut, `@click="methodName"`
@@ -25,7 +27,7 @@ Vue.createApp({
Result:
-
+
## Method Event Handlers
@@ -62,7 +64,7 @@ Vue.createApp({
Result:
-
+
## Methods in Inline Handlers
@@ -87,7 +89,7 @@ Vue.createApp({
Result:
-
+
Sometimes we also need to access the original DOM event in an inline statement handler. You can pass it into a method using the special `$event` variable:
@@ -169,7 +171,7 @@ To address this problem, Vue provides **event modifiers** for `v-on`. Recall tha
```
::: tip
-Order matters when using modifiers because the relevant code is generated in the same order. Therefore using `@click.prevent.self` will prevent **all clicks** while `@click.self.prevent` will only prevent clicks on the element itself.
+Order matters when using modifiers because the relevant code is generated in the same order. Therefore using `@click.prevent.self` will prevent **clicks default action on the element itself and its children** while `@click.self.prevent` will only prevent clicks default action on the element itself.
:::
```html
diff --git a/src/guide/forms.md b/src/guide/forms.md
index 6b2c7a53e1..41571c86a3 100644
--- a/src/guide/forms.md
+++ b/src/guide/forms.md
@@ -1,5 +1,7 @@
# Form Input Bindings
+Learn how to work with form inputs with a free Vue School lesson
+
## Basic Usage
You can use the `v-model` directive to create two-way data bindings on form input, textarea, and select elements. It automatically picks the correct way to update the element based on the input type. Although a bit magical, `v-model` is essentially syntax sugar for updating data on user input events, plus special care for some edge cases.
@@ -16,7 +18,7 @@ You can use the `v-model` directive to create two-way data bindings on form inpu
::: tip Note
-For languages that require an [IME](https://en.wikipedia.org/wiki/Input_method) (Chinese, Japanese, Korean etc.), you'll notice that `v-model` doesn't get updated during IME composition. If you want to cater for these updates as well, use `input` event instead.
+For languages that require an [IME](https://en.wikipedia.org/wiki/Input_method) (Chinese, Japanese, Korean etc.), you'll notice that `v-model` doesn't get updated during IME composition. If you want to respond to these updates as well, use an `input` event listener and `value` binding instead of using `v-model`.
:::
### Text
@@ -157,7 +159,7 @@ Multiple select (bound to array):
Selected: {{ selected }}
```
-
+
Dynamic options rendered with `v-for`:
@@ -267,10 +269,10 @@ By default, `v-model` syncs the input with the data after each `input` event (wi
If you want user input to be automatically typecast as a number, you can add the `number` modifier to your `v-model` managed inputs:
```html
-
+
```
-This is often useful, because even with `type="number"`, the value of HTML input elements always returns a string. If the value cannot be parsed with `parseFloat()`, then the original value is returned.
+This is often useful when the input type is `text`. If the input type is `number`, Vue can automatically convert the raw string value to a number, and you don't need to add the `.number` modifier to `v-model`. If the value cannot be parsed with `parseFloat()`, then the original value is returned.
### `.trim`
diff --git a/src/guide/installation.md b/src/guide/installation.md
index 282241dd23..7cd93128a8 100644
--- a/src/guide/installation.md
+++ b/src/guide/installation.md
@@ -2,11 +2,12 @@
Vue.js is built by design to be incrementally adoptable. This means that it can be integrated into a project multiple ways depending on the requirements.
-There are three primary ways of adding Vue.js to a project:
+There are four primary ways of adding Vue.js to a project:
1. Import it as a [CDN package](#cdn) on the page
-2. Install it using [npm](#npm)
-3. Use the official [CLI](#cli) to scaffold a project, which provides batteries-included build setups for a modern frontend workflow (e.g., hot-reload, lint-on-save, and much more)
+2. Download the JavaScript files and [host them yourself](#download-and-self-host)
+3. Install it using [npm](#npm)
+4. Use the official [CLI](#cli) to scaffold a project, which provides batteries-included build setups for a modern frontend workflow (e.g., hot-reload, lint-on-save, and much more)
## Release Notes
@@ -18,6 +19,8 @@ Detailed release notes for each version are available on [GitHub](https://github
> Currently in Beta - Vuex and Router integration is still WIP
+Learn how to install and use Vue Devtools in a free Vue School lesson
+
When using Vue, we recommend also installing the [Vue Devtools](https://github.com/vuejs/vue-devtools#vue-devtools) in your browser, allowing you to inspect and debug your Vue applications in a more user-friendly interface.
[Get the Chrome Extension](https://chrome.google.com/webstore/detail/vuejs-devtools/ljjemllljcmogpfapbkkighbhhppjdbg)
@@ -36,18 +39,36 @@ For prototyping or learning purposes, you can use the latest version with:
For production, we recommend linking to a specific version number and build to avoid unexpected breakage from newer versions.
+## Download and Self Host
+
+If you want to avoid using build tools but can't use a CDN in production then you can download the relevant `.js` file and host it using your own web server. You can then include it using a `
```
-While this seems like a convenience, it requires a custom syntax that breaks the assumption of expressions inside of curly braces being "just JavaScript," which has both learning and implementation costs.
+While this seems like a convenience, it requires a custom syntax that breaks the assumption of expressions inside curly braces being "just JavaScript," which has both learning and implementation costs.
## 3.x Update
@@ -73,13 +73,18 @@ Using the example above, here is one example of how it could be implemented.
Instead of using filters, we recommend replacing them with computed properties or methods.
+[Migration build flags:](migration-build.html#compat-configuration)
+
+- `FILTERS`
+- `COMPILER_FILTERS`
+
### Global Filters
If you are using filters that were globally registered and then used throughout your app, it's likely not convenient to replace them with computed properties or methods in each individual component.
Instead, you can make your global filters available to all components through [globalProperties](../../api/application-config.html#globalproperties):
-```javascript
+```js
// main.js
const app = createApp(App)
diff --git a/src/guide/migration/functional-components.md b/src/guide/migration/functional-components.md
index 876195dcde..f84aa94b20 100644
--- a/src/guide/migration/functional-components.md
+++ b/src/guide/migration/functional-components.md
@@ -117,3 +117,4 @@ For more information on the usage of the new functional components and the chang
- [Migration: Render Functions](/guide/migration/render-function-api.html)
- [Guide: Render Functions](/guide/render-function.html)
+- [Migration build flag: `COMPONENT_FUNCTIONAL`](migration-build.html#compat-configuration)
diff --git a/src/guide/migration/global-api-treeshaking.md b/src/guide/migration/global-api-treeshaking.md
index d730cf71e6..502a272334 100644
--- a/src/guide/migration/global-api-treeshaking.md
+++ b/src/guide/migration/global-api-treeshaking.md
@@ -107,7 +107,7 @@ export function render() {
This essentially means the `Transition` component only gets imported when the application actually makes use of it. In other words, if the application doesn’t have any `` component, the code supporting this feature will not be present in the final bundle.
-With global tree-shaking, the user only “pay” for the features they actually use. Even better, knowing that optional features won't increase the bundle size for applications not using them, framework size has become much less a concern for additional core features in the future, if at all.
+With global tree-shaking, the users only “pay” for the features they actually use. Even better, knowing that optional features won't increase the bundle size for applications not using them, framework size has become much less a concern for additional core features in the future, if at all.
::: warning Important
The above only applies to the [ES Modules builds](/guide/installation.html#explanation-of-different-builds) for use with tree-shaking capable bundlers - the UMD build still includes all features and exposes everything on the Vue global variable (and the compiler will produce appropriate output to use APIs off the global instead of importing).
diff --git a/src/guide/migration/global-api.md b/src/guide/migration/global-api.md
index 1d3a402799..564247dcb1 100644
--- a/src/guide/migration/global-api.md
+++ b/src/guide/migration/global-api.md
@@ -75,16 +75,17 @@ const app = createApp({})
An app instance exposes a subset of the Vue 2 global APIs. The rule of thumb is _any APIs that globally mutate Vue's behavior are now moved to the app instance_. Here is a table of the Vue 2 global APIs and their corresponding instance APIs:
-| 2.x Global API | 3.x Instance API (`app`) |
-| -------------------------- | ----------------------------------------------------------------------------------------------- |
-| Vue.config | app.config |
-| Vue.config.productionTip | _removed_ ([see below](#config-productiontip-removed)) |
-| Vue.config.ignoredElements | app.config.isCustomElement ([see below](#config-ignoredelements-is-now-config-iscustomelement)) |
-| Vue.component | app.component |
-| Vue.directive | app.directive |
-| Vue.mixin | app.mixin |
-| Vue.use | app.use ([see below](#a-note-for-plugin-authors)) |
-| Vue.prototype | app.config.globalProperties ([see below](#vue-prototype-replaced-by-config-globalproperties)) | |
+| 2.x Global API | 3.x Instance API (`app`) |
+| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
+| Vue.config | app.config |
+| Vue.config.productionTip | _removed_ ([see below](#config-productiontip-removed)) |
+| Vue.config.ignoredElements | app.config.compilerOptions.isCustomElement ([see below](#config-ignoredelements-is-now-config-compileroptions-iscustomelement)) |
+| Vue.component | app.component |
+| Vue.directive | app.directive |
+| Vue.mixin | app.mixin |
+| Vue.use | app.use ([see below](#a-note-for-plugin-authors)) |
+| Vue.prototype | app.config.globalProperties ([see below](#vue-prototype-replaced-by-config-globalproperties)) |
+| Vue.extend | _removed_ ([see below](#vue-extend-removed)) |
All other global APIs that do not globally mutate behavior are now named exports, as documented in [Global API Treeshaking](./global-api-treeshaking.html).
@@ -94,7 +95,9 @@ In Vue 3.x, the "use production build" tip will only show up when using the "dev
For ES modules builds, since they are used with bundlers, and in most cases a CLI or boilerplate would have configured the production env properly, this tip will no longer show up.
-### `config.ignoredElements` Is Now `config.isCustomElement`
+[Migration build flag: `CONFIG_PRODUCTION_TIP`](migration-build.html#compat-configuration)
+
+### `config.ignoredElements` Is Now `config.compilerOptions.isCustomElement`
This config option was introduced with the intention to support native custom elements, so the renaming better conveys what it does. The new option also expects a function which provides more flexibility than the old string / RegExp approach:
@@ -104,17 +107,19 @@ Vue.config.ignoredElements = ['my-el', /^ion-/]
// after
const app = createApp({})
-app.config.isCustomElement = tag => tag.startsWith('ion-')
+app.config.compilerOptions.isCustomElement = tag => tag.startsWith('ion-')
```
::: tip Important
In Vue 3, the check of whether an element is a component or not has been moved to the template compilation phase, therefore this config option is only respected when using the runtime compiler. If you are using the runtime-only build, `isCustomElement` must be passed to `@vue/compiler-dom` in the build setup instead - for example, via the [`compilerOptions` option in vue-loader](https://vue-loader.vuejs.org/options.html#compileroptions).
-- If `config.isCustomElement` is assigned to when using a runtime-only build, a warning will be emitted instructing the user to pass the option in the build setup instead;
+- If `config.compilerOptions.isCustomElement` is assigned to when using a runtime-only build, a warning will be emitted instructing the user to pass the option in the build setup instead;
- This will be a new top-level option in the Vue CLI config.
:::
+[Migration build flag: `CONFIG_IGNORED_ELEMENTS`](migration-build.html#compat-configuration)
+
### `Vue.prototype` Replaced by `config.globalProperties`
In Vue 2, `Vue.prototype` was commonly used to add properties that would be accessible in all components.
@@ -134,6 +139,58 @@ app.config.globalProperties.$http = () => {}
Using `provide` (discussed [below](#provide-inject)) should also be considered as an alternative to `globalProperties`.
+[Migration build flag: `GLOBAL_PROTOTYPE`](migration-build.html#compat-configuration)
+
+### `Vue.extend` Removed
+
+In Vue 2.x, `Vue.extend` was used to create a "subclass" of the base Vue constructor with the argument that should be an object containing component options. In Vue 3.x, we don't have the concept of component constructors anymore. Mounting a component should always use the `createApp` global API:
+
+```js
+// before - Vue 2
+
+// create constructor
+const Profile = Vue.extend({
+ template: '{{firstName}} {{lastName}} aka {{alias}}
',
+ data() {
+ return {
+ firstName: 'Walter',
+ lastName: 'White',
+ alias: 'Heisenberg'
+ }
+ }
+})
+// create an instance of Profile and mount it on an element
+new Profile().$mount('#mount-point')
+```
+
+```js
+// after - Vue 3
+const Profile = {
+ template: '{{firstName}} {{lastName}} aka {{alias}}
',
+ data() {
+ return {
+ firstName: 'Walter',
+ lastName: 'White',
+ alias: 'Heisenberg'
+ }
+ }
+}
+
+Vue.createApp(Profile).mount('#mount-point')
+```
+
+#### Type Inference
+
+In Vue 2, `Vue.extend` was also used for providing TypeScript type inference for the component options. In Vue 3, the `defineComponent` global API can be used in place of `Vue.extend` for the same purpose.
+
+Note that although the return type of `defineComponent` is a constructor-like type, it is only used for TSX inference. At runtime `defineComponent` is largely a noop and will return the options object as-is.
+
+#### Component Inheritance
+
+In Vue 3, we strongly recommend favoring composition via [Composition API](/api/composition-api.html) over inheritance and mixins. If for some reason you still need component inheritance, you can use the [`extends` option](/api/options-composition.html#extends) instead of `Vue.extend`.
+
+[Migration build flag: `GLOBAL_EXTEND`](migration-build.html#compat-configuration)
+
### A Note for Plugin Authors
It is a common practice for plugin authors to install the plugins automatically in their UMD builds using `Vue.use`. For instance, this is how the official `vue-router` plugin installs itself in a browser environment:
@@ -187,6 +244,8 @@ app.directive('focus', {
app.mount('#app')
```
+[Migration build flag: `GLOBAL_MOUNT`](migration-build.html#compat-configuration)
+
## Provide / Inject
Similar to using the `provide` option in a 2.x root instance, a Vue 3 app instance can also provide dependencies that can be injected by any component inside the app:
@@ -219,7 +278,7 @@ import Bar from './Bar.vue'
const createMyApp = options => {
const app = createApp(options)
- app.directive('focus', /* ... */)
+ app.directive('focus' /* ... */)
return app
}
diff --git a/src/guide/migration/inline-template-attribute.md b/src/guide/migration/inline-template-attribute.md
index faa79067e4..05cf6f126c 100644
--- a/src/guide/migration/inline-template-attribute.md
+++ b/src/guide/migration/inline-template-attribute.md
@@ -30,6 +30,8 @@ This feature will no longer be supported.
Most of the use cases for `inline-template` assumes a no-build-tool setup, where all templates are written directly inside the HTML page.
+[Migration build flag: `COMPILER_INLINE_TEMPLATE`](migration-build.html#compat-configuration)
+
### Option #1: Use ``
- In-browser playground on [Codepen](https://codepen.io/yyx990803/pen/OJNoaZL)
- In-browser Sandbox on [CodeSandbox](https://v3.vue.new)
- Scaffold via [Vite](https://github.com/vitejs/vite):
```bash
- npm init vite-app hello-vue3 # OR yarn create vite-app hello-vue3
+ npm init vite hello-vue3 -- --template vue # OR yarn create vite hello-vue3 --template vue
```
- Scaffold via [vue-cli](https://cli.vuejs.org/):
@@ -37,6 +40,10 @@ Start learning Vue 3 at [Vue Mastery](https://www.vuemastery.com/courses-path/vu
# select vue 3 preset
```
+## Migration Build
+
+If you have an existing Vue 2 project or library that you intend to upgrade to Vue 3, we provide a build of Vue 3 that offers Vue 2 compatible APIs. Check out the [Migration Build](./migration-build.html) page for more details.
+
## Notable New Features
Some of the new features to keep an eye on in Vue 3 include:
@@ -46,16 +53,13 @@ Some of the new features to keep an eye on in Vue 3 include:
- [Fragments](/guide/migration/fragments.html)
- [Emits Component Option](/guide/component-custom-events.html)
- [`createRenderer` API from `@vue/runtime-core`](https://github.com/vuejs/vue-next/tree/master/packages/runtime-core) to create custom renderers
-- [SFC Composition API Syntax Sugar (`
+```
+
+The `` component has two slots. Both slots only allow for one immediate child node. The node in the `default` slot is shown if possible. If not, the node in the `fallback` slot will be shown instead.
+
+Importantly, the async component doesn't need to be the immediate child of the ``. It can be at any depth within the component tree and doesn't need to appear in the same template as the `` itself. The content is only considered resolved once all descendants are ready.
+
+The other way to trigger the `fallback` slot is for a descendant component to return a promise from its `setup` function. This is typically implemented using `async` rather than explicitly returning a promise:
+
+```js{2}
+export default {
+ async setup() {
+ // Be very careful using `await` inside `setup` as
+ // most Composition API functions will only work
+ // prior to the first `await`
+ const data = await loadData()
+
+ // This is implicitly wrapped in a promise because
+ // the function is `async`
+ return {
+ // ...
+ }
+ }
+}
+```
+
+## Child Updates
+
+Once a `` has resolved the contents of its `default` slot, it can only be triggered again if the `default` root node is replaced. New components nested deeper in the tree are not sufficient to move the `` back into a pending state.
+
+If the root node does change it will trigger the `pending` event. However, by default, it won't update the DOM to show the `fallback` content. Instead, it will continue to show the old DOM until the new components are ready. This can be controlled using the `timeout` prop. This value, expressed in milliseconds, tells the `` component how long to wait before showing the `fallback`. A value of `0` will show it immediately when the `` enters the pending state.
+
+## Events
+
+In addition to the `pending` event, the `` component also has `resolve` and `fallback` events. The `resolve` event is emitted when new content has finished resolving in the `default` slot. The `fallback` event is fired when the contents of the `fallback` slot are shown.
+
+The events could be used, for example, to show a loading indicator in front of the old DOM while new components are loading.
+
+## Combining with Other Components
+
+It is common to want to use `` in combination with the [``](/api/built-in-components.html#transition) and [``](/api/built-in-components.html#keep-alive) components. The nesting order of these components is important to get them all working correctly.
+
+In addition, these components are often used in conjunction with the `` component from [Vue Router](https://next.router.vuejs.org/).
+
+The following example shows how to nest these components so that they all behave as expected. For simpler combinations you can remove the components that you don't need:
+
+```html
+
+
+
+
+
+
+
+
+ Loading...
+
+
+
+
+
+
+
+```
+
+Vue Router has built-in support for [lazily loading components](https://next.router.vuejs.org/guide/advanced/lazy-loading.html) using dynamic imports. These are distinct from async components and currently they will not trigger ``. However, they can still have async components as descendants and those can trigger `` in the usual way.
diff --git a/src/guide/migration/transition-as-root.md b/src/guide/migration/transition-as-root.md
new file mode 100644
index 0000000000..bb15893766
--- /dev/null
+++ b/src/guide/migration/transition-as-root.md
@@ -0,0 +1,61 @@
+---
+badges:
+ - breaking
+---
+
+# Transition as Root
+
+## Overview
+
+Using a `` as a component's root will no longer trigger transitions when the component is toggled from the outside.
+
+## 2.x Behavior
+
+In Vue 2, it was possible to trigger a transition from outside a component by using a `` as the component's root:
+
+```html
+
+
+
+
+
+
+```
+
+```html
+
+hello
+```
+
+Toggling the value of `showModal` would trigger a transition inside the modal component.
+
+This worked by accident, not by design. A `` is supposed to be triggered by changes to its children, not by toggling the `` itself.
+
+This quirk has now been removed.
+
+## Migration Strategy
+
+A similar effect can be achieved by passing a prop to the component instead:
+
+```vue
+
+
+
+
+
+
+```
+
+```html
+
+hello
+```
+
+## See also
+
+- [Some transition classes got a rename](/guide/migration/transition.html)
+- [`` now renders no wrapper element by default](/guide/migration/transition-group.html)
diff --git a/src/guide/migration/transition-group.md b/src/guide/migration/transition-group.md
index 86509dedc5..4137ae4d5f 100644
--- a/src/guide/migration/transition-group.md
+++ b/src/guide/migration/transition-group.md
@@ -8,11 +8,11 @@ badges:
## Overview
-`` no longer renders a root element by default, but can still create one with the `tag` prop.
+`` no longer renders a root element by default, but can still create one with the `tag` attribute.
## 2.x Syntax
-In Vue 2, ``, like other custom components, needed a root element, which by default was a `` but was customizable via the `tag` prop.
+In Vue 2, ``, like other custom components, needed a root element, which by default was a `` but was customizable via the `tag` attribute.
```html
@@ -26,7 +26,7 @@ In Vue 2, ``, like other custom components, needed a root elem
In Vue 3, we have [fragment support](/guide/migration/fragments.html), so components no longer _need_ a root node. Consequently, `` no longer renders one by default.
-- If you already have the `tag` prop defined in your Vue 2 code, like in the example above, everything will work as before
+- If you already have the `tag` attribute defined in your Vue 2 code, like in the example above, everything will work as before
- If you didn't have one defined _and_ your styling or other behaviors relied on the presence of the `` root element to work properly, simply add `tag="span"` to the ``:
```html
@@ -35,6 +35,11 @@ In Vue 3, we have [fragment support](/guide/migration/fragments.html), so compon
```
+## Migration Strategy
+
+[Migration build flag: `TRANSITION_GROUP_ROOT`](migration-build.html#compat-configuration)
+
## See also
- [Some transition classes got a rename](/guide/migration/transition.html)
+- [`` as a root can no longer be toggled from the outside](/guide/migration/transition-as-root.html)
diff --git a/src/guide/migration/transition.md b/src/guide/migration/transition.md
index 645c5697fa..1261c208b8 100644
--- a/src/guide/migration/transition.md
+++ b/src/guide/migration/transition.md
@@ -63,4 +63,5 @@ The `` component's related prop names are also changed:
## See also
+- [`` as a root can no longer be toggled from the outside](/guide/migration/transition-as-root.html)
- [`` now renders no wrapper element by default](/guide/migration/transition-group.html)
diff --git a/src/guide/migration/v-bind.md b/src/guide/migration/v-bind.md
index b5d97fa7ec..902e2ce4cb 100644
--- a/src/guide/migration/v-bind.md
+++ b/src/guide/migration/v-bind.md
@@ -12,11 +12,11 @@ badges:
## Introduction
-When dynamically binding attributes on an element, a common scenario involves using both the `v-bind="object"` syntax as well as individual properties in the same element. However, this raises questions as far as the priority of merging.
+When dynamically binding attributes on an element, a common scenario involves using both the `v-bind="object"` syntax as well as individual attributes in the same element. However, this raises questions as far as the priority of merging.
## 2.x Syntax
-In 2.x, if an element has both `v-bind="object"` and an identical individual property defined, the individual property would always overwrite bindings in the `object`.
+In 2.x, if an element has both `v-bind="object"` and an identical individual attribute defined, the individual attribute would always overwrite bindings in the `object`.
```html
@@ -27,7 +27,7 @@ In 2.x, if an element has both `v-bind="object"` and an identical individual pro
## 3.x Syntax
-In 3x, if an element has both `v-bind="object"` and an identical individual property defined, the order of how the bindings are declared determines how they are merged. In other words, rather than assuming developers want the individual property to always override what is defined in the `object`, developers now have more control over the desired merging behavior.
+In 3x, if an element has both `v-bind="object"` and an identical individual attribute defined, the order of how the bindings are declared determines how they are merged. In other words, rather than assuming developers want the individual attribute to always override what is defined in the `object`, developers now have more control over the desired merging behavior.
```html
@@ -43,4 +43,6 @@ In 3x, if an element has both `v-bind="object"` and an identical individual prop
## Migration Strategy
-If you are relying on this override functionality for `v-bind`, we currently recommend ensuring that your `v-bind` attribute is defined before individual properties.
+If you are relying on this override functionality for `v-bind`, we currently recommend ensuring that your `v-bind` attribute is defined before individual attributes.
+
+[Migration build flag: `COMPILER_V_BIND_OBJECT_ORDER`](migration-build.html#compat-configuration)
diff --git a/src/guide/migration/v-if-v-for.md b/src/guide/migration/v-if-v-for.md
index 75f52016cf..8908083771 100644
--- a/src/guide/migration/v-if-v-for.md
+++ b/src/guide/migration/v-if-v-for.md
@@ -28,6 +28,8 @@ It is recommended to avoid using both on the same element due to the syntax ambi
Rather than managing this at the template level, one method for accomplishing this is to create a computed property that filters out a list for the visible elements.
+[Migration build flag: `COMPILER_V_IF_V_FOR_PRECEDENCE`](migration-build.html#compat-configuration)
+
## See also
- [List Rendering - Displaying Filtered/Sorted Results](/guide/list.html#displaying-filtered-sorted-results)
diff --git a/src/guide/migration/v-model.md b/src/guide/migration/v-model.md
index 22b5a96a26..928a2b3169 100644
--- a/src/guide/migration/v-model.md
+++ b/src/guide/migration/v-model.md
@@ -86,7 +86,7 @@ Then the parent could listen to that event and update a local data property, if
```
-For convenience, we had a shorthand for this pattern with the .sync modifier:
+For convenience, we had a shorthand for this pattern with the `.sync` modifier:
```html
@@ -182,10 +182,15 @@ We recommend:
}
```
+[Migration build flags:](migration-build.html#compat-configuration)
+
+- `COMPONENT_V_MODEL`
+- `COMPILER_V_BIND_SYNC`
+
## Next Steps
For more information on the new `v-model` syntax, see:
- [Using `v-model` on Components](../component-basics.html#using-v-model-on-components)
- [`v-model` arguments](../component-custom-events.html#v-model-arguments)
-- [Handling `v-model` modifiers](../component-custom-events.html#v-model-arguments)
+- [Handling `v-model` modifiers](../component-custom-events.html#handling-v-model-modifiers)
diff --git a/src/guide/migration/v-on-native-modifier-removed.md b/src/guide/migration/v-on-native-modifier-removed.md
index fcaffe0870..fd9c153b49 100644
--- a/src/guide/migration/v-on-native-modifier-removed.md
+++ b/src/guide/migration/v-on-native-modifier-removed.md
@@ -49,6 +49,8 @@ Consequently, Vue will now add all event listeners that are _not_ defined as com
- remove all instances of the `.native` modifier.
- ensure that all your components document their events with the `emits` option.
+[Migration build flag: `COMPILER_V_ON_NATIVE`](migration-build.html#compat-configuration)
+
## See also
- [Relevant RFC](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0031-attr-fallthrough.md#v-on-listener-fallthrough)
diff --git a/src/guide/migration/vnode-lifecycle-events.md b/src/guide/migration/vnode-lifecycle-events.md
new file mode 100644
index 0000000000..8ad0b231b9
--- /dev/null
+++ b/src/guide/migration/vnode-lifecycle-events.md
@@ -0,0 +1,50 @@
+---
+badges:
+ - breaking
+---
+
+# VNode Lifecycle Events
+
+## Overview
+
+In Vue 2, it was possible to use events to listen for key stages in a component's lifecycle. These events had names that started with the prefix `hook:`, followed by the name of the corresponding lifecycle hook.
+
+In Vue 3, this prefix has been changed to `vnode-`. In addition, these events are now available for HTML elements as well as components.
+
+## 2.x Syntax
+
+In Vue 2, the event name is the same as the equivalent lifecycle hook, prefixed with `hook:`:
+
+```html
+
+
+
+```
+
+## 3.x Syntax
+
+In Vue 3, the event name is prefixed with `vnode-`:
+
+```html
+
+
+
+```
+
+Or just `vnode` if you're using camelCase:
+
+```html
+
+
+
+```
+
+## Migration Strategy
+
+In most cases it should just require changing the prefix. The lifecycle hooks `beforeDestroy` and `destroyed` have been renamed to `beforeUnmount` and `unmounted` respectively, so the corresponding event names will also need to be updated.
+
+[Migration build flags: `INSTANCE_EVENT_HOOKS`](migration-build.html#compat-configuration)
+
+## See Also
+
+- [Migration guide - Events API](/guide/migration/events-api.html)
diff --git a/src/guide/migration/watch.md b/src/guide/migration/watch.md
index 3a6f579518..56d93be663 100644
--- a/src/guide/migration/watch.md
+++ b/src/guide/migration/watch.md
@@ -27,4 +27,6 @@ watch: {
## Migration Strategy
-If you rely on watching array mutations, add the `deep` property to ensure that your callback is triggered correctly.
+If you rely on watching array mutations, add the `deep` option to ensure that your callback is triggered correctly.
+
+[Migration build flag: `WATCH_ARRAY`](migration-build.html#compat-configuration)
diff --git a/src/guide/mixins.md b/src/guide/mixins.md
index 8d21cf11be..8103c019b2 100644
--- a/src/guide/mixins.md
+++ b/src/guide/mixins.md
@@ -31,7 +31,7 @@ app.mount('#mixins-basic') // => "hello from mixin!"
When a mixin and the component itself contain overlapping options, they will be "merged" using appropriate strategies.
-For example, data objects undergo a recursive merge, with the component's data taking priority in cases of conflicts.
+For example, each mixin can have its own `data` function. Each of them will be called, with the returned objects being merged. Properties from the component's own data will take priority in cases of conflicts.
```js
const myMixin = {
@@ -213,12 +213,14 @@ app.mixin({
})
```
-## Precautions
+## Drawbacks
In Vue 2, mixins were the primary tool to abstract parts of component logic into reusable chunks. However, they have a few issues:
-- Mixins are conflict-prone: Since properties from each feature are merged into the same component, you still have to know about every other feature to avoid property name conflicts and for debugging.
+- Mixins are conflict-prone: Since properties from each mixin are merged into the same component, you still have to know about every other mixin to avoid property name conflicts.
-- Reusability is limited: we cannot pass any parameters to the mixin to change its logic which reduces their flexibility in terms of abstracting logic
+- Properties seem to appear from nowhere: If a component uses multiple mixins it isn't necessarily obvious which properties came from which mixin.
+
+- Reusability is limited: we cannot pass any parameters to the mixin to change its logic, which reduces their flexibility in terms of abstracting logic.
To address these issues, we added a new way to organize code by logical concerns: the [Composition API](composition-api-introduction.html).
diff --git a/src/guide/mobile.md b/src/guide/mobile.md
index b9b1188c16..8ed7e3e34c 100644
--- a/src/guide/mobile.md
+++ b/src/guide/mobile.md
@@ -20,4 +20,4 @@ While Vue.js does not natively support mobile app development, there are a numbe
**Resources**
-- [NativeScript + Vue.js Guide](https://nativescript.org/vue/)
+- [NativeScript + Vue.js Guide](https://nativescript-vue.org/)
diff --git a/src/guide/optimizations.md b/src/guide/optimizations.md
index 46303af8c5..b5f6d62e65 100644
--- a/src/guide/optimizations.md
+++ b/src/guide/optimizations.md
@@ -7,10 +7,10 @@
Now that we know how watchers are updating the components, you might ask how those changes eventually make it to the DOM! Perhaps you’ve heard of the Virtual DOM before, many frameworks including Vue use this paradigm to make sure our interfaces reflect the changes we’re updating in JavaScript effectively
-
+
-We make a copy of the DOM in JavaScript called the Virtual DOM, we do this because touching the DOM with JavaScript is computationally expensive. While performing updates in JavaScript is cheap, finding the required DOM nodes and updating them with JS is expensive. So we batch calls, and change the DOM all at once.
+We make a copy of the DOM in JavaScript called the Virtual DOM, we do this because touching the DOM with JavaScript is computationally expensive. While performing updates in JavaScript is cheap, finding the required DOM nodes and updating them with JavaScript is expensive. So we batch calls, and change the DOM all at once.
The Virtual DOM is a lightweight JavaScript object, created by a render function. It takes three arguments: the element, an object with data, props, attrs and more, and an array. The array is where we pass in the children, which have all these arguments too, and then they can have children and so on, until we build a full tree of elements.
diff --git a/src/guide/plugins.md b/src/guide/plugins.md
index 48481a9587..aa3b44f847 100644
--- a/src/guide/plugins.md
+++ b/src/guide/plugins.md
@@ -6,7 +6,7 @@ There is no strictly defined scope for a plugin, but common scenarios where plug
1. Add some global methods or properties, e.g. [vue-custom-element](https://github.com/karol-f/vue-custom-element).
-2. Add one or more global assets: directives/filters/transitions etc. (e.g. [vue-touch](https://github.com/vuejs/vue-touch)).
+2. Add one or more global assets: directives/transitions etc. (e.g. [vue-touch](https://github.com/vuejs/vue-touch)).
3. Add some component options by global mixin (e.g. [vue-router](https://github.com/vuejs/vue-router)).
diff --git a/src/guide/reactivity-computed-watchers.md b/src/guide/reactivity-computed-watchers.md
index 20c75b62fc..b1860b03ef 100644
--- a/src/guide/reactivity-computed-watchers.md
+++ b/src/guide/reactivity-computed-watchers.md
@@ -4,7 +4,7 @@
## Computed values
-Sometimes we need state that depends on other state - in Vue this is handled with component [computed properties](computed.html#computed-properties). To directly create a computed value, we can use the `computed` method: it takes a getter function and returns an immutable reactive [ref](reactivity-fundamentals.html#creating-standalone-reactive-values-as-refs) object for the returned value from the getter.
+Sometimes we need state that depends on other state - in Vue this is handled with component [computed properties](computed.html#computed-properties). To directly create a computed value, we can use the `computed` function: it takes a getter function and returns an immutable reactive [ref](reactivity-fundamentals.html#creating-standalone-reactive-values-as-refs) object for the returned value from the getter.
```js
const count = ref(1)
@@ -30,9 +30,39 @@ plusOne.value = 1
console.log(count.value) // 0
```
+### Computed Debugging
+
+`computed` accepts a second argument with `onTrack` and `onTrigger` options:
+
+- `onTrack` will be called when a reactive property or ref is tracked as a dependency.
+- `onTrigger` will be called when the watcher callback is triggered by the mutation of a dependency.
+
+Both callbacks will receive a debugger event which contains information on the dependency in question. It is recommended to place a `debugger` statement in these callbacks to interactively inspect the dependency:
+
+```js
+const plusOne = computed(() => count.value + 1, {
+ onTrack(e) {
+ // triggered when count.value is tracked as a dependency
+ debugger
+ },
+ onTrigger(e) {
+ // triggered when count.value is mutated
+ debugger
+ }
+})
+
+// access plusOne, should trigger onTrack
+console.log(plusOne.value)
+
+// mutate count.value, should trigger onTrigger
+count.value++
+```
+
+`onTrack` and `onTrigger` only work in development mode.
+
## `watchEffect`
-To apply and _automatically re-apply_ a side effect based on reactive state, we can use the `watchEffect` method. It runs a function immediately while reactively tracking its dependencies and re-runs it whenever the dependencies are changed.
+To apply and _automatically re-apply_ a side effect based on reactive state, we can use the `watchEffect` function. It runs a function immediately while reactively tracking its dependencies and re-runs it whenever the dependencies are changed.
```js
const count = ref(0)
@@ -83,8 +113,10 @@ We are registering the invalidation callback via a passed-in function instead of
```js
const data = ref(null)
-watchEffect(async (onInvalidate) => {
- onInvalidate(() => { /* ... */ }) // we register cleanup function before Promise resolves
+watchEffect(async onInvalidate => {
+ onInvalidate(() => {
+ /* ... */
+ }) // we register cleanup function before Promise resolves
data.value = await fetchData(props.id)
})
```
@@ -95,26 +127,25 @@ An async function implicitly returns a Promise, but the cleanup function needs t
Vue's reactivity system buffers invalidated effects and flushes them asynchronously to avoid unnecessary duplicate invocation when there are many state mutations happening in the same "tick". Internally, a component's `update` function is also a watched effect. When a user effect is queued, it is by default invoked **before** all component `update` effects:
-```html
-
+```vue
{{ count }}
```
@@ -123,7 +154,7 @@ In this example:
- The count will be logged synchronously on initial run.
- When `count` is mutated, the callback will be called **before** the component has updated.
-In cases where a watcher effect needs to be re-run **after** component updates, we can pass an additional `options` object with the `flush` option (default is `'pre'`):
+In cases where a watcher effect needs to be re-run **after** component updates (i.e. when working with [Template Refs](./composition-api-template-refs.md#watching-template-refs)), we can pass an additional `options` object with the `flush` option (default is `'pre'`):
```js
// fire after component updates so you can access the updated DOM
@@ -141,6 +172,8 @@ watchEffect(
The `flush` option also accepts `'sync'`, which forces the effect to always trigger synchronously. This is however inefficient and should be rarely needed.
+In Vue >= 3.2.0, `watchPostEffect` and `watchSyncEffect` aliases can also be used to make the code intention more obvious.
+
### Watcher Debugging
The `onTrack` and `onTrigger` options can be used to debug a watcher's behavior.
@@ -201,15 +234,48 @@ watch(count, (count, prevCount) => {
A watcher can also watch multiple sources at the same time using an array:
```js
-const firstName = ref('');
-const lastName = ref('');
+const firstName = ref('')
+const lastName = ref('')
watch([firstName, lastName], (newValues, prevValues) => {
- console.log(newValues, prevValues);
+ console.log(newValues, prevValues)
})
-firstName.value = "John"; // logs: ["John",""] ["", ""]
-lastName.value = "Smith"; // logs: ["John", "Smith"] ["John", ""]
+firstName.value = 'John' // logs: ["John", ""] ["", ""]
+lastName.value = 'Smith' // logs: ["John", "Smith"] ["John", ""]
+```
+
+However, if you are changing both watched sources simultaneously in the same function, the watcher will be executed only once:
+
+```js{9-13}
+setup() {
+ const firstName = ref('')
+ const lastName = ref('')
+
+ watch([firstName, lastName], (newValues, prevValues) => {
+ console.log(newValues, prevValues)
+ })
+
+ const changeValues = () => {
+ firstName.value = 'John'
+ lastName.value = 'Smith'
+ // logs: ["John", "Smith"] ["", ""]
+ }
+
+ return { changeValues }
+}
+```
+
+Note that multiple synchronous changes will only trigger the watcher once.
+
+It is possible to force the watcher to trigger after every change by using the setting `flush: 'sync'`, though that isn't usually recommended. Alternatively, [nextTick](/api/global-api.html#nexttick) can be used to wait for the watcher to run before making further changes. e.g.:
+
+```js
+const changeValues = async () => {
+ firstName.value = 'John' // logs: ["John", ""] ["", ""]
+ await nextTick()
+ lastName.value = 'Smith' // logs: ["John", "Smith"] ["John", ""]
+}
```
### Watching Reactive Objects
@@ -222,8 +288,9 @@ const numbers = reactive([1, 2, 3, 4])
watch(
() => [...numbers],
(numbers, prevNumbers) => {
- console.log(numbers, prevNumbers);
- })
+ console.log(numbers, prevNumbers)
+ }
+)
numbers.push(5) // logs: [1,2,3,4,5] [1,2,3,4]
```
@@ -231,62 +298,51 @@ numbers.push(5) // logs: [1,2,3,4,5] [1,2,3,4]
Attempting to check for changes of properties in a deeply nested object or array will still require the `deep` option to be true:
```js
-const state = reactive({
- id: 1,
- attributes: {
- name: "",
- },
-});
+const state = reactive({
+ id: 1,
+ attributes: {
+ name: ''
+ }
+})
watch(
() => state,
(state, prevState) => {
- console.log(
- "not deep ",
- state.attributes.name,
- prevState.attributes.name
- );
+ console.log('not deep', state.attributes.name, prevState.attributes.name)
}
-);
+)
watch(
() => state,
(state, prevState) => {
- console.log(
- "deep ",
- state.attributes.name,
- prevState.attributes.name
- );
+ console.log('deep', state.attributes.name, prevState.attributes.name)
},
{ deep: true }
-);
+)
-state.attributes.name = "Alex"; // Logs: "deep " "Alex" "Alex"
+state.attributes.name = 'Alex' // Logs: "deep" "Alex" "Alex"
```
However, watching a reactive object or array will always return a reference to the current value of that object for both the current and previous value of the state. To fully watch deeply nested objects and arrays, a deep copy of values may be required. This can be achieved with a utility such as [lodash.cloneDeep](https://lodash.com/docs/4.17.15#cloneDeep)
```js
-import _ from 'lodash';
+import _ from 'lodash'
const state = reactive({
id: 1,
attributes: {
- name: "",
- },
-});
+ name: ''
+ }
+})
watch(
() => _.cloneDeep(state),
(state, prevState) => {
- console.log(
- state.attributes.name,
- prevState.attributes.name
- );
+ console.log(state.attributes.name, prevState.attributes.name)
}
-);
+)
-state.attributes.name = "Alex"; // Logs: "Alex" ""
+state.attributes.name = 'Alex' // Logs: "Alex" ""
```
### Shared Behavior with `watchEffect`
diff --git a/src/guide/reactivity-fundamentals.md b/src/guide/reactivity-fundamentals.md
index b2a440b165..bbaef88a95 100644
--- a/src/guide/reactivity-fundamentals.md
+++ b/src/guide/reactivity-fundamentals.md
@@ -1,5 +1,7 @@
# Reactivity Fundamentals
+> This section uses [single-file component](single-file-component.html) syntax for code examples
+
## Declaring Reactive State
To create a reactive state from a JavaScript object, we can use a `reactive` method:
@@ -45,13 +47,14 @@ console.log(count.value) // 1
### Ref Unwrapping
-When a ref is returned as a property on the render context (the object returned from [setup()](composition-api-setup.html)) and accessed in the template, it automatically unwraps to the inner value. There is no need to append `.value` in the template:
+When a ref is returned as a property on the render context (the object returned from [setup()](composition-api-setup.html)) and accessed in the template, it automatically shallow unwraps the inner value. Only the nested ref will require `.value` in the template:
```vue-html
{{ count }}
Increment count
+ Nested Increment count
@@ -61,13 +64,27 @@ When a ref is returned as a property on the render context (the object returned
setup() {
const count = ref(0)
return {
- count
+ count,
+
+ nested: {
+ count
+ }
}
}
}
```
+:::tip
+If you don't want to access the actual object instance, you can wrap it in a `reactive`:
+
+```js
+nested: reactive({
+ count
+})
+```
+:::
+
### Access in Reactive Objects
When a `ref` is accessed or mutated as a property of a reactive object, it automatically unwraps to the inner value so it behaves like a normal property:
diff --git a/src/guide/reactivity.md b/src/guide/reactivity.md
index a1ec3a855d..ba6ce6c3ee 100644
--- a/src/guide/reactivity.md
+++ b/src/guide/reactivity.md
@@ -13,62 +13,100 @@ This term comes up in programming quite a bit these days, but what do people mea
Your browser does not support the video tag.
-If you put the number two in the first cell, and the number 3 in the second and asked for the SUM, the spreadsheet would give it to you. No surprises there. But if you update that first number, the SUM automagically updates too.
+If you put the number 2 in the first cell, and the number 3 in the second and asked for the SUM, the spreadsheet would give it to you. No surprises there. But if you update that first number, the SUM automagically updates too.
-JavaScript doesn’t usually work like this -- If we were to write something comparable in JavaScript:
+JavaScript doesn’t usually work like this. If we were to write something comparable in JavaScript:
```js
-var val1 = 2
-var val2 = 3
-var sum = val1 + val2
+let val1 = 2
+let val2 = 3
+let sum = val1 + val2
-// sum
-// 5
+console.log(sum) // 5
val1 = 3
-// sum
-// 5
+console.log(sum) // Still 5
```
If we update the first value, the sum is not adjusted.
So how would we do this in JavaScript?
-- Detect when there’s a change in one of the values
-- Track the function that changes it
-- Trigger the function so it can update the final value
+As a high-level overview, there are a few things we need to be able to do:
-## How Vue Tracks These Changes
+1. **Track when a value is read.** e.g. `val1 + val2` reads both `val1` and `val2`.
+2. **Detect when a value changes.** e.g. When we assign `val1 = 3`.
+3. **Re-run the code that read the value originally.** e.g. Run `sum = val1 + val2` again to update the value of `sum`.
-When you pass a plain JavaScript object to an application or component instance as its `data` option, Vue will walk through all of its properties and convert them to [Proxies](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy) using a handler with getters and setters. This is an ES6-only feature, but we offer a version of Vue 3 that uses the older `Object.defineProperty` to support IE browsers. Both have the same surface API, but the Proxy version is slimmer and offers improved performance.
+We can't do this directly using the code from the previous example but we'll come back to this example later to see how to adapt it to be compatible with Vue's reactivity system.
-
-
-
+First, let's dig a bit deeper into how Vue implements the core reactivity requirements outlined above.
-That was rather quick and requires some knowledge of [Proxies](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy) to understand! So let’s dive in a bit. There’s a lot of literature on Proxies, but what you really need to know is that a **Proxy is an object that encases another object or function and allows you to intercept it.**
+## How Vue Knows What Code Is Running
-We use it like this: `new Proxy(target, handler)`
+To be able to run our sum whenever the values change, the first thing we need to do is wrap it in a function:
```js
-const dinner = {
- meal: 'tacos'
+const updateSum = () => {
+ sum = val1 + val2
}
+```
-const handler = {
- get(target, prop) {
- return target[prop]
+But how do we tell Vue about this function?
+
+Vue keeps track of which function is currently running by using an *effect*. An effect is a wrapper around the function that initiates tracking just before the function is called. Vue knows which effect is running at any given point and can run it again when required.
+
+To understand that better, let's try to implement something similar ourselves, without Vue, to see how it might work.
+
+What we need is something that can wrap our sum, like this:
+
+```js
+createEffect(() => {
+ sum = val1 + val2
+})
+```
+
+We need `createEffect` to keep track of when the sum is running. We might implement it something like this:
+
+```js
+// Maintain a stack of running effects
+const runningEffects = []
+
+const createEffect = fn => {
+ // Wrap the passed fn in an effect function
+ const effect = () => {
+ runningEffects.push(effect)
+ fn()
+ runningEffects.pop()
}
+
+ // Automatically run the effect immediately
+ effect()
}
+```
-const proxy = new Proxy(dinner, handler)
-console.log(proxy.meal)
+When our effect is called it pushes itself onto the `runningEffects` array, before calling `fn`. Anything that needs to know which effect is currently running can check that array.
-// tacos
-```
+Effects act as the starting point for many key features. For example, both component rendering and computed properties use effects internally. Any time something magically responds to data changes you can be pretty sure it has been wrapped in an effect.
+
+While Vue's public API doesn't include any way to create an effect directly, it does expose a function called `watchEffect` that behaves a lot like the `createEffect` function from our example. We'll discuss that in more detail [later in the guide](/guide/reactivity-computed-watchers.html#watcheffect).
+
+But knowing what code is running is just one part of the puzzle. How does Vue know what values the effect uses and how does it know when they change?
-Ok, so far, we’re just wrapping that object and returning it. Cool, but not that useful yet. But watch this, we can also intercept this object while we wrap it in the Proxy. This interception is called a trap.
+## How Vue Tracks These Changes
+
+We can't track reassignments of local variables like those in our earlier examples, there's just no mechanism for doing that in JavaScript. What we can track are changes to object properties.
+
+When we return a plain JavaScript object from a component's `data` function, Vue will wrap that object in a [Proxy](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy) with handlers for `get` and `set`. Proxies were introduced in ES6 and allow Vue 3 to avoid some of the reactivity caveats that existed in earlier versions of Vue.
+
+
+
+
+
+That was rather quick and requires some knowledge of [Proxies](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy) to understand! So let’s dive in a bit. There’s a lot of literature on Proxies, but what you really need to know is that a **Proxy is an object that encases another object and allows you to intercept any interactions with that object.**
+
+We use it like this: `new Proxy(target, handler)`
```js
const dinner = {
@@ -76,9 +114,9 @@ const dinner = {
}
const handler = {
- get(target, prop) {
+ get(target, property) {
console.log('intercepted!')
- return target[prop]
+ return target[property]
}
}
@@ -89,9 +127,11 @@ console.log(proxy.meal)
// tacos
```
+Here we've intercepted attempts to read properties of the target object. A handler function like this is also known as a *trap*. There are many different types of trap available, each handling a different type of interaction.
+
Beyond a console log, we could do anything here we wish. We could even _not_ return the real value if we wanted to. This is what makes Proxies so powerful for creating APIs.
-Furthermore, there’s another feature Proxies offer us. Rather than just returning the value like this: `target[prop]`, we could take this a step further and use a feature called `Reflect`, which allows us to do proper `this` binding. It looks like this:
+One challenge with using a Proxy is the `this` binding. We'd like any methods to be bound to the Proxy, rather than the target object, so that we can intercept them too. Thankfully, ES6 introduced another new feature, called `Reflect`, that allows us to make this problem disappear with minimal effort:
```js{7}
const dinner = {
@@ -99,7 +139,7 @@ const dinner = {
}
const handler = {
- get(target, prop, receiver) {
+ get(target, property, receiver) {
return Reflect.get(...arguments)
}
}
@@ -110,7 +150,7 @@ console.log(proxy.meal)
// tacos
```
-We mentioned before that in order to have an API that updates a final value when something changes, we’re going to have to set new values when something changes. We do this in the handler, in a function called `track`, where we pass in the `target` and `key`.
+The first step towards implementing reactivity with a Proxy is to track when a property is read. We do this in the handler, in a function called `track`, where we pass in the `target` and `property`:
```js{7}
const dinner = {
@@ -118,8 +158,8 @@ const dinner = {
}
const handler = {
- get(target, prop, receiver) {
- track(target, prop)
+ get(target, property, receiver) {
+ track(target, property)
return Reflect.get(...arguments)
}
}
@@ -130,7 +170,9 @@ console.log(proxy.meal)
// tacos
```
-Finally, we also set new values when something changes. For this, we’re going to set the changes on our new proxy, by triggering those changes:
+The implementation of `track` isn't shown here. It will check which *effect* is currently running and record that alongside the `target` and `property`. This is how Vue knows that the property is a dependency of the effect.
+
+Finally, we need to re-run the effect when the property value changes. For this we're going to need a `set` handler on our proxy:
```js
const dinner = {
@@ -138,12 +180,12 @@ const dinner = {
}
const handler = {
- get(target, prop, receiver) {
- track(target, prop)
+ get(target, property, receiver) {
+ track(target, property)
return Reflect.get(...arguments)
},
- set(target, key, value, receiver) {
- trigger(target, key)
+ set(target, property, value, receiver) {
+ trigger(target, property)
return Reflect.set(...arguments)
}
}
@@ -154,13 +196,52 @@ console.log(proxy.meal)
// tacos
```
-Remember this list from a few paragraphs ago? Now we have some answers to how Vue handles these changes:
+Remember this list from earlier? Now we have some answers to how Vue implements these key steps:
+
+1. **Track when a value is read**: the `track` function in the proxy's `get` handler records the property and the current effect.
+2. **Detect when that value changes**: the `set` handler is called on the proxy.
+3. **Re-run the code that read the value originally:** the `trigger` function looks up which effects depend on the property and runs them.
+
+The proxied object is invisible to the user, but under the hood it enables Vue to perform dependency-tracking and change-notification when properties are accessed or modified. One caveat is that console logging will format proxied objects differently, so you may want to install [vue-devtools](https://github.com/vuejs/vue-devtools) for a more inspection-friendly interface.
+
+If we were to rewrite our original example using a component we might do it something like this:
+
+```js
+const vm = createApp({
+ data() {
+ return {
+ val1: 2,
+ val2: 3
+ }
+ },
+ computed: {
+ sum() {
+ return this.val1 + this.val2
+ }
+ }
+}).mount('#app')
+
+console.log(vm.sum) // 5
+
+vm.val1 = 3
+
+console.log(vm.sum) // 6
+```
+
+The object returned by `data` will be wrapped in a reactive proxy and stored as `this.$data`. The properties `this.val1` and `this.val2` are aliases for `this.$data.val1` and `this.$data.val2` respectively, so they go through the same proxy.
+
+Vue will wrap the function for `sum` in an effect. When we try to access `this.sum`, it will run that effect to calculate the value. The reactive proxy around `$data` will track that the properties `val1` and `val2` were read while that effect is running.
-- Detect when there’s a change in one of the values : we no longer have to do this, as Proxies allow us to intercept it
-- **Track the function that changes it**: We do this in a getter within the proxy, called `effect`
-- **Trigger the function so it can update the final value**: We do in a setter within the proxy, called `trigger`
+As of Vue 3, our reactivity is now available in a [separate package](https://github.com/vuejs/vue-next/tree/master/packages/reactivity). The function that wraps `$data` in a proxy is called [`reactive`](/api/basic-reactivity.html#reactive). We can call this directly ourselves, allowing us to wrap an object in a reactive proxy without needing to use a component:
+
+```js
+const proxy = reactive({
+ val1: 2,
+ val2: 3
+})
+```
-The proxied object is invisible to the user, but under the hood they enable Vue to perform dependency-tracking and change-notification when properties are accessed or modified. As of Vue 3, our reactivity is now available in a [separate package](https://github.com/vuejs/vue-next/tree/master/packages/reactivity). One caveat is that browser consoles format differently when converted data objects are logged, so you may want to install [vue-devtools](https://github.com/vuejs/vue-devtools) for a more inspection-friendly interface.
+We'll explore the functionality exposed by the reactivity package over the course of the next few pages of this guide. That includes functions like `reactive` and `watchEffect` that we've already met, as well as ways to use other reactivity features, such as `computed` and `watch`, without needing to create a component.
### Proxied Objects
@@ -168,12 +249,13 @@ Vue internally tracks all objects that have been made reactive, so it always ret
When a nested object is accessed from a reactive proxy, that object is _also_ converted into a proxy before being returned:
-```js
+```js{6-7}
const handler = {
- get(target, prop, receiver) {
- track(target, prop)
+ get(target, property, receiver) {
+ track(target, property)
const value = Reflect.get(...arguments)
if (isObject(value)) {
+ // Wrap the nested object in its own reactive proxy
return reactive(value)
} else {
return value
@@ -185,7 +267,7 @@ const handler = {
### Proxy vs. original identity
-The use of Proxy does introduce a new caveat to be aware with: the proxied object is not equal to the original object in terms of identity comparison (`===`). For example:
+The use of Proxy does introduce a new caveat to be aware of: the proxied object is not equal to the original object in terms of identity comparison (`===`). For example:
```js
const obj = {}
@@ -194,10 +276,9 @@ const wrapped = new Proxy(obj, handlers)
console.log(obj === wrapped) // false
```
-The original and the wrapped version will behave the same in most cases, but be aware that they will fail
-operations that rely on strong identity comparisons, such as `.filter()` or `.map()`. This caveat is unlikely to come up when using the options API, because all reactive state is accessed from `this` and guaranteed to already be proxies.
+Other operations that rely on strict equality comparisons can also be impacted, such as `.includes()` or `.indexOf()`.
-However, when using the composition API to explicitly create reactive objects, the best practice is to never hold a reference to the original raw object and only work with the reactive version:
+The best practice here is to never hold a reference to the original raw object and only work with the reactive version:
```js
const obj = reactive({
@@ -205,18 +286,26 @@ const obj = reactive({
}) // no reference to original
```
-## Watchers
+This ensures that both equality comparisons and reactivity behave as expected.
-Every component instance has a corresponding watcher instance, which records any properties "touched" during the component’s render as dependencies. Later on when a dependency’s setter is triggered, it notifies the watcher, which in turn causes the component to re-render.
+Note that Vue does not wrap primitive values such as numbers or strings in a Proxy, so you can still use `===` directly with those values:
-
-
-
+```js
+const obj = reactive({
+ count: 0
+})
+
+console.log(obj.count === 0) // true
+```
+
+## How Rendering Reacts to Changes
-When you pass an object to a component instance as data, Vue converts it to a proxy. This proxy enables Vue to perform dependency-tracking and change-notification when properties are accessed or modified. Each property is considered a dependency.
+The template for a component is compiled down into a [`render`](/guide/render-function.html) function. The `render` function creates the [VNodes](/guide/render-function.html#the-virtual-dom-tree) that describe how the component should be rendered. It is wrapped in an effect, allowing Vue to track the properties that are 'touched' while it is running.
-After the first render, a component would have tracked a list of dependencies — the properties it accessed during the render. Conversely, the component becomes a subscriber to each of these properties. When a proxy intercepts a set operation, the property will notify all of its subscribed components to re-render.
+A `render` function is conceptually very similar to a `computed` property. Vue doesn't track exactly how dependencies are used, it only knows that they were used at some point while the function was running. If any of those properties subsequently changes, it will trigger the effect to run again, re-running the `render` function to generate new VNodes. These are then used to make the necessary changes to the DOM.
-[//]: # 'TODO: Insert diagram'
+
+
+
> If you are using Vue 2.x and below, you may be interested in some of the change detection caveats that exist for those versions, [explored in more detail here](change-detection.md).
diff --git a/src/guide/render-function.md b/src/guide/render-function.md
index 1c9ac18760..902f229421 100644
--- a/src/guide/render-function.md
+++ b/src/guide/render-function.md
@@ -21,7 +21,9 @@ Anchored headings are used very frequently, we should create a component:
The component must generate a heading based on the `level` prop, and we quickly arrive at this:
```js
-const app = Vue.createApp({})
+const { createApp } = Vue
+
+const app = createApp({})
app.component('anchored-heading', {
template: `
@@ -58,11 +60,13 @@ This template doesn't feel great. It's not only verbose, but we're duplicating `
While templates work great for most components, it's clear that this isn't one of them. So let's try rewriting it with a `render()` function:
```js
-const app = Vue.createApp({})
+const { createApp, h } = Vue
+
+const app = createApp({})
app.component('anchored-heading', {
render() {
- return Vue.h(
+ return h(
'h' + this.level, // tag name
{}, // props/attributes
this.$slots.default() // array of children
@@ -109,7 +113,7 @@ Or in a render function:
```js
render() {
- return Vue.h('h1', {}, this.blogTitle)
+ return h('h1', {}, this.blogTitle)
}
```
@@ -120,7 +124,7 @@ And in both cases, Vue automatically keeps the page updated, even when `blogTitl
Vue keeps the page updated by building a **virtual DOM** to keep track of the changes it needs to make to the real DOM. Taking a closer look at this line:
```js
-return Vue.h('h1', {}, this.blogTitle)
+return h('h1', {}, this.blogTitle)
```
What is the `h()` function returning? It's not _exactly_ a real DOM element. It returns a plain object which contains information describing to Vue what kind of node it should render on the page, including descriptions of any child nodes. We call this node description a "virtual node", usually abbreviated to **VNode**. "Virtual DOM" is what we call the entire tree of VNodes, built by a tree of Vue components.
@@ -132,9 +136,9 @@ The `h()` function is a utility to create VNodes. It could perhaps more accurate
```js
// @returns {VNode}
h(
- // {String | Object | Function } tag
- // An HTML tag name, a component or an async component.
- // Using function returning null would render a comment.
+ // {String | Object | Function} tag
+ // An HTML tag name, a component, an async component, or a
+ // functional component.
//
// Required.
'div',
@@ -169,7 +173,9 @@ If there are no props then the children can usually be passed as the second argu
With this knowledge, we can now finish the component we started:
```js
-const app = Vue.createApp({})
+const { createApp, h } = Vue
+
+const app = createApp({})
/** Recursively get text from children nodes */
function getChildrenTextContent(children) {
@@ -192,8 +198,8 @@ app.component('anchored-heading', {
.replace(/\W+/g, '-') // replace non-word characters with dash
.replace(/(^-|-$)/g, '') // remove leading and trailing dashes
- return Vue.h('h' + this.level, [
- Vue.h(
+ return h('h' + this.level, [
+ h(
'a',
{
name: headingId,
@@ -220,8 +226,8 @@ All VNodes in the component tree must be unique. That means the following render
```js
render() {
- const myParagraphVNode = Vue.h('p', 'hi')
- return Vue.h('div', [
+ const myParagraphVNode = h('p', 'hi')
+ return h('div', [
// Yikes - duplicate VNodes!
myParagraphVNode, myParagraphVNode
])
@@ -232,9 +238,9 @@ If you really want to duplicate the same element/component many times, you can d
```js
render() {
- return Vue.h('div',
+ return h('div',
Array.from({ length: 20 }).map(() => {
- return Vue.h('p', 'hi')
+ return h('p', 'hi')
})
)
}
@@ -246,16 +252,20 @@ To create a VNode for a component, the first argument passed to `h` should be th
```js
render() {
- return Vue.h(ButtonCounter)
+ return h(ButtonCounter)
}
```
If we need to resolve a component by name then we can call `resolveComponent`:
```js
+const { h, resolveComponent } = Vue
+
+// ...
+
render() {
- const ButtonCounter = Vue.resolveComponent('ButtonCounter')
- return Vue.h(ButtonCounter)
+ const ButtonCounter = resolveComponent('ButtonCounter')
+ return h(ButtonCounter)
}
```
@@ -269,7 +279,7 @@ components: {
ButtonCounter
},
render() {
- return Vue.h(Vue.resolveComponent('ButtonCounter'))
+ return h(resolveComponent('ButtonCounter'))
}
```
@@ -277,7 +287,7 @@ Rather than registering a component by name and then looking it up we can use it
```js
render() {
- return Vue.h(ButtonCounter)
+ return h(ButtonCounter)
}
```
@@ -300,11 +310,11 @@ This could be rewritten with JavaScript's `if`/`else` and `map()` in a render fu
props: ['items'],
render() {
if (this.items.length) {
- return Vue.h('ul', this.items.map((item) => {
- return Vue.h('li', item.name)
+ return h('ul', this.items.map((item) => {
+ return h('li', item.name)
}))
} else {
- return Vue.h('p', 'No items found.')
+ return h('p', 'No items found.')
}
}
```
@@ -319,7 +329,7 @@ The `v-model` directive is expanded to `modelValue` and `onUpdate:modelValue` pr
props: ['modelValue'],
emits: ['update:modelValue'],
render() {
- return Vue.h(SomeComponent, {
+ return h(SomeComponent, {
modelValue: this.modelValue,
'onUpdate:modelValue': value => this.$emit('update:modelValue', value)
})
@@ -332,7 +342,7 @@ We have to provide a proper prop name for the event handler, e.g., to handle `cl
```js
render() {
- return Vue.h('div', {
+ return h('div', {
onClick: $event => console.log('clicked', $event.target)
})
}
@@ -340,13 +350,13 @@ render() {
#### Event Modifiers
-For the `.passive`, `.capture`, and `.once` event modifiers, they can be concatenated after the event name using camel case.
+For the `.passive`, `.capture`, and `.once` event modifiers, they can be concatenated after the event name using camelCase.
For example:
-```javascript
+```js
render() {
- return Vue.h('input', {
+ return h('input', {
onClickCapture: this.doThisInCapturingMode,
onKeyupOnce: this.doThisOnce,
onMouseoverOnceCapture: this.doThisOnceInCapturingMode
@@ -356,27 +366,27 @@ render() {
For all other event and key modifiers, no special API is necessary, because we can use event methods in the handler:
-| Modifier(s) | Equivalent in Handler |
-| ----------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
-| `.stop` | `event.stopPropagation()` |
-| `.prevent` | `event.preventDefault()` |
-| `.self` | `if (event.target !== event.currentTarget) return` |
-| Keys: `.enter`, `.13` | `if (event.keyCode !== 13) return` (change `13` to [another key code](http://keycode.info/) for other key modifiers) |
-| Modifiers Keys: `.ctrl`, `.alt`, `.shift`, `.meta` | `if (!event.ctrlKey) return` (change `ctrlKey` to `altKey`, `shiftKey`, or `metaKey`, respectively) |
+| Modifier(s) | Equivalent in Handler |
+| ---------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
+| `.stop` | `event.stopPropagation()` |
+| `.prevent` | `event.preventDefault()` |
+| `.self` | `if (event.target !== event.currentTarget) return` |
+| Keys: e.g. `.enter` | `if (event.key !== 'Enter') return` Change `'Enter'` to the appropriate [key](http://keycode.info/) |
+| Modifier Keys: `.ctrl`, `.alt`, `.shift`, `.meta` | `if (!event.ctrlKey) return` Likewise for `altKey`, `shiftKey`, and `metaKey` |
Here's an example with all of these modifiers used together:
```js
render() {
- return Vue.h('input', {
+ return h('input', {
onKeyUp: event => {
// Abort if the element emitting the event is not
// the element the event is bound to
if (event.target !== event.currentTarget) return
// Abort if the key that went up is not the enter
- // key (13) and the shift key was not held down
- // at the same time
- if (!event.shiftKey || event.keyCode !== 13) return
+ // key and the shift key was not held down at the
+ // same time
+ if (!event.shiftKey || event.key !== 'Enter') return
// Stop event propagation
event.stopPropagation()
// Prevent the default keyup handler for this element
@@ -394,7 +404,7 @@ We can access slot contents as arrays of VNodes from [`this.$slots`](../api/inst
```js
render() {
// `
`
- return Vue.h('div', this.$slots.default())
+ return h('div', this.$slots.default())
}
```
@@ -402,7 +412,7 @@ render() {
props: ['message'],
render() {
// `
`
- return Vue.h('div', this.$slots.default({
+ return h('div', this.$slots.default({
text: this.message
}))
}
@@ -413,14 +423,14 @@ For component VNodes, we need to pass the children to `h` as an object rather th
```js
render() {
// `{{ props.text }}
`
- return Vue.h('div', [
- Vue.h(
- Vue.resolveComponent('child'),
+ return h('div', [
+ h(
+ resolveComponent('child'),
null,
// pass `slots` as the children object
// in the form of { name: props => VNode | Array }
{
- default: (props) => Vue.h('span', props.text)
+ default: (props) => h('span', props.text)
}
)
])
@@ -433,10 +443,10 @@ The slots are passed as functions, allowing the child component to control the c
// ` {{ text }} `
render() {
// Calls to resolveComponent should be outside the slot function
- const Button = Vue.resolveComponent('MyButton')
- const Icon = Vue.resolveComponent('MyIcon')
+ const Button = resolveComponent('MyButton')
+ const Icon = resolveComponent('MyIcon')
- return Vue.h(
+ return h(
Button,
null,
{
@@ -445,7 +455,7 @@ render() {
// Reactive properties should be read inside the slot function
// so that they become dependencies of the child's rendering
return [
- Vue.h(Icon, { name: this.icon }),
+ h(Icon, { name: this.icon }),
this.text
]
}
@@ -458,7 +468,7 @@ If a component receives slots from its parent, they can be passed on directly to
```js
render() {
- return Vue.h(Panel, null, this.$slots)
+ return h(Panel, null, this.$slots)
}
```
@@ -466,7 +476,7 @@ They can also be passed individually or wrapped as appropriate:
```js
render() {
- return Vue.h(
+ return h(
Panel,
null,
{
@@ -478,7 +488,7 @@ render() {
default: (props) => {
const children = this.$slots.default ? this.$slots.default(props) : []
- return children.concat(Vue.h('div', 'Extra child'))
+ return children.concat(h('div', 'Extra child'))
}
}
)
@@ -490,10 +500,14 @@ render() {
Behind the scenes, templates use `resolveDynamicComponent` to implement the `is` attribute. We can use the same function if we need all the flexibility provided by `is` in our `render` function:
```js
+const { h, resolveDynamicComponent } = Vue
+
+// ...
+
// ` `
render() {
- const Component = Vue.resolveDynamicComponent(this.name)
- return Vue.h(Component)
+ const Component = resolveDynamicComponent(this.name)
+ return h(Component)
}
```
@@ -508,7 +522,7 @@ If the VNode is always an HTML element then we can pass its name directly to `h`
```js
// ` `
render() {
- return Vue.h(this.bold ? 'strong' : 'em')
+ return h(this.bold ? 'strong' : 'em')
}
```
@@ -516,18 +530,82 @@ Similarly, if the value passed to `is` is a component options object then there'
Much like a `` tag, a `` tag is only required in templates as a syntactical placeholder and should be discarded when migrating to a `render` function.
+### Custom Directives
+
+Custom directives can be applied to a VNode using [`withDirectives`](/api/global-api.html#withdirectives):
+
+```js
+const { h, resolveDirective, withDirectives } = Vue
+
+// ...
+
+//
+render () {
+ const pin = resolveDirective('pin')
+
+ return withDirectives(h('div'), [
+ [pin, 200, 'top', { animate: true }]
+ ])
+}
+```
+
+[`resolveDirective`](/api/global-api.html#resolvedirective) is the same function that templates use internally to resolve directives by name. That is only necessary if you don't already have direct access to the directive's definition object.
+
+### Built-in Components
+
+[Built-in components](/api/built-in-components.html) such as ``, ``, ``, and `` are not registered globally by default. This allows bundlers to perform tree-shaking, so that the components are only included in the build if they are used. However, that also means we can't access them using `resolveComponent` or `resolveDynamicComponent`.
+
+Templates have special handling for those components, automatically importing them when they are used. When we're writing our own `render` functions, we need to import them ourselves:
+
+```js
+const { h, KeepAlive, Teleport, Transition, TransitionGroup } = Vue
+
+// ...
+
+render () {
+ return h(Transition, { mode: 'out-in' }, /* ... */)
+}
+```
+
+## Return Values for Render Functions
+
+In all of the examples we've seen so far, the `render` function has returned a single root VNode. However, there are alternatives.
+
+Returning a string will create a text VNode, without any wrapping element:
+
+```js
+render() {
+ return 'Hello world!'
+}
+```
+
+We can also return an array of children, without wrapping them in a root node. This creates a fragment:
+
+```js
+// Equivalent to a template of `Hello world!`
+render() {
+ return [
+ 'Hello',
+ h('br'),
+ 'world!'
+ ]
+}
+```
+
+If a component needs to render nothing, perhaps because data is still loading, it can just return `null`. This will be rendered as a comment node in the DOM.
+
## JSX
If we're writing a lot of `render` functions, it might feel painful to write something like this:
```js
-Vue.h(
- Vue.resolveComponent('anchored-heading'),
+h(
+ resolveComponent('anchored-heading'),
{
level: 1
},
{
- default: () => [Vue.h('span', 'Hello'), ' world!']
+ default: () => [h('span', 'Hello'), ' world!']
}
)
```
@@ -558,6 +636,31 @@ app.mount('#demo')
For more on how JSX maps to JavaScript, see the [usage docs](https://github.com/vuejs/jsx-next#installation).
+## Functional Components
+
+Functional components are an alternative form of component that don't have any state of their own. They are rendered without creating a component instance, bypassing the usual component lifecycle.
+
+To create a functional component we use a plain function, rather than an options object. The function is effectively the `render` function for the component. As there is no `this` reference for a functional component, Vue will pass in the `props` as the first argument:
+
+```js
+const FunctionalComponent = (props, context) => {
+ // ...
+}
+```
+
+The second argument, `context`, contains three properties: `attrs`, `emit`, and `slots`. These are equivalent to the instance properties [`$attrs`](/api/instance-properties.html#attrs), [`$emit`](/api/instance-methods.html#emit), and [`$slots`](/api/instance-properties.html#slots) respectively.
+
+Most of the usual configuration options for components are not available for functional components. However, it is possible to define [`props`](/api/options-data.html#props) and [`emits`](/api/options-data.html#emits) by adding them as properties:
+
+```js
+FunctionalComponent.props = ['value']
+FunctionalComponent.emits = ['click']
+```
+
+If the `props` option is not specified, then the `props` object passed to the function will contain all attributes, the same as `attrs`. The prop names will not be normalized to camelCase unless the `props` option is specified.
+
+Functional components can be registered and consumed just like normal components. If you pass a function as the first argument to `h`, it will be treated as a functional component.
+
## Template Compilation
You may be interested to know that Vue's templates actually compile to render functions. This is an implementation detail you usually don't need to know about, but if you'd like to see how specific template features are compiled, you may find it interesting. Below is a little demo using `Vue.compile` to live-compile a template string:
diff --git a/src/guide/routing.md b/src/guide/routing.md
index 288f5bef4d..0bca2d5ba4 100644
--- a/src/guide/routing.md
+++ b/src/guide/routing.md
@@ -2,13 +2,15 @@
## Official Router
-For most Single Page Applications, it's recommended to use the officially-supported [vue-router library](https://github.com/vuejs/vue-router). For more details, see vue-router's [documentation](https://next.router.vuejs.org/).
+For most Single Page Applications, it's recommended to use the officially-supported [vue-router library](https://github.com/vuejs/vue-router-next). For more details, see vue-router's [documentation](https://next.router.vuejs.org/).
## Simple Routing from Scratch
If you only need very simple routing and do not wish to involve a full-featured router library, you can do so by dynamically rendering a page-level component like this:
```js
+const { createApp, h } = Vue
+
const NotFoundComponent = { template: 'Page not found
' }
const HomeComponent = { template: 'Home page
' }
const AboutComponent = { template: 'About page
' }
@@ -30,11 +32,11 @@ const SimpleRouter = {
},
render() {
- return Vue.h(this.CurrentComponent)
+ return h(this.CurrentComponent)
}
}
-Vue.createApp(SimpleRouter).mount('#app')
+createApp(SimpleRouter).mount('#app')
```
Combined with the [History API](https://developer.mozilla.org/en-US/docs/Web/API/History_API/Working_with_the_History_API), you can build a very basic but fully-functional client-side router. To see that in practice, check out [this example app](https://github.com/phanan/vue-3.0-simple-routing-example).
diff --git a/src/guide/security.md b/src/guide/security.md
new file mode 100644
index 0000000000..5f7813e00a
--- /dev/null
+++ b/src/guide/security.md
@@ -0,0 +1,187 @@
+# Security
+
+## Reporting Vulnerabilities
+
+When a vulnerability is reported, it immediately becomes our top concern, with a full-time contributor dropping everything to work on it. To report a vulnerability, please email [security@vuejs.org](mailto:security@vuejs.org).
+
+While the discovery of new vulnerabilities is rare, we also recommend always using the latest versions of Vue and its official companion libraries to ensure your application remains as secure as possible.
+
+## Rule No.1: Never Use Non-trusted Templates
+
+The most fundamental security rule when using Vue is **never use non-trusted content as your component template**. Doing so is equivalent to allowing arbitrary JavaScript execution in your application - and worse, could lead to server breaches if the code is executed during server-side rendering. An example of such usage:
+
+```js
+Vue.createApp({
+ template: `` + userProvidedString + `
` // NEVER DO THIS
+}).mount('#app')
+```
+
+Vue templates are compiled into JavaScript, and expressions inside templates will be executed as part of the rendering process. Although the expressions are evaluated against a specific rendering context, due to the complexity of potential global execution environments, it is impractical for a framework like Vue to completely shield you from potential malicious code execution without incurring unrealistic performance overhead. The most straightforward way to avoid this category of problems altogether is to make sure the contents of your Vue templates are always trusted and entirely controlled by you.
+
+## What Vue Does to Protect You
+
+### HTML content
+
+Whether using templates or render functions, content is automatically escaped. That means in this template:
+
+```html
+{{ userProvidedString }}
+```
+
+if `userProvidedString` contained:
+
+```js
+''
+```
+
+then it would be escaped to the following HTML:
+
+```html
+<script>alert("hi")</script>
+```
+
+thus preventing the script injection. This escaping is done using native browser APIs, like `textContent`, so a vulnerability can only exist if the browser itself is vulnerable.
+
+### Attribute bindings
+
+Similarly, dynamic attribute bindings are also automatically escaped. That means in this template:
+
+```html
+
+ hello
+
+```
+
+if `userProvidedString` contained:
+
+```js
+'" onclick="alert(\'hi\')'
+```
+
+then it would be escaped to the following HTML:
+
+```html
+" onclick="alert('hi')
+```
+
+thus preventing the close of the `title` attribute to inject new, arbitrary HTML. This escaping is done using native browser APIs, like `setAttribute`, so a vulnerability can only exist if the browser itself is vulnerable.
+
+## Potential Dangers
+
+In any web application, allowing unsanitized, user-provided content to be executed as HTML, CSS, or JavaScript is potentially dangerous, so should be avoided wherever possible. There are times when some risk may be acceptable though.
+
+For example, services like CodePen and JSFiddle allow user-provided content to be executed, but it's in a context where this is expected and sandboxed to some extent inside iframes. In the cases when an important feature inherently requires some level of vulnerability, it's up to your team to weigh the importance of the feature against the worst-case scenarios the vulnerability enables.
+
+### Injecting HTML
+
+As you learned earlier, Vue automatically escapes HTML content, preventing you from accidentally injecting executable HTML into your application. However, in cases where you know the HTML is safe, you can explicitly render HTML content:
+
+- Using a template:
+
+ ```html
+
+ ```
+
+- Using a render function:
+
+ ```js
+ h('div', {
+ innerHTML: this.userProvidedHtml
+ })
+ ```
+
+- Using a render function with JSX:
+
+ ```jsx
+
+ ```
+
+:::tip
+Note that user-provided HTML can never be considered 100% safe unless it's in a sandboxed iframe or in a part of the app where only the user who wrote that HTML can ever be exposed to it. Additionally, allowing users to write their own Vue templates brings similar dangers.
+:::
+
+### Injecting URLs
+
+In a URL like this:
+
+```html
+
+ click me
+
+```
+
+There's a potential security issue if the URL has not been "sanitized" to prevent JavaScript execution using `javascript:`. There are libraries such as [sanitize-url](https://www.npmjs.com/package/@braintree/sanitize-url) to help with this, but note:
+
+:::tip
+If you're ever doing URL sanitization on the frontend, you already have a security issue. User-provided URLs should always be sanitized by your backend before even being saved to a database. Then the problem is avoided for _every_ client connecting to your API, including native mobile apps. Also note that even with sanitized URLs, Vue cannot help you guarantee that they lead to safe destinations.
+:::
+
+### Injecting Styles
+
+Looking at this example:
+
+```html
+
+ click me
+
+```
+
+let's assume that `sanitizedUrl` has been sanitized, so that it's definitely a real URL and not JavaScript. With the `userProvidedStyles`, malicious users could still provide CSS to "click jack", e.g. styling the link into a transparent box over the "Log in" button. Then if `https://user-controlled-website.com/` is built to resemble the login page of your application, they might have just captured a user's real login information.
+
+You may be able to imagine how allowing user-provided content for a `
+```
+
+To keep your users fully safe from click jacking, we recommend only allowing full control over CSS inside a sandboxed iframe. Alternatively, when providing user control through a style binding, we recommend using its [object syntax](class-and-style.html#object-syntax-2) and only allowing users to provide values for specific properties it's safe for them to control, like this:
+
+```html
+
+ click me
+
+```
+
+### Injecting JavaScript
+
+We strongly discourage ever rendering a `
-```html
-
- This will be pre-compiled
+ {{ greeting }}
-
-
-```
-
-## Getting Started
-### Example Sandbox
-
-If you want to dive right in and start playing with single-file components, check out [this simple todo app](https://codesandbox.io/s/vue-todo-list-app-with-single-file-component-vzkl3?file=/src/App.vue) on CodeSandbox.
+
+```
-### For Users New to Module Build Systems in JavaScript
+As we can see, Vue SFC is a natural extension of the classic trio of HTML, CSS and JavaScript. Each `*.vue` file consists of three types of top-level language blocks: ``, `
+```
+
+### `entry-client.js`
+
+The client entry creates the application using the `App.vue` component and mounts it to the DOM:
+
+```js
+import { createSSRApp } from 'vue'
+import App from './App.vue'
+
+// client-specific bootstrapping logic...
+
+const app = createSSRApp(App)
+
+// this assumes App.vue template root element has `id="app"`
+app.mount('#app')
+```
+
+### `entry-server.js`
+
+The server entry uses a default export which is a function that can be called repeatedly for each render. At this moment, it doesn't do much other than returning the app instance - but later we will perform server-side route matching and data pre-fetching logic here.
+
+```js
+import { createSSRApp } from 'vue'
+import App from './App.vue'
+
+export default function () {
+ const app = createSSRApp(App)
+
+ return {
+ app
+ }
+}
+```
diff --git a/src/guide/ssr/universal.md b/src/guide/ssr/universal.md
new file mode 100644
index 0000000000..3aa552700f
--- /dev/null
+++ b/src/guide/ssr/universal.md
@@ -0,0 +1,29 @@
+# Writing Universal Code
+
+Before going further, let's take a moment to discuss the constraints when writing "universal" code - that is, code that runs on both the server and the client. Due to use case and platform API differences, the behavior of our code will not be exactly the same when running in different environments. Here we will go over the key things you need to be aware of.
+
+## Data Reactivity on the Server
+
+In a client-only app, every user will be using a fresh instance of the app in their browser. For server-side rendering we want the same: each request should have a fresh, isolated app instance so that there is no cross-request state pollution.
+
+Because the actual rendering process needs to be deterministic, we will also be "pre-fetching" data on the server - this means our application state will be already resolved when we start rendering. This means data reactivity is unnecessary on the server, so it is disabled by default. Disabling data reactivity also avoids the performance cost of converting data into reactive objects.
+
+## Component Lifecycle Hooks
+
+Since there are no dynamic updates, the only [lifecycle hooks](/guide/instance.html#lifecycle-hooks) that will be called during SSR are `beforeCreate` and `created`. This means any code inside other lifecycle hooks such as `beforeMount` or `mounted` will only be executed on the client.
+
+Another thing to note is that you should avoid code that produces global side effects in `beforeCreate` and `created`, for example setting up timers with `setInterval`. In client-side only code we may setup a timer and then tear it down in `beforeUnmount` or `unmounted`. However, because the destroy hooks will not be called during SSR, the timers will stay around forever. To avoid this, move your side-effect code into `beforeMount` or `mounted` instead.
+
+## Access to Platform-Specific APIs
+
+Universal code cannot assume access to platform-specific APIs, so if your code directly uses browser-only globals like `window` or `document`, they will throw errors when executed in Node.js, and vice-versa.
+
+For tasks shared between server and client but using different platform APIs, it's recommended to wrap the platform-specific implementations inside a universal API, or use libraries that do this for you. For example, [axios](https://github.com/axios/axios) is an HTTP client that exposes the same API for both server and client.
+
+For browser-only APIs, the common approach is to lazily access them inside client-only lifecycle hooks.
+
+Note that if a 3rd party library is not written with universal usage in mind, it could be tricky to integrate it into an server-rendered app. You _might_ be able to get it working by mocking some of the globals, but it would be hacky and may interfere with the environment detection code of other libraries.
+
+## Custom Directives
+
+Most [custom directives](/guide/custom-directive.html#custom-directives) directly manipulate the DOM, which will cause errors during SSR. We recommend to prefer using components as the abstraction mechanism instead of directives.
diff --git a/src/guide/state-management.md b/src/guide/state-management.md
index 0525003cbc..a67491ec6a 100644
--- a/src/guide/state-management.md
+++ b/src/guide/state-management.md
@@ -2,7 +2,7 @@
## Official Flux-Like Implementation
-Large applications can often grow in complexity, due to multiple pieces of state scattered across many components and the interactions between them. To solve this problem, Vue offers [Vuex](https://next.vuex.vuejs.org/), our own Elm-inspired state management library. It even integrates into [vue-devtools](https://github.com/vuejs/vue-devtools), providing zero-setup access to [time travel debugging](https://raw.githubusercontent.com/vuejs/vue-devtools/master/media/demo.gif).
+Large applications can often grow in complexity, due to multiple pieces of state scattered across many components and the interactions between them. To solve this problem, Vue offers [Vuex](https://next.vuex.vuejs.org/), our own Elm-inspired state management library. It even integrates into [vue-devtools](https://github.com/vuejs/vue-devtools), providing zero-setup access to [time travel debugging](https://raw.githubusercontent.com/vuejs/vue-devtools/legacy/media/demo.gif).
### Information for React Developers
@@ -13,17 +13,19 @@ If you're coming from React, you may be wondering how vuex compares to [redux](h
It is often overlooked that the source of truth in Vue applications is the reactive `data` object - a component instance only proxies access to it. Therefore, if you have a piece of state that should be shared by multiple instances, you can use a [reactive](/guide/reactivity-fundamentals.html#declaring-reactive-state) method to make an object reactive:
```js
-const sourceOfTruth = Vue.reactive({
+const { createApp, reactive } = Vue
+
+const sourceOfTruth = reactive({
message: 'Hello'
})
-const appA = Vue.createApp({
+const appA = createApp({
data() {
return sourceOfTruth
}
}).mount('#app-a')
-const appB = Vue.createApp({
+const appB = createApp({
data() {
return sourceOfTruth
}
@@ -39,7 +41,7 @@ const appB = Vue.createApp({
Now whenever `sourceOfTruth` is mutated, both `appA` and `appB` will update their views automatically. We have a single source of truth now, but debugging would be a nightmare. Any piece of data could be changed by any part of our app at any time, without leaving a trace.
```js
-const appB = Vue.createApp({
+const appB = createApp({
data() {
return sourceOfTruth
},
@@ -55,7 +57,7 @@ To help solve this problem, we can adopt a **store pattern**:
const store = {
debug: true,
- state: Vue.reactive({
+ state: reactive({
message: 'Hello!'
}),
@@ -88,7 +90,7 @@ In addition, each instance/component can still own and manage its own private st
```
```js
-const appA = Vue.createApp({
+const appA = createApp({
data() {
return {
privateState: {},
@@ -100,7 +102,7 @@ const appA = Vue.createApp({
}
}).mount('#app-a')
-const appB = Vue.createApp({
+const appB = createApp({
data() {
return {
privateState: {},
diff --git a/src/guide/teleport.md b/src/guide/teleport.md
index 5e65865271..c47ff0c571 100644
--- a/src/guide/teleport.md
+++ b/src/guide/teleport.md
@@ -4,7 +4,7 @@
Vue encourages us to build our UIs by encapsulating UI and related behavior into components. We can nest them inside one another to build a tree that makes up an application UI.
-However, sometimes a part of a component's template belongs to this component logically, while from a technical point of view, it would be preferable to move this part of the template somewhere else in the DOM, outside of the Vue app.
+However, sometimes a part of a component's template belongs to this component logically, while from a technical point of view, it would be preferable to move this part of the template somewhere else in the DOM, outside of the Vue app.
A common scenario for this is creating a component that includes a full-screen modal. In most cases, you'd want the modal's logic to live within the component, but the positioning of the modal quickly becomes difficult to solve through CSS, or requires a change in component composition.
@@ -21,7 +21,7 @@ Consider the following HTML structure.