1、學習背景
隨着前端web應用的需求不斷發展和變化,vue生態圈也緊跟開發者步伐,不斷演化。盡管vue2.0已經很完善了,很多人掌握的vue2.0,感覺實在學不動了,意料之外的是尤先生繼續更新vue到3.0版本,以補充vue2.0的不足之處。隨着vue3.0問世,vite2.5.1也油然而生,vue始終沒有放棄對項目響應速度和編譯速度的追求,vite的到來,對於前端開發者而言,簡直不要太幸福了。vue3.0不僅全面支持TypeScript語法,還對生命周期鈎子進行了優化和剔除,加上工具setup的語法糖,vue單頁面多個根元素的擴展,代碼精簡不說,還很有條理,vue3.0的出現再次提升了開發者的編碼體驗和幸福感。另外vue3整合typescript語言是前端未來發展的必然趨勢,而生為vue家族的新成員vite也是前端技術愛好者的學習目標,贏在起點,從學習新技術開始。活到老,學到老,是一個合格前端開發者的畢生信仰。
2、vite簡介
vite誕生是為了提升web項目運行性能,以更快的速度將應用頁面展示給用戶。Vite 以 原生 ESM 方式提供源碼。這實際上是讓瀏覽器接管了打包程序的部分工作:Vite 只需要在瀏覽器請求源碼時進行轉換並按需提供源碼。根據情景動態導入代碼,即只在當前屏幕上實際使用時才會被處理。
提供的驅動力:
2.1、優化緩慢的服務器啟動(冷啟動開發服務器和正式環境響應速度);
2.2、優化緩慢的項目更新(vite服務器);
3、vite創建項目
兼容性注意:
Vite 需要 Node.js 版本 >= 12.0.0。
必須安裝Volar插件,用vscode編輯器
// 安裝vite
1、npm init vite@latest
// 安裝vite同時創建vite項目
2、npm init vite@latest my-vue-app --template vue
{
"scripts": {
"dev": "vite", // 啟動開發服務器
"build": "vite build", // 為生產環境構建產物
"serve": "vite preview" // 本地預覽生產構建產物
}
}
4、版本依賴兼容和項目目錄介紹
package.json版本依賴說明, 這里是整個項目依賴版本配置,相關安裝指令后面視頻中會逐個教大家如何安裝。
注意:vuex和router都是4.0及以上版本的,否則用vue3時,找不到暴露的api
{
"name": "vite-ts-vue3-plus-demo",
"version": "0.0.0",
"scripts": {
"dev": "vite",
"build": "vue-tsc --noEmit && vite build",
"serve": "vite preview"
},
"dependencies": {
"@element-plus/icons": "0.0.11",
"dotenv": "^10.0.0",
"element-plus": "^1.1.0-beta.7",
"vue": "^3.0.5",
"vue-router": "^4.0.11",
"vuex": "^4.0.2"
},
"devDependencies": {
"@types/node": "^16.7.1",
"@vitejs/plugin-vue": "^1.3.0",
"@vue/compiler-sfc": "^3.0.5",
"node-sass": "^6.0.1",
"sass": "^1.38.1",
"sass-loader": "^12.1.0",
"typescript": "^4.3.2",
"vite": "^2.4.4",
"vue-tsc": "^0.2.2"
}
}
5、setup語法糖使用
5.1 setup前身組合式API(基礎用法)
注意:在setup()中不能用this
在 setup 中你應該避免使用 this,因為它不會找到組件實例。setup 的調用發生在 data property、computed property 或 methods 被解析之前,所以它們無法在 setup 中被獲取,這也是為了避免setup()和其他選項式API混淆。
/* 參數說明
- props 是響應式的,當傳入新的 prop 時,它將被更新
- context 是一個普通的上下文JavaScript對象,它暴露組件的三個 property(包括屬性,插槽,方法),
- 如下示例1所示
*/
// 示例1
5.2 setup后世(高級用法),推薦用法
注意:defineProps不需要引入,vue單文件內部自動暴露的API
// 高級用法
6、defineProps 和 defineEmits
注意:defineProps 和 defineEmits 都是只在 6.2 、父組件vue
7、正確使用defineExpose
使用
7.2、父組件調用子組件方法和屬性
<TestPropsEmit ref="propsEmitRef" :msg='msg' @on-change="handleChange">
7.3 在setup如何定義變量(字符串,對象,數組)
{{count}} {{user.name}}
{{item}}
<button @click="setName">點擊我增加
8、Watch和WatchEffect
1、基本使用方法:
{{originData.count}} {{originData.user.name}}
{{item}}
<button @click="incriment">點擊我count增加
2、watch與 watchEffect 比較,推薦watch監聽
watch: 頁面更新后不會立即執行,而watchEffect 它會執行;
如果要實現:watch在頁面更新之后就執行,需要增加立即執行的屬性;
watch([count,originData.user], (n, o)=> {console.log(n[0],n[1].name)}, {deep: true, immediate: true})
1、watch和watchEffect都懶執行副作用,不過watchEffect比較頻繁,在vue組件更新之前執行;
2、watch更具體地說明什么狀態應該觸發偵聽器重新運行,watchEffect就沒有這么友好;
3、watch訪問偵聽狀態變化前后的值。
9、在setup中的生命周期鈎子
因為 setup 是圍繞 beforeCreate 和 created 生命周期鈎子運行的,所以不需要顯式地定義它們。換句話說,在這些鈎子中編寫的任何代碼都應該直接在 setup 函數中編寫。
下表包含如何在 setup () 內部調用生命周期鈎子:
選項式 APIHook inside setup
beforeCreateNot needed* 不需要
createdNot needed* 不需要
beforeMountonBeforeMount 掛載之前
mountedonMounted 頁面加載完成時執行
beforeUpdateonBeforeUpdate
updatedonUpdated
beforeUnmountonBeforeUnmount
unmountedonUnmounted 頁面銷毀時執行
errorCapturedonErrorCaptured
renderTrackedonRenderTracked
renderTriggeredonRenderTriggered
activatedonActivated
deactivatedonDeactivated
10、用Ts限制define(Emits|Props)參數類型
注意:
1、在setup語法糖中引入組件不需要注冊聲明就可以直接用了
2、ts 限制組件傳參類型,默認是必須傳值的,否則控制台出現警告, 引入組件的地方會出現紅色提醒,不想必傳在綁定參數后加?即可
3、ts傳參支持多種類型校驗,一個參數可以傳字符串,數組,Boolean等
4、用ts方式限制defineEmits和defineProps參數類型
1、子組件
{{msg}}
{{title}}
<input v-model="inputValue" type="text" @blur="handleUpdate($event)">
2、父組件
11、遞歸組件
一個單文件組件可以通過它的文件名被其自己所引用。例如:名為 FooBar.vue 的組件可以在其模板中用
請注意這種方式相比於 import 導入的組件優先級更低。如果有命名的 import 導入和組件的推斷名沖突了,可以使用 import 別名導入:
import { FooBar as FooBarChild } from './components'
注意:這里有小問題,當單文件引入單文件時會出現內存溢出現象
12、component動態組件
由於組件被引用為變量而不是作為字符串鍵來注冊的,在
// 為 store state 聲明類型
export interface State {
username: string,
count: number
}
// 定義 injection key
export const key: InjectionKey<Store
// 導出store模塊
export const store = createStore
state: {
username: "測試store",
count: 0
},
getters:{
getName: state =>{
return state.username
}
},
mutations: {
// 重置名稱
SET_NAME(state, params:string) {
state.username = params
}
},
actions:{}
})
// 定義自己的 useStore 組合式函數
export function useStore () {
return baseUseStore(key)
}
14.3 在根目錄下新建vuex-d.ts文件,內容如下所示:
// 一個聲明文件來聲明 Vue 的自定義類型 ComponentCustomProperties
import { ComponentCustomProperties } from 'vue';
import { Store } from 'vuex';
declare module '@vue/runtime-core' {
// 聲明自己的 store state
interface State {
count: number,
username: string
}
// 為 this.$store 提供類型聲明
interface ComponentCustomProperties {
$store: Store
}
}
14.4 在main.ts中注入store模塊
import { createApp } from 'vue';
import App from './App.vue';
// 導入store模塊, 傳入 injection key
import { store, key } from '@/store';
const app = createApp(App)
app.use(store, key)
app.mount('#app')
14.5 引用測試vuex配置是否正確
<el-button @click="changeName" size="small">點擊修改名稱
15、router配置以及使用詳解
15.1 安裝
創建項目時我們已經引入了router4.0版本,接下來我們就開始配置router4.0並測試。
// 注意:安裝router必須是4.0及以上
npm install vue-router@4
15.2 頁面准備
目錄結構
頁面具體內容:
1、layout/index.vue
2、layout/header/index.vue
<h2 @click="handleClick(1)">首頁
<h2 @click="handleClick(0)">關於
3、pages/home/index.vue
home
4、pages/about/index.vue
about
15.3 在src目錄下創建router文件夾,然后創建index.ts文件,內容如下所示:
import { createRouter, createWebHashHistory } from "vue-router";
import LayOut from "../components/layout/index.vue";
const routes = [
{
path: '/',
component: LayOut,
redirect: '/home',
children:[
{
path: '/home',
name: 'home',
component: ()=> import("../pages/home/index.vue"),
meta:{
title: '首頁',
icon: ''
}
},
{
path: '/about',
name: 'about',
component: ()=> import("../pages/about/index.vue"),
meta:{
title: '關於',
icon: ''
}
}
]
}
]
const router = createRouter({
history: createWebHashHistory(),
routes
})
export default router
15.4 在main.ts中注入router模塊, 重新啟動項目,訪問路由,看是否正確
import { createApp } from 'vue'
import App from './App.vue'
import { store, key } from './store';
import router from './router';
const app = createApp(App);
app.use(store, key);
app.use(router);
app.mount('#app');
15.5 使用路由
<h2 @click="handleClick(1)">首頁
<h2 @click="handleClick(0)">關於
16、引入element-plus以及注意事項
16.1 安裝
npm install element-plus --save
or
yarn add element-plus
安裝icon圖標依賴庫
npm install @element-plus/icons
or
yarn add @element-plus/icons
16.2 在main.ts 文件中引入配置
import { createApp } from 'vue'
import App from './App.vue'
import { store, key } from './store';
// 注入路由
import router from './router';
// 引入ui組件
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
const app = createApp(App);
app.use(store, key);
app.use(router);
app.use(ElementPlus);
app.mount('#app');
16.3 在vue文件中引用ui組件
1、單個圖標引用
2、全局注冊圖標使用
import { createApp } from 'vue'
import App from './App.vue'
import { Edit,Search } from '@element-plus/icons'
const app = createApp(App);
app.component("edit", Edit)
app.component("search", Search)
app.mount('#app');
2、1 使用圖標
home頁面
17、配置vite.config.ts
這里主要配置vite服務器的打包保存地址,打包分解,端口號,是否自動打開瀏覽器,遠程請求地址代理目標,目錄別名,全局樣式配置等。
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import { loadEnv } from 'vite';
// nodejs寫法,獲取項目目錄
import path from 'path';
// https://vitejs.dev/config/
export default({ command, mode }) => {
return defineConfig({
plugins: [vue()],
server:{
host: '127.0.0.1',
port: Number(loadEnv(mode, process.cwd()).VITE_APP_PORT),
strictPort: true, // 端口被占用直接退出
https: false,
open: true,// 在開發服務器啟動時自動在瀏覽器中打開應用程序
proxy: {
// 字符串簡寫寫法
// '/foo': '',
// 選項寫法
'/api': {
target: loadEnv(mode, process.cwd()).VITE_APP_BASE_URL,
changeOrigin: true,
rewrite: (path) => path.replace(/^/api/, '')
},
// 正則表達式寫法
// '^/fallback/.': {
// target: 'http://jsonplaceholder.typicode.com',
// changeOrigin: true,
// rewrite: (path) => path.replace(/^/fallback/, '')
// },
},
hmr:{
overlay: true // 屏蔽服務器報錯
}
},
resolve:{
alias:{
'@': path.resolve(__dirname,'./src')
}
},
css:{
// css預處理器
preprocessorOptions: {
// 引入 var.scss 這樣就可以在全局中使用 var.scss中預定義的變量了
// 給導入的路徑最后加上 ;
scss: {
additionalData: '@import "@/assets/styles/global.scss";'
}
}
},
build:{
chunkSizeWarningLimit: 1500, // 分塊打包,分解塊,將大塊分解成更小的塊
rollupOptions: {
output:{
manualChunks(id) {
if (id.includes('node_modules')) {
return id.toString().split('node_modules/')[1].split('/')[0].toString();
}
}
}
}
}
})
}
18、tsconfig.json 配置
這是typescript的識別配置文件,也是ts向js轉化的中間站,這里配置了許多關於ts的文件類型和策略方法。
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"moduleResolution": "node",
"strict": true,
"jsx": "preserve",
"sourceMap": true,
"resolveJsonModule": true,
"esModuleInterop": true,
"lib": ["esnext", "dom"],
"baseUrl": ".",
"paths": {
"@/":["src/"]
}
},
"include": ["src/**/.ts", "src//*.d.ts", "src//.tsx", "src/**/.vue"]
}
19、shims-vue.d.ts配置, 聲明vue文件全局模塊
這里是配置聲明,比如css,vue等文件,ts不能識別,需要配置聲明模塊類型。
declare module '*.vue' {
import { DefineComponent } from 'vue'
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
const component: DefineComponent<{}, {}, any>
export default component
}
20、安裝scss並配置全局樣式文件
// 注意要使用scss,必須安裝依賴,否則報錯
npm install node-sass sass-loader sass -D
20.1 需要在src目錄的assets靜態目錄新增一個全局global.scss文件,其他樣式文件導入到該文件即可全局使用和修改。
或者在global.scss寫一個單獨屬性測試
$color-primary: #007aff;
vue使用全局樣式變量
home頁面
20.2 在vite.config.ts 文件中配置global.scss
css:{
// css預處理器
preprocessorOptions: {
// 引入 var.scss 這樣就可以在全局中使用 var.scss中預定義的變量了
// 給導入的路徑最后加上 ;
scss: {
additionalData: '@import "@/assets/styles/global.scss";'
}
}
}
21、響應式API
響應式狀態需要明確使用響應式 APIs 來創建。和從 setup() 函數中返回值一樣,ref 值在模板中使用的時候會自動解包:
請求接口
VITE_APP_BASE_URL = "http://39.12.29.12:8080"
//env.d.ts文件內進行 環境變量智能提示配置
interface ImportMetaEnv {
VITE_APP_TITLE: string,
VITE_APP_PORT: string,
VITE_APP_BASE_URL: string
}
22.2 vite.config.ts 讀取配置文件
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import { loadEnv } from 'vite';
// nodejs寫法,獲取項目目錄
import path from 'path';
// https://vitejs.dev/config/
export default({ command, mode }) => {
// console.log(command, mode, 5555);
return defineConfig({
plugins: [vue()],
server:{
host: '127.0.0.1',
port: Number(loadEnv(mode, process.cwd()).VITE_APP_PORT),
strictPort: true, // 端口被占用直接退出
https: false,
open: true,// 在開發服務器啟動時自動在瀏覽器中打開應用程序
proxy: {
// 字符串簡寫寫法
// '/foo': '',
// 選項寫法
'/api': {
target: loadEnv(mode, process.cwd()).VITE_APP_BASE_URL,
changeOrigin: true,
rewrite: (path) => path.replace(/^/api/, '')
},
// 正則表達式寫法
// '^/fallback/.*': {
// target: 'http://jsonplaceholder.typicode.com',
// changeOrigin: true,
// rewrite: (path) => path.replace(/^/fallback/, '')
// },
// 使用 proxy 實例
// '/api': {
// target: 'http://jsonplaceholder.typicode.com',
// changeOrigin: true,
// configure: (proxy, options) => {
// // proxy 是 'http-proxy' 的實例
// },
// }
},
hmr:{
overlay: true // 屏蔽服務器報錯
}
},
resolve:{
alias:{
'@': path.resolve(__dirname,'./src')
}
},
css:{
// css預處理器
preprocessorOptions: {
// 引入 var.scss 這樣就可以在全局中使用 var.scss中預定義的變量了
// 給導入的路徑最后加上 ;
scss: {
additionalData: '@import "@/assets/styles/global.scss";'
},
sass: {
additionalData: '@import "@/assets/styles/global.scss";'
}
}
},
build:{
chunkSizeWarningLimit: 1500, // 分塊打包,分解塊,將大塊分解成更小的塊
rollupOptions: {
output:{
manualChunks(id) {
if (id.includes('node_modules')) {
return id.toString().split('node_modules/')[1].split('/')[0].toString();
}
}
}
}
}
})
}
22.3 其他文件讀取環境變量
首頁內容展示
附上學習視頻:
https://www.bilibili.com/video/BV1QP4y1p748?spm_id_from=333.999.0.0
