Vue入门
1.准备工作
1.1导入js
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
1. 2初始化Vue
var app = new Vue({
el: '#app',
data: {},
methods:{}
})
-
el:用来指定Vue的作用范围,推荐id选择器
-
data:用来给Vue中定义数据,如:
data: { message:"" }
data的形式主要有三种:
(1)字符串:""
(2)数组:[]
(3)对象:{}
注:数组和对象可以相互嵌套
-
methods:用来定义Vue中的函数
-
注意:页面渲染有先后顺序,js一定要在组件之后
2.Vue语法
2.1 v-text
-
用来将data中的数据以文本的形式直接渲染到标签内部,同时,{{}}也有这个效果,两者的区别是:
(1)v-text会将空间中原有的值覆盖
(2)v-text可以避免由于网络波动出现的闪烁问题
2.2 v-html
- 与v-text的区别是,从data中取值,先渲染成html再放在页面之中。
2.3 v-on
Vue事件绑定,事件的触发条件有很多,如click,move…,形如
vue-on:'触发条件'="方法名"
其中,在Vue的实例对象中的methods中,有两种定义方式
//第一种定义形式
方法名:function(){
方法体
}
//第二种定义形式
方法名(){
方法体
}
其次,v-on有简写方式,即将 v-on: 变化为@,注意冒号
再者,函数可以传参,主要用于将后台数据传递给函数
最后,一个示例
//Vue对象
var app = new Vue({
el:"#wrap",
data:{},
methods:{
getImg(id){
方法体
}
}
});
<!--调用-->
<a href="javascript:;" @click="getImg(id)">换一张图片</a>
2.4 v-show
用来控制页面中某个标签元素是否展示,底层是display
,true显示,false不显示,
此外,可以写表达式与变量,形如
v-show = "age>=20"
2.5 v-if
用来控制页面元素是否展示,底层是控制DOM元素
,true显示,false不显示
2.6 v-bind
用来给标签绑定相应的属性
如在Vue实例对象的data中设置了想应变量,可用v-bind:标签属性="变量名"进行绑定,形如:
v-bind:class="css"
简化写法,将 v-bind: 属性名简化为 :属性名
2.7 v-for
用来进行循环和遍历
(1)遍历对象
(index,key,value) in 对象
(2)遍历数组
//u是指别名
(u,index) in 数组
(3)遍历数组中对象
(u,index) in 数组
//这时,u就是一个个对象,通过u.属性来取值
注意:Vue建议在使用较为复杂的v-for时,给组件用v-bind:key绑定一个数组中的唯一值,如:
v-bind:key="user.id"
2.8 v-model
实现组件中的值和Vue中data:{}中的值进行双向绑定,即双方任意一个地方修改,另一个也跟着修改,如:
data:{
user:{
sex:"男"
},
},
<td valign="middle" align="left">
男
<input type="radio" class="inputgri" v-model="user.sex" value="男" checked="checked"/>
女
<input type="radio" class="inputgri" v-model="user.sex" value="女"/>
</td>
3.Vue中常用的事件修饰符
与事件连用,来决定事件是否触发
3.1 stop
用来阻止事件冒泡
-
何为事件冒泡?
当点击标签内部的相关事件时,当事件执行完成之后,会向外层父元素进行传递,如果外部元素也有相应的事件就会执行
-
如何解决?
在事件后加上.stop
-
示例
vue-on:click.stop="btnClick"
3.2 prevent
阻止标签的默认事件
-
何为默认事件?
如给a标签添加了点击事件,执行事件后会继续执行标签的默认事件,加上prevent后会组织
-
注意:
修饰符可以串联,形如
vue-on:click.stop.prevent="btnClick"
3.3 self
只关心自身事件的触发,不监听事件冒泡,比如在
上设置,则其中所有标签都不会冒泡,不用重复添加stop3.4 once
让指定的事件只触发一次
-
按键修饰符举例
用来与键盘中按键事件绑定在一起,用来修饰特定的按键事件的修饰符
-
.enter
用来绑定回车按键
@keyup.enter="函数名"
-
.tab
-
.delete
-
4.计算属性
var vm = new Vue({
el:'#app',
data:{
message:"hello"
},
methods:{
currentTime1:function(){
return Date.now();
}
},
//computed中的方法名如果与methods中的相同,就不会被调用到
//调用methods中的方法是,currentTime1()
//调用computed中的方法是,currentTime2,是调用属性的方法来调用
//调用currentTime2,会被缓存,不会变,但当其中的某一个属性改变时,会刷新,如this.message
computed:{
currentTime2:function(){
this.message;
return Date.now();
}
}
});
5.ES6新特性
5.1变量的声明 var ==> let、const
- var声明的是全局变量,会出现变量混淆污染问题
- let作用:用来声明一个局部变量
- const作用:用来定义一个常量,变量一旦被定义,不能被修改
- 使用const定义对象,对象的地址不能改变,对象中的值可以修改
5.2字符串模板
-
通过反引号使用字符串模板
let html = `<select name="Country"> <option value="China" selected>中国</option> <option value="US">美国</option> <option value="Switzerland">瑞士</option> <option value="India">印度</option> </select>`
5.3匿名函数简化写法 ()=>{}
- 匿名函数存在自己的this,箭头函数没有自己的this
5.4对象定义扩展
-
当对象属性名和属性赋值变量名一致时,可以省略变量名不写
//es5 let name = "小黑"; let age = 23; const user = {name:name,age:age}; //es6 const stu ={name,age};
6.Vue生命周期
- Init Events & Lifecycle
- 使用new关键字进行vue初始化,在这个时候开始完成的是vue实例自身对象内部使用相关事件和对应生命周期函数初始化。
- beforeCreate():自己定义的数据、方法并没有初始化。
- Init injections & reactivity
- 向Vue中注入data、methods、computed…
- created():这个函数执行的时候,自定义的属性方法已经注入,并完成了语法校验
- Has “el” option?
- Yes(一般都有)
- Has “template” option?
- Yes
- No 把"el"包含的HTML编译成模板template
- Has “template” option?
- Yes(一般都有)
- beforeMount():仅仅把HTML编译成模板,还没有渲染。
- Create vm.$el and replace “el” with it
- 创建虚拟的DOM来替换"el"模板
- mounted():这个函数执行时,拿到了页面渲染之后的页面。
至此,Vue初始化完成,进入运行阶段
- Mounted:运行过程中(不断循环)
- beforeUpdate():当数据改变时,触发,数据改变了,但是页面还没改变
- Virtual DOM re-render and patch:虚拟DOM的重新渲染和修改
- updated():data中数据与页面中数据一致
进入销毁阶段
- when vm.$destroy() is called
- beforeDestroy():在这个函数执行的时候,vue实例中的data,methods都还未销毁
- Tear down watchers, child components and event listeners:当子组件、事件监听等都销毁之后
- destroyed():此函数触发时,全部都清空
7.Axios
建议在created(){}周期函数中写ajax请求,不必二次渲染
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
axios.get("url?key=value&key1=value1").then(res=>{ console.log(res.data); //回调函数,获得响应数据 }).catch(function(error){ console.log(error); //如果有错误,就执行catch });
var app = new Vue({
el:"#wrap",
data:{
url:"",
user:{
sex:"男"
},
code:""
},
methods:{
//更换验证码
getImg(){
var _this = this;
axios.get("http://localhost:8080/user/getImage?time="+ Math.random()).then(res=>{
console.log(res.data);
_this.url = res.data;
});
},
//用来注册用户信息
register(){
axios.post("http://localhost:8080/user/register?code="+this.code,this.user).then(res=>{
console.log(res.data);
if(res.data.state){
alert(res.data.msg + ",点击确定跳转至登录页面");
location.href="http://localhost:8080/user/login.html"
}else {
alert(res.data.msg);
}
this.getImg();
});
}
},
created(){
this.getImg();
}
});
8.Vue组件
8.1总览
传统Vue中标准开发方式 ==>
SPA
Vue中标准开发,Single Page (Web)Application,单页面web应用,即Veu推荐我们开发采用单页面开发,一个项目中只有一张页面。
单页面好处:一个页面只有一个Vue实例,称为Vue根实例
如果单页面,如何开发?
- 解决方案1:将所有的业务实现写在一个页面中
- 不便于维护
- 考虑变量命名的冲突问题
- 解决方案2:Vue组件 component
- 一切皆组件
减少Vue实例对象中的代码量,可以根据不同业务功能将页面划分为不同的多个组件,然后由多个组件去完成整个页面的布局
8.2全局组件的注册
注册到Vue中根实例上的组件,一旦定义,可以在任意组件中使用,
一般用在公共页面
-
注册位置
在中注册
Vue.component('名称',{组件配置对象})
-
组件配置对象
形如:template:html代码
注意:html代码必须有且只能有一个容器,容器中可以包含其他容器。
-
调用
使用<名称></名称>即可,可以在Vue实例范围任意位置使用
-
如果名称是驼峰命名,则调用应采用 大写字母 转 -小写字母 的方式,如
userLogin -> user-login
<body>
<div id="app">
<login></login>
</div>
</body>
<script>
Vue.component('login',{
template:"<div><h3>用户登录</h3></div>"
});
const app = new Vue({
el:"#app",
data:{
},
method:{
},
computed:{
}
});
</script>
8.3局部组件注册
-
定义
通多将组件注册给Vue实例中一个components属性来完成组件注册
-
形如:
<!--第一种写法--> <body> <div id="app"> <login></login> </div> </body> <script> const app = new Vue({ el:"#app", data:{ }, method:{ }, computed:{ }, components:{ login:{ template:"<div><h3>用户登录</h3></div>" } } }); </script>
<!--第二种写法--> <body> <div id="app"> <login></login> </div> </body> <script> const login = { template:"<div><h3>用户登录</h3></div>" } const app = new Vue({ el:"#app", data:{ }, method:{ }, computed:{ }, components:{ //es5中这么写 //login:login, //es6中可以直接写 login, } }); </script>
-
调用:
<login></login>
8.4组件中定义data,methods,computed以及生命周期函数
<body>
<div id="app">
<users></users>
</div>
</body>
<script>
//定义用户列表组件
const users = {
//用来定义组件html内容
template:"<div><h3>用户列表</h3><h3>{{count}}</h3><button @click='changeCount'>+</button><h3>{{countSqrt}}</h3><ul><li v-for="item in items">{{item}}</li></ul></div>",
//用来定义组件的数据,只能在当前组件使用
data(){
return {
count:0,
items:[],
}
},
//用来定义当前组件定义一系列事件
method:{
changeCount(){
this.count++;
}
},
//用来定义当前组件定义一系列计算属性,用来对页面中结果进行二次计算处理的时候
computed:{
countSqrt(){
return this.count*this.count;
}
},
//组件已经注入了自定义的数据与方法,他有所有的生命周期函数
created:{
axios.get("/xxx").then(res=>{
this.items = res.data;
})
}
}
const app = new Vue({
el:"#app",
data:{
},
method:{
},
computed:{
},
components:{
users,
}
});
</script>
8.5Vue中父子组件数据传递(Prop使用)
父组件向子组件的数据传递
- 静态数据
<body>
<div id="app">
<!--传递静态数据,在组件上传递写死的数据-->
<login aa="我是aa"></login>
</div>
</body>
<script>
const login = {
template:"<div><h3>登录界面{{aa}}</h3></div>",
data(){
return{
name:"小黑",
//如何定义的数据与外部传递的冲突,以prop为主。
aa:"我是aa2",
//把外部传过来的aa赋值给自己的数据,inneraa
inneraa:this.aa,
},
props:['aa'],//用来接收组件传来的数据 aa
}
}
//父组件向子组件传递数据使用prop属性
const app = new Vue({
el:"#app",
data:{
name:"小黑"
},
method:{
},
computed:{
},
components:{
login,
}
});
</script>
- 动态数据
<body>
<div id="app">
<!--传递动态数据,title绑定Vue实例中的name-->
<login v-bind:title="name"></login>
</div>
</body>
<script>
const login = {
template:"<div><h3>登录界面{{mytitle}}</h3></div>",
data(){
return{
mytitle:this.title
}
},
props:['title']
}
const app = new Vue({
el:"#app",
data:{
name:"小黑"
},
method:{
},
computed:{
},
components:{
login,
}
});
</script>
-
单向数据流
所有的prop都使得父子prop之间形成了一个单向下行绑定:父级prop的更新会向下流到子组件中,但是反过来不行。这样会防止子组件意外变更父组件的状态。
8.6子组件调用父组件中定义的事件
想从
子组件向父组件传递数据
,但是并没有提供这种方法,通过事件传递来完成这个目的。具体来讲是,子组件调用父组件中的方法,然后通过回调函数给父组件传值。
<body>
<div id="app">
<!--传递父组件中的事件,传递一个aa事件,@别名="父组件中指定事件"-->
<login @aa="findAll" @bb="givenum"></login>
</div>
</body>
<script>
const login = {
template:"<div><h3>登录界面</h3><button @click='test'>点我触发事件</button></div>",
data(){
return{
msg:"子组件的数据"
};
},
methods:{
test(){
alert("子组件中的事件");
//调用父组件中的代码
this.$emit('aa');
//调用事件并传递参数
this.$emit('bb',this.msg);
}
}
}
const app = new Vue({
el:"#app",
data:{
msg:"",
user:{id:1,name:"小黑",age:22}
},
method:{
//子组件调用父组件事件
findAll(){
alert("父组件中的事件");
},
//子组件向父组件传值
givenum(msg){
alert("子组件向父组件传递的参数"+msg);
this.msg = msg;
}
},
components:{
login,
}
});
</script>
8.7组件中的插槽使用(slot)
插槽必须配合组件才能使用,用来扩展自定义组件变得更加灵活,用来个性定制自己的组件
<body>
<div id="app">
<login><h5>我是用户自定义的slot</h5></login>
</div>
</body>
<script>
const login = {
//如果用户自己写了就替换,没有写就用默认的span
//这样写会把”<h5>我是用户自定义的slot</h5>“插到所有的slot(如果有多个)中
template:"<div><h3>登录界面</h3><slot><span>默认扩展</span></slot></div>",
data(){
},
methods:{
}
}
const app = new Vue({
el:"#app",
data:{
msg:"slot插槽使用"
},
method:{
},
components:{
login,
}
});
</script>
<body>
<div id="app">
<!--具名插槽-->
<login><h5 slot="aa">我是用户自定义的slot</h5></login>
</div>
</body>
<script>
const login = {
//如果空间没指定具体的slot,就用默认
template:"<div><h3>登录界面</h3><slot name="aa"><span>默认扩展</span></slot></div>",
data(){
},
methods:{
}
}
const app = new Vue({
el:"#app",
data:{
msg:"slot插槽使用"
},
method:{
},
components:{
login,
}
});
</script>
9.Vue路由
9.1路由
Vue推荐整个工程只有一张页面,但是如果将所有的功能都展示在一个页面中,是不切实际的且没有用户体验的,因此我们需要用到路由。路由帮我们
管理子组件
,并为每一个子组件分配一个url,当有外部请求时,先经过路由,如果匹配,则在唯一的一张页面中渲染出相应的组件,实现动态变换。
9.2安装
https://unpkg.com/vue-routre/dist/vue-router.js
此外,在引入时,vue的引入必须在路由的引用之前
9.3基本使用
<body>
<div id="app">
<h2>
{{msg}}
</h2>
<!--指定路由组件在哪里展示-->
<router-view></router-view>
<!--路径中,#代表哈希路由,表示在原来的路径上加上我们配置的路由-->
<a href="#/users">用户管理</a>
<a href="#/emps">员工管理</a>
</div>
</body>
<script>
//创建子组件
const users = {
template:"<div><h3>用户管理</h3></div>"
}
const emps = {
template:"<div><h3>员工管理</h3></div>"
}
//创建路由对象
const router = new VueRouter({
//用来定义一系列规则
routes:[
//用来定义具体的某个路由组件的规则
//path指访问路径,component指定路径对应的组件
{path:'/users',component:users},
{path:'/emps',component:emps}
]
});
const app = new Vue({
el:"#app",
data:{
msg:"路由的使用",
},
methods:{},
computed:{},
router:router,//用来注册路由的配置
});
</script>
9.4 router-link标签的使用
在a标签中写路径还需要写#,Vue推出 router-link标签来替换a标签的使用,书写路径时不需要写#,
to
属性用来指定路径,tag
属性用来指定router-link底层的渲染,如可以指定为button等,默认为a标签。
<body>
<div id="app">
<h2>
{{msg}}
</h2>
<router-view></router-view>
<!--两者的对比-->
<a href="#/users">用户管理</a>
<router-link to="/emps" tag="button">员工管理(router-link)</router-link>
</div>
</body>
<script>
const users = {
template:"<div><h3>用户管理</h3></div>"
}
const emps = {
template:"<div><h3>员工管理</h3></div>"
}
const router = new VueRouter({
routes:[
{path:'/users',component:users},
{path:'/emps',component:emps}
]
});
const app = new Vue({
el:"#app",
data:{
msg:"router-link",
},
methods:{},
computed:{},
router,//es6写法
});
</script>
9.5默认路由
给 ‘/’ 一个访问组件,但是一般不这么做,会让用户产生混淆,可以给’/'绑定一个重定向事件。
<body>
<div id="app">
<h2>
{{msg}}
</h2>
<router-view></router-view>
<a href="#/users">用户管理</a>
<a href="#/emps">员工管理</a>
</div>
</body>
<script>
const users = {
template:"<div><h3>用户管理</h3></div>"
}
const emps = {
template:"<div><h3>员工管理</h3></div>"
}
const router = new VueRouter({
routes:[
//默认路由
//{path:'/',component:users},
//重定向
{path:'/',redirect:'/users'},
{path:'/users',component:users},
{path:'/emps',component:emps}
]
});
const app = new Vue({
el:"#app",
data:{
msg:"默认路由",
},
methods:{},
computed:{},
router,
});
</script>
9.6路由动态切换两种方式
-
通过标签方式切换
不好之处:点击标签直接切换,实际应用中通常需要处理业务才切换。
<body> <div id="app"> <h2> {{msg}} </h2> <router-view></router-view> <a href="#/users">用户管理</a> <a href="#/emps">员工管理</a> <router-link to="/users">用户管理(router-link)</router-link> <router-link to="/emps">员工管理(router-link)</router-link> </div> </body> <script> const users = { template:"<div><h3>用户管理</h3></div>" } const emps = { template:"<div><h3>员工管理</h3></div>" } const router = new VueRouter({ routes:[ //默认路由 //{path:'/',component:users}, //重定向 {path:'/',redirect:'/users'}, {path:'/users',component:users}, {path:'/emps',component:emps} ] }); const app = new Vue({ el:"#app", data:{ msg:"默认路由", }, methods:{}, computed:{}, router, }); </script>
-
通过js代码方式切换
好处:可以通过名称进行路由切换
<body> <div id="app"> <h2> {{msg}} </h2> <router-view></router-view> <button @click="test"> 测试路由切换 </button> </div> </body> <script> const users = { template:"<div><h3>用户管理</h3></div>" } const emps = { template:"<div><h3>员工管理</h3></div>" } const router = new VueRouter({ routes:[ {path:'/',redirect:'/users'}, {path:'/users',component:users}, //可以通过name起别名,名字唯一 {path:'/emps',name:"emps",component:emps} ] }); const app = new Vue({ el:"#app", data:{ msg:"默认路由", }, methods:{ test(){ //代表要切换路由路径 //this.$router.push({path:'/users'}); //可以通过别名来切换 //$router是路由管理器 this.$router.push({name:"emps"});//[推荐用名称,解耦] } }, computed:{}, router, }); </script>
9.7路由中的参数传递
<body>
<div id="app">
<h2>
{{msg}}
</h2>
<router-view></router-view>
<!--传递参数-->
<a href="#/users?deptId=21">用户管理</a>
<a href="#/emps">员工管理</a>
</div>
</body>
<script>
const users = {
template:"<div><h3>用户管理</h3></div>",
data(){
return{
depId:"",
}
},
method:{
},
created(){
//获取路由路径中的参数,获取queryString(url?key=value)中传递的参数
//$route是当前路由,注意区分与$router路由管理器的区别
this.depId = this.$route.query.deptId
}
}
const emps = {
template:"<div><h3>员工管理</h3></div>"
}
const router = new VueRouter({
routes:[
{path:'/',redirect:'/users'},
{path:'/users',component:users},
{path:'/emps',component:emps}
]
});
const app = new Vue({
el:"#app",
data:{
msg:"参数传递",
},
methods:{},
computed:{},
router,
});
</script>
rest风格传参
<body>
<div id="app">
<h2>
{{msg}}
</h2>
<router-view></router-view>
<a href="#/users">用户管理</a>
<!--模拟rest风格-->
<a href="#/emps/11/小黑">员工管理</a>
</div>
</body>
<script>
const users = {
template:"<div><h3>用户管理</h3></div>"
}
const emps = {
template:"<div><h3>员工管理</h3></div>",
data(){
return{
empId:"",
name:"",
}
},
method:{
},
created(){
//通过this.$route.params.属性拿到,
this.empId = this.$route.params.id;
this.name = this.$route.params.name;
}
}
const router = new VueRouter({
routes:[
{path:'/',redirect:'/users'},
{path:'/users',component:users},
//rest风格通过这种方式传递
{path:'/emps/:id/:name',component:emps}
]
});
const app = new Vue({
el:"#app",
data:{
msg:"参数传递",
},
methods:{},
computed:{},
router,
});
</script>
9.8嵌套路由
把一个组件作为另一个组件的子组件来进行展示,而非跳转到另一个页面
注释中标明的序号表示数据传递过程中的顺序
<body>
<div id="app">
<h2>
{{msg}}
</h2>
<router-view></router-view>
<router-link to="/products">商品管理</router-link>
</div>
</body>
<template id="productsTemplate">
<div>
<h3>
商品列表
</h3>
<!--访问嵌套路由中的子组件-->
<a href="#/products/add">
<table>
<tr>
<th>编号</th>
<th>名称</th>
<th>价格</th>
<th>操作</th>
</tr>
<tr>
<td>1</td>
<td>手机</td>
<td>3000</td>
<!--4.想点击修改时,将数据传过去进行显示,参数是一个用户对象-->
<td><a href="..">删除</a><a href="javascript:;" @click="editRow({id:1,name:'手机',price:'20'})">修改</a></td>
</tr>
</table>
<!--router-view用来展示子组件路由-->
<router-view></router-view>
</div>
</template>
<script>
//3.父组件
const products = {
template:"#productsTemplate",
data(){
return {
},
},
method:{
//5.注意,方法不能定义到edit中,要定义到他的父组件中
editRow(user){
console.log(user);
//写query就相当于url携带参数,传给子组件
this.$router.push({path:'/product/edit',query:user});
}
}
};
//创建添加商品信息的子组件
const add = {
template:"<div><form action="">商品名称:<input type='text'><br>商品价格:<input type='text'><br><input type='button' value='保存商品'></form></div>"
};
const edit = {
//7.在里面进行v-model双向绑定
template:"<div><form action="" v-model='user.name'>商品名称:<input type='text'><br>商品价格:<input type='text' v-model='user.price'><br><input type='button' value='保存商品'></form></div>",
data(){
return{
user:{},
}
},
methods:{
},
created(){
//6.拿到父组件传递的参数
this.user = this.$route.query;
}
}
const router = new VueRouter({
routes:[
{path:'/',redirect:products},
{
//1.父组件
path:'/products',
name:'products',
component:products,
//2.父组件中的子路由,子路由path属性不能使用/开头
children:[
{path:"add",component:add},
{path:"edit",component:edit}
]
},
]
});
const app = new Vue({
el:"#app",
data:{
msg:"嵌套路由",
},
methods:{},
computed:{},
router,
});
</script>