python 全栈开发,Day90(Vue组件,前端开发工具包)

python 全栈开发,Day90(Vue组件,前端开发工具包)

昨日内容回顾

1. Vue使用
    1. 生成Vue实例和DOM中元素绑定
    2. app.$el     --> 取出该vue实例绑定的DOM标签
    3. app.$data   --> 取出该vue实例绑定的data属性
2. 模板语法
    1. {
        {name}}     --> 在标签中间引用data属性中的变量
    2. v-html='msg' --> 在页面上显示我定义的标签元素
    
    3. v-if='ok'    --> 控制标签在不在DOM中
    4. v-show='!ok' --> 控制标签的display属性的值(是否显示)
    
    5. v-bind:href='url'  --> 将标签的属性和vue实例的data属性绑定起来
    6. v-on:click='dianwo'--> 给标签绑定一个点击事件,方法需要在vue实例的methods中定义
    
    7. v-model='article'  --> 用在input标签和textarea标签中,将用户输入和vue实例中的数据属性建立双向绑定

3. 计算属性和侦听器
    1. 计算属性
        (字符串翻转的例子)
        1. 计算属性需要定义在vue实例的 computed 中
        2. 多用于对变量做一些自定义的操作
        3. 我们在页面上像使用普通data属性一样使用计算属性
    2. 侦听器
        多用于一些复杂的运算或者异步操作
        侦听器需要放在 vue实例的 watch 中
4. class和style属性
    1. 基于对象的绑定
    2. 基于数组的绑定
5. 条件渲染
    v-if
        如果想控制多个标签的显示与否,可以使用 template 标签把它们包起来
    v-if/v-else
    v-if/v-else if /v-else
    
6. 列表渲染
    v-for循环
7. 事件处理
    详见2.模板语法
8. 表单绑定
    详见2.模板语法

二. 小清单
1. 对象的深拷贝
2. 根据索引删除数组中的元素
    splice(索引,删除个数,插入的元素)
View Code

 

一、Vue组件

组件基础

组件的组织

通常一个应用会以一棵嵌套的组件树的形式来组织:

python 全栈开发,Day90(Vue组件,前端开发工具包)_第1张图片

 将上图理解为一个网页

python 全栈开发,Day90(Vue组件,前端开发工具包)_第2张图片

例如,你可能会有页头、侧边栏、内容区等组件,每个组件又包含了其它的像导航链接、博文之类的组件。

为了能在模板中使用,这些组件必须先注册以便 Vue 能够识别。这里有两种组件的注册类型:全局注册和局部注册。至此,我们的组件都只是通过 Vue.component 全局注册的:

Vue.component('my-component-name', {
  // ... options ...
})

全局注册的组件可以用在其被注册之后的任何 (通过 new Vue) 新创建的 Vue 根实例,也包括其组件树中的所有子组件的模板中。

到目前为止,关于组件注册你需要了解的就这些了,如果你阅读完本页内容并掌握了它的内容,我们会推荐你再回来把组件注册读完。

 

 

组件注册

组件名

在注册一个组件的时候,我们始终需要给它一个名字。比如在全局注册时:

Vue.component('my-component-name', { /* ... */ })

该组件名就是 Vue.component 的第一个参数。

你给予组件的名字可能依赖于你打算拿它来做什么。当直接在 DOM 中使用一个组件 (而不是在字符串模板或单文件组件) 的时候,我们强烈推荐遵循 W3C 规范中的自定义组件名 (字母全小写且必须包含一个连字符)。这会帮助你避免和当前以及未来的 HTML 元素相冲突。

你可以在风格指南中查阅到关于组件名的其它建议。

 

组件名大小写

定义组件名的方式有两种:

使用 kebab-case

Vue.component('my-component-name', { /* ... */ })

当使用 kebab-case (短横线分隔命名) 定义一个组件时,你也必须在引用这个自定义元素时使用 kebab-case,例如 

使用 PascalCase

Vue.component('MyComponentName', { /* ... */ })

当使用 PascalCase (驼峰式命名) 定义一个组件时,你在引用这个自定义元素时两种命名法都可以使用。也就是说  和 都是可接受的。注意,尽管如此,直接在 DOM (即非字符串的模板) 中使用时只有 kebab-case 是有效的

全局注册

到目前为止,我们只用过 Vue.component 来创建组件:

Vue.component('my-component-name', {
  // ... 选项 ...
})

这些组件是全局注册的。也就是说它们在注册之后可以用在任何新创建的 Vue 根实例 (new Vue) 的模板中。比如:

js代码:

Vue.component('component-a', { /* ... */ })
Vue.component('component-b', { /* ... */ })
Vue.component('component-c', { /* ... */ })

new Vue({ el: '#app' })

html代码:

"app">

在所有子组件中也是如此,也就是说这三个组件在各自内部也都可以相互使用。

 

常规举例


"en">

    "UTF-8">
    Title
    
    


"app">
View Code

访问网页,点击按钮,效果如下:
python 全栈开发,Day90(Vue组件,前端开发工具包)_第3张图片

 

如果页面其他地方,也需要弹框效果呢?代码复制一遍?
这不符合编程习惯,需要使用组件

组件是可复用的 Vue 实例,且带有一个名字

组件举例


"en">

    "UTF-8">
    Title
    
    


"app">
View Code

刷新网页,效果同上!

 

在这个例子中是 ,就是一个自定义组件。它作为一个自定义元素来使用!

因为组件是可复用的 Vue 实例,所以它们与 new Vue 接收相同的选项,例如 datacomputedwatchmethods 以及生命周期钩子等。仅有的例外是像 el 这样根实例特有的选项。

 

组件的复用

你可以将组件进行任意次数的复用:

"app">
View Code

注意当点击按钮时,每个组件都会各自独立维护它的 count。因为你每用一次组件,就会有一个它的新实例被创建。

刷新网页,效果如下:

python 全栈开发,Day90(Vue组件,前端开发工具包)_第4张图片

 

data 必须是一个函数

当我们定义这个  组件时,你可能会发现它的 data 并不是像这样直接提供一个对象:

data: {
  count: 0
}

取而代之的是,一个组件的 data 选项必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝:

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

如果 Vue 没有这条规则,点击一个按钮就可能会影响到其它所有实例

 

举例:点击按钮,数字加1


"en">

    "UTF-8">
    Title
    
    


"app">
View Code

刷新网页,效果如下:

 

局部注册

全局注册往往是不够理想的。比如,如果你使用一个像 webpack 这样的构建系统,全局注册所有的组件意味着即便你已经不再使用一个组件了,它仍然会被包含在你最终的构建结果中。这造成了用户下载的 JavaScript 的无谓的增加。

在这些情况下,你可以通过一个普通的 JavaScript 对象来定义组件:

js代码

var ComponentA = { /* ... */ }
var ComponentB = { /* ... */ }
var ComponentC = { /* ... */ }

然后在 components 选项中定义你想要使用的组件:

new Vue({
  el: '#app'
  components: {
    'component-a': ComponentA,
    'component-b': ComponentB
  }
})

对于 components 对象中的每个属性来说,其属性名就是自定义元素的名字,其属性值就是这个组件的选项对象。

注意局部注册的组件在其子组件中不可用。例如,如果你希望 ComponentA 在 ComponentB 中可用,则你需要这样写:

var ComponentA = { /* ... */ }

var ComponentB = {
  components: {
    'component-a': ComponentA
  },
  // ...
}

 

举例:


"en">

    "UTF-8">
    Title
    
    


"app">
View Code

刷新网页,效果同上!

 

总结:

注意事项:
    组件中的data属性必须设置成一个函数!!!
1. 注册全局组件
    Vue.component(组件名,{
        template: ``,
        data: function(){
            return {}
        },
        methods: {...}
    })
2. 注册局部组件(当前vue实例才能使用的组件)
    new Vue({
        el: '#app',
        components: {
            alert: {
                template: ``,
                data: function(){
                    return {}
                },
                methods: {...}
            }
        }
    })
View Code

 

通过 Prop 向子组件传递数据

早些时候,我们提到了创建一个博文组件的事情。问题是如果你不能向这个组件传递某一篇博文的标题或内容之类的我们想展示的数据的话,它是没有办法使用的。这也正是 prop 的由来。

Prop 是你可以在组件上注册的一些自定义特性。当一个值传递给一个 prop 特性的时候,它就变成了那个组件实例的一个属性。为了给博文组件传递一个标题,我们可以用一个 props 选项将其包含在该组件可接受的 prop 列表中:

js代码:

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

{ { title }}

' })

一个组件默认可以拥有任意数量的 prop,任何值都可以传递给任何 prop。在上述模板中,你会发现我们能够在组件实例中访问这个值,就像访问 data 中的值一样。

一个 prop 被注册之后,你就可以像这样把数据作为一个自定义特性传递进来:

 html代码:

"My journey with Vue">
"Blogging with Vue">
"Why Vue is so fun">

效果图

python 全栈开发,Day90(Vue组件,前端开发工具包)_第5张图片

然而在一个典型的应用中,你可能在 data 里有一个博文的数组:

 js代码:

new Vue({
  el: '#blog-post-demo',
  data: {
    posts: [
      { id: 1, title: 'My journey with Vue' },
      { id: 2, title: 'Blogging with Vue' },
      { id: 3, title: 'Why Vue is so fun' }
    ]
  }
})
View Code

并想要为每篇博文渲染一个组件:

html代码:

post
  v-for="post in posts"
  v-bind:key="post.id"
  v-bind:title="post.title"
>
View Code

如上所示,你会发现我们可以使用 v-bind 来动态传递 prop。这在你一开始不清楚要渲染的具体内容,比如从一个 API 获取博文列表的时候,是非常有用的。

到目前为止,关于 prop 你需要了解的大概就这些了,如果你阅读完本页内容并掌握了它的内容,我们会推荐你再回来把 prop 读完。

 

举例:公司客服,每点击一次,表示接待了一个客户


"en">

    "UTF-8">
    Title
    
    


"app"> for="item in list" v-bind:name="item">
View Code

刷新网页,效果如下:

父子传值,一定要使用props。这里说的父,指的是网页整体。子,指的是tankuang局部组件。

网页点击之后,需要将值传给button按钮,最终展示数据!

关键步骤如下:

python 全栈开发,Day90(Vue组件,前端开发工具包)_第6张图片

1.定义了一个列表list,使用for循环遍历每一个元素

2.将值赋给item

3.item动态赋值给name

4.name传给props里面的name,name必须是字符串。

5.通过校验后,将值传给{ {name}},前端来渲染。

 

子组件向父组件传值

需要统计每一个客服,一共接待了多少客户。每增加一个客户,总数加1。


"en">

    "UTF-8">
    Title
    
    


"app">

本月接待客户数:{ {totalCount}}

for="item in list" v-bind:name="item">
View Code

刷新网页,效果如下:

python 全栈开发,Day90(Vue组件,前端开发工具包)_第7张图片

发现一个问题,总数没有变动?为什么呢?

因为这一段代码

methods:{
            ttt:function(){
                //自增
                this.count +=1
            }
        }

改变时,没有把外面的总数加1。那么如何改变父层的元素呢?需要使用自定义事件

 

自定义事件

事件名

跟组件和 prop 不同,事件名不存在任何自动化的大小写转换。而是触发的事件名需要完全匹配监听这个事件所用的名称。举个例子,如果触发一个 camelCase 名字的事件:

js代码:

this.$emit('myEvent')

则监听这个名字的 kebab-case 版本是不会有任何效果的:

html代码:

"doSomething">

跟组件和 prop 不同,事件名不会被用作一个 JavaScript 变量名或属性名,所以就没有理由使用 camelCase 或 PascalCase 了。并且 v-on 事件监听器在 DOM 模板中会被自动转换为全小写 (因为 HTML 是大小写不敏感的),所以 v-on:myEvent 将会变成 v-on:myevent——导致 myEvent 不可能被监听到。

因此,我们推荐你始终使用 kebab-case 的事件名

 

举例:


"en">

    "UTF-8">
    Title
    
    


"app">

本月接待客户数:{ {totalCount}}

for="item in list" v-bind:name="item" v-on:jiedai="add">
View Code

刷新网页,效果如下:

python 全栈开发,Day90(Vue组件,前端开发工具包)_第8张图片

这样设计的好处是,子组件只能修改自己的。如果需要修改父组件,必须通过自定义事件来完成!

 

插槽

插槽内容

Vue 实现了一套内容分发的 API,这套 API 基于当前的 Web Components 规范草案,将  元素作为承载分发内容的出口。

它允许你像这样合成组件:

html代码:

"/profile">
  Your Profile

然后你在  的模板中可能会写为:

html代码:

<a
  v-bind:href="url"
  class="nav-link"
>
  

当组件渲染的时候,这个  元素将会被替换为"Your Profile"。插槽内可以包含任何模板代码,包括 HTML:

"/profile">
  
  class="fa fa-user">
  Your Profile

甚至其它的组件:

html代码:

"/profile">
  
  "user">
  Your Profile

如果  没有包含一个  元素,则任何传入它的内容都会被抛弃。

 

举例:


"en">

    "UTF-8">
    Title
    
    


"app">

本月接待客户数:{ {totalCount}}

for="item in list" v-bind:name="item" v-on:jiedai="add"> 上班了!
View Code

刷新网页,效果如下:

python 全栈开发,Day90(Vue组件,前端开发工具包)_第9张图片

注意:template必须包含一个 元素,否则中的内容会被丢弃掉,网页不展示!

网页无法展示。template必须使用闭合标签,否则报错!

 

具名插槽

有些时候我们需要多个插槽。例如,一个假设的  组件多模板如下:

html代码:

class="container">
View Code

对于这样的情况, 元素有一个特殊的特性:name。这个特性可以用来定义额外的插槽:

class="container">
"header">
"footer">
View Code

在向具名插槽提供内容的时候,我们可以在一个父组件的