Vue组件化编程

2023-11-01  本文已影响0人  渚清与沙白

组件:实现应用中局部功能代码和资源的集合
模块:对外提供特定功能的js程序,一般是一个js文件
模块化:应用中JS都是以模块来编写,这就一个模块化应用
组件化:应用中的功能都是多组件的方式编写,这就是一个组件化应用

组件命名规范

  1. 单个单词的名字
    (1). 纯小写,注册和使用时都用小写
    (2). 注册和使用时首字母大写,vue渲染时本身就是大写开头,可以与之呼应。

  2. 多个单词组成的名字
    (1). 全部小写,单词之间短横线连接
    (2). 脚手架环境下,注册和使用时每个单词首字母大写

  3. 创建组件时配置的组件名字name仅仅在开发工具中呈现。

非单文件组件

// 创建组件
const student = Vue.extend({
  name: 'student'
  template:` <div> <h1> {{ name }} </h1> </div>`,
  data(){
     return {
         name:'',
     }
   },
   methods: {
      copyName(){},
   },
});

# 简写形式  不需要写Vue.extend   内部源码进行了判断
const student = {
   name: 'student'
   template:` <div> <h1> {{ name }} </h1> </div>`,
   data(){
       return {
          name:'',
       }
   },
   methods: {
       copyName(){},
   },
};
// 全局注册
Vue.component('student',student);  # 第一个参数是组件名,第二个参数是组件

// 局部注册
new Vue({
    data() {
       return {
          time: 1
       };
    },
   // 局部注册组件
   components:{ student }
});
<div id="root"> 
   <student> </student>  
</div>

// cli环境下
<template>
  <div>
    <student />
  </div>
</template>

单文件组件

VueComponent

VueComponent是一个构造函数

  1. 组件的本质是一个名为VueComponent的构造函数,是Vue.extend()生成的。

  2. 在使用组件时,Vue解析时会去创建组件的实例对象,Vue执行了new VueComponent(option)

  3. 每次执行Vue.extend时都会返回一个全新的VueComponent

  4. 在注册组件并使用组件标签后,才会创建VueComponent实例对象

// 模拟 Vue.extend 生成VueComponent
Vue.extend = function(extendOptions) {
  /*..........*/
  var sub = function VueComponent(options){
    this._init(options);
  }
  return sub;
  /*..........*/
}
  1. this指向问题。
    (1)在组件配置中,data函数,methods中的函数、watch中的函数、computed中的函数,他们的this指向均是【VueComponent实例对象】
    (2)在new Vue(options)配置中,data函数,methods中的函数、watch中的函数、computed中的函数,他们的this指向均是【Vue实例对象】
  2. VueComponent实例对象称之为vc组件实例对象;Vue实例对象称之为vm。vm和vc都有数据代理和数据监视。vc配置项不可传eldata必须是一个函数vm配置项可以传el,data可以是函数,也可以是对象。
  3. VueComponent.prototype.__proto__Vue.prototype指向相同。存在 VueComponent.prototype.__proto__ === Vue.prototype 的关系。组件实例对象vc可以访问到Vue原型上的属性和方法
    image.png
单文件配置

对外暴露的是Vue.extend配置对象,这里没有写Vue.extend,Vue会帮助我们使用Vue.extend来创建VueComponenet实例对象

export default {
  data(){
    return {};
  },
  methods:{
  }
}

Vue CLI

CLI: command line interface
全局安装@vue/cli:npm install -g @vue/cli
创建项目:vue create xxx
启动项目:vue run serve

// index.html
<!DOCTYPE html>
<html lang="">
  <head>
    <meta charset="utf-8">
    <!-- 让IE浏览器以最高的渲染 级别渲染页面 -->
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <!-- 开启移动端的理想视口 -->
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <!-- 配置页签图标 BASE_URL代表public目录 -->
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
    <!-- 配置网页标题 值是获取package.json中的name -->
    <title><%= htmlWebpackPlugin.options.title %></title>
  </head>
  <body>
    <!-- 当浏览器不支持js时,该标签的内容就会渲染 -->
    <noscript>
      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly...</strong>
    </noscript>
    <!-- 容器 -->
    <div id="app"></div>
    <!-- built files will be auto injected -->
  </body>
</html>
render函数

完整版vue是由vue核心模板解析器组成。模板解析器所占体积占用vue三分之一。如果使用完整版vue,webpack在打包时也会将模板解析器打包,由于webpack打包时会做翻译.vue解析模板的工作,所以模板解析器就没有存在的必要,于是就产生了没有模板解析器的精简版Vue。

image.png
new Vue({
    router,
    store,
    render: (h) => h(App),
}).$mount('#app');
  1. render的本质是一个函数,render函数的参数是createElement,createElement也是一个函数。
    import Vue from 'vue'引入的是ES6模块化版本dist/vue.runtime.esm.js,这个版本的没有模板解析器,带有runtime的版本都没有模板解析器。在new Vue(options)时,配置对象template选项,就无法解析。

  2. render的作用是为了弥补不完整版本vue.js的功能:模板解析。
    引入了运行时版Vue,在new Vue(options)时,需要使用render函数

  3. vue-template-compiler插件 用于解析组件中的template

  4. vue.js与vue.runtime.xx.js的区别
    (1). vue.js是完整版的Vue,包含Vue核心和模板解析器
    (2). vue.runtime.xx.js是运行版的Vue,只包含Vue核心,不包含模板解析器

  5. 为什么要使用render函数?
    因为vue.runtime.esm.js没有模板解析器,所以不能使用template配置项,需要使用render函数接收到的createElement函数去指定具体内容。

配置文件

vue.config.js是可选配置文件,使用了commonJS暴露。Vue会将该文件输送给webpack,与webpack中的配置进行合并。webpack是基于node的,nodeJS采用的模块化是commonJS。

ref属性
props配置项
// 1. 简单接收  数组形式,无法指定数据类型
props: ['name','age'],

// 2. 仅限制数据类型
props: {
    name:String,
    age:Number,
},

// 3. 指定多种类型
props: {
     type: [String, Number],
}

// 4. 完整配置 require与default不必同时配置
props: {
    name: {
      type:String,
      require:true,
    },
    age: {
      type:Number,
      default:60,
    }
},

props可以接收函数参数

data(){
  return {
    myName:this.name, // props的数据优先于data,所以可以这样使用
  }
},
props:[ 'name' ]

修改myName即可,但是不会影响name的值。

mixins配置项
插件

插件是一个必须含义install函数的对象

  // plugins.js
  export default {
    install(Vue){
        Vue.filter();// 添加过滤器
        Vue.directive();// 添加全局指令
        Vue.mixin();// 配置全局混入
        Vue.prototype.hello = ()=>{} // 添加实例方法
     }
  }
组件的自定义事件
全局事件总线
new Vue({
  render: h => h(App),
  beforeCreate() {
    Vue.prototype.$bus = this;
  },
}).$mount('#app')
消息订阅与发布

消息订阅与发布库:pubsub-js

this.$nextTick()

this.$nextTick()中的回调在下一次DOM更新完毕之后再执行

Vue控制动画和过渡

<translation name="hello" :appear="true"></translation>
<translation/>标签不是真实的DOM元素,只能控制单个DOM元素
<translation-group/> 可以控制多个DOM元素,但是必须给元素设置key属性

<template>
  <div>
    <button @click="show = !show">显示/隐藏</button>
    <!-- name 决定class的名称前缀; appear 决定第一次显示是否执行动画 -->
    <transition name="move" :appear="true">
      <h1 v-show="show">渚清与沙白</h1>
    </transition>
  </div>
</template>

<script>
export default {
    name: "Test",
    data() {
      return {
        show: true,
      };
    },
};
</script>

<style scoped>
  h1 {
    background-color: coral;
  }

  /** 进入时激活的动画 */
  /** 不设置name="move",这里使用 v-enter-active */
  .move-enter-active {
    animation: move 1s linear;
  }
  /** 离开时激活的动画 */
  .move-leave-active {
    animation: move 1s linear reverse;
  }
  @keyframes move {
    from {
      transform: translateX(-100%);
    }
    to {
      transform: translateX(0);
    }
  }
</style>
<template>
  <div>
    <button @click="show = !show">显示/隐藏</button>
    <!-- name 决定class的名称前缀 appear 决定第一次显示是否执行动画 -->
    <transition name="move" :appear="true">
      <h1 v-show="show">渚清与沙白</h1>
    </transition>
  </div>
</template>

<script>
export default {
  name: "Test",
  data() {
    return {
      show: true,
    };
  },
};
</script>

<style scoped>
  h1 {
    background-color: cornflowerblue;
  }
  /** 进入的起点、离开的终点 */
  .move-enter, .move-leave-to {
    opacity: 0;
    transform: translateX(-100%);
  }

  .move-enter-active, .move-leave-active {
    transition: all 0.5s linear;
  }

  /** 进入的终点、离开的起点 */
  .move-enter-to, .move-leave{
    opacity: 1;
    transform: translateX(0);
  }
</style>
  <transition
      name="animate__animated animate_bounce"
      :appear="true"
      enter-active-class="animate__fadeInUp"
      leave-active-class="animate__fadeOutDown"
    >
      <h1 v-show="show">渚清与沙白</h1>
    </transition>
配置代理

发送Ajax请求有哪些方式?

  1. XMLHttpRequest
  2. jQuery
  3. axios
  4. fetch
    jQuery和axios是对XMLHttpRequest的封装。XMLHttpRequest是js内置的对象。fetch和XMLHttpRequest平级,浏览器window对象上就有XMLHttpRequestfetch
    fetch:双层Promise、不支持IE
    **** CORS **** Access-Control-Allow-Origin ****跨域,违背同源策略。同源策略规定协议名主机名端口号必须一致。
    跨域是客户端请求发送成功,服务器也收到数据,并且还返回了数据,浏览器发现跨域,将数据留下来了。

开启代理服务器

  1. nginx
  2. 借助Vue CLI 脚手架
  1. 方式一
    vue.config.js脚本中配置
  devServer: {
    proxy: 'http://localhost:5000',// 接口服务器的地址
  },

优点:配置简单,请求资源时直接发送给前端(8080)即可
缺点:不能配置多个代理,不能灵活控制是否走代理
工作方式:当请求了前端不存在的资源时,该请求会转发给服务器。(优先匹配前端资源)

  1. 方式二
    在vue.config.js脚本中配置
  // /api: 请求前缀 方式二
  // 收到请求时发现有前缀才会转发到接口服务器  否则不做转发
  devServer:{
    proxy: {
       https: false,
      '/api': {//控制是否走代理
        target: 'http://localhost:5000',
        changeOrigin: true,// 用于控制请求头的host值
        ws: true,// 用于支持websocket
        pathRewrite: {// 重写地址
          '^/api': ''// 正则匹配,匹配所有以/api开头的请求路径
        }
      },
      '/demo':{// 配置多个代理
        target: 'http://localhost:5001',
        changeOrigin: true,// 用于控制请求头的host值
        pathRewrite: {// 重写地址
          '^/demo': ''// 正则匹配,匹配所有以/demo开头的请求路径
        }
      }
    }
  }
  // 请求 http://localhost:5000/api/user/login  无需配置pathRewrite
  // 请求 http://localhost:5000/user/login  需配置pathRewrite

changeOrigin: true:服务器收到的请求中请求头的host是http://localhost:5000/,与服务器端口一致
changeOrigin: false:服务器收到的请求中请求头的host是http://localhost:8080/,与前端端口一致
优点:可以配置多个代理,可以灵活控制请求是否走代理
缺点:配置繁琐,请求接口时必须加前缀

插槽
// 定义插槽 插槽名是title,默认值是title变量值
<slot name="title">{{title}}</slot>  

// 使用组件 传递title
<el-collapse-item title="一致性 Consistency" name="1">
    <div>与现实生活一致:与现实生活的流程、逻辑保持一致,遵循用户习惯的语言和概念;</div>
</el-collapse-item>

// 使用插槽 替换title
<template slot="title">
     <div>与现实生活一致:与现实生活的流程、逻辑保持一致,遵循用户习惯的语言和概念;</div>
</template>

2.6.0 新增
跟 v-on 和 v-bind 一样,v-slot 也有缩写,即把参数之前的所有内容 (v-slot:) 替换为字符#。例如 v-slot:header 可以被重写为 #header

定义具名插槽

<template>
  <div>
    <p> 我是子组件 </p>
    <solt></solt>
    <solt name='top'></solt>
  </div>
</template>

引用插槽v-solt:

// v-slot:top写法只能在template标签中使用
<template v-slot:top>
   <h1>1</h1>
   <h1>2</h1>
   <h1>3</h1>
</template>

引用插槽v-solt:header的简写形式#header

// 简写
<template>
  <h1>1</h1>
  <template #top>
      <h1>2</h1>
  </template>
</template>
// 父组件
<category>
  <template slot-scope="scopeData">// slot-scope=""和scope=“”均可,目前已过时!
    <h1>{{scopeData}}</h1>
  </template>
</category>
// v-slot
<category>
  <template  v-slot="scopeData">
    <h1>{{scopeData}}</h1>
  </template>
</category>

// 子组件
 <template>
  <div>
    <slot :datas="list"></slot>
  </div>
 </template>

在 2.6.0 中,我们为具名插槽和作用域插槽引入了一个新的统一的语法 (即v-slot指令)。它取代了 slot 和 slot-scope。

<base-layout>
  <template v-slot:[dynamicSlotName]>
    ...
  </template>
</base-layout>

组件间通信

  1. props
  2. 自定义事件
  3. 全局事件总线
  4. 消息订阅与发布
  5. 插槽
  6. vuex
上一篇下一篇

猜你喜欢

热点阅读