VUE学习笔记

框架和插件

  • 框架不能轻易换,库可以

MVC和MVVM

  • node(后端)中MVC:后端的分层开发概念

  • MVVM是前端视图层的概念,主要关注视图层分离,把前端视图层分成三部分:Model、view、vm viewmodel

image

VUE基础

一、数据与方法
  • 当数据改变时,视图会进行重渲染。值得注意的是只有当实例被创建时就已经存在于 data 中的属性才是响应式的。
  • 使用 Object.freeze(),会阻止修改现有的属性,也意味着响应系统无法再追踪变化。
  •  var obj = {
        foo: 'bar'
     }
     Object.freeze(obj)
     new Vue({
       el: '#app',
       data: obj
     })
      <div id="app">
          <p>{{ foo }}</p>
          <!-- 这里的 `foo` 不会更新! -->
          <button v-on:click="foo = 'baz'">Change it</button>
      </div>
    
  • 不要在选项属性或回调上使用箭头函数,因为箭头函数并没有 this,this 会作为变量一直向上级词法作用域查找,直至找到为止,经常导致错误。
  • 动态参数表达式:从 2.6.0 开始,可以用方括号括起来的 JavaScript 表达式作为一个指令的参数:<a :[attributeName]="url"> ... </a>
    • 动态参数表达式有一些语法约束,因为某些字符,如空格和引号,放在 HTML attribute 名里是无效的。
  • 还需要避免使用大写字符来命名键名,因为浏览器会把 attribute 名全部强制转为小写:

二、指令

  • v-cloak <style>[v-cloak]{display:none }</style> 解决插值表达式闪烁问题
  • v-text:没有闪烁问题,会把标签中内容覆盖v-text=“msg”
  • v-html:渲染成html
  • v-once:只拿一次数据进行显示,后面数据更改,页面不会跟着更改
  • v-pre:不解析{{}},直接显示

事件修饰符

  • 常见事件修饰符
    • @click.stop=""----阻止事件冒泡
    • @click.prevent ----阻止默认行为
    • @click.self ----只允许自身触发
    • @click.captures----实现捕获触发机制
    • @click.once----只执行一次
    • @keyup.enter----回车键
  • 使用修饰符时,顺序很重要;相应的代码会以同样的顺序产生。因此,用 v-on:click.prevent.self 会阻止所有的点击,而 v-on:click.self.prevent 只会阻止对元素自身的点击。

三、按键修饰符

  • Vue.config.keyCodes.f2 = 113 使用 @keyup.f2
  • 为了在必要的情况下支持旧浏览器,Vue 提供了绝大多数常用的按键码的别名: .enter、 .tab、 .delete (捕获“删除”和“退格”键)、 .esc、 .space、 .up、 .down、 .left、 .right

四、内联样式style(v-bind,语法糖:)

  • 对象就是无序键值对的集合 "{'color‘:’red','font-size':'50px'}"
  • 直接传递对象,对象在data中定义
  • 在data中定义多个样式对象,以数组的方式传给style

五、class类属性(v-bind,语法糖:)

  • 使用方式一:直接传递一个数组,数组中的需要单引号 "['more','add']"
  • 使用方式二: 在数组中使用三元表达式
    "['more','add',{'another':item.age<30}]"
  • 使用方式三:直接传递对象 “{active:true,thin:false}” 对象属性为类名

六、条件渲染(v-if 、v-else-if、 v-else、v-show)

  • v-if 每次都会重新创建或删除
  • v-show,不会重新创建或删除,只是切换display属性
  • 元素涉及频繁的切换,使用v-show。

七、列表渲染(v-for)

  • v-for="( item, index ) in list"
  • 顺序:值-键-索引
  • in后面放数组,表示循环几次
  • 在组件中使用v-for时,需要指定key属性
  • 如果不添加key,则在数组中新增元素的时候,创建虚拟dom会进行对比,直接把原来4的li的值改成0000,4以后的往后移动;添加以后会插入0000的li(提高性能)
      <div id="app">
          <ul>
              <li v-for="item in thingArray">{{item}}</li>
          </ul>
          <button @click="addItem">在3-4中间添加元素</button>
      </div>
      <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
      <script>
          const app = new Vue({
              el: "#app",
              data: {
                  thingArray: [1, 2, 3, 4, 5, 6, 7]
              },
              methods: {
                  addItem() {
                      this.thingArray.splice(2, 0, '0000')
                      console.log(this.thingArray)
                  }
              }
          })
      </script>
    

八、v-model的使用

<body>
    <div id="main">
        <input type="text" v-model="inputValue">
        <span>输入框的值是:{{inputValue}}</span>
        

        <h3>2.v-model的原理</h3>
        <input type="text" :value="inputValue" @input="changeInputValue">
        <span>输入框的值是:{{inputValue}}</span>
        

        <h2>3.v-model和radio</h2>
        <label for="male">
            <input type="radio" id="male" name="sex" value="男" v-model="gender">男
        </label>
        <label for="female">
            <input type="radio" id="female" name="sex" value="女" v-model="gender">女
        </label>
        <h5> ---您选择的性别是:{{gender}}</h5>
        

        <h2>4.v-model和checkbox</h2>
        <label for="agree">
            <input type="checkbox" id="agree" v-model="isAgree">知情同意
        </label>
        <h4>是否选择{{isAgree}}</h4>
        <button :disabled="!isAgree">下一步</button>
        
        
        <h2>5.v-model 和 checkbox的多选框</h2>
        <label v-for="hobby in constHobby">
            <input type="checkbox" :id="hobby" :value="hobby" :key="hobby" v-model="chooseHobby"> {{hobby}}
        </label>
        <h5>你选择的爱好是:{{chooseHobby}}</h5>
        
        
        <h2>6.v-model和select</h2>
        <select name="" id="" v-model="selectHobby">
            <option v-for="hobby in constHobby" :value="hobby" >{{hobby}}</option>
        </select>
        <h5>你选择的爱好是:{{selectHobby}}</h5>

        <h2>7.v-model和select多选</h2>
        <select name="" id="" v-model="selectHobbyArray" multiple>
            <option v-for="hobby in constHobby" :value="hobby" >{{hobby}}</option>
        </select>
        <h5>你选择的爱好是:{{selectHobbyArray}}</h5>

        <h2>8.v-model的修饰符</h2>
        <!-- 懒加载,只有在按回车和失去焦点的时候会重新加载该数据 -->
        <input type="text" v-model.lazy="lazyValue">
        <span>输入框的值:{{lazyValue}}</span>
        <!-- 去掉前后空格 -->
        <input type="text" v-model.trim="trimValue">
        <span>输入框的值:{{trimValue}}</span>
        <!-- 数据类型改为number -->
        <input type="text" v-model.number="numberValue">
        <span>输入框的值:{{numberValue}} {{typeof numberValue}}</span>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        const app = new Vue({
            el: "#main",
            data: {
                inputValue: "111",
                gender: "男",
                isAgree: false,
                constHobby: ['篮球', '羽毛球', '乒乓球', '足球'],
                chooseHobby: [],
                selectHobby: '篮球',
                selectHobbyArray:[],
                lazyValue:'',
                trimValue:"",
                numberValue:""
            },
            methods: {
                changeInputValue(event) {
                    this.inputValue = event.target.value;
                }
            }
        })
    </script>
</body>

九、computed:计算属性

  • 包含本实例里面的变量发生变化触发(数据联动)
  • 在html中
<div id="box">
    <label>firstName:</label><input type="text" v-model="firstName">+
    <label>lastName:</label><input type="text" v-model="lastName">=
    <label>fullName: </label><input type="text" v-model="fullName">
</div>
  • 在JS中
<script src="js/vue.js"></script>
<script>
   new Vue({
       el:'#box',
       data:{
           firstName: '',
           lastName: ''
       },
       computed:{
          //计算属性的本质是对象,有一个get方法和set方法,我们一般不同set方法(没有set方法,相当于一个只读属性)
           fullName:{
                get:function(){
                   return this.firstName+'-'+this.lastName
               },
              set:function(){}
           }
          // 没有set方法,可以直接如下写
           fullName:function () {
               return this.firstName+'-'+this.lastName
           }
       }
   })
</script>
  • 特点:
    • 计算属性在引用的时候,不需要加()来调用,直接把他当成是普通的属性使用
    • 和methods的区别:只要在计算属性的function内部,所用到的data中的数据发生变化,computed就会重新计算这个 计算属性,当触发重新渲染时,调用方法将总会再次执行函数。
    • 计算属性求值的结果会被缓存起来,方便下次使用;如果计算属性方法中的数据没有发生变化,则不会重新对这个 计算属性 求值(computed种的属性: 在页面中使用四次,只会调用一次,其余使用缓存显示)(methods 中的方法,在页面中使用四次,会调用四次)

watch :侦听(异步场景)

  • 通常更好的做法是使用计算属性而不是命令式的 watch 回调
<script src="js/vue.js"></script>
<script>
   new Vue({
       el:'#box',
       data:{
           firstName: '',
           lastName: '',
           fullName: ''
       },
       watch:{
           firstName: function (newValue) {
               this.fullName = newValue +'----'+ this.lastName
           },
           lastName: function (newValue) {
               this.fullName = this.firstName +'----'+ newValue
           }
       }
   })
</script>
  • 上面代码是命令式且重复的。

过滤器:

  • 全局过滤器:Vue.filter('过滤器的名称',function (dateStr,formate) { } )
    • 函数的第一个参数是管道前面传递过来的数据
  • 私有的过滤器:new Vue({ el:'#box',data:{},
    filters:{ dateFormate:function (dateStr, formate) {} } })

组件化

  • 定义:为了拆分Vue实例的代码量,能够用不同的组件划分不同的功能块。

    • 模块化:从代码逻辑的角度进行划分,方便代码分层开发,保证模块的职能单一。
    • 组件化:从UI界面的角度进行划分,前端的组件化,方便UI组件的重用
  • 创建组件的方式

    1. 创建方式一:使用Vue.extend({template:' '})来创建全局的组件。使用Vue.component('组件名称',创建出来的组件)
    • var myCompontnt = Vue.extend({
        template:`<h2>这是用extend创建</h2>` 
       })
       Vue.component('myCompontnt',myCompontnt);
      
    • Vue.component('myCompontnt',Vue.extend({
        template:`<h2>这是用extend创建</h2>`
      }));
      
    1. 创建方式二:Vue.component('组件名称',组件的模板template)
        Vue.component('myCompontnt',{
        template:`<h2>这是用extend创建</h2>`
       });
      
    2. 创建方式三:将template写在html中 Vue.component('组件名称',{template:'#id'})
  • 组件注册

    • Vue.component('my-component-name',{ /* ... */ })
    • 全局注册: Vue.component('component-a',{ /* ... */ })在注册之后可以用在任何新创建的 Vue 根实例 (new Vue) 的模板中
    • 局部注册:
    • var ComponentA = { /* ... */ }
      var ComponentB = { /* ... */ }
      var ComponentC = { /* ... */ }
      //components 选项中定义你想要使用的组件:
      new Vue({
        el: '#app',
        components: {
            'component-a': ComponentA,
            'component-b': ComponentB
          }
      })
      
      • 局部注册的组件在其子组件中不可用
  • 组件中的data

    • 组件可以拥有自己的data数据,组件中的data是一个方法,而且必须返回一个对象
  • 组件名的命名和使用

    • 在使用驼峰命名定义一个组件时,在引用这个元素时使用kebab-case的方式。不使用驼峰则直接使用定义的名称。
  • 组件切换可以使用<component :is="存组件名称的参数">通过改变参数

  • 传递参数

    • 父组件向子组件传递参数使用props
    •  
        Vue.component('todo-item', {
             props: ['todo'],
             template: '<li>{{ todo.text }}</li>'
        })
          /*使用*/
        <todo-item
          v-for="item in groceryList"
           :todo="item"
           :key="item.id"
         ></todo-item>
      
      • props中的数据是只读的,data中的数据是可读可写的
      • prpos:使用驼峰命名法,在传参时需要使用其等价的短横线分隔命名
      • prop 指定的值类型。以对象形式列出 prop。props: { title: String }
      • 在JavaScript 中对象和数组是通过引用传入的,所以对于一个数组或对象类型的 prop 来说,在子组件中改变这个对象或数组本身将会影响到父组件的状态。
    • 子组件通过事件调用向父组件传值
    <div id="app7">
       <childNode @func="show"></childNode>
    </div>
    <template id="temp1"><!--这是样式-->
       <div>
          <h2>这里是子组件</h2>
          <input type="button" value="调用父组件函数" @click="emitfunction">
       </div>
    </template>
    <script src="js/vue.js"></script>
    <script>
       Vue.component('childnode',{
          template:'#temp1',
          data : function(){
          return {
                user:[{id:1,name:'张三'},{id:2,name:'李四'}]
           }
         },
        methods:{
            emitfunction (){
                this.$emit('func','111','222',this.user)
            }
        }
    });
    var app7 = new Vue({
        el:'#app7',
        data:{
            msg:'父组件的数据',
            user:[]
        },
        methods:{
            show(msg1,msg2,data){
                console.log('这是父组件的函数调用'+msg1+'---'+msg2)
                this.user = data
                console.log(this.user[0].id)
            }
        }
    })
    </script>
    
  • 使用ref获取组件元素和dom方法

     <div id="app7">
        <input type="button" value="使用ref" @click="getDomMesg">
        <h2 ref="title">这是父组件的H2啊</h2>
        <childnode ref="child"></childnode>
    </div>
    <template id="temp1">
    <div>
        <h2>这里是子组件</h2>
    </div>
    </template>
    <script src="js/vue.js"></script>
    <script>
        Vue.component('childnode',{
            template:'#temp1',
            data : function(){
                 return {  msg:'这是子组件的msg' }
            },
            methods:{
                 show (){  console.log('调用了子组件的方法------')  }
            }
        });
      var app7 = new Vue({
          el:'#app7',
          data:{
                msg:'父组件的数据',
                user:[]
          },
          methods:{
                getDomMesg(){
                    console.log(this.$refs.title.innerText)
                    console.log(this.$refs.child.msg)
                    this.$refs.child.show()
                 }
           }
      })
    </script>
    

自定义全局指令

Vue.directive( 'focus',{
    updated:function (el) {  },//可执行多次,当VNode更新时使用
    bind:function (el,bindding) {  },//只执行一次,当指令绑定到元素时。第二个参数可获得传入的参数,bindding。value。和样式有关的操作。
    inserted:function (el) {  el.focus() }//只执行元素,当元素插入dom中。和JS行为有关的操作在这执行
})
  • 定义私有指令
 directives:{ 
    'fontweight':{ 
         bind:function (el,bindding){  
             el.style.fontWeight = bindding.value
         }  
      } 
   }
  • 简写:
  'fontsize':function (el,bindding) {
         el.style.fontSize = parseInt(bindding.value) +'px'
  }

生命周期

  • 生命周期钩子 = 生命周期函数 = 生命周期事件
  • 创建期间的生命周期函数
    1. beforeCreate: data和methods中的数据都还未被初始化
    2. created: data和methods中的数据初始化完成,调用数据和方法最早在这个方法中
    3. beforeMount:模板在内存中已经编辑好了,但是还没渲染到页面上,页面中的元素还未被真正的替换,只是一些模板字符串。document.getElementbyId(‘info’).innerText----->获取不到
    4. mounted:实例创建期间的最后一个生命周期函数,实例完全创建好了,没有其他操作的话,这实例就不变。操作dom节点最早在mounted
  • 实例运行期间的生命周期函数
    1. updated和beforeUpdate在data中数据发生改变时触发0次或多次。
    2. beforeUpdate执行时,页面中的数据还是旧的,data中数据是最新的,页面和最新的数据还未同步
    3. updated事件执行时,界面和data已经保持同步,都是最新的
  • 销毁阶段的钩子
    1. beforeDestroy:Vue实例从运行阶段进入销毁阶段。实例上的所有data和methods以及过滤器、指令等都处于可用状态,此时还未真正销毁。
    2. destoryed。组件完全被销毁。组件中所有的数据等都不可以用。
生命周期.png

v-resource发起get、post、jsonp请求

 methods:{
      getMessage (){
          this.$http.get('http://vue.studyit.io/api/getlunbo').then(
               ( result ) => {
                   this.getMessageResult = result.body;
                   console.log(result.body)
           })
      },
       postMessage () {
          this.$http.post('http://vue.studyit.io/api/post',{headers:
              {'Access-Control-Allow-origin': 'http://localhost:8080/resource'}},
              {emulateJSON:true}).then(
                  ( result ) => {
                      this.PostMessageResult = result.body;
                      console.log(result.body)
                 })
      },
       jsonpMessage (){
          this.$http.jsonp('http://vue.studyit.io/api/jsonp').then(
             ( result ) => {
                this.JsonpMessageResult = result.body;
                console.log(result.body) 
             })
       }
  }

动画

图片.png
  • 在html中
<button @click="flag = !flag">toggle</button>
<transition> 
        <h2 v-show="flag">hello animation</h2>
</transition>

+在css中

  <style>
    .v-enter,
    .v-leave-to{
        transform: translateX(200px);
        opacity: 0;
    }
    .v-enter-active,
    .v-leave-active{
        transition: all 0.8s ease;
   }
 </style>
  • 在<transition name='disp'> 定义了name后,修改v-前缀为disp-
  • 使用第三方类库animate.css 在<transition enter-active-class='animated bounceIn '>
  • 使用钩子函数完成半场动画
    • 在html中
 <button @click="flag = !flag">加入购物车</button>
    <transition   @before-enter="beforeEnter" @enter="Enter" @after-enter="afterEnter">
        <div class="ball" v-show="flag"></div>
    </transition>

  • 在JS中
new Vue({
        el:'#box',
        data:{
            flag:false
        },
        methods:{
            beforeEnter (el){
                el.style.transform = 'translate(0,0)'
            },
            Enter (el,done){
                el.offsetLeft //没有实际作用,但是不写,没有显示动画效果
                el.style.transform = 'translate(200px,300px)'
                el.style.transition= 'all 1s ease'
                done()//done是afterEnter函数的引用,不调用会延迟
            },
            afterEnter(el){//flag的作用是控制小球的隐藏和显示;跳过后半场动画
                this.flag = !this.flag//直接使用opacity无法实现该功能
            }

        }
    })
  • <transition-group>实现列表动画
    <div id="box">
    <label>Id : <input type="text" v-model="id"></label>
    <label>Name : <input type="text" v-model="name"></label>
    <button @click="add">添加</button>
    <ul>
        <transition-group>
            <li v-for="item in list" :key="item.id" @click="del(item.id)">{{item.id}} and {{item.name}}</li>
        </transition-group>
    </ul>
    </div>
    
    • style
         <style>
         li{
             width: 400px;
             height:30px;
             font-size: 20px;
             border: 1px dashed #999;
             margin: 5px;
             list-style: none;
             padding-left: 10px;
         }
         li:hover{
             background-color: burlywood;
         }
         .v-enter,.v-leave-to{
             opacity: 0;
             transform: translateY(80px);
         }
         .v-enter-active,.v-leave-active{
             transition: all 3s ease;
         }
         .v-move{
             transition: all 1s ease;
         }
         .v-leave-active{
             position: absolute;
         }
         </style>
      
  • 动画-transition-group中appear和tag属性的作用
    • appear 实现页面刚展示出来的时候的效果
    • tag用来指定渲染成什么属性,没有添加默认渲染成<span>属性
    • mode可以实现组件切换时进出的先后顺序

插槽

  • 编译作用域:父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。
  • 后备内容(默认的)内容)
  • 具名插槽 <slot> 元素有一个特殊的特性:name
  • 任何没有被包裹在带有 v-slot 的 template>中的内容都会被视为默认插槽的内容。任何没有被包裹在带有 v-slot 的 template>中的内容都会被视为默认插槽的内容。
  • 插槽prop
    <slot v-bind:user="user">{{ user.lastName }} </slot>
  • v-slot也有缩写,字符 “#”
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335

推荐阅读更多精彩内容