创建vue项目
方式一:使用 npm命令
npm init vue@latest
创建成功后输入项目名称,然后一直选择否,直到项目创建成功
注意:有的时候可能会报错,可能是由于node.js的版本不一致所导致的,应该更改node.js的版本.见此贴:修改以及管理node.js的版本号方法
方式二:使用vue命令
vue create vue-demo
可以选择默认的模版也可以选择自定义的模版创建项目
vue基础知识
项目目录结构
采用方式一创建之后的项目的目录结构如图:
之后执行命令cnpm install
来安装项目所需要的依赖文件也就是node_modules文件夹
使用npm run dev
让项目跑起来
模版语法
删除无关紧要的模版代码
在App.vue中写入需要的代码
- 能够计算有结果的值
- 能够使用三目运算符
- 能够对字符串进行切割
- 能够使用html标签
<template>
<h3>语法模版</h3>
<p>{{ msg }}</p>
<p>{{ hello }}</p>
<p>能够计算有结果的值:10+1={{ number + 1}}</p>
<p>能够使用三目运算符:ok?yes:no={{ ok?'yes':'no'}}</p>
<p>能够对字符串进行切割:{{ message.split("").reverse().join("")}}</p>
<p>能够使用html标签:{{ myhtml }}</p>
<!-- 正确使用原始html标签:v-html -->
<p v-html="myhtml"></p>
</template>
<script>
export default {
data(){
return{
msg: "神奇的语法!",
hello: "hello",
number:10,
ok:true,
message:'大家好,vue真的很有趣!',
myhtml:'<div>这是一个div标签</div>'
}
}
}
</script>
属性绑定
属性attribute不能够直接在html中使用,然而可以通过v-bind召指令来进行绑定,
<template>
<h1>属性绑定</h1>
<div>{{ msg }}</div>
<div v-bind:id="divId" v-bind:class="divClass">测试</div>
</template>
<script>
export default {
data() {
return {
msg: "Hello World",
divId: "myDivId",
divClass: "myDivClass"
};
}
};
</script>
可以看到对应的id和class绑定的分别是divId和divCLass对应的的值
这样做的好处是方便管理以及对样式进行修改
也可以将id和class以及一些其他的属性写到集合中,再去绑定
<div v-bind="totalAttr">测试2</div>
totalAttr: {
id: "myId",
class: "myDivClass",
name: "myName",
style: "color:red;font-size:20px;",
},
可以看到也是成功的将属性赋值
同样v-bind能够缩写成:
<button :='buttonAttr'>按钮</button>
buttonAttr: {
id: "myButtonId",
class: "myButtonClass",
name: "myButtonName",
disabled: true,
},
条件渲染
vue为我们提供了四个条件判断语句分别是:
v-if
v-else
v-else-if
v-show
q其中v-if和v-show时限的效果是相同的,但是在加载上有所区别:
v-if是惰性加载,如果条件是false,那么就不会去记载这个模块
v-show无论条件是否为false,都会加载模块,只不过属性相当于display
列表渲染
使用v-for的指令基于数组来进行渲染,其中item in items的方法进行使用,
<template>
<h1>列表渲染</h1>
<div v-for="book,index in booklists">
{{ book.name }} {{ index }}
</div>
<table border="1">
<tr>
<th>序号</th>
<th>书名</th>
<th>价格</th>
</tr>
<tr v-for="book,index in booklists">
<td>{{ index+1 }}</td>
<td>{{ book.name }}</td>
<td>{{ book.price }}</td>
</tr>
</table>
</template>
<script>
export default {
data(){
return {
booklists: [
{name: 'book1', price: 1001},
{name: 'book2', price: 1002},
{name: 'book3', price: 1003},
{name: 'book4', price: 1004},
{name: 'book5', price: 1005},
{name: 'book6', price: 1006}
]
}
}
};
</script>
其中有一个index属性能够直接使用,自动的为对象进行编号,从0开始
v-for="(book,index) in booklists"
同样也能使用of进行迭代,效果一样:
v-for="(book,index) of booklists"
通过:key来补充状态(对列表循环的一个补充):
当Vue 正在更新使用vfor渲染的元素列表时,它默认使用"就地更新"的策略。如果数据项的顺序被改变,Vue将不会移动DOM元素来匹配数据项的顺序,而是就地更新每个元素,并且确保它们在每个索引位置正确渲染。
为了给Vue一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一的key attribute:
<div v-for="(item, index) in items":key="item.id|index">
<l-- 内容-->
</div>
事件处理
使用v-on
命令,相当于@
- 内联事件处理器:事件被触发时执行的内联JavaScript语句(与onclick类似)
- 方法事件处理器:一个指向组件上定义的方法的属性名或是路径
通过内联事件处理器,通俗一点来讲就是直接引用方法同时又有参数传递,例如:
<h1>事件绑定:</h1>
<button @click="count++"> 这个是采用内联处理器进行的 :点我加一</button>
<p>{{ count }}</p>
data(){
return {
count:0,
count1:0,
msg:""
}
},
以及:
<input type="text" v-model="msg">
<button @click="showMsg(msg)"> 这个是采用内联处理器进行的 :点我查看输入的信息</button>
methods: {
add(){
this.count1++;
},
showMsg(massage){
alert(massage);
}
}
方法事件处理器就是通过绑定方法,然后执行一些操作例如:
<button @click="add()"> 这个是采用方法事件处理器进行的 :点我加一</button>
<p>{{ count1 }}</p>
methods: {
add(){
this.count1++;
},
}
参数传递
可以通过事件获取一些信息:
<h1>参数传递:</h1>
<input type="text" v-model="msg">
<button @click="showMsg(msg)"> 点我查看输入的信息</button>
<p @click="clickEvent(list,$event)" v-for="(list,index) in lists"> {{list}}</p>
data(){
return {
msg:"",
lists:['day1','day2','day3']
}
},
methods: {
showMsg(massage){
alert(massage);
},
clickEvent(content,e){
console.log(e);
console.log(content);
}
}
此时如果还需要获取event信息,需要在调用端使用$event
,例如@click="clickEvent(list,$event)"
组件部分都能添加事件,例如标签
事件修饰符
在我们写点击事件的时候,常常会有一些特殊的要求,就比如点击a标签之后不跳转链接,
在处理事件时调用event.preventDefault()
或event.stopPropagation()
是很常见的。尽管我们可以直接在方法内调用,但如果方法能更专注于数据逻辑而不用去处理DOM事件的细节会更好
在这里vue为我们提供了更加便捷的方式去实现这些处理事件的功能.
常见的有以下几种:
- .stop
- .prevent
- .once
- .enter
<!-- 单击事件将停止传递 -->
<a @click.stop="doThis"></a>
<!-- 提交事件将不再重新加载页面 -->
<form @submit.prevent="onSubmit"></form>
<!-- 修饰语可以使用链式书写 -->
<a @click.stop.prevent="doThat"></a>
<!-- 也可以只有修饰符 -->
<form @submit.prevent></form>
<!-- 仅当 event.target 是元素本身时才会触发事件处理器 -->
<!-- 例如:事件处理器不来自子元素 -->
<div @click.self="doThat">...</div>
原始的写法是这样的:
<template>
<h1>事件修饰符</h1>
<a @click="clickEvent1()" :href="url">点我跳转百度</a>
</template>
<script>
export default {
data(){
return {
msg: false,
url: "https://www.baidu.com"
}
},
methods:{
clickEvent1(event){
event.preventDefault();
console.log("clickEvent1");
}
}
};
</script>
可以用.prevent来代替
<template>
<h1>事件修饰符</h1>
<a @click.prevent="clickEvent1()" :href="url">点我跳转百度</a>
</template>
<script>
export default {
data(){
return {
msg: false,
url: "https://www.baidu.com"
}
},
methods:{
clickEvent1(){
console.log("clickEvent1");
}
}
};
</script>
再比如event.stopPropagation(),是阻止子类的事件引起父类事件的反应
<div @click="clickEvent2">
<button @click="clickEvent3">点我打印</button>
</div>
clickEvent2(){
console.log("div");
},
clickEvent3(){
console.log("button");
},
点击之后会发现打印button后,还会将div中的事件进行打印操作,
我们可以使用event.stopPropagation(),也可以直接在click后添加.stop
<div @click="clickEvent2">
<button @click.stop="clickEvent3">点我打印</button>
</div>
可以看到只会button
其他方法可以参考vue文档vue.js官方文档
数组变化侦测
Vue 能够侦听响应式数组的变更方法,并在它们被调用时触发相关的更新。这些变更方法包括:
- push()
- pop()
- shift()
- unshift()
- splice()
- sort()
- reverse()
原始数组发生变化,且动态加载最新更改的数组
替换数组
顾名思义,就是会对调用它们的原数组进行变更。相对地,也有一些不可变 (immutable) 方法,例如 filter(),concat() 和 slice(),这些都不会更改原数组,而总是返回一个新数组。当遇到的是非变更方法时,我们需要将旧的数组替换为新的:
// `items` 是一个数组的 ref
items.value = items.value.filter((item) => item.message.match(/Foo/))
在计算属性中使用 reverse() 和 sort() 的时候务必小心!这两个方法将变更原始数组,计算函数中不应该这么做。请在调用这些方法之前创建一个原数组的副本
计算属性
模板中的表达式虽然方便,但也只能用来做简单的操作。如果在模板中写太多逻辑,会让模板变得臃肿,难以维护。
const author = reactive({
name: 'John Doe',
books: [
'Vue 2 - Advanced Guide',
'Vue 3 - Basic Guide',
'Vue 4 - The Mystery'
]
})
<p>Has published books:</p>
<span>{{ author.books.length > 0 ? 'Yes' : 'No' }}</span>
这里的模板看起来有些复杂。我们必须认真看好一会儿才能明白它的计算依赖于 author.books。更重要的是,如果在模板中需要不止一次这样的计算,我们可不想将这样的代码在模板里重复好多遍。
因此我们推荐使用计算属性来描述依赖响应式状态的复杂逻辑。这是重构后的示例:
<script setup>
import { reactive, computed } from 'vue'
const author = reactive({
name: 'John Doe',
books: [
'Vue 2 - Advanced Guide',
'Vue 3 - Basic Guide',
'Vue 4 - The Mystery'
]
})
// 一个计算属性 ref
const publishedBooksMessage = computed(() => {
return author.books.length > 0 ? 'Yes' : 'No'
})
</script>
<template>
<p>Has published books:</p>
<span>{{ publishedBooksMessage }}</span>
</template>
计算属性和方法
<template>
<h1>计算属性vs方法</h1>
<p>{{ isNullComputed }}</p>
<p>{{ isNullMethod() }}</p>
</template>
<script>
export default {
data(){
return {
msg: false,
lists:[1,2,3,4]
}
},
methods:{
isNullMethod(){ // 方法
return this.lists.length==0 ? 'yes' : 'no';
}
},
computed: {// 计算属性
isNullComputed(){
return this.lists.length==0 ? 'yes' : 'no';
}
}
};
</script>
2种方式在结果上确实是完全相同的,然而,不同之处在于计算属性值会基于其响应式依赖被缓存。一个计算属性仅会在其响应式依赖更新时才重新计算。
简单理解就是
computed只会计算一次,只要所涉及的对象不发生变化,就调用多次不会重新计算
methods就是调用一次,就会进行一次运算,调用多次就会进行多次运算
class属性绑定
方式一:动态绑定单个class
<template>
<h1>class绑定样式</h1>
<p :class="myClass">这是一段用来测试的话,你好你好啊黑客技术打卡机阿好哒</p>
</template>
<script>
export default {
data(){
return {
myClass:"classForGreen",
}
}
};
</script>
<style>
.classForGreen{
color: green;
background-color: black;
}
</style>
方式二:绑定多个类,这时可以利用数组,将类写进数组中
<template>
<h1>class绑定样式</h1>
<p :class="[myClass1,myClass2]">这是一段用来测试的话,你好你好啊黑客技术打卡机阿好哒</p>
</template>
<script>
export default {
data(){
return {
myClass1:"classForGreen",
myClass2:"classForBigSize"
}
}
};
</script>
<style>
.classForGreen{
color: green;
background-color: black;
}
.classForBigSize{
font-size: 50px;
}
</style>
也可以在return中写好
<template>
<h1>class绑定样式</h1>
<p :class="myClass">这是一段用来测试的话,你好你好啊黑客技术打卡机阿好哒</p>
</template>
<script>
export default {
data(){
return {
myClass :["classForGreen","classForBigSize"]
}
}
};
</script>
<style>
.classForGreen{
color: green;
background-color: black;
}
.classForBigSize{
font-size: 50px;
}
</style>
方式三:采用对象的方式进行动态绑定,也就是集合的形式,同时还能对多个类进行动态的管理,比如添加或者删除某个类
<template>
<h1>class绑定样式</h1>
<p :class="{'classForGreen':flag,'classForBigSize':flag}">这是一段用来测试的话,你好你好啊黑客技术打卡机阿好哒</p>
<button @click="flag=!flag">点击切换</button>
</template>
<script>
export default {
data(){
return {
flag:true
}
}
};
</script>
<style>
.classForGreen{
color: green;
background-color: black;
}
.classForBigSize{
font-size: 50px;
}
</style>
同时还支持数组包含对象的写法,但是要注意的是不能够对象包着数组
style绑定
常规的绑定为:
<p style="font-size: 15px;color: #c4a7a7;">这是一段用来测试style绑定的话</p>
动态绑定需要写成集合的形式:
<p :style="{color:'red' ,fontSize:20+'px'}">这是一段用来测试style绑定的话</p>
可以发现样式的绑定还是有点麻烦的,但是vue为我们提供了增强的绑定方法,也就是直接绑定一个样式对象:
data() {
return {
styleObject: {
color: 'red',
fontSize: '13px'
}
}
}
<div :style="styleObject"></div>
监听器
用来监听data中数据的变化,之后再执行某种函数/方法操作.
<template>
<h1>监听器</h1>
<p>{{ msg }}</p>
<button @click="changeMsg">修改数据信息</button>
</template>
<script>
export default {
data(){
return {
msg: false,
}
},
methods:{
changeMsg(){
this.msg = !this.msg;
}
},
computed: {
},
watch: {// 监听器,监听数据的变化,方法名必须与data中定义的属性名一致
msg(newValue, oldValue) {// newValue:新值, oldValue:旧值
console.log(newValue, oldValue);
}
}
};
</script>
函数名必须与被监听的命名一样
表单输入绑定
使用v-model将data中的数据进行绑定,也就是说输入框发生变化,data中绑定的数据也会随之发生变化.
<template>
<h1>表单输入绑定</h1>
<input type="text" v-model="msg">
<p>{{ msg }}</p>
</template>
<script>
export default {
data(){
return {
msg: "",
}
}
};
</script>
同样也适合于单选框与复选框
<input type="checkbox" id="checkbox" v-model="checked" />
<!-- for 绑定label对应的id -->
<label for="checkbox">{{ checked }}</label>
也可以收集在数组中
<div>选择的科目: {{ checkedNames }}</div>
<input type="checkbox" id="jack" value="语文" v-model="checkedNames" />
<label for="jack">语文</label>
<input type="checkbox" id="john" value="数学" v-model="checkedNames" />
<label for="john">数学</label>
<input type="checkbox" id="mike" value="英语" v-model="checkedNames" />
<label for="mike">英语</label>
同时v-model也提供了修饰符
.lazy | 失去焦点后生效 |
---|---|
.number | 输入只能为数字 |
.trim | 去掉头尾空格 |
<input type="text" v-model.lazy.trim="msg">
<p>{{ msg }}</p>
操作DOM
vue为我们封装好了很多东西,比如
操作数据用{{ }}的形式
绑定属性使用v-bind
绑定方法事件使用v-on
但是有的是后还是需要操作DOM元素才能够满足要求
vue也为我们提供了操作DOM的语法
虽然 Vue 的声明性渲染模型为你抽象了大部分对 DOM 的直接操作,但在某些情况下,我们仍然需要直接访问底层 DOM 元素。要实现这一点,我们可以使用特殊的 ref attribute:
<input ref="input">
ref 是一个特殊的 attribute,和 v-for 章节中提到的 key 类似。它允许我们在一个特定的 DOM 元素或子组件实例被挂载后,获得对它的直接引用。这可能很有用,比如说在组件挂载时将焦点设置到一个 input 元素上,或在一个元素上初始化一个第三方库。
<template>
<div ref="myDivRef">今天好热</div>
<button @click="changeTemperature">点击按钮变得不热</button>
</template>
<script>
export default {
methods:{
changeTemperature(){
this.$refs.myDivRef.innerText = "今天一点都不热";
}
}
};
</script>
同样也可以用来获取输入框中的信息
<template>
<div ref="myDivRef">今天好热</div>
<button @click="changeTemperature">点击按钮变得不热</button>
请输入信息:
<input type="password" ref="myInputRef">
<p></p>
<button @click="showPassword">点击按钮获取密码</button>
密码是:<p ref="showPassword"></p>
</template>
<script>
export default {
methods:{
changeTemperature(){
this.$refs.myDivRef.innerText = "今天一点都不热";
},
showPassword(){
console.log(this.$refs.myInputRef.value);
this.$refs.showPassword.innerText = this.$refs.myInputRef.value//value获取输入框中的值
}
}
};
</script>
组件
组件基础
app.vue列出了应用组件的三个步骤:
<script>
// 第一步,引入组件
import HelloWorld from './components/HelloWorld.vue'
// 第二步,注册组件
export default {
components: {
HelloWorld
}
}
</script>
<template>
<!-- 第三步,使用组件-->
<HelloWorld/>
</template>
组件允许我们将 UI 划分为独立的、可重用的部分,并且可以对每个部分进行单独的思考。在实际应用中,组件常常被组织成层层嵌套的树状结构:
这和我们嵌套 HTML 元素的方式类似,Vue 实现了自己的组件模型,使我们可以在每个组件内封装自定义内容与逻辑。Vue 同样也能很好地配合原生 Web Component。
组件之间可以相互嵌套
代码:
app.vue
<script>
// 第一步,引入组件
import myTitle from './pages/title.vue'
import myMain from './pages/Main.vue'
import myAside from './pages/Aside.vue'
// 第二步,注册组件
export default {
components: {
myTitle,
myMain,
myAside
}
}
</script>
<template>
<!-- 使用组件-->
<myTitle/>
<myMain/>
<myAside/>
</template>
main.vue
<template>
<div class="main">
<h1>Main</h1>
<Article/>
<Article/>
<Article/>
</div>
</template>
<script>
import Article from "@/pages/Article.vue";
export default {
components: {
Article
}
}
</script>
<style scoped>
.main{
float: left;
width: 65%;
height: 600px;
border: #6b7885 5px solid;
box-sizing: border-box;
}
</style>
aside.vue
<template>
<div class="aside">
<h1>Aside</h1>
<MyItem/>
<MyItem/>
<MyItem/>
</div>
</template>
<script>
import MyItem from '@/pages/Item.vue'
export default {
components: {
MyItem
}
}
</script>
<style scoped>
.aside {
float: right;
width: 35%;
height: 600px;
border: #6b7885 5px solid;
box-sizing: border-box;
}
</style>
article.vue
<template>
<h1>Article</h1>
</template>
<script>
export default {
}
</script>
<style scoped>
h1{
float: left;
width: 60%;
height: 100px;
border: #6b7885 5px solid;
text-align: center;
margin: 10px 0 0 20%;
background-color: #42b983;
}
</style>
item.vue
<template>
<h1>Item</h1>
</template>
<script>
export default {
name: "Item"
}
</script>
<style scoped>
h1 {
width: 60%;
height: 100px;
border: #6b7885 5px solid;
background-color: #42b983;
text-align: center;
line-height: 100px;
margin: 10px 0 0 20%;
}
</style>
结果:
局部组件与全局组件
之前我们使用的是局部组件
全局组件顾名思义就是能够在全局进行使用的组件
可以这样使用之前的组件:
import { createApp } from 'vue'
import App from './App.vue'
import Title from './pages/title.vue'
const app = createApp(App)
// 注册全局组件写在这
app.component('myTitle', Title)
app.mount('#app')
组件传递数据,父传子和子传父
组件与组件之是有数据传递的
父组件传递给子组件
通过关键字props
使用方法如下:
<template>
<div class="green12">
<h1>我是父组件</h1>
<!-- 在调用处绑定需要传递的参数,传送给子组件-->
<child :msg="msg" :num="num" :arr="arr"/>
</div>
</template>
<script>
import Child from './Child.vue'
export default {
name: "Parent",
data(){
return{
msg:'我是来自父组件的数据',
num:1,
arr:[1,2,3,4,5]
}
},
components:{
Child
}
}
</script>
<style scoped>
.green12 {
border: #2f80c2 5px solid;
height: 600px;
}
</style>
子组件通过props进行接收
<template>
<div class="green11">
<h1>我是子组件</h1>
{{msg}}------{{num}}
<ul>
<li v-for="(item,index) in arr" :key="index">{{item}}</li>
</ul>
</div>
</template>
<script>
export default {
name: "Child",
props:['msg','num','arr']
}
</script>
<style scoped>
.green11 {
width: 60%;
margin: 0 0 0 20%;
border: #42b983 5px solid;
height: auto;
}
</style>
可以看到props可以传递多个参数,而且任何参数都可以
props同样提供了校验方法,使用方法如下:
export default {
name: "Child",
props: {
msg: {
type: String, //类型
default: '' // 默认值
// required: true, //是否必传,不传前端会给出提醒
},
num: {
type: Number,
default: 0
},
arr: {
type: Array,
default: function () { //数组和对象的默认值是需要返回一个函数
return []
}
}
}
}
同样还有一点就是,在子组件中,不能够对父类传入的值进行修改
否则会直接报错
子组件传递给父组件的方法
前面我们知道可以通过<child :msg="msg" :num="num" :arr="arr"/>
的方式传给子组件一些东西,其中也包括了一些方法,也就是自定义监听事件
使用@方法名='方法名'
的形式传递给子组件
子组件通过this.$emit ('父组件传过来的方法a',a方法所需要的参数)
来回调父组件的方法
父组件代码:
<template>
<div class="green12">
<h1>我是父组件</h1>{{ fromChild }}
<!-- 在调用处绑定需要传递的参数,传送给子组件-->
<MyChild @getData="getData"/>
</div>
</template>
<script>
import MyChild from './MyChild.vue'
export default {
name: "Parent",
data() {
return {
fromChild: '',
msg: '我是来自父组件的数据',
num: 1,
arr: [1, 2, 3, 4, 5]
}
},
components: {
MyChild
},
methods: {
getData(myData, myNum, myArr) {
this.fromChild = myData
console.log(myNum)
console.log(myArr)
}
}
}
</script>
<style scoped>
.green12 {
border: #2f80c2 5px solid;
height: 600px;
}
</style>
子组件代码:
<template>
<div class="green11">
<h1>我是子组件</h1>
<button @click="emitEvent">点我传送数据给父组件</button>
</div>
</template>
<script>
export default {
name: "Child",
data() {
return {
msg: '我是来自子组件的数据',
num: 1,
arr: [1, 2, 3, 4, 5]
}
},
methods:{
emitEvent(){
this.$emit('getData',this.msg,this.num,this.arr)
}
}
}
</script>
<style scoped>
.green11 {
width: 60%;
margin: 0 0 0 20%;
border: #42b983 5px solid;
height: auto;
}
</style>
效果图:
插槽
插槽在父类与子类之间的数据传递
具名插槽
组件生命周期
生命周期函数
- 创建期:beforeCreate—created
- 挂载期:beforeMount—mounted
- 更新期:beforeUpdate—updated
- 销毁期:beforeUnmount— unmounted
mounted 一般放网络请求,在模版加载之后,再将得到的数据渲染上去.