天天看点

【尚硅谷】Vue2.x组件化编码学习笔记--渐进式的JS框架

【尚硅谷】Vue2.x组件化编码学习笔记--渐进式的JS框架

Vue组件化编码

  • ​​一、使用Vue-cli创建项目​​
  • ​​1.1 说明​​
  • ​​1.2 创建Vue项目​​
  • ​​1.2.1 如何修改端口以及自动运行​​
  • ​​1.3 Vue-cli创建的项目的目录结构​​
  • ​​1.4 组件如何定义和使用​​
  • ​​1.4.1 组件定义​​
  • ​​1.4.2 使用组件​​
  • ​​1.5 入口函数main.js​​
  • ​​二、项目打包与发布​​
  • ​​2.1 打包​​
  • ​​2.2 发布1:使用静态服务器工具包​​
  • ​​2.3 发布2:使用动态的web服务器tomcat​​
  • ​​三、Vue案例-评论室​​
  • ​​3.1 初始化显示​​
  • ​​3.1.1 Add_Comments.vue​​
  • ​​3.1.2 Comments_List.vue​​
  • ​​3.1.3 List_Item.vue​​
  • ​​3.1.4 App.vue​​
  • ​​3.1.5 main.js​​
  • ​​3.1.6 小结​​
  • ​​3.2 动态的加载数据​​
  • ​​3.2.1 组件间如何通信​​
  • ​​3.2 交互添加​​
  • ​​3.3 交互删除​​
  • ​​四、Vue案例-任务列表​​
  • ​​4.1 数据动态显示​​
  • ​​4.2 滑动显示效果​​
  • ​​4.3 如何缓存数据​​
  • ​​4.4 如何实现​​
  • ​​五、消息订阅发布​​
  • ​​5.1 订阅消息​​
  • ​​5.2 发布消息​​
  • ​​5.3 使用​​
  • ​​5.3 注意事项​​

一、使用Vue-cli创建项目

1.1 说明

  1. vue-cli是vue官方提供的

1.2 创建Vue项目

  1. npm install -g vue-cli
  2. vue init webpack vue_demo:
  3. cd vue_demo
  4. npm install
  5. npm run dev
  6. 访问:http://localhost:8080/
【尚硅谷】Vue2.x组件化编码学习笔记--渐进式的JS框架

如果出现安装慢,可以去看我的另一篇文章。

​​​npm install 安装满,怎么解决​​

1.2.1 如何修改端口以及自动运行

在config目录下,找到index.js文件,如下图,重启项目。

【尚硅谷】Vue2.x组件化编码学习笔记--渐进式的JS框架

1.3 Vue-cli创建的项目的目录结构

【尚硅谷】Vue2.x组件化编码学习笔记--渐进式的JS框架
|-- build                            // 项目构建(webpack)相关代码
|   |-- build.js                     // 生产环境构建代码
|   |-- check-version.js             // 检查node、npm等版本
|   |-- utils.js                     // 构建工具相关
|   |-- vue-loader.conf.js           // webpack loader配置
|   |-- webpack.base.conf.js         // webpack基础配置
|   |-- webpack.dev.conf.js          // webpack开发环境配置,构建开发本地服务器
|   |-- webpack.prod.conf.js         // webpack生产环境配置
|-- config                           // 项目开发环境配置
|   |-- dev.env.js                   // 开发环境变量
|   |-- index.js                     // 项目一些配置变量
|   |-- prod.env.js                  // 生产环境变量
|-- src                              // 源码目录
|   |-- components                   // vue公共组件
|   |-- router                       // vue的路由管理
|   |-- App.vue                      // 页面入口文件
|   |-- main.js                      // 程序入口文件,加载各种公共组件
|-- static                           // 静态文件,比如一些图片,json数据等
|-- .babelrc                         // ES6语法编译配置
|-- .editorconfig                    // 定义代码格式
|-- .eslintignore          // eslint语法忽略的配置
|-- .gitignore                       // git上传需要忽略的文件格式
|-- .postcsssrc                      // postcss配置文件
|-- README.md                        // 项目说明
|-- index.html                       // 入口页面
|-- package.json                     // 项目基本信息,包依赖信息等      

❓什么是组件🔥🔥🔥

组件是Vue中的一个重要概念,是一个可以重复使用的Vue实例,它拥有独一无二的组件名称,它可以扩展HTML元素,以组件名称的方式作为自定义的

HTML标签。

组件:​

​把一些公共的模块抽取出来,然后写成单独的的工具组件或者页面,在需要的页面中就直接引入即可。那么我们可以将其抽出为一个组件进行复用。​

1.4 组件如何定义和使用

1.4.1 组件定义

一般会先创建一个components组件目录,在这个文件目录里面写所需要定义的组件

这里我先定义一个HellWorld的组件

<template>
  <div>
    <p class="msg">{{msg}}</p> <!--这里的公共功能暂时是一个显示一条数据,msg-->
  </div>
</template>

<script>/*向外默认暴露一个配置对象*/
  export default {
    name: "HelloWorld",
    data(){ // 注意这里的data只能用函数来外暴露,不能使用对象的方式
      return{
        msg:"你好 Components Hello World" // 定义了一个data为msg,用于vue来动态绑定
      }
    }
  }</script>

<style scoped>.msg{
    color: yellowgreen;
    font-size: 24px;
  }</style>      

​这里的data只能使用函数来向外暴露使用,否则不起效。​

1.4.2 使用组件

在使用组件的时候,我们需要有三个步骤

  1. 引入组件
  2. 映射组件标签
  3. 使用组件标签
<!--模板 -模板必须要有一个根标签-->
<template>
  <div>
    <img src="./assets/logo.png" alt="logo" class="logo">
    <!--3、使用HelloWorld组件标签-->
    <HelloWorld></HelloWorld>
  </div>
</template>
<!--js-->
<script>/*1、引入组件HelloWorld*/
  import HelloWorld from "./components/HelloWorld";
  export default {
    name: "App.vue",
    /*2、映射组件helloWorld标签*/
    components: {HelloWorld}
  }</script>
<!--css样式 这里可以定义,除组件之外的模板的样式 -->
<style scoped>.logo{
    width: 200px;
    height: 200px;
    border: 1px solid seagreen;
  }</style>      

1.5 入口函数main.js

/*
* 入口js 1、创建Vue.js
* */
import Vue from 'vue' // 引入Vue
// 1、引入组件
import App from "./App.vue"; // 引入App.vue

// 创建Vue实例对象
new Vue({
  el:"#app",
  // 2、映射组件标签
  components:{
    App
  },
  // 3、使用组件标签
  template:'<App/>'
})      
【尚硅谷】Vue2.x组件化编码学习笔记--渐进式的JS框架
【尚硅谷】Vue2.x组件化编码学习笔记--渐进式的JS框架

二、项目打包与发布

2.1 打包

npm      

2.2 发布1:使用静态服务器工具包

npm install      

访问http://localhost:端口

【尚硅谷】Vue2.x组件化编码学习笔记--渐进式的JS框架

2.3 发布2:使用动态的web服务器tomcat

修改配置:webpack.prod.conf.js

output:{
  publicPath:'/xxx/'  // 项目文件夹的名称
}      

重新打包:

npm      

修改dist文件夹为项目名称:xxx

将xxx拷贝到运行的tomcat的webapps目录下

三、Vue案例-评论室

但我们拿到一个页面的时候,Vue的思想就是组件化的开发,那么我们就必须要将一个也面划分为几个可以重复使用的​

​组件​

​,比如如下的划分:

【尚硅谷】Vue2.x组件化编码学习笔记--渐进式的JS框架

3.1 初始化显示

根据上面的分析,我们先将页面拆分成三个组件,并将整个view视图分为一个App的容器包裹。容器内部分为左边的添加区域;右边的评论列表。

所以,综上—>:

  1. 我们新建三个名为​

    ​Add_Comments.vue​

    ​​、​

    ​Comments_List.vue​

    ​​、​

    ​List_item.vue​

    ​的三个组件,
  2. 并将​

    ​Add_Comments.vue​

    ​​和​

    ​Comments_List.vue​

    ​​作为组件添加到​

    ​App.vue​

    ​里,
  3. 最后,将​

    ​main.js​

    ​​的入口文件中,使得​

    ​App.vue​

    ​作为一个标签组件去显示到页面上。
  4. 【尚硅谷】Vue2.x组件化编码学习笔记--渐进式的JS框架

3.1.1 Add_Comments.vue

<template>
  <div class="col-md-4">
    <form class="form-horizontal">
      <div class="form-group">
        <label>用户名</label>
        <input type="text" class="form-control" v-model="name" placeholder="用户名">
      </div>
      <div class="form-group">
        <label>评论内容</label>
        <textarea class="form-control" rows="6" v-model="content" placeholder="评论内容"></textarea>
      </div>
      <div class="form-group">
        <div class="col-sm-offset-2 col-sm-10">
          <button type="button" class="btn btn-default pull-right">提交</button>
        </div>
      </div>
    </form>
  </div>
</template>

<script>export default {
    name: "Add_Comments",
  }</script>      

3.1.2 Comments_List.vue

<template>
  <div class="col-md-8">
    <h3 class="reply">评论回复:</h3>
    <h2 v-show="comments.length<=0">暂无评论,点击左侧添加评论!!!</h2>
    <ul class="list-group">
      <List_Item/>
    </ul>
  </div>
</template>

<script>import List_Item from "./List_Item";

  export default {
    name: "Comments_List",
    components: {
      List_Item
    },</script>

<style scoped>.reply {
    margin-top: 0px;
  }</style>      

3.1.3 List_Item.vue

<template>
  <div>
    <li class="list-group-item">
      <div class="handle">
        <a href="javascript:;">删除</a>
      </div>
      <p class="user"><span>Rose</span><span>说:</span></p>
      <p class="centence">很好</p>
    </li>
  </div>
</template>

<script>export default {
    name: "List_Item",
  }</script>

<style scoped>li {
    transition: .5s;
    overflow: hidden;
  }

  .handle {
    width: 40px;
    border: 1px solid #ccc;
    background: #fff;
    position: absolute;
    right: 10px;
    top: 1px;
    text-align: center;
  }

  .handle a {
    display: block;
    text-decoration: none;
  }

  .list-group-item .centence {
    padding: 0px 50px;
  }

  .user {
    font-size: 22px;
  }</style>      

3.1.4 App.vue

<template>
  <div>
    <header class="site-header jumbotron">
      <div class="container">
        <div class="row">
          <div class="col-xs-12">
            <h1>请发表对Vue的评论</h1>
          </div>
        </div>
      </div>
    </header>
    <div class="container">
      <Add_Comments :addComment="addComment"></Add_Comments>
      <Comments_List :comments="comments" :delItem="delItem"></Comments_List>
    </div>
  </div>
</template>

<script>import Add_Comments from "./components/Add_Comments";
  import Comments_List from "./components/Comments_List";

  export default {
    name: "App",
    methods: {},
    data() { // 测试数据
      return {
        comments: [ // 操作在那个组件 更新组件的那个行为(方法,函数)就在那个组件里
          {
            name: 'Rose',
            content: 'Vue 还不错偶!!'
          },
          {
            name: 'Jack',
            content: 'yes,果然还不错'
          },
          {
            name: 'Robby',
            content: '尤雨溪YYDS'
          }
        ]
      }
    },
    components: {
      Add_Comments,
      Comments_List
    }
  }</script>      

3.1.5 main.js

import Vue from 'vue'
import App from './App.vue'

new Vue({
  el: '#app',
  components: {
    App
  },
  template: '<App/>'
})      

3.1.6 小结

vue的组件化开发可以将一个项目所有公共的模块抽取,​

​这样就很大程度上降低了代码冗余度,提高了开发效率。​

​​所以,我们在刚开始学习Vue的时候,遇到一个大的页面可以先分析将其划分成小的功能模块,然后​

​分模块​

​的去开发。

3.2 动态的加载数据

首先这里我们可以现在App.vue里面模拟一些数据。

思考一个问题?​

​我们在那个模块里面写公共的数据​

​​

分析: 这些数据哪些模块里要使用?1、Add_Comments;2、Comments_List要使用

结论: ​

​既然两个子模块都需要用到这些模拟数据,那么我们就将数据定义到App.vue里面​

data() {
     return {
       comments: [ // 操作在那个组件 更新组件的那个行为(方法,函数)就在那个组件里
         {
           name: 'Rose',
           content: 'Vue 还不错偶!!'
         },
         {
           name: 'Jack',
           content: 'yes,果然还不错'
         },
         {
           name: 'Robby',
           content: '尤雨溪YYDS'
         }
       ]
     }
   },      

3.2.1 组件间如何通信

模拟完数据之后,接下来要做的就是如何将这些模拟数据在子模块中使用❓

vue的模块间通讯方式有很多种,这里我就使用 ​

​props/ $emit(父子间传递常用方式)​

​​

1、首先,我们现在App.vue里面的组件标签上使用数据绑定指令v-bind来将这些模拟数据传输给子组件。

<Comments_List :comments="comments"></Comments_List>      

2、Comments_List.vue子组件中使用 ​

​props​

​来对传输过来的数据进行声明绑定

export default {
    name: "Comments_List",
    components: {
      List_Item
    },
    // 这种方式只指定属性名 声明接收属性:props
    props: ['comments']
  }      

3、声明完属性之后,我们就需要使用数据–遍历数组中的数据

<ul class="list-group">
      <List_Item v-for="(comment,index) in comments" :key="index" :index="index" :comment="comment"/>
    </ul>      

4、Comments_List组件将comments数组中的每一个对象遍历出来后,将每一个comment作为一个数据,向他的子组件List_Item传递,

export default {
    name: "List_Item",
    // 这种方式指定属性名和属性值
    props: {
      comment: Object,
    }
 }      
说明:​

​props官网说明​

​​
【尚硅谷】Vue2.x组件化编码学习笔记--渐进式的JS框架

// 简单语法Vue.component('props-demo-simple', {

props: ['size', 'myMessage']

})

// 对象语法,提供验证

Vue.component('props-demo-advanced', {

props: {

// 检测类型

height: Number,

// 检测类型 + 其他验证

age: {

type: Number,

default: 0,

required: true,

validator: function (value) {

return value >= 0

}

}

}

})

使用玩上面的定义后,我们就可以在html标签中利用Vue的插值语法去获取具体的Comments数组中的值了,如下所示:

<p class="user"><span>{{comment.name}}</span><span>说:</span></p>
<p class="centence">{{comment.content}}</p>      

3.2 交互添加

完成完上面的数据的动态绑定之后,我们做的就是有交互效果

【交互添加评论信息】

1、给按钮绑定点击事件

<button type="button" class="btn btn-default pull-right" @click="add">提交</button>      

2、添加事件函数

​这时候,我们就需要思考了?​

​ 添加数据的步骤是什么?
  1. 校验表单输入的数据的合法性
  2. 封装表单数据为一个coment对象
  3. 将comment对象添加到App.vue里定义的Comments数组对象中

​操作在那个组件 更新组件的那个行为(方法,函数)就在那个组件​

​,而我们需要操作的Comments正好在App.vue组件中,所以,我们将添加的函数放到App.vue里面
export default {
    name: "App",
    methods: {
      // 添加评论方法
      addComment(comment){
        // 添加到数组的前面
        this.comments.unshift(comment)
      }
    }
  }      

定义完方法之后,我们同理也是必须要向Add_Comments暴露

<Add_Comments :addComment="addComment"></Add_Comments>      

3、接收都组件传递的数据并使用

export default {
    name: "Add_Comments",
    // 接受传递过来的 addComment方法
    props: {
      addComment: { // 制定了属性名 、属性值的类型、必要性(默认值,验证方法)
        type: Function, // (String,Number,Boolean,Function,Object,Array,Symbol)原生构造器
        required: true
      }
    },
    data() {
      return {
        name: '',
        content: ''
      }
    },
    methods: {
      add() {
        // 1、检查输入的合法性
        const name = this.name;
        const content = this.content.trim();
        if (!name || !content) {
          alert("姓名或者内容不能为空")
          return;
        }
        // 2、根据输入的数据,封装成一个comment对象
        const comment = {
          name,
          content
        }
        // 3、添加到comments中 comments在app组件中 操作在那个组件 更新组件的那个行为(方法,函数)就在那个组件
        this.addComment(comment)
        // 4、清除输入框中的内容
        this.name = "";
        this.content = "";
      }
    }
  }
</script>      

3.3 交互删除

交互添加做完之后,我们的交互删除操作也是同样的道理,只不过的是,交互删除需要用到两次的传递,​

​App.vue--->Comments_List--->List_Item​

这里只给出最终的List_Item的核心代码

<script>
  export default {
    name: "List_Item",
    // 这种方式指定属性名和属性值
    props: {
      comment: Object,
      deleteItem: Function,
      index: Number
    },
    methods: {
      delItem() {
        const {comment,index,deleteItem} = this
        if (confirm(`确认删除${comment.name}的评论么?`)) {
          deleteItem(index)
        }
      }
    }
  }
</script>      

四、Vue案例-任务列表

老规矩,先拆分页面

【尚硅谷】Vue2.x组件化编码学习笔记--渐进式的JS框架

一样的三步走

4.1 数据动态显示

App.vue里面模拟数据

export default {
    name: "App",
    data() {
      return {
        todos: [
          {
            title: '背单词50个',
            complete: false
          },
          {
            title: '学习Vue2.x',
            complete: true
          },
          {
            title: '好好干饭',
            complete: false
          },
        ]
      }
    },
    components: {
      Todo_Oper,
      Todo_List,
      Todo_Input
    },
  }
</script>      

向外暴露给Todo_List和Todo_Oper组件中

<Todo_List :todos="todos"></Todo_List>
<Todo_Oper :todos="todos"></Todo_Oper>      

子组件进行数据的处理渲染到页面

<ul class="todo-main">
      <Todo_Item v-for="(todo,index) in todos" :key="index" :todo="todo" :index="index" :deleteTodo="deleteTodo"></Todo_Item>
</ul>      
<li>
    <label>
      <input type="checkbox"/>
      <span>{{todo.title}}</span>
    </label>
    <button class="btn btn-danger">删除</button>
 </li>      

4.2 滑动显示效果

显示玩数据之后,后续的操作和案例一十分类似,这里省略,

这里主要记录不同的代码,先看效果图:

【尚硅谷】Vue2.x组件化编码学习笔记--渐进式的JS框架

要完成这个效果,我们首先要思考几个问题?先这种鼠标划过和移除时的事件是什么?

1️⃣questionOne:如何是实现鼠标滑过切换效果

以下四个方法有何区别—>

  • onmouseout:鼠标滑出
  • onmouseover:鼠标滑过(在表面经过即可)
  • onmouseenter:鼠标进入(进入到里面)
  • onmouseleave:鼠标离开

​核心区别:​

​​

​​

​onmouseover 和 onmouseout 存在冒泡机制。​

​​划过和划出(鼠标在谁身上,相当于划过谁)

​​

​onmouseenter 和 onmouseleave 不存在冒泡机制。​

​进入和离开

所以,我们要使用的是哪一个方法???

👌没错了,这里我们使用的是​​

​onmouseenter​

​​和​

​onmouseleave​

​​,因为这里​

​button 按钮​

​​是在​

​li标签里面​

2️⃣questionTwo:方法里面的参数有还是没有,有的话是什么,如何确定?

我们想一想,鼠标进入切换和鼠标离开切换无非两种状态1、划过变颜色按钮显示,2、离开变另一种颜色按钮隐藏,所以,是肯定有参数的,而且参数可以是布尔类型,也可以是数字,但没必要,布尔类型完全够用

【步骤一:绑定事件】

<li @mouseenter="handEnterShow(true)" :style="{background:bgColor}" @mouseleave="handEnterShow(false)">
    <label>
      <input type="checkbox" v-model="todo.complete"/>
      <span>{{todo.title}}</span>
    </label>
    <button class="btn btn-danger" v-show="isShow" @click="deleteItem">删除</button>
</li>      

【步骤二:编写函数】

export default {
    name: "Todo_Item",
    props: {
      todo: {
        type: Object
      },
      index: {
        type: Number
      },
      deleteTodo: {
        type: Function
      },
      deleteTodo: {
        type: Function
      }
    },
    data() {
      return {
        bgColor: 'white', // 默认的背景颜色
        isShow: false     // 默认的显示状态
      }
    },

    methods: {
      handEnterShow(isEnter) {
        // console.log(isEnter)
        if (isEnter) {
          // 进入
          // 更新状态
          this.bgColor = '#aaaaaa'
          this.isShow = true
        } else {
          // 离开
          this.bgColor = 'white'
          this.isShow = false
        }
      },
      deleteItem() {
        const {todo, index,deleteTodo} = this
        if (confirm(`确认删除${todo.title}么?`)) {
          deleteTodo(index)
        }
      }
    }
  }      

4.3 如何缓存数据

【思考👽】 ​

​数据缓存到哪里比较合适???​

首先,我们先有一个需求,就是我们想要我们的todolist页面,当我们去点击一个任务项的时候,这时候我们关闭页面或者刷新页面之后,下一次打开浏览器再次看到这个页面的时候,仍然访问到关闭之前的页面数据。

【分析🕵️】 我们如果将数据缓存到内存中,这样的话,下次打开浏览器,缓存的数据将会丢失,所以,为了满足需求,​

​我们只能将数据缓存到文件中,下次直接去从上一次的缓存文件中取数据即可​

​那么缓存到文件中,需要用到什么呢?​

​—localstorage

4.4 如何实现

刚说到需要用到缓存数据来实现上述的需求,那么我们有需要考虑两个问题:

1、什么时候存数据–当我们页面发生改变[删除、添加、勾选]–deepWatch

2、什么时候取数据–当页面每次打开的时候

3、存的是什么数据–todos

这里我们可以通过将原本的todos的数组,用localStorage去代替原本的数组

export default {
    name: "App",
    data() {
      return {
        // 从localstorage中读取数据 读取的是一个字符串 需要得到一个json字串
        todos: JSON.parse(window.localStorage.getItem("todos_key") || "[]")
      }
    },
    components: {
      Todo_Oper,
      Todo_List,
      Todo_Input
    },
    methods: {
      /*Input里面的函数*/
      addTodo(todo) {
        this.todos.unshift(todo)
      },
      /*Item里面的函数*/
      deleteTodo(index) {
        this.todos.splice(index, 1)
      },
      /*Oper里面的函数*/
      deleteAll() {
        // 过滤 然后覆盖掉之前的数组
        this.todos = this.todos.filter(todo => !todo.complete);
      },
      selectAllTodos(isCheck) {
        // 先遍历
        this.todos.forEach(todo => todo.complete = isCheck)
      }
    },
    watch: {
      todos:{ // 开始监视todos
        deep: true, // 开启深度监视
        handler:function (value) {
          // 将todos最新的值转化为JSON格式,保存到localstorage中
          window.localStorage.setItem("todos_key",JSON.stringify(value));
        }
      }
    }
  }
</script>      

这里需要注意的是:

1、​​

​localStorage的getItem和setItem方法 返回的只是一个字串​

​​,所以我们需要将其转化为JSON格式

从localstorage中读取数据 读取的是一个字符串 需要得到一个json字串

2、我们在更新loaclStorage中的数据的时候,是需要实时的监听数据的,所以我们​​

​需要使用到watch,而且是深度监听。​

【尚硅谷】Vue2.x组件化编码学习笔记--渐进式的JS框架

五、消息订阅发布

消息订阅发布式组件间通信的一种表现形式,利用到了​

​pubsubJs库​

首先,我们可定是需要安装的这个库的

npm install      

5.1 订阅消息

订阅消息可以被理解为发布消息 简单的将:

绑定事件监听 ------ 订阅消息

触发事件 ------- 发布消息

【语法】

Pubsub.subscribe('msg',fucntion(msg,data){})      

【订阅消息时的参数】

  • msg:为消息订阅的名字
  • function:为消息订阅的回调函数
  • msg:消息订阅的名字(完全冗余)
  • data: 发布消息的data数据(参数)

5.2 发布消息

【语法】

Pubsub.publish('msg',data)      

【发布消息的参数】

  • msg:消息订阅的名字
  • data:参数

5.3 使用

【步骤一】:导入Pubsub模块

import Pubsub from 'pubsub-js'      
Pubsub.subscribe('deleteTodo',(msg,index) =>{
        this.deleteTodo(index)
})      
Pubsub.publish('deleteTodo',index);      

5.3 注意事项

  1. 优点:此种方式可以实现任意关系组件间的通信(数据)

继续阅读