Vue

因为之前曾接触过小程序的开发,所以 入手 Vue 相对爽了一些,因为思想都是数据绑定,Vue是华人尤雨溪开发的,这可是个大佬,B站上有个关于大佬的纪律片,是 Up主:鱼C-小甲鱼 翻译的,有兴趣的话可以看看。

https://www.bilibili.com/video/BV1dT4y1V7Dn


M V VM

  • 他是一种架构方式、开发思想
  • M:Model 数据模型 (操作数据的类)
  • V:View 视图界面 (HTML)
  • VM:ViewModel 视图模型

Vue 的思想

Vue是比较重要的是用数据来驱动页面,下面是一个简单的用Vue实现的页面

1
2
3
4
5
6
7
8
9
10
11
12
<body>
<div id="app">{{message}}</div>
</body>
<script src="js/vue.js"></script>
<script>
var app = new Vue({
el:"#app",
data:{
message:"hello world"
}
})
</script>

生命周期函数

需要记得有

  • beforeCreate
  • create
  • beforeMount
  • mounted
  • beforeUpdata
  • updated
  • beforeDestroy
  • destroyed

其实有规律可循,创造-挂载-更新-销毁
在这里插入图片描述


Vue实例中的选项

el

  • 获取元素,你可以用#id.类名,类似JQuery的选择器等等,也可以用document.querySelectAll()之类的

    data

  • 实例中的数据都放在这里

    computed 计算属性

  • 计算属性的结果是放在缓存里的

常见API的使用

v-if 条件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<body>
<!-- 条件语句必须if/else紧跟着 -->
<div id="app">
{{message}}
<div v-if="message == 'hello world'">if</div>
<div v-else-if="message == 'hello world1'">else-if</div>
<div v-else>else</div>
</div>
</body>
<script src="js/vue.js"></script>
<script>
var app = new Vue({
el:"#app",
data:{
message:"hello world"
}
})
</script>

v-show

  • v-show不能搭配template ,即使条件不对也会显示到页面上
    1
    2
    3
    <template v-show="1 == 2">
    <h1>我是个标题</h1>
    </template>
  • 如果v-show里面的条件不对的话,页面渲染的时候会使用display:none把他隐藏掉
    1
    2
    3
    <div v-show="1 == 2">
    <h1>我是个标题</h1>
    </div>

    v-for 循环

  • 比方说我要循环一个名字叫li的数组
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<body>
<div id="app">
<!-- 这里也可以写成 (each,index) of li,借用了ES6的语法-->
<div v-for="(each,index) in li">
each:{{each}}-----index:{{index}}
</div>
</div>
</body>
<script src="../js/vue.js"></script>
<script>
var app = new Vue({
el:"#app",
data:{
li:['one','two','three','four','five']
}
})
</script>

在这里插入图片描述


v-key**

参考博文1
参考博文2

  • 虚拟DOM的Diff算法,其核心是基于两个简单的假设:1. 两个相同的组件产生类似的DOM结构,不同的组件产生不同的DOM结构。2. 同一层级的一组节点,他们可以通过唯一的id进行区分。
  • 总结一句话:为了更好的让Vue更新虚拟Dom,key来给每个节点做一个唯一标识。
  • 影响:如果不加上标识的话,在插入的时候会发生错误
    在这里插入图片描述

在这里插入图片描述

v-cloak 取消抖动

用Vue渲染页面的时候,你刷新页面,会有页面没有加载数据就渲染出来的情况,一闪而过(尝试截取动图但是截不到),但是为了美,这种缺点怎么可能忍呢!!!

只需两步轻松解决

  • 在标签里写上 v-cloak
  • 在样式里写`[v-cloak]{
        display: none;
    }`

举个小例子

1
2
3
4
5
6
7
8
<style>
[v-cloak]{
display: none;
}
</style>
<body>
<div id="app" v-cloak></div>
</body>

v-once

首次渲染之后,页面不再随着数据的变化而变化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<body>
<div id="app">
<div v-once>
<!--可以在控制台上改变数值 app.message = 'nihao' -->
{{message}}
</div>
</div>
</body>
<script src="js/vue.js"></script>
<script>
var app = new Vue({
el:"#app",
data:{
message:"hello world"
}
})
</script>

v-bind 绑定属性 **

v-bind可以简略写成:,v-bind:后面接标签的属性,用来绑定标签的属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<body>
<div id="app">
<!-- 下面两个div的绑定类是一样的,都是name1 -->
<div v-bind:class="className"> v-bind:class="className"</div>
<div :class="className">:class</div>
</div>
</body>
<script src="js/vue.js"></script>
<script>
var app = new Vue({
el:"#app",
data:{
className:"name1"
}
})
</script>

在这里插入图片描述


v-on 绑定事件

  • v-on 也可以写成 @
  • 可以传递参数,如果向添加事件因子,可以@click="函数名($event)这样传递
1
2
3
4
<!-- 下面两个div都绑定了click点击事件,效果一样 -->
<div v-on:click=""></div>
<div @click=""></div>
<button @click="hand2(10,$event)">btn2---$event</button>

修饰符

.这个点就是修饰符,能修饰什么呢?下面就是例子

冒泡与捕获

  • 阻止冒泡:.stop
1
2
3
4
<div @click="fa">fa
<div @click.stop="son">son</div>
<!-- 这样,点击son,就不会出发fa的点击事件了 -->
</div>
  • 捕获:.capture
    1
    2
    3
    4
    <div @click.capture="fa">fa
    <div @click="son">son</div>
    </div>
    <!-- 这样,点击son,就会先触发fa,然后son -->

按键触发事件

  • 37是键盘上的左方向键
  • 如果向要组合按键,触发连招的话,可以连着修饰符写,如下
    1
    2
    3
    4
    <input type="text" @keydown.37="key1Handler">
    <input type="text" @keydown.space="key1Handler1" placeholder="space">
    <!-- 组合按键 -->
    <input type="text" @keydown.shift.83="key1Handler2" placeholder="shift+S">

v-html 编译 & v-pre 跳过

注意 v-html 的一些细节

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<body>
<div id="app">
<span v-html="tar">span1</span>
<span v-pre="tar1">span2</span>
</div>
</body>
<script src="js/vue.js"></script>
<script>
// v-html 转化成html
// 注意转化后的页面格式,是插入此标签内了,标签内原有的内容将被覆盖

// v-pre 跳过这个元素和它的子元素的编译过程。
// 可以用来显示原始 Mustache 标签。跳过大量没有指令的节点会加快编译。
var app = new Vue({
el:'#app',
data:{
tar:"<a href='#'>11</a>",
tar1:"<a href='#'>11</a>"
}
})
</script>

图片展示在这里插入图片描述


v-model 绑定输入

  • v-model可以绑定inputtextarea的文本
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    <body>
    <div id="app" v-cloak>
    <input type="text" v-model="message">
    {{message}}
    <textarea rows="" cols="" v-model="message">

    </textarea>
    </div>
    </body>
    <script src="../js/vue.js"></script>
    <script>
    var app = new Vue({
    el:"#app",
    data:{
    message:"hello world"
    }
    })
    </script>
  • input失焦时在改变data,把输入框中的值变为number、去掉输入框中值的首尾空格
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<body>
<div id="app">
<!-- .lazy 是change事件,当失去焦点的时候会改变 -->
<input type="text" v-model.lazy="mes">

<!-- .number 把输入数据变成类型为number -->
<input type="text" v-model.number="mes">

<!-- .trim 去首和尾空格 -->
<input type="text" v-model.trim="mes">

<h1>{{mes}}</h1>
</div>
</body>
<script src="../js/vue.js"></script>
<script>
var app = new Vue({
el:"#app",
data:{
mes:"hello world"
}
})
</script>

单选框、复选框、下拉框

  • 都是通过v-model来分组,相当于校服,不同学校相当于不同的组。校服一样说明是一个学校的
  • 单选框
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    <body>
    <div id="app">
    <!-- 单选:多个互斥 -->
    <input type="radio" v-model="pick" value="male"> <label></label>
    <input type="radio" v-model="pick" value="female"> <label></label>
    <input type="radio" v-model="pick" value="???"> <label>男女男</label>
    </div>
    </body>
    <script src="../js/vue.js"></script>
    <script>
    // 单选框
    // 页面上选择哪一项,pick的值就是哪一个value值
    var app = new Vue({
    el:"#app",
    data:{
    pick:"male"
    }
    })
    </script>
  • 多选框
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<body>
<div id="app">
<!-- 复选框多选 -->
<input type="checkbox" v-model="check" value="checkBox1"> <label>复选框1</label>
<input type="checkbox" v-model="check" value="checkBox2"> <label>复选框2</label>
</div>
</body>
<script src="../js/vue.js"></script>
<script>
var app = new Vue({
el:"#app",
data:{
// check里存储谁被选中了
check:[]
}
})
</script>
  • 下拉框
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<body>
<div id="app">
<!-- select如果想添加多个属性,加multiple -->
<select v-model="select">
<!-- 默认是个option中的文本进行匹配
如果用了value的话优先value
-->
<option>1</option>
<option>2</option>
<option value='你好'>3</option>
</select>
</div>
</body>
<script src="../js/vue.js"></script>
<script>
var app = new Vue({
el:"#app",
data:{
select:"你好"
}
})
</script>

组件

组件命名要求

  • 短横线分隔命名

  • 首字母大写命名

    全局/局部组件定义

    顾名思义,组件的使用范围不一样

  • 需要注意的是,全局组件要在实例化对象之前就要注册,说人话,就是
    在这里插入图片描述

  • 为什么要先注册在实例化呢?反过来行不行呢?我反过来会报如下的错误

    1
    2
    3
    [Vue warn]: Unknown custom element: <card> -
    did you register the component correctly?
    For recursive components, make sure to provide the "name" option.

    报错信息提示说:不知道的元素card,是否正确注册组件?,看一下生命周期那张流程图:创建完Vue实例之后就已经渲染完template了,这时候在注册带有template的组件的话,Vue是不认识的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<body>
<div id="app">
{{message}}
<else-name></else-name>
</div>
</body>
<script src="../js/vue.js"></script>
<script>
// 全局组件:
// else-name 是我自定义的标签名称
// 命名规范:不能用驼峰命名,推荐都用小写
Vue.component('else-name',{
template:'<div>这是组件中</div>'
})
</script>

局部的组件,这里写了两种写法

  • 直接把模板放到组件里
  • 把模板放到外边:方便修改
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 局部:只在当前vue中有效
var child = {
template:'<div>这是组件局部,内部中2</div>'
}
var app = new Vue({
el:"#app",
data:{
message:'hello world'
},
components:{
'demo1':{
template:'<div>这是组件局部,内部中1</div>'
},
'demo2':child
}
})

组件中的模板

  • 模板里只能由只能有一个根元素,不能有两个以上的根元素

意思是说可以这样:<div> <div></div> </div> 不可以<div></div> <div></div>
前者两个div是父子关系,后者两个div是同级的哥们关系

组件中的变量

组件中的变量可以这样定义和使用

1
2
3
4
5
6
7
8
9
Vue.component('else-name',{
template:'<div>这是组件全局-外部中{{mess1}} {{mess2}}</div>',
data:function(){
return {
mess1:'<<这是组件中的data1>>',
mess2:'<<这是组件中的data2>>'
}
}
})

组件的通信

  • 先来个简单的,Vue的实例对象和组件的通信
    在这里插入图片描述

  • 假如我设Vue实例是爸爸,实例中的组件是儿子,儿子里又有个组件叫孙子,那么,爸爸和孙子之间怎么交流?

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
<body>
<div id="app">
<son :msgson=msgfa></son>
</div>
</body>
<script src="../js/vue.js"></script>
<script>
// props单项传递,父向子传
var app = new Vue({
el: '#app',
data: {
msgfa: '我是你爸爸'
},
components:{
'son': {
props:["msgson"],
template:`<div>
介绍:{{msgson}}
<grandson :msggrandson='sonData'></grandson>
</div>`,
components:{
'grandson':{
props:['msggrandson'],
template:`<div>{{msggrandson}}</div>`
}
},
data:function(){
return {
sonData:'儿子'
}
}
}
}
})
</script>
  • 儿子如何向爸爸传递信息呢,也就是子向父传递,这时候用到了自定义函数
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
<body>
<div id="app">
<show @myevent="showVal"></show>
</div>
</body>
<script src="../js/vue.js"></script>
<script>
// 子向父亲 $emit()触发事件,只是为了传值 v-on
Vue.component('show', {
data: function(){
return {
str: '我是组件中的string'
}
},
methods: {
sendMsgToFa: function(){
// 这里的this指向组件
// 自定义事件
this.$emit('myevent',this.str);
}
},
template: `<button @click="sendMsgToFa">点击弹出组件传递的数据</button>`
})
var app = new Vue({
el:"#app",
data:{
message:"hello world"
},
methods:{
showVal: function(a){
alert(a)
}
}
})
</script>
  • 如果我的孙子向给儿子传递信息呢?(就是组件中的组件组件 传递信息)
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
<body>
<div id="app">
我有三个阶级关系,分别是父亲--儿子--孙子
<son :msgson=msgfa></son>

</div>
</body>
<script src="../js/vue.js"></script>
<script>
// props单项传递,父向子传
var app = new Vue({
el: '#app',
data: {
msgfa: '我是你爸爸'
},
components:{
'son': {
props:["msgson"],
template:`<div>
介绍:{{msgson}}
<grandson :msggrandson='sonData' @myevent='showVal'></grandson>

</div>`,
components:{
'grandson':{
props:['msggrandson'],
template:`<div>
{{msggrandson}}
<button @click="sendMsgToFa">孙子给儿子传值</button>
</div>`,
methods:{
sendMsgToFa: function(){
// 传值不是自发的,而是事件驱动的,所以需要个绑定事件
// 自定义事件确定了,父亲的接收函数名,和想要传递给父亲的数据,
this.$emit('myevent','我是孙子里面的消息')
}
}
}
},
data:function(){
return {
sonData:'儿子'
}
},
methods:{
showVal: function(val){
console.log(val);
}
}
}
}
})
</script>

中央事件总线bus:数据的中介

如果我现在还是有三层关系,分别是父亲,儿子,和孙子,父亲是Vue实例,儿子是实例中的组件,孙子是组件中的组件
如果这三个人,两两之间可以互相通信,那么就不向上面那样麻烦了,如何做到呢?

我们需要借助一个中介:中央事件总线
在这里插入图片描述

父链/子链

  • 子组件可以通过父链拿到父组件的所有,也可以修改父组件的数据
  • 取父组件中的数据可以,但是如果是修改的话,那么父子组件的关系就会更紧,这是解耦合的操作,不太推荐(组件之间的关系应该尽量独立,不要太过亲昵)在这里插入图片描述

    给组件起名字,并通过名字获得组件

  • 给组件起名字 ref
  • 取值的话通过this.$refs.组件名字
    在这里插入图片描述

组件的插槽

  • 插槽的作用在于内容的分发
    在这里插入图片描述
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
<body>
<!-- 编译的作用域 -->
<!-- 父组件的模板的内容实在父组件的作用域编译 -->
<!-- 插槽 -->
<div id="app">
<comp>
<p>父亲模板的内容</p>
</comp>
</div>
</body>
<script src="../js/vue.js"></script>
<script>
// slot是插槽
Vue.component('comp',{
template:`<div>
<slot>如果父模板没有内容,就显示我</slot>
</div>`
})
var app = new Vue({
el:"#app",
data:{
message:"hello world"
}
})
</script>
  • 指定插槽的内容分发,我们可以给每个插槽起个名字
    在这里插入图片描述
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
<body>
<!-- 插槽: 具名的插槽分发-->
<div id="app">
<comp>
<!-- 注意这里不是v-slot -->
<p slot="top">top</p>
<p slot="foot">foot</p>
<p>我没有名字1</p>
<p>我没有名字2</p>
</comp>
</div>
</body>
<script src="../js/vue.js"></script>
<script>
// slot是插槽
Vue.component('comp',{
template:`<div>
<slot name="top"></slot>
<slot name="foot"></slot>
<div>
<slot></slot>
</div>
</div>`
})
var app = new Vue({
el:"#app",
data:{
message:"hello world"
}
})
</script>

动态切换组件

  • component 标签上有个属性is,里面填写组件的名字
    在这里插入图片描述

路由

简单路由

在这里插入图片描述

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
<script src="./node_modules/vue/dist/vue.js"></script>
<script src="./node_modules/vue-router/dist/vue-router.js"></script>
<body>
<div id="app">
<router-link to="/shangcheng">商城</router-link>
<router-link to="/shuma">数码</router-link>
<router-link to="/jiaju">家具</router-link>
<router-view></router-view>
</div>
</body>
<script>
var sc = { template:`<h1>商城组件</h1>` }
var sm = { template:`<h1>数码组件</h1>` }
var jj = { template:`<h1>家具组件</h1>` }
var router = new VueRouter({
routes:[
// redirect 重定向
{ path: '/',redirect: '/shuma' },
{ path: '/shangcheng',component: sc},
{ path: '/shuma',component: sm},
{ path: '/jiaju',component: jj}
]
})
var app = new Vue({
el:"#app",
router,
})
</script>

动态路由

  • 动态路由可以
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    <body>
    <div id="app">
    <router-link to="hello/65">点击</router-link>
    <router-view></router-view>
    </div>
    </body>
    <script>
    // 动态路由
    var Hello = {
    template: `<h1>你好 {{$route.params.id}} </h1>`
    }
    var router = new VueRouter({
    routes:[
    { path:'/hello/:id', component: Hello }
    ]
    })
    var app = new Vue({
    el:"#app",
    router
    })
    </script>

    路由前进/后退

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
<body>
<div id="app">
<router-link to="hello">点击</router-link>
<router-view></router-view>
<button v-on:click="back">后退</button>
<button v-on:click="go">前进</button>
</div>
</body>

<script>
// 组件
var Hello = {
template: `<h1>名字{{name}},年龄{{age}} </h1>`,
props:['name', 'age']
}
// 路由传参
var router = new VueRouter({
routes:[
{
path: '/hello',
component: Hello ,
props: { name:'张三', age:11 }
}
]
})
var app = new Vue({
el:"#app",
router,
methods:{
// 路由的前进和后退
go(){
this.$router.go(1)
},
back(){
this.$router.go(-1)
}
}
})
</script>

监听路由

  • 我想点击登录的时候在右上角更改信息,就是把原先的登录注册按钮改成用户名和注销
  • 然而我的登录写成了组件,在组件中更改App.vue的一个data信息,奈何学术不精,百度了好久,想到通过监听路由来出发事件
  • 主要的是watch()这个API,只要路由变化就会触发,但是浏览器的后退是不会触发的
    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
    export default{
    data:function(){
    return{
    isLogin: false,
    userName:''
    }
    },
    methods:{
    zhuxiao(){
    window.sessionStorage.setItem('isLogin', false)
    this.isLogin = false
    this.$router.push({
    path: `/login`
    })
    }
    },
    watch:{
    $route(to){
    if(to.path === '/view'){
    this.isLogin = true
    this.userName = window.sessionStorage.getItem('userName')
    }
    }
    }
    }