### 什么是Vue.js
+ Vue.js 是目前最火的一個前端框架,React是最流行的一個前端框架(React除了開發網站,還可以開發手機App, Vue語法也是可以用于進行手機App開發的,需要借助于Weex)
+ Vue.js 是前端的**主流框架之一**,和Angular.js、React.js 一起,并成為前端三大主流框架!
+ Vue.js 是一套構建用戶界面的框架,**只關注視圖層**,它不僅易于上手,還便于與第三方庫或既有項目整合。(Vue有配套的第三方類庫,可以整合起來做大型項目的開發)
+ 前端的主要工作?主要負責MVC中的V這一層;主要工作就是和界面打交道,來制作前端頁面效果;
## 為什么要學習流行框架
+ 企業為了提高開發效率:在企業中,時間就是效率,效率就是金錢;
? - 企業中,使用框架,能夠提高開發的效率;
+ 提高開發效率的發展歷程:原生JS -> Jquery之類的類庫 -> 前端模板引擎 -> Angular.js / Vue.js(能夠幫助我們減少不必要的DOM操作;提高渲染效率;雙向數據綁定的概念【通過框架提供的指令,我們前端程序員只需要關心數據的業務邏輯,不再關心DOM是如何渲染的了】)
+ 在Vue中,一個核心的概念,就是讓用戶不再操作DOM元素,解放了用戶的雙手,讓程序員可以更多的時間去關注業務邏輯;
+ 增強自己就業時候的競爭力
? - 人無我有,人有我優
? - 你平時不忙的時候,都在干嘛?
## 框架和庫的區別
+ 框架:是一套完整的解決方案;對項目的侵入性較大,項目如果需要更換框架,則需要重新架構整個項目。
? - node 中的 express;
+ 庫(插件):提供某一個小功能,對項目的侵入性較小,如果某個庫無法完成某些需求,可以很容易切換到其它庫實現需求。
? - 1. 從Jquery 切換到 Zepto
? - 2. 從 EJS 切換到 art-template
## Node(后端)中的 MVC 與 前端中的 MVVM 之間的區別
+ MVC 是后端的分層開發概念;
+ MVVM是前端視圖層的概念,主要關注于 視圖層分離,也就是說:MVVM把前端的視圖層,分為了 三部分 Model, View , VM ViewModel
+ 為什么有了MVC還要有MVVM
## Vue.js 基本代碼 和 MVVM 之間的對應關系
## Vue之 - `基本的代碼結構`和`插值表達式`、`v-cloak`
## Vue指令之`v-text`和`v-html`
## Vue指令之`v-bind`的三種用法
1. 直接使用指令`v-bind`
2. 使用簡化指令`:`
3. 在綁定的時候,拼接綁定內容:`:title="btnTitle + ', 這是追加的內容'"`
## Vue指令之`v-on`和`跑馬燈效果`
### 跑馬燈效果
1. HTML結構:
```
<div id="app">
? ? <p>{{info}}</p>
? ? <input type="button" value="開啟" v-on:click="go">
? ? <input type="button" value="停止" v-on:click="stop">
? </div>
```
2. Vue實例:
```
// 創建 Vue 實例,得到 ViewModel
? ? var vm = new Vue({
? ? ? el: '#app',
? ? ? data: {
? ? ? ? info: '猥瑣發育,別浪~!',
? ? ? ? intervalId: null
? ? ? },
? ? ? methods: {
? ? ? ? go() {
? ? ? ? ? // 如果當前有定時器在運行,則直接return
? ? ? ? ? if (this.intervalId != null) {
? ? ? ? ? ? return;
? ? ? ? ? }
? ? ? ? ? // 開始定時器
? ? ? ? ? this.intervalId = setInterval(() => {
? ? ? ? ? ? this.info = this.info.substring(1) + this.info.substring(0, 1);
? ? ? ? ? }, 500);
? ? ? ? },
? ? ? ? stop() {
? ? ? ? ? clearInterval(this.intervalId);
? ? ? ? }
? ? ? }
? ? });
```
## Vue指令之`v-on的縮寫`和`事件修飾符`
### 事件修飾符:
+ .stop? ? ? 阻止冒泡
+ .prevent? ? 阻止默認事件
+ .capture? ? 添加事件偵聽器時使用事件捕獲模式
+ .self? ? ? 只當事件在該元素本身(比如不是子元素)觸發時觸發回調
+ .once? ? ? 事件只觸發一次
## Vue指令之`v-model`和`雙向數據綁定`
## 簡易計算器案例
1. HTML 代碼結構
```
? <div id="app">
? ? <input type="text" v-model="n1">
? ? <select v-model="opt">
? ? ? <option value="0">+</option>
? ? ? <option value="1">-</option>
? ? ? <option value="2">*</option>
? ? ? <option value="3">÷</option>
? ? </select>
? ? <input type="text" v-model="n2">
? ? <input type="button" value="=" v-on:click="getResult">
? ? <input type="text" v-model="result">
? </div>
```
2. Vue實例代碼:
```
// 創建 Vue 實例,得到 ViewModel
? ? var vm = new Vue({
? ? ? el: '#app',
? ? ? data: {
? ? ? ? n1: 0,
? ? ? ? n2: 0,
? ? ? ? result: 0,
? ? ? ? opt: '0'
? ? ? },
? ? ? methods: {
? ? ? ? getResult() {
? ? ? ? ? switch (this.opt) {
? ? ? ? ? ? case '0':
? ? ? ? ? ? ? this.result = parseInt(this.n1) + parseInt(this.n2);
? ? ? ? ? ? ? break;
? ? ? ? ? ? case '1':
? ? ? ? ? ? ? this.result = parseInt(this.n1) - parseInt(this.n2);
? ? ? ? ? ? ? break;
? ? ? ? ? ? case '2':
? ? ? ? ? ? ? this.result = parseInt(this.n1) * parseInt(this.n2);
? ? ? ? ? ? ? break;
? ? ? ? ? ? case '3':
? ? ? ? ? ? ? this.result = parseInt(this.n1) / parseInt(this.n2);
? ? ? ? ? ? ? break;
? ? ? ? ? }
? ? ? ? }
? ? ? }
? ? });
```
## 在Vue中使用樣式
### 使用class樣式
1. 數組
```
<h1 :class="['red', 'thin']">這是一個邪惡的H1</h1>
```
2. 數組中使用三元表達式
```
<h1 :class="['red', 'thin', isactive?'active':'']">這是一個邪惡的H1</h1>
```
3. 數組中嵌套對象
```
<h1 :class="['red', 'thin', {'active': isactive}]">這是一個邪惡的H1</h1>
```
4. 直接使用對象
```
<h1 :class="{red:true, italic:true, active:true, thin:true}">這是一個邪惡的H1</h1>
```
### 使用內聯樣式
1. 直接在元素上通過 `:style` 的形式,書寫樣式對象
```
<h1 :style="{color: 'red', 'font-size': '40px'}">這是一個善良的H1</h1>
```
2. 將樣式對象,定義到 `data` 中,并直接引用到 `:style` 中
+ 在data上定義樣式:
```
data: {
? ? ? ? h1StyleObj: { color: 'red', 'font-size': '40px', 'font-weight': '200' }
}
```
+ 在元素中,通過屬性綁定的形式,將樣式對象應用到元素中:
```
<h1 :style="h1StyleObj">這是一個善良的H1</h1>
```
3. 在 `:style` 中通過數組,引用多個 `data` 上的樣式對象
+ 在data上定義樣式:
```
data: {
? ? ? ? h1StyleObj: { color: 'red', 'font-size': '40px', 'font-weight': '200' },
? ? ? ? h1StyleObj2: { fontStyle: 'italic' }
}
```
+ 在元素中,通過屬性綁定的形式,將樣式對象應用到元素中:
```
<h1 :style="[h1StyleObj, h1StyleObj2]">這是一個善良的H1</h1>
```
## Vue指令之`v-for`和`key`屬性
1. 迭代數組
```
<ul>
? <li v-for="(item, i) in list">索引:{{i}} --- 姓名:{{item.name}} --- 年齡:{{item.age}}</li>
</ul>
```
2. 迭代對象中的屬性
```
<!-- 循環遍歷對象身上的屬性 -->
? ? <div v-for="(val, key, i) in userInfo">{{val}} --- {{key}} --- {{i}}</div>
```
3. 迭代數字
```
<p v-for="i in 10">這是第 {{i}} 個P標簽</p>
```
> 2.2.0+ 的版本里,**當在組件中使用** v-for 時,key 現在是必須的。
當 Vue.js 用 v-for 正在更新已渲染過的元素列表時,它默認用 “**就地復用**” 策略。如果數據項的順序被改變,Vue將**不是移動 DOM 元素來匹配數據項的順序**, 而是**簡單復用此處每個元素**,并且確保它在特定索引下顯示已被渲染過的每個元素。
為了給 Vue 一個提示,**以便它能跟蹤每個節點的身份,從而重用和重新排序現有元素**,你需要為每項提供一個唯一 key 屬性。
## Vue指令之`v-if`和`v-show`
> 一般來說,v-if 有更高的切換消耗而 v-show 有更高的初始渲染消耗。因此,如果需要頻繁切換 v-show 較好,如果在運行時條件不大可能改變 v-if 較好。
## 品牌管理案例
### 添加新品牌
### 刪除品牌
### 根據條件篩選品牌
1. 1.x 版本中的filterBy指令,在2.x中已經被廢除:
[filterBy - 指令](https://v1-cn.vuejs.org/api/#filterBy)
```
<tr v-for="item in list | filterBy searchName in 'name'">
? <td>{{item.id}}</td>
? <td>{{item.name}}</td>
? <td>{{item.ctime}}</td>
? <td>
? ? <a href="#" @click.prevent="del(item.id)">刪除</a>
? </td>
</tr>
```
2. 在2.x版本中[手動實現篩選的方式](https://cn.vuejs.org/v2/guide/list.html#顯示過濾-排序結果):
+ 篩選框綁定到 VM 實例中的 `searchName` 屬性:
```
<hr> 輸入篩選名稱:
<input type="text" v-model="searchName">
```
+ 在使用 `v-for` 指令循環每一行數據的時候,不再直接 `item in list`,而是 `in` 一個 過濾的methods 方法,同時,把過濾條件`searchName`傳遞進去:
```
<tbody>
? ? ? <tr v-for="item in search(searchName)">
? ? ? ? <td>{{item.id}}</td>
? ? ? ? <td>{{item.name}}</td>
? ? ? ? <td>{{item.ctime}}</td>
? ? ? ? <td>
? ? ? ? ? <a href="#" @click.prevent="del(item.id)">刪除</a>
? ? ? ? </td>
? ? ? </tr>
? ? </tbody>
```
+ `search` 過濾方法中,使用 數組的 `filter` 方法進行過濾:
```
search(name) {
? return this.list.filter(x => {
? ? return x.name.indexOf(name) != -1;
? });
}
```
## Vue調試工具`vue-devtools`的安裝步驟和使用
[Vue.js devtools - 翻墻安裝方式 - 推薦](https://chrome.google.com/webstore/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpd?hl=zh-CN)
## 過濾器
概念:Vue.js 允許你自定義過濾器,**可被用作一些常見的文本格式化**。過濾器可以用在兩個地方:**mustache 插值和 v-bind 表達式**。過濾器應該被添加在 JavaScript 表達式的尾部,由“管道”符指示;
### 私有過濾器
1. HTML元素:
```
<td>{{item.ctime | dataFormat('yyyy-mm-dd')}}</td>
```
2. 私有 `filters` 定義方式:
```
filters: { // 私有局部過濾器,只能在 當前 VM 對象所控制的 View 區域進行使用
? ? dataFormat(input, pattern = "") { // 在參數列表中 通過 pattern="" 來指定形參默認值,防止報錯
? ? ? var dt = new Date(input);
? ? ? // 獲取年月日
? ? ? var y = dt.getFullYear();
? ? ? var m = (dt.getMonth() + 1).toString().padStart(2, '0');
? ? ? var d = dt.getDate().toString().padStart(2, '0');
? ? ? // 如果 傳遞進來的字符串類型,轉為小寫之后,等于 yyyy-mm-dd,那么就返回 年-月-日
? ? ? // 否則,就返回? 年-月-日 時:分:秒
? ? ? if (pattern.toLowerCase() === 'yyyy-mm-dd') {
? ? ? ? return `${y}-${m}-$xweae33`;
? ? ? } else {
? ? ? ? // 獲取時分秒
? ? ? ? var hh = dt.getHours().toString().padStart(2, '0');
? ? ? ? var mm = dt.getMinutes().toString().padStart(2, '0');
? ? ? ? var ss = dt.getSeconds().toString().padStart(2, '0');
? ? ? ? return `${y}-${m}-$xbaxuqg ${hh}:${mm}:${ss}`;
? ? ? }
? ? }
? }
```
> 使用ES6中的字符串新方法 String.prototype.padStart(maxLength, fillString='') 或 String.prototype.padEnd(maxLength, fillString='')來填充字符串;
### 全局過濾器
```
// 定義一個全局過濾器
Vue.filter('dataFormat', function (input, pattern = '') {
? var dt = new Date(input);
? // 獲取年月日
? var y = dt.getFullYear();
? var m = (dt.getMonth() + 1).toString().padStart(2, '0');
? var d = dt.getDate().toString().padStart(2, '0');
? // 如果 傳遞進來的字符串類型,轉為小寫之后,等于 yyyy-mm-dd,那么就返回 年-月-日
? // 否則,就返回? 年-月-日 時:分:秒
? if (pattern.toLowerCase() === 'yyyy-mm-dd') {
? ? return `${y}-${m}-$s4c14ha`;
? } else {
? ? // 獲取時分秒
? ? var hh = dt.getHours().toString().padStart(2, '0');
? ? var mm = dt.getMinutes().toString().padStart(2, '0');
? ? var ss = dt.getSeconds().toString().padStart(2, '0');
? ? return `${y}-${m}-$lsns6gc ${hh}:${mm}:${ss}`;
? }
});
```
> 注意:當有局部和全局兩個名稱相同的過濾器時候,會以就近原則進行調用,即:局部過濾器優先于全局過濾器被調用!
## 鍵盤修飾符以及自定義鍵盤修飾符
### 1.x中自定義鍵盤修飾符【了解即可】
```
Vue.directive('on').keyCodes.f2 = 113;
```
### [2.x中自定義鍵盤修飾符](https://cn.vuejs.org/v2/guide/events.html#鍵值修飾符)
1. 通過`Vue.config.keyCodes.名稱 = 按鍵值`來自定義案件修飾符的別名:
```
Vue.config.keyCodes.f2 = 113;
```
2. 使用自定義的按鍵修飾符:
```
<input type="text" v-model="name" @keyup.f2="add">
```
## [自定義指令](https://cn.vuejs.org/v2/guide/custom-directive.html)
1. 自定義全局和局部的 自定義指令:
```
? ? // 自定義全局指令 v-focus,為綁定的元素自動獲取焦點:
? ? Vue.directive('focus', {
? ? ? inserted: function (el) { // inserted 表示被綁定元素插入父節點時調用
? ? ? ? el.focus();
? ? ? }
? ? });
? ? // 自定義局部指令 v-color 和 v-font-weight,為綁定的元素設置指定的字體顏色 和 字體粗細:
? ? ? directives: {
? ? ? ? color: { // 為元素設置指定的字體顏色
? ? ? ? ? bind(el, binding) {
? ? ? ? ? ? el.style.color = binding.value;
? ? ? ? ? }
? ? ? ? },
? ? ? ? 'font-weight': function (el, binding2) { // 自定義指令的簡寫形式,等同于定義了 bind 和 update 兩個鉤子函數
? ? ? ? ? el.style.fontWeight = binding2.value;
? ? ? ? }
? ? ? }
```
2. 自定義指令的使用方式:
```
<input type="text" v-model="searchName" v-focus v-color="'red'" v-font-weight="900">
```
## Vue 1.x 中 自定義元素指令【已廢棄,了解即可】
```
Vue.elementDirective('red-color', {
? bind: function () {
? ? this.el.style.color = 'red';
? }
});
```
使用方式:
```
<red-color>1232</red-color>
```
## [vue實例的生命周期](https://cn.vuejs.org/v2/guide/instance.html#實例生命周期)
+ 什么是生命周期:從Vue實例創建、運行、到銷毀期間,總是伴隨著各種各樣的事件,這些事件,統稱為生命周期!
+ [生命周期鉤子](https://cn.vuejs.org/v2/api/#選項-生命周期鉤子):就是生命周期事件的別名而已;
+ 生命周期鉤子 = 生命周期函數 = 生命周期事件
+ 主要的生命周期函數分類:
- 創建期間的生命周期函數:
? + beforeCreate:實例剛在內存中被創建出來,此時,還沒有初始化好 data 和 methods 屬性
? + created:實例已經在內存中創建OK,此時 data 和 methods 已經創建OK,此時還沒有開始 編譯模板
? + beforeMount:此時已經完成了模板的編譯,但是還沒有掛載到頁面中
? + mounted:此時,已經將編譯好的模板,掛載到了頁面指定的容器中顯示
- 運行期間的生命周期函數:
+ beforeUpdate:狀態更新之前執行此函數, 此時 data 中的狀態值是最新的,但是界面上顯示的 數據還是舊的,因為此時還沒有開始重新渲染DOM節點
+ updated:實例更新完畢之后調用此函數,此時 data 中的狀態值 和 界面上顯示的數據,都已經完成了更新,界面已經被重新渲染好了!
- 銷毀期間的生命周期函數:
+ beforeDestroy:實例銷毀之前調用。在這一步,實例仍然完全可用。
+ destroyed:Vue 實例銷毀后調用。調用后,Vue 實例指示的所有東西都會解綁定,所有的事件監聽器會被移除,所有的子實例也會被銷毀。
## [vue-resource 實現 get, post, jsonp請求](https://github.com/pagekit/vue-resource)
除了 vue-resource 之外,還可以使用 `axios` 的第三方包實現實現數據的請求
1. 之前的學習中,如何發起數據請求?
2. 常見的數據請求類型?? get? post jsonp
3. 測試的URL請求資源地址:
+ get請求地址: http://vue.studyit.io/api/getlunbo
+ post請求地址:http://vue.studyit.io/api/post
+ jsonp請求地址:http://vue.studyit.io/api/jsonp
4. JSONP的實現原理
+ 由于瀏覽器的安全性限制,不允許AJAX訪問 協議不同、域名不同、端口號不同的 數據接口,瀏覽器認為這種訪問不安全;
+ 可以通過動態創建script標簽的形式,把script標簽的src屬性,指向數據接口的地址,因為script標簽不存在跨域限制,這種數據獲取方式,稱作JSONP(注意:根據JSONP的實現原理,知曉,JSONP只支持Get請求);
+ 具體實現過程:
- 先在客戶端定義一個回調方法,預定義對數據的操作;
- 再把這個回調方法的名稱,通過URL傳參的形式,提交到服務器的數據接口;
- 服務器數據接口組織好要發送給客戶端的數據,再拿著客戶端傳遞過來的回調方法名稱,拼接出一個調用這個方法的字符串,發送給客戶端去解析執行;
- 客戶端拿到服務器返回的字符串之后,當作Script腳本去解析執行,這樣就能夠拿到JSONP的數據了;
+ 帶大家通過 Node.js ,來手動實現一個JSONP的請求例子;
```
? ? const http = require('http');
? ? // 導入解析 URL 地址的核心模塊
? ? const urlModule = require('url');
? ? const server = http.createServer();
? ? // 監聽 服務器的 request 請求事件,處理每個請求
? ? server.on('request', (req, res) => {
? ? ? const url = req.url;
? ? ? // 解析客戶端請求的URL地址
? ? ? var info = urlModule.parse(url, true);
? ? ? // 如果請求的 URL 地址是 /getjsonp ,則表示要獲取JSONP類型的數據
? ? ? if (info.pathname === '/getjsonp') {
? ? ? ? // 獲取客戶端指定的回調函數的名稱
? ? ? ? var cbName = info.query.callback;
? ? ? ? // 手動拼接要返回給客戶端的數據對象
? ? ? ? var data = {
? ? ? ? ? name: 'zs',
? ? ? ? ? age: 22,
? ? ? ? ? gender: '男',
? ? ? ? ? hobby: ['吃飯', '睡覺', '運動']
? ? ? ? }
? ? ? ? // 拼接出一個方法的調用,在調用這個方法的時候,把要發送給客戶端的數據,序列化為字符串,作為參數傳遞給這個調用的方法:
? ? ? ? var result = `${cbName}(${JSON.stringify(data)})`;
? ? ? ? // 將拼接好的方法的調用,返回給客戶端去解析執行
? ? ? ? res.end(result);
? ? ? } else {
? ? ? ? res.end('404');
? ? ? }
? ? });
? ? server.listen(3000, () => {
? ? ? console.log('server running at http://127.0.0.1:3000');
? ? });
```
5. vue-resource 的配置步驟:
+ 直接在頁面中,通過`script`標簽,引入 `vue-resource` 的腳本文件;
+ 注意:引用的先后順序是:先引用 `Vue` 的腳本文件,再引用 `vue-resource` 的腳本文件;
6. 發送get請求:
```
getInfo() { // get 方式獲取數據
? this.$http.get('http://127.0.0.1:8899/api/getlunbo').then(res => {
? ? console.log(res.body);
? })
}
```
7. 發送post請求:
```
postInfo() {
? var url = 'http://127.0.0.1:8899/api/post';
? // post 方法接收三個參數:
? // 參數1: 要請求的URL地址
? // 參數2: 要發送的數據對象
? // 參數3: 指定post提交的編碼類型為 application/x-www-form-urlencoded
? this.$http.post(url, { name: 'zs' }, { emulateJSON: true }).then(res => {
? ? console.log(res.body);
? });
}
```
8. 發送JSONP請求獲取數據:
```
jsonpInfo() { // JSONP形式從服務器獲取數據
? var url = 'http://127.0.0.1:8899/api/jsonp';
? this.$http.jsonp(url).then(res => {
? ? console.log(res.body);
? });
}
```
## 配置本地數據庫和數據接口API
1. 先解壓安裝 `PHPStudy`;
2. 解壓安裝 `Navicat` 這個數據庫可視化工具,并激活;
3. 打開 `Navicat` 工具,新建空白數據庫,名為 `dtcmsdb4`;
4. 雙擊新建的數據庫,連接上這個空白數據庫,在新建的數據庫上`右鍵` -> `運行SQL文件`,選擇并執行 `dtcmsdb4.sql` 這個數據庫腳本文件;如果執行不報錯,則數據庫導入完成;
5. 進入文件夾 `vuecms3_nodejsapi` 內部,執行 `npm i` 安裝所有的依賴項;
6. 先確保本機安裝了 `nodemon`, 沒有安裝,則運行 `npm i nodemon -g` 進行全局安裝,安裝完畢后,進入到 `vuecms3_nodejsapi`目錄 -> `src`目錄 -> 雙擊運行 `start.bat`
7. 如果API啟動失敗,請檢查 PHPStudy 是否正常開啟,同時,檢查 `app.js` 中第 `14行` 中數據庫連接配置字符串是否正確;PHPStudy 中默認的 用戶名是root,默認的密碼也是root
## [Vue中的動畫](https://cn.vuejs.org/v2/guide/transitions.html)
為什么要有動畫:動畫能夠提高用戶的體驗,幫助用戶更好的理解頁面中的功能;
### 使用過渡類名
1. HTML結構:
```
<div id="app">
? ? <input type="button" value="動起來" @click="myAnimate">
? ? <!-- 使用 transition 將需要過渡的元素包裹起來 -->
? ? <transition name="fade">
? ? ? <div v-show="isshow">動畫哦</div>
? ? </transition>
? </div>
```
2. VM 實例:
```
// 創建 Vue 實例,得到 ViewModel
var vm = new Vue({
? el: '#app',
? data: {
? ? isshow: false
? },
? methods: {
? ? myAnimate() {
? ? ? this.isshow = !this.isshow;
? ? }
? }
});
```
3. 定義兩組類樣式:
```
/* 定義進入和離開時候的過渡狀態 */
? ? .fade-enter-active,
? ? .fade-leave-active {
? ? ? transition: all 0.2s ease;
? ? ? position: absolute;
? ? }
? ? /* 定義進入過渡的開始狀態 和 離開過渡的結束狀態 */
? ? .fade-enter,
? ? .fade-leave-to {
? ? ? opacity: 0;
? ? ? transform: translateX(100px);
? ? }
```
### [使用第三方 CSS 動畫庫](https://cn.vuejs.org/v2/guide/transitions.html#自定義過渡類名)
1. 導入動畫類庫:
```
<link rel="stylesheet" type="text/css" href="./lib/animate.css">
```
2. 定義 transition 及屬性:
```
<transition
enter-active-class="fadeInRight"
? ? leave-active-class="fadeOutRight"
? ? :duration="{ enter: 500, leave: 800 }">
? <div class="animated" v-show="isshow">動畫哦</div>
</transition>
```
### 使用動畫鉤子函數
1. 定義 transition 組件以及三個鉤子函數:
```
<div id="app">
? ? <input type="button" value="切換動畫" @click="isshow = !isshow">
? ? <transition
? ? @before-enter="beforeEnter"
? ? @enter="enter"
? ? @after-enter="afterEnter">
? ? ? <div v-if="isshow" class="show">OK</div>
? ? </transition>
? </div>
```
2. 定義三個 methods 鉤子方法:
```
methods: {
? ? ? ? beforeEnter(el) { // 動畫進入之前的回調
? ? ? ? ? el.style.transform = 'translateX(500px)';
? ? ? ? },
? ? ? ? enter(el, done) { // 動畫進入完成時候的回調
? ? ? ? ? el.offsetWidth;
? ? ? ? ? el.style.transform = 'translateX(0px)';
? ? ? ? ? done();
? ? ? ? },
? ? ? ? afterEnter(el) { // 動畫進入完成之后的回調
? ? ? ? ? this.isshow = !this.isshow;
? ? ? ? }
? ? ? }
```
3. 定義動畫過渡時長和樣式:
```
.show{
? ? ? transition: all 0.4s ease;
? ? }
```
### [v-for 的列表過渡](https://cn.vuejs.org/v2/guide/transitions.html#列表的進入和離開過渡)
1. 定義過渡樣式:
```
<style>
? ? .list-enter,
? ? .list-leave-to {
? ? ? opacity: 0;
? ? ? transform: translateY(10px);
? ? }
? ? .list-enter-active,
? ? .list-leave-active {
? ? ? transition: all 0.3s ease;
? ? }
</style>
```
2. 定義DOM結構,其中,需要使用 transition-group 組件把v-for循環的列表包裹起來:
```
? <div id="app">
? ? <input type="text" v-model="txt" @keyup.enter="add">
? ? <transition-group tag="ul" name="list">
? ? ? <li v-for="(item, i) in list" :key="i">{{item}}</li>
? ? </transition-group>
? </div>
```
3. 定義 VM中的結構:
```
? ? // 創建 Vue 實例,得到 ViewModel
? ? var vm = new Vue({
? ? ? el: '#app',
? ? ? data: {
? ? ? ? txt: '',
? ? ? ? list: [1, 2, 3, 4]
? ? ? },
? ? ? methods: {
? ? ? ? add() {
? ? ? ? ? this.list.push(this.txt);
? ? ? ? ? this.txt = '';
? ? ? ? }
? ? ? }
? ? });
```
### 列表的排序過渡
`<transition-group>` 組件還有一個特殊之處。不僅可以進入和離開動畫,**還可以改變定位**。要使用這個新功能只需了解新增的 `v-move` 特性,**它會在元素的改變定位的過程中應用**。
+ `v-move` 和 `v-leave-active` 結合使用,能夠讓列表的過渡更加平緩柔和:
```
.v-move{
? transition: all 0.8s ease;
}
.v-leave-active{
? position: absolute;
}
```
## 相關文章
1. [vue.js 1.x 文檔](https://v1-cn.vuejs.org/)
2. [vue.js 2.x 文檔](https://cn.vuejs.org/)
3. [String.prototype.padStart(maxLength, fillString)](http://www.css88.com/archives/7715)
4. [js 里面的鍵盤事件對應的鍵碼](http://www.cnblogs.com/wuhua1/p/6686237.html)
5. [pagekit/vue-resource](https://github.com/pagekit/vue-resource)
6. [navicat如何導入sql文件和導出sql文件](https://jingyan.baidu.com/article/a65957f4976aad24e67f9b9b.html)
7. [貝塞爾在線生成器](http://cubic-bezier.com/#.4,-0.3,1,.33)
## 定義Vue組件
什么是組件: 組件的出現,就是為了拆分Vue實例的代碼量的,能夠讓我們以不同的組件,來劃分不同的功能模塊,將來我們需要什么樣的功能,就可以去調用對應的組件即可;
組件化和模塊化的不同:
+ 模塊化: 是從代碼邏輯的角度進行劃分的;方便代碼分層開發,保證每個功能模塊的職能單一;
+ 組件化: 是從UI界面的角度進行劃分的;前端的組件化,方便UI組件的重用;
### 全局組件定義的三種方式
1. 使用 Vue.extend 配合 Vue.component 方法:
```
var login = Vue.extend({
? ? ? template: '<h1>登錄</h1>'
? ? });
? ? Vue.component('login', login);
```
2. 直接使用 Vue.component 方法:
```
Vue.component('register', {
? ? ? template: '<h1>注冊</h1>'
? ? });
```
3. 將模板字符串,定義到script標簽種:
```
<script id="tmpl" type="x-template">
? ? ? <div><a href="#">登錄</a> | <a href="#">注冊</a></div>
? ? </script>
```
同時,需要使用 Vue.component 來定義組件:
```
Vue.component('account', {
? ? ? template: '#tmpl'
? ? });
```
> 注意: 組件中的DOM結構,有且只能有唯一的根元素(Root Element)來進行包裹!
### 組件中展示數據和響應事件
1. 在組件中,`data`需要被定義為一個方法,例如:
```
Vue.component('account', {
? ? ? template: '#tmpl',
? ? ? data() {
? ? ? ? return {
? ? ? ? ? msg: '大家好!'
? ? ? ? }
? ? ? },
? ? ? methods:{
? ? ? ? login(){
? ? ? ? ? alert('點擊了登錄按鈕');
? ? ? ? }
? ? ? }
? ? });
```
2. 在子組件中,如果將模板字符串,定義到了script標簽中,那么,要訪問子組件身上的`data`屬性中的值,需要使用`this`來訪問;
### 【重點】為什么組件中的data屬性必須定義為一個方法并返回一個對象
1. 通過計數器案例演示
### 使用`components`屬性定義局部子組件
1. 組件實例定義方式:
```
<script>
? ? // 創建 Vue 實例,得到 ViewModel
? ? var vm = new Vue({
? ? ? el: '#app',
? ? ? data: {},
? ? ? methods: {},
? ? ? components: { // 定義子組件
? ? ? ? account: { // account 組件
? ? ? ? ? template: '<div><h1>這是Account組件{{name}}</h1><login></login></div>', // 在這里使用定義的子組件
? ? ? ? ? components: { // 定義子組件的子組件
? ? ? ? ? ? login: { // login 組件
? ? ? ? ? ? ? template: "<h3>這是登錄組件</h3>"
? ? ? ? ? ? }
? ? ? ? ? }
? ? ? ? }
? ? ? }
? ? });
? </script>
```
2. 引用組件:
```
<div id="app">
? ? <account></account>
? </div>
```
## 使用`flag`標識符結合`v-if`和`v-else`切換組件
1. 頁面結構:
```
<div id="app">
? ? <input type="button" value="toggle" @click="flag=!flag">
? ? <my-com1 v-if="flag"></my-com1>
? ? <my-com2 v-else="flag"></my-com2>
? </div>
```
2. Vue實例定義:
```
<script>
? ? Vue.component('myCom1', {
? ? ? template: '<h3>奔波霸</h3>'
? ? })
? ? Vue.component('myCom2', {
? ? ? template: '<h3>霸波奔</h3>'
? ? })
? ? // 創建 Vue 實例,得到 ViewModel
? ? var vm = new Vue({
? ? ? el: '#app',
? ? ? data: {
? ? ? ? flag: true
? ? ? },
? ? ? methods: {}
? ? });
? </script>
```
## 使用`:is`屬性來切換不同的子組件,并添加切換動畫
1. 組件實例定義方式:
```
? // 登錄組件
? ? const login = Vue.extend({
? ? ? template: `<div>
? ? ? ? <h3>登錄組件</h3>
? ? ? </div>`
? ? });
? ? Vue.component('login', login);
? ? // 注冊組件
? ? const register = Vue.extend({
? ? ? template: `<div>
? ? ? ? <h3>注冊組件</h3>
? ? ? </div>`
? ? });
? ? Vue.component('register', register);
? ? // 創建 Vue 實例,得到 ViewModel
? ? var vm = new Vue({
? ? ? el: '#app',
? ? ? data: { comName: 'login' },
? ? ? methods: {}
? ? });
```
2. 使用`component`標簽,來引用組件,并通過`:is`屬性來指定要加載的組件:
```
? <div id="app">
? ? <a href="#" @click.prevent="comName='login'">登錄</a>
? ? <a href="#" @click.prevent="comName='register'">注冊</a>
? ? <hr>
? ? <transition mode="out-in">
? ? ? <component :is="comName"></component>
? ? </transition>
? </div>
```
3. 添加切換樣式:
```
? <style>
? ? .v-enter,
? ? .v-leave-to {
? ? ? opacity: 0;
? ? ? transform: translateX(30px);
? ? }
? ? .v-enter-active,
? ? .v-leave-active {
? ? ? position: absolute;
? ? ? transition: all 0.3s ease;
? ? }
? ? h3{
? ? ? margin: 0;
? ? }
? </style>
```
## 父組件向子組件傳值
1. 組件實例定義方式,注意:一定要使用`props`屬性來定義父組件傳遞過來的數據
```
<script>
? ? // 創建 Vue 實例,得到 ViewModel
? ? var vm = new Vue({
? ? ? el: '#app',
? ? ? data: {
? ? ? ? msg: '這是父組件中的消息'
? ? ? },
? ? ? components: {
? ? ? ? son: {
? ? ? ? ? template: '<h1>這是子組件 --- {{finfo}}</h1>',
? ? ? ? ? props: ['finfo']
? ? ? ? }
? ? ? }
? ? });
? </script>
```
2. 使用`v-bind`或簡化指令,將數據傳遞到子組件中:
```
<div id="app">
? ? <son :finfo="msg"></son>
? </div>
```
## 子組件向父組件傳值
1. 原理:父組件將方法的引用,傳遞到子組件內部,子組件在內部調用父組件傳遞過來的方法,同時把要發送給父組件的數據,在調用方法的時候當作參數傳遞進去;
2. 父組件將方法的引用傳遞給子組件,其中,`getMsg`是父組件中`methods`中定義的方法名稱,`func`是子組件調用傳遞過來方法時候的方法名稱
```
<son @func="getMsg"></son>
```
3. 子組件內部通過`this.$emit('方法名', 要傳遞的數據)`方式,來調用父組件中的方法,同時把數據傳遞給父組件使用
```
<div id="app">
? ? <!-- 引用父組件 -->
? ? <son @func="getMsg"></son>
? ? <!-- 組件模板定義 -->
? ? <script type="x-template" id="son">
? ? ? <div>
? ? ? ? <input type="button" value="向父組件傳值" @click="sendMsg" />
? ? ? </div>
? ? </script>
? </div>
? <script>
? ? // 子組件的定義方式
? ? Vue.component('son', {
? ? ? template: '#son', // 組件模板Id
? ? ? methods: {
? ? ? ? sendMsg() { // 按鈕的點擊事件
? ? ? ? ? this.$emit('func', 'OK'); // 調用父組件傳遞過來的方法,同時把數據傳遞出去
? ? ? ? }
? ? ? }
? ? });
? ? // 創建 Vue 實例,得到 ViewModel
? ? var vm = new Vue({
? ? ? el: '#app',
? ? ? data: {},
? ? ? methods: {
? ? ? ? getMsg(val){ // 子組件中,通過 this.$emit() 實際調用的方法,在此進行定義
? ? ? ? ? alert(val);
? ? ? ? }
? ? ? }
? ? });
? </script>
```
## 評論列表案例
目標:主要練習父子組件之間傳值
## 使用 `this.$refs` 來獲取元素和組件
```
? <div id="app">
? ? <div>
? ? ? <input type="button" value="獲取元素內容" @click="getElement" />
? ? ? <!-- 使用 ref 獲取元素 -->
? ? ? <h1 ref="myh1">這是一個大大的H1</h1>
? ? ? <hr>
? ? ? <!-- 使用 ref 獲取子組件 -->
? ? ? <my-com ref="mycom"></my-com>
? ? </div>
? </div>
? <script>
? ? Vue.component('my-com', {
? ? ? template: '<h5>這是一個子組件</h5>',
? ? ? data() {
? ? ? ? return {
? ? ? ? ? name: '子組件'
? ? ? ? }
? ? ? }
? ? });
? ? // 創建 Vue 實例,得到 ViewModel
? ? var vm = new Vue({
? ? ? el: '#app',
? ? ? data: {},
? ? ? methods: {
? ? ? ? getElement() {
? ? ? ? ? // 通過 this.$refs 來獲取元素
? ? ? ? ? console.log(this.$refs.myh1.innerText);
? ? ? ? ? // 通過 this.$refs 來獲取組件
? ? ? ? ? console.log(this.$refs.mycom.name);
? ? ? ? }
? ? ? }
? ? });
? </script>
```
## 什么是路由
1. 對于普通的網站,所有的超鏈接都是URL地址,所有的URL地址都對應服務器上對應的資源;
2. 對于單頁面應用程序來說,主要通過URL中的hash(#號)來實現不同頁面之間的切換,同時,hash有一個特點:HTTP請求中不會包含hash相關的內容;所以,單頁面程序中的頁面跳轉主要用hash實現;
3. 在單頁面應用程序中,這種通過hash改變來切換頁面的方式,稱作前端路由(區別于后端路由);
## 在 vue 中使用 vue-router
1. 導入 vue-router 組件類庫:
```
<!-- 1. 導入 vue-router 組件類庫 -->
? <script src="./lib/vue-router-2.7.0.js"></script>
```
2. 使用 router-link 組件來導航
```
<!-- 2. 使用 router-link 組件來導航 -->
<router-link to="/login">登錄</router-link>
<router-link to="/register">注冊</router-link>
```
3. 使用 router-view 組件來顯示匹配到的組件
```
<!-- 3. 使用 router-view 組件來顯示匹配到的組件 -->
<router-view></router-view>
```
4. 創建使用`Vue.extend`創建組件
```
? ? // 4.1 使用 Vue.extend 來創建登錄組件
? ? var login = Vue.extend({
? ? ? template: '<h1>登錄組件</h1>'
? ? });
? ? // 4.2 使用 Vue.extend 來創建注冊組件
? ? var register = Vue.extend({
? ? ? template: '<h1>注冊組件</h1>'
? ? });
```
5. 創建一個路由 router 實例,通過 routers 屬性來定義路由匹配規則
```
// 5. 創建一個路由 router 實例,通過 routers 屬性來定義路由匹配規則
? ? var router = new VueRouter({
? ? ? routes: [
? ? ? ? { path: '/login', component: login },
? ? ? ? { path: '/register', component: register }
? ? ? ]
? ? });
```
6. 使用 router 屬性來使用路由規則
```
// 6. 創建 Vue 實例,得到 ViewModel
? ? var vm = new Vue({
? ? ? el: '#app',
? ? ? router: router // 使用 router 屬性來使用路由規則
? ? });
```
## 設置路由高亮
## 設置路由切換動效
## 在路由規則中定義參數
1. 在規則中定義參數:
```
{ path: '/register/:id', component: register }
```
2. 通過 `this.$route.params`來獲取路由中的參數:
```
var register = Vue.extend({
? ? ? template: '<h1>注冊組件 --- {{this.$route.params.id}}</h1>'
? ? });
```
## 使用 `children` 屬性實現路由嵌套
```
? <div id="app">
? ? <router-link to="/account">Account</router-link>
? ? <router-view></router-view>
? </div>
? <script>
? ? // 父路由中的組件
? ? const account = Vue.extend({
? ? ? template: `<div>
? ? ? ? 這是account組件
? ? ? ? <router-link to="/account/login">login</router-link> |
? ? ? ? <router-link to="/account/register">register</router-link>
? ? ? ? <router-view></router-view>
? ? ? </div>`
? ? });
? ? // 子路由中的 login 組件
? ? const login = Vue.extend({
? ? ? template: '<div>登錄組件</div>'
? ? });
? ? // 子路由中的 register 組件
? ? const register = Vue.extend({
? ? ? template: '<div>注冊組件</div>'
? ? });
? ? // 路由實例
? ? var router = new VueRouter({
? ? ? routes: [
? ? ? ? { path: '/', redirect: '/account/login' }, // 使用 redirect 實現路由重定向
? ? ? ? {
? ? ? ? ? path: '/account',
? ? ? ? ? component: account,
? ? ? ? ? children: [ // 通過 children 數組屬性,來實現路由的嵌套
? ? ? ? ? ? { path: 'login', component: login }, // 注意,子路由的開頭位置,不要加 / 路徑符
? ? ? ? ? ? { path: 'register', component: register }
? ? ? ? ? ]
? ? ? ? }
? ? ? ]
? ? });
? ? // 創建 Vue 實例,得到 ViewModel
? ? var vm = new Vue({
? ? ? el: '#app',
? ? ? data: {},
? ? ? methods: {},
? ? ? components: {
? ? ? ? account
? ? ? },
? ? ? router: router
? ? });
? </script>
```
## 命名視圖實現經典布局
1. 標簽代碼結構:
```
<div id="app">
? ? <router-view></router-view>
? ? <div class="content">
? ? ? <router-view name="a"></router-view>
? ? ? <router-view name="b"></router-view>
? ? </div>
? </div>
```
2. JS代碼:
```
<script>
? ? var header = Vue.component('header', {
? ? ? template: '<div class="header">header</div>'
? ? });
? ? var sidebar = Vue.component('sidebar', {
? ? ? template: '<div class="sidebar">sidebar</div>'
? ? });
? ? var mainbox = Vue.component('mainbox', {
? ? ? template: '<div class="mainbox">mainbox</div>'
? ? });
? ? // 創建路由對象
? ? var router = new VueRouter({
? ? ? routes: [
? ? ? ? {
? ? ? ? ? path: '/', components: {
? ? ? ? ? ? default: header,
? ? ? ? ? ? a: sidebar,
? ? ? ? ? ? b: mainbox
? ? ? ? ? }
? ? ? ? }
? ? ? ]
? ? });
? ? // 創建 Vue 實例,得到 ViewModel
? ? var vm = new Vue({
? ? ? el: '#app',
? ? ? data: {},
? ? ? methods: {},
? ? ? router
? ? });
? </script>
```
3. CSS 樣式:
```
? <style>
? ? .header {
? ? ? border: 1px solid red;
? ? }
? ? .content{
? ? ? display: flex;
? ? }
? ? .sidebar {
? ? ? flex: 2;
? ? ? border: 1px solid green;
? ? ? height: 500px;
? ? }
? ? .mainbox{
? ? ? flex: 8;
? ? ? border: 1px solid blue;
? ? ? height: 500px;
? ? }
? </style>
```
## `watch`屬性的使用
考慮一個問題:想要實現 `名` 和 `姓` 兩個文本框的內容改變,則全名的文本框中的值也跟著改變;(用以前的知識如何實現???)
1. 監聽`data`中屬性的改變:
```
<div id="app">
? ? <input type="text" v-model="firstName"> +
? ? <input type="text" v-model="lastName"> =
? ? <span>{{fullName}}</span>
? </div>
? <script>
? ? // 創建 Vue 實例,得到 ViewModel
? ? var vm = new Vue({
? ? ? el: '#app',
? ? ? data: {
? ? ? ? firstName: 'jack',
? ? ? ? lastName: 'chen',
? ? ? ? fullName: 'jack - chen'
? ? ? },
? ? ? methods: {},
? ? ? watch: {
? ? ? ? 'firstName': function (newVal, oldVal) { // 第一個參數是新數據,第二個參數是舊數據
? ? ? ? ? this.fullName = newVal + ' - ' + this.lastName;
? ? ? ? },
? ? ? ? 'lastName': function (newVal, oldVal) {
? ? ? ? ? this.fullName = this.firstName + ' - ' + newVal;
? ? ? ? }
? ? ? }
? ? });
? </script>
```
2. 監聽路由對象的改變:
```
<div id="app">
? ? <router-link to="/login">登錄</router-link>
? ? <router-link to="/register">注冊</router-link>
? ? <router-view></router-view>
? </div>
? <script>
? ? var login = Vue.extend({
? ? ? template: '<h1>登錄組件</h1>'
? ? });
? ? var register = Vue.extend({
? ? ? template: '<h1>注冊組件</h1>'
? ? });
? ? var router = new VueRouter({
? ? ? routes: [
? ? ? ? { path: "/login", component: login },
? ? ? ? { path: "/register", component: register }
? ? ? ]
? ? });
? ? // 創建 Vue 實例,得到 ViewModel
? ? var vm = new Vue({
? ? ? el: '#app',
? ? ? data: {},
? ? ? methods: {},
? ? ? router: router,
? ? ? watch: {
? ? ? ? '$route': function (newVal, oldVal) {
? ? ? ? ? if (newVal.path === '/login') {
? ? ? ? ? ? console.log('這是登錄組件');
? ? ? ? ? }
? ? ? ? }
? ? ? }
? ? });
? </script>
```
## `computed`計算屬性的使用
1. 默認只有`getter`的計算屬性:
```
<div id="app">
? ? <input type="text" v-model="firstName"> +
? ? <input type="text" v-model="lastName"> =
? ? <span>{{fullName}}</span>
? </div>
? <script>
? ? // 創建 Vue 實例,得到 ViewModel
? ? var vm = new Vue({
? ? ? el: '#app',
? ? ? data: {
? ? ? ? firstName: 'jack',
? ? ? ? lastName: 'chen'
? ? ? },
? ? ? methods: {},
? ? ? computed: { // 計算屬性; 特點:當計算屬性中所以來的任何一個 data 屬性改變之后,都會重新觸發 本計算屬性 的重新計算,從而更新 fullName 的值
? ? ? ? fullName() {
? ? ? ? ? return this.firstName + ' - ' + this.lastName;
? ? ? ? }
? ? ? }
? ? });
? </script>
```
2. 定義有`getter`和`setter`的計算屬性:
```
<div id="app">
? ? <input type="text" v-model="firstName">
? ? <input type="text" v-model="lastName">
? ? <!-- 點擊按鈕重新為 計算屬性 fullName 賦值 -->
? ? <input type="button" value="修改fullName" @click="changeName">
? ? <span>{{fullName}}</span>
? </div>
? <script>
? ? // 創建 Vue 實例,得到 ViewModel
? ? var vm = new Vue({
? ? ? el: '#app',
? ? ? data: {
? ? ? ? firstName: 'jack',
? ? ? ? lastName: 'chen'
? ? ? },
? ? ? methods: {
? ? ? ? changeName() {
? ? ? ? ? this.fullName = 'TOM - chen2';
? ? ? ? }
? ? ? },
? ? ? computed: {
? ? ? ? fullName: {
? ? ? ? ? get: function () {
? ? ? ? ? ? return this.firstName + ' - ' + this.lastName;
? ? ? ? ? },
? ? ? ? ? set: function (newVal) {
? ? ? ? ? ? var parts = newVal.split(' - ');
? ? ? ? ? ? this.firstName = parts[0];
? ? ? ? ? ? this.lastName = parts[1];
? ? ? ? ? }
? ? ? ? }
? ? ? }
? ? });
? </script>
```
## `watch`、`computed`和`methods`之間的對比
1. `computed`屬性的結果會被緩存,除非依賴的響應式屬性變化才會重新計算。主要當作屬性來使用;
2. `methods`方法表示一個具體的操作,主要書寫業務邏輯;
3. `watch`一個對象,鍵是需要觀察的表達式,值是對應回調函數。主要用來監聽某些特定數據的變化,從而進行某些具體的業務邏輯操作;可以看作是`computed`和`methods`的結合體;
## `nrm`的安裝使用
作用:提供了一些最常用的NPM包鏡像地址,能夠讓我們快速的切換安裝包時候的服務器地址;
什么是鏡像:原來包剛一開始是只存在于國外的NPM服務器,但是由于網絡原因,經常訪問不到,這時候,我們可以在國內,創建一個和官網完全一樣的NPM服務器,只不過,數據都是從人家那里拿過來的,除此之外,使用方式完全一樣;
1. 運行`npm i nrm -g`全局安裝`nrm`包;
2. 使用`nrm ls`查看當前所有可用的鏡像源地址以及當前所使用的鏡像源地址;
3. 使用`nrm use npm`或`nrm use taobao`切換不同的鏡像源地址;
## 相關文件
1. [URL中的hash(井號)](http://www.cnblogs.com/joyho/articles/4430148.html)
Data多實例共享問題:
app.vue文件我們可以分成三部分解讀,
<template></template>標簽包裹的內容:這是模板的HTMLDom結構,里邊引入了一張圖片和<router-view></router-view>標簽,<router-view>標簽說明使用了路由機制。我們會在以后專門拿出一篇文章講Vue-router。
<script></script>標簽包括的js內容:你可以在這里些一些頁面的動態效果和Vue的邏輯代碼。
<style></style>標簽包裹的css內容:這里就是你平時寫的CSS樣式,對頁面樣子進行裝飾用的,需要特別說明的是你可以用<style scoped></style>來聲明這些css樣式只在本模板中起作用。