④ vue

目录

1 MongoDB在NodeJS中操作

  • 驱动:mongodb mongoose

1.1 document的操作

1.1.1 增

  • insertOne(Object)

  • insertMany(Array)

1.1.2 删

  • deleteOne(query)

  • deleteMany(query)

1.1.3 改

  • updateOne(query,{$set:data})

  • updateMany(query,{$set:data})

  • save(document)

1.1.4 查

  • find(query)

  • findOne(query)

1.1.5 条件与筛选

1.2 封装(重点)

  • async & await

2 了解VUE

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

2.1 前端框架发展史

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

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

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

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

2.2 架构模式

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

2.2.1 MVC

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

  • 模型Model:数据处理

  • 视图View:数据展示

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

MVC

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

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

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

  • 缺点:依赖复杂
    • View 依赖 Controller 和 Model
    • Controller 依赖 View 和 Model

2.2.2 MVP

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

  • Model

  • View

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

MVP

2.2.3 MVVM

MVP 模式演变而来

  • Model

  • View

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

MVVM

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

2.3 学习 Vue 需要改变关注点

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

  • Vue中的关注点:数据

3 使用VUE

3.1 实例化new Vue()

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

3.2 常用配置选项

3.2.1 关于DOM节点

el(类型:Selector|Element)

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

template(类型:String)

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

  • Selector:提取内容到template标签,并指定选择器
render(类型:Function)

template 的代替方案,允许你发挥 JavaScript 最大的编程能力。该渲染函数接收一个 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'},'文章标题')
    }
  })

3.2.2 关于数据

data(类型:Object|Function)

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

computed(类型:Object)

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

methods(类型:Object)

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

watch(Object)

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

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

3.3 实例属性&方法

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

3.3.1 属性特性

1. 值属性(有自己的值的属性)
  • configurable 可配置性(属性特性总开关)

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

  • writable 可写性

  • value

2. 存储器属性(本身没有值,一般用于代理其他数据)
  • configurable 可配置性(属性特性总开关)

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

  • get 监听读取操作

  • set 监听写入操作

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()

3.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
    })
设置响应式属性
  • 设置初始化数据 data

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

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

  • 数组变异方法

3.3.3 内置属性

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

  • $data: 同 data

  • $el: 同 el节点

  • $refs

  • $parent

  • $children

  • $root

3.3.4 内置方法

  • 数据data

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

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

  • 事件event

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

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

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

  • 生命周期函数

3.4 指令 directive

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

3.4.1 内置指令

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

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

  • v-text:显示文本

  • v-html:显示html内容

  • v-bind

  1. 可绑定任意属性

       <img v-bind:src="imgurl">
       <!-- 简写  -->
       <img :src="imgurl">
    
  2. 对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> -->
    
  3. 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(不频繁的显示隐藏)

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

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

  • 事件绑定数量

  • ....

Virtual DOM

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

  • 优化方式

    • 优化节点操作

    • 优化事件处理

    • ....

  • 虚拟DOM是怎么优化性能的:背后有一套强大的算法: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:事件类型.修饰符="事件处理函数"

事件修饰符
  • stop

  • prevent

  • capture

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

  • once 事件将只会触发一次

  • 按键修饰符

    1. 直接使用键码来作为按键修饰符

          <!-- 只有在 `keyCode` 是 13 时调用 `vm.submit()` -->
          <input v-on:keyup.13="submit">
      
    2. 使用别名作为按键修饰符

      • left up right down
      • enter
      • tab
      • esc
      • space

3.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>' //指令修饰符
      }
    });

3.5 生命周期函数

beforeCreate()

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

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

created()

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

beforeMount()

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

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

mounted()

实例挂载到 DOM

  • 应用:节点操作

beforeUpdate()

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

updated()

  • 更新节点完毕

beforeDestroy()

destroyed()

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

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

3.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)
    })

3.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" }
  }
})

3.8 开发插件

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

3.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) {
      // 逻辑...
    }
  }

3.8.2 使用

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

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

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

4 组件 Component

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

4.1 组件定义和使用

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

4.1.1 组件要求

  • data 必须为 Function 类型

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

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

4.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
      }
    });

4.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>

4.2 组件通讯

原则:谁的数据谁修改

4.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
        }
      }
    }
  })

4.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>

4.2.3 兄弟组件通信

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

4.2.4 多层级组件通讯

  • 利用一个 Vue 实例作为中间桥梁实现传参
事件总线 Bus
  1. 自定义事件

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

  2. 触发自定义事件

    发送方触发事件 $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
        }
    });

4.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> 内容分发

4.4 插槽

  • 内容通讯:插槽 slot

4.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>

4.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>

5 模块系统

5.1 前言

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

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

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

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

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

5.2 vue单文件组件

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

  • vue单文件优点

    • 完整语法高亮

    • CommonJS 模块

    • 组件作用域的 CSS

5.3 vue单文件组件开发流程

webpack打包

5.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"
    }

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

5.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);
        }
    });

5.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>

5.4 ES Module

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

5.4.1 基本特点

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

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

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

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

5.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';

5.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';

5.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后引入(推荐)

6 过渡动画

  • <transition>
  • <transition-group>

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

6.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>

6.2 触发动画场景

Vue会自动检测是否设置 css动画JavaScript钩子,并在下列情形中添加进入/离开过渡效果(css过渡或js过渡)

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

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

  • 动态组件

  • 组件根节点

6.3 CSS过渡

通过CSS过渡类名

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

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

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

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

6.4 JavaScript过渡

通过内置事件实现过渡动画效果,可以利用第三方动画库(如: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) {
        // ...
      }
    }

7 面试题

  • 响应式属性在哪个生命周阶段处理:创建阶段

  • 在父子组件通讯过程中,在哪个生命周期函数最先获取到父组件传入的数据

  • 在mounted生命周期函数中设置了定时器,组件销毁时定时器是否还在运行,如何清除

    • ajax请求 -> 如何取消ajax请求(xhr.abort())
原文地址:https://www.cnblogs.com/pleaseAnswer/p/15010057.html