Vue第三篇(组件化开发与webpack详解)

目录(点击下面选项跳转知识点)

  • 知识概览
  • 一、组件化的实现和使用步骤(掌握)
  • 二、组件化的基本使用过程(掌握)
  • 三、全局组件和局部组件(掌握)
  • 四、父组件和子组件的区分(掌握)
  • 五、注册组件的语法糖写法(掌握)
  • 六、组件模板抽离(分离)的写法(掌握)
  • 七、为什么组件中的data必须是函数(理解)
  • 八、父子组件通信-父传子(掌握)
  • 九、父子组件通信-子级向父级传递(掌握)
  • 十、父子组件通信-结合双向绑定案例(掌握)
  • 十一、结合双向绑定案例-画图分析(掌握)
  • 十二、结合双向绑定案例-watch实现
  • 十三、父组件访问子组件-children-refs(掌握)
  • 十四、子组件访问父组件-parent-root(理解)
  • 组件高级化开端:十五、slot-插槽的基本使用(掌握)
  • 十六、slot-具名插槽的作用(掌握)
  • 十七、编译作用域的概念(理解)
  • 十八、作用域插槽的使用(掌握)
  • 十九、前端代码复杂带来的问题(理解)
  • 模块化开发的开端:二十、前端模块化雏形和CommonJS(理解)
  • 二十一、ES模块化的导入和导出(掌握)
  • 二十二、webpack的介绍和安装(理解)
  • 二十三、webpack的基本使用过程(掌握)
  • 二十四、webpack.config.js配置和package.json配置(掌握)
  • 二十五、webpack中使用css文件的配置(掌握)
  • 二十六、webpack-less文件的处理(掌握)
  • 二十七、webpack-图片文件的处理(掌握)
  • 二十八、webpack-ES6转ES5的babel(掌握)
  • VueJs模块化组件的开端:二十九、webpack-使用Vue的配置过程(掌握)
  • 三十、创建Vue时template和el关系(掌握)
  • 三十一、Vue的终极使用方案(重点掌握)【重点在vue文件开发】
  • 三十二、webpack-横幅Plugin的使用(掌握)
  • 三十三、webpack-HtmlWebpackPlugin的使用(掌握)
  • 三十四、webpack-UglifyjsWebpackPlugin的使用(掌握)
  • 三十五、webpack-dev-server搭建本地服务器(掌握)
  • 三十六、webpack-配置文件的分离(掌握)

知识概览

返回目录

Vue第三篇(组件化开发与webpack详解)_第1张图片

一、组件化的实现和使用步骤(掌握)

返回目录

什么是组件化?

  • 人在面对复杂问题的处理方式:
    • 任何一个人处理信息的逻辑能力是有限的
    • 所以,当面对一个非常复杂的问题时,我们不太可能一次性搞定一大堆的内容
    • 但是,我们人有一种天生的能力,就是将问题拆解
    • 如果将一个复杂的问题,拆分成很多个可以处理的小问题,再将其放在整体当中,你会发现大的问题也会迎刃而解。
  • 组件化也是类似的思想:
    • 如果我们将一个页面中所有的处理逻辑全部放在一起,处理起来就会变得非常复杂,而且不利于后续的管理以及扩展
    • 但如果,我们将一个页面拆分成一个个小的功能块,每个功能块完成属于自己这部分独立的功能,那么之后整个页面的管理和维护就变得非常容易了。

Vue第三篇(组件化开发与webpack详解)_第2张图片
Vue的组件思想

  • 组件化是Vue.js中的重要思想
    • 它提供了一种抽象,让我们可以开发出一个个独立可复用的小组件来构造我们的应用
    • 任何的应用都会被抽象成一颗组件树

Vue第三篇(组件化开发与webpack详解)_第3张图片
组件化思想的应用:

  • 有了组件化的思想,我们在之后的开发中就要充分的利用它
  • 尽可能的将页面拆分成一个个小的、可复用的组件
  • 这样让我们的代码更加方便组织和管理,并且扩展性也更强。

注册组件的基本步骤

  • 组件的使用分成三个步骤:
    • 创建组件构造器
    • 注册组件
    • 使用组件
      Vue第三篇(组件化开发与webpack详解)_第4张图片

二、组件化的基本使用过程(掌握)

返回目录

步骤:

  • 创建组件构造器: 调用 Vue.extend() 方法创建组件构造器
  • 注册组件:调用 Vue.component() 方法注册组件
  • 使用组件:在 Vue 实例的作用范围内使用组件
DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>Titletitle>
head>
<body>

<div id="app">
    <cpn>cpn>
    <cpn>cpn>
    <cpn>cpn>
    <cpn>cpn>
div>

<script src="../js/vue.js">script>
<script>
    //ES6用``可以代替字符串并不需要+拼接来换行写

    // 1.创建组件构造器对象
    const cpnC = Vue.extend({
        template:`
          

我是标题

我是内容,哈哈哈哈

我是内容,呵呵呵呵

`
}); //注册组件 Vue.component('cpn',cpnC) const app = new Vue({ el: '#app', data: { message: '你好啊' } });
script> body> html>

Vue第三篇(组件化开发与webpack详解)_第5张图片
注册组件步骤解析:
Vue.extend():

  • 调用 Vue.extend() 创建的是一个组件构造器
  • 通常在创建组件构造器时,传入 template 代表我们自定义组件的模板
  • 该模板就是在使用到组件的地方,要显示的HTML代码
  • 事实上,这种写法在Vue2.x的文档中几乎已经看不到了,它会直接使用下面我们会讲
  • 的语法糖,但是在很多资料还是会提到这种方式,而且这种方式是学习后面方式的基础。

Vue.component():

  • 调用 Vue.component() 是将刚才的组件构造器注册为一个组件,并且给它起一个组件的标签名称
  • 所以需要传递两个参数:1、注册组件的标签名。 2、组件构造器

组件必须挂载在某个Vue实例下,否则它不会生效

我们来看下面我使用了三次 ,而第三次其实并没有生效
Vue第三篇(组件化开发与webpack详解)_第6张图片

三、全局组件和局部组件(掌握)

返回目录

当我们调用Vue.component()注册组件时,组件的注册是全局的

  • 这意味着该组件可以在任意Vue示例下使用
  • 但是如果我们注册的组件是挂载在某个实例中,那么就是一个局部组件
DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>Titletitle>
head>
<body>
<div id="app2">
    <cpn>cpn>
div>

<div id="app">
    <cpn>cpn>
    <cpn>cpn>
    <cpn>cpn>
div>

<script src="../js/vue.js">script>
<script>
    // 1.创建组件构造器
    const cpnC = Vue.extend({
      template:`
          

我是标题

我是内容,哈哈哈哈

我是内容,呵呵呵呵

`
}); // 2.注册组件(全局组件,意味着可以在多个Vue的实例下面使用) /*Vue.component('cpn',cpnC)*/ const app = new Vue({ el: '#app', data: { message: '你好啊' }, components:{ // cpn使用组件时的标签名,局部注册!!! cpn: cpnC } }); const app2=new Vue({ el:'#app2' })
script> body> html>

Vue第三篇(组件化开发与webpack详解)_第7张图片

通过Vue.component()方法注册的组件是全局组件,通过 components 注册的是私有子组件

四、父组件和子组件的区分(掌握)

返回目录

DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>Titletitle>
head>
<body>
    <div id="app">
        <cpn2>cpn2>
    div>

    <script src="../js/vue.js">script>
    <script>
        // 1.创建第一个组件构造器(子组件)<只能在父组件中使用,想要外部使用,在外部也要进行注册>
        const cpnC1=Vue.extend({
          template:`
            

我是标题

我是内容,哈哈哈

`
}) // 2.创建第一个组件构造器(父组件) const cpnC2=Vue.extend({ template:`

我是标题2

我是内容,呵呵呵

`
, components:{ cpn1:cpnC1 } }) // root组件 const app = new Vue({ el: '#app', data: { message: '你好啊' }, components: { cpn2: cpnC2 } });
script> body> html>

Vue第三篇(组件化开发与webpack详解)_第8张图片
父子组件的错误用法: 以子组件的形式在 Vue 实例中使用

  • 因为当子组件注册到父组件的 components 时,Vue 会编译好父组件的模块
  • 该模板的内容已经决定了父组件将要渲染的 HTML(相当于父组件中已经有了子组件的内容了)
  • 是只能在父组件中被识别的
  • 类似这种用法, 是会被浏览器忽略的。
<div id="app">
  <cpn2>cpn2>
    
  
div>

<script src="../js/vue.js">script>
<script>
  // 1.创建第一个组件构造器(子组件)
  const cpnC1 = Vue.extend({
    template: `
      

我是标题1

我是内容, 哈哈哈哈

`
}) // 2.创建第二个组件构造器(父组件) const cpnC2 = Vue.extend({ template: `

我是标题2

我是内容, 呵呵呵呵

`
, components: { // 在父组件中注册子组件,这样就可以在父组件里面使用子组件 // 例如上面的 cpn1: cpnC1 } }) // root组件 const app = new Vue({ el: '#app', data: { message: '你好啊' }, components: { // cpn1子组件在cpn2父组件中注册,父组件cpn2在Vue实例里面注册 cpn2: cpnC2 } })
script>

五、注册组件的语法糖写法(掌握)

返回目录

Vue 为了简化这个过程,提供了注册的语法糖
主要是省去了调用 Vue.extend() 的步骤,而是可以直接使用一个对象来代替

DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>Titletitle>
head>
<body>
<div id="app">
    <cpn>cpn>
    <cpn2>cpn2>
div>

<script src="../js/vue.js">script>
<script>
    // 1.全局组件注册的语法糖
    // 不推荐的写法:const cpn = Vue.extend()

    //2.注册组件(全局组件)
    Vue.component('cpn',{
      template:`
            

我是标题

我是内容,哈哈哈

`
}) const app = new Vue({ el: '#app', data: { message: '你好啊' }, components:{ 'cpn2':{ template: `

我是标题2

我是内容,哈哈哈

`
} } });
script> body> html>

Vue第三篇(组件化开发与webpack详解)_第9张图片

六、组件模板抽离(分离)的写法(掌握)

返回目录

通过语法糖简化了 Vue 组件的注册过程,另外还有一个地方的写法比较麻烦,就是 template 模块写法

  • 如果我们能将其中的HTML分离出来写,然后挂载到对应的组件上,必然结构会变得非常清晰
  • Vue 提供了两种方案来定义HTML模板内容
    • 使用 < script > 标签
    • 使用 < template > 标签

使用 script 标签:

DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>Titletitle>
head>
<body>
<div id="app">
    <cpn>cpn>
    <cpn>cpn>
    <cpn>cpn>
div>


<script type="text/x-template" id="cpn">
    <div>
        <h2>我是标题</h2>
        <p>我是内容,哈哈哈</p>
    </div>
script>

<script src="../js/vue.js">script>
<script>

  // 1.注册一个全局组件
  Vue.component('cpn', {
    template: '#cpn'
  })

  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊'
    }
  })
script>

body>
html>

重点:使用template标签

DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>Titletitle>
head>
<body>

<div id="app">
    <cpn>cpn>
    <cpn>cpn>
    <cpn>cpn>
div>


<template id="cpn">
    <div>
        <h2>我是标题h2>
        <p>我是内容,呵呵呵p>
    div>
template>

<script src="../js/vue.js">script>
<script>

  // 1.注册一个全局组件
  Vue.component('cpn', {
    template: '#cpn'
  })

  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊'
    }
  })
script>

body>
html>

七、为什么组件中的data必须是函数(理解)

返回目录

组件可以访问Vue实例数据吗?
Vue第三篇(组件化开发与webpack详解)_第10张图片
结论:组件不能直接访问Vue实例中的 data
注:即使访问放也不建议写在Vue实例中

组件是一个单独功能模块的封装:

  • 这个模块有属于自己的 HTML 模板,也应该有属于自己的数据 data

组件自己的数据存放在哪呢?

  • 组件对象也有一个 data 属性(也可以有 methods 属性)
  • 只是这个 data 属性必须是一个函数
  • 而且这个函数返回一个对象,对象内部保存着数据

Vue第三篇(组件化开发与webpack详解)_第11张图片

<div id="app">
  <cpn>cpn>
  <cpn>cpn>
  <cpn>cpn>
div>


<template id="cpn">
  <div>
    <h2>{{title}}h2>
    <p>我是内容,呵呵呵p>
  div>
template>

<script src="../js/vue.js">script>
<script>

  // 1.注册一个全局组件
  Vue.component('cpn', {
    template: '#cpn',
    data() {
      return {
        title: 'abc'
      }
    }
  })

  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊',
      // title: '我是标题'
    }
  })
script>

为什么 data 在组件中必须是一个函数呢?

  • 首先,如果不算是一个函数,Vue 直接就会报错
  • 其次,原因是在于 Vue 让每个组件对象都返回一个新的对象,因为如果是同一个对象,组件在多次使用后会相互影响(为了达到相互隔离的效果)

八、父子组件通信-父传子(掌握)

返回目录

在上一个小节中,我们提到了子组件是不能引用父组件或者Vue实例的数据的

但是,在开发中,往往一些数据确实需要从上层传递到下层

  • 比如在一个页面中,我们从服务器请求到了很多的数据
  • 其中的一部分数据,并非是我们整个页面的大组件来展示的,而是需要下面的子组件进行展示
  • 这个时候,并不会让子组件再次发送一个网络请求,而是直接让大组件(父组件) 将数据传递给小组件(子组件)

如何进行父子组件间的通信呢?

  • 通过 props 向子组件传递数据
  • 通过事件向父组件发送消息

Vue第三篇(组件化开发与webpack详解)_第12张图片

props 基本用法

在组件中,使用选项 props 来声明需要从父级接收到的数据(properties)
props 的值有两种方式:

  • 方式一:字符串数组,数组中的字符串就是传递时的名称
  • 方式二:对象,对象可以设置传递时的类型,也可以设置默认值等。

传数组:

<div id="app">
    
  <cpn :cmessage="message" :cmovies="movies">cpn>
div>


<template id="cpn">
  <div>
    <h1>{{cmovies}}}h1>
    <h1>{{cmessage}}h1>
  div>
template>

<script src="../js/vue.js">script>
<script>
  // 1.创建组件构造器(子组件)
  const cpn = {
    template: '#cpn',
    props: ['cmovies', 'cmessage']	//父传子,props
  }
  // 3.注册组件(将子组件在父组件里面注册)
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊',
      movies: ['海王', '海贼王', '海尔兄弟']
    },
    components: {
      //对象字面量增强写法的属性增强写法
      cpn
    }
  })
script>

Vue第三篇(组件化开发与webpack详解)_第13张图片
注意:我们在使用组件时,需要用v-bind 动态绑定数据。

传对象:

  • 在前面,我们的 props 选项是使用一个数组
  • 除了数组之外,我们也可以使用对象,当需要对props 进行类型等验证时,就需要对象写法了

验证支持的数据类型有:

  • String
  • Number
  • Boolean
  • Array
  • Object
  • Date
  • Function
  • Symbol

①类型限制
我们可以在 props 里面限制父组件给子组件传递的数据类型


<div id="app">
  <cpn :cmessage="message" :cmovies="movies">cpn>
div>


<template id="cpn">
  <div>
    <h1>{{cmovies}}}h1>
    <h1>{{cmessage}}h1>
  div>
template>

<script src="../js/vue.js">script>
<script>
  // 父传子: props
  const cpn = {
    template: '#cpn',
    props: {
      // 1.类型限制
      cmovies: Array,		// 限制父组件传的是数组类型
      cmessage: String,		// 限制父组件传的是字符串类型
    }

  }
  // root组件,我们当作父组件
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊',
      movies: ['海王', '海贼王', '海尔兄弟']
    },
    components: {
      //对象字面量增强写法的属性增强写法
      cpn
    }
  })
script>

②默认值和必传值

  • type : 限制的类型
  • default : 如果没有传值,给一个默认值
    • 注意:类型是对象或者数组时, 默认值必须是一个函数
  • required : 必须的,即意味着这个值是必须要传递的,不传就报错
<div id="app">
    
  <cpn :cmessage="message" :cmovies="movies">cpn>
div>

<template id="cpn">
  <div>
    <h1>{{cmovies}}}h1>
    <h1>{{cmessage}}h1>
  div>
template>

<script src="../js/vue.js">script>
<script>
  // 父传子: props
  const cpn = {
    template: '#cpn',
    // props: ['cmovies', 'cmessage'],
    props: {

      // 2.提供一些默认值, 以及必传值
      cmessage: {
        type: String,         // 类型限制为 String
        default: 'aaaaaaaa',  // 如果没有传值,则给一个默认值
        required: true        // required 必须的,即意味着这个值是必须要传递的,不传就报错
      },
      // 类型是对象或者数组时, 默认值必须是一个函数
      cmovies: {
        type: Array,
        default() {
          return []
        }
      }
    },


  // root组件,我们当作父组件
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊',
      movies: ['海王', '海贼王', '海尔兄弟']
    },
    components: {
      //对象字面量增强写法的属性增强写法
      cpn
    }
  })
script>

③自定义类型
当我们有自定义构造函数时,验证也支持自定义的类型
Vue第三篇(组件化开发与webpack详解)_第14张图片
④props驼峰标识
当我们 props 里面的属性是驼峰写法的时,在传入值时需要进行 - 连接

<div id="app">
  
  <cpn :c-info="info" :child-my-message="message" >cpn>
div>

<template id="cpn">
  <div>
    <h2>{{cInfo}}h2>
    <h2>{{childMyMessage}}h2>
  div>
template>

<script src="../js/vue.js">script>
<script>
  const cpn = {
    template: '#cpn',
    props: {
      // 驼峰写法cInfo
      cInfo: {
        //类型是对象或者数组时, 默认值必须是一个函数
        type: Object,
        default() {
          return {}
        }
      },
      childMyMessage: {
        type: String,
        default: ''
      }
    }
  }

  const app = new Vue({
    el: '#app',
    data: {
      info: {
        name: 'why',
        age: 18,
        height: 1.88
      },
      message: 'aaaaaa'
    },
    components: {
      cpn
    }
  })
script>

九、父子组件通信-子级向父级传递(掌握)

返回目录

  • props 用于父组件向子组件传递数据,还有一种比较常见的是子组件传递数据或事件到父组件去
  • 这个时候,我们需要使用自定义事件来完成

什么时候需要自定义事件呢?

  • 当子组件需要向父组件传递数据时,就要用到自定义事件了
  • 我们之前学习的 v-on 不仅仅可以用于监听 DOM 事件,也可以用于组件间的自定义事件

自定义事件的流程:

  • 在子组件中,通过$emit() 来触发事件
  • 在父组件中,通过v-on 来监听子组件事件

我们来看一个简单的例子:

  • 我们之前做过一个两个按钮 +1 和 -1,点击后修改 counter

  • 我们整个操作的过程还是在子组件中完成,但是之后的展示交给父组件

  • 这样,我们就需要将子组件中的 counter,传给父组件的某个属性,比如total

Vue第三篇(组件化开发与webpack详解)_第15张图片

<body>


<div id="app">
  
  <cpn @item-click="cpnClick">cpn>
div>


<template id="cpn">
  <div>
    <button v-for="item in categories"
            @click="btnClick(item)">
      {{item.name}}
    button>
  div>
template>

<script src="../js/vue.js">script>
<script>

  // 1.子组件
  const cpn = {
    template: '#cpn',
    data() {
      return {
        categories: [
          {id: 'aaa', name: '热门推荐'},
          {id: 'bbb', name: '手机数码'},
          {id: 'ccc', name: '家用家电'},
          {id: 'ddd', name: '电脑办公'},
        ]
      }
    },
    methods: {
      btnClick(item) {
        // console.log(item);
        // 1.发射事件: 自定义事件
        // 第一个参数是自定义事件的名称,第二个参数是自定义事件的参数
        this.$emit('item-click', item)//发射
      }
    }
  }

  // 2.父组件
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊'
    },
    components: {
      cpn
    },
    methods: {
      // 3.父组件里面定义方法处理
      cpnClick(item) {
        console.log('cpnClick', item);
      }
    }
  })
script>

body>

Vue第三篇(组件化开发与webpack详解)_第16张图片

十、父子组件通信-结合双向绑定案例(掌握)

返回目录

DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>Titletitle>
head>
<body>

<div id="app">
    <cpn :number1="num1" :number2="num2" @num1change="num1change" @num2change="num2change">cpn>
div>
<template id="cpn">
    <div>
        <h2>props:{{number1}}h2>
        <h2>data:{{dnumber1}}h2>
            
            <input type="text" :value="dnumber1" @input="num1Input">
        <h2>props:{{number2}}h2>
        <h2>data:{{dnumber2}}h2>
            
            <input type="text" :value="dnumber2" @input="num2Input">
    div>
template>
<script src="../js/vue.js">script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      num1:1,
      num2:0
    },
    methods:{
      num1change(value){
        this.num1=Number(value);
      },
      num2change(value){
        this.num2=Number(value);
      }
    },
    components:{
      cpn:{
        template:'#cpn',
        props:{
          number1:Number,
          number2:Number,
        },
        data(){
          return{
            dnumber1:this.number1,
            dnumber2:this.number2
          }
        },
        methods:{
          num1Input(event){
            this.dnumber1=event.target.value;
            this.$emit('num1change',this.dnumber1)
          },
          num2Input(event){
            this.dnumber2=event.target.value;
            this.$emit('num2change',this.dnumber2)
          }
        }
      }
    }
  });
script>

body>
html>

十一、结合双向绑定案例-画图分析(掌握)

返回目录

Vue第三篇(组件化开发与webpack详解)_第17张图片

十二、结合双向绑定案例-watch实现

返回目录

DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>Titletitle>
head>
<body>
<div id="app">
    <cpn :number1="num1" :number2="num2" @num1change="num1change" @num2change="num2change">cpn>
div>
<template id="cpn">
    <div>
        <h2>props:{{dnumber1}}h2>
        <h2>data:{{dnumber1}}h2>
        <input type="text" v-model="dnumber1">
        <h2>props:{{dnumber2}}h2>
        <h2>data:{{dnumber2}}h2>
        <input type="text" v-model="dnumber2">
    div>
template>

<script src="../js/vue.js">script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      num1:1,
      num2:0
    },
    methods:{
      num1change(value){
        this.num1=value;
      },
      num2change(value){
        this.num2=value;
      }
    },
    components:{
      cpn:{
        template:'#cpn',
        props:{
          number1:Number,
          number2:Number,
        },
        data(){
          return{
            dnumber1:this.number1,
            dnumber2:this.number2
          }
        },
        watch:{
          dnumber1(newValue){
            this.dnumber1=newValue
            this.$emit('num1change',this.newValue)
          },
          dnumber2(newValue){
            this.dnumber2=newValue
            this.$emit('num2change',this.newValue)
          }
        }
      }
    }
  });
script>
body>
html>

十三、父组件访问子组件-children-refs(掌握)

返回目录

有时候我们需要父组件直接访问子组件,子组件直接访问父组件,或者是子组件访问根组件。

  • 父组件访问子组件:使用 $children 或 $refs

  • 子组件访问父组件:使用$parent

我们先来看下$children (它是一个复数)的访问

  • this.$children 是一个数组类型,它包含所有子组件对象

$children 的缺陷:

  • 通过 $children 访问子组件时,是一个数组类型,访问其中的子组件必须通过索引值
  • 但是当子组件过多,我们需要拿到其中一个时,往往不能确定它的索引值,甚至还可能会发生变化
  • 有时候,我们想明确获取其中一个特定的组件,这个时候就可以使用 $refs

$refs 的使用

  • $ refs 和 ref 指令通常是一起使用的
  • 首先,我们通过 ref 给某一个子组件绑定一个特定的 ID
  • 其次,通过 this.$refs.ID 就可以访问到该组件了
DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>Titletitle>
head>
<body>

<div id="app">
    <cpn>cpn>
    <cpn>cpn>
    <cpn ref="aaa">cpn>
    <button @click="btnClick">按钮button>
div>


<template id="cpn">
    <div>我是子组件div>
template>

<script src="../js/vue.js">script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊'
    },
    methods: {
      btnClick() {
        // 2.$refs => 对象类型, 默认是一个空的对象 必须在组件上加 ref='bbb'
        /*console.log(this.$children)//$children不常使用因为不建议使用下标值取值
        this.$children[0].showMessage();
        this.$children[0].name;*/
        console.log(this.$refs.aaa.name);
      }
    },
    components: {
      cpn: {
        template: '#cpn',
        data() {
          return {
            name: '我是子组件的name'
          }
        },
        methods: {
          showMessage(){
            console.log('我是子组件的方法')
          }
        }
      },
    }
  })
script>
body>
html>

Vue第三篇(组件化开发与webpack详解)_第18张图片

十四、子组件访问父组件-parent-root(理解)

返回目录

如果我们想在子组件中直接访问父组件,可以通过 $parent

  • 尽管在 Vue 开发中,我们允许通过 $parent 来访问父组件,但是在真实开发中尽量不要这要做

  • 子组件应该尽量避免直接访问父组件的数据,因为这样耦合度太高了

  • 如果我们将子组件放入另外一个组件之内,很可能该父组件没有对应的属性,往往会引起问题

$parent(了解即可)

DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>Titletitle>
head>
<body>

<div id="app">
    <cpn>cpn>
div>


<template id="cpn">
    <div>
        <h2>我是cpn组件h2>
        <ccpn>ccpn>
    div>
template>

<template id="ccpn">
    <div>
        <h2>我是ccpn子组件h2>
        <button @click="btnClick">按钮button>
    div>
template>

<script src="../js/vue.js">script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊'
    },
    components: {
      cpn: {
        template: '#cpn',
        data(){
          return{
            name:'我是cpn组件的name'
          }
        },
        components: {
          ccpn: {
            template: '#ccpn',
            methods: {
              btnClick() {
                // 1.访问父组件$parent
                console.log(this.$parent)
                // 2.访问父组件的name
                console.log(this.$parent.name);
              }
            }
          }
        }
      }
    }
  })
script>
body>
html>

Vue第三篇(组件化开发与webpack详解)_第19张图片
$root(也用的比较少)
Vue第三篇(组件化开发与webpack详解)_第20张图片

Vue第三篇(组件化开发与webpack详解)_第21张图片

组件化高级

十五、slot-插槽的基本使用(掌握)

返回目录

为什么使用slot

slot翻译为插槽

  • 在生活中很多地方都有插槽,电脑的USB插槽,插板当中的电源插槽。
  • 插槽的目的是让我们原来的设备具备更多的扩展性
  • 比如电脑的USB我们可以插入U盘、硬盘、手机、音响、键盘、鼠标等等

组件的插槽

  • 组件的插槽也是为了我们封装的组件更加具有扩展性
  • 让使用者可以决定组件内部的一些内容到底展示什么

栗子:移动网站中的导航栏

  • 移动开发中,几乎每个页面都有导航栏
  • 导航栏我们必然会封装成一个插件,比如nav-bar组件
  • 一旦有了这个组件,我们就可以在多个页面中复用了

如何封装这类组件呢?slot

如何封装这类组件呢?

  • 它们也有很多区别,但是也有很多共性
  • 如果,我们每一个单独去封装一个组件,显然不合适:比如每个页面都返回,这部分内容我们就要重复去封装
  • 但是,我们封装成一个,好像也不合理;有些左侧是菜单,有些是返回,有些中间是搜索,有些是文字

如何封装合适呢?抽取共性,保留不同

  • 最好的封装方式就是将共性抽取到组件中,将不同暴露为插槽
  • 一旦我们预留了插槽,就可以让使用者根据自己的需求,决定插槽中插入什么内容
  • 是搜索框,还是文字,还是菜单。由调用者自己来决定
  • 这就是我们为什么要学习组件中的插槽slot的原因

说明图:
Vue第三篇(组件化开发与webpack详解)_第22张图片

DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>Titletitle>
head>
<body>
<div id="app">
    <cpn><button>我是按钮button>cpn>
    <cpn><span>我是spanspan>cpn>
    <cpn>
        <cpn>cpn>
    cpn>
    <cpn>
        <button>按钮1button>
        <button>按钮2button>
        <button>按钮3button>
    cpn>
div>

<template id="cpn">
    <div>
        <h2>我是组件h2>
        <p>我是组件,哈哈哈p>
        <slot><button>外界未指定则显示此默认button>slot>
    div>
template>

<script src="../js/vue.js">script>

<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊'
    },
    components:{
      cpn: {
        template:'#cpn'
      }
    }
  });
script>

body>
html>

运行结果:
Vue第三篇(组件化开发与webpack详解)_第23张图片

十六、slot-具名插槽的作用(掌握)

返回目录

当子组件的功能复杂时,子组件的插槽可能并非是一个。

  • 比如我们封装一个导航栏的子组件,可能就需要三个插槽,分别代表左边、中间、右边。
  • 那么,外面在给插槽插入内容时,如何区分插入的是哪一个呢?p这个时候,我们就需要给插槽起一个名字

如何给插槽起名字呢?

  • 给插槽起一个名字,只要给 slot 元素一个 name 属性即可
DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>Titletitle>
head>
<body>
<div id="app">
   <cpn><span  slot="center">中间标题span><button slot="right">右边按钮button>cpn>
div>

<template id="cpn">
    <div>
        <slot name="left"><span>左边span>slot>
        <slot name="center"><span>中间span>slot>
        <slot name="right"><span>右边span>slot>
        
    div>
template>

<script src="../js/vue.js">script>

<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊'
    },
    components:{
      cpn: {
        template:'#cpn'
      }
    }
  });
script>

body>
html>

Vue第三篇(组件化开发与webpack详解)_第24张图片

十七、编译作用域的概念(理解)

返回目录

  • 在真正学习插槽之前,我们需要先理解一个概念:编译作用域
  • 官方对于编译的作用域解析比较简单,我们自己来通过一个例子来理解这个概念:
  • 我们来考虑下面的代码是否最终是可以渲染出来的:
    • < my-cpn v-show=“isShow”>中,我们使用了isShow属性
    • isShow属性包含在组件中,也包含在Vue实例中
DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>Titletitle>
head>
<body>
<div id="app">
    <cpn v-show="isShow">cpn>
div>

<template id="cpn">
    <div>
        <h2>我是子组件h2>
        <p>我是内容哈哈哈p>
        <button v-show="isShow">按钮button>
    div>
template>

<script src="../js/vue.js">script>

<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊',
      isShow: true
    },
    components:{
      cpn: {
        template:'#cpn',
        data(){
          return{
            isShow:false
          }
        }
      }
    }
  });
script>

body>
html>

答案:最终可以渲染出来,也就是使用的是 Vue 实例的属性

为什么呢?

  • 官方给出了一条准则:父组件模板的所有东西都会在父级作用域内编译;子组件模板的所有东西都会在级作用域内编译
  • 而我们在使用 < my-cpn v-show=“isShow”> 的时候,整个组件的使用过程相当于在父组件中出现的
  • 那么它的作用域就是父组件,使用的属性也是属于父组件的属性
  • 因此 ,isShow 使用的是 Vue 实例中的属性,而不是子组件的属性

十八、作用域插槽的使用(掌握)

返回目录

一句话总结:父组件替换插槽的标签,但是内容由子组件来提供

我们先提一个需求:

  • 子组件包括一组数据,比如:PLanguages:[‘javascript’,‘python’,‘Swift’,‘Go’,‘C++’]
  • 需要在多个界面进行展示
    • 某些界面是以水平方向一一展示的
    • 某些界面是以列表形式展示
    • 某些界面直接展示一个数组
  • 内容在子组件,希望父组件告诉我们如何展示,怎么办呢?
    • 利用 slot 作用域插槽就可以了
DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>Titletitle>
head>
<body>
<div id="app">
    <cpn>cpn>
    <cpn>
        
        
       <template v-slot="slot">
            <span v-for="item in slot.data">{{item}}-span>
        template>
    cpn>
    <cpn>
        <template slot-scope="slot">
            <span v-for="item in slot.data">{{item}}***span>
        template>
    cpn>
    <cpn>
        <template slot-scope="slot">
            <span>{{slot.data.join('***')}}span>
        template>
    cpn>
div>

<template id="cpn">
    <div>
        <slot :data="pLanguages">
            <ul>
                <li v-for="item in pLanguages">{{item}}li>
            ul>
        slot>
    div>
template>

<script src="../js/vue.js">script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: '你好啊'
    },
    components:{
      cpn:{
        template:'#cpn',
        data(){
          return{
            pLanguages:['javascript','python','Swift','Go','C++','java','C#']
          }
        }
      }
    }
  });
script>
body>
html>

Vue第三篇(组件化开发与webpack详解)_第25张图片

十九、前端代码复杂带来的问题(理解)

返回目录

模块化开发引入

JavaScript原始功能

  • 在网页开发的早期,js制作作为一种脚本语言,做一些简单的表单验证或动画实现等,那个时候代码还是很少的。

    • 那个时代的代码是怎么写的呢?直接将代码写在可以去掉
      在webpack.config.js中配置:
      Vue第三篇(组件化开发与webpack详解)_第126张图片
      Vue第三篇(组件化开发与webpack详解)_第127张图片
      打包:
      Vue第三篇(组件化开发与webpack详解)_第128张图片

      三十四、webpack-UglifyjsWebpackPlugin的使用(掌握)

      返回目录

      在项目发布之前,我们必然需要对js等文件进行压缩处理

      • 这里,我们就对打包的js文件进行压缩
      • 我们使用一个第三方的插件uglifyjs-webpack-plugin,且版本号指定1.1.1,和CLI2保持一致
      npm install [email protected] --save-dev
      

      修改webpack.config.js文件,使用插件:
      Vue第三篇(组件化开发与webpack详解)_第129张图片
      Vue第三篇(组件化开发与webpack详解)_第130张图片

      查看打包后的bunlde.js文件,是已经被压缩过了
      Vue第三篇(组件化开发与webpack详解)_第131张图片
      Vue第三篇(组件化开发与webpack详解)_第132张图片

      三十五、webpack-dev-server搭建本地服务器(掌握)

      返回目录
      webpack提供了一个可选的本地开发服务器,这个本地服务器基于node.js搭建,内部使用express框架,可以实现我们想要的让浏览器自动刷新显示我们修改后的结果。

      类似于 node.js 阶段用到的 nodemon 工具,每当修改了源代码, webpack 会自动进行项目的动态打包构建(没有真实的映射到硬盘,所以最后测试完发布的时候只需要必须执行一次打包命令

      使用之前需要先安装webpack-dev-server

      npm install [email protected] --save-dev
      

      配置:
      Vue第三篇(组件化开发与webpack详解)_第133张图片

      • contentBase:——————为哪一个文件夹提供本地服务,默认是根文件夹,我们这里填写
      • inline:true ————页面实时刷新
      • port: ————指定访问的端口号
      • historyApiFallback: ————在SPA页面中,依赖HTML5的history模式(介绍路由时再详解)
      • host: ‘127.0.0.1’: ————实时打包所使用的主机地址

      配置命令:
      Vue第三篇(组件化开发与webpack详解)_第134张图片
      输入npm run dev指令
      Vue第三篇(组件化开发与webpack详解)_第135张图片
      测试:
      Vue第三篇(组件化开发与webpack详解)_第136张图片
      Vue第三篇(组件化开发与webpack详解)_第137张图片
      服务器运行时我们进行动态修改代码就行了,最后部署的时候一定要执行打包命令

      优化配置,不需要我们手动访问浏览器
      Vue第三篇(组件化开发与webpack详解)_第138张图片
      测试指令npm run dev,完成(Ctrl+C中断服务器)
      Vue第三篇(组件化开发与webpack详解)_第139张图片

      三十六、webpack-配置文件的分离(掌握)

      返回目录

      场景引入:
      Vue第三篇(组件化开发与webpack详解)_第140张图片
      这时候就需要我们进行单独分离了,把测试时和打包发布时的配置分离出来

      平时小项目且生产环境和开发环境不是很复杂的时候是不需要分离的,而且Vue脚手架也是做了分离的

      建立build文件夹和三个js文件
      Vue第三篇(组件化开发与webpack详解)_第141张图片
      现在的问题是怎么在不同的阶段组合配置文件
      下载安装webpack-merge(merge是合并的意思)

      npm install webpack-merge
      

      base.config.js中放公共的配置:

      /*动态获取路径第一步导入我们的path*/
      const path =require('path')/*这个path文件存在node包中,需要我们手动装*/
      
      const webpack = require('webpack');
      
      const HtmlWebpackPlugin=require('html-webpack-plugin');
      
      module.exports={
        entry:'./src/main.js',
        output:{
          path:path.resolve(__dirname,"dist"),/*动态获取我们的路径,resolve方法拼接,__dirname为所依赖的路径*/
          filename:'bundle.js',
          /*publicPath:'dist/'不再需要*/
        },
        module: {
          rules: [
            {
              test: /\.css$/,
              use: [ 'style-loader' , 'css-loader']
            },
            {
              test: /\.less$/,
              use: [{
                loader: "style-loader" // creates style nodes from JS strings
              }, {
                loader: "css-loader" // translates CSS into CommonJS
              }, {
                loader: "less-loader" // compiles Less to CSS
              }]
            },
            {
              test: /\.(png|jpg|gif|jpeg)$/,
              use: [
                {
                  loader: 'url-loader',
                  options: {
                    limit: 8192,
                    esModule:false,
                    name: 'img/[name].[hash:8].[ext]'
                  },
                }]
            },
            {
              test: /\.m?js$/,
              exclude: /node_modules/,
              use: {
                loader: 'babel-loader',
                options: {
                  presets: [
                    ['@babel/preset-env', { targets: "defaults" }]
                  ]
                }
              }
            },
            {
              test: /\.vue/,
              use: ['vue-loader']
            }
          ]
        },
        resolve:{
          //alias:别名
          alias:{
            'vue$':'vue/dist/vue.esm.js'
          }
        },
        plugins:[
          new webpack.BannerPlugin('最终版权归ldx所有'),
          new HtmlWebpackPlugin({
            template:'index.html'
          }),
        ]
      }
      

      dev.config.js:放入测试时的配置:

      const webpackMerge=require('webpack-merge')
      const baseConfig=require('./base.config')
      
      module.exports= webpackMerge.merge(baseConfig,{//高版本的webpack-merge是一个对象,需要调用其merge方法才能正常运行
        devServer:{
          contentBase:'./dist',/*为哪一个文件夹提供本地服务,默认是根文件夹,我们这里填写./dist*/
          inline:true/*页面实时刷新*/
        }
      })
      
      
      
      

      pro.config.js:放入打包发布时的配置:

      const UglifyJsWebpackPlugin=require('uglifyjs-webpack-plugin')
      const webpackMerge=require('webpack-merge')
      const baseConfig=require('./base.config.js')
      
      
      module.exports=webpackMerge.merge(baseConfig,{//高版本的webpack-merge是一个对象,需要调用其merge方法才能正常运行
        plugins:[
          new UglifyJsWebpackPlugin()
        ]
      })
      
      

      删除原来的webpack.config.js文件

      在package.json中配置命令:
      Vue第三篇(组件化开发与webpack详解)_第142张图片
      打包:路径问题
      Vue第三篇(组件化开发与webpack详解)_第143张图片
      打包位置在build下的dist包中,需要我们修改base.config.js文件的输出路径:
      Vue第三篇(组件化开发与webpack详解)_第144张图片
      再次打包:
      Vue第三篇(组件化开发与webpack详解)_第145张图片
      路径正确:
      Vue第三篇(组件化开发与webpack详解)_第146张图片
      可以试试npm run dev:
      也成功运行:
      Vue第三篇(组件化开发与webpack详解)_第147张图片
      动态添加内容试试:
      Vue第三篇(组件化开发与webpack详解)_第148张图片
      浏览器也是实时刷新:
      Vue第三篇(组件化开发与webpack详解)_第149张图片

你可能感兴趣的:(Vuejs学习,vue.js,html,javascript)