vue是根据数据来构建用户界面的一套框架
创建一个vue实例
<!--
1.创建一个容器
2.引入vue.js开发版本(全局的)
3.创建实例对象
4.配置选项 => 完成渲染
-->
<div id="app">
{{ msg }}
</div>
<script src='vue.js'></script>
<script>
const app = new Vue({
el:"#app",
data:{
msg: 'hello vue'
}
})
</script>
插值表达式 {{ }}
利用表达式进行插值,渲染到页面。所谓表达式就是有结果的一段代码。
语法:{{ 表达式 }}
注意:
1.使用的数据必须存在(data)
2.标签属性中不能使用
vue指令
指令:带有v-前缀的特殊标签属性
动态的解析标签:v-html=“表达式”->innerHTML
v-show和v-if
作用:控制元素的显示和隐藏
v-show=“true/false”,使用display:none实现的,频繁切换显示隐藏的场景
v-if=“true/false”,通过判断条件来创建或移除元素节点,要么显示要么隐藏不频繁切换的场景
v-else和v-else-if
作用:辅助v-if进行判断渲染
v-on
作用:注册事件 = 添加监听 + 提供处理逻辑
语法:
v-on:事件名=“内联语句”
v-on:事件名=“methods中的函数名”
@事件名= “ ”
注意:methods里的函数的this指向创建的实例,如果要传参数这样写:@click=“fn(参数1,参数2)”
v-bind
作用:动态的设置html的标签属性 -> src url title…
语法:v-bind:属性名=“表达式”,有省略的写法 :属性名=“表达式”
v-for
作用:基于数据循环,多次渲染整个元素 - > 数组、对象、数字…
语法:v-for=“(item每一项,index下标) in 数组”
v-for里面的key,给列表项添加的唯一标识符,便于vue进行列表项的正确排序复用
:key=“唯一标识符”
v-model
作用:给表单元素使用,双向数据绑定 -> 可以快速获取或者设置表单元素内容
v-model = '变量‘
指令修饰符
通过 “.” 指明一些指令后缀,不同后缀封装了不同的处理操作 -> 简化代码
eg:
@keyup.enter 键盘回车监听
v-model.trim 去除首位空格
v-model.number 转数字
@事件名.stop 阻止冒泡
@事件名.prevent 阻止默认行为
v-bind对于样式控制的增强 - 操作class
:class=“对象/数组”
对象:键是类名,值为true有这个类,否则没有这个类
<div class="box" :class="{类名1: 布尔值,类名2: 布尔值}"></div>
数组:数组中所有的类,都会添加到盒子上,本质上就是一个class列表
<div class="box" :class="[类名1,类名2]"></div>
计算属性
基于现有的数据,计算出来的新属性。依赖的数据发生变化,自动重新计算。
computed: {
计算属性名 () {
//基于现有的数据,编写求值逻辑
return 结果
}
}
computed计算属性 vs methods方法
computed 计算属性:封装了一段对数据的处理,求得一个结果
- 写在 computed 配置项中
- 作为属性,直接使用 -> this.计算属性 {{ 计算属性 }}
methods 方法:给实例提供一个方法,调用以处理业务逻辑
- 写在methods 配置项中
- 作为方法,需要调用 -> this.方法名() {{ 方法名() }} @事件名=“方法名”
计算属性完整写法
计算属性默认简写,只能读取访问,不能修改,要修改需要写计算属性的完整写法
computed: {
计算属性名: {
get() {
一段代码逻辑(计算逻辑)
return 结果
},
set(修改的值) {
//当计算属性的值被修改时,实行set
一段代码逻辑(修改逻辑)
}
}
}
watch侦听器(监视器)
作用:监视数据变化,执行一些业务逻辑或者异步操作
简单写法-简单类型数据,直接监视
data: {
words: '苹果',
obj: {
words: '苹果'
}
},
watch: {
//该方法会在数据变化时,触发执行oldValue一般不用,只有一个newValue
数据属性名 (newValue,oldValue) {
一些业务逻辑 或 异步操作
},
'对象.属性名'(newValue,oldValue) {
一些业务逻辑 或 异步操作
}
}
完整写法 -> 添加额外配置项
(1)deep: true 对复杂类型深度监视
(2)immediate:true 初始化立刻执行一次handler方法
data: {
obj: {
words: '苹果'
lang: 'italy'
},
},
watch: {
对象名: {
deep: true,
immediate: true, //初始化就立即执行,不必等到数据变化
handler (newValue) {
console.log(newValue)
}
}
}
Vue生命周期和生命周期的四个阶段
vue生命周期:一个vue实例从创建到销毁的整个过程
阶段:
- 创建阶段:准备响应式数据,之后就可以请求后台的数据了
- 挂载阶段:渲染模板
- 更新阶段:修改数据,更新视图,这里可以循环进行
- 销毁阶段:销毁
生命周期函数(钩子函数)
概念:vue生命周期过程中,会自动运行一些函数 -> 让开发者可以在特定阶段运行自己的代码
工程化开发和脚手架
传统核心包开发模式:基于html、css、js文件,开发vue
工程化开发模式:基于构建工具(如webpack)的环境中开发vue
vue CLI是vue官方的一个全局命令工具,可以帮我们快速创建一个开发vue项目的标准化基础架子(webpack配置 )
使用步骤:
- 根据官方安装CLI
- 检查vue版本:vue -version
- 创建项目架子:vue create project-name
- 启动项目:npm run serve
脚手架目录文件&项目运行流程
目录文件的截图
项目运行流程:npm run开始启动项目,之后加载 main.js,该js文件将app.vue(这个就是一开始学习写在标签里的模板)根组件渲染到 index.html 容器中展示页面。
//文件核心作用:导入App.vue,基于App.vue创建结构渲染index.html
//1、导入 Vue 核心包
import Vue from 'vue'
//2、导入 App.vue 根组件
import App from './App.vue'
//提示:当前处于什么状态(生产环境/开发环境)
Vue.config.productionTip = false
//1、Vue实例化。提供render方法 基于App.vue创建结构渲染index.html
new Vue({
render: h => h(App),
}).$mount('#app')//这一行的效果相当于 el: '#app'
组件化开发&根组件 Vetur插件
组件化:一个页面可以划分成一个个组件,每个组件有着自己独立的结构、样式、行为。好处:便于维护,利于复用,提升开发效率。
普通组件的注册使用
组件注册的两种方式:
- 局部注册:只能在注册的组件内使用
1> 创建 .vue文件
2> 在使用的组件内导入并注册
//比如在app.vue
//导入需要注册的组件
import 组件对象 from '.vue文件路径'
import HmHeaderr from './components/HHmHeader'
export default {
//局部注册
components: {
'组件名': 组件对象,
HmHeader: HmHeader
}
}
- 全局注册:所有组件内都能使用
1> 创建 .vue 文件
2> main.js中进行全局注册
import HmButton from './components/HmButton'
// 调用Vue.component 进行全局注册
// vue.component('组件名',组件对象)
Vue.component('HmButton',HmButton)
组件的三大部分组成
结构template:只有一个根元素
样式style:全局样式(默认):影响所有组件;局部样式:scoped下样式,只作用于当前组件
逻辑scrript:el跟实例独有,组件中的data是一个函数,其他配置项一致
export default {
data () {
return {
count: 999
}
}
}
组件通信
1.父子关系
props 和 $emit
父组件通过props将数据传递给子组件,子组件利用 $emit通知父组件修改更新
父组件先动态绑定属性值,然后子组件通过prpos:{‘属性名’}来接受父组件的值
vuex
2.非父子关系
provide & inject
eventbus
vuex
prop
定义:组件上注册的一些自定义属性
作用:向组件传递数据
特点:任意数量,任何类型
props校验
props: {
校验的属性名: 类型
}
props: {
校验的属性名: {
type: 类型,
required: true,//是否必填
default: 默认值,//默认值
validator (value){
//自定义校验逻辑
return 是否通过校验
}
}
}
prop & data,单向数据流
单向数据流:子组件要修改父组件传来的值,只能告诉父组件让父组件改
prop:外面传进来的值,不能随便改
data:自己的值,随便改
非父子通信 - event bus 事件总线
非父子通信 - provide & inject
v-model 原理
<div id = "app">
<input v-model="msg" type="text">
<input :value="msg" @input="msg = $event.target.value" type="text">
</div>
表单类组件封装 & v-model 简化代码
步骤:
- 父传子:数据由父组件通过props传递过来,然后拆解v-model来绑定数据
- 子传父:监听输入,将值再传递给父组件
//子组件Sun
<select :value="CityId" @change="handleChange">
<option value="102"></option>
</select>
props: {
cityId: String
},
methods: {
handleChange(e){
this.$emit('事件名',e.target.value)
}
}
//父组件
<faSelect :cityId="selected" @事件名="selected = $event"></faSelect>
以上代码的简化:
- 子组件中:props传值要给value,触发事件为 input 事件
- 父组件中:v-model给组件直接绑定数据(:value + @input)
//子组件
<select :value="value" @change=“handle”>
<option value="102"><.option>
</select>
props: {
value: Number
},
methods: {
handle(e){
this.$emit('input',e.target.value)
}
}
//父组件
<select v-model="selectId "/>
.sync 修饰符
作用:可以实现子组件与父组件数据的双向绑定
特点:prop属性名,可以自定义,非固定为value
场景:封装弹框类的基础组件,visible属性true显示fale隐藏
// 父组件
<BaseDia :visible.sync="isShow"/>
---------------------------------上下一样
<BaseDia :visible="isShow" @update:visible="isShow = $event"/>
// 子组件
props: {
visible: Boolean
},
this.$emit('update:visable',false)
ref 和 $refs
作用:利用ref和$refs可以用于获取dom元素,或者组件实例
特点:查找范围 当前组件内
- 获取DOM:
第一步:找到目标标签,添加 ref 属性
<div ref="charRef">111</div>
第二步:恰当时机,通过this.$refs.xxx获取目标元素
mounted () {
console.log(this.$refs.charRef)
}
- 获取组件实例
第一步:找到目标组件添加 ref 属性
<BaseForm ref="baseForm"></BaseForm>
第二步:恰当时机,通过 this.$refs.xx获取目标组件,就可以调用组件对象里面的方法
this.$refs.baseForm.组件方法()
vue异步更新dom & $nextTick
- Vue是异步更新DOM的
- 想要在DOM更新完成之后做某件事,可以使用$nextTick
//这段代码起不到作用,第一句异步更新,第二句会再第一句前面执行
handleEdit () {
//1.显示输入框
this.isShowEdit = true
//2.让输入框获取焦点
this.$refs,inp.focus()
}
// 修改代码
handleEdit () {
//1.显示输入框
this.isShowEdit = true
//2.让输入框获取焦点
this.$nextTick(()=>{
this.$refs,inp.focus()
})
}
自定义指令
- 全局注册
Vue.directive('指令名'.{
//inserted 会在指令所在的元素插入到页面中的时候出发
inserted (el) {
//el 就是指令所绑定的元素
el.focus()
}
})
- 局部注册
directives: {
"指令名": {
inserted () {
//可以对el标签,扩展额外功能
el.focus()
}
}
}
自定义指令 - 指令的值
需求:实现一个color指令 - 传入不同的颜色,给标签设置文字颜色
- 语法:再绑定自定义指令时,可以通过“等号”的形式为指令绑定具体的参数值
<div v-color="color1">我是内容</div>
- 通过binding.value 可以拿到指令值,指令值修改会触发update函数
data () {
return {
color1: red
}
},
directives: {
color: {
//提供的是元素被添加到页面中时的逻辑
inserted (el,binding) {
el.style.color = binding.value
},
//指令的值的修改的时候触发,提供值变化后,dom更新的逻辑
update (el,binding) {
el.style.color = binding.value
}
}
}
插槽
作用:让组件内部的一些结构支持自定义
用法:
第一步:将希望自定义组件的某部分使用 slot 占位
<!--组件TodoList-->
<template>
<div>111</div>
<slot></slot>
<div></div>
</template>
第二步:在使用组件的地方自由定制
<TodoList>
<!--在这里自定义想要的内容-->
<span>我在这里自定义,将在slot部位显示</span>
</TodoList>
插槽默认内容的显示
只需要在 slot 部位写入默认值就可以,当使用插槽的时候自定义的内容就会覆盖掉默认内容
具名插槽
作用:组件内有多处需要自定义
<!--MyDialog组件-->
<div>
<slot name="head"></slot>
</div>
<div>
<slot name="content"></slot>
</div>
<div>
<slot name="footer"></slot>
</div>
<MyDialog>
<template v-slot:head>
自定义内容1
</template>
<template v-slot:content>
自定义内容2
</template>
<template #footer>
自定义内容3
</template>
</MyDialog>
作用域插槽
封装通用组件
VueRouter(5 + 2)
- 下载:下载VueRouter模块到当前工程,版本3.6.5
npm
- 引入
import VueRouter from 'vue-router'
- 安装路由对象
Vue.use(VueRouter)
- 创建路由对象
const router = new VueRouter()
- 注入,将路由对象注入到 new Vue 实例中,建立关联
new Vue({
render: h => h(App),
router
}).$mount('#app')
两个核心的步骤
- 创建需要的组件(views目录),配置路由柜子
Find.vue My.vue Friend.vue
import Find from './views/Find.vue'
import My from './views/My.vue'
import Friend from './views/Friend.vue'
const router = new VueRouter({
routes: [
{path: '/find',component: Find},
{path: '/my',component: My},
{path: '/friend',component:Friend},
]
})
2.配置导航,配置路由出口(路径匹配的组件显示的位置)
<div class="footer_wrap">
<a href="#/find">发现音乐</a>
<a href="#/my">我的音乐</a>
<a href="#/friend">朋友</a>
</div>
<div class="top">
<router-view></router-view>
</div>
组件存放目录问题(组件分类)
- src/views 文件夹:页面组件-页面展示-配合路由使用
- src/components文件夹:复用组件-展示数据-常用于复用
路由封装
- 将原来在main.js中的路由代码剪切到 src/router/index.js 文件中方便管理
- 原来的路径可以写成绝对路径 @代表 src 目录
声明式导航 - 导航链接
vue-router 提供了一个全局组件 router-link(取代a标签)
-
能实现跳转,配置 to 属性来指定路径(必须),相比于a标签省略 #
-
自带高亮类名,可以通过css手动设置高亮类型
router-link-active 模糊匹配 to=“/my"可以匹配 /my /my/a /my/b …
router-link-exact-active 精确匹配 to=”/my" 只可以匹配 /my
如何自定义这两个类名
const router = new VueRoouter({
routes:[...],
linkActiveClass:'类名1',
linkExactiveClass:'类名2'
})
声明式导航-跳转传参
- 查询参数传参(适合传多个参数)
- to = “/path?参数名=值”
- 对应页面组件接受传递过来的值 $router.query.参数名
- 动态路由传参 (单个)
- 配置动态路由
const router = new VueRouter({
routes: [
...,
{
path: '/search/:参数名',
component: Search
}
]
})
- 配置导航链接 to=“/path/参数值”
- 对应页面组件接受传递过来的值 $route.params.参数名
可选符 ?
如果按照第一步那样配置参数名的话在跳转链接的时候就必须有参数名,如后面增加可选符的话有没有参数就都可以 path: ‘/search/:参数名?’,
路由重定向
问题:网页打开后,url是默认路径 / ,未匹配到组件时会出现空白
重定向:匹配到某一路径的时候,强制跳转到某一路径
const router = new VueRouter({
routes: [
{path: '/',redirect: '/home'},
{path: '/home',component: Home},
]
})
路由 404
作用:当路径找不到匹配时,给一个提示页面
位置:在路由地址配置项的最后
const router = new VueRouter({
routes: [
{path: '/',redirect: '/home'},
{path: '/home',component: Home},
{path: '*',component: NotFind} //当前面两个选项都匹配不到的时候就轮到这行了
]
})
路由模式
- hash 路由(默认)
- history路由(常用)上线需要服务端支持
const router = new VueRouter({
routes: [
{path: '/',redirect: '/home'},
{path: '/home',component: Home},
{path: '*',component: NotFind} //当前面两个选项都匹配不到的时候就轮到这行了
],
mode: "history"
})
编程式导航 - 按钮基本跳转
methods: {
goSearch() {
//通过路径的方式跳转
// 1
this.$router.push('/search')
// 2
this.$router.push({
path: '/search'
})
}
}
//通过命名路由的方式跳转,适合名字长的
const router = new VueRouter({
routes: [
{path: '/',redirect: '/home'},
{name: 'search' path: '/home',component: Home},
{path: '*',component: NotFind} //当前面两个选项都匹配不到的时候就轮到这行了
],
mode: "history"
})
methods: {
goSearch() {
this.$router.push({
name: 'search'
})
}
}
编程式导航 - 路由传参
// 通过路径参数传参
data () {
return {
inpvalue: ''
}
},
methods: {
goSearch() {
//通过路径的方式跳转
// 1
this.$router.push(`/search?key=${this.inpvlaue}`)
// 2
this.$router.push({
path: '/search',
query: {
key: this.inpValue
}
})
}
}
//接受key
{{this.$route.query.key}}
// 动态路由传参
methods: {
goSearch() {
//通过路径的方式跳转
// 1
this.$router.push(`/search/${this.inpValue}`)
// 2
this.$router.push({
path: `/search/${this.inpValue}`
})
}
}
//接受
$route.pramas.words
组件缓存 keep-alive
概念:是Vue的内置组件,当他包裹动态组件的时候,会缓存不活动的组件实例而不是销毁他,同时有了新的钩子函数,同时他是一个抽象组件,自身不会渲染DOM元素。
能解决的问题:在组件切换过程中可以避免组件的多次重复渲染,减少了加载时间,提高用户体验
三个属性:
- include:组件名数组,只有匹配的组件会被缓存
- exclude:组件名数组,只有匹配的组件会被缓存
- max:最多可以缓存多少组件实例
新的两个生命周期钩子:
- actived:激活时,组件被看到时触发
- deactived:失活时,离开页面组件不看见
vuex概述
1.是什么?
vuex是一个vue的状态(数据)管理工具。vuex是一个插件,可以帮我们管理vue通用的数据(多组件共享的数据)
2.场景
某个状态在很多个组件来使用,多个组件共同维护一份数据
新建一个空仓库
- 安装 vuex
- 新建 store/index.js 专门存放 vuex
- 注册 Vue.use(Vuex) 创建仓库 const store = new Vuex.Store()
- main.js 中导入挂载
//创建数据
const store = new Vuex.Store({
state: {
count: 100
}
})
// 使用方法
// 模板中: {{$store.state.xxx}}
// 逻辑组件中:this.$store.state.xxx
// JS模块中:store.state.xxx
以上代码可以通过辅助函数 mapState() 帮助我们把store中的数据自动映射到组件的计算属性中
// 在需要的组件中先导入这个函数
import {mapState} from 'vuex'
// 计算属性中使用
computed: {
...mapState(['count'])
}
// 模板中使用: {{ count }}
核心概念 - mutations(同步)
vuex同样遵循单向数据流,组件中不能直接修改仓库里的数据(strict: ture可以开启严格模式)
mutations可以来修改state数据。(state数据的修改只能通过它)
const store = new Vuex.Store({
state: {
count: 100
},
mutations: {
add (state) {
state.count += 1
}
}
})
// 组件中调用mutations
this.$store.commit('add')
mutations的传参语法
mutations: {
add (state,n) {
state.count += n
}
}
// 组件提交中调用
this.$store.commit('add',10)
mapMutations把位于mutations中的方法提取出来,映射到组件methods中
mutations: {
add (state,n) {
state.count += n
}
}
// 组件中直接调用这个方法
import {mapMutations} from 'vuex'
methods: {
...mapMutations({'add'})//相当于组件中有了这个函数
}
核心概念 - actions
相比于mutations同步处理数据,便于监测数据变化,记录调试,actions能处理异步操作
// 组件中调用actions中的方法
methods: {
change () {
this.$store.dispatch('方法名',载荷)
}
}
// 提供actions方法
actions: {
方法名 (context, 载荷) {
setTimeout(()=>{
context.commit('m中的方法',载荷)
})
}
}
// mutations 中接受 a'ctions
mutations: {
m中方法 (state,载荷){
state.count = 载荷
}
}
mapActions…也是映射到组件的methosd方法中
核心概念-getters
类似于之前的计算属性,有时候我们还需要从state中派生出一些状态,这些状态依赖state,此时会用到getters
getters: {
filterList (state) {
return state.list.filter(item => item > 5)
}
}
组件中访问getters的方法有两种:
底层:{{ $store.getters.filterList }}
通过辅助函数:在计算属性中注册,在模版中直接插入表达式
辅助函数小结:
state和getters都是在组件的计算属性中注册
mutations和actions是在组件的方法中注册
核心概念-模块module
解决的问题:因项目太大而导致的state臃肿不以维护,从而将他拆解成多个模块。
// 创建小的模块 store/modules/user.js
const state = {
userInfo: {
name: ‘zjx’
age: 18
}
}
const mutations = {}
const actions = {}
const getters = {}
export default {
state,
mutations,
actions,
getters
}
// 将小的模块挂载到main.js中的store中
import user from ‘./modules/user’
const store = new Vuex.Store({
modules: {
user
}
})
模块中state的访问方法
- 直接通过模块名访问 $store.state.模块名.xxx
- 通过mapState映射
- 根级别的映射 mapState([‘模块名’]) 模块名.小组.小组成员
- 子模块的映射(需要在模块文件中开启命名空间 namespaced: true ) mapState(‘组件民’,[’ 小组 ']) 小组.小组成员
模块中getters的访问方法
- 原生方法直接通过模块名访问 $store.getters[’ 模块名 / xxx ']
- mapGetters映射与state相同
模块中mutation的访问方法
默认模块(没有开启命名空间)mutation 和 actions会被挂载到全局,只有开启命名空间,才会挂载到子模块
- 原生方法 $store.commit( ’ 模块名/mutation中方法 ', 额外参数 )
- mapMutations映射
- mapMutations([’ xxx '])
- mapMutations(‘模块名’,[‘xxx’]),需要开启命名空间
模块中action的访问方法(与mutation类似)
基于json-server工具,模拟后端接口服务环境
- 安装全局工具(仅一次)json-server
- 代码根目录新建一个 db 目录
- 将资料 index.json 移入db目录
- 进入 db目录,执行命令,启动后端接口服务
- 访问接口测试 http://localhost:3000/