vue 📑

创建实例

  1. new vue对象
  2. v-on:click = @click
  3. v-bind:href = :href
  4. v-html v-text vue

计算属性和侦听器:

data里命名与methods里命名不应该一样 vue

dom里渲染了data里的数据,methods里的方法就会执行 vue

watch监听data里数据就可以避免像methods里方法被迫执行 vue

computed计算属性,避免watch里的代码冗余 vue

computed里可以写方法,也可以写对象,方法名或者对象名为变量名,对象里有两个方法get()和set(),get方法与直接执行computed里的方法一样效果,set方法是当外部改变变量值时执行,set方法()里的参数是value,value值就是外部改变的值,可以通过操作这个值进行一些功能设置 vue

class与style绑定:

使用 : 绑定----class与style后面可以接{}或者[] class-对象写法
vue

class-数组写法
vue

style-绑定用法
vue

style-绑定对象
vue

style-绑定数组
vue

##条件渲染 v-if和v-show都是控制元素的显示与隐藏;v-if不频繁操作dom可以用,需要频繁操作的时候用v-show
vue

v-if和v-else配合使用的时候,中间不能有其他的元素
vue
vue

表单重新渲染时,如果元素一样,之前填写的也会渲染过来,想要不渲染过来,可以加key值区分开
vue

列表渲染:

  1. v-for数组和对象更改原有数据时,可以动态更新视图。
  2. 无用标签可以用template代替。制作模板,代替无用的DIV标签时用它
  3. *《这里写的数据是数组》*通过vm.newsList[新增的index] = {新的对象} 这样的方法增加的数据不会动态更新视图(即浏览器动态更新),要想动态更新视图就要用v-for里的方法,例如vm.newsList.push({新的对象}),这些方法包括:push() pop() shift() unshift() splice() sort() reverse()
  4. *《这里写的数据是数组》*动态更新视图还可以用vue上的方法:一个是set方法--> vue. set(vue.newsList,7,{ })第一个参数是哪个数据,第二个参数是索引,第三个参数是变化的值可以是对象。vue.set()和vm. set()是一样的效果。
  5. *《这里写的数据是数组》*动态更新视图还可以用替换的方法: vue.newsList = [{一个新的数据对象}] ---- 对象也可以用类似的方法,但是这样的方法太暴力了 一般不会用 vue

《这里写的数据是对象》 vue

*《这里写的数据是对象》*动态更新视图还可以用vue上的方法:一个是set方法-----> Vue. set(vm.userInfo, 'age', 18)

事件处理和表单绑定:

事件处理
表单绑定

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>表单输入绑定</title>
    <script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script>
    <style>
    * {
        list-style-type: lower-alpha;
    }
    span {
        color: red;
        font-size: 12px;
    }
    </style>
</head>
<body>
    <div id="app">
        <div>
            <!--你可以用 v-model 指令在表单 <input>、<textarea> 及 <select> 元素上创建双向数据绑定。它会根据控件类型自动选取正确的方法来更新元素。-->
            <!--<input type="text" v-model="inputVal" @input="addTask">  <!--(1)这种方式输入的立即显示--> 
            <!--<input type="text" v-model.lazy="inputVal"> <!--(2)这种方式不能在输入后清除输入框数据,绑定.lazy后双向数据绑定失效-->
            <!--<input type="text" v-model.trim="inputVal" @change="addTask"><!--(3)-->
            <input type="text" v-model.trim="inputVal" @keyup.13="addTask">

            <button @click="addTask">提交</button>
            <span>{{propmt}}</span>            
            <ul>
                <li v-for="(item, index) in todoList" :key="item">
                        {{item}}
                    <button @click="compeletTask(index)">X</button>                   
                </li>
            </ul>
        </div>
        <div>
            <h4>已完成任务</h4>
            <ul>
                <li v-for="(item, index) in completeList" :key="item">
                        {{item}}
                </li>
            </ul>
        </div>

    <script>
        const vm = new Vue({
            el: '#app',
            data: {
                propmt: '',
                todoList: [],
                completeList: [],
                inputVal: '',
            },
            methods: {
                addTask () {
                    if(this.inputVal == '') {
                        this.propmt = "不能输入一个空任务";                        
                    }else if(this.todoList.includes(this.inputVal)) {//数组方法.includes()数组里是否还有***
                        this.propmt = "不能输入相同任务";                        
                    }else {
                    this.todoList.push(this.inputVal);
                    this.inputVal = '';//(3)@change时,一直走这里,就会触发第一个if
                    }
                },
                compeletTask (index) {
                    const task = this.todoList.splice(index,1);
                    this.completeList.push(...task);
                }
            },
            // watch: {//(2)
            //     inputVal () {
            //         this.todoList.push(this.inputVal);
            //         this.inputVal = 'sadf';
            //     }
            // },
        })  
    </script>   
    </div>
</body>
</html>
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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81

组件基础&父子组件传值:

全局组件

调用Vue.component()注册组件时,组件的注册是全局的,这意味着该组件可以在任意Vue示例下使用。

vue

局部组件

如果不需要全局注册,或者是让组件使用在其它组件内,可以用选项对象的components属性实现局部注册。

vue vue

具体实例

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <style>
        * {
            margin: 0;
            padding: 0;
            list-style: none;
        }

        #app {
            display: inline-block;
            margin: 20px;
            border: 1px solid #000;
            padding: 20px;
            padding-bottom: 0;
            font-size: 14px;
            cursor: pointer;
        }

        .course {
            display: flex;
            margin-bottom: 15px;
        }

        .img-box {
            position: relative;
            margin-right: 20px;

        }

        .course-img {
            width: 140px;
            height: 80px;
            vertical-align: bottom;
        }

        .course span {
            box-sizing: border-box;
            position: absolute;
            bottom: 0;
            display: block;
            width: 100%;
            line-height: 20px;
            padding-left: 10px;
            color: #fff;
            font-size: 10px;
            background-color: rgba(0, 0, 0, .6);
        }

        .title {
            width: 250px;
            box-sizing: border-box;
            text-overflow: ellipsis;
            white-space: nowrap;
            overflow: hidden;
        }

        .free {
            color: #5fb41b;
        }

        .play-back {
            display: inline-block;
            padding: 0 3px;
            font-size: 12px;
            border: 1px solid #73b3e7;
            color: #73b3e7;
        }

        .price {
            color: #e85308;
        }

        .choose {
            opacity: .3;
        }
    </style>
</head>

<body>
    <div id="app">
        <!-- 监测price变化 -->
        <course-list :list="courseList" @price="getPrice"></course-list>
        合计:{{ price }}
    </div>


    <script>
        //全局组件
        // Vue.component('courseList',{
        //     template:'<div>dsds</div>'
        // });
        //局部组件
        const courseList = {
            // props父组件传来的值进行规定  只有有props才能使用父组件的值
            props: { //只有注册一个名,例如list才能传值
                list: {
                    type: Array,
                    default: [], //默认值   如果父组件没有传值 就用默认值
                    required: true, //true表示父组件的值必须传过来,不传就报警告
                    // validator(value) { //对父组件传来的值进行约束,value是传过来的值
                    //     return true//必须要有return,
                    //     // return value.length < 5
                    // }
                },
            },
            //组件里的参数声明要放到data函数里的返回对象里,如果像VUE声明里直接放到data对象里有可能会数据冲突
            data() {
                return {                    
                    totalPriceArr: [],
                    alreadyHasArr: [],
                    totalPrice: 0,
                }
            },
            // 创建一个模板 @contextmenu.prevent="removeFromBuy(index, item.price)"//监测右键事件,.prevent取消默认事件
            template: `<ul>                                                       
                        <li v-for="(item,index) in list"
                            :key="item.name"
                            @click="addTobuy(index,item.price)"
                            @contextmenu.prevent="removeFromBuy(index, item.price)"
                            :class="{course:true,choose: alreadyHasArr[index]}">
                                <div class="img-box">
                                    <img class="course-img" :src="item.poster" alt="">
                                    <span v-if="item.enrollment">{{item.enrollment}}人报名</span>
                                    <span v-if="item.buyNumber">{{item.buyNumber}}人购买</span>
                                </div>
                                <div class="content-box">
                                    <div class="title">爱上了的经历</div>
                                    <div class="free" v-if="item.free">免费</div>
                                    <div class="play-back" v-if="item.playback">回放</div>
                                    <div class="price" v-if="item.price">¥{{item.price}}</div> 
                                </div>
                        </li>
                    </ul>`,
            methods: {//组件写方法,也写到这里
                addTobuy(index, price) {
                    if (this.alreadyHasArr[index]) {
                        return
                    }
                    this.totalPriceArr.push(price);
                    this.$set(this.alreadyHasArr, index, true); //将数组的index位改为true
                    // this.alreadyHasArr.splice(index,1,true);使用这个方法只能再最后一位添加true,存在bug,只能用set方法
                    this.getTotalPrice();

                },
                removeFromBuy(index, price) {
                    if (!this.totalPriceArr) {
                        return
                    };
                    //找到移除商品的价格
                    const totalPriceIndex = this.totalPriceArr.findIndex(item => {
                        return item == price
                    });
                    this.alreadyHasArr.splice(index, 1, false);
                    this.totalPriceArr.splice(totalPriceIndex, 1);//从总价数组里删除,移除的价格
                    this.getTotalPrice();//重新计算总价
                },
                getTotalPrice() {
                    if (this.totalPriceArr.length === 0) {
                        this.totalPrice = 0;
                    } else {
                        this.totalPrice = this.totalPriceArr.reduce((prev, cur) => {//计算前后传入值
                            return prev + cur;
                        });
                    }
                    this.$emit('price', this.totalPrice)//向父组件传值,price需在父组件声明
                },
            },
        };
        //vm是courseList的父组件
        const vm = new Vue({
            el: '#app',
            components: { //注册组件
                // course: courseList,
                courseList
            },
            data: {
                price: 0,
                courseList: [{ //给子组件传值命名与子组件命名必须一样
                        name: 'Web前端开发之JavaScript(Js)精英课堂【渡一教育】',
                        free: true,
                        playback: false,
                        price: 0,
                        buyNumber: 0,
                        enrollment: 4942,
                        poster: 'https://10.url.cn/qqcourse_logo_ng/ajNVdqHZLLBsSud06XlZnJACsTWd7OSA5phIFPRTNibFBeuT8jRjy00Jb5ticSEKYUnpUYdBCicH2U/356'
                    },
                    {
                        name: 'Web前端开发之HTML+CSS精英课堂【渡一教育】',
                        free: true,
                        playback: false,
                        price: 0,
                        buyNumber: 0,
                        enrollment: 2982,
                        poster: 'https://10.url.cn/qqcourse_logo_ng/ajNVdqHZLLBDFLdArBwf70PjMrL6bq0OI9LesEsskk1iamJKibriaic0QkBZhINoDuN0DicaojkqyQjk/356'
                    },
                    {
                        name: '淘宝商城项目实战开发【渡一教育】',
                        free: true,
                        playback: false,
                        price: 0,
                        buyNumber: 0,
                        enrollment: 1742,
                        poster: 'https://10.url.cn/qqcourse_logo_ng/ajNVdqHZLLDBpXKL1sgAOKcbSz0Od2abiaqdJibiaDDQWfmrVNBCFrIpbTQKlNyWbPg5Uo1dnT7Znk/356'
                    },
                    {
                        name: '零基础WEB前端入门',
                        free: false,
                        playback: true,
                        price: 9.9,
                        buyNumber: 2514,
                        enrollment: 0,
                        poster: 'https://10.url.cn/qqcourse_logo_ng/ajNVdqHZLLCRlDgtxVbmyclxP9fDUYmnuqAFbN5mYsncrPrVyQbtA3fNfuZ3qicSe1r7Ppt7tICs/356'
                    },
                    {
                        name: 'React顶级企业实战,全流程制作淘票票WebApp【渡一教育】',
                        free: false,
                        playback: true,
                        price: 399,
                        buyNumber: 670,
                        enrollment: 0,
                        poster: 'https://10.url.cn/qqcourse_logo_ng/ajNVdqHZLLB2CyXIxsb8iaAUibBB9mzlkSgKNU7GiciaYxG1SicoLjkiahZ3ia54eZWbtCGDkFyvMo0uKk/356'
                    },
                    {
                        name: '原生JS贪吃蛇游戏实战开发【渡一教育】',
                        free: true,
                        playback: false,
                        price: 0,
                        buyNumber: 0,
                        enrollment: 1123,
                        poster: 'https://10.url.cn/qqcourse_logo_ng/ajNVdqHZLLCfsOGnxjhUXFA6SHMOu0fMUicuucRlIVNo9zBnnLiacovMnePD6586QlRj2qvwudbn8/356'
                    },
                    {
                        name: '原生JS扫雷游戏实战开发【渡一教育】',
                        free: true,
                        playback: false,
                        price: 0,
                        buyNumber: 0,
                        enrollment: 631,
                        poster: 'https://10.url.cn/qqcourse_logo_ng/ajNVdqHZLLDaTljUSVGk2eqS1MCT3DwDqxgs7KhfMf3411SXQkRvmiaicIuz47ydO27zGPWK97wicw/356'
                    },
                    {
                        name: '你不知道的Javascript【渡一教育】',
                        free: true,
                        playback: false,
                        price: 0,
                        buyNumber: 0,
                        enrollment: 1696,
                        poster: 'https://10.url.cn/qqcourse_logo_ng/ajNVdqHZLLCelo2lyibtrxDia7aq7tC0LE1Lcz2LVoDqhQy5tnOHicTzWhLuvPekL35rEmwTdgNuwI/356'
                    }
                ]
            },
            methods: {
                getPrice(price) {
                    this.price = price;                  
                }
            },
        })
    </script>
</body>
</html>
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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268

单向数据流:

子组件不能随意改父组件传递过来的值,会影响父组件的数据,应该深度克隆后使用
vue

在创建的dom上监听子组件原生dom的事件要用事件修饰符.native
vue

动态组件、插槽:

说明

动态组件标签<component :is="组件名变量" >组件名变量用哪个就显示哪个组件
动态组件标签放到<keep-alive>标签里,防止切换组件后,原组件的数据销毁
在组件模型里添加标签<slot name="name">,插入组件的dom就都会放到这个标签里,name是插槽的名字,用于区分组件里不同插槽
dom结构里的组件,必须写template标签和slot-scope

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

</head>
<body>
    <div id="app">
        <button @click="changeCmp">切换</button>
        <!-- 组件里不安装插槽,写任何东西是不显示的,要往组件标签里写东西就要写插槽 -->
        <cmp-one>
            <span slot="before">绑定具名插槽</span>            
            <!-- <span slot="after">绑定具名插槽</span> -->
        </cmp-one>
        <!-- 组件复用的时候,要组件内引用的不一样,去改组件模板是做不到的,可以改插槽绑定的属性值 -->
        <cmp-two :list="oList">
            <!-- 要使用slot-scope属性时,必须使用template标签 -->
            <template slot-scope="ss">
                <!-- 通过slot-scope="ss"属性绑定的名,引用插槽绑定的数据 -->
                <li>{{ss.index}} - {{ss.item}}</li>
            </template>    
        </cmp-two>
        <!-- 下面复用的组件与上面的组件不一样了 -->
        <cmp-two :list="oList">
                <template slot-scope="bb">                    
                    <li> {{bb.item}}  -  {{bb.index}}</li>
                </template> 
        </cmp-two>

        <!-- component是动态组件标签,通过绑定is属性,控制该标签变为哪个子组件 -->
        <!-- 动态标签切换后销毁原来的组件及数据,keep-alive标签是防止原组件销毁,还是原组件的切换 -->
        <!-- <keep-alive>
            <component :is="type"></component>     
        </keep-alive> -->
        
    </div>
    <script>
    const cmpOne = {
        template:`  <div>
                        <slot name="before"></slot>
                        组件1:<input type="value">
                        <slot name="after"></slot>
                    </div>`
    };
    
    const cmpTwo = {
        props:{
            list: {
                type:Array,
                default: [], 
                required: true,
            }
        },
        data () {//20190202这天在这范了错,子组件data应写成函数形式,里面return出数据
            return {
                oList: this.list.concat([])
            }
        },
        template:`<div>
                    组件2:<input type="value">
                    <ul>
                        <li v-for="(item,index) in oList" 
                              :key="item">
                              <!--插槽标签里不能绑定key值-->
                            <slot :item="item"
                                  :index="index">
                            </slot>
                        </li>
                    </ul>
                </div>`,
        
    };

    const vm = new Vue({
        el: '#app',
        components: {
            cmpOne,
            cmpTwo,
        },
        data: {
            type: 'cmp-one',
            oList: [1,2,3,4,5]
        },
        methods: {
            changeCmp () {
                this.type = this.type == 'cmp-one' ? 'cmp-two' : 'cmp-one'
            }
        },

    });
    
    </script>
</body>
</html>
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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98

el template render 生命周期:

vm.$mount('#app')与el是一样效果
vue

VUE里的template属性
vue

杉杉老师讲的vue生成过程
vue

render优先级高于template高于el
vue

Vue生命周期
vue

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
    <div id="app">
        {{msg}}
        <button @click="change">change</button>
    </div>
    <script>
        const vm = new Vue({
            el: '#app',
            data: {
                msg: 'hello'
            },
            methods: {
                a() {
                    console.log('aa');
                },
                change() {
                    this.msg = "我改变了!"
                }
            },
            beforeCreate() {//执行时,data里的数据找不到,methods里的方法找不到
                console.log('beforeCreate');
                console.log(this.msg);
                console.log(this.a);
                alert('beforeCreate')                
            },
            created() {//执行时,data里的数据可以找到,methods里的方法可以找到
                console.log('created');
                console.log(this.msg);
                this.a();
                alert('created')                
            },
            beforeMount() {//DOM渲染前
                alert('beforeMount')
            },
            mounted() {//DOM渲染后
                alert('mounted')
            },
            beforeUpdate() {//数据改变前执行
                alert('数据还没改变')
            },
            updated() {//数据改变后执行,执行后再渲染页面
                alert('数据改变了')
            },
            beforeDestroy() {//执行vm.$destroy()这个方法后,先执行这个beforeDestroy() 方法,再执行destroyed()
                alert('销毁前')
            },
            destroyed() {//销毁后  这个vue就失效了,所有数据方法都不能再用了
                alert('销毁后')
            },
        })      
    </script>
</body>
</html>
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
54
55
56
57
58
59
60
61
62

通过vue-cli创建项目:

安装

npm install -g @vue/cli,你还可以用这个命令来检查其版本是否正确 (3.x):
vue --version

创建项目

vue create hello-world(这里hello-world是项目文件夹)

预设: vue
vue
vue
vue
vue

生成的文件目录 vue

main.js




 










import Vue from 'vue'
import App from './App.vue'
import router from './router'//引入路由配置文件
import './assets/styles/reset.css'//引入重置css文件,其中.css必须写
import store from './store'//引入数据配置文件

Vue.config.productionTip = false//设置为 false 以阻止 vue 在启动时生成生产提示。
Vue.prototype.bus = new Vue()//在vue原型上声明一个方法,所有组件都可以用,取到原型上的数据
new Vue({
  router,//router路由声明
  store,
  render: h => h(App)//通过vue-loader将APP.vue解析为对象,再通过render函数挂载到页面上
}).$mount('#app')//挂载到index.html上
1
2
3
4
5
6
7
8
9
10
11
12
13

App.vue




 




























//组件文件结构,template是DOM结构,script是vue的使用,style是本组件的样式
<template>
<!--只能挂载一个根节点<div id="app">-->
  <div id="app">
    <img alt="Vue logo" src="./assets/logo.png">
    <HelloWorld msg="Welcome to Your Vue.js App"/>
  </div>
</template>

<script>
import HelloWorld from './components/HelloWorld.vue'//引入子组件

export default {
  //这里的写法个VUE里的写法一样,可以写data、methods等
  name: 'app',
  components: {
    HelloWorld//声明子组件
  }
}
</script>

<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>
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

⑦插播----独立查看某个组件的视图方法 vue

⑧利用vue ui命令进入图形化界面进行创建vue

初识路由:

  1. 什么是路由,路由就是用来实现网页间的跳转
  2. 安装路由:vue add router安装命令
  3. 配置路由:
    写路由配置文件router.js



 


























import Vue from 'vue'  //引入vue
import Router from 'vue-router'  //引入路由
import Home from './views/Home.vue'  //引入路由组件
import About from './views/About.vue'
//所有用到路由都要再这里引用

Vue.use(Router) //使用路由
//配置路由
const router =  new Router({
  mode: 'history',//是路由的模式配置,去掉url上的/#
  base: process.env.BASE_URL, //不知道
  linkActiveClass: 'active',
  linkExactActiveClass: 'exact',
  routes: [ //配置路由的名字/路径/引用的组件名
    {
      path: '/home',
      name: 'home',//引用路由时,可以直接引用这个名字就能使用
      components: {//复数形式
        default: Home, //默认组件
        // 'academic': Academic,//复用的组件
      }
    },
    {
      path: '/about',
      name: 'about',
      component: About
    },    
})
export default router;//导出配置
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

main.js引入路由

import Vue from 'vue'
import App from './App.vue'
import router from './router'//引入路由配置文件

Vue.config.productionTip = false//设置为 false 以阻止 vue 在启动时生成生产提示。
new Vue({
  router,//router路由声明  
  render: h => h(App)//通过vue-loader将APP.vue解析为对象,再通过render函数挂载到页面上
}).$mount('#app')//挂载到index.html上
1
2
3
4
5
6
7
8
9

组件内引入路由

<template>
<div>    //router-link相当于a标签,但是它有缓存的作用
    <router-link :to="{name: 'changeCourse'}"//绑定跳转路径
                 tag="button"//绑定显示标签
                 class="change-course-btn"
    >
        修改课程
    </router-link>
<router-view class="router-view"/>//使用这个标签使路由显示再组件视图里
</div>
</template>

<script>
export default {
    components: {        
    }    
}
</script>

<style scoped>
.change-course-btn {
    display: block;
    margin: 20px auto;
    width: 200px;
    line-height: 40px;
    background-color: #000;
    color: #fff;
}
</style>
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

嵌套路由

设置组件格式

ctrl+shift+p 搜configure ---->找代码片段 ---->找vue.jion---->设置如下---->设置后在组件路由里敲vue+Tab键就可以生成固定格式

"Print to console": {
        "prefix": "vue:5",
        "body": [
        "<template>",
        "",
        "",
        "</template>",
        "",
        "<script>",
        "export default {",
        "",
        "}",
        "</script>",
        "",
        "<style scoped>",
        "",
        "</style>"
        ],
        "description": "Log output to console"
    }

根据需求写路由配置文件router.js,具体写法见上文。
将路由引入主页面即App.vue,

<template>
  <div id="app">
    <div class="header">
      <div class="center">
        <div class="logo">
          <img src="./assets/images/logo.jpg" alt>
        </div>
        <ul class="nav">
          <router-link tag="li" to="/home">首页</router-link><!-- router-link标签用来引入配置的路由,tag改该标签的dom元素 to绑定路径-->
          <router-link tag="li" to="/learn">课程学习</router-link>
          <router-link tag="li" to="/student">学员展示</router-link>
          <router-link tag="li" to="/community">社区</router-link>
          <router-link tag="li" to="/about">关于</router-link>
        </ul>
      </div>
    </div>
    <router-view class="router-view"/>
    <!-- <router-view class="router-view" name="academic"/>复用的组件要写name -->

  </div>
</template>

<style>
主页样式
</style>
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

assets里styles下的css是项目整体ccs重置,去网上查标准写法
写嵌套路由(主页面的路由都写在views文件夹里,主页面的路由下嵌套路由放在components文件夹里)
引入嵌套路由 → 配置router. js

import Academic from './components/community/Academic'
import Download from './components/community/Download'
import Personal from './components/community/Personal'
{
      path: '/community',
      name: 'community',
      component: Community,      
      children: [ //子路由要再children数组下配置
        {
          path: '/community/academic',
          name: 'academic',
          component: Academic,          
        },
        {
          path: '/community/download',
          name: 'download',
          component: Download
        },
        {
          path: '/community/personal',
          name: 'personal',
          component: Personal
        },
      ]
    },
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

上面的子路由的路径可以简写

  {
        path: 'academic',//不写/community/,就自动添加父路由路径
        name: 'academic',
        component: Academic,          
        },

在主页面下的某个路由下写router-link和router-view
写主页面下的某个路由样式

<template>
  <div>
    <div class="nav">
      <router-link :to="{name: 'academic'}">学术讨论</router-link>
      <router-link :to="{name: 'download'}">资源下载</router-link>
      <router-link :to="{name: 'personal'}">个人中心</router-link>
    </div>
    <router-view></router-view>
  </div>
</template>

<script>
export default {};
</script>

<style scoped>
/* 子路由样式 */
.nav a {
    display: inline-block;
    padding: 5px 10px;
    margin-right: 20px;
}
.active {
  background-color: black;
  color: aliceblue;
}
</style>
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

写router-link标签活动样式
活动项class名:router-link自带两个class类名router-link-exact-active 和router-link-active,可再router里简写。

linkActiveClass: 'active',//自带的活动项名  在router.js里的Router实例里进行简写
linkExactActiveClass: 'exact',

router-link-exact-active:点击哪个router-link,哪个就有这个类名,整个项目只能有一个router-link可以有这个类名,因此,有嵌套路由的情况不适合使用。
router-link-active:这个活动项类名,是路径下的所有项router-link标签都会赋予这个类名(即如果/home/aa有,/home也有,/也有),因此,在使用这个类名的时候,在router.js里配置路径时应该为不同路由设置不同路径;子路由的样式如果不想继承父路由的样式,在router.js里配置路径时应该在父路由的路径上再写路径(即父路由路径是/community ,子路由路径应该是/community/academic )

动态路由、router-view复用:

  1. 各路由的样式不要互相影响,在路由里的<style>标签里写<style scoped>,这样这个路由的样式只作用到自己身上。在设置组件格式时,直接设置好就行了,以后不用总写。

  2. 动态路由:像新闻列表,每一项点击后都要进入新的页面,因为不能确定有多少新闻,不能固定写好路由,应该使用一个路由,动态的引入。
    router.js里引入动态路由并配置

    import Question from './components/Question.vue'//引入动态路由需要的路由组件

    { path: '/question/:id',//设置:id就表示是动态路径 name: 'question', component: Question }, 在某一个子路由里设置如何跳转动态路由

<template>
    <ul>
        <router-link    tag="li" 
                        v-for="item in questionList" 
                        :to="{name:'question', params:{id: item.questionId}}"
                        :key="item.questionId">
            {{item.title}}
        </router-link>
        <!--:to="{name:'question', params:{id: item.questionId}}   name是去到的页面,params是url拼接的地址-->
    </ul>

</template>

<script>
export default {
   data() {
    return {
      questionList: [
        {
          questionId: 201801,
          title: "到底什么是es6中的class(类)?怎么实现class(类)?"
        },
        {
          questionId: 201802,
          title:
            "什么是es6箭头函数?与普通函数主要区别在哪里?到底该不该使用箭头函数?"
        },
        {
          questionId: 201803,
          title:
            "什么是es6的解构赋值,每次都创建一个对象吗?会加重GC的负担吗?为什么?"
        }
      ]
    };
  }
};
</script>

<style scoped>
ul {
    margin-top: 20px;
}
li {
    line-height: 30px;
}
</style>
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

设置动态路由组件:如果用户输入一个我们没有的ID,就让它跳转到Err路由组件

<template>
  <div>
      问题:{{question}}
  </div>
</template>

<script>
export default {
    created() {//看生命周期
        const questionId = this.$route.params.id;  //取到跳转到该路由的id值   
        //如果上面的id值在这里的数据数组里也有,就拿取出相应的数据的索引   
        const index = this.questionList.findIndex( item => item.questionId == questionId );
        //防止去到未设置的页面出现不好的体验
        if(index == -1) {//如果没有找到索引,就执行
            //跳转到特定页面
            // this.$router.go(-1)//用户体验差,为什么突然跳回去了
            this.$router.replace({name: 'err'})//替换到{name: 'err'}这个地址

            // this.$router.push({name: 'err'})//push进去{name: 'err'}这个地址
        }else {
            //找到索引就把索引下的值取出
            this.question = this.questionList[index].title;
        }
    },
    data() {
        return {
            question: '',
            questionList: [
                {
                questionId: 201801,
                title: "到底什么是es6中的class(类)?怎么实现class(类)?"
                },
                {
                questionId: 201802,
                title:
                    "什么是es6箭头函数?与普通函数主要区别在哪里?到底该不该使用箭头函数?"
                },
                {
                questionId: 201803,
                title:
                    "什么是es6的解构赋值,每次都创建一个对象吗?会加重GC的负担吗?为什么?"
                }
            ]
        };
    }
};
</script>

<style scoped>
</style>
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

④设置路由重定向

{
    path: '/community',
    name: 'community',
    component: Community,
    redirect: '/community/academic',//路径重定向,只要进入/community就进入/community/academic

如果用户输入错误url,应如何跳转,在router.js里设置

{
    path: '*',//无论用户输入什么,按照下面的规则跳转
    redirect(to) {//to是对象,.path的值来判断用户输入URL,从而判断给用户返回哪个网页
        if(to.path == '/') {
        return '/home'
        }else {
        return {name: 'err'}
        }
    }
    }

组件复用
在router.js里的某个组件配置下进行设置(确保复用的组件在router.js已经引入)

{
      path: '/home',
      name: 'home',
      components: {//复数形式
        default: Home, //默认组件
        // 'academic': Academic,//复用的组件
      }
    },
1
2
3
4
5
6
7
8

在要复用的路由组件里设置,复用的组件要写name,name赋值为要引用的组件名

<router-view class="router-view" name="academic"/>

导航守卫

路由别名:alias:‘/Home’就是访问哪个名都可以找到这个网址

{
    path: '/',
    name: 'home',
    alias:'/home',//路由别名,即用户输入/和/home都可以进入到该路由
    components: {//复数形式
        default: Home, //默认组件
        // 'academic': Academic,//复用的组件
    }
    },

全局守卫

每次进入到项目都会询问,下面这段程序写到router .js中的const router = new Router({})外面

router.beforeEach((to, from, next) => {
//to是要去哪   from是从哪来的   next跳转到哪去
if(to.path == '/community/academic') {
    const answer = confirm('你还没有登陆,要登录后才能浏览信息,要登录吗?')
    if(answer) {
    next({name:'personal'})//跳转到登陆页
    }else {
    next(false)//不跳转
    }
}else {
    next()//可以跳转到要去的地方
}
})

路由独享守卫

每次进到该路径才询问,下面这段程序写到router .js中的某个路由配置下

{
        path: 'academic',//不写/community/,就自动添加父路由路径
        name: 'academic',
        component: Academic,
        //路由独享守卫
        // beforeEnter(to, from, next) {
        //   const answer = confirm('你还没有登陆,要登录后才能浏览信息,要登录吗?')
        //     if(answer) {
        //       next({name:'personal'})
        //     }else {
        //       next(false)
        //     }
        // }
        },

组件内守卫

写在组件里

export default {
//组件内守卫,写在组件里
beforeRouteEnter (to, from, next) {//进入该组件前运行
    const isLogin = to.matched[0].meta.login;
    if(isLogin) {
    next();
    return;
    }
    const answer = confirm('你还没有登陆,要登录后才能浏览信息,要登录吗?')
    if(answer) {
    next({name:'personal'})
    }else {
    next(false)
    }
},

元信息及一点小应用 在router.js里的父路径上设置元信息

{
    path: '/community',
    name: 'community',
    component: Community,
    redirect: '/community/academic',//路径重定向,只要进入/community就进入/community/academic
    meta: {//元信息定义在父路由上,子路由都可以通过this.$route.matched[0].meta.login;找到
        login: false,
    },

在子组件里使用元信息

export default {
  //组件内守卫,写在组件里  
  beforeRouteEnter (to, from, next) {//进入该组件前运行  
    // const isLogin = this.$route.matched[0].meta.login;
    //这里没有this值,数据还没有呢!!!
    const isLogin = to.matched[0].meta.login;//在to上能找到元信息  

    if(isLogin) {//如果为真就进入该组件      
      next();
      return;
    }
    const answer = confirm('你还没有登陆,要登录后才能浏览信息,要登录吗?')
    if(answer) {
      next({name:'personal'})
    }else {
      next(false)
    }
  },
  beforeRouteLeave (to, from, next) {//离开该组件前运行
    const answer = confirm('确定要离开吗?')
    if(answer) {
      next();
    }else {
      next(false);
    }
  },
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

路由懒加载

在配置router.js时,为了提高加载速度,采用懒加载的办法,即用到哪个路由再加载哪个。

//路由懒加载,就是需要的时候加载,不打开其它路径就不加载
const Home = () => import ('./views/Home.vue')
const About = () => import ('./views/About.vue')

非父子组件传值:

  1. 第一种方法可以用:子一传父,父传子二。这种办法只适合父子组件较少的情况,如果太多就太麻烦了。(了解即可)
  2. 第二种方法:(事件总线)可以在vue原型上声明一个方法,即再main.js里声明一个vue,所有组件都可以向原型上传数据,所有组件也可以去原型上取数据(路由较少时,可以使用这种方法,但一般都用vuex)


    Vue.prototype.bus = new Vue()//在vue原型上声明一个方法,所有组件都可以用,取到原型上的数据  
1
  1. 第三种方法:利用vuex,vuex像仓库一样,所有用到仓库中的东西,只要仓库内的东西变化,组件引用的值就随之改变(注意main.js要引入store.js------ import store from './store') 安装 vue add vuex--->安装后自动添加了store.js就时公用数据池的配置文件

    import Vue from 'vue'//必须引用vue vuex import Vuex from 'vuex'

    Vue.use(Vuex)//要使用vuex

    export default new Vuex.Store({//这里要注意new Vuex.Store和其它组件的写法不同

vuex state

mapState 函数返回的是一个对象

从vuex里取值

即从store.js里的data里取值

store.js里的data里设置

export default new Vuex.Store({
    strict: true,//使用严格模式,从而组件引用VUEX的时候不能随意更改这里的变量
    state: {
        name:'shanshan',
        age: '18',
            look: 'budka',

路由组件里取值的方法 →
通过this.$store.state.name的方法取值,this.$store.state.name可以直接写在组件模板里的




 





    {{ this.$store.state.name }}里。这种方法拿值,数据池数据发生变化后,这里的变量不能随之变化。
    export default {
    data() {
        return {
        name: "",
        storeName: this.$store.state.name//取到store.js里的值,赋给新的变量
        };
    },
1
2
3
4
5
6
7
8

写到组件路由的计算属性里

computed: {
    //如果拿多个值,工作量太大
    storeName() {
        return this.$store.state.name//作为方法后,方法里的值改变随store里的值改变
    }
},

引入新的方法----- mapState

import { mapState } from "vuex"; //引入mapState,简便引入store里值的写法,写在要用的组件里

计算属性中的写法1:

computed: mapState(['name', 'age', 'look']),//这样写要注意,这里的名不能与这个组件里已经声明的变量名冲突

计算属性中的写法2:给变量起别名

//下面这种写法,存在不能写自己的定义的
computed: mapState({
    storeName: state => state.name,//为store里的值起个别名
    storeAge: state => state.age,
    storeLook: state => state.look,

}),

计算属性中的写法3:改良上面的写法

computed: {
    ...mapState({ //用三目运算符取到这里面的值,这样就可以在下面写自己的方法了
    storeName: state => state.name, //为store里的值起个别名
    storeAge: state => state.age,
    storeLook: state => state.look
    })
},

利用vuex进行路由间的数据传递 ①在数据池里留出数据存放空间(store.js里的data里设置数组)

export default new Vuex.Store({
strict: true,//使用严格模式,从而组件引用VUEX的时候不能随意更改这里的变量
state: {
    name:'shanshan',
    age: '18',
    look: 'budka',
    studentList: [],

②设置要传递值的路由
模板:

<input type="text" v-model="name">
    <button @click="add">确认添加</button>

方法:将数据传给数据池

add() {   
    this.$store.state.studentList.push(this.name)//由于上面是双向数据绑定,name的变化会随输入的变化而变化
    }

③设置要接收值的路由
引入mapState

import { mapState } from 'vuex'

引入数据池数据

computed: {
        ...mapState('student', ['studentList']),        
    },

vuex getter:

为什么用getters

使用state时,所有路由取到的数据是一样的。但是当有的路由有不同的需求时,state就办不到了,这时vuex引入了新的方法getters

在store.js里的方法




 















//通过getters对state里的值进行操作,组件就可以通过应用getters里的方法进而使用
  getters: {
    newStudent (state,getters) {//getters里的函数可以引用第二个参数getters,从而使引用getters参数的函数可以引用getters里其它的方法
      return state.studentList.map((item, index) => {
        if(index == 0) {
          return '**' + item + getters.a
        }else if (index < 3) {
          return item + '**'
        }else {
          return '++' + item + '++'
        }
      })
    },
    a () {//getters函数里的a()可以被函数里进行引用---getters.a
      return '111'
    }

  },
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

3.路由里引入mapGetters

import { mapState, mapGetters } from 'vuex'

4.路由接收数据

export default {
    computed: {      
        ...mapGetters({
            student: 'newStudent'
        })
    },    
}

5.数据池的严格模式
vue

数据池的严格模式设置

export default new Vuex.Store({
strict: true,//使用严格模式,从而组件引用VUEX的时候不能随意更改这里的变量

vuex mutation action:

mutation函数

在严格模式下,应通过mutations更改state里的变量,组件再引用这里的方法,否则报错

①写mutation函数:在store.js里




 












export default new Vuex.Store({
  strict: true,//使用严格模式,从而组件引用VUEX的时候不能随意更改这里的变量
  state: {
    name:'shanshan',
    age: '18',
    look: 'budka',
    studentList: [],
    }
//在严格模式下,应通过mutations更改state里的变量,组件再引用这里的方法,否则报错
  mutations: {
    changeStudent (state, {name,number}) {//只可以写两个参数,第一个参数写数据池state,第二个参数写要处理的数据池里的参数,要写多个参数第二个参数要写成对象
      state.studentList.push(name+number);
    }
  },  
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

②在组件路由里调用mutation函数




 




methods: {    
    add() {      
    // this.$store.state.studentList.push(this.name)//由于上面是双向数据绑定,name的变化会随输入的变化而变化
    //在严格模式下,不允许更改数据池数据,但可以用下面的方法调用store里的mutations里的方法
    this.$store.commit('changeStudent', {name:this.name,number:1})//引用store里的mutations里的方法,可以传参,要传多个参数,第二个参数要写成对象 
    }
  }
1
2
3
4
5
6
7

actions函数

在mutations执行异步操作(如setTmimeout),会因为函数执行时不在该作用域下而报错,且不好调试。利用actions函数解决上面的问题,主要用来执行异步操作。

①写actions函数:在store.js里




 




actions: {
    changeStudent (ctx, {name,number}) {//ctx是上下文对象,相当于这个store。
      setTimeout(() => {//延迟1000ms触发,
        ctx.commit('changeStudent', {name,number});//通过.commit调用mutations里的函数
      },1000)
    }
  },
1
2
3
4
5
6
7

②在组件路由里调用actions函数




 


methods: {    
    add() {      
    this.$store.dispatch('changeStudent', {name:this.name,number:1})//引用store里的actions里的方法,用于异步操作 
    }
  }
1
2
3
4
5

注意

简写在组件里引用actions函数的方法:store.js里的state、getters、mutation也都可以用类似的引用方法

import { mapState, mapActions, mapMutations } from "vuex";



 



methods: {
    // ...mapActions(['changeStudent'])//this.$store.dispatch的简写
    ...mapActions({//this.$store.dispatch的简写形式,写成对象形式
      add:'changeStudent'//为actions里的方法起别名
    })    
  }
1
2
3
4
5
6

4.总结: vue

利用vuex实现课程修改:

注意:

mutations: {   
    changeCourse (state, {index, name, price}) { //这里必须传参     
    state.courseList[index].name = name;
    state.courseList[index].price = price;
    },
},

vuex module:(数据池里的数据模块化管理)

store.js里的数据的模块化管理
①src下建立文件夹modules,用来存放抽离出来的数据(以js文件形式存储)
vue

②抽离数据:将某一功用或相关的数据、方法抽离到一个js文件里




 

































export default {
    namespaced: true,//命名空间,只有true才能使用下面的数据
    state: {
      studentList: [],
    },
    getters: {
      newStudent (state,getters) {//getters里的函数可以引用第二个参数getters,从而使引用getters参数的函数可以引用getters里其它的方法
        return state.studentList.map((item, index) => {
          if(index == 0) {
            return '**' + item + getters.a
          }else if (index < 3) {
            return item + '**'
          }else {
            return '++' + item + '++'
          }
        })
      },
      a () {//getters函数里的a()可以被函数里进行引用---getters.a
        return '111'
      }
    },
    mutations: {        
      changeStudent (state, {name,number}) {//只可以写两个参数,第一个参数写数据池state,第二个参数写要处理的数据池里的参数,要写多个参数第二个参数要写成对象
        console.log(this.studentList);
        state.studentList.push(name+number);
      },
    },
    actions: {
      changeStudent (ctx, {name,number}) {//ctx是上下文对象,相当于这个store。
        console.log(this.studentList);
        setTimeout(() => {//延迟1000ms触发,
          ctx.commit('changeStudent', {name,number});//通过.commit调用mutations里的函数
        },1000)
      }
    },
  };
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

③store.js里引用




 
















import Vue from 'vue'
import Vuex from 'vuex'

import student from './modules/student'

Vue.use(Vuex)

export default new Vuex.Store({
  strict: true,//使用严格模式,从而组件引用VUEX的时候不能随意更改这里的变量
  state: {
    name:'shanshan',
    age: '18',
    look: 'budka',       
  },  
  modules: {//使用引入的外部数据
    course,
    student
  }  
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

2.组件路由里如何引用数据池里的值:
①数组的写法




 







//直接引用store.js里的数据方法
        // ...mapState(['courseList'])//写引用的数据

        //引用抽离出来的course模块下的数据方法
        // courseList() {
        //     return this.$store.state.course.courseList
        // }

        //利用mapState引用course模块下的数据方法
        ...mapState('course', ['courseList'])//第一个参数写要引用的模块名(在store.js里声明的)
1
2
3
4
5
6
7
8
9
10

②对象的写法




 
...mapActions('student',{//this.$store.dispatch的简写形式,写成对象形式
      add:'changeStudent'//为actions里的方法起别名
    })
1
2
3

3.组件路由里如何向数据池里的传值

this.$store.commit('course/changeCourse', {index, name, price});//向抽离出数据的course.js里的mutations里方法传值的方法

过滤器

过滤器的声明及使用(这里只写了字符串的操作)




 
























<div id="app">
        <!--  调用过滤器 | upperCase ,括号里是(第二个参数传值) 。可以调用多个过滤器,但要注意先后顺序,先调用的先执行-->
        {{msg | upperCase(true) | removeSpace}}
    </div>

    <script>
        const vm = new Vue({
            el: '#app',
            data: {
               msg: "hello world!"
            },
            filters: {//过滤器
                upperCase (val, isFirstWord) {//声明一个过滤器,第一个参数是传进来的值,第二个参数是第一个字符串是否存在
                    val = val.toString();
                    if(isFirstWord) {
                        return val.charAt(0).toUpperCase() + val.slice(1)//第一个字母大写 + 将字符串第一个字母裁掉
                    }else {
                        return val.toUpperCase();//将传入的字符串转换为大写

                    }
                },
                removeSpace (val) {//声明一个过滤器,去除空格
                    return val.toString().replace(/ /g,"")
                }
            },
        })      
    </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

官网教程

过渡与动画

过渡与动画