1.生命周期钩子
1.是什么
生命周期
概念:就是一个Vue实例从创建 到 销毁 的整个过程
生命周期包括:① 创建 ② 挂载 ③ 更新 ④ 销毁 四个阶段
1.创建阶段:创建响应式数据
2.挂载阶段:渲染模板
3.更新阶段:修改数据,更新视图
4.销毁阶段:销毁Vue实例
作用:Vue底层提供了很多生命周期钩子,让开发者可以在Vue生命周期【特定阶段】植入一些自己的代码来实现一些功能。
生命周期钩子
生命周期钩子概念: 钩子是Vue生命周期过程中,会自动执行的一些函数
作用: 让开发者可以在【特定阶段】运行自己的代码
思考:有哪些功能需要用到生命周期钩子呢?
- 什么时候可以发送初始化渲染请求?(越早越好)
- 什么时候可以开始操作dom?(至少dom得渲染出来)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> </head> <body> <div id="app"> <h3>{{ title }}</h3> <div> <button @click="count--">-</button> <span>{{ count }}</span> <button @click="count++">+</button> </div> </div> <br /> <hr /> <button id="btn">销毁Vue</button> <script src="./vue.2.7.js"></script> <script> const app = new Vue({ el: '#app', data: { count: 100, title: '计数器', }, // 1. 创建阶段(准备数据) beforeCreate() { console.log('1 .beforeCreate'); }, created() { // ✨✨作用:发ajax请求获取数据赋值给响应式数据(data中定义的数据) console.log('2. created'); }, // 2. 挂载阶段(渲染模板) beforeMount() { console.log('3. beforeMount'); }, mounted() { // ✨✨操作dom操作,比如: document.querySelector('button') console.log('4. mounted'); }, // 3. 更新阶段(修改数据 → 更新视图) beforeUpdate() { console.log('1. beforeUpdate'); }, updated() { console.log('2. updated'); }, // 4. 卸载阶段 beforeDestroy() { // ✨✨✨ 实例销毁之前,做一些定时器的清除工作(clearInterval() , clearTimeout()) console.log('1. beforeDestroy'); }, destroyed() { console.log('2. destroyed'); }, }); btn.addEventListener('click', () => { app.$destroy(); }); </script> </body> </html>
复制
2.案例
1.案例1
需求:页面进入后请求新闻接口获取数据后展示在页面上
核心思路:
1. 等data中的数据侦听完成(形成响应式数据), created 钩子
2. 发送ajax请求获取数据v-for渲染
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> * { margin: 0; padding: 0; list-style: none; } .news { display: flex; height: 120px; width: 600px; margin: 0 auto; padding: 20px 0; cursor: pointer; } .news .left { flex: 1; display: flex; flex-direction: column; justify-content: space-between; padding-right: 10px; } .news .left .title { font-size: 20px; } .news .left .info { color: #999999; } .news .left .info span { margin-right: 20px; } .news .right { width: 160px; height: 120px; } .news .right img { width: 100%; height: 100%; object-fit: cover; } </style> </head> <body> <div id="app"> <ul> <li v-for="(item, index) in list" :key="item.id" class="news"> <div class="left"> <div class="title">{{ item.title }}</div> <div class="info"> <span>{{ item.source }}</span> <span>{{ item.time }}</span> </div> </div> <div class="right"> <img :src="item.img" alt=""> </div> </li> </ul> </div> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script> <script> // 接口地址:http://hmajax.itheima.net/api/news // 请求方式:get const app = new Vue({ el: '#app', data: { list: [] }, async created () { // 1. 发送请求获取数据 const res = await axios.get('http://hmajax.itheima.net/api/news') // 2. 更新到 list 中,用于页面渲染 v-for this.list = res.data.data } }) </script> </body> </html>
复制
2.案例2
需求:页面进入后,文本框自动获取焦点
核心思路:
1. 等input框渲染出来 mounted 钩子
2. 让input框获取焦点 inp.focus()
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>示例-获取焦点</title> <!-- 初始化样式 --> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/reset.css@2.0.2/reset.min.css"> <!-- 核心样式 --> <style> html, body { height: 100%; } .search-container { position: absolute; top: 30%; left: 50%; transform: translate(-50%, -50%); text-align: center; } .search-container .search-box { display: flex; } .search-container img { margin-bottom: 30px; } .search-container .search-box input { width: 512px; height: 16px; padding: 12px 16px; font-size: 16px; margin: 0; vertical-align: top; outline: 0; box-shadow: none; border-radius: 10px 0 0 10px; border: 2px solid #c4c7ce; background: #fff; color: #222; overflow: hidden; box-sizing: content-box; -webkit-tap-highlight-color: transparent; } .search-container .search-box button { cursor: pointer; width: 112px; height: 44px; line-height: 41px; line-height: 42px; background-color: #ad2a27; border-radius: 0 10px 10px 0; font-size: 17px; box-shadow: none; font-weight: 400; border: 0; outline: 0; letter-spacing: normal; color: white; } body { background: no-repeat center /cover; background-color: #edf0f5; } </style> </head> <body> <div class="container" id="app"> <div class="search-container"> <img src="https://www.itheima.com/images/logo.png" alt=""> <div class="search-box"> <input type="text" v-model="words" id="inp"> <button>搜索一下</button> </div> </div> </div> <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { words: '' }, // 核心思路: // 1. 等input框渲染出来 mounted 钩子 // 2. 让input框获取焦点 inp.focus() mounted () { document.querySelector('#inp').focus() } }) </script> </body> </html>
复制
3.案例3
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> <!-- CSS only --> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" /> <style> .red { color: red !important; } .search { width: 300px; margin: 20px 0; } .my-form { display: flex; margin: 20px 0; } .my-form input { flex: 1; margin-right: 20px; } .table > :not(:first-child) { border-top: none; } .contain { display: flex; padding: 10px; } .list-box { flex: 1; padding: 0 30px; } .list-box a { text-decoration: none; } .echarts-box { width: 600px; height: 400px; padding: 30px; margin: 0 auto; border: 1px solid #ccc; } tfoot { font-weight: bold; } @media screen and (max-width: 1000px) { .contain { flex-wrap: wrap; } .list-box { width: 100%; } .echarts-box { margin-top: 30px; } } </style> </head> <body> <div id="app"> <div class="contain"> <!-- 左侧列表 --> <div class="list-box"> <!-- 添加资产 --> <form class="my-form"> <input type="text" class="form-control" placeholder="消费名称" v-model.trim="name" /> <input type="text" class="form-control" placeholder="消费价格" v-model.trim.number="price" /> <button type="button" class="btn btn-primary" @click="addsalary"> 添加账单 </button> </form> <table class="table table-hover"> <thead> <tr> <th>编号</th> <th>消费名称</th> <th>消费价格</th> <th>操作</th> </tr> </thead> <tbody> <tr v-for="(item, index) in list" :key="item.id"> <td>{{index+1}}</td> <td>{{item.name}}</td> <td>{{item.price}}</td> <td><a href="javascript:;" @click="del(item.id)">删除</a></td> </tr> </tbody> <tfoot> <tr> <td colspan="4">消费总计:{{totalPrice}}</td> </tr> </tfoot> </table> </div> <!-- 右侧图表 --> <div class="echarts-box" id="main"></div> </div> </div> <script src="https://cdn.jsdelivr.net/npm/echarts@5.4.0/dist/echarts.min.js"></script> <script src="./vue.2.7.js"></script> <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script> <script> /** * 接口文档地址: * https://www.apifox.cn/apidoc/shared-24459455-ebb1-4fdc-8df8-0aff8dc317a8/api-53371058 * * 功能需求: * 1. 基本渲染 * 2. 添加功能 * 3. 删除功能 * 4. 饼图渲染 */ const app = new Vue({ el: '#app', data: { name: '', price: '', list: [], }, created() { this.getLists(); }, mounted() { this.myChart = echarts.init(document.getElementById('main')); this.myChart.setOption({ // 大标题 title: { text: '消费账单列表', left: 'center', }, // 提示框 tooltip: { trigger: 'item', }, // 图例 legend: { orient: 'vertical', left: 'left', }, // 数据项 series: [ { name: '消费账单', type: 'pie', radius: '50%', // 半径 data: [ { value: 1048, name: '球鞋' }, { value: 735, name: '防晒霜' }, ], emphasis: { itemStyle: { shadowBlur: 10, shadowOffsetX: 0, shadowColor: 'rgba(0, 0, 0, 0.5)', }, }, }, ], }); }, computed: { totalPrice() { return this.list.reduce((prev, curr) => { return (prev += curr.price); }, 0); }, }, methods: { async getLists() { let res = await axios({ url: 'https://applet-base-api-t.itheima.net/bill', params: { creator: 'zs123', }, }); this.list = res.data.data; this.myChart.setOption({ series: [ { data: this.list.map((item) => { return { value: item.price, name: item.name }; }), }, ], }); }, addsalary() { if (this.name && this.price) { axios({ method: 'post', url: 'https://applet-base-api-t.itheima.net/bill', data: { creator: 'zs123', name: this.name, price: this.price, }, }).then((res) => { this.getLists(); this.name = ''; this.price = ''; }); } else { alert('请输入消费名称或价格'); } }, async del(id) { console.log(id); await axios({ method: 'delete', url: `https://applet-base-api-t.itheima.net/bill/${id}`, }); this.getLists(); }, }, }); </script> </body> </html>
复制
2.工程化
开发Vue的两种方式
- 核心包传统开发模式:基于html / css / js 文件,直接引入核心包,开发 Vue。
- 工程化开发模式:基于构建工具(例如:webpack)的环境中开发Vue。
脚手架Vue CLI
概念:Vue CLI 是Vue官方提供的一个全局命令工具 【集成了webpack配置】
作用:可以帮助我们快速创建一个Vue项目的标准化基础架子
好处:
- 内置了webpack工程化需要的相关loader,plugins等工具
-
- css-loader
- html-webpack-plugin
- 开箱即用,零配置
-
- 比如扩展less-loader 只需要 npm i less less-loader安装即可支持,无需再配置
使用步骤:
- 全局安装(只需安装一次即可) yarn global add @vue/cli 或者 npm i @vue/cli -g
- 查看vue/cli版本: vue --version
- 创建项目架子:vue create project-name(项目名不能使用中文,所在路径中不能有特殊字符比如 (),& )
- 启动项目:yarn serve 或者 npm run serve(命令不固定,找package.json)
项目目录介绍和运行流程
1.项目目录介绍
虽然脚手架中的文件有很多,目前咱们只需认识三个文件即可
- main.js 入口文件
- App.vue App根组件
- index.html 模板文件
2.运行流程
3.组件化开发
1.是什么
组件化:一个页面可以拆分成一个个组件,每个组件有着自己独立的结构、样式、行为。
好处:便于维护,利于复用 → 提升开发效率。
组件分类:普通组件、根组件。
比如:下面这个页面,可以把所有的代码都写在一个页面中,但是这样显得代码比较混乱,难易维护。咱们可以按模块进行组件划分
Vue中每个组件均由三部分构成:
template:结构 (有且只能一个根元素)
script: js逻辑
style: 样式 (可支持less,需要装包)
(1) 装包: npm i less less-loader -D
(2)style标签,lang="less" 开启less功能
语法高亮插件 Vetur
2.局部注册
概念:局部注册的组件,只能在注册的组件内使用
组件局部注册步骤:
① 创建.vue文件(三个组成部分)
② 注册组件
③ 使用组件<组件名></组件名>
组件名规范 —> 大驼峰命名法, 如 HelloWorld
3.全局注册
概念:全局注册的组件,在项目的任何组件中都能使用
组件全局注册步骤:
① 创建.vue文件(三个组成部分)
② 在main.js中进行全局注册
③ 项目任意组件中使用
组件名规范 —> 大驼峰命名法, 如 HmButton
注意:组件的template中只能有一个根元素