zoukankan      html  css  js  c++  java
  • Vue 学习笔记 [Part 4]

    作者:故事我忘了
    个人微信公众号:程序猿的月光宝盒

    一. 组件化开发

    1.1. 父子组件的访问

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

    • 父组件访问子组件:$children或$refs(引用)

      图片

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

      这里通过一个遍历,取出所有子组件的message状态。

      $children的缺陷:

      通过$children访问子组件时,是一个数组类型,访问其中的子组件必须通过索引值

      但是当子组件过多,我们需要拿到其中一个时,往往不能确定它的索引值,甚至还可能会发生变化。

      有时候,我们想明确获取其中一个特定的组件,这个时候就可以使用$refs

      $refs的使用:

      $refsref指令通常是一起使用的。

        首先,我们通过``ref``给某一个子组件绑定一个特定的``ID``。
      
        其次,通过``this.$refs.ID``就可以访问到该组件了。
      
      <!DOCTYPE html>
      <html lang="en">
      <head>
        <meta charset="UTF-8">
        <title>Title</title>
      </head>
      <body>
      
      <div id="app">
        <cpn></cpn>
        <cpn></cpn>
      
        <my-cpn></my-cpn>
        <y-cpn></y-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() {
              // 1.$children
              // console.log(this.$children);
              // for (let c of this.$children) {
              //   console.log(c.name);
              //   c.showMessage();
              // }
              // console.log(this.$children[3].name);
      
              // 2.$refs => 对象类型, 默认是一个空的对象 ref='bbb'
              console.log(this.$refs.aaa.name);
            }
          },
          components: {
            cpn: {
              template: '#cpn',
              data() {
                return {
                  name: '我是子组件的name'
                }
              },
              methods: {
                showMessage() {
                  console.log('showMessage');
                }
              }
            },
          }
        })
      </script>
      
      </body>
      </html>
      

      图片

    • 子组件访问父组件:$parent/root

      注意事项:

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

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

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

      ​ 另外,更不好做的是通过$parent直接修改父组件的状态,那么父组件中的状态将变得飘忽不定,很不利于我的调试和维护。

      图片

    1.2. slot的使用

    为什么使用solt

    slot翻译为插槽:

    ​ 在生活中很多地方都有插槽,电脑的USB插槽,插板当中的电源插槽。

    ​ 插槽的目的是让我们原来的设备具备更多的扩展性。

    ​ 比如电脑的USB我们可以插入U盘、硬盘、手机、音响、键盘、鼠标等等。

    组件的插槽:

    ​ 组件的插槽也是为了让我们封装的组件更加具有扩展性。

    ​ 让使用者可以决定组件内部的一些内容到底展示什么。

    • 基本使用

      ​ 在子组件中,使用特殊的元素就可以为子组件开启一个插槽。

      ​ 该插槽插入什么内容取决于父组件如何使用。

      图片

      <!DOCTYPE html>
      <html lang="en">
      <head>
        <meta charset="UTF-8">
        <title>Title</title>
      </head>
      <body>
      
      <!--
      1.插槽的基本使用 <slot></slot>
      2.插槽的默认值 <slot>button</slot>
      3.如果有多个值, 同时放入到组件进行替换时, 一起作为替换元素
      -->
      
      <div id="app">
        <cpn></cpn>
      
        <cpn><span>哈哈哈</span></cpn>
        <cpn><i>呵呵呵</i></cpn>
        <cpn>
          <i>呵呵呵</i>
          <div>我是div元素</div>
          <p>我是p元素</p>
        </cpn>
      
        <cpn></cpn>
        <cpn></cpn>
        <cpn></cpn>
        <cpn></cpn>
      </div>
      
      
      <template id="cpn">
        <div>
          <h2>我是组件</h2>
          <p>我是组件, 哈哈哈</p>
          <slot><button>按钮</button></slot>
          <!--<button>按钮</button>-->
        </div>
      </template>
      
      <script src="../js/vue.js"></script>
      <script>
        const app = new Vue({
          el: '#app',
          data: {
            message: '你好啊'
          },
          components: {
            cpn: {
              template: '#cpn'
            }
          }
        })
      </script>
      
      </body>
      </html>
      
    • 具名插槽

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

      ​ 比如我们封装一个导航栏的子组件,可能就需要三个插槽,分别代表左边、中间、右边。

      ​ 那么,外面在给插槽插入内容时,如何区分插入的是哪一个呢?

      ​ 这个时候,我们就需要给插槽起一个名字

      如何使用具名插槽呢?

      ​ 非常简单,只要给slot元素一个name属性即可

      <slot name='myslot'></slot>

      我们来给出一个案例:

      ​ 这里我们先不对导航组件做非常复杂的封装,先了解具名插槽的用法。

      图片

      <!DOCTYPE html>
      <html lang="en">
      <head>
        <meta charset="UTF-8">
        <title>Title</title>
      </head>
      <body>
      
      <div id="app">
        <cpn><span slot="center">标题</span></cpn>
        <cpn><button slot="left">返回</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>
      
    • 编译的作用域

      在真正学习插槽之前,我们需要先理解一个概念:编译作用域。

      ​ 官方对于编译的作用域解析比较简单,我们自己来通过一个例子来理解这个概念:

      ​ 我们来考虑下面的代码是否最终是可以渲染出来的:

      <my-cpn v-show="isShow"></my-cpn>中,我们使用了isShow属性。

      isShow属性包含在组件中,也包含在Vue实例中。

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

      为什么呢?

      ​ 官方给出了一条准则:父组件模板的所有东西都会在父级作用域内编译;子组件模板的所有东西都会在子级作用域内编译。

      而我们在使用<my-cpn v-show="isShow"></my-cpn>的时候,整个组件的使用过程是相当于在父组件中出现的。

      那么他的作用域就是父组件,使用的属性也是属于父组件的属性。

      因此,isShow使用的是Vue实例中的属性,而不是子组件的属性。

      图片

    • 作用域插槽

    作用域插槽是slot一个比较难理解的点,而且官方文档说的又有点不清晰。

    这里,我们用一句话对其做一个总结,然后我们在后续的案例中来体会:

    父组件替换插槽的标签,但是内容由子组件来提供。

    我们先提一个需求:

    ​ 子组件中包括一组数据,比如:pLanguages: ['JavaScript', 'Python', 'Swift', 'Go', 'C++']

    需要在多个界面进行展示:

    ​ 某些界面是以水平方向一一展示的,

    ​ 某些界面是以列表形式展示的,

    ​ 某些界面直接展示一个数组

    ​ 内容在子组件,希望父组件告诉我们如何展示,怎么办呢?

    ​ 利用slot作用域插槽就可以了

    我们来看看子组件的定义:

    图片

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>Title</title>
    </head>
    <body>
    
    <div id="app">
      <cpn></cpn>
    
      <cpn>
        <!--目的是获取子组件中的pLanguages,引用cacao对象-->
        <template slot-scope="slot">
          <!--<span v-for="item in slot.data"> - {{item}}</span>-->
          <span>{{slot.data.join(' - ')}}</span>
        </template>
      </cpn>
    
      <cpn>
        <!--目的是获取子组件中的pLanguages-->
        <template slot-scope="slot">
          <!--<span v-for="item in slot.data">{{item}} * </span>-->
          <span>{{slot.data.join(' * ')}}</span>
        </template>
      </cpn>
      <!--<cpn></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', 'C++', 'Java', 'C#', 'Python', 'Go', 'Swift']
              }
            }
          }
        }
      })
    </script>
    
    
    </body>
    </html>
    

    二. 前端模块化

    2.1. 为什么要使用模块化

    • 简单写js代码带来的问题

    • 闭包引起代码不可复用.

    • 自己实现了简单的模块化,匿名函数

      (function() {
      	var flag = true
      })()
      
    • AMD/CMD/CommonJS

      CommonJS

      导出:

      module.export = {
      	flag: true,
      	test(a, b) {
      		return a + b
      	},
      	demon(a, b) {
      		return a * b
      	} 
      }
      

      导入:

      //CommonJS模块
      let {test, demon , flag} = require('moduleA');
      
      //等同于
      let _mA = require('moduleA');
      let test = _mA.test;
      let demo = _mA.demon;
      let flag = _mA.flag;
      

    2.2. ES6中模块化的使用

    • export:导出变量

      图片

      图片

      某些情况下,一个模块中包含某个的功能,我们并不希望给这个功能命名,而让导入者可以自己来命名

      ​ 这个时候就可以使用export default

      图片

      我们来到main.js中,这样使用就可以了

      ​ 这里的myFunc是我自己命名的,你可以根据需要命名它对应的名字

      图片

      另外,需要注意

      export default在同一个模块中,不允许同时存在多个。

    • import 导出

    我们使用export指令导出了模块对外提供的接口,下面我们就可以通过import命令来加载对应的这个模块了

    首先,我们需要在HTML代码中引入两个js文件,并且类型需要设置为module

    图片

    import指令用于导入模块中的内容,比如main.js的代码

    图片

    如果我们希望某个模块中所有的信息都导入,一个个导入显然有些麻烦:

    通过``*``可以导入模块中所有的export变量
    
    但是通常情况下我们需要给``*``起一个别名,方便后续的使用
    

    图片

    三. webpack

    3.1. 什么是webpack

    • webpack和gulp对比

      webpack其中一个核心就是让我们可能进行模块化开发,并且会帮助我们处理模块间的依赖关系。

      而且不仅仅是JavaScript文件,我们的CSS、图片、json文件等等在webpack中都可以被当做模块来使用。

      这就是webpack中模块化的概念。

      打包如何理解呢?

      ​ 理解了webpack可以帮助我们进行模块化,并且处理模块间的各种复杂关系后,打包的概念就非常好理解了。

      ​ 就是将webpack中的各种资源模块进行打包合并成一个或多个包(Bundle)。并且在打包的过程中,还可以对资源进行处理,比如压缩图片,将scss转成css,将ES6语法转成ES5语法,将TypeScript转成JavaScript等等操作。

    • 安装webpack

    安装webpack首先需要安装Node.js,Node.js自带了软件包管理工具npm

    ​ 查看自己的node版本:node -v

    全局安装``webpack``(这里我先指定版本号3.6.0,因为vue cli2依赖该版本)
    

    npm install webpack@3.6.0 -g

    局部安装webpack(后续才需要)

    ​ --save-dev`是开发时依赖,项目打包后不需要继续使用的。

    cd 对应目录
    npm install webpack#3.6.0 --save-dev
    

    为什么全局安装后,还需要局部安装呢?

    ​ 在终端直接执行webpack命令,使用的全局安装的webpack

    ​ 当在package.json中定义了scripts时,其中包含了webpack命令,那么使用的是局部webpack

    3.2. webpack的起步

    创建如下文件和文件夹:

    文件和文件夹解析:

    图片

    dist文件夹:用于存放之后打包的文件

    src文件夹:用于存放我们写的源文件

    main.js:项目的入口文件。具体内容查看下面详情。

    mathUtils.js:定义了一些数学工具函数,可以在其他地方引用,并且使用。具体内容查看下面的详情。

    index.html:浏览器打开展示的首页html

    package.json:通过npm init生成的,npm包管理的文件(暂时没有用上,后面才会用上)

    mathUtils.js文件中的代码:

    图片

    main.js文件中的代码:

    图片

    • webpack命令

      现在的js文件中使用了模块化的方式进行开发,他们可以直接使用吗?

      ​ 不可以。

      因为如果直接在index.html引入这两个js文件,浏览器并不识别其中的模块化代码。

      另外,在真实项目中当有许多这样的js文件时,我们一个个引用非常麻烦,并且后期非常不方便对它们进行管理。

      我们应该怎么做呢?

      ​ 使用webpack工具,对多个js文件进行打包。

      我们知道,webpack就是一个模块化的打包工具,所以它支持我们代码中写模块化,可以对模块化的代码进行处理。

      另外,如果在处理完所有模块之间的关系后,将多个js打包到一个js文件中,引入时就变得非常方便了。

      OK,如何打包呢?使用webpack的指令即可

      webpack src/main.js dist/bundle.js

      打包后会在dist文件下,生成一个bundle.js文件

      ​ 文件内容有些复杂,这里暂时先不看,后续再进行分析。

      bundle.js文件,是webpack处理了项目直接文件依赖后生成的一个js文件,我们只需要将这个js文件index.html中引入即可

    • webpack配置: webpack.config.js/package.json(scripts)

    我们考虑一下,如果每次使用webpack的命令都需要写上入口和出口作为参数,就非常麻烦,有没有一种方法可以将这两个参数写到配置中,在运行时,直接读取呢?

    当然可以,就是创建一个webpack.config.js文件

    图片

    局部安装webpack

    ​ 目前,我们使用的webpack是全局的webpack,如果我们想使用局部来打包呢?

    ​ 因为一个项目往往依赖特定的webpack版本,全局的版本可能很这个项目的webpack版本不一致,导出打包出现问题。

    ​ 所以通常一个项目,都有自己局部的webpack。

    第一步,项目中需要安装自己局部的webpack

    ​ 这里局部安装webpack3.6.0

    ​ Vue CLI3中已经升级到webpack4,但是它将配置文件隐藏了起来,所以查看起来不是很方便。

    npm install webpack@3.6.0 --save-dev

    第二步,通过node_modules/.bin/webpack启动webpack打包

    package.json中定义启动

    但是,每次执行都敲这么一长串有没有觉得不方便呢?

    ​ OK,我们可以在package.json的scripts中定义自己的执行脚本。

    package.json中的scripts的脚本在执行时,会按照一定的顺序寻找命令对应的位置。

    图片

    ​ 首先,会寻找本地的node_modules/.bin路径中对应的命令。

    ​ 如果没有找到,会去全局的环境变量中寻找。

    ​ 如何执行我们的build指令呢?

    npm run build

    3.3. webpack的loader

    loader是webpack中一个非常核心的概念。

    webpack用来做什么呢?

    ​ 在我们之前的实例中,我们主要是用webpack来处理我们写的js代码,并且webpack会自动处理js之间相关的依赖。

    ​ 但是,在开发中我们不仅仅有基本的js代码处理,我们也需要加载css、图片,也包括一些高级的将ES6转成ES5代码,将TypeScript转成ES5代码,将scss、less转成css,将.jsx、.vue文件转成js文件等等。

    ​ 对于webpack本身的能力来说,对于这些转化是不支持的。

    ​ 那怎么办呢?

    ​ 给webpack扩展对应的loader就可以啦。

    loader使用过程:

    ​ 步骤一:通过npm安装需要使用的loader

    ​ 步骤二:在webpack.config.js中的modules关键字下进行配置

    大部分loader我们都可以在webpack的官网中找到,并且学习对应的用法

    • css-loader/style-loader

      用于CSS文件处理

      项目开发过程中,我们必然需要添加很多的样式,而样式我们往往写到一个单独的文件中。

      ​ 在src目录中,创建一个css文件,其中创建一个normal.css文件。

      ​ 我们也可以重新组织文件的目录结构,将零散的js文件放在一个js文件夹中。

      normal.css中的代码非常简单,就是将body设置为red

      ​ 但是,这个时候normal.css中的样式会生效吗?

      ​ 当然不会,因为我们压根就没有引用它。

      webpack也不可能找到它,因为我们只有一个入口,webpack会从入口开始查找其他依赖的文件。

      图片

      重新打包,会出现如下错误:

      图片

      这个错误告诉我们:加载normal.css文件必须有对应的loader。

      在webpack的官方中,我们可以找到如下关于样式的loader使用方法:

      按照官方配置webpack.config.js文件

      ​ 注意:配置中有一个style-loader,我们并不知道它是什么,所以可以暂时不进行配置。

      重新打包项目:

      图片

      但是,运行index.html,你会发现样式并没有生效。

      ​ 原因是css-loader只负责加载css文件,但是并不负责将css具体样式嵌入到文档中。

      ​ 这个时候,我们还需要一个style-loader帮助我们处理。

      我们来安装style-loader

      图片

      注意:style-loader需要放在css-loader的前面。

      疑惑:不对吧?按照我们的逻辑,在处理css文件过程中,应该是css-loader先加载css文件,再由style-loader来进行进一步的处理,为什么会将style-loader放在前面呢?

      答案:这次因为webpack在读取使用的loader的过程中,是按照从右向左的顺序读取的。

      目前,webpack.config.js的配置如下:

      图片

  • 相关阅读:
    android29
    android28
    android27
    android26
    Dynamics CRM2011 MspInstallAction failed when installing an Update Rollup
    Dynamics CRM Import Solution Attribute Display Name description is null or empty
    The service cannot be activated because it does not support ASP.NET compatibility
    IIS部署WCF报 无法读取配置节“protocolMapping”,因为它缺少节声明
    Unable to access the IIS metabase.You do not have sufficient privilege
    LM算法与非线性最小二乘问题
  • 原文地址:https://www.cnblogs.com/jsccc520/p/12901915.html
Copyright © 2011-2022 走看看