初学VUE

Vue

Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式JavaScript框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用(SPA)提供驱动。

官网:Vue.js (vuejs.org)

Soc原则(关注点分离原则)

它只做视图层HTML+CSS+JS :给用户看,刷新后台给的数据

网络通信:axios

页面跳转:vue-router

状态管理:vuex

Vue-UI:ICE

前端三要素(简述)

HTML(结构)+CSS(表现)+Javascript(行为)

HTML

CSS

CSS层叠样式表是一门标记语言,并不是编程语言,因此不可以自定义变量,不可以引用等,换句话说就是不具备任何语法支持,它主要缺陷如下:

  • 语法不够强大,比如无法嵌套书写,导致模块化开发中需要书写很多重复的选择器;
  • 没有变量和合理的样式复用机制,使得逻辑上相关的属性值必须以字面量的形式重复输出,导致难以维护;

这就导致我们在工作中增加了许多工作量,所以前端人员会使用一种【CSS预处理器】来减少冗余代码。提高代码的可维护星期,大大提高开发上的效率。

css预处理器

css预处理器定义了一种新的语言,其基本思想是用一种专门的编程语言,为CSS增加一些编码的特性,将CSS作为目标生成文件,然后开发者只要使用这种语言进行CSS的编码工作。简单来说就是 **"用一种专门的编程语言,进行Web页面样式设计,再通过编译器转化成正常的CSS文件,以供项目使用” **。

常用的CSS预处理器有

  • SASS:基于Ruby,通过服务端处理,功能强大。解析效率高。需要学习Ruby语言,上手难度高于LESS。
  • LESS:基于 NodeJS,通过客户端处理,使用简单。功能比 SASS简单,解析效率也低于sASS,但在实际开发中足够了,所以我们后台人员如果需要的话,建议使用LESS.

JavaScript

弱类型脚本语言,目前用的是ES6的规范。

JavaScript框架:

  • jQuery
  • Angular
  • React
  • Vue
  • Axios(前端通信框架,ajax)

UI框架

  • Ant-Design
  • ElementUI、iview、ice
  • Bootstrap
  • AmazeUI

JavaScript构建工具

  • Babel:js编译工具
  • WebPack:模块打包器

学习Vue

什么是MVVM

MVVM (Model-View-ViewModel)是一种软件架构设计模式,由微软WPF(用于替代WinForm,以前就是用这个技术开发桌面应用程序的和Silverlight(类似于Java Applet,简单点说就是在浏览器上运行的 WPF)的架构师Ken Cooper和Ted Peters开发,是一种简化用户界面的事件驱动编程方式

MVVM源自于经典的MVC (Model-View-Controller)模式。MVVM 的核心是ViewModel层,负责转换 Model中的数据对象来让数据变得更容易管理和使用,其作用如下:

  • 该层向上与视图层进行双向数据绑定
  • 向下与Model层通过接口请求进行数据交互

MVVM模式的实现者

  • Model:模型层,在这里表示JavaScript对象
  • View:视图层,在这里表示DOM(HTML操作的元素)
  • ViweModel:连接视图和数据的中间件,Vue.js就是MVVM模式中的ViewModel的实现者

在MVVM架构中,是不允许数据和视图直接通信的,只能通过ViewModel来通信,而ViewModel就是定义了一个Observer观察者

  • ViewModel能够观察到数据的变化,并对视图对应的内容进行更新
  • ViewModel能够监听到视图的变化,并能够通知数据发生改变

至此,我们就明白了Vue.js就是一个MVVM的实现者,他的核心就是实现类DOM监听与数据绑定。

使用MVVM的好处

MVVM模式和MVC模式一样,主要目的是分离视图(View)和模型(Model),有几大好处

  • 低耦合:视图(View)可以独立于Model变化和修改,一个ViewModel可以绑定到不同的View 上,当View变化的时候Model可以不变,当Model变化的时候View 也可以不变。
  • 可复用:你可以把一些视图逻辑放在一个ViewModel里面,让很多View重用这段视图逻辑。
  • 独立开发:开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员可以专注于页面设计。
  • 可测试:界面素来是比较难于测试的,而现在测试可以针对ViewModel来写。

ViewModel层

ViewModel是由前端开发人员组织生成和维护的视图数据层。在这一层,前端开发者对从后端获取的 Model数据进行转换处理,做二次封装,以生成符合View层使用预期的视图数据模型。
需要注意的是ViewModel所封装出来的数据檬型包括视图的状态和行为两部分,而Model层的数据模型是只包含状态的

  • 比如页面的这一块展示什么,那一块展示什么这些都属于视图状态(展示)

  • 页面加载进来时发生什么,点击这一块发生什么,这一块滚动时发生什么这些都属于视图行为(交互)

视图状态和行为都封装在了ViewModel里。这样的封装使得ViewModel可以完整地去描述View层。
由于实现了双向绑定,ViewModel的内容会实时展现在View层,这是激动人心的,因为前端开发者再也不必低效又麻烦地通过操纵DOM去更新视图。
MVVM框架已经把最脏最累的一块做好了,我们开发者只需要处理和维护ViewModel,更新数据视图就会自动得到相应更新,真正实现事件驱动编程。
View层展现的不是 Model层的数据,而是 viewNodel的数据,由viewModel负责与 Model层交互,这就完全解耦了View层和 Model层,这个解耦是至关重要的,它是前后端分离方案实施的重要一环

第一个vue程序

我们的开发工具,一般vscode比较多,这里我们使用idea,新建一个vue的文件夹,然后使用idea打开,给我们的idea安上vue的插件

(这里安装后可能会出现,新建时还是没有Vue Component的情况,请见这个显示Vue Component

下载地址

 <script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.js"></script>
 <script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>

这里我们开发只要导入CDN的第二个就行

创建出一个html的文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<!--view层 模板-->
<div id="app">
    {{message}}
</div>
<!--导入Vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js" ></script>
<script>
    var vm = new Vue({
        el:"#app",
        //Model:数据
        data:{
            message:"hello vue!"
        }
    })
</script>
</body>
</html>

之后在浏览器打开

其中我们展示的div就是view,data就是我们的model,再来感受下其中的VM,右键出控制台,输入,vm.message="某某" ,回车,我们马上就可以看到我们的视图上进行了同步的更新,这就是我们的双向数据绑定。

这里可能不太好理解,我们平时修改网页之后必须刷新之后才能显示出我们修改后的样子,而这里我们是动态刷新的,不需要重新加载界面,只要修改model中的数据后,通过我们的ViewModel之后可以动态的更新我们View中数据。

这里还可以使用vue中的 v-bind 标签来绑定元素

<div id="app">
    <span v-bind:title="message">鼠标悬浮我</span>
</div>
<!--导入Vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js" ></script>
<script>
    var vm = new Vue({
        el:"#app",
        //Model:数据
        data:{
            message:"hello vue!"
        }
    })
</script>

这样鼠标悬浮在字体上是同样也会出现我们的message

你看到的v-bind 等被称为指令。指令带有前缀v-,以表示它们是Vue提供的特殊特性。可能你已经猜到了,它们会在渲染的DOM上应用特殊的响应式行为。在这里,该指令的意思是:“将这个元素节点的title特性和Vue 实例的message属性保持一致”。

v-if 、v-else

直接看下代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<!--view层 模板-->
<div id="app">
    <h1 v-if="ok">Yes</h1>
    <h1 v-else>No</h1>
</div>
<!--导入Vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js" ></script>
<script>
    var vm = new Vue({
        el:"#app",
        //Model:数据
        data:{
            ok: true
        }
    })
</script>
</body>
</html>

我们同样在控制台进行调用来试试

还有v-else-if

<div id="app">
    <h1 v-if="type==='A'">A</h1>
    <h1 v-else-if="type==='B'">B</h1>
    <h1 v-else-if="type==='C'">C</h1>
    <h1 v-else="type==='D'">D</h1>
</div>
<!--导入Vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js" ></script>
<script>
    var vm = new Vue({
        el:"#app",
        //Model:数据
        data:{
            type: 'A'
        }
    })
</script>

同样可以在控制台修改试试(注意这里的===是指全等于,包括数据的类型也要相等)

v-for

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<!--view层 模板-->
<div id="app">
    <li v-for="(item,index) in items">
        {{item.message}}
    </li>
</div>
<!--导入Vue.js-->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js" ></script>
<script>
    var vm = new Vue({
        el:"#app",
        //Model:数据
        data:{
            items:[
                {message:"大家好"},
                {message: "你好"},
                {message: "我不好"}
            ]
        }
    });
</script>
</body>
</html>

事件处理

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div id="app">
<button v-on:click="sayHi">click</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js" ></script>
<script>
    var vm = new Vue({
        el:"#app",
        data:{
            message:"hello boy"
        },
        methods:{ //方法必须定义在Vue的method中,
            sayHi:function (){
                alert(this.message);
            }
        }
    })
</script>
</body>
</html>


Vue双向数据绑定

什么是双向数据绑定

Vue.js 是一个MVVM框架,即数据双向绑定,即当数据发生变化的时候,视图也就发生变化,当视图发生变化的时候,数据也会跟着同步变化。这也算是Vue.js 的精髓之处了。
值得注意的是,我们所说的数据双向绑定,一定是对于UI控件来说的,非UI控件不会涉及到数据双向绑定。单向数据绑定是使用状态管理工具的前提。如果我们使用vuex,那么数据流也是单项的,这时就会和双向数据绑定有冲突。

在表单中使用双向数据绑定

你可以用v-model指令在表单<input> , <textarea>及<select>元素上创建双向数据绑定。它会根据控件类型自动选取正确的方法来更新元素。尽管有些神奇,但v-model本质上不过是语法糖。它负责监听用户的输入事件以更新数据,并对一些极端场景进行一些特殊处理。
注意: v-model 会忽略所有表单元素的value、checked、selected 特性的初始值而总是将Vue实例的数据作为数据来源。你应该通过JavaScript在组件的data选项中声明初始值!

现在我们尝试完成我们在某处增添内容,在另一处也实时的展示我们的数据的一个案例

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div id="app">
    输入的文本:<input type="text" v-model="message">{{message}}
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js" ></script>
<script>
    var vm = new Vue({
        el:"#app",
        data:{
            message: "123"
        }
    })
</script>
</body>
</html>

image

之后我们在控制台上调用vm中的message的值或者直接在文本框中输入值,都可以同时改变两个地方的值。

关于单复选框

<div id="app">
    选择性别:<input type="radio" name="sex" value="男" v-model="hi">男
            <input type="radio" name="sex" value="女" v-model="hi">女
    <p>选中了:{{hi}}</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js" ></script>
<script>
    var vm = new Vue({
        el:"#app",
        data:{
            hi: ''
        }
    })
</script>

image

关于下拉框

<div id="app">
    选择:<select v-model="selected">
            <option disabled value="">请选择</option>
            <option>A</option>
            <option>B</option>
            <option>C</option>
        </select>
    <p>选中了:{{selected}}</p>
</div>
<script>
    var vm = new Vue({
        el:"#app",
        data:{
            selected: ''
        }
    })
</script>

image

这个内容也就会跟着我们的所选的内容自动的改变

注意:如果v-model表达式的初始值未能匹配任何选项,<select>元素将被渲染为“未选中”状态。在 iOS 中,这会使用户无法选择第一个选项。因为这样的情况下,iOS不会触发change事件。因此,更推荐像上面这样提供一个值为空的禁用选项。

Vue组件

自定义标签

    <div id="app">
        <qingzheng></qingzheng>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
    <script>
        //定义了一个名叫qingzheng的标签
        Vue.component("qingzheng",{
            template:'<li>Hello</li>'
        });
        var vm = new Vue({
            el:"#app",
            data:{
            }
        })
    </script>

image

这里我们只是写了一个我们自己定义的标签,就可以把我们自己所定义的组件放到页面之中,组件就一个模板的意思

这里我们右键新建一个Vue的文件a.vue

<template>

</template>

<script>
export default {
  name: "a.vue"
}
</script>

<style scoped>

</style>

这是一个刚创建出来的vue的基本格式,其中最上面的<template>就是我们的模板的意思,其实就是我们的组件

那之后,我们可能就会发现,我们的前端项目只需要一个标签就完成了,当然像其中的Hello的数据就要我们另外传递进去

    <div id="app">
        <!--组件:传递组件中的值:props-->
        <qingzheng v-for="item in items" v-bind:item1="item"></qingzheng>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
    <script>
        //定义了一个名叫qingzheng的标签
        Vue.component("qingzheng",{
            props: ['item1'], //这里必须通过props这个参数来接收我们的item,不用它是无法接收外面的参数的
            template:'<li>{{item1}}</li>'
        });
        var vm = new Vue({
            el:"#app",
            data:{
                items :["java","Linux","web"]
            }
        })
    </script>

image

网络通信Axios

什么是Axios

Axios是一个开源的可以用在浏览器端和Node”s的异步通信框架,它的主要作用就是实现 AJAX异步通信,其功能特点如下:

  • 从浏览器中创建XMLHttpRequests

  • 从node.js 创建http请求

  • 支持Promise APl[JS中链式编程]

  • 拦截请求和响应

  • 转换请求数据和响应数据

  • 取消请求

  • 自动转换JSON 数据

  • 客户端支持防御XSRF(跨站请求伪造)

GitHub :https://github.com/axios/axios

中文文档 :http://www.axios-js.com/

Vue的生命周期

Vue实例有一个完整的生命周期,也就是从开始创建、初始化数据、编译模板、挂载DOM、渲染→更新→渲染、卸载等一系列过程,我们称这是Vue的生命周期。通俗说就是Vue实例从创建到销毁的过程,就是生命周期。

在页面加载的时候他是先加载模板再加载数据进行渲染,如果我们的网速够慢的话就可以看到这个过程,在这个过程中,我们可以在某个地方进行插入钩子函数,完成我们要做的事

我们新建一个json文件

{
  "name": "java",
  "url": "http://baidu.com",
  "page": 1,
  "isNonProfit":true,
  "address": {
    "street": "创业路",
    "city":"福建福州",
    "country": "中国"
  },
  "links": [
    {
      "name": "B站",
      "url": "https://www.bilibili.com/"
    },
    {
      "name": "4399",
      "url": "https://www.4399.com/"
    },
    {
      "name": "百度",
      "url": "https://www.baidu.com/"
    }
  ]
}

完成一个案例

<div id="vue">
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script>
    var vm = new Vue({
        el:"#vue",
        mounted(){ //钩子函数,链式编程 ES6
          axios.get('data.json').then(response=>(console.log(response.data)));
        }
    })
</script>

image

我们在控制台可以看到他是可以取到其中的数据的

image

可以看到这也是异步的请求,和Ajax没什么差别

这里就是通过axios.get调用一个接口,然后获得结果后,使用response来控制我们取得的数据,现在我们想将其在界面上渲染出来,我们就要将其返回到data中,

<div id="vue">
    <div>{{info.address.city}}</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script>
    var vm = new Vue({
        el:"#vue",
        data(){
          return{ //在这里返回到data中
              //请求的返回参数合适,必须和json字符串一样
              info:{ //可以为null,但不能写错
                  name:null,
                  address:{
                      street:null,
                      city:null,
                      country:null
                  }
              }
          }
        },
        mounted(){ //钩子函数,链式编程
          axios.get('data.json').then(response=>(this.info=response.data));
        }
    })
</script>

image

这里还有一个闪烁的问题,就是可能会出现模板一闪而过的情况,这里我们使用v-clock

    <style>
        [v-clock]{
            display: none;
        }
    </style>
</head>
<body>
<div id="vue" v-clock>
    <div>{{info.address.city}}</div>
</div>

如果我们要用a标签取url,可以<a v-bind:href="info.url">百度</a>

计算属性

计算属性的重点突出在属性两个字上(属性是名词),首先它是个属性其次这个属性有计算的能力(计算是动词),这里的计算就是个函数;简单点说,它就是一个能够将计算结果缓存起来的属性(将行为转化成了静态的属性),仅此而已;可以想象为缓存!

我们这里用个案例说下它这个缓存

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div id="app">
    <p>currentTime1 {{currentTime1()}}</p>
    <p>currentTime2 {{currentTime2}}</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js" ></script>
<script>
    var vm = new Vue({
        el:"#app",
        data:{
            message:"hello boy"
        },
        methods:{
            currentTime1:function(){
                return Date.now(); //返回一个时间戳
            }
        },
        computed:{ //计算属性:methods,computed方法名不能重名,重名之后,只会调用methods中的方法
            currentTime2:function(){
                return Date.now(); //返回一个时间戳
            }
        }
    })
</script>
</body>
</html>

image

注意这里的两者的调用不同,methods中是调用方法,computed中是调用属性,

image

我们可以看到我们每次调用time1的值都是在变化的,time2的值都是不变的,这是因为,此时的time2被缓存起来了,已经是一个属性值

  • methods:定义方法,调用方法使用currentTime1(),需要带括号
  • computed:定义计算属性,调用属性使用currentTime2,不需要带括号; this.message是为了能够让currentTime2观察到数据变化而变化
  • 如何在方法中的值发生了变化,则缓存就会刷新!可以在控制台使用vm.message="XXX" ,改变下数据的值,再次测试观察效果!

结论:
调用方法时,每次都需要进行计算,既然有计算过程则必定产生系统开销,那如果这个结果是不经常变化的呢?此时就可以考虑将这个结果缓存起来,采用计算属性可以很方便的做到这一点,计算属性的主要特性就是为了将不经常变化的计算结果进行缓存,以节约我们的系统开销。

内容分发

在vue.js中我们使用<slot>元素作为承载分发内容的出口,作者称其为插槽,可以应用在组合组件的场景中。仔细看下这个

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<div id="app">
    <todo>
        <todo-title slot="todo-title" :title="title"></todo-title>
        <todo-items slot="todo-items" v-for="item in todoItems" :item="item"></todo-items>
    </todo>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js" ></script>
<script>
    //slot:插槽
    Vue.component('todo',{
        template: '<div>\
                    <slot name="todo-title"></slot>\
                    <ul>\
                        <slot name="todo-items"></slot>\
                    </ul>\
                    </div>'
    });
    Vue.component("todo-title",{
        props:['title'],
        template: '<div>{{title}}</div>'
    });
    Vue.component("todo-items",{
        props:['item'],
        template: '<li>{{item}}</li>'
    });
    var vm = new Vue({
        el:"#app",
        data: {
            title: "语言列表",
            todoItems:['C++','Java','C','Go']
        }
    })
</script>
</body>
</html>

image

注意看整体的绑定流程,其中slot中通过name来绑定组件,slot感觉就是把组件组合起来,可以把某个自定义的组件插入到整体组件的某个地方,在整体组件的模板中设置插槽,在视图层使用组件插槽的时候,要声明使用所放置的slot,slot="todo-title" 注意这的:title="title"这个是v-bind的简写形式,v-on的简写是@。首先加载整个组件的模板,然后加载组件中的插槽,将插槽绑定上其他的组件,组合成一个整体的模板,之后把数据调出来,进行渲染。

自定义事件

现在我们要实现增加按钮,点击后动态删除列表中的某一项,Vue为我们提供了自定义事件的功能很好的帮助我们解决了这个问题;使用this.$emit('自定义事件名',参数)。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<div id="app">
    <todo>
        <todo-title slot="todo-title" :title="title"></todo-title>
        <todo-items slot="todo-items" v-for="(item,index) in todoItems" :item="item" :index="index"
                    v-on:remove="removeItems(index)" :key="index"></todo-items>
    </todo>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.min.js" ></script>
<script>
    //slot:插槽
    Vue.component('todo',{
        template: '<div>\
                    <slot name="todo-title"></slot>\
                    <ul>\
                        <slot name="todo-items"></slot>\
                    </ul>\
                    </div>'
    });
    Vue.component("todo-title",{
        props:['title'],
        template: '<div>{{title}}</div>'
    });
    Vue.component("todo-items",{
        props:['item','index'],
        //这个按钮只能绑定当前组件里的方法
        template: '<li>{{index}}--{{item}}<button @click="remove">删除</button></li>',
        methods: {
            remove:function (index){
                //this.$emit() 自定义事件
                this.$emit('remove',index);
            }
        }
    });
    var vm = new Vue({
        el:"#app",
        data: {
            title: "语言列表",
            todoItems:['C++','Java','C','Go']
        },
        methods:{
            removeItems:function (index){
                console.log("删除了"+this.todoItems[index]+"ok");
                this.todoItems.splice(index,1);//一次删除一个元素
            }
        }
    })
</script>
</body>
</html>

image

看下这个图来了解它,绑来绑去(晕

image

到这里我们可以创建Vue的单页面的应用了

入门小结

核心:数据驱动,组件化

优点:借鉴了AngulaJS的模块化开发和React的虚拟Dom,虚拟Dom就是把Dom操作放到内存中执行;

常用的属性:

  • v-if
  • v-else-if
  • v-for
  • v-on 绑定事件,简写@
  • v-model 数据双向绑定
  • v-bind给组件绑定参数,简写:

组件化

  • 组合组件slot插槽
  • 组件内部绑定事件需要使用到this.$emit("事件名",参数)
  • 计算属性的特色,缓存计算数据

遵循SoC关注度分离原则,Vue是纯粹的视图框架,并不包含,比如Ajax之类的通信功能,为了解决通信问题,我们需要使用Axios框架做异步通信;

Vue的开发都是要基于NodeJS,实际开发采用vue-cli脚手架开发,vue-router路由,vuex做状态管理; Vue Ul,界面我们一般使用ElementUI(饿了么出品),或者ICE(阿里巴巴出品!)来快速搭建前端项目

官网:

第一个vue-cli程序

什么是vue-cli

vue-cli官方提供的一个脚手架,用于快速生成一个vue的项目模板;
预先定义好的目录结构及基础代码,就好比咱们在创建 Maven项目时可以选择创建一个骨架项目,这个骨架项目就是脚手架,我们的开发更加的快速;

主要的功能:

  • 统一的目录结构
  • 本地调试
  • 热部署
  • 单元测试
  • 集成打包上线

环境

自行安装Node.js :http://nodejs.cn/download 和Git:https://git-scm/downloads

确认其安装成功

image

可以安装node.js淘宝镜像加速器(cnpm)

#-g就是全局安装
npm install cnpm -g
#或使用如下语句解决npm速度慢的问题
npm install --registry=https://registry.npm.taobao.org

安装vue-cli

cnpm install vue-cli -g
#测试是否安装成功
#查看可以基于哪些模板创建vue应用程序,通常我们选择webpack
vue list

创建Vue程序

命令行到我们要创建项目的目录后使用命令vue init webpack myvue 用webpack模板初始化一个名为myvue的项目

image

前面几个直接默认回车,Vue Build选第一个,之后的都选NO就行

这样我们的Vue项目是创建好了

image

说明:

  • Project name:项目名称,默认回车即可
  • Project description:项目描述,默认回车即可· Author:项目作者,默认回车即可
  • lnstall vue-router:是否安装 vue-router,选择n不安装(后期需要再手动添加)
  • Use ESLint to lint your code:是否使用ESLint做代码检查,选择n不安装(后期需要再手动添加)
  • Set up unit tests:单元测试相关,选择n不安装(后期需要再手动添加)
  • Setup e2e tests with Nightwatch:单元测试相关,选择n不安装(后期需要再手动添加)
  • Should we run npm install for you after the,project has been created:创建完成后直接初始化,选择n,我们手动执行;运行结果

初始化并运行

cd myvue
npm install  #安装项目的所以依赖,执行完后我们的node_modules中就会增加很多的东西
npm run dev

我们执行完 npm install后就会多出一个node_modules这个目录,我们可以将其类似的看成java项目中的maven ,其中的package.json文件相当于java项目中的pom.xml文件

我们通过在命令行使用npm run dev来启动我们的项目,将运行在我们的8080端口

image

我们就可以在浏览器中进入localhost:8080

image

我们在idea中打开此项目

image

我们也可以直接在idea中启动我们的项目(如果不行可能是因为需要管理员权限)

image

启动之后都是使用Ctrl+C结束的

我们可以看到整个项目的入口是main.js它就是和最外面的index.html绑定在一起

image

Ctrl点击那个App就可以点进去看组件具体的内容

image

这是我们的index.html的主页面,我们开发过程中不需要去动它。

WebPack

什么是webpack

本质上,webpack 是一个现代JavaScript应用程序的静态模块打包器(module bundler)。当webpack处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个bundle.
Webpack 是当下最热门的前端资源模块化管理和打包工具,它可以将许多松散耦合的模块按照依赖和规则打包成符合生产环境部署的前端资源。还可以将按需加载的模块进行代码分离,等到实际需要时再异步加载。通过loader转换,任何形式的资源都可以当做模块,比如CommonsJS、AMD、ES6、css、JSON、CoffeeScript、LESS等;
伴随着移动互联网的大潮,当今越来越多的网站已经从网页模式进化到了WebApp模式。它们运行在现代浏览器里,使用HTML5、CSS3、ES6等新的技术来开发丰富的功能,网页已经不仅仅是完成浏览器的基本需求; WebApp通常是一个SPA (单页面应用),每一个视图通过异步的方式加载,这导致页面初始化和使用过程中会加载越来越多的JS代码,这给前端的开发流程和资源组织带来了巨大挑战。
前端开发和其他开发工作的主要区别,首先是前端基于多语言、多层次的编码和组织工作,其次前端产品的交付是基于浏览器的,这些资源是通过增量加载的方式运行到浏览器端,如何在开发环境组织好这些碎片化的代码和资源,并且保证他们在浏览器端快速、优雅的加载和更新,就需要一个模块化系统,这个理想中的模块化系统是前端工程师多年来一直探索的难题。

模块化的演进

Script

<script src="module1.js"></script>
<script src="module2.js"></script>
<script src="module3.js"></script>

这是最原始的JavaScript文件加载方式,如果把每一个文件看做是一个模块,那么他们的接口通常是暴露在全局作用域下,也就是定义在 window对象中,不同模块的调用都是一个作用域。
这种原始的加载方式暴露了一些显而易见的弊端:

  • 全局作用域下容易造成变量冲突
  • 文件只能按照<script>的书写顺序进行加载·开发人员必须主观解决模块和代码库的依赖关系
  • 在大型项目中各种资源难以管理,长期积累的问题导致代码库混乱不堪

CommonJS

服务器端的NodeJS遵循CommonsJS规范,该规范核心思想是允许模块通过require方法来同步加载所需依赖的其它模块,然后通过exports或 module.exports 来导出需要暴露的接口。

require("module");
require("../module.js");
export .doStuff = function() {};
module.exports = someValue;

优点:

  • 服务器端模块便于重用
  • NPM中已经有超过45万个可以使用的模块包
  • 简单易用

缺点:

  • 同步的模块加载方式不适合在浏览器环境中,同步意味着阻塞加载,浏览器资源是异步加载的
  • 不能非阻塞的并行加载多个模块

实现

  • 服务端的NodeJS
  • Browserify,浏览器端的CommonsJS实现,可以使用NPM的模块,但是编译打包后的文件体积较大
  • modules-webmake,类似Browserify,但不如 Browserify灵活
  • wreq,Browserify的前身

AMD

Asynchronous Module Definition规范其实主要一个主要接口define(id?,dependencies?, factory);它要在声明模块的时候指定所有的依赖dependencies,并且还要当做形参传到 factory中,对于依赖的模块提前执行。

define("module",[ "dep1","dep2"],function(d1,d2)freturn someExportedvalue;});
require(["module", "../file.js"],function(module, file {});

优点

  • 适合在浏览器环境中异步加载模块

  • 可以并行加载多个模块

缺点

  • 提高了开发成本,代码的阅读和书写比较困难,模块定义方式的语义不畅
  • 不符合通用的模块化思维方式,是一种妥协的实现

实现

  • RequireJs
  • curl

CMD

Commons Module Definition规范和AMD很相似,尽量保持简单,并与CommonsJS和NodeJS的 Modules规范保持了很大的兼容性。

优点

  • 依赖就近,延迟执行
  • 可以很容易在NodeJS中运行

缺点

  • 依赖SPM打包,模块的加载逻辑偏重

实现

  • Sea.js
  • coolie

ES6模块

EcmaScript6标准增加了JavaScript语言层面的模块体系定义。ES6模块的设计思想,是尽量静态化,使编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonsJS和AMD模块,都只能在运行时确定这些东西。

import "jquery";
export function doStuff(){}
module "localModule" {}

优点

  • 容易进行静态分析
  • 面向未来的EcmaScript标准

缺点

  • 原生浏览器端还没有实现该标准
  • 全新的命令,新版的NodeJS才支持

实现

  • Babel

安装Webpack

WebPack 是一款模块加载器兼打包工具,它能把各种资源,如JS、JSX、ES6、SASS、LESS.图片等都作为模块来处理和使用。将ES6规范的代码打包成ES5规范的代码,然后所有浏览器都能运行。

npm install webpack -g
npm install webpack-cli -g

测试安装成功,可以使用这个命令

webpack -v

webpack-cli -v

image

配置

创建webpack.config-js配置文件

  • entry:入口文件,指定WebPack 用哪个文件作为项目的入口
  • output:输出,指定WebPack把处理完成的文件放置到指定路径
  • module:模块,用于处理各种类型的文件
  • plugins:插件,如:热更新、代码重用等resolve:设置路径指向
  • watch:监听,用于设置文件改动后直接打包
module.exports = {
	entry:"",
	output: {
		path:"",
        filename:"",
    },
	module: {
		loaders : [
            {test: /\.js$/, loader: ""}
        ]
    },
	plugins: {},
    resolve: {},
 	watch: true   
}

使用webpack

1.创建项目
2.创建一个名为modules的目录,用于放置JS模块等资源文件
3.在modules下创建模块文件,如hello.js,用于编写JS模块相关代码

//暴露一个方法
exports.sayHi = function () {
    document.write("<h1>Hello world</h1>");
};

4.在modules下创建一个名为main.js的入口文件,用于打包时设置entry 属性

//require导入一个模块,就可以调用这个模块中的方法了
var hello = require("./hello"); //导入进来(java中的导包)
hello.sayHi(); //可以直接调用到其中的方法,模块化开发

5.在项目目录下创建webpack.config.js 配置文件,使用webpack命令打包

module.exports = {
    entry: "./modules/main.js",
    output: {
        filename:"./js/bundle.js" //一般规范的就是输出到这里
    }
};

之后我们的项目就会多出我们输出的文件

image

打包成功后,我们引入,我们新建一个index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <!--前端的模块化开发-->
    <script src="dist/js/bundle.js"></script>
</body>
</html>

在浏览器中打开它就可以看到我们写的内容

image

还可以使用webpack --watch命令来一直监听我们的操作。

Vue-router路由

Vue Router是 Vue.js官方的路由管理。它和Vue.js 的核心深度集成,让构建单页面应用变得易如反掌。包含的功能有:

  • 嵌套的路由/视图表
  • 模块化的、基于组件的路由配置
  • 路由参数.查询、通配符
  • 基于Vue.js 过渡系统的视图过渡效果
  • 细粒度的导航控制
  • 带有自动激活的Css class的链接
  • HTML5历史模式或 hash模式,在IE9中自动降级
  • 自定义的滚动条行为

安装

基于第一个vue-cli进行测试学习;先查看node_modules中是否存在vue-router

vue-router是一个插件包,所以我们还是需要用npm/cnpm来进行安装的。打开命令行工具,进入你的项目目录,输入下面命令。
npm install vue-router --save-dev
如果在一个模块化工程中使用它,必须要通过 Vue.use()明确地安装路由功能:

import Vue from 'vue'
import VueRouter from 'vue-router'

Vue.use(VueRouter);

我们将我们之前那个第一个vur-cli的程序中删除一些文件,使其到一个初始工程的样子

image

其中App.vue

<template>
  <div id="app">

  </div>
</template>

<script>

export default {
  name: 'App'
}
</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>

其中main.js

import Vue from 'vue'
import App from './App'

Vue.config.productionTip = false

/* eslint-disable no-new */
new Vue({
  el: '#app',
  components: { App },
  template: '<App/>'
})

都删到剩这些就够了

接下来安装vue-router npm install vue-router --save-dev

image

使用

我们可以直接在main.js中将其导入使用

import Vue from 'vue'
import App from './App'
import VueRouter from 'vue-router'

Vue.config.productionTip = false

//显示声明使用VueRouter
Vue.use("VueRouter");

new Vue({
  el: '#app',
  components: { App },
  template: '<App/>'
})

我们运行项目,npm run dev 之后到localhost:8080 就可以看到我们的主页,这时我们的主页啥也没有,但只要我们在App.vue中加上东西,他就能马上部署上去,这也叫热部署。

imageimage

我们这里来试着开发我们自己的组件Content.vue,其中简要写些内容

<template>
  <h1>内容页</h1>
</template>

<script>
export default {
  name: "Content.vue"
}
</script>

<style scoped>

</style>

再来一个Main.vue

<template>
  <h1>首页</h1>
</template>

<script>
export default {
  name: "Main.vue"
}
</script>

<style scoped>

</style>

我们新建一个router的目录,其中新建一个index.js文件,这是他的主配置文件

import Vue from 'vue'
import VueRouter from 'vue-router'

import Content from "../components/Content"
import Main from "../components/Main"

//安装路由
Vue.use(VueRouter);
//配置导出路由
export default new VueRouter({
  routes:[
    {
      //路由路径
      path:'/content',
      name: 'content',
      component: Content
    },
    {
      //路由路径
      path:'/main',
      name: 'main',
      component: Main
    }
  ]
});

然后就是我们的main.js中加上路由配置(这里导入配置就不需要导入路由了)

import Vue from 'vue'
import App from './App'
import router from './router' //自动扫描其中的路由配置

Vue.config.productionTip = false

new Vue({
  el: '#app',
  //配置路由
  router,
  components: { App },
  template: '<App/>'
})

最后就可以在App.vue中直接使用了

<template>
  <div id="app">
    <h1>Vue-Router</h1>
    <router-link to="/main">首页</router-link>
    <router-link to="/content">内容页</router-link>
    <router-view></router-view>
  </div>
</template>

<script>

export default {
  name: 'App'
}
</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>

其中router-link来控制路由的跳转,相当于a标签,router-view来控制显示,就是将跳转后的内容显示出来。

启动后

image

点击首页和点击内容页,就会分别展示其组件中的内容

imageimage

我们分析下整个流程,创建自己的组件,然后加入的路由的配置文件router/index.js中在其中配置其组件的路径等信息,然后在main.js中配置路由,之后就可以在App.vue中直接通过router-linkrouter-view来使用我们的各个组件,有点像servlet。

我们之后要添加组件的话,只需要创建自己的组件,然后在路由的配置文件中加入并配置好路径等信息,即可在App.vue等地方使用它。

ElementUI

Element 饿了么前端出品的一套 基于 Vue 2.0 的桌面端组件库

官网: Element - 网站快速成型工具

实例

使用vue init webpack hello-vue 使用vue初始化创建一个叫hello-vue的webpack项目。

image

这样一个初始化项目就好了。

#进入工程目录
cd hello-vue
#安装vue-router
npm install vue-router --save-dev
#安装element- ui
npm i element-ui -S
#安装依赖
npm instal1
#安装SASS加载器
cnpm install sass-loader node-sass --save-dev
#启动测试
npm run dev

Npm命令解释:
npm install moduleName :安装模块到项目目录下
npm install -g moduleName : -g 的意思是将模块安装到全局,具体安装到磁盘哪个位置,要看npm config prefix的位置
npm install -save moduleName : --save 的意思是将模块安装到项目目录下,并在package 文件的dependencies节点写入依赖,-S为该命令的缩写
npm install -save-dev moduleName :--save-dev的意思是将模块安装到项目目录下,并在 package文件的 devDependencies节点写入依赖,-D为该命令的缩写

我们启动测试成功显示默认vue主页,就可以将其导入idea了

image

导入idea后,和之前一样删掉不用的东西,src/assets/logo.pngsrc/component/HelloWorld.vue以及App.vue中的一些内容。

其中在src下新建views和router文件夹,静态资源都放在最外面的static文件夹中,views一般放视图组件,components一般放一些功能组件,router中就是放路由的。

在views中创建出Main.vue

<template>
  <h1>首页</h1>
</template>

<script>
export default {
  name: "Main.vue"
}
</script>

<style scoped>

</style>

创建一个登陆的组件Login.vue

<template>
  <div>
  <el-form ref="loginForm" :model="form" :rules="rules" label-width="80px" class="login-box">
    <h3 class="login-title">欢迎登录</h3>
    <el-form-item label=账号" prop="username">
      <el-input type="text" placeholder="请输入账号" v-model="form.username"/>
    </el-form-item>
    <el-form-item label="密码" prop="password">
      <el-input type="password" placeholder="请输入密码"v-model="form.password" />
    </el-form-item>
    <el-form-item>
      <el-button type="primary" v-on:click="onSubmit('loginForm')">登录</el-button>
    </el-form-item>
  </el-form>Ⅰ
  <el-dialog
      title="温馨提示"
      :visible.sync="dialogVisible"
      width="30%"
      :before-close="handleClose">
      <span>请输入账号和密码</span>
      <span slot="footer" class="dialog-footer">
        <el-button type="primary" @click="dialogVisible = false">确 定</el-button>
      </span>
    </el-dialog>
  </div>
</template>

<script>
  export default {
    name: "Login",
    data() {
      return {
        form: {
          username: '',
          password: ''
        },

        //表单验证。需要在el-form-item元素中增加 prop属性
        rules: {
          username: [
            {required: true, message: "账号不可为空", trigger: 'blur'}
          ],
          password: [
            {required: true, message: "密码不可为空", trigger: 'blur'}
          ]
        },

        //对话框显示和隐藏
        dialogVisible: false
      }
    },
    methods: {
      onSubmit(formName) {
        //为表单绑定验证功能
        this.$refs[formName].validate((valid) => {
          if (valid) {
            //使用vue-router路由到指定页面,该方式称之为编程式导航
            this.$router.push("/main");
          } else {
            this.dialogVisible = true;
            return false;
          }
        });
      }
    }
  }
</script>

<style lang="scss" scoped>
  .login-box{
    border: 1px solid #DCDFE6;
    width: 350px;
    margin: 180px auto;
    padding: 35px 35px 15px 35px;
    border-radius: 5px;
    -webkit-border-radius: 5px;
    -moz-border-radius: 5px;
    box-shadow:0 0 25px #909399;
  }
  .login-title {
    text-align: center;
    margin:0 auto 40px auto;
    color: #303133;
  }
</style>

在router文件夹下新建一个index.js,在这里面进行路由的配置

import Vue from 'vue'
import Router from 'vue-router'

import Main from '../views/Main'
import Login from "../views/Login";

Vue.use(Router);

export default new Router({
  routes:[
    {
      path: '/main',
      component: Main
    },{
      path: '/login',
      component: Login
    }
  ]
});

然后就是在main.js中的配置

import Vue from 'vue'
import App from './App'

import router from './router'
//导入element的东西
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';

Vue.use(router);
Vue.use(ElementUI);

new Vue({
  el: '#app',
  router,
  render: h => h(App) //ElementUI
});

之后就是App.vue中的内容

<template>
  <div id="app">
    <router-view></router-view>
  </div>
</template>

<script>

export default {
  name: 'App'
}
</script>

之后在命令行中执行npm run dev启动 ,可能会发生如下的报错

image

这是因为我们的sass-loader的版本太高了,这个我们可以将package.json中的sass-loader的版本调低一点(比如),然后再执行一下npm install (可能还会出现其他版本问题)

Login.vue会报错的原因可能是最下面的style标签加上了lang=scss,解决办法:
1.去掉lang="scss",(我用的这个。。)
2.版本不兼容问题,可以降低版本,注意:降低版本之后会遇到其他版本也需要降低的问题,然后继续降低版本,,,

之后到 http://localhost:8080/#/login 就可以看到我们使用ElementUI做的登陆界面。

image

任意输入内容,点击登陆即可跳转到我们的/main的首页。

路由嵌套

嵌套路由又称子路由,在实际应用中,通常由多层嵌套的组件组合而成,URL中各段动态路径也按某种结构对应嵌套的各层组件。

实例

在views中新建一个user目录,其中新建一个组件Profile.vue

<template>
  <h1>个人信息</h1>
</template>

<script>
  export default {
    name: "UserProfile"
  }
</script>

<style scoped>

</style>

和一个List.vue

<template>
  <h1>用户列表</h1>
</template>

<script>
  export default {
    name: "UserList"
  }
</script>

<style scoped>

</style>

创建完善好Main.vue

<template>
  <div>
    <el-container>
      <el-aside width="200px">
        <el-menu :default-openeds="['1']">
          <el-submenu index="1">
            <template slot="title"><i class="el-icon-caret-right"></i>用户管理</template>
            <el-menu-item-group>
              <el-menu-item index="1-1">
                <router-link to="/user/profile">个人信息</router-link>
              </el-menu-item>
              <el-menu-item index="1-2">
                <router-link to="/user/list">用户列表</router-link>
              </el-menu-item>
            </el-menu-item-group>
          </el-submenu>
          <el-submenu index="2">
            <template slot="title"><i class="el-icon-caret-right"></i>内容管理</template>
            <el-menu-item-group>
              <el-menu-item index="2-1">分类管理</el-menu-item>
              <el-menu-item index="2-2">内容列表</el-menu-item>
            </el-menu-item-group>
          </el-submenu>
        </el-menu>
      </el-aside>

      <el-container>
        <el-header style="text-align: right; font-size: 12px">
          <el-dropdown>
            <i class="el-icon-setting" style="margin-right: 15px"></i>
            <el-dropdown-menu slot="dropdown">
              <el-dropdown-item>个人信息</el-dropdown-item>
              <el-dropdown-item>退出登录</el-dropdown-item>
            </el-dropdown-menu>
          </el-dropdown>
          </el-header>

        <el-main>
          <router-view />
        </el-main>
      </el-container>
    </el-container>
  </div>
</template>

<script>
  export default {
    name: "Main"
  }
</script>

<style scoped>
    .el-header {
      background-color: #36ba2a;
      color: #333;
      line-height: 60px;
    }
    .el-aside {
      color: #333;
    }
</style>

最后再在router/index.js中配置好路由

import Vue from 'vue'
import Router from 'vue-router'

import Main from '../views/Main';
import Login from "../views/Login";
import UserList from '../views/user/List';
import UserProfile from '../views/user/Profile';

Vue.use(Router);

export default new Router({
  routes:[
    {
      path: '/main',
      component: Main,
      children:[ //嵌套路由
        {path:'/user/profile',component:UserProfile},
        {path:'/user/list',component:UserList}
      ]
    },{
      path: '/login',
      component: Login
    }
  ]
});

现在我们就可以 npm run dev执行测试下,进到http://localhost:8080/#/main

image

点击个人信息或用户列表就可以相应的跳转到我们之前设置的组件界面,网页上的url也会改变成我们设置的url。

imageimage

参数传递及重定向

我们的界面应该是根据不同的用户参数从而展示不同的内容,这就要用到参数传递。下面例子

直接传参

在Main.vue中进行修改

<el-menu-item index="1-1">
    <!--name 组件名,params 传递参数  v-bind绑定简写 :-->
      <router-link :to="{name:'UserProfile',params:{id:1}}">个人信息</router-link>
</el-menu-item>

在index.js处

      path: '/main',
      component: Main,
      children:[ //嵌套路由
        {path:'/user/profile/:id',name:'UserProfile' ,component:UserProfile},
        {path:'/user/list',component:UserList}
      ]

然后我们试着将其在组件页面中取得展示出来,Profile.vue

<template>
  <div>
    <h1>个人信息</h1>
    {{ $route.params.id }}
  </div>
</template>

(注意:我们在template组件中写的内容必须都要在某个标签之中,不然会报错)

之后我们运行测试下 http://localhost:8080/#/user/profile/1

image

将地址上的数字改成另外的内容,下面的展示也会做相应的改变。

我们在Main.vue中通过绑定到to,让他变成一个对象去传,然后传给某个路由,给某个路由中的path来接收,然后在某个组件页面通过{{route.params.XXXX}}来接收。

props传参

还可以通过props来解耦,取得参数

这种方式,我们的Main.vue中不用改变,我们需要在index.js中设置下

{path:'/user/profile/:id',name:'UserProfile' ,component:UserProfile,props: true}

将其设置为true,从而支持参数传递

之前在前端组件Profile.vue调用的话,是通过组件来取得参数

<template>
  <div>
    <h1>个人信息</h1>
<!--    {{ $route.params.id }}-->
    {{id}}
  </div>
</template>

<script>
  export default {
    props:['id'],
    name: "UserProfile"
  }
</script>

<style scoped>

</style>

这样也是和之前一样的效果。

重定向

我们在Main.vue再加上一个重新回到主页的选项

<el-menu-item index="1-3">
  <router-link to="/goHome">回到首页</router-link>
</el-menu-item>

我们只需要在router/index.js中设置下重定向即可,在route中再加上一个路径的重定向

{
   path:'/goHome',
   redirect:'/main'
}

image

之后点击回到首页后就可以回到我们一片空白的首页。

我们把登陆的也连接一下,在Login.vue中将我们的username传过来展示,修改下其中的onSubmit方法,将用户名一起传输。

      onSubmit(formName) {
        //为表单绑定验证功能
        this.$refs[formName].validate((valid) => {
          if (valid) {
            //使用vue-router路由到指定页面,该方式称之为编程式导航
            this.$router.push("/main/"+this.form.username);
          } else {
            this.dialogVisible = true;
            return false;
          }
        });
      }

之后在router/index.js中接收

    {
      path: '/main/:name',
      props:true,
      component: Main,
      children:[ //嵌套路由
        {path:'/user/profile/:id',name:'UserProfile' ,component:UserProfile,props: true},
        {path:'/user/list',component:UserList}
      ]
    }

这里接收后,再Main.vue中接收使用即可,

<script>
  export default {
    props:['name'],
    name: "Main"
  }
</script>

在任意位置加一个span的标签

          <el-dropdown>
            <i class="el-icon-setting" style="margin-right: 15px"></i>
            <el-dropdown-menu slot="dropdown">
              <el-dropdown-item>个人信息</el-dropdown-item>
              <el-dropdown-item>退出登录</el-dropdown-item>
            </el-dropdown-menu>
          </el-dropdown>
          <span>{{name}}</span>

运行测试下,首先来到登陆界面http://localhost:8080/#/login

imageimage

点击登陆进入系统后,可以看到右上角成功出现我们的名字。

路由模式与404

路由模式

路由模式有两种

  • hash: 路径带# 符号,如http://localhost/#/login

  • history: 路径不带#符号,如 http://localhost/login

修改路由配置,代码如下:

export default new Router({
	mode: 'history',
	routes : []
});

404

当我们的界面找不到的时候,我们应该出现404的页面

在views中创建404组件页面NotFound.vue

<template>
  <div>
    <h1>404</h1>
    <h2>你的网页走丢了。。。</h2>
  </div>
</template>

<script>
export default {
  name: "NotFound"
}
</script>

<style scoped>

</style>

再配置其路由设置

{
  path: '*', //访问不到的就到*
  component: NotFound
}

运行测试

image

路由钩子与异步请求

  • beforeRouteEnter:在进入路由前执行
  • beforeRouteLeave:在离开路由前执行

他们其中都有三个参数(to,from,next),这个不能乱写,固定的,beforeRouteEnter其实和过滤器一样,在进入路由前执行。

<script>
  export default {
    props:['id'],
    name: "UserProfile",
    beforeRouteEnter:(to,from,next)=>{
      console.log("进入之前");
      next();
    },
    beforeRouteLeave:(to,from,next)=>{
       console.log("离开之前");
       next();
    }
  }
</script>

可以像这样使用。

参数说明:

  • to:路由将要跳转的路径信息.
  • from:路径跳转前的路径信息.
  • next:路由的控制参数
    • next()跳入下一个页面
    • next('/path')改变路由的跳转方向,使其跳到另一个路由
    • next(false)返回原来的页面
    • next((vm)=>{ })仅在 beforeRouteEnter 中可用,vm是组件实例

在钩子函数中使用异步请求

安装 Axios cnpm install --save axios vue-axios

在main.js中引入

import axios from 'axios'
import VueAxios from 'vue-axios'

Vue.use(VueAxios, axios)

这里我们在static目录下新建一个mock的目录,我们一般将测试数据之类的东西放在这里面

其中新建一个data.json

{
  "name": "java",
  "url": "http://baidu.com",
  "page": 1,
  "isNonProfit":true,
  "address": {
    "street": "创业路",
    "city":"福建福州",
    "country": "中国"
  },
  "links": [
    {
      "name": "B站",
      "url": "https://www.bilibili.com/"
    },
    {
      "name": "4399",
      "url": "https://www.4399.com/"
    },
    {
      "name": "百度",
      "url": "https://www.baidu.com/"
    }
  ]
}

这里我们想要将这个数据在前端展示,我们是让他在进入一个页面前加载好数据,使用beforeRouteEnter这个钩子函数。

<template>
  <div>
    <h1>个人信息</h1>
    {{id}}
  </div>
</template>

<script>
  export default {
    props:['id'],
    name: "UserProfile",
    beforeRouteEnter:(to,from,next)=>{
      console.log("进入之前");
      //加载数据
      next(vm=>{ //直接拿到当前vm的实例对象
        vm.getData(); //进入路由之前执行getData
      });
    },
    beforeRouteLeave:(to,from,next)=>{
       console.log("离开之前");
       next();
    },
    methods:{
      getData: function (){
        this.axios({
          method:'get',
          url:'http://localhost:8080/static/mock/data.json'
        }).then(function (response){
          console.log(response);
        })
      }
    }
  }
</script>

<style scoped>

</style>

之后我们启动系统进入这个页面后就可以在输出台看到我们获取的数据

image

其实直接这里只是做一个转接。

posted @ 2021-08-03 17:22  Plokit  阅读(107)  评论(0)    收藏  举报