vue_02_重学Vue

重学_vue_01

基础

创建 VUE 实例的几种方式

创建组件实例有两个方法:1.通过 new Vue()、2. 通过 new 构造器()
挂载到容器上也有两个方法:1. 通过\(el;2. 通过\)mount

  1. Vue 实例 new Vue()

    1. 可以通过自身 components 引用 vue.extend 构造,通过自身 data 向构造传参
    2. 可以通过自身 component 引用组件模版,通过自身 data 向组件传参
  2. Vue 组件 Vue.Component()

    Vue.Component('name', options) 用来创建全局组件
    options 可以是 Vue.extend 或 {}

    1. 可通过自身初始化组件结构
    2. 可通过引入 vue.extend 初始化组件结构
    3. 可通过第三方模版 temple.html 初始化组件结构
    4. 使用范围: 任何已被 vue 初始化的元素内
  3. Vue 构造 Vue.extend( options )

    参数 options 是一个包含组件选项的对象,data 比较特殊必须是一个方法

    vue.extend 创建了一个 vue 组件的子类但是没有实例化这个子类,可以挂载在组件是使用

    1. 只能通过自身初始化结构 使用范围
    2. 挂载在某元素下
    3. 被 vue 实例的 components 引用
    4. Vue.component 组件引用
  4. new vue,vue.extend,vue.component 的区别

    关系:vue 构造--->vue 组件---->vue 实例
    不同的组件可以共用同一个 vue 构造,不同的 vue 实例可以共用一个 vue 组件

  // Vue 构造
  const extend1 = Vue.extend({
    template: `<div>{{text}}</div>`,
    props: ['T'],
    data: function() {
      return {
        text: "这里是构造器"
      }
    }
  })

  const extend2 = {
    template: `<div>{{text}}</div>`,
    props: ['T'],
    data: function() {
      return {
        text: "其中也可以不使用 extend 方法,而直接定义对象。"
      }
    }
  }

  // Vue 组件
  Vue.component("name1", extend1); // 可以是 Vue.extend
  Vue.component("name2", extend2); // 也可以直接是个对象

  // Vue 实例
  // 当要在页面展示时,需要用到组件实例,需要将要展示的组件实例和容器绑定起来。
  // 创建组件实例有两个方法:1. 通过new Vue(); 2.通过new 构造器()。
  // 挂载到容器上也有两个方法:1. 通过$el;2. 通过$mount

  // 通过new Vue()
  new Vue {
    el: "#app",
    data: function() {
      return {

      }
    }
  }

  const app = new Vue {
    data: function() {
      return {}
    }
  }
  app.$mount('#app');

  const componentA = { /* ... */};
  new Vue {
    components: {
      'component-a': componentA,
    }
  }

  // 通过new 构造器()
  new extend1({
    el: "#app",
    propsData: {
      T: "可以通过xxx创建xxx"
    }
  })

  new extend2().$mount('#app');

  // 组件的渲染方式也有两种,普通的就是通过字符串模板,即HTML,在通过component。
  // 还可以通过渲染函数——render。

VUE 的生命周期

1. 生命周期

Vue是一个构造函数,当执行执行这个函数时,相当于初始化vue实例在创建实例过程中,需要设置数据监听,编译模板,将实例挂载到DOM上,数据更新能够让DOM也更新,在这个初始化,又会不同阶段默认调用一些函数执行,这些函数就是生命周期的钩子函数

  1. beforeCreate // vue 实例 创建前
  2. created // 创建实例成功(一般此处请求实现数据的异步请求)
  3. beforeMount // 第一次渲染前
  4. mounted // 渲染成功
  5. beforeUpdate // 更新前
  6. updated // 更新完成
  7. beforeDestroy // 销毁前
  8. destroyed // 销毁完成
    注: 创建前后 可以获取数据及方法 渲染前后可以获取到真实的 DOM 更新前后能数据更新执行 销毁前后 销毁实例、数据不在双向绑定
2.其它三个钩子函数

生命周期钩子函数,让开发者在初始化实例时,添加自己的代码; 生命周期的钩子函数中的this,会默认指向vue的实例;
component 内置组件,用 is 属性来判断组件; is 属性的属性值和组件名称一直
keep-alive 内置组件 用于缓存组件

  1. activated // 当缓存组件有被显示出来时,会触发这个钩子函数
  2. deactivated // 当缓存的组件隐藏时,会触发这个钩子函数;
  3. errorCaptured // 当子孙组件出错时,会调用这个钩子函数
    生命周期钩子函数图

  const html1 = `<div>模板1</div>`;
  const html2 = `<div>模板2</div>`;
  const html3 = `<div>模板3</div>`;

  const template = `
    <keep-alive>
      <component :is="element"></component>
    </keep-alive>
  `;
  
  const app = new Vue({
    template: template,
    data: function() {
      return data {
        element: 'html1',
      }
    },
    components: {
      html1: html1,
      html2: html2,
      html3: html3
    },
    beforeCreate() {
      console.log("vue 实例化之前");
    },
    created() {
      console.log("创建后");
    },
    beforeMount() {
      console.log("渲染前");
    },
    mounted() {
      console.log("渲染后");
    },
    beforeUpdate() {
      console.log("更新前");
    },
    updated() {
      console.log("更新后");
    },
    beforeDestroy() {
      console.log("销毁前");
    },
    destroyed() {
      console.log("销毁后");
    },
    activated() {
      console.log("缓存的组件显示出来了");
    },
    deactivated() {
      console.log("缓存的组件被隐藏了");
    },
    errorCaptured(a,b,c) {
      // 当捕获一个来自子孙组件的错误时被调用。
      // 此钩子会收到三个参数:错误对象、发生错误的组件实例以及一个包含错误来源信息的字符串。
      // 此钩子可以返回 false 以阻止该错误继续向上传播。
      console.log("子组件报错");
    },
    methods: {
      fn() {
        const ary = ['html1', 'html2', 'html3'];
        let fi = ary.indexOf(this.element);
        if(fi<2) {
          this.element = ary[fi+1];
        } else {
          this.element = ary[0];
        }
      }
    }
  });

数据、方法

Vue实现使用new Vue函数创建,当一个 Vue 实例被创建时,它向 Vue 的响应式系统中加入了其 data 对象中能找到的所有的属性,当这些属性的值发生改变时,视图将会产生“响应”,即匹配更新为新的值

  1. 一般用 {{...}} 来引用属性
  2. vue 中使属性双向绑定,需要将属性写在data中
  3. 可用 Object.freeze() 阻止修改现有属性
new Vue({
  template: 
    `<div id="app">
      <input :value="data"/>
    </div>`,
  data() {
    return {
      data: "",
    }
  },
  created() {
    this.closeData = ""; // 此处会挂载在 当前实例上 但是无法双向绑定
  },
  update() {
    this.getData();
  },
  methods: {
    getData() {
      console.log("data", data); // data
    }
  }
})

计算属性和侦听器

  1. 计算属性在依赖的响应式属性改变之前会一直返回之前计算好的值`
  2. 侦听属性监听的属性必须是data中声明过或props中的数据,当数据发送变化的时候触发其它操作`
  3. 侦听属性对对象或数组进行监听的时候可以设置 deep:true 开启深度监听 也可以通过 计算属性+侦听属性的方式监听对象属性的改变 设置 immediate: true 可以让侦听属性在页面开始时便开始监听`
new Vue({
  template: `<div>{{fullName}} || {{fullName('123456')}}</div>`,
  data() {
    return {
      name: "",
      obj: { 
        name: ''
      }
    }
  },
  computed: {     // 计算属性
    fullName: {
      set: (newValue)=>{
        this.name = "xxxxxxxxx"+newValue;
      },
      get: ()=>{
        return this.name + 'xxxxxxxxx';
      }
    }
  },
  watch: {
    name: (newValue, oldValue)=>{
      this.name = newValue + oldValue;
    },
    'obj.name': {
      handler: (newValue, oldValue) => {
        
      },
      deep:true,     // 深度监听
      immediate:true ,// 是否在页面开始的时候运行
    }
  }
})

混入

多个组件都需要同样的方法和变量等的时候可以用 混入 提高复用性

  1. 混入的钩子函数在本组件同名钩子函数之前调用,且同名钩子函数会合并
  2. 混入和组件,属性或方法名冲突时,取组件的对象和方法
  3. 全局混入会影响之后创建的所有vue实例(在main.js中用 Vue.mixin())
let mixin = {
  data () {
    return {
      name: '混入XXXX'
    }
  },
  methods: {
    getName () {
      console.log(this.name)
    }
  }
}

new Vue({
  mixins: [mixin],
  data () {
    return {
      sex: "男",
    }
  },
  methods: {
    getName () {
      console.log('name', this.name, 'sex:', this.sex)
    }
  }
})

指令

1. 指令的本质:语法糖,标志位。在编译阶段 render 函数里,会把指令编译成 JavaScript 代码。
2. 不推荐同时使用 v-if 和 v-for 应为 v-for的执行优先比v-if要高 会造成多次 判断
3. 给 v-for 设置key 的原因是 高效的更新虚拟dom
4. v-model text 和 textarea 元素使用 value 属性和 input 事件
5. v-model checkbox 和 radio 使用 checked property 和 change 事件
6. v-model select 字段将 value 作为 prop 并将 change 作为事件

  1. v-text 插入文本
  2. v-html 插入html
  3. v-show true 显示 false 隐藏 类似于display: none;
  4. v-if / v-else-if / v-else true 显示 false 隐藏 false时删除节点
  5. v-for 循环 使用的元素需要添加 key 属性
  6. v-on 简写 @ 绑定 事件
  7. v-bind 简写 : 动态地绑定一个或多个属性
  8. v-model 双向绑定的语法糖
  9. v-slot 简写 # 插槽
  10. v-pre 跳过这个元素和它的子元素的编译过程
  11. v-once 只渲染元素和组件一次
  12. v-cloak 保持在元素上直到关联实例结束编译(先隐藏等编译完成显示) 需配合css
<template>
  <div 
    v-html="html"
    v-show="false"
    v-once
    v-cloak
  >
    名称:{{name}}
  </div>
  <div>
    <div v-if="false">
      <input :value="name" @input="input"/>
      <input v-model="name"/>
      <!-- .lazy 在 change 时而非 input 时更新 -->
      <input v-model.lazy="name" />
      <!-- .number 将输入 string 变成 number -->
      <input v-model.number="name" type="text" />
       <!-- .trim 去除前后空格 -->
      <input 
        v-model.trim="name" />
    </div>
    <div v-else>
      <span v-for="i in 10" :key="'sp_'+i" v-text="i"></span>
    </div>

    <div v-pre>
      {{name}}
    </div>
  </div>
</template>

<script>
export default {
  data () {
    return {
      name: "指令",
      html: `<span style="color: red;">xxxx</span>`
    }
  },
  methods: {
    input(e) {
      this.name = e.target.value;
    } 
  }
}
</script>

<style>
[v-cloak] {
  display: none;
}
</style>

自定义指令

  1. 全局自定义指令 Vue.directive('指令名', {})
  2. 局部自定义指令 directive: { '指令名':
  3. 生命周期:
    a. bind 指令第一次绑定到元素时调用,只调用一次
    b. inserted 被绑定元素插入父节点时调用
    c. update 所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前
    d. componentUpdated 指令所在组件的 VNode 及其子 VNode 全部更新后调用
    e. unbind 指令与元素解绑时调用
Vue.directive('v-custom',{
  bind(el, binding, vnode){
      // el:      绑定元素的 dom 
      // binding: 指令相关的集合
      // vnode:   Vue 编译生成的虚拟节点
      // oldVnode:上一个虚拟节点 只有 update 和 componentUpdated 可用
  },
  inserted(el, binding, vnode) {

  },
  update(el, binding, vnode, oldVnode) {

  },
  componentUpdated(el, binding, vnode, oldVnode) {

  },
  unbind(el, binding, vnode, prevVno) {

  }
})

条件、列表渲染

  1. v-if / v-else-if / v-else
  2. v-show
  3. v-for
  4. 想要 v-if 和 v-for 的元素节点不渲染后不包含 所在节点可用使用 template
  5. 用 key 管理可复用的元素 ( vue 一般情况下会复用已有元素 )
  6. v-if 与 v-show 的区别: v-show 只是隐藏元素 无论是否显示都渲染 v-if 是移除元素
  7. v-if 与 v-for 不一起使用 因为 v-for 优先级高于 v-if 会造成多次判断

过滤器

  1. 属性 | 过滤器 | 过滤器 在 {{}} 和 v-bind 中可用使用
  2. 全局过滤器 Vue.filters({})

<template >
  <div>
    {{'aaa' | filters('name', 1)}}
  </div>
</template>



Vue.filters({
  filterValue: (value, name, slice)=>{
    return (value+name).slice(0,-2);
  }
})

渲染函数和 JSX

  1. <template> 里面所使用模板HTML语法组建页面的,vue 中都会编译成 render 函数,因为vue 中采用的是 虚拟DOM 所以拿到template模板时也要转译成 VNode(virtual node 虚拟节点) 函数
  2. render 有一个参数 createElement
    a. createElement('标签名', { '/*样式,属性,传的组件的参数,需要绑定的事件 */' }, ['设置分发的内容,如新增的其他组件 内如 ,子节点 createElement'])
// VNode属性
// {
//               class: {   // 与 `v-bind:class`
//                 foo: true,
//                 bar: false
//               },
//               style: {   // 与 `v-bind:style`
//                 color: 'red',
//                 fontSize: '14px'
//               },
//               attrs: {  // 普通的 HTML attribute 也可以用于传参到 props
//                 id: 'foo'
//               },
//               domProps: { // DOM property
//                 innerHTML: 'baz'
//               },
//               // 事件监听器在 `on` 内,
//               // 但不再支持如 `v-on:keyup.enter` 这样的修饰器。
//               // 需要在处理函数中手动检查 keyCode。
//               on: {
//               },
//               // 仅用于组件,用于监听原生事件,而不是组件内部使用
//               // `vm.$emit` 触发的事件。
//               nativeOn: {
                
//               },
//               // 自定义指令。注意,你无法对 `binding` 中的 `oldValue`
//               // 赋值,因为 Vue 已经自动为你进行了同步。
//               directives: [
//                 {
//                   name: 'my-custom-directive',
//                   value: '2',
//                   expression: '1 + 1',
//                   arg: 'foo',
//                   modifiers: {
//                     bar: true
//                   }
//                 }
//               ],
//               // 作用域插槽的格式为
//               // { name: props => VNode | Array<VNode> }
//               scopedSlots: {
//                 default: props => createElement('span', props.text)
//               },
//               // 如果组件是其它组件的子组件,需为插槽指定名称
//               slot: 'name-of-slot',
//               // 其它特殊顶层 property
//               key: 'myKey',
//               ref: 'myRef',
//               // 如果你在渲染函数中给多个元素都应用了相同的 ref 名,
//               // 那么 `$refs.myRef` 会变成一个数组。
//               refInFor: true
//             }


// 只 创建
let render_1 = {
  // render 和 template 不可共存 render 返回的就是一个node 节点
  // render 会传递一个参数 createElement 
  render(createElement) {
    return createElement(
      "div", 
      {
        style: { color: "red" },
        domProps: {
          // 此处会覆盖掉 第三个参数中的内容
          innerHTML: "此处可以插入值"
        }
      },
      [ `createElement('name', VNode属性, children) `
        `createElement('标签名', { /*样式,属性,传的组件的参数,需要绑定的事件*/ },[ '子元素' ])`
      ]
    );
  }
}

new Vue ({
  el: "#app",
  mixins: [render_1]
})
// render 组件和插槽
let render_2 = {
  props: {
    tag: String,
    data: Array
  },
  data () {
    return {
      name: "此处是xxxx"
    }
  },
  render(c) {
    let elem = this.data.map(item=> c('div', { style: { color: 'red'} },item.toString()))
    return c(
      this.tag, 
      {
        on: {
          click: (e)=>{
            console.log("点击事件xxxx",e);
          }
        },
      },
      [
        c('header', this.$slots.header&&this.$scopedSlots.header({ user: 'header' }) || '头部插槽默认内容'),
        c('main', [this.$scopedSlots.default({ content: this.name }) || elem]),
        c('footer', [this.$slots.footer&&this.$scopedSlots.footer({ footer: this.name })]),
      ]
    )
  }
}

new Vue ({
  el: "#app",
  components: {render_2},
  render: c=>c(
    render_2,
    {
      props: {
        tag: 'div',
        data: [1,2,3,4,5,6,7,8,9,10]
      },
      scopedSlots: {
        default: prop => {
           console.log("prop",prop);
           return prop.user
        },
        footer: prop => { // 作用域插槽
            // 这两的返回值会覆盖掉  children 中的值
            return prop.footer + 'U盾四安徽'
        }
      }
    },
    [
      '使用默认插槽 可以不写插槽名称',
      c('div', { slot: 'footer' }, '使用底部插槽')
    ]
  )
})

组件

组件注册

PROP

父子组件传参等

插槽(SLOT)

动态组件与异步组件

单文本组件

过度和动画

进入、离开、列表过度

状态过度

VUE API

版本 2.7

组合式 API

与 Vue 3 的行为差异

posted @ 2023-05-10 17:19  tsuru  阅读(26)  评论(0编辑  收藏  举报