④ vue

1 了解VUE

  • Vue.js 是一个基于 MVVM 模式的一套渐进式框架。它是以数据驱动和组件化的思想构建的,采用自底向上增量开发的设计

1.1 前端框架发展史

  1. Jquery(2006):节点操作简单易用,浏览器兼容

  2. Angular(2009):MVC 模式,双向数据绑定,依赖注入

  3. React(2013):高性能(虚拟 DOM)

  4. Vue(2014):综合 angular 与 react 的优点,MVVM 模式,是一款高性能高效率的框架

1.2 架构模式

复杂的软件必须有清晰合理的架构,更容易开发、维护和测试

1.2.1 MVC

MVC 模式的意思是,软件可以分成三个部分

  • 模型 Model:数据处理

  • 视图 View:数据展示

  • 控制器 Controller:业务逻辑处理(M 和 V 之间的连接器)

MVC

  1. View 传送指令到 Controller(用户发送指令)

  2. Controller 完成业务逻辑后,要求 Model 改变状态

  3. Model 将新的数据发送到 View,用户得到反馈

  • 缺点:依赖复杂

    • View 依赖 ControllerModel

    • Controller 依赖 ViewModel

1.2.2 MVP

MVP 架构模式是 MVC 的改良模式(改进Controller, 把Model和View完全隔离开)

  • Model

  • View

  • Presenter 可以理解为松散的控制器,其中包含了视图的 UI 业务逻辑,所有从视图发出的事件,都会通过代理给 Presenter 进行处理;同时,Presenter 也通过视图暴露的接口与其进行通信

MVP

1.2.3 MVVM

MVP 模式演变而来

  • Model 数据处理

  • View 数据展示

  • ViewModel 类似与 MVP 中的 Presenter,唯一的区别是,它采用 双向绑定View 的变动,自动反映在 ViewModel,反之亦然

MVVM

  • 核心思想:关注 Model 的变化,让 MVVM 框架利用自己的机制去自动更新 DOM,从而把开发者从操作 DOM 的繁琐中解脱出来!

1.3 学习 Vue 需要改变关注点

  • jquery 和原生 js 的关注点:节点

  • Vue 中的关注点:数据

2 使用VUE

2.1 实例化 new Vue()

  var data = { name: 'zhoutest' }
  var vm = new Vue({
    el: '#app',
    data: data
  });

2.2 常用配置选项

2.2.1 关于 DOM 节点

el(类型:Selector|Element)

Vue 实例的挂载目标(实例中所有的属性/方法可直接在 el 中直接使用),挂载元素会被 Vue 生成的 DOM 替换

template(类型:String)

模板,如果不指定则以 ele 所在元素作为模板

  • Selector:提取内容到 template 标签,并指定选择器

render(类型:Function)

template 的代替方案,允许你发挥 js 最大的编程能力。该渲染函数接收一个 createElement(tagName,props,children) 方法作为第一个参数用来创建 VNode

  • 优先级:render > template > el.outerHTML
  new Vue({
    //...
    el:'#app',
    template:`<div>{{username}}</div>`,
    render:createElement=>{
      return createElement('h1',{title:'标题',class:'title'},'文章标题')
    }
  })

2.2.2 关于数据

data(类型:Object|Function)

Vue 实例化时,它将 data 中所有的属性添加到 响应式系统 中,当这些数据改变时,视图会进行重渲染

  • data 下的属性会自动成为 vm 下的属性:Vue 在实例化时,会遍历 data 下所有属性,把他们变成响应式属性

computed(类型:Object)

对于需要复杂逻辑或运算才能得到的值,应当使用计算属性

methods(类型:Object)

一般用于编写公共方法、事件处理函数等,方法中的 this 指向实例,所以不应该使用箭头函数来定义 method 函数

watch(Object)

监听属性(Function),监听的值被修改时会自动调用函数,当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的

  watch: {
    username: function (val, oldVal) {
      console.log('new: %s, old: %s', val, oldVal)
    }
  }

2.3 实例属性&方法

Vue 实例化时,会遍历 data/computed/methods 中所有属性/方法,并写入Vue的实例

2.3.1 属性特性

1. 值属性(有自己的值的属性)

  • configurable 可配置性(属性特性总开关)

  • enumerable 可枚举性(是否可遍历)

  • writable 可写性

  • value

2. 存储器属性(本身没有值,一般用于代理其他数据)

  • configurable 可配置性(属性特性总开关)

  • enumerable 可枚举性(是否可遍历)

  • get 监听读取操作

  • set 监听写入操作

传统方式设置的属性,所有的属性特性默认为 true;而通过 Object.defineProperty() 设置的属性特性默认为 false

3. 设置属性特性

  • Object.defineProperty(obj, key, descriptor)

  • Object.defineProperties(obj, descriptors)

  Object.defineProperties(user,{
    age:{
      configurable:true
      value:18
    },
    password:{}
  })

4. 获取属性特性

  • Object.getOwnPropertyDescriptor(obj, key)

  • Object.getOwnPropertyDescriptors()

2.3.2 响应式属性

  • Vue 在实例化时,会自动遍历 data 下的所有属性,并通过 Object.defineProperty() 把他们变成存储器属性,并写入 Vue 的实例

  • 特点:对属性的修改 UI 会自动更新

  • 原理:getter & setter

当这些属性值发生改变时,视图将会产生“响应”,及匹配更新为新的值

  let data = {
    username:'zhoutest',
    age:18,
    password:123456
  }
  let vm = new Vue({
    el:'#app'
    data
  })
  1. new Vue 开始,首先通过 get、set 监听 Data 中的数据变化,同时创建 Dep 用来搜集使用该 DataWatcher

  2. 编译模板,创建 Watcher,并将 Dep.target 标识为当前 Watcher

  3. 编译模板时,如果使用到了 Data 中的数据,就会触发 Dataget 方法,同时调用 Dep.addSubWatcher 搜集起来

  4. 数据更新时,会触发 Dataset 方法,然后调用 Dep.notify 通知所有使用到该 Data Watcher 去更新 DOM

设置响应式属性

  • 设置初始化数据 data

  • Vue.set(target,key,val) 向__响应式系统__中的对象添加属性并自动渲染视图

    注意:target 对象不能是 Vue 实例,或者 Vue 实例的根数据对象

  • 数组变异方法

2.3.3 内置属性

除了数据属性,Vue 实例还提供了一些有用的实例属性与方法。它们都有前缀$,以便与用户定义的属性区分开来

  • $data: 同 data

  • $el: 同 el节点

  • $refs

  • $parent

  • $children

  • $root

2.3.4 内置方法

  • 数据 data

    • $watch():监听数据变化,同 watch 配置选项

    • $set():Vue.set() 的别名

  • 事件 event

    • $on():监听当前实例上的自定义事件

    • $off():移除自定义事件监听器

    • $emit():触发当前实例上的事件

  • 生命周期函数

2.4 指令 directive

  • 指令是带有 v-* 前缀的特殊属性,格式:v-指令名:参数.修饰符="值"

2.4.1 内置指令

1. 数据绑定

单向数据绑定
  • {{}}:插值表达式

    差值表达式中应当放置一些简单的运算(data 中的数据、函数执行、三元运算等),对于任何复杂逻辑,你都应当使用计算属性操作完成后再写入插值表达式

  • v-text:显示文本

  • v-html:显示 html 内容

  • v-bind

  1. 可绑定任意属性
  <img v-bind:src="imgurl">
  <!-- 简写  -->
  <img :src="imgurl">
  1. 对 style 与 class 的绑定

在将 v-bind 用于 class 和 style 时,Vue 做了专门的增强。表达式结果的类型除了字符串之外,还可以是对象或数组

<div class="static"
   v-bind:class="{ active: isActive, 'text-danger': hasError }"
   v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }">
</div>
<script>
   new Vue({
     data: {
       isActive: true,
       hasError: false,
       activeColor: 'red',
       fontSize: 30
     }
   })
</script>
<!-- 最终结果:<div class="static active" style="color:red;font-size:30px"></div> -->
  1. v-bind 无参数绑定对象
 <div v-bind="{username:'laoxie',age:18,gender:'男'}"></div>
 <!-- 等效于 -->
 <div v-bind:username="laoxie" v-bind:age="18" v-bind:gender="男"></div>
v-model 双向数据绑定

v-model 一般用于表单元素,会忽略所有表单元素的 value、checked、selected 特性的初始值而总是将 Vue 实例的数据作为数据来源

  • v-model值绑定到 value 属性

    • 单行文本框 text

    • 多行文本框 textarea

    • 单选框 radio

    • 选择框 select (无 value 属性则与内容绑定)

    • 复选框 checkbox

      • 初始值为数组,与 value 属性绑定

      • 初始值为其他,与 checked 属性绑定(true,false)

      • true-value:设置选中时的值

      • false-value:设置补选中时的值

  • 修饰符

    • lazy:input 触发改成 change 触发

    • number:输出值为 number 类型

    • trim:自动清楚前后空格

  • 双向数据绑定原理

    • Model -> View:响应式属性

    • View -> Model:事件

  • v-model的原理(替代方案)

    • v-bind:value="val"

    • v-on:input="val=$event.target.value"

      组件中使用 v-model 等效于:v-on:input="val=arguments[0]"

列表渲染
  • v-for

    可遍历 Array | Object | number | string | Iterable

    • 遍历数组
      <li v-for="(value, index) in arr">{{value}}</li>
    
    • 遍历对象
      <tr v-for="(value, key, index) in obj">
        <td>{{index+1}}</td>
        <td>{{key}}-{{value}}</td>
      </tr>
    
    • key:Vue 识别 DOM 节点的一个通用机制(用于 diff 算法)

      • Vue 对相同的元素进行展示排序等操作时,遵循“就地复用”原则,因为这样更加高效,性能更好

      • 但对于依赖子组件状态或临时 DOM 状态 (如:表单输入值、复选框选中等)的列表,会出现操作混乱的问题

      • 指定 key 属性后,意为去掉“就地复用”特性(建议尽可能在使用 v-for 时提供 key)

2. 显示隐藏

  • v-show(频繁显示隐藏)

    通过 display 属性控制元素的显示隐藏

  • v-if | v-else | v-else-if(不频繁的显示隐藏)

    通过创建/移除的方式控制元素的显示隐藏

影响页面性能几大因素
  • 节点的频繁操作

    • 判断一段 js 代码所花的时间 console.time(name); | console.timeEnd(name);
  • 事件绑定数量

Virtual DOM

一个结构类似与真实 DOM 节点的 js 对象

  • 优化方式

    • 优化节点操作

    • 优化事件处理

  • 虚拟 DOM 是怎么优化性能的:背后有一套强大的算法:diff算法

    • 只改变对象初始 | 结束状态不同部分

    • diff 算法如何比较同级同类标签的不同 -- 复用原则

  • key:唯一且稳定

  // 虚拟DOM大概样子
  {
    type:'div',
    attrs:{},
    children:[{
      type:'h1',
      children:'2021'
    },{
      type:'ul',
      children:[{
        type:'li',
        children:'1111'
      }.{
        type:'li',
        children:'2222'
      },{
        type:'li',
        children:'3333'
      }]
    }]
  }

3. 事件绑定

  • 格式:v-on:事件类型.修饰符="事件处理函数"

event 对象的获取:在事件触发时自动写入 vm.$event

事件修饰符
  • stop

  • prevent

  • capture

  • self:只当在 event.target 是当前元素自身时触发处理函数(e.target === e.currentTarget)

  • once 事件将只会触发一次

  • 按键修饰符

    1. 直接使用键码来作为按键修饰符
    <!-- 只有在 `keyCode` 是 13 时调用 `vm.submit()` -->
    <input v-on:keyup.13="submit">
    
    1. 使用别名作为按键修饰符
    • left up right down
    • enter
    • tab
    • esc
    • space

2.4.2 自定义指令

1. 全局指令

  • 格式:Vue.directive(name, option)

  • 参数

    • name:指令名字,使用格式:v-name

    • option

      • Object:放钩子函数

      • Function:默认为bind和update的钩子函数

2. 局部指令

  • 格式:directives: {}
  // 使用指令:v-zhoutest
  Vue.directive('zhoutest', {
    bind: function (el, binding, vnode) {
    //binding参数如下
    el.innerHTML =
      'name: ' + JSON.stringify(binding.name) + '<br>' + //指令名
      'value: ' + JSON.stringify(binding.value) + '<br>' + //指令值
      'expression: ' + JSON.stringify(binding.expression) + '<br>' + //字符串形式的指令表达式
      'arg: ' + JSON.stringify(binding.arg) + '<br>' + //指令参数,
      'modifiers: ' + JSON.stringify(binding.modifiers) + '<br>' //指令修饰符
    }
  });

2.5 生命周期函数

beforeCreate()

  • 初始化完成,但为往实例添加属性

  • 应用:可以在这加个loading事件

created()

  • 应用:在这结束loading,还做一些初始化,实现函数自执行

beforeMount()

  • 可以获取节点,但数据未渲染

  • 应用:在这发起ajax请求,拿回数据,配合路由钩子做一些事情

mounted()

实例挂载到 DOM

  • 应用:节点操作

beforeUpdate()

  • 数据有更新但未更新节点

updated()

  • 更新节点完毕

beforeDestroy()

destroyed()

执行destroy()后,不会改变已生成的DOM节点,但后续就不再受vue控制了

  • 应用:清除定时器、延迟器、取消ajax请求等

2.6 过滤器

Vue 允许你自定义过滤器,可被用于一些常见的文本格式化

  • 过滤器可以用在两个地方:双花括号插值v-bind
  <!-- 在双花括号中 -->
  {{ message | capitalize }}

  <!-- 在 `v-bind` 中 -->
  <div v-bind:id="rawId | formatId"></div>

全局过滤器

格式:Vue.filter(name, definition)

局部过滤器

格式: filters 属性

  // 首字母大写
  Vue.filter('capitalize', function (value) {
    if (!value) return ''
    value = value.toString()
    return value.charAt(0).toUpperCase() + value.slice(1)
  })

2.7 mixin 混入

混入一般用于组件选项的复用,并以一定的合并规则混入到组件中

全局 mixin:Vue.mixin(options)

全局注册一个混入,会影响后面所有创建的每个 Vue 实例/组件(影响较大,一般用于插件编写)

  Vue.mixin({
    created: function () {
      // created生命周期函数会混入到下面的Vue实例中,且不会影响原来的选项
      console.log('global mixin:', this.username)
    }
  });

  new Vue({
    data:{
      username:'zhoutest'
    },
    created(){
      console.log('app.username', this.username)
    }
  });

局部 mixins:mixins:[mymixin]

一般用于提取多个组件的公有部分配置

  var mixin = {
  data: function () {
    return {
      message: 'hello',
      foo: 'abc'
    }
  }
}

new Vue({
  mixins: [mixin],
  data: function () {
    return {
      message: 'goodbye',
      bar: 'def'
    }
  },
  created: function () {
    console.log(this.$data);// => { message: "goodbye", foo: "abc", bar: "def" }
  }
})

2.8 开发插件

插件可以是一个对象(必须提供 install 方法),也可以是一个函数,它会被作为 install 方法,并把 Vue 作为参数传入

2.8.1 插件类型

  • 添加全局方法或者属性,如: vue-custom-element

  • 添加全局资源:指令/过滤器/过渡等,如 vue-touch

  • 通过全局 mixin 方法添加一些组件选项,如: vue-router

  • 添加 Vue 实例方法,通过把它们添加到 Vue.prototype 上实现。

  • 一个库,提供自己的 API,同时提供上面提到的一个或多个功能,如 vue-router

  MyPlugin.install = function(Vue, options) {
    // 1. 添加全局方法或属性
    Vue.myGlobalMethod = function() {
      // 逻辑...
    }

    // 2. 添加全局资源
    Vue.directive('my-directive', {
      bind (el, binding, vnode, oldVnode) {
        // 逻辑...
      }
      ...
    })

    // 3. 注入组件(影响后面定义的所有组件)
    Vue.mixin({
      created: function () {
        // 逻辑...
      }
      ...
    })
    Vue.component('mycomponent',{
      // 继承mixin中的created等配置
    })

    // 4. 添加实例方法
    Vue.prototype.$myMethod = function (methodOptions) {
      // 逻辑...
    }
  }

2.8.2 使用

通过全局方法 Vue.use() 使用插件。它需要在你调用 new Vue() 启动应用之前完成

  Vue.use(MyPlugin);//会自动调用MyPlugin中的install方法

  new Vue({
    //... options
  })

3 组件 Component

  • 优点:代码复用 & 便于维护

3.1 组件定义和使用

组件是可复用的 Vue 实例,带有一个名字,所以它们与 new Vue 接收相同的选项,例如 data、computed、watch、methods 以及生命周期钩子等(el 选项除外)

3.1.1 组件要求

  • data 必须为 Function 类型

  • 每个组件必须只有一个根元素,否则报错

  • 注册时组件名可以是 kebab-casePascalCase ,但在html页面上使用时,必须写成遵循W3C 规范中的自定义组件名 (字母全小写且必须包含一个连字符)

3.1.2 定义

1. 全局组件

  • Vue.component(name, options),所有的 Vue 实例都可以使用

  • 类似于 new Vue(options)

  Vue.component('my-component', {
    // ... options ...
    template:'<p>我是全局组件</p>'
  })

2. 局部组件

  • 在某个Vue实例中通过 components 属性注册的组件为局部组件,只有当前实例能使用
  var Child = {
    data() {
      return {
        name: '我是局部组件'
      }
    },
    template: '<h1>hello, {{name}}</h1>'
  }
   
  // 创建根实例
  new Vue({
    el: '#app',
    components: {
      Child
    }
  });

3.1.3 使用

  • 使用组件时,组件 template 中的内容会替换调组件所在位置
  <div id="app">
    <my-component></my-component>
    <child></child>
  </div>
  • 注意:由于 Vue 只有在浏览器解析和标准化 HTML 后才能获取模板内容,所以把组件嵌套在某些特定的元素(如 table, ul, ol, select 等)上时,会导致解析错误
  <!-- table为已经存在html页面上的元素 -->
  <table>
    <my-row></my-row>
  </table>
  Vue.component('my-row',{
    template:'<tr><td>test</td></tr>'
  });
  • 以上解析的结果为,tr 被解析到了 table 外面,解决方式也很简单,利用特殊的 is 属性实现
  <table>
    <tr is="my-row"></tr>
  </table>

3.2 组件通讯

原则:谁的数据谁修改

3.2.1 父组件 -> 子组件:props

  • 组件实例的作用域是孤立的

  • 要让子组件使用父组件的数据,需要通过子组件的 props 选项

1. 步骤

  1. 在子组件上定义属性,并传递数据

  2. 在子组件中通过 props 配置参数接收数据

接收到的数据会自动称为子组件的属性

2. props 声明属性

  • 声明的属性会自动成为组件实例的属性(可通过 this.xx 访问)

  • prop 传递是单向的,当父组件的属性变化时,将传导给子组件,但是不会反过来

  <blog-post mytitle="静态数据"></blog-post>
  Vue.component('blog-post', {
    props: ['mytitle'],
    template: '<h3>{{ mytitle }}</h3>'
  })
 <blog-post :msg="message"></blog-post>
  <!-- 传入一个对象 -->
 <blog-post v-bind:author="{ name: 'zhoutest', age:18 }"></blog-post>
  Vue.component('blog-post', {
    props: ['msg','author'],
    template: '<h3>{{ msg }}</h3>'
  });

  let vm = new Vue({
    data:{
      message:'hello zhoutest'
    }
  })

3. 非 props 属性

  • 此类属性会自动成为组件根节点的属性(可通过 {inheritAttrs: false} 关闭)

4. prop 数据验证

对传入的 prop 属性进行校验,如:数据类型、必填、默认值等

  Vue.component('my-component', {
    props: {
      // 基础的类型检查 (`null` 匹配任何类型)
      propA: Number,
      // 多个可能的类型
      propB: [ String, Number ],
      // 必填的字符串
      propC: {
        type: String,
        required: true
      },
      // 带有默认值的数字,无prop属性传入时,默认得到100
      propD: {
        type: Number,
        default: 100
      },
      // 带有默认值的对象
      propE: {
        type: Object,
        // 对象或数组默认值必须从一个工厂函数获取
        default: function () {
          return { message: 'hello' }
        }
      },
      // 自定义验证函数
      myscore: {
        validator: function (value) {
          // 这个值必须大于等于60,否则报错
          return val >= 60
        }
      }
    }
  })

3.2.2 子组件 -> 父组件:自定义事件 + $emit

  • Vue 遵循 单向数据流 原则,不允许在子组件中直接修改 props 传入的父组件数据,可以通过自定义事件系统,利用 $emit() 方法触发父组件函数来达到修改的效果

1. 方法①

  1. 在子组件上定义一个事件 v-on:additem

  2. 在子组件内部触发这个自定义事件:$emit()

2. 方法②

  1. 可以利用 v-bind:xx.sync 修饰符(如下color属性)

  2. 子组件调用 this.$emit('update:xx',val) 触发更新

  <div id="app">
    <p :style="{fontSize:fontSize+'px'}">字体大小:{{fontSize}}</p>

    <btn-change :font-size="fontSize" @bigger="updateFontSize" :color.sync="color"></btn-change>
  </div>

  <template id="myButton">
    <button @click="changeSize">改变字体大小</button>
    <button @click="changeColor">改变字体颜色</button>
  </template>

  <script>
    new Vue({
      el:'#app',
      data:{
          fontSize:16,
          color:'red'
      },
      components:{
        btnChange:{
          props:['fontSize'],
          template:'#myButton',
          methods:{
            changeSize(){
              this.initFontSize++;
              // 手动触发自定义事件并传递修改后的值
              this.$emit('bigger',this.fontSize+1);
            },
            changeColor(){
              this.$emit('update:color','#58bc58');
            }
          }
        }
      },
      methods:{
        updateFontSize(val){
          // 触发自定义事件的事件处理函数,并修改字体大小
          this.fontSize = val;
        }
      }
    })
  </script>

3.2.3 兄弟组件通信

  • 组件A -> 父组件 -> 组件B

3.2.4 多层级组件通讯

  • 利用一个 Vue 实例作为中间桥梁实现传参

事件总线 Bus

  1. 自定义事件

接收方自定义事件 $on()

  1. 触发自定义事件

发送方触发事件 $emit()

    // 定义中间桥梁bus
    let bus = new Vue();

    //组件A
    let comA = {
        data(){
            return {
              msg:'I am A'
            }
        },
        template:`<div>
            <p>{{msg}}</p>
            <button @click="send">传数据到B组件</button>
        </div>`,
        methods:{
            send(){
                bus.$emit('data',this.msg);
            }
        }
    }

    // 组件B
    let comB = {
        data:()=>({
            msg:'I am B'
        }),
        mounted(){
            bus.$on('data',val=>this.msg = val)
        },
        template:`<div><p>{{this.msg}}</p></div>`
    }

    // 创建实例,并注册子组件
    new Vue({
        el:'#app',
        components:{
            comA,
            comB
        }
    });

provide + inject

  1. 爷:定义 provide 对象传递

  2. 孙:使用 inject 接收

3.3 内置组件

<component> 动态组件

  • is:指定渲染的组件
  <component v-bind:is="currentTabComponent"></component>

<keep-alive> 缓存组件

把切换出去的组件保留在内存中,可以保留它的状态或避免重新渲染可以添加一个 keep-alive

包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们,主要用于保留组件状态或避免重新渲染

  • include(String/Regexp):指定缓存组件名

  • exclude(String/Regexp):指定不缓存的组件名

  <keep-alive>
     <component v-bind:is="currentTabComponent"></component>
  </keep-alive>

<slot> 内容分发

3.4 插槽

  • 内容通讯:插槽 slot

3.4.1 利用组件内容进行通讯(组件外 -> 组件内)

  • 在组件模板中利用内置组件 <slot></slot> 来承载组件内容,否则它的内容都会被忽略

默认插槽:<slot>

  <!-- 使用组件 -->
  <nav-link url="/home">首页</nav-link>

  <!-- 定义组件 -->
  <script>
    Vue.component('nav-link',{
      props:['url']
      template:`<a :href="url"><slot></slot><span>Home</span></a>`
    });
  </script>

具名插槽:<slot name="xx">

  • 模板内容:给<slot/>组件命名(设置name属性)

  • 组件内容:设置 slot 属性 v-slot:name,实现内容精准显示到模板具体位置

  <!-- 组件模板内容 -->
  <template id="myTest">
    <div>
      <slot name="header">这是拥有命名的slot的默认内容</slot>
      <slot>这是拥有命名的slot的默认内容</slot>
      <slot name="footer">这是拥有命名的slot的默认内容</slot>
    </div>
  </template>

  <!-- 使用组件 -->
  <my-component>
    <template v-slot:header>这里的内容显示到name为header的插槽</template>
    <span>这里的内容显示到默认插槽</span>
    <template v-slot:footer>这里的内容显示到name为footer的插槽</template>
  </my-component>

3.4.2 作用域插槽(组件内 -> 组件外)

  1. 把需要传递的参数写入 slot 属性

  2. v-slot="scope" (scope为写入solt的所有属性组成的对象)

  <!-- mynav组件模板 -->
  <div class="box">
    <slot :msg="msg" :username="username">{{username}}, {{msg}}</slot>
    <slot name="footer" title="播放器" :player="player">{{player}}</slot>
  </div>

  <!-- 组件内容 -->
  <mynav>
    <!-- props为传过来的数据组成的对象 -->
    <div slot-scope="props">{{props.msg}}, {{props.username}}</div>

    <!-- Vue2.6+用法 -->
    <div v-slot:default="props">{{props.msg}}, {{props.username}}</div>
    <div v-slot:footer="foot">{{foot.title}}, {{foot.player}}</div>
  </mynav>

4 模块系统

4.1 前言

常规的定义组件的方法在复杂的项目中存在以下缺点

  • 全局定义 强制要求每个 component 中的命名不得重复

  • 字符串模板 缺乏语法高亮,在 HTML 有多行的时候,需要用到丑陋的 \

  • 不支持 CSS 意味着当 HTML 和 JavaScript 组件化时,CSS 明显被遗漏

  • 没有构建步骤 限制只能使用 HTML 和 ES5 JavaScript, 而不能使用预处理器,如 Pug (formerly Jade) 和 Babel

4.2 vue 单文件组件

  • Vue 单文件组件(扩展名为 .vue),由于浏览器不支持 .vue 文件, 和ES6的模块化( import , export )开发, 必须利用 babelwebpack 工具来辅助实现编译成浏览器支持的格式

  • vue单文件优点

    • 完整语法高亮

    • CommonJS 模块

    • 组件作用域的 CSS

4.3 vue 单文件组件开发流程

webpack打包

4.3.1 安装必要模块

  "devDependencies": {
    "html-webpack-plugin": "^3.2.0",
    "vue": "^2.5.17",
    "vue-loader": "^15.4.2",
    "vue-template-compiler": "^2.5.17",
    "webpack": "^4.18.0",
    "webpack-cli": "^3.2.3"
  }

4.3.2 设置 webpack 配置文件(webpack.config.js)

4.3.3 应用入口文件(app.js)

  //ES6 引入其他模块(可以是js,css,vue单文件等)
  import Vue from 'vue';
  import App from './app.vue';

  new Vue({
    el:'#app',

    // render 函数若存在,则忽略 template 或 el 元素中的内容
    render(create){
      return create(App);
    }
  });

4.3.4 单文件组件(app.vue)

  <template>
    <div class="container">
      <h1>Hello {{name}}</h1>
      <button class="btn btn-success">点击进入</button>
    </div>
  </template>

  <script>
    // 导出当前组件配置选项
    export default{
      data(){
        return{
          name:'Vue单文件组件'
        }
      }
    }
  </script>
  
  <!-- 设置具有组件作用域的样式 -->
  <style scoped>
    h1{color:#58bc58;}
  </style>

4.4 ES Module

浏览器和服务器通用的模块解决方案,完全可以取代 CommonJSAMD 规范

4.4.1 基本特点

  • 每一个模块只加载一次, 并执行一次,再次加载同一文件,直接从内存中读取

  • 每一个模块内声明的变量都是局部变量, 不会污染全局作用域

  • 通过 export 导出模块,通过 import 导入模块

  • ES6模块只支持静态导入和导出,只可以在模块的最外层作用域使用 importexport

4.4.2 export

  • export 命令用于规定模块的对外接口,只允许导出最外层函数、类以及 var、let 或 const 声明的变量,可多次 export,export 出去后自动成为 模块对象的属性

export 后只能跟 functionclassvarletconstdefault{}

1. 基本用法

  //base.js
  var myName = 'laoxie';
  var age = 1995;

  // 多次export
  export { myName };
  export let gender = "男";
  export function show() { 
    console.log(666); 
  }

2. as

  • 通常情况下,export 输出的变量就是本来的名字,但是可以使用 as 关键字重命名
  function show() {
    console.log('my name is show');
  }
  
  export { show as showName };

3. default

  • 为模块指定默认输出,这样就可以在使用 import 指令的时候,不必知道所要加载的变量名或函数名
  export default {
    data:{
      path:'/src/'
    }
  }

4. * 作为中转模块导出,把某个模块的所有相属性/方法导出

  export * from './md.js';

4.4.3 import

  • import 命令用于导入其他模块提供的功能

  • 格式:import <module> from <url>

1. url 支持格式

  // 支持
  import base from 'http://laoxie.com/js/base.js';
  import base from '/js/base.js';
  import base from './base.js';
  import base from '../base.js';

  // 不支持
  import base from 'base.js';
  import base from 'js/base.js';

2. 基本用法

  //从模块对象中导入属性为default的值,并赋值给变量res,无则得到undefined
  import res from './base.js';

  //导入模块对象中属性为myName的值并赋值给变量myName
  import { myName } from './base.js';

3. as 修改变量名

  //导入模块对象中属性为myName的值并赋值给变量username
  import { myName as username } from './base.js';

4. * 导入整个模块对象

  //导入整个模块对象,并赋值给myModule变量
  import * as myModule from './base.js';

4.5 在 html 中使用 ES Module

浏览器支持 ES Module

<script> 标签中指定 type="module"

    <script type="module">
        import res from './base.js';
        console.log(res)
    </script>

    <script type="module" src="js/base.js"></script>

浏览器不支持 ES Module

利用 webpack 等工具转换成ES5后引入(推荐)

5 过渡动画

  • <transition>
  • <transition-group>

<transition>用于单个元素动画,<transition-group>用于多个元素并解析为一个标签(默认:span)

5.1 属性

  • name: 过渡类名前缀(默认:v)

    如设置 name="fade",过渡类名变成:fade-enter / fade-enter-active /fade-leave / fade-leave-active

  • css: boolean,是否使用 CSS 过渡类(默认:true)。设置为 false,将只通过组件事件触发注册的 JavaScript 钩子。

自定义过渡类名(可配合 animate.css 框架实现过渡效果)

  • enter-class

  • enter-active-class

  • enter-to-class

  • leave-class

  • leave-active-class

  • leave-to-class

  <transition
    enter-active-class="bounceIn"
    leave-active-class="bounceOut"
  >
  </transition>

5.2 触发动画场景

前提条件(缺一不可)

  1. 状态改变 --> 通过样式 | js改变

  2. 使用 <transition> | <transition-group> 单元素 | 单组件

  3. 动画元素必须有进入状态、离开状态

如何让组件发生进入状态、离开状态?

  • 条件渲染 (使用 v-if)

  • 条件展示 (使用 v-show)

  • 动态组件

  • 组件根节点

5.3 CSS 过渡

通过CSS过渡类名

  • v-enter:进入过渡的开始状态,元素被插入时生效,只应用一帧后立即删除

  • v-enter-active:进入过渡的结束状态,元素被插入时就生效,在过渡过程完成之后移除

  • v-leave:离开过渡的开始状态,元素被删除时触发,只应用一帧后立即删除

  • v-leave-active:离开过渡的结束状态,元素被删除时生效,离开过渡完成之后被删除

5.4 js 过渡

通过内置事件实现过渡动画效果,可以利用第三方动画库(如:velocity.js,jquery等)实现动画效果

  <transition
    v-on:before-enter="beforeEnter"
    v-on:enter="enter"
    v-on:after-enter="afterEnter"
    v-on:enter-cancelled="enterCancelled"
    v-on:before-leave="beforeLeave"
    v-on:leave="leave"
    v-on:after-leave="afterLeave"
    v-on:leave-cancelled="leaveCancelled"
  >
  </transition>
  methods: {
    // 过渡进入
    // 设置过渡进入之前的组件状态
    beforeEnter: function (el) {
      // ...
    },
    // 设置过渡进入完成时的组件状态
    enter: function (el, done) {
      // ...
      done()
    },
    // 设置过渡进入完成之后的组件状态
    afterEnter: function (el) {
      // ...
    },
    enterCancelled: function (el) {
      // ...
    },
    // 过渡离开
    // 设置过渡离开之前的组件状态
    beforeLeave: function (el) {
      // ...
    },
    // 设置过渡离开完成时地组件状态
    leave: function (el, done) {
      // ...
      done()
    },
    // 设置过渡离开完成之后的组件状态
    afterLeave: function (el) {
      // ...
    },
    // leaveCancelled 只用于 v-show 中
    leaveCancelled: function (el) {
      // ...
    }
  }
posted on 2021-07-14 11:28  pleaseAnswer  阅读(33)  评论(0编辑  收藏  举报