Vue 基础入门与进阶
简介
1、IDE简介
- IDE(Integrated Development Environment,集成开发环境)是含代码编辑、关键字高亮、智能提示、智能纠错、格式美化等功能于一身的“高级代码编辑器”
- Visual Studio Code 简称 VS Code,来自微软公司
- 优点:内置功能非常丰富、插件全且安装简单、轻量、有MAC版本
- VS Code 下载:https://code.visualstudio.com/
- VS Code 中文配置:在插件中心搜索 Chinese,安装插件即可
- VS Code 颜色主题:在Code->首选项->颜色主题,将主题改为:浅色 + (默认浅色)
- VS Code 集成 Sublime 的快捷键:在插件中心搜索 Sublime,安装插件即可
- 多行编辑:按住鼠标滚轮,然后下拉,即可进行多行编辑
- VS Code 安装 Live Server 插件,这个插件可以让“实时热更新”网页,自动刷新网页
2、在线引用 Vue
<script src = "https://unpkg.com/vue@next"></script>
3、使用 vue-cli 创建项目
- 创建项目
// front 是项目名
vue create front
- 选择 “Manually select features” 手动选择功能(使用上下键控制上下,回车进入下一步)
- 使用空格键确定要选择的功能,一般项目选择如下图
Choose Vue version:选择 Vue 版本
Babel:使用 Babel
Router:使用 Router
Vuex:使用 Vuex
CSS Pre-processors:使用 CSS 预编译
Linter / Formatter:格式化代码
- 选择需要的版本
- 是否使用 history 模式的路由:N
- 选择使用 Sass/SCSS
- 使用 ESLint 作为标准配置
- 选择 Lint on save
- 选择 In dedicated config files
- 选择 N,不保存这个预设
- 出现下图则创建项目成功
- 使用“ cd 项目名”进入项目目录下
- 使用“npm run serve”启动项目
一、Vue 基础语法
1、Vue 的基本结构
- 使用 Vue.createApp({}) 去创建一个应用,存储到 app 变量中
- 传入的参数表示,这个应用最外层的组件应该如何展示
- 将 app对应的应用挂载(只作用)在 id = "root"的元素上
- vm 代表的就是 vue 应用的根组件,其充当了 vm 层的角色
<!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>Document</title>
<script src="https://unpkg.com/vue@next"></script>
</head>
<body>
<div id="root"></div>
</body>
<script>
// 使用 Vue.createApp({}) 去创建一个应用
// 传入的参数表示,这个应用最外层的组件应该如何展示
const app = Vue.createApp({
data() {
return {
msg:"hello world!"
}
},
template:`<div>{{msg}}</div>`
});
// 将 app对应的应用挂载(只作用)在 id = "root"的元素上
// vm 代表的就是 vue 应用的根组件,其充当了 vm层的角色
const vm = app.mount('#root');
</script>
</html>
2、mvvm 设计模式:
- m ->model 数据
- v -> view 视图
- vm -> viewModel 视图数据连接层
3、Vue生命周期函数
- 生命周期函数:在 某一时刻 会 自动 执行的函数。
- 在实例生成之前会自动执行的函数:beforeCreate() {},
- 在实例生成之后会自动执行的函数:created() {},
- 在组件内容被渲染到页面之前会自动执行的函数:beforeMount() {},
- 在组件内容被渲染到页面之后会自动执行的函数:mounted() {},
- 当 data 中的数据发生改变时会立即自动执行的函数:beforeUpdate (){},
- 当 data 中的数据发生变化,同时页面重新完成更新后,会自动执行的函数:updated (){},
- 当 Vue 实例(应用)失效时,自动执行的函数:beforeUnmount() {},
- 当 Vue 实例(应用)失效时,且 dom 完全销毁之后,自动执行的函数:unmounted() {}
// 使用 Vue.createApp({}) 去创建一个应用
// 传入的参数表示,这个应用最外层的组件应该如何展示
const app = Vue.createApp({
data() {
return {
msg:"hello world!"
}
},
// 生命周期函数
// 在实例生成之前会自动执行的函数
beforeCreate() {
},
// 在实例生成之后会自动执行的函数
created() {
},
// 在组件内容被渲染到页面之前会自动执行的函数
beforeMount() {
},
// 在组件内容被渲染到页面之后会自动执行的函数
mounted() {
},
// 当 data 中的数据发生改变时会立即自动执行的函数
beforeUpdate(){
},
// 当 data 中的数据发生变化,同时页面重新完成更新后,会自动执行的函数
updated(){
},
// 当 Vue 实例(应用)失效时,自动执行的函数
beforeUnmount() {
},
// 当 Vue 实例(应用)失效时,且 dom 完全销毁之后,自动执行的函数
unmounted() {
}
});
// 将 app对应的应用挂载(只作用)在 id = "root"的元素上
// vm 代表的就是 vue 应用的根组件,其充当了 vm层的角色
const vm = app.mount('#root');
4、模版语法讲解
- v-html = “”:表示html标签按原html执行,不进行转义为字符串展示
<script>
// 使用 Vue.createApp({}) 去创建一个应用
// 传入的参数表示,这个应用最外层的组件应该如何展示
const app = Vue.createApp({
data() {
return {
msg:"<strong>hello world!</strong>"
}
},
template:`<div v-html="msg"></div>`
});
// 将 app对应的应用挂载(只作用)在 id = "root"的元素上
// vm 代表的就是 vue 应用的根组件,其充当了 vm层的角色
const vm = app.mount('#root');
</script>
- v-bind:(可简写为:“:”):表示对属性进行绑定
<script>
const app = Vue.createApp({
data() {
return {
disabled: true
}
},
template:`<input v-bind:disabled= "disabled" />`
});
const vm = app.mount('#root');
</script>
- v-on:(可简写为:“@”):表示对事件进行绑定
<script>
const app = Vue.createApp({
data() {
return {
message: "弹出",
}
},
methods: {
handleClick() {
alert('click');
}
},
template:`<div v-on:click="handleClick">{{message}}</div>`
});
const vm = app.mount('#root');
</script>
- 动态参数:
动态属性:v-bind:[自定义属性] 自定义属性:"要绑定的属性名"
动态事件:v-on:[自定义事件] 自定义事件:"要绑定的事件名"
<script>
const app = Vue.createApp({
data() {
return {
message: "hello world!",
show: false,
name: 'title',
event: 'click'
}
},
methods: {
handleClick() {
alert('click');
}
},
template:`<div :[name]="message" @[event]="handleClick">{{message}}</div>`
});
const vm = app.mount('#root');
</script>
- v-once:表示在第一次完成渲染之后,后面再改变数据,依旧保持第一次渲染的结果,可以降低无用渲染
<script>
const app = Vue.createApp({
data() {
return {
message: "hello world!"
}
},
template:`<div v-once>{{message}}</div>`
});
const vm = app.mount('#root');
</script>
- v-if = “condition”:表示该标签的内容展不展示由v-if="值"的值决定
- v-else:表示当 if 的条件不成立时执行(v-else 可以不加 condition)
- v-else-if = “condition”:表示当 if 的条件不成立且 else if 的条件成立时执行
<script>
const app = Vue.createApp({
data() {
return {
message: "hello world!",
elseMessage: "elseMessage",
show: false
}
},
template:`
<div v-if="show">{{message}}</div>
<div v-else>{{elseMessage}}</div>
`
});
const vm = app.mount('#root');
</script>
- v-show = “condition”:表示控制该标签内容的展示与否
- v-if 和 v-show 的区别:
v-if 是通过控制 dom 元素的存在与否来控制展示与不展示
v-show 是通过控制 style 样式来控制展示与不展示(style=“display: none;”)
如果频繁的改变 DOM 元素的展示与否,建议使用 v-show,性能会好一些;如果不涉及频繁销毁或创建 DOM,v-if 和 v-show 都差不多 - v-for ="":表示对列表循环渲染
1)普通列表的渲染:v-for="(value,index) in listArray"
2)对象列表的渲染:v-for="(value,key,index) in listObject"
3)在做v-for 时增加 :key="",key值尽量用唯一的,这样可以优化v-for指令,表示在第二次渲染的时候,如果Vue的底层发现某个元素的key值两次是一样的,那么Vue看看之前key值对应的dom元素能不能复用,如果能复用,就不再创建该元素
5、表单中双向绑定指令v-model 与修饰符
- input:在使用 input 进行双向绑定时,就可以不必写 value=""
<script>
const app = Vue.createApp({
data() {
return {
message: 'hello'
}
},
template:`
<div>
{{message}}
<input v-model="message"/>
</div>
`
});
const vm = app.mount('#root');
</script>
- textarea:绑定文本域
<script>
const app = Vue.createApp({
data() {
return {
message: 'hello'
}
},
template:`
<div>
{{message}}
<textarea v-model="message"/>
</div>
`
});
const vm = app.mount('#root');
</script>
- checkbox:使用数组去存
默认选中是 true,不选中是 false;可以使用 true-value="" false-value="" 形式去自定义选中和不选中时展示的内容
<script>
const app = Vue.createApp({
data() {
return {
message: []
}
},
template:`
<div>
{{message}}
A <input type="checkbox" v-model="message" value="A"/>
B <input type="checkbox" v-model="message" value="B"/>
C <input type="checkbox" v-model="message" value="C"/>
</div>
`
});
const vm = app.mount('#root');
</script>
- radio:使用字符串去存
<script>
const app = Vue.createApp({
data() {
return {
message: ''
}
},
template:`
<div>
{{message}}
A <input type="radio" v-model="message" value="A"/>
B <input type="radio" v-model="message" value="B"/>
C <input type="radio" v-model="message" value="C"/>
</div>
`
});
const vm = app.mount('#root');
</script>
- select:下拉选择
<script>
const app = Vue.createApp({
data() {
return {
message: '',
options: [
{value: 'A'},
{value: 'B'},
{value: 'C'},
{value: 'D'},
{value: 'E'}
]
}
},
template:`
<div>
{{message}}
<select v-model="message">
<option v-for="item in options">{{item.value}}</option>
</select>
</div>
`
});
const vm = app.mount('#root');
</script>
v-model 修饰符:
- lazy 修饰符:数据改变的时候不实时跟着改变,当改变完成失去焦点时才改变
- number 修饰符:将数据转为 number 类型再存到绑定的变量
- trim 修饰符:去除内容前后的空格
<script>
const app = Vue.createApp({
data() {
return {
message: 'hello'
}
},
template:`
<div>
{{message}}
<input v-model.lazy="message" />
</div>
`
});
const vm = app.mount('#root');
</script>
6、data & methods & computed & watch
- data:可以REPL环境中通过 vm.$data.“属性” = “值” 的方式改变data 里某一个属性的值(其中 vm = app.mount(’#root’);)
如果属性是一级属性,还可以简写为:vm.“属性” = “值” 的方式 - methods:只要页面重新发生渲染,就会执行重新计算
- computed:只有当计算属性依赖的内容发生变更时,才会重新执行计算(只能同步)
- watch(侦听器):可以侦听data里面数据的变化,当数据变化后才会执行(当异步执行时可以使用)
- methods、computed、watch 三者之间的区别与使用:
computed 和 methods 都能实现的功能,建议使用 computed,因为有类似缓存的功能
computed 和 watch 都能实现的功能,建议使用 computed,因为更加简洁
7、样式绑定
- 在 Vue 里面使用 class 定义样式,可以使用字符串、对象、数组形式(记得class 前加 v-bind)
- 字符串形式:在<style>标签中定义好样式之后,可以直接以字符串的形式引入
- 对象形式:以对象形式时,true 表示使用,false 表示不使用
- 数组形式:可以将需要展示的样式列在数组中
<style>
.red {
color: red;
}
.green {
color: green;
}
</style>
<body>
<div id="root"></div>
</body>
<script>
const app = Vue.createApp({
data() {
return {
message: "hello world!",
classString: 'red',
classObject: {red: true, green: true},
classArray: ['red', 'green']
}
},
template:`<div :class="classObject" >{{message}}</div>`
});
const vm = app.mount('#root');
</script>
- 在 Vue 里面使用 style 定义行内样式,可以直接在标签内使用 style=“color: yellow” 的形式使用,也可以使用字符串、对象形式(记得class 前加 v-bind)行内样式
<body>
<div id="root"></div>
</body>
<script>
const app = Vue.createApp({
data() {
return {
styleString: 'color: red;',
styleObject: {
color: 'green',
background: 'yellow'
}
}
},
template:`
<div style="color: yellow" >hello world!</div>
<div :style="styleString" >hello world!</div>
<div :style="styleObject" >hello world!</div>
`
});
const vm = app.mount('#root');
</script>
- 父子组件间样式传递:当子组件最外层只有一个标签,样式可以写在子组件上或者父组件中调用的子组件标签上
<script>
const app = Vue.createApp({
data() {
return {
styleObject: {
color: 'green',
}
}
},
template:`
<div :style="styleObject" >
hello world
// <demo />
<demo class="red"/>
</div>
`
});
app.component('demo',{
template:`
// <div class="red">one</div>
<div >one</div>
`
})
const vm = app.mount('#root');
</script>
- 父子组件间样式传递:
当子组件最外层有多个标签,样式可以写在子组件上;
或者父组件中调用的子组件标签上,由于样式写在父组件中调用的子组件标签上,导致子组件上不明确哪个标签会使用该样式,所以子组件都会使用相同的样式;如果想单独指定子组件标签的样式,可以使用 :class="$attrs.class" ,表示子组件标签的样式是父组件属性的 class 的值
<script>
const app = Vue.createApp({
data() {
return {
styleObject: {
color: 'green',
}
}
},
template:`
<div :style="styleObject" >
hello world
<demo class="red"/>
</div>
`
});
app.component('demo',{
template:`
<div :class="$attrs.class">one</div>
<div >two</div>
`
})
const vm = app.mount('#root');
</script>
8、事件绑定与修饰符
事件绑定:
- 在 Vue 里面如果想获取原生的事件的话,在函数的第一个参数默认就是原生的事件对象 event
<script>
const app = Vue.createApp({
data() {
return {
counter: 0
}
},
methods: {
handleBtnClick() {
console.log(event);
this.counter += 2;
}
},
template:`
<div>
{{counter}}
<button @click="handleBtnClick">按钮</button>
</div>
`
});
const vm = app.mount('#root');
</script>
- 当事件传有参数并且想同时获取原生的事件对象的话,那么需要的模版里使用 $event 作为参数传给事件函数
<script>
const app = Vue.createApp({
data() {
return {
counter: 0
}
},
methods: {
handleBtnClick(num, event) {
console.log(event);
this.counter += num;
}
},
template:`
<div>
{{counter}}
<button @click="handleBtnClick(2, $event)">按钮</button>
</div>
`
});
const vm = app.mount('#root');
</script>
- 如果绑定事件想执行多个函数,把多个函数用逗号间隔,调用这些函数的时候不能直接写函数的引用了,需要使用圆括号
<script>
const app = Vue.createApp({
data() {
return {
counter: 0
}
},
methods: {
handleBtnClick(num) {
console.log(this.counter += num);
},
handleBtnClick1(num) {
console.log(this.counter += num);
}
},
template:`
<div>
{{counter}}
<button @click="handleBtnClick(1),handleBtnClick1(2)">按钮</button>
</div>
`
});
const vm = app.mount('#root');
</script>
事件修饰符:
- stop 修饰符:阻止事件向外冒泡
- self 修饰符:事件的触发必须是自己的DOM标签触发的才会执行,若是子标签触发的事件不会执行
- prevent 修饰符:阻止默认事件
- capture 修饰符:会将事件由默认的冒泡模式转为捕获模式
- once 修饰符:绑定的事件只执行一次
- passive 修饰符:
<script>
const app = Vue.createApp({
data() {
return {
counter: 0
}
},
methods: {
handleDivClick() {
alert('div clicked');
},
handleBtnClick() {
this.counter += 1;
}
},
template:`
<div>
<div @click="handleDivClick">
{{counter}}
<button @click.stop="handleBtnClick()">按钮</button>
</div>
</div>
`
});
const vm = app.mount('#root');
</script>
按键修饰符:
- enter 修饰符:当按下键盘的 enter 键时才会触发事件
- tab 修饰符:当按下键盘的 tab 键时才会触发事件
- delete 修饰符:当按下键盘的 delete 键时才会触发事件
- esc 修饰符:当按下键盘的 esc 键时才会触发事件
- up 修饰符:当按下键盘的 up 键时才会触发事件
- down 修饰符:当按下键盘的 down 键时才会触发事件
- left 修饰符:当按下键盘的 left 键时才会触发事件
- right 修饰符:当按下键盘的 right 键时才会触发事件
- ......
<script>
const app = Vue.createApp({
methods: {
handleKeyDown() {
console.log('keydown');
}
},
template:`
<div>
<input @keydown.enter="handleKeyDown" />
</div>
`
});
const vm = app.mount('#root');
</script>
鼠标修饰符:
- left 修饰符:当按下鼠标的左键时才会触发事件
- right 修饰符:当按下鼠标的右键时才会触发事件
- middle 修饰符:当按下鼠标的滚轮键时才会触发事件
<script>
const app = Vue.createApp({
methods: {
handleClick() {
console.log('鼠标按键');
}
},
template:`
<div>
<div @click.left="handleClick">鼠标按键</div>
</div>
`
});
const vm = app.mount('#root');
</script>
精确修饰符:
- exact 修饰符:当没有这个修饰符时,只要按有对应的键就能触发事件(即按住对应的键和其它键也能触发事件,只要按有对应的键);当加上 exact 修饰符,就必须只能按住对应的键,多按其他键也不会触发事件
<script>
const app = Vue.createApp({
methods: {
handleClick() {
console.log('按键');
}
},
template:`
<div>
<div @click.ctrl.exact="handleClick">按键</div>
</div>
`
});
const vm = app.mount('#root');
</script>
二、Vue 组件
1、组件的定义及使用、局部组件和全局组件
- 组件的概念:将一个复杂的应用拆分成很多子组件去完成,再由许多小组件组成一个完整的应用
- 组件的特点:具备复用性,组件内的数据是被当前组件所独享的,不会被其它同名组件共享
<script>
const app = Vue.createApp({
data() {
return {
message: 'hello'
}
},
template:`
<div>
<counter/>
<counter/>
</div>
`
});
app.component('counter',{
data() {
return {
count: 1
}
},
template: `<div @click="count += 1">{{count}}</div>`
})
const vm = app.mount('#root');
</script>
- 全局组件:只要定义了,随处可以使用,由于无论用不用到都会挂载在Vue上,所以性能不高,但使用起来简单,名字建议是小写字母单词,多个单词间用“-”分隔app.component (‘lgk-test’,{})
<script>
const app = Vue.createApp({
data() {
return {
message: 'hello'
}
},
template:`
<div>
<counter-parent/>
<counter-parent/>
</div>
`
});
app.component('counter-parent',{
template: `<counter />`
})
app.component('counter',{
data() {
return {
count: 1
}
},
template: `<div @click="count += 1">{{count}}</div>`
})
const vm = app.mount('#root');
</script>
- 局部组件:定义后需要用 components 注册后才能使用,性能比较高,但使用起来有些麻烦,组件名建议大写字母开头,中间使用驼峰命名,局部组件使用时,要做一个名字和组件间的映射对象,如果不写,Vue 底层也会自动尝试帮做映射
<script>
const Counter = {
data() {
return {
count: 1
}
},
template: `<div @click="count += 1">{{count}}</div>`
}
const HelloWorld = {
template: `<div>hello world</div>`
}
const app = Vue.createApp({
components: {
Counter: Counter,
HelloWorld: HelloWorld
},
template:`
<div>
<counter-parent/>
<counter-parent/>
<Counter />
<Counter />
<HelloWorld />
<HelloWorld />
</div>
`
});
app.component('counter-parent',{
components: {
Counter: Counter
},
template: `<Counter />`
})
const vm = app.mount('#root');
</script>
2、组件间传值及传值校验
- 组件间传值:父组件调用子组件的标签,然后通过标签上的属性向子组件传递值,子组件通过 props 属性接收父组件传过来的内容,然后子组件就能使用
- 静态传值:一般直接通过属性进行传值,传入的是写死的值
<script>
const app = Vue.createApp({
template:`
<div>
<test content="hello world!" />
</div>
`
});
app.component('test',{
props: ['content'],
template: `<div>{{content}}</div>`
})
const vm = app.mount('#root');
</script>
- 动态传值:根据 data 中的值的变化使用 v-bind 动态绑定属性,然后传入到子组件中
<script>
const app = Vue.createApp({
data() {
return {
message: 'hello world!'
}
},
template:`
<div>
<test :content="message" />
</div>
`
});
app.component('test',{
props: ['content'],
template: `<div>{{content}}</div>`
})
const vm = app.mount('#root');
</script>
- 传值校验:子组件可以对父组件传过来的值进行校验,可以校验的类型有 String、Number、Boolean、Array、Object、Function、Symbol …
- required 属性:值为 true 或 false,表示是否要求必须传值
- default 属性:值为默认值,表示不传值时默认值为多少(默认值也可以为函数)
- validator 校验:如果值为 true 则通过校验,否则不通过校验
<script>
const app = Vue.createApp({
data() {
return {
message: 123
}
},
template:`
<div>
<test :content="message" />
</div>
`
});
app.component('test',{
props: {
content: {
type: Number,
// required: true,
validator: function(value) {
return value < 1000;
},
default: 0
}
},
template: `<div >{{content}}</div>`
})
const vm = app.mount('#root');
</script>
- 当传有多个参数时,可以用对象的形式传,其中 v-bind=“params” 等价于 :a=“params.a” :b=“params.b” :c="params.c"
<script>
const app = Vue.createApp({
data() {
return {
params: {
a: 11,
b: 22,
c: 33
}
}
},
template:`
<div>
<!-- <test :="params" /> 等同于下面 -->
<test v-bind="params" />
<!-- 等价于 -->
<test :a="params.a" :b="params.b" :c="params.c" />
</div>
`
});
app.component('test',{
props: ['a','b','c'],
template: `<div >{{a}}-{{b}}-{{c}}</div>`
})
const vm = app.mount('#root');
</script>
- 在 HTML 中以 data-param 这种属性命名方式传值的时候,要使用驼峰式 dataParam 形式接
<script>
const app = Vue.createApp({
data() {
return {
param: 1232
}
},
template:`
<div>
<test :data-param="param" />
</div>
`
});
app.component('test',{
props: ['dataParam'],
template: `<div >{{dataParam}}</div>`
})
const vm = app.mount('#root');
</script>
3、单项数据流的理解
- 子组件可以使用父组件传递过来的数据,但绝对不能直接修改传递过来的数据
- 如果子组件想要修改数据,可以将父组件传过来的值赋给子组件的某个属性,然后改变子组件的属性值从而达到修改数据的目的
<script>
const app = Vue.createApp({
data() {
return {
count: 1
}
},
template:`
<div>
<test :count="count" />
</div>
`
});
app.component('test',{
props: ['count'],
data() {
return {
myparam: this.count
}
},
methods: {
handleClick() {
this.myparam += 1;
}
},
template: `<div @click="handleClick">{{myparam}}</div>`
})
const vm = app.mount('#root');
</script>
- 为什么有单项数据流:假设父组件多次使用同一个子组件时,当某个子组件改变父组件传过去的值,就会影响父组件的值,同时也会影响其它子组件的值,这就导致子组件间相互耦合
4、Non-props 属性
- 父组件向子组件传递数据,子组件不通过 props 来接收,此时底层会把父组件传递过来的内容放在子组件最外层的 dom 标签下,变成子组件最外层 dom标签的属性
- 若是不想子组件最外层dom标签展示传过来的数据,可以加 inheritAttrs: false 特性,表示不继承父组件传递过来的这些 Non-props 属性
<script>
const app = Vue.createApp({
template:`
<div>
<test msg="hello" />
</div>
`
});
app.component('test',{
inheritAttrs: false,
template: `<div >hello world</div>`
})
const vm = app.mount('#root');
</script>
- Non-props 属性一般用于对子组件样式修饰
<script>
const app = Vue.createApp({
template:`
<div>
<test style="color: red" />
</div>
`
});
app.component('test',{
// inheritAttrs: false,
template: `<div >hello world</div>`
})
const vm = app.mount('#root');
</script>
- v-bind="$attrs" 表示把父组件传递过来的所有 props 属性都放到具有v-bind="$attrs"的这个元素上
- 如果想得到某个Non-props特性,可以通过 :属性名="$attrs.某个属性名"获取到
<script>
const app = Vue.createApp({
template:`
<div>
<test style="color: red" msg1="hello" msg2="world" />
</div>
`
});
app.component('test',{
template: `
<div v-bind:style="$attrs.style" :msg1="$attrs.msg1">one</div>
<div v-bind:msg1="$attrs.msg1">two</div>
<div v-bind:msg2="$attrs.msg2">three</div>
<div v-bind="$attrs">four</div>
`
})
const vm = app.mount('#root');
</script>
5、父子组件间如何通过事件进行通信
- 父组件向子组件传递参数,子组件通过触发事件改变参数:
- 子组件方法中通过 this.$emit(‘方法名’[,params]) 向父组件触发事件(该方法名采用驼峰命名,也可以加一或多个参数)
- 或者也可以在子组件中加:emits:[‘要触发的方法’]表示子组件向外触发事件(emits 也可以是一个对象)
- 父组件通过 @‘方法名’=‘父组件自定义方法名’ 接受事件(此处的‘方法名’如果由多个单词组成,单词间用“-”分隔)
<script>
const app = Vue.createApp({
data(){
return {
count:1
}
},
methods: {
// handleAddOne(){
handleAddOne(param){
// this.count += 1
this.count += param
}
},
template:`
<div>
<counter :count = "count" @add-one = "handleAddOne"/>
</div>
`
});
app.component('counter',{
props:['count'],
// emits:['addOne'],
methods: {
handleClick(){
// this.$emit('addOne');
this.$emit('addOne',3);
}
},
template:`
<div @click = "handleClick">{{count}}</div>
`
});
const vm = app.mount("#root");
</script>
如果父子组件存在双向绑定的时候:
- 接收的参数必须为:modelValue 这是固定写法;
- 触发事件的名字一定是:update:modelValue 也是固定写法;
- 如果想改变接收参数的名字,把modelValue 改为‘自定义名称’,则把传入参数改为:v-model:自定义名称;
- v-model=“属性” 绑定属性的形式只能使用一次,原因是只有一个 modelValue;若想使用多次,必须改为 v-model:自定义名称1=“属性” v-model:自定义名称2=“属性” 的形式,然后子组件就可以使用 props: [‘自定义名称1’, ‘自定义名称2’] 的方式接受参数
<script>
const app = Vue.createApp({
data(){
return {
count:1
}
},
template:`
<div>
<!-- <counter v-model='count'/> -->
<counter v-model:app='count'/>
</div>
`
});
app.component('counter',{
// props:['modelValue'],
props:['app'],
methods: {
handleClick(){
// this.$emit('addOne');
// this.$emit('update:modelValue',this.modelValue + 3);
this.$emit('update:app',this.app + 3);
}
},
template:`
<!-- <div @click = "handleClick">{{modelValue}}</div> -->
<div @click = "handleClick">{{app}}</div>
`
});
const vm = app.mount("#root");
</script>
6、插槽、具名插槽及作用域插槽解决组件内容传递问题
- 插槽slot:如果父组件想向子组件传一些 dom 节点或元素标签,可以直接把这些元素标签写在组件标签里就可以了,子组件通过<slot></slot>标签调用传过来的元素标签即可。
- slot 是不能直接绑定事件的,但可以在外层包一个<span></span>标签,然后在标签里绑定事件
- 插槽里不仅可以传标签,还可以传字符串、子组件
<script>
const app = Vue.createApp({
template:`
<div>
<myform>
<div>提交</div>
</myform>
<myform>
<button>提交</button>
</myform>
</div>
`
});
app.component('myform',{
methods: {
handleClick(){
alert(123)
}
},
template:`
<input />
<span @click="handleClick">
<slot></slot>
</span>
`
});
const vm = app.mount("#root");
</script>
slot中使用的数据作用域的问题:
- 父模版里调用的数据属性,使用的都是父模版里的数据
- 子模版里调用的数据属性,使用的都是子模版里的数据
具名插槽:
- 将插槽拆成几个部分,每个部分在子组件分开调用
- 在父组件中用<template v-slot:自定义名称>内容</template>的方式定义小块(其中 “v-slot:” 可以用“#”替换)
- 在子组件用<slot name=“自定义名称”></slot>的方式接收
<script>
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("#root");
</script>
作用域插槽:
- 作用域插槽解决了:当父组件需要使用子组件的数据时,可以通过作用域插槽将子组件的数据传给父组件使用
- 子组件在 <slot> 标签内绑定属性进行参数传递,父组件在使用子组件时使用 v-slot="" 接收参数
<script>
const app = Vue.createApp({
template:`
<test v-slot="slotProps">
<div>{{slotProps.value}}-{{slotProps.index}}</div>
</test>
<!-- 也可以用解构的形式 -->
<!--
<test v-slot="{value}">
<div>{{value}}</div>
</test>
-->
`
});
app.component('test',{
data() {
return {
lists: [1,2,3]
}
},
template:`
<div>
<slot v-for="(item,index) in lists" :value = "item" :index="index" />
</div>
`
});
const vm = app.mount("#root");
</script>
7、动态组件和异步组件
- 动态组件:根据数据变化,结合 <component :is=“切换条件值” /> 这个标签来实现组件动态切换
- 若是要保存切换前的数据到切换后还存在,可以利用<keep-alive> <component :is=“切换条件值” />包裹
<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';
}
}
},
template:`
<keep-alive>
<component :is="currentItem" />
</keep-alive>
<!-- <component :is="currentItem" /> 等价于
<input-item v-show="currentItem === 'input-item'" />
<common-item v-show="currentItem === 'common-item'" />
-->
<button @click="handleClick">切换</button>
`
});
app.component('input-item',{
template:`
<div>
<input />
</div>
`
});
app.component('common-item',{
template:`
<div>hello world</div>
`
});
const vm = app.mount("#root");
</script>
- 异步组件:异步执行某些组件的逻辑
<script>
const app = Vue.createApp({
template:`
<common-item />
<async-common-item />
`
});
app.component('async-common-item',Vue.defineAsyncComponent(() => {
return new Promise((resolve,reject) => {
setTimeout(() => {
resolve({
template: `<div>this is a async component</div>`
});
},5000)
})
}));
app.component('common-item',{
template:`
<div>hello world</div>
`
});
const vm = app.mount("#root");
</script>
8、基础知识查缺补漏
- v-once: 让某个元素标签值渲染一次
- ref: 获取 dom 节点或者组件引用的一个语法。可以在想要获取的 dom标签上加 ref=“自定义名称”,然后等待挂载结束在mounted(){this.$refs.自定义名称}获取到这个dom节点
- provide/inject:多级组件传值
假设父组件要传值到孙组件,且子组件不需要这个值,这时可以在父组件定义一个provide:{变量:值}存放要传的值,然后在孙组件用inject:[‘变量’]接收
如果想要传的是data里面的参数值,可以将provide定义为函数的形式:
provide(){
return {
变量: this.‘data里的变量名’
}
}
但是这种方式传值只能执行一次,并不是双向绑定的(也就是会说,如果父组件的值改变了,孙组件的不会变)
三、Vue 实现动画
过渡: 缓慢变化的过程称为过渡效果
动画: 由一个效果迅速转换为另一个效果
四、Vue 的高级语法
1、mixin 混入的基础语法
- 组件 data、methods 优先级高于 mixin 里 data、methods 的优先级
- 生命周期函数,先执行 mixin 里面的,再执行组件里面的
- 全局的 mixin 不用 mixins: [mixin] 形式引入就能直接使用,但不建议使用全局的 mixin
<script>
const myMixin = {
data() {
return {
number: 2
}
},
created() {
console.log('mixin created');
},
methods: {
handleClick() {
this.number += 1;
console.log('mixin: ' + this.number);
}
},
};
const app = Vue.createApp({
data() {
return {
number: 1
}
},
created() {
console.log('created');
},
methods: {
handleClick() {
this.number += 1;
console.log('component: ' + this.number);
}
},
mixins: [myMixin],
template:`
<div @click="handleClick">{{number}}</div>
`
});
// // 全局的 mixin
// app.mixin({
// data() {
// return {
// number: 2,
// count: 22
// }
// },
// created() {
// console.log('mixin created');
// },
// methods: {
// handleClick() {
// this.number += 1;
// console.log('mixin: ' + this.number);
// }
// }
// });
const vm = app.mount("#root");
</script>
- 自定义属性(如下例中的 number),组件中的属性优先级高于 mixin 属性的优先级
- 由于自定义属性不属于 data、methods、computed等,所以需要使用 “this.$options.自定义属性” 方式调用
- 也可以通过下例中的方式使 mixin 中自定义属性 number 的优先级高于 组件 中自定义属性 number 的优先级
<script>
const myMixin = {
number: 1
};
const app = Vue.createApp({
number: 2,
mixins: [myMixin],
template:`
<div >{{this.$options.number}}</div>
`
});
// 可以通过这种方式使 mixin 中自定义属性 number 的优先级高于 组件 中自定义属性 number 的优先级
// app.config.optionMergeStrategies.number = (mixinValue,appValue) => {
// return mixinValue || appValue;
// };
const vm = app.mount("#root");
</script>
2、实现 Vue 自定义指令(directive)
- 定义局部指令时需要使用 directives 进行注册指令,类似于使用 components 注册组件
- 自定义指令如果接收参数,可以使用生命周期函数的第二个参数(binding,第一个参数是:el)接收,然后通过 binding.value 就可以获取到使用指令时传入的值
- 如果指令绑定某个属性(如:v-focus:hello=“value”),可以通过 binding.arg 获取到属性 hello
- 一般使用 mounted 生命周期函数,原因是当挂载好之后才开始起作用(也可以使用其它生命周期函数,具体情况具体分析)
- 定义全局指令
<script>
// 全局指令
const app = Vue.createApp({
template:`
<div >
<input v-focus />
</div>
`
});
app.directive('focus',{
mounted(el) {
el.focus();
}
});
const vm = app.mount("#root");
</script>
- 定义局部指令
<script>
// 局部指令
const lgkFocus = {
focus: {
mounted(el) {
el.focus();
}
}
};
const app = Vue.createApp({
directives: lgkFocus,
template:`
<div >
<input v-focus />
</div>
`
});
const vm = app.mount("#root");
</script>
3、teleport 传送门功能
- 使用 teleport 标签可以将该标签下的 dom 元素直接传送到其它位置进行展示
<body>
<div id="root"></div>
<div id="hello"></div>
</body>
<script>
const app = Vue.createApp({
data() {
return {
message: 'hello world'
}
},
template:`
<div >
<div>原来的</div>
<teleport to="#hello">
<div>{{message}}</div>
</teleport>
</div>
`
});
const vm = app.mount("#root");
</script>
4、render 函数
5、插件(plugin)的定义和使用
- plugin 插件也就是把一些通用的功能封装起来
- 插件的定义:必须有 install() 方法,该方法里面有两个参数,第一个参数是 Vue 的实例,第二个参数是使用插件传入的参数对象
- 插件是使用:通过 Vue 的实例打点调用 use() 方法,其中 use() 方法的第一个参数是插件的名字,第二个参数是将要传的实参
<script>
const myPlugin = {
install(app,options) {
app.provide('name','hello lgk');
app.directive('lgk-focus', {
mounted(el) {
el.focus();
}
});
app.config.globalProperties.$sayHello = 'hello world!';
}
};
const app = Vue.createApp({
template:`
<lgk-test />
<input v-lgk-focus />
`
});
app.component('lgk-test',{
inject: ['name'],
mounted() {
console.log(this.$sayHello);
},
template: `
<div>{{name}}</div>
`
});
app.use(myPlugin, {name: 'lgk'});
const vm = app.mount("#root");
</script>
五、Composition API
1、Setup 函数的使用
- setup() 函数在 created 实例被完全初始化之前执行
- setup() 函数中 return 出去的内容会被暴露在外部,在外部的 template 模版里可以直接使用暴露出来的内容
- setup() 函数中不能使用其外部的东西,但它外部却可以使用它 return 出去的内容
<script>
const app = Vue.createApp({
setup(props,context) {
return {
message: 'hello world'
}
},
template:`
<div>{{message}}</div>
`
});
const vm = app.mount("#root");
</script>
2、ref、reactive、toRefs 响应式引用的用法和原理
- 响应式引用的原理:通过 proxy 对数据进行封装,当数据变化时,触发模版等内容的更新
- ref 处理基础类型的数据
- 在使用 ref 处理数据时,将新值赋值给变量必须赋给的是 变量.value
- reactive 处理非基础类型的数据
- 在使用 reactive 处理数据时,对象类型的直接赋值给对应的对象属性,数组类型的直接赋值给对应的索引位置
<script>
const app = Vue.createApp({
setup(props,context) {
// const {ref} = Vue;
// let message = ref('message');
// // 使用 proxy 会将 'message' 变成 proxy({value: 'message'}) 这样的一个响应式引用
// setTimeout(() => {
// message.value = 'hello world';
// },3000);
// return {
// message
// }
const {reactive} = Vue;
let message = reactive({name: 'zhangsan', age: 12, sex: 'male'});
// 使用 proxy 会将 {name: 'zhangsan', age: 12, sex: 'male'} 变成 proxy({name: 'zhangsan', age: 12, sex: 'male'}) 这样的一个响应式引用
setTimeout(() => {
message.name = 'hello world';
message.age = 18;
},3000);
return {
message
}
},
template:`
<div>{{message.name}}</div>
`
});
const vm = app.mount("#root");
</script>
- readonly 表示将某个属性变为只读,不能被修改
<script>
const app = Vue.createApp({
setup(props,context) {
const {reactive, readonly} = Vue;
let message = reactive({name: 'zhangsan', age: 12, sex: 'male'});
const messageCopy = readonly(message.name);
setTimeout(() => {
message.name = 'hello world';
message.age = 18;
},3000);
return {
message
}
},
template:`
<div>{{message}}</div>
`
});
const vm = app.mount("#root");
</script>
- 如果想将值以解构的方式返回出去,可以使用 toRefs
<script>
const app = Vue.createApp({
setup(props,context) {
const {reactive, toRefs} = Vue;
let message = reactive({name: 'zhangsan', age: 12, sex: 'male'});
setTimeout(() => {
message.name = 'hello world';
message.age = 18;
},3000);
// toRefs 原理:proxy 会将 proxy({name: 'zhangsan', age: 12, sex: 'male'})
// 变为 {name: proxy({value: 'zhangsan'}), age: proxy({value: 12}), sex: proxy({value: 'male'})} 的形式
const {name,age,sex} = toRefs(message);
return {
message,name,age,sex
}
},
template:`
<div>{{message.name}}-{{name}}</div>
`
});
const vm = app.mount("#root");
</script>
3、toRef 和 setup()函数中的 context 参数
- toRef 相比于 toRefs 可以在响应式对象中添加默认响应式引用
- 如果对象里没有的属性,又可能出现该属性,想将该属性具备响应式特性时,可以使用 toRef
<script>
const app = Vue.createApp({
setup(props,context) {
const {reactive, toRef} = Vue;
let message = reactive({name: 'zhangsan', age: 12});
const sex = toRef(message,'sex');
return {
sex
}
},
template:`
<div>{{sex}}</div>
`
});
const vm = app.mount("#root");
</script>
- setup()函数中的 context 参数:context 里面有 3 个值,分别为:attrs、slots、emit
- attrs 指的是父组件传递到子组件的 Non-props 属性
- slots 指的是插槽
- emit 指的是子组件触发事件让父组件去完成对应的方法
<script>
const app = Vue.createApp({
methods: {
handleChangeClick() {
alert('handleChangeClick');
}
},
template:`
<div>
<child @change="handleChangeClick">
content
</child>
</div>
`
});
app.component('child',{
template:`
<div @click="handleClick">123</div>
`,
setup(props, context) {
const {attrs, slots, emit} = context;
// console.log(attrs.param);
// const {h} = Vue;
// console.log(slots.default());
// return () => h('div',{},slots.default())
function handleClick() {
emit('change');
};
return {
handleClick
}
}
});
const vm = app.mount("#root");
</script>
4、computed 方法生成计算属性
- Composition API 中 computed 的使用和之前的差不多,只不过需要从 Vue 中引入
<script>
const app = Vue.createApp({
setup() {
const {reactive, computed} = Vue;
let countObj = reactive({count: 0});
// let countComputed = computed(() => {
// return countObj.count + 5;
// });
let countComputed = computed({
get: () => {
return countObj.count + 5;
},
set: () => {
countObj.count = 12;
}
});
const handleClick = () => {
countObj.count += 1;
// countObj.count = 12;
};
return {countComputed,countObj,handleClick}
},
template:`
<div @click="handleClick">
{{countObj.count}}---- {{countComputed}}
</div>
`
});
const vm = app.mount("#root");
</script>
5、watch 和 watchEffect 的使用和差异性
watch 侦听器:
- 具备一定的惰性 lazy,即首次加载的时候不执行,只有在输入新内容的时候才会执行
- watch 侦听器函数有两个参数,第一个是要侦听的属性,第二个参数是回调函数
- watch 也可以变为非惰性,可以在 watch 的第三个参数中将 immediate 设置为 true
watch(要侦听的数据,回调函数,{immediate: true})
- 回调函数参数可以拿到当前值和原始值,回调函数中第一个参数是当前值,第二个参数是原始值
- 当侦听的是 reactive 这种数据,watch 的第一个参数要写为函数的形式
<script>
const app = Vue.createApp({
setup() {
const {watch} = Vue;
const {ref,reactive,toRefs} = Vue;
// const msg = ref('');
// watch(msg,(currentValue,prevValue) => {
// console.log(currentValue,prevValue);
// });
const message = reactive({msg: ''})
const {msg} = toRefs(message);
watch(() => message.msg,(currentValue,prevValue) => {
console.log(currentValue,prevValue);
});
return {
msg
}
},
template:`
<div>
<div>
输入:<input v-model="msg" />
</div>
<div>
输入的内容为:{{msg}}
</div>
</div>
`
});
const vm = app.mount("#root");
</script>
- watch 侦听器不仅可以侦听单个参数,也可以侦听多个参数,只不过多个参数要放在数组中
- watch 可以侦听多个数据的变化,用一个侦听器承载
<script>
const app = Vue.createApp({
setup() {
const {watch} = Vue;
const {ref,reactive,toRefs} = Vue;
// const msg = ref('');
// watch(msg,(currentValue,prevValue) => {
// console.log(currentValue,prevValue);
// });
const message = reactive({msg: '',hello: ''})
const {msg,hello} = toRefs(message);
watch([() => message.msg,() => message.hello],([currentMsgValue,currentHelloValue],[prevMsgValue,prevHelloValue]) => {
console.log(currentMsgValue,prevMsgValue,'====',currentHelloValue,prevHelloValue);
});
return {
msg,
hello
}
},
template:`
<div>
<div>
输入:<input v-model="msg" />
</div>
<div>
输入的内容为:{{msg}}
</div>
<div>
输入:<input v-model="hello" />
</div>
<div>
输入的内容为:{{hello}}
</div>
</div>
`
});
const vm = app.mount("#root");
</script>
watchEffect 侦听器:
- watchEffect 立即执行,没有惰性
- 如果侦听的数据没有变化,则不执行
- 不需要传递需要侦听的内容,会自动感知代码依赖
- 不需要传递很多参数,只需要传递一个回调函数
- watchEffect 不能获取数据之前的值
<script>
const app = Vue.createApp({
setup() {
const {watch,watchEffect} = Vue;
const {ref,reactive,toRefs} = Vue;
// const msg = ref('');
// watch(msg,(currentValue,prevValue) => {
// console.log(currentValue,prevValue);
// });
const message = reactive({msg: '',hello: ''})
const {msg,hello} = toRefs(message);
// watch([() => message.msg,() => message.hello],([currentMsgValue,currentHelloValue],[prevMsgValue,prevHelloValue]) => {
// console.log(currentMsgValue,prevMsgValue,'====',currentHelloValue,prevHelloValue);
// });
watchEffect(() => {
console.log(message.msg);
console.log(message.hello);
});
return {
msg,
hello
}
},
template:`
<div>
<div>
输入:<input v-model="msg" />
</div>
<div>
输入的内容为:{{msg}}
</div>
<div>
输入:<input v-model="hello" />
</div>
<div>
输入的内容为:{{hello}}
</div>
</div>
`
});
const vm = app.mount("#root");
</script>
6、生命周期函数的新写法
- Composition API 的生命周期函数中没有原来 beforeCreate 和 Created 对应的生命周期函数,原因是 setup() 函数执行的时间点就在 beforeCreate、Created 两个生命周期函数之间,如果想写在这两个生命周期函数中的内容可以之间写在 setup() 函数中即可
- beforeMount 等价于 onBeforeMount
- mounted 等价于 onMounted
- beforeUpdate 等价于 onBeforeUpdate
- updated 等价于 onUpdated
- beforeUnmount 等价于 onBeforeUnmount
- unmounted 等价于 onUnmounted
- Composition API 新增了两个生命周期函数:onRenderTracked 和 onRenderTriggered
- onRenderTracked 是在每次渲染后收集响应式依赖会自动执行的函数
- onRenderTriggered 每次触发页面重新渲染时会自动执行的函数
<script>
const app = Vue.createApp({
// beforeMount <=> onBeforeMount
// mounted <=> onMounted
// beforeUpdate <=> onBeforeUpdate
// updated <=> onUpdated
// beforeUnmount <=> onBeforeUnmount
// unmounted <=> onUnmounted
setup() {
const {onBeforeMount, onMounted,onBeforeUpdate,onUpdated,onBeforeUnmount,onUnmounted,onRenderTracked,onRenderTriggered} = Vue;
const {ref} = Vue;
const msg = ref('hello');
onBeforeMount(() => {
console.log("onBeforeMount");
});
onMounted(() => {
console.log("onMounted");
});
onBeforeUpdate(() => {
console.log("onBeforeUpdate");
});
onUpdated(() => {
console.log("onUpdated");
});
onBeforeUnmount(() => {
console.log("onBeforeUnmount");
});
onUnmounted(() => {
console.log("onUnmounted");
});
onRenderTracked(() => {
console.log("onRenderTracked");
});
onRenderTriggered(() => {
console.log("onRenderTriggered");
});
const handleClick = () => {
msg.value = 'message'
}
return {
msg,
handleClick
}
},
template:`
<div @click="handleClick">hello world!---{{msg}}</div>
`
});
const vm = app.mount("#root");
</script>
7、provide、inject、模版 ref 的用法
- provide 和 inject 搭配使用,常用于跨多层组件之间的传值
- provide 的参数是键值对的形式,且键值对用逗号分隔(值也可以是函数)
- inject 的参数是传过来的 key,也可以设置第二个参数为默认值
<script>
const app = Vue.createApp({
setup() {
const {provide} = Vue;
provide('key','value');
return {}
},
template:`
<child />
`
});
app.component('child',{
template: `
<child-child />
`
});
app.component('child-child',{
template: `
<div>{{test}}</div>
`,
setup() {
const {inject} = Vue;
const test = inject('key','默认值');
// const test = inject('key1','默认值');
return {
test
}
}
});
const vm = app.mount("#root");
</script>
- Composition API 的语法下获取真实的 DOM 元素节点
- const hello = ref(null); 是固定写法,表示创建一个空的响应式对象,其中的 hello 是模版中 ref 的值
- 模版中的 ref 表示的是获取 DOM 节点的引用,ref(null) 中的 ref 表示的是获取响应式的引用
<script>
const app = Vue.createApp({
setup() {
const {ref, onMounted} = Vue;
const hello = ref(null);
onMounted(() => {
console.log(hello.value);
})
return {
hello
}
},
template:`
<div ref="hello">hello world</div>
`
});
const vm = app.mount("#root");
</script>
六、Vue 项目开发配套工具
1、Vue Cli 的使用和单文件组件
(1)安装node.js环境
- node.js安装:https://nodejs.org/en/download/
(2)基于node.js安装淘宝镜像
- 在 node 环境终端安装 nrm,然后再使用淘宝镜像源
192:~ $ node -v
v15.12.0
192:~ $ npm -v
7.6.3
192:~ $ npm install nrm -g
npm WARN deprecated har-validator@5.1.5: this library is no longer supported
npm WARN deprecated mkdirp@0.3.5: Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.)
npm WARN deprecated request@2.88.2: request has been deprecated, see https://github.com/request/request/issues/3142
npm WARN deprecated coffee-script@1.7.1: CoffeeScript on NPM has moved to "coffeescript" (no hyphen)
added 61 packages, and audited 315 packages in 1m
14 packages are looking for funding
run `npm fund` for details
found 0 vulnerabilities
npm notice
npm notice New minor version of npm available! 7.6.3 -> 7.12.1
npm notice Changelog: https://github.com/npm/cli/releases/tag/v7.12.1
npm notice Run npm install -g npm@7.12.1 to update!
npm notice
192:~ $ nrm ls
* npm -------- https://registry.npmjs.org/
yarn ------- https://registry.yarnpkg.com/
cnpm ------- http://r.cnpmjs.org/
taobao ----- https://registry.npm.taobao.org/
nj --------- https://registry.nodejitsu.com/
npmMirror -- https://skimdb.npmjs.com/registry/
edunpm ----- http://registry.enpmjs.org/
192:~ $ nrm use taobao
Registry has been set to: https://registry.npm.taobao.org/
192:~ $ nrm ls
npm -------- https://registry.npmjs.org/
yarn ------- https://registry.yarnpkg.com/
cnpm ------- http://r.cnpmjs.org/
* taobao ----- https://registry.npm.taobao.org/
nj --------- https://registry.nodejitsu.com/
npmMirror -- https://skimdb.npmjs.com/registry/
edunpm ----- http://registry.enpmjs.org/
192:~ $
- 或者直接使用命令安装淘宝镜像源
npm install -g cnpm --registry=https://registry.npm.taobao.org
(3)删除 vue-cli 并安装 @vue/cli
- 删除老版本脚手架,Vue 2.x 版本脚手架使用的是:vue-cli 形式
npm uninstall vue-cli -g
- 如果本地有 yarn,还需运行如下命令
yarn global remove vue-cli
- 开始安装最新版 vue-cli,Vue 3.x 版本脚手架使用的是:@vue/cli 形式
npm install -g @vue/cli
- 检查安装是否成功(查看安装版本)
vue -V
(4)利用脚手架创建新项目
- 进入项目所要安装目录下,然后执行如下命令创建项目
vue create demo
- 运行项目,首先先进入 demo 文件中,然后就可以运行项目了
cd demo
// 注意是 serve 不是 server
npm run serve
2、Vue-Router 的理解和使用
- 路由是指根据 URL 的不同,展示不同的内容
- router-link 是跳转路由的标签
<router-link to="/">home</router-link>
<router-link to="/about">About</router-link>
- router-view 负责展示当前路由对应的组件内容
<router-view />
- 异步加载路由
{
path: '/',
name: 'Home',
component: () => import(/* webpackChunkName: "home" */'../views/home/Home')
}
- 路由守卫实现基础登陆校验功能
- 每一个路由项下面都可以加 beforeEnter() 函数,这个函数在进入这个路由之前会被执行
- router 上整体可以加一个 beforeEach() 函数,这个函数会在路由每次进行切换的时候执行
import { createRouter, createWebHashHistory } from 'vue-router'
import Home from '../views/home/Home'
import Login from '../views/login/Login'
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/login',
name: 'Login',
component: Login,
beforeEnter(to, from, next) {
// const isLogin = localStorage.isLogin
// if (isLogin) {
// next({ name: 'Home' })
// } else {
// next()
// }
const { isLogin } = localStorage
isLogin ? next({ name: 'Home' }) : next()
}
}
]
const router = createRouter({
history: createWebHashHistory(),
routes
})
router.beforeEach((to, from, next) => {
// console.log(to, from)
// const isLogin = localStorage.isLogin
// if (isLogin || to.name === 'Login') {
// next()
// } else {
// next({ name: 'Login' })
// }
const { isLogin } = localStorage;
(isLogin || to.name === 'Login') ? next() : next({ name: 'Login' })
})
export default router
- 在 vue-router 里面提供一个 useRouter() 方法,可以通过该方法获取 router 实例
- 如果在 JS 代码里面想做路由的一些跳转,可以通过路由实例的 push() 方法(router.push())实现跳转到下一个页面
<script>
import { useRouter } from 'vue-router'
export default {
name: 'Login',
setup() {
const router = useRouter()
const handleLogin = () => {
localStorage.isLogin = true
router.push({ name: 'Home' })
}
return {
handleLogin
}
}
}
</script>
3、VueX 的语法详解
- VueX 是数据管理框架
import { createStore } from 'vuex'
export default createStore({
state: {
message: 'hello'
},
mutations: {
},
actions: {
},
modules: {
}
})
- VueX 中 state 创建了全局唯一的仓库,用来存放全局的数据
- 组件中使用 state 中的 数据
{{this.$store.state.message}}
修改 VueX 中全局数据的步骤:
- 第一步,通过 dispatch 方法派发一个方法(change)到 actions
this.$store.dispatch('change');
- 第二步,actions 感知到 change 这个方法,执行 store 中 actions 下面的 change 方法
- 第三步,commit 提交一个叫做 change 方法触发 mutations
actions: {
change() {
this.commit('change');
}
},
- 第四步,mutation 感知到提交的 change 改变,执行 change 方法
- 第五步,在 mutations 中的 change 方法中 改变数据
mutations: {
change() {
this.state.message = 'hello world';
}
},
- 对于同步执行的数据,可以不用 dispatch 派发方法到 actions,可以直接 commit 提交到 mutations 去执行修改数据
this.$store.commit('change');
- 在 VueX ,默认 mutations 里面只允许写同步代码,不允许写异步代码,如果想写异步代码,可以写在 actions 里面
- 在 actions 和 mutations 中的方法若是有参数,actions 里面方法的第一个参数是 store,第二个才是传入的参数;mutations 里面方法的第一个参数是 state,第二个参数才是传入的参数
mutations: {
change(state,param) {
state.message = param;
}
},
actions: {
change(store,param) {
store.commit('change',param);
}
},
4、在 Composition API 中使用 VueX
<script>
import {useStore} from 'vuex';
import {toRefs} from 'vue';
export default {
name: 'Home',
setup() {
const store = useStore();
// const message = store.state.message;
const {message} = toRefs(store.state);
return {
message
}
}
}
</script>
5、使用 axios 发送 Ajax 请求
- 首先安装 axios
npm install axios --save
// yarn add axios
- 然后就可以引用
import axios from 'axios';
6、基础样式集成及开发模拟器的使用
(1)统一不同浏览器之间的显示差异
- 安装 normalize.css
npm install normalize.css --save
- 在 main.js 中引入 normalize.css,并且在 src 目录下创建一个 style/base.scss 文件
import 'normalize.css'
import './style/base.scss'
// base.scss 文件
// 1 rem = html fontsize
html {
font-size: 100px;
}
(2)在 scss 环境下提取公共样式
- 在 src 目录下创建一个 style/viriables.scss 文件,文件中的公告样式名前加 $ 符
// 例如
$content-fontcolor: #333;
$content-bgColor: #f1f1f1;
$search-bgColor: #F5F5F5;
- 在需要使用的文件中的 <style> 标签中使用 @import 引入样式即可
<style lang="scss" scoped>
@import '../style/viriables.scss';
// 样式
</style>
七、项目目录结构
- node_modules:用于存放依赖包,如果被删除了,可以在终端控制台输入:npm install 进行安装
- index.html:整个项目的默认模版
- favicon.ico:用于显示在浏览器页面 tab 栏,在 index.html 文件中的 <head>标签中引用
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
- .browserslistrc:用于配置浏览器相关的配置
> 1% // 打包过程尽量支持全球范围内用户使用量大于 1% 的浏览器
last 2 versions // 支持最新两个版本浏览器
not dead // 仍在维护的浏览器
- .editorconfig:用于配置编辑器的默认配置
[*.{js,jsx,ts,tsx,vue}]
indent_style = space
indent_size = 2 // 使用 tab 键时使用2个空格作为 tab 的间距
trim_trailing_whitespace = true // 去除多余的空白
insert_final_newline = true // 在每个文件的末尾创建新的一行
- .eslintrc.js:使用 Eslint 时的相关配置
- babel.config.js:主要用于配置 vue 里面用到 Babel 的配置
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
]
}
- package-lock.json:保证多人共同协作开发时,反复安装依赖使用固定版本
- package.json:用于放置项目配置相关的依赖
- README.md:项目描述性的文本内容
- src 是项目源代码目录
- main.js:是整个项目的入口文件
- App.vue 组件:
- assets 目录:用于放一些静态文件
- component 目录:放一些公共的组件
- router 目录:定义路由相关的配置
- store 目录:放置 VueX 相关的数据
- views 目录:放置页面级别的组件
八、虚拟 DOM(Virtual DOM)和 diff
- vdom 是实现 Vue 和 React 的重要基石
- diff 算法是 vdom 中最核心、最关键的部分
- vdom 是一个热门话题,也是面试中的热门问题