vue的使用總結(3)
1、動態(tài)組件
代碼示例:

component 直接綁定全局組件的組件名:
<body>
<div id="dynamic-component-demo" class="demo">
<button v-for="tab in tabs" v-bind:key="tab" v-bind:class="['tab-button', { active: currentTab === tab }]"
v-on:click="currentTab = tab">
{{ tab }}
</button>
<component v-bind:is="currentTabComponent" class="tab"></component>
</div>
<script>
Vue.component("tab-home", {
template: "<div>Home component</div>"
});
Vue.component("tab-posts", {
template: "<div>Posts component</div>"
});
Vue.component("tab-archive", {
template: "<div>Archive component</div>"
});
new Vue({
el: "#dynamic-component-demo",
data: {
currentTab: "Home",
tabs: ["Home", "Posts", "Archive"]
},
computed: {
currentTabComponent: function () {
return "tab-" + this.currentTab.toLowerCase();
}
}
});
</script>
</body>
或者 component 也可以綁定組件選項對象,而不是組件名:
<body>
<div id="dynamic-component-demo" class="demo">
<button v-for="tab in tabs" v-bind:key="tab.name"
v-bind:class="['tab-button', { active: currentTab.name === tab.name }]" v-on:click="currentTab = tab">
{{ tab.name }}
</button>
<component v-bind:is="currentTab.component" class="tab"></component>
</div>
<script>
var tabs = [
{
name: "Home",
component: {
template: "<div>Home component</div>"
}
},
{
name: "Posts",
component: {
template: "<div>Posts component</div>"
}
},
{
name: "Archive",
component: {
template: "<div>Archive component</div>"
}
}
];
new Vue({
el: "#dynamic-component-demo",
data: {
tabs: tabs,
currentTab: tabs[0]
}
});
</script>
</body>
1.1、緩存動態(tài)組件(keep-alive)
在使用動態(tài)組件時,組件切換后再切換回來,該組件之前的狀態(tài)并不會被保存起來。在每次切換渲染不同組件的時候,Vue 都會重新創(chuàng)建一個該組價的新實例。
有時候在切換后,我們希望重新切換回該組件時,該組件仍能維持切換之前的狀態(tài)。比如一開始渲染的是 A 組件,并且在 A 組件中選中了某個表單,此時從 A 組件切換到 B 組件,然后再切換回 A 組件時,我們希望 A 組件的表單仍保持切換之前的狀態(tài),而不是恢復到初始狀態(tài)。要想保持組件的狀態(tài),我們可以使用 <keep-alive> 標簽來包裹動態(tài)組件,實現(xiàn)對組件的緩存:
<!-- 失活的組件將會被緩存!-->
<keep-alive>
<component v-bind:is="currentTabComponent"></component>
</keep-alive>
注意 <keep-alive> 要求被切換到的組件都有自己的名字,不論是通過組件的 name 選項還是局部/全局注冊。
2、異步組件(按需加載)
官方文檔:https://cn.vuejs.org/v2/guide/components-dynamic-async.html
在單頁應用中,如果沒有應用懶加載,運用webpack打包后的文件將會異常的大(此時通常只有一個JS文件),造成進入首頁時,需要加載的內(nèi)容過大,延時過長,不利于用戶體驗,而運用懶加載則可以將頁面進行劃分,需要的時候加載頁面,可以有效的分擔首頁所承擔的加載壓力,減少首頁加載用時。
Vue 允許我們以一個工廠函數(shù)的方式來定義組件,這個工廠函數(shù)會異步解析你的組件定義。并且 vue 只有在這個組件需要被渲染的時候才會觸發(fā)該工廠函數(shù),且會把結果緩存起來供未來重渲染。
Vue.component('async-example', function (resolve, reject) {
setTimeout(function () {
// 向 `resolve` 回調(diào)傳遞組件定義
resolve({
template: '<div>I am async!</div>'
})
}, 1000)
})
//將異步組件和 webpack 的 code-splitting 功能一起配合使用:
Vue.component('async-webpack-example', function (resolve) {
// 這個特殊的 `require` 語法將會告訴 webpack自動將你的構建代碼切割成多個包,這些包會通過 Ajax 請求加載
require(['./my-async-component'], resolve)
})
//把 webpack 2 和 ES2015 語法加在一起,我們可以這樣使用動態(tài)導入:
Vue.component(
'async-webpack-example',
// 這個動態(tài)導入會返回一個 `Promise` 對象。
() => import('./my-async-component')
)
//當使用局部注冊的時候,你也可以直接提供一個返回 Promise 的函數(shù):
new Vue({
// ...
components: {
'my-component': () => import('./my-async-component')
}
})
在 vue 中實現(xiàn)按需加載非常簡單,只要將平常的導入組件的方式改一下就行。
//一般的導入方式 import componentA from './componentA.js'
異步懶加載導入方式有多種:
//使用() => import() const componentA = () => import ('/componentA.js') //使用resolve => require([’’], resolve) const componentA = resolve => require([’./componentA.js’], resolve) //webpack提供的require.ensure require.ensure([’./componentA.js’], () => { resolve(require(’./componentA.js’)) }, ‘tab0’) //除了導入方式不同以外,其他地方的使用,比如注冊等,都是一樣的 components: { componentA }
或者也可以使用Webpack 的內(nèi)置語句: import(*).then(),在有需要時才調(diào)用該方法進行導入。比如在某個 tabs 的 change 事件觸發(fā)時才加載某個組件:

import('./xxx/index.vue').then(item => {
Vue.component('my-comp', item.default)
})
//上面的路徑可以由字符串變量拼接,但不能只是一個字符串變量構成組件的路徑。
//下面寫法正常:
let url = demo/demo.vue
import(`./modules/${url}`).then(item => {
Vue.component('my-comp', item.default)
})
//這種寫法會報錯:
let url = demo/demo.vue
import(url).then(item => {
Vue.component('my-comp', item.default)
})
下面將演示使用resolve => require([’’], resolve) 的方法來實現(xiàn)懶加載,跟使用其他函數(shù)來實現(xiàn)的代碼是一樣的,只是導入時的函數(shù)不一樣而已。
2.1、使用resolve => require(['xxx.vue'], resolve)實現(xiàn)按需加載
創(chuàng)建一個項目,組件目錄:Helloworld 組件是首頁,在 HelloWord 中引入加載 item01 組件,在 item01 組件中引入加載 item02 組件。
1、使用普通的引入方式:
//HelloWord.vue 中的代碼 <template> <div class="hello"> <h3>this is HelloWorld</h3> <template v-if="showItem"> <Item01></Item01> </template> <button @click="toggleItem">切換</button> </div> </template> import Item01 from './Item01.vue' export default { name: 'HelloWorld', data () { return { showItem: false } }, components: { Item01 }, methods: { toggleItem () { this.showItem = !this.showItem; } } } //Item01.vue 中的代碼 <template> <div> <h3>this is item01</h3> <Item02></Item02> </div> </template> import Item02 from './Item02.vue'
運行項目:

點擊切換按鈕,顯示 item01、item02 組件:

可以看到,加載的 JS 文件并沒有任何變化,即沒有新加載 JS 文件。可以看到 item01和 item02 組件都被打包到了 app.js 文件中,所有的組件 JS 代碼都被打包到了一個文件中,這會導致該文件過大,影響加載速度。
2、使用按需加載的方式:
//HelloWord.vue 中的代碼
<template>
<div class="hello">
<h3>this is HelloWorld</h3>
<template v-if="showItem">
<Item01></Item01>
</template>
<button @click="toggleItem">切換</button>
</div>
</template>
const Item01 = resolve => require(['./Item01'],resolve) //按需加載 item01 組件
export default {
name: 'HelloWorld',
data () {
return {
showItem: false
}
},
components: {
Item01
},
methods: {
toggleItem () {
this.showItem = !this.showItem;
}
}
}
//Item01.vue 中的代碼
<template>
<div>
<h3>this is item01</h3>
<Item02></Item02>
</div>
</template>
const Item02 = resolve => require(['./Item02'],resolve) //按需加載 item02 組件
運行項目:

點擊切換按鈕,顯示 item01、item02 組件:

可以看到,一開始沒有顯示 item01、item02 組件時,瀏覽器只加載了 app.js 文件。當點擊按鈕顯示 item01、item02 組件后,開始依次加載 item01、item02打包后的 JS 文件。
通過按需加載,webpack 會幫我們將需要按需加載的組件跟一開始需要加載的 JS 文件分離開,當需要加載的時候再加載。按需加載只是改變掉加載組件的方式而已,webpack 會自動幫我們實現(xiàn)按需加載。
利用此特性,我們便能做很多針對前端的優(yōu)化。比如:將頁面核心功能(音、視頻播放、文章、商品等等)打包成一個核心模塊,通過框架優(yōu)先加載。其他的一些周邊功能打包后,通過服務器異步加載,從而解決業(yè)務需求越來越多導致的系統(tǒng)難維護、訪問慢問題。
參考:https://segmentfault.com/a/1190000012138052,
其它實現(xiàn)按需加載的引入方法可以參考:https://blog.csdn.net/scarlett_dream/article/details/83756392、https://segmentfault.com/a/1190000011519350
3、使用渲染函數(shù)render定義組件
一般我們都會使用模板 template 來定義組件,但有時候使用 template 可能比較繁瑣,這時候我們可以使用 render 函數(shù)來定義組件。
示例:
使用 template 定義組件:
//注冊組件 Vue.component('anchored-heading', { template: ` <div> <h1 v-if="level === 1"> <slot></slot> </h1> <h2 v-else-if="level === 2"> <slot></slot> </h2> <h3 v-else-if="level === 3"> <slot></slot> </h3> </div> `, props: { level: { type: Number, required: true } } })
可以看到,上面的判斷條件比較多,使用 template 時代碼比較冗余。
此時我們可以使用 render 函數(shù)來定義組件:
Vue.component('anchored-heading', {
render: function (createElement) {
return createElement(
'h' + this.level, // 標簽名稱
this.$slots.default // 子節(jié)點數(shù)組。默認插槽
)
},
props: {
level: {
type: Number,
required: true
}
}
})
createElement 返回的不是一個實際的 DOM 元素,它返回的是虛擬節(jié)點。更準確的名字可能是 createNodeDescription,因為它所包含的信息會告訴 Vue 頁面上需要渲染什么樣的節(jié)點,包括及其子節(jié)點的描述信息。我們把這樣的節(jié)點描述為“虛擬節(jié)點 (virtual node)”,也常簡寫它為“VNode”。“虛擬 DOM”是我們對由 Vue 組件樹建立起來的整個 VNode 樹的稱呼。
3.1、creatElement() 函數(shù)
createElement() 函數(shù)接受三個參數(shù),第一個參數(shù)指定了最外圍的元素,第二個參數(shù)可選,用來指定模板中的選項對象,第三個參數(shù)指定最外圍元素的子元素。
Vue.component('anchored-heading', {
render: function (createElement) {
return createElement( // @returns {VNode}
// {String | Object | Function}
// 一個 HTML 標簽名、組件選項對象,或者
// resolve 了上述任何一種的一個 async 函數(shù)。必填項。
'div',
// {Object}
// 一個與模板中 attribute 對應的數(shù)據(jù)對象。可選。
{},
// {String | Array}
// 子級虛擬節(jié)點 (VNodes),由 `createElement()` 構建而成,
// 也可以使用字符串來生成“文本虛擬節(jié)點”??蛇x。
[
'可以插入文字',
createElement('h1', '一則頭條'),
createElement(MyComponent, {
props: {
someProp: 'foobar'
}
})
]
)
}
})
數(shù)據(jù)對象中定義組件的一些選項:
{ // 與 `v-bind:class` 的 API 相同, // 接受一個字符串、對象或字符串和對象組成的數(shù)組 'class': { foo: true, bar: false },
// 與 `v-bind:style` 的 API 相同, // 接受一個字符串、對象,或?qū)ο蠼M成的數(shù)組 style: { color: 'red', fontSize: '14px' },
// 普通的 HTML attribute attrs: { id: 'foo' },
// 組件 prop props: { myProp: 'bar' },
// DOM property domProps: { innerHTML: 'baz' },
// 事件監(jiān)聽器在 `on` 內(nèi), // 但不再支持如 `v-on:keyup.enter` 這樣的修飾器。 // 需要在處理函數(shù)中手動檢查 keyCode。 on: { click: this.clickHandler },
// 僅用于組件,用于監(jiān)聽原生事件,而不是組件內(nèi)部使用 // `vm.$emit` 觸發(fā)的事件。 nativeOn: { click: this.nativeClickHandler },
// 自定義指令。注意,你無法對 `binding` 中的 `oldValue` // 賦值,因為 Vue 已經(jīng)自動為你進行了同步。 directives: [ { name: 'my-custom-directive', value: '2', expression: '1 + 1', arg: 'foo', modifiers: { bar: true } } ],
// 作用域插槽的格式為 // { name: props => VNode | Array<VNode> } scopedSlots: { default: props => createElement('span', props.text) },
// 如果組件是其它組件的子組件,需為插槽指定名稱 slot: 'name-of-slot',
// 其它特殊頂層 property key: 'myKey',
ref: 'myRef',
// 如果你在渲染函數(shù)中給多個元素都應用了相同的 ref 名, // 那么 `$refs.myRef` 會變成一個數(shù)組。 refInFor: true }
3.2、使用 JSX 語法
使用 render() 函數(shù)寫起來很復雜,我們可以使用 JSX 語法,這樣寫起來就比較簡單:
import AnchoredHeading from './AnchoredHeading.vue' new Vue({ el: '#demo', render: function (h) { return ( <AnchoredHeading level={1}> <span>Hello</span> world! </AnchoredHeading> ) } }) //上面對應的模板如下: <anchored-heading :level="1"> <span>Hello</span> world! </anchored-heading>
3.3、函數(shù)式組件
我們可以將組件標記為 functional,這意味它無狀態(tài) (沒有響應式數(shù)據(jù)),也沒有實例 (沒有 this 上下文)。
示例:
Vue.component('my-component', {
functional: true,
// Props 是可選的
props: {
// ...
},
// 為了彌補缺少的實例
// 提供第二個參數(shù)作為上下文
render: function (createElement, context) {
// ...
}
})
注意:在 2.3.0 之前的版本中,如果一個函數(shù)式組件想要接收 prop,則 props 選項是必須的。在 2.3.0 或以上的版本中,你可以省略 props 選項,所有組件上的 attribute 都會被自動隱式解析為 prop。
4、keep-alive組件
keep-alive是vue的內(nèi)置組件,能在組件切換過程中將狀態(tài)保留在內(nèi)存中,相當于緩存,防止DOM的重復渲染。
keep-alive有三個屬性:include(只有名字匹配的才會被緩存)、exclude(任何名字匹配的都不會被緩存)、max(最多可以緩存多少個組件)。
在路由router的中:相應組件下規(guī)定meta屬性,定義keep-alive:true
源碼實現(xiàn)方面:可以結合Vue組件實例加載順序講解,VNode -> 實例化 -> _updata -> 真實Node,在實例化的時候會判斷該組件是否被 keep-alive 保存過,是的話則直接拿其中的DOM進行渲染。
5、服務端器渲染和客戶端渲染
5.1、服務器端渲染(SSR,server side render)
服務器端渲染:DOM樹在服務端生成,然后返回給前端。實際上指的就是頁面內(nèi)容通過后端直接返回,相當于是前后端不分離,由后端直接返回頁面內(nèi)容。
實現(xiàn)方式:傳統(tǒng)的JSP、express+react、express+ejs、vue+nuxt。比如可以通過 express 啟動一個后端服務器,然后通過接口直接返回 html 內(nèi)容,示例可參考:https://cp_fe.gitee.io/course-advance/#/vue-advance/03ssr
服務器端渲染只是通過服務器返回了一串字符串,瀏覽器直接顯示該字符串而已,此時就會有一些問題,比如給元素綁定的事件不生效,vue一些特性不生效等問題,解決該問題的方法是將服務器端返回的字符串作為代碼在客戶端再重新執(zhí)行一遍。
從頭搭建這么一個服務端渲染的應用是相當復雜的,我們可以使用 Nuxt.js 框架來進行服務器端渲染的開發(fā)。nuxt.js 是一套使用vue框架開發(fā)應用的服務端渲染框架,提供了開箱即用的功能。
5.2、客戶端渲染(CSR,client side render)
客戶端渲染指的是后端只提供json格式的數(shù)據(jù),渲染成什么樣子由客戶端通過js控制,也就是平常使用的前后端分離框架。
5.3、服務器端渲染和客戶端渲染的對比
主要對比就是首屏時間、SEO優(yōu)化、開發(fā)效率。
服務端渲染的優(yōu)點:
- 首屏時間短。因為頁面由服務器端返回,瀏覽器壓力小,所以耗時少,速度快。
- 有利于SEO優(yōu)化,因為在后端有完整的html頁面,所以爬蟲更容易爬取信息。
服務端渲染的缺點:
- 不利于前后端分離,開發(fā)的效率降低了。
- 對html的解析,對前端來說加快了速度,但是加大了服務器的壓力。
客戶端渲染的優(yōu)點:
- 前后端分離,開發(fā)效率高。
- 用戶體驗更好,我們將網(wǎng)站做成SPA(單頁面應用)或者部分內(nèi)容做成SPA,當用戶點擊時,不會形成頻繁的跳轉(zhuǎn)。
客戶端渲染的缺點:
- 首屏時間長。前端響應速度慢,特別是首屏,這樣用戶是受不了的。
- 不利于SEO優(yōu)化,因為爬蟲不認識SPA,所以它只是記錄了一個頁面。

浙公網(wǎng)安備 33010602011771號