學習心得,
組件(四),
直接上代碼了
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>組件四雜項</title>
<script src="../js/vue.js"></script>
</head>
<body>
<!--雜項-->
<!--編寫可復用組件-->
<!--在編寫組件時,最好考慮好以后是否要進行復用。-->
<!--一次性組件間有緊密的耦合沒關系,但是可復用組件應當定義一個清晰的公開接口,同時也不要對其使用的外層數據作出任何假設。-->
<!--Vue 組件的 API 來自三部分——prop、事件和插槽:-->
<!--Prop 允許外部環境傳遞數據給組件;-->
<!--事件允許從組件內觸發外部環境的副作用;-->
<!--插槽允許外部環境將額外的內容組合在組件中。-->
<!--使用 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
//當 ref 和 v-for 一起使用時,獲取到的引用會是一個數組,包含和循環數據源對應的子組件。
//$refs 只在組件渲染完成后才填充,并且它是非響應式的。
// 它僅僅是一個直接操作子組件的應急方案——應當避免在模板或計算屬性中使用 $refs。
</script>
<div id="app-1">
<async-example>
</async-example>
</div>
<hr>
<script>
// 異步組件
//
// 在大型應用中,我們可能需要將應用拆分為多個小模塊,按需從服務器下載。
//為了進一步簡化,Vue.js 允許將組件定義為一個工廠函數,異步地解析組件的定義。
//Vue.js 只在組件需要渲染時觸發工廠函數,并且把結果緩存起來,用于后面的再次渲染。
//例如:
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>
//工廠函數接收一個 resolve 回調,在收到從服務器下載的組件定義時調用。
//也可以調用 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'
// });
// 你可以在工廠函數中返回一個 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,因為它對異步加載進行了內置、全面的支持。
</script>
<script>
// 高級異步組件
//
// 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-router 2.4.0 以上的版本。
</script>
<script>
// 組件命名約定
//
// 當注冊組件 (或者 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>
// 當使用字符串模式時,可以不受 HTML 大小寫不敏感的限制。
// 這意味實際上在模板中,你可以使用下面的方式來引用你的組件:
//kebab-case
// camelCase 或 kebab-case (如果組件已經被定義為 camelCase)
// kebab-case、camelCase 或 PascalCase (如果組件已經被定義為 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 是最通用的使用約定。
//如果組件未經 slot 元素傳入內容,你甚至可以在組件名后使用 / 使其自閉合:
// <my-component/>
// 當然,這只在字符串模板中有效。因為自閉的自定義元素是無效的 HTML,瀏覽器原生的解析器也無法識別它。
</script>
<script>
// 遞歸組件
//
// 組件在它的模板內可以遞歸地調用自己。不過,只有當它有 name 選項時才可以這么做:
//name: 'unique-name-of-my-component'
// 當你利用 Vue.component 全局注冊了一個組件,全局的 ID 會被自動設置為組件的 name。
//Vue.component('unique-name-of-my-component', {
// // ...
//})
// 如果稍有不慎,遞歸組件可能導致死循環:
//name: 'stack-overflow',
// template: '<div><stack-overflow></stack-overflow></div>'
// 上面組件會導致一個“max stack size exceeded”錯誤,所以要確保遞歸調用有終止條件 (比如遞歸調用時使用 v-if 并最終解析為 false)。
</script>
<script>
// 組件間的循環引用
//
// 假設你正在構建一個文件目錄樹,像在 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>
// 當你仔細看時,會發現在渲染樹上這兩個組件同時為對方的父節點和子節點——這是矛盾的!
// 當使用 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')
//}
// 問題解決了!
</script>
<script>
// 內聯模板
//
// 如果子組件有 inline-template 特性,組件將把它的內容當作它的模板,而不是把它當作分發內容。這讓模板編寫起來更靈活。
// <my-component inline-template>
// <div>
// <p>這些將作為組件自身的模板。</p>
// <p>而非父組件透傳進來的內容。</p>
// </div>
// </my-component>
// 但是 inline-template 讓模板的作用域難以理解。使用 template 選項在組件內定義模板或者在 .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'
})
// 這在有很多大模板的演示應用或者特別小的應用中可能有用,其它場合應該避免使用,因為這將模板和組件的其它定義分離了。
</script>
<script>
// 對低開銷的靜態組件使用 v-once
//
// 盡管在 Vue 中渲染 HTML 很快,不過當組件中包含大量靜態內容時,可以考慮使用 v-once 將渲染結果緩存起來,就像這樣:
//Vue.component('terms-of-service', {
// template: '\
// <div v-once>\
// <h1>Terms of Service</h1>\
// ...很多靜態內容...\
// </div>\
// '
//})
</script>
</body>
</html>