如果您想訂閱本博客內容,每天自動發到您的郵箱中, 請點這里
【Vue原理】Watch - 白話版
簡述 響應式
監聽的數據改變的時,watch 如何工作
設置 immediate 時,watch 如何工作
設置了 deep 時, watch 如何工作
舉栗子
結論
沒有設置 deep
設置了 deep
實際證明
專注 Vue 源碼分享,為了方便大家理解,分為了白話版和 源碼版,白話版可以輕松理解工作原理和設計思想,源碼版可以更清楚內部操作和 Vue的美,喜歡我就關注我的公眾號,公眾號的文章,排版更好看
如果你覺得排版難看,請點擊下面公眾號鏈接
【Vue原理】Watch - 白話版
今天我們用白話文解讀 watch 的工作原理,輕松快速理解 watch 內部工作原理。你說,你只懂怎么用的,卻不懂他內部是怎么工作的,這樣能有什么用?
近期有篇 《停止學習框架》很火,其實本意不是讓我們不要學框架,而是讓我們不要只停留在框架表面,我們要學會深入,以一敵十,讓我們也要學會框架的抽象能力
watch 想必大家用得應該也挺多的,用得也很順,如果你順便花一點點時間了解一下內部的工作原理,相信肯定會對你的工作有事半功倍的效果
watch 的工作原理其實挺簡單的,如果你有看過我之前講解其他選項的文章,你可以一下子就明白 watch 是如何工作的,所以這邊文章我也?得很快
根據 watch 的 api,我們需要了解三個地方
1、監聽的數據改變的時,watch 如何工作
2、設置 immediate 時,watch 如何工作
3、設置了 deep 時,watch 如何工作
簡述 響應式
Vue 會把數據設置響應式,既是設置他的 get 和 set
當 數據被讀取,get 被觸發,然后收集到讀取他的東西,保存到依賴收集器
當 數據被改變,set 被觸發,然后通知曾經讀取他的東西進行更新
如果你不了解,可以查看下 以前的文章
【Vue原理】響應式原理 - 白話版
監聽的數據改變的時,watch 如何工作
watch 在一開始初始化的時候,會 讀取 一遍 監聽的數據的值,于是,此時 那個數據就收集到 watch 的 watcher 了
然后 你給 watch 設置的 handler ,watch 會放入 watcher 的更新函數中
當 數據改變時,通知 watch 的 watcher 進行更新,于是 你設置的 handler 就被調用了
設置 immediate 時,watch 如何工作
當你設置了 immediate 時,就不需要在 數據改變的時候 才會觸發。
而是在 初始化 watch 時,在讀取了 監聽的數據的值 之后,便立即調用一遍你設置的監聽回調,然后傳入剛讀取的值
設置了 deep 時, watch 如何工作
我們都知道 watch 有一個 deep 選項,是用來深度監聽的。什么是深度監聽呢?就是當你監聽的屬性的值是一個對象的時候,如果你沒有設置深度監聽,當對象內部變化時,你監聽的回調是不會被觸發的
在說明這個之前,請大家先了解一下
當你使用 Object.defineProperty 給 【值是對象的屬性】 設置 set 和 get 的時候
1如果你直接改變或讀取這個屬性 ( 直接賦值 ),可以觸發這個屬性的設置的 set 和 get
2但是你改變或讀取它內部的屬性,get 和 set 不會被觸發的
舉栗子
var inner = { first:1111 }
var test={ name:inner }
Object.defineProperty(test,"name",{
get(){
console.log("name get被觸發")
return inner
},
set(){
console.log("name set被觸發")
}
})
// 訪問 test.name 第一次,觸發 name 的 get
Object.defineProperty(test.name,"first",{
get(){
return console.log("first get被觸發")
},
set(){
console.log("first set被觸發")
}
})
// 訪問 test.name 第二次,觸發 name 的 get
var a = test.name
// 獨立訪問 first 第一次
var b= a.first
// 獨立訪問 first 第二次
b= a.first
// 獨立改變 first
a.first = 5
能看到除了有兩次需要訪問到 name,必不可少會觸發到 name 的 get
之后,當我們獨立訪問 name 內部的 first 的時,只會觸發 first 的 get 函數,而 name 設置的 get 并不會被觸發
結論
看上面的例子后,所以當你的 data 屬性值是對象,比如下面的 info
data(){
return {
info:{ name:1 }
}
}
此時,Vue在設置響應式數據的時候, 遇到值是對象的,會遞歸遍歷,把對象內所有的屬性都設置為響應式,就是每個屬性都設置 get 和 set,于是每個屬性都有自己的一個依賴收集器
首先,再次說明,watch初始化的時候,會先讀取一遍監聽數據的值
沒有設置 deep
1、因為讀取了監聽的 data 的屬性,watch 的 watcher 被收集在 這個屬性的 收集器中
設置了 deep
1、因為讀取了監聽的data 的屬性,watch 的 watcher 被收集在 這個屬性的 收集器中
2、在讀取 data 屬性的時候,發現設置了 deep 而且值是一個對象,會遞歸遍歷這個值,把內部所有屬性逐個讀取一遍,于是 屬性和 它的對象值內每一個屬性 都會收集到 watch 的 watcher
于是,無論對象嵌套多深的屬性,只要改變了,會通知 相應的 watch 的 watcher 去更新,于是 你設置的 watch 回調就被觸發了
實際證明
證明 watch 的 watcher 深度監聽時是否被內部每個屬性都收集
我在 Vue 內部給 watch 的 watcher 加了一個 屬性,標識他是 watch 的 watcher,并且去掉了多余的屬性,為了截圖短一點
藍藍設計( www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、網站建設 、平面設計服務。
如果您想訂閱本博客內容,每天自動發到您的郵箱中, 請點這里
1.無序列表
無序列表是網頁中最常用的列表,之所以稱為“無序列表”,是因為其各個列表項之間為并列關系,沒有順序級別之分,列如:
<ul>
<li>列表1</li>
<li>列表2</li>
<li>列表3</li>
</ul>
2.有序列表
有序列表即為有順序的列表,其各個列表項會按照一定的順序排列,列如:
<ol>
<li>列表1</li>
<li>列表2</li>
<li>列表3</li>
</ol>
注:列表可以嵌套。
3.超鏈接標記< a>
1.超鏈接
一個網站通常由多個頁面構成,進入網站時首先看到的是其首頁,如果想從首頁跳轉到其子頁面,就需要在首頁相應的位置添加超鏈接。其基本語法格式為:
<a href="跳轉目標" target=“目標窗口的彈出方式”>文本或者圖像</a>
1
其中,target有兩種取值方式:
–blank (在新窗口中打開)-self(默認在本窗口打開)
2.偽類
超鏈接標記< a >的偽類 含義
a:link{ CSS樣式規則; } 未訪問時超鏈接的狀態
a:visited{ CSS樣式規則;} 訪問之后超鏈接的狀態
a:hover{ CSS樣式規則;} 鼠標經過,懸停時超鏈接的狀態
a:active{ CSS樣式規則;} 鼠標單擊不動時超鏈接的狀態
---------------------
藍藍設計( www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、網站建設 、平面設計服務。
如果您想訂閱本博客內容,每天自動發到您的郵箱中, 請點這里
HTML 中的div定義:
可定義文檔中的分區或節(division/section)。
標簽可以把文檔分割為獨立的、不同的部分。它可以用作嚴格的組織工具,并且不使用任何格式與其關聯。
如果用 id 或 class 來標記 div,那么該標簽的作用會變得更加有效。
用法
div是一個塊級元素。這意味著它的內容自動地開始一個新行。實際上,換行是
固有的唯一格式表現??梢酝ㄟ^ div的 class 或 id 應用額外的樣式。
不必為每一個 div都加上類或 id,雖然這樣做也有一定的好處。
可以對同一個 div 元素應用 class 或 id 屬性,但是更常見的情況是只應用其中一種。這兩者的主要差異是,class 用于元素組(類似的元素,或者可以理解為某一類元素),而 id 用于標識單獨的唯一的元素。
實例
<div id = " text"><div> <div class = " text1"><div> <div class = " text1"><div>
//div 中可以設置id屬性,通過引用id屬性來為div設置一些樣式
//在style標簽中,可以對你寫的代碼進行樣式的設計,樣式設計可以通過以下幾種方法來寫
1.通過引用id來設置樣式,在id名稱前加上# 格式: #id名稱{ }
#text{ }
2.通過class來設置樣式,class后面的名字可以是一樣的,而id取名唯一,因此在需要設置同類型的樣式時可以使用class來設置 格式: .class名稱{ }
.text1{ }
3.通過標簽名稱來設置樣式 格式: div{ }
div{ }
樣式:
1.width :50px; // 寬度
2.height :50px;//高度
3.border : 1px solid red; //邊框,border可以設置三個屬性,分別是邊框寬度,邊框樣式(實線,虛線等),邊框顏色
4.margin:屬性定義及使用說明
margin簡寫屬性在一個聲明中設置所有外邊距屬性。該屬性可以有1到4個值。
實例:
margin:10px 5px 15px 20px;
上邊距是 10px
右邊距是 5px
下邊距是 15px
左邊距是 20px
margin:10px 5px 15px;
上邊距是 10px
右邊距和左邊距是 5px
下邊距是 15px
margin:10px 5px;
上邊距和下邊距是 10px
右邊距和左邊距是 5px
margin:10px;
所有四個邊距都是 10px
藍藍設計( www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、網站建設 、平面設計服務。
如果您想訂閱本博客內容,每天自動發到您的郵箱中, 請點這里
1 狀態共享
隨著組件的細化,就會遇到多組件狀態共享的情況,Vuex當然可以解決這類問題,不過就像Vuex官方文檔所說的,如果應用不夠大,為避免代碼繁瑣冗余,最好不要使用它,今天我們介紹的是vue.js 2.6新增加的Observable API ,通過使用這個api我們可以應對一些簡單的跨組件數據狀態共享的情況。
如下這個例子,我們將在組件外創建一個store,然后在App.vue組件里面使用store.js提供的store和mutation方法,同理其它組件也可以這樣使用,從而實現多個組件共享數據狀態。
首先創建一個store.js,包含一個store和一個mutations,分別用來指向數據和處理方法。
import Vue from "vue";
export const store = Vue.observable({ count: 0 });
export const mutations = {
setCount(count) {
store.count = count;
}
};
復制代碼
然后在App.vue里面引入這個store.js,在組件里面使用引入的數據和方法
<template>
<div id="app">
<img width="25%" src="./assets/logo.png">
<p>count:{{count}}</p>
<button @click="setCount(count+1)">+1</button>
<button @click="setCount(count-1)">-1</button>
</div>
</template>
<script>
import { store, mutations } from "./store";
export default {
name: "App",
computed: {
count() {
return store.count;
}
},
methods: {
setCount: mutations.setCount
}
};
</script>
<style>
復制代碼
你可以點擊在線DEMO查看最終效果
2 長列表性能優化
我們應該都知道vue會通過object.defineProperty對數據進行劫持,來實現視圖響應數據的變化,然而有些時候我們的組件就是純粹的數據展示,不會有任何改變,我們就不需要vue來劫持我們的數據,在大量數據展示的情況下,這能夠很明顯的減少組件初始化的時間,那如何禁止vue劫持我們的數據呢?可以通過object.freeze方法來凍結一個對象,一旦被凍結的對象就再也不能被修改了。
export default {
data: () => ({
users: {}
}),
async created() {
const users = await axios.get("/api/users");
this.users = Object.freeze(users);
}
};
復制代碼
另外需要說明的是,這里只是凍結了users的值,引用不會被凍結,當我們需要reactive數據的時候,我們可以重新給users賦值。
export default {
data: () => ({
users: []
}),
async created() {
const users = await axios.get("/api/users");
this.users = Object.freeze(users);
},
methods:{
// 改變值不會觸發視圖響應
this.users[0] = newValue
// 改變引用依然會觸發視圖響應
this.users = newArray
}
};
復制代碼
3 去除多余的樣式
隨著項目越來越大,書寫的不注意,不自然的就會產生一些多余的css,小項目還好,一旦項目大了以后,多余的css會越來越多,導致包越來越大,從而影響項目運行性能,所以有必要在正式環境去除掉這些多余的css,這里推薦一個庫purgecss,支持CLI、JavascriptApi、Webpack等多種方式使用,通過這個庫,我們可以很容易的去除掉多余的css。
我做了一個測試,在線DEMO
<h1>Hello Vanilla!</h1>
<div>
We use Parcel to bundle this sandbox, you can find more info about Parcel
<a target="_blank" rel="noopener noreferrer">here</a>.
</div>
復制代碼
body {
font-family: sans-serif;
}
a {
color: red;
}
ul {
li {
list-style: none;
}
} import Purgecss from "purgecss";
const purgecss = new Purgecss({
content: ["**/*.html"],
css: ["**/*.css"]
});
const purgecssResult = purgecss.purge();
終產生的purgecssResult結果如下,可以看到多余的a和ul標簽的樣式都沒了
4 作用域插槽
利用好作用域插槽可以做一些很有意思的事情,比如定義一個基礎布局組件A,只負責布局,不管數據邏輯,然后另外定義一個組件B負責數據處理,布局組件A需要數據的時候就去B里面去取。假設,某一天我們的布局變了,我們只需要去修改組件A就行,而不用去修改組件B,從而就能充分復用組件B的數據處理邏輯,關于這塊我之前寫過一篇實際案例,可以點擊這里查看。
這里涉及到的一個最重要的點就是父組件要去獲取子組件里面的數據,之前是利用slot-scope,自vue 2.6.0起,提供了更好的支持 slot和 slot-scope 特性的 API 替代方案。
比如,我們定一個名為current-user的組件:
<span>
<slot>{{ user.lastName }}</slot>
</span>
復制代碼
父組件引用current-user的組件,但想用名替代姓(老外名字第一個單詞是名,后一個單詞是姓):
<current-user>
{{ user.firstName }}
</current-user>
復制代碼
這種方式不會生效,因為user對象是子組件的數據,在父組件里面我們獲取不到,這個時候我們就可以通過v-slot 來實現。
首先在子組件里面,將user作為一個<slot>元素的特性綁定上去:
<span>
<slot v-bind:user="user">
{{ user.lastName }}
</slot>
</span>
復制代碼
之后,我們就可以在父組件引用的時候,給v-slot帶一個值來定義我們提供的插槽 prop 的名字:
<current-user>
<template v-slot:default="slotProps">
{{ slotProps.user.firstName }}
</template>
</current-user>
復制代碼
這種方式還有縮寫語法,可以查看獨占默認插槽的縮寫語法,最終我們引用的方式如下:
<current-user v-slot="slotProps">
{{ slotProps.user.firstName }}
</current-user>
復制代碼
相比之前slot-scope代碼更清晰,更好理解。
5 屬性事件傳遞
寫過高階組件的童鞋可能都會碰到過將加工過的屬性向下傳遞的情況,如果碰到屬性較多時,需要一個個去傳遞,非常不友好并且費時,有沒有一次性傳遞的呢(比如react里面的{...this.props})?答案就是v-bind和v-on。
舉個例子,假如有一個基礎組件BaseList,只有基礎的列表展示功能,現在我們想在這基礎上增加排序功能,這個時候我們就可以創建一個高階組件SortList。
<!-- SortList -->
<template>
<BaseList v-bind="$props" v-on="$listeners"> <!-- ... --> </BaseList>
</template>
<script>
import BaseList from "./BaseList";
// 包含了基礎的屬性定義
import BaseListMixin from "./BaseListMixin";
// 封裝了排序的邏輯
import sort from "./sort.js";
export default {
props: BaseListMixin.props,
components: {
BaseList
}
};
</script>
復制代碼
可以看到傳遞屬性和事件的方便性,而不用一個個去傳遞
6 函數式組件
函數式組件,即無狀態,無法實例化,內部沒有任何生命周期處理方法,非常輕量,因而渲染性能高,特別適合用來只依賴外部數據傳遞而變化的組件。
寫法如下:
在template標簽里面標明functional
只接受props值
不需要script標簽
<!-- App.vue -->
<template>
<div id="app">
<List
:items="['Wonderwoman', 'Ironman']"
:item-click="item => (clicked = item)"
/>
<p>Clicked hero: {{ clicked }}</p>
</div>
</template>
<script>
import List from "./List";
export default {
name: "App",
data: () => ({ clicked: "" }),
components: { List }
};
</script>
復制代碼
<!-- List.vue 函數式組件 -->
<template functional>
<div>
<p v-for="item in props.items" @click="props.itemClick(item);">
{{ item }}
</p>
</div>
</template>
復制代碼
7 監聽組件的生命周期
比如有父組件Parent和子組件Child,如果父組件監聽到子組件掛載mounted就做一些邏輯處理,常規的寫法可能如下:
// Parent.vue
<Child @mounted="doSomething"/>
// Child.vue
mounted() {
this.$emit("mounted");
}
復制代碼
這里提供一種特別簡單的方式,子組件不需要任何處理,只需要在父組件引用的時候通過@hook來監聽即可,代碼重寫如下:
<Child @hook:mounted="doSomething"/>
復制代碼
當然這里不僅僅是可以監聽mounted,其它的生命周期事件,例如:created,updated等都可以,是不是特別方便~
參考鏈接:
vueTips
vuePost
藍藍設計( www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務。
如果您想訂閱本博客內容,每天自動發到您的郵箱中, 請點這里
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Vue 測試實例</title>
<script src="../static/vue.min.js"></script>
</head>
<body>
<div id="vue_data">
<h1>title : {{title}}</h1>
<h1>url : {{url}}</h1>
<h1>{{info()}}</h1>
</div>
<script type="text/javascript">
var vm = new Vue({
el: '#vue_data',
data: {
title: "Vue.js",
url: "https://cn.vuejs.org"
},
methods: {
info: function() {
return this.title + " - 堅持學習!";
}
}
})
</script>
</body>
</html>
1、每個Vue應用都需要實例化Vue來實現
var vm = new Vue({
//*******
})
2、el參數
在上面實例中的id為vue_data,在div元素中:
<div id="vue_data"></div>
意味著所有的改動均在這個id為vau_data的div中,外部不受影響。
3、定義數據對象
data用于定義屬性,在上述實例中有2個屬性,分別為:title、url。
methods用于定義函數,可以通過return來返回函數值。
{{ }}用于輸出對象屬性和函數返回值。
當一個Vue實例創建時,Vue的響應系統加入了data對象中能找到的所有屬性。當這些屬性的值發生改變時,html視圖也會產生相應的變化。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Vue 測試實例</title>
<script src="../static/vue.min.js"></script>
</head>
<body>
<div id="vue_data">
<h1>title : {{title}}</h1>
<h1>url : {{url}}</h1>
</div>
<script type="text/javascript">
//數據對象
var data = {title: "Vue.js",url: "https://cn.vuejs.org"}
var vm = new Vue({
el: '#vue_data',
data: data
})
//設置屬性會影響到原始數據
vm.title = "spring";
document.write(data.title + "<br>");
//同樣
data.url = "https://spring.io";
document.write(vm.url);
</script>
</body>
</html>
Vue還提供了實例屬性與方法,以前綴$與用戶定義的屬性區分開來。
document.write(vm.$data === data) // true
藍藍設計( www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務。
如果您想訂閱本博客內容,每天自動發到您的郵箱中, 請點這里
使用新版本的彈性伸縮布局
display使用彈性伸縮盒
direction容器盒內元素的排列順序
flex-wrap設置無法容納時,自動換行
justify-content 伸縮項目的排列方式
align-items 處理額外空間
align-self 單獨處理一個伸縮項目的額外空間
flex 控制伸縮容器的比例分配
order 設置伸縮項目出現的位置
dislpay
值:
flex 將容器盒作為塊級彈性伸縮盒顯示。
inline-flex:將容器盒作為內聯級彈性伸縮盒顯示。
實際現實中 這兩個值沒區別。
direction
容器盒內元素的排列順序
值:
row:從左到右排列
row-reverse:從右到左排列
column:從上倒下排列
column-reverse從下到上排列
flex-wrap
設置無法容納時,自動換行
值:
nowrap:不換行
wrap:自動換行
wrap-reverse:自動換行,方向和wrap相反
下圖為正常排序
使用wrap-reverse后縮小瀏覽器時:
justify-content
伸縮項目的排列方式
值:
flex-start:伸縮項目以起始點靠齊
flex-end:伸縮項目以結尾靠齊
center:以中心點靠齊
space-between:伸縮項目平均分布
space-around:同上但兩段保留一般的空間
實例:使用space-around的排列效果
align-items
處理額外空間
值:
flex-start:以頂部為基準,清理底部的額外空間
flex-end:以底部為基準,清理頂部的額外空間
center:以中間為基準,清理上下部分的額外空間
baseline:以基線為基準,清理額外的空間
stretch:伸縮項目填充整個容器,默認值
align-self
處理額外空間
值:與align-items的值一樣,需要用nth-child()設置某一個需要處理的伸縮項目
flex
設置伸縮項目分配比例
p:nth-child(1)
{
flex: 1;
}
p:nth-child(2)
{
flex: 2;
}
p:nth-child(3)
{
flex: 2;
}
p:nth-child(4)
{
flex: 1;
}
order
設置伸縮項目出現的位置
p:nth-child(1)
{
order:2;
}
p:nth-child(2)
{
order:3;
}
p:nth-child(3)
{
order:4;
}
p:nth-child(4)
{
order:1;
}
---------------------
藍藍設計( www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務。
如果您想訂閱本博客內容,每天自動發到您的郵箱中, 請點這里
做了一個項目,有充值功能,充值方式為 微信和支付寶,效果如下:
代碼:
<template>
<el-card class="box-card">
<ul class="msg-box">
<li>
<h4>我要充值</h4>
</li>
<li>
<h4 style="margin-bottom: 15px;">充值金額</h4>
<el-radio-group v-model="amountVal" @change="amountChange">
<el-radio border :label="''+ 100">充值100</el-radio>
<el-radio border :label="''+ 500">充值500</el-radio>
<el-radio border :label="''+ 1000">充值1000</el-radio>
<el-radio border :label="''+ 2000">充值2000</el-radio>
<el-radio border :label="''+ 5000">充值5000</el-radio>
<el-radio border :label="''">自定義</el-radio>
</el-radio-group>
</li>
<li>
<h4 style="margin-bottom: 15px;">充值方式</h4>
<el-radio-group v-model="rechargeParams.paymentType" @change="paymentTypeChange">
<el-radio border :label="''+ 0">微信</el-radio>
<el-radio border :label="''+ 1">支付寶</el-radio>
</el-radio-group>
</li>
<li>
<h4 style="margin-bottom: 15px;">充值金額</h4>
<el-input :disabled="disabled" clearable v-model="rechargeParams.totalAmt" placeholder="請輸入金額" style="width: 150px;"></el-input>
</li>
</ul>
<div style="text-align: center; margin-top: 30px;">
<el-button type="primary" @click="surePay">確認支付</el-button>
</div>
</el-card>
</template>
<script>
export default {
data() {
return {
amountVal: '',
disabled: false,
//充值參數
rechargeParams: {
"totalAmt": '', //金額
"paymentType": "0", //支付方式[0:微信,1:支付寶,2:余額,3:活動]
"transType": "0" //交易類型[0:充值,1:消費]
}
}
},
methods: {
//充值金額
amountChange(val) {
this.rechargeParams.totalAmt = val;
if (val == '') {
this.disabled = false
} else {
this.disabled = true
}
},
//支付方式
paymentTypeChange(val) {
this.rechargeParams.paymentType = val
},
//確認支付
async surePay() {
if (this.rechargeParams.totalAmt == '') {
this.$message.warning('請輸入金額');
return;
}
const res = await this.$http.post('orderInfo/createOrderInfo', this.rechargeParams)
const {
code,
msg,
result
} = res.data
if (code === '200') {
//支付方式跳轉
if (this.rechargeParams.paymentType == '0') {
this.$message.success('微信支付');
this.wechatPay(result);
} else if (this.rechargeParams.paymentType == '1') {
this.$message.success('支付寶支付')
const payDiv = document.getElementById('payDiv');
if (payDiv) {
document.body.removeChild(payDiv);
}
const div = document.createElement('div');
div.id = 'payDiv';
div.innerHTML = result;
document.body.appendChild(div);
document.getElementById('payDiv').getElementsByTagName('form')[0].submit();
} else if (this.rechargeParams.paymentType == '2') {
this.$message.success('余額支付成功');
this.$router.push({
name: 'order'
})
} else {
this.$message.success('活動支付')
}
} else if (code === 401) {
this.$message.error(msg)
this.$router.push({
name: 'login'
})
} else {
this.$message.error(msg)
}
},
//微信支付
wechatPay(result) {
if (result) {
const orderParams = JSON.parse(result);
sessionStorage.qrurl = orderParams.qrurl;
sessionStorage.amt = orderParams.amt;
sessionStorage.returnUrl = orderParams.returnUrl;
sessionStorage.order_id = orderParams.order_id;
this.$router.push({
name: 'wechatPay'
})
}
}
}
}
</script>
<style scoped>
/* 信息列表樣式 */
.msg-box > li {
list-style: none;
border-bottom: 1px solid #c5c5c5;
padding: 20px 10px;
}
</style>
支付寶方式:后臺會返回來一個form,然后提交form自動跳轉到支付寶支付頁面。
微信方式:需要自己根據后臺返回的url生成二維碼頁面,如圖所示:
代碼:
<template>
<div class="payBox">
<div class="img-logo">
<img src="http://img.huoxingbeidiao.com/public/WePayLogo.png" alt="">
</div>
<div class="info-box">
<div style="padding-bottom: 20px;">
<qrcode-vue :value="qrurl" :size="200" level="H"></qrcode-vue>
</div>
<img src="http://img.huoxingbeidiao.com/public/WePayInfo.png" alt="">
<p class="price">¥ {{amt}}</p>
</div>
</div>
</template>
<script>
import QrcodeVue from 'qrcode.vue'
export default {
data() {
return {
amt: 0,
qrurl: '',
timer: null
}
},
components: {
QrcodeVue
},
methods: {
getOrderInfo() {
if (sessionStorage.qrurl && sessionStorage.amt) {
this.qrurl = sessionStorage.qrurl;
this.amt = sessionStorage.amt;
}
},
startLoop() {
this.timer = setInterval(() => {
this.isPaySuccess()
}, 3000)
},
async isPaySuccess() {
const orderId = sessionStorage.order_id;
const res = await this.$http.get('orderInfo/queryOrder?orderId=' + orderId)
const {
code,
msg,
resultList
} = res.data
if (code === '200') {
clearInterval(this.timer);
this.timer = null;
sessionStorage.removeItem('qrurl');
sessionStorage.removeItem('amt');
sessionStorage.removeItem('order_id');
sessionStorage.removeItem('returnUrl');
setTimeout(() => {
this.$router.push({
name: 'order'
})
}, 3000)
} else if (code === 401) {
clearInterval(this.timer);
this.timer = null;
sessionStorage.removeItem('qrurl');
sessionStorage.removeItem('amt');
sessionStorage.removeItem('order_id');
sessionStorage.removeItem('returnUrl');
this.$message.error(msg)
this.$router.push({
name: 'login'
})
} else {
}
}
},
created() {
this.getOrderInfo()
this.startLoop()
},
beforeDestroy() {
clearInterval(this.timer)
this.timer = null
}
}
</script>
<style scoped>
.payBox {
width: 1000px;
margin: 0 auto;
}
.payBox .img-logo {
padding: 20px 0;
text-align: center;
}
.payBox .img-logo img {
width: 180px;
}
.info-box {
padding: 60px 0;
border-top: 3px solid #F43B66;
-webkit-box-shadow: 0 0 32px 0 rgba(0, 0, 0, .18);
box-shadow: 0 0 32px 0 rgba(0, 0, 0, .18);
text-align: center;
}
.info-box .price {
color: #F43B66;
font-size: 40px;
padding-top: 20px;
padding-bottom: 20px;
border-bottom: 1px solid #f1f1f1;
}
</style>
需要安裝qrcode.vue
npm install --save qrcode.vue 或 yarn add qrcode.vue
藍藍設計( www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務。
如果您想訂閱本博客內容,每天自動發到您的郵箱中, 請點這里
ID選擇器>類選擇器>標簽選擇器
行內樣式>內部樣式>外部樣式
*結構偽類選擇器 一旦設置 不管在行內還是在后面重新設置,都沒辦法改變
*結構偽類選擇器設置奇偶行以及從第三行開始變色如何實現
nth-cild(2N+3)表示從第三行開始的奇數行
同理 nth-child(2N+4)表示從第四行開始的偶數行
nth-cild(2N+5)表示從第五行開始的奇數行
*設置前三個:
p:nth-child(-n+3){
background-color: #b3d4fc;
}
*使用E F:nth-child(n)和E F:nth-of-type(n)的 關鍵點
E F:nth-child(n)在父級里從一個元素開始查找,不分類型
E F:nth-of-type(n)在父級里先看類型,再看位置
注意
child 跟子選擇器沒有關系,可以是子選擇,也可以是后代選擇 由層次選擇器 (如table tr)來控制
1.層次選擇器
table td 后代選擇器 td包含在table里
div>p子選擇器 p是div的子元素
p1+p p1后面一個兄弟p變化 是對p進行處理 p1不變 而且只是下面相鄰的變化 上面相鄰不變化
p1~p p1后面所有兄弟p變化 p1不變
2.結構偽類選擇器
P:first-child 作為父元素的第一個子元素得元素 p
P:last-child 作為父元素的最后一個子元素得元素 p
P a :nth-child(n) p中第n個a元素 (even)(odd)
p:first-of-type 必須是p元素 不是子元素也行
p a:nth-of-type(n)
必須是a元素 不是a的子元素也行
3.屬性選擇器
a[id] a標簽中含有id的
a[id=111] a標簽中的id=111的
a[href*=http] a標簽中包含href屬性 且都包含http
a[href&=png] a標簽中包含href屬性 且最后以png結尾
a[href^=http://] a標簽中包含href屬性且以http://開頭
1.層次選擇器
*相鄰兄弟選擇器,只對后面的兄弟有作用,對前面的兄弟沒效果。
*相鄰兄弟選擇器,E+F E不會產生效果效果
2.結構偽類選擇器
使用E F:nth-child(n)和E F:nth-of-type(n)的 關鍵點
E F:nth-child(n)在父級里從一個元素開始查找,不分類型
E F:nth-of-type(n)在父級里先看類型,再看位置
注意
child 跟子選擇器沒有關系,可以是子選擇,也可以是后代選擇 由層次選擇器 (如table tr)來控制
設置前三個:
p:nth-child(-n+3){
background-color: #b3d4fc;
}
3.屬性選擇器
舉例:
a[href=^http]{backgroud-color:red}
藍藍設計( www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務。
如果您想訂閱本博客內容,每天自動發到您的郵箱中, 請點這里
微信小程序動畫之點擊效果
代碼:
js:
// pages/test/test.js
Page({
containerTap: function (res) {
var that = this
var x = res.touches[0].pageX;
var y = res.touches[0].pageY + 85;
this.setData({
rippleStyle: ''
});
setTimeout(function () {
that.setData({
rippleStyle: 'top:' + y + 'px;left:' + x + 'px;-webkit-animation: ripple 0.4s linear;animation:ripple 0.4s linear;'
});
}, 200)
},
})
wxml:
<view class="ripple" style="{{rippleStyle}}"></view>
<view class="container" bindtouchstart="containerTap"></view>
wxss:
page{height:100%}
.container{
width:100%;
height:100%;
overflow: hidden
}
.ripple {
background-color:aquamarine;
border-radius: 100%;
height:10px;
width:10px;
margin-top: -90px;
position: absolute;
overflow: hidden
}
@-webkit-keyframes ripple {
100% {
webkit-transform: scale(12);
transform: scale(12);
background-color: transparent;
}
}
藍藍設計( www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、網站建設 、平面設計服務。
如果您想訂閱本博客內容,每天自動發到您的郵箱中, 請點這里
這周萌芽決定好好學習一下ES6,感興趣的小伙伴們來一起學習吧~
ES6(ES2015)——IE10+、Chrome、FireFox、移動端、Node.js
編譯轉換
1.在線轉換(browser.js)
2.提前編譯
ES6新特性
1.變量
2.函數
3.數組
4.字符串
5.面向對象
6.promise(串行化異步交互)
7.generator(把同步拆分為異步)
8.模塊化(ES6自帶模塊化)
變量
var
1.可以重復聲明
2.無法限制修改
3.沒有塊級作用域(沒有語法塊!)
let 不能重復聲明(變量,可以修改)
const 不能重復聲明(常量,不能修改)
塊級作用域,let在外部無法調用
函數
箭頭函數 =>
function show(){
//這是我們平常的函數
}
let show=()=>{
//箭頭函數
}
//區別不大,把function省略掉換成箭頭,主要就是為了方便,可傳參
1.如果只有一個參數,()可以省去。
let show=a=>{
return a*2
}
2.如果只有一個return,{}可以省略
let show=a=>a*2;
let arr = [15,2,37,11,67,4,6]; //排序
arr.sort((n1,n2)=>{
return n1-n2;
})
arr.sort((n1,n2)=> n1-n2 );
console.log(arr)
函數的參數
1.參數擴展/展開
2.默認參數
參數的擴展
1.收集參數
function arrData(a,b,...args) {
alert(a);
alert(b);
alert(args);
}
*注意:Rest Parameter必須是最后一個(除其他語言)
1
2.展開數組
arrData(...arr); //等價于 arrData(1,2,3);
1
這仨點兒【…】代表把數組內容掏出來放這。
默認參數
//jQuery中的默認參數
$('#div1').animate({width:'200px'});
$('#div1').animate({width:'200px'},1000);
//ES6默認傳參
function showOne(a,b=10,c=5) {
console.log(a,b,c)
}
解構賦值
1.左右兩邊解構必須一樣
2.右邊必須是個合法的東西
3.聲明和賦值不能分開(必須在一句話里完成)
let [one,two,three] = [10,20,30];
let {one1,two2,three3} = {a:10,b:20,c:30};
數組
map 映射(一個對一個。傳幾個返回幾個)
let result = arr.map(function (item) {
return item*2;
});//簡寫一下
let result = arr.map(item=>item*2 );
//判斷考試成績
let score = [19,18,78,65,100];
let result = score.map(item=>item>=60?'及格':'不及格');
reduce 匯總(算個總數,算個平均數)
//tmp:上次求和總和,為兩兩相加,如果之前沒有結果則為傳進來的數組的第一個數。
//itme:當前的數。
//index:執行的次數。
let result = arr.reduce(function (tmp, item, index) {
return tmp + item;
});
//簡寫
arr.reduce((tmp, item, index)=>tmp + item);
filter 過濾器(篩選掉不需要的)
let result = arr.filter(item=>{
if (item%2 == 0){
return true;
} else {
return false;
}
});
//簡寫
let result = arr.filter(item=>item%2 == 0);
//***萌芽在這里提一下!json和之前的item都不是固定的,可以隨便命名。意思都是當前的值!
let arrProce = [
{title:'男士襯衫',price:75},
{title:'女士包包',price:5000},
{title:'男士包包',price:20},
{title:'女士鞋',price:2500}
];
let result = arrProce.filter(jsom=>json.price >= 2000);
console.log(result);
forEach循環(迭代)
arr.forEach((item,index)=>{
alert(index+":"+item)
})
字符串
1.多了倆新方法
startsWith(); //判斷開頭,返回布爾類型
endWith(); //判斷結尾,返回布爾類型
let str='hello,world!'
str.startsWith('h')
str.endWith('!') //返回true
2.字符串模板
字符串連接
2.1直接把東西塞進字符串里面 ${東西}
2.2可以折行
<h1>${title}</h1>
<p>${content}</p>
ES6的面向對象
1.class關鍵字,構造器和類分開啦。
2.class里面直接加方法。
class User{
constructor(name,password){ //構造器
this.name = name;
this.password = password;
}
showName(){
alert(this.name);
}
showPass(){
alert(this.password);
}
}
var user = new User('萌芽子','123456');
user.showName();
user.showPass();
繼承
class VipUser extends User{
constructor(name,password,age){
super(name,password); //super 超類
this.age = age;
}
showAge(){
alert(this.age)
}
}
var user = new VipUser('萌芽子','123456','18歲');
user.showName();
user.showPass();
user.showAge();
不得不說作為一只JAVA汪,這種寫法真得勁!
面向對象的應用
React
1.組件化(class)
2.JSX(JSXbabelbrowser.js)
JSX屬于JS的擴展版
class Test extends React.Component{
constructor(...args){
super(...args);
}
render(){
return <li>{this.props.str}</li> //props:屬性
}
}
window.onload = function(){
let oDiv = document.getElementById('div1');
ReactDOM.render(
<ul>
<Item str="你好"></Item>
<Item str="世界!"></Item>
</ul>
oDiv
);
};
打卡,下次就學這個了!===============
json
1.JSON對象
JSON.stringify() json轉字符串
let json = {a:10, b:20};//JSON.stringify 字符串化
let str = 'http://www.baidu.com/path/user?data='+JSON.stringify(json);
str = 'http://www.baidu.com/path/user?data='+encodeURIComponent(JSON.stringify(json));
alert(str)
1
2
3
4
JSON.parse() 字符串轉json
let str = '{"a":12,"b":20,"c":"可樂"}';
let json = JSON.parse(str);
console.log(json);
2.簡寫
在新版的ES6當中名字一樣的鍵(key)和值(value)可以只寫一個。
let a = 12;
let b = 5;
let json = {a,b,c:21};
簡化了JSON中的方法。
let json ={
a:12,
showJson(){
alert(this.a);
}
};
json.showJson();
json的標準寫法:
1.只能用雙引號
2.所有的名字都必須用引號包起來(所有的key都必須是雙引號)
{a:12,b:5} × 錯誤的寫法
{"a":"萌萌萌","b":"芽子"} √ 正確的寫法
1
2
Promise(承諾)
異步:操作之間沒啥關系,同時進行多個操作
同步:同時只能做一件事
優缺點:
異步:代碼更復雜
同步:代碼簡單
Promise——消除異步操作
*用同步一樣的方式來書寫異步代碼;
let p = new Promise(function (resolve,reject) {
//異步代碼
//resolve——成功
//reject——失敗
})
-----------------------------------------訪問我們的arr.txt文件,這里用到了jQuery的ajax就不詳細介紹了。
let p = new Promise(function (resolve, reject) {
//異步代碼
//resolve——成功
//reject——失敗
$.ajax({
url: 'arr.txt',
dataType: 'json',
success(arr) {
resolve(arr);
}, error(err) {
reject(err);
}
})
});
//結果
p.then(function (arr) {
alert('成功啦' + arr)
}, function (err) {
alert('失敗了' + err)
console.log(err)
});
-----------------------------------------------多個請求地址
Promise.all([p1,p2]).then(function (arr){
let [res1,res2] = arr;
alert('全部成功啦');
console.log(res1);
console.log(res2);
},function (){
alert('至少有一個失敗了');
});
----------------------------再簡化
function createPromise(url){
return new Promise(function (resolve, reject) {
$.ajax({
url,
dataType: 'json',
success(arr) {
resolve(arr);
}, error(err) {
reject(err);
}
})
});
}
Promise.all([
createPromise('arr.txt'),
createPromise('json.txt')
]).then(function (arr){
let [res1,res2] = arr;
alert('全部成功啦');
console.log(res1);
console.log(res2);
},function (){
alert('至少有一個失敗了');
});
----------------------完美寫法
Promise.all([
$.ajax({url:'arr.txt',dataType:'json'}),
$.ajax({url:'json.txt',dataType:'json'})
]).then(function (results) {
let [arr,json] = results;
alert("成功了");
console.log(arr,json)
},function () {
alert("失敗了")
})
我們有了promise之后的異步:
Promise.all([ $.ajax(),$.ajax() ]).then( results=>{
//對了
},err=> {
//錯了
})
Promise.all (必須全部成功)
Promise.race(同事讀多個數據,即使失敗也沒關系)
generator(生成器)
普通函數 - 一路到底執行不可中斷
generator函數 - 可中斷
function * show() {
alert('a');
yield;//暫時放棄執行
alert('b');
}
let genObj = show();
genObj.next();
genObj.next();
yield
yield傳參
function * show(num1,num2) {
alert(`${num1},${num2}`);//es6
alert('a');
let a = yield;//暫時放棄執行
console.log(a);
alert('b');
}
let genObj = show(99,88);
genObj.next(12);//第一個next無法給yield傳參的,廢的
genObj.next(5);
yield返回
function *show() {
alert('a');
yield 12;
alert('b');
return 55;
}
let gen = show();
let res1 = gen.next();
console.log(res1); //{value: 12, done: false}
let res2 = gen.next();
console.log(res2);//{value: undefined, done: true} 加了return {value: 55, done: true}
還沒做的菜叫函數參數,過程是yield之前函數里面的東西,干凈的菜,切好的菜是中間過程也就是yield,最終我們將它返回出去!不得不說這圖很生動。
異步操作
1.回調
$.ajax({
url:'url',
dataType:'json',
success(data){
$.ajax({
url:'xxx',
dataType: 'json',
success(data) {
//完事兒了
},error(err) {
alert('錯了')
}
})
},error(){
alert('失敗')
}
})
2.Promise
Promise.all([
$.ajax({url:xxx,dataType:'json'}),
$.ajax({url:xxx,dataType:'json'}),
$.ajax({url:xxx,dataType:'json'})
]).then(results=>{
//完事兒
},err=>{
//錯誤的
})
3.generator
runner(function *(){
let data1 = yield $.ajax({ulr:xxx,dataType:'json'});
let data2 = yield $.ajax({ulr:xxx,dataType:'json'});
let data3 = yield $.ajax({ulr:xxx,dataType:'json'});
})
1
2
3
4
5
generator(不能用=>函數)
邏輯判斷下非常好用。
Promise:一次讀一堆。
generator:邏輯性。
runner(function *(){
let userData = yield $.ajax({url:'getUserData',dataType:'json'});
if(userData.type == 'VIP'){
let items = yield $.ajax({url:'getVIPItems',dataTyoe:'jsom'});
}else{
let items = yield $.ajax({url:'getItems',dataTyoe:'jsom'});
}
//生成...
}
})
總結
1.變量:
var:能重復聲明、函數級
let: 嚴格的,不能重復聲明,塊級,變量
const:嚴格的,不能重復聲明,塊級,常量
2.箭頭函數
2.1方便
i.如果只有一個參數,()可以省
ii.如果只有一個return,{}可以省
2.2修正了this
this相對正常點
3.參數擴展
…能收集
…能擴展
默認參數
4.數組方法
map 映射
reduce 匯總
filter 過濾
forEach 循環
5.字符串
starsWith/endWith
字符串模板:${a}xxx
6.Promise
封裝異步操作
Promise.all([]);
7.generator
function *show(){
yield
}
8.JSON
JSON.stringify({ a :12,b :5}) => {“a”:12,“b”:5}
JSON.parse(’{“a”:12,“b”:5}’) =>{a:12,b:5}//字符串
9.解構賦值
let [a,b,c] = [12,5,8];
左右結構一樣,右邊是個合法的東西,連生命帶賦值一次完成。
10.面向對象
class Test(){
constructor(xxx){
this = xxx
}
方法1(){
}
方法2(){
}
}
繼承
class Test2 extends Test(){
constructor(){
super();
}
}
談談ES7和ES8
1.數組includes
數組是否包含某個東西
2.數組 keys/values/entries
for…in(循環數組)
對于數組來講循環的是下標
對于json循環的是key
for…of(循環迭代器)
對于數組循環的是值
不能用于JSON,json并不是迭代器
keys = >所有的key拿出來 0,1,2,3…
values =>所有的values拿出來 23,5,8,1…
entries =>所有的鍵值對拿出來 {key:0,value:a}
let arr = [12,5,8,99];
for(let [key,value] of arr.entries()){
alert(`${key} = ${value}`);
預覽版,目前極大多數瀏覽器都不支持,以后可能會支持,了解一下就好。
藍藍設計( www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、網站建設 、平面設計服務。
藍藍設計的小編 http://www.syprn.cn