vue 如何处理两个组件异步问题_Vue.js 可复用组件,子组件,异步组件,递归组件,x-template,v-once-歪脖网...

本文详细介绍了Vue.js中处理组件异步加载的方法,包括使用工厂函数和异步组件的高级选项。此外,还讲解了递归组件的概念,强调了在递归组件中设置name选项的重要性,以及如何解决组件之间的循环依赖问题。同时,提到了inline-template特性和v-once指令的使用场景。
摘要由CSDN通过智能技术生成

Vue.js 基础

Vue.js 安装

Vue.js 介绍

Vue.js 实例

Vue.js 模板语法

Vue.js 计算属性和侦听器

Vue.js Class 与 Style 绑定

Vue.js 条件渲染

Vue.js 列表渲染

Vue.js 事件处理

Vue.js 表单输入绑定

Vue.js 组件

动态组件

使用插槽分发内容

自定义事件

非 Prop 特性

Prop

使用组件

什么是组件

杂项

在编写组件时,最好考虑好以后是否要进行复用。一次性组件间有紧密的耦合没关系,但是可复用组件应当定义一个清晰的公开接口,同时也不要对其使用的外层数据作出任何假设。

Vue 组件的 API 来自三部分——prop、事件和插槽:

Prop允许外部环境传递数据给组件;

事件允许从组件内触发外部环境的副作用;

插槽允许外部环境将额外的内容组合在组件中。

使用v-bind和v-on的简写语法,模板的意图会更清楚且简洁:

:foo="baz"

:bar="qux"

@event-a="doThis"

@event-b="doThat"

>

Hello!

尽管有 prop 和事件,但是有时仍然需要在 JavaScript 中直接访问子组件。为此可以使用ref为子组件指定一个引用 ID。例如:

var parent = new Vue({ el: '#parent' })

// 访问子组件实例

var child = parent.$refs.profile

当ref和v-for一起使用时,获取到的引用会是一个数组,包含和循环数据源对应的子组件。

$refs只在组件渲染完成后才填充,并且它是非响应式的。它仅仅是一个直接操作子组件的应急方案——应当避免在模板或计算属性中使用$refs。

在大型应用中,我们可能需要将应用拆分为多个小模块,按需从服务器下载。为了进一步简化,Vue.js 允许将组件定义为一个工厂函数,异步地解析组件的定义。Vue.js 只在组件需要渲染时触发工厂函数,并且把结果缓存起来,用于后面的再次渲染。例如:

Vue.component('async-example', function (resolve, reject) {

setTimeout(function () {

// 将组件定义传入 resolve 回调函数

resolve({

template: '

I am async!
'

})

}, 1000)

})

工厂函数接收一个resolve回调,在收到从服务器下载的组件定义时调用。也可以调用reject(reason)指示加载失败。这里使用setTimeout只是为了演示,实际上如何获取组件完全由你决定。推荐配合webpack 的代码分割功能来使用:

Vue.component('async-webpack-example', function (resolve) {

// 这个特殊的 require 语法告诉 webpack

// 自动将编译后的代码分割成不同的块,

// 这些块将通过 Ajax 请求自动下载。

require(['./my-async-component'], resolve)

})

你可以在工厂函数中返回一个Promise,所以当使用 webpack 2 + ES2015 的语法时可以这样:

Vue.component(

'async-webpack-example',

// 该 `import` 函数返回一个 `Promise` 对象。

() => import('./my-async-component')

)

当使用局部注册时,也可以直接提供一个返回Promise的函数:

new Vue({

// ...

components: {

'my-component': () => import('./my-async-component')

}

})

如果你是Browserify用户,可能就无法使用异步组件了,它的作者已经表明Browserify 将“永远不会支持异步加载”。Browserify 社区发现了一些解决方法,可能会有助于已存在的复杂应用。对于其他场景,我们推荐使用 webpack,因为它对异步加载进行了内置、全面的支持。

2.3.0 新增

自 2.3.0 起,异步组件的工厂函数也可以返回一个如下的对象:

const AsyncComp = () => ({

// 需要加载的组件。应当是一个 Promise

component: import('./MyComp.vue'),

// 加载中应当渲染的组件

loading: LoadingComp,

// 出错时渲染的组件

error: ErrorComp,

// 渲染加载中组件前的等待时间。默认:200ms。

delay: 200,

// 最长等待时间。超出此时间则渲染错误组件。默认:Infinity

timeout: 3000

})

注意,当一个异步组件被作为vue-router的路由组件使用时,这些高级选项都是无效的,因为在路由切换前就会提前加载所需要的异步组件。另外,如果你要在路由组件中使用上述写法,需要使用vue-router2.4.0 以上的版本。

当注册组件 (或者 prop) 时,可以使用 kebab-case (短横线分隔命名)、camelCase (驼峰式命名) 或 PascalCase (单词首字母大写命名)。

// 在组件定义中

components: {

// 使用 kebab-case 注册

'kebab-cased-component': { /* ... */ },

// 使用 camelCase 注册

'camelCasedComponent': { /* ... */ },

// 使用 PascalCase 注册

'PascalCasedComponent': { /* ... */ }

}

在 HTML 模板中,请使用 kebab-case:

当使用字符串模式时,可以不受 HTML 大小写不敏感的限制。这意味实际上在模板中,你可以使用下面的方式来引用你的组件:

kebab-case

camelCase 或 kebab-case (如果组件已经被定义为 camelCase)

kebab-case、camelCase 或 PascalCase (如果组件已经被定义为 PascalCase)

components: {

'kebab-cased-component': { /* ... */ },

camelCasedComponent: { /* ... */ },

PascalCasedComponent: { /* ... */ }

}

这意味着 PascalCase 是最通用的声明约定而 kebab-case 是最通用的使用约定。

如果组件未经slot元素传入内容,你甚至可以在组件名后使用/使其自闭合:

当然,这只在字符串模板中有效。因为自闭的自定义元素是无效的 HTML,浏览器原生的解析器也无法识别它。

组件在它的模板内可以递归地调用自己。不过,只有当它有name选项时才可以这么做:

name: 'unique-name-of-my-component'

当你利用Vue.component全局注册了一个组件,全局的 ID 会被自动设置为组件的name。

Vue.component('unique-name-of-my-component', {

// ...

})

如果稍有不慎,递归组件可能导致死循环:

name: 'stack-overflow',

template: '

上面组件会导致一个“max stack size exceeded”错误,所以要确保递归调用有终止条件 (比如递归调用时使用v-if并最终解析为false)。

假设你正在构建一个文件目录树,像在 Finder 或资源管理器中。你可能有一个tree-folder组件:

{{ folder.name }}

以及一个tree-folder-contents组件:

  • {{ child.name }}

当你仔细看时,会发现在渲染树上这两个组件同时为对方的父节点和子节点——这是矛盾的!当使用Vue.component将这两个组件注册为全局组件的时候,框架会自动为你解决这个矛盾。如果你已经是这样做的,就跳过下面这段吧。

然而,如果你使用诸如 webpack 或者 Browserify 之类的模块化管理工具来 require/import 组件的话,就会报错了:

Failed to mount component: template or render function not defined.

为了解释为什么会报错,简单的将上面两个组件称为 A 和 B。模块系统看到它需要 A,但是首先 A 需要 B,但是 B 需要 A,而 A 需要 B,循环往复。因为不知道到底应该先解析哪个,所以将会陷入无限循环。要解决这个问题,我们需要在其中一个组件中告诉模块化管理系统:“A 虽然最后会用到 B,但是不需要优先导入 B”。

在我们的例子中,可以选择让tree-folder组件中来做这件事。我们知道引起矛盾的子组件是tree-folder-contents,所以我们要等到beforeCreate生命周期钩子中才去注册它:

beforeCreate: function () {

this.$options.components.TreeFolderContents = require('./tree-folder-contents.vue').default

}

问题解决了!

如果子组件有inline-template特性,组件将把它的内容当作它的模板,而不是把它当作分发内容。这让模板编写起来更灵活。

这些将作为组件自身的模板。

而非父组件透传进来的内容。

但是inline-template让模板的作用域难以理解。使用template选项在组件内定义模板或者在.vue文件中使用template元素才是最佳实践。

另一种定义模板的方式是在 JavaScript 标签里使用text/x-template类型,并且指定一个 id。例如:

Hello hello hello

Vue.component('hello-world', {

template: '#hello-world-template'

})

这在有很多大模板的演示应用或者特别小的应用中可能有用,其它场合应该避免使用,因为这将模板和组件的其它定义分离了。

尽管在 Vue 中渲染 HTML 很快,不过当组件中包含大量静态内容时,可以考虑使用v-once将渲染结果缓存起来,就像这样:

Vue.component('terms-of-service', {

template: '\

\

Terms of Service

\

...很多静态内容...\

\

'

})

×

打赏

如果本教程对您帮助很大,请随意打赏。您的支持,将鼓励我们提供更好的教程!

« 上一节← 键盘方向键翻页 →下一节 »

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值