Web前端-Vue2+Vue3基础入门到实战项目-Day3
- 生命周期
- 生命周期 & 生命周期四个阶段
- 生命周期钩子
- 生命周期案例
- created应用
- mounted应用
- 案例 - 小黑记账清单
- 工程化开发入门
- 工程化开发和脚手架
- 项目运行流程
- index.html
- main.js
- 组件化
- 组件注册
- 局部注册
- 全局注册
- 来源
生命周期
生命周期 & 生命周期四个阶段
- Vue生命周期: 一个Vue实例从创建到销毁的整个过程
- 生命周期四个阶段:
- 创建(
new Vue()
): 响应式数据 - 挂载: 渲染模板
- 更新: 数据修改, 更新视图
- 销毁: 销毁实例
- 创建(
- 什么时候可以发送初始化渲染请求
- 越早越好, 创建阶段
- 什么时候可以开始操作dom
- 至少dom得渲染出来, 挂载阶段
生命周期钩子
- Vue生命周期函数(钩子函数): Vue生命周期过程中, 会自动运行一些函数, 被称为生命周期钩子 -> 可以在特定阶段运行代码
<!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>
</head><body><div id="app"><h3>{{ title }}</h3><div><button @click="count--">-</button><span>{{ count }}</span><button @click="count++">+</button></div></div><script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script><script>const app = new Vue({el: '#app',data: {count: 100,title: '计数器'},// 1. 创建阶段(准备数据)beforeCreate(){console.log('beforeCreate 响应式数据准备好之前', this.count) // undefined},created() {console.log('beforeCreate 响应式数据准备好之前', this.count) // 100},// 2. 挂载阶段(渲染模板)beforeMount() {console.log('beforeMount 模板渲染之前', document.querySelector('h3').innerHTML) // {{ title }}},mounted() {console.log('mounted 模板渲染之后', document.querySelector('h3').innerHTML) // 计数器},// 3. 更新阶段(修改数据 -> 更新视图)beforeUpdate() {console.log('beforeUpdate 数据修改了, 视图还没更新', document.querySelector('span').innerHTML)},updated() {console.log('updated 数据修改了, 视图已经更新', document.querySelector('span').innerHTML)},// 4. 卸载阶段beforeDestroy() {console.log('beforeDestroy 卸载前')console.log('清除掉一些Vue以外的资源占用, 定时器, 延时器...')},destroyed() {console.log('destroyed 卸载后')}})</script>
</body></html>
生命周期案例
created应用
<!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 class="news" v-for="(item, index) in list" :key="item.id"><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/dist/vue.js"></script><script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script><script>// 接口地址:http://hmajax.itheima.net/api/news// 请求方式:getconst app = new Vue({el: '#app',data: {list: []},async created () {// 1. 发送请求, 获取数据const res = await axios.get('http://hmajax.itheima.net/api/news')// 2. 将数据更新给data中的listthis.list = res.data.data}})</script>
</body>
</html>
mounted应用
<!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/dist/vue.js"></script>
<script>const app = new Vue({el: '#app',data: {words: ''},// 1. 等输入框渲染出来mounted() {// 2. 让输入框获取焦点document.querySelector('#inp').focus()},})
</script></body></html>
案例 - 小黑记账清单
- 基本渲染
- created请求数据(封装渲染方法)
- 将数据更新给data
- 数据动态渲染
- 添加功能
- 收集表单数据 v-model
- 点击发送添加请求
- 重新渲染(封装渲染方法)
- 删除功能
- 点击按钮传递id
- 根据id发送删除请求
- 重新渲染(封装渲染方法)
- 饼图渲染
- mounted初始化echarts实例
- 渲染函数中setOption动态更新饼图(map)
<!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 --><linkrel="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.number="price"/><button type="button" class="btn btn-primary" @click="add">添加账单</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 :class="{red: item.price > 500}"> {{item.price.toFixed(2)}} </td><td><a href="javascript:;" @click.prevent="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.staticfile.org/echarts/4.3.0/echarts.min.js"></script><script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.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. 基本渲染* 1.1 发送请求获取数据 created* 1.2 拿到数据, 存到data的响应式数据中* 1.3 结合数据, 进行渲染 v-for* 1.4 消费统计 => 计算属性* 2. 添加功能* 2.1 收集表单数据 v-model* 2.2 给添加按钮注册点击事件, 发送添加请求* 2.3 需要重新渲染* 3. 删除功能* 3.1 注册点击事件, 传参传 id* 3.2 根据id发送删除请求* 3.3 需要重新渲染* 4. 饼图渲染* 4.1 初始化一个饼图 echarts.init(dom) mounted钩子实现* 4.2 根据数据实时更新饼图 echarts.setOption({ ... })*/const app = new Vue({el: '#app',data: {list: [],name: '',price: '' },created () {this.getList()},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: [],emphasis: {itemStyle: {shadowBlur: 10,shadowOffsetX: 0,shadowColor: 'rgba(0, 0, 0, 0.5)'}}}]})},methods: {async getList(){const res = await axios.get('https://applet-base-api-t.itheima.net/bill', {params: {creator: 'cen'}})this.list = res.data.data// 更新图标this.myChart.setOption({series: [{// data: [// { value: 1048, name: 'Search Engine' },// { value: 735, name: 'Direct' },// ],data: this.list.map(item => ({value: item.price, name: item.name}))}]})},async add(){if(!this.name){alert('请输入消费名称')return}if(typeof this.price !== 'number'){alert('请输入正确的消费价格')return}// 1. 发送添加请求const res = await axios.post('https://applet-base-api-t.itheima.net/bill', {creator: 'cen',name: this.name,price: this.price})// 2. 重新渲染this.getList()this.name = ''this.price = ''},async del(id){// 1. 发送删除请求const res = await axios.delete(`https://applet-base-api-t.itheima.net/bill/${id}`)// 2. 重新渲染this.getList()}},computed: {totalPrice(){return this.list.reduce((sum, item) => sum+item.price, 0)}}})</script></body>
</html>
工程化开发入门
工程化开发和脚手架
- 使用步骤
- 全局安装: 右键开始菜单, 在Windows PowerShell(管理员)中输入
yarn global add @vue/cli
或npm i @vue/cli -g
- 查看Vue版本:
vue --version
- 创建项目架子:
vue create project-name
(项目名不能用中文) - 启动项目:
yarn serve
或npm run serve
(找package.json)
- 全局安装: 右键开始菜单, 在Windows PowerShell(管理员)中输入
项目运行流程
index.html
<!DOCTYPE html>
<html lang=""><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"><link rel="icon" href="<%= BASE_URL %>favicon.ico"><title><%= htmlWebpackPlugin.options.title %></title></head><body><!-- 兼容: 给不支持js的浏览器一个提示 --><noscript><strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><!-- Vue所管理的容器: 将来创建结构动态渲染这个容器 --><div id="app"><!-- 工程化开发模式中: 这里不再直接编写模板语法, 而是通过App.vue提供结构渲染 --></div><!-- built files will be auto injected --></body>
</html>
main.js
// 文件核心作用: 导入App.vue, 基于App.vue创建结构渲染index.html
// 1. 导入 Vue 核心包
import Vue from 'vue'
// 2. 导入 App.vue 根组件
import App from './App.vue'// 提示: 当前处于生命环境(生产环境 / 开发环境)
Vue.config.productionTip = false// 3. Vue实例化, 提供render方法 -> 基于App.vue创建结构渲染index.html
new Vue({// el: '#app', // 作用: 和$mount('选择器')作用一致, 用于指定Vue所管理容器// render: h => h(App),render: (createElement) => {// 基于App创建元素结构return createElement(App)}
}).$mount('#app')
组件化
- 组件化: 页面拆分成一个个组件, 每个组件有着独立的结构, 样式, 行为
- 好处: 便于维护, 利于复用 -> 提升开发效率
- 组件分类: 普通组件, 根组件
- 根组件: 整个应用最上层的组件, 包裹所有普通小组件, 一个根组件App.vue, 包含三个部分
- template结构(只能有一个根节点-vue2)
- style样式(可以支持less, 需要装包less和less-loader)
- script行为
<template><div class="App"><div class="box" @click="fn"></div></div>
</template><script>
export default {methods: {fn () {console.log('点击按钮')}}
}
</script><style lang="less">
/*
让style支持less
1. 给style加上 lang="less"
2. 安装依赖包 less less-loadernpm install less less-loader --save-dev
*/
.App {width: 400px;height: 400px;background-color: pink;.box {width: 100px;height: 100px;background-color: blue;}
}
</style>
组件注册
- 使用: 当成html标签使用
<组件名></组件名>
- 注意:
- 组件名规范 -> 大驼峰命名法, HmHeader
局部注册
局部注册: 只能在注册的组件内使用
- 创建.vue文件(三个组成部分)
- 在使用的组件内导入并注册
<template><div class="App"><!-- 头部组件 --><HmHeader></HmHeader><!-- 主体组件 --><HmMain></HmMain><!-- 底部组件 --><HmFooter></HmFooter></div>
</template><script>
import HmHeader from './components/HmHeader.vue'
import HmMain from './components/HmMain.vue'
import HmFooter from './components/HmFooter.vue'
export default {components: {// '组件名': 组件对象HmHeader: HmHeader,HmMain: HmMain,HmFooter: HmFooter}
}
</script><style>
.App {width: 600px;height: 700px;background-color: #87ceeb;margin: 0 auto;padding: 20px;
}
</style>
全局注册
在main.js中导入
import HmButton from './components/HmButton.vue'
Vue.component('HmButton', HmButton)
来源
黑马程序员. Vue2+Vue3基础入门到实战项目