<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                # 組件 ## 使用組件 ### 注冊 之前說過,我們可以用 `Vue.extend()` 創建一個組件構造器: ``` var MyComponent = Vue.extend({ // 選項... }) ``` 要把這個構造器用作組件,需要用 `Vue.component(tag, constructor)` **注冊** : ``` // 全局注冊組件,tag 為 my-component Vue.component('my-component', MyComponent) ``` 對于自定義標簽名字,Vue.js 不強制要求遵循 [W3C 規則](http://www.w3.org/TR/custom-elements/#concepts)(小寫,并且包含一個短杠),盡管遵循這個規則比較好。 在注冊之后,組件便可以用在父實例的模塊中,以自定義元素 `<my-component>` 的形式使用。要確保在初始化根實例**之前**注冊了組件: ``` <div id="example"> <my-component></my-component> </div> ``` ``` // 定義 var MyComponent = Vue.extend({ template: '<div>A custom component!</div>' }) // 注冊 Vue.component('my-component', MyComponent) // 創建根實例 new Vue({ el: '#example' }) ``` 渲染為: ``` <div id="example"> <div>A custom component!</div> </div> ``` ![](https://box.kancloud.cn/2016-01-03_5688e1a875767.png) 注意組件的模板**替換**了自定義元素,自定義元素的作用只是作為一個**掛載點**。這可以用實例選項 `replace` 改變。 ### 局部注冊 不需要全局注冊每個組件。可以讓組件只能用在其它組件內,用實例選項 `components` 注冊: ``` var Child = Vue.extend({ /* ... */ }) var Parent = Vue.extend({ template: '...', components: { // <my-component> 只能用在父組件模板內 'my-component': Child } }) ``` 這種封裝也適用于其它資源,如指令、過濾器和過渡。 ### 注冊語法糖 為了讓事件更簡單,可以直接傳入選項對象而不是構造器給 `Vue.component()` 和 `component` 選項。Vue.js 在背后自動調用 `Vue.extend()`: ``` // 在一個步驟中擴展與注冊 Vue.component('my-component', { template: '<div>A custom component!</div>' }) // 局部注冊也可以這么做 var Parent = Vue.extend({ components: { 'my-component': { template: '<div>A custom component!</div>' } } }) ``` ### 組件選項問題 傳入 Vue 構造器的多數選項也可以用在 `Vue.extend()` 中,不過有兩個特例: `data` and `el`。試想如果我們簡單地把一個對象作為 `data` 選項傳給 `Vue.extend()`: ``` var data = { a: 1 } var MyComponent = Vue.extend({ data: data }) ``` 這么做的問題是 `MyComponent` 所有的實例將共享同一個 `data` 對象!這基本不是我們想要的,因此我們應當使用一個函數作為 `data` 選項,函數返回一個新對象: ``` var MyComponent = Vue.extend({ data: function () { return { a: 1 } } }) ``` 同理,`el` 選項用在 `Vue.extend()` 中時也須是一個函數。 ### `is` 特性 一些 HTML 元素,如 `<table>`,限制什么元素可以放在它里面。自定義元素不在白名單上,將被放在元素的外面,因而渲染不正確。這時應當使用 `is` 特性,指示它是一個自定義元素: ``` <table> <tr is="my-component"></tr> </table> ``` ## Props ### 使用 Props 傳遞數據 **組件實例的作用域是孤立的**。這意味著不能并且不應該在子組件的模板內直接引用父組件的數據。可以使用 **props** 把數據傳給子組件。 “prop” 是組件數據的一個字段,期望從父組件傳下來。子組件需要顯式地用 [`props` 選項](/api/#props) 聲明 props: ``` Vue.component('child', { // 聲明 props props: ['msg'], // prop 可以用在模板內 // 可以用 `this.msg` 設置 template: '<span>{{ msg }}</span>' }) ``` 然后向它傳入一個普通字符串: ``` <child msg="hello!"></child> ``` **結果:** ![](https://box.kancloud.cn/2016-01-03_5688e1a883ea1.png) ### camelCase vs. kebab-case HTML 特性不區分大小寫。名字形式為 camelCase 的 prop 用作特性時,需要轉為 kebab-case(短橫線隔開): ``` Vue.component('child', { // camelCase in JavaScript props: ['myMessage'], template: '<span>{{ myMessage }}</span>' }) ``` ``` <!-- kebab-case in HTML --> <child my-message="hello!"></child> ``` ### 動態 Props 類似于綁定一個普通的特性到一個表達式,也可以用 `v-bind` 綁定動態 Props 到父組件的數據。每當父組件的數據變化時,也會傳導給子組件: ``` <div> <input v-model="parentMsg"> <br> <child v-bind:my-message="parentMsg"></child> </div> ``` 使用 `v-bind` 的縮寫語法通常更簡單: ``` <child :my-message="parentMsg"></child> ``` **結果:** ![](https://box.kancloud.cn/2016-01-03_5688e1a890130.png) ### 字面量語法 vs. 動態語法 初學者常犯的一個錯誤是使用字面量語法傳遞數值: ``` <!-- 傳遞了一個字符串 "1" --> <comp some-prop="1"></comp> ``` 因為它是一個字面 prop,它的值以字符串 `"1"` 而不是以實際的數字傳下去。如果想傳遞一個實際的 JavaScript 數字,需要使用動態語法,從而讓它的值被當作 JavaScript 表達式計算: ``` <!-- 傳遞實際的數字 --> <comp :some-prop="1"></comp> ``` ### Prop 綁定類型 prop 默認是**單向**綁定:當父組件的屬性變化時,將傳導給子組件,但是反過來不會。這是為了防止子組件無意修改了父組件的狀態——這會讓應用的數據流難以理解。不過,也可以使用 `.sync` 或 `.once` **綁定修飾符**顯式地強制雙向或單次綁定: 比較語法: ``` <!-- 默認為單向綁定 --> <child :msg="parentMsg"></child> <!-- 雙向綁定 --> <child :msg.sync="parentMsg"></child> <!-- 單次綁定 --> <child :msg.once="parentMsg"></child> ``` 雙向綁定會把子組件的 `msg` 屬性同步回父組件的 `parentMsg` 屬性。單次綁定在建立之后不會同步之后的變化。 注意如果 prop 是一個對象或數組,是按引用傳遞。在子組件內修改它**會**影響父組件的狀態,不管是使用哪種綁定類型。 ### Prop 驗證 組件可以為 props 指定驗證要求。當組件給其他人使用時這很有用,因為這些驗證要求構成了組件的 API,確保其他人正確地使用組件。此時 props 的值是一個對象,包含驗證要求: ``` Vue.component('example', { props: { // 基礎類型檢測 (`null` 意思是任何類型都可以) propA: Number, // 必需且是字符串 propB: { type: String, required: true }, // 數字,有默認值 propC: { type: Number, default: 100 }, // 對象/數組的默認值應當由一個函數返回 propD: { type: Object, default: function () { return { msg: 'hello' } } }, // 指定這個 prop 為雙向綁定 // 如果綁定類型不對將拋出一條警告 propE: { twoWay: true }, // 自定義驗證函數 propF: { validator: function (value) { return value > 10 } }, // 轉換函數(1.0.12 新增) // 在設置值之前轉換值 propG: { coerce: function (val) { return val + '' // 將值轉換為字符串 } } } }) ``` `type` 可以是下面原生構造器: * String * Number * Boolean * Function * Object * Array `type` 也可以是一個自定義構造器,使用 `instanceof` 檢測。 當 prop 驗證失敗了,Vue 將拒絕在子組件上設置此值,如果使用的是開發版本會拋出一條警告。 ## 父子組件通信 ### 父鏈 子組件可以用 `this.$parent` 訪問它的父組件。根實例的后代可以用 `this.$root` 訪問它。父組件有一個數組 `this.$children`,包含它所有的子元素。 盡管可以訪問父鏈上任意的實例,不過子組件應當避免直接依賴父組件的數據,應當顯式地使用 props 傳遞數據。另外,在子組件中修改父組件的狀態是非常糟糕的做法,因為: 1. 這讓父組件與子組件緊密地耦合; 2. 只看父組件,很難理解父組件的狀態。因為它可能被任意子組件修改!理想情況下,只有組件自己能修改它的狀態。 ### 自定義事件 Vue 實例實現了一個自定義事件接口,用于在組件樹中通信。這個事件系統獨立于原生 DOM 事件,做法也不同。 每個 Vue 實例都是一個事件觸發器: * 使用 `$on()` 監聽事件; * 使用 `$emit()` 在它上面觸發事件; * 使用 `$dispatch()` 派發事件,事件沿著父鏈冒泡; * 使用 `$broadcast()` 廣播事件,事件向下傳導給所有的后代。 不同于 DOM 事件,Vue 事件在冒泡過程中第一次觸發回調之后自動停止冒泡,除非回調明確返回 `true`。 簡單例子: ``` <!-- 子組件模板 --> <template id="child-template"> <input v-model="msg"> <button v-on:click="notify">Dispatch Event</button> </template> <!-- 父組件模板 --> <div id="events-example"> <p>Messages: {{ messages &#124; json }}</p> <child></child> </div> ``` ``` // 注冊子組件 // 將當前消息派發出去 Vue.component('child', { template: '#child-template', data: function () { return { msg: 'hello' } }, methods: { notify: function () { if (this.msg.trim()) { this.$dispatch('child-msg', this.msg) this.msg = '' } } } }) // 啟動父組件 // 將收到消息時將事件推入一個數組 var parent = new Vue({ el: '#events-example', data: { messages: [] }, // 在創建實例時 `events` 選項簡單地調用 `$on` events: { 'child-msg': function (msg) { // 事件回調內的 `this` 自動綁定到注冊它的實例上 this.messages.push(msg) } } }) ``` ![](https://box.kancloud.cn/2016-01-03_5688e1a89c262.png) ### 使用 v-on 綁定自定義事件 上例非常好,不過看著父組件的代碼, `"child-msg"` 事件來自哪里不直觀。如果我們在模板中子組件用到的地方聲明事件處理器會更好。為了做到這點,子組件可以用 `v-on` 監聽自定義事件: ``` <child v-on:child-msg="handleIt"></child> ``` 這讓事情非常清晰:當子組件觸發了 `"child-msg"` 事件,父組件的 `handleIt` 方法將被調用。所有影響父組件狀態的代碼放到父組件的 `handleIt` 方法中;子組件只關注觸發事件。 ### 子組件索引 盡管有 props 和 events,但是有時仍然需要在 JavaScript 中直接訪問子組件。為此可以使用 `v-ref` 為子組件指定一個索引 ID。例如: ``` <div id="parent"> <user-profile v-ref:profile></user-profile> </div> ``` ``` var parent = new Vue({ el: '#parent' }) // 訪問子組件 var child = parent.$refs.profile ``` `v-ref` 和 `v-for` 一起用時,ref 是一個數組或對象,包含相應的子組件。 ## 使用 Slot 分發內容 在使用組件時,常常要像這樣組合它們: ``` <app> <app-header></app-header> <app-footer></app-footer> </app> ``` 注意兩點: 1. `<app>` 組件不知道它的掛載點會有什么內容,掛載點的內容是由 `<app>` 的父組件決定的。 2. `<app>` 組件很可能有它自己的模板。 為了讓組件可以組合,我們需要一種方式來混合父組件的內容與子組件自己的模板。這個處理稱為**內容分發**(或 “transclusion”,如果你熟悉 Angular)。Vue.js 實現了一個內容分發 API,參照了當前 [Web 組件規范草稿](https://github.com/w3c/webcomponents/blob/gh-pages/proposals/Slots-Proposal.md),使用特殊的 `<slot>` 元素作為原始內容的插槽。 ### 編譯作用域 在深入內容分發 API 之前,我們先明確內容的編譯作用域。假定模板為: ``` <child> {{ msg }} </child> ``` `msg` 應該綁定到父組件的數據,還是綁定到子組件的數據?答案是父組件。組件作用域簡單地說是: > 父組件模板的內容在父組件作用域內編譯;子組件模板的內容在子組件作用域內編譯 一個常見錯誤是試圖在父組件模板內將一個指令綁定到子組件屬性/方法: ``` <!-- 無效 --> <child v-show="someChildProperty"></child> ``` 假定 `someChildProperty` 是子組件的屬性,上例不能如預期工作。父組件模板不知道子組件的狀態。 如果要綁定子組件內的指令到一個組件的根節點,應當在它的模板內這么做: ``` Vue.component('child-component', { // 有效,因為是在正確的作用域內 template: '<div v-show="someChildProperty">Child</div>', data: function () { return { someChildProperty: true } } }) ``` 類似地,分發內容是在父組件作用域內編譯。 ### 單個 Slot 父組件的內容將被**拋棄**,除非子組件模板包含 `<slot>`。如果只有一個沒有特性的 slot,整個內容將被插到它所在的地方,替換 slot。 `<slot>` 標簽的內容視為**回退內容**。回退內容在子組件的作用域內編譯,只有當宿主元素為空并且沒有內容供插入時顯示。 假定 `my-component` 組件有下面模板: ``` <div> <h1>This is my component!</h1> <slot> This will only be displayed if there is no content to be distributed. </slot> </div> ``` 父組件模板: ``` <my-component> <p>This is some original content</p> <p>This is some more original content</p> </my-component> ``` 渲染結果: ``` <div> <h1>This is my component!</h1> <p>This is some original content</p> <p>This is some more original content</p> </div> ``` ### 命名 Slot `<slot>` 元素有一個特殊特性 `name`,用于配置如何分發內容。多個 slot 可以有不同的名字。命名 slot 將匹配有對應 `slot` 特性的內容片斷。 也可以有一個未命名 slot,它是**默認 slot**,作為找不到匹配內容的回退插槽。如果沒有默認的 slot,不匹配內容將被拋棄。 例如,假定我們有一個 `multi-insertion` 組件,它的模板為: ``` <div> <slot name="one"></slot> <slot></slot> <slot name="two"></slot> </div> ``` 父組件模板: ``` <multi-insertion> <p slot="one">One</p> <p slot="two">Two</p> <p>Default A</p> </multi-insertion> ``` 渲染結果為: ``` <div> <p slot="one">One</p> <p>Default A</p> <p slot="two">Two</p> </div> ``` 在組合組件時,內容分發 API 是非常有用的機制。 ## 動態組件 多個組件可以使用同一個掛載點,然后動態地在它們之間切換。使用保留的 `<component>` 元素,動態地綁定到它的 `is` 特性: ``` new Vue({ el: 'body', data: { currentView: 'home' }, components: { home: { /* ... */ }, posts: { /* ... */ }, archive: { /* ... */ } } }) ``` ``` <component :is="currentView"> <!-- 組件在 vm.currentview 變化時改變 --> </component> ``` 如果把切換出去的組件保留在內存中,可以保留它的狀態或避免重新渲染。為此可以添加一個 `keep-alive` 指令參數: ``` <component :is="currentView" keep-alive> <!-- 非活動組件將被緩存 --> </component> ``` ### `activate` 鉤子 在切換組件時,切入組件在切入前可能需要進行一些異步操作。為了控制組件切換時長,給切入組件添加 `activate` 鉤子: ``` Vue.component('activate-example', { activate: function (done) { var self = this loadDataAsync(function (data) { self.someData = data done() }) } }) ``` 注意 `activate` 鉤子只作用于動態組件切換或靜態組件初始化渲染的過程中,不作用于使用實例方法手工插入的過程中。 ### `transition-mode` `transition-mode` 特性用于指定兩個動態組件之間如何過渡。 在默認情況下,進入與離開平滑地過渡。這個特性可以指定另外兩種模式: * `in-out`:新組件先過渡進入,等它的過渡完成之后當前組件過渡出去。 * `out-in`:當前組件先過渡出去,等它的過渡完成之后新組件過渡進入。 **示例:** ``` <!-- 先淡出再淡入 --> <component :is="view" transition="fade" transition-mode="out-in"> </component> ``` ``` .fade-transition { transition: opacity .3s ease; } .fade-enter, .fade-leave { opacity: 0; } ``` ![](https://box.kancloud.cn/2016-01-03_5688e1a8ad10b.png) ## 雜項 ### 組件和 v-for 自定義組件可以像普通元素一樣直接使用 `v-for`: ``` <my-component v-for="item in items"></my-component> ``` 但是,不能傳遞數據給組件,因為組件的作用域是孤立的。為了傳遞數據給組件,應當使用 props: ``` <my-component v-for="item in items" :item="item" :index="$index"> </my-component> ``` 不自動把 `item` 注入組件的原因是這會導致組件跟當前 `v-for` 緊密耦合。顯式聲明數據來自哪里可以讓組件復用在其它地方。 ### 編寫可復用組件 在編寫組件時,記住是否要復用組件有好處。一次性組件跟其它組件緊密耦合沒關系,但是可復用組件應當定義一個清晰的公開接口。 Vue.js 組件 API 來自三部分——prop,事件和 slot: * **prop** 允許外部環境傳遞數據給組件; * **事件** 允許組件觸發外部環境的 action; * **slot** 允許外部環境插入內容到組件的視圖結構內。 使用 `v-bind` 和 `v-on` 的簡寫語法,模板的縮進清楚且簡潔: ``` <my-component :foo="baz" :bar="qux" @event-a="doThis" @event-b="doThat"> <!-- content --> <img slot="icon" src="..."> <p slot="main-text">Hello!</p> </my-component> ``` ### 異步組件 在大型應用中,我們可能需要將應用拆分為小塊,只在需要時才從服務器下載。為了讓事情更簡單,Vue.js 允許將組件定義為一個工廠函數,動態地解析組件的定義。Vue.js 只在組件需要渲染時觸發工廠函數,并且把結果緩存起來,用于后面的再次渲染。例如: ``` Vue.component('async-example', function (resolve, reject) { setTimeout(function () { resolve({ template: '<div>I am async!</div>' }) }, 1000) }) ``` 工廠函數接收一個 `resolve` 回調,在收到從服務器下載的組件定義時調用。也可以調用 `reject(reason)` 指示加載失敗。這里 `setTimeout` 只是為了演示。怎么獲取組件完全由你決定。推薦配合使用 [Webpack 的代碼分割功能](http://webpack.github.io/docs/code-splitting.html): ``` Vue.component('async-webpack-example', function (resolve) { // 這個特殊的 require 語法告訴 webpack // 自動將編譯后的代碼分割成不同的塊, // 這些塊將通過 ajax 請求自動下載。 require(['./my-async-component'], resolve) }) ``` ### 資源命名約定 一些資源,如組件和指令,是以 HTML 特性或 HTML 自定義元素的形式出現在模板中。因為 HTML 特性的名字和標簽的名字**不區分大小寫**,所以資源的名字通常需使用 kebab-case 而不是 camelCase 的形式,這不大方便。 Vue.js 支持資源的名字使用 camelCase 或 PascalCase 的形式,并且在模板中自動將它們轉為 kebab-case(類似于 prop 的命名約定): ``` // 在組件定義中 components: { // 使用 camelCase 形式注冊 myComponent: { /*... */ } } ``` ``` <!-- 在模板中使用 kebab-case 形式 --> <my-component></my-component> ``` [ES6 對象字面量縮寫](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#New_notations_in_ECMAScript_6) 也沒問題: ``` // PascalCase import TextBox from './components/text-box'; import DropdownMenu from './components/dropdown-menu'; export default { components: { // 在模板中寫作 <text-box> 和 <dropdown-menu> TextBox, DropdownMenu } } ``` ### 遞歸組件 組件在它的模板內可以遞歸地調用自己,不過,只有當它有 `name` 選項時才可以: ``` var StackOverflow = Vue.extend({ name: 'stack-overflow', template: '<div>' + // 遞歸地調用它自己 '<stack-overflow></stack-overflow>' + '</div>' }) ``` 上面組件會導致一個錯誤 “max stack size exceeded”,所以要確保遞歸調用有終止條件。當使用 `Vue.component()` 全局注冊一個組件時,組件 ID 自動設置為組件的 `name` 選項。 ### 片斷實例 在使用 `template` 選項時,模板的內容將替換實例的掛載元素。因而推薦模板的頂級元素始終是單個元素。 不這么寫模板: ``` <div>root node 1</div> <div>root node 2</div> ``` 推薦這么寫: ``` <div> I have a single root node! <div>node 1</div> <div>node 2</div> </div> ``` 下面幾種情況會讓實例變成一個**片斷實例**: 1. 模板包含多個頂級元素。 2. 模板只包含普通文本。 3. 模板只包含其它組件(其它組件可能是一個片段實例)。 4. 模板只包含一個元素指令,如 `<partial>` 或 vue-router 的 `<router-view>`。 5. 模板根節點有一個流程控制指令,如 `v-if` 或 `v-for`。 這些情況讓實例有未知數量的頂級元素,它將把它的 DOM 內容當作片斷。片斷實例仍然會正確地渲染內容。不過,它**沒有**一個根節點,它的 `$el` 指向一個錨節點,即一個空的文本節點(在開發模式下是一個注釋節點)。 但是更重要的是,**組件元素上的非流程控制指令,非 prop 特性和過渡將被忽略**,因為沒有根元素供綁定: ``` <!-- 不可以,因為沒有根元素 --> <example v-show="ok" transition="fade"></example> <!-- props 可以 --> <example :prop="someData"></example> <!-- 流程控制可以,但是不能有過渡 --> <example v-if="ok"></example> ``` 當然片斷實例有它的用處,不過通常給組件一個根節點比較好。它會保證組件元素上的指令和特性能正確地轉換,同時性能也稍微好些。 ### 內聯模板 如果子組件有 `inline-template` 特性,組件將把它的內容當作它的模板,而不是把它當作分發內容。這讓模板更靈活。 ``` <my-component inline-template> <p>These are compiled as the component's own template</p> <p>Not parent's transclusion content.</p> </my-component> ``` 但是 `inline-template` 讓模板的作用域難以理解,并且不能緩存模板編譯結果。最佳實踐是使用 `template` 選項在組件內定義模板。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看