Vue阶段总结
Vue總結
vue的簡介
Vue的特點和Web開發中常見的高級功能:
? 1、解耦視圖和數據
? 2、雙向數據綁定
? 3、可復用的組件
? 4、前端路由技術
? 5、狀態管理
? 6、虛擬DOM(js對象)
學習vue的注意點
注意:
? 學習Vue框架,必須嚴格遵循他的語法規則,Vue代碼的書寫思路和以前的JQuery完全不一樣。所以,在項目中使用Vue之前,必須先學習Vue的基本語法規則。
Vue 安裝使用
方式一:直接CDN引入
<!-- 開發環境版本,包含了有幫助的命令行警告 --> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <!-- 生產環境版本,優化了尺寸和速度 --> <script src="https://cdn.jsdelivr.net/npm/vue"></script>方式二:下載和引入
// 開發環境 https://vuejs.org/js/vue.js // 生產環境 https://vuejs.org/js/vue.min.js方式三:NPM安裝(今天先不掌握)
? 后續通過Vue-Cli4(腳手架)方式引入,我們使用該方式
創建Vue對象
先看js代碼,會發現創建了一個Vue對象
創建Vue對象的時候,傳入了一個對象:{}
2.1 {}中的el屬性:該屬性決定了這個Vue對象掛載到哪一個元素上,很明顯,我們這里掛載到了id為app的元素上。
2.2 {}中包含了data屬性:該屬性中通常會存儲一些數據,好像上面例子中的str1就是直接定義出來的數據
定義Vue對象基本格式
var vm = new Vue({el: "要綁定的標簽",data: {變量名1:值1,變量名2:值2,變量名3:值3,...},methods: {方法名1: function(){},方法名2: function(){},方法名3: function(){}}});Vue常見的語法格式(重點)
插值
{{ 變量key值 }}v-bind控制標簽屬性
<a v-bind:href="bd">百度</a><a :href="tb">淘寶</a>可以使用簡寫 ‘:’v-on事件格式
<button v-on:click="num+=1">點我數字增加</button><button v-on:click="add">點我數字加5</button><button @click="add2(10)">點我數字加10</button>簡寫方法 “@”如果使用函數,必須在創建的Vue對象里面,添加methodsmethods:{add:function(){this.num += 5},add2(){this.num += 10}vue控制類名
<div id="app"><!--控制標簽的類名 :class= 的值可以是 js對象 {類名:布爾值} --><div class="box" :class="{box1:bool1, box2:bool2}"></div><!--控制標簽的類名 :class= 的值可以是 數組 [類名1,類名2] -->可以是多個的類名<div class="box" :class="['box3', 'box4']"></div><!--掌握--><div class="box" :class=" bool3 ? 'current':'' "></div><!--通過控制bool3位真還是假,來控制這個盒子有還是沒有current這個類-->style樣式(動態控制類名)
<div :style="{fontSize:'40px' }">文字</div>可以使用對象<div :style="{ fontSize:false?'40px' : '50px'}">文字</div>可以使用數組<div :style="[{fontSize:false?'40px' : '50px'},{background:'pink'}]">文字</div></div> <!-- 第一種綁定class --> <div :class="['classA', 'classB']"></div><!-- 第二種綁定class --> <div :class="{'classA': true, 'classB' : false}"></div><!-- 第一種綁定style --> <div :style="{fontSize: '16px', color: 'red'}"></div> :style="{'background-image': 'url('+userInfoFn.headImg變量+')'}"<!-- 第二種綁定style --> <div :style="[{fontSize: '16px', color: 'red'}]"></div>三元表達式: :class="['iconfont樣式名',item.checked變量()? 'true走這邊': 'false走這邊']"控制標簽是否顯示
v-show
v-show是對樣式層面的控制
例如
<p v-show = 'turn'>新聞</p> 在頁面中的顯示是 <p style="display: none;">新聞</p>style="display: nonev-if
v-if是對dom節點的控制
例如: v-if指令的作用: 控制標簽是否展示,不展示則刪除
常與 v-eles 一起用(中間可以插入別的,但是一般識別距離最近的 v-if ; v-eles)<!--<div v-if="bool1">11111</div>--> <!--<div v-else>222222</div>-->bool1的值為true 第一個盒子被保留,刪除第二個盒子,為false的話,第2個盒子保留,刪除第1個盒子兩者的具體應用
v-show 一般用于頻繁切換操作,否則可以使用v-if
列表的渲染(v-for)
語法:v-for="(item, index) in 需要遍歷的數組數組"元素 索引具體用法: <li v-for="i in list1">{{ i }}</li>還可以做數組的添加和刪除 unshift > 在前面新增 push > 在后面新增 shift > 在前面刪除 pop > 在后面刪除對象的渲染(v-for)
語法:v-for="(value, key) in 需要遍歷的對象"value值 key值具體的用法:<!--i是my_obj對象中的值 j是my_obj對象中的鍵名--><li v-for="(i, j) in my_obj">{{i}} {{j}}</li>表單數據綁定(雙向數據綁定)
v-model的值是,vue接收表單元素的值的變量名, 即v1的值就是用戶填寫的內容
(甚至可以在data中設置v1的值,從而設置表單元素的默認值)
可以設置初始值,捕獲改變后的值(最新的值)
可以做tab欄的切換
<div id="app"><input type="text" v-model="txt1"> <!-- v-model表示了用戶輸入的這個數據--><p>{{ txt1 }}</p><select v-model="sel1"><option value="0">北京</option><option value="1">上海</option><option value="2">廣州</option></select></div><script>var vm = new Vue({el:"#app",data:{//通過改變txt1或者sel1,來使對應的表單元素的v-model的值發生了變化,所以這個表單元素的value就變化了(v-model看成我們之前將的value)txt1: '默認值',sel1: 0}})</script>模板語法的插值操作(其他不常用指令)
v-html 往標簽內部插入html文本
data:{htmlText:"<p>你好!世界!</p>"}<p v-html = "htmlText"></p> 顯示:你好!世界!v-text 往標簽內部插入普通文本(解析不了標簽)
<p v-text = "htmlText"></p> 顯示:<p>你好!世界!</p>v-pre 在界面上直接展示胡子語法
<p v-pre>{{htmlText}}</p> 顯示:{{htmlText}}v-cloak 隱藏數據渲染到頁面之前,胡子語法在界面上的展示
<p v-cloak>{{htmlText}}</p>搭配 style 隱藏 使用<style>[v-cloak]{display: none;}</style>如果不使用隱藏,在加載完成之前,顯示:{{htmlText}} 加載完成之后,顯示:<p>你好!世界!</p>與加載的網絡有關數組方法
常用方法使用
var arr = [1, 2, 3] // 往數組最后一位添加一個數字 arr.push(4) // [1, 2, 3, 4] // 刪除數組最后一個數字 arr.pop() // [1, 2, 3] console.log(arr) // 往數組第一位添加一個數字 arr.unshift(0) console.log(arr) // 刪除數組第一個元素 arr.shift() console.log(arr) // splice // 刪除第一個元素 arr.splice(1, 2) console.log(arr) arr.splice(1, 2, 2, 4, 5) console.log(arr) // 合并數組 console.log([1, 6].concat([5, 7]))push(返回數組長度)、unshift(返回數組長度)、shift(返回刪除的值)、pop(返回刪除的值)、splice、concat(返回新數組)
filter方法
過濾
let arr = [1, 2, 3, 4, 5, 6]// filter() 過濾 ,如果是true 則保留,如果是false 則過濾// 不會修改原數組let new1 = arr.filter((item, index) => {return item > 3})console.log(new1, arr);//?[4, 5, 6] [1, 2, 3, 4, 5, 6]map方法
// map() 修改數組里面的每一個元素,并且新的數組和舊的數組長度是一樣的// 不會改變原數組let new2 = arr.map((item, index) => {return item + 2})console.log(new2);//[3, 4, 5, 6, 7, 8]let new3 = arr.map((item, index) => {return {id:item+2}})console.log(new3);//?[{…}, {…}, {…}, {…}, {…}, {…}]reduce方法
利用reduce方法遍歷數組的每一個元素,reduce()調用結果最后返回一個最終值(最后一次return值)。
//數組名.reduce(回調函數,pre的初始值)arr.reduce(function(pre, current){// reduce這個方法被調用時,會遍歷arr這個數組的每一個元素,每遍歷一個元素,就執行一次這里的代碼// current表示當前正在遍歷的這個元素// pre 是上一次的這個函數return的值// !!!因為第一次遍歷沒有上一個return值,所以,交給了第二個參數,設置pre的初始值console.log(pre, current)return 10},0)//!!!并且reduce方法最終會返回最后一次的return值每一次,都返回pre// reduce: 遍歷數組,不會改變原數組/*特點:1、reduce 第二個參數如果沒有,name第一次的prev取數組的第一個元素,回調函數是從數組的第二個元素開始如果有第二個參數,name第一次的prev的值用的就是傳入reduce的第二個參數,回調函數從數組的第一個元素開始遍歷2、reduce里面的prev的值,第一次是由reduce第二個參數決定的,其他的情況下是上一次回調函數的返回值所決定的。reduce 返回值就是最后一次回調函數的返回值。*/用處:可以做計算,匯總…… 語法: array.reduce(function(total, currentValue, currentIndex, arr), initialValue)(total 必填;初始值或者計算結束后的返回值(后面沒有初始值,默認初始值為數組的第一個數 prev的第一次等于數組的第一個數) (currentValue 必填 當前元素) (currentIndex 可填 當前元素的索引) (arr 可選 當前元素所屬的數組對象)(initialValue 可選。傳遞給函數的初始值 不填,默認是數組的第一個數,然后從第二數開始遍歷)其余prev 都是undefined 因為沒有返回 return數組去重
var arr2 = [1, 2, 3, 1, 6, 2, 3]//ES6consoloe.log([...new Set(arr2)])console.log(Array.from(new Set(arr2)))var newArray = [];for(var i=0; i<arr2.length; i++){if(newArray.indexOf(arr2[i])==-1){newArray.push(arr2[i])}}console.log(newArray)var newArray2 = [];var obj = {};for(var i=0; i<arr2.length; i++){if(!obj[arr2[i]]){ //如果不在obj中,就表示不重復的數據,就在對象中添加鍵值對obj[arr2[i]] = arr2[i]newArray2.push(arr2[i])}}console.log(newArray2)Vue的計算屬性computed 的使用
computed:{//computed里面的方法必須有返回值!這個return值將來在視圖中被{{total}}引用***函數 無法傳入參數total(){var a = this.arr.reduce(function(pre, current){console.log(pre, current)// var total = 當前這次的 price*count + 上一次的totalvar total = current.price*current.count + prereturn total},0)return a}}computed內部方法有緩存的作用
函數調用了三遍,卻只執行了1遍,這是computed內部方法的緩存起了作用
computed內部方法的另一種寫法(知道有get和 set的格式)
....computed:{//computed里面的方法必須有返回值!這個return值將來在視圖中被{{total}}引用total:{get(){console.log("total_get")var a = this.arr.reduce(function(pre, current){// var total = 當前這次的 price*count + 上一次的totalvar total = current.price*current.count + prereturn total},0)return a},set(){console.log("total_set")},}}...vm.total = 3 //觸發調用set方法使用:value和@input代替v-model
v-model 本質上包含了兩個操作:
本地存儲
localStorage永久存儲
語法:
// 添加數據;setItem的value值是字符串類型的數據 先對數組或對象進行字符串轉換------JSON.stringify(數組或對象);localStorage.setItem('name','張三');// 獲取數據 取值如果是對象,數組字符串的,那么要進行轉換,JSON.parse例子:let time = localStorage.getItem('time');console.log(JSON.parse(time));localStorage.getItem('name'); // 張三// 刪除(time)某一項數據的值 localStorage.removeItem('time')// 清空 localStorage.clear();注意事項:
5M = 1024 * 5kb
sessionStorage臨時會話存儲
語法:
// 添加數據;setItem的value值是字符串類型的數據 存儲的值必須是字符串(先對數組,對象進行字符串轉換,JSON.stringify) JSON.stringify(數組或對象);sessionStorage.setItem('name','張三');// 獲取數據 取值如果是對象,數組字符串的,那么要進行轉換,JSON.parse例子:let time = localStorage.getItem('time');console.log(JSON.parse(time)); sessionStorage.getItem('name'); // 張三// 刪除(time)某一項數據的值 sessionStorage.removeItem('time')// 清空 sessionStorage.clear();注意事項:
cookie
網站中,http請求是無狀態的。也就是第一次登陸成功(發送請求),第二次請求服務器依然不知道是哪一個用戶。這時候的cookie就是解決這個問題的,第一次登陸后服務器返回數據(cookie)給瀏覽器,然后瀏覽器保存在本地,當該用戶發送第二次請求,瀏覽器自動會把上次請求存儲的cookie數據自動帶上給服務器,服務器根據客戶端的cookie來判斷當前是哪一個用戶。cookie存儲有大小限制,不同瀏覽器不一樣,一般是4kb,所以cookie只能存儲小量數據。
token: 用戶登錄憑證,服務端返回給瀏覽器(前后端分離項目,基本都是發送ajax請求)
session
session和cookie的作用有點類似,也是存儲用戶相關信息。不同的是cookie存儲在瀏覽器,而session存儲在服務器。
Vue過濾器
語法:
過濾器 filters: {函數 toFix2(price參數變量名) {return price.toFixed(2)(toFixed 把數字轉換為字符串,結果的小數點后有指定位數的數字)(用法:toFixed() 方法可把 Number 四舍五入為指定小數位數的數字。)},}Vue指令
深入雙向數據綁定原理
vue雙向數據綁定原理:
借助Object.defineProperty()對數據進行劫持,并結合發布-訂閱者模式,來實現雙向數據綁定
vue的雙向數據綁定原理是什么?
vue數據雙向綁定是通過數據劫持結合“發布者-訂閱者模式”的方式來實現的。
vue是通過Object.defineProperty()來實現數據劫持,其中會有getter()和setter方法;當讀取屬性值時,就會觸發getter()方法,在view中如果數據發生了變化,就會通過Object.defineProperty()對屬性設置一個setter函數,當數據改變了就會來觸發這個函數;
語法:
Object.defineProperty(obj, prop, desc)1. `obj` 需要定義屬性的當前對象 2. `prop` 當前需要定義的屬性名 3. `desc` 屬性描述符數據屬性:
通過Object.defineProperty()為對象定義屬性,有兩種形式,分別為數據描述符,存取描述符,下面分別描述兩者的區別:
例子:
var p = {};// defineProperty(對象, 屬性名, {value: 值}) === p.name = '張三'Object.defineProperty(p, 'name', {value: '張三',writable: true, // writable: 設置屬性(name)是否可以修改,默認值: false(不可修改),true(可以修改)configurable: true, // configurable: 控制屬性是否可以刪除,默認值:false(不可以刪除), true(可以刪除)enumerable: true, // enumerable: 控制屬性是否可以被遍歷,默認值:false(不可以遍歷), true(可以遍歷)})console.log(p.name);p.name = '李四'//不可以直接修改,要先設置屬性console.log(p.name);// delete p.name;//不可以直接刪除,要先設置屬性// console.log(p);for (let key in p) {console.log(key)//不可以直接遍歷,要先設置屬性}另一種方法:let num = 0;// 定義p對象上age屬性; get和set方法分別對age屬性的獲取和賦值進行攔截,get方法的返回值就是age屬性對應的值Object.defineProperty(p, 'age', {// value: 20,get() {//監聽讀取操作獲取數據的時候 本身是沒有值的 值是有另外一個函數 return 出來的get就是在讀取name屬性這個值觸發的函數console.log('age上的get方法')// document.getElementById()return num;},set(val) {//監聽寫入操作改變數據的時候 進入的set 里面set就是在設置name屬性這個值觸發的函數num += (val + 100)console.log('age上的set方法', val)}})p.age = 30;console.log(p.age);自定義指令
除了一些內置的制定(v-model和v-show…),Vue也允許注冊自定義指令。
全局自定義指令格式:
// 注冊一個全局自定義指令 v-demo Vue.directive('demo(指定名)', {inserted: function (el, binding) {console.log(el, binding);},update(el, binding){} })局部指令
// 組件中注冊局部指令 new Vue({el: '#app',data: {},directives: {demo(指令名): {bind(el, binding){},inserted: function (el, binding) {cosnole.log(el, binding);},update(el, binding){},}} })鉤子函數:
一個指令定義對象可以提供如下幾個鉤子函數 (均為可選):
- bind:只調用一次,指令第一次綁定到元素時調用。在這里可以進行一次性的初始化設置。
- inserted:被綁定元素插入父節點時調用 (僅保證父節點存在,但不一定已被插入文檔中)。
- update:所在組件的 VNode 更新時調用,但是可能發生在其子 VNode 更新之前。指令的值可能發生了改變,也可能沒有。
參數:
- el:指令所綁定的元素,可以用來直接操作 DOM 。
- binding:一個對象,包含以下屬性:
- name:指令名,不包括 v- 前綴。
- value:指令的綁定值,例如:v-demo="1 + 1" 中,綁定值為 2。
- oldValue:指令綁定的前一個值
- expression:字符串形式的指令表達式。例如 v--demo="1 + 1" 中,表達式為 "1 + 1"。
- modifiers:一個包含修飾符的對象。例如:v-demo.foo.bar 中,修飾符對象為 { foo: true, bar: true }。
Vue組件化開發
組件化的思想
組件化開發的優勢:可維護性高 可復用性高
組件化思想的應用開發:
局部組件
通過 Vue.component 方式注冊的組件,稱之為全局組件。任何地方都可以使用。全局注冊往往是不夠理想的。比如,如果你使用一個像 webpack 這樣的構建系統,全局注冊所有的組件意味著即便你已經不再使用一個組件了,它仍然會被包含在你最終的構建結果中。這造成了用戶下載的 JavaScript 的無謂的增加。
<template id="tmp1"><!-- 組件必須只有一個根標簽 --><div><header>頭部 num: {{num}}</header><div>123</div></div></template>new Vue({el: "#app",components: {// key: 組件名稱, value: 組件內容aheader: {//任意命名//template標簽------實際上 元素是被當做一個不可見的包裹元素,主要用于分組的條件判斷和列表渲染。// 只能用這個標簽包裹template : '#tmp1',// 組件內的data必須是一個函數data() {return {num: 1}}}}})全局組件
通過Vue CLI 進行Vue全局組件
1、下載安裝包
npm install -g @vue/cli # OR yarn global add @vue/cli2、檢查版本,并確認是否安裝成功
vue --version3、如需升級全局的 Vue CLI 包,請運行:
npm update -g @vue/cli# 或者 yarn global upgrade --latest @vue/cli4、創建一個項目
vue create 自定義文件名(英文) 例如 vue create hello-world5、下載所需要的包工具
會自動識別文件package-lock 里面所需要的工具包
npm i6、開發模式運行
npm run serve生產模式運行
npm run build7、刪除項目
del 文件名8、轉換文件夾名
cd 文件路徑 例如: cd ./文件名9、清空
cls 清空父組件和子組件
組件和組件之間存在層級關系,而其中一種最重要的關系就是父子組件關系。
data屬性必須是一個函數,而且函數返回一個對象 ,對象保存著數據
data() {//函數return {//返回一個對象title: 'zujianbiaoti'}}為什么data在組件中必須是一個函數呢?
原因是在于Vue讓每個組件對象都返回一個新的對象,因為如果是同一個對象的,組件在多次使用后會相互影響。
父子組件間的通訊
父級向子級傳遞
在組件中,使用選項props來聲明需要從父級接收到的數據。
props的值有兩種方式:
子級向父級傳遞
子組件向父組件傳遞數據,通過自定義事件
子級 <div @click="函數名(submitFn)(參數)"></div>methods: {cancelFn(參數) {this.$emit("canceler自定義事件名" 參數);},},父級<Wrapper @canceler自定義事件名='fn函數名' :wrapperDate = 'textObj'></Wrapper>methods:{fn(){this.clickResult = this.textObj.cancelTxt},},多層父子組件之間的通訊
例如祖父和孫子之間或者更多層
使用:Vue提供的更高階方法:provide/inject
這對選項需要一起使用,以允許一個祖先組件向其所有子孫后代注入一個依賴,不論組件層次有多深,并在其上下游關系成立的時間里始終生效。
provide:Object | () => Object inject:Array<string> | { [key: string]: string | Symbol | Object }provide 選項應該是一個對象或返回一個對象的函數。該對象包含可注入其子孫的 property。在該對象中你可以使用 ES2015 Symbols 作為 key,但是只在原生支持 Symbol 和 Reflect.ownKeys 的環境下可工作。inject 選項應該是:一個字符串數組,或 一個對象,對象的 key 是本地的綁定名,value 是: 在可用的注入內容中搜索用的 key (字符串或 Symbol),或 一個對象,該對象的: from property 是在可用的注入內容中搜索用的 key (字符串或 Symbol) default property 是降級情況下使用的 value 提示:provide 和 inject 綁定并不是可響應的。這是刻意為之的。然而,如果你傳入了一個可監聽的對象,那么其對象的 property 還是可響應的。具體用法: // 父級組件提供 'foo' var Provider = {provide: {foo: 'bar'},// ... }// 子組件注入 'foo' var Child = {inject: ['foo'],created () {console.log(this.foo) // => "bar"}// ... }兄弟組件間的傳遞
方法一:bus
EventBus中央事件總線
1、新建一個bus.js的文件,在文件中寫入
import Vue from 'vue' export default new Vue2、在需要進行操作的組件中,引入bus.js
import bus from "../../bus/bus"3、傳參的組件
事件showFn() {//用bus.$emit(第一個參數代表事件名稱,第二個參數代表需要傳過去的參數,第二個參數代表需要傳過去的第二個參數……可以傳入多個參數)bus.$emit("showninfo", this.showin);},4、接收參數的組件
通過$on監聽事件。//掛載mounted(){bus.$on('showninfo',val=>{console.log(val);this.showuin = val})this.bus.emit和this.bus.emit 和this.bus.emit和this.bus.on來進行跨組件通信了
方法二:Vuex 提供的功能
變更vuex中的數據或者狀態 this.$store.commit('increment') 第一個是方法名,第二個是參數拿到vuex中的數據 訪問數據用:$store methods: {increment() {this.$store.commit('increment')console.log(this.$store.state.count)} }通常放到計算屬性中computed: {count () {return this.$store.state.count}} }方法三:v-model
v-model 的本質就是綁定一個屬性和事件
// 父組件 <aa class="abc" v-model="test" ></aa> // aa子組件實現一: <template><div><ul><li>{{'里面的值:'+ msg}}</li><button @click="fn2">里面改變外面</button></ul></div> </template><script>export default {model: { // 使用modelprop: 'msg', //prop屬性將msg作為該組件被使用時(此處為aa組件被父組件調用)v-model能取到的值,event: 'cc' // event中的cc就是父組件上的自定義事件,用來更新父組件上的test值},props: {msg: ''},methods: {fn2 () {this.$emit('cc', this.msg+2)}}} </script>// aa子組件實現方法二: <template><div><ul><li>{{'里面的值:'+ value}}</li> // value會被賦值為v-model綁定的test的值。<button @click="fn2">里面改變外面</button></ul></div> </template><script>export default {props: {value: { // 必須要使用valuedefault: '',},},methods: {fn2 () {this.$emit('input', this.value+2) // 這兒必須用input 發送數據,發送的數據會被父級v-model=“test”接受到,再被value=test傳回來。}}}插槽slot
為什么要使用插槽?
slot翻譯為插槽,組件的插槽:
如何封裝合適呢?抽取共性,保留不同
匿名插槽
如何使用slot?
具名插槽
當子組件的功能復雜時,子組件的插槽可能并非是一個。
如何使用具名插槽呢?
作用域插槽
默認情況下,父組件使用子組件,插槽數據默認是拿父組件的數據,而不是子組件拿數據。
作用域插槽在父組件使用我們的子組件時, 插槽的數據從子組件中拿到數據,而不是從父組件拿到。
? 作用域插槽,作用:可以拿到子組件slot標簽上自定義屬性的集合
父組件<Todolist><template v-slot:btn={idx}><a href="javascript:;" class="del" @click="reMove(idx)">刪除</a></template></Todolist>子組件<template v-slot:default="{$index}"><slot name="btn" :idx="$index"></slot></template>{idx}是Es6的解構語法$index 自定義的命名default意思是默認,下一個子組件不需要給name屬性,直接可以渲染替換<slot></slot>里面的內容孫子組件<slot :$index="i"></slot>當組件渲染的時候,<slot></slot> 將會被替換為“a標簽”(<Todolist>標簽里面的內容)。插槽內可以包含任何模板代碼,包括 HTML:多種寫法
// 1、基本寫法 <one-comp><button slot="btn" slot-scope="scope">按鈕{{scope.msg}}</button> </one-comp>// 2、基本寫法之模板寫法 <one-comp><template slot="btn" slot-scope="scope"><button>按鈕{{scope.msg}}</button></template> </one-comp>// 3、指令寫法 <one-comp v-slot:btn="scope"><button>按鈕{{scope.msg}}</button> </one-comp>// 4、指令寫法之模板寫法 <one-comp><template v-slot:btn="scope"><button>按鈕{{scope.msg}}</button></template> </one-comp>FileZilla
FileZilla是一個免費開源的FTP軟件,分為客戶端版本和服務器版本
Webpack
Webpack(自動化 模塊化 前端開發構建工具)
1. gulp和webpack
Gulp側重于前端開發的 整個過程 的控制管理(像是流水線),我們可以通過給gulp配置不同的task(通過Gulp中的gulp.task()方法配置,比如啟動server、sass/less預編譯、文件的合并壓縮等等)來讓gulp實現不同的功能,從而構建整個前端開發流程。
1. 生成項目依賴文件
// 執行后生成package.json文件 npm init -y2. 安裝依賴(node環境在12.10.0下!)
nvm install 12.10.0
nvm use 12.10.0
npm i webpack@4.44.1 webpack-cli@3.3.12 -g
// 最后的參數-D是安裝到package.json的開發依賴devDependencies(開發環境)對象里,也可以用 --save-dev代替 npm install webpack@4.44.1 webpack-cli@3.3.12 -D// 全局安裝webpack和webpack-cli npm i webpack@4.44.1 webpack-cli@3.3.12 -g// -S是--save的簡寫,這樣安裝的話,會安裝到dependencies(生產環境)對象里,也可以用 --save代替 npm install jquery -S // package.json {"name": "webpack-demo","version": "1.0.0","description": "","main": "index.js","scripts": {"test": "echo \"Error: no test specified\" && exit 1"},"author": "","license": "ISC","devDependencies": {"webpack": "^4.40.2","webpack-cli": "^3.3.9"},"dependencies": {"jquery": "^3.4.1"} }devDependencies與dependencies的區別:
在發布npm包的時候,本身dependencies下的模塊會作為依賴,一起被下載;devDependencies下面的模塊就不會自動下載了;但對于項目而言,npm install 會自動下載devDependencies和dependencies下面的模塊。
3.創建入口文件
`index.html`引入js文件:<script src="./index.js"></script>js文件import $ from 'jquery' $('ul li:even').css({background: 'red'}) $('ul li:odd').css({background: 'green'})瀏覽器打開會出現報錯,因為瀏覽器并不兼容import引入模塊這種方式,所以我們要用到webpack打包
4、通過webpack打包
// 執行命令 output輸出 webpack index.js -o dist/bundle.js出現 “無法將webpack 識別為……的報錯
出現這個報錯,這是因為命令行執行時候會找全局的webpack,但是我們并沒有安裝全局的webpack,所以我們可以安裝全局webpack,或者是使用腳本方式啟動
執行package.json文件中添加的start命令
{"name": "webpack-demo","version": "1.0.0","description": "","main": "index.js","scripts": {"start": "webpack index.js -o dist/bundle.js"},"author": "","license": "ISC","devDependencies": {"webpack": "^4.40.2","webpack-cli": "^3.3.9"},"dependencies": {"jquery": "^3.4.1"} }跑一下
// 生成 dist文件夾和bundle.js文件 npm run start然后再把index.html原來引入index.js的地方改成是通過webpack生成的bundle.js
<!--index.html文件--> <!--<script src="./index.js"></script>--> <script src="./dist/bundle.js"></script>在根目錄里,建立webpack.config.js:
const path = require('path');module.exports = {entry: path.join(__dirname, './index.js'), // dirname代表索引到文件所在目錄output: {path: path.join(__dirname, './dist'),filename: 'bundle.js'} }package.json:
文件修改命令
"scripts": {"start": "webpack --config webpack.config.js"}webpack-dev-server
這時候如果修改index.html的背景顏色red改成是gray,會發現瀏覽器刷新也沒有效果,需要再跑一次npm run start命令才有用,這時候就需要webpack-dev-server(熱重載)
安裝:
npm install webpack-dev-server@3.11.2 -Dpackage.json:
"scripts": {"start": "webpack-dev-server --config webpack.config.js --open --port 3002 --hot"} // --open 自動打開瀏覽器 // --port 服務監聽的端口 3002 // --hot 自動更新html-webpack-plugin
安裝:npm install html-webpack-plugin@4.5.1 -D
webpack.config.js
const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin');module.exports = {entry: path.join(__dirname, './index.js'),output: {path: path.join(__dirname, './dist'),filename: 'bundle.js'},plugins: [new HtmlWebpackPlugin({template: path.join(__dirname, './index.html'),filename: 'index.html'})] }刪掉index.html文件里面的bundle.js引用,因為html-webpack-plugin會自動把打包出來的bundle自動加到我們的index.html代碼里
因為webpack默認是不識別.css文件的,需要我們通過 loader 將 .css 文件進行解釋成正確的模塊。
安裝css-loader和style-loader
npm install css-loader@5.2.4 style-loader@2.0.0 -D //index.css -> bundle.js -> style-loader -> <style> index.html // css-loader的作用是將index.css文件解析為webpack能識別的模塊,然后打包進bundle.js里面,但是這樣樣式并未成功顯示在瀏覽器中。 // style-loader的作用就是將打包到bundle.js中的樣式綁定到瀏覽器上,以style標簽的形式顯示出來webpack.config.js
const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin');module.exports = {entry: path.join(__dirname, './index.js'),output: {path: path.join(__dirname, './dist'),filename: 'bundle.js'},plugins: [new HtmlWebpackPlugin({template: path.join(__dirname, './index.html'),filename: 'index.html'})],module: {rules: [{test: /\.css$/,use: ['style-loader', 'css-loader'] // 注意:這里的數組是反向讀取的(即從右往左)}]} }補充:引入的文件是less
安裝:npm install less-loader@7.2.1 less@4.1.0 -D
規則:
{
? test: /.less$/,
? use: [‘style-loader’, ‘css-loader’, ‘less-loader’]
}
ES6 轉 ES5
安裝依賴包
npm install babel-core babel-loader@7.1.5 babel-plugin-transform-runtime@6.23.0 babel-preset-env@1.7.0 babel-preset-stage-0@6.24.1 -Dhtml熱更新
在安裝過html-webpack-plugin之后,安裝:
npm install --save-dev raw-loader@4.0.2在webpack.config.js中配置raw-loader:
module.exports = {......module: {rules: [{test: /\.(htm|html)$/,use: ['raw-loader']},......]} }在 index.js 中引入html:
import './index.html'Vue CLI腳手架
Vue CLI 是一個基于 Vue.js 進行快速開發的完整系統,提供:
-
通過 @vue/cli 實現的交互式的項目腳手架。
-
通過 @vue/cli + @vue/cli-service-global 實現的零配置原型開發。
-
一個運行時依賴 (
@vue/cli-service),該依賴:
- 可升級;
- 基于 webpack 構建,并帶有合理的默認配置;
- 可以通過項目內的配置文件進行配置;
- 可以通過插件進行擴展。
-
一個豐富的官方插件集合,集成了前端生態中最好的工具。
-
一套完全圖形化的創建和管理 Vue.js 項目的用戶界面。
**安裝Vue CLI腳手架的包
npm install -g @vue/cli # OR yarn global add @vue/cli檢查版本
安裝成功之后,可以用這個命令來檢查其版本是否正確 (4.x):
vue --version vue -V如果安裝比較慢,可以把下載源切換成淘寶的源:
npm 對應的淘寶下載源設置:
//切換taobao鏡像源 npm config set registry https://registry.npm.taobao.org/ // 查看下載源 npm config get registryyarn 對應的淘寶下載源設置:
//切換taobao鏡像源 yarn config set registry https://registry.npm.taobao.org/// 查看下載源 yarn config get registry創建項目
初始化項目
vue create
運行以下命令來創建一個新項目:
vue create 文件名 例如 vue create hello-world 會自動新生成一個文件可以選擇手動模式生成
對于每一項的功能,此處做個簡單描述:
- TypeScript 支持使用 TypeScript 書寫源碼。
- Progressive Web App (PWA) Support PWA支持
- Router 路由
Vuex 狀態管理
CSS Pre-processors 支持 CSS 預處理器。
Linter / Formatter 支持代碼風格檢查和格式化。
Unit Testing 支持單元測試。
E2E Testing 支持 E2E 測試。
vue.config.js
vue.config.js 是一個可選的配置文件,如果項目的 (和 package.json 同級的) 根目錄中存在這個文件,那么它會被 @vue/cli-service 自動加載。你也可以使用 package.json 中的 vue 字段,但是注意這種寫法需要你嚴格遵照 JSON 的格式來寫。
這個文件應該導出一個包含了選項的對象:
// vue.config.js module.exports = {// 選項...(例如:)lintOnSave: false // 關閉eslint }靜態資源參考:
HTML和靜態資源
預處理器 (Sass/Less/Stylus):
如果當時沒有選好,內置的 webpack 仍然會被預配置為可以完成所有的處理。你也可以手動安裝相應的 webpack loader
# Sass npm install -D sass-loader sass# Less npm install -D less-loader less# Stylus npm install -D stylus-loader stylus 1、需要先額外安裝一個全局的擴展 npm install -g @vue/cli-service-global環境
開發環境
生產環境
預生產環境
測試環境
打包部署
npm run build多了個dist目錄文件夾(html css,js)部署
上傳本地的dist文件夾到服務器
使用FileZilla
1、新建站點,選擇SETP協議
報錯404,可能文件路徑出現問題了(加個.就好了)
安裝瀏覽器插件Vue-Devtools
Vue-Devtools是vue在做項目開發時常用的瀏覽器插件。
安裝
- 點擊瀏覽器右上角的 三個點 ,在 更多工具 中,選擇 擴展程序
- 打開 擴展程序 的 開發者模式
- 將 Vue.js Devtools_5.3.3.crx 這個插件直接拽入當前界面,即可完成安裝
常用修飾符
vue中常用的修飾符分三種,分別是事件修飾符、按鍵修飾符和表單修飾符。
1、事件修飾符
- .stop 阻止事件冒泡(*)
- .prevent 阻止默認事件(*)
- .prevent.stop 阻止默認事件的同時阻止冒泡
- .once 阻止事件重復觸發(once與stop不能一起使用,否則再次觸發事件,依然會冒泡)(*)
語法: @click.stop.prevent = “事件名”
<template><div><div class="big" @click="cb"><div class="small" @click="cs"><a href="#" @click.stop.prevent="ca">a標簽</a></div></div><button @click.once="cc">點我</button></div> </template><script> export default {methods:{cb(){console.log("點擊大的");},cs(){console.log("點擊小的");},ca(){console.log("點擊a標簽");},cc(){console.log("點擊按鈕");}}} </script><style> .big{width: 300px;height: 300px;background-color: pink; } .small{width: 200px;height: 200px;background-color: skyblue; } </style>2、按鍵修飾符
在監聽鍵盤事件時,我們經常需要檢查詳細的按鍵。Vue 允許為 v-on 在監聽鍵盤事件時添加按鍵修飾符:
<input v-on:keyup.enter="submit"> <input v-on:keyup.13="submit">3、表單修飾符
表單上常用的 trim 和 number :
想去除用戶輸入的前后空格:
<input v-model.trim="val" type="text">希望在input的change之后再更新輸入框的值
<input v-model.lazy="val" type="text">.trim與.lazy可以合并使用:
二者沒有先后順序
<template> <!-- 表單修飾符 --><div><!-- 加了.trim之后,獲取到的是去除前后空格的結果 --><input type="text" v-model.trim="iptVal"><button @click="handleClcik">按鈕</button><br><!-- 加了.number之后, 獲取到的用戶輸入的信息就是number類型了--><input type="number" v-model.number="numVal"><button @click="handleClcik2">按鈕</button><br><br><!-- 加了.lazy, 用戶每一次觸發就不會馬上更新這個值,等到用戶按回車的時候,才更新這個值 --><input type="text" v-model.lazy="ipt2Val"><p>{{ipt2Val}}</p></div> </template>Watch
watch的作用可以監控一個值的變換,并調用因為變化需要執行的方法。可以通過watch動態改變關聯的狀態。
簡單點說,就是實時監聽某個數據的變化。
普通監聽
<input type="text" v-model="msg"><h3>{{msg}}</h3>watch: {msg(val, oldVal){console.log(val, oldVal)}}跟data 同級立即監聽
如果我們需要在最初綁定值的時候也執行函數,則就需要用到immediate屬性。
<input type="text" v-model="num">跟data 同級watch: { 監聽變化的數據名num: {handler(val, oldVal) {console.log(val, oldVal);},// 組件注入頁面時就立即監聽immediate: true}}immediate需要搭配handler一起使用,其在最初綁定時,調用的函數也就是這個handler函數。
深度監聽
當需要監聽一個對象的改變時,普通的watch方法無法監聽到對象內部屬性的改變,只有data中的數據才能夠監聽到變化,此時就需要deep屬性對對象進行深度監聽。
<h3>{{obj.age}}</h3><script> export default {name: 'Home',data(){return {obj: {name: "Lucy",age: 13}}},methods: {btnClick(){this.obj.age = 33;}},watch: {obj: {handler(val, oldVal){console.log(val.age, oldVal.age) // 33 33},deep: true}} } </script>注意:
1、如果監聽的數據是一個對象,那么 immediate: true失效;
2、一般使用于對引用類型的監聽,深度監聽,如監聽一個Object,只要Object里面的任何一個字段發生變化都會被監聽,但是比較消耗性能,根據需求使用,能不用則不用。
3、因為上面代碼obj是引用數據類型,val, oldVal指向一致,導致看到的結果一樣。
deep優化
我們可以通過點語法獲取對象中的屬性,然后轉為字符串,即是對深度監聽的優化
<template><div class="home"><h3>{{obj.age}}</h3><button @click="btnClick">按鈕</button></div> </template><script> export default {name: "Home",data() {return {obj: {name: "Lucy",age: 13}};},methods: {btnClick() {this.obj.age = 33;}},watch: {// 通過點語法獲取對象中的屬性,然后轉為字符串,即是對深度監聽的優化"obj.age": {handler(val, oldVal) {console.log(val, oldVal);},deep: true,immediate: true, // 此時監聽的數據不是一個對象,可以使用immediate}} }; </script>Watch與Computed的區別
- watch中的函數是不需要調用的,computed內部的函數調用的時候不需要加()
- watch(屬性監聽),監聽的是屬性的變化,而computed(計算屬性),是通過計算而得來的數據
- watch需要在數據變化時執行異步或開銷較大的操作時使用,而對于任何復雜邏輯或一個數據屬性,在它所依賴的屬性發生變化時,也要發生變化,這種情況下,我們最好使用計算屬性computed。
- computed 屬性的結果會被緩存,且computed中的函數必須用return返回最終的結果
- watch 一個對象,鍵是需要觀察的表達式,值是對應回調函數。主要用來監聽某些特定數據的變化,從而進行某些具體的業務邏輯操作;
Watch與Computed的使用場景
-
computed
- 當一個結果受多個屬性影響的時候就需要用到computed
- 最典型的例子: 購物車商品結算的時候
-
watch
- 當一個數據的變化需要有額外操作的時候就需要用watch
- 搜索數據
-
總結:
- 一個值的結果受其他值的影響,用computed
- 一個值的變化將時刻影響其他值,用watch
Mixins混入
mixins就是定義一部分公共的方法或者計算屬性,然后混入到各個組件中使用,方便管理與統一修改。同一個生命周期,混入對象會比組件的先執行。
1、導出mixins
在src下創建 mixins/index.js,寫入:
export const MixinsFn = {created() { console.log("這是mixins觸發的created")} }2、引用mixins
<template><div class="about"><h1>This is an about page</h1></div> </template> <script> import { MixinsFn } from '@/mixins/index.js' export default {created(){console.log("這是about觸發的created")},mixins: [MixinsFn] } </script>我們會發現,mixins中的created 比 about中的created 優先執行。
ref與$refs
vue中獲取頁面里的某個元素(標簽或組件),可以給它綁定ref屬性,有點類似于給它添加id名。
1、簡單使用
<template><div class="">//ref = '屬性名'//獲取 $refs.屬性名======獲取到標簽<h3 ref="title">{{msg}}</h3><button @click="btnclick">按鈕</button></div> </template><script> export default {data() {return {msg: "你好"};},methods: {btnclick() {console.log(this.$refs.title); // 得到h3標簽}} }; </script><style lang = "less" scoped> </style>2、子組件中的數據獲取及方法調用
子組件:
<template><div class=""><h4>{{num}}</h4></div> </template><script> export default {data() {return {num: 100};},methods: {subFn(){console.log('子組件中的方法')}} }; </script><style lang = "less" scoped> </style>父組件:
<template><div class=""><Sub ref="sub" /><button @click="btnclick">按鈕</button></div> </template><script> import Sub from '../components/Sub' export default {methods: {btnclick() {//獲取子組件的屬性值和子組件的方法console.log(this.$refs.sub.num); // 100 this.$refs.sub.subFn(); // '子組件中的方法'}},components: {Sub} }; </script><style lang = "less" scoped> </style>獲取Dom節點
<button @click="fnNum" ref="btn">獲取child組件的num</button>methods:{fn() {console.log(this.$refs.btn); //可以獲取Dom節點----<button @click="fnNum" ref="btn">獲取child組件的num</button>this.$refs.btn.innerText 可以獲取Dom節點的內容-----獲取child組件的num}, }vue生命周期
什么是生命周期: 從Vue創建、運行、到銷毀期間,總是伴隨著各種各樣的事件,這些事件,統稱為生命周期。
**生命周期鉤子函數:**就是生命周期事件的別名;
初始
**beforeCreate:**實例剛剛在內存中被創建出來,此時還沒有初始化 data 和 methods 屬性。
**created:**實例已經在內存中創建好,此時 data和methods 已經創建好,此時還沒有開始編譯模板
和 methods、data 是同級 初始beforeCreate() {console.log("1.1---------------beforeCreate");console.log(1.1, this.num, this.fn, document.getElementById("op"));},created() {console.log("1.2-------------created");console.log(1.2, this.num, this.fn, document.getElementById("op"));},掛載beforeMount() {console.log("2.1---------------beforeMount");console.log(2.1, this.num, this.fn, document.getElementById("op"));},mounted() {console.log("2.2-------------mounted");console.log(2.2, this.num, this.fn, document.getElementById("op"));},更新beforeUpdate(){ //視圖跟新之前console.log("3.1-------------beforeUpdate");console.log(3.1, this.num, this.$refs.myp.innerHTML);},updated(){ //視圖跟新之后console.log("3.2-------------updated");console.log(3.2, this.num, this.$refs.myp.innerHTML);},銷毀beforeDestory(){ },destoryed(){}keep-alive包含的組件是不需要再重新創建(Create)
掛載
**beforeMount:**此時已經完成了模板編譯,但是還沒有掛載到頁面中;
**mounted:**這個時候已經把編譯好的模板掛載到頁面指定的容器里;
beforeMount: data 的數據可以訪問和修改,而且此時的模板已經編譯好了,還沒有更新到頁面中
mounted: 此時編譯的模板更新到頁面中了
更新
**beforeUpdate:**狀態更新之前執行此函數,此時的 data 中的數據是最新,但是界面上顯示的還是舊的,因為此時還沒有開始重新渲染DOM節點;
**updated:**實例更新完畢之后調用此函數,此時data 中的狀態值和界面上顯示的數據,都已經完成了更新,界面已經被重新渲染好了;
beforeUpdate: 此時修改輸入框的內容,data中的數據已經更新了,但是頁面上顯示的還是舊數據;
**updated:**此時 data 的內容已經更新,頁面顯示的也是最新的數據。
銷毀
**beforeDestroy:**實例銷毀之前調用,在這一步,實例讓然完全可用。
**destroyed:**實例銷毀后調用,調用后,Vue實例指向的所以東西會被解綁,所有的事件監聽器會被移除,所有的子實力也會被銷毀。
銷毀聲明周期(使用keep-alive組件是不會觸發銷毀生命周期)
活躍和不活躍
要搭配keep-alive組件來使用
// 從不活躍進入活躍狀態// activated() {// console.log('activated');// },// // 從活躍進入不活躍// deactivated() {// console.log('deactivated')// },Vue-router(路由)
路由的概念
簡單來說路由 就是用來跟后端服務器進行交互的一種方式,通過不同的路徑,來請求不 同的資源,請求不同的頁面是路由的其中一種功能。
1.后端路由
概念:根據不同的用戶的不同URL請求,發送到服務器以后返回不同的內容。
本質:是URL請求地址與服務器資源之間的對應關系。
2.前端路由
概念:根據不同的用戶事件,顯示不同的頁面內容
本質:用戶事件與事件處理函數之間的對應關系
前后端分離階段:
- 隨著Ajax的出現,有了前后端分離的開發模式;
- 后端只提供API來返回數據(json,xml),前端通過Ajax獲取數據,并且可以通過JavaScript將數據渲染到頁面中
- 這樣做最大的優點就是前后端責任的清晰, 后端專注于數據上, 前端專注于交互和可視化上
- 并且當移動端(iOS/Android)出現后, 后端不需要進行任何處理, 依然使用之前的一套API即可
- 目前很多的網站依然采用這種模式開發
單頁面應用階段:
- 其實SPA最主要的特點就是在前后端分離的基礎上加了一層前端路由
- 也就是前端來維護一套路由規則
前端路由的核心是什么呢?
- 改變URL,但是頁面不進行整體的刷新
前端路由規則
URL的hash和HTML5的history模式
1、hash模式
在瀏覽器中符號“#”,#以及#后面的字符稱之為hash,用window.location.hash讀取; 特點:hash雖然在URL中,但不被包括在HTTP請求中;用來指導瀏覽器動作,對服務端安全無用,hash不會重加載頁面。 hash 模式下,僅 hash 符號之前的內容會被包含在請求中,如 http://www.xxx.com (opens new window),因此對于后端來說,即使沒有做到對路由的全覆蓋,也不會返回 404 錯誤。
2、history模式
history采用HTML5的新特性;且提供了兩個新方法:pushState(),replaceState()可以對瀏覽器歷史記錄棧進行修改,以及popState事件的監聽到狀態變更。 history 模式下,前端的 URL 必須和實際向后端發起請求的 URL 一致,如 http://www.xxx.com/items/id。后端如果缺少對 /items/id 的路由處理,將返回 404 錯誤。Vue-Router 官網里如此描述:“不過這種模式要玩好,還需要后臺配置支持……所以呢,你要在服務端增加一個覆蓋所有情況的候選資源:如果 URL 匹配不到任何靜態資源,則應該返回同一個 index.html 頁面,這個頁面就是你 app 依賴的頁面。”
//寫在導出模塊里面 export defaultexport default new VueRouter({routes,mode: 'hash'//還可以是 'history'//兩者區別: hash 后面跟著# 之后才是path值//http://192.168.6.170:8080/#/me http://192.168.6.170:8080/#/cart//history /后面才是path值//http://192.168.6.170:8080/cate http://192.168.6.170:8080/home })Vue-router基本使用
目前前端流行的三大框架,都有自己的路由實現:
- Angular的ngRouter
- React的ReactRouter
- Vue的vue-router
vue-router是Vue的官方路由插件,它和Vue是深度集成的,適合用于構建單頁面應用 https://router.vuejs.org/zh/ 。
vue-router是基于路由和組件的,路由用于設定訪問路徑, 將路徑和組件映射起來;在vue-router的單頁面應用中, 頁面的路徑的改變就是組件的切換.
安裝:
npm install vue-router一般項目中建議在cli創建項目時就直接選擇需要路由,并搭配history模式。
選擇router 模式
在index.js文件中,明確安裝模塊功能(并實例化)
import Vue from 'vue' import VueRouter from 'vue-router'Vue.use(VueRouter)對index.js文件進行路由設置
//導入模塊 import VueRouter from 'vue-router' import Vue from 'vue' // 導入路由對象 Vue.use(VueRouter) // 定義路由規則 const routes = [{path: '/',component: () => import('../views/index.vue') ,children:[//定義方法一:{path: '/', // 瀏覽器訪問//name的作用用于之后傳參獲取定義的名(此名是唯一)name: 'Home',component: Homechildren:[{path: 'cate',component: () => import('../views/cate.vue') // 路由懶加載}]},//定義方法二:{path: 'cate',component: () => import('../views/cate.vue') // 路由懶加載},{path: '/',redirect: '/cart' // 這就是路由的重定向,重新定義跳轉路徑},{path: 'cart', // 改成這個之后,原來的/就沒有對用的組件了component: () => import('../views/cart.vue') },{path: 'me',component: () => import('../views/Mehome.vue') },{path: '*', // 匹配所有剩余的路由,只要不是上面提及的頁面,全部跳轉到404頁面component: () => import('@/views/Page404.vue')}]},] // 創建路由實例 并導出 export default new VueRouter({// 路由模式mode: 'hash', // history hash// 路由規則(路由映射關系)routes, })懶加載的方式
// 方式一: 結合Vue的異步組件和Webpack的代碼分析 const User = resolve => { require.ensure(['@/views/User.vue'], () => { resolve(require('@/views/User.vue')) }) };// 方式二: AMD寫法 const User = resolve => require(['@/views/User.vue'], resolve);// 方式三: 在ES6中, 我們可以有更加簡單的寫法來組織Vue異步組件和Webpack的代碼分割. const Home = () => import(/* webpackChunkName: "user" */ '../views/User.vue')路由跳轉方式
使用 router-link 標簽來實現跳轉
<div id="nav"><router-link to="/">Home</router-link> | //等同于a標簽的效果<router-link to="/user">User</router-link> </div>///跳轉之后,內容將替換<router-view/> //然后通過 `router-view` 來顯示頁面。`router-link` 最終會被渲染為a標簽。 <router-view/>router-link 默認會被解析為a標簽,如果想讓它轉換成其他的標簽,就需要添加tag屬性:
<router-link tag="li" to="/user">User</router-link>此時,router-link 就被解析為li標簽。
點擊跳轉到指定的頁面
設置點擊事件 方法jumpToAbout() {push 在末尾添加 this.$router.push({ push里面寫的是對象// path: '/home'name: 'Home'name是在定義路由規則里面設置的,等同于 path的作用})簡寫: // 簡寫 this.$router.push('/user')除了push,還有
go、 整數是前進,負數是后退
forward、 前進
back、 后退
replace、替換----replace(新的訪問 例如 “/name”)
這幾個來觸發不同情況的跳轉。
router.push(location, onComplete?, onAbort?) router.push(location).then(onComplete).catch(onAbort) router.replace(location, onComplete?, onAbort?) router.replace(location).then(onComplete).catch(onAbort) router.go(n) router.back() router.forward()攜帶參數
jumpFn(){this.$router.push({// path:""name:'About',攜帶的參數的方法名query:{id:123456,name:"張三"}})},在網頁上顯示:http://192.168.6.170:8080/cate?id=123456&name=張三在子組件中可以拿到 - 通過生命周期初始函數得到 然后可以放入data 之后可以直接調用export default {data(){return {id:null,name:null}},生命周期初始函數created(){console.log(this.$route.query);this.id = this.$route.query.idthis.name = this.$route.query.name}}使用params傳參
使用params傳參,得到的結果與使用query傳參得到的結果有以下區別:
this.$router.push({name: "User", params: {userId: 123}}) // http://localhost:8081/user/123 this.$router.push({name: "User", query: {userId: 123}}) // http://localhost:8081/?userId=123 在路由的js文件中:'/editbrand': {path: '/editbrand/:id', //品牌管理編輯name: 'editbrand',component: () =>import(/* webpackChunkName: "editbrand" */ '../views/editbrand/Editbrand.vue'),},組件中:this.$router.push({name:'editbrand',params:{id:row.id}})* 重調強調:
編程式導航中,使用name進行路徑跳轉,攜帶參數可以通過params和query,其中query會將參數攜帶在導航路徑上,而使用path進行路徑跳轉,無法攜帶params,只能攜帶query。
補充:
params參數傳參寫法相當于在路徑直接添加:
//App.vue中: this.$router.push('/user/12');// router/index.js中: path: '/user/:userId',// User.vue中: created(){console.log(this.$route.params.userId); // 獲取到用戶id12 }路由的重定向
const routes = [{path: '/',redirect: '/home' // 這就是路由的重定向,重新定義跳轉路徑},{path: '/home', // 改成這個之后,原來的/就沒有對用的組件了component: () => import('@/views/Home.vue')},... ...{path: '*', // 匹配所有剩余的路由,只要不是上面提及的頁面,全部跳轉到404頁面component: () => import('@/views/Page404.vue')} ]vue-router 有幾種導航鉤子?
1、全局守衛: router.beforeEach2、全局解析守衛: router.beforeResolve3、全局后置鉤子: router.afterEach4、路由獨享的守衛: beforeEnter5、組件內的守衛: beforeRouteEnter、beforeRouteUpdate (2.2 新增)、beforeRouteLeave全局守衛
router.beforeEach
全局前置守衛
導航或者路由被觸發時,全局前置守衛被調用,解析執行完成之后,導航再跳轉
守衛是異步解析執行,此時導航在所有守衛 resolve 完之前一直處于 等待中。
注冊:全局前置守衛 const router = new VueRouter({ ... })router.beforeEach((to, from, next) => {// ... })接收三個參數 to: (去) Route: 即將要進入的目標 路由對象from: (來) Route: 當前導航正要離開的路由next : Function: 一定要調用該方法來 resolve 這個鉤子。執行效果依賴 next 方法的調用參數。 一般會使用 next(): 進行管道中的下一個鉤子。next(): 進行管道中的下一個鉤子。如果全部鉤子執行完了,則導航的狀態就是 confirmed (確認的)。next(false): 中斷當前的導航。如果瀏覽器的 URL 改變了 (可能是用戶手動或者瀏覽器后退按鈕),那么 URL 地址會重 置到 from 路由對應的地址。next('/') 或者 next({ path: '/' }): 跳轉到一個不同的地址。當前的導航被中斷,然后進行一個新的導航。你可以向 next 傳遞任意位置對象,且允許設置諸如 replace: true、name: 'home' 之類的選項以及任何用在 router-link 的 to prop 或 router.push 中的選項。next(error): (2.4.0+) 如果傳入 next 的參數是一個 Error 實例,則導航會被終止且該錯誤會被傳遞給router.onError() 注冊過的回調。全局解析守衛
router.beforeResolve
router.beforeResolve注冊方法和全局前置守衛一樣這和 router.beforeEach 類似,區別是在導航被確認之前,同時在所有組件內守衛和異步路由組件被解析之后,解析守衛就被調用。全局后置鉤子
router.afterEach
這些鉤子不會接受 next 函數也不會改變導航本身
注冊: router.afterEach((to, from) => {// ... })路由獨享的守衛
beforeEnter
可以在路由配置上直接定義 beforeEnter 守衛:const router = new VueRouter({routes: [{path: '/foo',component: Foo,beforeEnter: (to, from, next) => {// ...}}] }) 守衛與全局前置守衛的方法參數是一樣的。組件內的守衛
beforeRouteEnter、beforeRouteUpdate (2.2 新增)、beforeRouteLeave
可以在路由組件內直接定義以下路由導航守衛:beforeRouteEnter beforeRouteUpdate (2.2 新增) beforeRouteLeaveconst Foo = {template: `...`,beforeRouteEnter(to, from, next) {// 在渲染該組件的對應路由被 confirm 前調用// 不!能!獲取組件實例 `this`// 因為當守衛執行前,組件實例還沒被創建//可以通過傳一個回調給 next來訪問組件實例。在導航被確認的時候執行回調,并且把組件實例作為回調方法的參數。//beforeRouteEnter 是支持給 next 傳遞回調的唯一守衛。對于 beforeRouteUpdate 和 beforeRouteLeave 來說,this 已經可用了,所以不支持傳遞回調,因為沒有必要了。},例如:beforeRouteEnter (to, from, next) {next(vm => {// 通過 `vm` 訪問組件實例})}beforeRouteUpdate(to, from, next) {// 在當前路由改變,但是該組件被復用時調用// 舉例來說,對于一個帶有動態參數的路徑 /foo/:id,在 /foo/1 和 /foo/2 之間跳轉的時候,// 由于會渲染同樣的 Foo 組件,因此組件實例會被復用。而這個鉤子就會在這個情況下被調用。// 可以訪問組件實例 `this`},beforeRouteLeave(to, from, next) {// 導航離開該組件的對應路由時調用// 可以訪問組件實例 `this`} }//這個離開守衛通常用來禁止用戶在還未保存修改前突然離開。該導航可以通過 next(false) 來取消。 beforeRouteLeave (to, from, next) {const answer = window.confirm('Do you really want to leave? you have unsaved changes!')if (answer) {next()} else {next(false)} }完整的導航解析流程
$refs 和 $el的用法
$refs
$refs 一個對象,持有注冊過 ref attribute 的所有 DOM 元素和組件實例。 dom還沒有渲染完成,是不能通過ref調用dom的。加在普通元素上,獲取的是dom元素<div ref="system">測試</div> // 獲取 mounted() {console.log(this.$refs.system); }加在子組件上,獲取的是組件實例,可以使用組件的所有方法 // this.$ref.xxx.方法名() // 父組件 <contact-info ref="contactInfo"/> import ContactInfo from './ContactInfo' components: { ContactInfo }, mounted() {this.$refs.contactInfo.initVal(data) // 調用子組件方法 } // 子組件 methods: {initVal(data){Object.keys(this.contactInfo).forEach(val=>{this.contactInfo[val] = data[val]})} }具體使用: 組件內(元素內)設置 <div ref="自定義名稱"> </div>this.$refs 拿到 如果是組件實例,可以使用組件的所有方法 this.$refs.方法名$el
$el Vue 實例使用的根 DOM 元素。 $el讀取的是組件實例掛載的dom元素// 子組件 <template><div>測試</div> </template> <script> export default {name: 'TestComs' }; </script>// 父組件<test ref="testCom" /><div ref="test">11</div>mounted() {console.log(this.$refs.testCom, '組件ref'); // 獲取組件實例console.log(this.$refs.testCom.$el, '組件el'); // 獲取組件實例的dom元素//獲得 <div> 測試 </div>console.log(this.$refs.test, '元素ref'); // 獲取dom元素//獲得<div ref="test">11</div>console.log(this.$refs.test.$el, '元素el'); // $el用于vue組件,普通dom元素不能用//獲得undefined},$set
Vue中data中變量的數據值發生改變,界面沒有跟著更新,是什么原因(Vue數據雙向綁定失效)
1.如果data里面定義了對象,對象里面的鍵值沒有,getter/setter函數沒法監聽到屬性值的數據變化,會導致此現象的發生。
解決方法:
Vue.set(obj, key, value); // or this.$set(obj, key, value);使用方法: 接收三個參數 this.$set(原數組或者原對象,索引值或者鍵值對名,需要賦的值)Vuex----store
Vuex 是一個專為 Vue.js 應用程序開發的狀態管理模式。它采用集中式存儲管理應用的所有組件的狀態,并以相應的規則保證狀態以一種可預測的方式發生變化。
說得直白點,vuex就是vue.js中管理數據狀態的一個庫,通過創建一個集中的數據存儲,供程序中所有組件訪問。
一個數據只要放在了vuex中,當前項目所有的組件都可以直接訪問這個數據。
安裝;
npm install vuex --save在store文件夾中的index.js中寫入
//引入模塊 import Vue from 'vue' import Vuex from 'vuex'Vue.use(Vuex)//導出模塊 export default new Vuex.Store({state: {num: 0,uerinfo:{}},mutations: {increment (state,user) {state.uerinfo = user}} })在miain.js文件中引入store
import store from './store'new Vue({router,store,render: h => h(App) }).$mount('#app')vuex有以下常用的幾個核心屬性概念:
State
-
State
(個人理解)存放數據的容器
vuex中的state類似于data,用于存放數據,只不過這個數據是所有組件公用的。
state: {數據,對象key : value值的方式num: 0,uerinfo:{}}, 組件中<template><div><h3>{{$store.state.num}}</h3></div> </template>也可以使用computed computed: {num(){return this.$store.state.num} }// 標簽中 <h3>{{num}}</h3>Getters
- Getters
vuex中的getters類似于computed計算屬性,getters的返回值會根據它的依賴被緩存起來,且只有當它的依賴值發生了改變才會被重新計算。
封裝異步操作
import Vue from 'vue' import Vuex from 'vuex'Vue.use(Vuex)export default new Vuex.Store({state: {num: 2},getters: {// 這里的參數state可以讓我們快速獲取到倉庫中的數據doubleNum(state) { return state.num * 2;}} })組件中 <template><div><h3>{{num}}</h3></div> </template><script> export default {computed: {num(){return this.$store.getters.doubleNum}} }; </script>Mutations
- Mutations
個人理解:存放方法,然后此方法被別的組件調用
更改 Vuex 的 store 中的狀態的唯一方法是提交 mutation。
import Vue from 'vue' import Vuex from 'vuex'Vue.use(Vuex)export default new Vuex.Store({state: {num: 2},mutations: {// payload專業名稱為“載荷”,其實就是個參數addNum(state, payload) {state.num += payload;}} })調用的組件<template><div><h3>{{num}}</h3><button @click="btnClick">累加2</button></div> </template><script> export default {computed: {num(){return this.$store.state.num}},methods: {btnClick(){// 使用commit來觸發事件,第二個參數是要傳遞給payload的數據this.$store.commit('addNum', 2)}} }; </script>如:this.$store.commit('increment',uerRes.data)this.$store.commit('函數方法名',傳入的參數)并且它會接受 state 作為第一個參數Actions
-
Actions
Action 類似于 mutation,不同在于:
- Action 提交的是 mutation,而不是直接變更狀態。
- Action 可以包含任意異步操作。
當然,上面actions中的寫法有點累贅,我們還可以改寫:
AsyncAddNum({ commit },payload) { setTimeout(() => {commit('addNum', payload)}, 1000) }// 如果你還想獲取state中的值,可以這樣: AsyncAddNum({ commit,state },payload) { console.log(state.num); // 2setTimeout(() => {commit('addNum', payload)}, 1000) }Modules
- Modules
把累加單獨抽出來作為一個模塊,在store下新建一個 add/index.js 文件:
export default {namespaced: true, // 命名空間,為true時,可以在store中把當前模塊文件夾名稱(add),當作模塊名使用state: {num: 2},getters: {doubleNum(state) {return state.num * 1;}},mutations: {addNum(state, payload) {state.num += payload;}},actions: {AsyncAddNum({ commit }, payload) {setTimeout(() => {commit('addNum', payload)}, 300)}} }把有關累加的所有內容,都移動至本文件。再到原來倉庫index.js中的modules添加:
引入存放模塊的文件 import add from './add'export default new Vuex.Store({...,modules: {add} })命名空間
如果希望你的模塊具有更高的封裝度和復用性,你可以通過添加 namespaced: true 的方式使其成為帶命名空間的模塊。當模塊被注冊后,它的所有 getter、action 及 mutation 都會自動根據模塊注冊的路徑調整命名。
const moduleA ={namespaced:true, //開啟namespace:true,該模塊就成為命名空間模塊了state:{count:10,countA:888},getters:{...},mutations:{...},actions:{...} }可以在單個模塊中通過添加namespaced:true的方式使其成為帶命名空間的模塊。組件中如何獲取帶有命名空間moduleA中的state數據? 1、基本方式:this.$store.state.moduleA.countA 2、mapState輔助函數方式:...mapState({count:state=>state.moduleB.countB}) 組件中調用命名空間模塊中的getters 共有三種方式,如下: //1. commonGetter(){this.$store.getters['moduleA/moduleAGetter'] }, //2. ...mapGetters('moduleA',['moduleAGetter']),此處的moduleA,不是以前綴的形式出現!!! //3.別名狀態下 ...mapGetters({paramGetter:'moduleA/moduleAGetter' }),組件中調用命名空間模塊中的Mutations 共有三種方式,如下: //1,3加個前綴moduleA/,都可以實現。2使用輔助函數未變名稱的特殊點!!! //1. commonMutation(){this.$store.commit('moduleA/moduleAMutation'); }, //2. ...mapMutations('moduleA',['moduleAMutation']), //3.別名狀態下 ...mapMutations({changeNameMutation:'moduleA/moduleAMutation' }),組件中調用命名空間模塊中的Actions(與mutations一致) 共有三種方式,如下: 1,3加個前綴moduleA/,都可以實現。2使用輔助函數未變名稱的特殊點!!! //1. commonAction(){this.$store.dispatch('moduleA/moduleAAction') }, //2. ...mapActions('moduleA',['moduleAAction']), //3.別名狀態下 ...mapActions({changeNameAction:'moduleA/moduleAAction' })在帶命名空間的模塊中,如何將action注冊為全局actions兩個條件:①添加 root: true②并將這個 action 的定義放在函數 handler 中//storeAction在命名空間moduleA中,但是它是一個全局actions const moduleA = {namespaced:true,storeAction:{root:true, //條件1handler(namespacedContext, payload){//條件2:handler//namespacedContext 上下文信息//payload 載荷,即參數console.log(namespacedContext)console.log(payload)alert("我是模塊A中的全局storeAction")}} }拆分寫法
實際上我們可以把state、getter、mutation、action和module都抽離出來,這樣可以讓store文件看著更加簡潔。我們來將 store/index.js 進行拆分:
state.js:
export default {num1: 0,title: '標題' }mutations.js:
export default {cutNum(state, payload) {state.num1 -= payload;} }actions.js:
export default {AsyncCutNum({ commit }, payload) {setTimeout(() => {commit('cutNum', payload)}, 300)} }modules.js:
import add from './add' export default {add }最后,在 store/index.js 中:
import Vue from 'vue' import Vuex from 'vuex' import state from './state' import mutations from './mutations' import actions from './actions' import modules from './modules'Vue.use(Vuex)export default new Vuex.Store({state,mutations,actions,modules })輔助函數
mapState
當一個組件需要獲取多個狀態的時候,將這些狀態都聲明為計算屬性會有些重復和冗余。為了解決這個問題,我們可以使用 mapState 輔助函數幫助我們生成計算屬性,讓你少按幾次鍵:
mapState 函數返回的是一個對象
// 在單獨構建的版本中輔助函數為 Vuex.mapState import { mapState } from 'vuex'export default {// ...computed: mapState({// 箭頭函數可使代碼更簡練(方法一)count: state => state.count,// (方法二)傳字符串參數 'count' 等同于 `state => state.count`countAlias: 'count',// (方法三)為了能夠使用 `this` 獲取局部狀態,必須使用常規函數countPlusLocalState (state) {return state.count + this.localCount}}) }當映射的計算屬性的名稱與 state 的子節點名稱相同時,我們也可以給 mapState 傳一個字符串數組。
computed: mapState([// 映射 this.count 為 store.state.count'count' ])mapState 函數返回的是一個對象
我們需要使用一個工具函數將多個對象合并為一個,以使我們可以將最終對象傳給 computed 屬性。但是自從有了對象展開運算符 (opens new window),我們可以極大地簡化寫法:
computed: {localComputed () { /* ... */ },// 使用對象展開運算符將此對象混入到外部對象中...mapState({// ...}) }mapGetters
mapGetters 輔助函數僅僅是將 store 中的 getter 映射到局部計算屬性:
import { mapGetters } from 'vuex'export default {// ...computed: {// 使用對象展開運算符將 getter 混入 computed 對象中...mapGetters(['doneTodosCount','anotherGetter',// ...])} }mapMutations
import { mapMutations } from 'vuex'export default {// ...methods: {...mapMutations(['increment', // 將 `this.increment()` 映射為 `this.$store.commit('increment')`// `mapMutations` 也支持載荷:'incrementBy' // 將 `this.incrementBy(amount)` 映射為 `this.$store.commit('incrementBy', amount)`]),...mapMutations({add: 'increment' // 將 `this.add()` 映射為 `this.$store.commit('increment')`})} }mapActions
import { mapActions } from 'vuex'export default {// ...methods: {...mapActions(['increment', // 將 `this.increment()` 映射為 `this.$store.dispatch('increment')`// `mapActions` 也支持載荷:'incrementBy' // 將 `this.incrementBy(amount)` 映射為 `this.$store.dispatch('incrementBy', amount)`]),...mapActions({add: 'increment' // 將 `this.add()` 映射為 `this.$store.dispatch('increment')`})} }內置組件
keep-alive
緩存加載
作用:主要用于保留組件狀態或避免重新渲染。
include 和 exclude prop 允許組件有條件地緩存。二者都可以用逗號分隔字符串、正則表達式或一個數組來表示:
用在包裹著占位符,包裹動態組件時,會緩存不活動的組件實例,而不是銷毀它們。<keep-alive exclude="Home"><router-view></router-view></keep-alive>include - 字符串或正則表達式。只有名稱匹配的組件會被緩存。exclude - 字符串或正則表達式。任何名稱匹配的組件都不會被緩存。max - 數字。最多可以緩存多少組件實例。給組件起名,以此來確定是需要操作那個組件export default {name:"Home",}逗號分隔字符串<keep-alive include="a,b">正則表達式 (使用 `v-bind`)<keep-alive :include="/a|b/">數組 (使用 `v-bind`)<keep-alive :include="['a', 'b']">最多可以緩存多少組件實例。一旦這個數字達到了,在新實例被創建之前,已緩存組件中最久沒有被訪問的實例會被銷毀掉。
axios 訪問 API
axios
安裝
npm install axiosyarn add axios引入模塊
import axios from "axios"; import qs from "qs"讀取 JSON 數據 methods;{Fn(){axios.post('http://192.168.113.249:8081/cms/phoneRegin',{params: {ID: 12345}}}.then(res=>{}).catch(function (error) {console.log(error);});}方法二 // 直接在 URL 上添加參數 ID=12345 axios.get('/user?ID=12345').then(function (response) {console.log(response);}).catch(function (error) {console.log(error);});請求頭axios.get('http://192.168.113.249:8081/cms/phoneRegin',{headers:{'x-auth-token':' '}post請求axios.post('https://www.runoob.com/try/ajax/demo_axios_post.php').then(response => (this.info = response)).catch(function (error) { // 請求失敗處理console.log(error);});}post請求 傳參說明 axios.post('/user', {firstName: 'Fred', // 參數 firstNamelastName: 'Flintstone' // 參數 lastName}).then(function (response) {console.log(response);}).catch(function (error) {console.log(error);});qs
qs 是一個增加了一些安全性的查詢字符串解析和序列化字符串的庫。
用法之一:解析 URI 編碼
qs.stringify({phone:this.username,password:this.pwd})項目
初始化樣式
<style lang="less" scoped>//在這里可以直接寫樣式或者引入less文件*scoped 樣式只作用于當前組件//.header{height: 50px;background-color: #333; } </style>安裝初始化樣式庫reset-css:
npm i reset-css 或者 yarn add reset-css安裝成功后在main.js中引入即可:
import "reset-css"網站數據請求模塊
發送請求:
安裝axios模塊: npm i axios嘗試在app.vue中做數據請求:
import axios from "axios" export default {...created(){//get請求axios.get("http://192.168.113.249:8081/cms/products/recommend").then(res=>{console.log(res.data);}).catch(err=>{console.log(err);}) }, }代理配置:
對 vue.config.js 進行配置:
module.exports = {devServer: {port: 8080,proxy: {'/api': {target: "http://192.168.113.249:8081/cms",pathRewrite: {'^/api': ''}}}} }由于配置文件修改了,這里一定要記得重新 yarn serve?(跑一下)!!
API與Request封裝
在 src 下新建 request目錄 ,在request目錄下新建 request.js
request.js 中:
import axios from "axios"const instance = axios.create({baseURL:"http://192.168.113.249:8081/cms",timeout:5000 })instance.interceptors.request.use(config=>{console.log("每一次發起請求前,都會先執行這里的代碼");console.log(config); //config本次請求的配置信息return config },err=>{return Promise.reject(err) }) = instance.interceptors.response.use(res=>{console.log("每一次接收到響應,都會先執行這里的代碼,再去執行成功的那個回調函數then");return res },err=>{return Promise.reject(err) })export default instance為了更好地管理我們的這些接口,我們把所有請求都抽取出來在一個api.js中
在request目錄下新建api.js,api.js 中:
import request from './request'// 請求精品推薦數據 export const JingpinAPI = () => request.get('/products/recommend')如:
request.js文件中
import axios from 'axios';const instance = axios.create({timeout:15000,baseURL:'', })instance.interceptors.request.use((config)=>{const token = localStorage.getItem('token')//設置請求頭if(token){config.headers['X-Nideshop-Token'] = token}return config },(err)=>{return Promise.reject(err) })instance.interceptors.response.use((reset)=>{return reset.data; },(err)=>{return Promise.reject(err) })export default instance;api.js文件中
import request from './request'; import qs from 'qs';//post請求 export const getUserByToken = (data)=>request.post('http://kumanxuan1.f3322.net:8360/admin/auth/getUserByToken',qs.stringify(data))//get請求 export const getGoods = (data)=>request.get('http://kumanxuan1.f3322.net:8360/admin/goods',{params:data})導航欄點擊之后的樣式顯示:
<li :class="$route.path==='/home'?'active':''">首頁</li>三元表達式: true 執行前面的,false 執行后面的點擊之后設置路由跳轉
<li @click="$router.push('/home')" :class="$route.path==='/home'?'active':''">首頁</li>拼圖驗證滑塊
插件參考:https://gitee.com/monoplasty/vue-monoplasty-slide-verify
安裝插件
npm install --save vue-monoplasty-slide-verify 或者 yarn add vue-monoplasty-slide-verifymain.js入口文件引中入
import SlideVerify from 'vue-monoplasty-slide-verify' // 拼圖驗證碼Vue.use(SlideVerify)在組件中使用
<template><slide-verify :l="42" :r="20" :w="362" :h="140" @success="onSuccess" @fail="onFail" @refresh="onRefresh" :style="{ width: '100%' }" class="slide-box" ref="slideBlock" :slider-text="msg"></slide-verify> </template><script> export default {data() {return {msg: "向右滑動"};},methods: {// 拼圖成功onSuccess(times) {let ms = (times / 1000).toFixed(1);this.msg = "login success, 耗時 " + ms + "s";},// 拼圖失敗onFail() {this.onRefresh(); // 重新刷新拼圖},// 拼圖刷新onRefresh() {this.msg = "再試一次";},}, }; </script><style lang="less" scoped> /deep/.slide-box {width: 100%;position: relative;box-sizing: border-box;canvas {position: absolute;left: 0;top: -120px;display: none;width: 100%;box-sizing: border-box;}.slide-verify-block{width: 85px;height: 136px;}.slide-verify-refresh-icon {top: -120px;display: none;}&:hover {canvas {display: block;}.slide-verify-refresh-icon {display: block;}} } </style>點擊獲取驗證碼按鈕的邏輯
可以正常獲取驗證碼的前提是:手機號格式正確
所以,點擊獲取驗證碼的邏輯如下:
1、如果校驗手機號格式不正確,則return
2、滑塊拼圖驗證不通過,則return
3、驗證成功后,發起請求,獲取驗證碼成功,則進行倒計時
【百度】結合運營商之后的手機號碼的正則:
/^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\d{8}$/ <div class="btn checkcode-btn" @click="getCode">獲取驗證碼</div> ... <script>getCode(){// 1、驗證手機號是否正確if(!/^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\d{8}$/.test(this.phoneNum)){alert("請輸入正確的手機號");this.$refs.phone.focus();return} alert("手機號格式正確");// 2、進行滑塊驗證// 3、驗證成功后,發起請求,獲取驗證碼成功,則進行倒計時,并展示秒數},</script>倒計時及其展示
<div class="btn checkcode-btn" @click="getCode"><span v-show="!isShowCount">獲取驗證碼</span><span v-show="isShowCount">{{count}} s</span> </div> <script>methods:{countdown(){// 計時的方法// 倒計時,實際上就是每隔1秒,count減去1// 每次點擊先讓count為60this.count=60;let timer = null;timer = setInterval(()=>{this.count--if(this.count===0){// 清除定時器 clearInterval(timer)}},1000);},getCode(){// 1、驗證手機號是否正確/* if(!/^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\d{8}$/.test(this.phoneNum)){alert("請輸入正確的手機號");this.$refs.phone.focus();return} */// 2、進行滑塊驗證if (this.msg == "再試一次" || this.msg == "向右滑動") {alert("請先進行滑塊驗證");return }// 3、驗證成功后,發起請求,獲取驗證碼成功,則進行倒計時,并展示秒數// 這里先展示秒數this.countdown();this.isShowCount=true;},} </script>連續點擊倒計時bug
此時連續點擊倒計時會有bug,數字越跳越快,主要是重復開啟倒計時造成的。
其實我們只需要把事件給到 “獲取驗證碼” 所在的span,就可以解決
<div class="btn checkcode-btn"><span v-show="!isShowCount" @click="getCode">獲取驗證碼</span><span v-show="isShowCount">{{count}} s</span> </div> <script> data () {return {// 是否展示表單的布爾值isShowForm:true,// 拼圖滑塊的文字msg: "向右滑動",// 用戶手機號phoneNum:"",// 最大的計時時間countMax:60,// 倒計時時間,每秒變化的那個數字count:0,// 是否展示秒數所在盒子isShowCount:false} }, ... ... countdown(){// 計時的方法// 倒計時,實際上就是每隔1秒,count減去1let timer = null;this.count = this.countMax;timer = setInterval(()=>{this.count--if(this.count===0){// 清除定時器 clearInterval(timer);}},1000); } </script>請求頭和請求攔截器
接口上有需要我們修改請求頭Content-Type字段,并使用qs.stringnify進行格式轉換:
需要在請求攔截器加上:
instance.interceptors.request.use(config=>{if (config.url === "/sendSMS" || config.url === "/wechatUsers/PCLogin") {config.headers["Content-Type"] = "application/x-www-form-urlencoded";}return config },err=>{return Promise.reject(err) })安裝qs模塊:
npm i qsapi.js中:
import qs from "qs" // 發送短信驗證碼請求 export const SendSMSAPI = params => request.post("/sendSMS",qs.stringify(params));作業
day01
聊天對話框
解題思路:利用v-for遍歷數組,利用v-model獲取變化的值
//先引入Vue的js文件 <script src="./vue.js"></script>創建Vue對象 new Vue({el:"選擇器",data:{//數據arr:[],val:""//val:"A"(意思是默認值是A)},methods: {//方法}})//獲取變化的值:用v-model 一般是用在父級元素上,獲取子級元素的值的變化 //還可以用在輸入框,獲取輸入框的值v-model = "val"v-model = "text"//遍歷數組---用 v-for"(item, index) in 需要遍歷的數組數組" //誰需要遍歷,就誰用v-for <div v-for='item in arr'><span>{{item.content}}</span></div>//判斷name是A還是B,分別對應給class值,控制類名 //用三元表達式:class="item.name==='A'?'atalk':'btalk'"//三元表達式 表達式?"等于(true)的值":"不等于(false)的值"//點擊事件,點擊給數組添加值,然后再遍歷 @click = '事件名'事件中調用data的值,需要用thismethods:{talkTxt(){this.arr.push(`{name:${this.val},content:${this.text}}`)this.text = ""//點擊之后自動清空文本框}}選項卡-tab欄
思路:事件點擊方法,傳參數,帶參數,然后將參數存起來;利用三元表達式判斷是否等于參數值,給類名值
@click='add(1)'三元表達式::class='val===1?"current":""'todolist-微博新增發言功能
思路:用數組將內容裝起來,然后遍歷獲取數組的item內容和index索引值,點擊刪除,刪除對應索引值的數組內容。
用v-for ’(item,index)in arr ‘
學生管理系統
思路:用來 v-for = “(item,index)in arr” ;v-show 顯示隱藏(true;false);將索引和item值存儲,然后傳給下個方法;
JavaScript如何判斷一個值是不是數字?
第一種方法:isNaN()
數字返回false 字符串返回true
缺點:值有一個空串或是一個空格,isNaN將把c當作數字0來處理,所以檢查不嚴謹。
第二種方法:正則表達式
/1+.?[0-9]*///判斷字符串是否為數字,判斷正整數用/[1?9]+[0?9]?]?/ //判斷字符串是否為數字 ,判斷正整數用 /^[1-9]+[0-9]*]*///判斷字符串是否為數字,判斷正整數用/[1?9]+[0?9]?]?/
reg.test( num )
第三種方法: 利用typeof的返回值
0-9 ??
總結
- 上一篇: Youtube上传视频处理失败怎么办
- 下一篇: html5倒计时秒杀怎么做,vue 设