Vue.js(四) 组件(component)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/vbirdbest/article/details/85081488

一:Vue组件简介

传统前端中一个网页应用是由很多.html文件组成,每个html文件又分为三部分,第一部分就是<html>用于展示视图,第二部分是<script type="text/javascript">用于和用户交互,第三部分是<style>用于控制视图的样式。开发中一般会将js脚本和样式文件单独抽出来作为一个单独的文件。如果想在一个html中嵌入另一个html可以通过<iframe src="foo.html" />来实现。

现代前端中一个网页应用是由多个.vue文件组成,每个vue文件又分为三部分,第一部分就是<template>用于展示视图,第二部分是<script>用于和用户交互,第三部分是<style>用于控制视图的样式。开发中通常都将<template>、<script>、<style>放在一个.vue文件中而不会分别独立成一个文件。如果想在一个vue中嵌入另一个vue可以通过注册组件以自定义标签形式来实现。

组件简单的说就是一个.vue文件,每个.vue文件就是一个组件,每个.vue文件对应着一个Vue实例(Vue类),一个应用程序是由很多个vue文件组成,在vue文件中可以嵌套其它vue文件,最终一个应用程序就像一棵倒立的树(组件)。组件是Vue中的最基础最核心的单位。

在这里插入图片描述

一个组件(component)大概就是下面这个样子:HelloWorld.vue

<template>
  <div>
    <span>{{ msg }}</span>
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  data () {
    return {
      msg : 'Hello Vue!'
    }
  }
}
</script>

<style scoped>
span {
  color: red
}
</style>

二:Vue三大部分

1. < template >

模板是用于编写视图(DOM)的地方,通常都会放在一个跟元素下面,通常根元素都是div。

<template>
	<div>
		<!-- view -->
	</div>
</template>

2. < script >

2.1 script脚本中包含两部分

  • import(导入):在传统前端开发中导入js文件使用 <script src="js/jquery.js"></script>来导入,在ES6中使用import 变量名 from '文件路径'语法来导入,作用是将某个文件创建成对象,然后将该对象赋值给一个变量名,可以导入 .js .vue .json文件,其中文件后缀名也可以省略

    import commonUtil from '@/utils/CommonUtil.js'
    import foo from '@/components/Foo.vue'
    

    两者效果类似

    var commonUtil = new CommonUtil();
    var foo = new Foo();
    
  • export(导出):export default 用于导出一个Vue的实例选项{ }, 被导出的对象可以通过import来导入,从而引用被导入的对象的属性和函数。在导入时可以通过@符号来表示src,导入时当后缀是.js、.vue、.json时是可以省略的,这些配置都可以在build/webpack.base.conf.js下的module.exports.resolve配置。在vue中导出的对象是一个Vue对象(包含name、data、methods等属性),Vue中有多个属性。

    build/webpack.base.conf.js

    module.exports = {
      resolve: {
        extensions: ['.js', '.vue', '.json'],
        alias: {
          'vue$': 'vue/dist/vue.esm.js',
          '@': resolve('src'),
        }
      }
    },
    
    // 导出默认的Vue对象
    export default {
    	// 配置Vue中的属性,如name、data、methods等
    	name: 'HelloWorld',
    	data() {
    		return { foo: 'foo', bar: 'bar' }
    	},
    	methods: {
    		foo() {
    		
    		},
    		bar() {
    		
    		}
    	}
    }
    

2.2 import 和 export 示例

我们定义一个非常简单的组件YesOrNoSelect.vue,组件中模板只有一个下拉框,导出的Vue实例选项也只有name和data,一个非常简单的样式。我们在另一个组件HelloWorld.vlue中导入importYesOrNoSelect.vue, 然后就可以在HelloWorld.vlue中来访问YesOrNoSelect.vue组件中导出的对象。

YesOrNoSelect.vue

<template>
    <select>
        <option v-for="item in options" v-bind:key="item.id">{{item.text}}</option>
    </select>
</template>

<script>
export default {
    name: 'YesOrNoSelector',
    data () {
        return {
            options: [
                {id: '', text:'全部'},
                {id: 0, text:'否'},
                {id: 1, text:'是'}
                ]
        }
    }
}
</script>

<style scoped>
select {
    width: 100px;
    height: 30px
}
</style>

HelloWorld.vue

<template>
  <div>
    <span>{{ msg }}</span>
  </div>
</template>

<script>
// 导入Vue
import YesOrNoSelect from '@/components/YesOrNoSelect'

export default {
  name: 'HelloWorld',
  data () {
    return {
      msg : 'Hello Vue!',
      // 引用导入的Vue的实例选项
      nameOfImportComponent: YesOrNoSelect.name,
      dataOfImportComponent: YesOrNoSelect.data()
    }
  }
}
</script>

在这里插入图片描述

3. < style>

style里和传统的css差不多,不同的是支持了更多的语法,比如scss、less、stylus等。

三:Vue生命周期

每个.vue文件都对应着一个Vue实例对象,每个Vue实例对象从创建到销毁都会经历以下几个过程:
在这里插入图片描述

开发中经常使用created函数做一些初始化工作,比如页面一加载时执行的动作。

<template>
  <div>
    <span>{{ msg }}</span>
  </div>
</template>

<script>

export default {
  name: 'HelloWorld',
  data () {
    return {
      msg : 'Hello Vue!'
    }
  },
  created() {
    console.log('init method created :'+ this.msg)
  }
}
</script>

在这里插入图片描述

四:Vue选项options

一个Vue实例包含如下几个属性和函数,这些属性和函数统称为选项options(就像配置参数一样)。下面使用伪代码列举了Vue的一些常用的属性和函数。

/**
* Vue 数据结构伪代码
*/
interface Vue {
	/** 组件名称 */
	String name;
	/** 模板视图 */
	String template;
	/** 在这里定义方法,可以定义多个 */
	Object methods;
	/** 局部注册组件 */
	Object components;
	/** 计算属性 */
	Object computed;
	/** 过滤器 用于格式化文本(如 金额、日期、货币) */
	Object filters;
	Object watch;
	/** 属性(properties) */
	Object props;
	
	/** 局部注册指令 */
	Object directives;
	Object[] mixins;
	
	/**
	* 根据选项返回一个Vue实例
	* 类似于构造函数
	*/
	Vue extend (options) {

	}
	
	/** 
	* 定义双向绑定的变量 
	* 在Vue实例中可以直接通过this来访问data函数返回值的对象的属性值
	*/
	public Object data {
		return { }
	}
	
	/**
	* Vue实例创建完成时执行的函数
	* 常用与做一些初始化操作,比如页面一加载就需要执行的动作。
	*/
	public void created {
	
	}
	
	public void activated {
	
	}
}

五:使用组件

5.1 Vue全局函数

  • Vue.directive(‘指令名称’, { }) 全局注册自定义指令
  • Vue.component(‘组件名称’, 组件实例) 全局注册组件
  • Vue.use(组件实例) 使用插件,它需要在你调用 new Vue() 启动应用之前完成:
  • Vue.filter(‘名称’, function(value) { }) 全局过滤器,格式化文本
  • Vue.extend(options) 构造一个Vue实例
  • Vue.set(object, key, value) 向对象添加响应式属性

5.2 自定义标签

5.2.1 定义需要被引入的组件YesOrNoSelect.vue

<template>
    <select>
        <option v-for="item in options" v-bind:key="item.id">{{item.text}}</option>
    </select>
</template>

<script>
export default {
    name: 'YesOrNoSelector',
    data () {
        return {
            options: [
                {id: '', text:'全部'},
                {id: 0, text:'否'},
                {id: 1, text:'是'}
                ]
        }
    }
}
</script>

<style scoped>
select {
    width: 100px;
    height: 30px
}
</style>

5.2.2 注册组件
在main.js中全局注册

import YesOrNoSelect from '@/components/YesOrNoSelect'
Vue.component('yes-or-no-select', YesOrNoSelect)

或者在HelloWorld.vue中局部注册

<script>
import YesOrNoSelect from '@/components/YesOrNoSelect'

export default {
  name: 'HelloWorld',
  components: {
    YesOrNoSelect
  }
}
</script>

5.2.3 使用自定义的组件 HelloWorld.vue

<template>
  <div>
    YesOrNo: <yes-or-no-select />
  </div>
</template>

<script>

import YesOrNoSelect from '@/components/YesOrNoSelect'

export default {
  name: 'HelloWorld',
  data () {
    return {
      
    }
  }
}
</script>

在这里插入图片描述

5.3 过滤器

过滤器其实就是对输入的值进行处理,返回处理后的值。

过滤器既可以在文本插值表达式中使用,也可以在v-bind等指令中使用。

5.3.1 在main.js中注册过滤器

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

5.3.2 在模板中使用管道命令符 | 来对前面的值进行格式化处理
注意:管道命令符可以同时使用多次

<template>
  <div>
    文本插值:{{msg | capitalize}} <br>
    指令:<input v-bind:value="msg | capitalize" />
  </div>
</template>

<script>
  export default {
    name: 'HelloWorld',
    data () {
      return {
        msg: 'shanghai'
      }
    }
  }
</script>

在这里插入图片描述

5.4 使用插件

在main.js中导入第三方组件并使用组件

import ElementUI from 'element-ui'
// 使用时需要在你调用 new Vue() 启动应用之前完成
Vue.use(ElementUI)

new Vue({
  el: '#app',
  router,
  components: { App },
  template: '<App/>'
})

5.5 计算属性

计算属性(computed):插值表达式{{ }}只适合比较简单的计算,如果计算逻辑比较复杂可以放到computed中作为一个函数,然后在插值表达式中直接将这个函数作为一个普通属性来使用。属性名称为计算属性对应的函数名称。

注意计算属性会缓存,当相关依赖没有发生变化时是直接返回缓存中的值而不会重新计算。

<template>
  <div>
    计算属性: {{upperCase}} <br>
    局部过滤器: {{realName | upperCaseFilter}} <br>
    方法调用: {{upperCaseMethod(realName)}}
  </div>
</template>

<script>

export default {
  name: 'HelloWorld',
  data () {
    return {
      realName: 'zhang san'
    }
  },
  computed: {
    upperCase () {
      return this.realName.toLowerCase().split(/\s+/).map(function(item) {
        return item.slice(0, 1).toUpperCase() + item.slice(1);
      }).join(' ');
    }
  },
  filters: {
    upperCaseFilter (value) {
      return value.toLowerCase().split(/\s+/).map(function(item) {
        return item.slice(0, 1).toUpperCase() + item.slice(1);
      }).join(' ');
    }
  },
  methods: {
    upperCaseMethod(value) {
      return value.toLowerCase().split(/\s+/).map(function(item) {
        return item.slice(0, 1).toUpperCase() + item.slice(1);
      }).join(' ');
    }
  }
}
</script>

在这里插入图片描述

使用方式 是否会缓存 使用场景
计算属性(computed) 直接作为普通属性来使用 {{ foo }} 会缓存(当依赖的值没有发生变化时) 使用比较多,计算比较复杂的
过滤器(filter) 通过管道命令符|来使用{{value | foo}} 不会缓存,每次都会执行函数 过滤数据或者格式化文本(日期、金额等)
方法(method) 方法调用通过小括号() { {foo(value) }} 不会缓存,每次都会执行函数 一般的函数

相同点:其实三者都能达到相同的目的,只不过在使用方式上语法上有一点区别,应用场景上有一点点小区别。

5.6 侦听属性(watch)

监听某个值的改变, 监听的值要和watch中的函数名保持一致。功能上和oninput事件差不多。

<template>
  <div>
    <input v-model="keyword"/>
  </div>
</template>

<script>
  export default {
    name: 'HelloWorld',
    data () {
      return {
        keyword: ''
      }
    },
    watch: {
        keyword() {
          console.log('keyword=' + this.keyword)
        }
      }
    }
</script>

在这里插入图片描述

5.7 Vue.extend({ })

<template>
  <div>
    Vue.extend(options)
  </div>
</template>

<script>
  import Vue from 'vue'

  let options = {
    data () {
      return {
        msg: 'Hello Vue!',
        foo: 'bar'
      }
    },
    created () {
      console.log(this.msg)
    }
  }

  // 构造Vue
  let Component = Vue.extend(options)
  // 创建组件对象,会调用选项中的created函数
  let component = new Component()

  export default {

  }
</script>

在这里插入图片描述

5.8 mixin(混入)

mixin(混入): 就是预先定义一个Vue选项(options),此时称之为混入(mixin),在创建Vue实例时将混入对象传给Vue选项,由于混入对象定义了一些Vue选项,而在创建Vue实例时也会定义一些选项,两者混合在一起,当重复时会根据一定的优先级是使用混入对象的属性还是使用Vue实例的属性。

  • 当混入的data中的属性和Vue实例中的data属性名字重复时使用Vue实例中的值,不重复时取并集
  • 当混入的methods中的方法重名时取Vue实例中的method的
  • 当钩子函数重名时,会取并集,会先调用混入的钩子函数,然后再调用组件的钩子函数。有点像构造函数中先调用父类的构造函数再调用子类的的构造函数的意思。
<template>
  <div>
    mixin
  </div>
</template>

<script>
  import Vue from 'vue'

  let mixin = {
    data () {
      return {
        foo: 'foo mixin',
        msg: 'msg mixin'
      }
    },
    methods: {
      foobar () {
        console.log("foobar method mixin")
      }
    },
    created () {
      console.log('created function mixin')
    }
  }

  let vm = new Vue({
    mixins: [mixin],
    data () {
      return {
        bar: 'bar self',
        msg: 'msg self'
      }
    },
    methods: {
      foobar () {
        console.log("foobar method self")
      }
    },
    created () {
      console.log('created function self')
    }
  })

  console.log(vm.$data)
  vm.foobar()

  export default {}
</script>

在这里插入图片描述

六:props

props可以为自定义标签(组件)来自定义属性。使用组件时可以通过自定义属性来向子组件传递参数。

props的作用就是父组件(使用子组件的组件)向子组件(被引用的组件)传递参数。

Props类型:

  • 字符串数组形式 props: [‘foo’, ‘bar’, ‘foobar’]
  • 数据类型形式 { foo: String, bar: Number, baz: Boolean, foobar: Array, xxx: Object }

传值方式:

  • 静态传值: 自定义属性名=“值” 或者 v-bind:自定义属性名=“表达式值”, 当值是数值或者布尔类型时需要使用v-bind方式,因为数值和布尔也是js中的表达式
  • 动态传值: v-bind:自定义属性名=“属性值”

无论静态传值还是动态传值推荐使用v-bind指令

6.1 YesOrNoSelect.vue定义子组件

声明了两个自定义属性props,自定义属性可以在指令或者文本插值表达式中使用:

  • options数组类型,select标签要循环的值。
  • index数值类型,默认被选中的option对应的索引, 如果不传递该属性则默认为0。
<template>
  <label>
    <select v-model="index === undefined ? 0 : index">
      <option v-for="item in options" v-bind:key="item.id" :value="item.id">{{item.text}}</option>
    </select>
  </label>
</template>

<script>
  export default {
    name: 'YesOrNoSelect',
    props: {
      options: Array,
      index: Number,
    }
  }
</script>

<style scoped>
  select {
    width: 100px;
    height: 30px;
  }
</style>

6.2 使用子组件 HelloWorld.vue

<template>
  <div>
    <!-- index 默认选中最后一项,该属性如果不传默认为0 -->
    YesOrNo: <yes-or-no-select :options="options" :index="2"/> <br>
  </div>
</template>

<script>
  import YesOrNoSelect from './YesOrNoSelect'

  export default {
    name: 'HelloWorld',
    components: {YesOrNoSelect},
    data () {
      return {
        options: [
          {id: 0, text:'全部'},
          {id: 1, text:'否'},
          {id: 2, text:'是'}
        ]
      }
    }
  }
</script>

在这里插入图片描述

七:全局属性

  • $options :获取Vue实例的选项
  • $data :获取Vue实例选项中的data
  • $refs: 获取子组件对象,先在子组件使用属性ref定义一个引用名称,然后根据this.$refs.引用名称就能获取到子组件对象。获取到子组件对象是非常重要的,这意味着在父组件中可以和子组件进行通讯。
  • $router: 路由器
  • $route: 当前路由

7.1 YesOrNoSelect.vue

子组件定义了一个getSelectedValue方法,供父组件来调用。

<template>
  <label>
    <select v-model="index === undefined ? 0 : index">
      <option v-for="item in options" v-bind:key="item.id" :value="item.id">{{item.text}}</option>
    </select>
  </label>
</template>

<script>
  export default {
    name: 'YesOrNoSelect',
    props: {
      options: Array,
      index: Number,
    },
    methods: {
      getSelectedValue() {
        return this.index
      }
    }
  }
</script>


<style scoped>
  select {
    width: 100px;
    height: 30px;
  }
</style>

7.2 HelloWorld.vue

父组件,在自定义标签中使用ref来定义一个引用的名称,然后通过this.$refs.引用名称就能拿到子组件对象,从而就可以调用子组件的方法。

<template>
  <div>
    YesOrNo: <yes-or-no-select ref="yesOrNo" :options="options" :index="2"/> <br>
    <button @click="getValue">调用子组件的方法</button>
  </div>
</template>

<script>
  import YesOrNoSelect from './YesOrNoSelect'

  export default {
    name: 'HelloWorld',
    components: {YesOrNoSelect},
    data () {
      return {
        options: [
          {id: 0, text:'全部'},
          {id: 1, text:'否'},
          {id: 2, text:'是'}
        ],
        value: 0
      }
    },
    methods: {
      getValue () {
        this.value = this.$refs.yesOrNo.getSelectedValue()
        console.log(this.$options)
        console.log(this.$data)
      }
    }
  }
</script>

在这里插入图片描述

注意:这里有个警告(Vue warn), 原因是我们在v-model="index === undefined ? 0 : index"对自定义的属性进行了运算,Vue推荐不应该对父组件传递过来的值进行计算,所以给出了一个警告,这个警告可以通过计算属性来实现,去除警告,但如果使用计算属性来实现那么v-mode的值index就不能随着option的改变而改变了。具体原因请参考官网Prop ,所有的警告都可以在生产环境下来去掉警告提示。

7.3 父子组件之间通讯

  • 父组件通过子子组件自定义的属性像子组件传递参数,这样子组件就能获取到父组件传递的值。
  • 父组件通过在组件上使用ref定义引用名称,然后通过this.$refs.引用名称来获取到子组件对象,通过调用子组件的方法来获取子组件中的值。这样父组件就能获取到子组件内部的数据。

八:Vue全局配置

开发中控制台经常报警告(可能是Vue不推荐这样做,但是却是自己想要的效果),如果想关闭警告可以通过Vue.config.productionTip 来配置。

// 生产环境下不警告
// 如果想在开发环境(npm run dev)下去掉警告可以修改dev.env.js 将NODE_ENV改为production值即可
Vue.config.productionTip = false

猜你喜欢

转载自blog.csdn.net/vbirdbest/article/details/85081488