生命周期
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 配置)
使用步骤
- 打开终端,全局安装 (一次):
yarn global add @vue/cli
或npm i @vue/cli -g
- 查看 Vue 版本:
vue --version
- 创建项目架子:
vue create 项目名称
(项目名称不能用中文) - 启动项目:
yarn serve
或npm 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><!-- 通过"自定义属性"向子组件发送数据 --><BaseTesttitle="这是来自'父组件'的数据":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 更新后,要执行的代码
})