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 @@ + + + + + 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. - + - - 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 ``, and `
      - -
      -``` + 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 + +### `