1、概念和特点
- 概念:Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式JavaScript框架。 与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。发布时间为2014年2月。
- 特点
- 更加轻量20kb min + gzip
- 渐进式框架
- 响应式的更新机制
- 学习成本低
2、MVVM模式的实现者
-
Model:模型层,在这里表示avaScript对象
-
View:视图层,在这里表示DOM(HTML操作的元素)
-
ViewModel:连接视图和数据的中间件,Vue.js就是MVVM中ViewModel的实现者
在MVVM架构中,是不允许数据和视图直接通信的,只能通过ViewModel来通信,而ViewModel就是定义了一个Observer观察者
-
ViewModel能够视察到数据的变化,并对视图对应的内容进行更新
-
ViewModel能够监听到视图的变化,并能够通知数据发生改变
3、Vue基本语法
带有前缀v-的被称为指令,以表示它们是Vue提供的体术属性,它们会在渲染的DOM上应用特殊的响应式行为,该指令的意思是“将这个元素节点的title特性和Vue实例的message属性保持一致”
如果再次打开浏览器的JavaScript控制台,输入app.message="新消息",就会再一次看到这个绑定了title特性的HTML已经进行了更新。
| <!DOCTYPE html> |
| <html lang="en" xmlns:v-bind="http://www.w3.org/1999/xhtml"> |
| <head> |
| <meta charset="UTF-8"> |
| <title>Title</title> |
| </head> |
| <body> |
| <div id="app"> |
| {{message}}<br/> |
| <span v-bind:title="message">消息</span> |
| </div> |
| <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> |
| <script> |
| var vm = new Vue({ |
| el:"#app", |
| data:{ |
| message:"Hello Vue!!!" |
| } |
| }); |
| </script> |
| </body> |
| </html> |
| <!DOCTYPE html> |
| <html lang="en" xmlns:v-bind="http://www.w3.org/1999/xhtml"> |
| <head> |
| <meta charset="UTF-8"> |
| <title>Title</title> |
| </head> |
| <body> |
| <div id="app"> |
| <span v-if="status">成功</span> |
| <span v-else>失败</span> |
| </div> |
| <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> |
| <script> |
| var vm = new Vue({ |
| el:"#app", |
| data:{ |
| status:true |
| } |
| }); |
| </script> |
| </body> |
| </html> |
| <!DOCTYPE html> |
| <html lang="en" xmlns:v-bind="http://www.w3.org/1999/xhtml"> |
| <head> |
| <meta charset="UTF-8"> |
| <title>Title</title> |
| </head> |
| <body> |
| <div id="app"> |
| <div v-for="arr in arrs"> |
| {{arr.message}} |
| </div> |
| </div> |
| <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> |
| <script> |
| var vm = new Vue({ |
| el:"#app", |
| data:{ |
| arrs:[ |
| {message:"啦啦啦"}, |
| {message:"哈哈哈"} |
| ] |
| } |
| }); |
| </script> |
| </body> |
| </html> |
4、Vue绑定事件(v-on)
| <!DOCTYPE html> |
| <html lang="en" xmlns:v-bind="http://www.w3.org/1999/xhtml" xmlns:v-on="http://www.w3.org/1999/xhtml"> |
| <head> |
| <meta charset="UTF-8"> |
| <title>绑定事件</title> |
| </head> |
| <body> |
| <div id="app"> |
| <button v-on:click="alertMsg">点我</button> |
| </div> |
| <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> |
| <script> |
| var vm = new Vue({ |
| el:"#app", |
| data:{ |
| message:"hahaha" |
| }, |
| methods:{ |
| alertMsg:function (event) { |
| alert(this.message); |
| } |
| } |
| }); |
| </script> |
| </body> |
| </html> |
5、Vue双向绑定(v-model)
| <!DOCTYPE html> |
| <html lang="en" xmlns:v-bind="http://www.w3.org/1999/xhtml" xmlns:v-on="http://www.w3.org/1999/xhtml"> |
| <head> |
| <meta charset="UTF-8"> |
| <title>双向绑定</title> |
| </head> |
| <body> |
| <div id="app"> |
| 请输入内容: |
| <input type="text" v-model="message"/><br/> |
| 输入的内容为:{{message}} |
| <br/><br/> |
| |
| 性别:<input type="radio" name="sex" value="男" v-model="checked"/>男 |
| <input type="radio" name="sex" value="女" v-model="checked"/>女 |
| <br/> |
| 选择的性别为:{{checked}} |
| <br/><br/> |
| |
| 兴趣爱好: |
| <select v-model="selected"> |
| <option value="" disabled>--请选择--</option> |
| <option>羽毛球</option> |
| <option>钢琴</option> |
| </select> |
| <br/> |
| 选择的兴趣爱好为:{{selected}} |
| </div> |
| <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> |
| <script> |
| var vm = new Vue({ |
| el:"#app", |
| data:{ |
| message:"121", |
| checked:"男", |
| selected:"" |
| }, |
| methods:{ |
| |
| } |
| }); |
| </script> |
| </body> |
| </html> |
注意:v-model会忽略所有表单元素的value、checked、selected特性的初始值,而总是将Vue实例的数据作为数据来源。你应该通过JavaScript在组件的data选项中声明初始值
6、Vue组件
| <!DOCTYPE html> |
| <html lang="en" xmlns:v-bind="http://www.w3.org/1999/xhtml" xmlns:v-on="http://www.w3.org/1999/xhtml"> |
| <head> |
| <meta charset="UTF-8"> |
| <title>组件</title> |
| </head> |
| <body> |
| <div id="app"> |
| <my-component v-for="hobby in hobbys"></my-component> |
| </div> |
| <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> |
| <script> |
| |
| Vue.component("my-component",{ |
| template:"<li>{{hobby}}</li>" |
| }); |
| |
| var vm = new Vue({ |
| el:"#app", |
| data:{ |
| hobbys:["钢琴","篮球","羽毛球"] |
| } |
| |
| }); |
| </script> |
| </body> |
| </html> |
说明:
- Vue.component():注册组件
- my-component:自定义组件的名字
- template:组件的模板
- 使用props属性传递参数
向上面那样用组件没有任何意义,所以我们是需要传递参数到组件的,此时就需要使用props属性了。
| <!DOCTYPE html> |
| <html lang="en" xmlns:v-bind="http://www.w3.org/1999/xhtml" xmlns:v-on="http://www.w3.org/1999/xhtml"> |
| <head> |
| <meta charset="UTF-8"> |
| <title>组件</title> |
| </head> |
| <body> |
| <div id="app"> |
| <my-component v-for="hobby in hobbys" v-bind:test="hobby"></my-component> |
| </div> |
| <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> |
| <script> |
| |
| Vue.component("my-component",{ |
| props:["test"], |
| template:"<li>{{test}}</li>" |
| }); |
| |
| var vm = new Vue({ |
| el:"#app", |
| data:{ |
| hobbys:["钢琴","篮球","羽毛球"], |
| test:"" |
| } |
| |
| }); |
| </script> |
| </body> |
| </html><!DOCTYPE html> |
| <html lang="en" xmlns:v-bind="http://www.w3.org/1999/xhtml" xmlns:v-on="http://www.w3.org/1999/xhtml"> |
| <head> |
| <meta charset="UTF-8"> |
| <title>组件</title> |
| </head> |
| <body> |
| <div id="app"> |
| |
| <my-component v-for="hobby in hobbys" v-bind:test="hobby"></my-component> |
| </div> |
| <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> |
| <script> |
| |
| Vue.component("my-component",{ |
| props:["test"], |
| template:"<li>{{test}}</li>" |
| }); |
| |
| var vm = new Vue({ |
| el:"#app", |
| data:{ |
| hobbys:["钢琴","篮球","羽毛球"], |
| test:"" |
| } |
| |
| }); |
| </script> |
| </body> |
| </html> |
7、Axios异步通信
Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中,是基于ajax的异步通信。
特性:
- 从浏览器中创建 XMLHttpRequests
- 从 node.js 创建 http 请求
- 支持 Promise API
- 拦截请求和响应
- 转换请求数据和响应数据
- 取消请求
- 自动转换 JSON 数据
- 客户端支持防御 XSRF
| <!DOCTYPE html> |
| <html lang="en" xmlns:v-bind="http://www.w3.org/1999/xhtml" xmlns:v-on="http://www.w3.org/1999/xhtml"> |
| <head> |
| <meta charset="UTF-8"> |
| <title>组件</title> |
| </head> |
| <body> |
| <div id="app"> |
| <div>{{info.name}}</div> |
| <div>{{info.address.street}}</div> |
| <a v-bind:href="info.link">点我</a> |
| </div> |
| <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> |
| <script src="https://unpkg.com/axios/dist/axios.min.js"></script> |
| <script> |
| |
| |
| var vm = new Vue({ |
| el:"#app", |
| data(){ |
| |
| return{ |
| info:{ |
| name:null |
| } |
| } |
| }, |
| |
| mounted(){ |
| axios.get("../data.json").then(response=>(this.info=response.data)) |
| } |
| |
| }); |
| </script> |
| </body> |
| </html> |
8、计算属性
| <!DOCTYPE html> |
| <html lang="en" xmlns:v-bind="http://www.w3.org/1999/xhtml" xmlns:v-on="http://www.w3.org/1999/xhtml"> |
| <head> |
| <meta charset="UTF-8"> |
| <title>组件</title> |
| </head> |
| <body> |
| <div id="app"> |
| currentTime1:{{currentTime1()}}<br/> |
| currentTime2:{{currentTime2}} |
| </div> |
| <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> |
| <script> |
| |
| |
| var vm = new Vue({ |
| el:"#app", |
| data:{ |
| message:"" |
| }, |
| methods:{ |
| currentTime1:function (event) { |
| return Date.now(); |
| } |
| }, |
| computed:{ |
| currentTime2:function (event) { |
| return Date.now(); |
| } |
| } |
| |
| }); |
| </script> |
| </body> |
| </html> |
说明:
- methods:定义方法,调用方法使用currentTime1(),需要带括号
- computed:定义计算属性,调用属性使用currentTime2,不需要带括号;this.message是为了能够让currentTime2观察到数据变化而变化
- 如果在方法中的值发生了变化,则缓存就会刷新。可以在控制台使用vm.message="zixin",改变下数据的值,再次测试观察效果
结论:
调用方法时,每次都需要进行计算,既然有计算过程则必定产生系统开销,那如果这个结果是不经常变化的,此时就可以考虑将这个结果缓存起来,采用计算属性可以很方便的做到这一点,计算属性的主要特性就是为了将不经常变化的计算结果进行缓存,以节约我们的系统开销。
9、插槽slot
在Vue.js中我们使用<slot>
元素作为分发内容的出口,称为插槽,可以用用在组合组件的场景中。
| <!DOCTYPE html> |
| <html lang="en" xmlns:v-bind="http://www.w3.org/1999/xhtml" xmlns:v-on="http://www.w3.org/1999/xhtml"> |
| <head> |
| <meta charset="UTF-8"> |
| <title>slot插槽</title> |
| </head> |
| <body> |
| |
| <div id="app"> |
| <father> |
| <son-title slot="son-title" v-bind:title="title"></son-title> |
| <son-item slot="son-item" v-for="it in items" v-bind:item="it"></son-item> |
| </father> |
| </div> |
| <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> |
| <script> |
| |
| Vue.component("father",{ |
| template:'<div>'+ |
| '<slot name="son-title"></slot>' + |
| '<ul>'+ |
| '<slot name="son-item"></slot>'+ |
| '</ul>' + |
| '</div>' |
| }); |
| |
| Vue.component("son-title",{ |
| props:["title"], |
| template:"<p>{{title}}</p>" |
| }); |
| |
| Vue.component("son-item",{ |
| props:["item"], |
| template: "<li>{{item}}</li>" |
| }); |
| |
| var vm = new Vue({ |
| el:"#app", |
| data:{ |
| message:"", |
| title:"兴趣爱好", |
| item:"", |
| items:["羽毛球","篮球","钢琴"] |
| } |
| |
| }); |
| </script> |
| </body> |
| </html> |
10、自定义事件内容分发
| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <title>事件分发</title> |
| </head> |
| <body> |
| <div id="app"> |
| <father> |
| <son-title slot="son-title" v-bind:title="title"></son-title> |
| <son-item slot="son-item" v-for="(it,index) in items" :item="it" :key="index" :index="index"></son-item> |
| </father> |
| </div> |
| <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> |
| <script> |
| |
| Vue.component("father",{ |
| template:'<div>'+ |
| '<slot name="son-title"></slot>' + |
| '<ul>'+ |
| '<slot name="son-item"></slot>'+ |
| '</ul>' + |
| '</div>' |
| }); |
| |
| Vue.component("son-title",{ |
| props:["title"], |
| template:"<p>{{title}}</p>" |
| }); |
| Vue.component("son-item",{ |
| props:["item","index"], |
| template: "<li>{{item}}<button v-on:click='removeItem(index)'>删除</button></li>" |
| }); |
| |
| var vm = new Vue({ |
| el:"#app", |
| data:{ |
| message:"", |
| title:"兴趣爱好", |
| item:"", |
| items:["羽毛球","篮球","钢琴"] |
| }, |
| methods:{ |
| removeItem:function (index) { |
| console.log("要删除的为"+this.items[index]); |
| } |
| } |
| |
| }); |
| </script> |
| </body> |
| </html> |
通过上面的代码不难发现,数据项在Vue的实例中,但删除操作要在组件中完成,那么组件如何才能删除Vue实例中的数据呢?此时就涉及到参数传递与事件分发了,Vue为我们提供了自定义事件的功能 ,很好地帮助了我们解决了这个问题:使用this.$emit('自定义事件名',参数),操作过程如下:
- 在Vue的实例中,增加了methods对象并定义了一个名为removeItem的方法
| removeItem:function (index) { |
| console.log("要删除的为"+this.items[index]); |
| this.items.splice(index, 1); |
| } |
- 修改son-item代办内容组件的代码,增加一个删除按钮,并绑定事件
| template: "<li>{{item}}<button type='button' v-on:click='butClick(index)'>删除</button></li>", |
- 修改son-item代办内容组件的HTML代码,增加一个自定义事件,比如叫butClick,可以和组件的方法绑定,然后绑定到Vue的方法中
| methods:{ |
| butClick:function (index) { |
| this.$emit('remove', index); |
| } |
| } |
| |
| |
| <son-item slot="son-item" v-for="(it,index) in items" v-bind:item="it" v-bind:key="index" v-bind:index="index" v-on:remove="removeItem(index)"></son-item> |
全部代码:
| <!DOCTYPE html> |
| <html lang="en" xmlns:v-bind="http://www.w3.org/1999/xhtml" xmlns:v-on="http://www.w3.org/1999/xhtml"> |
| <head> |
| <meta charset="UTF-8"> |
| <title>事件分发</title> |
| <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> |
| </head> |
| <body> |
| <div id="app"> |
| <father> |
| <son-title slot="son-title" v-bind:title="title"></son-title> |
| <son-item slot="son-item" v-for="(it,index) in items" v-bind:item="it" v-bind:key="index" v-bind:index="index" v-on:remove="removeItem(index)"></son-item> |
| </father> |
| </div> |
| <script> |
| |
| Vue.component("father",{ |
| template:'<div>'+ |
| '<slot name="son-title"></slot>' + |
| '<ul>'+ |
| '<slot name="son-item"></slot>'+ |
| '</ul>' + |
| '</div>' |
| }); |
| |
| Vue.component("son-title",{ |
| props:["title"], |
| template:"<p>{{title}}</p>" |
| }); |
| Vue.component("son-item",{ |
| props:["item","index"], |
| template: "<li>{{item}}<button type='button' v-on:click='butClick(index)'>删除</button></li>", |
| methods:{ |
| butClick:function (index) { |
| this.$emit('remove', index); |
| } |
| } |
| }); |
| |
| var vm = new Vue({ |
| el:"#app", |
| data:{ |
| message:"", |
| title:"兴趣爱好", |
| item:"", |
| items:["羽毛球","篮球","钢琴"] |
| }, |
| methods:{ |
| removeItem:function (index) { |
| console.log("要删除的为"+this.items[index]); |
| this.items.splice(index, 1); |
| } |
| } |
| |
| }); |
| </script> |
| </body> |
| </html> |
11、第一个vue-cli程序
准备工作
-
下载和安装Node.js
官网下载后打开文件,然后无脑下一步,即可安装完成。
-
检查Node.js是否安装成功
在控制台输入node -v与npm -v,若输出版本号表示安装成功
-
安装Node.js淘宝镜像加速器(cnpm)
Node.js有时候下载会很慢,下载淘宝加速器,下载会快很多
| |
| npm install cnpm -g |
| |
| |
| npm install --registry=https://registry.npm.taobao.org |
建议npm下载失败再用cnpm下载,因为cnpm下载的文件有时候会出问题
- 安装vue-cli
| cnpm install vue-cli -g |
| |
| #测试是否安装成功 |
| #查看可以基于那些模块创建vue应用程序,通常我们选择webpack |
| vue list |
创建项目步骤
-
创建一个Vue项目,我们随便建立一个空的文件夹在电脑上,我这里在F盘下新建一个目录
F:\ideaworkplace\vuestudy
-
创建一个基于webpack模板的vue应用程序
| #这里的myvue时项目名称,可以根据自己的需求起名 |
| vue init webpack myvue |
一路都选择no即可

说明:
- Project name:项目名称,默认回车即可
- Project description:项目描述,默认回车即可
- Author:作者
- Vue build:vue构建方式
- Install vue-router:是否安装路由
- Use ESLint to lint your code:使用ESLint对代码进行规范
- Set up unit tests:是否设置单元测试
- Setup e2e tests with Nightwatch:是否使用Nightwatch设置e2e
- Should we run
npm install
for you after the project has been created? (recommended):是否在工程创建后就去跑 npm 安装依赖
创建好的项目目录

- 初始化项目并运行
| cd myvue |
| npm install |
| npm run dev |
-
先执行前面两个命令,下载依赖

-
执行完成后,目录多了很多依赖

-
再执行npm run dev
运行项目

-
运行成功页面如下

12、webpack学习使用
- 安装
| npm install webpack -g |
| npm install webpack-cli -g |
- 测试是否安装成功
- webpack -v
- webpack-cli -v

- 配置
创建webpack.config.js
配置文件
- entry:入口文件,指定WebPack用那个文件作为项目的入口
- output:输出,指定WebPack把处理完成的文件防止到指定路径
- module:模块,用于处理各种类型的文件
- plugins:插件,如:热更新、代码重用等
- resovle:设置路径指向
- watch:监听,用于设置文件改动后直接打包
| modules.exports = { |
| entry:"", |
| output:{ |
| path:"", |
| filename:"" |
| }, |
| modules:{ |
| loaders:{ |
| {test:/\.js$/,loader:""} |
| } |
| }, |
| plugins:{}, |
| resolve:{}, |
| watch:true |
| } |
| |
- 使用webpack
- 创建一个项目(空文件夹,用idea打开)
- 创建一个名为modules的目录,用于放置JS模块等资源文件
- 在modules下创建模块文件,如hello.js,用于编写JS模块相关代码
| |
| exports.sayHi = function(){ |
| document.write("<div>Hello WebPack!</div>") |
| } |
- 在modules下创建一个名为main.js的入口文件,用于打包时设置entry属性
| |
| var hello = require("./hello"); |
| hello.sayHi(); |
- 在项目目录下创建webpack.config.js配置文件
| module.exports = { |
| entry:"./modules/main.js", |
| output:{ |
| filename:"./js/bundle.js" |
| } |
| }; |
| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <title>测试引入打包后的js文件</title> |
| <script src="dist/js/bundle.js"></script> |
| </head> |
| <body> |
| |
| </body> |
| </html> |
- 运行index.html文件,看效果

13、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 |
此时遇到安装失败如下

原因是npm版本过高,此时需要降低npm的版本,执行执行下面命令
| |
| npm install npm@6.14.10 -g |
注意:如果在一个模块化工程中使用它,必须要通过Vue.use()明确地安装路由功能
| import Vue from 'vue' |
| import VueRouter from 'vue-router' |
| |
| Vue.use(VueRouter); |
- 测试
- 先删除没有用的东西
- components目录下存放我们自己编写的组件
- 定义一个Content.vue组件
| <template> |
| <div> |
| <h1>内容页</h1> |
| </div> |
| </template> |
| |
| <script> |
| export default { |
| name:'Content' |
| } |
| </script> |
| <style scoped> |
| |
| </style> |
| <template> |
| <div> |
| <h1>首页</h1> |
| </div> |
| </template> |
| |
| <script> |
| export default { |
| name: "Main" |
| } |
| </script> |
| |
| <style scoped> |
| |
| </style> |
- 安装路由,在src目录下,新建一个文件夹: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 |
| }, |
| |
| ] |
| }); |
| import Vue from 'vue' |
| import App from './App' |
| |
| |
| import router from './router' |
| |
| Vue.use(router); |
| |
| new Vue({ |
| el: '#app', |
| |
| router, |
| render:h=>h(App) |
| }); |
| <template> |
| <div id="app"> |
| <h1>路由首页</h1> |
| <router-link to="/main">首页</router-link> |
| <router-link to="/content">内容页</router-link> |
| <router-view></router-view> |
| </div> |
| </template> |
| |
| <script> |
| |
| export default { |
| name: 'App', |
| components: { |
| } |
| } |
| </script> |
| |
| <style> |
| |
| </style> |
- 测试,发现了下面的错误

问题原因:安装了最新的vue-router,版本大概是4.0.12,我最开始安装vuecli没有指定版本
解决方法:降低vue-router的版本,这里我换成了3.0.1
| |
| npm uninstall vue-router |
| |
| npm i vue-router@3.0.1 |
- 重新运行,得到如下运行成功的页面

14、vue+elementUI
创建工程(命令行都要使用管理员模式运行)
-
创建一个名为hellovue的工程vue init webpack hellovue
-
安装依赖,我们需要安装vue-router、element-ui、sass-loader和node-sass四个插件
| |
| cd hellovue |
| |
| |
| npm install vue-router --save |
| |
| |
| npm i element-ui -S |
| |
| |
| npm install |
| |
| |
| cnpm install sass-loader node-sass --save-dev |
| |
| |
| npm run dev |
- npm命令解释
npm install moduleName
:安装模块到项目目录下
npm installl -g moduleName
:-g的意思是将模块安装到全局,具体安装到磁盘那个位置,要看npm config prefix的位置
npm install -save moduleName
:--save的意思是将模块安装到项目目录下,并在package文件的dependencies节点写入依赖,-S为该命令的缩写
npm install --save-dev moduleName
:--save-dev的意思是将模块安装到项目目录下,并在package文件的devDependencies节点写入依赖,-D为该命令的缩写
-
用idea打开项目,并删除多余的东西,重新使用npm run dev
运行项目,运行结果如下

-
测试
| <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> |
| <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> |
| <e1-menu-item-group> |
| <el-menu-item index="2-1">分类管理</el-menu-item> |
| <el-menu-item index="2-2">内容列表</el-menu-item> |
| </e1-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 lang="scss"> |
| .el-header { |
| background-color: #048bd1; |
| color: #333; |
| line-height: 60px; |
| } |
| |
| .el-aside { |
| color: #333; |
| } |
| </style> |
| <template> |
| <div> |
| <h1>个人信息</h1> |
| </div> |
| </template> |
| |
| <script> |
| export default { |
| name: "Profile" |
| } |
| </script> |
| |
| <style scoped> |
| |
| </style> |
| <template> |
| <div> |
| <h1>用户列表</h1> |
| </div> |
| </template> |
| |
| <script> |
| export default { |
| name: "UserList" |
| } |
| </script> |
| |
| <style scoped> |
| |
| </style> |
| import Vue from 'vue' |
| import Router from 'vue-router' |
| |
| |
| import Main from "../components/Main"; |
| import Login from "../components/Login"; |
| import Profile from "../components/user/Profile"; |
| import UserList from "../components/user/UserList"; |
| |
| |
| Vue.use(Router); |
| |
| let router = new Router({ |
| routes:[ |
| { |
| path:'/main', |
| name:'Main', |
| component:Main, |
| children:[ |
| { |
| path:'/user/profile', |
| name:'Profile', |
| component:Profile, |
| }, |
| { |
| path:'/user/list', |
| name:'UserList', |
| component:UserList, |
| } |
| ] |
| }, |
| { |
| path: '/login', |
| name:'Login', |
| component: Login |
| } |
| ] |
| }); |
| export default router |
| import Vue from 'vue' |
| |
| import App from './App' |
| |
| import ElementUI from 'element-ui'; |
| import 'element-ui/lib/theme-chalk/index.css'; |
| |
| |
| import router from './router'; |
| |
| Vue.use(ElementUI); |
| Vue.use(router); |
| |
| new Vue({ |
| el: '#app', |
| router, |
| render: h => h(App) |
| }); |
| <template> |
| <div id="app"> |
| <router-view/> |
| </div> |
| </template> |
| |
| <script> |
| |
| export default { |
| name: 'App' |
| } |
| </script> |
| |
| <style> |
| |
| </style> |
运行时遇到问题

原因:因为node-loader版本过高导致的问题,我们尝试将版本降低到@4.14.1
解决方法:直接使用命令:npm install sass-loader@4.14.1 --save-dev
降低版本号,再进行npm run dev即可运行
15、路由嵌套
- 如上面的Main.vue页面中代码,点击这些链接,右边显示对应的页面
| <!--导航栏:用于在右边显示不同的页面-> |
| <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-main> |
| <router-view/> |
| </el-main> |
- 在router目录下的index.js中的routes数组,在component为Main中添加如下代码
| children:[ |
| { |
| path:'/user/profile', |
| name:'Profile', |
| component:Profile, |
| }, |
| { |
| path:'/user/list', |
| name:'UserList', |
| component:UserList, |
| } |
| ] |
注意:必须要引入对应的组件
16、参数传递及重定向
- 参数传递
方式一
| <!--有参数时,必须使用对象,同时to必须绑定--> |
| <router-link :to="{name:'Profile',params:{id:1}}">个人信息</router-link> |
- index.js中Profile组件的代码修改为如下:
| { |
| path:'/user/profile/:id', |
| name:'Profile', |
| component:Profile, |
| }, |
注意:参数名必须与传过来的一致
方式二
- 在Login.vue中的onSubmit方法跳转到main页面时添加参数:
| onSubmit(formName) { |
| //为表单绑定验证功能 |
| this.$refs[formName].validate((valid) => { |
| if (valid) { |
| //使用vue-router路由到指定页面,该方式称之为编程式导航 |
| this.$router.push("/main/"+this.form.username); |
| } else { |
| this.dialogVisible = true; |
| return false; |
| } |
| }); |
| }, |
- 在路由主文件中的Main路由对象中,开启props,同时在path的后面添加/:name
| { |
| path:'/main/:name', |
| name:'Main', |
| component:Main, |
| props:true, |
| children:[ |
| { |
| path:'/user/profile/:id', |
| name:'Profile', |
| component:Profile, |
| }, |
| { |
| path:'/user/list', |
| name:'UserList', |
| component:UserList, |
| }, |
| { |
| path:'/goTop', |
| redirect:'/main' |
| } |
| ] |
| }, |
| { |
| path: '/login', |
| name:'Login', |
| component: Login |
| }, |
| { |
| path: '*', |
| name:'NotFound', |
| component: NotFound |
| } |
| <script> |
| export default { |
| name: "Main", |
| props:["name"] |
| } |
| </script> |
- 重定向
| <el-submenu index="3"> |
| <template slot="title"><i class="el-icon-caret-right"></i>系统管理</template> |
| <e1-menu-item-group> |
| <el-menu-item index="3-1"> |
| <router-link to="/goTop">回到首页</router-link> |
| </el-menu-item> |
| </e1-menu-item-group> |
| </el-submenu> |
- 在index.js中的children中添加如下代码
| { |
| path:'/goTop', |
| redirect:'/main' |
| } |
17、404和路由钩子
路由模式有两种
修改路由配置,代码如下:
| export default new VueRouter({ |
| mode:'history', |
| routes:[ |
| ] |
| }) |
处理404
- 在component文件夹下创建一个名为NotFound.vue的视图组件,代码如下:
| <template> |
| <div> |
| 页面不存在,请重试 |
| </div> |
| </template> |
| |
| <script> |
| export default { |
| name:'NotFound' |
| } |
| </script> |
| <style scoped> |
| |
| </style> |
| { |
| path: '*', |
| name:'404', |
| component: NotFound |
| } |
18.路由钩子与异步请求
- 路由钩子
beforeRouteEnter:在渲染该组件的第一营路由被confirm前调用,不能获取组件实例this
,因为当守卫执行前,组件实例还没被创建
beforeRouteLeave:导航离开该组件的对应路由时调用,可以访问组件实例this
-
to: Route: 即将要进入的目标路由对象
-
from: Route: 当前导航正要离开的路由
-
next:理由的控制参数
- next():跳入下一个页面
- next('/path'):改变路由的跳转方向,使其跳到另一个路由
- next(false):返回原来的页面
- next((vm)=>{})仅在beforeRouteEnter中可用,vm使组件实例
测试:
| <template> |
| <div> |
| <h1>个人信息</h1> |
| {{$route.params.id}} |
| </div> |
| </template> |
| |
| <script> |
| export default { |
| name: "Profile", |
| beforeRouteEnter(to, from, next){ |
| console.log("进入组件时路由调用"); |
| next(); |
| }, |
| beforeRouteLeave(to, from, next){ |
| console.log("离开组件时路由调用"); |
| next(); |
| } |
| } |
| </script> |
- 运行测试
点击个人信息链接时执行beforeRouteEnter,点击其他链接时执行beforeRouteLeave

- 在钩子函数中使用异步请求
- 安装Axios
cnpm install axios --save
- 在main.js中引用Axios
| import axios from 'axios' |
| |
| Vue.prototype.axios = axios; |
- 准备数据:只有我们的static目录下的文件是可以被访问到的,所以我们把静态文件放入该目录下
| |
| static/jsondata/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/" |
| } |
| ] |
| } |
- 在Profile.vue页面中的methods{}中添加一个getData方法
| methods:{ |
| getData(){ |
| this.axios({ |
| method:'get', |
| url:'http://localhost:8080/static/jsondata/data.json' |
| }).then(response=>{ |
| console.log(response); |
| }) |
| } |
| } |
- 在beforeRouteEnter中进行异步请求
| beforeRouteEnter(to, from, next){ |
| console.log("进入组件时路由调用"); |
| next((vm)=>{ |
| vm.getData(); |
| }); |
| |
| }, |
- 运行测试,遇到下面的问题

原因是:将this.axios写成了this.$axios
修改后成功获取到数据

【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 【.NET】调用本地 Deepseek 模型
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
· DeepSeek “源神”启动!「GitHub 热点速览」
· 上周热点回顾(2.17-2.23)