通俗重制系列--Vue2基础教程

简介: 通俗重制系列--Vue2基础教程

通俗重制系列--Vue2基础教程


网络异常,图片无法展示
|

起手式

完整版同时包括编译器(compiler) 和 运行时(runtime)将模板字符串编译为 JavaScript 渲染函数(render函数)的代码 运行时的功能包括创建 Vue 实例、渲染并处理虚拟 DOM 等,它包括除了编译器的其他所有功能\

两个版本的区别

Vue完整版 Vue只包含运行时版
特点 有compiler 没有compiler
视图 写在HTML里,或者写在template选项里 写在render函数里,用h创建标签
cdn引入 vue.js vue.runtime.js
webpack引入 需要配置alias 默认使用
vue@cli引入 需要额外配置 默认使用

那究竟应该使用哪一个版本呢?

1、   对于用户来说,非完整版 (即runtime版)体积小,用户体验好,但只支持h函数

2、 对于程序员来说,只能写h函数的话,开发体验不好,如果有compiler, 开发者就能写更直观更语义化的HTML标签和template, 所以我们需要一个compiler

3、   vue-loader就可以引入compiler, 把vue文件里的HTML标签和template 会在构建时预编译成 h函数,这样用户和开发者都高兴

template 和 render 的用法

// 需要编译器
new Vue({
  template: '<div>{{ hi }}</div>'
})
// 不需要编译器
new Vue({
  render (h) {
    return h('div', this.hi)
  }
})

template标签和JS里的template

//vue文件中的template标签
  <template>
      <div id="app">      
          {{n}}
          <button @click="add">+1</button>   
     </div> 
  </template>
//js中的template
    template : `
        <div id="app">      
          {{n}}
          <button @click="add">+1</button>   
        </div> 
    `

render函数:

//不完整版在js中构建视图
  render(h){ 
       return h('div', [this.n,h('{on:{click:this.add}’,'+1'])
   }
//不完整版使用vue-loader
//先创建一个demo.vue文件,在里面构建视图
    import demo from "./demo.vue"
     new Vue({
       el: "#app",
       render(h) {
         return h(demo)
       }
     })

options选项

new Vue() 这就是构造一个Vue的实例。

网络异常,图片无法展示
|

这个实例会根据你给的选项得出一个对象(vm),vm封装了这个DOM对象以及对应的所有操作,不管是事件绑定还是数据的读写、DOM更新,全部都由vm这个对象负责。你只需要去调用它的API就好了。

原型:对象.__proto__===其构造函数.prototype 推出vm.__proto__===Vue.prototype

函数也是对象,所以Vue函数对象的内存图如上。

函数.__proto__===Function.prototype推出Vue.__proto__===Function.prototype

问题一: 初始化时可以写些什么对象进去(options)?

问题二: vm自己有哪些属性?

问题三: Vue函数本身有哪些属性?

问题四: 每个函数都有个属性叫prototype,同时每个对象都有个属性叫__proto__。假设Vue.prototype对应的对象的地址是#419,那请问这个#419里面有哪些属性呢?

问题五:Vue.prototype.__proto__= ?

options的五类属性

  • DON: el,template,render,rebderError
  • 生命周期钩子函数:beforeCreate,created,beforeMount,mounted,beforeUpdate,updated,activated,deactivated,beforeDestroy,destroyed,erroCaptured。
  • 资源:directives,filters,components
  • 组合:parent,mxins,extends,provide,inject

第1类属性 数据(Data):

data 数据 
props 属性
computed 计算属性 //被计算出来的
methods 方法,用来定义方法的
watch 观察 //当data变化时做某些事情就用watch
propsData //很少用,单元测试会用

方法和函数的区别?

1.概念:方法是属于面向对象概念,函数属于数学概念。

在面向对象里叫方法,有对象才有方法,方法依附于对象即对象.方法,比如说obj.sayhi()sayhi就叫方法也是函数,一般叫方法。 如果sayhi()这样写就叫函数

在数学里叫函数。

2.都指同一个东西

function(p1,p2){
  return 
}

第2类属性 DOM:

el 挂载点 //你要用你的模版替换页面上的哪一块,你的挂载点
template //你的HTML内容。着重讲语法v-if、v-for
render  渲染 //⚠️注意template和render只能二选一!
//template是给完整版用的,render是给非完整版用的。一起用必然有一个会失效!
renderError //很少用

第3类属性 生命周期钩子:

生命周期:Vue组件在页面中插入一个<div>监听它的事件,然后用户点击按钮时变化。可以切入的点叫做钩子。

beforeCreate  创建之前
created  创建之后
beforeMount
mounted 挂在之后
beforeUpdate
updated 更新之后
activated
deactivated
beforeDestroy
destroyed 失败之后
errorCaptured //很少用

网络异常,图片无法展示
|

第4类属性 资源

directives 指令
filters 过滤器 //尽量不去用,用methods代替
components 组件 
//如果要在一个文件引用另一个Vue的文件就用组建。Demo.vue就叫组件,英文名叫components。

第5类属性 组合:

parent //很少用
mixins 混入
extends 扩展
provide 提供
inject 注入

入门属性

1.el 挂载点

可以用$mount代替

你要用你的模版替换页面上的哪一块,可以用$mount代替。组件或者实例的挂载点。

index.html
<div id="app"> {{n}} </div>
main.js
new Vue({ 
  el: '#app',
  render(h) {
    return h(Demo)
  }
})
/*可以用$mount替代:*/
new Vue({ 
  render: h => h(Demo)
}).$mount('#app')
//const vm = new Vue({
//  render: h => h(Demo)
//})
//vm.$mount('#app')

总结

特点1.名字要保持一致

特点2.如果在<div>内加内容,那么多半用户是看不见的。js加载完后会把hello干掉。

特点3.可以用$mount替代。挂载基本等同于replace或append

2.data 内部数据

组件的定义只接受函数。

用vue完整版来示例

main.js
console.log(window.Vue)
// import Vue from 'vue' 删掉,这次不从npm引入,直接使用全局的Vue
const Vue = window.Vue
Vue.config.productionTip = false
new Vue({ //实例
  data: { //内部数据,只支持函数
    n: 0
  },
  template: `
  <div class="red">
    {{n}}
    <button @click="add">+1</button>
  </div>
  `,
  methods: {
    add() { //add要先定义好,不然会报错
      this.n += 1
    }
  }
}).$mount('#app')

Bug: Vue的data有bug,后面讲"数据响应式"时会说。

为什么data必须是函数?

如果你是个组件,比如Demo组件,引入Demo组件import Demo from './Demo.vue'

//Demo.vue
export default {
    data(){ //vue-loader写文件时,data必须是函数
      return {
        n:0
      }
    },
}

Demo实际上是对象,Vue会自动把Demo传给new Vue(Demo)

假设如果有两个组件共用内部数据data,当其中一个改变时另一个也会变,因为它们引用的是同一个data。函数会阻止两个组件共用data的问题。

main.js
render:h=>h(x,[h(Demo),h(Demo)])

3.methods 方法

事件处理函数或者是普通函数

add必须写在methods里面,如果写到外面会报错。

网络异常,图片无法展示
|

main.js
//new Vue({  错误
//  add() { this.n += 1 },
//  methods: {
//  }
//})

事件处理函数: 写到一个@click或keypress或者任何事件作为它的处理函数

普通函数method代替filter。

main.js
new Vue({ //实例
  data: {
    n: 0,
    array:[1,2,3,4,5,6,7,8,9]
  },
  template: `
    <div class="red">
      {{n}}
      <button @click="add">+1</button>
      <hr>
      {{filter(array)}} //2'filter()
    </div>
   `,
   methods: {
    add() {
      this.n += 1
    },
  filter(array) {
    return array.filter(i => i % 2 === 0)
    }
//filter() {
  //return this.array.filter(i => i % 2 === 0)
  //}
}).$mount('#app')

网络异常,图片无法展示
|

bug: methods第2种用法,用来主动在模版里调用。这种调用特点是每次渲染都会重新调用。就算毫无意义跟之前是相同的结果它也会执行。

4.components 组件

使用Vue组件,注意大小写

如果要在一个文件引用另一个Vue的文件就用组件。Demo.vue就叫组件(components)。

const vm=new Vue({...})vm是Vue实例或Vue对象

这个不能叫做组件,它使用"其它的Vue实例"的时候,"其它的Vue实例"才是组件。

如何使用组件?

首先要创建组件,组件的3种引入形式

1' 创建一个.vue文件(推荐)。

这个文件就是Vue组件,比如Demo.vue,然后引入该文件

使用组件

说明要用的组件是frank,然后就可以在template里面写frank。

main.js
import Demo from './Demo.vue' //引入Demo文件
new Vue({ //实例
  components: { //说明要用的组件是frank
    frank: Demo //名字:值,Demo组件
  //Demo:  Demo //es6语法可以简写为Demo
  },
  template: `
  <div class="red">
    <frank/>
  </div>
  `,
}).$mount('#app')  
Demo.vue
<template>
    <div class="red">
       fuck
    </div>
</template>

网络异常,图片无法展示
|

优先使用第1种,其它2种不够模块化。

2' 用js的方式

不要components,直接声明全局的Demo2。

main.js
Vue.component('Demo2', {
  template: `
    <div> demo2 </div>
  `
})
new Vue({ 
template: `
  <div class="red">
    <Demo2/>
  </div>
  `,
}).$mount('#app')

网络异常,图片无法展示
|

你是入口就是实例,被别人用的就是组件。

3' 前2种的结合

保留js对象,又保留components

main.js
Vue.component('Demo2', {
  template: `
    <div> demo2 </div>
  `
})
new Vue({ 
  components: {
    fuck: {
      template: `
      <div> demo3 </div>
    `
    }
  },
 template: `
  <div class="red">
     <fuck/>
  </div>
  `,

网络异常,图片无法展示
|

fuck也可以有data

fuck: {
      data() { //组件data必须用函数
        return { n: 0 }
      },
      template: `
      <div> fuck's n:{{n}} </div>
    `
    }

fuck对象里面的写法,跟外面的options是完全一样的。

网络异常,图片无法展示
|

什么是组件?

组件:可以组合的物件就叫组件。比如手臂、腿就是人的组件

组件可以认为是实例中的实例。

注意大小写

1.文件名最好全小写,因为有些古老的操作系统,比如window10可能不能识别大小写,防止2个大小写文件重名。

2.组件首字母最好大写。

网络异常,图片无法展示
|

5.四个钩子

1.created 实例出现在内存中

2.mounted 实例出现在页面中

3.updated 实例更新了

4.destroyed 实例消亡了

1.2.3.created、mounted、updated

new Vue({ //实例
  created() {
    //debugger
    console.log("这玩意出现在内存中")
  },
  mounted() {
    //debugger
    console.log("这玩意出现在页面中")
  },
   updated() {
    console.log("更新了") //点击+1按钮后显示更新了
    console.log(this.n) //每次拿到的n都是最新的
  },
}).$mount('#app')

可以通过debugger验证实例是否出现在页面:n和button没加载出来说明出现在内存,加载出来证明出现在页面。

网络异常,图片无法展示
|

网络异常,图片无法展示
|

网络异常,图片无法展示
|

4.destroyed 实例消亡了

步骤

逻辑:让一个组件出现又消失

1.src新建文件demo2.vue

把目前的实例变组件:main.jsnew Vue({ //实例 })的实例剪切到demo2.vue<script>里。别忘了把template内容也移到<template>里。

2.创建实例

//main.js
import Demo from './demo2.vue'
new Vue({ //实例
  components: { Demo },
  data: { //自己new Vue就不是组件,所以data可以是对象
    visible: true
  },
  template: `
    <div>
      <button @click="toggle">toggle</button>
      <hr>
      <Demo v-if="visible===true"/>
    </div>
  `,
  methods: {
    toggle() {
      this.visible = !this.visible //把visible变为反值,实现按钮的切换
    }
  }
}).$mount('#app')

3.监听destroyed

destroyed(){
    console.log("已经消亡了")
  }

每次toggle后n将重新初始化为0。

网络异常,图片无法展示
|

知识点

1.渲染页面:  render函数

render: h => h(Demo) //更简单
//等价于
components: { Demo },
  template: `
    <Demo/>
  `,

2.v-if什么时候出现

new Vue({ 
  components: { Demo },
  data: { //自己new Vue就不是组件,所有data可以是对象
    visible: true
  },
  template: `
      <Demo v-if="visible===true"/>
  `
}).$mount('#app')

3.实例 VS 组件

实例就是main.js,代码特征new Vue({ }),data可以是对象、函数。 实例需要导入组件demo.vue实例包含组件,如果实例是爸爸,那组件就是流落在外的儿子。

main.js
import Demo from './demo.vue' //导入组件`demo.vue`
new Vue({ 
  data: { //data可以是对象
    visible: true
  },
}).$mount('#app')
复制代码

组件就是新建的demo.vue,代码特征3个标签<template>、<script>、<style scoped>,data必须是函数。 可以认为是实例中的实例。

demo.vue
//组件
<template> //html
</template>
<script> //js
export default {
 data(){ //vue-loader写文件时,data必须是函数 }
    }
</script>
<style scoped> //css
</style>

4.函数和方法的区别?

函数(function) 是可以执行的javascript代码块,由javascript程序定义或javascript实现预定义。函数可以带有实际参数或者形式参数,用于指定这个函数执行计算要使用的一个或多个值,而且还可以返回值,以表示计算的结果。

方法(method) 是通过对象调用的javascript函数。也就是说,方法也是函数,只是比较特殊的函数。假设有一个函数是fn,一个对象是obj,那么就可以定义一个method。方法和对象相关,函数和对象无关。

方法和函数大致上是相同的,但有两个主要的不同之处:

(1)方法中的数据是隐式传递的。

(2)方法可以操作类内部的数据(请记住,对象是类的实例化–类定义了一个数据类型,而对象是该数据类型的一个实例化)

6.props 外部数据

外部数据是由外部来传值的(值是字符串),也叫外部属性

1' 传字符串message="n"

2' 传变量 :message="n" 传入this.n数据

3' 传函数:fn="add" 传入this.add函数

1' 传字符串 message="n"

步骤

(1)新建文件demo3.vue

props从外部接收message,这个message会自动绑到this上。Vue允许省掉this。

//demo3.vue
<template>
    <div class="red">
        这里是demo3的内部
        {{message}} //{{this.message}}this可省
    </div>
</template>
<script>
  export default {
   //声明:props:[属性名]
     props:['message'] //从外部接收message,这个message会自动绑到this上
  }
</script>
<style scoped>
  .red{ color: red; }
</style>

(2)使用props外部数据

main.js
import Demo from './demo3.vue'
new Vue({ //实例
components: { Demo },
 template: `
    <div>
      <Demo message="你好 props"/> //传值:在组件后加key value
    </div>
  `,
}).$mount('#app')

message="字符串"

网络异常,图片无法展示
|

2' 传变量 :message="n" 传入this.n数据

加空格和冒号" :", 注意Demo后有空格!

// main.js
import Demo from './demo3.vue'
new Vue({ //实例
 components: { Demo },
  data: {
    n:0
  },
   template: `
    <div>
      <Demo :message="n"/> <!--传变量(数据) -->
    </div>
  `,
}).$mount('#app')

空格:message="JS(变量)"

网络异常,图片无法展示
|

3' 传方法(函数):fn="add" 传入this.add函数

1.添加第2个参数fn

demo3.vue
<template>
    <div class="red">
        这里是demo3的内部
        {{message}}  
        <button @click="fn">call fn</button>
    </div>
</template>
<script>
export default {
    props:['message','fn']
  //从外部接收message、fn,会自动绑到this上
}
</script>
<style scoped>
.red{ color: red; }
</style>

2.接收方法

main.js
import Demo from './demo3.vue'
new Vue({ //实例
  components: { Demo },
  data: { //实例的data可以是对象
    visible: true,
    n: 0
  },
   template: `
    <div>
      {{n}}
      <Demo :fn="add"/> <!--传JS变量(数据) --> 
    </div>
  `, 
  methods: {
    add() {
      this.n += 1
       }, 
  }
}).$mount('#app') 

空格:message="JS(方法)"

网络异常,图片无法展示
|

把n回传给里面的儿子,得到的是最新的n。

main.js
template: `
  <div>
    {{n}}
    <Demo :message="n" :fn="add"/>
  </div> 
`,

网络异常,图片无法展示
|

对Vue 数据响应式的理解

getter、setter

当你把一个普通的 JavaScript 对象传入 Vue 实例作为 data 选项,Vue 将遍历此对象所有的 property,并使用 Object.defineProperty 把这些 property 全部转为 getter/setter

网络异常,图片无法展示
|
总结一下:

  1. 任何一个 Vue Component 都有一个与之对应的 Watcher 实例。
  2. Vue 的 data 上的属性会被添加 getter 和 setter 属性。
  3. 当 Vue Component render 函数被执行的时候, data 上会被 触碰(touch), 即被, getter 方法会被调用, 此时 Vue 会去记录此 Vue component 所依赖的所有 data。(这一过程被称为依赖收集)
  4. data 被改动时(主要是用户操作), 即被, setter 方法会被调用, 此时 Vue 会去通知所有依赖于此 data 的组件去调用他们的 render 函数进行更新
let obj0 = {
  姓: "高", 名: "圆圆", age: 18
};
// 需求一,得到姓名
let obj1 = {
  姓: "高", 名: "圆圆", 姓名() {
    return this.姓 + this.名;
  }, age: 18
};
console.log("需求一:" + obj1.姓名());
// 姓名后面的括号能删掉吗?不能,因为它是函数
// 怎么去掉括号?
// 需求二,姓名不要括号也能得出值
let obj2 = {
  姓: "高", 名: "圆圆", get 姓名() {
    return this.姓 + this.名;
  }, age: 18
};
console.log("需求二:" + obj2.姓名);
// 总结:getter 就是这样用的。不加括号的函数,仅此而已。
// 需求三:姓名可以被写
let obj3 = {
  姓: "高", 名: "圆圆", get 姓名() {
    return this.姓 + this.名;
  }, set 姓名(xxx) {
    this.姓 = xxx[0]
    this.名 = xxx.slice(1)
  }, age: 18
};
obj3.姓名 = '高媛媛'
console.log(`需求三:姓 ${obj3.姓},名 ${obj3.名}`)
// 总结:setter 就是这样用的。用 = xxx 触发 set 函数

网络异常,图片无法展示
|

Object.defineProperty()

Vue是通过 JS 标准内置对象方法 Object.defineProperty 来设定将data中普通的属性n转化为getter、setter方法的属性n的。

当你把一个普通的 JavaScript 对象传入 Vue 实例作为 data 选项,Vue 将遍历此对象所有的 property,并使用 Object.defineProperty 把这些 property 全部转为 getter/setter。

语法Object.defineProperty(obj, prop, descriptor)

参数

obj:要在其上定义属性的对象。

prop:要定义或修改的属性的名称。

descriptor:将被定义或修改的属性描述符。

返回值: 被传递给函数的对象。

Object.defineProperty() - JavaScript | MDN (mozilla.org)

let data0 = {
  n: 0
}
// 需求一:用 Object.defineProperty 定义 n
let data1 = {}
Object.defineProperty(data1, 'n', {
  value: 0
})
console.log(`需求一:${data1.n}`)
// 总结:这煞笔语法把事情搞复杂了?非也,继续看。
// 需求二:n 不能小于 0
// 即 data2.n = -1 应该无效,但 data2.n = 1 有效
let data2 = {}
data2._n = 0 // _n 用来偷偷存储 n 的值
Object.defineProperty(data2, 'n', {
  get() {
    return this._n
  },
  set(value) {
    if (value < 0) return
    this._n = value
  }
})
console.log(`需求二:${data2.n}`)
data2.n = -1
console.log(`需求二:${data2.n} 设置为 -1 失败`)
data2.n = 1
console.log(`需求二:${data2.n} 设置为 1 成功`)
// 抬杠:那如果对方直接使用 data2._n 呢?
// 算你狠

网络异常,图片无法展示
|

proxy() 代理

// 需求三:使用代理
let data3 = proxy({data: {n: 0}}) // 括号里是匿名对象,无法访问
function proxy({data}/* 解构赋值,别TM老问 */) {
  const obj = {}
  // 这里的 'n' 写死了,理论上应该遍历 data 的所有 key,这里做了简化
  // 因为我怕你们看不懂
  Object.defineProperty(obj, 'n', {
    get() {
      return data.n
    },
    set(value) {
      if (value < 0) return
      data.n = value
    }
  })
  return obj // obj 就是代理
}
// data3 就是 obj
console.log(`需求三:${data3.n}`)
data3.n = -1
console.log(`需求三:${data3.n},设置为 -1 失败`)
data3.n = 1
console.log(`需求三:${data3.n},设置为 1 成功`)
// 杠精你还有话说吗?
// 杠精说有!你看下面代码
// 需求四
let myData = {n: 0}
let data4 = proxy({data: myData}) // 括号里是匿名对象,无法访问
// data3 就是 obj
console.log(`杠精:${data4.n}`)
myData.n = -1
console.log(`杠精:${data4.n},设置为 -1 失败了吗!?`)
// 我现在改 myData,是不是还能改?!你奈我何
// 艹,算你狠
// 需求五:就算用户擅自修改 myData,也要拦截他
let myData5 = {n: 0}
let data5 = proxy2({data: myData5}) // 括号里是匿名对象,无法访问
function proxy2({data}/* 解构赋值,别TM老问 */) {
  // 这里的 'n' 写死了,理论上应该遍历 data 的所有 key,这里做了简化
  // 因为我怕你们看不懂
  let value = data.n
  Object.defineProperty(data, 'n', {
    get() {
      return value
    },
    set(newValue) {
      if (newValue < 0) return
      value = newValue
    }
  })
  // 就加了上面几句,这几句话会监听 data
  const obj = {}
  Object.defineProperty(obj, 'n', {
    get() {
      return data.n
    },
    set(value) {
      if (value < 0) return//这句话多余了
      data.n = value
    }
  })
  return obj // obj 就是代理
}
// data3 就是 obj
console.log(`需求五:${data5.n}`)
myData5.n = -1
console.log(`需求五:${data5.n},设置为 -1 失败了`)
myData5.n = 1
console.log(`需求五:${data5.n},设置为 1 成功了`)
// 这代码看着眼熟吗?
// let data5 = proxy2({ data:myData5 })
// let vm = new Vue({data: myData})
// 现在我们可以说说 new Vue 做了什么了

网络异常,图片无法展示
|
结论:Vue会遍历传入的data对象所有属性,并使用Object.defineProperty把这些属性全部转为getter/setter,这样就生成一个新的对象全权负责数据——就是实例化的Vue对象vm。这样vm会成为data 的代理,对 data 的所有属性进行监控,当数值发生改变的时候,vue就调用render函数重新渲染视图。

网络异常,图片无法展示
|

数据响应式

当你创建一个实例时

const vm = new Vue({data:{n: 0}})
  • vue 会让 vm 成为 myData 的代理。
  • vue 会对 myData 的所有属性进行监控。
  • 示例

1、在 data 中添加属性

对于一般的对象来说,可以在 data 中预先把所有可能用到的属性全部写出来,这样并不需要新增属性,只需要改它。 也可以通过其他方法来添加属性。 在了解以上原理后,我们来了解 Vue 提供的一个 API:

Vue.set(object, key, value)
this.$set(object, key, value)

网络异常,图片无法展示
|

2、对数组的方法

vue对数组进行了改变,给数组加了一层原型,在其中Vue修改了7个方法覆盖了之前数组原型的7个方法。调用这些Vue新定义的方法时,在这些新方法里Vue会加上对新添的元素的监听(相当于进行了set操作),把新数据也进行代理,这样vue就能重新监测到数组的变化了更新UI操作 具体的七个变更方法:

  • push()(在数组结尾处)向数组添加一个新的元素
  • pop()方法从数组中删除最后一个元素
  • shift()会删除首个数组元素,并把所有其他元素“位移”到更低的索引
  • unshift()(在开头)向数组添加新元素,并“反向位移”旧元素
  • splice()拼接,可用于向数组添加新项
  • sort()
  • reverse()

computed 和 watch的区别

computed

对比

  1. 不用 computed 筛选男女
  2. 用 computed 筛选男女

这个我有过总结computed 和 watch的区别

模板、指令与修饰符

Vue 模板、指令与修饰符

进阶构造属性

Vue 进阶属性

directives、mixins、extends、provide、inject

directives 指令

内置指令

  • v-if、v-for、v-show、v-html

自定义指令

一、 声明一个全局指令

Vue.directive('x', directiveOptions)

二、  声明一个局部指令

new Vue({
    ...,
    directives: {
        "x": directiveOptions
    }
})

directiveOptions

五个函数属性

  • bind(el, info, vnode, oldVnode) - 类似 created
  • inserted(参数同上) - 类似 mounted
  • update(参数同上) - 类似 updated
  • componentUpdated(参数同上) - 用的不多
  • unbind(参数同上) - 类似 destroyed

缩写

指令的作用

主要用于 DOM 操作

  • Vue 实例/组件用于数据绑定、事件监听、DOM 更新
  • Vue 指令主要目的就是原生 DOM 操作

减少重复

  • 如果某个 DOM 操作你经常使用,就可以封装为指令
  • 如果某个 DOM 操作比较复杂,也可以封装为指令

mixin 混入

类比

  • directives 的作用是减少 DOM 操作的重复
  • mixins 的作用是减少 data、methods、钩子的重复

网络异常,图片无法展示
|
代码

技巧

  • 选项智能合并 混入
  • Vue.mixin 全局混入

extends 继承、扩展

减少重复

  • 遇到与 mixins 同样的需求
  • Vue.extend 或 options.extends
const MyVue = Vue.extend({
    data(){return {name:'', time:undefined}}
    created(){
        if(!this.name){console.error(no name!)}
        this.time = new Date()
    },
    beforeDestroy(){
        const duration = (new Date()) - this.time
        console.log(`${this.name}存活时间${duration}`)
    }
})
  • 然后就可以使用 new MyVue(options)

provide 和 inject

使用

  • provideObject | () => Object
  • injectArray<string> | { [key: string]: string | Symbol | Object }
  • provide 是祖先组件向子孙后代注入一个依赖
  • inject 是让后代注入祖先提供的依赖

网络异常,图片无法展示
|

示例

示例

总结

  • 作用:大范围的 data 和 method 等共用
  • 注意:不能只传值不传方法,因为值是被复制给 provide的
  • 可以传引用但不推荐,容易失控

表单与v-model

基本用法

input / textarea / checkbox / radio / select / form

  • input-文本:
<input v-model="message" placeholder="edit me"> 
<p>Message is: {{ message }}</p>
  • textarea-多行文本:
<span>Multiline message is:</span> 
<p style="white-space: pre-line;">{{ message }}</p> 
<br> 
<textarea v-model="message" placeholder="add multiple lines"></textarea>
  • checkbox-复选框:
<input type="checkbox" id="checkbox" v-model="checked"> 
<label for="checkbox">{{ checked }}</label>
  • radio-单选按钮:
<div id="example-4"> 
    <input type="radio" id="one" value="One" v-model="picked"> 
    <label for="one">One</label>
    <br> 
    <input type="radio" id="two" value="Two" v-model="picked"> 
    <label for="two">Two</label> 
    <br> 
    <span>Picked: {{ picked }}</span> 
</div>
new Vue({ 
    el: '#example-4', 
    data: { 
        picked: '' 
    } 
})
  • select-选择框:
  • 单选时:
<div id="example-5"> 
    <select v-model="selected"> 
    <option disabled value="">请选择</option> 
        <option>A</option> 
        <option>B</option> 
        <option>C</option> 
    </select> 
    <span>Selected: {{ selected }}</span> 
</div>
new Vue({ 
    el: '...', 
    data: { 
        selected: '' 
    } 
})

多选时:

<div id="example-6"> 
    <select v-model="selected" multiple style="width: 50px;"> 
        <option>A</option> 
        <option>B</option> 
        <option>C</option> 
    </select>
    <br> 
    <span>Selected: {{ selected }}</span> 
</div>
new Vue({ 
    el: '#example-6', 
    data: { 
        selected: [] 
    } 
})
  • form:
<form @submit.prevent="onSubmit">
  <label>
    <span>用户名</span>
    <input type="text" v-model="user.username" />
  </label>
  <label>
    <span>密码</span>
    <input type="text" v-model="user.password" />
  </label>
  <button type="submit">登录</button>
</form>
new Vue({ 
      name: "App",
  data(){
    return {
      user:{
        username: "",
        password: ""
      },
    }
  },
  methods:{
    onSubmit()
  },
  components: {},
})

修饰符

.lazy / .number / .trim

  • v-model.lazy,焦点失去时生效
  • v-model.number,只接收数字
  • v-model.trim,两头空格去掉

v-model

  • 默认利用名为 value 的 prop 和名为 input 的事件
  • 等价于 <input type="text" :value="user.username" @input="user.username = $event.target.value">
  • 双向绑定:v-model会绑定一个变量,在变量变化的时候 UI 会变化,用户改变 UI 的时候,数据也会改变
  • v-model 是 v-bind:value 和 v-on:input 的语法糖
  • 自定义:v-on:input="$event",原生:v-on:input="$event.target.value"
  • 监听的事件
  • input 事件,键盘、鼠标、任何输入设备的输入
  • change 事件,只在 input 是去焦点时触发 (v-model.lazy)

使用 Ant Design of Vue

使用组件

$ npm i --save ant-design-vue
/* 完整引入 Antd 组件 */
import Vue from 'vue'
import App from './App.vue'
import Antd from 'ant-design-vue'
import 'ant-design-vue/dist/antd.css'
Vue.config.productionTip = false;
Vue.use(Antd)
new Vue({
  render: h => h(App),
}).$mount('#app')
/* 局部导入组件 */
import { Button, message } from 'ant-design-vue'
Vue.use(Button)

VueRouter

Vue-Router 前端路由实现的思路

Vue 动画原理

#文档 过渡 & 动画

轮播组件slides

轮播难点在于最末位到首位的切换方式,在讲轮播之前需要讲下动画。 Vue动画支持很多种不同的方式。

Vue动画方式1 - CSS transition

Vue提供了transition组件

HTML
//先引入Vue(bootCDN)
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.5.17/vue.min.js"></script>
<div id="demo">
  <button v-on:click="show = !show">
    Toggle
  </button>
//1.写`<transition>`
  <transition name="fade"> 
    <p v-if="show">hello</p>
  </transition>
</div>
CSS
//2.写类
.fade-enter-active, .fade-leave-active {
  transition: all 2s;
  }
.fade-enter, .fade-leave-to {
  opacity: 0;
  width:100px
  }
//3.设置初始值
p{ 
  border:1px solid red;
  width:300px
  }
JS
new Vue({
  el: '#demo',
  data: { show: true }
})

步骤

第1步.在html里写<transition>

第2步.在css里写.fade开头的一系列类

最后给需要的属性添加初始值

对于这些在过渡中切换的类名来说,如果你使用一个没有名字的 ,则v-是这些类名的默认前缀。如果你使用了 <transition name="fade">,那么v-enter会替换为fade-enter

文档 过渡的类名

在进入/离开的过渡中,会有6个class切换:

v-enter-active、v-leave-active表示过渡时间有多长,一般会合并写这2个类,因为动的都是一样的。 v-enter、v-leave-to表示进入和退出状态,这2个类也会合写。剩下2个一般不用。

网络异常,图片无法展示
|

p经历的过程:

一开始p就是这个类,但是由于目前是隐藏的show: false,所以需要enter进入DOM,进入DOM的过程可以详细的去控制。

v-enter控制进入时的开始的状态,v-enter-to控制进入时的结束的状态,fade-enter-active控制它如何去添加补间动画,一般不需要加v-enter-to因为结束状态应该就是它原本状态p,没必要加。 等动画结束,就会把这3个类v-enter、fade-enter-active、v-enter-to都删掉,恢复到原始状态p

p由于目前是隐藏的所以需要enter进入DOM,进入DOM的过程可以详细的进行控制。开始是红色,然后变成黑色,过程持续3s。动画结束后enter被删掉,恢复到原始的白色

CSS 过渡

html
<div id="example-1">
  <button @click="show = !show"> Toggle render </button>
  <transition name="slide-fade"> //滑出
    <p v-if="show">hello</p>
  </transition>
</div>
CSS
/* 可以设置不同的进入和离开动画,设置持续时间和动画函数 */
.slide-fade-enter-active { 
  transition: all 3s ease;//滑出淡入不是线性3s
}
.slide-fade-leave-active { 
  transition: all 1s cubic-bezier(1, 0.5, 0.8, 1);
}
.slide-fade-enter, .slide-fade-leave-to {
//fade-leave-to"淡出的结束状态"就是"淡入开始的状态",fade-enter对应fade-leave-to
  transform: translateX(10px);//淡入那一瞬间的位置
  opacity: 0;
}
JS
new Vue({
    el: '#example-1',
    data: { show: true }
})

Vue动画方式2 - CSS animation

html
<div id="example-2">
  <button @click="show = !show">Toggle show</button>
  <transition name="bounce"> //bounce类的前缀
    <p v-if="show">Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
  </transition>
</div>
CSS
.bounce-enter-active { 
  animation: bounce-in .5s;
}
.bounce-leave-active { 
  animation: bounce-in .5s reverse; //bounce-in
}
@keyframes bounce-in { //bounce-in
  0% {transform: scale(0);}
  50% {transform: scale(1.5);}
  100% {transform: scale(1);} //结束时恢复正常状态
}
JS
new Vue({
  el: '#example-2',
  data: { show: true }
})

keyframes语法

@keyframes spin { 
  0% {opacity: 0;}
  100% {opacity: 1;}
}
使用
.fade-enter-active{
  animation:spin 1s reverse;
}

类名

可以通过以下 attribute 来自定义过渡类名:
enter-class、enter-active-class、enter-to-class、
leave-class、leave-active-class、leave-to-class

也可以结合第三方CSS动画库 Animate.css 提供了很多动画效果。

Animate.css

bootCDN选择animate.min.css示例

html
<link href="https://cdn.bootcdn.net/ajax/libs/animate.css/3.5.2/animate.min.css" rel="stylesheet">
<div id="example-3">
  <button @click="show = !show">
    Toggle render
  </button>
  <transition
    enter-active-class="animated tada" 
  //animated后接的就是动画效果,格式animated xxx
    leave-active-class="animated bounce" 
  >
    <p v-if="show">hello</p>
  </transition>
</div>
JS
new Vue({
  el: '#example-3',
  data: {show: true}
})
CSS 不需要写CSS

同时使用过渡和动画

在一些场景中,你需要给同一个元素同时设置两种过渡动效,比如 animation很快的被触发并完成了,而transition效果还没结束。在这种情况中,你就需要使用type属性并设置animationtransition来明确声明你需要Vue监听的类型。

显性的过渡持续时间

有的时候需要手动设置淡入淡出时间

<transition :duration="1000">...</transition> //:duration="1000"
复制代码

JavaScript 钩子

文档 JS钩子

可以在attribute中声明JS钩子,你可以用这些钩子知道当前动画处在哪个阶段。

Vue动画方式3 - JS 操作动画

velocity是一个非常著名的用JS操作动画的库,推荐用这个做动画。

bootCDN选择velocity.min.js,最好用版本1.2.3的。

<script src="https://cdnjs.cloudflare.com/ajax/libs/velocity/1.2.3/velocity.min.js"></script>
<div id="example-4">
  <button @click="show = !show">
    Toggle
  </button>
  <transition
    v-on:before-enter="beforeEnter"
    v-on:enter="enter"
    v-on:leave="leave"
    v-bind:css="false"
  >
    <p v-if="show">
      Demo
    </p>
  </transition>
</div>
JS
new Vue({
  el: '#example-4',
  data: {
    show: false
  },
  methods: {
    beforeEnter: function (el) {
      el.style.opacity = 0
      el.style.transformOrigin = 'left'
    },
    enter: function (el, done) {
      Velocity(el, { opacity: 1, fontSize: '1.4em' }, { duration: 300 })
      Velocity(el, { fontSize: '1em' }, { complete: done })
    },
    leave: function (el, done) {
      Velocity(el, { translateX: '15px', rotateZ: '50deg' }, { duration: 600 })
      Velocity(el, { rotateZ: '100deg' }, { loop: 2 })
      Velocity(el, {
        rotateZ: '45deg',
        translateY: '30px',
        translateX: '30px',
        opacity: 0
      }, { complete: done })
    }
  }
})

Vue动画方式4 - 多元素动画

一.多个元素的过渡文档

示例1:淡出淡入
<div id="example-4">
  <transition name="fade" mode="out-in">
    <button key="on" v-if="status==='off'" @click="status='on'">on</button>
    <button key="off" v-else @click="status='off'">off</button>
  </transition>
</div>
JS
new Vue({
    el: '#example-4',
    data: { status: 'on'},
})    
CSS
.fade-enter-active,.fade-leave-active {
  transition: all 1s;
}
.fade-enter {
  opacity: 0;
}
.fade-leave-to {
  opacity: 0;
}
示例2:先进再出
<transition name="fade" mode="in-out">
CSS
.fade-enter-active,.fade-leave-active {
  transition: all 1s;
}
.fade-enter {
  opacity: 0;
  transform:translateX(100px)
}
.fade-leave-to {
  opacity: 0;
  transform:translateX(-100px)
}
#example-4{
  position:relative;
}
button{
 position:absolute;
}
JS同上略
示例3:实现轮播效果
<transition name="fade">
CSS
#example-4{
  position:relative;
  padding:100px;
}
JS同上略

必须要加key才有动画,不然它不知道你要变什么,它会以为两个button是一个。当两个标签一样的时候一定要加key

同时生效的进入和离开的过渡不能满足要求,所以Vue提供了过渡模式:

out-in:当前元素先进行过渡,完成之后新元素过渡进入。

in-out(用的少):淡出的时候往左移

mode="out-in/in-out"是为了解决2个元素占用位置冲突的问题。

二.多个组件的过渡

就是用<component>绑定is属性

只需要使用动态组件

//实际上就是tab切换
<div id="transition-components-demo">
  <transition name="component-fade" mode="out-in">
    <component v-bind:is="view"></component>
  </transition>
</div>
JS
new Vue({
  el: '#transition-components-demo',
  data: {
    view: 'v-a' //view是啥,就是哪个组件的名字
  },
  components: {
    'v-a': {
      template: '<div>Component A</div>'
    },
    'v-b': {
      template: '<div>Component B</div>'
    }
  }
})
CSS
.component-fade-enter-active, .component-fade-leave-active {
  transition: opacity .3s ease;
}
.component-fade-enter, .component-fade-leave-to {
  opacity: 0;
}

适用场景:如果你有10个组件在一个地方显示,想在切的时候有动画。就用<component :is="view">组件,如果你想让第一个组件出现就让view: 'v-a',view等于第几个组件,就默认显示它。不需要加key。

Vue动画5 - 列表动画(常用)

文档列表过渡

<div id="list-demo" class="demo">
  <button v-on:click="add">Add</button>
  <button v-on:click="remove">Remove</button>
  <transition-group name="list" tag="p"> 
    <span v-for="item in items" v-bind:key="item" class="list-item">
      {{ item }}
    </span>
  </transition-group>
</div>
CSS
.list-item {
  display: inline-block;
  margin-right: 10px;
}
.list-enter-active, .list-leave-active {
  transition: all 1s;
}
.list-enter, .list-leave-to
/* .list-leave-active for below version 2.1.8 */ {
  opacity: 0;
  transform: translateY(30px);
}
JS
new Vue({
  el: '#list-demo',
  data: {
    items: [1,2,3,4,5,6,7,8,9],
    nextNum: 10
  },
  methods: {
    randomIndex: function () {
      return Math.floor(Math.random() * this.items.length)
    },
    add: function () {
      this.items.splice(this.randomIndex(), 0, this.nextNum++)
    },
    remove: function () {
      this.items.splice(this.randomIndex(), 1)
    },
  }
})

知识点

1.name="list"list就是css的前缀

2.<transition-group> 组件

使用场景:要想用v-for实现同时渲染整个列表,这时候就可以使用<transition-group>组件。

该组件特点:

(1)默认为一个<span>,也可以通过 tag attribute更换为其他元素。

<transition-group name="list" tag="p">
  <span v-for="item in items" v-bind:key="item" class="list-item">
    {{ item }}
  </span>
</transition-group>
复制代码

tag='p'tag取值是p,<transition-group>就会将自身替换为p标签。tag取值是什么,<span>就会被什么标签包围。不写tag就默认是<span>

网络异常,图片无法展示
|

(2)过渡模式不可用,因为我们不再相互切换特有的元素。 mode=“out-in"不能用,,<transition-group>不支持mode

(3)内部元素总是需要提供唯一的 key attribute 值。 内部元素<span>必须写keyv-bind:key="item"

(4)CSS 过渡的类将会应用在内部的元素中,而不是这个组/容器本身。

for循环如何做动画?

<transition-group> + tag="div" + v-for循环的模版(span/div)

注意:不能加其它的标签,for循环模版外只能是<transition-group>

总结

网络异常,图片无法展示
|

Previous模板、指令与修饰符

相关文章
|
6月前
|
存储 JavaScript 前端开发
Vue3 详细教程及实例(完整版)
Vue3 详细教程及实例(完整版)
364 0
|
JavaScript 前端开发 Java
Vue系列教程(24)- 阶段总结
Vue系列教程(24)- 阶段总结
54 1
|
JavaScript 前端开发 Java
Vue系列教程(22)- 路由钩子
Vue系列教程(22)- 路由钩子
64 1
|
JavaScript 前端开发 容器
Vue系列教程(19)- 嵌套路由(ElementUI)
Vue系列教程(19)- 嵌套路由(ElementUI)
102 1
|
JavaScript 前端开发
Vue系列教程(17)- 路由(vue-router)
Vue系列教程(17)- 路由(vue-router)
94 1
|
缓存 JavaScript 前端开发
Vue系列教程(13)- 计算属性(computed)
Vue系列教程(13)- 计算属性(computed)
52 0
|
JavaScript 前端开发
Vue系列教程(11)- 组件详解(Vue.component、props)
Vue系列教程(11)- 组件详解(Vue.component、props)
69 0
|
6月前
|
缓存 JavaScript 开发工具
【安装指南】VSCode搭建运行Vue的详细教程
【安装指南】VSCode搭建运行Vue的详细教程
2217 0
|
6月前
|
JavaScript
Vue安装教程
Vue安装教程
76 0
|
机器学习/深度学习 JavaScript 前端开发
Vue系列教程(23)- npm小结
Vue系列教程(23)- npm小结
126 1