如果有出错的地方请多多指教!
web前端框架
先创建一个Vue实例
Vue实例的构造需要配置对象
Vue实例需要和html中的容器产生关系
:el:“id选择器”
数据的绑定和引用:data中的数据可以通过{{}}在html中引用
即使是用css选择器选择到了多个div,也只接管第一个div
js表达式
,只要是能生成一个值,可以放在任何需要值得地方,就叫js表达式
,比如
数据和展示页面
之间用vue
搭建一个桥梁,vue来将数据绑定到视图层,同时监听视图层的变化,反映给数据
根据传参的配置对象,可以让这个属性和普通对象属性有些不同
对象中的属性值来自对象外部的变量时
,更改变量的值,对象中的值也想同步更改value:18 //添加得属性得value
enumerable:true //是否可以被枚举 for in是否可以获取到 默认:false
writeable:true //value是否可以被修改 默认:false
configurable:true //是否可以被删除 默认:false
get(){} //当这个属性被获取的时候调用
set(){} //当这个属性被修改的时候调用
Object.defineProperty(对象,要添加得键,{配置对象})
let person={
name:'szk'
}
Object.defineProperty(person,"age",{
value:18 //添加得属性得value
enumerable:true //是否可以被枚举 for in是否可以获取到 默认:false
writeable:true //value是否可以被修改 默认:false
configurable:true //是否可以被删除 默认:false
get(){} //当这个属性被获取的时候调用
set(){} //当这个属性被修改的时候调用
})
修改代理的(调用setter),就可以修改到真正的,就是数据代理
_data
,它和vue实例中的data是同一个东西,一毛一样
通过Object.defineProperty添加到vm实例上
,并且提供了getter和setter方法
调用getter,获取_data中的值
,当修改的时候调用setter修改_data中的值
{{vm._data.name}}和{{name}}是等价的,{{vm_data.name='szk2'}}和{{name='szk2'}}也是等价的
data加工了一下,让每个属性有了getter和setter
数据变了,改变页面
监听者observer来监听data中的数据,这个getter和setter就是监听者里面的方法
setter则是监听当数据发生变化的时候执行操作的
当修改属性的时候,setter被调用,在setter方法中就会让订阅者执行重新解析模板的操作,从而改变了页面
添加属性
—>就是在运行时添加响应式数据
更改响应式的数组数据
非响应式的,没有getter和setter
模板
,在之中会有相应的语法{{}}
,里面写的是js表达式
标签体中使用插值语法
v-xxx
开头:
v-bind:要绑定的属性名='js表达式'
单项数据绑定
:data中的值改变可以影响页面,但页面的值改变了不会影响data
数据双向绑定
:页面值改变了会影响data中的值
一般应用在表单元素中
v-model = 'js表达式'
radio
这种单选框,一定要给其指定value
,不然绑定的数据就是true/false而不是需要的值checkbox
这种多选的,接收的时候要用数组接收
,否则接收的也是true/falseform的submit
,可以通过 @submit.prevent
来组织表单提交后刷新页面需要传值为数字的
,可以使用后缀 v-model.number
,这样收集到的就会是数字绑定事件
,简写是@methods中
event参数
$event
在传参表中声明,否则不会有event参数传入.
出来就可以html结构出来了,但vue还没来,导致网站只显示一堆{{}}这样的结构
等模板全部渲染好之后才展示页面
v-cloak 只有属性没有值
v-show="布尔值表达式"
show的隐藏是将元素隐藏了,但结构还在
v-if:="布尔值表达式"
//后两者可选,可以不写
v-else-if:="布尔值表达式"
v-else:="布尔值表达式"
v-if实际渲染的时候如果是false是将整个元素结构都删除了,而show只是隐藏了
连贯的
必须要执行key
index作为key
,在一些特殊情况下会出错,下文会讲// 最好是迭代对象中有id属性,作为key,作为每次循环出来的“身份证号”
<div v-for='item in data中要迭代对象' :key='item.id'>
//用自己生成的index作为key的话这样写
<div v-for='(item,index) in data中要迭代的对象' :key='index'>
键值对
一定要写key,不然会降低效率,甚至会出错
虚拟DOM时对于更新了的内容会进行对比算法
,key这时就会被作为对比的依据
插入数据的顺序破坏了原本的index(此处就是往头的地方插入了数据)
,则新数据会拥有旧数据的index
有相同key的地方
,有新的内容就会代替掉,一样的地方就会复用
DOM中vue认为他们是一样的
input中的值其实是老数据中index为0的input中的值
key都是唯一的话
,就不会进行上述的对比算法
,只有新旧DOM中都有key为同一个值时才会对比亲自操作dom元素
计算属性类似的东西,可以写成一个函数
v-指令名字
,比如定义了一个叫big的指令,在标签中使用时就要用v-big=‘’
函数,参数第一个element是dom所在元素,第二个参数binding是和这个dom绑定了的一个对象
v-刚才写的函数名='value'
,这个value就是会放到bingding对象中的value的值
对象中的三个函数bind,inserted,update
dom渲染到页面上才奏效的,如dom.focus,如果用上一点中说的函数法,就无法控制在那个阶段指定,一些具体细节就无法调整
使用-对多单词进行分割
加工和运算,得到一个全新的属性
computed
Object.definedProperty
,计算属性得出的属性也会被挂在在vm
上computed:{
要计算的属性:{
get(){
return 计算出的内容
}
//但一般set用的少,因为一般是根据data中数据计算出属性,而不是改变属性来改变data中的值
set(value){
this.属性0=value
}
}
}
实例:
初次读取计算属性的时候
所依赖的数据没有发生变化,则一直是使用这个缓存
====>如果用的是函数就得重复调用多次,使用计算属性节省了很多资源开销所依赖的数据发生变化时
,又会调用get监视属性的修改
,做出反应watch:{
要监视的属性名:{
handler(oldVal,newVal){
要进行的操作
}
}
}
对象内部属性的改变
deep:true
属性可以让其监听到对象内部都能完成
在模板显示一个值,而不需要对这个值进行后续操作
,用计算属性一般会简单更新的值需要有进一步操作,如一些异步操作时,则只能使用监听属性
return 的返回值
,而监听函数可以有更多操作v-on:class='data中数据'
如果绑定的是对象,对象的键名为class动态变化的值,value为布尔值,为true就添加到class,false不添加
特殊时间点调用的函数
,就叫做生命周期函数要实现的功能一到某个阶段时就得开始调用
,就需要使用生命周期函数挂载时就执行,不需要用一个click之类的事件进行触发
vm (Vue实例对象)
有初始化的vue实例,但是但没开始数据代理
拿不到vm中的data和method
完成了数据代理,可以拿到vm中的data和method
vue已经生成了虚拟DOM,但还没放到页面上成为真实DOM
DOM的操作,最终渲染出来都不奏效
未经编译的DOM结构
经过vue编译的DOM,就没有{{}}这些东西了
初次
把真实DOM放入
页面后(挂在完毕),调用mounted初次
就是相对于之后数据更改后重新渲染模板时,就不算初次了,不会调用mounted数据已经更新了,但页面还是旧的,新数据没有放到页面上去
页面和数据都是新的
vm.$destroy()
,vm就会被销毁,但销毁之前还有beforeDestroy
濒死状态
,这个时期一般可以关闭定时器,取消订阅消息等首尾操作修改的数据,已经不会更新了
父组件中:
子组件中
使用this.$emit('自定义事件名',参数1,参数2....)
就触发这个自定义事件,传值为123
,相当于是个回调函数,返回123可以被父组件中接收触发流程
子组件标签上写上ref
this/$refs.
就可以取到子组件的实例对象on('自定义事件名称',this.要调用的方法,参数)
也可以声明自定义事件这样做就灵活很多,可以在不同的生命周期函数中调用
普通函数的回调
,里面的this并不是父组件的实例对象,而是子组件的实例对象箭头函数没有自己的this,向外寻找this,这里找到的就是mounted的this,就是父组件的实例对象,就有name属性
on方法里的回调中的this一定要是父组件的实例对象才有用
这个自定义事件用on方法添加到子组件的实例对象上
自定义事件玩出来的
一种可以实现任何组件通信的方式父子组件之间的通信,那对于兄弟组件怎么办呢?
都能使用的工具人,中转一下
大家都能访问的工具人,就是全局事件总线的精髓所在
所有的组件都可以看到
添加自定义事件
Vue实例和VueComponent实例顺着原型链都会最终找到Vue的原型对象
找到Vue的原型对象上去
Vue.prototype
就是大家都可以看到的地方
往子组件的实例对象上用on函数添加了自定义事件
Vue实例对象来当工具人
需要借助外部的js库
npm i pubsub-js
const vm=new Vue({
data:{
person:'szk'
}
})
这个函数不能是箭头函数,箭头函数没有自己的this,箭头函数的this是window,而普通函数的this是调用者Vue
const vm = new Vue({
data:function(){
return {
person:"szk"
}
}
})
可以简写成
const vm = new Vue({
data(){
return {
person:"szk"
}
}
})
在一个函数中有多次data中值的修改,vue会走完整个函数才重新渲染页面,而不是遇到一个数据修改就重新渲染一次
vue不要走完整个函数就重新渲染页面
时使用this.$nextTick(function)
花更多精力关注dom本身,而少关注因为动画带来的各种class问题
animate.css
举例子,animate.css官网npm install animate.css
import 'animate.css'
<transition
appear
name="要绑定的class名"
enter-active-class="进入时动画名"
leave-active-class="离开时动画名"
>
<h1 v-show="isShow">helloh1>
这个是需要变换的元素
transition>
局部功能代码和资源的集合
——在vue中组件一般就是html,css,js
的集合在这里插入图片描述
复用,简化编码,提高效率
vue-cli
中我们实际都写单文件组件(一个.vue文件就是一个单独的组件)
vue中组件的编写
更好const comp1=Vue.extend({ //传入一个配置对象
// 组件中一定不能写 el:
// 这个是最后在vue开发者工具里显示的名字
name:"student"
//template中写的是这个组件的模板
template:`
{{name}}-----{{age}}
`,
//data跟Vue实例中的很像,但注意一定要写成函数的形式
data(){
return{
name:"szk",
age:90
}
},
methods:{},
// ....vue实例中能写的基本都能写
})
el:""
,这个只有在vue实例中才能写
不能写成对象的形式
,要写成函数+返回值
的形式 ,如果不这样的话,最终挂载到实例上,各个组件的data就会混乱,相互可以修改对方组件中的数据,如果用的是函数+返回值的方式,data中的值是调用函数返回的,而不是从data对象中获取的,就不会出现这个问题
vue实例上注册组件,当然组件之间可以实现嵌套,就是在组件中可以注册组件,让其称为子组件
const vm=new Vue({
components:{
// key就是 要在模板(html)中使用这个组件的标签名,comp1就是上面一点钟创建的组件对象
student: comp1
people: comp2
}
})
<div id="root">
<div>
这里就是刚才在实例中注册的components的名字,用标签的形式使用
<student>student>
<people>people>
div>
div>
返回一个新的VueComponent构造函数
VueComponent构造函数并返回
返回一个VueComponent构造函数,而且每次调用产生的VueComponent是全新的
和vue实例长得差不多的VueComponent实例对象
vetur插件
,为.vue文件提供提示<template>
template>
<script>
// 写js或ts代码
script>
<style scoped>
/* 写样式css或less等 */
style>
-
连接,或者大驼峰
在组件中,有一些地方需要单独定制,在使用这个组件标签的时候再决定定制的地方怎么写
组件中的局部
的<slot name='slot的名字'> slot>
template
标签包起来,这样做而不是用div包起来是因为最终vue会脱去template标签,就可以少出现一层莫名其妙的div结构<子组件标签>
<template slot='插槽名字'>
template>
子组件标签>
原型和原型链的内容
组件实例的原型对象VueComponent 的原型对象是Vue的原型对象
实现的找到Vue的原型对象
Vue.filter
这样的操作,在Vue原型对象上加属性,就可以同时应用到组件上,实现全局配置
vue中的id
渲染的数据不同,且数据由父组件决定时,使用props传参
:+传参名称='传参内容'
的方式传参props属性接收参数
简单声明,参数名放在一个数组中
:参数名=''
,使用v-on动态绑定的形式,这样引号中会被当成是js表达式,否则数字传过去也是字符串
字符串时,则不能加 :
,因为:参数名=''的冒号中会被当成表达式,如果填字符串就代表数据的引用
,如果data或者computed中没这个属性就会报错:
高于data
,如果props中的值和data中同名,渲染props中的
公共js部分
可以抽取出来写在mixins 中mixin.js
用来写抽出来的部分mixins:[]
# 先全局安装/更新vue-cli
npm install -g @vue/cli
# vue create 项目名称创建项目
vue create myPro1
npm init vue@latest
cd myPro2
npm install
npm run dev
集中式数据管理
的插件
多个组件依赖同一个状态
action(dispatch)
:客户给服务员提需求—————> action调用mutations(commit)
:服务员让厨师做菜————>mutations修改state中数据值(厨师做好了菜)
————>客户拿到菜(拿到state中的值)npm i vuex@3
npm i vuex
import { createStore } from "vuex";
export default createStore({
state:{},
getters:{},
mutations:{},
actions:{},
modules: {},
});
数据库
store端
组件中使用:
store端
DAO层
,只能写同步方法,用来直接与state中的数据进行交互,最好不涉及业务逻辑
commit('mutations中要被调用的方法名',参数)
组件端
store端
组件端
store端
组件端
注意:使用这种方法调用一定要在每个模块vuex中加入namespaced:true
单页面应用
:整个应用只有一个完整的的页面,点击导航连接页面不刷新,只会局部刷新
,数据通过ajax请求获取
npm i vue-router
import { createRouter, createWebHistory } from 'vue-router'
const routes = [
{
path: '/test01',
name: 'test01',
component: ()=>import ('../views/router/TestRouter01.vue') //这里填的就是组件的路径
},
]
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
})
export default router
注意这里是导入的useRouter 而不是useRoute
,router是路由器,掌管push,back,forward这些路由器操作,而route是路由信息相关操作
<router-link to='路径'>跳转</router-link>
// to后用对象形式写
<router-link :to="{
name:'test01',
params:{
name:'dio',
age:101
}
}">跳转</router-link>
同一个路由之下
,就像父子一样,就要用到嵌套路由children
属性中又可以配置数组对象,每个对象由path和component组成,称为子路由注意在子路由中的path不用加/
直接在需要跳转的地方声明就可以,路由配置不用改
方式一:直接在url中拼接,用?分隔url和传参,之后写成键值对形式,不同参数之间用&连接
,这种写法不能写:to 直接写to,后面当成字符串
需要接收的时候
restful风格传参,路由需要修改,需要跳转的地方也要修改
满足条件才能触发路由
,可以理解成前端的鉴权
全局前置路由守卫和后置路由守卫
,分别在切换路由之前和切换路由之后调用router.beforeEach((to,from,next)=>{
next() //next函数用来放行,否则统统拦截
})
router.afterEach((to,from)=>{
})
const routes=[{
path:'/personal',
name:'personal',
components: Personal,
beforeEnter((to,from,next)=>{
//判断逻辑
})
}]
beforeRouteEnter,beforeRouteLeave,beforeRouteUpdate
onBeforeRouteUpdate,onBeforeRouteLeave
由于setup作用时,路由已经生效了,因此没有了onBeforeRouteEnter
组合式API
本质就是个函数
数据,各种函数,生命周期函数
暴露给模板中的东西
,可以是数据,可以是方法插值语法{{}}
取到了<script>
export default {
name:"comp1",
setup(){
let a=0 //曾经data的定义,全部放在setup中
function fun1(){ //methods中的函数,也丢在setup中
console.log(a) //由于是定义在setup这个函数中,定义的数据可以直接使用,不需要this
}
watch(){ //监听函数等等都可以在里面写
return { //既然是函数,就要有返回值,返回值就是要暴露给模板中的东西,可以是数据,可以是方法
a,
fun1
}
}
}
}
</script>
porps和context
$emit
响应式数据
对象类型的响应式数据,但不能定义基本类型
源数据放进去
RefImpl对象,本质通过Object,defineProperty实现响应式
Proxy对象
,本质通过Proxy
实现响应式基本类型或者对象类型的响应式数据
,定义对象的响应式时其实是求助了Proxy对象类型的响应式数据
RefImpl对象,需要通过.value的方式才能取到里面的值
.属性名
就可以获取属性将现有的值计算后导出,而不用在计算属性中操作别的数据时,使用简易写法
,一般不在computed中修改数据,简易写法一般够用const 属性名=computed(()=>{
return 计算出来的属性
})
watch(要监听的数据,(newValue,oldValue)=>{
console.log('xxx的数据被修改了')
},{immediate:true,deep:true})
watch([要监听的数据1,数据2...],(newValue,oldValue)=>{
console.log('xxx的数据被修改了')
},{immediate:true,deep:true})
watch(()=>对象.属性,(newValue,oldValue)=>{
console.log('xxx的数据被修改了')
},{immediate:true,deep:true})
即为ref()或者reactive()包裹的数据
ref处理的数据,不需要.value
reactive处理的整个对象
时,oldVal无法获取reactive处理的整个对象
时,强制deep:true 深度监视,对象内部属性被修改也能监视到
deep:true 深度监视才能监视的到
用到的响应式数据的改变
不用指出监视是什么属性,回调中用到什么属性就监视什么属性
watchEffect更注重过程(回调函数体),而计算属性注重返回值
const person=reactive({
name:'jojo'
})
watchEffect(()=>{
let name=person.name
//这里用到了响应式数据person,因此就会监视person.name
console.log(‘回调执行’)
})
封装
,可以供其他组件中使用函数
,供其他组件引入后调用,获得其返回值toRef(对象名,要单独变成响应式数据的属性名)
setup() {
const person=reactive({
name:'jojo',
age:90,
hobby:{
violin:'twoSetViolin',
pho:'saul'
}
})
return {
name:toRef(person,'name'),
hobby:toRef(person,'hobby')
}
}
祖宗组件中provide(要传的数据名,要传的数据)
setup(){
const person={name:'jojo',age:90}
provide('personData',person)
}
子孙组件中inject('数据名')
const person2=inject('personData')