我们在使用vue的时候不建议引入jQuery
开发或学习引入下面这个js,这个js中包含很多提示
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
如果是生产环境,则引入下面这个js
<script src="https://cdn.jsdelivr.net/npm/[email protected]">script>
仅使用vue,不加入任何后端代码。在使用前端js库的时候,首先要引入相关的js库,vue也一样,首先将vue引入到页面中。
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<div id="app">
{
{msg}} {
{msg.toUpperCase()}}
<span>{
{username}}span>
<br>
{
{user}} {
{user.name}} {
{user.age}}
<br>
{
{ok ? "yes":"no"}}
<br>
{
{num + 1}}
div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
<script>
const vue = new Vue({
el:"#app",
data:{
msg:"hello vue",
username:"我是张晓明",
user:{
name:"张晓明",age:16},
arr:["北京","天津","上海"],
users:[{
name:"张晓明",age:16},{
name:"李晓华",age:26}],
ok:true,
num:13
}
})
script>
body>
html>
代码解释:
- 首先是我们将script标签和js代码块没有像以前一样写在head标签中,因为我们知道HTML页面是从上往下加载的,如果我们将js代码块写在了head中,那么此时div标签还没有加载,因此vue无法找到需要加载的元素,数据也无法绑定。(vue使用双向绑定模式mvvc)
- 使用ES6语法的方式声明一个vue对象
const vue = new Vue({...})
- 在vue中声明了两个属性
- el:用来表示vue对象要作用的范围,例如此处的
id=APP
的div元素,那么我们这个vue对象不仅能作用于div内部,也可以作用于这个div所有的子元素。那么为什么不给body元素声明一个id呢?因为body元素在HTML中是一个比较特殊的元素,因此我们通常不选取body,而是作用到整个页面的最外围的div元素。
- 选择器:el元素的选择器可以使用jQuery的其他选择器,例如class、attr等等,但是不建议这么使用,建议使用id选择器,这表示vue实例作用于唯一的作用域
- data:表示数据,data内部的变量名称我们可以随意取
- 字符串:直接使用{ {xxx}}的方式来取出即可
- 对象:如上例子,如果使用{ {user}} 则在页面上显示该对象信息
{name:"张晓明",age16}
,而使用{ {user.name}}就可以取出对象里的值- 数组:如果使用{ {arr}},则显示全部数组
["北京","天津","上海"]
,如果要取出某个下标的元素,则使用{ {arr[0]}} 这种方式取出- 对象数组:如上面的例子中users,可以使用{ {users[0].name}}
- 其他:
- 字符串拼接:如上面的例子,如过我们要对取出来的msg拼接一个其他的字符串,可以使用
{ {msg + '???'}}
- 方法调用:
{ {msg.toUpperCase()}}
将英文小写转为大写,或{ { message.split('').reverse().join('') }}
- 运算符:例如上面例子中的ok和num
在上面的例子中基本已经列举
下面介绍的都是指令,而上面使用的{ {}}则是插值表达式
<div id="app">
<span>{
{msg}} 原始值span>
<br>
<span v-text="msg">原始值span>
<br>
<span v-html="vhtml">span>
<br>
<div v-bind:id="dynamicId">vbind测试div>
div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
<script>
const vue = new Vue({
el:"#app",
data:{
msg:"hello vue",
vhtml:"vhtml
",
dynamicId:"aa"
}
})
script>
v-text
:与插值表达式{ {}}的作用差不多,都是将数据取出绑定到对应标签上。那么区别是什么呢?
v-text
会覆盖原有的值,而插值表达式不会v-text
没有插值闪烁(什么是插值闪烁?就是在网络比较差的时候{ {msg}}
的值还没有响应但是页面已经加载完的时候,会在页面上直接显示{ {msg}}
直到数据获取到后才变为实际的数据,存在一个从表达式变为数据的闪烁。而v-text在数据没有加载出来的时候直接显示为空,不会出现其他东西)v-html
:如上面例子中的vhtml属性,如果不使用v-html,则会直接在页面上显示vhtml
,并且也会替换掉原本的值v-bind
:插值表达式不能改变元素的属性,如果需要改变元素的属性,则需要使用v-bind
,例如上面的dynamicId,意思就是将该div修改为vbind测试
v-show
:用来控制页面中某个元素是否显示,底层控制的就是css的display属性,而不是移除删除dom元素,不会频繁操作DOM树,因此相对来说效率较高。值是一个布尔类型的值,true表示显示,false表示不显示。还可以绑定我们vue中的属性,例如下面的hello:<div id="app">
<h1 v-show="hello">你好h1>
div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
<script>
const vue = new Vue({
el:"#app",
data:{
hello:false
}
})
v-if
:与v-show
的作用相同,也是控制页面元素显示还是隐藏。不过与之不同的是v-if是通过对dom树的节点增加或删除来控制隐藏还是显示的v-bind
:用来给页面中标签元素绑定相应的属性例如:你好
,这里的vuetitle可以是vuedata中的一个变量,我们只需要改变这个变量的值就可以动态的改变这个标签内的属性的值。简化写法,直接使用冒号你好
v-for
:用来 对数组或对象进行遍历
<div id="app">
<spa v-for="(value,key,index) in user">
{
{key}}:{
{value}}
spa>
div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
<script>
const vue = new Vue({
el:"#app",
data:{
user:{
name:"小陈",age:12}
}
})
script>
v-bind:key="user.id"
这是因为vue官方建议我们使用v-for遍历元素的时候,可以给遍历的元素绑定一个唯一标识,用来便于vue内部做重用和排序,因此我们建议在使用v-for时加上:key,用来给vue内部提供重用和排序的唯一key<div id="app">
<ul>
<li v-for="a,index in arr">
{
{index }}:{
{a}}
li>
ul>
<ul>
<li v-for="user,index in users" v-bindL:key="user.id">
{
{index }}:{
{a}}
li>
ul>
div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
<script>
const vue = new Vue({
el:"#app",
data:{
user:{
name:"小陈",age:12},
arr:["上海","北京","天津"],
users:[{
id:1,name:"小陈",age:12},{
id:2,name:"小力",age:12}]
}
})
script>
v-model
:双向绑定。用来绑定标签元素的值与vue实例对象中的data数据保持一致,从而实现双向数据绑定。如下:当我们在input输入框中输入任意值时,{ {msg}}也会跟着变成与input框中相同的值,而当我们 通过事件改变data中的msg的值的时候,input输入框的值和{ {msg}}中的值也会跟着变,这也就是说在这种情况的时候,input、插值表达式以及data.msg三个地方的值都已经统一了,这就是双向绑定<div id="app">
<input type="text" v-model="msg">
{
{msg}}
<br>
<input type="button" @click="changeValue" value="点我改变msg">
div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
<script>
const vue = new Vue({
el:"#app",
data:{
msg:""
},
methods:{
changeValue(){
this.msg='苏菲的世界';
}
}
})
script>
总结:
1、使用v-model指令可以实现数据双向绑定
2、所谓双向绑定,表单中数据变换导致vue实例data数据变化,vue实例中data数据大变化导致 表单中数据变化
mvvm架构:双向绑定机制
model:数据 Vue实例中绑定的数据 vm:ViewModel监听器View:页面,页面中展示的数据 ``
(事件也是通过指令来绑定)
事件三要素:
事件源:发生事件的dom元素
事件:事件执行的动作,例如单击双击等等
监听器:发生特定动作之后的处理程序,通常是js的函数
<div id="app">
<span>{
{age}}span>
<input type="button" v-on:click="changeAge" value="点我加一">
<input type="button" @click="reduceAge" value="点我减一">
<input type="button" @click="reduceAge2" value="点我减二">
<input type="button" @click="reduceAge3(23)" value="给多少是多少">
div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
<script>
const vue = new Vue({
el:"#app",
data:{
msg:"hello vue",
age:16
},
methods:{
//methods用来调用vue中的事件
changeAge:function(){
//事件函数中的this就是当前vue对象
this.age++ ;
},
reduceAge:function(){
this.age--;
},
reduceAge2(){
this.age = this.age -2;
},
reduceAge3(count){
this.age = count;
}
}
})
script>
v-on
:使用v-on指令的方式声明事件,然后将事件定义在vue对象的methods对象中。例如onclick事件,onchange事件等等,具体写法如上。在这个例子中实现了单向绑定,即我们没有改变任何dom元素,即可在调用事件的时候改变data中的数据实现改变dom页面上的数据。事件函数中的this就是当前vue对象,可以直接用this点出本身的属性,例如这里的age、msg等等v-on:
vue.reduceAge2()
.下面这个小例子将上面讲解的语法及属性做了一个小实验。在文本框中输入文字,然后点击添加到记事本后,列表增加一列。主要是用到v-model实现msg消息双向绑定。还有v-on事件绑定,v-for循环
<div id="app">
<input type="text" v-model="msg" v-model="msg"><button v-on:click="add">添加到记事本button>
<ul>
<li v-for="item,index in list">{
{index}}{
{item}}<a href="javascript:;" @click="delRaw(index)">删除a>li>
ul>
<br>
<span>总数量:{
{list.length}}span><input type="button" @click="deleteAll" value="删除所有">
div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
<script>
const vue = new Vue({
el:"#app",
data:{
msg:"",
list:["嘻嘻嘻","哈哈哈","呵呵呵"]
},
methods:{
add(){
if (this.msg != ""){
this.list.push(this.msg)
this.msg = '';
}
},
delRaw(index){
this.list.splice(index,1)
},
deleteAll(){
this.list = [];
}
}
})
script>
作用:用来和事件联用,用来决定事件触发条件或者是阻止事件的触发机制
.stop
这就是阻止事件冒泡的修饰符 <div id="app">
<div @click.self="divBtn" style="width: 200px;height: 200px;background-color: rebeccapurple">
<input type="button" value="我是一个按钮" @click.stop="inputBtn">
div>
div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
<script>
const app = new Vue({
el: '#app',
data: {
},
methods: {
divBtn(){
alert("div被点击了")
},
inputBtn(){
alert("input被点击了")
}
}
})
script>
,这个代码中once表示只执行一次,因此prevent的事件修饰符只会在第一次组织事件的默认行为跳转href连接。由此可见,绑定了once修饰符的 事件,其他事件也是会只生效一次。用来与键盘中按键事件绑定在一起 用来修饰特定的按键修饰
keyup按键抬起时触发,我们打的每一个字母在按下的时候不会触发,松开后才会触发,而这个例子的意思就是回车键抬起时才会触发
同上 esc键抬起时触发
当使用tab键切换到该事件绑定的input时才会触发在 vue中不建议使用jQuery,因此在前后端分离场景下,我们需要使用类似ajax的方式来获取数据。因此vue建议我们使用Axios。
Axios是一个异步请求技术,核心作用就是用来在页面中发送异步请求,并获取对应数据在页面中渲染,又叫
Axios不是vue团队开发,是其他团队开发,官网链接
axios.get("localhost:8080/findAll?name=xiaocheng").then(function (response) {
console.log(response)
}).catch(function (err) {
console.log(err)
})
axios.post("http://localhost:8080/user/save",{
username:"zhangsan",
age:16,
email:'[email protected]',
phone:'13909552264'
}).then(function (response) {
console.log(response)
}).catch(function (onerror){
console.log(onerror);
})
axios.all
去统一调用,然后在Axios.spread 中统一返回所有请求的响应function axiosget() {
return axios.get("localhost:8080/findAll>name=xiaocheng");
}
function axiospost(){
return axios.post("http://localhost:8080/user/save",{
username:"zhangsan",
age:16,
email:'[email protected]',
phone:'13909552264'
})
}
axios.all([axiosget(),axiospost()]).then(axios.spread(function (res1,res2) {
console.log(res1);
console.log(res2);
})
分为三个阶段
<div id="app">
<span id="sd">{
{message}}span>
div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
<script>
const app = new Vue({
el: '#app',
data: {
message : "msg"},
methods: {
},
beforeCreate() {
//1.生命周期中的第一个函数,该函数在执行时vue仅仅完成了自身的事件绑定和生命周期函数的初始化工作,vue实例此时还没有data、methods等
console.log(this.message)
},
created(){
//2.生命周期第二个函数,该函数执行时vue实例已经完成了初始化工作,将el、data等都绑定到实例上了
console.log(this.message)
},
beforeMount() {
//3.生命周期的第三个函数,该函数在执行时vue将el指定的作用范围作为模板编译,但是还未对模板中的插值表达式等等做解析,因此这个时候无法获得模板中的插值表达式的值
console.log(document.getElementById("sd").innerText);
},
//4.将模板进行编译、解析、渲染后,替换页面上原本的el区域
mounted() {
//5.该函数执行时,vue已经将数据渲染到界面中并且更新页面,这个时候打印出来的值就不是{
{message}} 而是msg
console.log(document.getElementById("sd").innerText);
}
mounted() {
//5.该函数执行时,vue已经将数据渲染到界面中并且更新页面,这个时候打印出来的值就不是{
{message}} 而是msg
console.log(document.getElementById("sd").innerText);
},
beforeUpdate() {
//6.当页面挂载完毕后,如果vue实例中的data发生了变化,则会触发该函数,然而这个时候仅仅是vue中的data发生了变化,而页面依然是原来的值 ,
// 这是因为当data发生变化时,先触发该函数,然后虚拟DOM会重新渲染并更新,然后当updated函数执行完毕后。如何验证呢?我们可以发现当点击修改数据按钮时触发函数upadteMsg,
// beforeUpdate和updated函数中打印的数据,一开始是data中修改了的数据xixix,然后是页面中未被修改掉的数据msg,然后updated中两个都是xixxi
console.log("beforeUpdate:"+this.message);
console.log("beforeUpdate:"+document.getElementById("sd").innerText);
},
updated() {
//该函数执行时,data中 的数据和页面中的一致,因为已经完成了dom重新渲染和 更新。这个函数和beforeUpdate函数在每次data中数据发生变化时都会执行
console.log("updated:"+this.message)
console.log("updated:"+document.getElementById("sd").innerText)
},
beforeDestroy() {
//该函数执行时,vue中的所有数据、method、component都没有被销毁
},
destroyed(){
//该函数执行时,vue已经被销毁
})
script>
那么有了生命周期能干什么呢?假设我们需要在页面加载完成之后就立即展示列表,那么我们就可以在created函数中给vue实例中的data通过ajax绑定数据,然后顺势渲染,如果在beforeCreate函数中可以吗,明显是不行的,因为此时data都还没有。那么在其他非destory函数中可以吗,其实也可以,但是会让给dom重新渲染。这只是一个小例子,具体怎么用这些生命周期函数,我也不知道。
作用:组件的作用就是提高复用性、减少代码量。
全局组件注册给了vue实例,日后可以在任意Vue实例的范围内使用该组件。案例如下:相当于自己编写了一个标签,然后在HTML中任意位置使用该标签就相当于是使用了组件。
<div id="app">
<login>login>
div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
<script>
//组件参数 1:组件的名称 2.组件的配置对象 template:用来书写组件的HTML代码
Vue.component("login",{
template:"登录
"
})
const app = new Vue({
el: '#app',
data: {
},
methods: {
}
})
script>
登录
像这种有两个容器的写法就会报错。通过将组件注册给对应Vue实例中的一个components属性
一般使用下面这种方式来注册局部组件。使用方式与全局相同
<div id="app">
<login>login>
div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
<script>
let loginTemplate = {
template: "用户登录
"
}
const app = new Vue({
el: '#app',
data: {
},
methods: {
},
components:{
login:loginTemplate
}
})
script>
-第二种开发方式:使用模板标签的方式来绑定模板。需要注意的是要把template模板标签放在vue实例的作用范围外
<div id="app">
<login>login>
div>
<template id="loginTemplate">
<h1>用户登录h1>
template>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
<script>
let loginTemplate = {
template: '#loginTemplate'
}
const app = new Vue({
el: '#app',
data: {
},
methods: {
},
components:{
login:loginTemplate //组件注册
}
})
script>
如果组件配置对象的名字也就是上面的loginTemplate叫login,则可以直接在component中写个login就行了,不需要后面的
:loginTemplate
。也就是说当配置对象与组件的名字相同时,就可以不用写重复的了。
作用:props用来给组件传递相应的静态数据或者动态数据
<div id="app">
<login name="zhangsna">login>
div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
<script>
let login = {
template:'你好:{
{name}}
',
props:["name"]
}
const app = new Vue({
el: '#app',
data: {
},
methods: {
},
components:{
login
}
})
script>
login 标签上的属性都可以使用props的方式来获取。如果name名字叫user-name,那么props属性中通过props:['userName']
这种方式来获取。获取到的值可以通过插值表达式显示使用。
如下案例:通过v-bind指令将属性name与data中的username绑定,当username变化的时候,组件的name值也跟着变,组件中插值表达式获取到的也就是动态的数据了。
<div id="app">
<login v-bind:name="username">login>
div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
<script>
const login = {
template:'你好:{
{name}}
',
props:["name"]
}
const app = new Vue({
el: '#app',
data: {
username:'李四'
},
methods: {
},
components:{
login
}
})
script>
我们也可以打开浏览器控制台尝试一下:如下图
app就是vue的实例对象,username是app对象的属性,我们可以通过改变属性值来测试组件的反映。
所有的prop都是的器父子prop之间形成了一个单向下行绑定:腹肌prop的更新会向下流动到子组件中,但是反过来则不行。这样会放置从子组件意外改变父子间的状态,从而导致你的应用的数据流向难以理解
<div id="app">
login>
div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
<script>
const login = {
template : '{
{name}}===={
{username}}
',
props:["username"],
data(){
return {
name:'小李'
}
}
}
const app = new Vue({
el: '#app',
data: {
username:'张三'
},
methods: {
},
components:{
login
}
})
script>
在上面的代码中我们可以发现,在组件自己的内部也有一个data属性,也就是说组件可以定义自己的属性,但是需要注意到 是,组件内部的 属性值不能与login标签的属性相同,否则会报错,也就是说
这里的username不能在login.data
中出现。上面这段代码的最终结果
那么如果我们想将张三,也就是vue实例APP中的数据传递给组件中的data内的name该怎么办呢?只需要将‘小李’
替换成this.username
即可。
上面已经讲过,这里再提一下,案例如下:
<div id="app">
<login>login>
div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
<script>
//组件中数据的定义
let login = {
template:"{
{msg}} 组件定义数据
- {
{item}}
",
data(){
return{
msg:"hello",
list:['spring','java','c++','php','python']
}
}
}
const app = new Vue({
el: '#app',
data: {
},
methods: {
},
components:{
login
}
})
script>
需要注意的就是单向数据流。在使用props的时候从组件中获取的值不能与组件自身定义的属性重复
与vue实例类似,事件的定义也是在组件的对象中
<div id="app">
<login v-bind:uname="username" v-bind:ugender="userGender">login>
div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
<script>
const login ={
template:"{
{name}} 组件定义数据
- {
{ item }}
",
props:['uname','ugender'],
data(){
return{
name:'张大彪',
age:16,
gender:this.ugender,
friend:['陈丹','赵丹','李丹','朱丹']
}
},
methods: {
change(username){
console.log("组件的事件调用")
console.log(this.name);
console.log(this.gender)
console.log(username);
}
}
}
const app = new Vue({
el: '#app',
data: {
username:'周杰伦',
userGender:'女'
},
methods: {
},
components:{
login
}
})
script>
总结:1. 组建中固定翼事件和直接在vue中定义事件基本一致,直接在组件内部对应的html代码上加入@事件名=函数名的方式即可
2.在组件内部使用methods属性来定义对应的时间函数即可。需要注意的是事件函数中的this指向的是当前组件的实例,而不是vue对象实例
组件中调用vue实例的函数
<div id="app">
<login v-on:cfind-all="findAll">login>
div>
<template id="loginTemp">
<input type="button" @click="change" value="点我触发组件函数">
template>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
<script>
/* 在vue中属性的绑定使用v-bind指令,也可以使用 :属性 的方式绑定。
* 在vue中事件的绑定用v-on指令,也可以使用 @事件名 的方式绑定
* 那么我们如果想要在组件内调用vue实例的函数,就需要通过v-on或@ 将vue实例的函数与组件绑定,然后通过 this.$emit('组件函数名')的方式来调用
* 需要注意的是,我一开始写的 v-on:c_findAll,结果报错了,vue在对这种驼峰命名的写法上似乎有一些特殊的要求,于是改成了cfind-all
* */
const login = {
template:'#loginTemp',
data(){
return{
}
},
methods: {
change(){
console.log("login组件中的change函数");
this.$emit('cfind-all')
}
}
}
const app = new Vue({
el: '#app',
data: {
},
methods: {
findAll(){
console.log("vue 实例中的findall函数");
}
},
components:{
login
}
})
script>
总结
- 组件声明(上面
const login = {}
的部分)- 注册组件(上面
const app = new Vue({ ... })
中components)- 使用组件并传递事件
,可帮助我们包裹元素,但在循环过程当中,template不会被渲染到页面上.:key
不需要key,因为key要标注在存在的标签中,而这个占位符不是存在的标签,只是一个虚拟标签v-if v-else-if v-else
这几个标签之间不能有其他元素