From 641b7d3afc9a7767f887ec5433438b21807d045a Mon Sep 17 00:00:00 2001 From: Jinjiang Date: Thu, 12 Apr 2018 16:40:41 +0800 Subject: [PATCH 1/7] added components-edge-cases --- src/v2/guide/components-edge-cases.md | 381 ++++++++++++++++++++++++++ 1 file changed, 381 insertions(+) create mode 100644 src/v2/guide/components-edge-cases.md diff --git a/src/v2/guide/components-edge-cases.md b/src/v2/guide/components-edge-cases.md new file mode 100644 index 000000000..88b845822 --- /dev/null +++ b/src/v2/guide/components-edge-cases.md @@ -0,0 +1,381 @@ +--- +title: Handling Edge Cases +type: guide +order: 106 +--- + +> This page assumes you've already read the [Components Basics](components.html). Read that first if you are new to components. + +

All the features on this page document the handling of edge cases, meaning unusual situations that sometimes require bending Vue's rules a little. Note however, that they all have disadvantages or situations where they could be dangerous. These are noted in each case, so keep them in mind when deciding to use each feature.

+ +## Element & Component Access + +In most cases, it's best to avoid reaching into other component instances or manually manipulating DOM elements. There are cases, however, when it can be appropriate. + +### Accessing the Root Instance + +In every subcomponent of a `new Vue` instance, this root instance can be accessed with the `$root` property. For example, in this root instance: + +```js +// The root Vue instance +new Vue({ + data: { + foo: 1 + }, + computed: { + bar: function () { /* ... */ } + } + methods: { + baz: function () { /* ... */ } + } +}) +``` + +All subcomponents will now be able to access this instance and use it as a global store: + +```js +// Get root data +this.$root.foo + +// Set root data +this.$root.foo = 2 + +// Access root computed properties +this.$root.bar + +// Call root methods +this.$root.baz() +``` + +

This can be convenient for demos or very small apps with a handful of components. However, the pattern does not scale well to medium or large-scale applications, so we strongly recommend using Vuex to manage state in most cases.

+ +### Accessing the Parent Component Instance + +Similar to `$root`, the `$parent` property can be used to access the parent instance from a child. This can be tempting to reach for as a lazy alternative to passing data with a prop. + +

In most cases, reaching into the parent makes your application more difficult to debug and understand, especially if you mutate data in the parent. When looking at that component later, it will be very difficult to figure out where that mutation came from.

+ +There are cases however, particularly shared component libraries, when this _might_ be appropriate. For example, in abstract components that interact with JavaScript APIs instead of rendering HTML, like these hypothetical Google Maps components: + +```html + + + +``` + +The `` component might define a `map` property that all subcomponents need access to. In this case `` might want to access that map with something like `this.$parent.getMap`, in order to add a set of markers to it. You can see this pattern [in action here](https://jsfiddle.net/chrisvfritz/ttzutdxh/). + +Keep in mind, however, that components built with this pattern are still inherently fragile. For example, imagine we add a new `` component and when `` appears within that, it should only render markers that fall within that region: + +```html + + + + + +``` + +Then inside `` you might find yourself reaching for a hack like this: + +```js +var map = this.$parent.map || this.$parent.$parent.map +``` + +This has quickly gotten out of hand. That's why to provide context information to descendent components arbitrarily deep, we instead recommend [dependency injection](#Dependency-Injection). + +### Accessing Child Component Instances & Child Elements + +Despite the existence of props and events, sometimes you might still need to directly access a child component in JavaScript. To achieve this you can assign a reference ID to the child component using the `ref` attribute. For example: + +```html + +``` + +Now in the component where you've defined this `ref`, you can use: + +```js +this.$refs.usernameInput +``` + +to access the `` instance. This may be useful when you want to, for example, programmatically focus this input from a parent. In that case, the `` component may similarly use a `ref` to provide access to specific elements inside it, such as: + +```html + +``` + +And even define methods for use by the parent: + +```js +methods: { + // Used to focus the input from the parent + focus: function () { + this.$refs.input.focus() + } +} +``` + +Thus allowing the parent component to focus the input inside `` with: + +```js +this.$refs.usernameInput.focus() +``` + +When `ref` is used together with `v-for`, the ref you get will be an array containing the child components mirroring the data source. + +

$refs are only populated after the component has been rendered, and they are not reactive. It is only meant as an escape hatch for direct child manipulation - you should avoid accessing $refs from within templates or computed properties.

+ +### Dependency Injection + +Earlier, when we described [Accessing the Parent Component Instance](#Accessing-the-Parent-Component-Instance), we showed an example like this: + +```html + + + + + +``` + +In this component, all descendants of `` needed access to a `getMap` method, in order to know which map to interact with. Unfortunately, using the `$parent` property didn't scale well to more deeply nested components. That's where dependency injection can be useful, using two new instance options: `provide` and `inject`. + +The `provide` options allows us to specify the data/methods we want to **provide** to descendent components. In this case, that's the `getMap` method inside ``: + +```js +provide: function () { + return { + getMap: this.getMap + } +} +``` + +Then in any descendants, we can use the `inject` option to receive specific properties we'd like to add to that instance: + +```js +inject: ['getMap'] +``` + +You can see the [full example here](https://jsfiddle.net/chrisvfritz/tdv8dt3s/). The advantage over using `$parent` is that we can access `getMap` in _any_ descendant component, without exposing the entire instance of ``. This allows us to more safely keep developing that component, without fear that we might change/remove something that a child component is relying on. The interface between these components remains clearly defined, just as with `props`. + +In fact, you can think of dependency injection as sort of "long-range props", except: + +* ancestor components don't need to know which descendants use the properties it provides +* descendant components don't know need to know where injected properties are coming from + +

However, there are downsides to dependency injection. It couples components in your application to the way they're currently organized, making refactoring more difficult. Provided properties are also not reactive. This is by design, because using them to create a central data store scales just as poorly as using $root for the same purpose. If the properties you want to share are specific to your app, rather than generic, or if you ever want to update provided data inside ancestors, then that's a good sign that you probably need a real state management solution like Vuex instead.

+ +Learn more about dependency injection in [the API doc](https://vuejs.org/v2/api/#provide-inject). + +## Programmatic Event Listeners + +So far, you've seen uses of `$emit`, listened to with `v-on`, but Vue instances also offer other methods in its events interface. We can: + +- Listen for an event with `$on(eventName, eventHandler)` +- Listen for an event only once with `$once(eventName, eventHandler)` +- Stop listening for an event with `$off(eventName, eventHandler)` + +You normally won't have to use these, but they're available for cases when you need to manually listen for events on a component instance. They can also be useful as a code organization tool. For example, you may often see this pattern for integrating a 3rd-party library: + +```js +// Attach the datepicker to an input once +// it's mounted to the DOM. +mounted: function () { + // Pikaday is a 3rd-party datepicker library + this.picker = new Pikaday({ + field: this.$refs.input, + format: 'YYYY-MM-DD' + }) +}, +// Right before the component is destroyed, +// also destroy the datepicker. +beforeDestroy: function () { + this.picker.destroy() +} +``` + +This has two potential issues: + +- It requires saving the `picker` to the component instance, when it's possible that only lifecycle hooks need access to it. This isn't terrible, but it could be considered clutter. +- Our setup code is kept separate from our cleanup code, making it more difficult to programmatically clean up anything we set up. + +You could resolve both issues with a programmatic listener: + +```js +mounted: function () { + var picker = new Pikaday({ + field: this.$refs.input, + format: 'YYYY-MM-DD' + }) + + this.$once('hook:beforeDestroy', function () { + picker.destroy() + }) +} +``` + +Using this strategy, we could even use Pikaday with several input elements, with each new instance automatically cleaning up after itself: + +```js +mounted: function () { + this.attachDatepicker('startDateInput') + this.attachDatepicker('endDateInput') +}, +methods: { + attachDatepicker: function (refName) { + var picker = new Pikaday({ + field: this.$refs[refName], + format: 'YYYY-MM-DD' + }) + + this.$once('hook:beforeDestroy', function () { + picker.destroy() + }) + } +} +``` + +See [this fiddle](https://jsfiddle.net/chrisvfritz/1Leb7up8/) for the full code. Note, however, that if you find yourself having to do a lot of setup and cleanup within a single component, the best solution will usually be to create more modular components. In this case, we'd recommend creating a reusable `` component. + +To learn more about programmatic listeners, check out the API for [Events Instance Methods](https://vuejs.org/v2/api/#Instance-Methods-Events). + +

Note that Vue's event system is different from the browser's EventTarget API. Though they work similarly, $emit, $on, and $off are not aliases for dispatchEvent, addEventListener, and removeEventListener.

+ +## Circular References + +### Recursive Components + +Components can recursively invoke themselves in their own template. However, they can only do so with the `name` option: + +``` js +name: 'unique-name-of-my-component' +``` + +When you register a component globally using `Vue.component`, the global ID is automatically set as the component's `name` option. + +``` js +Vue.component('unique-name-of-my-component', { + // ... +}) +``` + +If you're not careful, recursive components can also lead to infinite loops: + +``` js +name: 'stack-overflow', +template: '
' +``` + +A component like the above will result in a "max stack size exceeded" error, so make sure recursive invocation is conditional (i.e. uses a `v-if` that will eventually be `false`). + +### Circular References Between Components + +Let's say you're building a file directory tree, like in Finder or File Explorer. You might have a `tree-folder` component with this template: + +``` html +

+ {{ folder.name }} + +

+``` + +Then a `tree-folder-contents` component with this template: + +``` html +
    +
  • + + {{ child.name }} +
  • +
+``` + +When you look closely, you'll see that these components will actually be each other's descendent _and_ ancestor in the render tree - a paradox! When registering components globally with `Vue.component`, this paradox is resolved for you automatically. If that's you, you can stop reading here. + +However, if you're requiring/importing components using a __module system__, e.g. via Webpack or Browserify, you'll get an error: + +``` +Failed to mount component: template or render function not defined. +``` + +To explain what's happening, let's call our components A and B. The module system sees that it needs A, but first A needs B, but B needs A, but A needs B, etc. It's stuck in a loop, not knowing how to fully resolve either component without first resolving the other. To fix this, we need to give the module system a point at which it can say, "A needs B _eventually_, but there's no need to resolve B first." + +In our case, let's make that point the `tree-folder` component. We know the child that creates the paradox is the `tree-folder-contents` component, so we'll wait until the `beforeCreate` lifecycle hook to register it: + +``` js +beforeCreate: function () { + this.$options.components.TreeFolderContents = require('./tree-folder-contents.vue').default +} +``` + +Or alternatively, you could use Webpack's asynchronous `import` when you register the component locally: + +``` js +components: { + TreeFolderContents: () => import('./tree-folder-contents.vue') +} +``` + +Problem solved! + +## Alternate Template Definitions + +### Inline Templates + +When the `inline-template` special attribute is present on a child component, the component will use its inner content as its template, rather than treating it as distributed content. This allows more flexible template-authoring. + +``` html + +
+

These are compiled as the component's own template.

+

Not parent's transclusion content.

+
+
+``` + +

However, inline-template makes the scope of your templates harder to reason about. As a best practice, prefer defining templates inside the component using the template option or in a <template> element in a .vue file.

+ +### X-Templates + +Another way to define templates is inside of a script element with the type `text/x-template`, then referencing the template by an id. For example: + +``` html + +``` + +``` js +Vue.component('hello-world', { + template: '#hello-world-template' +}) +``` + +

These can be useful for demos with large templates or in extremely small applications, but should otherwise be avoided, because they separate templates from the rest of the component definition.

+ +## Controlling Updates + +Thanks to Vue's Reactivity system, it always knows when to update (if you use it correctly). There are edge cases, however, when you might want to force an update, despite the fact that no reactive data has changed. Then there are other cases when you might want to prevent unnecessary updates. + +### Forcing an Update + +

If you find yourself needing to force an update in Vue, in 99.99% of cases, you've made a mistake somewhere.

+ +You may not have accounted for change detection caveats [with arrays](https://vuejs.org/v2/guide/list.html#Caveats) or [objects](https://vuejs.org/v2/guide/list.html#Object-Change-Detection-Caveats), or you may be relying on state that isn't tracked by Vue's reactivity system, e.g. with `data`. + +However, if you've ruled out the above and find yourself in this extremely rare situation of having to manually force an update, you can do so with [`$forceUpdate`](../api/#vm-forceUpdate). + +### Cheap Static Components with `v-once` + +Rendering plain HTML elements is very fast in Vue, but sometimes you might have a component that contains **a lot** of static content. In these cases, you can ensure that it's only evaluated once and then cached by adding the `v-once` directive to the root element, like this: + +``` js +Vue.component('terms-of-service', { + template: ` +
+

Terms of Service

+ ... a lot of static content ... +
+ ` +}) +``` + +

Once again, try not to overuse this pattern. While convenient in those rare cases when you have to render a lot of static content, it's simply not necessary unless you actually notice slow rendering -- plus, it could cause a lot of confusion later. For example, imagine another developer who's not familiar with v-once or simply misses it in the template. They might spend hours trying to figure out why the template isn't updating correctly.

From a7dd483bd1507f7aad76f7e8efa19e0da6115568 Mon Sep 17 00:00:00 2001 From: Jinjiang Date: Sat, 14 Apr 2018 11:20:23 +0800 Subject: [PATCH 2/7] translated components-edge-cases.md#access --- src/v2/guide/components-edge-cases.md | 84 +++++++++++++-------------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/src/v2/guide/components-edge-cases.md b/src/v2/guide/components-edge-cases.md index 88b845822..86a8f28f9 100644 --- a/src/v2/guide/components-edge-cases.md +++ b/src/v2/guide/components-edge-cases.md @@ -1,23 +1,23 @@ --- -title: Handling Edge Cases +title: 处理边界情况 type: guide order: 106 --- -> This page assumes you've already read the [Components Basics](components.html). Read that first if you are new to components. +> 该页面假设你已经阅读过了[组件基础](components.html)。如果你还对组件不太了解,推荐你先阅读它。 -

All the features on this page document the handling of edge cases, meaning unusual situations that sometimes require bending Vue's rules a little. Note however, that they all have disadvantages or situations where they could be dangerous. These are noted in each case, so keep them in mind when deciding to use each feature.

+

这里记录的都是和处理边界情况有关的功能,即一些需要对 Vue 的规则做一些小调整的特殊情况。不过注意这些功能都是有劣势或危险的场景的。我们会在每个案例中注明,所以当你使用每个功能的时候请稍加留意。

-## Element & Component Access +## 访问元素 & 组件 -In most cases, it's best to avoid reaching into other component instances or manually manipulating DOM elements. There are cases, however, when it can be appropriate. +在绝大多数情况下,我们最好不要触达另一个组件实例内部或手动操作 DOM 元素。不过也确实在一些情况下做这些事情是合适的。 -### Accessing the Root Instance +### 访问根实例 -In every subcomponent of a `new Vue` instance, this root instance can be accessed with the `$root` property. For example, in this root instance: +在每个 `new Vue` 实例的子组件中,其根实例可以通过 `$root` 属性进行访问。例如,在这个根实例中: ```js -// The root Vue instance +// Vue 根实例 new Vue({ data: { foo: 1 @@ -31,31 +31,31 @@ new Vue({ }) ``` -All subcomponents will now be able to access this instance and use it as a global store: +所有的子组件都可以将这个实例作为一个全局 store 来访问或使用。 ```js -// Get root data +// 获取根组件的数据 this.$root.foo -// Set root data +// 写入根组件的数据 this.$root.foo = 2 -// Access root computed properties +// 访问根组件的计算属性 this.$root.bar -// Call root methods +// 调用根组件的方法 this.$root.baz() ``` -

This can be convenient for demos or very small apps with a handful of components. However, the pattern does not scale well to medium or large-scale applications, so we strongly recommend using Vuex to manage state in most cases.

+

对于 demo 或非常小型的有少量组件的应用来说这是很方便的。不过这个模式扩展到中大型应用来说就不然了。因此在绝大多数情况下,我们强烈推荐使用 Vuex 来管理应用的状态。

-### Accessing the Parent Component Instance +### 访问父级组件实例 -Similar to `$root`, the `$parent` property can be used to access the parent instance from a child. This can be tempting to reach for as a lazy alternative to passing data with a prop. +和 `$root` 类似,`$parent` 属性可以用来从一个子组件访问父组件的实例。它提供了一种机会,可以在后期随时触达父级组件,以替代将数据以 prop 的方式传入子组件的方式。 -

In most cases, reaching into the parent makes your application more difficult to debug and understand, especially if you mutate data in the parent. When looking at that component later, it will be very difficult to figure out where that mutation came from.

+

在绝大多数情况下,触达父级组件会使得你的应用更难调试和理解,尤其是当你变更了父级组件的数据的时候。当我们稍后回看那个组件的时候,很难找出那个变更是从来里发起的。

-There are cases however, particularly shared component libraries, when this _might_ be appropriate. For example, in abstract components that interact with JavaScript APIs instead of rendering HTML, like these hypothetical Google Maps components: +另外在一些_可能_适当的时候,你需要特别地共享一些组件库。举个例子,在和 JavaScript API 进行交互而不渲染 HTML 的抽象组件内,诸如这些假设性的 Google 地图组件一样: ```html @@ -63,9 +63,9 @@ There are cases however, particularly shared component libraries, when this _mig ``` -The `` component might define a `map` property that all subcomponents need access to. In this case `` might want to access that map with something like `this.$parent.getMap`, in order to add a set of markers to it. You can see this pattern [in action here](https://jsfiddle.net/chrisvfritz/ttzutdxh/). +这个 `` 组件可以定义一个 `map` 属性,所有的子组件都需要访问它。在这种情况下 `` 可能想要通过类似 `this.$parent.getMap` 的方式访问那个地图,以便为其添加一组标记。你可以在[这里](https://jsfiddle.net/chrisvfritz/ttzutdxh/)查阅这种模式。 -Keep in mind, however, that components built with this pattern are still inherently fragile. For example, imagine we add a new `` component and when `` appears within that, it should only render markers that fall within that region: +请留意,尽管如此,通过这种模式构建出来的那个组件的内部仍然是容易出现问题的。比如,设想一下我们添加一个新的 `` 组件,当 `` 在其内部出现的时候,只会渲染那个区域内的标记: ```html @@ -75,58 +75,58 @@ Keep in mind, however, that components built with this pattern are still inheren ``` -Then inside `` you might find yourself reaching for a hack like this: +那么在 `` 内部你可能发现自己需要一些类似这样的 hack: ```js var map = this.$parent.map || this.$parent.$parent.map ``` -This has quickly gotten out of hand. That's why to provide context information to descendent components arbitrarily deep, we instead recommend [dependency injection](#Dependency-Injection). +很快它就会失控。这也是我们针对需要向任意更深层级的组件提供上下文信息时推荐[依赖注入](#依赖注入)的原因。 -### Accessing Child Component Instances & Child Elements +### 访问子组件实例或子元素 -Despite the existence of props and events, sometimes you might still need to directly access a child component in JavaScript. To achieve this you can assign a reference ID to the child component using the `ref` attribute. For example: +尽管存在 prop 和事件,有的时候你仍可能需要在 JavaScript 里直接访问一个子组件。为了达到这个目的,你可以通过 `ref` 特性为这个子组件赋予一个 ID 引用。例如: ```html ``` -Now in the component where you've defined this `ref`, you can use: +现在在你已经定义了这个 `ref` 的组件里,你可以使用: ```js this.$refs.usernameInput ``` -to access the `` instance. This may be useful when you want to, for example, programmatically focus this input from a parent. In that case, the `` component may similarly use a `ref` to provide access to specific elements inside it, such as: +来访问这个 `` 实例,以便不时之需。比如程序化的从一个父级组件聚焦这个输入框。在刚才那个例子中,该 `` 组件也可以使用一个类似的 `ref` 提供对内部这个指定元素的访问,例如: ```html ``` -And even define methods for use by the parent: +甚至可以通过其父级组件定义方法: ```js methods: { - // Used to focus the input from the parent + // 用来从父级组件聚焦输入框 focus: function () { this.$refs.input.focus() } } ``` -Thus allowing the parent component to focus the input inside `` with: +这样就允许父级组件通过下面的代码聚焦 `` 里的输入框: ```js this.$refs.usernameInput.focus() ``` -When `ref` is used together with `v-for`, the ref you get will be an array containing the child components mirroring the data source. +当 `ref` 和 `v-for` 一起使用的时候,你得到的引用将会是一个包含了对应数据源的这些子组件的数组。 -

$refs are only populated after the component has been rendered, and they are not reactive. It is only meant as an escape hatch for direct child manipulation - you should avoid accessing $refs from within templates or computed properties.

+

$refs 只会在组件渲染完成之后生效,并且它们不是响应式的。这只意味着一个直接的子组件封装的“逃生舱”——你应该避免在模板或计算属性中访问 $refs

-### Dependency Injection +### 依赖注入 -Earlier, when we described [Accessing the Parent Component Instance](#Accessing-the-Parent-Component-Instance), we showed an example like this: +在此之前,在我们描述[访问父级组件实例](#访问父级组件实例)的时候,展示过一个类似这样的例子: ```html @@ -136,9 +136,9 @@ Earlier, when we described [Accessing the Parent Component Instance](#Accessing- ``` -In this component, all descendants of `` needed access to a `getMap` method, in order to know which map to interact with. Unfortunately, using the `$parent` property didn't scale well to more deeply nested components. That's where dependency injection can be useful, using two new instance options: `provide` and `inject`. +在这个组件里,所有 `` 的后代都需要访问一个 `getMap` 方法,以便知道要跟那个地图进行交互。不幸的是,使用 `$parent` 属性无法很好的扩展到更深层级的嵌套组件上。这也是依赖注入的用武之地,它用到了两个新的实例选项:`provide` 和 `inject`。 -The `provide` options allows us to specify the data/methods we want to **provide** to descendent components. In this case, that's the `getMap` method inside ``: +`provide` 选项允许我们指定我们想要**提供**给后代组件的数据/方法。在这个例子中,就是 `` 内部的 `getMap` 方法: ```js provide: function () { @@ -148,22 +148,22 @@ provide: function () { } ``` -Then in any descendants, we can use the `inject` option to receive specific properties we'd like to add to that instance: +然后在任何后代组件里,我们都可以使用 `inject` 选项来接收指定的我们想要添加在这个实例上的属性: ```js inject: ['getMap'] ``` -You can see the [full example here](https://jsfiddle.net/chrisvfritz/tdv8dt3s/). The advantage over using `$parent` is that we can access `getMap` in _any_ descendant component, without exposing the entire instance of ``. This allows us to more safely keep developing that component, without fear that we might change/remove something that a child component is relying on. The interface between these components remains clearly defined, just as with `props`. +你可以在[这里](https://jsfiddle.net/chrisvfritz/tdv8dt3s/)看到完整的示例。相比 `$parent` 来说,这个用法可以让我们在_任意_后代组件中访问 `getMap`,而不需要暴露整个 `` 实例。这允许我们更好的持续研发该组件,而不需要担心我们可能会改变/移除一些子组件依赖的东西。同时这些组件之间的接口是始终明确定义的,就和 `props` 一样。 -In fact, you can think of dependency injection as sort of "long-range props", except: +实际上,你可以把依赖注入看作一部分“大范围有效的 prop”,除了: -* ancestor components don't need to know which descendants use the properties it provides -* descendant components don't know need to know where injected properties are coming from +* 祖先组件不需要知道哪些后代组件使用它提供的属性 +* 后代组件不需要知道被注入的属性来自哪里 -

However, there are downsides to dependency injection. It couples components in your application to the way they're currently organized, making refactoring more difficult. Provided properties are also not reactive. This is by design, because using them to create a central data store scales just as poorly as using $root for the same purpose. If the properties you want to share are specific to your app, rather than generic, or if you ever want to update provided data inside ancestors, then that's a good sign that you probably need a real state management solution like Vuex instead.

+

然而,依赖注入还是有负面影响的。它将你的应用目前的组件组织方式耦合了起来,使重构变得更加困难。同时所提供的属性是非响应式的。这是出于设计的考虑,因为使用它们来创建一个中心化规模化的数据跟使用 $root做这件事都是不够好的。如果你想要共享的这个属性是你的应用特有的,而不是通用化的,或者如果你想在祖先组件中更新所提供的数据,那么这意味着你可能需要换用一个像 Vuex 这样真正的状态管理方案了。

-Learn more about dependency injection in [the API doc](https://vuejs.org/v2/api/#provide-inject). +你可以在 [API 参考文档](https://vuejs.org/v2/api/#provide-inject)学习更多关于依赖注入的知识。 ## Programmatic Event Listeners From 5dd5558b6826f6dcdba76daf5a2af86ccccd9c67 Mon Sep 17 00:00:00 2001 From: Jinjiang Date: Sat, 14 Apr 2018 22:55:54 +0800 Subject: [PATCH 3/7] translated components-edge-cases.md#events --- src/v2/guide/components-edge-cases.md | 37 ++++++++++++++------------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/src/v2/guide/components-edge-cases.md b/src/v2/guide/components-edge-cases.md index 86a8f28f9..ee11bfcc2 100644 --- a/src/v2/guide/components-edge-cases.md +++ b/src/v2/guide/components-edge-cases.md @@ -165,39 +165,40 @@ inject: ['getMap'] 你可以在 [API 参考文档](https://vuejs.org/v2/api/#provide-inject)学习更多关于依赖注入的知识。 -## Programmatic Event Listeners +## 程序化的事件侦听器 -So far, you've seen uses of `$emit`, listened to with `v-on`, but Vue instances also offer other methods in its events interface. We can: +现在,你已经知道了 `$emit` 的用法,它可以被 `v-on` 侦听,但是 Vue 实例同时在其事件接口中提供了其它的方法。我们可以: -- Listen for an event with `$on(eventName, eventHandler)` -- Listen for an event only once with `$once(eventName, eventHandler)` -- Stop listening for an event with `$off(eventName, eventHandler)` +- 通过 `$on(eventName, eventHandler)` 侦听一个事件 +- 通过 `$once(eventName, eventHandler)` 一次性侦听一个事件 +- 通过 `$off(eventName, eventHandler)` 停止侦听一个事件 You normally won't have to use these, but they're available for cases when you need to manually listen for events on a component instance. They can also be useful as a code organization tool. For example, you may often see this pattern for integrating a 3rd-party library: +你通常不会用到这些,但是当你需要在一个组件实例上手动侦听事件时,它们是排得上用场的。它们也可以用于代码组织工具。例如,你可能经常看到这种集成一个第三方库的模式: ```js -// Attach the datepicker to an input once -// it's mounted to the DOM. +// 一次性将这个日期选择器附加到一个输入框上 +// 它会被挂载到 DOM 上。 mounted: function () { - // Pikaday is a 3rd-party datepicker library + // Pikaday 是一个第三方日期选择器到库 this.picker = new Pikaday({ field: this.$refs.input, format: 'YYYY-MM-DD' }) }, -// Right before the component is destroyed, -// also destroy the datepicker. +// 在组件被销毁之前, +// 也销毁这个日期选择器。 beforeDestroy: function () { this.picker.destroy() } ``` -This has two potential issues: +这里有两个潜在的问题: -- It requires saving the `picker` to the component instance, when it's possible that only lifecycle hooks need access to it. This isn't terrible, but it could be considered clutter. -- Our setup code is kept separate from our cleanup code, making it more difficult to programmatically clean up anything we set up. +- 它需求在这个组件实例中保存这个 `picker`,如果可以的话最好只有生命周期钩子可以访问到它。这并不算严重的问题,但是它可以被视为杂物。 +- 我们的建立代码独立于我们的清理代码,这使得我们比较难于程序化的清理我们建立的所有东西。 -You could resolve both issues with a programmatic listener: +你应该通过一个程序化的侦听器解决这两个问题: ```js mounted: function () { @@ -212,7 +213,7 @@ mounted: function () { } ``` -Using this strategy, we could even use Pikaday with several input elements, with each new instance automatically cleaning up after itself: +使用了这个策略,我甚至可以让多个输入框元素同时使用不同的 Pikaday,每个新的实例都程序化的在后期清理它自己: ```js mounted: function () { @@ -233,11 +234,11 @@ methods: { } ``` -See [this fiddle](https://jsfiddle.net/chrisvfritz/1Leb7up8/) for the full code. Note, however, that if you find yourself having to do a lot of setup and cleanup within a single component, the best solution will usually be to create more modular components. In this case, we'd recommend creating a reusable `` component. +查阅[这个 fiddle](https://jsfiddle.net/chrisvfritz/1Leb7up8/) 可以了解到完整的代码。注意,即便如此,如果你发现自己不得不在单个组件里做很多建立和清理的工作,最好的方式通常还是创建更多的模块化组件。在这个例子中,我们推荐创建一个可复用的 `` 组件。 -To learn more about programmatic listeners, check out the API for [Events Instance Methods](https://vuejs.org/v2/api/#Instance-Methods-Events). +想了解更多程序化侦听器的内容,请查阅[实例方法 / 事件](https://cn.vuejs.org/v2/api/#实例方法-事件)相关的 API。 -

Note that Vue's event system is different from the browser's EventTarget API. Though they work similarly, $emit, $on, and $off are not aliases for dispatchEvent, addEventListener, and removeEventListener.

+

注意 Vue 的事件系统不同于浏览器的 EventTarget API。尽管它们工作起来是相似的,但是 $emit$on, 和 $off 并不是 dispatchEventaddEventListenerremoveEventListener 的别名。

## Circular References From 84ac8226d395c4fbed8cd71848808ea2fc7c674b Mon Sep 17 00:00:00 2001 From: Jinjiang Date: Sat, 14 Apr 2018 23:44:41 +0800 Subject: [PATCH 4/7] translated components-edge-cases.md#circular --- src/v2/guide/components-edge-cases.md | 30 +++++++++++++-------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/v2/guide/components-edge-cases.md b/src/v2/guide/components-edge-cases.md index ee11bfcc2..5f4309128 100644 --- a/src/v2/guide/components-edge-cases.md +++ b/src/v2/guide/components-edge-cases.md @@ -240,17 +240,17 @@ methods: {

注意 Vue 的事件系统不同于浏览器的 EventTarget API。尽管它们工作起来是相似的,但是 $emit$on, 和 $off 并不是 dispatchEventaddEventListenerremoveEventListener 的别名。

-## Circular References +## 循环引用 -### Recursive Components +### 递归组件 -Components can recursively invoke themselves in their own template. However, they can only do so with the `name` option: +组件是可以在它们自己的模板中调用自身的。不过它们只能通过 `name` 选项来做这件事: ``` js name: 'unique-name-of-my-component' ``` -When you register a component globally using `Vue.component`, the global ID is automatically set as the component's `name` option. +当你使用 `Vue.component` 全局注册一个组件时,这个全局的 ID 会自动设置为该组件的 `name` 选项。 ``` js Vue.component('unique-name-of-my-component', { @@ -258,18 +258,18 @@ Vue.component('unique-name-of-my-component', { }) ``` -If you're not careful, recursive components can also lead to infinite loops: +稍有不慎,递归组件就可能导致无限循环: ``` js name: 'stack-overflow', template: '
' ``` -A component like the above will result in a "max stack size exceeded" error, so make sure recursive invocation is conditional (i.e. uses a `v-if` that will eventually be `false`). +类似上述的组件将会导致“max stack size exceeded”错误,所以请确保递归调用是条件性的 (例如使用一个最终会得到 `false` 的 `v-if`)。 -### Circular References Between Components +### 组件之间的循环引用 -Let's say you're building a file directory tree, like in Finder or File Explorer. You might have a `tree-folder` component with this template: +假设你需要构建一个文件目录树,像访达或资源管理器那样的。你可能有一个 `` 组件,模板是这样的: ``` html

@@ -278,7 +278,7 @@ Let's say you're building a file directory tree, like in Finder or File Explorer

``` -Then a `tree-folder-contents` component with this template: +还有一个 `` 组件,模板是这样的: ``` html
    @@ -289,17 +289,17 @@ Then a `tree-folder-contents` component with this template:
``` -When you look closely, you'll see that these components will actually be each other's descendent _and_ ancestor in the render tree - a paradox! When registering components globally with `Vue.component`, this paradox is resolved for you automatically. If that's you, you can stop reading here. +当你仔细观察的时候,你会发现这些组件在渲染树中互为对方的后代_和_祖先——一个悖论!当通过 `Vue.component` 全局注册组件的时候,这个悖论会被自动解开。如果你是这样做的,那么你可以跳过这里。 -However, if you're requiring/importing components using a __module system__, e.g. via Webpack or Browserify, you'll get an error: +然而,如果你使用一个_模块系统_依赖/导入组件,例如通过 webpack 或 Browserify,你会遇到一个错误: ``` Failed to mount component: template or render function not defined. ``` -To explain what's happening, let's call our components A and B. The module system sees that it needs A, but first A needs B, but B needs A, but A needs B, etc. It's stuck in a loop, not knowing how to fully resolve either component without first resolving the other. To fix this, we need to give the module system a point at which it can say, "A needs B _eventually_, but there's no need to resolve B first." +为了解释这里发生了什么,我们先把两个组件称为 A 和 B。模块系统发现它需要 A,但是首先 A 依赖 B,但是 B 又依赖 A,但是 A 又依赖 B,如此往复。这变成了一个循环,不知道如何不经过其中一个组件而完全解析出另一个组件。为了解决这个问题,我们需要给模块系统一个点,在那里“A _反正_是需要 B 的,但是我们不需要先解析 B。” -In our case, let's make that point the `tree-folder` component. We know the child that creates the paradox is the `tree-folder-contents` component, so we'll wait until the `beforeCreate` lifecycle hook to register it: +在我们的例子中,把 `` 组件设为了那个点。我们知道那个产生悖论的子组件是 `` 组件,所以我们会等到生命周期钩子 `beforeCreate` 时去注册它: ``` js beforeCreate: function () { @@ -307,7 +307,7 @@ beforeCreate: function () { } ``` -Or alternatively, you could use Webpack's asynchronous `import` when you register the component locally: +或者,在本地注册组件的时候,你可以使用 webpack 的异步 `import`: ``` js components: { @@ -315,7 +315,7 @@ components: { } ``` -Problem solved! +这样问题就解决了! ## Alternate Template Definitions From 2517138d1b57171f18d8e080a6adcf4410dffe98 Mon Sep 17 00:00:00 2001 From: Jinjiang Date: Sun, 15 Apr 2018 00:09:50 +0800 Subject: [PATCH 5/7] translated components-edge-cases.md#template --- src/v2/guide/components-edge-cases.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/v2/guide/components-edge-cases.md b/src/v2/guide/components-edge-cases.md index 5f4309128..945e19991 100644 --- a/src/v2/guide/components-edge-cases.md +++ b/src/v2/guide/components-edge-cases.md @@ -317,11 +317,11 @@ components: { 这样问题就解决了! -## Alternate Template Definitions +## 模板定义的替代品 -### Inline Templates +### 内联模板 -When the `inline-template` special attribute is present on a child component, the component will use its inner content as its template, rather than treating it as distributed content. This allows more flexible template-authoring. +当 `inline-template` 这个特殊的特性出现在一个子组件上时,这个组件将会使用其里面的内容作为模板,而不是将其作为被分发的内容。这使得模板的撰写工作更加灵活。 ``` html @@ -332,11 +332,11 @@ When the `inline-template` special attribute is present on a child component, th ``` -

However, inline-template makes the scope of your templates harder to reason about. As a best practice, prefer defining templates inside the component using the template option or in a <template> element in a .vue file.

+

不过,inline-template 会让你模板的作用域变得更加难以理解。所以作为最佳实践,请在组件内优先选择 template 选项或 .vue 文件里的一个 <template> 元素来定义模板。

### X-Templates -Another way to define templates is inside of a script element with the type `text/x-template`, then referencing the template by an id. For example: +另一个定义模板的方式是在一个 `