绑定样式
<body>
<div id="app">
<ul class="list">
<!-- 方式一 对象形式 根据js表达式的值添加样式 -->
<!-- class绑定样式 对象的属性名是样式名称,属性值是布尔值 -->
<li v-for="(item,index) in citys" :key="index" @click="dot(index)" :class="{active:index===activeIndex}">
{{item}}</li>
</ul>
<!-- 方式二 三元表达式 必须在数组里面 -->
<ul class="list">
<li v-for="(item,index) in citys" :key="index" @click="dot(index)" :class="[index===activeIndex?'active':'']">
{{item}}</li>
</ul>
<hr>
<!-- 绑定style 行内样式的值可以被设置为变量 -->
<div class="box" :style="{backgroundColor:bgColor,fontSize:fs}">Hello</div>
<button @click="bgColor='tomato'">添加背景</button><button @click="fs='30px'">增大字体</button>
</div>
<script>
new Vue({
el: '#app',
data: {
// 定义高亮索引
activeIndex: 0,
bgColor: 'white',
fs: '15px',
// 定义循环列表
citys: ["运城", "大同", "太原", "临汾"]
},
methods: {
dot(index) {
this.activeIndex = index
}
},
})
</script>
</body>
路由
1.配置和守卫
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
//定义路由具体信息
const routes = [
{
path: '/',
// 重定向到指定的路由
redirect: '/home'
},{
path: '/home',
// 路由的懒加载方式一:每个都组件懒加载
/* component: () =>
import ('@v/Home.vue') */
// 路由的懒加载方式二:按模块分,可将组件分成几个模块分别打包
component: () =>
import ( /* webpackChunkName: "home" */ '@v/Home.vue')
},]
//创建路由器对象,管理所有路由
const router = new VueRouter({
routes
})
// 路由前置守卫
router.beforeEach((to, from, next) => {
NProgress.start();
next()
})
//路由后置守卫
router.afterEach((to, from) => {
NProgress.done();
//获取元信息更改标题
document.title = to.meta.title
})
export default router
2.在main.js中引入
import router from './router'
引入后必须在当前项目中注册router ,重要
3.路由传参
1.params
params相当于post传参
第一种:http://localhost:8080/newsPublishPC/newsDetails key和val都是隐式
跳转的是路由的name,所以路由必须有name
url不显示键值对,需要缓存 刷新会丢失
路由配置
{
path: '/newsDetails',
name: 'newsDetails',
meta: { title: '新闻详情', },
component: () =>
import('@/views/newsPublishPC/newsDetails'),
},
传递参数
sessionStorage.setItem('newsPublishPC_id', item.id)
this.$router.push({
name: 'newsDetails',
params: {
id: item.id
}
})
接收参数
this.params.id = this.$route.params.id || sessionStorage.getItem('newsPublishPC_id')
第二种:http://localhost:8080/newsPublishPC/newsDetails/12345
路由后面拼接上参数传递,刷新不会丢失,url会显示val,不显示key
路由配置 props为true
{
path: '/newsDetails/:id',
props: true,
meta: { title: '新闻详情', },
component: () =>
import('@/views/newsPublishPC/newsDetails'),
}
传递
this.$router.push(`/newsDetails/${item.id}`)
接收
props:['id']
2.query
类似于get参数 刷新不会丢失 无需缓存
注意:传递对象的话需要用json.stringify和json.parse
传递
this.$router.push({
path: '/newsDetails',
query: {
id: item.id
}
})
接收
this.$router.query.id
4.多级路由跳转路径
// 第一种显示完整路径
<router-link to="/stu-manager/stu-list">查询学生</router-link>
// 第二种短路径 没有前缀
<router-link to="/stu-list">查询学生</router-link>
{
// 第一种不加 ‘/’ 显示完整路径
path: 'stu-list',
// 第二种加‘/’ 显示单独路径
path: '/stu-list',
meta: {
title: '查询学生'
},
component: () =>
import ( /* webpackChunkName: "stu-manager" */ '../views/stu/StuList.vue')
}
5.路由懒加载
// 1.每个路径都懒加载
component: () => import ('../views/stu/StuList.vue')
component: () => import ( /* webpackChunkName: "stu-manager" */ '../views/stu/StuList.vue')
6.路由缓存
// 默认是全部缓存, :include设置包裹哪些组件进行缓存,数组形式单引号,组件的name,注意大小写 ,缓存了以后vue原有的钩子就不会执行了
<keep-alive :include="['Student', 'System']">
<router-view />
</keep-alive>
7.路由钩子
// 路由的两个钩子 只有当路由被缓存时再用这两个钩子 否则没有意义,在缓存后需要做销毁动作可在这两个钩子里面 例如 销毁定时器等等
activated() {
this.start();
console.log("student挂载了");
},
deactivated() {
clearInterval(this.timer);
console.log("student销毁了");
}
8.路由钩子的使用
// 路由被缓存后切换过来的激活钩子
activated() {
this.$http.get('/json/news.json').then(({ data: { result: { list } } }) => {
// 初次给值 相当于mounted的初次渲染列表
this.news = list
// 后续操作列表后全部用这个列表获取到被操作后的导航列表,根据留下的列表加载新闻页面
this.news = list.filter(r => this.$store.state.myNav.includes(r.classify))
})
},
vuex
1.安装
npm i vuex@3
2.配置
import Vue from 'vue'
import Vuex from 'vuex'
import axios from 'axios'
Vue.use(Vuex)
export default new Vuex.Store({
//定义全局状态
state:{}
//定义全局计算属性
//计算属性只有一个参数就是state,用他来点出属性
getters: {
// 计算属性
fullName(state) {
return state.firstName + state.lastName
}
},
//定义全局方法
//方法有两个参数,state,val
//state表示当前状态,val表示新值
mutations: {
setFirstName(state, val) {
return state.firstName = val
},
setLastName(state, val) {
return state.lastName = val
},
updateYwScore(state, val) {
state.ywScore = val
},
updateStudent(state, val) {
state.student = val
}
},
// 异步必须在action里面 形参是context是上下文, val是具体的值
actions: {
updateYwScore(context, val) {
// setTimeout(() => {
// // 通过context.commit(),再次调用mutation里面的方法实现异步方法
// context.commit('updateYwScore', parseInt(val - Math.random() * 10))
// }, 1000)
axios.get("https://www.bingjs.com:8001/Student/GetAll").then(({ data }) => {
context.commit('updateYwScore', data.length)
// 调用mutations里面的赋值方法,将请求回来的值给到state里面
context.commit('updateStudent', data)
});
}
},
//moules定义模块 定义的模块要引入到index.js中并注册
modules: {
car,
plane
}
})
定义modules模块 定义后要引入到index.js中
export default {
// namespaced模块私有化属性, 默认是false不私有化
// 非私有化的模块在合并模块里面的成员时,state会包一层对象,必须点出来,其他的不会包
// 私有化的getters,mutations,actios里面的方法在合并时会变成:模块名称/方法名 重新命名
namespaced: true,
state: {
name: '保时捷',
price: 150
},
getters: {
carInfo(state) {
return `汽车名称:${state.name},汽车价格:${state.price}`
}
},
mutations: {
updateCarName(state, val) {
state.name = val
},
updateCarPrice(state, val) {
state.price = val
}
},
actions: {
updateCarPrice(context, val) {
setTimeout(() => {
context.commit('updateCarPrice', val)
}, 1000)
}
}
}
3.组件调用store
调用属性
模板中:{{ $store.state.firstName }}
如果调用私有化的模块 {{this.$store.state.plane.list}}
// 或者在计算属性中调用,使模板简洁,前提是在store的index文件中已经计算好了
computed: {
fullName() {
return this.$store.getters.fullName;
},
sumScore() {
return this.$store.getters.sumScore;
},
}
调用方法
// 调用mutations方法用commit 调用actions方法用dispatch
updateYwScore() {
// 通过commit调用mutation里面的方法 同步
this.$store.commit("updateYwScore", 100);
// 通过dispatch调用actions里面的方法 异步
this.$store.dispatch("updateYwScore", 100);
},
// 调用私有化模块的话要加私有化的前缀
methods: {
add() {
// 不私有化不用 加前缀
this.$store.commit("addPlane", this.feiji);
// 私有化要加前缀
// this.$store.commit("plan/addPlane", this.feiji);
},
},
加载数据
// 在组件中通过 mounted钩子调用本函数的方法
methods: {
updateYwScore() {
this.$store.dispatch("updateYwScore", 100);
this.student = $store.state.student;
},
},
mounted() {
this.updateYwScore();
},
4.映射函数
// 导入映射函数
import { mapState, mapGetters, mapMutations, mapActions } from "vuex"
// 映射数据和计算属性
computed: {
// 映射函数会将state中的数据映射到组件
...mapState(["firstName", "lastName", "age"]),
// 映射vuex中的getters
...mapGetters(["fullName", "shMoney"])
// 映射modules里面的要加前缀
...mapState("car", ["name", "color", "price"]),
// ...mapState(["name", "color", "price"]),
...mapGetters("car", ["carInfo"]),
},
//映射函数
methods: {
// 映射vuex中的方法,只能行内传参 方法名必须和vuex一样,且必须传参
...mapMutations(["updateFirstName", "updateLastName"]),
// 映射vuex中的mapActions 用法同映射mapMutations一样
...mapActions(["updateAge", "updateMoney"]),
------映射modules里面的要加前缀-----
...mapMutations("car", ["updateName", "updateColor"]),
...mapActions("car", ["updatePrice"]),
},
5.上下互换位置方法 (对象)
up(index) {
// index获取当前对象
let obj1 = this.list[index];
// index获取当前对象的上一个对象
let obj2 = this.list[index - 1];
// 通过$set互换属性
this.$set(this.list, index, obj2);
this.$set(this.list, index - 1, obj1);
},
down(index) {
// index获取当前对象
let obj1 = this.list[index];
// index获取当前对象的上一个对象
let obj2 = this.list[index + 1];
// 通过$set互换属性
this.$set(this.list, index, obj2);
this.$set(this.list, index + 1, obj1);
},
事件修饰符
1.prevent:阻止默认事件(常用);
2.stop:阻止事件冒泡(常用);
3.once:事件只触发一次(常用);
4.capture:使用事件的捕获模式;
5.self:只有event.target是当前操作的元素时才触发事件;
6.passive:事件的默认行为立即执行,无需等待事件回调执行完毕;
按钮修饰符
enter .tab .delete (捕获“删除”和“退格”键) .esc .space .up .down .left .right
<input type="text" @keyup.enter="search">
<button>搜索</button>
指令
v-model: 双向数据绑定
v-for: 遍历数组/对象/字符串
v-on: 绑定事件监听, 可简写为@
v-if: 条件渲染(动态控制节点是否存存在)
v-else: 条件渲染(动态控制节点是否存存在)
v-show: 条件渲染 (动态控制节点是否展示)
v-text:
1.作用:向其所在的节点中渲染文本内容。
2.与插值语法的区别:v-text会替换掉节点中的内容,{{xx}}则不会。
v-html:
1.作用:向指定节点中渲染包含html结构的内容
2.与插值语法的区别:(1).v-html会替换掉节点中所有的内容,{{xx}}则不会。(2).v-html可以识别html结构。
3.严重注意:v-html有安全性问题!(1).在网站上动态渲染任意HTML是非常危险的,容易导致XSS攻击。(2).一定要在可信的内容上使用v-html,永不要用在用户提交的内容上!
v-cloak :
1.本质是一个特殊属性,Vue实例创建完毕并接管容器后,会删掉v-cloak属性。
2.使用css,display:none配合v-cloak可以解决网速慢时页面展示出{{xxx}}的问题
v-once:
1.v-once所在节点在初次动态渲染后,就视为静态内容了。
2.以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能。
v-pre:
1.跳过其所在节点的编译过程。
2.可利用它跳过没有使用指令语法、没有使用插值语法的节点,会加快编译。
动画
动画语法:
默认是v-开头 如果给transtion加了name属性就要用name开头
给循环的标签加动画必须-grop 且必须有k值
定义动画
@keyframes 名字{
from{}
to{}
}
动画进入
.v-enter-active{
animation: 名字 时间 匀速
}
动画离开
.v-enter-active{
animation: 名字 时间 匀速
}
定义过渡(谁用过渡给谁身上加过渡)
进入的起点 和离开的终点一样
.v-enter{}
进入的终点 和离开的起点一样
.v-enter-to{}
离开的起点
.v-leave{}
离开的终点
.v-leave-to{}
动画库
npm install animate.css --save
哪个组件用就在哪个组件引入
import 'animate.css'
使用:
name="animate__animated animate__bounce"
enter-active-class="动画名"
leave-active-class="动画名"
例子:循环要放到transition-group上面 里面嵌套盒子
<transition-group
v-for="(item, index) in channel"
:key="index"
name="animate__animated animate__bounce"
enter-active-class="animate__lightSpeedInLeft"
leave-active-class="animate__lightSpeedOutRight"
appear
>
<div class="item" @click="decrement(index)" key="index">
{{ item }}
</div>
</transition-group>
组件传值
1.props 和$emit
父传子
父组件在子组件标签中通过 :name=“name”的方式
子组件用props接收数据,注意是只读的,如果要修改数据必须使用中转变量来备份
子传父
1.子组件中定义事件通过this.$emit(‘自定义事件’,数据)
<view class="item" @click="updateIndex(index)" :class="{active:myActive===index}" v-for="(item,index) in list">{{item}}</view>
methods: {
updateIndex(index){
this.myActive=index
this.$emit('updateIndex',index)
}
}
2.父组件中在子组件的标签上通过 @自定义事件=“父组件的事件”
// 简单的直接通过$event赋值
<tabMenu label="配料" :list="pl" :active="plActive" @updateIndex="plActive=$event"></tabMenu>
// 复杂的定义自定义事件
<tabMenu label="温度" :list="wd" :active="wdActive" @updateIndex="getIndex"></tabMenu>
methods: {
getIndex(e){
console.log(e)
this.wdActive=e
}
}
- 还可以将事件合并成v-model
子组件接收传过去的值必须是'value',自定义事件必须'input'
export default {
props:['label','list','value'], //这里必须是value
data() {
return {
myActive:this.value
}
},
methods: {
updateIndex(index){
this.myActive=index
this.$emit('input',index) //这里必须是input
}
}
}
父组件合并事件为v-model
<tabMenu label="糖分" :list="tf" v-model="tfActive"></tabMenu>
<tabMenu label="配料" :list="pl" v-model="plActive"></tabMenu>
<tabMenu label="温度" :list="wd" v-model="wdActive"></tabMenu>
2.依赖注入 provide 和inject
父可以给任意后代传递数据
用法等同于props,区别是可以传函数
父组件中定义provide(){return{数据或者函数}}
子组件用inject接收 ,注意,也是只读,修改需要备份
子组件通过接收到的函数可以向父级通信
父组件
provide() {
return {
address: this.address,
// 注入方法,这个方法可以让后代修改自己的值 方法定义在methods
setAddress: this.setAddress,
};
},
setAddress(val) {
this.address = val;
},
子组件
inject: ["address", "setAddress"]
methods: {
undateAddress() {
this.myAddress = "南京";
this.setAddress(this.myAddress);
},
},
3.$bus
1.在main.js创建中央事件总线$bus
Vue.prototype.$bus = new Vue()
2.A组件发布消息
methods: {
getData() {
this.$bus.$emit("hello", this.car);
},
},
3.B组件接收消息,注意:这里必须用箭头函数,否则this指向的是$bus,是获取不到数据的
mounted() {
this.$bus.$on("hello", (e) => { //e是形参
this.car = e;
});
},
4.B组件销毁事件
beforeDestroy() {
this.$bus.$off("hello");
},
4.$children $parent
<body>
<!-- 父组件修改子组件
1.$children[index],通过index获取到对应的子组件来修改某个值,但是这种方法不好,因为DOM变化了索引就会变
2.给组件添加ref属性,然后用$refs获取到子组件,来修改子组件的值, ref可以给组件加,也可以加在div上面-->
<!-- 子组件修改父组件
1. $parent父级 2.$root根级 -->
<div id="app" v-cloak>
<div class="box">
<div>
<p>姓名:{{name}}</p>
<p>年龄:{{age}}</p>
<button @click="update1">修改1</button>
<button @click="update2">修改2</button>
<button @click="update3">修改3</button>
</div>
<child1 ref="a"></child1>
<child2 ref="b"></child2>
<child3 ref="c"></child3>
</div>
</div>
<!-- 引入vue -->
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
<script>
Vue.component('child1', {
template: `
<div class="child">
<p>姓名:{{name}}</p>
<p>姓名:{{age}}</p>
</div>
`,
data() {
return {
name: '周星驰',
age: 55
}
},
})
Vue.component('child2', {
template: `
<div class="child">
<p>姓名:{{name}}</p>
<p>姓名:{{age}}</p>
<child2-1></child2-1>
</div>
`,
data() {
return {
name: '周比利',
age: 50
}
},
})
Vue.component('child2-1', {
template: `
<div class="child">
<p>姓名:{{name}}</p>
<p>姓名:{{age}}</p>
<button @click="updateParent">修改父级</button>
<button @click="updateRoot">修改根级</button>
</div>
`,
data() {
return {
name: '儿子',
age: 15
}
},
methods: {
updateParent() {
this.$parent.name = '爸爸'
this.$parent.age = 52
},
updateRoot() {
this.$root.name = '爷爷'
this.$root.age = 90
}
},
})
Vue.component('child3', {
template: `
<div class="child">
<p>姓名:{{name}}</p>
<p>姓名:{{age}}</p>
</div>
`,
data() {
return {
name: '周润发',
age: 65
}
},
})
// 关闭生产提示
Vue.config.productionTip = false
new Vue({
el: '#app',
data() {
return {
name: '李欢',
age: 22
}
},
methods: {
update1() {
// this.$refs.a.name = '朱茵'
this.$children[0].name = '朱茵'
},
update2() {
// this.$refs.b.name = '关晓彤'
this.$children[1].name = '关晓彤'
},
update3() {
// this.$refs.c.name = '赵丽颖'
this.$children[2].name = '赵丽颖'
}
},
})
</script>
</body>
5. v-model
只能绑一个v-model
<body>
<!-- 组件中使用v-model就是将v-bind:value 和 v-on:input事件给集合起来
用法: 1.组件中传递的属性值必须是 value,
2.组件中要备份value值,
3.组件中自定义回传事件必须写成input ,回传的值是备份的值
updateTitle() {
this.$emit('input', this.myTitle)
}
4.在组件中使用时直接用v-model绑定父组件中传过去的值
-->
<div id="app" v-cloak>
{{title}}
<hr>
<div>严格按照v-model的写法</div>
<b-box :value="title" @input="title=$event"></b-box>
<div>简写</div>
<b-box v-model="title"></b-box>
</div>
<!-- 引入vue -->
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
<script>
// 关闭生产提示
Vue.config.productionTip = false
Vue.component('b-box', {
// 1.定义组件
template: `
<div class="box">
<span>标题:</span>
<input type="text" v-model="myTitle">
<button @click="updateTitle">提交</button>
</div>
`,
props: ['value'], //2.接收父组件传过来的属性
// 3.备份父组件传过来的属性
data() {
return {
myTitle: this.value
}
},
methods: {
// 4.自定义事件回传组件内部title改变后的值
updateTitle() {
this.$emit('input', this.myTitle)
}
}
})
new Vue({
el: '#app',
data() {
return {
title: '你好啊小宝贝'
}
},
/* methods: {
updateTitle(e) {
this.title = e
}
}, */
})
</script>
</body>
6.sync
<body>
<div id="app" v-cloak>
<div>{{title}}</div>
<div>{{msg}}</div>
<div>{{time}}</div>
<b-box v-model="title" :time.sync="time" :msg.sync="msg"></b-box>
</div>
<!-- 引入vue -->
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
<script>
// 关闭生产提示
Vue.config.productionTip = false
Vue.component('b-box', {
// 1.定义组件
template: `
<div class="box">
<div>
<span>标题:</span>
<input type="text" v-model="myTitle">
<button @click="updateTitle">提交</button>
</div>
<div>
<span>时间:</span>
<input type="text" v-model="myTime">
<button @click="updateTime">提交</button>
</div>
<div>
<span>信息:</span>
<input type="text" v-model="myMsg">
<button @click="updateMsg">提交</button>
</div>
</div>
`,
props: ['value', 'msg', 'time'],
data() {
return {
myTitle: this.value,
myTime: this.time,
myMsg: this.msg
}
},
methods: {
updateTitle() {
this.$emit('input', this.myTitle)
},
updateTime() {
// sync修饰符必须用 update:属性名 固定写法
// 然后在组件中用 :time.sync="time" :msg.sync="msg" 双向绑定
this.$emit('update:time', this.myTime)
},
updateMsg() {
this.$emit('update:msg', this.myMsg)
}
}
})
new Vue({
el: '#app',
data() {
return {
title: '你好啊小宝贝',
msg: '宝马7系yyds',
time: '2022-2-22'
}
},
/* methods: {
updateTitle(e) {
this.title = e
}
}, */
})
</script>
</body>
自定义指令和插件
1.main.js导入自定义插件
import myplugins from './plugins'
Vue.use(myplugins)
2.创建插件和指令
plugins文件夹
export default {
install(Vue, options) {
// console.log('------------自定义全局指令-------------------');
// 定义一个v - red指令
Vue.directive('red', function(el, bind) {
el.style.color = 'red'
})
// 定义一个v - color=""指令
Vue.directive('color', function(el, { value }) {
el.style.color = value
})
// 定义一个点击事件
Vue.directive('myclick', function(el, bind) {
el.onclick = function() {
alert('你好啊')
this.style.color = 'pink'
}
})
// 定义一个鼠标进入事件
Vue.directive('active', function(el, bind) {
el.onmouseenter = function() {
this.style.color = 'pink'
}
})
Vue.directive('unactive', function(el, bind) {
el.onmouseleave = function() {
this.style.color = 'black'
}
})
// console.log('------------自定义全局混入-------------------');
Vue.mixin({
data() {
return {
base_url: "https://bingjs.com:8001",
};
},
methods: {
sayHi() {
alert("你好!我是一个组件!");
},
},
mounted() {
console.log("当前组件挂载完毕....");
},
});
// console.log('------------自定义全局过滤器-------------------');
Vue.filter("filterUS", function(val) {
return "$" + val.toFixed(2);
});
// console.log('------------自定义组件-------------------');
Vue.prototype.$http = { name: "axios" };
// 使用Vue,注册全局组件
Vue.component("b-box", {
render: (h) => {
return h("div", "b-box");
},
});
}
}
钩子
普通8个钩子
<script>
Vue.config.productionTip = false
let vm = new Vue({
el: '#app',
data: {
name: "张三",
age: 18
},
beforeCreate() {
// 此阶段还未获取到数据
console.log('代理之前');
},
created() {
// 此阶段已经获取到了数据
console.log('代理完成');
// debugger // 打断点可以看到DOM没有被渲染
},
beforeMount() {
// 此阶段已经获取到了数据但还未开始渲染页面
console.log('挂载渲染页面之前');
},
mounted() {
// mounted阶段获取到了数据并渲染页面完成
console.log('挂载完成');
// debugger
},
beforeUpdate() {
// 更新之前数据已经变化了但是还未更新页面
console.log('更新之前');
},
updated() {
//此时数据变化了 页面被被重新渲染
console.log('更新完成');
},
beforeDestroy() {
// 在销毁之前进行收尾:关闭定时器,关闭消息订阅,解绑自定义事件等
console.log('销毁之前');
},
destroyed() {
// vm被销毁了,失去了响应式
console.log('销毁完成');
},
})
setTimeout(() => {
// 销毁vm,但是钩子还可以获取到vm的东西
vm.$destroy()
}, 5000)
</script>
路由的两个钩子
// 路由的两个钩子 只有当路由被缓存时再用这两个钩子 否则没有意义,在缓存后需要做销毁动作可在这两个钩子里面 例如 销毁定时器等等
activated() {
this.start();
console.log("student挂载了");
},
deactivated() {
clearInterval(this.timer);
console.log("student销毁了");
}
第十一个钩子$nextTick
watch: {
isShow(val) {
if (val) {
// $nextTick 第十一个钩子,在等DOM渲染出来以后再执行对应的方法,类似于定时器,但是如果DOM不渲染他不会执行
this.$nextTick(() => {
this.$refs.inp.focus();
// console.log(val);
});
}
},
},
配置代理服务器跨域
module.exports = defineConfig({
transpileDependencies: true,
lintOnSave: false,
//开启代理服务器(方式一) 只写端口号跨域 和服务器地址一样
// devServer: {
// // proxy: 'http://localhost:8848',
// // proxy: 'http://localhost:8888'
// },
//开启代理服务器(方式二) 添加多个代理
devServer: {
proxy: {
//请求前缀 配置多个代理服务器要加前缀
'/car': {
// 代理地址
target: 'http://localhost:8848',
//忽略前缀 替换成空 必须加
pathRewrite: { '^/car': '' },
ws: true, //用于支持websocket
// secure: false, // 如果是https接口,需要配置这个参数
// changeOrigin: true // 用于控制请求头中额host值 为真 骗取真实服务器请求来自于那里? 默认就是true
},
'/phone': { //请求前缀
target: 'http://localhost:8888',
pathRewrite: { '^/phone': '' }, //忽略前缀 必须加
ws: true, //用于支持websocket
// changeOrigin: true // 用于控制请求头中额host值 为真 骗取真实服务器请求来自于那里?
}
}
}
})
单个代理用法: 直接写路由
// axios.get("/list").then(({ data }) => {
// this.cars = data;
// });
多个代理用法:必须加前缀
axios.get("/car/list").then(({ data }) => {
this.cars = data;
});
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">
<link rel="stylesheet" href="//at.alicdn.com/t/font_3177501_1q8vo20s883.css">
<title>Document</title>
<script src="../js/vue.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.24.0/axios.min.js"></script>
<style>
#app {
position: relative;
width: 800px;
margin: 0 auto;
}
img {
width: 100%;
}
.left,
.right {
width: 60px;
height: 80px;
background-color: rgba(227, 245, 168, 0.212);
font-size: 30px;
color: aqua;
text-align: center;
line-height: 80px;
position: absolute;
top: 30%;
cursor: pointer;
}
.left {
top: 0;
bottom: 0;
margin: auto;
left: 0px;
user-select: none;
}
.right {
top: 0;
bottom: 0;
margin: auto;
right: 0px;
user-select: none;
}
.dot ul {
width: 100%;
display: flex;
justify-content: center;
position: absolute;
bottom: 10px;
}
.dot ul li {
list-style: none;
width: 10px;
height: 10px;
background-color: #ccc;
border-radius: 50%;
margin: 0 5px;
cursor: pointer;
}
.dot ul li.active {
background-color: tomato;
}
</style>
</head>
<div id="app">
<!-- 图片 -->
<img :src="imgs[index1]" @mouseenter="stop" @mouseleave="start">
<!-- 左右切换按钮 -->
<div class="left" @click="prev"><</div>
<div class="right" @click="next">></div>
<!-- 点 -->
<div class="dot">
<ul>
<!-- 点的长度取决于图片的数量 高亮跟随 -->
<li v-for="(item,index) in imgs" :class="{active:index===index1}"></li>
<li v-for="(item,index) in imgs" :class="[index===index1?'active':'']" @click="dotClick(index)"></li>
</ul>
</div>
</div>
<body>
<script>
new Vue({
el: '#app',
data: {
// 定义图片的索引
index1: 0,
// 定时器开关
timer: null,
// 图片数据 axios请求
imgs: [
]
},
methods: {
// 向左方法
prev() {
if (--this.index1 <= 0) {
// 向左到第一张后返回第五张循环播放
this.index1 = this.imgs.length - 1
}
},
// 向右
next() {
if (++this.index1 >= this.imgs.length) {
// 向右到最后一张后返回第1张循环播放
this.index1 = 0
}
},
// 点点高亮的点击事件
dotClick(index) {
this.index1 = index
},
// 鼠标悬停方法
stop() {
clearInterval(this.timer)
},
// 鼠标离开开始播放
start() {
this.timer = setInterval(() => {
if (++this.index1 >= this.imgs.length) {
this.index1 = 0
}
}, 1000);
}
},
// 数据代理完成 在代理完成后发送axios请求
created() {
axios.get('./img.json').then(r => {
console.log(r);
let {
data
} = r
console.log(data);
this.imgs = data
})
},
// 挂载完成
mounted() {
this.timer = setInterval(() => {
if (++this.index1 >= this.imgs.length) {
this.index1 = 0
}
}, 3000);
},
/* // 销毁之前
beforeDestroy() {
clearInterval(this.timer)
}, */
})
</script>
</body>
</html>
#计算属性
computed: {
// 简写
/* fullName1() {
console.log('计算属性被调用了!');
return this.firstName + '.' + this.lastName
} */
// 完整写法 计算属性一般是只读的,如果要可写得用完整写法,采用对象的形式
fullName1: {
// 用于返回计算属性的值
get() {
return this.firstName + '.' + this.lastName
},
// 用于重新计算属性的值
set(val) {
let arr = val.split('.')
this.firstName = arr[0]
this.lastName = arr[1]
}
}
}
侦听器
<body>
<!--
computed和watch之间的区别:
1.computed:将数据经过计算后返回对应的结果,因为计算属性用到的数据发生改变后会重新执行,所以computed具备一定的侦听能力。它能完成的功能,watch都可以完成。
2.watch:要侦听哪个属性就以该属性key定义方法 两个参数,新值和旧值,该方法只是纯粹的监听指定的属性是否发生变化,如果变化了就执行对应的侦听方法。watch能完成的功能,computed不一定能完成,例如:watch可以进行异步操作。
总结: 如果一个数据发生变化,需要重新返回一个结果,用computed 而需要重新执行某些程序,用watch
两个重要的原则
1.所被Vue管理的函数,最好写成普通函数,这样this的指向才是vm 或 组件实例对象。
2.所有不被Vue所管理的函数(定时器的回调函数、ajax的回调函数等、Promise的回调函数),最好写成箭头函数,
这样this的指向才是vm 或 组件实例对象。
-->
<div id="app">
<p>姓名:{{name}} <button @click="name+='@'">修改姓名</button></p>
<p>年龄:{{age}} <button @click="setAge">修改年龄</button></p>
<div v-if="isShow">22岁显示(methods)</div>
<div v-if="isShow2">25岁显示(computed)</div>
<div v-if="isShow3">30岁显示(watch)</div>
<hr> {{emp}} <br>
<button @clocl="emp = {}">替换整个对象</button>
<button @click="emp.name+='@'">修改姓名</button>
<button @click="emp.age--">修改年龄</button>
<button @click="emp.car.carPrice++">修改汽车价格</button>
</div>
<script>
Vue.config.productionTip = false
new Vue({
el: '#app',
data() {
return {
name: '李欢',
age: 19,
isShow: false,
isShow3: false,
emp: {
name: '秦玉姣',
age: 30,
car: {
carName: '大众',
carPrice: 25
}
}
}
},
methods: {
setAge() {
if (++this.age >= 22) this.isShow = true
}
},
// computed: 将数据经过计算后返回对应的结果, 因为计算属性用到的数据发生改变后会重新执行,所以computed具备一定的侦听能力
computed: {
isShow2() {
return this.age >= 25
}
},
// watch:要侦听哪个属性就以该属性key定义方法 两个参数,新值和旧值,该方法只是纯粹的监听指定的属性是否发生变化,如果变化了就执行对应的侦听方法
watch: {
// 轻度监视
age(newVal, oldVal) {
if (newVal >= 30) {
this.isShow3 = true
}
},
// 深度监视 以配置对象的形式
emp: {
// immediate: true 渲染完成立即执行一次
immediate: true,
// 监视对象的某个属性发生变化要开启deep: true
// deep: true,
handler(n, o) {
// 监视对象只传一参
console.log(n, o);
}
}
}
})
</script>
</body>
过滤器
局部过滤器
<body>
<div id="app">
<!-- 同时使用全局和局部过滤器,以局部过滤器为准 -->
<h2>{{price1 | toFixed2}} </h2>
<h2>{{price2 | toFixed | ceil}} </h2>
<h2>{{price3 | toFixed | ceil}} </h2>
<h2>{{price4 | toFixed | ceil}}</h2>
<!-- 输入框动态绑定值不需要加括号 -->
<span><input type="text" :value="price1 | toFixed2 "></span>
<hr>
<p>{{msg}}</p>
<p>{{msg | reverse}}</p>
<p>{{birthday}}</p>
<p>{{birthday | formatDate}}</p>
</div>
<script src="../js/vue.js"></script>
<script src="./全局过滤器.js"></script>
<script>
// 关闭生产提示
Vue.config.productionTip = false
/* // 全局过滤器 第一个参数是名字 后面是回调函数
Vue.filter('toFixed2', function(val) {
return val.toFixed(3)
})
Vue.filter('reverse', function(val) {
return val.split('').reverse().join('')
})
Vue.filter('formatDate', function(val) {
let year = val.getFullYear()
let month = val.getMonth() + 1
let day = val.getDate()
return [year, month, day].join('---')
}) */
new Vue({
el: '#app',
data() {
return {
price1: 1.11111,
price2: 2.222,
price3: 3.5555555,
price4: 4.9899,
msg: 'HelloWrold',
// new Date 要加单引号
birthday: new Date('1992-05-28')
}
},
// 局部过滤器
filters: {
toFixed(val) {
return val.toFixed(2)
},
ceil(val) {
return Math.ceil(val)
},
}
})
</script>
</body>
全局过滤器
要在组件中引入
// 全局过滤器 第一个参数是名字 后面是回调函数
Vue.filter('toFixed2', function (val) {
return val.toFixed(3)
})
Vue.filter('reverse', function (val) {
return val.split('').reverse().join('')
})
Vue.filter('formatDate', function (val) {
let year = val.getFullYear()
let month = val.getMonth() + 1
let day = val.getDate()
console.log([year, month, day].join('-'));
return [year, month, day].join('-')
})
v-model
用法
<body>
<div id="app">
<!--
lazy - 取代 input 监听 change 事件
number - 输入字符串转为有效的数字
trim - 输入首尾空格过滤 -->
<!--1. v-model直接给输入框赋值 -->
<h1><input type="text" v-model.lazy="msg">{{msg}}</h1>
<hr>
<!-- 2.复选框v-model获取checkbox的布尔值 -->
<input type="checkbox" v-model="isAgree">是否同意? {{isAgree}}
<hr>
<!-- 3.单选框 v-model可以根据data中的数据默认勾选 ,然后获取value值-->
<input type="radio" value="男" v-model="sex">男
<input type="radio" value="女" v-model="sex">女 {{sex}}
<hr>
<!-- 4. 多选框 v-model获取值生成到数组 写了value获取到是value的值-->
<input type="checkbox" value="抽烟" v-model="hobby">北京
<input type="checkbox" value="喝酒" v-model="hobby">上海
<input type="checkbox" value="烫头" v-model="hobby">运城
<input type="checkbox" value="洗澡" v-model="hobby">临汾 <br> {{hobby}}
<hr>
<!-- 5. 单选下拉框 v-model可以获取下拉框的值 不写value获取到是文本的值-->
<select v-model="address">
<option>北京</option>
<option>上海</option>
<option>运城</option>
<option>临汾</option>
</select>
<br>{{address}}
<br>
<!-- 6. 多选下拉框 v-model可以获取下拉框的值 不写value获取到是文本的值-->
<select v-model="address1" multiple>
<option>北京</option>
<option>上海</option>
<option>运城</option>
<option>临汾</option>
</select>
<br>{{address}}
</div>
<script>
new Vue({
el: '#app',
data() {
return {
msg: '你好啊',
isAgree: true,
sex: '男',
hobby: ['喝酒', '烫头'],
address: '北京',
address1: ['北京', '上海']
}
},
})
</script>
</body>
案例
<body>
<div id="app">
<h1>口碑信息统计表</h1>
<table>
<tr>
<td>姓名:</td>
<td> <input type="text" v-model.lazy="kb.name" placeholder="请输入姓名"></td>
</tr>
<tr>
<td>年龄:</td>
<td> <input type="text" v-model.number.lazy="kb.age" placeholder="请输入年龄"></td>
</tr>
<tr>
<td>性别:</td>
<td>
<input type="radio" v-model="kb.sex" value="男">男
<input type="radio" v-model="kb.sex" value="女">女
</td>
</tr>
<tr>
<td>手机号:</td>
<td> <input type="tel" v-model.lazy.trim.number="kb.tel" placeholder="请输入手机号"></td>
</tr>
<tr>
<td>学历:</td>
<td>
<select v-model="kb.educationBackground">
<option value="小学">小学</option>
<option value="初中">初中</option>
<option value="高中">高中</option>
<option value="大专">大专</option>
<option value="本科">本科</option>
</select>
</td>
</tr>
<tr>
<td>意向:</td>
<td>
<input type="checkbox" v-model="kb.purpose" value="UI">UI
<input type="checkbox" v-model="kb.purpose" value="软件测试">软件测试
<input type="checkbox" v-model="kb.purpose" value="web前端">web前端
<input type="checkbox" v-model="kb.purpose" value="JAVA后端">JAVA后端
<input type="checkbox" v-model="kb.purpose" value="云计算">云计算
<input type="checkbox" v-model="kb.purpose" value="大数据">大数据
</td>
</tr>
<tr>
<td>给谁:</td>
<td>
<input v-model="kb.toWho" type="radio" value="秦老师">秦老师
<input v-model="kb.toWho" type="radio" value="顾老师">顾老师
<input v-model="kb.toWho" type="radio" value="班主任">班主任
<input v-model="kb.toWho" type="radio" value="就业老师">就业老师
</td>
</tr>
<tr>
<td colspan="3">
<div style="color: pink; font-size: 20px;" v-if="kb.toWho==='秦老师'">爱你呦亲爱的!</div>
<div style="color: black; font-size: 20px;" v-if="kb.toWho==='顾老师'">教员提供终生技术指导</div>
<div style="color: red; font-size: 20px;" v-if="kb.toWho==='班主任'">谢谢宝子们!</div>
<div style="color: green; font-size: 20px;" v-if="kb.toWho==='就业老师'">包你高薪就业!</div>
</td>
</tr>
<tr>
<td></td>
<td> <input type="checkbox" v-model="kb.isOk">是否同意
<button :disabled="!kb.isOk">提交</button>
</td>
</tr>
</table>
{{kb}}
</div>
<!-- 引入vue -->
<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.24.0/axios.min.js"></script>
<!-- 引入axios -->
<script src='https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.js'></script>
<script>
// 关闭生产提示
Vue.config.productionTip = false
new Vue({
el: '#app',
data() {
return {
kb: {
name: '',
age: '',
sex: '男',
tel: '',
educationBackground: '大专',
purpose: [],
toWho: '',
isOk: false
}
}
}
})
</script>
</body>
v2购物车
<body>
<div id="app">
<table v-cloak>
<!-- 表头 -->
<thead>
<tr>
<th> <input type="checkbox" v-model="isCkAll">全选</th>
<th style="width: 60px; height: 50px;">名称</th>
<th>图片</th>
<th style="width: 60px;">单价</th>
<th style="width: 100px;">数量</th>
<th style="width: 60px;">小计</th>
<th style="width: 60px;">操作</th>
</tr>
</thead>
<!-- 主体 -->
<tbody v-if="this.goods.length>0">
<tr v-for="(g, index) in goods" :key="g.id">
<td> <input type="checkbox" v-model="g.isCk"></td>
<td>{{g.name}}</td>
<td>
<img :src="g.pic">
</td>
<td>{{g.price | toRMB}}</td>
<td>
<button @click="g.count--" :disabled="g.count===1">-</button>
<input type="text" class="count" v-model.number="g.count" readonly>
<button @click=" jiaPrice(index)">+</button>
</td>
<td>{{g.price*g.count | toRMB}}</td>
<td>
<a href="javascript:;" @click="del">删除</a>
</td>
</tr>
</tbody>
<tbody v-else>
<tr>
<td colspan="7">
<img class="del" src="http://www.ykyao.com/postsystem/docroot/images/kindeditor/image/20160406/2016040615404366627.png">
</td>
</tr>
</tbody>
<!-- 尾部 -->
<tfoot>
<td colspan="7" style="text-align: right;">
<span>总价:{{totalPrice | toRMB}}</span>
<span></span>
</td>
</tfoot>
</table>
</div>
<!-- 引入vue -->
<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.24.0/axios.min.js"></script>
<!-- 引入axios -->
<script src='https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.js'></script>
<script>
// 关闭生产提示
Vue.config.productionTip = false
new Vue({
el: '#app',
data() {
return {
goods: [{
id: 1,
name: '手表',
pic: 'https://img13.360buyimg.com/n7/jfs/t1/90088/17/20462/75026/61d1b2e7Efd207486/4723905806de1f2c.jpg',
price: 2199,
count: 1,
isCk: false
}, {
id: 2,
name: '手机',
pic: 'https://img13.360buyimg.com/n7/jfs/t1/155183/24/7208/46178/5fbe0446E88417894/63cd6bffac98185d.jpg',
price: 3999,
count: 1,
isCk: true
}, {
id: 3,
name: '电视',
pic: 'https://img10.360buyimg.com/n7/jfs/t1/200000/38/15689/51980/6182619eE5044433f/22a173da97a3d92e.jpg',
price: 3499,
count: 1,
isCk: true
}, {
id: 4,
name: '冰箱',
pic: 'https://img10.360buyimg.com/n7/jfs/t1/149908/15/2773/53205/5f0ad38bEeeedb207/aefa4ba990cbf08d.jpg',
price: 2999,
count: 1,
isCk: false
}, ]
}
},
methods: {
// 加
jiaPrice(index) {
if (this.goods[index].count >= 9) {
alert('最多购买9件')
} else {
this.goods[index].count++
}
},
// 删除
del(index) {
// if (!confirm('确定删除吗?')) return
this.goods.splice(index, 1)
}
},
filters: {
toRMB(val) {
return '¥' + val.toFixed(2)
}
},
computed: {
// 判断是否全选
isCkAll: {
get() {
// 每一项被选就全选
return this.goods.length > 0 && this.goods.every(r => r.isCk)
},
set(val) {
// val是isCkAll的值
this.goods.forEach(r => r.isCk = val)
}
},
totalPrice() {
// let sum = 0
// this.goods.filter(r => r.isCk).map(r => {
// sum += r.price * r.count
// })
// return sum
return this.goods.filter(r => r.isCk).map(r => r.price * r.count).reduce((a, b) => {
return a + b
}, 0)
}
}
})
</script>
</body>
深度响应式
Vue监视数据的原理:
1. vue会监视data中所有层次的数据。
2. 如何监测对象中的数据?
通过setter实现监视,且要在new Vue时就传入要监测的数据。
(1).对象中后追加的属性,Vue默认不做响应式处理
(2).如需给后添加的属性做响应式,请使用如下API:
Vue.set(target,propertyName/index,value) 或
vm.$set(target,propertyName/index,value)
3. 如何监测数组中的数据?
通过包裹数组更新元素的方法实现,本质就是做了两件事:
(1).调用原生对应的方法对数组进行更新。
(2).重新解析模板,进而更新页面。
4.在Vue修改数组中的某个元素一定要用如下方法:
1.使用这些API:push()、pop()、shift()、unshift()、splice()、sort()、reverse()
2.Vue.set() 或 vm.$set()
特别注意:Vue.set() 和 vm.$set() 不能给vm 或 vm的根数据对象 添加属性!!!
插槽
普通插槽
<body>
<div id="app" v-cloak>
<c-car :title="bsj.title" :content="bsj.content">
</c-car>
</div>
<!-- 引入vue -->
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
<script>
Vue.component('c-car', {
template: `
<div class="box">
<h2>{{title}}</h2>
<slot></slot>
<div>{{content}}</div>
</div>
`,
props: ['title', 'content']
})
// 关闭生产提示
Vue.config.productionTip = false
new Vue({
el: '#app',
data() {
return {
bsj: {
title: '保时捷',
content: '必须买一辆'
}
}
},
})
</script>
</body>
具名插槽
<body>
<div id="app" v-cloak>
<a-box>
<!-- 在组件内部写 <template></template>标签,在标签内部写上要放到哪个插槽 v-slot 简写为# -->
<template #header>
<h2>保时捷</h2>
</template>
<template #content>
</template>
<template #footer>
<div>100万</div>
</template>
</a-box>
</div>
<!-- 引入vue -->
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
<script>
Vue.component('a-box', {
template: `
<div class="box">
<div class="header"><slot name="header"></slot></div>
<div class="content"><slot name="content"></slot></div>
<div class="footer"><slot name="footer"></slot></div>
</div>
`,
})
</script>
</body>
作用域插槽
<body>
<div id="app" v-cloak>
<b-box>
<!-- 2.具名插槽接收到组件传出来的数据scope,通过scope对数据进行增删改查 -->
<template #btn="scope">
<button @click="scope.list[scope.index].price++">修改价格</button>
<button @click="scope.list.splice(scope.index,1)">行内删除</button>
<!-- 3.如果要实现更复杂的业务,需要单独定义方法,在vue实例中定义,将整个scope传出去 -->
<button @click="del(scope)">方法删除</button>
</template>
</b-box>
</div>
<!-- 引入vue -->
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
<script>
Vue.component('b-box', {
// 1.作用域插槽必须是具名插槽 slot定义插槽名字。 将索引和列表传出去
template: `
<div class="box">
<ul>
<li v-for="(item, index) in list" :key="index">
<span>{{item.id}}--{{item.name}}--{{item.price}} </span>
<slot name="btn" :index="index" :list="list"></slot>
</li>
</ul>
</div>
`,
data() {
return {
list: [{
id: 1,
name: '奔驰',
price: 100
}, {
id: 2,
name: '宝马',
price: 120
}, {
id: 3,
name: '奥迪',
price: 150
}]
}
},
})
// 关闭生产提示
Vue.config.productionTip = false
new Vue({
el: '#app',
data() {
return {}
},
mounted() {},
methods: {
del(scope) {
if (confirm('确定删除吗?'))
scope.list.splice(scope.index, 1)
}
},
computed: {},
watch: {}
})
</script>
</body>
自定义轮播组件
<!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">
<link rel="stylesheet" href="//at.alicdn.com/t/font_3177501_1q8vo20s883.css">
<title>Document</title>
<style>
#app {
position: relative;
width: 800px;
margin: 0 auto;
}
.lunbo {
position: relative;
}
img {
width: 100%;
}
.left,
.right {
width: 60px;
height: 80px;
background-color: rgba(227, 245, 168, 0.212);
font-size: 30px;
color: aqua;
text-align: center;
line-height: 80px;
position: absolute;
top: 30%;
cursor: pointer;
}
.lunbo .left {
top: 0;
bottom: 0;
margin: auto;
left: 0px;
user-select: none;
}
.lunbo .right {
top: 0;
bottom: 0;
margin: auto;
right: 0px;
user-select: none;
}
.dot ul {
width: 100%;
display: flex;
justify-content: center;
position: absolute;
bottom: 10px;
}
.dot ul li {
list-style: none;
width: 10px;
height: 10px;
background-color: #ccc;
border-radius: 50%;
margin: 0 5px;
cursor: pointer;
}
.dot ul li.active {
background-color: tomato;
}
</style>
</head>
<div id="app">
<!-- :index1="index1" 控制图片索引 非必传
:imgs="imgs" 控制图片数据 必传
:lunbotime="lunbotime" 控制轮播间隔时间 非必传 -->
<b-lunbo :imgs="imgs"></b-lunbo>
</div>
<body>
<!-- 引入axios -->
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<!-- 引入vue -->
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
<script>
Vue.config.productionTip = false
Vue.component('b-lunbo', {
template: `
<div class="lunbo">
<!-- 图片 -->
<img :src="imgs[myIndex1]" @mouseenter="stop" @mouseleave="start">
<!-- 左右切换按钮 -->
<div class="left" @click="prev"><</div>
<div class="right" @click="next">></div>
<!-- 点点 -->
<div class="dot">
<ul>
<li v-for="(item,index) in imgs" :class="[index===myIndex1?'active':'']" @click="dotClick(index)"></li>
</ul>
</div>
</div>
`,
props: {
index1: {
type: Number,
default: 0
},
imgs: {
type: Array,
},
// 轮播时间默认3000
lunbotime: {
type: Number,
default: 3000
}
},
data() {
return {
// 定时器开关
timer: null,
// 备份
myIndex1: this.index1,
myLunboTime: this.lunbotime
}
},
methods: {
// 向左方法
prev() {
if (--this.myIndex1 <= 0) {
// 向左到第一张后返回第五张循环播放
this.myIndex1 = this.imgs.length - 1
}
},
// 向右
next() {
if (++this.myIndex1 >= this.imgs.length) {
// 向右到最后一张后返回第1张循环播放
this.myIndex1 = 0
}
},
// 鼠标悬停方法
stop() {
// 清除定时器停止播放
clearInterval(this.timer)
},
// 圆点的点击事件
dotClick(index) {
this.myIndex1 = index
},
// 定时器
start() {
this.timer = setInterval(() => {
if (++this.myIndex1 >= this.imgs.length) {
this.myIndex1 = 0
}
}, this.myLunboTime);
}
},
// 挂载完成后开始播放
mounted() {
this.start()
},
})
new Vue({
el: '#app',
data: {
// 控制轮播时间
lunboTime: 1000,
// 图片数据 axios请求
imgs: []
},
methods: {
// 请求数据方法 赋值给imgs
async getImg() {
let {
data
} = await axios.get('./img.json')
this.imgs = data
}
},
// 挂载完成
mounted() {
// 请求数据
this.getImg()
},
})
</script>
</body>
render函数
new Vue({
//在工程化开发的环境中,引入的模板是不完整的,不带解析模板的
//原因是为了减少文件体积,不带解析模板200kb,带模板300kb
// 直接导入vue会报错,要求使用render函数,可以在vue的package里面找到入口和出口文件,改成完整版的vue
el: "#app2",
// 如果手动添加了render函数,vue会以render函数为主,而不去使用el选项
//reder函数会替换掉html容器里面的内容
// 给vue换个容器 改成app2
render: function(h) {
return h('h1', this.name + '-----' + this.age)
},
data() {
return {
name: '李欢',
age: 18
}
},
})