vue基础

千锋逆战班,vue的基础,或许有一些些小错误或者语病,例子估计不怎么精炼,但还可以直接复制粘贴执行代码查看效果。

Vue知识点总结

一、起步

1、 首先在HTML文件中引入vue.js。

2、新建一个div标签,并且为其设一个id值。<div id="app"> {{msg}}</div>

3、js代码里面创建实例。

  // vue实例就是ViewModel:负责联系view和model之间,实现响应式的双向数据绑定
    const app = new Vue({
      el: '#app',
      // data就是model:定义数据的
      data: {
        msg: 'hello 1912'
      }
    })

下面是完整的引用代码。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <script src="./vue.js"></script>
</head>
<body>
  <!-- view: 视图,页面显示,当model层的数据发生修改,ViewModel层会响应式的更新DOM -->
  <div id="app">
    {{msg}}
  </div>
  <script>
    console.dir(Vue)
    // vue实例就是ViewModel:负责联系view和model之间,实现响应式的双向数据绑定
    const app = new Vue({
      el: '#app',
      // data就是model:定义数据的
      data: {
        msg: 'hello 1912'
      }
    })
  </script>
</body>
</html>

二、原理(实现方法)

1、Object.defineProperty

  • 静态方法Object.defineProperty()直接在对象上定义新属性,或修改对象上的现有属性,然后返回对象。
var obj  ={}
Object.defineProperty(obj, "key", {
    enumerable: false, // 不可枚举:通过for-in不能遍历到
    configurable: false, // 不可配置:不能通过delete obj.key 进行删除
    writable: false, // 不可写:值不能被修改
    value: "static"
})
  • 用法

    1. 在页面HTML文件中写入<div id="app"></div>

    2. 申明一个新的对象objconst obj = {}

    3. 设置Object.defineProperty。

      let v=10;//定义初始值
      Object.defineProperty(obj, 'x', {
      // get是再每次访问obj的x属性的时候就会执行,并且这个属性得到的值来自于get的返回值
          get() {
              return v
          },
          // 每次要修改obj的x属性,就会调用set
          set(newValue) {
              console.log('newValue:' + newValue)
              v = newValue
              // 每次set的时候都调用渲染DOM的方法,这样就实现了响应式
              setAppContent()
          }
      })
      // 这个方法专门负责渲染DOM
      const setAppContent = () => {
          document.querySelector('#app').innerHTML = obj.x
      }
      setAppContent();//首先div#app的内容渲染成obj.x的值,但是只要调用了obj.x=""这个语句,obj.x的值就会被改变,然后再次调用渲染页面的方法,这样就可以实现页面有数据渲染。
      

2、Proxy代理实现

  • 用法
    const obj=new Proxy({},{
        get(obj,key){
            retuen obj[key]
        },
        set(obj,key,value){
            obj[key]=value
            setAppContent();//重新渲染页面
        }
    })
    const setAppContent = () => {
        document.querySelector('#app').innerHTML = obj.x
    }
    

3、v-model绑定input原理

  • 注意:原生js的input元素也有input事件的,相当于onkeydown
  <div id="app">
    <input type="text" :value="username" @input="handleUsernameInput">
    {{username}}
  </div>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        username: ''
      },
      methods: {
        handleUsernameInput (e) {
          this.username = e.target.value
        }
      }
    })
  </script>

4、v-model绑定checkbox原理

  • (checked属性不显示在浏览器标签内)
  <div id="app">
    <p>
      <label><input @change="handleLikesChange" :checked="likes.includes('')" type="checkbox" value=""></label>
      <label><input @change="handleLikesChange" :checked="likes.includes('')" type="checkbox" value=""></label>
      <label><input @change="handleLikesChange" :checked="likes.includes('rap')" type="checkbox" value="rap">rap</label>
      <label><input @change="handleLikesChange" :checked="likes.includes('篮球')" type="checkbox" value="篮球">篮球</label>
    </p>
    {{likes}}
    
  </div>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        likes: ['唱']
      },
      methods: {
        handleLikesChange (e) {
          const value = e.target.value
          console.log(value)
          // 判断likes里是否包含value,如果包含,从likes里把这条数据删除
          if (this.likes.includes(value)) {
            this.likes = this.likes.filter(like => like !== value)
          } else {
            // likes不包含当前value,说明目前没有选中,把value push到likes里
            this.likes.push(value)
          }
        }
      }
    })
  </script>

5、v-model绑定radio原理

  • (checked属性不显示在浏览器标签内)
<div id="app">
        <p>
            你要找
            <input type="radio" :checked="friend.includes('男朋友')" @change="radioChange" value="男朋友">男朋友
            <input type="radio" :checked="friend.includes('女朋友')" @change="radioChange" value="女朋友">女朋友
            <div style="color: red;">
                {{friend}}
            </div>
        </p>
    </div>
    <script>
        const app = new Vue({
            el: '#app',
            data: {
                friend: '女朋友'
            },
            methods: {
                radioChange(e) {
                    this.friend = e.target.value
                }
            }
        })
    </script>

6、v-model绑定select下拉框原理

  • (seleceted属性不显示在浏览器标签内)
    <div id="app">
        <select @change="selectedOne">
            <option :selected="like.includes('')" value=""></option>
            <option :selected="like.includes('')" value=""></option>
            <option :selected="like.includes('rap')" value="rap">rap</option>
            <option :selected="like.includes('篮球')" value="篮球">篮球</option>
        </select>
        <p>{{like}}</p>
    </div>
    <script>
        const app = new Vue({
            el: '#app',
            data: {
                like: '篮球'
            },
            methods: {
                selectedOne(e) {
                    this.like = e.target.value
                }
            }
        })
    </script>

三、语法

  • 注意:Vue里面的数值基本都做了处理,可以通过this.msg获取data里面的msg,也可以通过this.$data.msg获取,但是this.data.msg是错误的用法。
  • 双大括号的文本插值,只是加载整个文本,不会渲染成标签 :<span>Message: {{ msg }}</span>
  • v-html 指令才能渲染成标签,v-text渲染文本,和{{}}差不多
  • 当指令和插值表达式发生冲突时,插值表达式生效

四、指令

1、v-html和v-text

  • v-html 指令渲染成标签,v-text渲染成文本
<div id="app">
	<div v-html="str">{{num}}</div>
    <!--title而不是1-->
    <div v-text="str">{{num}}</div>
    <!--<h1>title</h1>而不是1-->
</div>
<script>
	const app = new Vue({
		el: '#app',
		data: {
			num: 1,
			str: '<h1>title</h1>'
		}
	})
</script>

2、v-if和v-show

  • v-if是不渲染这个标签,v-show通过设置display:none来控制元素的显示与否
    • 注意:v-if和v-else必须用在相邻的兄弟元素,否则会报错
  <div id="app">
    <p v-if="isShow > 0">这是一段文字</p>
    <p v-else-if="isShow < 0">这是第二段文字</p>
    <p v-else>这是第三段文字</p>
    <p v-show="isShow > 0">v-show案例</p>
  </div>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        isShow: 0
      }
    })
  </script>

3、v-for

  • 用法:
    • <div v-for="val in $"></div>
    • 或者<div v-for="val of $ "></div>
    • 或者<div v-for="(val,key) in obj"></div>
    • 或者<div v-for="(val,index) in arr"></div>
    • 或者<div v-for="(val,index) in str"></div>
  • $可以是数组,对象,字符串和数字(Number)

4、v-on:event绑定事件(@event)

v-on绑定事件

  • 用法
    • v-on:click="handleBtnClick" 可以简写成@click="handleBtnClick"
    • v-on:click="isShow=!isShow"可以直接写js代码。
    • @click = " fun() "@click = " fun "效果一样,在methods里面的第一个参数就是事件源/事件对象。
    • @click = " fun( $event, n1, n2... )"如果需要传其他参数并且获取事件源应当这样写。
  <div id="app">
    <button v-on:click="isShow = !isShow">显示/隐藏弹框</button>
    <button v-on:click="handleBtnClick">显示/隐藏弹框</button>
    <button v-on:click="handleBtnClick2($event, 123)">显示/隐藏弹框</button>
    <button @click="handleBtnClick2($event, 123)">显示/隐藏弹框</button>
    <p v-show="isShow">这是一个弹窗</p>
  </div>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        isShow: false,
        data: 'sddsds'
      },
      methods: {
        handleBtnClick (e) {
          console.log(e)
          this.isShow = !this.isShow
        },
        handleBtnClick2 (e, num) {
          console.log(e)
          console.log(num)
        }
      }
    })
    console.log(app)
  </script>

5、v-cloak

  • v-cloak属性是vue实例化之前存在,之后就自动消失 ,一般用来处理页面的加载问题。

  • 可以利用它让元素一开始的时候隐藏,有vue实例之后再显示 ,隐藏未编译的{{msg}}标签直到实例准备完毕。

  • 使用属性选择器设置css样式:[v-cloak]属性选择器。

    [v-cloak] {
        display: none;
    }  
    <div id="app" v-cloak>
        {{str}}
      </div>
      <script>
        setTimeout(() => {
          const app = new Vue({
            el: '#app',
            data: {
              str: 'hello'
            }        
          })
        }, 1000)    
      </script>
    

6、v-bind:attribute绑定属性(:attribute)

  • v-bind可以用来绑定任意属性,包括自定义属性

      <div id="app">
        <img v-bind:src="user.avatar" v-bind:alt="user.name" :title="user.name">
      </div>
      <script>
        const app = new Vue({
          el: '#app',
          data: {
            user: {
              avatar: 'https://cn.vuejs.org/images/dcloud.gif',
              name: '张三'
            }
          }
        })    
      </script>
    

7、class和style绑定

1)、对象语法:
  • class名可叠加

  • 给v-bind:class传一个对象,表示这个div的class值取决于isActive的值是否为true;

    <div class="class1"  v-bind:class="{active:isActive ,'class2':isActive}"></div>
    

    若isActive=true,则结果为<div class="class1 active class2"></div>

  • 绑定的数据对象不必内联定义在模板里:

    <div :class="classObject"></div>
    
    data:{
        classObject:{
            'active':true,
            class2:false
        }
    }
    
2)、三目运算符
<div class="class1"  v-bind:class="isActive ? 'active' : ''"></div>
3)、数组语法
<div v-bind:class="[activeClass, errorClass]"></div>
data: {
  activeClass: 'active',
  errorClass: 'text-danger'
}
<div id="app">
  <div :style="{ width: divWidth }">box1</div>
  <!-- 样式绑定一个对象,也可以和原生样式一起使用,最终渲染结果会合并 -->
  <div :style="styleObj" style="color: #fff">box2</div>

  <!-- 可以直接把data作为class使用 -->
  <div :class="className">box</div>
  <!-- 三目运算表达式绑定class -->
  <div :class="active ? 'ac' : '' ">box3</div>
  <!-- 绑定对象 -->
  <div :class="{ ac: active }">box4</div>
  <!-- 绑定数组 -->
  <div :class="[className, className2, 'ac']">box5</div>
  <!-- 绑定数组里再嵌套对象 -->
  <div :class="[className, className2, { ac: active }]">box6</div>
  <!-- 绑定的class和原生class可以共存,会合并到一起 -->
  <div :class="[className, className2]" class='ac'>box7</div>
</div>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      divWidth: '200px',
      styleObj: {
        height: '300px',
        width: '300px',
        backgroundColor: 'red'
      },
      active: false,
      className: 'current',
      className2: 'current2'
    }
  })
  
</script>

8、v-model双向绑定数据

 <div id="app">
    <!-- input的v-model的原理 -->
    <input type="text" v-mode="username">
    {{username}}
  </div>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        username: '555'
      }
    })
  </script>

9、修饰符(modifier)

1)、表单输入修饰符
  • .lazy :在input的值发生改变时更新data,而非input或keydown时更新

    <!-- 在“change”时而非“input”时更新 -->
    <input v-model.lazy="msg" >
    
  • number : 自动将用户的输入值转为数值类型 。因为即使在 type="number" 时,HTML 输入元素的值也总会返回字符串。

    <input v-model.number="age" type="number">
    
  • trim : 自动过滤用户输入的首尾空白字符

    <input v-model.trim="msg">
    
2)、按键修饰符
  • 自定义按键修饰符别名

    // 可以使用 `v-on:keyup.f1`
    Vue.config.keyCodes.f1 = 112
    
  • 按键码.13

    <input v-on:keyup.13="submit">
    <!--和按下enter键一样-->
    <input v-on:keyup.enter="submit">
    
  • .enter

  • .tab

  • .delete (捕获“删除”和“退格”键)

  • .esc

  • .space

  • .up

  • .down

  • .left

  • .right

3)、事件修饰符(event-modifier)

[例] event.preventDefault() 阻止默认事件(.prevent> ),或 event.stopPropagation() 阻止冒泡(.stop)是非常常见的需求,vue提供了相应的修饰符

  • .stop
  • .prevent
  • .capture
  • .self
  • .once
  • .passive
  • 2.1.4新增.once
  • 2.3.0新增.passive,不要把 .passive.prevent 一起使用,因为 .prevent 将会被忽略,同时浏览器可能会向你展示一个警告。请记住,.passive 会告诉浏览器你想阻止事件的默认行为。
<!-- 阻止单击事件继续传播 -->
<a v-on:click.stop="doThis"></a>

<!-- 提交事件不再重载页面 -->
<form v-on:submit.prevent="onSubmit"></form>

<!-- 修饰符可以串联 -->
<a v-on:click.stop.prevent="doThat"></a>

<!-- 只有修饰符 -->
<form v-on:submit.prevent></form>

<!-- 添加事件监听器时使用事件捕获模式 -->
<!-- 即内部元素触发的事件先在此处理,然后才交由内部元素进行处理 -->
<div v-on:click.capture="doThis">...</div>

<!-- 只当在 event.target 是当前元素自身时触发处理函数 -->
<!-- 即事件不是从内部元素触发的 -->
<div v-on:click.self="doThat">...</div>

<!-- 点击事件将只会触发一次 -->
<a v-on:click.once="doThis"></a>

<!-- 滚动事件的默认行为 (即滚动行为) 将会立即触发 -->
<!-- 而不会等待 `onScroll` 完成  -->
<!-- 这其中包含 `event.preventDefault()` 的情况 -->
<div v-on:scroll.passive="onScroll">...</div>

五、computed计算属性

  • computed是一个对象,里面写方法,这个方法名可以作为data去使用,值就是这个方法的返回值 ,只有当值改变的时候才调用,如果将逻辑写在页面上,则每一次都需要重新渲染。
  • 计算属性是有依赖缓存的
  <div id="app">
    {{str}} <br>
    {{ str.split('').reverse().join('') }}<br>
    {{ str1 }}
  </div>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        str: 'abc'
      },
      computed: {
        str1 () {
          return this.str.split('').reverse().join('')
        }
      }
    })
  </script>

computed原理(set,get)

  • 如果写成普通函数的方式,就相当于get(){}方法,不能设置了,也就不能改动。
  <div id="app">
    姓:<input type="text" v-model="xing">
    名:<input type="text" v-model="ming">
    姓名:<input type="text" v-model="username">
  </div>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        xing: '',
        ming: ''
      },
      computed: {
        username: {
          set (value) {
            // 修改username的时候就会走set,参数就是修改之后的值
            console.log(value)
            this.xing = value.slice(0, 1)
            this.ming = value.slice(1)
          },
          get () {
            return this.xing + this.ming
          }
        }
      }
    })
  </script>

六、methods

  • 用法

    var vm = new Vue({
      data: { a: 1 },
      methods: {
        plus: function () {
          this.a++
        }
      }
    })
    vm.plus()
    

    也可以在标签内调用

    <div @click="plus"></div>
    

    具体用法及注意事项->4、v-on:event绑定事件(@event):v-on绑定事件

七、watch侦听器

  • watch是监听器,只要数据被修改了就会重新渲染。computed有依赖缓存,computed一般是依赖某个属性得到一个新的值。watch一般用来监听某个值的变化然后进行一些操作,可能并不需要得到新的值
  <div id="app">
    姓:<input type="text" v-model="xing">
    名:<input type="text" v-model="ming">
    姓名:<input type="text" v-model="username">
  </div>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        xing: '',
        ming: '',
        username: ''
      },
      watch: {
        xing (newV, oldV) {
          // 当data里的xing字段发生变化的时候就会走这个方法
          // newV是修改之后的最新的值, oldV是修改之前的旧值
          this.username = newV + this.ming
        },
        ming (newV, oldV) {
          this.username = this.xing + newV
        },
        username (newV, oldV) {
          this.xing = newV.slice(0, 1)
          this.ming = newV.slice(1)
        }
      }
    })
  </script>

八、filters 过滤器

1、全局过滤器:Vue.filter( ‘’ ,()=>{})

  • 全局过滤器,每个vue实例里面都能用

    Vue.filter('addDanwei', (value) => {
    	return '¥' + value
    })
    

2、局部过滤器:filters: {fn(){}}

  • 在其他Vue实例里面就不能用。
filters: {
    toFixed2 (num) {
        return num.toFixed(2)
    }
}

3、用法:在{{}}加中线,可叠加使用

<p>{{shop.price | toFixed2 | addDanwei}}</p>

  <div id="app">
    <ul>
      <li v-for="shop in list">
        <span>{{shop.title}}</span>
        <b>{{shop.status | formatStatus}}</b>
        <em>{{shop.price | toFixed2 | addDanwei}}</em>
      </li>
    </ul>
  </div>
  <div id="app1">
    {{ num  | addDanwei }}
    <!-- {{ num | toFixed2 | addDanwei }} -->
  </div>
  <script>
    // 全局过滤器,在下面这两个vue实例里都能使用
    Vue.filter('addDanwei', (value) => {
      return '¥' + value
    })

    // app1里就用不了app里定义的局部过滤器
    const app1 = new Vue({
      el: '#app1',
      data: {
        num: 12
      }
    })

    const app = new Vue({
      el: '#app',
      data: {
        list: [
          { id: 1, status: '001', title: '拖把', price: 19.99999 },
          { id: 2, status: '004', title: '扫把', price: 9 },
          { id: 3, status: '003', title: '簸箕', price: 29.1 },
          { id: 4, status: '002', title: '吸尘器', price: 2999.159839 },
          { id: 5, status: '005', title: '鸡毛掸子', price: 23.19 }
        ]
      },
      // 局部过滤器,只有当前vue实例里能使用
      filters: {
        formatStatus (status) {
          // status就是使用过滤器位置的值
          const statusMap = {
            '001': '未付款',
            '002': '待发货',
            '003': '已发货',
            '004': '已签收',
            '005': '已评价'
          }
          return statusMap[status]
        },
        toFixed2 (num) {
          return num.toFixed(2)
        }
      }      
    })
  </script>

九、AJAX请求

1、 fetch

  • 原生的fetch,返回的是一个promise,返回的结果做了二次封装,所以第一个then里先解析

      <div id="app">
        <ol>
          <li v-for="todo in todos" :key="todo.id">
            {{todo.title}}
          </li>
        </ol>
      </div>
      <script>
        const app = new Vue({
          el: '#app',
          data: {
            todos: []
          },
          created () {
            // 一开始就会执行的代码
            // 原生的fetch,返回的是一个promise,返回的结果做了二次封装,所以第一个then里先解析
            fetch('https://jsonplaceholder.typicode.com/todos')
              .then(resp => resp.json())
              .then(todos => {
                this.todos = todos
              })
          }
        })
      </script>
    

2、 axios

  • 使用前必须先引入axios.min.js或axios包,可以配置许多参数(如baseURL和headers)。还可以进行请求拦截和响应拦截,还可以把request挂在Vue的原型上,这样的话用的时候可以直接this.$request而不用每次引入。
  <div id="app">
    <ol>
      <li v-for="todo in todos" :key="todo.id">
        {{todo.title}}
      </li>
    </ol>
  </div>
  <script>
    

    const request = axios.create({
      baseURL: 'https://jsonplaceholder.typicode.com',
    })

    // 请求拦截器:每次请求发出去之前都会进入这里
    request.interceptors.request.use((config) => {
      // 在这里可以对请求的一些参数做全局配置,比如token,每隔请求接口都要携带的数据在这里配置
      // config.url += '?token=dhfgkalk'

      // 在这里还可以给用户转个圈
      console.log('request starting')

      // 这里必须要return加工之后的config,请求才能发的出去
      return config
    })

    // 响应拦截器:每次数据返回的时候进入这里
    request.interceptors.response.use(response => {
      // 停止转圈
      console.log('recieved response')
      // 在这里我们还可以做一些全局错误处理
      if (response.status === 200) {
        return response.data
      }
      return response
    })

    // 可以把request挂在Vue的原型上,这样的话用的时候可以直接this.$request而不用每次引入
    Vue.prototype.$request = request

    const app = new Vue({
      el: '#app',
      data: {
        todos: []
      },
      created () {
        this.$request.get('/todos').then(todos => {
          console.log(todos)
          this.todos = todos
        })
      }
    })
  </script>

九、component组件

  • *注意:*在template里只能由一个子元素

1、 全局注册

    const hello = {
      template: '<p>hello components</p>'
    }

    // 把这个hello对象注册成为一个helloWorld全局组件
    Vue.component('helloWorld', hello)

2、 局部注册

    const hello2 = {
      template: '<p>hello components2222</p>'
    }

    const app = new Vue({
      el: '#app',
      data: {
        title: 'hello 1912'
      },
      components: {
        hello2
      }
    })

3、 普通写法

  • 将组建的内容写在对象里面,相当于字符串做值

      <div id="app">
        <hello-world></hello-world>
        <hello2></hello2>
      </div>
      <div id="app1">
        <hello-world></hello-world>
        <!-- hello2是app实例里局部注册的组件,所以这里用不了 -->
        <!-- <hello2></hello2> -->
      </div>
      <script>
        // 定义了对象
        const hello = {
          template: '<p>hello components</p>'
        }
    
        // 把这个hello对象注册成为一个helloWorld全局组件
        Vue.component('helloWorld', hello)
    
        const hello2 = {
          template: '<p>hello components2222</p>'
        }
    
        const app = new Vue({
          el: '#app',
          data: {
            title: 'hello 1912'
          },
          components: {
            hello2
          }
        })
    
        const app1 = new Vue({
          el: '#app1',
          data: {
          }
        })
      </script>
    

4、 高级写法

  • 可以在DOM中定义template,template选项指向这个template ,将组建的内容写在html里面,在js里面进行操作,方便观察组件的结构以及组件的嵌套。
<template id="hello">
    <div>
        <p>hellovvvvvvvvvvvv</p>
        <son></son>
    </div>
</template>  
<template id="son">
    <div>
        son
    </div>
</template>

<div id="app">
    {{title}}
    <hello-world></hello-world>
</div>
<script>
        // 嵌套组件
    const son = {
      template: '#son'
    }

    const helloWorld = {
      template: '#hello'
      components: {
        son
      },
    }
    const app = new Vue({
        el: '#app',
        data: {
            title: 'hello 1912'
        },
        components: {
            helloWorld
        }
    })
</script>

5、 组件内部属性

  • 组件都是对象,这个对象里有template选项,还可以定义data,methods,computed这些Vue实例里有的选项, 组件里的data是一个方法,返回一个对象,data属于当前组件本身,只有这个组件可以使用,子父组件里的data默认情况下不会互相影响的。其他和vue的入口#app差不多。
  <div id="app">
    {{title}}
    <hello-world></hello-world>
  </div>
  <script>
    // 子组件都是对象,这个对象里有template选项,还可以定义data,methods,computed这些Vue实例里有的选项
    const helloWorld = {
      template: '<p>hello {{title | a}}</p>',
      // 组件里的data是一个方法,返回一个对象
      // 这里定义的data属于当前组件本身,只有这个组件可以使用
      // 子父组件里的data默认情况下不会互相影响的
      data () {
        return {
          title: 'happy new year'
        }
      },
      filters: {
        a (value) {
          return value + ' 123'
        }
      }
    }
    const app = new Vue({
      el: '#app',
      data: {
        title: 'hello 1912'
      },
      components: {
        helloWorld
      }
    })
  </script>

6、 组件props(父->子)通信

  • 父组件通过属性给子组件传值,子组件通过props接收属性值,可以直接当做data属性用。

    <shop-item
            v-for="shopItem in list"
            :key="shopItem.id"
            :title="shopItem.title"
            :status="shopItem.status"
            :price="shopItem.price"
            :id="shopItem.id"
      ><shop-item>
    props: [ 'title', 'status', 'id', 'price' ],或者
    props: {
            title: String,
            status: {
              type: String,
              required: false,
              default: '001'
            },
            id: Number,
            price: Number
          },
    
  <template id="shop-item">
    <div>
      <!-- 3. 子组件接收到的props就可以直接作为data去使用 -->
      <p>
        <span>{{ id }}</span>
        <b>{{ title }}</b>
        <span>{{ status | formatStatus }}</span>
        <em>{{ price | toFixed2 }}</em>
      </p>
    </div>
  </template>
  <div id="app">
    <div class="container">
      <!-- 子组件也可以写成单标签 -->
      <!-- 1. 父组件里使用子组件的时候可以通过绑定属性来传参 -->
      <shop-item
        v-for="shopItem in list"
        :key="shopItem.id"
        :title="shopItem.title"
        :status="shopItem.status"
        :price="shopItem.price"
        :id="shopItem.id"
      />
    </div>
  </div>
  <script>
    const shopItem = {
      template: '#shop-item',
      // 2. 在子组件里就可以通过props来接收参数
      // props: [ 'title', 'status', 'id', 'price' ],
      // props还可以用一个对象的方式来接收
      props: {
        title: String,
        status: {
          type: String,
          required: false,
          default: '001'
        },
        id: Number,
        price: Number
      },
      filters: {
        formatStatus (status) {
          // status就是使用过滤器位置的值
          const statusMap = {
            '001': '未付款',
            '002': '待发货',
            '003': '已发货',
            '004': '已签收',
            '005': '已评价'
          }
          return statusMap[status]
        },
        toFixed2 (num) {
          return num.toFixed(2)
        }
      }
    }

    const app = new Vue({
      el: '#app',
      data: {
        list: [
          { id: 1, status: '001', title: '拖把', price: 19.99999 },
          { id: 2, status: '004', title: '扫把', price: 9 },
          { id: 3, status: '003', title: '簸箕', price: 29.1 },
          { id: 4, status: '002', title: '吸尘器', price: 2999.159839 },
          { id: 5, status: '005', title: '鸡毛掸子', price: 23.19 }
        ]
      },
      components: {
        shopItem
      }
    })
  </script>

7、组件emit(子->父)通信

  • 父组件通过触发emit时间操作子组件的值。

    <div id="box">
          父组件--{{myname}}
          <child @myevent="handleEvent"></child>
        </div>
        
        <script type="text/javascript">
            //子组件
          Vue.component("child",{
            template:`<div>
              我是child
              <button @click="handleClick()">click</button>
            </div>`,
            data(){
              return {
                childname:"我式child状态"
              }
            },
            methods:{
              handleClick(){
                //通知父组件
                this.$emit("myevent","来自子组件问候",this.childname)
              }
            }
          })
     
          new Vue({
            el:"#box",
            data:{
              myname:""
            },
            methods: {
              handleEvent(data1,data2){
                console.log("父组件调用",data1,data2)
                // data2
                this.myname = data2
              }
            }
          })    
        </script>
    

8、 中央时间总线event bus

  • bus其实就是一个空的Vue实例,作用帮助两个非父子关系组件之间通信
<div id="app">
    <dage></dage>
    <erdi></erdi>
  </div>
  <script>
    // 事件总线:event bus
    // bus其实就是一个空的Vue实例,作用帮助两个非父子关系组件之间通信
    const bus = new Vue()

    const dage = {
      template: '<p @click="playErdi">打我弟</p>',
      methods: {
        playErdi () {
          // 先把这个行为通知bus,bus再去通知其他关联的组件
          // 向bus触发一个自定义事件
          bus.$emit('play', { ku: true })
        }
      }
    }

    const erdi = {
      template: '<div><p v-if="ku">哭 wuwuwu</p></div>',
      data () {
        return {
          ku: false
        }
      },
      created () {
        // 通过bus去监听play事件
        bus.$on('play', ({ ku }) => {
          this.ku = ku
        })
      }
    }

    const app = new Vue({
      el: '#app',
      data: {
      },
      components: {
        dage,
        erdi
      }
    })
  </script>

9、refs通信

  • ref放在标签上, 拿到的是原生节点, ref放在组件上, 拿到的是组件对象,比较粗暴的直接获取原生对象节点。
 <div id="box">
       <input type="text" ref="myinput"/>
       <input type="password" ref="mypassword"/>
       <button @click="handleClick()">click</button> 
       <child ref="mychild"></child>
    </div>
    <script type="text/javascript">
        //子组件   
        Vue.component("child",{
          template:`<div>
             child  
          </div>`,
          data(){
            return {
              childname:"11111111111"
            }
          },
          methods:{
            getState(data){
              console.log(data)
            }
          }
        }) 
        new Vue({
          el:"#box",
          data:{
            parentname:"2222222222"
          },
          methods: {
            handleClick(){
              // console.log(this.$refs.myinput.value,this.$refs.mypassword.value)
              console.log(this.$refs.mychild.childname)
              // this.$refs.mychild.childname="22222"
              this.$refs.mychild.getState(this.parentname)
            }
          },
        })
    </script>

10、 v-model通信

    <div id="box">
        <input type="text" v-model="mytext"/>
        <child :mytext="mytext" @event="handleEvent"></child>
             
        </div>
    <script>
        Vue.component("child",{
            template:`
                <div>
                    child-{{mytext}}
                    <button @click="handleClick">click</button>
                </div>
            `,
            props:["mytext"],
            methods: {
                handleClick(){
                    this.$emit("event","222222222222222222222")
                }
            },
        })
        new Vue({
            el:"#box",
            data:{
                mytext:""
            },
            methods: {
                handleEvent(data){
                    this.mytext = data
                }
            },
        })
    </script>

11、props修改

  • this.$emit(“update:title”,“2222222222”),加update关键字
    <div id="box">
        {{mytitle}}
        <child :title.sync="mytitle"></child>
    </div>
 
    <script>
        Vue.component("child",{
            template:`
                <div v-once>
                    child--{{title}}
                    <button @click="handleClick()">胆大的按钮</button>    
                </div>
            `,
            methods: {
                handleClick(){
                    // this.title="kerwin-"+this.title
                    this.$emit("update:title","2222222222")
                }
            },
            props:["title"]
        })
        new Vue({
            el:"#box",
            data:{
                mytitle:"11111111111111"
            }
        })
    </script>

12、动态组件dynamic

  • comoponent标签的is属性来决定渲染哪一个子组件

  • <keep-alive><keep-alive>缓存当前页面的数据,如正在输入时跳转到其他页面再回到此页面时输入的数据还在。

   <div id="box">
       
       <!-- <shopcar></shopcar> -->
       <keep-alive>
         <component :is="which"></component>
       </keep-alive>
 
       <footer>
         <ul>
           <li><a  @click="which='home'" >首页</a></li>
           <li><a  @click="which='list'">列表页</a></li>
           <li><a  @click="which='shopcar'">购物车页面</a></li>
         </ul>
       </footer>
    </div>
    <script type="text/javascript"> 
     var vm =  new Vue({
        el:"#box",
        data:{
          which:"home"
        },
        components:{
          "home":{template:`<div>home组件<input type="text"/></div>`},
          "list":{template:`<div>list组件</div>`},
          "shopcar":{template:`<div>shopcar组件</div>`}
        }
      }) 
    </script>

13、插槽slot

  • 在定义子组件的时候预留一个插槽,使用子组件的时候可以传递html结构进来。单插槽不需要写name,直接插入即可,但是多插槽的话需要给每个插槽写上name属性,插入标签的时候写上slot属性,跟插槽的name对应

  <template id="hello">
    <div>
      <slot name="s1"></slot>
      <h3>{{title}}</h3>
      <slot name="s2"></slot>
    </div>
  </template>
  <div id="app">
    <hello-world :title="title">
      <p slot="s1">content</p>
      <p slot="s2">content2</p>
    </hello-world>

    <hello-world :title="title">
      <son slot="s1"></son>
    </hello-world>
  </div>
  <script>
    const helloWorld = {
      template: '#hello',
      props: ['title', 'content']
    }
    const son = {
      template: '<b>son</b>'
    }

    const app = new Vue({
      el: '#app',
      data: {
        title: 'hello 1912'
      },
      components: {
        helloWorld,
        son
      }
    })
  </script>
  • 新写法 ,template不会渲染成任何标签。

       <div id="box">
            <child>
              <template v-slot:a>
                  <div>联通卡</div>
              </template>
              <template v-slot:b>
                  <div>移动卡</div>
              </template>
              <template v-slot:c>
                    电信卡
              </template>
               
               
            </child>
        </div>
        
        <script type="text/javascript">
        
          Vue.component("child",{
            template:`<div>
                111111
                <slot name="a"></slot>
                2222222
                <slot name="b"></slot>
                3333333
                <slot name="c"></slot>
              </div>  
            `
          })
     
          new Vue({
            el:"#box"
          })
         
        </script>
    
    

十、transition过渡动画

  • <transition> 把过渡效果应用到其包裹的内容上 ,name和mode,mode属性可以指定过度模式: out-in指的是先消失,消失之后新的组件在进来
  <style>
    .v-enter,
    .v-leave-to {
      opacity: 0;
    }
    .v-enter-active,
    .v-leave-active {
      transition: opacity 1s;
    }
    .v-enter-to,
    .v-leave {
      opacity: 1;
    }

    .guoji-enter,
    .guoji-leave-to {
      position: absolute;
      left: -100%;
    }
    .guoji-enter-active,
    .guoji-leave-active {
      position: absolute;
      transition: left 1s;
    }
    .guoji-enter-to,
    .guoji-leave {
      position: absolute;
      left: 0;
    }
  </style>
</head>
<body>
  <div id="app">
    <button @click="news = 'guonei'">国内新闻</button>
    <button @click="news = 'guoji'">国际新闻</button>
    <div>
      <!-- 没有name属性的过度应用的是全局的v-开头的class -->
      <!-- mode属性可以指定过度模式: out-in指的是先消失,消失之后新的组件在进来 -->
      <transition mode="out-in">
        <component :is="news"></component>
      </transition>
      <!-- 加了name属性过度应用的就是guoji-开头的class -->
      <transition name="guoji">
        <component :is="news"></component>
      </transition>
    </div>
  </div>
  <script>

    const guonei = {
      template: '<h3>cctv国内新闻</h3>'
    }
    const guoji = {
      template: '<h3>international news</h3>'
    }
   
    const app = new Vue({
      el: '#app',
      data: {
        news: 'guoji'
      },
      components: {
        guonei,
        guoji
      }
    })
  </script>
  • 写法2:@keyframes,reverse
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Examples</title>
<meta name="description" content="">
<meta name="keywords" content="">
<link href="" rel="stylesheet">
<style>
 
  .a-enter-active, .a-leave-active {
    transition: all 1.5s;
  }
  .a-enter, .a-leave-to /* .fade-leave-active below version 2.1.8 */ {
    opacity: 0;
    transform: translateX(100px);
  }
 
  .b-enter-active {
    animation: aaa 1.5s;
  }
  .b-leave-active {
    animation: aaa 1.5s reverse;
  } 
  @keyframes aaa {
    0% {
      opacity: 0;
      transform: translateX(100px);
    }
 
    100% {
      opacity: 1;
      transform: translateX(0px);
    }
  }
 
</style>
<link rel="stylesheet" href="lib/animate.css">
<script type="text/javascript" src="lib/vue.js"></script>
</head>
<body>
    <!-- <h1 class="animated hinge">动画</h1>  -->
 
    <div id="box">
      <button @click="isShow=!isShow">click</button>
      <transition name="a">
        <div v-if="isShow">
          111111111111111111111
        </div>
      </transition>
 
      <transition name="b">
        <div v-if="isShow">
          222222222222222222
        </div>
      </transition>
 
 
      <transition enter-active-class="animated bounceInRight"
      leave-active-class="animated bounceOutRight">
        <div v-if="isShow">
          3333333333333333333333
        </div>
      </transition>
    </div>
 
    <script>
      var vm = new Vue({
        el:"#box",
        data:{
          isShow:true
        }
      }) 
    </script>
</body>
</html>

  • 写法三

          <transition enter-active-class="animated bounceInRight"
          leave-active-class="animated bounceOutRight">
            <div v-if="isShow">
              3333333333333333333333
            </div>
          </transition>
    
  • transition-group

    <style>
        .b-enter-active {
          animation: aaa 1.5s;
        }
     
        .b-leave-active {
          animation: aaa 1.5s reverse;
        }
     
        @keyframes aaa {
          0% {
            opacity: 0;
            transform: translateX(100px);
          }
     
          100% {
            opacity: 1;
            transform: translateX(0px);
          }
        }
      </style>
    </head>
     
    <body>
      <div id="box">
        <input type="text" v-model="mytext" />
        <button @click="handleClick()">add</button>
     
     
        <transition-group tag="ul" name="b">
          <li v-for="(item,index) in items" :key="item">
            {{item}} -{{index}}
            <button @click="handleDelClick(index)">del</button>
          </li>
        </transition-group>
     
      </div>
      <script>
        var vm = new Vue({
          el: "#box",
          data: {
            mytext: "", //状态
            items: ["aaa", "bbbb", "ccc"]
          },
          methods: {
            handleClick() {
              console.log("输入框value", this.mytext)
              this.items.push(this.mytext)
            },
            handleDelClick(index) {
              console.log("del", index)
              this.items.splice(index, 1);
            }
          },
        })
      </script>
    

十一、生命周期

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <script src="./vue.js"></script>
</head>
<body>
  <div id="app">
    <input type="text" ref="myinput">
    {{msg}}
  </div>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        msg: 'hello 1912'
      },
      beforeCreate () {
        // vue实例创建之前,这里什么都获取不到
        console.log('--------beforeCreate----------')
        console.log(this.$el, this.$refs.myinput, this.$data)
      },
      created () {
        // vue实例创建完成,在这里可以做一些初始化的操作,一般初始的ajax请求很多时候可以在这里做
        console.log('--------created----------')
        console.log(this.$el, this.$refs.myinput, this.$data)
      },
      beforeMount () {
        // 挂载之前,这里虚拟DOM已经创建好了,但是还没有真实DOM
        console.log('--------beforeMount----------')
        console.log(this.$el, this.$refs.myinput, this.$data)
      },
      mounted () {
        // 完成挂载,这里就能获取到真实DOM了
        console.log('--------mounted----------')
        console.log(this.$el, this.$refs.myinput, this.$data)
      },
      beforeUpdate () {
        // 如果修改了数据,这是数据更新以后,DOM刷新之前,这货没啥用
        console.log('--------beforeUpdate----------')
        console.log(this.$el, this.$refs.myinput, this.$data.msg)
      },
      updated () {
        // 如果修改了数据,这是数据更新以后,DOM也完成刷,这货也没啥用
        console.log('--------updated----------')
        console.log(this.$el, this.$refs.myinput, this.$data.msg)
      },
      beforeDestroy () {
        // 销毁之前,一般在这里做一些善后工作,比如:清除定时器,或者在销毁之前给后端发送一些数据
      },
      destroyed () {
        // 销毁完成,这里也没啥好做的
      }
    })
  </script>
</body>
</html>

十二、Vue.nextTick

  • 修改相应到真实DOM之后才能调用swiper

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <meta http-equiv="X-UA-Compatible" content="ie=edge">
      <title>Document</title>
      <script src="./vue.js"></script>
      <script src="https://cdn.bootcss.com/Swiper/4.5.1/js/swiper.min.js"></script>
      <link href="https://cdn.bootcss.com/Swiper/4.5.1/css/swiper.min.css" rel="stylesheet">
      <style>
      .swiper-container {
        width: 800px;
        height: 500px;
      }
      </style>
    </head>
    <body>
      <div id="app">
        <div class="swiper-container">
          <div class="swiper-wrapper">
              <div clas hs="swiper-slide" v-for="item in banner" :key="item.id">{{ item.title }}</div>
          </div>
          <!-- 如果需要分页器 -->
          <div class="swiper-pagination"></div>
      </div>
      </div>
      <script>
        const app = new Vue({
          el: '#app',
          data: {
            banner: []
          },
          created () {
            // 在这里发送ajax请求,拿到swiper的数据渲染轮播图
            // 用定时器模拟一下
            // 有可能在拿到数据以后DOM还没有更新
            setTimeout(() => {
              this.banner = [
                { id: 1, title: 'slider1' },
                { id: 2, title: 'slider2' },
                { id: 3, title: 'slider3' }
              ]
              // 应该banner的修改相应到真实DOM之后才能调用swiper
              Vue.nextTick().then(() => {
                this.initSwiper()
              })
            }, 100)
          },
          methods: {
            initSwiper () {
              var mySwiper = new Swiper ('.swiper-container', {
              loop: true, // 循环模式选项
              
              // 如果需要分页器
              pagination: {
                el: '.swiper-pagination',
              },
              
              // 如果需要前进后退按钮
              navigation: {
                nextEl: '.swiper-button-next',
                prevEl: '.swiper-button-prev',
              },
              
              // 如果需要滚动条
              scrollbar: {
                el: '.swiper-scrollbar',
              },
            })    
            }
          }
        })
      </script>
    </body>
    </html>
    
    
          <swiper :key="datalist.length" :perview="3">
            <div v-for="data in datalist" :key="data.filmId"
            class="swiper-slide">
              <img :src="data.poster"/>
            </div>
          </swiper>
    

十三、自定义指令

  • directives - 对普通 DOM 元素进行底层操作

    (2)钩子函数

    • 参数 el,binding,vnode,oldvnode
    • bind,inserted,update,componentUpdated,unbind
    • bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。

    • inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。

    • update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新

    • componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。

    • unbind:只调用一次,指令与元素解绑时调用。

    • el:指令所绑定的元素,可以用来直接操作 DOM 。

    • binding:一个对象,包含以下属性:

    • name:指令名,不包括 v- 前缀。

    • value:指令的绑定值,例如:v-my-directive=“1 + 1” 中,绑定值为 2。

    • oldValue:指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用。无论值是否改变都可用。

    • expression:字符串形式的指令表达式。例如 v-my-directive=“1 + 1” 中,表达式为 “1 + 1”。

    • arg:传给指令的参数,可选。例如 v-my-directive:foo 中,参数为 “foo”。

    • modifiers:一个包含修饰符的对象。例如:v-my-directive.foo.bar 中,修饰符对象为 { foo: true, bar: true }。

    • vnode:Vue 编译生成的虚拟节点。移步 VNode API 来了解更多详情。

    • oldVnode:上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用。

    <div id="box">
          <!-- <div v-show="isShow"></div> -->
          <div v-hello="'red'">{{myname}}</div>
          <div v-hello="'yellow'">{{myname}}</div>
          <div v-hello="'blue'">{{myname}}</div>
          {{mycolor}}
          <div v-hello="mycolor">{{myname}}</div>
          <input type="text" v-focus/>
        </div>    
     
        <script type="text/javascript">
          Vue.directive("hello",{
            inserted(el,binding){
              //指令的生命周期-1 ,这个节点插入dom,会被调用
              console.log(binding.value)
              el.style.background=binding.value
            },
            update(el,binding){
              // update 不是updated
              el.style.background=binding.value
            }
          })
     
          Vue.directive("focus",{
            inserted(el){
              el.focus()
            }
          })
     
          var vm = new Vue({
            el:"#box",
            data:{
              mycolor:"pink",
              myname:"kerwin"
            }
          })
            
        </script>
    </body>
    </html>
    
    

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值