[TOC]
>[success] # 從數字漸變組件談第三方JS庫使用
我們接下來要封裝一個 **CountTo組件** ,這里使用到了一個第三方庫 [**CountUp.js**](http://inorganik.github.io/countUp.js/) ,它的作用主要用來做 **數字動畫** ,設置一個 **最終值**,我們的 **數字** 會有一個 **漸變的效果的動畫** ,我們將會 **把這個JS庫封裝成一個Vue組件** , 通過這次 **封裝** 來學習一下 **Vue組件封裝的一些要點** 。
>[success] ## 組件封裝
1. **父組件**
首先在 **路由列表** 的 **路由對象** 中添加新創建的 **count-to** 頁面配置路由,**這個頁面名字跟上面的組件名重名,但是無關系,這個文件只是用來展示的頁面,不是組件,注意一下就好**
**src/router/router.js**
~~~
export default [
{
path: '/count-to',
name: '/count_to',
component: () => import('@/views/count-to')
}
]
~~~
然后在 **src/views/count-to.vue** 頁面 **引入count-to組件**
**src/views/count-to.vue**
~~~
<template>
<div>
<!-- endValue在子組件中是駝峰命名,但是父組件中使用時建議用end-value形式 -->
<count-to ref="countTo" :end-value="endValue" @on-animation-end="handleEnd">
<span slot="left">總金額:</span>
<span slot="right">元</span>
</count-to>
<button @click="getNumber">獲取數值</button>
<button @click="up">更新值</button>
</div>
</template>
<script>
import CountTo from '@/components/count-to'
export default {
name: 'count_to',
components: {
CountTo
},
data(){
return{
endValue: 100
}
},
methods: {
// 獲取子組件值方法
getNumber(){
this.$refs.countTo.getCount()
},
// 更新值方法
up(){
this.endValue += Math.random() * 100 // Math.random()會生成一個0-1的隨機數,乘以100就是0-100的隨機數
},
// 子傳父的監聽事件
handleEnd(endValue){
console.log(endValue)
}
}
}
</script>
~~~
引入時 **如果組件是駝峰式的命名** ,在 **template標簽** 中使用時 **建議使用小寫單詞字母中劃線區分** ,例如: **CountTo 在使用時寫成這樣 \<count-to />** 。上面也可以使用 **雙標簽 :\<count-to>\</count-to>** 一般沒有使用到組件中定義的 **插槽** ,還是建議使用 **單標簽** 。
2. **子組件**
首先我們在 **src/components** 文件夾中創建 **count-to** 文件夾,跟 **count-to組件有關的文件都放在這個文件夾中** 。 接下來創建一個 **count-to.vue** 文件,該文件 **作為組件文件**
**src/components/count-to/count-to.vue**
~~~
<template>
<div>
<slot name="left"></slot>
<span ref="number" :class="countClass" :id="eleId"></span>
<slot name="right"></slot>
</div>
</template>
<script>
import CountUp from 'countup'
export default {
name: 'CountTo',
computed: {
// 用_uid來達到id唯一
eleId(){
return `count_up_${ this._uid }`
},
// 支持傳入class樣式
countClass(){
return [
'count-to-number',
this.className
]
}
},
data(){
return{
counter: {} // CountUp實例化對象
}
},
props: {
/**
* @description 起始值
*/
startValue: {
type: Number,
default: 0
},
/**
* @description 最終值
*/
endValue: {
type: Number,
required: true
},
/**
* @description 小數點后保留幾位小數
*/
decimals: {
type: Number,
default: 0
},
/**
* @description 動畫延遲開始時間
*/
delay: {
type: Number,
default: 0
},
/**
* @description 漸變時長
*/
duration: {
type: Number,
default: 1
},
/**
* @description 是否使用變速效果
*/
useEasing: {
type: Boolean,
default: false
},
/**
* @description 是否使用變速效果
*/
useGrouping: {
type: Boolean,
default: true
},
/**
* @description 分組符號
*/
separator: {
type: String,
default: ','
},
/**
* @description 整數和小數分隔符號
*/
decimal: {
type: String,
default: '.'
},
/**
* @description 動態添加類名
*/
className: {
type: String,
default: ''
}
},
methods: {
getCount(){
// id或者refs來獲取dom元素的值
return this.$refs.number.innerText
},
emitEndEvent(){
setTimeout(() => {
this.$nextTick(() => {
this.$emit('on-animation-end', Number(this.getCount()))
})
}, this.duration * 1000 + 5)
}
},
watch:{
// endValue發生變化重新觸發組件更新方法
endValue(newVal, oldVal){
this.counter.update(newVal)
this.emitEndEvent()
}
},
mounted(){
this.$nextTick(() => {
// 實例化CountUp對象
this.counter = new CountUp(this.eleId, this.startValue, this.endValue, this.decimals, this.duration, {
useEasing: this.useEasing,
useGrouping: this.useGrouping,
separator: this.separator,
decimal: this.decimal
})
// 執行動畫開始
setTimeout(() => {
this.counter.start()
this.emitEndEvent()
}, this.delay)
})
}
}
</script>
<style lang="scss" scoped>
@import './count-to.scss'
</style>
~~~
再創建一個 **index.js** ,在 **index.js** 中引入 **count-to.vue組件** 并 **導出(export default)** ,這么做就可以在引入組件的地方直接這么引入:**import CountTo from '@/components/count-to'** ,這樣就是相當于引用了 **src/components/count-to** 文件夾中的 **index.js** 文件。
**src/components/count-to/index.js**
~~~
import CountTo from './count-to.vue'
export default CountTo
~~~
然后可以 **單獨創建一個只供該組件使用的.scss或者.less** 樣式文件
**count-to.scss**
~~~
.count-to-number{
color: palevioletred;
}
~~~
**.scss或者.less** 有 **2種引入方式**:
1. 直接在 **script標簽** 最上面寫: **import'./count-to.scss'**
2. 在 **style標簽** 最上面寫: **@import './count-to.scss'**
3. 或者直接 **在style標簽中直接寫樣式**
>[success] ## 組件中使用id值
上面 **組件中使用到了id值** ,我們都知道 **id是唯一的** ,**如果這個組件在多個頁面、多次使用** ,此時 **組件中創建的實例** 就會有問題 ,所以說我們 **每個組件都應該有自己的id,不跟其他組件發生沖突**,我們可以使用 **Vue** 提供的 **this._uid** 用來拼接 ,每個組件中 **_uid** 都不相同,這樣就可以保證是 **全局唯一的** 。
>[success] ## 組件中獲取DOM
在 **Vue** 中獲取 **DOM** 有 **2種方法** ,一種是使用 **id** 來獲取 **DOM** ,另外一種是使用 **ref** 的形式來獲取 **DOM** 。
**場景** :有時候我們的 **組件** ,有一些 **內部方法** , 可以 **供外部使用** ,這時候就需要 **父組件通過ref調用子組件方法** , 在上面 **封裝組件** 的代碼中,**父組件** 中通過 **點擊button** 執行 **getNumber** 方法中的 **this.$refs.countTo.getCount()** 來通過 **ref** 的方式 **調用子組件方法** , **子組件** 中使用 **ref** 來**取值并且返回值** ,具體實現看上面 **封裝組件的代碼** 。
>[success] ## 封裝組件技巧
1. **創建組件** 時可以在 **components文件夾** 中 **創建一個文件夾儲存組件相關文件**
2. **創建組件時** 可以創建一個對應 **index.js** 來達到 **引入時直接簡寫的方式引入組件**
3. **引入第三方庫,npm安裝成功** 后,可以在 **package.json** 中的 **dependencies對象** 中看到安裝的 **第三方庫名稱** , **如果看到自己安裝的第三方庫名稱即代表安裝成功**
4. **組件中使用id值解決辦法,看上面文章**
5. 把 **組件內動態的值都抽離出去,用props傳入進來** ,規定 **是否必傳,以及默認值**
6. **Vue組件插槽**
6.1. **匿名插槽用法**
**子組件**
~~~
<template>
<div>
<slot></slot>
<span :class="countClass" :id="eleId"></span>
</div>
</template>
~~~
**父組件**
~~~
<template>
<div>
<count-to :end-value="10">
<span>總金額:</span>
</count-to>
</div>
</template>
~~~
這樣寫 **總金額:** 三個字就會在組件的 **slot標簽** 位置出現
6.2. **具名插槽**
.
**子組件**
~~~
<template>
<div>
<slot name="left"></slot>
<span :class="countClass" :id="eleId"></span>
<slot name="right"></slot>
</div>
</template>
~~~
**父組件**
~~~
<template>
<div>
<!-- endValue在子組件中是駝峰命名,但是父組件中使用時建議用end-value形式 -->
<count-to :end-value="10">
<span slot="left">總金額:</span>
<span slot="right">元</span>
</count-to>
</div>
</template>
~~~
**具名插槽**父子組件都定義好指定的 **name** ,這樣 **插入的內容就會放入到指定 name 的位置**
7. **組件中獲取DOM解決辦法,看上面文章**
8. **props** 中的 **default** 的值如果是 **Boolean、Number、String** 這 **3種類型** 就直接 **值是什么可以寫成什么** , **如果默認值是Object或者Array或者Function** 就需要寫成 **函數并且返回return值**
- vue 26課
- Vue-cli3.0項目搭建
- Vue-ui 創建cli3.0項目
- Vue-ui 界面詳解
- 項目目錄詳解
- public文件夾
- favicon.ico
- index.html
- src文件夾
- api文件夾
- assets文件夾
- components文件夾
- config文件夾
- directive文件夾
- lib文件夾
- mock文件夾
- mock簡明文檔
- router文件夾
- store文件夾
- views文件夾
- App.vue
- main.js
- .browserslistrc
- .editorconfig
- .eslintrc.js
- .gitignore
- babel.config.js
- package-lock.json
- package.json
- postcss.config.js
- README.en.md
- README.md
- vue.config.js
- Vue Router
- 路由詳解(一)----基礎篇
- 路由詳解(二)----進階篇
- Vuex
- Bus
- Vuex-基礎-state&getter
- Vuex-基礎-mutation&action/module
- Vuex-進階
- Ajax請求
- 解決跨域問題
- 封裝axios
- Mock.js模擬Ajax響應
- 組件封裝
- 從數字漸變組件談第三方JS庫使用
- 從SplitPane組件談Vue中如何【操作】DOM
- 渲染函數和JSX快速掌握
- 遞歸組件的使用
- 登陸/登出以及JWT認證
- 響應式布局
- 可收縮多級菜單的實現
- vue雜項
- vue遞歸組件
- vue-cli3.0多環境打包配置
- Vue+Canvas實現圖片剪切
- vue3系統入門與項目實戰
- Vue語法初探
- 初學編寫 HelloWorld 和 Counter
- 編寫字符串反轉和內容隱藏功能
- 編寫TodoList功能了解循環與雙向綁定
- 組件概念初探,對 TodoList 進行組件代碼拆分
- Vue基礎語法
- Vue 中應用和組件的基礎概念
- 理解 Vue 中的生命周期函數
- 常用模版語法講解
- 數據,方法,計算屬性和偵聽器
- 樣式綁定語法
- 條件渲染
- 列表循環渲染
- 事件綁定
- 表單中雙向綁定指令的使用
- 探索組件的理念
- 組件的定義及復用性,局部組件和全局組件
- 組件間傳值及傳值校驗
- 單向數據流的理解
- Non-Props 屬性是什么
- 父子組件間如何通過事件進行通信
- 組件間雙向綁定高級內容
- 使用匿名插槽和具名插槽解決組件內容傳遞問題
- 作用域插槽
- 動態組件和異步組件
- 基礎語法知識點查缺補漏
- Vue 中的動畫
- 使用 Vue 實現基礎的 CSS 過渡與動畫效果
- 使用 transition 標簽實現單元素組件的過渡和動畫效果
- 組件和元素切換動畫的實現
- 列表動畫
- 狀態動畫
- Vue 中的高級語法
- Mixin 混入的基礎語法
- 開發實現 Vue 中的自定義指令
- Teleport 傳送門功能
- 更加底層的 render 函數
- 插件的定義和使用
- 數據校驗插件開發實例
- Composition API
- Setup 函數的使用
- ref,reactive 響應式引用的用法和原理
- toRef 以及 context 參數
- 使用 Composition API 開發TodoList
- computed方法生成計算屬性
- watch 和 watchEffect 的使用和差異性
- 生命周期函數的新寫法
- Provide,Inject,模版 Ref 的用法
- Vue 項目開發配套工具講解
- VueCLI 的使用和單文件組件
- 使用單文件組件編寫 TodoList
- Vue-Router 路由的理解和使用
- VueX 的語法詳解
- CompositionAPI 中如何使用 VueX
- 使用 axios 發送ajax 請求
- Vue3.0(正式版) + TS
- 你好 Typescript: 進入類型的世界
- 什么是 Typescript
- 為什么要學習 Typescript
- 安裝 Typescript
- 原始數據類型和 Any 類型
- 數組和元組
- Interface- 接口初探
- 函數
- 類型推論 聯合類型和 類型斷言
- class - 類 初次見面
- 類和接口 - 完美搭檔
- 枚舉(Enum)
- 泛型(Generics) 第一部分
- 泛型(Generics) 第二部分 - 約束泛型
- 泛型第三部分 - 泛型在類和接口中的使用
- 類型別名,字面量 和 交叉類型
- 聲明文件
- 內置類型
- 總結