????????Vue (讀音 /vju?/,類似于?view
) 是一套用于構建用戶界面的漸進式框架。與其它大型框架不同的是,Vue
?被設計為可以自底向上逐層應用。Vue.js
?的核心是一個允許采用簡潔的模板語法來聲明式地將數據渲染進?DOM
?的系統,只關注視圖層,易于上手。所有東西都是響應式的。本文大部分內容來源于Vue3中文文檔,但結合?uni-app
?做了部分調整,以更有利于開發者快速上手。
1?vue的優勢
????????在傳統開發中,用原生的 JavaScript DOM 操作函數對 DOM 進行頻繁操作的時候,瀏覽器要不停的渲染新的 DOM 樹,導致頁面看起來非常卡頓。vue 是單頁面應用,使頁面局部刷新,不用每次跳轉頁面都要請求所有數據和 DOM ,這樣大大加快了訪問速度和提升用戶體驗。
vue的優勢:
- 輕量級:幾十K的體積
- 界面與邏輯分離,與html接近的概念和寫法
- 響應式雙向數據綁定,更新數據時無需再寫代碼更新界面,反之亦然。
- 組件化,可方便協作。方便造輪子,也就自然有大量輪子可用
- 虛擬DOM,比大多數手寫操作dom的代碼都更高效
- 易于上手,設計直觀、文檔豐富
vue3相比vue2的優勢:
- 響應式系統提升
- 更快,性能比Vue2快1.2~2倍(diff方法優化、靜態提升、時間偵聽器緩存、ssr渲染)
- 更小,按需編譯,體積比Vue2更小
- 組合式API,提供更靈活的寫法,也易于吸引react開發者
- 加強TypeScript支持
2?白話uni-app
????????如果你了解html、js,那么本章節將讓你快速了解uni-app和它們的差異。
2.1?文件類型變化
- 以前是.html文件,開發也是html,運行也是html。
- 現在每個頁面是一個.vue文件,開發是vue,但經過編譯后,運行時已經變成了js文件(如果是uts則可能編譯成kotlin、swift)。
- 現代前端開發,很少直接使用HTML,基本都是開發、編譯、運行。所以?
uni-app
?有編譯器、運行時的概念。
2.2?文件內代碼架構的變化
????????以前一個?html
?大節點,里面有?script
?和?style
?節點;
<!DOCTYPE html><html><head><meta charset="utf-8" /><title></title><script type="text/javascript"></script><style type="text/css"></style></head><body></body></html>
????????現在?template
?是一級節點,用于寫tag組件,?script
?和?style
?是并列的一級節點,也就是有3個一級節點。這個叫vue單文件組成規范。
<template><view>注意必須有一個view,且只能有一個根view。所有內容寫在這個view下面。</view></template><script>export default {}</script><style></style>
2.3?外部文件引用方式變化
????????以前通過script src、link href引入外部的js和css;
<script src="js/jquery-1.10.2.js" type="text/javascript"></script>
<link href="css/bootstrap.css" rel="stylesheet" type="text/css"/>
????????現在是es6的寫法,?import
?引入外部的js模塊(注意不是文件)或css;js要require進來,變成了對象。
<script>var util = require('../../../common/util.js'); //require這個js模塊var formatedPlayTime = util.formatTime(playTime); //調用js模塊的方法</script>
????????而在這個?util.js
?里,要把之前的?function
?封裝為模塊(module)的方法并導出(exports)。只有被導出的方法和屬性才能被外部調用,不導出的屬于模塊內部函數和變量。這是es6的模塊規范。
function formatTime(time) {return time;//這里沒寫邏輯}module.exports = {formatTime: formatTime}
????????當然還有一些高級的用法,比如在導出時可以重命名
// 直接使用js模塊的屬性。var dateUtils = require('../../../common/util.js').dateUtils;// 將js導入并重命名為echarts,然后使用echarts.來繼續執行方法。在hello uni-app有示例import * as echarts from '/components/echarts/echarts.simple.min.js';
????????css外部文件導入。全局樣式,在根目錄下的?app.vue
?里寫入,每個頁面都會加載?app.vue
?里的樣式。
<style>@import "./common/uni.css";.uni-hello-text{color:#7A7E83;}</style>
????????另外,vue支持組件導入,可以更方便的封裝一個包括界面、js、樣式的庫。
2.4?組件/標簽的變化
????????以前是html標簽,比如?<div>
?,現在是小程序組件,比如?<view>
?。標簽與組件的區別:
- 其實標簽是老的概念,標簽屬于瀏覽器內置的東西。
- 但組件,是可以自由擴展的。類似你可以把一段js封裝成函數或模塊,你也可以把一個ui控件封裝成一個組件。
2.5?js的變化
????????以前script里隨便寫js變量和function
<script type="text/javascript">var a;function funa () {}
</script>
????????現在script里默認有export default,在里面寫data、事件和method?
- 寫在?
export default {
?前面的變量,是頁面內部的全局變量,可以在各種方法里使用。 export default {}
?里是一個大json,data、生命周期、method都需要用逗號分隔。- data -> return 里,編寫可以綁定在頁面template模板里的變量,頁面組件的text里綁定data數據使用{{}},比如下面例子中的
textvalue
。而下面的globalvar就不能在模板里綁定使用。在HBuilderX中,敲vdata代碼塊,可以快捷生成data的代碼結構。 - 頁面的生命周期/事件,如下面的
onLoad
,和data平級。 - 模板里要調用的方法,都需要寫在
methods
下面。每個方法也需要用逗號分隔。不需要再使用function
聲明,只要寫在methods
下的函數,都可以在template里調用。
<template><view><text>{{textvalue}}</text><!-- 這里演示了組件值的綁定 --><button :type="buttontype" @click="changetextvalue()">修改為789</button><!-- 這里演示了屬性和事件的綁定 --></view></template>
<script>var globalvar = 1function globalfun(){}export default {data() {return {textvalue:"123",buttontype:"primary"};},onLoad() {globalvar = 2globalfun()this.textvalue="456"//這里修改textvalue的值},methods: {changetextvalue() {this.textvalue="789"//這里修改textvalue的值}}}
</script>
????????在上述例子中,傳統寫法的定義的變量globalvar和函數globalfun,可以在export default { }
里使用,但無法在模板里直接綁定和調用。模板里只能綁定data里的變量、調用methods里的方法。
????????以前的 DOM 操作,如果你想改變某個 DOM 元素的顯示內容,比如一個view的顯示文字:給view設id,然后js里通過選擇器獲取 DOM 元素,進一步通過js進行賦值操作,修改 DOM 元素的屬性或值。
<html><head><script type="text/javascript">document.addEventListener("DOMContentLoaded",function () {document.getElementById("spana").innerText="456"})function changetextvalue () {document.getElementById("spana").innerText="789"}</script></head><body><span id="spana">123</span><button type="button" onclick="changetextvalue()">修改為789</button></body></html>
????????現在的做法,是vue的綁定模式,給這個 DOM 元素綁定一個js變量,在script中修改js變量的值,DOM 會自動變化,頁面會自動更新渲染。
- 前端改用?MVVM(Model-View-ViewModel的簡寫)模式,簡單來說,Model:代表數據模型,View:只專注視圖UI處理,ViewModel:只處理業務和數據。它的核心是 MVVM 中的 VM,也就是 ViewModel。 ViewModel負責連接 View 和 Model,保證視圖和數據的一致性,這種輕量級的架構讓前端開發更加高效、便捷,大幅減少代碼行數,同時差量渲染性能更好。
uni-app
?使用vue的數據綁定方式解決js和 DOM 界面交互的問題。
<template><view><text>{{textvalue}}</text><!-- 這里演示了組件值的綁定 --><button :type="buttontype" @click="changetextvalue()">修改為789</button><!-- 這里演示了屬性和事件的綁定 --></view></template><script>export default {data() {return {textvalue:"123",buttontype:"primary"};},onLoad() {this.textvalue="456"//這里修改textvalue的值,其實123都來不及顯示就變成了456},methods: {changetextvalue() {this.textvalue="789"//這里修改textvalue的值,頁面自動刷新為789}}}</script>
- 以前在是html的tag里用一個屬性
onclick
來寫點擊事件 - 現在是使用
@click
(其實是v-on:click
的縮寫,在uni-app里基本都使用縮寫)調用methods里的方法。
3?在 uni-app 中使用vue的差異
? ?uni-app
?在發布到H5時支持所有vue的語法;發布到App和小程序時,由于平臺限制,無法實現全部vue語法,但?uni-app
?仍是對vue語法支持度最高的跨端框架。相比Web平臺, Vue.js 在?uni-app
?中使用差異主要集中在兩個方面:
- 新增:
uni-app
?除了支持 Vue 實例的組件生命周期,還擁有應用生命周期及頁面的生命周期。 - 受限:相比 Web 平臺,在小程序和 App 端部分功能支持不完善。
? ?uni-app
?項目對 vue 3.0 的支持版本情況如下:
- Web平臺:支持。
- 小程序平臺:
HBuilderX 3.3.3+
?編譯器改為?vite
,之前版本的編譯器為webpack
。 - App 平臺:
uni-app 3.2.5+
支持。HBuilderX 3.3.13
?起?nvue
編譯器升級為vite
。
注意事項
- vue3 響應式基于?
Proxy
?實現,不支持iOS9
和ie11
。 - 暫不支持新增的?
Teleport
,Suspense
?組件。 - 目前?
HBuilderX 3.2
?起已預置,之前的版本只能使用cli方式。
4?模板語法
? ?Vue.js
?使用了基于?HTML
?的模板語法,允許開發者聲明式地將?DOM
?綁定至底層組件實例的數據。 所有?Vue.js
?的模板都是合法的?HTML
,所以能被遵循規范的瀏覽器和?HTML
?解析器解析。在底層的實現上,Vue
?將模板編譯成虛擬?DOM
?渲染函數。結合響應性系統,Vue
?能夠智能地計算出最少需要重新渲染多少組件,并把?DOM
?操作次數減到最少。
4.1?插值
4.1.1?文本
????????數據綁定最常見的形式就是文本插值:
<template><view><view>Message: {{ msg }}</view></view></template><script>export default {data() {return {msg: 'Hello Vue!'}}}</script>
? ? ? ? {{ }}的內容將會被替代為對應數據對象上msg的值。無論何時,綁定的數據對象上msg發生了改變,插值處的內容都會更新。
4.1.2?使用 JavaScript 表達式
????????迄今為止,在我們的模板中,我們一直都只綁定簡單的?property
?鍵值。但實際上,對于所有的數據綁定,Vue.js 都提供了完全的?JavaScript
?表達式支持。
<template><view><view>{{ number + 1 }}</view><view>{{ ok ? 'YES' : 'NO' }}</view><!-- 把一個字符串分割成字符串數組,顛倒其元素的順序,把數組中的所有元素放入一個字符串 --><view>{{ message.split('').reverse().join('') }}</view></view></template><script>export default {data() {return {number:1,ok:true,message: 'Hello Vue!'}}}</script>
<template><view><view v-for="(item,index) in 10"><!-- 通過%運算符求余數,實現隔行換色的效果 --><view :class="'list-' + index%2">{{index%2}}</view></view></view></template><script>export default {data() {return { }}}</script><style>.list-0{background-color: #aaaaff;}.list-1{background-color: #ffaa7f;}</style>
????????這些表達式會在所屬 Vue 實例的數據作用域下作為?JavaScript
?被解析。有個限制就是,每個綁定都只能包含單個表達式,所以下面的例子都不會生效。
<template><view><!-- 這是語句,不是表達式 --><view>{{ var a = 1 }}</view><!-- 流控制也不會生效,請使用三元表達式 --><view>{{ if (ok) { return message } }}</view></view></template><script>export default {data() {return {ok:true,message: 'Hello Vue!'}}}</script>
????????模板表達式都被放在沙盒中,只能訪問全局變量的一個白名單:
Infinity
undefined
NaN
isFinite
isNaN
parseFloat
parseInt
decodeURI
decodeURIComponent
encodeURI
encodeURIComponent
Math
Number
Date
Array
Object
Boolean
String
RegExp
Map
Set
JSON
Intl
BigInt
????????你不應該在模板表達式中試圖訪問用戶定義的全局變量。
4.2?指令
????????指令是帶有 v- 前綴的特殊屬性。
- 指令屬性的值預期是單個?
JavaScript
?表達式?(v-for
?和?v-on
?是例外情況,稍后我們再討論)。 - 指令的作用是,當表達式的值改變時,將其產生的連帶影響,響應式地作用于?
DOM
?。 - 一些指令能夠接收一個“參數”,在指令名稱之后以冒號( : )表示。
4.2.1?v-bind
????????動態地綁定一個或多個屬性,v-bind
縮寫為‘ : ’,可以用于響應式地更新?HTML attribute
:
<!-- 完整語法 --><image v-bind:src="imgUrl"></image><!-- 縮寫 --><image :src="imgUrl"></image><button v-bind:disabled="isButtonDisabled">Button</button>
????????在這里?src
?是參數,告知?v-bind
?指令將該元素的?src
?attribute 與表達式?imgUrl
?的值綁定。如果?isButtonDisabled
?的值是?null
?或?undefined
,則?disabled
?attribute 甚至不會被包含在渲染出來的?button
?元素中。
4.2.2?v-on
????????v-on 指令,它用于監聽?DOM
?事件。v-on縮寫為‘ @ ’,下文簡稱為?@事件
<!-- 完整語法 --><view v-on:click="doSomething">點擊</view><!-- 縮寫 --><view @click="doSomething">點擊</view>
4.2.3?v-once
????????只渲染元素和組件一次。隨后的重新渲染,元素/組件及其所有的子節點將被視為靜態內容并跳過。和前端框架中的理解不同,客戶端里要實現復用的邏輯,會標記模板節點的狀態,添加了?v-once
?能保證節點只渲染一次,但是并不一定能優化渲染性能,反而可能會拖慢客戶端復用節點時的比對效率。h5、微信小程序均不支持。
<!-- 單個元素 --><view v-once>This will never change: {{msg}}</view><!-- 有子元素 --><view v-once><text>comment</text><text>{{msg}}</text></view>
4.2.4?v-html
????????更新元素的?innerHTML。
- 注意:內容按普通 HTML 插入 - 不會作為 Vue 模板進行編譯。
- 如果試圖使用 v-html 組合模板,可以重新考慮是否通過使用組件來替代。
- App端和H5端支持?
v-html
?,微信小程序會被轉為?rich-text
,其他端不支持?v-html
?。
<template><view><view v-html="rawHtml"></view></view></template><script>export default {data() {return {rawHtml: '<div style="text-align:center;background-color: #007AFF;"><div >我是內容</div><img src="https://qiniu-web-assets.dcloud.net.cn/unidoc/zh/uni@2x.png"/></div>'}}}</script>
5?Data 選項
? ?data
?選項已標準化為只接受返回一個初始數據對象的函數(注意函數內返回的數據對象不要直接引用函數外的對象);否則頁面關閉時,數據不會自動銷毀,再次打開該頁面時,會顯示上次數據。
//正確用法,使用函數返回對象data() {return {title: 'Hello'}}//錯誤寫法,會導致再次打開頁面時,顯示上次數據data: {title: 'Hello'}//錯誤寫法,同樣會導致多個組件實例對象數據相互影響const obj = {title: 'Hello'}data() {return {obj}}
6?Class 與 Style 綁定
6.1?綁定 HTML Class
????????我們可以傳給?:class
?(v-bind:class
?的簡寫) 一個對象,實現動態地切換?class
。也可以在對象中傳入更多字段來動態切換多個?class
。此外,v-bind:class
?指令也可以與普通的?class
?共存。
<template><view><view class="static" :class="{ active: isActive}">hello uni-app</view><view class="static" :class="{ active: isActive, 'text-danger': hasError }">hello uni-app</view></view></template><script>export default {data() {return {isActive: true,hasError: false}}}</script><style>.static{color: #2C405A;}.active{background-color: #007AFF;font-size:30px;}.text-danger{color: #DD524D;}</style>
????????渲染結果為
<view class="static active"></view>
????????當?isActive
?或者?hasError
?變化時,class 列表將相應地更新。例如,如果?hasError
?的值為?true
?,class 列表將變為?static active text-danger
。
????????可以把一個數組傳給?v-bind:class
,以應用一個?class
?列表。
<template><view><view :class="[activeClass,errorClass]">hello uni-app</view></view></template><script>export default {data() {return {activeClass: 'active',errorClass: 'text-danger'}}}</script><style>.active{background-color: #007AFF;}.text-danger{font-size:60rpx;color:#DD524D;}</style>
????????渲染的結果為:?
<view class="active text-danger"></view>
????????如果你想根據條件切換列表中的 class,可以使用三元表達式:
<view :class="[isActive ? activeClass : '', errorClass]"></view>
????????這樣寫將始終添加?errorClass
,但是只有在?isActive
?為?truthy
?時才添加?activeClass
。在?JavaScript
?中,truthy
(真值)指的是在布爾值上下文中,轉換后的值為真的值。所有值都是真值,除非它們被定義為 假值(即除?false
、0、""、null
、undefined
?和?NaN
?以外皆為真值)。
????????不過,當有多個條件 class 時這樣寫有些繁瑣。所以在數組語法中也可以使用對象語法:
<template><view><view class="static" :class="[{ active: isActive }, errorClass]">hello uni-app</view></view></template><script>export default {data() {return {isActive: true,errorClass: 'text-danger'}}}</script><style>.static{font-size:30rpx;}.active{background-color: #007AFF;}.text-danger{font-size:60rpx;color:#DD524D;}</style>
????????注意:以:style=""這樣的方式設置px像素值,其值為實際像素,不會被編譯器轉換。此外還可以用計算屬性?computed
?方法生成?class
?或者?style
?字符串,插入到頁面中,舉例說明:
<template><view><view class="container" :class="computedClassStr">hello uni-app</view><view class="container" :class="{active: isActive}">hello uni-app</view></view></template><script>export default {data() {return {isActive: true}},computed: {computedClassStr() {return this.isActive ? 'active' : ''}}}</script><style>.active {background-color: #007AFF;font-size:30px;}</style>
6.2 綁定內聯樣式
????????:style 的對象語法十分直觀——看著非常像 CSS,但其實是一個?JavaScript
?對象。CSS property 名可以用駝峰式 (camelCase
) 或短橫線分隔 (kebab-case
,記得用引號括起來) 來命名:
<template><view :style="{ color: activeColor, fontSize: fontSize + 'px' }">hello uni-app</view></template><script>export default {data() {return {activeColor: 'red',fontSize: 30}}}</script>
????????直接綁定到一個樣式對象通常更好,這會讓模板更清晰:
<template><view :style="styleObject">hello uni-app</view></template><script>export default {data() {return {styleObject: {color: 'red',fontSize: '13px'}}}}</script>
????????同樣的,對象語法常常結合返回對象的計算屬性使用。:style
?的數組語法可以將多個樣式對象應用到同一個元素上:
<template><view><view :style="[baseStyles, overridingStyles]">hello uni-app</view></view></template><script>export default {data() {return {baseStyles: {color: 'green',fontSize: '30px'},overridingStyles: {'font-weight': 'bold'}}}}</script>
????????在?:style
?中使用需要 (瀏覽器引擎前綴)?vendor prefixesa 的?CSS property
?時,如?transform
,Vue
?將自動偵測并添加相應的前綴。可以為?style
?綁定中的?property
?提供一個包含多個值的數組,常用于提供多個帶前綴的值,例如:
<view :style="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }"></view>
????????這樣寫只會渲染數組中最后一個被瀏覽器支持的值。在本例中,如果瀏覽器支持不帶瀏覽器前綴的?flexbox
,那么就只會渲染?display: flex
。
7?條件渲染
7.1?v-if和v-else
? ?v-if
?指令用于條件性地渲染一塊內容。這塊內容只會在指令的表達式返回?truthy
?值的時候被渲染。 使用?v-else
?指令來表示?v-if
?的“else 塊”。?v-else
?元素必須緊跟在帶?v-if
?或者?v-else-if
?的元素的后面,否則它將不會被識別。
<template><view><view v-if="seen">現在你看到我了</view><view v-else>你看不到我了</view></view></template><script>export default {data() {return {seen: true}}}</script>
? ?v-else-if
,顧名思義,充當 v-if 的“else-if 塊”,可以連續使用:
<template><view><view v-if="type === 'A'">A</view><view v-else-if="type === 'B'">B</view><view v-else-if="type === 'C'">C</view><view v-else>Not A/B/C</view></view></template><script>export default {data() {return {type:'B'}}}</script>
????????類似于?v-else
?,v-else-if
?也必須緊跟在帶?v-if
?或者?v-else-if
?的元素之后。
7.2?條件渲染分組
????????因為?v-if
?是一個指令,所以必須將它添加到一個元素上。但是如果想切換多個元素呢?此時可以把一個?template
?元素當做不可見的包裹元素,并在上面使用?v-if
。最終的渲染結果將不包含?template
?元素。
<template v-if="seen"><view>標題</view><view>內容:現在你看到我了</view></template>
7.3?v-show
????????另一個用于根據條件展示元素的選項是?v-show
?指令。用法大致一樣:
<view v-show="ok">Hello!</view>
????????不同的是帶有?v-show
?的元素始終會被渲染并保留在?DOM
?中。v-show
?只是簡單地切換元素的?CSS
?屬性的?display
?。注意,v-show 不支持 template 元素,也不支持 v-else。nvue 頁面不支持 v-show。
7.4?v-if 和 v-show 區別
v-if
?是“真正”的條件渲染,因為它會確保在切換過程中條件塊內的事件監聽器和子組件適當地被銷毀和重建。v-if
?也是惰性的:如果在初始渲染時條件為假,則什么也不做——直到條件第一次變為真時,才會開始渲染條件塊。- 相比之下,
v-show
?就簡單得多——不管初始條件是什么,元素總是會被渲染,并且只是簡單地基于 CSS 進行切換,來控制元素的顯示和隱藏。
根據應用場景選擇
v-if
?有更高的切換開銷,如果在運行時條件很少改變,則使用 v-if 較好。v-show
?有更高的初始渲染開銷。如果需要非常頻繁地切換,則使用 v-show 較好。
注意
- 不推薦同時使用?
v-if
?和?v-for
。 - 當?
v-if
?與?v-for
?一起使用時,v-if
?具有比?v-for
?更高的優先級。