后端小白的VUE入门笔记, 前端高能慎入

因为项目需要前后端分离, 后端竟然不用控制 view 层了, 页面的跳转后端不再干涉,(前端的 vue 经过打包后成了一张 index.html) 后端只需要响应给前端 json 串就 ok, 其实这不是爽歪歪? 但是觉得还是奇奇怪怪, 感觉前端是个黑盒了, 于是忍不住去学习了 vue

感觉前端的框架带来的这种前后端分离变化还是特别明显的, 后端确实不用再操心 view 层了, 页面的转换有 vue 通过后端的切换不同的组件, 后端基本上没有什么变化, 但是相应数据基本上是清一色的 json 格式的数据了, 此外, 目前碰到的后端的安全框架 SpringSecurity 的使用有了些许变化, 起码认证成功还是失败, 不能往指定的页面跳转了, 转而使用消息 + 状态码提示, 因为就一张 index.html, 还能往哪里跳转?

下面是近几天的学习笔记, 还是再整理一遍, 毕竟会忘

认识 MVVM 框架 Vue#

MV VM 分别对应着

  • model : 数据模型, 存放后端传递过来的数据
  • view : 视图, 其实就是 html, 页面
  • viewModel : vue 的实例

下面是一个入门的例子: 通过这个例子可以看到:

  • 我们 new 出来 vue 的实例, 然后把它关联在了 html 中 id 为 app 的代码块, 这样目的是如果我们在这个代码块中使用 vue 的模板语法,vue 可以解析
  • data: 这个模块盛放的 mvvm 中的第一个 m

其实这也可以看出,vue 的开发模式, 它的出现屏蔽掉了 dom 操作, 我们再也不用 document.getElementById(), 然后 innnerHtml 了, 现在的工作就是把后端给的值填充进 data 块中的属性字段就 ok, 一旦发生改变, 页面会自动渲染上最新的值

<body>
<div id="app">
    <input type="text" v-model="username"><!-- 声明式开发 dom 监听  -->
    <p>Haha {{username}}</p>   <!-- 数据绑定 -->
</div>

</body>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
var vm = new Vue({
    el:'#app', // 元素选择器, 选出根路径
    data:{ // Data Model 数据模型
       username:'哈哈'
    }
});
</script>

模板语法:#

双大括号 取值:

<p>{{msg}}</p>
<p>{{msg.toUpperCase()}}</p>  <!--  可以调用 js 函数 -->

嵌入文本或 html

<p v-text="msg"></p>    <!--  相当于 textContent -->
<p v-html="msg"></p>   <!--  相当于 innerHtml -->

强制数据绑定, 在原标签前添加 :

<img :src="imaUrl" alt="">

绑定监听事件: @符

  • 比较有趣的地方, 如果在 methods 块中,js 函数的定义是无参数据的, 在 html 代码块中可以直接写函数名, 而不写小括号, 因为 java 代码写多了, 看了想笑 ,(当然 (), 也可以写上, 但是 js 编程者会认为这是没事找事 )
<button @click="text"> 点我111 </button>
<button @click="text222('haha')"> 点我222 </button>

计算属性#

计算属性, 说白了就是 vue 给我们的一块糖, 让我们定制数据的变化规则, 然后 vue 帮我们渲染在 html 页面上

现在是 2020 年的 5 月 23 号,当我再回来看 computed 时,想再补充一下,如何更好的理解计算属性: 所谓计算属性,说白了,其实就是根据 data 中现有的属性计算得到一个新的属性 ,此外,计算属性函数是不需要我们手动执行的,会自动执行

  • 计算属性是针对 data 中的字段的操作
  • 计算属性中的每一个函数, 都分两部分: get 和 set , 默认是 get, 作用是把这个方法的返回值渲染进页面, set 方法, 就是重新设置值, 然后 get 会重新渲染 html
  • 计算属性是存在缓存的,key 就是函数的名字,value 就是计算得到的值

例子:

<body>
<div id="app">
姓名: <input type="text" placeholder="FirstName" v-model="secondName"> <br>
姓名1: <input type="text" placeholder="FullName1" v-model="FullName1"> <br>
姓名3: <input type="text" placeholder="FullName3" v-model="FullName3">  <br>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
// 下面的全部回调函数中, this 都是 vm 对象
var vm = new Vue({
el: '#app',
data: {
    firstName: 'A',
    secondName: 'B',
},
computed: {
    FullName1() {
        return this.firstName + ' ' + this.secondName;
    },
    // todo  通过计算属性实现双向的数据绑定, 不受其他影响
    FullName3: {
        get() { // 计算并返回当前属性的值
            return this.firstName + ' ' + this.secondName;
        },
        set(vel) {  // get 执行之后 把结果返回给 set
            const names = vel.split(' ');
            alert(names[0]);
            alert(names[1]);
            this.firstName = names[0];
            this.secondName = names[1];
        }
    }
}
});

计算属性写在 computed 块中, 可以看到它常用的两种写法, 在使用是时候都是直接使用函数名就行, 因为它并没有参数

  • 函数名 (){}
  • 对象名:{get(){} , set()

上面的FullName1以函数的, 这种书写格式是对 get 方法的默认实现, 方法的返回值会被渲染到页面上

FullName3还重写了 set(val){} 方法, 如果我们在FullName3对应的输入框里面输入新的值, val 就是这个新的值, 在 set 方法中, 如果对当前 vue 实例的 data 中的属性做了改动, 这个改动是双向的, 页面中所有使用对应字段的地方的值, 都会重新渲染

事件的监听:#

它的语法:

// 下面的全部回调函数中, this 都是 vm 对象
var vm = new Vue({
el: '#app',
data: {
    firstName:'',
    secondName:''
},
computed: {},
method: {},
watch: {/* 监视 */
    firstName: function (newVal) {
        this.firstName = newVal + ' ' + this.secondName;
    }
}
});

它会监听 data 中的属性, 当用户改变了 data 中属性的值, 就会触发对应的回调

class 和 style 的绑定#

class 和 style 的属性绑定同样使用的是使用 : 强制属性绑定

首先是写好 css 属性, 才能进一步使用 vue 将属性样式绑定在 html 上

head>
<meta charset="UTF-8">
<title>Title</title>
<style>
    .aClass{
        font-size: 30px; 
        color: #ff4400;
    }
    .bClass{
        color: #00b4ff;
    }
    .cClass{
        color: #1c036c;
    }
</style>
</head>

语法:

  • :class="data 中的 css 属性"
  • :class="{data 中的 css 属性:boolean, data 中的 css 属性:boolean}", 这种对象语法,就是当类名确定但是是否显示不确定时使用,比如让一个导航栏中的一个高亮显示
  • :style="{color:activeColor,fontSize:fontSize +'px'}", style 有单位的 + 单位
<body>
<div id="text">
    <h2>1. class绑定 :: class='X X X '</h2>
    <p :class="a">123123字符串</p>
    <p :class="{aClass:isa, bClass:isb}">class是对象,绑定class 类名:boolean </p>
    <h2>2. style 绑定 </h2>
    <p :style="{color:activeColor,fontSize:fontSize +'px'}">2. style 绑定 </p>
<button @click="update" >点击</button>
</div>

下面的 vue 对象

<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
var vm = new Vue({
    el: '#text',
    data: {
        a:'aClass', // 关联着最上面的 css 样式
        isa:true,
        isb:false,
        activeColor:'red',
        fontSize:'30'
    },
    methods:{
        update() {
            this.a='bClass'
        }
    }
})

条件渲染#

<p v-if="ok">deal</p> --> ok是vue中data的数据,ok为true时, 显示
<p v-else>false</p>  --> 和 v-if成对出现, ok为false, 显示

<p v-show="ok"> 成功 </p>
<p v-show="!ok"> 失败 </p>

列表的渲染 v-for, 及对数组的操作#

下面的例子使用 v-for 遍历数组中的每一个数据, 遍历的同时使用 {{对象. 属性}} 展示属性, 同时可以根据每个 li 的 index 绑定上不同的事件

<body>
<div id="text">
    <ul>
        <!-- 一旦有 v-for  加上 key-->
        <li v-for="(p,index) in person" :key="index">
            {{p.name}} : {{p.age}} : {{index}}
            <button @click="deleteP(index)"> 删除</button>
            <button @click="updateP(index,{name:'Cat',age:20})"> 更新</button>
        </li>
    </ul>
</div>

var vm = new Vue({
el: '#text',
data: {
person: [  /*  vue 只会监视 person 的改变, 不会监视数组中数据的改变 */
    {name: 'tom', age: 23},
    {name: 'tom2', age: 223},
    {name: 'tom2', age: 23},
    {name: 'tom3', age: 232},
    {name: 'tom5', age: 23}
]
},
methods: {
deleteP(index) {
    this.person.splice(index, 1);  // 从 index 开始 删除 1 个
},
updateP(index, person) {
    //  this.person[index]=person; 并没有改变 persons  , 从 index 开始,删除 1 个 添加 person
    this.person.splice(index, 1, person)
}

如果我们更新的 js 是这样写的, 数组中的内容确实会被改变, 但是页面的上数据并不会更新

 this.person[index]=person; 并没有改变 persons  , 从index开始,删除1个 添加person

因为 vue 监听的 person 的改变,person 中只有一个数组, 虽然数组中的数据变了, 但是数组没变, 所以我们使用 vue 的提供的 splice 进行数组的操作

splice(下标, 数量,[ 新的对象数组])#

他可以实现数组的增删改的效果

  • 删除
// 删除起始下标为 1,长度为 2 的一个值 (len 设置 2)
var arr2 = ['a','b','c','d']
arr2.splice(1,2);
console.log(arr2); 
//['a','d']
  • 修改
// 替换起始下标为 1,长度为 1 的一个值为‘ttt’,len 设置的 1
var arr = ['a','b','c','d'];
arr.splice(1,1,'ttt');
console.log(arr);        
//['a','ttt','c','d'] 
  • 添加
var arr = ['a','b','c','d'];
arr.splice(1,0,'ttt');
console.log(arr);        
//['a','ttt','b','c','d']

其他数组相关的操作

  • unshift()添加到第一个
  • shift() 添加到最后一个
  • push() 压栈, 栈顶
  • pop()弹出
  • sort()排序
  • reverse() 反转

数组的映射, 过滤, 排序#

js 的箭头函数和 java8 的 lambda 表达式特别像

  • 映射
array.map(item=>item.id)
// 可以将数组中的每一个元素映射成他的 id 属性 
  • 过滤
persons = person.filter(p => p.name.indexOf(searchModel)>=0); 
// 保留数组中满足条件的对象
  • ES6 的语法糖
    把对象的指定字段存放进声明的多个常量中
const{searchModel,person,orderType} = this;
  • 排序
persons.sort(function (p1,p2) {
                // 升序
                if (orderType===1){
                    return p1.age-p2.age;
                } else if (orderType===2){ // 降序
                    return p2.age-p1.age;
                }

事件绑定相关#

@click绑定事件#

<button @click="text1">text1</button>
<button @click="text2('haha')">text2</button>
<button @click="text3($event)">text3</button>
<button @click="text4">text4</button><!-- 如果没有指定参数进去, 传递进去的就是 event-->
<button @click="text5(123,$event)">text5</button>

 var vm = new Vue({
el:'#test',
methods:{
    text1(){
        alert("text 1");
    },
    text2(msg){
        alert(msg);
    },
    text3(event){
        alert(event.target.innerHTML);
    },
    text4(event){
        alert(event.target.innerHTML);
    },
    text5(msg,event){
        alert(msg+event.target.innerHTML);
    },

可以看到@click使用 vue 中 method 的函数时, 如果没有参数, 可以简写, 去掉 (), 如果不写参数, 传递进去的是事件本身 event , text 三中通过 event 拿到了标签的文本内容

@click.prevent阻止事件的默认行为#

<a href="http:www.baidu.com" @click.prevent="text8">百度一下</a>  <!-- 阻止事件的默认行为 -->

监听某个按键的点击事件#

<input type="text" @keyup.enter="text9"> <!--  @keyup.13(名字)  监听某一个键的点击事件 -->

收集表单数据#

使用 vue 将用户填入表单中的数据收集起来, 收集到哪里去? 其实是收集到 vue 的 data 块中的属性中

其实就是在 html 使用v-model暴力绑定 dom 监听, 将单选框, 输入框, 多选框中用户输入进去的内容和 data 中的属性关联起来

  • input,textarea 等输入框, 收集起来的值就是用户输入进去的值
  • 单选框 radio , 多选框 checkbox 等选择框, 收集起来的值的 html 中的 value 属性的值
<h1>表单中最终提交给后台的是 value值</h1><br>
<h2> 使用v-model实现表单数据的自动收集 </h2>
<form action="/XXX"  @submit.prevent="handleSubmit" ><!-- 阻止表单的默认自动提交事件 -->
   <span>用户名:</span>
   <input type="text" v-model="username"><br>
   <span>密码</span>
   <input type="password"  v-model="pwd" ><br>

   <span>性别</span><br>
   <input type="radio" id="female" value="女" v-model="sex">
   <label for="female"></label><br>
   <input type="radio" id="male"  value="男" v-model="sex">
   <label for="male"></label><br><br>

   <span>爱好</span><br>
   <input type="checkbox" id="basket" value="basket" v-model="likes">
   <label for="basket">篮球</label>
   <input type="checkbox" id="foot" value="foot"  v-model="likes">
   <label for="foot">足球</label>
   <input type="checkbox" id="pingpang"  value="pingpang"   v-model="likes">
   <label for="pingpang">乒乓球</label><br><br>

   <span>城市</span><br>
   <select v-model="cityId">
       <option value="">未选择</option>
       <option :value="city.id" v-for="(city,index) in allCitys" :key="index">{{city.name}}</option>
   </select>
   <span>介绍:</span>
   <textarea name="" id="" cols="30" rows="10" v-model="dec"></textarea>

<input type="submit" value="注册"><br>

    </form>
    </div>
    <script type="text/javascript" src="js/vue.js"></script>

<script type="text/javascript">

var vm = new Vue({
    el:'#test',
    data:{
        username:'',
        pwd:'',
        sex:'女',
        likes:['foot'],
        allCitys:[{id:1,name:'北京'},{id:2,name:"山东"},{id:3 ,name:"青岛"}],
        cityId:'3' /* 默认让 3 被选中 */,
        dec:"哈哈"
    },

    methods:{
        handleSubmit(){
            alert(this.username+this.pwd);
            alert(this.sex);
        }
    }
})

vue 的生命周期#

vue 对象在创建初始化的过程中一次执行如下声明周期相关的方法, 根据这个特性, 通常把加载进入一个新的页面中时去发送 ajax 请求的方法放到 mounted(){}, 收尾工作放在 beforeDestroy(){}

var vm = new Vue({
el: "#text",
data: {},

beforeCreate() {  // 创建之前回调
    console.log("beforeCreate");
},

created() {  // 创建之后回调
    console.log("created");
},

beforeMount() {
    console.log("beforrMount");
},

// todo 常用, 异步操作, 比如发起 ajax 请求获取数据, 添加定时器
mounted() { // 初始化显示之后会立即调用一次
    console.log("mounted");
    this.intervalId = setInterval(() => {
        console.log("干掉 vm 之后, 定时器还在跑, 内存泄露了");
        this.isShow = !this.isShow;
    }, 1000);

    /*
      如果下面不使用箭头回调函数, this 就是 window, 而不是 vm
    * setInterval(() => {
        this.isShow= !this.isShow;
    },1000);
    * */
},

// 更新阶段
beforeUpdate() {  // 更新阶段之前回调
    console.log("beforeUpdate");
},

updated() {  // 更新阶段之后回调
    console.log("updated");
},

// 死亡阶段
// todo 常用 收尾工作
beforeDestroy() {  // 死亡之前回调一次
    console.log("beforeDestroy");
    clearInterval(this.intervalId);
},

destroyed() {
    console.log("destroyed");
},

methods: {}
}
});

ES 的语法糖, 箭头函数#

比如在设置定时器时, 定时器中需要对 vue 的属性进行操作, 在定时器的代码块中 this 指的是定时器对象,es6 的箭头语法解决就这个问题, 在箭头函数中 this 没有的属性, 会到外层的 vue 中来找

this.intervalId = setInterval(() => {
    console.log("干掉 vm 之后, 定时器还在跑, 内存泄露了");
    this.isShow = !this.isShow;
}, 1000);

动画#

按照 vue 的下面的几步要求, vue 会给目标元素添加或者移除特定的 css, 实现动画的效果

  1. 需要添加动画的标签被 <transition name="" > XXX </transition>包裹

<div id="test">
   <transition name="YYY">
    <p v-show="isShow" class="">toggle</p>
    <button @click="isShow=!isShow">toggle</button>

    </transition>
</div>


<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
    new Vue({
        el: '#test',
        data() {
            return {
                isShow: true
            }
        }
    });

  1. 定义以 .YYY-开头的 css 属性, 这个 YYY 就是上面自定义的 YYY, 需要在这些自定义的属性中指定过度的属性以及隐藏的属性

一个简单的动画效果标签从隐藏 -> 出现, 再从出现到隐藏的过程, 就像下面这样

v-enter  v-enter-to   v-leave    v-leave-to
  隐藏     出现         出现       隐藏

自定义这四个时期的状态

/* 显示的过度效果*/
.YYY-enter-active {
    transition: all 1s;
}

/* 隐藏的过度效果*/
.YYY-leave-active {
    transition: all 3s;
}

/* 从无到有的样式  */
.YYY-enter {
    opacity: 0;
}

/* 从有到无的样式  */
.YYY-leave-to {
    opacity: 0;
    transform: translateX(20px);  /* 离开时,向X轴的正方向移动20px*/
}

格式化时间的插件库#

点击进入 moment.js 网址 , 在这里可以找到需要引入的 script 标签

点击进入 moment.js 的文档 在文档中可以找到对应的格式和例子

 <div id="test">
    <h2>显示格式化的日期时间</h2>
    <p>{{date}}</p>
    <p>默认完整: {{date | dateFormat}}</p><!--  一旦我们这么写, 他就会把date的值,传递给dateFormat函数 -->
    <p>年月日: {{date | dateFormat('YYYY-MM-DD')}}</p><!--  一旦我们这么写, 他就会把date的值,传递给dateFormat函数 -->
    <p>时分秒: {{date | dateFormat('HH🇲🇲ss')}}</p><!--  一旦我们这么写, 他就会把date的值,传递给dateFormat函数 -->

</div>

/*  这个在官网上查找 */
<script  type="text/javascript" src="https://cdn.bootcss.com/moment.js/2.21.0/moment.js"></script>

<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
        // 自定义过滤器
    Vue.filter('dateFormat',(value,format)=>{ /*  Vue 是函数对象 */
      return moment(value).format(format || 'YYYY-MM-DD HH🇲🇲ss');
    });

    new Vue({
        el:'#test',
        data:{
            date:new Date()
        }
    });

Es 的语法糖#

es6 的语法: 形参默认值 , 没传值的话, 就使用默认值

 function(value,format="YYYY-MM-DD"){
     return moment(value).format(format);
 }

vue 的指令#

常见的原生指令如下

v:text : 更新元素的textContent <br>
v:html : 更新元素的innerHtml<br>
v-if: true    如果为true,标签才会输出到页面 <br>
v-else:    如果为false,标签才会输出到页面 <br>
v-show:  通过控制display的样式来控制显示和隐藏<br>
v-for:    遍历数组对象 <br>
v-on:   绑定监听事件, 一般直接写  @ <br>
v-bind:  强制绑定解析表达式  一般简写成 : <br>
v-model:  双向数据绑定 <br>
ref:  指定唯一的标识, Vue对象可以通过 $els 属性来访问这个元素对象  <br>
v-cloak: 防止闪现可能应为网速的原因{{msg}} 一直解析不了, 于是用户就看到它了,不友好, 于是 vue推出 与css配合 [v-cloak] {display:none}  <br>

补充最后两个

  • ref 指定唯一的标识, Vue 对象可以通过 $els 属性来访问这个元素对象
  • 防止闪现可能应为网速的原因 {{msg}} 一直解析不了, 于是用户就看到它了, 不友好, 于是 vue 推出 与 css 配合 [v-cloak]

例子:

<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        [v-cloak] {   /*  回去寻找有这个属性名的标签  [v-cloak]   , 就是下面的 p 标签 */
            display:none
        }
    </style>
</head>
<body>

<div id="test">
    <p ref="content123">哈哈哈哈</p>
    <button @click="hint">提示</button>
    //  v-cloak="" + 上面的css 样式避免 {{ }} 闪现的效果
    <p v-cloak="">{{msg}}</p>
    <br>

</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
    // 注册全局指令
    Vue.directive('')

    new Vue({
       el:'#test',
       data:{
        msg: '嘿嘿'
       } ,
        methods:{
            hint(){
              //  因为 `<p ref="content123"> 哈哈哈哈 </p>` 使用了 ref, 所以 vue 可以通过 this.$refs.content123 找到指定的这个元素
                alert(this.$refs.content123.textContent)
            }
        }
    });