一、vue(上)
1.vue开始
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>vueStart</title>
</head>
<body>
<div id="app"></div>
<!-- 引入vue的库 -->
<script src="https://unpkg.com/vue@next"></script>
<script>
//创建vue应用
const app = Vue.createApp({
data(){
return{
message:'hello vue'
}
},
template:`<div>{{message}}</div>`
});
//挂载
//vm就是vue的根组件
const vm = app.mount('#app');
</script>
</body>
</html>
2.先了解一点点
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title></title>
</head>
<body>
<div id="app">先了解一点点</div>
<script src="https://unpkg.com/vue@next"></script>
<script>
/**
* vue 面向数据编程
*/
const app = Vue.createApp({
data(){
return{
content: 1
}
},
//挂载完成执行
mounted(){
setInterval(()=>{
this.content += 1;
},1000)
},
template:'<div>{{content}}</div>'
});
app.mount('#app');
</script>
</body>
</html>
3.基础操作
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>基础操作</title>
</head>
<body>
<div id="app"></div>
<script src="https://unpkg.com/vue@next"></script>
<script>
// Vue.createApp({
// data(){
// return{
// content:'hello'
// }
// },
// methods:{
// handleBtnClick(){
// this.content = this.content.split('').reverse().join('');
// }
// },
// template:`
// <div>
// {{content}}
// <button @click="handleBtnClick">反过来打印</button>
// </div>`
// }).mount("#app");
/**
* 显示/隐藏
*
*/
Vue.createApp({
data(){
return{
show: true,
}
},
methods:{
handleBtnClick(){
this.show = false;
// this.show = !this.false;
}
},
template:`
<div>
<span v-if="show">内容</span>
<button @click="handleBtnClick">显示/隐藏</button>
</div>`
}).mount("#app");
</script>
</body>
</html>
4. 循环和双向绑定
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>循环和双向绑定</title>
</head>
<body>
<div id="app"></div>
<script src="https://unpkg.com/vue@next"></script>
<script>
Vue.createApp({
data(){
return{
inputVal:'',
list:[]
}
},
methods:{
hanleAddItem(){
this.list.push(this.inputVal);
this.inputVal = ''
}
},
template:`
<div>
<input v-model="inputeVal" />
<button @click="hanleAddItem">添加数据 </button>
<ul>
<li v-for="item of list">{{item}}</li>
</ul>
</div>`
}).mount("#app");
</script>
</body>
</html>
5.组件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>组件</title>
</head>
<body>
<div id="app"></div>
<script src="https://unpkg.com/vue@next"></script>
<script>
/**
* 数据双向绑定(同步更新) v-model
* 数据与属性绑定 :title = 'valueName'
*/
const app = Vue.createApp({
data(){
return{
inputVal:'',
list:[]
}
},
methods:{
hanleAddItem(){
this.list.push(this.inputVal);
this.inputVal = ''
}
},
template:`
<div>
<input v-model="inputeVal" />
<button @click="hanleAddItem" :title="inputVal">添加数据 </button>
<ul>
<todo-item
v-for="(item,index) of list"
:content="item"
:index="index"
/>
</ul>
</div>`
})
//注册组件(只有注册才能使用)
app.component('todo-item',{
props:['content','index'], //props用来接收父组件绑定的属性值
template:`<li>{{content}}-{{index}}</li>`
});
/**
* 组件之间可以通过绑定属性的方式传值
* 接收值用props['srcName1','srcName2']
*/
app.mount("#app");
</script>
</body>
</html>
6.稍微扩展一点
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>扩展</title>
</head>
<body>
<div id="app"></div>
<script src="https://unpkg.com/vue@next"></script>
<script>
/**
* vue其实是参考了mvvm
* m-->model数据,v-->view视图,vm-->viewModel视图数据连接层
*/
const app = Vue.createApp({
data(){
return{
message:'hello'
}
},
template:`<div>{{message}}</div>`
});
//vm就是vue的根组件
const vm = app.mount('#app');
//获取根组件上的数据:vm.$data.
</script>
</body>
</html>
7. 常用的生命周期函数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>常用的生命周期函数</title>
</head>
<body>
<div id="app"></div>
<script src="https://unpkg.com/vue@next"></script>
<script>
/**
* 生命周期函数:在某一时刻会自动执行的函数
* 生命周期函数有很多,常用的就下面四个,其他可到vue官网了了解
*/
const app = Vue.createApp({
data(){
return{
message:'hello'
}
},
//vue应用创建之前执行
beforeCreate(){
alert('beforeCreate');
},
//vue应用创建完成执行
created(){
alert('created');
},
//挂载完成之前执行
beforeMounted(){
alert('beforeMounted');
},
//挂载完成后执行
mounted(){
alert('mounted');
},
template:`<div>{{message}}</div>`
});
app.mount('#app');
</script>
</body>
</html>
</html>
8.常用模板语法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>常用模板语法</title>
</head>
<body>
<div id="app"></div>
<script src="https://unpkg.com/vue@next"></script>
<script>
/**
* 常用模板语法
* 差值表达式:{{}}
* 转义:v-html
* 绑定属性: v-bing 或 : (绑定标题等)
* v-noce:<div v-once>内容</div>-->div里的内容只使用一次,即使改变内容也改变不了
* v-if: v-if="show":data里的show的状态
* 事件绑定:v-on 或 @
* 修饰符:prevent-->阻止默认行为:@click.prevent="handleClick";
* 数据绑定会涉及到修饰符,先了解一下,后面还会提到,如
* 数字类型:v-model.nunber=""
* 失去焦点才触发数据的双向绑定:v-model.lazy=""
* 输入框首位去空格:v-model.trim=""
*/
const app = Vue.createApp({
data(){
return{
message:<strong>hello</strong>
}
},
template:`<div v-html='message'></div>`
});
app.mount('#app');
</script>
</body>
</html>
</html>
9.数据.方法.计算属性和侦听器
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>数据.方法.计算属性和侦听器</title>
</head>
<body>
<div id="app"></div>
<script src="https://unpkg.com/vue@next"></script>
<script>
/**
* data:数据
* methods:方法
* computed:计算属性
* watcher:侦听器
* this指向data里的数据
*/
const app = Vue.createApp({
data(){
return{
message:'hello',
count: 2,
proce: 5,
}
},
//watch监听数据有变化后打印处内容(异步)
//异步处理,其他如computed等是不能的
watch:{
price(current, prev){ //current:接收的是当前的值,prev:接收的是之前的值
setTimeout(()=>{
console.log('数据变化2秒后的输出结果')
},2000)
}
},
computed:{
total(){
return this.count * this.price
}
},
/**
* 虽然方法也可以实现计算,但方法里的计算方法是会
* 随着页面重新架加载重新执行,而计算属性里的函数
* 则不会,会缓存数据,执行效率更高
*/
methods:{
getTotal(){
return this.count * this.price
}
},
template:`<div>{{total}}</div>`
});
app.mount('#app');
</script>
</body>
</html>
</html>
10.样式绑定语法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>样式绑定语法</title>
<style>
.red{
color:red;
}
.green{
color:green;
}
</style>
</head>
<body>
<div id="app"></div>
<script src="https://unpkg.com/vue@next"></script>
<script>
const app = Vue.createApp({
data(){
return{
classString: 'red',
classObject:{ red: true, green: true },
classArray:['red', 'green'],
//行内样式绑定
styleString: 'color:blue',
//推荐
styleObject:{
color:'pink',
background:'yellow'
}
}
},
template:`<div :style='styleObject'>
hello
<demo class='green' />
</div>`
});
app.component('demo',{
template:`<div :class='$attrs.class'>子组件的内容</div>`
})
app.mount('#app');
</script>
</body>
</html>
</html>
11.条件渲染
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>条件渲染</title>
</head>
<body>
<div id="app"></div>
<script src="https://unpkg.com/vue@next"></script>
<script>
/**
* v-if:直接去除
* v-show:把display:none
* 频繁改变状态用v-show
* v-if可以结合v-else-id,v-else使用
*/
const app = Vue.createApp({
data(){
return{
show:true,
conditionOne: true,
conditionTwo: true
}
},
template:`
<div v-if="show">hello</div>
<div v-if="conditionOne">hello</div>
<div v-else-if="conditionTwo">hello-1</div>
<div v-else>world</div>
<div v-show="show">world</div>
`
});
app.mount('#app');
</script>
</body>
</html>
</html>
12.列表渲染
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>列表渲染</title>
</head>
<body>
<div id="app"></div>
<script src="https://unpkg.com/vue@next"></script>
<script>
const app = Vue.createApp({
data(){
return{
listArray:['xiaoming','xiaohong','xiaojun'],
listObject:{
firstName:'xiaoming',
lastName:'xiaohong',
job:'student'
}
}
},
methods:{
handleAddBtnClick(){
//1.使用数组的方法
this.listArray.push('xiaoli');
//从末尾删除元素
//this.listArray.pop();
//从开头删除元素
//this.listArray.shift();
//从开头新增元素
//this.listArray.unshift('xiaoqiang');
//取反
//this.listArray.reverse();
//过滤
//this.listArray = ['xioayan','xiaohua'].filter(item=>item === 'xiaoyan')
//2.直接替换
//this.listArray = ['xiaoyan','xiaoyang']
//3.直接更新数组的内容
//this.listArray[0] = 'xiaoyan'
//添加对象的内容
this.listObject.age = 20,
this.listObject.sex = '男'
}
},
/**
* 在循环渲染的时候,为了减少性能消耗最好加一个:key="index"(唯一的key值)
*/
template:`
<div>
<div v-for="(item,index) in listArray" :key="index">{{item}}-{{index}}</div>
<div v-for="(value,key,index) in listObject">{{value}}-{{key}}-{{index}}</div>
<button @click="handleAddBtnClick">新增</button>
</div>
`
});
app.mount('#app');
</script>
</body>
</html>
</html>
13.事件绑定&修饰符
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>事件绑定&修饰符</title>
</head>
<body>
<div id="app"></div>
<script src="https://unpkg.com/vue@next"></script>
<script>
/**
* 1.事件绑定中常用的事件修饰符
* @click.stop="handleEventName":阻止事件冒泡
* @click.self="handleEventName":如果盒子内还有其他点击事件,只有点击自己才会执行
* @click.prevent="handleEventName":阻止默认行为
* @click.capture="handleEventName":设置为捕获状态(即从外到内)
* @click.once="handleEventName":只执行一次
* 2.事件绑定中的按键修饰符(指定是那一个按键)
* @keydown.enter = "handleKeyDown":输入的时候只有按回车才会执行
* tab,delete,shift等
* 3.事件绑定中的鼠标修饰符
* left,right,middle
* @click.left="handleEventName"
* 3.事件绑定中的精确(精确到按住哪一个键,点击时才会执行)修饰符
* @click.alt="handleEventName"等
*/
const app = Vue.createApp({
data(){
return{
count:0,
}
},
methods:{
handleBtnClick(num,event){
this.count += num;
}
},
template:`
<div>
{{count}}
<button @click='handleBtnClick(2,$event)'></button>
</div>`
});
app.mount('#app');
</script>
</body>
</html>
</html>
14.表单中双向绑定指令的使用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>表单中双向绑定指令的使用</title>
</head>
<body>
<div id="app"></div>
<script src="https://unpkg.com/vue@next"></script>
<script>
/**
* 表单中的修饰符
* v-model.lazy="message":输入框失去焦点时数据才同步
* v-model.number="message":规定只能数据数字类型
* v-model.trim="message":去除输入框前后的空格
*/
const app = Vue.createApp({
data(){
return{
//message:'hello'
//message:[]
//message:''
message:'A'
}
},
// template:`
// <div>
// {{message}}
// <input v-model="message" />
// <textarea v-model="message" />
// </div>`
// template:`
// <div>
// {{message}}
// xiaoming:<input type="checkbox" v-model="message" value="xiaoming" />
// xiaohong:<input type="checkbox" v-model="message" value="xiaohong" />
// xiaoyang:<input type="checkbox" v-model="message" value="xiaoyang" />
// </div>`
// template:`
// <div>
// {{message}}
// xiaoming:<input type="radio" v-model="message" value="xiaoming" />
// xiaohong:<input type="radio" v-model="message" value="xiaohong" />
// xiaoyang:<input type="radio" v-model="message" value="xiaoyang" />
// </div>`
template:`
<div>
{{message}}
<select v-model="message">
<option>A</option>
<option>B</option>
<option>C</option>
</select>
</div>`
});
app.mount('#app');
</script>
</body>
</html>
</html>
15.vue基础篇小案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>vue基础综合案例</title>
</head>
<body>
<div id="app">
<h1>我的购物车</h1>
<form @submit.prevent="insert">
<input
type="text"
placeholder="商品名称"
v-model="name"
>
<input
type="text"
placeholder="商品价格"
v-model.number="price"
>
<button>添加商品</button>
</form>
<ul>
<li v-for="(item,index) in list">
商品名称:{{item.name}}
商品价格:{{item.price}}
商品数量:
<button @click="sub(index)">-</button>
<span>{{item.count}}</span>
<button @click="add(index)">+</button>
</li>
</ul>
<h1>总价:{{totalPrice}}</h1>
</div>
<script src="https://unpkg.com/vue@next"></script>
<script>
Vue.createApp({
data(){
return {
// list:[
// {
// name:'香蕉',
// price:0,
// count:4
// },
// {
// name:'苹果',
// price:0,
// count:4
// }
// ]
list:[]
}
},
//计算属性computed是单独的模块
computed:{
//总价计算需要一个函数才能完成
totalPrice(){
let sum = 0;
this.list.forEach(v=>{
sum+= (v.price * v.count);
})
return sum;
}
},
methods:{
insert(){
this.list.push({
//输入框输入的内容
name:this.name,
price:this.price,
//默认值是1件商品
count:1,
})
},
sub(i){
/*通过索引把具体哪个商品传进来
*然后操作它自己的数量
*/
this.list[i].count --;
if(this.list[i].count <1 &&
confirm('是否要删除该商品')){
//删除第i个删评并且只删除一个商品
this.list.splice(i,1);
}
},
add(i){
this.list[i].count ++;
},
}
}).mount("#app")
</script>
</body>
</html>
二、vue (中)
1.组件的定义
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>组件的定义</title>
</head>
<body>
<div id="app"></div>
<script src="https://unpkg.com/vue@next"></script>
<script>
const app = Vue.createApp({
/**
* 组件:简单来说组件就是页面的某个部分
* 当一个页面比较复杂的时候,就需要把组件拆分出去
*/
template: `
<div>
<hello />
<world />
</div>`,
});
/**
* 定义全局组件:app.component("组件名",{})
* 全局组件的使用:在使用组件的那个组件的模板里
* 加上 <组件名 />即可
* */
app.component('hello',{
template: `
<div>hello</div>
`,
})
app.component('world',{
template: `
<div>world</div>
`,
})
const vm = app.mount("#app");
</script>
</body>
</html>
2.全局组件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>全局组件</title>
</head>
<body>
<div id="app"></div>
<script src="https://unpkg.com/vue@next"></script>
<script>
/**
* 组件具有复用性,并且组件中的数据是独立的
* 在复用时互不影响
* 全局组件:是通过app.component创建的
* 全局组件定义之后,即使没有被使用,它也会占用app资源,
* 但全局组件使用起来比较简单。
*/
const app = Vue.createApp({
template: `
<div>
<counter-parent />
</div>`,
});
//定全局组件
app.component('counter',{
template: `
<counter />
`,
})
app.component('counter',{
data(){
return {
count:1,
}
},
methods:{
handleClick(){
this.count++;
}
},
template: `
<div @click="handleClick">{{count}}</div>
`,
})
const vm = app.mount("#app");
</script>
</body>
</html>
3. 局部组件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>局部组件</title>
</head>
<body>
<div id="app"></div>
<script src="https://unpkg.com/vue@next"></script>
<script>
/**
* 定义局部组件 const 组件名 = {};组件名建议使用驼峰命名,
* 只用在使用局部组件的组件里注册才能在模板里使用,
* 注册:components:{counter:counter},
* 布局组件性能高,但使用起来相对全局组件来说相对麻烦;
* 为了性能,开发中一般使用局部组件
* 局部组件之间的使用同理。
*/
const Counter = {
data(){
return {
count:1,
}
},
methods:{
handleClick(){
this.count++;
}
},
template: `
<div @click="handleClick">{{count}}</div>
`,
}
const newContent = {
data(){
return{
msg:"局部组件的内容,使用之前记得先在模板里注册。"
}
},
template:`
<p>{{msg}}</p>
`
}
const app = Vue.createApp({
//注册
components:{ Counter, newContent },
//使用
template: `
<div>
<counter />
<newContent />
</div>`,
});
const vm = app.mount("#app");
</script>
</body>
</html>
4.组件间的静态传值
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>组件间的传值(静态)</title>
</head>
<body>
<div id="app"></div>
<script src="https://unpkg.com/vue@next"></script>
<script>
const app = Vue.createApp({
/**
* 父组件调用子组件,父组件通过自定义属性
* 向子组件传递值,子组将通过props,来接
* 收父组件传递过来的值。
*/
template: `
<div>
<content useContent="this is useContent"></content>
</div>`,
});
app.component("content",{
/**
* 要使用根组件中的useContent
* 需要使用props
* */
props:["useContent"],
template:`<div>{{useContent}}</div>`
})
const vm = app.mount("#app");
</script>
</body>
</html>
5.组件间的动态传值
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>组件间的传值(动态)</title>
</head>
<body>
<div id="app"></div>
<script src="https://unpkg.com/vue@next"></script>
<script>
const app = Vue.createApp({
/**
* 如果父组件件要向子组件传参,就动态传
* 参,即: :userData="123",没有冒号
* 的是静态传参,即传一些字符类型的
*/
data(){
return{
num:123,
mes:"字符串",
fun:()=>{ alert("this is function") }
}
},
template: `
<div>
<content :useData="num"></content>
<newContent :useString="mes"></newContent>
<func :useFun="fun"></func>
<doType :useType="num"></doType>
</div>`,
});
//接收Number
app.component("content",{
props:["useData"],
template:`<div>{{useData}}</div>`
})
//接收String
app.component("newContent",{
/**
* 注:如果父组件传过来的的值是字符串类型
* 的数据,在props接收的时候需要以{}的形
* 式接收,并且同时要声明其类型,这样就有
* 了类型校验的作用,eg:String,boolean,
* Array,Object,function,Symbol(占位符)等。
*/
props:{ useString:String },
template:`<div>{{useString}}</div>`
})
//接收function
app.component("func",{
props:{ useFun:Function },
methods:{
handleClick(){
alert("hello");
this.useFun();
}
},
template:`<div @click="handleClick">{{useFun}}</div>`
})
//类型的校验:规定父组件传进来的必须是子组件规定的类型
app.component("doType",{
props:{
useType:{
type:Number,
required: true
}
},
template:`
<div>{{useType}}</div>
`
})
const vm = app.mount("#app");
</script>
</body>
</html>
6.单项数据流
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>单项数据流</title>
</head>
<body>
<div id="app"></div>
<script src="https://unpkg.com/vue@next"></script>
<script>
/**
* 单向数据流
* 父组件可以向子组件传递数据
* 但这个数据不能在子组件修改,
* 它是一个单边流的形式。
* 如果想修改父组件中的数据,
* 可以在子组件上重新定义数,
* 把父组件的数据当作子组件
* 的初始值即可。
*/
const app = Vue.createApp({
data(){
return{
num:1,
/**
* 当需要传递多个数据时,
* 需要把数据封装到一个
* 对象中
*/
params:{
num:123,
a:111,
b:222,
c:333
},
content:1024,
}
},
template: `
<div>
<Counter :count="num"></Counter>
<doType v-bind="params"></doType>
<Content :content="content"></Content>
</div>`,
});
app.component("Counter",{
props:["count"],
data(){
return{
newCount:this.count,
}
},
methods:{
handleClick(){
this.newCount ++;
}
},
template:`
<div @click="handleClick">{{newCount}}</div>
`
})
app.component("doType",{
props:["num","a","b","c"],
template:`
<div>{{num}}-{{a}}-{{b}}-{{c}}</div>
`
})
app.component("Content",{
props:["content"],
template:`
<div>{{content}}</div>
`
})
const vm = app.mount("#app");
</script>
</body>
</html>
7.Non-Props属性
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Non-Props属性</title>
</head>
<body>
<div id="app"></div>
<script src="https://unpkg.com/vue@next"></script>
<script>
/**
* Non-Props属性
* 在父组件给子组件传递数据的时候,
* 子组件不用props接收也可以,vue
* 底层会自动把父组件的dom附加到
* 子组件模板中。在子组件使用时用
* v-bind="$attrs"即可使用。
* No-Props主要用于样式传递的场景。
*/
const app = Vue.createApp({
template: `
<div>
<content msg="hello" style="color:red"></content>
</div>`,
});
app.component("content",{
/**
* 如果不想接收父组件的msg属性
* 直接加上inheritAttrs:false
*/
//inheriAttrs:false,
/**
* 当有多个节点时,需要用v-bind="$attrs"
* 来指定谁需要父组件的样式
*/
template:`
<div :msg="$attrs.msg">Content</div>
<div v-bind="$attrs">Content</div>
<div>Content</div>
`
})
const vm = app.mount("#app");
</script>
</body>
</html>
8.父子组件如何通过事件进行通信
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>父子组件如何通过事件进行通信</title>
</head>
<body>
<div id="app"></div>
<script src="https://unpkg.com/vue@next"></script>
<script>
const app = Vue.createApp({
data(){
return{
num:1
}
},
methods:{
handleAdd(param){
this.num += param;
}
},
/**
* 用$emite记得在父组件的模板里用 @触发事件名 来监听
* 子组件的事件
*/
template: `
<div>
<content :count="num" @add="handleAdd"/>
</div>`,
});
app.component("content",{
props:["count"],
methods:{
handleClickAdd(){
/**
* 除了前面提到的把父组件的数据赋值给
* 子组件数据的方法,可以用$emit('eventName')
* 的方法,来操作父组件的数据
*/
this.$emit("add",2);//向外触发一个事件
}
},
template:`
<div @click="handleClickAdd">{{count}}</div>
`
})
const vm = app.mount("#app");
</script>
</body>
</html>
9.插槽(slot)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>插槽(slot)</title>
</head>
<body>
<div id="app"></div>
<script src="https://unpkg.com/vue@next"></script>
<script>
/**
* 插槽(slot):其实就是把调用的组件标签变为
* 双标签往里面插内容,插入的内容就是插槽。
* 插槽的使用:
* 直接用<slot></slot>标签即可。
* 插槽的作用:把自己的需求在父组件的插槽定义
* 然后在子组件中使用,具有很好的灵活性。摆脱
* 了属性属性形式的传值。多用于父组件给子组件
* 传dom节点的场景,eg:div标签,以属性的方式
* 传显然很麻烦。
*/
const app = Vue.createApp({
data(){
return{
text:"提交"
}
},
template: `
<div>
<myForm>
<div>{{text}}</div>
</myForm>
<myForm>
<button>{{text}}</button>
</myForm>
</div>`,
});
app.component("myForm",{
methods:{
handleClick(){
alert(333);
}
},
/**
* 注:slot标签是不可以绑定事件的,
* 需要在slot外包裹一层进行事件的
* 绑定
*/
template:`
<input type="text">
<span @click="handleClick">
<slot></slot>
</span>
`
})
const vm = app.mount("#app");
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>插槽(slot)-1</title>
</head>
<body>
<div id="app"></div>
<script src="https://unpkg.com/vue@next"></script>
<script>
const app = Vue.createApp({
data(){
return{
text:'提交'
}
},
template: `
<div>
<myForm>
<div>{{text}}</div>
</myForm>
<myForm>
<button>{{text}}</button>
</myForm>
</div>`,
});
app.component("myForm",{
methods:{
handleClick(){
alert(333);
}
},
template:`
<input type="text">
<span @click="handleClick">
<slot></slot>
</span>
`
})
const vm = app.mount("#app");
</script>
</body>
</html>
10.具名插槽
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>具名插槽</title>
</head>
<body>
<div id="app"></div>
<script src="https://unpkg.com/vue@next"></script>
<script>
/**
* 具名插槽:
* 给插槽外层的template通过v-slot:插槽名的
* 形式给插槽命名,命名后该插槽就是具名插槽。
* eg:
* <template v-slot:header>
<div>header</div>
</template>
* 可简写为:
<template #header>
<div>header</div>
</template>
*/
const app = Vue.createApp({
template: `
<layout>
<template v-slot:header>
<div>header</div>
</template>
<template v-slot:footer>
<div>footer</div>
</template>
</layout>
`,
});
app.component("layout",{
template:`
<div>
<slot name="header"></slot>
<div>content</div>
<slot name="footer"></slot>
</div>
`
})
const vm = app.mount("#app");
</script>
</body>
</html>
11.作用域插槽
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>作用域插槽</title>
</head>
<body>
<div id="app"></div>
<script src="https://unpkg.com/vue@next"></script>
<script>
/**
* 作用域插槽
*/
const app = Vue.createApp({
/**
* 通过v-slot="slotProps"接收
*/
template: `
<list v-slot="slotProps">
<div>{{slotProps.item}}</div>
</list>
`,
});
app.component("list",{
data(){
return{
list:[1,2,3,4,5]
}
},
/* 通过:item="item"把item传给父组件 */
template:`
<div>
<slot v-for="item in list" :item="item"></slot>
</div>
`
})
const vm = app.mount("#app");
</script>
</body>
</html>
12.动态组件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>动态组件</title>
</head>
<body>
<div id="app"></div>
<script src="https://unpkg.com/vue@next"></script>
<script>
const app = Vue.createApp({
data(){
return{
currentItem:'input-item',
}
},
methods:{
handleClick(){
if(this.currentItem === 'input-item'){
this.currentItem = 'common-item';
}else{
this.currentItem = 'input-item';
}
}
},
/**
* 动态组件:根据数据的变化,结合component这个标签,来随时
* 动态的切换组件的显示情况。
* <component :is="currentItem" />
* 缓存标签:<keep-alive></keep-alive>
*/
template: `
<keep-alive>
<component :is="currentItem" />
</keep-alive>
<button @click="handleClick">切换</button>
`,
});
app.component("inputItem",{
template:`
<input />
`
})
app.component("commonItem",{
template:`
<div>hello</div>
`
})
const vm = app.mount("#app");
</script>
</body>
</html>
13.异步组件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>异步组件</title>
</head>
<body>
<div id="app"></div>
<script src="https://unpkg.com/vue@next"></script>
<script>
const app = Vue.createApp({
template: `
<inputItem />
<asyncCommonItem />
`,
});
app.component("inputItem",{
template:`
<input />
`
})
//异步组件的定义
app.component("asyncCommonItem",Vue.defineAsyncComponent(()=>{
return new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve({
template:`<div>这是异步组件的内容</div>`
})
},3000)
})
}));
const vm = app.mount("#app");
</script>
</body>
</html>
14.语法补充
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>补充</title>
</head>
<body>
<div id="app"></div>
<script src="https://unpkg.com/vue@next"></script>
<script>
/**
* v-once:某个元素标签只渲染一次。
* ref:想获取哪个dom标签元素,就在哪个标签上加ref="name",
* 通过this.$refs.name获取dom节点。
* provide 和 inject结合使用,用于多层组件传值的情况,
* 用provide定义数据,用inject来接收数据。
*/
const app = Vue.createApp({
data(){
return{
num:1,
content:"hello",
count:1,
}
},
//provide定义数据
provide:{
count:2,
},
//provide还可以使用data里的数据
// provide(){
// count:this.count
// },
methods:{
handleClickAdd(){
this.num ++;
}
},
mounted(){
console.log(this.$refs.domName);
},
template: `
<div>
<div @click="handleClickAdd" v-once>{{num}}</div>
<div ref="domName">{{content}}</div>
</div>
<div>
<Child :count="count" />
</div>
`,
});
app.component("Child",{
template:`<childChild />`
})
app.component("childChild",{
//inject接收数据
inject:["count"],
template:`<div>{{count}}</div>`
})
const vm = app.mount("#app");
</script>
</body>
</html>
15.vue+css实现动画
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>vue实现css动画</title>
<style>
/* 动画 */
@keyframes leftToRight {
0%{
transform:translateX(-100px);
}
50%{
transform: translateX(-50px);
}
0%{
transform: translateX(0px);
}
}
.animation{
animation:leftToRight 3s;
}
</style>
</head>
<body>
<div id="app"></div>
<script src="https://unpkg.com/vue@next"></script>
<script>
const app = Vue.createApp({
data(){
return{
animate:{
animation:false,
},
}
},
methods:{
handleClick(){
this.animate.animation = !this.animate.animation;
}
},
template: `
<div>
<div :class="animate">我是动画</div>
<button @click="handleClick">切换动画</button>
</div>
`,
});
const vm = app.mount("#app");
</script>
</body>
</html>
16.vue+css实现过渡
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>vue实现css过渡</title>
<style>
.transition{
transition:3s background-color ease;
}
</style>
</head>
<body>
<div id="app"></div>
<script src="https://unpkg.com/vue@next"></script>
<script>
const app = Vue.createApp({
data(){
return{
styleObj:{
backgroundColor:'blue'
}
}
},
methods:{
handleClick(){
if(this.styleObj.backgroundColor === "blud"){
this.styleObj.backgroundColor = "blue";
}else{
this.styleObj.backgroundColor = "red";
}
}
},
template: `
<div>
<div class="transition" :style="styleObj">我是过渡</div>
<button @click="handleClick">点击过渡</button>
</div>
`,
});
const vm = app.mount("#app");
</script>
</body>
</html>
17.transition标签实现单元组件的过渡
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>transition标签实现单元组件的过渡</title>
<style>
/*动画*/
@keyframes shake{
0%{
transform:translateX(-100px);
}
50%{
transform:translateX(-50px);
}
100%{
transform:translateX(50px);
}
}
/*过渡的固定的入场写法*/
.v-enter-from{
opacity:0;
}
.v-enter-active{
transition: opacity 3s ease-out;
}
.v-enter-to{
opacity: 1;
}
/*过渡的固定的出场写法*/
.v-leave-form{
opacity: 1;
}
.v-leave-active{
transition: opacity 3s ease-in;
}
.v-leave-to{
opacity:0;
}
</style>
</head>
<body>
<div id="app"></div>
<script src="https://unpkg.com/vue@next"></script>
<script>
/**
* 单元素,单组件的入场出场动画
*/
const app = Vue.createApp({
data(){
return{
show:false
}
},
methods:{
handleClick(){
this.show = !this.show;
}
},
/**
* 入场和出场都封装在了<transition></transition>里了
*/
template: `
<div>
<transition>
<div v-if="show">hello</div>
</transition>
<button @click="handleClick">切换</button>
</div>
`,
});
const vm = app.mount("#app");
</script>
</body>
</html>
18.transition标签实现单元组件的动画
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>transition标签实现单元组件的动画</title>
<style>
/*动画*/
@keyframes shake {
0% {
transform: translateX(-100px);
}
50% {
transform: translateX(-50px);
}
100% {
transform: translateX(50px);
}
}
.v-enter-active {
animation: shake 2s;
}
.v-enter-active {
animation: shake 2s;
}
</style>
</head>
<body>
<div id="app"></div>
<script src="https://unpkg.com/vue@next"></script>
<script>
const app = Vue.createApp({
data() {
return {
show: false
}
},
methods: {
handleClick() {
this.show = !this.show;
}
},
/**
* 补充:如果同时有过渡和动画,想要同步它们的执行时间
* 就在transition标签上加 type="animation" 或 :duration="1000",
* :duration="{ enter:1000,leave:3000 }"
*/
template: `
<div>
<transition>
<div v-if="show">hello</div>
</transition>
<button @click="handleClick">切换</button>
</div>
`,
});
const vm = app.mount("#app");
</script>
</body>
</html>
19.transition标签动画库的使用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>transition标签动画库的使用</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css" />
</head>
<body>
<div id="app"></div>
<script src="https://unpkg.com/vue@next"></script>
<script>
/**
* transition标签动画
* 可以调用它的动画库,完成需要的
* 动画。https://animate.style/
*/
const app = Vue.createApp({
data() {
return {
show: false
}
},
methods: {
handleClick() {
this.show = !this.show;
}
},
template: `
<div>
<transition
enter-active-class="animate__animated animate__bounce"
leave-active-class="animate__animated animate__bounce"
>
<div v-if="show">hello</div>
</transition>
<button @click="handleClick">切换</button>
</div>
`,
});
const vm = app.mount("#app");
</script>
</body>
</html>
20.transition标签+js实现过渡和动画
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>transition标签+js实现过渡和动画</title>
</head>
<body>
<div id="app"></div>
<script src="https://unpkg.com/vue@next"></script>
<script>
const app = Vue.createApp({
data() {
return {
show: false
}
},
methods: {
handleClick() {
this.show = !this.show;
},
handleBeforeEnter(el) {
el.style.color = 'red';
},
handleEnterActive(el, done) {
const animatiton = setInterval(() => {
const color = el.style.color;
if (color === "red") {
el.style.color = 'pink';
} else {
el.style.color = 'red';
}
}, 1000)
setTimeout(() => {
clearInterval(animatiton);
done();
}, 3000)
},
handleEnterEnd() {
alert("动画已经执行完");
}
},
/**
* 入场和出场都封装在了<transition></transition>里了
* 如果需要js实现动画需要在transition标签上加@before-enter=""钩子
* 出场动画:
* @before-enter="handleBeforeEnter"
* @enter="handleEnterActive"
* @after-enter="handleEnterEnd"
* 入场动画
* @berfore-leave=""
* @leave=""
* @leave-after=""
*/
template: `
<div>
<transition
:css="false"
@before-enter="handleBeforeEnter"
@enter="handleEnterActive"
@after-enter="handleEnterEnd"
>
<div v-if="show">hello</div>
</transition>
<button @click="handleClick">切换</button>
</div>
`,
});
const vm = app.mount("#app");
</script>
</body>
</html>
21.单元素间的动画切换
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>单元素间的动画切换</title>
<style>
/*入场*/
.v-enter-from {
opacity: 0;
}
/*入场的过程中*/
.v-enter-active {
transition: opacity 3s ease-in;
}
/*入场结束*/
.v-enter-to {
opacity: 1;
}
/*出场*/
.v-leave-from {
opacity: 1;
}
.v-leave-active {
transition: opacity 3s ease-in;
}
.v-leave-to {
opacity: 0;
}
</style>
</head>
<body>
<div id="app"></div>
<script src="https://unpkg.com/vue@next"></script>
<script>
const app = Vue.createApp({
data() {
return {
show: false
}
},
methods: {
handleClick() {
this.show = !this.show;
}
},
template: `
<div>
<transition mode="out-in" apppear>
<div v-if="show">hello</div>
<div v-else="show">world</div>
</transition>
<button @click="handleClick">切换</button>
</div>
`,
});
const vm = app.mount("#app");
</script>
</body>
</html>
22.单组件间的动画切换
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>单组件间的动画切换</title>
<style>
/*入场*/
.v-enter-from {
opacity: 0;
}
/*入场的过程中*/
.v-enter-active {
transition: opacity 3s ease-in;
}
/*入场结束*/
.v-enter-to {
opacity: 1;
}
/*出场*/
.v-leave-from {
opacity: 1;
}
.v-leave-active {
transition: opacity 3s ease-in;
}
.v-leave-to {
opacity: 0;
}
</style>
</head>
<body>
<div id="app"></div>
<script src="https://unpkg.com/vue@next"></script>
<script>
const ComponentA = {
template: `<div>hello</div>`
}
const ComponentB = {
template: `<div>world</div>`
}
const app = Vue.createApp({
data() {
return {
show: false
}
},
components: {
ComponentA,
ComponentB
},
methods: {
handleClick() {
this.show = !this.show;
}
},
template: `
<div>
<transition mode="out-in" apppear>
<ComponentA v-if="show" />
<ComponentB v-else="show" />
</transition>
<button @click="handleClick">切换</button>
</div>
`,
});
const vm = app.mount("#app");
</script>
</body>
</html>
23.列表动画
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>列表动画</title>
<style>
.v-enter-from {
opacity: 0;
transform: translateY(30px);
}
.v-enter-active {
transition: all 2s ease-in;
}
.v-enter-to {
opacity: 1;
transform: translateY(0px);
}
.v-move {
transition: all 2s ease-in
}
.list-item {
display: inline-block;
margin-left: 10px;
}
</style>
</head>
<body>
<div id="app"></div>
<script src="https://unpkg.com/vue@next"></script>
<script>
const app = Vue.createApp({
data() {
return {
list: [1, 2, 3]
}
},
methods: {
handleClick() {
this.list.unshift(this.list.length + 1);
}
},
/**
* 列表动画用<tansition-group></tansition-group>标签
*/
template: `
<div>
<transition-group>
<span class="list-item" v-for="item in list" :key="item">{{item}}</span>
</transition-group>
<button @click="handleClick">增加</button>
</div>
`,
});
const vm = app.mount("#app");
</script>
</body>
</html>
24.状态动画
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>状态动画</title>
</head>
<body>
<div id="app"></div>
<script src="https://unpkg.com/vue@next"></script>
<script>
/**
* 状态动画:通过数据来控制动画
*/
const app = Vue.createApp({
data() {
return {
number: 1,
animateNumber: 1
}
},
methods: {
handleClick() {
this.number = 10;
if (this.animateNumber < this.number) {
const animation = setInterval(() => {
this.animateNumber += 1;
if (this.animateNumber === 10) {
clearInterval(animation)
}
}, 100);
}
}
},
/**
* 列表动画用<tansition-group></tansition-group>标签
*/
template: `
<div>
<div>{{animateNumber}}</div>
<button @click="handleClick">增加</button>
</div>
`,
});
const vm = app.mount("#app");
</script>
</body>
</html>
三、vue(下)
1.局部Mixin
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>局部Mixin</title>
</head>
<body>
<div id="app"></div>
<script src="https://unpkg.com/vue@next"></script>
<script>
/**
* Mixin:把某些内容混入到哪里去,
* 组件 data, methods 优先级高于 mixin data, methods 优先级。
* 生命周期函数:先执行mixin里的,再执行组件里的。
* vue3建议尽量不要用mixin,因为它的可维护性不高,
* 可用Composition API来代替,后面会提到。
*/
//定义局部mixin
const myMixin = {
data() {
return {
number: 2,
content: "Mixin"
}
},
created() {
console.log("mixin的函数");
}
}
const app = Vue.createApp({
data() {
return {
number: 1,
}
},
/**
* 使用mixin即把mixin的内容混入
*/
mixins: [myMixin],
created() {
console.log("组件")
},
methods: {
handleClick() {
alert("hello");
}
},
template: `
<div>
<div>{{number}}</div>
<div>{{content}}</div>
<button @click="handleClick">增加</button>
</div>
`,
});
const vm = app.mount("#app");
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>局部Mixin-1</title>
</head>
<body>
<div id="app"></div>
<script src="https://unpkg.com/vue@next"></script>
<script>
/**
* 自定义属性会自动存入$options,所以使用。
* 自定义属性时直接用$options即可。
* 自定义属性:组件中的属性优先级高于 mixin 属性的优先级。
*/
//定义局部mixin
const myMixin = {
//自定义属性
number: 1,
}
const app = Vue.createApp({
//自定义属性
number: 2,
mixins: [myMixin],
template: `
<div>
<div>{{this.$options.number}}</div>
</div>
`,
});
//修改属属性优先级
appVue.config.optionMergeStrategies.number = (mixinVal, appVue) => {
return mixinVal || appVue;
};
const vm = app.mount("#app");
</script>
</body>
</html>
2.全局Mixin
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>全局Mixin</title>
</head>
<body>
<div id="app"></div>
<script src="https://unpkg.com/vue@next"></script>
<script>
const app = Vue.createApp({
data() {
return {
number: 1,
}
},
created() {
console.log("组件")
},
methods: {
handleClick() {
alert("hello");
}
},
template: `
<div>
<div>{{number}}</div>
<div>{{content}}</div>
<button @click="handleClick">增加</button>
</div>
`,
});
/**
* 定义全局Mixin
* 全局Mixin不需要用mixins: [myMixin]使用,
* vue底层会自动注入。但开发时,不推荐使用,
* 全局Mixin可维护性不高。
* */
app.mixin({
data() {
return {
number: 2,
content: "Mixin"
}
},
created() {
console.log("mixin的函数");
}
})
const vm = app.mount("#app");
</script>
</body>
</html>
3.自定义指令
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>自定义指令</title>
</head>
<body>
<div id="app"></div>
<script src="https://unpkg.com/vue@next"></script>
<script>
/**
* 自定义指令:
* 定义后,使用v-指令名即可使用自定义指令。
* 注:局部自定义指令需要在组件注册才能使用
*/
//定义局部指令
// const directives = {
// focus: {
// mounted(el) {
// el.focus();
// }
// }
// }
const app = Vue.createApp({
//局部指令的注册
//directives,
template: `
<div>
<input v-focus />
</div>
`,
});
//定义全局指令
app.directive("focus", {
mounted(el) {
el.focus();
}
})
const vm = app.mount("#app");
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>自定义指令-1</title>
<style>
.header {
position: absolute;
}
</style>
</head>
<body>
<div id="app"></div>
<script src="https://unpkg.com/vue@next"></script>
<script>
const app = Vue.createApp({
data() {
return {
top: 100,
}
},
template: `
<div v-pos="top" class="header">
<input />
</div>
`,
});
app.directive("pos", {
mounted(el, binding) {
el.style.top = binding.value + "px";
},
updated(el, binding) {
el.style.top = binding.value + "px";
}
})
const vm = app.mount("#app");
</script>
</body>
</html>
4.Teleport传送门
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Teleport传送门</title>
<style>
.area {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 200px;
height: 300px;
background: pink;
}
.mask {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
background: #000;
opacity: 0.5;
}
</style>
</head>
<body>
<div id="app"></div>
<script src="https://unpkg.com/vue@next"></script>
<script>
/**
* Teleport传送门其实就是一个标签
* <teleport to="传送的地方"></teleport>
* 传送到指定的地方,进行操作。
*/
const app = Vue.createApp({
data() {
return {
show: false,
}
},
methods: {
handleBtnClick() {
this.show = !this.show;
}
},
template: `
<div class="area">
<button @click="handleBtnClick">按钮</button>
<teleport to="body">
<div class="mask" v-show="show"></div>
</teleport>
</div>
`,
});
const vm = app.mount("#app");
</script>
</body>
</html>
5.render函数(底层)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>render函数(底层)</title>
</head>
<body>
<div id="app"></div>
<script src="https://unpkg.com/vue@next"></script>
<script>
/**
* template-->rander-->h-->生成虚拟DOM(js对象)-->真实的DOM-->展示在页面
*/
const app = Vue.createApp({
template: `
<myTitle :level="1">
hello
</myTitle>
`,
});
app.component("myTitle", {
props: ["level"],
//render函数
render() {
const {
h
} = Vue;
/*h()返回是虚拟DOM*/
return h("h" + this.level, {}, this.$slots.default())
}
// template: `
// <h1 v-if="level===1"><slot /></h1>
// <h2 v-if="level===2"><slot /></h2>
// `
})
const vm = app.mount("#app");
</script>
</body>
</html>
6.插件(plugin)的定义和使用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>插件(plugin)的定义和使用</title>
</head>
<body>
<div id="app"></div>
<script src="https://unpkg.com/vue@next"></script>
<script>
/**
* 插件(plugin):其实就是把通过性的功能封装起来
*/
//定义插件
const myPlugin = {
//插件被使用的时候,install函数就会执行
install(app, options) {
/**app:应用实例,options:接收传进来的数据
*console.log(app, options);
*通过app可以扩展很多需要的功能
**/
app.provide("name", "张三"); //数据传值
app.directive("focus", { //自定义指令
mounted(el) {
el.focus();
}
})
app.mixin({
mounted() {
console.log("mixin");
}
})
}
}
const app = Vue.createApp({
template: `
<myTitle />
`,
});
app.component("myTitle", {
inject: ["name"],
template: `
<div>
{{name}}
<input v-focus />
</div>
`
})
//使用插件
app.use(myPlugin, {
name: "xiaoming"
});
const vm = app.mount("#app");
</script>
</body>
</html>
7.数据校验插件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>数据校验插件</title>
</head>
<body>
<div id="app"></div>
<script src="https://unpkg.com/vue@next"></script>
<script>
const app = Vue.createApp({
data() {
return {
name: "zhangsan",
age: 18
}
},
rules: {
age: {
validate: (age) => {
return age > 18
},
message: "你已经成年"
},
name: {
validate: (name) => {
return name >= 4
},
message: "名字太长了"
}
},
template: `
<div>name:{{name}},age:{{age}}</div>
`,
});
/**
* 用插件封装一个能识别自己的定义的规则
*/
const validatorPlugin = (app, options) => {
app.mixin({
created() {
for (let key in this.$options.rules) {
const item = this.$options.rules[key];
//console.log(key,item);
this.$watch(key, (value) => {
//console.log(key + "已经发生变化");
const result = item.validate(value);
if (!result) {
console.log(item.message);
}
})
}
}
})
}
app.use(validatorPlugin);
const vm = app.mount("#app");
</script>
</body>
</html>
8.Setup函数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Setup函数</title>
</head>
<body>
<div id="app"></div>
<script src="https://unpkg.com/vue@next"></script>
<script>
const app = Vue.createApp({
template: `
<div @click="handleClick">name:{{name}},age:{{age}}</div>
`,
/**
* setup 会在create实例被完全初始化执行
* setup函数属于Composition API
* setup里的数据直接可以在组件中使用
*/
setup(props, context) {
return {
name: "zhangsan",
age: 19,
handleClick() {
alert("hello")
}
}
}
});
const vm = app.mount("#app");
</script>
</body>
</html>
9.ref、reactive响应式引用的用法和原理
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ref、reactive响应式引用的用法和原理</title>
</head>
<body>
<div id="app"></div>
<script src="https://unpkg.com/vue@next"></script>
<script>
/**
* 响应式引用的原理:通过proxy对数据进行封装,
* 当数据变化时,模板等内容也随着变化。
* ref():处理基础类型的数据。
* reactive():处理非基础类型的数据。
* ref和reactive都属于Comsition API,可以用来
* 代替data
*/
const app = Vue.createApp({
template: `
<div>name:{{nameObj.name}}</div>
`,
setup(props, context) {
/**
* proxy把"lisi"变成proxy({value:"lisi"})
* 这样的一个响应式引用,改变它时,需要.value
* 来改变。即let name = ref("lise"),name.value="zhangsan"
*/
// const {
// ref
// } = Vue; //引入ref才能使用它
// let name = ref("lisi");
// setTimeout(() => {
// name.value = "zhangsang";
// }, 2000);
// return {
// name
// }
const {
reactive
} = Vue;
const nameObj = reactive({
name: "lisi"
});
setTimeout(() => {
nameObj.name = "zhangsan";
}, 3000);
return {
nameObj
}
}
});
const vm = app.mount("#app");
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ref、reactive响应式引用的用法和原理</title>
</head>
<body>
<div id="app"></div>
<script src="https://unpkg.com/vue@next"></script>
<script>
const app = Vue.createApp({
template: `
<div>{{name}}</div>
`,
setup(props, context) {
const {
reactive,
toRefs
} = Vue;
const nameObj = reactive({
name: "zhangsan"
});
setTimeout(() => {
nameObj.name = "lisi";
}, 3000);
/**
* toRefs底层:会把proxy({name:"zhangsan"})的格式
* 转换为{name:proxy({value:"zhangsan"})}的格式。
* 简单理解:如果不想在使用时用Obj.name的形式,想
* 直接用name就能获取值的话,就需要做一次toRefs的
* 转换即roResf(Obj),此时就可以直接使用name取值了。
*/
const {
name
} = toRefs(nameObj);
return {
name
}
}
});
const vm = app.mount("#app");
</script>
</body>
</html>
10.toRef、context参数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>toRef、context参数</title>
</head>
<body>
<div id="app"></div>
<script src="https://unpkg.com/vue@next"></script>
<script>
const app = Vue.createApp({
template: `
<div>{{age}}</div>
`,
setup(props, context) {
const {
reactive,
toRef
} = Vue;
const data = reactive({
name: "zhangsan"
});
/**
* toRef:是用来处理默认值的,即取原来没有的数据,
* 本来是会报错的,但用toRef给其赋一个默认值,它
* 就不会报错了。toRef和toRefs不同,toRef后需要
* 用.value来改变值。
*/
const age = toRef(data, "age");
setTimeout(() => {
age.value = "lisi"
}, 3000)
return {
age
}
}
});
const vm = app.mount("#app");
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>toRef、context参数</title>
</head>
<body>
<div id="app"></div>
<script src="https://unpkg.com/vue@next"></script>
<script>
const app = Vue.createApp({
methods: {
handleChange(){
alert('change');
}
},
template: `
<child title="hello" @change="handleChange">slot</child>
`,
});
app.component("child", {
template: `
<div @click="handleClick">chlid</div>
`,
setup(props, context) {
const { attrs,slots, emit } = context;
/**
* attrs:接收父组件传过来的静态属性:title="hello"
* slots:接收父组件传过来的插槽
* emit:向往触发函数
*/
//console.log(attrs.title);
// const { h } = Vue;
// return () => h("div", {}, slots.default());
function handleClick(){
emit("change");//向外触发change函数
}
return {
handleClick
}
}
})
const vm = app.mount("#app");
</script>
</body>
</html>
11.使用Comsition API 开发 ToList
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>使用Comsition API 开发 ToList</title>
</head>
<body>
<div id="app"></div>
<script src="https://unpkg.com/vue@next"></script>
<script>
//list的封装
const listRelativeEffect = ()=>{
const { reactive } = Vue;
//创建非基础类型的响应式数据要reactive
const list = reactive([]);
const addItemToList = (item)=>{
list.push(item);
}
return { list, addItemToList }
}
//inpu的封装
const inputtRelativeEffect = ()=>{
const { ref } = Vue;
const inputVal = ref('');
const handleInputValueChange = (e)=>{
inputVal.value = e.target.value;
}
return { inputVal,handleInputValueChange }
}
const app = Vue.createApp({
setup(){
const { list, addItemToList } = listRelativeEffect();
const { inputVal,handleInputValueChange } = inputtRelativeEffect();
return {
list,
addItemToList,
inputVal,
handleInputValueChange,
}
},
template: `
<div>
<div>
<input :value="inputVal" @input="handleInputValueChange" />
<div>{{inputVal}}</div>
<button @click="()=>addItemToList(inputVal)">提交</button>
</div>
<ul>
<li v-for="(item,index) in list">{{item}}</li>
</ul>
</div>
`,
});
const vm = app.mount("#app");
</script>
</body>
</html>
12.cmoputed计算属性
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>cmoputed计算属性</title>
</head>
<body>
<div id="app"></div>
<script src="https://unpkg.com/vue@next"></script>
<script>
const app = Vue.createApp({
setup(){
const { ref, computed } = Vue;
const count = ref(0);
const handleClick = ()=>{
count.value += 1;
}
const countAddFive = computed(()=>{
return count.value + 5;
})
return { count,handleClick,countAddFive }
},
template:`
<div>
<span @click="handleClick">{{count}}--{{countAddFive}}</span>
</div>
`
});
const vm = app.mount("#app");
</script>
</body>
</html>
13.watch和watchEffect侦听器
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>watch和watchEffect侦听器</title>
</head>
<body>
<div id="app"></div>
<script src="https://unpkg.com/vue@next"></script>
<script>
const app = Vue.createApp({
setup(){
const { reactive, watch, watchEffect, toRefs } = Vue;
const nameObj = reactive(
{
name:"张三",
englishName:"zhangsan"
}
);
/**
* watch
* 具有一定的懒惰性 lazy
* 参数可以获取到原始值和当前值
*/
watch([()=>nameObj.name,()=>nameObj.englishName],([curName,curEng],[prevName,prevEng])=>{
console.log(curName,prevName,'---',curEng,prevEng);
})
/**
* watchEffect
* 立即执行,没有惰性
* 不需要传递所侦听的内容,它会自动感知代码依赖
* 不需要传递很多参数,只需要传递一个回调函数
* 不能获取之前数据值
* 异步处理可以用watchEffect
*/
const stop = watchEffect(()=>{
console.log(nameObj.name);
console.log(nameObj.englishName);
setTimeout(()=>{
stop();
},3000);
})
const { name, englishName } = toRefs(nameObj);
return { name, englishName }
},
template:`
<div>
<div>
Name:<input v-model="name" />
</div>
<div>
Name is:{{name}}
</div>
<div>
EnglishName:<input v-model="englishName" />
</div>
<div>
EnglishName is:{{englishName}}
</div>
</div>
`
});
const vm = app.mount("#app");
</script>
</body>
</html>
14.生命周期函数的新写法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>生命周期函数的新写法</title>
</head>
<body>
<div id="app"></div>
<script src="https://unpkg.com/vue@next"></script>
<script>
const app = Vue.createApp({
setup(){
/**
* Composition API里生命周期函数其实就是在原来的基础
* 上加on引入进来就可以使用了,使用还是原来的方法。
*/
const { ref, onBeforeMount, onMounted, onBeforeUpdate } = Vue;
const name = ref("zhangsan");
onBeforeMount(()=>{
console.log("onBeforeMount");
})
onMounted(()=>{
console.log("onMunted");
})
onBeforeUpdate(()=>{
console.log("onBeforeUpdate")
})
const handleClick = ()=>{
name.value="lisi";
}
return { name, handleClick }
},
template:`
<div @click="handleClick">
{{name}}
</div>
`
});
const vm = app.mount("#app");
</script>
</body>
</html>
15.Provide,Inject,DOM中的Ref的用法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Provide,Inject,DOM中的Ref的用法</title>
</head>
<body>
<div id="app"></div>
<script src="https://unpkg.com/vue@next"></script>
<script>
const app = Vue.createApp({
setup(){
const { provide,ref,readonly } = Vue;
const name = ref("lisi");
//readonly(name) 保证单项数据流
provide("name",readonly(name));
provide("changeName",(value)=>{
name.value = value;
})
return { }
},
template:`
<div>
<child />
</div>
`
});
app.component("child",{
setup(){
const { inject } = Vue;
const name = inject("name","如果取不到就显示这个默认值");
//获取父组件传过来的方法
const changeName = inject("changeName");
const handleClick = ()=>{
//调用父组件传过来的方法进行更改
changeName("zhangsan");
}
return { name, handleClick }
},
template:`<div @click="handleClick">{{name}}</div>`
})
const vm = app.mount("#app");
</script>
</body>
</html>
16.DOM中的Ref的用法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>DOM中的Ref的用法</title>
</head>
<body>
<div id="app"></div>
<script src="https://unpkg.com/vue@next"></script>
<script>
const app = Vue.createApp({
setup(){
const { ref, onMounted } = Vue;
const hello = ref(null);
onMounted(()=>{
console.log(hello.value);//获取DOM元素
})
return { hello }
},
/**
* 注:div里ref和上面引入的ref是不同的,
* div里的ref是获取DOM元素的一个标记,而
* 上面引入的ref是用来创建响应式数据的。
*/
template:`
<div>
<div ref="hello">hello wrold</div>
</div>
`
});
const vm = app.mount("#app");
</script>
</body>
</html>
17.vuecli脚手架
1).真正开发是需要脚手架来创建vue项目的
2).安装node,内包含npm(包管理工具)
3).在命令行安装nrm(镜像源):npm install nrm -g ;
显示当前用的镜像源:
npm get registry
切换到淘宝镜像源
npm config set registry http://registry.npm.taobao.org
使用taobao镜像,在下载依赖包速度会快一点,nrm use taoba0
4).安装脚手架:npm install -g @vue/cli || 安装指定版本:npm install @vue/cli@版本号
5).vue项目的创建
在VScode里:Ctrl + ~ 进入控制台;
切换到自己想创建的目录:
eg:
PS D:\koa\vue3.0> cd vue-3
PS D:\koa\vue3.0\vue-3> cd vue21
PS D:\koa\vue3.0\vue-3\vue21> vue create 项目名
vue-cli脚手架工程目录简介
18.vue-router
入口文件
import { createApp } from 'vue'
import App from './App.vue'
/**
* 路由:是指根据url的不同,展示不同的内容
*/
import router from './router'
createApp(App).use(router).mount('#app')
路由文件
import { createRouter, createWebHashHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
import LoginDome from '../views/LoginDome.vue'
const routes = [
{
path: '/',
name: 'home',
component: HomeView
},
//这种写法同步加载不推荐,当然首页可以这样写,其他路由就下面的写法即可
{
path: '/login',
name: 'logindemo',
component: LoginDome
},
{
path: '/about',
name: 'about',
//这种写法是异步加载路由的意思(即按需加载)--推荐
component: () => import(/* webpackChunkName: "about" */ '../views/AboutView.vue')
}
]
const router = createRouter({
history: createWebHashHistory(),
routes
})
export default router
展示路由的文件
<template>
<div>
<nav>
<!-- router-link:是路由跳转的标签 -->
<router-link to="/">Home</router-link> |
<router-link to="/login">Login</router-link> |
<router-link to="/about">About</router-link>
</nav>
<!--router-view:用来展示路由跳转到的地址的内容 -->
<router-view/>
</div>
</template>
19.vuex数据仓库
以下代码可以参考图来理解,vuex数据仓库,在其他组件进行修改的流程,确实稍微有点复杂
需要修改数据的组件
<template>
<div>
<img alt="Vue logo" src="../assets/logo.png">
<h1 @click="handleClick">{{myName}}</h1>
</div>
</template>
<script>
export default {
name: 'HomeView',
computed:{
myName(){
//获取vuex仓库中state里的数据
return this.$store.state.name;
}
},
methods: {
handleClick(){
/**
* 想要修改vuex仓库里的数据,vuex要求
* 第一要派发(dispatch)一个action(change)
*/
this.$store.dispatch("change","wangwu");//wangwu是要修成的数据
}
},
}
</script>
使用Composition API修改数据文件
<template>
<div>
<h1 @click="handleClickChange">{{name}}</h1>
</div>
</template>
<script>
import { useStore } from 'vuex';
import { toRefs } from 'vue';
//import axios from 'axios';
export default {
name:"AboutViews",
setup(){
//获取全局的数据对象
const store = useStore();
//const name = store.state.name;
const {name} = toRefs(store.state);
const handleClickChange = ()=>{
store.dispatch("changeName","zhaoliu");
}
//vue 使用axios发送ajax请求
// axios.get('接口地址')
// .then((response)=>{
// console.log(response);
// })
return{ name,handleClickChange }
}
}
</script>
vuex仓库文件
import { createStore } from 'vuex'
/**
* vuex:是一个数据管理框架
* vuex创建了一个全局唯一的框架,用来存放全局的数据
*/
export default createStore({
state: {
/**
*state存放全局的数据,
*任何组件都可以使用这里的数据
*/
name:"zhangsan"
},
getters: {
},
mutations: {
/**
* 第四接收change提交过来的一个叫muta的mutation,
* 并把muta转为一个函数执行
*/
muta(store,str){
/*第五修改你需要修改的数据*/
this.state.name = str;
},
mutas(store,str1){
this.state.name = str1;
}
},
actions: {
/**
* 第二actions会感知到你派发了一个叫做
* change的action,并把change转为一个
* 函数执行。
*/
change(store,str){
/**
* 第三change函数的执行会提交一个commit
* 触发一个mutation(muta)
* 此处也可以处理异步操作
*/
this.commit("muta",str)
},
changeName(store,str){
setTimeout(()=>{
this.commit("mutas",str);
},5000)
}
},
modules: {
}
})