概述
Vue2中文文档
1.模板语法
- 插值
<body>
<div class="app">
<!-- 模板语法中支持表达式,不支持语句
表达式:会生成一个值,比如a,a+b,demo(1),ok ? 'YES' : 'NO'
就是我们可以拿一个变量在表达式左边接到值,可以用+连接-->
<h1> Hello {{address+name}},{{name}},{{1+1+num}}</h1>
<h2>{{school.name}}</h2>
</div>
<script>
// 阻止Vue在启动时生成生产提升
Vue.config.productionTip = false
var app = new Vue({
el: '.app',
// data中用于存储数据,数据供el所指定的容器去使用,data中数据改变,页面也会随之改变
data: {
name: 'Vue!',
address: '北京',
num: 2,
school: {
name: '国防科大'
}
}
})
-
指令
<div id="root"> <h1>插值语法</h1> <h3>你好,{{name}}</h3> <hr> <h1>指令语法</h1> <a v-bind:href="url">点我去B站</a> <!-- 指令简写 --> <a :href="url.toUpperCase()">点我去B站</a> <!-- 动态参数 --> <a :[att]="url">动态参数</a> <hr> <h1>dom事件</h1> </div> <script> new Vue({ el: '#root', data: { name: 'jact', url: 'https://www.bilibili.com/', att: 'href' } })
2.el和data两种写法
<div id="root">
<h1>{{name}}</h1>
</div>
<script>
const v = new Vue({
el: '#root',
/* data: {
// name: '对象式data写法'
} */
//data写法2函数式
//vue管理的函数不要写箭头函数,否则this就不再是Vue实例了
data: function () {
return {
name: '函数式data写法'
}
},
})
console.log(v);
// setTimeout(() => {
// v.$mount('#root');el第2种写法
// }, 1000)
</script>
3.数据绑定
v-bind ⭐️
拿到的是“”双引号里面的值,没加则拿到的是字符串
<!-- v-bind数据只能从data流向页面 -->
<h2>单向数据绑定:<input type="text" :value="name"></h2>
<!-- v-module只能应用在表单类元素上,对应的value属性 -->
<h2>双向数据绑定:<input type="text" v-model:value="name"></h2>
<h2>双向数据绑定简写:<input type="text" v-model="name"></h2>
v-model
- v-model绑定的值不能是props传过来的值,因为props是不可以修改的
- props传过来的若是对象类型的值,只要不直接修改对象而是修改对象中的属性时Vue不会报错,但不推荐这样做
4.MVVM模型
M—model(模型):对应data中的数据
V–View(视图):模板代码
VM–ViewModel(视图模板):Vue实例对象
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jSW2F9R4-1669432222699)(D:新的开始mdVueimage1.png)]
5.Object.defineProperty
基本使用
object.defineProperty(obj,prop,descriptor)
只有用这个方法加进去的才受控制
-
obj:必需。目标对象
-
prop:必需。需定义或修改的属性的名字
-
descriptor:必需。目标属性所拥有的特性
- value:设置属性的值
- writable:值是否可以重写修改。true/false,默认为false
- enumerable:目标属性是否可以被枚举。true/false,默认为false
- configurable:目标属性是否可以被删除或是否可以再次修改特性true/false,默认为false
- 这里id是本身有的,所以不受控制
var obj = { id: 1, pname: '小米', price: 1999 }; // 有则修改,无则添加 Object.defineProperty(obj, 'num', { value: 1000, // 因为writable默认为false }); obj.num = 3; obj.id = 3; console.log(obj);//id:3,num:1000
getter和setter数据代理
let number = 18;
let person = {
name: '张三',
sex: '男'
}
Object.defineProperty(person, 'age', {
/*- value:设置属性的值
- writable:值是否可以重写修改。true/false,默认为false
- enumerable:目标属性是否可以被枚举。true/false,默认为false
- configurable:目标属性是否可以被删除或是否可以再次修改特性true/false,默认为false*/
//当有人读取person的age属性时,get函数就会被调用,且返回值就是age的值
get() {
// 无论你咋修改person的值它都是取number
console.log('有人读取了age属性');
return number
},
//当有人修改age属性时,set函数就会被调用,且会收到修改的具体值
set(value) {
console.log('有人修改了age属性,且值是' + value);
number = value;
}
})
简单数据代理
let obj = {
x: 100
}
let obj2 = {
y: 100
}
Object.defineProperty(obj2, 'x', {
// 当有人读取Obj2的x属性时,就会调用get函数,且返回的值会给x
get() {
return obj.x
},
//当有人修改Obj2的x属性时,set函数就会被调用,且会收到修改的具体值
set(value) {
obj.x = value
}
})
Vue中的数据代理
也是利用Object.defineProperty中的getter和setter
<h1 id="main">学校名称: {{name}} <br>
校址: {{address}}</h1>
<script>
const vm = new Vue({
el: '#main',
data: {
name: '井冈山大学',
address: '江西吉安'
}
})
console.log(vm);
console.log(vm.address === vm._data.address);//true,江西吉安
6.事件处理⭐️
事件回调要写到methods对象上,this.name可以拿到data里面的name数据
1.使用v-on:xxx或者@click绑定事件,xxx是事件名,2.事件回调要写到methods对象上,最终会在vm中,3.this指向vm或组件实例对象,4.@click='demo’和=demo($event)效果一致
<div id="root">
<h2>欢迎来到{{name}}学习</h2>
<button @click="showInfo">不传参</button>
<button v-on:click="showInfo2(66)">传参</button>
<button @click="showInfo3('刘总',$event)">传2个参数和evert</button>
</div>
<script>
const vm = new Vue({
el: '#root',
data: {
name: '武当派',
},
methods: {
showInfo(e) {
console.log(e.target);
console.log(this === vm);//true
},
showInfo2(number) {
console.log(number);
},
showInfo3(str, e) {
console.log(str);
console.log(e);
}
},
})
Vue中的事件修饰符
还可以连着使用.stop.prevent,可以看文档
- prevent:阻止默认事件(常用)
- stop:阻止事件冒泡(常用)
- once:事件只触发一次(常用)
- capture:使用事件的捕获模式
- self:只有event.target是当前操作的元素时才触发事件
- passive:事件的默认行为立即执行,无需等待事件回调执行完毕
对图片的监听
<!-- @Load监听图片是否加载完 -->
<img v-lazy='showImage' @load="imageLoad">
methods:{
imageLoad(){
this.$bus.$emit('itemImageLoad')
Vue中常用键盘事件
-
enter
-
.tab
-
.delete
(捕获“删除”和“退格”键) -
.esc
-
.space
-
.up
-
.down
-
.left
-
.right
-
<div id="root"> <input type="text" placeholder="按下回车提示输入" @keyup.enter="showInfo"> </div> <script> new Vue({ el: '#root', methods: { showInfo(e) { console.log(e.target.value); } }, })
7.method与computed的区别
computed计算属性会有缓存,依赖数据改变才会重新触发,下面打印一个,methods打印3次
computed⭐️
计算属性不要加(),是靠返回值拿东西的;
刚加载页面初始化自动触发一次,然后依赖数据发生改变也会触发
如果值只读取,则可以简写,如果还要修改则得写setter
<span>{{fullName}}</span><br>
<span>{{fullName}}</span><br>
<span>{{fullName}}</span>
</div>
<script>
new Vue({
el: '#root',
data: {
user: '刘',
name: '总'
},
computed: {
fullName() {
console.log('444');
return this.user + '-' + this.name
}
}
// methods: {
// fullName() {
// console.log('444');
// return this.user + '-' + this.name
// }
// },
8.watch侦听器
Vue自身可以检测对象内部值的改变,但是watch默认不行,得加deep:true
- 配合本地存储贼好用
import { setItem, getItem } from "@/utils/storage";
export default {
components: { searchHistory, SearchSuggest, SearchResult },
name: "Search",
data() {
return {
searchText: "",
isResultSHow: false, //控制搜索结果的展示
searchHistories: getItem("SEARCH-HISTORY") || [], //搜索的历史记录
};
},
watch: {
searchHistories(newVal) {
setItem("SEARCH-HISTORY", newVal);
},
},
<div id="root">
<h1>今天天气很{{info}}</h1>
<button @click="change">切换天气</button>
<hr>
<h3>a的值是:{{numbers.a}}</h3>
<button @click="numbers.a++">点我让a+1</button>
</div>
<script>
const vm = new Vue({
el: '#root',
data: {
isHot: true,
numbers: {
a: 1,
b: 1
}
},
computed: {
info() {
return this.isHot ? '炎热' : '凉爽';
}
},
methods: {
change() {
this.isHot = !this.isHot
//console.log(vm);
//写法2
// this.$watch('isHot', {
// deep: true,
// handler() {
// console.log('f方法2');
// }
// })
},
},
watch: {
isHot: {
//immediate: true,初始化时handler调用一下
handler(news, olds) {
console.log('isHot被修改了');
console.log(news, olds);
}
},
numbers: {
// 监听多级结构中某个属性的变化
deep: true,
handler() {
console.log('对象发生了变化');
}
}
}
})
watch简写
不需要额外参数只有handler时可以简写比如监听isHot
this指向vm
isHot(news){
console.log(news);}
watch与computed的区别
比如整个定时器,用watch,computed返回不了
异步计算优先用watch,其他都用computed
关键字搜索案例⭐️
-
watch实现
<div id="root"> <input type="text" v-model="keyword"> <ul> <li v-for="(item,i) in filPersons" :key="item.id">{{item.name+'-'+item.age+'-'+item.sex}}</li> </ul> </div> <script> new Vue({ el: '#root', data: { keyword: '', persons: [ { id: '1', name: '马冬梅', age: 18, sex: '女' }, { id: '2', name: '周冬雨', age: 19, sex: '女' }, { id: '3', name: '周杰伦', age: 20, sex: '男' }, { id: '4', name: '温兆伦', age: 21, sex: '男' }, ], filPersons: [] }, watch: { keyword: { immediate: true, handler(news) { this.filPersons = this.persons.filter((value) => { return value.name.indexOf(news) !== -1; }) } } } })
-
computed实现⭐️
<div id="root"> <input type="text" v-model="keyword"> <ul> <li v-for="(item,i) in filPersons" :key="item.id">{{item.name+'-'+item.age+'-'+item.sex}}</li> </ul> </div> <script> new Vue({ el: '#root', data: { keyword: '', persons: [ { id: '1', name: '马冬梅', age: 18, sex: '女' }, { id: '2', name: '周冬雨', age: 19, sex: '女' }, { id: '3', name: '周杰伦', age: 20, sex: '男' }, { id: '4', name: '温兆伦', age: 21, sex: '男' }, ], }, computed: { filPersons() { return this.persons.filter((value) => { return value.name.indexOf(this.keyword) !== -1; }) } } })
8.class和style
绑定class
<div id="root">
<!--字符串写法,适用于类名不确定需要动态指定 -->
<div class="base" :class="main" @click="change">{{name}}</div>
<!-- 数组写法:适用于要绑定的样式个数不确定,名字也不确定 -->
<div class="base" :class="arr">{{name}}</div>
<!-- 对象写法:适用于要绑定样式个数确定,名字也确定,但要动态决定用不用 -->
<div class="base" :class="obj">哈哈哈哈</div>
<div class="base" :class="{a:active1,b:active2}">哈哈哈哈</div>
</div>
<script>
const vm = new Vue({
el: '#root',
data: {
name: '刘-总',
main: 'fun',
arr: ['a', 'b', 'c'],
obj: {
a: false,
b: false
},
active1: true,
active2: false
},
methods: {
change() {
var arr = ['fun', 'sad', 'happy'];
let index = Math.floor(Math.random() * 3);
this.main = arr[index];
}
style
直接看文档
9.条件渲染
-
v-if适合切换频率低的
-
v-show适合高频率切换的,display的形式
-
v-if还可以配合template来使用
-
v-if和v-else-if,v-else一起使用时,中间不能有其他元素打断
-
使用v-if时,元素可能无法获取到,而v-show一定可以获取到
-
下面的input加key是为了防止input复用,这样里面的内容也会复用不好
-
<div id="root"> <span v-if="flag"><label for="username">用户账号</label> <input type="text" id="username" placeholder="用户账号" key="acount"></span> <span v-else><label for="mails">用户邮箱</label> <input type="text" id="mains" placeholder="用户邮箱" key="mail"></span> <button @click="info()">切换类型</button> </div> <script> new Vue({ el: '#root', data: { flag: true }, methods: { info() { this.flag = !this.flag }, } }) </script> </body>
8.
# 10.v-for列表渲染
> 如果不写key,则会默认补key为index,最好以唯一标识为key
>
> index如果逆序添加也是会出问题的,最好是id
```js
<!-- 遍历数组 -->
<h2>人员列表</h2>
<!-- <ul>
<li v-for="item of persons" :key="item.id">{{item.name+'-'+item.age}}</li>
</ul> -->
<ol>
<!-- 可以有两个参数,第一个参数为值,第2个参数为索引 -->
<li v-for="(item,index) in persons" :key="item.id">{{item.name+'-'+index}}</li>
</ol>
<!-- 遍历对象 -->
<h2>车辆信息</h2>
<ul>
<li v-for="(value,key) in car" :key="key">
{{value+': '+key}}
</li>
</ul>
</div>
<script>
new Vue({
el: '#root',
data: {
persons: [
{ id: '1', name: '张三', age: 18 },
{ id: '2', name: '李四', age: 19 },
{ id: '3', name: '王五', age: 20 },
],
car: {
name: '奥迪A8',
price: '70万',
color: '蓝色'
}
关键字搜索案例⭐️
# <body>
<div id="root">
<input type="text" v-model="keyword">
<button @click="sortType=2">年龄升序</button>
<button @click="sortType=1">年龄降序</button>
<button @click="sortType=0">默认排序</button>
<ul>
<li v-for="(item,i) in filPersons" :key="item.id">{{item.name+'-'+item.age+'-'+item.sex}}</li>
</ul>
</div>
<script>
new Vue({
el: '#root',
data: {
keyword: '',
sortType: 0,//0原顺序 1降序 2升序
persons: [
{ id: '1', name: '马冬梅', age: 62, sex: '女' },
{ id: '2', name: '周冬雨', age: 29, sex: '女' },
{ id: '3', name: '周杰伦', age: 55, sex: '男' },
{ id: '4', name: '温兆伦', age: 41, sex: '男' },
],
sortType: 0,
},
computed: {
filPersons() {
const arr = this.persons.filter((val, index) => {
return val.name.indexOf(this.keyword) !== -1;
})
if (this.sortType) {
arr.sort((a, b) => {
return this.sortType === 1 ? b.age - a.age : a.age - b.age;
})
}
return arr
}
}
})
11.Vue监测数据⭐️
这样加Vue才会监测到,才是响应式的,且不能直接给data添加数据,会报错,下面代码可以配合文档看
对象Vue.set
body>
<div id="root">
<h2>{{ta}}</h2>
<h2>姓名:{{student.name}}</h2>
<h2>年龄:{{student.age}}</h2>
<!-- 如果值为未定义undefined但它是有依赖student属性的,那么它既不会报错也不会展示 -->
<h2 v-if="student.sex">性别:{{student.sex}}</h2>
<button @click="add">添加一个性别属性,默认值为男</button>
</div>
<script>
const vm = new Vue({
el: '#root',
data: {
student: {
name: 'tom',
age: 13
},
},
methods: {
add() {
/* 不能直接给data添加数据,会报错
/this.ta = '666'
这样Vue监测不到,不是响应式的
this.student.sex = 'ss1'*/
Vue.set(this.student, 'sex', '男')
this.$delete(this.student,'name')
}
},
})
数组Vue.set或者原生7个方法
<body>
<div id="root">
<ol>
<li v-for="(val,i) in hobby">{{val}}</li>
</ol>
<button @click="add">Vue.set修改数组属性</button>
<button @click="update">利用其他方法</button>
</div>
<script>
const vm = new Vue({
el: '#root',
data: {
hobby: ['抽烟', '喝酒', '打豆豆']
},
methods: {
add() {
Vue.set(this.hobby, 2, '赌博')
},
update() {
// push(),pop(),shift(),unshift(),splice(),sort(),reverse()
this.hobby.push('炸街')
}
},
})
</script>
</body>
12.收集表单数据
还有3个修饰符trim,lazy,number,可以结合文档使用
<div id="root">
<form action="" style="background-color: #ccc;">
账号:<input type="text" v-model="account"><br>
密码:<input type="password" v-model="password"><br>
年龄:<input type="number" v-model.number="age"><br>
性别:男 <input type="radio" v-model="picked" value="male"> 女 <input type="radio" v-model="picked" value="famale"
name="sex" id=""><br>
爱好:抽烟 <input type="checkbox" name="hoddy" v-model="checkedNames" value="抽烟">
喝酒 <input type="checkbox" name="hoddy" v-model="checkedNames" value="喝酒">
烫头 <input type="checkbox" name="hoddy" v-model="checkedNames" value="烫头"> <br>
所属校区:<select v-model="selected">
<option value="">请选择校区</option>
<option value="beijing">北京</option>
<option value="shanghai">上海</option>
<option value="shenzheng">深圳</option>
<option value="wuhan">武汉</option>
</select><br><br>
其他信息: <textarea v-model="message" cols="30" rows="10"></textarea><br><br>
<input type="checkbox" name="auto" v-model="check"> 阅读并接受<a href="http://www.atguigu.com">《用户协议》</a><br>
<input type="submit" value="提交" @click.prevent="demo">
</form>
</div>
<script>
new Vue({
el: '#root',
data: {
account: '',
password: '',
picked: 'male',
checkedNames: [],
selected: '',
message: '',
check: '',
age: ''
},
methods: {
demo() {
//不能这样拿,会报错
//console.log(this.data);
console.log(this._data);
console.log(JSON.stringify(this._data));
}
},
})
</script>
13.过滤器
过滤器只可以用在两个地方:双花括号插值和 v-bind 表达式
前端第3方库
<script src="../vue.js"></script>
<script src="../dayjs.min.js"></script>
</head>
<body>
<div id="root">
<h2>显示格式化后的时间</h2>
<!-- 通过计算属性实现 -->
<h3>现在是:{{fmtTime}}</h3>
<!-- 通过methods方法实现 -->
<h3>现在是:{{getFmtTime()}}</h3>
<!-- 过滤器实现 -->
<h3>现在是:{{time | timeFmt}}</h3>
<!-- 过滤器传参,第一个参数就是time,这是不会变的 -->
<h3>现在是:{{time | timeFmt('YYYY-MM-DD')}}</h3>
<!-- 过滤器是可以叠加的 -->
<h3>现在是:{{time | timeFmt('YYYY-MM-DD') | mySlice}}</h3>
</div>
<div id="main">
<h3 :x="msg |mySlice">666</h3>
</div>
<script>
// 全局过滤器
Vue.filter('mySlice', function (value) {
return value.slice(0, 4)
})
new Vue({
el: '#root',
data: {
time: Date.now()
},
computed: {
fmtTime() {
return dayjs(this.time).format('YYYY-MM-DD HH:mm:ss');
}
},
methods: {
getFmtTime() {
return dayjs(this.time).format('YYYY年MM月DD日 HH:mm:ss');
}
},
//局部过滤器
filters: {
timeFmt(value, str = 'YYYY-MM-DD HH:mm:ss') {
return dayjs(value).format(str);
},
// mySlice(value) {
// return value.slice(0, 4)
// }
}
})
new Vue({
el: '#main',
data: {
msg: '你好,尚硅谷'
}
})
14.指令
v-html,text
<div id="root">
<!-- 会覆盖原内容,且不会解析html -->
<h2 v-text="name">欢迎大家</h2>
<h1>{{name}}</h1>
<h2 v-text="str"></h2>
//与v-text区别是可以识别html
<h2 v-html="str"></h2>
</div>
<script>
new Vue({
el: '#root',
data: {
name: '大白兔',
str: '<h1>你好呀!</h1>'
}
})
v-pre,once
<div id="root">
<h2>{{name}}</h2>
<!-- 只渲染一次,不会随着数据的改变而改变 -->
<h2 v-once>{{name}}</h2>
</div>
<script>
const vm = new Vue({
el: '#root',
data: {
name: '大白兔',
str: '<h1>你好呀!</h1>'
}
})
v-cloak
<style>
[v-cloak] {
display: none;
}
</style>
<body>
<!-- Vue解析之前div中有一个属性,v-cloak -->
<div id="root" v-cloak>
<!-- v-pre跳过这个元素和子元素的编译过程,不会识别模板语法是属性 -->
<h2>Vue其实很简单</h2>
<h2>当前的n值是:{{n}}</h2>
</div>
<script>
setTimeout(function () {
new Vue({
el: '#root',
data: {
n: 0,
}
})
}, 2000)
自定义指令
-
指令定义时不加v-,但使用时要加v-;
-
指令名如果是多个单词,要使用kebab-case命名方式,不要用camelCase命名。且定义的时后哟要‘kebab-case包起来
-
bind():指令与元素成功绑定时,inserted():指令所指元素被插入页面时,update()指令所在模板被重新解析时
-
<div id="root"> <h2>当前的n值是: <span v-text="n"></span></h2> <h2>放大10倍后的n值是: <span v-big="n"></span></h2> <button @click="n++">点我n+1</button> <hr> <input type="text" v-fbind:value="n"> </div> <script> /* 1.指令定义时不加v-,但使用时要加v-; 2.指令名如果是多个单词,要使用kebab-case命名方式,不要用camelCase命名。 //定义全局指令 Vue.directive('fbind',{ //指令与元素成功绑定时(一上来) bind(element,binding){ element.value = binding.value }, //指令所在元素被插入页面时 inserted(element,binding){ element.focus() }, //指令所在的模板被重新解析时 update(element,binding){ element.value = binding.value } }) vue.directives('big', function (element, binding) { console.log('big', this) //注意此处的this是window // console.log('big') element.innerText = binding.value * 10 }) */ new Vue({ el: '#root', data: { n: 1 }, directives: { // 这是简写,只能实现部分功能 // 刚绑定时或指令所在模板被重新解析时 big(element, binding) { element.innerText = binding.value * 10 }, fbind: { //指令与元素成功绑定时(一上来) bind(element, binding) { element.value = binding.value }, //指令所在元素被插入页面时 inserted(element, binding) { // 没有插入的话,focus是无效的 element.focus() }, //指令所在的模板被重新解析时 update(element, binding) { element.value = binding.value } } } })
15.生命周期⭐️
- 创建期间的生命周期函数
- beforeCreate:实例刚在内存中创建出来,此时还没有初始化data和methods属性
- created:实例已经在内存中创建OK,此时data和methods已经创建ok,此时还没有开始编译模板
- beforeMount: 此时已经完成了模板的编译,但还没有挂载到页面中
- mounted:此时,已经将编译好的模板,挂载到了页面指定的容器中显示
- 运行期间的生命周期函数:
- beforeUpdate:状态更新之前执行此函数,此时data中的状态值是最新的,但是界面上显示的数据还是旧的,因为此时还没有重新开始渲染Dom节点
- 这里可以访问到数据和调用方法,但是对数据的更改不会再更新了
- updated:实例更新完毕后调用此函数,此时data中的状态值和界面上显示的数据都已经完成了更新,界面已经被重新1渲染好了
- beforeUpdate:状态更新之前执行此函数,此时data中的状态值是最新的,但是界面上显示的数据还是旧的,因为此时还没有重新开始渲染Dom节点
- 销毁期间的生命周期函数:
- beforeDestory:实例销毁之前调用,在这一步,实例仍完全可用,这里可以访问到数据和调用方法,但是对数据的更改不会再更新了
- destroyed:Vue实例销毁后调用,调用后,Vue实例指示的所有东西都会解除绑定,所有的事件监听器都会被移除,所有的子实例也会被销毁
16.非单文件组件
组件:先创建,后注册,然后使用-------注意:组件里面的data要写成函数的形式
局部组件注册使用
<div id="root">
<h2>{{msg}}</h2>
<hr>
<!-- 3.编写组件标签 -->
<school></school>
<!-- <xuexiao></xuexiao> -->
<hr>
<!-- 组件复用,因为data写的函数式,改其中的值不影响另一个组件 -->
<student></student>
<student></student>
<!-- <xuesheng></xuesheng> -->
</div>
<div id="main">
</div>
<script>
// 创建school组件
const school = Vue.extend({
data() {
return {
schoolName: '尚硅谷',
address: '北京昌平',
}
},
template: `
<div>
<h2>学校名:{{address}}</h2>
<h2>学校地址:{{schoolName}}</h2>
<button @click="showName">点我提示学校名</button>
</div>
`,
methods: {
showName() {
alert(this.schoolName)
}
},
})
// 1.创建学生组件
const student = Vue.extend({
template: `
<div>
<h2>学生姓名:{{studentName}}</h2>
<h2>学生年龄:{{age}}</h2>
</div>
`,
// 组件里面的data必须写成函数的形式
data() {
return {
studentName: '张三',
age: 18
}
}
})
new Vue({
el: '#root',
data: {
msg: '你好啊'
},
// 2注册组件(局部注册)
components: {
school,
student
// xuexiao: school,
// xuesheng: student
}
})
全局组件
<body>
<div id="root">
<hello></hello>
</div>
<hr>
<div id="main">
<hello></hello>
</div>
<script>
const qj = Vue.extend({
data() {
return {
name: 666
}
},
template: `<h2>{{name}}</h2>`
})
Vue.component('hello', qj)
new Vue({
el: '#root',
})
new Vue({
el: '#main'
})
注意事项
- 单文件组件名应该要么始终是单词大写开头 (PascalCase),要么始终是横线连接 (kebab-case)。
- 自闭合组件不能写在Dom模板(html里)
- 对于绝大多数项目来说,在单文件组件和字符串模板中组件名应该总是 PascalCase 的——但是在 DOM 模板中总是 kebab-case 的。
1.关于组件名
一个单词组成:
第一种写法(首字母小写):school
第二种写法(首字母大写):School
多个单词组成:
第一种写法(kebab-case命名):'my-school'
第二种写法(CamelCase命名):MySchool (需要Vue脚手架支持)
备注:
(1).组件名尽可能回避HTML中已有的元素名称,例如:h2、H2都不行。
(2).可以使用name配置项指定组件在开发者工具中呈现的名字。
2.关于组件标签:
第一种写法:<school></school>
第二种写法:<school/>
备注:不用使用脚手架时,<school/>会导致后续组件不能渲染。
- 自闭合组件不能写在Dom模板(html里)
- 对于绝大多数项目来说,在单文件组件和字符串模板中组件名应该总是 PascalCase 的——但是在 DOM 模板中总是 kebab-case 的。
- template里面的内容一定要用一个根元素包起来,比如拿div做根元素把他们包起来
template:
`
<div>
<h2>学校名称:{{schoolName}}</h2>
<h2>学校地址:{{address}}</h2>
</div>
`
- 组件标签名如果定义时设置了name属性则按最终按name的来
组件的嵌套
vm直接管理的app跟组件
<div id="root">
<app></app>
</div>
<script>
// 定义学生1组件
const student = Vue.extend({
name: 'student',
data() {
return {
studentName: '尚硅谷',
age: '18'
}
},
template:
`
<div>
<h2>学生名:{{studentName}}</h2>
<h2>学生年龄:{{age}}</h2>
</div>
`
})
// 定义学生1组件
const hello = Vue.extend({
data() {
return {
msg: '欢迎来到尚硅谷学习'
}
},
template:
`
<h1>{{msg}}</h1>
`
})
// 定义school组件包裹student组件
const school = Vue.extend({
name: 'school',
data() {
return {
schoolName: '尚硅谷',
address: '北京'
}
},
template:
`
<div>
<h2>学校名称:{{schoolName}}</h2>
<h2>学校地址:{{address}}</h2>
<student></student>
</div>
`,
components: {
student
}
})
// 定义根组件App,
const app = Vue.extend({
name: 'app',
template:
`
<div><school></school>
<hello></hello></div>
`,
components: {
school,
hello
}
})
new Vue({
el: '#root',
components: {
app
}
})
</script>
VueComponent
1.school组件本质是一个名为VueComponent的构造函数,且不是程序员定义的,是Vue.extend生成的。
2.我们只需要写<school/>或<school></school>,Vue解析时会帮我们创建school组件的实例对象,即Vue帮我们执行的:new VueComponent(options)。
3.特别注意:每次调用Vue.extend,返回的都是一个全新的VueComponent!!!!
4.关于this指向:
(1).组件配置中:data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是【VueComponent实例对象】。
(2).new Vue(options)配置中:data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是【Vue实例对象】。
5.VueComponent的实例对象,以后简称vc(也可称之为:组件实例对象)。Vue的实例对象,以后简称vm。
==VueComponent.prototype.proto=============Vue.prototype
17.Cli脚手架
自带express,可以直接用
cli创建项目中main.js的render函数
/*
该文件是整个项目的入口文件
*/
//引入Vue,没有写明具体引入vue的哪个东西,默认引入vue.runtime.xxx.js是运行版的Vue,只包含:核心功能;没有模板解析器。
import Vue from 'vue'
//引入App组件,它是所有组件的父组件
import App from './App.vue'
//关闭vue的生产提示
Vue.config.productionTip = false
/*
关于不同版本的Vue:
1.vue.js与vue.runtime.xxx.js的区别:
(1).vue.js是完整版的Vue,包含:核心功能+模板解析器。
(2).vue.runtime.xxx.js是运行版的Vue,只包含:核心功能;没有模板解析器。
2.因为vue.runtime.xxx.js没有模板解析器,所以不能使用template配置项,需要使用
render函数接收到的createElement函数去指定具体内容。
*/
//创建Vue实例对象---vm
new Vue({
el:'#app',
//render函数完成了这个功能:将App组件放入容器中
render: h => h(App),
// render:q=> q('h1','你好啊')
// template:`<h1>你好啊</h1>`,
// components:{App},
})
修改默认配置
Vue.config.js可以修改默认配置
18.ref属性
可以拿到元素或者组件实例
div>
<h1 v-text="msg" ref="title"></h1>
<button @click="showDom" ref="btn">点我输出上方的Dom元素</button>
<School ref="sch"></School>
</div>
</template>
<script>
import School from "./components/School.vue";
export default {
name: "App",
components: { School },
data() {
return {
msg: "欢迎学习Vue",
};
},
methods: {
showDom() {
console.log(this.$refs.btn);
console.log(this.$refs.title);
console.log(this.$refs.sch);
},
},
};
19.props属性父传子⭐️
-
父组件App.vue传值,多个单词用camelCase小驼峰命名法,不写:默认为传字符串
<template> <div> <Student name="李四" sex="女" :age="18"></Student> </div> </template> <script> import Student from "./components/Student.vue"; export default { name: "App", components: { Student }, }; </script> <style> </style>
-
子组件接收值,porps是只读的,接收的值不能直接修改,需要借助变量转接,或者可以改数组对象内部的值,可以深改不能浅改(意思就是不能之间赋值,但是可以改里面的数据)
<template> <div> <h1>{{ msg }}</h1> <h2>学生姓名:{{ name }}</h2> <h2>学生性别:{{ sex }}</h2> <h2>学生年龄:{{ myAge + 1 }}</h2> <button @click="updateAge">修改年龄+1</button> </div> </template> <script> export default { name: "Student", data() { return { msg: "我是一个尚硅谷的学生", // 传过来的值我们直接修改会报错,但是我们可以借助变量修改 myAge: this.age, }; }, methods: { updateAge() { this.myAge++; }, }, //1.简单接收 props: ["name", "sex", "age"], //2.接收的同时对数据进行类型限制 // props: { // name: String, // age: Number, // sex: String, // },3.复杂配置 // props: { // name: { // type: String, // required: true, //name是必要的 // }, // age: { // type: Number, // default: 99, //默认值 // }, // sex: { // type:[Number,String], // required: true, // }, // }, // };
20.mixin
可以把多个组件共用的配置提取出来到 xxx.js中,其他组件可以引入使用,最好不要全局引入
-
mixin.js
export const hunhe = { methods: { showName() { alert(this.name); }, }, mounted() { console.log('你好啊'); }, } export const hunhe2 = { data() { return { x: 100, y: 200 } } }
-
全局引入在main.js中引入,慎用,它会默认影响vm和所有vc
import Vue from 'vue' import App from './App.vue' import { hunhe, hunhe2 } from './mixin' Vue.mixin(hunhe, hunhe2) new Vue({ el: '#app', render: h => h(App) })
-
局部引入
<template> <div> <h2 @click="showName">学生姓名:{{ name }}</h2> <h2>学生性别:{{ sex }}</h2> <h2>{{ x + y }}</h2> </div> </template> <script> import { hunhe, hunhe2 } from "../mixin"; export default { name: "Student", data() { return { name: "刘总", sex: "男", }; }, mixins: [hunhe, hunhe2], };
21.插件
-
定义一个插件plugins.js,里面的功能vm和所有vc可以挑着用
-
本质是包含install方法的一个对象,第一个参数默认为Vue,第2个以后的参数是插件使用者传递的数据
export default { install(Vue, x, y, z) { console.log(x, y, z) //全局过滤器 Vue.filter('mySlice', function (value) { return value.slice(0, 4) }) //定义全局指令 Vue.directive('fbind', { //指令与元素成功绑定时(一上来) bind(element, binding) { element.value = binding.value }, //指令所在元素被插入页面时 inserted(element, binding) { element.focus() }, //指令所在的模板被重新解析时 update(element, binding) { element.value = binding.value } }) //定义混入 Vue.mixin({ data() { return { x: 100, y: 200 } }, }) //给Vue原型上添加一个方法(vm和vc就都能用了) Vue.prototype.hello = () => { alert('你好啊') } } }
-
main.js引入
import Vue from 'vue' import App from './App.vue' // 引入插件 import plugins from './plugins' Vue.use(plugins, 1, 2, 3) new Vue({ el: '#app', render: h => h(App) })
-
其他组件可以直接使用
<template> <div> <h2>学校名称:{{ name | mySlice }}</h2> <h2>学校地址:{{ address }}</h2> <input type="text" v-fbind:value="name" /> </div> </template>
22.scoped样式
让样式在局部生效,防止冲突
23.自动生成id插件
npm i nanoid
import { nanoid } from "nanoid";
export default {
methods: {
add(e) {
// 校验数据
if (!this.title.trim()) return alert("输入不能为空");
// 将用户的输入包装成一个todo对象
const todoObj = { id: nanoid(), title: e.target.value, done: false };
this.addTodo(todoObj);
this.title = "";
24.自定义事件子传父
如果简单的话直接写模板上,$event就是我们所传递的那个参数
this.$emit(“updata_followed”, !this.is_followed);
@updata_followed=“articles.is_followed = $event”
-
App.vue
<template> <div id="app"> <h1>{{ msg }}</h1> <!-- 通过父组件给子组件传递函数类型的props实现:子给父传递数据 --> <School :getSchoolName="getSchoolName"></School> <!-- 通过父组件给子组件绑定一个自定义事件实现:子传父 --> <Student @atguigu.once="getStudentName"></Student> <!-- <Student ref="student"></Student> --> </div> </template> <script> import School from "./components/School.vue"; import Student from "./components/Student.vue"; export default { name: "App", components: { School, Student, }, data() { return { msg: "你好啊!", }; }, methods: { getSchoolName(name) { console.log("App收到了学校名", name); }, getStudentName(name) { console.log("App收到了学生们:", name); }, }, // mounted() { // this.$refs.student.$on("atguigu", this.getStudentName); // }, };
-
student.vue
<<template> <div class="student"> <h2>学生姓名:{{ name }}</h2> <h2>学生年龄:{{ age }}</h2> <button @click="sendStudentName">把学生名给App</button> <button @click="unbind">解绑atguigu事件</button> <button @click="dead">注销组件</button> </div> </template> <script> export default { name: "Student", data() { return { name: "张三", age: 18, }; }, methods: { sendStudentName() { // 触发Student组件实例身上的atguigu事件 this.$emit("atguigu", this.name); this.$emit("demo"); }, unbind() { this.$off("atguigu"); //解绑一个自定义事件 this.$off(["atguigu", "demo"]); //解绑多个自定义事件 this.$off(); //解绑所有的自定义事件 }, dead() { this.$destroy(); }, }, }; </script> <style lang="less" scoped> .student { background-color: pink; padding: 5px; margin-top: 30px; } </style>
25.事件总线
可以实现各组件共享数据,任意组件间通信
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aBUFThc1-1669432222701)(D:新的开始mdVueimage事件总线.jpg)]
main.js
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
Vue.prototype.$bus = new Vue()
const app = new Vue({
render: h => h(App),
// beforeCreate() {
// Vue.prototype.$bus = this //安装全局事件总线
// console.log(this);
// },
}).$mount('#app')
console.log(app === Vue);//false
console.log(app);
console.log(Vue);
全局要用this.$bus.$on
接收的那个组件销毁前要这样
name: "School",
data() {
return {
schoolName: "尚硅谷",
address: "北京昌平",
};
},
mounted() {
this.$bus.$on("share", (val) => {
console.log("我是school组件,拿到了student组件传过来的数据" + val);
});
},
// 要记得销毁前解绑事件
beforeDestroy() {
this.$bus.$off("share");
},
};
</script>
name: "Student",
data() {
return {
name: "张三",
age: 18,
};
},
methods: {
sendStudentName() {
this.$bus.$emit("share", this.name);
},
},
};
26.消息订阅与组件通信
功能与事件总线类似,且各个框架都能用
利用pubsub-js第3方库
先引入库 npm i pubsub-js
- 触发事件方(发送方)
<template>
<div class="student">
<h2>学生姓名:{{name}}</h2>
<h2>学生性别:{{sex}}</h2>
<button @click="sendStudentName">把学生名给School组件</button>
</div>
</template>
<script>
import pubsub from 'pubsub-js'
export default {
name:'Student',
data() {
return {
name:'张三',
sex:'男',
}
},
methods: {
sendStudentName(){
// this.$bus.$emit('hello',this.name)
pubsub.publish('hello',666)
}
},
}
-
绑定方(接收方)
template> <div class="school"> <h2>学校名称:{{name}}</h2> <h2>学校地址:{{address}}</h2> </div> </template> <script> import pubsub from 'pubsub-js' export default { name:'School', data() { return { name:'尚硅谷', address:'北京', } }, mounted() { // console.log('School',this) /* this.$bus.$on('hello',(data)=>{ console.log('我是School组件,收到了数据',data) }) */ //第一个形参默认为事件名 this.pubId = pubsub.subscribe('hello',(msgName,data)=>{ console.log(this) // console.log('有人发布了hello消息,hello消息的回调执行了',msgName,data) }) }, beforeDestroy() { // this.$bus.$off('hello') pubsub.unsubscribe(this.pubId) }, } </script>
27.this.$nextTick()
可以保证页面结构一定是有的,经常和很多插件一起使用(都需要DOM存在了)
handleEdit(todo){
//要走完这些才会渲染到模板上,而走this.$refs.inputTitle.focus()的时候input还没有来到页面上,所以要用nextTick函数或者定时器
if(todo.hasOwnProperty('isEdit')){
todo.isEdit = true
}else{
// console.log('@')
this.$set(todo,'isEdit',true)
}
this.$nextTick(function(){
this.$refs.inputTitle.focus()
})
},
28.axios
axios直接发起get和post请求
<button id="btnGET">GET</button>
<button id="btnPOST">POST</button>
<script src="./lib/axios.js"></script>
<script>
document.querySelector('#btnGET').addEventListener('click', async function () {
/* axios.get('url地址', {
// GET 参数
params: {}
}) */
const { data: res } = await axios.get('http://www.liulongbin.top:3006/api/getbooks', {
params: { id: 1 }
})
console.log(res)
})
document.querySelector('#btnPOST').addEventListener('click', async function () {
// axios.post('url', { /* POST 请求体数据 */ })
const { data: res } = await axios.post('http://www.liulongbin.top:3006/api/post', { name: 'zs', gender: '女' })
console.log(res)
})
</script>
</body>
axios封装默认配置
import axios from 'axios'
const request = axios.create({
// 指定请求的路径
baseURL: 'https://www.escook.cn'
})
export default request
import api from "@/utils/request";
export default function request(_page, _list) {
return api.get('/articles', {
// 请求参数
params: {
_page,
_list
}
})
}
------------------------------------------------------
import getArticle from "../../api/art.js";
methods: {
async init(update) {
const { data: res } = await getArticle(this.page, this.list);
拦截器
import axios from "axios";
// axios的2次封装
// 引入进度条
import nprogress from 'nprogress';
// 引入进度条样式
import 'nprogress/nprogress.css'
console.log(nprogress);
const requests = axios.create({
// 会自动拼接/api
baseURL: '/api',
// timeout: 5000
})
//2、配置请求拦截器
requests.interceptors.request.use(config => {
//config内主要是对请求头Header配置
//比如添加token
//开启进度条
nprogress.start();
return config;
})
//3、配置相应拦截器
requests.interceptors.response.use((res) => {
//成功的回调函数
//响应成功,关闭进度条
nprogress.done()
return res.data;
}, (error) => {
//失败的回调函数
console.log("响应失败" + error)
return Promise.reject(new Error('fail'))
})
//4、对外暴露
export default requests
29.前端跨域解决方案配置代理
如果后端API 接口没有开启CORS 跨域资源共享,接口则无法请求成功
通过proxy跨域代理解决接口的跨域问题
仅在开发调式阶段生效,,项目上线发布时,仍要后端开启cors跨域资源共享
module.exports = {
devServer: {
proxy: {
'/api': {// 匹配所有以 '/api1'开头的请求路径
target: 'http://gmall-h5-api.atguigu.cn',// 代理目标的基础路径
changeOrigin: true,
// pathRewrite: { '^/api': '' }
},
}
}
}
Vue封装的过度与动画
-
作用:在插入、更新或移除 DOM元素时,在合适的时候给元素添加样式类名。
-
写法:
-
准备好样式:
- 元素进入的样式:
- v-enter:进入的起点
- v-enter-active:进入过程中
- v-enter-to:进入的终点
- 元素离开的样式:
- v-leave:离开的起点
- v-leave-active:离开过程中
- v-leave-to:离开的终点
- 元素进入的样式:
-
使用
<transition>
包裹要过度的元素,并配置name属性:<transition name="hello"> <h1 v-show="isShow">你好啊!</h1> </transition>
-
备注:若有多个元素需要过度,则需要使用:
<transition-group>
,且每个元素都要指定key
值。
-
vue脚手架配置代理
方法一
在vue.config.js中添加如下配置:
devServer:{
proxy:"http://localhost:5000"
}
说明:
- 优点:配置简单,请求资源时直接发给前端(8080)即可。
- 缺点:不能配置多个代理,不能灵活的控制请求是否走代理。
- 工作方式:若按照上述配置代理,当请求了前端不存在的资源时,那么该请求会转发给服务器 (优先匹配前端资源)
方法二
编写vue.config.js配置具体代理规则:
module.exports = {
devServer: {
proxy: {
'/api1': {// 匹配所有以 '/api1'开头的请求路径
target: 'http://localhost:5000',// 代理目标的基础路径
changeOrigin: true,
pathRewrite: {'^/api1': ''}
},
'/api2': {// 匹配所有以 '/api2'开头的请求路径
target: 'http://localhost:5001',// 代理目标的基础路径
changeOrigin: true,
pathRewrite: {'^/api2': ''}
}
}
}
}
/*
changeOrigin设置为true时,服务器收到的请求头中的host为:localhost:5000
changeOrigin设置为false时,服务器收到的请求头中的host为:localhost:8080
changeOrigin默认值为true
*/
说明:
- 优点:可以配置多个代理,且可以灵活的控制请求是否走代理。
- 缺点:配置略微繁琐,请求资源时必须加前缀。
实战
接口路径:http://gmall-h5-api.atguigu.cn/api/product/getBaseCategoryList
------------------------------------------------------------------------
const requests = axios.create({
// 发请求时候,路径当中会出现api
baseURL: '/api',
// timeout: 5000
})
------------------------------------------------------------------
module.exports = {
devServer: {
proxy: {
'/api': {// 匹配所有以 '/api1'开头的请求路径
target: 'http://gmall-h5-api.atguigu.cn',// 代理目标的基础路径
changeOrigin: true,
// pathRewrite: { '^/api': '' }
},
}
}
}
----------------------------------------------------------------------
// 当前这个模块,API进行统一管理
import requests from "./request";
import mockRequests from './mockAjax'
export const reqCategoryList = () => {
return requests({ url: 'product/getBaseCategoryList', method: 'get' })
}
最后
以上就是贤惠泥猴桃为你收集整理的Vue2知识点总结及学习心得1.模板语法2.el和data两种写法3.数据绑定4.MVVM模型5.Object.defineProperty6.事件处理⭐️7.method与computed的区别8.watch侦听器8.class和style9.条件渲染11.Vue监测数据⭐️12.收集表单数据13.过滤器14.指令15.生命周期⭐️16.非单文件组件17.Cli脚手架18.ref属性19.props属性父传子⭐️20.mixin21.插件22.scoped样式23.自动生成id插件24.自定义的全部内容,希望文章能够帮你解决Vue2知识点总结及学习心得1.模板语法2.el和data两种写法3.数据绑定4.MVVM模型5.Object.defineProperty6.事件处理⭐️7.method与computed的区别8.watch侦听器8.class和style9.条件渲染11.Vue监测数据⭐️12.收集表单数据13.过滤器14.指令15.生命周期⭐️16.非单文件组件17.Cli脚手架18.ref属性19.props属性父传子⭐️20.mixin21.插件22.scoped样式23.自动生成id插件24.自定义所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复