前言
學習前端勢在必行, 這裏是一個初學者(只寫過 JS+BootStrap)學習 vue3 的學習筆記, 課程在 玩轉 Vue 3 全家桶 (geekbang.org)
爲什麼是 vue3
前端發展歷程
- 純靜態網頁: 純靜態, 手動更新頁面
- 模板語言網頁: 直接嵌入後端數據, 數據更新需要重新刷新頁面
- ajax: 前端異步獲取數據並動態刷新
- jquery+bootStrap: 通過 jquery 操作 dom, bootStrap 做基本的響應式和柵格處理
- angularJs+nodeJs: MVVM 模式, 前端可以入侵到後端,
數據驅動頁面
, 數據變化則頁面自動變化, 而不需要操作 dom, 開發者只需要關注數據的變化, 對 dom 的修改由框架完成 - 百花齊放: angularJs/vue/react.....
三大框架
- angularJs
- vue
- react
實現原理
- angular : 髒檢查, 每次交互都檢查數據是否變化, 從而更新 dom
- vue: 響應式, 對於每個需要變化的數據都建立一個 watcher 監聽數據的屬性, 有變化時才通知修改對應的 dom
- react: 虛擬 dom, 通過 js 來生成虛擬的 dom, diff 檢測數據更新直接更改虛擬 dom, 更快速
vue 和 angular 區別
- angular 通過 diff 來自己進行數據變化的感知, vue 框架本身在數據變化時會主動通知
vue 和 react 區別
vue爲了實現數據感知, 需要在框架內生成若干 watcher, 數量多就影響性能.
react 生成虛擬 dom, 每次需要對虛擬 dom 進行 diff 來得知數據變化, 當虛擬 dom 很大則影響性能
- react 使用將虛擬 dom 分片的方式將 dom 切片, 在瀏覽器空閒時再進行 diff, 每次計算一片, 當瀏覽器需要計算時下次讓給瀏覽器, 解決卡頓
- vue2 引入了虛擬 dom, 取 react 之長, 對於組件之間的變化, 才通過 watcher 通知, 對於組件內的變化, 通過虛擬 dom 來更新, 組件數量不會很多, 解決了 watcher 多導致的性能問題, 同時, 每個組件都單獨的虛擬 dom, 也避免虛擬 dom 大導致的性能問題
- react 將 jsx 編譯成 js 執行, 所以語法都是 js 本身的語法和特徵
- vue 是自己的語法, vue 通過語法檢測數據是否是需要監聽的, 在 vue3中做到了極致, vue3對代碼進行精準的分類, 只有需要監聽的數據更改才進行虛擬 dom 的修改, 不需要監聽的數據, 越過了虛擬 dom 檢測, 速度更快
小知識
Q: vue 在引入虛擬 dom 後, 需不需要 react 的分片來提高性能?
A: 不需要, vue 的虛擬 dom 是組件級別, 所以虛擬 dom 不大, 進行 diff 不會有性能問題
清單應用
編寫一個清單應用, 類似於各種 TODO 程序, 用戶在輸入框中寫要做的事情, 回車會加入到下面的若干 TODO 列表中, 同時列表某一條可以點擊完成, 呈現出不同的效果
思想的轉變
接觸過傳統的 JS+BootStrop 的開發者來講, 可能對於這種需求, 思路是找到輸入框並進行監聽, 當用戶輸入後, 先在 dom 中找到對應元素, 然後進行修改dom, 這種思路需要轉變
對於 MVVM 方式的框架來講, 我們只需要關注數據是怎麼變化, 而不是 dom 怎麼操作
列表的加載
我們先進行數據的展示, 之前說過, 我們只關注數據, 在 vue 中, 我們可以在 dom 中使用 {{}}
來進行已定義的數據展示, 使用 v-for
可以循環列表類型的數據
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://unpkg.com/vue@next"></script>
</head>
<body>
<div id="app">
<ul>
<!-- v-for 循環 todos, 每個值叫 todo, li 的值爲每個 todo -->
<li v-for="todo in todos"> {{todo}} </li>
</ul>
</div>
</body>
<script>
// 建立變量 App
const App = {
// data() 返回監聽的變量
data() {
return {
todos: ["喫飯", "睡覺"],
}
},
}
// 創建 App, 綁定到 id app
Vue.createApp(App).mount("#app")
</script>
</html>
數據的追加
MVVM 中, 數據的修改, 框架會自己監聽並重新渲染 dom
使用@keydown.enter
監聽鍵盤迴車事件, 執行方法addTodo
在這裏, 你會發現, 當數據 todos 進行變化後, 相對應的 dom 會自己發生變化, 這就是 MVVM 的奇妙之處, 數據驅動頁面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://unpkg.com/vue@next"></script>
</head>
<body>
<div id="app">
<!-- 監聽變量 title 的值-->
<h2>{{title}}</h2>
<!-- v-model 將 input 值綁定到變量 title -->
<!-- @keydown.enter 在回車鍵時觸發方法 addTodo -->
<input type="text" v-model="title" @keydown.enter="addTodo">
<ul>
<!-- v-for 循環 todos, 每個值叫 todo, li 的值爲每個 todo -->
<li v-for="todo in todos"> {{todo}} </li>
</ul>
</div>
</body>
<script>
// 建立變量 App
const App = {
// data() 返回監聽的變量
data() {
return {
title: "",
todos: ["喫飯", "睡覺"],
}
},
// methods 設置方法
methods: {
// 設置 addTodo 方法
addTodo() {
// this 指的是自己, 也就是 App
// 如果 title 爲空, 不添加, 目的是防止無輸入直接回車
if (this.title === ""){
return
}
this.todos.push(this.title) // 給 todos 列表新增一個值爲用戶輸入的 title 值
this.title = "" // 將 title 設置爲空
}
}
}
// 創建 App, 綁定到 id app
Vue.createApp(App).mount("#app")
</script>
</html>
添加完成按鈕和效果
TODO 都是可以進行選中完成的, 我們也要實現, 爲了給每個 todo 添加是否完成的標識, 我們將 todo 的類型從字符串變成對象
還是那句話, 數據驅動頁面, 當我們的 checkbox 的 checked 屬性發生變化時, 也會重新渲染對應的 dom, 導致 class 也動態的增減
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://unpkg.com/vue@next"></script>
</head>
<body>
<div id="app">
<!-- 監聽變量 title 的值-->
<h2>{{title}}</h2>
<!-- v-model 將 input 值綁定到變量 title -->
<!-- @keydown.enter 在回車鍵時觸發方法 addTodo -->
<input type="text" v-model="title" @keydown.enter="addTodo">
<ul>
<!-- v-for 循環 todos, 每個值叫 todo, li 的值爲每個 todo -->
<li v-for="todo in todos">
<!-- 當 type 爲 checkbox 時, v-mode 綁定的變量如果是數組時, 作爲 checkbox 的 value 使用, 當爲 bool 時, 作爲 checkbox 的 checked 值使用 -->
<!-- 使用 todo.done 屬性標識是否選中 -->
<input type="checkbox" v-model="todo.done">
<!-- vue 中, : 用來傳遞數據, 這裏的意思是, 如果 todo.done 爲真, 則將 css done 傳遞給 class -->
<span :class="{done:todo.done}">{{todo.text}}</span>
</li>
</ul>
</div>
</body>
<script>
// 建立變量 App
const App = {
// data() 返回監聽的變量
data() {
return {
title: "",
todos: [
{ done: false, text: "喫飯" },
{ done: true, text: "睡覺" }
],
}
},
// methods 設置方法
methods: {
// 設置 addTodo 方法
addTodo() {
// this 指的是自己, 也就是 App
// 如果 title 爲空, 不添加, 目的是防止無輸入直接回車
if (this.title === "") {
return
}
this.todos.push({
done: false,
text: this.title
}) // 給 todos 列表新增一個值爲用戶輸入的 title 值
this.title = "" // 將 title 設置爲空
}
}
}
// 創建 App, 綁定到 id app
Vue.createApp(App).mount("#app")
</script>
<style>
/* 顏色變灰, 中間加劃線 */
.done {
color: gray;
text-decoration: line-through;
}
</style>
</html>
添加統計
在頁面添加未完成的數量和全部數量
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://unpkg.com/vue@next"></script>
</head>
<body>
<div id="app">
<!-- 監聽變量 title 的值-->
<h2>{{title}}</h2>
<!-- v-model 將 input 值綁定到變量 title -->
<!-- @keydown.enter 在回車鍵時觸發方法 addTodo -->
<input type="text" v-model="title" @keydown.enter="addTodo">
<ul>
<!-- v-for 循環 todos, 每個值叫 todo, li 的值爲每個 todo -->
<li v-for="todo in todos">
<!-- 當 type 爲 checkbox 時, v-mode 綁定的變量如果是數組時, 作爲 checkbox 的 value 使用, 當爲 bool 時, 作爲 checkbox 的 checked 值使用 -->
<!-- 使用 todo.done 屬性標識是否選中 -->
<input type="checkbox" v-model="todo.done">
<!-- vue 中, : 用來傳遞數據, 這裏的意思是, 如果 todo.done 爲真, 則將 css done 傳遞給 class -->
<span :class="{done:todo.done}">{{todo.text}}</span>
</li>
</ul>
<!-- 在 html 中寫代碼的方式雖然可行, 但是不推薦!, 並且性能不好! -->
<div>
<!-- .filter 對 todos 進行篩選, () 內是篩選規則, v 是每個 todo, 如果爲 true 則此 v 篩選通過 -->
<!-- 這裏統計未完成數量, 因此對 v.done 進行反轉, 如果 done 爲 true, 反轉爲 false, 篩選失敗, 反之成功 -->
{{todos.filter(v=>!v.done).length}}
/
<!-- .length 計算列表長度 -->
{{todos.length}}
</div>
</div>
</body>
<script>
// 建立變量 App
const App = {
// data() 返回監聽的變量
data() {
return {
title: "",
todos: [
{ done: false, text: "喫飯" },
{ done: true, text: "睡覺" }
],
}
},
// methods 設置方法
methods: {
// 設置 addTodo 方法
addTodo() {
// this 指的是自己, 也就是 App
// 如果 title 爲空, 不添加, 目的是防止無輸入直接回車
if (this.title === "") {
return
}
this.todos.push({
done: false,
text: this.title
}) // 給 todos 列表新增一個值爲用戶輸入的 title 值
this.title = "" // 將 title 設置爲空
}
}
}
// 創建 App, 綁定到 id app
Vue.createApp(App).mount("#app")
</script>
<style>
/* 顏色變灰, 中間加劃線 */
.done {
color: gray;
text-decoration: line-through;
}
</style>
</html>
如同註釋所說, 雖然在html 中編寫代碼的方式可以實現, 但是並不推薦, 有如下問題:
- 把 JS 代碼放置進了 body 中, 不美觀
- 使用 vue 的計算屬性功能, 會將結果進行緩存優化(當這個屬性在多地方使用時, 只計算一次), 避免每次的重複計算導致性能問題
下面, 我們使用計算屬性功能來完成統計
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://unpkg.com/vue@next"></script>
</head>
<body>
<div id="app">
<!-- 監聽變量 title 的值-->
<h2>{{title}}</h2>
<!-- v-model 將 input 值綁定到變量 title -->
<!-- @keydown.enter 在回車鍵時觸發方法 addTodo -->
<input type="text" v-model="title" @keydown.enter="addTodo">
<ul>
<!-- v-for 循環 todos, 每個值叫 todo, li 的值爲每個 todo -->
<li v-for="todo in todos">
<!-- 當 type 爲 checkbox 時, v-mode 綁定的變量如果是數組時, 作爲 checkbox 的 value 使用, 當爲 bool 時, 作爲 checkbox 的 checked 值使用 -->
<!-- 使用 todo.done 屬性標識是否選中 -->
<input type="checkbox" v-model="todo.done">
<!-- vue 中, : 用來傳遞數據, 這裏的意思是, 如果 todo.done 爲真, 則將 css done 傳遞給 class -->
<span :class="{done:todo.done}">{{todo.text}}</span>
</li>
</ul>
<div>
<!-- 直接調用計算屬性 -->
{{active}}/{{all}}
</div>
</div>
</body>
<script>
// 建立變量 App
const App = {
// data() 返回監聽的變量
data() {
return {
title: "",
todos: [
{ done: false, text: "喫飯" },
{ done: true, text: "睡覺" }
],
}
},
// methods 設置方法
methods: {
// 設置 addTodo 方法
addTodo() {
// this 指的是自己, 也就是 App
// 如果 title 爲空, 不添加, 目的是防止無輸入直接回車
if (this.title === "") {
return
}
this.todos.push({
done: false,
text: this.title
}) // 給 todos 列表新增一個值爲用戶輸入的 title 值
this.title = "" // 將 title 設置爲空
}
},
// computed 設置 vue 的計算屬性
computed: {
// active 屬性, 返回 done 爲 false 的長度
active() {
return this.todos.filter(v => !v.done).length
},
// all 屬性, 返回總長度
all() {
return this.todos.length
}
}
}
// 創建 App, 綁定到 id app
Vue.createApp(App).mount("#app")
</script>
<style>
/* 顏色變灰, 中間加劃線 */
.done {
color: gray;
text-decoration: line-through;
}
</style>
</html>
全選
我們需要加入一個全選框, 當點擊全選時, 所有todo 都選中, 並且, 當手動將所有 todo 選中時, 全選框默認變爲勾選狀態
計算屬性, 不止可以用來做數據統計, 也可以修改對應的數據, 例如
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://unpkg.com/vue@next"></script>
</head>
<body>
<div id="app">
<!-- 監聽變量 title 的值-->
<h2>{{title}}</h2>
<!-- v-model 將 input 值綁定到變量 title -->
<!-- @keydown.enter 在回車鍵時觸發方法 addTodo -->
<input type="text" v-model="title" @keydown.enter="addTodo">
<div>
<!-- checkbox 綁定 allDone 計算屬性, 生成時調用 get 方法, 返回 bool, 作爲 checkbox 的 checked 使用 -->
<!-- 當點擊 checkbox 時, 計算屬性發生變化, 將 checked 作爲參數調用 allDone 的 set 方法 -->
全選<input type="checkbox" v-model="allDone">
<span>{{active}}/{{all}}</span>
</div>
<ul>
<!-- v-for 循環 todos, 每個值叫 todo, li 的值爲每個 todo -->
<li v-for="todo in todos">
<!-- 當 type 爲 checkbox 時, v-mode 綁定的變量如果是數組時, 作爲 checkbox 的 value 使用, 當爲 bool 時, 作爲 checkbox 的 checked 值使用 -->
<!-- 使用 todo.done 屬性標識是否選中 -->
<input type="checkbox" v-model="todo.done">
<!-- vue 中, : 用來傳遞數據, 這裏的意思是, 如果 todo.done 爲真, 則將 css done 傳遞給 class -->
<span :class="{done:todo.done}">{{todo.text}}</span>
</li>
</ul>
<div>
<!-- 直接調用計算屬性 -->
{{active}}/{{all}}
</div>
</div>
</body>
<script>
// 建立變量 App
const App = {
// data() 返回監聽的變量
data() {
return {
title: "",
todos: [
{ done: false, text: "喫飯" },
{ done: true, text: "睡覺" }
],
}
},
// methods 設置方法
methods: {
// 設置 addTodo 方法
addTodo() {
// this 指的是自己, 也就是 App
// 如果 title 爲空, 不添加, 目的是防止無輸入直接回車
if (this.title === "") {
return
}
this.todos.push({
done: false,
text: this.title
}) // 給 todos 列表新增一個值爲用戶輸入的 title 值
this.title = "" // 將 title 設置爲空
}
},
// computed 設置 vue 的計算屬性
computed: {
// active 屬性, 返回 done 爲 false 的長度
active() {
return this.todos.filter(v => !v.done).length
},
// all 屬性, 返回總長度
all() {
return this.todos.length
},
// 計算屬性爲對象時, 可以通過定義 get 和 set 方法來修改屬性
allDone: {
// get 在獲取計算屬性時觸發
get() {
// 調用 this.active 計算屬性, active 返回的是 done 爲 false 的長度, 這裏與0進行比較, 爲0代表全部勾選, 返回 true
return this.active === 0
},
// set 在計算屬性發生變化時觸發
set(val) {
console.log(val)
// forEach 是循環
// 循環 todos , 將每個 done 設置爲 val
this.todos.forEach(todo => {
todo.done = val
});
}
}
}
}
// 創建 App, 綁定到 id app
Vue.createApp(App).mount("#app")
</script>
<style>
/* 顏色變灰, 中間加劃線 */
.done {
color: gray;
text-decoration: line-through;
}
</style>
</html>
清空按鈕
快完成了, 我們添加一個清空按鈕在輸入框之後, 要求點擊清空則將所有已完成條目刪除, 而當沒有已完成條目時, 按鈕不展示
使用 v-if 來進行條件篩選渲染
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://unpkg.com/vue@next"></script>
</head>
<body>
<div id="app">
<!-- 監聽變量 title 的值-->
<h2>{{title}}</h2>
<!-- v-model 將 input 值綁定到變量 title -->
<!-- @keydown.enter 在回車鍵時觸發方法 addTodo -->
<input type="text" v-model="title" @keydown.enter="addTodo">
<!-- v-if 作爲條件篩選, 當值爲 true 才顯示該 dom -->
<!-- @click 在鼠標點擊時觸發, 這裏調用 clear 方法 -->
<button v-if="active<all" @click="clear">清理</button>
<div>
<!-- checkbox 綁定 allDone 計算屬性, 生成時調用 get 方法, 返回 bool, 作爲 checkbox 的 checked 使用 -->
<!-- 當點擊 checkbox 時, 計算屬性發生變化, 將 checked 作爲參數調用 allDone 的 set 方法 -->
全選<input type="checkbox" v-model="allDone">
<span>{{active}}/{{all}}</span>
</div>
<ul>
<!-- v-for 循環 todos, 每個值叫 todo, li 的值爲每個 todo -->
<li v-for="todo in todos">
<!-- 當 type 爲 checkbox 時, v-mode 綁定的變量如果是數組時, 作爲 checkbox 的 value 使用, 當爲 bool 時, 作爲 checkbox 的 checked 值使用 -->
<!-- 使用 todo.done 屬性標識是否選中 -->
<input type="checkbox" v-model="todo.done">
<!-- vue 中, : 用來傳遞數據, 這裏的意思是, 如果 todo.done 爲真, 則將 css done 傳遞給 class -->
<span :class="{done:todo.done}">{{todo.text}}</span>
</li>
</ul>
<div>
<!-- 直接調用計算屬性 -->
{{active}}/{{all}}
</div>
</div>
</body>
<script>
// 建立變量 App
const App = {
// data() 返回監聽的變量
data() {
return {
title: "",
todos: [
{ done: false, text: "喫飯" },
{ done: true, text: "睡覺" }
],
}
},
// methods 設置方法
methods: {
// 設置 addTodo 方法
addTodo() {
// this 指的是自己, 也就是 App
// 如果 title 爲空, 不添加, 目的是防止無輸入直接回車
if (this.title === "") {
return
}
this.todos.push({
done: false,
text: this.title
}) // 給 todos 列表新增一個值爲用戶輸入的 title 值
this.title = "" // 將 title 設置爲空
},
clear() {
// 將 todos 賦值爲老的 todos 中 done 爲 false 的部分
// 去除了已經完成的條目
this.todos = this.todos.filter(v => !v.done)
}
},
// computed 設置 vue 的計算屬性
computed: {
// active 屬性, 返回 done 爲 false 的長度
active() {
return this.todos.filter(v => !v.done).length
},
// all 屬性, 返回總長度
all() {
return this.todos.length
},
// 計算屬性爲對象時, 可以通過定義 get 和 set 方法來修改屬性
allDone: {
// get 在獲取計算屬性時觸發
get() {
// 調用 this.active 計算屬性, active 返回的是 done 爲 false 的長度, 這裏與0進行比較, 爲0代表全部勾選, 返回 true
return this.active === 0
},
// set 在計算屬性發生變化時觸發
set(val) {
console.log(val)
// forEach 是循環
// 循環 todos , 將每個 done 設置爲 val
this.todos.forEach(todo => {
todo.done = val
});
}
}
}
}
// 創建 App, 綁定到 id app
Vue.createApp(App).mount("#app")
</script>
<style>
/* 顏色變灰, 中間加劃線 */
.done {
color: gray;
text-decoration: line-through;
}
</style>
</html>
添加空置提示
最後一步!, 我們爲這個清單應用添加最後一個功能: 當清單爲空時, 展示目前沒有數據
我們這裏搭配使用 v-if
與 v-else
, 來做分支處理, 當然還可以搭配 v-else-if
針對多分支處理
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://unpkg.com/vue@next"></script>
</head>
<body>
<div id="app">
<!-- 監聽變量 title 的值-->
<h2>{{title}}</h2>
<!-- v-model 將 input 值綁定到變量 title -->
<!-- @keydown.enter 在回車鍵時觸發方法 addTodo -->
<input type="text" v-model="title" @keydown.enter="addTodo">
<!-- v-if 作爲條件篩選, 當值爲 true 才顯示該 dom -->
<!-- @click 在鼠標點擊時觸發, 這裏調用 clear 方法 -->
<button v-if="active<all" @click="clear">清理</button>
<div>
<!-- checkbox 綁定 allDone 計算屬性, 生成時調用 get 方法, 返回 bool, 作爲 checkbox 的 checked 使用 -->
<!-- 當點擊 checkbox 時, 計算屬性發生變化, 將 checked 作爲參數調用 allDone 的 set 方法 -->
全選<input type="checkbox" v-model="allDone">
<span>{{active}}/{{all}}</span>
</div>
<!-- 如果長度不爲0代表目前有數據, 展示數據 -->
<ul v-if="all!==0">
<!-- v-for 循環 todos, 每個值叫 todo, li 的值爲每個 todo -->
<li v-for="todo in todos">
<!-- 當 type 爲 checkbox 時, v-mode 綁定的變量如果是數組時, 作爲 checkbox 的 value 使用, 當爲 bool 時, 作爲 checkbox 的 checked 值使用 -->
<!-- 使用 todo.done 屬性標識是否選中 -->
<input type="checkbox" v-model="todo.done">
<!-- vue 中, : 用來傳遞數據, 這裏的意思是, 如果 todo.done 爲真, 則將 css done 傳遞給 class -->
<span :class="{done:todo.done}">{{todo.text}}</span>
</li>
</ul>
<!-- v-else 必須緊跟在 v-if 的 dom 之後, 當 if 不通過執行渲染 else 內容 -->
<div v-else>
暫無數據
</div>
<div>
<!-- 直接調用計算屬性 -->
{{active}}/{{all}}
</div>
</div>
</body>
<script>
// 建立變量 App
const App = {
// data() 返回監聽的變量
data() {
return {
title: "",
todos: [
{ done: false, text: "喫飯" },
{ done: true, text: "睡覺" }
],
}
},
// methods 設置方法
methods: {
// 設置 addTodo 方法
addTodo() {
// this 指的是自己, 也就是 App
// 如果 title 爲空, 不添加, 目的是防止無輸入直接回車
if (this.title === "") {
return
}
this.todos.push({
done: false,
text: this.title
}) // 給 todos 列表新增一個值爲用戶輸入的 title 值
this.title = "" // 將 title 設置爲空
},
clear() {
// 將 todos 賦值爲老的 todos 中 done 爲 false 的部分
// 去除了已經完成的條目
this.todos = this.todos.filter(v => !v.done)
}
},
// computed 設置 vue 的計算屬性
computed: {
// active 屬性, 返回 done 爲 false 的長度
active() {
return this.todos.filter(v => !v.done).length
},
// all 屬性, 返回總長度
all() {
return this.todos.length
},
// 計算屬性爲對象時, 可以通過定義 get 和 set 方法來修改屬性
allDone: {
// get 在獲取計算屬性時觸發
get() {
// 調用 this.active 計算屬性, active 返回的是 done 爲 false 的長度, 這裏與0進行比較, 爲0代表全部勾選, 返回 true
return this.active === 0
},
// set 在計算屬性發生變化時觸發
set(val) {
console.log(val)
// forEach 是循環
// 循環 todos , 將每個 done 設置爲 val
this.todos.forEach(todo => {
todo.done = val
});
}
}
}
}
// 創建 App, 綁定到 id app
Vue.createApp(App).mount("#app")
</script>
<style>
/* 顏色變灰, 中間加劃線 */
.done {
color: gray;
text-decoration: line-through;
}
</style>
</html>
初探 vue3 新特性
相比於 vue2, vue3 的優勢是什麼?
vue2 的歷史遺留問題
- vue2 使用
Flow.js
作爲類型校驗, 但是Flow.js
已經停止維護, 整個社區都在使用TypeScript
來構建基礎庫, vue 也需要這樣 - vue2 內部運行時, 直接執行瀏覽器的 API, 這樣在跨端時, 就需要適配多端,的否則會出現問題
- vue2 並不是真正意義上的代理(響應式), 而是基於
Object.defineProperty()
實現的, 這是對某個屬性進行攔截, 有一些缺陷, 比如無法監聽刪除(vue 使用 $delete 輔助才能達到效果) - vue2 使用 OptionApi, 在代碼比較多時, 對於功能的修改, 需要兼顧
data
,methods
代碼塊, 比較麻煩
vue3的新特性
RFC 機制
這與代碼無關, 這是 vue 團隊的開發工作方式, 對於新的功能和語法, 先放置在 github 徵求意見, 任何人都可以討論和嘗試實現
響應式系統
之前說過, vue2使用Object.defineProperty()
實現響應式, 而開發者必須將defineProperty
監聽的數據明確的寫在代碼中, 這是因爲, defineProperty
對於不存在的屬性無法攔截, 因此必須在data
中聲明監聽變量
而 vue3 可以使用 proxy
, 他的代碼類似於
new Proxy(obj, {
get(){},
set(){}
})
proxy
對 obj 是什麼屬性不做關心, 統一攔截, 還可以監聽更多的格式數據, 例如Set/Map
等
需要注意的是, Proxy
不支持 IE11 以下的瀏覽器
Proxy
是瀏覽器的新特性, 這代表着, 框架隨着更新會和瀏覽器相輔相成, 一起爲前端提供更多可能.
自定義渲染
vue2 內部的模塊都是揉和在一起的, 導致擴展比較困難, 而 vue3 使用流行的 monorepo
的拆包管理方式, 將模塊剝離, 進行解耦.
全部模塊使用 TS 進行重構
TS(TypeScript)帶來了系統類型, 能讓代碼的提示更爲智能, 同時提高代碼的健壯性
這裏博主也推薦大家有空學習一下 TS, 類型註解可以讓代碼的編寫更爲得心應手.
並且 TypeScript 也是目前前端流行的技術, 很多框架都已經使用 TS 來進行底層的編寫
Compostion API
Composition API 作者將他稱之爲組合 API, 從 DEMO 來了解他們的不同
比如我們使用 vue2 來編寫一個累加器, 並且有一個計算屬性顯示累加器 x2的結果
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://unpkg.com/vue@next"></script>
</head>
<body>
<div id="app">
<!-- 點擊觸發 add 函數 count*2, double 計算*2結果 -->
<h1 @click="add">{{count}} x 2 = {{double}}</h1>
</div>
</body>
<script>
// 建立變量 App
const App = {
data(){
return{
count: 1
}
},
methods: {
add(){
this.count += 1
}
},
computed:{
double(){
return this.count * 2
}
}
}
// 創建 App, 綁定到 id app
Vue.createApp(App).mount("#app")
</script>
</html>
而在 vue3 裏, 我們可以使用 setup
來寫
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://unpkg.com/vue@next"></script>
</head>
<body>
<div id="app">
<!-- 點擊觸發 add 函數 count*2, double 計算*2結果 -->
<h1 @click="add">{{state.count}} x 2 = {{double}}</h1>
</div>
</body>
<script>
// 導入所需的模塊
const {reactive, computed} = Vue
// 建立變量 App
let App = {
// setup 這裏返回 App 的一些方法和變量等
setup(){
// 新建 state 對象
const state = reactive({
count: 1 // count 屬性
})
// 新建 add 方法
function add(){
// count 屬性+1
state.count += 1
}
// 創建 double 的計算屬性
const double = computed(()=>state.count * 2)
return {state, add, double} // 返回到外層
}
}
// 創建 App, 綁定到 id app
Vue.createApp(App).mount("#app")
</script>
</html>
使用新版的組合 api 之後, 表面看代碼反而繁瑣了, 但是之前的 OptionsAPI 有幾個嚴重的問題:
- 所有的數據都掛載在 this 裏, 因此對於類型推倒很不友好, 並且在清理和代碼分塊時很難受
- 新增功能都要修改 data, method 塊, 維護困難
- 代碼難以複用, 因爲代碼都糅雜在一起, 還可能會出現命名衝突
而使用組合 API 之後, 好處多多: - api 都是通過 import 引入, 不需要的模塊無需引入
- 將某塊功能所有的 methods/data 封裝在一個獨立的函數中, 複用很容易, 也沒有衝突問題
- 組合 api 新增的 return 等語句, 在實際項目中可清除
新增組件
vue3新增了若干組件, 比如: - Fragment: 不再要求有一個唯一的根節點, 清理無用的 div 標籤
- Teleport: 允許組件渲染在別的元素內, 在開發彈窗組件時特別有用
- Suspense: 異步請求組件
Vite
Vite 跟 vue3 並不是綁定關係, 和 vue 也不是強制綁定, Vite 的競品是 Webpack, 主要提升開發的體驗
傳統的 webpack, 在打包時, 是將所有的代碼和頁面打包完成再啓動, 可能需要幾分鐘, 而 Vite 是階梯式打包, 按需加載, 這樣大大提升了開發體驗
vue2 如何升級到 vue3
原作者說了很多, 大致是使用一些工具來進行升級
但是我個人認爲, 如果是已經完成或者開發中的 vue2 項目, 不建議升級到 vue3, 我做過一些項目, 我認爲前端組件的版本升級帶來的問題很多, 尤其是不兼容問題. 所以我不建議對已有的項目進行升級
而對於新的項目, 可以使用 vue3來從頭開始
搭建 vue3 項目的第一步
環境準備
你必須首先安裝以下軟件:
- NodeJs
初始化代碼
找到你的工作目錄, 命令行輸入
npm init @vitejs/app
初始化會讓你輸入項目的名字, 這裏我們輸入 student
➜ Vue npm init @vitejs/app
@vitejs/create-app is deprecated, use npm init vite instead
? Project name: › student
之後選擇 vue 的項目
? Select a framework: › - Use arrow-keys. Return to submit.
vanilla
❯ vue
react
preact
lit
svelte
回車後再選擇 vue(javaScript) 或者 vue-ts(typeScript語言), 這裏我們先選擇普通的 vue, 方便學習, 之後學習了 TS 之後, 可以選擇 vue-ts
? Select a variant: › - Use arrow-keys. Return to submit.
❯ vue
vue-ts
之後就創建成功了項目
Scaffolding project in /Users/chenming/Work/Code/Vue/student...
Done. Now run:
cd student
npm install
npm run dev
而後在當前文件夾就生成了一個新的文件夾 student
我們使用 vscode 打開 student 目錄
現在目錄下就會生成若干文件
➜ student tree
.
├── README.md
├── index.html 入口文件
├── package.json
├── public 資源文件夾
│ └── favicon.ico
├── src 源碼
│ ├── App.vue 組件
│ ├── assets
│ │ └── logo.png
│ ├── components
│ │ └── HelloWorld.vue
│ └── main.js 入口
└── vite.config.js vite 配置
4 directories, 9 files
運行
此時腳手架就算搭建完成, 然後我們執行命令來安裝依賴
npm install
執行完命令後, 會在當前路徑下生成新的文件夾node_modules
, 裏面存放下載的依賴文件, 通常, 該文件夾不應上傳到代碼倉庫, 而是由開發者本地生成
再執行命令, 在本地啓動 dev 環境
npm run dev
> [email protected] dev
> vite
vite v2.9.14 dev server running at:
> Local: http://localhost:3000/
> Network: use `--host` to expose
ready in 177ms.
而後瀏覽器打開網址 http://localhost:3000/
, 會出現歡迎頁面代表成功
同時, 我們修改文件 src/APP.vue
的內容, 會發現網頁會同步刷新, 這樣就給我們的開發提供了很大的便利
同時, 因爲我們的開發是多頁面, 同時要和後端進行交互, 因此我們還需要安裝兩個組件來幫助我們完成需求, 分別是vuex(管理數據)
和vue-router(管理路由)
npm install vuex@next vue-router@next