Vue2学习笔记2——B站

文章目录

  • 3 组件化开发
    • 3.1 全局组件和局部组件
    • 3.2 父组件和子组件
      • 3.2.1 父组件直接访问子组件 $children
      • 3.2.2 子组件访问父组件 $parent
      • 3.2.3 直接访问Vue实例(顶层)$root
    • 3.3 模板抽离
    • 3.4 组件data
    • 3.5 组件传值props(父组件-->子组件)
    • 3.6 组件事件监听$emit(子组件-->父组件)
      • 3.6.1 方法一
      • 3.6.2 方法二
    • 3.7 组件插槽slot
      • 3.7.1 具名插槽
      • 3.7.2 作用域插槽
      • 3.7.3 解构插槽
      • 3.7.4 动态插槽
    • 3.8 动态组件
  • 4 模块化开发
    • 4.1 CommonJS
    • 4.2 ES6 export/import
      • 4.2.1 export default
      • 4.2.2 统一全部导出

3 组件化开发

为了提高代码复用性,使用组件化开发,将一串拥有独立功能、可以接受参数、可渲染控件的代码封装起来,并给与一个名字,在使用时与其他html基础元素相同。

组件其实就是可复用的Vue实例,和new Vue一样,存在data、computed、watch、methods等属性,而el是根实例特有的!

注意:vue2要求组件模板为单根元素,vue3无该要求

组件构建三步骤:

  1. 创建组件构造器:Vue.extend()
  2. 注册组件:Vue.component()
  3. 使用

注意:目前建议直接使用注册组件方法一步到位合并1,2两步:

Vue.component("abc",{
	template:`
`
, })

Vue2学习笔记2——B站_第1张图片

组件使用:

DOCTYPE html>
<html lang="en">
<head>
    <script src="../js/vue.js">script>
head>
<body>
    <div id="app">
        <button-counter>button-counter>
    div>
    <script>
    // 全局组件注册
        Vue.component('button-counter', {
            data: function () {
                return {
                    count: 0
                }
            },
            template: ''
        })
        new Vue({
            el: '#app'
        })
    script>
body>
html>

对于组件的构成,分为单文件组件和多文件组件,单文件组件,对每个组件来说,只有一个文件同时包含html、css、js,而多文件则这几样分开存放,然后引入html作为组件

  1. 单文件组件
DOCTYPE html>
<html>

<head>
    <meta charset="utf-8" />
    <title>项目title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js">script>
head>

<body>
    <div id="root">div>
    <script>

        const Child = Vue.extend({
            template: `
child
`
}); const Parent = Vue.extend({ template: `
parent
`
, components:{ Child } }); new Vue({ el: "#root", template: `
`
, components: { Parent }, methods: { show() { alert("success"); } } });
script> body> html>
  1. 多文件组件(需要vue脚手架支持)
<template>
  <div id="app">
    <img alt="Vue logo" src="./assets/logo.png" width="25%">
    <HelloWorld msg="Hello Vue in CodeSandbox!"/>
  div>
template>

<script>
import HelloWorld from "./components/HelloWorld";

export default {
  name: "App",
  components: {
    HelloWorld
  }
};
script>

<style>
#app {
  font-family: "Avenir", Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
style>

3.1 全局组件和局部组件

全局组件:可以在所有的Vue实例下使用(可以有多个Vue实例,但是不推荐)

Vue.component('abc',abc)

局部组件:是在Vue实例下注册的组件

new Vue({
components:{
'abc':abc
}
})

3.2 父组件和子组件

在父组件构造器中注册的组件称为父组件的子组件

const cp2 = Vue.extend({
	template:`
		

222

`
, }) const cp1 = Vue.extend({ template:`

111

`
, components:{ cp2:cp2 } })

注意:如果想在当前作用域中使用自定义组件,必须在当前的作用域中注册该组件,不存在传递!

其实在父组件中,vue编译父组件的时候,子组件就被替换成对应的模板了,最终当前作用域中只存在父组件编译好的所有模板。即子组件不是在当前作用域中编译解析的,所以在当前作用域中需要使用子组件必须再次注册。

3.2.1 父组件直接访问子组件 $children

父组件如果向直接访问子组件可以使用:$children$refs

this.$children获取到的是当前组件的所有子组件,可以通过遍历来访问子组件中的方法或者属性

更好的目的性更明确地是使用$refs,使用方法:

  1. 在需要访问的组件上使用ref定义属性:
  2. 在需要访问的地方使用:this.$refs.a即可

3.2.2 子组件访问父组件 $parent

使用方法:this.$parent

不建议使用,耦合性太高了

3.2.3 直接访问Vue实例(顶层)$root

this.$root

3.3 模板抽离

将模板抽离出js内,放到html中去写,这样可以使用编辑器的代码提示功能,同时看起来比较方便

  1. html中使用script标签,类型为text/x-template
<html>
	<body><div id="root">div>body>
	<script type="text/x-template" id="cp1">
	<div>
		<h2>abc</h2>
	</div>
	script>
	<script>
	Vue.component('cp1',{template:'#cp1'}
	script>
html>
  1. 使用template标签
<template id="cp1">
<div>
	<h2>abch2>
div>
template>
<script>
Vue.component('cp1',{template:'#cp1'}
script>

3.4 组件data

组件的data必须是一个函数! 不能是一个对象,这样每个组件实例才能获得一份独立的数据拷贝!否则所有组件状态会相同!

data: function () {
  return {
    count: 0
  }
}

因为如果不是函数,则每次复用组件会导致组件内部状态一样,因为获取到的是同一个对象

3.5 组件传值props(父组件–>子组件)

Prop可以在组件上注册自定义的属性,props属性接收一个变量数组[]

Vue.component('blog-post', {
  props: ['title'],
  template: '

{{ title }}

'
})
<blog-post title="My journey with Vue">blog-post>

<blog-post :title="msg">blog-post>

props支持验证和默认值设置,这需要使用对象来传递

Vue.component('blog-post', {
  props: {
	title: String,
	message: [String, Number],
	name: {
		type: String,
		required: true,
		default: "pp"
		}
	hobby: {
		type: Array,
		default: function(){return ["jump","eat"]}
		}
	other: {
		type: Object,
		default: function(){return {abc:"aaa"}}
		}
	},
  template: '

{{ title }}

'
})

注意:这几种props写法都可以,一种是数组,一种是对象。其中对象写法可以定义类型和是否必须,当类型为数组或者对象时,对于低版本Vue的默认值必须是一个函数
类型可以定义为:String|Number|Boolean|Array|Object|Date|Function|Symbol

当然也可以自定义类型:

function Person(a, b){
	this.a = a
	this.b = b
}

props:{
	author: Person
}

注意:在标签属性中不支持驼峰命名,如果在vue对象中的变量是驼峰命名,可以在v-bind的属性上使用-进行分割,如:v-bind:abC=""转化成v-bind:ab-c=""

3.6 组件事件监听$emit(子组件–>父组件)

情景:当子组件事件需要让父组件监听行为并作出一些改变时:
方法一:子组件无需复杂操作,仅仅传递参数给父组件:使用v-on:自定义事件="函数"v-on:事件=$emit("自定义事件")组合来实现
方法二:子组件需要复杂操作,并传递参数给父组件:使用props传递函数

3.6.1 方法一

父组件:定义监听自定义事件

<blog-post v-on:enlarge-text="run">blog-post>

子组件:定义触发方法,抛出对应的自定义事件名

<button v-on:click="$emit('enlarge-text')">Enlarge textbutton>


<button v-on:click="crun">Enlarge textbutton>

相当于子组件通过标准事件抛出自定义事件,然后父组件监听到该自定义事件,并触发相应函数进行操作

有时候,需要对自定义事件传参,则须要用到$emit(自定义事件名,参数),使用第二个参数进行传参,传递的参数在监听函数中使用$event获取,此处的$event就是参数,可以直接使用


<button v-on:click="$emit('enlarge-text', 0.1)">Enlarge textbutton>

<blog-post v-on:enlarge-text="postFontSize += $event">blog-post>

更常用的是通过函数参数来获取

methods: {
  onEnlargeText: function (enlargeAmount) {
    this.postFontSize += enlargeAmount
  }
}

Vue2学习笔记2——B站_第2张图片

3.6.2 方法二

当然,对于子组件向父组件发起控制也可以通过props来做,具体思路:(由于回调函数定义在父组件中,可以更改父组件状态,而触发操作在子组件中,相当于子组件控制父组件)

  1. 父组件定义一个回调函数,将这个回调函数传递给子组件的绑定属性
  2. 子组件某个操作调用过程中调用父组件传过来的回调函数,并传递值即可

3.7 组件插槽slot

在 2.6.0 中,具名插槽和作用域插槽引入了一个新的统一的语法 (即 v-slot 指令)。它取代了 slot 和 slot-scope 这两个目前已被废弃但未被移除且仍在文档中的 attribute。新语法的由来可查阅这份 RFC。

插槽:让组件扩展性更强,抽取共性,插槽用来替换个性
插槽用于在自定义组件中放置可以显示在自定义标签中间的内容,即自定义标签中间的部分会替换到插槽位置

Vue.component('alert-box', {
  template: `
    
Error!
`
}) <alert-box> Something bad happened. </alert-box>

插槽中间可以放其他标签,作为默认值,如果自定义标签中间存在标签,则默认值被该标签替换,否则显示默认标签

123

如果存在多个插槽,则会重复替换,如果有多个标签,则会一起替换到插槽位置

特别注意:插槽上不要写css或者判断,因为会被直接替换掉!可以采取间接方法用div包裹插槽,然后在div上进行判断和样式

3.7.1 具名插槽

多插槽情况,如果插槽具有name属性,则必须替换时使用slot属性指定替换的插槽,v-slot:可以简写为#


<abc>
	<div slot="a">444div>
abc>

<abc>
	<div v-slot:a>444div>
	<div #a>444div>
abc>

<div class="abc">
  <strong>Error!strong>
  <slot name="a">slot>
  <slot name="b">slot>
  <slot name="c">slot>
div>

3.7.2 作用域插槽

作用域插槽相当于将本作用域的一些数据提供到外部使用,即暴露本作用域中的内容


<template id="myabc">
    <div>
        <slot :mydata="abc">
            <span>默认显示:{{abc}}span>
        slot>
    div>
template>

<script>
..
// 注册组件
myabc: {
    template: "#myabc",
    data:function(){
        return{
            abc: 1111
        }
    }
}
...
script>


<myabc>
    <template slot="default" slot-scope="myslot">
        <span>改写显示:{{myslot}}span>
    template>
myabc>

<myabc>
    <template v-slot:default="myslot">
        <span>改写显示:{{myslot}}span>
    template>
myabc>

分析使用过程:组件中在slot标签上定义自定义属性,然后赋值,在组件外部(即使用该组建的组件中)可以通过:slot-scope获取到内部的slot上定义的自定义属性!最终展示结果为:改写显示:{ "mydata": 1111 }

注意:默认插槽的缩写语法不能和具名插槽混用,因为它会导致作用域不明确。只要出现多个插槽,请始终为所有的插槽使用完整的基于