第一个Vue程序:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!--导入vue的cdn-->
<script src="https://cdn.bootcss.com/vue/2.5.2/vue.min.js"></script>
</head>
<body>
<div id="app">
<!--实现双向绑定-->
{{message}}
</div>
<script>
let vm = new Vue({
el: '#app',
data: {
message: 123
}
});
</script>
</body>
</html>
这样,只要在网页控制台中输入:
vm.message=234;
显示的内容就会在不刷新网页的情况下自动变成234。
Vue基本语法
使用v-bind进行绑定:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcss.com/vue/2.5.2/vue.min.js"></script>
</head>
<body>
<div id="app" v-bind:title="message">
hello!
</div>
<script>
let vm = new Vue({
el: '#app',
data: {
message: 123
}
});
</script>
</body>
</html>
在控制台中把message进行修改,对应的悬浮提示框中内容也会修改。
使用v-if进行判断:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcss.com/vue/2.5.2/vue.min.js"></script>
</head>
<body>
<div id="app">
<span v-if="message==='A'">A</span>
<span v-else-if="message==='B'">B</span>
<span v-else>C</span>
</div>
<script>
let vm = new Vue({
el: '#app',
data: {
message: 'A'
}
});
</script>
</body>
</html>
使用v-for循环:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcss.com/vue/2.5.2/vue.min.js"></script>
</head>
<body>
<div id="app">
<h1 v-for="(item,index) in items">{{item}}+{{index}}</h1>
</div>
<script>
let vm = new Vue({
el: '#app',
data: {
items:['apple','bird','cat','dog']
}
});
</script>
</body>
</html>
这样就能把items中的字符串以及其索引一一列出来了。
Vue绑定事件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcss.com/vue/2.5.2/vue.min.js"></script>
</head>
<body>
<div id="app">
<button v-on:click="sayHello">click me</button>
</div>
<script>
let vm = new Vue({
el: '#app',
data: {
message:"hello"
},
methods: {
sayHello:function (){
alert(this.message);
}
}
});
</script>
</body>
</html>
我们用v-on来绑定事件就可以了,vue对象使用methods来定义方法。
Vue双向绑定
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcss.com/vue/2.5.2/vue.min.js"></script>
</head>
<body>
<div id="app">
<input type="text" v-model="message">{{message}}
</div>
<script>
let vm = new Vue({
el: '#app',
data: {
message:"hello"
}
});
</script>
</body>
</html>
这说明数据是双向绑定的,当输入框中的数据改变时,和它关联的数据也会变化。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcss.com/vue/2.5.2/vue.min.js"></script>
</head>
<body>
<div id="app">
<select v-model="message">
<option value="" disabled>请选择</option>
<option>A</option>
<option>B</option>
<option>C</option>
</select>
{{message}}
</div>
<script>
let vm = new Vue({
el: '#app',
data: {
message:""
}
});
</script>
</body>
</html>
这是一个下拉框的例子,当下拉框的某个option没有value时,其内容就是value。下拉框的绑定实质上是和value绑定。在此例中,message和下拉框的“请选择”的value都是空(相匹配),这就导致载入网页的时候默认会显示“请选择”,尽管它是disabled的。
Vue组件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcss.com/vue/2.5.2/vue.min.js"></script>
</head>
<body>
<div id="app">
<!-- 使用自定义的hello组件,并且把item传入hi中,由组件的props接收 -->
<hello v-for="item in items" v-bind:hi="item"></hello>
</div>
<script>
//定义一个组件,名字叫hello,内容就是一些li标签
Vue.component("hello",{
props:['hi'],
template:'<li>{{hi}}</li>>'
})
let vm = new Vue({
el: '#app',
data: {
items:['apple','bird','cat']
}
});
</script>
</body>
</html>
Axios异步通信
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcss.com/vue/2.5.2/vue.min.js"></script>
<!-- axios的cdn -->
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>
<body>
<div id="app">
<div>{{info.name}}</div>
</div>
<script>
let vm = new Vue({
el: '#app',
data(){
return{
info:''
}
},
mounted(){
//同级目录下有一个a.json的文件,模拟后端返回数据
axios.get("a.json").then(response=>{
this.info=response.data
})
}
});
</script>
</body>
</html>
其中mounted是一个钩子函数,是进行到Vue的某个生命周期的状态时自动调用的。还有很多其他的钩子函数。
另外一个注意点是使用了data()+return而不是使用data。不使用return包裹的数据会在项目的全局可见,会造成变量污染。使用return包裹后数据中变量只在当前组件中生效,不会影响其他组件。[个人理解是data类似于java中的static,一个Vue模板公用一个;而data()则是每一个由Vue模板创建出来的组件各有一个。]
计算属性
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcss.com/vue/2.5.2/vue.min.js"></script>
</head>
<body>
<div id="app">
<p>{{currentTime1()}}</p>
<p>{{currentTime2}}</p>
</div>
<script>
let vm = new Vue({
el: '#app',
methods: {
currentTime1: function () {
return Date.now();
}
},
computed:{
currentTime2:function (){
return Date.now();
}
}
});
</script>
</body>
</html>
我们可以发现,计算属性和方法很像,都是通过函数来返回一个值,但是区别在于方法需要通过方法调用才能使用,而计算属性直接通过属性来调用。
另一个注意点是,方法和计算属性可以重名,但是这样就调不到计算属性了。
计算属性的特点是,计算出这个值会被缓存。计算属性还可以依赖多个Vue 实例的数据,只要其中任一数据变化,计算属性就会重新执行,视图也会更新。我们在浏览器控制台中输入vm.currentTime1(),结果是实时变化的。但是如果输入vm.currentTime2,结果则是不变的。
计算属性的特征是为了将不经常变化的计算结果进行缓存,以减少系统开销。
slot插槽
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcss.com/vue/2.5.2/vue.min.js"></script>
</head>
<body>
<div id="app">
<todo>
<todo-title slot="title-slot" v-bind:t="title"></todo-title>
<todo-list slot="list-slot" v-for="item in list" v-bind:v="item"></todo-list>
</todo>
</div>
<script>
Vue.component("todo",{
template:'<div>\
<slot name="title-slot"></slot>\
<ul>\
<slot name="list-slot"></slot>\
</ul>\
</div>'
})
Vue.component("todo-title",{
props:['t'],
template:'<p>{{t}}</p>'
})
Vue.component("todo-list",{
props: ['v'],
template:'<li>{{v}}</li>'
})
let vm = new Vue({
el: '#app',
data:{
title:'hi',
list:['a','b','c']
}
});
</script>
</body>
</html>
这个例子中,我们在todo这个组件中加入了两个插槽,我们只要把应该装进去的组件对应到应该对应的插槽中去即可。
自定义事件内容分发
如何让组件里面的元素使用对象中的方法呢?
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcss.com/vue/2.5.2/vue.min.js"></script>
</head>
<body>
<div id="app">
<todo>
<todo-title slot="title-slot" v-bind:t="title"></todo-title>
<todo-list slot="list-slot" v-for="(item,index) in list"
v-bind:v="item" v-bind:n="index" v-on:delete="removeItem($event)"></todo-list>
</todo>
</div>
<script>
Vue.component("todo",{
template:'<div>\
<slot name="title-slot"></slot>\
<ul>\
<slot name="list-slot"></slot>\
</ul>\
</div>'
})
Vue.component("todo-title",{
props:['t'],
template:'<p>{{t}}</p>'
})
Vue.component("todo-list",{
props: ['v','n'],
template:'<li>{{v}}<button v-on:click="remove()">删除</button></li>',
methods:{
remove:function() {
this.$emit('delete',this.n);
}
}
})
let vm = new Vue({
el: '#app',
data:{
title:'hi',
list:['a','b','c']
},
methods: {
removeItem:function (i){
this.list.splice(i,1);
}
}
});
</script>
</body>
</html>
当我们点击按钮时,会触发click事件,从而调用组件自身的remove方法,同时props接收index参数并称其为’n’。在remove方法中,调用this.$emit方法,第一个参数是要触发的事件名称delete,第二个参数是要传递的参数。当delete事件触发之后,前端通过$event来获得刚刚传递的参数,并且绑定到vue对象的removeItem方法上。removeItem方法删除位于i的数据,过程完成。
组件和vue对象之间不应该直接有调用关系,而是应该通过前端作为桥梁来沟通。
第一个Vue-cli程序
vue-cli是官方提供的一个脚手架,用于快速生成一个vue项目模板。
使用需要安装环境Node.js。Node.js官网
选择Windows64位.msi安装包进行下载。双击无脑next即可。
打开测试cmd,输入node -v和npm -v,有结果即安装成功。
接下来安装淘宝镜像加速器(cnpm)。
npm install cnpm -g
接下来安装vue-cli。
cnpm install vue-cli -g
接下来开始自己的第一个项目,把cmd用管理员身份打开并来到自己想创建的目录,然后输入如下命令:
vue init webpack myvue
myvue是自己想用的项目名称。建立过程中回答他的问题,并在所有的y/n选项中暂时都选no。
然后进入该目录,初始化并运行。
cd myvue
cnpm install
npm run dev
然后会显示如下提示:
DONE Compiled successfully in 3091ms
I Your application is running here: http://localhost:8080
然后访问该网页即可。如何停止?ctrl+c即可。
用idea打开,并在idea的terminal输入npm run dev也没有问题。
webpack学习使用
WebPack 是一款模块加载器兼打包工具,它能把各种资源,如JS、JSX、ES6、SASS、LESS、图片等都作为模块来处理和使用。
安装在管理员身份终端输入:
cnpm install webpack -g
cnpm install webpack-cli -g
测试:
webpack -v
webpack-cli -v
体验一下webpack:
(1)新建一个空文件夹webpackStudy,并且用idea打开。
(2)新建一个package名叫module,并在其中添加文件hello.js:
exports.sayHi=function(){
document.write('<h1>hello</h1>>')
}
exports相当于把sayHi这个方法给暴露出去了。
(3)再添加一个文件main.js:
var hello=require("hello");
hello.sayHi();
require把刚刚暴露的hello.js给引入了,并且调用了暴露的方法。
(4)在根目录下创建文件webpack.config.js:
module.exports={
entry:'./Module/main.js',
output:{
filename:'./js/bundle.js'
}
}
entry后面是唯一的入口,output则是要导出的文件名称。
(5)然后控制台输入webpack,打包后发现多出了dist文件夹,其中就有bundle.js,发现里面的代码已经面目全非。
(6)新建一个index.html,并且把bundle.js引入即可,浏览器成功显示hello。
使用webpack可以将代码降级并打包。由于开发用到了ES6,而浏览器只识别ES5,因此降级是必要的。而打包让代码更加简明,引入时也只要引入打包完成的代码即可。
几种npm install的介绍
1.npm install
从git上面clone下来的vue项目一般都没有node_modules文件夹(因为过于庞大),因此,使用npm install可以根据项目中的package.json文件来生成自己的node_modules文件夹(很像maven,是不是?)。
2.npm install -g modulename
这条指令将modulename模块安装到全局而不是将这个模块安装到某个特定的项目中,具体安装到磁盘哪个位置,要看 npm config prefix 的位置。优点在于可以在终端直接进行modulename指令的执行,比如上面的npm,cnpm,vue-cli,webpack。
3.npm install modulename
这条指令和2的区别在于,会将modulename安装到项目目录下,但是不会写入package.json文件中,所以很少用。
4.npm install --save modulename
这条指令也会把modulename安装到项目目录下,而且会写入package.json文件的dependencies中,这样git之后,别人clone下来也可以使用npm install来初始化项目。另外一点和5指令相区别,下面会提出。
5.npm install --save-dev modulename
这条指令也会把modulename安装到项目目录下,而且会写入package.json文件的devDependencies中。dependencies是生产环境需要依赖的库,而devDependencies是只有开发环境下需要依赖的库。因此,运行时需要用到的包使用––save,否则使用––save-dev。
6.npm install --production
这条指令和1很像,区别在于1会把package.json中的所有dependencies安装好,而6仅仅安装dependencies中内容而不安装devDependencies的内容。
vue-router路由
找一开始的空项目,并且输入npm命令安装vue-router:
cnpm install vue-router --save-dev
首先,我们在component文件夹下新建两个组件Content.vue和Main.vue:
<template>
<h1>内容页</h1>
</template>
<script>
export default {
name: "Content"
}
</script>
<style scoped>
</style>
<template>
<h1>主页</h1>
</template>
<script>
export default {
name: "Main"
}
</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', //路由路径
component:Content //跳转的组件
},
{
path: '/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">
<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的to中写路由路径,而router-view也是必不可少的,其作用是将组件展示出来。
vue+elementUI
首先新建一个项目,然后进入项目目录,安装一些模块:
vue init webpack hello-vue
cnpm install vue-router --save-dev
cnpm install element-ui --save
cnpm install
先在component文件夹下加两个组件,一个是Main,一个是Login。其中Login使用ElementUI组件。
<template>
<h1>首页</h1>
</template>
<script>
export default {
name: "Main"
}
</script>
<style scoped>
</style>
<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>
然后在router文件夹下加上路由配置文件index.js:
import Vue from 'vue'
import VueRouter from "vue-router";
import Main from "../components/Main";
import login from "../components/login";
Vue.use(VueRouter)
export default new VueRouter({
routes:[
{
path: '/main',
component: Main
},{
path: '/login',
component: login
}
]
})
接着只要在App.vue的组件里写上<router-view>就行。
<template>
<div id="app">
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'App',
}
</script>
在main.js中需要引入elementUI组件,别忘了把router加进去:
import router from './router'
import Vue from 'vue'
import App from './App'
import Element from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
Vue.use(Element)
new Vue({
el: '#app',
components: { App },
template: '<App/>',
router
})
运行项目,直接进入/main或者/login界面即可。
路由嵌套
在上面的项目中,在component文件夹下再新建两个组件:profile和userlist。
<template>
<h1>用户列表</h1>
</template>
<script>
export default {
name: "list"
}
</script>
<style scoped>
</style>
<template>
<h1>个人信息</h1>
</template>
<script>
export default {
name: "profile"
}
</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="/profile">个人信息</router-link>
</el-menu-item>
<el-menu-item index="1-2">
<!--插入的地方-->
<router-link to="/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 style="">
<el-header style="text-align: right; font-size: 12px; background-color: aquamarine">
<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>
我们想让main这个组件的一部分进行路由,而不是把整个main组件进行路由,这时候我们就需要路由嵌套了。修改router下的index.js:
import Vue from 'vue'
import VueRouter from "vue-router";
import Main from "../components/Main";
import login from "../components/login";
import profile from "../components/profile";
import list from "../components/list";
Vue.use(VueRouter)
export default new VueRouter({
routes:[
{
path: '/main',
component: Main,
children:[{
path:'/profile',
component:profile
},{
path:'/list',
component:list
}]
},{
path: '/login',
component: login
}
]
})
可以发现在main下面多了一个children属性,而children中可以继续写组件和路由,这就是路由嵌套。为何叫做嵌套?因为最外层的main本身就是一个路由,而main里面还需要路由,这就是嵌套。假设我们不使用children而是直接把两个新组件直接放入routes对象中,那么路由时main组件将不复存在了。
参数传递及重定向
如果要触发传递参数,可以有这么两种做法:
(1)通过router-link
在main组件中的跳转中加入对象,注意to变成了:to,path变成了name,后面多了传入的参数params。
<router-link :to="{name:'profile',params:{id: 1}}">个人信息</router-link>
或者:
<router-link :to="{path:'/profile/'+1}">个人信息</router-link>
(2)通过methods中的函数
在main组件的methods中写事件触发函数。
this.$router.push('/profile/'+this.id);
或者:
this.$router.push({name:'profile',params:{id:this.id}});
如果要接收传递参数,可以有这么两种做法
(1)用路由本身来传递
在router下的index.js中需要改动,注意path后面加入了:id作为占位符,如果触发传参的条件使用了name,则还需加入name来和前面的跳转相对应。
{
path:'/profile/:id',
component:profile,
name: 'profile' //传参使用name时需要这条语句
}
然后在profile组件中用router展示出来:
<div>
<h1>个人信息</h1>
{{$route.params.id}}
</div>
(2)使用props来展示
对于展示信息,也可以通过props来做,耦合性更弱一些:
在index.js中加入一句props:true即可。
{
path:'/profile/:id',
name: 'profile' ,
component:profile,
props:true
}
然后在profile组件中加一个props和双向绑定即可:
<template>
<div>
<h1>个人信息</h1>
{{id}}
</div>
</template>
<script>
export default {
name: "profile",
props:['id']
}
</script>
重定向问题
关于重定向,只要在index.js中加入如下代码即可:
{
path:'/goHome',
redirect:'/main'
}
极其简单。
404和路由钩子
首先,页面的url出现了’#'符号,我们只要在路由配置中在和routes并列的地方加上如下语句即可:
mode:'history'
关于404的处理很简单,只要加一个error组件:
<template>
<h1>404</h1>
</template>
<script>
export default {
name: "404"
}
</script>
<style scoped>
</style>
并且将该组件设置进index.js即可。
{
path:'*',
component: error
}
路由钩子的使用:
<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>
<style scoped>
</style>
两个函数一个是在路由进入时调用,一个是在路由离开时调用。next()是必须的,否则会卡住。
配合axios使用:
cnpm install --save axios vue-axios
main.js中加入:
import Vue from 'vue'
import axios from 'axios'
import VueAxios from 'vue-axios'
Vue.use(VueAxios, axios)
接着修改profile.vue的代码:
<template>
<div>
<h1>个人信息</h1>
{{$route.params.id}}
</div>
</template>
<script>
export default {
name: "profile",
beforeRouteEnter:(to,from,next)=>{
console.log('路由进入');
//通过next操作vue组件实例vm,可以修改vm中的值
next(vm => {
vm.axios({
method:'get',
url:'http://localhost:8080/static/p.json'
}).then(response=>{
console.log(response)
})
});
},
beforeRouteLeave:(to,from,next)=>{
console.log('路由离开');
next();
}
}
</script>
<style scoped>
</style>
提示一下,组件中的data需要用到这种形式:
data(){
return{
id:1
}
},