學(xué)習(xí)心得,
組件(四),
直接上代碼了
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>組件四雜項</title>
<script src="../js/vue.js"></script>
</head>
<body>
<!--雜項-->
<!--編寫可復(fù)用組件-->
<!--在編寫組件時,最好考慮好以后是否要進行復(fù)用。-->
<!--一次性組件間有緊密的耦合沒關(guān)系吁峻,但是可復(fù)用組件應(yīng)當(dāng)定義一個清晰的公開接口,同時也不要對其使用的外層數(shù)據(jù)作出任何假設(shè)费尽。-->
<!--Vue 組件的 API 來自三部分——prop、事件和插槽:-->
<!--Prop 允許外部環(huán)境傳遞數(shù)據(jù)給組件羊始;-->
<!--事件允許從組件內(nèi)觸發(fā)外部環(huán)境的副作用旱幼;-->
<!--插槽允許外部環(huán)境將額外的內(nèi)容組合在組件中。-->
<!--使用 v-bind 和 v-on 的簡寫語法突委,模板的意圖會更清楚且簡潔:-->
<my-component
v-bind:foo = "baz"
v-bind:bar = "qux"
v-on:event-a = "doThis"
v-on:event-b = "doThat"
>
<img slot="icon" src="#" >
<p slot="main-text">Hello!</p>
</my-component>
<!--子組件引用-->
<!--盡管有 prop 和事件柏卤,但是有時仍然需要在 JavaScript 中直接訪問子組件。-->
<!--為此可以使用 ref 為子組件指定一個引用 ID匀油。例如:-->
<div id="parent">
<user-profile ref="profile"></user-profile>
</div>
<hr>
<script>
Vue.component('my-component',{
template:''
});
Vue.component('user-profile',{
template:'<p></p>'
});
var parent = new Vue({ el: '#parent'})
// 訪問子組件實例
var child = parent.$refs.profile
//當(dāng) ref 和 v-for 一起使用時闷旧,獲取到的引用會是一個數(shù)組,包含和循環(huán)數(shù)據(jù)源對應(yīng)的子組件钧唐。
//$refs 只在組件渲染完成后才填充,并且它是非響應(yīng)式的匠襟。
// 它僅僅是一個直接操作子組件的應(yīng)急方案——應(yīng)當(dāng)避免在模板或計算屬性中使用 $refs钝侠。
</script>
<div id="app-1">
<async-example>
</async-example>
</div>
<hr>
<script>
// 異步組件
//
// 在大型應(yīng)用中,我們可能需要將應(yīng)用拆分為多個小模塊酸舍,按需從服務(wù)器下載帅韧。
//為了進一步簡化,Vue.js 允許將組件定義為一個工廠函數(shù)啃勉,異步地解析組件的定義忽舟。
//Vue.js 只在組件需要渲染時觸發(fā)工廠函數(shù),并且把結(jié)果緩存起來,用于后面的再次渲染叮阅。
//例如:
Vue.component('async-example',function(resolve,reject){
setTimeout(function(){
//將組建定義傳入resolve
resolve({
template:'<div>I am async!</div>'
})
},1000)
});
var app1 = new Vue({
el:'#app-1'
});
</script>
<div id="app-2">
<async-webpack-example>
</async-webpack-example>
</div>
<hr>
<script>
//工廠函數(shù)接收一個 resolve 回調(diào)刁品,在收到從服務(wù)器下載的組件定義時調(diào)用。
//也可以調(diào)用 reject(reason) 指示加載失敗浩姥。
//這里使用 setTimeout 只是為了演示挑随,實際上如何獲取組件完全由你決定。
//推薦配合 webpack 的代碼分割功能 來使用:
// Vue.component(
// 'async-webpack-example',function(resolve){
// // 這個特殊的 require 語法告訴 webpack
// // 自動將編譯后的代碼分割成不同的塊勒叠,
// // 這些塊將通過 Ajax 請求自動下載兜挨。
// require(['./my-async-component'],resolve)
// });
// var app2 = new Vue({
// el:'#app-2'
// });
// 你可以在工廠函數(shù)中返回一個 Promise,所以當(dāng)使用 webpack 2 + ES2015 的語法時可以這樣:
//Vue.component(
// 'async-webpack-example',
// // 該 `import` 函數(shù)返回一個 `Promise` 對象眯分。
// () => import('./my-async-component')
// )
// 當(dāng)使用局部注冊時拌汇,也可以直接提供一個返回 Promise 的函數(shù):
//new Vue({
// // ...
// components: {
// 'my-component': () => import('./my-async-component')
//}
// })
// 如果你是 Browserify 用戶,可能就無法使用異步組件了弊决,
// 它的作者已經(jīng)表明 Browserify 將“永遠不會支持異步加載”噪舀。
// Browserify 社區(qū)發(fā)現(xiàn)了一些解決方法,可能會有助于已存在的復(fù)雜應(yīng)用丢氢。
// 對于其他場景傅联,我們推薦使用 webpack,因為它對異步加載進行了內(nèi)置疚察、全面的支持蒸走。
</script>
<script>
// 高級異步組件
//
// 2.3.0 新增
// 自 2.3.0 起,異步組件的工廠函數(shù)也可以返回一個如下的對象:
//const AsyncComp = () => ({
//// 需要加載的組件貌嫡。應(yīng)當(dāng)是一個 Promise
// component: import('./MyComp.vue'),
//// 加載中應(yīng)當(dāng)渲染的組件
// loading: LoadingComp,
//// 出錯時渲染的組件
// error: ErrorComp,
//// 渲染加載中組件前的等待時間比驻。默認:200ms。
// delay: 200,
//// 最長等待時間岛抄。超出此時間則渲染錯誤組件别惦。默認:Infinity
// timeout: 3000
// })
// 注意,當(dāng)一個異步組件被作為 vue-router 的路由組件使用時夫椭,
//這些高級選項都是無效的掸掸,因為在路由切換前就會提前加載所需要的異步組件。
//另外蹭秋,如果你要在路由組件中使用上述寫法扰付,需要使用 vue-router 2.4.0 以上的版本。
</script>
<script>
// 組件命名約定
//
// 當(dāng)注冊組件 (或者 prop) 時仁讨,可以使用 kebab-case (短橫線分隔命名)羽莺、camelCase (駝峰式命名) 或 PascalCase (單詞首字母大寫命名)。
// // 在組件定義中
// components: {
// // 使用 kebab-case 注冊
// 'kebab-cased-component': { /* ... */ },
// // 使用 camelCase 注冊
// 'camelCasedComponent': { /* ... */ },
// // 使用 PascalCase 注冊
// 'PascalCasedComponent': { /* ... */ }
// }
// 在 HTML 模板中洞豁,請使用 kebab-case:
// <!-- 在 HTML 模板中始終使用 kebab-case -->
// <kebab-cased-component></kebab-cased-component>
// <camel-cased-component></camel-cased-component>
// <pascal-cased-component></pascal-cased-component>
// 當(dāng)使用字符串模式時盐固,可以不受 HTML 大小寫不敏感的限制荒给。
// 這意味實際上在模板中,你可以使用下面的方式來引用你的組件:
//kebab-case
// camelCase 或 kebab-case (如果組件已經(jīng)被定義為 camelCase)
// kebab-case刁卜、camelCase 或 PascalCase (如果組件已經(jīng)被定義為 PascalCase)
// components: {
// 'kebab-cased-component': { /* ... */ },
// camelCasedComponent: { /* ... */ },
// PascalCasedComponent: { /* ... */ }
// }
// <kebab-cased-component></kebab-cased-component>
// <camel-cased-component></camel-cased-component>
// <camelCasedComponent></camelCasedComponent>
// <pascal-cased-component></pascal-cased-component>
// <pascalCasedComponent></pascalCasedComponent>
// <PascalCasedComponent></PascalCasedComponent>
// 這意味著 PascalCase 是最通用的聲明約定而 kebab-case 是最通用的使用約定志电。
//如果組件未經(jīng) slot 元素傳入內(nèi)容,你甚至可以在組件名后使用 / 使其自閉合:
// <my-component/>
// 當(dāng)然长酗,這只在字符串模板中有效溪北。因為自閉的自定義元素是無效的 HTML,瀏覽器原生的解析器也無法識別它夺脾。
</script>
<script>
// 遞歸組件
//
// 組件在它的模板內(nèi)可以遞歸地調(diào)用自己之拨。不過,只有當(dāng)它有 name 選項時才可以這么做:
//name: 'unique-name-of-my-component'
// 當(dāng)你利用 Vue.component 全局注冊了一個組件咧叭,全局的 ID 會被自動設(shè)置為組件的 name蚀乔。
//Vue.component('unique-name-of-my-component', {
// // ...
//})
// 如果稍有不慎,遞歸組件可能導(dǎo)致死循環(huán):
//name: 'stack-overflow',
// template: '<div><stack-overflow></stack-overflow></div>'
// 上面組件會導(dǎo)致一個“max stack size exceeded”錯誤菲茬,所以要確保遞歸調(diào)用有終止條件 (比如遞歸調(diào)用時使用 v-if 并最終解析為 false)吉挣。
</script>
<script>
// 組件間的循環(huán)引用
//
// 假設(shè)你正在構(gòu)建一個文件目錄樹,像在 Finder 或資源管理器中婉弹。你可能有一個 tree-folder 組件:
// <p>
// <span>{{ folder.name }}</span>
// <tree-folder-contents :children="folder.children"/>
// </p>
// 以及一個 tree-folder-contents 組件:
// <ul>
// <li v-for="child in children">
// <tree-folder v-if="child.children" :folder="child"/>
// <span v-else>{{ child.name }}</span>
// </li>
// </ul>
// 當(dāng)你仔細看時睬魂,會發(fā)現(xiàn)在渲染樹上這兩個組件同時為對方的父節(jié)點和子節(jié)點——這是矛盾的!
// 當(dāng)使用 Vue.component 將這兩個組件注冊為全局組件的時候镀赌,框架會自動為你解決這個矛盾氯哮。如果你已經(jīng)是這樣做的焰雕,就跳過下面這段吧拜轨。
//然而膝宁,如果你使用諸如 webpack 或者 Browserify 之類的模塊化管理工具來 require/import 組件的話涤久,就會報錯了:
//Failed to mount component: template or render function not defined.
// 為了解釋為什么會報錯,簡單的將上面兩個組件稱為 A 和 B鹅很。模塊系統(tǒng)看到它需要 A醉者,但是首先 A 需要 B啼器,但是 B 需要 A玛追,而 A 需要 B税课,循環(huán)往復(fù)。
// 因為不知道到底應(yīng)該先解析哪個痊剖,所以將會陷入無限循環(huán)伯复。要解決這個問題,我們需要在其中一個組件中告訴模塊化管理系統(tǒng):“A 雖然最后會用到 B邢笙,但是不需要優(yōu)先導(dǎo)入 B”。
//在我們的例子中侍匙,可以選擇讓 tree-folder 組件中來做這件事氮惯。
// 我們知道引起矛盾的子組件是 tree-folder-contents叮雳,所以我們要等到 beforeCreate 生命周期鉤子中才去注冊它:
//beforeCreate: function () {
// this.$options.components.TreeFolderContents = require('./tree-folder-contents.vue')
//}
// 問題解決了!
</script>
<script>
// 內(nèi)聯(lián)模板
//
// 如果子組件有 inline-template 特性妇汗,組件將把它的內(nèi)容當(dāng)作它的模板帘不,而不是把它當(dāng)作分發(fā)內(nèi)容。這讓模板編寫起來更靈活杨箭。
// <my-component inline-template>
// <div>
// <p>這些將作為組件自身的模板寞焙。</p>
// <p>而非父組件透傳進來的內(nèi)容。</p>
// </div>
// </my-component>
// 但是 inline-template 讓模板的作用域難以理解互婿。使用 template 選項在組件內(nèi)定義模板或者在 .vue 文件中使用 template 元素才是最佳實踐捣郊。
</script>
<!--X-Template-->
<!--另一種定義模板的方式是在 JavaScript 標簽里使用 text/x-template 類型,并且指定一個 id慈参。例如:-->
<script type="text/x-template" id="hello-world-template">
<p>Hello hello hello</p>
</script>
<script>
Vue.component('hello-world', {
template: '#hello-world-template'
})
// 這在有很多大模板的演示應(yīng)用或者特別小的應(yīng)用中可能有用呛牲,其它場合應(yīng)該避免使用,因為這將模板和組件的其它定義分離了驮配。
</script>
<script>
// 對低開銷的靜態(tài)組件使用 v-once
//
// 盡管在 Vue 中渲染 HTML 很快娘扩,不過當(dāng)組件中包含大量靜態(tài)內(nèi)容時,可以考慮使用 v-once 將渲染結(jié)果緩存起來壮锻,就像這樣:
//Vue.component('terms-of-service', {
// template: '\
// <div v-once>\
// <h1>Terms of Service</h1>\
// ...很多靜態(tài)內(nèi)容...\
// </div>\
// '
//})
</script>
</body>
</html>