一、課程介紹
內(nèi)容
使用 Vue.js 系列技術(shù)棧進(jìn)行網(wǎng)站應(yīng)用開發(fā)
具體知識(shí)點(diǎn):
- ECMAScript 6
- Vue.js
- Vue Router
- Vuex
- Ajax
- Vue CLI
- axios
- webpack
- Vue SSR
- RESTful
前置知識(shí)
- HTML
- CSS
- JavaScript
- 基本的 ECMAScript 6 語法
- 基本的 Ajax 使用
- node命令行操作
- npm命令
- 基本的 Git 操作
二敌厘、項(xiàng)目實(shí)戰(zhàn)
賣座移動(dòng)端Vue
使用vue-cli腳手架搭建項(xiàng)目
導(dǎo)入有贊UI組件和使用
字體圖標(biāo)使用
搭建底部路由切換部分
vue路由切換總是默認(rèn)添加一個(gè)class router-link-exact-active
三汹桦、ES6常用的基礎(chǔ)知識(shí)點(diǎn)
ES6介紹
ECMAScript 6.0(以下簡稱 ES6)是 JavaScript 語言的下一代標(biāo)準(zhǔn),已經(jīng)在 2015 年 6 月正式發(fā)布了也叫ECMAScript 2015准验。它的目標(biāo)赎线,是使得 JavaScript 語言可以用來編寫復(fù)雜的大型應(yīng)用程序,成為企業(yè)級(jí)開發(fā)語言
let 和 const 命令
let
ES6 新增了let命令糊饱,用來聲明變量垂寥。它的用法類似于var,但是所聲明的變量另锋,只在let命令所在的代碼塊內(nèi)有效滞项。
{
let a = 10;
var b = 1;
}
a // ReferenceError: a is not defined.
b // 1
for (let i = 0; i < 10; i++) {
// ...
}
console.log(i);
// ReferenceError: i is not defined
const 和let不存在變量提升
const
const聲明一個(gè)只讀的常量。一旦聲明砰蠢,常量的值就不能改變蓖扑。
const PI = 3.1415;
PI // 3.1415
PI = 3;
// TypeError: Assignment to constant variable.
-------------------------------------------------
const foo = {};
// 為 foo 添加一個(gè)屬性,可以成功
foo.prop = 123;
foo.prop // 123
// 將 foo 指向另一個(gè)對(duì)象台舱,就會(huì)報(bào)錯(cuò)
foo = {}; // TypeError: "foo" is read-only
變量的解構(gòu)賦值
數(shù)組的解構(gòu)
let a = 1;
let b = 2;
let c = 3;
ES6 允許寫成下面這樣律杠。
let [a, b, c] = [1, 2, 3];
對(duì)象的解構(gòu)賦值
let { foo, bar } = { foo: 'aaa', bar: 'bbb' };
foo // "aaa"
bar // "bbb"
--------------------------------------------
let { bar, foo } = { foo: 'aaa', bar: 'bbb' };
foo // "aaa"
bar // "bbb"
let { baz } = { foo: 'aaa', bar: 'bbb' };
baz // undefined
模板字符串
let place = "world"
// 變量place沒有聲明
let msg = `Hello, ${place}`;
對(duì)象的擴(kuò)展
屬性的簡潔表示法
const foo = 'bar';
const baz = {foo};
baz // {foo: "bar"}
// 等同于
const baz = {foo: foo};
--------------------------------------
function f(x, y) {
return {x, y};
}
// 等同于
function f(x, y) {
return {x: x, y: y};
}
f(1, 2) // Object {x: 1, y: 2}
除了屬性簡寫,方法也可以簡寫竞惋。
const o = {
method() {
return "Hello!";
}
};
// 等同于
const o = {
method: function() {
return "Hello!";
}
};
------------------------------------------
let birth = '2000/01/01';
const Person = {
name: '張三',
//等同于birth: birth
birth,
// 等同于hello: function ()...
hello() { console.log('我的名字是', this.name); }
};
函數(shù)的擴(kuò)展
函數(shù)的擴(kuò)展 ES6 允許使用“箭頭”(=>)定義函數(shù)柜去。
var f = v => v;
// 等同于
var f = function (v) {
return v;
};
如果箭頭函數(shù)的代碼塊部分多于一條語句,就要使用大括號(hào)將它們括起來拆宛,并且使用return語句返回嗓奢。
var sum = (num1, num2) => { return num1 + num2; }
箭頭函數(shù)有幾個(gè)使用注意點(diǎn)
- 函數(shù)體內(nèi)的this對(duì)象,就是定義時(shí)所在的對(duì)象浑厚,而不是使用時(shí)所在的對(duì)象
- 不可以當(dāng)作構(gòu)造函數(shù)股耽,也就是說根盒,不可以使用new命令,否則會(huì)拋出一個(gè)錯(cuò)誤
- 不可以使用arguments對(duì)象物蝙,該對(duì)象在函數(shù)體內(nèi)不存在炎滞。如果要用,可以用 rest 參數(shù)代替诬乞。
- 第一點(diǎn)尤其值得注意册赛。this對(duì)象的指向是可變的,但是在箭頭函數(shù)中震嫉,它是固定的森瘪。
Promise
Promise 是異步編程的一種解決方案,比傳統(tǒng)的解決方案——回調(diào)函數(shù)和事件——更合理和更強(qiáng)大票堵。它由社區(qū)最早提出和實(shí)現(xiàn)扼睬,ES6 將其寫進(jìn)了語言標(biāo)準(zhǔn),統(tǒng)一了用法换衬,原生提供了Promise對(duì)象痰驱。
基本用法 ES6 規(guī)定,Promise對(duì)象是一個(gè)構(gòu)造函數(shù)瞳浦,用來生成Promise實(shí)例担映。下面代碼創(chuàng)造了一個(gè)Promise實(shí)例。
const promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 異步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
let promise = new Promise(function(resolve, reject) {
console.log('Promise');
resolve();
});
promise.then(function() {
console.log('resolved.');
});
console.log('Hi!');
// Promise
// Hi!
// resolved
錯(cuò)誤捕獲
// bad
promise
.then(function(data) {
// success
}, function(err) {
// error
});
// good
promise
.then(function(data) { //cb
// success
})
.catch(function(err) {
// error
});
promise.all() promise.race()
async/ awite
ES2017 標(biāo)準(zhǔn)引入了 async 函數(shù)叫潦,使得異步操作變得更加方便蝇完。async 函數(shù)是什么?一句話矗蕊,它就是 Generator 函數(shù)的語法糖短蜕。
基本用法
async函數(shù)返回一個(gè) Promise 對(duì)象,可以使用then方法添加回調(diào)函數(shù)傻咖。當(dāng)函數(shù)執(zhí)行的時(shí)候朋魔,一旦遇到await就會(huì)先返回,等到異步操作完成卿操,再接著執(zhí)行函數(shù)體內(nèi)后面的語句警检。
function timeout(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
async function asyncPrint(value, ms) {
await timeout(ms);
console.log(value);
}
asyncPrint('hello world', 50);
上面代碼指定 50 毫秒以后,輸出hello world害淤。
返回 Promise 對(duì)象
async function f() {
return 'hello world';
}
f().then(v => console.log(v))
// "hello world"
第 1 章 Vue.js 介紹
前端技術(shù)發(fā)展歷程
- 前端開發(fā)的三個(gè)時(shí)代
- 純?cè)?JavaScript
- jQuery(僅僅是提高了 DOM 操作的效率)
- 框架時(shí)代
- 前端交互越來越多扇雕,功能需求越來越復(fù)雜;舊瀏覽器逐漸淘汰窥摄,移動(dòng)端需求增加
- 架構(gòu)由傳統(tǒng)的后臺(tái) MVC 向 REST API + MV* 發(fā)展
- 前后端分離開發(fā)方式
- 各種前端框架誕生(vue,react,angular)
Vue是什么
以下引自官網(wǎng)原話:
Vue (讀音 /vju?/镶奉,類似于 view) 是一套用于構(gòu)建用戶界面的漸進(jìn)式框架。與其它大型框架不同的是,Vue 被設(shè)計(jì)為可以自底向上逐層應(yīng)用哨苛。Vue 的核心庫只關(guān)注視圖層鸽凶,不僅易于上手,還便于與第三方庫或既有項(xiàng)目整合移国。另一方面吱瘩,當(dāng)與現(xiàn)代化的工具鏈以及各種支持類庫結(jié)合使用時(shí),Vue 也完全能夠?yàn)閺?fù)雜的單頁應(yīng)用提供驅(qū)動(dòng)迹缀。
- 一款非常優(yōu)秀的前端漸進(jìn)式 JavaScript 框架
- Vue 本身只是一個(gè)用于數(shù)據(jù)驅(qū)動(dòng)視圖更新的庫
- 隨著生態(tài)的慢慢發(fā)展,如今已經(jīng)有了 Vue Router蜜徽、Vuex祝懂、Vue CLI唯笙、Vue Server Renderer 等功能庫尼荆,所以當(dāng) Vue 和這些核心功能庫結(jié)合到一起的時(shí)候,我們稱之為一個(gè)框架
- 這些技術(shù)一般也稱之為 Vue 全家桶膝迎,所以學(xué)習(xí) Vue 實(shí)際上就是要學(xué)習(xí) Vue 全家桶才能發(fā)揮出 Vue 的威力
- 由尤雨溪開發(fā)并于 2014 年 2 月開源于 Github(14w+ ??)
- 可以輕松構(gòu)建 SPA 應(yīng)用程序
- 通過 指令 擴(kuò)展了 HTML盆色,通過 表達(dá)式 綁定數(shù)據(jù)到 HTML,通過組件化開發(fā)極大的提高了開發(fā)的效率和可維護(hù)性
- 最大程度上解放了 DOM 操作
發(fā)展歷史
- Vue.js 由尤雨溪個(gè)人正式發(fā)布于 2014 年 2 月灰蛙,并開源于 Github
- 2015 年 10 月 27 日,正式發(fā)布 1.0
- 2016 年 8 月 1 日隔躲,正式發(fā)布 2.0
- 截止到 2019-6 目前最新版本為 2.6.10
- 預(yù)計(jì)今年(2019)會(huì)發(fā)布 3.0 版本
- 目前已在 Github 收獲 14w+ Star
Vue 核心思想
Vue 是為了克服 HTML 在構(gòu)建應(yīng)用上的不足而設(shè)計(jì)的摩梧。Vue 有著諸多特性,最為核心的是:
- 數(shù)據(jù)驅(qū)動(dòng)
- DOM 是數(shù)據(jù)的一種自然映射
- 數(shù)據(jù)改變自動(dòng)驅(qū)動(dòng)視圖更新
- 組件化
-
擴(kuò)展 HTML 元素宣旱,封裝可重用代碼
相關(guān)鏈接
關(guān)于作者
- 尤雨溪 - 知乎
- Vue 作者尤雨溪:以匠人的態(tài)度不斷打磨完善 Vue (圖靈訪談)
- 尤雨溪 - 新浪微博
- 比較詳細(xì)的采訪 Vue 作者尤雨溪
- 尤雨溪 GitHub
- 尤雨溪談 Vue.js :締造自由與真我
第 2 章 Vue 基礎(chǔ)
Vue.js 不支持 IE8 及其以下版本,因?yàn)?Vue 使用了 IE8 無法模擬的 ECMAScript 5 特性仅父。但它支持所有兼容 ECMAScript 5 的瀏覽器。最新穩(wěn)定版本:2.6.10
安裝
- 直接下載
- 開發(fā)版本:https://vuejs.org/js/vue.js
- 生產(chǎn)版本:https://vuejs.org/js/vue.min.js
- CDN
-
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
最新穩(wěn)定版 -
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
指定版本
-
- 使用 npm 下載(最好創(chuàng)建一個(gè) package.json 文件浑吟,用來存儲(chǔ)第三方包依賴信息)
-
npm install vue
最新穩(wěn)定版 -
npm install vue@版本號(hào)
指定版本
-
創(chuàng)建了第一個(gè) Vue 應(yīng)用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="app">
<h1>{{ message }}</h1>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<script>
new Vue({
el: '#app',
data: {
message: 'Hello Vue.js!'
}
})
</script>
</body>
</html>
聲明式渲染
需求:下面的數(shù)據(jù)如何顯示到頁面中笙纤?
const data = {
message: 'Hello Vue.js!',
user: {
name: '張三',
age: 18,
gender: 0
}
}
------------------------------------
<h1></h1>
<p>姓名:</p>
<p>年齡:</p>
<p>性別:</p>
方式一:操作 DOM
方式二:模板引擎
方式三:使用 Vue
<body>
<div id="app">
<h1>{{message}}</h1>
<p>姓名:{{user.name}}</p>
<p>年齡:{{user.age}}</p>
<p>性別:{{user.gender}}</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<script>
new Vue({
el: '#app',
data: {
message: 'Hello Vue.js!',
user: {
name: '張三',
age: 18,
gender: 0
}
}
})
</script>
</body>
實(shí)例選項(xiàng) - el
- 類型:string | Element:
- 詳細(xì): 提供一個(gè)在頁面上已存在的 DOM 元素作為 Vue 實(shí)例的掛載目標(biāo)∽榱Γ可以是 CSS 選擇器省容,也可以是一個(gè) HTMLElement 實(shí)例。在實(shí)例掛載之后燎字,元素可以用 vm.$el 訪問
el 不能是 html腥椒、body 節(jié)點(diǎn)
實(shí)例選項(xiàng) - data
- 類型:Object | Function
new Vue({
el: '#app',
data(){
return {message:"demo"}
}
})
---------------------------
new Vue({
el: '#app',
data:{message:"demo"}
})
組件的定義只接受 function,模板中訪問的數(shù)據(jù)必須初始化到 data 中
模板語法 - 插值{{}}
-
文本
<p>{{ message }}</p> <span>{{ message }}</span> <strong>{{ message }}</strong>
-
JavaScript 表達(dá)式
<p>{{ number + 1 }}</p> <p>{{ number + 1 > 10 ? 'number大于10' : 'number小于10' }}</p> <p>{{ arr }}</p> <p>{{ message.split('').reverse().join('') }}</p>
模板語法 - 指令
指令 (Directives) 是帶有 v- 前綴的特殊特性。指令特性的值預(yù)期是單個(gè) JavaScript 表達(dá)式 (v-for 是例外情況轩触,稍后我們?cè)儆懻?
常用指令:
-
v-if:
v-if 指令用于條件性地渲染一塊內(nèi)容寞酿。這塊內(nèi)容只會(huì)在指令的表達(dá)式返回真值的時(shí)候被渲染。 <h1 v-if="awesome">Vue is awesome!</h1>
-
v-else
<div v-if="Math.random() > 0.5"> Now you see me </div> <div v-else> Now you don't </div>
-
v-else-if
<div v-if="type === 'A'"> A </div> <div v-else-if="type === 'B'"> B </div> <div v-else-if="type === 'C'"> C </div> <div v-else> Not A/B/C </div>
-
v-show
<h1 v-show="ok">Hello!</h1>
-
v-text
<div v-text="text"></div> // 帶標(biāo)簽的內(nèi)容不會(huì)被解析
-
v-html
<div v-text="text"></div> // 帶標(biāo)簽的內(nèi)容會(huì)被解析
-
v-model(表單輸入綁定)
<input v-model="test"/> //這個(gè)指令是一個(gè)語法糖脱柱,一般只用于input標(biāo)簽
v-for (循環(huán)指令伐弹,后面講)
v-bind (綁定指令,后面講)
v-on (監(jiān)聽指令)
一般來說,v-if 有更高的切換開銷惨好,而 v-show 有更高的初始渲染開銷煌茴。因此,如果需要非常頻繁地切換日川,則使用 v-show 較好蔓腐;如果在運(yùn)行時(shí)條件很少改變,則使用 v-if 較好
列表渲染
- v-for="item in todos"
- v-for="(item, index) in todos"
數(shù)組數(shù)據(jù)渲染
對(duì)象數(shù)據(jù)渲染
字符串?dāng)?shù)據(jù)渲染
綁定指令v-bind
綁定指令能綁定我們?cè)厣系囊恍傩裕?code>eg:<a v-bind:href="url">...</a> v-bind 指令將該元素的 href 特性與變量 url 的值綁定龄句。
綁定指令能綁定元素上的任何屬性回论,常用的比如 id, style, class, src, ... 也能綁定自定義屬性(父子傳參使用,到時(shí)候再講)
操作元素的 class 列表和內(nèi)聯(lián)樣式是數(shù)據(jù)綁定的一個(gè)常見需求,因?yàn)樗鼈兌际菍傩苑中晕覀兛梢杂?v-bind 處理它們,在將 v-bind 用于 class 和 style 時(shí)傀蓉,Vue.js 做了專門的增強(qiáng)。表達(dá)式結(jié)果的類型除了字符串之外职抡,還可以是對(duì)象或數(shù)組
Class 綁定
-
一般使用
// class的值就是變量className的值 <div v-bind:class="className"></div> <div v-bind:class="className?'a':'b'"></div>
對(duì)象語法
// 如果變量isActive 值為真則有active這個(gè)類葬燎,否則沒有
<div v-bind:class="{ active: isActive }"></div>
-----------------------------------------------
數(shù)據(jù):
data: {
isActive: true
},
-----------------------------------------------
渲染為:
<div class="active"></div>
- 數(shù)組語法(使用偏少)
<div v-bind:class="[activeClass, errorClass]"></div>
---------------------------------------------------
數(shù)據(jù):
data: {
activeClass: 'active',
errorClass: 'text-danger'
}
-----------------------------------------------
渲染為:
<div class="active text-danger"></div>
Style 綁定
- 對(duì)象語法
<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
-----------------------------
數(shù)據(jù):
data: {
activeColor: 'red',
fontSize: 30
}
-----------------------------
直接綁定到一個(gè)樣式對(duì)象通常更好,這會(huì)讓模板更清晰:
<div v-bind:style="styleObject"></div>
data: {
styleObject: {
color: 'red',
fontSize: '13px'
}
}
- 數(shù)組語法(很少用缚甩,不介紹)
縮寫
<!-- 完整語法 -->
<a v-bind:href="url">...</a>
<!-- 縮寫 -->
<a :href="url">...</a>
監(jiān)聽指令v-on
監(jiān)聽事件
可以用 v-on
指令監(jiān)聽 DOM 事件谱净,并在觸發(fā)時(shí)運(yùn)行一些 JavaScript 代碼。
<div id="example-1">
<button v-on:click="counter += 1">Add 1</button>
<p>The button above has been clicked {{ counter }} times.</p>
</div>
var example1 = new Vue({
el: '#example-1',
data: {
counter: 0
}
})
事件處理方法(常用寫法)
許多事件處理邏輯會(huì)更為復(fù)雜擅威,所以直接把 JavaScript 代碼寫在 v-on 指令中是不可行的壕探。因此 v-on 還可以接收一個(gè)需要調(diào)用的方法名稱
<div id="example-2">
<!-- `greet` 是在下面定義的方法名 -->
<button v-on:click="greet">Greet</button>
</div>
var example2 = new Vue({
el: '#example-2',
data: {
name: 'Vue.js'
},
// 在 `methods` 對(duì)象中定義方法
methods: {
greet: function (event) {
// `this` 在方法里指向當(dāng)前 Vue 實(shí)例
alert('Hello ' + this.name + '!')
// `event` 是原生 DOM 事件
if (event) {
alert(event.target.tagName)
}
}
}
})
事件傳參
<div id="example-3">
<button v-on:click="say('hi')">Say hi</button>
<!-- DOM的原生事件,可以用特殊變量 $event 把它傳入方法 -->
<button v-on:click="say('what',$event)">Say what</button>
</div>
new Vue({
el: '#example-3',
methods: {
say: function (message,ev) {
alert(message)
console.log(ev)
}
}
})
v-on 縮寫
<!-- 完整語法 -->
<a v-on:click="doSomething">...</a>
<!-- 縮寫 -->
<a @click="doSomething">...</a>
練習(xí)題
以下是幾個(gè)小練習(xí)裕寨,用來輔助大家增強(qiáng)體會(huì) Vue 的數(shù)據(jù)驅(qū)動(dòng)視圖思想(MVVM):
1浩蓉、姓名展示:兩個(gè)文本框,讓用戶分別輸入性和名宾袜,然后將數(shù)據(jù)實(shí)時(shí)展示到界面上 2捻艳、數(shù)字自動(dòng)增長:一個(gè)文本框用來呈遞數(shù)字,一個(gè)按鈕庆猫,用戶點(diǎn)擊按鈕认轨,文本框中的數(shù)字+1 3、購物車計(jì)價(jià)器:有商品價(jià)格月培,和商品數(shù)量嘁字,用戶可以點(diǎn)擊加減按鈕改變商品數(shù)量,將價(jià)格實(shí)時(shí)展示到界面上 4杉畜、數(shù)字計(jì)算器:實(shí)現(xiàn)加法計(jì)算器 5纪蜒、數(shù)字計(jì)算器:實(shí)現(xiàn)加減乘除 以上所有練習(xí),都不要著急寫代碼此叠,不要用以前 DOM 的思想去操作纯续, 利用 Vue 的數(shù)據(jù)驅(qū)動(dòng)視圖的思想,去考慮問題:
練習(xí)todoList(簡單版本)
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
ul li {
list-style: none;
}
.hasDone{
text-decoration: line-through;
}
</style>
</head>
<body>
<div id="app">
<h1>TODO</h1>
<div>
<ul>
<li v-for="item in todoList">
<input type="checkbox" @click="change(item)">
<span :class="{hasDone: item.complete}">{{item.describe}}</span>
</li>
</ul>
</div>
<div>
<input type="text" v-model="todoText" placeholder="add new todo here">
<button @click="addTodo">Add</button>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<script>
new Vue({
el: "#app",
data() {
return {
todoList: [{
describe: "the one something todo",
complete: false
},
{
describe: "the two something todo",
complete: false
}
],
todoText: ''
}
},
methods: {
addTodo() {
if (this.todoText) {
this.todoList.push({
describe: this.todoText,
complete: false
})
}
},
change(todo){
todo.complete = !todo.complete
}
},
})
</script>
</body>
計(jì)算屬性(computed)
- 計(jì)算屬性是根據(jù)源數(shù)據(jù)衍生出來的新數(shù)據(jù),既然是衍生的數(shù)據(jù)猬错,那只要源數(shù)據(jù)在data中聲明過即可窗看,同時(shí),計(jì)算屬性的試圖更新必須依賴于data屬性的更新倦炒,此外衍生出來的新數(shù)據(jù)不能在data中有定義
- 計(jì)算屬性的名稱就是計(jì)算屬性處理函數(shù)的名稱显沈,使用時(shí)可以在模版中綁定計(jì)算屬性,綁定方法與data中的普通屬性一致
- 計(jì)算結(jié)果并返回逢唤,只有當(dāng)被計(jì)算的值發(fā)生改變時(shí)才會(huì)觸發(fā)
// 基礎(chǔ)用法
data() {
return {
str: 'string'
}
}
computed: {
beautifyStr() {
return this.str.split('');
}
}
監(jiān)聽器(watch)
對(duì)data屬性的監(jiān)聽拉讯,說明屬性是在data中聲明過的屬性更新時(shí)調(diào)用監(jiān)聽函數(shù),可選參數(shù)分別為新值和舊值鳖藕,對(duì)屬性重新設(shè)置值只要跟原來的值相等就不會(huì)觸發(fā)函數(shù)調(diào)用遂唧,這一點(diǎn)跟計(jì)算屬性是相似的,監(jiān)聽某一個(gè)值吊奢,當(dāng)被監(jiān)聽的值發(fā)生變化時(shí),執(zhí)行對(duì)應(yīng)的操作纹烹,與computed的區(qū)別是页滚,watch更加適用于監(jiān)聽某一個(gè)值的變化并做對(duì)應(yīng)的操作,比如請(qǐng)求后臺(tái)接口等铺呵,而computed適用于計(jì)算已有的值并返回結(jié)果
// 基礎(chǔ)用法
new Vue({
el: '#id',
data: {
firstName: 'Leo',
lastName: 'Alan',
obj1: {
a: 0
}
},
watch: {
// 監(jiān)聽firstName,當(dāng)firstName發(fā)生變化時(shí)就會(huì)執(zhí)行該函數(shù)
firstName (newName, oldName) {
// 執(zhí)行需要的操作...
// 注:初始化不會(huì)執(zhí)行裹驰,只有當(dāng)被監(jiān)聽的值(firstName)發(fā)生變化時(shí)才會(huì)執(zhí)行
},
// 監(jiān)聽lastName
lastName: {
handler (newName, oldName) {
// 執(zhí)行需要的操作...
},
immediate: true // true: 初始化時(shí)就會(huì)先執(zhí)行一遍該監(jiān)聽對(duì)應(yīng)的操作
},
obj1: {
handler () {
// 執(zhí)行需要的操作...
},
deep: true // 該屬性默認(rèn)值為false.
// 當(dāng)被監(jiān)聽的值是對(duì)象,只有deep為true時(shí),對(duì)應(yīng)屬性的值(obj1.a)發(fā)生變化時(shí)才能觸發(fā)監(jiān)聽事件,但是這樣非常消耗性能
},
// 監(jiān)聽對(duì)象具體的屬性片挂, deep就不需要設(shè)置為true了
'obj1.a': {
handler () {
// 執(zhí)行需要的操作...
}
}
}
})
加強(qiáng)版todoList
計(jì)算未做完的事watch: {
todoList:{
handler(){
var count = 0
this.todoList.forEach(item=>{
if(!item.complete){
count +=1
}
})
this.count = count
},
immediate:true,
deep:true
}
},
第三章案例介紹
案例基本會(huì)涵蓋到以下內(nèi)容知識(shí)點(diǎn):
- 數(shù)據(jù)綁定
- 計(jì)算屬性 computed
- 偵聽器 watch
- Class 與 Style 樣式處理
- 條件渲染
- 列表渲染
- 事件處理
- 表單輸入雙向數(shù)據(jù)綁定
案例介紹
項(xiàng)目初始化
下載模板
1幻林、mkdir todomvc-vue
2、cd todomvc-vue
3音念、git clone https://github.com/tastejs/todomvc-app-template.git
4沪饺、npm install
5、安裝完成闷愤,打開 index.html 預(yù)覽模板整葡。
6、導(dǎo)入vue的js文件
7讥脐、開始項(xiàng)目功能開發(fā)
添加功能
- 回車健添加到任務(wù)列表中
- 不允許有非空數(shù)據(jù)
- 添加完清空輸入框內(nèi)的文本
任務(wù)項(xiàng)操作
- 點(diǎn)擊復(fù)選框完成todo遭居,
- 點(diǎn)擊刪除實(shí)現(xiàn)刪除功能
- 點(diǎn)擊全選/取消 完成全選/取消功能
- 監(jiān)控是否全選,是否非全選顯示全選是否選中
底部按鈕的操作和顯示
- 顯示當(dāng)前多少項(xiàng)未完成
- all/active/complete 三個(gè)面板切換
- 清除已經(jīng)完成的項(xiàng)
- 編輯旬渠、修改title
完整代碼
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Template ? TodoMVC</title>
<link rel="stylesheet" href="node_modules/todomvc-common/base.css">
<link rel="stylesheet" href="node_modules/todomvc-app-css/index.css">
<!-- CSS overrides - remove if you don't need it -->
<link rel="stylesheet" href="css/app.css">
</head>
<body>
<section class="todoapp">
<header class="header">
<h1>todos</h1>
<input class="new-todo" placeholder="What needs to be done?" autofocus v-model="needTodoText" v-on:keyup.enter="addTodo">
</header>
<!-- This section should be hidden by default and shown when there are todos -->
<section class="main">
<input id="toggle-all" class="toggle-all" type="checkbox" :checked="checkedAll" @change="selectAll">
<label for="toggle-all" >Mark all as complete</label>
<ul class="todo-list">
<!-- These are here just to show the structure of the list items -->
<!-- List items should get the class `editing` when editing and `completed` when marked as completed -->
<li :class="{completed:item.isComplete, editing:item == editedTodo}" v-for="(item,index) in filterData">
<div class="view">
<input class="toggle" type="checkbox" :checked="item.isComplete" @change="change(item)">
<label @dblclick="edit(item)">{{item.title}}</label>
<button class="destroy" @click="deleteTodo(index)"></button>
</div>
<input class="edit" v-model="item.title"
@blur="doneEdit(item)"
@keyup.enter="doneEdit(item)"
@keyup.esc="cancelEdit(item)"
>
</li>
</ul>
</section>
<!-- This footer should hidden by default and shown when there are todos -->
<footer class="footer">
<!-- This should be `0 items left` by default -->
<span class="todo-count"><strong>{{unComplete}}</strong> item left</span>
<!-- Remove this if you don't implement routing -->
<ul class="filters">
<li>
<a :class="{selected:visibility=='all'}" href="#/" @click.prevent="showPanel('all')">All</a>
</li>
<li>
<a :class="{selected:visibility=='active'}" href="#/active" @click.prevent="showPanel('active')">Active</a>
</li>
<li>
<a :class="{selected:visibility=='completed'}" href="#/completed" @click="showPanel('completed')">Completed</a>
</li>
</ul>
<!-- Hidden if no completed items are left ↓ -->
<button class="clear-completed" @click="ClearCompleted">Clear completed</button>
</footer>
</section>
<footer class="info">
<p>Double-click to edit a todo</p>
<!-- Remove the below line ↓ -->
<p>Template by <a >Sindre Sorhus</a></p>
<!-- Change this out with your name and url ↓ -->
<p>Created by <a >you</a></p>
<p>Part of <a >TodoMVC</a></p>
</footer>
<!-- Scripts here. Don't remove ↓ -->
<script src="node_modules/todomvc-common/base.js"></script>
<script src="js/app.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<script>
new Vue({
el:".todoapp",
data(){
return {
todoList:[
{title:"xx",isComplete:false},
{title:"xx",isComplete:false}
],
needTodoText:'',
visibility:"all", // 通過這個(gè)屬性值的變化對(duì)數(shù)據(jù)進(jìn)行篩選
checkedAll:false, // 全選按鈕
editedTodo:null
}
},
methods: {
// 添加todoList
addTodo(){
if(this.needTodoText){
this.todoList.push({
title:this.needTodoText,
isComplete:false
});
this.needTodoText = "";
}
},
// 刪除
deleteTodo(index){
this.todoList.splice(index,1)
},
showPanel(s){
this.visibility = s
},
change(item){
item.isComplete = !item.isComplete
},
ClearCompleted(){
this.todoList = this.todoList.filter(item=>{
return !item.isComplete
})
},
selectAll(){
if(this.checkedAll){
this.todoList.forEach(item=>{
item.isComplete = false
})
}else{
this.todoList.forEach(item=>{
item.isComplete = true
})
}
this.checkedAll = !this.checkedAll
},
edit(todo){
console.log(todo)
this.editedTodo = todo
this.beforeEditCache = todo.title
},
doneEdit(todo){
if (!this.editedTodo) {
return
}
this.editedTodo = null
todo.title = todo.title.trim()
if (!todo.title) {
this.deleteTodo(todo)
}
},
cancelEdit(todo){
this.editedTodo = null
todo.title = this.beforeEditCache
}
},
watch: {
todoList: {
handler: function (todos) {
var countAll = todos.length;
var countComplete = 0
todos.forEach(item=>{
if(item.isComplete){
countComplete++
}
})
if(countComplete === countAll){
this.checkedAll = true
}else{
this.checkedAll = false
}
},
deep: true
}
},
computed: {
unComplete(){
return this.todoList.filter(item=>{
return !item.isComplete
}).length
},
filterData(){
//過濾的時(shí)候有三種情況 all completed unCompleted
var filter = {
all:function(list){
return list;
},
completed:function(list){
return list.filter(item=>{
return item.isComplete;
})
},
active:function(list){
return list.filter(item=>{
return !item.isComplete;
})
}
}
//如果找到了過濾函數(shù)俱萍,就返回過濾后的數(shù)據(jù),如果沒有找到就返回所有的數(shù)據(jù)
return filter[this.visibility]?filter[this.visibility](this.todoList):this.todoList;
},
},
})
</script>
</body>
</html>
第四章Vue-Cli腳手架
Vue CLI 是 Vue 的腳手架工具告丢,它可以幫助我們快速生成 Vue 基礎(chǔ)項(xiàng)目代碼枪蘑,提供開箱即用的功能特性。
- 基礎(chǔ)代碼目錄結(jié)構(gòu)
- 開發(fā)服務(wù)
- 本地調(diào)試
- 代碼部署
- 熱加載
- 單元測(cè)試 ....
Vue CLI 致力于將 Vue 生態(tài)中的工具基礎(chǔ)標(biāo)準(zhǔn)化。它確保了各種構(gòu)建工具能夠基于智能的默認(rèn)配置即可平穩(wěn)銜接腥寇,這樣你可以專注在撰寫應(yīng)用上成翩,而不必花好幾天去糾結(jié)配置的問題。與此同時(shí)赦役,它也為每個(gè)工具提供了調(diào)整配置的靈活性
安裝
依賴要求:Vue CLI 需要 Node.js 8.9 或更高版本 (推薦 8.11.0+)麻敌。
npm install -g @vue/cli
使用 vue --version 確認(rèn)是否安裝成功。創(chuàng)建項(xiàng)目
運(yùn)行以下命令來創(chuàng)建一個(gè)新項(xiàng)目
vue create my-project
選擇默認(rèn)按enter即可
啟動(dòng)開發(fā)模式:npm run serve
開發(fā)期間不要關(guān)閉命令窗口掂摔,如果關(guān)了术羔,就重新 npm run serve
瀏覽器中打開http://localhost:8080/
看到vue初始頁面,則說明初始化創(chuàng)建成功了
目錄結(jié)構(gòu)
名稱 | 說明 |
---|---|
node_modules | 第三方包存儲(chǔ)目錄 |
public | 靜態(tài)資源乙漓,已被托管 |
src | 源代碼 |
.gitignore | git 忽略文件级历,暫時(shí)不關(guān)心,我們還沒有在項(xiàng)目中使用 git |
babel.config.js | 先不關(guān)心 |
package.json | 包說明文件 |
README.md | 項(xiàng)目說明文件 |
package-lock.json | 包的版本鎖定文件 |
src 目錄結(jié)構(gòu)
名稱 | 說明 |
---|---|
assets | 資源目錄叭披,存儲(chǔ)靜態(tài)資源寥殖,例如圖片等 |
components | 存儲(chǔ)其它組件的目錄 |
App.vue | 根組件 |
main.js | 入口文件 |
程序的啟動(dòng)
- 找到
main.js
入口 - 加載 Vue
- 加載 App 組件
- 創(chuàng)建 Vue 實(shí)例
- 將 App 組件替換到入口節(jié)點(diǎn)
.vue 單文件組件
template
作用:組件的模板
-
注意:只能有一個(gè)根節(jié)點(diǎn)(template 本身不算)
<template> <div id="app"> <img alt="Vue logo" src="./assets/logo.png"> </div> </template>
script
- 作用:組件的 JavaScript ,用來配置組件的選項(xiàng)(data涩蜘、methods嚼贡、watch。同诫。粤策。)
<script>
// 1. 使用一個(gè)普通對(duì)象配置組件的選項(xiàng)
const componentOptions = {
data() {
return {}
},
methods: {}
}
// 2. 將這個(gè)對(duì)象導(dǎo)出(組件的選項(xiàng)對(duì)象必須顯式的導(dǎo)出,否則不會(huì)生效)
export default componentOptions
</script>
為了方便误窖,我們可以直接在定義的同時(shí)直接導(dǎo)出
<script>
export default {
data() {
return {}
},
methods: {}
}
</script>
style
當(dāng) <style> 標(biāo)簽有 scoped 屬性時(shí)叮盘,它的 CSS 只作用于當(dāng)前組件中的元素。
<style scoped>
.example {
color: red;
}
</style>
<template>
<div class="example">hi</div>
</template>
單文件組件的定義和使用
創(chuàng)建單文件組件
- 推薦把通用組件創(chuàng)建到 components 目錄中
- 把視圖組件定義到 views 目錄中
- 單文件組件只是承載組件的容器而已霹俺,既不是全局也不是局部柔吼,如果要使用這個(gè)單文件組件,必須 注冊(cè)
- 全局注冊(cè)使用吭服,可以在任何組件中使用
- 局部注冊(cè)使用嚷堡,只能在注冊(cè)的組件中被使用
建立一個(gè)單文件的組件
<template>
<div>
foo 組件
</div>
</template>
<script>
export default {
data() {},
methods: {}
// ...
}
</script>
<style></style>
全局注冊(cè)使用
在 main.js 文件中
...
import Vue from 'vue'
import Com1 from './components/Com1.vue'
...
Vue.component('Com1', Com1)
// 接下來就可以在任何組件中使用 Com1 組件了
局部注冊(cè)使用
在某個(gè)組價(jià)中局部注冊(cè)使用
<template>
<div>
<!-- 使用 Com2 組件 -->
<Com2></Com2>
</div>
</template>
<script>
import Com2 from './components/Com2'
export default {
components: {
Com2
}
}
</script>
ECMAScript 6 Module
歷史上,JavaScript 一直沒有模塊(module)體系艇棕,無法將一個(gè)大程序拆分成互相依賴的小文件蝌戒,再用簡單的方法拼裝起來。其他語言都有這項(xiàng)功能沼琉,比如 Ruby 的require北苟、Python 的import,甚至就連 CSS 都有@import
打瘪,但是 JavaScript 任何這方面的支持都沒有友鼻,這對(duì)開發(fā)大型的傻昙、復(fù)雜的項(xiàng)目形成了巨大障礙。
在 ES6 之前彩扔,社區(qū)制定了一些模塊加載方案妆档,最主要的有 CommonJS 和 AMD(require.js 庫,專門用于在瀏覽器中進(jìn)行模塊化開發(fā)虫碉,幾乎已經(jīng)淘汰了) 兩種贾惦。前者用于服務(wù)器(Node.js),后者用于瀏覽器敦捧。ES6 在語言標(biāo)準(zhǔn)的層面上须板,實(shí)現(xiàn)了模塊功能,而且實(shí)現(xiàn)得相當(dāng)簡單兢卵,完全可以取代 CommonJS 和 AMD 規(guī)范习瑰,成為瀏覽器和服務(wù)器通用的模塊解決方案。
學(xué)習(xí)準(zhǔn)備環(huán)境
目前無論是 Node.js 還是瀏覽器都還無法直接原生支持 ECMAScript 6 模塊 API(import秽荤、export)甜奄。
但是對(duì)于瀏覽器來說我們可以使用構(gòu)建工具將模塊代碼轉(zhuǎn)換為瀏覽器能識(shí)別的代碼。
Node.js 中可以開啟實(shí)驗(yàn)功能來支持該功能窃款。
為了學(xué)習(xí)方便贺嫂,我們這里使用 Node 環(huán)境來學(xué)習(xí) ES6 模塊規(guī)則。
首先將你的文件后綴名定義為 .mjs雁乡,然后執(zhí)行這個(gè)腳本文件的時(shí)候加上 --experimental-modules 選項(xiàng)。
例如我有一個(gè) main.mjs糜俗,則執(zhí)行命令是:node --experimental-modules main.js
這里只是用于測(cè)試學(xué)習(xí)踱稍,不要在生產(chǎn)環(huán)境中使用這個(gè)模塊規(guī)則。
foo.js
// export 也用于導(dǎo)出
// 但是可以多次使用
export const a = 1
export const b = 2
export const c = 3
export const d = 4
function add(x, y) {
return x + y
}
// 等價(jià)于 module.exports = add
// export default 只能使用一次
export default add
// 模塊中有多個(gè)成員悠抹,一般都使用 export xxx
// 如果只有一個(gè)成員珠月,那就 export default
main.js
// 加載 export default 導(dǎo)出的成員
// import foo from './foo'
// console.log(foo)
// 按需加載 export 導(dǎo)出的成員
// import { a, b } from './foo.mjs'
// console.log(a, b)
// 一次性加載所有成員(包括 default 成員)
// import * as foo from './foo.mjs'
// console.log(foo)
// console.log(foo.a)
// console.log(foo.default(10, 3)) // 很少這樣去訪問 default 成員
// 為了方便,先加載默認(rèn) default 成員楔敌,然后加載 其它 export 成員
// import abc, { a, b } from './foo'
// console.log(abc) // export default 成員
// console.log(a, b) //
// 可以使用 as 起別名
import { a as aa } from './foo'
console.log(aa)
// console.log(foo(1, 2))
相關(guān)命令
# 啟動(dòng)開發(fā)服務(wù)
npm run serve
# 項(xiàng)目打包
npm run build
# 代碼檢查
npm run lint
第五章vue中和服務(wù)端通信
概述
Vue 不像 jQuery 內(nèi)置了 ajax 請(qǐng)求函數(shù)啤挎,在 Vue 中沒有提供這樣的功能。所以當(dāng)我們需要在 Vue 中和服務(wù)端進(jìn)行通信的時(shí)候可選擇的方式會(huì)更靈活一些卵凑。
注意:Vue 不提供的原因是為了讓 Vue 本身更專注于視圖部分庆聘,保持其漸進(jìn)靈活的特性
所以 Vue 給了我們更多的選擇空間,例如我們可以使用下面的可選方案:
- 原生的 XMLHttpRequest
- 原生的 Fetch
- 也可以結(jié)合使用 jQuery 自帶的 Ajax 請(qǐng)求函數(shù)
- 早期大家開發(fā) Vue 應(yīng)用喜歡使用一個(gè)第三方插件:Vue Resource
- 目前主流的方案是使用社區(qū)中知名的第三方庫 axios
axios介紹
axios 是一個(gè)基于 Promise 的第三方 HTTP 客戶端請(qǐng)求庫勺卢,可以用于瀏覽器或者 Node.js伙判。 axios 本身和 Vue 沒有一毛錢關(guān)系,只是簡單純粹的封裝了 HTTP 請(qǐng)求功能黑忱⊙绺В可以運(yùn)行在任何支持 JavaScript 環(huán)境的平臺(tái)勒魔。
axios 依賴原生的 ECMAScript 6 Promise 支持。如果瀏覽器不支持 ECMAScript 6 Promise菇曲,可以使用 es6-promise 進(jìn)行兼容處理
起步
npm install axios
執(zhí)行一個(gè) GET 請(qǐng)求
const axios = require('axios');
// Make a request for a user with a given ID
axios.get('/user?ID=12345')
.then(function (response) {
// handle success
console.log(response);
})
.catch(function (error) {
// handle error
console.log(error);
})
.finally(function () {
// always executed
});
// Want to use async/await? Add the `async` keyword to your outer function/method.
async function getUser() {
try {
const response = await axios.get('/user?ID=12345');
console.log(response);
} catch (error) {
console.error(error);
}
}
執(zhí)行一個(gè) POST 請(qǐng)求
axios
.post('/user', {
firstName: 'Fred',
lastName: 'Flintstone'
})
.then(function(response) {
console.log(response)
})
.catch(function(error) {
console.log(error)
})
執(zhí)行多個(gè)并發(fā)請(qǐng)求
function getUserAccount() {
return axios.get('/user/12345')
}
function getUserPermissions() {
return axios.get('/user/12345/permissions')
}
axios.all([getUserAccount(), getUserPermissions()]).then(
axios.spread(function(acct, perms) {
// Both requests are now complete
})
)
axios API
axios(config),我們可以像使用 $.ajax() 一樣來使用 axios
// Send a POST request
axios({
method: 'post',
url: '/user/12345',
data: {
firstName: 'Fred',
lastName: 'Flintstone'
}
})
// GET request for remote image
axios({
method: 'get',
url: 'http://bit.ly/2mTM3nY',
responseType: 'stream'
}).then(function(response) {
response.data.pipe(fs.createWriteStream('ada_lovelace.jpg'))
})
第六章組件基礎(chǔ)
- 了解組件的概念
- 掌握組件的定義
- 了解組件的組織方式
- 掌握組件的聲明周期
- 掌握組件通信
.vue 單文件組件
單文件組件是最常用的編寫組件的方式弟胀,我們90%以上都是這種寫法,還有其他寫法很少用蕊玷,暫時(shí)不用掌握
組件注冊(cè)方式
- 全局組件
- 定義在全局邮利,在任意組件中都可以直接使用
- 局部組件
- 定義在組件內(nèi)部,只能在當(dāng)前組件使用
注冊(cè)和使用方式清跳轉(zhuǎn)到 第四章的Vue-cli的 單文件組件的定義和使用 這一章節(jié)
組件的生命周期
組件的有幾個(gè)階段
- 初始化階段
- 運(yùn)行中階段
- 銷毀階段
初始化階段
- beforeCreate
表示組件創(chuàng)建前的準(zhǔn)備工作垃帅, 為事件的發(fā)布訂閱 和 生命周期的開始做初始化
這個(gè)鉤子函數(shù)中,數(shù)據(jù)拿不到延届, 真實(shí)DOM也拿不到,這個(gè)鉤子在項(xiàng)目中我們沒有什么實(shí)際用途
beforeCreate () { //表示組件創(chuàng)建前的準(zhǔn)備工作( 初始化事件和生命周期 )
console.log('beforeCreate')
console.log(this.msg) //undefind
console.log(document.querySelector('p')) //null 沒有真實(shí)DOM
}
- created
表示組件創(chuàng)建結(jié)束,這個(gè)鉤子函數(shù)中,數(shù)據(jù)拿到了, 但是真實(shí)DOM沒有拿到
這個(gè)鉤子函數(shù)在項(xiàng)目一般數(shù)據(jù)請(qǐng)求贸诚,然后可以進(jìn)行一次默認(rèn)數(shù)據(jù)的修改
created () { // 組件創(chuàng)建結(jié)束
console.log('created')
console.log(this.msg) //(vue.js) 有數(shù)據(jù)
console.log(document.querySelector('p')) //null 沒有真實(shí)DOM
axios({
url: './data.json'
})
.then( res => {
this.msg = res
})
.catch( error => {
throw error
})
}
- beforeMounte
表示組件裝載前的準(zhǔn)備工作,判斷 el選項(xiàng)有沒有方庭, 判斷 template選項(xiàng)有沒有 , 如果沒有酱固, 那么需要手動(dòng)裝載,如果有械念,那么通過render函數(shù)進(jìn)行模板的渲染
這個(gè)鉤子函數(shù)中,數(shù)據(jù)拿到了, 真實(shí)DOM沒有拿到,這個(gè)鉤子函數(shù)在項(xiàng)目中运悲,數(shù)據(jù)請(qǐng)求龄减, 它也可以進(jìn)行一次數(shù)據(jù)修
beforeMount () {
console.log('beforeMount')
console.log(this.msg) //(vue.js) 有數(shù)據(jù)
console.log(document.querySelector('p')) //null 沒有真實(shí)DOM
// axios({
// url: './data.json'
// })
// .then( res => {
// this.msg = res
// })
// .catch( error => {
// throw error
// })
}
- mounted
表示組件裝載結(jié)束, 就是我們可以在視圖中看到了,這個(gè)鉤子函數(shù)中,數(shù)據(jù)拿到了班眯, 真實(shí)DOM也拿到了,
這個(gè)鉤子函數(shù)在項(xiàng)目DOM操作就可以進(jìn)行了希停, 第三方庫的實(shí)例化
mounted () {
console.log('mount')
console.log(this.msg) //(vue.js) 有數(shù)據(jù)
console.log(document.querySelector('p')) //有真實(shí)DOM
axios({
url: './data.json'
})
.then( res => {
this.msg = res
})
.catch( error => {
throw error
})
}
總結(jié): 由上對(duì)比,我們可以知道署隘, 數(shù)據(jù)請(qǐng)求越提前越好一些宠能, created常用于數(shù)據(jù)的請(qǐng)求和數(shù)據(jù)的修改, 第三方庫的實(shí)例化常在mounted中進(jìn)行書寫
運(yùn)行中階段
- beforeUpdate
表示數(shù)據(jù)更新前的準(zhǔn)備工作,這個(gè)鉤子不主動(dòng)執(zhí)行磁餐,當(dāng)數(shù)據(jù)修改了违崇, 才會(huì)執(zhí)行,這個(gè)鉤子函數(shù)中數(shù)據(jù)拿到了, 并且拿到的是修改后的數(shù)據(jù),DOM也輸出了诊霹。這個(gè)鉤子函數(shù)更多的工作內(nèi)容為:生成新的 VDOM , 然后通過diff算法進(jìn)行兩次VDOM 對(duì)比羞延。這個(gè)鉤子在項(xiàng)目中。因?yàn)樗饕龅氖虑槭莾?nèi)部進(jìn)行的脾还, 所以對(duì)我們而言沒有太多的操作意義
- updated
表示數(shù)據(jù)更新結(jié)束肴楷, 通過render函數(shù)渲染真實(shí)DOM。這個(gè)鉤子函數(shù)的執(zhí)行也是荠呐, 當(dāng)數(shù)據(jù)修改的時(shí)候才執(zhí)行赛蔫。這個(gè)鉤子函數(shù)中數(shù)據(jù)拿到了砂客, DOM也拿到了。
總結(jié): 數(shù)據(jù)更新呵恢, 也要進(jìn)行DOM操作那么鞠值, 我們使用update這個(gè)鉤子
銷毀階段
- beforeDestroy
- destroyed
這兩個(gè)鉤子無差別,在項(xiàng)目中做善后工作 , 手動(dòng)清除一些計(jì)時(shí)器渗钉, 和一些方法彤恶, 還有第三方實(shí)例化出來的對(duì)象
Vue.component('LifeCircle',{
template: '#life-circle',
methods: {
destroy(){
this.$destroy()
}
},
created () {
this.timer = setInterval( () => {
console.log('1')
},1000)
},
beforeDestroy () {
console.log('beforeDestory')
},
destroyed () {
console.log('destroyed')
clearInterval( this.timer )
// 如果是用$destroy這個(gè)方法來清除組件, 那么我們必須手動(dòng)清除這個(gè)組件的外殼
document.querySelector('#app div').remove()
}
})
new Vue({
el: '#app',
data: {
flag: true
}
})
組件通信
組件就像零散的積木鳄橘,我們需要把這些積木按照一定的規(guī)則拼裝起來声离,而且要讓它們互相之間能進(jìn)行通訊,這樣才能構(gòu)成一個(gè)有機(jī)的完整系統(tǒng)瘫怜。
在真實(shí)的應(yīng)用中术徊,組件最終會(huì)構(gòu)成樹形結(jié)構(gòu),就像人類社會(huì)中的家族樹一樣
在樹形結(jié)構(gòu)里面鲸湃,組件之間有幾種典型的關(guān)系:父子關(guān)系赠涮、兄弟關(guān)系、沒有直接關(guān)系暗挑。
相應(yīng)地笋除,組件之間有以下幾種典型的通訊方案:
直接的父子關(guān)系
- 父組件通過 this.$refs 訪問子組件
- 子組件 this.$parent 訪問其父組件
直接父子關(guān)系
- 父組件通過 Props 給子組件下發(fā)數(shù)據(jù)
- 子組件通過事件方式給父組件發(fā)送消息
沒有直接關(guān)系
- 簡單場景:借助于事件機(jī)制進(jìn)行通訊
- 復(fù)雜場景:使用狀態(tài)管理容器(例如 Vue 生態(tài)中的 Vuex、React 生態(tài)中的 Redux炸裆、Mobx 等)
利用 sessionStorage 和 localStorage 進(jìn)行通訊
無論你使用什么前端框架垃它,組件之間的通訊都離開不以上幾種方案,這些方案與具體框架無關(guān)烹看。
父子組件通信
組件設(shè)計(jì)初衷就是要配合使用的嗤瞎,最常見的就是形成父子組件的關(guān)系:組件 A 在它的模板中使用了組件 B。它們之間必然需要相互通信:父組件可能要給子組件下發(fā)數(shù)據(jù)听系,子組件則可能要將它內(nèi)部發(fā)生的事情告知父組件。然而虹菲,通過一個(gè)良好定義的接口來盡可能將父子組件解耦也是很重要的靠胜。這保證了每個(gè)組件的代碼可以在相對(duì)隔離的環(huán)境中書寫和理解,從而提高了其可維護(hù)性和復(fù)用性毕源。 在 Vue 中浪漠,父子組件的關(guān)系可以總結(jié)為 prop 向下傳遞揽乱,事件向上傳遞群叶。父組件通過 prop 給子組件下發(fā)數(shù)據(jù)掰派,子組件通過事件給父組件發(fā)送消息慕匠∠Γ看看它們是怎么工作的腐缤。
父傳子(Props Down)
我們可以用 v-bind 來動(dòng)態(tài)地將 prop 綁定到父組件的數(shù)據(jù)债查。每當(dāng)父組件的數(shù)據(jù)變化時(shí)毁嗦,該變化也會(huì)傳導(dǎo)給子組件:
父組件
<div>
<input v-model="parentMsg">
<br>
<child :myMessage="parentMsg"></child>
<!-- <child v-bind:my-message="parentMsg"></child> -->
</div>
子組件通過prop屬性接受,prop中的變量名不能在data中定義
<script>
import HelloWorld from './components/HelloWorld.vue'
export default {
name: 'app',
props:['myMessage']
components: {
HelloWorld
}
}
</script>
單向數(shù)據(jù)流
Prop 是單向綁定的:當(dāng)父組件的屬性變化時(shí),將傳導(dǎo)給子組件娘纷,但是反過來不會(huì)嫁审。這是為了防止子組件無意間修改了父組件的狀態(tài),來避免應(yīng)用的數(shù)據(jù)流變得難以理解赖晶。
另外律适,每次父組件更新時(shí),子組件的所有 prop 都會(huì)更新為最新值遏插。這意味著你不應(yīng)該在子組件內(nèi)部改變 prop捂贿。如果你這么做了,Vue 會(huì)在控制臺(tái)給出警告胳嘲。
另外厂僧,每次父組件更新時(shí),子組件的所有 prop 都會(huì)更新為最新值胎围。這意味著你不應(yīng)該在子組件內(nèi)部改變 prop吁系。如果你這么做了,Vue 會(huì)在控制臺(tái)給出警告白魂。
- 1汽纤、Prop 作為初始值傳入后,子組件想把它當(dāng)作局部數(shù)據(jù)來用
- 2福荸、Prop 作為原始數(shù)據(jù)傳入蕴坪,由子組件處理成其它數(shù)據(jù)輸出
對(duì)這兩種情況,正確的應(yīng)對(duì)方式是:
-
定義一個(gè)局部變量敬锐,并用 prop 的值初始化它:
props: ['initialCounter'], data: function () { // var a = 1 // var b = a // a = 123 // b ? // b 456 // a ? return { counter: this.initialCounter } }
2.定義一個(gè)計(jì)算屬性背传,處理 prop 的值并返回:
// ... props: ['size'], computed: { normalizedSize: function () { return this.size.trim().toLowerCase() } },
注意在 JavaScript 中對(duì)象和數(shù)組是引用類型,指向同一個(gè)內(nèi)存空間台夺,如果 prop 是一個(gè)對(duì)象或數(shù)組径玖,在子組件內(nèi)部改變它會(huì)影響父組件的狀態(tài)。即便引用類型可以颤介,也不要利用這個(gè)特性梳星,記住一個(gè)原則:組件的數(shù)據(jù)狀態(tài)在組件內(nèi)部管理維護(hù),不要在其他位置去修改它
Prop 驗(yàn)證(這個(gè)我們當(dāng)前用不到滚朵,有多余時(shí)間了解的可以學(xué)冤灾,不講)
我們可以為組件的 prop 指定驗(yàn)證規(guī)則。如果傳入的數(shù)據(jù)不符合要求辕近,Vue 會(huì)發(fā)出警告韵吨。這對(duì)于開發(fā)給他人使用的組件非常有用。 要指定驗(yàn)證規(guī)則移宅,需要用對(duì)象的形式來定義 prop归粉,而不能用字符串?dāng)?shù)組:
export default {
data(){
return {}
},
props: {
// 基礎(chǔ)類型檢測(cè) (`null` 指允許任何類型)
propA: Number,
// 可能是多種類型
propB: [String, Number],
// 必傳且是字符串
propC: {
type: String,
required: true
},
// 數(shù)值且有默認(rèn)值
propD: {
type: Number,
default: 100
},
// 數(shù)組/對(duì)象的默認(rèn)值應(yīng)當(dāng)由一個(gè)工廠函數(shù)返回
propE: {
type: Object,
default: function () {
return { message: 'hello' }
}
},
// 自定義驗(yàn)證函數(shù)
propF: {
validator: function (value) {
return value > 10
}
}
}
}
type 可以是下面原生構(gòu)造器:
- String
- Number
- Boolean
- Function
- Object
- Array
- Symbol type 也可以是一個(gè)自定義構(gòu)造器函數(shù)椿疗,使用 instanceof 檢測(cè)。當(dāng) prop 驗(yàn)證失敗盏浇,Vue 會(huì)拋出警告 (如果使用的是開發(fā)版本)变丧。 注意 prop 會(huì)在組件實(shí)例創(chuàng)建之前進(jìn)行校驗(yàn),所以在 default 或 validator 函數(shù)里绢掰,諸如 data痒蓬、computed 或 methods 等實(shí)例屬性還無法使用。
子傳父(Events Up)
我們知道滴劲,父組件使用 prop 傳遞數(shù)據(jù)給子組件攻晒。但子組件怎么跟父組件通信呢?這個(gè)時(shí)候 Vue 的自定義事件系統(tǒng)就派得上用場了班挖。
1鲁捏、在子組件中調(diào)用 $emit() 方法發(fā)布一個(gè)事件
methods: {
incrementCounter: function () {
this.counter += 1
// 發(fā)布一個(gè)名字叫 increment 的事件
this.$emit('increment')
// this.$emit('update:foo', "傳遞過去的參數(shù)");
}
},
2、在父組件中提供一個(gè)子組件內(nèi)部發(fā)布的事件處理函數(shù)
new Vue({
el: '#counter-event-example',
data: {
total: 0
},
methods: {
incrementTotal: function () {
this.total += 1
}
}
});
-
在使用子組件的模板的標(biāo)簽上訂閱子組件內(nèi)部發(fā)布的事件
<div id="counter-event-example"> <p>{{ total }}</p> <!-- 訂閱子組件內(nèi)部發(fā)布的 increment 事件 當(dāng)子組件內(nèi)部 $commit('increment') 發(fā)布的時(shí)候萧芙,就會(huì)調(diào)用到父組件中的 incrementTotal 方法 --> <button-counter v-on:increment="incrementTotal"></button-counter> </div>
非父子組件通信
簡單場景:Event Bus (少用)
有時(shí)候给梅,非父子關(guān)系的兩個(gè)組件之間也需要通信。在簡單的場景下双揪,可以使用一個(gè)空的 Vue 實(shí)例作為事件總線:
var bus = new Vue();
// 觸發(fā)組件 A 中的事件
bus.$emit('id-selected', 1);
// 在組件 B 創(chuàng)建的鉤子中監(jiān)聽事件
bus.$on('id-selected', function (id) {
// ...
});
復(fù)雜場景:Vuex(最常用)
在復(fù)雜的情況下动羽,我們應(yīng)該考慮使用專門的狀態(tài)管理模式。
第七章Vuex
組件通信
- 父子通信
- Props Down
- Events Up
- 非父子怎么辦渔期?
- Vuex
- 全局的 Global Bus
Vuex 是什么
Vuex 是一個(gè)專為 Vue.js 應(yīng)用程序開發(fā)的狀態(tài)管理模式运吓。它采用集中式存儲(chǔ)管理應(yīng)用的所有組件的狀態(tài),并以相應(yīng)的規(guī)則保證狀態(tài)以一種可預(yù)測(cè)的方式發(fā)生變化疯趟。Vuex 也集成到 Vue 的官方調(diào)試工具 devtools extension拘哨,提供了諸如零配置的 time-travel 調(diào)試、狀態(tài)快照導(dǎo)入導(dǎo)出等高級(jí)調(diào)試功能什么情況下我應(yīng)該使用 Vuex
Vuex 可以幫助我們管理共享狀態(tài)信峻,并附帶了更多的概念和框架倦青。這需要對(duì)短期和長期效益進(jìn)行權(quán)衡。
如果您不打算開發(fā)大型單頁應(yīng)用盹舞,使用 Vuex 可能是繁瑣冗余的产镐。確實(shí)是如此——如果您的應(yīng)用夠簡單,您最好不要使用 Vuex矾策。一個(gè)簡單的 store 模式就足夠您所需了。但是峭沦,如果您需要構(gòu)建一個(gè)中大型單頁應(yīng)用贾虽,您很可能會(huì)考慮如何更好地在組件外部管理狀態(tài),Vuex 將會(huì)成為自然而然的選擇吼鱼。
基本用法
在SPA單頁面組件的開發(fā)中 Vue的vuex和React的Redux 都統(tǒng)稱為同一狀態(tài)管理蓬豁,個(gè)人的理解是全局狀態(tài)管理更合適绰咽;簡單的理解就是你在state中定義了一個(gè)數(shù)據(jù)之后,你可以在所在項(xiàng)目中的任何一個(gè)組件里進(jìn)行獲取地粪、進(jìn)行修改取募,并且你的修改可以得到全局的響應(yīng)變更。下面咱們一步一步地剖析下vuex的使用:
1蟆技、安裝vuex
npm install vuex --save
2玩敏、創(chuàng)建倉庫 然后 在src文件目錄下新建一個(gè)名為store的文件夾,為方便引入并在store文件夾里新建一個(gè)index.js,里面的內(nèi)容如下: 如果腳手架生成有vuex那直接跳到3步质礼。
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const store = new Vuex.Store();
export default store;
接下來旺聚,在 main.js里面引入store,然后再全局注入一下眶蕉,這樣一來就可以在任何一個(gè)組件里面使用this.$store了:
import store from './store'//引入store
new Vue({
store
render: h => h(App),
}).$mount('#app')
state:狀態(tài)
設(shè)置倉庫要保存的值和操作那些值的方法 回到store文件的index.js里面砰粹,我們先聲明一個(gè)state變量,并賦值一個(gè)空對(duì)象給它造挽,里面隨便定義兩個(gè)初始屬性值碱璃;然后再在實(shí)例化的Vuex.Store里面?zhèn)魅胍粋€(gè)空對(duì)象,并把剛聲明的變量state仍里面:
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const state={//要設(shè)置的全局訪問的state對(duì)象
showFooter: true,
changableNum:0
//要設(shè)置的初始屬性值
};
const store = new Vuex.Store({
state
});
export default store;
getters:計(jì)算屬性
現(xiàn)在可以用this.store.state.changebleNum在任何一個(gè)組件里面獲取showfooter和changebleNum定義的值了饭入,但這不是理想的獲取方式嵌器;vuex官方API提供了一個(gè)getters,和vue計(jì)算屬性computed一樣圣拄,來實(shí)時(shí)監(jiān)聽state值的變化(最新狀態(tài))嘴秸,并把它也仍進(jìn)Vuex.Store里面,具體看下面代碼:
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const state={ //要設(shè)置的全局訪問的state對(duì)象
showFooter: true,
changableNum:0
//要設(shè)置的初始屬性值
};
const getters = { //實(shí)時(shí)監(jiān)聽state值的變化(最新狀態(tài))
isShow(state) { //方法名隨意,主要是來承載變化的showFooter的值
return state.showFooter
},
getChangedNum(){ //方法名隨意,主要是用來承載變化的changableNum的值
return state.changebleNum
}
};
const store = new Vuex.Store({
state,
getters
});
export default store;
mutations:更改狀態(tài)
光有定義的state的初始值庇谆,不改變它不是我們想要的需求岳掐,接下來要說的就是mutations了,mutattions也是一個(gè)對(duì)象饭耳,這個(gè)對(duì)象里面可以放改變state的初始值的方法串述,具體的用法就是給里面的方法傳入?yún)?shù)state或額外的參數(shù),然后利用vue的雙向數(shù)據(jù)驅(qū)動(dòng)進(jìn)行值的改變,同樣的定義好之后也把這個(gè)mutations扔進(jìn)Vuex.Store里面寞肖,如下:
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const state={ //要設(shè)置的全局訪問的state對(duì)象
showFooter: true,
changableNum:0
//要設(shè)置的初始屬性值
};
const getters = { //實(shí)時(shí)監(jiān)聽state值的變化(最新狀態(tài))
isShow(state) { //承載變化的showFooter的值
return state.showFooter
},
getChangedNum(){ //承載變化的changebleNum的值
return state.changableNum
}
};
const mutations = {
show(state) { //自定義改變state初始值的方法纲酗,這里面的參數(shù)除了state之外還可以再傳額外的參數(shù)(變量或?qū)ο?;
state.showFooter = true;
},
hide(state) { //同上
state.showFooter = false;
},
newNum(state,sum){ //同上,這里面的參數(shù)除了state之外還傳了需要增加的值sum
state.changableNum+=sum;
}
};
const store = new Vuex.Store({
state,
getters,
mutations
});
export default store;
actions:異步操作
這時(shí)候你完全可以用 this.store.commit('hide') 以及 this.store.commit('newNum',sum)方法,兩個(gè)組件里用執(zhí)行得到的值吮螺,每次都是一樣的,這樣肯定不是理想的需求
vuex官方API還提供了一個(gè)actions,這個(gè)actions也是個(gè)對(duì)象變量鸠补,最大的作用就是里面的Action方法 可以包含任意異步操作萝风,這里面的方法是用來異步觸發(fā)mutations里面的方法,actions里面自定義的函數(shù)接收一個(gè)context參數(shù)和要變化的形參紫岩,context與store實(shí)例具有相同的方法和屬性规惰,所以它可以執(zhí)行context.commit(' '),然后也不要忘了把它也扔進(jìn)Vuex.Store里面:
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const state={ //要設(shè)置的全局訪問的state對(duì)象
showFooter: true,
changableNum:0
//要設(shè)置的初始屬性值
};
const getters = { //實(shí)時(shí)監(jiān)聽state值的變化(最新狀態(tài))
isShow(state) { //承載變化的showFooter的值
return state.showFooter
},
getChangedNum(){ //承載變化的changebleNum的值
return state.changableNum
}
};
const mutations = {
show(state) { //自定義改變state初始值的方法,這里面的參數(shù)除了state之外還可以再傳額外的參數(shù)(變量或?qū)ο?;
state.showFooter = true;
},
hide(state) { //同上
state.showFooter = false;
},
newNum(state,sum){ //同上泉蝌,這里面的參數(shù)除了state之外還傳了需要增加的值sum
state.changableNum+=sum;
}
};
const actions = {
hideFooter(context) { //自定義觸發(fā)mutations里函數(shù)的方法歇万,context與store 實(shí)例具有相同方法和屬性
context.commit('hide');
},
showFooter(context) { //同上注釋
context.commit('show');
},
getNewNum(context,num){ //同上注釋,num為要變化的形參
context.commit('newNum',num)
}
};
const store = new Vuex.Store({
state,
getters,
mutations,
actions
});
export default store;
this.store.store.dispatch('increment') 觸發(fā) actions
// 以載荷形式分發(fā) actions this.$store.dispatch('incrementAsync', { amount: 10 }) // 以對(duì)象形式分發(fā) actions this.$store.dispatch({ type: 'incrementAsync', amount: 10 })
而在外部組件里進(jìn)行全局執(zhí)行actions里面方法的時(shí)候梨与,你只需要用執(zhí)行this.$store.dispatch('hideFooter')
或this.$store.dispatch('showFooter')
以及this.$store.dispatch('getNewNum'堕花,6)
//6要變化的實(shí)參,這樣就可以全局改變改變showfooter或changebleNum的值了
不要濫用 Vuex,推薦方式一粥鞋,能父子的就父子缘挽,非父子的使用 Vuex。
總結(jié):
- 把需要共享的狀態(tài)放到 Vuex 容器中進(jìn)行管理呻粹,不需要共享的還是放到組件內(nèi)部去管理
- 容器也支持計(jì)算屬性 getters
- 修改容器的狀態(tài)務(wù)必通過 mutation 函數(shù)
- 注意:不要在 mutation 函數(shù)中執(zhí)行異步操作去修改 state
- 如果需要需要執(zhí)行異步操作修改 state
- 定義 action
- 在 action 中執(zhí)行異步操作
- 在 action 中執(zhí)行異步操作結(jié)束只有通過提交 mutation 的方式來修改數(shù)據(jù)狀態(tài)
簡單一句話:非異步操作修改狀態(tài)使用 mutation壕曼,異步操作修改狀態(tài)使用 action 。其次我們使用 Vuex 并不意味著你需要將所有的狀態(tài)放入 Vuex等浊。雖然將所有的狀態(tài)放到 Vuex 會(huì)使?fàn)顟B(tài)變化更顯式和易調(diào)試腮郊,但也會(huì)使代碼變得冗長和不直觀。如果有些狀態(tài)嚴(yán)格屬于單個(gè)組件筹燕,最好還是作為組件的局部狀態(tài)轧飞。你應(yīng)該根據(jù)你的應(yīng)用開發(fā)需要進(jìn)行權(quán)衡和確定。
modules 模塊化 以及 組件中引入 mapGetters撒踪、mapActions 和 mapStates的使用,這些目前不講过咬,有精力的同學(xué)可以自行看
第八章 Vue-router
單頁面應(yīng)用程序:
- 網(wǎng)易云音樂
- CODING
單頁應(yīng)用(英語:single-page application,縮寫SPA):在傳統(tǒng)的網(wǎng)頁應(yīng)用中制妄,瀏覽器更多的是充當(dāng)一個(gè)展示層掸绞,路由處理、服務(wù)調(diào)用耕捞、頁面跳轉(zhuǎn)流程都由服務(wù)端來處理衔掸。即 MVC 都放在服務(wù)器端,SPA技術(shù)將邏輯從服務(wù)器轉(zhuǎn)移到了客戶端。這導(dǎo)致Web服務(wù)器發(fā)展為一個(gè)純數(shù)據(jù)API或Web服務(wù)俺抽。這種架構(gòu)的轉(zhuǎn)變?cè)谝恍┤ψ又斜环Q為“瘦服務(wù)器架構(gòu)”敞映,以強(qiáng)調(diào)復(fù)雜性已從服務(wù)端轉(zhuǎn)移到客戶端,并認(rèn)為這最終降低了系統(tǒng)的整體復(fù)雜性磷斧。
傳統(tǒng)的網(wǎng)站有以下特點(diǎn):
- 重服務(wù)端振愿,由于 MVC 都存在于服務(wù)器上诗芜,因此這類應(yīng)用在開發(fā)資源和開發(fā)的重心都偏向后端,往往是后端工程師來主導(dǎo)整個(gè)項(xiàng)目開發(fā)埃疫;
- 頁面頻繁刷新,由于瀏覽器端只是一個(gè)展現(xiàn)層孩哑,當(dāng)頁面功能有所變化的時(shí)栓霜,頁面就刷新,這會(huì)導(dǎo)致資源的浪費(fèi)横蜒,用戶需要花費(fèi)額外的時(shí)間等待頁面刷新胳蛮,用戶體驗(yàn)不佳。
單頁面應(yīng)用丛晌,只有一張Web頁面的應(yīng)用仅炊,是一種從Web服務(wù)器加載的富客戶端,單頁面跳轉(zhuǎn)僅刷新局部資源 澎蛛,公共資源(js抚垄、css等)僅需加載一次。
SPA優(yōu)缺點(diǎn)
優(yōu)點(diǎn):
- 無刷新體驗(yàn)谋逻,這個(gè)應(yīng)該是最顯著的有點(diǎn)呆馁,由于路由分發(fā)直接在瀏覽器端完成,頁面是不刷新毁兆,對(duì)用戶的響應(yīng)非常及時(shí)浙滤,因此提升了用戶體驗(yàn)
- 減輕服務(wù)器壓力,服務(wù)器只用出數(shù)據(jù)就可以气堕,不用管展示邏輯和頁面合成纺腊,吞吐能力會(huì)提高幾倍
- 完全的前端組件化,前端開發(fā)不再以頁面為單位茎芭,更多地采用組件化的思想揖膜,代碼結(jié)構(gòu)和組織方式更加規(guī)范化,便于修改和調(diào)整
- 良好的前后端分離開發(fā)骗爆,前端負(fù)責(zé)界面顯示次氨,后端負(fù)責(zé)數(shù)據(jù)存儲(chǔ)和計(jì)算,各司其職摘投,不會(huì)把前后端的邏輯混雜在一起
缺點(diǎn):
- 不利于 SEO煮寡,單頁頁面,數(shù)據(jù)在前端渲染犀呼,就意味著沒有 SEO幸撕,或者需要使用變通的方案(不過目前可以配置服務(wù)端渲染來解決這個(gè)問題,但還不夠足夠成熟)
- 初次加載耗時(shí)相對(duì)增多外臂,要在一個(gè)頁面上為用戶提供產(chǎn)品的所有功能坐儿,在這個(gè)頁面加載的時(shí)候,首先要加載大量的靜態(tài)資源,這個(gè)加載時(shí)間相對(duì)比較長
- 較高的前端開發(fā)門檻貌矿,對(duì)開發(fā)人員技能水平要求較高炭菌,不再是『切切圖,畫畫頁面這么簡單』
- 低版本瀏覽器兼容差
路由
前端路由目前主要有兩種方法:
- 利用url的hash,就是常用的錨點(diǎn)(#)操作逛漫,類似頁面中點(diǎn)擊某小圖標(biāo)黑低,返回頁面頂部,JS通過hashChange事件來監(jiān)聽url的改變酌毡,IE7及以下需要輪詢進(jìn)行實(shí)現(xiàn)克握。一般常用框架的路由機(jī)制都是用的這種方法,例如Angualrjs自帶的ngRoute和二次開發(fā)模塊ui-router枷踏,react的react-route,vue-route…
- 利用HTML5的History模式菩暗,使url看起來類似普通網(wǎng)站,以”/”分割旭蠕,沒有”#”停团,但頁面并沒有跳轉(zhuǎn),不過使用這種模式需要服務(wù)器端的支持掏熬,服務(wù)器在接收到所有的請(qǐng)求后客蹋,都指向同一個(gè)html文件,通過historyAPI孽江,監(jiān)聽popState事件讶坯,用pushState和replaceState來實(shí)現(xiàn)。
SPA 前端路由原理與實(shí)現(xiàn)方式:通常 SPA 中前端路由有2中實(shí)現(xiàn)方式
- 修改 url 中 Hash
- 利用 H5 中的 history
Vue-router
Vue-router 是 Vue.js 官方的路由管理器岗屏。它和 Vue.js 的核心深度集成辆琅,讓構(gòu)建單頁面應(yīng)用變得易如反掌
安裝
使用腳手架初始化的時(shí)候可以初始化安裝vue-router,如果沒有就手動(dòng)下載和使用
npm install vue-router
使用
新建router文件夾,然后建立一個(gè)index.js文件这刷,寫入路由的代碼
import Vue from 'vue' //引入Vue
import Router from 'vue-router' //引入vue-router
import Hello from '@/components/HelloWorld' //引入組件目錄下的HelloWorld.vue組件
import Demo from '@/components/Demo' //引入組件目錄下的Demo.vue組件
Vue.use(Router) //Vue全局使用Router
export default new Router({
routes: [ //配置路由婉烟,這里是個(gè)數(shù)組
{ //每一個(gè)鏈接都是一個(gè)對(duì)象
path: '/', //鏈接路徑 當(dāng)路徑為 http://localhost:8080/#/顯示此組件
name: 'Hello', //路由名稱,
component: Hello //對(duì)應(yīng)要顯示的組件
},
{ //每一個(gè)鏈接都是一個(gè)對(duì)象
path: '/demo', //鏈接路徑暇屋,當(dāng)路徑為 http://localhost:8080/#/demo顯示此組件
name: 'Demo', //路由名稱似袁,
component: Demo //對(duì)應(yīng)要顯示的組件
}
]
})
在main.js中使用router
import Vue from 'vue'
import App from './App.vue'
import router from "./router/index.js" // 導(dǎo)入路由
Vue.config.productionTip = false
new Vue({
router, // 使用路由
render: h => h(App),
}).$mount('#app')
在App.js中要使用 router-view
組件
<template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png">
<!-- 路由出口 -->
<!-- 路由匹配到的組件將渲染在這里 -->
<router-view></router-view>
</div>
</template>
路由跳轉(zhuǎn)方式
-
聲明式導(dǎo)航
<template> <div id="app"> <h1>Hello App!</h1> <p> <!-- 使用 router-link 組件來導(dǎo)航. --> <!-- 通過傳入 `to` 屬性指定鏈接. --> <!-- <router-link> 默認(rèn)會(huì)被渲染成一個(gè) `<a>` 標(biāo)簽 --> <router-link to="/">Go to root</router-link> <router-link to="/demo">Go to demo</router-link> </p> <!-- 路由出口 --> <!-- 路由匹配到的組件將渲染在這里 --> <router-view></router-view> </div> </template>
編程式導(dǎo)航
除了使用 創(chuàng)建 a 標(biāo)簽來定義導(dǎo)航鏈接,我們還可以借助 router 的實(shí)例方法咐刨,通過編寫代碼來實(shí)現(xiàn)
前進(jìn)和后退this.router.go(1):功能跟我們?yōu)g覽器上的后退和前進(jìn)按鈕一樣昙衅,這在業(yè)務(wù)邏輯中經(jīng)常用到。比如條件不滿足時(shí)定鸟,我們需要后退而涉。
app.vue文件里加入一個(gè)按鈕,按鈕并綁定一個(gè)goback( )方法
<button @click="goback">后退</button>
<script>
export default {
name: 'app',
methods:{
goback(){
this.$router.go(-1);
}
}
}
</script>
編程式導(dǎo)航都作用就是跳轉(zhuǎn)联予,比如我們判斷用戶名和密碼正確時(shí)啼县,需要跳轉(zhuǎn)到用戶中心頁面或者首頁材原,都用到這個(gè)編程的方法來操作路由。this.$router.push
export default {
name: 'app',
methods:{
goback(){
this.$router.go(-1);
},
goHome(){
// 字符串
this.$router.push('/');
}
}
}
子路由
export default new Router({
routes: [ //配置路由季眷,這里是個(gè)數(shù)組
{ //每一個(gè)鏈接都是一個(gè)對(duì)象
path: '/', //鏈接路徑
name: 'Hello', //路由名稱余蟹,
component: Hello //對(duì)應(yīng)要顯示的組件
},
{ //每一個(gè)鏈接都是一個(gè)對(duì)象
path: '/demo', //鏈接路徑
name: 'Demo', //路由名稱,
component: Demo //對(duì)應(yīng)要顯示的組件
},
{
// 對(duì)某個(gè)路由使用子路由子刮,那么需得在Hi1組件內(nèi)使用 <router-view></router-view>客叉,否則切換
// 了子路由也無法顯示出來
path:'/h1',
name:'Hi1',
component:Hi1, // 當(dāng)訪問 /h1的時(shí)候顯示Hi1這個(gè)組件
children:[
{path:"/", component:Hi1}, // 當(dāng)訪問 /h1的時(shí)候<router-view></router-view>被這個(gè)組件替換,就會(huì)有2個(gè)Hi1組件內(nèi)容话告。一般不這么寫,一般這么寫{ path: '', component: Hi1 },
{path:"h2", component:Hi2}, // 當(dāng) /h1/h2 匹配成功卵慰,Hi2會(huì)被渲染在 Hi1 的 <router-view> 中
{path:"h3", component:Hi3} // 當(dāng) /h1/h3 匹配成功沙郭,Hi3會(huì)被渲染在 Hi1 的 <router-view> 中
]
}
]
})
動(dòng)態(tài)路由
- 動(dòng)態(tài)路由的匹配和獲取值
const router = new VueRouter({
routes: [
{ path: '/user/:id', component: User }
]
})
---------在User組件中可以通過下面方式去拿------------
{{$route.params.id}}
- 動(dòng)態(tài)路由的跳轉(zhuǎn)
第一種聲明式動(dòng)態(tài)路由跳轉(zhuǎn)
<!-- 動(dòng)態(tài)路由通過命名路由跳轉(zhuǎn) /user/123 -->
<router-link :to="{ name: 'user', params: { userId: 123 }}">User</router-link>
第二種編程式導(dǎo)航的動(dòng)態(tài)路由跳轉(zhuǎn)
const userId = '123'
// 對(duì)象,命名的動(dòng)態(tài)路由
router.push({ name: 'user', params: { userId }}) // -> /user/123
// 如果提供了 path裳朋,params 會(huì)被忽略
router.push({ path: `/user/${userId}` }) // -> /user/123
// 這里的 params 不生效
router.push({ path: '/user', params: { userId }}) // -> /user
路由重定向
重定向也是通過 routes 配置來完成病线,下面例子是從 /a 重定向到 /b
const router = new VueRouter({
routes: [
{ path: '/a', redirect: '/b' }
]
})
重定向的目標(biāo)也可以是一個(gè)命名的路由:
const router = new VueRouter({
routes: [
{ path: '/a', redirect: { name: 'foo' }}
]
})
路由守衛(wèi)
(路由的聲明周期鉤子函數(shù),進(jìn)階部分學(xué)習(xí))