1.1 搭建Vue开发环境
- 项目导入开发版本文件
<script type="text/javascript" src="../js/vue.js"></script>
-
浏览器安装插件Vue Devtools
-
去除Vue版本提示
<script>
Vue.config.productionTip = false
</script>
1.2 Hello案例
在根目录放置 favicon.ico 文件即可在标签中显示
一般推荐在配置结束后再开始 Vue 组件
- 想让Vue工作,就必须创建一个Vue实例,且要传入一个配置对象;
- root容器里的代码依然符合html规范,只不过混入部分Vue语法;
- root容器里的代码称为【Vue模板】;
- Vue实例和容器是一一对应的;
- 真实开发中只有一个Vue实例,并且会配合着组件一起使用;
- {{xxx}}中的xxx要写js表达式,且xxx可以自动读取到data中的所有属性;
- 一旦data中的数据发生改变,那么模板中用到该数据的地方也会自动更新;
<body>
<!-- 准备一个容器 -->
<div id="root">
<h1>Hello,{{name}},{{Date.now()}}</h1>
</div>
<script>
Vue.config.productionTip = false
// 创建Vue实例
new Vue({
el: '#root',
data: {
name: 'HMS Home'
}
})
</script>
</body>
1.3 模板语法
- 插值语法
功能:用于解析标签体内容
写法:{{xxx}},xxx是js表达式,且可以直接读取data中的所有属性
- 指令语法
功能:用于解析标签(包括:标签属性、标签体内容、绑定事件……)
写法:v-???=“xxx”,xxx同样要写js表达式
<body>
<div id="root">
<h1>插值语法</h1>
<h3>Hello,{{name}}</h3>
<hr>
<h1>指令语法</h1>
<a :href="school.url">{{school.name}}的博客</a>
</div>
</body>
<script>
Vue.config.productionTip = false
new Vue({
el: '#root',
data: {
name: "HMS Home",
school: {
name: "HMS Home",
url: "https://blog.csdn.net/Winchester_Y?spm=1010.2135.3001.5421"
}
}
})
</script>
1.4 数据绑定
Vue中有2种数据绑定的方式:
- 单向绑定(v-bind):数据只能从data流向页面
- 双向绑定(v-model):数据会 data<=>页面
- 双向绑定只能运用在表单类元素上(如:input、select等)
- v-model:value可以简写为 v-model,因为v-model默认收集的就是value值
<body>
<div id="root">
<!-- 单向数据绑定:<input type="text" v-bind:value="name"><br>
双向数据绑定:<input type="text" v-model:value="name"> -->
单向数据绑定:<input type="text" :value="name"><br>
双向数据绑定:<input type="text" v-model="name">
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
new Vue({
el: '#root',
data: {
name: 'HMS Home'
}
})
</script>
1.5 el和data的两种写法
-
el有2种写法
- new Vue时配置el属性
- 先创建Vue实例,再通过xxx.$mount(‘#root’)指定el值
-
data有2种写法
-
对象式
-
函数式
学到组件时,data必须使用函数式,否则会报错
-
-
一个重要的原则
由Vue管理的函数,一定不要写箭头函数
<body>
<div id="root">
你好,{{name}}
</div>
</body>
<script>
Vue.config.productionTip = false
// const v = new Vue({
// // el: '#root',
// data: {
// name: 'HMS Home'
// }
// })
// console.log(v);
// v.$mount('#root')
new Vue({
// data的第一种写法:函数式
el: '#root',
// data: {
// name: 'HMS Home'
// }
data: function () {
return {
name: 'HMS Home'
}
}
})
</script>
1.6 MVVM模型
- M:模型(model):data中的数据
- V:视图(View):模板代码
- VM:视图模型(ViewModel):Vue实例
观察发现
- data中所有的属性,都出现在了vm身上
- vm身上所有的属性以及Vue原型上所有的属性,在Vue模板中都可以直接使用
1.7 数据代理
1.8 事件处理
1.8.1事件修饰符
- prevent:阻止默认事件(常用)
- stop:阻止事件冒泡(常用)
- once:事件只触发一次
- 修饰符可以连续写
<body>
<div id="root">
<h2>欢迎来到{{name}}的博客</h2>
<a href="https://blog.csdn.net/Winchester_Y?spm=1010.2135.3001.5421" @click.prevent="showInfo">查看</a>
<div class="demo1" @click="showInfo">
<button @click.stop="showInfo">点击</button>
</div>
<button @click.once="showInfo">点击</button>
<div class="box1" @click="showMsg(1)">
div1
<div class="box2" @click.stop="showMsg(2)">
div2
</div>
</div>
</div>
</body>
<script>
Vue.config.productionTip = false
new Vue({
el: '#root',
data: {
name: 'HMS Home'
},
methods: {
showInfo() {
alert('你好')
},
showMsg(msg) {
console.log(msg);
}
}
})
</script>
1.8.2键盘事件
- Vue常用按键
- 回车 enter
- 删除 delete
- 退出 esc
- 空格 space
- 换行 tab(特殊,必须搭配keydown使用)
- 上 up
- 下 down
- 左 left
- 右 right
- Vue未提供的按键可以使用按键原始的key值去绑定,但注意格式
- 键盘也可以连着写,说明同时按下会发生的操作
<body>
<div id="root">
<input type="text" placeholder="键盘事件" @keydown.tab="showInfo">
</div>
</body>
<script>
Vue.config.productionTip = false
new Vue({
el: '#root',
data: {
},
methods: {
showInfo(e) {
console.log(e.key, e.keyCode)
}
}
})
</script>
1.9 计算属性-computed
- 定义:要用的属性不存在,要通过已有属性计算得来
- 原理:底层借助了Object.defineproperty方法提供的getter和setter
- get函数什么时候执行?
- 初次执行时会执行一次
- 当以来的数据发生改变时会被再次调用
- 优势:与methods实现相比,内部有缓存机制,效率更高
- 备注:
- 计算属性最终会出现在vm上,直接读取即可
- 如果计算属性要被修改,那么必须写set函数去响应修改,且set中要引起计算时依赖的数据发生
<body>
<div id="root">
姓:<input type="text" v-model:value="xing"><br>
名:<input type="text" v-model:value="ming"><br>
你的名字:{{fullName}}
</div>
</body>
<script>
Vue.config.productionTip = false
var vm = new Vue({
el: '#root',
data: {
xing: '',
ming: ''
},
methods: {
},
computed: {
fullName: {
get() {
return this.xing + ' ' + this.ming
},
set(value) {
const arr = value.split(' ');
this.xing = arr[0];
this.ming = arr[1]
}
}
}
})
</script>
1.10 监视属性-watch
- 当被监视的属性变化时,回调函数自动调用,进行相关操作
- 监视的属性必须存在,才能进行监视!!
- 监视的两种写法:
- new Vue时传入watch配置
- 通过vm.$watch监视
<body>
<div id="root">
<h2>天气很{{weather}}</h2>
<button @click="changeWeather">切换天气</button>
</div>
</body>
<script>
Vue.config.productionTip = false
new Vue({
el: '#root',
data: {
isHot: true
},
methods: {
changeWeather() {
this.isHot = !this.isHot
}
},
computed: {
weather() {
return this.isHot ? '炎热' : '凉爽'
}
},
watch: {
isHot: {
handler(newValue, oldValue) {
console.log('isHot被修改了', newValue, oldValue);
}
}
}
})
</script>
1.10.1深度监视
- 深度监视:
- Vue中的watch默认不监测对象内部值得改变(一层)
- 配置deep:true可以监测对象内部值改变(多层)
- 备注:
- Vue自身可以监测对象内部值的改变,但Vue提供的watch默认不可以
- 使用watch时根据数据的具体结构,决定是否采用深度监测
numbers: {
deep: true,
handler() {
console.log('numbers被改变了');
}
}
1.10.2computed和watch区别
- computed和watch之间的区别
- computed能完成的功能,watch都可以完成
- watch能完成的功能,computed不一定能完成,例如:异步操作
- 两个重要的小原则
- 所被Vue管理的函数,最好写成普通函数,这样this的指向才是vm或组件实例对象
- 所有不被Vue管理的函数(定时器的回调函数,ajax的回调函数等),最好写成箭头函数,这样this的指向才是vm或组件实例对象
1.11 条件渲染
-
v-if
-
写法
- v-if=“表达式”
- v-else-if=“表达式”
- v-else=“表达式”
适用于:切换频率较低的场景
特点:不展示的DOM元素直接被移除
注意:可以搭配template使用,可以在不破坏结构的条件下做修改
-
-
v-show
- 写法:v-show=“表达式”
- 适用于:切换频率较高
- 特点:不展示的DOM元素未被移除,仅仅是使用样式隐藏掉
<body>
<div id="root">
<h2 v-if="a">你好,{{name}}</h2>
<button @click="ifShow">显示/隐藏</button>
</div>
</body>
<script>
Vue.config.productionTip = false
new Vue({
el: '#root',
data: {
name: 'HMS Home',
a: true
},
methods: {
ifShow() {
this.a = !this.a
}
}
})
</script>
1.12 列表渲染
1.12.1基本列表
v-for指令
- 用于展示列表数据
- 语法:v-for=“(item,index) in xxx” :key=“yyy”
- 可遍历:数组、对象、字符串(用得少)、指定次数(用得少)
<body>
<div id="root">
<!-- 遍历数组 -->
<h2>复刻的部分舰船</h2>
<ul>
<li v-for="(p,index) in persons" :key="p.id">
{{p.name}}-{{p.rare}}
</li>
</ul>
</div>
</body>
<script>
Vue.config.productionTip = false
new Vue({
el: '#root',
data: {
persons: [
{ id: '001', name: 'HMS Howe', rare: 3 },
{ id: '002', name: 'HMS Perseus', rare: 3 },
{ id: '003', name: 'HMS Valiant', rare: 2 },
]
},
methods: {
}
})
</script>
1.12.2key作用与原理
- 用index作为key可能会引发的问题:
- 若对数据进行:逆序添加、逆序删除等破坏顺序操作==>会产生没有必要的真实DOM更新
- 如果结构包含输入类DOM:会产生错误DOM更新
- 开发中如何选择key
- 最好每天数据有唯一的标识作为key
- 如果不存在逆序操作等破坏顺序的操作,用index作为key是没问题的
1.12.3总结
Vue监视数据的原理:
-
Vue会监视data中所有层次的数据
-
如何监视对象中的数据?
- 通过setter实现,且要在new Vue时就传入要监测的数据
- 对象中后追加的属性,Vue默认不做响应式处理
- 如需给后添加的属性做响应式,请使用如下API
- Vue.set(target,propertyName/index,value)
- vm.$set(target,propertyName/index,value)
- 通过setter实现,且要在new Vue时就传入要监测的数据
-
如何监测数组中的数据
- 通过包裹数组更新元素的方法实现,本质就是做了两件事
- 调用原生对应的方法对数组进行更新
- 重新解析模板,进而更新页面
- 通过包裹数组更新元素的方法实现,本质就是做了两件事
-
在Vue修改数组中的某个元素一定要用如下方法
- 使用这些API:push()、pop()、shift()、unshift()、splice()、sort()、reverse()
- Vue.set() 或 vm.$set()
- 特别注意:Vue.set()和vm.$set()不能给vm或vm的根数据对象添加属性!!!
<body>
<div id="root">
<h1>舰船信息</h1>
<button @click="people.rare++">稀有度+1</button><br>
<button @click="addCamp">添加默认主阵营,默认值碧蓝航线</button><br>
<button @click="people.camp='赤色中轴'">修改主阵营</button><br>
<button @click="addOthers">在列表首位添加一个其他舰娘</button><br>
<button @click="changeOthers">修改第一个舰娘的名字为:小加加</button><br>
<button @click="addMengdian">添加第一个萌点</button><br>
<button @click="changeMengdian">修改第一个萌点为:小饼干</button><br>
<button @click="removeMengdian">过滤掉萌点中的一脸嫌弃</button><br>
<h3>名称:{{people.name}}</h3>
<h3>稀有度:{{people.rare}}</h3>
<h3 v-if="people.camp">主阵营:{{people.camp}}</h3>
<h3>萌点:</h3>
<ul>
<li v-for="(h,index) in people.mengdian" :key="index">
{{h}}
</li>
</ul>
<h3>皇家的其他角色</h3>
<ul>
<li v-for="(f,index) in people.others" ::key="index">
{{f.name}}-{{f.rare}}
</li>
</ul>
</div>
</body>
<script>
Vue.config.productionTip = false
const vm = new Vue({
el: '#root',
data: {
people: {
name: 'HMS Home',
rare: 3,
mengdian: ['连身袜', '半掌手套', '女子力爆表', '单纯的贵族小姐', '一脸嫌弃'],
others: [
{ name: 'HMS Perseus', rare: '3' },
{ name: 'HMS Valiant', rare: '2' }
]
}
},
methods: {
addCamp() {
Vue.set(this.people, 'camp', '碧蓝航线')
},
addOthers() {
this.people.others.unshift({ name: '樫野', rare: '2' })
},
changeOthers() {
this.people.others[0].name = "萨拉托加"
},
addMengdian() {
this.people.mengdian.unshift('撩舰')
},
changeMengdian() {
Vue.set(this.people.mengdian, 0, '小饼干')
},
removeMengdian() {
this.people.mengdian = this.people.mengdian.filter((h) => {
return h !== '一脸嫌弃'
})
}
}
})
</script>
1.13 收集表单数据
-
,则v-model收集的是value值,用户输入的就是value值
-
,则v-model收集的是value值,要给标签配置value值
-
- 没有配置input的value属性,那么收集的就是checked
- 配置input的value属性
- v-model的初始值是非数组,那么收集的就是checked
- v-model的初始值是数组,那么收集的的就是value组成的数组
备注:v-model的三个修饰符:
- lazy:失去焦点再收集数据
- number:输入字符串转为有效的数字
- trim:输入首尾空格过滤
1.14 过滤器
1.15 内置指令
1.16 自定义指令
1.17 生命周期
1.17.1引出生命周期
生命周期:
- 又名:生命周期回调函数、生命周期函数、生命周期钩子
- 是什么:Vue在关键时刻帮我们调用的一些特殊名称的函数
- 生命周期函数的名字不可更改,但函数的具体内容是程序员根据需求编写的
- 生命周期函数中的this指向的是vm 或 组件实例对象
<body>
<div id="root">
<h2 :style="{opacity}">{{name}}</h2>
</div>
</body>
<script>
Vue.config.productionTip = false
const vm = new Vue({
el: '#root',
data: {
name: 'HMS Home',
opacity: 1,
},
mounted(){
setInterval(() => {
this.opacity -= 0.01;
if (this.opacity <= 0) vm.opacity = 1
}, 16)
}
})
</script>
1.17.2挂载流程
- 常用的生命周期钩子
- mounted:发送ajax请求、启动定时器、绑定自定义事件、订阅信息等
- beforeDestory:清楚定时器、解绑自定义事件、取消订阅消息等
- 关于销毁Vue实例
- 销毁后Vue开发者工具看不到任何消息
- 销毁后自定义事件会失效,但原生DOM事件任然生效
- 一般不会在beforeDestory操作数据,因为即使操作,也不会触发更新
2.1 模块与组件
Vue中使用组件的三大步骤:
- 定义组件(创建组件)
- 使用Vue.extend(options)创建,其中options和new Vue(options)时传入的那个options几乎一样,但也有点区别:
- el不要写,为什么? —— 最终所有的组件都要经过一个vm的管理,由vm中的el决定服务哪个容器
- data必须写成函数,为什么? —— 避免组件被复用时,数据存在引用关系
- 使用Vue.extend(options)创建,其中options和new Vue(options)时传入的那个options几乎一样,但也有点区别:
- 注册组件
- 局部注册:靠new Vue的时候传入components选项
- 全局注册:靠Vue.component(‘组件名’,组件)
- 编写组件标签:
- <组件名></组件名>
<body>
<div id="root">
<!-- 第三步:编写组件标签 -->
<hello></hello>
<hr>
<character></character>
<hr>
<game></game>
</div>
</body>
<script>
Vue.config.productionTip = false
// 第一步:创建组件
const character = Vue.extend({
template: `
<div>
<h2>角色名称:{{characterName}}</h2>
<h2>公会名称:{{sociaty}}</h2>
<button @click="name">点我弹出角色名</button>
</div>
`,
data() {
return {
characterName: '佩可莉姆',
sociaty: '美食殿堂'
}
},
methods: {
name() {
alert(this.characterName)
}
},
})
const game = Vue.extend({
template: `
<div>
<h2>游戏名称:{{gameName}}</h2>
<h2>厂商名称:{{company}}</h2>
</div>
`,
data() {
return {
gameName: '赛马娘',
company: 'Cygames'
}
}
})
const hello = Vue.extend({
template: `
<div>
<h2>{{hello}}</h2>
</div>
`,
data() {
return {
hello: 'hello'
}
}
})
// 第二步:注册组件(全局注册)
Vue.component('hello', hello)
new Vue({
el: '#root',
// 第二步:注册组件(局部注册)
components: {
character: character,
game: game,
}
})
</script>
2.2 几个注意点
- 关于组件名:
- 一个单词组成:
- 第一种写法(首字母小写):school
- 第二种写法(首字母大写):School
- 多个单词组成:
- 第一种写法(kebab-case命名):my-school
- 第二种写法(CamelCase命名):MySchool (需要Vue脚手架支持)
- 备注:
- 组件名尽可能回避HTML中已有的元素名称,例如:h2、H2都不行
- 可以使用name配置项指定组件在开发者工具中呈现的名字
- 一个单词组成:
- 关于组件标签:
- 第一种写法:
- 第二种写法:
- 备注:不用使用脚手架时,会导致后续组件不能渲染
- 一个简写方式:
- const school = Vue.extend(options) 可简写为:const school = options
2.3 组件的嵌套
<body>
<div id="root">
</div>
</body>
<script>
Vue.config.productionTip = false
const game = Vue.extend({
template: `
<div>
<h2>游戏名称:{{gameName}}</h2>
<h2>厂商名称:{{company}}</h2>
</div>
`,
data() {
return {
gameName: '赛马娘',
company: 'Cygames'
}
}
})
const character = Vue.extend({
template: `
<div>
<h2>角色名称:{{characterName}}</h2>
<h2>公会名称:{{sociaty}}</h2>
<hr>
<game/>
<hr>
</div>
`,
data() {
return {
characterName: '佩可莉姆',
sociaty: '美食殿堂'
}
},
components: {
game
}
})
const hello = {
template: `
<div>
<h2>{{msg}}</h2>
</div>
`,
data() {
return {
msg: 'HMS Home'
}
}
}
const app = {
template: `
<div>
<character/>
<hello/>
</div>
`,
components: {
character,
hello
}
}
new Vue({
el: '#root',
template: `<app/>`,
components: {
app
}
})
</script>
2.4 VueComponent
关于VueComponent:
- school组件本质是一个名为VueComponent的构造函数,且不是程序员定义的,是Vue.extend生成的
- 我们只需要写或,Vue解析时会帮我们创建school组件的实例对象,即Vue帮我们执行的:new VueComponent(options)
- 特别注意:每次调用Vue.extend,返回的都是一个全新的VueComponent
- 关于this指向:
- 组件配置中:data函数、methods中的函数、watch中的函数、computed中的函数中的this均是【VueComponent实例对象】
- new Vue(options)配置中:data函数、methods中的函数、watch中的函数、computed中的函数中的this均是【Vue实例对象】
- VueComponent的实例对象,以后简称vc(也可称之为:组件实例对象);Vue的实例对象,以后简称vm