vue基础 | 8月更文挑战

224 阅读7分钟

说明:本文本来是提供给公司内开发阅读,有些地方不必参考

认识项目结构

image.png 详细的看juejin.cn/post/684490…(包含项目结构和webpack)

如果你有vue基础请跳到下一节

vue简单介绍(核心:采用简洁的模板语法来声明式地将数据渲染进 DOM 的系统)

image (1).png

先来一个例子

<!-- 指定vue管理内容区域(id为app的标签内区域),需要通过vue展示的内容都要放到这个元素中  通常我们也把它叫做边界 数据只在边界内部解析-->
<template>
  <div id="app">{{ msg }}</div>
</template>
<script>
export default {
name:'app',
  data() {
    return {
      msg:'hello vue'
    }
  },
  mounted() {
    console.log(this.msg)
  }
}
</script> 
  • 先在data里面声明数据,再使用数据
    • data必须以函数返回值的形式定义,这样复用组件就返回一份新的data
    • 如果使用对象形式的话一个变就全部变(对象是引用类型)
  • 在其他属性或者钩子函数中通过this.msg可用访问数据

下面所有语句基于上面的例子

  • 模板语法(页面中绑定数据的方法)

    1. v-text='msg'或者{{msg}}
      1. 原理:更新DOM的textContent
    2. v-html
      1. 可输入的文本不要使用v-html,可能导致XSS攻击
      2. 原理:更新DOM的innerHtml
    3. 在模板语法中可以使用js表达式,如{{ msg.split('').reverse().join('') }}
      1. 只能使用单个表达式,注意不是js语句(const a = '1'这是js语句)
      2. 不建议在vue文件使用js表达式,建议写到计算属性
  • 属性绑定

    • v-bind,语法糖为:(英文的冒号)
    • 用于绑定html标签的所有属性(例如:title='msg')
    • 组件传值使用v-bind(组件中有事例)
    • 当绑定的数据或者表达式的值改变会响应式的作用于DOM
    • 如果绑定对象,读取的是对象的toString()方法后返回的值,如果没有重写这个方法,绑定值为[object Object](例1)
    • 绑定属性时加冒号是绑定变量或者变量表达式,不加冒号是绑定字符串(所以传入number和Boolean值常量时也需要加冒号)(例2)

例1

<script>
  data() {
    return {
      obj:{
        name:'yangf'
      }
    }
  }
  mounted() {
    obj.toString = function() {
        return obj.name;
    }
  }
</script>
<template>
  <!--绑定变量-->
  <div :title='obj'></div>
  <!--不加toString方法编译为-->
  <div title=[object Object]></div>
  <!--加了toString方法编译为-->
  <div title='yangf'></div>
</template>

例2

<script>
  data() {
    return {
      msg:'hello',
      num: 5
    }
  }
</script>
<template>
  <!--绑定变量-->
  <div :title='msg' :data='num'></div>
  <!--tab为组件-->
  <!--这里的title如果加:组件里面读到的是hello,不加冒号读到的是'hello'-->
  <tab :num='5' :title="'hello'"></tab>
</template>
  • 绑定方法

    • v-on,语法糖@
    • 用于添加事件,绑定的事件写到methods对象中
    • v-on:click='clickBtn'或者v-on:click='clickBtn('参数',$event)'或者@click='clickBtn'
    • v-on可以监听多个方法
    <input type="text" v-on="{input:onInput,focus:onFocus}">
    
    • 可以添加键盘事件@keyup.13="submit"和@keyup.enter="submit"相同
    • 事件修饰符
      • .stop阻止冒泡,调用 event.stopPropagation()
      • .prevent阻止默认行为,调用 event.preventDefault()
      • .capture添加事件侦听器时使用事件捕获模式
      • .self只当事件在该元素本身(比如不是子元素)触发时,才会触发事件
      • .once事件只触发一次
      • 修饰键(.ctrl等)、鼠标按键修饰符(.left等)
  • v-model

    • 在表单元素(input、textarea等)上创建数据双向绑定(监听输入事件改变数据)
    • 对于需要使用输入法的语言,在输入法组合文字的过程中得不到更新
    • 修饰符:
      • .lazy 通过这个修饰符,转变为在执行 change 事件再更新数据
      • .number 自动将用户的输入值转化为数值类型
      • .trim 自动过滤用户输入的首尾空格

v-model只是语法糖,不同的输入元素使用不同的property并抛出不同的事件

  1. text 和 textarea 元素使用 value property 和 input 事件
  2. checkbox 和 radio 使用 checked property 和 change 事件
  3. select 字段将 value 作为 prop 并将 change 作为事件
<input v-model="msg" />  //这一行等于下一行
<input v-bind:value="msg" v-on:input="msg = $event.target.value" />
  • v-for

    • 循环渲染数组或者对象
    • 推荐添加key属性
      • vue默认“就地更新”原则,复用已有元素,若数据项顺序改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序
      • 使用 key,VUE会基于 key 的变化重新排列元素顺序,并且会移除 key 不存在的元素。
      • 更准确:因为带 key 就不是就地复用了,在 sameNode 函数 a.key === b.key 对比中可以避免就地复用的情况
      • 更快速:利用 key 的唯一性生成 map 对象来获取对应节点,比遍历方式更快
  • v-if和v-show

    • v-if根据表达式的值的真假条件,销毁或重建元素;有更高的切换开销;表达式初始值为false将不会渲染标签
    • v-show:根据表达式之真假值,切换元素的 display CSS 属性;有更高的初始渲染开销,无论表达式真假初始就会渲染
  • 样式处理 -class和style

    • v-bind:class="expression" or :class="expression"
    • expression为字符串、数组、对象
<!-- 1 -->
<div v-bind:class="{ active: true }"></div>
<!--解析后-->
<div class="active"></div>
<!-- 2 -->
<div :class="['active', 'text-danger']"></div>
<!--解析后-->
<div class="active text-danger"></div>
<!-- 3 -->
<div v-bind:class="[{ active: true }, errorClass]"></div>
<!--解析后-->
<div class="active text-danger"></div>
  • 监听器和计算属性

    • watch
      • watch一般写成一个对象,键是需要观察的表达式,值是对应回调函数
      • 不缓存,当表达式的值发生变化后,会调用对应的回调函数完成响应的监视操作
      • watch支持异步;
      • 函数接收两个参数,第一个参数是最新的值;第二个参数是输入之前的值;
      • 监听数据必须是data中声明过或者父组件传递过来的props中的数据
        • 父组件传递过来的props中的数据是不允许被改变的
      • 最好不要监听整个对象,而是监听对象中的某个属性
data() {
  return {
    a:1,
    person:{
      age:18
    }
  }
},
watch:{
    a: function(val, oldVal) {
      // val 表示当前值
      // oldVal 表示旧值
      console.log('当前值为:' + val, '旧值为:' + oldVal)
    },
     // 监听对象中所有属性的变化      
     // 给对象的所有属性都加上这个监听器,但是这样性能开销就会非常大了
     // 任何修改obj里面任何一个属性都会触发这个监听器里的handler
    person: {
      // 确认是否以当前的初始值执行handler的函数,组件加载立即执行函数
      immediate: true,
      handler: function (val, oldVal) { /* ... */ },
      deep: true
    },
    // 优化方案
    'person.age': {
      immediate: true,
      handler: function (val, oldVal) { /* ... */ }
    }
}
  • computed

    • 计算属性是基于它们的依赖进行缓存的,只有在它的依赖发生改变时才会重新求值
    • 模板语法中不要放入太多的逻辑,否则会让模板过重、难以理解和维护,推荐使用计算属性
    • computed中的属性不能与data中的属性同名,否则会报错
  • 组件传值

    • 父组件到子组件
      • 通过子组件props属性来传递
        • props可以是数组(指定传入的属性名即可)
        • 也可以是对象(传入的属性可以指定默认值、是否必传等)
      • 组件中从父级传来的属性值必须在组件中通过props属性显示指定
      • 传递过来的props属性的用法与data属性的用法相同
      • 父级传值使用v-bind绑定属性
    • 子组件传值到父组件
      • 子组件中通过$emit()触发自定义事件
      • 父级接收使用v-on绑定方法
    • 组件上的 v-model 默认会利用名为 value 的 prop 和名为 input 的事件
      • 因为是实现双向绑定,在父页面不需要绑定额外的事件

      • 但是像单选框、复选框等类型的输入控件可能会将 value 用于不同的目的

      • 组件中使用model 选项来避免这样的冲突

      • 下面的(第一种方法)和(第二种方法)用于举例使用

      • 多层级组件传值,可以在中间层级组件绑定attrs,不用在中间层级组件增加使用propsattrs,不用在中间层级组件增加使用props和emit

    <router-view  v-bind="$attrs"/>
    

例子(组件传值的方法还有很多,这两个是使用最普遍的,所以只列举props和emit)

// 组件的js部分,html(vue)部分和一般vue页面相同
export default {
    name: 'components-tab',
    // 用来实现双向绑定(第二种方法)
    model: {
      prop: 'checked',
      event: 'change'
    },
    // 子组件需要接收的参数
    props:{
        // 对应model中的prop值名
        checked: Boolean,// (第二种方法)
        dataList:{
            type: Array,
            default: function () {
                return [];
            },
            required: true
        },
        changeType:{
            type:String,
            default:'scree'
        },
        activeItem:{
            type:Object,
            default:function () {
                return {};
            }
        },
        // 在父组件使用v-model传递的,在子组件可以使用value接收(第一种方法)
        value: {
            type:String,
            default:''
        }
    },
    // 需要改变的要定义,不需要改变的可以直接使用props的值
    data() {
        return {
            langTabData:this.dataList,
            curActiveItem:this.activeItem,
            tabBtn:false,
            // 当前选中的item的index
            activeIndex:0
        };
    },
    methods: {
        // 在父组件使用v-model传递的,在子组件可以使用input事件传出(第一种方法)
        changeBtn() {
            this.$emit('input', this.activeIndex)
        },
        // 对应model中的event值名,作用和input相同(第二种方法)
        changeBtn1() {
            this.$emit('change', this.activeIndex)
        }
    }
};
// 在页面中使用组件
<template>
    <div class="fd-page-box">
        <lang-tab class="fd-tab-box"
                :dataList="dataList"
                :activeItem="activeItem"
                @click="clickTab"></lang-tab>
                <span v-text="activeItem.key"></span>
    </div>
</template>
import tab from "./components/common/tab/index.vue";
export default {
    name: 'page-tab',
    components: {tab},
    data() {
        return {
            dataList:[{
                label:'申请延长解回信息1',
                key:'sqycjhxx1'
            }, {
                label:'申请延长解回信息2',
                key:'sqycjhxx2'
            }],
            activeItem:{
                label:'申请延长解回信息5',
                key:'sqycjhxx5'
            }
        }
    },
    methods: {
        clickTab(item){
            this.activeItem = item;
        }
    }
};

Vue Tips:

  • 只有data中的数据才是响应式的,动态添加的是非响应式的
    • 解决方法:常用$set()、添加多个属性使用Object.assign()
<script>
export default {
  name:'app',
  data() {
    return {
      obj:{
        name:'喜羊羊'
      }
    }
  },
  mounted() {
    obj.age = '18'; // 后面不可以动态响应
    // 使用以下两种方式
    // 推荐使用
    this.$set(this.obj,'age','18');
    // 添加多个属性是使用
    this.obj = Object.assign({}, vm.stu, { age: '18' });
  }
}
</script> 
  • $nextTick怎么使用
    • vue是异步更新DOM,去除重复数据,避免不必要的计算和重复DOM操作,所以要拿到更新DOM后的数据需要使用$nextTick
methods: {
  getInnerText() {
    this.msg = 'change';
    this.$nextTick(() =>{
      console.log('$nextTick中打印:', this.$el.children[0].innerText);
    })    
    console.log('直接打印:', this.$el.children[0].innerText);
  }
}
  • vue是单向数据流,数据总是从父组件传到子组件,子组件不能修改父组件传过来的值,只能请求父组件对原始值进行修改
    • 在子组件中使用v-model绑定父组件传过来的props值是不规范的会报警告
    • 如果需要改变父组件props的值,需要在data里面定义一个变量,初始值为props的值
  • 如果频繁的切换使用v-show;如果在运行时条件很少改变使用v-if
  • v-if和v-for不建议一起使用
    • vue2.0解析时先解析v-for再解析v-if
    • 哪怕我们只渲染出一小部分满足条件的元素,也得在每次重渲染的时候遍历整个列表,不论满足条件的数据是否发生了变化

数据变化重渲染的时候遍历整个列表,不论满足条件的数据是否变化: rander函数执行以后会生成vnode(虚拟dom),当数据发生变化时,会触发watcher执行update方法,会重新执行render方法生成新的vnode,所以需要重新遍历数据   在vue2中,为了优化性能,将watcher的粒度放大,变为一个组件一个watcher,所以数据变化值呢通知组件,至于组件的哪个数据变化,应该更新哪个节点,需要和之前生成的vnode进行对比(使用diff算法)