Vue-2

生命周期

Vue 生命周期

在这里插入图片描述

Vue 生命周期函数

Vue 生命周期过程中,会自动运行一些函数,被称为"生命周期钩子",让开发者可以在特定阶段运行自己的代码

在这里插入图片描述

  • created 应用演示
<body>
  <div class="box">
    <ul v-for="item in list" :key="item.id">
      <li><img :src="item.img"></li>
      <li>{{ item.title }}</li>
      <li>{{ item.source }}</li>
      <li>{{ item.time }}</li> 
      <hr>
    </ul>
  </div>

  <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
  <script src="./js/vue.js"></script>
  <script>
    const boxObj = new Vue({
      el: '.box',
      data: {
        list: []
      },
      // created 应用演示
      async created() {
        const res = await axios.get('http://hmajax.itheima.net/api/news/')
        this.list = res.data.data
      }
    })
  </script>
</body>
  • mounted 应用演示
<body>
  <div class="box">
    <input type="text" v-model="words" class="search">
    <button @click=" words='' ">搜索一下</button>
  </div>

  <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
  <script src="./js/vue.js"></script>
  <script>
    const boxObj = new Vue({
      el: '.box',
      data: {
        words: ''
      },
      // mounted 应用演示
      // 等输入框渲染出来后,获取输入框焦点
      mounted() {
        document.querySelector('.search').focus()
      }
    })
  </script>
</body>

Vue CLI

基本介绍

Vue CLI 是 Vue 官方提供的一个全局命令工具,它可以帮助我们快速创建一个开发 Vue 项目的标准化基础架子(集成了 webpack 配置)

使用步骤

  1. 打开终端,全局安装 (一次):yarn global add @vue/clinpm i @vue/cli -g
  2. 查看 Vue 版本:vue --version
  3. 创建项目架子:vue create 项目名称 (项目名称不能用中文)
  4. 启动项目:yarn servenpm run serve (在 package.json 文件中可自定义命令)

目录文件介绍

在这里插入图片描述

组件化开发

认识组件化

  • 什么是组件化
一个页面可以拆分成一个个组件 (部分),每个组件有着自己独立的结构、样式、行为
  • 组件化的好处
便于维护,利于复用,提升了开发效率
  • 分类
1.根组件(App.vue)
2.普通组件

在这里插入图片描述

  • 组件的三个构成部分
template 结构 (有且只能有一个根元素)
script   行为 (写js逻辑)
style    样式 (可支持less,需要安装包)
在 style 中使用 less 语法的具体操作:
	1.安装依赖包 less 和 less-loader
	  输入命令即可:	yarn add less less-loader -D
	  其中 -D 的意思是只在开发时使用
	2.设置属性,例如: <style lang="less">...</style>

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

组件的注册使用

方式分类

方式一:局部注册,只能在注册的组件内使用

  • 创建 .vue 文件(三个组成部分)
  • 在使用的组件内导入并注册

方式二:全局注册,所有组件内都能使用

注意事项

组件名规范,采用大驼峰命名法

局部注册

  • 文件路径:src/App.vue
<template>
  <div class="App">
    <!-- 头部组件 -->
    <Header></Header>
    <!-- 主体组件 -->
    <Main></Main>
    <!-- 底部组件 -->
    <Footer></Footer>
  </div>
</template>

<script>
// 导入普通组件
import JackHeader from "./components/JackHeader.vue";
import JackMain from "./components/JackMain.vue";
import JackFooter from "./components/JackFooter.vue";

// 导出的默认配置
export default {
  components: {
    // '组件名':组件对象
    Header: JackHeader,
    Main: JackMain,
    Footer: JackFooter
  },
};
</script>

<style>
.App {
  width: 600px;
  height: 700px;
  background-color: #87ceeb;
  margin: 0 auto;
  padding: 20px;
}
</style>
  • 文件路径:src/components/JackHeader.vue
<template>
  <div class="header">头部</div>
</template>

<script>
export default {
  
};
</script>

<style>
.header {
  height: 100px;
  line-height: 100px;
  text-align: center;
  font-size: 30px;
  background-color: #8064a2;
  color: white;
}
</style>
  • 文件路径:src/components/JackMain.vue
<template>
  <div class="main">主体</div>
</template>

<script>
export default {
  
};
</script>

<style>
.main {
  height: 400px;
  line-height: 400px;
  text-align: center;
  font-size: 30px;
  background-color: #f79646;
  color: white;
  margin: 20px 0;
}
</style>
  • 文件路径:src/components/JackFooter.vue
<template>
  <div class="footer">底部</div>
</template>

<script>
export default {
  
};
</script>

<style>
.footer {
  height: 100px;
  line-height: 100px;
  text-align: center;
  font-size: 30px;
  background-color: #4f81bd;
  color: white;
}
</style>

全局注册

  • 文件路径:src/main.js
// 本文件的核心作用:导入 App.vue 文件,基于 App.vue 文件创建结构渲染 index.html

// 1. 导入 Vue 核心包
import Vue from 'vue'

// 2. 导入 App.vue 根组件
import App from './App.vue'

// 5. 导入组件
import JackButton from "./components/JackButton.vue"

// 3. 提示:当前处于什么环境(生产环境 / 开发环境)
Vue.config.productionTip = false

// 6. 对组件进行全局注册
// Vue.component(组件名, 组件对象)
Vue.component('MyButton', JackButton)

// 4. Vue 实例化,提供 render 方法,基于 App.vue 创建结构渲染 index.html
// new Vue({
//   el: '#app',
//   render: (createElement) => { return createElement(App) }
// })
// 上面的代码等价于下面代码
new Vue({
  render: createElement => createElement(App)
}).$mount('#app')

组件的样式冲突

冲突问题

默认情况下,写在组件中的样式会"全局生效",因此,很容易造成多个组件之间的样式冲突问题发生

解决办法

可以给组件中的 style 标签加上 scoped 属性,这样可以让该组件的 style 样式只作用于当前组件,例如 `<style scoped>...</style>`

data 函数

<template>
  <div>
    <button @click="count--">-</button>
    <span>{{ count }}</span>
    <button @click="count++">+</button>
  </div>
</template>

<script>
export default {
  // data 函数保证每一个组件实例,维护一个独立的数据对象
  data() {
    return {
      count: 99,
    };
  },
};
</script>

<style scoped>
div {
  margin: 20px;
}
span {
  margin: 10px;
}
</style>

组件通信

基本内容

含义介绍:组件通信,就是指"组件与组件"之间的数据传递

组件关系

  • 父子关系
  • 非父子关系

通信方案

  • 父子关系 =》使用 props & $emit
  • 非父子关系 =》使用 provide & inject 或者 event bus
  • 通用解决方案 =》使用 Vuex(适合复杂业务场景)

在这里插入图片描述

父子通信

  • 文件路径:src/App.vue
<template>
  <div>
    <!-- 通过"自定义属性"向子组件发送数据 -->
    <BaseTest
      title="这是来自'父组件'的数据"
      :username="username"
      :age="age"
      :isSingle="isSingle"
      :hobby="hobby"
      @content="infoFn"
    ></BaseTest>
    <!-- 展示来自"子组件"的数据 -->
    <div class="box">{{ something }}</div>
  </div>
</template>

<script>
import BaseTest from "./components/BaseTest.vue";
export default {
  data() {
    return {
      something:'',
      // 假设从服务器端获得了数据
      username: "Jack",
      age: 30,
      isSingle: true,
      hobby: ["code", "run", "swim"],
    };
  },
  methods: {
    // 接收来自"子组件"的数据
    infoFn(info) {
      this.something = info
    },
  },
  components: {
    BaseTest,
  },
};
</script>

<style>
</style>
  • 文件路径:src/components/BaseTest.vue
<template>
  <div>
    <hr />
    <h3>{{ title }}</h3>
    <p>姓名:{{ username }}</p>
    <p>年龄:{{ age }}</p>
    <p>是否单身:{{ isSingle ? "是" : "否" }}</p>
    <p>兴趣爱好:{{ hobby.join("、") }}</p>
    <button @click="func">确认收到</button>
    <hr />
  </div>
</template>

<script>
export default {
  // prop 是在组件上注册的一些自定义属性
  // 通过 props 进行接收这些属性
  props: ["title", "username", "age", "isSingle", "hobby"],
  methods: {
    func() {
      // 通过 $emit 向"父组件"发送消息
      this.$emit("content", "子组件已确认收到消息");
    },
  },
};
</script>

<style scoped>
</style>

props 验证

  • 文件路径:src/App.vue
<template>
  <div>
    <!-- 通过"自定义属性"向子组件发送数据 -->
    <BaseTest :w="width"></BaseTest>
  </div>
</template>

<script>
import BaseTest from "./components/BaseTest.vue";
export default {
  data() {
    return {
      width: 300,
    };
  },
  components: {
    BaseTest,
  },
};
</script>

<style>
</style>
  • 文件路径:src/components/BaseTest.vue
<template>
  <div>
    <div class="base-progress">
      <div class="inner" :style="'width:' + w + '%'"></div>
    </div>
    <span>{{ w }}%</span>
  </div>
</template>

<script>
export default {
  // 通过 props 接收自定义属性,然后对其进行校验,如果校验失败,则控制台报错
  // 1. 基础写法(类型校验)
  // props: {
  //   w: Number,  // 约束传递过来的数据的类型
  // },
  // 2. 完整写法(更多校验)
  props: {
    w: {
      type: Number, // 类型
      required: true, // 是否必须(非空判断)
      default: 0, // 默认值
      validator(value) {
        if (value < 0 && value > 100) return true;
        else {
          console.error("数据范围必须是0~100");
          return false; // false 代表没有通过校验
        }
      },
    },
  },
};
</script>

<style scoped>
.base-progress {
  height: 26px;
  width: 400px;
  border-radius: 15px;
  background-color: #272425;
  border: 3px solid #272425;
  box-sizing: border-box;
  margin-bottom: 10px;
}
.inner {
  height: 90%;
  background-color: rgb(124, 193, 246);
  border-radius: 15px;
  border: 1px solid #272425;
}
</style>

非父子通信

  • 文件路径:utils/EventBus.js
// 1. 创建一个事件总线(空的 Vue 实例)
import Vue from 'vue'
const Bus = new Vue()
// 2. 导出事件总线
export default Bus
  • 文件路径:src/App.vue
<template>
  <div>
    <BaseA></BaseA>
    <BaseB></BaseB>
  </div>
</template>

<script>
import BaseA from "./components/BaseA.vue"
import BaseB from "./components/BaseB.vue"
export default {
  components: {
    BaseA,
    BaseB,
  },
};
</script>

<style>
</style>
  • 文件路径:src/components/BaseB.vue
<template>
  <div>
    <span>BaseB</span>
    <button @click="sendMsg">发布消息</button>
  </div>
</template>

<script>
import Bus from "../utils/EventBus.js";
export default {
  methods: {
    sendMsg(){
      // 发布消息,该消息可以被多个组件同时接收
      Bus.$emit('B_Send_Msg','Hello, How are you?')
    }
  },
};
</script>

<style>
</style>
  • 文件路径:src/components/BaseA.vue
<template>
  <div>
    <span>BaseA</span>
  </div>
</template>

<script>
import Bus from "../utils/EventBus.js";
export default {
  // 钩子函数(详情回顾:生命周期知识点)
  created() {
    // 监听 Bus 事件
    Bus.$on("B_Send_Msg", (msg) => {
      alert("来自B发送的信息: " + msg);
    });
  },
};
</script>

<style>
</style>

跨层级非父子通信

  • 文件路径:src/App.vue
<template>
  <div>
    <BaseA></BaseA>
  </div>
</template>

<script>
import BaseA from "./components/BaseA.vue";
export default {
  // 所有的子孙后代都能共享这些数据
  provide() {
    return {
      color: this.color, // 传递"简单类型"是非响应式的,即数据不会更新
      userInfo: this.userInfo, // 传递"复杂类型"是响应式的,数据可以动态变化(推荐)
    };
  },
  data() {
    return {
      color: "pink",
      userInfo: {
        name: "Jack",
        age: 30,
      },
    };
  },
  components: {
    BaseA,
  },
};
</script>

<style>
</style>
  • 文件路径:src/components/BaseA.vue
<template>
  <div>
    <p>{{ color }}</p>
    <p>{{ userInfo.name }}</p>
    <p>{{ userInfo.age }}</p>
  </div>
</template>

<script>
export default {
  inject: ["color", "userInfo"],
};
</script>

<style>
</style>

v-model 原理

原理解析

v-model 本质上是一个"语法糖"(即:value 属性 和 input 事件的合写)

作用:提供数据的双向绑定

  • 数据变化,视图跟着变化
  • 视图变化,数据跟着变化

注意:$event 用于模板中,获取事件的形参

<template>
   <div>
    <input v-model="msg" type="text" />
    <!-- 上面的代码本质是下面的代码 -->
    <input :value="msg" @input="msg = $event.target.value" type="text" />
  </div>
</template>

表单类组件封装

数据绑定问题

在开发中,我们需要对"表单类组件"进行封装,而这个"子组件"的数据是来自"父组件"的,我们无法直接对"子组件"的"表单类标签"使用 v-model 进行双向的数据绑定,所以我们需要使用 `v-model拆解` 实现绑定数据

具体解决

  • 文件路径:src/App.vue
<template>
  <div>
    <BaseSelect
    :cityId="selectId"
    @changeId="selectId = $event"
    ></BaseSelect>
  </div>
</template>

<script>
import BaseSelect from "./components/BaseSelect.vue";
export default {
  data() {
    return {
      selectId: "102",	// 城市编号
    };
  },
  components: {
    BaseSelect,
  },
};
</script>

<style>
</style>
  • 文件路径:src/components/BaseSelect.vue
<template>
  <div>
    <select :value="cityId" @change="handleChange">
      <option value="101">北京</option>
      <option value="102">上海</option>
      <option value="103">广州</option>
      <option value="104">深圳</option>
      <option value="105">重庆</option>
    </select>
  </div>
</template>

<script>
export default {
  props: {
    cityId: String,
  },
  methods: {
    handleChange(e) {
      this.$emit("changeId", e.target.value);
    },
  },
};
</script>

<style>
</style>

组件数据双向绑定

.sync 修饰符

作用:可以实现"子组件"与"父组件"的双向绑定,简化代码

特定:prop 属性名,可以自定义,不用固定为 value

具体演示

  • 文件路径:src/App.vue
<template>
  <div>
    <button @click="isShow = true">确认付款</button>
    <!-- <BaseDialog :visible="isShow" @update:visible="isShow = $event"></BaseDialog> -->
    <!-- :visible.sync => :visible + @update:visible 化简后结果如下所示:-->
    <BaseDialog :visible.sync="isShow"></BaseDialog>
  </div>
</template>

<script>
import BaseDialog from "./components/BaseDialog.vue";
export default {
  data() {
    return {
      isShow: false,
    };
  },
  components: {
    BaseDialog,
  },
};
</script>

<style>
</style>
  • 文件路径:src/components/BaseDialog.vue
<template>
  <div>
    <div v-show="visible" class="dialog">
      <p>你是否确定要付款?</p>
      <button @click="close">确定</button>
      <button @click="close">取消</button>
    </div>
  </div>
</template>

<script>
export default {
  props: {
    visible: Boolean,
  },
  methods: {
    close() {
      this.$emit("update:visible", false);
    },
  },
};
</script>

<style>
.dialog {
  margin: 0 auto;
  width: 200px;
  border: 5px solid #000;
  border-radius: 10px;
}
</style>

ref 和 $refs

通过 ref 与 $refs 获取 dom 元素

<template>
  <div>
    <div class="box" ref="myBox"></div>
  </div>
</template>

<script>
export default {
  mounted() {
    // 通过 ref 与 $refs 获取 dom 元素,防止 document.querySelector() 从全局中获取元素产生冲突
    const myBox = this.$refs.myBox;
  },
};
</script>

<style>
</style>

通过 ref 与 $refs 获取"组件实例"

  • 第一步:给目标组件添加 ref 属性
<BaseForm ref="hhh"></BaseForm>
  • 第二步:使用 this.$refs.xxx 获取目标组件(同时可以调用其方法)
this.$refs.hhh.组件方法

Vue 异步更新

问题解析:

Vue 为了提高性能,修改了 dom 并不会立即更新,而是会等待一会儿再集中一起更新。如果在 dom 还没有更新的时候,代码继续执行下去,可能会出现找不到某个 dom 元素的问题。 

解决方案:

使用 $nextTick 后,等 DOM 更新后,才会触发执行此方法里的函数体
this.$nextTick(()=>{
	// 这里写 DOM 更新后,要执行的代码
})
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值