键盘敲烂,月薪过万作业不做,等于没学
当前系列: Vue.js 修改讲义

为什么需要?

几乎任意类型的应用界面都可以抽象为一个组件树:

复习:DOM树

但关键的关键,是有了组件,我们就可以重用(reuse)在各个页面反复出现的部分。

@悟@:如果没有组件(以及后面的route、脚手架等),vue就只能算是个类库,不能算是框架


先睹为快

官方文档定义:组件

本质上是一个拥有预定义选项的一个 Vue 实例

可以通过Vue.component()创建

Vue.component('todo-item', {//指定组件名称
    template: '<li>这是个待办项</li>'//指定template,模板
})

component()接收两个参数:

  • 组件名:建议组件名总是由多个单词组成,避免和HTML原有标签冲突(飞哥个人建议加前缀,比如yz-)。你可以使用:      
    • kebab-case(羊肉串命名法):如上所示
    • PascalCase(驼峰命名法):比如TodoItem,但HTML中使用该模板的时候,只能用相应的kebab-case(所以不建议)
    风格指南(回头再看,当做复习)
    PS:JavaScript中命名不允许使用短横线,所以组件被解析之后的名称为驼峰命名。
  • template:实际上就是组件要使用的一段HTML代码(现在知道我们为什么把vue所使用的HTML称之为模板了吧?)

然后,我们就可以使用todo-item实例了:(注意它放置的位置,演示移到#yz-hello以外)

    <form id="yz-hello">
        <input type="file" name="icon" id="" v-on:change="upload" ref="icon">
        <todo-item></todo-item>
    </form>
注意:Vue.component()一定要放在new Vue() 前面?,否则就会报错:

@想一想@:为什么呢?


注册:全局 vs 局部

通过Vue.component()生成的组件,注册(使用)之后,可以会被所有的模板使用:这种组件被称为全局组件。

演示:

<div id="fg-welcome">
    <yz-table></yz-table>
</div>
const welcome = new Vue({
    el: "#fg-welcome"  //模板,渲染
})
更多的时候我们会使用局部组件(因为冲突和性能):
  1. 声明一个JavaScript对象,将组件所需的数据赋值给它:
    const password = {
        template: '<a>忘记密码</a>'
    }
  2. 然后在vue的根实例下“按需挂载”
    components: {
        "forget-password": password
    },
    演示:不设置components


层层嵌套

在组件的template中还可以再嵌入组件:这样才能形成组件树

@想一想@:new Vue()生成的根实例是不是也是一个组件

template中使用:

  • 全局组件
    const password = {
        //直接在template中使用全局组件
        template: `<a><todo-item></todo-item>忘记密码</a>'
    }
  • 局部组件
    const icon = {
        template: '<span>图标</span>'
    }
    
    const password = {
        template: '<a><yz-icon></yz-icon>忘记密码</a>',
        components: {    //使用局部组件一样要显式注册
            "yz-icon": icon
        }
    }


单个根元素

当template由多个HTML元素组成时,需要注意:

必须要有一个且仅有一个根元素,能几个HTML元素并列,比如像这样:

template:  `<h3>{{ title }}</h3>
            <div v-html="content"></div>`
要把它们套到同一个HTML元素中,比如:(复习:v-html vs {{}}
<div class="blog-post">
  <h3>{{ title }}</h3>
  <div v-html="content"></div>
</div>


对比根实例

因为组件是可复用的 Vue 实例,所以它们与 new Vue 接收相同的选项,例如 data、computed、watch、methods 以及生命周期钩子等。但是:

el:DMO元素

el 是根实例特有的选项。

本质上是用一个页面已存在的DOM元素作为Vue实例的template。

data:数据

使用函数(的返回值),而不是直接赋值。

Vue.component('todo-item', {
    template: '<li>这是个待办项:{{pwd}}</li>',
    // data: function () { 两种写法都OK
    data() {
        return {
            pwd: '忘记密码'
        }
    }
})

否则,新版会直接报警告,老版会让该data被多个实例共享,造成很多不必要的问题……

再看Vue实例生命周期:有无template/el……


props:传值

我们可能:希望组件在不同的地方使用时,有不同的呈现内容。

可以像根实例一样,在组件的模板中绑定文本和属性,但是需要使用props(而不是data)

  1. 首先在声明组件时
    1. 引入props
      props: ["todo""url"],    //注意这是数组
    2. 在template中指定如何使用props
      template: '<a :href="url">{{todo}}</a>',
  2. 然后在使用组件时通过“属性”传递值:
    <todo-item todo="忘记密码" url="https://17bang.ren"></todo-item>

注意对比:


用于
数据来源
存储
组织形式
props
组件
HTML
键名
数组
data
根实例
JavaScript代码
键值对
对象
data()
组件
JavaScript代码
键值对
函数(返回值)

从data到props

实际开发中还有可能数据是存放在根实例的data中的,比如:
el: "#yz-hello",
data:{ dUrl: "https://17bang.ren/Code" },
这就需要我们使用之前的v-bind:
<forget-password :url="dUrl"></forget-password>

层层对应关系……

如果没有props

vue会把父组件template中声明的属性当做一个普通属性,传递给子组件template的根节点:

<todo-item style="color: red;"></todo-item>

todo-item的template是:

Vue.component('todo-item', {
    template: '<a>忘记密码 <span>(~ ̄(OO) ̄)ブ</span> </a>',
生成的HTML:
<a style="color: red;"> <span>(~ ̄(OO) ̄)ブ</span></a>
这个特性尝尝被用于父组件控制子组件的样式,还可以添加class、title啥的……,且子组件template中根元素上已经存在的style/class之类的值不会被覆盖。

但是,子组件不能像上文有props声明一样使用父组件中的prop值。(演示:去掉props中的url后报错


作业

  1. 想一想,以前实现的功能(完成的作业),有哪些可以适用于组件封装?应用这一章节学到的知识,试着做一做。

学习笔记
源栈学历
今天学习不努力,明天努力找工作

作业

觉得很 ,不要忘记分享哟!

任何问题,都可以直接加 QQ群:273534701

在当前系列 Vue.js 中继续学习:

多快好省!前端后端,线上线下,名师精讲

  • 先学习,后付费;
  • 不满意,不要钱。
  • 编程培训班,我就选源栈

更多了解 加:

QQ群:273534701

答疑解惑,远程debug……

B站 源栈-小九 的直播间

写代码要保持微笑 (๑•̀ㅂ•́)و✧

公众号:源栈一起帮

二维码