118、ref引用 【1】JQuery里面的$("#app")。vue mv vm 【2】在vue中,程序员不需要操作dom。程序员只需要维护好数据即可(数据驱动视图)。所以在vue下,强烈不建议使用jquery!!! 【3】假设在vue项目中需要操作dom,怎么办呢,vue提供了ref,在不依赖jquery的情况下获取dom。 <!--第1部分--> <template> <div> <h1 ref="ref_h1">App.vue父组件---{{countFromSon}}</h1> <!--!!!!!!!!!!!!!!!!!!!!!!--> <button @click="showThis">打印this</button> </div> </template> <!--第2部分--> <script> // 导入需要使用的组件 /* import Left from "@/components/Left"; import Right from "@/components/Right";*/ export default { /* components: { Left, Right },*/ data() { return { // 定义从子组件传值的属性 countFromSon: 0 } }, methods: { showThis() { // console.log(this) this.$refs.ref_h1.style.color = "black" // !!!!!!!!!!!!!!!!!!!!!!!!!! } } } </script> <!--第3部分--> <style> .box { background-color: pink; border: 3px solid black; h1 { color: red; } } </style>
****************************************************************************************************************************************************************************
119、使用ref引用组件实例 【1】感觉这种方式也可以用于父子兄弟传值呀.... 卧槽!!!!!!!!这个方案最简单的呀,from老师的透露!!!!!!!!! 【2】Left.vue <template> <div> 我是Left.vue子组件---{{count}}<br/> <button @click="count=count+1">点击+1</button> <button @click="count=0">重置</button> </div> </template> <script> import eventBus from "@/components/eventBus"; export default { name: "Left", data() { return { count: 0 } }, methods: { sendMsg() { // 通过eventBus发送数据 eventBus.$emit("sendMsgToBrotherRight", this.msg) } } } </script> <style scoped> .box { background-color: yellow; } </style> 【3】App.vue <!--第1部分--> <template> <div> <h1>App.vue父组件</h1> <button @click="showThis">打印this</button> <button @click="reset">重置Left组件的count</button> <Left ref="refLeft"></Left> </div> </template> <!--第2部分--> <script> // 导入需要使用的组件 import Left from "@/components/Left"; import Right from "@/components/Right"; export default { components: { Left, Right }, data() { return { name: '陈翔' } }, methods: { showThis() { console.log(this) }, reset() { this.$refs.refLeft.count = 0 } } } </script> <!--第3部分--> <style> .box { background-color: pink; border: 3px solid black; h1 { color: red; } } </style>
****************************************************************************************************************************************************************************
120、初步实现按钮与文本框依次展示 【1】实现切换 <!--第1部分--> <template> <div> <h1>App.vue父组件</h1> <hr/> <input type="text" v-if="visFlag" @blur="showButton"/> <button @click="showInput" v-else>展示输入框</button> </div> </template> <!--第2部分--> <script> // 导入需要使用的组件 import Left from "@/components/Left"; import Right from "@/components/Right"; export default { components: { Left, Right }, data() { return { // 控制输入框与按钮展示切换 visFlag: false } }, methods: { showThis() { console.log(this) }, reset() { this.$refs.refLeft.count = 0 }, showInput() { this.visFlag = true }, showButton() { this.visFlag = false } } } </script> <!--第3部分--> <style> .box { background-color: pink; border: 3px solid black; h1 { color: red; } } </style> 【2】解决input不自动获取焦点问题。updated不行因为当隐藏input的时候,refInput还是未定义,会报错。感觉也可以使用v-show,我真是个天才呀。 <!--第1部分--> <template> <div> <h1>App.vue父组件</h1> <hr/> <input type="text" v-if="visFlag" @blur="showButton" ref="refInput"/> <button @click="showInput" v-else>展示输入框</button> </div> </template> <!--第2部分--> <script> // 导入需要使用的组件 import Left from "@/components/Left"; import Right from "@/components/Right"; export default { components: { Left, Right }, data() { return { // 控制输入框与按钮展示切换 visFlag: false } }, methods: { showThis() { console.log(this) }, reset() { this.$refs.refLeft.count = 0 }, showInput() { this.visFlag = true // Cannot read properties of undefined (reading 'focus')" 报错是因为还没有updated完毕 /* console.log(this.$refs.refInput) this.$refs.refInput.focus()*/ // 用到了延迟cb调用 this.$nextTick(() => { // 需要dom渲染完毕后 执行 就是延迟执行。 this.$refs.refInput.focus() // !!!!!!!!!!!!!!!!!! }) }, showButton() { this.visFlag = false } } } </script> <!--第3部分--> <style> .box { background-color: pink; border: 3px solid black; h1 { color: red; } } </style>
****************************************************************************************************************************************************************************
123、购物车案例-some循环 【1】主要是解决forEach的全部循环问题 <!--第1部分--> <template> <div> <h1>App.vue父组件</h1> </div> </template> <!--第2部分--> <script> // 导入需要使用的组件 export default { name: "App", data() { return { // 控制输入框与按钮展示切换 myArray: ['小红', '倪大红', '苏大强', '宝贝'] } }, created() { /* this.myArray.forEach((item, index) => { console.log('OK') // 打印4次,说明性能浪费,一直执行,return没用 。一旦开始 无法在中间停止 if (item == '小红') { console.log("找到的索引位置为:" + index) return } })*/ this.myArray.some((item, index) => { console.log('OK') if (item == '小红') { console.log("找到的索引位置为:" + index) return true // 通过return true 可以终止循环 固定写法 } }) }, methods: {} } </script> <!--第3部分--> <style> .box { background-color: pink; border: 3px solid black; h1 { color: red; } } </style>
****************************************************************************************************************************************************************************
124、every循环 【1】都是true就返回true,有一项是false则返回false <!--第1部分--> <template> <div> <h1>App.vue父组件</h1> </div> </template> <!--第2部分--> <script> // 导入需要使用的组件 export default { name: "App", data() { return { // 控制输入框与按钮展示切换 myArray: [ {id: 1, name: '西瓜', state: true}, {id: 2, name: '橙子', state: true}, {id: 3, name: '操作', state: false} ] } }, created() { const res = this.myArray.every(item => item.state) console.log(res) }, methods: {} } </script> <!--第3部分--> <style> .box { background-color: pink; border: 3px solid black; h1 { color: red; } } </style>
****************************************************************************************************************************************************************************
125、reduce方法的使用 【1】普通用法 <!--第1部分--> <template> <div> <h1>App.vue父组件</h1> </div> </template> <!--第2部分--> <script> // 导入需要使用的组件 export default { name: "App", data() { return { // 控制输入框与按钮展示切换 myArray: [ {id: 1, name: '西瓜', state: true, price: 10, count: 1}, {id: 2, name: '橙子', state: true, price: 20, count: 2}, {id: 3, name: '操作', state: false, price: 30, count: 3} ] } }, created() { let totalPrice = 0; // 总价 this.myArray.filter(item => item.state).forEach(item => { totalPrice = totalPrice + item.price * item.count }) console.log(totalPrice) }, methods: {} } </script> <!--第3部分--> <style> .box { background-color: pink; border: 3px solid black; h1 { color: red; } } </style> 【2】高级用法 created() { // let totalPrice = 0; // 总价 const res = this.myArray.filter(item => item.state).reduce((totalPrice, item) => { return totalPrice = totalPrice + item.price * item.count }, 0) console.log(res) }
****************************************************************************************************************************************************************************
126、reduce的简化写法 【1】缩写版 const res = this.myArray.filter(item => item.state).reduce((totalPrice, item) => totalPrice = totalPrice + item.price * item.count, 0) console.log(res)
****************************************************************************************************************************************************************************
127、购入车案例 【1】git ee网址可以下载优秀的项目,天下武功唯快不破。想让自己厉害,现在厉害的项目做起。
****************************************************************************************************************************************************************************
128、发起axios请求 【1】async function(){...await} 【2】在created调用initCartList方法 created() { this.initCartList(); console.log('========') }
****************************************************************************************************************************************************************************
129、请求回来的数据存到data里面去 【1】请求数据在页面要用的,必须转存到data中。 【2】此list数据就是转存到data里定义的list数据。
****************************************************************************************************************************************************************************
130、渲染Goods列表 【1】在App里面引用Goods,然后使用v-for="item in list"循环展示
****************************************************************************************************************************************************************************
131、为Goods组件封装属性 【1】:goodsName="item.goods_name",把App拿到的item属性,传递给Goods组件使用。 【2】思路:父组件向子组件传值。包括名称、图片、价格等属性
****************************************************************************************************************************************************************************
132、Goods组件封装 【1】状态的封装
****************************************************************************************************************************************************************************
133、分析封装props 【1】:goods="item" ====> 这是面向对象的思想,厉害 厉害 厉害 goods: { type: Object, default: {} } 【2】假如有个商品列表页面{id,title,price,image}、促销商品列表页面{id,title,x_price,x_image}。如果用面向对象的思想,在Goods.vue就不能做兼容处理了 【3】所以,还是需要分开接受属性,我的哥哥呀。还得是老师分开的对。解决了是用goods.price还是goods.x_price呢,所以还是得分开写。提升了复用性。
****************************************************************************************************************************************************************************
134、如何修改商品 【1】@change 只要复选框勾选状态发生变化,会自动触发change函数 @change="sonStateChange" /*勾选框状态改变触发的函数 * 注意e 就是子组件通过$emit传递到父组件中的数据,格式为{id,value}*/ sonStateChange(e) { const newState = e.target.checked; // 拿到最新状态 this.$emit("son-fatherStateChange", { // 以此名称为准 id: this.id, value: newState, }); }, 【2】<Goods @son-fatherStateChange="fatherStateChange"></Goods> fatherStateChange(e) { this.list.some((item) => { if (item.id === e.id) { item.goods_state = e.value; return true; } }); },
****************************************************************************************************************************************************************************
136、修改对应商品的状态 【1】使用some即可 fatherStateChange(e) { /*根据子组件传的id value修改list里面的数据*/ this.list.some((item) => { if (item.id === e.id) { item.goods_state = e.value; return true; } }); },
****************************************************************************************************************************************************************************
137、定义fullState 【1】使用every即可 // 动态计算全选状态 fullState() { return this.list.every((item) => item.goods_state); }, 【2】自学能力、调试BUG能力、学习分析解决问题的能力。 【3】state是true还是false就决定是否全选了。 <!--底部组件 state就是全选的标记--> <Footer :state="fullState" @isFull="getState" :amount="totalAmount" :totalPrice="totalPrice"> </Footer> 【4】又是一轮子传父 Footer.vue---App.vue <input type="checkbox" id="allCheck" :checked="state" @change="getState"/> ******************************************************************************************** getState(e) { this.$emit('sonFatherIsFull', e.target.checked) } ******************************************************************************************** /*全选按钮的切换*/ getState(val) { this.list.forEach((item) => { item.goods_state = val; }); }, 【5】父传子---子传父
****************************************************************************************************************************************************************************
140、总数量、价格的计算 【1】App的computed计算属性里面 /*已勾选商品的数量*/ totalAmount() { return this.list .filter((item) => item.goods_state) .reduce((amount, item) => (amount += item.goods_count), 0); }, ***************************************************************先指令后函数绑定 <Footer :state="fullState" :amount="totalAmount" :totalPrice="totalPrice" @sonFatherIsFull="getState"> </Footer> 【2】也是App的computed计算属性里面 【3】父传子---子再传子...
****************************************************************************************************************************************************************************
142、发送给App.vue 【1】发现一个问题Counter---Goods---App传两次太麻烦了...不如都在App.vue中引用,这样最多就是父子关系对吧。App.vue是所有的父,其他的都是子。 <Goods v-for="item in list" :key="item.id" :goodsName="item.goods_name" :goodsPrice="item.goods_price" :imgUrl="item.goods_img" :goodsState="item.goods_state" :id="item.id" @son-fatherStateChange="fatherStateChange"> <!--商品的数量--> <Counter :counter_goodsCount="item.goods_count" @amount-change="getNewAmount(item, $event)"> </Counter> </Goods> ********************************************************************************* /*最新商品数量*/ getNewAmount(item, e) { console.log(e) item.goods_count = e; }, 【2】学好Vue先给用组件名_变量名的形式规范定义变量。难度将会大大降低!!!!!!!!!!!!!!!!!!改完后看着舒服多了。 <template> <div id="app"> <Header></Header> <!--循环渲染Goods--> <Goods v-for="item in list" :key="item.id" :Goods_goodsName="item.goods_name" :Goods_goodsPrice="item.goods_price" :Goods_imgUrl="item.goods_img" :Goods_goodsState="item.goods_state" :Goods_id="item.id" @Goods_sonFatherStateChange="Goods_sonFatherStateChange"> <!--商品的数量--> <Counter :Counter_goodsCount="item.goods_count" @amount-change="getNewAmount(item, $event)"> </Counter> </Goods> <!--底部组件 state就是全选的标记--> <Footer :Footer_state="fullState" :Footer_amount="totalAmount" :Footer_totalPrice="totalPrice" @Footer_sonFatherIsFull="Footer_sonFatherIsFull"> </Footer> </div> </template> <script> import Header from "@/components/Header"; import Goods from "@/components/Goods"; import Footer from "@/components/Footer"; import axios from "axios"; import Counter from "@/components/Counter"; export default { name: "App", data() { return { list: [], }; }, components: { Header, Goods, Counter, Footer, }, methods: { // 封装请求列表数据方法 async initCartList() { // 结构data后重命名为res const {data: res} = await axios.get("https://www.escook.cn/api/cart"); if (res.status === 200) { this.list = res.list; console.log(res.list); } }, Goods_sonFatherStateChange(e) { /*根据子组件传的id value修改list里面的数据*/ this.list.some((item) => { if (item.id === e.id) { item.goods_state = e.value; return true; } }); }, /*全选按钮的切换*/ Footer_sonFatherIsFull(val) { this.list.forEach((item) => { item.goods_state = val; }); }, /*最新商品数量*/ getNewAmount(item, e) { console.log(JSON.stringify(item) + "============" + e) item.goods_count = e; }, }, created() { this.initCartList(); console.log('========') }, computed: { // 动态计算全选状态 fullState() { return this.list.every((item) => item.goods_state); }, /*已勾选商品的价格*/ totalAmount() { return this.list .filter((item) => item.goods_state) .reduce((amount, item) => (amount += item.goods_count), 0); }, totalPrice() { return this.list .filter((item) => item.goods_state) .reduce( (totalPrice, item) => (totalPrice += item.goods_count * item.goods_price), 0 ); }, }, }; </script> <style lang="less"> </style>
****************************************************************************************************************************************************************************
144、动态计算已勾选的商品 【1】越看越知道规范命名的重要性,一是规范命名,二是命名能起到区分的作用才最重要的。 【2】南朝四百八十寺,多少楼台烟雨中?
****************************************************************************************************************************************************************************
145、课程概述 【1】动态组件、插槽、自定义指令(没必要吧)
****************************************************************************************************************************************************************************
146、动态组件 【1】控制多个组件的隐藏与展示 【2】<component>组件,专门实现动态组件的渲染 【3】is属性的值就是渲染组件的名字。:is='comName' 动态绑定属性的值。 <template> <div> <h1>App根组件</h1> <!--动态组件渲染--> <component :is="comName"></component> </div> </template> <script> import Left from "@/components/Left"; import Right from "@/components/Right"; export default { name: "App", data() { return { comName: 'Left' }; }, components: { Left, Right }, methods: {} }; </script> <style> </style>
****************************************************************************************************************************************************************************
147、动态切换组件的展示 【1】动态修改属性的值而已 <template> <div> <h1>App根组件</h1> <button @click="comName='Left'">展示 Left</button> <button @click="comName='Right'">展示 Right</button> <!--动态组件渲染--> <component :is="comName"></component> </div> </template> <script> import Left from "@/components/Left"; import Right from "@/components/Right"; export default { name: "App", data() { return { comName: 'Left' }; }, components: { Left, Right }, methods: {} }; </script> <style> </style>
****************************************************************************************************************************************************************************
148、keep-alive的使用 【1】如果Left组件有个自增函数,但当切换Right后再切回Left会发现,left组件重新初始化了,值也变为了0。 <h3>Left组件---{{count}}</h3> <button @click="count=count+1">点击+1</button> ********************************************************************* <template> <div> <h3>Left组件---{{count}}</h3> <button @click="count=count+1">点击+1</button> </div> </template> <script> export default { name: "Left", data() { return { count: 0 } }, created() { console.log("Left创建") }, destroyed() { console.log("Left销毁") } } </script> <style scoped> .box { background-color: yellow; } </style> 【2】让离开时不被销毁,包一层<keep-alive>即可。可以把内部的组件进行缓存,而不是销毁。 <!--动态组件渲染--> <keep-alive> <component :is="comName"></component> </keep-alive>
****************************************************************************************************************************************************************************
149、keep-alive组件指定 【1】include属性,通过include="Left,Another"只会缓存Left与Another,多个用逗号间隔。 <!--动态组件渲染--> <keep-alive include="Left,Another"> <component :is="comName"></component> </keep-alive> 【2】exclude代表排除组件。但是不能与include同时使用。
****************************************************************************************************************************************************************************
150、组件注册名称 【1】如果在声明组件时,没有为组件指定name名称,则组件名称默认就是注册时名称。 【2】默认的都是使用默认与组件名一样的名称。
****************************************************************************************************************************************************************************
151、插槽 【1】是vue为组件封装者提供的能力,一般开发用不到。 【2】<slot></slot>占位标签。小霸王游戏机的插槽来插卡,插什么样的卡,就是什么样的游戏 <template> <div> <h3>Left组件---{{count}}</h3> <!--声明一个插槽--> <slot></slot> </div> </template> <script> export default { name: "Left", data() { return { count: 0 } }, created() { console.log("Left创建") }, destroyed() { console.log("Left销毁") } } </script> <style scoped> .box { background-color: yellow; } </style> *********************************************************** <template> <div> <h1>App根组件</h1> <Left>这是用来填充Left插槽的...</Left> </div> </template> <script> import Left from "@/components/Left"; import Right from "@/components/Right"; export default { name: "App", data() { return { comName: 'Left' }; }, components: { Left, Right }, methods: {} }; </script> <style> </style>
****************************************************************************************************************************************************************************
152、v-slot指令 【1】<slot></slot>默认的name="default" <slot name="default"></slot> 【2】指定插槽的名称,放置内容。template只起到包裹作用,不是真正的标签。 <Left> <template v-slot:default> // !!!!!!!!!!!!!! <span>这是用来填充Left插槽的...</span> </template> </Left> 【3】 v-slot:default必须用在template标签上,不能用在普通元素标签上。
****************************************************************************************************************************************************************************
153、v-slot的简写形式 【1】v-slot:简写是# 【2】插槽的默认内容指定 <template> <div class="box"> <h3>Left组件---{{count}}</h3> <!--声明一个插槽--> <slot name="default">这是名字为default的slot ...默认的</slot> </div> </template>
****************************************************************************************************************************************************************************
154、具名插槽的定义与使用 【1】Vant组件库。插槽的一一定义,与对应的一一使用。 <template> <div> <!--标题--> <div> <slot name="title"></slot> </div> <!--内容--> <div> <slot name="content"></slot> </div> <!--作者--> <div> <slot name="author"></slot> </div> </div> </template> <script> export default { name: "Article" } </script> <style scoped> .article-container { > div { min-height: 150px; } .header-box { background-color: pink; } .content-box { background-color: lightblue; } .footer-box { background-color: lightcoral; } } </style> 【2】使用时一一对应 <template> <div> <h1>App根组件</h1> <Article> <template #title> <span>一首诗</span> </template> <template #content> <span>琴瑟无端五十铉,一年一柱思华年</span> </template> <template #author> <span>李商隐</span> </template> </Article> </div> </template> <script> import Left from "@/components/Left"; import Right from "@/components/Right"; import Article from "@/components/Article"; export default { name: "App", data() { return { comName: 'Left' }; }, components: { Left, Right, Article }, methods: {} }; </script> <style> </style>
****************************************************************************************************************************************************************************
155、作用域插槽的基本用法 【1】插槽里定义数据。加了属性的插槽就变成了作用域插槽。 <!--内容--> <div class="content-box"> <slot name="content" msg="Hello Vue"></slot> </div> 【2】父组件接收使用的是{}对象,.属性就可以使用了。 <template #content="object"> <span>琴瑟无端五十铉,一年一柱思华年</span> {{object.msg}} </template>
****************************************************************************************************************************************************************************
156、作用域插槽解构赋值 【1】作用域插槽 <template> <div> <!--标题--> <div> <slot name="title"></slot> </div> <!--内容--> <div> <slot name="content" msg="Hello Vue" :user="user"></slot> </div> <!--作者--> <div> <slot name="author"></slot> </div> </div> </template> <script> export default { name: "Article", data() { return { user: { name: "陈翔", age: 20 } }; }, } </script> <style scoped> .article-container { > div { min-height: 150px; } .header-box { background-color: pink; } .content-box { background-color: lightblue; } .footer-box { background-color: lightcoral; } } </style> 【2】解构msg <template #content="{msg}"> <span>琴瑟无端五十铉,一年一柱思华年</span><br/> {{msg}} </template> 【3】解构user <template #content="{user}"> <span>琴瑟无端五十铉,一年一柱思华年</span><br/> {{user}} </template> 【4】同时结构 <template #content="{msg,user}"> <span>琴瑟无端五十铉,一年一柱思华年</span><br/> {{msg}} {{user}} </template>
****************************************************************************************************************************************************************************
157、基于slot插槽购物车重构 【1】功能一定好好测试,确实容易漏问题,人非圣贤,孰能无过。定义插槽 <div class="goods_pa"> <div class="price">¥{{ Goods_goodsPrice }}</div> <slot></slot> </div> 【2】使用插槽。 <Counter :Counter_goodsCount="item.goods_count" @amount-change="getNewAmount(item, $event)"> </Counter>
****************************************************************************************************************************************************************************
160、私有自定义指令 【1】定义名字为color的指令,指向一个配置对象 <h1 v-color>App根组件</h1> ************************************* directives: { color: {} }, *******************************************<h1>哪个元素绑定,哪个元素变红!!! <template> <div> <h1 v-color>App根组件</h1> <Article> <template #title> <span>一首诗</span> </template> <template #content="{msg,user}"> <span>琴瑟无端五十铉,一年一柱思华年</span><br/> {{msg}} {{user}} </template> <template #author> <span>李商隐</span> </template> </Article> </div> </template> <script> import Left from "@/components/Left"; import Right from "@/components/Right"; import Article from "@/components/Article"; export default { name: "App", components: { Left, Right, Article }, directives: { color: { /*当指令第一次被绑定到元素上的时候,会立即触发bind函数*/ bind(el) { console.log("触发v-color的bind函数...") el.style.color = "red" } } }, methods: {} }; </script> <style> </style>
****************************************************************************************************************************************************************************
161、使用binding.value的值 【1】动态绑定data属性值。 <h1 v-color="color">App根组件</h1> ************************************************** data() { return { color: 'green' } }, directives: { color: { /*当指令第一次被绑定到元素上的时候,会立即触发bind函数*/ bind(el, binding) { console.log("触发v-color的bind函数..." + JSON.stringify(binding)) el.style.color = binding.value } } }, ***********************************************************这种传字符串的用法也可以 <h2 v-color="'black'">App根组件</h2>
****************************************************************************************************************************************************************************
162、自定义指令-update函数 【1】bind函数只会调用一次。更新则不会被执行 【2】 <button @click="color='red'">改变 color 的颜色值</button> data() { return { color: 'green' } }, directives: { color: { /*当指令第一次被绑定到元素上的时候,会立即触发bind函数*/ bind(el, binding) { console.log("触发v-color的bind函数..." + JSON.stringify(binding)) el.style.color = binding.value }, /*定义update!!!!!!!!!!!!!!*/ update(el, binding) { console.log("触发v-color的bind函数..." + JSON.stringify(binding)) el.style.color = binding.value } } },
****************************************************************************************************************************************************************************
163、自定义指令的简写 【1】原来的 VS 简写 color: { /!*当指令第一次被绑定到元素上的时候,会立即触发bind函数*!/ bind(el, binding) { console.log("触发v-color的bind函数..." + JSON.stringify(binding)) el.style.color = binding.value }, /!*定义update*!/ update(el, binding) { console.log("触发v-color的bind函数..." + JSON.stringify(binding)) el.style.color = binding.value } } **************************************************************************** color(el, binding) { el.style.color = binding.value }
****************************************************************************************************************************************************************************
164、全局自定义指令 【1】像定义全局过滤器一样。这样不管是App.vue还是Article.vue都能用了。 /*main.js*/ import Vue from 'vue' import App from './App.vue' import axios from 'axios' Vue.config.productionTip = false // 全局自定义指令 Vue.directive('color', function (el, binding) { el.style.color = binding.value }) new Vue({ render: h => h(App), }).$mount('#app')
****************************************************************************************************************************************************************************
165、main.js的小提示 【1】Vue.config.productionTip = false /*控制台console能不能看到提示,没有什么实际意义*/
****************************************************************************************************************************************************************************
166、eslint概述与作用 【1】约定代码风格。结尾要不要分号、缩进几个空格,保证写出代码的风格几乎是完全一样的。一开始用会比较难。 【2】WebStorm设置File---Settings---Editor-code style --- javascript 【3】no-console... no-debugger 【4】常用的eslint的语法,名字后面少一个空格。/**/前后强制使用一样的间距。 【5】vscode配置eslint
****************************************************************************************************************************************************************************
172、axios 的基本用法 【1】npm i axios -S <template> <div> <h3>Left组件---</h3> <button @click="getInfo">发起GET请求</button> </div> </template> <script> import axios from 'axios' export default { name: "Left", data() { return { count: 0 } }, methods: { async getInfo() { const {data} = await axios.get(`http://localhost:8002/login`); console.log(data) } } } </script> <style scoped> .left-container { background-color: orange; min-height: 200px; flex: 1; } </style> 【2】发起post请求 <template> <div> <h3>Right组件</h3> <button @click="postInfo">发起POST请求</button> </div> </template> <script> import axios from 'axios' export default { name: "Right", methods: { async postInfo() { const {data} = await axios.post(`http://localhost:8002/login`) console.log(data) } } } </script> <style scoped> .right-container { background-color: lightblue; min-height: 200px; flex: 1; } </style>
****************************************************************************************************************************************************************************
173、把axios挂载到vue 【1】挂载到Vue原型属性 /*main.js*/ import Vue from 'vue' import App from './App.vue' import axios from 'axios' Vue.config.productionTip = false /*控制台console能不能看到提示,没有什么实际意义*/ // 全局自定义指令 Vue.directive('color', function (el, binding) { el.style.color = binding.value }) // 挂载axios Vue.prototype.axios = axios new Vue({ render: h => h(App), }).$mount('#app') 【2】const {data} = await this.axios.get(`http://localhost:8002/login`);使用不需要import操作了。 【3】后端路径优化操作!!!!!!!!!!!!!!!还是挂原型。这个比青戈讲的透彻呀。 // 挂载axios axios.defaults.baseURL = `http://localhost:8002` // !!!!!!!!!!!!! Vue.prototype.axios = axios
****************************************************************************************************************************************************************************
174、axios不利于API接口的复用。 【1】它无法实现API接口的复用。每次调用都需要重新写请求方法与请求参数。 【2】所以还有优化的空间。
****************************************************************************************************************************************************************************
175、路由的概念 【1】英文router,就是对应关系。SPA单页面应用程序。 【2】前端里,路由就是Hash地址,就是页面与地址的对应关系。
****************************************************************************************************************************************************************************
176、路由的工作方式 【1】点击路由链接---导致URL地址栏中Hash值发生变化---前端路由监听变化---Hash地址对应组件渲染到浏览器中。
****************************************************************************************************************************************************************************
178、路由的基本用法 【1】vue-router路由包,只能在vue项目使用。注意看官方文档。 【2】使用 *********************************************安装vue-router包 npm i vue-router@3.5.2 -S *********************************************创建路由模块.js文件 src/router/index.js *********************************************导入挂载 /*index.js路由模块*/ // 导入 import Vue from "vue"; import VueRouter from "vue-router"; // 调用 Vue.use(VueRouter) // 创建路由实例对象 const router = new VueRouter() // 向外共享路由实例对象 export default router *********************************************声明路由和占位符 /*main.js*/ import Vue from 'vue' import App from './App.vue' import axios from 'axios' import myRouter from './router/index.js' // 导入路由模块,拿到路由的实例对象 Vue.config.productionTip = false /*控制台console能不能看到提示,没有什么实际意义*/ // 全局自定义指令 Vue.directive('color', function (el, binding) { el.style.color = binding.value }) // 挂载axios axios.defaults.baseURL = `http://localhost:8002` Vue.prototype.axios = axios new Vue({ render: h => h(App), router: myRouter }).$mount('#app')
****************************************************************************************************************************************************************************
179、基本用法-注意点 【1】import myRouter from './router/index.js' 可以写成import myRouter from './router' 【2】说明,如果给定的是文件夹,则默认是文件夹下面的index.js文件
****************************************************************************************************************************************************************************
180、路由模块基本用法、 【1】App中使用 <template> <div> <h1>App组件</h1> <hr/> <a href="#/home">首页</a> <a href="#/movie">电影</a> <a href="#/about">关于</a> <hr/> <!--只要在main.js配置了vue-router,则·可以使用router-view--> <router-view></router-view> </div> </template> <script> export default { name: "App", }; </script> <style scoped> .box { display: flex; } </style> 【2】index.js定义Hash与页面的对应关系 /*路由模块*/ // 导入 import Vue from "vue"; import VueRouter from "vue-router"; import Home from "@/components/Home"; import Move from "@/components/Movie"; import About from "@/components/About"; // 调用 Vue.use(VueRouter) // 创建路由实例对象 const router = new VueRouter({ // routers是一个数组,是来定义hash地址与组件间的对应关系的。 routes: [ // !!!!!!!!!!!!!注意不是routers {path: '/home', component: Home}, {path: '/movie', component: Move}, {path: '/about', component: About}, ] }) // 向外共享路由实例对象 export default router
****************************************************************************************************************************************************************************
181、路由基本用法 【1】下面的就是叫做“路由规则” routes: [ {path: '/home', component: Home}, {path: '/movie', component: Move}, {path: '/about', component: About}, ] 【2】router-link就是用来替换a标签的 <!-- <a href="#/home">首页</a> <a href="#/movie">电影</a> <a href="#/about">关于</a>--> <!--安装与配置了vue-router后可以用router-link来替换普通的链接了--> <router-link to="/home">首页</router-link> <router-link to="/movie">电影</router-link> <router-link to="/about">关于</router-link> 【3】路由的重定向。解决访问localhost:8080能直接访问home页面,而不是空页面 routes: [ {path: '/', redirect: '/home'},/*重定向到home*/ {path: '/home', component: Home}, {path: '/movie', component: Move}, {path: '/about', component: About}, ]
****************************************************************************************************************************************************************************
183、嵌套路由 【1】套娃 { path: '/about', component: About, children: [ {path: '/left', component: Left}, {path: '/right', component: Right}, ] }, ********************************************************** <template> <div class="about-container"> <h3>About组件</h3> <hr/> <router-link to="/left">Left组件</router-link> <router-link to="/right">Right组件</router-link> <hr/> <router-view></router-view> </div> </template>
****************************************************************************************************************************************************************************
185、嵌套路由-默认子路由 【1】子路由一般不写/ <router-link to="/about/left">Left组件</router-link> <router-link to="/about/right">Right组件</router-link> 【2】子路由一般不写/ { path: '/about', component: About, redirect: '/about/left', // 默认子路由页面 children: [ //需要注意的是:子路由规则不要以/开头 {path: 'left', component: Left}, {path: 'right', component: Right} ] } 【3】完整路由 /*路由模块*/ // 导入 import Vue from "vue"; import VueRouter from "vue-router"; import Home from "@/components/Home"; import Move from "@/components/Movie"; import About from "@/components/About"; import Left from "@/components/Left"; import Right from "@/components/Right"; // 调用 Vue.use(VueRouter) // 创建路由实例对象 const router = new VueRouter({ // routers是一个数组,是来定义hash地址与组件间的对应关系的。 routes: [ {path: '/', redirect: '/home'},/*重定向到home*/ {path: '/home', component: Home}, {path: '/movie', component: Move}, { path: '/about', component: About, redirect: '/about/left', children: [ //需要注意的是:子路由规则不要以/开头 {path: 'left', component: Left}, {path: 'right', component: Right} ] }, ] }) // 向外共享路由实例对象 export default router
****************************************************************************************************************************************************************************
186、动态路由匹配 【1】/mv/1 /mv/2 /mv/3不同的短片mv,怎么定向? 【2】使用英文的冒号: 可以指定动态路由项目。 {path: '/movie/:id', component: Move}, 【3】这样电影1-5都能匹配到Move组件。 <router-link to="/home">首页</router-link> <router-link to="/movie/1">电影1</router-link> <router-link to="/movie/2">电影2</router-link> <router-link to="/movie/3">电影3</router-link> <router-link to="/movie/4">电影4</router-link> <router-link to="/movie/5">电影5</router-link> 【4】需求,Move组件中需要根据id的值来展示对应电影的详情信息。还可以写成 <span>获取到的id的值为{{$route.params.id}}</span> <template> <div class="movie-container"> <h3>Movie组件</h3> <!--<button @click="printThis">打印</button>--> <span>获取到的id的值为{{this.$route.params.id}}</span> !!!!!!!!!!!!!!!!! </div> </template> <script> export default { name: "Move" } </script> <style scoped> </style>
****************************************************************************************************************************************************************************
187、动态路由-开启props 【1】this.$route是路由的参数对象。this.$router是路由的导航对象。 ***************************************开启props routes: [ {path: '/', redirect: '/home'},/*重定向到home*/ {path: '/home', component: Home}, {path: '/movie/:id', component: Move, props: true}, // !!!!!!必须开启 { path: '/about', component: About, redirect: '/about/left', children: [ //需要注意的是:子路由规则不要以/开头 {path: 'left', component: Left}, {path: 'right', component: Right} ] }, ] ***************************************接收 export default { name: "Move", props: ['id'] } ***************************************使用 <span>获取到的id的值为{{this.$route.params.id}}---{{id}}</span> 【2】props: true路由规则里有参数项,必须指定。如果没有参数的就无需指定了。
****************************************************************************************************************************************************************************
188、拓展query和fullPath 【1】/movie/2 斜线后面2这叫做路径参数。 ************************************************* $route.params.id通过这个来拿$route.params.xxx 【2】/movie/2?name=zs&age=20 这种叫做查询参数 ************************************************* 问号后面的参数项,叫做查询参数 通过this.$route.query查询 *************************************************可以通过Movie.vue打印this查看 <template> <div class="movie-container"> <h3>Movie组件</h3> <!--<button @click="printThis">打印</button>--> <!--this.$route是路由的参数对象--> <span>获取到的id的值为{{this.$route.params.id}}---{{id}}</span> <button @click="showThis">打印this</button> </div> </template> <script> export default { name: "Move", props: ['id'], methods: { showThis() { console.log(this); } } } </script> <style scoped> </style> 【2】通过打印this可以看到path为/movie/2。而fullPath为/movie/2?name=zs。
****************************************************************************************************************************************************************************
189、编程式导航跳转-push、replace、go 【1】声明式导航(点击标签的形式)&编程式导航(js写代码实现的跳转) 【2】vue-router提供了编程式导航push、replace、go *********************************都是this.$router(注意多个r)push 会增加一条历史记录 <template> <div class="home-container"> <h3>Home组件</h3> <hr/> <button @click="gotoMovie1">跳转到电影1页面</button> </div> </template> <script> export default { name: "Home", methods: { gotoMovie1() { this.$router.push('/movie/1') } } } </script> <style scoped> </style> *********************************replace,跳转指定hash地址,并替换掉当前的历史记录 <template> <div class="home-container"> <h3>Home组件</h3> <hr/> <button @click="pushMovie1">push跳转到电影1页面</button> <button @click="replaceMovie1">replace跳转到电影1页面</button> </div> </template> <script> export default { name: "Home", methods: { pushMovie1() { this.$router.push('/movie/1') }, replaceMovie1() { this.$router.replace('/movie/1') } } } </script> <style scoped> </style> ******************************************************************go(数值n) <template> <div class="movie-container"> <h3>Movie组件</h3> <!--<button @click="printThis">打印</button>--> <!--this.$route是路由的参数对象--> <span>获取到的id的值为{{this.$route.params.id}}---{{id}}</span> <button @click="showThis">打印this</button> <button @click="goBack">回退</button> </div> </template> <script> export default { name: "Move", props: ['id'], methods: { showThis() { console.log(this); }, goBack() { this.$router.go(-1) } } } </script> <style scoped> </style> ******************************************************************go的简洁用法 this.$router.back() this.$router.forward()
****************************************************************************************************************************************************************************
190、导航守卫可以控制路由的访问权限 【1】比如需要token才能访问 Main—>导航守卫(检测是否登录)—>Login登录—>Main后台主页 【2】全局前置守卫 ****************************************************************** 每次发生路由的导航跳转时,都会触发全局前置守卫。因此全局前置守卫中,程序 员可以对每个路由进行访问权限控制。 【3】守卫方法的3个形参to、from、next ******************************************************************router-index.js // 为router声明全局前置导航守卫 // 发生路由跳转,必然触发beforeEach指定的function回调 router.beforeEach(function (to, from, next) { // next() 不调用则所有的路由都被拦截,不调谁都不能访问 }) ****************************************************************** // 为router声明全局前置导航守卫 // 发生路由跳转,必然触发beforeEach指定的function回调 router.beforeEach(function (to, from, next) { console.log(from) // 将要离开的 console.log(to) // 将要访问的 next() })
****************************************************************************************************************************************************************************
191、导航守卫-next函数的三种调用方式 【1】如果有访问权限,next() 直接放行 【2】如果没有访问权限,强制用next('/Login')强制访问Login页面 【3】强制停留在当前页面next(false)
****************************************************************************************************************************************************************************
192、路由守卫-控制访问权限 【1】定义Login和Main的路由规则 ********************************************************************* // 创建路由实例对象 const router = new VueRouter({ // routers是一个数组,是来定义hash地址与组件间的对应关系的。 routes: [ {path: '/', redirect: '/home'},/*重定向到home*/ {path: '/home', component: Home}, {path: '/movie/:id', component: Move, props: true}, { path: '/about', component: About, redirect: '/about/left', children: [ //需要注意的是:子路由规则不要以/开头 {path: 'left', component: Left}, {path: 'right', component: Right} ] }, {path: "/login", component: Login}, {path: "/main", component: Main}, ] }) *********************************************************************拦截判断 router.beforeEach(function (to, from, next) { // 拿到用户将要访问的hash地址,如果等于/main 证明页面是需要登录才能访问的 if (to.path === '/main') { const token = localStorage.getItem('token') if (token) { next() } else { next('/login') } } else { next() } }) *********************************************************************模拟登录 在LocalStorage下面的本地网址添加token 123然后再点击登录
****************************************************************************************************************************************************************************
193、VUE2.0实例-安装和配置路由 【1】npm i vue-router@3.5.2 -S **********************************************************路由的配置 Vue.use(VueRouter); ********************************************************** import Vue from 'vue' import App from './App.vue' import router from './router' // 导入样式 import './assets/css/bootstrap.css' import './index.css' Vue.config.productionTip = false new Vue({ render: h => h(App), router }).$mount('#app')
****************************************************************************************************************************************************************************
194、基于路由渲染登录组件 【1】在router-index.js里配置路由跳转 ******************************************************************** //登录的路由规则,上面导入 { path: "/login", component: Login, }, ********************************************************************App.vue <template> <!-- 路由占位符 --> <router-view></router-view> </template> ********************************************************************根路径重定向 { path: "/", redirect: "/login", },
****************************************************************************************************************************************************************************
195、模拟登录 【1】登录的判断逻辑admin 123456 <template> <div> <div> <!-- 头像区域 --> <div class="text-center avatar-box"> <img src="../assets/logo.png" class="img-thumbnail avatar" alt=""/> </div> <!-- 表单区域 --> <div class="form-login p-4"> <!-- 登录名称 --> <div class="form-group form-inline"> <label for="username">登录名称</label> <input type="text" class="form-control ml-2" id="username" placeholder="请输入登录名称" autocomplete="off" v-model.trim="username"/> </div> <!-- 登录密码 --> <div class="form-group form-inline"> <label for="password">登录密码</label> <input type="password" class="form-control ml-2" id="password" placeholder="请输入登录密码" v-model.trim="password"/> </div> <!-- 登录和重置按钮 --> <div class="form-group form-inline d-flex justify-content-end"> <button type="button" class="btn btn-secondary mr-2" @click="reset"> 重置 </button> <button type="button" class="btn btn-primary" @click="login"> 登录 </button> </div> </div> </div> </div> </template> <script> export default { name: 'MyLogin', data() { return { username: '', password: '' } }, methods: { reset() { //!!!!!!!!!!!!! this.username = '' this.password = '' }, login() { //!!!!!!!!!!!!! if (this.username === 'admin' && this.password === '123456') { //存储token localStorage.setItem('token', 'kong xxx') //跳转 到后台主页 this.$router.push('/home') } else { //登录失败 localStorage.removeItem('token') } } } } </script> <style scoped> .login-container { background-color: #35495e; height: 100%; .login-box { width: 400px; height: 250px; background-color: #fff; border-radius: 3px; position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); box-shadow: 0 0 6px rgba(255, 255, 255, 0.5); .form-login { position: absolute; bottom: 0; left: 0; width: 100%; box-sizing: border-box; } } } .form-control { flex: 1; } .avatar-box { position: absolute; width: 100%; top: -65px; left: 0; .avatar { width: 120px; height: 120px; border-radius: 50% !important; box-shadow: 0 0 6px #efefef; } } </style>
****************************************************************************************************************************************************************************
196、说明Token 【1】前缀带不带取决于后端服务器
****************************************************************************************************************************************************************************
197、实现后台主页的基础布局 【1】Home.vue ************************************************************** <template> <div> <!-- 头部区域 --> <MyHeader></MyHeader> <!-- 页面主体区域 --> <div> <!-- 左侧边栏 --> <MyAside></MyAside> <!-- 右侧内容主体区 --> <div> <!-- 路由占位符 --> <router-view></router-view> </div> </div> </div> </template> 【2】Header.vue <template> <div class="layout-header-container d-flex justify-content-between align-items-center p-3"> <!-- 左侧 logo 和 标题区域 --> <div class="layout-header-left d-flex align-items-center user-select-none"> <!-- logo --> <img src="../../assets/heima.png" alt=""> <!-- 标题 --> <h4 class="layout-header-left-title ml-3">黑马后台管理系统</h4> </div> <!-- 右侧按钮区域 --> <div> <button type="button" class="btn btn-light" @click="logout"> 退出登录 </button> </div> </div> </template> <script> export default { name: 'MyHeader', methods: { logout() { //清空token localStorage.removeItem('token') //跳转回登录页面 this.$router.push('/login') } } } </script> <style scoped> .layout-header-container { height: 60px; border-bottom: 1px solid #eaeaea; } .layout-header-left-img { height: 50px; } </style> 【3】Aside.vue <template> <div> <!-- 左侧边栏列表 --> <ul class="user-select-none menu"> <li> <!-- 子路由链接要以/开头 --> <router-link to="/home/users">用户管理</router-link> </li> <li> <router-link to="/home/rights">权限管理</router-link> </li> <li> <router-link to="/home/goods">商品管理</router-link> </li> <li> <router-link to="/home/orders">订单管理</router-link> </li> <li> <router-link to="/home/settings">系统设置</router-link> </li> </ul> </div> </template> <script> export default { name: 'MyAside' } </script> <style scoped> .layout-aside-container { width: 250px; height: 100%; border-right: 1px solid #eaeaea; } .menu { list-style-type: none; padding: 0; .menu-item { line-height: 50px; font-weight: bold; font-size: 14px; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; &:hover { background-color: #efefef; cursor: pointer; } a { display: block; color: black; padding-left: 30px; &:hover { text-decoration: none; } } } } // 设置路由高亮效果 .router-link-active { background-color: #efefef; box-sizing: border-box; position: relative; // 伪元素实现路由高亮效果 &::before { content: ' '; display: block; width: 4px; height: 100%; position: absolute; left: 0; top: 0; background-color: #42b983; } } </style>
****************************************************************************************************************************************************************************
199、实现子路由的嵌套展示 【1】具体的路由嵌套。 routes: [ { path: "/", redirect: "/login", }, //登录的路由规则,上面导入 { path: "/login", component: Login, }, //后台主页的路由规则 { path: "/Home", component: Home, redirect: '/home/users', children: [ //在router-link中需要写全/,在子路由规则中,子路由 / 不需要再写 {path: 'users', component: Users}, {path: 'rights', component: Rights}, {path: 'goods', component: Goods}, {path: 'orders', component: Orders}, {path: 'settings', component: Settings}, //用户详情页的路由规则 (只有在路由规则中可以出现冒号:) {path: 'userinfo/:id', component: UserDetail}, //第2+1种方式:users{path:'users/:id',component:UserDetail}, // 在userdetail中获取路径中的id值信息(第二种方式) //{path:'userinfo/:id',component:UserDetail,props:true}, ]
****************************************************************************************************************************************************************************
200、点击进入用户详情页 【1】用户列表页面 <template> <div> <!-- 标题 --> <h4>用户管理</h4> <!-- 用户列表 --> <table class="table table-bordered table-striped table-hover"> <thead> <tr> <th>#</th> <th>姓名</th> <th>年龄</th> <th>头衔</th> <th>操作</th> </tr> </thead> <tbody> <tr v-for="item in userlist" :key="item.id"> <td>{{item.id}}</td> <td>{{item.name}}</td> <td>{{item.age}}</td> <td>{{item.position}}</td> <td> <a href="#" @click.prevent="gotoDeatail(item.id)">详情</a> <!-- 第2+1种方式 --> <!-- <router-link :to="'/home/users/' + item.id">详情</router-link> --> </td> </tr> </tbody> </table> </div> </template> <script> export default { name: 'MyUser', data() { return { // 用户列表数据 userlist: [ {id: 1, name: '嬴政', age: 18, position: '始皇帝'}, {id: 2, name: '李斯', age: 35, position: '丞相'}, {id: 3, name: '吕不韦', age: 50, position: '商人'}, {id: 4, name: '赵姬', age: 48, position: '王太后'} ] } }, methods: { gotoDeatail(id) { this.$router.push('/home/userinfo/' + id) } } } </script> <style scoped></style> 【2】用户详情页面 <template> <div> <button type="button" class="btn btn-light btn-sm" @click="$router.back()">后退</button> <!-- 使用this.$route.params.id 获取路径中的id值信息(第一种方式)--> <h4>用户详情 --- {{this.$route.params.id}}</h4> <!-- (第二种方式)使用下面props属性赋值,并在路由规则中props:true,就可以直接使用id --> <!-- <h4>用户详情 --- {{id}}</h4> --> </div> </template> <script> export default { name: 'MyUserDetail', props: ['id'] } </script> <style scoped></style>
****************************************************************************************************************************************************************************
201、升级用户详情页的路由 【1】动态写id传递id参数 gotoDetail(id) { this.$router.push('/home/userinfo/' + id) } 【2】详情页拿到id {{this.$route.params.id}}
****************************************************************************************************************************************************************************
202、路由path的注意点 【1】路由重定向 { path: "/Home", component: Home, redirect: '/home/users', children: [ //在router-link中需要写全/,在子路由规则中,子路由 / 不需要再写 {path: 'users', component: Users}, {path: 'rights', component: Rights}, {path: 'goods', component: Goods}, {path: 'orders', component: Orders}, {path: 'settings', component: Settings}, //用户详情页的路由规则 (只有在路由规则中可以出现冒号:) {path: 'userinfo/:id', component: UserDetail}, //第2+1种方式:users{path:'users/:id',component:UserDetail}, // 在userdetail中获取路径中的id值信息(第二种方式) //{path:'userinfo/:id',component:UserDetail,props:true}, ] }, 【2】path后面不要跟大写字母,这是约定
****************************************************************************************************************************************************************************
203、如何控制页面的权限 【1】判断不需要权限的菜单集合 const pathArray=['/home','/home/users']; if(pathArray.indexOf(to.path)!==-1){ 走token判断 }else{ 不走token判断 }
****************************************************************************************************************************************************************************
204、黑白头条项目结构 【1】vue-router的版本很重要 npm i vue-router@3.5.2 -S ****************************************************************** import Vue from 'vue' import VueRouter from 'vue-router' // 1、把VueRouter安装为Vue的插件 Vue.use(VueRouter) // 2、路由规则数组 const routeArray = [] // 3、创建路由实例对象 const router = new VueRouter({ routes: routeArray }) // 4、导出路由实例对象 export default router ****************************************************************** import Vue from 'vue' import App from './App.vue' import router from "@/router" Vue.config.productionTip = false new Vue({ router, render: h => h(App), }).$mount('#app') 【2】说明从0敲才能学得会
****************************************************************************************************************************************************************************
205、安装配置Vant组件库 【1】现成的组件库 npm i vant@latest-v2 ********************************************************************* import Vue from 'vue' import App from './App.vue' import router from "@/router" // 导入并安装vant插件 import Vant from 'vant' import 'vant/lib/index.css' Vue.use(Vant) Vue.config.productionTip = false new Vue({ router, render: h => h(App), }).$mount('#app') ********************************************************************* <van-button type="primary">主要按钮</van-button> <van-button type="success">成功按钮</van-button> <van-button type="default">默认按钮</van-button> <van-button type="warning">警告按钮</van-button> <van-button type="danger">危险按钮</van-button>
****************************************************************************************************************************************************************************
206、初始化-使用Tabbar组件 【1】开启路由模式的tabBar <template> <div id="app"> <!--路由占位符--> <!--tabBar--> <van-tabbar route> <van-tabbar-item replace to="/" icon="home-o">首页</van-tabbar-item> <van-tabbar-item replace to="/user" icon="user-0">我的</van-tabbar-item> </van-tabbar> </div> </template> <script> export default { name: "App" } </script> <style scoped> </style>
****************************************************************************************************************************************************************************
207、通过路由展示对应页面 【1】你只管努力,生活会给你惊喜!!!!!!!!!! <template> <div id="app"> <!--路由占位符--> <router-view></router-view> <!--tabBar--> <van-tabbar route> <van-tabbar-item icon="home-o" to="/">首页</van-tabbar-item> <van-tabbar-item icon="user-o" to="/user">我的</van-tabbar-item> </van-tabbar> </div> </template> <script> export default { name: "App" } </script> <style scoped> </style> ************************************************************************************ import Vue from 'vue' import VueRouter from 'vue-router' import Home from "@/views/Home/Home"; import User from "@/views/User/User"; // 1、把VueRouter安装为Vue的插件 Vue.use(VueRouter) // 2、路由规则数组 const routeArray = [ // 首页路由规则 {path: '/', component: Home}, {path: '/user', component: User} ] // 3、创建路由实例对象 const router = new VueRouter({ routes: routeArray }) // 4、导出路由实例对象 export default router
****************************************************************************************************************************************************************************
208、使用NavBar组件 【1】初步体验标题固定+内容展示 <template> <div> <!--头部区域--> <van-nav-bar title="标题" fixed/> <h3>aaa</h3> <h3>xxx</h3> <h3>xxx</h3> <h3>xxx</h3> <h3>xxx</h3> <h3>xxx</h3> <h3>xxx</h3> <h3>xxx</h3> <h3>xxx</h3> <h3>xxx</h3> <h3>xxx</h3> <h3>xxx</h3> <h3>xxx</h3> <h3>xxx</h3> <h3>xxx</h3> <h3>xxx</h3> <h3>xxx</h3> <h3>xxx</h3> <h3>xxx</h3> <h3>zzz</h3> </div> </template> <script> export default { name: "Home" } </script> <style scoped> .home-container { padding: 46px 0 50px 0; } </style>
****************************************************************************************************************************************************************************
209、覆盖NavBar的默认样式 【1】会用到类选择器的知识 <template> <div> <!--头部区域--> <van-nav-bar title="黑马头条" fixed/> <h3>aaa</h3> <h3>xxx</h3> <h3>xxx</h3> <h3>xxx</h3> <h3>xxx</h3> <h3>xxx</h3> </div> </template> <script> export default { name: "Home" } </script> <style scoped> .home-container { padding: 46px 0 50px 0; .van-nav-bar { background-color: #007bff; } /deep/ .van-nav-bar__title { color: white; } } </style>
****************************************************************************************************************************************************************************
210、获取列表数据 【1】主要是API接口返回json数据结构概述
****************************************************************************************************************************************************************************
211、封装utils目录下的方法 【1】配置axios ********************************************************************安装 npm i axios -S ********************************************************************src下创建utils /*req.js*/ import axios from "axios"; // 创建req即为小的axios const req = axios.create({ // 指定请求的根路径 baseURL: 'https://www.escook.cn' }) export default req
****************************************************************************************************************************************************************************
212、在Home组件封装方法 【1】async...await *********************************************************注意get请求的传参 created() { this.initList(); }, methods: { // 获取文章列表数据的方法 async initList() { const {data: res} = await req.get(`/articles`, { // 请求参数 params: { _page: this.page, _limit: this.limit } }) console.log(res); } }
****************************************************************************************************************************************************************************
213、封装articleAPI模块 【1】主要是每次拿数据的复用 /*文章相关的API都放在这个下面*/ // 向外按需导出一个API函数 import req from "@/utils/req"; export const getArticleListAPI = function (_page, _limit) { // 返回了一个promise对象 return req.get(`/articles`, { // 请求参数 params: { _page: _page, _limit: _limit } }) } 【2】Home User多方调用 import {getArticleListAPI} from '@/api/articleAPI.js' // 获取文章列表数据的方法 async initList() { const {data: res} = await getArticleListAPI(this.page, this.limit); console.log(res); } *************************************************************************************** import {getArticleListAPI} from '@/api/articleAPI.js' // 获取文章列表数据的方法 async initList() { const {data: res} = await getArticleListAPI(this.page, this.limit); console.log(res); } 【3】这才是axios的真正的用法,法拉利开出法拉利的速度!!!!!!!!!
****************************************************************************************************************************************************************************
214、封装ArticleInfo组件 【1】如果不是直接被路由到的组件,就放到components文件夹下面 <template> <div> ArticleInfo组件 </div> </template> <script> export default { name: "ArticleInfo" } </script> <style scoped> </style> ************************************************************************************ <!--导入 注册 并使用ArticleInfo 组件--> <ArticleInfo v-for="item in articleList" :key="item.id"></ArticleInfo> ************************************************************************************ components: { //注册组件 ArticleInfo },
****************************************************************************************************************************************************************************
215、升级ArticleInfo 【1】自定义属性。 props: { // 自定义属性 // 标题 title: {type: String, default: ''}, // 作者 author: {type: String, default: ''}, // 评论数 类型指向数组,包含可能的类型即可。 commentNum: {type: [Number, String], default: 0}, time: {type: String, default: ''} } 【2】父组件传值 <!--导入 注册 并使用ArticleInfo 组件--> <ArticleInfo v-for="item in articleList" :key="item.id" :title="item.title" :author="item.aut_name" :commentNum="item.comm_count" :time="item.pubdate"> </ArticleInfo> 【3】大写的可以改成连字符(建议,不使用也没关系)
****************************************************************************************************************************************************************************
226、图片 【1】自定义图片对象属性 // 图片 cover: { type: Object, default: function () { return {cover: 0} } } 【2】v-if来展示1张还是3张图片 <img :src="cover.images[0]" v-if="cover.type===1"> <div v-if="cover.type===3"> <img :src="cover.images[0]"> <img :src="cover.images[1]"> <img :src="cover.images[2]"> </div>
****************************************************************************************************************************************************************************
227、上拉加载更多,了解List组件 【1】List组件通过loading和finised两个变量控制加载状态 *****************************************************触发load把loading改为true 为true的时候,不会反复调用load事件,防止用户非法操作。 拿到数据后,把loading的值改为false *****************************************************finised用来标记是否还有更多数据
****************************************************************************************************************************************************************************
218、使用List组件 【1】真牛批,大成若缺!!! <template> <div> <!--头部区域--> <van-nav-bar title="黑马头条" fixed/> <!--导入 注册 并使用ArticleInfo 组件--> <van-list v-model:loading="loading" :finished="finished" finished-text="没有更多了" @load="onLoad"> <ArticleInfo v-for="item in articleList" :key="item.id" :title="item.title" :author="item.aut_name" :commentNum="item.comm_count" :time="item.pubdate" :cover="item.cover"> </ArticleInfo> </van-list> </div> </template> <script> import {getArticleListAPI} from '@/api/articleAPI.js'; import ArticleInfo from "@/components/Article/ArticleInfo"; export default { name: "Home", components: { //注册组件 ArticleInfo }, data() { return { page: 1, limit: 10, articleList: [], loading: true, // 是否正在加载下一页 finished: false // 所有数据是否加载完毕 } }, created() { this.initList(); }, methods: { // 获取文章列表数据的方法 async initList() { const {data: res} = await getArticleListAPI(this.page, this.limit); // console.log(res); this.articleList = [...this.articleList, ...res]; // 旧数据在前,新数据在后 this.loading = false; if (res.length === 0) { // 如果res为空 表示数据加载完毕 this.finished = true; } }, onLoad() { console.log("触发load事件") // 页码值甲乙 this.page++; // 重新调用方法 this.initList(); } } } </script> <style scoped> .home-container { padding: 46px 0 50px 0; .van-nav-bar { background-color: #007bff; } /deep/ .van-nav-bar__title { color: white; } } </style>
****************************************************************************************************************************************************************************
220、实现下拉刷新功能 【1】下拉刷新,牛批!!!!!!!!!!!!!!!!!!! <van-pull-refresh v-model="refreshing" @refresh="onRefresh"> *************************************************************************** <template> <div> <!--头部区域--> <van-nav-bar title="黑马头条" fixed/> <!--导入 注册 并使用ArticleInfo 组件--> <van-pull-refresh v-model="refreshing" @refresh="onRefresh" :disabled="finished"> <!-- :disabled="finished"控制加载完毕后的无效刷新--> <van-list v-model:loading="loading" :finished="finished" finished-text="没有更多了" @load="onLoad"> <ArticleInfo v-for="item in articleList" :key="item.id" :title="item.title" :author="item.aut_name" :commentNum="item.comm_count" :time="item.pubdate" :cover="item.cover"> </ArticleInfo> </van-list> </van-pull-refresh> </div> </template> <script> import {getArticleListAPI} from '@/api/articleAPI.js'; import ArticleInfo from "@/components/Article/ArticleInfo"; export default { name: "Home", components: { //注册组件 ArticleInfo }, data() { return { page: 1, limit: 10, articleList: [], loading: true, // 是否正在加载下一页 finished: false, // 所有数据是否加载完毕 refreshing: false } }, created() { this.initList(); }, methods: { // 获取文章列表数据的方法 async initList(refreshFlag) { // 封装的思想 const {data: res} = await getArticleListAPI(this.page, this.limit); // console.log(res); if (refreshFlag) { // 下拉刷新 this.articleList = [...res, ...this.articleList]; // 新在前 this.refreshing = false; } else { this.articleList = [...this.articleList, ...res]; // 旧数据在前,新数据在后 this.loading = false; } if (res.length === 0) { // 如果res为空 表示数据加载完毕 this.finished = true; } }, onLoad() { console.log("触发load事件") // 页码值加一 this.page++; // 重新调用方法 this.initList(false); }, onRefresh() { // 下拉刷新的处理函数 console.log("触发了下拉刷新") this.page++; // 头部拼接 this.initList(true); } } } </script> <style scoped> .home-container { padding: 46px 0 50px 0; .van-nav-bar { background-color: #007bff; } /deep/ .van-nav-bar__title { color: white; } } </style>
****************************************************************************************************************************************************************************
221、Vant定制主题 【1】为了覆盖默认的less变量,在main.js引入import 'vant/lib/index.less'
****************************************************************************************************************************************************************************
222、覆盖主题变量 【1】修改样式变量,但是每次都重启,开发中不用这个。 // vue.config.js module.exports = { css: { loaderOptions: { less: { // 若 less-loader 版本小于 6.0,请移除 lessOptions 这一级,直接配置选项。 lessOptions: { modifyVars: { // 直接覆盖变量 'nav-bar-background-color': 'orange', // 或者可以通过 less 文件覆盖(文件路径为绝对路径) // hack: `true; @import "your-less-file-path.less";`, }, }, }, }, }, }; 【2】less是可编程的样式语言。
****************************************************************************************************************************************************************************
224、通过theme.less指定 【1】less文件 /* theme.less */ @blue: #007bff; // 覆盖NavBar的less样式 @nav-bar-background-color: @blue; @nav-bar-title-text-color: white; 【2】less文件绝对路径指定 // vue.config.js // webpack在进行打包的时候,底层用到了node.js 因此在vue.config.js配置文件中,可以导入并使用node.js中的核心模块 const path = require('path') const themePath = path.join(__dirname, './src/theme.less') module.exports = { css: { loaderOptions: { less: { // 若 less-loader 版本小于 6.0,请移除 lessOptions 这一级,直接配置选项。 lessOptions: { modifyVars: { // 直接覆盖变量 // 'nav-bar-background-color': 'orange', // 或者可以通过 less 文件覆盖(文件路径为绝对路径) 这个修改就不用重启服务器!!!!!!!!!!!!!!!!!!!!!!!!! hack: `true; @import "${themePath}";`, }, }, }, }, }, };
****************************************************************************************************************************************************************************
225、最后 【1】在前后端分离的情况下。而在开发过程中,我们常常需要在window系统下使用 Nginx作为Web服务器。 【2】安装完毕后,启动后在html下面放dist就完事了
****************************************************************************************************************************************************************************
226、总结 【1】vue-cli创建与开发工程化的vue项目 【2】组件化的开发思想 【3】指令、插槽、侦听器、声明周期、通讯函数来完成开发 【4】axios封装请求API接口 【5】vue-router实现单页面应用程序开发 【6】使用Vant与Element UI 【7】vue-devtools调试自己的vue项目