VUE快速入門與實踐(上)
阿新 • • 發佈:2018-12-11
簡介
vue官網 vue.js是一套構建使用者介面的漸進式框架。 vue全家桶:vue.js+vue-router+vuex+axios
特點
- 核心只關注檢視層(view)
- 易學,輕量,靈活的特點
- 適用於移動端專案
- 漸進式框架
漸進式的理解
- 宣告式渲染(無需關係如何實現)
- 組建系統
- 客戶端路由(vue-router)
- 大規模狀態管理(vuex)
- 構建工具(vue-cli)
核心
- 響應的資料變化。當資料發生改變->試圖的自動更新
- 組合的檢視元件。ui頁面對映為組建樹,劃分組建可維護、可複用、可測試
安裝vue
- 安裝vue
npm install vue
- 安裝bootstrap
npm install bootstrap
- 安裝 axios
npm install axios
- 初始化npm
npm init -y
,初始化生成一個新的 package.json 檔案,如果使用了 -f(代表force)、-y(代表yes),則跳過提問階段,直接生成一個新的 package.json 檔案。
指令
v-model
:用於表單中的input ,textarea中,會忽略value,checked,selected,將資料繫結在試圖上,檢視修改後會影響資料的變化。同angularJS中的ng-modelv-text
:雙向繫結資料,同{{}}。<div v-text="msg"></div>
v-cloak
v-once
:一次繫結資料。<div v-once>{{msg}}</div>
v-html
:繫結html,一定是安全資料。<div v-html="msg"></div>
v-for
:迴圈陣列,同angularJS中的指令ng-repeat 例:o in arr 或 (o,index) in arr
a in obj 或 (value,key,index) in obj
v-bind
:動態繫結屬性。v-bind:src="" 或 :src=""
v-if
:同angularJSng-if
,if操作的是domv-show
:同angularJSng-show
,show操作的是樣式。如果頻繁的切換dom使用v-show
,如果一開始就確定下來使用v-if
更好,如果if不通過內部指令不會再執行。只用dom從顯示到隱藏或隱藏到顯示菜使用vue動畫- 繫結事件
v-on:click="fn" 或 @click="fn"
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>事件</title>
</head>
<body>
<div id="app">
<!-- 如果不傳遞引數,則不要寫括號會自動傳入事件源,如果寫括號要手動傳入$event -->
<div v-on:mousedown="fn()">點我</div><br>
<!-- 事件繫結可以用@代替-->
<div @mousedown="fn2(2)">點我</div>
</div>
<script src="node_modules/vue/dist/vue.min.js"></script>
<script>
let vm=new Vue({//根例項
el:'#app',
methods:{//methods和data中的資料會全部放到vm上,而且名字不能衝突,衝突會報錯,methods中的this指向的都是例項,不能使用箭頭函式
fn(){alert(this.a)},
fn2(i){alert(i)}
},
data:{
a:[1,2,3,4,5],
}
});
</script>
</body>
</html>
資料響應的變化
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>資料響應的變化(物件)</title>
</head>
<body>
<div id="app">
{{msg}}{{a.school}}
</div>
<script src="node_modules/vue/dist/vue.min.js"></script>
<script>
//引入vue後會給一個Vue的建構函式
//vue會迴圈data中的資料(資料劫持),依次的增加getter和setter
let vm=new Vue({//vm===viewModel
el:'#app',//vue控制的div
data:{//data的中的資料會被vue代理
msg:'hello world!',//可以通過vm.msg取得相對應的資料
a:{school:''}//1.使用變數時先進行初始化,否則新新增的屬性不會導致頁面重新整理(推薦)
}
});
//2.如果沒有初始化資料,可以通過$set方法來給物件新增響應式資料
vm.$set(vm.a,'school',1);
//3.通過替換原物件初始化資料
vm.a={school:''};
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>資料響應的變化(陣列)</title>
</head>
<body>
<div id="app">
{{arr}}
</div>
<script src="node_modules/vue/dist/vue.min.js"></script>
<script>
let vm=new Vue({
el:'#app',
data:{
arr:[1,2,3,4,5],
}
});
//去改變陣列中的某一項時監控不到的,也不能使用改變陣列長度的方法
//錯誤:vm.arr[0]=100;vm.arr.length=2;
//可以使用變異方法:pop push shift unshift sort reserve splice
vm.arr=vm.arr.map(item=>item*3);//filter map
</script>
</body>
</html>
checkbox,select,radio
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>checkbox,select,radio</title>
</head>
<body>
<div id="app">
<div>
<input type="checkbox" v-model="hobby" value="0">游泳
<input type="checkbox" v-model="hobby" value="1">跑步
<input type="checkbox" v-model="hobby" value="2">射箭<br>
愛好:{{hobby}}
</div><br>
<div>
<select v-model="address">
<option value="" disabled>請選擇</option>
<option value="1">上海</option>
<option value="2">北京</option>
<option value="3">天津</option>
</select><br>
地址:{{address}}
</div><br>
<div>
<input type="radio" v-model="sex" value="1">男
<input type="radio" v-model="sex" value="2">女
性別:{{sex}}
</div>
</div>
<script src="node_modules/vue/dist/vue.min.js"></script>
<script>
let vm=new Vue({
el:'#app',
data:{
hobby:[],
address:'',
sex:''
}
});
</script>
</body>
</html>
Promise
//回撥函式:將後續的處理邏輯傳入到當前要做的事,事情做好後呼叫此函式
function buy(callback) {
setTimeout(()=>{
let a='蘑菇';
callback(a);
},2000);
}
buy(function (val) {
console.log(val);
});
//可用promise處理回撥問題,共三個狀態:成功、失敗、等待
//resolve對應data,reject對應err,resolve和reject均是函式
//promise的例項就是一個then方法,then方法中有兩個引數
var p=new Promise((resolve,reject)=>{
setTimeout(()=>{
let a='青椒';
resolve(a);
},2000);
});
p.then((data)=>{
console.log(data);
},(err)=>{
});
//promise示例
function buyPack() {
return new Promise((resolve,reject)=>{
setTimeout(()=>{
if(Math.random()>0.5){
resolve('買');
}else {
reject('不買');
}
},Math.random()*1000);
});
}
buyPack().then((data)=>{
console.log(data);
},(data)=>{
console.log(data);
});
Promise-ajax
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>promise-ajax</title>
</head>
<body>
<div id="app">
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<!-- 基於promise -->
<script src="assets/js/promise-ajax.js"></script>
<script>
let vm=new Vue({
el:'#app',
created(){
ajax({url:'carts.json'}).then((data)=>{
console.log(data);
})
},
data:{
products:[]
}
});
function ajax({url='',type='get',dataType='JSON'}) {
return new Promise((resolve,reject)=>{
let xhr=new XMLHttpRequest();
xhr.open(type,url,true);
xhr.responseType=dataType;
xhr.onload=function () {//shr.readyState=4 shr.status=200
if(xhr.status==200){
resolve(xhr.response);//成功呼叫成功的方法
}else{
reject('not found');//失敗呼叫失敗的方法
}
};
xhr.onerror=function (err) {
reject(err);//失敗呼叫失敗的方法
};
xhr.send();
});
}
</script>
</body>
</html>
carts.json檔案
[
{
"isSelected":true,
"name":"由淺入深學習vue",
"img":"https://img10.360buyimg.com/cms/s80x80_jfs/t18310/260/2349409676/18067/46f30b6f/5aeffa25Nc7c83599.jpg",
"info":"小米(MI) 米家自動傘 男女通用晴雨兩用 一鍵自動開合 黑色",
"price":99,
"count":3
},
{
"isSelected":true,
"name":"由淺入深學習vue",
"img":"https://img10.360buyimg.com/cms/s80x80_jfs/t18310/260/2349409676/18067/46f30b6f/5aeffa25Nc7c83599.jpg",
"info":"小米(MI) 米家自動傘 男女通用晴雨兩用 一鍵自動開合 黑色",
"price":99,
"count":3
}
]
axios
更多關於axios,點選這裡
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>axios</title>
</head>
<body>
<div id="app">
<input type="number" v-model.number="num">
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<!-- 基於promise -->
<script src="node_modules/axios/dist/axios.js"></script>
<script>
let vm=new Vue({
el:'#app',
//專門用來發送Ajax的方法
created(){//再資料被初始化後會呼叫(鉤子函式),this指向是vm例項
let that=this;
axios.get('carts.json').then(function (res) {
that.products=res.data;
},function (err) {
console.log(err);
});
//也可以用箭頭函式
axios.get('carts.json').then(res=> {
this.products=res;
},err=> {
console.log(err);
})
},
data:{
products:[],
num:0
}
});
</script>
</body>
</html>
filter,created,computed
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>購物車</title>
<link type="text/css" rel="stylesheet" href="node_modules/bootstrap/dist/css/bootstrap.css">
</head>
<body>
<div id="app">
<div class="container">
<table class="table table-bordered table-hover">
<tr>
<th>全選<input type="checkbox" v-model="selectAll"></th>
<th>商品</th>
<th>描述</th>
<th>單價</th>
<th>數量</th>
<th>總價</th>
<th>操作</th>
</tr>
<tr v-for="(product,index) in products">
<td><input type="checkbox" v-model="product.isSelected"></td>
<td><img :src="product.img">{{product.name}}</td>
<td>{{product.info}}</td>
<td>{{product.price}}</td>
<td><input type="number" v-model.number="product.count" min="1"></td>
<td>{{product.price*product.count|toFixed(2)}}</td>
<td><input type="button" class="btn btn-danger" value="刪除" @click="remove(product)"></td>
</tr>
<tr>
<td>總價</td>
<td>{{sum}}</td>
</tr>
</table>
</div>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<!-- 基於promise -->
<script src="node_modules/axios/dist/axios.js"></script>
<script>
let vm=new Vue({
el:'#app',
computed:{//用來計算
selectAll:{//放棄賦予複選框change方法後,可以通過“計算屬性”實現全選與單選
//當products值變化時會重新計算
get(){//get和set中的this指向例項,預設v-model會獲取selectAll的值,多以呼叫get方法
return this.products.every(p=>p.isSelected);
},set(val){//當給checkbox賦值時
this.products.forEach(p=>p.isSelected=val);
}
},
sum(){//如果計算屬性寫成函式,預設呼叫get方法,等價於下面的sum屬性
return this.products.reduce((pre,next)=>{
if(!next.isSelected) return pre;
return pre+next.price*next.count;
},0)
},
// sum:{//總價,sum結果會被快取,如果依賴的資料沒有變化就不會重新執行
// get(){
// return this.products.reduce((pre,next)=>{
// if(!next.isSelected) return pre;
// return pre+next.price*next.count;
// },0)
// }
// }
},
filters:{//過濾器
toFixed(n,s){//這裡的this指的是window
return '¥'+n.toFixed(s);
}
},
created(){//在資料被初始化後會呼叫(鉤子函式),this指向是vm例項
this.getData();
},
methods:{
// checkOne(){//單選
// this.selectAll=this.products.every(item=>item.isSelected);
// },
// checkAll(){//全選/反選
// this.products.forEach(item=>item.isSelected=this.selectAll);
// },
// sum(){//總價,資料只要有變化就會呼叫此方法,不會快取上一次的結果,computed會解決這個問題
// return this.products.reduce((pre,next)=>{
// return pre+next.price*next.count;
// },0)
// },
remove(p){//刪除
this.products=this.products.filter(item=>item!=p);
},
getData(){
axios.get('carts.json').then(res=> {
this.products=res.data;
// this.checkAll();
},err=> {
console.log(err);
})
}
},
data:{
products:[]
// selectAll:false
}
});
</script>
</body>
</html>
全域性filter
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>全域性filter</title>
</head>
<body>
<div id="app1">{{info|select}}</div>
<div id="app2">{{info|select}}</div>
<script src="node_modules/vue/dist/vue.min.js"></script>
<script>
Vue.filter('select',(data)=>data+'小紅');//全域性filter
new Vue({
el:'#app1',
// filters:{
// choose(data){
// return data+'小紅';
// }
// },
data:{
info:'hello'
}
});
new Vue({
el:'#app2',
data:{
info:'hello'
}
});
</script>
</body>
</html>
vue動畫
v-if
:操作dom,可以和v-else-if,v-else連寫v-show
:操作的是樣式
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vue動畫</title>
<link type="text/css" rel="stylesheet" href="node_modules/animate.css/animate.css">
<style>
.one-enter{opacity: 1;}
.one-leave-active{opacity: 0;transition: 1s linear}
/*如果只有一個動畫可這樣寫*/
/*.v-enter{opacity: 1;}*/
/*.v-leave-active{opacity: 0;transition: 1s linear}*/
</style>
</head>
<body>
<div id="app">
<input type="button" @click="flag=!flag" value="切換">
<transition name="one"><!-- 當存在多個動畫時,可設定name區分 -->
<div style="width: 100px;height: 100px;background: red;" v-show="flag"></div>
</transition>
<input type="text" v-model="query">
<transition-group name="two" enter-active-class="animated bounceInUp" leave-active-class="animated bounceOutDown">
<div v-for="(a,index) in newArr" :key="Math.random()">{{a}}</div>
</transition-group>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
let vm=new Vue({
el:'#app',
computed:{
newArr(){
return this.arr.filter(item=>item==this.query);
}
},
data:{
query:'',
arr:[1,2,3,4,5],
flag:true
}
});
</script>
</body>
</html>
v-if
和v-else
例子
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>v-if,else</title>
</head>
<body>
<div id="app">
<div v-if="flag">紅色</div>
<div v-else>藍色</div>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
let vm=new Vue({
el:'#app',
data:{
flag:false
}
});
</script>
</body>
</html>
修飾符
可進入官網 檢視修飾符內容
表單輸入繫結修飾符
.lazy
:在預設情況下,v-model
在每次 input 事件觸發後將輸入框的值與資料進行同步 (除了上述輸入法組合文字時)。你可以新增 lazy 修飾符,從而轉變為使用change
事件進行同步:.number
:限制輸入框只能填寫數字 。<input type="number" v-model.number="num">
.trim
:自動過濾使用者輸入的首尾空白字元。<input v-model.trim="msg">
事件修飾符
.stop
:阻止繼續冒泡,向上傳播。阻止事件傳播。同e.stopPropagation
和cancelBubble=true
效果相同.capture
:停止捕獲,向下傳播。阻止事件傳播。類似:xxx.addEventListener('clicl',fn,true)
.prevent
:阻止預設行為。如a標籤的跳轉。類似:e.preventDefault
和returnValue=false
.once
:只觸發一次.self
:只有觸發自身事件源才會呼叫。類似:e.srcElement&&e.target
<!-- 新增事件監聽器時使用事件捕獲模式 -->
<!-- 即元素自身觸發的事件先在此處處理,然後才交由內部元素進行處理 -->
<div v-on:click.capture="doThis">...</div>
<!-- 只當在 event.target 是當前元素自身時觸發處理函式 -->
<!-- 即事件不是從內部元素觸發的 -->
<div v-on:click.self="doThat">...</div>
computed
computed
計算屬性,它不是方法。- 方法不會快取,
computed
會根據依賴的屬性進行快取(歸vue管理的資料,可以響應式變化的)。 - 由
get
和set
兩部分組成(不能只寫set
,可以只寫get
)。一般情況下,通過js賦值影響其他元素或者表單元素設定值的時候會呼叫set方法。 - computed預設呼叫get方法,必須要有return,而且不支援非同步
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>computed</title>
</head>
<body>
<div id="app">
<input type="text" v-model="a">{{msg}}
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
let vm=new Vue({
el:'#app',
computed:{//computed預設呼叫get方法,必須要有return,而且不支援非同步
msg(){
if(this.a.length<3) return '少了';
if(this.a.length>6) return '多了';
return '';
}
},
data:{
a:''
}
});
</script>
</body>
</html>
watch
- 偵聽器
- 只有值變化的時候才會觸發,並且支援非同步。其他情況我們更善於使用computed
- watch的屬性名字要和觀察的名字一樣
- 當你有一些資料需要隨著其它資料變動而變動時,你很容易濫用 watch——特別是如果你之前使用過 AngularJS。然而,通常更好的做法是使用計算屬性而不是命令式的 watch 回撥。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>watch</title>
</head>
<body>
<div id="app">
<input type="text" v-model="a">{{msg}}
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
let vm=new Vue({
el:'#app',
watch:{//只有值變化的時候才會觸發,並且支援非同步。其他情況我們更善於使用computed
a(newVal,oldVal){//watch的屬性名字要和觀察的名字一樣
if(newVal.length<3){
this.msg='太少';
}else if(newVal.length>6){
this.msg='太多';
}else{
this.msg='';
}
}
},
data:{
a:'',
msg:''
}
});
</script>
</body>
</html>
template
template
標籤是vue提供給我們的沒有任何意義,只是用來包裹元素用的。v-show
不支援template
。- 預設情況下切換dom時相同的結構會被複用,如果不需要複用,需要新增key屬性
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>v-if,else</title>
</head>
<body>
<div id="app">
<!-- template標籤是vue提供給我們的沒有任何意義,只是用來包裹元素用的。v-show不支援template-->
<template v-if="flag">
<div>紅色</div>
<div>紅色</div>
<div>紅色</div>
<div>紅色</div>
<div>紅色</div>
</template>
<div v-else>藍色</div>
<br><br>
<!-- 預設情況下切換dom時相同的結構會被複用,如果不需要複用,需要新增key-->
<input type="button" value="切換" @click="cut=!cut">
<template v-if="cut">
<label>登入</label>
<input type="text" key="1">
</template>
<template v-else>
<label>註冊</label>
<input type="text" key="2">
</template>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
let vm=new Vue({
el:'#app',
data:{
flag:true,
cut:true
}
});
</script>
</body>
</html>
:class :style
class和style的繫結有兩種形式:一、物件,二、陣列
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>v-bind-class,v-bind-style</title>
<style>
.x {color: red;}
.y {font-size: 30px;}
.z {background: antiquewhite;}
</style>
</head>
<body>
<div id="app">
<p :class="{x:flag,y:true,z:false}">測試</p>
<p :class="[class1,class2,{z:true}]">測試</p>
<p :style="{color:'red',fontSize:'30px',background:'antiquewhite'}">測試</p>
<p :style="[style1,style2,{background:'antiquewhite'}]">測試</p>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
let vm=new Vue({
el:'#app',
data:{
class1:'x',
class2:'y',
style1:{color:'red'},
style2:{fontSize:'30px'},
flag:true,
cut:true
}
});
</script>
</body>
</html>
實現單頁開發的方式
- 通過hash(#)記錄跳轉的路徑(可以產生歷史管理)
- 瀏覽器自帶的歷史管理的方法history(history。pushState()),但可能會導致404錯誤
- 開發時使用hash的方法,上線的時候使用history的方法
自定義指令
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>自定義指令</title>
<style>
.box {
width: 100px;
height: 100px;
background: red;
position: absolute;;
}
</style>
</head>
<body>
<div id="app">
<button v-color="flag">變色</button>
<div class="box" v-drag></div>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
let vm=new Vue({
el:'#app',
directives:{
color(el,bindings){//el指代的是元素本身,這裡指的是button
el.style.background=bindings.value;
},
drag(el,bindings){
el.onmousedown=function(e){
var disX=e.pageX-el.offsetLeft;
var disY=e.pageY-el.offsetTop;
document.onmousemove=function (e) {
el.style.left=e.pageX-disX+'px';
el.style.top=e.pageY-disY+'px';
};
e.preventDefault();
}
}
},
data:{
flag:'red'
}
});
</script>
</body>
</html>
元件
- 分類是頁面級的,1.一個頁面是一個元件;2.將可複用的部分抽離出來形成的基礎元件
- 全域性元件:可以宣告一次在任何地方使用
- 區域性元件:必須告訴這個元件屬於誰
- 一般寫外掛用的是全域性元件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>全域性元件</title>
</head>
<body>
<div id="app">
<my-remark></my-remark>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
//1.元件名不要帶有大寫,多個單詞用-連線
//2.只要元件名和定義名字相同就可以,但只有首字母可以大寫
//3.html採用短橫線隔開命名法,js中轉駝峰也是可以的
Vue.component('my-remark',{
template:'<div>{{msg}}測試啦</div>',
data(){//元件中的資料必須是函式型別,返回一個例項作為元件的資料
return {msg:'啦啦啦,'}
}
});
let vm=new Vue({
el:'#app',
});
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>區域性元件</title>
</head>
<body>
<div id="app">
<component1></component1>
<component2></component2>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
//區域性元件使用三部曲:1.建立元件,2.註冊元件,3.使用元件
//元件是相互獨立的,不能之間跨作用域。例項也是一個元件,元件中擁有宣告周期函式
let obj={school:'lalala'};//如果元件公用了資料,會導致同時更新(獨立性)
//子元件不能直接使用父元件的資料(元件之間的資料互動)
//元件理論上可以無限巢狀
let component1={
template:'<div @click="fn">元件1{{school}}</div>',
data(){
return obj;
},
methods:{
fn(){
this.school='hello'
}
}
};
let component2={
template:'<div>元件2{{school}}</div>',
data(){
return obj;
}
};
let vm=new Vue({
el:'#app',
components:{
component1,component2
}
});
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>巢狀元件</title>
</head>
<body>
<div id="app">
<parent></parent>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
//如果要再一個元件使用另一個元件,1.先保證使用的元件是真實存在的,2.在需要引用這個元件的例項上通過components註冊這個元件,3.元件需要在父級的模板中通過標籤的形式引入
let grandson={
template:'<h3>grandson</h3>'
};
let son={
template:'<h2>son<grandson></grandson></grandson></h2>',
components:{
grandson
}
};
let parent={
template:'<h1>parent<son></son></h1>',
components:{
son
}
};
let vm=new Vue({
el:'#app',
components:{
parent
}
});
</script>
</body>
</html>
- 父傳子
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>父傳子</title>
</head>
<body>
<div id="app">
父親:{{money}}
<!-- 當前元件的屬性=父級的值。給兒子加了一個m屬性,屬性對應的資料是屬於父親的-->
<child :m="money"></child>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
//父傳遞子
let vm=new Vue({//parent
el:'#app',
data:{money:200},
components:{
child:{
props:{//屬性名和data中名字不能相同,校驗時不能阻斷程式碼的指向,只是警告而已
m:{//校驗屬性的型別,如果不帶:得到的肯定時字串型別:m='1' :m='true'
type:[String,Function,Object,Array,Number,Boolean],
// default:0,//可以給m賦予預設值,如果不傳預設會呼叫default
required:true,//次屬性是限制必須傳遞,但不能給default同用
validator(val){//第一個引數是當前傳遞的值,返回true標識通過,false不通過
return val>300;//自定義校驗器(用的不是很多)
}
}
},//物件的形式可以通過校驗
// props:['m'],//this.m=200;會在當前子元件上宣告一個m屬性值是父親的
template:'<div>兒子:{{m}}</div>'
}
}
});
</script>
</body>
</html>
- 釋出訂閱
//釋出emit 訂閱on
function Girl() {
this._events={};
}
Girl.prototype.on=function (eventName,callback) {
if(this._events[eventName]){//不是第一次
this._events[eventName].push(callback);//{失戀:[cry,shopping,eat]}
}else{
this._events[eventName]=[callback]//{失戀:[cry]}
}
};
Girl.prototype.emit=function (eventName,...args) {
//[我,你,他]
//[].slice.call(arguments,1);
//Array.from(arguments).slice(1);
this._events[eventName].forEach(cb=>cb(args));
};
let girl=new Girl();
let cry=(who)=>{console.log(who+'哭')};
let shopping=(who)=>{console.log(who+'購物')};
let eat=(who)=>{console.log(who+'吃')};
girl.on('失戀',cry);//{失戀:[cry]}
girl.on('失戀',shopping);//{失戀:[cry,shopping]}
girl.on('失戀',eat);//{失戀:[cry,shopping,eat]}
girl.emit('失戀','我','你');
- sync修飾符
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>sync修飾符</title>
</head>
<body>
<div id="app">
<!-- 使用.sync修飾符同步資料 -->
父親:{{money}}
<!--<child :m="money" @update:m="val=>this.money=val"></child>-->
<child :m.sync="money"></child>
<!-- 寫的時候還是按原有的方法來寫即可,即註釋的方法 -->
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
//父親繫結一些事件,兒子觸發這個事件將引數傳遞過去,單向資料流,父親資料重新整理,兒子就重新整理
let vm=new Vue({//parent
el:'#app',
data:{money:200},
components:{
child:{
props:['m'],
template:'<div>兒子:{{m}}<button @click="getMoney">多要錢</button></div>',
methods:{
getMoney(){
this.$emit('update:m',400);//觸發自定義事件
}
}
}
}
});
</script>
</body>
</html>
- 例項
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>彈窗</title>
<style>
.mask {position: fixed;top: 0;left: 0;width: 100%;height: 100%;background: rgba(0,0,0,.3);}
.pop {position: fixed;top: 50%;left: 50%;width: 500px;height: 200px;background: #fff;transform: translate3d(-50%,-50%,0);}
</style>
</head>
<body>
<div id="app">
<button @click="open">點選</button>
<pop :shows="flag" @close="()=>flag=false"></pop>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
let pop={
props:['shows'],
template:' <div class="mask" v-show="shows">' +
'<div class="pop">' +
'<button @click="down">關閉彈窗</button>' +
'</div>' +
'</div>',
methods:{
down(){
this.$emit('close');
}
}
};
let vm=new Vue({//parent
el:'#app',
data:{flag:false},
components:{
pop
},
methods:{
open(){
this.flag=true;
}
}
});
</script>
</body>
</html>
- slot插槽
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>slot插槽</title>
</head>
<body>
<div id="app">
<container><h1 slot="title">標題</h1><p slot="content">內容</p><span>預設內容嗎</span></container>
<!--<box>測試啦</box>-->
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
let container={
template:'<div><slot name="title"></slot><slot name="content"></slot><slot name="default"></slot></div>',
};
let vm=new Vue({//parent
el:'#app',
components:{
container
}
});
//最基本的方法
// let box={
// //slot中的內容為元件標籤內容為空時的預設內容,可不填
// template:'<div><slot>預設內容</slot></div>',//預設slot擁有name為default的屬性,若只有一個slot,可不填寫name屬性
// };
// let vm=new Vue({//parent
// el:'#app',
// components:{
// box
// }
// });
</script>
</body>
</html>
- ref 父呼叫子方法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>父呼叫子方法,ref獲取真實dom</title>
</head>
<body>
<div id="app">
<loading ref="load"></loading>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
//父呼叫子方法
let loading={
data(){
return {flag:true};
},
template:'<div v-show="flag">瘋狂載入中...</div>',
methods:{
hide(){
this.flag=false;
}
}
};
let vm=new Vue({
el:'#app',
mounted(){
console.log(this.$refs.load);
this.$refs.load.hide();
// this.$refs.load.$el.style.color='red';
},
components:{
loading
},
});
</script>
</body>
</html>
- keep-alive
<keep-alive>
包裹動態元件時, 會快取不活動的元件例項,而不是銷燬它們。和<transition>
相似,<keep-alive>
是一個抽象元件:它自身不會渲染一個 DOM 元素,也不會出現在父元件鏈中。 當元件在<keep-alive>
內被切換,它的activated
和deactivated
這兩個生命週期鉤子函式將會被對應執行。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>keep-alive,component標籤</title>
</head>
<body>
<div id="app">
<input type="radio" v-model="flag" value="home" name="flag">home
<input type="radio" v-model="flag" value="list" name="flag">list
<!-- component是vue自帶的標籤,template,slot,transition同樣也是-->
<!-- 這種方法在跳轉的時候會銷燬前一個元件-->
<component :is="flag"></component>
<!-- 可以新增keep-alive標籤,就不會銷燬元件-->
<!-- 一般用作快取,為的是後面的路由做準備-->
<keep-alive>
<component :is="flag"></component>
</keep-alive>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
//父呼叫子方法
let home={
template:'<div>home</div>',
beforeDestroy(){
alert('銷燬');
}
};
let list={
template:'<div>list</div>',
beforeDestroy(){
alert('銷燬');
}
};
let vm=new Vue({
el:'#app',
data:{
flag:'home'
},
components:{
home,list
},
});
</script>
</body>
</html>
元件非同步渲染問題
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>元件渲染掛載問題$nextTick</title>
</head>
<body>
<div id="app">
<child ref="child"></child>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
//子元件和父元件同時擁有mounted,先執行子元件,後執行父元件
let child={
data(){
return {arr:[1,2,3]}
},
template:'<div><li v-for="a in arr">{{a}}</li></div>',
mounted(){
this.arr=[4,5,6];//此處是非同步渲染dom
alert('child');
},
};
let vm=new Vue({
el:'#app',
mounted(){
alert('parent');
console.log(this.$refs.child.$el.innerHTML);//結果是<li>1</li><li>2</li><li>3</li>
//頁面顯示是4,5,6,為什麼父元件列印的是1,2,3。因為mounted是非同步渲染的,所以此時需要$nextTick,使用mounted一定要使用$nextTick
this.$nextTick(()=>{
console.log(this.$refs.child.$el.innerHTML);
})
},
components:{
child
},
});
</script>
</body>
</html>
- slot實踐
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>slot</title>
<link type="text/css" rel="stylesheet" href="node_modules/bootstrap/dist/css/bootstrap.css">
</head>
<body>
<div id="app">
<panel type="primary" @say-title="parent">
<div slot="title">怎樣學習vue</div>
<div slot="body">
<p>js基礎</p>
<p>vue基礎</p>
<p>vue進階</p>
</div>
<div slot="foot">作者:小明</div>
</panel>
</div>
<template id="panel">
<div class="panel" :class="[color]">
<div class="panel-heading" ref="head">
<slot name="title"></slot>
</div>
<div class="panel-body">
<slot name="body"></slot>
</div>
<div class="panel-footer">
<slot name="foot">無名</slot>
</div>
<button class="btn btn-default" @click="getTitle">點選獲取標題</button>
</div>
</template>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
let panel={
template:'#panel',
computed:{
color(){
return 'panel-'+this.type
}
},
methods:{
getTitle(){
this.$emit('say-title',this.$refs.head.innerText);
}
},
props:{
type:{//this.type='primary',子不能更改父元件傳遞的屬性
type:[String],
default:'default'
}
}
};
let vm=new Vue({
el:'#app',
components:{
panel
},
methods:{
parent(val){
alert(val);
}
}
});
</script>
</body>
</html>
- 元件的迴圈
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>元件的迴圈</title>
<link type="text/css" rel="stylesheet" href="node_modules/bootstrap/dist/css/bootstrap.css">
</head>
<body>
<div id="app">
<!-- 在2.2.0版本以上,當元件使用v-for時,key是必須的 -->
<panel :type="article.type" @say-title="parent" v-for="(article,index) in articles" :key="index">
<div slot="title" v-html="article.title"></div>
<div slot="body">{{article.content}}</div>
<div slot="foot">{{article.auth}}</div>
</panel>
</div>
<template id="panel">
<div class="panel" :class="[color]">
<div class="panel-heading" ref="head">
<slot name="title"></slot>
</div>
<div class="panel-body">
<slot name="body"></slot>
</div>
<div class="panel-footer">
<slot name="foot">無名</slot>
</div>
<button class="btn btn-default" @click="getTitle">點選獲取標題</button>
</div>
</template>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
let panel={
template:'#panel',
computed:{
color(){
return 'panel-'+this.type
}
},
methods:{
getTitle(){
this.$emit('say-title',this.$refs.head.innerText);
}
},
props:{
type:{//this.type='primary',子不能更改父元件傳遞的屬性
type:[String],
default:'default'
}
}
};
let vm=new Vue({
el:'#app',
components:{
panel
},
methods:{
parent(val){
alert(val);
}
},
data:{
articles:[
{type:'primary',title:'<h1>vue</h1>',content:'這是vue文章',auth:'作者:小明'},
{type:'success',title:'<h1>react</h1>',content:'這是react文章',auth:''},
{type:'info',title:'<h1>angularJs</h1>',content:'這是angularJs文章',auth:'作者:小紅'},
]
}
});
</script>
</body>
</html>
路由
- 宣告式路由
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>宣告式路由</title>
<style>
/*.router-link-active {color: red;}*/
.active {color: red;}
</style>
</head>
<body>
<!-- hash模式#,開發時使用,不會導致404,但不支援seo-->
<!-- h5的history.pushState,上線後採用h5的跳轉方式-->
<div id="app">
<!-- 宣告式導航 -->
<router-link to="/home" tag="button">首頁</router-link>
<router-link to="/list" tag="button">列表頁</router-link>
<router-view></router-view>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script src="node_modules/vue-router/dist/vue-router.js"></script>
<script>
let home={template:'<div>首頁</div>'};
let list={template:'<div>列表頁</div>'};
let routes=[//路由的對映表,配置路徑和元件的關係
{path:'/home',component:home},//配置的關係就是頁面級元件
{path:'/list',component:list}//路徑必須加/
];
let router=new VueRouter({//引入vue-router自帶VueRouter類
//vue-router 預設 hash 模式 —— 使用 URL 的 hash 來模擬一個完整的 URL,於是當 URL 改變時,頁面不會重新載入。如果不想要很醜的 hash,我們可以用路由的 history 模式,這種模式充分利用 history.pushState API 來完成 URL 跳轉而無須重新載入頁面。
mode:'history',
routes,
linkActiveClass:'active'//將router-link按鈕自帶觸發類名改為active
});
let vm=new Vue({
el:'#app',
router
});
</script>
</body>
</html>
- 程式設計式路由
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>程式設計式路由</title>
<style>
/*.router-link-active {color: red;}*/
.active {color: red;}
</style>
</head>
<body>
<div id="app">
<!-- 程式設計式導航 -->
<router-link :to="{path:'/home'}" tag="button">首頁</router-link>
<router-link :to="{path:'/list'}" tag="button">列表頁</router-link>
<router-view></router-view>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script src="node_modules/vue-router/dist/vue-router.js"></script>
<script>
let home={
template:'<div>首頁<button @click="goList">列表頁</button></div>',
methods:{
goList(){
this.$router.push('/list');
// this.$router.replace('/list');不常用
}
}
};
let list={
template:'<div>列表頁<button @click="back">返回</button></div>'
,methods:{
back(){
this.$router.go(-1);//返回上一級
}
}
};
let routes=[//路由的對映表,配置路徑和元件的關係
{path:'',component:home},//預設的路由
{path:'/home',component:home},//配置的關係就是頁面級元件
{path:'/list',component:list},//路徑必須加/
// {path:'*',component:list}//當沒有可匹配的頁面時,展示這個頁面。但這個地方路徑不會變,只是切換了元件而已
{path:'*',redirect:'/home'}//路徑變,元件也會變。常用於404
];
let router=new VueRouter({//引入vue-router自帶VueRouter類
routes,
linkActiveClass:'active'//將router-link按鈕自帶觸發類名改為active
});
let vm=new Vue({
el:'#app',
router//定義router後,每個元件都會擁有$router(有r放的都是方法)和$route(沒有r放的都是屬性)
});
</script>
</body>
</html>
- 路由巢狀
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>路由巢狀</title>
</head>
<body>
<div id="app">
<router-link to="/home">首頁</router-link>
<router-link to="/detail">詳情頁</router-link>
<router-view></router-view>
</div>
<template id="detail">
<div>
<router-link to="/detail/profile">個人中心</router-link>
<router-link to="/detail/about">關於我們</router-link>
<router-view></router-view>
</div>
</template>
<script src="node_modules/vue/dist/vue.js"></script>
<script src="node_modules/vue-router/dist/vue-router.js"></script>
<script>
let home={template:'<div>home</div>'};
let detail={template:'#detail'};
let profile={template:'<div>profile</div>'};
let about={template:'<div>about</div>'};
let routes=[
{path:'/home',component:home},
{
path:'/detail',
component:detail,
children:[//children中的路徑永遠不帶/,如果帶/表示1級路由
{path:'profile',component:profile},
{path:'about',component:about}
]
}
];
let router =new VueRouter({
routes
});
let vm=new Vue({
el:'#app',
router
});
</script>
</body>
</html>
- 路由引數
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>路由引數</title>
</head>
<body>
<div id="app">
<!-- 如果用物件作為to的屬性,並且使用了引數,必須給路由起一個名字,通過名字跳轉-->
<router-link :to="{name:'pro',params:{num:1,id:'a'}}">商品1</router-link>
<router-link to="/article/2/b">商品2</router-link>
<router-link to="/article/3/c">商品3</router-link>
<router-view></router-view>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script src="node_modules/vue-router/dist/vue-router.js"></script>
<script>
let article={
template:'<div>這是第{{$route.params.num}}篇文章</div>',
watch:{//路徑引數發生變化,監控引數的變化來發送Ajax
$route(){
alert('ajax');
}
}
};
let routes=[
{path:'/article/:num/:id',component:article,name:'pro'},
];
let router =new VueRouter({
routes
});
let vm=new Vue({
el:'#app',
router
});
</script>
</body>
</html>
- 路由動畫
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>路由動畫</title>
<link type="text/css" rel="stylesheet" href="node_modules/animate.css/animate.css">
</head>
<body>
<div id="app">
<router-link to="/home" tag="button">首頁</router-link>
<router-link to="/list" tag="button">列表頁</router-link>
<!-- mode="out-in"動畫的模式,預設是in-out-->
<!-- 要想呈現相對流暢的動畫,可以設定檢視為絕對位置-->
<transition enter-active-class="animated fadeIn" leave-active-class="animated fadeOut">
<router-view style="position: absolute;top: 50px;left: 0;width: 100%;height: 50px;"></router-view>
</transition>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script src="node_modules/vue-router/dist/vue-router.js"></script>
<script>
let home={template:'<div style="background: red">首頁</div>'};
let list={template:'<div style="background: green">列表頁</div>'};
let routes=[
{path:'/home',component:home},
{path:'/list',component:list}
];
let router=new VueRouter({
routes
});
let vm=new Vue({
el:'#app',
router
});
</script>
</body>
</html>