三阶段-vue

vue

特点

  • 渐进式 核心 库 只有 核心功能,不会包含所有的api,框架将很多api,变成了插件(中间), 拔插式方式 使用插件方法,使用某个插件就具备另外功能,不使用就没有
  • 声明式 开发方式 和声明式 对比 命令式 命令式: 将 要干什么 一步一步告诉 机器(详细每一个步骤) 声明式:告诉机器我要干什么,只要结果(怎么干不管)mvvm m数据
  • m --> v v视图 m 改变 视图自动改变 通过vm来实现 数据改变视图自动刷新

 

vue 雏形 (vm是如何创建)

<div id="app">
    {{ msg }}
  </div>
  <script src="./vue.js"></script>
  <script>
    const vm = new Vue({
      el:"#app",
      data: { // 存放数据
        msg:'你好vue'
      }
    })
  </script>

 vue中的模板 {{}}

主要用于渲染视图(渲染m) {{}}

  • 渲染数据 (写表达式 变量只认识 实例上变量)

  • 调用函数 (只会调用实例上的 方法)

const vm = new Vue({
  el: '#app',
  data: { // 定义实例的数据
    msg: '你好世界'
  },
  methods: { // 定义实例的方法
    act(){
      console.log(111)
    }
  }
})
// new Vue 传入对象 data中属性和 methods中的方法会直接挂在到实例上

注意: {{ }} 模板 使用全局方法 有些可以有些不可以 vue 对于全局方法设置白名单

 'Infinity,undefined,NaN,isFinite,isNaN,' +
    'parseFloat,parseInt,decodeURI,decodeURIComponent,encodeURI,encodeURIComponent,' +
    'Math,Number,Date,Array,Object,Boolean,String,RegExp,Map,Set,JSON,Intl,' +
    'require'

指令

vue指令 扩展了 标签(组件) 属性 如何使用指令 <组件 v-指令名=值/> 指令的值 是一个 js环境 里面的变量 需要是实例上的属性(否则找不到报错)

  • v-model 将表单控件的值和 一个 实例数据进行双向绑定

  • v-text 不需要刻意记

  • v-html 渲染富文本数据

  • v-bind:属性 将标签 普通属性(包含任何自定义属性) 变成动态属性(属性值 与 实例的数据进行绑定) 简写 :属性

  • v-on:事件 将 标签 的事件 与 实例方法进行绑定(事件触发时 ,实例方法会调用) 简写 @事件

  • v-once 让元素内部的 数据只渲染一次

 

深入事件

// 作为事件函数  事件函数的第一个参数就是事件对象
<button @click="fn"></button>


new Vue({
  methods: {
    fn(e){
      e.stopPropagation(); // 阻止冒泡
    }
  }
})
// 直接调用  需要传参时候 fn不再事件函数,只是在事件发生时,调用了 这个方法
// vue 提供了一个 类似于全局变量 $event
<button @click="fn(11, $event)"></button>


new Vue({
  methods: {
    fn(n,e){
     console.log(n); 
     e.preventDefault();
    }
  }
})

 

如何在vue事件中阻止事件冒泡 获取事件源 阻止默认事件

e.stopPropagation() e.preventDefault() e.target vue 事件绑定的方法 中的this指向是指向 实例(vm),而不是事件源,获取事件源,一定通过 e.target获取

<div id="app">
    <div id="div1" @click="fn1(1)">
      <div id="div2" @click="fn2(2)">
        <div id="div3" @click="fn3(3, $event)"></div>
      </div>
    </div>
  </div>
  <script src="./vue.js"></script>
  <script>
    var a = 10;
    const vm = new Vue({
      el: '#app',
      data: {
        msg: '你好世界',
        
      },
      methods: {
        fn1(n){
          alert(n)
        },
        fn2(n){
          alert(n)
        },
        fn3(n, e){
          console.log(this);
          e.stopPropagation();
          console.log(e.target);
          alert(3)
        }
      }
    })

   
  </script>

事件修饰符

@事件 v-on:事件 vue提供修饰符 修饰事件绑定 @事件.修饰符 @click.stop="方法"

.stop  // 阻止事件冒泡
.prevent // 阻止默认事件
.capture // 捕获阶段就提前触发
.once  // 只绑定一次
.self // 事件只能由自身触发 后代元素无法触发(不能事件委托了)
//vue的显示隐藏案列
<style> #box1 { position: fixed; top: 0; left: 0; bottom: 0; right: 0; margin: auto; z-index: 1000; background-color: rgba(0, 0, 0, .6); display: flex; align-items: center; justify-content: center; } #connect { width: 40%; height: 40%; background-color: #fff; } </style> </head> <body> <div id="box"> <button @click="tab">{{ isshow?'显示':'消失' }}</button> <div id="box1" v-show='isshow' @click.self="change"> <div id="connect"> //在这阻止此div的冒泡也可达到此效果,@click.stop即可 苗有方之所以放下弓箭,并察觉出这些人有问题,靠的不是智慧,而是武者的危机预感没有反馈。   这说明那群飞兽军没有敌意。   “不对?”   许二郎抬了抬手,挡开要强行护送他离开的百夫长,侧头看向苗有方。 </div> </div> </div> </body> <script src="./vue.js"></script> <script> var vm = new Vue({ el: '#box', data: { isshow: false, }, methods: { tab() { this.isshow = !this.isshow }, change () { this.isshow = false } } }) </script>

 

条件渲染(面试题)

v-if 将一个元素显示消失 与 一个变量进行绑定(true显示 false消失)

对比 v-show 相同点:控制元素的显示消失的状态 不同点: v-show 控制元素的 display:none 属性 控制显示消失 v-if 如果值为false时,则 直接移除这个元素(不渲染) 使用场景: v-show 适用于频繁切换 状态时 v-if 初始 不 渲染(初始值为false 且 不频繁切换)

v-if 搭配使用 v-else-if v-else 搭配使用时 一定是连续的兄弟 且 v-if是第一个兄弟 v-else 是最后一个兄弟

<style>
    #box{
      width: 200px;
      height: 200px;
      background-color: rgb(211, 24, 24);
      margin: 100px auto;
    }
    #box2{
      width: 200px;
      height: 200px;
      background-color: rgba(30, 167, 60, 0.459);
      margin: 100px auto;
    }
    #box3{
      width: 200px;
      height: 200px;
      background-color: rgba(153, 114, 78, 0.459);
      margin: 100px auto;
    }
  </style>
</head>
<body>
  <div id="app">
    <button @click="change">显示</button>
    <div id="box"  v-if="num%3 === 0">111</div>
    <div id="box2" v-else-if="num%3===1">222</div>
    <div id="box3" v-else>333</div>
  </div>
  <script src="./vue.js"></script>
  <script>

    const vm = new Vue({
      el: '#app',
      data: {
        num: 0
      },
      methods: {
        change(){
          this.num++;
        }
      }
    })
    // btn.addEventListener('事件',回调函数,[false/true])
  </script>

循环渲染

// 循环数组
<ul>
  <li v-for="item in arr">
    {{ item }}
  </li>
</ul>
// 循环并定义下标
<ul>
  <li v-for="(item,index) in arr">
    {{ item }}
    {{ index }}
  </li>
</ul>
/* 
注意:
循环时声明的循环变量和循环下标,作用域 是循环这个元素内部
如果出现 循环嵌套循环,注意 内层循环变量和下标不要出现命名冲突
否则:在内层循环拿不到外层循环变量和外层循环下标
*/
new Vue({
  el: '#app',
  data:{
    arr: [1,2,3,4]
  }
})

 复杂渲染

<div id="app">
    <div class="floor" v-for="(floor,index) in floors">
      <h2>
        {{ floor.title }}
      </h2>
      <ul>
        <li v-for="(item,index2) in floor.items">
          <h5>{{ item.name }}</h5>
          <p>{{item.price}}</p>
        </li>
      </ul>
    </div>
  </div>
  <script src="./vue.js"></script>
  <script>

    const vm = new Vue({
      el: '#app',
      data: {
       floors:[
         {
           title:"手机",
           items: [
             {
               name:"商品1",
               price: 10
             },
             {
               name:"商品2",
               price: 30
             },{
               name:"商品3",
               price: 40
             },{
               name:"商品4",
               price: 10
             }
           ]
         },
         {
           title:"电视",
           items: [
             {
               name:"商品1",
               price: 10
             },
             {
               name:"商品2",
               price: 30
             },{
               name:"商品3",
               price: 40
             },{
               name:"商品4",
               price: 10
             }
           ]
         },
         {
           title:"笔记本",
           items: [
             {
               name:"商品1",
               price: 10
             },
             {
               name:"商品2",
               price: 30
             },{
               name:"商品3",
               price: 40
             },{
               name:"商品4",
               price: 10
             }
           ]
         }
       ]
      },
      methods: {
        removeLi(i){
          this.arr.splice(i, 1);
        }
      }
    })
    // btn.addEventListener('事件',回调函数,[false/true])
  </script>

 

*深入 vue mvvm原理

Vue利用Object.defineProperty()

☆☆☆过程: 当我们 new Vue 创建一个vm(实例时),需要传入一个对象,对象有一个data属性也是对象,vue会自动遍历 data对象。拿到他里面所有属性,使用Object.defineProperty;得到每一个data中属性的getter和setter 每一个 实例创建后 还会创建一个 watcher(观察者), 在setter触发时(data中属性值改变了),通知观察者, 观察者回调函数中,会立即调用(render渲染模板 一般是隐藏),实例的render方法,重新生成 虚拟dom ...

虚拟dom在vue中的 作用: 第一次 vue在渲染真实dom后,都会将基于真实dom,生成虚拟dom,保存在内存中,当改变了数据后,setter通知观察者,重新调用render函数 生成 数据改变 新的虚拟的dom树,和 上一次 保存在内存中的虚拟dom树进行比较(diff算法) ,得到最少代价去操作dom diff在比较中做了什么: dom树中 不同的层级 按照相同层级进行比较 key属性在diff算法 比较时做了什么: 要求key是独一无二 要求:循环中 必须要加 key属性 为了加快diff 比较效率

虚拟dom 概念:用js 对象的 结构来描述一段dom树

  <div id="box" class="wrap">
      <p class="op">你是p</p>
      <span>我是span</span>
      文本内容
    </div>
    {
      tagName:"div",
      attrs:{
        id: 'box',
        className:"wrap"
      },
      children: [
        {
          tagName:"p",
          attrs: {
            className:'op'
          },
          chidren: ['你是p']
        },
        {
          tagName:"span",
          attr:null,
          children: ['我的span']
        },
        '文本内容'
      ]
    }

vue中的 class

:class

<div :class="a"></div> // class="box"
<div :class="b">  // class="box box2"
<div :class="[a,'box2']"> // class="box box2"
<div :class="{active:表达式}"> // div有没有active类,取决于active属性值是不是真


{
  data:{
    a: 'box',
    b: [a,'box2']
  }
}

vue中的style

将 元素的style 映射成 对象的结构 好处:样式值 就可以写表达式(值、变量、计算、三目、短路)

 <div id="app">
    <div :style="sty"></div>
    <div :style="{width:100+300+500+'px',height:2>3?'100px':'500px',background:'skyblue'}"></div>
  </div>
  <script src="./vue.js"></script>
  <script>
    const vm = new Vue({
      el:"#app",
      data: {
        sty:{
          width: '200px',
          height: '300px',
          background: 'blue'
        }
      }
    })
  </script>

vue做tab切换案列

<div id="app">
    <div :style="sty"></div>
    <div :style="{width:100+300+500+'px',height:2>3?'100px':'500px',background:'skyblue'}"></div>
  </div>
  <script src="./vue.js"></script>
  <script>
    const vm = new Vue({
      el:"#app",
      data: {
        sty:{
          width: '200px',
          height: '300px',
          background: 'blue'
        }
      }
    })
  </script>

vue记事本案列

<link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.css" rel="stylesheet">
</head>
<body>
  <div id="app">
    <div class="wrap" style="max-width:600px;margin:20px auto">
      <h2>今日事、今日毕</h2>
      <div class="row">
        <div class="col-md-10 col-sm-10">
          <input type="text" v-model="inputTxt" class="form-control">
        </div>
        <div class="col-md-2 col-sm-2">
          <button @click="addTodo" class="btn btn-primary">增加</button>
        </div>
      </div>
      <div class="content" style="margin-top: 10px">
        <ul class="list-group">
          <li v-if="!todos.length" class="text-center list-group-item">暂无代办事项</li>
          <li 
            v-for="(todo,index) in todos"
            :key="index"
            class="list-group-item">
            {{ todo.content }}
            <button 
              @click="delTodo(index)"
              class="btn btn-danger btn-xs">删除</button>
            <button 
              :disabled="todo.isCompleted" class="btn btn-success btn-xs">{{ todo.isCompleted?'已完成':'点击完成' }}</button>
          </li>
        </ul>
      </div>
    </div>
  </div>
  <script src="./vue.js"></script>
  <script>
    const vm = new Vue({
      el: '#app',
      data: {
        inputTxt:'',
        todos:[
         {
           content:'明天爬山',
           isCompleted: false
         },
         {
           content:'明天爬1111',
           isCompleted: true
         }
        ]
      },
      methods: {
        delTodo(index){
          // 删除代办事项
          if(confirm('您确认删除吗?')){
            this.todos.splice(index,1)
          }
        },
        addTodo(){
          // 添加代办事项
          if(this.inputTxt.trim() === ""){
            return 
          }
          this.todos.push({
            content: this.inputTxt,
            isCompleted: false
          })
          this.inputTxt = "";
        }
      }
    })
  </script>

 

vue开发方式 是 数据驱动

mvvm 问题也就是存在于vue2的bug

数组中的某些操作 Object.defineProperty setter 捕获不到

  • 数组[下标]=值 

    this.$set(对象,属性,值)
    // 手动强制 调用render 刷新视图
    // eg
    this.$set(this.arr, 1, 100);

     

  • 数组.length = 值 解决方法:手动改变数组length 不用直接改变length 使用splice取代

 

动态创建的数据 改变数据时视图不刷新

当没有定义在data中定义变量时,new Vue 自动遍历 data对象,中的所有属性,转换成getter setter 而,初始没有在data中动态创建就没有setter getter 所有 watcher无法捕获到 数据改变视图就不会刷新。

解决方法:

所有需要用到数据 一定要在data中定义初始值 初始值:考虑到不同数据类型 对象: null {} 数组 [] number 0 字符串 '' boolean true/false

 

<div id="app">
    <button @click="add">增加一个数据</button>
    
    <div v-if="isShow">
      <button @click="changeMsg">改变msg</button>
      {{ msg }}
    </div>
  </div>
  <script src="./vue.js"></script>
  <script>
    const vm = new Vue({
      el: '#app',
      data: {
       isShow: false
      },
      methods: {
        add(){
      
          this.msg = '你好世界';
         
          this.isShow = true;
        },
        changeMsg(){
          this.msg="hello world";
        }
      }
    })
  </script>

 vue购物车案列

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.css" rel="stylesheet">
</head>
<body>
  <div id="app">
    <table style="max-width:600px;margin: 50px auto;" class="table table-bordered table-hover">
      <thead>
        <tr>
          <th>商品名称</th>
          <th>商品价格</th>
          <th>商品数量</th>
          <th>小计</th>
          <th>操作</th>
        </tr>
      </thead>
     <tbody>
       <tr>
        <td colspan="5" class="text-center">
          暂无商品
        </td>
       </tr>
      <tr v-for="(item,index) in items" :key="item.id">
        <td>
          <input
            @change="singleCheck"
            type="checkbox" :value="item.id" v-model="checkArr">
          {{ item.name }}
        </td>
        <td>{{ item.price }}</td>
        <td>
          <button @click="addNum(index)" class="btn btn-xs">+</button>
          <span>{{item.num}}</span>
          <button @click="reduceNum(index)" class="btn btn-xs">-</button>
        </td>
        <td>{{ (item.num*item.price).toFixed(2) }}</td>
        <td>
          <button @click="delItem(index)" class="btn btn-danger btn-xs">删除</button>
        </td>
      </tr>
     </tbody>
      <tfoot>
        <tr>
          <td colspan="5">
            全选:
            <input
              @click="allChecked"
              :checked="items.length === checkArr.length" type="checkbox">
            总价格:{{ total }}
          </td>
        </tr>
      </tfoot>
    </table>
  </div>
  <script src="./vue.js"></script>
  <script>
    const vm = new Vue({
      el:'#app',
      data: {
        items: [ // 所有商品
          {
            id:1,
            name:"商品1",
            price: 15.5,
            num: 2
          },
          {
            id:2,
            name:"商品2",
            price: 18.5,
            num: 1
          },
          {
            id:3,
            name:"商品3",
            price: 20.5,
            num: 1
          },
          {
            id:4,
            name:"商品4",
            price: 19.9,
            num: 1
          }
        ],
        total:0, //总价格
        checkArr: [1,3,4] // 所有选中商品 id
      },
      methods: {
        singleCheck(){
          // 点击改变商品选中状态
          this.calcTotal();
        },
        delItem(index){
          if(confirm('您确认删除吗?')){
            this.items.splice(index, 1);
            this.calcTotal();
          }
        },
        addNum(index){
          // 增加购买数量
          this.items[index].num++;
          this.calcTotal();
        },
        reduceNum(index){
          this.items[index].num--;
          if(this.items[index].num <= 1){
            this.items[index].num = 1;
          }
          this.calcTotal();
        },
        calcTotal(){
          // 计算总价格
          /* 
          所有选中商品的 价格*数量的总和
          */
          let checkedItems = [];
          this.items.forEach(item=>{
            //console.log(item.id)
           
            if(this.checkArr.includes(item.id)){
              checkedItems.push(item);
            }
          })
          this.total = 0;
          checkedItems.forEach(item=>{
            this.total += item.num*item.price
          })
          this.total = parseFloat(this.total.toFixed(2));
        },
        allChecked(e){
          console.log(e.target.checked)
          let allItemsIdS =[]; //所有商品id数组

          this.items.forEach(item=>{
            allItemsIdS.push(item.id);
          })
          if(e.target.checked){
            this.checkArr = allItemsIdS;
          }else{
            this.checkArr = [];
          }
          this.calcTotal();
        }
      },
      mounted() {
       this.calcTotal();
      }
    })
  </script>
</body>
</html>

 

 

 

 

posted @ 2021-03-15 17:40  技术活当赏  阅读(279)  评论(0)    收藏  举报