- Vue开发前的准备
【打开命令行】
查看版本:node -v
【开发环境搭建】
创建Vue项目:npm init vue@latest
Project name:vue-base (名字输入只能小写)
下边的信息全选NO.
安装完成后,开始运行:
cd .\vue-base\
cnpm install(安装node_modules)
npm run dev
访问地址。
打开Visual Studio Code,查看默认代码
打开App.vue字体显示的是白色,需要安装一个插件:点开左侧Extensions扩展,搜索Volar,(Vue Language Features)
- Vue项目目录结构
- .vscode ---VSCode工具的配置文件夹(可删)
- node_modules ---Vue项目的运行依赖文件夹
- public ---资源文件夹(浏览器图标)
- src ---源码文件夹(source code)
【assets资产。存储静态资源。eg.图片、音频、视频、字体;
components组件。可重用组件,可以是UI组件(eg.按钮、表单、布局等),也可以是处理应用程序逻辑的逻辑组件】
- .gitignore ---git忽略文件
- index.html ---入口HTML文件(必须,不改)
- package.json ---信息描述文件
- README.md ---注释文件
- vite.cnofig.js ---Vue配置文件(打包)
- 模板语法
【文本插值】:最基本的数据绑定形式,它使用的是“Mustache”语法(双大括号)
用创建好的项目vue-base,删除一些默认的代码:
删icons HelloWorld.vue TheWelcome.vue WelcomeItem.vue
打开App.vue: 只保留<template></template> <script></script>
【App.vue】添加
<template>
<h3>模板语法</h3>
<p>{{ msg }}</p>
</template>
<script>
export default {
data(){
return{
msg:"神奇的语法"
}
}
}
</script>
删 import './assets/main.css'
【explorer】
删assets(资源文件夹)中的base.css logo.svg main.css
【使用js表达式】
每个绑定仅支持单一表达式,也就是一段能够被求值的js代码。判断方法:是否可以合法地写在return后面
<template>
<h3>模板语法</h3>
<p>{{ msg }}</p>
<p>{{ number + 1 }}</p>
<p>{{ ok ? 'Yes' : 'No' }}</p>
<p>{{ message.split("").reverse().join("") }}</p>
</template>
<script>
export default {
data(){
return{
msg:"神奇的语法",
number:10,
ok:true,
message:"大家好"
}
}
}
</script>
(<p>{{ message.split("").reverse().join("") }}</p>是使用js编写的 ,将字符串反转。
- message.split(“”):将message字符串分割成字符数组。
eg.如果message是“hello”,那么这行代码将返回[“h”,”e”,”l”,”l”,”o”]。
- reverse():将数组反转。
eg.如果数组是[“h”,”e”,”l”,”l”,”o”],那么reverse()将返回[“o”,”l”,”l”,”e”,”h”]。
- join(””):将反转的字符数组重新组合成一个字符串。
eg.如果数组是[“o”,”l”,”l”,”e”,”h”,那么join(“”)将返回“olleh”]。
- {{ … }}:这是AngularJs的插值表达式,用于将结果插入HTML中
所以,如果message是“hello”,那么这段代码将生成一个HTML元素,内容是“olleh”。)
- 属性绑定(attributes属性)
【创建新项目】
npm init vue@latest
vue-demo
cd .\vue-demo\
cnpm install
npm run dev
访问地址。
打开Visual Studio Code,
删icons TheWelcome.vue WelcomeItem.vue
打开App.vue: 只保留:
【打开main.js】
删 import './assets/main.css'
【HelloWorld.vue】
只保留<template></template> <script></script>
双大括号不能在HTML attributes中使用。想要响应式地绑定一个attribute,应该使用v-bind指令。(v-bind=”:”)
<template>
<div v-bind:id="dynamicId" v-bind:class="dynamiclass" v-bind:title="dynamicTitle">测试</div>
</template>
<script>
export default {
data() {
return{
dynamiclass:"appclass",
dynamicId:"appid",
dynamicTitle:undefined
}
}
}
</script>
<style>
.appclass{
color: red;
font-size: 30px;
}
</style>
(<template>标签定义了组件的模板部分。有一个<div>元素 ,它绑定了三个动态属性:id,class,title。这些属性的值会根据组件的数据动态变化。这里显示“测试。
<script>标签中定义了组件的逻辑部分。这里导出了一个Vue组件,它包含三个属性:dynamiclass,dynamicId,dynamicTitle。这些属性的值会在组件被创建时初始化。
<style>标签定义了组件的样式部分。在这,appclass是一个CSS类,它设置了文本颜色为红色,字体大小为30像素。 )
v-bind指令指示Vue将元素的id attribute 与组件的dynamicId属性保持一致。如果绑定的值是null或者undefined,那么attribute将会从渲染的元素上移除
【简写】v-bind写成:
<div :id="dynamicId" :class="dynamiclass"></div>
【布尔值attribute】
布尔值attribute依据true\false值来决定attribute是否应该存在于该元素上,disabled就是最常见的例子之一
<template>
<div :id="dynamicId" :class="dynamiclass">测试</div>
<button :disabled="isButtonDisabled">Button</button>
<div v-bind="objectOfAttrs">测试</div>
</template>
<script>
export default {
data() {
return{
dynamiclass:"appclass",
dynamicId:"appid",
isButtonDisabled: false,
objectOfAttrs: {
dynamiclass:"appclass",
dynamicId:"appid"
}
}
}
}
</script>
<style>
.appclass {
color: red;
font-size: 30px;
}
</style>
- 条件渲染
在components下新建一个IfDemo.vue文件
添<template> <h3>条件渲染</h3> </template> <script></script>
要显示上边文件,打开App.vue,仿照HelloWorld引入文件
<script setup>
import HelloWorld from './components/HelloWorld.vue'
import IfDemo from './components/IfDemo.vue'
</script>
<template>
<HelloWorld/>
<IfDemo/>
</template>
【v-if】条件性地渲染一块内容。这块内容只会在指令的表达式返回真值时才会渲染。
【v-else】可用v-else为v-if添加一个“else区块”
<template>
<h3>条件渲染</h3>
<div v-if="flag">你能看见我么</div>
<div v-else>那你还是看看我吧</div>
</template>
<script>
export default {
data(){
return{
flag:true
}
}
}
</script>
(根据值的不同true\false显示不同的模块)
【v-else-if】它提供的是相应于v-if的“else if区块”。它可以连续多次重复使用
<template>
<h3>条件渲染</h3>
<div v-if="flag">你能看见我么</div>
<div v-else>那你还是看看我吧</div>
<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>
</template>
<script>
export default {
data(){
return{
flag:true,
type: "D"
}
}
}
</script>
【v-show】另一个可用来按条件显示一个元素的指令(只能判定自己本身能显示或者不能显示),其基本用法一样
补:v-if和v-show
v-if:
1.是“真实的”按条件渲染,因为它确保了在切换时,条件区块内的事件监听器和子 组件都会被销毁与重建。
2.是惰性的:如果在初次渲染是条件值为false,则不会做任何事。条件区块只有 当条件首次变为true时才会被渲染。
v-show:
元素无论初始条件如何,始终会被渲染,只有CSS display属性会被切换。
总之,v-if有更高切换开销,而v-show有更高的初始渲染开销。
因此,如果需要频繁切换,则使用v-show较好;如果在运行时绑定条件很少改变,则v-if会更合适
- 列表渲染
用v-for指令基于一个数组来渲染一个列表。(v-for遍历数组只有两个值)
v-for指令的值需要使用item in items 形式的特殊语法,其中items是源数据的数组,而item是迭代的别名。
在components下新建ListDemo.vue
【ListDemo.vue】添
<template>
<h3>列表渲染</h3>
</template>
<script>
</script>
【app.vue】(仿照,引入ListDemo. from './components/ListDemo.vue';再在template中显示)
<script setup>
import HelloWorld from './components/HelloWorld.vue'
import IfDemo from './components/IfDemo.vue'
import ListDemo from './components/ListDemo.vue'
</script>
<template>
<HelloWorld/>
<IfDemo/>
<ListDemo/>
</template>
此时,打开浏览器显示“列表渲染”
【LIstDemo.vue】
<template>
<h3>列表渲染</h3>
<p v-for="item in names">{{ item }}</p>
</template>
<script>
export default{
data(){
return{
names:["百战程序员","尚学堂","IT"]
}
}
}
</script>
(其中names为数组,
item为别名 即“百战程序员,尚学堂,IT“。item名字不是固定的,是可以修改的)
打开浏览器,页面显示“百战程序员 尚学堂 IT”。
打开控制台,看到在<h3>标签下有三个<p>标签。通过v-for的形式遍历循环出来三个<p>标签中的每个<p>标签承载的数组中的内容。
【复杂数据】大多数情况下,我们渲染的数据来源于网络请求,也就是JSON格式
<template>
<h3>列表渲染</h3>
<p v-for="item in names">{{ item }}</p>
<div v-for="item in result">
<p>{{ item.title }}</p>
<img :src="item.avator" alt="">
</div>
</template>
<script>
export default{
data(){
return{
names:["百战程序员","尚学堂","IT"],
result:[
{
"id": 2261677,
"title": "鄂尔多斯 | 感受一座城市的璀璨夜景 感受一座城市,除了白日里的车水马龙,喧嚣繁华之",
"avator": "https://pic.qyer.com/avatar/002/25/77/30/200?v=1560226451",
},
]
}
}
}
</script>
v-for 可支持使用可选的第二个参数表示当前的位置索引。
<template>
<h3>列表渲染</h3>
<p v-for="(item,index) in names">{{ item }}-{{ index }}</p>
</template>
<script>
export default{
data(){
return{
names:["百战程序员","尚学堂","IT"]
}
}
}
</script>
(index代表数组的下标,{{ item }}让它显示出来)
在浏览器中显示:
也可用of作为分隔符来替代in,这更接近js的迭代器语法。
<p v-for="(item,index) of names">{{ item }}-{{ index }}</p>
【v-for与对象】可用v-for来遍历一个对象的所有属性。(遍历对象有三个值)
<template>
<h3>列表渲染</h3>
<div>
<p v-for="item of UserInfo">{{ item }}</p>
</div>
</template>
<script>
export default{
data(){
return{
UserInfo:{
name:"iwen",
age:20,
sex:"男"
}
}
}
}
</script>
(item代表UserInfo对象里边每一个里边的值:“iwen“ 20 男)
在浏览器打开:
<p v-for="(value,key,index) of UserInfo">{{ value }}-{{ key }}-{{ index }}</p>
(value还是代表值
Key代表“name age sex”
Index代表下标。
这三个值的顺序不可更改,是默认的。)
在浏览器打开:
- 通过Key管理状态
Vue默认按照“就地更新”的策略来更新通过v-for渲染的元素列表。
当数据项的顺序改变时,Vue不会随之移动DOM元素的顺序,而是就地更新每个元素,确保它们在原本指定的索引位置上渲染。
为了给Vue一个提示,以便它可以跟踪每个节点的标识,从而重用和重新排序现有的元素,需要为每个元素对应的块提供一个唯一的key attribute:
【新建KeyDemo.vue】
<template>
<h3>Key属性添加到v-for中</h3>
</template>
<script>
</script>
【App.vue】(应用KeyDemo 显示)
<script setup>
import HelloWorld from './components/HelloWorld.vue'
import IfDemo from './components/IfDemo.vue'
import ListDemo from './components/ListDemo.vue'
import KeyDemo from './components/KeyDemo.vue'
</script>
<template>
<HelloWorld/>
<IfDemo/>
<ListDemo/>
<KeyDemo/>
</template>
【KeyDemo.vue】
<template>
<h3>Key属性添加到v-for中</h3>
<p v-for="(item,index) in names" :key="index">{{ item }}</p>
<div v-for="item of result" :key="item.id">
<p>{{ item.title }}</p>
<img :src="item.avator" alt="">
</div>
</template>
<script>
export default{
data(){
return{
names:["百战程序员","尚学堂","IT"],
result:[
{
"id": 2261677,
"title": "鄂尔多斯 | 感受一座城市的璀璨夜景 感受一座城市,除了白日里的车水马龙,喧嚣繁华之",
"avator": "https://pic.qyer.com/avatar/002/25/77/30/200?v=1560226451",
},
]
}
}
}
</script>
Key在这里是一个通过v-bind绑定的特殊attribute
推荐在任何可行的时候为v-for提供一个key attribute
Key绑定的值期望是一个基础类型的值,eg.字符串或number类型
- 事件处理
用v-on指令(简写为@)来监听DOM事件,并在事件触发时执行对应的js
用法:v-on:click=”methodName”或”@click=”handler”
事件处理器的值可以是:
- 内联事件处理器:事件被触发时执行的内联js语句(与onclick类似){简单场景}
- 方法事件处理器:一个指向组件上定义的方法的属性名或是路径
【内联事件处理器 新建EventDemo1.vue】
<template>
<h3>内联事件处理器</h3>
<button @click="count++">Add</button>
</template>
<script>
export default{
data(){
return{
count:0
}
}
}
</script>
【方法事件处理器 新建EventDemo2.vue】
<template>
<h3>方法事件处理器</h3>
<button @click="addCount">Add</button>
<p>{{ count }}</p>
</template>
<script>
export default{
data(){
return{
count:0
}
},
methods:{
addCount(){
//读取到data里面的数据的方案:this.count
this.count++
}
}
}
</script>
- 事件参数
可获取event对象和通过事件传递数据
【获取event对象】
<template>
<h3>方法事件处理器</h3>
<button @click="addCount">Add</button>
<p>{{ count }}</p>
</template>
<script>
export default{
data(){
return{
count:0
}
},
methods:{
//event对象
addCount(e){
//Vue中的event对象,就是原生js的Event对象
e.target.innerHTML = 'Add' + this.count
this.count++
}
}
}
</script>
【传递数据】
<template>
<h3>方法事件处理器</h3>
<button @click="addCount('hello')">Add</button>
<p>{{ count }}</p>
</template>
<script>
export default{
data(){
return{
count:0
}
},
methods:{
//event对象
addCount(msg){
//Vue中的event对象,就是原生js的Event对象
//e.target.innerHTML = 'Add' + this.count
console.log(msg);
this.count++
}
}
}
</script>
【传递参数过程中获取event】
<template>
<h3>事件传参</h3>
<p @click="getNameHandler(item,$event)" v-for="(item,index) of names" :key="index">{{ item }}</p>
</template>
<script>
export default{
data(){
return{
names:["iwen","ime","frank"]
}
},
methods:{
getNameHandler(name,e){
console.log(name);
console.log(e);
}
}
}
</script>
(传递参数:@click=”getNameHandle(item)”
传递参数过程中,获取event对象:@click=”getNameHandle(item,$event)” )
- 事件修饰符(简化代码)
处理事件时,常用event.preventDefault()或event.stopPropagation().
但用数据逻辑更好,Vue为v-on提供了事件修饰符:.stop,.prevent,.once,.enter,…
【阻止默认事件】.prevent
<template>
<h3>事件修饰符</h3>
<a @click.prevent="clickHandle" href="https://itbaizhan.com">百战程序员</a>
</template>
<script>
export default{
data(){
return{
}
},
methods:{
clickHandle(e){
//阻止默认事件
//e.preventDefault();
console.log("点击了");
}
}
}
</script>
【阻止事件冒泡】.stop
<template>
<h3>事件修饰符</h3>
<a @click.prevent="clickHandle" href="https://itbaizhan.com">百战程序员</a>
<div @click="clickDiv">
<p @click.stop="clickP">测试冒泡</p>
</div>
</template>
<script>
export default{
data(){
return{
}
},
methods:{
clickHandle(e){
//阻止默认事件
//e.preventDefault();
console.log("点击了");
},
clickDiv(){
console.log("DIV");
},
clickP(e){
//e.stopPropagation();
console.log("P");
}
}
}
</script>
十一、数组变化侦测
【变更方法】对调用它们的原数组进行变更。eg. push(),pop(),shift(),unshift(),splice(),sort(),reverse()
【替换一个数组】不可变方法。eg. filter(),concat(),slice(),这些都不会更改原数组,而总是返回一个新数组。
<template>
<h3>数组变化侦听</h3>
<button @click="addListHandle">添加数据</button>
<ul>
<li v-for="(item,index) of names" :key="index">{{ item }}</li>
</ul>
</template>
<script>
export default{
data(){
return{
names:["iwen","ime","frank"]
}
},
methods:{
addListHandle(){
//引起UI自动更新(检测变更方法)
//this.names.push("sakura")
//不会引起UI自动更新
this.names = this.names.concat(["sakura"])
}
}
}
</script>
<template>
<h3>数组变化侦听</h3>
<button @click="addListHandle">添加数据</button>
<ul>
<li v-for="(item,index) of names" :key="index">{{ item }}</li>
</ul>
</template>
<script>
export default{
data(){
return{
names:["iwen","ime","frank"]
}
},
methods:{
addListHandle(){
//引起UI自动更新
//this.names.push("sakura")
//不会引起UI自动更新
this.names = this.names.concat(["sakura"])
}
}
}
</script>
十二、计算属性(描述复杂逻辑)
把模板语法里的复杂逻辑放到计算属性。
【应用计算属性】
<template>
<h3>{{ itbaizhan.name }}</h3>
<p>{{ itbaizhanContent }}</p>
</template>
<script>
export default{
data(){
return{
itbaizhan:{
name:"百战程序员",
content:["前端","Java","python"]
}
}
},
//计算属性
computed:{
itbaizhanContent(){
return this.itbaizhan.content.length > 0 ? 'Yes' : 'No'
}
}
}
</script>
【计算属性缓存vs方法】
计算属性:计算属性值会基于其响应式依赖被缓存。一个计算属性仅会在其响应式依赖更新时才重新计算。(计算一次)
方法:方法调用总是会在重渲染发生时再次执行函数。(计算多次)
<template>
<h3>{{ itbaizhan.name }}</h3>
<p>{{ itbaizhanContent }}</p>
<p>{{ itbaizhanContents() }}</p>
</template>
<script>
export default{
data(){
return{
itbaizhan:{
name:"百战程序员",
content:["前端","Java","python"]
}
}
},
//计算属性
computed:{
itbaizhanContent(){
return this.itbaizhan.content.length > 0 ? 'Yes' : 'No'
}
},
//放函数或者方法
methods:{
itbaizhanContents(){
return this.itbaizhan.content.length > 0 ? 'Yes' : 'No'
}
}
}
</script>
十三、Class绑定