Vue简介
渐进式JavaScript框架,易学易用,性能出色,使用场景丰富的web前端框架
Vue(发音为/vju:/,类似view)是一款用于构建用户界面的JavaScript框架。基于标椎HTML、CSS和JavaScript构建,提供了一套声明式的、组件化的编程模型,帮助你高效地开发用户界面。无论是简单还是复杂的界面,Vue都能胜任
渐进式框架 Vue 是一个框架,也是一个生态。其功能覆盖了大部分前端开发常见的需求。但 Web 世界是十分多样化的。不同的开发者在 Web 上构建的东西可能在形式和规模上会有很大的不同。考虑到这一点,Vue 的设计非常注重灵活性和“可以被逐步集成 ”这个特点。根据你的需求场景,你可以用不同的方式使用 Vue:
无需构建步骤,渐进式增强静态的HTML
在任何页面中作为Web Components嵌入
单页应用(SPA)(整个项目在一个页面)
全栈/服务端渲染(SSR)
JamStack/静态站点生成(SSG)
开发桌面端、移动端、WebGL,甚至是命令行终端中的界面
API风格 选项式API
Options API
使用选项式API,我们可以用包含多个选项的对象来描述组件的逻辑,例如data、methods、mounted。选项所定义的属性都会暴露在函数内部this上,会指向当前的组件实例
例如实现一个按钮累加的功能
组合式API
Composition API
通过组合式API,可以使用导入的API函数来描述组件逻辑
该选哪一个? 两种 API风格都能够覆盖大部分的应用场景。它们只是同一个底层系统所提供的两套不同的接口。实际上,选项式 API是在组合式 API 的基础上实现的!关于 Vue 的基础概念和知识在它们之间都是通用的。
生产项目中
当你不需要使用构建工具,或者打算主要在低复杂度的场景中使用 Vue,例如渐进增强的应用场景,推荐 采用选项式 API
当你打算用 Vue 构建完整的单页应用,推荐采用组合式 API+ 单文件组件
目录结构
node_moudles — vue项目的运行依赖文件夹
public — 资源文件夹(浏览器图标..)
src — 源码文件夹
.gitignore — git忽略文件
index.html — 入口html文件
package.json — 信息描述文件
README.md — 注释文件
vite.config.js — Vue配置文件
vite构建
vite是新一代前端构建工具
轻量快速的热重载(HMR),能实现极速的服务启动
对TypeScript、JSX、CSS 等支持开箱即用
真正的按需编译,不再等待整个应用编译完成
具体操作 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 # npm create vue@latest # # √ Project name: vue3_test # √ Add TypeScript? Yes # √ Add JSX Support? No # √ Add Vue Router for Single Page Application development? No # √ Add Pinia for state management? No # √ Add Vitest for Unit Testing? No # √ Add an End-to-End Testing Solution? » No # √ Add ESLint for code quality? Yes # √ Add Prettier for code formatting? No
核心语法 setup
setup是Vue3中一个新的配置项,值是一个函数,它是 Composition API “表演的舞台 ” ,组件中所用到的:数据、方法、计算属性、监视…等等,均配置在setup中
特点
setup函数返回的对象中的内容,可直接在模板中使用。
setup中访问this是undefined。
setup函数会在beforeCreate之前调用,它是“领先”所有钩子执行的
代码示例:
注意这里由于安装了eslint插件,校验代码格式和规范,不按照它的来会直接报错。我在此之后关闭了校验。(这里是因为name用的person,不是eslint插件规定的多个单词驼峰命名—坑)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 <template> <div class="person"> <h2>姓名: {{name}}</h2> <h2>年龄: {{age}}</h2> <button @click="changeName">修改名字</button> <button @click="changeAge">年龄+1</button> <button @click="showTel">点我查看联系方式</button> </div> </template> <script> export default { name: 'myPerson', setup() { let name = '张三' let age = 18 let tel = '13888888888' function changeName() { name = 'zhang-san' console.log(name) } function changeAge() { age += 1 console.log(age) } function showTel() { alert(tel) } return {name, age, tel, changeName, changeAge, showTel} } } </script>
返回值
setup与OptionsAPI的关系
Vue2 的配置(data、methos…)中可以访问到 setup中的属性、方法。
但在setup中不能访问到 Vue2的配置(data、methos…)。
如果与Vue2冲突,则setup优先。
setup语法糖 setup函数有一个语法糖,这个语法糖,可以让我们把setup独立出去,代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 <template> <div class="person"> <h2>姓名:{{name}}</h2> <h2>年龄:{{age}}</h2> <button @click="changeName">修改名字</button> <button @click="changAge">年龄+1</button> <button @click="showTel">查看联系方式</button> </div> </template> <script> export default { name:'Person' } </script> <script setup> console.log(this) let name = '张三' let age = 18 let tel = '13888888888' function changeName() { name='李四' } function changAge(){ console.log(age) age += 1 } function showTel(){ alert(tel) } </script>
扩展: 上述代码还需要编写一个不写setup的script标签,去指定组件名字,比较麻烦,我们可以借助vite中的插件简化
npm i vite-plugin-vue-setup-extend -D
vite.config.ts 中
1 2 3 4 5 6 import { defineConfig } from 'vite' import VueSetupExtend from 'vite-plugin-vue-setup-extend' export default defineConfig ({ plugins : [ VueSetupExtend () ] })
设置为script setup lang=”ts” name=”Person”
ref创建:基本类型
ref基本类型的响应式数据
作用 :定义响应式变量
语法 :let xxx = ref(初始值)
返回值 :一个RefImpl的实例对象,简称ref对像或ref,ref对象的value属性是响应式的
注意点:
ts中操作数据需要:xxx.value,但模板中不需要.value,直接使用即可
何时需要.value?模板中不需要;包裹在响应式对象里面的ref不需要;未包裹的ref需要
对于let name = ref(‘张三’)来说,name不是响应式的,name.value是响应式的
注意到此,作者JYH发现vite的好处,所以说重新用vite构建项目 (目录中可以找到vite构建的步骤及好处)
ref创建代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 <template> <div class="person"> <h2>姓名:{{name}}</h2> <h2>年龄:{{age}}</h2> <button @click="changName">修改名字</button> <button @click="changAge">年龄+1</button> <button @click="showTel">点我查看联系方式</button> </div> </template> <!-- 下面的写法是setup语法糖 --> <script setup lang="ts" name="Person"> import {ref} from 'vue' let name = ref('张三') let age = ref(18) //tel就是一个普通的字符串不是响应式的 let tel = '13888888888' // 方法 function changName(){ name.value = '李四' } function changAge(){ console.log(age) age.value += 1 //注意:此时这么修改age页面是不变化的 } function showTel(){ alert(tel) } </script>
reactive创建:对象类型
对象类型的响应式数据
作用 :定义一个响应式对象(基本类型不要用它,要用ref,否则报错)
语法 :let 响应式对象=reactive(源对象)
返回值 :一个Proxy的实例对象,简称:响应式对象
注意点 :reactive定义的响应式数据是“深层次的”
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 <template> <div class="person"> <h2>汽车信息:一台{{ car.brand }}汽车,价值{{ car.price }}万</h2> <h2>游戏列表:</h2> <ul> <li v-for="g in games" :key="g.id">{{ g.name }}</li> </ul> <h2>测试:{{ obj.a.b.c.d }}</h2> <button @click="changeCarPrice">修改汽车价格</button> <button @click="changeFirstGame">修改第一游戏</button> <button @click="test">测试</button> </div> </template> <script lang="ts" setup name="Person"> import { reactive } from 'vue'; let car = reactive({brand:'奔驰', price:100}) let games = reactive([ {id:'ahsgdyfa01', name: '英雄联盟'}, {id:'ahsgdyfa02', name: '王者荣耀'}, {id:'ahsgdyfa03', name: '原神!'}, ]) let obj = reactive({ a:{ b:{ c:{ d:666 } } } }) function changeCarPrice() { car.price += 10; } function changeFirstGame() { games[0].name = '流星蝴蝶剑' } function test() { obj.a.b.c.d = 999 } </script>
ref创建:对象类型
其实ref接收的数据可以是:基本类型 、对象类型 。
若ref接收的是对象类型,内部其实也是调用了reactive函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 <template> <div class="person"> <h2>汽车信息:一台{{ car.brand }}汽车,价值{{ car.price }}万</h2> <h2>游戏列表:</h2> <ul> <li v-for="g in games" :key="g.id">{{ g.name }}</li> </ul> <h2>测试:{{obj.a.b.c.d}}</h2> <button @click="changeCarPrice">修改汽车价格</button> <button @click="changeFirstGame">修改第一游戏</button> <button @click="test">测试</button> </div> </template> <script lang="ts" setup name="Person"> import { ref } from 'vue' // 数据 let car = ref({ brand: '奔驰', price: 100 }) let games = ref([ { id: 'ahsgdyfa01', name: '英雄联盟' }, { id: 'ahsgdyfa02', name: '王者荣耀' }, { id: 'ahsgdyfa03', name: '原神' } ]) let obj = ref({ a:{ b:{ c:{ d:666 } } } }) console.log(car) function changeCarPrice() { car.value.price += 10 } function changeFirstGame() { games.value[0].name = '流星蝴蝶剑' } function test(){ obj.value.a.b.c.d = 999 } </script>
ref与reactive
宏观角度
ref定义:基本类型数据、对象类型数据
reactive用来定义:对象类型数据
区别
ref创建的变量必须使用.value(可以使用volar插件自动添加.value)
reactive重新分配一个新对象,会失去 响应式
使用原则
若需要一个基本类型的响应式数据,必须使用ref
若需要一个响应式对象,层级不深,ref、reactive都可以
若需要一个响应式对象,且层级较深,推荐使用reactive
toRefs与toRef
computed
根据已有数据计算出新的数据
watch
作用:监视数据的变化
特点:Vue3中的watch只能监视一下四种数据
ref定义的数据
reactive定义的数据
函数返回一个值(getter函数)
一个包含上述内容的数组
vue3中使用watch会有以下五种情况
情况一
监视ref定义的基本类型数据:直接写数据名即可,监视的是其value值的改变
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 <template> <div class="person"> <h1>情况一:监视【ref】定义的【基本类型】数据</h1> <h2>当前求和为:{{sum}}</h2> <button @click="changeSum">点我sum+1</button> </div> </template> <script lang="ts" setup name="Person"> import {ref,watch} from 'vue' // 数据 let sum = ref(0) // 方法 function changeSum(){ sum.value += 1 } // 监视,情况一:监视【ref】定义的【基本类型】数据 const stopWatch = watch(sum,(newValue,oldValue)=>{ console.log('sum变化了',newValue,oldValue) if(newValue >= 10){ stopWatch() } }) </script>
情况二
监视ref定义的【对象类型】数据:直接写数据名,监视的是对象的【地址值】,若想监视对象内部的数据,要手动开启深度监视。
若修改的是ref定义的对象中的属性,newValue 和 oldValue 都是新值,因为它们是同一个对象。
若修改整个ref定义的对象,newValue 是新值, oldValue 是旧值,因为不是同一个对象了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 <template> <div class="person"> <h1>情况二:监视【ref】定义的【对象类型】数据</h1> <h2>姓名:{{ person.name }}</h2> <h2>年龄:{{ person.age }}</h2> <button @click="changeName">修改名字</button> <button @click="changeAge">修改年龄</button> <button @click="changePerson">修改整个人</button> </div> </template> <script lang="ts" setup name="Person"> import {ref,watch} from 'vue' // 数据 let person = ref({ name:'张三', age:18 }) // 方法 function changeName(){ person.value.name += '~' } function changeAge(){ person.value.age += 1 } function changePerson(){ person.value = {name:'李四',age:90} } /* 监视,情况一:监视【ref】定义的【对象类型】数据,监视的是对象的地址值,若想监视对象内部属性的变化,需要手动开启深度监视 watch的第一个参数是:被监视的数据 watch的第二个参数是:监视的回调 watch的第三个参数是:配置对象(deep、immediate等等.....) */ watch(person,(newValue,oldValue)=>{ console.log('person变化了',newValue,oldValue) },{deep:true,immediate:true}) </script>
情况三
监视reactive定义的【对象类型】数据,且默认开启了深度监视,且深层监视无法关闭。
无法监视地址值,因为对象地址值没有改变,本质上assign在原对象上进行的是赋值。
newValue和oldValue值相同,都是新值,还是因为对象地址值没有改变,本质上assign在原对象上进行的是赋值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 <template> <div class="person"> <h1>情况三:监视【reactive】定义的【对象类型】数据</h1> <h2>姓名:{{ person.name }}</h2> <h2>年龄:{{ person.age }}</h2> <button @click="changeName">修改名字</button> <button @click="changeAge">修改年龄</button> <button @click="changePerson">修改整个人</button> <hr> <h2>测试:{{obj.a.b.c}}</h2> <button @click="test">修改obj.a.b.c</button> </div> </template> <script lang="ts" setup name="Person"> import {reactive,watch} from 'vue' // 数据 let person = reactive({ name:'张三', age:18 }) let obj = reactive({ a:{ b:{ c:666 } } }) // 方法 function changeName(){ person.name += '~' } function changeAge(){ person.age += 1 } function changePerson(){ // person = { name: '李四', age: 80 }//直接修改,不行 // person = reactive({ name: '李四', age: 80 })//reactive包裹修改,也不行 Object.assign(person,{name:'李四',age:80})//本质上替换了原person的每个属性值 } function test(){ obj.a.b.c = 888 } // 监视,情况三:监视【reactive】定义的【对象类型】数据,且默认是开启深度监视的 watch(person,(newValue,oldValue)=>{ console.log('person变化了',newValue,oldValue) }) watch(obj,(newValue,oldValue)=>{ console.log('Obj变化了',newValue,oldValue) }) </script>
情况四
监视ref或reactive定义的【对象类型】数据中的某个属性 ,注意点如下:
若该属性值不是 【对象类型】即【基本类型】,需要写成函数形式,此时oldValue是旧值,newValue是新值。
若该属性值是依然 是【对象类型】,可直接编,也可写成函数,建议写成函数。
直接写:可以监视到对象内部属性a,b…的变化,但是监视不到整体的变化。整体改变时,对象地址值变化了,所以监视不到了。
写函数(不开启深度监视):监视不到对象内部属性a,b…的变化,但是可以监视到整体的变化,函数返回值监视的是对象的地址值,改变整体是产生一个新对象,所以能监视到,并且新值是新值,旧值是旧值。(不过对象内部属性a,b…的新旧值都是新值)
写函数(开启深度监视)推荐:既能监视到对象内部属性a,b…的变化,也可以监视到整体的变化,函数返回值监视的是对象的地址值,改变整体是产生一个新对象,所以能监视到,并且新值是新值,旧值是旧值。(不过对象内部属性a,b…的新旧值都是新值)
情况五
监视上述的多个数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 <template> <div> <h1>情况五:监视上述的多个数据</h1> <h2>姓名:{{ person.name }}</h2> <h2>年龄:{{ person.age }}</h2> <h2>汽车:{{ person.car.c1 }}、{{ person.car.c2 }}</h2> <button @click="changeName">修改名字</button> <button @click="changeAge">修改年龄</button> <button @click="changeC1">修改第一台车</button> <button @click="changeC2">修改第二台车</button> <button @click="changeCar">修改整个车</button> </div> </template> <script lang="ts" setup name="Person"> import { reactive,watch } from "vue"; let person = reactive({ name:'张三', age:18, car:{ c1:'奔驰', c2:'宝马' } }) function changeName() { person.name += '~' } function changeAge() { person.age += 1 } function changeC1(){ person.car.c1 = '奥迪' } function changeC2(){ person.car.c2 = '大众' } function changeCar(){ person.car = {c1:'雅迪',c2:'爱玛'} } watch([() => person.name, person.car], (newValue, oldValue) =>{ console.log('person.car变化了', newValue, oldValue) }, {deep:true}) </script>
watchEffect
立即运行一个函数,同时响应式地追踪其依赖,并在依赖更改时重新执行该函数
watch对比watchEffect
都能监听响应式数据的变化,不同的是监听数据变化的方式不同
watch:要明确指出监视的数据
watchEffect:不用明确指出监视的数据(函数中用到哪些属性,就监视哪些属性)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 <template> <div class="person"> <h1>需求:水温达到50℃,或水位达到20cm,则联系服务器</h1> <h2 id="demo">水温:{{temp}}</h2> <h2>水位:{{height}}</h2> <button @click="changePrice">水温+1</button> <button @click="changeSum">水位+10</button> </div> </template> <script lang="ts" setup name="Person"> import {ref,watch,watchEffect} from 'vue' // 数据 let temp = ref(0) let height = ref(0) // 方法 function changePrice(){ temp.value += 10 } function changeSum(){ height.value += 1 } // 用watch实现,需要明确的指出要监视:temp、height watch([temp,height],(value)=>{ // 从value中获取最新的temp值、height值 const [newTemp,newHeight] = value // 室温达到50℃,或水位达到20cm,立刻联系服务器 if(newTemp >= 50 || newHeight >= 20){ console.log('联系服务器') } }) // 用watchEffect实现,不用 const stopWtach = watchEffect(()=>{ // 室温达到50℃,或水位达到20cm,立刻联系服务器 if(temp.value >= 50 || height.value >= 20){ console.log(document.getElementById('demo')?.innerText) console.log('联系服务器') } // 水温达到100,或水位达到50,取消监视 if(temp.value === 100 || height.value === 50){ console.log('清理了') stopWtach() } }) </script>
标签的ref属性
用于注册模板的引用
用在普通的DOM标签上,获取的是DOM节点
用在组件标签上,获取的是组件实例对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 <template> <div class="person"> <h1 ref="title1">尚硅谷</h1> <h2 ref="title2">前端</h2> <h3 ref="title3">Vue</h3> <input type="text" ref="input"> <br><br> <button @click="showLog">点我打印内容</button> </div> </template> <script lang="ts" setup name="Person"> import {ref} from 'vue' let title1 = ref() let title2 = ref() let title3 = ref() function showLog() { //通过id获取元素 // const t1 = document.getElementById('title1') // console.log((t1 as HTMLElement).innerText) // console.log((<HTMLElement>t1).innerText) // console.log(t1?.innerText) //通过ref获取元素 console.log(title1.value) console.log(title2.value) console.log(title3.value) } </script>
未完待续…