Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。
特点: 易用,灵活,高效
渐进式框架   
  
逐一递增 vue + components + vue-router + vuex + vue-cli
框架是库的升级版
new Vue({
    el:'#app',
    template:'<div>我是姜文~~</div>', // 优先使用template
    data:{}
});

在传统的mvc中除了model和view以外的逻辑都放在了controller中,导致controller逻辑复杂难以维护,在mvvm中view和model没有直接的关系,全部通过viewModel进行交互
允许开发者声明式地将 DOM 绑定至底层 Vue 实例的数据。在使用数据前需要先声明
<div id="app">
  {{ 1+1 }}
  {{ msg == 'hello'?'yes':'no' }}
  {{ {name:1} }}
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script>
  let vm = new Vue({
      el:'#app',
      data:{
          msg:'hello'
      }
  })
</script>
function notify(){
    console.log('视图更新')
}
let data = {
    name:'jw',
    age:18,
    arr:[]
}
// 重写数组的方法
let oldProtoMehtods = Array.prototype;
let proto = Object.create(oldProtoMehtods);
['push','pop','shift','unshift'].forEach(method=>{
    proto[method] = function(){
        notify();
        oldProtoMehtods[method].call(this,...arguments)
    }
})
function observer(obj){
    if(Array.isArray(obj)){
        obj.__proto__ = proto
        return;
    }
    if(typeof obj === 'object'){
        for(let key in obj){
            defineReactive(obj,key,obj[key]);
        }
    }
}
function defineReactive(obj,key,value){
    observer(value); // 再一次循环value
    Object.defineProperty(obj,key,{ // 不支持数组
        get(){
            return value;
        },
        set(val){
            notify();
            observer(val);
            value = val;
        }
    });
}
observer(data);
data.arr.push(1);
let obj = {
    name:{name:'jw'},
    arr:['吃','喝','玩']
}
let handler = {
    get(target,key,receiver){
        if(typeof target[key] === 'object' &&  target[key] !== null){
            return new Proxy(target[key],handler);
        }
        return Reflect.get(target,key,receiver);
    },
    set(target,key,value,receiver){
        if(key === 'length') return true;
        console.log('update')
        return Reflect.set(target,key,value,receiver);
    }
}
let proxy = new Proxy(obj,handler);
proxy.name.name = 'zf';
数组的变异方法(不能通过通过长度,索引改变数组)
  <div id="app">
      {{hobbies}}
  </div>
  <script src="node_modules/vue/dist/vue.js"></script>
  <script>
      let vm = new Vue({
          el:'#app',
          data:{
              hobbies:['洗澡','吃饭','睡觉']
          }
      });
      vm.hobbies[0] = '喝水';// 数据变化视图不刷新
      vm.hobbies.length--;// 数据变化视图不会刷新
  </script>
  vm.hobbies = ['喝水']; // 替换的方式
  vm.hobbies.push('吃饭'); // push slice pop ...变异方法
不能给对象新增属性
  <div id="app">
  {{state.a}}
  </div>
  <script src="node_modules/vue/dist/vue.js"></script>
  <script>
      let vm = new Vue({
          el:'#app',
          data:{
              state:{count:0}
          }
      });
      //vm.state.a = 100; // 新增熟悉不会响应到视图上
  </script>
使用vm.$set方法强制添加响应式数据
  function $set(data,key,val){
  if(Array.isArray(data)){
      return data.splice(key,1,val);
  }
  defineReactive(data,key,val);
  }
  $set(data.arr,0,1);
  vm.$set(vm.state,'a','100');
vm.$set();
  vm.$set(vm.state,'a','100');
vm.$watch();
  vm.$watch('state.count',function(newValue,oldValue){
      console.log(newValue,oldValue);
  });
vm.$mount();
  let vm = new Vue({
      data:{state:{count:0}}
  });
  vm.$mount('#app');
vm.$nextTick();
  vm.state.count = 100; // 更高数据后会将更改的内容缓存起来
  // 在下一个事件循环tick中 刷新队列
  vm.$nextTick(function(){
      console.log(vm.$el.innerHTML);
  });
vm.$data
vm.$el
在vue中 指令 (Directives) 是带有 v- 前缀的特殊特性,主要的功能就是操作DOM
v-once
  <div v-once>{{state.count}} </div>
v-html (不要对用户输入使用v-html显示)
  <div v-html="text"></div>
v-for遍历数组
  fruits:['香蕉','苹果','桃子']
  <div v-for="(fruit,index) in fruits" :key="index">
      {{fruit}}
  </div>
v-for遍历对象
  info:{name:'jiang',location:'回龙观',phone:18310349227}
  <div v-for="(item,key) in info" :key="key">
      {{item}} {{key}}
  </div>
template的使用
  <template v-for="(item,key) in fruits">
      <p v-if="key%2">hello</p>
      <span v-else>world</span>
  </template>
key属性的应用
  <div v-if="flag">
      <span>珠峰</span>
      <input key="2"/>   
  </div>
  <div v-else>
      <span>架构</span>
      <input key="1"/>  
  </div>
key尽量不要使用索引
   <ul>
      <li key="0">🍌</li>
      <li key="1">🍎</li>
      <li key="2">🍊</l>
  </ul>
  <ul>
      <li key="0">🍊</li>
      <li key="1">🍎</li>
      <li key="2">🍌</li>
  </ul>
Class 与 Style 绑定
数组的绑定
      <div :class="['apple','banana']" ></div>
      <div :class="{banana:true}"></div>
对象类型的绑定
      <div :class="['apple','banana']" ></div>
      <div :class="{banana:true}"></div>
      <div :style="[{background:'red'},{color:'red'}]"></div>
      <div :style="{color:'red'}"></div>
    <input type="text" :value="value" @input="input">
    <input type="text" v-model="value">
select
  <select v-model="select">
      <option 
          v-for="fruit in fruits"
          :value="fruit">
              {{fruit}}
      </option>
  </select>
radio
  <input type="radio" v-model="value"  value="男">
  <input type="radio" v-model="value"  value="女">
checkbox
  <input type="checkbox" v-model="checks" value="游泳" >
  <input type="checkbox" v-model="checks" value="健身">
修饰符应用 .number .lazy .trim
  <input type="text" v-model.number="value">
  <input type="text" v-model.trim="value">
Vue.config.keyCodes = {
  'f1':112
}
计算属性和watch的区别 (异步)
  let vm = new Vue({
      el:'#app',
      data:{
          firstName:'姜',
          lastName:'文',
          fullName:''
      },
      mounted(){
          this.getFullName();
      },
      methods:{
          getFullName(){
              this.fullName = this.firstName + this.lastName
          }
      },
      watch:{
          firstName(){
              setTimeout(()=>{
                  this.getFullName();
              },1000)
          },
          lastName(){
                  this.getFullName();
          }
      }
      // 计算属性不支持异步
      // computed:{
      //    fullName(){
      //        return this.firstName + this.lastName;
      //    }
      // }
  });
计算属性和 method的区别 (缓存)
<div>{{'hello' | capitalize(3)}}</div>
Vue.filter('capitalize',(value,count=1)=>{
  return value.slice(0,count).toUpperCase() + value.slice(count);
});
编写一个自定义指令
钩子函数bind,inserted,update
      <input type="text" v-focus.color="'red'">
      Vue.directive('focus',{
          inserted:(el,bindings)=>{
              let color = bindings.modifiers.color;
              if(color){
                  console.log('color')
                  el.style.boxShadow = `1px 1px 2px ${bindings.value}`
              }   
              el.focus();
          }
      });
clickoutside指令
<div v-click-outside="change">
  <input type="text"  @focus="flag=true" >
  <div v-show="flag">
      contenter
  </div>
</div>
let vm = new Vue({
  el:'#app',
  data:{
      flag:false
  },
  methods:{
      change(){
          this.flag = false
      }
  },
  directives:{
      'click-outside'(el,bindings,vnode){
          document.addEventListener('click',(e)=>{
              if(!el.contains(e.target,vnode)){
                  let eventName = bindings.expression;
                  vnode.context[eventName]()
              }
          })
      }
  }
})
  
vue中的动画就是从无到有或者从有到无产生的。有以下几个状态 transition组件的应用
.v-enter-active,.v-leave-active {
    transition: opacity 0.25s ease-out;
}
.v-enter, .v-leave-to {
    opacity: 0;
}
切换isShow的显示或者隐藏就显示出效果啦~
<button @click="toggle">toggle</button>
<transition>
    <span v-show="isShow">珠峰架构</span>
</transition>
默认的name是以v-开头,当然你可以自己指定name属性来修改前缀
.v-enter-active {
    animation:zoomIn  2s linear
}
.v-leave-avitve{
    animation:zoomOut 2s linear
}
直接修改激活时的样式
<transition 
    enter-active-class="zoomIn"
    leave-active-class="zoomOut"
>
    <span class="animated" v-show="isShow">珠峰架构</span>
</transition>
<transition 
    @before-enter="beforeEnter"
    @enter="enter"
    @after-enter="afterEnter"
>   
    <span class="animated" v-show="isShow">珠峰架构</span>
</transition>
对应的钩子有before-leave,leave,after-leave钩子函数,函数的参数为当前元素
beforeEnter(el){
    el.style.color="red"
},
enter(el,done){
    setTimeout(()=>{
        el.style.color = 'green'
    },1000);
    setTimeout(() => {
        done();
    }, 2000);
},
afterEnter(el){
    el.style.color = 'blue';
}
<script src="node_modules/velocity-animate/velocity.js"></script>
beforeEnter(el){
    el.style.opacity = 0;
},
enter(el,done){
    Velocity(el, {opacity: 1}, {duration: 2000, complete: done})
},
afterEnter(el){
    el.style.color = 'blue';
},
leave(el,done){
    Velocity(el, {opacity: 0}, {duration: 2000, complete: done})
}
<div id="app">
    <input type="text" v-model="filterData">
    <transition-group  
        enter-active-class="zoomInLeft" 
        leave-active-class="zoomOutRight"
    >
        <div v-for="(l,index) in computedData" :key="l.title" class="animated">
            {{l.title}}
        </div>
    </transition-group>  
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script>
    let vm = new Vue({
        el:'#app',
        data:{
            filterData:'',
            dataList:[
                {title:'标题1'},
                {title:'标题2'},
                {title:'标题4'},
                {title:'标题3'}
            ]
        },
        computed:{
            computedData(){
                return this.dataList.filter((item)=>{
                    return item.title.includes(this.filterData);
                })
            }
        },
    })
</script>
slot v-for i in 5