title: Vue基础教程
date: 2022-01-26 21:09:30
tags: [前端框架,Vue]
categories: 前 端
cover:
Vue基础入门
基础知识:
- HTML+JavaScript+CSS
- Node环境和npm(依赖管理 )
- webpack(可选)
官方文档:
- 学习任何框架,最好的教程就是官方文档,但是官方文档往往是英文的,可能会劝退:( 建议提升英文:)
- 初学者更适合视频学习,等有一定能力便可以向文档进发,因为更加详细:)
DOM是什么?
Document Object Model,简称DOM,中文叫做“文档对象模型”
粗略理解就是:
DOM提供了对文档的结构化表述。从HTML或者xml程序中,对其结构进行访问,以及修改文档的结构、样式和内容,也就是可以对文档结构实现读写功能。(web页面与其源码都被称作是文档)
DOM 的实现
要想实现将js代码中的数据呈现到HTML页面上用DOM实现:
要先获取document/DOM,再获取节点,然后再去操作这个DOM节点(也就是在HTML中进行一系列的操作)。而使用Vue可以直接在html程序中绑定数据,当我们改变js中的数据时,就可以直接在页面上呈现出来
Vue相比于DOM的优点
要想实现将js代码中的数据呈现到HTML页面上,采用Vue可以让这个过程简化许多。
先创建Vue实例:
//创建VUE实例
new Vue({
el:"#app", //选择器
data:{
message:"我喜欢你"
}
})
然后将此实例和HTML中的
绑定到一起<div id="app">
<h1>{{meaasge}}</h1> <!-- 双花括号是表达式的意思 -->
</div>
所以,以后我们要在HTML里面用js的数据,就直接写相应的数据名称就可以了,让数据和页面形成一个绑定,我们只要操作数据,页面自己就会跟着变化。
入门知识点
-
文本:双花括号
-
属性:v-bind:
可省略,只写冒号:
-
事件:v-on:
可省略,只写@
注:如果要在函数里面使用Vue中的数据,可以使用this来访问
举例:
<div id="app">
<h1>message</h1>
<img v-bind:src="url"></img>
<img :src="url"></img>
<button v-on:click=""></button>
<button @click=""></button>
</div>
<script>
new Vue({
el:"#app", <!-- el为元素选择器的意思-->
data:{
message="我喜欢你",
url="inmages/1.jpg"
},
methods:{
sayLoveYou(){
alert("Love you!")
}
//Vue里的函数集合:methods,包含多个函数
//数据集合:data,包含多个变量数据
}
})
</script>
小练习:做一个翻页按钮
<body>
<div id="app">
<button @click="sub">-</button>
<span>{{ number }}</span>
<button @click="add">+</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
<script>
new Vue({
el:"#app",
data:{
number:0,
},
methods: {
add(){
this.number=this.number+1;
},
sub(){
if(this.number==0){
alert("不可以小于零")
}
else
this.number--
}
}
})
</script>
</body>
</html>
效果:
创建Vue项目
配置开发环境
使用Node.js环境开发,
- cmd安装vue/cli
- 创建项目hello,建议在对应的目录下创建
- 启动服务器
命令:
npm install -g @vue/cli
vue create hello
cd hello
npm run serve
组件化开发概述
什么是组件化呢?
就是我们在html中进经常要用到标签,但是总有我们需要的标签HTML提供不了,所以,组件化开发就是,我们开发的.vue文件(又叫vue组件)作为html中的标签来使用。
例如我们可以开发.vue登录组件或者.vue轮播图组件然后插入到HTML中
优点:简洁与复用
项目结构概述
- 后缀.vue是我们上文中提到的组件,
- main.js是主文件,用于对应id与对应的组件(App.vue)
- components意为组件,一般我们自己开发的,也就是自定义的组件就放在这个目录下
剩余的一些为配置文件,
在这个vue中,我们可以写html、JavaScript、CSS代码,分别对应的是、、
例如:
<template>
<!-- html代码 -->
<div id="app">
<h1>I lOVE You</h1>
<img alt="Vue logo" src="./assets/friend_404.gif">
<h1>{{message}}</h1>
</div>
</template>
<script>
// js代码、vue代码
export default { //首先,VUE中的js一定要用export来暴露自己
//其次在以vue为后缀的文件中vue表达数据的书写方式跟在以html为后缀的文件中vue的写法不一样
data(){
return{
message: "Hello world",
}
}
}
</script>
<style>
/* css代码 */
</style>
引入vue.js文件与Node环境下书写格式的区别
-
引入vue.js:
data:{ message:"我喜欢你!" }
-
Node环境下:
data(){ return{ message: "Hello world", } }
模板语法
指令
v-开头的
条件判断类:
- v-if
- v-show
v-if与v-show的异同点:
-
同:
都可以隐藏或者显示元素(true:显示、false:不显示)
-
异:
if不渲染DOM,如果if为false,在浏览器中不显示
show渲染DOM,如果show都为false,在浏览器中将元素设置为display:one
显示列表与制表类:
v-for
这个for啊,极有可能是遍历,循环的意思
利用v-for指令显示列表
<template>
<!-- html代码 -->
<div id="app">
<ul>
<li v-for="(fruit,index) of fruits" :key="index">
<p>水果名称:{{fruit}}</p>
<p>水果序号:{{index}}</p>
<p>水果名称和序号:{{fruit}}序号为{{index}}</p>
</li>
</ul>
</div>
</template>
<script>
// js代码、vue代码
export default { //首先,VUE中的js一定要用export来暴露自己
//其次在vue中的js代码中的书写方式跟在html中的vue的写法不一样
data(){
return{
fruits:["苹果","芒果","香蕉","原梦"]
}
}
}
</script>
显示结果:
可以看出就像是一个遍历呢,
一长串如何记住呢?
v-for="(fruit,index) of fruits" :key="index"
元素索引属于集合,key的值为索引,然后将这个元素放在{{}}中
利用v-for制表
<template>
<!-- html代码 -->
<div>
<table>
<!-- 将studens显示在表格里 -->
<thead>
<th>序号</th>
<th>姓名</th>
<th>年龄</th>
</thead>
<tbody>
<!-- <tr>{{v.n}}</tr> -->
<tr v-for="(v,i) of students" :key="i">
<td>{{i + 1}}</td>
<td>{{v.name }}</td>
<td>{{v.age}}</td>
</tr>
<!-- <tr v-for="(v,i) of students" :key="i">{{v.age}}</tr> -->
</tbody>
</table>
</div>
</template>
<script>
// js代码、vue代码
export default { //首先,VUE中的js一定要用export来暴露自己
//其次在vue中的js代码中的书写方式跟在html中的vue的写法不一样
components:{Hello,MenuList},
data(){
return{
students:[
{name:"张三 ",age:13},
{name:"罗翔 ",age:12},
{name:"John ",age:18}
]
}
}
}
</script>
<style>
/* css代码 */
</style>
演示效果:
组件嵌套
组件A想要用组件B 的功能,然后需要把组件B嵌套在A中。
三步走:命名、注册、传值
组件命名
B组件命名要首字母大写(小写可能会很HTML标签冲突,不建议这样写),大驼峰式,后缀.vue
注册组件
在B组件中暴露自己,在A组件中引入B组件
import B from "./components/B.vue";
在A中注册组件B,需要在A中写
components:{
B:B
}
最后就是将B组价作为A组件的标签,放入A中:
<B>
</B>
组件传值
父子级的概念是,A组件如果包含B组件标签,则称A是B 的父级。
父级向子级传递数据
格式是属性传递(所以用双花括号来表达数据,用:来绑定标签)
首先需要在父级中单向引入子级
然后需要在子级中加入props属性
props是个字符串数组,里面的值为父级传过来的属性名
App.vue为父级
Child.vue为子级
<template>
<!-- html代码 -->
<div>
<h1>I lOVE You</h1>
<Child :msg="message"></Child>
<Child :mag="message1"></Child>
</div>
</template>
<script>
import Child from "./components/Child.vue"
export default {
components:{Child},
data(){
return{
message:"这里是Vue的数据,我要传到Child组件中,也就是父传子",
message1:"我是Vue的二号数据"
}
}
}
</script>
<style>
/* css代码 */
</style>
<!--==============上面为App.vue=================下面为Child.vue====================-->
<template>
<div>
<h1>{{msg}} {{mag}}</h1>
</div>
</template>
<script>
export default {
props: ["msg","mag"], //props是一个数组的字符串,值为传过来的属性名msg
data(){
return{
}
}
}
</script>
效果图:
子级向父级传递数据
格式是事件传递(@来绑定标签)
比如说我现在想写一个功能,一个子级的按钮button,点击它的时候,把子级的数据传递给父级,然后父级的数据变成子级的一个数据。
先在父级中定义一个自定义事件,然后在子级定义一个按钮,然后绑定点击按钮(@click)发生的方法,在子级中定义这个方法,然后在这个方法中调用方法
this.$emit("toParent", this.msg);
其中
//$emit方法可以触发父级(App.vue)的自定义事件,也就是进行了绑定,向自定义事件toParent传递数据
//$emit方法需要两个参数,1、要绑定的父级中的自定义事件,2、所要传递是数据
//可以这样理解,因为我在App中的Child标签中自定义了事件toParent
//然后我又在Child中的调用了方法this.$emit,所以App就通过Chlid联系起来了
然后,分别在子级,父级中定义变量,用来存放数据(一般都充当容器的作用),子级的变量用来存放要给父级传递的数据,父级的变量用来接收子级传来的数据。
<template>
<div>
<div>我是父组件</div>
<div>我即将接收第二组件传值是:{{child2Msg}}</div>
<div>
<div>
<Child @toParent="getMag" />
</div>
</div>
</div>
</template>
<script>
import Child from "./components/Child.vue";
export default {
components: {
Child
},
data() {
return {
child2Msg: ""
};
},
methods: {
getMag(msg) {
this.child2Msg = msg;
}
}
};
</script>
<!--==============上面为App.vue=================下面为Child.vue====================-->
<template>
<div>
<div>我是第二个子组件</div>
<div>我要发送给父组件的值:{{msg}}</div>
<button @click="toParent">向父组件发送信息</button>
</div>
</template>
<script>
export default {
data() {
return {
msg: "我是第二组件,我要给父组件传值",
};
},
methods: {
toParent() {
this.$emit("toParent", this.msg);
}
}
};
</script>
效果图:
点击前
点击后
一个父传子,子传父的综合例子:
-
分析过程:
首先我们创建三个组件App.vue、Carts.vue、Counter.vue,然后
-
代码:
<template> <div> <Carts></Carts> </div> </template> <script> import Carts from "./components/Carts.vue" export default { components:{ Carts }, data(){ return{ msg:"" } } } </script> <!--==============上面为App.vue=================下面为Carts.vue====================--> <template> <div> <h1>购物车</h1> <ul> <li v-for="(v,i) of cars" :key="i"> {{v.name}} 单价:{{v.price}} <Counter :qu="v.qu" :index="i" @sub="sub" @add="add" ></Counter> </li> </ul> </div> </template> <script> import Counter from "./Counter.vue" export default { components:{Counter}, data(){ return{ qu:0, cars:[ {name:"兰博基尼",price:10000,qu:0}, {name:"宝马",price:2000,qu:0}, {name:"奔驰",price:4000,qu:0}, {name:"特斯拉",price:5000,qu:0}, ] } }, methods:{ sub(index){ if(this.cars[index].qu==0){ alert("数量不可为0哦!") } else{ this.cars[index].qu--; } }, add(index){ this.cars[index].qu++; } } } </script> <!--============================下面为Counter.vue===============================--> <template> <span> <button @click="sub">-</button> <span>{{qu}}</span> <button @click="add">+</button> </span> </template> <script> export default { props:["qu","index"], data(){ return{ } }, methods:{ sub(){ this.$emit("sub",this.index); }, add(){ this.$emit("add",this.index); } } } </script> <style> </style>
效果图:
总结:
非子级之间传递数据
需要定义一个.js文件,里面存放有两个子级文件都需要操作的全局数据与调用的方法,然后在两个子级文件中分别引入这个.js文件。需要注意的是,如何在两个子级文件中表示对js文件中数据与方法的使用。
<template>
<div>
<Brother></Brother>
<Sister></Sister>
</div>
</template>
<script>
import Brother from "./components/Brother"
import Sister from "./components/Sister"
export default {
components:{Carts,Brother, Sister},
data(){
return{
}
}
};
</script>
<!--==============上面为App.vue===============下面为Brother.vue====================-->
<template>
<div>
<h1>brother <button @click="changeData">改变数据</button></h1>
<p>{{bro.message}}</p>
</div>
</template>
<script>
import store from "../store.js"
export default{
data(){
return{
bro:store.state
}
},
methods:{
changeData(){
store.setStateMessage("brother data")
}
}
}
</script>
<style>
</style>
<!--============================下面为Sister.vue===============================-->
<template>
<div>
<h1>sister</h1>
<p>{{sis.message}}</p>
</div>
</template>
<script>
import store from "../store.js"
export default{
data(){
return{
sis:store.state
}
}
}
</script>
<style>
</style>
<!--============================下面为store.js===============================-->
export default {
state:{
message:"Hello Vue"
},
setStateMessage(str){
this.state.message = str;
}
}
在Brother中定义变量bro,然后将js文件中的数据赋值给它,使用时使用bro.message,因为在store.js中已经定义了message,Sister中同理。
在Brother中调用store.js的setStateMessage方法的调用格式为:store.setStateMessage()
计算属性与监听器
计算属性:computed
计算属性要解决的问题就是:
我有一个非常复杂的计算表达式,直接将这个表达式放在标签里显得很乱,很复杂。我们为了简洁好看,将这个表达式用花括号{}包装起来,然后给它起个名字,加个参数,它就变成了一个方法了,然后把这个方法放在computed:{ }里面,然后在标签里面如何调用这个非常复杂的计算表达式呢?那就是在双花括号里面写入方法的名字{{方法名}}。
下面这个例子计算:总价=单价×数量×折扣
<template>
<div id="app">
<h1>{{message}}</h1>
<p>单价{{price}}</p>
<p>数量
<button @click="sub">-</button>
<span>{{quatity}}</span>
<button @click="add">+</button>
</p>
<p> 折扣 {{discount}}</p>
<h2>总价:{{totalPrice}}</h2>
</div>
</template>
<script>
export default{
data(){
return{
message:"Hello world",
price:99,
quatity:0,
discount:0.5
}
},
computed:{
totalPrice(){
return this.price*this.quatity*this.discount
}
},
methods:{
sub(){
this.quatity--
},
add(){
this.quatity++
}
}
}
</script>
<style>
</style>
监听器:watch
监听器就是监听一个值,然后把这个值变化之后的值传到一个方法里面,方法的名字就是这个值的名字,方法的内容往往是对这个值变化之后的值进行一系列的计算。
例如:监听器要监听value的值,然后就把val变化之后的值传到value(val){ },其中val是value变化后的值。
<template>
<div id="app">
<h1>{{message}}</h1>
<p>单价{{price}}</p>
<p>数量
<button @click="sub">-</button>
<span>{{quatity}}</span>
<button @click="add">+</button>
</p>
<p> 折扣 {{discount}}</p>
<h2>总价:{{totalPrice}}</h2>
</div>
</template>
<script>
export default{
data(){
return{
message:"Hello world",
price:99,
quatity:0,
discount:0.5,
totalPrice:0
}
},
// computed:{
// totalPrice(){
// return this.price*this.quatity*this.discount
// }
// },
watch:{
quatity(qu){
this.totalPrice = this.price * qu * this.discount
}
},
methods:{
sub(){
this.quatity--
},
add(){
this.quatity++
}
}
}
</script>
<style>
</style>
计算属性与监听器的区别
多个值改变,为了得到一个值的结果,一般用计算属性(computed)
一个值的改变,会引起多个值的改变,一般用监听器(watch)
实际开发中,大部分时候用computed属性,性能也比较好
但是在用到路由的时候,只能用监听器。