diff --git a/.vitepress/config.js b/.vitepress/config.js index 8e337bc..cbb921e 100644 --- a/.vitepress/config.js +++ b/.vitepress/config.js @@ -6,13 +6,14 @@ import locales from "./locales" export default { srcDir: 'src', locales: locales.vitepressConfig, - + themeConfig: { localeLinks: { items: [ {text: 'English', link: '/'}, - {text: '中文简体', link: '/zh/'} + {text: '中文简体', link: '/zh/'}, + {text: '日本語(翻訳中)', link: '/ja/'}, ] }, locales: locales.themeConfig diff --git a/.vitepress/locales/index.js b/.vitepress/locales/index.js index ff3ef3c..bdd2f4b 100644 --- a/.vitepress/locales/index.js +++ b/.vitepress/locales/index.js @@ -1,13 +1,16 @@ import en from './en' import zh from './zh' +import ja from './ja' export default { vitepressConfig: { '/': en.vitepressConfig, - '/zh/': zh.vitepressConfig + '/zh/': zh.vitepressConfig, + '/ja/': ja.vitepressConfig, }, themeConfig: { '/': en.themeConfig, - '/zh/': zh.themeConfig + '/zh/': zh.themeConfig, + '/ja/': ja.themeConfig, } -} \ No newline at end of file +} diff --git a/.vitepress/locales/ja.js b/.vitepress/locales/ja.js new file mode 100644 index 0000000..0372d60 --- /dev/null +++ b/.vitepress/locales/ja.js @@ -0,0 +1,164 @@ +export default { + vitepressConfig: { + title: 'Vue 3 移行ガイド', + description: 'Vue 2 から Vue 3 への移行に関するガイド', + lang: 'ja-JP', + }, + themeConfig: { + docFooter: { + prev: '前のページ', + next: '次のページ', + }, + outlineTitle: 'ページの内容', + nav: [ + { text: 'Vue 3 ドキュメント', link: 'https://ja.vuejs.org' }, + ], + + sidebar: [ + { + text: 'ガイド', + items: [ + { text: '概要', link: '/ja/' }, + { text: '新しい推奨事項', link: '/ja/recommendations' }, + { text: '移行ビルド', link: '/ja/migration-build' }, + { + text: '破壊的変更', + link: '/ja/breaking-changes/' + } + ] + }, + { + text: 'Global API', + items: [ + { + text: 'Global API Application Instance', + link: '/ja/breaking-changes/global-api' + }, + { + text: 'Global API Treeshaking', + link: '/ja/breaking-changes/global-api-treeshaking' + } + ] + }, + { + text: 'Template Directives', + items: [ + { text: 'v-model', link: '/ja/breaking-changes/v-model' }, + { + text: 'key Usage Change', + link: '/ja/breaking-changes/key-attribute' + }, + { + text: 'v-if vs. v-for Precedence', + link: '/ja/breaking-changes/v-if-v-for' + }, + { text: 'v-bind Merge Behavior', link: '/ja/breaking-changes/v-bind' }, + { + text: 'v-on.native modifier removed', + link: '/ja/breaking-changes/v-on-native-modifier-removed' + } + ] + }, + { + text: 'Components', + items: [ + { + text: 'Functional Components', + link: '/ja/breaking-changes/functional-components' + }, + { + text: 'Async Components', + link: '/ja/breaking-changes/async-components' + }, + { text: 'emits Option', link: '/ja/breaking-changes/emits-option' } + ] + }, + { + text: 'Render Function', + items: [ + { + text: 'Render Function API', + link: '/ja/breaking-changes/render-function-api' + }, + { + text: 'Slots Unification', + link: '/ja/breaking-changes/slots-unification' + }, + { + text: '$listeners merged into $attrs', + link: '/ja/breaking-changes/listeners-removed' + }, + { + text: '$attrs includes class & style', + link: '/ja/breaking-changes/attrs-includes-class-style' + } + ] + }, + { + text: 'Custom Elements', + items: [ + { + text: 'Custom Elements Interop Changes', + link: '/ja/breaking-changes/custom-elements-interop' + } + ] + }, + { + text: 'Removed APIs', + items: [ + { + text: 'v-on keyCode Modifiers', + link: '/ja/breaking-changes/keycode-modifiers' + }, + { text: 'Events API', link: '/ja/breaking-changes/events-api' }, + { text: 'Filters', link: '/ja/breaking-changes/filters' }, + { + text: 'inline-template', + link: '/ja/breaking-changes/inline-template-attribute' + }, + { text: '$children', link: '/ja/breaking-changes/children' }, + { text: 'propsData option', link: '/ja/breaking-changes/props-data' } + ] + }, + { + text: 'Other Minor Changes', + items: [ + { + text: 'Attribute Coercion Behavior', + link: '/ja/breaking-changes/attribute-coercion' + }, + { + text: 'Custom Directives', + link: '/ja/breaking-changes/custom-directives' + }, + { text: 'Data Option', link: '/ja/breaking-changes/data-option' }, + { + text: 'Mount API changes', + link: '/ja/breaking-changes/mount-changes' + }, + { + text: 'Props Default Function this Access', + link: '/ja/breaking-changes/props-default-this' + }, + { + text: 'Transition Class Change', + link: '/ja/breaking-changes/transition' + }, + { + text: 'Transition as Root', + link: '/ja/breaking-changes/transition-as-root' + }, + { + text: 'Transition Group Root Element', + link: '/ja/breaking-changes/transition-group' + }, + { + text: 'VNode lifecycle events', + link: '/ja/breaking-changes/vnode-lifecycle-events' + }, + { text: 'Watch on Arrays', link: '/ja/breaking-changes/watch' } + ] + } + ] + } +} diff --git a/src/ja/breaking-changes/async-components.md b/src/ja/breaking-changes/async-components.md new file mode 100644 index 0000000..c6c9981 --- /dev/null +++ b/src/ja/breaking-changes/async-components.md @@ -0,0 +1,98 @@ +--- +badges: + - new +--- + +# Async Components + +## Overview + +Here is a high level overview of what has changed: + +- New `defineAsyncComponent` helper method that explicitly defines async components +- `component` option renamed to `loader` +- Loader function does not inherently receive `resolve` and `reject` arguments and must return a Promise + +For a more in-depth explanation, read on! + +## Introduction + +Previously, async components were created by simply defining a component as a function that returned a promise, such as: + +```js +const asyncModal = () => import('./Modal.vue') +``` + +Or, for the more advanced component syntax with options: + +```js +const asyncModal = { + component: () => import('./Modal.vue'), + delay: 200, + timeout: 3000, + error: ErrorComponent, + loading: LoadingComponent +} +``` + +## 3.x Syntax + +Now, in Vue 3, since functional components are defined as pure functions, async components definitions need to be explicitly defined by wrapping it in a new `defineAsyncComponent` helper: + +```js +import { defineAsyncComponent } from 'vue' +import ErrorComponent from './components/ErrorComponent.vue' +import LoadingComponent from './components/LoadingComponent.vue' + +// Async component without options +const asyncModal = defineAsyncComponent(() => import('./Modal.vue')) + +// Async component with options +const asyncModalWithOptions = defineAsyncComponent({ + loader: () => import('./Modal.vue'), + delay: 200, + timeout: 3000, + errorComponent: ErrorComponent, + loadingComponent: LoadingComponent +}) +``` + +::: tip NOTE +Vue Router supports a similar mechanism for asynchronously loading route components, known as *lazy loading*. Despite the similarities, this feature is distinct from Vue's support for async components. You should **not** use `defineAsyncComponent` when configuring route components with Vue Router. You can read more about this in the [Lazy Loading Routes](https://router.vuejs.org/guide/advanced/lazy-loading.html) section of the Vue Router documentation. +::: + +Another change that has been made from 2.x is that the `component` option is now renamed to `loader` in order to accurately communicate that a component definition cannot be provided directly. + +```js{4} +import { defineAsyncComponent } from 'vue' + +const asyncModalWithOptions = defineAsyncComponent({ + loader: () => import('./Modal.vue'), + delay: 200, + timeout: 3000, + errorComponent: ErrorComponent, + loadingComponent: LoadingComponent +}) +``` + +In addition, unlike 2.x, the loader function no longer receives the `resolve` and `reject` arguments and must always return a Promise. + +```js +// 2.x version +const oldAsyncComponent = (resolve, reject) => { + /* ... */ +} + +// 3.x version +const asyncComponent = defineAsyncComponent( + () => + new Promise((resolve, reject) => { + /* ... */ + }) +) +``` + +For more information on the usage of async components, see: + +- [Guide: Async Components](https://ja.vuejs.org/guide/components/async.html) +- [Migration build flag: `COMPONENT_ASYNC`](../migration-build.html#compat-configuration) diff --git a/src/ja/breaking-changes/attribute-coercion.md b/src/ja/breaking-changes/attribute-coercion.md new file mode 100644 index 0000000..dcb9a7c --- /dev/null +++ b/src/ja/breaking-changes/attribute-coercion.md @@ -0,0 +1,144 @@ +--- +badges: + - breaking +--- + +# Attribute Coercion Behavior + +::: info Info +This is a low-level internal API change and does not affect most developers. +::: + +## Overview + +Here is a high level summary of the changes: + +- Drop the internal concept of enumerated attributes and treat those attributes the same as normal non-boolean attributes +- **BREAKING**: No longer removes attribute if the value is boolean `false`. Instead, it's set as attr="false". To remove the attribute, use `null` or `undefined`. + +For more information, read on! + +## 2.x Syntax + +In 2.x, we had the following strategies for coercing `v-bind` values: + +- For some attribute/element pairs, Vue is always using the corresponding IDL attribute (property): [like `value` of ``, ``, ``, etc](https://github.com/vuejs/vue/blob/bad3c326a3f8b8e0d3bcf07917dc0adf97c32351/src/platforms/web/util/attrs.js#L11-L18). + +- For "[boolean attributes](https://github.com/vuejs/vue/blob/bad3c326a3f8b8e0d3bcf07917dc0adf97c32351/src/platforms/web/util/attrs.js#L33-L40)" and [xlinks](https://github.com/vuejs/vue/blob/bad3c326a3f8b8e0d3bcf07917dc0adf97c32351/src/platforms/web/util/attrs.js#L44-L46), Vue removes them if they are "falsy" ([`undefined`, `null` or `false`](https://github.com/vuejs/vue/blob/bad3c326a3f8b8e0d3bcf07917dc0adf97c32351/src/platforms/web/util/attrs.js#L52-L54)) and adds them otherwise (see [here](https://github.com/vuejs/vue/blob/bad3c326a3f8b8e0d3bcf07917dc0adf97c32351/src/platforms/web/runtime/modules/attrs.js#L66-L77) and [here](https://github.com/vuejs/vue/blob/bad3c326a3f8b8e0d3bcf07917dc0adf97c32351/src/platforms/web/runtime/modules/attrs.js#L81-L85)). + +- For "[enumerated attributes](https://github.com/vuejs/vue/blob/bad3c326a3f8b8e0d3bcf07917dc0adf97c32351/src/platforms/web/util/attrs.js#L20)" (currently `contenteditable`, `draggable` and `spellcheck`), Vue tries to [coerce](https://github.com/vuejs/vue/blob/bad3c326a3f8b8e0d3bcf07917dc0adf97c32351/src/platforms/web/util/attrs.js#L24-L31) them to string (with special treatment for `contenteditable` for now, to fix [vuejs/vue#9397](https://github.com/vuejs/vue/issues/9397)). + +- For other attributes, we remove "falsy" values (`undefined`, `null`, or `false`) and set other values as-is (see [here](https://github.com/vuejs/vue/blob/bad3c326a3f8b8e0d3bcf07917dc0adf97c32351/src/platforms/web/runtime/modules/attrs.js#L92-L113)). + +The following table describes how Vue coerce "enumerated attributes" differently with normal non-boolean attributes: + +| Binding expression | `foo` normal | `draggable` enumerated | +| ------------------- | ----------------------- | --------------------------------- | +| `:attr="null"` | - | `draggable="false"` | +| `:attr="undefined"` | - | - | +| `:attr="true"` | `foo="true"` | `draggable="true"` | +| `:attr="false"` | - | `draggable="false"` | +| `:attr="0"` | `foo="0"` | `draggable="true"` | +| `attr=""` | `foo=""` | `draggable="true"` | +| `attr="foo"` | `foo="foo"` | `draggable="true"` | +| `attr` | `foo=""` | `draggable="true"` | + +We can see from the table above, current implementation coerces `true` to `'true'` but removes the attribute if it's `false`. This also led to inconsistency and required users to manually coerce boolean values to string in very common use cases like `aria-*` attributes like `aria-selected`, `aria-hidden`, etc. + +## 3.x Syntax + +We intend to drop this internal concept of "enumerated attributes" and treat them as normal non-boolean HTML attributes. + +- This solves the inconsistency between normal non-boolean attributes and “enumerated attributes” +- It also makes it possible to use values other than `'true'` and `'false'`, or even keywords yet to come, for attributes like `contenteditable` + +For non-boolean attributes, Vue will stop removing them if they are `false` and coerce them to `'false'` instead. + +- This solves the inconsistency between `true` and `false` and makes outputting `aria-*` attributes easier + +The following table describes the new behavior: + +| Binding expression | `foo` normal | `draggable` enumerated | +| ------------------- | -------------------------- | --------------------------------- | +| `:attr="null"` | - | - * | +| `:attr="undefined"` | - | - | +| `:attr="true"` | `foo="true"` | `draggable="true"` | +| `:attr="false"` | `foo="false"` * | `draggable="false"` | +| `:attr="0"` | `foo="0"` | `draggable="0"` * | +| `attr=""` | `foo=""` | `draggable=""` * | +| `attr="foo"` | `foo="foo"` | `draggable="foo"` * | +| `attr` | `foo=""` | `draggable=""` * | + +*: changed + +Coercion for boolean attributes is left untouched. + +## Migration Strategy + +### Enumerated attributes + +The absence of an enumerated attribute and `attr="false"` may produce different IDL attribute values (which will reflect the actual state), described as follows: + +| Absent enumerated attr | IDL attr & value | +| ---------------------- | ------------------------------------ | +| `contenteditable` | `contentEditable` → `'inherit'` | +| `draggable` | `draggable` → `false` | +| `spellcheck` | `spellcheck` → `true` | + +Since we no longer coerce `null` to `'false'` for “enumerated properties” in 3.x, in the case of `contenteditable` and `spellcheck`, developers will need to change those `v-bind` expressions that used to resolve to `null` to resolve to `false` or `'false'` in order to maintain the same behavior as 2.x. + +In 2.x, invalid values were coerced to `'true'` for enumerated attributes. This was usually unintended and unlikely to be relied upon on a large scale. In 3.x `true` or `'true'` should be explicitly specified. + +### Coercing `false` to `'false'` instead of removing the attribute + +In 3.x, `null` or `undefined` should be used to explicitly remove an attribute. + +### Comparison between 2.x & 3.x behavior + + + + + Attribute + v-bind value 2.x + v-bind value 3.x + HTML output + + + + + 2.x “Enumerated attrs”i.e. contenteditable, draggable and spellcheck. + undefined + undefined, null + removed + + + + true, 'true', '', 1, + 'foo' + + true, 'true' + "true" + + + null, false, 'false' + false, 'false' + "false" + + + Other non-boolean attrseg. aria-checked, tabindex, alt, etc. + undefined, null, false + undefined, null + removed + + + 'false' + false, 'false' + "false" + + + + +[Migration build flags:](../migration-build.html#compat-configuration) + +- `ATTR_FALSE_VALUE` +- `ATTR_ENUMERATED_COERCION` diff --git a/src/ja/breaking-changes/attrs-includes-class-style.md b/src/ja/breaking-changes/attrs-includes-class-style.md new file mode 100644 index 0000000..720f4e0 --- /dev/null +++ b/src/ja/breaking-changes/attrs-includes-class-style.md @@ -0,0 +1,71 @@ +--- +title: $attrs includes class & style +badges: + - breaking +--- + +# `$attrs` includes `class` & `style` + +## Overview + +`$attrs` now contains _all_ attributes passed to a component, including `class` and `style`. + +## 2.x Behavior + +`class` and `style` attributes get some special handling in the Vue 2 virtual DOM implementation. For that reason, they are _not_ included in `$attrs`, while all other attributes are. + +A side effect of this manifests when using `inheritAttrs: false`: + +- Attributes in `$attrs` are no longer automatically added to the root element, leaving it to the developer to decide where to add them. +- But `class` and `style`, not being part of `$attrs`, will still be applied to the component's root element: + +```vue + + + + + + +``` + +when used like this: + +```html + +``` + +...will generate this HTML: + +```html + + + +``` + +## 3.x Behavior + +`$attrs` contains _all_ attributes, which makes it easier to apply all of them to a different element. The example from above now generates the following HTML: + +```html + + + +``` + +## Migration Strategy + +In components that use `inheritAttrs: false`, make sure that styling still works as intended. If you previously relied on the special behavior of `class` and `style`, some visuals might be broken as these attributes might now be applied to another element. + +[Migration build flag: `INSTANCE_ATTRS_CLASS_STYLE`](../migration-build.html#compat-configuration) + +## See also + +- [Relevant RFC](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0031-attr-fallthrough.md) +- [Migration guide - `$listeners` removed](./listeners-removed.md) +- [Migration guide - New Emits Option](./emits-option.md) +- [Migration guide - `.native` modifier removed](./v-on-native-modifier-removed.md) +- [Migration guide - Changes in the Render Functions API](./render-function-api.md) diff --git a/src/ja/breaking-changes/children.md b/src/ja/breaking-changes/children.md new file mode 100644 index 0000000..57d1901 --- /dev/null +++ b/src/ja/breaking-changes/children.md @@ -0,0 +1,44 @@ +--- +badges: + - removed +--- + +# $children + +## Overview + +The `$children` instance property has been removed from Vue 3.0 and is no longer supported. + +## 2.x Syntax + +In 2.x, developers could access direct child components of the current instance with `this.$children`: + +```vue + + + + Change logo + + + + +``` + +## 3.x Update + +In 3.x, the `$children` property is removed and no longer supported. Instead, if you need to access a child component instance, we recommend using [template refs](https://ja.vuejs.org/guide/essentials/template-refs.html#template-refs). + +## Migration Strategy + +[Migration build flag: `INSTANCE_CHILDREN`](../migration-build.html#compat-configuration) diff --git a/src/ja/breaking-changes/custom-directives.md b/src/ja/breaking-changes/custom-directives.md new file mode 100644 index 0000000..d8e6eea --- /dev/null +++ b/src/ja/breaking-changes/custom-directives.md @@ -0,0 +1,111 @@ +--- +badges: + - breaking +--- + +# Custom Directives + +## Overview + +The hook functions for directives have been renamed to better align with the component lifecycle. + +Additionally, the `expression` string is no longer passed as part of the `binding` object. + +## 2.x Syntax + +In Vue 2, custom directives were created by using the hooks listed below to target an element’s lifecycle, all of which are optional: + +- **bind** - Called once the directive is bound to the element. Called only once. +- **inserted** - Called once the element is inserted into the parent DOM. +- **update** - This hook is called when the element updates, but children haven't been updated yet. +- **componentUpdated** - This hook is called once the component and the children have been updated. +- **unbind** - This hook is called once the directive is removed. Also called only once. + +Here’s an example of this: + +```html +Highlight this text bright yellow +``` + +```js +Vue.directive('highlight', { + bind(el, binding, vnode) { + el.style.background = binding.value + } +}) +``` + +Here, in the initial setup for this element, the directive binds a style by passing in a value, that can be updated to different values through the application. + +## 3.x Syntax + +In Vue 3, however, we’ve created a more cohesive API for custom directives. As you can see, they differ greatly from our component lifecycle methods even though we’re hooking into similar events. We’ve now unified them like so: + +- **created** - new! This is called before the element's attributes or event listeners are applied. +- bind → **beforeMount** +- inserted → **mounted** +- **beforeUpdate**: new! This is called before the element itself is updated, much like the component lifecycle hooks. +- update → removed! There were too many similarities to `updated`, so this is redundant. Please use `updated` instead. +- componentUpdated → **updated** +- **beforeUnmount**: new! Similar to component lifecycle hooks, this will be called right before an element is unmounted. +- unbind -> **unmounted** + +The final API is as follows: + +```js +const MyDirective = { + created(el, binding, vnode, prevVnode) {}, // new + beforeMount() {}, + mounted() {}, + beforeUpdate() {}, // new + updated() {}, + beforeUnmount() {}, // new + unmounted() {} +} +``` + +The resulting API could be used like this, mirroring the example from earlier: + +```html +Highlight this text bright yellow +``` + +```js +const app = Vue.createApp({}) + +app.directive('highlight', { + beforeMount(el, binding, vnode) { + el.style.background = binding.value + } +}) +``` + +Now that the custom directive lifecycle hooks mirror those of the components themselves, they become easier to reason about and remember! + +### Edge Case: Accessing the component instance + +It's generally recommended to keep directives independent of the component instance they are used in. Accessing the instance from within a custom directive is often a sign that the directive should rather be a component itself. However, there are situations where this actually makes sense. + +In Vue 2, the component instance had to be accessed through the `vnode` argument: + +```js +bind(el, binding, vnode) { + const vm = vnode.context +} +``` + +In Vue 3, the instance is now part of the `binding`: + +```js +mounted(el, binding, vnode) { + const vm = binding.instance +} +``` + +:::warning +With [fragments](../new/fragments.html#overview) support, components can potentially have more than one root node. When applied to a multi-root component, a custom directive will be ignored and a warning will be logged. +::: + +## Migration Strategy + +[Migration build flag: `CUSTOM_DIR`](../migration-build.html#compat-configuration) diff --git a/src/ja/breaking-changes/custom-elements-interop.md b/src/ja/breaking-changes/custom-elements-interop.md new file mode 100644 index 0000000..69e145d --- /dev/null +++ b/src/ja/breaking-changes/custom-elements-interop.md @@ -0,0 +1,134 @@ +--- +badges: + - breaking +--- + +# Custom Elements Interop + +## Overview + +- **BREAKING:** The checks to determine whether tags should be treated as custom elements are now performed during template compilation, and should be configured via compiler options instead of runtime config. +- **BREAKING:** Special `is` attribute usage is restricted to the reserved `` tag only. +- **NEW:** To support 2.x use cases where `is` was used on native elements to work around native HTML parsing restrictions, prefix the value with `vue:` to resolve it as a Vue component. + +## Autonomous Custom Elements + +If we want to add a custom element defined outside of Vue (e.g. using the Web Components API), we need to 'instruct' Vue to treat it as a custom element. Let's use the following template as an example. + +```html + +``` + +### 2.x Syntax + +In Vue 2.x, configuring tags as custom elements was done via `Vue.config.ignoredElements`: + +```js +// This will make Vue ignore custom element defined outside of Vue +// (e.g., using the Web Components APIs) + +Vue.config.ignoredElements = ['plastic-button'] +``` + +### 3.x Syntax + +**In Vue 3.0, this check is performed during template compilation.** To instruct the compiler to treat `` as a custom element: + +- If using a build step: pass the `isCustomElement` option to the Vue template compiler. If using `vue-loader`, this should be passed via `vue-loader`'s `compilerOptions` option: + + ```js + // in webpack config + rules: [ + { + test: /\.vue$/, + use: 'vue-loader', + options: { + compilerOptions: { + isCustomElement: tag => tag === 'plastic-button' + } + } + } + // ... + ] + ``` + +- If using on-the-fly template compilation, pass it via `app.config.compilerOptions.isCustomElement`: + + ```js + const app = Vue.createApp({}) + app.config.compilerOptions.isCustomElement = tag => tag === 'plastic-button' + ``` + + It's important to note the runtime config only affects runtime template compilation - it won't affect pre-compiled templates. + +## Customized Built-in Elements {#customized-built-in-elements} + +The Custom Elements specification provides a way to use custom elements as [Customized Built-in Element](https://html.spec.whatwg.org/multipage/custom-elements.html#custom-elements-customized-builtin-example) by adding the `is` attribute to a built-in element: + +```html +Click Me! +``` + +Vue's usage of the `is` special attribute was simulating what the native attribute does before it was made universally available in browsers. However, in 2.x it was interpreted as rendering a Vue component with the name `plastic-button`. This blocks the native usage of Customized Built-in Element mentioned above. + +In 3.0, we are limiting Vue's special treatment of the `is` attribute to the `` tag only. + +- When used on the reserved `` tag, it will behave exactly the same as in 2.x; +- When used on normal components, it will behave like a normal attribute: + + ```html + + ``` + + - 2.x behavior: renders the `bar` component. + - 3.x behavior: renders the `foo` component and passing the `is` attribute. + +- When used on plain elements, it will be passed to the `createElement` call as the `is` attribute, and also rendered as a native attribute. This supports the usage of customized built-in elements. + + ```html + Click Me! + ``` + + - 2.x behavior: renders the `plastic-button` component. + - 3.x behavior: renders a native button by calling + + ```js + document.createElement('button', { is: 'plastic-button' }) + ``` + +[Migration build flag: `COMPILER_IS_ON_ELEMENT`](../migration-build.html#compat-configuration) + +## `vue:` Prefix for In-DOM Template Parsing Workarounds + +> Note: this section only affects cases where Vue templates are directly written in the page's HTML. +> 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. + +### 2.x Syntax + +In Vue 2 we recommended working around with these restrictions by using the `is` attribute on a native tag: + +```html + + + +``` + +### 3.x Syntax + +With the behavior change of `is`, a `vue:` prefix is now required to resolve the element as a Vue component: + +```html + + + +``` + +## Migration Strategy + +- Replace `config.ignoredElements` with either `vue-loader`'s `compilerOptions` (with the build step) or `app.config.compilerOptions.isCustomElement` (with on-the-fly template compilation) + +- Change all non-`` tags with `is` usage to `` (for SFC templates) or prefix it with `vue:` (for in-DOM templates). + +## See Also + +- [Guide - Vue and Web Components](https://ja.vuejs.org/guide/extras/web-components.html) diff --git a/src/ja/breaking-changes/data-option.md b/src/ja/breaking-changes/data-option.md new file mode 100644 index 0000000..d21170b --- /dev/null +++ b/src/ja/breaking-changes/data-option.md @@ -0,0 +1,128 @@ +--- +title: Data Option +badges: + - breaking +--- + +# {{ $frontmatter.title }} + +## Overview + +- **BREAKING**: `data` component option declaration no longer accepts a plain JavaScript `object` and expects a `function` declaration. + +- **BREAKING**: when merging multiple `data` return values from mixins or extends, the merge is now shallow instead of deep (only root-level properties are merged). + +## 2.x Syntax + +In 2.x, developers could define the `data` option with either an `object` or a `function`. + +For example: + +```html + + + + + +``` + +Though this provided some convenience in terms of root instances having a shared state, this has led to confusion due to the fact that its only possible on the root instance. + +## 3.x Update + +In 3.x, the `data` option has been standardized to only accept a `function` that returns an `object`. + +Using the example above, there would only be one possible implementation of the code: + +```html + +``` + +## Mixin Merge Behavior Change {#mixin-merge-behavior-change} + +In addition, when `data()` from a component and its mixins or extends base are merged, the merge is now performed *shallowly*: + +```js +const Mixin = { + data() { + return { + user: { + name: 'Jack', + id: 1 + } + } + } +} + +const CompA = { + mixins: [Mixin], + data() { + return { + user: { + id: 2 + } + } + } +} +``` + +In Vue 2.x, the resulting `$data` is: + +```json +{ + "user": { + "id": 2, + "name": "Jack" + } +} +``` + +In 3.0, the result will be: + +```json +{ + "user": { + "id": 2 + } +} +``` + +[Migration build flag: `OPTIONS_DATA_FN`](../migration-build.html#compat-configuration) + +## Migration Strategy + +For users relying on the object declaration, we recommend: + +- Extracting the shared data into an external object and using it as a property in `data` +- Rewrite references to the shared data to point to a new shared object + +For users relying on the deep merge behavior from mixins, we recommend refactoring your code to avoid such reliance altogether, since deep merges from mixins are very implicit and can make the code logic more difficult to understand and debug. + +[Migration build flags:](../migration-build.html#compat-configuration) + +- `OPTIONS_DATA_FN` +- `OPTIONS_DATA_MERGE` diff --git a/src/ja/breaking-changes/emits-option.md b/src/ja/breaking-changes/emits-option.md new file mode 100644 index 0000000..01add6c --- /dev/null +++ b/src/ja/breaking-changes/emits-option.md @@ -0,0 +1,97 @@ +--- +title: emits Option +badges: + - new +--- + +# `emits` Option + +## Overview + +Vue 3 now offers an `emits` option, similar to the existing `props` option. This option can be used to define the events that a component can emit to its parent. + +## 2.x Behavior + +In Vue 2, you can define the props that a component receives, but you can't declare which events it can emit: + +```vue + + + {{ text }} + OK + + + +``` + +## 3.x Behavior + +Similar to props, the events that the component emits can now be defined with the `emits` option: + +```vue + + + {{ text }} + OK + + + +``` + +The option also accepts an object, which allows the developer to define validators for the arguments that are passed with the emitted event, similar to validators in `props` definitions. + +For more information on this, please read the [API documentation for this feature](https://ja.vuejs.org/api/options-state.html#emits). + +## Migration Strategy + +It is highly recommended that you document all the events emitted by each of your components using `emits`. + +This is especially important because of [the removal of the `.native` modifier](./v-on-native-modifier-removed.md). Any listeners for events that aren't declared with `emits` will now be included in the component's `$attrs`, which by default will be bound to the component's root node. + +### Example + +For components that re-emit native events to their parent, this would now lead to two events being fired: + +```vue + + OK + + +``` + +When a parent listens for the `click` event on the component: + +```html + +``` + +it would now be triggered _twice_: + +- Once from `$emit()`. +- Once from a native event listener applied to the root element. + +Here you have two options: + +1. Properly declare the `click` event. This is useful if you actually do add some logic to that event handler in ``. +2. Remove the re-emitting of the event, since the parent can now listen for the native event easily, without adding `.native`. Suitable when you really only re-emit the event anyway. + +## See also + +- [Relevant RFC](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0030-emits-option.md) +- [Migration guide - `.native` modifier removed](./v-on-native-modifier-removed.md) +- [Migration guide - `$listeners` removed](./listeners-removed.md) +- [Migration guide - `$attrs` includes `class` & `style`](./attrs-includes-class-style.md) +- [Migration guide - Changes in the Render Functions API](./render-function-api.md) diff --git a/src/ja/breaking-changes/events-api.md b/src/ja/breaking-changes/events-api.md new file mode 100644 index 0000000..9376423 --- /dev/null +++ b/src/ja/breaking-changes/events-api.md @@ -0,0 +1,104 @@ +--- +badges: + - breaking +--- + +# Events API + +## Overview + +`$on`, `$off` and `$once` instance methods are removed. Component instances no longer implement the event emitter interface. + +## 2.x Syntax + +In 2.x, a Vue instance could be used to trigger handlers attached imperatively via the event emitter API (`$on`, `$off` and `$once`). This could be used to create an _event bus_ to create global event listeners used across the whole application: + +```js +// eventBus.js + +const eventBus = new Vue() + +export default eventBus +``` + +```js +// ChildComponent.vue +import eventBus from './eventBus' + +export default { + mounted() { + // adding eventBus listener + eventBus.$on('custom-event', () => { + console.log('Custom event triggered!') + }) + }, + beforeDestroy() { + // removing eventBus listener + eventBus.$off('custom-event') + } +} +``` + +```js +// ParentComponent.vue +import eventBus from './eventBus' + +export default { + methods: { + callGlobalCustomEvent() { + eventBus.$emit('custom-event') // if ChildComponent is mounted, we will have a message in the console + } + } +} +``` + +## 3.x Update + +We removed `$on`, `$off` and `$once` methods from the instance completely. `$emit` is still a part of the existing API as it's used to trigger event handlers declaratively attached by a parent component. + +## Migration Strategy + +[Migration build flag: `INSTANCE_EVENT_EMITTER`](../migration-build.html#compat-configuration) + +In Vue 3, it is no longer possible to use these APIs to listen to a component's own emitted events from within a component. There is no migration path for that use case. + +### Root Component Events + +Static event listeners can be added to the root component by passing them as props to `createApp`: + +```js +createApp(App, { + // Listen for the 'expand' event + onExpand() { + console.log('expand') + } +}) +``` + +### Event Bus + +The event bus pattern can be replaced by using an external library implementing the event emitter interface, for example [mitt](https://github.com/developit/mitt) or [tiny-emitter](https://github.com/scottcorgan/tiny-emitter). + +Example: + +```js +// eventBus.js +import emitter from 'tiny-emitter/instance' + +export default { + $on: (...args) => emitter.on(...args), + $once: (...args) => emitter.once(...args), + $off: (...args) => emitter.off(...args), + $emit: (...args) => emitter.emit(...args) +} +``` + +This provides the same event emitter API as in Vue 2. + +In most circumstances, using a global event bus for communicating between components is discouraged. While it is often the simplest solution in the short term, it almost invariably proves to be a maintenance headache in the long term. Depending on the circumstances, there are various alternatives to using an event bus: + +* Props and events should be your first choice for parent-child communication. Siblings can communicate via their parent. +* Provide / inject allow a component to communicate with its slot contents. This is useful for tightly-coupled components that are always used together. +* Provide / inject can also be used for long-distance communication between components. It can help to avoid 'prop drilling', where props need to be passed down through many levels of components that don't need those props themselves. +* Prop drilling can also be avoided by refactoring to use slots. If an interim component doesn't need the props then it might indicate a problem with separation of concerns. Introducing a slot in that component allows the parent to create the content directly, so that props can be passed without the interim component needing to get involved. +* [Global state management](https://ja.vuejs.org/guide/scaling-up/state-management.html), such as [Pinia](https://pinia.vuejs.org/). diff --git a/src/ja/breaking-changes/filters.md b/src/ja/breaking-changes/filters.md new file mode 100644 index 0000000..4dcccb0 --- /dev/null +++ b/src/ja/breaking-changes/filters.md @@ -0,0 +1,107 @@ +--- +badges: + - removed +--- + +# Filters + +## Overview + +Filters are removed from Vue 3.0 and no longer supported. + +## 2.x Syntax + +In 2.x, developers could use filters in order to apply common text formatting. + +For example: + +```html + + Bank Account Balance + {{ accountBalance | currencyUSD }} + + + +``` + +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 + +In 3.x, filters are removed and no longer supported. Instead, we recommend replacing them with method calls or computed properties. + +Using the example above, here is one example of how it could be implemented. + +```html + + Bank Account Balance + {{ accountInUSD }} + + + +``` + +## Migration Strategy + +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](https://ja.vuejs.org/api/application.html#app-config-globalproperties): + +```js +// main.js +const app = createApp(App) + +app.config.globalProperties.$filters = { + currencyUSD(value) { + return '$' + value + } +} +``` + +Then you can fix all templates using this `$filters` object like this: + +```html + + Bank Account Balance + {{ $filters.currencyUSD(accountBalance) }} + +``` + +Note that with this approach, you can only use methods, not computed properties, as the latter only make sense when defined in the context of an individual component. diff --git a/src/ja/breaking-changes/functional-components.md b/src/ja/breaking-changes/functional-components.md new file mode 100644 index 0000000..953431d --- /dev/null +++ b/src/ja/breaking-changes/functional-components.md @@ -0,0 +1,120 @@ +--- +badges: + - breaking +--- + +# Functional Components + +## Overview + +In terms of what has changed, at a high level: + +- Performance gains from 2.x for functional components are now negligible in 3.x, so we recommend just using stateful components +- Functional components can only be created using a plain function that receives `props` and `context` (i.e., `slots`, `attrs`, `emit`) +- **BREAKING:** `functional` attribute on single-file component (SFC) `` is removed +- **BREAKING:** `{ functional: true }` option in components created by functions is removed + +For more information, read on! + +## Introduction + +In Vue 2, functional components had two primary use cases: + +- as a performance optimization, because they initialized much faster than stateful components +- to return multiple root nodes + +However, in Vue 3, the performance of stateful components has improved to the point that the difference is negligible. In addition, stateful components now also include the ability to return multiple root nodes. + +As a result, the only remaining use case for functional components is simple components, such as a component to create a dynamic heading. Otherwise, it is recommended to use stateful components as you normally would. + +## 2.x Syntax + +Using the `` component, which is responsible for rendering out the appropriate heading (i.e., `h1`, `h2`, `h3`, etc.), this could have been written as a single-file component in 2.x as: + +```js +// Vue 2 Functional Component Example +export default { + functional: true, + props: ['level'], + render(h, { props, data, children }) { + return h(`h${props.level}`, data, children) + } +} +``` + +Or, for those who preferred the `` in a single-file component: + +```vue + + + + + + +``` + +## 3.x Syntax + +### Components Created by Functions + +Now in Vue 3, all functional components are created with a plain function. In other words, there is no need to define the `{ functional: true }` component option. + +They will receive two arguments: `props` and `context`. The `context` argument is an object that contains a component's `attrs`, `slots`, and `emit` properties. + +In addition, rather than implicitly provide `h` in a `render` function, `h` is now imported globally. + +Using the previously mentioned example of a `` component, here is how it looks now. + +```js +import { h } from 'vue' + +const DynamicHeading = (props, context) => { + return h(`h${props.level}`, context.attrs, context.slots) +} + +DynamicHeading.props = ['level'] + +export default DynamicHeading +``` + +### Single File Components (SFCs) {#single-file-components-sfcs} + +In 3.x, the performance difference between stateful and functional components has been drastically reduced and will be insignificant in most use cases. As a result, the migration path for developers using `functional` on SFCs is to remove the attribute and rename all references of `props` to `$props` and `attrs` to `$attrs`. + +Using our `` example from before, here is how it would look now. + +```vue{1,3,4} + + + + + +``` + +The main differences are that: + +1. `functional` attribute removed on `` +1. `listeners` are now passed as part of `$attrs` and can be removed + +## Next Steps + +For more information on the usage of the new functional components and the changes to render functions in general, see: + +- [Migration: Render Functions](./render-function-api.html) +- [Guide: Render Functions](https://ja.vuejs.org/guide/extras/render-function.html#render-functions-jsx) +- [Migration build flag: `COMPONENT_FUNCTIONAL`](../migration-build.html#compat-configuration) diff --git a/src/ja/breaking-changes/global-api-treeshaking.md b/src/ja/breaking-changes/global-api-treeshaking.md new file mode 100644 index 0000000..f4c1184 --- /dev/null +++ b/src/ja/breaking-changes/global-api-treeshaking.md @@ -0,0 +1,166 @@ +--- +badges: + - breaking +--- + +# Global API Treeshaking + +## 2.x Syntax + +If you’ve ever had to manually manipulate DOM in Vue, you might have come across this pattern: + +```js +import Vue from 'vue' + +Vue.nextTick(() => { + // something DOM-related +}) +``` + +Or, if you’ve been unit-testing an application involving async components, chances are you’ve written something like this: + +```js +import { shallowMount } from '@vue/test-utils' +import { MyComponent } from './MyComponent.vue' + +test('an async feature', async () => { + const wrapper = shallowMount(MyComponent) + + // execute some DOM-related tasks + + await wrapper.vm.$nextTick() + + // run your assertions +}) +``` + +`Vue.nextTick()` is a global API exposed directly on a single Vue object – in fact, the instance method `$nextTick()` is just a handy wrapper around `Vue.nextTick()` with the callback’s `this` context automatically bound to the current instance for convenience. + +But what if you’ve never had to deal with manual DOM manipulation, nor are you using or testing async components in your app? Or, what if, for whatever reason, you prefer to use the good old `window.setTimeout()` instead? In such a case, the code for `nextTick()` will become dead code – that is, code that’s written but never used. And dead code is hardly a good thing, especially in our client-side context where every kilobyte matters. + +Module bundlers like webpack and Rollup (which Vite is based upon) support [tree-shaking](https://webpack.js.org/guides/tree-shaking/), which is a fancy term for “dead code elimination.” Unfortunately, due to how the code is written in previous Vue versions, global APIs like `Vue.nextTick()` are not tree-shakeable and will be included in the final bundle regardless of where they are actually used or not. + +## 3.x Syntax + +In Vue 3, the global and internal APIs have been restructured with tree-shaking support in mind. As a result, the global APIs can now only be accessed as named exports for the ES Modules build. For example, our previous snippets should now look like this: + +```js +import { nextTick } from 'vue' + +nextTick(() => { + // something DOM-related +}) +``` + +and + +```js +import { shallowMount } from '@vue/test-utils' +import { MyComponent } from './MyComponent.vue' +import { nextTick } from 'vue' + +test('an async feature', async () => { + const wrapper = shallowMount(MyComponent) + + // execute some DOM-related tasks + + await nextTick() + + // run your assertions +}) +``` + +Calling `Vue.nextTick()` directly will now result in the infamous `undefined is not a function` error. + +With this change, provided the module bundler supports tree-shaking, global APIs that are not used in a Vue application will be eliminated from the final bundle, resulting in an optimal file size. + +## Affected APIs + +These global APIs in Vue 2.x are affected by this change: + +- `Vue.nextTick` +- `Vue.observable` (replaced by `Vue.reactive`) +- `Vue.version` +- `Vue.compile` (only in full builds) +- `Vue.set` (only in compat builds) +- `Vue.delete` (only in compat builds) + +## Internal Helpers + +In addition to public APIs, many of the internal components/helpers are now exported as named exports as well. This allows the compiler to output code that only imports features when they are used. For example the following template: + +```html + + hello + +``` + +is compiled into something similar to the following: + +```js +import { h, Transition, withDirectives, vShow } from 'vue' + +export function render() { + return h(Transition, [withDirectives(h('div', 'hello'), [[vShow, this.ok]])]) +} +``` + +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 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](https://github.com/vuejs/core/tree/master/packages/vue#which-dist-file-to-use) 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). +::: + +## Usage in Plugins + +If your plugin relies on an affected Vue 2.x global API, for instance: + +```js +const plugin = { + install: Vue => { + Vue.nextTick(() => { + // ... + }) + } +} +``` + +In Vue 3, you’ll have to import it explicitly: + +```js +import { nextTick } from 'vue' + +const plugin = { + install: app => { + nextTick(() => { + // ... + }) + } +} +``` + +If you use a module bundle like webpack, this may cause Vue’s source code to be bundled into the plugin, and more often than not that’s not what you'd expect. A common practice to prevent this from happening is to configure the module bundler to exclude Vue from the final bundle. In webpack's case, you can use the [`externals`](https://webpack.js.org/configuration/externals/) configuration option: + +```js +// webpack.config.js +module.exports = { + /*...*/ + externals: { + vue: 'Vue' + } +} +``` + +This will tell webpack to treat the Vue module as an external library and not bundle it. + +If your module bundler of choice happens to be [Rollup](https://rollupjs.org/), you basically get the same effect for free, as by default Rollup will treat absolute module IDs (`'vue'` in our case) as external dependencies and not include them in the final bundle. During bundling though, it might emit a [“Treating vue as external dependency”](https://rollupjs.org/guide/en/#warning-treating-module-as-external-dependency) warning, which can be suppressed with the `external` option: + +```js +// rollup.config.js +export default { + /*...*/ + external: ['vue'] +} +``` diff --git a/src/ja/breaking-changes/global-api.md b/src/ja/breaking-changes/global-api.md new file mode 100644 index 0000000..043f3ee --- /dev/null +++ b/src/ja/breaking-changes/global-api.md @@ -0,0 +1,290 @@ +--- +badges: + - breaking +--- + +# Global API Application Instance + +Vue 2.x has a number of global APIs and configurations that globally mutate Vue’s behavior. For instance, to register a global component, you would use the `Vue.component` API like this: + +```js +Vue.component('button-counter', { + data: () => ({ + count: 0 + }), + template: 'Clicked {{ count }} times.' +}) +``` + +Similarly, this is how a global directive is declared: + +```js +Vue.directive('focus', { + inserted: (el) => el.focus() +}) +``` + +While this approach is convenient, it leads to a couple of problems. Technically, Vue 2 doesn't have a concept of an "app". What we define as an app is simply a root Vue instance created via `new Vue()`. Every root instance created from the same Vue constructor **shares the same global configuration**. As a result: + +- Global configuration makes it easy to accidentally pollute other test cases during testing. Users need to carefully store original global configuration and restore it after each test (e.g. resetting `Vue.config.errorHandler`). Some APIs like `Vue.use` and `Vue.mixin` don't even have a way to revert their effects. This makes tests involving plugins particularly tricky. In fact, vue-test-utils has to implement a special API `createLocalVue` to deal with this: + + ```js + import { createLocalVue, mount } from '@vue/test-utils' + + // create an extended `Vue` constructor + const localVue = createLocalVue() + + // install a plugin “globally” on the “local” Vue constructor + localVue.use(MyPlugin) + + // pass the `localVue` to the mount options + mount(Component, { localVue }) + ``` + +- Global configuration makes it difficult to share the same copy of Vue between multiple "apps" on the same page, but with different global configurations. + + ```js + // this affects both root instances + Vue.mixin({ + /* ... */ + }) + + const app1 = new Vue({ el: '#app-1' }) + const app2 = new Vue({ el: '#app-2' }) + ``` + +To avoid these problems, in Vue 3 we introduce… + +## A New Global API: `createApp` {#a-new-global-api-createapp} + +Calling `createApp` returns an _app instance_, a new concept in Vue 3. + +```js +import { createApp } from 'vue' + +const app = createApp({}) +``` + +If you're using a CDN build of Vue then `createApp` is exposed via the global `Vue` object: + +```js +const { createApp } = Vue + +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.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). + +### `config.productionTip` Removed {#config-productiontip-removed} + +In Vue 3.x, the "use production build" tip will only show up when using the "dev + full build" (the build that includes the runtime compiler and has warnings). + +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. + +[Migration build flag: `CONFIG_PRODUCTION_TIP`](../migration-build.html#compat-configuration) + +### `config.ignoredElements` Is Now `config.compilerOptions.isCustomElement` {#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: + +```js +// before +Vue.config.ignoredElements = ['my-el', /^ion-/] + +// after +const app = createApp({}) +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.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` {#vue-prototype-replaced-by-config-globalproperties} + +In Vue 2, `Vue.prototype` was commonly used to add properties that would be accessible in all components. + +The equivalent in Vue 3 is [`config.globalProperties`](https://ja.vuejs.org/api/application.html#app-config-globalproperties). These properties will be copied across as part of instantiating a component within the application: + +```js +// before - Vue 2 +Vue.prototype.$http = () => {} +``` + +```js +// after - Vue 3 +const app = createApp({}) +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 {#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](https://ja.vuejs.org/guide/reusability/composables.html) over inheritance and mixins. If for some reason you still need component inheritance, you can use the [`extends` option](https://ja.vuejs.org/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: + +```js +var inBrowser = typeof window !== 'undefined' +/* … */ +if (inBrowser && window.Vue) { + window.Vue.use(VueRouter) +} +``` + +As the `use` global API is no longer available in Vue 3, this method will cease to work and calling `Vue.use()` will now trigger a warning. Instead, the end-user will now have to explicitly specify using the plugin on the app instance: + +```js +const app = createApp(MyApp) +app.use(VueRouter) +``` + +## Mounting App Instance {#mounting-app-instance} + +After being initialized with `createApp(/* options */)`, the app instance `app` can be used to mount a root component instance with `app.mount(domTarget)`: + +```js +import { createApp } from 'vue' +import MyApp from './MyApp.vue' + +const app = createApp(MyApp) +app.mount('#app') +``` + +With all these changes, the component and directive we have at the beginning of the guide will be rewritten into something like this: + +```js +const app = createApp(MyApp) + +app.component('button-counter', { + data: () => ({ + count: 0 + }), + template: 'Clicked {{ count }} times.' +}) + +app.directive('focus', { + mounted: (el) => el.focus() +}) + +// now every application instance mounted with app.mount(), along with its +// component tree, will have the same “button-counter” component +// and “focus” directive without polluting the global environment +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: + +```js +// in the entry +app.provide('guide', 'Vue 3 Guide') + +// in a child component +export default { + inject: { + book: { + from: 'guide' + } + }, + template: `{{ book }}` +} +``` + +Using `provide` is especially useful when writing a plugin, as an alternative to `globalProperties`. + +## Share Configurations Among Apps + +One way to share configurations e.g. components or directives among apps is to create a factory function, like this: + +```js +import { createApp } from 'vue' +import Foo from './Foo.vue' +import Bar from './Bar.vue' + +const createMyApp = (options) => { + const app = createApp(options) + app.directive('focus' /* ... */) + + return app +} + +createMyApp(Foo).mount('#foo') +createMyApp(Bar).mount('#bar') +``` + +Now the `focus` directive will be available in both `Foo` and `Bar` instances and their descendants. diff --git a/src/ja/breaking-changes/index.md b/src/ja/breaking-changes/index.md new file mode 100644 index 0000000..e528093 --- /dev/null +++ b/src/ja/breaking-changes/index.md @@ -0,0 +1,66 @@ +# 破壊的変更 + +このページでは、Vue 2 から Vue 3 の破壊的変更をすべてリストアップしています。 + +たくさん変わったように見えますが、Vue について皆さんが知っていることや気に入っていることの多くは変わりません。しかしできる限り徹底して、ドキュメント化されたすべての変更について詳細な説明と例を提供したいと考えました。 + +## 詳細 + +### グローバル API + +- [グローバル Vue API はアプリケーションインスタンスを使用するように変更されました](./global-api.html) +- [グローバル API と内部 API が再構築され、ツリーシェイキングが可能になりました](./global-api-treeshaking.html) + +### テンプレートディレクティブ + +- [コンポーネントでの `v-model` の使用方法が見直され、`v-bind.sync` を置き換えます](./v-model.html) +- [`` や `v-for` でないノードでの `key` の使い方が変更されました](./key-attribute.html) +- [`v-if` と `v-for` を同じ要素で使用した場合の優先順位が変更されました](./v-if-v-for.html) +- [`v-bind="object"` は順序に依存するようになりました](./v-bind.html) +- [`v-on:event.native` 修飾子は削除されました](./v-on-native-modifier-removed.md) + +### コンポーネント + +- [関数型コンポーネントは普通の関数のみで作成できます](./functional-components.html) +- [単一ファイルコンポーネント(SFC)での `` の `functional` 属性と、コンポーネントオプションの `functional` は非推奨です](./functional-components.html) +- [非同期コンポーネントを作成するには `defineAsyncComponent` メソッドが必要になりました](./async-components.html) +- [コンポーネントイベントは `emits` オプションで宣言する必要があります](./emits-option.md) + +### レンダー関数 + +- [レンダー関数の API が変更されました](./render-function-api.html) +- [`$scopedSlots` プロパティーは削除され、すべてのスロットが `$slots` で関数として公開されます](./slots-unification.html) +- [`$listeners` は削除され、`$attrs` に統合されました](./listeners-removed) +- [`$attrs` には `class` と `style` 属性が含まれるようになりました](./attrs-includes-class-style.md) + +### カスタム要素 + +- [テンプレートのコンパイル時に、カスタム要素のチェックが行われるようになりました](./custom-elements-interop.html) +- [特別な `is` 属性の使用は、予約済みである `` タグのみに制限されます](./custom-elements-interop.html#customized-built-in-elements) + +### その他の小さな変更 + +- ライフサイクルの `destroyed` オプションは `unmounted` に名称変更されました +- ライフサイクルの `beforeDestroy` オプションは `beforeUnmount` に名称変更されました +- [props の `default` ファクトリー関数は `this` コンテキストにアクセスできなくなりました](./props-default-this.html) +- [カスタムディレクティブ API は、コンポーネントのライフサイクルに合わせて変更され、`binding.expression` は削除されました](./custom-directives.html) +- [`data` オプションは、常に関数として宣言する必要があります](./data-option.html) +- [mixins の `data` オプションは浅くマージされるようになりました](./data-option.html#mixin-merge-behavior-change) +- [属性の型強制戦略が変更されました](./attribute-coercion.html) +- [一部のトランジションクラス名が変更されました](./transition.html) +- [`` はデフォルトでラッパー要素をレンダリングしないようになりました](./transition-group.html) +- [配列を監視している場合、コールバックは配列が置換されたときにのみトリガーされます。変更時にトリガーする必要がある場合は、`deep` オプションを指定する必要があります。](./watch.html) +- 特別なディレクティブ(`v-if/else-if/else`, `v-for`, `v-slot`)を持たない `` タグはプレーンな要素として扱われ、その内部コンテンツをレンダリングする代わりに、ネイティブの `` 要素が生成されるようになりました。 +- [マウントされたアプリケーションは、そこにマウントされている要素を置き換えません](./mount-changes.html) +- [ライフサイクルの `hook:` イベントプレフィックスが `vnode-` に変更されました](./vnode-lifecycle-events.html) + +### 削除された API + +- [`v-on` 修飾子としての `keyCode` サポート](./keycode-modifiers.html) +- [$on, $off, \$once インスタンスメソッド](./events-api.html) +- [フィルター](./filters.html) +- [インラインテンプレート属性](./inline-template-attribute.html) +- [`$children` インスタンスプロパティー](./children.html) +- [`propsData` オプション](./props-data.html) +- `$destroy` インスタンスメソッド。ユーザーは、個々の Vue コンポーネントのライフサイクルを手動で管理する必要がなくなりました。 +- グローバル関数の `set` と `delete`、インスタンスメソッドの `$set` と `$delete` は削除されました。プロキシベースの変更検出では不要になりました。 diff --git a/src/ja/breaking-changes/inline-template-attribute.md b/src/ja/breaking-changes/inline-template-attribute.md new file mode 100644 index 0000000..5a4d8af --- /dev/null +++ b/src/ja/breaking-changes/inline-template-attribute.md @@ -0,0 +1,84 @@ +--- +badges: + - breaking +--- + +# Inline Template Attribute + +## Overview + +Support for the [inline-template feature](https://v2.ja.vuejs.org/v2/guide/components-edge-cases#%E3%82%A4%E3%83%B3%E3%83%A9%E3%82%A4%E3%83%B3%E3%83%86%E3%83%B3%E3%83%97%E3%83%AC%E3%83%BC%E3%83%88) has been removed. + +## 2.x Syntax + +In 2.x, Vue provided the `inline-template` attribute on child components to use its inner content as its template instead of treating it as distributed content. + +```html + + + These are compiled as the component's own template. + Not parent's transclusion content. + + +``` + +## 3.x Syntax + +This feature will no longer be supported. + +## Migration Strategy + +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 ` +``` + +And in the component, target the template using a selector: + +```js +const MyComp = { + template: '#my-comp-template' + // ... +} +``` + +This doesn't require any build setup, works in all browsers, is not subject to any in-DOM HTML parsing caveats (e.g. you can use camelCase prop names), and provides proper syntax highlighting in most IDEs. In traditional server-side frameworks, these templates can be split out into server template partials (included into the main HTML template) for better maintainability. + +### Option #2: Default Slot + +A component previously using `inline-template` can also be refactored using the default slot - which makes the data scoping more explicit while preserving the convenience of writing child content inline: + +```html + + + {{ msg }} {{ childState }} + + + + + {{ parentMsg }} {{ childState }} + +``` + +The child, instead of providing no template, should now render the default slot\*: + +```html + + + + +``` + +> - Note: In 3.x, slots can be rendered as the root with native [fragments](../new/fragments) support! diff --git a/src/ja/breaking-changes/key-attribute.md b/src/ja/breaking-changes/key-attribute.md new file mode 100644 index 0000000..e4d195e --- /dev/null +++ b/src/ja/breaking-changes/key-attribute.md @@ -0,0 +1,91 @@ +--- +badges: + - breaking +--- + +# `key` Attribute + +## Overview + +- **NEW:** `key`s are no longer necessary on `v-if`/`v-else`/`v-else-if` branches, since Vue now automatically generates unique `key`s. + - **BREAKING:** If you manually provide `key`s, then each branch must use a unique `key`. You can no longer intentionally use the same `key` to force branch reuse. +- **BREAKING:** `` `key` should be placed on the `` tag (rather than on its children). + +## Background + +The `key` special attribute is used as a hint for Vue's virtual DOM algorithm to keep track of a node's identity. That way, Vue knows when it can reuse and patch existing nodes and when it needs to reorder or recreate them. For more information, see the following sections: + +- [List Rendering: Maintaining State](https://ja.vuejs.org/guide/essentials/list.html#maintaining-state-with-key) +- [API Reference: `key` Special Attribute](https://ja.vuejs.org/api/built-in-special-attributes.html#key) + +## On conditional branches {#on-conditional-branches} + +In Vue 2.x, it was recommended to use `key`s on `v-if`/`v-else`/`v-else-if` branches. + +```html + +Yes +No +``` + +The example above still works in Vue 3.x. However, we no longer recommend using the `key` attribute on `v-if`/`v-else`/`v-else-if` branches, since unique `key`s are now automatically generated on conditional branches if you don't provide them. + +```html + +Yes +No +``` + +The breaking change is that if you manually provide `key`s, each branch must use a unique `key`. In most cases, you can remove these `key`s. + +```html + +Yes +No + + +Yes +No + + +Yes +No +``` + +## With `` {#with-template-v-for} + +In Vue 2.x, a `` tag could not have a `key`. Instead, you could place the `key`s on each of its children. + +```html + + + ... + ... + +``` + +In Vue 3.x, the `key` should be placed on the `` tag instead. + +```html + + + ... + ... + +``` + +Similarly, when using `` with a child that uses `v-if`, the `key` should be moved up to the `` tag. + +```html + + + ... + ... + + + + + ... + ... + +``` diff --git a/src/ja/breaking-changes/keycode-modifiers.md b/src/ja/breaking-changes/keycode-modifiers.md new file mode 100644 index 0000000..bfc05bf --- /dev/null +++ b/src/ja/breaking-changes/keycode-modifiers.md @@ -0,0 +1,72 @@ +--- +badges: + - breaking +--- + +# KeyCode Modifiers + +## Overview + +Here is a quick summary of what has changed: + +- **BREAKING**: Using numbers, i.e. keyCodes, as `v-on` modifiers is no longer supported +- **BREAKING**: `config.keyCodes` is no longer supported + +## 2.x Syntax + +In Vue 2, `keyCodes` were supported as a way to modify a `v-on` method. + +```html + + + + + +``` + +In addition, you could define your own aliases via the global `config.keyCodes` option. + +```js +Vue.config.keyCodes = { + f1: 112 +} +``` + +```html + + + + + +``` + +## 3.x Syntax + +Since [`KeyboardEvent.keyCode` has been deprecated](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode), it no longer makes sense for Vue 3 to continue supporting this as well. As a result, it is now recommended to use the kebab-case name for any key you want to use as a modifier. + +```html + + + + + +``` + +As a result, this means that `config.keyCodes` is now also deprecated and will no longer be supported. + +## Migration Strategy + +For those using `keyCode` in their codebase, we recommend converting them to their kebab-cased named equivalents. + +The keys for some punctuation marks can just be included literally. e.g. For the `,` key: + +```html + +``` + +Limitations of the syntax prevent certain characters from being matched, such as `"`, `'`, `/`, `=`, `>`, and `.`. For those characters you should check `event.key` inside the listener instead. + +[Migration build flags:](../migration-build.html#compat-configuration) + +- `CONFIG_KEY_CODES` +- `V_ON_KEYCODE_MODIFIER` diff --git a/src/ja/breaking-changes/listeners-removed.md b/src/ja/breaking-changes/listeners-removed.md new file mode 100644 index 0000000..828035a --- /dev/null +++ b/src/ja/breaking-changes/listeners-removed.md @@ -0,0 +1,76 @@ +--- +title: $listeners removed +badges: + - breaking +--- + +# `$listeners` removed + +## Overview + +The `$listeners` object has been removed in Vue 3. Event listeners are now part of `$attrs`: + +```js +{ + text: 'this is an attribute', + onClose: () => console.log('close Event triggered') +} +``` + +## 2.x Syntax + +In Vue 2, you can access attributes passed to your components with `this.$attrs`, and event listeners with `this.$listeners`. +In combination with `inheritAttrs: false`, they allow the developer to apply these attributes and listeners to some other element instead of the root element: + +```html + + + + + + +``` + +## 3.x Syntax + +In Vue 3's virtual DOM, event listeners are now just attributes, prefixed with `on`, and as such are part of the `$attrs` object, so `$listeners` has been removed. + +```vue + + + + + + +``` + +If this component received an `id` attribute and a `v-on:close` listener, the `$attrs` object will now look like this: + +```js +{ + id: 'my-input', + onClose: () => console.log('close Event triggered') +} +``` + +## Migration Strategy + +Remove all usages of `$listeners`. + +[Migration build flag: `INSTANCE_LISTENERS`](../migration-build.html#compat-configuration) + +## See also + +- [Relevant RFC](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0031-attr-fallthrough.md) +- [Migration guide - `$attrs`includes `class` & `style` ](./attrs-includes-class-style.md) +- [Migration guide - Changes in the Render Functions API](./render-function-api.md) +- [Migration guide - New Emits Option](./emits-option.md) +- [Migration guide - `.native` modifier removed](./v-on-native-modifier-removed.md) diff --git a/src/ja/breaking-changes/mount-changes.md b/src/ja/breaking-changes/mount-changes.md new file mode 100644 index 0000000..e230336 --- /dev/null +++ b/src/ja/breaking-changes/mount-changes.md @@ -0,0 +1,98 @@ +--- +title: 'Mount API changes' +badges: + - breaking +--- + +# Mounted application does not replace the element + +## Overview + +In Vue 2.x, when mounting an application that has a `template`, the rendered content replaces the element we mount to. In Vue 3.x, the rendered application is appended as a child of such an element, replacing element's `innerHTML`. + +## 2.x Syntax + +In Vue 2.x, we pass an HTML element selector to `new Vue()` or `$mount`: + +```js +new Vue({ + el: '#app', + data() { + return { + message: 'Hello Vue!' + } + }, + template: ` + {{ message }} + ` +}) + +// or +const app = new Vue({ + data() { + return { + message: 'Hello Vue!' + } + }, + template: ` + {{ message }} + ` +}) + +app.$mount('#app') +``` + +When we mount this application to the page that has a `div` with the passed selector (in our case, it's `id="app"`): + +```html + + + Some app content + + +``` + +in the rendered result, the mentioned `div` will be replaced with the rendered application content: + +```html + + Hello Vue! + +``` + +## 3.x Syntax + +In Vue 3.x, when we mount an application, its rendered content will replace the `innerHTML` of the element we pass to `mount`: + +```js +const app = Vue.createApp({ + data() { + return { + message: 'Hello Vue!' + } + }, + template: ` + {{ message }} + ` +}) + +app.mount('#app') +``` + +When this app is mounted to the page that has a `div` with `id="app"`, this will result in: + +```html + + + Hello Vue! + + +``` + +## Migration Strategy + +[Migration build flag: `GLOBAL_MOUNT_CONTAINER`](../migration-build.html#compat-configuration) + +## See Also + +- [`mount` API](https://ja.vuejs.org/api/application.html#app-mount) diff --git a/src/ja/breaking-changes/props-data.md b/src/ja/breaking-changes/props-data.md new file mode 100644 index 0000000..72953f1 --- /dev/null +++ b/src/ja/breaking-changes/props-data.md @@ -0,0 +1,41 @@ +--- +badges: + - removed +--- + +# `propsData` + +## Overview + +The `propsData` option, used to pass props to the Vue instance during its creation, is removed. To pass props to the root component of a Vue 3 application, use the second argument of [createApp](https://ja.vuejs.org/api/application.html#createapp). + +## 2.x Syntax + +In 2.x, we were able to pass props to a Vue instance during its creation: + +```js +const Comp = Vue.extend({ + props: ['username'], + template: '{{ username }}' +}) + +new Comp({ + propsData: { + username: 'Evan' + } +}) +``` + +## 3.x Update + +The `propsData` option has been removed. If you need to pass props to the root component instance during its creation, you should use the second argument of `createApp`: + +```js +const app = createApp( + { + props: ['username'], + template: '{{ username }}' + }, + { username: 'Evan' } +) +``` diff --git a/src/ja/breaking-changes/props-default-this.md b/src/ja/breaking-changes/props-default-this.md new file mode 100644 index 0000000..2b667e5 --- /dev/null +++ b/src/ja/breaking-changes/props-default-this.md @@ -0,0 +1,36 @@ +--- +title: Props Default Function this Access +badges: + - breaking +--- + +# Props Default Function `this` Access + +Props default value factory functions no longer have access to `this`. + +Instead: + +- Raw props received by the component are passed to the default function as argument; + +- The [inject](https://ja.vuejs.org/api/composition-api-dependency-injection.html#inject) API can be used inside default functions. + +```js +import { inject } from 'vue' + +export default { + props: { + theme: { + default (props) { + // `props` is the raw values passed to the component, + // before any type / default coercions + // can also use `inject` to access injected properties + return inject('theme', 'default-theme') + } + } + } +} +``` + +## Migration Strategy + +[Migration build flag: `PROPS_DEFAULT_THIS`](../migration-build.html#compat-configuration) diff --git a/src/ja/breaking-changes/render-function-api.md b/src/ja/breaking-changes/render-function-api.md new file mode 100644 index 0000000..b8d94fd --- /dev/null +++ b/src/ja/breaking-changes/render-function-api.md @@ -0,0 +1,146 @@ +--- +badges: + - breaking +--- + +# Render Function API + +## Overview + +This change will not affect `` users. + +Here is a quick summary of what has changed: + +- `h` is now globally imported instead of passed to render functions as an argument +- render function arguments changed to be more consistent between stateful and functional components +- VNodes now have a flat props structure + +For more information, read on! + +## Render Function Argument + +### 2.x Syntax + +In 2.x, the `render` function would automatically receive the `h` function (which is a conventional alias for `createElement`) as an argument: + +```js +// Vue 2 Render Function Example +export default { + render(h) { + return h('div') + } +} +``` + +### 3.x Syntax + +In 3.x, `h` is now globally imported instead of being automatically passed as an argument. + +```js +// Vue 3 Render Function Example +import { h } from 'vue' + +export default { + render() { + return h('div') + } +} +``` + +## VNode Props Format + +### 2.x Syntax + +In 2.x, `domProps` contained a nested list within the VNode props: + +```js +// 2.x +{ + staticClass: 'button', + class: {'is-outlined': isOutlined }, + staticStyle: { color: '#34495E' }, + style: { backgroundColor: buttonColor }, + attrs: { id: 'submit' }, + domProps: { innerHTML: '' }, + on: { click: submitForm }, + key: 'submit-button' +} +``` + +### 3.x Syntax + +In 3.x, the entire VNode props structure is flattened. Using the example from above, here is what it would look like now. + +```js +// 3.x Syntax +{ + class: ['button', { 'is-outlined': isOutlined }], + style: [{ color: '#34495E' }, { backgroundColor: buttonColor }], + id: 'submit', + innerHTML: '', + onClick: submitForm, + key: 'submit-button' +} +``` + +## Registered Component + +### 2.x Syntax + +In 2.x, when a component has been registered, the render function would work well when passing the component's name as a string to the first argument: + +```js +// 2.x +Vue.component('button-counter', { + data() { + return { + count: 0 + } + } + template: ` + + Clicked {{ count }} times. + + ` +}) + +export default { + render(h) { + return h('button-counter') + } +} +``` + +### 3.x Syntax + +In 3.x, with VNodes being context-free, we can no longer use a string ID to implicitly lookup registered components. Instead, we need to use an imported `resolveComponent` method: + +```js +// 3.x +import { h, resolveComponent } from 'vue' + +export default { + setup() { + const ButtonCounter = resolveComponent('button-counter') + return () => h(ButtonCounter) + } +} +``` + +For more information, see [The Render Function Api Change RFC](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0008-render-function-api-change.md#context-free-vnodes). + +## Migration Strategy + +[Migration build flag: `RENDER_FUNCTION`](../migration-build.html#compat-configuration) + +### Library Authors + +`h` being globally imported means that any library that contains Vue components will include `import { h } from 'vue'` somewhere. As a result, this creates a bit of overhead since it requires library authors to properly configure the externalization of Vue in their build setup: + +- Vue should not be bundled into the library +- For module builds, the import should be left alone and be handled by the end user bundler +- For UMD / browser builds, it should try the global Vue.h first and fallback to require calls + +## Next Steps + +See [Render Function Guide](https://ja.vuejs.org/guide/extras/render-function.html) for more detailed documentation! diff --git a/src/ja/breaking-changes/slots-unification.md b/src/ja/breaking-changes/slots-unification.md new file mode 100644 index 0000000..bf814a7 --- /dev/null +++ b/src/ja/breaking-changes/slots-unification.md @@ -0,0 +1,67 @@ +--- +badges: + - breaking +--- + +# Slots Unification + +## Overview + +This change unifies normal and scoped slots in 3.x. + +Here is a quick summary of what has changed: + +- `this.$slots` now exposes slots as functions +- **BREAKING**: `this.$scopedSlots` is removed + +For more information, read on! + +## 2.x Syntax + +When using the render function, i.e., `h`, 2.x used to define the `slot` data property on the content nodes. + +```js +// 2.x Syntax +h(LayoutComponent, [ + h('div', { slot: 'header' }, this.header), + h('div', { slot: 'content' }, this.content) +]) +``` + +In addition, when referencing scoped slots, they could be referenced using the following syntax: + +```js +// 2.x Syntax +this.$scopedSlots.header +``` + +## 3.x Syntax + +In 3.x, slots are defined as children of the current node as an object: + +```js +// 3.x Syntax +h(LayoutComponent, {}, { + header: () => h('div', this.header), + content: () => h('div', this.content) +}) +``` + +And when you need to reference scoped slots programmatically, they are now unified into the `$slots` option. + +```js +// 2.x Syntax +this.$scopedSlots.header + +// 3.x Syntax +this.$slots.header() +``` + +## Migration Strategy + +A majority of the change has already been shipped in 2.6. As a result, the migration can happen in one step: + +1. Replace all `this.$scopedSlots` occurrences with `this.$slots` in 3.x. +2. Replace all occurrences of `this.$slots.mySlot` with `this.$slots.mySlot()` + +[Migration build flag: `INSTANCE_SCOPED_SLOTS`](../migration-build.html#compat-configuration) diff --git a/src/ja/breaking-changes/transition-as-root.md b/src/ja/breaking-changes/transition-as-root.md new file mode 100644 index 0000000..b8de226 --- /dev/null +++ b/src/ja/breaking-changes/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](./transition.html) +- [`` now renders no wrapper element by default](./transition-group.html) diff --git a/src/ja/breaking-changes/transition-group.md b/src/ja/breaking-changes/transition-group.md new file mode 100644 index 0000000..4638051 --- /dev/null +++ b/src/ja/breaking-changes/transition-group.md @@ -0,0 +1,45 @@ +--- +title: Transition Group Root Element +badges: + - breaking +--- + +# {{ $frontmatter.title }} + +## Overview + +`` 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` attribute. + +```html + + + {{ item }} + + +``` + +## 3.x Syntax + +In Vue 3, we have [fragment support](../new/fragments.html), so components no longer _need_ a root node. Consequently, `` no longer renders one by default. + +- 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 + + + +``` + +## Migration Strategy + +[Migration build flag: `TRANSITION_GROUP_ROOT`](../migration-build.html#compat-configuration) + +## See also + +- [Some transition classes got a rename](./transition.html) +- [`` as a root can no longer be toggled from the outside](./transition-as-root.html) diff --git a/src/ja/breaking-changes/transition.md b/src/ja/breaking-changes/transition.md new file mode 100644 index 0000000..24327ae --- /dev/null +++ b/src/ja/breaking-changes/transition.md @@ -0,0 +1,67 @@ +--- +title: Transition Class Change +badges: + - breaking +--- + +# {{ $frontmatter.title }} + +## Overview + +The `v-enter` transition class has been renamed to `v-enter-from` and the `v-leave` transition class has been renamed to `v-leave-from`. + +## 2.x Syntax + +Before v2.1.8, we had two transition classes for each transition direction: initial and active states. + +In v2.1.8, we introduced `v-enter-to` to address the timing gap between enter/leave transitions. However, for backward compatibility, the `v-enter` name was untouched: + +```css +.v-enter, +.v-leave-to { + opacity: 0; +} + +.v-leave, +.v-enter-to { + opacity: 1; +} +``` + +This became confusing, as _enter_ and _leave_ were broad and not using the same naming convention as their class hook counterparts. + +## 3.x Update + +In order to be more explicit and legible, we have now renamed these initial state classes: + +```css +.v-enter-from, +.v-leave-to { + opacity: 0; +} + +.v-leave-from, +.v-enter-to { + opacity: 1; +} +``` + +It's now much clearer what the difference between these states is. + +![Transition Diagram](/images/transitions.svg) + +The `` component's related prop names are also changed: + +- `leave-class` is renamed to `leave-from-class` (can be written as `leaveFromClass` in render functions or JSX) +- `enter-class` is renamed to `enter-from-class` (can be written as `enterFromClass` in render functions or JSX) + +## Migration Strategy + +1. Replace instances of `.v-enter` to `.v-enter-from` +2. Replace instances of `.v-leave` to `.v-leave-from` +3. Replace instances of related prop names, as above. + +## See also + +- [`` as a root can no longer be toggled from the outside](./transition-as-root.html) +- [`` now renders no wrapper element by default](./transition-group.html) diff --git a/src/ja/breaking-changes/v-bind.md b/src/ja/breaking-changes/v-bind.md new file mode 100644 index 0000000..5277bce --- /dev/null +++ b/src/ja/breaking-changes/v-bind.md @@ -0,0 +1,48 @@ +--- +title: v-bind Merge Behavior +badges: + - breaking +--- + +# {{ $frontmatter.title }} + +## Overview + +- **BREAKING**: Order of bindings for v-bind will affect the rendering result. + +## Introduction + +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 attribute defined, the individual attribute would always overwrite bindings in the `object`. + +```html + + + + +``` + +## 3.x Syntax + +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 + + + + + + + + + +``` + +## 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 attributes. + +[Migration build flag: `COMPILER_V_BIND_OBJECT_ORDER`](../migration-build.html#compat-configuration) diff --git a/src/ja/breaking-changes/v-if-v-for.md b/src/ja/breaking-changes/v-if-v-for.md new file mode 100644 index 0000000..3781e6d --- /dev/null +++ b/src/ja/breaking-changes/v-if-v-for.md @@ -0,0 +1,36 @@ +--- +title: v-if vs. v-for Precedence +badges: + - breaking +--- + +# {{ $frontmatter.title }} + +## Overview + +- **BREAKING**: If used on the same element, `v-if` will have higher precedence than `v-for` + +## Introduction + +Two of the most commonly used directives in Vue.js are `v-if` and `v-for`. So it's no surprise that there comes a time when developers want to use both together. While this is not a recommended practice, there may be times when this is necessary, so we wanted to provide guidance for how it works. + +## 2.x Syntax + +In 2.x, when using `v-if` and `v-for` on the same element, `v-for` would take precedence. + +## 3.x Syntax + +In 3.x, `v-if` will always have the higher precedence than `v-for`. + +## Migration Strategy + +It is recommended to avoid using both on the same element due to the syntax ambiguity. + +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](https://ja.vuejs.org/guide/essentials/list.html#displaying-filtered-sorted-results) +- [List Rendering - `v-for` with `v-if`](https://ja.vuejs.org/guide/essentials/list.html#v-for-with-v-if) diff --git a/src/ja/breaking-changes/v-model.md b/src/ja/breaking-changes/v-model.md new file mode 100644 index 0000000..87609ee --- /dev/null +++ b/src/ja/breaking-changes/v-model.md @@ -0,0 +1,196 @@ +--- +badges: + - breaking +--- + +# `v-model` + +## Overview + +In terms of what has changed, at a high level: + +- **BREAKING:** When used on custom components, `v-model` prop and event default names are changed: + - prop: `value` -> `modelValue`; + - event: `input` -> `update:modelValue`; +- **BREAKING:** `v-bind`'s `.sync` modifier and component `model` option are removed and replaced with an argument on `v-model`; +- **NEW:** Multiple `v-model` bindings on the same component are possible now; +- **NEW:** Added the ability to create custom `v-model` modifiers. + +For more information, read on! + +## Introduction + +When Vue 2.0 was released, the `v-model` directive required developers to always use the `value` prop. And if developers required different props for different purposes, they would have to resort to using `v-bind.sync`. In addition, this hard-coded relationship between `v-model` and `value` led to issues with how native elements and custom elements were handled. + +In 2.2 we introduced the `model` component option that allows the component to customize the prop and event to use for `v-model`. However, this still only allowed a single `v-model` to be used on the component. + +With Vue 3, the API for two-way data binding is being standardized in order to reduce confusion and to allow developers more flexibility with the `v-model` directive. + +## 2.x Syntax + +In 2.x, using a `v-model` on a component was an equivalent of passing a `value` prop and emitting an `input` event: + +```html + + + + + +``` + +If we wanted to change prop or event names to something different, we would need to add a `model` option to `ChildComponent` component: + +```html + + + +``` + +```js +// ChildComponent.vue + +export default { + model: { + prop: 'title', + event: 'change' + }, + props: { + // this allows using the `value` prop for a different purpose + value: String, + // use `title` as the prop which take the place of `value` + title: { + type: String, + default: 'Default title' + } + } +} +``` + +So, `v-model` in this case would be a shorthand to + +```html + +``` + +### Using `v-bind.sync` + +In some cases, we might need "two-way binding" for a prop (sometimes in addition to existing `v-model` for the different prop). To do so, we recommended emitting events in the pattern of `update:myPropName`. For example, for `ChildComponent` from the previous example with the `title` prop, we could communicate the intent of assigning a new value with: + +```js +this.$emit('update:title', newValue) +``` + +Then the parent could listen to that event and update a local data property, if it wants to. For example: + +```html + +``` + +For convenience, we had a shorthand for this pattern with the `.sync` modifier: + +```html + +``` + +## 3.x Syntax + +In 3.x `v-model` on the custom component is an equivalent of passing a `modelValue` prop and emitting an `update:modelValue` event: + +```html + + + + + +``` + +### `v-model` arguments + +To change a model name, instead of a `model` component option, now we can pass an _argument_ to `v-model`: + +```html + + + + + +``` + +![v-bind anatomy](/images/v-bind-instead-of-sync.png) + +This also serves as a replacement to `.sync` modifier and allows us to have multiple `v-model`s on the custom component. + +```html + + + + + +``` + +### `v-model` modifiers + +In addition to 2.x hard-coded `v-model` modifiers like `.trim`, now 3.x supports custom modifiers: + +```html + +``` + +Read more about [custom `v-model` modifiers on components](https://ja.vuejs.org/guide/components/v-model.html#handling-v-model-modifiers). + +## Migration Strategy + +We recommend: + +- checking your codebase for `.sync` usage and replace it with `v-model`: + + ```html + + + + + + ``` + +- for all `v-model`s without arguments, make sure to change props and events name to `modelValue` and `update:modelValue` respectively + + ```html + + ``` + + ```js + // ChildComponent.vue + + export default { + props: { + modelValue: String // previously was `value: String` + }, + emits: ['update:modelValue'], + methods: { + changePageTitle(title) { + this.$emit('update:modelValue', title) // previously was `this.$emit('input', title)` + } + } + } + ``` + +[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](https://ja.vuejs.org/guide/components/v-model.html) +- [`v-model` arguments](https://ja.vuejs.org/guide/components/v-model.html#v-model-arguments) +- [Handling `v-model` modifiers](https://ja.vuejs.org/guide/components/v-model.html#handling-v-model-modifiers) diff --git a/src/ja/breaking-changes/v-on-native-modifier-removed.md b/src/ja/breaking-changes/v-on-native-modifier-removed.md new file mode 100644 index 0000000..cd49dec --- /dev/null +++ b/src/ja/breaking-changes/v-on-native-modifier-removed.md @@ -0,0 +1,59 @@ +--- +title: v-on.native modifier removed +badges: + - breaking +--- + +# `v-on.native` modifier removed + +## Overview + +The `.native` modifier for `v-on` has been removed. + +## 2.x Syntax + +Event listeners passed to a component with `v-on` are by default only triggered by emitting an event with `this.$emit`. To add a native DOM listener to the child component's root element instead, the `.native` modifier can be used: + +```html + +``` + +## 3.x Syntax + +The `.native` modifier for `v-on` has been removed. At the same time, the [new `emits` option](./emits-option.md) allows the child to define which events it does indeed emit. + +Consequently, Vue will now add all event listeners that are _not_ defined as component-emitted events in the child as native event listeners to the child's root element (unless `inheritAttrs: false` has been set in the child's options). + +```html + +``` + +`MyComponent.vue` + +```html + +``` + +## Migration Strategy + +- 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) +- [Migration guide - New Emits Option](./emits-option.md) +- [Migration guide - `$listeners` removed](./listeners-removed.md) +- [Migration guide - Changes in the Render Functions API](./render-function-api.md) diff --git a/src/ja/breaking-changes/vnode-lifecycle-events.md b/src/ja/breaking-changes/vnode-lifecycle-events.md new file mode 100644 index 0000000..92c9df8 --- /dev/null +++ b/src/ja/breaking-changes/vnode-lifecycle-events.md @@ -0,0 +1,42 @@ +--- +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 `vue:`. 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 `vue:`: + +```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](./events-api.html) diff --git a/src/ja/breaking-changes/watch.md b/src/ja/breaking-changes/watch.md new file mode 100644 index 0000000..1bc0d55 --- /dev/null +++ b/src/ja/breaking-changes/watch.md @@ -0,0 +1,32 @@ +--- +title: Watch on Arrays +badges: + - breaking +--- + +# {{ $frontmatter.title }} + +## Overview + +- **BREAKING**: When watching an array, the callback will only trigger when the array is replaced. If you need to trigger on mutation, the `deep` option must be specified. + +## 3.x Syntax + +When using [the `watch` option](https://ja.vuejs.org/api/options-state.html#watch) to watch an array, the callback will only trigger when the array is replaced. In other words, the watch callback will no longer be triggered on array mutation. To trigger on mutation, the `deep` option must be specified. + +```js +watch: { + bookList: { + handler(val, oldVal) { + console.log('book list changed') + }, + deep: true + }, +} +``` + +## Migration Strategy + +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/ja/index.md b/src/ja/index.md new file mode 100644 index 0000000..20c507a --- /dev/null +++ b/src/ja/index.md @@ -0,0 +1,49 @@ +# Vue 3 移行ガイド + +:::warning Vue 2 のサポートは 2023 年 12 月 31 日をもって終了します。 +EOL の日付までに Vue 3 へのアップグレードが不可能な場合は、[Extended LTS](https://v2.vuejs.org/lts/) の詳細をご覧ください。 +::: + +このガイドは主に、Vue 2 の経験があり、Vue 3 との変更点について学びたいユーザーのためのものです。**Vue 3 を試す前に最初から最後まで読まなければならないものではありません。** Vue 3 を学ぶには、[新しいドキュメント](https://ja.vuejs.org)を読むのがおすすめです。 + + + + + + +## 注目すべき新機能 + +Vue 3 で注目すべき新機能には、以下のようなものがあります: + +- [Composition API](https://ja.vuejs.org/guide/extras/composition-api-faq.html)\* +- [SFC Composition API のシンタックスシュガー(`
v-bind
contenteditable
draggable
spellcheck
undefined
null
true
'true'
''
1
'foo'
"true"
false
'false'
"false"
aria-checked
tabindex
alt
Highlight this text bright yellow
{{ text }}
{{ accountBalance | currencyUSD }}
{{ accountInUSD }}
{{ $filters.currencyUSD(accountBalance) }}
{{firstName}} {{lastName}} aka {{alias}}
These are compiled as the component's own template.
Not parent's transclusion content.