重点梳理及引用知识点的铺垫
知道window.onload的作用
ajax是什么, XMLHttpRequest的使用步骤(四步)
Promise的作用
async和await的作用-如何和Promise配合
同步异步的概念, 代码执行顺序
请求和响应, 以及JSON解析能力
Vue基础, 组件使用, props传值, 组件通信, 计算属性使用, 对象引用类型使用
笔记内容概览
vue组件生命周期
axios的使用
$refs, $nextTick使用
购物车案例开发
vue组件生命周期
什么是组件生命周期
一个组件从 创建 到 销毁 的整个过程就是生命周期
生命周期函数(钩子函数)
vue 框架内置函数,随着组件的生命周期,自动 按次序 执行
作用:特定的时间点,执行某些特定的操作
场景: 组件创建完毕后,可以在created 生命周期函数中发起Ajax 请求,从而初始化 data 数据
四个阶段:
初始化 => 创建组件 => beforeCreate created
挂载 => 渲染显示组件 => beforeMount mouted
更新 => 修改了变量 => 触发视图刷新 => beforeUpdate updated
销毁 => 切换页面 => 会把组件对象从内存删除 => beforeDestory destoryed
官网文档
下图展示了实例的生命周期。你不需要立马弄明白所有的东西,不过随着你的不断学习和使用,它的参考价值会越来越高。
生命周期函数执行过程
components/MyCom.vue - 创建一个文件(复制代码,演示执行过程)
<template>  <div>    <ul id="myUl">      <li v-for="(item, ind) in arr" :key="ind">{{ item }}</li>    </ul>    <button      @click="arr.push(Math.random() * 10)"    >      增加一个元素    </button>  </div></template><script>export default {  data () {    return {      msg: "我是变量",      arr: [1, 2, 3, 4]    }  },  beforeCreate () {    // 1. 创建前    console.log("beforeCreate --- 实例初始化前")    console.log(this.msg) // undefined  },  created () {    // 2. 创建后=> 发送ajax请求    console.log("created ---  实例初始化后")    console.log(this.msg) // "我是变量"    // 给对象添加属性    this.timer = setInterval(()=>{      console.log(Date.now())    }, 1000)    this.fn = ()=>{ console.log(Date.now())}    document.addEventListener('mousemove', this.fn)  },  beforeMount () {    // 3. 挂载前    console.log("beforeMount --- vue的虚拟DOM, 挂载到真实的网页之前")    console.log(document.getElementById("myUl")) // null    // console.log(document.getElementById("myUl").children[1].innerHTML) // 报错  },  mounted () {    // 4. 挂载后=> 操作dom    console.log("mounted --- vue的虚拟DOM, 挂载到真实的网页上 ")    // console.log(document.getElementById("myUl").children[1].innerHTML)    console.log(document.querySelector('#myUl').children[1].innerText)  },  beforeUpdate () {    // 5. 更新前    console.log("beforeUpdate --- 数据更新, 页面更新前")    // 比如点击新增数组元素, vue会触发此生命周期函数, 但是此时页面并未更新, 所以获取不到新增的li标签    // console.log(document.getElementById("myUl").children[4].innerHTML) // 报错  },  updated () {    // 6. 更新后    console.log("updated --- 数据更新, 页面更新后")    console.log(document.getElementById("myUl").children[4].innerHTML)  },  beforeDestroy () {    // 7. 销毁前     // (清除定时器 / 解绑js定义的事件)    console.log("beforeDestroy --- 实例销毁之前调用")  },  destroyed () {    // document.removeEventListener('mousemove', 回调函数的名字)    document.removeEventListener('mousemove', this.fn)    // clearInterval(this.timer)    // 8. 销毁后    // (清除定时器 / 解绑js定义的事件)    console.log("destroyed --- 销毁完成")  }};</script>
App.vue - 引入使用
通过v-if的切换,可以触发子组件的destory
<template>  <div>    <MyCom v-if="isShow"/>    <hr>    <button @click="isShow = !isShow">销毁Life组件</button>  </div></template><script>import MyCom from './MyCom'export default {  data(){    return {      isShow: true    }  },  components: {    MyCom  }};</script>
小结
生命周期钩子函数是可选的
打印结果
复习axios
axios文档 是一个专门用于发送ajax请求的库
支持客户端发送Ajax请求
支持服务端Node.js发送请求
支持Promise相关用法
支持请求和响应的拦截器功能
自动转换JSON数据
axios 底层还是原生js实现, 内部通过Promise封装的
axios基本使用
npm i axios
// 1. 导入axiosimport axios from 'axios'// 2. 使用1axios({  method: '请求方式', // get post  url: '请求地址',  data: {    // 拼接到请求体的参数,  post请求的参数    xxx: xxx,  },  params: {  // 拼接到请求url的参数, get请求的参数    xxx: xxx   }}).then(res => {  console.log(res.data) // 后台返回的结果}).catch(err => {  console.log(err) // 后台报错返回})// 2. 使用2 async awaitfunction async fn(){  try {    const res = await axios.get('地址')  } catch (err) {    console.log('err', err)  }}
案例-图书演示
    练习模板
    <template>  <div>    <div>      <h4>1. 查询所有图书信息</h4>      <button @click="allFn">查询-看控制台</button>    </div>    <div>      <h4>2. 查询某本图书信息</h4>      <input type="text" placeholder="输入要查询的书名" v-model="bookName" />      <button @click="selFn">查询-看控制台</button>    </div>    <div>      <h4>3. 新增图书信息</h4>      <input type="text" placeholder="书名" v-model="obj.bookname" />      <input type="text" placeholder="作者" v-model="obj.author" />      <input type="text" placeholder="出版社" v-model="obj.publisher" />    </div>    <button @click="addFn">点击新增图书</button>  </div></template><script>export default {  data () {    return {      bookName: "",      obj: {        bookname: "",        author: "",        publisher: ""      }    }  },  methods: {    // 获取所有图书    allFn () {    },    // 查询某个图书信息    selFn () {    },    // 新增图书    addFn () {    }  },}</script>
    参考答案
<template>  <div>    <div>      <h4>1. 查询所有图书信息</h4>      <button @click="allFn">查询-看控制台</button>    </div>    <div>      <h4>2. 查询某本图书信息</h4>      <input type="text" placeholder="输入要查询的书名" v-model="bookName" />      <button @click="selFn">查询-看控制台</button>    </div>    <div>      <h4>3. 新增图书信息</h4>      <input type="text" placeholder="书名" v-model="obj.bookname"/>      <input type="text" placeholder="作者" v-model="obj.author"/>      <input type="text" placeholder="出版社" v-model="obj.publisher"/>    </div>    <button @click="addFn">点击新增图书</button>  </div></template><script>import axios from "axios";export default {  data() {    return {      bookName: "",      obj: {          bookname: "",          author: "",          publisher: ""      }    };  },  methods: {    allFn() {      axios({        method: "get",        url: "http://123.57.109.30:3006/api/getbooks",      }).then(function (res) {        console.log(res);      });    },    selFn() {      axios({        method: "get",        url: "http://123.57.109.30:3006/api/getbooks",        params: {          bookname: this.bookName,        },      }).then(function (res) {        console.log(res);      });    },    addFn(){        axios({            method: "POST",            url: "http://123.57.109.30:3006/api/addbook",            data: {                appkey: "7250d3eb-18e1-41bc-8bb2-11483665535a",                ...this.obj            }        }).then(res => {            console.log(res);        })    }  },};</script><style></style>
全局默认配置
            入口main.js导入axios进行配置        
+ import axios from "axios";+ axios.defaults.baseURL = "http://123.57.109.30:3006"
组件中使用
import axios from "axios";// 所有的axios请求都可以不带域名前缀了, 默认都拼接基础urlaxios({    method: "get",-    url: "http://123.57.109.30:3006/api/getbooks",+    url: "/api/getbooks",}).then(function (res) {    console.log(res);});
$refs使用
目标
掌握ref的使用,能用它获取dom元素或者组件实例
获取到原生dom标签
App.vue
<template>  <div>    <h1 ref="myH1">1. ref获取原生dom</h1>    <button @click="fn">点击修改上面内容</button>  </div></template><script>export default {  methods: {    fn() {      console.log(this.$refs.myH1); // <h1></h1> 原生DOM标签      this.$refs.myH1.innerHTML = "改内容了";    }  }}</script>
获取组件对象
components/Demo.vue
<template>  <div>    <p :style="{ color: ind == 0 ? 'red' : '' }">首页</p>    <p :style="{ color: ind == 1 ? 'red' : '' }">分类</p>    <p :style="{ color: ind == 2 ? 'red' : '' }">爱好</p>  </div></template><script>export default {  data() {    return {      ind: 0,    };  },  methods: {    changeIndex(index) {      this.ind = index;    },  },};</script>
App.vue - 获取组件对象 - 调用组件方法
<h1>2. 调用demo组件方法</h1><button @click="fn2">点击demo组件里最后一个高亮</button><Demo ref="de"></Demo><script>import Demo from "./components/Demo";export default {  components: {    Demo,  },  methods: {    // ...省略    fn2() {      this.$refs.de.changeIndex(2);    },  },};</script>
小结
ref可以用来获取dom或组件实例对象
nextTick使用
背景
数据变化而导致的dom更新是异步的
基础示例
<template>  <div>    <MyCom v-if="isShow"/>    <hr>    <button @click="isShow = !isShow">销毁Life组件</button>  </div></template><script>import MyCom from './MyCom'export default {  data(){    return {      isShow: true    }  },  components: {    MyCom  }};</script>0
应用场景
在js代码中,修改数据之后,希望随后拿到最新的dom
<template>  <div>    <MyCom v-if="isShow"/>    <hr>    <button @click="isShow = !isShow">销毁Life组件</button>  </div></template><script>import MyCom from './MyCom'export default {  data(){    return {      isShow: true    }  },  components: {    MyCom  }};</script>1
小结
dom是异步更新的, $nextTick可以等待dom更新后触发此方法
nextTick使用场景
目标: 点击搜索按钮, 显示聚焦的输入框, 按钮消失=》默认输入框不显示
Search.vue
<template>  <div>    <MyCom v-if="isShow"/>    <hr>    <button @click="isShow = !isShow">销毁Life组件</button>  </div></template><script>import MyCom from './MyCom'export default {  data(){    return {      isShow: true    }  },  components: {    MyCom  }};</script>2
App.vue - 引入使用即可
扩展-$nextTick返回Promise对象
$nextTick底层异步语法promise.then与定时器应用Vue2源码分析(本质是把vue中的 同步执行的任务转化成微任务执行如果浏览器不支持就转成定时器的宏任务)
所以上面还可以改成如下写法
<template>  <div>    <MyCom v-if="isShow"/>    <hr>    <button @click="isShow = !isShow">销毁Life组件</button>  </div></template><script>import MyCom from './MyCom'export default {  data(){    return {      isShow: true    }  },  components: {    MyCom  }};</script>3
案例-购物车
-项目初始化
目标
初始化新项目,选择less编写样式,清空不要的东西;下载bootstrap库;
创建项目和安装开发依赖
<template>  <div>    <MyCom v-if="isShow"/>    <hr>    <button @click="isShow = !isShow">销毁Life组件</button>  </div></template><script>import MyCom from './MyCom'export default {  data(){    return {      isShow: true    }  },  components: {    MyCom  }};</script>4
在main.js中引入bootstrap库样式
<template>  <div>    <MyCom v-if="isShow"/>    <hr>    <button @click="isShow = !isShow">销毁Life组件</button>  </div></template><script>import MyCom from './MyCom'export default {  data(){    return {      isShow: true    }  },  components: {    MyCom  }};</script>5
-创建组件
目标:根据页面效果,进行组件拆分=》组件化开发
组件拆分技巧:可以按照布局进行拆分,根据实际需要细化组件拆分
图示:
按照需求, 把项目页面拆分成几个组件
MyHeader组件
<template>  <div>    <MyCom v-if="isShow"/>    <hr>    <button @click="isShow = !isShow">销毁Life组件</button>  </div></template><script>import MyCom from './MyCom'export default {  data(){    return {      isShow: true    }  },  components: {    MyCom  }};</script>6
MyFooter组件
<template>  <div>    <MyCom v-if="isShow"/>    <hr>    <button @click="isShow = !isShow">销毁Life组件</button>  </div></template><script>import MyCom from './MyCom'export default {  data(){    return {      isShow: true    }  },  components: {    MyCom  }};</script>7
MyGoods组件 - 商品列表
<template>  <div>    <MyCom v-if="isShow"/>    <hr>    <button @click="isShow = !isShow">销毁Life组件</button>  </div></template><script>import MyCom from './MyCom'export default {  data(){    return {      isShow: true    }  },  components: {    MyCom  }};</script>8
label 的for属性
MyCount组件=>在MyGoods中引入使用MyCount组件
    components/MyCount.vue
<template>  <div>    <MyCom v-if="isShow"/>    <hr>    <button @click="isShow = !isShow">销毁Life组件</button>  </div></template><script>import MyCom from './MyCom'export default {  data(){    return {      isShow: true    }  },  components: {    MyCom  }};</script>9
然后引入到App.vue上注册
// 1. 导入axiosimport axios from 'axios'// 2. 使用1axios({  method: '请求方式', // get post  url: '请求地址',  data: {    // 拼接到请求体的参数,  post请求的参数    xxx: xxx,  },  params: {  // 拼接到请求url的参数, get请求的参数    xxx: xxx   }}).then(res => {  console.log(res.data) // 后台返回的结果}).catch(err => {  console.log(err) // 后台报错返回})// 2. 使用2 async awaitfunction async fn(){  try {    const res = await axios.get('地址')  } catch (err) {    console.log('err', err)  }}0
总结: 明确目标, 拆分组件, 准备基础结构和样式
目标
头部的标题, 颜色, 背景色可以随便修改, props类型的校验
说明❓:Number, String, Boolean, Array, Function, Object (常用类型)
思路:
在MyHeader.vue中准备props里变量, 然后使用
在使用MyHeader.vue组件时, 传入相应的值 (color和backgroundColor)
MyHeader.vue
// 1. 导入axiosimport axios from 'axios'// 2. 使用1axios({  method: '请求方式', // get post  url: '请求地址',  data: {    // 拼接到请求体的参数,  post请求的参数    xxx: xxx,  },  params: {  // 拼接到请求url的参数, get请求的参数    xxx: xxx   }}).then(res => {  console.log(res.data) // 后台返回的结果}).catch(err => {  console.log(err) // 后台报错返回})// 2. 使用2 async awaitfunction async fn(){  try {    const res = await axios.get('地址')  } catch (err) {    console.log('err', err)  }}1
App.vue传入相应自定义的值
// 1. 导入axiosimport axios from 'axios'// 2. 使用1axios({  method: '请求方式', // get post  url: '请求地址',  data: {    // 拼接到请求体的参数,  post请求的参数    xxx: xxx,  },  params: {  // 拼接到请求url的参数, get请求的参数    xxx: xxx   }}).then(res => {  console.log(res.data) // 后台返回的结果}).catch(err => {  console.log(err) // 后台报错返回})// 2. 使用2 async awaitfunction async fn(){  try {    const res = await axios.get('地址')  } catch (err) {    console.log('err', err)  }}2
总结:
props: [] - 只能声明变量和接收, 不能类型校验
props: { } - 声明变量和校验类型规则 - 外部传入值不对则报错
-后台请求数据
目标: 使用axios把数据请求回来
接口地址:
get: https://www.escook.cn/api/cart
            下载axios npm i axios        
App.vue请求使用
// 1. 导入axiosimport axios from 'axios'// 2. 使用1axios({  method: '请求方式', // get post  url: '请求地址',  data: {    // 拼接到请求体的参数,  post请求的参数    xxx: xxx,  },  params: {  // 拼接到请求url的参数, get请求的参数    xxx: xxx   }}).then(res => {  console.log(res.data) // 后台返回的结果}).catch(err => {  console.log(err) // 后台报错返回})// 2. 使用2 async awaitfunction async fn(){  try {    const res = await axios.get('地址')  } catch (err) {    console.log('err', err)  }}3
小结
利用axios, 调用接口, 把数据请求回来
数据渲染
目标
把之前请求的数据渲染到页面上
App.vue
// 1. 导入axiosimport axios from 'axios'// 2. 使用1axios({  method: '请求方式', // get post  url: '请求地址',  data: {    // 拼接到请求体的参数,  post请求的参数    xxx: xxx,  },  params: {  // 拼接到请求url的参数, get请求的参数    xxx: xxx   }}).then(res => {  console.log(res.data) // 后台返回的结果}).catch(err => {  console.log(err) // 后台报错返回})// 2. 使用2 async awaitfunction async fn(){  try {    const res = await axios.get('地址')  } catch (err) {    console.log('err', err)  }}4
MyGoods.vue
// 1. 导入axiosimport axios from 'axios'// 2. 使用1axios({  method: '请求方式', // get post  url: '请求地址',  data: {    // 拼接到请求体的参数,  post请求的参数    xxx: xxx,  },  params: {  // 拼接到请求url的参数, get请求的参数    xxx: xxx   }}).then(res => {  console.log(res.data) // 后台返回的结果}).catch(err => {  console.log(err) // 后台报错返回})// 2. 使用2 async awaitfunction async fn(){  try {    const res = await axios.get('地址')  } catch (err) {    console.log('err', err)  }}5
小结
把各个组件关联起来, 把数据都铺设到页面
-数量加减
MyCount.vue
// 1. 导入axiosimport axios from 'axios'// 2. 使用1axios({  method: '请求方式', // get post  url: '请求地址',  data: {    // 拼接到请求体的参数,  post请求的参数    xxx: xxx,  },  params: {  // 拼接到请求url的参数, get请求的参数    xxx: xxx   }}).then(res => {  console.log(res.data) // 后台返回的结果}).catch(err => {  console.log(err) // 后台报错返回})// 2. 使用2 async awaitfunction async fn(){  try {    const res = await axios.get('地址')  } catch (err) {    console.log('err', err)  }}6
小结
子组件接受数据是引用类型,可以局部修改,而且互相影响
-选中功能
问题: 点击label发现总是第一个被选中
原来id和for都是"input"
但是id是唯一的啊, 所以用数据的id来作为标签的id, 分别独立, 为了兼容label点击图片也能选中的效果
// 1. 导入axiosimport axios from 'axios'// 2. 使用1axios({  method: '请求方式', // get post  url: '请求地址',  data: {    // 拼接到请求体的参数,  post请求的参数    xxx: xxx,  },  params: {  // 拼接到请求url的参数, get请求的参数    xxx: xxx   }}).then(res => {  console.log(res.data) // 后台返回的结果}).catch(err => {  console.log(err) // 后台报错返回})// 2. 使用2 async awaitfunction async fn(){  try {    const res = await axios.get('地址')  } catch (err) {    console.log('err', err)  }}7
总结: lable的for值对应input的id, 点击label就可以操作对应ID的元素
-全选功能
目标: 在底部组件上, 完成全选功能
思路:
点击获取全选框的选中状态
同步给上面每个小选框 - 而小选框的选中状态又在数组里
把数组传给MyFooter, 然后更新即可 - 因为对象都是引用关系的
MyFooter.vue
// 1. 导入axiosimport axios from 'axios'// 2. 使用1axios({  method: '请求方式', // get post  url: '请求地址',  data: {    // 拼接到请求体的参数,  post请求的参数    xxx: xxx,  },  params: {  // 拼接到请求url的参数, get请求的参数    xxx: xxx   }}).then(res => {  console.log(res.data) // 后台返回的结果}).catch(err => {  console.log(err) // 后台报错返回})// 2. 使用2 async awaitfunction async fn(){  try {    const res = await axios.get('地址')  } catch (err) {    console.log('err', err)  }}8
App.vue
把数据传递给子组件MyFooter
// 1. 导入axiosimport axios from 'axios'// 2. 使用1axios({  method: '请求方式', // get post  url: '请求地址',  data: {    // 拼接到请求体的参数,  post请求的参数    xxx: xxx,  },  params: {  // 拼接到请求url的参数, get请求的参数    xxx: xxx   }}).then(res => {  console.log(res.data) // 后台返回的结果}).catch(err => {  console.log(err) // 后台报错返回})// 2. 使用2 async awaitfunction async fn(){  try {    const res = await axios.get('地址')  } catch (err) {    console.log('err', err)  }}9
总结: 全选框v-model的值绑定计算属性, 需要使用计算属性完整写法
选中数量
目标: 完成底部组件, 显示选中的商品的总数量
思路:
计算父组件传递的商品列表数据=》渲染展示
MyFooter.vue
    <template>  <div>    <div>      <h4>1. 查询所有图书信息</h4>      <button @click="allFn">查询-看控制台</button>    </div>    <div>      <h4>2. 查询某本图书信息</h4>      <input type="text" placeholder="输入要查询的书名" v-model="bookName" />      <button @click="selFn">查询-看控制台</button>    </div>    <div>      <h4>3. 新增图书信息</h4>      <input type="text" placeholder="书名" v-model="obj.bookname" />      <input type="text" placeholder="作者" v-model="obj.author" />      <input type="text" placeholder="出版社" v-model="obj.publisher" />    </div>    <button @click="addFn">点击新增图书</button>  </div></template><script>export default {  data () {    return {      bookName: "",      obj: {        bookname: "",        author: "",        publisher: ""      }    }  },  methods: {    // 获取所有图书    allFn () {    },    // 查询某个图书信息    selFn () {    },    // 新增图书    addFn () {    }  },}</script>0
总结: 计算属性依赖的数据也可以是父组件传递的数据
案例-购物车-计算总价(作业)
目标: 完成选中商品总价计算
MyFooter.vue
    <template>  <div>    <div>      <h4>1. 查询所有图书信息</h4>      <button @click="allFn">查询-看控制台</button>    </div>    <div>      <h4>2. 查询某本图书信息</h4>      <input type="text" placeholder="输入要查询的书名" v-model="bookName" />      <button @click="selFn">查询-看控制台</button>    </div>    <div>      <h4>3. 新增图书信息</h4>      <input type="text" placeholder="书名" v-model="obj.bookname" />      <input type="text" placeholder="作者" v-model="obj.author" />      <input type="text" placeholder="出版社" v-model="obj.publisher" />    </div>    <button @click="addFn">点击新增图书</button>  </div></template><script>export default {  data () {    return {      bookName: "",      obj: {        bookname: "",        author: "",        publisher: ""      }    }  },  methods: {    // 获取所有图书    allFn () {    },    // 查询某个图书信息    selFn () {    },    // 新增图书    addFn () {    }  },}</script>1
总结: 根据选中的商品和数量, 统计总价
总结
vue的生命周期哪4个阶段, 哪8个自执行回调函数(钩子函数)
axios是什么, 底层是什么, 具体如何使用=》发送ajax请求获取后台数据
axios返回的是什么, 如何接收结果=》返回Promise=>通过then回调或者async/await形式获取response结果
知道$refs使用和作用 =》可以获取DOM元素或组件实例
知道$nextTick的作用=》因为数据驱动视图=》数据的修改(同步),但是DOM更新是异步的
附件:接口文档
axios请求接口使用
根域名: http://123.57.109.30:3006
- 图书相关
获取
请求方式: GET
请求地址: 根域名/api/getbooks
请求参数:
不传参获取所有默认书籍
也可以选择传递下面任意1-多个参数, 获取指定的相关书籍信息
返回示例
    <template>  <div>    <div>      <h4>1. 查询所有图书信息</h4>      <button @click="allFn">查询-看控制台</button>    </div>    <div>      <h4>2. 查询某本图书信息</h4>      <input type="text" placeholder="输入要查询的书名" v-model="bookName" />      <button @click="selFn">查询-看控制台</button>    </div>    <div>      <h4>3. 新增图书信息</h4>      <input type="text" placeholder="书名" v-model="obj.bookname" />      <input type="text" placeholder="作者" v-model="obj.author" />      <input type="text" placeholder="出版社" v-model="obj.publisher" />    </div>    <button @click="addFn">点击新增图书</button>  </div></template><script>export default {  data () {    return {      bookName: "",      obj: {        bookname: "",        author: "",        publisher: ""      }    }  },  methods: {    // 获取所有图书    allFn () {    },    // 查询某个图书信息    selFn () {    },    // 新增图书    addFn () {    }  },}</script>2
添加
请求方式: POST
请求地址: 根域名/api/addbook
请求参数:
返回示例:
    <template>  <div>    <div>      <h4>1. 查询所有图书信息</h4>      <button @click="allFn">查询-看控制台</button>    </div>    <div>      <h4>2. 查询某本图书信息</h4>      <input type="text" placeholder="输入要查询的书名" v-model="bookName" />      <button @click="selFn">查询-看控制台</button>    </div>    <div>      <h4>3. 新增图书信息</h4>      <input type="text" placeholder="书名" v-model="obj.bookname" />      <input type="text" placeholder="作者" v-model="obj.author" />      <input type="text" placeholder="出版社" v-model="obj.publisher" />    </div>    <button @click="addFn">点击新增图书</button>  </div></template><script>export default {  data () {    return {      bookName: "",      obj: {        bookname: "",        author: "",        publisher: ""      }    }  },  methods: {    // 获取所有图书    allFn () {    },    // 查询某个图书信息    selFn () {    },    // 新增图书    addFn () {    }  },}</script>3原文:https://juejin.cn/post/7098254677824765965